@clipbus/plugin-sdk 0.9.7 → 0.9.8

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/API.md CHANGED
@@ -505,7 +505,6 @@ export interface PluginRegistry {
505
505
 
506
506
  - `attachmentKey`: `string`
507
507
  - `payloadJson`: `string`
508
- - `syncScope`: `string`
509
508
  - `createdAtMs?`: `int`
510
509
  - `updatedAtMs?`: `int`
511
510
 
@@ -663,7 +662,6 @@ Discriminated union on `kind`: { kind: `'text'`, text: `string` } | { kind: `'im
663
662
  - `attachmentKey`: `string`
664
663
  - `payloadJson`: `string`
665
664
  - `searchProjection?`: [`PluginDetectorSearchProjection`](#plugindetectorsearchprojection)
666
- - `attachmentSyncScope?`: `string`
667
665
  - `createdAtMs?`: `int`
668
666
  - `updatedAtMs?`: `int`
669
667
 
package/README.md CHANGED
@@ -55,6 +55,9 @@ module.exports = definePlugin({
55
55
  ### `actionResult.text(value, options?)`
56
56
 
57
57
  Returns a locked `{ result: { resultKind: 'text', text }, userMessage }` shape.
58
+ When returned by an auto-run Action, or submitted by a Draft Action through
59
+ `clipbus.action.complete(...)`, a successful value can be explicitly advanced
60
+ by the user as the next Action input.
58
61
 
59
62
  | Param | Type | Description |
60
63
  |---|---|---|
@@ -80,7 +83,8 @@ Returns a locked `{ result: { resultKind: 'image', imageTempPath, imageFormatHin
80
83
  Returns a safe path-reference result. Entries must either reuse a current input
81
84
  entry (`{source: 'input', inputIndex}`) or reference a path allocated by
82
85
  `ctx.host.action.allocateOutputFilePath()` (`{source: 'allocated_file', path, displayName}`).
83
- Arbitrary filesystem paths are rejected by the host.
86
+ Arbitrary filesystem paths are rejected by the host. Like text and image,
87
+ `path_reference` is a value result that the user can explicitly advance.
84
88
 
85
89
  ---
86
90
 
@@ -89,9 +93,13 @@ Arbitrary filesystem paths are rejected by the host.
89
93
  Action handlers receive `{ sourceItem, content, attachments }`.
90
94
  `sourceItem` remains the original clipboard item identity used by item mutation
91
95
  verbs; `content` is the current cascade value and may change kind after every
92
- auto Action. The Action `attachments` field is reserved for wire stability and
93
- is currently always `[]` in SDK 0.9.0. Draft Actions can display and copy their
94
- result, but do not advance to another Action.
96
+ Action step. The Action `attachments` field is reserved for wire stability and
97
+ is currently always `[]` in SDK 0.9.0. Successful Draft results with `text`,
98
+ `image`, or `path_reference` are shown for confirmation and can be explicitly
99
+ advanced by the user; `none` remains non-continuable. Completion never
100
+ auto-advances or implicitly selects another Action. This is host behavior for
101
+ the existing schema v4 result shapes, so plugins do not need a manifest or
102
+ generated-code update.
95
103
 
96
104
  ### `messageHandlers` — handle UI → Runtime RPC calls
97
105
 
@@ -320,7 +328,7 @@ Namespace of verbs and sub-topics scoped to the action WebView. `clipbus.action`
320
328
  | Member | Shape | Description |
321
329
  |---|---|---|
322
330
  | `clipbus.action.setButtons({buttons})` | `Verb` | Replace native action button list (whole-list replace; first call overrides `resolveSession` seed). Strict wire shape — pass `{buttons: [...]}`. |
323
- | `clipbus.action.complete({result, userMessage?})` | `Verb` | Submit draft action result to host; triggers phase transition to `.success` |
331
+ | `clipbus.action.complete({result, userMessage?})` | `Verb` | Submit a Draft result and enter the host success presentation; a text/image/path_reference value may then be explicitly advanced by the user, while none cannot advance |
324
332
  | `clipbus.action.onHostInvoke(fn)` | `Stream` | Subscribe to host-dispatched button clicks; callback receives `{ buttonID }` |
325
333
 
326
334
  #### `clipbus.action.input` — Topic\<PluginActionUIInput\>
package/SPECIFICATION.md CHANGED
@@ -106,8 +106,12 @@ Both Action inputs use `{ sourceItem, content, attachments }`. `sourceItem`
106
106
  preserves the original clipboard identity for item mutations; `content` is the
107
107
  current cascade value and can change kind between Actions. The Action
108
108
  `attachments` field is reserved for wire stability and is currently always `[]`
109
- in SDK 0.9.0. Draft results are terminal and are never forwarded to another
110
- Action.
109
+ in SDK 0.9.0. A Draft `complete(...)` result with `text`, `image`, or
110
+ `path_reference` enters the host's success presentation and can be explicitly
111
+ advanced by the user as the next Action input; `none` cannot advance. Completion
112
+ does not auto-advance or implicitly select another Action. These semantics use
113
+ the existing schema v4 result shapes and require no protocol or generated-code
114
+ change.
111
115
 
112
116
  ### Strongly typed drafts
113
117
 
@@ -173,7 +177,7 @@ Capabilities are split by catalog `surface.side`: runtime capabilities are expos
173
177
  | Auto-fit height | `window.autoFit` | — | `clipbus.window.autoFit()` | Verb |
174
178
  | Draft | draftHostEvent | input param | `clipbus.action.draft.current()` | OptionalTopic; UI is read-only — no `update()` verb |
175
179
  | Replace action button list | `action.setButtons` | — | `clipbus.action.setButtons({buttons})` | Verb; buttons have `isEnabled?: boolean` field |
176
- | Submit draft result | `action.complete` | — | `clipbus.action.complete({result, userMessage?})` | Verb; draft lifecycle only |
180
+ | Submit draft result | `action.complete` | — | `clipbus.action.complete({result, userMessage?})` | Verb; draft lifecycle only; value results can be explicitly advanced by the user, `none` cannot |
177
181
  | Allocate image temp path | `action.allocateImageTempPath` | `ctx.host.action.allocateImageTempPath({formatHint?})` | — | Verb; runtime-only via user RPC bridge; removed from UI in plugin-api-shrink |
178
182
  | Materialize current Action image | `action.materializeInputImagePath` | `ctx.host.action.materializeInputImagePath()` | — | Reads current cascade image, not the original item |
179
183
  | Allocate Action output file | `action.allocateOutputFilePath` | `ctx.host.action.allocateOutputFilePath({suggestedName?})` | — | Path may only be returned as an `allocated_file` result entry |
@@ -1,7 +1,6 @@
1
1
  export interface PluginAttachmentMutationEntry {
2
2
  attachmentKey: string;
3
3
  payloadJson: string;
4
- syncScope: string;
5
4
  createdAtMs?: number;
6
5
  updatedAtMs?: number;
7
6
  }
@@ -201,7 +200,6 @@ export interface PluginDetectorArtifact {
201
200
  attachmentKey: string;
202
201
  payloadJson: string;
203
202
  searchProjection?: PluginDetectorSearchProjection;
204
- attachmentSyncScope?: string;
205
203
  createdAtMs?: number;
206
204
  updatedAtMs?: number;
207
205
  }
@@ -99,7 +99,7 @@ function isValidPluginID(id) {
99
99
  }
100
100
  function validateSchemaVersion(raw, issues) {
101
101
  const version = typeof raw["schemaVersion"] === "number" ? raw["schemaVersion"] : 0;
102
- if (version !== 3) {
102
+ if (version !== 4) {
103
103
  issues.push(issue("unsupportedSchemaVersion", `Unsupported plugin schema version: ${version}`));
104
104
  }
105
105
  }
@@ -118,13 +118,13 @@ var LEGACY_ACTION_FIELDS = [
118
118
  function validateLegacyFields(raw, issues) {
119
119
  const plugin = asRecord(raw["plugin"]);
120
120
  if (plugin && "displayName" in plugin) {
121
- issues.push(issue("legacyFieldNotSupported", `Legacy manifest field is not supported in v3: plugin.displayName`));
121
+ issues.push(issue("legacyFieldNotSupported", `Legacy manifest field is not supported in v4: plugin.displayName`));
122
122
  }
123
123
  const renderers = asRecordArray(raw["attachmentRenderers"]);
124
124
  for (let i = 0; i < renderers.length; i++) {
125
125
  for (const field of LEGACY_RENDERER_FIELDS) {
126
126
  if (field in renderers[i]) {
127
- issues.push(issue("legacyFieldNotSupported", `Legacy manifest field is not supported in v3: attachmentRenderers[${i}].${field}`));
127
+ issues.push(issue("legacyFieldNotSupported", `Legacy manifest field is not supported in v4: attachmentRenderers[${i}].${field}`));
128
128
  }
129
129
  }
130
130
  }
@@ -132,7 +132,7 @@ function validateLegacyFields(raw, issues) {
132
132
  for (let i = 0; i < detectors.length; i++) {
133
133
  for (const field of LEGACY_DETECTOR_FIELDS) {
134
134
  if (field in detectors[i]) {
135
- issues.push(issue("legacyFieldNotSupported", `Legacy manifest field is not supported in v3: detectors[${i}].${field}`));
135
+ issues.push(issue("legacyFieldNotSupported", `Legacy manifest field is not supported in v4: detectors[${i}].${field}`));
136
136
  }
137
137
  }
138
138
  }
@@ -140,7 +140,7 @@ function validateLegacyFields(raw, issues) {
140
140
  for (let i = 0; i < actions.length; i++) {
141
141
  for (const field of LEGACY_ACTION_FIELDS) {
142
142
  if (field in actions[i]) {
143
- issues.push(issue("legacyFieldNotSupported", `Legacy manifest field is not supported in v3: actions[${i}].${field}`));
143
+ issues.push(issue("legacyFieldNotSupported", `Legacy manifest field is not supported in v4: actions[${i}].${field}`));
144
144
  }
145
145
  }
146
146
  }
@@ -64,7 +64,7 @@ function isValidPluginID(id) {
64
64
  }
65
65
  function validateSchemaVersion(raw, issues) {
66
66
  const version = typeof raw["schemaVersion"] === "number" ? raw["schemaVersion"] : 0;
67
- if (version !== 3) {
67
+ if (version !== 4) {
68
68
  issues.push(issue("unsupportedSchemaVersion", `Unsupported plugin schema version: ${version}`));
69
69
  }
70
70
  }
@@ -83,13 +83,13 @@ var LEGACY_ACTION_FIELDS = [
83
83
  function validateLegacyFields(raw, issues) {
84
84
  const plugin = asRecord(raw["plugin"]);
85
85
  if (plugin && "displayName" in plugin) {
86
- issues.push(issue("legacyFieldNotSupported", `Legacy manifest field is not supported in v3: plugin.displayName`));
86
+ issues.push(issue("legacyFieldNotSupported", `Legacy manifest field is not supported in v4: plugin.displayName`));
87
87
  }
88
88
  const renderers = asRecordArray(raw["attachmentRenderers"]);
89
89
  for (let i = 0; i < renderers.length; i++) {
90
90
  for (const field of LEGACY_RENDERER_FIELDS) {
91
91
  if (field in renderers[i]) {
92
- issues.push(issue("legacyFieldNotSupported", `Legacy manifest field is not supported in v3: attachmentRenderers[${i}].${field}`));
92
+ issues.push(issue("legacyFieldNotSupported", `Legacy manifest field is not supported in v4: attachmentRenderers[${i}].${field}`));
93
93
  }
94
94
  }
95
95
  }
@@ -97,7 +97,7 @@ function validateLegacyFields(raw, issues) {
97
97
  for (let i = 0; i < detectors.length; i++) {
98
98
  for (const field of LEGACY_DETECTOR_FIELDS) {
99
99
  if (field in detectors[i]) {
100
- issues.push(issue("legacyFieldNotSupported", `Legacy manifest field is not supported in v3: detectors[${i}].${field}`));
100
+ issues.push(issue("legacyFieldNotSupported", `Legacy manifest field is not supported in v4: detectors[${i}].${field}`));
101
101
  }
102
102
  }
103
103
  }
@@ -105,7 +105,7 @@ function validateLegacyFields(raw, issues) {
105
105
  for (let i = 0; i < actions.length; i++) {
106
106
  for (const field of LEGACY_ACTION_FIELDS) {
107
107
  if (field in actions[i]) {
108
- issues.push(issue("legacyFieldNotSupported", `Legacy manifest field is not supported in v3: actions[${i}].${field}`));
108
+ issues.push(issue("legacyFieldNotSupported", `Legacy manifest field is not supported in v4: actions[${i}].${field}`));
109
109
  }
110
110
  }
111
111
  }
package/docs/authoring.md CHANGED
@@ -28,7 +28,6 @@ const detector: PluginDetectorHandler = {
28
28
  attachmentType: 'plugin.example.sample.card', // 必须已在 manifest 声明
29
29
  attachmentKey: `card-${input.item.id}`, // 稳定 key,不允许空字符串
30
30
  payloadJson: JSON.stringify({ text: input.content.text }),
31
- attachmentSyncScope: 'syncable', // 'syncable' | 'local_only'
32
31
  searchProjection: { // 可选
33
32
  scope: 'sample',
34
33
  searchText: input.content.text.slice(0, 80),
@@ -45,6 +44,7 @@ const detector: PluginDetectorHandler = {
45
44
  - 未命中时返回 `[]`,不要返回半成品 artifact
46
45
  - Detector 内不应执行宿主 mutation(标签写入等);那是 action 的职责
47
46
  - `attachmentType` 必须出自 manifest 的 `detectors[].attachmentTypes`(manifest 结构见 [manifest.md](./manifest.md))
47
+ - Attachment 是当前设备的 detector 派生数据。schema v4 不提供同步范围字段,宿主统一以 `local_only` 持久化;若内容需要在尚未选中 item 时参与跨设备搜索,请显式提供 `searchProjection`
48
48
 
49
49
  ---
50
50
 
@@ -262,12 +262,15 @@ async function handleApply() {
262
262
  }
263
263
  ```
264
264
 
265
- Draft 完成后宿主展示结果供复制或关闭,但不会把结果移交到下一个 Action;
266
- 只有 auto-run Action 的非 `none` 成功结果可以继续级联。
265
+ Draft 完成后宿主先展示结果供确认、复制或关闭;`text`、`image` 与
266
+ `path_reference` 实际值可由用户显式提交为下一个 Action 的输入,`none`
267
+ 不可继续。`complete(...)` 本身不会自动推进,也不会隐式选择其他 Action。
268
+ 这是既有 schema v4 结果形状的宿主交互变化,插件无需修改 manifest、
269
+ 重新 codegen 或重建。
267
270
 
268
271
  ### Draft Action:UI 需要 runtime 协助时
269
272
 
270
- `clipbus.action.complete` 接收 `{result: {resultKind: 'text' | 'image' | 'none', …}}`。**image 结果的 `imageTempPath` 必须由 runtime 端通过 `host.action.allocateImageTempPath` 分配**(UI 端没有这个能力),所以图片类 draft 的典型流程是:
273
+ `clipbus.action.complete` 接收 `{result: {resultKind: 'text' | 'image' | 'path_reference' | 'none', …}}`。**image 结果的 `imageTempPath` 必须由 runtime 端通过 `host.action.allocateImageTempPath` 分配**(UI 端没有这个能力),所以图片类 draft 的典型流程是:
271
274
 
272
275
  ```ts
273
276
  // shared/contracts.ts —— 两端共享类型契约
package/docs/manifest.md CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  | 字段 | 类型 | 必填 | 说明 |
12
12
  |---|---|---|---|
13
- | `schemaVersion` | `number` | 是 | 固定 `3` |
13
+ | `schemaVersion` | `number` | 是 | 固定 `4` |
14
14
  | `plugin` | `object` | 是 | 插件身份信息 |
15
15
  | `install` | `object` | 否 | 安装期 hook 配置 |
16
16
  | `runtime` | `object` | 是 | runtime 与 UI 根目录 |
@@ -109,7 +109,7 @@
109
109
 
110
110
  ```json
111
111
  {
112
- "schemaVersion": 3,
112
+ "schemaVersion": 4,
113
113
  "plugin": {
114
114
  "id": "plugin.example.sample",
115
115
  "title": "Sample Plugin",
@@ -150,10 +150,11 @@
150
150
  }
151
151
  ```
152
152
 
153
- ## 从 schema v2 升级
153
+ ## 从 schema v3 升级
154
154
 
155
- schema v3 是有意的 breaking change。将每个 Action 的
156
- `supportedItemTypes` 改为 `supportedInputKinds`,并移除
157
- `inputConstraints`、`possibleResultKinds`(若曾自行添加)。宿主不会把
158
- v2 Action 适配为 v3;已安装或开发插件会显示“需要更新”,直到 manifest
159
- 和构建产物通过 v3 校验。
155
+ schema v4 是 attachment 本机化的 breaking change。Detector artifact 删除
156
+ `attachmentSyncScope`,`item.setAttachments` 的每个 entry 删除 `syncScope`;
157
+ 宿主统一以 `local_only` 写入 attachment。Search extension 仍可同步,因此需要
158
+ 跨设备预搜索的 detector 文本应继续通过 `searchProjection` 或
159
+ `item.setSearchExtension` 显式写入。宿主不提供 v3/v4 字段兼容层;已安装或开发
160
+ 插件会显示“需要更新”,直到 manifest 和构建产物通过 v4 校验。
package/docs/preview.md CHANGED
@@ -88,7 +88,7 @@ interface PreviewScenario {
88
88
  | `viewport` | 该 scenario 的视口配置;未设置时使用 `opts.defaultViewport` |
89
89
  | `view` | 透传给 `mount` adapter 的不透明 hint(e.g. `'compact'`/`'expanded'`) |
90
90
 
91
- > **Draft Action 是终点 lifecycle。** `clipbus.action.complete()` 把一个最终结果交回宿主,不会继续进入下一个级联 Action。Preview 因而只模拟 Draft 交互与结果提交,不提供“下一步 Action”状态机。
91
+ > **Preview 不模拟完整宿主级联。** 真实宿主收到 `clipbus.action.complete()` 的 `text`、`image` 或 `path_reference` 结果后,会先展示确认阶段,用户可再显式推进到下一 Action;`none` 不可继续。Preview harness 只模拟 Draft 交互与结果提交并记录 native call,不实现宿主的结果确认、“下一步 Action”、动态过滤、rollback 或焦点状态机。
92
92
 
93
93
  ---
94
94
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clipbus/plugin-sdk",
3
- "version": "0.9.7",
3
+ "version": "0.9.8",
4
4
  "type": "module",
5
5
  "description": "Typed SDK for authoring Clipbus plugins — runtime (Node.js) and UI (WebView) helpers generated from the Clipbus plugin wire contract.",
6
6
  "keywords": [