@tensor_1/react 2.11.0 → 2.13.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 (65) hide show
  1. package/README.md +26 -1
  2. package/dist/ChartCore-Bt1C84ty.d.cts +22 -0
  3. package/dist/calendar-model.d.ts +31 -0
  4. package/dist/calendar-model.js +89 -0
  5. package/dist/chart-preset.cjs +168 -0
  6. package/dist/chart-preset.d.cts +9 -0
  7. package/dist/chart-preset.d.ts +9 -0
  8. package/dist/chart-preset.js +35 -0
  9. package/dist/chart.cjs +58 -32
  10. package/dist/chart.d.cts +6 -21
  11. package/dist/chart.d.ts +2 -1
  12. package/dist/color-model.d.ts +20 -0
  13. package/dist/color-model.js +74 -0
  14. package/dist/components/BrandProfiles.d.ts +45 -0
  15. package/dist/components/BrandProfiles.js +150 -0
  16. package/dist/components/BuilderCanvas.d.ts +34 -0
  17. package/dist/components/BuilderCanvas.js +117 -0
  18. package/dist/components/Chart.d.ts +6 -21
  19. package/dist/components/Chart.js +5 -76
  20. package/dist/components/ChartCore.d.ts +33 -0
  21. package/dist/components/ChartCore.js +100 -0
  22. package/dist/components/ColorPicker.d.ts +17 -0
  23. package/dist/components/ColorPicker.js +158 -0
  24. package/dist/components/DateRangePicker.d.ts +23 -0
  25. package/dist/components/DateRangePicker.js +123 -0
  26. package/dist/components/DiffViewer.d.ts +18 -0
  27. package/dist/components/DiffViewer.js +93 -0
  28. package/dist/components/Inbox.js +0 -1
  29. package/dist/components/LicensePositionBar.d.ts +17 -0
  30. package/dist/components/LicensePositionBar.js +50 -0
  31. package/dist/components/MatchReview.d.ts +39 -0
  32. package/dist/components/MatchReview.js +120 -0
  33. package/dist/components/Meter.js +0 -1
  34. package/dist/components/MetricInput.d.ts +23 -0
  35. package/dist/components/MetricInput.js +92 -0
  36. package/dist/components/NavFavourites.js +35 -108
  37. package/dist/components/RelationshipGraph.d.ts +30 -0
  38. package/dist/components/RelationshipGraph.js +138 -0
  39. package/dist/components/RuleBuilder.d.ts +45 -0
  40. package/dist/components/RuleBuilder.js +163 -0
  41. package/dist/components/SchedulingCalendar.d.ts +42 -0
  42. package/dist/components/SchedulingCalendar.js +124 -0
  43. package/dist/components/Segmented.js +3 -2
  44. package/dist/components/Table.d.ts +1 -1
  45. package/dist/components/Table.js +4 -3
  46. package/dist/components/Tabs.js +3 -2
  47. package/dist/diff-model.d.ts +23 -0
  48. package/dist/diff-model.js +50 -0
  49. package/dist/hooks/useTable.js +11 -7
  50. package/dist/hooks/useVirtualRows.d.ts +27 -0
  51. package/dist/hooks/useVirtualRows.js +50 -0
  52. package/dist/index.cjs +1930 -194
  53. package/dist/index.d.cts +464 -2
  54. package/dist/index.d.ts +19 -0
  55. package/dist/index.js +93 -1
  56. package/dist/json-value.d.ts +10 -0
  57. package/dist/json-value.js +19 -0
  58. package/dist/metric-model.d.ts +9 -0
  59. package/dist/metric-model.js +25 -0
  60. package/dist/relationship-graph-model.d.ts +31 -0
  61. package/dist/relationship-graph-model.js +68 -0
  62. package/dist/rule-builder-model.d.ts +41 -0
  63. package/dist/rule-builder-model.js +101 -0
  64. package/dist/table-engine.js +8 -6
  65. package/package.json +16 -2
package/dist/index.cjs CHANGED
@@ -24,6 +24,9 @@ __export(src_exports, {
24
24
  AppShell: () => AppShell,
25
25
  Avatar: () => Avatar,
26
26
  AvatarGroup: () => AvatarGroup,
27
+ BrandProfileEditor: () => BrandProfileEditor,
28
+ BrandProfiles: () => BrandProfiles,
29
+ BuilderCanvas: () => BuilderCanvas,
27
30
  BulkBar: () => BulkBar,
28
31
  Button: () => Button,
29
32
  ButtonGroup: () => ButtonGroup,
@@ -31,6 +34,7 @@ __export(src_exports, {
31
34
  CellStrong: () => CellStrong,
32
35
  CellSub: () => CellSub,
33
36
  Checkbox: () => Checkbox,
37
+ ColorPicker: () => ColorPicker,
34
38
  CommandGroup: () => CommandGroup,
35
39
  CommandItem: () => CommandItem,
36
40
  CommandPalette: () => CommandPalette,
@@ -40,9 +44,11 @@ __export(src_exports, {
40
44
  ComposerBar: () => ComposerBar,
41
45
  ContentList: () => ContentList,
42
46
  ContentListItem: () => ContentListItem,
47
+ DateRangePicker: () => DateRangePicker,
43
48
  DescriptionItem: () => DescriptionItem,
44
49
  DescriptionList: () => DescriptionList,
45
50
  DetailLayout: () => DetailLayout,
51
+ DiffViewer: () => DiffViewer,
46
52
  Drawer: () => Drawer,
47
53
  EmptyState: () => EmptyState,
48
54
  ErrorState: () => ErrorState,
@@ -63,11 +69,14 @@ __export(src_exports, {
63
69
  KanbanCard: () => KanbanCard,
64
70
  KanbanColumn: () => KanbanColumn,
65
71
  Label: () => Label,
72
+ LicensePositionBar: () => LicensePositionBar,
66
73
  List: () => List,
67
74
  ListItem: () => ListItem,
68
75
  LoadingState: () => LoadingState,
69
76
  Mark: () => Mark,
77
+ MatchReview: () => MatchReview,
70
78
  Meter: () => Meter,
79
+ MetricInput: () => MetricInput,
71
80
  Modal: () => Modal,
72
81
  NavFavourites: () => NavFavourites,
73
82
  NavGroup: () => NavGroup,
@@ -84,9 +93,12 @@ __export(src_exports, {
84
93
  Prose: () => Prose,
85
94
  Radio: () => Radio,
86
95
  RadioGroup: () => RadioGroup,
96
+ RelationshipGraph: () => RelationshipGraph,
87
97
  ResourceGrid: () => ResourceGrid,
88
98
  RowAction: () => RowAction,
89
99
  RowActions: () => RowActions,
100
+ RuleBuilder: () => RuleBuilder,
101
+ SchedulingCalendar: () => SchedulingCalendar,
90
102
  SectionHead: () => SectionHead,
91
103
  Segmented: () => Segmented,
92
104
  SegmentedOption: () => SegmentedOption,
@@ -117,30 +129,70 @@ __export(src_exports, {
117
129
  Tr: () => Tr,
118
130
  Tree: () => Tree,
119
131
  UserChip: () => UserChip,
132
+ addCalendarDays: () => addCalendarDays,
133
+ addCalendarMonths: () => addCalendarMonths,
134
+ appendRuleNode: () => appendRuleNode,
135
+ applyBrandProfile: () => applyBrandProfile,
120
136
  applyFilters: () => applyFilters,
137
+ boundRelationshipGraph: () => boundRelationshipGraph,
138
+ buildMonthGrid: () => buildMonthGrid,
139
+ buildThreeWayDiff: () => buildThreeWayDiff,
140
+ clampMetricValue: () => clampMetricValue,
121
141
  clampWidth: () => clampWidth,
122
142
  computePosition: () => computePosition,
143
+ contrastRatio: () => contrastRatio,
123
144
  cx: () => cx,
145
+ enumerateDateSpan: () => enumerateDateSpan,
146
+ findRuleNodeDepth: () => findRuleNodeDepth,
124
147
  flattenTree: () => flattenTree,
148
+ hexToRgb: () => hexToRgb,
125
149
  highlightSegments: () => highlightSegments,
150
+ hsvToHex: () => hsvToHex,
151
+ hsvToRgb: () => hsvToRgb,
152
+ isDateInRange: () => isDateInRange,
153
+ isDateUnavailable: () => isDateUnavailable,
154
+ isRuleGroup: () => isRuleGroup,
155
+ layoutRelationshipGraph: () => layoutRelationshipGraph,
156
+ mapRuleGroup: () => mapRuleGroup,
126
157
  matchFilter: () => matchFilter,
158
+ meetsContrast: () => meetsContrast,
127
159
  mergePinned: () => mergePinned,
160
+ moveRuleNode: () => moveRuleNode,
128
161
  multiSort: () => multiSort,
129
162
  nextSortDir: () => nextSortDir,
163
+ normalizeHex: () => normalizeHex,
164
+ parseMetricValue: () => parseMetricValue,
130
165
  parseView: () => parseView,
166
+ relationshipNeighbors: () => relationshipNeighbors,
167
+ removeRuleNode: () => removeRuleNode,
131
168
  reorder: () => reorder,
169
+ restoreBrandProfile: () => restoreBrandProfile,
170
+ rgbToHex: () => rgbToHex,
171
+ rgbToHsv: () => rgbToHsv,
172
+ ruleDepth: () => ruleDepth,
132
173
  searchRows: () => searchRows,
133
174
  selectAllState: () => selectAllState,
175
+ selectRangeDate: () => selectRangeDate,
134
176
  serializeView: () => serializeView,
135
177
  shouldVirtualize: () => shouldVirtualize,
178
+ startOfMonth: () => startOfMonth,
179
+ stepMetricValue: () => stepMetricValue,
180
+ summarizeDiff: () => summarizeDiff,
181
+ summarizeRule: () => summarizeRule,
182
+ toDateKey: () => toDateKey,
136
183
  toggleId: () => toggleId,
137
184
  togglePage: () => togglePage,
138
185
  toggleSort: () => toggleSort,
186
+ traverseRelationshipNode: () => traverseRelationshipNode,
139
187
  treeNav: () => treeNav,
140
188
  useAnchoredPosition: () => useAnchoredPosition,
141
189
  useDialog: () => useDialog,
142
190
  useTable: () => useTable,
143
- virtualWindow: () => virtualWindow
191
+ useVirtualRows: () => useVirtualRows,
192
+ validateRuleDefinition: () => validateRuleDefinition,
193
+ virtualWindow: () => virtualWindow,
194
+ zonedDateKey: () => zonedDateKey,
195
+ zonedDateSpan: () => zonedDateSpan
144
196
  });
145
197
  module.exports = __toCommonJS(src_exports);
146
198
 
@@ -203,22 +255,23 @@ function useDialog(open) {
203
255
  var import_react2 = require("react");
204
256
 
205
257
  // src/table-engine.ts
258
+ var stringCollator = new Intl.Collator(void 0, { numeric: true, sensitivity: "base" });
206
259
  function compare(a, b) {
207
260
  if (a == null && b == null) return 0;
208
261
  if (a == null) return -1;
209
262
  if (b == null) return 1;
210
263
  if (typeof a === "number" && typeof b === "number") return a - b;
211
- return String(a).localeCompare(String(b), void 0, { numeric: true, sensitivity: "base" });
264
+ return stringCollator.compare(String(a), String(b));
212
265
  }
213
266
  function multiSort(rows, keys) {
214
267
  if (!keys.length) return rows.slice();
215
- return rows.map((row, i) => ({ row, i })).sort((x, y) => {
268
+ return rows.slice().sort((a, b) => {
216
269
  for (const k of keys) {
217
- const c = compare(x.row[k.field], y.row[k.field]);
270
+ const c = compare(a[k.field], b[k.field]);
218
271
  if (c !== 0) return k.dir === "ascending" ? c : -c;
219
272
  }
220
- return x.i - y.i;
221
- }).map((w) => w.row);
273
+ return 0;
274
+ });
222
275
  }
223
276
  function nextSortDir(current) {
224
277
  if (current === "ascending") return "descending";
@@ -418,7 +471,8 @@ function toggleId(set, id) {
418
471
  return next;
419
472
  }
420
473
  function selectAllState(selected, pageIds) {
421
- const on = pageIds.filter((id) => selected.has(id)).length;
474
+ let on = 0;
475
+ for (const id of pageIds) if (selected.has(id)) on += 1;
422
476
  if (on === 0) return "none";
423
477
  return on === pageIds.length ? "all" : "some";
424
478
  }
@@ -444,18 +498,22 @@ function useTable(data, options = {}) {
444
498
  const [query, setQuery] = (0, import_react2.useState)(() => options.initialQuery ?? "");
445
499
  const [selected, setSelected] = (0, import_react2.useState)(() => /* @__PURE__ */ new Set());
446
500
  const [pinned, setPinned] = (0, import_react2.useState)(() => new Set(options.initialPinned ?? []));
447
- const rows = (0, import_react2.useMemo)(() => {
448
- const filtered = applyFilters(data, filters);
449
- const searched = searchRows(filtered, query, searchFields);
450
- const sorted = multiSort(searched, sort);
451
- return mergePinned(data, sorted, pinned, idField);
452
- }, [data, filters, query, searchFields, sort, pinned, idField]);
501
+ const visibleRows = (0, import_react2.useMemo)(() => {
502
+ const filtered = filters.length ? applyFilters(data, filters) : data;
503
+ const searched = query.trim() ? searchRows(filtered, query, searchFields) : filtered;
504
+ return sort.length ? multiSort(searched, sort) : searched;
505
+ }, [data, filters, query, searchFields, sort]);
506
+ const rows = (0, import_react2.useMemo)(
507
+ () => pinned.size ? mergePinned(data, visibleRows, pinned, idField) : visibleRows.slice(),
508
+ [data, visibleRows, pinned, idField]
509
+ );
453
510
  const pageIds = (0, import_react2.useMemo)(() => rows.map((r) => String(r[idField])), [rows, idField]);
454
511
  const selectAll = selectAllState(selected, pageIds);
455
512
  const doToggleSort = (0, import_react2.useCallback)((field, additive = false) => {
456
513
  setSort((keys) => toggleSort(keys, field, additive));
457
514
  }, []);
458
515
  const toggleRow = (0, import_react2.useCallback)((id) => setSelected((s) => toggleId(s, id)), []);
516
+ const isSelected = (0, import_react2.useCallback)((id) => selected.has(id), [selected]);
459
517
  const toggleAllRows = (0, import_react2.useCallback)(() => setSelected((s) => togglePage(s, pageIds)), [pageIds]);
460
518
  const clearSelection = (0, import_react2.useCallback)(() => setSelected(/* @__PURE__ */ new Set()), []);
461
519
  const togglePin = (0, import_react2.useCallback)((id) => setPinned((s) => toggleId(s, id)), []);
@@ -479,7 +537,7 @@ function useTable(data, options = {}) {
479
537
  setSort,
480
538
  setFilters,
481
539
  setQuery,
482
- isSelected: (id) => selected.has(id),
540
+ isSelected,
483
541
  toggleRow,
484
542
  toggleAllRows,
485
543
  clearSelection,
@@ -490,8 +548,55 @@ function useTable(data, options = {}) {
490
548
  };
491
549
  }
492
550
 
493
- // src/hooks/useAnchoredPosition.ts
551
+ // src/hooks/useVirtualRows.ts
494
552
  var import_react3 = require("react");
553
+ function useVirtualRows(rows, { rowHeight, overscan = 6, threshold = 100 }) {
554
+ const containerRef = (0, import_react3.useRef)(null);
555
+ const [viewport, setViewport] = (0, import_react3.useState)({ scrollTop: 0, height: 0 });
556
+ (0, import_react3.useLayoutEffect)(() => {
557
+ const element = containerRef.current;
558
+ if (!element) return;
559
+ let frame = 0;
560
+ const commitMeasurement = () => {
561
+ const next = { scrollTop: element.scrollTop, height: element.clientHeight };
562
+ setViewport((current) => current.scrollTop === next.scrollTop && current.height === next.height ? current : next);
563
+ };
564
+ const measure = () => {
565
+ if (frame) return;
566
+ frame = requestAnimationFrame(() => {
567
+ frame = 0;
568
+ commitMeasurement();
569
+ });
570
+ };
571
+ commitMeasurement();
572
+ element.addEventListener("scroll", measure, { passive: true });
573
+ const observer = new ResizeObserver(measure);
574
+ observer.observe(element);
575
+ return () => {
576
+ element.removeEventListener("scroll", measure);
577
+ observer.disconnect();
578
+ if (frame) cancelAnimationFrame(frame);
579
+ };
580
+ }, []);
581
+ const enabled = viewport.height > 0 && shouldVirtualize(rows.length, threshold);
582
+ const window2 = (0, import_react3.useMemo)(
583
+ () => enabled ? virtualWindow(viewport.scrollTop, viewport.height, rowHeight, rows.length, overscan) : { start: 0, end: rows.length, padTop: 0, padBottom: 0, total: rows.length * rowHeight },
584
+ [enabled, overscan, rowHeight, rows.length, viewport.height, viewport.scrollTop]
585
+ );
586
+ const visibleRows = (0, import_react3.useMemo)(() => rows.slice(window2.start, window2.end), [rows, window2.start, window2.end]);
587
+ return {
588
+ containerRef,
589
+ rows: visibleRows,
590
+ startIndex: window2.start,
591
+ padTop: window2.padTop,
592
+ padBottom: window2.padBottom,
593
+ totalHeight: window2.total,
594
+ virtualized: enabled
595
+ };
596
+ }
597
+
598
+ // src/hooks/useAnchoredPosition.ts
599
+ var import_react4 = require("react");
495
600
 
496
601
  // src/overlay-position.ts
497
602
  var OPPOSITE = { top: "bottom", bottom: "top", left: "right", right: "left" };
@@ -555,12 +660,12 @@ function computePosition(anchor, floating, viewport, options = {}) {
555
660
  }
556
661
 
557
662
  // src/hooks/useAnchoredPosition.ts
558
- var useIsomorphicLayoutEffect = typeof document !== "undefined" ? import_react3.useLayoutEffect : import_react3.useEffect;
663
+ var useIsomorphicLayoutEffect = typeof document !== "undefined" ? import_react4.useLayoutEffect : import_react4.useEffect;
559
664
  var resolve = (a) => a && typeof a === "object" && "current" in a ? a.current : a ?? null;
560
665
  function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-start", offset = 6, padding = 8 }) {
561
- const opts = (0, import_react3.useRef)({ placement, offset, padding });
666
+ const opts = (0, import_react4.useRef)({ placement, offset, padding });
562
667
  opts.current = { placement, offset, padding };
563
- const reposition = (0, import_react3.useCallback)(() => {
668
+ const reposition = (0, import_react4.useCallback)(() => {
564
669
  const anchorEl = resolve(anchor);
565
670
  const floating = floatingRef.current;
566
671
  if (!anchorEl || !floating) return;
@@ -595,9 +700,9 @@ function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-st
595
700
  }
596
701
 
597
702
  // src/components/Button.tsx
598
- var import_react4 = require("react");
703
+ var import_react5 = require("react");
599
704
  var import_jsx_runtime = require("react/jsx-runtime");
600
- var Button = (0, import_react4.forwardRef)(
705
+ var Button = (0, import_react5.forwardRef)(
601
706
  ({ variant = "secondary", size = "md", icon = false, loading = false, className, children, disabled, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
602
707
  "button",
603
708
  {
@@ -622,9 +727,9 @@ function ButtonGroup({ children, className }) {
622
727
  }
623
728
 
624
729
  // src/components/Input.tsx
625
- var import_react5 = require("react");
730
+ var import_react6 = require("react");
626
731
  var import_jsx_runtime2 = require("react/jsx-runtime");
627
- var Input = (0, import_react5.forwardRef)(
732
+ var Input = (0, import_react6.forwardRef)(
628
733
  ({ size = "md", invalid, valid, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
629
734
  "input",
630
735
  {
@@ -657,9 +762,9 @@ function InputGroup({ children, leadingIcon, trailingIcon, className }) {
657
762
  }
658
763
 
659
764
  // src/components/Textarea.tsx
660
- var import_react6 = require("react");
765
+ var import_react7 = require("react");
661
766
  var import_jsx_runtime3 = require("react/jsx-runtime");
662
- var Textarea = (0, import_react6.forwardRef)(
767
+ var Textarea = (0, import_react7.forwardRef)(
663
768
  ({ invalid, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
664
769
  "textarea",
665
770
  {
@@ -673,9 +778,9 @@ var Textarea = (0, import_react6.forwardRef)(
673
778
  Textarea.displayName = "Textarea";
674
779
 
675
780
  // src/components/Select.tsx
676
- var import_react7 = require("react");
781
+ var import_react8 = require("react");
677
782
  var import_jsx_runtime4 = require("react/jsx-runtime");
678
- var Select = (0, import_react7.forwardRef)(
783
+ var Select = (0, import_react8.forwardRef)(
679
784
  ({ size = "md", className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
680
785
  "select",
681
786
  {
@@ -688,14 +793,14 @@ var Select = (0, import_react7.forwardRef)(
688
793
  Select.displayName = "Select";
689
794
 
690
795
  // src/components/Checkbox.tsx
691
- var import_react8 = require("react");
796
+ var import_react9 = require("react");
692
797
  var import_jsx_runtime5 = require("react/jsx-runtime");
693
798
  var CheckIcon = () => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "1.5,6 4.5,9 10.5,3" }) });
694
799
  var IndeterminateIcon = () => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { viewBox: "0 0 12 12", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: "2", y: "5", width: "8", height: "2", rx: "1", fill: "currentColor" }) });
695
- var Checkbox = (0, import_react8.forwardRef)(
800
+ var Checkbox = (0, import_react9.forwardRef)(
696
801
  ({ label, indeterminate, className, ...props }, forwardedRef) => {
697
- const innerRef = (0, import_react8.useRef)(null);
698
- (0, import_react8.useEffect)(() => {
802
+ const innerRef = (0, import_react9.useRef)(null);
803
+ (0, import_react9.useEffect)(() => {
699
804
  const el = innerRef.current;
700
805
  if (el) el.indeterminate = !!indeterminate;
701
806
  }, [indeterminate]);
@@ -720,9 +825,9 @@ var Checkbox = (0, import_react8.forwardRef)(
720
825
  Checkbox.displayName = "Checkbox";
721
826
 
722
827
  // src/components/Radio.tsx
723
- var import_react9 = require("react");
828
+ var import_react10 = require("react");
724
829
  var import_jsx_runtime6 = require("react/jsx-runtime");
725
- var Radio = (0, import_react9.forwardRef)(
830
+ var Radio = (0, import_react10.forwardRef)(
726
831
  ({ label, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: cx("uix-radio", className), children: [
727
832
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "radio", ref, ...props }),
728
833
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "uix-radio__dot" }),
@@ -735,9 +840,9 @@ function RadioGroup({ children, className }) {
735
840
  }
736
841
 
737
842
  // src/components/Switch.tsx
738
- var import_react10 = require("react");
843
+ var import_react11 = require("react");
739
844
  var import_jsx_runtime7 = require("react/jsx-runtime");
740
- var Switch = (0, import_react10.forwardRef)(
845
+ var Switch = (0, import_react11.forwardRef)(
741
846
  ({ label, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: cx("uix-switch", className), children: [
742
847
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { type: "checkbox", role: "switch", ref, ...props }),
743
848
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "uix-switch__track" }),
@@ -747,13 +852,13 @@ var Switch = (0, import_react10.forwardRef)(
747
852
  Switch.displayName = "Switch";
748
853
 
749
854
  // src/components/Field.tsx
750
- var import_react11 = require("react");
855
+ var import_react12 = require("react");
751
856
  var import_jsx_runtime8 = require("react/jsx-runtime");
752
857
  function Field({ label, hint, error, success, required, children, className, htmlFor }) {
753
- const uid = (0, import_react11.useId)();
858
+ const uid = (0, import_react12.useId)();
754
859
  const msgId = `${uid}-msg`;
755
860
  const describedBy = error || success || hint ? msgId : void 0;
756
- const control = (0, import_react11.isValidElement)(children) ? (0, import_react11.cloneElement)(children, {
861
+ const control = (0, import_react12.isValidElement)(children) ? (0, import_react12.cloneElement)(children, {
757
862
  "aria-describedby": [children.props["aria-describedby"], describedBy].filter(Boolean).join(" ") || void 0,
758
863
  "aria-invalid": error ? true : children.props["aria-invalid"]
759
864
  }) : children;
@@ -890,11 +995,11 @@ function ListItem({ title, meta, trail, leading, children, className, ...props }
890
995
  }
891
996
 
892
997
  // src/components/DescriptionList.tsx
893
- var import_react12 = require("react");
998
+ var import_react13 = require("react");
894
999
  var import_jsx_runtime16 = require("react/jsx-runtime");
895
1000
  function DescriptionList({ items, children, className, ...props }) {
896
1001
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("dl", { className: cx("uix-dl", className), ...props, children: [
897
- items?.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react12.Fragment, { children: [
1002
+ items?.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react13.Fragment, { children: [
898
1003
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("dt", { children: item.term }),
899
1004
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("dd", { children: item.description })
900
1005
  ] }, item.id ?? i)),
@@ -909,14 +1014,15 @@ function DescriptionItem({ term, children }) {
909
1014
  }
910
1015
 
911
1016
  // src/components/Tabs.tsx
912
- var import_react13 = require("react");
1017
+ var import_react14 = require("react");
913
1018
  var import_jsx_runtime17 = require("react/jsx-runtime");
914
- var TabsCtx = (0, import_react13.createContext)({});
1019
+ var TabsCtx = (0, import_react14.createContext)({});
915
1020
  function Tabs({ variant = "line", value, onChange, children, className }) {
916
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(TabsCtx.Provider, { value: { value, onChange }, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { role: "tablist", className: cx("uix-tabs", `uix-tabs--${variant}`, className), children }) });
1021
+ const contextValue = (0, import_react14.useMemo)(() => ({ value, onChange }), [value, onChange]);
1022
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(TabsCtx.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { role: "tablist", className: cx("uix-tabs", `uix-tabs--${variant}`, className), children }) });
917
1023
  }
918
1024
  function Tab({ value, children, disabled, className, onClick, ...props }) {
919
- const ctx = (0, import_react13.useContext)(TabsCtx);
1025
+ const ctx = (0, import_react14.useContext)(TabsCtx);
920
1026
  const selected = ctx.value === value;
921
1027
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
922
1028
  "button",
@@ -936,7 +1042,7 @@ function Tab({ value, children, disabled, className, onClick, ...props }) {
936
1042
  }
937
1043
 
938
1044
  // src/components/AppShell.tsx
939
- var import_react14 = require("react");
1045
+ var import_react15 = require("react");
940
1046
  var import_jsx_runtime18 = require("react/jsx-runtime");
941
1047
  function AppShell({
942
1048
  nav,
@@ -951,7 +1057,7 @@ function AppShell({
951
1057
  ...props
952
1058
  }) {
953
1059
  const navState = nav ?? (collapsed ? "rail" : "full");
954
- (0, import_react14.useEffect)(() => {
1060
+ (0, import_react15.useEffect)(() => {
955
1061
  if (!focus || !onExitFocus) return;
956
1062
  const onKey = (e) => {
957
1063
  if (e.key === "Escape") onExitFocus();
@@ -978,7 +1084,7 @@ function AppShell({
978
1084
  }
979
1085
 
980
1086
  // src/components/Sidebar.tsx
981
- var import_react15 = require("react");
1087
+ var import_react16 = require("react");
982
1088
  var import_jsx_runtime19 = require("react/jsx-runtime");
983
1089
  var ChevronIcon = () => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("svg", { className: "uix-navgroup__chevron", viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("path", { d: "M2 4l4 4 4-4" }) });
984
1090
  function Sidebar({ collapsed, onToggle, brand, toggleIcon, children, className, ...props }) {
@@ -1032,7 +1138,7 @@ function NavItem({ icon, badge, active, as: Tag = "a", children, className, ...p
1032
1138
  );
1033
1139
  }
1034
1140
  function NavGroup({ icon, label, children, defaultExpanded = true }) {
1035
- const [expanded, setExpanded] = (0, import_react15.useState)(defaultExpanded);
1141
+ const [expanded, setExpanded] = (0, import_react16.useState)(defaultExpanded);
1036
1142
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "uix-navgroup", children: [
1037
1143
  /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
1038
1144
  "button",
@@ -1100,7 +1206,7 @@ function StarButton({
1100
1206
  }
1101
1207
 
1102
1208
  // src/components/NavFavourites.tsx
1103
- var import_react16 = require("react");
1209
+ var import_react17 = require("react");
1104
1210
  var import_jsx_runtime21 = require("react/jsx-runtime");
1105
1211
  var ChevronDownIcon = () => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1106
1212
  "svg",
@@ -1114,7 +1220,7 @@ var ChevronDownIcon = () => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1114
1220
  strokeLinecap: "round",
1115
1221
  strokeLinejoin: "round",
1116
1222
  "aria-hidden": "true",
1117
- style: { flexShrink: 0 },
1223
+ className: "uix-favourites__chevron",
1118
1224
  children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "m6 9 6 6 6-6" })
1119
1225
  }
1120
1226
  );
@@ -1130,7 +1236,7 @@ var ChevronRightIcon = () => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1130
1236
  strokeLinecap: "round",
1131
1237
  strokeLinejoin: "round",
1132
1238
  "aria-hidden": "true",
1133
- style: { flexShrink: 0 },
1239
+ className: "uix-favourites__chevron",
1134
1240
  children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "m9 18 6-6-6-6" })
1135
1241
  }
1136
1242
  );
@@ -1153,7 +1259,6 @@ var MoreIcon = () => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
1153
1259
  ]
1154
1260
  }
1155
1261
  );
1156
- var MUTED = "color-mix(in srgb, var(--uix-text) 50%, transparent)";
1157
1262
  function NavFavourites({
1158
1263
  items,
1159
1264
  collapsed = false,
@@ -1164,20 +1269,20 @@ function NavFavourites({
1164
1269
  emptyReason,
1165
1270
  className
1166
1271
  }) {
1167
- const regionId = (0, import_react16.useId)();
1168
- const headerRef = (0, import_react16.useRef)(null);
1169
- const regionRef = (0, import_react16.useRef)(null);
1170
- const focusAfterRef = (0, import_react16.useRef)(null);
1171
- const triggerRefs = (0, import_react16.useRef)(/* @__PURE__ */ new Map());
1172
- const order = items.map((i) => i.id);
1173
- (0, import_react16.useEffect)(() => {
1272
+ const regionId = (0, import_react17.useId)();
1273
+ const headerRef = (0, import_react17.useRef)(null);
1274
+ const regionRef = (0, import_react17.useRef)(null);
1275
+ const focusAfterRef = (0, import_react17.useRef)(null);
1276
+ const triggerRefs = (0, import_react17.useRef)(/* @__PURE__ */ new Map());
1277
+ const order = (0, import_react17.useMemo)(() => items.map((item) => item.id), [items]);
1278
+ (0, import_react17.useEffect)(() => {
1174
1279
  const id = focusAfterRef.current;
1175
1280
  if (!id) return;
1176
1281
  focusAfterRef.current = null;
1177
1282
  const el = triggerRefs.current.get(id);
1178
1283
  if (el) el.focus();
1179
1284
  else headerRef.current?.focus();
1180
- }, [order.join("|")]);
1285
+ }, [order]);
1181
1286
  const handleToggle = () => {
1182
1287
  if (!collapsed) {
1183
1288
  const active = document.activeElement;
@@ -1187,7 +1292,7 @@ function NavFavourites({
1187
1292
  }
1188
1293
  onToggleCollapsed?.();
1189
1294
  };
1190
- const move = (id, dir) => {
1295
+ const move = (0, import_react17.useCallback)((id, dir) => {
1191
1296
  const idx = order.indexOf(id);
1192
1297
  const next = idx + dir;
1193
1298
  if (idx < 0 || next < 0 || next >= order.length) return;
@@ -1197,14 +1302,18 @@ function NavFavourites({
1197
1302
  reordered.splice(next, 0, it);
1198
1303
  focusAfterRef.current = id;
1199
1304
  onReorder(reordered);
1200
- };
1201
- const remove = (id) => {
1305
+ }, [onReorder, order]);
1306
+ const remove = (0, import_react17.useCallback)((id) => {
1202
1307
  const idx = order.indexOf(id);
1203
1308
  const neighbour = order[idx + 1] ?? order[idx - 1] ?? null;
1204
1309
  focusAfterRef.current = neighbour;
1205
1310
  onRemove(id);
1206
- };
1207
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: cx("uix-favourites", className), style: { marginBottom: "var(--uix-space-1)" }, children: [
1311
+ }, [onRemove, order]);
1312
+ const registerTrigger = (0, import_react17.useCallback)((id, element) => {
1313
+ if (element) triggerRefs.current.set(id, element);
1314
+ else triggerRefs.current.delete(id);
1315
+ }, []);
1316
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: cx("uix-favourites", className), children: [
1208
1317
  /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
1209
1318
  "button",
1210
1319
  {
@@ -1213,18 +1322,7 @@ function NavFavourites({
1213
1322
  "aria-expanded": !collapsed,
1214
1323
  "aria-controls": regionId,
1215
1324
  onClick: handleToggle,
1216
- className: "uix-text-eyebrow",
1217
- style: {
1218
- display: "flex",
1219
- width: "100%",
1220
- alignItems: "center",
1221
- gap: "6px",
1222
- padding: "8px 4px 4px",
1223
- border: 0,
1224
- background: "transparent",
1225
- cursor: "pointer",
1226
- color: "inherit"
1227
- },
1325
+ className: "uix-favourites__heading uix-text-eyebrow",
1228
1326
  children: [
1229
1327
  collapsed ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ChevronRightIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ChevronDownIcon, {}),
1230
1328
  labels.heading
@@ -1237,20 +1335,12 @@ function NavFavourites({
1237
1335
  ref: regionRef,
1238
1336
  id: regionId,
1239
1337
  hidden: collapsed,
1240
- style: {
1241
- display: "flex",
1242
- flexDirection: "column",
1243
- gap: "1px",
1244
- listStyle: "none",
1245
- margin: 0,
1246
- padding: 0
1247
- },
1338
+ className: "uix-favourites__list",
1248
1339
  children: items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1249
1340
  "li",
1250
1341
  {
1251
- className: "uix-text-meta",
1342
+ className: "uix-favourites__empty uix-text-meta",
1252
1343
  "data-empty-reason": emptyReason,
1253
- style: { padding: "6px 8px", color: MUTED },
1254
1344
  children: labels.empty
1255
1345
  }
1256
1346
  ) : items.map((item, idx) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
@@ -1260,10 +1350,9 @@ function NavFavourites({
1260
1350
  isFirst: idx === 0,
1261
1351
  isLast: idx === items.length - 1,
1262
1352
  labels,
1263
- onMoveUp: () => move(item.id, -1),
1264
- onMoveDown: () => move(item.id, 1),
1265
- onRemove: () => remove(item.id),
1266
- registerTrigger: (el) => triggerRefs.current.set(item.id, el)
1353
+ onMove: move,
1354
+ onRemove: remove,
1355
+ registerTrigger
1267
1356
  },
1268
1357
  item.id
1269
1358
  ))
@@ -1271,28 +1360,26 @@ function NavFavourites({
1271
1360
  )
1272
1361
  ] });
1273
1362
  }
1274
- function FavouriteRow({
1363
+ var FavouriteRow = (0, import_react17.memo)(function FavouriteRow2({
1275
1364
  item,
1276
1365
  isFirst,
1277
1366
  isLast,
1278
1367
  labels,
1279
- onMoveUp,
1280
- onMoveDown,
1368
+ onMove,
1281
1369
  onRemove,
1282
1370
  registerTrigger
1283
1371
  }) {
1284
- const [open, setOpen] = (0, import_react16.useState)(false);
1285
- const [revealed, setRevealed] = (0, import_react16.useState)(false);
1286
- const menuId = (0, import_react16.useId)();
1287
- const triggerRef = (0, import_react16.useRef)(null);
1288
- const itemRefs = (0, import_react16.useRef)([]);
1372
+ const [open, setOpen] = (0, import_react17.useState)(false);
1373
+ const menuId = (0, import_react17.useId)();
1374
+ const triggerRef = (0, import_react17.useRef)(null);
1375
+ const itemRefs = (0, import_react17.useRef)([]);
1289
1376
  const optionsTemplate = labels.options ?? "{label} favourite options";
1290
1377
  const actions = [
1291
- ...isFirst ? [] : [{ label: labels.moveUp, onSelect: onMoveUp }],
1292
- ...isLast ? [] : [{ label: labels.moveDown, onSelect: onMoveDown }],
1293
- { label: labels.remove.replace("{label}", item.label), onSelect: onRemove }
1378
+ ...isFirst ? [] : [{ label: labels.moveUp, onSelect: () => onMove(item.id, -1) }],
1379
+ ...isLast ? [] : [{ label: labels.moveDown, onSelect: () => onMove(item.id, 1) }],
1380
+ { label: labels.remove.replace("{label}", item.label), onSelect: () => onRemove(item.id) }
1294
1381
  ];
1295
- (0, import_react16.useEffect)(() => {
1382
+ (0, import_react17.useEffect)(() => {
1296
1383
  if (open) itemRefs.current[0]?.focus();
1297
1384
  }, [open]);
1298
1385
  const closeAndReturnFocus = () => {
@@ -1319,34 +1406,27 @@ function FavouriteRow({
1319
1406
  if (!e.altKey) return;
1320
1407
  if (e.key === "ArrowUp" && !isFirst) {
1321
1408
  e.preventDefault();
1322
- onMoveUp();
1409
+ onMove(item.id, -1);
1323
1410
  } else if (e.key === "ArrowDown" && !isLast) {
1324
1411
  e.preventDefault();
1325
- onMoveDown();
1412
+ onMove(item.id, 1);
1326
1413
  }
1327
1414
  };
1328
- const triggerVisible = revealed || open;
1329
1415
  return (
1330
1416
  // biome-ignore lint/a11y/noStaticElementInteractions: keyboard accelerators on the row; the focusable controls inside carry their own semantics.
1331
1417
  /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
1332
1418
  "li",
1333
1419
  {
1334
- style: { position: "relative", display: "flex", alignItems: "center" },
1420
+ className: "uix-favourites__row",
1335
1421
  onKeyDown: onRowKeyDown,
1336
- onMouseEnter: () => setRevealed(true),
1337
- onMouseLeave: () => setRevealed(false),
1338
- onFocus: () => setRevealed(true),
1339
- onBlur: (e) => {
1340
- if (!e.currentTarget.contains(e.relatedTarget)) setRevealed(false);
1341
- },
1342
1422
  children: [
1343
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { minWidth: 0, flex: 1 }, children: item.link }),
1423
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "uix-favourites__link", children: item.link }),
1344
1424
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1345
1425
  "button",
1346
1426
  {
1347
1427
  ref: (el) => {
1348
1428
  triggerRef.current = el;
1349
- registerTrigger(el);
1429
+ registerTrigger(item.id, el);
1350
1430
  },
1351
1431
  type: "button",
1352
1432
  "aria-haspopup": "menu",
@@ -1354,22 +1434,7 @@ function FavouriteRow({
1354
1434
  "aria-controls": open ? menuId : void 0,
1355
1435
  "aria-label": optionsTemplate.replace("{label}", item.label),
1356
1436
  onClick: () => setOpen((v) => !v),
1357
- style: {
1358
- marginLeft: "auto",
1359
- display: "inline-flex",
1360
- height: "20px",
1361
- width: "20px",
1362
- flexShrink: 0,
1363
- alignItems: "center",
1364
- justifyContent: "center",
1365
- border: 0,
1366
- borderRadius: "2px",
1367
- background: "transparent",
1368
- cursor: "pointer",
1369
- color: MUTED,
1370
- opacity: triggerVisible ? 1 : 0,
1371
- transition: "opacity var(--uix-dur-fast) var(--uix-ease-out)"
1372
- },
1437
+ className: "uix-favourites__menu-trigger",
1373
1438
  children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(MoreIcon, {})
1374
1439
  }
1375
1440
  ),
@@ -1380,7 +1445,7 @@ function FavouriteRow({
1380
1445
  "aria-hidden": "true",
1381
1446
  role: "presentation",
1382
1447
  onClick: () => setOpen(false),
1383
- style: { position: "fixed", inset: 0, cursor: "default", background: "transparent", zIndex: 40 }
1448
+ className: "uix-favourites__backdrop"
1384
1449
  }
1385
1450
  ),
1386
1451
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
@@ -1389,21 +1454,7 @@ function FavouriteRow({
1389
1454
  id: menuId,
1390
1455
  role: "menu",
1391
1456
  onKeyDown: onMenuKeyDown,
1392
- style: {
1393
- position: "absolute",
1394
- right: 0,
1395
- top: "calc(100% + 2px)",
1396
- zIndex: 50,
1397
- minWidth: "160px",
1398
- overflow: "hidden",
1399
- margin: 0,
1400
- padding: "4px 0",
1401
- listStyle: "none",
1402
- borderRadius: "6px",
1403
- border: "1px solid var(--uix-border)",
1404
- background: "var(--uix-surface)",
1405
- boxShadow: "var(--uix-shadow-popover)"
1406
- },
1457
+ className: "uix-favourites__menu",
1407
1458
  children: actions.map((action, i) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("li", { role: "presentation", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1408
1459
  "button",
1409
1460
  {
@@ -1417,25 +1468,7 @@ function FavouriteRow({
1417
1468
  setOpen(false);
1418
1469
  action.onSelect();
1419
1470
  },
1420
- style: {
1421
- display: "flex",
1422
- width: "100%",
1423
- alignItems: "center",
1424
- padding: "6px 12px",
1425
- border: 0,
1426
- background: "transparent",
1427
- cursor: "pointer",
1428
- textAlign: "left",
1429
- fontSize: "13px",
1430
- color: "color-mix(in srgb, var(--uix-text) 85%, transparent)",
1431
- transition: "background-color var(--uix-dur-fast) var(--uix-ease-out)"
1432
- },
1433
- onMouseEnter: (e) => {
1434
- e.currentTarget.style.background = "var(--uix-bg-hover)";
1435
- },
1436
- onMouseLeave: (e) => {
1437
- e.currentTarget.style.background = "transparent";
1438
- },
1471
+ className: "uix-favourites__menu-item",
1439
1472
  children: action.label
1440
1473
  }
1441
1474
  ) }, action.label))
@@ -1446,7 +1479,7 @@ function FavouriteRow({
1446
1479
  }
1447
1480
  )
1448
1481
  );
1449
- }
1482
+ });
1450
1483
 
1451
1484
  // src/components/Alert.tsx
1452
1485
  var import_jsx_runtime22 = require("react/jsx-runtime");
@@ -1502,10 +1535,11 @@ function Toaster({ children, className, ...props }) {
1502
1535
  }
1503
1536
 
1504
1537
  // src/components/Table.tsx
1538
+ var import_react18 = require("react");
1505
1539
  var import_jsx_runtime25 = require("react/jsx-runtime");
1506
- function TableWrap({ children, className, ...props }) {
1507
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: cx("uix-table-wrap", className), ...props, children });
1508
- }
1540
+ var TableWrap = (0, import_react18.forwardRef)(function TableWrap2({ children, className, ...props }, ref) {
1541
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { ref, className: cx("uix-table-wrap", className), ...props, children });
1542
+ });
1509
1543
  function Table({
1510
1544
  density = "standard",
1511
1545
  zebra = true,
@@ -1735,13 +1769,13 @@ function Stat({ label, value, icon, meta, trend, className, ...props }) {
1735
1769
  }
1736
1770
 
1737
1771
  // src/components/Tooltip.tsx
1738
- var import_react17 = require("react");
1772
+ var import_react19 = require("react");
1739
1773
  var import_jsx_runtime31 = require("react/jsx-runtime");
1740
1774
  function Tooltip({ label, placement = "top", className, children, ...props }) {
1741
- const triggerRef = (0, import_react17.useRef)(null);
1742
- const bubbleRef = (0, import_react17.useRef)(null);
1743
- const [open, setOpen] = (0, import_react17.useState)(false);
1744
- const id = (0, import_react17.useId)();
1775
+ const triggerRef = (0, import_react19.useRef)(null);
1776
+ const bubbleRef = (0, import_react19.useRef)(null);
1777
+ const [open, setOpen] = (0, import_react19.useState)(false);
1778
+ const id = (0, import_react19.useId)();
1745
1779
  useAnchoredPosition(triggerRef, bubbleRef, { open, placement, offset: 6 });
1746
1780
  const show = () => {
1747
1781
  const b = bubbleRef.current;
@@ -1763,7 +1797,7 @@ function Tooltip({ label, placement = "top", className, children, ...props }) {
1763
1797
  }
1764
1798
  setOpen(false);
1765
1799
  };
1766
- (0, import_react17.useEffect)(() => () => {
1800
+ (0, import_react19.useEffect)(() => () => {
1767
1801
  const b = bubbleRef.current;
1768
1802
  if (b && b.matches(":popover-open")) {
1769
1803
  try {
@@ -1894,11 +1928,12 @@ function ComposerBar({ children, className, ...props }) {
1894
1928
  }
1895
1929
 
1896
1930
  // src/components/Segmented.tsx
1897
- var import_react18 = require("react");
1931
+ var import_react20 = require("react");
1898
1932
  var import_jsx_runtime37 = require("react/jsx-runtime");
1899
- var SegmentedCtx = (0, import_react18.createContext)({});
1933
+ var SegmentedCtx = (0, import_react20.createContext)({});
1900
1934
  function Segmented({ value, onChange, children, className, ...props }) {
1901
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(SegmentedCtx.Provider, { value: { value, onChange }, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { role: "group", className: cx("uix-segmented", className), ...props, children }) });
1935
+ const contextValue = (0, import_react20.useMemo)(() => ({ value, onChange }), [value, onChange]);
1936
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(SegmentedCtx.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { role: "group", className: cx("uix-segmented", className), ...props, children }) });
1902
1937
  }
1903
1938
  function SegmentedOption({
1904
1939
  value,
@@ -1907,7 +1942,7 @@ function SegmentedOption({
1907
1942
  onClick,
1908
1943
  ...props
1909
1944
  }) {
1910
- const ctx = (0, import_react18.useContext)(SegmentedCtx);
1945
+ const ctx = (0, import_react20.useContext)(SegmentedCtx);
1911
1946
  const selected = ctx.value === value;
1912
1947
  return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1913
1948
  "button",
@@ -1956,7 +1991,7 @@ function Note({ tone, icon, children, className, ...props }) {
1956
1991
  }
1957
1992
 
1958
1993
  // src/components/Popover.tsx
1959
- var import_react19 = require("react");
1994
+ var import_react21 = require("react");
1960
1995
  var import_jsx_runtime40 = require("react/jsx-runtime");
1961
1996
  function Popover({
1962
1997
  popover = "auto",
@@ -1967,15 +2002,15 @@ function Popover({
1967
2002
  children,
1968
2003
  ...props
1969
2004
  }) {
1970
- const ref = (0, import_react19.useRef)(null);
1971
- const [open, setOpen] = (0, import_react19.useState)(false);
2005
+ const ref = (0, import_react21.useRef)(null);
2006
+ const [open, setOpen] = (0, import_react21.useState)(false);
1972
2007
  const popoverAttr = { popover };
1973
2008
  const reposition = useAnchoredPosition(anchor ?? null, ref, {
1974
2009
  open: open && !!anchor,
1975
2010
  placement,
1976
2011
  offset
1977
2012
  });
1978
- (0, import_react19.useEffect)(() => {
2013
+ (0, import_react21.useEffect)(() => {
1979
2014
  const el = ref.current;
1980
2015
  if (!el || !anchor) return;
1981
2016
  const sync = () => {
@@ -2073,7 +2108,7 @@ function KanbanCard({ title, meta, children, className, ...props }) {
2073
2108
  }
2074
2109
 
2075
2110
  // src/components/Tree.tsx
2076
- var import_react20 = require("react");
2111
+ var import_react22 = require("react");
2077
2112
 
2078
2113
  // src/tree-model.ts
2079
2114
  function flattenTree(nodes, expanded, level = 1) {
@@ -2185,12 +2220,12 @@ function Tree({
2185
2220
  className,
2186
2221
  ...props
2187
2222
  }) {
2188
- const [internalExpanded, setInternalExpanded] = (0, import_react20.useState)(defaultExpanded ?? /* @__PURE__ */ new Set());
2223
+ const [internalExpanded, setInternalExpanded] = (0, import_react22.useState)(defaultExpanded ?? /* @__PURE__ */ new Set());
2189
2224
  const expanded = controlledExpanded ?? internalExpanded;
2190
- const rootRef = (0, import_react20.useRef)(null);
2191
- const [focusedId, setFocusedId] = (0, import_react20.useState)(void 0);
2225
+ const rootRef = (0, import_react22.useRef)(null);
2226
+ const [focusedId, setFocusedId] = (0, import_react22.useState)(void 0);
2192
2227
  const selectable = selected !== void 0 || onSelect != null;
2193
- const handleToggle = (0, import_react20.useCallback)((id) => {
2228
+ const handleToggle = (0, import_react22.useCallback)((id) => {
2194
2229
  if (!controlledExpanded) {
2195
2230
  setInternalExpanded((prev) => {
2196
2231
  const next = new Set(prev);
@@ -2201,7 +2236,7 @@ function Tree({
2201
2236
  }
2202
2237
  onToggle?.(id);
2203
2238
  }, [controlledExpanded, onToggle]);
2204
- const flat = (0, import_react20.useMemo)(() => flattenTree(nodes, expanded), [nodes, expanded]);
2239
+ const flat = (0, import_react22.useMemo)(() => flattenTree(nodes, expanded), [nodes, expanded]);
2205
2240
  const useVirtual = virtualize ?? shouldVirtualize(flat.length);
2206
2241
  const tabbableId = focusedId ?? selected ?? nodes[0]?.id;
2207
2242
  if (useVirtual) {
@@ -2305,12 +2340,12 @@ function VirtualTreeView({
2305
2340
  className,
2306
2341
  ...props
2307
2342
  }) {
2308
- const scrollRef = (0, import_react20.useRef)(null);
2309
- const [scrollTop, setScrollTop] = (0, import_react20.useState)(0);
2310
- const pendingFocus = (0, import_react20.useRef)(false);
2343
+ const scrollRef = (0, import_react22.useRef)(null);
2344
+ const [scrollTop, setScrollTop] = (0, import_react22.useState)(0);
2345
+ const pendingFocus = (0, import_react22.useRef)(false);
2311
2346
  const win = virtualWindow(scrollTop, maxHeight, rowHeight, flat.length);
2312
2347
  const rows = flat.slice(win.start, win.end);
2313
- (0, import_react20.useEffect)(() => {
2348
+ (0, import_react22.useEffect)(() => {
2314
2349
  if (!pendingFocus.current || focusedId == null) return;
2315
2350
  const el = scrollRef.current?.querySelector(`[role="treeitem"][data-id="${CSS.escape(focusedId)}"]`);
2316
2351
  if (el) {
@@ -2565,12 +2600,1664 @@ function StatusRow({ indicator, name, meta, className, ...props }) {
2565
2600
  meta != null && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "uix-list-meta", children: meta })
2566
2601
  ] });
2567
2602
  }
2603
+
2604
+ // src/rule-builder-model.ts
2605
+ function isRuleGroup(node) {
2606
+ return "conditions" in node;
2607
+ }
2608
+ function ruleDepth(group) {
2609
+ return 1 + Math.max(0, ...group.conditions.filter(isRuleGroup).map(ruleDepth));
2610
+ }
2611
+ function findRuleNodeDepth(group, id, depth = 1) {
2612
+ if (group.id === id) return depth;
2613
+ for (const node of group.conditions) {
2614
+ if (node.id === id) return depth + 1;
2615
+ if (isRuleGroup(node)) {
2616
+ const found = findRuleNodeDepth(node, id, depth + 1);
2617
+ if (found !== void 0) return found;
2618
+ }
2619
+ }
2620
+ return void 0;
2621
+ }
2622
+ function mapRuleGroup(group, id, update) {
2623
+ if (group.id === id) return update(group);
2624
+ return {
2625
+ ...group,
2626
+ conditions: group.conditions.map((node) => {
2627
+ if (node.id === id) return update(node);
2628
+ return isRuleGroup(node) ? mapRuleGroup(node, id, update) : node;
2629
+ })
2630
+ };
2631
+ }
2632
+ function appendRuleNode(group, parentId, node, maxDepth = 3) {
2633
+ const parentDepth = findRuleNodeDepth(group, parentId);
2634
+ if (parentDepth === void 0) return group;
2635
+ const addedDepth = isRuleGroup(node) ? ruleDepth(node) : 0;
2636
+ if (parentDepth + addedDepth > maxDepth) return group;
2637
+ return mapRuleGroup(group, parentId, (parent) => isRuleGroup(parent) ? { ...parent, conditions: [...parent.conditions, node] } : parent);
2638
+ }
2639
+ function removeRuleNode(group, id) {
2640
+ return {
2641
+ ...group,
2642
+ conditions: group.conditions.filter((node) => node.id !== id).map((node) => isRuleGroup(node) ? removeRuleNode(node, id) : node)
2643
+ };
2644
+ }
2645
+ function moveRuleNode(group, id, direction) {
2646
+ const index = group.conditions.findIndex((node) => node.id === id);
2647
+ if (index >= 0) {
2648
+ const target = index + direction;
2649
+ if (target < 0 || target >= group.conditions.length) return group;
2650
+ const conditions = [...group.conditions];
2651
+ [conditions[index], conditions[target]] = [conditions[target], conditions[index]];
2652
+ return { ...group, conditions };
2653
+ }
2654
+ return {
2655
+ ...group,
2656
+ conditions: group.conditions.map((node) => isRuleGroup(node) ? moveRuleNode(node, id, direction) : node)
2657
+ };
2658
+ }
2659
+ function validateRuleDefinition(value, maxDepth = 3) {
2660
+ const issues = [];
2661
+ const ids = /* @__PURE__ */ new Set();
2662
+ const visit = (group, path, depth) => {
2663
+ if (ids.has(group.id)) issues.push({ path, message: `Duplicate id \u201C${group.id}\u201D.` });
2664
+ ids.add(group.id);
2665
+ if (depth > maxDepth) issues.push({ path, message: `Nesting exceeds ${maxDepth} levels.` });
2666
+ if (group.conditions.length === 0) issues.push({ path, message: "Add at least one condition." });
2667
+ group.conditions.forEach((node, index) => {
2668
+ const nodePath = `${path}.conditions[${index}]`;
2669
+ if (ids.has(node.id)) issues.push({ path: nodePath, message: `Duplicate id \u201C${node.id}\u201D.` });
2670
+ if (isRuleGroup(node)) visit(node, nodePath, depth + 1);
2671
+ else {
2672
+ ids.add(node.id);
2673
+ if (!node.field) issues.push({ path: nodePath, message: "Choose a field." });
2674
+ if (!node.operator) issues.push({ path: nodePath, message: "Choose an operator." });
2675
+ }
2676
+ });
2677
+ };
2678
+ visit(value.when, "when", 1);
2679
+ if (value.then.length === 0) issues.push({ path: "then", message: "Add at least one action." });
2680
+ value.then.forEach((action, index) => {
2681
+ if (ids.has(action.id)) issues.push({ path: `then[${index}]`, message: `Duplicate id \u201C${action.id}\u201D.` });
2682
+ ids.add(action.id);
2683
+ if (!action.type) issues.push({ path: `then[${index}]`, message: "Choose an action." });
2684
+ });
2685
+ return issues;
2686
+ }
2687
+ function summarizeRule(value, labels = {}) {
2688
+ const formatValue = (input) => input === void 0 ? "" : ` ${JSON.stringify(input)}`;
2689
+ const formatGroup = (group) => group.conditions.map(
2690
+ (node) => isRuleGroup(node) ? `(${formatGroup(node)})` : `${labels.fields?.[node.field] ?? node.field} ${labels.operators?.[node.operator] ?? node.operator}${formatValue(node.value)}`
2691
+ ).join(` ${group.combinator.toUpperCase()} `);
2692
+ const actions = value.then.map((action) => labels.actions?.[action.type] ?? action.type).join(", ");
2693
+ return `When ${formatGroup(value.when) || "no conditions"}, then ${actions || "no actions"}.`;
2694
+ }
2695
+
2696
+ // src/components/RuleBuilder.tsx
2697
+ var import_jsx_runtime46 = require("react/jsx-runtime");
2698
+ var ruleId = 0;
2699
+ var nextId = (prefix) => `${prefix}-${Date.now().toString(36)}-${(++ruleId).toString(36)}`;
2700
+ function defaultCondition(fields, operators) {
2701
+ const field = fields[0];
2702
+ const operator = operators.find((item) => field?.operators?.includes(item.id)) ?? operators[0];
2703
+ return { id: nextId("condition"), field: field?.id ?? "", operator: operator?.id ?? "", value: "" };
2704
+ }
2705
+ function RuleGroupEditor({ group, depth, root, fields, operators, maxDepth, disabled, onRootChange, labels }) {
2706
+ const updateNode = (id, update) => onRootChange(mapRuleGroup(root, id, update));
2707
+ const addCondition = () => onRootChange(appendRuleNode(root, group.id, defaultCondition(fields, operators), maxDepth));
2708
+ const addGroup = () => onRootChange(appendRuleNode(root, group.id, {
2709
+ id: nextId("group"),
2710
+ combinator: "and",
2711
+ conditions: [defaultCondition(fields, operators)]
2712
+ }, maxDepth));
2713
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("fieldset", { className: "uix-rule-builder__group", "data-depth": depth, children: [
2714
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("legend", { className: "uix-rule-builder__group-heading", children: [
2715
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { children: depth === 1 ? "When" : "Group" }),
2716
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("label", { children: [
2717
+ "Match",
2718
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("select", { className: "uix-select uix-select--sm", value: group.combinator, disabled, onChange: (event) => updateNode(group.id, (node) => ({ ...node, combinator: event.currentTarget.value })), children: [
2719
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("option", { value: "and", children: "all (AND)" }),
2720
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("option", { value: "or", children: "any (OR)" })
2721
+ ] })
2722
+ ] })
2723
+ ] }),
2724
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "uix-rule-builder__rows", children: group.conditions.map((node, index) => isRuleGroup(node) ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "uix-rule-builder__nested", children: [
2725
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(RuleGroupEditor, { ...{ group: node, depth: depth + 1, root, fields, operators, maxDepth, disabled, onRootChange, labels } }),
2726
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(RowControls, { label: "group", first: index === 0, last: index === group.conditions.length - 1, disabled, onUp: () => onRootChange(moveRuleNode(root, node.id, -1)), onDown: () => onRootChange(moveRuleNode(root, node.id, 1)), onRemove: () => onRootChange(removeRuleNode(root, node.id)) })
2727
+ ] }, node.id) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2728
+ ConditionRow,
2729
+ {
2730
+ condition: node,
2731
+ fields,
2732
+ operators,
2733
+ disabled,
2734
+ onChange: (condition) => updateNode(node.id, () => condition),
2735
+ controls: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(RowControls, { label: "condition", first: index === 0, last: index === group.conditions.length - 1, disabled, onUp: () => onRootChange(moveRuleNode(root, node.id, -1)), onDown: () => onRootChange(moveRuleNode(root, node.id, 1)), onRemove: () => onRootChange(removeRuleNode(root, node.id)) })
2736
+ },
2737
+ node.id
2738
+ )) }),
2739
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "uix-rule-builder__add", children: [
2740
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", onClick: addCondition, disabled: disabled || fields.length === 0 || operators.length === 0, children: labels.addCondition }),
2741
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: addGroup, disabled: disabled || depth >= maxDepth, children: labels.addGroup })
2742
+ ] })
2743
+ ] });
2744
+ }
2745
+ function ConditionRow({ condition, fields, operators, disabled, onChange, controls }) {
2746
+ const field = fields.find((item) => item.id === condition.field);
2747
+ const availableOperators = field?.operators?.length ? operators.filter((item) => field.operators.includes(item.id)) : operators;
2748
+ const operator = operators.find((item) => item.id === condition.operator);
2749
+ const setField = (id) => {
2750
+ const nextField = fields.find((item) => item.id === id);
2751
+ const nextOperator = operators.find((item) => nextField?.operators?.includes(item.id)) ?? availableOperators[0] ?? operators[0];
2752
+ onChange({ ...condition, field: id, operator: nextOperator?.id ?? "" });
2753
+ };
2754
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "uix-rule-builder__condition", children: [
2755
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("label", { children: [
2756
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "uix-visually-hidden", children: "Field" }),
2757
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("select", { className: "uix-select", value: condition.field, onChange: (event) => setField(event.currentTarget.value), disabled, children: fields.map((item) => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("option", { value: item.id, children: item.label }, item.id)) })
2758
+ ] }),
2759
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("label", { children: [
2760
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "uix-visually-hidden", children: "Operator" }),
2761
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("select", { className: "uix-select", value: condition.operator, onChange: (event) => onChange({ ...condition, operator: event.currentTarget.value }), disabled, children: availableOperators.map((item) => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("option", { value: item.id, children: item.label }, item.id)) })
2762
+ ] }),
2763
+ operator?.requiresValue !== false && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "uix-rule-builder__value", children: field?.renderValueEditor?.({ condition, onChange: (value) => onChange({ ...condition, value }), disabled }) ?? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("input", { className: "uix-input", value: typeof condition.value === "string" || typeof condition.value === "number" ? condition.value : condition.value == null ? "" : JSON.stringify(condition.value), onChange: (event) => onChange({ ...condition, value: event.currentTarget.value }), disabled, "aria-label": `Value for ${field?.label ?? "condition"}` }) }),
2764
+ controls
2765
+ ] });
2766
+ }
2767
+ function RowControls({ label, first, last, disabled, onUp, onDown, onRemove }) {
2768
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("span", { className: "uix-rule-builder__row-actions", children: [
2769
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: onUp, disabled: disabled || first, "aria-label": `Move ${label} up`, children: "Up" }),
2770
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: onDown, disabled: disabled || last, "aria-label": `Move ${label} down`, children: "Down" }),
2771
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: onRemove, disabled, "aria-label": `Remove ${label}`, children: "Remove" })
2772
+ ] });
2773
+ }
2774
+ function RuleBuilder({
2775
+ value,
2776
+ onChange,
2777
+ fields,
2778
+ operators,
2779
+ actions,
2780
+ maxDepth = 3,
2781
+ readOnly = false,
2782
+ validate,
2783
+ className,
2784
+ addConditionLabel = "Add condition",
2785
+ addGroupLabel = "Add group",
2786
+ addActionLabel = "Add action"
2787
+ }) {
2788
+ const issues = [...validateRuleDefinition(value, maxDepth), ...validate?.(value) ?? []];
2789
+ const fieldLabels = Object.fromEntries(fields.map((field) => [field.id, field.label]));
2790
+ const operatorLabels = Object.fromEntries(operators.map((operator) => [operator.id, operator.label]));
2791
+ const actionLabels = Object.fromEntries(actions.map((action) => [action.id, action.label]));
2792
+ if (readOnly) return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("section", { className: cx("uix-rule-builder uix-rule-builder--summary", className), "aria-label": "Rule summary", children: [
2793
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { children: summarizeRule(value, { fields: fieldLabels, operators: operatorLabels, actions: actionLabels }) }),
2794
+ issues.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("p", { className: "uix-rule-builder__invalid", children: [
2795
+ "Rule has ",
2796
+ issues.length,
2797
+ " validation issue",
2798
+ issues.length === 1 ? "" : "s",
2799
+ "."
2800
+ ] })
2801
+ ] });
2802
+ const updateActions = (next) => onChange({ ...value, then: next });
2803
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("section", { className: cx("uix-rule-builder", className), "aria-label": "Rule builder", children: [
2804
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(RuleGroupEditor, { group: value.when, depth: 1, root: value.when, fields, operators, maxDepth, disabled: readOnly, onRootChange: (when) => onChange({ ...value, when }), labels: { addCondition: addConditionLabel, addGroup: addGroupLabel } }),
2805
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("fieldset", { className: "uix-rule-builder__actions", children: [
2806
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("legend", { children: "Then" }),
2807
+ value.then.map((action, index) => {
2808
+ const definition = actions.find((item) => item.id === action.type);
2809
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "uix-rule-builder__action", children: [
2810
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("select", { className: "uix-select", value: action.type, onChange: (event) => updateActions(value.then.map((item) => item.id === action.id ? { ...item, type: event.currentTarget.value } : item)), "aria-label": "Action type", children: actions.map((item) => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("option", { value: item.id, children: item.label }, item.id)) }),
2811
+ definition?.renderParameters?.(action, (parameters) => updateActions(value.then.map((item) => item.id === action.id ? { ...item, parameters } : item)), false),
2812
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2813
+ RowControls,
2814
+ {
2815
+ label: "action",
2816
+ first: index === 0,
2817
+ last: index === value.then.length - 1,
2818
+ disabled: false,
2819
+ onUp: () => {
2820
+ const next = [...value.then];
2821
+ [next[index - 1], next[index]] = [next[index], next[index - 1]];
2822
+ updateActions(next);
2823
+ },
2824
+ onDown: () => {
2825
+ const next = [...value.then];
2826
+ [next[index + 1], next[index]] = [next[index], next[index + 1]];
2827
+ updateActions(next);
2828
+ },
2829
+ onRemove: () => updateActions(value.then.filter((item) => item.id !== action.id))
2830
+ }
2831
+ )
2832
+ ] }, action.id);
2833
+ }),
2834
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", disabled: actions.length === 0, onClick: () => updateActions([...value.then, { id: nextId("action"), type: actions[0]?.id ?? "", parameters: {} }]), children: addActionLabel })
2835
+ ] }),
2836
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "uix-rule-builder__validation", "aria-live": "polite", children: issues.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { children: "Rule is valid." }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { children: [
2837
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("strong", { children: [
2838
+ issues.length,
2839
+ " validation issue",
2840
+ issues.length === 1 ? "" : "s"
2841
+ ] }),
2842
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("ul", { children: issues.map((issue, index) => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("li", { children: issue.message }, `${issue.path}-${index}`)) })
2843
+ ] }) })
2844
+ ] });
2845
+ }
2846
+
2847
+ // src/components/BuilderCanvas.tsx
2848
+ var import_react23 = require("react");
2849
+ var import_jsx_runtime47 = require("react/jsx-runtime");
2850
+ function BuilderCanvas({
2851
+ items,
2852
+ onItemsChange,
2853
+ palette,
2854
+ selectedId: controlledSelectedId,
2855
+ onSelect,
2856
+ renderItem,
2857
+ renderProperties,
2858
+ loading,
2859
+ error,
2860
+ onRetry,
2861
+ emptyMessage = "Add an item from the palette to begin.",
2862
+ className
2863
+ }) {
2864
+ const [internalSelectedId, setInternalSelectedId] = (0, import_react23.useState)();
2865
+ const [draggedId, setDraggedId] = (0, import_react23.useState)();
2866
+ const propertyHeadingRef = (0, import_react23.useRef)(null);
2867
+ const shouldFocusProperties = (0, import_react23.useRef)(false);
2868
+ const selectedId = controlledSelectedId ?? internalSelectedId;
2869
+ const selected = items.find((item) => item.id === selectedId);
2870
+ (0, import_react23.useEffect)(() => {
2871
+ if (shouldFocusProperties.current && selected) {
2872
+ propertyHeadingRef.current?.focus();
2873
+ shouldFocusProperties.current = false;
2874
+ }
2875
+ }, [selected]);
2876
+ const select = (id, focusProperties = true) => {
2877
+ shouldFocusProperties.current = focusProperties;
2878
+ if (controlledSelectedId === void 0) setInternalSelectedId(id);
2879
+ onSelect?.(id);
2880
+ };
2881
+ const move = (id, direction) => {
2882
+ const index = items.findIndex((item) => item.id === id);
2883
+ const target = index + direction;
2884
+ if (index < 0 || target < 0 || target >= items.length) return;
2885
+ onItemsChange(reorder(items, index, target));
2886
+ };
2887
+ const add = (paletteItem) => {
2888
+ const item = paletteItem.create();
2889
+ onItemsChange([...items, item]);
2890
+ select(item.id);
2891
+ };
2892
+ const remove = (id) => {
2893
+ onItemsChange(items.filter((item) => item.id !== id));
2894
+ if (selectedId === id) select(void 0, false);
2895
+ };
2896
+ const drop = (event, targetId) => {
2897
+ event.preventDefault();
2898
+ if (!draggedId || draggedId === targetId) return;
2899
+ const from = items.findIndex((item) => item.id === draggedId);
2900
+ const to = items.findIndex((item) => item.id === targetId);
2901
+ if (from >= 0 && to >= 0) onItemsChange(reorder(items, from, to));
2902
+ setDraggedId(void 0);
2903
+ };
2904
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("section", { className: cx("uix-builder-canvas", className), "aria-label": "Builder canvas", children: [
2905
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("aside", { className: "uix-builder-canvas__palette", "aria-label": "Item palette", children: [
2906
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("h3", { children: "Palette" }),
2907
+ palette.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { children: "No item types available." }) : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("ul", { children: palette.map((item) => /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("button", { type: "button", onClick: () => add(item), children: [
2908
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("strong", { children: item.label }),
2909
+ item.description && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { children: item.description }),
2910
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { "aria-hidden": "true", children: "Add" })
2911
+ ] }) }, item.id)) })
2912
+ ] }),
2913
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "uix-builder-canvas__workspace", children: [
2914
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("h3", { children: "Canvas" }),
2915
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { className: "uix-builder-canvas__state", role: "status", children: "Loading canvas\u2026" }) : error ? /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "uix-builder-canvas__state", role: "alert", children: [
2916
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { children: error }),
2917
+ onRetry && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary", onClick: onRetry, children: "Try again" })
2918
+ ] }) : items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "uix-builder-canvas__state", children: [
2919
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { children: emptyMessage }),
2920
+ palette[0] && /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("button", { type: "button", className: "uix-btn uix-btn--primary", onClick: () => add(palette[0]), children: [
2921
+ "Add ",
2922
+ palette[0].label
2923
+ ] })
2924
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("ol", { className: "uix-builder-canvas__items", children: items.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
2925
+ "li",
2926
+ {
2927
+ draggable: true,
2928
+ onDragStart: () => setDraggedId(item.id),
2929
+ onDragEnd: () => setDraggedId(void 0),
2930
+ onDragOver: (event) => event.preventDefault(),
2931
+ onDrop: (event) => drop(event, item.id),
2932
+ className: cx("uix-builder-canvas__item", selectedId === item.id && "uix-builder-canvas__item--selected"),
2933
+ children: [
2934
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("button", { type: "button", className: "uix-builder-canvas__item-main", "aria-pressed": selectedId === item.id, onClick: () => select(item.id), children: renderItem?.(item, selectedId === item.id) ?? /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_jsx_runtime47.Fragment, { children: [
2935
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("strong", { children: item.label }),
2936
+ item.description && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { children: item.description })
2937
+ ] }) }),
2938
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("span", { className: "uix-builder-canvas__item-actions", children: [
2939
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("button", { type: "button", onClick: () => move(item.id, -1), disabled: index === 0, "aria-label": `Move ${item.label} up`, children: "Up" }),
2940
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("button", { type: "button", onClick: () => move(item.id, 1), disabled: index === items.length - 1, "aria-label": `Move ${item.label} down`, children: "Down" }),
2941
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("button", { type: "button", onClick: () => remove(item.id), "aria-label": `Remove ${item.label}`, children: "Remove" })
2942
+ ] })
2943
+ ]
2944
+ },
2945
+ item.id
2946
+ )) })
2947
+ ] }),
2948
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("aside", { className: "uix-builder-canvas__properties", "aria-label": "Item properties", children: [
2949
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("h3", { ref: propertyHeadingRef, tabIndex: -1, children: "Properties" }),
2950
+ !selected ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { children: "Select a canvas item to edit its properties." }) : renderProperties?.(selected, (updated) => onItemsChange(items.map((item) => item.id === updated.id ? updated : item))) ?? /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("dl", { children: [
2951
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("dt", { children: "Type" }),
2952
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("dd", { children: selected.type }),
2953
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("dt", { children: "Label" }),
2954
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("dd", { children: selected.label })
2955
+ ] })
2956
+ ] })
2957
+ ] });
2958
+ }
2959
+
2960
+ // src/components/SchedulingCalendar.tsx
2961
+ var import_react24 = require("react");
2962
+
2963
+ // src/calendar-model.ts
2964
+ function fromDateKey(value) {
2965
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
2966
+ if (!match) throw new TypeError(`Invalid ISO date: ${value}`);
2967
+ return new Date(Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3])));
2968
+ }
2969
+ function toDateKey(date) {
2970
+ return `${date.getUTCFullYear().toString().padStart(4, "0")}-${(date.getUTCMonth() + 1).toString().padStart(2, "0")}-${date.getUTCDate().toString().padStart(2, "0")}`;
2971
+ }
2972
+ function addCalendarDays(value, amount) {
2973
+ const date = fromDateKey(value);
2974
+ date.setUTCDate(date.getUTCDate() + amount);
2975
+ return toDateKey(date);
2976
+ }
2977
+ function addCalendarMonths(value, amount) {
2978
+ const date = fromDateKey(value);
2979
+ const day = date.getUTCDate();
2980
+ date.setUTCDate(1);
2981
+ date.setUTCMonth(date.getUTCMonth() + amount);
2982
+ const last = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, 0)).getUTCDate();
2983
+ date.setUTCDate(Math.min(day, last));
2984
+ return toDateKey(date);
2985
+ }
2986
+ function startOfMonth(value) {
2987
+ return `${value.slice(0, 7)}-01`;
2988
+ }
2989
+ function buildMonthGrid(month, weekStartsOn = 1) {
2990
+ const first = fromDateKey(startOfMonth(month));
2991
+ const offset = (first.getUTCDay() - weekStartsOn + 7) % 7;
2992
+ const start = addCalendarDays(toDateKey(first), -offset);
2993
+ return Array.from({ length: 42 }, (_, index) => {
2994
+ const date = addCalendarDays(start, index);
2995
+ const parsed = fromDateKey(date);
2996
+ return {
2997
+ date,
2998
+ day: parsed.getUTCDate(),
2999
+ inMonth: date.slice(0, 7) === month.slice(0, 7),
3000
+ weekday: parsed.getUTCDay()
3001
+ };
3002
+ });
3003
+ }
3004
+ function zonedDateKey(instant, timeZone) {
3005
+ const date = instant instanceof Date ? instant : new Date(instant);
3006
+ if (Number.isNaN(date.getTime())) throw new TypeError(`Invalid instant: ${String(instant)}`);
3007
+ const parts = new Intl.DateTimeFormat("en-CA", {
3008
+ timeZone,
3009
+ year: "numeric",
3010
+ month: "2-digit",
3011
+ day: "2-digit"
3012
+ }).formatToParts(date);
3013
+ const get = (type) => parts.find((part) => part.type === type)?.value ?? "";
3014
+ return `${get("year")}-${get("month")}-${get("day")}`;
3015
+ }
3016
+ function zonedDateSpan(start, end, timeZone) {
3017
+ const startKey = zonedDateKey(start, timeZone);
3018
+ const endKey = zonedDateKey(end, timeZone);
3019
+ return startKey <= endKey ? { start: startKey, end: endKey } : { start: endKey, end: startKey };
3020
+ }
3021
+ function enumerateDateSpan(span, limit = 370) {
3022
+ const dates = [];
3023
+ let current = span.start;
3024
+ while (current <= span.end && dates.length < limit) {
3025
+ dates.push(current);
3026
+ current = addCalendarDays(current, 1);
3027
+ }
3028
+ return dates;
3029
+ }
3030
+ function selectRangeDate(current, date) {
3031
+ if (!current.start || current.end) return { start: date };
3032
+ return date < current.start ? { start: date, end: current.start } : { start: current.start, end: date };
3033
+ }
3034
+ function isDateInRange(date, value) {
3035
+ return !!value.start && !!value.end && date >= value.start && date <= value.end;
3036
+ }
3037
+ function isDateUnavailable(date, options) {
3038
+ return !!options.min && date < options.min || !!options.max && date > options.max || !!options.isDisabled?.(date);
3039
+ }
3040
+
3041
+ // src/components/SchedulingCalendar.tsx
3042
+ var import_jsx_runtime48 = require("react/jsx-runtime");
3043
+ var stateLabel = { scheduled: "Scheduled", conflicted: "Conflicted", "in-progress": "In progress", "blackout-violation": "Blackout violation" };
3044
+ var stateTone = (state) => state === "scheduled" ? "info" : state === "in-progress" ? "success" : "danger";
3045
+ var dateLabel = (date, locale) => new Intl.DateTimeFormat(locale, { dateStyle: "full", timeZone: "UTC" }).format(/* @__PURE__ */ new Date(`${date}T00:00:00Z`));
3046
+ var EMPTY_OVERLAYS = [];
3047
+ function SchedulingCalendar({
3048
+ entries,
3049
+ anchorDate,
3050
+ timeZone,
3051
+ view: controlledView,
3052
+ onViewChange,
3053
+ onAnchorDateChange,
3054
+ onSelectEntry,
3055
+ overlays = EMPTY_OVERLAYS,
3056
+ filter,
3057
+ renderEntry,
3058
+ locale,
3059
+ loading,
3060
+ error,
3061
+ onRetry,
3062
+ className
3063
+ }) {
3064
+ const [internalView, setInternalView] = (0, import_react24.useState)("month");
3065
+ const view = controlledView ?? internalView;
3066
+ const [activeDate, setActiveDate] = (0, import_react24.useState)(anchorDate);
3067
+ const gridRef = (0, import_react24.useRef)(null);
3068
+ const visibleEntries = (0, import_react24.useMemo)(() => entries.filter((entry) => filter?.(entry) ?? true), [entries, filter]);
3069
+ const entrySpans = (0, import_react24.useMemo)(() => new Map(visibleEntries.map((entry) => [entry.id, zonedDateSpan(entry.start, entry.end, timeZone)])), [visibleEntries, timeZone]);
3070
+ const overlaySpans = (0, import_react24.useMemo)(() => new Map(overlays.map((overlay) => [overlay.id, { start: overlay.start.slice(0, 10), end: overlay.end.slice(0, 10) }])), [overlays]);
3071
+ const monthDays = (0, import_react24.useMemo)(() => buildMonthGrid(startOfMonth(anchorDate)), [anchorDate]);
3072
+ const weekday = ((/* @__PURE__ */ new Date(`${anchorDate}T00:00:00Z`)).getUTCDay() + 6) % 7;
3073
+ const weekStart = addCalendarDays(anchorDate, -weekday);
3074
+ const weekDays = (0, import_react24.useMemo)(() => Array.from({ length: 7 }, (_, index) => addCalendarDays(weekStart, index)), [weekStart]);
3075
+ const days = (0, import_react24.useMemo)(() => view === "month" ? monthDays.map((day) => day.date) : weekDays, [view, monthDays, weekDays]);
3076
+ (0, import_react24.useEffect)(() => {
3077
+ if (view !== "agenda" && !days.includes(activeDate)) setActiveDate(days[0]);
3078
+ }, [activeDate, days, view]);
3079
+ const setView = (next) => {
3080
+ if (controlledView === void 0) setInternalView(next);
3081
+ onViewChange?.(next);
3082
+ };
3083
+ const movePeriod = (direction) => onAnchorDateChange?.(view === "month" ? addCalendarMonths(anchorDate, direction) : addCalendarDays(anchorDate, direction * 7));
3084
+ const focusDate = (date) => {
3085
+ setActiveDate(date);
3086
+ requestAnimationFrame(() => gridRef.current?.querySelector(`[data-calendar-date="${date}"]`)?.focus());
3087
+ };
3088
+ const onDayKeyDown = (event, date) => {
3089
+ const offset = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -7, ArrowDown: 7 }[event.key];
3090
+ if (offset) {
3091
+ event.preventDefault();
3092
+ focusDate(addCalendarDays(date, offset));
3093
+ } else if (event.key === "Home") {
3094
+ event.preventDefault();
3095
+ focusDate(days[0]);
3096
+ } else if (event.key === "End") {
3097
+ event.preventDefault();
3098
+ focusDate(days[days.length - 1]);
3099
+ }
3100
+ };
3101
+ const entriesFor = (date) => visibleEntries.filter((entry) => {
3102
+ const span = entrySpans.get(entry.id);
3103
+ return date >= span.start && date <= span.end;
3104
+ });
3105
+ const overlaysFor = (date) => overlays.filter((overlay) => {
3106
+ const span = overlaySpans.get(overlay.id);
3107
+ return date >= span.start && date <= span.end;
3108
+ });
3109
+ const formatInstant = (instant) => new Intl.DateTimeFormat(locale, { timeZone, dateStyle: "medium", timeStyle: "short" }).format(new Date(instant));
3110
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("section", { className: cx("uix-scheduling-calendar", className), "aria-label": `Scheduling calendar in ${timeZone}`, children: [
3111
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "uix-scheduling-calendar__header", children: [
3112
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { children: [
3113
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => movePeriod(-1), disabled: !onAnchorDateChange, children: "Previous" }),
3114
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => movePeriod(1), disabled: !onAnchorDateChange, children: "Next" })
3115
+ ] }),
3116
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("strong", { children: view === "month" ? new Intl.DateTimeFormat(locale, { month: "long", year: "numeric", timeZone: "UTC" }).format(/* @__PURE__ */ new Date(`${anchorDate}T00:00:00Z`)) : `${dateLabel(weekStart, locale)} \u2013 ${dateLabel(weekDays[weekDays.length - 1], locale)}` }),
3117
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: "uix-segmented", role: "group", "aria-label": "Calendar view", children: ["month", "week", "agenda"].map((option) => /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("button", { type: "button", className: "uix-segmented__option", "aria-pressed": view === option, "data-selected": view === option || void 0, onClick: () => setView(option), children: [
3118
+ option[0].toUpperCase(),
3119
+ option.slice(1)
3120
+ ] }, option)) })
3121
+ ] }),
3122
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: "uix-scheduling-calendar__state", role: "status", children: "Loading schedule\u2026" }) : error ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "uix-scheduling-calendar__state", role: "alert", children: [
3123
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("p", { children: error }),
3124
+ onRetry && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary", onClick: onRetry, children: "Try again" })
3125
+ ] }) : view === "agenda" ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: "uix-scheduling-calendar__agenda", role: "region", "aria-label": "Schedule agenda", children: visibleEntries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("p", { children: "No scheduled entries match the current filters." }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("ol", { children: [...visibleEntries].sort((a, b) => a.start.localeCompare(b.start)).map((entry) => {
3126
+ const state = entry.state ?? "scheduled";
3127
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("button", { type: "button", onClick: () => onSelectEntry?.(entry), children: [
3128
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { children: [
3129
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("strong", { children: renderEntry?.(entry) ?? entry.title }),
3130
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { children: [
3131
+ formatInstant(entry.start),
3132
+ " \u2013 ",
3133
+ formatInstant(entry.end)
3134
+ ] }),
3135
+ entry.meta && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { children: entry.meta })
3136
+ ] }),
3137
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(StatusPill, { tone: stateTone(state), children: stateLabel[state] })
3138
+ ] }) }, entry.id);
3139
+ }) }) }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { ref: gridRef, className: cx("uix-scheduling-calendar__grid", view === "week" && "uix-scheduling-calendar__grid--week"), role: "group", "aria-label": `${view} schedule`, children: days.map((date, index) => {
3140
+ const dayEntries = entriesFor(date);
3141
+ const dayOverlays = overlaysFor(date);
3142
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "uix-scheduling-calendar__day", "data-outside": view === "month" && !monthDays[index].inMonth || void 0, children: [
3143
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("button", { type: "button", className: "uix-scheduling-calendar__date", "data-calendar-date": date, tabIndex: activeDate === date ? 0 : -1, onFocus: () => setActiveDate(date), onKeyDown: (event) => onDayKeyDown(event, date), "aria-label": dateLabel(date, locale), children: Number(date.slice(-2)) }),
3144
+ dayOverlays.map((overlay) => /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "uix-scheduling-calendar__overlay", "data-kind": overlay.kind, children: overlay.label }, overlay.id)),
3145
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: "uix-scheduling-calendar__entries", children: dayEntries.map((entry) => {
3146
+ const span = entrySpans.get(entry.id);
3147
+ const state = entry.state ?? "scheduled";
3148
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("button", { type: "button", className: "uix-scheduling-calendar__entry", "data-state": state, "data-range-start": date === span.start || void 0, "data-range-end": date === span.end || void 0, onClick: () => onSelectEntry?.(entry), "aria-label": `${entry.title}, ${stateLabel[state]}, ${formatInstant(entry.start)} to ${formatInstant(entry.end)}`, children: [
3149
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { children: renderEntry?.(entry) ?? entry.title }),
3150
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "uix-visually-hidden", children: stateLabel[state] })
3151
+ ] }, entry.id);
3152
+ }) })
3153
+ ] }, date);
3154
+ }) }),
3155
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: "uix-scheduling-calendar__legend", "aria-label": "Schedule state legend", children: ["scheduled", "conflicted", "in-progress", "blackout-violation"].map((state) => /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { "data-state": state, children: stateLabel[state] }, state)) })
3156
+ ] });
3157
+ }
3158
+
3159
+ // src/components/DateRangePicker.tsx
3160
+ var import_react25 = require("react");
3161
+ var import_jsx_runtime49 = require("react/jsx-runtime");
3162
+ var monthLabel = (date, locale) => new Intl.DateTimeFormat(locale, { month: "long", year: "numeric", timeZone: "UTC" }).format(/* @__PURE__ */ new Date(`${date}T00:00:00Z`));
3163
+ var dateLabel2 = (date, locale) => new Intl.DateTimeFormat(locale, { dateStyle: "full", timeZone: "UTC" }).format(/* @__PURE__ */ new Date(`${date}T00:00:00Z`));
3164
+ function DateRangePicker({
3165
+ value,
3166
+ onChange,
3167
+ visibleMonth: controlledMonth,
3168
+ onVisibleMonthChange,
3169
+ months = 2,
3170
+ min,
3171
+ max,
3172
+ isDateDisabled,
3173
+ locale,
3174
+ label = "Choose date range",
3175
+ invalid,
3176
+ invalidMessage = "The selected date range is invalid.",
3177
+ disabled,
3178
+ className
3179
+ }) {
3180
+ const id = (0, import_react25.useId)();
3181
+ const [internalMonth, setInternalMonth] = (0, import_react25.useState)(() => startOfMonth(controlledMonth ?? value.start ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)));
3182
+ const visibleMonth = startOfMonth(controlledMonth ?? internalMonth);
3183
+ const [activeDate, setActiveDate] = (0, import_react25.useState)(value.end ?? value.start ?? visibleMonth);
3184
+ const rootRef = (0, import_react25.useRef)(null);
3185
+ const pendingFocus = (0, import_react25.useRef)(false);
3186
+ const count = Math.max(1, Math.min(3, months));
3187
+ const calendars = (0, import_react25.useMemo)(() => Array.from({ length: count }, (_, index) => {
3188
+ const month = startOfMonth(addCalendarMonths(visibleMonth, index));
3189
+ return { month, days: buildMonthGrid(month).filter((day) => day.inMonth) };
3190
+ }), [visibleMonth, count]);
3191
+ const rangeInvalid = invalid || !!value.start && !!value.end && value.start > value.end;
3192
+ (0, import_react25.useEffect)(() => {
3193
+ const first = calendars[0]?.days[0]?.date;
3194
+ const finalDays = calendars[calendars.length - 1]?.days;
3195
+ const last = finalDays?.[finalDays.length - 1]?.date;
3196
+ if (first && last && (activeDate < first || activeDate > last)) setActiveDate(first);
3197
+ }, [activeDate, calendars]);
3198
+ (0, import_react25.useEffect)(() => {
3199
+ if (!pendingFocus.current) return;
3200
+ rootRef.current?.querySelector(`[data-date="${activeDate}"]`)?.focus();
3201
+ pendingFocus.current = false;
3202
+ }, [activeDate, visibleMonth]);
3203
+ const setMonth = (month) => {
3204
+ const next = startOfMonth(month);
3205
+ if (controlledMonth === void 0) setInternalMonth(next);
3206
+ onVisibleMonthChange?.(next);
3207
+ };
3208
+ const focusDate = (date) => {
3209
+ setActiveDate(date);
3210
+ if (date < calendars[0].month || date.slice(0, 7) > calendars[calendars.length - 1].month.slice(0, 7)) setMonth(startOfMonth(date));
3211
+ pendingFocus.current = true;
3212
+ };
3213
+ const choose = (date) => {
3214
+ if (disabled || isDateUnavailable(date, { min, max, isDisabled: isDateDisabled })) return;
3215
+ onChange(selectRangeDate(value, date));
3216
+ setActiveDate(date);
3217
+ };
3218
+ const onDateKeyDown = (event, date) => {
3219
+ const offsets = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -7, ArrowDown: 7 };
3220
+ if (event.key in offsets) {
3221
+ event.preventDefault();
3222
+ focusDate(addCalendarDays(date, offsets[event.key]));
3223
+ return;
3224
+ }
3225
+ if (event.key === "Home" || event.key === "End") {
3226
+ event.preventDefault();
3227
+ const weekday = (/* @__PURE__ */ new Date(`${date}T00:00:00Z`)).getUTCDay();
3228
+ const mondayIndex = (weekday + 6) % 7;
3229
+ focusDate(addCalendarDays(date, event.key === "Home" ? -mondayIndex : 6 - mondayIndex));
3230
+ } else if (event.key === "PageUp" || event.key === "PageDown") {
3231
+ event.preventDefault();
3232
+ focusDate(addCalendarMonths(date, event.key === "PageUp" ? -1 : 1));
3233
+ }
3234
+ };
3235
+ const announcement = !value.start ? "No date range selected." : !value.end ? `Start date ${dateLabel2(value.start, locale)} selected. Choose an end date.` : `${dateLabel2(value.start, locale)} to ${dateLabel2(value.end, locale)} selected.`;
3236
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("section", { className: cx("uix-date-range-picker", rangeInvalid && "uix-date-range-picker--invalid", className), "aria-labelledby": `${id}-label`, children: [
3237
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "uix-date-range-picker__header", children: [
3238
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("h3", { id: `${id}-label`, children: label }),
3239
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { children: [
3240
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => setMonth(addCalendarMonths(visibleMonth, -1)), "aria-label": "Show previous month", children: "Previous" }),
3241
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => setMonth(addCalendarMonths(visibleMonth, 1)), "aria-label": "Show next month", children: "Next" })
3242
+ ] })
3243
+ ] }),
3244
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { ref: rootRef, className: "uix-date-range-picker__months", children: calendars.map(({ month, days }) => /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "uix-date-range-picker__month", children: [
3245
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("h4", { children: monthLabel(month, locale) }),
3246
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { className: "uix-date-range-picker__weekdays", "aria-hidden": "true", children: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"].map((day) => /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { children: day }, day)) }),
3247
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "uix-date-range-picker__grid", role: "group", "aria-label": monthLabel(month, locale), children: [
3248
+ Array.from({ length: ((/* @__PURE__ */ new Date(`${month}T00:00:00Z`)).getUTCDay() + 6) % 7 }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { "aria-hidden": "true" }, `blank-${index}`)),
3249
+ days.map((day) => {
3250
+ const unavailable = disabled || isDateUnavailable(day.date, { min, max, isDisabled: isDateDisabled });
3251
+ const edge = day.date === value.start ? "start" : day.date === value.end ? "end" : void 0;
3252
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3253
+ "button",
3254
+ {
3255
+ type: "button",
3256
+ "data-date": day.date,
3257
+ "data-range-edge": edge,
3258
+ "data-in-range": isDateInRange(day.date, value) || void 0,
3259
+ "aria-pressed": edge != null || isDateInRange(day.date, value),
3260
+ "aria-label": `${dateLabel2(day.date, locale)}${edge ? `, ${edge} of selected range` : isDateInRange(day.date, value) ? ", in selected range" : ""}`,
3261
+ tabIndex: activeDate === day.date ? 0 : -1,
3262
+ disabled: unavailable,
3263
+ onFocus: () => setActiveDate(day.date),
3264
+ onKeyDown: (event) => onDateKeyDown(event, day.date),
3265
+ onClick: () => choose(day.date),
3266
+ children: day.day
3267
+ },
3268
+ day.date
3269
+ );
3270
+ })
3271
+ ] })
3272
+ ] }, month)) }),
3273
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("p", { className: "uix-date-range-picker__selection", "aria-live": "polite", children: announcement }),
3274
+ rangeInvalid && /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("p", { className: "uix-field__error", role: "alert", children: invalidMessage })
3275
+ ] });
3276
+ }
3277
+
3278
+ // src/components/RelationshipGraph.tsx
3279
+ var import_react26 = require("react");
3280
+
3281
+ // src/relationship-graph-model.ts
3282
+ function boundRelationshipGraph(nodes, edges, maxNodes = 40) {
3283
+ const boundedNodes = nodes.slice(0, Math.max(1, maxNodes));
3284
+ const ids = new Set(boundedNodes.map((node) => node.id));
3285
+ return {
3286
+ nodes: boundedNodes,
3287
+ edges: edges.filter((edge) => ids.has(edge.source) && ids.has(edge.target)),
3288
+ omittedNodeCount: Math.max(0, nodes.length - boundedNodes.length)
3289
+ };
3290
+ }
3291
+ function relationshipNeighbors(nodeId, edges) {
3292
+ const result = [];
3293
+ for (const edge of edges) {
3294
+ if (edge.source === nodeId) result.push(edge.target);
3295
+ else if (edge.target === nodeId) result.push(edge.source);
3296
+ }
3297
+ return [...new Set(result)];
3298
+ }
3299
+ function layoutRelationshipGraph(nodes, edges, rootId = nodes[0]?.id) {
3300
+ if (nodes.length === 0) return [];
3301
+ const root = nodes.some((node) => node.id === rootId) ? rootId : nodes[0].id;
3302
+ const depth = /* @__PURE__ */ new Map([[root, 0]]);
3303
+ const queue = [root];
3304
+ while (queue.length) {
3305
+ const current = queue.shift();
3306
+ for (const neighbor of relationshipNeighbors(current, edges)) {
3307
+ if (!depth.has(neighbor) && nodes.some((node) => node.id === neighbor)) {
3308
+ depth.set(neighbor, (depth.get(current) ?? 0) + 1);
3309
+ queue.push(neighbor);
3310
+ }
3311
+ }
3312
+ }
3313
+ let disconnectedDepth = Math.max(0, ...depth.values()) + 1;
3314
+ nodes.forEach((node) => {
3315
+ if (!depth.has(node.id)) depth.set(node.id, disconnectedDepth);
3316
+ });
3317
+ const groups = /* @__PURE__ */ new Map();
3318
+ nodes.forEach((node) => {
3319
+ const level = depth.get(node.id);
3320
+ groups.set(level, [...groups.get(level) ?? [], node]);
3321
+ });
3322
+ const maxDepth = Math.max(1, ...groups.keys());
3323
+ return nodes.map((node) => {
3324
+ const level = depth.get(node.id);
3325
+ if (node.x !== void 0 && node.y !== void 0) return { ...node, x: node.x, y: node.y, depth: level };
3326
+ if (level === 0) return { ...node, x: 50, y: 50, depth: 0 };
3327
+ const peers = groups.get(level);
3328
+ const index = peers.findIndex((peer) => peer.id === node.id);
3329
+ const angle = Math.PI * 2 * index / peers.length - Math.PI / 2;
3330
+ const radius = 16 + level / maxDepth * 26;
3331
+ return {
3332
+ ...node,
3333
+ x: Number((50 + Math.cos(angle) * radius).toFixed(3)),
3334
+ y: Number((50 + Math.sin(angle) * radius).toFixed(3)),
3335
+ depth: level
3336
+ };
3337
+ });
3338
+ }
3339
+ function traverseRelationshipNode(nodes, currentId, direction) {
3340
+ if (nodes.length === 0) return void 0;
3341
+ const current = Math.max(0, nodes.findIndex((node) => node.id === currentId));
3342
+ return nodes[(current + direction + nodes.length) % nodes.length]?.id;
3343
+ }
3344
+
3345
+ // src/components/RelationshipGraph.tsx
3346
+ var import_jsx_runtime50 = require("react/jsx-runtime");
3347
+ var EMPTY_IDS = /* @__PURE__ */ new Set();
3348
+ var EMPTY_LEGEND = [];
3349
+ function RelationshipGraph({
3350
+ nodes,
3351
+ edges,
3352
+ selectedId,
3353
+ onSelect,
3354
+ onExpandNeighbors,
3355
+ onOpenDetails,
3356
+ maxNodes = 40,
3357
+ density = "standard",
3358
+ highlightedNodeIds = EMPTY_IDS,
3359
+ conflictedNodeIds = EMPTY_IDS,
3360
+ highlightedEdgeIds = EMPTY_IDS,
3361
+ conflictedEdgeIds = EMPTY_IDS,
3362
+ legend = EMPTY_LEGEND,
3363
+ loading,
3364
+ error,
3365
+ onRetry,
3366
+ className
3367
+ }) {
3368
+ const bounded = (0, import_react26.useMemo)(() => boundRelationshipGraph(nodes, edges, maxNodes), [nodes, edges, maxNodes]);
3369
+ const positioned = (0, import_react26.useMemo)(() => layoutRelationshipGraph(bounded.nodes, bounded.edges, selectedId), [bounded, selectedId]);
3370
+ const positionedById = (0, import_react26.useMemo)(() => new Map(positioned.map((node) => [node.id, node])), [positioned]);
3371
+ const [zoom, setZoom] = (0, import_react26.useState)(1);
3372
+ const [pan, setPan] = (0, import_react26.useState)({ x: 0, y: 0 });
3373
+ const [focusedId, setFocusedId] = (0, import_react26.useState)(selectedId ?? positioned[0]?.id);
3374
+ const svgRef = (0, import_react26.useRef)(null);
3375
+ const select = (id) => {
3376
+ const node = bounded.nodes.find((item) => item.id === id);
3377
+ if (node) onSelect?.(node);
3378
+ };
3379
+ const focusNode = (id) => {
3380
+ if (!id) return;
3381
+ setFocusedId(id);
3382
+ requestAnimationFrame(() => svgRef.current?.querySelector(`[data-node-id="${id}"]`)?.focus());
3383
+ };
3384
+ const onNodeKeyDown = (event, id) => {
3385
+ if (["ArrowRight", "ArrowDown", "ArrowLeft", "ArrowUp"].includes(event.key)) {
3386
+ event.preventDefault();
3387
+ focusNode(traverseRelationshipNode(positioned, id, event.key === "ArrowRight" || event.key === "ArrowDown" ? 1 : -1));
3388
+ } else if (event.key === "Enter" || event.key === " ") {
3389
+ event.preventDefault();
3390
+ select(id);
3391
+ }
3392
+ };
3393
+ const reset = () => {
3394
+ setZoom(1);
3395
+ setPan({ x: 0, y: 0 });
3396
+ };
3397
+ if (loading) return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: cx("uix-relationship-graph uix-relationship-graph--state", className), role: "status", children: "Loading relationships\u2026" });
3398
+ if (error) return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: cx("uix-relationship-graph uix-relationship-graph--state", className), role: "alert", children: [
3399
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("p", { children: error }),
3400
+ onRetry && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary", onClick: onRetry, children: "Try again" })
3401
+ ] });
3402
+ if (bounded.nodes.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: cx("uix-relationship-graph uix-relationship-graph--state", className), children: [
3403
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("p", { children: "No relationships to display." }),
3404
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("p", { children: "Adjust the neighborhood or filters to include a node." })
3405
+ ] });
3406
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("section", { className: cx("uix-relationship-graph", `uix-relationship-graph--${density}`, className), "aria-label": "Relationship graph", children: [
3407
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "uix-relationship-graph__toolbar", "aria-label": "Graph view controls", children: [
3408
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("button", { type: "button", onClick: () => setPan((value) => ({ ...value, x: value.x - 24 })), "aria-label": "Pan left", children: "Left" }),
3409
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("button", { type: "button", onClick: () => setPan((value) => ({ ...value, y: value.y - 24 })), "aria-label": "Pan up", children: "Up" }),
3410
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("button", { type: "button", onClick: () => setPan((value) => ({ ...value, y: value.y + 24 })), "aria-label": "Pan down", children: "Down" }),
3411
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("button", { type: "button", onClick: () => setPan((value) => ({ ...value, x: value.x + 24 })), "aria-label": "Pan right", children: "Right" }),
3412
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("button", { type: "button", onClick: () => setZoom((value) => Math.min(2, Number((value + 0.2).toFixed(1)))), "aria-label": "Zoom in", children: "Zoom in" }),
3413
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("button", { type: "button", onClick: () => setZoom((value) => Math.max(0.5, Number((value - 0.2).toFixed(1)))), "aria-label": "Zoom out", children: "Zoom out" }),
3414
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("button", { type: "button", onClick: reset, children: "Reset view" }),
3415
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("span", { "aria-live": "polite", children: [
3416
+ "Zoom ",
3417
+ Math.round(zoom * 100),
3418
+ "%"
3419
+ ] })
3420
+ ] }),
3421
+ bounded.omittedNodeCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("p", { className: "uix-relationship-graph__bounded", role: "status", children: [
3422
+ "Showing ",
3423
+ bounded.nodes.length,
3424
+ " nodes; ",
3425
+ bounded.omittedNodeCount,
3426
+ " outside this bounded neighborhood."
3427
+ ] }),
3428
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: "uix-relationship-graph__visual", children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("svg", { ref: svgRef, viewBox: "0 0 600 400", role: "group", "aria-label": `Graph with ${bounded.nodes.length} nodes and ${bounded.edges.length} relationships`, children: /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("g", { transform: `translate(${pan.x} ${pan.y}) scale(${zoom})`, children: [
3429
+ bounded.edges.map((edge) => {
3430
+ const source = positionedById.get(edge.source);
3431
+ const target = positionedById.get(edge.target);
3432
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("line", { x1: source.x * 6, y1: source.y * 4, x2: target.x * 6, y2: target.y * 4, className: "uix-relationship-graph__edge", "data-highlighted": highlightedEdgeIds.has(edge.id) || void 0, "data-conflicted": conflictedEdgeIds.has(edge.id) || void 0, children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("title", { children: edge.label ?? edge.type ?? "Relationship" }) }, edge.id);
3433
+ }),
3434
+ positioned.map((node) => /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
3435
+ "g",
3436
+ {
3437
+ transform: `translate(${node.x * 6} ${node.y * 4})`,
3438
+ role: "button",
3439
+ "aria-label": `${node.label}${node.type ? `, ${node.type}` : ""}${conflictedNodeIds.has(node.id) ? ", conflict" : ""}`,
3440
+ tabIndex: focusedId === node.id ? 0 : -1,
3441
+ "data-node-id": node.id,
3442
+ className: "uix-relationship-graph__node",
3443
+ "data-selected": selectedId === node.id || void 0,
3444
+ "data-highlighted": highlightedNodeIds.has(node.id) || void 0,
3445
+ "data-conflicted": conflictedNodeIds.has(node.id) || void 0,
3446
+ onFocus: () => setFocusedId(node.id),
3447
+ onKeyDown: (event) => onNodeKeyDown(event, node.id),
3448
+ onClick: () => select(node.id),
3449
+ children: [
3450
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("rect", { x: "-55", y: "-18", width: "110", height: "36", rx: "8" }),
3451
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("text", { textAnchor: "middle", dominantBaseline: "middle", children: node.label.length > 16 ? `${node.label.slice(0, 15)}\u2026` : node.label })
3452
+ ]
3453
+ },
3454
+ node.id
3455
+ ))
3456
+ ] }) }) }),
3457
+ legend.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("ul", { className: "uix-relationship-graph__legend", "aria-label": "Relationship types", children: legend.map((item) => /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("li", { "data-type": item.id, children: item.label }, item.id)) }),
3458
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "uix-relationship-graph__list", role: "region", "aria-label": "Accessible relationship list", children: [
3459
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("h3", { children: "Relationship list" }),
3460
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("ul", { children: bounded.nodes.map((node) => {
3461
+ const neighbors = relationshipNeighbors(node.id, bounded.edges).map((id) => bounded.nodes.find((item) => item.id === id)?.label ?? id);
3462
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("li", { "data-selected": selectedId === node.id || void 0, children: [
3463
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("button", { type: "button", onClick: () => select(node.id), "aria-pressed": selectedId === node.id, children: [
3464
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("strong", { children: node.label }),
3465
+ node.type && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { children: node.type }),
3466
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { children: neighbors.length ? `Connected to ${neighbors.join(", ")}` : "No visible connections" })
3467
+ ] }),
3468
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("span", { className: "uix-relationship-graph__list-actions", children: [
3469
+ onExpandNeighbors && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("button", { type: "button", onClick: () => onExpandNeighbors(node), children: "Expand neighbors" }),
3470
+ onOpenDetails && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("button", { type: "button", onClick: () => onOpenDetails(node), children: "Open details" })
3471
+ ] })
3472
+ ] }, node.id);
3473
+ }) })
3474
+ ] })
3475
+ ] });
3476
+ }
3477
+
3478
+ // src/components/MatchReview.tsx
3479
+ var import_react27 = require("react");
3480
+ var import_jsx_runtime51 = require("react/jsx-runtime");
3481
+ var valueText = (value) => value == null ? "\u2014" : typeof value === "string" ? value : JSON.stringify(value);
3482
+ function MatchReview({
3483
+ incoming,
3484
+ incomingLabel = "Incoming record",
3485
+ fields,
3486
+ candidates,
3487
+ selectedIds,
3488
+ onSelectionChange,
3489
+ onDecision,
3490
+ onBulkDecision,
3491
+ loading,
3492
+ error,
3493
+ onRetry,
3494
+ className
3495
+ }) {
3496
+ const [internalSelection, setInternalSelection] = (0, import_react27.useState)(/* @__PURE__ */ new Set());
3497
+ const [announcement, setAnnouncement] = (0, import_react27.useState)("");
3498
+ const [bulkPending, setBulkPending] = (0, import_react27.useState)(false);
3499
+ const selection = selectedIds ?? internalSelection;
3500
+ const pending = (0, import_react27.useMemo)(() => candidates.filter((candidate) => candidate.status !== "resolved"), [candidates]);
3501
+ const setSelection = (next) => {
3502
+ if (selectedIds === void 0) setInternalSelection(next);
3503
+ onSelectionChange?.(next);
3504
+ };
3505
+ const toggle = (id) => {
3506
+ const next = new Set(selection);
3507
+ if (next.has(id)) next.delete(id);
3508
+ else next.add(id);
3509
+ setSelection(next);
3510
+ };
3511
+ const runBulk = async (action) => {
3512
+ if (!onBulkDecision || selection.size === 0 || bulkPending) return;
3513
+ const ids = [...selection];
3514
+ setBulkPending(true);
3515
+ setAnnouncement(`${action === "accept" ? "Accepting" : "Dismissing"} ${ids.length} candidate${ids.length === 1 ? "" : "s"}\u2026`);
3516
+ try {
3517
+ const result = await onBulkDecision(ids, action);
3518
+ const succeeded = result?.succeeded ?? ids.length;
3519
+ const failed = result?.failed ?? 0;
3520
+ setAnnouncement(`${succeeded} candidate${succeeded === 1 ? "" : "s"} ${action === "accept" ? "accepted" : "dismissed"}${failed ? `; ${failed} failed` : ""}.`);
3521
+ if (!failed) setSelection(/* @__PURE__ */ new Set());
3522
+ } catch {
3523
+ setAnnouncement(`Bulk ${action} failed. Try again.`);
3524
+ } finally {
3525
+ setBulkPending(false);
3526
+ }
3527
+ };
3528
+ if (loading) return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { className: cx("uix-match-review uix-match-review--state", className), role: "status", children: "Loading match candidates\u2026" });
3529
+ if (error) return /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: cx("uix-match-review uix-match-review--state", className), role: "alert", children: [
3530
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("p", { children: error }),
3531
+ onRetry && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary", onClick: onRetry, children: "Try again" })
3532
+ ] });
3533
+ if (candidates.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: cx("uix-match-review uix-match-review--state", className), children: [
3534
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("p", { children: "No candidate matches found." }),
3535
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("p", { children: "Choose another record source or continue without a match." })
3536
+ ] });
3537
+ return /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("section", { className: cx("uix-match-review", className), "aria-label": "Match review", children: [
3538
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: "uix-match-review__summary", children: [
3539
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { children: [
3540
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("strong", { children: incomingLabel }),
3541
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("span", { children: [
3542
+ pending.length,
3543
+ " pending of ",
3544
+ candidates.length
3545
+ ] })
3546
+ ] }),
3547
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { className: "uix-match-review__incoming", children: fields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("span", { children: [
3548
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("b", { children: field.label }),
3549
+ field.render?.(incoming[field.id], incoming) ?? valueText(incoming[field.id])
3550
+ ] }, field.id)) })
3551
+ ] }),
3552
+ onBulkDecision && /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: "uix-match-review__bulk", "aria-label": "Bulk candidate actions", children: [
3553
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("span", { children: [
3554
+ selection.size,
3555
+ " selected"
3556
+ ] }),
3557
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", disabled: selection.size === 0 || bulkPending, onClick: () => void runBulk("accept"), children: bulkPending ? "Applying\u2026" : "Accept selected" }),
3558
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", disabled: selection.size === 0 || bulkPending, onClick: () => void runBulk("dismiss"), children: bulkPending ? "Applying\u2026" : "Dismiss selected" })
3559
+ ] }),
3560
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { className: "uix-match-review__table-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("table", { className: "uix-table uix-match-review__table", children: [
3561
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("caption", { className: "uix-visually-hidden", children: "Candidate matches and available decisions" }),
3562
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("tr", { children: [
3563
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("th", { scope: "col", children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("span", { className: "uix-visually-hidden", children: "Select" }) }),
3564
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("th", { scope: "col", children: "Confidence" }),
3565
+ fields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("th", { scope: "col", children: field.label }, field.id)),
3566
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("th", { scope: "col", children: "Status" }),
3567
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("th", { scope: "col", children: "Actions" })
3568
+ ] }) }),
3569
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("tbody", { children: candidates.map((candidate) => {
3570
+ const resolved = candidate.status === "resolved";
3571
+ return /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("tr", { "data-status": candidate.status ?? "pending", children: [
3572
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("input", { type: "checkbox", checked: selection.has(candidate.id), onChange: () => toggle(candidate.id), "aria-label": `Select candidate ${candidate.id}` }) }),
3573
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("span", { className: "uix-match-review__confidence", children: candidate.confidence == null ? "Not scored" : `${Math.round(candidate.confidence * 100)}%` }) }),
3574
+ fields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("td", { children: field.render?.(candidate.record[field.id], candidate.record) ?? valueText(candidate.record[field.id]) }, field.id)),
3575
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(StatusPill, { tone: resolved ? "success" : "warning", children: resolved ? candidate.decision ?? "Resolved" : "Pending" }) }),
3576
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { className: "uix-match-review__actions", children: ["accept", "dismiss", "split", "pick-other"].map((action) => /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3577
+ "button",
3578
+ {
3579
+ type: "button",
3580
+ className: "uix-btn uix-btn--ghost uix-btn--sm",
3581
+ disabled: resolved,
3582
+ onClick: () => onDecision(candidate.id, action),
3583
+ children: action === "pick-other" ? "Pick other" : `${action[0].toUpperCase()}${action.slice(1)}`
3584
+ },
3585
+ action
3586
+ )) }) })
3587
+ ] }, candidate.id);
3588
+ }) })
3589
+ ] }) }),
3590
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("p", { className: "uix-visually-hidden", "aria-live": "polite", children: announcement })
3591
+ ] });
3592
+ }
3593
+
3594
+ // src/components/MetricInput.tsx
3595
+ var import_react28 = require("react");
3596
+
3597
+ // src/metric-model.ts
3598
+ function decimals(value) {
3599
+ const text = String(value).toLowerCase();
3600
+ if (text.includes("e-")) return Number(text.split("e-")[1]);
3601
+ return text.includes(".") ? text.split(".")[1].length : 0;
3602
+ }
3603
+ function clampMetricValue(value, min, max) {
3604
+ return Math.min(max ?? Number.POSITIVE_INFINITY, Math.max(min ?? Number.NEGATIVE_INFINITY, value));
3605
+ }
3606
+ function stepMetricValue(value, direction, options = {}) {
3607
+ const step = options.step && options.step > 0 ? options.step : 1;
3608
+ const base = value ?? options.min ?? 0;
3609
+ const precision = Math.max(decimals(base), decimals(step), decimals(options.min ?? 0), decimals(options.max ?? 0));
3610
+ const next = Number((base + direction * step).toFixed(precision));
3611
+ return clampMetricValue(next, options.min, options.max);
3612
+ }
3613
+ function parseMetricValue(value) {
3614
+ if (value.trim() === "") return null;
3615
+ const parsed = Number(value);
3616
+ return Number.isFinite(parsed) ? parsed : null;
3617
+ }
3618
+
3619
+ // src/components/MetricInput.tsx
3620
+ var import_jsx_runtime52 = require("react/jsx-runtime");
3621
+ var defaultMetricFormat = (input) => input == null ? "" : String(input);
3622
+ function MetricInput({
3623
+ value,
3624
+ onValueChange,
3625
+ min,
3626
+ max,
3627
+ step = 1,
3628
+ unit,
3629
+ label,
3630
+ invalid,
3631
+ errorMessage,
3632
+ disabled,
3633
+ readOnly,
3634
+ parse = parseMetricValue,
3635
+ format = defaultMetricFormat,
3636
+ className,
3637
+ id: providedId
3638
+ }) {
3639
+ const generatedId = (0, import_react28.useId)();
3640
+ const id = providedId ?? generatedId;
3641
+ const errorId = `${id}-error`;
3642
+ const [text, setText] = (0, import_react28.useState)(() => format(value));
3643
+ (0, import_react28.useEffect)(() => setText(format(value)), [value, format]);
3644
+ const commit = (input) => {
3645
+ const parsed = parse(input);
3646
+ if (input.trim() === "" || parsed !== null) {
3647
+ onValueChange(parsed === null ? null : clampMetricValue(parsed, min, max));
3648
+ }
3649
+ };
3650
+ const stepBy = (direction) => {
3651
+ const next = stepMetricValue(value, direction, { min, max, step });
3652
+ onValueChange(next);
3653
+ setText(format(next));
3654
+ };
3655
+ const onKeyDown = (event) => {
3656
+ if (event.key === "ArrowUp" || event.key === "ArrowDown") {
3657
+ event.preventDefault();
3658
+ stepBy(event.key === "ArrowUp" ? 1 : -1);
3659
+ }
3660
+ };
3661
+ const onChange = (event) => {
3662
+ setText(event.currentTarget.value);
3663
+ commit(event.currentTarget.value);
3664
+ };
3665
+ const onBlur = (event) => {
3666
+ const parsed = parse(event.currentTarget.value);
3667
+ setText(parsed === null && event.currentTarget.value.trim() !== "" ? format(value) : format(parsed));
3668
+ };
3669
+ return /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: cx("uix-metric-input", invalid && "uix-metric-input--invalid", className), children: [
3670
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("label", { className: "uix-field__label", htmlFor: id, children: label }),
3671
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "uix-metric-input__control", children: [
3672
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3673
+ Input,
3674
+ {
3675
+ id,
3676
+ value: text,
3677
+ onChange,
3678
+ onBlur,
3679
+ onKeyDown,
3680
+ inputMode: "decimal",
3681
+ role: "spinbutton",
3682
+ "aria-valuenow": value ?? void 0,
3683
+ "aria-valuemin": min,
3684
+ "aria-valuemax": max,
3685
+ "aria-describedby": invalid && errorMessage ? errorId : void 0,
3686
+ invalid,
3687
+ disabled,
3688
+ readOnly
3689
+ }
3690
+ ),
3691
+ unit && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { className: "uix-metric-input__unit", "aria-hidden": "true", children: unit }),
3692
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("span", { className: "uix-metric-input__steps", children: [
3693
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("button", { type: "button", className: "uix-metric-input__step", onClick: () => stepBy(1), disabled: disabled || readOnly, "aria-label": `Increase ${label}`, children: "+" }),
3694
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("button", { type: "button", className: "uix-metric-input__step", onClick: () => stepBy(-1), disabled: disabled || readOnly, "aria-label": `Decrease ${label}`, children: "\u2212" })
3695
+ ] })
3696
+ ] }),
3697
+ unit && /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("span", { className: "uix-visually-hidden", children: [
3698
+ "Unit: ",
3699
+ unit
3700
+ ] }),
3701
+ invalid && errorMessage && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { id: errorId, className: "uix-field__error", children: errorMessage })
3702
+ ] });
3703
+ }
3704
+
3705
+ // src/components/LicensePositionBar.tsx
3706
+ var import_jsx_runtime53 = require("react/jsx-runtime");
3707
+ var defaultCapacityFormat = (value) => new Intl.NumberFormat().format(value);
3708
+ function LicensePositionBar({
3709
+ consumed,
3710
+ entitled,
3711
+ consumedLabel = "Consumed",
3712
+ entitledLabel = "Entitled",
3713
+ availableLabel = "available",
3714
+ overLabel = "over limit",
3715
+ unit = "",
3716
+ formatValue = defaultCapacityFormat,
3717
+ className,
3718
+ ...props
3719
+ }) {
3720
+ const safeConsumed = Math.max(0, consumed);
3721
+ const safeEntitled = Math.max(0, entitled);
3722
+ const over = Math.max(0, safeConsumed - safeEntitled);
3723
+ const available = Math.max(0, safeEntitled - safeConsumed);
3724
+ const percent = safeEntitled === 0 ? safeConsumed > 0 ? 100 : 0 : safeConsumed / safeEntitled * 100;
3725
+ const format = (value) => `${formatValue(value)}${unit ? ` ${unit}` : ""}`;
3726
+ const summary = `${consumedLabel}: ${format(safeConsumed)}. ${entitledLabel}: ${format(safeEntitled)}. ${over > 0 ? `${format(over)} ${overLabel}` : `${format(available)} ${availableLabel}`}.`;
3727
+ return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: cx("uix-license-position", over > 0 && "uix-license-position--over", className), ...props, children: [
3728
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "uix-license-position__header", children: [
3729
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("span", { className: "uix-license-position__value", children: [
3730
+ format(safeConsumed),
3731
+ " / ",
3732
+ format(safeEntitled)
3733
+ ] }),
3734
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(StatusPill, { tone: over > 0 ? "danger" : percent >= 80 ? "warning" : "success", children: over > 0 ? `${format(over)} ${overLabel}` : `${format(available)} ${availableLabel}` })
3735
+ ] }),
3736
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "uix-license-position__track", children: [
3737
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Meter, { value: Math.min(100, percent), tone: over > 0 ? "danger" : percent >= 80 ? "warning" : "success", "aria-label": summary, "aria-valuetext": summary }),
3738
+ over > 0 && /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3739
+ "span",
3740
+ {
3741
+ className: "uix-license-position__overflow",
3742
+ style: { width: `${Math.min(100, safeEntitled === 0 ? 100 : over / safeEntitled * 100)}%` },
3743
+ "aria-hidden": "true"
3744
+ }
3745
+ )
3746
+ ] }),
3747
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "uix-visually-hidden", children: summary })
3748
+ ] });
3749
+ }
3750
+
3751
+ // src/components/BrandProfiles.tsx
3752
+ var import_react30 = require("react");
3753
+
3754
+ // src/color-model.ts
3755
+ var clamp2 = (value, min, max) => Math.min(max, Math.max(min, value));
3756
+ function normalizeHex(value) {
3757
+ const raw = value.trim().replace(/^#/, "");
3758
+ if (/^[\da-f]{3}$/i.test(raw)) return `#${raw.split("").map((char) => char + char).join("").toUpperCase()}`;
3759
+ if (/^[\da-f]{6}$/i.test(raw)) return `#${raw.toUpperCase()}`;
3760
+ return void 0;
3761
+ }
3762
+ function hexToRgb(value) {
3763
+ const hex = normalizeHex(value);
3764
+ if (!hex) return void 0;
3765
+ return {
3766
+ r: Number.parseInt(hex.slice(1, 3), 16),
3767
+ g: Number.parseInt(hex.slice(3, 5), 16),
3768
+ b: Number.parseInt(hex.slice(5, 7), 16)
3769
+ };
3770
+ }
3771
+ function rgbToHex({ r, g, b }) {
3772
+ return `#${[r, g, b].map((value) => Math.round(clamp2(value, 0, 255)).toString(16).padStart(2, "0")).join("").toUpperCase()}`;
3773
+ }
3774
+ function rgbToHsv({ r, g, b }) {
3775
+ const values = [r, g, b].map((value) => clamp2(value, 0, 255) / 255);
3776
+ const max = Math.max(...values);
3777
+ const min = Math.min(...values);
3778
+ const delta = max - min;
3779
+ let h = 0;
3780
+ if (delta !== 0) {
3781
+ if (max === values[0]) h = 60 * ((values[1] - values[2]) / delta % 6);
3782
+ else if (max === values[1]) h = 60 * ((values[2] - values[0]) / delta + 2);
3783
+ else h = 60 * ((values[0] - values[1]) / delta + 4);
3784
+ }
3785
+ if (h < 0) h += 360;
3786
+ return { h, s: max === 0 ? 0 : delta / max, v: max };
3787
+ }
3788
+ function hsvToRgb({ h, s, v }) {
3789
+ const hue = (h % 360 + 360) % 360;
3790
+ const sat = clamp2(s, 0, 1);
3791
+ const value = clamp2(v, 0, 1);
3792
+ const chroma = value * sat;
3793
+ const x = chroma * (1 - Math.abs(hue / 60 % 2 - 1));
3794
+ const m = value - chroma;
3795
+ const sector = Math.floor(hue / 60);
3796
+ const rgb = sector === 0 ? [chroma, x, 0] : sector === 1 ? [x, chroma, 0] : sector === 2 ? [0, chroma, x] : sector === 3 ? [0, x, chroma] : sector === 4 ? [x, 0, chroma] : [chroma, 0, x];
3797
+ return { r: (rgb[0] + m) * 255, g: (rgb[1] + m) * 255, b: (rgb[2] + m) * 255 };
3798
+ }
3799
+ function hsvToHex(value) {
3800
+ return rgbToHex(hsvToRgb(value));
3801
+ }
3802
+ function relativeLuminance({ r, g, b }) {
3803
+ const linear = [r, g, b].map((channel) => {
3804
+ const value = channel / 255;
3805
+ return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
3806
+ });
3807
+ return 0.2126 * linear[0] + 0.7152 * linear[1] + 0.0722 * linear[2];
3808
+ }
3809
+ function contrastRatio(background, foreground) {
3810
+ const bg = hexToRgb(background);
3811
+ const fg = hexToRgb(foreground);
3812
+ if (!bg || !fg) return void 0;
3813
+ const [light, dark] = [relativeLuminance(bg), relativeLuminance(fg)].sort((a, b) => b - a);
3814
+ return (light + 0.05) / (dark + 0.05);
3815
+ }
3816
+ function meetsContrast(background, foreground, minimum = 4.5) {
3817
+ return (contrastRatio(background, foreground) ?? 0) >= minimum;
3818
+ }
3819
+
3820
+ // src/components/ColorPicker.tsx
3821
+ var import_react29 = require("react");
3822
+ var import_jsx_runtime54 = require("react/jsx-runtime");
3823
+ var EMPTY_COLORS = [];
3824
+ function ColorPicker({
3825
+ value,
3826
+ onChange,
3827
+ foreground,
3828
+ presets = EMPTY_COLORS,
3829
+ recent = EMPTY_COLORS,
3830
+ minimumContrast = 4.5,
3831
+ label = "Choose color",
3832
+ disabled,
3833
+ className
3834
+ }) {
3835
+ const id = (0, import_react29.useId)();
3836
+ const triggerRef = (0, import_react29.useRef)(null);
3837
+ const dialogRef = (0, import_react29.useRef)(null);
3838
+ const [open, setOpen] = (0, import_react29.useState)(false);
3839
+ const normalized = normalizeHex(value) ?? "#000000";
3840
+ const [hexText, setHexText] = (0, import_react29.useState)(normalized);
3841
+ const hsv = (0, import_react29.useMemo)(() => rgbToHsv(hexToRgb(normalized)), [normalized]);
3842
+ const ratio = contrastRatio(normalized, foreground) ?? 0;
3843
+ const passes = ratio >= minimumContrast;
3844
+ (0, import_react29.useEffect)(() => setHexText(normalized), [normalized]);
3845
+ (0, import_react29.useEffect)(() => {
3846
+ if (!open) return;
3847
+ const dialog = dialogRef.current;
3848
+ dialog?.querySelector("input,button")?.focus();
3849
+ const onPointer = (event) => {
3850
+ if (!dialog?.contains(event.target) && !triggerRef.current?.contains(event.target)) setOpen(false);
3851
+ };
3852
+ document.addEventListener("mousedown", onPointer);
3853
+ return () => document.removeEventListener("mousedown", onPointer);
3854
+ }, [open]);
3855
+ const close = () => {
3856
+ setOpen(false);
3857
+ requestAnimationFrame(() => triggerRef.current?.focus());
3858
+ };
3859
+ const onDialogKeyDown = (event) => {
3860
+ if (event.key === "Escape") {
3861
+ event.preventDefault();
3862
+ close();
3863
+ return;
3864
+ }
3865
+ if (event.key !== "Tab") return;
3866
+ const focusable = Array.from(dialogRef.current?.querySelectorAll('button:not([disabled]),input:not([disabled]),[tabindex="0"]') ?? []);
3867
+ if (!focusable.length) return;
3868
+ const first = focusable[0];
3869
+ const last = focusable[focusable.length - 1];
3870
+ if (event.shiftKey && document.activeElement === first) {
3871
+ event.preventDefault();
3872
+ last.focus();
3873
+ } else if (!event.shiftKey && document.activeElement === last) {
3874
+ event.preventDefault();
3875
+ first.focus();
3876
+ }
3877
+ };
3878
+ const updateHsv = (part) => onChange(hsvToHex({ ...hsv, ...part }));
3879
+ const commitHex = () => {
3880
+ const next = normalizeHex(hexText);
3881
+ if (next) onChange(next);
3882
+ };
3883
+ const choose = (candidate) => {
3884
+ const next = normalizeHex(candidate);
3885
+ if (next) onChange(next);
3886
+ };
3887
+ const sampleStyle = { backgroundColor: normalized };
3888
+ return /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: cx("uix-color-picker", className), children: [
3889
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(
3890
+ "button",
3891
+ {
3892
+ ref: triggerRef,
3893
+ type: "button",
3894
+ className: "uix-color-picker__trigger",
3895
+ "aria-label": `${label}: ${normalized}`,
3896
+ "aria-expanded": open,
3897
+ "aria-controls": `${id}-dialog`,
3898
+ onClick: () => setOpen((current) => !current),
3899
+ disabled,
3900
+ children: [
3901
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { className: "uix-color-picker__swatch", style: sampleStyle, "aria-hidden": "true" }),
3902
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { children: normalized })
3903
+ ]
3904
+ }
3905
+ ),
3906
+ open && /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(
3907
+ "div",
3908
+ {
3909
+ ref: dialogRef,
3910
+ id: `${id}-dialog`,
3911
+ role: "dialog",
3912
+ "aria-modal": "true",
3913
+ "aria-label": label,
3914
+ className: "uix-color-picker__popover",
3915
+ onKeyDown: onDialogKeyDown,
3916
+ children: [
3917
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("div", { className: "uix-color-picker__preview", style: sampleStyle, children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { style: { color: foreground }, children: "Aa" }) }),
3918
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("label", { className: "uix-color-picker__range", children: [
3919
+ "Hue ",
3920
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("span", { children: [
3921
+ Math.round(hsv.h),
3922
+ "\xB0"
3923
+ ] }),
3924
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("input", { type: "range", min: "0", max: "359", value: Math.round(hsv.h), onChange: (event) => updateHsv({ h: Number(event.currentTarget.value) }) })
3925
+ ] }),
3926
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("label", { className: "uix-color-picker__range", children: [
3927
+ "Saturation ",
3928
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("span", { children: [
3929
+ Math.round(hsv.s * 100),
3930
+ "%"
3931
+ ] }),
3932
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("input", { type: "range", min: "0", max: "100", value: Math.round(hsv.s * 100), onChange: (event) => updateHsv({ s: Number(event.currentTarget.value) / 100 }) })
3933
+ ] }),
3934
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("label", { className: "uix-color-picker__range", children: [
3935
+ "Brightness ",
3936
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("span", { children: [
3937
+ Math.round(hsv.v * 100),
3938
+ "%"
3939
+ ] }),
3940
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("input", { type: "range", min: "0", max: "100", value: Math.round(hsv.v * 100), onChange: (event) => updateHsv({ v: Number(event.currentTarget.value) / 100 }) })
3941
+ ] }),
3942
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("label", { className: "uix-color-picker__hex", children: [
3943
+ "Hex",
3944
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("span", { className: "uix-color-picker__hex-row", children: [
3945
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("input", { className: "uix-input", value: hexText, onChange: (event) => setHexText(event.currentTarget.value), onBlur: commitHex, onKeyDown: (event) => {
3946
+ if (event.key === "Enter") commitHex();
3947
+ }, "aria-invalid": !normalizeHex(hexText) }),
3948
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", onClick: commitHex, disabled: !normalizeHex(hexText), children: "Set" })
3949
+ ] })
3950
+ ] }),
3951
+ (presets.length > 0 || recent.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "uix-color-picker__palettes", children: [
3952
+ presets.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("fieldset", { children: [
3953
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("legend", { children: "Presets" }),
3954
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("div", { children: presets.map((color) => /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("button", { type: "button", className: "uix-color-picker__preset", style: { backgroundColor: normalizeHex(color) ?? color }, onClick: () => choose(color), "aria-label": `Use preset ${color}` }, color)) })
3955
+ ] }),
3956
+ recent.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("fieldset", { children: [
3957
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("legend", { children: "Recent" }),
3958
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("div", { children: recent.map((color) => /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("button", { type: "button", className: "uix-color-picker__preset", style: { backgroundColor: normalizeHex(color) ?? color }, onClick: () => choose(color), "aria-label": `Use recent color ${color}` }, color)) })
3959
+ ] })
3960
+ ] }),
3961
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("p", { className: cx("uix-color-picker__contrast", passes ? "uix-color-picker__contrast--pass" : "uix-color-picker__contrast--fail"), role: "status", "aria-live": "polite", children: [
3962
+ "Contrast ",
3963
+ ratio.toFixed(2),
3964
+ ":1 \u2014 ",
3965
+ passes ? `passes WCAG AA (${minimumContrast}:1)` : `does not meet WCAG AA (${minimumContrast}:1)`
3966
+ ] }),
3967
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("button", { type: "button", className: "uix-btn uix-btn--primary uix-btn--sm", onClick: close, children: "Done" })
3968
+ ]
3969
+ }
3970
+ )
3971
+ ] });
3972
+ }
3973
+
3974
+ // src/components/BrandProfiles.tsx
3975
+ var import_jsx_runtime55 = require("react/jsx-runtime");
3976
+ var PROFILE_PROPERTIES = {
3977
+ brand: "--uix-brand",
3978
+ brandForeground: "--uix-brand-fg",
3979
+ sans: "--uix-font-sans",
3980
+ heading: "--uix-font-heading",
3981
+ mono: "--uix-font-mono"
3982
+ };
3983
+ var EMPTY_PROFILES = [];
3984
+ var EMPTY_COLORS2 = [];
3985
+ function applyBrandProfile(profile, target) {
3986
+ const element = target ?? (typeof document === "undefined" ? void 0 : document.documentElement);
3987
+ if (!element) return void 0;
3988
+ const brand = normalizeHex(profile.brand);
3989
+ const foreground = normalizeHex(profile.brandForeground);
3990
+ if (!brand || !foreground) throw new TypeError("Brand profiles require valid hex brand and foreground colors.");
3991
+ const properties = {};
3992
+ Object.values(PROFILE_PROPERTIES).forEach((property) => {
3993
+ properties[property] = element.style.getPropertyValue(property);
3994
+ });
3995
+ const snapshot = {
3996
+ profileId: element.dataset.uixBrandProfile,
3997
+ properties,
3998
+ logoSrc: element.dataset.uixBrandLogoSrc,
3999
+ logoAlt: element.dataset.uixBrandLogoAlt
4000
+ };
4001
+ element.style.setProperty(PROFILE_PROPERTIES.brand, brand);
4002
+ element.style.setProperty(PROFILE_PROPERTIES.brandForeground, foreground);
4003
+ const typography = profile.typography ?? {};
4004
+ for (const [key, property] of Object.entries({ sans: PROFILE_PROPERTIES.sans, heading: PROFILE_PROPERTIES.heading, mono: PROFILE_PROPERTIES.mono })) {
4005
+ const value = typography[key];
4006
+ if (value) element.style.setProperty(property, value);
4007
+ else element.style.removeProperty(property);
4008
+ }
4009
+ element.dataset.uixBrandProfile = profile.id;
4010
+ if (profile.logo) {
4011
+ element.dataset.uixBrandLogoSrc = profile.logo.src;
4012
+ element.dataset.uixBrandLogoAlt = profile.logo.alt;
4013
+ } else {
4014
+ delete element.dataset.uixBrandLogoSrc;
4015
+ delete element.dataset.uixBrandLogoAlt;
4016
+ }
4017
+ return snapshot;
4018
+ }
4019
+ function restoreBrandProfile(snapshot, target) {
4020
+ const element = target ?? (typeof document === "undefined" ? void 0 : document.documentElement);
4021
+ if (!element) return;
4022
+ Object.entries(snapshot.properties).forEach(([property, value]) => {
4023
+ if (value) element.style.setProperty(property, value);
4024
+ else element.style.removeProperty(property);
4025
+ });
4026
+ if (snapshot.profileId) element.dataset.uixBrandProfile = snapshot.profileId;
4027
+ else delete element.dataset.uixBrandProfile;
4028
+ if (snapshot.logoSrc) element.dataset.uixBrandLogoSrc = snapshot.logoSrc;
4029
+ else delete element.dataset.uixBrandLogoSrc;
4030
+ if (snapshot.logoAlt) element.dataset.uixBrandLogoAlt = snapshot.logoAlt;
4031
+ else delete element.dataset.uixBrandLogoAlt;
4032
+ }
4033
+ function BrandProfileEditor({
4034
+ value,
4035
+ onChange,
4036
+ profiles = EMPTY_PROFILES,
4037
+ onSelectProfile,
4038
+ onApply,
4039
+ applyTarget,
4040
+ onLogoFile,
4041
+ colorPresets = EMPTY_COLORS2,
4042
+ className
4043
+ }) {
4044
+ const [announcement, setAnnouncement] = (0, import_react30.useState)("");
4045
+ const update = (patch) => onChange({ ...value, ...patch });
4046
+ const apply = () => {
4047
+ if (applyTarget !== null) applyBrandProfile(value, applyTarget);
4048
+ onApply?.(value);
4049
+ setAnnouncement(`${value.name} applied.`);
4050
+ };
4051
+ const onFile = (event) => {
4052
+ const file = event.currentTarget.files?.[0];
4053
+ if (file) onLogoFile?.(file);
4054
+ };
4055
+ const previewStyle = {
4056
+ "--uix-brand": normalizeHex(value.brand) ?? value.brand,
4057
+ "--uix-brand-fg": normalizeHex(value.brandForeground) ?? value.brandForeground,
4058
+ ...value.typography?.sans ? { "--uix-font-sans": value.typography.sans } : {},
4059
+ ...value.typography?.heading ? { "--uix-font-heading": value.typography.heading } : {}
4060
+ };
4061
+ return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("section", { className: cx("uix-brand-profiles", className), "aria-label": "Brand profile editor", children: [
4062
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "uix-brand-profiles__form", children: [
4063
+ profiles.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("label", { className: "uix-field__label", children: [
4064
+ "Profile",
4065
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("select", { className: "uix-select", value: value.id, onChange: (event) => {
4066
+ const selected = profiles.find((profile) => profile.id === event.currentTarget.value);
4067
+ if (selected) onSelectProfile?.(selected);
4068
+ }, children: profiles.map((profile) => /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("option", { value: profile.id, children: profile.name }, profile.id)) })
4069
+ ] }),
4070
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("label", { className: "uix-field__label", children: [
4071
+ "Name",
4072
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("input", { className: "uix-input", value: value.name, onChange: (event) => update({ name: event.currentTarget.value }) })
4073
+ ] }),
4074
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "uix-brand-profiles__colors", children: [
4075
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("label", { children: [
4076
+ "Brand",
4077
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(ColorPicker, { label: "Brand color", value: value.brand, foreground: value.brandForeground, presets: colorPresets, onChange: (brand) => update({ brand }) })
4078
+ ] }),
4079
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("label", { children: [
4080
+ "On brand",
4081
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(ColorPicker, { label: "Brand foreground color", value: value.brandForeground, foreground: value.brand, presets: colorPresets, onChange: (brandForeground) => update({ brandForeground }) })
4082
+ ] })
4083
+ ] }),
4084
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("label", { className: "uix-field__label", children: [
4085
+ "Body font",
4086
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("input", { className: "uix-input", value: value.typography?.sans ?? "", onChange: (event) => update({ typography: { ...value.typography, sans: event.currentTarget.value || void 0 } }) })
4087
+ ] }),
4088
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("label", { className: "uix-field__label", children: [
4089
+ "Heading font",
4090
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("input", { className: "uix-input", value: value.typography?.heading ?? "", onChange: (event) => update({ typography: { ...value.typography, heading: event.currentTarget.value || void 0 } }) })
4091
+ ] }),
4092
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("label", { className: "uix-file-upload uix-brand-profiles__upload", children: [
4093
+ "Logo file",
4094
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("input", { type: "file", accept: "image/*", onChange: onFile })
4095
+ ] }),
4096
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("button", { type: "button", className: "uix-btn uix-btn--primary", onClick: apply, children: "Apply profile" }),
4097
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "uix-visually-hidden", "aria-live": "polite", children: announcement })
4098
+ ] }),
4099
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "uix-brand-profiles__preview", style: previewStyle, "aria-label": `Live preview of ${value.name}`, children: [
4100
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "uix-brand-profiles__preview-header", children: [
4101
+ value.logo ? /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("img", { src: value.logo.src, alt: value.logo.alt }) : /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "uix-brand-profiles__logo-placeholder", children: "Logo" }),
4102
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("strong", { children: value.name || "Brand preview" })
4103
+ ] }),
4104
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "uix-brand-profiles__preview-body", children: [
4105
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("h3", { children: "Readable, serializable branding" }),
4106
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("p", { children: "Accent, links, rings, and muted states continue through the existing UIx token chain." }),
4107
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("button", { type: "button", className: "uix-btn uix-btn--primary", children: "Primary action" }),
4108
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("a", { href: "#brand-profile-preview", children: "Preview link" })
4109
+ ] })
4110
+ ] })
4111
+ ] });
4112
+ }
4113
+ var BrandProfiles = BrandProfileEditor;
4114
+
4115
+ // src/components/DiffViewer.tsx
4116
+ var import_react31 = require("react");
4117
+
4118
+ // src/diff-model.ts
4119
+ function isJsonObject(value) {
4120
+ return value !== null && typeof value === "object" && !Array.isArray(value);
4121
+ }
4122
+ function jsonEqual(a, b) {
4123
+ if (a === b) return true;
4124
+ if (Array.isArray(a) && Array.isArray(b)) {
4125
+ return a.length === b.length && a.every((value, index) => jsonEqual(value, b[index]));
4126
+ }
4127
+ if (isJsonObject(a) && isJsonObject(b)) {
4128
+ const aKeys = Object.keys(a).sort();
4129
+ const bKeys = Object.keys(b).sort();
4130
+ return aKeys.length === bKeys.length && aKeys.every((key, index) => key === bKeys[index] && jsonEqual(a[key], b[key]));
4131
+ }
4132
+ return false;
4133
+ }
4134
+ function classify(base, current, incoming) {
4135
+ if (base === void 0) return "added";
4136
+ if (incoming === void 0) return "removed";
4137
+ const currentChanged = !jsonEqual(base, current);
4138
+ const incomingChanged = !jsonEqual(base, incoming);
4139
+ if (currentChanged && incomingChanged && !jsonEqual(current, incoming)) return "conflicted";
4140
+ return "changed";
4141
+ }
4142
+ function buildThreeWayDiff(base, current, incoming, path = "$") {
4143
+ if (jsonEqual(current, incoming)) return [];
4144
+ if (isJsonObject(base) || isJsonObject(current) || isJsonObject(incoming)) {
4145
+ const objects = [base, current, incoming].filter(isJsonObject);
4146
+ const keys = [...new Set(objects.flatMap((value) => Object.keys(value)))].sort();
4147
+ return keys.flatMap((key) => buildThreeWayDiff(
4148
+ isJsonObject(base) ? base[key] : void 0,
4149
+ isJsonObject(current) ? current[key] : void 0,
4150
+ isJsonObject(incoming) ? incoming[key] : void 0,
4151
+ `${path}.${key}`
4152
+ ));
4153
+ }
4154
+ return [{ path, kind: classify(base, current, incoming), base, current, incoming }];
4155
+ }
4156
+ function summarizeDiff(entries, resolutions = {}) {
4157
+ const summary = { added: 0, removed: 0, changed: 0, conflicted: 0, resolved: 0, pending: 0 };
4158
+ entries.forEach((entry) => {
4159
+ summary[entry.kind] += 1;
4160
+ if ((resolutions[entry.path] ?? "pending") === "pending") summary.pending += 1;
4161
+ else summary.resolved += 1;
4162
+ });
4163
+ return summary;
4164
+ }
4165
+
4166
+ // src/components/DiffViewer.tsx
4167
+ var import_jsx_runtime56 = require("react/jsx-runtime");
4168
+ var GROUPS = [
4169
+ { kind: "conflicted", label: "Conflicted" },
4170
+ { kind: "changed", label: "Changed" },
4171
+ { kind: "added", label: "Added" },
4172
+ { kind: "removed", label: "Removed" }
4173
+ ];
4174
+ var DEFAULT_LABELS = {};
4175
+ var renderValue = (value) => value === void 0 ? "Not present" : JSON.stringify(value, null, 2);
4176
+ function DiffViewer({
4177
+ base,
4178
+ current,
4179
+ incoming,
4180
+ entries: providedEntries,
4181
+ resolutions: controlledResolutions,
4182
+ onResolutionChange,
4183
+ labels = DEFAULT_LABELS,
4184
+ className
4185
+ }) {
4186
+ const id = (0, import_react31.useId)();
4187
+ const entries = (0, import_react31.useMemo)(() => providedEntries ?? buildThreeWayDiff(base, current, incoming), [providedEntries, base, current, incoming]);
4188
+ const [internalResolutions, setInternalResolutions] = (0, import_react31.useState)({});
4189
+ const resolutions = controlledResolutions ?? internalResolutions;
4190
+ const summary = summarizeDiff(entries, resolutions);
4191
+ const resolve2 = (path, resolution) => {
4192
+ if (controlledResolutions === void 0) setInternalResolutions((currentState) => ({ ...currentState, [path]: resolution }));
4193
+ onResolutionChange?.(path, resolution);
4194
+ };
4195
+ if (entries.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("section", { className: cx("uix-diff-viewer uix-diff-viewer--empty", className), "aria-label": "Configuration differences", children: [
4196
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("h3", { children: "No differences" }),
4197
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("p", { children: "Base, current, and incoming values are aligned." })
4198
+ ] });
4199
+ return /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("section", { className: cx("uix-diff-viewer", className), "aria-label": "Configuration differences", children: [
4200
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("div", { className: "uix-diff-viewer__summary", "aria-label": "Difference summary", children: [
4201
+ GROUPS.map((group) => /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("span", { children: [
4202
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("b", { children: summary[group.kind] }),
4203
+ " ",
4204
+ group.label.toLowerCase()
4205
+ ] }, group.kind)),
4206
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("span", { children: [
4207
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("b", { children: summary.resolved }),
4208
+ " resolved"
4209
+ ] }),
4210
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("span", { children: [
4211
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("b", { children: summary.pending }),
4212
+ " pending"
4213
+ ] })
4214
+ ] }),
4215
+ GROUPS.map((group) => {
4216
+ const grouped = entries.filter((entry) => entry.kind === group.kind);
4217
+ if (!grouped.length) return null;
4218
+ return /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("section", { className: "uix-diff-viewer__group", "aria-labelledby": `${id}-${group.kind}`, children: [
4219
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("h3", { id: `${id}-${group.kind}`, children: [
4220
+ group.label,
4221
+ " ",
4222
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("span", { children: grouped.length })
4223
+ ] }),
4224
+ grouped.map((entry) => {
4225
+ const resolution = resolutions[entry.path] ?? "pending";
4226
+ return /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("details", { className: "uix-diff-viewer__entry", "data-kind": entry.kind, open: entry.kind === "conflicted", children: [
4227
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("summary", { children: [
4228
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("code", { children: entry.path }),
4229
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(StatusPill, { tone: resolution === "pending" ? "warning" : "success", children: resolution })
4230
+ ] }),
4231
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("div", { className: "uix-diff-viewer__versions", children: ["base", "current", "incoming"].map((version) => /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("div", { role: "region", "aria-label": `${labels[version] ?? `${version[0].toUpperCase()}${version.slice(1)}`} value for ${entry.path}`, children: [
4232
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("strong", { children: labels[version] ?? `${version[0].toUpperCase()}${version.slice(1)}` }),
4233
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("pre", { children: renderValue(entry[version]) })
4234
+ ] }, version)) }),
4235
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("div", { className: "uix-diff-viewer__actions", "aria-label": `Resolve ${entry.path}`, children: [
4236
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("button", { type: "button", className: "uix-btn uix-btn--primary uix-btn--sm", "aria-pressed": resolution === "accept", onClick: () => resolve2(entry.path, "accept"), children: "Accept incoming" }),
4237
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", "aria-pressed": resolution === "skip", onClick: () => resolve2(entry.path, "skip"), children: "Keep current" }),
4238
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", "aria-pressed": resolution === "pending", onClick: () => resolve2(entry.path, "pending"), children: "Mark pending" })
4239
+ ] })
4240
+ ] }, entry.path);
4241
+ })
4242
+ ] }, group.kind);
4243
+ }),
4244
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("p", { className: "uix-visually-hidden", "aria-live": "polite", children: [
4245
+ summary.resolved,
4246
+ " of ",
4247
+ entries.length,
4248
+ " differences resolved."
4249
+ ] })
4250
+ ] });
4251
+ }
2568
4252
  // Annotate the CommonJS export names for ESM import in node:
2569
4253
  0 && (module.exports = {
2570
4254
  Alert,
2571
4255
  AppShell,
2572
4256
  Avatar,
2573
4257
  AvatarGroup,
4258
+ BrandProfileEditor,
4259
+ BrandProfiles,
4260
+ BuilderCanvas,
2574
4261
  BulkBar,
2575
4262
  Button,
2576
4263
  ButtonGroup,
@@ -2578,6 +4265,7 @@ function StatusRow({ indicator, name, meta, className, ...props }) {
2578
4265
  CellStrong,
2579
4266
  CellSub,
2580
4267
  Checkbox,
4268
+ ColorPicker,
2581
4269
  CommandGroup,
2582
4270
  CommandItem,
2583
4271
  CommandPalette,
@@ -2587,9 +4275,11 @@ function StatusRow({ indicator, name, meta, className, ...props }) {
2587
4275
  ComposerBar,
2588
4276
  ContentList,
2589
4277
  ContentListItem,
4278
+ DateRangePicker,
2590
4279
  DescriptionItem,
2591
4280
  DescriptionList,
2592
4281
  DetailLayout,
4282
+ DiffViewer,
2593
4283
  Drawer,
2594
4284
  EmptyState,
2595
4285
  ErrorState,
@@ -2610,11 +4300,14 @@ function StatusRow({ indicator, name, meta, className, ...props }) {
2610
4300
  KanbanCard,
2611
4301
  KanbanColumn,
2612
4302
  Label,
4303
+ LicensePositionBar,
2613
4304
  List,
2614
4305
  ListItem,
2615
4306
  LoadingState,
2616
4307
  Mark,
4308
+ MatchReview,
2617
4309
  Meter,
4310
+ MetricInput,
2618
4311
  Modal,
2619
4312
  NavFavourites,
2620
4313
  NavGroup,
@@ -2631,9 +4324,12 @@ function StatusRow({ indicator, name, meta, className, ...props }) {
2631
4324
  Prose,
2632
4325
  Radio,
2633
4326
  RadioGroup,
4327
+ RelationshipGraph,
2634
4328
  ResourceGrid,
2635
4329
  RowAction,
2636
4330
  RowActions,
4331
+ RuleBuilder,
4332
+ SchedulingCalendar,
2637
4333
  SectionHead,
2638
4334
  Segmented,
2639
4335
  SegmentedOption,
@@ -2664,28 +4360,68 @@ function StatusRow({ indicator, name, meta, className, ...props }) {
2664
4360
  Tr,
2665
4361
  Tree,
2666
4362
  UserChip,
4363
+ addCalendarDays,
4364
+ addCalendarMonths,
4365
+ appendRuleNode,
4366
+ applyBrandProfile,
2667
4367
  applyFilters,
4368
+ boundRelationshipGraph,
4369
+ buildMonthGrid,
4370
+ buildThreeWayDiff,
4371
+ clampMetricValue,
2668
4372
  clampWidth,
2669
4373
  computePosition,
4374
+ contrastRatio,
2670
4375
  cx,
4376
+ enumerateDateSpan,
4377
+ findRuleNodeDepth,
2671
4378
  flattenTree,
4379
+ hexToRgb,
2672
4380
  highlightSegments,
4381
+ hsvToHex,
4382
+ hsvToRgb,
4383
+ isDateInRange,
4384
+ isDateUnavailable,
4385
+ isRuleGroup,
4386
+ layoutRelationshipGraph,
4387
+ mapRuleGroup,
2673
4388
  matchFilter,
4389
+ meetsContrast,
2674
4390
  mergePinned,
4391
+ moveRuleNode,
2675
4392
  multiSort,
2676
4393
  nextSortDir,
4394
+ normalizeHex,
4395
+ parseMetricValue,
2677
4396
  parseView,
4397
+ relationshipNeighbors,
4398
+ removeRuleNode,
2678
4399
  reorder,
4400
+ restoreBrandProfile,
4401
+ rgbToHex,
4402
+ rgbToHsv,
4403
+ ruleDepth,
2679
4404
  searchRows,
2680
4405
  selectAllState,
4406
+ selectRangeDate,
2681
4407
  serializeView,
2682
4408
  shouldVirtualize,
4409
+ startOfMonth,
4410
+ stepMetricValue,
4411
+ summarizeDiff,
4412
+ summarizeRule,
4413
+ toDateKey,
2683
4414
  toggleId,
2684
4415
  togglePage,
2685
4416
  toggleSort,
4417
+ traverseRelationshipNode,
2686
4418
  treeNav,
2687
4419
  useAnchoredPosition,
2688
4420
  useDialog,
2689
4421
  useTable,
2690
- virtualWindow
4422
+ useVirtualRows,
4423
+ validateRuleDefinition,
4424
+ virtualWindow,
4425
+ zonedDateKey,
4426
+ zonedDateSpan
2691
4427
  });