@unofficialbox/box-open-elements 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/README.md +3 -3
  2. package/dist/components/collections/table.d.ts +57 -3
  3. package/dist/components/collections/table.js +339 -20
  4. package/dist/components/feedback/due-badge.d.ts +38 -0
  5. package/dist/components/feedback/due-badge.js +168 -0
  6. package/dist/components/feedback/due-types.d.ts +32 -0
  7. package/dist/components/feedback/due-types.js +91 -0
  8. package/dist/components/feedback/progress-steps.d.ts +51 -5
  9. package/dist/components/feedback/progress-steps.js +200 -22
  10. package/dist/components/feedback/stage-path.d.ts +44 -0
  11. package/dist/components/feedback/stage-path.js +235 -0
  12. package/dist/components/forms/select.d.ts +21 -0
  13. package/dist/components/forms/select.js +100 -3
  14. package/dist/components/forms/text-field.d.ts +21 -0
  15. package/dist/components/forms/text-field.js +100 -1
  16. package/dist/components/layout/nav-sidebar.d.ts +14 -0
  17. package/dist/components/layout/nav-sidebar.js +49 -0
  18. package/dist/components/layout/split-view.d.ts +14 -0
  19. package/dist/components/layout/split-view.js +80 -1
  20. package/dist/components/overlays/command-palette.d.ts +62 -0
  21. package/dist/components/overlays/command-palette.js +537 -0
  22. package/dist/components/overlays/command-types.d.ts +90 -0
  23. package/dist/components/overlays/command-types.js +259 -0
  24. package/dist/components/overlays/drawer.d.ts +18 -0
  25. package/dist/components/overlays/drawer.js +135 -9
  26. package/dist/components/overlays/shortcuts-overlay.d.ts +48 -0
  27. package/dist/components/overlays/shortcuts-overlay.js +385 -0
  28. package/dist/components/overlays/tooltip.js +1 -1
  29. package/dist/element-maps.d.ts +292 -0
  30. package/dist/element-maps.js +1 -0
  31. package/dist/entries/activity-density.d.ts +1 -0
  32. package/dist/entries/activity-density.js +1 -0
  33. package/dist/entries/agent-chat.d.ts +1 -0
  34. package/dist/entries/agent-chat.js +1 -0
  35. package/dist/entries/audit-log.d.ts +1 -0
  36. package/dist/entries/audit-log.js +1 -0
  37. package/dist/entries/command-palette.d.ts +1 -0
  38. package/dist/entries/command-palette.js +1 -0
  39. package/dist/entries/compare-view.d.ts +1 -0
  40. package/dist/entries/compare-view.js +1 -0
  41. package/dist/entries/diff-viewer.d.ts +1 -0
  42. package/dist/entries/diff-viewer.js +1 -0
  43. package/dist/entries/due-badge.d.ts +1 -0
  44. package/dist/entries/due-badge.js +1 -0
  45. package/dist/entries/form-wizard.d.ts +1 -0
  46. package/dist/entries/form-wizard.js +1 -0
  47. package/dist/entries/lineage-graph.d.ts +1 -0
  48. package/dist/entries/lineage-graph.js +1 -0
  49. package/dist/entries/notification-bell.d.ts +1 -0
  50. package/dist/entries/notification-bell.js +1 -0
  51. package/dist/entries/notification-inbox.d.ts +1 -0
  52. package/dist/entries/notification-inbox.js +1 -0
  53. package/dist/entries/provenance-strip.d.ts +1 -0
  54. package/dist/entries/provenance-strip.js +1 -0
  55. package/dist/entries/run-trace.d.ts +1 -0
  56. package/dist/entries/run-trace.js +1 -0
  57. package/dist/entries/shortcuts-overlay.d.ts +1 -0
  58. package/dist/entries/shortcuts-overlay.js +1 -0
  59. package/dist/entries/signature-ceremony.d.ts +1 -0
  60. package/dist/entries/signature-ceremony.js +1 -0
  61. package/dist/entries/stage-path.d.ts +1 -0
  62. package/dist/entries/stage-path.js +1 -0
  63. package/dist/entries/timeline.d.ts +1 -0
  64. package/dist/entries/timeline.js +1 -0
  65. package/dist/entries/version-graph.d.ts +1 -0
  66. package/dist/entries/version-graph.js +1 -0
  67. package/dist/entries/version-list.d.ts +1 -0
  68. package/dist/entries/version-list.js +1 -0
  69. package/dist/entries/wizard-summary.d.ts +1 -0
  70. package/dist/entries/wizard-summary.js +1 -0
  71. package/dist/entries/work-queue.d.ts +1 -0
  72. package/dist/entries/work-queue.js +1 -0
  73. package/dist/entries/workload-board.d.ts +1 -0
  74. package/dist/entries/workload-board.js +1 -0
  75. package/dist/index.d.ts +21 -0
  76. package/dist/index.js +21 -0
  77. package/dist/patterns/agent-chat/agent-chat.d.ts +75 -0
  78. package/dist/patterns/agent-chat/agent-chat.js +820 -0
  79. package/dist/patterns/agent-chat/controller.d.ts +27 -0
  80. package/dist/patterns/agent-chat/controller.js +182 -0
  81. package/dist/patterns/agent-chat/index.d.ts +3 -0
  82. package/dist/patterns/agent-chat/index.js +6 -0
  83. package/dist/patterns/agent-chat/types.d.ts +109 -0
  84. package/dist/patterns/agent-chat/types.js +1 -0
  85. package/dist/patterns/audit/activity-density.d.ts +68 -0
  86. package/dist/patterns/audit/activity-density.js +428 -0
  87. package/dist/patterns/audit/audit-log.d.ts +81 -0
  88. package/dist/patterns/audit/audit-log.js +794 -0
  89. package/dist/patterns/audit/index.d.ts +3 -0
  90. package/dist/patterns/audit/index.js +7 -0
  91. package/dist/patterns/audit/shared.d.ts +10 -0
  92. package/dist/patterns/audit/shared.js +50 -0
  93. package/dist/patterns/audit/types.d.ts +143 -0
  94. package/dist/patterns/audit/types.js +327 -0
  95. package/dist/patterns/content-explorer/actions/controller.js +12 -2
  96. package/dist/patterns/content-explorer/box-transport.js +117 -25
  97. package/dist/patterns/content-explorer/content-explorer.d.ts +13 -1
  98. package/dist/patterns/content-explorer/content-explorer.js +36 -3
  99. package/dist/patterns/content-explorer/contracts.d.ts +24 -1
  100. package/dist/patterns/content-explorer/contracts.js +68 -11
  101. package/dist/patterns/content-explorer/controller.d.ts +16 -1
  102. package/dist/patterns/content-explorer/controller.js +130 -4
  103. package/dist/patterns/content-explorer/navigation/controller.js +11 -9
  104. package/dist/patterns/content-explorer/types.d.ts +58 -1
  105. package/dist/patterns/content-picker/content-picker.d.ts +71 -0
  106. package/dist/patterns/content-picker/content-picker.js +747 -0
  107. package/dist/patterns/content-picker/controller.d.ts +38 -0
  108. package/dist/patterns/content-picker/controller.js +141 -0
  109. package/dist/patterns/content-picker/index.d.ts +3 -0
  110. package/dist/patterns/content-picker/index.js +6 -0
  111. package/dist/patterns/content-picker/types.d.ts +52 -0
  112. package/dist/patterns/content-picker/types.js +26 -0
  113. package/dist/patterns/content-sidebar/content-sidebar.d.ts +43 -0
  114. package/dist/patterns/content-sidebar/content-sidebar.js +293 -0
  115. package/dist/patterns/content-sidebar/index.d.ts +2 -0
  116. package/dist/patterns/content-sidebar/index.js +4 -0
  117. package/dist/patterns/content-sidebar/types.d.ts +18 -0
  118. package/dist/patterns/content-sidebar/types.js +20 -0
  119. package/dist/patterns/content-uploader/box-transport.d.ts +14 -0
  120. package/dist/patterns/content-uploader/box-transport.js +54 -0
  121. package/dist/patterns/content-uploader/content-uploader.d.ts +62 -0
  122. package/dist/patterns/content-uploader/content-uploader.js +559 -0
  123. package/dist/patterns/content-uploader/controller.d.ts +39 -0
  124. package/dist/patterns/content-uploader/controller.js +227 -0
  125. package/dist/patterns/content-uploader/index.d.ts +4 -0
  126. package/dist/patterns/content-uploader/index.js +8 -0
  127. package/dist/patterns/content-uploader/types.d.ts +120 -0
  128. package/dist/patterns/content-uploader/types.js +38 -0
  129. package/dist/patterns/diff/compare-view.d.ts +47 -0
  130. package/dist/patterns/diff/compare-view.js +276 -0
  131. package/dist/patterns/diff/diff-viewer.d.ts +42 -0
  132. package/dist/patterns/diff/diff-viewer.js +434 -0
  133. package/dist/patterns/diff/engine.d.ts +22 -0
  134. package/dist/patterns/diff/engine.js +276 -0
  135. package/dist/patterns/diff/index.d.ts +4 -0
  136. package/dist/patterns/diff/index.js +8 -0
  137. package/dist/patterns/diff/types.d.ts +69 -0
  138. package/dist/patterns/diff/types.js +24 -0
  139. package/dist/patterns/form-wizard/controller.d.ts +39 -0
  140. package/dist/patterns/form-wizard/controller.js +157 -0
  141. package/dist/patterns/form-wizard/form-wizard.d.ts +63 -0
  142. package/dist/patterns/form-wizard/form-wizard.js +468 -0
  143. package/dist/patterns/form-wizard/index.d.ts +4 -0
  144. package/dist/patterns/form-wizard/index.js +8 -0
  145. package/dist/patterns/form-wizard/types.d.ts +109 -0
  146. package/dist/patterns/form-wizard/types.js +83 -0
  147. package/dist/patterns/form-wizard/wizard-summary.d.ts +62 -0
  148. package/dist/patterns/form-wizard/wizard-summary.js +324 -0
  149. package/dist/patterns/internal/safe-href.d.ts +17 -0
  150. package/dist/patterns/internal/safe-href.js +17 -0
  151. package/dist/patterns/lineage/index.d.ts +3 -0
  152. package/dist/patterns/lineage/index.js +6 -0
  153. package/dist/patterns/lineage/lineage-graph.d.ts +30 -0
  154. package/dist/patterns/lineage/lineage-graph.js +434 -0
  155. package/dist/patterns/lineage/provenance-strip.d.ts +22 -0
  156. package/dist/patterns/lineage/provenance-strip.js +194 -0
  157. package/dist/patterns/lineage/types.d.ts +53 -0
  158. package/dist/patterns/lineage/types.js +49 -0
  159. package/dist/patterns/notifications/index.d.ts +3 -0
  160. package/dist/patterns/notifications/index.js +6 -0
  161. package/dist/patterns/notifications/notification-bell.d.ts +38 -0
  162. package/dist/patterns/notifications/notification-bell.js +198 -0
  163. package/dist/patterns/notifications/notification-inbox.d.ts +46 -0
  164. package/dist/patterns/notifications/notification-inbox.js +537 -0
  165. package/dist/patterns/notifications/types.d.ts +61 -0
  166. package/dist/patterns/notifications/types.js +108 -0
  167. package/dist/patterns/preview/annotation-inspector.d.ts +25 -6
  168. package/dist/patterns/preview/annotation-inspector.js +239 -94
  169. package/dist/patterns/preview/annotation-thread.d.ts +25 -5
  170. package/dist/patterns/preview/annotation-thread.js +233 -82
  171. package/dist/patterns/preview/annotation-toolbar.d.ts +20 -6
  172. package/dist/patterns/preview/annotation-toolbar.js +162 -96
  173. package/dist/patterns/preview/content-preview-adapter.d.ts +10 -1
  174. package/dist/patterns/preview/content-preview-adapter.js +26 -1
  175. package/dist/patterns/preview/preview-element.d.ts +15 -2
  176. package/dist/patterns/preview/preview-element.js +223 -52
  177. package/dist/patterns/preview/provider-adapter.d.ts +47 -0
  178. package/dist/patterns/preview/provider-adapter.js +17 -0
  179. package/dist/patterns/run/index.d.ts +2 -0
  180. package/dist/patterns/run/index.js +4 -0
  181. package/dist/patterns/run/run-trace.d.ts +40 -0
  182. package/dist/patterns/run/run-trace.js +441 -0
  183. package/dist/patterns/run/types.d.ts +66 -0
  184. package/dist/patterns/run/types.js +152 -0
  185. package/dist/patterns/signature/index.d.ts +2 -0
  186. package/dist/patterns/signature/index.js +4 -0
  187. package/dist/patterns/signature/signature-ceremony.d.ts +33 -0
  188. package/dist/patterns/signature/signature-ceremony.js +317 -0
  189. package/dist/patterns/signature/types.d.ts +68 -0
  190. package/dist/patterns/signature/types.js +89 -0
  191. package/dist/patterns/timeline/index.d.ts +2 -0
  192. package/dist/patterns/timeline/index.js +4 -0
  193. package/dist/patterns/timeline/timeline.d.ts +27 -0
  194. package/dist/patterns/timeline/timeline.js +430 -0
  195. package/dist/patterns/timeline/types.d.ts +36 -0
  196. package/dist/patterns/timeline/types.js +27 -0
  197. package/dist/patterns/versions/graph-layout.d.ts +43 -0
  198. package/dist/patterns/versions/graph-layout.js +159 -0
  199. package/dist/patterns/versions/index.d.ts +4 -0
  200. package/dist/patterns/versions/index.js +8 -0
  201. package/dist/patterns/versions/shared.d.ts +4 -0
  202. package/dist/patterns/versions/shared.js +13 -0
  203. package/dist/patterns/versions/types.d.ts +37 -0
  204. package/dist/patterns/versions/types.js +39 -0
  205. package/dist/patterns/versions/version-graph.d.ts +36 -0
  206. package/dist/patterns/versions/version-graph.js +438 -0
  207. package/dist/patterns/versions/version-list.d.ts +39 -0
  208. package/dist/patterns/versions/version-list.js +407 -0
  209. package/dist/patterns/work-queue/controller.d.ts +26 -0
  210. package/dist/patterns/work-queue/controller.js +150 -0
  211. package/dist/patterns/work-queue/index.d.ts +4 -0
  212. package/dist/patterns/work-queue/index.js +7 -0
  213. package/dist/patterns/work-queue/types.d.ts +128 -0
  214. package/dist/patterns/work-queue/types.js +45 -0
  215. package/dist/patterns/work-queue/work-queue.d.ts +48 -0
  216. package/dist/patterns/work-queue/work-queue.js +534 -0
  217. package/dist/patterns/work-queue/workload-board.d.ts +53 -0
  218. package/dist/patterns/work-queue/workload-board.js +608 -0
  219. package/package.json +89 -5
@@ -1,3 +1,4 @@
1
+ import { resolvePreviewStatus, } from "./provider-adapter.js";
1
2
  import { BaseElement } from "../../core/index.js";
2
3
  import { boePanel } from "../../foundations/geometry/index.js";
3
4
  const DEFAULT_TAG_NAME = "box-preview-element";
@@ -19,6 +20,7 @@ const elementStyles = `
19
20
  --_obp-text-muted: var(--boe-token-text-text-secondary, #6f6f6f);
20
21
  --_obp-brand: var(--boe-token-surface-surface-brand, #0061d5);
21
22
  --_obp-brand-soft: color-mix(in srgb, var(--_obp-brand) 12%, var(--boe-token-surface-surface, #ffffff) 88%);
23
+ --_obp-error: var(--boe-token-surface-status-surface-error, #ed3757);
22
24
  }
23
25
 
24
26
  [part="shell"] {
@@ -58,6 +60,20 @@ const elementStyles = `
58
60
  text-transform: uppercase;
59
61
  }
60
62
 
63
+ [part="status"][data-status="error"] {
64
+ background: color-mix(in srgb, var(--_obp-error) 14%, var(--_obp-surface) 86%);
65
+ color: var(--_obp-error);
66
+ }
67
+
68
+ [part="error"] {
69
+ margin: 0;
70
+ padding: 0.5rem 0.7rem;
71
+ border: 1px solid color-mix(in srgb, var(--_obp-error) 50%, #fff);
72
+ border-radius: 8px;
73
+ background: color-mix(in srgb, var(--_obp-error) 10%, #fff);
74
+ color: inherit;
75
+ }
76
+
61
77
  [part="adapter-meta"],
62
78
  [part="capabilities"] {
63
79
  display: flex;
@@ -105,7 +121,8 @@ const elementStyles = `
105
121
  gap: 0.5rem;
106
122
  }
107
123
 
108
- [part="action"] {
124
+ [part="action"],
125
+ [part="control"] {
109
126
  appearance: none;
110
127
  min-height: 2rem;
111
128
  padding: 0.4rem 0.7rem;
@@ -123,6 +140,24 @@ const elementStyles = `
123
140
  color: var(--boe-token-text-text-on-brand, #ffffff);
124
141
  }
125
142
 
143
+ [part="control"]:disabled {
144
+ opacity: 0.5;
145
+ cursor: not-allowed;
146
+ }
147
+
148
+ [part="controls"] {
149
+ display: flex;
150
+ flex-wrap: wrap;
151
+ align-items: center;
152
+ gap: 0.45rem;
153
+ }
154
+
155
+ [part="control-readout"] {
156
+ color: var(--_obp-text-muted);
157
+ font-size: 0.85rem;
158
+ font-variant-numeric: tabular-nums;
159
+ }
160
+
126
161
  [part="toolbar"] {
127
162
  display: block;
128
163
  }
@@ -134,6 +169,10 @@ const elementStyles = `
134
169
  align-items: start;
135
170
  }
136
171
 
172
+ [part="workspace"][data-sidebar="empty"] {
173
+ grid-template-columns: minmax(0, 1fr);
174
+ }
175
+
137
176
  [part="stage"] {
138
177
  min-block-size: 13rem;
139
178
  border: 1px solid var(--_obp-border-subtle);
@@ -142,11 +181,28 @@ const elementStyles = `
142
181
  overflow: hidden;
143
182
  }
144
183
 
184
+ [part="stage"][aria-busy="true"] {
185
+ opacity: 0.7;
186
+ }
187
+
188
+ [part="stage-mount"] {
189
+ width: 100%;
190
+ height: 100%;
191
+ }
192
+
193
+ [part="stage-mount"][hidden] {
194
+ display: none;
195
+ }
196
+
145
197
  [part="sidebar"] {
146
198
  display: grid;
147
199
  gap: 0.6rem;
148
200
  }
149
201
 
202
+ [part="workspace"][data-sidebar="empty"] [part="sidebar"] {
203
+ display: none;
204
+ }
205
+
150
206
  ::slotted([slot="toolbar"]) {
151
207
  display: block;
152
208
  }
@@ -171,6 +227,7 @@ export class Preview extends BaseElement {
171
227
  static tagName = DEFAULT_TAG_NAME;
172
228
  providerAdapterValue = null;
173
229
  providerAdapterUnsubscribe = null;
230
+ adapterMounted = false;
174
231
  static get observedAttributes() {
175
232
  return ["actions", "adapter-state", "item-label", "message", "provider", "provider-label", "status", "heading"];
176
233
  }
@@ -252,12 +309,8 @@ export class Preview extends BaseElement {
252
309
  set providerAdapter(value) {
253
310
  this.teardownProviderAdapter();
254
311
  this.providerAdapterValue = value;
255
- if (value?.subscribe) {
256
- this.providerAdapterUnsubscribe = value.subscribe(() => {
257
- if (this.isRendered) {
258
- this.update();
259
- }
260
- });
312
+ if (this.isConnected) {
313
+ this.attachProviderAdapter();
261
314
  }
262
315
  if (this.isRendered) {
263
316
  this.update();
@@ -265,13 +318,16 @@ export class Preview extends BaseElement {
265
318
  }
266
319
  connectedCallback() {
267
320
  super.connectedCallback();
321
+ // Re-attach after a detach: disconnectedCallback tears the subscription and
322
+ // mount down, so a reconnect must restore both or the element goes stale.
323
+ this.attachProviderAdapter();
324
+ if (this.isRendered) {
325
+ this.update();
326
+ }
268
327
  }
269
328
  disconnectedCallback() {
270
329
  this.teardownProviderAdapter();
271
330
  }
272
- attributeChangedCallback(name, oldValue, newValue) {
273
- super.attributeChangedCallback(name, oldValue, newValue);
274
- }
275
331
  parseJsonAttribute(name, fallback) {
276
332
  const raw = this.getAttribute(name);
277
333
  if (!raw) {
@@ -284,9 +340,36 @@ export class Preview extends BaseElement {
284
340
  return fallback;
285
341
  }
286
342
  }
343
+ attachProviderAdapter() {
344
+ const adapter = this.providerAdapterValue;
345
+ if (!adapter) {
346
+ return;
347
+ }
348
+ if (adapter.subscribe && !this.providerAdapterUnsubscribe) {
349
+ this.providerAdapterUnsubscribe = adapter.subscribe(() => {
350
+ if (this.isRendered) {
351
+ this.update();
352
+ }
353
+ });
354
+ }
355
+ const mountNode = this.shadowRoot?.querySelector('[part="stage-mount"]');
356
+ if (adapter.mount && mountNode && !this.adapterMounted) {
357
+ mountNode.hidden = false;
358
+ adapter.mount(mountNode);
359
+ this.adapterMounted = true;
360
+ }
361
+ }
287
362
  teardownProviderAdapter() {
288
363
  this.providerAdapterUnsubscribe?.();
289
364
  this.providerAdapterUnsubscribe = null;
365
+ if (this.adapterMounted) {
366
+ this.providerAdapterValue?.unmount?.();
367
+ this.adapterMounted = false;
368
+ const mountNode = this.shadowRoot?.querySelector('[part="stage-mount"]');
369
+ if (mountNode) {
370
+ mountNode.hidden = true;
371
+ }
372
+ }
290
373
  }
291
374
  getActiveProvider() {
292
375
  return this.providerAdapterValue?.getProvider() ?? this.provider;
@@ -303,13 +386,39 @@ export class Preview extends BaseElement {
303
386
  provider,
304
387
  providerId: provider?.id ?? null,
305
388
  };
306
- void this.providerAdapterValue?.performAction?.(detail);
389
+ Promise.resolve(this.providerAdapterValue?.performAction?.(detail)).catch((error) => {
390
+ this.dispatchEvent(new CustomEvent("action-error", {
391
+ bubbles: true,
392
+ composed: true,
393
+ detail: {
394
+ action: actionId,
395
+ message: error instanceof Error ? error.message : "Preview action failed",
396
+ },
397
+ }));
398
+ });
307
399
  this.dispatchEvent(new CustomEvent("action", {
308
400
  bubbles: true,
309
401
  composed: true,
310
402
  detail,
311
403
  }));
312
- this.dispatchEvent(new CustomEvent("provider-action", {
404
+ }
405
+ emitCommand(command) {
406
+ const provider = this.getActiveProvider();
407
+ const detail = {
408
+ command,
409
+ providerId: provider?.id ?? null,
410
+ };
411
+ Promise.resolve(this.providerAdapterValue?.sendCommand?.(command)).catch((error) => {
412
+ this.dispatchEvent(new CustomEvent("action-error", {
413
+ bubbles: true,
414
+ composed: true,
415
+ detail: {
416
+ action: command.command,
417
+ message: error instanceof Error ? error.message : "Preview command failed",
418
+ },
419
+ }));
420
+ });
421
+ this.dispatchEvent(new CustomEvent("command", {
313
422
  bubbles: true,
314
423
  composed: true,
315
424
  detail,
@@ -319,11 +428,58 @@ export class Preview extends BaseElement {
319
428
  if (!this.shadowRoot) {
320
429
  return;
321
430
  }
431
+ // Stable skeleton: the chrome host is re-rendered per update, while the
432
+ // workspace (adapter mount node + slots) keeps node identity so mounted
433
+ // preview engines and slotted content survive chrome updates.
322
434
  this.shadowRoot.innerHTML = `
323
435
  <style>${elementStyles}</style>
324
- <div part="content-host"></div>
436
+ <article part="shell">
437
+ <div part="content-host"></div>
438
+ <div part="toolbar">
439
+ <slot name="toolbar"></slot>
440
+ </div>
441
+ <div part="workspace" data-sidebar="empty">
442
+ <div part="stage">
443
+ <div part="stage-mount" hidden></div>
444
+ <slot name="stage"></slot>
445
+ </div>
446
+ <aside part="sidebar">
447
+ <slot name="sidebar"></slot>
448
+ </aside>
449
+ </div>
450
+ </article>
325
451
  `;
326
452
  }
453
+ setupListeners() {
454
+ if (!this.shadowRoot) {
455
+ return;
456
+ }
457
+ // Delegated once — chrome re-renders never re-bind listeners.
458
+ this.shadowRoot.querySelector('[part="content-host"]')?.addEventListener("click", event => {
459
+ const target = event.target.closest("[data-action-id], [data-command]");
460
+ if (!target || target.disabled) {
461
+ return;
462
+ }
463
+ const actionId = target.getAttribute("data-action-id");
464
+ if (actionId) {
465
+ this.emitAction(actionId);
466
+ return;
467
+ }
468
+ const command = target.getAttribute("data-command");
469
+ if (command) {
470
+ this.emitCommand({ command });
471
+ }
472
+ });
473
+ const sidebarSlot = this.shadowRoot.querySelector('slot[name="sidebar"]');
474
+ const workspace = this.shadowRoot.querySelector('[part="workspace"]');
475
+ const syncSidebar = () => {
476
+ const hasContent = (sidebarSlot?.assignedNodes({ flatten: true }) ?? []).some(node => node.nodeType === Node.ELEMENT_NODE || (node.textContent ?? "").trim() !== "");
477
+ workspace?.setAttribute("data-sidebar", hasContent ? "filled" : "empty");
478
+ };
479
+ sidebarSlot?.addEventListener("slotchange", syncSidebar);
480
+ syncSidebar();
481
+ this.attachProviderAdapter();
482
+ }
327
483
  update() {
328
484
  if (!this.shadowRoot) {
329
485
  return;
@@ -331,8 +487,15 @@ export class Preview extends BaseElement {
331
487
  const provider = this.getActiveProvider();
332
488
  const adapterState = this.getActiveAdapterState();
333
489
  const providerLabel = provider?.label ?? this.providerLabel;
334
- const providerStatus = provider?.status ?? this.status;
335
- const statusMarkup = providerStatus ? `<span part="status">${escapeHtml(providerStatus)}</span>` : "";
490
+ const lifecycle = resolvePreviewStatus(adapterState);
491
+ const statusLabel = provider?.status ?? this.status ?? "";
492
+ const statusText = statusLabel || (adapterState ? lifecycle : "");
493
+ const statusMarkup = statusText
494
+ ? `<span part="status" role="status" data-status="${escapeHtml(lifecycle)}">${escapeHtml(statusText)}</span>`
495
+ : "";
496
+ const errorMarkup = lifecycle === "error"
497
+ ? `<p part="error" role="alert">${escapeHtml(adapterState?.errorMessage ?? "Preview failed to load.")}</p>`
498
+ : "";
336
499
  const messageMarkup = this.message ? `<div part="message">${escapeHtml(this.message)}</div>` : "";
337
500
  const providerMarkup = providerLabel ? `<span part="provider">${escapeHtml(providerLabel)}</span>` : "";
338
501
  const itemMarkup = this.itemLabel ? `<div part="item-label">${escapeHtml(this.itemLabel)}</div>` : "";
@@ -346,9 +509,6 @@ export class Preview extends BaseElement {
346
509
  ${adapterState.currentAnnotationId
347
510
  ? `<span part="adapter-chip">${escapeHtml(`Annotation ${adapterState.currentAnnotationId}`)}</span>`
348
511
  : ""}
349
- ${typeof adapterState.ready === "boolean"
350
- ? `<span part="adapter-chip">${adapterState.ready ? "Ready" : "Connecting"}</span>`
351
- : ""}
352
512
  </div>
353
513
  `
354
514
  : "";
@@ -359,6 +519,7 @@ export class Preview extends BaseElement {
359
519
  </div>
360
520
  `
361
521
  : "";
522
+ const controlsMarkup = this.renderControls(adapterState);
362
523
  const actionsMarkup = this.actions.length
363
524
  ? `
364
525
  <div part="actions">
@@ -382,42 +543,52 @@ export class Preview extends BaseElement {
382
543
  return;
383
544
  }
384
545
  host.innerHTML = `
385
- <article part="shell">
386
- <div part="topline">
387
- <div part="topline-meta">
388
- ${providerMarkup}
389
- ${statusMarkup}
390
- </div>
391
- ${actionsMarkup}
392
- </div>
393
- <header part="header">
394
- <h2 part="title">${escapeHtml(this.heading)}</h2>
395
- ${itemMarkup}
396
- ${messageMarkup}
397
- ${adapterMarkup}
398
- ${capabilityMarkup}
399
- </header>
400
- <div part="toolbar">
401
- <slot name="toolbar"></slot>
546
+ <div part="topline">
547
+ <div part="topline-meta">
548
+ ${providerMarkup}
549
+ ${statusMarkup}
402
550
  </div>
403
- <div part="workspace">
404
- <div part="stage">
405
- <slot name="stage"></slot>
406
- </div>
407
- <aside part="sidebar">
408
- <slot name="sidebar"></slot>
409
- </aside>
410
- </div>
411
- </article>
551
+ ${actionsMarkup}
552
+ </div>
553
+ <header part="header">
554
+ <h2 part="title">${escapeHtml(this.heading)}</h2>
555
+ ${itemMarkup}
556
+ ${messageMarkup}
557
+ ${errorMarkup}
558
+ ${adapterMarkup}
559
+ ${capabilityMarkup}
560
+ ${controlsMarkup}
561
+ </header>
412
562
  `;
413
- this.shadowRoot.querySelectorAll('[part="action"]').forEach(button => {
414
- button.addEventListener("click", () => {
415
- const actionId = button.getAttribute("data-action-id");
416
- if (actionId) {
417
- this.emitAction(actionId);
418
- }
419
- });
420
- });
563
+ const stage = this.shadowRoot.querySelector('[part="stage"]');
564
+ stage?.setAttribute("aria-busy", lifecycle === "loading" ? "true" : "false");
565
+ }
566
+ /**
567
+ * Paging/zoom controls render only when the adapter accepts commands and the
568
+ * state carries the numeric fields to drive them.
569
+ */
570
+ renderControls(adapterState) {
571
+ if (!this.providerAdapterValue?.sendCommand || !adapterState) {
572
+ return "";
573
+ }
574
+ const segments = [];
575
+ if (typeof adapterState.page === "number" && typeof adapterState.pageCount === "number") {
576
+ const atFirst = adapterState.page <= 1;
577
+ const atLast = adapterState.page >= adapterState.pageCount;
578
+ segments.push(`
579
+ <button type="button" part="control" data-command="previous-page" aria-label="Previous page" ${atFirst ? "disabled" : ""}>&#8249;</button>
580
+ <span part="control-readout">Page ${adapterState.page} of ${adapterState.pageCount}</span>
581
+ <button type="button" part="control" data-command="next-page" aria-label="Next page" ${atLast ? "disabled" : ""}>&#8250;</button>
582
+ `);
583
+ }
584
+ if (typeof adapterState.zoomPercent === "number") {
585
+ segments.push(`
586
+ <button type="button" part="control" data-command="zoom-out" aria-label="Zoom out">&#8722;</button>
587
+ <span part="control-readout">${Math.round(adapterState.zoomPercent)}%</span>
588
+ <button type="button" part="control" data-command="zoom-in" aria-label="Zoom in">&#43;</button>
589
+ `);
590
+ }
591
+ return segments.length ? `<div part="controls" aria-label="Preview controls">${segments.join("")}</div>` : "";
421
592
  }
422
593
  }
423
594
  Preview.register();
@@ -5,13 +5,49 @@ export type PreviewProvider = {
5
5
  label: string;
6
6
  status?: string;
7
7
  };
8
+ /** Lifecycle of the previewed content, from nothing loaded to failure. */
9
+ export type PreviewStatus = "idle" | "loading" | "ready" | "error";
8
10
  export type PreviewAdapterState = {
9
11
  currentAnnotationId?: string;
10
12
  mode?: string;
13
+ /** Display-only label; prefer the numeric `page`/`pageCount` when driving UI. */
11
14
  pageLabel?: string;
15
+ /** Legacy readiness flag; prefer `status`. Kept for adapter compatibility. */
12
16
  ready?: boolean;
13
17
  selectionLabel?: string;
18
+ /** Display-only label; prefer the numeric `zoomPercent` when driving UI. */
14
19
  zoomLabel?: string;
20
+ /** Content lifecycle. When absent, derived from `ready` (see resolvePreviewStatus). */
21
+ status?: PreviewStatus;
22
+ /** Human-readable failure description; render when `status` is "error". */
23
+ errorMessage?: string;
24
+ /** 1-based current page for paged content. */
25
+ page?: number;
26
+ pageCount?: number;
27
+ /** Zoom level as a percentage (100 = actual size). */
28
+ zoomPercent?: number;
29
+ };
30
+ /**
31
+ * Effective lifecycle for a state object: an explicit `status` wins; otherwise
32
+ * the legacy `ready` flag maps true→ready / false→loading; no state → idle.
33
+ */
34
+ export declare const resolvePreviewStatus: (state: PreviewAdapterState | null | undefined) => PreviewStatus;
35
+ /**
36
+ * A typed command the host UI sends INTO the preview engine. Known commands
37
+ * carry their payload shape; the string fallback keeps the channel open for
38
+ * engine-specific extensions.
39
+ */
40
+ export type PreviewCommand = {
41
+ command: "next-page" | "previous-page" | "zoom-in" | "zoom-out" | "toggle-fullscreen" | "download" | "print";
42
+ } | {
43
+ command: "go-to-page";
44
+ value: number;
45
+ } | {
46
+ command: "set-zoom";
47
+ value: number;
48
+ } | {
49
+ command: string;
50
+ value?: unknown;
15
51
  };
16
52
  export type PreviewProviderActionDetail = {
17
53
  action: string;
@@ -24,6 +60,16 @@ export type PreviewProviderAdapter = {
24
60
  getState: () => PreviewAdapterState | null;
25
61
  performAction?: (detail: PreviewProviderActionDetail) => void | Promise<void>;
26
62
  subscribe?: (listener: () => void) => () => void;
63
+ /**
64
+ * Hand the adapter a stable container to render the preview engine into.
65
+ * `box-preview-element` calls this with its stage node when present; the
66
+ * node survives shell re-renders, so mounted DOM is never destroyed by
67
+ * chrome updates.
68
+ */
69
+ mount?: (container: HTMLElement) => void;
70
+ unmount?: () => void;
71
+ /** Drive the engine (paging, zoom, download, …) with a typed command. */
72
+ sendCommand?: (command: PreviewCommand) => void | Promise<void>;
27
73
  };
28
74
  export type StaticPreviewProviderAdapter = PreviewProviderAdapter & {
29
75
  setProvider: (provider: PreviewProvider | null) => void;
@@ -31,6 +77,7 @@ export type StaticPreviewProviderAdapter = PreviewProviderAdapter & {
31
77
  };
32
78
  export declare const createStaticPreviewProviderAdapter: (config: {
33
79
  onAction?: (detail: PreviewProviderActionDetail) => void | Promise<void>;
80
+ onCommand?: (command: PreviewCommand) => void | Promise<void>;
34
81
  provider?: PreviewProvider | null;
35
82
  state?: PreviewAdapterState | null;
36
83
  }) => StaticPreviewProviderAdapter;
@@ -1,3 +1,19 @@
1
+ /**
2
+ * Effective lifecycle for a state object: an explicit `status` wins; otherwise
3
+ * the legacy `ready` flag maps true→ready / false→loading; no state → idle.
4
+ */
5
+ export const resolvePreviewStatus = (state) => {
6
+ if (!state) {
7
+ return "idle";
8
+ }
9
+ if (state.status) {
10
+ return state.status;
11
+ }
12
+ if (typeof state.ready === "boolean") {
13
+ return state.ready ? "ready" : "loading";
14
+ }
15
+ return "idle";
16
+ };
1
17
  export const createStaticPreviewProviderAdapter = (config) => {
2
18
  let provider = config.provider ?? null;
3
19
  let state = config.state ?? null;
@@ -11,6 +27,7 @@ export const createStaticPreviewProviderAdapter = (config) => {
11
27
  getProvider: () => provider,
12
28
  getState: () => state,
13
29
  performAction: config.onAction,
30
+ ...(config.onCommand ? { sendCommand: config.onCommand } : {}),
14
31
  setProvider(nextProvider) {
15
32
  provider = nextProvider;
16
33
  notify();
@@ -0,0 +1,2 @@
1
+ export * from "./types.js";
2
+ export * from "./run-trace.js";
@@ -0,0 +1,4 @@
1
+ // Run model, rules, and validation
2
+ export * from "./types.js";
3
+ // Machine execution trace surface
4
+ export * from "./run-trace.js";
@@ -0,0 +1,40 @@
1
+ import type { RunResolution, RunStep, RunStepStatus } from "./types.js";
2
+ import { BaseElement } from "../../core/index.js";
3
+ /** Every status in words — colour never carries the meaning alone. */
4
+ export declare const RUN_STEP_STATUS_LABEL: Record<RunStepStatus, string>;
5
+ /**
6
+ * Machine execution trace: a job, pipeline, or agent run, top-down. The
7
+ * rules live in `resolveRunSteps` (pure), the same way `resolveCeremony`
8
+ * backs the signature card: an explicit status wins, a failure shadows the
9
+ * queue behind it as skipped, timestamps derive running/succeeded, and the
10
+ * host can drive its notifications from the identical function.
11
+ *
12
+ * Deliberately not `box-timeline` (the human activity feed): a run reads
13
+ * forward, has one step in flight, and takes no comments.
14
+ *
15
+ * The summary chip doubles as a polite status region, so a run driven by
16
+ * attribute updates announces "Running Deploy — step 3 of 5" → "Completed"
17
+ * without extra wiring. Per-step detail expands in place: `description`
18
+ * plus a `detail-<id>` slot for rich host content, plus child tasks with
19
+ * live `box-progress-bar` rows.
20
+ */
21
+ export declare class RunTrace extends BaseElement {
22
+ static readonly tagName: string;
23
+ static get observedAttributes(): string[];
24
+ private hostEl;
25
+ private stepsRaw;
26
+ private stepsCache;
27
+ private readonly expandedIds;
28
+ get heading(): string;
29
+ set heading(value: string);
30
+ get steps(): RunStep[];
31
+ set steps(value: RunStep[]);
32
+ /** The resolved run — same object the render uses. */
33
+ get resolution(): RunResolution;
34
+ /** Ids of the steps whose detail is expanded (host-readable). */
35
+ get expandedSteps(): string[];
36
+ protected renderTemplate(): void;
37
+ protected setupListeners(): void;
38
+ private childrenMarkup;
39
+ protected update(): void;
40
+ }