From 0e5470ccdd2e1d663a1a0ac180233dc482f7b736 Mon Sep 17 00:00:00 2001 From: jxjz <1766730816@qq.com> Date: Sun, 18 Oct 2026 20:58:09 +0800 Subject: [PATCH] fix(personal-agent): render GFM Markdown safely --- bun.lock | 37 +++ docs/images/personal-agent-markdown-gfm.png | Bin 0 -> 56779 bytes frontend/package.json | 1 + frontend/src/index.css | 17 + frontend/src/pages/PersonalAgent.tsx | 46 ++- .../tests/personal-agent-markdown.test.tsx | 304 ++++++++++++++++++ 6 files changed, 402 insertions(+), 3 deletions(-) create mode 100644 docs/images/personal-agent-markdown-gfm.png create mode 100644 frontend/tests/personal-agent-markdown.test.tsx diff --git a/bun.lock b/bun.lock index 30437bac..b81a5227 100644 --- a/bun.lock +++ b/bun.lock @@ -86,6 +86,7 @@ "react-markdown": "^10.1.0", "react-resizable-panels": "^3.0.6", "react-router-dom": "^7.13.1", + "remark-gfm": "^4.0.1", "sonner": "^2.0.7", "tailwind-merge": "^3.5.0", "tailwindcss": "^4.2.2", @@ -1194,12 +1195,28 @@ "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], + "matcher": ["matcher@3.0.0", "", { "dependencies": { "escape-string-regexp": "^4.0.0" } }, "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng=="], "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.3", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-xPgpDNl0/OXHsI7TlaIs22lWnH3KlpvZdXG1ET/m/YT2Hhdkx8lJV4kLphE6l9simyM4KPDFBwas08BgEob6Jw=="], + "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.3", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q=="], + "mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.0", "mdast-util-gfm-strikethrough": "^2.0.0", "mdast-util-gfm-table": "^2.0.0", "mdast-util-gfm-task-list-item": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="], + + "mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="], + + "mdast-util-gfm-footnote": ["mdast-util-gfm-footnote@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0" } }, "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ=="], + + "mdast-util-gfm-strikethrough": ["mdast-util-gfm-strikethrough@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-OuJHqvr455pwu2OaOrir7dbzqs4jlWKOtlu9L6GjcIjjQwNyw02VntQsr/Ek6/A13vgnXfUhBekWRUP1OkNivw=="], + + "mdast-util-gfm-table": ["mdast-util-gfm-table@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg=="], + + "mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="], + "mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="], "mdast-util-mdx-jsx": ["mdast-util-mdx-jsx@3.2.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", "unist-util-stringify-position": "^4.0.0", "vfile-message": "^4.0.0" } }, "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q=="], @@ -1218,6 +1235,20 @@ "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], + "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="], + + "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="], + + "micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="], + + "micromark-extension-gfm-strikethrough": ["micromark-extension-gfm-strikethrough@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw=="], + + "micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.2", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-pRzm4kDTu0MjlmBkxmS9yYhw60nncfcEwu9NNdPFSQEFXS95ZKyIIyTSHu/o3ReBUrLKYEq+7YaXCRn/bPB4MA=="], + + "micromark-extension-gfm-tagfilter": ["micromark-extension-gfm-tagfilter@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg=="], + + "micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="], + "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="], "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="], @@ -1444,10 +1475,14 @@ "readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="], + "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], "remark-rehype": ["remark-rehype@11.1.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw=="], + "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="], + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], @@ -1728,6 +1763,8 @@ "lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], + "mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + "node-gyp/env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], "node-gyp/undici": ["undici@6.28.0", "", {}, "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA=="], diff --git a/docs/images/personal-agent-markdown-gfm.png b/docs/images/personal-agent-markdown-gfm.png new file mode 100644 index 0000000000000000000000000000000000000000..b23f37255c7734bca465386268d6cab3de84cdb6 GIT binary patch literal 56779 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�(F?$EkxY;;_pZf2YgYFwUTN{p!H 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 p2gb+$|1;Soe*%Ms4b ( +
+ {children}
+
+ ), +}; + +function MessageMarkdown({ content }: { content: string }) { + const messageId = useId(); + const clobberPrefix = `user-content-agent-${messageId}-`; + const footnoteLabelId = `${clobberPrefix}footnote-label`; + // clobberPrefix 隔离脚注及回链;上游固定的 footnote-label 和 aria-describedby 需单独隔离。 + // + // clobberPrefix scopes footnotes and backlinks; the upstream fixed footnote-label and aria-describedby need separate scoping. + const components = useMemo(() => ({ + ...MARKDOWN_COMPONENTS, + h2: ({ node, ...props }) => ( +

+ ), + a: ({ node, ...props }) => ( + + ), + }), [footnoteLabelId]); + + return ( + + {content} + + ); +} + const STARTERS = [ { icon: Brain, text: "根据我的画像,告诉我现在最该补什么" }, { icon: BookOpen, text: "结合我的错题,帮我安排一周复习计划" }, @@ -502,7 +542,7 @@ export default function PersonalAgent() {
- {message.content} +
{!!message.sources?.length && (
diff --git a/frontend/tests/personal-agent-markdown.test.tsx b/frontend/tests/personal-agent-markdown.test.tsx new file mode 100644 index 00000000..3809544b --- /dev/null +++ b/frontend/tests/personal-agent-markdown.test.tsx @@ -0,0 +1,304 @@ +import { afterEach, beforeEach, expect, test } from "bun:test"; +import { GlobalWindow } from "happy-dom"; +import { act } from "react"; +import type { Root } from "react-dom/client"; +import type { AgentMessage, ConversationDetail } from "../src/api/personalAgent"; + +const conversationId = "markdown-regression"; +const conversationTitle = "Markdown 回归会话"; +const timestamp = "2026-01-01T00:00:00.000Z"; +const tableMarkdown = [ + "| 能力方向 | 当前重点 | 下一步 |", + "| --- | --- | --- |", + "| TypeScript | **联合类型** | 编写类型守卫 |", + "| React | 状态管理 | 练习 `useState` |", +].join("\n"); + +let browser: GlobalWindow; +let host: HTMLDivElement; +let root: Root | undefined; +let historyContent: string; +let replyContent: string; +let messages: AgentMessage[]; +let requests: string[]; +let sentMessages: Array<{ message: string; conversation_id: string | null }>; +let unexpectedRequests: string[]; +let originalFetch: typeof fetch; +const originals = new Map(); + +const json = (data: unknown) => new Response(JSON.stringify(data), { + headers: { "Content-Type": "application/json" }, +}); + +beforeEach(() => { + // GlobalWindow 复用宿主构造器,避免 Bun 的 VM 上下文缺少 SyntaxError;仍禁用页面脚本执行。 + // + // GlobalWindow supplies host constructors missing from Bun's VM context; page script evaluation stays disabled. + browser = new GlobalWindow({ + url: "http://localhost", + settings: { + enableJavaScriptEvaluation: false, + disableJavaScriptFileLoading: true, + disableCSSFileLoading: true, + navigation: { disableChildFrameNavigation: true }, + }, + }); + Object.defineProperty(browser.HTMLElement.prototype, "scrollIntoView", { + configurable: true, + value: () => {}, + }); + for (const [key, value] of Object.entries({ + window: browser, + document: browser.document, + navigator: browser.navigator, + localStorage: browser.localStorage, + HTMLElement: browser.HTMLElement, + Element: browser.Element, + Node: browser.Node, + MutationObserver: browser.MutationObserver, + getComputedStyle: browser.getComputedStyle.bind(browser), + requestAnimationFrame: browser.requestAnimationFrame.bind(browser), + cancelAnimationFrame: browser.cancelAnimationFrame.bind(browser), + IS_REACT_ACT_ENVIRONMENT: true, + })) { + originals.set(key, Object.getOwnPropertyDescriptor(globalThis, key)); + Object.defineProperty(globalThis, key, { configurable: true, writable: true, value }); + } + host = document.createElement("div"); + document.body.append(host); + historyContent = `## 学习安排\n\n${tableMarkdown}`; + replyContent = `## 后续学习安排\n\n${tableMarkdown}`; + messages = []; + requests = []; + sentMessages = []; + unexpectedRequests = []; + originalFetch = globalThis.fetch; + + // 只允许约定的内存 API 路由;未知请求直接失败,绝不转发到真实服务。 + // + // Only the in-memory API routes below are allowed; unknown requests fail without forwarding. + globalThis.fetch = (async (input, options) => { + const url = new URL(typeof input === "string" || input instanceof URL ? input : input.url, + "http://localhost"); + const method = (options?.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase(); + const request = `${method} ${url.pathname}`; + requests.push(request); + const summary = { + conversation_id: conversationId, + title: conversationTitle, + message_count: messages.length, + created_at: timestamp, + updated_at: timestamp, + }; + if (url.origin === "http://localhost") { + if (request === "GET /api/personal-agent/documents") { + return json({ items: [], supported_extensions: [".md"], max_upload_bytes: 20 * 1024 * 1024 }); + } + if (request === "GET /api/personal-agent/conversations") return json({ items: [summary] }); + if (request === `GET /api/personal-agent/conversations/${conversationId}`) { + return json({ ...summary, user_id: "markdown-test-user", messages } satisfies ConversationDetail); + } + if (request === "POST /api/personal-agent/chat") { + const payload = JSON.parse(String(options?.body)); + sentMessages.push(payload); + const reply: AgentMessage = { + role: "assistant", content: replyContent, created_at: timestamp, sources: [], + }; + messages.push({ role: "user", content: payload.message, created_at: timestamp }, reply); + return json({ conversation_id: conversationId, title: conversationTitle, message: reply }); + } + } + unexpectedRequests.push(request); + throw new Error(`Unexpected API request: ${request}`); + }) as typeof fetch; +}); + +afterEach(async () => { + try { + if (root) await act(async () => root!.unmount()); + await browser.happyDOM.close(); + } finally { + root = undefined; + globalThis.fetch = originalFetch; + for (const [key, descriptor] of originals) { + if (descriptor) Object.defineProperty(globalThis, key, descriptor); + else Reflect.deleteProperty(globalThis, key); + } + originals.clear(); + } + expect(unexpectedRequests).toEqual([]); +}); + +async function settle() { + await act(async () => { await new Promise((resolve) => setTimeout(resolve, 0)); }); +} + +async function openHistory(initialMessages?: AgentMessage[]) { + messages = initialMessages ?? [ + { role: "user", content: "请整理学习计划", created_at: timestamp }, + { role: "assistant", content: historyContent, created_at: timestamp, sources: [] }, + ]; + // 独立运行时先安装 DOM,再加载真实页面;同进程中的 React DOM 可能已由其他测试加载。 + // + // Install the DOM before loading the real page; another test may already have loaded React DOM in this process. + const [{ default: PersonalAgent }, { createRoot }] = await Promise.all([ + import("../src/pages/PersonalAgent"), import("react-dom/client"), + ]); + root = createRoot(host); + await act(async () => { root!.render(); }); + await settle(); + const conversation = Array.from(host.querySelectorAll("button")) + .find((button) => button.textContent?.includes(conversationTitle)); + expect(conversation, "历史会话入口应由 mock API 加载").toBeDefined(); + await act(async () => { conversation!.click(); }); + await settle(); + expect(requests).toContain(`GET /api/personal-agent/conversations/${conversationId}`); + const userMessage = messages.find((message) => message.role === "user"); + if (userMessage) expect(host.textContent).toContain(userMessage.content); +} + +const texts = (parent: ParentNode, selector: string) => + Array.from(parent.querySelectorAll(selector), (element) => element.textContent); + +function expectThreeColumnTable() { + const tables = host.querySelectorAll("table"); + expect(tables).toHaveLength(1); + const table = tables[0]; + expect(table.closest(".md-table-wrapper")).not.toBeNull(); + expect(texts(table, "thead tr th")).toEqual(["能力方向", "当前重点", "下一步"]); + const rows = table.querySelectorAll("tbody tr"); + expect(rows).toHaveLength(2); + expect(texts(rows[0], "td")).toEqual(["TypeScript", "联合类型", "编写类型守卫"]); + expect(texts(rows[1], "td")).toEqual(["React", "状态管理", "练习 useState"]); + expect(table.querySelectorAll("tbody td")).toHaveLength(6); + expect(table.querySelector("td strong")?.textContent).toBe("联合类型"); + expect(table.querySelector("td code")?.textContent).toBe("useState"); +} + +test("历史消息将 GFM 表格渲染为三列表头和两行数据,并保留单元格内 Markdown", async () => { + await openHistory(); + expectThreeColumnTable(); +}); + +test("历史消息保留标题、列表、引用、强调、代码和安全链接", async () => { + historyContent = [ + "## 学习笔记", "", "**重点**、*提醒*,使用 `useMemo`。", "", + "- 阅读文档", "- 编写练习", "", "1. 复盘", "2. 调整", "", + "> 每天进步一点。", "", "```ts", "const ready = true;", "```", "", + "[学习资料](https://example.com/learning?step=1)", + ].join("\n"); + await openHistory(); + expect(texts(host, "h2")).toContain("学习笔记"); + expect(texts(host, "ul > li")).toEqual(["阅读文档", "编写练习"]); + expect(texts(host, "ol > li")).toEqual(["复盘", "调整"]); + expect(texts(host, "blockquote p")).toEqual(["每天进步一点。"]); + expect(texts(host, "strong")).toContain("重点"); + expect(texts(host, "em")).toContain("提醒"); + expect(texts(host, "code")).toContain("useMemo"); + expect(host.querySelector("pre > code.language-ts")?.textContent).toBe("const ready = true;\n"); + const link = Array.from(host.querySelectorAll("a")).find((element) => element.textContent === "学习资料"); + expect(link?.getAttribute("href")).toBe("https://example.com/learning?step=1"); +}); + +test("多条消息的脚注、重复引用、回链和无障碍描述仅关联本消息,重渲染后保持稳定", async () => { + await openHistory([1, 2].map((number) => ({ + role: "assistant", + content: `## 消息${number}\n\n内容[^1],再次引用[^1]。[普通链接](#footnote-label)\n\n[^1]: 注释${number}`, + created_at: timestamp, + sources: [], + }))); + + function expectIsolatedFootnotes() { + const ids = Array.from(document.querySelectorAll("[id]"), (element) => element.id); + expect(new Set(ids).size, "同页全部 ID 必须唯一").toBe(ids.length); + const blocks = Array.from(host.querySelectorAll(".md-content")); + expect(blocks).toHaveLength(2); + return blocks.map((block, index) => { + const heading = block.querySelector("h2:not(.sr-only)"); + expect(heading?.textContent).toBe(`消息${index + 1}`); + expect(heading?.hasAttribute("id")).toBe(false); + expect(block.querySelector('a:not([data-footnote-ref]):not([data-footnote-backref])') + ?.getAttribute("href")).toBe("#footnote-label"); + const section = block.querySelector("section[data-footnotes]"); + const label = section?.querySelector("h2"); + const definitions = Array.from(section?.querySelectorAll("li[id]") ?? []); + const references = Array.from(block.querySelectorAll("a[data-footnote-ref]")); + const backlinks = Array.from(block.querySelectorAll("a[data-footnote-backref]")); + expect(label?.id).toBeTruthy(); + expect(label?.classList.contains("sr-only")).toBe(true); + expect(definitions).toHaveLength(1); + expect(definitions[0].textContent).toContain(`注释${index + 1}`); + expect(references).toHaveLength(2); + expect(backlinks).toHaveLength(2); + for (const element of [label!, definitions[0], ...references]) { + expect(element.id.startsWith("user-content-")).toBe(true); + } + for (const [referenceIndex, reference] of references.entries()) { + const href = reference.getAttribute("href"); + expect(href?.startsWith("#")).toBe(true); + expect(document.getElementById(href!.slice(1))).toBe(definitions[0]); + const descriptions = reference.getAttribute("aria-describedby")?.split(/\s+/); + expect(descriptions).toEqual([label!.id]); + expect(document.getElementById(descriptions![0])).toBe(label); + const backlink = backlinks[referenceIndex]; + const backHref = backlink.getAttribute("href"); + expect(backHref?.startsWith("#")).toBe(true); + expect(document.getElementById(backHref!.slice(1))).toBe(reference); + } + expect(block.querySelector("[node]")).toBeNull(); + return block.innerHTML; + }); + } + + const initialMarkup = expectIsolatedFootnotes(); + const collapseLibrary = host.querySelector('button[aria-label="收起资料库"]'); + expect(collapseLibrary).not.toBeNull(); + await act(async () => { collapseLibrary!.click(); }); + expect(expectIsolatedFootnotes()).toEqual(initialMarkup); +}); + +test("历史消息不生成可执行的原始 HTML 或 javascript/data 危险链接", async () => { + historyContent = [ + "## 安全渲染", "", + '', "", + '', "", + '', "", + '原始链接', "", + "[危险脚本](javascript:alert%281%29)", "", + "[危险数据](data:text/html;base64,PHNjcmlwdD4=)", "", + "[安全链接](https://example.com/safe)", + ].join("\n"); + await openHistory(); + expect(texts(host, "h2")).toContain("安全渲染"); + // 排除活动 HTML 节点及危险 href,不能仅凭 happy-dom 禁用脚本就判定安全。 + // + // Exclude active HTML nodes and unsafe hrefs instead of relying only on happy-dom disabling scripts. + expect(host.querySelector("script, iframe, img, #raw-agent-html, [onerror], [onclick]")).toBeNull(); + expect(Reflect.get(browser, "__agentMarkdownExecuted")).toBeUndefined(); + expect(host.textContent).toContain("危险脚本"); + expect(host.textContent).toContain("危险数据"); + const unsafeLinks = Array.from(host.querySelectorAll("a[href]")) + .filter((link) => /^(?:javascript|vbscript|data):/i.test(link.getAttribute("href")!.trim())); + expect(unsafeLinks).toHaveLength(0); + const safeLink = Array.from(host.querySelectorAll("a")).find((link) => link.textContent === "安全链接"); + expect(safeLink?.getAttribute("href")).toBe("https://example.com/safe"); +}); + +test("继续聊点击建议问题后,新的 Agent 回复也渲染三列表格而非原始管道文本", async () => { + await openHistory([]); + expect(host.querySelectorAll("table")).toHaveLength(0); + // 通过建议问题的真实点击验证发送链路,避免 textarea 合成事件受 React DOM 模块加载顺序影响。 + // + // Exercise sending through a real starter click, avoiding synthesized textarea events sensitive to React DOM import order. + const question = "结合我的错题,帮我安排一周复习计划"; + const starter = Array.from(host.querySelectorAll("button")) + .find((button) => button.textContent === question); + expect(starter).toBeDefined(); + await act(async () => { starter!.click(); }); + await settle(); + expect(requests).toContain("POST /api/personal-agent/chat"); + expect(sentMessages).toEqual([{ message: question, conversation_id: conversationId }]); + expect(host.textContent).toContain(question); + expect(texts(host, "h2")).toContain("后续学习安排"); + expectThreeColumnTable(); +});