@reopt-ai/opt-ui 1.10.0 → 1.12.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 (73) hide show
  1. package/COMPONENT_CATALOG.md +522 -218
  2. package/README.md +61 -7
  3. package/dist/core/index.cjs +10 -8
  4. package/dist/core/index.d.cts +3 -3
  5. package/dist/core/index.d.ts +3 -3
  6. package/dist/core/index.js +3 -3
  7. package/dist/docs/02-components/01-core.md +213 -96
  8. package/dist/docs/02-components/02-visuals.md +1 -1
  9. package/dist/docs/02-components/03-shells.md +334 -123
  10. package/dist/docs/02-components/04-surfaces.md +1 -1
  11. package/dist/docs/02-components/index.md +5 -5
  12. package/dist/docs/03-recipes/02-dashboards.md +126 -10
  13. package/dist/{field-sidebar-B6Ndr8HE.cjs → field-sidebar-BYgEmdy0.cjs} +2128 -1558
  14. package/dist/{field-sidebar-BXjvV7Cz.js → field-sidebar-BuO37l4c.js} +2111 -1561
  15. package/dist/{field-sidebar-nwcE9LC4.d.cts → field-sidebar-CrYlRZew.d.ts} +375 -154
  16. package/dist/{field-sidebar-DWfJAJlx.d.ts → field-sidebar-uXmoyvd7.d.cts} +375 -154
  17. package/dist/id-registry.cjs +124 -12
  18. package/dist/id-registry.d.cts +1 -2
  19. package/dist/id-registry.d.ts +1 -2
  20. package/dist/id-registry.js +124 -14
  21. package/dist/id-registry.json +265 -31
  22. package/dist/index.cjs +1162 -253
  23. package/dist/index.d.cts +573 -60
  24. package/dist/index.d.ts +573 -60
  25. package/dist/index.js +1134 -242
  26. package/dist/{key-pad-menu-DfZbeXhG.d.cts → key-pad-menu-CwcY_Vps.d.cts} +151 -7
  27. package/dist/{key-pad-menu-BQunU20e.js → key-pad-menu-Di2aRGbM.js} +388 -461
  28. package/dist/{key-pad-menu-D5mQcS5v.cjs → key-pad-menu-SItoHPLu.cjs} +490 -609
  29. package/dist/{key-pad-menu-BsgJMjD4.d.ts → key-pad-menu-WBJShCJn.d.ts} +152 -8
  30. package/dist/meta.cjs +1304 -168
  31. package/dist/meta.d.cts +12 -36
  32. package/dist/meta.d.ts +12 -36
  33. package/dist/meta.js +1304 -170
  34. package/dist/query.d.cts +1 -2
  35. package/dist/query.d.ts +1 -2
  36. package/dist/query.js +0 -2
  37. package/dist/shells/index.cjs +4 -1
  38. package/dist/shells/index.d.cts +2 -2
  39. package/dist/shells/index.d.ts +2 -2
  40. package/dist/shells/index.js +2 -2
  41. package/dist/{text-truncate-BGr7bB_8.d.cts → text-truncate-CQyfUZvq.d.cts} +48 -5
  42. package/dist/{text-truncate-BGr7bB_8.d.ts → text-truncate-CQyfUZvq.d.ts} +48 -5
  43. package/dist/{text-truncate-j6X3z7Pv.js → text-truncate-DSuZECy0.js} +446 -94
  44. package/dist/{text-truncate-CxdzAnhS.cjs → text-truncate-I1XLWmRa.cjs} +522 -90
  45. package/dist/theme/server.d.cts +1 -2
  46. package/dist/theme/server.d.ts +1 -2
  47. package/dist/theme/server.js +0 -2
  48. package/dist/visuals/index.js +0 -1
  49. package/package.json +4 -3
  50. package/dist/field-sidebar-BXjvV7Cz.js.map +0 -1
  51. package/dist/field-sidebar-DWfJAJlx.d.ts.map +0 -1
  52. package/dist/field-sidebar-nwcE9LC4.d.cts.map +0 -1
  53. package/dist/id-registry.d.cts.map +0 -1
  54. package/dist/id-registry.d.ts.map +0 -1
  55. package/dist/id-registry.js.map +0 -1
  56. package/dist/index.d.cts.map +0 -1
  57. package/dist/index.d.ts.map +0 -1
  58. package/dist/index.js.map +0 -1
  59. package/dist/key-pad-menu-BQunU20e.js.map +0 -1
  60. package/dist/key-pad-menu-BsgJMjD4.d.ts.map +0 -1
  61. package/dist/key-pad-menu-DfZbeXhG.d.cts.map +0 -1
  62. package/dist/meta.d.cts.map +0 -1
  63. package/dist/meta.d.ts.map +0 -1
  64. package/dist/meta.js.map +0 -1
  65. package/dist/query.d.cts.map +0 -1
  66. package/dist/query.d.ts.map +0 -1
  67. package/dist/query.js.map +0 -1
  68. package/dist/text-truncate-BGr7bB_8.d.cts.map +0 -1
  69. package/dist/text-truncate-BGr7bB_8.d.ts.map +0 -1
  70. package/dist/text-truncate-j6X3z7Pv.js.map +0 -1
  71. package/dist/theme/server.d.cts.map +0 -1
  72. package/dist/theme/server.d.ts.map +0 -1
  73. package/dist/theme/server.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,17 +1,355 @@
1
1
  "use client";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const require_text_truncate = require("./text-truncate-CxdzAnhS.cjs");
4
- const require_key_pad_menu = require("./key-pad-menu-D5mQcS5v.cjs");
5
- const require_field_sidebar = require("./field-sidebar-B6Ndr8HE.cjs");
3
+ const require_text_truncate = require("./text-truncate-I1XLWmRa.cjs");
4
+ const require_key_pad_menu = require("./key-pad-menu-SItoHPLu.cjs");
5
+ const require_field_sidebar = require("./field-sidebar-BYgEmdy0.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");
9
10
  let lucide_react = require("lucide-react");
10
11
  let _reopt_ai_opt_palette = require("@reopt-ai/opt-palette");
11
12
  let _reopt_ai_opt_charts = require("@reopt-ai/opt-charts");
12
13
  let match_sorter = require("match-sorter");
13
14
  let next_navigation = require("next/navigation");
14
- let _reopt_ai_opt_ui_primitives = require("@reopt-ai/opt-ui-primitives");
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({ className: require_text_truncate.cn("flex items-start rounded-lg border", toneStyles[tone], sizeStyles$3[size], className) }, props), {}, {
83
+ "data-opt-id": "AT7TX",
84
+ "data-opt-slug": "callout.Callout",
85
+ "data-tone": tone,
86
+ children: [
87
+ glyph && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
88
+ className: require_text_truncate.cn("mt-0.5 shrink-0", iconTone[tone]),
89
+ children: glyph
90
+ }),
91
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
92
+ className: "min-w-0 flex-1",
93
+ children: [title && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
94
+ className: require_text_truncate.cn("font-medium", titleTone[tone]),
95
+ children: title
96
+ }), children && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
97
+ className: require_text_truncate.cn("text-text-secondary", title && "mt-1"),
98
+ children
99
+ })]
100
+ }),
101
+ action && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
102
+ className: "shrink-0 self-center",
103
+ children: action
104
+ })
105
+ ]
106
+ }));
107
+ }
108
+ //#endregion
109
+ //#region src/core/status-dot.tsx
110
+ const _excluded$1 = [
111
+ "tone",
112
+ "size",
113
+ "hint",
114
+ "className",
115
+ "children"
116
+ ];
117
+ const dotTone = {
118
+ neutral: "bg-text-tertiary",
119
+ muted: "bg-border-hover",
120
+ info: "bg-info",
121
+ success: "bg-success",
122
+ warning: "bg-warning",
123
+ danger: "bg-danger"
124
+ };
125
+ const textTone = {
126
+ neutral: "text-text-secondary",
127
+ muted: "text-text-tertiary",
128
+ info: "text-text-secondary",
129
+ success: "text-text-secondary",
130
+ warning: "text-text-secondary",
131
+ danger: "text-text-secondary"
132
+ };
133
+ const sizeStyles$2 = {
134
+ sm: "gap-1.5 text-[11px]",
135
+ md: "gap-2 text-xs"
136
+ };
137
+ const dotSize = {
138
+ sm: "size-1.5",
139
+ md: "size-2"
140
+ };
141
+ /**
142
+ * StatusDot — a state, said in a dot and a word.
143
+ *
144
+ * `Badge` is a chip: a filled pill that reads as a label somebody attached.
145
+ * A status is not attached, it is observed, and a column of seven filled pills
146
+ * is a column nobody scans. This is the other shape — a dot, the word, no
147
+ * chrome — which is what a dense table can afford to repeat on every row.
148
+ *
149
+ * The dot is decorative on purpose. It never carries meaning the text does not
150
+ * also carry, so the component cannot be used to say something in colour alone.
151
+ *
152
+ * @example
153
+ * ```tsx
154
+ * <StatusDot tone="warning" hint="7일 이상 수신되지 않음">비활성</StatusDot>
155
+ * ```
156
+ */
157
+ function StatusDot(_ref) {
158
+ let { tone = "neutral", size = "md", hint, className, children } = _ref, props = require_text_truncate._objectWithoutProperties(_ref, _excluded$1);
159
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({
160
+ className: require_text_truncate.cn("inline-flex items-center whitespace-nowrap", sizeStyles$2[size], textTone[tone], className),
161
+ title: hint
162
+ }, props), {}, {
163
+ "data-opt-id": "N51AT",
164
+ "data-opt-slug": "status-dot.StatusDot",
165
+ "data-tone": tone,
166
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
167
+ className: require_text_truncate.cn("shrink-0 rounded-full", dotSize[size], dotTone[tone]),
168
+ "aria-hidden": "true"
169
+ }), children]
170
+ }));
171
+ }
172
+ //#endregion
173
+ //#region src/core/copy-button.tsx
174
+ const _excluded = [
175
+ "value",
176
+ "variant",
177
+ "size",
178
+ "feedbackDuration",
179
+ "onCopy",
180
+ "labels",
181
+ "className",
182
+ "disabled",
183
+ "onClick"
184
+ ];
185
+ const defaultLabels$35 = {
186
+ copy: "복사",
187
+ copied: "복사됨",
188
+ failed: "복사할 수 없습니다"
189
+ };
190
+ const sizeStyles$1 = {
191
+ sm: "h-6 gap-1 text-[11px] [&>svg]:size-3",
192
+ md: "h-7 gap-1.5 text-xs [&>svg]:size-3.5"
193
+ };
194
+ const paddingStyles = {
195
+ icon: {
196
+ sm: "w-6 justify-center",
197
+ md: "w-7 justify-center"
198
+ },
199
+ text: {
200
+ sm: "px-1.5",
201
+ md: "px-2"
202
+ }
203
+ };
204
+ const stateIcon = {
205
+ idle: lucide_react.Copy,
206
+ copied: lucide_react.Check,
207
+ failed: lucide_react.X
208
+ };
209
+ const stateTone = {
210
+ idle: "text-text-tertiary hover:text-text-primary",
211
+ copied: "text-success-fg",
212
+ failed: "text-danger-fg"
213
+ };
214
+ /**
215
+ * CopyButton — put a value on the clipboard, and say whether it went.
216
+ *
217
+ * Copying an identifier is the most common thing a reader does with one: an
218
+ * event key, a property name, a request id. Every surface that shows one was
219
+ * writing this button again, and each copy re-decided what happens when the
220
+ * clipboard refuses — mostly by pretending it did not.
221
+ *
222
+ * The confirmed state is announced, not just drawn, so the answer reaches a
223
+ * reader who is not looking at the glyph.
224
+ *
225
+ * @example
226
+ * ```tsx
227
+ * <CopyButton value={event.name} labels={{ copy: "이벤트 키 복사" }} />
228
+ * ```
229
+ */
230
+ function CopyButton(_ref) {
231
+ let { value, variant = "icon", size = "md", feedbackDuration = 1500, onCopy, labels: customLabels, className, disabled, onClick } = _ref, props = require_text_truncate._objectWithoutProperties(_ref, _excluded);
232
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$35), customLabels);
233
+ const [state, setState] = (0, react.useState)("idle");
234
+ const timer = (0, react.useRef)(null);
235
+ (0, react.useEffect)(() => () => {
236
+ if (timer.current) clearTimeout(timer.current);
237
+ }, []);
238
+ const handleClick = (0, react.useCallback)(async (event) => {
239
+ onClick === null || onClick === void 0 || onClick(event);
240
+ if (event.defaultPrevented) return;
241
+ let ok = false;
242
+ try {
243
+ await navigator.clipboard.writeText(value);
244
+ ok = true;
245
+ } catch (_unused) {
246
+ ok = false;
247
+ }
248
+ setState(ok ? "copied" : "failed");
249
+ onCopy === null || onCopy === void 0 || onCopy(ok);
250
+ if (timer.current) clearTimeout(timer.current);
251
+ timer.current = setTimeout(() => setState("idle"), feedbackDuration);
252
+ }, [
253
+ feedbackDuration,
254
+ onClick,
255
+ onCopy,
256
+ value
257
+ ]);
258
+ const Icon = stateIcon[state];
259
+ const label = state === "copied" ? labels.copied : state === "failed" ? labels.failed : labels.copy;
260
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({
261
+ type: "button",
262
+ onClick: handleClick,
263
+ disabled,
264
+ "aria-label": variant === "icon" ? label : void 0,
265
+ 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)
266
+ }, props), {}, {
267
+ "data-opt-id": "E0C8T",
268
+ "data-opt-slug": "copy-button.CopyButton",
269
+ "data-copy-state": state,
270
+ children: [
271
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { "aria-hidden": "true" }),
272
+ variant === "text" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label }),
273
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
274
+ role: "status",
275
+ "aria-live": "polite",
276
+ className: "sr-only",
277
+ children: state === "idle" ? "" : label
278
+ })
279
+ ]
280
+ }));
281
+ }
282
+ //#endregion
283
+ //#region src/core/segmented-control.tsx
284
+ const sizeStyles = {
285
+ sm: "h-7 text-[11px] [&_svg]:size-3",
286
+ md: "h-8 text-xs [&_svg]:size-3.5"
287
+ };
288
+ const segmentPadding = {
289
+ sm: "gap-1 px-2",
290
+ md: "gap-1.5 px-2.5"
291
+ };
292
+ /**
293
+ * SegmentedControl — one of a few views, all of them visible.
294
+ *
295
+ * A `Select` hides the alternatives behind a click; a `ToggleGroup` presses
296
+ * buttons that may be on together. Neither is what "이벤트 | 속성" is: exactly
297
+ * one is true, the set is small enough to show, and the reader is choosing a
298
+ * view rather than filtering anything.
299
+ *
300
+ * Built on the radio-group primitive rather than on buttons, so the arrow keys,
301
+ * the group name and the checked state come from the platform. A control that
302
+ * merely looks like a radiogroup is one a keyboard cannot drive.
303
+ *
304
+ * @example
305
+ * ```tsx
306
+ * <SegmentedControl
307
+ * aria-label="카탈로그 탭"
308
+ * options={[
309
+ * { value: "events", label: "이벤트", badge: 128 },
310
+ * { value: "properties", label: "속성", badge: 341 },
311
+ * ]}
312
+ * value={tab}
313
+ * onChange={setTab}
314
+ * />
315
+ * ```
316
+ */
317
+ function SegmentedControl({ options, value, onChange, size = "md", fullWidth = false, disabled = false, "aria-label": ariaLabel, className }) {
318
+ const name = (0, react.useId)();
319
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.RadioGroupRoot, {
320
+ value,
321
+ onValueChange: (next) => onChange(next),
322
+ disabled,
323
+ orientation: "horizontal",
324
+ name,
325
+ "aria-label": ariaLabel,
326
+ 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),
327
+ "data-opt-id": "YKENE",
328
+ "data-opt-slug": "segmented-control.SegmentedControl",
329
+ children: options.map((option) => {
330
+ const selected = option.value === value;
331
+ const itemDisabled = disabled || option.disabled;
332
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
333
+ 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"),
334
+ "data-selected": selected || void 0,
335
+ children: [
336
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.Radio, {
337
+ value: option.value,
338
+ disabled: itemDisabled,
339
+ className: "sr-only"
340
+ }),
341
+ option.icon,
342
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: option.label }),
343
+ option.badge != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
344
+ className: require_text_truncate.cn("tabular-nums", selected ? "text-text-tertiary" : "text-text-tertiary/70"),
345
+ children: option.badge
346
+ })
347
+ ]
348
+ }, option.value);
349
+ })
350
+ });
351
+ }
352
+ //#endregion
15
353
  //#region src/lib/schema-validator.ts
16
354
  /** Handles schema validator. */
17
355
  function schemaValidator(schema) {
@@ -86,7 +424,7 @@ function PageAside({ header, children, footer, widthClassName = "w-60", label, h
86
424
  PageAside.displayName = "PageAside";
87
425
  //#endregion
88
426
  //#region src/shells/task-list.tsx
89
- const defaultLabels$31 = {
427
+ const defaultLabels$34 = {
90
428
  title: "작업 목록",
91
429
  emptyMessage: "작업이 없습니다",
92
430
  completedOf: "{completed}/{total} 완료",
@@ -106,9 +444,9 @@ const defaultLabels$31 = {
106
444
  };
107
445
  /** Renders the task list component. */
108
446
  function TaskList({ tasks, title, onTaskClick, onTaskStatusChange, emptyMessage, showSubtasks = false, maxItems, labels: customLabels, className }) {
109
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$31), customLabels), {}, {
110
- priority: require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$31.priority), customLabels === null || customLabels === void 0 ? void 0 : customLabels.priority),
111
- status: require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$31.status), customLabels === null || customLabels === void 0 ? void 0 : customLabels.status)
447
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$34), customLabels), {}, {
448
+ priority: require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$34.priority), customLabels === null || customLabels === void 0 ? void 0 : customLabels.priority),
449
+ status: require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$34.status), customLabels === null || customLabels === void 0 ? void 0 : customLabels.status)
112
450
  });
113
451
  const displayTitle = title !== null && title !== void 0 ? title : labels.title;
114
452
  const displayEmptyMessage = emptyMessage !== null && emptyMessage !== void 0 ? emptyMessage : labels.emptyMessage;
@@ -266,7 +604,7 @@ function TaskList({ tasks, title, onTaskClick, onTaskStatusChange, emptyMessage,
266
604
  }
267
605
  //#endregion
268
606
  //#region src/shells/activity-feed.tsx
269
- const defaultLabels$30 = {
607
+ const defaultLabels$33 = {
270
608
  title: "활동 피드",
271
609
  emptyMessage: "활동이 없습니다",
272
610
  moreItems: "{count}개 더 보기",
@@ -316,24 +654,24 @@ const variantBorder = {
316
654
  function ActivityFeed({ activities, title, maxItems, onActivityClick, onShowMore, emptyMessage, labels: customLabels, locale = "ko-KR", loading = false, layout = "list", renderActions, className }) {
317
655
  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;
318
656
  const mergedTimeAgo = {
319
- 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$30.timeAgo.justNow,
320
- 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$30.timeAgo.minutesAgo,
321
- 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$30.timeAgo.hoursAgo,
322
- 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$30.timeAgo.daysAgo
657
+ 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,
658
+ 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,
659
+ 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,
660
+ 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
323
661
  };
324
662
  const mergedActivityVerbs = {
325
- 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$30.activityVerbs.created,
326
- 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$30.activityVerbs.updated,
327
- 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$30.activityVerbs.completed,
328
- 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$30.activityVerbs.commented,
329
- 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$30.activityVerbs.assigned,
330
- 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$30.activityVerbs.deployed,
331
- 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$30.activityVerbs.merged
663
+ 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,
664
+ 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,
665
+ 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,
666
+ 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,
667
+ 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,
668
+ 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,
669
+ 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
332
670
  };
333
671
  const labels = {
334
- title: (_customLabels$title = customLabels === null || customLabels === void 0 ? void 0 : customLabels.title) !== null && _customLabels$title !== void 0 ? _customLabels$title : defaultLabels$30.title,
335
- emptyMessage: (_customLabels$emptyMe = customLabels === null || customLabels === void 0 ? void 0 : customLabels.emptyMessage) !== null && _customLabels$emptyMe !== void 0 ? _customLabels$emptyMe : defaultLabels$30.emptyMessage,
336
- moreItems: (_customLabels$moreIte = customLabels === null || customLabels === void 0 ? void 0 : customLabels.moreItems) !== null && _customLabels$moreIte !== void 0 ? _customLabels$moreIte : defaultLabels$30.moreItems,
672
+ title: (_customLabels$title = customLabels === null || customLabels === void 0 ? void 0 : customLabels.title) !== null && _customLabels$title !== void 0 ? _customLabels$title : defaultLabels$33.title,
673
+ emptyMessage: (_customLabels$emptyMe = customLabels === null || customLabels === void 0 ? void 0 : customLabels.emptyMessage) !== null && _customLabels$emptyMe !== void 0 ? _customLabels$emptyMe : defaultLabels$33.emptyMessage,
674
+ moreItems: (_customLabels$moreIte = customLabels === null || customLabels === void 0 ? void 0 : customLabels.moreItems) !== null && _customLabels$moreIte !== void 0 ? _customLabels$moreIte : defaultLabels$33.moreItems,
337
675
  timeAgo: mergedTimeAgo,
338
676
  activityVerbs: mergedActivityVerbs
339
677
  };
@@ -461,13 +799,13 @@ function ActivityFeed({ activities, title, maxItems, onActivityClick, onShowMore
461
799
  ActivityFeed.displayName = "ActivityFeed";
462
800
  //#endregion
463
801
  //#region src/shells/filter-bar.tsx
464
- const defaultLabels$29 = {
802
+ const defaultLabels$32 = {
465
803
  clearAll: "필터 초기화",
466
804
  searchPlaceholder: "검색..."
467
805
  };
468
806
  /** Renders the filter bar component. */
469
807
  function FilterBar({ filters, onFilterChange, onDateRangeChange, onClearAll, labels: customLabels, presetActions, className }) {
470
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$29), customLabels);
808
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$32), customLabels);
471
809
  const [searchValues, setSearchValues] = (0, react.useState)({});
472
810
  const [tagInputValues, setTagInputValues] = (0, react.useState)({});
473
811
  const [multiSelectOpen, setMultiSelectOpen] = (0, react.useState)({});
@@ -693,10 +1031,10 @@ function matchesShortcut(event, shortcut) {
693
1031
  if (requiresMeta && !event.metaKey) return false;
694
1032
  return event.key.toLowerCase() === key;
695
1033
  }
696
- const defaultLabels$28 = { title: "빠른 작업" };
1034
+ const defaultLabels$31 = { title: "빠른 작업" };
697
1035
  /** Renders the quick actions component. */
698
1036
  function QuickActions({ actions, title, layout = "grid", columns = 3, showDescriptionInGrid = false, bindShortcuts = false, labels: customLabels, className }) {
699
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$28), customLabels);
1037
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$31), customLabels);
700
1038
  const displayTitle = title !== null && title !== void 0 ? title : labels.title;
701
1039
  const titleId = (0, react.useId)();
702
1040
  (0, react.useEffect)(() => {
@@ -774,7 +1112,7 @@ function QuickActions({ actions, title, layout = "grid", columns = 3, showDescri
774
1112
  QuickActions.displayName = "QuickActions";
775
1113
  //#endregion
776
1114
  //#region src/shells/team-member-list.tsx
777
- const defaultLabels$27 = {
1115
+ const defaultLabels$30 = {
778
1116
  title: "팀원",
779
1117
  emptyMessage: "팀원이 없습니다",
780
1118
  onlineCount: "{online}/{total} 온라인",
@@ -795,7 +1133,7 @@ const statusDot = {
795
1133
  };
796
1134
  /** Renders the team member list component. */
797
1135
  function TeamMemberList({ members, title, layout = "list", maxDisplay, onMemberClick, onShowMore, emptyMessage, labels: customLabels, className }) {
798
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$27), customLabels), {}, { status: require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$27.status), customLabels === null || customLabels === void 0 ? void 0 : customLabels.status) });
1136
+ 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) });
799
1137
  const displayTitle = title !== null && title !== void 0 ? title : labels.title;
800
1138
  const displayEmptyMessage = emptyMessage !== null && emptyMessage !== void 0 ? emptyMessage : labels.emptyMessage;
801
1139
  const titleId = (0, react.useId)();
@@ -1695,7 +2033,7 @@ const COLOR_PRESETS = [
1695
2033
  "#f97316",
1696
2034
  "#14b8a6"
1697
2035
  ];
1698
- const defaultLabels$26 = {
2036
+ const defaultLabels$29 = {
1699
2037
  eventLabelPrefix: "Event",
1700
2038
  defaultAriaLabel: "Event icon"
1701
2039
  };
@@ -1726,7 +2064,7 @@ function getEventIconStyle(color) {
1726
2064
  /** Renders the event icon component. */
1727
2065
  function EventIcon({ icon, color, size = "md", className, labels: customLabels }) {
1728
2066
  var _iconEntry$label;
1729
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$26), customLabels);
2067
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$29), customLabels);
1730
2068
  const sizes = SIZE_MAP[size];
1731
2069
  const resolvedColor = (color === null || color === void 0 ? void 0 : color.trim()) ? color : void 0;
1732
2070
  const iconEntry = typeof icon === "string" ? require_text_truncate.getIconRegistryEntry(icon) : void 0;
@@ -1818,14 +2156,14 @@ function ConnectionIndicator({ status, tone, label, showLabel = true, pulse, cla
1818
2156
  ConnectionIndicator.displayName = "ConnectionIndicator";
1819
2157
  //#endregion
1820
2158
  //#region src/shells/floating-action-bar.tsx
1821
- const defaultLabels$25 = {
2159
+ const defaultLabels$28 = {
1822
2160
  toolbarAriaLabel: "Actions",
1823
2161
  selectedLabel: (count) => `${count} selected`,
1824
2162
  dismissAriaLabel: "Dismiss"
1825
2163
  };
1826
2164
  /** Renders the floating action bar component. */
1827
2165
  function FloatingActionBar({ actions, selectedCount, onAction, onDismiss, className, labels: customLabels }) {
1828
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$25), customLabels);
2166
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$28), customLabels);
1829
2167
  if (actions.length === 0) return null;
1830
2168
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1831
2169
  role: "toolbar",
@@ -1862,7 +2200,7 @@ function FloatingActionBar({ actions, selectedCount, onAction, onDismiss, classN
1862
2200
  FloatingActionBar.displayName = "FloatingActionBar";
1863
2201
  //#endregion
1864
2202
  //#region src/shells/template-picker.tsx
1865
- const defaultLabels$24 = {
2203
+ const defaultLabels$27 = {
1866
2204
  ariaLabel: "Templates",
1867
2205
  defaultCategory: "General",
1868
2206
  searchPlaceholder: "Search templates...",
@@ -1870,7 +2208,7 @@ const defaultLabels$24 = {
1870
2208
  };
1871
2209
  /** Renders the template picker component. */
1872
2210
  function TemplatePicker({ templates, selected, onSelect, labels: customLabels, showSearch = false, className }) {
1873
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$24), customLabels);
2211
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$27), customLabels);
1874
2212
  const [search, setSearch] = react.useState("");
1875
2213
  const filtered = react.useMemo(() => {
1876
2214
  if (!showSearch || !search) return templates;
@@ -1956,7 +2294,7 @@ function TemplatePicker({ templates, selected, onSelect, labels: customLabels, s
1956
2294
  TemplatePicker.displayName = "TemplatePicker";
1957
2295
  //#endregion
1958
2296
  //#region src/shells/code-snippet-viewer.tsx
1959
- const defaultLabels$23 = {
2297
+ const defaultLabels$26 = {
1960
2298
  copy: "Copy",
1961
2299
  copied: "Copied!",
1962
2300
  codeRegion: "Code snippet"
@@ -1994,7 +2332,7 @@ const syntaxThemeClasses = {
1994
2332
  /** Renders the code snippet viewer component. */
1995
2333
  function CodeSnippetViewer({ code, language = "text", title, showLineNumbers = true, showCopyButton = true, maxHeight = 400, syntaxTheme = "default", className, labels: customLabels }) {
1996
2334
  var _syntaxThemeClasses$s;
1997
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$23), customLabels);
2335
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$26), customLabels);
1998
2336
  const [copied, setCopied] = react.useState(false);
1999
2337
  const lines = code.split("\n");
2000
2338
  const theme = (_syntaxThemeClasses$s = syntaxThemeClasses[syntaxTheme]) !== null && _syntaxThemeClasses$s !== void 0 ? _syntaxThemeClasses$s : syntaxThemeClasses.default;
@@ -2046,13 +2384,15 @@ function CodeSnippetViewer({ code, language = "text", title, showLineNumbers = t
2046
2384
  CodeSnippetViewer.displayName = "CodeSnippetViewer";
2047
2385
  //#endregion
2048
2386
  //#region src/shells/property-explorer.tsx
2049
- const defaultLabels$22 = {
2387
+ const defaultLabels$25 = {
2050
2388
  searchPlaceholder: "Search properties...",
2051
- noPropertiesFound: "No properties found"
2389
+ noPropertiesFound: "No properties found",
2390
+ edit: (property) => `${property} 편집`,
2391
+ hidden: "숨김"
2052
2392
  };
2053
2393
  /** Renders the property explorer component. */
2054
- function PropertyExplorer({ groups, onSelect, className, labels: customLabels }) {
2055
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$22), customLabels);
2394
+ function PropertyExplorer({ groups, onSelect, onEdit, className, labels: customLabels }) {
2395
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$25), customLabels);
2056
2396
  const [search, setSearch] = react.useState("");
2057
2397
  const filtered = react.useMemo(() => {
2058
2398
  if (!search) return groups;
@@ -2078,36 +2418,55 @@ function PropertyExplorer({ groups, onSelect, className, labels: customLabels })
2078
2418
  children: group.name
2079
2419
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2080
2420
  className: "gap-element flex flex-col",
2081
- children: group.properties.map((prop) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2082
- onClick: () => onSelect === null || onSelect === void 0 ? void 0 : onSelect(prop),
2083
- className: require_text_truncate.cn("flex w-full items-center justify-between rounded-md px-2.5 py-1.5 text-left", "hover:bg-bg-subtle transition-colors"),
2084
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2085
- className: "min-w-0",
2086
- children: [
2087
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.FieldToken, {
2088
- type: require_text_truncate.resolveFieldType(prop.type),
2089
- size: "xs",
2090
- title: prop.type,
2091
- className: "mr-2 align-[-2px]"
2092
- }),
2093
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2094
- className: "text-text-primary text-sm font-medium",
2095
- children: prop.name
2096
- }),
2097
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2098
- className: "text-text-tertiary ml-2 font-mono text-xs",
2099
- children: prop.type
2100
- }),
2101
- prop.description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2102
- className: "text-text-tertiary truncate text-xs",
2103
- children: prop.description
2421
+ children: group.properties.map((prop) => {
2422
+ var _prop$dataType;
2423
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2424
+ className: require_text_truncate.cn("hover:bg-bg-subtle flex items-center rounded-md transition-colors", prop.hidden && "opacity-60"),
2425
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2426
+ onClick: () => onSelect === null || onSelect === void 0 ? void 0 : onSelect(prop),
2427
+ className: "flex min-w-0 flex-1 items-center justify-between px-2.5 py-1.5 text-left",
2428
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2429
+ className: "min-w-0",
2430
+ children: [
2431
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.FieldToken, {
2432
+ type: (_prop$dataType = prop.dataType) !== null && _prop$dataType !== void 0 ? _prop$dataType : require_text_truncate.resolveFieldType(prop.type),
2433
+ size: "xs",
2434
+ title: prop.type,
2435
+ className: "mr-2 align-[-2px]"
2436
+ }),
2437
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2438
+ className: "text-text-primary text-sm font-medium",
2439
+ children: prop.name
2440
+ }),
2441
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2442
+ className: "text-text-tertiary ml-2 font-mono text-xs",
2443
+ children: prop.type
2444
+ }),
2445
+ prop.hidden && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2446
+ className: "text-text-tertiary ml-2 text-xs",
2447
+ children: labels.hidden
2448
+ }),
2449
+ prop.description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2450
+ className: "text-text-tertiary truncate text-xs",
2451
+ children: prop.description
2452
+ })
2453
+ ]
2454
+ }), prop.eventCount !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2455
+ className: "text-text-tertiary ml-2 shrink-0 text-xs",
2456
+ children: require_text_truncate.formatOptNumber(prop.eventCount)
2457
+ })]
2458
+ }), onEdit && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2459
+ type: "button",
2460
+ onClick: () => onEdit(prop),
2461
+ "aria-label": labels.edit(prop.name),
2462
+ className: "text-text-tertiary hover:text-text-primary mr-1.5 shrink-0 rounded p-1",
2463
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Pencil, {
2464
+ "aria-hidden": "true",
2465
+ className: "size-3.5"
2104
2466
  })
2105
- ]
2106
- }), prop.eventCount !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2107
- className: "text-text-tertiary ml-2 shrink-0 text-xs",
2108
- children: require_key_pad_menu.formatOptNumber(prop.eventCount)
2109
- })]
2110
- }, prop.name))
2467
+ })]
2468
+ }, prop.name);
2469
+ })
2111
2470
  })] }, group.name)), filtered.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2112
2471
  className: "text-text-tertiary py-4 text-center text-sm",
2113
2472
  children: labels.noPropertiesFound
@@ -2118,14 +2477,14 @@ function PropertyExplorer({ groups, onSelect, className, labels: customLabels })
2118
2477
  PropertyExplorer.displayName = "PropertyExplorer";
2119
2478
  //#endregion
2120
2479
  //#region src/shells/query-results-table.tsx
2121
- const defaultLabels$21 = {
2480
+ const defaultLabels$24 = {
2122
2481
  emptyMessage: "No results",
2123
2482
  loadingMessage: "Running query...",
2124
2483
  rowsSuffix: "row(s)"
2125
2484
  };
2126
2485
  /** Renders the query results table component. */
2127
2486
  function QueryResultsTable({ columns, rows, maxHeight = 400, emptyMessage, loading = false, error, labels: customLabels, className }) {
2128
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$21), customLabels);
2487
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$24), customLabels);
2129
2488
  if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2130
2489
  className: require_text_truncate.cn("border-danger bg-danger-subtle rounded-lg border p-4", className),
2131
2490
  role: "alert",
@@ -2209,7 +2568,7 @@ function QueryResultsTable({ columns, rows, maxHeight = 400, emptyMessage, loadi
2209
2568
  QueryResultsTable.displayName = "QueryResultsTable";
2210
2569
  //#endregion
2211
2570
  //#region src/shells/alert-builder.tsx
2212
- const defaultLabels$20 = {
2571
+ const defaultLabels$23 = {
2213
2572
  alertNameLabel: "Alert name",
2214
2573
  conditionsLabel: "Conditions",
2215
2574
  metricLabel: "Metric",
@@ -2280,7 +2639,7 @@ function AlertBuilder({ value, onChange, metrics = [
2280
2639
  ], labels: customLabels, className }) {
2281
2640
  const resolvedMetrics = eventOptions ? eventOptions.map((e) => e.id) : metrics;
2282
2641
  const metricLabelMap = eventOptions ? Object.fromEntries(eventOptions.map((e) => [e.id, e.name])) : null;
2283
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$20), customLabels);
2642
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$23), customLabels);
2284
2643
  const alertNameId = react.useId();
2285
2644
  const conditionsId = react.useId();
2286
2645
  const checkFrequencyId = react.useId();
@@ -2435,7 +2794,7 @@ function AlertBuilder({ value, onChange, metrics = [
2435
2794
  AlertBuilder.displayName = "AlertBuilder";
2436
2795
  //#endregion
2437
2796
  //#region src/shells/sql-editor.tsx
2438
- const defaultLabels$19 = {
2797
+ const defaultLabels$22 = {
2439
2798
  placeholder: "SELECT * FROM events LIMIT 100",
2440
2799
  runHint: "Ctrl+Enter to run",
2441
2800
  runButton: "Run Query",
@@ -2443,7 +2802,7 @@ const defaultLabels$19 = {
2443
2802
  };
2444
2803
  /** Renders the sql editor component. */
2445
2804
  function SqlEditor({ value, onChange, onRun, onRunAndAdvance, placeholder, readOnly = false, height = 200, schema, completionSources, keymap: customKeymap, extensions: customExtensions, ariaLabel, autoFocus = false, showFooter, labels: customLabels, className }) {
2446
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$19), customLabels);
2805
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$22), customLabels);
2447
2806
  const fill = height === "fill";
2448
2807
  const footerVisible = showFooter !== null && showFooter !== void 0 ? showFooter : Boolean(onRun);
2449
2808
  const editorRef = react.useRef(null);
@@ -2615,7 +2974,7 @@ function SqlEditor({ value, onChange, onRun, onRunAndAdvance, placeholder, readO
2615
2974
  SqlEditor.displayName = "SqlEditor";
2616
2975
  //#endregion
2617
2976
  //#region src/shells/event-meta-editor.tsx
2618
- const defaultLabels$18 = {
2977
+ const defaultLabels$21 = {
2619
2978
  icon: "Icon",
2620
2979
  color: "Color",
2621
2980
  name: "Event name",
@@ -2629,8 +2988,8 @@ const defaultLabels$18 = {
2629
2988
  emptyTagsMessage: "No tags added yet."
2630
2989
  };
2631
2990
  /** Renders the event meta editor component. */
2632
- function EventMetaEditor({ value, onChange, icons, className, labels: customLabels }) {
2633
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$18), customLabels);
2991
+ function EventMetaEditor({ value, onChange, icons, sections, footer, className, labels: customLabels }) {
2992
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$21), customLabels);
2634
2993
  const hasTags = value.tags.length > 0;
2635
2994
  const nameId = react.useId();
2636
2995
  const displayNameId = react.useId();
@@ -2723,17 +3082,32 @@ function EventMetaEditor({ value, onChange, icons, className, labels: customLabe
2723
3082
  className: "text-text-tertiary mt-1 block text-xs",
2724
3083
  children: labels.emptyTagsMessage
2725
3084
  })
2726
- ] })
3085
+ ] }),
3086
+ sections === null || sections === void 0 ? void 0 : sections.map((section) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3087
+ className: "gap-element flex flex-col",
3088
+ children: [
3089
+ section.title && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3090
+ className: "text-text-tertiary block text-xs font-medium",
3091
+ children: section.title
3092
+ }),
3093
+ section.children,
3094
+ section.description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3095
+ className: "text-text-tertiary block text-xs",
3096
+ children: section.description
3097
+ })
3098
+ ]
3099
+ }, section.id)),
3100
+ footer
2727
3101
  ]
2728
3102
  });
2729
3103
  }
2730
3104
  EventMetaEditor.displayName = "EventMetaEditor";
2731
3105
  //#endregion
2732
3106
  //#region src/shells/widget-grid.tsx
2733
- const defaultLabels$17 = { emptyMessage: "No widgets to display" };
3107
+ const defaultLabels$20 = { emptyMessage: "No widgets to display" };
2734
3108
  /** Renders the widget grid component. */
2735
3109
  function WidgetGrid({ widgets, cols = 12, rowHeight = 80, gap = 16, editable = false, onReorder, labels: customLabels, className }) {
2736
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$17), customLabels);
3110
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$20), customLabels);
2737
3111
  const [dragIdx, setDragIdx] = react.useState(null);
2738
3112
  const [overIdx, setOverIdx] = react.useState(null);
2739
3113
  if (widgets.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -2778,7 +3152,7 @@ function WidgetGrid({ widgets, cols = 12, rowHeight = 80, gap = 16, editable = f
2778
3152
  WidgetGrid.displayName = "WidgetGrid";
2779
3153
  //#endregion
2780
3154
  //#region src/shells/report-builder.tsx
2781
- const defaultLabels$16 = {
3155
+ const defaultLabels$19 = {
2782
3156
  stepsLabel: "Report steps",
2783
3157
  stepType: "Chart Type",
2784
3158
  stepConfig: "Data Config",
@@ -2854,7 +3228,7 @@ const STEPS = [
2854
3228
  ];
2855
3229
  /** Renders the report builder component. */
2856
3230
  function ReportBuilder({ chartTypes = DEFAULT_CHART_TYPES, dataSources = [], onSave, onCancel, configForm, styleForm, preview, onChartTypeChange, onDataSourceChange, onStepChange, selectedChartType: controlledChartType, selectedDataSource: controlledDataSource, labels: customLabels, className }) {
2857
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$16), customLabels);
3231
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$19), customLabels);
2858
3232
  const [stepIndex, setStepIndex] = react.useState(0);
2859
3233
  const [internalChartType, setInternalChartType] = react.useState(null);
2860
3234
  const [internalDataSource, setInternalDataSource] = react.useState("");
@@ -3074,7 +3448,7 @@ function ReportBuilder({ chartTypes = DEFAULT_CHART_TYPES, dataSources = [], onS
3074
3448
  ReportBuilder.displayName = "ReportBuilder";
3075
3449
  //#endregion
3076
3450
  //#region src/shells/audit-timeline.tsx
3077
- const defaultLabels$15 = {
3451
+ const defaultLabels$18 = {
3078
3452
  title: "변경 이력",
3079
3453
  empty: "변경 이력이 없습니다",
3080
3454
  expand: "상세 보기",
@@ -3153,7 +3527,7 @@ function DiffTable({ changes, labels }) {
3153
3527
  }
3154
3528
  /** Renders the audit timeline component. */
3155
3529
  function AuditTimeline({ entries, labels: customLabels, className, onEntryClick }) {
3156
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$15), customLabels);
3530
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$18), customLabels);
3157
3531
  const [expandedIds, setExpandedIds] = react.useState(/* @__PURE__ */ new Set());
3158
3532
  const toggleExpanded = react.useCallback((id) => {
3159
3533
  setExpandedIds((prev) => {
@@ -3276,7 +3650,7 @@ function AuditTimeline({ entries, labels: customLabels, className, onEntryClick
3276
3650
  AuditTimeline.displayName = "AuditTimeline";
3277
3651
  //#endregion
3278
3652
  //#region src/shells/segment-builder.tsx
3279
- const defaultLabels$14 = {
3653
+ const defaultLabels$17 = {
3280
3654
  match: "Match",
3281
3655
  ofTheFollowing: "of the following",
3282
3656
  groupLabel: "Segment group",
@@ -3397,7 +3771,7 @@ function SegmentBuilder({ groups, onChange, eventOptions = [
3397
3771
  "New Users",
3398
3772
  "Power Users"
3399
3773
  ], className, labels: customLabels }) {
3400
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$14), customLabels);
3774
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$17), customLabels);
3401
3775
  const builderId = react.useId().replace(/:/g, "");
3402
3776
  const nextIdRef = react.useRef(1);
3403
3777
  const createId = react.useCallback(() => {
@@ -3610,7 +3984,7 @@ function SegmentBuilder({ groups, onChange, eventOptions = [
3610
3984
  SegmentBuilder.displayName = "SegmentBuilder";
3611
3985
  //#endregion
3612
3986
  //#region src/shells/insights-panel.tsx
3613
- const defaultLabels$13 = {
3987
+ const defaultLabels$16 = {
3614
3988
  title: "Insights",
3615
3989
  dismiss: "Dismiss",
3616
3990
  showDismissed: "Show dismissed",
@@ -3641,7 +4015,7 @@ const severityConfig = {
3641
4015
  /** Renders the insights panel component. */
3642
4016
  function InsightsPanel({ insights, stats, detectionTypes, onDetectionTypeToggle, detectionConfig, onDetectionConfigChange, onDismiss, onInsightClick, onStatClick, header, actions, labels: customLabels, className }) {
3643
4017
  var _detectionConfig$days, _detectionConfig$zSco;
3644
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$13), customLabels);
4018
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$16), customLabels);
3645
4019
  const [showDismissed, setShowDismissed] = react.useState(false);
3646
4020
  const visibleInsights = showDismissed ? insights : insights.filter((i) => !i.dismissed);
3647
4021
  const hasStats = stats && stats.length > 0;
@@ -4033,7 +4407,7 @@ function ExportButton({ data, columns, fileName = "export", formats = ["csv", "j
4033
4407
  ExportButton.displayName = "ExportButton";
4034
4408
  //#endregion
4035
4409
  //#region src/shells/summary-row.tsx
4036
- const defaultLabels$12 = {
4410
+ const defaultLabels$15 = {
4037
4411
  emptyTitle: "No summary stats",
4038
4412
  emptyDescription: "Provide at least one stat to render the summary row."
4039
4413
  };
@@ -4051,20 +4425,22 @@ function formatByStatType(stat, durationFractionDigits, percentageFractionDigits
4051
4425
  const colsClass = {
4052
4426
  2: "grid-cols-1 sm:grid-cols-2",
4053
4427
  3: "grid-cols-1 sm:grid-cols-3",
4054
- 4: "grid-cols-2 sm:grid-cols-4"
4428
+ 4: "grid-cols-2 sm:grid-cols-4",
4429
+ 5: "grid-cols-2 sm:grid-cols-3 xl:grid-cols-5",
4430
+ 6: "grid-cols-2 sm:grid-cols-3 xl:grid-cols-6"
4055
4431
  };
4056
4432
  /** Renders the summary row component. */
4057
- function SummaryRow({ stats, columns = 4, onStatClick, formatValue, durationFractionDigits = 1, percentageFractionDigits = 1, loading = false, labels: customLabels, className }) {
4058
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$12), customLabels);
4433
+ function SummaryRow({ stats, columns = 4, onStatClick, selectedStatId, size = "md", formatValue, durationFractionDigits = 1, percentageFractionDigits = 1, loading = false, labels: customLabels, className }) {
4434
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$15), customLabels);
4059
4435
  if (loading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4060
4436
  className: require_text_truncate.cn("gap-group grid", colsClass[columns], className),
4061
- "data-opt-id": "DA0AX",
4437
+ "data-opt-id": "FGFP3",
4062
4438
  "data-opt-slug": "summary-row.SummaryRow",
4063
- children: Array.from({ length: columns }).map((_, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_key_pad_menu.SkeletonCard, {}, i))
4439
+ children: Array.from({ length: stats.length || columns }).map((_, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.SkeletonCard, {}, i))
4064
4440
  });
4065
4441
  if (stats.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4066
4442
  className: require_text_truncate.cn("flex min-h-40 items-center justify-center", className),
4067
- "data-opt-id": "DA0AX",
4443
+ "data-opt-id": "FGFP3",
4068
4444
  "data-opt-slug": "summary-row.SummaryRow",
4069
4445
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.EmptyState, {
4070
4446
  title: labels.emptyTitle,
@@ -4073,29 +4449,527 @@ function SummaryRow({ stats, columns = 4, onStatClick, formatValue, durationFrac
4073
4449
  });
4074
4450
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4075
4451
  className: require_text_truncate.cn("gap-group grid", colsClass[columns], className),
4076
- "data-opt-id": "DA0AX",
4452
+ "data-opt-id": "FGFP3",
4077
4453
  "data-opt-slug": "summary-row.SummaryRow",
4078
4454
  children: stats.map((s) => {
4079
4455
  const displayValue = formatValue ? formatValue(s) : formatByStatType(s, durationFractionDigits, percentageFractionDigits);
4080
- const card = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.StatCard, {
4456
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.StatCard, {
4081
4457
  id: s.id,
4082
4458
  title: s.title,
4083
4459
  value: displayValue,
4084
4460
  change: s.change,
4085
- trend: s.trend
4461
+ trend: s.trend,
4462
+ polarity: s.polarity,
4463
+ description: s.description,
4464
+ hint: s.hint,
4465
+ size,
4466
+ selected: selectedStatId === s.id,
4467
+ onSelect: onStatClick ? () => onStatClick(s) : void 0
4086
4468
  }, s.id);
4087
- if (onStatClick) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4088
- type: "button",
4089
- onClick: () => onStatClick(s),
4090
- className: "rounded-lg text-left transition-transform hover:scale-[1.02] focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2",
4091
- children: card
4092
- }, s.id);
4093
- return card;
4094
4469
  })
4095
4470
  });
4096
4471
  }
4097
4472
  SummaryRow.displayName = "SummaryRow";
4098
4473
  //#endregion
4474
+ //#region src/lib/query/query-match.ts
4475
+ function toText(value) {
4476
+ if (value === null || value === void 0) return "";
4477
+ if (typeof value === "string") return value;
4478
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
4479
+ return JSON.stringify(value);
4480
+ }
4481
+ function compare(left, right) {
4482
+ const leftNumber = typeof left === "number" ? left : Number(toText(left));
4483
+ const rightNumber = Number(right);
4484
+ if (Number.isFinite(leftNumber) && Number.isFinite(rightNumber)) return leftNumber - rightNumber;
4485
+ return toText(left).localeCompare(right);
4486
+ }
4487
+ function matchesClause(clause, read, searchable) {
4488
+ if (clause.type === "term") return searchable().toLowerCase().includes(clause.value.toLowerCase());
4489
+ const value = read(clause.field);
4490
+ switch (clause.operator) {
4491
+ case "exists": return value !== void 0 && value !== null && value !== "";
4492
+ case "eq": return clause.values.some((candidate) => toText(value).toLowerCase() === candidate.toLowerCase());
4493
+ case "contains": return clause.values.some((candidate) => toText(value).toLowerCase().includes(candidate.toLowerCase()));
4494
+ case "gt":
4495
+ var _clause$values$;
4496
+ return compare(value, (_clause$values$ = clause.values[0]) !== null && _clause$values$ !== void 0 ? _clause$values$ : "") > 0;
4497
+ case "gte":
4498
+ var _clause$values$2;
4499
+ return compare(value, (_clause$values$2 = clause.values[0]) !== null && _clause$values$2 !== void 0 ? _clause$values$2 : "") >= 0;
4500
+ case "lt":
4501
+ var _clause$values$3;
4502
+ return compare(value, (_clause$values$3 = clause.values[0]) !== null && _clause$values$3 !== void 0 ? _clause$values$3 : "") < 0;
4503
+ case "lte":
4504
+ var _clause$values$4;
4505
+ return compare(value, (_clause$values$4 = clause.values[0]) !== null && _clause$values$4 !== void 0 ? _clause$values$4 : "") <= 0;
4506
+ default: return true;
4507
+ }
4508
+ }
4509
+ /**
4510
+ * Whether one row satisfies every clause.
4511
+ *
4512
+ * `searchable` is lazy because most queries are field constraints, and
4513
+ * flattening a row to text for a term match nobody asked for is the expensive
4514
+ * part of filtering a live tail.
4515
+ */
4516
+ function matchesQuery(query, read, searchable) {
4517
+ return query.clauses.every((clause) => {
4518
+ const matched = matchesClause(clause, read, searchable);
4519
+ return clause.negated ? !matched : matched;
4520
+ });
4521
+ }
4522
+ /** Convenience for plain objects, including one level of nested properties. */
4523
+ function matchesQueryObject(query, row, options = {}) {
4524
+ const nested = options.nested ? row[options.nested] : void 0;
4525
+ const read = (field) => field in row ? row[field] : nested === null || nested === void 0 ? void 0 : nested[field];
4526
+ let cached = null;
4527
+ const searchable = () => {
4528
+ var _cached;
4529
+ (_cached = cached) !== null && _cached !== void 0 || (cached = [...Object.values(row).filter((value) => typeof value !== "object"), ...Object.values(nested !== null && nested !== void 0 ? nested : {})].map(toText).join(" "));
4530
+ return cached;
4531
+ };
4532
+ return matchesQuery(query, read, searchable);
4533
+ }
4534
+ //#endregion
4535
+ //#region src/lib/use-media-query.ts
4536
+ /**
4537
+ * Whether a media query matches, kept in sync with the browser.
4538
+ *
4539
+ * `useSyncExternalStore` rather than an effect: a layout that switches on width
4540
+ * must not render the wrong shape for a frame first. The server snapshot is the
4541
+ * caller's `serverFallback`, because the server does not know the viewport and
4542
+ * guessing "narrow" collapses a wide layout on every first paint.
4543
+ *
4544
+ * Internal. Exported from `lib` for the components in this package, not from
4545
+ * the package barrel — a hook that answers "is this viewport wide" invites
4546
+ * layout decisions that belong in CSS.
4547
+ */
4548
+ function useMediaQuery(query, serverFallback = false) {
4549
+ return (0, react.useSyncExternalStore)((onChange) => {
4550
+ if (typeof window === "undefined") return () => {};
4551
+ const list = window.matchMedia(query);
4552
+ list.addEventListener("change", onChange);
4553
+ return () => list.removeEventListener("change", onChange);
4554
+ }, () => typeof window === "undefined" ? serverFallback : window.matchMedia(query).matches, () => serverFallback);
4555
+ }
4556
+ //#endregion
4557
+ //#region src/shells/inspector-layout.tsx
4558
+ /** Tailwind needs whole class names, so the queries and widths are literals. */
4559
+ const breakpointQuery = {
4560
+ lg: "(min-width: 64rem)",
4561
+ xl: "(min-width: 80rem)",
4562
+ "2xl": "(min-width: 96rem)"
4563
+ };
4564
+ const pushWidth = {
4565
+ sm: "w-[22rem]",
4566
+ md: "w-[30rem]",
4567
+ lg: "w-[42rem]"
4568
+ };
4569
+ const flyoutSize = {
4570
+ sm: "sm",
4571
+ md: "md",
4572
+ lg: "lg"
4573
+ };
4574
+ /**
4575
+ * InspectorLayout — a list and the record it is showing, side by side.
4576
+ *
4577
+ * Reading a catalogue is a loop: look at the list, open one, decide, come back.
4578
+ * A dialog breaks that loop — it takes the list away, and coming back costs the
4579
+ * reader their scroll position and their place in the scan. This keeps both on
4580
+ * screen where there is room, and falls back to an overlay where there is not.
4581
+ *
4582
+ * Every surface that wanted this was writing `w-80 border-l` by hand and
4583
+ * re-deciding the two things that are easy to get wrong: which width switches
4584
+ * the layout, and whether the list stays mounted.
4585
+ *
4586
+ * Focus is deliberately **not** one of them. `Flyout` already remembers who
4587
+ * opened it and hands focus back on close, and its effect runs before this
4588
+ * one — a parent that also tried to capture `document.activeElement` would
4589
+ * read the panel the child had just focused and "restore" to a node that is
4590
+ * about to be removed. One owner, and it is the one closest to the panel.
4591
+ *
4592
+ * @example
4593
+ * ```tsx
4594
+ * <InspectorLayout
4595
+ * open={selected !== null}
4596
+ * onClose={() => setSelected(null)}
4597
+ * title={selected?.name}
4598
+ * inspector={<EventInspector name={selected?.name} />}
4599
+ * >
4600
+ * <DataTable … />
4601
+ * </InspectorLayout>
4602
+ * ```
4603
+ */
4604
+ function InspectorLayout({ children, inspector, open, onClose, title, description, actions, footer, breakpoint = "xl", width = "md", labels, className }) {
4605
+ const canPush = useMediaQuery(breakpointQuery[breakpoint], true);
4606
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4607
+ className: require_text_truncate.cn("flex min-w-0 items-stretch gap-group", className),
4608
+ "data-opt-id": "MN44T",
4609
+ "data-opt-slug": "inspector-layout.InspectorLayout",
4610
+ "data-inspector-mode": canPush ? "push" : "overlay",
4611
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4612
+ className: "min-w-0 flex-1",
4613
+ children
4614
+ }), canPush ? open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("aside", {
4615
+ className: require_text_truncate.cn("shrink-0", pushWidth[width]),
4616
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_field_sidebar.Flyout, {
4617
+ open,
4618
+ onClose,
4619
+ type: "inline",
4620
+ title,
4621
+ description,
4622
+ actions,
4623
+ footer,
4624
+ labels,
4625
+ className: "h-full",
4626
+ children: inspector
4627
+ })
4628
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_field_sidebar.Flyout, {
4629
+ open,
4630
+ onClose,
4631
+ type: "overlay",
4632
+ scrim: true,
4633
+ size: flyoutSize[width],
4634
+ title,
4635
+ description,
4636
+ actions,
4637
+ footer,
4638
+ labels,
4639
+ children: inspector
4640
+ })]
4641
+ });
4642
+ }
4643
+ //#endregion
4644
+ //#region src/shells/query-filter-bar.tsx
4645
+ const defaultLabels$14 = {
4646
+ placeholder: "검색하거나 필터를 입력하세요",
4647
+ ariaLabel: "검색",
4648
+ filtersAriaLabel: "필터",
4649
+ clearAll: "필터 지우기",
4650
+ activeCount: (count) => `${count}개 적용됨`
4651
+ };
4652
+ function toggleValue(query, field, value, active, multiSelect) {
4653
+ if (active) {
4654
+ const remaining = require_field_sidebar.fieldValues(query, field).filter((it) => it !== value);
4655
+ const cleared = require_field_sidebar.withoutField(query, field);
4656
+ return remaining.reduce((next, keep) => require_field_sidebar.withFieldValue(next, field, keep), cleared);
4657
+ }
4658
+ return multiSelect ? require_field_sidebar.withFieldValue(query, field, value) : require_field_sidebar.withFieldValue(require_field_sidebar.withoutField(query, field), field, value);
4659
+ }
4660
+ /**
4661
+ * QueryFilterBar — filter buttons and a query string that are the same thing.
4662
+ *
4663
+ * The failure this exists to prevent is two sources of truth. A search box
4664
+ * beside a row of filter buttons will drift: the buttons keep their own state,
4665
+ * the box keeps the text, and a link to the page carries one of them. Readers
4666
+ * find the two disagreeing and stop trusting either.
4667
+ *
4668
+ * So the buttons hold no state at all. Each one reads the query to decide
4669
+ * whether it is active and writes the query when clicked, which makes the
4670
+ * behaviour fall out for free: typing `status:archived` lights up the status
4671
+ * filter, clicking the filter puts `status:archived` in the box, and the URL
4672
+ * that carries the query carries the whole filter state with it.
4673
+ *
4674
+ * @example
4675
+ * ```tsx
4676
+ * <QueryFilterBar
4677
+ * value={query}
4678
+ * onChange={setQuery}
4679
+ * filters={[
4680
+ * { id: "conversion", label: "전환", kind: "is", field: "is", value: "conversion" },
4681
+ * { id: "status", label: "상태", kind: "field_value_selection", field: "status",
4682
+ * options: [{ value: "archived", label: "보관", count: 12 }] },
4683
+ * ]}
4684
+ * />
4685
+ * ```
4686
+ */
4687
+ function QueryFilterBar({ value, onChange, filters, fields, actions, showClauses = true, live = false, disabled = false, labels: customLabels, queryBarLabels, className }) {
4688
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$14), customLabels);
4689
+ const activeCount = (0, react.useMemo)(() => filters.reduce((count, filter) => {
4690
+ const values = require_field_sidebar.fieldValues(value, filter.field);
4691
+ if (filter.kind === "field_value_selection") return count + values.length;
4692
+ return count + (filter.value && values.includes(filter.value) ? 1 : 0);
4693
+ }, 0), [filters, value]);
4694
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4695
+ className: require_text_truncate.cn("flex flex-col gap-element", className),
4696
+ "data-opt-id": "SK31N",
4697
+ "data-opt-slug": "query-filter-bar.QueryFilterBar",
4698
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_field_sidebar.QueryBar, {
4699
+ value,
4700
+ onChange,
4701
+ fields,
4702
+ showClauses,
4703
+ live,
4704
+ disabled,
4705
+ labels: require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, queryBarLabels), {}, {
4706
+ placeholder: labels.placeholder,
4707
+ ariaLabel: labels.ariaLabel
4708
+ })
4709
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4710
+ className: "gap-element flex flex-wrap items-center",
4711
+ role: "group",
4712
+ "aria-label": labels.filtersAriaLabel,
4713
+ children: [
4714
+ filters.map((filter) => {
4715
+ var _filter$multiSelect, _filter$value;
4716
+ const values = require_field_sidebar.fieldValues(value, filter.field);
4717
+ const multiSelect = (_filter$multiSelect = filter.multiSelect) !== null && _filter$multiSelect !== void 0 ? _filter$multiSelect : true;
4718
+ if (filter.kind === "field_value_selection") {
4719
+ var _filter$options, _filter$options2;
4720
+ const selected = (_filter$options = filter.options) === null || _filter$options === void 0 ? void 0 : _filter$options.filter((option) => values.includes(option.value));
4721
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_text_truncate.PopoverRoot, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_text_truncate.PopoverTrigger, {
4722
+ disabled,
4723
+ className: require_text_truncate.cn("inline-flex h-7 items-center gap-1 rounded-md border px-2 text-xs transition-colors", require_text_truncate.OPT_FOCUS_VISIBLE, (selected === null || selected === void 0 ? void 0 : selected.length) ? "border-accent bg-accent-subtle text-text-primary" : "border-border text-text-secondary hover:text-text-primary", disabled && "pointer-events-none opacity-50"),
4724
+ children: [
4725
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: filter.label }),
4726
+ (selected === null || selected === void 0 ? void 0 : selected.length) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Badge, {
4727
+ variant: "info",
4728
+ size: "sm",
4729
+ children: selected.length
4730
+ }) : null,
4731
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronDown, {
4732
+ "aria-hidden": "true",
4733
+ className: "size-3"
4734
+ })
4735
+ ]
4736
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.PopoverContent, {
4737
+ className: "min-w-48 p-1",
4738
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4739
+ role: "group",
4740
+ "aria-label": filter.label,
4741
+ children: (_filter$options2 = filter.options) === null || _filter$options2 === void 0 ? void 0 : _filter$options2.map((option) => {
4742
+ const active = values.includes(option.value);
4743
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4744
+ type: "button",
4745
+ disabled,
4746
+ "aria-pressed": active,
4747
+ onClick: () => onChange(toggleValue(value, filter.field, option.value, active, multiSelect)),
4748
+ className: require_text_truncate.cn("flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-xs", "hover:bg-bg-subtle", "disabled:pointer-events-none disabled:opacity-50", require_text_truncate.OPT_FOCUS_VISIBLE),
4749
+ children: [
4750
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Check, {
4751
+ "aria-hidden": "true",
4752
+ className: require_text_truncate.cn("size-3 shrink-0", active ? "opacity-100" : "opacity-0")
4753
+ }),
4754
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4755
+ className: "flex-1 truncate",
4756
+ children: option.label
4757
+ }),
4758
+ option.count != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4759
+ className: "text-text-tertiary tabular-nums",
4760
+ children: option.count
4761
+ })
4762
+ ]
4763
+ }, option.value);
4764
+ })
4765
+ })
4766
+ })] }, filter.id);
4767
+ }
4768
+ const filterValue = (_filter$value = filter.value) !== null && _filter$value !== void 0 ? _filter$value : filter.id;
4769
+ const active = values.includes(filterValue);
4770
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4771
+ type: "button",
4772
+ disabled,
4773
+ "aria-pressed": active,
4774
+ onClick: () => onChange(toggleValue(value, filter.field, filterValue, active, multiSelect)),
4775
+ className: require_text_truncate.cn("inline-flex h-7 items-center rounded-md border px-2 text-xs transition-colors", require_text_truncate.OPT_FOCUS_VISIBLE, active ? "border-accent bg-accent-subtle text-text-primary" : "border-border text-text-secondary hover:text-text-primary", "disabled:pointer-events-none disabled:opacity-50"),
4776
+ children: filter.label
4777
+ }, filter.id);
4778
+ }),
4779
+ activeCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4780
+ className: "text-text-tertiary text-xs",
4781
+ children: labels.activeCount(activeCount)
4782
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_text_truncate.Button, {
4783
+ variant: "ghost",
4784
+ size: "sm",
4785
+ disabled,
4786
+ onClick: () => onChange({ clauses: [] }),
4787
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, {
4788
+ "aria-hidden": "true",
4789
+ className: "size-3"
4790
+ }), labels.clearAll]
4791
+ })] }),
4792
+ actions
4793
+ ]
4794
+ })]
4795
+ });
4796
+ }
4797
+ /** Whether the bar is filtering anything — for an empty state's wording. */
4798
+ function hasActiveQuery(query) {
4799
+ return !require_field_sidebar.isEmptyQuery(query);
4800
+ }
4801
+ //#endregion
4802
+ //#region src/shells/catalog-frame.tsx
4803
+ /**
4804
+ * CatalogRowActions — a row's actions, two of them visible.
4805
+ *
4806
+ * Beyond two, a row's action column starts competing with the row's content
4807
+ * for the reader's eye, so the rest go behind one menu. The click never
4808
+ * reaches the row: acting on a row and selecting it are different intentions,
4809
+ * and a table that does both at once is one a reader stops trusting.
4810
+ */
4811
+ function CatalogRowActions({ item, actions, visibleCount = 2, labels }) {
4812
+ var _labels$rowActions, _labels$moreActions;
4813
+ const applicable = actions.filter((action) => {
4814
+ var _action$available, _action$available2;
4815
+ return (_action$available = (_action$available2 = action.available) === null || _action$available2 === void 0 ? void 0 : _action$available2.call(action, item)) !== null && _action$available !== void 0 ? _action$available : true;
4816
+ });
4817
+ const visible = applicable.slice(0, visibleCount);
4818
+ const overflow = applicable.slice(visibleCount);
4819
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4820
+ className: "flex items-center justify-end gap-1",
4821
+ role: "group",
4822
+ "aria-label": (_labels$rowActions = labels === null || labels === void 0 ? void 0 : labels.rowActions) !== null && _labels$rowActions !== void 0 ? _labels$rowActions : "행 액션",
4823
+ onClick: (event) => event.stopPropagation(),
4824
+ children: [visible.map((action) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_text_truncate.Button, {
4825
+ variant: "ghost",
4826
+ size: "sm",
4827
+ onClick: () => action.onSelect(item),
4828
+ children: [action.icon, action.label]
4829
+ }, 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, {
4830
+ "aria-label": (_labels$moreActions = labels === null || labels === void 0 ? void 0 : labels.moreActions) !== null && _labels$moreActions !== void 0 ? _labels$moreActions : "더 보기",
4831
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.MoreHorizontal, {
4832
+ "aria-hidden": "true",
4833
+ className: "size-4"
4834
+ })
4835
+ }), /* @__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, {
4836
+ onClick: () => action.onSelect(item),
4837
+ children: action.label
4838
+ }, action.id)) })] })]
4839
+ });
4840
+ }
4841
+ /**
4842
+ * Keeps a cell's own controls from also selecting the row.
4843
+ *
4844
+ * A click on a switch or a copy button reaches the row handler on its way up,
4845
+ * so toggling a flag would open the inspector at the same time.
4846
+ */
4847
+ function CatalogCellControls({ children }) {
4848
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4849
+ className: "contents",
4850
+ onClick: (event) => event.stopPropagation(),
4851
+ children
4852
+ });
4853
+ }
4854
+ const defaultLabels$13 = {
4855
+ emptyTitle: "아직 아무것도 없습니다",
4856
+ emptyDescription: "데이터가 도착하면 여기에 나타납니다.",
4857
+ filteredEmptyTitle: "조건에 맞는 항목이 없습니다",
4858
+ filteredEmptyDescription: "필터를 지우면 전체 목록이 보입니다.",
4859
+ clearFilters: "필터 지우기",
4860
+ errorTitle: "목록을 불러오지 못했습니다",
4861
+ retry: "다시 시도"
4862
+ };
4863
+ /**
4864
+ * CatalogFrame — the shape every catalogue list has.
4865
+ *
4866
+ * Two catalogues (events, properties) differ only in their columns and their
4867
+ * words; everything around the table is the same, and writing it twice meant
4868
+ * two copies of the decisions that are easy to get wrong — that a failed read
4869
+ * must not look like an empty list, that an empty list reads differently when a
4870
+ * filter is on, and that a selection must not survive a filter change.
4871
+ *
4872
+ * The last one is enforced here rather than left to the caller. A bulk action
4873
+ * applied to rows the reader can no longer see is the kind of mistake that is
4874
+ * only noticed afterwards.
4875
+ */
4876
+ function CatalogFrame({ children, isEmpty, query, onQueryChange, filters, stats, onStatClick, notice, emptyActions, header, actions, toolbar, inspector, inspectorOpen, onInspectorClose, inspectorTitle, inspectorDescription, inspectorActions, inspectorFooter, inspectorWidth, page = 1, pageSize = 50, totalCount, onPageChange, selectedKeys, onSelectionChange, bulkActions, onBulkAction, error, onRetry, labels: customLabels, className }) {
4877
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$13), customLabels);
4878
+ const lastScope = (0, react.useRef)({
4879
+ query,
4880
+ page
4881
+ });
4882
+ (0, react.useEffect)(() => {
4883
+ const changed = lastScope.current.query !== query || lastScope.current.page !== page;
4884
+ lastScope.current = {
4885
+ query,
4886
+ page
4887
+ };
4888
+ if (changed && selectedKeys && selectedKeys.length > 0) onSelectionChange === null || onSelectionChange === void 0 || onSelectionChange([]);
4889
+ }, [
4890
+ query,
4891
+ page,
4892
+ selectedKeys,
4893
+ onSelectionChange
4894
+ ]);
4895
+ const filtering = !require_field_sidebar.isEmptyQuery(query);
4896
+ const body = error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.EmptyState, {
4897
+ tone: "error",
4898
+ hasBorder: true,
4899
+ title: labels.errorTitle,
4900
+ body: error,
4901
+ actions: onRetry ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Button, {
4902
+ variant: "secondary",
4903
+ onClick: onRetry,
4904
+ children: labels.retry
4905
+ }) : void 0
4906
+ }) : isEmpty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.EmptyState, {
4907
+ tone: "subdued",
4908
+ hasBorder: true,
4909
+ title: filtering ? labels.filteredEmptyTitle : labels.emptyTitle,
4910
+ description: filtering ? labels.filteredEmptyDescription : labels.emptyDescription,
4911
+ actions: filtering ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Button, {
4912
+ variant: "secondary",
4913
+ onClick: () => onQueryChange({ clauses: [] }),
4914
+ children: labels.clearFilters
4915
+ }) : emptyActions
4916
+ }) : children;
4917
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4918
+ className: require_text_truncate.cn("flex flex-col gap-section", className),
4919
+ children: [
4920
+ (header || actions) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4921
+ className: "gap-group flex min-w-0 flex-col lg:flex-row lg:items-start lg:justify-between",
4922
+ children: [header && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4923
+ className: "min-w-0 flex-1",
4924
+ children: header
4925
+ }), actions && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4926
+ className: "gap-element flex flex-wrap items-center",
4927
+ children: actions
4928
+ })]
4929
+ }),
4930
+ stats && stats.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SummaryRow, {
4931
+ stats,
4932
+ onStatClick
4933
+ }),
4934
+ notice,
4935
+ toolbar && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4936
+ className: "gap-group flex flex-wrap items-center",
4937
+ children: toolbar
4938
+ }),
4939
+ filters && filters.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QueryFilterBar, {
4940
+ value: query,
4941
+ onChange: onQueryChange,
4942
+ filters,
4943
+ disabled: Boolean(error)
4944
+ }),
4945
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InspectorLayout, {
4946
+ open: inspectorOpen && Boolean(inspector),
4947
+ onClose: onInspectorClose,
4948
+ inspector,
4949
+ title: inspectorTitle,
4950
+ description: inspectorDescription,
4951
+ actions: inspectorActions,
4952
+ footer: inspectorFooter,
4953
+ width: inspectorWidth,
4954
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4955
+ className: "gap-group flex flex-col",
4956
+ children: [body, onPageChange && totalCount != null && totalCount > pageSize && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Pagination, {
4957
+ currentPage: page,
4958
+ totalPages: Math.ceil(totalCount / pageSize),
4959
+ onPageChange
4960
+ })]
4961
+ })
4962
+ }),
4963
+ bulkActions && bulkActions.length > 0 && onBulkAction && selectedKeys && selectedKeys.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FloatingActionBar, {
4964
+ actions: bulkActions,
4965
+ selectedCount: selectedKeys.length,
4966
+ onAction: onBulkAction,
4967
+ onDismiss: () => onSelectionChange === null || onSelectionChange === void 0 ? void 0 : onSelectionChange([])
4968
+ })
4969
+ ]
4970
+ });
4971
+ }
4972
+ //#endregion
4099
4973
  //#region src/shells/comparison-selector.tsx
4100
4974
  /** Renders the comparison selector component. */
4101
4975
  function ComparisonSelector({ segments, valueA, valueB, onChangeA, onChangeB, values, onValuesChange, maxItems = 5, labels: customLabels, className }) {
@@ -5250,7 +6124,7 @@ function ThemeSwitcher({ className, labels: userLabels, variant = "default", cus
5250
6124
  ThemeSwitcher.displayName = "ThemeSwitcher";
5251
6125
  //#endregion
5252
6126
  //#region src/shells/prompt-template.tsx
5253
- const defaultLabels$11 = {
6127
+ const defaultLabels$12 = {
5254
6128
  copy: "Copy",
5255
6129
  copied: "Copied!",
5256
6130
  copyFailed: "Copy failed"
@@ -5260,7 +6134,7 @@ function renderPrompt(template, values) {
5260
6134
  }
5261
6135
  /** Renders the prompt template component. */
5262
6136
  function PromptTemplate({ title, description, template, variables = [], className, labels: customLabels }) {
5263
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$11), customLabels);
6137
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$12), customLabels);
5264
6138
  const [values, setValues] = react.useState(() => {
5265
6139
  const initial = {};
5266
6140
  for (const v of variables) if (v.defaultValue) initial[v.name] = v.defaultValue;
@@ -5749,14 +6623,14 @@ function AsyncCombobox({ onSearch, debounceMs = 300, loadingText, minChars = 1,
5749
6623
  }
5750
6624
  //#endregion
5751
6625
  //#region src/shells/vertical-tabs.tsx
5752
- const defaultLabels$10 = {
6626
+ const defaultLabels$11 = {
5753
6627
  ariaLabel: "Vertical tabs",
5754
6628
  emptyMessage: "No tabs available"
5755
6629
  };
5756
6630
  /** Renders the vertical tabs component. */
5757
6631
  function VerticalTabs({ tabs, defaultTabId, labels: customLabels, className }) {
5758
6632
  var _tabs$;
5759
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$10), customLabels);
6633
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$11), customLabels);
5760
6634
  if (tabs.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5761
6635
  className: require_text_truncate.cn("text-text-secondary text-sm", className),
5762
6636
  "data-opt-id": "J81G4",
@@ -5765,6 +6639,7 @@ function VerticalTabs({ tabs, defaultTabId, labels: customLabels, className }) {
5765
6639
  });
5766
6640
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.TabsRoot, {
5767
6641
  defaultSelectedId: defaultTabId !== null && defaultTabId !== void 0 ? defaultTabId : (_tabs$ = tabs[0]) === null || _tabs$ === void 0 ? void 0 : _tabs$.id,
6642
+ orientation: "vertical",
5768
6643
  "data-opt-id": "J81G4",
5769
6644
  "data-opt-slug": "vertical-tabs.VerticalTabs",
5770
6645
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -5789,13 +6664,13 @@ function VerticalTabs({ tabs, defaultTabId, labels: customLabels, className }) {
5789
6664
  }
5790
6665
  //#endregion
5791
6666
  //#region src/shells/column-toggler.tsx
5792
- const defaultLabels$9 = {
6667
+ const defaultLabels$10 = {
5793
6668
  triggerLabel: (count) => `Columns (${count})`,
5794
6669
  emptyMessage: "No columns available"
5795
6670
  };
5796
6671
  /** Renders the column toggler component. */
5797
6672
  function ColumnToggler({ columns, visibleColumns, onChange, className, labels: customLabels }) {
5798
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$9), customLabels);
6673
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$10), customLabels);
5799
6674
  const visibleCount = visibleColumns.length;
5800
6675
  const hasColumns = columns.length > 0;
5801
6676
  const toggle = (id) => {
@@ -5874,7 +6749,7 @@ function BulkActions({ selectedCount, actions, onAction, onClear, labels: custom
5874
6749
  }
5875
6750
  //#endregion
5876
6751
  //#region src/shells/saved-filters.tsx
5877
- const defaultLabels$8 = {
6752
+ const defaultLabels$9 = {
5878
6753
  triggerLabel: (count) => `Saved Filters (${count})`,
5879
6754
  emptyMessage: "No saved filters",
5880
6755
  deleteLabel: (name) => `Delete ${name}`,
@@ -5885,7 +6760,7 @@ const defaultLabels$8 = {
5885
6760
  };
5886
6761
  /** Renders the saved filters component. */
5887
6762
  function SavedFilters({ currentFilters, presets, onSavePreset, onLoadPreset, onDeletePreset, className, labels: customLabels }) {
5888
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$8), customLabels);
6763
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$9), customLabels);
5889
6764
  const [newName, setNewName] = react.useState("");
5890
6765
  const [pendingDeleteId, setPendingDeleteId] = react.useState(null);
5891
6766
  const triggerId = react.useId();
@@ -6037,7 +6912,7 @@ function SavedFilters({ currentFilters, presets, onSavePreset, onLoadPreset, onD
6037
6912
  }
6038
6913
  //#endregion
6039
6914
  //#region src/shells/notification-center.tsx
6040
- const defaultLabels$7 = {
6915
+ const defaultLabels$8 = {
6041
6916
  triggerLabel: "Notifications",
6042
6917
  unreadSuffix: (count) => `(${count} unread)`,
6043
6918
  title: "Notifications",
@@ -6046,7 +6921,7 @@ const defaultLabels$7 = {
6046
6921
  };
6047
6922
  /** Renders the notification center component. */
6048
6923
  function NotificationCenter({ notifications, onMarkRead, onMarkAllRead, onAction, className, labels: customLabels }) {
6049
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$7), customLabels);
6924
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$8), customLabels);
6050
6925
  const unreadCount = notifications.filter((n) => !n.read).length;
6051
6926
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_text_truncate.PopoverRoot, {
6052
6927
  "data-opt-id": "7A0XY",
@@ -6250,9 +7125,29 @@ function AuditLog({ entries, onLoadMore, loading, labels: customLabels, classNam
6250
7125
  AuditLog.displayName = "AuditLog";
6251
7126
  //#endregion
6252
7127
  //#region src/shells/tree-table.tsx
7128
+ const defaultLabels$7 = {
7129
+ ariaLabel: "Tree table",
7130
+ expand: (rowLabel) => `Expand ${rowLabel}`,
7131
+ collapse: (rowLabel) => `Collapse ${rowLabel}`,
7132
+ level: (depth) => `Level ${depth}`
7133
+ };
7134
+ function flattenTree(nodes, expanded, depth = 0, ancestorsVisible = true) {
7135
+ return nodes.flatMap((node) => {
7136
+ var _node$children;
7137
+ const visible = ancestorsVisible;
7138
+ return [{
7139
+ node,
7140
+ depth,
7141
+ visible
7142
+ }, ...flattenTree((_node$children = node.children) !== null && _node$children !== void 0 ? _node$children : [], expanded, depth + 1, visible && expanded.has(node.id))];
7143
+ });
7144
+ }
6253
7145
  /** Renders the tree table component. */
6254
- function TreeTable({ data, columns, defaultExpanded = [], indentSize = 24, className }) {
7146
+ function TreeTable({ data, columns, defaultExpanded = [], indentSize = 24, getRowLabel, labels: customLabels, className, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy }) {
6255
7147
  const [expanded, setExpanded] = react.useState(() => new Set(defaultExpanded));
7148
+ const baseId = react.useId();
7149
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$7), customLabels);
7150
+ const rows = flattenTree(data, expanded);
6256
7151
  const toggle = (id) => {
6257
7152
  setExpanded((prev) => {
6258
7153
  const next = new Set(prev);
@@ -6266,46 +7161,82 @@ function TreeTable({ data, columns, defaultExpanded = [], indentSize = 24, class
6266
7161
  if (typeof col.accessor === "function") return col.accessor(row);
6267
7162
  return String((_row$col$accessor = row[col.accessor]) !== null && _row$col$accessor !== void 0 ? _row$col$accessor : "");
6268
7163
  };
6269
- const renderRows = (nodes, depth) => nodes.flatMap((node) => {
6270
- const hasChildren = node.children && node.children.length > 0;
6271
- const isExpanded = expanded.has(node.id);
6272
- return [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", {
6273
- className: "border-border border-b",
6274
- children: columns.map((col, ci) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
6275
- className: require_text_truncate.cn(require_text_truncate.OPT_TD),
6276
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6277
- className: "flex items-center",
6278
- style: ci === 0 ? { paddingLeft: depth * indentSize } : void 0,
6279
- children: [
6280
- ci === 0 && hasChildren && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6281
- type: "button",
6282
- onClick: () => toggle(node.id),
6283
- className: require_text_truncate.cn("mr-1 flex h-5 w-5 items-center justify-center rounded text-xs", "outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-ring-offset"),
6284
- "aria-expanded": isExpanded,
6285
- "aria-label": isExpanded ? "Collapse" : "Expand",
6286
- children: isExpanded ? "▼" : "▶"
6287
- }),
6288
- ci === 0 && !hasChildren && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "mr-1 inline-block w-5" }),
6289
- getCellValue(node.data, col)
6290
- ]
6291
- })
6292
- }, col.id))
6293
- }, node.id), ...isExpanded && hasChildren ? renderRows(node.children, depth + 1) : []];
6294
- });
7164
+ const rowId = (id) => `${baseId}-row-${encodeURIComponent(id)}`;
7165
+ const resolveRowLabel = (node) => {
7166
+ if (getRowLabel) return getRowLabel(node);
7167
+ const firstColumn = columns[0];
7168
+ if (firstColumn) {
7169
+ const value = getCellValue(node.data, firstColumn);
7170
+ if (typeof value === "string" || typeof value === "number") return String(value);
7171
+ }
7172
+ return node.id;
7173
+ };
6295
7174
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6296
7175
  className: require_text_truncate.cn("overflow-auto", className),
6297
- "data-opt-id": "EJRTW",
7176
+ "data-opt-id": "PP194",
6298
7177
  "data-opt-slug": "tree-table.TreeTable",
6299
7178
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
6300
7179
  className: require_text_truncate.cn("w-full border-collapse", require_text_truncate.OPT_BORDER),
7180
+ "aria-label": ariaLabelledBy == null ? ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : labels.ariaLabel : void 0,
7181
+ "aria-labelledby": ariaLabelledBy,
6301
7182
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", {
6302
7183
  className: "border-border border-b",
6303
7184
  children: columns.map((col) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {
7185
+ scope: "col",
6304
7186
  className: require_text_truncate.cn(require_text_truncate.OPT_TH),
6305
7187
  style: { width: col.width },
6306
7188
  children: col.header
6307
7189
  }, col.id))
6308
- }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: renderRows(data, 0) })]
7190
+ }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: rows.map(({ node, depth, visible }) => {
7191
+ var _node$children$length, _node$children2, _node$children3;
7192
+ const hasChildren = ((_node$children$length = (_node$children2 = node.children) === null || _node$children2 === void 0 ? void 0 : _node$children2.length) !== null && _node$children$length !== void 0 ? _node$children$length : 0) > 0;
7193
+ const isExpanded = expanded.has(node.id);
7194
+ const resolvedRowLabel = resolveRowLabel(node);
7195
+ const controlledRows = ((_node$children3 = node.children) !== null && _node$children3 !== void 0 ? _node$children3 : []).map((child) => rowId(child.id)).join(" ");
7196
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", {
7197
+ id: rowId(node.id),
7198
+ hidden: !visible,
7199
+ className: "border-border border-b",
7200
+ children: columns.map((col, columnIndex) => {
7201
+ const content = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7202
+ className: "flex items-center",
7203
+ style: columnIndex === 0 ? { paddingLeft: depth * indentSize } : void 0,
7204
+ children: [
7205
+ columnIndex === 0 && hasChildren && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7206
+ type: "button",
7207
+ onClick: () => toggle(node.id),
7208
+ className: require_text_truncate.cn("mr-1 flex h-5 w-5 items-center justify-center rounded text-xs", "outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-ring-offset"),
7209
+ "aria-expanded": isExpanded,
7210
+ "aria-controls": controlledRows || void 0,
7211
+ "aria-label": isExpanded ? labels.collapse(resolvedRowLabel) : labels.expand(resolvedRowLabel),
7212
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7213
+ "aria-hidden": "true",
7214
+ children: isExpanded ? "▼" : "▶"
7215
+ })
7216
+ }),
7217
+ columnIndex === 0 && !hasChildren && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "mr-1 inline-block w-5" }),
7218
+ columnIndex === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
7219
+ className: "sr-only",
7220
+ children: [
7221
+ labels.level(depth + 1),
7222
+ ".",
7223
+ " "
7224
+ ]
7225
+ }),
7226
+ getCellValue(node.data, col)
7227
+ ]
7228
+ });
7229
+ return columnIndex === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {
7230
+ scope: "row",
7231
+ className: require_text_truncate.cn(require_text_truncate.OPT_TD),
7232
+ children: content
7233
+ }, col.id) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
7234
+ className: require_text_truncate.cn(require_text_truncate.OPT_TD),
7235
+ children: content
7236
+ }, col.id);
7237
+ })
7238
+ }, node.id);
7239
+ }) })]
6309
7240
  })
6310
7241
  });
6311
7242
  }
@@ -7047,17 +7978,37 @@ function DataImportWizard({ fields, onImport, acceptedFormats = ".csv", labels:
7047
7978
  //#endregion
7048
7979
  //#region src/shells/mega-menu.tsx
7049
7980
  const defaultLabels$3 = { trigger: "Menu" };
7981
+ function MegaMenuItemContent({ item }) {
7982
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [item.icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7983
+ "aria-hidden": "true",
7984
+ className: "text-text-secondary mt-0.5 flex-shrink-0",
7985
+ children: item.icon
7986
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
7987
+ className: "flex flex-col text-left",
7988
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7989
+ className: "text-text-primary text-sm font-medium",
7990
+ children: item.label
7991
+ }), item.description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7992
+ className: "text-text-tertiary text-xs",
7993
+ children: item.description
7994
+ })]
7995
+ })] });
7996
+ }
7050
7997
  /** Renders the mega menu component. */
7051
- function MegaMenu({ menus, columns = 3, footer, triggerLabel, labels: customLabels, className }) {
7998
+ function MegaMenu({ menus, columns = 3, footer, triggerLabel, labels: customLabels, className, "aria-label": ariaLabel }) {
7999
+ var _ref;
7052
8000
  const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$3), customLabels);
7053
8001
  const resolvedTriggerLabel = triggerLabel !== null && triggerLabel !== void 0 ? triggerLabel : labels.trigger;
8002
+ const navigationLabel = (_ref = ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : labels.navigation) !== null && _ref !== void 0 ? _ref : `${resolvedTriggerLabel} navigation`;
8003
+ const itemClassName = "hover:bg-bg-subtle gap-element flex w-full items-start rounded-md p-2 transition-colors";
7054
8004
  if (menus.length === 0) return null;
7055
8005
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_text_truncate.PopoverRoot, {
7056
- "data-opt-id": "42177",
8006
+ "data-opt-id": "AHTK9",
7057
8007
  "data-opt-slug": "mega-menu.MegaMenu",
7058
8008
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_text_truncate.PopoverTrigger, {
7059
8009
  className: require_text_truncate.cn("border-border bg-surface text-text-primary hover:bg-bg-subtle inline-flex h-9 items-center gap-1.5 rounded-lg border px-3 text-sm font-medium", className),
7060
8010
  children: [resolvedTriggerLabel, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
8011
+ "aria-hidden": "true",
7061
8012
  width: "12",
7062
8013
  height: "12",
7063
8014
  viewBox: "0 0 12 12",
@@ -7066,38 +8017,41 @@ function MegaMenu({ menus, columns = 3, footer, triggerLabel, labels: customLabe
7066
8017
  strokeWidth: "1.5",
7067
8018
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M3 5l3 3 3-3" })
7068
8019
  })]
7069
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_text_truncate.PopoverContent, {
8020
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.PopoverContent, {
7070
8021
  className: "w-auto max-w-2xl p-0",
7071
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7072
- className: "gap-group grid p-4",
7073
- style: { gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` },
7074
- children: menus.map((section) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7075
- className: "gap-element flex flex-col",
7076
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
7077
- className: "text-text-tertiary text-xs font-semibold tracking-wider uppercase",
7078
- children: section.title
7079
- }), section.items.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
7080
- href: item.href || "#",
7081
- className: "hover:bg-bg-subtle gap-element flex items-start rounded-md p-2 transition-colors",
7082
- children: [item.icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7083
- className: "text-text-secondary mt-0.5 flex-shrink-0",
7084
- children: item.icon
7085
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7086
- className: "flex flex-col",
7087
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7088
- className: "text-text-primary text-sm font-medium",
7089
- children: item.label
7090
- }), item.description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7091
- className: "text-text-tertiary text-xs",
7092
- children: item.description
7093
- })]
8022
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("nav", {
8023
+ "aria-label": navigationLabel,
8024
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8025
+ className: "gap-group grid p-4",
8026
+ style: { gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` },
8027
+ children: menus.map((section) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8028
+ className: "gap-element flex flex-col",
8029
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
8030
+ className: "text-text-tertiary text-xs font-semibold tracking-wider uppercase",
8031
+ children: section.title
8032
+ }), section.items.map((item) => {
8033
+ if (item.href != null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
8034
+ href: item.href,
8035
+ className: itemClassName,
8036
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MegaMenuItemContent, { item })
8037
+ }, item.label);
8038
+ if (item.onSelect) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.PopoverClose, {
8039
+ onClick: item.onSelect,
8040
+ className: itemClassName,
8041
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MegaMenuItemContent, { item })
8042
+ }, item.label);
8043
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8044
+ "aria-disabled": "true",
8045
+ className: require_text_truncate.cn(itemClassName, "cursor-not-allowed opacity-60"),
8046
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MegaMenuItemContent, { item })
8047
+ }, item.label);
7094
8048
  })]
7095
- }, item.label))]
7096
- }, section.title))
7097
- }), footer && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7098
- className: "border-border bg-bg-subtle border-t px-4 py-3",
7099
- children: footer
7100
- })]
8049
+ }, section.title))
8050
+ }), footer && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8051
+ className: "border-border bg-bg-subtle border-t px-4 py-3",
8052
+ children: footer
8053
+ })]
8054
+ })
7101
8055
  })]
7102
8056
  });
7103
8057
  }
@@ -7214,12 +8168,12 @@ function ChatSidebar({ conversations, activeConversationId, onConversationSelect
7214
8168
  }
7215
8169
  function formatTimestamp(timestamp, now) {
7216
8170
  const date = new Date(timestamp);
7217
- if (!now) return require_key_pad_menu.formatOptShortDate(date);
8171
+ if (!now) return require_text_truncate.formatOptShortDate(date);
7218
8172
  const diffMs = now.getTime() - date.getTime();
7219
8173
  const diffDays = Math.floor(diffMs / 864e5);
7220
- if (diffMs >= 0 && diffDays === 0) return require_key_pad_menu.formatOptTime(date);
8174
+ if (diffMs >= 0 && diffDays === 0) return require_text_truncate.formatOptTime(date);
7221
8175
  if (diffMs >= 0 && diffDays < 7) return `${diffDays}d`;
7222
- return require_key_pad_menu.formatOptShortDate(date);
8176
+ return require_text_truncate.formatOptShortDate(date);
7223
8177
  }
7224
8178
  ChatSidebar.displayName = "ChatSidebar";
7225
8179
  //#endregion
@@ -7485,7 +8439,7 @@ function ChatMessageList({ messages, loading = false, renderContent, assistantAv
7485
8439
  });
7486
8440
  }
7487
8441
  function formatMessageTime(timestamp) {
7488
- return require_key_pad_menu.formatOptTime(timestamp);
8442
+ return require_text_truncate.formatOptTime(timestamp);
7489
8443
  }
7490
8444
  ChatMessageList.displayName = "ChatMessageList";
7491
8445
  //#endregion
@@ -8295,67 +9249,6 @@ const enStarRatingLabels = {
8295
9249
  };
8296
9250
  const enBreadcrumbLabels = { navAriaLabel: "Go to {label}" };
8297
9251
  //#endregion
8298
- //#region src/lib/query/query-match.ts
8299
- function toText(value) {
8300
- if (value === null || value === void 0) return "";
8301
- if (typeof value === "string") return value;
8302
- if (typeof value === "number" || typeof value === "boolean") return String(value);
8303
- return JSON.stringify(value);
8304
- }
8305
- function compare(left, right) {
8306
- const leftNumber = typeof left === "number" ? left : Number(toText(left));
8307
- const rightNumber = Number(right);
8308
- if (Number.isFinite(leftNumber) && Number.isFinite(rightNumber)) return leftNumber - rightNumber;
8309
- return toText(left).localeCompare(right);
8310
- }
8311
- function matchesClause(clause, read, searchable) {
8312
- if (clause.type === "term") return searchable().toLowerCase().includes(clause.value.toLowerCase());
8313
- const value = read(clause.field);
8314
- switch (clause.operator) {
8315
- case "exists": return value !== void 0 && value !== null && value !== "";
8316
- case "eq": return clause.values.some((candidate) => toText(value).toLowerCase() === candidate.toLowerCase());
8317
- case "contains": return clause.values.some((candidate) => toText(value).toLowerCase().includes(candidate.toLowerCase()));
8318
- case "gt":
8319
- var _clause$values$;
8320
- return compare(value, (_clause$values$ = clause.values[0]) !== null && _clause$values$ !== void 0 ? _clause$values$ : "") > 0;
8321
- case "gte":
8322
- var _clause$values$2;
8323
- return compare(value, (_clause$values$2 = clause.values[0]) !== null && _clause$values$2 !== void 0 ? _clause$values$2 : "") >= 0;
8324
- case "lt":
8325
- var _clause$values$3;
8326
- return compare(value, (_clause$values$3 = clause.values[0]) !== null && _clause$values$3 !== void 0 ? _clause$values$3 : "") < 0;
8327
- case "lte":
8328
- var _clause$values$4;
8329
- return compare(value, (_clause$values$4 = clause.values[0]) !== null && _clause$values$4 !== void 0 ? _clause$values$4 : "") <= 0;
8330
- default: return true;
8331
- }
8332
- }
8333
- /**
8334
- * Whether one row satisfies every clause.
8335
- *
8336
- * `searchable` is lazy because most queries are field constraints, and
8337
- * flattening a row to text for a term match nobody asked for is the expensive
8338
- * part of filtering a live tail.
8339
- */
8340
- function matchesQuery(query, read, searchable) {
8341
- return query.clauses.every((clause) => {
8342
- const matched = matchesClause(clause, read, searchable);
8343
- return clause.negated ? !matched : matched;
8344
- });
8345
- }
8346
- /** Convenience for plain objects, including one level of nested properties. */
8347
- function matchesQueryObject(query, row, options = {}) {
8348
- const nested = options.nested ? row[options.nested] : void 0;
8349
- const read = (field) => field in row ? row[field] : nested === null || nested === void 0 ? void 0 : nested[field];
8350
- let cached = null;
8351
- const searchable = () => {
8352
- var _cached;
8353
- (_cached = cached) !== null && _cached !== void 0 || (cached = [...Object.values(row).filter((value) => typeof value !== "object"), ...Object.values(nested !== null && nested !== void 0 ? nested : {})].map(toText).join(" "));
8354
- return cached;
8355
- };
8356
- return matchesQuery(query, read, searchable);
8357
- }
8358
- //#endregion
8359
9252
  exports.ALL_COMPOUND_THEMES = ALL_COMPOUND_THEMES;
8360
9253
  exports.AccordionContent = require_key_pad_menu.AccordionContent;
8361
9254
  exports.AccordionItem = require_key_pad_menu.AccordionItem;
@@ -8380,12 +9273,16 @@ exports.Breadcrumb = require_key_pad_menu.Breadcrumb;
8380
9273
  exports.BulkActions = BulkActions;
8381
9274
  exports.Button = require_text_truncate.Button;
8382
9275
  exports.COLOR_PRESETS = COLOR_PRESETS;
9276
+ exports.Callout = Callout;
8383
9277
  exports.Card = require_key_pad_menu.Card;
8384
9278
  exports.CardContent = require_key_pad_menu.CardContent;
8385
9279
  exports.CardDescription = require_key_pad_menu.CardDescription;
8386
9280
  exports.CardFooter = require_key_pad_menu.CardFooter;
8387
9281
  exports.CardHeader = require_key_pad_menu.CardHeader;
8388
9282
  exports.CardTitle = require_key_pad_menu.CardTitle;
9283
+ exports.CatalogCellControls = CatalogCellControls;
9284
+ exports.CatalogFrame = CatalogFrame;
9285
+ exports.CatalogRowActions = CatalogRowActions;
8389
9286
  exports.ChatInput = ChatInput;
8390
9287
  exports.ChatMessageList = ChatMessageList;
8391
9288
  exports.ChatSidebar = ChatSidebar;
@@ -8427,6 +9324,7 @@ exports.ConfirmDialog = ConfirmDialog;
8427
9324
  exports.ConnectionIndicator = ConnectionIndicator;
8428
9325
  exports.Container = require_key_pad_menu.Container;
8429
9326
  exports.ContentTabs = require_field_sidebar.ContentTabs;
9327
+ exports.CopyButton = CopyButton;
8430
9328
  exports.DEFAULT_PRESETS = require_text_truncate.DEFAULT_PRESETS;
8431
9329
  exports.DEFAULT_TIME_PRESETS = require_field_sidebar.DEFAULT_TIME_PRESETS;
8432
9330
  exports.DashboardGrid = require_field_sidebar.DashboardGrid;
@@ -8434,6 +9332,7 @@ exports.DataImportWizard = DataImportWizard;
8434
9332
  exports.DataTable = require_field_sidebar.DataTable;
8435
9333
  exports.DateRangePicker = require_text_truncate.DateRangePicker;
8436
9334
  exports.DeploymentTimeline = require_field_sidebar.DeploymentTimeline;
9335
+ exports.DescriptionList = require_key_pad_menu.DescriptionList;
8437
9336
  exports.DesignGuidePanel = DesignGuidePanel;
8438
9337
  exports.DialogBody = require_text_truncate.DialogBody;
8439
9338
  exports.DialogDescription = require_text_truncate.DialogDescription;
@@ -8470,6 +9369,7 @@ Object.defineProperty(exports, "EventSparkline", {
8470
9369
  return _reopt_ai_opt_charts.EventSparkline;
8471
9370
  }
8472
9371
  });
9372
+ exports.EventTimeline = require_field_sidebar.EventTimeline;
8473
9373
  exports.ExportButton = ExportButton;
8474
9374
  exports.Expression = require_key_pad_menu.Expression;
8475
9375
  exports.ExpressionGroup = require_key_pad_menu.ExpressionGroup;
@@ -8483,9 +9383,11 @@ exports.FaqAccordion = require_field_sidebar.FaqAccordion;
8483
9383
  exports.FeatureFlagPanel = FeatureFlagPanel;
8484
9384
  exports.FieldSidebar = require_field_sidebar.FieldSidebar;
8485
9385
  exports.FieldToken = require_text_truncate.FieldToken;
9386
+ exports.FieldTokenLegend = require_text_truncate.FieldTokenLegend;
8486
9387
  exports.FileUploadForm = FileUploadForm;
8487
9388
  exports.FilterBar = FilterBar;
8488
9389
  exports.FloatingActionBar = FloatingActionBar;
9390
+ exports.Flyout = require_field_sidebar.Flyout;
8489
9391
  exports.FormCheckbox = require_text_truncate.FormCheckbox;
8490
9392
  exports.FormColorPicker = require_text_truncate.FormColorPicker;
8491
9393
  exports.FormControl = require_text_truncate.FormControl;
@@ -8520,10 +9422,12 @@ exports.GLOBAL_THEME_STYLE_ID = GLOBAL_THEME_STYLE_ID;
8520
9422
  exports.Heading = require_key_pad_menu.Heading;
8521
9423
  exports.Highlight = require_text_truncate.Highlight;
8522
9424
  exports.IconPicker = require_text_truncate.IconPicker;
9425
+ exports.Identity = require_key_pad_menu.Identity;
8523
9426
  exports.InlineEdit = require_key_pad_menu.InlineEdit;
8524
9427
  exports.InlineEditTable = InlineEditTable;
8525
9428
  exports.Input = require_text_truncate.Input;
8526
9429
  exports.InsightsPanel = InsightsPanel;
9430
+ exports.InspectorLayout = InspectorLayout;
8527
9431
  exports.KanbanBoard = KanbanBoard;
8528
9432
  exports.Kbd = require_text_truncate.Kbd;
8529
9433
  exports.KeyPadMenu = require_key_pad_menu.KeyPadMenu;
@@ -8543,7 +9447,7 @@ exports.MenuSeparator = require_text_truncate.MenuSeparator;
8543
9447
  exports.MenuTrigger = require_text_truncate.MenuTrigger;
8544
9448
  exports.MenubarContainer = require_text_truncate.MenubarContainer;
8545
9449
  exports.MenubarRoot = require_text_truncate.MenubarRoot;
8546
- exports.Meter = require_key_pad_menu.Meter;
9450
+ exports.Meter = require_text_truncate.Meter;
8547
9451
  Object.defineProperty(exports, "MetricsCard", {
8548
9452
  enumerable: true,
8549
9453
  get: function() {
@@ -8571,6 +9475,7 @@ exports.ProjectSwitcher = require_field_sidebar.ProjectSwitcher;
8571
9475
  exports.PromptTemplate = PromptTemplate;
8572
9476
  exports.PropertyExplorer = PropertyExplorer;
8573
9477
  exports.QueryBar = require_field_sidebar.QueryBar;
9478
+ exports.QueryFilterBar = QueryFilterBar;
8574
9479
  exports.QueryResultsTable = QueryResultsTable;
8575
9480
  exports.QuickActions = QuickActions;
8576
9481
  exports.Radio = require_key_pad_menu.Radio;
@@ -8593,10 +9498,12 @@ exports.SHADER_PARAM_KEYS = require_key_pad_menu.SHADER_PARAM_KEYS;
8593
9498
  exports.SHADER_PRESETS = require_key_pad_menu.SHADER_PRESETS;
8594
9499
  exports.SHADER_PRESET_IDS = require_key_pad_menu.SHADER_PRESET_IDS;
8595
9500
  exports.SavedFilters = SavedFilters;
9501
+ exports.ScoreBreakdown = require_field_sidebar.ScoreBreakdown;
8596
9502
  exports.ScrollArea = require_key_pad_menu.ScrollArea;
8597
9503
  exports.SearchCombobox = require_field_sidebar.SearchCombobox;
8598
9504
  exports.Section = require_key_pad_menu.Section;
8599
9505
  exports.SegmentBuilder = SegmentBuilder;
9506
+ exports.SegmentedControl = SegmentedControl;
8600
9507
  exports.SelectItem = require_text_truncate.SelectItem;
8601
9508
  exports.SelectLabel = require_text_truncate.SelectLabel;
8602
9509
  exports.SelectPopover = require_text_truncate.SelectPopover;
@@ -8607,17 +9514,18 @@ exports.SettingsForm = require_field_sidebar.SettingsForm;
8607
9514
  exports.ShaderSurface = require_key_pad_menu.ShaderSurface;
8608
9515
  exports.Sidebar = Sidebar;
8609
9516
  exports.SidebarNav = require_field_sidebar.SidebarNav;
8610
- exports.Skeleton = require_key_pad_menu.Skeleton;
8611
- exports.SkeletonAvatar = require_key_pad_menu.SkeletonAvatar;
8612
- exports.SkeletonCard = require_key_pad_menu.SkeletonCard;
8613
- exports.SkeletonTable = require_key_pad_menu.SkeletonTable;
8614
- exports.SkeletonText = require_key_pad_menu.SkeletonText;
9517
+ exports.Skeleton = require_text_truncate.Skeleton;
9518
+ exports.SkeletonAvatar = require_text_truncate.SkeletonAvatar;
9519
+ exports.SkeletonCard = require_text_truncate.SkeletonCard;
9520
+ exports.SkeletonTable = require_text_truncate.SkeletonTable;
9521
+ exports.SkeletonText = require_text_truncate.SkeletonText;
8615
9522
  exports.Slider = require_text_truncate.Slider;
8616
9523
  exports.SpatialNavProvider = SpatialNavProvider;
8617
9524
  exports.Spinner = require_text_truncate.Spinner;
8618
9525
  exports.SqlEditor = SqlEditor;
8619
9526
  exports.StarRating = require_text_truncate.StarRating;
8620
9527
  exports.StatCard = require_text_truncate.StatCard;
9528
+ exports.StatusDot = StatusDot;
8621
9529
  exports.StatusSelect = require_field_sidebar.StatusSelect;
8622
9530
  exports.StepBuilder = require_key_pad_menu.StepBuilder;
8623
9531
  exports.SummaryRow = SummaryRow;
@@ -8683,10 +9591,10 @@ exports.enComboboxLabels = enComboboxLabels;
8683
9591
  exports.enStarRatingLabels = enStarRatingLabels;
8684
9592
  exports.fieldValues = require_field_sidebar.fieldValues;
8685
9593
  exports.formatClause = require_field_sidebar.formatClause;
8686
- exports.formatOptDateTime = require_key_pad_menu.formatOptDateTime;
8687
- exports.formatOptNumber = require_key_pad_menu.formatOptNumber;
8688
- exports.formatOptShortDate = require_key_pad_menu.formatOptShortDate;
8689
- exports.formatOptTime = require_key_pad_menu.formatOptTime;
9594
+ exports.formatOptDateTime = require_text_truncate.formatOptDateTime;
9595
+ exports.formatOptNumber = require_text_truncate.formatOptNumber;
9596
+ exports.formatOptShortDate = require_text_truncate.formatOptShortDate;
9597
+ exports.formatOptTime = require_text_truncate.formatOptTime;
8690
9598
  exports.formatQuery = require_field_sidebar.formatQuery;
8691
9599
  exports.generatedTheme = generatedTheme;
8692
9600
  exports.getCompoundIdForSpec = getCompoundIdForSpec;
@@ -8695,6 +9603,7 @@ exports.getShaderFragment = require_key_pad_menu.getShaderFragment;
8695
9603
  exports.getShaderPreset = require_key_pad_menu.getShaderPreset;
8696
9604
  exports.getThemeDescriptor = getThemeDescriptor;
8697
9605
  exports.getTrappedContextRole = getTrappedContextRole;
9606
+ exports.hasActiveQuery = hasActiveQuery;
8698
9607
  exports.isDescriptorSpec = isDescriptorSpec;
8699
9608
  exports.isEmptyQuery = require_field_sidebar.isEmptyQuery;
8700
9609
  exports.isForceLightSpec = isForceLightSpec;