Move Qt/Tauri/full Web SPA under archive/, ship hermit as CLI/TUI plus libcstcore and cst-webui/1 HTTP+SSE (turn lifecycle hooks, capabilities). Co-authored-by: Cursor <cursoragent@cursor.com>
118 lines
3.7 KiB
QML
118 lines
3.7 KiB
QML
import QtQuick
|
|
import QtQuick.Controls
|
|
import QtQuick.Layouts
|
|
|
|
// One message in the transcript: the prompt, the answer, or an error. The
|
|
// answer arrives as plain text while it streams and is rendered from markdown
|
|
// (by the engine, through AgentCore::renderMarkdown) once it is complete.
|
|
Rectangle {
|
|
id: card
|
|
property string role: "agent" // user | agent | error
|
|
property string text: ""
|
|
property string html: ""
|
|
property bool complete: true
|
|
property string images: ""
|
|
|
|
radius: Theme.radius
|
|
color: role === "user" ? Theme.surfaceAlt : Theme.surface
|
|
border.width: 1
|
|
border.color: role === "error" ? Theme.err
|
|
: role === "user" ? Theme.border : Theme.border
|
|
implicitHeight: layout.implicitHeight + Theme.pad * 2
|
|
|
|
ColumnLayout {
|
|
id: layout
|
|
anchors {
|
|
left: parent.left
|
|
right: parent.right
|
|
top: parent.top
|
|
margins: Theme.pad
|
|
}
|
|
spacing: Theme.gap
|
|
|
|
RowLayout {
|
|
spacing: Theme.gap
|
|
Rectangle {
|
|
width: 6
|
|
height: 6
|
|
radius: 3
|
|
color: card.role === "user" ? Theme.accent
|
|
: card.role === "error" ? Theme.err : Theme.ok
|
|
}
|
|
Text {
|
|
text: card.role === "user" ? "You"
|
|
: card.role === "error" ? "Error" : "CST-agent"
|
|
color: Theme.textDim
|
|
font.pixelSize: Theme.smallSize
|
|
font.bold: true
|
|
}
|
|
Text {
|
|
visible: !card.complete
|
|
text: "streaming…"
|
|
color: Theme.textFaint
|
|
font.pixelSize: Theme.smallSize
|
|
}
|
|
Item { Layout.fillWidth: true }
|
|
ToolButton {
|
|
implicitWidth: 26
|
|
implicitHeight: 22
|
|
visible: card.text.length > 0
|
|
ToolTip.visible: hovered
|
|
ToolTip.text: "Copy"
|
|
contentItem: ThemeIcon {
|
|
name: "edit-copy"
|
|
glyph: "⧉"
|
|
size: 14
|
|
color: parent.hovered ? Theme.text : Theme.textFaint
|
|
}
|
|
background: Rectangle {
|
|
radius: Theme.radiusSm
|
|
color: parent.hovered ? Theme.surfaceHi : "transparent"
|
|
}
|
|
onClicked: {
|
|
plain.text = card.text
|
|
plain.selectAll()
|
|
plain.copy()
|
|
}
|
|
}
|
|
}
|
|
|
|
// Hidden holder so "copy" always has the plain text, rtf or not.
|
|
TextEdit {
|
|
id: plain
|
|
visible: false
|
|
}
|
|
|
|
TextEdit {
|
|
id: streamText
|
|
visible: card.html.length === 0
|
|
Layout.fillWidth: true
|
|
text: card.text
|
|
readOnly: true
|
|
selectByMouse: true
|
|
wrapMode: TextEdit.Wrap
|
|
color: card.role === "error" ? Theme.err : Theme.text
|
|
font.pixelSize: Theme.bodySize
|
|
selectionColor: Theme.accentDim
|
|
}
|
|
|
|
Text {
|
|
visible: card.html.length > 0
|
|
Layout.fillWidth: true
|
|
text: card.html
|
|
textFormat: Text.RichText
|
|
color: Theme.text
|
|
font.pixelSize: Theme.bodySize
|
|
wrapMode: Text.Wrap
|
|
onLinkActivated: function (link) { Qt.openUrlExternally(link) }
|
|
}
|
|
|
|
// What the prompt carried: pictures from the store, not their names.
|
|
ImageStrip {
|
|
Layout.fillWidth: true
|
|
visible: card.images.length > 0
|
|
text: card.images
|
|
}
|
|
}
|
|
}
|