aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/assistant/Card.qml
diff options
context:
space:
mode:
Diffstat (limited to 'desktop/assistant/Card.qml')
-rw-r--r--desktop/assistant/Card.qml85
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"