@clipbus/plugin-sdk 0.8.4 → 0.8.6

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/README.md CHANGED
@@ -526,6 +526,27 @@ Exits 0 on success, 1 if any issues are found.
526
526
 
527
527
  ---
528
528
 
529
+ ## Preview harness (`@clipbus/plugin-sdk/preview`)
530
+
531
+ Framework-neutral development-time workbench for iterating on plugin UI without a real macOS host process. Provides wire injection (host → plugin bootstrap), a bidirectional fake host (plugin → host calls go to a call log panel), and viewport height tracking via `clipbus.window.setHeight`.
532
+
533
+ ```js
534
+ import { createPreviewWorkbench } from '@clipbus/plugin-sdk/preview';
535
+
536
+ createPreviewWorkbench(document.getElementById('app')!, {
537
+ scenarios,
538
+ mount(slotEl, { scenario }) {
539
+ const app = createApp(MyPlugin);
540
+ app.mount(slotEl);
541
+ return () => app.unmount();
542
+ },
543
+ });
544
+ ```
545
+
546
+ See [`docs/preview.md`](./docs/preview.md) for `PreviewScenario` / `PreviewViewport` field reference and full harness API.
547
+
548
+ ---
549
+
529
550
  ## See also
530
551
 
531
552
  - [API.md](./API.md) — autoritative API reference, regenerated from `protocol/plugin/src/catalog.ts`
@@ -0,0 +1,22 @@
1
+ import type { PluginAttachmentPayload, PluginClipboardItem, PluginThemeTokenSnapshot } from './data.generated.js';
2
+ export interface PluginContextPayload {
3
+ mode: 'attachmentRenderer' | 'action';
4
+ pluginID: string;
5
+ }
6
+ export interface PluginLocalePayload {
7
+ locale: string;
8
+ }
9
+ export declare function emitAttachmentBootstrap(p: {
10
+ context?: PluginContextPayload;
11
+ item?: PluginClipboardItem;
12
+ attachment?: PluginAttachmentPayload;
13
+ theme?: PluginThemeTokenSnapshot;
14
+ locale?: PluginLocalePayload;
15
+ }): void;
16
+ export declare function emitActionBootstrap(p: {
17
+ context?: PluginContextPayload;
18
+ item?: PluginClipboardItem;
19
+ draft?: Record<string, unknown>;
20
+ theme?: PluginThemeTokenSnapshot;
21
+ locale?: PluginLocalePayload;
22
+ }): void;
@@ -1,3 +1,5 @@
1
1
  export declare const STRUCTURED_ERROR_PREFIX: "__clipbus_structured_error__:";
2
2
  export declare const CAPABILITIES_GLOBAL: "__CLIPBUS_PLUGIN_CAPABILITIES__";
3
3
  export declare const CAPABILITY_UNSUPPORTED_ERROR_NAME: "PluginCapabilityUnsupported";
4
+ export declare const PLUGIN_CALL_HANDLER_NAME: "clipbusPluginCall";
5
+ export declare const WINDOW_SET_HEIGHT_METHOD: "window.setHeight";
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Pure DOM workbench chrome — no framework dependencies.
3
+ *
4
+ * Responsibilities:
5
+ * - Header: Wire Bench wordmark + controls console
6
+ * (Surface toggle, Scenario select, Theme select, Width slider)
7
+ * - Stage: workspace hero (left) + dock (right, position:sticky)
8
+ * - Workspace: ws-head label + ws-surface holding the frozen specimen card
9
+ * subtree (card shell → viewport → webview slot) + dims readout + growth floor
10
+ * - Dock: IN attachment card (Wire Input mount point) over OUT attachment card
11
+ * (Native Calls single-open accordion)
12
+ * - Viewport centered in workspace; width driven by range slider per mode
13
+ * - Fixed-height viewport driven by setHeight calls
14
+ * - Host button strip; click dispatches host-invoke events
15
+ * - clipbus-plugin-set-buttons listener (updates button strip from plugin)
16
+ * - Native Calls OUT panel: single-open accordion, newest auto-opens
17
+ */
18
+ import type { PreviewScenario, PreviewWorkbenchOptions } from './types.js';
19
+ import type { PreviewThemePreset } from './theme.js';
20
+ /**
21
+ * Group scenarios by mode, preserving original order within each group.
22
+ * Returns only groups that have at least one scenario.
23
+ */
24
+ export declare function groupScenariosByMode(scenarios: PreviewScenario[]): {
25
+ mode: 'attachmentRenderer' | 'action';
26
+ scenarios: PreviewScenario[];
27
+ }[];
28
+ /**
29
+ * Resolve effective viewport width settings for a scenario.
30
+ * scenario.viewport.width / widthMin / widthMax override mode defaults.
31
+ */
32
+ export declare function resolveViewportWidth(scenario: PreviewScenario): {
33
+ width: number;
34
+ min: number;
35
+ max: number;
36
+ };
37
+ /**
38
+ * Clamp a width value within [min, max].
39
+ */
40
+ export declare function clampWidth(value: number, min: number, max: number): number;
41
+ export interface ChromeHandlers {
42
+ /**
43
+ * Scenario or view switched — full (re)activation; remount expected.
44
+ * Second arg is the active theme preset key (string, e.g. 'graphite').
45
+ */
46
+ onActivate(scenario: PreviewScenario, presetKey: string): void;
47
+ /**
48
+ * Theme picker changed — re-inject wire so theme subscribers receive the new
49
+ * scheme. No remount: theme.onChange is designed to restyle in place.
50
+ * Second arg is the new active preset key.
51
+ */
52
+ onThemeChange(scenario: PreviewScenario, presetKey: string): void;
53
+ }
54
+ export interface ChromeResult {
55
+ /** The <div> slot where the plugin UI should be mounted. */
56
+ slotEl: HTMLElement;
57
+ /**
58
+ * The scroll container of the IN attachment card where renderWireInput()
59
+ * should mount the Wire Input collapsible sections.
60
+ */
61
+ inputContainer: HTMLElement;
62
+ setViewportHeight(height: number): void;
63
+ /** Update the viewport width imperatively (also moves the slider thumb). */
64
+ setViewportWidth(px: number): void;
65
+ appendLogEntry(method: string, payload: unknown): void;
66
+ /**
67
+ * Update the host button strip. Called when the mounted plugin invokes a
68
+ * `*.setButtons` host verb so the strip reflects the plugin's real titles.
69
+ */
70
+ applyButtons(buttons: {
71
+ id: string;
72
+ title: string;
73
+ isEnabled?: boolean;
74
+ }[], defaultButtonID?: string | null): void;
75
+ /**
76
+ * Write preset CSS variables to the workbench root and set data-theme-scheme /
77
+ * data-theme-key attributes. Called internally by start() and on theme change;
78
+ * exposed for external callers that need to force a theme update.
79
+ */
80
+ applyTheme(preset: PreviewThemePreset): void;
81
+ /**
82
+ * Trigger the first scenario activation. Must be called by the caller AFTER
83
+ * it has finished destructuring this result and wired up any hooks — calling
84
+ * onActivate inline during renderChrome would access `slotEl` in its TDZ.
85
+ */
86
+ start(): void;
87
+ /** Clean up any window-level listeners (reserved for future use). */
88
+ destroy(): void;
89
+ }
90
+ /**
91
+ * Render the Wire Bench chrome into `root` and return imperative handles.
92
+ *
93
+ * DOM structure:
94
+ * .cbp-wb
95
+ * .cbp-wb__header (brand + controls console)
96
+ * .cbp-wb__rule (separator)
97
+ * .cbp-wb__stage
98
+ * .cbp-wb__workspace (hero — fills left column)
99
+ * .cbp-wb__ws-head
100
+ * .cbp-wb__ws-surface
101
+ * .cbp-wb__specimen-wrap
102
+ * .cbp-wb__card-shell [frozen]
103
+ * .cbp-wb__viewport [frozen]
104
+ * .cbp-wb__webview [frozen slotEl]
105
+ * .cbp-wb__strip [frozen]
106
+ * .cbp-wb__ws-dims
107
+ * .cbp-wb__ws-floor
108
+ * .cbp-wb__dock (sticky right column)
109
+ * .cbp-wb__att.cbp-wb__att--in (Wire Input — inputContainer)
110
+ * .cbp-wb__att.cbp-wb__att--out (Native Calls accordion)
111
+ *
112
+ * @param root - Container element (e.g. document.body or a div).
113
+ * @param scenarios - Full list of scenarios.
114
+ * @param opts - Workbench options (for defaultViewport).
115
+ * @param handlers - `onActivate` fires immediately with the initial scenario and
116
+ * again on scenario/view switch; `onThemeChange` fires when the theme picker
117
+ * changes. Both carry the current preset key.
118
+ */
119
+ export declare function renderChrome(root: HTMLElement, scenarios: PreviewScenario[], opts: Pick<PreviewWorkbenchOptions, 'defaultViewport'>, handlers: ChromeHandlers): ChromeResult;
@@ -0,0 +1,30 @@
1
+ /** Button spec carried by the `*.setButtons` host verbs. */
2
+ export interface FakeHostButton {
3
+ id: string;
4
+ title: string;
5
+ isEnabled?: boolean;
6
+ }
7
+ export interface FakeHostHooks {
8
+ onCall(method: string, payload: unknown): void;
9
+ onSetHeight(height: number): void;
10
+ /**
11
+ * Fired for `attachmentRenderer.setButtons` / `action.setButtons` so the host
12
+ * chrome can reflect the plugin's real button titles. The wire contract does
13
+ * not carry a default button id, so `defaultButtonID` is null unless a payload
14
+ * defensively supplies one.
15
+ */
16
+ onSetButtons(buttons: FakeHostButton[], defaultButtonID: string | null): void;
17
+ }
18
+ /**
19
+ * Install a fake webkit.messageHandlers bridge on `target`.
20
+ *
21
+ * Intercepts all plugin→host calls:
22
+ * - Invokes hooks.onCall(method, payload) for every call.
23
+ * - Invokes hooks.onSetHeight(height) when method === WINDOW_SET_HEIGHT_METHOD.
24
+ * - Invokes hooks.onSetButtons(...) when method ends with `.setButtons`.
25
+ * - Returns per-verb default replies so plugin awaits never deadlock or crash.
26
+ *
27
+ * Handler name and setHeight method name come from generated wire constants —
28
+ * no hardcoded strings here.
29
+ */
30
+ export declare function installFakeHost(target: Window, hooks: FakeHostHooks): void;