@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
@@ -0,0 +1,407 @@
1
+ import { orderVersionsForDisplay } from "./graph-layout.js";
2
+ import { escapeHtml, STATUS_LABELS } from "./shared.js";
3
+ import { isVersionNodeRecord, resolveVersionKind, resolveVersionStatus } from "./types.js";
4
+ import { formatItemDate } from "../content-explorer/adapters/item-summary.js";
5
+ import { BaseElement } from "../../core/index.js";
6
+ import { boeMotionDuration, boeMotionEasing } from "../../foundations/motion/index.js";
7
+ import { boePanel, boeRadius } from "../../foundations/geometry/index.js";
8
+ const DEFAULT_TAG_NAME = "box-version-list";
9
+ const elementStyles = `
10
+ [hidden] {
11
+ display: none !important;
12
+ }
13
+
14
+ :host {
15
+ display: block;
16
+ color: inherit;
17
+ font: inherit;
18
+ }
19
+
20
+ section[part="panel"] {
21
+ display: grid;
22
+ gap: ${boePanel.gap};
23
+ padding: ${boePanel.padding};
24
+ border: 1px solid color-mix(in srgb, var(--boe-token-stroke-stroke, #e8e8e8) 82%, transparent);
25
+ border-radius: ${boePanel.radius};
26
+ background: ${boePanel.background};
27
+ }
28
+
29
+ [part="title"] {
30
+ margin: 0;
31
+ font: inherit;
32
+ font-size: 1.1rem;
33
+ font-weight: 700;
34
+ color: var(--boe-token-text-text, #1f1e1b);
35
+ }
36
+
37
+ [part="rows"] {
38
+ list-style: none;
39
+ margin: 0;
40
+ padding: 0;
41
+ display: grid;
42
+ gap: 0.45rem;
43
+ }
44
+
45
+ [part="row"] {
46
+ display: grid;
47
+ gap: 0.3rem;
48
+ padding: 0.55rem 0.6rem;
49
+ border: 1px solid color-mix(in srgb, var(--boe-token-stroke-stroke, #e8e8e8) 52%, transparent);
50
+ border-radius: ${boeRadius.large};
51
+ background: color-mix(in srgb, var(--boe-token-surface-surface, #ffffff) 82%, transparent);
52
+ }
53
+
54
+ [part="row"][data-status="current"] {
55
+ border-color: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 45%, transparent);
56
+ }
57
+
58
+ [part="row"][data-compare-selected="true"] {
59
+ background: var(--boe-token-surface-item-surface-selected, #f2f7fd);
60
+ }
61
+
62
+ [part="row-header"] {
63
+ display: flex;
64
+ flex-wrap: wrap;
65
+ align-items: center;
66
+ gap: 0.45rem;
67
+ }
68
+
69
+ [part="marker"] {
70
+ inline-size: 0.7rem;
71
+ block-size: 0.7rem;
72
+ border-radius: 999px;
73
+ border: 2px solid var(--boe-token-text-text-secondary, #6f6f6f);
74
+ background: transparent;
75
+ }
76
+
77
+ [part="marker"][data-kind="major"],
78
+ [part="marker"][data-kind="merge"] {
79
+ background: var(--boe-token-text-text-secondary, #6f6f6f);
80
+ }
81
+
82
+ [part="row"][data-status="current"] [part="marker"] {
83
+ border-color: var(--boe-token-surface-surface-brand, #0061d5);
84
+ background: var(--boe-token-surface-surface-brand, #0061d5);
85
+ }
86
+
87
+ [part="marker"][data-kind="draft"] {
88
+ border-style: dashed;
89
+ }
90
+
91
+ [part="row-title"] {
92
+ appearance: none;
93
+ border: none;
94
+ background: transparent;
95
+ padding: 0;
96
+ font: inherit;
97
+ font-weight: 700;
98
+ color: var(--boe-token-text-text, #1f1e1b);
99
+ text-align: left;
100
+ cursor: pointer;
101
+ }
102
+
103
+ [part="row-title"]:hover {
104
+ color: var(--boe-token-surface-surface-brand, #0061d5);
105
+ }
106
+
107
+ [part="row-title"]:focus-visible {
108
+ outline: none;
109
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 18%, transparent);
110
+ border-radius: ${boeRadius.med};
111
+ }
112
+
113
+ [part="status"] {
114
+ display: inline-flex;
115
+ padding: 0.14rem 0.45rem;
116
+ border-radius: 999px;
117
+ background: var(--boe-token-surface-item-surface-selected, #f2f7fd);
118
+ color: var(--boe-token-text-text-secondary, #6f6f6f);
119
+ font-size: 0.74rem;
120
+ font-weight: 700;
121
+ }
122
+
123
+ [part="status"][data-status="current"] {
124
+ background: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 14%, transparent);
125
+ color: var(--boe-token-surface-surface-brand, #0061d5);
126
+ }
127
+
128
+ [part="status"][data-status="executed"] {
129
+ background: color-mix(in srgb, var(--boe-token-surface-status-surface-success, #26c281) 16%, transparent);
130
+ color: color-mix(in srgb, var(--boe-token-surface-status-surface-success, #26c281) 52%, black 48%);
131
+ }
132
+
133
+ [part="status"][data-status="abandoned"] {
134
+ background: color-mix(in srgb, var(--boe-token-surface-status-surface-error, #ed3757) 12%, transparent);
135
+ color: color-mix(in srgb, var(--boe-token-surface-status-surface-error, #ed3757) 74%, black 26%);
136
+ }
137
+
138
+ [part="meta"] {
139
+ display: flex;
140
+ flex-wrap: wrap;
141
+ align-items: center;
142
+ gap: 0.45rem;
143
+ font-size: 0.8rem;
144
+ color: var(--boe-token-text-text-secondary, #6f6f6f);
145
+ }
146
+
147
+ [part="note"] {
148
+ margin: 0;
149
+ font-size: 0.84rem;
150
+ color: var(--boe-token-text-text-secondary, #6f6f6f);
151
+ }
152
+
153
+ [part="row-actions"] {
154
+ display: flex;
155
+ flex-wrap: wrap;
156
+ gap: 0.4rem;
157
+ }
158
+
159
+ [part="row-action"] {
160
+ appearance: none;
161
+ font: inherit;
162
+ font-size: 0.76rem;
163
+ font-weight: 600;
164
+ padding: 0.26rem 0.6rem;
165
+ border-radius: 999px;
166
+ border: 1px solid color-mix(in srgb, var(--boe-token-stroke-stroke, #e8e8e8) 60%, transparent);
167
+ background: var(--boe-token-surface-surface, #ffffff);
168
+ color: inherit;
169
+ cursor: pointer;
170
+ transition: background ${boeMotionDuration.interactive} ${boeMotionEasing.standard};
171
+ }
172
+
173
+ [part="row-action"]:hover {
174
+ background: var(--boe-token-surface-surface-hover, #f4f4f4);
175
+ }
176
+
177
+ [part="row-action"][aria-pressed="true"] {
178
+ border-color: var(--boe-token-surface-surface-brand, #0061d5);
179
+ color: var(--boe-token-surface-surface-brand, #0061d5);
180
+ }
181
+
182
+ [part="row-action"]:focus-visible {
183
+ outline: none;
184
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 18%, transparent);
185
+ }
186
+
187
+ [part="empty"] {
188
+ padding: ${boePanel.padding};
189
+ border-radius: ${boeRadius.large};
190
+ border: 1px dashed color-mix(in srgb, var(--boe-token-stroke-stroke, #e8e8e8) 70%, transparent);
191
+ color: var(--boe-token-text-text-secondary, #6f6f6f);
192
+ }
193
+ `;
194
+ /**
195
+ * The accessible core of the versions surface (CLM gap 5): a topologically
196
+ * ordered, newest-first version history with status tones, two-version
197
+ * compare pairing, and restore/promote surfaced as intent events for the
198
+ * host's confirm-before-apply flow. `box-version-graph` renders the same
199
+ * model as a git-style network; this list is the contract, the graph is
200
+ * progressive enhancement.
201
+ */
202
+ export class VersionList extends BaseElement {
203
+ static tagName = DEFAULT_TAG_NAME;
204
+ static get observedAttributes() {
205
+ return ["can-promote", "can-restore", "heading", "versions"];
206
+ }
207
+ versionsValue = [];
208
+ compareIds = [];
209
+ hostEl;
210
+ get heading() {
211
+ return this.getAttribute("heading") ?? "Version history";
212
+ }
213
+ set heading(value) {
214
+ this.setAttribute("heading", value);
215
+ }
216
+ /** Enables the per-row Restore intent button (non-current rows). */
217
+ get canRestore() {
218
+ return this.hasAttribute("can-restore");
219
+ }
220
+ set canRestore(value) {
221
+ this.toggleAttribute("can-restore", value);
222
+ }
223
+ /** Enables the per-row Promote intent button (non-current rows). */
224
+ get canPromote() {
225
+ return this.hasAttribute("can-promote");
226
+ }
227
+ set canPromote(value) {
228
+ this.toggleAttribute("can-promote", value);
229
+ }
230
+ get versions() {
231
+ return this.versionsValue;
232
+ }
233
+ set versions(value) {
234
+ this.versionsValue = Array.isArray(value) ? value.filter(isVersionNodeRecord) : [];
235
+ this.pruneCompareSelection();
236
+ if (this.isRendered) {
237
+ this.update();
238
+ }
239
+ }
240
+ /** Ids currently toggled for compare, in selection order (max two). */
241
+ get compareSelection() {
242
+ return [...this.compareIds];
243
+ }
244
+ clearCompareSelection() {
245
+ this.compareIds = [];
246
+ if (this.isRendered) {
247
+ this.update();
248
+ }
249
+ }
250
+ attributeChangedCallback(name, oldValue, newValue) {
251
+ if (name === "versions") {
252
+ this.versionsValue = this.parseVersionsAttribute(newValue);
253
+ this.pruneCompareSelection();
254
+ }
255
+ super.attributeChangedCallback(name, oldValue, newValue);
256
+ }
257
+ parseVersionsAttribute(raw) {
258
+ if (!raw) {
259
+ return [];
260
+ }
261
+ try {
262
+ const parsed = JSON.parse(raw);
263
+ return Array.isArray(parsed) ? parsed.filter(isVersionNodeRecord) : [];
264
+ }
265
+ catch {
266
+ return [];
267
+ }
268
+ }
269
+ pruneCompareSelection() {
270
+ const known = new Set(this.versionsValue.map(node => node.id));
271
+ this.compareIds = this.compareIds.filter(id => known.has(id));
272
+ }
273
+ getVersion(id) {
274
+ return this.versionsValue.find(node => node.id === id);
275
+ }
276
+ toggleCompare(id) {
277
+ if (this.compareIds.includes(id)) {
278
+ this.compareIds = this.compareIds.filter(other => other !== id);
279
+ }
280
+ else {
281
+ this.compareIds = [...this.compareIds, id].slice(-2);
282
+ }
283
+ if (this.compareIds.length === 2) {
284
+ const ordered = orderVersionsForDisplay(this.versionsValue).map(node => node.id);
285
+ // Display order is newest first, so the later index is the older side.
286
+ const [a, b] = this.compareIds;
287
+ const older = ordered.indexOf(a) > ordered.indexOf(b) ? a : b;
288
+ const newer = older === a ? b : a;
289
+ this.dispatchEvent(new CustomEvent("compare-requested", {
290
+ bubbles: true,
291
+ composed: true,
292
+ detail: { baseId: older, targetId: newer },
293
+ }));
294
+ }
295
+ this.update();
296
+ }
297
+ rowHtml(node) {
298
+ const kind = resolveVersionKind(node.kind);
299
+ const status = resolveVersionStatus(node.status);
300
+ const isCurrent = status === "current";
301
+ const compareSelected = this.compareIds.includes(node.id);
302
+ const when = node.timestamp ? formatItemDate(node.timestamp) : "";
303
+ const actions = [
304
+ `<button type="button" part="row-action" data-action="compare" data-id="${escapeHtml(node.id)}" aria-pressed="${compareSelected ? "true" : "false"}">Compare</button>`,
305
+ ];
306
+ if (this.canRestore && !isCurrent) {
307
+ actions.push(`<button type="button" part="row-action" data-action="restore" data-id="${escapeHtml(node.id)}">Restore</button>`);
308
+ }
309
+ if (this.canPromote && !isCurrent) {
310
+ actions.push(`<button type="button" part="row-action" data-action="promote" data-id="${escapeHtml(node.id)}">Promote</button>`);
311
+ }
312
+ return `
313
+ <li part="row" data-id="${escapeHtml(node.id)}" data-kind="${kind}"${status ? ` data-status="${status}"` : ""} data-compare-selected="${compareSelected ? "true" : "false"}">
314
+ <div part="row-header">
315
+ <span part="marker" data-kind="${kind}" aria-hidden="true"></span>
316
+ <button type="button" part="row-title" data-id="${escapeHtml(node.id)}">${escapeHtml(node.label)}</button>
317
+ ${status ? `<span part="status" data-status="${status}">${STATUS_LABELS[status]}</span>` : ""}
318
+ </div>
319
+ <div part="meta">
320
+ ${node.actor ? `<span part="actor">${escapeHtml(node.actor.name)}</span>` : ""}
321
+ ${when ? `<time part="when" datetime="${escapeHtml(node.timestamp ?? "")}">${escapeHtml(when)}</time>` : ""}
322
+ </div>
323
+ ${node.note ? `<p part="note">${escapeHtml(node.note)}</p>` : ""}
324
+ <div part="row-actions">${actions.join("")}</div>
325
+ </li>
326
+ `;
327
+ }
328
+ renderTemplate() {
329
+ if (!this.shadowRoot) {
330
+ return;
331
+ }
332
+ this.shadowRoot.innerHTML = `
333
+ <style>${elementStyles}</style>
334
+ <div part="content-host"></div>
335
+ `;
336
+ this.hostEl = this.shadowRoot.querySelector('[part="content-host"]');
337
+ }
338
+ setupListeners() {
339
+ this.hostEl.addEventListener("click", event => {
340
+ const target = event.target;
341
+ const actionButton = target.closest('[part="row-action"]');
342
+ if (actionButton && this.hostEl.contains(actionButton)) {
343
+ const id = actionButton.getAttribute("data-id") ?? "";
344
+ const action = actionButton.getAttribute("data-action") ?? "";
345
+ const version = this.getVersion(id);
346
+ if (!version) {
347
+ return;
348
+ }
349
+ if (action === "compare") {
350
+ this.toggleCompare(id);
351
+ }
352
+ else if (action === "restore" || action === "promote") {
353
+ // Restore/promote mutate governed records — the host owns the
354
+ // confirm-before-apply flow, so these only surface intent.
355
+ this.dispatchEvent(new CustomEvent(`${action}-requested`, {
356
+ bubbles: true,
357
+ composed: true,
358
+ detail: { version },
359
+ }));
360
+ }
361
+ return;
362
+ }
363
+ const titleButton = target.closest('[part="row-title"]');
364
+ if (titleButton && this.hostEl.contains(titleButton)) {
365
+ const version = this.getVersion(titleButton.getAttribute("data-id") ?? "");
366
+ if (version) {
367
+ this.dispatchEvent(new CustomEvent("version-selected", {
368
+ bubbles: true,
369
+ composed: true,
370
+ detail: { version },
371
+ }));
372
+ }
373
+ }
374
+ });
375
+ }
376
+ update() {
377
+ if (!this.hostEl) {
378
+ return;
379
+ }
380
+ const active = this.shadowRoot?.activeElement;
381
+ const focusKey = active && this.hostEl.contains(active)
382
+ ? {
383
+ part: active.getAttribute("part") ?? "",
384
+ id: active.getAttribute("data-id"),
385
+ action: active.getAttribute("data-action"),
386
+ }
387
+ : null;
388
+ const ordered = orderVersionsForDisplay(this.versionsValue);
389
+ this.hostEl.innerHTML = `
390
+ <section part="panel" aria-label="${escapeHtml(this.heading)}">
391
+ <h2 part="title">${escapeHtml(this.heading)}</h2>
392
+ ${ordered.length > 0
393
+ ? `<ol part="rows" role="list">${ordered.map(node => this.rowHtml(node)).join("")}</ol>`
394
+ : `<div part="empty">No versions.</div>`}
395
+ </section>
396
+ `;
397
+ if (focusKey?.part) {
398
+ const target = Array.from(this.hostEl.querySelectorAll(`[part="${focusKey.part}"]`)).find(node => node.getAttribute("data-id") === focusKey.id &&
399
+ (!focusKey.action || node.getAttribute("data-action") === focusKey.action));
400
+ // The rebuild removed the focused control's version (or its action):
401
+ // keep keyboard focus inside the component instead of dropping to body.
402
+ const fallback = this.hostEl.querySelector('[part="row-title"], [part="row-action"]');
403
+ (target ?? fallback)?.focus();
404
+ }
405
+ }
406
+ }
407
+ VersionList.register();
@@ -0,0 +1,26 @@
1
+ import { Controller } from "../../core/controller.js";
2
+ import type { WorkItem, WorkQueueEvents, WorkQueueFilters, WorkQueueSessionConfig, WorkQueueState } from "./types.js";
3
+ /**
4
+ * Headless work-queue session shared by both projections (`box-work-queue`
5
+ * and `box-workload-board`): filtered loads with abort-superseded requests,
6
+ * and the four governed mutations (claim / reassign / complete / escalate)
7
+ * with reload-on-success — the transport owns the actual state transition.
8
+ */
9
+ export declare class WorkQueueController extends Controller<WorkQueueState, WorkQueueEvents> {
10
+ readonly config: WorkQueueSessionConfig;
11
+ private activeLoadRequestId;
12
+ private activeLoadAbortController;
13
+ constructor(config: WorkQueueSessionConfig);
14
+ connect(): Promise<void>;
15
+ disconnect(): void;
16
+ /** Merge a filter patch (undefined values clear a filter) and reload. */
17
+ setFilters(patch: WorkQueueFilters): Promise<void>;
18
+ reload(): Promise<void>;
19
+ getItem(itemId: string): WorkItem | undefined;
20
+ /** Claim an item for `assigneeId` (typically the current user). */
21
+ claimItem(itemId: string, assigneeId: string): Promise<WorkItem | null>;
22
+ reassignItem(itemId: string, assigneeId: string): Promise<WorkItem | null>;
23
+ completeItem(itemId: string): Promise<WorkItem | null>;
24
+ escalateItem(itemId: string, reason?: string): Promise<WorkItem | null>;
25
+ private runMutation;
26
+ }
@@ -0,0 +1,150 @@
1
+ import { Controller } from "../../core/controller.js";
2
+ const createInitialState = (config) => ({
3
+ connected: false,
4
+ loading: false,
5
+ items: [],
6
+ filters: { ...(config.filters ?? {}) },
7
+ error: null,
8
+ });
9
+ /**
10
+ * Headless work-queue session shared by both projections (`box-work-queue`
11
+ * and `box-workload-board`): filtered loads with abort-superseded requests,
12
+ * and the four governed mutations (claim / reassign / complete / escalate)
13
+ * with reload-on-success — the transport owns the actual state transition.
14
+ */
15
+ export class WorkQueueController extends Controller {
16
+ config;
17
+ activeLoadRequestId = 0;
18
+ activeLoadAbortController = null;
19
+ constructor(config) {
20
+ super(createInitialState(config));
21
+ this.config = config;
22
+ }
23
+ async connect() {
24
+ if (this.state.connected) {
25
+ return;
26
+ }
27
+ this.setState({ ...this.state, connected: true });
28
+ this.emit("connected", undefined);
29
+ await this.reload();
30
+ }
31
+ disconnect() {
32
+ if (!this.state.connected) {
33
+ return;
34
+ }
35
+ this.activeLoadRequestId += 1;
36
+ this.activeLoadAbortController?.abort();
37
+ this.activeLoadAbortController = null;
38
+ this.setState(createInitialState(this.config));
39
+ this.emit("disconnected", undefined);
40
+ }
41
+ /** Merge a filter patch (undefined values clear a filter) and reload. */
42
+ async setFilters(patch) {
43
+ const filters = { ...this.state.filters };
44
+ for (const [key, value] of Object.entries(patch)) {
45
+ if (value === undefined) {
46
+ delete filters[key];
47
+ }
48
+ else {
49
+ filters[key] = value;
50
+ }
51
+ }
52
+ this.setState({ ...this.state, filters });
53
+ this.emit("filtersChanged", { filters });
54
+ if (this.state.connected) {
55
+ await this.reload();
56
+ }
57
+ }
58
+ async reload() {
59
+ if (!this.state.connected) {
60
+ return;
61
+ }
62
+ const requestId = this.activeLoadRequestId + 1;
63
+ this.activeLoadRequestId = requestId;
64
+ this.activeLoadAbortController?.abort();
65
+ const abortController = typeof AbortController === "function" ? new AbortController() : null;
66
+ this.activeLoadAbortController = abortController;
67
+ this.setState({ ...this.state, loading: true, error: null });
68
+ this.emit("loadingChanged", { loading: true });
69
+ try {
70
+ const result = await this.config.transport.loadItems({
71
+ filters: this.state.filters,
72
+ token: this.config.token,
73
+ ...(abortController ? { signal: abortController.signal } : {}),
74
+ });
75
+ if (requestId !== this.activeLoadRequestId) {
76
+ return;
77
+ }
78
+ this.setState({ ...this.state, items: result.items, loading: false });
79
+ this.emit("loadingChanged", { loading: false });
80
+ this.emit("itemsChanged", { items: result.items });
81
+ }
82
+ catch (error) {
83
+ if (requestId !== this.activeLoadRequestId) {
84
+ return;
85
+ }
86
+ const message = error instanceof Error ? error.message : "Failed to load work items";
87
+ this.setState({ ...this.state, loading: false, error: message });
88
+ this.emit("loadingChanged", { loading: false });
89
+ this.emit("loadFailed", { message });
90
+ }
91
+ finally {
92
+ if (this.activeLoadAbortController === abortController) {
93
+ this.activeLoadAbortController = null;
94
+ }
95
+ }
96
+ }
97
+ getItem(itemId) {
98
+ return this.state.items.find(item => item.id === itemId);
99
+ }
100
+ /** Claim an item for `assigneeId` (typically the current user). */
101
+ async claimItem(itemId, assigneeId) {
102
+ const transport = this.config.transport;
103
+ if (!transport.claimItem) {
104
+ throw new Error("Work-queue transport does not support claim");
105
+ }
106
+ // Called on the transport so class-based implementations keep `this`.
107
+ return this.runMutation("claim", itemId, () => transport.claimItem({ itemId, assigneeId, token: this.config.token }));
108
+ }
109
+ async reassignItem(itemId, assigneeId) {
110
+ const transport = this.config.transport;
111
+ if (!transport.reassignItem) {
112
+ throw new Error("Work-queue transport does not support reassign");
113
+ }
114
+ // Called on the transport so class-based implementations keep `this`.
115
+ return this.runMutation("reassign", itemId, () => transport.reassignItem({ itemId, assigneeId, token: this.config.token }));
116
+ }
117
+ async completeItem(itemId) {
118
+ const transport = this.config.transport;
119
+ if (!transport.completeItem) {
120
+ throw new Error("Work-queue transport does not support complete");
121
+ }
122
+ // Called on the transport so class-based implementations keep `this`.
123
+ return this.runMutation("complete", itemId, () => transport.completeItem({ itemId, token: this.config.token }));
124
+ }
125
+ async escalateItem(itemId, reason) {
126
+ const transport = this.config.transport;
127
+ if (!transport.escalateItem) {
128
+ throw new Error("Work-queue transport does not support escalate");
129
+ }
130
+ // Called on the transport so class-based implementations keep `this`.
131
+ return this.runMutation("escalate", itemId, () => transport.escalateItem({ itemId, reason, token: this.config.token }));
132
+ }
133
+ async runMutation(kind, itemId, perform) {
134
+ if (!this.state.connected || !this.getItem(itemId)) {
135
+ return null;
136
+ }
137
+ try {
138
+ const item = await perform();
139
+ this.emit("itemMutated", { kind, item });
140
+ await this.reload();
141
+ return item;
142
+ }
143
+ catch (error) {
144
+ const message = error instanceof Error ? error.message : `Work-queue ${kind} failed`;
145
+ this.setState({ ...this.state, error: message });
146
+ this.emit("mutationFailed", { kind, itemId, message });
147
+ return null;
148
+ }
149
+ }
150
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./types.js";
2
+ export * from "./controller.js";
3
+ export * from "./work-queue.js";
4
+ export * from "./workload-board.js";
@@ -0,0 +1,7 @@
1
+ // Work-item model, transport contract, buckets, and workload summaries
2
+ export * from "./types.js";
3
+ // Headless queue session shared by both projections
4
+ export * from "./controller.js";
5
+ // Composed surfaces: individual triage list + supervisor board
6
+ export * from "./work-queue.js";
7
+ export * from "./workload-board.js";