Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Agent 笔记:Markdown 离线化学公式与下标

状态:已实现

## 先说结论

macOS 已有数学公式和 Mermaid 图表的离线引擎,不需要再造解析器或接在线服务。
本次补上 KaTeX 的化学扩展 mhchem,使 `\ce{...}` 反应式和 `\pu{...}` 物理单位可用,
并启用 Comrak 现成的下标扩展。macOS PlantUML 已由独立改动接入,见
[离线 PlantUML 预览](2026-11-01-macos-offline-plantuml.md);Windows 方言对齐等仍未完成,
不能因这些 macOS 增强而把 Issue #706 当作整体完成。

## 问题

KaTeX 是把 LaTeX 数学语法转换为网页公式的成熟库,但基础库不包含 mhchem,
后者是负责解析化学反应和单位的可选扩展。只打包基础库时,数学公式能显示,
`\ce{H2O}` 却报未定义命令。共享 Markdown 解析器 Comrak 已支持上下标,
此前只启用了上标,`H~2~O` 不能正确输出下标。

## 决策

- 从同版本 KaTeX 0.16.0 npm 分发包提取 mhchem,先验证整个归档的 SHA-512 完整性,
再固定脚本 SHA-256,保留源文件中被压缩分发去掉的版权和适配声明。
- 模板按 KaTeX、mhchem、预览脚本顺序加载。现有公式渲染器继续使用 `trust: false`,
不接受公式引入脚本、网页或网络资源;运行时不下载任何依赖。
- 开启 Comrak 的 `subscript`。单波浪线表示下标,双波浪线仍表示删除线,
代码中的字面语法保持原文。正确例子是 `H~2~O`,不要用正则替换全文中的 `~`,
否则代码、删除线和公式会被误改。
- 共享输入加真实 WebKit 浏览器引擎回归,同时检查数学、化学、Mermaid 生成的 SVG、
脚注及回链,不能仅断言代码块带有语言类名就声称图表可用。

## 考虑过的备选方案

- 重新实现化学语法:已有与当前 KaTeX 完全匹配的成熟上游,不采用。
- 运行时从 CDN 加载:离线失败且扩大网络和供应链边界,不采用。
- 顺手升级 KaTeX、Mermaid 或把 Windows 解析器整体迁移:增加无关兼容性风险,
本批只填已证实缺口,不把两个平台现状混为一致。
- 直接接入在线 PlantUML 服务器:会把用户文档发送到第三方,也不能离线,不采用。
PlantUML 的独立上游、分发和执行隔离由上述独立决策验证,不复用公式渲染的执行上下文。

## 后果

数学公式、化学反应、单位和上下标可以在无网络预览中共存,新增脚本与已有 KaTeX 版本绑定。
代价是需要随 KaTeX 更新一起核对扩展、许可、来源哈希及真实引擎回归。
GitHub 特有媒体及 Windows 方言对齐不是本批已交付能力;macOS PlantUML 的能力和限制以独立笔记为准。

这些模板和第三方脚本是 Git 输入,构建复制后再签名,运行时只读加载。
渲染状态仅在内存和非持久 WebKit 中存活,不新增缓存、下载或安装目录写入;
资源复用清单显式排除整个 Markdown 预览资源,不跨工作树复制生成 bundle。
这不改变代码签名或 Sparkle 增量更新所依赖的发布字节。

## 验证

- `cargo test --manifest-path rust/Cargo.toml -p lithe-core project::markdown::tests`。
- `./.agents/skills/write-stable-tests/scripts/test-stability-macos.sh -- --filter MarkdownPreviewRichContentTests`:
深浅主题使用生产 Rust bridge 和打包模板,检查公式与可访问数学标记 MathML、图表、脚注和代码,
并比较运行前后全部预览文件 SHA-256,检查没有远程资源请求。
- `node --test scripts/resources/test-reuse-worktree-resources.mjs`:拒绝跨工作树复制预览资源。
- `./scripts/macos/verify/verify-runtime-bundle-immutability.sh`。

原生组件测试不代替完整应用操作验收,Windows 运行时未在本批验证。

## 适用范围

- `rust/lithe-core/src/project/markdown.rs`
- `macos/Sources/Lithe/Resources/MarkdownPreview/`
- `macos/Tests/LitheTests/MarkdownPreviewRichContentTests.swift`
- `shared/fixtures/editor/markdown-rich-content-v1.json`
- `scripts/resources/worktree-resources.json`
7 changes: 7 additions & 0 deletions docs/ci-builds.md
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,13 @@ Inter 4.1 的 18 个静态 OTF(内部版本 4.001)及许可、JetBrains Mono

以下目录不应直接复制或跨工作树共享:

- Markdown 离线预览的模板、脚本、样式、字体和许可来自 Git 跟踪的
`macos/Sources/Lithe/Resources/MarkdownPreview/`。KaTeX 0.16.0 的 mhchem 扩展与现有
KaTeX 版本匹配,npm 归档完整性及文件 SHA-256 固定在 `THIRD_PARTY_NOTICES.md`。
输入与平台架构、工具链无关;构建时从当前 checkout 复制进 bundle 后签名,运行时仅只读加载,
不下载、不写回、不产生持久化渲染缓存,不影响签名和 Sparkle delta。生成 bundle 没有可靠的
跨 worktree identity stamp,`excludedResources.markdown-preview-assets` 走资源脚本的
通用拒绝路由,任何复制阶段都不得从其他工作树或安装 app 复用这些资源。
- macOS Markdown PlantUML:Git 中的 `MarkdownPreview/vendor/plantuml/`、
`plantuml.html` 与 `plantuml-renderer.js` 随当前 checkout 打包,来源为固定的
MIT `@plantuml/core` 1.2026.9,归档 SHA-512、引擎 SHA-256 和上游 revision 见
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ All preview dependencies are bundled with Lithe and loaded from the application

- Highlight.js 11.5.1 — BSD-3-Clause — `vendor/highlight-LICENSE`
- KaTeX 0.16.0 and its fonts — MIT — `vendor/katex-LICENSE`
- KaTeX 0.16.0 mhchem extension (mhchem 3.3.0 adaptation) — MIT / upstream Apache-2.0 notice — `vendor/mhchem-NOTICE`
- Mermaid 11.14.0 — MIT — `vendor/mermaid-LICENSE`
- D3 6.7.0 — ISC — `vendor/d3-LICENSE`
- markmap-lib 0.14.1 — MIT — `vendor/markmap-lib-LICENSE`
Expand All @@ -12,6 +13,8 @@ All preview dependencies are bundled with Lithe and loaded from the application

The Highlight.js browser bundle is the distribution already vendored by MiaoYan; its version banner and upstream license are preserved. The remaining files come from the matching npm distributions.

`vendor/mhchem.min.js` comes from `https://registry.npmjs.org/katex/-/katex-0.16.0.tgz` (`package/dist/contrib/mhchem.min.js`), matching the bundled KaTeX runtime. The archive is verified with npm's SHA-512 integrity `sha512-wPRB4iUPysfH97wTgG5/tRLYxmKVq6Q4jRAWRVOUxXB1dsiv4cvcNjqabHkrOvJHM1Bpk3WrgmllSO1vIvP24w==`; the extracted file's SHA-256 is `5c0a121a8b490afc85860a522347aeb34fb508c6b23044e5d29f6b2194227b51`. The source copyright and adaptation notice are preserved separately because the minified distribution omits that header. This is a checked-in build input, never downloaded or modified at runtime.

PlantUML uses the official JavaScript build's built-in Smetana layout engine. Graphviz/Viz.js, the URL encoder, optional standard libraries, themes, emoji and external include loaders are not bundled. Rendering is local in a nonpersistent, restricted WebView; the result is displayed as an SVG image, never injected into the document DOM.

`vendor/plantuml/plantuml.js` comes unchanged from `https://registry.npmjs.org/@plantuml/core/-/core-1.2026.9.tgz` (`package/plantuml.js`, upstream Git revision `18d0aae311a4f1eb3723bb4b0ad939185e22f166`). npm archive integrity: `sha512-Qy46og9hEb+wOFtjUfniWkl/BDwHpDuebQX5rF+35s9PS2G4cKZf1iWw4E/ELLQ0pTkESPDoQTbKWcc3ZyVcVA==`. Script SHA-256: `21c049ce47d087eff61ae0836f9c62a2b0c0d2528a5f16c2247057544228fae7`. The npm release's MIT license and upstream notices are preserved in `vendor/plantuml/LICENSE` (line endings normalized to LF only). Versions before 1.2026.6 use different licensing and must not be substituted. These are Git-tracked build inputs, not runtime downloads or writable bundle state.
1 change: 1 addition & 0 deletions macos/Sources/Lithe/Resources/MarkdownPreview/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<link rel="stylesheet" href="preview.css">
<script defer src="vendor/highlight.min.js"></script>
<script defer src="vendor/katex.min.js"></script>
<script defer src="vendor/mhchem.min.js"></script>
<script defer src="vendor/mermaid.min.js"></script>
<script defer src="vendor/d3.min.js"></script>
<script defer src="vendor/markmap-lib.min.js"></script>
Expand Down
Loading
Loading