diff options
Diffstat (limited to 'desktop/assistant/History.qml')
| -rw-r--r-- | desktop/assistant/History.qml | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/desktop/assistant/History.qml b/desktop/assistant/History.qml new file mode 100644 index 0000000..114ab2b --- /dev/null +++ b/desktop/assistant/History.qml @@ -0,0 +1,130 @@ +// Copyright (C) 2026 Danilo M. <danix@danix.xyz> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +import QtQuick +import QtQuick.Controls +import ".." + +// The history panel, over the transcript. Empty search: every conversation, +// newest first. Text in the field: the daemon's full-text results, with the +// matched words in bold from its snippet markers. +Rectangle { + id: panel + property var canvas + + color: Qt.alpha(Theme.base, 0.97) + radius: 8 + + onVisibleChanged: if (visible) { field.text = ""; field.forceActiveFocus(); } + + MouseArea { anchors.fill: parent } // keep clicks off the transcript below + + TextField { + id: field + anchors { top: parent.top; left: parent.left; right: parent.right } + height: 32 + placeholderText: "Search conversations" + color: Theme.text + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 } + background: Rectangle { + radius: 6 + color: Qt.alpha(Theme.surface, 0.9) + border.width: 1 + border.color: Qt.alpha(Theme.text, 0.15) + } + onTextChanged: debounce.restart() + } + + // One query per pause in typing, not per key. + Timer { + id: debounce + interval: 150 + onTriggered: field.text.trim() === "" ? panel.canvas.send({ type: "list" }) + : panel.canvas.send({ type: "search", q: field.text }) + } + + ListView { + id: rows + anchors { top: field.bottom; topMargin: 8; left: parent.left; right: parent.right; bottom: parent.bottom } + clip: true + spacing: 4 + boundsBehavior: Flickable.StopAtBounds + model: field.text.trim() === "" ? panel.canvas.convs : panel.canvas.results + + delegate: Rectangle { + id: r + required property var modelData + width: rows.width + height: col.height + 12 + radius: 6 + color: hover.hovered ? Qt.alpha(Theme.surface, 0.9) : "transparent" + border.width: r.modelData.id === panel.canvas.convId ? 1 : 0 + border.color: Qt.alpha(Theme.accent, 0.5) + + HoverHandler { id: hover } + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { panel.canvas.send({ type: "open", id: r.modelData.id }); panel.canvas.historyOpen = false; } + } + + Column { + id: col + anchors { left: parent.left; right: del.left; verticalCenter: parent.verticalCenter; margins: 8 } + spacing: 2 + Text { + width: col.width + text: r.modelData.title + elide: Text.ElideRight + color: Theme.text + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 } + } + Text { + width: col.width + // A list row has its date; a search row its snippet. + text: r.modelData.snippet !== undefined + // Backticks go: a match inside a code span would + // show its ** markers literally. + ? r.modelData.snippet.replace(/\s+/g, " ").replace(/`/g, "") + : new Date(r.modelData.updated * 1000).toLocaleString(Qt.locale(), Locale.ShortFormat) + textFormat: r.modelData.snippet !== undefined ? Text.MarkdownText : Text.PlainText + elide: Text.ElideRight + maximumLineCount: 1 + color: Theme.subtext + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } + } + } + + // Two clicks: the first arms it for 3 seconds, as the clipboard's + // clear-all does. + Button { + id: del + anchors { right: parent.right; rightMargin: 6; verticalCenter: parent.verticalCenter } + text: armed.running ? "Sure?" : "Delete" + danger: true + onClicked: { + if (!armed.running) { armed.restart(); return; } + armed.stop(); + panel.canvas.send({ type: "delete", id: r.modelData.id }); + } + Timer { id: armed; interval: 3000 } + } + } + + Text { + anchors.centerIn: parent + visible: rows.count === 0 + text: field.text.trim() === "" ? "No conversations yet." : "Nothing found." + color: Theme.overlay + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 } + } + } +} |
