本文不是“让 AI 猜一个插件然后直接写代码”的教程,而是一套先验证 MarginNote 4 能力边界、再用 Codex 完成真实插件的开发方法。
实测基准:MarginNote 4.4.5(Build 2)、iPadOS、2026 年 8 月。MarginNote 更新后,涉及宿主界面层级的结论必须重新测试。
这篇文章适合谁
- 没有完整插件开发经验,但想用 Codex 把自己的 MarginNote 工作流做成插件的用户。
- 已经会 JavaScript,希望快速理解 MarginNote 插件运行时、工程结构和能力边界的开发者。
- 希望了解真实用户还需要哪些接口、MarginNote 还能进入哪些专业工作场景的官方团队。
读完后,你应该能做到四件事:
- 从零建立一个 MarginNote 4 插件工程并打包为
.mnaddon。 - 让 Codex 先做 API 审计和安全探针,而不是直接根据需求幻想实现。
- 判断一个功能属于“公开 API 可稳定实现”“真机可实现但依赖内部 UI”“尚无接口”中的哪一类。
- 用可验证、可回滚、可持续迭代的方式,把学习软件扩展成真正的工作工具。
一、为什么不能一上来就把需求交给 AI 写代码
MarginNote 插件开发最大的风险通常不是“代码写不出来”,而是:
- 文档里看见了一个类,但它没有真正注册到当前 JavaScriptCore 环境。
- 方法在头文件里存在,但目标 MarginNote 版本中的 selector 名称不同。
- MarginNote 产品本身能做某件事,不代表插件 API 也能做。
- AI 根据 iOS、浏览器或 Node.js 的常识,写出了宿主里根本不存在的接口。
- 屏幕浮层看起来成功,但它没有进入 PDF 文档坐标,不能缩放、重开或导出。
我们最初的真实需求很简单:
在施工现场用 iPad、PDF 和 Apple Pencil 测量隔墙。用户在图纸上画线和箭头,在箭头末端记录墙体类型、唯一编号、长度和高度;最终批量导出到 Excel。
原来的人工流程有三个痛点:
- 手写编号会重复或缺号。
- 快速手写的数字容易误读。
- 所有数据还要重新录入 Excel。
如果直接按照需求写插件,很容易假设“MarginNote 一定有创建 PDF 文本框、读取 Pencil 笔迹坐标、写入手写层”的公开 API。实际探测结果是:这些能力并不处在同一个开放层级。
因此,正确顺序应当是:
场景痛点
→ 资料审计
→ 只读能力探针
→ 受控写入探针
→ 针对关键链路的真机实验
→ 最小可用插件
→ 现场节奏优化
这套顺序看似比“直接生成代码”慢,实际上能避免在不存在的 API 上浪费整个项目。
二、先认识 MarginNote 插件的真实结构
2.1 插件主体运行在 JavaScriptCore,不是浏览器,也不是 Node.js
MarginNote 插件主体是 JavaScript,但运行环境是 JavaScriptCore。
在我们测试的 iPad 版本中:
Promise、globalThis、WebAssembly可用。- 插件主运行时没有
fetch。 - 插件主运行时没有
setTimeout、setInterval。 - 没有 DOM、
window、document、localStorage和 Node.js 文件系统模块。 - 延时和轮询使用
NSTimer。 - 网络请求使用
NSURLConnection、NSMutableURLRequest等桥接对象。
官方文档入口:
下面这种浏览器写法在插件主运行时不可依赖:
setTimeout(function () {}, 500);
fetch("https://example.com/api");
document.querySelector("#app");
应改成宿主已导出的能力:
NSTimer.scheduledTimerWithTimeInterval(0.5, false, function () {
// 延时任务
});
如果使用 Web 模板,WebView 内部的网页环境可以有 DOM、fetch 和前端框架;但 WebView 不能直接操作 MarginNote 数据,必须通过桥接把请求交给插件侧执行。
2.2 .mnaddon 是一个归档包
最小插件至少包含:
my-addon/
├── main.js
├── mnaddon.json
└── icon.png # 可选
mnaddon.json 示例:
{
"addonid": "com.example.myaddon",
"author": "Your Name",
"title": "My Addon",
"version": "0.1.0",
"marginnote_version_min": "4.4.5",
"cert_key": ""
}
入口脚本必须实现 JSB.newAddon(mainPath),并返回一个继承 JSExtension 的类,而不是实例:
JSB.newAddon = function (mainPath) {
return JSB.defineClass(
"MyAddon : JSExtension",
{
sceneWillConnect: function () {
Application.sharedInstance().showHUD("插件已加载", self.window, 1);
},
queryAddonCommandStatus: function () {
return {
image: "icon.png",
object: self,
selector: "runAddon:",
checked: false
};
},
runAddon: function () {
Application.sharedInstance().alert("Hello MarginNote");
}
},
{
addonDidConnect: function () {},
addonWillDisconnect: function () {}
}
);
};
官方入门文档:快速开始
三、资料从哪里找:给用户和 Codex 的参考文献地图
建议同时保留下面四类资料,不要只看一个仓库。
| 资料 | 作用 | 使用原则 |
|---|---|---|
| MarginNote 官方 Addon 仓库 | Objective-C/JSB 头文件、注册实现、示例 | 判断一个类或方法是否真正导出时,优先级最高 |
| MarginNote 插件文档站 | 入门、指南、API 参考、Cookbook | 适合按任务学习,但关键 selector 仍应和头文件、真机交叉验证 |
| marginnote-addon-docs 仓库 | 文档源码和本地 MCP 检索 | 推荐接入 Codex,让 AI 先检索再读完整文档 |
| mn-rails | 创建工程、开发脚本、打包、Web 模板 | 它是脚手架,不是完整 SDK |
| MN Rails 官方论坛教程 | 社区推荐的工程化工作流 | 用于快速进入开发和发布流程 |
3.1 mn-rails 里的 base/standard 是完整 SDK 吗?
不是。
它主要提供:
- 标准插件目录结构。
main.js和最小JSExtension示例。mnaddon.json模板。- 本地部署、监听、版本号和打包脚本。
- 面向 AI 开发的
AGENTS.md。
它没有提供:
- 完整的 MarginNote 业务对象封装。
- 所有 API 的 TypeScript SDK。
- MarginNote 宿主模拟器。
- 私有接口兼容层。
- 自动化真机测试框架。
- 后台同步、权限或安全凭据系统。
所以更准确的说法是:
mn-rails负责把项目“搭起来、跑起来、打包出来”;官方 Addon 头文件和文档负责告诉你宿主可能暴露什么;真机探针负责确认当前版本到底能不能用。
3.2 给 Codex 配置本地文档检索
marginnote-addon-docs 内置 MCP Server,可以让 Codex 先发现相关文档,再读取全文。
配置示例:
{
"mcpServers": {
"mn-docs": {
"command": "npx",
"args": ["mn-docs-mcp"]
}
}
}
如果已克隆仓库,也可以使用本地路径:
{
"mcpServers": {
"mn-docs-local": {
"command": "node",
"args": ["mcp/cli.mjs"],
"cwd": "/path/to/marginnote-addon-docs"
}
}
}
推荐 AI 检索顺序:
- 先搜索相关文档。
- 再读取目标文档全文。
- 查对应 Objective-C/JSB 头文件。
- 查
.m注册代码,确认类是否真正注入 JSContext。 - 最后在目标设备上做最小探针。
四、截至本次真机测试,已经证实的能力
下面严格区分证据等级。
| 标记 | 含义 |
|---|---|
| 公开 | 官方文档或头文件明确提供,适合作为长期方案基础 |
| 真机 | 已在指定 iPad/MN4 版本中用探针或正式插件验证 |
| 内部 UI | 真机可用,但依赖 MarginNote 内部视图、类名或按钮位置,升级后可能失效 |
| 待复核 | 有人工结果或间接证据,但还没有形成同一条可审计链路 |
4.1 运行时与基础设施
| 能力 | 结论 | 证据 |
|---|---|---|
| 插件生命周期、工具栏命令 | 可用 | 公开 + 真机 |
JSB.defineClass、JSB.require |
可用 | 公开 + 真机 |
原生 UIView、UILabel、UIButton、手势 |
可用 | 公开 + 真机 |
WebView 页面与 evaluateJavaScript |
可用 | 真机对象为 MNUWebView;页面内有 Promise 和 fetch |
NSUserDefaults 保存少量设置 |
可用 | 公开 + 真机 |
Application.documentPath/cachePath/tempPath |
存在且可写 | 真机 |
| JSON、文本、二进制文件读写 | 可用 | 公开 + 真机 |
| 系统文件保存选择器 | 可用 | 公开 + 真机 |
主运行时 fetch/setTimeout/setInterval |
不可用 | 公开 + 真机 |
NSURLSession |
当前目标版本全局对象不存在 | 真机;不要因为头文件存在就直接使用 |
存储文档:存储与文件
4.2 笔记、数据库、脑图和搜索
已证实可以:
- 获取当前学习集、当前文档、当前页等上下文。
- 列举文档和笔记本。
- 通过
getNoteById获取笔记。 - 创建测试笔记、保存数据库并回读。
- 使用
UndoManager撤销受控写入。 - 删除笔记或笔记树。
- 克隆笔记、克隆为复习卡片。
- 读取页级 sketch note 的数量、笔画数和绘图字节大小。
- 使用文本搜索,以及目标版本中存在的部分索引和相似笔记能力。
数据库写入应遵循:
取得目标对象
→ UndoManager.undoGrouping
→ 修改或创建
→ 保存数据库
→ refreshAfterDBChanged
→ 真机回读验证
参考:笔记与数据库
4.3 PDF 页面上的原生文本标签
这是本项目最重要、也最需要说明证据边界的能力。
我们已经在正式插件链路中验证:
- 用户校准一次 MarginNote 顶部文本框按钮。
- 插件通过页面根视图
hitTest重新取得真实UIButton。 - 插件发送
TouchUpInside,文本工具从未选中切换到选中。 - 用户点击一次 PDF 目标位置。
PDFPageView子树出现新的ResizeTextBoxView和RichTextEditor。- 插件调用原生
paste,标签逐字回读一致。 - 插件设置文字颜色、字体和文本框 frame。
resignFirstResponder成功,编辑器失去焦点后仍显示。- 文本框位于 PDF 页面坐标中,放大缩小时随 PDF 同比例移动和缩放。
正式真机日志中,三条连续标签均完成:
- 新编辑器初始文字长度为 0。
- 粘贴后长度与目标字符串完全一致。
- 提交后
editable=false、selectable=false、firstResponder=false。 ResizeTextBoxView仍可见,并挂在PDFPageView。- 用户目视确认失焦显示和缩放跟随通过。
这使下面的现场工作流成为可能:
选择墙体类型
→ 输入长×高
→ 原子分配唯一编号
→ 用户点 PDF 箭头末端
→ 自动写入彩色机器文字
→ 保存结构化台账
→ 导出 CSV/Excel
但是要特别强调:
ResizeTextBoxView、RichTextEditor、工具栏按钮校准和视图层级扫描,属于“真机已证实的内部 UI 路线”,不是官方承诺稳定的文档层 API。
官方公开了通用 UIControl 和 UITextView 能力,但没有公开“在指定 PDF 页和指定矩形创建文本批注并返回 annotationId”的正式接口。
因此这条路线可以做真实插件,却必须:
- 固定目标 MarginNote 版本测试。
- 把工具按钮位置保存为归一化坐标,并允许重新校准。
- 每次放置重新
hitTest,不能长期持有旧按钮引用。 - 记录编辑器是否出现、文字是否一致、是否提交。
- MarginNote 更新后重新跑回归测试。
4.4 结构化台账与导出
现场工程量插件已经实现并验证的业务能力包括:
- Q1~Q5 多种墙体类型独立编号。
- 每种类型从 001 开始递增。
- 点击放置前先原子占用编号。
- 取消记为
void,失败记为pending,编号不回收。 - 标签使用机器文字,避免手写误读。
- 同时保存长度、高度、面积、图纸、页码、状态和时间。
- 使用 JSON 持久化结构化数据。
- 导出带 UTF-8 BOM 的 CSV,可直接由 Excel 打开。
这说明插件不必把所有数据都“塞进 MarginNote 笔记字段”。更稳健的模式是:
PDF 上的可视标签 = 给人看
插件自己的结构化台账 = 给程序、Excel 和校验逻辑使用
唯一编号 = 两者之间的稳定主键
当前原生文本框没有取得公开的 noteId/annotationId,所以编号加 docMd5 + page + normalizedFrame + label 是现实的关联方式,但不等同于官方稳定标识。
五、哪些路线失败了,以及为什么失败
失败结果同样是开发文档的一部分。
5.1 直接调用 EditTextBox command
在目标 PDF 上,EditTextBox 和 EditPaste 多轮查询均返回禁用。移除插件面板、切换输入状态、变更 window 参数后仍然无效。
结论:
- 不能仅因为内置命令表中有
EditTextBox,就把它当成当前上下文可调用 API。 - 最终成功路线是命中真实工具栏
UIControl,不是 command。 - 官方若开放正式的文档批注创建 API,插件将不再需要模拟用户点击工具栏。
5.2 在屏幕坐标上添加普通浮层
普通 UILabel 或自定义面板可以快速显示文字,但如果挂载在学习界面根视图:
- PDF 放大缩小时位置不跟随。
- 关闭文档后不会持久化。
- 导出 PDF 时不会出现。
它适合做工具面板、HUD、临时提示,不适合做最终文档批注。
5.3 假设文本框等于普通笔记
原生文本框出现时,没有新的 focusNote,也没有取得 noteId。因此不能假设它是 MbBookNote,再去写 noteTitle 或评论字段。
产品里看起来相似的对象,数据模型可能完全不同。
5.4 从手写层读取或写入完整笔画几何
目标版本可以读取 sketch note 的汇总信息,例如笔画数量和绘图数据大小;但没有发现公开 API 提供:
- 每一笔的坐标序列。
- Apple Pencil 压力、倾角和时间戳。
- 创建 stroke 并写回手写层。
- 创建直线、箭头、矩形等矢量形状。
- 把机器生成文字或图形合并到手写层。
因此当前插件仍让用户使用 MarginNote 原生画笔绘制线和箭头,插件只负责结构化数据和原生文本标签。
5.5 手动插入 PDF 留白
施工图四周可能都有有效内容,统一插入横向留白会切断图纸或影响阅读,不适合现场 CAD/PDF 场景。
一个功能“技术上能调用”,不代表它符合真实工作流程。
六、MarginNote 4 插件的能力边界
6.1 可以作为稳定基础的能力
- 插件生命周期和工具栏入口。
- 笔记、笔记本、文档的公开数据操作。
- Undo、数据库刷新和批量处理。
- 原生 UIKit 小面板。
- WebView 复杂界面及桥接。
- 本地配置、JSON/文本/二进制文件。
- 系统文件导入导出。
- 前台网络请求。
- 用户主动触发的自动化工作流。
6.2 能做,但需要版本探针和降级方案
- 扫描 MarginNote 内部视图层级。
- 通过真实工具栏按钮切换宿主工具。
- 捕获内部文本编辑器并修改内容。
- 使用未形成正式文档契约的搜索或索引方法。
- 依赖具体 controller 结构、类名或 selector 的功能。
这类功能必须配备:
- 版本检测。
- 方法存在性检查。
- 一键重新校准。
- 失败后保留数据、不重复编号。
- 诊断日志。
- 可关闭的实验开关。
6.3 当前没有可靠公开证据支持的能力
- 插件退出或 MarginNote 关闭后的无限后台运行。
- 任意访问 iPad 或其他 App 的文件系统。
- 在插件中加载 Swift/Objective-C 原生动态库。
- 执行系统 shell 命令。
- 稳定读写完整 Pencil 笔画几何。
- 通过正式 API 在 PDF 任意坐标创建和管理文本/图形批注。
- 任意控制脑图节点的最终布局坐标和渲染器。
- 把
base模板当作完整 SDK 或宿主模拟器。
七、从零开始:用 mn-rails 创建第一个工程
7.1 准备环境
建议准备:
- 一台安装 MarginNote 4 的 Mac 或 iPad。
- Node.js 和 npm/pnpm。
- Git。
- Codex。
- 一个备份后的测试学习集,前期不要使用重要数据。
在空目录中运行:
npx mn-rails
也可以指定模板:
npx mn-rails --template standard
npx mn-rails --template web
模板选择建议:
| 模板 | 适用场景 |
|---|---|
standard(仓库内部目录常称 base) |
小型工具、原生按钮、浮动面板、数据处理、探针插件 |
web |
设置中心、统计报表、复杂表格、React 界面、远程服务控制台 |
进入项目后:
npm install
npm run dev
构建发布包:
npm run build
Mac 上可以使用脚手架的本地热部署;iPad 测试通常是构建 .mnaddon,再通过 iCloud Drive、AirDrop 或系统“文件”导入 MarginNote。
7.2 推荐的源码分层
不要把所有代码塞进 main.js。
src/
├── main.js # 只负责 JSB.require 和 JSB.newAddon
├── Addon.js # 生命周期与 selector
├── Context.js # 当前学习集/文档/页面解析
├── Store.js # JSON 数据和迁移
├── NativeUI.js # 小型原生 UI
├── Feature.js # 业务逻辑
├── Diagnostics.js # 能力与错误日志
├── icon.png
└── mnaddon.json
main.js 保持简单:
JSB.require("Context");
JSB.require("Store");
JSB.require("NativeUI");
JSB.require("Feature");
JSB.require("Addon");
JSB.newAddon = function (mainPath) {
return createMyAddon(mainPath);
};
7.3 给 Codex 写一份项目规则
在项目根目录放置 AGENTS.md,至少写清楚:
# MarginNote 插件开发规则
- 运行环境是 JavaScriptCore,不使用 DOM、fetch、setTimeout 或 setInterval。
- 延时与轮询使用 NSTimer。
- 只在 src/main.js 调用 JSB.require。
- 结构化数据写入 Application.documentPath 下的插件专用目录。
- 数据库写入使用 UndoManager,并在写后刷新和回读。
- 不调用未验证的 selector;先检查方法是否存在。
- 修改后运行语法测试、业务测试和构建。
- 真机未通过的能力不得写成“已实现”。
这能显著减少 AI 把浏览器、Node.js 或普通 iOS 开发经验错误套进 MarginNote。
八、Codex 应该怎样参与开发
8.1 第一条提示词不要先写需求实现
可以把下面这段直接交给 Codex:
我要开发一个 MarginNote 4 插件,主要在 iPad 使用。
请先不要根据最终需求直接写插件。第一阶段请完成:
1. 阅读 marginnoteapp/Addon、mn-docs.museday.top、
Temsys-Shen/marginnote-addon-docs 和 Temsys-Shen/mn-rails。
2. 区分脚手架、官方头文件、文档、公开 API 和私有/内部 UI。
3. 输出能力矩阵:已公开、需真机验证、明确缺失、替代方案。
4. 为目标 MarginNote 版本制作只读探针,不读取笔记内容和敏感标识。
5. 探针通过后,再进入可撤销的受控写入测试。
6. 每条结论必须绑定日志或真机证据;不允许因为产品界面存在某功能,
就推断插件一定有对应 API。
然后再补充你的场景痛点、设备、MarginNote 版本和可接受的测试权限。
8.2 让 Codex 先建立能力矩阵
建议矩阵至少包含:
| 能力 | 文档证据 | 当前对象是否存在 | 方法是否存在 | 是否真机执行 | 风险 |
|---|---|---|---|---|---|
| 当前文档和页码 | 官方文档 | 是 | 是 | 是 | 低 |
| 创建普通笔记 | 官方文档 | 是 | 是 | 受控写入通过 | 中 |
| 创建 PDF 文本批注 | 无正式接口 | 内部编辑器存在 | 内部路线 | 真机通过 | 高 |
| 写入手写 stroke | 未发现 | 无 | 无 | 未执行 | 不可作为方案 |
8.3 分阶段授权探针
推荐安全等级:
阶段 A:只读环境探针
- 检查全局对象和方法是否存在。
- 检查生命周期、路径和 WebView。
- 不读取笔记正文、标识符和原始路径。
- 不写数据库。
阶段 B:受控写入
- 仅在备份后的测试学习集中进行。
- 创建带显眼前缀的测试笔记。
- 立即回读。
- 使用 Undo 或精确删除清理。
- 报告是否仍残留。
阶段 C:目标链路探针
- 一次只验证一个关键假设。
- 记录调用前后对象、文本、状态和时间。
- 避免同时执行两个兜底路径,否则无法判断是谁成功。
- 需要人工确认的项目与自动检测项目分开记录。
阶段 D:正式 MVP
- 探针和正式插件使用不同 addonid。
- 正式数据模型先于 UI 动画。
- 失败状态不能伪装成成功。
- 编号、账目等关键数据在 UI 操作前先落盘。
8.4 让诊断日志成为产品的一部分
推荐日志结构:
{
"schemaVersion": 1,
"pluginVersion": "0.1.0",
"host": {
"appVersion": "4.4.5",
"osName": "iPadOS"
},
"events": [
{
"at": "2026-08-02T00:00:00.000Z",
"event": "feature-started",
"details": {}
}
],
"errors": []
}
默认日志不要包含:
- 笔记正文。
- 用户身份。
- 原始沙盒路径。
- 不必要的 notebookId、noteId。
- 网络密钥。
需要排查时,再由用户明确授权扩展范围。
九、一个可复用的能力探针写法
先检查存在性,不要直接调用:
function hasMethod(object, name) {
if (!object) return false;
try {
return typeof object[name] === "function";
} catch (error) {
return false;
}
}
var app = Application.sharedInstance();
var db = Database.sharedInstance();
var report = {
application: {
studyController: hasMethod(app, "studyController"),
openURL: hasMethod(app, "openURL"),
saveFileWithUti: hasMethod(app, "saveFileWithUti")
},
database: {
getNoteById: hasMethod(db, "getNoteById"),
savedb: hasMethod(db, "savedb"),
allDocuments: hasMethod(db, "allDocuments")
},
runtime: {
Promise: typeof Promise,
fetch: typeof fetch,
setTimeout: typeof setTimeout
}
};
方法存在只代表“可以继续测试”,不等于业务结果已经通过。
完整验收应分成:
对象存在
→ selector 存在
→ 调用没有抛错
→ 返回值正确
→ 宿主界面发生目标变化
→ 数据可回读
→ 重开仍存在
→ 导出结果正确
十、如何设计一个不会重号、不会漏数据的插件
工程量插件里最重要的不是文本框动画,而是数据规则。
10.1 先占号,再操作 UI
用户点击“开始放置”
→ 写入 reserved 记录
→ nextSequence + 1
→ 原子保存 JSON
→ 激活文本工具
→ 用户点 PDF
→ 粘贴成功后改为 placed
如果用户取消:
reserved → void
如果文本框超时或粘贴失败:
reserved → pending
void 和 pending 都不回收编号。这样可能出现“作废号”,但不会出现两个实体共用同一编号。
10.2 配置和业务数据分开存
NSUserDefaults:面板位置、选中墙型、字号等少量设置。- JSON 文件:项目、墙型、计数器、工程量记录、状态。
- 临时目录:待导出的 CSV、诊断快照。
关键 JSON 使用原子写入并回读:
var data = NSJSONSerialization.dataWithJSONObjectOptions(store, 1);
var ok = data.writeToFileAtomically(filePath, true);
if (!ok) throw new Error("数据写入失败");
var readBack = NSData.dataWithContentsOfFile(filePath);
if (!readBack || readBack.length() === 0) {
throw new Error("写入后回读失败");
}
10.3 现场 UI 要为 PDF 让路
真正录入时,插件不应长期占用大块 PDF:
- 常驻态只显示一个小胶囊:当前类型和下一个编号。
- 输入时短暂展开数字卡片。
- 等待 PDF 点击时只保留细条状态提示。
- 写入完成立即收起。
- 复杂设置和台账放在单独页面。
“设置页面可以大,现场录入必须小”是专业工作插件的重要设计原则。
十一、给 MarginNote 官方的 API 建议
现在的接口已经足以证明专业工作场景的价值,但大量开发精力仍花在“寻找内部按钮和视图”上。以下接口会直接改变插件生态的上限。
P0:正式的 PDF 批注 API
建议提供:
var annotation = DocumentAnnotation.createText({
documentId: "...",
pageIndex: 0,
rect: { x: 0.4, y: 0.3, width: 0.2, height: 0.04 },
coordinateSpace: "normalized-page",
text: "Q2-017|3.60×2.80",
color: "#D6191E",
fontSize: 7
});
同时需要:
- 返回稳定的
annotationId。 - 查询、更新、删除、移动和调整大小。
- 明确页面坐标和屏幕坐标转换。
- 重开、同步和 PDF 导出的行为契约。
- Undo/Redo 事务。
- 给批注附加插件自己的结构化 metadata。
有了这组 API,当前依赖 UIButton → RichTextEditor → ResizeTextBoxView 的内部 UI 路线可以全部删除。
P0:手写与矢量形状 API
建议开放:
- 读取每一笔的点、压力、倾角、时间。
- 创建和更新 stroke。
- 直线、箭头、矩形、圆、多段线等形状。
- 图层枚举、显隐、锁定和排序。
- Pencil 事件订阅。
- 插件生成图形进入原生同步和 PDF 导出。
这会让测量、批改、工程标注、医疗影像、设计审阅等场景真正形成闭环。
P0:工具状态与工具切换 API
建议提供:
reader.currentTool();
reader.activateTool("pen", { presetId: "red-2px" });
reader.activateTool("text");
目前插件只能通过校准并点击内部工具栏按钮切换工具,工具栏一旦改版就需要重新适配。
P1:文档事件和稳定定位
建议增加:
- 当前可见页变化事件。
- PDF zoom、pan、rotation 事件。
- 页面视口与页坐标转换。
- 批注创建、修改、删除事件。
- 稳定的文档、页面、批注关联 ID。
P1:专业数据与导出扩展点
建议增加:
- PDF 导出前后 hook。
- 批注 flatten 策略。
- 插件自定义数据随学习集备份/同步。
- 标准 CSV/XLSX 导出帮助器。
- 可控的文件夹授权和安全书签。
P1:安全与长期任务
建议明确:
- 插件权限声明。
- 安全凭据存储。
- 前台长任务、取消和进度 API。
- 有限后台同步的规则。
- 插件数据容量、迁移和卸载策略。
十二、MN4 从学习阶段走向工作阶段的可能性
MarginNote 的核心优势不是单一的 PDF 阅读,也不是单一的脑图,而是:
原始文档
+ 手写/批注
+ 结构化笔记
+ 脑图关系
+ 搜索与复习
+ 插件自动化
当这些能力进入专业工作,可能形成下面的产品方向。
工程与施工
- 现场工程量测量。
- 图纸问题编号和整改闭环。
- 材料、房间、设备分类统计。
- 图纸批注与 Excel/BIM/CAD 数据联动。
建筑、设计和制造
- 方案审阅和设计变更记录。
- 图纸版本差异对照。
- 零件、节点和问题卡片自动生成。
- 评审意见按专业和责任人汇总。
法律与合规
- 合同条款编号和风险分类。
- 证据页码与案件时间线关联。
- 审查意见批量导出。
- 文档批注与结构化案件数据同步。
医疗、科研和质量管理
- 医学图像或报告的结构化标注。
- 文献证据卡片与研究问题关联。
- 巡检、缺陷、实验结果的编号和统计。
- 标准操作程序与现场记录闭环。
教育之外的知识工作
- 咨询项目资料库。
- 产品需求评审。
- 会议材料到行动项。
- 采购比价、投标文件、尽调和审计。
学习场景通常关注“理解和记忆”;工作场景还要求:
- 唯一编号。
- 状态流转。
- 数据校验。
- 批量导出。
- 可追溯。
- 多文档关联。
- 可复核和可交付。
插件正是连接这两类场景的桥梁。
十三、发布前检查清单
资料与能力
- 每个关键 API 都有官方文档、头文件或真机证据。
- 没有把“宿主能做”写成“插件能做”。
- 内部 UI 路线已显眼标注升级风险。
- MarginNote 版本和设备已记录。
数据安全
- 先在备份测试集验证。
- 数据库写入有 Undo 或精确清理方案。
- 关键数据先落盘,再执行 UI 动作。
- 失败、取消和超时不会复用编号。
- 日志默认不包含正文、身份和密钥。
交付质量
- 运行 JavaScript 语法检查。
- 纯业务逻辑有宿主外单元测试。
- 真机测试失焦、缩放、翻页、重开和导出。
- 工具栏布局变化后重新校准。
-
.mnaddon根目录结构正确。 - 版本号、README 和构建产物一致。
论坛发帖建议
- 明确测试版本,避免把经验写成永久契约。
- 同时写成功路线和失败路线。
- 提供最小复现、日志字段和验收标准。
- 对官方提出具体 API 形态,而不只是“请开放更多接口”。
十四、最后的经验
用 Codex 开发 MarginNote 插件,最重要的不是提示词有多长,而是证据链是否完整。
最有效的合作方式是:
用户提供真实痛点和现场约束
Codex 负责资料审计、探针、实现和日志分析
MarginNote 真机负责证明宿主行为
项目文档负责保存结论和失败历史
不要让 AI 先承诺“什么都能实现”;也不要因为公开 API 不完整,就过早认定所有路线都走不通。
这次工程量插件经历了多轮失败:command 禁用、浮层不随页、文本框归因错误、提交证据不完整。最终通过真实 UIControl、原生编辑器、PDF 页面坐标和结构化台账,完成了可用链路。
这正是 Codex 最适合发挥价值的地方:它不只是生成代码,还可以持续阅读文档、构造安全实验、分析日志、修正结论,并把一个模糊痛点逐步变成可验证的工具。
而对 MarginNote 来说,插件生态的下一步也不只是“更多学习插件”。如果正式开放 PDF 批注、手写几何、工具状态、坐标转换和专业数据接口,MN4 完全有潜力从个人学习系统继续扩展为工程、法律、医疗、科研、设计和现场管理的平台。