# Changelog ## 0.1.1 — 2026-08-24 ### 审计补缺轮(功能缺陷修复) - **设置项死绑定修复**:设置页保存的「默认导出格式 / 默认时间戳 / 导出主题」此前从未传入导出弹窗(弹窗 useState 硬编码 md/true/auto),现已通过 inject face 直读快照作为弹窗初始值(惰性求值,不引入渲染期订阅,规避 React #185) - **块级公式定界符修复**:PNG / PDF 此前只识别 `$…$`,`\[…\]` 与 `$$…$$` 块级公式会以字面量泄漏(PDF 中不转 MathML、PNG 中不光栅化);现导出前统一经 `normalizeMathDelimiters` 归一化 - **PNG 中文排版修复**:`wrapText` 重写为按实测宽度的贪心断行(CJK 逐字、西文整词、超长无空格串硬切),修复中英混排溢出画布;高度估算改用视觉宽度单位(CJK 记 2),修复纯中文长会话因估算偏低被**静默截断** - **blob URL 泄漏修复**:`resolveImages` 创建的 objectURL 在导出完成 / 取消 / 失败路径统一回收(PDF 路径延迟至 iframe 兜底清理之后) - **公式契约漂移修复**:`export-settings.ts` 的 `FormulaFormat` 含 `'plain'` 而 client 模板表为 `'display'`,两份联合类型不一致;已对齐为模板表 9 值,并新增契约守护测试 - **恢复设置加固**:备份文件解析改走 `coerceBackup` 白名单校验,非法枚举值直接丢弃(不再污染 select 状态);设置页「公式复制格式」下拉与浮层对齐开放全部 9 种格式 - **i18n 补齐**:弹窗范围提示 / 取消 toast / 处理中 / PNG 回退提示、设置页归档按钮与说明等硬编码中文全部走词典(zh/en 同步) - **杂项清理**:公式浮层「点击外部关闭」监听器随 closeMenu 一并移除(防滞留累积);删除无效的 `.replace('LaTeX','LaTeX')`;PDF 打印前等待 iframe load 事件(1.5s 兜底)替代固定 450ms;构建补齐 `lib/types` 声明产物(package.json 早已声明却从未生成) ### 工程与文档 - `.gitignore` 补 `.pnpm-store/`(pnpm 缓存误落仓库目录);README 移除 PLAN.md 死链(内部文档不入库);测试 31 → 41 全绿,typecheck / build 全绿 ## 0.1.0 — 2026-08-22 ### 真机事故修复 + 第三轮契约审计(2026-08-22 晚) **真机事故** - **dsh 启动崩溃(ERR_MODULE_NOT_FOUND)**:交付缺 host 半产物 `lib/index.js`(build.mjs 的 host 自检是假的,node 无法直接 import .ts 被静默吞掉)→ build.mjs 改为真构建(esbuild bundle `src/index.ts` → ESM,动态 import 保留)+ 真实加载自检;lib 产物入库;bundle 测试校验 main 指向 - **React #185 无限渲染**:自定义 `useSession`(useSyncExternalStore + 不稳定 getSnapshot)覆盖了宿主 framework session kit → 删除自研 hook,改用宿主注入的稳定 kit(ui-conversation slots.d.ts 契约);导出链路全走 `readSnapshot()` 一次性读取 **采集层契约重写(第三轮审计实锤——原实现对宿主数据结构假设全错,真机导出必为空)** - `chat.nodes` 是 **ChatNodeStore 接口 `{get(key), values()}`**,非 Map/Record → 取节点改为方法调用优先 + values()/索引兜底 - 业务负载在 `node.data`(ConversationViewNode{key,kind,id,target,data,visibility}):user/steering/context 为顶层 `content: ContentBlock[]`(type 标签),assistant 为 **`blocks: AssistantBlock[]`**(kind 标签),时间在节点顶层 `time` - 图片块为 `{image, attachment: ImageAttachmentRef{attachmentId,mediaType,width,height,name}}`,无 url/alt → CollectedTurn.images 改为 ImageRef 结构;PNG/PDF 在每轮尾部按 attachmentId 渲染真图/占位 - `readAttachment(id)` 返回 **RpcResult**(`{ok:true,value:{attachment,data:Uint8Array}}`)非 Blob → 解包后 `new Blob([data],{type:mediaType})` + objectURL;装饰图过滤改用 ref 自带 width/height(<48px),不再加载测量 - reasoning 块不进导出正文;tool-call 渲染为摘要行;hidden 节点跳过;快照无 title → 新增 `getTitle()` 从 sessions list 行解析 - 测试 fixture 全部按真实契约重写(ChatNodeStore.get 接口 + data 负载 + blocks/content 双形态 + hidden/unknown/tool-call 用例),31 tests green ### 审计修复(2026-08-22 第二轮) - **temml 真正内联**:原先 `require('temml')` 动态调用不被 esbuild 打包(bundle 仅 66k,运行时必然落空)→ 改为静态 `import temml from 'temml'`,公式光栅化 / PDF MathML / Word MathML 全链路可用 - **渲染失败语义修正**:temml 对非法 LaTeX 不抛错而是返回 `temml-error` 包装 → `renderLatexToMathML` 检测错误包装并回退 LaTeX(新增单测) - **设置行 hooks 命名修正**:`SettingsRow` 原用 `useExportKitSettings` 但注册面是 `hooks:{defaultFormat, includeTime, theme}` → slot 只注入 `useDefaultFormat/useIncludeTime/useTheme`;已按 turn-rail 模式改为直读 hook,并补齐 `useFormulaFormat`(设置页公式默认格式下拉,联动浮层首项) - **分页闭包修复**:`collectAllHistory` 改实时快照 + stall 检测(连续 2 轮 order 无增长即停)+ loading 卡死上限(25 轮 ≈ 2s) - **图片解析接入**、**PDF iframe afterprint 生命周期**、**全会话归档 skipped 标记**、**typecheck 全绿**(rc.2 类型 devDeps)、移除 vitest oxc 冲突配置 ### M1~M10 主体(2026-08-22 首轮) - **M1 Scaffold**: `package.json` (`dsh.bundle` + `dsh.client`), `cordis.patch.yml`, `tsconfig.json`, `vitest.config.ts`, `pnpm-workspace.yaml` (`allowBuilds: {esbuild: true}`), esbuild envelope `lib/client.js` - **M2 Collect**: `chat.order/nodes` → `CollectedTurn`, `resolveImageBlobs` (concurrency 4, <48px decoration filter), `normalizeMathDelimiters` (code fences untouched) + `markdownToPlainText`, `collectAllHistory` (stall/loading guards) - **M3 Exporters**: MD/TXT/JSON/CSV pure functions (time toggle, image placeholder, empty-turn handling, `sanitizeFilename`/`formatExportTime`/`buildExportFilename`, CSV RFC4180 + BOM) - **M4 UI**: `conversation.session.header.actions` export button (`order:10`), `ExportModal` (6 formats, `range: all/visible`, `includeTime`, `theme auto/light/dark`, progress bar + cancel via `AbortController`, `showToast`) - **M5 PNG**: header theme color, `parseMarkdownBlocks`, formula rasterization `temml→MathML→SVG foreignObject→canvas` (fallback LaTeX, taint degraded), `CE_MAX_CANVAS_HEIGHT=32000` truncation - **M6 PDF**: hidden iframe HTML template, `triggerPdfPrint`, MathML via temml fallback `` - **M7 Formula**: 4-level extraction, 9 templates, `latexToMathML`/`latexToWordMathML` (`mml:` prefix), `installFormulaOverlay` (event delegation, Esc close, selected-text guard) - **M8 Backup**: Settings JSON backup/restore + bulk archive (session list → per-session JSON) - **M9 Settings**: `settings.general.item`, `createSnapshotStore` binding, `zh/en` locales - **M10 Tests/Delivery**: 26 vitest tests + bundle smoke, docs (zh/en README, CHANGELOG, screenshot placeholder), git commits per milestone, no `dsh plugin add` / restart