pragma Singleton pragma ComponentBehavior: Bound import qs.modules.common import qs.modules.common.functions import QtQuick import Quickshell import Quickshell.Io Singleton { id: root // property string cliphistBinary: FileUtils.trimFileProtocol(`${Directories.home}/.cargo/bin/stash`) property string cliphistBinary: "cliphist" property real pasteDelay: 0.05 property string pressPasteCommand: "ydotool key -d 1 29:1 47:1 47:0 29:0" property bool sloppySearch: Config.options?.search.sloppy ?? false property real scoreThreshold: 0.2 property list entries: [] readonly property var preparedEntries: entries.map(a => ({ name: Fuzzy.prepare(`${a.replace(/^\s*\S+\s+/, "")}`), entry: a })) function fuzzyQuery(search: string): var { if (root.sloppySearch) { const results = entries.slice(0, 100).map(str => ({ entry: str, score: Levendist.computeTextMatchScore(str.toLowerCase(), search.toLowerCase()) })).filter(item => item.score > root.scoreThreshold) .sort((a, b) => b.score - a.score) return results .map(item => item.entry) } return Fuzzy.go(search, preparedEntries, { all: true, key: "name" }).map(r => { return r.obj.entry }); } function entryIsImage(entry) { return !!(/^\d+\t\[\[.*binary data.*\d+x\d+.*\]\]$/.test(entry)) } function refresh() { readProc.buffer = [] readProc.running = true } function copy(entry) { if (root.cliphistBinary.includes("cliphist")) // Classic cliphist Quickshell.execDetached(["bash", "-c", `printf '${StringUtils.shellSingleQuoteEscape(entry)}' | ${root.cliphistBinary} decode | wl-copy`]); else { // Stash const entryNumber = entry.split("\t")[0]; Quickshell.execDetached(["bash", "-c", `${root.cliphistBinary} decode ${entryNumber} | wl-copy`]); } } function paste(entry) { if (root.cliphistBinary.includes("cliphist")) // Classic cliphist Quickshell.execDetached(["bash", "-c", `printf '${StringUtils.shellSingleQuoteEscape(entry)}' | ${root.cliphistBinary} decode | wl-copy && wl-paste`]); else { // Stash const entryNumber = entry.split("\t")[0]; Quickshell.execDetached(["bash", "-c", `${root.cliphistBinary} decode ${entryNumber} | wl-copy; ${root.pressPasteCommand}`]); } } function superpaste(count, isImage = false) { // Find entries const targetEntries = entries.filter(entry => { if (!isImage) return true; return entryIsImage(entry); }).slice(0, count) const pasteCommands = [...targetEntries].reverse().map(entry => `printf '${StringUtils.shellSingleQuoteEscape(entry)}' | ${root.cliphistBinary} decode | wl-copy && sleep ${root.pasteDelay} && ${root.pressPasteCommand}`) // Act Quickshell.execDetached(["bash", "-c", pasteCommands.join(` && sleep ${root.pasteDelay} && `)]); } Process { id: deleteProc property string entry: "" command: ["bash", "-c", `echo '${StringUtils.shellSingleQuoteEscape(deleteProc.entry)}' | ${root.cliphistBinary} delete`] function deleteEntry(entry) { deleteProc.entry = entry; deleteProc.running = true; deleteProc.entry = ""; } onExited: (exitCode, exitStatus) => { root.refresh(); } } function deleteEntry(entry) { deleteProc.deleteEntry(entry); } Process { id: wipeProc command: [root.cliphistBinary, "wipe"] onExited: (exitCode, exitStatus) => { root.refresh(); } } function wipe() { wipeProc.running = true; } Connections { target: Quickshell function onClipboardTextChanged() { delayedUpdateTimer.restart() } } Timer { id: delayedUpdateTimer interval: Config.options.hacks.arbitraryRaceConditionDelay repeat: false onTriggered: { root.refresh() } } Process { id: readProc property list buffer: [] command: [root.cliphistBinary, "list"] stdout: SplitParser { onRead: (line) => { readProc.buffer.push(line) } } onExited: (exitCode, exitStatus) => { if (exitCode === 0) { root.entries = readProc.buffer } else { console.error("[Cliphist] Failed to refresh with code", exitCode, "and status", exitStatus) } } } IpcHandler { target: "cliphistService" function update(): void { root.refresh() } } }