@xiongtao.xt/tau-plugins 0.31.0 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +2 -2
  3. package/client/{chunk-ECEBBIHP.js → chunk-SVY5LBAT.js} +44 -0
  4. package/docs/README.md +43 -0
  5. package/docs/ai.md +169 -0
  6. package/docs/backend.md +168 -0
  7. package/docs/editor.md +457 -0
  8. package/docs/excerpts.md +126 -0
  9. package/docs/flashcards.md +127 -0
  10. package/docs/getting-started.md +134 -0
  11. package/docs/mcp.md +173 -0
  12. package/docs/plugin-basics.md +209 -0
  13. package/docs/providers/README.md +108 -0
  14. package/docs/providers/images.md +65 -0
  15. package/docs/providers/publish.md +144 -0
  16. package/docs/providers/sync.md +75 -0
  17. package/docs/providers/tts.md +62 -0
  18. package/docs/providers/upload.md +77 -0
  19. package/docs/providers/video.md +118 -0
  20. package/docs/release.md +130 -0
  21. package/docs/services.md +159 -0
  22. package/docs/site-data.md +90 -0
  23. package/docs/skills.md +129 -0
  24. package/docs/storage-and-network.md +62 -0
  25. package/docs/ui.md +335 -0
  26. package/docs/user-data.md +389 -0
  27. package/docs/whiteboard.md +290 -0
  28. package/package.js +1 -1
  29. package/package.json +1 -1
  30. package/publish-artifacts.js +1 -1
  31. package/publish-client.js +3 -1
  32. package/types/packages/plugins/src/categories.d.ts +3 -0
  33. package/types/packages/plugins/src/contract.d.ts +0 -2
  34. package/types/packages/plugins/src/index.d.ts +0 -1
  35. package/types/packages/plugins/src/package.d.ts +2 -0
  36. package/AI.md +0 -116
  37. package/API.md +0 -881
  38. package/BACKEND.md +0 -68
  39. package/DEVELOPMENT.md +0 -173
  40. package/EDITOR.md +0 -192
  41. package/FLASHCARDS.md +0 -92
  42. package/IMAGES.md +0 -16
  43. package/MCP.md +0 -118
  44. package/NOTIFICATIONS.md +0 -48
  45. package/PUBLISH-DATA.md +0 -48
  46. package/PUBLISH.md +0 -50
  47. package/SERVICES.md +0 -119
  48. package/SYNC.md +0 -37
  49. package/TTS.md +0 -20
  50. package/UPLOAD.md +0 -35
  51. package/VIDEO.md +0 -65
  52. package/WHITEBOARD.md +0 -115
  53. package/types/packages/plugins/src/skills.d.ts +0 -8
package/MCP.md DELETED
@@ -1,118 +0,0 @@
1
- # 插件 MCP
2
-
3
- 通过 `context.mcp.register()` 向 Tau 的 AI 提供工具,一个插件可以注册多个 MCP。它们直接出现在已有 MCP 页面和聊天快捷面板,用户可以整项启停、逐工具启停,不需要插件自行开发管理界面。
4
-
5
- 需要 SDK 0.11.0 和包含此能力的 Tau 0.59.0 桌面客户端构建。旧客户端没有 `context.mcp`,应检查并明确提示升级;SDK 类型版本不能替代客户端能力检查。
6
-
7
- ## 本地工具和外部服务器
8
-
9
- 插件页面可以通过 `PluginPageProps.pageContext.register(provider)` 给现有 `get_current_context` 提供上下文,返回任意 JSON,形状完全由作者决定,见 [API.md 的页面说明](API.md#提供当前页面上下文)。AI 读取后可以把其中的信息传给本插件注册的 MCP;页面上下文和 MCP 独立注册,不新增页面操作接口。该能力需要 SDK 0.29.0 和包含实现的客户端构建。
10
-
11
- 每次注册填写局部 `id`、`name` 和可选 `description`,再提供 `tools` 或 `transport`,两者互斥。相同插件内 MCP 的 ID 不能重复;不同插件可以使用相同局部 ID。每个 MCP 中的工具名称唯一。
12
-
13
- 下面是一个完整插件示例。第一项提供本地查询函数;第二项连接外部 MCP,示例地址须替换成真实服务。
14
-
15
- ```ts
16
- import { definePlugin } from "@tau/plugins";
17
-
18
- declare const __PLUGIN_META__: { id: string; name: string; version: string };
19
-
20
- export default definePlugin({
21
- ...__PLUGIN_META__,
22
- create: () => ({
23
- activate(context) {
24
- if (!context.mcp) throw new Error("请升级到支持插件 MCP 的 Tau 客户端");
25
-
26
- context.mcp.register({
27
- id: "notes",
28
- name: "我的笔记",
29
- description: "读取本插件保存的笔记",
30
- tools: [
31
- {
32
- name: "read_note",
33
- description: "按笔记键读取本插件中的一条笔记",
34
- inputSchema: {
35
- type: "object",
36
- properties: { key: { type: "string", description: "笔记键" } },
37
- required: ["key"],
38
- additionalProperties: false,
39
- },
40
- async execute(args, { signal }) {
41
- signal.throwIfAborted();
42
- if (typeof args.key !== "string") throw new Error("请提供笔记键");
43
- const note = await context.storage.get(args.key);
44
- signal.throwIfAborted();
45
- return { content: note === undefined ? "未找到笔记" : JSON.stringify(note) };
46
- },
47
- },
48
- ],
49
- });
50
-
51
- context.mcp.register({
52
- id: "remote",
53
- name: "外部知识服务",
54
- transport: { type: "streamable-http", url: "https://your-service.example/mcp" },
55
- });
56
- },
57
- }),
58
- });
59
- ```
60
-
61
- 本地工具填写 `name`、`description`、对象类型的 JSON Schema `inputSchema`,以及 `execute(args, { signal })`。执行结果为 `{ content: string }`,结构化数据可用 JSON.stringify 返回。工具名称使用 1–128 位字母、数字、点、横线或下划线;宿主会生成全局工具名称,作者不要自行拼接命名空间。所有插件工具沿用现有 execute 级审批。
62
-
63
- 需要本地进程时,先按 BACKEND.md 使用格式 2、独立后台入口及 `systemAccess: ["process"]`,并在 `backend.tools` 声明对应 command 和固定参数前缀。该版本需要系统能力审核和本机后台授权;未声明的旧 stdio 插件需更新,HTTP 和本地 JS tools 不受此要求影响。命令不经过 Shell,工作目录须为绝对路径;环境只包含宿主最小集合与插件显式传入值。Windows 目标必须是 EXE。
64
-
65
- 例如声明 `tools: [{ id: "mcp", command: "node", args: ["/path/to/your-server.mjs"] }]` 后注册:
66
-
67
- ```ts
68
- context.mcp.register({
69
- id: "local-server",
70
- name: "本地 MCP 服务",
71
- transport: {
72
- type: "stdio",
73
- command: "node",
74
- args: ["/path/to/your-server.mjs"],
75
- },
76
- });
77
- ```
78
-
79
- 该片段放在 activate(context) 内,路径须替换。不会自动安装 Node、Python、npm 包或其他依赖,启动前须具备对应运行环境。stdio 支持可选 args、env、cwd;streamable-http 支持可选 headers。不要把密钥、令牌或用户机器路径打进分发包,用户可在 MCP 页面配置本机连接参数。
80
-
81
- ## 管理和生命周期
82
-
83
- - 激活成功后才展示和提供工具;重复启用不会重复登记。激活失败、插件停用、切库或卸载自动回收。
84
- - MCP 页面关闭一项只关闭该项 AI 能力,不停用插件的页面、命令或其他 MCP。用户选择会跨启用和重启保留,重新注册不会强行开启。
85
- - 用户可管理每个工具的开关。插件提供的名称和说明由作者维护;外部服务器支持连接配置、日志和重连,插件函数没有连接配置。
86
- - 返回的 Disposable 可提前注销单项 MCP,无需再交给 context.register。
87
- - 工具调用 signal 在取消、插件停用、整项或对应工具关闭时中止,调用上限为 60 秒。异步代码应响应 signal;取消不能撤销已经完成的写入或外部请求。
88
- - 旧实例、旧数据库和重连前的调用不能转交给新实例。不要缓存上下文到下一次启用。
89
- - Skill 可描述这些工具的用途;ctx.ai.chat/stream 仍是文本生成接口,不会因为注册 MCP 而自动获得工具调用能力。
90
-
91
- ## 聊天工具自定义渲染
92
-
93
- `context.chat.registerToolRenderer` 的类型契约自 SDK 0.15.0 提供,须搭配包含实现的客户端构建。旧客户端即使同为 0.59.0 也可能没有此能力,必须检查方法是否存在;只提高 SDK 类型版本不能替代客户端检查。
94
-
95
- 在 `async activate(context)` 中,先通过 `context.mcp.register()` 声明工具,再注册组件并 await:
96
-
97
- ```tsx
98
- await context.chat.registerToolRenderer({
99
- mcpId: "notes",
100
- toolName: "read_note",
101
- component: ({ execution, renderDefault }) =>
102
- execution.status === "success" ? <div>{execution.output}</div> : renderDefault(),
103
- });
104
- ```
105
-
106
- 这段代码放在异步激活函数内;`mcpId` 是本插件注册 MCP 时的 id,`toolName` 是其中的原始工具名,不拼接全局名称。可以绑定本地工具或本插件 transport 中的远端工具,不能绑定内置工具、其他插件或用户手动添加的服务器。返回 `Promise<Disposable>`,失败随 await 抛出;激活成功后才发布组件,停用或切库自动回收,无需再次 context.register。
107
-
108
- 本地工具可返回 `{ content: string, render: { version: 1, data: JsonValue } }`;content 提供模型文本及默认展示,render.data 是独立的可持久化展示数据。组件参数为 `PluginToolRendererProps`(从 `@tau/plugins` 导入),包括 execution、isDark、parsedInput、inputParseError、data、dataVersion 和 renderDefault。展示版本为正整数,数据只接受有限 JSON;不支持的版本应调用 renderDefault。
109
-
110
- transport 工具当前读取 execution.output,不提供标准 structuredContent 或 MCP Apps HTML 界面能力。宿主决定渲染路由,远端返回不能选择其他渲染器。等待审批继续使用宿主审批 UI,失败和取消显示通用工具行。
111
-
112
- 关闭 MCP 或工具执行开关不会隐藏历史结果;停用插件时回到通用输入输出,再启用后恢复。组件不应在渲染或挂载时自动执行工具。使用现有 React 和 `@tau/ui`/主题样式,不导入内部聊天包。
113
-
114
- 完整示例见仓库 `scripts/plugins/fixtures/chat-tool-renderer.tsx`。在宿主仓库执行 `node scripts/plugins/verify-chat-author.mjs`,会生成 SDK 包、在独立目录安装、类型检查并构建该示例 ZIP;不安装到用户目录,也不发布。产物和验收记录保存在 `output/plugin-chat-sdk/`。
115
-
116
- ## 验证
117
-
118
- 检查类型并打包后,在包含此实现的客户端启用插件:确认两项 MCP 出现在现有页面和快捷面板;测试整项开关、逐工具开关、外部连接配置和重连,再测试停用、重新启用及重启后偏好保留。真实服务器连接与模型调用需使用自己的服务和已配置的模型。
package/NOTIFICATIONS.md DELETED
@@ -1,48 +0,0 @@
1
- # 插件通知能力
2
-
3
- SDK 0.28.0 与包含此接口的客户端通过激活上下文提供 `context.notifications`,旧客户端可先检查该属性是否存在。本接口属于渲染端 `PluginContext`,独立后台可经既有后台事件通知渲染插件,再由渲染插件调用;应用退出后不会继续发送。
4
-
5
- ```ts
6
- const notification = context.notifications.send({
7
- title: "同步完成",
8
- body: "本次更新了 12 条内容。",
9
- mode: "auto",
10
- tone: "ok",
11
- });
12
-
13
- // 需要提前撤销时调用;停用或切库时宿主也会自动回收。
14
- await notification.dispose();
15
- ```
16
-
17
- | mode | 行为 |
18
- | -------------- | ---------------------------------------------------------- |
19
- | `auto`(默认) | 插件所在窗口聚焦时进入应用内通知栈,失焦时进入独立通知窗口 |
20
- | `in-app` | 固定进入插件所在窗口的通知栈,不主动聚焦窗口 |
21
- | `system` | 固定进入 Tau 独立通知窗口,不使用操作系统原生通知 |
22
-
23
- 分流发生在发送时;发送后切换窗口焦点不会搬移通知。Web 预览没有独立窗口,沿平台既有规则退化为应用内展示。
24
-
25
- 标题必填,最多 200 字符;正文可选,最多 5000 字符。`tone` 支持 `neutral`、`ok`、`danger`,默认 `neutral`。`sticky: true` 保持显示直到用户处理、关闭或插件撤销。来源标签固定为插件名称,不接受插件伪造来源或指定全局通知 ID。
26
-
27
- 可传 `actions`(最多两个按钮,最多一个 `primary: true`):
28
-
29
- ```ts
30
- context.notifications.send({
31
- title: "扫描完成",
32
- body: "点击查看结果。",
33
- sticky: true,
34
- actions: [
35
- {
36
- label: "查看结果",
37
- primary: true,
38
- onClick: async () => {
39
- await resultsPage.open();
40
- },
41
- },
42
- ],
43
- });
44
- ```
45
-
46
- 按钮回调返回 Promise 时显示执行中状态,失败在通知内展示;可用 `resolvedText` 指定成功后的结果文案。独立通知窗口通过现有桥接在来源窗口执行回调。通知撤销或插件失活后,旧回调拒绝执行;已经开始的异步业务仍应遵循 `context.signal`。
47
-
48
- 发送不需要业务数据权限或即时用户手势,适用于异步任务完成提示。`send` 返回的是已提交给通知路由的句柄,不是用户已看到通知的回执;主进程不可用时沿 `appNotify` 的既有策略放弃推送。
package/PUBLISH-DATA.md DELETED
@@ -1,48 +0,0 @@
1
- # 独立站点的数据接入
2
-
3
- 自 SDK 0.19.0 提供以下公开子入口;本文的数据结构对应 SDK 0.23.0(含 0.20.0 的站点数据调整)。这些入口用于独立站点,和 [PUBLISH.md](PUBLISH.md) 中向远端上传文件的发布提供方是两种用途。
4
-
5
- 普通 Tau 插件仍然使用 SDK 类型开发,构建时将宿主注入模块设为 external。SDK 根入口和 UI 包不携带编辑器、组件或页面的运行实现。
6
-
7
- 博客模板一般直接使用 `@tau/blog`:它包含数据读取、搜索、正文渲染和内容插件接入,见该包 README。只想读取数据、自己渲染正文的站点,可使用 `@tau/plugins/publish-data` 的纯数据类型和 `@tau/plugins/publish-client` 的数据客户端。客户端只有数据读取、摘要校验、取消和搜索,不包含 React、渲染器、路由或预览界面。
8
-
9
- ```ts
10
- import { createClient } from "@tau/plugins/publish-client";
11
-
12
- const client = createClient({ manifestUrl: "https://example.com/data/site.<摘要>.json" });
13
- const manifest = await client.readManifest();
14
- const item = manifest.items[0];
15
- if (item) {
16
- const result = await client.readData({ key: item.key });
17
- console.log(result);
18
- }
19
- ```
20
-
21
- ## 数据分布
22
-
23
- - 清单 `site.<摘要>.json`:站点信息、全部内容的 key 与数据文件、搜索索引引用,以及只出现一次的站点级数据——`settings`(发布外观设置)、`collections`(项目列表)、`cardTagTree`(卡片标签树)、`projectTrees`(按项目 id 的文档目录)。`readData` 会把 `settings` 一并带回。
24
- - 站点信息 `site`:宿主提供 `title`、`basePath`、可选的 `publicUrl` 与 `favicon`;其余站点配置(描述、副标题、布局开关等)由模板在 manifest 的 `template.siteFields` 中声明,用户在「站点设置 · 博客模板」填写,发布时以 `site.fields[key]` 交给模板。`fields` 只包含已声明的键,未填写时取声明的 `default`;没有默认值时文本类为空字符串、开关为 `false`、下拉为第一个选项。字段类型为 `text`、`textarea`、`url`、`select`、`switch`,最多 32 个。首页数据只含时间线与统计,介绍区由模板根据自己的字段渲染。
25
- - 内容文件:`key`、`meta`、`content`,以及可选的 `links`(本文正文里已发布的站内链接目标)、`pluginRenderers`(内容插件产物)和 `contentStyles`(正文动态样式)。资源以所属数据文件的 URL 为上下文解析。
26
- - 公开路由完全由模板决定,数据里只有 ContentRef(`{ key, type, id? }`),没有 URL。
27
-
28
- 正文的站内链接由 `@tau/blog` 按 `links` 渲染,模板只提供 `hrefFor`。自己渲染正文的站点按 `links` 的键(`${contentType}:${refId}`,与求索资料的 `citeKey` 同一口径)查目标,查不到即目标未发布。
29
-
30
- ## 样式
31
-
32
- `contentStyles` 是宿主为正文里的 HTML 节点编译的 Tailwind 工具类,属于过渡产物:按原文加载,含 `:root` 主题变量和全局类名,正文挂载期间对整个页面生效。模板若自定义了同名 Tailwind 主题变量,正文挂载时可能被这些默认值覆盖。
33
-
34
- ## 插件产物
35
-
36
- `@tau/plugins/publish-artifacts` 提供 C23 清单校验、定义求值与节点归属检查,仅处理协议,不提供 UI、模块映射或资源生命周期。共享模块由模板通过实际公开能力提供;`@tau/blog` 已内置这部分接入。
37
-
38
- ## 构建边界
39
-
40
- | 入口 | 用途 | 独立站点构建 |
41
- | ---------------------------------------------- | ----------------------------- | -------------------------------- |
42
- | `@tau/plugins`、`@tau/ui` | Tau 注入的插件 API 与界面组件 | 只有类型;不能当作站点运行时打包 |
43
- | `@tau/plugins/whiteboard`、`@tau/plugins/sync` | 白板与同步类型契约 | 使用 `import type` |
44
- | `@tau/plugins/publish-data` | 发布数据类型 | 使用 `import type` |
45
- | `@tau/plugins/publish-client` | 数据读取、摘要校验与搜索 | 有 ESM 实现,可以打包进独立站点 |
46
- | `@tau/plugins/publish-artifacts` | 插件发布产物协议处理 | 有 ESM 实现,可以打包进独立站点 |
47
-
48
- 本模板的 `build.mjs` 面向运行于 Tau 内的代码插件,生成 CJS ZIP;独立博客站点应使用自己的站点构建,不直接复用计数器入口或本地安装脚本。根入口在普通 Node 或浏览器执行时会报错,这是宿主注入约定。模板分发包与代码插件也不能在同一 manifest 中同时声明。
package/PUBLISH.md DELETED
@@ -1,50 +0,0 @@
1
- # 博客发布提供方
2
-
3
- 需要 SDK 0.21.0 及包含博客发布能力的客户端。提供方只在渲染进程注册和执行,网络使用 `context.fetch`。本地导出由宿主提供,不注册本地发布插件。
4
-
5
- 在 activate 中调用 `context.publish.register({ id: context.pluginId, name, create(config) })`。create 返回文件式或快照式实例,配置是本轮固定的 JSON 副本。提供方 ID 必须等于所属插件 ID。
6
-
7
- 两种实例都实现 `describe(signal)` 与 `checkConnection(signal)`;describe 返回 `identity`、`basePath`、可选 `publicUrl`。identity 是不含密钥的物理目标稳定身份,不随插件版本、模板、Token 或提交方式变化。basePath 以斜杠开头和结尾,根路径为 `/`。
8
-
9
- `checkConnection` 返回 `Promise<boolean>`,只检查目标可访问性;网络或认证失败可以抛出可读错误。以下注册结构可放在插件的 `activate` 内,实际存储协议由 `createTarget` 实现:
10
-
11
- ```ts
12
- import type { JsonValue, PluginContext, PublishInstance } from "@tau/plugins";
13
-
14
- export function registerPublisher(
15
- context: PluginContext,
16
- createTarget: (config: JsonValue) => PublishInstance
17
- ) {
18
- return context.publish.register({
19
- id: context.pluginId,
20
- name: "我的发布目标",
21
- create: createTarget,
22
- });
23
- }
24
- ```
25
-
26
- 文件式实例声明 `mode: "files"`,实现 `uploadFile(path, Uint8Array, signal)` 与 `deleteFile(path, signal)`,可用 `concurrency` 声明 1–32 的上限。方法成功返回 void,失败抛出异常。宿主负责增量、重试、依赖先行和入口后置、旧资源清理,不把远端目录当成可任意删除的文件清单。
27
-
28
- 快照式实例声明 `mode: "snapshot"`,实现 `publish(snapshot, signal)`。snapshot 包含本轮临时 id、待上传文件的路径和摘要及删除路径。只有远端确认整轮提交后才返回;失败抛异常,不返回部分成功。可选 dispose 在会话关闭时回收连接。
29
-
30
- Git 快照使用 `context.services.git.publishSnapshot({ publicationId: snapshot.id, remote, branch, author: { name, email }, authentication: { username, password }, cacheId? }, { signal })`。remote 只接受不含凭据的 HTTPS 地址;认证通过任务通道使用,不写入 remote URL。publicationId 仅在所属窗口本轮发布中有效,不能用于读取任意本地文件。需要缓存目录时调用 `context.services.git.selectCacheDirectory({})`,由宿主选择并返回 `{ id, label }`;只保存 id 和展示用 label,不传入任意 cwd 或 shell 命令。
31
-
32
- 配置视图使用 `context.settings.register({ id, title, target: "publish", component })`。组件接收 `PluginSettingsProps.publishConfig` 的 value 与 save,保存成功后再提交表单值,不用按工作区隔离的 context.storage 保存账号。普通插件详情页提供前往博客发布页的入口。
33
-
34
- 配置视图渲染在宿主「站点设置 · 发布目标」组卡片内,位于宿主的连接状态条与「服务」行之后。表单按行输出:每行为左侧 64px 标签加右侧控件,`min-height: 46px`、`padding: 8px 12px`,每行顶部用 `--tau-line-2` 分隔,标签字号用 `--tau-text-meta`、颜色用 `--tau-ink-2`。不要再包一层卡片或标题。辅助动作(如「读取仓库」)放在对应输入框的尾部;低频项收进行内的「高级选项」折叠区。宿主负责测试连接和完成按钮,插件不提供「保存」或「测试连接」按钮,改为自动保存:输入停止约 600ms 或失焦时调用 `save`,组件卸载前保存未提交的修改。宿主回写的配置与最近一次提交值相同时,不要覆盖正在输入的表单。
35
-
36
- 离开发布页继续运行;停用、切库和发起窗口关闭使旧会话失效。每次请求都传递 signal,错误信息不得包含凭据。新安装默认停用,插件缺失或未启用时宿主保留用户配置及选择,不自动切换其他目标。
37
-
38
- ## 配置与错误处理
39
-
40
- `publishConfig` 是可选属性,组件先检查它是否存在,再读取 `value: JsonValue` 并校验字段。`await publishConfig.save(value)` 失败时在表单末行显示错误并保留当前输入,下次修改后重试。配置随用户选择的提供方保存,不将 Token 写入 `identity`、公开 URL、README 或日志。
41
-
42
- 文件路径由宿主给出,是发布站点内的相对路径。适配具体远端接口时保留路径语义并正确编码,不拼成 shell 命令。使用 `context.fetch` 时检查 `response.ok`,每次请求都传入本轮 signal;不吞掉异常后返回成功,否则宿主会误记为已发布。删除操作应能处理文件已经不存在的重试。
43
-
44
- `selectCacheDirectory` 在用户取消时返回 `null`,不要把它当成失败或自行构造缓存 ID。`create(config)` 同步返回实例,连接和发布任务放入对应异步方法,不在创建实例时发起后台请求。
45
-
46
- ## 验证
47
-
48
- 至少覆盖:配置保存失败、连接失败、重复发布、远端文件已存在或已删除、用户取消、插件停用、切换工作区,以及快照提交中途失败后重试。用真实远端验证成功之后,才能声称支持该发布目标;SDK 类型检查不验证远端协议。
49
-
50
- 发布目标插件负责把文件送到远端,独立站点读取数据与模板构建见 [PUBLISH-DATA.md](PUBLISH-DATA.md)。
package/SERVICES.md DELETED
@@ -1,119 +0,0 @@
1
- # 编译与转换服务
2
-
3
- 需要在插件里编译 Tailwind,转换 Markdown,或处理音视频时,可以使用这些服务。通过 `context.services` 调用,类型从 `@tau/plugins` 导入,需要 Tau 0.59.0 或更高版本及实现相应能力的客户端构建。本文对应 SDK 0.24.0。TikZ 编译属于独立绘图插件,Typst 排版属于 `tau.typst` 插件(自带 WebAssembly 引擎),均不再提供宿主服务。
4
-
5
- 所有服务都是异步方法,只处理传入数据并返回结果,不读写笔记、不选择或保存文件、不自动注入样式。
6
-
7
- ## API 一览
8
-
9
- | 方法 | 输入 | 返回 |
10
- | ---------------------------------------- | ------------------------------------------------- | --------------------------------------- |
11
- | `tailwind.compile(input, options?)` | `{ source, language?: "html" \| "tsx" \| "jsx" }` | `{ css }` |
12
- | `markdown.deserialize(source, options?)` | Markdown 字符串 | `{ nodes: Descendant[], warnings }` |
13
- | `markdown.serialize(nodes, options?)` | `Descendant[]` | `{ markdown, warnings }` |
14
- | `markdown.render(source, options?)` | Markdown 字符串 | `{ html, css, warnings }` |
15
- | `ffmpeg.run(input, options?)` | `{ files, args, outputs }` | `{ files, log }` |
16
- | `videoExtractor.probe(url, options?)` | 视频页面地址,可选 `proxy`(SDK 0.22.0) | `{ title, cover?, duration?, formats }` |
17
- | `git.publishSnapshot(input, options?)` | 当前发布快照、HTTPS 仓库、分支与认证 | `void` |
18
- | `git.selectCacheDirectory({}, options?)` | 打开宿主缓存目录选择器 | `{ id, label } \| null` |
19
-
20
- Git 服务自 SDK 0.21.0 提供,完整参数与发布会话约束见 [PUBLISH.md](PUBLISH.md)。视频解析与播放源协议见 [VIDEO.md](VIDEO.md)。
21
-
22
- ## 在静态组件中获取服务
23
-
24
- SDK 0.16.0 的 `usePluginServices()` 返回当前插件可用的同一组服务,类型为 `PluginServices | undefined`。服务由插件运行时提供,独立编辑窗口无需 activate;博客和未包裹的普通组件返回 undefined,组件应显示已保存结果或源码。它不属于源码块或 EditorKit,不要将服务存入模块全局变量。
25
-
26
- 组件需用自己的 AbortSignal 取消过期或卸载的调用,宿主同时处理窗口与工作区失效。内容插件的启停按现有重启生效规则处理;写入启停意图不会提前拆掉当前窗口已生效的贡献。
27
-
28
- ## 取消与错误
29
-
30
- 第二个参数均支持 `{ signal: AbortSignal }`。插件停用会自动取消本实例的任务,不必重复传入 `context.signal`。用户主动取消可使用单次任务的 `AbortController`。
31
-
32
- ```ts
33
- const controller = new AbortController();
34
- try {
35
- const { css } = await context.services.tailwind.compile(
36
- { source: '<div class="p-4">Hello</div>', language: "html" },
37
- { signal: controller.signal }
38
- );
39
- // 将 css 用于插件自己的预览。
40
- } catch (error) {
41
- if (error instanceof Error && error.name === "AbortError") return;
42
- throw error;
43
- }
44
- // 对应取消按钮:controller.abort();
45
- ```
46
-
47
- 失败通过 Promise 拒绝返回 `Error`,`message` 包含可展示的错误信息;不要匹配内部错误文本决定业务流程。Markdown 同步解析/序列化只能在调用前后检查取消,不能中断正在执行的 JavaScript。HTML 转换内部已开始的渲染可能继续完成,但取消后不会交回结果。
48
-
49
- ## Tailwind
50
-
51
- ```ts
52
- const { css } = await context.services.tailwind.compile({
53
- source: '<section class="p-4 rounded-tau-md bg-surface text-ink-1">Hello</section>',
54
- language: "html",
55
- });
56
- ```
57
-
58
- 使用 Tailwind 4,支持 Tau 语义类。返回 theme/utilities,不重复提供 preflight。源码只用于提取完整类名,不执行 TSX/JSX,也不编译 React。动态拼接类名不能保证被提取。Tau 语义类依赖 Tau 提供的 `--tau-*` 变量,离开 Tau 的页面需要自行提供相应主题变量。
59
-
60
- 固定插件界面通常在打包时编译样式;此服务主要用于运行时生成内容。返回 CSS 由插件自行使用并在停用时清理。
61
-
62
- ## Markdown 与编辑器节点
63
-
64
- ```ts
65
- import type { Descendant } from "@tau/plugins";
66
-
67
- const parsed = await context.services.markdown.deserialize("# 标题\n\n* [x] 已完成");
68
- const nodes: Descendant[] = parsed.nodes;
69
- const result = await context.services.markdown.serialize(nodes);
70
- const preview = await context.services.markdown.render(result.markdown, { theme: "light" });
71
- ```
72
-
73
- 使用与 Tau 客户端一致的语法,包括标题、段落、列表和任务列表、表格、代码、数学公式、图片与媒体、Tau directive 等。`Descendant[]` 是数据,不关联某篇笔记,不写入编辑器。转换可能生成新的节点 UUID,不要依靠反复解析保留节点身份。
74
-
75
- 优先处理解析得到的节点。Tau 文本节点包含 `type: "formatted"`,不是只写 `{ text: "..." }`;直接构造节点时遵循 SDK 类型。
76
-
77
- 自定义节点可通过单次调用的 `codecs` 提供编解码器:
78
-
79
- ```ts
80
- const result = await context.services.markdown.serialize(nodes, {
81
- codecs: [
82
- {
83
- type: "example.note",
84
- serializer: { toMarkdown: (_node: unknown, children: string) => `> ${children}` },
85
- },
86
- ],
87
- });
88
- ```
89
-
90
- `codecs` 类型为 `EditorMarkdownCodec[]`;同类型替换本次转换的默认 codec,调用结束不保留。编辑器贡献的 codec 不隐式加入服务,需明确传入。仅声明序列化器不会自动得到反序列化规则;需要往返时还需提供相应的 directive 或解析规则。
91
-
92
- `warnings` 每项包含 `code`、`message` 和可选节点 `path`。当前报告未识别节点和 HTML 已知降级,不是任意语法、属性的完整无损审计。未知节点序列化可能保留为 Tau 扩展编码,其他 Markdown 工具未必理解。
93
-
94
- HTML 渲染不自动执行 TikZ、Typst、Mermaid 编译器;相关节点可能显示源码并返回 `DISPLAY_FALLBACK`,音视频可能显示链接。原始 HTML 和远程资源按客户端导出规则保留,输出不是经过净化的 HTML。`css` 是本次生成内容附带的样式,不包含完整 Tau 页面主题,也不打包图片附件。
95
-
96
- ## FFmpeg
97
-
98
- SDK 0.23.0 已移除 `ffmpeg.probe` 和 `MediaInfo`,客户端不再随包分发 FFprobe。已有插件应删除对应调用与类型依赖;需要本地媒体元数据时由插件自行实现。`videoExtractor.probe` 只查询视频页面,不能接收本地文件。
99
-
100
- `ServiceFile` 为 `{ name: string, data: Blob }`;文件名仅在任务内使用,不是客户端路径。输入输出名称不得重复,目录使用 `/`。文件整体经内存传输,适合可以放进内存的素材,不支持大文件流式处理。
101
-
102
- ```ts
103
- const input = { name: "input.wav", data: audioBlob };
104
- const result = await context.services.ffmpeg.run(
105
- {
106
- files: [input],
107
- args: ["-i", "input.wav", "-c:a", "pcm_s16le", "output.wav"],
108
- outputs: [{ name: "output.wav", mimeType: "audio/wav" }],
109
- },
110
- {
111
- onProgress: ({ time }) => console.log("已处理秒数", time),
112
- }
113
- );
114
- const outputBlob = result.files[0].data;
115
- ```
116
-
117
- `args` 不包含程序名;含空格的参数作为一个数组元素,不额外加 shell 引号。服务设置非交互、覆盖临时输出和进度通道,不要覆盖 `-progress` 或依赖交互 stdin。`outputs` 声明要取回的产物,名称与参数中的输出一致;文件未生成会报错,退出非零不会返回部分成功结果。
118
-
119
- `onProgress.time` 是已处理媒体时间(秒),不是百分比。首次使用 FFmpeg 时 Tau 可能需要联网下载,之后会复用;取消任务不会删除已下载的 FFmpeg。`log` 保留最后 64 KiB 诊断输出。返回 MIME 类型由 `outputs.mimeType` 指定,调用者须确保与实际格式一致。
package/SYNC.md DELETED
@@ -1,37 +0,0 @@
1
- # 同步存储插件
2
-
3
- 需要 SDK 0.10.0 及以上,以及包含渲染端同步注册支持的 Tau 0.59.0 客户端构建。插件只在渲染进程执行,沿用main.js入口,不声明manifest.sync,也不提供Node后台产物。
4
-
5
- ## 注册与职责
6
-
7
- 在activate(context)中调用context.sync.register({ id: context.pluginId, name, create })。一个插件注册一个提供方,create(config)根据本次任务的JSON配置返回下列五个方法;停用或激活失败时注册自动回收。create在真正发起任务时调用,打开配置页不会连接存储服务。
8
-
9
- | 方法 | 返回值 | 用途 |
10
- | ------------------------------- | ------------------------- | ---------------------------------------------------- |
11
- | uploadFile(name, bytes) | Promise<boolean> | 普通上传 |
12
- | downloadFile(name) | Promise<Uint8Array> | 读取对象 |
13
- | fileExists(name) | Promise<boolean> | 检查对象是否存在 |
14
- | listFiles(prefix?) | Promise<RemoteFileInfo[]> | 内部完成分页,返回相对路径、名称、大小及可选修改时间 |
15
- | createFileIfAbsent(name, bytes) | Promise<boolean> | 按远端原子条件创建对象 |
16
-
17
- SyncProvider、SyncProviderDefinition和RemoteFileInfo类型从@tau/plugins/sync导入。该模块仅提供纯契约与声明辅助函数,不是Node执行入口。
18
-
19
- 客户端负责同步内容、对象命名、版本提交、冲突、资源计划和恢复。插件负责存储服务请求,不增加能力声明、服务商探测或独立同步引擎。条件创建不能用先查询再覆盖替代;请求失败后由客户端按原提交规则核验结果。
20
-
21
- ## 网络与依赖
22
-
23
- 通过context.fetch发送请求。它由宿主提供网络传输,并与插件生命周期关联。签名、URL与协议逻辑在插件内执行;浏览器可用的SDK可以把自己的HTTP传输接到context.fetch。
24
-
25
- 普通npm依赖随main.js按浏览器目标打包;不导入fs、http、Node Buffer或其他Node内置模块,不借用宿主node_modules,不另打包Node依赖目录。保留模板external配置。
26
-
27
- ## 配置界面
28
-
29
- 通过context.settings.register({ id: "sync", title, target: "sync", component })将配置表单挂在“设置→同步”。每个插件注册一个同步配置分区。
30
-
31
- 组件从PluginSettingsProps.syncConfig.value读取配置,通过await syncConfig.save(config)保存。字段和校验由插件定义;失败时保留输入并展示错误。凭据不要写入日志或工作区context.storage;宿主按插件ID在本机应用级保存配置。
32
-
33
- 客户端每次任务固定配置。关闭设置页不影响任务,停用插件保留配置;运行中的任务阻止停用和卸载。窗口关闭或崩溃时任务不会转移到Node进程继续执行。CLI不再提供同步功能。
34
-
35
- ## 从旧同步示例升级
36
-
37
- SDK0.9.0的manifest.sync/sync.js示例已撤回。将存储实现作为普通模块由main.js导入,在activate内注册,并改用浏览器兼容依赖及context.fetch;删除manifest.sync,重新打包。不要通过其他字段恢复Node入口。
package/TTS.md DELETED
@@ -1,20 +0,0 @@
1
- # 语音提供方
2
-
3
- 需要 SDK 0.13.0 和包含语音插件能力的 Tau 0.59.0 客户端。
4
-
5
- 在activate中调用context.tts.register({id: context.pluginId, name, create})。create(config, modelId)同步创建任务实例,固定该账号配置和模型,不发送网络请求。每个插件可对应多个用户账号,不能用插件全局变量保存“当前API Key”。
6
-
7
- | 方法或字段 | 职责 |
8
- | -------------------------------------------------- | ----------------------------------------------------------------------------------- |
9
- | getVoices(signal) | 返回id、name、type(system/cloned/custom)、可选description组成的音色数组 |
10
- | synthesize({text,voiceId,options},signal) | 返回{audio:Uint8Array,mime:string};options仅含speed、volume、pitch |
11
- | cloneVoice({file,requestedId?,name?},signal),可选 | 每次一个{name,mimeType,content}参考文件,返回实际创建的音色;不返回上传文件ID或void |
12
- | textInstructions,可选 | 当前模型的合成文本规则;供宿主播客脚本使用,缺省为纯文本 |
13
-
14
- 请求使用context.fetch并传入方法收到的signal。服务商超时用AbortSignal.any组合,不能覆盖取消信号;退避等待也须取消,不把AbortError当作可重试错误。
15
-
16
- 配置通过settings.register的target:"tts"挂载。组件接收ttsConfig.instanceId、value、models和save(value,models),只保存当前账号,不自动选为默认,不覆盖同插件其他账号。模型项为{id,label?};用户可填写自定义模型。保存失败保留表单并显示错误,不另存一份凭据到context.storage。
17
-
18
- 需要公开上传参考音频时调用context.upload.uploadRemote(file,{signal}),复用用户选定的远端上传配置。本地模式明确报错;MiniMax自有文件上传不需要图床。不要自行选择用户的上传插件、读取上传凭据或修改桶权限。
19
-
20
- 主进程只转发数据和提供宿主能力。插件在主窗口渲染进程执行,不使用Node后台入口。宿主负责音频播放、合并和保存;插件不直接写笔记或文件。
package/UPLOAD.md DELETED
@@ -1,35 +0,0 @@
1
- # 资源上传插件
2
-
3
- 需要 SDK 0.12.0 和包含资源上传插件能力的 Tau 0.59.0 客户端。上传返回正文可直接引用的 HTTP(S) 地址;数据库和资源目录同步使用 SYNC.md 的另一套接口。
4
-
5
- SDK 0.13.0 增加第三个signal参数与context.upload.uploadRemote(file,{signal})。提供方应将signal合并进context.fetch请求;调用方使用uploadRemote时,本地目标明确报错,不自动更换存储。signal取消会结束等待并通知提供方,不代表撤回已经上传的远端对象。
6
-
7
- 在 activate 中注册提供方:
8
-
9
- ```tsx
10
- context.upload.register({
11
- id: context.pluginId,
12
- name: "我的资源存储",
13
- async upload(file, config, signal) {
14
- // 用 context.fetch 请求服务商,认证与路径由本次 config 决定。
15
- // file.name 已唯一命名,file.mimeType 是 MIME,file.content 为 Uint8Array。
16
- return uploadToStorage(context.fetch, file, config, signal);
17
- },
18
- });
19
- context.settings.register({
20
- id: "upload",
21
- title: "我的资源存储",
22
- target: "upload",
23
- component: UploadSettings,
24
- });
25
- ```
26
-
27
- UploadSettings 接收 PluginSettingsProps。读取 uploadConfig.value,在用户保存时调用 await uploadConfig.save(config),宿主保存并选中该提供方;不要将同一份上传配置再存到 context.storage。配置是 JsonValue,读取时自行校验字段并给出错误提示。组件在资源存储设置页显示;插件详情页提供跳转入口。
28
-
29
- 每个插件只能注册一个上传提供方,id 必须等于 context.pluginId。宿主负责压缩、文件唯一命名、本地保存和跨窗口转发。插件只负责远端上传和返回资源地址,不读取用户文件路径,也不修改笔记内容。
30
-
31
- 请求通过 context.fetch 发送,失败抛出错误,不返回空地址或吞掉失败。停用插件会取消上下文请求并拒绝迟到结果。插件在渲染进程执行,不使用 Node 模块或额外后台入口。
32
-
33
- 返回地址必须长期可引用;不要返回短期签名 URL。桶或仓库的公开读取策略由用户配置,上传成功不等于匿名用户可以访问。不要自动修改用户桶权限。需要自定义公开域名时由插件设置提供,按对象路径返回完整 URL。
34
-
35
- 完成后验证:本地模式不调用插件、远端上传成功及失败、保存配置、停用、独立窗口上传、重启恢复。README.md 应说明如何配置公开访问和常见服务商错误。
package/VIDEO.md DELETED
@@ -1,65 +0,0 @@
1
- # 视频提供方
2
-
3
- 需要 SDK 0.22.0 和支持该能力的客户端。通过 `context.video.register({ id: context.pluginId, name, create })` 注册,每个插件一个提供方。视频来源可以是视频网站,也可以是自建的视频服务(如 NAS)。
4
-
5
- 启用后,编辑器、白板、项目视频笔记和学习资料的「添加视频」中会出现以 `name` 命名的入口。用户从该入口粘贴视频链接,客户端先用 `matches` 确认链接属于本插件,再调用 `resolve`。
6
-
7
- 插件只负责视频来源本身的知识:识别链接、查询标题与可选清晰度、给出真实的流地址和所需请求头。选择弹窗、下载进度、合并音视频、缓存和播放都由客户端完成,插件不提供这些界面,也不传输视频字节。
8
-
9
- ## 实例
10
-
11
- `create(config)` 按当前插件配置同步创建实例,不发网络请求。实例需要实现四个方法:
12
-
13
- - `matches(url)`:同步判断链接是否属于本来源,不访问网络。链接属于其他已启用插件时,客户端会提示用户改用对应入口。
14
- - `resolve(url, signal)`:返回 `{ title, cover?, variants, defaultVariant? }`。`variants` 是可选清晰度(至少一项),每项为 `{ label, hint?, ref }`。
15
- - `cacheKey(ref)`:返回稳定的缓存身份,应包含清晰度。视频已缓存时客户端不再调用 `getPlayback`,可以离线播放。
16
- - `getPlayback(ref, signal)`:每次缓存前现取播放源,可以返回会过期的直链。
17
-
18
- ## 视频引用
19
-
20
- 用户选中的 `ref` 原样写入文档和数据库,以后只能通过它找回视频。它必须是可长期保存的 JSON,例如视频 ID 加清晰度,不要放会过期的直链、Cookie 或令牌。`type`、`title`、`thumbnail` 三个键由客户端占用,引用中不要使用。
21
-
22
- ## 播放源
23
-
24
- `getPlayback` 返回以下三种之一:
25
-
26
- - `{ kind: "file", stream }`:单个音视频合一的文件。
27
- - `{ kind: "split", video, audio }`:音视频分离的流,客户端分别下载后无损合并。
28
- - `{ kind: "extract", url, videoFormat?, audioFormat?, proxy? }`:交给客户端内置的视频解析器下载,格式 ID 来自 `context.services.videoExtractor.probe`。
29
-
30
- `stream` 为 `{ url, headers? }`。需要 Referer、Cookie 等请求头时放在 `headers` 中,客户端只在下载时使用,不写入文档或缓存索引。网络请求使用 `context.fetch` 并传入方法的 signal,它可以设置这些请求头。
31
-
32
- ## 视频解析服务
33
-
34
- `context.services.videoExtractor.probe(url, { proxy?, signal? })` 返回 `{ title, cover?, duration?, formats }`,`formats` 每项包含 `id`、`videoOnly`、`audioOnly`、`ext`、`width`、`height`、`fps`、`bitrate`、`size`、`note`。插件根据格式列表组织清晰度,再返回 `kind: "extract"` 的播放源;插件不能自行启动外部程序。
35
-
36
- ## 配置
37
-
38
- 账号、Cookie 或代理通过 `context.settings.register({ id, title, target: "video", component })` 注册配置视图,挂载在「设置 · 账号与连接 · 在线视频」。组件接收 `PluginSettingsProps.videoConfig`:`value` 和 `save(value)`,每个插件一份配置,保存后下一次调用 `create` 即读到新值。保存失败时保留草稿,成功后才提示已保存。
39
-
40
- 插件停用或调用方取消时,进行中的请求会收到取消信号,迟到的结果被丢弃。插件未启用时,已保存的视频显示需要启用插件的提示,原数据保留。移动端和发布站点不运行插件,插件提供的视频在那里只显示说明。
41
-
42
- ## 注册与验证
43
-
44
- 注册放在 `activate` 中,实例的四个方法由来源适配实现,并使用 SDK 类型检查返回值:
45
-
46
- ```ts
47
- import type { JsonValue, PluginContext, VideoInstance } from "@tau/plugins";
48
-
49
- export function registerVideoSource(
50
- context: PluginContext,
51
- createSource: (config: JsonValue) => VideoInstance
52
- ) {
53
- return context.video.register({
54
- id: context.pluginId,
55
- name: "我的视频来源",
56
- create: createSource,
57
- });
58
- }
59
- ```
60
-
61
- 在 manifest 中声明 `categories: ["video"]`,便于用户从插件中心筛选;分类本身不注册提供方。解析输入、网络响应与持久化的 ref 都要校验;`defaultVariant` 是从 0 开始的索引,缺省时宿主使用第一项。不要把 `cacheKey` 简化为本次的签名直链,账号刷新或直链过期不应改变同一视频清晰度的缓存身份。
62
-
63
- 验证应覆盖:来源不匹配、格式为空、链接过期后重新取源、音视频分离、已缓存时离线播放、账号更新、取消与停用。单文件流须能由宿主直接下载;需要视频解析器处理的页面使用 `kind: "extract"`。具体网站的限制与认证失败由插件返回可读错误,不返回虚假的可播放结果。
64
-
65
- SDK 0.23.0 删除的是 `services.ffmpeg.probe` 本地媒体探测,`services.videoExtractor.probe` 保留;区别与 FFmpeg 转换接口见 [SERVICES.md](SERVICES.md)。