feat: clipboard menu

This commit is contained in:
2026-05-04 17:01:08 +02:00
parent aad8121fa4
commit 6f4e397f6c
3 changed files with 83 additions and 1 deletions

View File

@@ -56,6 +56,7 @@ def _action(name: str) -> Callable[[argparse.Namespace], None]:
COMMANDS: list[Command] = [ COMMANDS: list[Command] = [
Command("finder", "open window finder", _action("open-finder")), Command("finder", "open window finder", _action("open-finder")),
Command("apps", "open application launcher", _action("open-app-launcher")), Command("apps", "open application launcher", _action("open-app-launcher")),
Command("clipboard", "open clipboard history picker", _action("open-clipboard-menu")),
Command("power", "open power menu", _action("open-power-menu")), Command("power", "open power menu", _action("open-power-menu")),
Command("screenshot", "open screenshot menu", _action("open-screenshot-menu")), Command("screenshot", "open screenshot menu", _action("open-screenshot-menu")),
Command("notmuch-refresh", "refresh unread mail count", _action("refresh-notmuch")), Command("notmuch-refresh", "refresh unread mail count", _action("refresh-notmuch")),

View File

@@ -21,6 +21,7 @@ from fabric.utils import (
from .modules.bar import StatusBar from .modules.bar import StatusBar
from .modules.window_fuzzy import FuzzyWindowFinder from .modules.window_fuzzy import FuzzyWindowFinder
from .modules.launcher.apps import AppLauncher from .modules.launcher.apps import AppLauncher
from .modules.launcher.clipboard import ClipboardMenu
from .modules.launcher.power import PowerMenu from .modules.launcher.power import PowerMenu
from .modules.launcher.screenrec import ScreenrecMenu from .modules.launcher.screenrec import ScreenrecMenu
from .modules.launcher.screenshot import ScreenshotMenu from .modules.launcher.screenshot import ScreenshotMenu
@@ -36,6 +37,7 @@ get_i3_connection()
dummy = Window(visible=False) dummy = Window(visible=False)
finder = FuzzyWindowFinder() finder = FuzzyWindowFinder()
app_launcher = AppLauncher() app_launcher = AppLauncher()
clipboard_menu = ClipboardMenu()
power_menu = PowerMenu(lock_command=POWER.get("lock_command", ["waylock"])) power_menu = PowerMenu(lock_command=POWER.get("lock_command", ["waylock"]))
screenshot_menu = ScreenshotMenu() screenshot_menu = ScreenshotMenu()
@@ -50,7 +52,7 @@ if SCREENREC.get("enable", False):
bar_windows = [] bar_windows = []
notmuch_widget = None notmuch_widget = None
_app_windows = [dummy, finder, app_launcher, power_menu, screenshot_menu] _app_windows = [dummy, finder, app_launcher, clipboard_menu, power_menu, screenshot_menu]
if screenrec_menu is not None: if screenrec_menu is not None:
_app_windows.append(screenrec_menu) _app_windows.append(screenrec_menu)
app = Application("sims", *_app_windows) app = Application("sims", *_app_windows)
@@ -66,6 +68,11 @@ def open_app_launcher():
app_launcher.show() app_launcher.show()
@Application.action()
def open_clipboard_menu():
clipboard_menu.show()
@Application.action() @Application.action()
def open_power_menu(): def open_power_menu():
power_menu.show() power_menu.show()

View File

@@ -0,0 +1,74 @@
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
@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]:
if not query:
return items
q = query.lower()
return [e for e in items if q in e.preview.lower()]
def render(self, item: ClipEntry) -> Gtk.Widget:
return Box(
name="slot-box",
orientation="h",
children=[
Label(
label=item.preview,
h_align="start",
ellipsization="end",
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,
)