ai: cleaner message block delegate, add regen button

This commit is contained in:
end-4
2025-10-30 00:40:23 +01:00
parent 128808a56d
commit fcc2ee3551
5 changed files with 90 additions and 44 deletions
@@ -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("```")
} }
}
}
}
@@ -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
@@ -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<string> 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<string> 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
@@ -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
@@ -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",