@xiongtao.xt/tau-plugins 0.19.0 → 0.21.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.
- package/CHANGELOG.md +13 -0
- package/PUBLISH-DATA.md +14 -4
- package/PUBLISH.md +17 -0
- package/package.json +1 -1
- package/publish-artifacts.js +51 -13
- package/publish-client.js +17 -8
- package/types/packages/markdown/src/codec.d.ts +2 -2
- package/types/packages/plugins/src/categories.d.ts +43 -0
- package/types/packages/plugins/src/contract.d.ts +2 -0
- package/types/packages/plugins/src/index.d.ts +1 -0
- package/types/packages/plugins/src/package.d.ts +10 -1
- package/types/packages/plugins/src/publish-artifacts.d.ts +0 -1
- package/types/packages/plugins/src/publish-data.d.ts +18 -12
- package/types/packages/plugins/src/publish.d.ts +59 -0
- package/types/packages/plugins/src/services.d.ts +5 -0
- package/types/packages/plugins/src/settings.d.ts +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# 更新记录
|
|
2
2
|
|
|
3
|
+
## 0.21.0
|
|
4
|
+
|
|
5
|
+
- 新增 context.publish 发布提供方、publishConfig 配置表单和文件式/快照式提交协议,本地导出仍由宿主提供。
|
|
6
|
+
- 新增受控 Git 快照发布与缓存目录选择服务,插件不获得任意命令或宿主文件访问。
|
|
7
|
+
- 插件分类新增博客发布。
|
|
8
|
+
|
|
9
|
+
## 0.20.0
|
|
10
|
+
|
|
11
|
+
- manifest 支持 `categories`(插件中心分类)与 `keywords`(目录搜索),门户上传严格校验,客户端忽略未知值。
|
|
12
|
+
- 博客模板包(`template` 分支)同样支持分类与关键词,只含静态站点产物,不能同时声明代码插件入口。
|
|
13
|
+
- publish-data:站点级数据(`settings`、`collections`、`cardTagTree`、`projectTrees`)改为只在清单中出现;内容文件新增 `links`,只含本文用到且已发布的站内链接目标。
|
|
14
|
+
- publish-client:`readData` 随正文带回清单的 `settings`;检索引擎按索引缓存,不再每次查询重建。
|
|
15
|
+
|
|
3
16
|
## 0.19.0
|
|
4
17
|
|
|
5
18
|
- 补齐 publish-data 类型、publish-client 纯数据客户端及 publish-artifacts 协议校验/定义求值入口;数据与产物声明不连带载入 Editor/React/UI 类型。
|
package/PUBLISH-DATA.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
普通 Tau 插件仍然使用 SDK 类型开发,构建时将宿主注入模块设为 external。SDK 根入口和 UI 包不携带编辑器、组件或页面的运行实现。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
博客模板一般直接使用 `@tau/blog`:它包含数据读取、搜索、正文渲染和内容插件接入,见该包 README。只想读取数据、自己渲染正文的站点,可使用 `@tau/plugins/publish-data` 的纯数据类型和 `@tau/plugins/publish-client` 的数据客户端。客户端只有数据读取、摘要校验、取消和搜索,不包含 React、渲染器、路由或预览界面。
|
|
6
6
|
|
|
7
7
|
```ts
|
|
8
8
|
import { createClient } from "@tau/plugins/publish-client";
|
|
@@ -12,8 +12,18 @@ const manifest = await client.readManifest();
|
|
|
12
12
|
const result = await client.readData({ key: manifest.items[0].key });
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
## 数据分布
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
- 清单 `site.<摘要>.json`:站点信息、全部内容的 key 与数据文件、搜索索引引用,以及只出现一次的站点级数据——`settings`(发布外观设置)、`collections`(项目列表)、`cardTagTree`(卡片标签树)、`projectTrees`(按项目 id 的文档目录)。`readData` 会把 `settings` 一并带回。
|
|
18
|
+
- 内容文件:`key`、`meta`、`content`,以及可选的 `links`(本文正文里已发布的站内链接目标)、`pluginRenderers`(内容插件产物)和 `contentStyles`(正文动态样式)。资源以所属数据文件的 URL 为上下文解析。
|
|
19
|
+
- 公开路由完全由模板决定,数据里只有 ContentRef(`{ key, type, id? }`),没有 URL。
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
正文的站内链接由 `@tau/blog` 按 `links` 渲染,模板只提供 `hrefFor`。自己渲染正文的站点按 `links` 的键(`${contentType}:${refId}`,与求索资料的 `citeKey` 同一口径)查目标,查不到即目标未发布。
|
|
22
|
+
|
|
23
|
+
## 样式
|
|
24
|
+
|
|
25
|
+
`contentStyles` 是宿主为正文里的 HTML 节点编译的 Tailwind 工具类,属于过渡产物:按原文加载,含 `:root` 主题变量和全局类名,正文挂载期间对整个页面生效。模板若自定义了同名 Tailwind 主题变量,正文挂载时可能被这些默认值覆盖。
|
|
26
|
+
|
|
27
|
+
## 插件产物
|
|
28
|
+
|
|
29
|
+
`@tau/plugins/publish-artifacts` 提供 C23 清单校验、定义求值与节点归属检查,仅处理协议,不提供 UI、模块映射或资源生命周期。共享模块由模板通过实际公开能力提供;`@tau/blog` 已内置这部分接入。
|
package/PUBLISH.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
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
|
+
文件式实例声明 `mode: "files"`,实现 `uploadFile(path, Uint8Array, signal)` 与 `deleteFile(path, signal)`,可用 `concurrency` 声明 1–32 的上限。方法成功返回 void,失败抛出异常。宿主负责增量、重试、依赖先行和入口后置、旧资源清理,不把远端目录当成可任意删除的文件清单。
|
|
10
|
+
|
|
11
|
+
快照式实例声明 `mode: "snapshot"`,实现 `publish(snapshot, signal)`。snapshot 包含本轮临时 id、待上传文件的路径和摘要及删除路径。只有远端确认整轮提交后才返回;失败抛异常,不返回部分成功。可选 dispose 在会话关闭时回收连接。
|
|
12
|
+
|
|
13
|
+
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 命令。
|
|
14
|
+
|
|
15
|
+
配置视图使用 `context.settings.register({ id, title, target: "publish", component })`。组件接收 `PluginSettingsProps.publishConfig` 的 value 与 save,保存成功后再提交表单值,不用按工作区隔离的 context.storage 保存账号。普通插件详情页提供前往博客发布页的入口。
|
|
16
|
+
|
|
17
|
+
离开发布页继续运行;停用、切库和发起窗口关闭使旧会话失效。每次请求都传递 signal,错误信息不得包含凭据。新安装默认停用,插件缺失或未启用时宿主保留用户配置及选择,不自动切换其他目标。
|
package/package.json
CHANGED
package/publish-artifacts.js
CHANGED
|
@@ -36,6 +36,54 @@ function comparePluginVersions(left, right) {
|
|
|
36
36
|
return 0;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
// packages/plugins/src/categories.ts
|
|
40
|
+
var PLUGIN_CATEGORIES = [
|
|
41
|
+
{ id: "sync", label: "\u540C\u6B65" },
|
|
42
|
+
{ id: "upload", label: "\u8D44\u6E90\u4E0A\u4F20" },
|
|
43
|
+
{ id: "editor", label: "\u7F16\u8F91\u5668" },
|
|
44
|
+
{ id: "whiteboard", label: "\u767D\u677F" },
|
|
45
|
+
{ id: "blog-template", label: "\u535A\u5BA2\u6A21\u677F" },
|
|
46
|
+
{ id: "blog-publish", label: "\u535A\u5BA2\u53D1\u5E03" },
|
|
47
|
+
{ id: "ai", label: "AI" },
|
|
48
|
+
{ id: "voice", label: "\u8BED\u97F3" },
|
|
49
|
+
{ id: "page", label: "\u9875\u9762" }
|
|
50
|
+
];
|
|
51
|
+
var PLUGIN_KEYWORD_LIMIT = 5;
|
|
52
|
+
var PLUGIN_KEYWORD_MAX_LENGTH = 20;
|
|
53
|
+
var categoryIds = new Set(PLUGIN_CATEGORIES.map((item) => item.id));
|
|
54
|
+
var isPluginCategory = (value) => typeof value === "string" && categoryIds.has(value);
|
|
55
|
+
function readCatalogFields(item, strict) {
|
|
56
|
+
const fail = (message) => {
|
|
57
|
+
if (strict) throw new Error(message);
|
|
58
|
+
};
|
|
59
|
+
const list = (value, name) => {
|
|
60
|
+
if (value === void 0) return [];
|
|
61
|
+
if (Array.isArray(value)) return value;
|
|
62
|
+
fail(`${name} \u5FC5\u987B\u662F\u6570\u7EC4`);
|
|
63
|
+
return [];
|
|
64
|
+
};
|
|
65
|
+
const categories = [];
|
|
66
|
+
for (const value of list(item.categories, "categories")) {
|
|
67
|
+
if (!isPluginCategory(value)) fail(`\u672A\u77E5\u63D2\u4EF6\u5206\u7C7B\uFF1A${String(value)}`);
|
|
68
|
+
else if (!categories.includes(value)) categories.push(value);
|
|
69
|
+
}
|
|
70
|
+
const keywords = [];
|
|
71
|
+
for (const value of list(item.keywords, "keywords")) {
|
|
72
|
+
const keyword = typeof value === "string" ? value.trim() : "";
|
|
73
|
+
if (!keyword || keyword.length > PLUGIN_KEYWORD_MAX_LENGTH)
|
|
74
|
+
fail(`\u5173\u952E\u8BCD\u5FC5\u987B\u4E3A 1 \u81F3 ${PLUGIN_KEYWORD_MAX_LENGTH} \u5B57`);
|
|
75
|
+
else if (!keywords.includes(keyword)) keywords.push(keyword);
|
|
76
|
+
}
|
|
77
|
+
if (keywords.length > PLUGIN_KEYWORD_LIMIT) {
|
|
78
|
+
fail(`\u5173\u952E\u8BCD\u6700\u591A ${PLUGIN_KEYWORD_LIMIT} \u4E2A`);
|
|
79
|
+
keywords.length = PLUGIN_KEYWORD_LIMIT;
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
...categories.length ? { categories: Object.freeze(categories) } : {},
|
|
83
|
+
...keywords.length ? { keywords: Object.freeze(keywords) } : {}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
39
87
|
// packages/plugins/src/package.ts
|
|
40
88
|
var PLUGIN_SHARED_MODULES = [
|
|
41
89
|
"@tau/plugins",
|
|
@@ -48,7 +96,7 @@ var PLUGIN_SHARED_MODULES = [
|
|
|
48
96
|
"react/jsx-runtime",
|
|
49
97
|
"react/jsx-dev-runtime"
|
|
50
98
|
];
|
|
51
|
-
function validatePluginManifest(value) {
|
|
99
|
+
function validatePluginManifest(value, options = {}) {
|
|
52
100
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
53
101
|
throw new Error("\u63D2\u4EF6\u6E05\u5355\u5FC5\u987B\u662F\u5BF9\u8C61");
|
|
54
102
|
const item = value;
|
|
@@ -73,6 +121,7 @@ function validatePluginManifest(value) {
|
|
|
73
121
|
if (item.editor !== void 0 && item.editor !== true) throw new Error("editor \u53EA\u80FD\u58F0\u660E\u4E3A true");
|
|
74
122
|
if (item.whiteboard !== void 0 && item.whiteboard !== true)
|
|
75
123
|
throw new Error("whiteboard \u53EA\u80FD\u58F0\u660E\u4E3A true");
|
|
124
|
+
const catalog = readCatalogFields(item, options.strict === true);
|
|
76
125
|
const integrity = {};
|
|
77
126
|
for (const name of [main, ...styles ? [styles] : []]) {
|
|
78
127
|
const hash = item.integrity && typeof item.integrity === "object" ? item.integrity[name] : void 0;
|
|
@@ -92,21 +141,12 @@ function validatePluginManifest(value) {
|
|
|
92
141
|
main,
|
|
93
142
|
...item.editor === true ? { editor: true } : {},
|
|
94
143
|
...item.whiteboard === true ? { whiteboard: true } : {},
|
|
144
|
+
...catalog,
|
|
95
145
|
integrity: Object.freeze(integrity),
|
|
96
146
|
...styles === void 0 ? {} : { styles }
|
|
97
147
|
});
|
|
98
148
|
}
|
|
99
149
|
|
|
100
|
-
// packages/plugins/src/sidebar.ts
|
|
101
|
-
var SIDEBAR_GROUPS = Object.freeze({
|
|
102
|
-
daily: "daily",
|
|
103
|
-
knowledge: "knowledge",
|
|
104
|
-
learning: "learning",
|
|
105
|
-
ai: "ai",
|
|
106
|
-
sharing: "sharing",
|
|
107
|
-
plugins: "plugins"
|
|
108
|
-
});
|
|
109
|
-
|
|
110
150
|
// packages/plugins/src/rendering.ts
|
|
111
151
|
var PLUGIN_RENDER_VERSION = 1;
|
|
112
152
|
function evaluatePluginPackage(artifact, modules) {
|
|
@@ -197,7 +237,6 @@ function assertRenderTypes(definition, expected) {
|
|
|
197
237
|
}
|
|
198
238
|
|
|
199
239
|
// packages/plugins/src/publish-artifacts.ts
|
|
200
|
-
var SIDEBAR_GROUPS2 = SIDEBAR_GROUPS;
|
|
201
240
|
var PLUGIN_RENDER_VERSION2 = PLUGIN_RENDER_VERSION;
|
|
202
241
|
function evaluatePluginPackage2(artifact, modules) {
|
|
203
242
|
return evaluatePluginPackage(artifact, modules);
|
|
@@ -208,7 +247,6 @@ function assertRenderTypes2(definition, types) {
|
|
|
208
247
|
export {
|
|
209
248
|
PLUGIN_RENDER_VERSION2 as PLUGIN_RENDER_VERSION,
|
|
210
249
|
PLUGIN_SHARED_MODULES,
|
|
211
|
-
SIDEBAR_GROUPS2 as SIDEBAR_GROUPS,
|
|
212
250
|
assertRenderTypes2 as assertRenderTypes,
|
|
213
251
|
evaluatePluginPackage2 as evaluatePluginPackage,
|
|
214
252
|
validatePluginManifest
|
package/publish-client.js
CHANGED
|
@@ -45,6 +45,7 @@ function createClient({ manifestUrl }) {
|
|
|
45
45
|
const cache = /* @__PURE__ */ new Map();
|
|
46
46
|
let manifestPromise;
|
|
47
47
|
let searchPromise;
|
|
48
|
+
let engine;
|
|
48
49
|
function json(url, expected) {
|
|
49
50
|
assert(/^[a-f0-9]{64}$/.test(expected), "INVALID_DATA", "\u6458\u8981\u683C\u5F0F\u9519\u8BEF");
|
|
50
51
|
const filenameHash = new URL(url).pathname.match(/\.([a-f0-9]{64})\.json$/)?.[1];
|
|
@@ -207,7 +208,11 @@ function createClient({ manifestUrl }) {
|
|
|
207
208
|
"IDENTITY_MISMATCH",
|
|
208
209
|
"\u5185\u5BB9\u8EAB\u4EFD\u4E0D\u5339\u914D"
|
|
209
210
|
);
|
|
210
|
-
return {
|
|
211
|
+
return {
|
|
212
|
+
data: value,
|
|
213
|
+
dataUrl,
|
|
214
|
+
...source.settings ? { settings: source.settings } : {}
|
|
215
|
+
};
|
|
211
216
|
},
|
|
212
217
|
async search(query, { limit = 12, signal } = {}) {
|
|
213
218
|
cancelled(signal);
|
|
@@ -219,13 +224,17 @@ function createClient({ manifestUrl }) {
|
|
|
219
224
|
signal
|
|
220
225
|
);
|
|
221
226
|
cancelled(signal);
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
227
|
+
if (engine?.documents !== documents)
|
|
228
|
+
engine = {
|
|
229
|
+
documents,
|
|
230
|
+
fuse: new Fuse(documents, {
|
|
231
|
+
keys: ["title", "summary", "content", "tags"],
|
|
232
|
+
includeMatches: true,
|
|
233
|
+
threshold: 0.3,
|
|
234
|
+
ignoreLocation: true
|
|
235
|
+
})
|
|
236
|
+
};
|
|
237
|
+
return engine.fuse.search(normalized, { limit }).map(({ item, matches }) => {
|
|
229
238
|
const { content: _content, ...hit } = item;
|
|
230
239
|
const ranges = (matches ?? []).flatMap((match) => {
|
|
231
240
|
if (!match.value || !["title", "summary", "content", "tags"].includes(match.key ?? ""))
|
|
@@ -41,7 +41,7 @@ export interface MarkdownCodec {
|
|
|
41
41
|
*/
|
|
42
42
|
fence?: {
|
|
43
43
|
language: string | readonly string[];
|
|
44
|
-
/** 源码落进元素的字段名,默认 "content"
|
|
44
|
+
/** 源码落进元素的字段名,默认 "content",由领域声明覆盖。 */
|
|
45
45
|
property?: string;
|
|
46
46
|
};
|
|
47
47
|
/**
|
|
@@ -69,7 +69,7 @@ export interface MarkdownCodec {
|
|
|
69
69
|
parent: Descendant | null;
|
|
70
70
|
previousSibling: Descendant | null;
|
|
71
71
|
nextSibling: Descendant | null;
|
|
72
|
-
/**
|
|
72
|
+
/** 按领域声明把块级子元素降级为行内形态,无声明则原样返回。 */
|
|
73
73
|
demote: (child: Descendant) => Descendant;
|
|
74
74
|
}) => Descendant[] | null;
|
|
75
75
|
/** 按目标类型贡献规范化,先于该类型自身 normalize;按 codec 顺序首个非 null 替换生效。 */
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** 插件中心分类按扩展点划分,顺序即展示顺序;新增面向用户的扩展点时同步扩充。 */
|
|
2
|
+
export declare const PLUGIN_CATEGORIES: readonly [{
|
|
3
|
+
readonly id: "sync";
|
|
4
|
+
readonly label: "同步";
|
|
5
|
+
}, {
|
|
6
|
+
readonly id: "upload";
|
|
7
|
+
readonly label: "资源上传";
|
|
8
|
+
}, {
|
|
9
|
+
readonly id: "editor";
|
|
10
|
+
readonly label: "编辑器";
|
|
11
|
+
}, {
|
|
12
|
+
readonly id: "whiteboard";
|
|
13
|
+
readonly label: "白板";
|
|
14
|
+
}, {
|
|
15
|
+
readonly id: "blog-template";
|
|
16
|
+
readonly label: "博客模板";
|
|
17
|
+
}, {
|
|
18
|
+
readonly id: "blog-publish";
|
|
19
|
+
readonly label: "博客发布";
|
|
20
|
+
}, {
|
|
21
|
+
readonly id: "ai";
|
|
22
|
+
readonly label: "AI";
|
|
23
|
+
}, {
|
|
24
|
+
readonly id: "voice";
|
|
25
|
+
readonly label: "语音";
|
|
26
|
+
}, {
|
|
27
|
+
readonly id: "page";
|
|
28
|
+
readonly label: "页面";
|
|
29
|
+
}];
|
|
30
|
+
export type PluginCategory = (typeof PLUGIN_CATEGORIES)[number]["id"];
|
|
31
|
+
export declare const PLUGIN_KEYWORD_LIMIT = 5;
|
|
32
|
+
export declare const PLUGIN_KEYWORD_MAX_LENGTH = 20;
|
|
33
|
+
export declare const isPluginCategory: (value: unknown) => value is PluginCategory;
|
|
34
|
+
export interface PluginCatalogFields {
|
|
35
|
+
categories?: readonly PluginCategory[];
|
|
36
|
+
keywords?: readonly string[];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 读取 manifest 中的分类与关键词。
|
|
40
|
+
* strict 用于门户上传,非法值直接报错;客户端用宽松模式静默丢弃,
|
|
41
|
+
* 使老客户端不会因词表新增或上限调整而拒收插件。
|
|
42
|
+
*/
|
|
43
|
+
export declare function readCatalogFields(item: Record<string, unknown>, strict: boolean): PluginCatalogFields;
|
|
@@ -11,6 +11,7 @@ import type { PluginSync } from "./sync";
|
|
|
11
11
|
import type { PluginUpload } from "./upload";
|
|
12
12
|
import type { PluginTTS } from "./tts";
|
|
13
13
|
import type { PluginImages } from "./images";
|
|
14
|
+
import type { PluginPublish } from "./publish";
|
|
14
15
|
import type { PluginSidebar } from "./sidebar";
|
|
15
16
|
import type { PluginExcerpts, PluginMaterials } from "./excerpts";
|
|
16
17
|
import type { PluginMCP } from "./mcp";
|
|
@@ -46,6 +47,7 @@ export interface PluginContext {
|
|
|
46
47
|
readonly upload: PluginUpload;
|
|
47
48
|
readonly tts: PluginTTS;
|
|
48
49
|
readonly images: PluginImages;
|
|
50
|
+
readonly publish: PluginPublish;
|
|
49
51
|
readonly sidebar: PluginSidebar;
|
|
50
52
|
readonly excerpts: PluginExcerpts;
|
|
51
53
|
readonly materials: PluginMaterials;
|
|
@@ -2,6 +2,7 @@ import type { PluginDefinition } from "./contract";
|
|
|
2
2
|
export type * from "./upload";
|
|
3
3
|
export type * from "./tts";
|
|
4
4
|
export type * from "./images";
|
|
5
|
+
export type * from "./publish";
|
|
5
6
|
export type { JsonValue, Disposable, PluginStorage, PluginCommand, PluginContext, PluginInstance, PluginDefinition, } from "./contract";
|
|
6
7
|
export type { PluginSettings, PluginSettingsDefinition, PluginSettingsProps, PluginSettingsHandle, } from "./settings";
|
|
7
8
|
export * from "./editor";
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { assertLocalId } from "./ids";
|
|
2
|
+
import { type PluginCategory } from "./categories";
|
|
2
3
|
export { comparePluginVersions } from "./version";
|
|
4
|
+
export { PLUGIN_CATEGORIES, PLUGIN_KEYWORD_LIMIT, PLUGIN_KEYWORD_MAX_LENGTH, isPluginCategory, type PluginCategory, } from "./categories";
|
|
3
5
|
export declare const PLUGIN_SHARED_MODULES: readonly ["@tau/plugins", "@tau/plugins/whiteboard", "@tau/plugins/sync", "@tau/ui", "react", "react-dom", "react-dom/client", "react/jsx-runtime", "react/jsx-dev-runtime"];
|
|
4
6
|
export interface PluginManifest {
|
|
5
7
|
formatVersion: 1;
|
|
@@ -16,6 +18,10 @@ export interface PluginManifest {
|
|
|
16
18
|
/** 静态编辑器贡献在窗口启动时装配,启停需重启。 */
|
|
17
19
|
editor?: true;
|
|
18
20
|
whiteboard?: true;
|
|
21
|
+
/** 插件中心分类,作者自填,不与实际注册对账。 */
|
|
22
|
+
categories?: readonly PluginCategory[];
|
|
23
|
+
/** 仅参与搜索匹配。 */
|
|
24
|
+
keywords?: readonly string[];
|
|
19
25
|
integrity: Record<string, string>;
|
|
20
26
|
}
|
|
21
27
|
/** 宿主内部传输的已读取产物,不属于作者运行时 API。 */
|
|
@@ -24,6 +30,9 @@ export interface DevelopmentPluginPackage {
|
|
|
24
30
|
code: string;
|
|
25
31
|
css?: string;
|
|
26
32
|
}
|
|
27
|
-
|
|
33
|
+
/** strict 供门户上传使用:分类或关键词不合规时报错,客户端默认静默丢弃。 */
|
|
34
|
+
export declare function validatePluginManifest(value: unknown, options?: {
|
|
35
|
+
strict?: boolean;
|
|
36
|
+
}): PluginManifest;
|
|
28
37
|
/** 静态内容贡献在启动时固定,启停与更新需要重启。 */
|
|
29
38
|
export declare const isContentPlugin: (manifest: Pick<PluginManifest, "editor" | "whiteboard">) => boolean;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export { validatePluginManifest, PLUGIN_SHARED_MODULES } from "./package";
|
|
2
|
-
export declare const SIDEBAR_GROUPS: Readonly<Record<string, string>>;
|
|
3
2
|
import type { DevelopmentPluginPackage } from "./package";
|
|
4
3
|
export declare const PLUGIN_RENDER_VERSION: 1;
|
|
5
4
|
export type { PublishedPluginRender } from "./render-artifact";
|
|
@@ -27,6 +27,15 @@ export interface SiteData {
|
|
|
27
27
|
mimeType: string;
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
|
+
export interface PublishSettings {
|
|
31
|
+
appearance?: {
|
|
32
|
+
image?: {
|
|
33
|
+
borderRadius?: string;
|
|
34
|
+
shadow?: string | boolean;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** 站点级数据只在清单中出现一次,各内容文件不重复携带。 */
|
|
30
39
|
export interface DataManifest {
|
|
31
40
|
version: 1;
|
|
32
41
|
site: SiteData;
|
|
@@ -35,6 +44,11 @@ export interface DataManifest {
|
|
|
35
44
|
integrity: string;
|
|
36
45
|
};
|
|
37
46
|
items: ManifestItem[];
|
|
47
|
+
settings?: PublishSettings;
|
|
48
|
+
collections?: Collection[];
|
|
49
|
+
cardTagTree?: CardTagTree[];
|
|
50
|
+
/** 键为项目 id。 */
|
|
51
|
+
projectTrees?: Record<string, TreeItem[]>;
|
|
38
52
|
}
|
|
39
53
|
export interface ContentNode {
|
|
40
54
|
[field: string]: unknown;
|
|
@@ -205,20 +219,10 @@ export interface CardTagTree {
|
|
|
205
219
|
export interface PublishedCommon {
|
|
206
220
|
version: 1;
|
|
207
221
|
key: string;
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
cardTagTree?: CardTagTree[];
|
|
211
|
-
contentLinkTargets?: Record<string, ContentRef>;
|
|
222
|
+
/** 本文正文里已发布的站内链接目标,键与 QuestMaterial.citeKey 同一口径;未发布的链接不在其中。 */
|
|
223
|
+
links?: Record<string, ContentRef>;
|
|
212
224
|
pluginRenderers?: import("./render-artifact").PublishedPluginRender[];
|
|
213
225
|
contentStyles?: ResourceRef[];
|
|
214
|
-
settings?: {
|
|
215
|
-
appearance?: {
|
|
216
|
-
image?: {
|
|
217
|
-
borderRadius?: string;
|
|
218
|
-
shadow?: string | boolean;
|
|
219
|
-
};
|
|
220
|
-
};
|
|
221
|
-
};
|
|
222
226
|
}
|
|
223
227
|
export type PublishedData = {
|
|
224
228
|
[K in DataKind]: PublishedCommon & {
|
|
@@ -239,6 +243,8 @@ export type PublishedData = {
|
|
|
239
243
|
export interface DataResult {
|
|
240
244
|
data: PublishedData;
|
|
241
245
|
dataUrl: string;
|
|
246
|
+
/** 清单中的发布设置,由客户端随正文带回。 */
|
|
247
|
+
settings?: PublishSettings;
|
|
242
248
|
}
|
|
243
249
|
export interface SearchDocument extends ContentRef {
|
|
244
250
|
type: "card" | "article" | "doc" | "quest";
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { Disposable, JsonValue } from "./contract";
|
|
2
|
+
export interface PublishTarget {
|
|
3
|
+
/** 物理目标的稳定身份,不包含凭据、插件或模板版本。 */
|
|
4
|
+
identity: string;
|
|
5
|
+
basePath: string;
|
|
6
|
+
publicUrl?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface PublicationSnapshot {
|
|
9
|
+
/** 宿主为本轮文件计划颁发的临时句柄,不能读取任意本地文件。 */
|
|
10
|
+
id: string;
|
|
11
|
+
files: readonly {
|
|
12
|
+
path: string;
|
|
13
|
+
hash: string;
|
|
14
|
+
}[];
|
|
15
|
+
deletions: readonly string[];
|
|
16
|
+
}
|
|
17
|
+
interface PublishInstanceBase {
|
|
18
|
+
describe(signal: AbortSignal): Promise<PublishTarget>;
|
|
19
|
+
checkConnection(signal: AbortSignal): Promise<boolean>;
|
|
20
|
+
dispose?(): void | Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
export interface FilePublishInstance extends PublishInstanceBase {
|
|
23
|
+
mode: "files";
|
|
24
|
+
concurrency?: number;
|
|
25
|
+
uploadFile(path: string, content: Uint8Array, signal: AbortSignal): Promise<void>;
|
|
26
|
+
deleteFile(path: string, signal: AbortSignal): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
export interface SnapshotPublishInstance extends PublishInstanceBase {
|
|
29
|
+
mode: "snapshot";
|
|
30
|
+
publish(snapshot: PublicationSnapshot, signal: AbortSignal): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
export type PublishInstance = FilePublishInstance | SnapshotPublishInstance;
|
|
33
|
+
export interface PublishProviderDefinition {
|
|
34
|
+
id: string;
|
|
35
|
+
name: string;
|
|
36
|
+
create(config: JsonValue): PublishInstance;
|
|
37
|
+
}
|
|
38
|
+
export interface PluginPublish {
|
|
39
|
+
register(definition: PublishProviderDefinition): Disposable;
|
|
40
|
+
}
|
|
41
|
+
export interface GitSnapshotRequest {
|
|
42
|
+
publicationId: string;
|
|
43
|
+
remote: string;
|
|
44
|
+
branch: string;
|
|
45
|
+
author: {
|
|
46
|
+
name: string;
|
|
47
|
+
email: string;
|
|
48
|
+
};
|
|
49
|
+
authentication: {
|
|
50
|
+
username: string;
|
|
51
|
+
password: string;
|
|
52
|
+
};
|
|
53
|
+
cacheId?: string;
|
|
54
|
+
}
|
|
55
|
+
export interface GitCacheDirectory {
|
|
56
|
+
id: string;
|
|
57
|
+
label: string;
|
|
58
|
+
}
|
|
59
|
+
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Descendant } from "slate";
|
|
2
2
|
import type { MarkdownCodec } from "../../markdown/src/index";
|
|
3
|
+
import type { GitSnapshotRequest, GitCacheDirectory } from "./publish";
|
|
3
4
|
export type { Descendant } from "slate";
|
|
4
5
|
export interface ServiceOptions {
|
|
5
6
|
signal?: AbortSignal;
|
|
@@ -39,6 +40,10 @@ export interface MediaInfo {
|
|
|
39
40
|
}[];
|
|
40
41
|
}
|
|
41
42
|
export interface PluginServices {
|
|
43
|
+
git: {
|
|
44
|
+
publishSnapshot(input: GitSnapshotRequest, options?: ServiceOptions): Promise<void>;
|
|
45
|
+
selectCacheDirectory(input: Record<string, never>, options?: ServiceOptions): Promise<GitCacheDirectory | null>;
|
|
46
|
+
};
|
|
42
47
|
tailwind: {
|
|
43
48
|
compile(input: {
|
|
44
49
|
source: string;
|
|
@@ -3,6 +3,10 @@ import type { Disposable, PluginContext } from "./index";
|
|
|
3
3
|
import type { JsonValue } from "./json";
|
|
4
4
|
export interface PluginSettingsProps {
|
|
5
5
|
readonly context: PluginContext;
|
|
6
|
+
readonly publishConfig?: {
|
|
7
|
+
readonly value: JsonValue;
|
|
8
|
+
save(value: JsonValue): Promise<void>;
|
|
9
|
+
};
|
|
6
10
|
readonly imageConfig?: {
|
|
7
11
|
readonly instanceId: string;
|
|
8
12
|
readonly value: JsonValue;
|
|
@@ -35,7 +39,7 @@ export interface PluginSettingsDefinition {
|
|
|
35
39
|
title: string;
|
|
36
40
|
description?: string;
|
|
37
41
|
/** 提供方配置表单挂载到对应宿主设置;普通插件设置省略。 */
|
|
38
|
-
target?: "sync" | "upload" | "tts" | "images";
|
|
42
|
+
target?: "sync" | "upload" | "tts" | "images" | "publish";
|
|
39
43
|
component: ComponentType<PluginSettingsProps>;
|
|
40
44
|
}
|
|
41
45
|
export interface PluginSettingsHandle extends Disposable {
|