diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-17 11:34:07 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-17 11:34:07 +0200 |
| commit | 080d3d9aaffa8848d6de8546511f096d8e7c30f5 (patch) | |
| tree | 2663bd5c774d8db7fa024e82546f6cfa04c07e29 /desktop/modules/assistant/PresetsPage.qml | |
| parent | b8c0f01f058c87db5924067dbb475aa241c71b46 (diff) | |
| download | quickshell-080d3d9aaffa8848d6de8546511f096d8e7c30f5.tar.gz quickshell-080d3d9aaffa8848d6de8546511f096d8e7c30f5.zip | |
settings: pages edit a draft, and Save writes it
The qtmaildir, Assistant and Ask presets pages wrote a field on Enter,
and a switch or chip the moment it was clicked, so a half-finished
change reached the file and a slip could not be taken back. They now
edit a draft as fields are typed, and a Save and Discard pair in the
page header (SaveBar, through Page.actions) writes or drops it. Save
waits while a field holds a value that cannot be written, and a draft
over a file that changed on disk meanwhile is refused at Save rather
than written over it. A dot in the sidebar marks a page with unsaved
edits; drafts survive moving between pages. The four Mail pages share
one draft, since they are one file. Tool switches are staged too and
renamed on Save; model Load, Prefer and Restart stay immediate, being
actions rather than settings.
Live editing needed two rules. A field is no longer bound to its value,
which would reset the cursor and eat a typed trailing space; it follows
the value only when the draft moves under it. And a Repeater's model is
now the set of row keys (or a count), assigned only when that set
changes: an array rebuilt from the draft on each keystroke recreates
every row, including the one being typed in, which loses its focus.
Git, autostart's rows, HyprSunset, HyprIdle and User keep their own
buttons.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'desktop/modules/assistant/PresetsPage.qml')
| -rw-r--r-- | desktop/modules/assistant/PresetsPage.qml | 60 |
1 files changed, 31 insertions, 29 deletions
diff --git a/desktop/modules/assistant/PresetsPage.qml b/desktop/modules/assistant/PresetsPage.qml index ec103cf..56d507c 100644 --- a/desktop/modules/assistant/PresetsPage.qml +++ b/desktop/modules/assistant/PresetsPage.qml @@ -13,14 +13,17 @@ import QtQuick import "../.." import "../../assistant/Presets.js" as Presets -// One card per preset: its label and question, saved on Enter, and the file -// kinds it is offered for. Every change saves the whole list to Prefs; Reset -// drops the saved list, which brings the built-in one back. +// One card per preset: its label and question and the file kinds it is +// offered for. Every change edits the module's draft; the header's Save +// hands the list to Prefs. Reset fills the draft with the built-in list. Column { id: page spacing: 10 - readonly property var list: Presets.list(Prefs.presets) + // The module, holding the draft. Not named pm, its id (it would bind to + // itself, AGENTS.md). + required property QtObject owner + readonly property var list: owner.shown // The kinds a preset can name: the four the canvas chats about, and // "many", offered whenever two or more files are staged. readonly property var kinds: ["pdf", "text", "code", "image", "many"] @@ -38,13 +41,13 @@ Column { if (r) out.push(r); }); page.error = ""; - page.confirming = -1; - Prefs.setPresets(out); + if (fn === null || out.length !== page.list.length) page.confirming = -1; + page.owner.edit(out); } function add() { - Prefs.setPresets(page.list.concat([{ label: "New preset", icon: Presets.FALLBACK, - kinds: ["text"], prompt: "Summarize this." }])); + page.owner.edit(page.list.concat([{ label: "New preset", icon: Presets.FALLBACK, + kinds: ["text"], prompt: "Summarize this." }])); } component Small: Text { @@ -52,27 +55,30 @@ Column { font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } } + // Reports every keystroke into the draft. Not bound to value, which + // would reset the cursor; it follows the value only when the draft moves + // under it (Discard, Reset). Empty is outlined, since Save refuses it. component Field: Rectangle { id: field property string value: "" property string placeholder: "" - signal commit(string text) + signal edited(string text) height: 32 radius: 8 color: Qt.alpha(Theme.base, 0.6) + border.width: ti.text.trim() === "" ? 1 : 0 + border.color: Theme.red + onValueChanged: if (ti.text !== field.value) ti.text = field.value TextInput { + id: ti anchors { fill: parent; leftMargin: 10; rightMargin: 10 } verticalAlignment: TextInput.AlignVCenter clip: true selectByMouse: true color: Theme.text font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 } - text: field.value - onAccepted: if (text !== field.value) field.commit(text) - Keys.onEscapePressed: event => { - if (text !== field.value) text = field.value; - else event.accepted = false; - } + Component.onCompleted: text = field.value + onTextEdited: field.edited(text) Text { anchors.verticalCenter: parent.verticalCenter visible: parent.text === "" @@ -87,8 +93,8 @@ Column { width: page.width wrapMode: Text.Wrap text: "Offered on the assistant canvas for the staged files: a preset shows when every staged file is " - + "one of its kinds, or, with \"many\", when two or more are staged. Enter saves a field; " - + "changes apply at once." + + "one of its kinds, or, with \"many\", when two or more are staged. Save applies the " + + "changes, which the canvas picks up at once." } Text { @@ -101,12 +107,14 @@ Column { } Repeater { - model: page.list + // A count, not the list: a new list on every keystroke would rebuild + // every card and drop the focus of the field being typed in. + model: page.list.length delegate: Rectangle { id: card - required property var modelData required property int index + readonly property var modelData: page.list[index] ?? { label: "", prompt: "", kinds: [], icon: "" } objectName: "preset:" + index width: page.width height: body.implicitHeight + 20 @@ -133,10 +141,7 @@ Column { anchors { left: glyph.right; leftMargin: 6; right: del.left; rightMargin: 8; top: parent.top; bottom: parent.bottom } value: card.modelData.label placeholder: "label" - onCommit: t => { - if (t.trim() === "") { page.error = "A preset needs a label."; return; } - page.edit(card.index, c => Object.assign(c, { label: t.trim() })); - } + onEdited: t => page.edit(card.index, c => Object.assign(c, { label: t })) } Button { id: del @@ -154,10 +159,7 @@ Column { width: parent.width value: card.modelData.prompt placeholder: "the question asked" - onCommit: t => { - if (t.trim() === "") { page.error = "A preset needs a question."; return; } - page.edit(card.index, c => Object.assign(c, { prompt: t.trim() })); - } + onEdited: t => page.edit(card.index, c => Object.assign(c, { prompt: t })) } // A kind toggles on click; the last one cannot be cleared, @@ -208,8 +210,8 @@ Column { Button { text: "Reset to defaults" danger: true - enabled: Prefs.presets !== null - onClicked: Prefs.resetPresets() + enabled: !page.owner.resetting && (Prefs.presets !== null || page.owner.dirty) + onClicked: page.owner.reset() } } } |
