ai: fix latex rendering

This commit is contained in:
end-4
2025-06-08 18:36:40 +02:00
parent cfb4f1a5e1
commit dc8bfce62e
5 changed files with 47 additions and 26 deletions
@@ -21,7 +21,7 @@ Singleton {
property string booruPreviews: FileUtils.trimFileProtocol(`${Directories.cache}/media/boorus`) property string booruPreviews: FileUtils.trimFileProtocol(`${Directories.cache}/media/boorus`)
property string booruDownloads: FileUtils.trimFileProtocol(Directories.pictures + "/homework") property string booruDownloads: FileUtils.trimFileProtocol(Directories.pictures + "/homework")
property string booruDownloadsNsfw: FileUtils.trimFileProtocol(Directories.pictures + "/homework/🌶️") property string booruDownloadsNsfw: FileUtils.trimFileProtocol(Directories.pictures + "/homework/🌶️")
property string latexOutput: FileUtils.trimFileProtocol(`${Directories.cache}/latex`) property string latexOutput: FileUtils.trimFileProtocol(`${Directories.cache}/media/latex`)
property string shellConfig: FileUtils.trimFileProtocol(`${Directories.config}/illogical-impulse`) property string shellConfig: FileUtils.trimFileProtocol(`${Directories.config}/illogical-impulse`)
property string shellConfigName: "config.json" property string shellConfigName: "config.json"
property string shellConfigPath: `${Directories.shellConfig}/${Directories.shellConfigName}` property string shellConfigPath: `${Directories.shellConfig}/${Directories.shellConfigName}`
@@ -125,9 +125,9 @@ int main(int argc, char* argv[]) {
### LaTeX ### LaTeX
- Simple inline: $\\frac{1}{2} = \\frac{2}{4}$ - Inline w/ dollar signs: $\\frac{1}{2} = \\frac{2}{4}$
- Complex inline: $$\\int_0^\\infty e^{-x^2} dx = \\frac{\\sqrt{\\pi}}{2}$$ - Inline w/ double dollar signs: $$\\int_0^\\infty e^{-x^2} dx = \\frac{\\sqrt{\\pi}}{2}$$
- Another complex inline: \\\\[\\int_0^\\infty \\frac{1}{x^2} dx = \\infty\\\\] - Inline w/ backslash and square brackets \\[\\int_0^\\infty \\frac{1}{x^2} dx = \\infty\\]
`, `,
Ai.interfaceRole); Ai.interfaceRole);
} }
@@ -28,6 +28,8 @@ Rectangle {
property bool renderMarkdown: true property bool renderMarkdown: true
property bool editing: false property bool editing: false
property list<var> messageBlocks: StringUtils.splitMarkdownBlocks(root.messageData?.content)
anchors.left: parent?.left anchors.left: parent?.left
anchors.right: parent?.right anchors.right: parent?.right
implicitHeight: columnLayout.implicitHeight + root.messagePadding * 2 implicitHeight: columnLayout.implicitHeight + root.messagePadding * 2
@@ -246,23 +248,25 @@ Rectangle {
spacing: 0 spacing: 0
Repeater { Repeater {
model: ScriptModel { model: ScriptModel {
values: StringUtils.splitMarkdownBlocks(root.messageData?.content) values: root.messageBlocks.map((block, index) => index)
} }
delegate: Loader { delegate: Loader {
required property int index
property var thisBlock: root.messageBlocks[index]
Layout.fillWidth: true Layout.fillWidth: true
// property var segment: modelData // property var segment: thisBlock
property var segmentContent: modelData.content property var segmentContent: thisBlock.content
property var segmentLang: modelData.lang property var segmentLang: thisBlock.lang
property var messageData: root.messageData property var messageData: root.messageData
property var editing: root.editing property var editing: root.editing
property var renderMarkdown: root.renderMarkdown property var renderMarkdown: root.renderMarkdown
property var enableMouseSelection: root.enableMouseSelection property var enableMouseSelection: root.enableMouseSelection
property bool thinking: root.messageData?.thinking ?? true property bool thinking: root.messageData?.thinking ?? true
property bool done: root.messageData?.done ?? false property bool done: root.messageData?.done ?? false
property bool completed: modelData.completed ?? false property bool completed: thisBlock.completed ?? false
source: modelData.type === "code" ? "MessageCodeBlock.qml" : source: thisBlock.type === "code" ? "MessageCodeBlock.qml" :
modelData.type === "think" ? "MessageThinkBlock.qml" : thisBlock.type === "think" ? "MessageThinkBlock.qml" :
"MessageTextBlock.qml" "MessageTextBlock.qml"
} }
@@ -277,7 +281,9 @@ Rectangle {
Layout.alignment: Qt.AlignLeft Layout.alignment: Qt.AlignLeft
Repeater { Repeater {
model: root.messageData?.annotationSources model: ScriptModel {
values: root.messageData?.annotationSources || []
}
delegate: AnnotationSourceButton { delegate: AnnotationSourceButton {
id: annotationButton id: annotationButton
displayText: modelData.text displayText: modelData.text
@@ -30,6 +30,18 @@ ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
Timer {
id: renderTimer
interval: 1000
repeat: true
onTriggered: {
renderLatex()
for (const hash of renderedLatexHashes) {
handleRenderedLatex(hash, true);
}
}
}
function renderLatex() { function renderLatex() {
// Regex for $...$, $$...$$, \[...\] // Regex for $...$, $$...$$, \[...\]
// Note: This is a simple approach and may need refinement for edge cases // Note: This is a simple approach and may need refinement for edge cases
@@ -53,16 +65,13 @@ ColumnLayout {
const imagePath = LatexRenderer.renderedImagePaths[hash]; const imagePath = LatexRenderer.renderedImagePaths[hash];
const markdownImage = `![latex](${imagePath})`; const markdownImage = `![latex](${imagePath})`;
const expression = StringUtils.escapeBackslashes(LatexRenderer.processedExpressions[hash]); const expression = LatexRenderer.processedExpressions[hash];
renderedSegmentContent = renderedSegmentContent.replace(expression, markdownImage); renderedSegmentContent = renderedSegmentContent.replace(expression, markdownImage);
} }
} }
onDoneChanged: { onDoneChanged: {
renderLatex() renderTimer.restart();
for (const hash of renderedLatexHashes) {
handleRenderedLatex(hash, true);
}
} }
onEditingChanged: { onEditingChanged: {
if (!editing) { if (!editing) {
+15 -9
View File
@@ -25,7 +25,8 @@ Singleton {
property list<string> processedHashes: [] property list<string> processedHashes: []
property var processedExpressions: ({}) property var processedExpressions: ({})
property var renderedImagePaths: ({}) property var renderedImagePaths: ({})
property string microtexBinaryPath: Qt.resolvedUrl("/opt/MicroTeX/LaTeX") property string microtexBinaryDir: "/opt/MicroTeX"
property string microtexBinaryName: "LaTeX"
property string latexOutputPath: Directories.latexOutput property string latexOutputPath: Directories.latexOutput
signal renderFinished(string hash, string imagePath) signal renderFinished(string hash, string imagePath)
@@ -51,23 +52,28 @@ Singleton {
} }
// 3. If not, render it with MicroTeX and mark as processed // 3. If not, render it with MicroTeX and mark as processed
// console.log(`[LatexRenderer] Rendering expression: ${expression} with hash: ${hash}`)
// console.log(` to file: ${imagePath}`)
// console.log(` with command: cd ${microtexBinaryDir} && ./${microtexBinaryName} -headless -input=${StringUtils.shellSingleQuoteEscape(expression)} -output=${imagePath} -textsize=${Appearance.font.pixelSize.normal} -padding=${renderPadding} -background=${Appearance.m3colors.m3tertiary} -foreground=${Appearance.m3colors.m3onTertiary} -maxwidth=0.85`)
const processQml = ` const processQml = `
import Quickshell.Io import Quickshell.Io
Process { Process {
id: microtexProcess${hash} id: microtexProcess${hash}
running: true running: true
command: [ "${microtexBinaryPath}", "-headless", command: [ "bash", "-c",
"-input=${StringUtils.escapeBackslashes(expression)}", "cd ${root.microtexBinaryDir} && ./${root.microtexBinaryName} -headless '-input=${StringUtils.shellSingleQuoteEscape(StringUtils.escapeBackslashes(expression))}' "
"-output=${imagePath}", + "'-output=${imagePath}' "
"-textsize=${Appearance.font.pixelSize.normal}", + "'-textsize=${Appearance.font.pixelSize.normal}' "
"-padding=${renderPadding}", + "'-padding=${renderPadding}' "
"-background=${Appearance.m3colors.m3tertiary}", // + "'-background=${Appearance.m3colors.m3tertiary}' "
"-foreground=${Appearance.m3colors.m3onTertiary}", + "'-foreground=${Appearance.colors.colOnLayer1}' "
"-maxwidth=0.85" ] + "-maxwidth=0.85 "
]
// stdout: SplitParser { // stdout: SplitParser {
// onRead: data => { console.log("MicroTeX: " + data) } // onRead: data => { console.log("MicroTeX: " + data) }
// } // }
onExited: (exitCode, exitStatus) => { onExited: (exitCode, exitStatus) => {
// console.log("[LatexRenderer] MicroTeX process exited with code: " + exitCode + ", status: " + exitStatus)
renderedImagePaths["${hash}"] = "${imagePath}" renderedImagePaths["${hash}"] = "${imagePath}"
root.renderFinished("${hash}", "${imagePath}") root.renderFinished("${hash}", "${imagePath}")
microtexProcess${hash}.destroy() microtexProcess${hash}.destroy()