@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
- import { $ as ToolbarButton$1, $t as Alert, A as FormSelectItem, At as DialogHeading$1, B as useFormStore, Bn as OPT_TEXT_LABEL, Bt as TabPanel$1, Cn as OPT_DIVIDER, Dn as OPT_DOT_YELLOW, En as OPT_DOT_RED, F as FormSwitch$1, Fn as OPT_SURFACE_POPOVER, Ft as SelectPopover$1, Gn as _objectSpread2, Gt as Composite, Hn as OPT_TEXT_SECONDARY, Ht as DisclosureContent$1, In as OPT_TABLE_ROW_ACTIVE, It as SelectRoot$1, J as PopoverContent$1, Jn as cn, Jt as CompositeRow, Kn as _objectWithoutProperties, Kt as CompositeItem, L as FormTextarea, Ln as OPT_TD, Lt as SelectTrigger$1, Mt as DialogRoot$1, Nn as OPT_ITEM_BASE, Nt as SelectItem$1, O as FormRoot$1, On as OPT_FOCUS, P as FormSubmit$1, Pn as OPT_SURFACE, Pt as SelectLabel$1, Rn as OPT_TD_MUTED, Rt as Tab$1, Sn as OPT_DISCLOSURE_TRIGGER, St as ComboboxRoot$1, T as FormRadioGroup$1, Tn as OPT_DOT_GREEN, Tt as DialogDescription$1, U as TagInput, Un as OPT_TEXT_TERTIARY, Ut as DisclosureRoot$1, Vn as OPT_TEXT_PRIMARY, Vt as TabsRoot$1, Wn as OPT_TH, Wt as DisclosureTrigger$1, X as PopoverTrigger$1, Y as PopoverRoot$1, Zt as Input, _n as OPT_BADGE_NEUTRAL, at as TooltipProvider$1, b as FormLabel$1, bn as OPT_BORDER, bt as ComboboxItem$1, ct as MenuItemCheckbox$1, dt as MenuRoot$1, en as Spinner, et as ToolbarContainer$1, fn as OPT_ACTIVE_ITEM, ft as MenuSeparator$1, gn as OPT_BADGE_GREEN, gt as ComboboxEmpty, hn as OPT_BADGE_BLUE, ht as MenubarRoot$1, i as FieldToken, it as TooltipAnchor$1, j as FormSelectPopover, jn as OPT_INPUT, jt as DialogPanel$1, k as FormSelect, kn as OPT_FOCUS_VISIBLE, l as FormCheckbox$1, lt as MenuItemRadio$1, m as FormError$1, mn as OPT_ANIMATE_ENTER_EXIT, mt as MenubarContainer$1, n as Highlight, nn as EmptyState, nt as ToolbarSeparator$1, o as Pagination, ot as MenuButtonArrow$1, pt as MenuTrigger$1, qn as Kbd, qt as CompositeProvider, rn as Button, rt as Tooltip$1, s as Checkbox, sn as StatCard, st as MenuItem$1, t as TextTruncate, tn as Badge, tt as ToolbarRoot$1, ut as MenuPopover$1, vn as OPT_BADGE_RED, w as FormRadio$1, xn as OPT_DISCLOSURE_CONTENT, xt as ComboboxPopover$1, y as FormInput$1, yn as OPT_BADGE_YELLOW, yt as ComboboxInput$1, zt as TabList$1 } from "./text-truncate-j6X3z7Pv.js";
2
+ import { $ as PopoverContent$1, $n as OPT_TEXT_PRIMARY, $t as CompositeRow, A as FormRadioGroup$1, An as OPT_BADGE_YELLOW, At as DialogDescription$1, B as FormSwitch$1, Bn as OPT_FOCUS_VISIBLE, Bt as SelectPopover$1, Cn as OPT_ACTIVE_ITEM, Dn as OPT_BADGE_GREEN, Dt as ComboboxRoot$1, En as OPT_BADGE_BLUE, Et as ComboboxPopover$1, F as FormSelectItem, Ft as DialogHeading$1, G as useFormStore, Gn as OPT_SURFACE, Gt as TabPanel$1, H as FormTextarea, Hn as OPT_INPUT, Ht as SelectTrigger$1, I as FormSelectPopover, In as OPT_DOT_GREEN, It as DialogPanel$1, J as TagInput, Jn as OPT_TABLE_ROW_ACTIVE, Jt as DisclosureRoot$1, Kn as OPT_SURFACE_POPOVER, Kt as TabsRoot$1, Ln as OPT_DOT_RED, Lt as DialogRoot$1, Mn as OPT_DISCLOSURE_CONTENT, N as FormRoot$1, Nn as OPT_DISCLOSURE_TRIGGER, On as OPT_BADGE_NEUTRAL, P as FormSelect, Pn as OPT_DIVIDER, Qn as OPT_TEXT_LABEL, Qt as CompositeProvider, Rn as OPT_DOT_YELLOW, Rt as SelectItem$1, T as FormLabel$1, Tn as OPT_ANIMATE_ENTER_EXIT, Tt as ComboboxItem$1, Ut as Tab$1, Vt as SelectRoot$1, Wn as OPT_ITEM_BASE, Wt as TabList$1, Xn as OPT_TD_MUTED, Xt as Composite, Yn as OPT_TD, Yt as DisclosureTrigger$1, Zt as CompositeItem, _t as MenuSeparator$1, an as Alert, ar as Kbd, at as ToolbarContainer$1, bt as MenubarRoot$1, cn as EmptyState, ct as Tooltip$1, dt as MenuButtonArrow$1, er as OPT_TEXT_SECONDARY, et as PopoverRoot$1, ft as MenuItem$1, gt as MenuRoot$1, ht as MenuPopover$1, i as FieldToken, ir as _objectWithoutProperties, it as ToolbarButton$1, jn as OPT_BORDER, k as FormRadio$1, kn as OPT_BADGE_RED, l as OPT_UI_DEFAULT_LOCALE, ln as Button, lt as TooltipAnchor$1, m as FormCheckbox$1, mn as Skeleton, mt as MenuItemRadio$1, n as Highlight, nr as OPT_TH, o as Pagination, on as Spinner, or as cn, ot as ToolbarRoot$1, p as formatOptTime, pn as StatCard, pt as MenuItemCheckbox$1, qt as DisclosureContent$1, rn as Input, rr as _objectSpread2, s as Checkbox, sn as Badge, st as ToolbarSeparator$1, t as TextTruncate, tn as Meter$1, tr as OPT_TEXT_TERTIARY, tt as PopoverTrigger$1, ut as TooltipProvider$1, vt as MenuTrigger$1, w as FormInput$1, wt as ComboboxInput$1, xt as ComboboxEmpty, y as FormError$1, yt as MenubarContainer$1, z as FormSubmit$1, zn as OPT_FOCUS, zt as SelectLabel$1 } from "./text-truncate-C17HHCR-.js";
3
3
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
4
  import * as React from "react";
5
5
  import { Fragment as Fragment$1, createContext, forwardRef, memo, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
@@ -253,7 +253,7 @@ function CommandPaletteTrigger({ commands, onSelect, labels }) {
253
253
  }
254
254
  //#endregion
255
255
  //#region src/shells/dashboard-grid.tsx
256
- const defaultLabels$18 = { ariaLabel: "대시보드 통계" };
256
+ const defaultLabels$21 = { ariaLabel: "대시보드 통계" };
257
257
  const MIN_DASHBOARD_COLUMNS = 1;
258
258
  const MAX_DASHBOARD_COLUMNS = 12;
259
259
  function normalizeDashboardColumns(columns) {
@@ -262,7 +262,7 @@ function normalizeDashboardColumns(columns) {
262
262
  }
263
263
  /** Renders the dashboard grid component. */
264
264
  function DashboardGrid({ stats, columns = 3, labels: customLabels }) {
265
- const labels = _objectSpread2(_objectSpread2({}, defaultLabels$18), customLabels);
265
+ const labels = _objectSpread2(_objectSpread2({}, defaultLabels$21), customLabels);
266
266
  const columnCount = normalizeDashboardColumns(columns);
267
267
  const rowStyle = { "--opt-dashboard-grid-columns": `repeat(${columnCount}, minmax(0, 1fr))` };
268
268
  const rows = [];
@@ -290,13 +290,13 @@ function DashboardGrid({ stats, columns = 3, labels: customLabels }) {
290
290
  }
291
291
  //#endregion
292
292
  //#region src/shells/search-combobox.tsx
293
- const defaultLabels$17 = {
293
+ const defaultLabels$20 = {
294
294
  noResults: "결과가 없습니다.",
295
295
  loading: "Loading…"
296
296
  };
297
297
  /** Renders the search combobox component. */
298
298
  function SearchCombobox({ items, placeholder = "검색...", label = "검색", onSelect, labels: customLabels, async: isAsync = false, onSearch, debounceMs = 300 }) {
299
- const labels = _objectSpread2(_objectSpread2({}, defaultLabels$17), customLabels);
299
+ const labels = _objectSpread2(_objectSpread2({}, defaultLabels$20), customLabels);
300
300
  const inputId = useId();
301
301
  const [query, setQuery] = useState("");
302
302
  const [asyncResults, setAsyncResults] = useState([]);
@@ -394,14 +394,14 @@ function SearchCombobox({ items, placeholder = "검색...", label = "검색", on
394
394
  }
395
395
  //#endregion
396
396
  //#region src/shells/status-select.tsx
397
- const defaultLabels$16 = {
397
+ const defaultLabels$19 = {
398
398
  selectLabel: "상태",
399
399
  emptyMessage: "사용 가능한 상태 옵션이 없습니다."
400
400
  };
401
401
  /** Renders the status select component. */
402
402
  function StatusSelect({ options, value: controlledValue, onChange, label, labels: customLabels }) {
403
403
  var _options$0$value, _options$;
404
- const labels = _objectSpread2(_objectSpread2({}, defaultLabels$16), customLabels);
404
+ const labels = _objectSpread2(_objectSpread2({}, defaultLabels$19), customLabels);
405
405
  const resolvedLabel = label !== null && label !== void 0 ? label : labels.selectLabel;
406
406
  const [internalValue, setInternalValue] = useState((_options$0$value = (_options$ = options[0]) === null || _options$ === void 0 ? void 0 : _options$.value) !== null && _options$0$value !== void 0 ? _options$0$value : "");
407
407
  const value = controlledValue !== null && controlledValue !== void 0 ? controlledValue : internalValue;
@@ -468,7 +468,7 @@ function StatusSelect({ options, value: controlledValue, onChange, label, labels
468
468
  }
469
469
  //#endregion
470
470
  //#region src/shells/app-menubar.tsx
471
- const defaultLabels$15 = { ariaLabel: "Application menu" };
471
+ const defaultLabels$18 = { ariaLabel: "Application menu" };
472
472
  function MenuSection({ menu }) {
473
473
  return /* @__PURE__ */ jsxs(MenuRoot$1, { children: [/* @__PURE__ */ jsxs(MenuTrigger$1, { children: [menu.label, /* @__PURE__ */ jsx(MenuButtonArrow$1, {})] }), /* @__PURE__ */ jsx(MenuPopover$1, { children: menu.items.map((item, i) => {
474
474
  if (item.type === "separator") return /* @__PURE__ */ jsx(MenuSeparator$1, {}, `sep-${i}`);
@@ -512,7 +512,7 @@ function MenuSection({ menu }) {
512
512
  }
513
513
  /** Renders the app menubar component. */
514
514
  function AppMenubar({ menus, labels: customLabels }) {
515
- const labels = _objectSpread2(_objectSpread2({}, defaultLabels$15), customLabels);
515
+ const labels = _objectSpread2(_objectSpread2({}, defaultLabels$18), customLabels);
516
516
  if (menus.length === 0) return null;
517
517
  return /* @__PURE__ */ jsx(MenubarRoot$1, {
518
518
  "data-opt-id": "BGKHW",
@@ -551,10 +551,10 @@ const sectionStyles = {
551
551
  Templates: "text-text-tertiary"
552
552
  };
553
553
  const defaultSectionStyle = "text-text-secondary";
554
- const defaultLabels$14 = { ariaLabel: "문서 네비게이션" };
554
+ const defaultLabels$17 = { ariaLabel: "문서 네비게이션" };
555
555
  /** Renders the sidebar nav component. */
556
556
  function SidebarNav({ items, labels: customLabels }) {
557
- const labels = _objectSpread2(_objectSpread2({}, defaultLabels$14), customLabels);
557
+ const labels = _objectSpread2(_objectSpread2({}, defaultLabels$17), customLabels);
558
558
  const pathname = usePathname();
559
559
  const activeItem = items.find((item) => item.href === pathname);
560
560
  const [activeId, setActiveId] = useState(activeItem ? domId(activeItem.id) : void 0);
@@ -602,7 +602,7 @@ function SidebarNav({ items, labels: customLabels }) {
602
602
  }
603
603
  //#endregion
604
604
  //#region src/shells/editor-toolbar.tsx
605
- const defaultLabels$13 = { ariaLabel: "텍스트 편집기 툴바" };
605
+ const defaultLabels$16 = { ariaLabel: "텍스트 편집기 툴바" };
606
606
  function ToolbarButtonWithTooltip({ button, active, onAction }) {
607
607
  return /* @__PURE__ */ jsxs(TooltipProvider$1, { children: [/* @__PURE__ */ jsx(TooltipAnchor$1, { children: /* @__PURE__ */ jsx(ToolbarButton$1, {
608
608
  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"}`,
@@ -612,7 +612,7 @@ function ToolbarButtonWithTooltip({ button, active, onAction }) {
612
612
  }
613
613
  /** Renders the editor toolbar component. */
614
614
  function EditorToolbar({ buttons, onAction, activeButtons = [], labels: customLabels }) {
615
- const labels = _objectSpread2(_objectSpread2({}, defaultLabels$13), customLabels);
615
+ const labels = _objectSpread2(_objectSpread2({}, defaultLabels$16), customLabels);
616
616
  const groups = [];
617
617
  let current = [];
618
618
  for (const btn of buttons) {
@@ -645,14 +645,14 @@ function EditorToolbar({ buttons, onAction, activeButtons = [], labels: customLa
645
645
  }
646
646
  //#endregion
647
647
  //#region src/shells/content-tabs.tsx
648
- const defaultLabels$12 = {
648
+ const defaultLabels$15 = {
649
649
  ariaLabel: "콘텐츠 탭",
650
650
  emptyMessage: "표시할 탭이 없습니다."
651
651
  };
652
652
  /** Renders the content tabs component. */
653
653
  function ContentTabs({ tabs, defaultTabId, labels: customLabels }) {
654
654
  var _tabs$, _tabs$find;
655
- const labels = _objectSpread2(_objectSpread2({}, defaultLabels$12), customLabels);
655
+ const labels = _objectSpread2(_objectSpread2({}, defaultLabels$15), customLabels);
656
656
  const [selectedTabId, setSelectedTabId] = useState(defaultTabId !== null && defaultTabId !== void 0 ? defaultTabId : (_tabs$ = tabs[0]) === null || _tabs$ === void 0 ? void 0 : _tabs$.id);
657
657
  if (tabs.length === 0) return /* @__PURE__ */ jsx("div", {
658
658
  className: "text-text-secondary text-sm",
@@ -684,13 +684,13 @@ function ContentTabs({ tabs, defaultTabId, labels: customLabels }) {
684
684
  }
685
685
  //#endregion
686
686
  //#region src/shells/faq-accordion.tsx
687
- const defaultLabels$11 = {
687
+ const defaultLabels$14 = {
688
688
  ariaLabel: "자주 묻는 질문",
689
689
  emptyMessage: "등록된 FAQ가 없습니다."
690
690
  };
691
691
  /** Renders the faq accordion component. */
692
692
  function FaqAccordion({ items, labels: customLabels }) {
693
- const labels = _objectSpread2(_objectSpread2({}, defaultLabels$11), customLabels);
693
+ const labels = _objectSpread2(_objectSpread2({}, defaultLabels$14), customLabels);
694
694
  if (items.length === 0) return /* @__PURE__ */ jsx("div", {
695
695
  className: "text-text-secondary text-sm",
696
696
  "data-opt-id": "QRBHV",
@@ -730,14 +730,14 @@ function FaqAccordion({ items, labels: customLabels }) {
730
730
  }
731
731
  //#endregion
732
732
  //#region src/shells/settings-form.tsx
733
- const defaultLabels$10 = {
733
+ const defaultLabels$13 = {
734
734
  formAriaLabel: "설정 폼",
735
735
  submitButton: "저장",
736
736
  successMessage: "설정이 저장되었습니다."
737
737
  };
738
738
  /** Renders the settings form component. */
739
739
  function SettingsForm({ fields, onSubmit, labels: customLabels }) {
740
- const labels = _objectSpread2(_objectSpread2({}, defaultLabels$10), customLabels);
740
+ const labels = _objectSpread2(_objectSpread2({}, defaultLabels$13), customLabels);
741
741
  const [submitted, setSubmitted] = useState(false);
742
742
  const defaultValues = {};
743
743
  for (const field of fields) {
@@ -1063,7 +1063,7 @@ function ProjectSwitcher({ projects, value, onChange, placeholder = "프로젝
1063
1063
  ProjectSwitcher.displayName = "ProjectSwitcher";
1064
1064
  //#endregion
1065
1065
  //#region src/shells/env-panel.tsx
1066
- const defaultLabels$9 = {
1066
+ const defaultLabels$12 = {
1067
1067
  varCount: (count) => `${count}개 변수`,
1068
1068
  envVarsLabel: (env) => `${env} 환경 변수`,
1069
1069
  secretBadge: "secret"
@@ -1075,7 +1075,7 @@ const envDot = {
1075
1075
  };
1076
1076
  /** Renders the env panel component. */
1077
1077
  function EnvPanel({ groups, labels: customLabels }) {
1078
- const labels = _objectSpread2(_objectSpread2({}, defaultLabels$9), customLabels);
1078
+ const labels = _objectSpread2(_objectSpread2({}, defaultLabels$12), customLabels);
1079
1079
  return /* @__PURE__ */ jsx("div", {
1080
1080
  className: `rounded-lg ${OPT_BORDER} divide-y ${OPT_DIVIDER} bg-surface`,
1081
1081
  "data-opt-id": "ZBDWK",
@@ -1148,7 +1148,7 @@ function EnvPanel({ groups, labels: customLabels }) {
1148
1148
  }
1149
1149
  //#endregion
1150
1150
  //#region src/shells/branch-select.tsx
1151
- const defaultLabels$8 = {
1151
+ const defaultLabels$11 = {
1152
1152
  selectLabel: "브랜치",
1153
1153
  emptySelectionLabel: "선택...",
1154
1154
  emptyMessage: "사용 가능한 브랜치가 없습니다.",
@@ -1157,7 +1157,7 @@ const defaultLabels$8 = {
1157
1157
  /** Renders the branch select component. */
1158
1158
  function BranchSelect({ branches, value: controlledValue, onChange, label, labels: customLabels }) {
1159
1159
  var _ref, _defaultBranch$name, _branches$, _selected$name;
1160
- const labels = _objectSpread2(_objectSpread2({}, defaultLabels$8), customLabels);
1160
+ const labels = _objectSpread2(_objectSpread2({}, defaultLabels$11), customLabels);
1161
1161
  const resolvedLabel = label !== null && label !== void 0 ? label : labels.selectLabel;
1162
1162
  const defaultBranch = branches.find((b) => b.isDefault);
1163
1163
  const [internalValue, setInternalValue] = 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 : "");
@@ -1266,7 +1266,7 @@ function BranchSelect({ branches, value: controlledValue, onChange, label, label
1266
1266
  BranchSelect.displayName = "BranchSelect";
1267
1267
  //#endregion
1268
1268
  //#region src/shells/domain-table.tsx
1269
- const defaultLabels$7 = {
1269
+ const defaultLabels$10 = {
1270
1270
  domain: "Domain",
1271
1271
  status: "Status",
1272
1272
  ssl: "SSL",
@@ -1296,7 +1296,7 @@ const sslLabel = {
1296
1296
  };
1297
1297
  /** Renders the domain table component. */
1298
1298
  function DomainTable({ domains, title = "도메인", labels: customLabels }) {
1299
- const labels = _objectSpread2(_objectSpread2({}, defaultLabels$7), customLabels);
1299
+ const labels = _objectSpread2(_objectSpread2({}, defaultLabels$10), customLabels);
1300
1300
  if (domains.length === 0) return /* @__PURE__ */ jsxs("div", {
1301
1301
  className: "gap-group flex flex-col",
1302
1302
  "data-opt-id": "A7SRM",
@@ -1902,7 +1902,7 @@ function NotificationToast({ toast, onDismiss, defaultDuration = 5e3 }) {
1902
1902
  }
1903
1903
  //#endregion
1904
1904
  //#region src/shells/failure-list.tsx
1905
- const defaultLabels$6 = {
1905
+ const defaultLabels$9 = {
1906
1906
  title: "최근 실패",
1907
1907
  emptyMessage: "실패 항목이 없습니다",
1908
1908
  moreItems: "{count}개 더 보기",
@@ -2001,20 +2001,20 @@ function FailureItemRow({ failure, isLast, onFailureClick, renderActions, merged
2001
2001
  function FailureList({ failures, title, maxItems, grouped = false, onFailureClick, onShowMore, labels: customLabels, locale = "ko-KR", loading = false, renderActions, className }) {
2002
2002
  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;
2003
2003
  const mergedTimeAgo = {
2004
- 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,
2005
- 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,
2006
- 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,
2007
- 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
2004
+ 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,
2005
+ 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,
2006
+ 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,
2007
+ 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
2008
2008
  };
2009
2009
  const mergedSeverityLabels = {
2010
- 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,
2011
- 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,
2012
- 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
2010
+ 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,
2011
+ 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,
2012
+ 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
2013
2013
  };
2014
2014
  const labels = {
2015
- title: (_customLabels$title = customLabels === null || customLabels === void 0 ? void 0 : customLabels.title) !== null && _customLabels$title !== void 0 ? _customLabels$title : defaultLabels$6.title,
2016
- emptyMessage: (_customLabels$emptyMe = customLabels === null || customLabels === void 0 ? void 0 : customLabels.emptyMessage) !== null && _customLabels$emptyMe !== void 0 ? _customLabels$emptyMe : defaultLabels$6.emptyMessage,
2017
- moreItems: (_customLabels$moreIte = customLabels === null || customLabels === void 0 ? void 0 : customLabels.moreItems) !== null && _customLabels$moreIte !== void 0 ? _customLabels$moreIte : defaultLabels$6.moreItems
2015
+ title: (_customLabels$title = customLabels === null || customLabels === void 0 ? void 0 : customLabels.title) !== null && _customLabels$title !== void 0 ? _customLabels$title : defaultLabels$9.title,
2016
+ emptyMessage: (_customLabels$emptyMe = customLabels === null || customLabels === void 0 ? void 0 : customLabels.emptyMessage) !== null && _customLabels$emptyMe !== void 0 ? _customLabels$emptyMe : defaultLabels$9.emptyMessage,
2017
+ moreItems: (_customLabels$moreIte = customLabels === null || customLabels === void 0 ? void 0 : customLabels.moreItems) !== null && _customLabels$moreIte !== void 0 ? _customLabels$moreIte : defaultLabels$9.moreItems
2018
2018
  };
2019
2019
  const displayTitle = title !== null && title !== void 0 ? title : labels.title;
2020
2020
  const allItems = maxItems ? failures.slice(0, maxItems) : failures;
@@ -2090,7 +2090,7 @@ function renderGrouped(items, remainingCount, rowProps, labels, onShowMore) {
2090
2090
  }
2091
2091
  //#endregion
2092
2092
  //#region src/shells/key-value-editor.tsx
2093
- const defaultLabels$5 = {
2093
+ const defaultLabels$8 = {
2094
2094
  keyPlaceholder: "키",
2095
2095
  valuePlaceholder: "값",
2096
2096
  addButton: "추가",
@@ -2104,7 +2104,7 @@ function generateId() {
2104
2104
  }
2105
2105
  /** Renders the key value editor component. */
2106
2106
  function KeyValueEditor({ value: controlledValue, defaultValue, onChange, showSecretToggle = false, maxEntries, readOnly = false, labels: customLabels, renderActions, validateKey, className }) {
2107
- const labels = _objectSpread2(_objectSpread2({}, defaultLabels$5), customLabels);
2107
+ const labels = _objectSpread2(_objectSpread2({}, defaultLabels$8), customLabels);
2108
2108
  const [internalEntries, setInternalEntries] = useState(() => defaultValue !== null && defaultValue !== void 0 ? defaultValue : []);
2109
2109
  const entries = controlledValue !== null && controlledValue !== void 0 ? controlledValue : internalEntries;
2110
2110
  const keyInputRef = useRef(null);
@@ -2400,6 +2400,555 @@ function WorkflowCanvas({ steps, connections = [], rows = 9, cols = 9, cellSize
2400
2400
  }
2401
2401
  WorkflowCanvas.displayName = "WorkflowCanvas";
2402
2402
  //#endregion
2403
+ //#region src/shells/event-timeline.tsx
2404
+ const defaultLabels$7 = {
2405
+ emptyTitle: "No events yet",
2406
+ emptyDescription: "Events appear here as soon as they arrive.",
2407
+ expand: "Show properties",
2408
+ collapse: "Hide properties",
2409
+ loadMore: "Load more",
2410
+ gap: (duration) => `+${duration}`,
2411
+ properties: "Event properties",
2412
+ openGroup: (title) => `Open ${title}`
2413
+ };
2414
+ /**
2415
+ * A line of text and the time it happened have to read as one row. Past about
2416
+ * this width the eye stops connecting them, so the rail is capped rather than
2417
+ * stretched to whatever the page allows. Override with `className` where a
2418
+ * layout genuinely needs the full width.
2419
+ */
2420
+ const TIMELINE_WIDTH = "max-w-5xl";
2421
+ const toneDot = {
2422
+ default: "bg-text-tertiary",
2423
+ accent: "bg-accent",
2424
+ success: "bg-success",
2425
+ warning: "bg-warning",
2426
+ danger: "bg-danger"
2427
+ };
2428
+ function toMillis(timestamp) {
2429
+ const parsed = Date.parse(timestamp);
2430
+ return Number.isNaN(parsed) ? 0 : parsed;
2431
+ }
2432
+ function stringifyValue(value) {
2433
+ if (value === null || value === void 0) return "—";
2434
+ if (typeof value === "string") return value;
2435
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
2436
+ return JSON.stringify(value);
2437
+ }
2438
+ function PropertyTable({ properties, ariaLabel }) {
2439
+ const entries = Object.entries(properties);
2440
+ if (entries.length === 0) return null;
2441
+ return /* @__PURE__ */ jsx("dl", {
2442
+ className: cn(OPT_BORDER, "gap-element bg-bg-subtle mt-2 grid grid-cols-1 rounded-lg p-2.5 sm:grid-cols-2"),
2443
+ "aria-label": ariaLabel,
2444
+ children: entries.map(([key, value]) => /* @__PURE__ */ jsxs("div", {
2445
+ className: "flex min-w-0 flex-col",
2446
+ children: [/* @__PURE__ */ jsx("dt", {
2447
+ className: cn(OPT_TEXT_TERTIARY, "truncate text-xs"),
2448
+ children: key
2449
+ }), /* @__PURE__ */ jsx("dd", {
2450
+ className: cn(OPT_TEXT_PRIMARY, "truncate text-xs"),
2451
+ children: stringifyValue(value)
2452
+ })]
2453
+ }, key))
2454
+ });
2455
+ }
2456
+ function TimelineRow({ item, gapLabel, labels, onItemSelect, isLast }) {
2457
+ var _item$tone, _item$content, _item$properties;
2458
+ const [open, setOpen] = useState(false);
2459
+ const expandable = Boolean(item.content || item.properties);
2460
+ const toggle = useCallback(() => setOpen((value) => !value), []);
2461
+ const headline = /* @__PURE__ */ jsx("span", {
2462
+ className: cn(OPT_TEXT_PRIMARY, "truncate text-sm font-medium"),
2463
+ children: item.name
2464
+ });
2465
+ return /* @__PURE__ */ jsxs("li", {
2466
+ className: "relative flex gap-3 pb-4 last:pb-0",
2467
+ children: [/* @__PURE__ */ jsxs("div", {
2468
+ className: "flex shrink-0 flex-col items-center",
2469
+ children: [/* @__PURE__ */ jsx("span", {
2470
+ className: cn("mt-1.5 size-2 shrink-0 rounded-full", toneDot[(_item$tone = item.tone) !== null && _item$tone !== void 0 ? _item$tone : "default"]),
2471
+ "aria-hidden": "true"
2472
+ }), !isLast && /* @__PURE__ */ jsx("span", {
2473
+ className: "bg-border mt-1 w-px grow",
2474
+ "aria-hidden": "true"
2475
+ })]
2476
+ }), /* @__PURE__ */ jsxs("div", {
2477
+ className: "flex min-w-0 grow flex-col",
2478
+ children: [
2479
+ gapLabel && /* @__PURE__ */ jsx("span", {
2480
+ className: cn("text-text-tertiary", "mb-1 text-xs tabular-nums"),
2481
+ children: labels.gap(gapLabel)
2482
+ }),
2483
+ /* @__PURE__ */ jsxs("div", {
2484
+ className: "flex min-w-0 items-baseline gap-2",
2485
+ children: [
2486
+ item.icon && /* @__PURE__ */ jsx("span", {
2487
+ className: "text-text-tertiary",
2488
+ "aria-hidden": "true",
2489
+ children: item.icon
2490
+ }),
2491
+ onItemSelect ? /* @__PURE__ */ jsx("button", {
2492
+ type: "button",
2493
+ onClick: () => onItemSelect(item),
2494
+ className: cn(OPT_FOCUS_VISIBLE, "hover:text-accent min-w-0 truncate rounded text-left"),
2495
+ children: headline
2496
+ }) : headline,
2497
+ /* @__PURE__ */ jsx("time", {
2498
+ dateTime: item.timestamp,
2499
+ className: cn(OPT_TEXT_TERTIARY, "ml-auto shrink-0 text-xs tabular-nums"),
2500
+ children: formatOptTime(item.timestamp)
2501
+ })
2502
+ ]
2503
+ }),
2504
+ item.detail && /* @__PURE__ */ jsx("span", {
2505
+ className: cn("text-text-secondary", "truncate text-xs"),
2506
+ children: item.detail
2507
+ }),
2508
+ expandable && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("button", {
2509
+ type: "button",
2510
+ onClick: toggle,
2511
+ "aria-expanded": open,
2512
+ className: 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"),
2513
+ children: [open ? /* @__PURE__ */ jsx(ChevronDown, {
2514
+ className: "size-3",
2515
+ "aria-hidden": "true"
2516
+ }) : /* @__PURE__ */ jsx(ChevronRight, {
2517
+ className: "size-3",
2518
+ "aria-hidden": "true"
2519
+ }), open ? labels.collapse : labels.expand]
2520
+ }), open && ((_item$content = item.content) !== null && _item$content !== void 0 ? _item$content : /* @__PURE__ */ jsx(PropertyTable, {
2521
+ properties: (_item$properties = item.properties) !== null && _item$properties !== void 0 ? _item$properties : {},
2522
+ ariaLabel: labels.properties
2523
+ }))] })
2524
+ ]
2525
+ })]
2526
+ });
2527
+ }
2528
+ /** Builds the gap label for each row, given the reading order. */
2529
+ function useGapLabels(items, showGaps, order, minGapMs, locale) {
2530
+ return useMemo(() => {
2531
+ if (!showGaps) return items.map(() => null);
2532
+ return items.map((item, index) => {
2533
+ const neighbour = order === "desc" ? items[index + 1] : items[index - 1];
2534
+ if (!neighbour) return null;
2535
+ const delta = Math.abs(toMillis(item.timestamp) - toMillis(neighbour.timestamp));
2536
+ if (delta < minGapMs) return null;
2537
+ return formatDuration(delta, locale);
2538
+ });
2539
+ }, [
2540
+ items,
2541
+ showGaps,
2542
+ order,
2543
+ minGapMs,
2544
+ locale
2545
+ ]);
2546
+ }
2547
+ function TimelineList({ items, showGaps, order, minGapMs, locale, labels, onItemSelect }) {
2548
+ const gaps = useGapLabels(items, showGaps, order, minGapMs, locale);
2549
+ return /* @__PURE__ */ jsx("ol", {
2550
+ className: "flex flex-col",
2551
+ children: items.map((item, index) => {
2552
+ var _gaps$index;
2553
+ return /* @__PURE__ */ jsx(TimelineRow, {
2554
+ item,
2555
+ gapLabel: (_gaps$index = gaps[index]) !== null && _gaps$index !== void 0 ? _gaps$index : null,
2556
+ labels,
2557
+ onItemSelect,
2558
+ isLast: index === items.length - 1
2559
+ }, item.id);
2560
+ })
2561
+ });
2562
+ }
2563
+ function GroupBlock({ group, showGaps, order, minGapMs, locale, labels, onItemSelect }) {
2564
+ var _group$defaultOpen;
2565
+ const [open, setOpen] = useState((_group$defaultOpen = group.defaultOpen) !== null && _group$defaultOpen !== void 0 ? _group$defaultOpen : true);
2566
+ return /* @__PURE__ */ jsxs("section", {
2567
+ className: cn(OPT_BORDER, "rounded-xl"),
2568
+ children: [/* @__PURE__ */ jsxs("div", {
2569
+ className: "flex items-center gap-2 px-3 py-2",
2570
+ children: [
2571
+ /* @__PURE__ */ jsxs("button", {
2572
+ type: "button",
2573
+ onClick: () => setOpen((value) => !value),
2574
+ "aria-expanded": open,
2575
+ className: cn(OPT_FOCUS_VISIBLE, "flex min-w-0 grow items-center gap-2 rounded text-left"),
2576
+ children: [
2577
+ open ? /* @__PURE__ */ jsx(ChevronDown, {
2578
+ className: cn(OPT_TEXT_TERTIARY, "size-4 shrink-0"),
2579
+ "aria-hidden": "true"
2580
+ }) : /* @__PURE__ */ jsx(ChevronRight, {
2581
+ className: cn(OPT_TEXT_TERTIARY, "size-4 shrink-0"),
2582
+ "aria-hidden": "true"
2583
+ }),
2584
+ /* @__PURE__ */ jsx("span", {
2585
+ className: cn(OPT_TEXT_PRIMARY, "truncate text-sm font-medium"),
2586
+ children: group.title
2587
+ }),
2588
+ /* @__PURE__ */ jsx(Badge, {
2589
+ size: "sm",
2590
+ variant: "default",
2591
+ className: "shrink-0",
2592
+ children: group.items.length
2593
+ })
2594
+ ]
2595
+ }),
2596
+ group.meta && /* @__PURE__ */ jsx("span", {
2597
+ className: cn("text-text-tertiary", "shrink-0 text-xs"),
2598
+ children: group.meta
2599
+ }),
2600
+ group.href && /* @__PURE__ */ jsx("a", {
2601
+ href: group.href,
2602
+ className: 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"),
2603
+ children: labels.openGroup(typeof group.title === "string" ? group.title : group.id)
2604
+ })
2605
+ ]
2606
+ }), open && /* @__PURE__ */ jsx("div", {
2607
+ className: "border-border-subtle border-t px-3 py-3",
2608
+ children: /* @__PURE__ */ jsx(TimelineList, {
2609
+ items: group.items,
2610
+ showGaps,
2611
+ order,
2612
+ minGapMs,
2613
+ locale,
2614
+ labels,
2615
+ onItemSelect
2616
+ })
2617
+ })]
2618
+ });
2619
+ }
2620
+ /**
2621
+ * EventTimeline — a sequence of events, read as a sequence.
2622
+ *
2623
+ * A table of events answers "what happened" and hides "how it went": the rows
2624
+ * are evenly spaced no matter whether they were two seconds or two hours
2625
+ * apart, and the payload that would explain a row lives in a column nobody
2626
+ * widened. This renders the same data as an ordered rail, labels the elapsed
2627
+ * time between adjacent events, and opens each payload in place. Grouping by
2628
+ * session restores the boundary that makes a history legible.
2629
+ *
2630
+ * @example
2631
+ * ```tsx
2632
+ * <EventTimeline
2633
+ * groups={sessions.map((session) => ({
2634
+ * id: session.id,
2635
+ * title: session.entryPath,
2636
+ * meta: `${session.duration} · ${session.events} events`,
2637
+ * items: session.events,
2638
+ * }))}
2639
+ * showGaps
2640
+ * />
2641
+ * ```
2642
+ */
2643
+ function EventTimeline({ items, groups, showGaps = true, order = "desc", minGapMs = 1e3, loading = false, hasMore = false, onLoadMore, onItemSelect, locale = OPT_UI_DEFAULT_LOCALE, labels: customLabels, className }) {
2644
+ const labels = _objectSpread2(_objectSpread2({}, defaultLabels$7), customLabels);
2645
+ const flatItems = items !== null && items !== void 0 ? items : [];
2646
+ const hasContent = groups ? groups.length > 0 : flatItems.length > 0;
2647
+ if (loading) return /* @__PURE__ */ jsx("div", {
2648
+ className: cn(TIMELINE_WIDTH, "flex flex-col gap-3", className),
2649
+ "data-opt-id": "H6S6T",
2650
+ "data-opt-slug": "event-timeline.EventTimeline",
2651
+ children: Array.from({ length: 5 }).map((_, index) => /* @__PURE__ */ jsxs("div", {
2652
+ className: "flex gap-3",
2653
+ children: [/* @__PURE__ */ jsx(Skeleton, { className: "mt-1.5 size-2 rounded-full" }), /* @__PURE__ */ jsxs("div", {
2654
+ className: "flex grow flex-col gap-1.5",
2655
+ children: [/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-40" }), /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-64" })]
2656
+ })]
2657
+ }, index))
2658
+ });
2659
+ if (!hasContent) return /* @__PURE__ */ jsx("div", {
2660
+ className: cn(TIMELINE_WIDTH, className),
2661
+ "data-opt-id": "H6S6T",
2662
+ "data-opt-slug": "event-timeline.EventTimeline",
2663
+ children: /* @__PURE__ */ jsx(EmptyState, {
2664
+ title: labels.emptyTitle,
2665
+ description: labels.emptyDescription,
2666
+ tone: "subdued",
2667
+ hasBorder: true
2668
+ })
2669
+ });
2670
+ return /* @__PURE__ */ jsxs("div", {
2671
+ className: cn(TIMELINE_WIDTH, "flex flex-col gap-3", className),
2672
+ "data-opt-id": "H6S6T",
2673
+ "data-opt-slug": "event-timeline.EventTimeline",
2674
+ children: [groups ? groups.map((group) => /* @__PURE__ */ jsx(GroupBlock, {
2675
+ group,
2676
+ showGaps,
2677
+ order,
2678
+ minGapMs,
2679
+ locale,
2680
+ labels,
2681
+ onItemSelect
2682
+ }, group.id)) : /* @__PURE__ */ jsx(TimelineList, {
2683
+ items: flatItems,
2684
+ showGaps,
2685
+ order,
2686
+ minGapMs,
2687
+ locale,
2688
+ labels,
2689
+ onItemSelect
2690
+ }), hasMore && onLoadMore && /* @__PURE__ */ jsx("div", {
2691
+ className: "flex justify-center pt-1",
2692
+ children: /* @__PURE__ */ jsx(Button, {
2693
+ variant: "secondary",
2694
+ size: "sm",
2695
+ onClick: onLoadMore,
2696
+ children: labels.loadMore
2697
+ })
2698
+ })]
2699
+ });
2700
+ }
2701
+ EventTimeline.displayName = "EventTimeline";
2702
+ //#endregion
2703
+ //#region src/shells/score-breakdown.tsx
2704
+ const defaultLabels$6 = {
2705
+ title: "Score",
2706
+ outOf: (max) => `out of ${max}`,
2707
+ weight: (percent) => `${percent} of total`,
2708
+ empty: "Not enough activity to score."
2709
+ };
2710
+ const toneText = {
2711
+ default: "text-text-primary",
2712
+ success: "text-success-fg",
2713
+ warning: "text-warning-fg",
2714
+ error: "text-danger-fg"
2715
+ };
2716
+ const toneMeter = {
2717
+ default: "default",
2718
+ success: "success",
2719
+ warning: "warning",
2720
+ error: "error"
2721
+ };
2722
+ /** Picks the band a score falls into — the highest `min` at or below it. */
2723
+ function resolveBand(score, bands) {
2724
+ return bands.filter((band) => score >= band.min).sort((left, right) => right.min - left.min)[0];
2725
+ }
2726
+ /**
2727
+ * ScoreBreakdown — a composite score you can argue with.
2728
+ *
2729
+ * Engagement, health and risk scores all collapse several signals into one
2730
+ * number, and the number alone tells nobody what to do about it. Showing the
2731
+ * axes and their weights beside the headline turns "43" into "recent, but
2732
+ * shallow", which is a finding rather than a verdict.
2733
+ */
2734
+ function ScoreBreakdown({ score, max = 100, axes, bands = [], formula, loading = false, labels: customLabels, className }) {
2735
+ var _band$tone;
2736
+ const labels = _objectSpread2(_objectSpread2({}, defaultLabels$6), customLabels);
2737
+ const band = resolveBand(score, bands);
2738
+ const tone = (_band$tone = band === null || band === void 0 ? void 0 : band.tone) !== null && _band$tone !== void 0 ? _band$tone : "default";
2739
+ if (loading) return /* @__PURE__ */ jsxs("section", {
2740
+ className: cn(OPT_BORDER, "flex flex-col gap-3 rounded-xl p-4", className),
2741
+ "data-opt-id": "DYZSG",
2742
+ "data-opt-slug": "score-breakdown.ScoreBreakdown",
2743
+ children: [/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-24" }), Array.from({ length: 4 }).map((_, index) => /* @__PURE__ */ jsx(Skeleton, { className: "h-6 w-full" }, index))]
2744
+ });
2745
+ return /* @__PURE__ */ jsxs("section", {
2746
+ className: cn(OPT_BORDER, "flex flex-col gap-4 rounded-xl p-4", className),
2747
+ "data-opt-id": "DYZSG",
2748
+ "data-opt-slug": "score-breakdown.ScoreBreakdown",
2749
+ children: [/* @__PURE__ */ jsxs("div", {
2750
+ className: "flex flex-col gap-1",
2751
+ children: [
2752
+ /* @__PURE__ */ jsx("span", {
2753
+ className: cn(OPT_TEXT_SECONDARY, "text-sm"),
2754
+ children: labels.title
2755
+ }),
2756
+ /* @__PURE__ */ jsxs("div", {
2757
+ className: "flex items-baseline gap-2",
2758
+ children: [
2759
+ /* @__PURE__ */ jsx("span", {
2760
+ className: cn("text-3xl font-bold tabular-nums", toneText[tone]),
2761
+ children: score
2762
+ }),
2763
+ /* @__PURE__ */ jsx("span", {
2764
+ className: cn(OPT_TEXT_TERTIARY, "text-xs"),
2765
+ children: labels.outOf(max)
2766
+ }),
2767
+ band && /* @__PURE__ */ jsx("span", {
2768
+ className: cn("ml-1 text-sm font-medium", toneText[tone]),
2769
+ children: band.label
2770
+ })
2771
+ ]
2772
+ }),
2773
+ formula && /* @__PURE__ */ jsx("p", {
2774
+ className: cn("text-text-tertiary", "text-xs"),
2775
+ children: formula
2776
+ })
2777
+ ]
2778
+ }), axes.length === 0 ? /* @__PURE__ */ jsx("p", {
2779
+ className: cn(OPT_TEXT_SECONDARY, "text-sm"),
2780
+ children: labels.empty
2781
+ }) : /* @__PURE__ */ jsx("dl", {
2782
+ className: "flex flex-col gap-3",
2783
+ children: axes.map((axis) => /* @__PURE__ */ jsxs("div", {
2784
+ className: "flex flex-col gap-1",
2785
+ children: [
2786
+ /* @__PURE__ */ jsxs("div", {
2787
+ className: "flex items-baseline justify-between gap-2",
2788
+ children: [/* @__PURE__ */ jsxs("dt", {
2789
+ className: cn(OPT_TEXT_PRIMARY, "text-sm"),
2790
+ title: axis.hint,
2791
+ children: [axis.label, axis.weight !== void 0 && /* @__PURE__ */ jsx("span", {
2792
+ className: cn("text-text-tertiary", "ml-1.5 text-xs"),
2793
+ children: labels.weight(`${Math.round(axis.weight * 100)}%`)
2794
+ })]
2795
+ }), /* @__PURE__ */ jsx("dd", {
2796
+ className: cn(OPT_TEXT_SECONDARY, "text-sm font-medium tabular-nums"),
2797
+ children: Math.round(axis.value)
2798
+ })]
2799
+ }),
2800
+ /* @__PURE__ */ jsx(Meter$1, {
2801
+ value: axis.value,
2802
+ max,
2803
+ size: "sm",
2804
+ variant: toneMeter[tone],
2805
+ "aria-label": axis.label
2806
+ }),
2807
+ axis.hint && /* @__PURE__ */ jsx("p", {
2808
+ className: cn("text-text-tertiary", "text-xs"),
2809
+ children: axis.hint
2810
+ })
2811
+ ]
2812
+ }, axis.id))
2813
+ })]
2814
+ });
2815
+ }
2816
+ ScoreBreakdown.displayName = "ScoreBreakdown";
2817
+ //#endregion
2818
+ //#region src/shells/flyout.tsx
2819
+ const defaultLabels$5 = { close: "Close panel" };
2820
+ const sizeClass = {
2821
+ sm: "sm:w-[22rem]",
2822
+ md: "sm:w-[30rem]",
2823
+ lg: "sm:w-[42rem]"
2824
+ };
2825
+ /**
2826
+ * Flyout — a detail panel that does not take the page away.
2827
+ *
2828
+ * Navigating from a list to a record and back costs the reader their scroll
2829
+ * position, their filters and their place in the scan. A flyout keeps the list
2830
+ * on screen and shows the record beside it, so comparing three rows is three
2831
+ * clicks rather than six navigations.
2832
+ *
2833
+ * Deliberately **not** a modal: no focus trap, no `aria-modal`, no inert page.
2834
+ * The list behind it is still the point, and a reader who tabs out of the panel
2835
+ * to change a filter is doing the expected thing. Escape closes; focus returns
2836
+ * to whatever opened it.
2837
+ *
2838
+ * @example
2839
+ * ```tsx
2840
+ * <Flyout open={Boolean(selected)} onClose={clear} title={selected?.name}>
2841
+ * <DescriptionList items={fields} />
2842
+ * </Flyout>
2843
+ * ```
2844
+ */
2845
+ function Flyout({ open, onClose, title, description, type = "overlay", side = "right", size = "md", actions, footer, scrim = false, closeOnOutsideClick = true, hideCloseButton = false, labels: customLabels, className, children }) {
2846
+ const labels = _objectSpread2(_objectSpread2({}, defaultLabels$5), customLabels);
2847
+ const panelRef = useRef(null);
2848
+ const openerRef = useRef(null);
2849
+ useEffect(() => {
2850
+ var _panelRef$current;
2851
+ if (!open) return;
2852
+ openerRef.current = document.activeElement;
2853
+ (_panelRef$current = panelRef.current) === null || _panelRef$current === void 0 || _panelRef$current.focus({ preventScroll: true });
2854
+ return () => {
2855
+ var _openerRef$current, _openerRef$current$fo;
2856
+ (_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 });
2857
+ };
2858
+ }, [open]);
2859
+ useEffect(() => {
2860
+ if (!open) return;
2861
+ const handleKeyDown = (event) => {
2862
+ if (event.key === "Escape") {
2863
+ event.stopPropagation();
2864
+ onClose();
2865
+ }
2866
+ };
2867
+ document.addEventListener("keydown", handleKeyDown);
2868
+ return () => document.removeEventListener("keydown", handleKeyDown);
2869
+ }, [open, onClose]);
2870
+ useEffect(() => {
2871
+ if (!open || !closeOnOutsideClick || type !== "overlay") return;
2872
+ const handlePointerDown = (event) => {
2873
+ const panel = panelRef.current;
2874
+ if (panel && !panel.contains(event.target)) onClose();
2875
+ };
2876
+ const timer = window.setTimeout(() => {
2877
+ document.addEventListener("pointerdown", handlePointerDown);
2878
+ }, 0);
2879
+ return () => {
2880
+ window.clearTimeout(timer);
2881
+ document.removeEventListener("pointerdown", handlePointerDown);
2882
+ };
2883
+ }, [
2884
+ open,
2885
+ closeOnOutsideClick,
2886
+ type,
2887
+ onClose
2888
+ ]);
2889
+ const handleClose = useCallback(() => onClose(), [onClose]);
2890
+ if (!open) return null;
2891
+ const header = (title || actions || !hideCloseButton) && /* @__PURE__ */ jsxs("header", {
2892
+ className: cn("border-border-subtle flex items-start gap-2 border-b px-4 py-3"),
2893
+ children: [
2894
+ /* @__PURE__ */ jsxs("div", {
2895
+ className: "flex min-w-0 grow flex-col gap-0.5",
2896
+ children: [title && /* @__PURE__ */ jsx("h2", {
2897
+ className: cn("text-text-primary", "truncate text-base font-semibold"),
2898
+ children: title
2899
+ }), description && /* @__PURE__ */ jsx("p", {
2900
+ className: cn("text-text-secondary", "truncate text-xs"),
2901
+ children: description
2902
+ })]
2903
+ }),
2904
+ actions,
2905
+ !hideCloseButton && /* @__PURE__ */ jsx("button", {
2906
+ type: "button",
2907
+ onClick: handleClose,
2908
+ "aria-label": labels.close,
2909
+ className: 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"),
2910
+ children: /* @__PURE__ */ jsx(X, {
2911
+ className: "size-4",
2912
+ "aria-hidden": "true"
2913
+ })
2914
+ })
2915
+ ]
2916
+ });
2917
+ const panel = /* @__PURE__ */ jsxs("aside", {
2918
+ ref: panelRef,
2919
+ role: "dialog",
2920
+ "aria-modal": false,
2921
+ "aria-label": typeof title === "string" ? title : void 0,
2922
+ tabIndex: -1,
2923
+ className: cn(OPT_FOCUS_VISIBLE, "flex min-h-0 flex-col", type === "overlay" && [
2924
+ "rounded-xl bg-surface-raised shadow-md",
2925
+ "fixed inset-y-0 z-40 w-full",
2926
+ sizeClass[size],
2927
+ side === "right" ? "right-0 border-l" : "left-0 border-r",
2928
+ "border-border"
2929
+ ], type === "inline" && ["border border-border", "rounded-xl bg-surface"], className),
2930
+ "data-opt-id": "85XVE",
2931
+ "data-opt-slug": "flyout.Flyout",
2932
+ children: [
2933
+ header,
2934
+ /* @__PURE__ */ jsx("div", {
2935
+ className: "min-h-0 grow overflow-y-auto px-4 py-4",
2936
+ children
2937
+ }),
2938
+ footer && /* @__PURE__ */ jsx("footer", {
2939
+ className: "border-border-subtle border-t px-4 py-3",
2940
+ children: footer
2941
+ })
2942
+ ]
2943
+ });
2944
+ if (type === "inline") return panel;
2945
+ return /* @__PURE__ */ jsxs(Fragment, { children: [scrim && /* @__PURE__ */ jsx("div", {
2946
+ className: "bg-overlay fixed inset-0 z-30",
2947
+ "aria-hidden": "true"
2948
+ }), panel] });
2949
+ }
2950
+ Flyout.displayName = "Flyout";
2951
+ //#endregion
2403
2952
  //#region src/lib/use-auto-refresh.ts
2404
2953
  /**
2405
2954
  * A refresh timer that waits for its own work.
@@ -4270,6 +4819,6 @@ function FieldValues({ values, loading, total, sampledRows, onFilter, labels })
4270
4819
  });
4271
4820
  }
4272
4821
  //#endregion
4273
- export { CommandList$1 as $, DataTable as A, SidebarNav as B, WorkflowCanvas as C, ToastProvider as D, NotificationToast as E, DeploymentTimeline as F, CommandPaletteTrigger as G, StatusSelect as H, SettingsForm as I, CommandDialog as J, CommandPalette as K, FaqAccordion as L, BranchSelect as M, EnvPanel as N, toast as O, ProjectSwitcher as P, CommandItem$1 as Q, ContentTabs as R, useAutoRefresh as S, FailureList as T, SearchCombobox as U, AppMenubar as V, DashboardGrid as W, CommandGroup$1 as X, CommandEmpty$1 as Y, CommandInput$1 as Z, withoutField as _, formatQuery as a, DEFAULT_TIME_PRESETS as b, parseQueryWithDiagnostics as c, applyCellAction as d, CommandSeparator$1 as et, fieldValues as f, withFieldValue as g, removeClause as h, formatClause as i, DomainTable as j, useToast as k, EMPTY_QUERY as l, queryFields as m, QueryBar as n, quoteValue as o, isEmptyQuery as p, Command as q, describeClause as r, parseQuery as s, FieldSidebar as t, CommandShortcut as tt, addClause as u, LogTable as v, KeyValueEditor as w, TimeRangeControl as x, TimeSeriesPanel as y, EditorToolbar as z };
4822
+ export { CommandGroup$1 as $, ToastProvider as A, FaqAccordion as B, Flyout as C, KeyValueEditor as D, WorkflowCanvas as E, BranchSelect as F, StatusSelect as G, EditorToolbar as H, EnvPanel as I, CommandPaletteTrigger as J, SearchCombobox as K, ProjectSwitcher as L, useToast as M, DataTable as N, FailureList as O, DomainTable as P, CommandEmpty$1 as Q, DeploymentTimeline as R, useAutoRefresh as S, EventTimeline as T, SidebarNav as U, ContentTabs as V, AppMenubar as W, Command as X, CommandPalette as Y, CommandDialog as Z, withoutField as _, formatQuery as a, DEFAULT_TIME_PRESETS as b, parseQueryWithDiagnostics as c, applyCellAction as d, CommandInput$1 as et, fieldValues as f, withFieldValue as g, removeClause as h, formatClause as i, CommandShortcut as it, toast as j, NotificationToast as k, EMPTY_QUERY as l, queryFields as m, QueryBar as n, CommandList$1 as nt, quoteValue as o, isEmptyQuery as p, DashboardGrid as q, describeClause as r, CommandSeparator$1 as rt, parseQuery as s, FieldSidebar as t, CommandItem$1 as tt, addClause as u, LogTable as v, ScoreBreakdown as w, TimeRangeControl as x, TimeSeriesPanel as y, SettingsForm as z };
4274
4823
 
4275
- //# sourceMappingURL=field-sidebar-BXjvV7Cz.js.map
4824
+ //# sourceMappingURL=field-sidebar-BXl5RJ3q.js.map