81 lines
2.2 KiB
Python
81 lines
2.2 KiB
Python
import subprocess
|
|
from dataclasses import dataclass
|
|
|
|
from fabric.widgets.box import Box
|
|
from fabric.widgets.label import Label
|
|
from gi.repository import Gtk
|
|
|
|
from .base import FuzzyMenu
|
|
from .ranking import rank_items
|
|
|
|
|
|
@dataclass
|
|
class ClipEntry:
|
|
raw: str # full "<id>\t<preview>" line as emitted by cliphist
|
|
preview: str
|
|
|
|
|
|
class ClipboardProvider:
|
|
def items(self) -> list[ClipEntry]:
|
|
proc = subprocess.run(
|
|
["cliphist", "list"],
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
if proc.returncode != 0:
|
|
return []
|
|
entries: list[ClipEntry] = []
|
|
for line in proc.stdout.splitlines():
|
|
if not line:
|
|
continue
|
|
_id, sep, preview = line.partition("\t")
|
|
if not sep:
|
|
continue
|
|
entries.append(ClipEntry(raw=line, preview=preview))
|
|
return entries
|
|
|
|
def filter(self, items: list[ClipEntry], query: str) -> list[ClipEntry]:
|
|
# Empty query keeps cliphist's newest-first order; a query is
|
|
# ranked like every other launcher menu.
|
|
return rank_items(
|
|
items,
|
|
query,
|
|
lambda entry: {"preview": entry.preview},
|
|
weights={"preview": 1.0},
|
|
)
|
|
|
|
def render(self, item: ClipEntry) -> Gtk.Widget:
|
|
return Box(
|
|
name="slot-box",
|
|
orientation="h",
|
|
children=[
|
|
Label(
|
|
label=item.preview,
|
|
h_align="start",
|
|
ellipsization="end",
|
|
max_chars_width=120,
|
|
name="clip-preview",
|
|
),
|
|
],
|
|
)
|
|
|
|
def activate(self, item: ClipEntry) -> None:
|
|
decoded = subprocess.run(
|
|
["cliphist", "decode"],
|
|
input=item.raw.encode("utf-8"),
|
|
capture_output=True,
|
|
)
|
|
if decoded.returncode != 0:
|
|
return
|
|
subprocess.run(["wl-copy"], input=decoded.stdout)
|
|
|
|
|
|
def ClipboardMenu(monitor: int = 0) -> FuzzyMenu:
|
|
return FuzzyMenu(
|
|
provider=ClipboardProvider(),
|
|
monitor=monitor,
|
|
placeholder="Clipboard...",
|
|
window_name="clipboard-menu",
|
|
max_results=12,
|
|
)
|