mirror of
https://github.com/mistralai/mistral-vibe
synced 2026-04-25 17:14:55 +02:00
Co-authored-by: Clément Sirieix <clement.sirieix@mistral.ai> Co-authored-by: Kim-Adeline Miguel <kimadeline.miguel@mistral.ai> Co-authored-by: Lucas Marandat <31749711+lucasmrdt@users.noreply.github.com> Co-authored-by: Michel Thomazo <51709227+michelTho@users.noreply.github.com> Co-authored-by: Paul Cacheux <paul.cacheux@mistral.ai> Co-authored-by: Peter Evers <pevers90@gmail.com> Co-authored-by: Pierre Rossinès <pierre.rossines@mistral.ai> Co-authored-by: Pierre Rossinès <pierre.rossines@protonmail.com> Co-authored-by: Quentin <quentin.torroba@mistral.ai> Co-authored-by: Simon Van de Kerckhove <simon.vandekerckhove@mistral.ai> Co-authored-by: Val <102326092+vdeva@users.noreply.github.com> Co-authored-by: Vincent G <10739306+VinceOPS@users.noreply.github.com> Co-authored-by: Mistral Vibe <vibe@mistral.ai>
161 lines
3.9 KiB
Python
161 lines
3.9 KiB
Python
from __future__ import annotations
|
|
|
|
import base64
|
|
from collections.abc import Callable
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
|
|
import pyperclip
|
|
from textual.app import App
|
|
|
|
|
|
def _copy_osc52(text: str) -> None:
|
|
encoded = base64.b64encode(text.encode("utf-8")).decode("ascii")
|
|
osc52_seq = f"\033]52;c;{encoded}\a"
|
|
if os.environ.get("TMUX"):
|
|
osc52_seq = f"\033Ptmux;\033{osc52_seq}\033\\"
|
|
|
|
with open("/dev/tty", "w") as tty:
|
|
tty.write(osc52_seq)
|
|
tty.flush()
|
|
|
|
|
|
def _copy_pyperclip(text: str) -> None:
|
|
pyperclip.copy(text)
|
|
|
|
|
|
def _has_cmd(cmd: str) -> bool:
|
|
return shutil.which(cmd) is not None
|
|
|
|
|
|
def _copy_pbcopy(text: str) -> None:
|
|
subprocess.run(["pbcopy"], input=text.encode("utf-8"), check=True)
|
|
|
|
|
|
def _copy_xclip(text: str) -> None:
|
|
subprocess.run(
|
|
["xclip", "-selection", "clipboard"], input=text.encode("utf-8"), check=True
|
|
)
|
|
|
|
|
|
def _copy_wl_copy(text: str) -> None:
|
|
subprocess.run(["wl-copy"], input=text.encode("utf-8"), check=True)
|
|
|
|
|
|
_CMD_STRATEGIES: list[tuple[str, Callable[[str], None]]] = [
|
|
("pbcopy", _copy_pbcopy),
|
|
("xclip", _copy_xclip),
|
|
("wl-copy", _copy_wl_copy),
|
|
]
|
|
|
|
_COPY_METHODS: list[Callable[[str], None]] = [
|
|
_copy_osc52,
|
|
_copy_pyperclip,
|
|
*[fn for cmd, fn in _CMD_STRATEGIES if _has_cmd(cmd)],
|
|
]
|
|
|
|
|
|
def _paste_pyperclip() -> str:
|
|
return pyperclip.paste()
|
|
|
|
|
|
def _paste_pbpaste() -> str:
|
|
return subprocess.run(["pbpaste"], capture_output=True, check=True).stdout.decode(
|
|
"utf-8"
|
|
)
|
|
|
|
|
|
def _paste_xclip() -> str:
|
|
return subprocess.run(
|
|
["xclip", "-selection", "clipboard", "-o"], capture_output=True, check=True
|
|
).stdout.decode("utf-8")
|
|
|
|
|
|
def _paste_wl_paste() -> str:
|
|
return subprocess.run(["wl-paste"], capture_output=True, check=True).stdout.decode(
|
|
"utf-8"
|
|
)
|
|
|
|
|
|
_PASTE_CMD_STRATEGIES: list[tuple[str, Callable[[], str]]] = [
|
|
("pbpaste", _paste_pbpaste),
|
|
("xclip", _paste_xclip),
|
|
("wl-paste", _paste_wl_paste),
|
|
]
|
|
|
|
_READ_CLIPBOARD_METHODS: list[Callable[[], str]] = [
|
|
_paste_pyperclip,
|
|
*[fn for cmd, fn in _PASTE_CMD_STRATEGIES if _has_cmd(cmd)],
|
|
]
|
|
|
|
|
|
def _read_clipboard() -> str | None:
|
|
for reader in _READ_CLIPBOARD_METHODS:
|
|
try:
|
|
return reader()
|
|
except Exception:
|
|
pass
|
|
return None
|
|
|
|
|
|
def _copy_to_clipboard(text: str) -> None:
|
|
all_strategies_failed = True
|
|
for to_clipboard in _COPY_METHODS:
|
|
try:
|
|
to_clipboard(text)
|
|
except Exception:
|
|
pass
|
|
else:
|
|
all_strategies_failed = False
|
|
if _read_clipboard() == text:
|
|
return
|
|
|
|
if all_strategies_failed:
|
|
raise RuntimeError("All clipboard strategies failed")
|
|
|
|
|
|
def _get_selected_texts(app: App) -> list[str]:
|
|
selected_texts = []
|
|
|
|
for widget in app.query("*"):
|
|
try:
|
|
if not hasattr(widget, "text_selection") or not widget.text_selection:
|
|
continue
|
|
selection = widget.text_selection
|
|
result = widget.get_selection(selection)
|
|
except Exception:
|
|
continue
|
|
|
|
if not result:
|
|
continue
|
|
|
|
selected_text, _ = result
|
|
if selected_text.strip():
|
|
selected_texts.append(selected_text)
|
|
|
|
return selected_texts
|
|
|
|
|
|
def copy_selection_to_clipboard(app: App, show_toast: bool = True) -> str | None:
|
|
selected_texts = _get_selected_texts(app)
|
|
if not selected_texts:
|
|
return None
|
|
|
|
combined_text = "\n".join(selected_texts)
|
|
try:
|
|
_copy_to_clipboard(combined_text)
|
|
if show_toast:
|
|
app.notify(
|
|
"Selection copied to clipboard",
|
|
severity="information",
|
|
timeout=2,
|
|
markup=False,
|
|
)
|
|
return combined_text
|
|
except Exception:
|
|
app.notify(
|
|
"Failed to copy - clipboard not available", severity="warning", timeout=3
|
|
)
|
|
return None
|