forked from Shinonome/dots-hyprland
ai: fix latex rendering
This commit is contained in:
@@ -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 = ``;
|
const markdownImage = ``;
|
||||||
|
|
||||||
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) {
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user