文档快照
读取当前或指定已打开文档,获取未保存正文、语言及 SHA-256。
sdk.editor.getActiveDocument()sdk.editor.getDocument(documentId)sdk.editor.listDocuments()sdk.editor.onDidChangeDocuments(callback)sdk.editor.onDidChangeContent(callback)sdk.editor.onDidSave(callback)用一个独立插件,扩展写作的每一步。
从 Markdown 样式到文档工具,让能力自然融入 MokiDown。
状态、差异与版本,使用编辑器现有界面。
本机热重载,一个文件完成打包和交付。
独立运行环境,关闭即撤销样式与界面贡献。
准备 Node.js 22.13+,在 MokiDown 源码目录安装依赖。脚手架会生成带配置、状态栏、选区操作和 Markdown 样式的阅读助手。
npm ci
npm run plugin:create -- ./my-plugins/reading example.reading
npm run plugin:dev -- ./my-plugins/reading打开「设置 → 语言服务 → 插件开发」,填写终端打印的 http://127.0.0.1:3141/package,点击连接。
编辑 src/index.mjs 或样式。开发服务重新构建,编辑器校验新包并重载运行环境,通常约 2 秒生效。
执行下方命令,在 releases/plugins 取得带版本号和摘要的单文件包,可直接导入或发布到 GitHub Release。
npm run plugin:pack -- ./my-plugins/reading
# 或进入生成的项目后执行 npm run dev / npm run build支持 127.0.0.1、localhost 和 IPv6 回环 HTTP 地址。会话不自动恢复,增加权限后需断开并重新连接;失败构建保留上次可用包。暂停插件即停止轮询,断开会话同时卸载运行环境。
reading/
├─ manifest.json # 元数据、权限和配置声明
├─ package.json # dev / build 与本地 SDK 类型
├─ .moki.mjs # 本地工具入口
└─ src/
├─ index.mjs # definePlugin 入口
└─ reading.css # 可撤销的 Markdown 样式插件只导入 @mokidown/sdk,不引用编辑器内部组件或 store。开发工具随 MokiDown 源码提供;移动插件目录后可设置 MOKIDOWN_TOOLS 指向此源码的 scripts 目录。执行 npm install 可安装脚手架声明的本地 SDK,供编辑器类型提示使用。
只声明实际使用的权限。fileExtensions 限定工作区读写类型;没有文件操作的样式插件可以省略它。配置表单由宿主生成,保存配置会重启插件并保留启用状态。
{
"id": "example.reading",
"name": "阅读助手",
"version": "1.0.0",
"entry": "src/index.mjs",
"permissions": ["editor.read", "ui.statusBar", "styles.markdown"],
"configuration": {
"fields": [
{"key": "fontSize", "label": "正文字号", "type": "number", "default": 16, "min": 12, "max": 28}
]
}
}配置类型:text、number、boolean、select、url。secret 使用固定键 credential,directory 使用 workspace。访问密钥由宿主保存,不返回给插件;目录使用工作区 API 访问,不暴露绝对根路径。
SDK 1.3.0 保持 apiVersion: 1。先查询能力,再调用新接口;旧插件保持兼容。卡片上的权限名称对应清单声明。
const context = await sdk.context();
if (await sdk.capabilities.has('editor.selection')) {
const selection = await sdk.editor.getSelection();
// selection: { documentId, from, to, text, checksum }
}读取当前或指定已打开文档,获取未保存正文、语言及 SHA-256。
sdk.editor.getActiveDocument()sdk.editor.getDocument(documentId)sdk.editor.listDocuments()sdk.editor.onDidChangeDocuments(callback)sdk.editor.onDidChangeContent(callback)sdk.editor.onDidSave(callback)读取源码编辑器的 UTF-16 选区与选中文本。
sdk.editor.getSelection(documentId?)通过校验和编辑正文、聚焦标签与设置选区,保留撤销历史。
sdk.editor.applyEdits(id, checksum, edits)sdk.editor.setSelection(id, checksum, from, to?)sdk.editor.focusDocument(id)sdk.editor.reloadDocument(id) — also workspace.read注入差异操作和历史列表,业务策略留在插件。
sdk.editor.showDiff(options)sdk.editor.showVersions(options)sdk.versions.set(source) / remove(path)sdk.versions.onDidSelect(callback)sdk.versions.onDidRequestRefresh(callback)列出和读取授权目录内声明的文件类型。
sdk.workspace.list(path?)sdk.workspace.read(path)通过预期校验和创建、更新或删除文件,拒绝覆盖未保存内容。
sdk.workspace.write(path, content, expectedHash?)sdk.workspace.delete(path, expectedHash)独立持久存储,支持读取、写入、列举及删除键。
sdk.storage.get(key) / set(key, value)sdk.storage.keys() / delete(key)向配置的服务发送 JSON 请求,密钥由宿主附加。
sdk.net.request('/path', { method, body })注册异步命令并返回操作结果,卸载时清理。
sdk.commands.register(id, title, asyncCallback) → unsubscribe注入文档状态、菜单动作和任务进度。
sdk.statusBar.set(id, item) / remove(id)sdk.tasks.set(id, task) / remove(id)按扩展名或路径注入 SVG 图标和状态标记。
sdk.fileIcons.set(id, decoration) / remove(id)sdk.fileBadges.set(id, decoration) / remove(id)发送使用编辑器主题的限时通知。
sdk.notifications.show(message, { tone: 'success', duration: 4000 })注入限定到 Markdown 内容区域的样式,卸载即撤销。
sdk.styles.setMarkdown(id, css) / remove(id)查询深浅主题、监听切换,通过 AbortSignal 与清理回调释放资源。
sdk.context()sdk.capabilities.get() / has(name)sdk.theme.get() / onDidChange(callback)sdk.onDispose(callback) / signal读取已声明的非密钥配置;不返回访问密钥和目录绝对路径。
sdk.configuration.get()没有匹配的接口,试试 editor、storage 或 theme。
下载完整 TypeScript 类型 ↗文档快照包含未保存正文和校验和。编辑及设置选区都要求 expectedChecksum,避免异步返回覆盖用户后续输入。偏移使用 UTF-16,与 JavaScript 字符串一致;多个编辑范围不得重叠。
import { definePlugin } from '@mokidown/sdk';
definePlugin(async (sdk) => {
const stop = await sdk.commands.register('reading.quote', '添加引用', async () => {
const selected = await sdk.editor.getSelection();
if (!selected?.text) return;
await sdk.editor.applyEdits(selected.documentId, selected.checksum, [{
from: selected.from, to: selected.to,
text: '> ' + selected.text,
}]);
});
sdk.onDispose(stop);
});读取文档和样式注入支持两种模式。选区读取、选区定位及文本编辑当前使用源码编辑器,富文本模式下返回 SOURCE_EDITOR_REQUIRED。编辑走原生编辑器事务,可撤销;应用修改不会自动保存磁盘文件。
onDidChangeContent 在输入暂停约 120ms 后发送 {documentIds},不携带整篇正文。按需调用 getDocument(id) 读取;onDidChangeDocuments 负责标签打开、切换与关闭,onDidSave 负责保存事件。
插件在隐藏的隔离运行环境里执行。通过 SDK 注册编辑器贡献:状态栏可绑定文档,任务显示进度,文件图标支持路径或扩展名,历史源进入版本列表。无需创建独立插件页面。
await sdk.statusBar.set('reading.state', {
documentId: doc.id, text: '检查完成', tone: 'success',
command: 'reading.review',
});
await sdk.editor.showDiff({
path: doc.path, content: referenceText, label: '修改建议',
actions: [{id: 'apply.edits', title: '应用修改', command: 'reading.apply'}],
});差异动作回调收到 document、draftContent 和 referenceContent。返回成功结果后关闭对比,抛出异常则保留界面。宿主不解释动作标题,也不承担同步、翻译或其他业务策略。编辑器只显示当前标签对应的状态;版本列表的选择和刷新通过 sdk.versions 事件交给插件。
静态样式在清单声明,动态样式通过 SDK 注入;两者都限定在 Markdown 内容内,不影响设置和工具栏。后启用的插件遵循 CSS 层叠,禁用后全部撤销。
const { fontSize = 16 } = await sdk.configuration.get();
await sdk.styles.setMarkdown('reading', `
:scope { font-size: ${fontSize}px; }
h1, h2 { color: var(--accent); }
blockquote { border-left: 3px solid var(--accent); }
`);
const stopTheme = sdk.theme.onDidChange(async (theme) => {
// 可以按深浅主题计算需要的样式变量。
});
sdk.onDispose(stopTheme);:scope / :root 代表 Markdown 容器。支持普通规则、@media 和 @supports;嵌套语法需预先展开。不支持外部 URL、@import、@font-face、fixed / sticky。单张样式上限 128 KiB;样式 ID 相同会替换。
definePlugin 的激活函数可返回清理函数;也可用 onDispose 注册多个清理回调,按注册逆序执行。关闭或更新插件后,宿主移除命令、状态、图标、版本源、样式、事件订阅和运行环境,取消宿主网络请求。
definePlugin(async (sdk) => {
const timer = setInterval(() => refresh(), 10000);
sdk.onDispose(() => clearInterval(timer));
const stop = sdk.editor.onDidSave(() => refresh());
sdk.onDispose(stop);
// 可将 sdk.signal 交给接受 AbortSignal 的异步任务。
return () => releaseLocalResources();
});激活失败或未捕获异常会显示运行错误并停用插件;启动未完成会报超时。开发会话在错误运行环境卸载后继续等待构建,修复源码会自动恢复。命令的业务异常仅返回给调用方,不自动停用。不要吞掉业务失败后返回成功。
打包器将 JavaScript 依赖合并为单文件 IIFE,并收集清单中声明的 CSS。不要从 JavaScript import CSS。生成的文件名含版本和摘要前缀;每次正式变更应增加版本号,不替换已发布版本的资产。
{
"id": "example.reading",
"name": "阅读助手",
"version": "1.0.0",
"url": "https://github.com/you/reading/releases/download/v1.0.0/example.reading-1.0.0-DIGEST.mokiplugin.json",
"sha256": "使用打包器输出的完整摘要",
"checksumMode": "canonical-json-v1"
}清单摘要使用规范化 JSON:移除顶层 integrity 后,递归排序对象键,保留数组顺序与字符串内容,再计算 SHA-256。它不是文件原始字节摘要。包、清单、版本和内置资源必须对应;更新会逐项校验,不通过则保留当前版本。
SDK 采用语义版本号,新能力通过 capability 查询。API 1 的既有方法保持兼容;旧宿主缺少 capabilities 时 SDK 返回空列表,插件可降级到已存在的功能。
| 错误 | 处理方式 |
|---|---|
| PERMISSION_DENIED | 将所需权限加入清单,重新打包;开发会话增加权限后需重新连接。 |
| DOCUMENT_CHANGED | 重新读取快照或选区,基于最新 checksum 计算修改。 |
| SOURCE_EDITOR_REQUIRED | 切换源码模式后执行选区或文本编辑;样式可继续在富文本中使用。 |
| PLUGIN_START_TIMEOUT | 确认入口调用 definePlugin,输出为完整 IIFE,且没有未打包的依赖。 |
| PLUGIN_CHECKSUM_MISMATCH | 对齐清单和包版本,使用 canonical-json-v1 摘要,不使用文件原始字节摘要。 |
| 开发服务 503 | 查看终端构建错误,修复后保存。运行中的旧包不会因此被替换。 |
文件读写仅在授权目录内进行,不能逃逸路径或覆盖未保存的编辑器内容。网络 API 仅访问配置的服务地址,RPC 默认超时 35 秒。通知每插件每 10 秒最多 8 条,展示 1.5–10 秒。版本源上限 100 项;文件列表 SVG 由宿主清理。