Skip to content

Development and Release

zhangxh edited this page Aug 17, 2026 · 5 revisions

开发与发布

本页说明 TeXLeaf 1.0.0 的源码结构、验证门禁、跨平台验收和 Marketplace/GitHub Release 流程。贡献代码前请同时阅读 片段与模板AI 写作助手文献与 ZoteroMath Preview致谢与联合开发,避免破坏已经明确的产品边界。

环境要求

  • Node.js 20 或兼容当前 VS Code Extension Host 的版本;
  • pnpm;
  • VS Code 1.98+ 桌面版;
  • Git;
  • 进行 Zotero 集成验收时需要 Zotero 桌面端,推荐 Better BibTeX;
  • 进行 GitHub 发布时可使用 GitHub CLI gh,也可通过网页创建 Release。

Windows、macOS 和 Linux 都是运行时支持平台。核心 TypeScript、Node Worker、VS Code API 和 Workspace FS 没有把扩展运行时绑定到 Windows 路径;平台特定内容主要位于测试启动器和人工视觉自动化。

获取源码

git clone https://github.com/zhangxh-math/texleaf.git
cd texleaf
pnpm install

仓库路径可以包含空格或非 ASCII 字符,但脚本和 CI 中必须把路径作为独立参数传递,不要用未引用的字符串拼接 shell 命令。

常用脚本

命令 作用
pnpm run check tsc -p tsconfig.json --noEmit 主源码静态检查
pnpm run test:compile 编译测试 TypeScript 到 .test-dist
pnpm test 运行不启动 VS Code 的核心、Snippet、模板、AI 正文/DeepSeek/OpenAI 客户端、citation、Zotero、Math Preview 单元/集成测试
pnpm run compile 用 esbuild 生成 dist/extension.jsdist/mathPreviewWorker.js
pnpm run test:bundle 检查扩展 bundle 自包含加载和 Math Preview Worker
pnpm run test:extension-host 构建后在隔离 VS Code Profile 运行 Extension Host 回归
pnpm run verify check + tests + compile + bundle smoke
pnpm run release:verify verify + Extension Host 回归
pnpm run package 先过 release:verify,再用 vsce 创建 VSIX
pnpm run watch 开发时持续构建 extension/worker bundle

发布前必须至少运行:

pnpm run release:verify
pnpm run package

不要直接调用 vsce package 绕过测试门禁后就发布。

架构概览

入口与配置

  • src/extension.ts:激活、命令、控制器和生命周期;
  • src/config.ts:52 个设置的读取、兼容值、作用域和安全规范化;其中 14 个 AI 设置只读取 application 级用户/Profile 值;
  • package.json:四组 Settings、命令、键位、工作区信任和 language defaults。

片段与模板

  • src/core/latexScanner.ts:共享的 TeX 文本/数学/环境扫描器;
  • src/defaultLibrary.tssrc/defaultSnippets.ts:工厂规则;
  • src/snippetRuntime.ts:匹配和 replacement 规划;
  • src/editorController.ts:输入事件、自动展开、分数、Tabout、矩阵快捷键;
  • src/snippetRepository.ts:Profile 内部 JSONC、迁移、CAS、备份和原子替换;
  • src/snippetSync.ts:Settings Sync envelope、lineage 和冲突处理;
  • src/snippetEditorPanel.tssrc/snippetManagerWebview.ts:结构化管理器 host/Webview;
  • src/templateLibrary.tssrc/templateManager.ts:内部模板目录、验证和运行时展开;
  • templates/*.tex:四个出厂模板种子,不是用户运行时可编辑外部依赖。

文献

  • src/core/citation.ts:citation 上下文、逗号分段、BibTeX 解析和范围,以及 citation key/标题/作者/年份/DOI/ISBN 的规范化多词 AND 搜索、相关度分级与稳定排序;
  • src/referenceMatcher.ts:bibliography/Zotero 文献身份、DOI/ISBN 规范化、跨 key 复用和同 key 元数据冲突;
  • src/citationRepository.ts:安全解析 bibliography URI 和 VS Code 文档模型;
  • src/zoteroClient.ts:固定回环 Better BibTeX JSON-RPC 与 Zotero Local API;
  • src/citationController.ts:bibliography 与 Zotero 全库本地快照、最多 100 条的末端截断、原生 Suggest、缓存、接受和原子导入。

TeXLeaf citation item 左侧是标题+来源,detail 保持空,右侧 Markdown 分字段显示作者、出版物、年份、Citation key、来源和操作状态。改变 UI 时必须同时覆盖:左侧无 key、右侧保留 key、顶部无重复 metadata summary、key 仍作为 insertText。搜索契约还必须保持:只搜索 key/标题/作者/年份/DOI/ISBN;多词去重后按 AND 跨字段命中;不搜索 container/摘要/标签/笔记且不做拼写纠错;相关度依次区分精确原始 key、紧凑 key/前缀、精确 DOI/ISBN、全词/词首和子串,相关度相同时才偏好 bibliography;全库本地排序后才截取最多 100 条。

AI 写作

  • src/core/aiProse.ts:正文 allowlist、fail-closed TeX 遮罩、受保护的等长 inline/display formula 语义占位、UTF-16 映射、中文无空格句子边界和模型 issue 安全规划;
  • src/core/aiIssueRetention.ts:多 contentChanges 事务重建、old ∪ new 句子上下文与 precise dirty-range 失效分离、split/merge/零宽/累计 pending 投影、同句非相交问题严格平移和 fail-closed 边界;
  • src/core/aiIssuePersistence.ts:本机问题快照 schema、SHA-256 exact-source 验证、逐条字段/TeX/可编辑区校验和 2048 条/2 MiB 上限;
  • src/aiIssuePersistenceStore.ts:Profile-local globalStorage 的 750 毫秒防抖、临时文件+rename best-effort 原子写、停用 flush,以及 256 文档/32 MiB 清理;不使用 Settings Sync;
  • src/ai/issueLocation.ts:把 UTF-16、Unicode code point、UTF-8 byte 与 CRLF 换行计数的模型坐标收敛到唯一逐字源码范围,不执行 Unicode/空白归一化或模糊匹配;
  • src/ai/deepseekClient.ts:DeepSeek Chat Completions Base URL 规范化、非思考 JSON 请求、空/无效 JSON 单次重试、严格单层完整 JSON fence、响应结构/长度/用量验证和安全错误子码;
  • src/ai/openaiClient.ts:OpenAI Responses + Structured Outputs 请求、默认 gpt-5.6-luna、Base URL 规范化、store:false、响应/refusal/incomplete 校验和脱敏错误;
  • src/aiIssuesTree.ts:活动栏“AI 写作问题”的检查/pending 状态、列表项、真实严重性、拒绝摘要、只滚动不移动主光标的源码定位和单条操作;模型文字始终按不受信任文本显示;
  • src/aiWritingController.ts:consent、SecretStorage、workspace trust、编辑器装饰线、专用 Hover/Quick Fix/问题树、改写、行内补全、默认 900 毫秒防抖、取消、同版本同句子去重、逐句提交、同句非相交问题保留、问题快照恢复、8/64/32 请求上限、批量应用确认和过期结果拒绝;AI 问题不发布原生 Diagnostic;
  • test/aiProse.test.tstest/aiIssuePersistence.test.tstest/deepseekClient.test.tstest/openaiClient.test.ts:遮罩/公式占位/映射、细粒度增量保留、exact-source-only 持久恢复、fail-closed、双 Provider 请求体、中文说明/原语言替换合约、URL/模型、错误分类和恶意/无效响应测试。

AI 网络入口必须继续满足:默认关闭、受信任窗口、已有文件名和路径的 file:/vscode-remote: .tex、LaTeX/TeX language ID、当前 Provider/规范化 endpoint 专用 consent 和当前环境 SecretStorage Key。这里使用当前编辑器内存正文,可能包含尚未落盘的编辑。DeepSeek 只调用规范化 Base URL 下的 /chat/completions;OpenAI 只调用规范化 Base URL 下的 /responses,两者不跨协议回退。每个规范化 DeepSeek/OpenAI URL 必须独立派生 Secret/consent;默认 DeepSeek 官方 URL 继续兼容旧 v1 记录,自定义 DeepSeek URL 绝不能复用。测试不得使用真实 API Key 或依赖实际 AI 网络。

DeepSeek 实现基线以官方 Create Chat CompletionJSON Output 文档为准,自定义地址只承诺兼容这套 Chat Completions 请求契约。OpenAI 实现基线以官方 Responses APIStructured Outputsgpt-5.6-luna 文档为准;自定义地址只承诺兼容这套 Responses 请求契约,不承诺兼容任意“OpenAI-compatible” Chat Completions 服务。

Math Preview

  • src/core/mathPreview.ts:公式快照、宏解析、光标安全边界;
  • src/mathPreviewController.ts:防抖、缓存、Worker、Hover、decoration 和过时结果丢弃;
  • src/mathPreviewWorker.ts:MathJax SVG 渲染与消毒;
  • src/mathPreviewLayout.ts:inline/block 锚点和上下方规划;
  • src/mathPreviewCard.tsmathPreviewAppearance.tsmathPreviewDataUri.ts:卡片几何、主题和安全数据 URI。

Worker 与 extension 分别 bundle;VSIX 中不包含 src/test/ 或 node_modules 散目录。

F5 调试

  1. 用 VS Code 打开仓库根目录;
  2. 运行 pnpm run watch
  3. F5 启动 Extension Development Host;
  4. 在宿主中新建并先保存 .tex
  5. 分别验证 lmdm\thm、管理器、\cite{}、公式预览,以及在不含隐私的临时 .tex 中验证 AI 默认关闭与作用域;
  6. 修改源码后重新启动调试宿主。

建议使用独立 Profile/临时工作区,避免开发版本迁移真实个人 Snippet 库。

自动化测试层次

纯测试

覆盖:

  • TeX 扫描、数学上下文、标签抑制;
  • trigger 匹配、priority、正则、v1/v2 占位符;
  • 默认 212 条 Snippet 与四个模板;
  • 管理器生成 HTML、CSP、ready 握手、表单和批量替换;
  • citation 分段、BibTeX 解析、key/标题/作者/年份/DOI/ISBN 查询、多词去重 AND 跨字段命中、相关度/来源平局排序、全库后截断和去重;
  • Zotero JSON-RPC/Local API、library、导出与错误映射;
  • AI 正文 allowlist/fail-closed 遮罩、公式等长语义占位、UTF-16/code point/UTF-8/CRLF offset 精确重定位、逐条响应校验、DeepSeek/OpenAI 请求契约、auto/English/Chinese 有界语言标签、简体中文说明/原语言 replacement、Base URL/Secret 身份隔离、脱敏安全子码,以及自动检查去重/取消、句子上下文+问题级增量保留、部分成功提交、无原生 Diagnostic UI、exact-source-only 本机问题恢复与活动栏 pending 问题树模型;
  • Math Preview 扫描、宏、光标、SVG、主题和布局。

测试数量会随版本增加,不应在 Wiki 固定为永久不变的数字;1.0.0 发布候选应以当次 pnpm test 的全绿结果为准。

Bundle smoke

test/bundle-smoke.cjs 检查生产 extension bundle 在没有开发源码/散依赖时可以加载;test/math-preview-worker.cjs 直接与 Worker 通信,覆盖 SVG、安全、Unicode、宏隔离、并发和错误恢复。

Extension Host

test/run-extension-host.cjs 创建唯一临时目录、隔离 user-data/extensions、双根 workspace,并禁用其他扩展后运行 test/extensionHost.cjs。覆盖真实 VS Code API 行为:

  • 激活与命令/设置清单;
  • 片段、模板、键位、迁移、备份、Sync;
  • 管理器相关入口;
  • 原生 citation label/detail/documentation/range/filter、完整本地候选池排序、100 条上限和逐键不请求 Zotero;
  • 普通片段 Suggest 的全局最长非零 trigger 前缀组过滤、所有已完整输入的 literal trigger 额外保留、仅 runtime 唯一 exact literal 获得 Keyword/preselect/exact sort,以及 Suggest 可见时“真实 Tabout 目标优先、否则接受所选补全”的 Tab 分流;Matrix/Align 还必须验证本地 Tabout 优先于插列,且自动放大与 Tabout 都不跨 & / TeX 行结束命令;
  • AI 命令(含显示问题列表/应用全部)与 14 项 application 级设置清单、900 毫秒默认检查防抖、默认关闭、受信任 .tex 作用域、双 Provider 和无对应 Key/consent 时不联网;工作区设置不能开启、重定向、换模型或改变费用相关参数;
  • Math Preview Hover、安全 SVG 和主题;
  • Math Preview cursor/both 的 last-known-good、稳定 decoration 原位换帧、750 ms 失败宽限、按需 Hover 资产和终止状态清理;
  • Math Preview autoBelow 下方优先、autoAbove 上方优先、首选侧不足时的换边、上下都不足时共用上方末尾保留策略、显式 above/below 固定方向,以及旧 autoautoBelow 的运行时兼容映射;
  • .tex/.bib/Untitled/多根作用域。

临时目录删除前会验证它确实是由本次 mkdtemp 创建的目标,避免递归删除错误路径。

Math Preview 视觉验收

仓库提供固定场景:

  • inline
  • multiline-inline
  • display
  • nested-display
  • tall-display
  • typing-stability

Windows 开发机可运行:

node .\test\run-math-preview-visual-host.cjs --scenario multiline-inline --placement autoBelow --theme dark
node .\test\run-math-preview-visual-host.cjs --scenario multiline-inline --placement autoAbove --theme light
node .\test\run-math-preview-visual-host.cjs --scenario nested-display --placement autoBelow --theme light --debug-port 9339
node .\test\math-preview-cdp-check.cjs --port 9339 --scenario nested-display
node .\test\run-math-preview-visual-host.cjs --scenario typing-stability --placement autoBelow --theme dark --debug-port 9341
node .\test\math-preview-cdp-check.cjs --port 9341 --scenario typing-stability

CDP 只绑定 127.0.0.1。几何场景检查真实 Monaco ::beforenested-display 要求卡片与 opening delimiter 误差不超过 3 px,tall-display 要求高度未被错误压缩并保持上方尾部策略。typing-stability 在专用临时夹具中连续输入并采样,要求防抖/渲染期间没有零 decoration 空帧、成功结果原位换帧、过时失败不能清除新输出,并验证离开公式、禁用/Dismiss 与停在无效状态超过 750 ms 后仍会清理。

这些视觉辅助脚本当前以 Windows 的 Code.exe/窗口环境作为主要、经过验证的自动化入口;虽然部分 runner 可通过环境变量指向其他可执行文件,但不能把它当作已完成的 macOS GUI 自动化。test/ 和这些脚本由 .vscodeignore 排除,不会进入 VSIX,因此不影响 macOS/Linux 运行时兼容性。

macOS 实机发布验收

每个 Release 至少应在真实 macOS VS Code 做一次人工检查:

  1. 从最终 VSIX 安装,而不是源码 F5;
  2. 在 Apple Silicon(可用时也抽查 Intel)打开已保存 .tex
  3. 验证 Cmd+Alt+L、Tab、Enter、Shift+Enter、Escape;
  4. 展开 lmdm\thm 和四个模板;
  5. 在本机 Zotero/BBT 中测试已有与未导入条目、多 key citation;
  6. 深色/浅色、Retina 和系统推荐缩放下检查行内活动行对齐、行间 opening 对齐、鲜艳光标和不透明卡片;
  7. 检查 Hover fallback、超宽裁切与超高尾部;
  8. 确认没有调用 Windows 路径、PowerShell、Code.exe 或测试脚本。

Linux 也应至少在一个常见发行版/桌面会话做安装、片段、引用回环和预览冒烟,特别注意 Snap/Flatpak 沙箱与 Wayland 分数缩放。

片段与 Tab 手工验收

  1. 在数学区域输入 +,确认可看到 +-;继续输入成 +1,刷新后的 Suggest 不得继续保留该 TeXLeaf 条目。
  2. 输入 ss,确认保留匹配两个字符的 SS2SSESSPSSS,不混入只匹配末尾单个 ssumsimsubsup;退回单个 s 和输入完整 trigger 时仍正常。另覆盖 regex shadow、重复 ID 和“完整 literal 不属于更长 partial group”:所有已完整输入的 literal trigger 都必须额外保留,但只有 runtime 唯一选中的 exact literal 获得 Keyword 类型、preselect 与 exact sort,其余保持普通 Snippet 排序。
  3. \(...^{+}\) 中把光标放在 + 与指数右花括号之间,确认 +- 已在 Suggest 中选中后按 Tab;应越过真实右括号,不得接受该候选。
  4. $+x$ 中把光标放在 +x 之间;+- 已选中时按 Tab 应接受该原生补全,因为右侧普通公式内容使当前位置没有真实 Tabout 目标,不得吞键或插入缩进。
  5. 数学区域的活动 Snippet Session 中显示竞争 Suggest,确认 Tab 先关闭 Suggest、再前往下一 tabstop;完整 TeXLeaf trigger、Inline Suggest 与 Rename 输入框分别保持既有优先级。Matrix/Align 中当前单元格有真实闭合符时先 Tabout,没有目标且 Suggest 可见时接受候选,没有 Suggest 时才插列。
  6. Ctrl+Alt+L(macOS 为 Cmd+Alt+L),确认没有输入 trigger 前缀时仍可浏览和插入当前上下文可直接插入的普通片段。
  7. $()$ 的圆括号内输入 sum,确认得到 $\left(\sum|\right)$;再输入 + 使当前位置没有精确手动片段 trigger,按一次 Tab 应跳到 \right) 后。刚停在 \sum 后直接按 Tab 时,既有 sum-limits 手动片段优先展开属于预期;本身带 tabstop 的片段不得被合成占位符打乱顺序。
  8. align* 中把普通 () 分放到未转义 &\\\cr\crcr\tabularnewline 两侧,再于其中输入 sum:不得生成跨单元格/跨行 \left...\right,光标应停在局部 \sum 后。另在 \frac{1}{n^{2}|} 中按 Tab:第一下越过分母 } 且不插 &,下一下无局部闭合符时才插列;转义 \& 与注释中的边界同形文本不得误阻断。

管理器手工验收

  • 全新 Profile 打开管理器,加载 212 条 Snippet 和四个模板;
  • 搜索、分类/状态筛选、添加、复制、删除;
  • 修改 trigger/replacement/options/category/description/priority/flags/syntaxVersion/enabled;
  • 表单同行输入框顶部对齐,说明文字位于字段下方;
  • 模板名称/trigger/说明/正文可编辑;
  • 干净 Profile 的四个默认 trigger 是 article-cnarticle-enbeamer-cnbeamer-en;用升级夹具验证 article factory trigger 的一次性迁移只考虑当前仍为 tmpa-cn / tmpa-en 的值。迁移 marker 必须先写入;当前为其他值不改,目标占用/前缀冲突/提交失败保留旧值并记录 Output,后续激活或用户日后主动改回旧 trigger 都不得再次迁移;
  • 查找替换字段范围、大小写、regex、预览、应用、撤销;
  • Ctrl+S/Cmd+S 后新 trigger 立即生效;
  • 另一个窗口修改后,旧草稿保存必须报 revision 冲突;
  • 超长字段/总量在进入永久 busy 前被拒绝;
  • 恢复默认会保护 dirty Webview 和高级 JSONC。

Citation 手工验收

准备 bibliography 与 Zotero 混合夹具,至少包含:100 条以上候选、带标点和去标点后相近的 key、可区分的标题/作者/年份、DOI/ISBN、只在 container/摘要/标签/笔记中含查询词的反例,以及同 DOI、异 DOI、共享 ISBN 的不同章节和重复 Zotero citation key 组。

检查:

  1. \cite{} 自动打开原生 Suggest;
  2. 左侧 TeXLeaf 行只有标题和来源,无 key、无 metadata detail;
  3. 右侧标题下没有重复“作者 · 期刊 · 年份”summary,并分字段显示作者、期刊/出版物、年份、Citation key、来源和状态;
  4. citation key、标题、作者、年份、DOI 和 ISBN 都能筛选;container/摘要/标签/笔记中的独占词不能筛出条目,错拼词不触发近似纠错;
  5. 多词查询规范化并去重后按 AND 组合,不同词可跨字段命中;任一词缺失时不命中;
  6. 分别验证精确原始 key(含标点)、紧凑 key 的精确/前缀、精确 DOI/ISBN、全词/词首和普通子串的相关度顺序;原始 key 标点精确匹配与紧凑 key 匹配等级不同;
  7. 相关度优先于来源,只有同等相关度才让 bibliography 排在 Zotero 前;
  8. 总 Suggest 最多 100 条,且对 bibliography+Zotero 全库匹配、排序后才截断;缩窄查询后,先前未进入前 100 条的项目能够出现;
  9. 记录 Zotero 请求次数:首次加载、缓存过期、相关设置变化或手动刷新之外,输入/退格每个字符都只筛选本地快照;
  10. 输入查询、退格清空、再次输入仍自动重开;
  11. \cite{keepA, query, keepB} 只替换 query;
  12. BibTeX/BibLaTeX、自定义路径、群组库、dirty .bib、Undo;
  13. Zotero 关闭、端口错误、库错误、超时和 Local API fallback;
  14. 未信任工作区不访问端口、不写入文件;
  15. 不同 key 但双方有效 DOI 相同必须复用已有 .bib key;双方有效 DOI 不同即使其他元数据相同也必须冲突;ISBN 只有标题一致且双方提供的第一作者 family name/年份不矛盾时才辅助判同,共享 ISBN 的不同章节不得折叠;
  16. Zotero 重复 citation key 的整组记录都不显示,Output 只报告组数;手动刷新前取得的旧候选在刷新后不得接受;
  17. 安装 LaTeX Workshop 时确认第三方候选无法由 TeXLeaf 选择性删除,并验证文档中推荐的 label 配置行为。

AI 写作手工验收

只使用不含隐私的临时 .tex、测试账户和可撤销 Key:

  1. 保持默认关闭,分别在已命名但 dirty 的 .tex、Untitled、.bib 和虚拟文档中编辑,确认没有请求;确认 14 个 AI 设置都使用 application scope,并向工作区/文件夹/.vscode/settings.json 注入总开关、Provider、两个 Base URL、模型、防抖和长度上限,验证运行时仍只采用用户/Profile 全局值;
  2. 在未信任工作区运行全部 AI 命令,确认被本地门禁阻止;
  3. DeepSeek 官方默认 endpoint 与一个自定义 Chat Completions endpoint 分别完成 consent、Key、检查、改写、补全与清除 Key 流程,并确认都只请求 {Base URL}/chat/completions;默认官方 URL 继续兼容旧 v1 Key/consent,自定义 URL 必须使用新记录且绝不复用;
  4. OpenAI 官方默认 gpt-5.6-luna 完成相同流程,并确认请求走 /v1/responses、Structured Outputs 且带 store:false; 两个 Provider 的全部操作都应把 auto/english/chinese 映射为 auto/English/Chinese;直接注入超长或含换行标签时须在联网前失败并保持 mock fetch 为 0,Review 中文说明要求不变;
  5. 对 DeepSeek/OpenAI 两种自定义 HTTPS 地址,以及各自 loopback HTTP 地址,分别验证 URL 规范化、独立 consent 与独立 Secret;
  6. 在两个 Provider 的官方/自定义地址之间来回切换,确认没有当前目标专用 Key/consent 时状态变为缺 Key,绝不复用或发送其他 URL/Provider 的 Key;切回后只恢复该规范化地址自己的状态;
  7. 两个 Provider 的远程 HTTP、URL 用户信息/查询/fragment、主机名尾随点、路径已以各自 /chat/completions/responses endpoint 结尾,以及非法模型 ID 都应在联网前失败;任何 HTTP 重定向都不得被跟随;
  8. DeepSeek 目标只提供 Responses、OpenAI 目标只提供 /chat/completions,或相应结构化输出契约不兼容时应给出协议错误,不能跨 endpoint 回退;
  9. 请求期间切换 Provider、模型、Base URL、清 Key、关闭总开关或继续编辑,旧响应不得生成问题标记或修改文档;
  10. 构造 DeepSeek 空内容/无效 JSON,确认各只自动重试一次;构造 schema 错误,确认不重试;纯 JSON 与单层完整 json fence 可解析,夹带说明、多个/嵌套/不完整 fence 被拒绝;
  11. 用含 emoji、CJK、CRLF、孤立 surrogate 与重复短语的响应覆盖零/一基的 UTF-16、Unicode code point、UTF-8 byte 和换行计数差异:只允许逐字且无歧义的本地重定位;空 original、非法 offset、找不到或歧义位置均按项丢弃,Unicode/引号/空白归一化和模糊匹配不得启用;请求 prompt 要求插入使用相邻非空原文锚点并在 replacement 保留锚点,但本地不得错误要求每个普通替换都包含 original;
  12. 同一响应混入一条无效、一对完全重复、一组真正重叠和两条独立有效建议,确认无效项被丢弃、重复项保留一条并计 duplicate-issue、重叠连通组整组丢弃、独立有效项保留;顶层 issues 结构无效仍整批失败,日志只记录拒绝数量和去重安全子码;
  13. 构造 OpenAI refusal、incomplete、空 output、错误 schema、401/403/429/5xx,确认 fail closed 且只记录安全错误子码;
  14. 检查活动栏“AI 写作问题”:当前文档状态、pending 数量、徽标、行号/类别/真实严重性/原文 → 替换/解释和单项应用/忽略正确;点击只滚动、不移动主光标;编辑器只画装饰线,专用 Hover 只出现一份,原生 Diagnostic/Problems 中无 AI 重复项,树严重性不变,TeXLeaf 不播放检查音频;模型文字不能形成可执行 Markdown 命令;
  15. 用 mock 构造 42 条独立有效建议和 20 条重复、重叠、找不到或歧义候选,确认 UI 显示 42 条可审阅和 20 条安全忽略,不误报 API Key/余额错误且原文未改;
  16. 从 Command Palette 与视图工具栏运行“应用全部”,确认先出现模态确认,并在确认后按稳定 lineage ID 从当前安全状态重新解析确认前捕获的全部建议,再验证文档版本、范围、原文和重叠;确认期间仅发生内容等价的 state 对象刷新时不得误失败,正文版本已变、任一捕获 ID 已移除/失效/无法唯一解析或任一校验失败时仍整批不修改;确认后新增、未被确认的问题不得纳入本批。另保存一个刷新前的问题树节点,在其问题前插入/删除文字使范围安全平移,确认旧节点仍能应用一次;真正失效的节点只刷新列表并显示短状态栏,不弹“文档已变化”,也不播放音效;
  17. 保持默认 900 毫秒,连续键入确认重置防抖、取消旧请求且不是每键联网;同版本同句子同配置去重,每批最多 8 句、每版本最多 64 个不同句子。覆盖中文无空格分句、句号/空行 split/merge、零宽边界、累计 pending 与同事务多处编辑:old ∪ new 只作为 provider 上下文;问题失效只看 precise dirty ranges,同句非相交项须经 exact original、strict remap、editable prose 后保留,自动回应仅替换命中 dirty/new issue 的旧项。保存/空 change 不清结果。让后句 API 失败,确认前句逐句提交不回滚;手动整篇单次最多 32 个正文段;
  18. 记录带 $PRIVATE$\[C_{PRIVATE}=42\] 的 mock 请求,确认公式源码缺席,等长 inline/display marker 受保护且维持 UTF-16 offset;Take 后的 display formula 不因遮罩误报缺宾语。两客户端 prompt 都要求 message/explanation 使用简体中文、replacement 保持来源正文原语言;
  19. 关闭文档/重启扩展宿主验证本机 globalStorage 问题恢复:仅在完整源码长度+SHA-256 完全一致后按 exact offset 恢复;外部改动或缓存不匹配整份丢弃并重检,不执行跨源 unique-original。验证不写工作区/Settings Sync/全文、单条校验、2048 条/2 MiB/256 文档/32 MiB 上限、损坏缓存、持久清除、不复活和 deactivate await best-effort flush;
  20. 检查 Output、错误通知与支持信息,不得出现 Key、请求/响应正文、original、原始错误 body 或敏感自定义 endpoint 路径;用户文档不得承诺无网络延迟、无限频率或免费“实时”检查,只能表述为受服务延迟、限额与费用约束的近实时。

安全与许可检查

发布前确认:

  • Snippet JSONC 仍是纯数据,不引入 eval/Function;
  • Zotero endpoint 仍固定 127.0.0.1,路径设置仍限制在 workspace 内;
  • Webview 有 nonce CSP,不用 innerHTML 注入用户数据;
  • MathJax Worker 输入长度/宏/队列/超时仍有限制;
  • 输出 SVG 仍拒绝脚本、事件属性、外部 URL 和 foreignObject;
  • 并发保存、恢复、Sync 与 bibliography 写入仍有 revision/版本校验;
  • AI 仍默认关闭,只处理受信任窗口中已有文件名和路径的 file:/vscode-remote: .tex;文档 dirty 时也可能发送当前编辑器内容,因此 consent 与文档必须准确描述;
  • DeepSeek 只调用 {normalized Base URL}/chat/completions 与 JSON Output;OpenAI 只调用 {normalized Base URL}/responses 与 Structured Outputs,两者不跨协议回退。两种远程 Base URL 仅允许 HTTPS,只有 loopback 可使用 HTTP,并拒绝 credentials/query/fragment、主机名尾随点和已含 endpoint 的地址,不跟随 HTTP 重定向;
  • 每个规范化 DeepSeek/OpenAI Base URL 都使用独立 Secret/consent;默认 DeepSeek 官方地址继续兼容旧 v1 记录,自定义 DeepSeek 地址不得读取或复用它,OpenAI 各地址与两个 Provider 之间也不得回退或复用;SecretStorage Key 和 consent 不进入普通 Settings Sync;
  • 全部 14 个 texleaf.aiWriting.* 设置仍为 application scope,只接受用户/Profile 值并可随普通 Settings Sync;工作区、文件夹和 .vscode/settings.json 不能开启、重定向、换模型或改变防抖/长度等费用参数,运行时仍应只读取 global value;
  • OpenAI 请求仍包含 store:false,但文档不得把它写成第三方不存储/不训练的保证;ChatGPT 订阅不得描述为 OpenAI API 额度;
  • DeepSeek 空内容/无效 JSON 只重试一次,只接受纯 JSON 或严格单层完整 json fence;schema/offset 等安全失败不重试;
  • AI offset 兼容只在零/一基的 UTF-16、Unicode code point、UTF-8 byte 与 CRLF 换行坐标之间做有限解释;original 必须非空并逐字、无歧义定位,插入的相邻锚点保留属于模型协议,重复歧义、Unicode/引号/空白归一化或模糊匹配一律不猜;
  • 单条无效建议与真正重叠的冲突组不得拖垮同一响应中的独立有效项;完全重复项去重保留一条,顶层结构错误仍整批 fail closed,日志只含拒绝数量和安全子码;
  • AI 日志只含整理过的安全子码和用量,不含 Key、正文、响应正文、原始错误 body 或自定义 endpoint 的敏感路径;
  • AI 模型建议仍经过 offset、原文、可编辑区、重叠、文档版本与 TeX 控制字符校验,不直接应用未验证 JSON;
  • 活动栏问题列表不得把模型文字标记为受信任 Markdown;单条与批量应用都必须重新验证当前版本、范围和 exact 原文,批量应用还必须确认无重叠且先获得用户模态确认;
  • 自动检查必须保持防抖、取消、同版本同句子去重及每批 8 句/每版本 64 句上限;old ∪ new 句子只作为 provider 上下文,旧问题失效只看 precise dirty ranges,同句非相交建议在 exact 原文、strict remap 与 editable prose 都成立时保留;保存/空 change 保留状态,不确定时清除而不是猜测;
  • AI 问题不得发布原生 Diagnostic 或进入 Problems;装饰线、专用 Hover、问题树和 Quick Fix 仍必须使用同一份经校验的状态,避免重复 Hover 与 Error/Warning accessibility signal;Hover 的“应用这条建议”只能信任白名单内部命令,点击后仍必须由后端重新验证版本、问题身份、范围、exact 原文与 editable prose,模型文字不得成为可执行命令;
  • 问题缓存只能使用当前 Profile/扩展宿主的私有 globalStorage,不得写工作区或注册 Settings Sync,不得保存论文全文;仅 exact source hash+length 可恢复,外部改动/缓存不匹配必须 fail closed 重检。上限、字段/TeX 校验、best-effort 原子写与停用 flush 不得绕过;
  • THIRD_PARTY_NOTICES.mdlicenses/Apache-2.0.txt 与 bundle 依赖一致;
  • README、Wiki 致谢与 THIRD_PARTY_NOTICES.md 能准确区分产品灵感、实际发行依赖和相应许可证;
  • LICENSE、第三方 notice 和模板隐私清理完整。

Release 检查清单

  1. 更新 package.json 版本为 1.0.0,把未发布的 0.8.12 变更并入 1.0.0,并同步 README/CHANGELOG/Wiki/release notes;
  2. 确认 README 按片段、AI 写作、文献、预览四部分描述,并准确说明 AI 默认关闭、DeepSeek Chat Completions/OpenAI Responses、两个独立自定义 Base URL、14 个 application 级设置、按目标隔离凭据、独立计费和隐私边界;
  3. 检查出厂 article 模板使用 \bibliographystyle{alpha},默认 trigger 为 article-cn / article-en,Beamer 仍为 beamer-cn / beamer-en;完成一次性旧 factory trigger 迁移的 marker、冲突和不重试验收;
  4. 运行 pnpm install --frozen-lockfile(CI/干净环境);
  5. 运行 pnpm run release:verify
  6. 完成 Windows、macOS、Linux 对应的自动/人工验收;
  7. 运行 pnpm run package 生成 texleaf-1.0.0.vsix
  8. vsce ls --no-dependencies 或解包检查 VSIX 清单;
  9. 确认不包含 src/test/.git/、node_modules 散目录、旧 VSIX、日志或用户数据;
  10. 在全新隔离 Profile 安装最终 VSIX再做一次管理器、AI 默认关闭/无 Key 不联网、citation 和 Math Preview 冒烟;citation 必须覆盖全库本地相关度排序、100 条末端截断、继续输入重算、逐键不请求 Zotero,以及同/异 DOI、受约束 ISBN 与重复 citation key 整组隐藏;
  11. 记录文件大小和 SHA-256;
  12. 提交可审阅的源码变更,创建签名/普通 tag v1.0.0
  13. 推送源码分支和 tag;
  14. 创建 GitHub Release,把 VSIX 作为 Release asset 上传;
  15. 下载 Release 资产复核哈希和安装。

二进制只放 GitHub Release

源码仓库的 .gitignore 排除:

dist/
out/
.test-dist/
*.vsix
*.tgz

正确发布模型:

  • Git commit:TypeScript、package/lockfile、模板、测试、README、许可证;
  • Git tag:对应可复现版本;
  • GitHub Release:texleaf-1.0.0.vsix、release notes,可选哈希;
  • 不把 VSIX 通过 Git LFS 或普通 Git 放进源码历史;
  • 不把本地 dist/ 当作源码提交。

示例(确认仓库、tag 和权限后执行):

git tag v1.0.0
git push origin main
git push origin v1.0.0
gh release create v1.0.0 texleaf-1.0.0.vsix \
  --title "TeXLeaf 1.0.0" \
  --generate-notes

创建前应在 GitHub 页面复核自动生成的说明,并补充 CHANGELOG.md 中的本版重点与最终 VSIX 的 SHA-256;不要调用仓库外或旧版本遗留的本地发布脚本。

不要自动覆盖已经存在的 Release 或 tag;发现同名对象时先核对其 commit 和资产。

Wiki 发布

GitHub Wiki 是独立 Git 仓库,地址通常为:

https://github.com/zhangxh-math/texleaf.wiki.git

本项目先在源码工作区的 .wiki-publish/ 准备页面;该目录由 .gitignore 排除,不进入源码 commit。发布时将这些 Markdown 复制/提交到独立 wiki 仓库:

git clone https://github.com/zhangxh-math/texleaf.wiki.git texleaf.wiki
# 将 .wiki-publish/*.md 放入 texleaf.wiki/
cd texleaf.wiki
git add Home.md _Sidebar.md *.md
git commit -m "Document TeXLeaf 1.0.0"
git push origin master

实际默认分支可能是 master 或其他名称,以 git branch -r 为准。推送前检查:

  • Home.md 存在;
  • _Sidebar.md 链接到全部页面;
  • Wiki 链接目标与文件 basename 一致;
  • 页面没有源码工作区绝对路径、个人信息或临时资产;
  • Wiki 仓库不包含 VSIX;二进制仍只属于 Release。

贡献准则

  • 一次提交聚焦一个可审阅目标;
  • 不覆盖用户工作树中的无关修改;
  • 改行为必须同步测试和用户文档;
  • 诊断问题与实现修复分开描述;
  • 对 VS Code 未公开 API 的兼容层明确写出限制和降级;
  • 外部项目只能借鉴公开产品行为,复制代码/资源必须满足许可并记录来源;
  • 网络、本机端口、文件写入、Webview 和递归删除必须有明确边界。

相关页面

Clone this wiki locally