diff options
Diffstat (limited to 'desktop/assistant/Segments.qml')
| -rw-r--r-- | desktop/assistant/Segments.qml | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/desktop/assistant/Segments.qml b/desktop/assistant/Segments.qml new file mode 100644 index 0000000..2b68b64 --- /dev/null +++ b/desktop/assistant/Segments.qml @@ -0,0 +1,90 @@ +// 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 Quickshell +import QtQuick +import ".." +import "Md.js" as Md + +// Markdown with a copy button per code block. Prose is a read-only TextEdit, +// selectable unlike Text. It goes through Notify.sanitize, which blocks +// remote images: model output can quote a search result, and an image URL +// in it would be fetched the moment it rendered. +Column { + id: seg + property string text + spacing: 8 + + Repeater { + model: Md.split(seg.text) + + Item { + id: part + required property var modelData + width: seg.width + height: modelData.kind === "code" ? block.height : prose.height + + TextEdit { + id: prose + visible: part.modelData.kind === "prose" + width: part.width + height: visible ? implicitHeight : 0 + readOnly: true + selectByMouse: true + textFormat: TextEdit.MarkdownText + text: visible ? Notify.sanitize(part.modelData.text) : "" + wrapMode: TextEdit.Wrap + color: Theme.text + selectionColor: Qt.alpha(Theme.accent, 0.4) + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 } + onLinkActivated: link => Qt.openUrlExternally(link) + HoverHandler { cursorShape: prose.hoveredLink ? Qt.PointingHandCursor : Qt.IBeamCursor } + } + + Rectangle { + id: block + visible: part.modelData.kind === "code" + width: part.width + height: visible ? code.height + 16 : 0 + radius: 8 + color: Qt.alpha(Theme.crust, 0.6) + + TextEdit { + id: code + anchors { left: parent.left; right: copy.left; top: parent.top; margins: 8 } + readOnly: true + selectByMouse: true + textFormat: TextEdit.PlainText + text: part.modelData.text ?? "" + wrapMode: TextEdit.Wrap + color: Theme.text + selectionColor: Qt.alpha(Theme.accent, 0.4) + font { family: Theme.iconFamily; pixelSize: Theme.fontSize - 3 } + } + + // Copy only a finished block: a half-streamed one is not the + // command the model meant. + Button { + id: copy + anchors { right: parent.right; top: parent.top; margins: 6 } + visible: !part.modelData.open + width: visible ? implicitWidth : 0 + text: copied.running ? "Copied" : (part.modelData.lang || "Copy") + onClicked: { + Quickshell.execDetached(["wl-copy", "--", part.modelData.text]); + copied.restart(); + } + Timer { id: copied; interval: 1200 } + } + } + } + } +} |
