diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-16 16:41:51 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-16 16:41:51 +0200 |
| commit | 607934e74ffabc1dcce72b29fc28e92a669e54ce (patch) | |
| tree | 47ca9f67742c1892ff9197b5d4d31f6dd6534d36 /desktop/assistant/Card.qml | |
| parent | 9a2a0c93cc30af979a26d957fef603e2378676be (diff) | |
| download | quickshell-607934e74ffabc1dcce72b29fc28e92a669e54ce.tar.gz quickshell-607934e74ffabc1dcce72b29fc28e92a669e54ce.zip | |
feat(assistant): the Do row of file operations
Conversions, extraction, merge and compression on the staged files from
the file state, one at a time, with Stop; Compress… opens its formats.
To .md goes through the daemon's write, the rest through fileops. The
card keeps the result line, Saved name with Open and Folder, or the
error, so another operation can follow on the same files. Results land in
Documents/Assistant.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'desktop/assistant/Card.qml')
| -rw-r--r-- | desktop/assistant/Card.qml | 85 |
1 files changed, 80 insertions, 5 deletions
diff --git a/desktop/assistant/Card.qml b/desktop/assistant/Card.qml index f63ce59..02ee476 100644 --- a/desktop/assistant/Card.qml +++ b/desktop/assistant/Card.qml @@ -9,10 +9,12 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. +import Quickshell import QtQuick import ".." import "Files.js" as Files import "Presets.js" as Presets +import "Ops.js" as Ops // The card in its three states (Assistant.qml, mode): entry is the input // line alone; file adds the staged chips and what can be done with them; @@ -33,6 +35,8 @@ Rectangle { height: card.tall ? card.fullHeight : footer.height + 28 + (chips.visible ? chips.height + 10 : 0) + (asks.visible ? asks.height + 10 : 0) + + (dos.visible ? dos.height + 10 : 0) + + (status.visible ? status.height + 10 : 0) + (actions.visible ? actions.height + 10 : 0) Behavior on height { NumberAnimation { duration: 180; easing.type: Easing.OutCubic } } clip: true @@ -153,7 +157,8 @@ Rectangle { id: chips visible: card.canvas.staged.length > 0 anchors { - bottom: asks.visible ? asks.top : actions.visible ? actions.top : footer.top + bottom: asks.visible ? asks.top : dos.visible ? dos.top : status.visible ? status.top + : actions.visible ? actions.top : footer.top bottomMargin: 10 left: parent.left; right: parent.right; leftMargin: 14; rightMargin: 14 } @@ -198,15 +203,14 @@ Rectangle { // --- what to do with them (file) --- // Ask: canned questions matching the staged kinds (Presets.js). A click - // sends one; Shift+click puts it in the input to edit first. The Do row - // (local operations) is its own spec and lands here too. + // sends one; Shift+click puts it in the input to edit first. Flow { id: asks readonly property var offered: Presets.offered(Presets.list(Prefs.presets), card.canvas.staged) - visible: card.mode === "file" && offered.length > 0 + visible: card.mode === "file" && offered.length > 0 && !card.canvas.opRunning anchors { - bottom: actions.top; bottomMargin: 10 + bottom: dos.visible ? dos.top : status.visible ? status.top : actions.top; bottomMargin: 10 left: parent.left; right: parent.right; leftMargin: 14; rightMargin: 14 } spacing: 8 @@ -230,6 +234,77 @@ Rectangle { } } + // Do: local operations (Ops.js, run by fileops or the daemon's write). + // Compress… swaps the row for its formats. Hidden while one runs. + property bool compressing: false + Connections { target: card.canvas; function onStagedChanged() { card.compressing = false; } } + + Flow { + id: dos + readonly property var offered: card.compressing + ? Ops.COMPRESS.concat([{ label: "Back", op: "back" }]) + : Ops.offered(card.canvas.staged) + visible: card.mode === "file" && offered.length > 0 && !card.canvas.opRunning + anchors { + bottom: status.visible ? status.top : actions.top; bottomMargin: 10 + left: parent.left; right: parent.right; leftMargin: 14; rightMargin: 14 + } + spacing: 8 + + Repeater { + model: dos.offered + Button { + required property var modelData + text: modelData.label + // Local operations need no daemon; To .md does. + enabled: modelData.op !== "md" || card.canvas.connected + onClicked: { + if (modelData.op === "compress") card.compressing = true; + else if (modelData.op === "back") card.compressing = false; + else { card.compressing = false; card.canvas.runOp(modelData); } + } + } + } + } + + // What the operation is doing, or what it made. + Row { + id: status + readonly property var c: card.canvas + visible: card.mode === "file" && (c.opRunning || c.opError !== "" || c.opOut.length > 0) + anchors { bottom: actions.top; bottomMargin: 10; left: parent.left; leftMargin: 14 } + spacing: 10 + + Text { + anchors.verticalCenter: parent.verticalCenter + text: status.c.opRunning + ? (status.c.opDaemon && status.c.opChars + ? `${status.c.opLabel}… ${(status.c.opChars / 1000).toFixed(1)}k characters` + : `Working on ${status.c.opLabel}…`) + : status.c.opError !== "" ? status.c.opError + : status.c.opOut.length === 1 ? `Saved ${status.c.opOut[0].slice(status.c.opOut[0].lastIndexOf("/") + 1)}` + : `Saved ${status.c.opOut.length} files` + color: status.c.opError !== "" && !status.c.opRunning ? Theme.red : Theme.text + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 } + } + Button { + visible: status.c.opRunning + text: "Stop" + danger: true + onClicked: status.c.stopOp() + } + Button { + visible: !status.c.opRunning && status.c.opOut.length === 1 + text: "Open" + onClicked: Quickshell.execDetached(["xdg-open", status.c.opOut[0]]) + } + Button { + visible: !status.c.opRunning && status.c.opOut.length > 0 + text: "Folder" + onClicked: Quickshell.execDetached(["xdg-open", status.c.outDir]) + } + } + Row { id: actions visible: card.mode === "file" |
