@clipbus/plugin-sdk 0.9.9 → 0.10.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 (49) hide show
  1. package/API.md +94 -27
  2. package/CHANGELOG.md +84 -0
  3. package/README.md +11 -3
  4. package/SPECIFICATION.md +3 -2
  5. package/dist/dom/index.cjs +29 -34
  6. package/dist/dom/index.js +29 -34
  7. package/dist/dom/topicAdapter.d.ts +1 -1
  8. package/dist/generated/capabilities.generated.d.ts +355 -0
  9. package/dist/generated/data.generated.d.ts +33 -0
  10. package/dist/generated/hostClients.generated.d.ts +8 -5
  11. package/dist/generated/hostEvents.generated.d.ts +68 -0
  12. package/dist/generated/nodeIPC.generated.d.ts +106 -0
  13. package/dist/generated/{runtime.definePlugin.generated.d.ts → runtime.registry.generated.d.ts} +2 -2
  14. package/dist/generated/ui.clipbus.generated.d.ts +19 -20
  15. package/dist/generated/wireConstants.generated.d.ts +21 -0
  16. package/dist/internal/capabilities.d.ts +1 -1
  17. package/dist/internal/hostEvents.d.ts +15 -0
  18. package/dist/internal/pluginCall.d.ts +11 -0
  19. package/dist/internal/pluginContext.d.ts +6 -0
  20. package/dist/preview/index.cjs +41 -48
  21. package/dist/preview/index.js +41 -48
  22. package/dist/preview/wire.d.ts +2 -2
  23. package/dist/runtime/defineMessage.d.ts +1 -1
  24. package/dist/runtime/definePlugin.d.ts +2 -0
  25. package/dist/runtime/index.cjs +10 -82
  26. package/dist/runtime/index.d.cts +7 -1
  27. package/dist/runtime/index.d.ts +7 -1
  28. package/dist/runtime/index.js +9 -80
  29. package/dist/ui/index.cjs +107 -169
  30. package/dist/ui/index.d.cts +5 -1
  31. package/dist/ui/index.d.ts +5 -1
  32. package/dist/ui/index.js +107 -169
  33. package/dist/validate/index.d.cts +5 -4
  34. package/dist/validate/index.d.ts +5 -4
  35. package/docs/authoring.md +1 -1
  36. package/docs/capability-detection.md +2 -2
  37. package/docs/entry.md +4 -3
  38. package/docs/item-context.md +5 -3
  39. package/docs/permissions.md +1 -1
  40. package/package.json +6 -4
  41. package/dist/generated/INDEX.runtime.generated.d.ts +0 -6
  42. package/dist/generated/INDEX.ui.generated.d.ts +0 -4
  43. package/dist/generated/capabilityClients.generated.d.ts +0 -231
  44. package/dist/generated/hostBootstrap.generated.d.ts +0 -23
  45. package/dist/generated/runtime.host.generated.d.ts +0 -39
  46. package/dist/generated/topicSubscribers.generated.d.ts +0 -39
  47. package/dist/generated/ui.bootstrap.generated.d.ts +0 -17
  48. package/dist/internal/ipcBus.d.ts +0 -48
  49. package/dist/internal/runtimeInvokeClient.d.ts +0 -3
package/dist/ui/index.js CHANGED
@@ -2,6 +2,14 @@
2
2
  var STRUCTURED_ERROR_PREFIX = "__clipbus_structured_error__:";
3
3
  var CAPABILITIES_GLOBAL = "__CLIPBUS_PLUGIN_CAPABILITIES__";
4
4
  var CAPABILITY_UNSUPPORTED_ERROR_NAME = "PluginCapabilityUnsupported";
5
+ var PLUGIN_CALL_HANDLER_NAME = "clipbusPluginCall";
6
+ var WIRE_ERROR_NAMES = {
7
+ methodNotFound: "PluginRuntimeMethodNotFound",
8
+ handlerNotFound: "PluginRuntimeHandlerNotFound",
9
+ invocationExpired: "PluginRuntimeInvocationExpired",
10
+ hostCallLimitExceeded: "PluginRuntimeHostCallLimitExceeded",
11
+ hostBridgeUnavailable: "PluginHostBridgeUnavailable"
12
+ };
5
13
 
6
14
  // src/internal/capabilities.ts
7
15
  var CapabilityUnsupportedError = class extends Error {
@@ -36,35 +44,36 @@ function mapToCapabilityError(err, unsupportedName) {
36
44
  return err;
37
45
  }
38
46
 
39
- // src/internal/runtimeInvokeClient.ts
40
- var HANDLER_NAME = "clipbusPluginCall";
41
- async function callRuntimeInvokeStrict(payload) {
42
- const handler = globalThis.webkit?.messageHandlers?.[HANDLER_NAME];
47
+ // src/internal/pluginCall.ts
48
+ async function callPluginMethod(method, payload) {
49
+ const handler = globalThis.webkit?.messageHandlers?.[PLUGIN_CALL_HANDLER_NAME];
43
50
  if (!handler) {
44
- throw new Error("runtime.invoke is only available inside a Clipbus plugin WebView");
51
+ const err = new Error("clipbus." + method + " is only available inside a Clipbus plugin WebView");
52
+ err.name = WIRE_ERROR_NAMES.hostBridgeUnavailable;
53
+ throw err;
45
54
  }
46
55
  const normalized = JSON.parse(JSON.stringify(payload));
47
- let reply;
48
56
  try {
49
- reply = await handler.postMessage({ method: "runtime.invoke", payload: normalized });
57
+ return await handler.postMessage({ method, payload: normalized });
50
58
  } catch (err) {
51
59
  throw parseReplyError(err);
52
60
  }
61
+ }
62
+ async function callRuntimeInvoke(payload) {
63
+ if (!globalThis.webkit?.messageHandlers?.[PLUGIN_CALL_HANDLER_NAME]) {
64
+ throw new Error("runtime.invoke is only available inside a Clipbus plugin WebView");
65
+ }
66
+ const reply = await callPluginMethod("runtime.invoke", payload);
53
67
  return reply?.response;
54
68
  }
55
69
  function parseReplyError(raw) {
56
70
  const message = extractMessage(raw);
57
71
  if (message.startsWith(STRUCTURED_ERROR_PREFIX)) {
58
- const json = message.slice(STRUCTURED_ERROR_PREFIX.length);
59
72
  try {
60
- const parsed = JSON.parse(json);
73
+ const parsed = JSON.parse(message.slice(STRUCTURED_ERROR_PREFIX.length));
61
74
  const err = new Error(typeof parsed.message === "string" ? parsed.message : "");
62
- if (typeof parsed.name === "string" && parsed.name.length > 0) {
63
- err.name = parsed.name;
64
- }
65
- if (parsed.data !== void 0) {
66
- err.data = parsed.data;
67
- }
75
+ if (typeof parsed.name === "string" && parsed.name.length > 0) err.name = parsed.name;
76
+ if (parsed.data !== void 0) err.data = parsed.data;
68
77
  return mapToCapabilityError(err, CAPABILITY_UNSUPPORTED_ERROR_NAME);
69
78
  } catch {
70
79
  return new Error(message);
@@ -82,44 +91,20 @@ function extractMessage(raw) {
82
91
  return String(raw);
83
92
  }
84
93
 
85
- // src/generated/capabilityClients.generated.ts
86
- var HANDLER_NAME2 = "clipbusPluginCall";
87
- async function callPluginMethod(method, payload) {
88
- const handler = globalThis.webkit?.messageHandlers?.[HANDLER_NAME2];
89
- if (!handler) {
90
- const err = new Error("clipbus." + method + " is only available inside a Clipbus plugin WebView");
91
- err.name = "PluginHostBridgeUnavailable";
92
- throw err;
93
- }
94
- const normalized = JSON.parse(JSON.stringify(payload));
95
- let reply;
96
- try {
97
- reply = await handler.postMessage({ method, payload: normalized });
98
- } catch (err) {
99
- throw parseReplyError(err);
100
- }
101
- return reply;
102
- }
103
- var callItemReadAttachment = (payload) => callPluginMethod("item.readAttachment", payload);
104
- var callAssetCurrentItemImageUrl = () => callPluginMethod("asset.currentItemImageUrl", {});
105
- var callAssetPathReferenceImageUrl = (payload) => callPluginMethod("asset.pathReferenceImageUrl", payload);
106
- var callAssetCurrentActionInputImageUrl = () => callPluginMethod("asset.currentActionInputImageUrl", {});
107
- var callAssetCurrentActionInputPathReferenceImageUrl = (payload) => callPluginMethod("asset.currentActionInputPathReferenceImageUrl", payload);
108
- var callClipboardCopyText = (payload) => callPluginMethod("clipboard.copyText", payload);
109
- var callNavigationOpenUrl = (payload) => callPluginMethod("navigation.openUrl", payload);
110
- var callNavigationRevealInFinder = (payload) => callPluginMethod("navigation.revealInFinder", payload);
111
- var callNavigationOpenFilePath = (payload) => callPluginMethod("navigation.openFilePath", payload);
112
- var callActionSetButtons = (payload) => callPluginMethod("action.setButtons", payload);
113
- var callActionComplete = (payload) => callPluginMethod("action.complete", payload);
114
- var callAttachmentRendererSetButtons = (payload) => callPluginMethod("attachmentRenderer.setButtons", payload);
115
- var callWindowSetHeight = (payload) => callPluginMethod("window.setHeight", payload);
116
- var callWindowAutoFit = () => callPluginMethod("window.autoFit", {});
117
- var callInfoPanelOpen = (payload) => callPluginMethod("infoPanel.open", payload);
118
- var callInfoPanelClose = (payload) => callPluginMethod("infoPanel.close", payload);
119
- var callSettingsGet = (payload) => callPluginMethod("settings.get", payload);
120
- var callSettingsGetAll = () => callPluginMethod("settings.getAll", {});
121
- var callConsoleLog = (payload) => callPluginMethod("console.log", payload);
122
- var callTextInputStateChanged = (payload) => callPluginMethod("textInput.stateChanged", payload);
94
+ // src/generated/hostEvents.generated.ts
95
+ var HOST_EVENT_WINDOW_GLOBALS = {
96
+ "clipbus-plugin-context": "__CLIPBUS_PLUGIN_CONTEXT__",
97
+ "clipbus-plugin-item": "__CLIPBUS_PLUGIN_ITEM__",
98
+ "clipbus-plugin-attachment": "__CLIPBUS_PLUGIN_ATTACHMENT__",
99
+ "clipbus-plugin-draft": "__CLIPBUS_PLUGIN_DRAFT__",
100
+ "clipbus-plugin-action-input": "__CLIPBUS_PLUGIN_ACTION_INPUT__",
101
+ "clipbus-plugin-theme": "__CLIPBUS_PLUGIN_THEME__",
102
+ "clipbus-plugin-attachment-host-invoke": null,
103
+ "clipbus-plugin-action-host-invoke": null,
104
+ "clipbus-plugin-info-panel-on-action": null,
105
+ "clipbus-plugin-info-panel-on-close": null,
106
+ "clipbus-plugin-locale": "__CLIPBUS_PLUGIN_LOCALE__"
107
+ };
123
108
 
124
109
  // src/internal/topic.ts
125
110
  function createTopic(initial) {
@@ -164,95 +149,36 @@ function readWindowGlobal(key) {
164
149
  return window[key];
165
150
  }
166
151
 
167
- // src/generated/topicSubscribers.generated.ts
168
- function onContext(listener) {
169
- const handler = (e) => listener(e.detail);
170
- globalThis.addEventListener("clipbus-plugin-context", handler);
171
- return () => globalThis.removeEventListener("clipbus-plugin-context", handler);
172
- }
173
- function onItem(listener) {
174
- const handler = (e) => listener(e.detail);
175
- globalThis.addEventListener("clipbus-plugin-item", handler);
176
- return () => globalThis.removeEventListener("clipbus-plugin-item", handler);
177
- }
178
- function onAttachment(listener) {
179
- const handler = (e) => listener(e.detail);
180
- globalThis.addEventListener("clipbus-plugin-attachment", handler);
181
- return () => globalThis.removeEventListener("clipbus-plugin-attachment", handler);
182
- }
183
- function onDraft(listener) {
184
- const handler = (e) => listener(e.detail);
185
- globalThis.addEventListener("clipbus-plugin-draft", handler);
186
- return () => globalThis.removeEventListener("clipbus-plugin-draft", handler);
187
- }
188
- function onActionInput(listener) {
152
+ // src/internal/hostEvents.ts
153
+ function onHostEvent(name, listener) {
189
154
  const handler = (e) => listener(e.detail);
190
- globalThis.addEventListener("clipbus-plugin-action-input", handler);
191
- return () => globalThis.removeEventListener("clipbus-plugin-action-input", handler);
155
+ globalThis.addEventListener(name, handler);
156
+ return () => globalThis.removeEventListener(name, handler);
192
157
  }
193
- function onTheme(listener) {
194
- const handler = (e) => listener(e.detail);
195
- globalThis.addEventListener("clipbus-plugin-theme", handler);
196
- return () => globalThis.removeEventListener("clipbus-plugin-theme", handler);
197
- }
198
- function onAttachmentHostInvoke(listener) {
199
- const handler = (e) => listener(e.detail);
200
- globalThis.addEventListener("clipbus-plugin-attachment-host-invoke", handler);
201
- return () => globalThis.removeEventListener("clipbus-plugin-attachment-host-invoke", handler);
202
- }
203
- function onActionHostInvoke(listener) {
204
- const handler = (e) => listener(e.detail);
205
- globalThis.addEventListener("clipbus-plugin-action-host-invoke", handler);
206
- return () => globalThis.removeEventListener("clipbus-plugin-action-host-invoke", handler);
158
+ function createHostTopic(name) {
159
+ const windowGlobal = HOST_EVENT_WINDOW_GLOBALS[name];
160
+ const topic = createTopic(
161
+ windowGlobal === null ? void 0 : readWindowGlobal(windowGlobal)
162
+ );
163
+ onHostEvent(name, (payload) => topic.set(payload));
164
+ return topic;
207
165
  }
208
- function onInfoPanelOnAction(listener) {
209
- const handler = (e) => listener(e.detail);
210
- globalThis.addEventListener("clipbus-plugin-info-panel-on-action", handler);
211
- return () => globalThis.removeEventListener("clipbus-plugin-info-panel-on-action", handler);
212
- }
213
- function onInfoPanelOnClose(listener) {
214
- const handler = (e) => listener(e.detail);
215
- globalThis.addEventListener("clipbus-plugin-info-panel-on-close", handler);
216
- return () => globalThis.removeEventListener("clipbus-plugin-info-panel-on-close", handler);
217
- }
218
- function onLocale(listener) {
219
- const handler = (e) => listener(e.detail);
220
- globalThis.addEventListener("clipbus-plugin-locale", handler);
221
- return () => globalThis.removeEventListener("clipbus-plugin-locale", handler);
166
+ function createHostStream(name) {
167
+ const stream = createStream();
168
+ onHostEvent(name, (payload) => stream.emit(payload));
169
+ return stream;
222
170
  }
223
171
 
224
- // src/generated/ui.bootstrap.generated.ts
225
- var _pluginContextTopic = createTopic(readWindowGlobal("__CLIPBUS_PLUGIN_CONTEXT__"));
226
- onContext((p) => _pluginContextTopic.set(p));
227
- var _itemTopic = createTopic(readWindowGlobal("__CLIPBUS_PLUGIN_ITEM__"));
228
- onItem((p) => _itemTopic.set(p));
229
- var _itemAttachmentTopic = createTopic(readWindowGlobal("__CLIPBUS_PLUGIN_ATTACHMENT__"));
230
- onAttachment((p) => _itemAttachmentTopic.set(p));
231
- var _actionDraftTopic = createTopic(readWindowGlobal("__CLIPBUS_PLUGIN_DRAFT__"));
232
- onDraft((p) => _actionDraftTopic.set(p));
233
- var _actionInputTopic = createTopic(readWindowGlobal("__CLIPBUS_PLUGIN_ACTION_INPUT__"));
234
- onActionInput((p) => _actionInputTopic.set(p));
235
- var _themeTopic = createTopic(readWindowGlobal("__CLIPBUS_PLUGIN_THEME__"));
236
- onTheme((p) => _themeTopic.set(p));
237
- var _attachmentRendererOnHostInvokeStream = createStream();
238
- onAttachmentHostInvoke((p) => _attachmentRendererOnHostInvokeStream.emit(p));
239
- var _actionOnHostInvokeStream = createStream();
240
- onActionHostInvoke((p) => _actionOnHostInvokeStream.emit(p));
241
- var _infoPanelOnActionStream = createStream();
242
- onInfoPanelOnAction((p) => _infoPanelOnActionStream.emit(p));
243
- var _infoPanelOnCloseStream = createStream();
244
- onInfoPanelOnClose((p) => _infoPanelOnCloseStream.emit(p));
245
- var _localeTopic = createTopic(readWindowGlobal("__CLIPBUS_PLUGIN_LOCALE__"));
246
- onLocale((p) => _localeTopic.set(p));
172
+ // src/internal/pluginContext.ts
247
173
  var PluginContextError = class extends Error {
248
174
  constructor(message) {
249
175
  super(message);
250
176
  this.name = "PluginContextError";
251
177
  }
252
178
  };
253
- function guardContext(expected, run) {
179
+ function guardContext(contextTopic, expected, run) {
254
180
  return (...args) => {
255
- const current = _pluginContextTopic.current()?.mode;
181
+ const current = contextTopic.current()?.mode;
256
182
  if (current !== expected) {
257
183
  throw new PluginContextError(
258
184
  `This verb is not available in the current plugin context (expected: ${expected}, got: ${current || "unknown"})`
@@ -263,90 +189,102 @@ function guardContext(expected, run) {
263
189
  }
264
190
 
265
191
  // src/generated/ui.clipbus.generated.ts
192
+ var pluginContextTopic = createHostTopic("clipbus-plugin-context");
193
+ var itemTopic = createHostTopic("clipbus-plugin-item");
194
+ var itemAttachmentTopic = createHostTopic("clipbus-plugin-attachment");
195
+ var actionDraftTopic = createHostTopic("clipbus-plugin-draft");
196
+ var actionInputTopic = createHostTopic("clipbus-plugin-action-input");
197
+ var themeTopic = createHostTopic("clipbus-plugin-theme");
198
+ var attachmentRendererOnHostInvokeStream = createHostStream("clipbus-plugin-attachment-host-invoke");
199
+ var actionOnHostInvokeStream = createHostStream("clipbus-plugin-action-host-invoke");
200
+ var infoPanelOnActionStream = createHostStream("clipbus-plugin-info-panel-on-action");
201
+ var infoPanelOnCloseStream = createHostStream("clipbus-plugin-info-panel-on-close");
202
+ var localeTopic = createHostTopic("clipbus-plugin-locale");
266
203
  var clipbus = {
267
204
  capabilities: createCapabilitiesApi(() => readInjectedCapabilities(globalThis, CAPABILITIES_GLOBAL)),
268
205
  runtime: {
269
- invoke: (payload) => callRuntimeInvokeStrict(payload)
206
+ invoke: (payload) => callRuntimeInvoke(payload)
270
207
  },
271
208
  item: {
272
- current: () => _itemTopic.current(),
273
- on: (fn) => _itemTopic.on(fn),
274
- readAttachment: (payload) => callItemReadAttachment(payload),
209
+ current: itemTopic.current,
210
+ on: itemTopic.on,
211
+ readAttachment: (payload) => callPluginMethod("item.readAttachment", payload),
275
212
  attachment: {
276
- current: () => _itemAttachmentTopic.current(),
277
- on: (fn) => _itemAttachmentTopic.on(fn)
213
+ current: itemAttachmentTopic.current,
214
+ on: itemAttachmentTopic.on
278
215
  }
279
216
  },
280
217
  asset: {
281
- currentItemImageUrl: () => callAssetCurrentItemImageUrl(),
282
- pathReferenceImageUrl: (payload) => callAssetPathReferenceImageUrl(payload),
283
- currentActionInputImageUrl: guardContext("action", () => callAssetCurrentActionInputImageUrl()),
284
- currentActionInputPathReferenceImageUrl: guardContext("action", (payload) => callAssetCurrentActionInputPathReferenceImageUrl(payload))
218
+ currentItemImageUrl: () => callPluginMethod("asset.currentItemImageUrl", {}),
219
+ pathReferenceImageUrl: (payload) => callPluginMethod("asset.pathReferenceImageUrl", payload),
220
+ currentActionInputImageUrl: guardContext(pluginContextTopic, "action", () => callPluginMethod("asset.currentActionInputImageUrl", {})),
221
+ currentActionInputPathReferenceImageUrl: guardContext(pluginContextTopic, "action", (payload) => callPluginMethod("asset.currentActionInputPathReferenceImageUrl", payload))
285
222
  },
286
223
  clipboard: {
287
- copyText: (payload) => callClipboardCopyText(payload)
224
+ copyText: (payload) => callPluginMethod("clipboard.copyText", payload)
288
225
  },
289
226
  navigation: {
290
- openUrl: (payload) => callNavigationOpenUrl(payload),
291
- revealInFinder: (payload) => callNavigationRevealInFinder(payload),
292
- openFilePath: (payload) => callNavigationOpenFilePath(payload)
227
+ openUrl: (payload) => callPluginMethod("navigation.openUrl", payload),
228
+ revealInFinder: (payload) => callPluginMethod("navigation.revealInFinder", payload),
229
+ openFilePath: (payload) => callPluginMethod("navigation.openFilePath", payload)
293
230
  },
294
231
  action: {
295
- setButtons: guardContext("action", (payload) => callActionSetButtons(payload)),
296
- complete: guardContext("action", (payload) => callActionComplete(payload)),
232
+ setButtons: guardContext(pluginContextTopic, "action", (payload) => callPluginMethod("action.setButtons", payload)),
233
+ complete: guardContext(pluginContextTopic, "action", (payload) => callPluginMethod("action.complete", payload)),
297
234
  draft: {
298
- current: () => _actionDraftTopic.current(),
299
- on: (fn) => _actionDraftTopic.on(fn)
235
+ current: actionDraftTopic.current,
236
+ on: actionDraftTopic.on
300
237
  },
301
238
  input: {
302
- current: () => _actionInputTopic.current(),
303
- on: (fn) => _actionInputTopic.on(fn)
239
+ current: actionInputTopic.current,
240
+ on: actionInputTopic.on
304
241
  },
305
242
  onHostInvoke: {
306
- on: (fn) => _actionOnHostInvokeStream.on(fn)
243
+ on: actionOnHostInvokeStream.on
307
244
  }
308
245
  },
309
246
  attachmentRenderer: {
310
- setButtons: guardContext("attachmentRenderer", (payload) => callAttachmentRendererSetButtons(payload)),
247
+ setButtons: guardContext(pluginContextTopic, "attachmentRenderer", (payload) => callPluginMethod("attachmentRenderer.setButtons", payload)),
311
248
  onHostInvoke: {
312
- on: (fn) => _attachmentRendererOnHostInvokeStream.on(fn)
249
+ on: attachmentRendererOnHostInvokeStream.on
313
250
  }
314
251
  },
315
252
  window: {
316
- setHeight: (payload) => callWindowSetHeight(payload),
317
- autoFit: () => callWindowAutoFit()
253
+ setHeight: (payload) => callPluginMethod("window.setHeight", payload),
254
+ autoFit: () => callPluginMethod("window.autoFit", {})
318
255
  },
319
256
  infoPanel: {
320
- open: (payload) => callInfoPanelOpen(payload),
321
- close: (payload) => callInfoPanelClose(payload),
257
+ open: (payload) => callPluginMethod("infoPanel.open", payload),
258
+ close: (payload) => callPluginMethod("infoPanel.close", payload),
322
259
  onAction: {
323
- on: (fn) => _infoPanelOnActionStream.on(fn)
260
+ on: infoPanelOnActionStream.on
324
261
  },
325
262
  onClose: {
326
- on: (fn) => _infoPanelOnCloseStream.on(fn)
263
+ on: infoPanelOnCloseStream.on
327
264
  }
328
265
  },
329
266
  settings: {
330
- get: (payload) => callSettingsGet(payload),
331
- getAll: () => callSettingsGetAll()
267
+ get: (payload) => callPluginMethod("settings.get", payload),
268
+ getAll: () => callPluginMethod("settings.getAll", {})
332
269
  },
333
270
  console: {
334
- log: (payload) => callConsoleLog(payload)
271
+ log: (payload) => callPluginMethod("console.log", payload),
272
+ diagnostic: (payload) => callPluginMethod("console.diagnostic", payload)
335
273
  },
336
274
  textInput: {
337
- stateChanged: (payload) => callTextInputStateChanged(payload)
275
+ stateChanged: (payload) => callPluginMethod("textInput.stateChanged", payload)
338
276
  },
339
277
  pluginContext: {
340
- current: () => _pluginContextTopic.current(),
341
- on: (fn) => _pluginContextTopic.on(fn)
278
+ current: pluginContextTopic.current,
279
+ on: pluginContextTopic.on
342
280
  },
343
281
  theme: {
344
- current: () => _themeTopic.current(),
345
- on: (fn) => _themeTopic.on(fn)
282
+ current: themeTopic.current,
283
+ on: themeTopic.on
346
284
  },
347
285
  locale: {
348
- current: () => _localeTopic.current(),
349
- on: (fn) => _localeTopic.on(fn)
286
+ current: localeTopic.current,
287
+ on: localeTopic.on
350
288
  }
351
289
  };
352
290
 
@@ -4,9 +4,10 @@
4
4
  * JavaScript mirror of the native PluginManifestValidator (Swift).
5
5
  * Authority: platform/macos/Sources/Services/Impl/Plugins/PluginManifestValidator.swift
6
6
  *
7
- * Parity: scripts/checks/check-plugin-manifest-validator-parity.sh enforces that
8
- * MANIFEST_VALIDATION_CODES equals the Swift ValidationError case set, and that
9
- * CANONICAL_ITEM_TYPES equals ClipboardItemType.swift enum case raw values.
7
+ * Parity: Swift, JavaScript and C# validator tests all consume
8
+ * protocol/plugin/fixtures/manifest-validator-parity.v4.json; MANIFEST_VALIDATION_CODES
9
+ * mirrors the Swift ValidationError case set and CANONICAL_ITEM_TYPES mirrors
10
+ * ClipboardItemType.swift enum case raw values.
10
11
  *
11
12
  * Design difference from native: native throws on the first error (fail-fast); this
12
13
  * function collects ALL issues and returns them — better DX for development so that
@@ -32,7 +33,7 @@ export interface ValidateManifestOptions {
32
33
  }
33
34
  /**
34
35
  * All validation error codes, verbatim camelCase mirror of Swift ValidationError cases.
35
- * Parity with native is enforced by scripts/checks/check-plugin-manifest-validator-parity.sh.
36
+ * Parity with native is proven by the shared manifest-validator-parity corpus.
36
37
  */
37
38
  export declare const MANIFEST_VALIDATION_CODES: readonly ["unsupportedSchemaVersion", "missingManifest", "missingRuntimeEntry", "missingUIRoot", "legacyFieldNotSupported", "duplicateAttachmentRendererID", "duplicateDetectorID", "duplicateActionID", "emptyPluginID", "invalidPluginID", "emptyPluginTitle", "installEntryMissing", "installEntryOutsideRoot", "rendererTitleEmpty", "detectorTitleEmpty", "actionTitleEmpty", "detectorMissingSupportedInputKinds", "detectorAttachmentTypesEmpty", "detectorAttachmentTypeNotRenderable", "detectorAttachmentTypeOutsideNamespace", "rendererUIEntryMissing", "rendererUIEntryOutsideRoot", "actionMissingSupportedInputKinds", "actionUIEntryMissing", "actionUIEntryOutsideRoot", "detectorInvalidSupportedInputKind", "actionInvalidSupportedInputKind"];
38
39
  /**
@@ -4,9 +4,10 @@
4
4
  * JavaScript mirror of the native PluginManifestValidator (Swift).
5
5
  * Authority: platform/macos/Sources/Services/Impl/Plugins/PluginManifestValidator.swift
6
6
  *
7
- * Parity: scripts/checks/check-plugin-manifest-validator-parity.sh enforces that
8
- * MANIFEST_VALIDATION_CODES equals the Swift ValidationError case set, and that
9
- * CANONICAL_ITEM_TYPES equals ClipboardItemType.swift enum case raw values.
7
+ * Parity: Swift, JavaScript and C# validator tests all consume
8
+ * protocol/plugin/fixtures/manifest-validator-parity.v4.json; MANIFEST_VALIDATION_CODES
9
+ * mirrors the Swift ValidationError case set and CANONICAL_ITEM_TYPES mirrors
10
+ * ClipboardItemType.swift enum case raw values.
10
11
  *
11
12
  * Design difference from native: native throws on the first error (fail-fast); this
12
13
  * function collects ALL issues and returns them — better DX for development so that
@@ -32,7 +33,7 @@ export interface ValidateManifestOptions {
32
33
  }
33
34
  /**
34
35
  * All validation error codes, verbatim camelCase mirror of Swift ValidationError cases.
35
- * Parity with native is enforced by scripts/checks/check-plugin-manifest-validator-parity.sh.
36
+ * Parity with native is proven by the shared manifest-validator-parity corpus.
36
37
  */
37
38
  export declare const MANIFEST_VALIDATION_CODES: readonly ["unsupportedSchemaVersion", "missingManifest", "missingRuntimeEntry", "missingUIRoot", "legacyFieldNotSupported", "duplicateAttachmentRendererID", "duplicateDetectorID", "duplicateActionID", "emptyPluginID", "invalidPluginID", "emptyPluginTitle", "installEntryMissing", "installEntryOutsideRoot", "rendererTitleEmpty", "detectorTitleEmpty", "actionTitleEmpty", "detectorMissingSupportedInputKinds", "detectorAttachmentTypesEmpty", "detectorAttachmentTypeNotRenderable", "detectorAttachmentTypeOutsideNamespace", "rendererUIEntryMissing", "rendererUIEntryOutsideRoot", "actionMissingSupportedInputKinds", "actionUIEntryMissing", "actionUIEntryOutsideRoot", "detectorInvalidSupportedInputKind", "actionInvalidSupportedInputKind"];
38
39
  /**
package/docs/authoring.md CHANGED
@@ -278,7 +278,7 @@ import { defineMessage } from '@clipbus/plugin-sdk/runtime'; // 或 /ui,类
278
278
  export const GenerateImage = defineMessage<{ prompt: string }, { imageTempPath: string }>('generate-image');
279
279
 
280
280
  // runtime/index.ts
281
- const { definePlugin, host } = require('@clipbus/plugin-sdk/runtime');
281
+ const { definePlugin } = require('@clipbus/plugin-sdk/runtime');
282
282
  const fs = require('node:fs/promises');
283
283
  const { GenerateImage } = require('../shared/contracts');
284
284
 
@@ -55,9 +55,9 @@ try {
55
55
  ### 调用前门控
56
56
 
57
57
  ```ts
58
- import type { PluginActionHandler } from '@clipbus/plugin-sdk/runtime';
58
+ import type { PluginAutoRunActionHandler } from '@clipbus/plugin-sdk/runtime';
59
59
 
60
- const action: PluginActionHandler = {
60
+ const action: PluginAutoRunActionHandler = {
61
61
  async runAutoAction(input, ctx) {
62
62
  if (ctx.host.capabilities.has('item.setTags')) {
63
63
  await ctx.host.item.setTags({ tags: ['processed'] });
package/docs/entry.md CHANGED
@@ -8,7 +8,7 @@ SDK 暴露两个 subpath。每个入口导出的符号**完全由 codegen 生成
8
8
 
9
9
  ```ts
10
10
  // CommonJS(推荐与现行 bundler 配合)
11
- const { definePlugin, host, actionResult, defineMessage } = require('@clipbus/plugin-sdk/runtime');
11
+ const { definePlugin, actionResult, defineMessage } = require('@clipbus/plugin-sdk/runtime');
12
12
 
13
13
  // ESM(types)
14
14
  import type { PluginAttachmentRendererHandler, PluginAutoRunActionHandler, PluginDetectorHandler } from '@clipbus/plugin-sdk/runtime';
@@ -16,11 +16,12 @@ import type { PluginAttachmentRendererHandler, PluginAutoRunActionHandler, Plugi
16
16
 
17
17
  入口导出:
18
18
 
19
- - `definePlugin` —— 注册 handler;本地校验 registry 形状
20
- - `host` —— 直接调宿主能力的单例(`host.<domain>.<verb>(payload)`)。**推荐写法**是从 handler 收到的 `ctx.host` 调用(同一个对象,但写在 handler 内更显式)。
19
+ - `definePlugin` —— 注册 handler;本地校验 registry 形状。传入 `{ setup(context) { … } }` 时,`context` 是宿主提供的 `PluginRuntimeLaunchContext`(`plugin`、`host`、`supportedCapabilities`)
21
20
  - `actionResult` —— `runAutoAction` 的返回值构造器
22
21
  - `defineMessage` —— UI ↔ Runtime RPC 的类型契约
23
22
 
23
+ 宿主能力只能通过 handler 收到的 `ctx.host.<domain>.<verb>(payload)` 调用:`ctx` 与本次调用绑定,调用结束后失效。0.10.0 起不再导出 `host` 单例(它从未能在宿主中工作);迁移方式是把 `host.x.y(...)` 改为 handler 内的 `ctx.host.x.y(...)`。
24
+
24
25
  ## UI 入口
25
26
 
26
27
  ```ts
@@ -153,6 +153,7 @@ WebView 沙箱禁止 `file://`,且 SDK **从不**把真实文件路径暴露
153
153
  | `clipbus.asset.pathReferenceImageUrl({ index })` | UI | `{ index }` | `{ url?: string }` | 当前 item 第 `index` 个 `path_reference` 条目的图(越界 / 非图片文件 → `url` 缺失) |
154
154
  | `clipbus.asset.currentActionInputImageUrl()` | UI/action | `{}` | `{ url?: string }` | 当前 Action image 值,与原始 item type 无关 |
155
155
  | `clipbus.asset.currentActionInputPathReferenceImageUrl({ index })` | UI/action | `{ index }` | `{ url?: string }` | 当前 Action path_reference 中第 `index` 张图 |
156
+ | `host.asset.allocateImageTempPath({ formatHint })` | runtime | `{ formatHint }` | `{ path: string }` | 为当前 session 分配生成图片的路径;随 owner 释放 |
156
157
  | `host.asset.registerImage({ path })` | runtime | `{ path }` | `{ url: string }` | 把 **Node 产出 / 已有**的本地图注册进当前 session,签发 URL |
157
158
 
158
159
  capability 完整签名见 [API.md](../API.md)。
@@ -196,8 +197,9 @@ const { GenerateImage } = require('../shared/contracts');
196
197
  module.exports = definePlugin({
197
198
  messageHandlers: Object.fromEntries([
198
199
  GenerateImage.handle(async (_req, ctx) => {
199
- const tmpPath = await writeSomePng(); // 你的产图逻辑,写到临时文件
200
- const { url } = await ctx.host.asset.registerImage({ path: tmpPath });
200
+ const { path } = await ctx.host.asset.allocateImageTempPath({ formatHint: 'png' });
201
+ await writeSomePng(path); // Node 写入宿主分配的会话路径
202
+ const { url } = await ctx.host.asset.registerImage({ path });
201
203
  return { url }; // 只回传 URL,不回传字节
202
204
  }),
203
205
  ]),
@@ -209,7 +211,7 @@ const { url } = await GenerateImage.invoke({});
209
211
  imageUrl.value = url; // <img :src="imageUrl">
210
212
  ```
211
213
 
212
- > **token 生命周期:** URL 绑定当前 session owner,invocation / WebView 释放时一并失效,且跨 owner 隔离——不要持久化或跨会话复用。
214
+ > **路径与 token 生命周期:** 分配的文件及 URL 绑定当前 session owner,invocation / WebView 释放时一并失效,且跨 owner 隔离——不要持久化或跨会话复用。Windows 只接受该 owner 已分配的注册路径。
213
215
  > **何时用哪个:** renderer 显示原始 item 用 `currentItemImageUrl` /
214
216
  > `pathReferenceImageUrl`;Draft Action 显示当前级联值用
215
217
  > `currentActionInputImageUrl` / `currentActionInputPathReferenceImageUrl`;
@@ -23,7 +23,7 @@
23
23
  | `setAttachment` | `host.item.setAttachments` |
24
24
  | `setSearchExtension` | `host.item.setSearchExtension` |
25
25
 
26
- **不受门控:** `host.clipboard.*`、`host.navigation.*`、`host.settings.*`(只读)、`host.console.log`、`host.action.allocateImageTempPath`、`host.action.materializeInputImagePath`、`host.action.allocateOutputFilePath`、`host.item.materializeImagePath`、`host.item.readAttachment`、所有 `clipbus.*` 中的 UI 专属 verb(`window.*`、`action.setButtons`、`action.complete`、`attachmentRenderer.setButtons` 等)。
26
+ **不受门控:** `host.clipboard.*`、`host.navigation.*`、`host.settings.*`(只读)、`host.console.log`、`host.console.diagnostic`、`host.asset.allocateImageTempPath`、`host.asset.registerImage`、`host.action.allocateImageTempPath`、`host.action.materializeInputImagePath`、`host.action.allocateOutputFilePath`、`host.item.materializeImagePath`、`host.item.readAttachment`、所有 `clipbus.*` 中的 UI 专属 verb(`window.*`、`action.setButtons`、`action.complete`、`attachmentRenderer.setButtons` 等)。
27
27
 
28
28
  各 verb 的完整签名见 [API.md](../API.md)。
29
29
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clipbus/plugin-sdk",
3
- "version": "0.9.9",
3
+ "version": "0.10.0",
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": [
@@ -9,10 +9,10 @@
9
9
  "sdk"
10
10
  ],
11
11
  "license": "MIT",
12
+ "homepage": "https://github.com/scubers/clipbus-release/releases?q=%22Clipbus+Plugin+SDK%22&expanded=true",
12
13
  "repository": {
13
14
  "type": "git",
14
- "url": "git+https://github.com/scubers/clipbus.git",
15
- "directory": "packages/plugin-sdk"
15
+ "url": "git+https://github.com/scubers/clipbus-release.git"
16
16
  },
17
17
  "engines": {
18
18
  "node": ">=18.0.0"
@@ -78,7 +78,8 @@
78
78
  "build": "node ./scripts/build.mjs",
79
79
  "pretest": "node ./scripts/build.mjs",
80
80
  "test": "node --require ./tests/setup.cjs --test",
81
- "prepublishOnly": "npm run build && npm test"
81
+ "prepack": "node ./scripts/release-notes.mjs changelog",
82
+ "prepublishOnly": "npm run build && npm test && node ./scripts/release-notes.mjs check"
82
83
  },
83
84
  "files": [
84
85
  "dist",
@@ -86,6 +87,7 @@
86
87
  "SPECIFICATION.md",
87
88
  "README.md",
88
89
  "API.md",
90
+ "CHANGELOG.md",
89
91
  "docs"
90
92
  ],
91
93
  "publishConfig": {
@@ -1,6 +0,0 @@
1
- export { host } from './runtime.host.generated.js';
2
- export { definePlugin } from './runtime.definePlugin.generated.js';
3
- export { actionResult } from './runtime.actionResult.generated.js';
4
- export type * from './data.generated.js';
5
- export type * from './capabilityClients.generated.js';
6
- export type * from './runtime.handlers.generated.js';
@@ -1,4 +0,0 @@
1
- export { clipbus } from './ui.clipbus.generated.js';
2
- export type * from './data.generated.js';
3
- export type * from './capabilityClients.generated.js';
4
- export { PluginContextError } from './ui.bootstrap.generated.js';