@tensor_1/react 2.20.0 → 2.22.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/dist/{ChartCore-Do6gRBN3.d.cts → ChartCore-DzWe10bN.d.cts} +16 -1
  2. package/dist/chart-preset.cjs +25 -13
  3. package/dist/chart-preset.d.cts +2 -2
  4. package/dist/chart-preset.d.ts +1 -1
  5. package/dist/chart-preset.js +2 -1
  6. package/dist/chart.cjs +25 -13
  7. package/dist/chart.d.cts +2 -2
  8. package/dist/chart.d.ts +1 -1
  9. package/dist/chart.js +2 -1
  10. package/dist/components/AppShell.d.ts +5 -1
  11. package/dist/components/AppShell.js +4 -2
  12. package/dist/components/Avatar.d.ts +4 -2
  13. package/dist/components/Avatar.js +2 -2
  14. package/dist/components/BrandProfiles.d.ts +29 -2
  15. package/dist/components/BrandProfiles.js +45 -20
  16. package/dist/components/BuilderCanvas.d.ts +27 -2
  17. package/dist/components/BuilderCanvas.js +45 -22
  18. package/dist/components/Chart.d.ts +1 -1
  19. package/dist/components/Chart.js +2 -1
  20. package/dist/components/ChartCore.d.ts +17 -2
  21. package/dist/components/ChartCore.js +24 -13
  22. package/dist/components/ColorPicker.d.ts +24 -2
  23. package/dist/components/ColorPicker.js +38 -20
  24. package/dist/components/CommandPalette.d.ts +3 -1
  25. package/dist/components/CommandPalette.js +2 -2
  26. package/dist/components/DateRangePicker.d.ts +21 -2
  27. package/dist/components/DateRangePicker.js +26 -6
  28. package/dist/components/DetailPage.d.ts +22 -3
  29. package/dist/components/DetailPage.js +33 -3
  30. package/dist/components/DiffViewer.d.ts +34 -3
  31. package/dist/components/DiffViewer.js +60 -40
  32. package/dist/components/Drawer.d.ts +3 -1
  33. package/dist/components/Drawer.js +2 -2
  34. package/dist/components/Inbox.d.ts +3 -1
  35. package/dist/components/Inbox.js +2 -2
  36. package/dist/components/MatchReview.d.ts +49 -2
  37. package/dist/components/MatchReview.js +72 -30
  38. package/dist/components/Meter.d.ts +3 -1
  39. package/dist/components/Meter.js +3 -2
  40. package/dist/components/Pagination.d.ts +10 -2
  41. package/dist/components/Pagination.js +11 -4
  42. package/dist/components/Peek.d.ts +7 -1
  43. package/dist/components/Peek.js +4 -4
  44. package/dist/components/RelationshipGraph.js +6 -2
  45. package/dist/components/RuleBuilder.d.ts +42 -2
  46. package/dist/components/RuleBuilder.js +71 -37
  47. package/dist/components/SchedulingCalendar.d.ts +25 -2
  48. package/dist/components/SchedulingCalendar.js +38 -15
  49. package/dist/components/Stat.d.ts +5 -0
  50. package/dist/components/Stat.js +2 -2
  51. package/dist/components/Table.d.ts +11 -3
  52. package/dist/components/Table.js +5 -8
  53. package/dist/components/Tabs.d.ts +8 -1
  54. package/dist/components/Tabs.js +4 -5
  55. package/dist/components/Toast.d.ts +6 -2
  56. package/dist/components/Toast.js +4 -4
  57. package/dist/components/Tree.d.ts +9 -1
  58. package/dist/components/Tree.js +55 -8
  59. package/dist/fill-label.d.ts +7 -0
  60. package/dist/fill-label.js +9 -0
  61. package/dist/index.cjs +765 -463
  62. package/dist/index.d.cts +341 -28
  63. package/dist/index.d.ts +10 -10
  64. package/dist/index.js +18 -9
  65. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -51,8 +51,17 @@ __export(src_exports, {
51
51
  ContentList: () => ContentList,
52
52
  ContentListItem: () => ContentListItem,
53
53
  CopyButton: () => CopyButton,
54
+ DEFAULT_BRAND_PROFILE_EDITOR_LABELS: () => DEFAULT_BRAND_PROFILE_EDITOR_LABELS,
55
+ DEFAULT_BUILDER_CANVAS_LABELS: () => DEFAULT_BUILDER_CANVAS_LABELS,
56
+ DEFAULT_COLOR_PICKER_LABELS: () => DEFAULT_COLOR_PICKER_LABELS,
57
+ DEFAULT_DATE_RANGE_PICKER_LABELS: () => DEFAULT_DATE_RANGE_PICKER_LABELS,
58
+ DEFAULT_DIFF_VIEWER_LABELS: () => DEFAULT_DIFF_VIEWER_LABELS,
59
+ DEFAULT_MATCH_REVIEW_LABELS: () => DEFAULT_MATCH_REVIEW_LABELS,
60
+ DEFAULT_PAGINATION_LABELS: () => DEFAULT_PAGINATION_LABELS,
54
61
  DEFAULT_RELATIONSHIP_GRAPH_LABELS: () => DEFAULT_RELATIONSHIP_GRAPH_LABELS,
62
+ DEFAULT_RULE_BUILDER_LABELS: () => DEFAULT_RULE_BUILDER_LABELS,
55
63
  DEFAULT_SAVE_STATUS_LABELS: () => DEFAULT_SAVE_STATUS_LABELS,
64
+ DEFAULT_SCHEDULING_CALENDAR_LABELS: () => DEFAULT_SCHEDULING_CALENDAR_LABELS,
56
65
  DateRangePicker: () => DateRangePicker,
57
66
  DescriptionItem: () => DescriptionItem,
58
67
  DescriptionList: () => DescriptionList,
@@ -1172,7 +1181,7 @@ function PromptDialog({ open, title, description, inputLabel, defaultValue = "",
1172
1181
  var import_react16 = require("react");
1173
1182
  var import_jsx_runtime12 = require("react/jsx-runtime");
1174
1183
  var CloseIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M4 4l8 8M12 4l-8 8" }) });
1175
- function Drawer({ open, onClose, title, children, footer, className, ...rest }) {
1184
+ function Drawer({ open, onClose, title, children, footer, closeLabel = "Close drawer", className, ...rest }) {
1176
1185
  const ref = useDialog(open, onClose);
1177
1186
  const titleId = (0, import_react16.useId)();
1178
1187
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("dialog", { ref, className: cx("uix-drawer", className), "aria-labelledby": title ? titleId : void 0, ...rest, children: [
@@ -1183,7 +1192,7 @@ function Drawer({ open, onClose, title, children, footer, className, ...rest })
1183
1192
  {
1184
1193
  style: { marginLeft: "auto", border: 0, background: "transparent", color: "var(--uix-text-muted)", cursor: "pointer", width: 30, height: 30, display: "grid", placeItems: "center", borderRadius: "var(--uix-radius-sm)" },
1185
1194
  onClick: onClose,
1186
- "aria-label": "Close drawer",
1195
+ "aria-label": closeLabel,
1187
1196
  children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CloseIcon2, {})
1188
1197
  }
1189
1198
  )
@@ -1199,7 +1208,7 @@ var import_jsx_runtime13 = require("react/jsx-runtime");
1199
1208
  var CloseIcon3 = () => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M4 4l8 8M12 4l-8 8" }) });
1200
1209
  var ChevronUp = () => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M2 8l4-4 4 4" }) });
1201
1210
  var ChevronDown = () => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M2 4l4 4 4-4" }) });
1202
- function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hint, className, onClick, ...rest }) {
1211
+ function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hint, previousLabel = "Previous record", nextLabel = "Next record", closeLabel = "Close preview", className, onClick, ...rest }) {
1203
1212
  const ref = useDialog(open, onClose);
1204
1213
  const hasNav = onNavPrev != null || onNavNext != null;
1205
1214
  const titleId = (0, import_react17.useId)();
@@ -1214,12 +1223,12 @@ function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hi
1214
1223
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("dialog", { ref, className: cx("uix-peek", className), "aria-labelledby": title ? titleId : void 0, ...rest, onClick: onBackdropClick, children: [
1215
1224
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "uix-peek__header", children: [
1216
1225
  hasNav && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "uix-peek__nav", children: [
1217
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { className: "uix-peek__navbtn", onClick: () => onNavPrev?.(), "aria-disabled": onNavPrev ? void 0 : true, "aria-label": "Previous record", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ChevronUp, {}) }),
1218
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { className: "uix-peek__navbtn", onClick: () => onNavNext?.(), "aria-disabled": onNavNext ? void 0 : true, "aria-label": "Next record", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ChevronDown, {}) })
1226
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { className: "uix-peek__navbtn", onClick: () => onNavPrev?.(), "aria-disabled": onNavPrev ? void 0 : true, "aria-label": previousLabel, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ChevronUp, {}) }),
1227
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { className: "uix-peek__navbtn", onClick: () => onNavNext?.(), "aria-disabled": onNavNext ? void 0 : true, "aria-label": nextLabel, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ChevronDown, {}) })
1219
1228
  ] }),
1220
1229
  title && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h2", { className: "uix-peek__title", id: titleId, children: title }),
1221
1230
  hint && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "uix-peek__hint", children: hint }),
1222
- onClose && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { className: "uix-peek__close", onClick: onClose, "aria-label": "Close preview", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CloseIcon3, {}) })
1231
+ onClose && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { className: "uix-peek__close", onClick: onClose, "aria-label": closeLabel, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CloseIcon3, {}) })
1223
1232
  ] }),
1224
1233
  children != null && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "uix-peek__body", children }),
1225
1234
  footer != null && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "uix-peek__footer", children: footer })
@@ -1293,16 +1302,46 @@ function DetailLayout({ side, children, className, ...props }) {
1293
1302
  }
1294
1303
 
1295
1304
  // src/components/DetailPage.tsx
1305
+ var import_react19 = require("react");
1296
1306
  var import_jsx_runtime18 = require("react/jsx-runtime");
1297
- function DetailPage({ back, eyebrow, title, subtitle, actions, tabs, metrics, side, children, tabsLabel, className, ...props }) {
1307
+ var plainLink = ({ href, className, children, onClick, ...rest }) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("a", { href, className, onClick, ...rest, children });
1308
+ var isPlainLeftClick = (e) => e.button === 0 && !e.metaKey && !e.ctrlKey && !e.shiftKey && !e.altKey;
1309
+ function DetailPage({
1310
+ back,
1311
+ eyebrow,
1312
+ title,
1313
+ subtitle,
1314
+ actions,
1315
+ tabs,
1316
+ metrics,
1317
+ side,
1318
+ children,
1319
+ tabsLabel,
1320
+ renderLink = plainLink,
1321
+ onTabSelect,
1322
+ className,
1323
+ ...props
1324
+ }) {
1298
1325
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("section", { className: cx("uix-detail-page", className), ...props, children: [
1299
- back && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("a", { className: "uix-link uix-link--quiet uix-detail-page__back", href: back.href, children: back.label }),
1326
+ back && renderLink({ href: back.href, className: "uix-link uix-link--quiet uix-detail-page__back", children: back.label }),
1300
1327
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(PageHeader, { eyebrow, title, subtitle, actions }),
1301
1328
  metrics && metrics.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("dl", { className: "uix-detail-page__metrics", children: metrics.map((metric) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { children: [
1302
1329
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("dt", { children: metric.label }),
1303
1330
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("dd", { children: metric.value })
1304
1331
  ] }, metric.id)) }),
1305
- tabs && tabs.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("nav", { className: "uix-tabs", "aria-label": tabsLabel, children: tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("a", { className: "uix-tab", "aria-current": tab.active ? "page" : void 0, href: tab.href, children: tab.label }, tab.id)) }),
1332
+ tabs && tabs.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("nav", { className: "uix-tabs", "aria-label": tabsLabel, children: tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react19.Fragment, { children: renderLink({
1333
+ href: tab.href,
1334
+ className: "uix-tab",
1335
+ children: tab.label,
1336
+ ...tab.active ? { "aria-current": "page" } : {},
1337
+ ...onTabSelect ? {
1338
+ onClick: (e) => {
1339
+ if (!isPlainLeftClick(e)) return;
1340
+ e.preventDefault();
1341
+ onTabSelect(tab.id);
1342
+ }
1343
+ } : {}
1344
+ }) }, tab.id)) }),
1306
1345
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DetailLayout, { side, children })
1307
1346
  ] });
1308
1347
  }
@@ -1365,11 +1404,11 @@ function ListItem({ title, meta, trail, leading, children, className, ...props }
1365
1404
  }
1366
1405
 
1367
1406
  // src/components/DescriptionList.tsx
1368
- var import_react19 = require("react");
1407
+ var import_react20 = require("react");
1369
1408
  var import_jsx_runtime23 = require("react/jsx-runtime");
1370
1409
  function DescriptionList({ items, children, className, ...props }) {
1371
1410
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("dl", { className: cx("uix-dl", className), ...props, children: [
1372
- items?.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react19.Fragment, { children: [
1411
+ items?.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react20.Fragment, { children: [
1373
1412
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("dt", { children: item.term }),
1374
1413
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("dd", { children: item.description })
1375
1414
  ] }, item.id ?? i)),
@@ -1384,21 +1423,21 @@ function DescriptionItem({ term, children }) {
1384
1423
  }
1385
1424
 
1386
1425
  // src/components/Tabs.tsx
1387
- var import_react20 = require("react");
1426
+ var import_react21 = require("react");
1388
1427
  var import_jsx_runtime24 = require("react/jsx-runtime");
1389
- var TabsCtx = (0, import_react20.createContext)({});
1428
+ var TabsCtx = (0, import_react21.createContext)({});
1390
1429
  var Chevron = ({ d }) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("path", { d }) });
1391
1430
  var inlineOffset = (el, rtl) => rtl ? -el.scrollLeft : el.scrollLeft;
1392
- function Tabs({ variant = "line", value, onChange, children, className, overflow = "wrap" }) {
1393
- const baseId = (0, import_react20.useId)();
1394
- const listRef = (0, import_react20.useRef)(null);
1395
- const contextValue = (0, import_react20.useMemo)(() => ({ value, onChange, baseId }), [value, onChange, baseId]);
1431
+ function Tabs({ variant = "line", value, onChange, children, className, overflow = "wrap", activation = "automatic" }) {
1432
+ const baseId = (0, import_react21.useId)();
1433
+ const listRef = (0, import_react21.useRef)(null);
1434
+ const contextValue = (0, import_react21.useMemo)(() => ({ value, onChange, baseId }), [value, onChange, baseId]);
1396
1435
  const scroll = overflow === "scroll";
1397
- const [hidden, setHidden] = (0, import_react20.useState)({ start: false, end: false });
1398
- const kids = import_react20.Children.toArray(children);
1399
- const panels = kids.filter((k) => (0, import_react20.isValidElement)(k) && k.type === TabPanel);
1400
- const tabs = kids.filter((k) => !((0, import_react20.isValidElement)(k) && k.type === TabPanel));
1401
- (0, import_react20.useEffect)(() => {
1436
+ const [hidden, setHidden] = (0, import_react21.useState)({ start: false, end: false });
1437
+ const kids = import_react21.Children.toArray(children);
1438
+ const panels = kids.filter((k) => (0, import_react21.isValidElement)(k) && k.type === TabPanel);
1439
+ const tabs = kids.filter((k) => !((0, import_react21.isValidElement)(k) && k.type === TabPanel));
1440
+ (0, import_react21.useEffect)(() => {
1402
1441
  const list2 = listRef.current;
1403
1442
  if (!list2) return;
1404
1443
  const all = Array.from(list2.querySelectorAll('[role="tab"]'));
@@ -1406,7 +1445,7 @@ function Tabs({ variant = "line", value, onChange, children, className, overflow
1406
1445
  const active = enabled.find((t) => t.getAttribute("aria-selected") === "true") ?? enabled[0];
1407
1446
  for (const t of all) t.tabIndex = t === active ? 0 : -1;
1408
1447
  });
1409
- (0, import_react20.useEffect)(() => {
1448
+ (0, import_react21.useEffect)(() => {
1410
1449
  const list2 = listRef.current;
1411
1450
  if (!scroll || !list2) return;
1412
1451
  const measure = () => {
@@ -1427,7 +1466,7 @@ function Tabs({ variant = "line", value, onChange, children, className, overflow
1427
1466
  observer.disconnect();
1428
1467
  };
1429
1468
  });
1430
- (0, import_react20.useEffect)(() => {
1469
+ (0, import_react21.useEffect)(() => {
1431
1470
  const list2 = listRef.current;
1432
1471
  const tab = list2?.querySelector('[role="tab"][aria-selected="true"]');
1433
1472
  if (!scroll || !list2 || !tab) return;
@@ -1469,10 +1508,9 @@ function Tabs({ variant = "line", value, onChange, children, className, overflow
1469
1508
  }
1470
1509
  e.preventDefault();
1471
1510
  const target = items[next];
1472
- if (target) {
1473
- target.focus();
1474
- target.click();
1475
- }
1511
+ if (!target) return;
1512
+ target.focus();
1513
+ if (activation === "automatic") target.click();
1476
1514
  };
1477
1515
  const list = /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1478
1516
  "div",
@@ -1518,7 +1556,7 @@ function Tabs({ variant = "line", value, onChange, children, className, overflow
1518
1556
  ] });
1519
1557
  }
1520
1558
  function Tab({ value, children, disabled, className, onClick, ...props }) {
1521
- const ctx = (0, import_react20.useContext)(TabsCtx);
1559
+ const ctx = (0, import_react21.useContext)(TabsCtx);
1522
1560
  const selected = ctx.value === value;
1523
1561
  return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1524
1562
  "button",
@@ -1540,7 +1578,7 @@ function Tab({ value, children, disabled, className, onClick, ...props }) {
1540
1578
  );
1541
1579
  }
1542
1580
  function TabPanel({ value, children, keepMounted, ...props }) {
1543
- const ctx = (0, import_react20.useContext)(TabsCtx);
1581
+ const ctx = (0, import_react21.useContext)(TabsCtx);
1544
1582
  const selected = ctx.value === value;
1545
1583
  if (!selected && !keepMounted) return null;
1546
1584
  return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
@@ -1558,7 +1596,7 @@ function TabPanel({ value, children, keepMounted, ...props }) {
1558
1596
  }
1559
1597
 
1560
1598
  // src/components/AppShell.tsx
1561
- var import_react21 = require("react");
1599
+ var import_react22 = require("react");
1562
1600
  var import_jsx_runtime25 = require("react/jsx-runtime");
1563
1601
  function AppShell({
1564
1602
  nav,
@@ -1570,11 +1608,13 @@ function AppShell({
1570
1608
  sidebar,
1571
1609
  topbar,
1572
1610
  children,
1611
+ skipToContentLabel = "Skip to content",
1612
+ exitFocusLabel = "Esc \xB7 Exit focus",
1573
1613
  className,
1574
1614
  ...props
1575
1615
  }) {
1576
1616
  const navState = nav ?? (collapsed ? "rail" : "full");
1577
- (0, import_react21.useEffect)(() => {
1617
+ (0, import_react22.useEffect)(() => {
1578
1618
  if (!focus || !onExitFocus) return;
1579
1619
  const onKey = (e) => {
1580
1620
  if (e.key === "Escape") onExitFocus();
@@ -1591,18 +1631,18 @@ function AppShell({
1591
1631
  "data-focus": focus || void 0,
1592
1632
  ...props,
1593
1633
  children: [
1594
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("a", { className: "uix-skiplink", href: `#${mainId}`, children: "Skip to content" }),
1634
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("a", { className: "uix-skiplink", href: `#${mainId}`, children: skipToContentLabel }),
1595
1635
  sidebar && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "uix-shell__sidebar", children: sidebar }),
1596
1636
  topbar && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "uix-shell__topbar", children: topbar }),
1597
1637
  /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("main", { id: mainId, tabIndex: -1, className: cx("uix-shell__main", mainBleed && "uix-shell__main--bleed"), children }),
1598
- focus && onExitFocus && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "uix-shell__focus-exit", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", onClick: onExitFocus, children: "Esc \xB7 Exit focus" }) })
1638
+ focus && onExitFocus && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "uix-shell__focus-exit", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", onClick: onExitFocus, children: exitFocusLabel }) })
1599
1639
  ]
1600
1640
  }
1601
1641
  );
1602
1642
  }
1603
1643
 
1604
1644
  // src/components/Sidebar.tsx
1605
- var import_react22 = require("react");
1645
+ var import_react23 = require("react");
1606
1646
  var import_jsx_runtime26 = require("react/jsx-runtime");
1607
1647
  var ChevronIcon = () => /* @__PURE__ */ (0, import_jsx_runtime26.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_runtime26.jsx)("path", { d: "M2 4l4 4 4-4" }) });
1608
1648
  function Sidebar({ collapsed, nav, onToggle, brand, toggleIcon, children, expandLabel = "Expand sidebar", collapseLabel = "Collapse sidebar", className, ...props }) {
@@ -1658,12 +1698,12 @@ function NavItem({ icon, badge, active, as: Tag = "a", children, className, ...p
1658
1698
  );
1659
1699
  }
1660
1700
  function NavGroup({ icon, label, children, defaultExpanded = true, expanded: controlledExpanded, onExpandedChange }) {
1661
- const [internalExpanded, setInternalExpanded] = (0, import_react22.useState)(defaultExpanded);
1701
+ const [internalExpanded, setInternalExpanded] = (0, import_react23.useState)(defaultExpanded);
1662
1702
  const expanded = controlledExpanded ?? internalExpanded;
1663
- const panelId = (0, import_react22.useId)();
1664
- const triggerRef = (0, import_react22.useRef)(null);
1665
- const panelRef = (0, import_react22.useRef)(null);
1666
- (0, import_react22.useEffect)(() => {
1703
+ const panelId = (0, import_react23.useId)();
1704
+ const triggerRef = (0, import_react23.useRef)(null);
1705
+ const panelRef = (0, import_react23.useRef)(null);
1706
+ (0, import_react23.useEffect)(() => {
1667
1707
  panelRef.current?.toggleAttribute("inert", !expanded);
1668
1708
  if (!expanded && panelRef.current?.contains(document.activeElement)) triggerRef.current?.focus();
1669
1709
  }, [expanded]);
@@ -1742,7 +1782,7 @@ function StarButton({
1742
1782
  }
1743
1783
 
1744
1784
  // src/components/CopyButton.tsx
1745
- var import_react23 = require("react");
1785
+ var import_react24 = require("react");
1746
1786
 
1747
1787
  // src/copy-model.ts
1748
1788
  async function copyText(value, clipboard = globalThis.navigator?.clipboard) {
@@ -1764,11 +1804,11 @@ var CopyGlyph = () => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("svg", { vi
1764
1804
  ] });
1765
1805
  var CheckGlyph = () => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M20 6 9 17l-5-5" }) });
1766
1806
  function CopyButton({ value, label, copiedLabel, failedLabel, className }) {
1767
- const [outcome, setOutcome] = (0, import_react23.useState)("idle");
1807
+ const [outcome, setOutcome] = (0, import_react24.useState)("idle");
1768
1808
  const copied = outcome === "copied";
1769
- const timer = (0, import_react23.useRef)(void 0);
1770
- const mounted = (0, import_react23.useRef)(true);
1771
- (0, import_react23.useEffect)(() => {
1809
+ const timer = (0, import_react24.useRef)(void 0);
1810
+ const mounted = (0, import_react24.useRef)(true);
1811
+ (0, import_react24.useEffect)(() => {
1772
1812
  mounted.current = true;
1773
1813
  return () => {
1774
1814
  mounted.current = false;
@@ -1803,7 +1843,7 @@ function CopyButton({ value, label, copiedLabel, failedLabel, className }) {
1803
1843
  }
1804
1844
 
1805
1845
  // src/components/NavFavourites.tsx
1806
- var import_react24 = require("react");
1846
+ var import_react25 = require("react");
1807
1847
  var import_jsx_runtime29 = require("react/jsx-runtime");
1808
1848
  var ChevronDownIcon = () => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1809
1849
  "svg",
@@ -1866,14 +1906,14 @@ function NavFavourites({
1866
1906
  emptyReason,
1867
1907
  className
1868
1908
  }) {
1869
- const regionId = (0, import_react24.useId)();
1870
- const headerRef = (0, import_react24.useRef)(null);
1871
- const regionRef = (0, import_react24.useRef)(null);
1872
- const [announcement, setAnnouncement] = (0, import_react24.useState)("");
1873
- const focusAfterRef = (0, import_react24.useRef)(null);
1874
- const triggerRefs = (0, import_react24.useRef)(/* @__PURE__ */ new Map());
1875
- const order = (0, import_react24.useMemo)(() => items.map((item) => item.id), [items]);
1876
- (0, import_react24.useEffect)(() => {
1909
+ const regionId = (0, import_react25.useId)();
1910
+ const headerRef = (0, import_react25.useRef)(null);
1911
+ const regionRef = (0, import_react25.useRef)(null);
1912
+ const [announcement, setAnnouncement] = (0, import_react25.useState)("");
1913
+ const focusAfterRef = (0, import_react25.useRef)(null);
1914
+ const triggerRefs = (0, import_react25.useRef)(/* @__PURE__ */ new Map());
1915
+ const order = (0, import_react25.useMemo)(() => items.map((item) => item.id), [items]);
1916
+ (0, import_react25.useEffect)(() => {
1877
1917
  const id = focusAfterRef.current;
1878
1918
  if (!id) return;
1879
1919
  focusAfterRef.current = null;
@@ -1890,7 +1930,7 @@ function NavFavourites({
1890
1930
  }
1891
1931
  onToggleCollapsed?.();
1892
1932
  };
1893
- const move = (0, import_react24.useCallback)((id, dir) => {
1933
+ const move = (0, import_react25.useCallback)((id, dir) => {
1894
1934
  const idx = order.indexOf(id);
1895
1935
  const next = idx + dir;
1896
1936
  if (idx < 0 || next < 0 || next >= order.length) return;
@@ -1903,13 +1943,13 @@ function NavFavourites({
1903
1943
  if (moved) setAnnouncement(`${moved.label} moved to position ${next + 1} of ${order.length}`);
1904
1944
  onReorder(reordered);
1905
1945
  }, [onReorder, order]);
1906
- const remove = (0, import_react24.useCallback)((id) => {
1946
+ const remove = (0, import_react25.useCallback)((id) => {
1907
1947
  const idx = order.indexOf(id);
1908
1948
  const neighbour = order[idx + 1] ?? order[idx - 1] ?? null;
1909
1949
  focusAfterRef.current = neighbour;
1910
1950
  onRemove(id);
1911
1951
  }, [onRemove, order]);
1912
- const registerTrigger = (0, import_react24.useCallback)((id, element) => {
1952
+ const registerTrigger = (0, import_react25.useCallback)((id, element) => {
1913
1953
  if (element) triggerRefs.current.set(id, element);
1914
1954
  else triggerRefs.current.delete(id);
1915
1955
  }, []);
@@ -1961,7 +2001,7 @@ function NavFavourites({
1961
2001
  )
1962
2002
  ] });
1963
2003
  }
1964
- var FavouriteRow = (0, import_react24.memo)(function FavouriteRow2({
2004
+ var FavouriteRow = (0, import_react25.memo)(function FavouriteRow2({
1965
2005
  item,
1966
2006
  isFirst,
1967
2007
  isLast,
@@ -1970,17 +2010,17 @@ var FavouriteRow = (0, import_react24.memo)(function FavouriteRow2({
1970
2010
  onRemove,
1971
2011
  registerTrigger
1972
2012
  }) {
1973
- const [open, setOpen] = (0, import_react24.useState)(false);
1974
- const menuId = (0, import_react24.useId)();
1975
- const triggerRef = (0, import_react24.useRef)(null);
1976
- const itemRefs = (0, import_react24.useRef)([]);
2013
+ const [open, setOpen] = (0, import_react25.useState)(false);
2014
+ const menuId = (0, import_react25.useId)();
2015
+ const triggerRef = (0, import_react25.useRef)(null);
2016
+ const itemRefs = (0, import_react25.useRef)([]);
1977
2017
  const optionsTemplate = labels.options ?? "{label} favourite options";
1978
2018
  const actions = [
1979
2019
  ...isFirst ? [] : [{ label: labels.moveUp, onSelect: () => onMove(item.id, -1) }],
1980
2020
  ...isLast ? [] : [{ label: labels.moveDown, onSelect: () => onMove(item.id, 1) }],
1981
2021
  { label: labels.remove.replace("{label}", item.label), onSelect: () => onRemove(item.id) }
1982
2022
  ];
1983
- (0, import_react24.useEffect)(() => {
2023
+ (0, import_react25.useEffect)(() => {
1984
2024
  if (open) itemRefs.current[0]?.focus();
1985
2025
  }, [open]);
1986
2026
  const closeAndReturnFocus = () => {
@@ -2124,14 +2164,14 @@ function Spinner({ size = "md", accent, label, className, ...props }) {
2124
2164
  }
2125
2165
 
2126
2166
  // src/components/Toast.tsx
2127
- var import_react25 = require("react");
2167
+ var import_react26 = require("react");
2128
2168
  var import_jsx_runtime32 = require("react/jsx-runtime");
2129
- var ToasterContext = (0, import_react25.createContext)(null);
2130
- function Toast({ title, message, tone, icon, onClose, leaving, className, ...props }) {
2169
+ var ToasterContext = (0, import_react26.createContext)(null);
2170
+ function Toast({ title, message, tone, icon, onClose, leaving, dismissLabel = "Dismiss", className, ...props }) {
2131
2171
  const assertive = tone === "danger";
2132
- const toaster = (0, import_react25.useContext)(ToasterContext);
2133
- const bodyRef = (0, import_react25.useRef)(null);
2134
- (0, import_react25.useEffect)(() => {
2172
+ const toaster = (0, import_react26.useContext)(ToasterContext);
2173
+ const bodyRef = (0, import_react26.useRef)(null);
2174
+ (0, import_react26.useEffect)(() => {
2135
2175
  if (assertive || !toaster) return;
2136
2176
  const text = bodyRef.current?.textContent?.trim();
2137
2177
  if (text) toaster.announce(text);
@@ -2163,33 +2203,33 @@ function Toast({ title, message, tone, icon, onClose, leaving, className, ...pro
2163
2203
  title && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "uix-toast__title", children: title }),
2164
2204
  message && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "uix-toast__msg", children: message })
2165
2205
  ] }),
2166
- onClose && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("button", { className: "uix-toast__close", onClick: handleClose, "aria-label": "Dismiss", children: "\xD7" })
2206
+ onClose && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("button", { className: "uix-toast__close", onClick: handleClose, "aria-label": dismissLabel, children: "\xD7" })
2167
2207
  ]
2168
2208
  }
2169
2209
  );
2170
2210
  }
2171
- function Toaster({ children, className, ...props }) {
2172
- const [announced, setAnnounced] = (0, import_react25.useState)("");
2173
- const clearTimer = (0, import_react25.useRef)(null);
2174
- const announce = (0, import_react25.useCallback)((text) => {
2211
+ function Toaster({ children, regionLabel = "Notifications", className, ...props }) {
2212
+ const [announced, setAnnounced] = (0, import_react26.useState)("");
2213
+ const clearTimer = (0, import_react26.useRef)(null);
2214
+ const announce = (0, import_react26.useCallback)((text) => {
2175
2215
  setAnnounced((cur) => cur ? `${cur} ${text}` : text);
2176
2216
  if (clearTimer.current != null) clearTimeout(clearTimer.current);
2177
2217
  clearTimer.current = window.setTimeout(() => setAnnounced(""), 3e3);
2178
2218
  }, []);
2179
- (0, import_react25.useEffect)(() => () => {
2219
+ (0, import_react26.useEffect)(() => () => {
2180
2220
  if (clearTimer.current != null) clearTimeout(clearTimer.current);
2181
2221
  }, []);
2182
- const ctx = (0, import_react25.useMemo)(() => ({ announce }), [announce]);
2183
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(ToasterContext.Provider, { value: ctx, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: cx("uix-toaster", className), role: "region", "aria-label": "Notifications", ...props, children: [
2222
+ const ctx = (0, import_react26.useMemo)(() => ({ announce }), [announce]);
2223
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(ToasterContext.Provider, { value: ctx, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: cx("uix-toaster", className), role: "region", "aria-label": regionLabel, ...props, children: [
2184
2224
  /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "uix-visually-hidden", role: "status", "aria-live": "polite", children: announced }),
2185
2225
  children
2186
2226
  ] }) });
2187
2227
  }
2188
2228
 
2189
2229
  // src/components/Table.tsx
2190
- var import_react26 = require("react");
2230
+ var import_react27 = require("react");
2191
2231
  var import_jsx_runtime33 = require("react/jsx-runtime");
2192
- var TableWrap = (0, import_react26.forwardRef)(function TableWrap2({ announcement, children, className, ...props }, ref) {
2232
+ var TableWrap = (0, import_react27.forwardRef)(function TableWrap2({ announcement, children, className, ...props }, ref) {
2193
2233
  return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { ref, className: cx("uix-table-wrap", className), ...props, children: [
2194
2234
  announcement != null && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { role: "status", className: "uix-visually-hidden", children: announcement }),
2195
2235
  children
@@ -2253,12 +2293,9 @@ function Tr({ selected, pinned, className, children, ...props }) {
2253
2293
  }
2254
2294
  );
2255
2295
  }
2256
- function BulkBar({ count, children, className, ...props }) {
2257
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: cx("uix-bulkbar", className), role: "toolbar", "aria-label": "Bulk actions", ...props, children: [
2258
- count != null && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("span", { className: "uix-bulkbar__count", children: [
2259
- count,
2260
- " selected"
2261
- ] }),
2296
+ function BulkBar({ count, children, label = "Bulk actions", selectedLabel = (n) => `${n} selected`, className, ...props }) {
2297
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: cx("uix-bulkbar", className), role: "toolbar", "aria-label": label, ...props, children: [
2298
+ count != null && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "uix-bulkbar__count", children: selectedLabel(count) }),
2262
2299
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "uix-bulkbar__spacer" }),
2263
2300
  children
2264
2301
  ] });
@@ -2269,7 +2306,7 @@ function RowActions({ children, className, ...props }) {
2269
2306
  function RowAction({ children, className, type = "button", ...props }) {
2270
2307
  return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("button", { type, className: cx("uix-rowact", className), ...props, children });
2271
2308
  }
2272
- function ExpandToggle({ expanded, controls, className, type = "button", ...props }) {
2309
+ function ExpandToggle({ expanded, controls, expandLabel = "Expand row", collapseLabel = "Collapse row", className, type = "button", ...props }) {
2273
2310
  return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
2274
2311
  "button",
2275
2312
  {
@@ -2277,7 +2314,7 @@ function ExpandToggle({ expanded, controls, className, type = "button", ...props
2277
2314
  className: cx("uix-table__expand", className),
2278
2315
  "aria-expanded": expanded,
2279
2316
  "aria-controls": controls,
2280
- "aria-label": expanded ? "Collapse row" : "Expand row",
2317
+ "aria-label": expanded ? collapseLabel : expandLabel,
2281
2318
  ...props,
2282
2319
  children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("path", { d: "M6 4l4 4-4 4" }) })
2283
2320
  }
@@ -2311,16 +2348,22 @@ function getPageNumbers(page, total) {
2311
2348
  pages.push(total);
2312
2349
  return pages;
2313
2350
  }
2314
- function Pagination({ page, pageCount, onChange, className, ...props }) {
2351
+ var DEFAULT_PAGINATION_LABELS = {
2352
+ region: "Pagination",
2353
+ previous: "Previous page",
2354
+ next: "Next page"
2355
+ };
2356
+ function Pagination({ page, pageCount, onChange, labels: labelOverrides, className, ...props }) {
2357
+ const labels = { ...DEFAULT_PAGINATION_LABELS, ...labelOverrides };
2315
2358
  const pages = getPageNumbers(page, pageCount);
2316
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("nav", { "aria-label": "Pagination", className: cx("uix-pagination", className), ...props, children: [
2359
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("nav", { "aria-label": labels.region, className: cx("uix-pagination", className), ...props, children: [
2317
2360
  /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2318
2361
  "button",
2319
2362
  {
2320
2363
  className: "uix-pagination__btn",
2321
2364
  onClick: () => onChange(page - 1),
2322
2365
  disabled: page <= 1,
2323
- "aria-label": "Previous page",
2366
+ "aria-label": labels.previous,
2324
2367
  children: "\u2039"
2325
2368
  }
2326
2369
  ),
@@ -2342,7 +2385,7 @@ function Pagination({ page, pageCount, onChange, className, ...props }) {
2342
2385
  className: "uix-pagination__btn",
2343
2386
  onClick: () => onChange(page + 1),
2344
2387
  disabled: page >= pageCount,
2345
- "aria-label": "Next page",
2388
+ "aria-label": labels.next,
2346
2389
  children: "\u203A"
2347
2390
  }
2348
2391
  )
@@ -2393,7 +2436,8 @@ var toneText = {
2393
2436
  attention: "needs attention",
2394
2437
  overdue: "overdue"
2395
2438
  };
2396
- function Meter({ value = 0, tone, label, className, ...props }) {
2439
+ function Meter({ value = 0, tone, label, toneLabels, className, ...props }) {
2440
+ const spoken = { ...toneText, ...toneLabels };
2397
2441
  const pct = Math.max(0, Math.min(100, value));
2398
2442
  return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2399
2443
  "div",
@@ -2402,7 +2446,7 @@ function Meter({ value = 0, tone, label, className, ...props }) {
2402
2446
  "aria-valuenow": pct,
2403
2447
  "aria-valuemin": 0,
2404
2448
  "aria-valuemax": 100,
2405
- "aria-valuetext": tone && tone !== "success" ? `${pct}%, ${toneText[tone]}` : void 0,
2449
+ "aria-valuetext": tone && tone !== "success" ? `${pct}%, ${spoken[tone]}` : void 0,
2406
2450
  "aria-label": label,
2407
2451
  className: cx("uix-meter", className),
2408
2452
  ...props,
@@ -2419,11 +2463,11 @@ function Meter({ value = 0, tone, label, className, ...props }) {
2419
2463
  }
2420
2464
 
2421
2465
  // src/components/Stat.tsx
2422
- var import_react27 = require("react");
2466
+ var import_react28 = require("react");
2423
2467
  var import_jsx_runtime38 = require("react/jsx-runtime");
2424
2468
  var Chevron2 = () => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "uix-stat__chevron", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("path", { d: "m6 9 6 6 6-6" }) }) });
2425
- var Stat = (0, import_react27.forwardRef)(function Stat2({ label, value, icon, meta, trend, tone = "neutral", size = "hero", onActivate, activateLabel, expanded, className, onClick, ...props }, ref) {
2426
- const id = (0, import_react27.useId)();
2469
+ var Stat = (0, import_react28.forwardRef)(function Stat2({ label, value, icon, meta, trend, tone = "neutral", size = "hero", onActivate, activateLabel, expanded, trendLabels, className, onClick, ...props }, ref) {
2470
+ const id = (0, import_react28.useId)();
2427
2471
  const interactive = onActivate != null;
2428
2472
  const classes = cx(
2429
2473
  "uix-stat",
@@ -2443,7 +2487,7 @@ var Stat = (0, import_react27.forwardRef)(function Stat2({ label, value, icon, m
2443
2487
  (meta != null || trend != null) && /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Row, { className: "uix-stat__meta", id: interactive ? `${id}-meta` : void 0, children: [
2444
2488
  trend != null && /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("span", { className: cx("uix-stat__trend", `uix-stat__trend--${trend.direction}`), children: [
2445
2489
  /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: trend.direction === "up" ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("path", { d: "M12 19V5m-6 6 6-6 6 6" }) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("path", { d: "M12 5v14m6-6-6 6-6-6" }) }),
2446
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "uix-visually-hidden", children: trend.direction === "up" ? "up" : "down" }),
2490
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "uix-visually-hidden", children: trend.direction === "up" ? trendLabels?.up ?? "up" : trendLabels?.down ?? "down" }),
2447
2491
  trend.label
2448
2492
  ] }),
2449
2493
  meta
@@ -2480,14 +2524,14 @@ var Stat = (0, import_react27.forwardRef)(function Stat2({ label, value, icon, m
2480
2524
  });
2481
2525
 
2482
2526
  // src/components/Tooltip.tsx
2483
- var import_react28 = require("react");
2527
+ var import_react29 = require("react");
2484
2528
  var import_jsx_runtime39 = require("react/jsx-runtime");
2485
2529
  function Tooltip({ label, placement = "top", className, children, ...props }) {
2486
- const triggerRef = (0, import_react28.useRef)(null);
2487
- const bubbleRef = (0, import_react28.useRef)(null);
2488
- const closeTimer = (0, import_react28.useRef)(null);
2489
- const [open, setOpen] = (0, import_react28.useState)(false);
2490
- const id = (0, import_react28.useId)();
2530
+ const triggerRef = (0, import_react29.useRef)(null);
2531
+ const bubbleRef = (0, import_react29.useRef)(null);
2532
+ const closeTimer = (0, import_react29.useRef)(null);
2533
+ const [open, setOpen] = (0, import_react29.useState)(false);
2534
+ const id = (0, import_react29.useId)();
2491
2535
  useAnchoredPosition(triggerRef, bubbleRef, { open, placement, offset: 6 });
2492
2536
  const cancelClose = () => {
2493
2537
  if (closeTimer.current != null) {
@@ -2521,7 +2565,7 @@ function Tooltip({ label, placement = "top", className, children, ...props }) {
2521
2565
  cancelClose();
2522
2566
  closeTimer.current = window.setTimeout(hide, 100);
2523
2567
  };
2524
- (0, import_react28.useEffect)(() => {
2568
+ (0, import_react29.useEffect)(() => {
2525
2569
  if (!open) return;
2526
2570
  const onKey = (e) => {
2527
2571
  if (e.key === "Escape") hide();
@@ -2529,7 +2573,7 @@ function Tooltip({ label, placement = "top", className, children, ...props }) {
2529
2573
  document.addEventListener("keydown", onKey);
2530
2574
  return () => document.removeEventListener("keydown", onKey);
2531
2575
  }, [open]);
2532
- (0, import_react28.useEffect)(() => () => {
2576
+ (0, import_react29.useEffect)(() => () => {
2533
2577
  if (closeTimer.current != null) clearTimeout(closeTimer.current);
2534
2578
  const b = bubbleRef.current;
2535
2579
  if (b && b.matches(":popover-open")) {
@@ -2540,8 +2584,8 @@ function Tooltip({ label, placement = "top", className, children, ...props }) {
2540
2584
  }
2541
2585
  }, []);
2542
2586
  const bubbleAttr = { popover: "manual" };
2543
- const single = (0, import_react28.isValidElement)(children);
2544
- const child = single ? (0, import_react28.cloneElement)(children, {
2587
+ const single = (0, import_react29.isValidElement)(children);
2588
+ const child = single ? (0, import_react29.cloneElement)(children, {
2545
2589
  "aria-describedby": [children.props["aria-describedby"], id].filter(Boolean).join(" ")
2546
2590
  }) : children;
2547
2591
  return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
@@ -2643,7 +2687,7 @@ function LoadingState({ rows = 3, density = "comfortable", label, className, ...
2643
2687
  }
2644
2688
 
2645
2689
  // src/components/RelativeTime.tsx
2646
- var import_react29 = require("react");
2690
+ var import_react30 = require("react");
2647
2691
 
2648
2692
  // src/relative-time-model.ts
2649
2693
  function parseDateValue(value) {
@@ -2706,12 +2750,12 @@ function RelativeTime({
2706
2750
  }) {
2707
2751
  const interval = refreshInterval === false ? 0 : Math.max(0, refreshInterval);
2708
2752
  const fixedNow = now == null ? void 0 : new Date(now).getTime();
2709
- const subscribe = (0, import_react29.useCallback)((listener) => fixedNow == null ? subscribeRelativeClock(interval, listener) : () => {
2753
+ const subscribe = (0, import_react30.useCallback)((listener) => fixedNow == null ? subscribeRelativeClock(interval, listener) : () => {
2710
2754
  }, [fixedNow, interval]);
2711
- const getSnapshot = (0, import_react29.useCallback)(() => fixedNow ?? readRelativeClock(interval), [fixedNow, interval]);
2712
- const getServerSnapshot = (0, import_react29.useCallback)(() => fixedNow ?? Date.now(), [fixedNow]);
2713
- const clock = (0, import_react29.useSyncExternalStore)(subscribe, getSnapshot, getServerSnapshot);
2714
- const date = (0, import_react29.useMemo)(() => parseDateValue(value), [value]);
2755
+ const getSnapshot = (0, import_react30.useCallback)(() => fixedNow ?? readRelativeClock(interval), [fixedNow, interval]);
2756
+ const getServerSnapshot = (0, import_react30.useCallback)(() => fixedNow ?? Date.now(), [fixedNow]);
2757
+ const clock = (0, import_react30.useSyncExternalStore)(subscribe, getSnapshot, getServerSnapshot);
2758
+ const date = (0, import_react30.useMemo)(() => parseDateValue(value), [value]);
2715
2759
  if (!date) return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("time", { "data-invalid": "true", suppressHydrationWarning: true, ...props, children: invalidLabel });
2716
2760
  const current = new Date(clock);
2717
2761
  const content = formatter ? formatter({ date, now: current, locale, timeZone }) : (() => {
@@ -2757,11 +2801,11 @@ function Label({ color, dot, children, className, style, ...props }) {
2757
2801
 
2758
2802
  // src/components/Avatar.tsx
2759
2803
  var import_jsx_runtime44 = require("react/jsx-runtime");
2760
- function Avatar({ size = "md", src, alt, children, status, className, ...props }) {
2804
+ function Avatar({ size = "md", src, alt, children, status, onlineLabel = "online", className, ...props }) {
2761
2805
  return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: cx("uix-avatar", size !== "md" && `uix-avatar--${size}`, className), ...props, children: [
2762
2806
  src ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("img", { src, alt: alt ?? "" }) : children,
2763
2807
  status && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "uix-avatar__status", "aria-hidden": "true" }),
2764
- status && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "uix-visually-hidden", children: "online" })
2808
+ status && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "uix-visually-hidden", children: onlineLabel })
2765
2809
  ] });
2766
2810
  }
2767
2811
  function AvatarGroup({ children, className, ...props }) {
@@ -2841,13 +2885,13 @@ function SaveStatus({ state, labels, onRetry, className, ...props }) {
2841
2885
  }
2842
2886
 
2843
2887
  // src/components/Composer.tsx
2844
- var import_react30 = require("react");
2888
+ var import_react31 = require("react");
2845
2889
  var import_jsx_runtime47 = require("react/jsx-runtime");
2846
2890
  function Composer({ children, className, label = "Add a comment", ...props }) {
2847
- return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { className: cx("uix-composer", className), ...props, children: import_react30.Children.map(children, (child) => {
2848
- if (!(0, import_react30.isValidElement)(child) || child.type !== "textarea") return child;
2891
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { className: cx("uix-composer", className), ...props, children: import_react31.Children.map(children, (child) => {
2892
+ if (!(0, import_react31.isValidElement)(child) || child.type !== "textarea") return child;
2849
2893
  const p = child.props;
2850
- return p["aria-label"] || p["aria-labelledby"] || p.id ? child : (0, import_react30.cloneElement)(child, { "aria-label": label });
2894
+ return p["aria-label"] || p["aria-labelledby"] || p.id ? child : (0, import_react31.cloneElement)(child, { "aria-label": label });
2851
2895
  }) });
2852
2896
  }
2853
2897
  function ComposerBar({ children, className, ...props }) {
@@ -2855,11 +2899,11 @@ function ComposerBar({ children, className, ...props }) {
2855
2899
  }
2856
2900
 
2857
2901
  // src/components/Segmented.tsx
2858
- var import_react31 = require("react");
2902
+ var import_react32 = require("react");
2859
2903
  var import_jsx_runtime48 = require("react/jsx-runtime");
2860
- var SegmentedCtx = (0, import_react31.createContext)({});
2904
+ var SegmentedCtx = (0, import_react32.createContext)({});
2861
2905
  function Segmented({ value, onChange, children, className, ...props }) {
2862
- const contextValue = (0, import_react31.useMemo)(() => ({ value, onChange }), [value, onChange]);
2906
+ const contextValue = (0, import_react32.useMemo)(() => ({ value, onChange }), [value, onChange]);
2863
2907
  return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(SegmentedCtx.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { role: "group", className: cx("uix-segmented", className), ...props, children }) });
2864
2908
  }
2865
2909
  function SegmentedOption({
@@ -2869,7 +2913,7 @@ function SegmentedOption({
2869
2913
  onClick,
2870
2914
  ...props
2871
2915
  }) {
2872
- const ctx = (0, import_react31.useContext)(SegmentedCtx);
2916
+ const ctx = (0, import_react32.useContext)(SegmentedCtx);
2873
2917
  const selected = ctx.value === value;
2874
2918
  return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
2875
2919
  "button",
@@ -2888,12 +2932,12 @@ function SegmentedOption({
2888
2932
  }
2889
2933
 
2890
2934
  // src/components/TableControls.tsx
2891
- var import_react32 = require("react");
2935
+ var import_react33 = require("react");
2892
2936
  var import_jsx_runtime49 = require("react/jsx-runtime");
2893
2937
  function ViewMenu({ density, densityLabel, densityOptions, onDensityChange, zebra, freeze, columns, columnsLabel, onColumnVisibilityChange }) {
2894
- const densityLabelId = (0, import_react32.useId)();
2895
- const zebraLabelId = (0, import_react32.useId)();
2896
- const freezeLabelId = (0, import_react32.useId)();
2938
+ const densityLabelId = (0, import_react33.useId)();
2939
+ const zebraLabelId = (0, import_react33.useId)();
2940
+ const freezeLabelId = (0, import_react33.useId)();
2897
2941
  return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "uix-view-menu", children: [
2898
2942
  /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "uix-view-menu__group", children: [
2899
2943
  /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { id: densityLabelId, className: "uix-view-menu__label", children: densityLabel }),
@@ -2958,10 +3002,10 @@ function TimelineItem({ node, meta, children, className, ...props }) {
2958
3002
  }
2959
3003
 
2960
3004
  // src/components/Pipeline.tsx
2961
- var import_react33 = require("react");
3005
+ var import_react34 = require("react");
2962
3006
  var import_jsx_runtime51 = require("react/jsx-runtime");
2963
3007
  function Pipeline({ detailed = false, children, className, tabIndex, ...props }) {
2964
- const stages = detailed ? import_react33.Children.map(children, (child) => (0, import_react33.isValidElement)(child) && child.type === PipelineStage ? (0, import_react33.cloneElement)(child, { detailed: child.props.detailed ?? true }) : child) : children;
3008
+ const stages = detailed ? import_react34.Children.map(children, (child) => (0, import_react34.isValidElement)(child) && child.type === PipelineStage ? (0, import_react34.cloneElement)(child, { detailed: child.props.detailed ?? true }) : child) : children;
2965
3009
  return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
2966
3010
  "ol",
2967
3011
  {
@@ -2987,7 +3031,7 @@ var DEFAULT_STATE_LABELS = {
2987
3031
  function PipelineStage({
2988
3032
  label,
2989
3033
  state = "pending",
2990
- stateLabel: stateLabel2,
3034
+ stateLabel,
2991
3035
  description,
2992
3036
  meta,
2993
3037
  marker,
@@ -2997,7 +3041,7 @@ function PipelineStage({
2997
3041
  ...props
2998
3042
  }) {
2999
3043
  const current = currentProp ?? (state === "active" || state === "current" || state === "running");
3000
- const resolvedStateLabel = stateLabel2 ?? DEFAULT_STATE_LABELS[state];
3044
+ const resolvedStateLabel = stateLabel ?? DEFAULT_STATE_LABELS[state];
3001
3045
  const showDetails = detailed || description != null || meta != null || marker != null;
3002
3046
  return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3003
3047
  "li",
@@ -3042,7 +3086,7 @@ var FLOW_STATE_LABELS = {
3042
3086
  function FlowNode({
3043
3087
  title,
3044
3088
  state = "pending",
3045
- stateLabel: stateLabel2,
3089
+ stateLabel,
3046
3090
  eyebrow,
3047
3091
  meta,
3048
3092
  icon,
@@ -3051,7 +3095,7 @@ function FlowNode({
3051
3095
  className,
3052
3096
  ...props
3053
3097
  }) {
3054
- const resolvedStateLabel = stateLabel2 ?? FLOW_STATE_LABELS[state];
3098
+ const resolvedStateLabel = stateLabel ?? FLOW_STATE_LABELS[state];
3055
3099
  return /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: cx("uix-node", `uix-node--${state}`, icon == null && "uix-node--no-icon", wide && "uix-node--wide", className), ...props, children: [
3056
3100
  icon != null && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { className: "uix-node__icon", "aria-hidden": "true", children: icon }),
3057
3101
  /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "uix-node__body", children: [
@@ -3077,7 +3121,7 @@ function Note({ tone, icon, children, className, ...props }) {
3077
3121
  }
3078
3122
 
3079
3123
  // src/components/Popover.tsx
3080
- var import_react34 = require("react");
3124
+ var import_react35 = require("react");
3081
3125
  var import_jsx_runtime54 = require("react/jsx-runtime");
3082
3126
  function Popover({
3083
3127
  popover = "auto",
@@ -3088,15 +3132,15 @@ function Popover({
3088
3132
  children,
3089
3133
  ...props
3090
3134
  }) {
3091
- const ref = (0, import_react34.useRef)(null);
3092
- const [open, setOpen] = (0, import_react34.useState)(false);
3135
+ const ref = (0, import_react35.useRef)(null);
3136
+ const [open, setOpen] = (0, import_react35.useState)(false);
3093
3137
  const popoverAttr = { popover };
3094
3138
  const reposition = useAnchoredPosition(anchor ?? null, ref, {
3095
3139
  open: open && !!anchor,
3096
3140
  placement,
3097
3141
  offset
3098
3142
  });
3099
- (0, import_react34.useEffect)(() => {
3143
+ (0, import_react35.useEffect)(() => {
3100
3144
  const el = ref.current;
3101
3145
  if (!el || !anchor) return;
3102
3146
  const sync = () => {
@@ -3111,26 +3155,26 @@ function Popover({
3111
3155
  }
3112
3156
 
3113
3157
  // src/components/CommandPalette.tsx
3114
- var import_react35 = require("react");
3158
+ var import_react36 = require("react");
3115
3159
  var import_jsx_runtime55 = require("react/jsx-runtime");
3116
- var CmdkContext = (0, import_react35.createContext)(null);
3117
- function CommandPalette({ inputProps, children, className, ...props }) {
3118
- const listId = (0, import_react35.useId)();
3119
- const [activeId, setActiveId] = (0, import_react35.useState)(null);
3120
- const [count, setCount] = (0, import_react35.useState)(0);
3121
- const [announced, setAnnounced] = (0, import_react35.useState)("");
3122
- const setActive = (0, import_react35.useCallback)((id, active) => {
3160
+ var CmdkContext = (0, import_react36.createContext)(null);
3161
+ function CommandPalette({ inputProps, children, inputLabel = "Command palette", className, ...props }) {
3162
+ const listId = (0, import_react36.useId)();
3163
+ const [activeId, setActiveId] = (0, import_react36.useState)(null);
3164
+ const [count, setCount] = (0, import_react36.useState)(0);
3165
+ const [announced, setAnnounced] = (0, import_react36.useState)("");
3166
+ const setActive = (0, import_react36.useCallback)((id, active) => {
3123
3167
  setActiveId((cur) => active ? id : cur === id ? null : cur);
3124
3168
  }, []);
3125
- const registerItem = (0, import_react35.useCallback)(() => {
3169
+ const registerItem = (0, import_react36.useCallback)(() => {
3126
3170
  setCount((c) => c + 1);
3127
3171
  return () => setCount((c) => c - 1);
3128
3172
  }, []);
3129
- (0, import_react35.useEffect)(() => {
3173
+ (0, import_react36.useEffect)(() => {
3130
3174
  const t = window.setTimeout(() => setAnnounced(`${count} result${count === 1 ? "" : "s"}`), 150);
3131
3175
  return () => clearTimeout(t);
3132
3176
  }, [count]);
3133
- const ctx = (0, import_react35.useMemo)(() => ({ listId, setActive, registerItem }), [listId, setActive, registerItem]);
3177
+ const ctx = (0, import_react36.useMemo)(() => ({ listId, setActive, registerItem }), [listId, setActive, registerItem]);
3134
3178
  return /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(CmdkContext.Provider, { value: ctx, children: /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: cx("uix-cmdk", className), ...props, children: [
3135
3179
  /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
3136
3180
  "input",
@@ -3141,7 +3185,7 @@ function CommandPalette({ inputProps, children, className, ...props }) {
3141
3185
  "aria-autocomplete": "list",
3142
3186
  "aria-controls": listId,
3143
3187
  "aria-activedescendant": activeId ?? void 0,
3144
- "aria-label": "Command palette",
3188
+ "aria-label": inputLabel,
3145
3189
  ...inputProps
3146
3190
  }
3147
3191
  ),
@@ -3150,18 +3194,18 @@ function CommandPalette({ inputProps, children, className, ...props }) {
3150
3194
  ] }) });
3151
3195
  }
3152
3196
  function CommandGroup({ label, children }) {
3153
- const labelId = (0, import_react35.useId)();
3197
+ const labelId = (0, import_react36.useId)();
3154
3198
  return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { role: "group", "aria-labelledby": label != null ? labelId : void 0, children: [
3155
3199
  label != null && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("div", { className: "uix-cmdk__group", id: labelId, children: label }),
3156
3200
  children
3157
3201
  ] });
3158
3202
  }
3159
3203
  function CommandItem({ icon, shortcut, active, children, className, id, ...props }) {
3160
- const ctx = (0, import_react35.useContext)(CmdkContext);
3161
- const autoId = (0, import_react35.useId)();
3204
+ const ctx = (0, import_react36.useContext)(CmdkContext);
3205
+ const autoId = (0, import_react36.useId)();
3162
3206
  const itemId = id ?? autoId;
3163
- (0, import_react35.useEffect)(() => ctx?.registerItem(), [ctx]);
3164
- (0, import_react35.useEffect)(() => {
3207
+ (0, import_react36.useEffect)(() => ctx?.registerItem(), [ctx]);
3208
+ (0, import_react36.useEffect)(() => {
3165
3209
  if (!ctx || !active) return;
3166
3210
  ctx.setActive(itemId, true);
3167
3211
  return () => ctx.setActive(itemId, false);
@@ -3185,16 +3229,16 @@ function CommandItem({ icon, shortcut, active, children, className, id, ...props
3185
3229
  }
3186
3230
 
3187
3231
  // src/components/Inbox.tsx
3188
- var import_react36 = require("react");
3232
+ var import_react37 = require("react");
3189
3233
  var import_jsx_runtime56 = require("react/jsx-runtime");
3190
3234
  function Inbox({ view, children, className, ...props }) {
3191
3235
  return /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("div", { className: cx("uix-inbox", className), "data-view": view, ...props, children });
3192
3236
  }
3193
- var InboxListCtx = (0, import_react36.createContext)({});
3194
- function InboxList({ children, className, ...props }) {
3195
- const listRef = (0, import_react36.useRef)(null);
3196
- const [activeId, setActiveId] = (0, import_react36.useState)(void 0);
3197
- const ctx = (0, import_react36.useMemo)(() => ({ activeId }), [activeId]);
3237
+ var InboxListCtx = (0, import_react37.createContext)({});
3238
+ function InboxList({ children, listLabel = "Inbox", className, ...props }) {
3239
+ const listRef = (0, import_react37.useRef)(null);
3240
+ const [activeId, setActiveId] = (0, import_react37.useState)(void 0);
3241
+ const ctx = (0, import_react37.useMemo)(() => ({ activeId }), [activeId]);
3198
3242
  const onKeyDown = (e) => {
3199
3243
  const opts = Array.from(listRef.current?.querySelectorAll('[role="option"]') ?? []);
3200
3244
  if (opts.length === 0) return;
@@ -3236,7 +3280,7 @@ function InboxList({ children, className, ...props }) {
3236
3280
  className: cx("uix-inbox__list", className),
3237
3281
  role: "listbox",
3238
3282
  tabIndex: 0,
3239
- "aria-label": "Inbox",
3283
+ "aria-label": listLabel,
3240
3284
  "aria-activedescendant": activeId,
3241
3285
  onKeyDown,
3242
3286
  ...props,
@@ -3255,9 +3299,9 @@ function InboxItem({
3255
3299
  id,
3256
3300
  ...props
3257
3301
  }) {
3258
- const autoId = (0, import_react36.useId)();
3302
+ const autoId = (0, import_react37.useId)();
3259
3303
  const itemId = id ?? autoId;
3260
- const { activeId } = (0, import_react36.useContext)(InboxListCtx);
3304
+ const { activeId } = (0, import_react37.useContext)(InboxListCtx);
3261
3305
  return /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(
3262
3306
  "div",
3263
3307
  {
@@ -3282,7 +3326,7 @@ function InboxDetail({ children, className, ...props }) {
3282
3326
  }
3283
3327
 
3284
3328
  // src/components/Kanban.tsx
3285
- var import_react37 = require("react");
3329
+ var import_react38 = require("react");
3286
3330
  var import_jsx_runtime57 = require("react/jsx-runtime");
3287
3331
  function Kanban({ announcement, children, className, ...props }) {
3288
3332
  return /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("div", { className: cx("uix-kanban", className), ...props, children: [
@@ -3291,7 +3335,7 @@ function Kanban({ announcement, children, className, ...props }) {
3291
3335
  ] });
3292
3336
  }
3293
3337
  function KanbanColumn({ title, count, children, className, ...props }) {
3294
- const headId = (0, import_react37.useId)();
3338
+ const headId = (0, import_react38.useId)();
3295
3339
  const hasHead = title != null || count != null;
3296
3340
  return /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("div", { className: cx("uix-kanban__col", className), ...props, children: [
3297
3341
  hasHead && /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("div", { className: "uix-kanban__head", id: headId, children: [
@@ -3335,7 +3379,7 @@ function KanbanCard({ title, meta, children, className, onMove, onKeyDown, ...pr
3335
3379
  }
3336
3380
 
3337
3381
  // src/components/Tree.tsx
3338
- var import_react38 = require("react");
3382
+ var import_react39 = require("react");
3339
3383
 
3340
3384
  // src/tree-model.ts
3341
3385
  function flattenTree(nodes, expanded, level = 1) {
@@ -3395,7 +3439,7 @@ function treeNav(flat, currentId, key) {
3395
3439
  // src/components/Tree.tsx
3396
3440
  var import_jsx_runtime58 = require("react/jsx-runtime");
3397
3441
  var ChevronIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("svg", { viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("path", { d: "M2 4l4 4 4-4" }) });
3398
- function TreeNode({ node, expanded, selected, selectable, tabbableId, onToggle, onSelect, onFocusNode, level = 1 }) {
3442
+ function TreeNode({ node, expanded, selected, selectable, tabbableId, onToggle, onSelect, onFocusNode, level = 1, rowToggles = true }) {
3399
3443
  const hasChildren = Array.isArray(node.children) && node.children.length > 0;
3400
3444
  const isExpanded = expanded.has(node.id);
3401
3445
  return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
@@ -3413,13 +3457,32 @@ function TreeNode({ node, expanded, selected, selectable, tabbableId, onToggle,
3413
3457
  },
3414
3458
  onClick: (e) => {
3415
3459
  e.stopPropagation();
3416
- if (hasChildren) onToggle(node.id);
3460
+ if (hasChildren && rowToggles) onToggle(node.id);
3417
3461
  onSelect?.(node.id);
3418
3462
  onFocusNode(node.id);
3419
3463
  },
3420
3464
  children: [
3421
3465
  /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("span", { className: "uix-tree__row", children: [
3422
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "uix-tree__toggle", children: hasChildren && /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(ChevronIcon2, {}) }),
3466
+ hasChildren ? (
3467
+ // TENSOR RX-125 (UIX-09): the chevron is its own pointer target, so a
3468
+ // branch can be expanded without selecting it. Keyboard users toggle with
3469
+ // the arrow keys on the treeitem, so it stays out of the tab order.
3470
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
3471
+ "button",
3472
+ {
3473
+ type: "button",
3474
+ tabIndex: -1,
3475
+ "aria-hidden": "true",
3476
+ className: "uix-tree__toggle",
3477
+ onClick: (e) => {
3478
+ e.stopPropagation();
3479
+ onToggle(node.id);
3480
+ onFocusNode(node.id);
3481
+ },
3482
+ children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(ChevronIcon2, {})
3483
+ }
3484
+ )
3485
+ ) : /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "uix-tree__toggle" }),
3423
3486
  node.icon && /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { "aria-hidden": "true", children: node.icon }),
3424
3487
  /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "uix-tree__label", children: node.label })
3425
3488
  ] }),
@@ -3434,7 +3497,8 @@ function TreeNode({ node, expanded, selected, selectable, tabbableId, onToggle,
3434
3497
  onToggle,
3435
3498
  onSelect,
3436
3499
  onFocusNode,
3437
- level: level + 1
3500
+ level: level + 1,
3501
+ rowToggles
3438
3502
  },
3439
3503
  child.id
3440
3504
  )) })
@@ -3452,15 +3516,17 @@ function Tree({
3452
3516
  virtualize,
3453
3517
  rowHeight = 32,
3454
3518
  maxHeight = 384,
3519
+ toggleOnRowClick,
3455
3520
  className,
3456
3521
  ...props
3457
3522
  }) {
3458
- const [internalExpanded, setInternalExpanded] = (0, import_react38.useState)(defaultExpanded ?? /* @__PURE__ */ new Set());
3523
+ const [internalExpanded, setInternalExpanded] = (0, import_react39.useState)(defaultExpanded ?? /* @__PURE__ */ new Set());
3459
3524
  const expanded = controlledExpanded ?? internalExpanded;
3460
- const rootRef = (0, import_react38.useRef)(null);
3461
- const [focusedId, setFocusedId] = (0, import_react38.useState)(void 0);
3525
+ const rootRef = (0, import_react39.useRef)(null);
3526
+ const [focusedId, setFocusedId] = (0, import_react39.useState)(void 0);
3462
3527
  const selectable = selected !== void 0 || onSelect != null;
3463
- const handleToggle = (0, import_react38.useCallback)((id) => {
3528
+ const rowToggles = toggleOnRowClick ?? !selectable;
3529
+ const handleToggle = (0, import_react39.useCallback)((id) => {
3464
3530
  if (!controlledExpanded) {
3465
3531
  setInternalExpanded((prev) => {
3466
3532
  const next = new Set(prev);
@@ -3471,7 +3537,7 @@ function Tree({
3471
3537
  }
3472
3538
  onToggle?.(id);
3473
3539
  }, [controlledExpanded, onToggle]);
3474
- const flat = (0, import_react38.useMemo)(() => flattenTree(nodes, expanded), [nodes, expanded]);
3540
+ const flat = (0, import_react39.useMemo)(() => flattenTree(nodes, expanded), [nodes, expanded]);
3475
3541
  const useVirtual = virtualize ?? shouldVirtualize(flat.length);
3476
3542
  const tabbableId = focusedId ?? selected ?? nodes[0]?.id;
3477
3543
  if (useVirtual) {
@@ -3488,6 +3554,7 @@ function Tree({
3488
3554
  onSelect,
3489
3555
  focusedId,
3490
3556
  setFocusedId,
3557
+ rowToggles,
3491
3558
  className,
3492
3559
  ...props
3493
3560
  }
@@ -3563,7 +3630,8 @@ function Tree({
3563
3630
  onToggle: handleToggle,
3564
3631
  onSelect,
3565
3632
  onFocusNode: setFocusedId,
3566
- level: 1
3633
+ level: 1,
3634
+ rowToggles
3567
3635
  },
3568
3636
  node.id
3569
3637
  )) });
@@ -3579,17 +3647,18 @@ function VirtualTreeView({
3579
3647
  onSelect,
3580
3648
  focusedId,
3581
3649
  setFocusedId,
3650
+ rowToggles,
3582
3651
  className,
3583
3652
  ...props
3584
3653
  }) {
3585
- const scrollRef = (0, import_react38.useRef)(null);
3586
- const [scrollTop, setScrollTop] = (0, import_react38.useState)(0);
3587
- const pendingFocus = (0, import_react38.useRef)(false);
3588
- const hadFocus = (0, import_react38.useRef)(false);
3654
+ const scrollRef = (0, import_react39.useRef)(null);
3655
+ const [scrollTop, setScrollTop] = (0, import_react39.useState)(0);
3656
+ const pendingFocus = (0, import_react39.useRef)(false);
3657
+ const hadFocus = (0, import_react39.useRef)(false);
3589
3658
  const win = virtualWindow(scrollTop, maxHeight, rowHeight, flat.length);
3590
3659
  const rows = flat.slice(win.start, win.end);
3591
3660
  const windowHasTabbable = tabbableId != null && rows.some((f) => f.node.id === tabbableId);
3592
- (0, import_react38.useEffect)(() => {
3661
+ (0, import_react39.useEffect)(() => {
3593
3662
  if (!pendingFocus.current || focusedId == null) return;
3594
3663
  const el = scrollRef.current?.querySelector(`[role="treeitem"][data-id="${CSS.escape(focusedId)}"]`);
3595
3664
  if (el) {
@@ -3597,7 +3666,7 @@ function VirtualTreeView({
3597
3666
  pendingFocus.current = false;
3598
3667
  }
3599
3668
  }, [focusedId, win.start, win.end]);
3600
- (0, import_react38.useEffect)(() => {
3669
+ (0, import_react39.useEffect)(() => {
3601
3670
  if (!hadFocus.current || pendingFocus.current) return;
3602
3671
  const active = document.activeElement;
3603
3672
  if (active === document.body || active == null) scrollRef.current?.focus({ preventScroll: true });
@@ -3652,7 +3721,10 @@ function VirtualTreeView({
3652
3721
  children: [
3653
3722
  win.padTop > 0 && /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("li", { role: "presentation", "aria-hidden": "true", style: { height: win.padTop } }),
3654
3723
  rows.map((f, i) => {
3655
- const rowStyle = { height: rowHeight, paddingLeft: `calc(${f.level - 1} * var(--uix-space-5))` };
3724
+ const rowStyle = {
3725
+ height: rowHeight,
3726
+ paddingLeft: `calc(${Math.min(f.level - 1, 4)} * var(--uix-space-5) + ${Math.max(0, f.level - 5)} * var(--uix-space-1))`
3727
+ };
3656
3728
  return /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
3657
3729
  "li",
3658
3730
  {
@@ -3670,12 +3742,31 @@ function VirtualTreeView({
3670
3742
  },
3671
3743
  onClick: (e) => {
3672
3744
  e.stopPropagation();
3673
- if (f.hasChildren) onToggle(f.node.id);
3745
+ if (f.hasChildren && rowToggles) onToggle(f.node.id);
3674
3746
  onSelect?.(f.node.id);
3675
3747
  setFocusedId(f.node.id);
3676
3748
  },
3677
3749
  children: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("span", { className: "uix-tree__row", style: rowStyle, children: [
3678
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "uix-tree__toggle", children: f.hasChildren && /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(ChevronIcon2, {}) }),
3750
+ f.hasChildren ? (
3751
+ // TENSOR RX-125 (UIX-09): the chevron is its own pointer target, so a
3752
+ // branch can be expanded without selecting it. Keyboard users toggle with
3753
+ // the arrow keys on the treeitem, so it stays out of the tab order.
3754
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
3755
+ "button",
3756
+ {
3757
+ type: "button",
3758
+ tabIndex: -1,
3759
+ "aria-hidden": "true",
3760
+ className: "uix-tree__toggle",
3761
+ onClick: (e) => {
3762
+ e.stopPropagation();
3763
+ onToggle(f.node.id);
3764
+ setFocusedId(f.node.id);
3765
+ },
3766
+ children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(ChevronIcon2, {})
3767
+ }
3768
+ )
3769
+ ) : /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "uix-tree__toggle" }),
3679
3770
  f.node.icon && /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { "aria-hidden": "true", children: f.node.icon }),
3680
3771
  /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "uix-tree__label", children: f.node.label })
3681
3772
  ] })
@@ -3950,8 +4041,47 @@ function summarizeRule(value, labels = {}) {
3950
4041
  return `When ${formatGroup(value.when) || "no conditions"}, then ${actions || "no actions"}.`;
3951
4042
  }
3952
4043
 
4044
+ // src/fill-label.ts
4045
+ function fillLabel(template, values) {
4046
+ return template.replace(
4047
+ /\{(\w+)\}/g,
4048
+ (match, key) => Object.prototype.hasOwnProperty.call(values, key) ? String(values[key]) : match
4049
+ );
4050
+ }
4051
+
3953
4052
  // src/components/RuleBuilder.tsx
3954
4053
  var import_jsx_runtime60 = require("react/jsx-runtime");
4054
+ var DEFAULT_RULE_BUILDER_LABELS = {
4055
+ region: "Rule builder",
4056
+ summary: "Rule summary",
4057
+ when: "When",
4058
+ group: "Group",
4059
+ match: "Match",
4060
+ all: "all (AND)",
4061
+ any: "any (OR)",
4062
+ field: "Field",
4063
+ operator: "Operator",
4064
+ valueFor: "Value for {field}",
4065
+ itemGroup: "group",
4066
+ itemCondition: "condition",
4067
+ itemAction: "action",
4068
+ moveUp: "Up",
4069
+ moveUpNamed: "Move {item} up",
4070
+ moveDown: "Down",
4071
+ moveDownNamed: "Move {item} down",
4072
+ remove: "Remove",
4073
+ removeNamed: "Remove {item}",
4074
+ then: "Then",
4075
+ actionType: "Action type",
4076
+ valid: "Rule is valid.",
4077
+ issuesOne: "{count} validation issue",
4078
+ issuesMany: "{count} validation issues",
4079
+ summaryIssuesOne: "Rule has {count} validation issue.",
4080
+ summaryIssuesMany: "Rule has {count} validation issues.",
4081
+ addCondition: "Add condition",
4082
+ addGroup: "Add group",
4083
+ addAction: "Add action"
4084
+ };
3955
4085
  var ruleId = 0;
3956
4086
  var nextId = (prefix) => `${prefix}-${Date.now().toString(36)}-${(++ruleId).toString(36)}`;
3957
4087
  function defaultCondition(fields, operators) {
@@ -3969,27 +4099,28 @@ function RuleGroupEditor({ group, depth, root, fields, operators, maxDepth, disa
3969
4099
  }, maxDepth));
3970
4100
  return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("fieldset", { className: "uix-rule-builder__group", "data-depth": depth, children: [
3971
4101
  /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("legend", { className: "uix-rule-builder__group-heading", children: [
3972
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { children: depth === 1 ? "When" : "Group" }),
4102
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { children: depth === 1 ? labels.when : labels.group }),
3973
4103
  /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("label", { children: [
3974
- "Match",
4104
+ labels.match,
3975
4105
  /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("select", { className: "uix-select uix-select--sm", value: group.combinator, disabled, onChange: (event) => updateNode(group.id, (node) => ({ ...node, combinator: event.currentTarget.value })), children: [
3976
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("option", { value: "and", children: "all (AND)" }),
3977
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("option", { value: "or", children: "any (OR)" })
4106
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("option", { value: "and", children: labels.all }),
4107
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("option", { value: "or", children: labels.any })
3978
4108
  ] })
3979
4109
  ] })
3980
4110
  ] }),
3981
4111
  /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { className: "uix-rule-builder__rows", children: group.conditions.map((node, index) => isRuleGroup(node) ? /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "uix-rule-builder__nested", children: [
3982
4112
  /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(RuleGroupEditor, { ...{ group: node, depth: depth + 1, root, fields, operators, maxDepth, disabled, onRootChange, labels } }),
3983
- /* @__PURE__ */ (0, import_jsx_runtime60.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)) })
4113
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(RowControls, { labels, label: labels.itemGroup, 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)) })
3984
4114
  ] }, node.id) : /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
3985
4115
  ConditionRow,
3986
4116
  {
4117
+ labels,
3987
4118
  condition: node,
3988
4119
  fields,
3989
4120
  operators,
3990
4121
  disabled,
3991
4122
  onChange: (condition) => updateNode(node.id, () => condition),
3992
- controls: /* @__PURE__ */ (0, import_jsx_runtime60.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)) })
4123
+ controls: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(RowControls, { labels, label: labels.itemCondition, 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)) })
3993
4124
  },
3994
4125
  node.id
3995
4126
  )) }),
@@ -3999,7 +4130,7 @@ function RuleGroupEditor({ group, depth, root, fields, operators, maxDepth, disa
3999
4130
  ] })
4000
4131
  ] });
4001
4132
  }
4002
- function ConditionRow({ condition, fields, operators, disabled, onChange, controls }) {
4133
+ function ConditionRow({ labels, condition, fields, operators, disabled, onChange, controls }) {
4003
4134
  const field = fields.find((item) => item.id === condition.field);
4004
4135
  const availableOperators = field?.operators?.length ? operators.filter((item) => field.operators.includes(item.id)) : operators;
4005
4136
  const operator = operators.find((item) => item.id === condition.operator);
@@ -4010,22 +4141,22 @@ function ConditionRow({ condition, fields, operators, disabled, onChange, contro
4010
4141
  };
4011
4142
  return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "uix-rule-builder__condition", children: [
4012
4143
  /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("label", { children: [
4013
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "uix-visually-hidden", children: "Field" }),
4144
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "uix-visually-hidden", children: labels.field }),
4014
4145
  /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("select", { className: "uix-select", value: condition.field, onChange: (event) => setField(event.currentTarget.value), disabled, children: fields.map((item) => /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("option", { value: item.id, children: item.label }, item.id)) })
4015
4146
  ] }),
4016
4147
  /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("label", { children: [
4017
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "uix-visually-hidden", children: "Operator" }),
4148
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "uix-visually-hidden", children: labels.operator }),
4018
4149
  /* @__PURE__ */ (0, import_jsx_runtime60.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_runtime60.jsx)("option", { value: item.id, children: item.label }, item.id)) })
4019
4150
  ] }),
4020
- operator?.requiresValue !== false && /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { className: "uix-rule-builder__value", children: field?.renderValueEditor?.({ condition, onChange: (value) => onChange({ ...condition, value }), disabled }) ?? /* @__PURE__ */ (0, import_jsx_runtime60.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"}` }) }),
4151
+ operator?.requiresValue !== false && /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { className: "uix-rule-builder__value", children: field?.renderValueEditor?.({ condition, onChange: (value) => onChange({ ...condition, value }), disabled }) ?? /* @__PURE__ */ (0, import_jsx_runtime60.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": fillLabel(labels.valueFor, { field: field?.label ?? labels.itemCondition }) }) }),
4021
4152
  controls
4022
4153
  ] });
4023
4154
  }
4024
- function RowControls({ label, first, last, disabled, onUp, onDown, onRemove }) {
4155
+ function RowControls({ labels, label, first, last, disabled, onUp, onDown, onRemove }) {
4025
4156
  return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("span", { className: "uix-rule-builder__row-actions", children: [
4026
- /* @__PURE__ */ (0, import_jsx_runtime60.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" }),
4027
- /* @__PURE__ */ (0, import_jsx_runtime60.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" }),
4028
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: onRemove, disabled, "aria-label": `Remove ${label}`, children: "Remove" })
4157
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: onUp, disabled: disabled || first, "aria-label": fillLabel(labels.moveUpNamed, { item: label }), children: labels.moveUp }),
4158
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: onDown, disabled: disabled || last, "aria-label": fillLabel(labels.moveDownNamed, { item: label }), children: labels.moveDown }),
4159
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: onRemove, disabled, "aria-label": fillLabel(labels.removeNamed, { item: label }), children: labels.remove })
4029
4160
  ] });
4030
4161
  }
4031
4162
  function RuleBuilder({
@@ -4038,38 +4169,42 @@ function RuleBuilder({
4038
4169
  readOnly = false,
4039
4170
  validate,
4040
4171
  className,
4041
- addConditionLabel = "Add condition",
4042
- addGroupLabel = "Add group",
4043
- addActionLabel = "Add action"
4172
+ addConditionLabel,
4173
+ addGroupLabel,
4174
+ addActionLabel,
4175
+ labels: labelOverrides
4044
4176
  }) {
4177
+ const labels = {
4178
+ ...DEFAULT_RULE_BUILDER_LABELS,
4179
+ ...addConditionLabel != null ? { addCondition: addConditionLabel } : {},
4180
+ ...addGroupLabel != null ? { addGroup: addGroupLabel } : {},
4181
+ ...addActionLabel != null ? { addAction: addActionLabel } : {},
4182
+ ...labelOverrides
4183
+ };
4184
+ const issuesText = (template1, templateN) => fillLabel(issues.length === 1 ? template1 : templateN, { count: issues.length });
4045
4185
  const issues = [...validateRuleDefinition(value, maxDepth), ...validate?.(value) ?? []];
4046
4186
  const fieldLabels = Object.fromEntries(fields.map((field) => [field.id, field.label]));
4047
4187
  const operatorLabels = Object.fromEntries(operators.map((operator) => [operator.id, operator.label]));
4048
4188
  const actionLabels = Object.fromEntries(actions.map((action) => [action.id, action.label]));
4049
- if (readOnly) return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("section", { className: cx("uix-rule-builder uix-rule-builder--summary", className), "aria-label": "Rule summary", children: [
4189
+ if (readOnly) return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("section", { className: cx("uix-rule-builder uix-rule-builder--summary", className), "aria-label": labels.summary, children: [
4050
4190
  /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("p", { children: summarizeRule(value, { fields: fieldLabels, operators: operatorLabels, actions: actionLabels }) }),
4051
- issues.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("p", { className: "uix-rule-builder__invalid", children: [
4052
- "Rule has ",
4053
- issues.length,
4054
- " validation issue",
4055
- issues.length === 1 ? "" : "s",
4056
- "."
4057
- ] })
4191
+ issues.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("p", { className: "uix-rule-builder__invalid", children: issuesText(labels.summaryIssuesOne, labels.summaryIssuesMany) })
4058
4192
  ] });
4059
4193
  const updateActions = (next) => onChange({ ...value, then: next });
4060
- return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("section", { className: cx("uix-rule-builder", className), "aria-label": "Rule builder", children: [
4061
- /* @__PURE__ */ (0, import_jsx_runtime60.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 } }),
4194
+ return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("section", { className: cx("uix-rule-builder", className), "aria-label": labels.region, children: [
4195
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(RuleGroupEditor, { group: value.when, depth: 1, root: value.when, fields, operators, maxDepth, disabled: readOnly, onRootChange: (when) => onChange({ ...value, when }), labels }),
4062
4196
  /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("fieldset", { className: "uix-rule-builder__actions", children: [
4063
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("legend", { children: "Then" }),
4197
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("legend", { children: labels.then }),
4064
4198
  value.then.map((action, index) => {
4065
4199
  const definition = actions.find((item) => item.id === action.type);
4066
4200
  return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "uix-rule-builder__action", children: [
4067
- /* @__PURE__ */ (0, import_jsx_runtime60.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_runtime60.jsx)("option", { value: item.id, children: item.label }, item.id)) }),
4201
+ /* @__PURE__ */ (0, import_jsx_runtime60.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": labels.actionType, children: actions.map((item) => /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("option", { value: item.id, children: item.label }, item.id)) }),
4068
4202
  definition?.renderParameters?.(action, (parameters) => updateActions(value.then.map((item) => item.id === action.id ? { ...item, parameters } : item)), false),
4069
4203
  /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
4070
4204
  RowControls,
4071
4205
  {
4072
- label: "action",
4206
+ labels,
4207
+ label: labels.itemAction,
4073
4208
  first: index === 0,
4074
4209
  last: index === value.then.length - 1,
4075
4210
  disabled: false,
@@ -4088,22 +4223,40 @@ function RuleBuilder({
4088
4223
  )
4089
4224
  ] }, action.id);
4090
4225
  }),
4091
- /* @__PURE__ */ (0, import_jsx_runtime60.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 })
4226
+ /* @__PURE__ */ (0, import_jsx_runtime60.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: labels.addAction })
4092
4227
  ] }),
4093
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { className: "uix-rule-builder__validation", "aria-live": "polite", children: issues.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { children: "Rule is valid." }) : /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(import_jsx_runtime60.Fragment, { children: [
4094
- /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("strong", { children: [
4095
- issues.length,
4096
- " validation issue",
4097
- issues.length === 1 ? "" : "s"
4098
- ] }),
4228
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { className: "uix-rule-builder__validation", "aria-live": "polite", children: issues.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { children: labels.valid }) : /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(import_jsx_runtime60.Fragment, { children: [
4229
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("strong", { children: issuesText(labels.issuesOne, labels.issuesMany) }),
4099
4230
  /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("ul", { children: issues.map((issue, index) => /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("li", { children: issue.message }, `${issue.path}-${index}`)) })
4100
4231
  ] }) })
4101
4232
  ] });
4102
4233
  }
4103
4234
 
4104
4235
  // src/components/BuilderCanvas.tsx
4105
- var import_react39 = require("react");
4236
+ var import_react40 = require("react");
4106
4237
  var import_jsx_runtime61 = require("react/jsx-runtime");
4238
+ var DEFAULT_BUILDER_CANVAS_LABELS = {
4239
+ region: "Builder canvas",
4240
+ palette: "Item palette",
4241
+ paletteHeading: "Palette",
4242
+ paletteEmpty: "No item types available.",
4243
+ add: "Add",
4244
+ addFirst: "Add {label}",
4245
+ canvasHeading: "Canvas",
4246
+ loading: "Loading canvas\u2026",
4247
+ retry: "Try again",
4248
+ moveUp: "Up",
4249
+ moveUpNamed: "Move {label} up",
4250
+ moveDown: "Down",
4251
+ moveDownNamed: "Move {label} down",
4252
+ remove: "Remove",
4253
+ removeNamed: "Remove {label}",
4254
+ properties: "Item properties",
4255
+ propertiesHeading: "Properties",
4256
+ propertiesEmpty: "Select a canvas item to edit its properties.",
4257
+ type: "Type",
4258
+ label: "Label"
4259
+ };
4107
4260
  function BuilderCanvas({
4108
4261
  items,
4109
4262
  onItemsChange,
@@ -4116,15 +4269,17 @@ function BuilderCanvas({
4116
4269
  error,
4117
4270
  onRetry,
4118
4271
  emptyMessage = "Add an item from the palette to begin.",
4119
- className
4272
+ className,
4273
+ labels: labelOverrides
4120
4274
  }) {
4121
- const [internalSelectedId, setInternalSelectedId] = (0, import_react39.useState)();
4122
- const [draggedId, setDraggedId] = (0, import_react39.useState)();
4123
- const propertyHeadingRef = (0, import_react39.useRef)(null);
4124
- const shouldFocusProperties = (0, import_react39.useRef)(false);
4275
+ const labels = { ...DEFAULT_BUILDER_CANVAS_LABELS, ...labelOverrides };
4276
+ const [internalSelectedId, setInternalSelectedId] = (0, import_react40.useState)();
4277
+ const [draggedId, setDraggedId] = (0, import_react40.useState)();
4278
+ const propertyHeadingRef = (0, import_react40.useRef)(null);
4279
+ const shouldFocusProperties = (0, import_react40.useRef)(false);
4125
4280
  const selectedId = controlledSelectedId ?? internalSelectedId;
4126
4281
  const selected = items.find((item) => item.id === selectedId);
4127
- (0, import_react39.useEffect)(() => {
4282
+ (0, import_react40.useEffect)(() => {
4128
4283
  if (shouldFocusProperties.current && selected) {
4129
4284
  propertyHeadingRef.current?.focus();
4130
4285
  shouldFocusProperties.current = false;
@@ -4158,26 +4313,23 @@ function BuilderCanvas({
4158
4313
  if (from >= 0 && to >= 0) onItemsChange(reorder(items, from, to));
4159
4314
  setDraggedId(void 0);
4160
4315
  };
4161
- return /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("section", { className: cx("uix-builder-canvas", className), "aria-label": "Builder canvas", children: [
4162
- /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("aside", { className: "uix-builder-canvas__palette", "aria-label": "Item palette", children: [
4163
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("h3", { children: "Palette" }),
4164
- palette.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("p", { children: "No item types available." }) : /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("ul", { children: palette.map((item) => /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("button", { type: "button", onClick: () => add(item), children: [
4316
+ return /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("section", { className: cx("uix-builder-canvas", className), "aria-label": labels.region, children: [
4317
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("aside", { className: "uix-builder-canvas__palette", "aria-label": labels.palette, children: [
4318
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("h3", { children: labels.paletteHeading }),
4319
+ palette.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("p", { children: labels.paletteEmpty }) : /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("ul", { children: palette.map((item) => /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("button", { type: "button", onClick: () => add(item), children: [
4165
4320
  /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("strong", { children: item.label }),
4166
4321
  item.description && /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { children: item.description }),
4167
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { "aria-hidden": "true", children: "Add" })
4322
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { "aria-hidden": "true", children: labels.add })
4168
4323
  ] }) }, item.id)) })
4169
4324
  ] }),
4170
4325
  /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "uix-builder-canvas__workspace", children: [
4171
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("h3", { children: "Canvas" }),
4172
- loading ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("div", { className: "uix-builder-canvas__state", role: "status", children: "Loading canvas\u2026" }) : error ? /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "uix-builder-canvas__state", role: "alert", children: [
4326
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("h3", { children: labels.canvasHeading }),
4327
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("div", { className: "uix-builder-canvas__state", role: "status", children: labels.loading }) : error ? /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "uix-builder-canvas__state", role: "alert", children: [
4173
4328
  /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("p", { children: error }),
4174
- onRetry && /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary", onClick: onRetry, children: "Try again" })
4329
+ onRetry && /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary", onClick: onRetry, children: labels.retry })
4175
4330
  ] }) : items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "uix-builder-canvas__state", children: [
4176
4331
  /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("p", { children: emptyMessage }),
4177
- palette[0] && /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("button", { type: "button", className: "uix-btn uix-btn--primary", onClick: () => add(palette[0]), children: [
4178
- "Add ",
4179
- palette[0].label
4180
- ] })
4332
+ palette[0] && /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("button", { type: "button", className: "uix-btn uix-btn--primary", onClick: () => add(palette[0]), children: fillLabel(labels.addFirst, { label: palette[0].label }) })
4181
4333
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("ol", { className: "uix-builder-canvas__items", children: items.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(
4182
4334
  "li",
4183
4335
  {
@@ -4193,21 +4345,21 @@ function BuilderCanvas({
4193
4345
  item.description && /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { children: item.description })
4194
4346
  ] }) }),
4195
4347
  /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("span", { className: "uix-builder-canvas__item-actions", children: [
4196
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("button", { type: "button", onClick: () => move(item.id, -1), disabled: index === 0, "aria-label": `Move ${item.label} up`, children: "Up" }),
4197
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("button", { type: "button", onClick: () => move(item.id, 1), disabled: index === items.length - 1, "aria-label": `Move ${item.label} down`, children: "Down" }),
4198
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("button", { type: "button", onClick: () => remove(item.id), "aria-label": `Remove ${item.label}`, children: "Remove" })
4348
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("button", { type: "button", onClick: () => move(item.id, -1), disabled: index === 0, "aria-label": fillLabel(labels.moveUpNamed, { label: item.label }), children: labels.moveUp }),
4349
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("button", { type: "button", onClick: () => move(item.id, 1), disabled: index === items.length - 1, "aria-label": fillLabel(labels.moveDownNamed, { label: item.label }), children: labels.moveDown }),
4350
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("button", { type: "button", onClick: () => remove(item.id), "aria-label": fillLabel(labels.removeNamed, { label: item.label }), children: labels.remove })
4199
4351
  ] })
4200
4352
  ]
4201
4353
  },
4202
4354
  item.id
4203
4355
  )) })
4204
4356
  ] }),
4205
- /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("aside", { className: "uix-builder-canvas__properties", "aria-label": "Item properties", children: [
4206
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("h3", { ref: propertyHeadingRef, tabIndex: -1, children: "Properties" }),
4207
- !selected ? /* @__PURE__ */ (0, import_jsx_runtime61.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_runtime61.jsxs)("dl", { children: [
4208
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("dt", { children: "Type" }),
4357
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("aside", { className: "uix-builder-canvas__properties", "aria-label": labels.properties, children: [
4358
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("h3", { ref: propertyHeadingRef, tabIndex: -1, children: labels.propertiesHeading }),
4359
+ !selected ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("p", { children: labels.propertiesEmpty }) : renderProperties?.(selected, (updated) => onItemsChange(items.map((item) => item.id === updated.id ? updated : item))) ?? /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("dl", { children: [
4360
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("dt", { children: labels.type }),
4209
4361
  /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("dd", { children: selected.type }),
4210
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("dt", { children: "Label" }),
4362
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("dt", { children: labels.label }),
4211
4363
  /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("dd", { children: selected.label })
4212
4364
  ] })
4213
4365
  ] })
@@ -4215,7 +4367,7 @@ function BuilderCanvas({
4215
4367
  }
4216
4368
 
4217
4369
  // src/components/SchedulingCalendar.tsx
4218
- var import_react40 = require("react");
4370
+ var import_react41 = require("react");
4219
4371
 
4220
4372
  // src/calendar-model.ts
4221
4373
  function fromDateKey(value) {
@@ -4297,7 +4449,23 @@ function isDateUnavailable(date, options) {
4297
4449
 
4298
4450
  // src/components/SchedulingCalendar.tsx
4299
4451
  var import_jsx_runtime62 = require("react/jsx-runtime");
4300
- var stateLabel = { scheduled: "Scheduled", conflicted: "Conflicted", "in-progress": "In progress", "blackout-violation": "Blackout violation" };
4452
+ var DEFAULT_SCHEDULING_CALENDAR_LABELS = {
4453
+ region: "Scheduling calendar in {timeZone}",
4454
+ previous: "Previous",
4455
+ next: "Next",
4456
+ viewGroup: "Calendar view",
4457
+ viewMonth: "Month",
4458
+ viewWeek: "Week",
4459
+ viewAgenda: "Agenda",
4460
+ loading: "Loading schedule\u2026",
4461
+ retry: "Try again",
4462
+ agenda: "Schedule agenda",
4463
+ agendaEmpty: "No scheduled entries match the current filters.",
4464
+ grid: "{view} schedule",
4465
+ entry: "{title}, {state}, {start} to {end}",
4466
+ legend: "Schedule state legend",
4467
+ states: { scheduled: "Scheduled", conflicted: "Conflicted", "in-progress": "In progress", "blackout-violation": "Blackout violation" }
4468
+ };
4301
4469
  var stateTone = (state) => state === "scheduled" ? "info" : state === "in-progress" ? "success" : "danger";
4302
4470
  var dateLabel = (date, locale) => new Intl.DateTimeFormat(locale, { dateStyle: "full", timeZone: "UTC" }).format(/* @__PURE__ */ new Date(`${date}T00:00:00Z`));
4303
4471
  var EMPTY_OVERLAYS = [];
@@ -4316,21 +4484,29 @@ function SchedulingCalendar({
4316
4484
  loading,
4317
4485
  error,
4318
4486
  onRetry,
4319
- className
4487
+ className,
4488
+ labels: labelOverrides
4320
4489
  }) {
4321
- const [internalView, setInternalView] = (0, import_react40.useState)("month");
4490
+ const labels = {
4491
+ ...DEFAULT_SCHEDULING_CALENDAR_LABELS,
4492
+ ...labelOverrides,
4493
+ states: { ...DEFAULT_SCHEDULING_CALENDAR_LABELS.states, ...labelOverrides?.states }
4494
+ };
4495
+ const stateLabel = labels.states;
4496
+ const viewLabel = { month: labels.viewMonth, week: labels.viewWeek, agenda: labels.viewAgenda };
4497
+ const [internalView, setInternalView] = (0, import_react41.useState)("month");
4322
4498
  const view = controlledView ?? internalView;
4323
- const [activeDate, setActiveDate] = (0, import_react40.useState)(anchorDate);
4324
- const gridRef = (0, import_react40.useRef)(null);
4325
- const visibleEntries = (0, import_react40.useMemo)(() => entries.filter((entry) => filter?.(entry) ?? true), [entries, filter]);
4326
- const entrySpans = (0, import_react40.useMemo)(() => new Map(visibleEntries.map((entry) => [entry.id, zonedDateSpan(entry.start, entry.end, timeZone)])), [visibleEntries, timeZone]);
4327
- const overlaySpans = (0, import_react40.useMemo)(() => new Map(overlays.map((overlay) => [overlay.id, { start: overlay.start.slice(0, 10), end: overlay.end.slice(0, 10) }])), [overlays]);
4328
- const monthDays = (0, import_react40.useMemo)(() => buildMonthGrid(startOfMonth(anchorDate)), [anchorDate]);
4499
+ const [activeDate, setActiveDate] = (0, import_react41.useState)(anchorDate);
4500
+ const gridRef = (0, import_react41.useRef)(null);
4501
+ const visibleEntries = (0, import_react41.useMemo)(() => entries.filter((entry) => filter?.(entry) ?? true), [entries, filter]);
4502
+ const entrySpans = (0, import_react41.useMemo)(() => new Map(visibleEntries.map((entry) => [entry.id, zonedDateSpan(entry.start, entry.end, timeZone)])), [visibleEntries, timeZone]);
4503
+ const overlaySpans = (0, import_react41.useMemo)(() => new Map(overlays.map((overlay) => [overlay.id, { start: overlay.start.slice(0, 10), end: overlay.end.slice(0, 10) }])), [overlays]);
4504
+ const monthDays = (0, import_react41.useMemo)(() => buildMonthGrid(startOfMonth(anchorDate)), [anchorDate]);
4329
4505
  const weekday = ((/* @__PURE__ */ new Date(`${anchorDate}T00:00:00Z`)).getUTCDay() + 6) % 7;
4330
4506
  const weekStart = addCalendarDays(anchorDate, -weekday);
4331
- const weekDays = (0, import_react40.useMemo)(() => Array.from({ length: 7 }, (_, index) => addCalendarDays(weekStart, index)), [weekStart]);
4332
- const days = (0, import_react40.useMemo)(() => view === "month" ? monthDays.map((day) => day.date) : weekDays, [view, monthDays, weekDays]);
4333
- (0, import_react40.useEffect)(() => {
4507
+ const weekDays = (0, import_react41.useMemo)(() => Array.from({ length: 7 }, (_, index) => addCalendarDays(weekStart, index)), [weekStart]);
4508
+ const days = (0, import_react41.useMemo)(() => view === "month" ? monthDays.map((day) => day.date) : weekDays, [view, monthDays, weekDays]);
4509
+ (0, import_react41.useEffect)(() => {
4334
4510
  if (view !== "agenda" && !days.includes(activeDate)) setActiveDate(days[0]);
4335
4511
  }, [activeDate, days, view]);
4336
4512
  const setView = (next) => {
@@ -4364,22 +4540,19 @@ function SchedulingCalendar({
4364
4540
  return date >= span.start && date <= span.end;
4365
4541
  });
4366
4542
  const formatInstant = (instant) => new Intl.DateTimeFormat(locale, { timeZone, dateStyle: "medium", timeStyle: "short" }).format(new Date(instant));
4367
- return /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("section", { className: cx("uix-scheduling-calendar", className), "aria-label": `Scheduling calendar in ${timeZone}`, children: [
4543
+ return /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("section", { className: cx("uix-scheduling-calendar", className), "aria-label": fillLabel(labels.region, { timeZone }), children: [
4368
4544
  /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "uix-scheduling-calendar__header", children: [
4369
4545
  /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { children: [
4370
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => movePeriod(-1), disabled: !onAnchorDateChange, children: "Previous" }),
4371
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => movePeriod(1), disabled: !onAnchorDateChange, children: "Next" })
4546
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => movePeriod(-1), disabled: !onAnchorDateChange, children: labels.previous }),
4547
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => movePeriod(1), disabled: !onAnchorDateChange, children: labels.next })
4372
4548
  ] }),
4373
4549
  /* @__PURE__ */ (0, import_jsx_runtime62.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)}` }),
4374
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("div", { className: "uix-segmented", role: "group", "aria-label": "Calendar view", children: ["month", "week", "agenda"].map((option) => /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("button", { type: "button", className: "uix-segmented__option", "aria-pressed": view === option, "data-selected": view === option || void 0, onClick: () => setView(option), children: [
4375
- option[0].toUpperCase(),
4376
- option.slice(1)
4377
- ] }, option)) })
4550
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("div", { className: "uix-segmented", role: "group", "aria-label": labels.viewGroup, children: ["month", "week", "agenda"].map((option) => /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("button", { type: "button", className: "uix-segmented__option", "aria-pressed": view === option, "data-selected": view === option || void 0, onClick: () => setView(option), children: viewLabel[option] }, option)) })
4378
4551
  ] }),
4379
- loading ? /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("div", { className: "uix-scheduling-calendar__state", role: "status", children: "Loading schedule\u2026" }) : error ? /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "uix-scheduling-calendar__state", role: "alert", children: [
4552
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("div", { className: "uix-scheduling-calendar__state", role: "status", children: labels.loading }) : error ? /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "uix-scheduling-calendar__state", role: "alert", children: [
4380
4553
  /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("p", { children: error }),
4381
- onRetry && /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary", onClick: onRetry, children: "Try again" })
4382
- ] }) : view === "agenda" ? /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("div", { className: "uix-scheduling-calendar__agenda", role: "region", "aria-label": "Schedule agenda", children: visibleEntries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("p", { children: "No scheduled entries match the current filters." }) : /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("ol", { children: [...visibleEntries].sort((a, b) => a.start.localeCompare(b.start)).map((entry) => {
4554
+ onRetry && /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary", onClick: onRetry, children: labels.retry })
4555
+ ] }) : view === "agenda" ? /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("div", { className: "uix-scheduling-calendar__agenda", role: "region", "aria-label": labels.agenda, children: visibleEntries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("p", { children: labels.agendaEmpty }) : /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("ol", { children: [...visibleEntries].sort((a, b) => a.start.localeCompare(b.start)).map((entry) => {
4383
4556
  const state = entry.state ?? "scheduled";
4384
4557
  return /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("button", { type: "button", onClick: () => onSelectEntry?.(entry), children: [
4385
4558
  /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { children: [
@@ -4393,7 +4566,7 @@ function SchedulingCalendar({
4393
4566
  ] }),
4394
4567
  /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(StatusPill, { tone: stateTone(state), children: stateLabel[state] })
4395
4568
  ] }) }, entry.id);
4396
- }) }) }) : /* @__PURE__ */ (0, import_jsx_runtime62.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) => {
4569
+ }) }) }) : /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("div", { ref: gridRef, className: cx("uix-scheduling-calendar__grid", view === "week" && "uix-scheduling-calendar__grid--week"), role: "group", "aria-label": fillLabel(labels.grid, { view: viewLabel[view] }), children: days.map((date, index) => {
4397
4570
  const dayEntries = entriesFor(date);
4398
4571
  const dayOverlays = overlaysFor(date);
4399
4572
  return /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "uix-scheduling-calendar__day", "data-outside": view === "month" && !monthDays[index].inMonth || void 0, children: [
@@ -4402,22 +4575,38 @@ function SchedulingCalendar({
4402
4575
  /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("div", { className: "uix-scheduling-calendar__entries", children: dayEntries.map((entry) => {
4403
4576
  const span = entrySpans.get(entry.id);
4404
4577
  const state = entry.state ?? "scheduled";
4405
- return /* @__PURE__ */ (0, import_jsx_runtime62.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: [
4578
+ return /* @__PURE__ */ (0, import_jsx_runtime62.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": fillLabel(labels.entry, { title: entry.title, state: stateLabel[state], start: formatInstant(entry.start), end: formatInstant(entry.end) }), children: [
4406
4579
  /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { children: renderEntry?.(entry) ?? entry.title }),
4407
4580
  /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "uix-visually-hidden", children: stateLabel[state] })
4408
4581
  ] }, entry.id);
4409
4582
  }) })
4410
4583
  ] }, date);
4411
4584
  }) }),
4412
- /* @__PURE__ */ (0, import_jsx_runtime62.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_runtime62.jsx)("span", { "data-state": state, children: stateLabel[state] }, state)) })
4585
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("div", { className: "uix-scheduling-calendar__legend", "aria-label": labels.legend, children: ["scheduled", "conflicted", "in-progress", "blackout-violation"].map((state) => /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { "data-state": state, children: stateLabel[state] }, state)) })
4413
4586
  ] });
4414
4587
  }
4415
4588
 
4416
4589
  // src/components/DateRangePicker.tsx
4417
- var import_react41 = require("react");
4590
+ var import_react42 = require("react");
4418
4591
  var import_jsx_runtime63 = require("react/jsx-runtime");
4592
+ var DEFAULT_DATE_RANGE_PICKER_LABELS = {
4593
+ previous: "Previous",
4594
+ previousMonth: "Show previous month",
4595
+ next: "Next",
4596
+ nextMonth: "Show next month",
4597
+ noneSelected: "No date range selected.",
4598
+ startSelected: "Start date {start} selected. Choose an end date.",
4599
+ rangeSelected: "{start} to {end} selected.",
4600
+ rangeStart: "start of selected range",
4601
+ rangeEnd: "end of selected range",
4602
+ inRange: "in selected range"
4603
+ };
4419
4604
  var monthLabel = (date, locale) => new Intl.DateTimeFormat(locale, { month: "long", year: "numeric", timeZone: "UTC" }).format(/* @__PURE__ */ new Date(`${date}T00:00:00Z`));
4420
4605
  var dateLabel2 = (date, locale) => new Intl.DateTimeFormat(locale, { dateStyle: "full", timeZone: "UTC" }).format(/* @__PURE__ */ new Date(`${date}T00:00:00Z`));
4606
+ var weekdayNames = (locale) => {
4607
+ const fmt = new Intl.DateTimeFormat(locale, { weekday: "short", timeZone: "UTC" });
4608
+ return Array.from({ length: 7 }, (_, i) => fmt.format(new Date(Date.UTC(2024, 0, 1 + i))));
4609
+ };
4421
4610
  function DateRangePicker({
4422
4611
  value,
4423
4612
  onChange,
@@ -4432,27 +4621,30 @@ function DateRangePicker({
4432
4621
  invalid,
4433
4622
  invalidMessage = "The selected date range is invalid.",
4434
4623
  disabled,
4435
- className
4624
+ className,
4625
+ labels: labelOverrides
4436
4626
  }) {
4437
- const id = (0, import_react41.useId)();
4438
- const [internalMonth, setInternalMonth] = (0, import_react41.useState)(() => startOfMonth(controlledMonth ?? value.start ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)));
4627
+ const id = (0, import_react42.useId)();
4628
+ const labels = { ...DEFAULT_DATE_RANGE_PICKER_LABELS, ...labelOverrides };
4629
+ const weekdays = (0, import_react42.useMemo)(() => weekdayNames(locale), [locale]);
4630
+ const [internalMonth, setInternalMonth] = (0, import_react42.useState)(() => startOfMonth(controlledMonth ?? value.start ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)));
4439
4631
  const visibleMonth = startOfMonth(controlledMonth ?? internalMonth);
4440
- const [activeDate, setActiveDate] = (0, import_react41.useState)(value.end ?? value.start ?? visibleMonth);
4441
- const rootRef = (0, import_react41.useRef)(null);
4442
- const pendingFocus = (0, import_react41.useRef)(false);
4632
+ const [activeDate, setActiveDate] = (0, import_react42.useState)(value.end ?? value.start ?? visibleMonth);
4633
+ const rootRef = (0, import_react42.useRef)(null);
4634
+ const pendingFocus = (0, import_react42.useRef)(false);
4443
4635
  const count = Math.max(1, Math.min(3, months));
4444
- const calendars = (0, import_react41.useMemo)(() => Array.from({ length: count }, (_, index) => {
4636
+ const calendars = (0, import_react42.useMemo)(() => Array.from({ length: count }, (_, index) => {
4445
4637
  const month = startOfMonth(addCalendarMonths(visibleMonth, index));
4446
4638
  return { month, days: buildMonthGrid(month).filter((day) => day.inMonth) };
4447
4639
  }), [visibleMonth, count]);
4448
4640
  const rangeInvalid = invalid || !!value.start && !!value.end && value.start > value.end;
4449
- (0, import_react41.useEffect)(() => {
4641
+ (0, import_react42.useEffect)(() => {
4450
4642
  const first = calendars[0]?.days[0]?.date;
4451
4643
  const finalDays = calendars[calendars.length - 1]?.days;
4452
4644
  const last = finalDays?.[finalDays.length - 1]?.date;
4453
4645
  if (first && last && (activeDate < first || activeDate > last)) setActiveDate(first);
4454
4646
  }, [activeDate, calendars]);
4455
- (0, import_react41.useEffect)(() => {
4647
+ (0, import_react42.useEffect)(() => {
4456
4648
  if (!pendingFocus.current) return;
4457
4649
  rootRef.current?.querySelector(`[data-date="${activeDate}"]`)?.focus();
4458
4650
  pendingFocus.current = false;
@@ -4489,18 +4681,18 @@ function DateRangePicker({
4489
4681
  focusDate(addCalendarMonths(date, event.key === "PageUp" ? -1 : 1));
4490
4682
  }
4491
4683
  };
4492
- 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.`;
4684
+ const announcement = !value.start ? labels.noneSelected : !value.end ? labels.startSelected.replace("{start}", dateLabel2(value.start, locale)) : labels.rangeSelected.replace("{start}", dateLabel2(value.start, locale)).replace("{end}", dateLabel2(value.end, locale));
4493
4685
  return /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("section", { className: cx("uix-date-range-picker", rangeInvalid && "uix-date-range-picker--invalid", className), "aria-labelledby": `${id}-label`, children: [
4494
4686
  /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "uix-date-range-picker__header", children: [
4495
4687
  /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("h3", { id: `${id}-label`, children: label }),
4496
4688
  /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { children: [
4497
- /* @__PURE__ */ (0, import_jsx_runtime63.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" }),
4498
- /* @__PURE__ */ (0, import_jsx_runtime63.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" })
4689
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => setMonth(addCalendarMonths(visibleMonth, -1)), "aria-label": labels.previousMonth, children: labels.previous }),
4690
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => setMonth(addCalendarMonths(visibleMonth, 1)), "aria-label": labels.nextMonth, children: labels.next })
4499
4691
  ] })
4500
4692
  ] }),
4501
4693
  /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("div", { ref: rootRef, className: "uix-date-range-picker__months", children: calendars.map(({ month, days }) => /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "uix-date-range-picker__month", children: [
4502
4694
  /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("h4", { children: monthLabel(month, locale) }),
4503
- /* @__PURE__ */ (0, import_jsx_runtime63.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_runtime63.jsx)("span", { children: day }, day)) }),
4695
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("div", { className: "uix-date-range-picker__weekdays", "aria-hidden": "true", children: weekdays.map((day, index) => /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { children: day }, index)) }),
4504
4696
  /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "uix-date-range-picker__grid", role: "group", "aria-label": monthLabel(month, locale), children: [
4505
4697
  Array.from({ length: ((/* @__PURE__ */ new Date(`${month}T00:00:00Z`)).getUTCDay() + 6) % 7 }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { "aria-hidden": "true" }, `blank-${index}`)),
4506
4698
  days.map((day) => {
@@ -4514,7 +4706,7 @@ function DateRangePicker({
4514
4706
  "data-range-edge": edge,
4515
4707
  "data-in-range": isDateInRange(day.date, value) || void 0,
4516
4708
  "aria-pressed": edge != null || isDateInRange(day.date, value),
4517
- "aria-label": `${dateLabel2(day.date, locale)}${edge ? `, ${edge} of selected range` : isDateInRange(day.date, value) ? ", in selected range" : ""}`,
4709
+ "aria-label": `${dateLabel2(day.date, locale)}${edge ? `, ${edge === "start" ? labels.rangeStart : labels.rangeEnd}` : isDateInRange(day.date, value) ? `, ${labels.inRange}` : ""}`,
4518
4710
  tabIndex: activeDate === day.date ? 0 : -1,
4519
4711
  disabled: unavailable,
4520
4712
  onFocus: () => setActiveDate(day.date),
@@ -4533,7 +4725,7 @@ function DateRangePicker({
4533
4725
  }
4534
4726
 
4535
4727
  // src/components/RelationshipGraph.tsx
4536
- var import_react43 = require("react");
4728
+ var import_react44 = require("react");
4537
4729
 
4538
4730
  // src/relationship-graph-model.ts
4539
4731
  function boundRelationshipGraph(nodes, edges, maxNodes = 40) {
@@ -5014,7 +5206,7 @@ function layeredNeighbor(layout, currentId, key) {
5014
5206
  }
5015
5207
 
5016
5208
  // src/components/RelationshipGraphLayered.tsx
5017
- var import_react42 = require("react");
5209
+ var import_react43 = require("react");
5018
5210
 
5019
5211
  // src/components/RelationshipGraphList.tsx
5020
5212
  var import_jsx_runtime64 = require("react/jsx-runtime");
@@ -5101,11 +5293,11 @@ function LayeredRelationshipGraph({
5101
5293
  focusId,
5102
5294
  onFocusChange
5103
5295
  }) {
5104
- const hintId = (0, import_react42.useId)();
5105
- const bounded = (0, import_react42.useMemo)(() => boundRelationshipGraph(nodes, edges, maxNodes), [nodes, edges, maxNodes]);
5106
- const [localExpanded, setLocalExpanded] = (0, import_react42.useState)(EMPTY_IDS);
5296
+ const hintId = (0, import_react43.useId)();
5297
+ const bounded = (0, import_react43.useMemo)(() => boundRelationshipGraph(nodes, edges, maxNodes), [nodes, edges, maxNodes]);
5298
+ const [localExpanded, setLocalExpanded] = (0, import_react43.useState)(EMPTY_IDS);
5107
5299
  const expanded = expandedClusterIds ?? localExpanded;
5108
- const layout = (0, import_react42.useMemo)(
5300
+ const layout = (0, import_react43.useMemo)(
5109
5301
  () => layoutLayeredGraph(bounded.nodes, bounded.edges, {
5110
5302
  ...rootId !== void 0 ? { rootId } : {},
5111
5303
  ...clusterThreshold !== void 0 ? { clusterThreshold } : {},
@@ -5114,29 +5306,29 @@ function LayeredRelationshipGraph({
5114
5306
  }),
5115
5307
  [bounded, rootId, clusterThreshold, expanded, density]
5116
5308
  );
5117
- const itemById = (0, import_react42.useMemo)(() => new Map(layout.items.map((item) => [item.id, item])), [layout]);
5118
- const viewportRef = (0, import_react42.useRef)(null);
5119
- const itemRefs = (0, import_react42.useRef)(/* @__PURE__ */ new Map());
5120
- const [size, setSize] = (0, import_react42.useState)({ width: 0, height: 0 });
5121
- const [view, setView] = (0, import_react42.useState)({ x: 0, y: 0, zoom: 1 });
5122
- const [animate, setAnimate] = (0, import_react42.useState)(false);
5123
- const [localFocus, setLocalFocus] = (0, import_react42.useState)(void 0);
5124
- const [hoverId, setHoverId] = (0, import_react42.useState)(void 0);
5125
- const viewRef = (0, import_react42.useRef)(view);
5309
+ const itemById = (0, import_react43.useMemo)(() => new Map(layout.items.map((item) => [item.id, item])), [layout]);
5310
+ const viewportRef = (0, import_react43.useRef)(null);
5311
+ const itemRefs = (0, import_react43.useRef)(/* @__PURE__ */ new Map());
5312
+ const [size, setSize] = (0, import_react43.useState)({ width: 0, height: 0 });
5313
+ const [view, setView] = (0, import_react43.useState)({ x: 0, y: 0, zoom: 1 });
5314
+ const [animate, setAnimate] = (0, import_react43.useState)(false);
5315
+ const [localFocus, setLocalFocus] = (0, import_react43.useState)(void 0);
5316
+ const [hoverId, setHoverId] = (0, import_react43.useState)(void 0);
5317
+ const viewRef = (0, import_react43.useRef)(view);
5126
5318
  viewRef.current = view;
5127
- const layoutRef = (0, import_react42.useRef)(layout);
5319
+ const layoutRef = (0, import_react43.useRef)(layout);
5128
5320
  layoutRef.current = layout;
5129
- const pendingFocus = (0, import_react42.useRef)(void 0);
5130
- const initialisedFor = (0, import_react42.useRef)(void 0);
5321
+ const pendingFocus = (0, import_react43.useRef)(void 0);
5322
+ const initialisedFor = (0, import_react43.useRef)(void 0);
5131
5323
  const rootItemId = layout.rootId !== void 0 ? layout.itemOf[layout.rootId] : layout.items[0]?.id;
5132
5324
  const requestedFocus = focusId !== void 0 ? layout.itemOf[focusId] ?? focusId : localFocus;
5133
5325
  const focusedId = requestedFocus !== void 0 && itemById.has(requestedFocus) ? requestedFocus : rootItemId;
5134
5326
  const selectedItemId = selectedId !== void 0 ? layout.itemOf[selectedId] : void 0;
5135
- const activeIds = (0, import_react42.useMemo)(
5327
+ const activeIds = (0, import_react43.useMemo)(
5136
5328
  () => new Set([hoverId, selectedItemId].filter((id) => id !== void 0)),
5137
5329
  [hoverId, selectedItemId]
5138
5330
  );
5139
- (0, import_react42.useEffect)(() => {
5331
+ (0, import_react43.useEffect)(() => {
5140
5332
  const element = viewportRef.current;
5141
5333
  if (!element) return;
5142
5334
  const measure = () => setSize({ width: element.clientWidth, height: element.clientHeight });
@@ -5146,18 +5338,18 @@ function LayeredRelationshipGraph({
5146
5338
  observer.observe(element);
5147
5339
  return () => observer.disconnect();
5148
5340
  }, []);
5149
- (0, import_react42.useEffect)(() => {
5341
+ (0, import_react43.useEffect)(() => {
5150
5342
  if (size.width === 0) return;
5151
5343
  const key = `${layout.rootId ?? ""}`;
5152
5344
  if (initialisedFor.current === key) return;
5153
5345
  initialisedFor.current = key;
5154
5346
  setView(initialView(layout, size));
5155
5347
  }, [layout, size]);
5156
- const moveView = (0, import_react42.useCallback)((next, smooth) => {
5348
+ const moveView = (0, import_react43.useCallback)((next, smooth) => {
5157
5349
  setAnimate(smooth);
5158
5350
  setView(next);
5159
5351
  }, []);
5160
- const ensureVisible = (0, import_react42.useCallback)((id, smooth) => {
5352
+ const ensureVisible = (0, import_react43.useCallback)((id, smooth) => {
5161
5353
  const element = viewportRef.current;
5162
5354
  const item = layoutRef.current.items.find((candidate) => candidate.id === id);
5163
5355
  if (!element || !item) return;
@@ -5176,7 +5368,7 @@ function LayeredRelationshipGraph({
5176
5368
  else if (bottom > heightPx - FOCUS_MARGIN) nextY -= bottom - (heightPx - FOCUS_MARGIN);
5177
5369
  if (nextX !== x || nextY !== y) moveView({ x: nextX, y: nextY, zoom }, smooth);
5178
5370
  }, [moveView]);
5179
- const focusItem = (0, import_react42.useCallback)((id) => {
5371
+ const focusItem = (0, import_react43.useCallback)((id) => {
5180
5372
  setLocalFocus(id);
5181
5373
  onFocusChange?.(id);
5182
5374
  requestAnimationFrame(() => {
@@ -5184,7 +5376,7 @@ function LayeredRelationshipGraph({
5184
5376
  ensureVisible(id, true);
5185
5377
  });
5186
5378
  }, [ensureVisible, onFocusChange]);
5187
- (0, import_react42.useEffect)(() => {
5379
+ (0, import_react43.useEffect)(() => {
5188
5380
  const wanted = pendingFocus.current;
5189
5381
  if (wanted !== void 0 && itemById.has(wanted)) {
5190
5382
  pendingFocus.current = void 0;
@@ -5194,8 +5386,8 @@ function LayeredRelationshipGraph({
5194
5386
  const controlledTarget = focusId !== void 0 ? layout.itemOf[focusId] ?? focusId : void 0;
5195
5387
  const controlledPresent = controlledTarget !== void 0 && itemById.has(controlledTarget);
5196
5388
  const controlledKey = `${controlledTarget ?? ""}|${controlledPresent}`;
5197
- const lastControlledKey = (0, import_react42.useRef)(controlledKey);
5198
- (0, import_react42.useEffect)(() => {
5389
+ const lastControlledKey = (0, import_react43.useRef)(controlledKey);
5390
+ (0, import_react43.useEffect)(() => {
5199
5391
  if (lastControlledKey.current === controlledKey) return;
5200
5392
  lastControlledKey.current = controlledKey;
5201
5393
  if (!controlledPresent || controlledTarget === void 0) return;
@@ -5204,7 +5396,7 @@ function LayeredRelationshipGraph({
5204
5396
  ensureVisible(controlledTarget, true);
5205
5397
  });
5206
5398
  }, [controlledKey, controlledTarget, controlledPresent, ensureVisible]);
5207
- (0, import_react42.useEffect)(() => {
5399
+ (0, import_react43.useEffect)(() => {
5208
5400
  const element = viewportRef.current;
5209
5401
  if (!element) return;
5210
5402
  const onWheel = (event) => {
@@ -5222,8 +5414,8 @@ function LayeredRelationshipGraph({
5222
5414
  element.addEventListener("wheel", onWheel, { passive: false });
5223
5415
  return () => element.removeEventListener("wheel", onWheel);
5224
5416
  }, []);
5225
- const pointers = (0, import_react42.useRef)(/* @__PURE__ */ new Map());
5226
- const pinchDistance = (0, import_react42.useRef)(void 0);
5417
+ const pointers = (0, import_react43.useRef)(/* @__PURE__ */ new Map());
5418
+ const pinchDistance = (0, import_react43.useRef)(void 0);
5227
5419
  const onPointerDown = (event) => {
5228
5420
  if (event.target.closest("button")) return;
5229
5421
  event.currentTarget.setPointerCapture?.(event.pointerId);
@@ -5524,7 +5716,7 @@ function radialViewBox(nodes) {
5524
5716
  return `${round(minX)} ${round(minY)} ${width} ${height}`;
5525
5717
  }
5526
5718
  function RelationshipGraph(props) {
5527
- const labels = (0, import_react43.useMemo)(() => ({ ...DEFAULT_RELATIONSHIP_GRAPH_LABELS, ...props.labels }), [props.labels]);
5719
+ const labels = (0, import_react44.useMemo)(() => ({ ...DEFAULT_RELATIONSHIP_GRAPH_LABELS, ...props.labels }), [props.labels]);
5528
5720
  const stateClass = cx("uix-relationship-graph uix-relationship-graph--state", props.className);
5529
5721
  if (props.loading) return /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("div", { className: stateClass, role: "status", children: labels.loading });
5530
5722
  if (props.error) {
@@ -5558,13 +5750,17 @@ function RadialRelationshipGraph({
5558
5750
  labels,
5559
5751
  showList = true
5560
5752
  }) {
5561
- const bounded = (0, import_react43.useMemo)(() => boundRelationshipGraph(nodes, edges, maxNodes), [nodes, edges, maxNodes]);
5562
- const positioned = (0, import_react43.useMemo)(() => layoutRelationshipGraph(bounded.nodes, bounded.edges, selectedId), [bounded, selectedId]);
5563
- const positionedById = (0, import_react43.useMemo)(() => new Map(positioned.map((node) => [node.id, node])), [positioned]);
5564
- const [zoom, setZoom] = (0, import_react43.useState)(1);
5565
- const [pan, setPan] = (0, import_react43.useState)({ x: 0, y: 0 });
5566
- const [focusedId, setFocusedId] = (0, import_react43.useState)(selectedId ?? positioned[0]?.id);
5567
- const svgRef = (0, import_react43.useRef)(null);
5753
+ const bounded = (0, import_react44.useMemo)(() => boundRelationshipGraph(nodes, edges, maxNodes), [nodes, edges, maxNodes]);
5754
+ const positioned = (0, import_react44.useMemo)(() => layoutRelationshipGraph(bounded.nodes, bounded.edges, selectedId), [bounded, selectedId]);
5755
+ const typeColor = (0, import_react44.useMemo)(
5756
+ () => new Map(legend.map((item, index) => [item.id, `var(--uix-chart-${index % 8 + 1})`])),
5757
+ [legend]
5758
+ );
5759
+ const positionedById = (0, import_react44.useMemo)(() => new Map(positioned.map((node) => [node.id, node])), [positioned]);
5760
+ const [zoom, setZoom] = (0, import_react44.useState)(1);
5761
+ const [pan, setPan] = (0, import_react44.useState)({ x: 0, y: 0 });
5762
+ const [focusedId, setFocusedId] = (0, import_react44.useState)(selectedId ?? positioned[0]?.id);
5763
+ const svgRef = (0, import_react44.useRef)(null);
5568
5764
  const select = (id) => {
5569
5765
  const node = bounded.nodes.find((item) => item.id === id);
5570
5766
  if (node) onSelect?.(node);
@@ -5603,7 +5799,7 @@ function RadialRelationshipGraph({
5603
5799
  bounded.edges.map((edge) => {
5604
5800
  const source = positionedById.get(edge.source);
5605
5801
  const target = positionedById.get(edge.target);
5606
- return /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("line", { x1: source.x * RADIAL_SCALE_X, y1: source.y * RADIAL_SCALE_Y, x2: target.x * RADIAL_SCALE_X, y2: target.y * RADIAL_SCALE_Y, 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_runtime66.jsx)("title", { children: edge.label ?? edge.type ?? labels.relationship }) }, edge.id);
5802
+ return /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("line", { x1: source.x * RADIAL_SCALE_X, y1: source.y * RADIAL_SCALE_Y, x2: target.x * RADIAL_SCALE_X, y2: target.y * RADIAL_SCALE_Y, className: "uix-relationship-graph__edge", "data-typed": edge.type != null && typeColor.has(edge.type) || void 0, style: edge.type != null && typeColor.has(edge.type) ? { "--uix-graph-type-color": typeColor.get(edge.type) } : void 0, "data-highlighted": highlightedEdgeIds.has(edge.id) || void 0, "data-conflicted": conflictedEdgeIds.has(edge.id) || void 0, children: /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("title", { children: edge.label ?? edge.type ?? labels.relationship }) }, edge.id);
5607
5803
  }),
5608
5804
  positioned.map((node) => /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(
5609
5805
  "g",
@@ -5629,14 +5825,55 @@ function RadialRelationshipGraph({
5629
5825
  node.id
5630
5826
  ))
5631
5827
  ] }) }) }),
5632
- legend.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("ul", { className: "uix-relationship-graph__legend", "aria-label": labels.legend, children: legend.map((item) => /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("li", { "data-type": item.id, children: item.label }, item.id)) }),
5828
+ legend.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("ul", { className: "uix-relationship-graph__legend", "aria-label": labels.legend, children: legend.map((item) => /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("li", { "data-type": item.id, style: { "--uix-graph-type-color": typeColor.get(item.id) }, children: item.label }, item.id)) }),
5633
5829
  showList && /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(EquivalentRelationshipList, { nodes: bounded.nodes, edges: bounded.edges, selectedId, labels, onSelect: select, onExpandNeighbors, onOpenDetails })
5634
5830
  ] });
5635
5831
  }
5636
5832
 
5637
5833
  // src/components/MatchReview.tsx
5638
- var import_react44 = require("react");
5834
+ var import_react45 = require("react");
5639
5835
  var import_jsx_runtime67 = require("react/jsx-runtime");
5836
+ var DEFAULT_MATCH_REVIEW_LABELS = {
5837
+ region: "Match review",
5838
+ loading: "Loading match candidates\u2026",
5839
+ retry: "Try again",
5840
+ empty: "No candidate matches found.",
5841
+ emptyDetail: "Choose another record source or continue without a match.",
5842
+ pendingOf: "{pending} pending of {total}",
5843
+ bulk: "Bulk candidate actions",
5844
+ selectedCount: "{count} selected",
5845
+ applying: "Applying\u2026",
5846
+ acceptSelected: "Accept selected",
5847
+ dismissSelected: "Dismiss selected",
5848
+ caption: "Candidate matches and available decisions",
5849
+ select: "Select",
5850
+ confidence: "Confidence",
5851
+ status: "Status",
5852
+ actions: "Actions",
5853
+ selectCandidate: "Select candidate {id}",
5854
+ notScored: "Not scored",
5855
+ resolved: "Resolved",
5856
+ pending: "Pending",
5857
+ decisionAccepted: "Accepted",
5858
+ decisionDismissed: "Dismissed",
5859
+ decisionSplit: "Split",
5860
+ decisionOther: "Other match",
5861
+ accept: "Accept",
5862
+ dismiss: "Dismiss",
5863
+ split: "Split",
5864
+ pickOther: "Pick other",
5865
+ acceptingOne: "Accepting {count} candidate\u2026",
5866
+ acceptingMany: "Accepting {count} candidates\u2026",
5867
+ dismissingOne: "Dismissing {count} candidate\u2026",
5868
+ dismissingMany: "Dismissing {count} candidates\u2026",
5869
+ acceptedOne: "{count} candidate accepted",
5870
+ acceptedMany: "{count} candidates accepted",
5871
+ dismissedOne: "{count} candidate dismissed",
5872
+ dismissedMany: "{count} candidates dismissed",
5873
+ failedSuffix: "; {count} failed",
5874
+ bulkAcceptFailed: "Bulk accept failed. Try again.",
5875
+ bulkDismissFailed: "Bulk dismiss failed. Try again."
5876
+ };
5640
5877
  var valueText = (value) => value == null ? "\u2014" : typeof value === "string" ? value : JSON.stringify(value);
5641
5878
  function MatchReview({
5642
5879
  incoming,
@@ -5650,13 +5887,18 @@ function MatchReview({
5650
5887
  loading,
5651
5888
  error,
5652
5889
  onRetry,
5653
- className
5890
+ className,
5891
+ labels: labelOverrides
5654
5892
  }) {
5655
- const [internalSelection, setInternalSelection] = (0, import_react44.useState)(/* @__PURE__ */ new Set());
5656
- const [announcement, setAnnouncement] = (0, import_react44.useState)("");
5657
- const [bulkPending, setBulkPending] = (0, import_react44.useState)(false);
5893
+ const labels = { ...DEFAULT_MATCH_REVIEW_LABELS, ...labelOverrides };
5894
+ const plural = (n, one, many) => fillLabel(n === 1 ? one : many, { count: n });
5895
+ const decisionLabel = { accepted: labels.decisionAccepted, dismissed: labels.decisionDismissed, split: labels.decisionSplit, other: labels.decisionOther };
5896
+ const actionLabel = { accept: labels.accept, dismiss: labels.dismiss, split: labels.split, "pick-other": labels.pickOther };
5897
+ const [internalSelection, setInternalSelection] = (0, import_react45.useState)(/* @__PURE__ */ new Set());
5898
+ const [announcement, setAnnouncement] = (0, import_react45.useState)("");
5899
+ const [bulkPending, setBulkPending] = (0, import_react45.useState)(false);
5658
5900
  const selection = selectedIds ?? internalSelection;
5659
- const pending = (0, import_react44.useMemo)(() => candidates.filter((candidate) => candidate.status !== "resolved"), [candidates]);
5901
+ const pending = (0, import_react45.useMemo)(() => candidates.filter((candidate) => candidate.status !== "resolved"), [candidates]);
5660
5902
  const setSelection = (next) => {
5661
5903
  if (selectedIds === void 0) setInternalSelection(next);
5662
5904
  onSelectionChange?.(next);
@@ -5671,67 +5913,61 @@ function MatchReview({
5671
5913
  if (!onBulkDecision || selection.size === 0 || bulkPending) return;
5672
5914
  const ids = [...selection];
5673
5915
  setBulkPending(true);
5674
- setAnnouncement(`${action === "accept" ? "Accepting" : "Dismissing"} ${ids.length} candidate${ids.length === 1 ? "" : "s"}\u2026`);
5916
+ setAnnouncement(action === "accept" ? plural(ids.length, labels.acceptingOne, labels.acceptingMany) : plural(ids.length, labels.dismissingOne, labels.dismissingMany));
5675
5917
  try {
5676
5918
  const result = await onBulkDecision(ids, action);
5677
5919
  const succeeded = result?.succeeded ?? ids.length;
5678
5920
  const failed = result?.failed ?? 0;
5679
- setAnnouncement(`${succeeded} candidate${succeeded === 1 ? "" : "s"} ${action === "accept" ? "accepted" : "dismissed"}${failed ? `; ${failed} failed` : ""}.`);
5921
+ const done = action === "accept" ? plural(succeeded, labels.acceptedOne, labels.acceptedMany) : plural(succeeded, labels.dismissedOne, labels.dismissedMany);
5922
+ setAnnouncement(`${done}${failed ? fillLabel(labels.failedSuffix, { count: failed }) : ""}.`);
5680
5923
  if (!failed) setSelection(/* @__PURE__ */ new Set());
5681
5924
  } catch {
5682
- setAnnouncement(`Bulk ${action} failed. Try again.`);
5925
+ setAnnouncement(action === "accept" ? labels.bulkAcceptFailed : labels.bulkDismissFailed);
5683
5926
  } finally {
5684
5927
  setBulkPending(false);
5685
5928
  }
5686
5929
  };
5687
- if (loading) return /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("div", { className: cx("uix-match-review uix-match-review--state", className), role: "status", children: "Loading match candidates\u2026" });
5930
+ if (loading) return /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("div", { className: cx("uix-match-review uix-match-review--state", className), role: "status", children: labels.loading });
5688
5931
  if (error) return /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("div", { className: cx("uix-match-review uix-match-review--state", className), role: "alert", children: [
5689
5932
  /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("p", { children: error }),
5690
- onRetry && /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary", onClick: onRetry, children: "Try again" })
5933
+ onRetry && /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary", onClick: onRetry, children: labels.retry })
5691
5934
  ] });
5692
5935
  if (candidates.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("div", { className: cx("uix-match-review uix-match-review--state", className), children: [
5693
- /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("p", { children: "No candidate matches found." }),
5694
- /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("p", { children: "Choose another record source or continue without a match." })
5936
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("p", { children: labels.empty }),
5937
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("p", { children: labels.emptyDetail })
5695
5938
  ] });
5696
- return /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("section", { className: cx("uix-match-review", className), "aria-label": "Match review", children: [
5939
+ return /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("section", { className: cx("uix-match-review", className), "aria-label": labels.region, children: [
5697
5940
  /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("div", { className: "uix-match-review__summary", children: [
5698
5941
  /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("div", { children: [
5699
5942
  /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("strong", { children: incomingLabel }),
5700
- /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("span", { children: [
5701
- pending.length,
5702
- " pending of ",
5703
- candidates.length
5704
- ] })
5943
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { children: fillLabel(labels.pendingOf, { pending: pending.length, total: candidates.length }) })
5705
5944
  ] }),
5706
5945
  /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("div", { className: "uix-match-review__incoming", children: fields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("span", { children: [
5707
5946
  /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("b", { children: field.label }),
5708
5947
  field.render?.(incoming[field.id], incoming) ?? valueText(incoming[field.id])
5709
5948
  ] }, field.id)) })
5710
5949
  ] }),
5711
- onBulkDecision && /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("div", { className: "uix-match-review__bulk", "aria-label": "Bulk candidate actions", children: [
5712
- /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("span", { children: [
5713
- selection.size,
5714
- " selected"
5715
- ] }),
5716
- /* @__PURE__ */ (0, import_jsx_runtime67.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" }),
5717
- /* @__PURE__ */ (0, import_jsx_runtime67.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" })
5950
+ onBulkDecision && /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("div", { className: "uix-match-review__bulk", "aria-label": labels.bulk, children: [
5951
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { children: fillLabel(labels.selectedCount, { count: selection.size }) }),
5952
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", disabled: selection.size === 0 || bulkPending, onClick: () => void runBulk("accept"), children: bulkPending ? labels.applying : labels.acceptSelected }),
5953
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", disabled: selection.size === 0 || bulkPending, onClick: () => void runBulk("dismiss"), children: bulkPending ? labels.applying : labels.dismissSelected })
5718
5954
  ] }),
5719
5955
  /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("div", { className: "uix-match-review__table-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("table", { className: "uix-table uix-match-review__table", children: [
5720
- /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("caption", { className: "uix-visually-hidden", children: "Candidate matches and available decisions" }),
5956
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("caption", { className: "uix-visually-hidden", children: labels.caption }),
5721
5957
  /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("tr", { children: [
5722
- /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("th", { scope: "col", children: /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "uix-visually-hidden", children: "Select" }) }),
5723
- /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("th", { scope: "col", children: "Confidence" }),
5958
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("th", { scope: "col", children: /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "uix-visually-hidden", children: labels.select }) }),
5959
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("th", { scope: "col", children: labels.confidence }),
5724
5960
  fields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("th", { scope: "col", children: field.label }, field.id)),
5725
- /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("th", { scope: "col", children: "Status" }),
5726
- /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("th", { scope: "col", children: "Actions" })
5961
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("th", { scope: "col", children: labels.status }),
5962
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("th", { scope: "col", children: labels.actions })
5727
5963
  ] }) }),
5728
5964
  /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("tbody", { children: candidates.map((candidate) => {
5729
5965
  const resolved = candidate.status === "resolved";
5730
5966
  return /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("tr", { "data-status": candidate.status ?? "pending", children: [
5731
- /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("input", { type: "checkbox", checked: selection.has(candidate.id), onChange: () => toggle(candidate.id), "aria-label": `Select candidate ${candidate.id}` }) }),
5732
- /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "uix-match-review__confidence", children: candidate.confidence == null ? "Not scored" : `${Math.round(candidate.confidence * 100)}%` }) }),
5967
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("input", { type: "checkbox", checked: selection.has(candidate.id), onChange: () => toggle(candidate.id), "aria-label": fillLabel(labels.selectCandidate, { id: candidate.id }) }) }),
5968
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "uix-match-review__confidence", children: candidate.confidence == null ? labels.notScored : `${Math.round(candidate.confidence * 100)}%` }) }),
5733
5969
  fields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("td", { children: field.render?.(candidate.record[field.id], candidate.record) ?? valueText(candidate.record[field.id]) }, field.id)),
5734
- /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime67.jsx)(StatusPill, { tone: resolved ? "success" : "warning", children: resolved ? candidate.decision ?? "Resolved" : "Pending" }) }),
5970
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime67.jsx)(StatusPill, { tone: resolved ? "success" : "warning", children: resolved ? candidate.decision ? decisionLabel[candidate.decision] : labels.resolved : labels.pending }) }),
5735
5971
  /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("div", { className: "uix-match-review__actions", children: ["accept", "dismiss", "split", "pick-other"].map((action) => /* @__PURE__ */ (0, import_jsx_runtime67.jsx)(
5736
5972
  "button",
5737
5973
  {
@@ -5739,7 +5975,7 @@ function MatchReview({
5739
5975
  className: "uix-btn uix-btn--ghost uix-btn--sm",
5740
5976
  disabled: resolved,
5741
5977
  onClick: () => onDecision(candidate.id, action),
5742
- children: action === "pick-other" ? "Pick other" : `${action[0].toUpperCase()}${action.slice(1)}`
5978
+ children: actionLabel[action]
5743
5979
  },
5744
5980
  action
5745
5981
  )) }) })
@@ -5751,7 +5987,7 @@ function MatchReview({
5751
5987
  }
5752
5988
 
5753
5989
  // src/components/MetricInput.tsx
5754
- var import_react45 = require("react");
5990
+ var import_react46 = require("react");
5755
5991
 
5756
5992
  // src/metric-model.ts
5757
5993
  function decimals(value) {
@@ -5795,11 +6031,11 @@ function MetricInput({
5795
6031
  className,
5796
6032
  id: providedId
5797
6033
  }) {
5798
- const generatedId = (0, import_react45.useId)();
6034
+ const generatedId = (0, import_react46.useId)();
5799
6035
  const id = providedId ?? generatedId;
5800
6036
  const errorId = `${id}-error`;
5801
- const [text, setText] = (0, import_react45.useState)(() => format(value));
5802
- (0, import_react45.useEffect)(() => setText(format(value)), [value, format]);
6037
+ const [text, setText] = (0, import_react46.useState)(() => format(value));
6038
+ (0, import_react46.useEffect)(() => setText(format(value)), [value, format]);
5803
6039
  const commit = (input) => {
5804
6040
  const parsed = parse(input);
5805
6041
  if (input.trim() === "" || parsed !== null) {
@@ -5908,7 +6144,7 @@ function LicensePositionBar({
5908
6144
  }
5909
6145
 
5910
6146
  // src/components/BrandProfiles.tsx
5911
- var import_react47 = require("react");
6147
+ var import_react48 = require("react");
5912
6148
 
5913
6149
  // src/color-model.ts
5914
6150
  var clamp2 = (value, min, max) => Math.min(max, Math.max(min, value));
@@ -5977,8 +6213,24 @@ function meetsContrast(background, foreground, minimum = 4.5) {
5977
6213
  }
5978
6214
 
5979
6215
  // src/components/ColorPicker.tsx
5980
- var import_react46 = require("react");
6216
+ var import_react47 = require("react");
5981
6217
  var import_jsx_runtime70 = require("react/jsx-runtime");
6218
+ var DEFAULT_COLOR_PICKER_LABELS = {
6219
+ trigger: "{label}: {color}",
6220
+ sample: "Aa",
6221
+ hue: "Hue",
6222
+ saturation: "Saturation",
6223
+ brightness: "Brightness",
6224
+ hex: "Hex",
6225
+ set: "Set",
6226
+ presets: "Presets",
6227
+ recent: "Recent",
6228
+ usePreset: "Use preset {color}",
6229
+ useRecent: "Use recent color {color}",
6230
+ contrastPasses: "Contrast {ratio}:1 \u2014 passes WCAG AA ({minimum}:1)",
6231
+ contrastFails: "Contrast {ratio}:1 \u2014 does not meet WCAG AA ({minimum}:1)",
6232
+ done: "Done"
6233
+ };
5982
6234
  var EMPTY_COLORS = [];
5983
6235
  function ColorPicker({
5984
6236
  value,
@@ -5989,19 +6241,21 @@ function ColorPicker({
5989
6241
  minimumContrast = 4.5,
5990
6242
  label = "Choose color",
5991
6243
  disabled,
5992
- className
6244
+ className,
6245
+ labels: labelOverrides
5993
6246
  }) {
5994
- const id = (0, import_react46.useId)();
5995
- const triggerRef = (0, import_react46.useRef)(null);
5996
- const dialogRef = (0, import_react46.useRef)(null);
5997
- const [open, setOpen] = (0, import_react46.useState)(false);
6247
+ const labels = { ...DEFAULT_COLOR_PICKER_LABELS, ...labelOverrides };
6248
+ const id = (0, import_react47.useId)();
6249
+ const triggerRef = (0, import_react47.useRef)(null);
6250
+ const dialogRef = (0, import_react47.useRef)(null);
6251
+ const [open, setOpen] = (0, import_react47.useState)(false);
5998
6252
  const normalized = normalizeHex(value) ?? "#000000";
5999
- const [hexText, setHexText] = (0, import_react46.useState)(normalized);
6000
- const hsv = (0, import_react46.useMemo)(() => rgbToHsv(hexToRgb(normalized)), [normalized]);
6253
+ const [hexText, setHexText] = (0, import_react47.useState)(normalized);
6254
+ const hsv = (0, import_react47.useMemo)(() => rgbToHsv(hexToRgb(normalized)), [normalized]);
6001
6255
  const ratio = contrastRatio(normalized, foreground) ?? 0;
6002
6256
  const passes = ratio >= minimumContrast;
6003
- (0, import_react46.useEffect)(() => setHexText(normalized), [normalized]);
6004
- (0, import_react46.useEffect)(() => {
6257
+ (0, import_react47.useEffect)(() => setHexText(normalized), [normalized]);
6258
+ (0, import_react47.useEffect)(() => {
6005
6259
  if (!open) return;
6006
6260
  const dialog = dialogRef.current;
6007
6261
  dialog?.querySelector("input,button")?.focus();
@@ -6051,7 +6305,7 @@ function ColorPicker({
6051
6305
  ref: triggerRef,
6052
6306
  type: "button",
6053
6307
  className: "uix-color-picker__trigger",
6054
- "aria-label": `${label}: ${normalized}`,
6308
+ "aria-label": fillLabel(labels.trigger, { label, color: normalized }),
6055
6309
  "aria-expanded": open,
6056
6310
  "aria-controls": `${id}-dialog`,
6057
6311
  onClick: () => setOpen((current) => !current),
@@ -6073,9 +6327,10 @@ function ColorPicker({
6073
6327
  className: "uix-color-picker__popover",
6074
6328
  onKeyDown: onDialogKeyDown,
6075
6329
  children: [
6076
- /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("div", { className: "uix-color-picker__preview", style: sampleStyle, children: /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("span", { style: { color: foreground }, children: "Aa" }) }),
6330
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("div", { className: "uix-color-picker__preview", style: sampleStyle, children: /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("span", { style: { color: foreground }, children: labels.sample }) }),
6077
6331
  /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("label", { className: "uix-color-picker__range", children: [
6078
- "Hue ",
6332
+ labels.hue,
6333
+ " ",
6079
6334
  /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("span", { children: [
6080
6335
  Math.round(hsv.h),
6081
6336
  "\xB0"
@@ -6083,7 +6338,8 @@ function ColorPicker({
6083
6338
  /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("input", { type: "range", min: "0", max: "359", value: Math.round(hsv.h), onChange: (event) => updateHsv({ h: Number(event.currentTarget.value) }) })
6084
6339
  ] }),
6085
6340
  /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("label", { className: "uix-color-picker__range", children: [
6086
- "Saturation ",
6341
+ labels.saturation,
6342
+ " ",
6087
6343
  /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("span", { children: [
6088
6344
  Math.round(hsv.s * 100),
6089
6345
  "%"
@@ -6091,7 +6347,8 @@ function ColorPicker({
6091
6347
  /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("input", { type: "range", min: "0", max: "100", value: Math.round(hsv.s * 100), onChange: (event) => updateHsv({ s: Number(event.currentTarget.value) / 100 }) })
6092
6348
  ] }),
6093
6349
  /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("label", { className: "uix-color-picker__range", children: [
6094
- "Brightness ",
6350
+ labels.brightness,
6351
+ " ",
6095
6352
  /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("span", { children: [
6096
6353
  Math.round(hsv.v * 100),
6097
6354
  "%"
@@ -6099,31 +6356,26 @@ function ColorPicker({
6099
6356
  /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("input", { type: "range", min: "0", max: "100", value: Math.round(hsv.v * 100), onChange: (event) => updateHsv({ v: Number(event.currentTarget.value) / 100 }) })
6100
6357
  ] }),
6101
6358
  /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("label", { className: "uix-color-picker__hex", children: [
6102
- "Hex",
6359
+ labels.hex,
6103
6360
  /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("span", { className: "uix-color-picker__hex-row", children: [
6104
6361
  /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("input", { className: "uix-input", value: hexText, onChange: (event) => setHexText(event.currentTarget.value), onBlur: commitHex, onKeyDown: (event) => {
6105
6362
  if (event.key === "Enter") commitHex();
6106
6363
  }, "aria-invalid": !normalizeHex(hexText) }),
6107
- /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", onClick: commitHex, disabled: !normalizeHex(hexText), children: "Set" })
6364
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", onClick: commitHex, disabled: !normalizeHex(hexText), children: labels.set })
6108
6365
  ] })
6109
6366
  ] }),
6110
6367
  (presets.length > 0 || recent.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("div", { className: "uix-color-picker__palettes", children: [
6111
6368
  presets.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("fieldset", { children: [
6112
- /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("legend", { children: "Presets" }),
6113
- /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("div", { children: presets.map((color) => /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("button", { type: "button", className: "uix-color-picker__preset", style: { backgroundColor: normalizeHex(color) ?? color }, onClick: () => choose(color), "aria-label": `Use preset ${color}` }, color)) })
6369
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("legend", { children: labels.presets }),
6370
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("div", { children: presets.map((color) => /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("button", { type: "button", className: "uix-color-picker__preset", style: { backgroundColor: normalizeHex(color) ?? color }, onClick: () => choose(color), "aria-label": fillLabel(labels.usePreset, { color }) }, color)) })
6114
6371
  ] }),
6115
6372
  recent.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("fieldset", { children: [
6116
- /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("legend", { children: "Recent" }),
6117
- /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("div", { children: recent.map((color) => /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("button", { type: "button", className: "uix-color-picker__preset", style: { backgroundColor: normalizeHex(color) ?? color }, onClick: () => choose(color), "aria-label": `Use recent color ${color}` }, color)) })
6373
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("legend", { children: labels.recent }),
6374
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("div", { children: recent.map((color) => /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("button", { type: "button", className: "uix-color-picker__preset", style: { backgroundColor: normalizeHex(color) ?? color }, onClick: () => choose(color), "aria-label": fillLabel(labels.useRecent, { color }) }, color)) })
6118
6375
  ] })
6119
6376
  ] }),
6120
- /* @__PURE__ */ (0, import_jsx_runtime70.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: [
6121
- "Contrast ",
6122
- ratio.toFixed(2),
6123
- ":1 \u2014 ",
6124
- passes ? `passes WCAG AA (${minimumContrast}:1)` : `does not meet WCAG AA (${minimumContrast}:1)`
6125
- ] }),
6126
- /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("button", { type: "button", className: "uix-btn uix-btn--primary uix-btn--sm", onClick: close, children: "Done" })
6377
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("p", { className: cx("uix-color-picker__contrast", passes ? "uix-color-picker__contrast--pass" : "uix-color-picker__contrast--fail"), role: "status", "aria-live": "polite", children: fillLabel(passes ? labels.contrastPasses : labels.contrastFails, { ratio: ratio.toFixed(2), minimum: minimumContrast }) }),
6378
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("button", { type: "button", className: "uix-btn uix-btn--primary uix-btn--sm", onClick: close, children: labels.done })
6127
6379
  ]
6128
6380
  }
6129
6381
  )
@@ -6189,6 +6441,27 @@ function restoreBrandProfile(snapshot, target) {
6189
6441
  if (snapshot.logoAlt) element.dataset.uixBrandLogoAlt = snapshot.logoAlt;
6190
6442
  else delete element.dataset.uixBrandLogoAlt;
6191
6443
  }
6444
+ var DEFAULT_BRAND_PROFILE_EDITOR_LABELS = {
6445
+ region: "Brand profile editor",
6446
+ profile: "Profile",
6447
+ name: "Name",
6448
+ brand: "Brand",
6449
+ brandColor: "Brand color",
6450
+ onBrand: "On brand",
6451
+ onBrandColor: "Brand foreground color",
6452
+ bodyFont: "Body font",
6453
+ headingFont: "Heading font",
6454
+ logoFile: "Logo file",
6455
+ apply: "Apply profile",
6456
+ applied: "{name} applied.",
6457
+ preview: "Live preview of {name}",
6458
+ logoPlaceholder: "Logo",
6459
+ previewTitleFallback: "Brand preview",
6460
+ previewHeading: "Readable, serializable branding",
6461
+ previewBody: "Accent, links, rings, and muted states continue through the existing UIx token chain.",
6462
+ previewPrimary: "Primary action",
6463
+ previewLink: "Preview link"
6464
+ };
6192
6465
  function BrandProfileEditor({
6193
6466
  value,
6194
6467
  onChange,
@@ -6198,14 +6471,16 @@ function BrandProfileEditor({
6198
6471
  applyTarget,
6199
6472
  onLogoFile,
6200
6473
  colorPresets = EMPTY_COLORS2,
6201
- className
6474
+ className,
6475
+ labels: labelOverrides
6202
6476
  }) {
6203
- const [announcement, setAnnouncement] = (0, import_react47.useState)("");
6477
+ const labels = { ...DEFAULT_BRAND_PROFILE_EDITOR_LABELS, ...labelOverrides };
6478
+ const [announcement, setAnnouncement] = (0, import_react48.useState)("");
6204
6479
  const update = (patch) => onChange({ ...value, ...patch });
6205
6480
  const apply = () => {
6206
6481
  if (applyTarget !== null) applyBrandProfile(value, applyTarget);
6207
6482
  onApply?.(value);
6208
- setAnnouncement(`${value.name} applied.`);
6483
+ setAnnouncement(fillLabel(labels.applied, { name: value.name }));
6209
6484
  };
6210
6485
  const onFile = (event) => {
6211
6486
  const file = event.currentTarget.files?.[0];
@@ -6217,54 +6492,54 @@ function BrandProfileEditor({
6217
6492
  ...value.typography?.sans ? { "--uix-font-sans": value.typography.sans } : {},
6218
6493
  ...value.typography?.heading ? { "--uix-font-heading": value.typography.heading } : {}
6219
6494
  };
6220
- return /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)("section", { className: cx("uix-brand-profiles", className), "aria-label": "Brand profile editor", children: [
6495
+ return /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)("section", { className: cx("uix-brand-profiles", className), "aria-label": labels.region, children: [
6221
6496
  /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)("div", { className: "uix-brand-profiles__form", children: [
6222
6497
  profiles.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)("label", { className: "uix-field__label", children: [
6223
- "Profile",
6498
+ labels.profile,
6224
6499
  /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("select", { className: "uix-select", value: value.id, onChange: (event) => {
6225
6500
  const selected = profiles.find((profile) => profile.id === event.currentTarget.value);
6226
6501
  if (selected) onSelectProfile?.(selected);
6227
6502
  }, children: profiles.map((profile) => /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("option", { value: profile.id, children: profile.name }, profile.id)) })
6228
6503
  ] }),
6229
6504
  /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)("label", { className: "uix-field__label", children: [
6230
- "Name",
6505
+ labels.name,
6231
6506
  /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("input", { className: "uix-input", value: value.name, onChange: (event) => update({ name: event.currentTarget.value }) })
6232
6507
  ] }),
6233
6508
  /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)("div", { className: "uix-brand-profiles__colors", children: [
6234
6509
  /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)("label", { children: [
6235
- "Brand",
6236
- /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(ColorPicker, { label: "Brand color", value: value.brand, foreground: value.brandForeground, presets: colorPresets, onChange: (brand) => update({ brand }) })
6510
+ labels.brand,
6511
+ /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(ColorPicker, { label: labels.brandColor, value: value.brand, foreground: value.brandForeground, presets: colorPresets, onChange: (brand) => update({ brand }) })
6237
6512
  ] }),
6238
6513
  /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)("label", { children: [
6239
- "On brand",
6240
- /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(ColorPicker, { label: "Brand foreground color", value: value.brandForeground, foreground: value.brand, presets: colorPresets, onChange: (brandForeground) => update({ brandForeground }) })
6514
+ labels.onBrand,
6515
+ /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(ColorPicker, { label: labels.onBrandColor, value: value.brandForeground, foreground: value.brand, presets: colorPresets, onChange: (brandForeground) => update({ brandForeground }) })
6241
6516
  ] })
6242
6517
  ] }),
6243
6518
  /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)("label", { className: "uix-field__label", children: [
6244
- "Body font",
6519
+ labels.bodyFont,
6245
6520
  /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("input", { className: "uix-input", value: value.typography?.sans ?? "", onChange: (event) => update({ typography: { ...value.typography, sans: event.currentTarget.value || void 0 } }) })
6246
6521
  ] }),
6247
6522
  /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)("label", { className: "uix-field__label", children: [
6248
- "Heading font",
6523
+ labels.headingFont,
6249
6524
  /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("input", { className: "uix-input", value: value.typography?.heading ?? "", onChange: (event) => update({ typography: { ...value.typography, heading: event.currentTarget.value || void 0 } }) })
6250
6525
  ] }),
6251
6526
  /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)("label", { className: "uix-file-upload uix-brand-profiles__upload", children: [
6252
- "Logo file",
6527
+ labels.logoFile,
6253
6528
  /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("input", { type: "file", accept: "image/*", onChange: onFile })
6254
6529
  ] }),
6255
- /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("button", { type: "button", className: "uix-btn uix-btn--primary", onClick: apply, children: "Apply profile" }),
6530
+ /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("button", { type: "button", className: "uix-btn uix-btn--primary", onClick: apply, children: labels.apply }),
6256
6531
  /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("span", { className: "uix-visually-hidden", "aria-live": "polite", children: announcement })
6257
6532
  ] }),
6258
- /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)("div", { className: "uix-brand-profiles__preview", style: previewStyle, "aria-label": `Live preview of ${value.name}`, children: [
6533
+ /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)("div", { className: "uix-brand-profiles__preview", style: previewStyle, "aria-label": fillLabel(labels.preview, { name: value.name }), children: [
6259
6534
  /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)("div", { className: "uix-brand-profiles__preview-header", children: [
6260
- value.logo ? /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("img", { src: value.logo.src, alt: value.logo.alt }) : /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("span", { className: "uix-brand-profiles__logo-placeholder", children: "Logo" }),
6261
- /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("strong", { children: value.name || "Brand preview" })
6535
+ value.logo ? /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("img", { src: value.logo.src, alt: value.logo.alt }) : /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("span", { className: "uix-brand-profiles__logo-placeholder", children: labels.logoPlaceholder }),
6536
+ /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("strong", { children: value.name || labels.previewTitleFallback })
6262
6537
  ] }),
6263
6538
  /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)("div", { className: "uix-brand-profiles__preview-body", children: [
6264
- /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("h3", { children: "Readable, serializable branding" }),
6265
- /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("p", { children: "Accent, links, rings, and muted states continue through the existing UIx token chain." }),
6266
- /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("button", { type: "button", className: "uix-btn uix-btn--primary", children: "Primary action" }),
6267
- /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("a", { href: "#brand-profile-preview", children: "Preview link" })
6539
+ /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("h3", { children: labels.previewHeading }),
6540
+ /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("p", { children: labels.previewBody }),
6541
+ /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("button", { type: "button", className: "uix-btn uix-btn--primary", children: labels.previewPrimary }),
6542
+ /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("a", { href: "#brand-profile-preview", children: labels.previewLink })
6268
6543
  ] })
6269
6544
  ] })
6270
6545
  ] });
@@ -6272,7 +6547,7 @@ function BrandProfileEditor({
6272
6547
  var BrandProfiles = BrandProfileEditor;
6273
6548
 
6274
6549
  // src/components/DiffViewer.tsx
6275
- var import_react48 = require("react");
6550
+ var import_react49 = require("react");
6276
6551
 
6277
6552
  // src/diff-model.ts
6278
6553
  function isJsonObject(value) {
@@ -6324,14 +6599,33 @@ function summarizeDiff(entries, resolutions = {}) {
6324
6599
 
6325
6600
  // src/components/DiffViewer.tsx
6326
6601
  var import_jsx_runtime72 = require("react/jsx-runtime");
6327
- var GROUPS = [
6328
- { kind: "conflicted", label: "Conflicted" },
6329
- { kind: "changed", label: "Changed" },
6330
- { kind: "added", label: "Added" },
6331
- { kind: "removed", label: "Removed" }
6332
- ];
6333
- var DEFAULT_LABELS = {};
6334
- var renderValue = (value) => value === void 0 ? "Not present" : JSON.stringify(value, null, 2);
6602
+ var DEFAULT_DIFF_VIEWER_LABELS = {
6603
+ base: "Base",
6604
+ current: "Current",
6605
+ incoming: "Incoming",
6606
+ region: "Configuration differences",
6607
+ summary: "Difference summary",
6608
+ noDifferences: "No differences",
6609
+ noDifferencesDetail: "Base, current, and incoming values are aligned.",
6610
+ conflicted: "Conflicted",
6611
+ changed: "Changed",
6612
+ added: "Added",
6613
+ removed: "Removed",
6614
+ resolved: "resolved",
6615
+ pending: "pending",
6616
+ valueFor: "{version} value for {path}",
6617
+ resolve: "Resolve {path}",
6618
+ acceptIncoming: "Accept incoming",
6619
+ keepCurrent: "Keep current",
6620
+ markPending: "Mark pending",
6621
+ resolutionAccept: "accepted",
6622
+ resolutionSkip: "kept",
6623
+ resolutionPending: "pending",
6624
+ notPresent: "Not present",
6625
+ progress: "{resolved} of {total} differences resolved."
6626
+ };
6627
+ var GROUPS = ["conflicted", "changed", "added", "removed"];
6628
+ var renderValue = (value, notPresent) => value === void 0 ? notPresent : JSON.stringify(value, null, 2);
6335
6629
  function DiffViewer({
6336
6630
  base,
6337
6631
  current,
@@ -6339,44 +6633,48 @@ function DiffViewer({
6339
6633
  entries: providedEntries,
6340
6634
  resolutions: controlledResolutions,
6341
6635
  onResolutionChange,
6342
- labels = DEFAULT_LABELS,
6636
+ labels: labelOverrides,
6343
6637
  className
6344
6638
  }) {
6345
- const id = (0, import_react48.useId)();
6346
- const entries = (0, import_react48.useMemo)(() => providedEntries ?? buildThreeWayDiff(base, current, incoming), [providedEntries, base, current, incoming]);
6347
- const [internalResolutions, setInternalResolutions] = (0, import_react48.useState)({});
6639
+ const labels = { ...DEFAULT_DIFF_VIEWER_LABELS, ...labelOverrides };
6640
+ const resolutionLabel = { accept: labels.resolutionAccept, skip: labels.resolutionSkip, pending: labels.resolutionPending };
6641
+ const id = (0, import_react49.useId)();
6642
+ const entries = (0, import_react49.useMemo)(() => providedEntries ?? buildThreeWayDiff(base, current, incoming), [providedEntries, base, current, incoming]);
6643
+ const [internalResolutions, setInternalResolutions] = (0, import_react49.useState)({});
6348
6644
  const resolutions = controlledResolutions ?? internalResolutions;
6349
6645
  const summary = summarizeDiff(entries, resolutions);
6350
6646
  const resolve2 = (path, resolution) => {
6351
6647
  if (controlledResolutions === void 0) setInternalResolutions((currentState) => ({ ...currentState, [path]: resolution }));
6352
6648
  onResolutionChange?.(path, resolution);
6353
6649
  };
6354
- if (entries.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("section", { className: cx("uix-diff-viewer uix-diff-viewer--empty", className), "aria-label": "Configuration differences", children: [
6355
- /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("h3", { children: "No differences" }),
6356
- /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("p", { children: "Base, current, and incoming values are aligned." })
6650
+ if (entries.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("section", { className: cx("uix-diff-viewer uix-diff-viewer--empty", className), "aria-label": labels.region, children: [
6651
+ /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("h3", { children: labels.noDifferences }),
6652
+ /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("p", { children: labels.noDifferencesDetail })
6357
6653
  ] });
6358
- return /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("section", { className: cx("uix-diff-viewer", className), "aria-label": "Configuration differences", children: [
6359
- /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("div", { className: "uix-diff-viewer__summary", "aria-label": "Difference summary", children: [
6360
- GROUPS.map((group) => /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("span", { children: [
6361
- /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("b", { children: summary[group.kind] }),
6654
+ return /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("section", { className: cx("uix-diff-viewer", className), "aria-label": labels.region, children: [
6655
+ /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("div", { className: "uix-diff-viewer__summary", "aria-label": labels.summary, children: [
6656
+ GROUPS.map((kind) => /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("span", { children: [
6657
+ /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("b", { children: summary[kind] }),
6362
6658
  " ",
6363
- group.label.toLowerCase()
6364
- ] }, group.kind)),
6659
+ labels[kind].toLowerCase()
6660
+ ] }, kind)),
6365
6661
  /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("span", { children: [
6366
6662
  /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("b", { children: summary.resolved }),
6367
- " resolved"
6663
+ " ",
6664
+ labels.resolved
6368
6665
  ] }),
6369
6666
  /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("span", { children: [
6370
6667
  /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("b", { children: summary.pending }),
6371
- " pending"
6668
+ " ",
6669
+ labels.pending
6372
6670
  ] })
6373
6671
  ] }),
6374
- GROUPS.map((group) => {
6375
- const grouped = entries.filter((entry) => entry.kind === group.kind);
6672
+ GROUPS.map((kind) => {
6673
+ const grouped = entries.filter((entry) => entry.kind === kind);
6376
6674
  if (!grouped.length) return null;
6377
- return /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("section", { className: "uix-diff-viewer__group", "aria-labelledby": `${id}-${group.kind}`, children: [
6378
- /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("h3", { id: `${id}-${group.kind}`, children: [
6379
- group.label,
6675
+ return /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("section", { className: "uix-diff-viewer__group", "aria-labelledby": `${id}-${kind}`, children: [
6676
+ /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("h3", { id: `${id}-${kind}`, children: [
6677
+ labels[kind],
6380
6678
  " ",
6381
6679
  /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("span", { children: grouped.length })
6382
6680
  ] }),
@@ -6385,27 +6683,22 @@ function DiffViewer({
6385
6683
  return /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("details", { className: "uix-diff-viewer__entry", "data-kind": entry.kind, open: entry.kind === "conflicted", children: [
6386
6684
  /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("summary", { children: [
6387
6685
  /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("code", { children: entry.path }),
6388
- /* @__PURE__ */ (0, import_jsx_runtime72.jsx)(StatusPill, { tone: resolution === "pending" ? "warning" : "success", children: resolution })
6686
+ /* @__PURE__ */ (0, import_jsx_runtime72.jsx)(StatusPill, { tone: resolution === "pending" ? "warning" : "success", children: resolutionLabel[resolution] })
6389
6687
  ] }),
6390
- /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("div", { className: "uix-diff-viewer__versions", children: ["base", "current", "incoming"].map((version) => /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("div", { role: "region", "aria-label": `${labels[version] ?? `${version[0].toUpperCase()}${version.slice(1)}`} value for ${entry.path}`, children: [
6391
- /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("strong", { children: labels[version] ?? `${version[0].toUpperCase()}${version.slice(1)}` }),
6392
- /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("pre", { children: renderValue(entry[version]) })
6688
+ /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("div", { className: "uix-diff-viewer__versions", children: ["base", "current", "incoming"].map((version) => /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("div", { role: "region", "aria-label": fillLabel(labels.valueFor, { version: labels[version], path: entry.path }), children: [
6689
+ /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("strong", { children: labels[version] }),
6690
+ /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("pre", { children: renderValue(entry[version], labels.notPresent) })
6393
6691
  ] }, version)) }),
6394
- /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("div", { className: "uix-diff-viewer__actions", "aria-label": `Resolve ${entry.path}`, children: [
6395
- /* @__PURE__ */ (0, import_jsx_runtime72.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" }),
6396
- /* @__PURE__ */ (0, import_jsx_runtime72.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" }),
6397
- /* @__PURE__ */ (0, import_jsx_runtime72.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" })
6692
+ /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("div", { className: "uix-diff-viewer__actions", "aria-label": fillLabel(labels.resolve, { path: entry.path }), children: [
6693
+ /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("button", { type: "button", className: "uix-btn uix-btn--primary uix-btn--sm", "aria-pressed": resolution === "accept", onClick: () => resolve2(entry.path, "accept"), children: labels.acceptIncoming }),
6694
+ /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", "aria-pressed": resolution === "skip", onClick: () => resolve2(entry.path, "skip"), children: labels.keepCurrent }),
6695
+ /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", "aria-pressed": resolution === "pending", onClick: () => resolve2(entry.path, "pending"), children: labels.markPending })
6398
6696
  ] })
6399
6697
  ] }, entry.path);
6400
6698
  })
6401
- ] }, group.kind);
6699
+ ] }, kind);
6402
6700
  }),
6403
- /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("p", { className: "uix-visually-hidden", "aria-live": "polite", children: [
6404
- summary.resolved,
6405
- " of ",
6406
- entries.length,
6407
- " differences resolved."
6408
- ] })
6701
+ /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("p", { className: "uix-visually-hidden", "aria-live": "polite", children: fillLabel(labels.progress, { resolved: summary.resolved, total: entries.length }) })
6409
6702
  ] });
6410
6703
  }
6411
6704
  // Annotate the CommonJS export names for ESM import in node:
@@ -6441,8 +6734,17 @@ function DiffViewer({
6441
6734
  ContentList,
6442
6735
  ContentListItem,
6443
6736
  CopyButton,
6737
+ DEFAULT_BRAND_PROFILE_EDITOR_LABELS,
6738
+ DEFAULT_BUILDER_CANVAS_LABELS,
6739
+ DEFAULT_COLOR_PICKER_LABELS,
6740
+ DEFAULT_DATE_RANGE_PICKER_LABELS,
6741
+ DEFAULT_DIFF_VIEWER_LABELS,
6742
+ DEFAULT_MATCH_REVIEW_LABELS,
6743
+ DEFAULT_PAGINATION_LABELS,
6444
6744
  DEFAULT_RELATIONSHIP_GRAPH_LABELS,
6745
+ DEFAULT_RULE_BUILDER_LABELS,
6445
6746
  DEFAULT_SAVE_STATUS_LABELS,
6747
+ DEFAULT_SCHEDULING_CALENDAR_LABELS,
6446
6748
  DateRangePicker,
6447
6749
  DescriptionItem,
6448
6750
  DescriptionList,