@clipbus/plugin-sdk 0.8.5 → 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.
@@ -1,41 +1,71 @@
1
1
  // src/preview/styles.ts
2
2
  var previewStyles = `
3
- /* \u2500\u2500 CSS variable defaults (graphite dark) \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 */
3
+ @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap');
4
+
5
+ /* \u2500\u2500 Wire Bench chrome tokens (stable \u2014 independent of native theme preset) \u2500 */
6
+ /* Intentional decoupling: the workbench chrome is a tool, not a specimen. */
7
+ /* The electric violet signal colour (#7B61FF) is chosen to not clash with */
8
+ /* any of the 4 native theme accents (teal / orange / blue / amber). */
9
+ .cbp-wb {
10
+ --bench: #14161D;
11
+ --bench-2: #1A1D26;
12
+ --bench-3: #222634;
13
+ --bench-line: rgba(152,164,196,.12);
14
+ --bench-line-2: rgba(152,164,196,.20);
15
+ --bench-ink: #E8EAF2;
16
+ --bench-ink-dim: #969EB2;
17
+ --bench-ink-faint: #646B7E;
18
+ --bench-signal: #7B61FF;
19
+ --bench-signal-2: #B9A8FF;
20
+ --bench-signal-soft: rgba(123,97,255,.16);
21
+ }
22
+
23
+ /* \u2500\u2500 Card-side theme vars (graphite defaults; applyTheme() overwrites these) \u2500 */
24
+ /* These track the active preset so the specimen card (card-shell / buttons) */
25
+ /* still recolours when the theme picker changes \u2014 native fidelity for the */
26
+ /* card interior. The chrome itself never reads these for its own background. */
4
27
  .cbp-wb {
5
- --cbp-wb-backdrop: #16181C;
6
- --cbp-wb-accent: #43C6AC;
28
+ --cbp-wb-backdrop: #16181C;
29
+ --cbp-wb-accent: #43C6AC;
7
30
  --cbp-wb-surface-elevated: rgba(35,37,42,.78);
8
- --cbp-wb-border: rgba(255,255,255,.10);
9
- --cbp-wb-text: #F3F4F6;
10
- --cbp-wb-text-secondary: #C2C6CF;
31
+ --cbp-wb-border: rgba(255,255,255,.10);
32
+ --cbp-wb-text: #F3F4F6;
33
+ --cbp-wb-text-secondary: #C2C6CF;
11
34
  }
12
35
 
13
- /* \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 */
36
+ /* \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 */
37
+ /* Background uses --bench (stable), not --cbp-wb-backdrop (theme-driven). */
38
+ /* Blueprint grid is a visual signature of the instrument chrome. */
14
39
  .cbp-wb {
15
40
  min-height: 100%;
16
- padding: 24px;
17
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
18
- font-size: 13px;
41
+ padding: 28px 26px 34px;
42
+ font-family: "Space Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
43
+ font-size: 14px;
19
44
  box-sizing: border-box;
20
- color: var(--cbp-wb-text);
21
- background: var(--cbp-wb-backdrop);
45
+ color: var(--bench-ink);
46
+ background:
47
+ linear-gradient(var(--bench-line) 1px, transparent 1px),
48
+ linear-gradient(90deg, var(--bench-line) 1px, transparent 1px),
49
+ var(--bench);
50
+ background-size: 32px 32px, 32px 32px, auto;
51
+ background-position: -1px -1px, -1px -1px, 0 0;
52
+ -webkit-font-smoothing: antialiased;
22
53
  }
23
54
 
24
55
  *, .cbp-wb * {
25
56
  box-sizing: border-box;
26
57
  }
27
58
 
28
- /* \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 */
29
- /* The default OS / WebKit scrollbar is an opaque bar (white on dark themes)
30
- that reads as jarring. Restyle every scroll area under the workbench \u2014 chrome
31
- (log panel) AND plugin content inside the slot \u2014 into a thin, translucent,
32
- theme-synced overlay that mirrors the native host's subtle scrollbars. The
33
- --cbp-wb-* vars cascade from the workbench root into the plugin slot, so one
34
- rule set covers both surfaces and recolours automatically on theme switch. */
59
+ /* \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 */
60
+ /* Restyle every scroll area \u2014 chrome AND plugin content inside the slot \u2014 */
61
+ /* into a thin, translucent bench-toned overlay. Uses bench-ink-dim (stable) */
62
+ /* rather than --cbp-wb-text-secondary so the scrollbar doesn't recolour */
63
+ /* when the theme switches (intentional \u2014 chrome palette is stable). */
64
+ /* The --cbp-wb-* vars still cascade into the plugin slot for its own use. */
35
65
  .cbp-wb,
36
66
  .cbp-wb * {
37
67
  scrollbar-width: thin;
38
- scrollbar-color: color-mix(in srgb, var(--cbp-wb-text-secondary) 30%, transparent) transparent;
68
+ scrollbar-color: color-mix(in srgb, var(--bench-ink-dim) 30%, transparent) transparent;
39
69
  }
40
70
 
41
71
  .cbp-wb ::-webkit-scrollbar {
@@ -48,14 +78,14 @@ var previewStyles = `
48
78
  }
49
79
 
50
80
  .cbp-wb ::-webkit-scrollbar-thumb {
51
- background-color: color-mix(in srgb, var(--cbp-wb-text-secondary) 30%, transparent);
81
+ background-color: color-mix(in srgb, var(--bench-ink-dim) 30%, transparent);
52
82
  border-radius: 999px;
53
83
  border: 2px solid transparent;
54
84
  background-clip: padding-box;
55
85
  }
56
86
 
57
87
  .cbp-wb ::-webkit-scrollbar-thumb:hover {
58
- background-color: color-mix(in srgb, var(--cbp-wb-text-secondary) 50%, transparent);
88
+ background-color: color-mix(in srgb, var(--bench-ink-dim) 50%, transparent);
59
89
  background-clip: padding-box;
60
90
  }
61
91
 
@@ -63,91 +93,489 @@ var previewStyles = `
63
93
  background: transparent;
64
94
  }
65
95
 
66
- /* \u2500\u2500 Controls bar \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 */
67
- .cbp-wb__controls {
96
+ /* \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 */
97
+ .cbp-wb__header {
68
98
  display: flex;
69
- gap: 12px;
99
+ justify-content: space-between;
100
+ align-items: flex-end;
101
+ gap: 24px;
70
102
  flex-wrap: wrap;
103
+ margin-bottom: 16px;
104
+ }
105
+
106
+ .cbp-wb__brand {
107
+ display: flex;
108
+ flex-direction: column;
109
+ gap: 5px;
110
+ }
111
+
112
+ .cbp-wb__brand-sn {
113
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
114
+ font-size: 11.5px;
115
+ letter-spacing: .16em;
116
+ color: var(--bench-ink-faint);
117
+ text-transform: uppercase;
118
+ }
119
+
120
+ .cbp-wb__brand-mark {
121
+ font-size: 28px;
122
+ font-weight: 700;
123
+ letter-spacing: -.01em;
124
+ line-height: 1;
125
+ }
126
+
127
+ .cbp-wb__brand-mark b { color: var(--bench-signal-2); }
128
+ .cbp-wb__brand-mark span { color: var(--bench-ink); }
129
+
130
+ /* \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 */
131
+ .cbp-wb__controls {
132
+ display: flex;
133
+ gap: 11px;
71
134
  align-items: flex-end;
72
- margin-bottom: 20px;
135
+ flex-wrap: wrap;
73
136
  }
74
137
 
75
138
  .cbp-wb__control {
76
- display: grid;
139
+ display: flex;
140
+ flex-direction: column;
77
141
  gap: 6px;
78
142
  }
79
143
 
80
144
  .cbp-wb__control-label {
145
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
81
146
  font-size: 11px;
82
- font-weight: 700;
83
- letter-spacing: 0.08em;
147
+ letter-spacing: .14em;
84
148
  text-transform: uppercase;
85
- color: var(--cbp-wb-text-secondary);
149
+ color: var(--bench-ink-faint);
86
150
  }
87
151
 
152
+ /* Scenario + Theme selects */
88
153
  .cbp-wb__control select,
89
154
  .cbp-wb__scenario-select {
90
- min-width: 160px;
91
- padding: 10px 12px;
92
- border-radius: 12px;
93
- border: 1px solid var(--cbp-wb-border);
94
- background: color-mix(in srgb, var(--cbp-wb-backdrop) 70%, transparent);
95
- color: var(--cbp-wb-text);
155
+ background: var(--bench-2);
156
+ border: 1px solid var(--bench-line);
157
+ border-radius: 11px;
158
+ color: var(--bench-ink);
159
+ font: inherit;
96
160
  font-size: 13px;
97
- font-family: inherit;
161
+ padding: 8px 13px;
162
+ min-width: 158px;
98
163
  }
99
164
 
100
- /* \u2500\u2500 Width slider \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 */
165
+ /* Width slider */
101
166
  .cbp-wb__width-slider {
102
- width: 140px;
103
- accent-color: var(--cbp-wb-accent);
167
+ accent-color: var(--bench-signal);
168
+ width: 128px;
104
169
  cursor: pointer;
105
170
  }
106
171
 
107
- /* \u2500\u2500 Canvas layout (host frame on top, native-call log below) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
108
- .cbp-wb__canvas {
172
+ /* Current width value shown inline with the label */
173
+ .cbp-wb__width-val {
174
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
175
+ font-size: 12px;
176
+ color: var(--bench-ink-dim);
177
+ }
178
+
179
+ /* \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 */
180
+ .cbp-wb__rule {
181
+ height: 1px;
182
+ background: var(--bench-line);
183
+ margin: 0 0 22px;
184
+ }
185
+
186
+ /* \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 */
187
+ .cbp-wb__stage {
109
188
  display: flex;
110
- flex-direction: column;
111
- gap: 20px;
112
- align-items: center;
189
+ gap: 22px;
190
+ align-items: flex-start;
113
191
  }
114
192
 
115
- /* Host frame hugs the fixed-width viewport (no flex:1 stretch) so a wide
116
- browser window leaves neutral backdrop on the sides instead of a large
117
- empty framed area \u2014 the centered, native-like card the viewport expects. */
118
- .cbp-wb__host-frame {
193
+ /* \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 */
194
+ .cbp-wb__workspace {
195
+ flex: 1 1 auto;
119
196
  min-width: 0;
120
- padding: 16px;
121
- border-radius: 16px;
122
- background: color-mix(in srgb, var(--cbp-wb-surface-elevated) 40%, transparent);
123
- border: 1px solid var(--cbp-wb-border);
124
197
  display: flex;
125
198
  flex-direction: column;
126
- align-items: center;
127
199
  }
128
200
 
129
- /* \u2500\u2500 Frame title \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 */
130
- .cbp-wb__frame-title {
201
+ .cbp-wb__ws-head {
131
202
  display: flex;
132
- justify-content: space-between;
133
- gap: 12px;
134
- margin-bottom: 12px;
203
+ align-items: baseline;
204
+ gap: 13px;
205
+ margin-bottom: 13px;
206
+ }
207
+
208
+ .cbp-wb__ws-kicker {
209
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
135
210
  font-size: 12px;
211
+ font-weight: 600;
212
+ letter-spacing: .2em;
213
+ color: var(--bench-signal-2);
214
+ }
215
+
216
+ .cbp-wb__ws-title {
217
+ font-size: 23px;
136
218
  font-weight: 700;
137
- letter-spacing: 0.04em;
138
- color: var(--cbp-wb-text-secondary);
219
+ letter-spacing: -.01em;
220
+ }
221
+
222
+ .cbp-wb__ws-sub {
223
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
224
+ font-size: 12px;
225
+ color: var(--bench-ink-faint);
226
+ }
227
+
228
+ /* Mode + scenario chip \u2014 right-aligned in the ws-head */
229
+ .cbp-wb__ws-chip {
230
+ margin-left: auto;
231
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
232
+ font-size: 12px;
233
+ color: var(--bench-ink-dim);
234
+ background: var(--bench-2);
235
+ border: 1px solid var(--bench-line);
236
+ border-radius: 9px;
237
+ padding: 6px 11px;
238
+ white-space: nowrap;
239
+ }
240
+
241
+ /* \u2500\u2500 Workspace surface \u2014 glass stage that holds the specimen card \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
242
+ /* min-height fills the viewport column height (hero). The card is anchored */
243
+ /* at the top; excess space becomes the "growth floor" labelling the 800 cap. */
244
+ .cbp-wb__ws-surface {
245
+ position: relative;
246
+ border: 1px solid var(--bench-line);
247
+ border-radius: 18px;
248
+ background:
249
+ radial-gradient(120% 64% at 50% 0%, var(--bench-signal-soft), transparent 56%),
250
+ var(--bench-2);
251
+ min-height: calc(100vh - 150px);
252
+ padding: 34px 36px 18px;
253
+ display: flex;
254
+ flex-direction: column;
255
+ align-items: center;
256
+ gap: 12px;
257
+ overflow: auto;
258
+ }
259
+
260
+ /* Violet accent line across the top edge \u2014 signal that this is live */
261
+ .cbp-wb__ws-surface::before {
262
+ content: "";
263
+ position: absolute;
264
+ left: 0;
265
+ right: 0;
266
+ top: 0;
267
+ height: 2px;
268
+ background: linear-gradient(90deg, transparent, var(--bench-signal-soft), transparent);
269
+ }
270
+
271
+ /* Small tape label anchored top-left inside the surface */
272
+ .cbp-wb__ws-tape {
273
+ position: absolute;
274
+ top: 13px;
275
+ left: 20px;
276
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
277
+ font-size: 11px;
278
+ letter-spacing: .14em;
279
+ text-transform: uppercase;
280
+ color: var(--bench-ink-faint);
281
+ }
282
+
283
+ /* Wrapper that provides a positioning context for the "card internals" tag */
284
+ .cbp-wb__specimen-wrap {
285
+ position: relative;
286
+ margin-top: 20px;
287
+ }
288
+
289
+ /* Dimensions readout (WxH) below the specimen */
290
+ .cbp-wb__ws-dims {
291
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
292
+ font-size: 12px;
293
+ color: var(--bench-ink-faint);
294
+ letter-spacing: .06em;
295
+ }
296
+
297
+ /* Growth floor \u2014 pushes to the bottom of the surface, labels the 800 ceiling */
298
+ /* Turns the blank space below the card into meaningful information: */
299
+ /* "this gap is the room left before the native height cap". */
300
+ .cbp-wb__ws-floor {
301
+ margin-top: auto;
139
302
  width: 100%;
303
+ padding-top: 16px;
304
+ border-top: 1px dashed var(--bench-line-2);
305
+ display: flex;
306
+ justify-content: center;
307
+ gap: 10px;
308
+ align-items: center;
309
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
310
+ font-size: 11.5px;
311
+ letter-spacing: .07em;
312
+ color: var(--bench-ink-faint);
140
313
  }
141
314
 
142
- /* \u2500\u2500 Viewport shell: centers the viewport horizontally \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
143
- .cbp-wb__viewport-shell {
315
+ .cbp-wb__ws-floor-pip {
316
+ color: var(--bench-signal-2);
317
+ font-size: 13px;
318
+ }
319
+
320
+ /* \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 */
321
+ /* Sticks to the top so the workspace can grow vertically without pushing */
322
+ /* the OUT card out of view. Right column is always in frame. */
323
+ .cbp-wb__dock {
324
+ flex: 0 0 470px;
325
+ position: sticky;
326
+ top: 8px;
327
+ align-self: flex-start;
328
+ max-height: calc(100vh - 54px);
329
+ overflow: auto;
144
330
  display: flex;
145
331
  flex-direction: column;
332
+ gap: 16px;
333
+ }
334
+
335
+ /* \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 */
336
+ .cbp-wb__att {
337
+ position: relative;
338
+ background: var(--bench-2);
339
+ border: 1px solid var(--bench-line);
340
+ border-radius: 18px;
341
+ display: flex;
342
+ flex-direction: column;
343
+ overflow: hidden;
344
+ }
345
+
346
+ .cbp-wb__att-head {
347
+ display: flex;
348
+ align-items: center;
349
+ gap: 11px;
350
+ padding: 14px 17px;
351
+ border-bottom: 1px solid var(--bench-line);
352
+ flex: none;
353
+ }
354
+
355
+ .cbp-wb__att-code {
356
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
357
+ font-size: 12px;
358
+ font-weight: 600;
359
+ letter-spacing: .16em;
360
+ color: var(--bench-signal-2);
361
+ }
362
+
363
+ .cbp-wb__att-title {
364
+ font-size: 15.5px;
365
+ font-weight: 700;
366
+ }
367
+
368
+ .cbp-wb__att-dir {
369
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
370
+ font-size: 11.5px;
371
+ color: var(--bench-ink-faint);
372
+ }
373
+
374
+ .cbp-wb__att-meta {
375
+ margin-left: auto;
376
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
377
+ font-size: 12px;
378
+ color: var(--bench-ink-dim);
379
+ }
380
+
381
+ .cbp-wb__att-scroll {
382
+ overflow: auto;
383
+ padding: 7px 0;
384
+ }
385
+
386
+ .cbp-wb__att--in .cbp-wb__att-scroll { max-height: 474px; }
387
+ .cbp-wb__att--out .cbp-wb__att-scroll { max-height: 320px; }
388
+
389
+ /* \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 */
390
+ /* Section dividers; first child has no top border. */
391
+ .cbp-wb__wire-sec {
392
+ border-top: 1px solid var(--bench-line);
393
+ }
394
+
395
+ .cbp-wb__wire-sec:first-child {
396
+ border-top: 0;
397
+ }
398
+
399
+ /* Clickable header row: chevron + key + kind tag + collapsed summary */
400
+ .cbp-wb__wire-sec-head {
401
+ display: flex;
146
402
  align-items: center;
403
+ gap: 10px;
404
+ padding: 11px 17px;
405
+ cursor: pointer;
406
+ user-select: none;
407
+ }
408
+
409
+ .cbp-wb__wire-sec-chev {
410
+ font-size: 10px;
411
+ width: 10px;
412
+ color: var(--bench-ink-faint);
413
+ flex: none;
414
+ }
415
+
416
+ .cbp-wb__wire-sec--open .cbp-wb__wire-sec-chev {
417
+ color: var(--bench-signal-2);
418
+ }
419
+
420
+ .cbp-wb__wire-sec-key {
421
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
422
+ font-size: 13px;
423
+ font-weight: 600;
424
+ color: var(--bench-ink);
425
+ }
426
+
427
+ .cbp-wb__wire-sec-tag {
428
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
429
+ font-size: 10.5px;
430
+ color: var(--bench-ink-faint);
431
+ letter-spacing: .04em;
432
+ border: 1px solid var(--bench-line);
433
+ border-radius: 5px;
434
+ padding: 1px 6px;
435
+ }
436
+
437
+ /* Content sections are highlighted \u2014 these are the user-facing payloads */
438
+ .cbp-wb__wire-sec-tag--content {
439
+ color: var(--bench-signal-2);
440
+ border-color: color-mix(in srgb, var(--bench-signal) 35%, transparent);
441
+ }
442
+
443
+ .cbp-wb__wire-sec-sum {
444
+ margin-left: auto;
445
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
446
+ font-size: 11.5px;
447
+ color: var(--bench-ink-faint);
448
+ max-width: 44%;
449
+ overflow: hidden;
450
+ text-overflow: ellipsis;
451
+ white-space: nowrap;
452
+ }
453
+
454
+ /* Collapsible payload body \u2014 hidden until the section is open */
455
+ .cbp-wb__wire-sec-body {
456
+ display: none;
457
+ padding: 0 17px 14px 32px;
458
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
459
+ font-size: 13px;
460
+ line-height: 1.65;
461
+ white-space: pre;
462
+ color: var(--bench-ink);
463
+ overflow-x: auto;
464
+ }
465
+
466
+ .cbp-wb__wire-sec--open .cbp-wb__wire-sec-body {
467
+ display: block;
468
+ }
469
+
470
+ /* \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 */
471
+ /* Single-open: exactly one entry is expanded at a time. Newest entry auto- */
472
+ /* opens on arrival and collapses all prior entries. Clicking an open entry */
473
+ /* closes it; clicking a closed entry opens it and closes the rest. */
474
+ .cbp-wb__calls {
475
+ display: flex;
476
+ flex-direction: column;
147
477
  gap: 8px;
148
- width: 100%;
478
+ padding: 11px 15px;
479
+ }
480
+
481
+ /* Empty-state label shown before any calls arrive */
482
+ .cbp-wb__calls-empty {
483
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
484
+ font-size: 12px;
485
+ color: var(--bench-ink-faint);
486
+ padding: 12px 17px;
487
+ font-style: italic;
488
+ }
489
+
490
+ /* .cbp-wb__log-entry \u2014 ONE per native call (class name frozen by tests and */
491
+ /* native fidelity contract). Serves as the accordion item outer shell. */
492
+ .cbp-wb__log-entry {
493
+ background: var(--bench-3);
494
+ border: 1px solid var(--bench-line);
495
+ border-radius: 11px;
496
+ overflow: hidden;
497
+ }
498
+
499
+ /* Open state: signal-tinted border + soft glow */
500
+ .cbp-wb__log-entry--open {
501
+ border-color: color-mix(in srgb, var(--bench-signal) 42%, transparent);
502
+ box-shadow: 0 0 0 1px var(--bench-signal-soft);
503
+ }
504
+
505
+ /* Clickable top row: chevron + sequence number + method name + summary */
506
+ .cbp-wb__log-entry-top {
507
+ display: flex;
508
+ gap: 10px;
509
+ align-items: center;
510
+ padding: 10px 13px;
511
+ cursor: pointer;
512
+ user-select: none;
513
+ }
514
+
515
+ .cbp-wb__log-entry-chev {
516
+ font-size: 10px;
517
+ width: 10px;
518
+ color: var(--bench-ink-faint);
519
+ flex: none;
149
520
  }
150
521
 
522
+ .cbp-wb__log-entry--open .cbp-wb__log-entry-chev {
523
+ color: var(--bench-signal-2);
524
+ }
525
+
526
+ .cbp-wb__log-entry-seq {
527
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
528
+ font-size: 11px;
529
+ color: var(--bench-ink-faint);
530
+ }
531
+
532
+ /* .cbp-wb__log-entry-method \u2014 method name (class name frozen by tests). */
533
+ .cbp-wb__log-entry-method {
534
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
535
+ font-size: 13px;
536
+ font-weight: 600;
537
+ color: var(--bench-signal-2);
538
+ }
539
+
540
+ /* One-line payload summary visible in collapsed state; hidden when expanded */
541
+ .cbp-wb__log-entry-sum {
542
+ margin-left: auto;
543
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
544
+ font-size: 11.5px;
545
+ color: var(--bench-ink-faint);
546
+ max-width: 50%;
547
+ overflow: hidden;
548
+ text-overflow: ellipsis;
549
+ white-space: nowrap;
550
+ }
551
+
552
+ .cbp-wb__log-entry--open .cbp-wb__log-entry-sum {
553
+ display: none;
554
+ }
555
+
556
+ /* Collapsible payload body \u2014 shown only when the entry is open */
557
+ .cbp-wb__log-entry-body {
558
+ display: none;
559
+ padding: 0 13px 12px 32px;
560
+ font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
561
+ font-size: 12.5px;
562
+ line-height: 1.6;
563
+ white-space: pre;
564
+ color: var(--bench-ink);
565
+ overflow-x: auto;
566
+ }
567
+
568
+ .cbp-wb__log-entry--open .cbp-wb__log-entry-body {
569
+ display: block;
570
+ }
571
+
572
+ /* \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 */
573
+ /* FROZEN SPECIMEN GEOMETRY \u2014 pixel-identical to HEAD (native measurement */
574
+ /* parity). Class names, padding, radius, border, and overflow rules are all */
575
+ /* verbatim from the prior implementation. Only the surrounding layout */
576
+ /* changed (canvas \u2192 stage / workspace). DO NOT adjust values below. */
577
+ /* \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 */
578
+
151
579
  /* Body clip box. NO border-radius of its own \u2014 the card shell (its parent)
152
580
  owns the rounding. A radius here would clip the card's own corners/border. */
153
581
  .cbp-wb__viewport {
@@ -162,17 +590,16 @@ var previewStyles = `
162
590
  transition: height 0.1s ease;
163
591
  }
164
592
 
165
- /* \u2500\u2500 Native card 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\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 */
166
- /* Geometry source: AttachmentRenderCardShell.swift:24-59 */
167
- /* 10 pt padding / 7 pt continuous radius / 1 pt accent border / elevated bg */
168
- /* Outer card: stacks the body (viewport) above the host action strip, like */
169
- /* native VStack { body; actions }. Width is set inline; height auto-wraps. */
593
+ /* Native card shell: 10 pt padding / 7 pt radius / 1 pt accent border / elevated bg */
594
+ /* Geometry source: AttachmentRenderCardShell.swift:24-59 */
595
+ /* Outer card stacks the body (viewport) above the host action strip: */
596
+ /* native VStack { body; actions }. Width is set inline; height auto-wraps. */
170
597
  .cbp-wb__card-shell {
171
598
  display: flex;
172
599
  flex-direction: column;
173
600
  gap: 8px; /* native cardSpacing: body \u2194 action strip */
174
601
  padding: 10px; /* native cardPadding */
175
- border-radius: 7px; /* native radius.panelInner (replaces the 20px viewport clip) */
602
+ border-radius: 7px; /* native radius.panelInner */
176
603
  /* Border tint = the attachment's tintColor (--cbp-wb-card-tint, set per
177
604
  scenario from scenario.accentHex), falling back to the theme accent \u2014
178
605
  mirrors native, where the card border uses record.tintColor. */
@@ -192,8 +619,8 @@ var previewStyles = `
192
619
  overflow-x: hidden;
193
620
  }
194
621
 
195
- /* \u2500\u2500 Host button strip (inside the card, below the body) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
196
- /* Native action row: HStack { buttons; Spacer } \u2192 left-aligned, compact. */
622
+ /* Host button strip (inside the card, below the body) */
623
+ /* Native action row: HStack { buttons; Spacer } \u2192 left-aligned, compact. */
197
624
  .cbp-wb__strip {
198
625
  display: flex;
199
626
  flex-wrap: wrap;
@@ -210,7 +637,7 @@ var previewStyles = `
210
637
  appearance: none;
211
638
  border: 1px solid var(--cbp-wb-border);
212
639
  border-radius: 999px;
213
- padding: 4px 10px; /* native actionPadding \u2248 3 v / 8 h \u2014 was 10/16 (too large) */
640
+ padding: 4px 10px; /* native actionPadding \u2248 3 v / 8 h */
214
641
  background: color-mix(in srgb, var(--cbp-wb-surface-elevated) 60%, transparent);
215
642
  color: var(--cbp-wb-text-secondary);
216
643
  font-size: 12px;
@@ -225,72 +652,33 @@ var previewStyles = `
225
652
  border-color: var(--cbp-wb-accent);
226
653
  }
227
654
 
228
- /* \u2500\u2500 Log panel (fixed-width right 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\u2500\u2500 */
229
- .cbp-wb__log {
230
- width: 100%;
231
- max-width: 720px;
232
- padding: 14px 16px;
233
- border-radius: 16px;
234
- background: color-mix(in srgb, var(--cbp-wb-surface-elevated) 50%, transparent);
235
- border: 1px solid var(--cbp-wb-border);
236
- overflow: hidden;
237
- display: flex;
238
- flex-direction: column;
239
- gap: 0;
240
- }
241
-
242
- .cbp-wb__log-title {
243
- margin: 0 0 10px;
244
- font-size: 12px;
245
- font-weight: 800;
246
- letter-spacing: 0.08em;
247
- text-transform: uppercase;
248
- color: var(--cbp-wb-text);
249
- }
250
-
251
- .cbp-wb__log-empty {
252
- font-size: 12px;
253
- color: color-mix(in srgb, var(--cbp-wb-text-secondary) 60%, transparent);
254
- font-style: italic;
255
- }
256
-
257
- .cbp-wb__log-list {
258
- list-style: none;
259
- margin: 0;
260
- padding: 0;
261
- display: flex;
262
- flex-direction: column;
263
- gap: 6px;
264
- max-height: 220px;
265
- overflow-y: auto;
266
- }
267
-
268
- .cbp-wb__log-entry {
269
- border-radius: 8px;
270
- padding: 8px 10px;
271
- background: color-mix(in srgb, var(--cbp-wb-surface-elevated) 60%, transparent);
272
- font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
273
- font-size: 11px;
274
- line-height: 1.5;
275
- word-break: break-all;
276
- }
277
-
278
- .cbp-wb__log-entry-method {
279
- font-weight: 700;
280
- color: var(--cbp-wb-accent);
281
- }
282
-
283
- .cbp-wb__log-entry-seq {
284
- font-weight: 400;
285
- color: var(--cbp-wb-text-secondary);
286
- margin-left: 6px;
287
- }
288
-
289
- .cbp-wb__log-entry-payload {
290
- margin-top: 2px;
291
- color: var(--cbp-wb-text-secondary);
292
- white-space: pre-wrap;
293
- overflow-wrap: anywhere;
655
+ /* \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 */
656
+ /* The side-by-side stage assumes a wide display: the renderer card runs up to
657
+ 900px and must sit beside the 470px dock. Below ~1335px the card would clip
658
+ against the dock. Stack instead \u2014 workspace on top at FULL width (the
659
+ specimen is never clipped), dock below as a row of the two attachment cards.
660
+ The dock drops its sticky behaviour here (it's no longer a side rail). */
661
+ @media (max-width: 1335px) {
662
+ .cbp-wb__stage {
663
+ flex-direction: column;
664
+ }
665
+ /* Surface no longer needs to fill the viewport column \u2014 let it wrap the card
666
+ so the dock below is reachable without a long scroll past empty space. */
667
+ .cbp-wb__ws-surface {
668
+ min-height: 420px;
669
+ }
670
+ .cbp-wb__dock {
671
+ position: static;
672
+ flex: none;
673
+ width: 100%;
674
+ max-height: none;
675
+ flex-direction: row;
676
+ align-items: flex-start;
677
+ }
678
+ .cbp-wb__att {
679
+ flex: 1 1 0;
680
+ min-width: 0;
681
+ }
294
682
  }
295
683
  `;
296
684
 
@@ -453,13 +841,25 @@ function renderChrome(root, scenarios, opts, handlers) {
453
841
  root.innerHTML = "";
454
842
  const wb = div("cbp-wb");
455
843
  root.appendChild(wb);
844
+ const header = div("cbp-wb__header");
845
+ wb.appendChild(header);
846
+ const brand = div("cbp-wb__brand");
847
+ const brandSn = document.createElement("div");
848
+ brandSn.className = "cbp-wb__brand-sn";
849
+ brandSn.textContent = "@clipbus/plugin-sdk \xB7 preview";
850
+ const brandMark = document.createElement("div");
851
+ brandMark.className = "cbp-wb__brand-mark";
852
+ brandMark.innerHTML = "<span>WIRE</span> <b>BENCH</b>";
853
+ brand.appendChild(brandSn);
854
+ brand.appendChild(brandMark);
855
+ header.appendChild(brand);
456
856
  const controls = div("cbp-wb__controls");
457
- wb.appendChild(controls);
857
+ header.appendChild(controls);
458
858
  const viewOptions = [
459
859
  { value: "attachmentRenderer", label: "Renderer" },
460
860
  { value: "action", label: "Action" }
461
861
  ];
462
- const viewSelect = buildSelect("cbp-wb-view", "View", viewOptions, selectedView, controls);
862
+ const viewSelect = buildSelect("cbp-wb-view", "Surface", viewOptions, selectedView, controls);
463
863
  const scenarioAreaWrapper = div("cbp-wb__control");
464
864
  controls.appendChild(scenarioAreaWrapper);
465
865
  const themeOptions = previewThemePresets.map((p) => ({ value: p.key, label: p.label }));
@@ -474,7 +874,11 @@ function renderChrome(root, scenarios, opts, handlers) {
474
874
  const widthLabel = document.createElement("label");
475
875
  widthLabel.className = "cbp-wb__control-label";
476
876
  widthLabel.htmlFor = "cbp-wb-width";
477
- widthLabel.textContent = `Width: ${currentWidth}px`;
877
+ widthLabel.textContent = "Width ";
878
+ const widthVal = document.createElement("span");
879
+ widthVal.className = "cbp-wb__width-val";
880
+ widthVal.textContent = `${currentWidth}px`;
881
+ widthLabel.appendChild(widthVal);
478
882
  widthControl.appendChild(widthLabel);
479
883
  const widthSlider = document.createElement("input");
480
884
  widthSlider.type = "range";
@@ -483,20 +887,34 @@ function renderChrome(root, scenarios, opts, handlers) {
483
887
  widthSlider.className = "cbp-wb__width-slider";
484
888
  widthControl.appendChild(widthSlider);
485
889
  controls.appendChild(widthControl);
486
- const canvas = div("cbp-wb__canvas");
487
- wb.appendChild(canvas);
488
- const hostFrame = div("cbp-wb__host-frame");
489
- canvas.appendChild(hostFrame);
490
- const frameTitle = div("cbp-wb__frame-title");
491
- const frameTitleLabel = span("cbp-wb__frame-title-label");
492
- const frameSizeLabel = span("cbp-wb__frame-title-size");
493
- frameTitle.appendChild(frameTitleLabel);
494
- frameTitle.appendChild(frameSizeLabel);
495
- hostFrame.appendChild(frameTitle);
496
- const viewportShell = div("cbp-wb__viewport-shell");
497
- hostFrame.appendChild(viewportShell);
890
+ const rule = div("cbp-wb__rule");
891
+ wb.appendChild(rule);
892
+ const stage = div("cbp-wb__stage");
893
+ wb.appendChild(stage);
894
+ const workspace = div("cbp-wb__workspace");
895
+ stage.appendChild(workspace);
896
+ const wsHead = div("cbp-wb__ws-head");
897
+ const wsKicker = span("cbp-wb__ws-kicker");
898
+ wsKicker.textContent = "PREVIEW";
899
+ const wsTitle = span("cbp-wb__ws-title");
900
+ wsTitle.textContent = "Workspace";
901
+ const wsSub = span("cbp-wb__ws-sub");
902
+ wsSub.textContent = "native-fidelity \xB7 grows with setHeight";
903
+ const wsChip = span("cbp-wb__ws-chip");
904
+ wsHead.appendChild(wsKicker);
905
+ wsHead.appendChild(wsTitle);
906
+ wsHead.appendChild(wsSub);
907
+ wsHead.appendChild(wsChip);
908
+ workspace.appendChild(wsHead);
909
+ const wsSurface = div("cbp-wb__ws-surface");
910
+ workspace.appendChild(wsSurface);
911
+ const wsTape = div("cbp-wb__ws-tape");
912
+ wsTape.textContent = "device under preview \xB7 specimen";
913
+ wsSurface.appendChild(wsTape);
914
+ const specimenWrap = div("cbp-wb__specimen-wrap");
915
+ wsSurface.appendChild(specimenWrap);
498
916
  const cardShell = div("cbp-wb__card-shell");
499
- viewportShell.appendChild(cardShell);
917
+ specimenWrap.appendChild(cardShell);
500
918
  const viewport = div("cbp-wb__viewport");
501
919
  cardShell.appendChild(viewport);
502
920
  const webview = div("cbp-wb__webview");
@@ -504,27 +922,69 @@ function renderChrome(root, scenarios, opts, handlers) {
504
922
  const slotEl = webview;
505
923
  const strip = div("cbp-wb__strip");
506
924
  cardShell.appendChild(strip);
507
- const logPanel = div("cbp-wb__log");
508
- canvas.appendChild(logPanel);
509
- const logTitle = document.createElement("p");
510
- logTitle.className = "cbp-wb__log-title";
511
- logTitle.textContent = "Native Calls";
512
- logPanel.appendChild(logTitle);
513
- const logEmpty = document.createElement("p");
514
- logEmpty.className = "cbp-wb__log-empty";
515
- logEmpty.textContent = "No calls yet.";
516
- logPanel.appendChild(logEmpty);
517
- const logList = document.createElement("ul");
518
- logList.className = "cbp-wb__log-list";
519
- logPanel.appendChild(logList);
925
+ const wsDims = div("cbp-wb__ws-dims");
926
+ wsSurface.appendChild(wsDims);
927
+ const wsFloor = div("cbp-wb__ws-floor");
928
+ const wsFloorPip = span("cbp-wb__ws-floor-pip");
929
+ wsFloorPip.textContent = "\u2195";
930
+ wsFloor.appendChild(wsFloorPip);
931
+ const wsFloorText = document.createTextNode(
932
+ " height follows the plugin \xB7 native ceiling \u2248 800px \xB7 content usually fits one screen"
933
+ );
934
+ wsFloor.appendChild(wsFloorText);
935
+ wsSurface.appendChild(wsFloor);
936
+ const dock = div("cbp-wb__dock");
937
+ stage.appendChild(dock);
938
+ const inAtt = div("cbp-wb__att cbp-wb__att--in");
939
+ dock.appendChild(inAtt);
940
+ const inHead = div("cbp-wb__att-head");
941
+ const inCode = span("cbp-wb__att-code");
942
+ inCode.textContent = "IN";
943
+ const inTitle = span("cbp-wb__att-title");
944
+ inTitle.textContent = "Wire Input";
945
+ const inDir = span("cbp-wb__att-dir");
946
+ inDir.textContent = "host \u2192 plugin";
947
+ const inMeta = span("cbp-wb__att-meta");
948
+ inMeta.textContent = "5 keys";
949
+ inHead.appendChild(inCode);
950
+ inHead.appendChild(inTitle);
951
+ inHead.appendChild(inDir);
952
+ inHead.appendChild(inMeta);
953
+ inAtt.appendChild(inHead);
954
+ const inputContainer = div("cbp-wb__att-scroll");
955
+ inAtt.appendChild(inputContainer);
956
+ const outAtt = div("cbp-wb__att cbp-wb__att--out");
957
+ dock.appendChild(outAtt);
958
+ const outHead = div("cbp-wb__att-head");
959
+ const outCode = span("cbp-wb__att-code");
960
+ outCode.textContent = "OUT";
961
+ const outTitle = span("cbp-wb__att-title");
962
+ outTitle.textContent = "Native Calls";
963
+ const outDir = span("cbp-wb__att-dir");
964
+ outDir.textContent = "plugin \u2192 host";
965
+ const outMeta = span("cbp-wb__att-meta");
966
+ outMeta.textContent = "0 calls";
967
+ outHead.appendChild(outCode);
968
+ outHead.appendChild(outTitle);
969
+ outHead.appendChild(outDir);
970
+ outHead.appendChild(outMeta);
971
+ outAtt.appendChild(outHead);
972
+ const outScroll = div("cbp-wb__att-scroll");
973
+ outAtt.appendChild(outScroll);
974
+ const callsEmpty = document.createElement("p");
975
+ callsEmpty.className = "cbp-wb__calls-empty";
976
+ callsEmpty.textContent = "No calls yet.";
977
+ outScroll.appendChild(callsEmpty);
978
+ const callsList = div("cbp-wb__calls");
979
+ outScroll.appendChild(callsList);
520
980
  let logSeq = 0;
521
981
  function applyTheme(preset) {
522
- wb.style.setProperty("--cbp-wb-backdrop", preset.backdrop);
523
982
  wb.style.setProperty("--cbp-wb-accent", preset.tokens.accent);
524
983
  wb.style.setProperty("--cbp-wb-surface-elevated", preset.tokens.surfaceElevated);
525
984
  wb.style.setProperty("--cbp-wb-border", preset.tokens.border);
526
985
  wb.style.setProperty("--cbp-wb-text", preset.tokens.textPrimary);
527
986
  wb.style.setProperty("--cbp-wb-text-secondary", preset.tokens.textSecondary);
987
+ wb.style.setProperty("--cbp-wb-backdrop", preset.backdrop);
528
988
  wb.dataset["themeScheme"] = preset.scheme;
529
989
  wb.dataset["themeKey"] = preset.key;
530
990
  }
@@ -537,9 +997,9 @@ function renderChrome(root, scenarios, opts, handlers) {
537
997
  function setViewportWidth(px) {
538
998
  currentWidth = px;
539
999
  cardShell.style.width = `${px}px`;
540
- widthLabel.textContent = `Width: ${px}px`;
1000
+ widthVal.textContent = `${px}px`;
541
1001
  widthSlider.value = String(px);
542
- frameSizeLabel.textContent = `${px} \xD7 ${currentBodyHeightLabel()}`;
1002
+ wsDims.textContent = `${px} \xD7 ${currentBodyHeightLabel()}`;
543
1003
  }
544
1004
  function updateWidthSlider(min, max, value) {
545
1005
  widthSlider.min = String(min);
@@ -566,30 +1026,66 @@ function renderChrome(root, scenarios, opts, handlers) {
566
1026
  clamped = Math.min(max, Math.max(min, height));
567
1027
  }
568
1028
  viewport.style.height = `${clamped}px`;
569
- frameSizeLabel.textContent = `${currentWidth} \xD7 ${clamped}`;
1029
+ wsDims.textContent = `${currentWidth} \xD7 ${clamped}`;
570
1030
  }
571
1031
  function appendLogEntry(method, payload) {
572
- logEmpty.style.display = "none";
1032
+ callsEmpty.style.display = "none";
573
1033
  logSeq += 1;
574
- const entry = document.createElement("li");
575
- entry.className = "cbp-wb__log-entry";
576
- const methodSpan = document.createElement("span");
577
- methodSpan.className = "cbp-wb__log-entry-method";
578
- methodSpan.textContent = method;
579
- const seqSpan = document.createElement("span");
580
- seqSpan.className = "cbp-wb__log-entry-seq";
581
- seqSpan.textContent = `#${logSeq}`;
582
- const payloadDiv = document.createElement("div");
583
- payloadDiv.className = "cbp-wb__log-entry-payload";
1034
+ let payloadText;
584
1035
  try {
585
- payloadDiv.textContent = JSON.stringify(payload, null, 2);
1036
+ payloadText = JSON.stringify(payload, null, 2);
586
1037
  } catch {
587
- payloadDiv.textContent = String(payload);
1038
+ payloadText = String(payload);
1039
+ }
1040
+ let summaryText;
1041
+ try {
1042
+ const compact = JSON.stringify(payload);
1043
+ summaryText = compact && compact.length > 60 ? compact.slice(0, 57) + "\u2026" : compact ?? "\u2014";
1044
+ } catch {
1045
+ summaryText = String(payload);
1046
+ }
1047
+ const entry = document.createElement("div");
1048
+ entry.className = "cbp-wb__log-entry cbp-wb__log-entry--open";
1049
+ const top = document.createElement("div");
1050
+ top.className = "cbp-wb__log-entry-top";
1051
+ const chev = span("cbp-wb__log-entry-chev");
1052
+ chev.textContent = "\u25BC";
1053
+ const seqEl = span("cbp-wb__log-entry-seq");
1054
+ seqEl.textContent = `#${logSeq}`;
1055
+ const methodSpan = span("cbp-wb__log-entry-method");
1056
+ methodSpan.textContent = method;
1057
+ const sumEl = span("cbp-wb__log-entry-sum");
1058
+ sumEl.textContent = summaryText;
1059
+ top.appendChild(chev);
1060
+ top.appendChild(seqEl);
1061
+ top.appendChild(methodSpan);
1062
+ top.appendChild(sumEl);
1063
+ const body = document.createElement("div");
1064
+ body.className = "cbp-wb__log-entry-body";
1065
+ body.textContent = payloadText;
1066
+ entry.appendChild(top);
1067
+ entry.appendChild(body);
1068
+ collapseAllLogEntries();
1069
+ callsList.insertBefore(entry, callsList.firstChild);
1070
+ outMeta.textContent = `${logSeq} call${logSeq === 1 ? "" : "s"}`;
1071
+ top.addEventListener("click", () => {
1072
+ const isOpen = entry.classList.contains("cbp-wb__log-entry--open");
1073
+ if (isOpen) {
1074
+ entry.classList.remove("cbp-wb__log-entry--open");
1075
+ chev.textContent = "\u25B6";
1076
+ } else {
1077
+ collapseAllLogEntries();
1078
+ entry.classList.add("cbp-wb__log-entry--open");
1079
+ chev.textContent = "\u25BC";
1080
+ }
1081
+ });
1082
+ }
1083
+ function collapseAllLogEntries() {
1084
+ for (const el of callsList.querySelectorAll(".cbp-wb__log-entry")) {
1085
+ el.classList.remove("cbp-wb__log-entry--open");
1086
+ const c = el.querySelector(".cbp-wb__log-entry-chev");
1087
+ if (c) c.textContent = "\u25B6";
588
1088
  }
589
- entry.appendChild(methodSpan);
590
- entry.appendChild(seqSpan);
591
- entry.appendChild(payloadDiv);
592
- logList.insertBefore(entry, logList.firstChild);
593
1089
  }
594
1090
  let activeButtons = [];
595
1091
  let activeDefaultButtonID = null;
@@ -649,10 +1145,11 @@ function renderChrome(root, scenarios, opts, handlers) {
649
1145
  });
650
1146
  scenarioAreaWrapper.appendChild(select);
651
1147
  }
652
- function updateFrameTitle() {
1148
+ function updateWorkspaceHead() {
653
1149
  const scenario = activeScenario();
654
- frameTitleLabel.textContent = scenario.mode === "attachmentRenderer" ? "Attachment Renderer" : "Draft Action";
655
- frameSizeLabel.textContent = `${currentWidth} \xD7 ${currentBodyHeightLabel()}`;
1150
+ const modeLabel = scenario.mode === "attachmentRenderer" ? "attachmentRenderer" : "action";
1151
+ wsChip.textContent = `${modeLabel} \xB7 ${scenario.label}`;
1152
+ wsDims.textContent = `${currentWidth} \xD7 ${currentBodyHeightLabel()}`;
656
1153
  }
657
1154
  function activateCurrentScenario() {
658
1155
  const scenario = activeScenario();
@@ -672,7 +1169,7 @@ function renderChrome(root, scenarios, opts, handlers) {
672
1169
  updateWidthSlider(min, max, initialWidth);
673
1170
  applyInitialHeight(scenario);
674
1171
  applyScenarioButtons(scenario);
675
- updateFrameTitle();
1172
+ updateWorkspaceHead();
676
1173
  syncQuery();
677
1174
  handlers.onActivate(scenario, selectedThemeKey);
678
1175
  }
@@ -710,6 +1207,7 @@ function renderChrome(root, scenarios, opts, handlers) {
710
1207
  }
711
1208
  return {
712
1209
  slotEl,
1210
+ inputContainer,
713
1211
  setViewportHeight,
714
1212
  setViewportWidth,
715
1213
  appendLogEntry,
@@ -914,6 +1412,132 @@ function applyThemeCssVars(target, snapshot) {
914
1412
  }
915
1413
  }
916
1414
 
1415
+ // src/preview/inputPanel.ts
1416
+ function classifyWireSections(payloads) {
1417
+ const sections = [];
1418
+ sections.push({
1419
+ key: "context",
1420
+ kind: "infra",
1421
+ defaultOpen: false,
1422
+ value: payloads.context
1423
+ });
1424
+ sections.push({
1425
+ key: "item",
1426
+ kind: "content",
1427
+ defaultOpen: true,
1428
+ value: payloads.item
1429
+ });
1430
+ if (payloads.mode === "attachmentRenderer") {
1431
+ sections.push({
1432
+ key: "attachment",
1433
+ kind: "content",
1434
+ defaultOpen: true,
1435
+ value: payloads.attachment
1436
+ });
1437
+ } else {
1438
+ sections.push({
1439
+ key: "draft",
1440
+ kind: "content",
1441
+ defaultOpen: true,
1442
+ value: payloads.draft
1443
+ });
1444
+ }
1445
+ sections.push({
1446
+ key: "theme",
1447
+ kind: "infra",
1448
+ defaultOpen: false,
1449
+ value: payloads.theme
1450
+ });
1451
+ sections.push({
1452
+ key: "locale",
1453
+ kind: "infra",
1454
+ defaultOpen: false,
1455
+ value: payloads.locale
1456
+ });
1457
+ return sections;
1458
+ }
1459
+ function parsePayloadJson(raw) {
1460
+ if (typeof raw !== "string") return { ok: false, raw };
1461
+ try {
1462
+ const value = JSON.parse(raw);
1463
+ return { ok: true, value };
1464
+ } catch {
1465
+ return { ok: false, raw };
1466
+ }
1467
+ }
1468
+ function sectionSummary(value) {
1469
+ if (value === void 0 || value === null) return "\u2014";
1470
+ if (typeof value !== "object") return String(value).slice(0, 48);
1471
+ try {
1472
+ const keys = Object.keys(value);
1473
+ const preview = keys.slice(0, 3).join(", ");
1474
+ return `{ ${preview}${keys.length > 3 ? ", \u2026" : ""} }`;
1475
+ } catch {
1476
+ return String(value);
1477
+ }
1478
+ }
1479
+ function formatSectionValue(value) {
1480
+ if (value === void 0) return "undefined";
1481
+ try {
1482
+ return JSON.stringify(
1483
+ value,
1484
+ (_key, v) => {
1485
+ if (_key === "payloadJson" && typeof v === "string") {
1486
+ const parsed = parsePayloadJson(v);
1487
+ return parsed.ok ? parsed.value : v;
1488
+ }
1489
+ return v;
1490
+ },
1491
+ 2
1492
+ );
1493
+ } catch {
1494
+ return String(value);
1495
+ }
1496
+ }
1497
+ function renderWireInput(containerEl, payloads) {
1498
+ containerEl.innerHTML = "";
1499
+ const sections = classifyWireSections(payloads);
1500
+ for (const section of sections) {
1501
+ const isOpen = section.defaultOpen;
1502
+ const sec = document.createElement("div");
1503
+ sec.className = "cbp-wb__wire-sec" + (isOpen ? " cbp-wb__wire-sec--open" : "");
1504
+ const head = document.createElement("div");
1505
+ head.className = "cbp-wb__wire-sec-head";
1506
+ const chev = document.createElement("span");
1507
+ chev.className = "cbp-wb__wire-sec-chev";
1508
+ chev.textContent = isOpen ? "\u25BC" : "\u25B6";
1509
+ const keySpan = document.createElement("span");
1510
+ keySpan.className = "cbp-wb__wire-sec-key";
1511
+ keySpan.textContent = section.key;
1512
+ const tag = document.createElement("span");
1513
+ tag.className = "cbp-wb__wire-sec-tag" + (section.kind === "content" ? " cbp-wb__wire-sec-tag--content" : "");
1514
+ tag.textContent = section.kind;
1515
+ const sum = document.createElement("span");
1516
+ sum.className = "cbp-wb__wire-sec-sum";
1517
+ sum.textContent = sectionSummary(section.value);
1518
+ head.appendChild(chev);
1519
+ head.appendChild(keySpan);
1520
+ head.appendChild(tag);
1521
+ head.appendChild(sum);
1522
+ const body = document.createElement("div");
1523
+ body.className = "cbp-wb__wire-sec-body";
1524
+ body.textContent = formatSectionValue(section.value);
1525
+ head.addEventListener("click", () => {
1526
+ const nowOpen = sec.classList.contains("cbp-wb__wire-sec--open");
1527
+ if (nowOpen) {
1528
+ sec.classList.remove("cbp-wb__wire-sec--open");
1529
+ chev.textContent = "\u25B6";
1530
+ } else {
1531
+ sec.classList.add("cbp-wb__wire-sec--open");
1532
+ chev.textContent = "\u25BC";
1533
+ }
1534
+ });
1535
+ sec.appendChild(head);
1536
+ sec.appendChild(body);
1537
+ containerEl.appendChild(sec);
1538
+ }
1539
+ }
1540
+
917
1541
  // src/preview/index.ts
918
1542
  function createPreviewWorkbench(root, opts) {
919
1543
  if (opts.scenarios.length === 0) {
@@ -938,6 +1562,7 @@ function createPreviewWorkbench(root, opts) {
938
1562
  let currentCleanup = null;
939
1563
  const {
940
1564
  slotEl,
1565
+ inputContainer,
941
1566
  setViewportHeight,
942
1567
  appendLogEntry,
943
1568
  applyButtons,
@@ -967,12 +1592,14 @@ function createPreviewWorkbench(root, opts) {
967
1592
  const payloads = computeWirePayloads(scenario, presetKey);
968
1593
  injectWire(window, payloads);
969
1594
  applyThemeCssVars(slotEl, payloads.theme);
1595
+ renderWireInput(inputContainer, payloads);
970
1596
  currentCleanup = opts.mount(slotEl, { scenario });
971
1597
  }
972
1598
  function reinjectTheme(scenario, presetKey) {
973
1599
  const payloads = computeWirePayloads(scenario, presetKey);
974
1600
  injectWire(window, payloads);
975
1601
  applyThemeCssVars(slotEl, payloads.theme);
1602
+ renderWireInput(inputContainer, payloads);
976
1603
  }
977
1604
  start();
978
1605
  root["__cbpWbDestroy__"] = () => {
@@ -1005,6 +1632,7 @@ export {
1005
1632
  WINDOW_SET_HEIGHT_METHOD,
1006
1633
  applyThemeCssVars,
1007
1634
  clampWidth,
1635
+ classifyWireSections,
1008
1636
  computeWirePayloads,
1009
1637
  createPreviewWorkbench,
1010
1638
  defaultDarkTheme,
@@ -1013,6 +1641,7 @@ export {
1013
1641
  groupScenariosByMode,
1014
1642
  injectWire,
1015
1643
  installFakeHost,
1644
+ parsePayloadJson,
1016
1645
  previewThemePresets,
1017
1646
  resolveViewportWidth
1018
1647
  };