diff --git a/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/AiMessage.qml b/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/AiMessage.qml index 62f1dc794..3cccb9d8f 100644 --- a/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/AiMessage.qml +++ b/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/AiMessage.qml @@ -105,7 +105,7 @@ Rectangle { anchors.right: parent.right anchors.leftMargin: 10 anchors.rightMargin: 10 - spacing: 7 + spacing: 12 Item { Layout.alignment: Qt.AlignVCenter @@ -177,6 +177,20 @@ Rectangle { ButtonGroup { spacing: 5 + AiMessageControlButton { + id: regenButton + buttonIcon: "refresh" + visible: messageData?.role === 'assistant' + + onClicked: { + Ai.regenerate(root.messageIndex) + } + + StyledToolTip { + text: Translation.tr("Regenerate") + } + } + AiMessageControlButton { id: copyButton buttonIcon: activated ? "inventory" : "content_copy" @@ -254,28 +268,50 @@ Rectangle { spacing: 0 Repeater { - model: root.messageBlocks.length - delegate: Loader { - required property int index - property var thisBlock: root.messageBlocks[index] - Layout.fillWidth: true - // property var segment: thisBlock - property var segmentContent: thisBlock.content - property var segmentLang: thisBlock.lang - property var messageData: root.messageData - property var editing: root.editing - property var renderMarkdown: root.renderMarkdown - property var enableMouseSelection: root.enableMouseSelection - property bool thinking: root.messageData?.thinking ?? true - property bool done: root.messageData?.done ?? false - property bool completed: thisBlock.completed ?? false + model: ScriptModel { + values: Array.from({ length: root.messageBlocks.length }, (msg, i) => { + return ({ + type: root.messageBlocks[i].type + }) + }); + } - property bool forceDisableChunkSplitting: root.messageData.content.includes("```") - - source: thisBlock.type === "code" ? "MessageCodeBlock.qml" : - thisBlock.type === "think" ? "MessageThinkBlock.qml" : - "MessageTextBlock.qml" + delegate: DelegateChooser { + id: messageDelegate + role: "type" + DelegateChoice { roleValue: "code"; MessageCodeBlock { + required property int index + property var thisBlock: root.messageBlocks[index] + editing: root.editing + renderMarkdown: root.renderMarkdown + enableMouseSelection: root.enableMouseSelection + segmentContent: thisBlock.content + segmentLang: thisBlock.lang + messageData: root.messageData + } } + DelegateChoice { roleValue: "think"; MessageThinkBlock { + required property int index + property var thisBlock: root.messageBlocks[index] + editing: root.editing + renderMarkdown: root.renderMarkdown + enableMouseSelection: root.enableMouseSelection + segmentContent: thisBlock.content + messageData: root.messageData + done: root.messageData?.done ?? false + completed: thisBlock.completed ?? false + } } + DelegateChoice { roleValue: "text"; MessageTextBlock { + required property int index + property var thisBlock: root.messageBlocks[index] + editing: root.editing + renderMarkdown: root.renderMarkdown + enableMouseSelection: root.enableMouseSelection + segmentContent: thisBlock.content + messageData: root.messageData + done: root.messageData?.done ?? false + forceDisableChunkSplitting: root.messageData.content.includes("```") + } } } } } diff --git a/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageCodeBlock.qml b/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageCodeBlock.qml index c72905688..a6f69063d 100644 --- a/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageCodeBlock.qml +++ b/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageCodeBlock.qml @@ -13,22 +13,20 @@ import org.kde.syntaxhighlighting ColumnLayout { id: root // These are needed on the parent loader - property bool editing: parent?.editing ?? false - property bool renderMarkdown: parent?.renderMarkdown ?? true - property bool enableMouseSelection: parent?.enableMouseSelection ?? false - property var segmentContent: parent?.segmentContent ?? ({}) - property var segmentLang: parent?.segmentLang ?? "txt" + property bool editing: false + property bool renderMarkdown: true + property bool enableMouseSelection: false + property var segmentContent: ({}) + property var segmentLang: "txt" + property var messageData: {} property bool isCommandRequest: segmentLang === "command" property var displayLang: (isCommandRequest ? "bash" : segmentLang) - property var messageData: parent?.messageData ?? {} property real codeBlockBackgroundRounding: Appearance.rounding.small property real codeBlockHeaderPadding: 3 property real codeBlockComponentSpacing: 2 spacing: codeBlockComponentSpacing - anchors.left: parent.left - anchors.right: parent.right Rectangle { // Code background Layout.fillWidth: true diff --git a/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageTextBlock.qml b/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageTextBlock.qml index a245b6979..2c471d3f8 100644 --- a/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageTextBlock.qml +++ b/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageTextBlock.qml @@ -14,17 +14,17 @@ import Quickshell.Hyprland ColumnLayout { id: root // These are needed on the parent loader - property bool editing: parent?.editing ?? false - property bool renderMarkdown: parent?.renderMarkdown ?? true - property bool enableMouseSelection: parent?.enableMouseSelection ?? false - property string segmentContent: parent?.segmentContent ?? ({}) - property var messageData: parent?.messageData ?? {} - property bool done: parent?.done ?? true - property list renderedLatexHashes: [] + property bool editing: false + property bool renderMarkdown: true + property bool enableMouseSelection: false + property var segmentContent: ({}) + property var messageData: {} + property bool done: true + property bool forceDisableChunkSplitting: false + property list renderedLatexHashes: [] property string renderedSegmentContent: "" property string shownText: "" - property bool forceDisableChunkSplitting: parent?.forceDisableChunkSplitting ?? false property bool fadeChunkSplitting: !forceDisableChunkSplitting && !editing && !/\n\|/.test(shownText) && Config.options.sidebar.ai.textFadeIn Layout.fillWidth: true diff --git a/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageThinkBlock.qml b/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageThinkBlock.qml index 8407a0b56..1463c6e60 100644 --- a/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageThinkBlock.qml +++ b/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageThinkBlock.qml @@ -11,13 +11,13 @@ import Qt5Compat.GraphicalEffects Item { id: root // These are needed on the parent loader - property bool editing: parent?.editing ?? false - property bool renderMarkdown: parent?.renderMarkdown ?? true - property bool enableMouseSelection: parent?.enableMouseSelection ?? false - property string segmentContent: parent?.segmentContent ?? ({}) - property var messageData: parent?.messageData ?? {} - property bool done: parent?.done ?? true - property bool completed: parent?.completed ?? false + property bool editing: false + property bool renderMarkdown: true + property bool enableMouseSelection: false + property var segmentContent: ({}) + property var messageData: {} + property bool done: true + property bool completed: false property real thinkBlockBackgroundRounding: Appearance.rounding.small property real thinkBlockHeaderPaddingVertical: 3 diff --git a/dots/.config/quickshell/ii/services/Ai.qml b/dots/.config/quickshell/ii/services/Ai.qml index 26657b0d1..47b1d151f 100644 --- a/dots/.config/quickshell/ii/services/Ai.qml +++ b/dots/.config/quickshell/ii/services/Ai.qml @@ -769,6 +769,18 @@ Singleton { root.pendingFilePath = CF.FileUtils.trimFileProtocol(filePath); } + function regenerate(messageIndex) { + if (messageIndex < 0 || messageIndex >= messageIDs.length) return; + const id = root.messageIDs[messageIndex]; + const message = root.messageByID[id]; + if (message.role !== "assistant") return; + // Remove all messages after this one + for (let i = root.messageIDs.length - 1; i >= messageIndex; i--) { + root.removeMessage(i); + } + requester.makeRequest(); + } + function createFunctionOutputMessage(name, output, includeOutputInChat = true) { return aiMessageComponent.createObject(root, { "role": "user",