@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.
@@ -0,0 +1,1670 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/preview/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ DEFAULT_THEME_KEY: () => DEFAULT_THEME_KEY,
24
+ PLUGIN_CALL_HANDLER_NAME: () => PLUGIN_CALL_HANDLER_NAME,
25
+ THEME_CSS_VAR_NAMES: () => THEME_CSS_VAR_NAMES,
26
+ WINDOW_SET_HEIGHT_METHOD: () => WINDOW_SET_HEIGHT_METHOD,
27
+ applyThemeCssVars: () => applyThemeCssVars,
28
+ clampWidth: () => clampWidth,
29
+ classifyWireSections: () => classifyWireSections,
30
+ computeWirePayloads: () => computeWirePayloads,
31
+ createPreviewWorkbench: () => createPreviewWorkbench,
32
+ defaultDarkTheme: () => defaultDarkTheme,
33
+ defaultLightTheme: () => defaultLightTheme,
34
+ getThemePreset: () => getThemePreset,
35
+ groupScenariosByMode: () => groupScenariosByMode,
36
+ injectWire: () => injectWire,
37
+ installFakeHost: () => installFakeHost,
38
+ parsePayloadJson: () => parsePayloadJson,
39
+ previewThemePresets: () => previewThemePresets,
40
+ resolveViewportWidth: () => resolveViewportWidth
41
+ });
42
+ module.exports = __toCommonJS(index_exports);
43
+
44
+ // src/preview/styles.ts
45
+ var previewStyles = `
46
+ @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap');
47
+
48
+ /* \u2500\u2500 Wire Bench chrome tokens (stable \u2014 independent of native theme preset) \u2500 */
49
+ /* Intentional decoupling: the workbench chrome is a tool, not a specimen. */
50
+ /* The electric violet signal colour (#7B61FF) is chosen to not clash with */
51
+ /* any of the 4 native theme accents (teal / orange / blue / amber). */
52
+ .cbp-wb {
53
+ --bench: #14161D;
54
+ --bench-2: #1A1D26;
55
+ --bench-3: #222634;
56
+ --bench-line: rgba(152,164,196,.12);
57
+ --bench-line-2: rgba(152,164,196,.20);
58
+ --bench-ink: #E8EAF2;
59
+ --bench-ink-dim: #969EB2;
60
+ --bench-ink-faint: #646B7E;
61
+ --bench-signal: #7B61FF;
62
+ --bench-signal-2: #B9A8FF;
63
+ --bench-signal-soft: rgba(123,97,255,.16);
64
+ }
65
+
66
+ /* \u2500\u2500 Card-side theme vars (graphite defaults; applyTheme() overwrites these) \u2500 */
67
+ /* These track the active preset so the specimen card (card-shell / buttons) */
68
+ /* still recolours when the theme picker changes \u2014 native fidelity for the */
69
+ /* card interior. The chrome itself never reads these for its own background. */
70
+ .cbp-wb {
71
+ --cbp-wb-backdrop: #16181C;
72
+ --cbp-wb-accent: #43C6AC;
73
+ --cbp-wb-surface-elevated: rgba(35,37,42,.78);
74
+ --cbp-wb-border: rgba(255,255,255,.10);
75
+ --cbp-wb-text: #F3F4F6;
76
+ --cbp-wb-text-secondary: #C2C6CF;
77
+ }
78
+
79
+ /* \u2500\u2500 Root chrome \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
80
+ /* Background uses --bench (stable), not --cbp-wb-backdrop (theme-driven). */
81
+ /* Blueprint grid is a visual signature of the instrument chrome. */
82
+ .cbp-wb {
83
+ min-height: 100%;
84
+ padding: 28px 26px 34px;
85
+ font-family: "Space Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
86
+ font-size: 14px;
87
+ box-sizing: border-box;
88
+ color: var(--bench-ink);
89
+ background:
90
+ linear-gradient(var(--bench-line) 1px, transparent 1px),
91
+ linear-gradient(90deg, var(--bench-line) 1px, transparent 1px),
92
+ var(--bench);
93
+ background-size: 32px 32px, 32px 32px, auto;
94
+ background-position: -1px -1px, -1px -1px, 0 0;
95
+ -webkit-font-smoothing: antialiased;
96
+ }
97
+
98
+ *, .cbp-wb * {
99
+ box-sizing: border-box;
100
+ }
101
+
102
+ /* \u2500\u2500 Theme-aware scrollbars \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
103
+ /* Restyle every scroll area \u2014 chrome AND plugin content inside the slot \u2014 */
104
+ /* into a thin, translucent bench-toned overlay. Uses bench-ink-dim (stable) */
105
+ /* rather than --cbp-wb-text-secondary so the scrollbar doesn't recolour */
106
+ /* when the theme switches (intentional \u2014 chrome palette is stable). */
107
+ /* The --cbp-wb-* vars still cascade into the plugin slot for its own use. */
108
+ .cbp-wb,
109
+ .cbp-wb * {
110
+ scrollbar-width: thin;
111
+ scrollbar-color: color-mix(in srgb, var(--bench-ink-dim) 30%, transparent) transparent;
112
+ }
113
+
114
+ .cbp-wb ::-webkit-scrollbar {
115
+ width: 9px;
116
+ height: 9px;
117
+ }
118
+
119
+ .cbp-wb ::-webkit-scrollbar-track {
120
+ background: transparent;
121
+ }
122
+
123
+ .cbp-wb ::-webkit-scrollbar-thumb {
124
+ background-color: color-mix(in srgb, var(--bench-ink-dim) 30%, transparent);
125
+ border-radius: 999px;
126
+ border: 2px solid transparent;
127
+ background-clip: padding-box;
128
+ }
129
+
130
+ .cbp-wb ::-webkit-scrollbar-thumb:hover {
131
+ background-color: color-mix(in srgb, var(--bench-ink-dim) 50%, transparent);
132
+ background-clip: padding-box;
133
+ }
134
+
135
+ .cbp-wb ::-webkit-scrollbar-corner {
136
+ background: transparent;
137
+ }
138
+
139
+ /* \u2500\u2500 Header: brand wordmark + controls console \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
140
+ .cbp-wb__header {
141
+ display: flex;
142
+ justify-content: space-between;
143
+ align-items: flex-end;
144
+ gap: 24px;
145
+ flex-wrap: wrap;
146
+ margin-bottom: 16px;
147
+ }
148
+
149
+ .cbp-wb__brand {
150
+ display: flex;
151
+ flex-direction: column;
152
+ gap: 5px;
153
+ }
154
+
155
+ .cbp-wb__brand-sn {
156
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
157
+ font-size: 11.5px;
158
+ letter-spacing: .16em;
159
+ color: var(--bench-ink-faint);
160
+ text-transform: uppercase;
161
+ }
162
+
163
+ .cbp-wb__brand-mark {
164
+ font-size: 28px;
165
+ font-weight: 700;
166
+ letter-spacing: -.01em;
167
+ line-height: 1;
168
+ }
169
+
170
+ .cbp-wb__brand-mark b { color: var(--bench-signal-2); }
171
+ .cbp-wb__brand-mark span { color: var(--bench-ink); }
172
+
173
+ /* \u2500\u2500 Controls console (Surface / Scenario / Theme / Width) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
174
+ .cbp-wb__controls {
175
+ display: flex;
176
+ gap: 11px;
177
+ align-items: flex-end;
178
+ flex-wrap: wrap;
179
+ }
180
+
181
+ .cbp-wb__control {
182
+ display: flex;
183
+ flex-direction: column;
184
+ gap: 6px;
185
+ }
186
+
187
+ .cbp-wb__control-label {
188
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
189
+ font-size: 11px;
190
+ letter-spacing: .14em;
191
+ text-transform: uppercase;
192
+ color: var(--bench-ink-faint);
193
+ }
194
+
195
+ /* Scenario + Theme selects */
196
+ .cbp-wb__control select,
197
+ .cbp-wb__scenario-select {
198
+ background: var(--bench-2);
199
+ border: 1px solid var(--bench-line);
200
+ border-radius: 11px;
201
+ color: var(--bench-ink);
202
+ font: inherit;
203
+ font-size: 13px;
204
+ padding: 8px 13px;
205
+ min-width: 158px;
206
+ }
207
+
208
+ /* Width slider */
209
+ .cbp-wb__width-slider {
210
+ accent-color: var(--bench-signal);
211
+ width: 128px;
212
+ cursor: pointer;
213
+ }
214
+
215
+ /* Current width value shown inline with the label */
216
+ .cbp-wb__width-val {
217
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
218
+ font-size: 12px;
219
+ color: var(--bench-ink-dim);
220
+ }
221
+
222
+ /* \u2500\u2500 Horizontal rule separating header from stage \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
223
+ .cbp-wb__rule {
224
+ height: 1px;
225
+ background: var(--bench-line);
226
+ margin: 0 0 22px;
227
+ }
228
+
229
+ /* \u2500\u2500 Stage: workspace (hero) left + dock right \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
230
+ .cbp-wb__stage {
231
+ display: flex;
232
+ gap: 22px;
233
+ align-items: flex-start;
234
+ }
235
+
236
+ /* \u2500\u2500 Workspace \u2014 hero, fills the left column \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
237
+ .cbp-wb__workspace {
238
+ flex: 1 1 auto;
239
+ min-width: 0;
240
+ display: flex;
241
+ flex-direction: column;
242
+ }
243
+
244
+ .cbp-wb__ws-head {
245
+ display: flex;
246
+ align-items: baseline;
247
+ gap: 13px;
248
+ margin-bottom: 13px;
249
+ }
250
+
251
+ .cbp-wb__ws-kicker {
252
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
253
+ font-size: 12px;
254
+ font-weight: 600;
255
+ letter-spacing: .2em;
256
+ color: var(--bench-signal-2);
257
+ }
258
+
259
+ .cbp-wb__ws-title {
260
+ font-size: 23px;
261
+ font-weight: 700;
262
+ letter-spacing: -.01em;
263
+ }
264
+
265
+ .cbp-wb__ws-sub {
266
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
267
+ font-size: 12px;
268
+ color: var(--bench-ink-faint);
269
+ }
270
+
271
+ /* Mode + scenario chip \u2014 right-aligned in the ws-head */
272
+ .cbp-wb__ws-chip {
273
+ margin-left: auto;
274
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
275
+ font-size: 12px;
276
+ color: var(--bench-ink-dim);
277
+ background: var(--bench-2);
278
+ border: 1px solid var(--bench-line);
279
+ border-radius: 9px;
280
+ padding: 6px 11px;
281
+ white-space: nowrap;
282
+ }
283
+
284
+ /* \u2500\u2500 Workspace surface \u2014 glass stage that holds the specimen card \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
285
+ /* min-height fills the viewport column height (hero). The card is anchored */
286
+ /* at the top; excess space becomes the "growth floor" labelling the 800 cap. */
287
+ .cbp-wb__ws-surface {
288
+ position: relative;
289
+ border: 1px solid var(--bench-line);
290
+ border-radius: 18px;
291
+ background:
292
+ radial-gradient(120% 64% at 50% 0%, var(--bench-signal-soft), transparent 56%),
293
+ var(--bench-2);
294
+ min-height: calc(100vh - 150px);
295
+ padding: 34px 36px 18px;
296
+ display: flex;
297
+ flex-direction: column;
298
+ align-items: center;
299
+ gap: 12px;
300
+ overflow: auto;
301
+ }
302
+
303
+ /* Violet accent line across the top edge \u2014 signal that this is live */
304
+ .cbp-wb__ws-surface::before {
305
+ content: "";
306
+ position: absolute;
307
+ left: 0;
308
+ right: 0;
309
+ top: 0;
310
+ height: 2px;
311
+ background: linear-gradient(90deg, transparent, var(--bench-signal-soft), transparent);
312
+ }
313
+
314
+ /* Small tape label anchored top-left inside the surface */
315
+ .cbp-wb__ws-tape {
316
+ position: absolute;
317
+ top: 13px;
318
+ left: 20px;
319
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
320
+ font-size: 11px;
321
+ letter-spacing: .14em;
322
+ text-transform: uppercase;
323
+ color: var(--bench-ink-faint);
324
+ }
325
+
326
+ /* Wrapper that provides a positioning context for the "card internals" tag */
327
+ .cbp-wb__specimen-wrap {
328
+ position: relative;
329
+ margin-top: 20px;
330
+ }
331
+
332
+ /* Dimensions readout (WxH) below the specimen */
333
+ .cbp-wb__ws-dims {
334
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
335
+ font-size: 12px;
336
+ color: var(--bench-ink-faint);
337
+ letter-spacing: .06em;
338
+ }
339
+
340
+ /* Growth floor \u2014 pushes to the bottom of the surface, labels the 800 ceiling */
341
+ /* Turns the blank space below the card into meaningful information: */
342
+ /* "this gap is the room left before the native height cap". */
343
+ .cbp-wb__ws-floor {
344
+ margin-top: auto;
345
+ width: 100%;
346
+ padding-top: 16px;
347
+ border-top: 1px dashed var(--bench-line-2);
348
+ display: flex;
349
+ justify-content: center;
350
+ gap: 10px;
351
+ align-items: center;
352
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
353
+ font-size: 11.5px;
354
+ letter-spacing: .07em;
355
+ color: var(--bench-ink-faint);
356
+ }
357
+
358
+ .cbp-wb__ws-floor-pip {
359
+ color: var(--bench-signal-2);
360
+ font-size: 13px;
361
+ }
362
+
363
+ /* \u2500\u2500 Dock \u2014 two attachment cards, position:sticky \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
364
+ /* Sticks to the top so the workspace can grow vertically without pushing */
365
+ /* the OUT card out of view. Right column is always in frame. */
366
+ .cbp-wb__dock {
367
+ flex: 0 0 470px;
368
+ position: sticky;
369
+ top: 8px;
370
+ align-self: flex-start;
371
+ max-height: calc(100vh - 54px);
372
+ overflow: auto;
373
+ display: flex;
374
+ flex-direction: column;
375
+ gap: 16px;
376
+ }
377
+
378
+ /* \u2500\u2500 Attachment card (IN and OUT share this shell) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
379
+ .cbp-wb__att {
380
+ position: relative;
381
+ background: var(--bench-2);
382
+ border: 1px solid var(--bench-line);
383
+ border-radius: 18px;
384
+ display: flex;
385
+ flex-direction: column;
386
+ overflow: hidden;
387
+ }
388
+
389
+ .cbp-wb__att-head {
390
+ display: flex;
391
+ align-items: center;
392
+ gap: 11px;
393
+ padding: 14px 17px;
394
+ border-bottom: 1px solid var(--bench-line);
395
+ flex: none;
396
+ }
397
+
398
+ .cbp-wb__att-code {
399
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
400
+ font-size: 12px;
401
+ font-weight: 600;
402
+ letter-spacing: .16em;
403
+ color: var(--bench-signal-2);
404
+ }
405
+
406
+ .cbp-wb__att-title {
407
+ font-size: 15.5px;
408
+ font-weight: 700;
409
+ }
410
+
411
+ .cbp-wb__att-dir {
412
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
413
+ font-size: 11.5px;
414
+ color: var(--bench-ink-faint);
415
+ }
416
+
417
+ .cbp-wb__att-meta {
418
+ margin-left: auto;
419
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
420
+ font-size: 12px;
421
+ color: var(--bench-ink-dim);
422
+ }
423
+
424
+ .cbp-wb__att-scroll {
425
+ overflow: auto;
426
+ padding: 7px 0;
427
+ }
428
+
429
+ .cbp-wb__att--in .cbp-wb__att-scroll { max-height: 474px; }
430
+ .cbp-wb__att--out .cbp-wb__att-scroll { max-height: 320px; }
431
+
432
+ /* \u2500\u2500 Wire Input sections (IN panel \u2014 host\u2192plugin) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
433
+ /* Section dividers; first child has no top border. */
434
+ .cbp-wb__wire-sec {
435
+ border-top: 1px solid var(--bench-line);
436
+ }
437
+
438
+ .cbp-wb__wire-sec:first-child {
439
+ border-top: 0;
440
+ }
441
+
442
+ /* Clickable header row: chevron + key + kind tag + collapsed summary */
443
+ .cbp-wb__wire-sec-head {
444
+ display: flex;
445
+ align-items: center;
446
+ gap: 10px;
447
+ padding: 11px 17px;
448
+ cursor: pointer;
449
+ user-select: none;
450
+ }
451
+
452
+ .cbp-wb__wire-sec-chev {
453
+ font-size: 10px;
454
+ width: 10px;
455
+ color: var(--bench-ink-faint);
456
+ flex: none;
457
+ }
458
+
459
+ .cbp-wb__wire-sec--open .cbp-wb__wire-sec-chev {
460
+ color: var(--bench-signal-2);
461
+ }
462
+
463
+ .cbp-wb__wire-sec-key {
464
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
465
+ font-size: 13px;
466
+ font-weight: 600;
467
+ color: var(--bench-ink);
468
+ }
469
+
470
+ .cbp-wb__wire-sec-tag {
471
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
472
+ font-size: 10.5px;
473
+ color: var(--bench-ink-faint);
474
+ letter-spacing: .04em;
475
+ border: 1px solid var(--bench-line);
476
+ border-radius: 5px;
477
+ padding: 1px 6px;
478
+ }
479
+
480
+ /* Content sections are highlighted \u2014 these are the user-facing payloads */
481
+ .cbp-wb__wire-sec-tag--content {
482
+ color: var(--bench-signal-2);
483
+ border-color: color-mix(in srgb, var(--bench-signal) 35%, transparent);
484
+ }
485
+
486
+ .cbp-wb__wire-sec-sum {
487
+ margin-left: auto;
488
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
489
+ font-size: 11.5px;
490
+ color: var(--bench-ink-faint);
491
+ max-width: 44%;
492
+ overflow: hidden;
493
+ text-overflow: ellipsis;
494
+ white-space: nowrap;
495
+ }
496
+
497
+ /* Collapsible payload body \u2014 hidden until the section is open */
498
+ .cbp-wb__wire-sec-body {
499
+ display: none;
500
+ padding: 0 17px 14px 32px;
501
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
502
+ font-size: 13px;
503
+ line-height: 1.65;
504
+ white-space: pre;
505
+ color: var(--bench-ink);
506
+ overflow-x: auto;
507
+ }
508
+
509
+ .cbp-wb__wire-sec--open .cbp-wb__wire-sec-body {
510
+ display: block;
511
+ }
512
+
513
+ /* \u2500\u2500 Native Calls accordion (OUT panel \u2014 plugin\u2192host) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
514
+ /* Single-open: exactly one entry is expanded at a time. Newest entry auto- */
515
+ /* opens on arrival and collapses all prior entries. Clicking an open entry */
516
+ /* closes it; clicking a closed entry opens it and closes the rest. */
517
+ .cbp-wb__calls {
518
+ display: flex;
519
+ flex-direction: column;
520
+ gap: 8px;
521
+ padding: 11px 15px;
522
+ }
523
+
524
+ /* Empty-state label shown before any calls arrive */
525
+ .cbp-wb__calls-empty {
526
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
527
+ font-size: 12px;
528
+ color: var(--bench-ink-faint);
529
+ padding: 12px 17px;
530
+ font-style: italic;
531
+ }
532
+
533
+ /* .cbp-wb__log-entry \u2014 ONE per native call (class name frozen by tests and */
534
+ /* native fidelity contract). Serves as the accordion item outer shell. */
535
+ .cbp-wb__log-entry {
536
+ background: var(--bench-3);
537
+ border: 1px solid var(--bench-line);
538
+ border-radius: 11px;
539
+ overflow: hidden;
540
+ }
541
+
542
+ /* Open state: signal-tinted border + soft glow */
543
+ .cbp-wb__log-entry--open {
544
+ border-color: color-mix(in srgb, var(--bench-signal) 42%, transparent);
545
+ box-shadow: 0 0 0 1px var(--bench-signal-soft);
546
+ }
547
+
548
+ /* Clickable top row: chevron + sequence number + method name + summary */
549
+ .cbp-wb__log-entry-top {
550
+ display: flex;
551
+ gap: 10px;
552
+ align-items: center;
553
+ padding: 10px 13px;
554
+ cursor: pointer;
555
+ user-select: none;
556
+ }
557
+
558
+ .cbp-wb__log-entry-chev {
559
+ font-size: 10px;
560
+ width: 10px;
561
+ color: var(--bench-ink-faint);
562
+ flex: none;
563
+ }
564
+
565
+ .cbp-wb__log-entry--open .cbp-wb__log-entry-chev {
566
+ color: var(--bench-signal-2);
567
+ }
568
+
569
+ .cbp-wb__log-entry-seq {
570
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
571
+ font-size: 11px;
572
+ color: var(--bench-ink-faint);
573
+ }
574
+
575
+ /* .cbp-wb__log-entry-method \u2014 method name (class name frozen by tests). */
576
+ .cbp-wb__log-entry-method {
577
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
578
+ font-size: 13px;
579
+ font-weight: 600;
580
+ color: var(--bench-signal-2);
581
+ }
582
+
583
+ /* One-line payload summary visible in collapsed state; hidden when expanded */
584
+ .cbp-wb__log-entry-sum {
585
+ margin-left: auto;
586
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
587
+ font-size: 11.5px;
588
+ color: var(--bench-ink-faint);
589
+ max-width: 50%;
590
+ overflow: hidden;
591
+ text-overflow: ellipsis;
592
+ white-space: nowrap;
593
+ }
594
+
595
+ .cbp-wb__log-entry--open .cbp-wb__log-entry-sum {
596
+ display: none;
597
+ }
598
+
599
+ /* Collapsible payload body \u2014 shown only when the entry is open */
600
+ .cbp-wb__log-entry-body {
601
+ display: none;
602
+ padding: 0 13px 12px 32px;
603
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
604
+ font-size: 12.5px;
605
+ line-height: 1.6;
606
+ white-space: pre;
607
+ color: var(--bench-ink);
608
+ overflow-x: auto;
609
+ }
610
+
611
+ .cbp-wb__log-entry--open .cbp-wb__log-entry-body {
612
+ display: block;
613
+ }
614
+
615
+ /* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
616
+ /* FROZEN SPECIMEN GEOMETRY \u2014 pixel-identical to HEAD (native measurement */
617
+ /* parity). Class names, padding, radius, border, and overflow rules are all */
618
+ /* verbatim from the prior implementation. Only the surrounding layout */
619
+ /* changed (canvas \u2192 stage / workspace). DO NOT adjust values below. */
620
+ /* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
621
+
622
+ /* Body clip box. NO border-radius of its own \u2014 the card shell (its parent)
623
+ owns the rounding. A radius here would clip the card's own corners/border. */
624
+ .cbp-wb__viewport {
625
+ width: 100%;
626
+ /* Native hard ceiling (AttachmentRenderHeightConstants.ceiling = 800): the
627
+ body never grows past this even under the 'auto' content-driven policy. */
628
+ max-height: 800px;
629
+ /* At the ceiling, content SCROLLS inside the (fixed) card chrome \u2014 it does
630
+ not clip away or push the card taller. */
631
+ overflow-y: auto;
632
+ overflow-x: hidden;
633
+ transition: height 0.1s ease;
634
+ }
635
+
636
+ /* Native card shell: 10 pt padding / 7 pt radius / 1 pt accent border / elevated bg */
637
+ /* Geometry source: AttachmentRenderCardShell.swift:24-59 */
638
+ /* Outer card stacks the body (viewport) above the host action strip: */
639
+ /* native VStack { body; actions }. Width is set inline; height auto-wraps. */
640
+ .cbp-wb__card-shell {
641
+ display: flex;
642
+ flex-direction: column;
643
+ gap: 8px; /* native cardSpacing: body \u2194 action strip */
644
+ padding: 10px; /* native cardPadding */
645
+ border-radius: 7px; /* native radius.panelInner */
646
+ /* Border tint = the attachment's tintColor (--cbp-wb-card-tint, set per
647
+ scenario from scenario.accentHex), falling back to the theme accent \u2014
648
+ mirrors native, where the card border uses record.tintColor. */
649
+ border: 1px solid color-mix(in srgb, var(--cbp-wb-card-tint, var(--cbp-wb-accent)) 26%, transparent);
650
+ background: var(--cbp-wb-surface-elevated);
651
+ /* No box-shadow per native spec */
652
+ }
653
+
654
+ .cbp-wb__webview {
655
+ width: 100%;
656
+ height: 100%;
657
+ /* Fixed/bounded body shorter than its content \u2192 scroll inside. (Only the one
658
+ element whose content actually exceeds its box scrolls, so there is never a
659
+ double scrollbar: in 'auto' the viewport scrolls; in fixed/bounded the
660
+ webview does.) */
661
+ overflow-y: auto;
662
+ overflow-x: hidden;
663
+ }
664
+
665
+ /* Host button strip (inside the card, below the body) */
666
+ /* Native action row: HStack { buttons; Spacer } \u2192 left-aligned, compact. */
667
+ .cbp-wb__strip {
668
+ display: flex;
669
+ flex-wrap: wrap;
670
+ gap: 10px; /* native action HStack spacing */
671
+ justify-content: flex-start; /* left-aligned, not centered */
672
+ }
673
+
674
+ /* No buttons \u2192 no strip box, and the card's flex gap reserves nothing. */
675
+ .cbp-wb__strip:empty {
676
+ display: none;
677
+ }
678
+
679
+ .cbp-wb__button {
680
+ appearance: none;
681
+ border: 1px solid var(--cbp-wb-border);
682
+ border-radius: 999px;
683
+ padding: 4px 10px; /* native actionPadding \u2248 3 v / 8 h */
684
+ background: color-mix(in srgb, var(--cbp-wb-surface-elevated) 60%, transparent);
685
+ color: var(--cbp-wb-text-secondary);
686
+ font-size: 12px;
687
+ font-weight: 600;
688
+ font-family: inherit;
689
+ cursor: pointer;
690
+ }
691
+
692
+ .cbp-wb__button--primary {
693
+ background: var(--cbp-wb-accent);
694
+ color: var(--cbp-wb-backdrop);
695
+ border-color: var(--cbp-wb-accent);
696
+ }
697
+
698
+ /* \u2500\u2500 Responsive reflow \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
699
+ /* The side-by-side stage assumes a wide display: the renderer card runs up to
700
+ 900px and must sit beside the 470px dock. Below ~1335px the card would clip
701
+ against the dock. Stack instead \u2014 workspace on top at FULL width (the
702
+ specimen is never clipped), dock below as a row of the two attachment cards.
703
+ The dock drops its sticky behaviour here (it's no longer a side rail). */
704
+ @media (max-width: 1335px) {
705
+ .cbp-wb__stage {
706
+ flex-direction: column;
707
+ }
708
+ /* Surface no longer needs to fill the viewport column \u2014 let it wrap the card
709
+ so the dock below is reachable without a long scroll past empty space. */
710
+ .cbp-wb__ws-surface {
711
+ min-height: 420px;
712
+ }
713
+ .cbp-wb__dock {
714
+ position: static;
715
+ flex: none;
716
+ width: 100%;
717
+ max-height: none;
718
+ flex-direction: row;
719
+ align-items: flex-start;
720
+ }
721
+ .cbp-wb__att {
722
+ flex: 1 1 0;
723
+ min-width: 0;
724
+ }
725
+ }
726
+ `;
727
+
728
+ // src/preview/theme.ts
729
+ var previewThemePresets = [
730
+ // ── Graphite · default dark ────────────────────────────────────────────────
731
+ {
732
+ key: "graphite",
733
+ label: "Graphite (Dark)",
734
+ scheme: "dark",
735
+ backdrop: "#16181C",
736
+ tokens: {
737
+ surface: "rgba(255,255,255,.08)",
738
+ surfaceElevated: "rgba(35,37,42,.78)",
739
+ textPrimary: "#F3F4F6",
740
+ textSecondary: "#C2C6CF",
741
+ textTertiary: "#8D95A3",
742
+ accent: "#43C6AC",
743
+ accentContrast: "#081411",
744
+ border: "rgba(255,255,255,.10)",
745
+ divider: "rgba(255,255,255,.14)",
746
+ success: "#22C55E",
747
+ warning: "#F59E0B",
748
+ danger: "#EF4444"
749
+ }
750
+ },
751
+ // ── Ember · dark warm ─────────────────────────────────────────────────────
752
+ {
753
+ key: "ember",
754
+ label: "Ember (Dark)",
755
+ scheme: "dark",
756
+ backdrop: "#1C120C",
757
+ tokens: {
758
+ surface: "rgba(58,36,26,.32)",
759
+ surfaceElevated: "rgba(43,28,21,.90)",
760
+ textPrimary: "#F7E7D8",
761
+ textSecondary: "#D6B89E",
762
+ textTertiary: "#9E7F69",
763
+ accent: "#F97316",
764
+ accentContrast: "#26160E",
765
+ border: "rgba(240,201,164,.10)",
766
+ divider: "rgba(240,201,164,.14)",
767
+ success: "#22C55E",
768
+ warning: "#F59E0B",
769
+ danger: "#EF4444"
770
+ }
771
+ },
772
+ // ── Porcelain · default light ─────────────────────────────────────────────
773
+ {
774
+ key: "porcelain",
775
+ label: "Porcelain (Light)",
776
+ scheme: "light",
777
+ backdrop: "#ECECEE",
778
+ tokens: {
779
+ surface: "rgba(255,255,255,.68)",
780
+ surfaceElevated: "rgba(249,251,254,.96)",
781
+ textPrimary: "#1F2A37",
782
+ textSecondary: "#4B5C6E",
783
+ textTertiary: "#7A8A99",
784
+ accent: "#2563EB",
785
+ accentContrast: "#FFFFFF",
786
+ border: "rgba(116,135,155,.16)",
787
+ divider: "rgba(116,135,155,.22)",
788
+ success: "#059669",
789
+ warning: "#D97706",
790
+ danger: "#DC2626"
791
+ }
792
+ },
793
+ // ── Sand · light warm ─────────────────────────────────────────────────────
794
+ {
795
+ key: "sand",
796
+ label: "Sand (Light)",
797
+ scheme: "light",
798
+ backdrop: "#EFE9DD",
799
+ tokens: {
800
+ surface: "rgba(255,255,255,.56)",
801
+ surfaceElevated: "rgba(247,241,232,.88)",
802
+ textPrimary: "#2F2419",
803
+ textSecondary: "#5F5246",
804
+ textTertiary: "#8A7C6F",
805
+ accent: "#D97706",
806
+ accentContrast: "#2B2117",
807
+ border: "rgba(142,126,106,.18)",
808
+ divider: "rgba(142,126,106,.24)",
809
+ success: "#15803D",
810
+ warning: "#CA8A04",
811
+ danger: "#DC2626"
812
+ }
813
+ }
814
+ ];
815
+ var DEFAULT_THEME_KEY = "graphite";
816
+ function getThemePreset(key) {
817
+ if (key === "light") return previewThemePresets[2];
818
+ if (!key || key === "dark") return previewThemePresets[0];
819
+ return previewThemePresets.find((p) => p.key === key) ?? previewThemePresets[0];
820
+ }
821
+ var defaultDarkTheme = {
822
+ scheme: "dark",
823
+ tokens: previewThemePresets[0].tokens
824
+ };
825
+ var defaultLightTheme = {
826
+ scheme: "light",
827
+ tokens: previewThemePresets[2].tokens
828
+ };
829
+
830
+ // src/preview/chrome.ts
831
+ var MODE_WIDTHS = {
832
+ attachmentRenderer: { width: 720, min: 560, max: 900 },
833
+ action: { width: 350, min: 300, max: 500 }
834
+ };
835
+ var RENDERER_HEIGHT_CEILING = 800;
836
+ function groupScenariosByMode(scenarios) {
837
+ const map = /* @__PURE__ */ new Map();
838
+ for (const s of scenarios) {
839
+ let group = map.get(s.mode);
840
+ if (!group) {
841
+ group = [];
842
+ map.set(s.mode, group);
843
+ }
844
+ group.push(s);
845
+ }
846
+ return Array.from(map.entries()).map(([mode, list]) => ({ mode, scenarios: list }));
847
+ }
848
+ function resolveViewportWidth(scenario) {
849
+ const def = MODE_WIDTHS[scenario.mode] ?? MODE_WIDTHS.attachmentRenderer;
850
+ return {
851
+ width: scenario.viewport?.width ?? def.width,
852
+ min: scenario.viewport?.widthMin ?? def.min,
853
+ max: scenario.viewport?.widthMax ?? def.max
854
+ };
855
+ }
856
+ function clampWidth(value, min, max) {
857
+ return Math.min(max, Math.max(min, value));
858
+ }
859
+ function renderChrome(root, scenarios, opts, handlers) {
860
+ const query = new URLSearchParams(
861
+ typeof window !== "undefined" ? window.location.search : ""
862
+ );
863
+ const initialView = query.get("view") === "action" ? "action" : "attachmentRenderer";
864
+ let selectedView = initialView;
865
+ const rawThemeKey = query.get("theme") ?? DEFAULT_THEME_KEY;
866
+ let selectedThemeKey = getThemePreset(rawThemeKey).key;
867
+ const rendererScenarios = scenarios.filter((s) => s.mode === "attachmentRenderer");
868
+ const actionScenarios = scenarios.filter((s) => s.mode === "action");
869
+ function scenariosForView(view) {
870
+ return view === "attachmentRenderer" ? rendererScenarios : actionScenarios;
871
+ }
872
+ function resolveInitialScenarioID(view) {
873
+ const requested = query.get("scenario");
874
+ const options = scenariosForView(view);
875
+ if (requested && options.some((s) => s.id === requested)) return requested;
876
+ return options[0]?.id ?? "";
877
+ }
878
+ let selectedScenarioID = resolveInitialScenarioID(initialView);
879
+ let currentWidth = MODE_WIDTHS[initialView].width;
880
+ function activeScenario() {
881
+ const list = scenariosForView(selectedView);
882
+ return list.find((s) => s.id === selectedScenarioID) ?? list[0];
883
+ }
884
+ root.innerHTML = "";
885
+ const wb = div("cbp-wb");
886
+ root.appendChild(wb);
887
+ const header = div("cbp-wb__header");
888
+ wb.appendChild(header);
889
+ const brand = div("cbp-wb__brand");
890
+ const brandSn = document.createElement("div");
891
+ brandSn.className = "cbp-wb__brand-sn";
892
+ brandSn.textContent = "@clipbus/plugin-sdk \xB7 preview";
893
+ const brandMark = document.createElement("div");
894
+ brandMark.className = "cbp-wb__brand-mark";
895
+ brandMark.innerHTML = "<span>WIRE</span> <b>BENCH</b>";
896
+ brand.appendChild(brandSn);
897
+ brand.appendChild(brandMark);
898
+ header.appendChild(brand);
899
+ const controls = div("cbp-wb__controls");
900
+ header.appendChild(controls);
901
+ const viewOptions = [
902
+ { value: "attachmentRenderer", label: "Renderer" },
903
+ { value: "action", label: "Action" }
904
+ ];
905
+ const viewSelect = buildSelect("cbp-wb-view", "Surface", viewOptions, selectedView, controls);
906
+ const scenarioAreaWrapper = div("cbp-wb__control");
907
+ controls.appendChild(scenarioAreaWrapper);
908
+ const themeOptions = previewThemePresets.map((p) => ({ value: p.key, label: p.label }));
909
+ const themeSelect = buildSelect(
910
+ "cbp-wb-theme",
911
+ "Theme",
912
+ themeOptions,
913
+ selectedThemeKey,
914
+ controls
915
+ );
916
+ const widthControl = div("cbp-wb__control");
917
+ const widthLabel = document.createElement("label");
918
+ widthLabel.className = "cbp-wb__control-label";
919
+ widthLabel.htmlFor = "cbp-wb-width";
920
+ widthLabel.textContent = "Width ";
921
+ const widthVal = document.createElement("span");
922
+ widthVal.className = "cbp-wb__width-val";
923
+ widthVal.textContent = `${currentWidth}px`;
924
+ widthLabel.appendChild(widthVal);
925
+ widthControl.appendChild(widthLabel);
926
+ const widthSlider = document.createElement("input");
927
+ widthSlider.type = "range";
928
+ widthSlider.id = "cbp-wb-width";
929
+ widthSlider.name = "cbp-wb-width";
930
+ widthSlider.className = "cbp-wb__width-slider";
931
+ widthControl.appendChild(widthSlider);
932
+ controls.appendChild(widthControl);
933
+ const rule = div("cbp-wb__rule");
934
+ wb.appendChild(rule);
935
+ const stage = div("cbp-wb__stage");
936
+ wb.appendChild(stage);
937
+ const workspace = div("cbp-wb__workspace");
938
+ stage.appendChild(workspace);
939
+ const wsHead = div("cbp-wb__ws-head");
940
+ const wsKicker = span("cbp-wb__ws-kicker");
941
+ wsKicker.textContent = "PREVIEW";
942
+ const wsTitle = span("cbp-wb__ws-title");
943
+ wsTitle.textContent = "Workspace";
944
+ const wsSub = span("cbp-wb__ws-sub");
945
+ wsSub.textContent = "native-fidelity \xB7 grows with setHeight";
946
+ const wsChip = span("cbp-wb__ws-chip");
947
+ wsHead.appendChild(wsKicker);
948
+ wsHead.appendChild(wsTitle);
949
+ wsHead.appendChild(wsSub);
950
+ wsHead.appendChild(wsChip);
951
+ workspace.appendChild(wsHead);
952
+ const wsSurface = div("cbp-wb__ws-surface");
953
+ workspace.appendChild(wsSurface);
954
+ const wsTape = div("cbp-wb__ws-tape");
955
+ wsTape.textContent = "device under preview \xB7 specimen";
956
+ wsSurface.appendChild(wsTape);
957
+ const specimenWrap = div("cbp-wb__specimen-wrap");
958
+ wsSurface.appendChild(specimenWrap);
959
+ const cardShell = div("cbp-wb__card-shell");
960
+ specimenWrap.appendChild(cardShell);
961
+ const viewport = div("cbp-wb__viewport");
962
+ cardShell.appendChild(viewport);
963
+ const webview = div("cbp-wb__webview");
964
+ viewport.appendChild(webview);
965
+ const slotEl = webview;
966
+ const strip = div("cbp-wb__strip");
967
+ cardShell.appendChild(strip);
968
+ const wsDims = div("cbp-wb__ws-dims");
969
+ wsSurface.appendChild(wsDims);
970
+ const wsFloor = div("cbp-wb__ws-floor");
971
+ const wsFloorPip = span("cbp-wb__ws-floor-pip");
972
+ wsFloorPip.textContent = "\u2195";
973
+ wsFloor.appendChild(wsFloorPip);
974
+ const wsFloorText = document.createTextNode(
975
+ " height follows the plugin \xB7 native ceiling \u2248 800px \xB7 content usually fits one screen"
976
+ );
977
+ wsFloor.appendChild(wsFloorText);
978
+ wsSurface.appendChild(wsFloor);
979
+ const dock = div("cbp-wb__dock");
980
+ stage.appendChild(dock);
981
+ const inAtt = div("cbp-wb__att cbp-wb__att--in");
982
+ dock.appendChild(inAtt);
983
+ const inHead = div("cbp-wb__att-head");
984
+ const inCode = span("cbp-wb__att-code");
985
+ inCode.textContent = "IN";
986
+ const inTitle = span("cbp-wb__att-title");
987
+ inTitle.textContent = "Wire Input";
988
+ const inDir = span("cbp-wb__att-dir");
989
+ inDir.textContent = "host \u2192 plugin";
990
+ const inMeta = span("cbp-wb__att-meta");
991
+ inMeta.textContent = "5 keys";
992
+ inHead.appendChild(inCode);
993
+ inHead.appendChild(inTitle);
994
+ inHead.appendChild(inDir);
995
+ inHead.appendChild(inMeta);
996
+ inAtt.appendChild(inHead);
997
+ const inputContainer = div("cbp-wb__att-scroll");
998
+ inAtt.appendChild(inputContainer);
999
+ const outAtt = div("cbp-wb__att cbp-wb__att--out");
1000
+ dock.appendChild(outAtt);
1001
+ const outHead = div("cbp-wb__att-head");
1002
+ const outCode = span("cbp-wb__att-code");
1003
+ outCode.textContent = "OUT";
1004
+ const outTitle = span("cbp-wb__att-title");
1005
+ outTitle.textContent = "Native Calls";
1006
+ const outDir = span("cbp-wb__att-dir");
1007
+ outDir.textContent = "plugin \u2192 host";
1008
+ const outMeta = span("cbp-wb__att-meta");
1009
+ outMeta.textContent = "0 calls";
1010
+ outHead.appendChild(outCode);
1011
+ outHead.appendChild(outTitle);
1012
+ outHead.appendChild(outDir);
1013
+ outHead.appendChild(outMeta);
1014
+ outAtt.appendChild(outHead);
1015
+ const outScroll = div("cbp-wb__att-scroll");
1016
+ outAtt.appendChild(outScroll);
1017
+ const callsEmpty = document.createElement("p");
1018
+ callsEmpty.className = "cbp-wb__calls-empty";
1019
+ callsEmpty.textContent = "No calls yet.";
1020
+ outScroll.appendChild(callsEmpty);
1021
+ const callsList = div("cbp-wb__calls");
1022
+ outScroll.appendChild(callsList);
1023
+ let logSeq = 0;
1024
+ function applyTheme(preset) {
1025
+ wb.style.setProperty("--cbp-wb-accent", preset.tokens.accent);
1026
+ wb.style.setProperty("--cbp-wb-surface-elevated", preset.tokens.surfaceElevated);
1027
+ wb.style.setProperty("--cbp-wb-border", preset.tokens.border);
1028
+ wb.style.setProperty("--cbp-wb-text", preset.tokens.textPrimary);
1029
+ wb.style.setProperty("--cbp-wb-text-secondary", preset.tokens.textSecondary);
1030
+ wb.style.setProperty("--cbp-wb-backdrop", preset.backdrop);
1031
+ wb.dataset["themeScheme"] = preset.scheme;
1032
+ wb.dataset["themeKey"] = preset.key;
1033
+ }
1034
+ function currentBodyHeightLabel() {
1035
+ const inline = parseInt(viewport.style.height, 10);
1036
+ if (!Number.isNaN(inline)) return String(inline);
1037
+ const rendered = Math.round(viewport.getBoundingClientRect().height);
1038
+ return rendered > 0 ? String(rendered) : "auto";
1039
+ }
1040
+ function setViewportWidth(px) {
1041
+ currentWidth = px;
1042
+ cardShell.style.width = `${px}px`;
1043
+ widthVal.textContent = `${px}px`;
1044
+ widthSlider.value = String(px);
1045
+ wsDims.textContent = `${px} \xD7 ${currentBodyHeightLabel()}`;
1046
+ }
1047
+ function updateWidthSlider(min, max, value) {
1048
+ widthSlider.min = String(min);
1049
+ widthSlider.max = String(max);
1050
+ widthSlider.value = String(value);
1051
+ }
1052
+ function applyInitialHeight(scenario) {
1053
+ const policy = scenario.viewport?.heightPolicy ?? "auto";
1054
+ if (policy === "fixed" && scenario.viewport?.height != null) {
1055
+ const h = Math.min(scenario.viewport.height, RENDERER_HEIGHT_CEILING);
1056
+ viewport.style.height = `${h}px`;
1057
+ } else {
1058
+ viewport.style.height = "";
1059
+ }
1060
+ }
1061
+ function setViewportHeight(height) {
1062
+ const scenario = activeScenario();
1063
+ const policy = scenario.viewport?.heightPolicy ?? "auto";
1064
+ if (policy === "fixed") return;
1065
+ let clamped = Math.min(height, RENDERER_HEIGHT_CEILING);
1066
+ if (policy === "bounded") {
1067
+ const min = scenario.viewport?.min ?? 0;
1068
+ const max = Math.min(scenario.viewport?.max ?? Infinity, RENDERER_HEIGHT_CEILING);
1069
+ clamped = Math.min(max, Math.max(min, height));
1070
+ }
1071
+ viewport.style.height = `${clamped}px`;
1072
+ wsDims.textContent = `${currentWidth} \xD7 ${clamped}`;
1073
+ }
1074
+ function appendLogEntry(method, payload) {
1075
+ callsEmpty.style.display = "none";
1076
+ logSeq += 1;
1077
+ let payloadText;
1078
+ try {
1079
+ payloadText = JSON.stringify(payload, null, 2);
1080
+ } catch {
1081
+ payloadText = String(payload);
1082
+ }
1083
+ let summaryText;
1084
+ try {
1085
+ const compact = JSON.stringify(payload);
1086
+ summaryText = compact && compact.length > 60 ? compact.slice(0, 57) + "\u2026" : compact ?? "\u2014";
1087
+ } catch {
1088
+ summaryText = String(payload);
1089
+ }
1090
+ const entry = document.createElement("div");
1091
+ entry.className = "cbp-wb__log-entry cbp-wb__log-entry--open";
1092
+ const top = document.createElement("div");
1093
+ top.className = "cbp-wb__log-entry-top";
1094
+ const chev = span("cbp-wb__log-entry-chev");
1095
+ chev.textContent = "\u25BC";
1096
+ const seqEl = span("cbp-wb__log-entry-seq");
1097
+ seqEl.textContent = `#${logSeq}`;
1098
+ const methodSpan = span("cbp-wb__log-entry-method");
1099
+ methodSpan.textContent = method;
1100
+ const sumEl = span("cbp-wb__log-entry-sum");
1101
+ sumEl.textContent = summaryText;
1102
+ top.appendChild(chev);
1103
+ top.appendChild(seqEl);
1104
+ top.appendChild(methodSpan);
1105
+ top.appendChild(sumEl);
1106
+ const body = document.createElement("div");
1107
+ body.className = "cbp-wb__log-entry-body";
1108
+ body.textContent = payloadText;
1109
+ entry.appendChild(top);
1110
+ entry.appendChild(body);
1111
+ collapseAllLogEntries();
1112
+ callsList.insertBefore(entry, callsList.firstChild);
1113
+ outMeta.textContent = `${logSeq} call${logSeq === 1 ? "" : "s"}`;
1114
+ top.addEventListener("click", () => {
1115
+ const isOpen = entry.classList.contains("cbp-wb__log-entry--open");
1116
+ if (isOpen) {
1117
+ entry.classList.remove("cbp-wb__log-entry--open");
1118
+ chev.textContent = "\u25B6";
1119
+ } else {
1120
+ collapseAllLogEntries();
1121
+ entry.classList.add("cbp-wb__log-entry--open");
1122
+ chev.textContent = "\u25BC";
1123
+ }
1124
+ });
1125
+ }
1126
+ function collapseAllLogEntries() {
1127
+ for (const el of callsList.querySelectorAll(".cbp-wb__log-entry")) {
1128
+ el.classList.remove("cbp-wb__log-entry--open");
1129
+ const c = el.querySelector(".cbp-wb__log-entry-chev");
1130
+ if (c) c.textContent = "\u25B6";
1131
+ }
1132
+ }
1133
+ let activeButtons = [];
1134
+ let activeDefaultButtonID = null;
1135
+ function renderButtons() {
1136
+ strip.innerHTML = "";
1137
+ for (const btn of activeButtons) {
1138
+ const button = document.createElement("button");
1139
+ button.type = "button";
1140
+ button.className = "cbp-wb__button" + (btn.id === activeDefaultButtonID ? " cbp-wb__button--primary" : "");
1141
+ button.textContent = btn.title;
1142
+ if (btn.isEnabled === false) {
1143
+ button.disabled = true;
1144
+ }
1145
+ button.addEventListener("click", () => {
1146
+ const scenario = activeScenario();
1147
+ const eventName = scenario.mode === "action" ? "clipbus-plugin-action-host-invoke" : "clipbus-plugin-attachment-host-invoke";
1148
+ window.dispatchEvent(
1149
+ new CustomEvent(eventName, { detail: { buttonID: btn.id } })
1150
+ );
1151
+ });
1152
+ strip.appendChild(button);
1153
+ }
1154
+ }
1155
+ function applyButtons(buttons, defaultButtonID) {
1156
+ activeButtons = Array.isArray(buttons) ? buttons.map((b) => ({ id: b.id, title: b.title, isEnabled: b.isEnabled })) : [];
1157
+ activeDefaultButtonID = defaultButtonID ?? null;
1158
+ renderButtons();
1159
+ }
1160
+ function applyScenarioButtons(scenario) {
1161
+ applyButtons(scenario.buttons ?? [], scenario.defaultButtonID ?? null);
1162
+ }
1163
+ function refreshScenarioArea() {
1164
+ scenarioAreaWrapper.innerHTML = "";
1165
+ const list = scenariosForView(selectedView);
1166
+ if (!list.some((s) => s.id === selectedScenarioID)) {
1167
+ selectedScenarioID = list[0]?.id ?? "";
1168
+ }
1169
+ const lbl = document.createElement("label");
1170
+ lbl.className = "cbp-wb__control-label";
1171
+ lbl.htmlFor = "cbp-wb-scenario";
1172
+ lbl.textContent = "Scenario";
1173
+ scenarioAreaWrapper.appendChild(lbl);
1174
+ const select = document.createElement("select");
1175
+ select.id = "cbp-wb-scenario";
1176
+ select.name = "cbp-wb-scenario";
1177
+ select.className = "cbp-wb__scenario-select";
1178
+ for (const s of list) {
1179
+ const opt = document.createElement("option");
1180
+ opt.value = s.id;
1181
+ opt.textContent = s.label;
1182
+ select.appendChild(opt);
1183
+ }
1184
+ select.value = selectedScenarioID;
1185
+ select.addEventListener("change", () => {
1186
+ selectedScenarioID = select.value;
1187
+ activateCurrentScenario();
1188
+ });
1189
+ scenarioAreaWrapper.appendChild(select);
1190
+ }
1191
+ function updateWorkspaceHead() {
1192
+ const scenario = activeScenario();
1193
+ const modeLabel = scenario.mode === "attachmentRenderer" ? "attachmentRenderer" : "action";
1194
+ wsChip.textContent = `${modeLabel} \xB7 ${scenario.label}`;
1195
+ wsDims.textContent = `${currentWidth} \xD7 ${currentBodyHeightLabel()}`;
1196
+ }
1197
+ function activateCurrentScenario() {
1198
+ const scenario = activeScenario();
1199
+ if (!scenario) return;
1200
+ if (scenario.accentHex) {
1201
+ cardShell.style.setProperty("--cbp-wb-card-tint", scenario.accentHex);
1202
+ } else {
1203
+ cardShell.style.removeProperty("--cbp-wb-card-tint");
1204
+ }
1205
+ const { width, min, max } = resolveViewportWidth(scenario);
1206
+ const initialWidth = clampWidth(
1207
+ scenario.viewport?.width ?? opts.defaultViewport?.width ?? width,
1208
+ min,
1209
+ max
1210
+ );
1211
+ setViewportWidth(initialWidth);
1212
+ updateWidthSlider(min, max, initialWidth);
1213
+ applyInitialHeight(scenario);
1214
+ applyScenarioButtons(scenario);
1215
+ updateWorkspaceHead();
1216
+ syncQuery();
1217
+ handlers.onActivate(scenario, selectedThemeKey);
1218
+ }
1219
+ function syncQuery() {
1220
+ if (typeof window === "undefined") return;
1221
+ const next = new URL(window.location.href);
1222
+ next.searchParams.set("view", selectedView);
1223
+ next.searchParams.set("scenario", selectedScenarioID);
1224
+ next.searchParams.set("theme", selectedThemeKey);
1225
+ window.history.replaceState({}, "", next.toString());
1226
+ }
1227
+ viewSelect.addEventListener("change", () => {
1228
+ selectedView = viewSelect.value;
1229
+ refreshScenarioArea();
1230
+ activateCurrentScenario();
1231
+ });
1232
+ themeSelect.addEventListener("change", () => {
1233
+ selectedThemeKey = themeSelect.value;
1234
+ const preset = getThemePreset(selectedThemeKey);
1235
+ applyTheme(preset);
1236
+ syncQuery();
1237
+ handlers.onThemeChange(activeScenario(), selectedThemeKey);
1238
+ });
1239
+ widthSlider.addEventListener("input", () => {
1240
+ const { min, max } = resolveViewportWidth(activeScenario());
1241
+ const clamped = clampWidth(parseInt(widthSlider.value, 10), min, max);
1242
+ setViewportWidth(clamped);
1243
+ });
1244
+ function start() {
1245
+ refreshScenarioArea();
1246
+ applyTheme(getThemePreset(selectedThemeKey));
1247
+ activateCurrentScenario();
1248
+ }
1249
+ function destroy() {
1250
+ }
1251
+ return {
1252
+ slotEl,
1253
+ inputContainer,
1254
+ setViewportHeight,
1255
+ setViewportWidth,
1256
+ appendLogEntry,
1257
+ applyButtons,
1258
+ applyTheme,
1259
+ start,
1260
+ destroy
1261
+ };
1262
+ }
1263
+ function div(className) {
1264
+ const el = document.createElement("div");
1265
+ if (className) el.className = className;
1266
+ return el;
1267
+ }
1268
+ function span(className) {
1269
+ const el = document.createElement("span");
1270
+ if (className) el.className = className;
1271
+ return el;
1272
+ }
1273
+ function buildSelect(id, labelText, options, initialValue, container) {
1274
+ const wrapper = div("cbp-wb__control");
1275
+ const labelEl = document.createElement("label");
1276
+ labelEl.className = "cbp-wb__control-label";
1277
+ labelEl.textContent = labelText;
1278
+ labelEl.htmlFor = id;
1279
+ wrapper.appendChild(labelEl);
1280
+ const select = document.createElement("select");
1281
+ select.id = id;
1282
+ select.name = id;
1283
+ for (const opt of options) {
1284
+ const option = document.createElement("option");
1285
+ option.value = opt.value;
1286
+ option.textContent = opt.label;
1287
+ select.appendChild(option);
1288
+ }
1289
+ select.value = initialValue;
1290
+ wrapper.appendChild(select);
1291
+ container.appendChild(wrapper);
1292
+ return select;
1293
+ }
1294
+
1295
+ // src/generated/wireConstants.generated.ts
1296
+ var PLUGIN_CALL_HANDLER_NAME = "clipbusPluginCall";
1297
+ var WINDOW_SET_HEIGHT_METHOD = "window.setHeight";
1298
+
1299
+ // src/preview/fakeHost.ts
1300
+ function installFakeHost(target, hooks) {
1301
+ const t = target;
1302
+ if (!t["webkit"]) {
1303
+ t["webkit"] = {};
1304
+ }
1305
+ const webkit = t["webkit"];
1306
+ if (!webkit["messageHandlers"]) {
1307
+ webkit["messageHandlers"] = {};
1308
+ }
1309
+ const handlers = webkit["messageHandlers"];
1310
+ handlers[PLUGIN_CALL_HANDLER_NAME] = {
1311
+ postMessage: async (msg) => {
1312
+ const { method, payload } = msg;
1313
+ hooks.onCall(method, payload);
1314
+ if (method === WINDOW_SET_HEIGHT_METHOD) {
1315
+ const height = payload?.height;
1316
+ if (typeof height === "number") {
1317
+ hooks.onSetHeight(height);
1318
+ }
1319
+ return defaultReplyFor(method);
1320
+ }
1321
+ if (method.endsWith(".setButtons")) {
1322
+ const p = payload ?? {};
1323
+ hooks.onSetButtons(
1324
+ Array.isArray(p.buttons) ? p.buttons : [],
1325
+ p.defaultButtonID ?? null
1326
+ );
1327
+ return defaultReplyFor(method);
1328
+ }
1329
+ return defaultReplyFor(method);
1330
+ }
1331
+ };
1332
+ }
1333
+ function defaultReplyFor(method) {
1334
+ if (method === "runtime.invoke") {
1335
+ return { response: void 0 };
1336
+ }
1337
+ return {};
1338
+ }
1339
+
1340
+ // src/generated/hostBootstrap.generated.ts
1341
+ function emitAttachmentBootstrap(p) {
1342
+ if (p.context !== void 0) {
1343
+ globalThis["__CLIPBUS_PLUGIN_CONTEXT__"] = p.context;
1344
+ globalThis.dispatchEvent(new CustomEvent("clipbus-plugin-context", { detail: p.context }));
1345
+ }
1346
+ if (p.item !== void 0) {
1347
+ globalThis["__CLIPBUS_PLUGIN_ITEM__"] = p.item;
1348
+ globalThis.dispatchEvent(new CustomEvent("clipbus-plugin-item", { detail: p.item }));
1349
+ }
1350
+ if (p.attachment !== void 0) {
1351
+ globalThis["__CLIPBUS_PLUGIN_ATTACHMENT__"] = p.attachment;
1352
+ globalThis.dispatchEvent(new CustomEvent("clipbus-plugin-attachment", { detail: p.attachment }));
1353
+ }
1354
+ if (p.theme !== void 0) {
1355
+ globalThis["__CLIPBUS_PLUGIN_THEME__"] = p.theme;
1356
+ globalThis.dispatchEvent(new CustomEvent("clipbus-plugin-theme", { detail: p.theme }));
1357
+ }
1358
+ if (p.locale !== void 0) {
1359
+ globalThis["__CLIPBUS_PLUGIN_LOCALE__"] = p.locale;
1360
+ globalThis.dispatchEvent(new CustomEvent("clipbus-plugin-locale", { detail: p.locale }));
1361
+ }
1362
+ }
1363
+ function emitActionBootstrap(p) {
1364
+ if (p.context !== void 0) {
1365
+ globalThis["__CLIPBUS_PLUGIN_CONTEXT__"] = p.context;
1366
+ globalThis.dispatchEvent(new CustomEvent("clipbus-plugin-context", { detail: p.context }));
1367
+ }
1368
+ if (p.item !== void 0) {
1369
+ globalThis["__CLIPBUS_PLUGIN_ITEM__"] = p.item;
1370
+ globalThis.dispatchEvent(new CustomEvent("clipbus-plugin-item", { detail: p.item }));
1371
+ }
1372
+ if (p.draft !== void 0) {
1373
+ globalThis["__CLIPBUS_PLUGIN_DRAFT__"] = p.draft;
1374
+ globalThis.dispatchEvent(new CustomEvent("clipbus-plugin-draft", { detail: p.draft }));
1375
+ }
1376
+ if (p.theme !== void 0) {
1377
+ globalThis["__CLIPBUS_PLUGIN_THEME__"] = p.theme;
1378
+ globalThis.dispatchEvent(new CustomEvent("clipbus-plugin-theme", { detail: p.theme }));
1379
+ }
1380
+ if (p.locale !== void 0) {
1381
+ globalThis["__CLIPBUS_PLUGIN_LOCALE__"] = p.locale;
1382
+ globalThis.dispatchEvent(new CustomEvent("clipbus-plugin-locale", { detail: p.locale }));
1383
+ }
1384
+ }
1385
+
1386
+ // src/preview/wire.ts
1387
+ function computeWirePayloads(scenario, themeKey = DEFAULT_THEME_KEY) {
1388
+ const context = {
1389
+ mode: scenario.mode,
1390
+ pluginID: scenario.pluginID ?? "plugin.preview.harness"
1391
+ };
1392
+ const preset = getThemePreset(themeKey);
1393
+ const theme = {
1394
+ scheme: preset.scheme,
1395
+ tokens: { ...preset.tokens }
1396
+ };
1397
+ const locale = { locale: "en" };
1398
+ if (scenario.mode === "attachmentRenderer") {
1399
+ return {
1400
+ mode: "attachmentRenderer",
1401
+ context,
1402
+ item: scenario.item,
1403
+ attachment: scenario.attachment,
1404
+ theme,
1405
+ locale
1406
+ };
1407
+ }
1408
+ return {
1409
+ mode: "action",
1410
+ context,
1411
+ item: scenario.item,
1412
+ draft: scenario.draft ?? {},
1413
+ theme,
1414
+ locale
1415
+ };
1416
+ }
1417
+ function injectWire(_target, payloads) {
1418
+ if (payloads.mode === "attachmentRenderer") {
1419
+ emitAttachmentBootstrap({
1420
+ context: payloads.context,
1421
+ item: payloads.item,
1422
+ attachment: payloads.attachment,
1423
+ theme: payloads.theme,
1424
+ locale: payloads.locale
1425
+ });
1426
+ } else {
1427
+ emitActionBootstrap({
1428
+ context: payloads.context,
1429
+ item: payloads.item,
1430
+ draft: payloads.draft,
1431
+ theme: payloads.theme,
1432
+ locale: payloads.locale
1433
+ });
1434
+ }
1435
+ }
1436
+ var THEME_CSS_VAR_NAMES = {
1437
+ surface: "--clipbus-surface",
1438
+ surfaceElevated: "--clipbus-surface-elevated",
1439
+ textPrimary: "--clipbus-text-primary",
1440
+ textSecondary: "--clipbus-text-secondary",
1441
+ textTertiary: "--clipbus-text-tertiary",
1442
+ accent: "--clipbus-accent",
1443
+ accentContrast: "--clipbus-accent-contrast",
1444
+ border: "--clipbus-border",
1445
+ divider: "--clipbus-divider",
1446
+ success: "--clipbus-success",
1447
+ warning: "--clipbus-warning",
1448
+ danger: "--clipbus-danger"
1449
+ };
1450
+ function applyThemeCssVars(target, snapshot) {
1451
+ const tokens = snapshot.tokens;
1452
+ for (const [field, varName] of Object.entries(THEME_CSS_VAR_NAMES)) {
1453
+ const value = tokens[field];
1454
+ if (typeof value === "string") target.style.setProperty(varName, value);
1455
+ }
1456
+ }
1457
+
1458
+ // src/preview/inputPanel.ts
1459
+ function classifyWireSections(payloads) {
1460
+ const sections = [];
1461
+ sections.push({
1462
+ key: "context",
1463
+ kind: "infra",
1464
+ defaultOpen: false,
1465
+ value: payloads.context
1466
+ });
1467
+ sections.push({
1468
+ key: "item",
1469
+ kind: "content",
1470
+ defaultOpen: true,
1471
+ value: payloads.item
1472
+ });
1473
+ if (payloads.mode === "attachmentRenderer") {
1474
+ sections.push({
1475
+ key: "attachment",
1476
+ kind: "content",
1477
+ defaultOpen: true,
1478
+ value: payloads.attachment
1479
+ });
1480
+ } else {
1481
+ sections.push({
1482
+ key: "draft",
1483
+ kind: "content",
1484
+ defaultOpen: true,
1485
+ value: payloads.draft
1486
+ });
1487
+ }
1488
+ sections.push({
1489
+ key: "theme",
1490
+ kind: "infra",
1491
+ defaultOpen: false,
1492
+ value: payloads.theme
1493
+ });
1494
+ sections.push({
1495
+ key: "locale",
1496
+ kind: "infra",
1497
+ defaultOpen: false,
1498
+ value: payloads.locale
1499
+ });
1500
+ return sections;
1501
+ }
1502
+ function parsePayloadJson(raw) {
1503
+ if (typeof raw !== "string") return { ok: false, raw };
1504
+ try {
1505
+ const value = JSON.parse(raw);
1506
+ return { ok: true, value };
1507
+ } catch {
1508
+ return { ok: false, raw };
1509
+ }
1510
+ }
1511
+ function sectionSummary(value) {
1512
+ if (value === void 0 || value === null) return "\u2014";
1513
+ if (typeof value !== "object") return String(value).slice(0, 48);
1514
+ try {
1515
+ const keys = Object.keys(value);
1516
+ const preview = keys.slice(0, 3).join(", ");
1517
+ return `{ ${preview}${keys.length > 3 ? ", \u2026" : ""} }`;
1518
+ } catch {
1519
+ return String(value);
1520
+ }
1521
+ }
1522
+ function formatSectionValue(value) {
1523
+ if (value === void 0) return "undefined";
1524
+ try {
1525
+ return JSON.stringify(
1526
+ value,
1527
+ (_key, v) => {
1528
+ if (_key === "payloadJson" && typeof v === "string") {
1529
+ const parsed = parsePayloadJson(v);
1530
+ return parsed.ok ? parsed.value : v;
1531
+ }
1532
+ return v;
1533
+ },
1534
+ 2
1535
+ );
1536
+ } catch {
1537
+ return String(value);
1538
+ }
1539
+ }
1540
+ function renderWireInput(containerEl, payloads) {
1541
+ containerEl.innerHTML = "";
1542
+ const sections = classifyWireSections(payloads);
1543
+ for (const section of sections) {
1544
+ const isOpen = section.defaultOpen;
1545
+ const sec = document.createElement("div");
1546
+ sec.className = "cbp-wb__wire-sec" + (isOpen ? " cbp-wb__wire-sec--open" : "");
1547
+ const head = document.createElement("div");
1548
+ head.className = "cbp-wb__wire-sec-head";
1549
+ const chev = document.createElement("span");
1550
+ chev.className = "cbp-wb__wire-sec-chev";
1551
+ chev.textContent = isOpen ? "\u25BC" : "\u25B6";
1552
+ const keySpan = document.createElement("span");
1553
+ keySpan.className = "cbp-wb__wire-sec-key";
1554
+ keySpan.textContent = section.key;
1555
+ const tag = document.createElement("span");
1556
+ tag.className = "cbp-wb__wire-sec-tag" + (section.kind === "content" ? " cbp-wb__wire-sec-tag--content" : "");
1557
+ tag.textContent = section.kind;
1558
+ const sum = document.createElement("span");
1559
+ sum.className = "cbp-wb__wire-sec-sum";
1560
+ sum.textContent = sectionSummary(section.value);
1561
+ head.appendChild(chev);
1562
+ head.appendChild(keySpan);
1563
+ head.appendChild(tag);
1564
+ head.appendChild(sum);
1565
+ const body = document.createElement("div");
1566
+ body.className = "cbp-wb__wire-sec-body";
1567
+ body.textContent = formatSectionValue(section.value);
1568
+ head.addEventListener("click", () => {
1569
+ const nowOpen = sec.classList.contains("cbp-wb__wire-sec--open");
1570
+ if (nowOpen) {
1571
+ sec.classList.remove("cbp-wb__wire-sec--open");
1572
+ chev.textContent = "\u25B6";
1573
+ } else {
1574
+ sec.classList.add("cbp-wb__wire-sec--open");
1575
+ chev.textContent = "\u25BC";
1576
+ }
1577
+ });
1578
+ sec.appendChild(head);
1579
+ sec.appendChild(body);
1580
+ containerEl.appendChild(sec);
1581
+ }
1582
+ }
1583
+
1584
+ // src/preview/index.ts
1585
+ function createPreviewWorkbench(root, opts) {
1586
+ if (opts.scenarios.length === 0) {
1587
+ root.textContent = "[preview] No scenarios provided.";
1588
+ return;
1589
+ }
1590
+ injectStyles(root.ownerDocument ?? document);
1591
+ let pendingLogEntry = null;
1592
+ let pendingSetHeight = null;
1593
+ let pendingSetButtons = null;
1594
+ installFakeHost(window, {
1595
+ onCall(method, payload) {
1596
+ pendingLogEntry?.(method, payload);
1597
+ },
1598
+ onSetHeight(height) {
1599
+ pendingSetHeight?.(height);
1600
+ },
1601
+ onSetButtons(buttons, defaultButtonID) {
1602
+ pendingSetButtons?.(buttons, defaultButtonID);
1603
+ }
1604
+ });
1605
+ let currentCleanup = null;
1606
+ const {
1607
+ slotEl,
1608
+ inputContainer,
1609
+ setViewportHeight,
1610
+ appendLogEntry,
1611
+ applyButtons,
1612
+ start,
1613
+ destroy
1614
+ } = renderChrome(
1615
+ root,
1616
+ opts.scenarios,
1617
+ opts,
1618
+ {
1619
+ onActivate: (scenario, presetKey) => activateScenario(scenario, presetKey),
1620
+ onThemeChange: (scenario, presetKey) => reinjectTheme(scenario, presetKey)
1621
+ }
1622
+ );
1623
+ pendingLogEntry = appendLogEntry;
1624
+ pendingSetHeight = setViewportHeight;
1625
+ pendingSetButtons = applyButtons;
1626
+ function activateScenario(scenario, presetKey) {
1627
+ if (currentCleanup) {
1628
+ try {
1629
+ currentCleanup();
1630
+ } catch {
1631
+ }
1632
+ currentCleanup = null;
1633
+ }
1634
+ slotEl.innerHTML = "";
1635
+ const payloads = computeWirePayloads(scenario, presetKey);
1636
+ injectWire(window, payloads);
1637
+ applyThemeCssVars(slotEl, payloads.theme);
1638
+ renderWireInput(inputContainer, payloads);
1639
+ currentCleanup = opts.mount(slotEl, { scenario });
1640
+ }
1641
+ function reinjectTheme(scenario, presetKey) {
1642
+ const payloads = computeWirePayloads(scenario, presetKey);
1643
+ injectWire(window, payloads);
1644
+ applyThemeCssVars(slotEl, payloads.theme);
1645
+ renderWireInput(inputContainer, payloads);
1646
+ }
1647
+ start();
1648
+ root["__cbpWbDestroy__"] = () => {
1649
+ currentCleanup?.();
1650
+ destroy();
1651
+ };
1652
+ }
1653
+ var STYLE_TAG_ID = "cbp-wb-styles";
1654
+ var FAVICON_TAG_ID = "cbp-wb-favicon";
1655
+ function injectStyles(doc) {
1656
+ const head = doc.head ?? doc.documentElement;
1657
+ if (!doc.getElementById(STYLE_TAG_ID)) {
1658
+ const style = doc.createElement("style");
1659
+ style.id = STYLE_TAG_ID;
1660
+ style.textContent = previewStyles;
1661
+ head.appendChild(style);
1662
+ }
1663
+ if (!doc.getElementById(FAVICON_TAG_ID)) {
1664
+ const icon = doc.createElement("link");
1665
+ icon.id = FAVICON_TAG_ID;
1666
+ icon.rel = "icon";
1667
+ icon.href = "data:,";
1668
+ head.appendChild(icon);
1669
+ }
1670
+ }