diff --git a/.config/quickshell/ii/modules/common/widgets/CliphistImage.qml b/.config/quickshell/ii/modules/common/widgets/CliphistImage.qml index ce15ef3ea..e72954622 100644 --- a/.config/quickshell/ii/modules/common/widgets/CliphistImage.qml +++ b/.config/quickshell/ii/modules/common/widgets/CliphistImage.qml @@ -53,7 +53,7 @@ Rectangle { Process { id: decodeImageProcess command: ["bash", "-c", - `[ -f ${imageDecodeFilePath} ] || echo '${StringUtils.shellSingleQuoteEscape(root.entry)}' | cliphist decode > '${imageDecodeFilePath}'` + `[ -f ${imageDecodeFilePath} ] || echo '${StringUtils.shellSingleQuoteEscape(root.entry)}' | ${Cliphist.cliphistBinary} decode > '${imageDecodeFilePath}'` ] onExited: (exitCode, exitStatus) => { if (exitCode === 0) { diff --git a/.config/quickshell/ii/modules/overview/SearchItem.qml b/.config/quickshell/ii/modules/overview/SearchItem.qml index 921fc8b6d..ebbfb090c 100644 --- a/.config/quickshell/ii/modules/overview/SearchItem.qml +++ b/.config/quickshell/ii/modules/overview/SearchItem.qml @@ -1,5 +1,6 @@ // pragma NativeMethodBehavior: AcceptThisObject import qs +import qs.services import qs.modules.common import qs.modules.common.widgets import qs.modules.common.functions @@ -89,8 +90,8 @@ RippleButton { } onClicked: { - root.itemExecute() GlobalStates.overviewOpen = false + root.itemExecute() } Keys.onPressed: (event) => { if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { @@ -201,7 +202,7 @@ RippleButton { } } Loader { // Clipboard image preview - active: root.cliphistRawString && /^\d+\t\[\[.*binary data.*\d+x\d+.*\]\]$/.test(root.cliphistRawString) + active: root.cliphistRawString && Cliphist.entryIsImage(root.cliphistRawString) sourceComponent: CliphistImage { Layout.fillWidth: true entry: root.cliphistRawString diff --git a/.config/quickshell/ii/modules/overview/SearchWidget.qml b/.config/quickshell/ii/modules/overview/SearchWidget.qml index fe45f9702..78a1b46b7 100644 --- a/.config/quickshell/ii/modules/overview/SearchWidget.qml +++ b/.config/quickshell/ii/modules/overview/SearchWidget.qml @@ -37,12 +37,24 @@ Item { // Wrapper } property var searchActions: [ + { + action: "accentcolor", + execute: args => { + Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--noswitch", "--color", ...(args != '' ? [`${args}`] : [])]); + } + }, { action: "dark", execute: () => { Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "dark", "--noswitch"]); } }, + { + action: "konachanwallpaper", + execute: () => { + Quickshell.execDetached([Quickshell.shellPath("scripts/colors/random_konachan_wall.sh")]); + } + }, { action: "light", execute: () => { @@ -50,21 +62,21 @@ Item { // Wrapper } }, { - action: "wall", - execute: () => { - Quickshell.execDetached([Directories.wallpaperSwitchScriptPath]); - } - }, - { - action: "konachanwall", - execute: () => { - Quickshell.execDetached([Quickshell.shellPath("scripts/colors/random_konachan_wall.sh")]); - } - }, - { - action: "accentcolor", + action: "superpaste", execute: args => { - Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--noswitch", "--color", ...(args != '' ? [`${args}`] : [])]); + if (!/^(\d+)/.test(args.trim())) { // Invalid if doesn't start with numbers + Quickshell.execDetached([ + "notify-send", + Translation.tr("Superpaste"), + Translation.tr("Usage: %1superpaste NUM_OF_ENTRIES[i]\nSupply i when you want images\nExamples:\n%1superpaste 4i for the last 4 images\n%1superpaste 7 for the last 7 entries").arg(Config.options.search.prefix.action), + "-a", "Shell" + ]); + return; + } + const syntaxMatch = /^(?:(\d+)(i)?)/.exec(args.trim()); + const count = syntaxMatch[1] ? parseInt(syntaxMatch[1]) : 1; + const isImage = !!syntaxMatch[2]; + Cliphist.superpaste(count, isImage); } }, { @@ -73,6 +85,12 @@ Item { // Wrapper Todo.addTask(args); } }, + { + action: "wallpaper", + execute: () => { + GlobalStates.wallpaperSelectorOpen = true; + } + }, ] function focusFirstItem() { diff --git a/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml b/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml index e3056b57d..22a64412c 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml @@ -219,7 +219,7 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) function handleEntry(entry: string) { imageDecodeFileName = parseInt(entry.match(/^(\d+)\t/)[1]) decodeImageAndAttachProc.exec(["bash", "-c", - `[ -f ${imageDecodeFilePath} ] || echo '${StringUtils.shellSingleQuoteEscape(entry)}' | cliphist decode > '${imageDecodeFilePath}'` + `[ -f ${imageDecodeFilePath} ] || echo '${StringUtils.shellSingleQuoteEscape(entry)}' | ${Cliphist.cliphistBinary} decode > '${imageDecodeFilePath}'` ]) } onExited: (exitCode, exitStatus) => { diff --git a/.config/quickshell/ii/services/Cliphist.qml b/.config/quickshell/ii/services/Cliphist.qml index 3bd9cbc00..32b2a0203 100644 --- a/.config/quickshell/ii/services/Cliphist.qml +++ b/.config/quickshell/ii/services/Cliphist.qml @@ -9,6 +9,10 @@ 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: [] @@ -35,19 +39,48 @@ Singleton { }); } + function entryIsImage(entry) { + return !!(/^\d+\t\[\[.*binary data.*\d+x\d+.*\]\]$/.test(entry)) + } + function refresh() { readProc.buffer = [] readProc.running = true } function copy(entry) { - Quickshell.execDetached(["bash", "-c", `echo '${StringUtils.shellSingleQuoteEscape(entry)}' | cliphist decode | wl-copy`]); + 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)}' | cliphist delete`] + command: ["bash", "-c", `echo '${StringUtils.shellSingleQuoteEscape(deleteProc.entry)}' | ${root.cliphistBinary} delete`] function deleteEntry(entry) { deleteProc.entry = entry; deleteProc.running = true; @@ -81,8 +114,8 @@ Singleton { Process { id: readProc property list buffer: [] - - command: ["cliphist", "list"] + + command: [root.cliphistBinary, "list"] stdout: SplitParser { onRead: (line) => {