mirror of
https://github.com/danbulant/dots-hyprland
synced 2026-05-24 12:22:09 +00:00
ai chat: action buttons
copy, edit, toggle markdown rendering, delete
This commit is contained in:
parent
38efbb0d21
commit
db173152c3
4 changed files with 137 additions and 16 deletions
|
|
@ -82,16 +82,24 @@ Item {
|
|||
Ai.addMessage("## ✏️ Markdown test\n"
|
||||
+ "- **Bold**, *Italic*, `Monospace`, [Link](https://example.com)\n\n"
|
||||
+ "- Table:\n\n"
|
||||
+ "| | Quickshell | AGS/Astal |\n"
|
||||
+ "|:-----------|:----------:|:---------:|\n"
|
||||
+ "| UI Toolkit | Qt | Gtk3/Gtk4 |\n"
|
||||
+ "| Language | QML | Js/Ts/Lua |\n"
|
||||
+ "| Reactivity | Implied | Needs declaration |\n"
|
||||
+ "| Widget placement | Mildly difficult | More intuitive |\n"
|
||||
+ "| Bluetooth & Wifi support | ❌ | ✅ |\n"
|
||||
+ "| No-delay keybinds<br/><sup>(hyprland_global_shortcuts_v1)</sup> | ✅ | ❌ |\n"
|
||||
+ "| Development | New APIs | New syntax |\n"
|
||||
+ "- Code block"
|
||||
+ "| | Quickshell | AGS/Astal |\n"
|
||||
+ "|:-------------------------|:----------------:|:-----------------:|\n"
|
||||
+ "| UI Toolkit | Qt | Gtk3/Gtk4 |\n"
|
||||
+ "| Language | QML | Js/Ts/Lua |\n"
|
||||
+ "| Reactivity | Implied | Needs declaration |\n"
|
||||
+ "| Widget placement | Mildly difficult | More intuitive |\n"
|
||||
+ "| Bluetooth & Wifi support | ❌ | ✅ |\n"
|
||||
+ "| No-delay keybinds | ✅ | ❌ |\n"
|
||||
+ "| Development | New APIs | New syntax |\n"
|
||||
+ "- Code block\n"
|
||||
+ "```cpp\n"
|
||||
+ "#include <bits/stdc++.h>\n"
|
||||
+ "const std::string GREETING = \"UwU\";\n"
|
||||
+ "int main(int argc, char* argv[]) {\n"
|
||||
+ " std::cout << GREETING;\n"
|
||||
+ "}\n"
|
||||
+ "```\n"
|
||||
|
||||
|
||||
, Ai.interfaceRole);
|
||||
}
|
||||
|
|
@ -157,6 +165,15 @@ Item {
|
|||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
||||
}
|
||||
}
|
||||
remove: Transition {
|
||||
NumberAnimation {
|
||||
property: "opacity"
|
||||
from: 1; to: 0
|
||||
duration: Appearance.animation.elementMoveEnter.duration
|
||||
easing.type: Appearance.animation.elementMoveEnter.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
||||
}
|
||||
}
|
||||
|
||||
model: ScriptModel {
|
||||
values: root.messages
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import QtQuick
|
|||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Services.Notifications
|
||||
|
||||
Button {
|
||||
id: button
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import "root:/services"
|
|||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "../"
|
||||
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
|
@ -15,12 +16,16 @@ import Qt5Compat.GraphicalEffects
|
|||
|
||||
Rectangle {
|
||||
id: root
|
||||
property int messageIndex
|
||||
property var messageData
|
||||
property var messageInputField
|
||||
|
||||
property real messagePadding: 7
|
||||
property real contentSpacing: 3
|
||||
|
||||
property bool renderMarkdown: true
|
||||
property bool editing: false
|
||||
|
||||
anchors.left: parent?.left
|
||||
anchors.right: parent?.right
|
||||
implicitHeight: columnLayout.implicitHeight + root.messagePadding * 2
|
||||
|
|
@ -38,6 +43,8 @@ Rectangle {
|
|||
spacing: root.contentSpacing
|
||||
|
||||
RowLayout { // Header
|
||||
spacing: 15
|
||||
|
||||
Rectangle { // Name
|
||||
id: nameWrapper
|
||||
color: Appearance.m3colors.m3secondaryContainer
|
||||
|
|
@ -102,9 +109,10 @@ Rectangle {
|
|||
Item { Layout.fillWidth: true }
|
||||
|
||||
Button { // Not visible to model
|
||||
id: modelVisibilityIndicator
|
||||
visible: messageData.role == 'interface'
|
||||
implicitWidth: Math.max(notVisibleToModelText.implicitWidth + 10 * 2, 30)
|
||||
implicitHeight: notVisibleToModelText.implicitHeight + 5 * 2
|
||||
implicitWidth: 16
|
||||
implicitHeight: 30
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
background: Item
|
||||
|
|
@ -120,21 +128,84 @@ Rectangle {
|
|||
content: qsTr("Not visible to model")
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: modelVisibilityIndicator.visible
|
||||
font.pixelSize: Appearance.font.pixelSize.larger
|
||||
color: Appearance.colors.colOnLayer1
|
||||
text: "•"
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: 5
|
||||
|
||||
AiMessageControlButton {
|
||||
id: copyButton
|
||||
buttonIcon: "content_copy"
|
||||
onClicked: {
|
||||
Hyprland.dispatch(`exec wl-copy '${StringUtils.shellSingleQuoteEscape(root.messageData.content)}'`)
|
||||
}
|
||||
StyledToolTip {
|
||||
content: qsTr("Copy")
|
||||
}
|
||||
}
|
||||
AiMessageControlButton {
|
||||
id: editButton
|
||||
activated: root.editing
|
||||
buttonIcon: "edit"
|
||||
onClicked: {
|
||||
root.editing = !root.editing
|
||||
if (!root.editing) { // Save changes
|
||||
root.messageData.content = messageText.text
|
||||
}
|
||||
}
|
||||
StyledToolTip {
|
||||
content: root.editing ? qsTr("Save") : qsTr("Edit")
|
||||
}
|
||||
}
|
||||
AiMessageControlButton {
|
||||
id: toggleMarkdownButton
|
||||
activated: !root.renderMarkdown
|
||||
buttonIcon: root.renderMarkdown ? "wysiwyg" : "code"
|
||||
onClicked: {
|
||||
root.renderMarkdown = !root.renderMarkdown
|
||||
if (root.renderMarkdown && messageData.finished) {
|
||||
messageText.text = root.messageData.content
|
||||
}
|
||||
}
|
||||
StyledToolTip {
|
||||
content: qsTr("Toggle Markdown rendering")
|
||||
}
|
||||
}
|
||||
AiMessageControlButton {
|
||||
id: deleteButton
|
||||
buttonIcon: "close"
|
||||
onClicked: {
|
||||
Ai.removeMessage(root.messageIndex)
|
||||
}
|
||||
StyledToolTip {
|
||||
content: qsTr("Delete")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextEdit { // Message
|
||||
id: messageText
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: messagePadding
|
||||
readOnly: true
|
||||
readOnly: !root.editing
|
||||
selectByMouse: true
|
||||
|
||||
renderType: Text.NativeRendering
|
||||
font.family: Appearance.font.family.reading
|
||||
font.hintingPreference: Font.PreferNoHinting // Prevent weird bold text
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
selectedTextColor: Appearance.m3colors.m3onPrimary
|
||||
selectionColor: Appearance.m3colors.m3primary
|
||||
wrapMode: Text.WordWrap
|
||||
color: messageData.thinking ? Appearance.colors.colSubtext : Appearance.colors.colOnLayer1
|
||||
textFormat: Text.MarkdownText
|
||||
textFormat: root.renderMarkdown ? TextEdit.MarkdownText : TextEdit.PlainText
|
||||
text: messageData.thinking ? qsTr("Waiting for response...") : root.messageData.content
|
||||
|
||||
Keys.onPressed: (event) => {
|
||||
|
|
@ -155,7 +226,7 @@ Rectangle {
|
|||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton // Only for hover
|
||||
hoverEnabled: true
|
||||
cursorShape: parent.hoveredLink !== "" ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
cursorShape: parent.hoveredLink !== "" ? Qt.PointingHandCursor : Qt.IBeamCursor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/services"
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
|
||||
Button {
|
||||
id: button
|
||||
property string buttonIcon
|
||||
property bool activated: false
|
||||
|
||||
implicitHeight: 30
|
||||
implicitWidth: 30
|
||||
|
||||
PointingHandInteraction {}
|
||||
|
||||
background: Rectangle {
|
||||
radius: Appearance.rounding.small
|
||||
color: button.activated ? Appearance.m3colors.m3primary :
|
||||
button.down ? Appearance.colors.colSurfaceContainerHighestActive :
|
||||
button.hovered ? Appearance.colors.colSurfaceContainerHighestHover :
|
||||
Appearance.m3colors.m3surfaceContainerHighest
|
||||
}
|
||||
|
||||
contentItem: MaterialSymbol {
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: Appearance.font.pixelSize.large
|
||||
color: button.activated ? Appearance.m3colors.m3onPrimary :
|
||||
Appearance.m3colors.m3onSurface
|
||||
text: buttonIcon
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue