ai: fade in response

This commit is contained in:
end-4
2025-10-12 12:58:03 +02:00
parent 79b49bd57e
commit a6e360c1db
2 changed files with 84 additions and 32 deletions
@@ -348,6 +348,9 @@ Singleton {
property JsonObject translator: JsonObject { property JsonObject translator: JsonObject {
property int delay: 300 // Delay before sending request. Reduces (potential) rate limits and lag. property int delay: 300 // Delay before sending request. Reduces (potential) rate limits and lag.
} }
property JsonObject ai: JsonObject {
property bool textFadeIn: true
}
property JsonObject booru: JsonObject { property JsonObject booru: JsonObject {
property bool allowNsfw: false property bool allowNsfw: false
property string defaultProvider: "yandere" property string defaultProvider: "yandere"
@@ -8,6 +8,7 @@ import qs.modules.common.functions
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell
import Quickshell.Hyprland import Quickshell.Hyprland
ColumnLayout { ColumnLayout {
@@ -22,6 +23,8 @@ ColumnLayout {
property list<string> renderedLatexHashes: [] property list<string> renderedLatexHashes: []
property string renderedSegmentContent: "" property string renderedSegmentContent: ""
property string shownText: ""
property bool fadeChunkSplitting: !editing && !/\n\|/.test(shownText) && Config.options.sidebar.ai.textFadeIn
Layout.fillWidth: true Layout.fillWidth: true
@@ -73,7 +76,7 @@ ColumnLayout {
renderLatex() renderLatex()
} else { } else {
// console.log("Editing mode enabled", segmentContent) // console.log("Editing mode enabled", segmentContent)
textArea.text = segmentContent root.shownText = segmentContent
} }
} }
@@ -88,7 +91,7 @@ ColumnLayout {
onRenderedSegmentContentChanged: { onRenderedSegmentContentChanged: {
// console.log("Rendered segment content changed: " + renderedSegmentContent); // console.log("Rendered segment content changed: " + renderedSegmentContent);
if (renderedSegmentContent) { if (renderedSegmentContent) {
textArea.text = renderedSegmentContent; root.shownText = renderedSegmentContent;
} }
} }
@@ -104,39 +107,85 @@ ColumnLayout {
} }
} }
TextArea { spacing: 0
id: textArea Repeater {
id: textLinesRepeater
Layout.fillWidth: true property list<real> textLineOpacities: []
readOnly: !editing model: ScriptModel {
selectByMouse: enableMouseSelection || editing // Split by either double newlines or single newlines in a list
renderType: Text.NativeRendering values: root.fadeChunkSplitting ? root.shownText.split(/\n\n|\n(?= {0,2}[-\*])/g).filter(line => line.trim() !== "") : [root.shownText]
font.family: Appearance.font.family.reading onValuesChanged: {
font.hintingPreference: Font.PreferNoHinting // Prevent weird bold text while (textLinesRepeater.textLineOpacities.length < values.length) {
font.pixelSize: Appearance.font.pixelSize.small textLinesRepeater.textLineOpacities.push(root.messageData.done ? 1 : 0);
selectedTextColor: Appearance.m3colors.m3onSecondaryContainer }
selectionColor: Appearance.colors.colSecondaryContainer }
wrapMode: TextEdit.Wrap
color: messageData.thinking ? Appearance.colors.colSubtext : Appearance.colors.colOnLayer1
textFormat: renderMarkdown ? TextEdit.MarkdownText : TextEdit.PlainText
text: Translation.tr("Waiting for response...")
onTextChanged: {
if (!root.editing) return
segmentContent = text
} }
delegate: TextArea {
id: textArea
required property int index
required property string modelData
onLinkActivated: (link) => { // Fade in animation
Qt.openUrlExternally(link) visible: opacity > 0
GlobalStates.sidebarLeftOpen = false opacity: fadeChunkSplitting ? (textLinesRepeater.textLineOpacities[index] ?? (root.messageData.done ? 1 : 0)) : 1
} Connections {
target: root.messageData
function onDoneChanged() {
if (root.messageData.done) {
textLinesRepeater.textLineOpacities[textArea.index] = 1
}
}
}
Connections {
target: textLinesRepeater.model
function onValuesChanged() {
if (textLinesRepeater.model.values.length > textArea.index + 1) {
textLinesRepeater.textLineOpacities[textArea.index] = 1
}
}
}
Behavior on opacity {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
MouseArea { // Pointing hand for links Layout.fillWidth: true
anchors.fill: parent readOnly: !editing
acceptedButtons: Qt.NoButton // Only for hover selectByMouse: enableMouseSelection || editing
hoverEnabled: true renderType: Text.NativeRendering
cursorShape: parent.hoveredLink !== "" ? Qt.PointingHandCursor : font.family: Appearance.font.family.reading
(enableMouseSelection || editing) ? Qt.IBeamCursor : Qt.ArrowCursor font.hintingPreference: Font.PreferNoHinting // Prevent weird bold text
font.pixelSize: Appearance.font.pixelSize.small
selectedTextColor: Appearance.m3colors.m3onSecondaryContainer
selectionColor: Appearance.colors.colSecondaryContainer
wrapMode: TextEdit.Wrap
color: messageData.thinking ? Appearance.colors.colSubtext : Appearance.colors.colOnLayer1
textFormat: renderMarkdown ? TextEdit.MarkdownText : TextEdit.PlainText
text: modelData
onTextChanged: {
if (!root.editing) return
segmentContent = text
}
onLinkActivated: (link) => {
Qt.openUrlExternally(link)
GlobalStates.sidebarLeftOpen = false
}
MouseArea { // Pointing hand for links
anchors.fill: parent
acceptedButtons: Qt.NoButton // Only for hover
hoverEnabled: true
cursorShape: parent.hoveredLink !== "" ? Qt.PointingHandCursor :
(enableMouseSelection || editing) ? Qt.IBeamCursor : Qt.ArrowCursor
}
// Rectangle {
// anchors.fill: parent
// color: "#22786378"
// border.width: 1
// border.color: "#7E7E7E"
// }
} }
} }
} }