@reopt-ai/opt-ui 1.10.0 → 1.11.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 (64) hide show
  1. package/COMPONENT_CATALOG.md +177 -66
  2. package/README.md +21 -5
  3. package/dist/core/index.cjs +10 -8
  4. package/dist/core/index.d.cts +3 -3
  5. package/dist/core/index.d.ts +3 -3
  6. package/dist/core/index.js +3 -3
  7. package/dist/docs/02-components/01-core.md +92 -41
  8. package/dist/docs/02-components/02-visuals.md +1 -1
  9. package/dist/docs/02-components/03-shells.md +96 -26
  10. package/dist/docs/02-components/04-surfaces.md +1 -1
  11. package/dist/docs/02-components/index.md +5 -5
  12. package/dist/{field-sidebar-DWfJAJlx.d.ts → field-sidebar-B5SQC35J.d.cts} +214 -3
  13. package/dist/field-sidebar-B5SQC35J.d.cts.map +1 -0
  14. package/dist/{field-sidebar-BXjvV7Cz.js → field-sidebar-BXl5RJ3q.js} +589 -40
  15. package/dist/field-sidebar-BXl5RJ3q.js.map +1 -0
  16. package/dist/{field-sidebar-nwcE9LC4.d.cts → field-sidebar-CBeWE8xX.d.ts} +214 -3
  17. package/dist/field-sidebar-CBeWE8xX.d.ts.map +1 -0
  18. package/dist/{field-sidebar-B6Ndr8HE.cjs → field-sidebar-DAuN4jPV.cjs} +605 -38
  19. package/dist/id-registry.cjs +46 -6
  20. package/dist/id-registry.d.cts.map +1 -1
  21. package/dist/id-registry.d.ts.map +1 -1
  22. package/dist/id-registry.js +46 -6
  23. package/dist/id-registry.js.map +1 -1
  24. package/dist/id-registry.json +97 -12
  25. package/dist/index.cjs +39 -33
  26. package/dist/index.d.cts +10 -6
  27. package/dist/index.d.cts.map +1 -1
  28. package/dist/index.d.ts +10 -6
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +20 -19
  31. package/dist/index.js.map +1 -1
  32. package/dist/{key-pad-menu-DfZbeXhG.d.cts → key-pad-menu-BxTy_u0Q.d.cts} +152 -7
  33. package/dist/key-pad-menu-BxTy_u0Q.d.cts.map +1 -0
  34. package/dist/{key-pad-menu-D5mQcS5v.cjs → key-pad-menu-Cg6rZBie.cjs} +490 -609
  35. package/dist/{key-pad-menu-BsgJMjD4.d.ts → key-pad-menu-u5GTJl9B.d.ts} +152 -7
  36. package/dist/key-pad-menu-u5GTJl9B.d.ts.map +1 -0
  37. package/dist/{key-pad-menu-BQunU20e.js → key-pad-menu-wXkWn1I1.js} +387 -458
  38. package/dist/key-pad-menu-wXkWn1I1.js.map +1 -0
  39. package/dist/meta.cjs +486 -9
  40. package/dist/meta.d.cts.map +1 -1
  41. package/dist/meta.d.ts.map +1 -1
  42. package/dist/meta.js +486 -9
  43. package/dist/meta.js.map +1 -1
  44. package/dist/shells/index.cjs +4 -1
  45. package/dist/shells/index.d.cts +2 -2
  46. package/dist/shells/index.d.ts +2 -2
  47. package/dist/shells/index.js +2 -2
  48. package/dist/{text-truncate-CxdzAnhS.cjs → text-truncate-BZdL1k5Z.cjs} +474 -83
  49. package/dist/{text-truncate-j6X3z7Pv.js → text-truncate-C17HHCR-.js} +405 -86
  50. package/dist/text-truncate-C17HHCR-.js.map +1 -0
  51. package/dist/{text-truncate-BGr7bB_8.d.ts → text-truncate-DW78lg4S.d.cts} +18 -3
  52. package/dist/text-truncate-DW78lg4S.d.cts.map +1 -0
  53. package/dist/{text-truncate-BGr7bB_8.d.cts → text-truncate-DW78lg4S.d.ts} +18 -3
  54. package/dist/text-truncate-DW78lg4S.d.ts.map +1 -0
  55. package/package.json +1 -1
  56. package/dist/field-sidebar-BXjvV7Cz.js.map +0 -1
  57. package/dist/field-sidebar-DWfJAJlx.d.ts.map +0 -1
  58. package/dist/field-sidebar-nwcE9LC4.d.cts.map +0 -1
  59. package/dist/key-pad-menu-BQunU20e.js.map +0 -1
  60. package/dist/key-pad-menu-BsgJMjD4.d.ts.map +0 -1
  61. package/dist/key-pad-menu-DfZbeXhG.d.cts.map +0 -1
  62. package/dist/text-truncate-BGr7bB_8.d.cts.map +0 -1
  63. package/dist/text-truncate-BGr7bB_8.d.ts.map +0 -1
  64. package/dist/text-truncate-j6X3z7Pv.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- const require_text_truncate = require("./text-truncate-CxdzAnhS.cjs");
2
+ const require_text_truncate = require("./text-truncate-BZdL1k5Z.cjs");
3
3
  let react_jsx_runtime = require("react/jsx-runtime");
4
4
  let react = require("react");
5
5
  react = require_text_truncate.__toESM(react, 1);
@@ -254,7 +254,7 @@ function CommandPaletteTrigger({ commands, onSelect, labels }) {
254
254
  }
255
255
  //#endregion
256
256
  //#region src/shells/dashboard-grid.tsx
257
- const defaultLabels$18 = { ariaLabel: "대시보드 통계" };
257
+ const defaultLabels$21 = { ariaLabel: "대시보드 통계" };
258
258
  const MIN_DASHBOARD_COLUMNS = 1;
259
259
  const MAX_DASHBOARD_COLUMNS = 12;
260
260
  function normalizeDashboardColumns(columns) {
@@ -263,7 +263,7 @@ function normalizeDashboardColumns(columns) {
263
263
  }
264
264
  /** Renders the dashboard grid component. */
265
265
  function DashboardGrid({ stats, columns = 3, labels: customLabels }) {
266
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$18), customLabels);
266
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$21), customLabels);
267
267
  const columnCount = normalizeDashboardColumns(columns);
268
268
  const rowStyle = { "--opt-dashboard-grid-columns": `repeat(${columnCount}, minmax(0, 1fr))` };
269
269
  const rows = [];
@@ -291,13 +291,13 @@ function DashboardGrid({ stats, columns = 3, labels: customLabels }) {
291
291
  }
292
292
  //#endregion
293
293
  //#region src/shells/search-combobox.tsx
294
- const defaultLabels$17 = {
294
+ const defaultLabels$20 = {
295
295
  noResults: "결과가 없습니다.",
296
296
  loading: "Loading…"
297
297
  };
298
298
  /** Renders the search combobox component. */
299
299
  function SearchCombobox({ items, placeholder = "검색...", label = "검색", onSelect, labels: customLabels, async: isAsync = false, onSearch, debounceMs = 300 }) {
300
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$17), customLabels);
300
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$20), customLabels);
301
301
  const inputId = (0, react.useId)();
302
302
  const [query, setQuery] = (0, react.useState)("");
303
303
  const [asyncResults, setAsyncResults] = (0, react.useState)([]);
@@ -395,14 +395,14 @@ function SearchCombobox({ items, placeholder = "검색...", label = "검색", on
395
395
  }
396
396
  //#endregion
397
397
  //#region src/shells/status-select.tsx
398
- const defaultLabels$16 = {
398
+ const defaultLabels$19 = {
399
399
  selectLabel: "상태",
400
400
  emptyMessage: "사용 가능한 상태 옵션이 없습니다."
401
401
  };
402
402
  /** Renders the status select component. */
403
403
  function StatusSelect({ options, value: controlledValue, onChange, label, labels: customLabels }) {
404
404
  var _options$0$value, _options$;
405
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$16), customLabels);
405
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$19), customLabels);
406
406
  const resolvedLabel = label !== null && label !== void 0 ? label : labels.selectLabel;
407
407
  const [internalValue, setInternalValue] = (0, react.useState)((_options$0$value = (_options$ = options[0]) === null || _options$ === void 0 ? void 0 : _options$.value) !== null && _options$0$value !== void 0 ? _options$0$value : "");
408
408
  const value = controlledValue !== null && controlledValue !== void 0 ? controlledValue : internalValue;
@@ -469,7 +469,7 @@ function StatusSelect({ options, value: controlledValue, onChange, label, labels
469
469
  }
470
470
  //#endregion
471
471
  //#region src/shells/app-menubar.tsx
472
- const defaultLabels$15 = { ariaLabel: "Application menu" };
472
+ const defaultLabels$18 = { ariaLabel: "Application menu" };
473
473
  function MenuSection({ menu }) {
474
474
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_text_truncate.MenuRoot, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_text_truncate.MenuTrigger, { children: [menu.label, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.MenuButtonArrow, {})] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.MenuPopover, { children: menu.items.map((item, i) => {
475
475
  if (item.type === "separator") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.MenuSeparator, {}, `sep-${i}`);
@@ -513,7 +513,7 @@ function MenuSection({ menu }) {
513
513
  }
514
514
  /** Renders the app menubar component. */
515
515
  function AppMenubar({ menus, labels: customLabels }) {
516
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$15), customLabels);
516
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$18), customLabels);
517
517
  if (menus.length === 0) return null;
518
518
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.MenubarRoot, {
519
519
  "data-opt-id": "BGKHW",
@@ -552,10 +552,10 @@ const sectionStyles = {
552
552
  Templates: "text-text-tertiary"
553
553
  };
554
554
  const defaultSectionStyle = "text-text-secondary";
555
- const defaultLabels$14 = { ariaLabel: "문서 네비게이션" };
555
+ const defaultLabels$17 = { ariaLabel: "문서 네비게이션" };
556
556
  /** Renders the sidebar nav component. */
557
557
  function SidebarNav({ items, labels: customLabels }) {
558
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$14), customLabels);
558
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$17), customLabels);
559
559
  const pathname = (0, next_navigation.usePathname)();
560
560
  const activeItem = items.find((item) => item.href === pathname);
561
561
  const [activeId, setActiveId] = (0, react.useState)(activeItem ? domId(activeItem.id) : void 0);
@@ -603,7 +603,7 @@ function SidebarNav({ items, labels: customLabels }) {
603
603
  }
604
604
  //#endregion
605
605
  //#region src/shells/editor-toolbar.tsx
606
- const defaultLabels$13 = { ariaLabel: "텍스트 편집기 툴바" };
606
+ const defaultLabels$16 = { ariaLabel: "텍스트 편집기 툴바" };
607
607
  function ToolbarButtonWithTooltip({ button, active, onAction }) {
608
608
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_text_truncate.TooltipProvider, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.TooltipAnchor, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.ToolbarButton, {
609
609
  className: `text-text-primary data-[active-item]:bg-bg-subtle data-[focus-visible]:ring-ring rounded-md p-2 transition-colors outline-none data-[focus-visible]:ring-2 ${active ? "bg-bg-muted" : "hover:bg-bg-subtle"}`,
@@ -613,7 +613,7 @@ function ToolbarButtonWithTooltip({ button, active, onAction }) {
613
613
  }
614
614
  /** Renders the editor toolbar component. */
615
615
  function EditorToolbar({ buttons, onAction, activeButtons = [], labels: customLabels }) {
616
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$13), customLabels);
616
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$16), customLabels);
617
617
  const groups = [];
618
618
  let current = [];
619
619
  for (const btn of buttons) {
@@ -646,14 +646,14 @@ function EditorToolbar({ buttons, onAction, activeButtons = [], labels: customLa
646
646
  }
647
647
  //#endregion
648
648
  //#region src/shells/content-tabs.tsx
649
- const defaultLabels$12 = {
649
+ const defaultLabels$15 = {
650
650
  ariaLabel: "콘텐츠 탭",
651
651
  emptyMessage: "표시할 탭이 없습니다."
652
652
  };
653
653
  /** Renders the content tabs component. */
654
654
  function ContentTabs({ tabs, defaultTabId, labels: customLabels }) {
655
655
  var _tabs$, _tabs$find;
656
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$12), customLabels);
656
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$15), customLabels);
657
657
  const [selectedTabId, setSelectedTabId] = (0, react.useState)(defaultTabId !== null && defaultTabId !== void 0 ? defaultTabId : (_tabs$ = tabs[0]) === null || _tabs$ === void 0 ? void 0 : _tabs$.id);
658
658
  if (tabs.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
659
659
  className: "text-text-secondary text-sm",
@@ -685,13 +685,13 @@ function ContentTabs({ tabs, defaultTabId, labels: customLabels }) {
685
685
  }
686
686
  //#endregion
687
687
  //#region src/shells/faq-accordion.tsx
688
- const defaultLabels$11 = {
688
+ const defaultLabels$14 = {
689
689
  ariaLabel: "자주 묻는 질문",
690
690
  emptyMessage: "등록된 FAQ가 없습니다."
691
691
  };
692
692
  /** Renders the faq accordion component. */
693
693
  function FaqAccordion({ items, labels: customLabels }) {
694
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$11), customLabels);
694
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$14), customLabels);
695
695
  if (items.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
696
696
  className: "text-text-secondary text-sm",
697
697
  "data-opt-id": "QRBHV",
@@ -731,14 +731,14 @@ function FaqAccordion({ items, labels: customLabels }) {
731
731
  }
732
732
  //#endregion
733
733
  //#region src/shells/settings-form.tsx
734
- const defaultLabels$10 = {
734
+ const defaultLabels$13 = {
735
735
  formAriaLabel: "설정 폼",
736
736
  submitButton: "저장",
737
737
  successMessage: "설정이 저장되었습니다."
738
738
  };
739
739
  /** Renders the settings form component. */
740
740
  function SettingsForm({ fields, onSubmit, labels: customLabels }) {
741
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$10), customLabels);
741
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$13), customLabels);
742
742
  const [submitted, setSubmitted] = (0, react.useState)(false);
743
743
  const defaultValues = {};
744
744
  for (const field of fields) {
@@ -1064,7 +1064,7 @@ function ProjectSwitcher({ projects, value, onChange, placeholder = "프로젝
1064
1064
  ProjectSwitcher.displayName = "ProjectSwitcher";
1065
1065
  //#endregion
1066
1066
  //#region src/shells/env-panel.tsx
1067
- const defaultLabels$9 = {
1067
+ const defaultLabels$12 = {
1068
1068
  varCount: (count) => `${count}개 변수`,
1069
1069
  envVarsLabel: (env) => `${env} 환경 변수`,
1070
1070
  secretBadge: "secret"
@@ -1076,7 +1076,7 @@ const envDot = {
1076
1076
  };
1077
1077
  /** Renders the env panel component. */
1078
1078
  function EnvPanel({ groups, labels: customLabels }) {
1079
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$9), customLabels);
1079
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$12), customLabels);
1080
1080
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1081
1081
  className: `rounded-lg ${require_text_truncate.OPT_BORDER} divide-y ${require_text_truncate.OPT_DIVIDER} bg-surface`,
1082
1082
  "data-opt-id": "ZBDWK",
@@ -1149,7 +1149,7 @@ function EnvPanel({ groups, labels: customLabels }) {
1149
1149
  }
1150
1150
  //#endregion
1151
1151
  //#region src/shells/branch-select.tsx
1152
- const defaultLabels$8 = {
1152
+ const defaultLabels$11 = {
1153
1153
  selectLabel: "브랜치",
1154
1154
  emptySelectionLabel: "선택...",
1155
1155
  emptyMessage: "사용 가능한 브랜치가 없습니다.",
@@ -1158,7 +1158,7 @@ const defaultLabels$8 = {
1158
1158
  /** Renders the branch select component. */
1159
1159
  function BranchSelect({ branches, value: controlledValue, onChange, label, labels: customLabels }) {
1160
1160
  var _ref, _defaultBranch$name, _branches$, _selected$name;
1161
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$8), customLabels);
1161
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$11), customLabels);
1162
1162
  const resolvedLabel = label !== null && label !== void 0 ? label : labels.selectLabel;
1163
1163
  const defaultBranch = branches.find((b) => b.isDefault);
1164
1164
  const [internalValue, setInternalValue] = (0, react.useState)((_ref = (_defaultBranch$name = defaultBranch === null || defaultBranch === void 0 ? void 0 : defaultBranch.name) !== null && _defaultBranch$name !== void 0 ? _defaultBranch$name : (_branches$ = branches[0]) === null || _branches$ === void 0 ? void 0 : _branches$.name) !== null && _ref !== void 0 ? _ref : "");
@@ -1267,7 +1267,7 @@ function BranchSelect({ branches, value: controlledValue, onChange, label, label
1267
1267
  BranchSelect.displayName = "BranchSelect";
1268
1268
  //#endregion
1269
1269
  //#region src/shells/domain-table.tsx
1270
- const defaultLabels$7 = {
1270
+ const defaultLabels$10 = {
1271
1271
  domain: "Domain",
1272
1272
  status: "Status",
1273
1273
  ssl: "SSL",
@@ -1297,7 +1297,7 @@ const sslLabel = {
1297
1297
  };
1298
1298
  /** Renders the domain table component. */
1299
1299
  function DomainTable({ domains, title = "도메인", labels: customLabels }) {
1300
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$7), customLabels);
1300
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$10), customLabels);
1301
1301
  if (domains.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1302
1302
  className: "gap-group flex flex-col",
1303
1303
  "data-opt-id": "A7SRM",
@@ -1903,7 +1903,7 @@ function NotificationToast({ toast, onDismiss, defaultDuration = 5e3 }) {
1903
1903
  }
1904
1904
  //#endregion
1905
1905
  //#region src/shells/failure-list.tsx
1906
- const defaultLabels$6 = {
1906
+ const defaultLabels$9 = {
1907
1907
  title: "최근 실패",
1908
1908
  emptyMessage: "실패 항목이 없습니다",
1909
1909
  moreItems: "{count}개 더 보기",
@@ -2002,20 +2002,20 @@ function FailureItemRow({ failure, isLast, onFailureClick, renderActions, merged
2002
2002
  function FailureList({ failures, title, maxItems, grouped = false, onFailureClick, onShowMore, labels: customLabels, locale = "ko-KR", loading = false, renderActions, className }) {
2003
2003
  var _customLabels$timeAgo, _customLabels$timeAgo2, _customLabels$timeAgo3, _customLabels$timeAgo4, _customLabels$timeAgo5, _customLabels$timeAgo6, _customLabels$timeAgo7, _customLabels$timeAgo8, _customLabels$severit, _customLabels$severit2, _customLabels$severit3, _customLabels$severit4, _customLabels$severit5, _customLabels$severit6, _customLabels$title, _customLabels$emptyMe, _customLabels$moreIte;
2004
2004
  const mergedTimeAgo = {
2005
- justNow: (_customLabels$timeAgo = customLabels === null || customLabels === void 0 || (_customLabels$timeAgo2 = customLabels.timeAgo) === null || _customLabels$timeAgo2 === void 0 ? void 0 : _customLabels$timeAgo2.justNow) !== null && _customLabels$timeAgo !== void 0 ? _customLabels$timeAgo : defaultLabels$6.timeAgo.justNow,
2006
- minutesAgo: (_customLabels$timeAgo3 = customLabels === null || customLabels === void 0 || (_customLabels$timeAgo4 = customLabels.timeAgo) === null || _customLabels$timeAgo4 === void 0 ? void 0 : _customLabels$timeAgo4.minutesAgo) !== null && _customLabels$timeAgo3 !== void 0 ? _customLabels$timeAgo3 : defaultLabels$6.timeAgo.minutesAgo,
2007
- hoursAgo: (_customLabels$timeAgo5 = customLabels === null || customLabels === void 0 || (_customLabels$timeAgo6 = customLabels.timeAgo) === null || _customLabels$timeAgo6 === void 0 ? void 0 : _customLabels$timeAgo6.hoursAgo) !== null && _customLabels$timeAgo5 !== void 0 ? _customLabels$timeAgo5 : defaultLabels$6.timeAgo.hoursAgo,
2008
- daysAgo: (_customLabels$timeAgo7 = customLabels === null || customLabels === void 0 || (_customLabels$timeAgo8 = customLabels.timeAgo) === null || _customLabels$timeAgo8 === void 0 ? void 0 : _customLabels$timeAgo8.daysAgo) !== null && _customLabels$timeAgo7 !== void 0 ? _customLabels$timeAgo7 : defaultLabels$6.timeAgo.daysAgo
2005
+ justNow: (_customLabels$timeAgo = customLabels === null || customLabels === void 0 || (_customLabels$timeAgo2 = customLabels.timeAgo) === null || _customLabels$timeAgo2 === void 0 ? void 0 : _customLabels$timeAgo2.justNow) !== null && _customLabels$timeAgo !== void 0 ? _customLabels$timeAgo : defaultLabels$9.timeAgo.justNow,
2006
+ minutesAgo: (_customLabels$timeAgo3 = customLabels === null || customLabels === void 0 || (_customLabels$timeAgo4 = customLabels.timeAgo) === null || _customLabels$timeAgo4 === void 0 ? void 0 : _customLabels$timeAgo4.minutesAgo) !== null && _customLabels$timeAgo3 !== void 0 ? _customLabels$timeAgo3 : defaultLabels$9.timeAgo.minutesAgo,
2007
+ hoursAgo: (_customLabels$timeAgo5 = customLabels === null || customLabels === void 0 || (_customLabels$timeAgo6 = customLabels.timeAgo) === null || _customLabels$timeAgo6 === void 0 ? void 0 : _customLabels$timeAgo6.hoursAgo) !== null && _customLabels$timeAgo5 !== void 0 ? _customLabels$timeAgo5 : defaultLabels$9.timeAgo.hoursAgo,
2008
+ daysAgo: (_customLabels$timeAgo7 = customLabels === null || customLabels === void 0 || (_customLabels$timeAgo8 = customLabels.timeAgo) === null || _customLabels$timeAgo8 === void 0 ? void 0 : _customLabels$timeAgo8.daysAgo) !== null && _customLabels$timeAgo7 !== void 0 ? _customLabels$timeAgo7 : defaultLabels$9.timeAgo.daysAgo
2009
2009
  };
2010
2010
  const mergedSeverityLabels = {
2011
- critical: (_customLabels$severit = customLabels === null || customLabels === void 0 || (_customLabels$severit2 = customLabels.severityLabels) === null || _customLabels$severit2 === void 0 ? void 0 : _customLabels$severit2.critical) !== null && _customLabels$severit !== void 0 ? _customLabels$severit : defaultLabels$6.severityLabels.critical,
2012
- error: (_customLabels$severit3 = customLabels === null || customLabels === void 0 || (_customLabels$severit4 = customLabels.severityLabels) === null || _customLabels$severit4 === void 0 ? void 0 : _customLabels$severit4.error) !== null && _customLabels$severit3 !== void 0 ? _customLabels$severit3 : defaultLabels$6.severityLabels.error,
2013
- warning: (_customLabels$severit5 = customLabels === null || customLabels === void 0 || (_customLabels$severit6 = customLabels.severityLabels) === null || _customLabels$severit6 === void 0 ? void 0 : _customLabels$severit6.warning) !== null && _customLabels$severit5 !== void 0 ? _customLabels$severit5 : defaultLabels$6.severityLabels.warning
2011
+ critical: (_customLabels$severit = customLabels === null || customLabels === void 0 || (_customLabels$severit2 = customLabels.severityLabels) === null || _customLabels$severit2 === void 0 ? void 0 : _customLabels$severit2.critical) !== null && _customLabels$severit !== void 0 ? _customLabels$severit : defaultLabels$9.severityLabels.critical,
2012
+ error: (_customLabels$severit3 = customLabels === null || customLabels === void 0 || (_customLabels$severit4 = customLabels.severityLabels) === null || _customLabels$severit4 === void 0 ? void 0 : _customLabels$severit4.error) !== null && _customLabels$severit3 !== void 0 ? _customLabels$severit3 : defaultLabels$9.severityLabels.error,
2013
+ warning: (_customLabels$severit5 = customLabels === null || customLabels === void 0 || (_customLabels$severit6 = customLabels.severityLabels) === null || _customLabels$severit6 === void 0 ? void 0 : _customLabels$severit6.warning) !== null && _customLabels$severit5 !== void 0 ? _customLabels$severit5 : defaultLabels$9.severityLabels.warning
2014
2014
  };
2015
2015
  const labels = {
2016
- title: (_customLabels$title = customLabels === null || customLabels === void 0 ? void 0 : customLabels.title) !== null && _customLabels$title !== void 0 ? _customLabels$title : defaultLabels$6.title,
2017
- emptyMessage: (_customLabels$emptyMe = customLabels === null || customLabels === void 0 ? void 0 : customLabels.emptyMessage) !== null && _customLabels$emptyMe !== void 0 ? _customLabels$emptyMe : defaultLabels$6.emptyMessage,
2018
- moreItems: (_customLabels$moreIte = customLabels === null || customLabels === void 0 ? void 0 : customLabels.moreItems) !== null && _customLabels$moreIte !== void 0 ? _customLabels$moreIte : defaultLabels$6.moreItems
2016
+ title: (_customLabels$title = customLabels === null || customLabels === void 0 ? void 0 : customLabels.title) !== null && _customLabels$title !== void 0 ? _customLabels$title : defaultLabels$9.title,
2017
+ emptyMessage: (_customLabels$emptyMe = customLabels === null || customLabels === void 0 ? void 0 : customLabels.emptyMessage) !== null && _customLabels$emptyMe !== void 0 ? _customLabels$emptyMe : defaultLabels$9.emptyMessage,
2018
+ moreItems: (_customLabels$moreIte = customLabels === null || customLabels === void 0 ? void 0 : customLabels.moreItems) !== null && _customLabels$moreIte !== void 0 ? _customLabels$moreIte : defaultLabels$9.moreItems
2019
2019
  };
2020
2020
  const displayTitle = title !== null && title !== void 0 ? title : labels.title;
2021
2021
  const allItems = maxItems ? failures.slice(0, maxItems) : failures;
@@ -2091,7 +2091,7 @@ function renderGrouped(items, remainingCount, rowProps, labels, onShowMore) {
2091
2091
  }
2092
2092
  //#endregion
2093
2093
  //#region src/shells/key-value-editor.tsx
2094
- const defaultLabels$5 = {
2094
+ const defaultLabels$8 = {
2095
2095
  keyPlaceholder: "키",
2096
2096
  valuePlaceholder: "값",
2097
2097
  addButton: "추가",
@@ -2105,7 +2105,7 @@ function generateId() {
2105
2105
  }
2106
2106
  /** Renders the key value editor component. */
2107
2107
  function KeyValueEditor({ value: controlledValue, defaultValue, onChange, showSecretToggle = false, maxEntries, readOnly = false, labels: customLabels, renderActions, validateKey, className }) {
2108
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$5), customLabels);
2108
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$8), customLabels);
2109
2109
  const [internalEntries, setInternalEntries] = (0, react.useState)(() => defaultValue !== null && defaultValue !== void 0 ? defaultValue : []);
2110
2110
  const entries = controlledValue !== null && controlledValue !== void 0 ? controlledValue : internalEntries;
2111
2111
  const keyInputRef = (0, react.useRef)(null);
@@ -2401,6 +2401,555 @@ function WorkflowCanvas({ steps, connections = [], rows = 9, cols = 9, cellSize
2401
2401
  }
2402
2402
  WorkflowCanvas.displayName = "WorkflowCanvas";
2403
2403
  //#endregion
2404
+ //#region src/shells/event-timeline.tsx
2405
+ const defaultLabels$7 = {
2406
+ emptyTitle: "No events yet",
2407
+ emptyDescription: "Events appear here as soon as they arrive.",
2408
+ expand: "Show properties",
2409
+ collapse: "Hide properties",
2410
+ loadMore: "Load more",
2411
+ gap: (duration) => `+${duration}`,
2412
+ properties: "Event properties",
2413
+ openGroup: (title) => `Open ${title}`
2414
+ };
2415
+ /**
2416
+ * A line of text and the time it happened have to read as one row. Past about
2417
+ * this width the eye stops connecting them, so the rail is capped rather than
2418
+ * stretched to whatever the page allows. Override with `className` where a
2419
+ * layout genuinely needs the full width.
2420
+ */
2421
+ const TIMELINE_WIDTH = "max-w-5xl";
2422
+ const toneDot = {
2423
+ default: "bg-text-tertiary",
2424
+ accent: "bg-accent",
2425
+ success: "bg-success",
2426
+ warning: "bg-warning",
2427
+ danger: "bg-danger"
2428
+ };
2429
+ function toMillis(timestamp) {
2430
+ const parsed = Date.parse(timestamp);
2431
+ return Number.isNaN(parsed) ? 0 : parsed;
2432
+ }
2433
+ function stringifyValue(value) {
2434
+ if (value === null || value === void 0) return "—";
2435
+ if (typeof value === "string") return value;
2436
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
2437
+ return JSON.stringify(value);
2438
+ }
2439
+ function PropertyTable({ properties, ariaLabel }) {
2440
+ const entries = Object.entries(properties);
2441
+ if (entries.length === 0) return null;
2442
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dl", {
2443
+ className: require_text_truncate.cn(require_text_truncate.OPT_BORDER, "gap-element bg-bg-subtle mt-2 grid grid-cols-1 rounded-lg p-2.5 sm:grid-cols-2"),
2444
+ "aria-label": ariaLabel,
2445
+ children: entries.map(([key, value]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2446
+ className: "flex min-w-0 flex-col",
2447
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", {
2448
+ className: require_text_truncate.cn(require_text_truncate.OPT_TEXT_TERTIARY, "truncate text-xs"),
2449
+ children: key
2450
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", {
2451
+ className: require_text_truncate.cn(require_text_truncate.OPT_TEXT_PRIMARY, "truncate text-xs"),
2452
+ children: stringifyValue(value)
2453
+ })]
2454
+ }, key))
2455
+ });
2456
+ }
2457
+ function TimelineRow({ item, gapLabel, labels, onItemSelect, isLast }) {
2458
+ var _item$tone, _item$content, _item$properties;
2459
+ const [open, setOpen] = (0, react.useState)(false);
2460
+ const expandable = Boolean(item.content || item.properties);
2461
+ const toggle = (0, react.useCallback)(() => setOpen((value) => !value), []);
2462
+ const headline = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2463
+ className: require_text_truncate.cn(require_text_truncate.OPT_TEXT_PRIMARY, "truncate text-sm font-medium"),
2464
+ children: item.name
2465
+ });
2466
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
2467
+ className: "relative flex gap-3 pb-4 last:pb-0",
2468
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2469
+ className: "flex shrink-0 flex-col items-center",
2470
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2471
+ className: require_text_truncate.cn("mt-1.5 size-2 shrink-0 rounded-full", toneDot[(_item$tone = item.tone) !== null && _item$tone !== void 0 ? _item$tone : "default"]),
2472
+ "aria-hidden": "true"
2473
+ }), !isLast && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2474
+ className: "bg-border mt-1 w-px grow",
2475
+ "aria-hidden": "true"
2476
+ })]
2477
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2478
+ className: "flex min-w-0 grow flex-col",
2479
+ children: [
2480
+ gapLabel && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2481
+ className: require_text_truncate.cn("text-text-tertiary", "mb-1 text-xs tabular-nums"),
2482
+ children: labels.gap(gapLabel)
2483
+ }),
2484
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2485
+ className: "flex min-w-0 items-baseline gap-2",
2486
+ children: [
2487
+ item.icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2488
+ className: "text-text-tertiary",
2489
+ "aria-hidden": "true",
2490
+ children: item.icon
2491
+ }),
2492
+ onItemSelect ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2493
+ type: "button",
2494
+ onClick: () => onItemSelect(item),
2495
+ className: require_text_truncate.cn(require_text_truncate.OPT_FOCUS_VISIBLE, "hover:text-accent min-w-0 truncate rounded text-left"),
2496
+ children: headline
2497
+ }) : headline,
2498
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("time", {
2499
+ dateTime: item.timestamp,
2500
+ className: require_text_truncate.cn(require_text_truncate.OPT_TEXT_TERTIARY, "ml-auto shrink-0 text-xs tabular-nums"),
2501
+ children: require_text_truncate.formatOptTime(item.timestamp)
2502
+ })
2503
+ ]
2504
+ }),
2505
+ item.detail && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2506
+ className: require_text_truncate.cn("text-text-secondary", "truncate text-xs"),
2507
+ children: item.detail
2508
+ }),
2509
+ expandable && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2510
+ type: "button",
2511
+ onClick: toggle,
2512
+ "aria-expanded": open,
2513
+ className: require_text_truncate.cn("outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-ring-offset", "text-text-tertiary", "hover:text-text-primary mt-1 flex w-fit items-center gap-1 rounded text-xs"),
2514
+ children: [open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronDown, {
2515
+ className: "size-3",
2516
+ "aria-hidden": "true"
2517
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronRight, {
2518
+ className: "size-3",
2519
+ "aria-hidden": "true"
2520
+ }), open ? labels.collapse : labels.expand]
2521
+ }), open && ((_item$content = item.content) !== null && _item$content !== void 0 ? _item$content : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PropertyTable, {
2522
+ properties: (_item$properties = item.properties) !== null && _item$properties !== void 0 ? _item$properties : {},
2523
+ ariaLabel: labels.properties
2524
+ }))] })
2525
+ ]
2526
+ })]
2527
+ });
2528
+ }
2529
+ /** Builds the gap label for each row, given the reading order. */
2530
+ function useGapLabels(items, showGaps, order, minGapMs, locale) {
2531
+ return (0, react.useMemo)(() => {
2532
+ if (!showGaps) return items.map(() => null);
2533
+ return items.map((item, index) => {
2534
+ const neighbour = order === "desc" ? items[index + 1] : items[index - 1];
2535
+ if (!neighbour) return null;
2536
+ const delta = Math.abs(toMillis(item.timestamp) - toMillis(neighbour.timestamp));
2537
+ if (delta < minGapMs) return null;
2538
+ return (0, _reopt_ai_opt_charts.formatDuration)(delta, locale);
2539
+ });
2540
+ }, [
2541
+ items,
2542
+ showGaps,
2543
+ order,
2544
+ minGapMs,
2545
+ locale
2546
+ ]);
2547
+ }
2548
+ function TimelineList({ items, showGaps, order, minGapMs, locale, labels, onItemSelect }) {
2549
+ const gaps = useGapLabels(items, showGaps, order, minGapMs, locale);
2550
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ol", {
2551
+ className: "flex flex-col",
2552
+ children: items.map((item, index) => {
2553
+ var _gaps$index;
2554
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TimelineRow, {
2555
+ item,
2556
+ gapLabel: (_gaps$index = gaps[index]) !== null && _gaps$index !== void 0 ? _gaps$index : null,
2557
+ labels,
2558
+ onItemSelect,
2559
+ isLast: index === items.length - 1
2560
+ }, item.id);
2561
+ })
2562
+ });
2563
+ }
2564
+ function GroupBlock({ group, showGaps, order, minGapMs, locale, labels, onItemSelect }) {
2565
+ var _group$defaultOpen;
2566
+ const [open, setOpen] = (0, react.useState)((_group$defaultOpen = group.defaultOpen) !== null && _group$defaultOpen !== void 0 ? _group$defaultOpen : true);
2567
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2568
+ className: require_text_truncate.cn(require_text_truncate.OPT_BORDER, "rounded-xl"),
2569
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2570
+ className: "flex items-center gap-2 px-3 py-2",
2571
+ children: [
2572
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2573
+ type: "button",
2574
+ onClick: () => setOpen((value) => !value),
2575
+ "aria-expanded": open,
2576
+ className: require_text_truncate.cn(require_text_truncate.OPT_FOCUS_VISIBLE, "flex min-w-0 grow items-center gap-2 rounded text-left"),
2577
+ children: [
2578
+ open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronDown, {
2579
+ className: require_text_truncate.cn(require_text_truncate.OPT_TEXT_TERTIARY, "size-4 shrink-0"),
2580
+ "aria-hidden": "true"
2581
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronRight, {
2582
+ className: require_text_truncate.cn(require_text_truncate.OPT_TEXT_TERTIARY, "size-4 shrink-0"),
2583
+ "aria-hidden": "true"
2584
+ }),
2585
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2586
+ className: require_text_truncate.cn(require_text_truncate.OPT_TEXT_PRIMARY, "truncate text-sm font-medium"),
2587
+ children: group.title
2588
+ }),
2589
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Badge, {
2590
+ size: "sm",
2591
+ variant: "default",
2592
+ className: "shrink-0",
2593
+ children: group.items.length
2594
+ })
2595
+ ]
2596
+ }),
2597
+ group.meta && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2598
+ className: require_text_truncate.cn("text-text-tertiary", "shrink-0 text-xs"),
2599
+ children: group.meta
2600
+ }),
2601
+ group.href && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
2602
+ href: group.href,
2603
+ className: require_text_truncate.cn("outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-ring-offset", "text-accent shrink-0 rounded text-xs"),
2604
+ children: labels.openGroup(typeof group.title === "string" ? group.title : group.id)
2605
+ })
2606
+ ]
2607
+ }), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2608
+ className: "border-border-subtle border-t px-3 py-3",
2609
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TimelineList, {
2610
+ items: group.items,
2611
+ showGaps,
2612
+ order,
2613
+ minGapMs,
2614
+ locale,
2615
+ labels,
2616
+ onItemSelect
2617
+ })
2618
+ })]
2619
+ });
2620
+ }
2621
+ /**
2622
+ * EventTimeline — a sequence of events, read as a sequence.
2623
+ *
2624
+ * A table of events answers "what happened" and hides "how it went": the rows
2625
+ * are evenly spaced no matter whether they were two seconds or two hours
2626
+ * apart, and the payload that would explain a row lives in a column nobody
2627
+ * widened. This renders the same data as an ordered rail, labels the elapsed
2628
+ * time between adjacent events, and opens each payload in place. Grouping by
2629
+ * session restores the boundary that makes a history legible.
2630
+ *
2631
+ * @example
2632
+ * ```tsx
2633
+ * <EventTimeline
2634
+ * groups={sessions.map((session) => ({
2635
+ * id: session.id,
2636
+ * title: session.entryPath,
2637
+ * meta: `${session.duration} · ${session.events} events`,
2638
+ * items: session.events,
2639
+ * }))}
2640
+ * showGaps
2641
+ * />
2642
+ * ```
2643
+ */
2644
+ function EventTimeline({ items, groups, showGaps = true, order = "desc", minGapMs = 1e3, loading = false, hasMore = false, onLoadMore, onItemSelect, locale = require_text_truncate.OPT_UI_DEFAULT_LOCALE, labels: customLabels, className }) {
2645
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$7), customLabels);
2646
+ const flatItems = items !== null && items !== void 0 ? items : [];
2647
+ const hasContent = groups ? groups.length > 0 : flatItems.length > 0;
2648
+ if (loading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2649
+ className: require_text_truncate.cn(TIMELINE_WIDTH, "flex flex-col gap-3", className),
2650
+ "data-opt-id": "H6S6T",
2651
+ "data-opt-slug": "event-timeline.EventTimeline",
2652
+ children: Array.from({ length: 5 }).map((_, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2653
+ className: "flex gap-3",
2654
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Skeleton, { className: "mt-1.5 size-2 rounded-full" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2655
+ className: "flex grow flex-col gap-1.5",
2656
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Skeleton, { className: "h-4 w-40" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Skeleton, { className: "h-3 w-64" })]
2657
+ })]
2658
+ }, index))
2659
+ });
2660
+ if (!hasContent) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2661
+ className: require_text_truncate.cn(TIMELINE_WIDTH, className),
2662
+ "data-opt-id": "H6S6T",
2663
+ "data-opt-slug": "event-timeline.EventTimeline",
2664
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.EmptyState, {
2665
+ title: labels.emptyTitle,
2666
+ description: labels.emptyDescription,
2667
+ tone: "subdued",
2668
+ hasBorder: true
2669
+ })
2670
+ });
2671
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2672
+ className: require_text_truncate.cn(TIMELINE_WIDTH, "flex flex-col gap-3", className),
2673
+ "data-opt-id": "H6S6T",
2674
+ "data-opt-slug": "event-timeline.EventTimeline",
2675
+ children: [groups ? groups.map((group) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupBlock, {
2676
+ group,
2677
+ showGaps,
2678
+ order,
2679
+ minGapMs,
2680
+ locale,
2681
+ labels,
2682
+ onItemSelect
2683
+ }, group.id)) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TimelineList, {
2684
+ items: flatItems,
2685
+ showGaps,
2686
+ order,
2687
+ minGapMs,
2688
+ locale,
2689
+ labels,
2690
+ onItemSelect
2691
+ }), hasMore && onLoadMore && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2692
+ className: "flex justify-center pt-1",
2693
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Button, {
2694
+ variant: "secondary",
2695
+ size: "sm",
2696
+ onClick: onLoadMore,
2697
+ children: labels.loadMore
2698
+ })
2699
+ })]
2700
+ });
2701
+ }
2702
+ EventTimeline.displayName = "EventTimeline";
2703
+ //#endregion
2704
+ //#region src/shells/score-breakdown.tsx
2705
+ const defaultLabels$6 = {
2706
+ title: "Score",
2707
+ outOf: (max) => `out of ${max}`,
2708
+ weight: (percent) => `${percent} of total`,
2709
+ empty: "Not enough activity to score."
2710
+ };
2711
+ const toneText = {
2712
+ default: "text-text-primary",
2713
+ success: "text-success-fg",
2714
+ warning: "text-warning-fg",
2715
+ error: "text-danger-fg"
2716
+ };
2717
+ const toneMeter = {
2718
+ default: "default",
2719
+ success: "success",
2720
+ warning: "warning",
2721
+ error: "error"
2722
+ };
2723
+ /** Picks the band a score falls into — the highest `min` at or below it. */
2724
+ function resolveBand(score, bands) {
2725
+ return bands.filter((band) => score >= band.min).sort((left, right) => right.min - left.min)[0];
2726
+ }
2727
+ /**
2728
+ * ScoreBreakdown — a composite score you can argue with.
2729
+ *
2730
+ * Engagement, health and risk scores all collapse several signals into one
2731
+ * number, and the number alone tells nobody what to do about it. Showing the
2732
+ * axes and their weights beside the headline turns "43" into "recent, but
2733
+ * shallow", which is a finding rather than a verdict.
2734
+ */
2735
+ function ScoreBreakdown({ score, max = 100, axes, bands = [], formula, loading = false, labels: customLabels, className }) {
2736
+ var _band$tone;
2737
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$6), customLabels);
2738
+ const band = resolveBand(score, bands);
2739
+ const tone = (_band$tone = band === null || band === void 0 ? void 0 : band.tone) !== null && _band$tone !== void 0 ? _band$tone : "default";
2740
+ if (loading) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2741
+ className: require_text_truncate.cn(require_text_truncate.OPT_BORDER, "flex flex-col gap-3 rounded-xl p-4", className),
2742
+ "data-opt-id": "DYZSG",
2743
+ "data-opt-slug": "score-breakdown.ScoreBreakdown",
2744
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Skeleton, { className: "h-9 w-24" }), Array.from({ length: 4 }).map((_, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Skeleton, { className: "h-6 w-full" }, index))]
2745
+ });
2746
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2747
+ className: require_text_truncate.cn(require_text_truncate.OPT_BORDER, "flex flex-col gap-4 rounded-xl p-4", className),
2748
+ "data-opt-id": "DYZSG",
2749
+ "data-opt-slug": "score-breakdown.ScoreBreakdown",
2750
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2751
+ className: "flex flex-col gap-1",
2752
+ children: [
2753
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2754
+ className: require_text_truncate.cn(require_text_truncate.OPT_TEXT_SECONDARY, "text-sm"),
2755
+ children: labels.title
2756
+ }),
2757
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2758
+ className: "flex items-baseline gap-2",
2759
+ children: [
2760
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2761
+ className: require_text_truncate.cn("text-3xl font-bold tabular-nums", toneText[tone]),
2762
+ children: score
2763
+ }),
2764
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2765
+ className: require_text_truncate.cn(require_text_truncate.OPT_TEXT_TERTIARY, "text-xs"),
2766
+ children: labels.outOf(max)
2767
+ }),
2768
+ band && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2769
+ className: require_text_truncate.cn("ml-1 text-sm font-medium", toneText[tone]),
2770
+ children: band.label
2771
+ })
2772
+ ]
2773
+ }),
2774
+ formula && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2775
+ className: require_text_truncate.cn("text-text-tertiary", "text-xs"),
2776
+ children: formula
2777
+ })
2778
+ ]
2779
+ }), axes.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2780
+ className: require_text_truncate.cn(require_text_truncate.OPT_TEXT_SECONDARY, "text-sm"),
2781
+ children: labels.empty
2782
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dl", {
2783
+ className: "flex flex-col gap-3",
2784
+ children: axes.map((axis) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2785
+ className: "flex flex-col gap-1",
2786
+ children: [
2787
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2788
+ className: "flex items-baseline justify-between gap-2",
2789
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dt", {
2790
+ className: require_text_truncate.cn(require_text_truncate.OPT_TEXT_PRIMARY, "text-sm"),
2791
+ title: axis.hint,
2792
+ children: [axis.label, axis.weight !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2793
+ className: require_text_truncate.cn("text-text-tertiary", "ml-1.5 text-xs"),
2794
+ children: labels.weight(`${Math.round(axis.weight * 100)}%`)
2795
+ })]
2796
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", {
2797
+ className: require_text_truncate.cn(require_text_truncate.OPT_TEXT_SECONDARY, "text-sm font-medium tabular-nums"),
2798
+ children: Math.round(axis.value)
2799
+ })]
2800
+ }),
2801
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Meter, {
2802
+ value: axis.value,
2803
+ max,
2804
+ size: "sm",
2805
+ variant: toneMeter[tone],
2806
+ "aria-label": axis.label
2807
+ }),
2808
+ axis.hint && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2809
+ className: require_text_truncate.cn("text-text-tertiary", "text-xs"),
2810
+ children: axis.hint
2811
+ })
2812
+ ]
2813
+ }, axis.id))
2814
+ })]
2815
+ });
2816
+ }
2817
+ ScoreBreakdown.displayName = "ScoreBreakdown";
2818
+ //#endregion
2819
+ //#region src/shells/flyout.tsx
2820
+ const defaultLabels$5 = { close: "Close panel" };
2821
+ const sizeClass = {
2822
+ sm: "sm:w-[22rem]",
2823
+ md: "sm:w-[30rem]",
2824
+ lg: "sm:w-[42rem]"
2825
+ };
2826
+ /**
2827
+ * Flyout — a detail panel that does not take the page away.
2828
+ *
2829
+ * Navigating from a list to a record and back costs the reader their scroll
2830
+ * position, their filters and their place in the scan. A flyout keeps the list
2831
+ * on screen and shows the record beside it, so comparing three rows is three
2832
+ * clicks rather than six navigations.
2833
+ *
2834
+ * Deliberately **not** a modal: no focus trap, no `aria-modal`, no inert page.
2835
+ * The list behind it is still the point, and a reader who tabs out of the panel
2836
+ * to change a filter is doing the expected thing. Escape closes; focus returns
2837
+ * to whatever opened it.
2838
+ *
2839
+ * @example
2840
+ * ```tsx
2841
+ * <Flyout open={Boolean(selected)} onClose={clear} title={selected?.name}>
2842
+ * <DescriptionList items={fields} />
2843
+ * </Flyout>
2844
+ * ```
2845
+ */
2846
+ function Flyout({ open, onClose, title, description, type = "overlay", side = "right", size = "md", actions, footer, scrim = false, closeOnOutsideClick = true, hideCloseButton = false, labels: customLabels, className, children }) {
2847
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$5), customLabels);
2848
+ const panelRef = (0, react.useRef)(null);
2849
+ const openerRef = (0, react.useRef)(null);
2850
+ (0, react.useEffect)(() => {
2851
+ var _panelRef$current;
2852
+ if (!open) return;
2853
+ openerRef.current = document.activeElement;
2854
+ (_panelRef$current = panelRef.current) === null || _panelRef$current === void 0 || _panelRef$current.focus({ preventScroll: true });
2855
+ return () => {
2856
+ var _openerRef$current, _openerRef$current$fo;
2857
+ (_openerRef$current = openerRef.current) === null || _openerRef$current === void 0 || (_openerRef$current$fo = _openerRef$current.focus) === null || _openerRef$current$fo === void 0 || _openerRef$current$fo.call(_openerRef$current, { preventScroll: true });
2858
+ };
2859
+ }, [open]);
2860
+ (0, react.useEffect)(() => {
2861
+ if (!open) return;
2862
+ const handleKeyDown = (event) => {
2863
+ if (event.key === "Escape") {
2864
+ event.stopPropagation();
2865
+ onClose();
2866
+ }
2867
+ };
2868
+ document.addEventListener("keydown", handleKeyDown);
2869
+ return () => document.removeEventListener("keydown", handleKeyDown);
2870
+ }, [open, onClose]);
2871
+ (0, react.useEffect)(() => {
2872
+ if (!open || !closeOnOutsideClick || type !== "overlay") return;
2873
+ const handlePointerDown = (event) => {
2874
+ const panel = panelRef.current;
2875
+ if (panel && !panel.contains(event.target)) onClose();
2876
+ };
2877
+ const timer = window.setTimeout(() => {
2878
+ document.addEventListener("pointerdown", handlePointerDown);
2879
+ }, 0);
2880
+ return () => {
2881
+ window.clearTimeout(timer);
2882
+ document.removeEventListener("pointerdown", handlePointerDown);
2883
+ };
2884
+ }, [
2885
+ open,
2886
+ closeOnOutsideClick,
2887
+ type,
2888
+ onClose
2889
+ ]);
2890
+ const handleClose = (0, react.useCallback)(() => onClose(), [onClose]);
2891
+ if (!open) return null;
2892
+ const header = (title || actions || !hideCloseButton) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
2893
+ className: require_text_truncate.cn("border-border-subtle flex items-start gap-2 border-b px-4 py-3"),
2894
+ children: [
2895
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2896
+ className: "flex min-w-0 grow flex-col gap-0.5",
2897
+ children: [title && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
2898
+ className: require_text_truncate.cn("text-text-primary", "truncate text-base font-semibold"),
2899
+ children: title
2900
+ }), description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2901
+ className: require_text_truncate.cn("text-text-secondary", "truncate text-xs"),
2902
+ children: description
2903
+ })]
2904
+ }),
2905
+ actions,
2906
+ !hideCloseButton && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2907
+ type: "button",
2908
+ onClick: handleClose,
2909
+ "aria-label": labels.close,
2910
+ className: require_text_truncate.cn("outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-ring-offset", "text-text-tertiary", "hover:text-text-primary shrink-0 rounded p-1 transition-colors"),
2911
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, {
2912
+ className: "size-4",
2913
+ "aria-hidden": "true"
2914
+ })
2915
+ })
2916
+ ]
2917
+ });
2918
+ const panel = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
2919
+ ref: panelRef,
2920
+ role: "dialog",
2921
+ "aria-modal": false,
2922
+ "aria-label": typeof title === "string" ? title : void 0,
2923
+ tabIndex: -1,
2924
+ className: require_text_truncate.cn(require_text_truncate.OPT_FOCUS_VISIBLE, "flex min-h-0 flex-col", type === "overlay" && [
2925
+ "rounded-xl bg-surface-raised shadow-md",
2926
+ "fixed inset-y-0 z-40 w-full",
2927
+ sizeClass[size],
2928
+ side === "right" ? "right-0 border-l" : "left-0 border-r",
2929
+ "border-border"
2930
+ ], type === "inline" && ["border border-border", "rounded-xl bg-surface"], className),
2931
+ "data-opt-id": "85XVE",
2932
+ "data-opt-slug": "flyout.Flyout",
2933
+ children: [
2934
+ header,
2935
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2936
+ className: "min-h-0 grow overflow-y-auto px-4 py-4",
2937
+ children
2938
+ }),
2939
+ footer && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("footer", {
2940
+ className: "border-border-subtle border-t px-4 py-3",
2941
+ children: footer
2942
+ })
2943
+ ]
2944
+ });
2945
+ if (type === "inline") return panel;
2946
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [scrim && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2947
+ className: "bg-overlay fixed inset-0 z-30",
2948
+ "aria-hidden": "true"
2949
+ }), panel] });
2950
+ }
2951
+ Flyout.displayName = "Flyout";
2952
+ //#endregion
2404
2953
  //#region src/lib/use-auto-refresh.ts
2405
2954
  /**
2406
2955
  * A refresh timer that waits for its own work.
@@ -4403,6 +4952,12 @@ Object.defineProperty(exports, "EnvPanel", {
4403
4952
  return EnvPanel;
4404
4953
  }
4405
4954
  });
4955
+ Object.defineProperty(exports, "EventTimeline", {
4956
+ enumerable: true,
4957
+ get: function() {
4958
+ return EventTimeline;
4959
+ }
4960
+ });
4406
4961
  Object.defineProperty(exports, "FailureList", {
4407
4962
  enumerable: true,
4408
4963
  get: function() {
@@ -4421,6 +4976,12 @@ Object.defineProperty(exports, "FieldSidebar", {
4421
4976
  return FieldSidebar;
4422
4977
  }
4423
4978
  });
4979
+ Object.defineProperty(exports, "Flyout", {
4980
+ enumerable: true,
4981
+ get: function() {
4982
+ return Flyout;
4983
+ }
4984
+ });
4424
4985
  Object.defineProperty(exports, "KeyValueEditor", {
4425
4986
  enumerable: true,
4426
4987
  get: function() {
@@ -4451,6 +5012,12 @@ Object.defineProperty(exports, "QueryBar", {
4451
5012
  return QueryBar;
4452
5013
  }
4453
5014
  });
5015
+ Object.defineProperty(exports, "ScoreBreakdown", {
5016
+ enumerable: true,
5017
+ get: function() {
5018
+ return ScoreBreakdown;
5019
+ }
5020
+ });
4454
5021
  Object.defineProperty(exports, "SearchCombobox", {
4455
5022
  enumerable: true,
4456
5023
  get: function() {