Skip to content
Closed
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
4 changes: 4 additions & 0 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ The current shortcut UI only accepts `Ctrl+letter` and `Ctrl+Shift+letter`, so t
- macOS: use the `.dmg` or `.app.zip` artifact.
- Microsoft Store channel: use the Store package/update flow when installed from Store.

## What is the final Office plugin direction?

The current `office_addin` project is an Office.js implementation kept for migration reference. The final Office integration is planned as a Windows-native `office_plugin` so LaTeXSnipper can provide persistent Ribbon loading, native shortcuts, double-click editing, OLE formula objects, Word OMML insertion, PowerPoint image insertion, and a local MathJax/native rendering pipeline without relying on Microsoft 365 enterprise deployment.

## Does LaTeXSnipper require an internet connection?

Core editing and local recognition workflows are designed to work locally after the required dependencies and models are installed. Some optional downloads, update checks, model downloads, and CDN fallbacks require network access.
Expand Down
121 changes: 47 additions & 74 deletions docs/office_addin_design.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
# Office Add-in 已实现设计
# Office.js Add-in 迁移记录

本文只描述当前已经交付的 Word 与 PowerPoint Office.js 行为,不记录未实现的愿景或兼容设想
本文记录 `office_addin` 已实现的 Office.js 行为和迁移价值。它不再作为 LaTeXSnipper Office 集成的最终设计文档;最终架构见 `docs/office_plugin_design.md`

## 目标与边界
## 结论

LaTeXSnipper Office 加载项连接本机 Office Bridge,负责公式输入、LaTeX 转换结果写入 Office、截图识别结果载入以及 Word 中由本加载项拥有的公式生命周期管理。
Office.js 已经验证了 LaTeXSnipper 与 Word/PowerPoint 集成的核心流程,但它不适合作为最终产品形态:

对于本产品目标,当前实现位于可可靠使用的 Office.js 能力边界:
- 单机安装无法像原生插件一样可靠持久显示 Ribbon;稳定分发依赖企业部署、Office Store 或受信任目录策略。
- Ribbon 图标、manifest、WebView Runtime、localhost HTTPS、证书和 sideload 缓存都属于宿主平台外部约束。
- 无法实现每个公式都是可双击编辑的原生 OLE 对象。
- 无法在 Word/PPT 对象模型底层提供类似 MathType/AxMath 的自绘公式对象、快捷键和对象生命周期控制。

- Word 维护本加载项创建的带标记 OMML 公式,具备源代码、更新、删除与编号闭环。
- PowerPoint 写入公式 PNG 图像;编号与公式合成为同一张图像。
- 任意 Word 原生公式、缺失元数据的对象和 PowerPoint 图像不被伪装为可编辑 LaTeXSnipper 公式。
- 不实现历史结构修复或猜测式兼容路径。
因此,`office_addin` 保留为迁移参考;新增和最终能力进入 Windows 原生 `office_plugin`。

## 运行结构
## 已验证能力

```text
Word / PowerPoint Ribbon
Expand All @@ -28,33 +28,18 @@ Word / PowerPoint Ribbon
GET /health, /config
```

Ribbon 命令通过共享运行时进入任务窗格调度层;`Editor`、`Insert`、`Screenshot OCR`、编号动作和 `Help` 都执行真实命令,而不是仅打开一个无动作的窗格。
这些能力可以迁移到原生插件:

截图识别返回后,任务窗格输入值总会更新;若 `Dialog editor` 正在打开,父窗口通过 Office 对话框消息同时将同一 LaTeX 写入该编辑器。
- Ribbon 命令模型:编辑器、插入、截图识别、加载、删除、编号、重编号。
- Bridge 协议:LaTeX 转 OMML、截图 OCR、OCR 状态、安装路径发现。
- MathLive 编辑器:公式输入、符号面板、结构模板、中英文 UI。
- Word 公式元数据:公式 ID、LaTeX 源码、显示模式、编号模式和编号值。
- 编号公式规则:每个编号公式独立布局,连续插入不得共享表格或对象容器。
- PowerPoint 图片路径:裁剪公式图像、手动编号合成图像。

## 语言
## 当前 Word 行为

`src/services/i18n.ts` 使用 `Office.context.displayLanguage` 选择 `en-US` 或 `zh-CN`:

- 任务窗格和编辑对话框通过文本键渲染。
- Ribbon 通过两份 manifest 内的 `zh-CN` override 由 Office 本身切换。
- 帮助入口根据当前语言打开 `help.html` 或 `help.zh-cn.html`。

## Word 对象模型

### 公式身份

每个由加载项创建的 Word 公式使用内容控件标记 `latexsnipper-eq-{id}`。文档设置保存其 LaTeX、显示模式、编号模式和编号值。无标记或无元数据的公式不是受管理公式。

### 普通公式

普通公式使用 OMML 内容控件。插入、加载、更新与删除只针对该内容控件;当公式位于用户表格中时,删除操作不会删除用户表格。

### 编号公式

编号公式使用独立的无边框三列表格:中间单元格放公式,右侧单元格放编号,左右空间保证公式居中。每次插入均保留表格后的段落边界,连续插入不会生成共享表格。

### Word 命令
每个由 Office.js 加载项创建的 Word 公式使用内容控件标记 `latexsnipper-eq-{id}`。文档设置保存其 LaTeX、显示模式、编号模式和编号值。无标记或无元数据的公式不是受管理公式。

| 命令 | 行为 |
|---|---|
Expand All @@ -67,7 +52,7 @@ Ribbon 命令通过共享运行时进入任务窗格调度层;`Editor`、`Inse

插入点若位于任意受管理公式或编号布局内部,适配器返回可读提示并拒绝写入。

## PowerPoint 对象模型
## 当前 PowerPoint 行为

`src/office/powerpointInsert.ts` 通过 `Office.context.document.setSelectedDataAsync` 和 `Office.CoercionType.Image` 写入 Bridge 生成的 PNG。

Expand All @@ -80,49 +65,37 @@ Ribbon 命令通过共享运行时进入任务窗格调度层;`Editor`、`Inse

PowerPoint 工作流没有 `Load Selected`、`Update`、`Delete Selected`、自动编号或 `Renumber All`。稳定发布路径使用 `Office.CoercionType.Image` 写入图像,该方法不返回可由本插件标记并持续追踪的图片对象。删除后的图片无法可靠参与编号重算,因此不保留只会递增的伪自动编号状态。

## Bridge 与数据

Bridge URL 与会话 token 由文档设置保存。Word 额外保存公式来源和自动编号状态;PowerPoint 插入结果不写入虚假的可编辑公式元数据。

本机安装包将构建后的 Office 站点、manifest 和 `localhost` TLS 配置安装到本机。启用 Office 功能后的桌面端发现这些资源后,以 `https://localhost:8765` 同时提供站点和 Bridge API:

- Windows Inno 包为当前用户安装站点与证书,将实际所选安装目录写入产品注册表供 Bridge 发现,并将 Word/PPT manifest 写入 `WEF\Developer` 本机 sideload 注册。不写入需要 UNC 共享目录的 `TrustedCatalogs`。
- macOS `.pkg` 安装站点与证书,并将 Word/PPT manifest 放入 Microsoft 文档指定的 `wef` 本机 sideload 目录。
- GitHub Release workflow 额外产出 `OfficeDeploymentManifests-<version>.zip`,供管理员通过 Microsoft 365 Integrated apps 统一部署;Windows 本机安装程序纳入 SignPath 签名路径。

本机 sideload 适用于单机安装和测试,不等同于 Microsoft 支持的组织级生产分发。组织内持续分配 Ribbon 插件由 Microsoft 365 Integrated apps 部署实现;Marketplace 上架仍不属于仓库可自动完成的发布步骤。

## Requirement Sets
## 不迁移的 Office.js 机制

| 宿主 | Manifest 声明 | 支持目标 |
|---|---|---|
| Word | `WordApi 1.3`, `SharedRuntime 1.1` | Windows: Microsoft 365 Word Version 2205 (Build 15202.10000) 或 Word 2024;Mac: Word 16.61 |
| PowerPoint | `ImageCoercion 1.1`, `SharedRuntime 1.1` | Windows: Microsoft 365 PowerPoint Version 2102 (Build 13722.10000) 或 PowerPoint 2021/2024;Mac: PowerPoint 16.46 |
以下机制只服务于旧实现,不应进入 `office_plugin`:

本机 Bridge 和桌面截图是产品依赖,因此不把 Office Web 或移动版列为交付目标。
- manifest sideload 作为持久安装方案。
- 依赖任务窗格保持状态的命令调度。
- 用文档设置弥补对象自身无法持久保存源数据的问题。
- 通过 `localhost` 静态站点承载 Office UI。
- 针对 Office.js 插入失败的 OOXML 字符串修补。
- PowerPoint 中不可追踪图片对象的伪编辑流程。

## 源码边界

| 文件 | 责任 |
| 文件 | 迁移价值 |
|---|---|
| `src/taskpane/App.ts` | 宿主工作流与命令调度 |
| `src/dialog/editorDialog.ts` | 可视化公式编辑 |
| `src/taskpane/App.ts` | 命令编排、状态提示、Bridge 调用顺序 |
| `src/dialog/editorDialog.ts` | 可视化公式编辑器交互 |
| `src/taskpane/mathliveEditor.ts` | MathLive 封装 |
| `src/office/wordInsert.ts` | Word 受管理公式适配器 |
| `src/office/powerpointInsert.ts` | PowerPoint 图像适配器 |
| `src/services/i18n.ts` | 中英文字符串与错误显示 |
| `src/services/equationSession.ts` | 文档设置持久化 |
| `src/services/ribbonCommands.ts` | Ribbon 与共享运行时队列 |
| `../src/integration/office/addin_runtime.py` | 正式安装站点及 TLS 文件发现 |
| `../scripts/build_office_addin_installer.ps1` | Windows Office 加载项安装包构建 |
| `../scripts/build_office_addin_macos.sh` | macOS Office 加载项安装包构建 |

## 官方依据

- [Word JavaScript API requirement sets](https://learn.microsoft.com/javascript/api/requirement-sets/word/word-api-requirement-sets)
- [Shared runtime requirement sets](https://learn.microsoft.com/javascript/api/requirement-sets/common/shared-runtime-requirement-sets)
- [Image coercion requirement sets](https://learn.microsoft.com/javascript/api/requirement-sets/common/image-coercion-requirement-sets)
- [Localize Office Add-ins](https://learn.microsoft.com/office/dev/add-ins/develop/localization)
- [Sideload Office Add-ins from a network share on Windows](https://learn.microsoft.com/office/dev/add-ins/testing/create-a-network-shared-folder-catalog-for-task-pane-and-content-add-ins)
- [Sideload Office Add-ins on Mac](https://learn.microsoft.com/office/dev/add-ins/testing/sideload-an-office-add-in-on-mac)
- [Deploy add-ins through Integrated apps](https://learn.microsoft.com/microsoft-365/admin/manage/test-and-deploy-microsoft-365-apps)
| `src/office/wordInsert.ts` | Word OMML、编号布局和元数据规则 |
| `src/office/powerpointInsert.ts` | PowerPoint 图像裁剪与编号合成 |
| `src/services/i18n.ts` | 中英文术语与错误提示 |
| `../src/integration/office/bridge_server.py` | Bridge API |

## 清理条件

只有在 `office_plugin` 完成以下闭环后,才能删除 `office_addin`:

- Word 原生 Ribbon 稳定加载。
- Word OMML 插入、加载、更新、删除、编号和重编号达到现有能力。
- Word OLE 公式对象可插入、双击编辑、保存源数据并重新渲染。
- PowerPoint 图片插入能力不低于当前 Office.js 实现。
- PowerPoint OLE 公式对象可插入、双击编辑、保存源数据并重新渲染。
- 快捷键和安装器持久注册链路完成。
- 回归测试覆盖对象生命周期、编号、文档保存/重开和卸载清理。
152 changes: 152 additions & 0 deletions docs/office_plugin_design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Windows 原生 Office 插件目标架构

`office_plugin` 是 LaTeXSnipper Office 集成的最终主线。目标是做成类似 MathType/AxMath 的 Windows 原生插件:安装后 Word 和 PowerPoint 持久显示 Ribbon,每个 LaTeXSnipper 公式都是可识别、可保存源数据、可双击打开编辑器的对象。

`office_addin` 暂时保留为迁移参考,迁移完成后删除。

## 产品目标

- 绑定 Windows 桌面版 Office,不再追求 Office.js 的 Web/Mac 覆盖。
- 原生 Ribbon 持久加载,不依赖 Microsoft 365 企业账号、Office 管理中心或 sideload manifest。
- 支持全局和 Office 内快捷键。
- Word 支持 OMML 公式和 LaTeXSnipper OLE 公式对象。
- PowerPoint 支持当前 OMML 图片插入和 LaTeXSnipper OLE 公式对象。
- 每个公式对象保存 LaTeX 源码、渲染参数、编号信息和 LaTeXSnipper 对象版本。
- 双击公式对象打开 LaTeXSnipper 编辑器,修改后原地更新。
- 本地 MathJax/MathLive/自绘渲染管线完全离线运行。

## 推荐技术路线

第一阶段优先使用 VSTO/C# 建立原生 Office 插件骨架;必要时用 COM/OLE 组件承载公式对象和自绘渲染。

| 层 | 建议 |
|---|---|
| Office 插件外壳 | VSTO Add-in for Word/PowerPoint |
| Ribbon | Ribbon XML,便于精确控制命令、图标和快捷键 |
| 公式对象 | LaTeXSnipper OLE/ActiveX 对象,支持嵌入、激活、双击编辑 |
| 编辑器 | WPF 或 WinUI 窗口;初期可复用本地 MathLive/WebView2,最终可替换为原生编辑器 |
| 渲染 | 本地 MathJax/MathLive 渲染服务 + 原生 GDI+/Direct2D/SVG/EMF 输出 |
| Bridge | 继续复用 LaTeXSnipper 桌面端本地服务,负责 OCR、转换和渲染 |
| 安装器 | Inno/MSI 写入 VSTO/COM/OLE 注册表,检测 Office 位数、VSTO Runtime、WebView2 Runtime |

裸 COM Add-in 控制力更强,但生命周期、异常隔离、注册和 Ribbon 维护成本更高。第一版建议先用 VSTO 收敛 Word/PPT 工作流,再把公式对象和渲染管线下沉为独立 COM/OLE 组件。

## 对象模型

### 统一公式身份

每个公式都有稳定 ID:

```text
latexsnipper:{document-id}:{equation-id}
```

对象内部保存:

- LaTeX 源码
- 显示模式
- 渲染后宽高
- 编号模式:无编号、自动编号、手动编号
- 编号值
- 渲染引擎:OMML、MathJax Native、图片兼容
- LaTeXSnipper 对象格式版本

这些数据必须随 Word/PPT 文档保存,不能依赖外部临时缓存。

### Word

Word 支持两条输出路径:

| 路径 | 用途 |
|---|---|
| OMML 原生公式 | 文档可编辑性优先,适合普通 Word 数学排版 |
| LaTeXSnipper OLE 公式对象 | 自绘效果、TeX 兼容性、双击编辑和 MathType 式体验优先 |

编号公式不再依赖 Office.js 的脆弱表格写入路径。最终可以由对象自身绘制编号区域,也可以由插件创建可控的 Word 布局容器,但公式身份必须绑定到单个 LaTeXSnipper 对象。

### PowerPoint

PowerPoint 支持两条输出路径:

| 路径 | 用途 |
|---|---|
| 图片插入 | 保留当前稳定行为,适合兼容导出和简单演示 |
| LaTeXSnipper OLE 公式对象 | 支持双击编辑、自绘渲染、源数据保存和精确缩放 |

自动编号不应使用全局递增缓存。若需要自动编号,必须扫描当前幻灯片/演示文稿中仍存在的 LaTeXSnipper 对象,按对象数据计算最大编号或重排编号。

## 渲染管线

```text
LaTeX source
-> normalize
-> render request
-> OMML converter
-> MathJax native renderer
-> image/SVG/EMF renderer
-> Office adapter
-> Word OMML
-> Word OLE object
-> PowerPoint image
-> PowerPoint OLE object
```

本地 MathJax 原生渲染管线的目标:

- 离线运行,不加载 CDN。
- 输出紧贴公式边界的位图/SVG/EMF。
- 支持对象 DPI、缩放、透明背景、深浅色策略。
- 与 OLE 对象自绘共享同一套布局结果。
- 渲染失败时返回明确错误,不生成不可编辑的半成品对象。

## 命令设计

| 命令 | Word | PowerPoint |
|---|---|---|
| `Open Editor` | 打开编辑器,可从当前选择加载公式 | 打开编辑器,可从当前选择加载公式 |
| `Insert OMML` | 插入 Word 原生 OMML | 插入当前兼容图片 |
| `Insert Native Object` | 插入 LaTeXSnipper OLE 公式对象 | 插入 LaTeXSnipper OLE 公式对象 |
| `Load Selected` | 加载选中 LaTeXSnipper 对象或受管 OMML | 加载选中 LaTeXSnipper 对象 |
| `Update Selected` | 原地更新对象 | 原地更新对象 |
| `Delete Selected` | 删除对象并清理元数据 | 删除对象并清理元数据 |
| `Auto Numbered` | 对受管公式启用自动编号 | 对受管对象启用自动编号 |
| `Renumber All` | 扫描文档对象重排 | 扫描演示文稿对象重排 |
| `Screenshot OCR` | 调用桌面端 OCR,写入编辑器 | 调用桌面端 OCR,写入编辑器 |

快捷键应在 Ribbon XML/VSTO 层注册,并与 LaTeXSnipper 桌面端全局快捷键保持不冲突。

## 安装与持久化

Windows 安装器负责:

- 安装 VSTO 插件。
- 注册 Word 和 PowerPoint 加载项。
- 注册 LaTeXSnipper OLE/COM 公式对象。
- 安装或检测 VSTO Runtime、WebView2 Runtime。
- 写入当前用户或机器级注册表项。
- 安装图标、Ribbon 资源和本地渲染资源。
- 卸载时清理插件注册、OLE 注册和临时缓存。

自定义安装位置不能影响插件加载。注册表中只保存稳定入口和资源根路径;插件启动时校验路径存在,不存在则显示可操作错误。

## 迁移阶段

1. 新建 `office_plugin` 项目骨架。
2. Word VSTO Ribbon 持久显示,命令能打开编辑器。
3. 复用 Bridge 完成 Word OMML 插入。
4. 迁移 Word 受管公式元数据、加载、更新、删除。
5. 迁移 Word 编号和重编号。
6. 实现 PowerPoint 图片插入,达到 `office_addin` 当前能力。
7. 引入 LaTeXSnipper OLE 公式对象。
8. 接入本地 MathJax 原生渲染管线。
9. 实现 Word/PPT 双击编辑和原地更新。
10. 完成快捷键、安装器、卸载器和回归测试。
11. 删除 `office_addin`。

## 不做的事

- 不继续投入 Office.js 持久安装方案。
- 不依赖企业账号作为普通用户安装前提。
- 不用图片对象伪装成可编辑公式。
- 不保留无法识别来源的历史兼容逻辑。
- 不让文档对象生命周期依赖任务窗格是否打开。
Loading
Loading