From f09fd5aa6989b54949073e3bf3cd0fa66de8d83f Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Mon, 18 May 2026 17:22:46 +0800 Subject: [PATCH] perf(ai): cache rendered message segments --- .../src/components/editor/AiAssistant.vue | 45 +-------- apps/desktop/src/lib/aiMessageRender.ts | 93 +++++++++++++++++++ packages/app-tests/aiMessageRender.test.ts | 42 +++++++++ 3 files changed, 140 insertions(+), 40 deletions(-) create mode 100644 apps/desktop/src/lib/aiMessageRender.ts create mode 100644 packages/app-tests/aiMessageRender.test.ts diff --git a/apps/desktop/src/components/editor/AiAssistant.vue b/apps/desktop/src/components/editor/AiAssistant.vue index 2f68e1565..bb1de9b38 100644 --- a/apps/desktop/src/components/editor/AiAssistant.vue +++ b/apps/desktop/src/components/editor/AiAssistant.vue @@ -49,6 +49,7 @@ import { useToast } from "@/composables/useToast"; import { buildAiContext, runAiStream, type AiAction } from "@/lib/ai"; import { buildAiAgentPlan } from "@/lib/aiAgentPlan"; import { buildAiAgentStepItems, type AiAgentStepItem, type AiAgentStepTone } from "@/lib/aiAgentStepPresentation"; +import { createAiMessageRenderer } from "@/lib/aiMessageRender"; import { Marked } from "marked"; import { aiCancelStream, @@ -624,44 +625,6 @@ function triggerAction(action: AiAction, instruction?: string) { defineExpose({ triggerAction }); -interface MessageSegment { - type: "text" | "code"; - content: string; - lang?: string; -} - -function parseMessage(text: string): MessageSegment[] { - const segments: MessageSegment[] = []; - const lines = text.split("\n"); - let i = 0; - - while (i < lines.length) { - const fenceMatch = lines[i].match(/^```(sql|mysql|postgresql|sqlite|tsql|clickhouse)?\s*$/i); - if (fenceMatch) { - const lang = (fenceMatch[1] || "sql").toUpperCase(); - const codeLines: string[] = []; - i++; - while (i < lines.length && !/^```\s*$/.test(lines[i])) { - codeLines.push(lines[i]); - i++; - } - if (i < lines.length) i++; - const content = codeLines.join("\n").trim(); - if (content) segments.push({ type: "code", lang, content }); - } else { - const textLines: string[] = []; - while (i < lines.length && !/^```(sql|mysql|postgresql|sqlite|tsql|clickhouse)?\s*$/i.test(lines[i])) { - textLines.push(lines[i]); - i++; - } - const content = textLines.join("\n"); - if (content.trim()) segments.push({ type: "text", content }); - } - } - - return segments; -} - const markedInstance = new Marked({ breaks: true, gfm: true, @@ -675,6 +638,8 @@ const markedInstance = new Marked({ function formatInlineText(text: string): string { return markedInstance.parse(text) as string; } + +const messageRenderer = createAiMessageRenderer({ markdown: formatInlineText });