@reopt-ai/opt-ui 1.12.5 → 1.14.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 (42) hide show
  1. package/CHANGELOG.md +236 -0
  2. package/COMPONENT_CATALOG.md +670 -123
  3. package/README.md +9 -7
  4. package/dist/core/index.cjs +13 -13
  5. package/dist/core/index.d.cts +2 -2
  6. package/dist/core/index.d.ts +2 -2
  7. package/dist/core/index.js +2 -2
  8. package/dist/docs/02-components/01-core.md +260 -61
  9. package/dist/docs/02-components/02-visuals.md +1 -1
  10. package/dist/docs/02-components/03-shells.md +431 -63
  11. package/dist/docs/02-components/04-surfaces.md +1 -1
  12. package/dist/docs/02-components/index.md +4 -4
  13. package/dist/{field-sidebar-YaWHfLLW.d.ts → field-sidebar-CJ4HxP8D.d.cts} +824 -2
  14. package/dist/{field-sidebar-Ct8c9L4V.d.cts → field-sidebar-DnhpcT2A.d.ts} +824 -2
  15. package/dist/{field-sidebar-DwtrYSSt.cjs → field-sidebar-ePqFX6MN.cjs} +4339 -244
  16. package/dist/{field-sidebar-Ben7MQD4.js → field-sidebar-mSVCMAZp.js} +3976 -235
  17. package/dist/id-registry.cjs +80 -0
  18. package/dist/id-registry.js +80 -0
  19. package/dist/id-registry.json +170 -0
  20. package/dist/index.cjs +1096 -1476
  21. package/dist/index.d.cts +243 -92
  22. package/dist/index.d.ts +243 -92
  23. package/dist/index.js +1004 -1415
  24. package/dist/{key-pad-menu-BpQebDyw.cjs → key-pad-menu-BgtSL3xe.cjs} +60 -487
  25. package/dist/{key-pad-menu-CYYtS_Tl.js → key-pad-menu-CqFI6la9.js} +61 -422
  26. package/dist/{key-pad-menu-CwcY_Vps.d.cts → key-pad-menu-D6pYzaj-.d.cts} +14 -56
  27. package/dist/{key-pad-menu-WBJShCJn.d.ts → key-pad-menu-Yh7Fpb8m.d.ts} +14 -56
  28. package/dist/meta.cjs +1312 -12
  29. package/dist/meta.js +1312 -12
  30. package/dist/shells/index.cjs +20 -1
  31. package/dist/shells/index.d.cts +2 -2
  32. package/dist/shells/index.d.ts +2 -2
  33. package/dist/shells/index.js +2 -2
  34. package/dist/{text-truncate-CQyfUZvq.d.cts → text-truncate-Bbul-jMD.d.cts} +52 -30
  35. package/dist/{text-truncate-CQyfUZvq.d.ts → text-truncate-Bbul-jMD.d.ts} +52 -30
  36. package/dist/{text-truncate-Dlm7JwDf.js → text-truncate-Bd-0WkSP.js} +461 -97
  37. package/dist/{text-truncate-CqdiQuj8.cjs → text-truncate-BeYW6XZu.cjs} +523 -99
  38. package/dist/theme/server.cjs +2 -2
  39. package/dist/theme/server.d.cts +2 -2
  40. package/dist/theme/server.d.ts +2 -2
  41. package/dist/theme/server.js +2 -2
  42. package/package.json +6 -2
package/dist/index.cjs CHANGED
@@ -1,114 +1,16 @@
1
1
  "use client";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const require_text_truncate = require("./text-truncate-CqdiQuj8.cjs");
4
- const require_key_pad_menu = require("./key-pad-menu-BpQebDyw.cjs");
5
- const require_field_sidebar = require("./field-sidebar-DwtrYSSt.cjs");
3
+ const require_text_truncate = require("./text-truncate-BeYW6XZu.cjs");
4
+ const require_key_pad_menu = require("./key-pad-menu-BgtSL3xe.cjs");
5
+ const require_field_sidebar = require("./field-sidebar-ePqFX6MN.cjs");
6
6
  let react_jsx_runtime = require("react/jsx-runtime");
7
7
  let react = require("react");
8
8
  react = require_text_truncate.__toESM(react, 1);
9
- let _reopt_ai_opt_ui_primitives = require("@reopt-ai/opt-ui-primitives");
10
9
  let lucide_react = require("lucide-react");
11
- let _reopt_ai_opt_palette = require("@reopt-ai/opt-palette");
12
10
  let _reopt_ai_opt_charts = require("@reopt-ai/opt-charts");
13
11
  let match_sorter = require("match-sorter");
14
12
  let next_navigation = require("next/navigation");
15
- //#region src/core/callout.tsx
16
- const _excluded$2 = [
17
- "tone",
18
- "title",
19
- "icon",
20
- "action",
21
- "size",
22
- "className",
23
- "children"
24
- ];
25
- const toneStyles = {
26
- info: "border-info/40 bg-info-subtle",
27
- success: "border-success/40 bg-success-subtle",
28
- warning: "border-warning/40 bg-warning-subtle",
29
- danger: "border-danger/40 bg-danger-subtle",
30
- neutral: "border-border bg-bg-subtle"
31
- };
32
- const iconTone = {
33
- info: "text-info-fg",
34
- success: "text-success-fg",
35
- warning: "text-warning-fg",
36
- danger: "text-danger-fg",
37
- neutral: "text-text-tertiary"
38
- };
39
- const titleTone = {
40
- info: "text-info-fg",
41
- success: "text-success-fg",
42
- warning: "text-warning-fg",
43
- danger: "text-danger-fg",
44
- neutral: "text-text-primary"
45
- };
46
- const defaultIcons = {
47
- info: lucide_react.Info,
48
- success: lucide_react.CircleCheck,
49
- warning: lucide_react.TriangleAlert,
50
- danger: lucide_react.CircleAlert,
51
- neutral: null
52
- };
53
- const sizeStyles$3 = {
54
- sm: "gap-2 p-2.5 text-xs [&_svg]:size-3.5",
55
- md: "gap-3 p-3 text-sm [&_svg]:size-4"
56
- };
57
- /**
58
- * Callout — a notice that belongs to the page, not to the moment.
59
- *
60
- * `Alert` reports something that just happened and can be dismissed once read.
61
- * A callout is the opposite: it is part of the layout, it is true for as long
62
- * as the condition holds, and dismissing it would only hide a fact that is
63
- * still true. "12 new events to triage" above a list, or "these numbers come
64
- * from a 10,000-row sample" above a chart — both are read many times and
65
- * dismissed never.
66
- *
67
- * That is why there is no `onDismiss` here, and why `action` takes one control:
68
- * a notice earns its space by naming the single thing to do about it.
69
- *
70
- * @example
71
- * ```tsx
72
- * <Callout tone="info" title="정리할 신규 이벤트 12개"
73
- * action={<Button size="sm" onClick={showNew}>보기</Button>}>
74
- * 7일 안에 처음 관측된 이벤트입니다.
75
- * </Callout>
76
- * ```
77
- */
78
- function Callout(_ref) {
79
- let { tone = "info", title, icon, action, size = "md", className, children } = _ref, props = require_text_truncate._objectWithoutProperties(_ref, _excluded$2);
80
- const DefaultIcon = defaultIcons[tone];
81
- const glyph = icon === void 0 ? DefaultIcon ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultIcon, { "aria-hidden": "true" }) : null : icon;
82
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({
83
- role: "status",
84
- className: require_text_truncate.cn("flex items-start rounded-lg border", toneStyles[tone], sizeStyles$3[size], className)
85
- }, props), {}, {
86
- "data-opt-id": "AT7TX",
87
- "data-opt-slug": "callout.Callout",
88
- "data-tone": tone,
89
- children: [
90
- glyph && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
91
- className: require_text_truncate.cn("mt-0.5 shrink-0", iconTone[tone]),
92
- children: glyph
93
- }),
94
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
95
- className: "min-w-0 flex-1",
96
- children: [title && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
97
- className: require_text_truncate.cn("font-medium", titleTone[tone]),
98
- children: title
99
- }), children && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
100
- className: require_text_truncate.cn("text-text-secondary", title && "mt-1"),
101
- children
102
- })]
103
- }),
104
- action && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
105
- className: "shrink-0 self-center",
106
- children: action
107
- })
108
- ]
109
- }));
110
- }
111
- //#endregion
13
+ let _reopt_ai_opt_ui_primitives = require("@reopt-ai/opt-ui-primitives");
112
14
  //#region src/core/status-dot.tsx
113
15
  const _excluded$1 = [
114
16
  "tone",
@@ -133,7 +35,7 @@ const textTone = {
133
35
  warning: "text-text-secondary",
134
36
  danger: "text-text-secondary"
135
37
  };
136
- const sizeStyles$2 = {
38
+ const sizeStyles$1 = {
137
39
  sm: "gap-1.5 text-[11px]",
138
40
  md: "gap-2 text-xs"
139
41
  };
@@ -164,7 +66,7 @@ const dotSize = {
164
66
  function StatusDot(_ref) {
165
67
  let { tone = "neutral", size = "md", hint, className, children } = _ref, props = require_text_truncate._objectWithoutProperties(_ref, _excluded$1);
166
68
  const dot = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({
167
- className: require_text_truncate.cn("inline-flex items-center whitespace-nowrap", sizeStyles$2[size], textTone[tone], hint && "focus-visible:ring-ring rounded-sm outline-none focus-visible:ring-2", className),
69
+ className: require_text_truncate.cn("inline-flex items-center whitespace-nowrap", sizeStyles$1[size], textTone[tone], hint && "focus-visible:ring-ring rounded-sm outline-none focus-visible:ring-2", className),
168
70
  tabIndex: hint ? 0 : void 0
169
71
  }, props), {}, {
170
72
  "data-opt-id": "N51AT",
@@ -191,12 +93,12 @@ const _excluded = [
191
93
  "disabled",
192
94
  "onClick"
193
95
  ];
194
- const defaultLabels$35 = {
96
+ const defaultLabels$34 = {
195
97
  copy: "복사",
196
98
  copied: "복사됨",
197
99
  failed: "복사할 수 없습니다"
198
100
  };
199
- const sizeStyles$1 = {
101
+ const sizeStyles = {
200
102
  sm: "h-6 gap-1 text-[11px] [&>svg]:size-3",
201
103
  md: "h-7 gap-1.5 text-xs [&>svg]:size-3.5"
202
104
  };
@@ -238,7 +140,7 @@ const stateTone = {
238
140
  */
239
141
  function CopyButton(_ref) {
240
142
  let { value, variant = "icon", size = "md", feedbackDuration = 1500, onCopy, labels: customLabels, className, disabled, onClick } = _ref, props = require_text_truncate._objectWithoutProperties(_ref, _excluded);
241
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$35), customLabels);
143
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$34), customLabels);
242
144
  const [state, setState] = (0, react.useState)("idle");
243
145
  const timer = (0, react.useRef)(null);
244
146
  (0, react.useEffect)(() => () => {
@@ -271,7 +173,7 @@ function CopyButton(_ref) {
271
173
  onClick: handleClick,
272
174
  disabled,
273
175
  "aria-label": variant === "icon" ? label : void 0,
274
- className: require_text_truncate.cn("inline-flex shrink-0 items-center rounded transition-colors", "disabled:pointer-events-none disabled:opacity-50", require_text_truncate.OPT_FOCUS_VISIBLE, sizeStyles$1[size], paddingStyles[variant][size], stateTone[state], className)
176
+ className: require_text_truncate.cn("inline-flex shrink-0 items-center rounded transition-colors", "disabled:pointer-events-none disabled:opacity-50", require_text_truncate.OPT_FOCUS_VISIBLE, sizeStyles[size], paddingStyles[variant][size], stateTone[state], className)
275
177
  }, props), {}, {
276
178
  "data-opt-id": "E0C8T",
277
179
  "data-opt-slug": "copy-button.CopyButton",
@@ -289,76 +191,6 @@ function CopyButton(_ref) {
289
191
  }));
290
192
  }
291
193
  //#endregion
292
- //#region src/core/segmented-control.tsx
293
- const sizeStyles = {
294
- sm: "h-7 text-[11px] [&_svg]:size-3",
295
- md: "h-8 text-xs [&_svg]:size-3.5"
296
- };
297
- const segmentPadding = {
298
- sm: "gap-1 px-2",
299
- md: "gap-1.5 px-2.5"
300
- };
301
- /**
302
- * SegmentedControl — one of a few views, all of them visible.
303
- *
304
- * A `Select` hides the alternatives behind a click; a `ToggleGroup` presses
305
- * buttons that may be on together. Neither is what "이벤트 | 속성" is: exactly
306
- * one is true, the set is small enough to show, and the reader is choosing a
307
- * view rather than filtering anything.
308
- *
309
- * Built on the radio-group primitive rather than on buttons, so the arrow keys,
310
- * the group name and the checked state come from the platform. A control that
311
- * merely looks like a radiogroup is one a keyboard cannot drive.
312
- *
313
- * @example
314
- * ```tsx
315
- * <SegmentedControl
316
- * aria-label="카탈로그 탭"
317
- * options={[
318
- * { value: "events", label: "이벤트", badge: 128 },
319
- * { value: "properties", label: "속성", badge: 341 },
320
- * ]}
321
- * value={tab}
322
- * onChange={setTab}
323
- * />
324
- * ```
325
- */
326
- function SegmentedControl({ options, value, onChange, size = "md", fullWidth = false, disabled = false, "aria-label": ariaLabel, className }) {
327
- const name = (0, react.useId)();
328
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.RadioGroupRoot, {
329
- value,
330
- onValueChange: (next) => onChange(next),
331
- disabled,
332
- orientation: "horizontal",
333
- name,
334
- "aria-label": ariaLabel,
335
- className: require_text_truncate.cn("inline-flex items-center rounded-lg border border-border bg-bg-subtle p-0.5", fullWidth && "flex w-full", sizeStyles[size], className),
336
- "data-opt-id": "YKENE",
337
- "data-opt-slug": "segmented-control.SegmentedControl",
338
- children: options.map((option) => {
339
- const selected = option.value === value;
340
- const itemDisabled = disabled || option.disabled;
341
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
342
- className: require_text_truncate.cn("relative inline-flex h-full cursor-pointer items-center justify-center", "rounded-md font-medium whitespace-nowrap transition-colors", "has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-ring has-[:focus-visible]:ring-offset-1 has-[:focus-visible]:ring-offset-ring-offset", segmentPadding[size], fullWidth && "flex-1", selected ? "bg-surface text-text-primary shadow-sm" : "text-text-tertiary hover:text-text-secondary", itemDisabled && "pointer-events-none opacity-50"),
343
- "data-selected": selected || void 0,
344
- children: [
345
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.Radio, {
346
- value: option.value,
347
- disabled: itemDisabled,
348
- className: "absolute inset-0 m-0 h-full w-full cursor-pointer opacity-0"
349
- }),
350
- option.icon,
351
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: option.label }),
352
- option.badge != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
353
- className: require_text_truncate.cn("tabular-nums", selected ? "text-text-tertiary" : "text-text-tertiary/70"),
354
- children: option.badge
355
- })
356
- ]
357
- }, option.value);
358
- })
359
- });
360
- }
361
- //#endregion
362
194
  //#region src/lib/schema-validator.ts
363
195
  /** Handles schema validator. */
364
196
  function schemaValidator(schema) {
@@ -433,7 +265,7 @@ function PageAside({ header, children, footer, widthClassName = "w-60", label, h
433
265
  PageAside.displayName = "PageAside";
434
266
  //#endregion
435
267
  //#region src/shells/task-list.tsx
436
- const defaultLabels$34 = {
268
+ const defaultLabels$33 = {
437
269
  title: "작업 목록",
438
270
  emptyMessage: "작업이 없습니다",
439
271
  completedOf: "{completed}/{total} 완료",
@@ -453,9 +285,9 @@ const defaultLabels$34 = {
453
285
  };
454
286
  /** Renders the task list component. */
455
287
  function TaskList({ tasks, title, onTaskClick, onTaskStatusChange, emptyMessage, showSubtasks = false, maxItems, labels: customLabels, className }) {
456
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$34), customLabels), {}, {
457
- priority: require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$34.priority), customLabels === null || customLabels === void 0 ? void 0 : customLabels.priority),
458
- status: require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$34.status), customLabels === null || customLabels === void 0 ? void 0 : customLabels.status)
288
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$33), customLabels), {}, {
289
+ priority: require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$33.priority), customLabels === null || customLabels === void 0 ? void 0 : customLabels.priority),
290
+ status: require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$33.status), customLabels === null || customLabels === void 0 ? void 0 : customLabels.status)
459
291
  });
460
292
  const displayTitle = title !== null && title !== void 0 ? title : labels.title;
461
293
  const displayEmptyMessage = emptyMessage !== null && emptyMessage !== void 0 ? emptyMessage : labels.emptyMessage;
@@ -613,7 +445,7 @@ function TaskList({ tasks, title, onTaskClick, onTaskStatusChange, emptyMessage,
613
445
  }
614
446
  //#endregion
615
447
  //#region src/shells/activity-feed.tsx
616
- const defaultLabels$33 = {
448
+ const defaultLabels$32 = {
617
449
  title: "활동 피드",
618
450
  emptyMessage: "활동이 없습니다",
619
451
  moreItems: "{count}개 더 보기",
@@ -663,24 +495,24 @@ const variantBorder = {
663
495
  function ActivityFeed({ activities, title, maxItems, onActivityClick, onShowMore, emptyMessage, labels: customLabels, locale = "ko-KR", loading = false, layout = "list", renderActions, className }) {
664
496
  var _customLabels$timeAgo, _customLabels$timeAgo2, _customLabels$timeAgo3, _customLabels$timeAgo4, _customLabels$timeAgo5, _customLabels$timeAgo6, _customLabels$timeAgo7, _customLabels$timeAgo8, _customLabels$activit, _customLabels$activit2, _customLabels$activit3, _customLabels$activit4, _customLabels$activit5, _customLabels$activit6, _customLabels$activit7, _customLabels$activit8, _customLabels$activit9, _customLabels$activit10, _customLabels$activit11, _customLabels$activit12, _customLabels$activit13, _customLabels$activit14, _customLabels$title, _customLabels$emptyMe, _customLabels$moreIte;
665
497
  const mergedTimeAgo = {
666
- 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$33.timeAgo.justNow,
667
- 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$33.timeAgo.minutesAgo,
668
- 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$33.timeAgo.hoursAgo,
669
- 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$33.timeAgo.daysAgo
498
+ 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$32.timeAgo.justNow,
499
+ 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$32.timeAgo.minutesAgo,
500
+ 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$32.timeAgo.hoursAgo,
501
+ 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$32.timeAgo.daysAgo
670
502
  };
671
503
  const mergedActivityVerbs = {
672
- created: (_customLabels$activit = customLabels === null || customLabels === void 0 || (_customLabels$activit2 = customLabels.activityVerbs) === null || _customLabels$activit2 === void 0 ? void 0 : _customLabels$activit2.created) !== null && _customLabels$activit !== void 0 ? _customLabels$activit : defaultLabels$33.activityVerbs.created,
673
- updated: (_customLabels$activit3 = customLabels === null || customLabels === void 0 || (_customLabels$activit4 = customLabels.activityVerbs) === null || _customLabels$activit4 === void 0 ? void 0 : _customLabels$activit4.updated) !== null && _customLabels$activit3 !== void 0 ? _customLabels$activit3 : defaultLabels$33.activityVerbs.updated,
674
- completed: (_customLabels$activit5 = customLabels === null || customLabels === void 0 || (_customLabels$activit6 = customLabels.activityVerbs) === null || _customLabels$activit6 === void 0 ? void 0 : _customLabels$activit6.completed) !== null && _customLabels$activit5 !== void 0 ? _customLabels$activit5 : defaultLabels$33.activityVerbs.completed,
675
- commented: (_customLabels$activit7 = customLabels === null || customLabels === void 0 || (_customLabels$activit8 = customLabels.activityVerbs) === null || _customLabels$activit8 === void 0 ? void 0 : _customLabels$activit8.commented) !== null && _customLabels$activit7 !== void 0 ? _customLabels$activit7 : defaultLabels$33.activityVerbs.commented,
676
- assigned: (_customLabels$activit9 = customLabels === null || customLabels === void 0 || (_customLabels$activit10 = customLabels.activityVerbs) === null || _customLabels$activit10 === void 0 ? void 0 : _customLabels$activit10.assigned) !== null && _customLabels$activit9 !== void 0 ? _customLabels$activit9 : defaultLabels$33.activityVerbs.assigned,
677
- deployed: (_customLabels$activit11 = customLabels === null || customLabels === void 0 || (_customLabels$activit12 = customLabels.activityVerbs) === null || _customLabels$activit12 === void 0 ? void 0 : _customLabels$activit12.deployed) !== null && _customLabels$activit11 !== void 0 ? _customLabels$activit11 : defaultLabels$33.activityVerbs.deployed,
678
- merged: (_customLabels$activit13 = customLabels === null || customLabels === void 0 || (_customLabels$activit14 = customLabels.activityVerbs) === null || _customLabels$activit14 === void 0 ? void 0 : _customLabels$activit14.merged) !== null && _customLabels$activit13 !== void 0 ? _customLabels$activit13 : defaultLabels$33.activityVerbs.merged
504
+ created: (_customLabels$activit = customLabels === null || customLabels === void 0 || (_customLabels$activit2 = customLabels.activityVerbs) === null || _customLabels$activit2 === void 0 ? void 0 : _customLabels$activit2.created) !== null && _customLabels$activit !== void 0 ? _customLabels$activit : defaultLabels$32.activityVerbs.created,
505
+ updated: (_customLabels$activit3 = customLabels === null || customLabels === void 0 || (_customLabels$activit4 = customLabels.activityVerbs) === null || _customLabels$activit4 === void 0 ? void 0 : _customLabels$activit4.updated) !== null && _customLabels$activit3 !== void 0 ? _customLabels$activit3 : defaultLabels$32.activityVerbs.updated,
506
+ completed: (_customLabels$activit5 = customLabels === null || customLabels === void 0 || (_customLabels$activit6 = customLabels.activityVerbs) === null || _customLabels$activit6 === void 0 ? void 0 : _customLabels$activit6.completed) !== null && _customLabels$activit5 !== void 0 ? _customLabels$activit5 : defaultLabels$32.activityVerbs.completed,
507
+ commented: (_customLabels$activit7 = customLabels === null || customLabels === void 0 || (_customLabels$activit8 = customLabels.activityVerbs) === null || _customLabels$activit8 === void 0 ? void 0 : _customLabels$activit8.commented) !== null && _customLabels$activit7 !== void 0 ? _customLabels$activit7 : defaultLabels$32.activityVerbs.commented,
508
+ assigned: (_customLabels$activit9 = customLabels === null || customLabels === void 0 || (_customLabels$activit10 = customLabels.activityVerbs) === null || _customLabels$activit10 === void 0 ? void 0 : _customLabels$activit10.assigned) !== null && _customLabels$activit9 !== void 0 ? _customLabels$activit9 : defaultLabels$32.activityVerbs.assigned,
509
+ deployed: (_customLabels$activit11 = customLabels === null || customLabels === void 0 || (_customLabels$activit12 = customLabels.activityVerbs) === null || _customLabels$activit12 === void 0 ? void 0 : _customLabels$activit12.deployed) !== null && _customLabels$activit11 !== void 0 ? _customLabels$activit11 : defaultLabels$32.activityVerbs.deployed,
510
+ merged: (_customLabels$activit13 = customLabels === null || customLabels === void 0 || (_customLabels$activit14 = customLabels.activityVerbs) === null || _customLabels$activit14 === void 0 ? void 0 : _customLabels$activit14.merged) !== null && _customLabels$activit13 !== void 0 ? _customLabels$activit13 : defaultLabels$32.activityVerbs.merged
679
511
  };
680
512
  const labels = {
681
- title: (_customLabels$title = customLabels === null || customLabels === void 0 ? void 0 : customLabels.title) !== null && _customLabels$title !== void 0 ? _customLabels$title : defaultLabels$33.title,
682
- emptyMessage: (_customLabels$emptyMe = customLabels === null || customLabels === void 0 ? void 0 : customLabels.emptyMessage) !== null && _customLabels$emptyMe !== void 0 ? _customLabels$emptyMe : defaultLabels$33.emptyMessage,
683
- moreItems: (_customLabels$moreIte = customLabels === null || customLabels === void 0 ? void 0 : customLabels.moreItems) !== null && _customLabels$moreIte !== void 0 ? _customLabels$moreIte : defaultLabels$33.moreItems,
513
+ title: (_customLabels$title = customLabels === null || customLabels === void 0 ? void 0 : customLabels.title) !== null && _customLabels$title !== void 0 ? _customLabels$title : defaultLabels$32.title,
514
+ emptyMessage: (_customLabels$emptyMe = customLabels === null || customLabels === void 0 ? void 0 : customLabels.emptyMessage) !== null && _customLabels$emptyMe !== void 0 ? _customLabels$emptyMe : defaultLabels$32.emptyMessage,
515
+ moreItems: (_customLabels$moreIte = customLabels === null || customLabels === void 0 ? void 0 : customLabels.moreItems) !== null && _customLabels$moreIte !== void 0 ? _customLabels$moreIte : defaultLabels$32.moreItems,
684
516
  timeAgo: mergedTimeAgo,
685
517
  activityVerbs: mergedActivityVerbs
686
518
  };
@@ -808,13 +640,13 @@ function ActivityFeed({ activities, title, maxItems, onActivityClick, onShowMore
808
640
  ActivityFeed.displayName = "ActivityFeed";
809
641
  //#endregion
810
642
  //#region src/shells/filter-bar.tsx
811
- const defaultLabels$32 = {
643
+ const defaultLabels$31 = {
812
644
  clearAll: "필터 초기화",
813
645
  searchPlaceholder: "검색..."
814
646
  };
815
647
  /** Renders the filter bar component. */
816
648
  function FilterBar({ filters, onFilterChange, onDateRangeChange, onClearAll, labels: customLabels, presetActions, className }) {
817
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$32), customLabels);
649
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$31), customLabels);
818
650
  const [searchValues, setSearchValues] = (0, react.useState)({});
819
651
  const [tagInputValues, setTagInputValues] = (0, react.useState)({});
820
652
  const [multiSelectOpen, setMultiSelectOpen] = (0, react.useState)({});
@@ -1001,7 +833,7 @@ function FilterBar({ filters, onFilterChange, onDateRangeChange, onClearAll, lab
1001
833
  })]
1002
834
  }, filter.id);
1003
835
  }
1004
- if (filter.type === "toggle") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_key_pad_menu.Switch, {
836
+ if (filter.type === "toggle") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Switch, {
1005
837
  label: filter.label,
1006
838
  size: "sm",
1007
839
  checked: filter.value === true,
@@ -1040,10 +872,10 @@ function matchesShortcut(event, shortcut) {
1040
872
  if (requiresMeta && !event.metaKey) return false;
1041
873
  return event.key.toLowerCase() === key;
1042
874
  }
1043
- const defaultLabels$31 = { title: "빠른 작업" };
875
+ const defaultLabels$30 = { title: "빠른 작업" };
1044
876
  /** Renders the quick actions component. */
1045
877
  function QuickActions({ actions, title, layout = "grid", columns = 3, showDescriptionInGrid = false, bindShortcuts = false, labels: customLabels, className }) {
1046
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$31), customLabels);
878
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$30), customLabels);
1047
879
  const displayTitle = title !== null && title !== void 0 ? title : labels.title;
1048
880
  const titleId = (0, react.useId)();
1049
881
  (0, react.useEffect)(() => {
@@ -1121,7 +953,7 @@ function QuickActions({ actions, title, layout = "grid", columns = 3, showDescri
1121
953
  QuickActions.displayName = "QuickActions";
1122
954
  //#endregion
1123
955
  //#region src/shells/team-member-list.tsx
1124
- const defaultLabels$30 = {
956
+ const defaultLabels$29 = {
1125
957
  title: "팀원",
1126
958
  emptyMessage: "팀원이 없습니다",
1127
959
  onlineCount: "{online}/{total} 온라인",
@@ -1142,7 +974,7 @@ const statusDot = {
1142
974
  };
1143
975
  /** Renders the team member list component. */
1144
976
  function TeamMemberList({ members, title, layout = "list", maxDisplay, onMemberClick, onShowMore, emptyMessage, labels: customLabels, className }) {
1145
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$30), customLabels), {}, { status: require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$30.status), customLabels === null || customLabels === void 0 ? void 0 : customLabels.status) });
977
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$29), customLabels), {}, { status: require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$29.status), customLabels === null || customLabels === void 0 ? void 0 : customLabels.status) });
1146
978
  const displayTitle = title !== null && title !== void 0 ? title : labels.title;
1147
979
  const displayEmptyMessage = emptyMessage !== null && emptyMessage !== void 0 ? emptyMessage : labels.emptyMessage;
1148
980
  const titleId = (0, react.useId)();
@@ -2042,7 +1874,7 @@ const COLOR_PRESETS = [
2042
1874
  "#f97316",
2043
1875
  "#14b8a6"
2044
1876
  ];
2045
- const defaultLabels$29 = {
1877
+ const defaultLabels$28 = {
2046
1878
  eventLabelPrefix: "Event",
2047
1879
  defaultAriaLabel: "Event icon"
2048
1880
  };
@@ -2073,7 +1905,7 @@ function getEventIconStyle(color) {
2073
1905
  /** Renders the event icon component. */
2074
1906
  function EventIcon({ icon, color, size = "md", className, labels: customLabels }) {
2075
1907
  var _iconEntry$label;
2076
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$29), customLabels);
1908
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$28), customLabels);
2077
1909
  const sizes = SIZE_MAP[size];
2078
1910
  const resolvedColor = (color === null || color === void 0 ? void 0 : color.trim()) ? color : void 0;
2079
1911
  const iconEntry = typeof icon === "string" ? require_text_truncate.getIconRegistryEntry(icon) : void 0;
@@ -2165,14 +1997,14 @@ function ConnectionIndicator({ status, tone, label, showLabel = true, pulse, cla
2165
1997
  ConnectionIndicator.displayName = "ConnectionIndicator";
2166
1998
  //#endregion
2167
1999
  //#region src/shells/floating-action-bar.tsx
2168
- const defaultLabels$28 = {
2000
+ const defaultLabels$27 = {
2169
2001
  toolbarAriaLabel: "Actions",
2170
2002
  selectedLabel: (count) => `${count} selected`,
2171
2003
  dismissAriaLabel: "Dismiss"
2172
2004
  };
2173
2005
  /** Renders the floating action bar component. */
2174
2006
  function FloatingActionBar({ actions, selectedCount, onAction, onDismiss, className, labels: customLabels }) {
2175
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$28), customLabels);
2007
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$27), customLabels);
2176
2008
  if (actions.length === 0) return null;
2177
2009
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2178
2010
  role: "toolbar",
@@ -2209,7 +2041,7 @@ function FloatingActionBar({ actions, selectedCount, onAction, onDismiss, classN
2209
2041
  FloatingActionBar.displayName = "FloatingActionBar";
2210
2042
  //#endregion
2211
2043
  //#region src/shells/template-picker.tsx
2212
- const defaultLabels$27 = {
2044
+ const defaultLabels$26 = {
2213
2045
  ariaLabel: "Templates",
2214
2046
  defaultCategory: "General",
2215
2047
  searchPlaceholder: "Search templates...",
@@ -2217,7 +2049,7 @@ const defaultLabels$27 = {
2217
2049
  };
2218
2050
  /** Renders the template picker component. */
2219
2051
  function TemplatePicker({ templates, selected, onSelect, labels: customLabels, showSearch = false, className }) {
2220
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$27), customLabels);
2052
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$26), customLabels);
2221
2053
  const [search, setSearch] = react.useState("");
2222
2054
  const filtered = react.useMemo(() => {
2223
2055
  if (!showSearch || !search) return templates;
@@ -2303,7 +2135,7 @@ function TemplatePicker({ templates, selected, onSelect, labels: customLabels, s
2303
2135
  TemplatePicker.displayName = "TemplatePicker";
2304
2136
  //#endregion
2305
2137
  //#region src/shells/code-snippet-viewer.tsx
2306
- const defaultLabels$26 = {
2138
+ const defaultLabels$25 = {
2307
2139
  copy: "Copy",
2308
2140
  copied: "Copied!",
2309
2141
  codeRegion: "Code snippet"
@@ -2341,7 +2173,7 @@ const syntaxThemeClasses = {
2341
2173
  /** Renders the code snippet viewer component. */
2342
2174
  function CodeSnippetViewer({ code, language = "text", title, showLineNumbers = true, showCopyButton = true, maxHeight = 400, syntaxTheme = "default", className, labels: customLabels }) {
2343
2175
  var _syntaxThemeClasses$s;
2344
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$26), customLabels);
2176
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$25), customLabels);
2345
2177
  const [copied, setCopied] = react.useState(false);
2346
2178
  const lines = code.split("\n");
2347
2179
  const theme = (_syntaxThemeClasses$s = syntaxThemeClasses[syntaxTheme]) !== null && _syntaxThemeClasses$s !== void 0 ? _syntaxThemeClasses$s : syntaxThemeClasses.default;
@@ -2393,7 +2225,7 @@ function CodeSnippetViewer({ code, language = "text", title, showLineNumbers = t
2393
2225
  CodeSnippetViewer.displayName = "CodeSnippetViewer";
2394
2226
  //#endregion
2395
2227
  //#region src/shells/property-explorer.tsx
2396
- const defaultLabels$25 = {
2228
+ const defaultLabels$24 = {
2397
2229
  searchPlaceholder: "Search properties...",
2398
2230
  noPropertiesFound: "No properties found",
2399
2231
  edit: (property) => `${property} 편집`,
@@ -2401,7 +2233,7 @@ const defaultLabels$25 = {
2401
2233
  };
2402
2234
  /** Renders the property explorer component. */
2403
2235
  function PropertyExplorer({ groups, onSelect, onEdit, className, labels: customLabels }) {
2404
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$25), customLabels);
2236
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$24), customLabels);
2405
2237
  const [search, setSearch] = react.useState("");
2406
2238
  const filtered = react.useMemo(() => {
2407
2239
  if (!search) return groups;
@@ -2485,96 +2317,206 @@ function PropertyExplorer({ groups, onSelect, onEdit, className, labels: customL
2485
2317
  }
2486
2318
  PropertyExplorer.displayName = "PropertyExplorer";
2487
2319
  //#endregion
2488
- //#region src/shells/query-results-table.tsx
2489
- const defaultLabels$24 = {
2490
- emptyMessage: "No results",
2491
- loadingMessage: "Running query...",
2492
- rowsSuffix: "row(s)"
2493
- };
2494
- /** Renders the query results table component. */
2495
- function QueryResultsTable({ columns, rows, maxHeight = 400, emptyMessage, loading = false, error, labels: customLabels, className }) {
2496
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$24), customLabels);
2497
- if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2498
- className: require_text_truncate.cn("border-danger bg-danger-subtle rounded-lg border p-4", className),
2499
- role: "alert",
2500
- "aria-live": "assertive",
2501
- "data-opt-id": "PNC1V",
2502
- "data-opt-slug": "query-results-table.QueryResultsTable",
2503
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2504
- className: "text-danger-fg font-mono text-sm",
2505
- children: error
2506
- })
2507
- });
2508
- if (loading) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2509
- className: require_text_truncate.cn("border-border rounded-lg border p-8 text-center", className),
2510
- role: "status",
2511
- "aria-live": "polite",
2512
- "aria-busy": "true",
2513
- "data-opt-id": "PNC1V",
2514
- "data-opt-slug": "query-results-table.QueryResultsTable",
2515
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2516
- className: "border-border border-t-accent inline-block h-5 w-5 animate-spin rounded-full border-2",
2517
- "aria-hidden": "true"
2518
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2519
- className: "text-text-tertiary mt-2 text-sm",
2520
- children: labels.loadingMessage
2521
- })]
2522
- });
2523
- if (rows.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2524
- className: require_text_truncate.cn("border-border rounded-lg border p-8 text-center", className),
2525
- "data-opt-id": "PNC1V",
2526
- "data-opt-slug": "query-results-table.QueryResultsTable",
2527
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2528
- className: "text-text-tertiary text-sm",
2529
- children: emptyMessage !== null && emptyMessage !== void 0 ? emptyMessage : labels.emptyMessage
2530
- })
2531
- });
2532
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2533
- className: require_text_truncate.cn("border-border overflow-hidden rounded-lg border", className),
2534
- "data-opt-id": "PNC1V",
2535
- "data-opt-slug": "query-results-table.QueryResultsTable",
2536
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2537
- className: "overflow-auto",
2538
- style: { maxHeight },
2539
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
2540
- className: "w-full text-sm",
2541
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", {
2542
- className: "bg-surface-inset sticky top-0",
2543
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {
2544
- className: "text-text-tertiary w-10 px-3 py-2 text-left text-xs font-medium",
2545
- children: "#"
2546
- }), columns.map((col) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {
2547
- className: "text-text-tertiary px-3 py-2 text-left text-xs font-medium whitespace-nowrap",
2548
- children: col
2549
- }, col))] })
2550
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: rows.map((row, ri) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", {
2551
- className: "border-border-subtle hover:bg-bg-subtle border-t",
2552
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
2553
- className: "text-text-tertiary px-3 py-1.5 font-mono text-xs",
2554
- children: ri + 1
2555
- }), columns.map((col) => {
2556
- var _row$col;
2557
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
2558
- className: "text-text-primary px-3 py-1.5 font-mono whitespace-nowrap",
2559
- children: String((_row$col = row[col]) !== null && _row$col !== void 0 ? _row$col : "")
2560
- }, col);
2561
- })]
2562
- }, ri)) })]
2563
- })
2564
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2565
- className: "border-border-subtle bg-surface-inset border-t px-3 py-1.5",
2566
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2567
- className: "text-text-tertiary text-xs",
2568
- children: [
2569
- rows.length,
2570
- " ",
2571
- labels.rowsSuffix
2572
- ]
2573
- })
2574
- })]
2575
- });
2320
+ //#region src/lib/sql/statements.ts
2321
+ /**
2322
+ * Split editor text into statements on top-level semicolons.
2323
+ *
2324
+ * Blank statements (two semicolons in a row, a trailing semicolon) are dropped.
2325
+ */
2326
+ function splitSqlStatements(text) {
2327
+ const out = [];
2328
+ let segmentStart = 0;
2329
+ let i = 0;
2330
+ const length = text.length;
2331
+ const push = (from, to) => {
2332
+ let start = from;
2333
+ let end = to;
2334
+ while (start < end && /\s/.test(text[start])) start++;
2335
+ while (end > start && /\s/.test(text[end - 1])) end--;
2336
+ if (end > start) out.push({
2337
+ text: text.slice(start, end),
2338
+ start,
2339
+ end
2340
+ });
2341
+ };
2342
+ while (i < length) {
2343
+ const ch = text[i];
2344
+ const next = text[i + 1];
2345
+ if (ch === "'" || ch === "\"" || ch === "`") {
2346
+ i = skipQuoted(text, i, ch);
2347
+ continue;
2348
+ }
2349
+ if (ch === "-" && next === "-") {
2350
+ i = skipLineComment(text, i + 2);
2351
+ continue;
2352
+ }
2353
+ if (ch === "/" && next === "*") {
2354
+ i = skipBlockComment(text, i + 2);
2355
+ continue;
2356
+ }
2357
+ if (ch === ";") {
2358
+ push(segmentStart, i);
2359
+ segmentStart = i + 1;
2360
+ }
2361
+ i++;
2362
+ }
2363
+ push(segmentStart, length);
2364
+ return out;
2365
+ }
2366
+ /**
2367
+ * The statement the cursor is in, or the nearest one when the cursor sits in
2368
+ * the whitespace between two. `null` only when there are no statements.
2369
+ *
2370
+ * "Nearest" prefers the statement before the cursor: a reader who just typed a
2371
+ * semicolon and pressed run means the thing they finished, not the empty line
2372
+ * after it.
2373
+ */
2374
+ function sqlStatementAtOffset(statements, offset) {
2375
+ var _before;
2376
+ if (statements.length === 0) return null;
2377
+ let before = null;
2378
+ for (const statement of statements) {
2379
+ if (offset >= statement.start && offset <= statement.end) return statement;
2380
+ if (statement.end < offset) before = statement;
2381
+ else break;
2382
+ }
2383
+ return (_before = before) !== null && _before !== void 0 ? _before : statements[0];
2384
+ }
2385
+ /**
2386
+ * Whether the SQL uses a `{name}` placeholder outside strings and comments.
2387
+ * Also matches `{name.` and `{name(` so a `{filters(...)}` binding counts.
2388
+ */
2389
+ function sqlUsesPlaceholder(sql, name) {
2390
+ const needle = `{${name}`;
2391
+ let i = 0;
2392
+ const length = sql.length;
2393
+ while (i < length) {
2394
+ const ch = sql[i];
2395
+ const next = sql[i + 1];
2396
+ if (ch === "'" || ch === "\"" || ch === "`") {
2397
+ i = skipQuoted(sql, i, ch);
2398
+ continue;
2399
+ }
2400
+ if (ch === "-" && next === "-") {
2401
+ i = skipLineComment(sql, i + 2);
2402
+ continue;
2403
+ }
2404
+ if (ch === "/" && next === "*") {
2405
+ i = skipBlockComment(sql, i + 2);
2406
+ continue;
2407
+ }
2408
+ if (sql.startsWith(needle, i)) {
2409
+ const after = sql[i + needle.length];
2410
+ if (after === "}" || after === "." || after === "(") return true;
2411
+ }
2412
+ i++;
2413
+ }
2414
+ return false;
2415
+ }
2416
+ /**
2417
+ * Replace every `{name}` placeholder outside strings and comments with the
2418
+ * given text. A `{name(...)}` binding is left alone — that form carries
2419
+ * arguments the caller must interpret, and a blind substitution would corrupt it.
2420
+ */
2421
+ function replaceSqlPlaceholder(sql, name, replacement) {
2422
+ const needle = `{${name}}`;
2423
+ let out = "";
2424
+ let i = 0;
2425
+ const length = sql.length;
2426
+ while (i < length) {
2427
+ const ch = sql[i];
2428
+ const next = sql[i + 1];
2429
+ let skipTo = -1;
2430
+ if (ch === "'" || ch === "\"" || ch === "`") skipTo = skipQuoted(sql, i, ch);
2431
+ else if (ch === "-" && next === "-") skipTo = skipLineComment(sql, i + 2);
2432
+ else if (ch === "/" && next === "*") skipTo = skipBlockComment(sql, i + 2);
2433
+ if (skipTo !== -1) {
2434
+ out += sql.slice(i, skipTo);
2435
+ i = skipTo;
2436
+ continue;
2437
+ }
2438
+ if (sql.startsWith(needle, i)) {
2439
+ out += replacement;
2440
+ i += needle.length;
2441
+ continue;
2442
+ }
2443
+ out += ch;
2444
+ i++;
2445
+ }
2446
+ return out;
2447
+ }
2448
+ /** Names of every `{name}` / `{name.x}` placeholder, in order of first use. */
2449
+ function sqlPlaceholderNames(sql) {
2450
+ const names = [];
2451
+ const seen = /* @__PURE__ */ new Set();
2452
+ let i = 0;
2453
+ const length = sql.length;
2454
+ while (i < length) {
2455
+ const ch = sql[i];
2456
+ const next = sql[i + 1];
2457
+ if (ch === "'" || ch === "\"" || ch === "`") {
2458
+ i = skipQuoted(sql, i, ch);
2459
+ continue;
2460
+ }
2461
+ if (ch === "-" && next === "-") {
2462
+ i = skipLineComment(sql, i + 2);
2463
+ continue;
2464
+ }
2465
+ if (ch === "/" && next === "*") {
2466
+ i = skipBlockComment(sql, i + 2);
2467
+ continue;
2468
+ }
2469
+ if (ch === "{") {
2470
+ const match = /^\{([A-Za-z_][A-Za-z0-9_]*)/.exec(sql.slice(i));
2471
+ if (match) {
2472
+ const name = match[1];
2473
+ if (!seen.has(name)) {
2474
+ seen.add(name);
2475
+ names.push(name);
2476
+ }
2477
+ i += match[0].length;
2478
+ continue;
2479
+ }
2480
+ }
2481
+ i++;
2482
+ }
2483
+ return names;
2484
+ }
2485
+ function skipQuoted(text, from, quote) {
2486
+ let i = from + 1;
2487
+ const length = text.length;
2488
+ while (i < length) {
2489
+ const ch = text[i];
2490
+ if (ch === "\\") {
2491
+ i += 2;
2492
+ continue;
2493
+ }
2494
+ if (ch === quote) {
2495
+ if (text[i + 1] === quote) {
2496
+ i += 2;
2497
+ continue;
2498
+ }
2499
+ return i + 1;
2500
+ }
2501
+ i++;
2502
+ }
2503
+ return length;
2504
+ }
2505
+ function skipLineComment(text, from) {
2506
+ let i = from;
2507
+ const length = text.length;
2508
+ while (i < length && text[i] !== "\n") i++;
2509
+ return i;
2510
+ }
2511
+ function skipBlockComment(text, from) {
2512
+ let i = from;
2513
+ const length = text.length;
2514
+ while (i < length) {
2515
+ if (text[i] === "*" && text[i + 1] === "/") return i + 2;
2516
+ i++;
2517
+ }
2518
+ return length;
2576
2519
  }
2577
- QueryResultsTable.displayName = "QueryResultsTable";
2578
2520
  //#endregion
2579
2521
  //#region src/shells/alert-builder.tsx
2580
2522
  const defaultLabels$23 = {
@@ -2802,188 +2744,8 @@ function AlertBuilder({ value, onChange, metrics = [
2802
2744
  }
2803
2745
  AlertBuilder.displayName = "AlertBuilder";
2804
2746
  //#endregion
2805
- //#region src/shells/sql-editor.tsx
2806
- const defaultLabels$22 = {
2807
- placeholder: "SELECT * FROM events LIMIT 100",
2808
- runHint: "Ctrl+Enter to run",
2809
- runButton: "Run Query",
2810
- editor: "SQL 편집기"
2811
- };
2812
- /** Renders the sql editor component. */
2813
- function SqlEditor({ value, onChange, onRun, onRunAndAdvance, placeholder, readOnly = false, height = 200, schema, completionSources, keymap: customKeymap, extensions: customExtensions, ariaLabel, autoFocus = false, showFooter, labels: customLabels, className }) {
2814
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$22), customLabels);
2815
- const fill = height === "fill";
2816
- const footerVisible = showFooter !== null && showFooter !== void 0 ? showFooter : Boolean(onRun);
2817
- const editorRef = react.useRef(null);
2818
- const viewRef = react.useRef(null);
2819
- const completionCompartmentRef = react.useRef(null);
2820
- const editableCompartmentRef = react.useRef(null);
2821
- const buildCompletionRef = react.useRef(null);
2822
- const latestValueRef = react.useRef(value);
2823
- const onChangeRef = react.useRef(onChange);
2824
- const onRunRef = react.useRef(onRun);
2825
- const onRunAndAdvanceRef = react.useRef(onRunAndAdvance);
2826
- const customKeymapRef = react.useRef(customKeymap);
2827
- const schemaRef = react.useRef(schema);
2828
- const completionSourcesRef = react.useRef(completionSources);
2829
- const autoFocusRef = react.useRef(autoFocus);
2830
- latestValueRef.current = value;
2831
- onChangeRef.current = onChange;
2832
- onRunRef.current = onRun;
2833
- onRunAndAdvanceRef.current = onRunAndAdvance;
2834
- customKeymapRef.current = customKeymap;
2835
- schemaRef.current = schema;
2836
- completionSourcesRef.current = completionSources;
2837
- autoFocusRef.current = autoFocus;
2838
- const autocompleteEnabled = react.useRef(schema !== void 0 || completionSources !== void 0).current;
2839
- react.useEffect(() => {
2840
- let view = null;
2841
- let cancelled = false;
2842
- async function init() {
2843
- var _customKeymapRef$curr;
2844
- const parent = editorRef.current;
2845
- if (!parent) return;
2846
- const [{ EditorView, keymap: cmKeymap, placeholder: cmPlaceholder }, { Compartment, EditorState }, langSql, { oneDark }, autocomplete] = await Promise.all([
2847
- import("@codemirror/view"),
2848
- import("@codemirror/state"),
2849
- import("@codemirror/lang-sql"),
2850
- import("@codemirror/theme-one-dark"),
2851
- autocompleteEnabled ? import("@codemirror/autocomplete") : Promise.resolve(null)
2852
- ]);
2853
- if (cancelled || !parent.isConnected) return;
2854
- const completionCompartment = new Compartment();
2855
- const editableCompartment = new Compartment();
2856
- completionCompartmentRef.current = completionCompartment;
2857
- editableCompartmentRef.current = editableCompartment;
2858
- const buildCompletion = (nextSchema, nextSources) => {
2859
- if (!autocomplete) return [];
2860
- const dialect = langSql.PostgreSQL;
2861
- return [autocomplete.autocompletion({ override: [
2862
- langSql.schemaCompletionSource({
2863
- dialect,
2864
- schema: nextSchema !== null && nextSchema !== void 0 ? nextSchema : {}
2865
- }),
2866
- langSql.keywordCompletionSource(dialect),
2867
- ...nextSources !== null && nextSources !== void 0 ? nextSources : []
2868
- ] }), cmKeymap.of(autocomplete.completionKeymap)];
2869
- };
2870
- buildCompletionRef.current = buildCompletion;
2871
- const runKeymap = [{
2872
- key: "Shift-Enter",
2873
- run: (target) => {
2874
- const handler = onRunAndAdvanceRef.current;
2875
- if (!handler) return false;
2876
- handler(target.state.doc.toString());
2877
- return true;
2878
- }
2879
- }, {
2880
- key: "Mod-Enter",
2881
- run: (target) => {
2882
- const handler = onRunRef.current;
2883
- if (!handler) return false;
2884
- handler(target.state.doc.toString());
2885
- return true;
2886
- }
2887
- }];
2888
- const extensions = [
2889
- langSql.PostgreSQL.extension,
2890
- completionCompartment.of(buildCompletion(schemaRef.current, completionSourcesRef.current)),
2891
- oneDark,
2892
- cmKeymap.of([...(_customKeymapRef$curr = customKeymapRef.current) !== null && _customKeymapRef$curr !== void 0 ? _customKeymapRef$curr : [], ...runKeymap]),
2893
- EditorView.theme({
2894
- "&": { height: "100%" },
2895
- "&.cm-focused": {
2896
- outline: "2px solid var(--opt-ring)",
2897
- outlineOffset: "-2px"
2898
- },
2899
- ".cm-scroller": {
2900
- height: "100%",
2901
- overflow: "auto"
2902
- }
2903
- }),
2904
- cmPlaceholder(placeholder !== null && placeholder !== void 0 ? placeholder : labels.placeholder),
2905
- EditorView.lineWrapping,
2906
- EditorView.updateListener.of((update) => {
2907
- if (update.docChanged) onChangeRef.current(update.state.doc.toString());
2908
- }),
2909
- editableCompartment.of(EditorView.editable.of(!readOnly)),
2910
- EditorView.contentAttributes.of({ "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : labels.editor }),
2911
- ...customExtensions !== null && customExtensions !== void 0 ? customExtensions : []
2912
- ];
2913
- view = new EditorView({
2914
- state: EditorState.create({
2915
- doc: latestValueRef.current,
2916
- extensions
2917
- }),
2918
- parent
2919
- });
2920
- viewRef.current = view;
2921
- if (autoFocusRef.current) view.focus();
2922
- }
2923
- init();
2924
- return () => {
2925
- cancelled = true;
2926
- view === null || view === void 0 || view.destroy();
2927
- if (viewRef.current === view) viewRef.current = null;
2928
- completionCompartmentRef.current = null;
2929
- editableCompartmentRef.current = null;
2930
- buildCompletionRef.current = null;
2931
- };
2932
- }, []);
2933
- react.useEffect(() => {
2934
- const view = viewRef.current;
2935
- if (view && view.state.doc.toString() !== value) view.dispatch({ changes: {
2936
- from: 0,
2937
- to: view.state.doc.length,
2938
- insert: value
2939
- } });
2940
- }, [value]);
2941
- react.useEffect(() => {
2942
- const view = viewRef.current;
2943
- const compartment = completionCompartmentRef.current;
2944
- const build = buildCompletionRef.current;
2945
- if (!view || !compartment || !build) return;
2946
- view.dispatch({ effects: compartment.reconfigure(build(schema, completionSources)) });
2947
- }, [schema, completionSources]);
2948
- react.useEffect(() => {
2949
- const view = viewRef.current;
2950
- const compartment = editableCompartmentRef.current;
2951
- if (!view || !compartment) return;
2952
- let cancelled = false;
2953
- import("@codemirror/view").then(({ EditorView }) => {
2954
- if (cancelled || viewRef.current !== view) return;
2955
- view.dispatch({ effects: compartment.reconfigure(EditorView.editable.of(!readOnly)) });
2956
- });
2957
- return () => {
2958
- cancelled = true;
2959
- };
2960
- }, [readOnly]);
2961
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2962
- className: require_text_truncate.cn("gap-element flex flex-col", fill && "h-full min-h-0", className),
2963
- "data-opt-id": "RSGKV",
2964
- "data-opt-slug": "sql-editor.SqlEditor",
2965
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2966
- ref: editorRef,
2967
- className: require_text_truncate.cn("border-border overflow-hidden rounded-lg border", fill && "min-h-0 flex-1"),
2968
- style: fill ? void 0 : { height }
2969
- }), footerVisible && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2970
- className: "flex flex-none items-center justify-between",
2971
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2972
- className: "text-text-tertiary text-xs",
2973
- children: labels.runHint
2974
- }), onRun && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Button, {
2975
- variant: "primary",
2976
- size: "sm",
2977
- onClick: () => onRun(value),
2978
- children: labels.runButton
2979
- })]
2980
- })]
2981
- });
2982
- }
2983
- SqlEditor.displayName = "SqlEditor";
2984
- //#endregion
2985
2747
  //#region src/shells/event-meta-editor.tsx
2986
- const defaultLabels$21 = {
2748
+ const defaultLabels$22 = {
2987
2749
  icon: "Icon",
2988
2750
  color: "Color",
2989
2751
  name: "Event name",
@@ -3001,7 +2763,7 @@ const defaultLabels$21 = {
3001
2763
  };
3002
2764
  /** Renders the event meta editor component. */
3003
2765
  function EventMetaEditor({ value, onChange, icons, sections, footer, className, labels: customLabels }) {
3004
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$21), customLabels);
2766
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$22), customLabels);
3005
2767
  const hasTags = value.tags.length > 0;
3006
2768
  const nameId = react.useId();
3007
2769
  const displayNameId = react.useId();
@@ -3124,10 +2886,10 @@ function EventMetaEditor({ value, onChange, icons, sections, footer, className,
3124
2886
  EventMetaEditor.displayName = "EventMetaEditor";
3125
2887
  //#endregion
3126
2888
  //#region src/shells/widget-grid.tsx
3127
- const defaultLabels$20 = { emptyMessage: "No widgets to display" };
2889
+ const defaultLabels$21 = { emptyMessage: "No widgets to display" };
3128
2890
  /** Renders the widget grid component. */
3129
2891
  function WidgetGrid({ widgets, cols = 12, rowHeight = 80, gap = 16, editable = false, onReorder, labels: customLabels, className }) {
3130
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$20), customLabels);
2892
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$21), customLabels);
3131
2893
  const [dragIdx, setDragIdx] = react.useState(null);
3132
2894
  const [overIdx, setOverIdx] = react.useState(null);
3133
2895
  if (widgets.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -3172,7 +2934,7 @@ function WidgetGrid({ widgets, cols = 12, rowHeight = 80, gap = 16, editable = f
3172
2934
  WidgetGrid.displayName = "WidgetGrid";
3173
2935
  //#endregion
3174
2936
  //#region src/shells/report-builder.tsx
3175
- const defaultLabels$19 = {
2937
+ const defaultLabels$20 = {
3176
2938
  stepsLabel: "Report steps",
3177
2939
  stepType: "Chart Type",
3178
2940
  stepConfig: "Data Config",
@@ -3248,7 +3010,7 @@ const STEPS = [
3248
3010
  ];
3249
3011
  /** Renders the report builder component. */
3250
3012
  function ReportBuilder({ chartTypes = DEFAULT_CHART_TYPES, dataSources = [], onSave, onCancel, configForm, styleForm, preview, onChartTypeChange, onDataSourceChange, onStepChange, selectedChartType: controlledChartType, selectedDataSource: controlledDataSource, labels: customLabels, className }) {
3251
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$19), customLabels);
3013
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$20), customLabels);
3252
3014
  const [stepIndex, setStepIndex] = react.useState(0);
3253
3015
  const [internalChartType, setInternalChartType] = react.useState(null);
3254
3016
  const [internalDataSource, setInternalDataSource] = react.useState("");
@@ -3468,7 +3230,7 @@ function ReportBuilder({ chartTypes = DEFAULT_CHART_TYPES, dataSources = [], onS
3468
3230
  ReportBuilder.displayName = "ReportBuilder";
3469
3231
  //#endregion
3470
3232
  //#region src/shells/audit-timeline.tsx
3471
- const defaultLabels$18 = {
3233
+ const defaultLabels$19 = {
3472
3234
  title: "변경 이력",
3473
3235
  empty: "변경 이력이 없습니다",
3474
3236
  expand: "상세 보기",
@@ -3547,7 +3309,7 @@ function DiffTable({ changes, labels }) {
3547
3309
  }
3548
3310
  /** Renders the audit timeline component. */
3549
3311
  function AuditTimeline({ entries, labels: customLabels, className, onEntryClick }) {
3550
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$18), customLabels);
3312
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$19), customLabels);
3551
3313
  const [expandedIds, setExpandedIds] = react.useState(/* @__PURE__ */ new Set());
3552
3314
  const toggleExpanded = react.useCallback((id) => {
3553
3315
  setExpandedIds((prev) => {
@@ -3670,7 +3432,7 @@ function AuditTimeline({ entries, labels: customLabels, className, onEntryClick
3670
3432
  AuditTimeline.displayName = "AuditTimeline";
3671
3433
  //#endregion
3672
3434
  //#region src/shells/segment-builder.tsx
3673
- const defaultLabels$17 = {
3435
+ const defaultLabels$18 = {
3674
3436
  match: "Match",
3675
3437
  ofTheFollowing: "of the following",
3676
3438
  groupLabel: "Segment group",
@@ -3791,7 +3553,7 @@ function SegmentBuilder({ groups, onChange, eventOptions = [
3791
3553
  "New Users",
3792
3554
  "Power Users"
3793
3555
  ], className, labels: customLabels }) {
3794
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$17), customLabels);
3556
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$18), customLabels);
3795
3557
  const builderId = react.useId().replace(/:/g, "");
3796
3558
  const nextIdRef = react.useRef(1);
3797
3559
  const createId = react.useCallback(() => {
@@ -4003,57 +3765,754 @@ function SegmentBuilder({ groups, onChange, eventOptions = [
4003
3765
  }
4004
3766
  SegmentBuilder.displayName = "SegmentBuilder";
4005
3767
  //#endregion
4006
- //#region src/shells/insights-panel.tsx
4007
- const defaultLabels$16 = {
4008
- title: "Insights",
4009
- dismiss: "Dismiss",
4010
- showDismissed: "Show dismissed",
4011
- hideDismissed: "Hide dismissed",
4012
- emptyTitle: "No insights",
4013
- emptyDescription: "No anomalies or notable events detected.",
4014
- detectionTitle: "Detection Settings",
4015
- daysLabel: "Days",
4016
- zScoreLabel: "Z-Score"
3768
+ //#region src/shells/criteria-builder.tsx
3769
+ const defaultLabels$17 = {
3770
+ builder: "조건 빌더",
3771
+ rootLogicLegend: "그룹 결합 방식",
3772
+ matchAllGroups: "모든 그룹 일치",
3773
+ matchAnyGroup: "하나 이상 일치",
3774
+ groupLogicLegend: (letter) => `그룹 ${letter} 조건 결합 방식`,
3775
+ matchAllCriteria: "모두 충족",
3776
+ matchAnyCriteria: "하나 이상 충족",
3777
+ and: "그리고",
3778
+ or: "또는",
3779
+ groupLabel: (letter) => `그룹 ${letter}`,
3780
+ criterionLabel: (letter, index) => `조건 ${letter}.${index}`,
3781
+ actionLabel: (action, target) => `${action}: ${target}`,
3782
+ addCriterion: "조건 추가",
3783
+ addGroup: "그룹 추가",
3784
+ kindMenuTitle: "조건 유형",
3785
+ duplicateCriterion: "조건 복제",
3786
+ removeCriterion: "조건 제거",
3787
+ moveUp: "위로 이동",
3788
+ moveDown: "아래로 이동",
3789
+ negate: "제외",
3790
+ duplicateGroup: "그룹 복제",
3791
+ removeGroup: "그룹 제거",
3792
+ emptyGroup: "이 그룹에는 아직 조건이 없습니다.",
3793
+ unknownKind: (kind) => `알 수 없는 조건 유형 "${kind}"`,
3794
+ unknownKindDescription: "이 조건은 표시할 수 없습니다. 유형을 바꾸거나 제거하세요.",
3795
+ replaceKind: "유형 바꾸기",
3796
+ emptyTitle: "조건이 없습니다",
3797
+ emptyDescription: "첫 그룹을 추가해 누가 포함되는지 정의하세요.",
3798
+ emptyAction: "첫 조건 추가",
3799
+ keyboardHint: "조건 추가 · 그룹 추가",
3800
+ maxGroupsReached: (max) => `그룹은 최대 ${max}개까지 추가할 수 있습니다.`,
3801
+ maxCriteriaReached: (max) => `한 그룹에는 조건을 최대 ${max}개까지 추가할 수 있습니다.`
4017
3802
  };
4018
- const severityConfig = {
4019
- critical: {
4020
- icon: "!!",
4021
- color: "text-danger-fg",
4022
- bg: "bg-danger-subtle border-danger"
4023
- },
4024
- warning: {
4025
- icon: "!",
4026
- color: "text-warning-fg",
4027
- bg: "bg-warning-subtle border-warning"
4028
- },
4029
- info: {
4030
- icon: "i",
4031
- color: "text-info-fg",
4032
- bg: "bg-info-subtle border-info"
3803
+ /** `A`, `B`, … `Z`, `AA`, `AB` — the badge a group wears. */
3804
+ function criteriaGroupLetter(index) {
3805
+ let n = Math.max(0, Math.floor(index));
3806
+ let out = "";
3807
+ do {
3808
+ out = String.fromCharCode(65 + n % 26) + out;
3809
+ n = Math.floor(n / 26) - 1;
3810
+ } while (n >= 0);
3811
+ return out;
3812
+ }
3813
+ function collectIds(value) {
3814
+ const ids = /* @__PURE__ */ new Set();
3815
+ for (const group of value.groups) {
3816
+ ids.add(group.id);
3817
+ for (const criterion of group.criteria) ids.add(criterion.id);
4033
3818
  }
4034
- };
4035
- /** Renders the insights panel component. */
4036
- function InsightsPanel({ insights, stats, detectionTypes, onDetectionTypeToggle, detectionConfig, onDetectionConfigChange, onDismiss, onInsightClick, onStatClick, header, actions, labels: customLabels, className }) {
4037
- var _detectionConfig$days, _detectionConfig$zSco;
4038
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$16), customLabels);
4039
- const [showDismissed, setShowDismissed] = react.useState(false);
4040
- const visibleInsights = showDismissed ? insights : insights.filter((i) => !i.dismissed);
4041
- const hasStats = stats && stats.length > 0;
4042
- const hasDetection = detectionTypes && detectionTypes.length > 0 || detectionConfig != null;
4043
- const isEmpty = visibleInsights.length === 0;
4044
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4045
- className: require_text_truncate.cn("gap-group flex flex-col", className),
4046
- "data-opt-id": "HCYZV",
4047
- "data-opt-slug": "insights-panel.InsightsPanel",
4048
- children: [
4049
- header || actions ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4050
- className: "flex items-center justify-between",
4051
- children: [header && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: header }), actions && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4052
- className: "gap-element flex items-center",
4053
- children: actions
4054
- })]
4055
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4056
- className: "flex items-center justify-between",
3819
+ return ids;
3820
+ }
3821
+ function uniqueId(base, taken) {
3822
+ if (!taken.has(base)) return base;
3823
+ let n = 2;
3824
+ while (taken.has(`${base}-${n}`)) n += 1;
3825
+ return `${base}-${n}`;
3826
+ }
3827
+ function replaceGroup(value, groupIndex, next) {
3828
+ return require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, value), {}, { groups: value.groups.map((group, i) => i === groupIndex ? next : group) });
3829
+ }
3830
+ /**
3831
+ * Moves one criterion a step up or down inside its group. Returns the same
3832
+ * value when the move is impossible, so a caller can `===` to know nothing
3833
+ * changed.
3834
+ */
3835
+ function criteriaBuilderMove(value, groupIndex, index, direction) {
3836
+ const group = value.groups[groupIndex];
3837
+ if (!group) return value;
3838
+ const target = direction === "up" ? index - 1 : index + 1;
3839
+ if (index < 0 || target < 0 || target >= group.criteria.length) return value;
3840
+ const criteria = [...group.criteria];
3841
+ const [moved] = criteria.splice(index, 1);
3842
+ criteria.splice(target, 0, moved);
3843
+ return replaceGroup(value, groupIndex, require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, group), {}, { criteria }));
3844
+ }
3845
+ /**
3846
+ * Inserts a copy of one criterion *before* the original, so the copy takes the
3847
+ * original's place in the reading order and the original follows as the thing
3848
+ * to edit.
3849
+ */
3850
+ function criteriaBuilderDuplicate(value, groupIndex, index, clone) {
3851
+ const group = value.groups[groupIndex];
3852
+ const original = group === null || group === void 0 ? void 0 : group.criteria[index];
3853
+ if (!group || !original) return value;
3854
+ const criteria = [...group.criteria];
3855
+ criteria.splice(index, 0, clone(original));
3856
+ return replaceGroup(value, groupIndex, require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, group), {}, { criteria }));
3857
+ }
3858
+ /** Removes one criterion. The group stays, even when it empties. */
3859
+ function criteriaBuilderRemove(value, groupIndex, index) {
3860
+ const group = value.groups[groupIndex];
3861
+ if (!group || index < 0 || index >= group.criteria.length) return value;
3862
+ return replaceGroup(value, groupIndex, require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, group), {}, { criteria: group.criteria.filter((_, i) => i !== index) }));
3863
+ }
3864
+ /** Appends a criterion to a group, or inserts it at `at`. */
3865
+ function criteriaBuilderInsert(value, groupIndex, criterion, at) {
3866
+ const group = value.groups[groupIndex];
3867
+ if (!group) return value;
3868
+ const criteria = [...group.criteria];
3869
+ const position = at === void 0 ? criteria.length : Math.max(0, Math.min(at, criteria.length));
3870
+ criteria.splice(position, 0, criterion);
3871
+ return replaceGroup(value, groupIndex, require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, group), {}, { criteria }));
3872
+ }
3873
+ /** Removes one group. */
3874
+ function criteriaBuilderRemoveGroup(value, groupIndex) {
3875
+ if (groupIndex < 0 || groupIndex >= value.groups.length) return value;
3876
+ return require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, value), {}, { groups: value.groups.filter((_, i) => i !== groupIndex) });
3877
+ }
3878
+ /** Inserts a copy of one group — every criterion cloned — before the original. */
3879
+ function criteriaBuilderDuplicateGroup(value, groupIndex, groupId, clone) {
3880
+ const group = value.groups[groupIndex];
3881
+ if (!group) return value;
3882
+ const copy = {
3883
+ id: groupId,
3884
+ logic: group.logic,
3885
+ criteria: group.criteria.map(clone)
3886
+ };
3887
+ const groups = [...value.groups];
3888
+ groups.splice(groupIndex, 0, copy);
3889
+ return require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, value), {}, { groups });
3890
+ }
3891
+ const EDITABLE_TARGET = "input, textarea, select, [contenteditable=\"\"], [contenteditable=\"true\"], [role=\"combobox\"], [role=\"textbox\"], [role=\"menu\"], [role=\"menuitem\"], [role=\"listbox\"], [role=\"option\"]";
3892
+ function isEditableTarget(target) {
3893
+ return target instanceof Element && target.closest(EDITABLE_TARGET) !== null;
3894
+ }
3895
+ function sectionKinds(kinds) {
3896
+ const sections = [];
3897
+ const byHeading = /* @__PURE__ */ new Map();
3898
+ for (const option of kinds) {
3899
+ let section = byHeading.get(option.group);
3900
+ if (!section) {
3901
+ section = {
3902
+ heading: option.group,
3903
+ entries: []
3904
+ };
3905
+ byHeading.set(option.group, section);
3906
+ sections.push(section);
3907
+ }
3908
+ section.entries.push({
3909
+ key: option.kind,
3910
+ kind: option.kind,
3911
+ negate: false,
3912
+ option
3913
+ });
3914
+ if (option.negatedLabel) section.entries.push({
3915
+ key: `${option.kind}:not`,
3916
+ kind: option.kind,
3917
+ negate: true,
3918
+ option
3919
+ });
3920
+ }
3921
+ return [...sections.filter((s) => s.heading === void 0), ...sections.filter((s) => s.heading !== void 0)];
3922
+ }
3923
+ function KindMenu({ kinds, menuLabel, triggerLabel, triggerAriaLabel, triggerIcon, disabled = false, disabledReason, variant = "ghost", onPick, addTarget }) {
3924
+ const sections = react.useMemo(() => sectionKinds(kinds), [kinds]);
3925
+ const trigger = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_text_truncate.DropdownTrigger, {
3926
+ disabled,
3927
+ "aria-label": triggerAriaLabel,
3928
+ title: disabled ? disabledReason : void 0,
3929
+ "data-criteria-add": addTarget,
3930
+ className: require_text_truncate.cn("inline-flex cursor-pointer items-center justify-center rounded-lg font-medium transition-colors", "h-[var(--opt-control-sm)] gap-[var(--opt-space-1-5)] px-[var(--opt-control-padding-sm)] text-xs", variant === "secondary" ? "border-border bg-surface text-text-primary hover:bg-bg-subtle active:bg-bg-muted border" : "text-text-secondary hover:bg-bg-subtle active:bg-bg-muted", "disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50", "[&_svg]:size-3.5", require_text_truncate.OPT_FOCUS_VISIBLE),
3931
+ children: [triggerIcon, triggerLabel]
3932
+ });
3933
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_text_truncate.DropdownMenu, {
3934
+ animated: false,
3935
+ children: [trigger, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.DropdownContent, {
3936
+ "aria-label": menuLabel,
3937
+ "aria-labelledby": void 0,
3938
+ className: "min-w-56",
3939
+ children: sections.map((section, si) => {
3940
+ var _section$heading;
3941
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [
3942
+ si > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.DropdownSeparator, {}),
3943
+ section.heading !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3944
+ role: "presentation",
3945
+ className: "text-text-tertiary px-2 pt-1.5 pb-1 text-[11px] font-semibold tracking-wide uppercase",
3946
+ children: section.heading
3947
+ }),
3948
+ section.entries.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_text_truncate.DropdownItem, {
3949
+ onClick: () => onPick(entry.kind, entry.negate),
3950
+ className: "flex items-start gap-2",
3951
+ children: [entry.option.icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3952
+ className: "text-text-tertiary mt-0.5 shrink-0 [&_svg]:size-3.5",
3953
+ children: entry.option.icon
3954
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3955
+ className: "flex min-w-0 flex-col",
3956
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3957
+ className: require_text_truncate.cn("text-sm", entry.negate && "text-danger-fg"),
3958
+ children: entry.negate ? entry.option.negatedLabel : entry.option.label
3959
+ }), !entry.negate && entry.option.description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3960
+ className: "text-text-tertiary text-xs",
3961
+ children: entry.option.description
3962
+ })]
3963
+ })]
3964
+ }, entry.key))
3965
+ ] }, (_section$heading = section.heading) !== null && _section$heading !== void 0 ? _section$heading : "__none");
3966
+ })
3967
+ })]
3968
+ });
3969
+ }
3970
+ function IconAction({ icon, label, onClick, disabled = false, danger = false }) {
3971
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3972
+ type: "button",
3973
+ onClick,
3974
+ disabled,
3975
+ "aria-label": label,
3976
+ title: label,
3977
+ className: require_text_truncate.cn("inline-flex size-7 shrink-0 items-center justify-center rounded-md transition-colors [&_svg]:size-3.5", danger ? "text-text-tertiary hover:bg-danger-subtle hover:text-danger-fg" : "text-text-tertiary hover:bg-bg-muted hover:text-text-primary", "disabled:pointer-events-none disabled:opacity-40", require_text_truncate.OPT_FOCUS_VISIBLE),
3978
+ children: icon
3979
+ });
3980
+ }
3981
+ function LogicDivider({ label, emphasis }) {
3982
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3983
+ role: "separator",
3984
+ "aria-label": label,
3985
+ className: require_text_truncate.cn("flex items-center", emphasis === "root" ? "gap-group" : "gap-element"),
3986
+ children: [
3987
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: require_text_truncate.cn("h-px flex-1", emphasis === "root" ? "bg-border" : "bg-border-subtle") }),
3988
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3989
+ className: require_text_truncate.cn("text-[11px] font-semibold tracking-wide uppercase", emphasis === "root" ? "bg-bg-subtle text-text-secondary border-border rounded-full border px-2 py-0.5" : "text-text-tertiary"),
3990
+ children: label
3991
+ }),
3992
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: require_text_truncate.cn("h-px flex-1", emphasis === "root" ? "bg-border" : "bg-border-subtle") })
3993
+ ]
3994
+ });
3995
+ }
3996
+ function Lettermark({ letter }) {
3997
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3998
+ "aria-hidden": "true",
3999
+ className: "bg-accent-subtle text-accent inline-flex size-6 shrink-0 items-center justify-center rounded-md text-xs font-semibold tabular-nums",
4000
+ children: letter
4001
+ });
4002
+ }
4003
+ /**
4004
+ * CriteriaBuilder — a two-level tree of criteria, edited as sentences.
4005
+ *
4006
+ * The builder owns what every criteria editor needs and nothing a particular
4007
+ * product does: groups and their AND/OR, the order of criteria, negation,
4008
+ * inline errors, the "add" menu, and the keyboard. The consumer owns the
4009
+ * criterion type and renders each one — usually as `Expression` chips whose
4010
+ * values open an editor — so the same shell serves a cohort of users, an
4011
+ * alert condition, or a feature-flag rollout without knowing which.
4012
+ *
4013
+ * Kinds the builder does not know render as an invalid row with a warning and
4014
+ * a replace/remove pair, never as some other kind's fields: a criterion saved
4015
+ * by a newer client should look broken, not silently different.
4016
+ *
4017
+ * @example
4018
+ * ```tsx
4019
+ * <CriteriaBuilder
4020
+ * value={value}
4021
+ * onChange={setValue}
4022
+ * kinds={[
4023
+ * { kind: "performed", label: "이벤트를 수행함", negatedLabel: "이벤트를 수행하지 않음" },
4024
+ * { kind: "property", label: "속성이 일치함" },
4025
+ * ]}
4026
+ * createCriterion={(kind, negate) => ({ id: nextId(), kind, negate, event: "" })}
4027
+ * renderCriterion={(c, api) => (
4028
+ * <ExpressionGroup>
4029
+ * <Expression description="이벤트" value={c.event} onClick={() => open(c.id)} />
4030
+ * </ExpressionGroup>
4031
+ * )}
4032
+ * />
4033
+ * ```
4034
+ */
4035
+ function CriteriaBuilder({ value, onChange, kinds, createCriterion, renderCriterion, cloneCriterion, createGroupId, errors, maxGroups = 10, maxCriteriaPerGroup = 20, allowNegation = true, disabled = false, aside, labels: customLabels, className, "aria-label": ariaLabel }) {
4036
+ const labels = react.useMemo(() => require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$17), customLabels), [customLabels]);
4037
+ const baseId = react.useId().replace(/:/g, "");
4038
+ const nextGroupRef = react.useRef(1);
4039
+ const rootRef = react.useRef(null);
4040
+ const [pendingOpen, setPendingOpen] = react.useState(null);
4041
+ const knownKinds = react.useMemo(() => new Set(kinds.map((k) => k.kind)), [kinds]);
4042
+ const groupsFull = value.groups.length >= maxGroups;
4043
+ const newGroupId = react.useCallback(() => {
4044
+ const taken = collectIds(value);
4045
+ if (createGroupId) return uniqueId(createGroupId(), taken);
4046
+ let id = "";
4047
+ do
4048
+ id = `group-${nextGroupRef.current++}`;
4049
+ while (taken.has(id));
4050
+ return id;
4051
+ }, [createGroupId, value]);
4052
+ const clone = react.useCallback((criterion, taken) => {
4053
+ if (cloneCriterion) return cloneCriterion(criterion);
4054
+ const id = uniqueId(`${criterion.id}-copy`, taken);
4055
+ taken.add(id);
4056
+ return require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, criterion), {}, { id });
4057
+ }, [cloneCriterion]);
4058
+ const addGroup = react.useCallback(() => {
4059
+ if (disabled || groupsFull) return null;
4060
+ const id = newGroupId();
4061
+ onChange(require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, value), {}, { groups: [...value.groups, {
4062
+ id,
4063
+ logic: "and",
4064
+ criteria: []
4065
+ }] }));
4066
+ return id;
4067
+ }, [
4068
+ disabled,
4069
+ groupsFull,
4070
+ newGroupId,
4071
+ onChange,
4072
+ value
4073
+ ]);
4074
+ const addCriterion = react.useCallback((groupIndex, kind, negate) => {
4075
+ const group = value.groups[groupIndex];
4076
+ if (!group || group.criteria.length >= maxCriteriaPerGroup) return;
4077
+ onChange(criteriaBuilderInsert(value, groupIndex, createCriterion(kind, negate)));
4078
+ }, [
4079
+ createCriterion,
4080
+ maxCriteriaPerGroup,
4081
+ onChange,
4082
+ value
4083
+ ]);
4084
+ const addFirst = react.useCallback((kind, negate) => {
4085
+ if (disabled) return;
4086
+ onChange(require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, value), {}, { groups: [{
4087
+ id: newGroupId(),
4088
+ logic: "and",
4089
+ criteria: [createCriterion(kind, negate)]
4090
+ }] }));
4091
+ }, [
4092
+ createCriterion,
4093
+ disabled,
4094
+ newGroupId,
4095
+ onChange,
4096
+ value
4097
+ ]);
4098
+ const replaceCriterion = react.useCallback((groupIndex, index, kind, negate) => {
4099
+ const group = value.groups[groupIndex];
4100
+ const current = group === null || group === void 0 ? void 0 : group.criteria[index];
4101
+ if (!group || !current) return;
4102
+ const replacement = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, createCriterion(kind, negate)), {}, { id: current.id });
4103
+ onChange(replaceGroup(value, groupIndex, require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, group), {}, { criteria: group.criteria.map((c, i) => i === index ? replacement : c) })));
4104
+ }, [
4105
+ createCriterion,
4106
+ onChange,
4107
+ value
4108
+ ]);
4109
+ const updateCriterion = react.useCallback((groupIndex, index, patch) => {
4110
+ const group = value.groups[groupIndex];
4111
+ if (!group) return;
4112
+ onChange(replaceGroup(value, groupIndex, require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, group), {}, { criteria: group.criteria.map((c, i) => i === index ? require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, c), patch) : c) })));
4113
+ }, [onChange, value]);
4114
+ const findAddTrigger = react.useCallback((groupId) => {
4115
+ var _rootRef$current$quer, _rootRef$current;
4116
+ return (_rootRef$current$quer = (_rootRef$current = rootRef.current) === null || _rootRef$current === void 0 ? void 0 : _rootRef$current.querySelector(`[data-criteria-add="${groupId.replace(/["\\]/g, "\\$&")}"]`)) !== null && _rootRef$current$quer !== void 0 ? _rootRef$current$quer : null;
4117
+ }, []);
4118
+ react.useEffect(() => {
4119
+ if (!pendingOpen) return;
4120
+ const trigger = findAddTrigger(pendingOpen);
4121
+ if (!trigger) return;
4122
+ setPendingOpen(null);
4123
+ trigger.focus();
4124
+ trigger.click();
4125
+ }, [
4126
+ findAddTrigger,
4127
+ pendingOpen,
4128
+ value
4129
+ ]);
4130
+ const handleKeyDown = react.useCallback((event) => {
4131
+ if (disabled || event.defaultPrevented) return;
4132
+ if (event.altKey || event.ctrlKey || event.metaKey) return;
4133
+ if (isEditableTarget(event.target)) return;
4134
+ const key = event.key.toLowerCase();
4135
+ if (key === "g") {
4136
+ const id = addGroup();
4137
+ if (id) {
4138
+ event.preventDefault();
4139
+ setPendingOpen(id);
4140
+ }
4141
+ return;
4142
+ }
4143
+ if (key === "a") {
4144
+ const host = event.target instanceof Element ? event.target.closest("[data-criteria-group]") : null;
4145
+ const groupId = host === null || host === void 0 ? void 0 : host.dataset.criteriaGroup;
4146
+ const trigger = groupId ? findAddTrigger(groupId) : null;
4147
+ if (trigger && !trigger.disabled) {
4148
+ event.preventDefault();
4149
+ trigger.focus();
4150
+ trigger.click();
4151
+ }
4152
+ }
4153
+ }, [
4154
+ addGroup,
4155
+ disabled,
4156
+ findAddTrigger
4157
+ ]);
4158
+ const rootLogicOptions = react.useMemo(() => [{
4159
+ value: "and",
4160
+ label: labels.matchAllGroups
4161
+ }, {
4162
+ value: "or",
4163
+ label: labels.matchAnyGroup
4164
+ }], [labels]);
4165
+ const groupLogicOptions = react.useMemo(() => [{
4166
+ value: "and",
4167
+ label: labels.matchAllCriteria
4168
+ }, {
4169
+ value: "or",
4170
+ label: labels.matchAnyCriteria
4171
+ }], [labels]);
4172
+ const isEmpty = value.groups.length === 0;
4173
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4174
+ ref: rootRef,
4175
+ role: "group",
4176
+ "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : labels.builder,
4177
+ className: require_text_truncate.cn("gap-section flex flex-col lg:flex-row", className),
4178
+ onKeyDown: handleKeyDown,
4179
+ "data-opt-id": "6F4JG",
4180
+ "data-opt-slug": "criteria-builder.CriteriaBuilder",
4181
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4182
+ className: "gap-group flex min-w-0 flex-1 flex-col",
4183
+ children: isEmpty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.EmptyState, {
4184
+ title: labels.emptyTitle,
4185
+ description: labels.emptyDescription,
4186
+ hasBorder: true,
4187
+ tone: "subdued",
4188
+ actions: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KindMenu, {
4189
+ kinds,
4190
+ menuLabel: labels.kindMenuTitle,
4191
+ triggerLabel: labels.emptyAction,
4192
+ triggerAriaLabel: labels.emptyAction,
4193
+ triggerIcon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Plus, { "aria-hidden": "true" }),
4194
+ variant: "secondary",
4195
+ disabled,
4196
+ onPick: addFirst
4197
+ }),
4198
+ footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4199
+ className: "gap-element inline-flex items-center",
4200
+ children: [
4201
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Kbd, {
4202
+ hint: true,
4203
+ children: "A"
4204
+ }),
4205
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Kbd, {
4206
+ hint: true,
4207
+ children: "G"
4208
+ }),
4209
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: labels.keyboardHint })
4210
+ ]
4211
+ })
4212
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
4213
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
4214
+ className: "gap-group m-0 flex min-w-0 flex-wrap items-center border-0 p-0",
4215
+ children: [
4216
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("legend", {
4217
+ className: "sr-only",
4218
+ children: labels.rootLogicLegend
4219
+ }),
4220
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_field_sidebar.SegmentedControl, {
4221
+ "aria-label": labels.rootLogicLegend,
4222
+ size: "sm",
4223
+ options: rootLogicOptions,
4224
+ value: value.logic,
4225
+ onChange: (logic) => onChange(require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, value), {}, { logic })),
4226
+ disabled
4227
+ }),
4228
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4229
+ className: "text-text-tertiary gap-element hidden items-center text-xs sm:inline-flex",
4230
+ children: [
4231
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Kbd, {
4232
+ hint: true,
4233
+ children: "A"
4234
+ }),
4235
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Kbd, {
4236
+ hint: true,
4237
+ children: "G"
4238
+ }),
4239
+ labels.keyboardHint
4240
+ ]
4241
+ })
4242
+ ]
4243
+ }),
4244
+ value.groups.map((group, groupIndex) => {
4245
+ const letter = criteriaGroupLetter(groupIndex);
4246
+ const groupName = labels.groupLabel(letter);
4247
+ const groupLabelId = `${baseId}-${group.id}-label`;
4248
+ const groupError = errors === null || errors === void 0 ? void 0 : errors[group.id];
4249
+ const groupErrorId = `${baseId}-${group.id}-error`;
4250
+ const criteriaFull = group.criteria.length >= maxCriteriaPerGroup;
4251
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [groupIndex > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LogicDivider, {
4252
+ emphasis: "root",
4253
+ label: value.logic === "and" ? labels.and : labels.or
4254
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4255
+ role: "group",
4256
+ "aria-labelledby": groupLabelId,
4257
+ "aria-describedby": groupError ? groupErrorId : void 0,
4258
+ "data-criteria-group": group.id,
4259
+ "data-invalid": groupError ? true : void 0,
4260
+ className: require_text_truncate.cn("bg-bg-subtle gap-element flex flex-col rounded-lg border p-3", groupError ? "border-danger/40" : "border-border"),
4261
+ children: [
4262
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4263
+ id: groupLabelId,
4264
+ className: "sr-only",
4265
+ children: groupName
4266
+ }),
4267
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4268
+ className: "gap-element flex flex-wrap items-center justify-between",
4269
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4270
+ className: "gap-element flex items-center",
4271
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Lettermark, { letter }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
4272
+ className: "m-0 border-0 p-0",
4273
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("legend", {
4274
+ className: "sr-only",
4275
+ children: labels.groupLogicLegend(letter)
4276
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_field_sidebar.SegmentedControl, {
4277
+ "aria-label": labels.groupLogicLegend(letter),
4278
+ size: "sm",
4279
+ options: groupLogicOptions,
4280
+ value: group.logic,
4281
+ onChange: (logic) => onChange(replaceGroup(value, groupIndex, require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, group), {}, { logic }))),
4282
+ disabled
4283
+ })]
4284
+ })]
4285
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4286
+ className: "flex items-center gap-0.5",
4287
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAction, {
4288
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Copy, { "aria-hidden": "true" }),
4289
+ label: labels.actionLabel(labels.duplicateGroup, groupName),
4290
+ disabled: disabled || groupsFull,
4291
+ onClick: () => {
4292
+ const taken = collectIds(value);
4293
+ const id = newGroupId();
4294
+ taken.add(id);
4295
+ onChange(criteriaBuilderDuplicateGroup(value, groupIndex, id, (c) => clone(c, taken)));
4296
+ }
4297
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAction, {
4298
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Trash2, { "aria-hidden": "true" }),
4299
+ label: labels.actionLabel(labels.removeGroup, groupName),
4300
+ disabled,
4301
+ danger: true,
4302
+ onClick: () => onChange(criteriaBuilderRemoveGroup(value, groupIndex))
4303
+ })]
4304
+ })]
4305
+ }),
4306
+ group.criteria.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4307
+ className: "text-text-tertiary text-xs",
4308
+ children: labels.emptyGroup
4309
+ }),
4310
+ group.criteria.map((criterion, index) => {
4311
+ const criterionName = labels.criterionLabel(letter, index + 1);
4312
+ const rowLabelId = `${baseId}-${criterion.id}-label`;
4313
+ const error = errors === null || errors === void 0 ? void 0 : errors[criterion.id];
4314
+ const errorId = `${baseId}-${criterion.id}-error`;
4315
+ const known = knownKinds.has(criterion.kind);
4316
+ const negated = criterion.negate === true;
4317
+ const api = {
4318
+ update: (patch) => updateCriterion(groupIndex, index, patch),
4319
+ invalid: Boolean(error) || !known,
4320
+ error,
4321
+ disabled,
4322
+ groupIndex,
4323
+ index
4324
+ };
4325
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [index > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LogicDivider, {
4326
+ emphasis: "group",
4327
+ label: group.logic === "and" ? labels.and : labels.or
4328
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4329
+ role: "group",
4330
+ "aria-labelledby": rowLabelId,
4331
+ "aria-describedby": error ? errorId : void 0,
4332
+ "data-criterion-kind": criterion.kind,
4333
+ "data-negated": negated || void 0,
4334
+ "data-invalid": error || !known ? true : void 0,
4335
+ className: require_text_truncate.cn("bg-surface gap-element flex flex-col rounded-md border p-2", error || !known ? "border-danger/40" : "border-border-subtle"),
4336
+ children: [
4337
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4338
+ id: rowLabelId,
4339
+ className: "sr-only",
4340
+ children: criterionName
4341
+ }),
4342
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4343
+ className: "gap-element flex flex-wrap items-start",
4344
+ children: [
4345
+ allowNegation && known && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4346
+ type: "button",
4347
+ "aria-pressed": negated,
4348
+ "aria-label": labels.actionLabel(labels.negate, criterionName),
4349
+ disabled,
4350
+ onClick: () => updateCriterion(groupIndex, index, { negate: !negated }),
4351
+ className: require_text_truncate.cn("inline-flex h-7 shrink-0 items-center rounded-md border px-2 text-[11px] font-semibold tracking-wide uppercase transition-colors", negated ? "border-danger/40 bg-danger-subtle text-danger-fg" : "border-border bg-bg-subtle text-text-tertiary hover:text-text-secondary", "disabled:pointer-events-none disabled:opacity-50", "outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-ring-offset"),
4352
+ children: labels.negate
4353
+ }),
4354
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4355
+ className: "min-w-0 flex-1",
4356
+ children: known ? renderCriterion(criterion, api) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_field_sidebar.Callout, {
4357
+ tone: "warning",
4358
+ size: "sm",
4359
+ title: labels.unknownKind(criterion.kind),
4360
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.TriangleAlert, { "aria-hidden": "true" }),
4361
+ action: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KindMenu, {
4362
+ kinds,
4363
+ menuLabel: labels.kindMenuTitle,
4364
+ triggerLabel: labels.replaceKind,
4365
+ triggerAriaLabel: labels.actionLabel(labels.replaceKind, criterionName),
4366
+ variant: "secondary",
4367
+ disabled,
4368
+ onPick: (kind, negate) => replaceCriterion(groupIndex, index, kind, negate)
4369
+ }),
4370
+ children: labels.unknownKindDescription
4371
+ })
4372
+ }),
4373
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4374
+ className: "flex items-center gap-0.5",
4375
+ children: [
4376
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAction, {
4377
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ArrowUp, { "aria-hidden": "true" }),
4378
+ label: labels.actionLabel(labels.moveUp, criterionName),
4379
+ disabled: disabled || index === 0,
4380
+ onClick: () => onChange(criteriaBuilderMove(value, groupIndex, index, "up"))
4381
+ }),
4382
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAction, {
4383
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ArrowDown, { "aria-hidden": "true" }),
4384
+ label: labels.actionLabel(labels.moveDown, criterionName),
4385
+ disabled: disabled || index === group.criteria.length - 1,
4386
+ onClick: () => onChange(criteriaBuilderMove(value, groupIndex, index, "down"))
4387
+ }),
4388
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAction, {
4389
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Copy, { "aria-hidden": "true" }),
4390
+ label: labels.actionLabel(labels.duplicateCriterion, criterionName),
4391
+ disabled: disabled || criteriaFull,
4392
+ onClick: () => onChange(criteriaBuilderDuplicate(value, groupIndex, index, (c) => clone(c, collectIds(value))))
4393
+ }),
4394
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAction, {
4395
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Trash2, { "aria-hidden": "true" }),
4396
+ label: labels.actionLabel(labels.removeCriterion, criterionName),
4397
+ disabled,
4398
+ danger: true,
4399
+ onClick: () => onChange(criteriaBuilderRemove(value, groupIndex, index))
4400
+ })
4401
+ ]
4402
+ })
4403
+ ]
4404
+ }),
4405
+ error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4406
+ id: errorId,
4407
+ className: "text-danger-fg text-xs",
4408
+ children: error
4409
+ })
4410
+ ]
4411
+ })] }, criterion.id);
4412
+ }),
4413
+ groupError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4414
+ id: groupErrorId,
4415
+ className: "text-danger-fg text-xs",
4416
+ children: groupError
4417
+ }),
4418
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4419
+ className: "gap-element flex flex-wrap items-center",
4420
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(KindMenu, {
4421
+ kinds,
4422
+ menuLabel: labels.kindMenuTitle,
4423
+ triggerLabel: labels.addCriterion,
4424
+ triggerAriaLabel: labels.actionLabel(labels.addCriterion, groupName),
4425
+ triggerIcon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Plus, { "aria-hidden": "true" }),
4426
+ disabled: disabled || criteriaFull,
4427
+ disabledReason: labels.maxCriteriaReached(maxCriteriaPerGroup),
4428
+ onPick: (kind, negate) => addCriterion(groupIndex, kind, negate),
4429
+ addTarget: group.id
4430
+ }), criteriaFull && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4431
+ className: "text-text-tertiary text-xs",
4432
+ children: labels.maxCriteriaReached(maxCriteriaPerGroup)
4433
+ })]
4434
+ })
4435
+ ]
4436
+ })] }, group.id);
4437
+ }),
4438
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4439
+ className: "gap-element flex flex-wrap items-center",
4440
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Button, {
4441
+ variant: "secondary",
4442
+ size: "sm",
4443
+ leftIcon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Plus, { "aria-hidden": "true" }),
4444
+ disabled: disabled || groupsFull,
4445
+ title: groupsFull ? labels.maxGroupsReached(maxGroups) : void 0,
4446
+ onClick: () => {
4447
+ const id = addGroup();
4448
+ if (id) setPendingOpen(id);
4449
+ },
4450
+ children: labels.addGroup
4451
+ }), groupsFull && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4452
+ className: "text-text-tertiary text-xs",
4453
+ children: labels.maxGroupsReached(maxGroups)
4454
+ })]
4455
+ })
4456
+ ] })
4457
+ }), aside !== void 0 && aside !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("aside", {
4458
+ className: "min-w-0 lg:w-72 lg:shrink-0",
4459
+ children: aside
4460
+ })]
4461
+ });
4462
+ }
4463
+ CriteriaBuilder.displayName = "CriteriaBuilder";
4464
+ //#endregion
4465
+ //#region src/shells/insights-panel.tsx
4466
+ const defaultLabels$16 = {
4467
+ title: "Insights",
4468
+ dismiss: "Dismiss",
4469
+ showDismissed: "Show dismissed",
4470
+ hideDismissed: "Hide dismissed",
4471
+ emptyTitle: "No insights",
4472
+ emptyDescription: "No anomalies or notable events detected.",
4473
+ detectionTitle: "Detection Settings",
4474
+ daysLabel: "Days",
4475
+ zScoreLabel: "Z-Score"
4476
+ };
4477
+ const severityConfig = {
4478
+ critical: {
4479
+ icon: "!!",
4480
+ color: "text-danger-fg",
4481
+ bg: "bg-danger-subtle border-danger"
4482
+ },
4483
+ warning: {
4484
+ icon: "!",
4485
+ color: "text-warning-fg",
4486
+ bg: "bg-warning-subtle border-warning"
4487
+ },
4488
+ info: {
4489
+ icon: "i",
4490
+ color: "text-info-fg",
4491
+ bg: "bg-info-subtle border-info"
4492
+ }
4493
+ };
4494
+ /** Renders the insights panel component. */
4495
+ function InsightsPanel({ insights, stats, detectionTypes, onDetectionTypeToggle, detectionConfig, onDetectionConfigChange, onDismiss, onInsightClick, onStatClick, header, actions, labels: customLabels, className }) {
4496
+ var _detectionConfig$days, _detectionConfig$zSco;
4497
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$16), customLabels);
4498
+ const [showDismissed, setShowDismissed] = react.useState(false);
4499
+ const visibleInsights = showDismissed ? insights : insights.filter((i) => !i.dismissed);
4500
+ const hasStats = stats && stats.length > 0;
4501
+ const hasDetection = detectionTypes && detectionTypes.length > 0 || detectionConfig != null;
4502
+ const isEmpty = visibleInsights.length === 0;
4503
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4504
+ className: require_text_truncate.cn("gap-group flex flex-col", className),
4505
+ "data-opt-id": "HCYZV",
4506
+ "data-opt-slug": "insights-panel.InsightsPanel",
4507
+ children: [
4508
+ header || actions ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4509
+ className: "flex items-center justify-between",
4510
+ children: [header && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: header }), actions && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4511
+ className: "gap-element flex items-center",
4512
+ children: actions
4513
+ })]
4514
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4515
+ className: "flex items-center justify-between",
4057
4516
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
4058
4517
  className: "text-text-primary text-lg font-semibold",
4059
4518
  children: labels.title
@@ -4408,17 +4867,17 @@ function ExportButton({ data, columns, fileName = "export", formats = ["csv", "j
4408
4867
  })
4409
4868
  })]
4410
4869
  });
4411
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_key_pad_menu.DropdownMenu, {
4870
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_text_truncate.DropdownMenu, {
4412
4871
  "data-opt-id": "MGP2B",
4413
4872
  "data-opt-slug": "export-button.ExportButton",
4414
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_key_pad_menu.DropdownTrigger, {
4873
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_text_truncate.DropdownTrigger, {
4415
4874
  className: require_text_truncate.cn("inline-flex items-center justify-center rounded-lg font-medium transition-colors", "border-border bg-surface text-text-primary hover:bg-bg-subtle active:bg-bg-muted border", "h-8 gap-1.5 px-3 text-xs", className),
4416
4875
  "aria-label": labels.export,
4417
4876
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4418
4877
  "aria-hidden": "true",
4419
4878
  children: "⬇"
4420
4879
  }), labels.export]
4421
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_key_pad_menu.DropdownContent, { children: formats.map((fmt, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [i > 0 && fmt === "pdf" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_key_pad_menu.DropdownSeparator, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_key_pad_menu.DropdownItem, {
4880
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.DropdownContent, { children: formats.map((fmt, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [i > 0 && fmt === "pdf" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.DropdownSeparator, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.DropdownItem, {
4422
4881
  onClick: () => handleExport(fmt),
4423
4882
  children: formatLabels[fmt]
4424
4883
  })] }, fmt)) })]
@@ -4565,28 +5024,6 @@ function matchesQueryObject(query, row, options = {}) {
4565
5024
  return matchesQuery(query, read, searchable);
4566
5025
  }
4567
5026
  //#endregion
4568
- //#region src/lib/use-media-query.ts
4569
- /**
4570
- * Whether a media query matches, kept in sync with the browser.
4571
- *
4572
- * `useSyncExternalStore` rather than an effect: a layout that switches on width
4573
- * must not render the wrong shape for a frame first. The server snapshot is the
4574
- * caller's `serverFallback`, because the server does not know the viewport and
4575
- * guessing "narrow" collapses a wide layout on every first paint.
4576
- *
4577
- * Internal. Exported from `lib` for the components in this package, not from
4578
- * the package barrel — a hook that answers "is this viewport wide" invites
4579
- * layout decisions that belong in CSS.
4580
- */
4581
- function useMediaQuery(query, serverFallback = false) {
4582
- return (0, react.useSyncExternalStore)((onChange) => {
4583
- if (typeof window === "undefined") return () => {};
4584
- const list = window.matchMedia(query);
4585
- list.addEventListener("change", onChange);
4586
- return () => list.removeEventListener("change", onChange);
4587
- }, () => typeof window === "undefined" ? serverFallback : window.matchMedia(query).matches, () => serverFallback);
4588
- }
4589
- //#endregion
4590
5027
  //#region src/shells/inspector-layout.tsx
4591
5028
  /** Tailwind needs whole class names, so the queries and widths are literals. */
4592
5029
  const breakpointQuery = {
@@ -4635,7 +5072,7 @@ const flyoutSize = {
4635
5072
  * ```
4636
5073
  */
4637
5074
  function InspectorLayout({ children, inspector, open, onClose, title, description, actions, footer, breakpoint = "xl", width = "md", labels, className }) {
4638
- const canPush = useMediaQuery(breakpointQuery[breakpoint], true);
5075
+ const canPush = require_field_sidebar.useMediaQuery(breakpointQuery[breakpoint], true);
4639
5076
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4640
5077
  className: require_text_truncate.cn("flex min-w-0 items-stretch gap-group", className),
4641
5078
  "data-opt-id": "MN44T",
@@ -4859,13 +5296,13 @@ function CatalogRowActions({ item, actions, visibleCount = 2, labels }) {
4859
5296
  size: "sm",
4860
5297
  onClick: () => action.onSelect(item),
4861
5298
  children: [action.icon, action.label]
4862
- }, action.id)), overflow.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_key_pad_menu.DropdownMenu, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_key_pad_menu.DropdownTrigger, {
5299
+ }, action.id)), overflow.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_text_truncate.DropdownMenu, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.DropdownTrigger, {
4863
5300
  "aria-label": (_labels$moreActions = labels === null || labels === void 0 ? void 0 : labels.moreActions) !== null && _labels$moreActions !== void 0 ? _labels$moreActions : "더 보기",
4864
5301
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.MoreHorizontal, {
4865
5302
  "aria-hidden": "true",
4866
5303
  className: "size-4"
4867
5304
  })
4868
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_key_pad_menu.DropdownContent, { children: overflow.map((action) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_key_pad_menu.DropdownItem, {
5305
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.DropdownContent, { children: overflow.map((action) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.DropdownItem, {
4869
5306
  onClick: () => action.onSelect(item),
4870
5307
  children: action.label
4871
5308
  }, action.id)) })] })]
@@ -5122,853 +5559,6 @@ function ComparisonSelector({ segments, valueA, valueB, onChangeA, onChangeB, va
5122
5559
  });
5123
5560
  }
5124
5561
  ComparisonSelector.displayName = "ComparisonSelector";
5125
- const THEME_REGISTRY = {
5126
- default: {
5127
- id: "default",
5128
- label: "Default",
5129
- description: "Blue accent, balanced radius & shadow",
5130
- palette: {
5131
- seed: "#2563eb",
5132
- harmony: "split-complementary",
5133
- radiusScale: "medium",
5134
- shadowIntensity: "subtle",
5135
- neutralTemperature: "cool"
5136
- },
5137
- switcherSwatches: [
5138
- "hsl(221 83% 53%)",
5139
- "hsl(142 71% 45%)",
5140
- "hsl(38 92% 50%)"
5141
- ]
5142
- },
5143
- minimal: {
5144
- id: "minimal",
5145
- label: "Minimal",
5146
- description: "Monochrome, no shadow, sharp edges",
5147
- palette: {
5148
- seed: "#333333",
5149
- harmony: "analogous",
5150
- radiusScale: "sharp",
5151
- shadowIntensity: "none",
5152
- neutralTemperature: "cool"
5153
- },
5154
- switcherSwatches: [
5155
- "hsl(0 0% 20%)",
5156
- "hsl(0 0% 45%)",
5157
- "hsl(0 0% 70%)"
5158
- ]
5159
- },
5160
- natural: {
5161
- id: "natural",
5162
- label: "Natural",
5163
- description: "Always light — sage accent, warm cream",
5164
- forceLightOnly: true,
5165
- palette: {
5166
- seed: "#5a9376",
5167
- harmony: "complementary",
5168
- radiusScale: "medium",
5169
- shadowIntensity: "subtle",
5170
- neutralTemperature: "warm"
5171
- },
5172
- switcherSwatches: [
5173
- "hsl(155 38% 45%)",
5174
- "hsl(25 65% 55%)",
5175
- "hsl(45 75% 50%)"
5176
- ]
5177
- },
5178
- pro: {
5179
- id: "pro",
5180
- label: "Pro",
5181
- description: "Neon blue accent, mono headings, ultra-dense",
5182
- palette: {
5183
- seed: "#0080ff",
5184
- harmony: "tetradic",
5185
- radiusScale: "sharp",
5186
- shadowIntensity: "subtle",
5187
- neutralTemperature: "cool"
5188
- },
5189
- switcherSwatches: [
5190
- "hsl(210 100% 55%)",
5191
- "hsl(170 90% 48%)",
5192
- "hsl(290 85% 60%)"
5193
- ]
5194
- },
5195
- "mono-dark": {
5196
- id: "mono-dark",
5197
- label: "Mono Dark",
5198
- description: "OLED-friendly black, neon cyan accent, mono headings",
5199
- palette: {
5200
- seed: "#00d4ff",
5201
- harmony: "tetradic",
5202
- radiusScale: "sharp",
5203
- shadowIntensity: "subtle",
5204
- neutralTemperature: "cool"
5205
- },
5206
- switcherSwatches: [
5207
- "hsl(190 100% 50%)",
5208
- "hsl(0 0% 8%)",
5209
- "hsl(280 90% 65%)"
5210
- ]
5211
- }
5212
- };
5213
- /** All registered preset ids in declaration order. */
5214
- const THEME_PRESET_IDS = Object.keys(THEME_REGISTRY);
5215
- /** Look up a descriptor by id. Returns `undefined` for unknown ids. */
5216
- function getThemeDescriptor(id) {
5217
- return THEME_REGISTRY[id];
5218
- }
5219
- /** Type guard for a known preset id. */
5220
- function isThemePresetId(id) {
5221
- return id in THEME_REGISTRY;
5222
- }
5223
- /** Preset ids that suppress dark mode generation. */
5224
- const FORCE_LIGHT_PRESET_IDS = new Set(THEME_PRESET_IDS.filter((id) => THEME_REGISTRY[id].forceLightOnly));
5225
- /**
5226
- * Map from removed preset ids to the closest surviving preset. Boot script and
5227
- * `readStoredTheme` consult this so users with persisted `corporate`/`playful`
5228
- * values silently land on a working preset on next page load instead of
5229
- * showing the global default fallback for an unknown id.
5230
- *
5231
- * Keep this around indefinitely — localStorage values are forever and the
5232
- * cost of two extra entries is negligible.
5233
- */
5234
- const DEPRECATED_PRESET_MIGRATIONS = Object.freeze({
5235
- corporate: "default",
5236
- playful: "default"
5237
- });
5238
- /**
5239
- * Resolve a possibly-deprecated preset id to a registered one. Returns the
5240
- * input unchanged when it is already valid; returns the migration target when
5241
- * it is a known deprecated id; returns `null` when the id is unknown
5242
- * altogether (caller should fall back to its default).
5243
- */
5244
- function migrateDeprecatedPresetId(id) {
5245
- if (!id) return null;
5246
- if (isThemePresetId(id)) return id;
5247
- if (id in DEPRECATED_PRESET_MIGRATIONS) return DEPRECATED_PRESET_MIGRATIONS[id];
5248
- return null;
5249
- }
5250
- //#endregion
5251
- //#region src/lib/theme/theme-types.ts
5252
- /**
5253
- * Author canonical theme overrides with token-name autocomplete.
5254
- * ThemeOverrides remains broad for backward compatibility with older custom
5255
- * variables; new themes should use this helper as the governed entry point.
5256
- */
5257
- function defineThemeOverrides(overrides) {
5258
- return overrides;
5259
- }
5260
- /** Construct a `PresetThemeSpec` — convenience for the common case. */
5261
- function presetTheme(preset, overrides) {
5262
- return overrides ? {
5263
- kind: "preset",
5264
- preset,
5265
- overrides
5266
- } : {
5267
- kind: "preset",
5268
- preset
5269
- };
5270
- }
5271
- /** Construct a `GeneratedThemeSpec` from an inline `PaletteConfig`. */
5272
- function generatedTheme(palette, options) {
5273
- return require_text_truncate._objectSpread2({
5274
- kind: "generated",
5275
- palette
5276
- }, options);
5277
- }
5278
- /** True when `spec` references a registered preset id. */
5279
- function isPresetSpec(spec) {
5280
- return spec.kind === "preset";
5281
- }
5282
- /** True when `spec` carries an inline `PaletteConfig`. */
5283
- function isGeneratedSpec(spec) {
5284
- return spec.kind === "generated";
5285
- }
5286
- /** True when `spec` wraps a fully-formed `ThemeDescriptor`. */
5287
- function isDescriptorSpec(spec) {
5288
- return spec.kind === "descriptor";
5289
- }
5290
- //#endregion
5291
- //#region src/lib/theme/theme-serialize.ts
5292
- /**
5293
- * theme-serialize.ts
5294
- *
5295
- * Pure helpers that turn a `ThemeSpec` into the artifacts the runtime needs:
5296
- * resolved descriptor + mode, computed token maps for generated palettes, and
5297
- * `<style>` CSS strings ready to be injected into `<head>`.
5298
- *
5299
- * Kept side-effect free so the boot script and the provider can share the
5300
- * same logic without dragging React into the client bundle's critical path.
5301
- */
5302
- /** Element id used for the global generated theme style tag. */
5303
- const GLOBAL_THEME_STYLE_ID = "opt-global-theme";
5304
- /** localStorage key for the persisted ThemeSpec (generated mode). */
5305
- const THEME_SPEC_STORAGE_KEY = "opt-theme-spec";
5306
- /**
5307
- * localStorage key for the cached, pre-serialized token maps that match the
5308
- * spec under `THEME_SPEC_STORAGE_KEY`. The boot script reads this directly so
5309
- * it can inline tokens without invoking opt-palette at startup.
5310
- */
5311
- const THEME_TOKENS_STORAGE_KEY = "opt-theme-tokens";
5312
- /** Stable id used for unnamed generated themes. */
5313
- const GENERATED_THEME_ID_DEFAULT = "generated";
5314
- /** Cached token maps keyed by spec hash. Lazy-built per process. */
5315
- const generatedCache = /* @__PURE__ */ new Map();
5316
- /** Build a stable cache key for a `PaletteConfig`. */
5317
- function paletteCacheKey(config) {
5318
- return JSON.stringify(config);
5319
- }
5320
- /** Run opt-palette with caching. */
5321
- function generatePaletteCached(config) {
5322
- const key = paletteCacheKey(config);
5323
- let cached = generatedCache.get(key);
5324
- if (!cached) {
5325
- cached = (0, _reopt_ai_opt_palette.generatePalette)(config);
5326
- generatedCache.set(key, cached);
5327
- }
5328
- return cached;
5329
- }
5330
- /** Apply sparse override maps onto a base token record. */
5331
- function applyOverrides(base, overrides, mode) {
5332
- if (!overrides) return base;
5333
- const merged = require_text_truncate._objectSpread2({}, base);
5334
- if (overrides.shared) Object.assign(merged, overrides.shared);
5335
- const modeOverrides = mode === "light" ? overrides.light : overrides.dark;
5336
- if (modeOverrides) Object.assign(merged, modeOverrides);
5337
- return merged;
5338
- }
5339
- /**
5340
- * Stable id used in `<html data-theme>` and the `<style>` selector.
5341
- * - preset → the preset id (force-light) or `${id}` / `${id}-dark`
5342
- * - generated → `spec.id ?? "generated"` with `-dark` suffix when applicable
5343
- * - descriptor → `descriptor.id` with same suffix logic
5344
- */
5345
- function getCompoundIdForSpec(spec, resolvedMode, forceLightOnly) {
5346
- const baseId = baseIdFor(spec);
5347
- if (forceLightOnly) return baseId;
5348
- return resolvedMode === "dark" ? `${baseId}-dark` : baseId;
5349
- }
5350
- function baseIdFor(spec) {
5351
- var _spec$id;
5352
- if (isPresetSpec(spec)) return spec.preset;
5353
- if (isDescriptorSpec(spec)) return spec.descriptor.id;
5354
- return (_spec$id = spec.id) !== null && _spec$id !== void 0 ? _spec$id : GENERATED_THEME_ID_DEFAULT;
5355
- }
5356
- /** Whether the given spec emits dark variants at all. */
5357
- function isForceLightSpec(spec) {
5358
- if (isPresetSpec(spec)) {
5359
- const desc = THEME_REGISTRY[spec.preset];
5360
- return (desc === null || desc === void 0 ? void 0 : desc.forceLightOnly) === true;
5361
- }
5362
- if (isDescriptorSpec(spec)) return spec.descriptor.forceLightOnly === true;
5363
- return spec.forceLightOnly === true;
5364
- }
5365
- /**
5366
- * Compute the inline tokens for a `GeneratedThemeSpec` or
5367
- * `DescriptorThemeSpec` whose descriptor carries a `palette`. Returns `null`
5368
- * for preset specs (their tokens live in the static CSS files).
5369
- */
5370
- function computeGeneratedTokens(spec) {
5371
- if (isPresetSpec(spec)) return null;
5372
- let palette;
5373
- let overrides;
5374
- let id;
5375
- let forceLight;
5376
- if (isGeneratedSpec(spec)) {
5377
- var _spec$id2;
5378
- palette = spec.palette;
5379
- overrides = spec.overrides;
5380
- id = (_spec$id2 = spec.id) !== null && _spec$id2 !== void 0 ? _spec$id2 : GENERATED_THEME_ID_DEFAULT;
5381
- forceLight = spec.forceLightOnly === true;
5382
- } else {
5383
- palette = spec.descriptor.palette;
5384
- overrides = spec.overrides;
5385
- id = spec.descriptor.id;
5386
- forceLight = spec.descriptor.forceLightOnly === true;
5387
- }
5388
- if (!palette) return null;
5389
- const generated = generatePaletteCached(palette);
5390
- return {
5391
- id,
5392
- light: applyOverrides(generated.light, overrides, "light"),
5393
- dark: forceLight ? null : applyOverrides(generated.dark, overrides, "dark")
5394
- };
5395
- }
5396
- /**
5397
- * Serialize a token record into a CSS rule body — `--name:value;...` — without
5398
- * surrounding selector or braces. Values are not escaped; callers are
5399
- * responsible for trusted input. Boot script and provider both rely on this
5400
- * shape.
5401
- */
5402
- function serializeTokenBody(tokens) {
5403
- return Object.entries(tokens).map(([k, v]) => `${k}:${v}`).join(";");
5404
- }
5405
- /**
5406
- * Build a CSS rule scoped to `[data-theme="<id>"]`.
5407
- */
5408
- function serializeThemeRule(themeId, tokens) {
5409
- return `[data-theme="${themeId}"]{${serializeTokenBody(tokens)}}`;
5410
- }
5411
- /**
5412
- * Build the full CSS for a generated theme style tag — covers light + dark
5413
- * selectors based on the computed tokens. Caller chooses the id used in
5414
- * the selector (typically the spec base id).
5415
- */
5416
- function serializeGeneratedTheme(tokens) {
5417
- const { id, light, dark } = tokens;
5418
- let css = serializeThemeRule(id, light);
5419
- if (dark) css += serializeThemeRule(`${id}-dark`, dark);
5420
- return css;
5421
- }
5422
- /**
5423
- * Resolve a spec into its descriptor counterpart. Preset specs map to
5424
- * `THEME_REGISTRY[preset]`. Generated specs are wrapped into a synthetic
5425
- * descriptor so callers can treat them uniformly.
5426
- */
5427
- function resolveDescriptor(spec) {
5428
- if (isPresetSpec(spec)) {
5429
- const desc = THEME_REGISTRY[spec.preset];
5430
- if (!desc) throw new Error(`[opt-ui] Unknown theme preset id: "${spec.preset}". Registered ids: ${Object.keys(THEME_REGISTRY).join(", ")}`);
5431
- return desc;
5432
- }
5433
- if (isDescriptorSpec(spec)) return spec.descriptor;
5434
- return generatedSpecToDescriptor(spec);
5435
- }
5436
- function generatedSpecToDescriptor(spec) {
5437
- var _spec$id3, _spec$label;
5438
- return {
5439
- id: (_spec$id3 = spec.id) !== null && _spec$id3 !== void 0 ? _spec$id3 : GENERATED_THEME_ID_DEFAULT,
5440
- label: (_spec$label = spec.label) !== null && _spec$label !== void 0 ? _spec$label : "Custom",
5441
- description: "Generated theme",
5442
- palette: spec.palette,
5443
- forceLightOnly: spec.forceLightOnly
5444
- };
5445
- }
5446
- /**
5447
- * Validate a `ThemeSpec` parsed from localStorage. Returns the spec on
5448
- * success or `null` if any required field is missing or the preset id is
5449
- * unknown. Defensive parsing — never throws.
5450
- */
5451
- function safeParseStoredSpec(json) {
5452
- if (!json) return null;
5453
- try {
5454
- const parsed = JSON.parse(json);
5455
- if (!parsed || typeof parsed !== "object" || !("kind" in parsed)) return null;
5456
- if (parsed.kind === "preset") {
5457
- const preset = parsed.preset;
5458
- if (typeof preset !== "string" || !isThemePresetId(preset)) return null;
5459
- return {
5460
- kind: "preset",
5461
- preset
5462
- };
5463
- }
5464
- if (parsed.kind === "generated") {
5465
- const palette = parsed.palette;
5466
- if (!isPaletteConfig(palette)) return null;
5467
- return {
5468
- kind: "generated",
5469
- palette,
5470
- id: typeof parsed.id === "string" ? parsed.id : void 0,
5471
- label: typeof parsed.label === "string" ? parsed.label : void 0,
5472
- forceLightOnly: parsed.forceLightOnly === true,
5473
- overrides: isObjectMap(parsed.overrides) ? parsed.overrides : void 0
5474
- };
5475
- }
5476
- return null;
5477
- } catch (_unused) {
5478
- return null;
5479
- }
5480
- }
5481
- function isObjectMap(value) {
5482
- return value !== null && typeof value === "object" && !Array.isArray(value);
5483
- }
5484
- function isPaletteConfig(value) {
5485
- if (!isObjectMap(value)) return false;
5486
- const v = value;
5487
- return typeof v.seed === "string" && typeof v.harmony === "string" && typeof v.radiusScale === "string" && typeof v.shadowIntensity === "string" && typeof v.neutralTemperature === "string";
5488
- }
5489
- /**
5490
- * Parse the cached token payload from localStorage. Returns `null` on any
5491
- * shape mismatch — caller will recompute from the spec.
5492
- */
5493
- function safeParseStoredTokens(json) {
5494
- if (!json) return null;
5495
- try {
5496
- const parsed = JSON.parse(json);
5497
- if (!parsed || typeof parsed.id !== "string" || !isObjectMap(parsed.light)) return null;
5498
- return {
5499
- id: parsed.id,
5500
- light: parsed.light,
5501
- dark: isObjectMap(parsed.dark) ? parsed.dark : void 0
5502
- };
5503
- } catch (_unused2) {
5504
- return null;
5505
- }
5506
- }
5507
- /** Build the storage payload that pairs with `opt-theme-spec`. */
5508
- function buildTokenCache(tokens) {
5509
- var _tokens$dark;
5510
- return {
5511
- id: tokens.id,
5512
- light: tokens.light,
5513
- dark: (_tokens$dark = tokens.dark) !== null && _tokens$dark !== void 0 ? _tokens$dark : void 0
5514
- };
5515
- }
5516
- //#endregion
5517
- //#region src/lib/theme/theme-resolution.ts
5518
- /**
5519
- * Registered preset id list. Preferred for runtime iteration.
5520
- * @deprecated Prefer `THEME_PRESET_IDS` from `./presets`.
5521
- */
5522
- const THEME_PRESET_VALUES = THEME_PRESET_IDS;
5523
- const THEME_MODE_VALUES = [
5524
- "light",
5525
- "dark",
5526
- "system"
5527
- ];
5528
- const LEGACY_THEME_STORAGE_KEY = "opt-theme";
5529
- const THEME_PRESET_STORAGE_KEY = "opt-preset";
5530
- const THEME_MODE_STORAGE_KEY = "opt-mode";
5531
- const DARK_MQ = "(prefers-color-scheme: dark)";
5532
- /**
5533
- * Presets that force light mode regardless of system dark/light preference.
5534
- * Mirrors `FORCE_LIGHT_PRESET_IDS` from `./presets` — kept as alias for
5535
- * existing imports.
5536
- */
5537
- const FORCE_LIGHT_PRESETS = FORCE_LIGHT_PRESET_IDS;
5538
- /** All valid compound theme names. Useful for static enumeration. */
5539
- const ALL_COMPOUND_THEMES = THEME_PRESET_IDS.flatMap((id) => FORCE_LIGHT_PRESET_IDS.has(id) ? [id] : [id, `${id}-dark`]);
5540
- function hasStorageMutation(storage) {
5541
- return typeof storage.setItem === "function" && typeof storage.removeItem === "function";
5542
- }
5543
- /** Handles is theme mode. */
5544
- function isThemeMode(value) {
5545
- return typeof value === "string" && THEME_MODE_VALUES.includes(value);
5546
- }
5547
- /** Compute compound theme name from preset + resolved mode. */
5548
- function getCompoundTheme(preset, resolvedMode) {
5549
- if (FORCE_LIGHT_PRESETS.has(preset)) return preset;
5550
- return resolvedMode === "dark" ? `${preset}-dark` : preset;
5551
- }
5552
- /** Handles resolve theme mode. */
5553
- function resolveThemeMode(preset, mode, systemDark) {
5554
- if (FORCE_LIGHT_PRESETS.has(preset)) return "light";
5555
- if (mode === "system") return systemDark ? "dark" : "light";
5556
- return mode;
5557
- }
5558
- /** Handles read stored theme. */
5559
- function readStoredTheme(storage, defaultPreset, defaultMode, options = {}) {
5560
- const legacy = storage.getItem(LEGACY_THEME_STORAGE_KEY);
5561
- const storedPreset = storage.getItem(THEME_PRESET_STORAGE_KEY);
5562
- const storedMode = storage.getItem(THEME_MODE_STORAGE_KEY);
5563
- let preset = defaultPreset;
5564
- if (typeof legacy === "string") {
5565
- const migratedLegacy = migrateDeprecatedPresetId(legacy);
5566
- if (migratedLegacy) {
5567
- if (options.migrateLegacy && hasStorageMutation(storage)) {
5568
- storage.setItem(THEME_PRESET_STORAGE_KEY, migratedLegacy);
5569
- storage.removeItem(LEGACY_THEME_STORAGE_KEY);
5570
- }
5571
- preset = migratedLegacy;
5572
- }
5573
- } else if (typeof storedPreset === "string") {
5574
- const migrated = migrateDeprecatedPresetId(storedPreset);
5575
- if (migrated) {
5576
- preset = migrated;
5577
- if (hasStorageMutation(storage) && migrated !== storedPreset && storedPreset in DEPRECATED_PRESET_MIGRATIONS) storage.setItem(THEME_PRESET_STORAGE_KEY, migrated);
5578
- }
5579
- }
5580
- const mode = isThemeMode(storedMode) ? storedMode : defaultMode;
5581
- return {
5582
- preset,
5583
- mode
5584
- };
5585
- }
5586
- /** Normalize a mode against an optional allowed-mode list. */
5587
- function coerceThemeMode(mode, allowedModes, fallback = "system") {
5588
- var _allowedModes$;
5589
- if (!allowedModes || allowedModes.length === 0) return mode;
5590
- if (allowedModes.includes(mode)) return mode;
5591
- if (allowedModes.includes(fallback)) return fallback;
5592
- return (_allowedModes$ = allowedModes[0]) !== null && _allowedModes$ !== void 0 ? _allowedModes$ : fallback;
5593
- }
5594
- /**
5595
- * Build the inline `<head>` boot script that paints the correct theme before
5596
- * React mounts. Two paths:
5597
- *
5598
- * 1. **Generated** — when both `opt-theme-spec` (kind: "generated") and
5599
- * `opt-theme-tokens` are present and well-formed, a `<style id="…">` tag
5600
- * is inserted with the cached tokens and `<html data-theme>` is set to the
5601
- * generated id (with optional `-dark` suffix). This bypasses opt-palette
5602
- * at startup so first paint is instant.
5603
- * 2. **Preset (legacy)** — falls through to the existing preset selector,
5604
- * including legacy `opt-theme` → `opt-preset` migration.
5605
- *
5606
- * Returned as a single minified line; intended for `dangerouslySetInnerHTML`.
5607
- */
5608
- function createThemeBootScript(defaultPreset = "default", defaultMode = "system", options = {}) {
5609
- var _options$allowedModes, _allowedModes$2;
5610
- const presetValues = JSON.stringify(THEME_PRESET_VALUES);
5611
- const modeValues = JSON.stringify(THEME_MODE_VALUES);
5612
- const allowedModes = ((_options$allowedModes = options.allowedModes) === null || _options$allowedModes === void 0 ? void 0 : _options$allowedModes.length) ? options.allowedModes : THEME_MODE_VALUES;
5613
- const allowedModeValues = JSON.stringify(allowedModes);
5614
- const forceLightPresets = JSON.stringify(Array.from(FORCE_LIGHT_PRESETS));
5615
- const deprecatedMap = JSON.stringify(DEPRECATED_PRESET_MIGRATIONS);
5616
- const lockedPreset = options.lockedPreset;
5617
- const defaultPresetLiteral = JSON.stringify(lockedPreset !== null && lockedPreset !== void 0 ? lockedPreset : defaultPreset);
5618
- const defaultModeLiteral = JSON.stringify(coerceThemeMode(defaultMode, allowedModes, (_allowedModes$2 = allowedModes[0]) !== null && _allowedModes$2 !== void 0 ? _allowedModes$2 : "system"));
5619
- const lockedPresetLiteral = JSON.stringify(lockedPreset !== null && lockedPreset !== void 0 ? lockedPreset : null);
5620
- const styleId = JSON.stringify(GLOBAL_THEME_STYLE_ID);
5621
- const specKey = JSON.stringify(THEME_SPEC_STORAGE_KEY);
5622
- const tokensKey = JSON.stringify(THEME_TOKENS_STORAGE_KEY);
5623
- const presetKey = JSON.stringify(THEME_PRESET_STORAGE_KEY);
5624
- const modeKey = JSON.stringify(THEME_MODE_STORAGE_KEY);
5625
- const legacyKey = JSON.stringify(LEGACY_THEME_STORAGE_KEY);
5626
- const darkMq = JSON.stringify(DARK_MQ);
5627
- return `
5628
- try {
5629
- var pv=${presetValues},mv=${modeValues},am=${allowedModeValues},fp=${forceLightPresets},dm=${deprecatedMap},lk=${lockedPresetLiteral};
5630
- function mp(v){if(typeof v!=="string")return null;if(pv.indexOf(v)>=0)return v;return Object.prototype.hasOwnProperty.call(dm,v)?dm[v]:null;}
5631
- var p=${defaultPresetLiteral};
5632
- if(!lk){
5633
- var lp=localStorage.getItem(${legacyKey}),sp=localStorage.getItem(${presetKey});
5634
- var lpm=mp(lp),spm=mp(sp);
5635
- p=lpm||spm||${defaultPresetLiteral};
5636
- if(lpm){localStorage.setItem(${presetKey},lpm);localStorage.removeItem(${legacyKey});}
5637
- else if(sp&&spm&&sp!==spm){localStorage.setItem(${presetKey},spm);}
5638
- }
5639
- var sm=localStorage.getItem(${modeKey});
5640
- var m=(typeof sm==="string"&&mv.indexOf(sm)>=0&&am.indexOf(sm)>=0)?sm:${defaultModeLiteral};
5641
- var spec=null,tk=null;
5642
- if(!lk){try{var sj=localStorage.getItem(${specKey});if(sj)spec=JSON.parse(sj);}catch(e){}}
5643
- if(!lk){try{var tj=localStorage.getItem(${tokensKey});if(tj)tk=JSON.parse(tj);}catch(e){}}
5644
- if(!lk&&spec&&spec.kind==="generated"&&tk&&typeof tk.id==="string"&&tk.light&&typeof tk.light==="object"){
5645
- var fld=spec.forceLightOnly===true;
5646
- var dk=!fld&&(m==="dark"||(m==="system"&&matchMedia(${darkMq}).matches));
5647
- var tt=(dk&&tk.dark)?tk.dark:tk.light;
5648
- var cid=(dk&&tk.dark)?(tk.id+"-dark"):tk.id;
5649
- var css="";
5650
- for(var k in tt){if(Object.prototype.hasOwnProperty.call(tt,k))css+=k+":"+tt[k]+";"}
5651
- var st=document.getElementById(${styleId});
5652
- if(!st){st=document.createElement("style");st.id=${styleId};document.head.appendChild(st);}
5653
- st.textContent='[data-theme="'+cid+'"]{'+css+'}';
5654
- document.documentElement.setAttribute("data-theme",cid);
5655
- } else {
5656
- var d=fp.indexOf(p)<0&&(m==="dark"||(m==="system"&&matchMedia(${darkMq}).matches));
5657
- document.documentElement.setAttribute("data-theme",d?p+"-dark":p);
5658
- }
5659
- } catch(e){}`.replace(/\n\s*/g, "");
5660
- }
5661
- //#endregion
5662
- //#region src/lib/theme/theme-controller-context.tsx
5663
- const OptThemeControllerContext = (0, react.createContext)(null);
5664
- /**
5665
- * Reads the controller exposed by the nearest `OptThemeProvider`. Returns
5666
- * `null` when used outside a provider — callers should treat that as "no
5667
- * promotion available" and behave as if scope === "override".
5668
- */
5669
- function useOptThemeController() {
5670
- return (0, react.useContext)(OptThemeControllerContext);
5671
- }
5672
- //#endregion
5673
- //#region src/lib/theme/theme-provider.tsx
5674
- const THEME_STORAGE_KEYS = /* @__PURE__ */ new Set([
5675
- LEGACY_THEME_STORAGE_KEY,
5676
- THEME_PRESET_STORAGE_KEY,
5677
- THEME_MODE_STORAGE_KEY,
5678
- THEME_SPEC_STORAGE_KEY,
5679
- THEME_TOKENS_STORAGE_KEY
5680
- ]);
5681
- function getSystemDark() {
5682
- if (typeof window === "undefined") return false;
5683
- return window.matchMedia(DARK_MQ).matches;
5684
- }
5685
- function subscribeSystemDark(cb) {
5686
- if (typeof window === "undefined") return () => {};
5687
- const mql = window.matchMedia(DARK_MQ);
5688
- mql.addEventListener("change", cb);
5689
- return () => mql.removeEventListener("change", cb);
5690
- }
5691
- /**
5692
- * Read the persisted ThemeSpec, preferring a generated spec when present.
5693
- * Falls back to the legacy preset path otherwise. Does not touch localStorage
5694
- * apart from the legacy migration handled by `readStoredTheme`.
5695
- */
5696
- function readStoredSpec(defaultPreset, defaultMode, options = {}) {
5697
- const generated = safeParseStoredSpec(window.localStorage.getItem(THEME_SPEC_STORAGE_KEY));
5698
- const { preset, mode } = readStoredTheme(window.localStorage, defaultPreset, defaultMode, options);
5699
- if (generated && generated.kind === "generated") return {
5700
- spec: generated,
5701
- mode,
5702
- presetShadow: preset
5703
- };
5704
- return {
5705
- spec: presetTheme(preset),
5706
- mode,
5707
- presetShadow: preset
5708
- };
5709
- }
5710
- function persistSpec(spec) {
5711
- if (isPresetSpec(spec)) {
5712
- window.localStorage.setItem(THEME_PRESET_STORAGE_KEY, spec.preset);
5713
- window.localStorage.removeItem(THEME_SPEC_STORAGE_KEY);
5714
- window.localStorage.removeItem(THEME_TOKENS_STORAGE_KEY);
5715
- return;
5716
- }
5717
- if (isGeneratedSpec(spec)) {
5718
- const tokens = computeGeneratedTokens(spec);
5719
- window.localStorage.setItem(THEME_SPEC_STORAGE_KEY, JSON.stringify(spec));
5720
- if (tokens) window.localStorage.setItem(THEME_TOKENS_STORAGE_KEY, JSON.stringify(buildTokenCache(tokens)));
5721
- else window.localStorage.removeItem(THEME_TOKENS_STORAGE_KEY);
5722
- return;
5723
- }
5724
- const descriptor = spec.descriptor;
5725
- if (isThemePresetId(descriptor.id) && !descriptor.palette) {
5726
- window.localStorage.setItem(THEME_PRESET_STORAGE_KEY, descriptor.id);
5727
- window.localStorage.removeItem(THEME_SPEC_STORAGE_KEY);
5728
- window.localStorage.removeItem(THEME_TOKENS_STORAGE_KEY);
5729
- return;
5730
- }
5731
- if (descriptor.palette) persistSpec({
5732
- kind: "generated",
5733
- id: descriptor.id,
5734
- label: descriptor.label,
5735
- palette: descriptor.palette,
5736
- forceLightOnly: descriptor.forceLightOnly
5737
- });
5738
- }
5739
- function upsertGlobalStyle(css) {
5740
- if (typeof document === "undefined") return;
5741
- let node = document.getElementById(GLOBAL_THEME_STYLE_ID);
5742
- if (!node) {
5743
- node = document.createElement("style");
5744
- node.id = GLOBAL_THEME_STYLE_ID;
5745
- document.head.appendChild(node);
5746
- }
5747
- if (node.textContent !== css) node.textContent = css;
5748
- }
5749
- function removeGlobalStyle() {
5750
- var _document$getElementB;
5751
- if (typeof document === "undefined") return;
5752
- (_document$getElementB = document.getElementById("opt-global-theme")) === null || _document$getElementB === void 0 || _document$getElementB.remove();
5753
- }
5754
- const ThemeContext = (0, react.createContext)(void 0);
5755
- /** Renders the opt theme provider component. */
5756
- function OptThemeProvider({ children, defaultTheme, controlledTheme, defaultPreset = "default", defaultMode = "system", lockedPreset, allowedModes, allowThemePromotion = true }) {
5757
- var _allowedModes$, _ref, _effectiveStackTop$sp;
5758
- const normalizedDefaultMode = coerceThemeMode(defaultMode, allowedModes, (_allowedModes$ = allowedModes === null || allowedModes === void 0 ? void 0 : allowedModes[0]) !== null && _allowedModes$ !== void 0 ? _allowedModes$ : "system");
5759
- const initialSpec = (_ref = controlledTheme !== null && controlledTheme !== void 0 ? controlledTheme : lockedPreset ? presetTheme(lockedPreset) : defaultTheme) !== null && _ref !== void 0 ? _ref : presetTheme(defaultPreset);
5760
- const initialPresetShadow = isPresetSpec(initialSpec) ? initialSpec.preset : lockedPreset !== null && lockedPreset !== void 0 ? lockedPreset : defaultPreset;
5761
- const [theme, setThemeState] = (0, react.useState)(initialSpec);
5762
- const [presetShadow, setPresetShadow] = (0, react.useState)(initialPresetShadow);
5763
- const [mode, setModeState] = (0, react.useState)(normalizedDefaultMode);
5764
- /**
5765
- * Transient theme override stack — descendants (typically harnesses in
5766
- * promote-global mode) push specs that take priority over `controlledTheme`
5767
- * and persisted state. The stack is intentionally kept out of localStorage.
5768
- */
5769
- const [stack, setStack] = (0, react.useState)([]);
5770
- const isControlled = controlledTheme !== void 0;
5771
- const isLocked = !isControlled && lockedPreset !== void 0;
5772
- (0, react.useEffect)(() => {
5773
- if (!isControlled) return;
5774
- setThemeState(controlledTheme);
5775
- if (isPresetSpec(controlledTheme)) setPresetShadow(controlledTheme.preset);
5776
- }, [controlledTheme, isControlled]);
5777
- (0, react.useEffect)(() => {
5778
- if (isControlled) return;
5779
- if (isLocked) {
5780
- const locked = lockedPreset !== null && lockedPreset !== void 0 ? lockedPreset : defaultPreset;
5781
- const { mode: storedMode } = readStoredTheme(window.localStorage, locked, normalizedDefaultMode);
5782
- const nextMode = coerceThemeMode(storedMode, allowedModes, normalizedDefaultMode);
5783
- const nextSpec = presetTheme(locked);
5784
- setThemeState((prev) => themeEqual(prev, nextSpec) ? prev : nextSpec);
5785
- setPresetShadow((prev) => prev === locked ? prev : locked);
5786
- setModeState((prev) => prev === nextMode ? prev : nextMode);
5787
- return;
5788
- }
5789
- const next = readStoredSpec(defaultPreset, defaultMode, { migrateLegacy: true });
5790
- const nextMode = coerceThemeMode(next.mode, allowedModes, normalizedDefaultMode);
5791
- setThemeState((prev) => themeEqual(prev, next.spec) ? prev : next.spec);
5792
- setPresetShadow((prev) => prev === next.presetShadow ? prev : next.presetShadow);
5793
- setModeState((prev) => prev === nextMode ? prev : nextMode);
5794
- }, [
5795
- allowedModes,
5796
- isControlled,
5797
- isLocked,
5798
- lockedPreset,
5799
- normalizedDefaultMode
5800
- ]);
5801
- (0, react.useEffect)(() => {
5802
- if (isControlled) return;
5803
- function handleStorage(event) {
5804
- if (event.storageArea && event.storageArea !== window.localStorage) return;
5805
- if (event.key != null && !THEME_STORAGE_KEYS.has(event.key)) return;
5806
- if (isLocked) {
5807
- const locked = lockedPreset !== null && lockedPreset !== void 0 ? lockedPreset : defaultPreset;
5808
- const { mode: storedMode } = readStoredTheme(window.localStorage, locked, normalizedDefaultMode);
5809
- const nextMode = coerceThemeMode(storedMode, allowedModes, normalizedDefaultMode);
5810
- const nextSpec = presetTheme(locked);
5811
- setThemeState((prev) => themeEqual(prev, nextSpec) ? prev : nextSpec);
5812
- setPresetShadow((prev) => prev === locked ? prev : locked);
5813
- setModeState((prev) => prev === nextMode ? prev : nextMode);
5814
- return;
5815
- }
5816
- const next = readStoredSpec(defaultPreset, defaultMode, { migrateLegacy: event.key === LEGACY_THEME_STORAGE_KEY });
5817
- const nextMode = coerceThemeMode(next.mode, allowedModes, normalizedDefaultMode);
5818
- setThemeState((prev) => themeEqual(prev, next.spec) ? prev : next.spec);
5819
- setPresetShadow((prev) => prev === next.presetShadow ? prev : next.presetShadow);
5820
- setModeState((prev) => prev === nextMode ? prev : nextMode);
5821
- }
5822
- window.addEventListener("storage", handleStorage);
5823
- return () => window.removeEventListener("storage", handleStorage);
5824
- }, [
5825
- allowedModes,
5826
- isControlled,
5827
- isLocked,
5828
- defaultMode,
5829
- defaultPreset,
5830
- lockedPreset,
5831
- normalizedDefaultMode
5832
- ]);
5833
- const systemDark = (0, react.useSyncExternalStore)(subscribeSystemDark, getSystemDark, () => false);
5834
- const stackTop = stack.length > 0 ? stack[stack.length - 1] : null;
5835
- const effectiveStackTop = allowThemePromotion ? stackTop : null;
5836
- const isPromoted = effectiveStackTop !== null;
5837
- const effectiveSpec = (_effectiveStackTop$sp = effectiveStackTop === null || effectiveStackTop === void 0 ? void 0 : effectiveStackTop.spec) !== null && _effectiveStackTop$sp !== void 0 ? _effectiveStackTop$sp : theme;
5838
- const forceLight = isForceLightSpec(effectiveSpec);
5839
- const resolvedMode = computeResolvedMode(forceLight, mode, systemDark);
5840
- const compoundId = getCompoundIdForSpec(effectiveSpec, resolvedMode, forceLight);
5841
- const [hydrated, setHydrated] = (0, react.useState)(false);
5842
- (0, react.useEffect)(() => {
5843
- setHydrated(true);
5844
- }, []);
5845
- (0, react.useEffect)(() => {
5846
- if (!hydrated) return;
5847
- if (document.documentElement.getAttribute("data-theme") !== compoundId) document.documentElement.setAttribute("data-theme", compoundId);
5848
- }, [compoundId, hydrated]);
5849
- (0, react.useEffect)(() => {
5850
- if (isPresetSpec(effectiveSpec)) {
5851
- removeGlobalStyle();
5852
- return;
5853
- }
5854
- const tokens = computeGeneratedTokens(effectiveSpec);
5855
- if (!tokens) {
5856
- removeGlobalStyle();
5857
- return;
5858
- }
5859
- upsertGlobalStyle(serializeGeneratedTheme(tokens));
5860
- if (!isControlled && !isPromoted && effectiveSpec === theme) try {
5861
- window.localStorage.setItem(THEME_TOKENS_STORAGE_KEY, JSON.stringify(buildTokenCache(tokens)));
5862
- } catch (_unused) {}
5863
- }, [
5864
- effectiveSpec,
5865
- theme,
5866
- isControlled,
5867
- isPromoted
5868
- ]);
5869
- const setTheme = (0, react.useCallback)((next) => {
5870
- if (isLocked) {
5871
- if (process.env.NODE_ENV !== "production") console.warn("[OptThemeProvider] setTheme called while lockedPreset is set — ignoring.");
5872
- return;
5873
- }
5874
- if (isControlled) {
5875
- if (process.env.NODE_ENV !== "production") console.warn("[OptThemeProvider] setTheme called while controlledTheme is set — ignoring.");
5876
- return;
5877
- }
5878
- setThemeState(next);
5879
- if (isPresetSpec(next)) setPresetShadow(next.preset);
5880
- try {
5881
- persistSpec(next);
5882
- } catch (_unused2) {}
5883
- }, [isControlled, isLocked]);
5884
- const setPreset = (0, react.useCallback)((p) => {
5885
- setTheme(presetTheme(p));
5886
- }, [setTheme]);
5887
- const setMode = (0, react.useCallback)((m) => {
5888
- const nextMode = coerceThemeMode(m, allowedModes, normalizedDefaultMode);
5889
- setModeState(nextMode);
5890
- if (!isControlled) try {
5891
- window.localStorage.setItem(THEME_MODE_STORAGE_KEY, nextMode);
5892
- } catch (_unused3) {}
5893
- }, [
5894
- allowedModes,
5895
- isControlled,
5896
- normalizedDefaultMode
5897
- ]);
5898
- const controller = (0, react.useMemo)(() => ({
5899
- pushTheme(id, spec) {
5900
- if (!allowThemePromotion) return;
5901
- setStack((prev) => {
5902
- return [...prev.filter((entry) => entry.id !== id), {
5903
- id,
5904
- spec
5905
- }];
5906
- });
5907
- },
5908
- popTheme(id) {
5909
- if (!allowThemePromotion) return;
5910
- setStack((prev) => prev.filter((entry) => entry.id !== id));
5911
- }
5912
- }), [allowThemePromotion]);
5913
- const value = (0, react.useMemo)(() => ({
5914
- theme: effectiveSpec,
5915
- setTheme,
5916
- preset: presetShadow,
5917
- setPreset,
5918
- mode,
5919
- setMode,
5920
- resolvedMode,
5921
- isControlled,
5922
- isGenerated: !isPresetSpec(effectiveSpec),
5923
- isPromoted
5924
- }), [
5925
- effectiveSpec,
5926
- setTheme,
5927
- presetShadow,
5928
- setPreset,
5929
- mode,
5930
- setMode,
5931
- resolvedMode,
5932
- isControlled,
5933
- isPromoted
5934
- ]);
5935
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ThemeContext.Provider, {
5936
- value,
5937
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OptThemeControllerContext.Provider, {
5938
- value: controller,
5939
- children
5940
- })
5941
- });
5942
- }
5943
- /** Provides opt theme. */
5944
- function useOptTheme() {
5945
- const ctx = (0, react.useContext)(ThemeContext);
5946
- if (!ctx) throw new Error("useOptTheme must be used within OptThemeProvider");
5947
- return ctx;
5948
- }
5949
- /** Reads opt theme context when present without requiring a provider. */
5950
- function useOptionalOptTheme() {
5951
- return (0, react.useContext)(ThemeContext);
5952
- }
5953
- function computeResolvedMode(forceLight, mode, systemDark) {
5954
- if (forceLight) return "light";
5955
- if (mode === "system") return systemDark ? "dark" : "light";
5956
- return mode;
5957
- }
5958
- function themeEqual(a, b) {
5959
- if (a === b) return true;
5960
- if (a.kind !== b.kind) return false;
5961
- if (a.kind === "preset" && b.kind === "preset") return a.preset === b.preset && overridesEqual(a.overrides, b.overrides);
5962
- if (a.kind === "generated" && b.kind === "generated") return a.id === b.id && a.label === b.label && a.forceLightOnly === b.forceLightOnly && JSON.stringify(a.palette) === JSON.stringify(b.palette) && overridesEqual(a.overrides, b.overrides);
5963
- if (a.kind === "descriptor" && b.kind === "descriptor") return a.descriptor === b.descriptor && overridesEqual(a.overrides, b.overrides);
5964
- return false;
5965
- }
5966
- function overridesEqual(a, b) {
5967
- if (a === b) return true;
5968
- if (!a && !b) return true;
5969
- if (!a || !b) return false;
5970
- return JSON.stringify(a) === JSON.stringify(b);
5971
- }
5972
5562
  //#endregion
5973
5563
  //#region src/shells/theme-switcher.tsx
5974
5564
  const FALLBACK_SWATCHES = [
@@ -5977,9 +5567,9 @@ const FALLBACK_SWATCHES = [
5977
5567
  "var(--opt-text)"
5978
5568
  ];
5979
5569
  function buildThemeOptions() {
5980
- return THEME_PRESET_IDS.map((id) => {
5570
+ return require_field_sidebar.THEME_PRESET_IDS.map((id) => {
5981
5571
  var _descriptor$switcherS;
5982
- const descriptor = THEME_REGISTRY[id];
5572
+ const descriptor = require_field_sidebar.THEME_REGISTRY[id];
5983
5573
  return {
5984
5574
  value: id,
5985
5575
  label: descriptor.label,
@@ -6060,7 +5650,7 @@ function resolveLabels(user) {
6060
5650
  /** Renders the theme switcher component. */
6061
5651
  function ThemeSwitcher({ className, labels: userLabels, variant = "default", customPresetEditor, preset: controlledPreset, onPresetChange, mode: controlledMode, onModeChange }) {
6062
5652
  var _ref;
6063
- const themeContext = useOptionalOptTheme();
5653
+ const themeContext = require_field_sidebar.useOptionalOptTheme();
6064
5654
  const preset = controlledPreset !== null && controlledPreset !== void 0 ? controlledPreset : themeContext === null || themeContext === void 0 ? void 0 : themeContext.preset;
6065
5655
  const mode = (_ref = controlledMode !== null && controlledMode !== void 0 ? controlledMode : themeContext === null || themeContext === void 0 ? void 0 : themeContext.mode) !== null && _ref !== void 0 ? _ref : "light";
6066
5656
  const setPreset = onPresetChange !== null && onPresetChange !== void 0 ? onPresetChange : themeContext === null || themeContext === void 0 ? void 0 : themeContext.setPreset;
@@ -6069,7 +5659,7 @@ function ThemeSwitcher({ className, labels: userLabels, variant = "default", cus
6069
5659
  const labels = resolveLabels(userLabels);
6070
5660
  const options = buildThemeOptions();
6071
5661
  const activePreset = preset;
6072
- const forcedMode = FORCE_LIGHT_PRESET_IDS.has(activePreset);
5662
+ const forcedMode = require_field_sidebar.FORCE_LIGHT_PRESET_IDS.has(activePreset);
6073
5663
  const showCustomBadge = controlledPreset === void 0 && ((themeContext === null || themeContext === void 0 ? void 0 : themeContext.isGenerated) === true || (themeContext === null || themeContext === void 0 ? void 0 : themeContext.isPromoted) === true);
6074
5664
  if (options.length === 0) return null;
6075
5665
  if (variant === "compact") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -6762,10 +6352,10 @@ function BulkActions({ selectedCount, actions, onAction, onClear, labels: custom
6762
6352
  labels.selected
6763
6353
  ]
6764
6354
  }),
6765
- hasActions ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_key_pad_menu.DropdownMenu, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_key_pad_menu.DropdownTrigger, {
6355
+ hasActions ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_text_truncate.DropdownMenu, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.DropdownTrigger, {
6766
6356
  className: "border-border bg-surface text-text-primary hover:bg-bg-subtle inline-flex h-8 items-center rounded-md border px-3 text-sm font-medium",
6767
6357
  children: labels.actions
6768
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_key_pad_menu.DropdownContent, { children: actions.map((action) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_key_pad_menu.DropdownItem, {
6358
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.DropdownContent, { children: actions.map((action) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.DropdownItem, {
6769
6359
  onClick: () => onAction(action.id),
6770
6360
  className: action.variant === "danger" ? "text-danger" : void 0,
6771
6361
  children: action.label
@@ -7800,7 +7390,7 @@ function FeatureFlagPanel({ flags, onToggle, onRolloutChange, environments, clas
7800
7390
  className: "text-text-tertiary text-xs",
7801
7391
  children: flag.description
7802
7392
  })]
7803
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_key_pad_menu.Switch, {
7393
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Switch, {
7804
7394
  checked: flag.enabled,
7805
7395
  onChange: (checked) => onToggle(flag.id, checked),
7806
7396
  label: flag.enabled ? labels.enabled : labels.disabled
@@ -9284,7 +8874,7 @@ const enStarRatingLabels = {
9284
8874
  };
9285
8875
  const enBreadcrumbLabels = { navAriaLabel: "Go to {label}" };
9286
8876
  //#endregion
9287
- exports.ALL_COMPOUND_THEMES = ALL_COMPOUND_THEMES;
8877
+ exports.ALL_COMPOUND_THEMES = require_field_sidebar.ALL_COMPOUND_THEMES;
9288
8878
  exports.AccordionContent = require_key_pad_menu.AccordionContent;
9289
8879
  exports.AccordionItem = require_key_pad_menu.AccordionItem;
9290
8880
  exports.AccordionRoot = require_key_pad_menu.AccordionRoot;
@@ -9308,7 +8898,7 @@ exports.Breadcrumb = require_key_pad_menu.Breadcrumb;
9308
8898
  exports.BulkActions = BulkActions;
9309
8899
  exports.Button = require_text_truncate.Button;
9310
8900
  exports.COLOR_PRESETS = COLOR_PRESETS;
9311
- exports.Callout = Callout;
8901
+ exports.Callout = require_field_sidebar.Callout;
9312
8902
  exports.Card = require_key_pad_menu.Card;
9313
8903
  exports.CardContent = require_key_pad_menu.CardContent;
9314
8904
  exports.CardDescription = require_key_pad_menu.CardDescription;
@@ -9360,6 +8950,7 @@ exports.ConnectionIndicator = ConnectionIndicator;
9360
8950
  exports.Container = require_key_pad_menu.Container;
9361
8951
  exports.ContentTabs = require_field_sidebar.ContentTabs;
9362
8952
  exports.CopyButton = CopyButton;
8953
+ exports.CriteriaBuilder = CriteriaBuilder;
9363
8954
  exports.DEFAULT_PRESETS = require_text_truncate.DEFAULT_PRESETS;
9364
8955
  exports.DEFAULT_TIME_PRESETS = require_field_sidebar.DEFAULT_TIME_PRESETS;
9365
8956
  exports.DashboardGrid = require_field_sidebar.DashboardGrid;
@@ -9367,7 +8958,7 @@ exports.DataImportWizard = DataImportWizard;
9367
8958
  exports.DataTable = require_field_sidebar.DataTable;
9368
8959
  exports.DateRangePicker = require_text_truncate.DateRangePicker;
9369
8960
  exports.DeploymentTimeline = require_field_sidebar.DeploymentTimeline;
9370
- exports.DescriptionList = require_key_pad_menu.DescriptionList;
8961
+ exports.DescriptionList = require_text_truncate.DescriptionList;
9371
8962
  exports.DesignGuidePanel = DesignGuidePanel;
9372
8963
  exports.DialogBody = require_text_truncate.DialogBody;
9373
8964
  exports.DialogDescription = require_text_truncate.DialogDescription;
@@ -9385,13 +8976,14 @@ exports.DomainTable = require_field_sidebar.DomainTable;
9385
8976
  exports.DrawerClose = require_key_pad_menu.DrawerClose;
9386
8977
  exports.DrawerPanel = require_key_pad_menu.DrawerPanel;
9387
8978
  exports.DrawerRoot = require_key_pad_menu.DrawerRoot;
9388
- exports.DropdownContent = require_key_pad_menu.DropdownContent;
9389
- exports.DropdownItem = require_key_pad_menu.DropdownItem;
9390
- exports.DropdownMenu = require_key_pad_menu.DropdownMenu;
9391
- exports.DropdownSeparator = require_key_pad_menu.DropdownSeparator;
9392
- exports.DropdownTrigger = require_key_pad_menu.DropdownTrigger;
8979
+ exports.DropdownContent = require_text_truncate.DropdownContent;
8980
+ exports.DropdownItem = require_text_truncate.DropdownItem;
8981
+ exports.DropdownMenu = require_text_truncate.DropdownMenu;
8982
+ exports.DropdownSeparator = require_text_truncate.DropdownSeparator;
8983
+ exports.DropdownTrigger = require_text_truncate.DropdownTrigger;
9393
8984
  exports.DynamicFieldForm = DynamicFieldForm;
9394
8985
  exports.EMPTY_QUERY = require_field_sidebar.EMPTY_QUERY;
8986
+ exports.EMPTY_QUERY_FILTERS = require_field_sidebar.EMPTY_QUERY_FILTERS;
9395
8987
  exports.EditorToolbar = require_field_sidebar.EditorToolbar;
9396
8988
  exports.EmptyState = require_text_truncate.EmptyState;
9397
8989
  exports.EnvPanel = require_field_sidebar.EnvPanel;
@@ -9409,8 +9001,8 @@ exports.ExportButton = ExportButton;
9409
9001
  exports.Expression = require_key_pad_menu.Expression;
9410
9002
  exports.ExpressionGroup = require_key_pad_menu.ExpressionGroup;
9411
9003
  exports.FIELD_TYPE_TOKENS = require_text_truncate.FIELD_TYPE_TOKENS;
9412
- exports.FORCE_LIGHT_PRESETS = FORCE_LIGHT_PRESETS;
9413
- exports.FORCE_LIGHT_PRESET_IDS = FORCE_LIGHT_PRESET_IDS;
9004
+ exports.FORCE_LIGHT_PRESETS = require_field_sidebar.FORCE_LIGHT_PRESETS;
9005
+ exports.FORCE_LIGHT_PRESET_IDS = require_field_sidebar.FORCE_LIGHT_PRESET_IDS;
9414
9006
  exports.Facet = require_key_pad_menu.Facet;
9415
9007
  exports.FacetGroup = require_key_pad_menu.FacetGroup;
9416
9008
  exports.FailureList = require_field_sidebar.FailureList;
@@ -9452,8 +9044,8 @@ exports.FormSubmit = require_text_truncate.FormSubmit;
9452
9044
  exports.FormSwitch = require_text_truncate.FormSwitch;
9453
9045
  exports.FormTagInput = require_text_truncate.FormTagInput;
9454
9046
  exports.FormTextarea = require_text_truncate.FormTextarea;
9455
- exports.GENERATED_THEME_ID_DEFAULT = GENERATED_THEME_ID_DEFAULT;
9456
- exports.GLOBAL_THEME_STYLE_ID = GLOBAL_THEME_STYLE_ID;
9047
+ exports.GENERATED_THEME_ID_DEFAULT = require_field_sidebar.GENERATED_THEME_ID_DEFAULT;
9048
+ exports.GLOBAL_THEME_STYLE_ID = require_field_sidebar.GLOBAL_THEME_STYLE_ID;
9457
9049
  exports.Heading = require_key_pad_menu.Heading;
9458
9050
  exports.Highlight = require_text_truncate.Highlight;
9459
9051
  exports.IconPicker = require_text_truncate.IconPicker;
@@ -9494,8 +9086,8 @@ exports.NotificationToast = require_field_sidebar.NotificationToast;
9494
9086
  exports.NumberInput = require_text_truncate.NumberInput;
9495
9087
  exports.OPT_TEXT_PRIMARY = require_text_truncate.OPT_TEXT_PRIMARY;
9496
9088
  exports.OPT_TEXT_SECONDARY = require_text_truncate.OPT_TEXT_SECONDARY;
9497
- exports.OptThemeControllerContext = OptThemeControllerContext;
9498
- exports.OptThemeProvider = OptThemeProvider;
9089
+ exports.OptThemeControllerContext = require_field_sidebar.OptThemeControllerContext;
9090
+ exports.OptThemeProvider = require_field_sidebar.OptThemeProvider;
9499
9091
  exports.OtpField = require_key_pad_menu.OtpField;
9500
9092
  exports.POPULAR_ICONS = POPULAR_ICONS;
9501
9093
  exports.PageAside = PageAside;
@@ -9511,7 +9103,14 @@ exports.PromptTemplate = PromptTemplate;
9511
9103
  exports.PropertyExplorer = PropertyExplorer;
9512
9104
  exports.QueryBar = require_field_sidebar.QueryBar;
9513
9105
  exports.QueryFilterBar = QueryFilterBar;
9514
- exports.QueryResultsTable = QueryResultsTable;
9106
+ exports.QueryFiltersMenu = require_field_sidebar.QueryFiltersMenu;
9107
+ exports.QueryHistoryList = require_field_sidebar.QueryHistoryList;
9108
+ exports.QueryOutputPane = require_field_sidebar.QueryOutputPane;
9109
+ exports.QueryResultChart = require_field_sidebar.QueryResultChart;
9110
+ exports.QueryResultsTable = require_field_sidebar.QueryResultsTable;
9111
+ exports.QueryToolbar = require_field_sidebar.QueryToolbar;
9112
+ exports.QueryVariablesMenu = require_field_sidebar.QueryVariablesMenu;
9113
+ exports.QueryWorkspace = require_field_sidebar.QueryWorkspace;
9515
9114
  exports.QuickActions = QuickActions;
9516
9115
  exports.Radio = require_key_pad_menu.Radio;
9517
9116
  exports.RadioGroup = require_key_pad_menu.RadioGroup;
@@ -9522,9 +9121,9 @@ Object.defineProperty(exports, "ReportWidget", {
9522
9121
  return _reopt_ai_opt_charts.ReportWidget;
9523
9122
  }
9524
9123
  });
9525
- exports.ResizableHandle = require_key_pad_menu.ResizableHandle;
9526
- exports.ResizablePanel = require_key_pad_menu.ResizablePanel;
9527
- exports.ResizablePanelGroup = require_key_pad_menu.ResizablePanelGroup;
9124
+ exports.ResizableHandle = require_text_truncate.ResizableHandle;
9125
+ exports.ResizablePanel = require_text_truncate.ResizablePanel;
9126
+ exports.ResizablePanelGroup = require_text_truncate.ResizablePanelGroup;
9528
9127
  exports.RichTextEditor = RichTextEditor;
9529
9128
  exports.RouteFocusManager = RouteFocusManager;
9530
9129
  exports.SHADER_OVERLAY_PARAM_KEYS = require_key_pad_menu.SHADER_OVERLAY_PARAM_KEYS;
@@ -9532,13 +9131,15 @@ exports.SHADER_PARAM_FALLBACK = require_key_pad_menu.SHADER_PARAM_FALLBACK;
9532
9131
  exports.SHADER_PARAM_KEYS = require_key_pad_menu.SHADER_PARAM_KEYS;
9533
9132
  exports.SHADER_PRESETS = require_key_pad_menu.SHADER_PRESETS;
9534
9133
  exports.SHADER_PRESET_IDS = require_key_pad_menu.SHADER_PRESET_IDS;
9134
+ exports.SaveQueryDialog = require_field_sidebar.SaveQueryDialog;
9535
9135
  exports.SavedFilters = SavedFilters;
9136
+ exports.SchemaTree = require_field_sidebar.SchemaTree;
9536
9137
  exports.ScoreBreakdown = require_field_sidebar.ScoreBreakdown;
9537
9138
  exports.ScrollArea = require_key_pad_menu.ScrollArea;
9538
9139
  exports.SearchCombobox = require_field_sidebar.SearchCombobox;
9539
9140
  exports.Section = require_key_pad_menu.Section;
9540
9141
  exports.SegmentBuilder = SegmentBuilder;
9541
- exports.SegmentedControl = SegmentedControl;
9142
+ exports.SegmentedControl = require_field_sidebar.SegmentedControl;
9542
9143
  exports.SelectItem = require_text_truncate.SelectItem;
9543
9144
  exports.SelectLabel = require_text_truncate.SelectLabel;
9544
9145
  exports.SelectPopover = require_text_truncate.SelectPopover;
@@ -9557,7 +9158,7 @@ exports.SkeletonText = require_text_truncate.SkeletonText;
9557
9158
  exports.Slider = require_text_truncate.Slider;
9558
9159
  exports.SpatialNavProvider = SpatialNavProvider;
9559
9160
  exports.Spinner = require_text_truncate.Spinner;
9560
- exports.SqlEditor = SqlEditor;
9161
+ exports.SqlEditor = require_field_sidebar.SqlEditor;
9561
9162
  exports.StarRating = require_text_truncate.StarRating;
9562
9163
  exports.StatCard = require_text_truncate.StatCard;
9563
9164
  exports.StatusDot = StatusDot;
@@ -9565,12 +9166,12 @@ exports.StatusSelect = require_field_sidebar.StatusSelect;
9565
9166
  exports.StepBuilder = require_key_pad_menu.StepBuilder;
9566
9167
  exports.SummaryRow = SummaryRow;
9567
9168
  exports.SurfaceLayout = require_key_pad_menu.BlockLayout;
9568
- exports.Switch = require_key_pad_menu.Switch;
9169
+ exports.Switch = require_text_truncate.Switch;
9569
9170
  exports.THEME_PALETTES = require_key_pad_menu.THEME_PALETTES;
9570
- exports.THEME_PRESET_IDS = THEME_PRESET_IDS;
9571
- exports.THEME_REGISTRY = THEME_REGISTRY;
9572
- exports.THEME_SPEC_STORAGE_KEY = THEME_SPEC_STORAGE_KEY;
9573
- exports.THEME_TOKENS_STORAGE_KEY = THEME_TOKENS_STORAGE_KEY;
9171
+ exports.THEME_PRESET_IDS = require_field_sidebar.THEME_PRESET_IDS;
9172
+ exports.THEME_REGISTRY = require_field_sidebar.THEME_REGISTRY;
9173
+ exports.THEME_SPEC_STORAGE_KEY = require_field_sidebar.THEME_SPEC_STORAGE_KEY;
9174
+ exports.THEME_TOKENS_STORAGE_KEY = require_field_sidebar.THEME_TOKENS_STORAGE_KEY;
9574
9175
  exports.Tab = require_text_truncate.Tab;
9575
9176
  exports.TabList = require_text_truncate.TabList;
9576
9177
  exports.TabPanel = require_text_truncate.TabPanel;
@@ -9581,7 +9182,7 @@ exports.TeamMemberList = TeamMemberList;
9581
9182
  exports.TemplatePicker = TemplatePicker;
9582
9183
  exports.Text = require_key_pad_menu.Text;
9583
9184
  exports.TextTruncate = require_text_truncate.TextTruncate;
9584
- exports.Textarea = require_key_pad_menu.Textarea;
9185
+ exports.Textarea = require_text_truncate.Textarea;
9585
9186
  exports.ThemeSwitcher = ThemeSwitcher;
9586
9187
  exports.TimeRangeControl = require_field_sidebar.TimeRangeControl;
9587
9188
  exports.TimeRangeSelector = TimeRangeSelector;
@@ -9603,6 +9204,7 @@ Object.defineProperty(exports, "TrendChart", {
9603
9204
  return _reopt_ai_opt_charts.TrendChart;
9604
9205
  }
9605
9206
  });
9207
+ exports.VALUELESS_OPERATORS = require_field_sidebar.VALUELESS_OPERATORS;
9606
9208
  exports.VerticalTabs = VerticalTabs;
9607
9209
  exports.VirtualList = require_key_pad_menu.VirtualList;
9608
9210
  exports.WebhookConfig = WebhookConfig;
@@ -9611,15 +9213,24 @@ exports.WizardForm = WizardForm;
9611
9213
  exports.WorkflowCanvas = require_field_sidebar.WorkflowCanvas;
9612
9214
  exports.addClause = require_field_sidebar.addClause;
9613
9215
  exports.applyCellAction = require_field_sidebar.applyCellAction;
9614
- exports.buildTokenCache = buildTokenCache;
9216
+ exports.buildTokenCache = require_field_sidebar.buildTokenCache;
9615
9217
  exports.cn = require_text_truncate.cn;
9616
- exports.coerceThemeMode = coerceThemeMode;
9617
- exports.computeGeneratedTokens = computeGeneratedTokens;
9218
+ exports.coerceThemeMode = require_field_sidebar.coerceThemeMode;
9219
+ exports.computeGeneratedTokens = require_field_sidebar.computeGeneratedTokens;
9618
9220
  exports.computeSpatialTargets = computeSpatialTargets;
9221
+ exports.countActiveQueryFilters = require_field_sidebar.countActiveQueryFilters;
9619
9222
  exports.createBlock = require_key_pad_menu.createBlock;
9620
9223
  exports.createSurface = require_key_pad_menu.createBlock;
9621
- exports.createThemeBootScript = createThemeBootScript;
9622
- exports.defineThemeOverrides = defineThemeOverrides;
9224
+ exports.createThemeBootScript = require_field_sidebar.createThemeBootScript;
9225
+ exports.criteriaBuilderDuplicate = criteriaBuilderDuplicate;
9226
+ exports.criteriaBuilderDuplicateGroup = criteriaBuilderDuplicateGroup;
9227
+ exports.criteriaBuilderInsert = criteriaBuilderInsert;
9228
+ exports.criteriaBuilderMove = criteriaBuilderMove;
9229
+ exports.criteriaBuilderRemove = criteriaBuilderRemove;
9230
+ exports.criteriaBuilderRemoveGroup = criteriaBuilderRemoveGroup;
9231
+ exports.criteriaGroupLetter = criteriaGroupLetter;
9232
+ exports.defaultQueryChartConfig = require_field_sidebar.defaultQueryChartConfig;
9233
+ exports.defineThemeOverrides = require_field_sidebar.defineThemeOverrides;
9623
9234
  exports.describeClause = require_field_sidebar.describeClause;
9624
9235
  exports.enBreadcrumbLabels = enBreadcrumbLabels;
9625
9236
  exports.enComboboxLabels = enComboboxLabels;
@@ -9631,42 +9242,51 @@ exports.formatOptNumber = require_text_truncate.formatOptNumber;
9631
9242
  exports.formatOptShortDate = require_text_truncate.formatOptShortDate;
9632
9243
  exports.formatOptTime = require_text_truncate.formatOptTime;
9633
9244
  exports.formatQuery = require_field_sidebar.formatQuery;
9634
- exports.generatedTheme = generatedTheme;
9635
- exports.getCompoundIdForSpec = getCompoundIdForSpec;
9636
- exports.getCompoundTheme = getCompoundTheme;
9245
+ exports.formatQueryCell = require_field_sidebar.formatQueryCell;
9246
+ exports.generatedTheme = require_field_sidebar.generatedTheme;
9247
+ exports.getCompoundIdForSpec = require_field_sidebar.getCompoundIdForSpec;
9248
+ exports.getCompoundTheme = require_field_sidebar.getCompoundTheme;
9637
9249
  exports.getShaderFragment = require_key_pad_menu.getShaderFragment;
9638
9250
  exports.getShaderPreset = require_key_pad_menu.getShaderPreset;
9639
- exports.getThemeDescriptor = getThemeDescriptor;
9251
+ exports.getThemeDescriptor = require_field_sidebar.getThemeDescriptor;
9640
9252
  exports.getTrappedContextRole = getTrappedContextRole;
9641
9253
  exports.hasActiveQuery = hasActiveQuery;
9642
- exports.isDescriptorSpec = isDescriptorSpec;
9254
+ exports.hasActiveQueryFilters = require_field_sidebar.hasActiveQueryFilters;
9255
+ exports.isDescriptorSpec = require_field_sidebar.isDescriptorSpec;
9643
9256
  exports.isEmptyQuery = require_field_sidebar.isEmptyQuery;
9644
- exports.isForceLightSpec = isForceLightSpec;
9645
- exports.isGeneratedSpec = isGeneratedSpec;
9646
- exports.isPresetSpec = isPresetSpec;
9647
- exports.isThemePresetId = isThemePresetId;
9257
+ exports.isForceLightSpec = require_field_sidebar.isForceLightSpec;
9258
+ exports.isGeneratedSpec = require_field_sidebar.isGeneratedSpec;
9259
+ exports.isPresetSpec = require_field_sidebar.isPresetSpec;
9260
+ exports.isThemePresetId = require_field_sidebar.isThemePresetId;
9261
+ exports.isValidQueryVariableName = require_field_sidebar.isValidQueryVariableName;
9648
9262
  exports.koBreadcrumbLabels = require_text_truncate.koBreadcrumbLabels;
9649
9263
  exports.koComboboxLabels = require_text_truncate.koComboboxLabels;
9650
9264
  exports.koStarRatingLabels = require_text_truncate.koStarRatingLabels;
9651
9265
  exports.matchesQuery = matchesQuery;
9652
9266
  exports.matchesQueryObject = matchesQueryObject;
9267
+ exports.numericResultColumns = require_field_sidebar.numericResultColumns;
9653
9268
  exports.parseQuery = require_field_sidebar.parseQuery;
9654
9269
  exports.parseQueryWithDiagnostics = require_field_sidebar.parseQueryWithDiagnostics;
9655
- exports.presetTheme = presetTheme;
9270
+ exports.presetTheme = require_field_sidebar.presetTheme;
9656
9271
  exports.queryFields = require_field_sidebar.queryFields;
9657
9272
  exports.quoteValue = require_field_sidebar.quoteValue;
9658
- exports.readStoredTheme = readStoredTheme;
9273
+ exports.readStoredTheme = require_field_sidebar.readStoredTheme;
9659
9274
  exports.removeClause = require_field_sidebar.removeClause;
9660
- exports.resolveDescriptor = resolveDescriptor;
9275
+ exports.replaceSqlPlaceholder = replaceSqlPlaceholder;
9276
+ exports.resolveDescriptor = require_field_sidebar.resolveDescriptor;
9661
9277
  exports.resolveFieldType = require_text_truncate.resolveFieldType;
9662
- exports.resolveThemeMode = resolveThemeMode;
9663
- exports.safeParseStoredSpec = safeParseStoredSpec;
9664
- exports.safeParseStoredTokens = safeParseStoredTokens;
9278
+ exports.resolveThemeMode = require_field_sidebar.resolveThemeMode;
9279
+ exports.safeParseStoredSpec = require_field_sidebar.safeParseStoredSpec;
9280
+ exports.safeParseStoredTokens = require_field_sidebar.safeParseStoredTokens;
9665
9281
  exports.schemaValidator = schemaValidator;
9666
- exports.serializeGeneratedTheme = serializeGeneratedTheme;
9667
- exports.serializeThemeRule = serializeThemeRule;
9668
- exports.serializeTokenBody = serializeTokenBody;
9282
+ exports.serializeGeneratedTheme = require_field_sidebar.serializeGeneratedTheme;
9283
+ exports.serializeThemeRule = require_field_sidebar.serializeThemeRule;
9284
+ exports.serializeTokenBody = require_field_sidebar.serializeTokenBody;
9669
9285
  exports.shouldSkipArrow = shouldSkipArrow;
9286
+ exports.splitSqlStatements = splitSqlStatements;
9287
+ exports.sqlPlaceholderNames = sqlPlaceholderNames;
9288
+ exports.sqlStatementAtOffset = sqlStatementAtOffset;
9289
+ exports.sqlUsesPlaceholder = sqlUsesPlaceholder;
9670
9290
  exports.toast = require_field_sidebar.toast;
9671
9291
  exports.useAutoRefresh = require_field_sidebar.useAutoRefresh;
9672
9292
  exports.useControlledState = require_text_truncate.useControlledState;
@@ -9674,9 +9294,9 @@ exports.useFieldValue = _reopt_ai_opt_ui_primitives.useFieldValue;
9674
9294
  exports.useFormContext = _reopt_ai_opt_ui_primitives.useFormContext;
9675
9295
  exports.useFormStore = _reopt_ai_opt_ui_primitives.useFormStore;
9676
9296
  exports.useInputId = require_text_truncate.useInputId;
9677
- exports.useOptTheme = useOptTheme;
9678
- exports.useOptThemeController = useOptThemeController;
9679
- exports.useOptionalOptTheme = useOptionalOptTheme;
9297
+ exports.useOptTheme = require_field_sidebar.useOptTheme;
9298
+ exports.useOptThemeController = require_field_sidebar.useOptThemeController;
9299
+ exports.useOptionalOptTheme = require_field_sidebar.useOptionalOptTheme;
9680
9300
  exports.useSidebar = useSidebar;
9681
9301
  exports.useToast = require_field_sidebar.useToast;
9682
9302
  exports.withFieldValue = require_field_sidebar.withFieldValue;