diff options
Diffstat (limited to 'desktop/modules/assistant/AssistantPage.qml')
| -rw-r--r-- | desktop/modules/assistant/AssistantPage.qml | 94 |
1 files changed, 48 insertions, 46 deletions
diff --git a/desktop/modules/assistant/AssistantPage.qml b/desktop/modules/assistant/AssistantPage.qml index c6b63fc..832e9be 100644 --- a/desktop/modules/assistant/AssistantPage.qml +++ b/desktop/modules/assistant/AssistantPage.qml @@ -14,9 +14,9 @@ import QtQuick.Controls import "../.." // The view over AssistantModule: the model settings, the tools' environment, -// the tools themselves, and the restart that applies them. A field saves on -// Enter; emptied, it falls back to the daemon's default, which it shows as -// its placeholder. +// the tools themselves, and the restart that applies them. Every field and +// switch edits the module's draft; the header's Save writes it. Emptied, a +// field falls back to the daemon's default, which it shows as placeholder. Column { id: page spacing: 8 @@ -44,20 +44,24 @@ Column { color: Theme.text } - // A one-line input that saves on Enter and undoes on Escape; edited - // reports every keystroke, for the add row. + // A one-line input reporting every keystroke, which the page turns into + // a draft edit. It is not bound to value, which would reset the cursor + // and eat a trailing space; it follows the value only when the draft + // moves under it (Discard, an add). bad outlines a refused value. component Field: Rectangle { id: field property string value: "" property string placeholder: "" - signal commit(string text) + property bool bad: false signal edited(string text) - // Commit on Enter even when unchanged: the add row's fields track - // their value as they are typed, so they never differ from it. - property bool live: false + signal accepted height: 34 radius: 8 color: Theme.surface + border.width: field.bad ? 1 : 0 + border.color: Theme.red + onValueChanged: if (ti.text.trim() !== field.value.trim()) ti.text = field.value + onBadChanged: if (!field.bad) ti.text = field.value TextInput { id: ti anchors { fill: parent; leftMargin: 10; rightMargin: 10 } @@ -66,13 +70,9 @@ Column { selectByMouse: true color: Theme.text font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 } - text: field.value + Component.onCompleted: text = field.value onTextEdited: field.edited(text) - onAccepted: if (field.live || text !== field.value) field.commit(text) - Keys.onEscapePressed: event => { - if (text !== field.value) text = field.value; - else event.accepted = false; - } + onAccepted: field.accepted() Text { anchors.verticalCenter: parent.verticalCenter width: parent.width @@ -126,7 +126,7 @@ Column { anchors { left: parent.left; right: restartBtn.left; rightMargin: 8; verticalCenter: parent.verticalCenter } wrapMode: Text.Wrap text: page.asst.pending ? "Saved. The assistant reads these at startup: restart it to apply." - : "The assistant reads these at startup." + : "The assistant reads these at startup: Save, then restart it." color: Theme.text font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 } } @@ -245,7 +245,7 @@ Column { anchors.fill: parent value: page.v.llm ?? "" placeholder: page.d.llm ?? "" - onCommit: t => page.asst.setKey("llm", t.trim() ? page.asst.quote(t.trim()) : null) + onEdited: t => page.asst.setKey("llm", t.trim() ? page.asst.quote(t.trim()) : null) } } @@ -258,16 +258,18 @@ Column { width: 120 value: page.v.temperature !== undefined ? String(page.v.temperature) : "" placeholder: "the model's preset" - onCommit: t => { + bad: page.asst.invalid["key:temperature"] ?? false + onEdited: t => { const x = Number(t.trim()); - if (t.trim() === "") page.asst.setKey("temperature", null); - else if (!isNaN(x) && x >= 0 && x <= 2) page.asst.setKey("temperature", String(x)); - else page.asst.error = "Temperature is a number from 0 to 2."; + const ok = t.trim() === "" || (!isNaN(x) && x >= 0 && x <= 2); + page.asst.markInvalid("key:temperature", !ok); + if (ok) page.asst.setKey("temperature", t.trim() === "" ? null : String(x)); } } Small { anchors { left: temp.right; leftMargin: 10; verticalCenter: parent.verticalCenter } text: "0 to 2; empty uses the llama-server preset's" + color: temp.bad ? Theme.red : Theme.subtext } } @@ -280,16 +282,18 @@ Column { width: 120 value: page.v.tool_rounds !== undefined ? String(page.v.tool_rounds) : "" placeholder: page.d.tool_rounds !== undefined ? String(page.d.tool_rounds) : "" - onCommit: t => { + bad: page.asst.invalid["key:tool_rounds"] ?? false + onEdited: t => { const n = Number(t.trim()); - if (t.trim() === "") page.asst.setKey("tool_rounds", null); - else if (Number.isInteger(n) && n >= 0 && n <= 20) page.asst.setKey("tool_rounds", String(n)); - else page.asst.error = "Tool rounds is a whole number from 0 to 20."; + const ok = t.trim() === "" || (Number.isInteger(n) && n >= 0 && n <= 20); + page.asst.markInvalid("key:tool_rounds", !ok); + if (ok) page.asst.setKey("tool_rounds", t.trim() === "" ? null : String(n)); } } Small { anchors { left: rounds.right; leftMargin: 10; verticalCenter: parent.verticalCenter } - text: "tool round trips per question" + text: "a whole number, 0 to 20: tool round trips per question" + color: rounds.bad ? Theme.red : Theme.subtext } } @@ -338,6 +342,11 @@ Column { background: null padding: 0 text: page.shown("system") + // Into the draft as it is typed; the default, or nothing, + // unsets the key rather than copying the default in. + onTextChanged: if (prompt.text !== page.shown("system")) + page.asst.setKey("system", prompt.text.trim() === "" || prompt.text === page.d.system + ? null : page.asst.quote(prompt.text)) } } } @@ -345,12 +354,6 @@ Column { Row { spacing: 8 Button { - text: "Save prompt" - enabled: prompt.text.trim() !== "" && prompt.text !== page.shown("system") - onClicked: page.asst.setKey("system", prompt.text === page.d.system ? null - : page.asst.quote(prompt.text)) - } - Button { text: "Use the default" enabled: page.v.system !== undefined onClicked: page.asst.setKey("system", null) @@ -374,12 +377,12 @@ Column { property string confirming: "" Repeater { - model: page.asst.envList + model: page.asst.envKeys delegate: Item { id: envRow - required property var modelData - objectName: "env:" + modelData.key + required property string modelData + objectName: "env:" + modelData width: page.width height: 34 @@ -388,25 +391,25 @@ Column { anchors { left: parent.left; verticalCenter: parent.verticalCenter } width: 120 elide: Text.ElideRight - text: envRow.modelData.key + text: envRow.modelData color: Theme.subtext font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 } } Field { anchors { left: envKey.right; leftMargin: 8; right: envDel.left; rightMargin: 8; top: parent.top; bottom: parent.bottom } - value: envRow.modelData.value - onCommit: t => page.asst.setEnv(envRow.modelData.key, t) + value: page.asst.envValue(envRow.modelData) + onEdited: t => page.asst.setEnv(envRow.modelData, t) } Button { id: envDel anchors { right: parent.right; verticalCenter: parent.verticalCenter } danger: true - text: page.confirming === envRow.modelData.key ? "Confirm" : "Remove" + text: page.confirming === envRow.modelData ? "Confirm" : "Remove" onClicked: { - if (page.confirming === envRow.modelData.key) { + if (page.confirming === envRow.modelData) { page.confirming = ""; - page.asst.setEnv(envRow.modelData.key, null); - } else page.confirming = envRow.modelData.key; + page.asst.setEnv(envRow.modelData, null); + } else page.confirming = envRow.modelData; } } } @@ -439,9 +442,8 @@ Column { anchors { left: keyField.right; leftMargin: 8; right: addBtn.left; rightMargin: 8; top: parent.top; bottom: parent.bottom } value: page.newValue placeholder: "value" - live: true onEdited: t => page.newValue = t - onCommit: page.addEnv() + onAccepted: page.addEnv() } Button { id: addBtn @@ -474,7 +476,7 @@ Column { Switch { id: toolSw anchors { left: parent.left; verticalCenter: parent.verticalCenter } - checked: toolRow.modelData.on + checked: page.asst.toolOn(toolRow.modelData) onToggled: page.asst.toggleTool(toolRow.modelData) } Column { @@ -482,7 +484,7 @@ Column { anchors { left: toolSw.right; leftMargin: 10; right: parent.right; verticalCenter: parent.verticalCenter } Text { text: toolRow.modelData.name - color: toolRow.modelData.on ? Theme.text : Theme.subtext + color: page.asst.toolOn(toolRow.modelData) ? Theme.text : Theme.subtext font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2; bold: true } } Small { |
