diff --git a/sundynix-desktop/frontend/src/components/Markdown.tsx b/sundynix-desktop/frontend/src/components/Markdown.tsx
new file mode 100644
index 0000000..ef842c7
--- /dev/null
+++ b/sundynix-desktop/frontend/src/components/Markdown.tsx
@@ -0,0 +1,112 @@
+import { type ReactNode } from "react";
+
+// 轻量 Markdown 渲染 —— 零依赖、行级解析,覆盖报告正文用到的子集:
+// # / ## / ### 标题、**粗** *斜* `码`、- 与 1. 列表、> 引用、--- 分隔、段落。
+// 流式安全:每个 token 重渲染,残缺语法也能容忍。
+
+// 行内:把一段文本切成 **粗** / *斜* / `码` / 纯文本节点。
+function inline(text: string, keyPrefix: string): ReactNode[] {
+ const out: ReactNode[] = [];
+ const re = /(\*\*([^*]+)\*\*|`([^`]+)`|\*([^*]+)\*)/g;
+ let last = 0;
+ let m: RegExpExecArray | null;
+ let i = 0;
+ while ((m = re.exec(text)) !== null) {
+ if (m.index > last) out.push(text.slice(last, m.index));
+ const key = `${keyPrefix}-${i++}`;
+ if (m[2] !== undefined) out.push({m[2]});
+ else if (m[3] !== undefined) out.push({m[3]});
+ else if (m[4] !== undefined) out.push({m[4]});
+ last = re.lastIndex;
+ }
+ if (last < text.length) out.push(text.slice(last));
+ return out;
+}
+
+export function Markdown({ text, className }: { text: string; className?: string }) {
+ const lines = text.replace(/\r\n/g, "\n").split("\n");
+ const blocks: ReactNode[] = [];
+ let para: string[] = [];
+ let list: { ordered: boolean; items: string[] } | null = null;
+ let k = 0;
+
+ const flushPara = () => {
+ if (para.length) {
+ blocks.push(
+
+ {inline(para.join(" "), `p${k}`)} +
, + ); + para = []; + } + }; + const flushList = () => { + if (list) { + const items = list.items.map((it, idx) => ( ++ {inline(line.replace(/^>\s?/, ""), `q${k}`)} +, + ); + } else if (ol) { + flushPara(); + if (!list || !list.ordered) { + flushList(); + list = { ordered: true, items: [] }; + } + list.items.push(ol[1]); + } else if (ul) { + flushPara(); + if (!list || list.ordered) { + flushList(); + list = { ordered: false, items: [] }; + } + list.items.push(ul[1]); + } else { + flushList(); + para.push(line); + } + } + flushPara(); + flushList(); + + return
{out}
+ {run.output}
+