aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/assistant/Card.qml
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-16 14:16:24 +0200
committerDanilo M. <danix@danix.xyz>2026-10-16 14:16:24 +0200
commitf99a9329caccdd14d9c530777a5e5b622d34f602 (patch)
treefb8351c3485b534cfa2337801f1eea2d973b8f6a /desktop/assistant/Card.qml
parent729e89b4dce7e6f89a8a20cb919ad1b05d825d09 (diff)
downloadquickshell-f99a9329caccdd14d9c530777a5e5b622d34f602.tar.gz
quickshell-f99a9329caccdd14d9c530777a5e5b622d34f602.zip
fix(assistant): keep focus in the card, scope errors, guard links
From review of the canvas. Escape stopped working in three ways, each leaving the keyboard grabbed with nothing to receive it: the history field hiding took focus with it, a disabled input could not hold focus while the daemon was down, and a click in a streaming reply focused text the next flush destroyed. The card now gives focus back to the input whenever it falls out, and the input is read-only rather than disabled. Only an error the daemon marks as ending the reply (turn) closes the live row; a command error, possibly another client's, used to freeze the reply mid-stream. The running tool comes from state events, hello included. Scrolling up during a stream no longer snaps back, since following stops when a movement starts. History snippets go through Notify.sanitize like any model output, links open only for http, https and mailto, and a line opening with inline triple-backtick code is no longer taken as a fence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'desktop/assistant/Card.qml')
-rw-r--r--desktop/assistant/Card.qml20
1 files changed, 19 insertions, 1 deletions
diff --git a/desktop/assistant/Card.qml b/desktop/assistant/Card.qml
index 543edd1..ab80d1b 100644
--- a/desktop/assistant/Card.qml
+++ b/desktop/assistant/Card.qml
@@ -34,6 +34,21 @@ Rectangle {
Component.onCompleted: input.forceActiveFocus()
+ // Focus falls out of the card when its holder goes: the history field
+ // hiding, or a streaming reply's text rebuilt under a click. With the
+ // keyboard grabbed, nothing would then reach Escape. Give it back.
+ Connections {
+ target: card.Window.window
+ function onActiveFocusItemChanged() {
+ const f = card.Window.activeFocusItem;
+ if (!f || f === card.Window.contentItem) Qt.callLater(() => input.forceActiveFocus());
+ }
+ }
+ Connections {
+ target: card.canvas
+ function onHistoryOpenChanged() { if (!card.canvas.historyOpen) input.forceActiveFocus(); }
+ }
+
// --- header ---
Item {
@@ -91,6 +106,7 @@ Rectangle {
// Follow the bottom while a reply streams, unless the user scrolled
// up to read; scrolling back to the end resumes following.
property bool stick: true
+ onMovementStarted: stick = false
onMovementEnded: stick = atYEnd
onContentHeightChanged: if (stick) Qt.callLater(positionViewAtEnd)
Component.onCompleted: positionViewAtEnd()
@@ -153,7 +169,9 @@ Rectangle {
width: scroller.width
wrapMode: TextEdit.Wrap
selectByMouse: true
- enabled: card.canvas.connected
+ // readOnly, not disabled: a disabled item cannot hold the
+ // focus that Escape needs to reach the card.
+ readOnly: !card.canvas.connected
color: Theme.text
font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 }
onCursorRectangleChanged: scroller.follow(cursorRectangle)