@reopt-ai/opt-ui 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/COMPONENT_CATALOG.md +3000 -0
  3. package/LICENSE +21 -0
  4. package/README.md +244 -0
  5. package/dist/chunk-3GWWZKX7.js +38 -0
  6. package/dist/chunk-AFF2HPE5.cjs +5008 -0
  7. package/dist/chunk-ELWICXYY.js +4745 -0
  8. package/dist/chunk-N4NDU5ET.cjs +4745 -0
  9. package/dist/chunk-ONE3C5RV.cjs +38 -0
  10. package/dist/chunk-QWBHD54V.js +3218 -0
  11. package/dist/chunk-RBM2RNC2.js +5008 -0
  12. package/dist/chunk-X5WCXJAF.cjs +3218 -0
  13. package/dist/core/index.cjs +324 -0
  14. package/dist/core/index.d.cts +5 -0
  15. package/dist/core/index.d.ts +5 -0
  16. package/dist/core/index.js +324 -0
  17. package/dist/docs/01-getting-started.md +129 -0
  18. package/dist/docs/02-components/01-core.md +1841 -0
  19. package/dist/docs/02-components/02-visuals.md +11 -0
  20. package/dist/docs/02-components/03-shells.md +1361 -0
  21. package/dist/docs/02-components/04-surfaces.md +11 -0
  22. package/dist/docs/02-components/index.md +106 -0
  23. package/dist/docs/03-recipes/01-forms.md +471 -0
  24. package/dist/docs/03-recipes/02-dashboards.md +397 -0
  25. package/dist/docs/03-recipes/03-layouts.md +424 -0
  26. package/dist/docs/04-theming.md +232 -0
  27. package/dist/docs/05-migration/01-breaking-changes.md +117 -0
  28. package/dist/docs/05-migration/02-formstore.md +336 -0
  29. package/dist/docs/06-troubleshooting.md +119 -0
  30. package/dist/docs/index.md +71 -0
  31. package/dist/id-registry.cjs +1875 -0
  32. package/dist/id-registry.d.cts +27 -0
  33. package/dist/id-registry.d.ts +27 -0
  34. package/dist/id-registry.js +1875 -0
  35. package/dist/id-registry.json +3799 -0
  36. package/dist/index-BZ_lBlO1.d.ts +474 -0
  37. package/dist/index-BuvxoWHf.d.cts +474 -0
  38. package/dist/index-DlAcuvQp.d.cts +1686 -0
  39. package/dist/index-Slu5hOj1.d.ts +1686 -0
  40. package/dist/index.cjs +9959 -0
  41. package/dist/index.d.cts +2351 -0
  42. package/dist/index.d.ts +2351 -0
  43. package/dist/index.js +9959 -0
  44. package/dist/meta.cjs +6898 -0
  45. package/dist/meta.d.cts +36 -0
  46. package/dist/meta.d.ts +36 -0
  47. package/dist/meta.js +6869 -0
  48. package/dist/shells/index.cjs +65 -0
  49. package/dist/shells/index.d.cts +5 -0
  50. package/dist/shells/index.d.ts +5 -0
  51. package/dist/shells/index.js +65 -0
  52. package/dist/tailwind.css +401 -0
  53. package/dist/theme/presets/default.css +355 -0
  54. package/dist/theme/presets/minimal.css +354 -0
  55. package/dist/theme/presets/mono-dark.css +354 -0
  56. package/dist/theme/presets/natural.css +181 -0
  57. package/dist/theme/presets/pro.css +354 -0
  58. package/dist/types-D4-0lwaE.d.cts +298 -0
  59. package/dist/types-D4-0lwaE.d.ts +298 -0
  60. package/dist/visuals/index.cjs +4 -0
  61. package/dist/visuals/index.d.cts +1 -0
  62. package/dist/visuals/index.d.ts +1 -0
  63. package/dist/visuals/index.js +4 -0
  64. package/package.json +165 -0
@@ -0,0 +1,3218 @@
1
+ "use client";
2
+ import {
3
+ Alert,
4
+ Badge,
5
+ Checkbox,
6
+ ComboboxEmpty,
7
+ ComboboxInput,
8
+ ComboboxItem,
9
+ ComboboxPopover,
10
+ ComboboxRoot,
11
+ Composite,
12
+ CompositeItem,
13
+ CompositeProvider,
14
+ CompositeRow,
15
+ DialogDescription,
16
+ DialogHeading,
17
+ DialogPanel,
18
+ DialogRoot,
19
+ DisclosureContent,
20
+ DisclosureRoot,
21
+ DisclosureTrigger,
22
+ EmptyState,
23
+ FormCheckbox,
24
+ FormError,
25
+ FormInput,
26
+ FormLabel,
27
+ FormRadio,
28
+ FormRadioGroup,
29
+ FormRoot,
30
+ FormSelect,
31
+ FormSelectItem,
32
+ FormSelectPopover,
33
+ FormSubmit,
34
+ FormSwitch,
35
+ FormTextarea,
36
+ Kbd,
37
+ MenuButtonArrow,
38
+ MenuItem,
39
+ MenuItemCheckbox,
40
+ MenuItemRadio,
41
+ MenuPopover,
42
+ MenuRoot,
43
+ MenuSeparator,
44
+ MenuTrigger,
45
+ MenubarContainer,
46
+ MenubarRoot,
47
+ OPT_ACTIVE_ITEM,
48
+ OPT_ANIMATE_ENTER_EXIT,
49
+ OPT_BADGE_BLUE,
50
+ OPT_BADGE_GREEN,
51
+ OPT_BADGE_NEUTRAL,
52
+ OPT_BADGE_RED,
53
+ OPT_BADGE_YELLOW,
54
+ OPT_BORDER,
55
+ OPT_DISCLOSURE_CONTENT,
56
+ OPT_DISCLOSURE_TRIGGER,
57
+ OPT_DIVIDER,
58
+ OPT_DOT_GREEN,
59
+ OPT_DOT_RED,
60
+ OPT_DOT_YELLOW,
61
+ OPT_FOCUS,
62
+ OPT_FOCUS_VISIBLE,
63
+ OPT_INPUT,
64
+ OPT_ITEM_BASE,
65
+ OPT_SURFACE_POPOVER,
66
+ OPT_TABLE_ROW_ACTIVE,
67
+ OPT_TD,
68
+ OPT_TD_MUTED,
69
+ OPT_TEXT_LABEL,
70
+ OPT_TEXT_PRIMARY,
71
+ OPT_TEXT_SECONDARY,
72
+ OPT_TEXT_TERTIARY,
73
+ OPT_TH,
74
+ Pagination,
75
+ SelectItem,
76
+ SelectLabel,
77
+ SelectPopover,
78
+ SelectRoot,
79
+ SelectTrigger,
80
+ Spinner,
81
+ StatCard,
82
+ Tab,
83
+ TabList,
84
+ TabPanel,
85
+ TabsRoot,
86
+ TagInput,
87
+ ToolbarButton,
88
+ ToolbarContainer,
89
+ ToolbarRoot,
90
+ ToolbarSeparator,
91
+ Tooltip,
92
+ TooltipAnchor,
93
+ TooltipProvider,
94
+ cn,
95
+ useFormStore
96
+ } from "./chunk-RBM2RNC2.js";
97
+ import {
98
+ __objRest,
99
+ __spreadProps,
100
+ __spreadValues
101
+ } from "./chunk-3GWWZKX7.js";
102
+
103
+ // src/core/command.tsx
104
+ import { SearchIcon } from "lucide-react";
105
+ import {
106
+ CommandRoot as PrimCommandRoot,
107
+ CommandInput as PrimCommandInput,
108
+ CommandList as PrimCommandList,
109
+ CommandEmpty as PrimCommandEmpty,
110
+ CommandGroup as PrimCommandGroup,
111
+ CommandSeparator as PrimCommandSeparator,
112
+ CommandItem as PrimCommandItem
113
+ } from "@reopt-ai/opt-ui-primitives";
114
+ import { jsx, jsxs } from "react/jsx-runtime";
115
+ function Command(_a) {
116
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
117
+ return /* @__PURE__ */ jsx(
118
+ PrimCommandRoot,
119
+ __spreadProps(__spreadValues({
120
+ className: cn(
121
+ "bg-surface text-text-primary flex w-full flex-col overflow-hidden rounded-xl",
122
+ className
123
+ )
124
+ }, props), {
125
+ "data-opt-id": "4FYBW",
126
+ "data-opt-slug": "command.Command"
127
+ })
128
+ );
129
+ }
130
+ function CommandDialog({
131
+ open,
132
+ onOpenChange,
133
+ title = "Command Palette",
134
+ description = "\uAC80\uC0C9 \uD6C4 Enter \uB610\uB294 \uD074\uB9AD\uC73C\uB85C \uBA85\uB839\uC744 \uC2E4\uD589\uD558\uC138\uC694.",
135
+ className,
136
+ commandClassName,
137
+ shouldFilter,
138
+ filter,
139
+ children
140
+ }) {
141
+ return /* @__PURE__ */ jsx(
142
+ DialogRoot,
143
+ {
144
+ open,
145
+ onOpenChange,
146
+ "data-opt-id": "DVRSX",
147
+ "data-opt-slug": "command.CommandDialog",
148
+ children: /* @__PURE__ */ jsxs(
149
+ DialogPanel,
150
+ {
151
+ className: cn(
152
+ "top-[20%] left-1/2 -translate-x-1/2",
153
+ "w-full max-w-lg overflow-hidden p-0 shadow-2xl",
154
+ className
155
+ ),
156
+ children: [
157
+ /* @__PURE__ */ jsx(DialogHeading, { className: "sr-only", children: title }),
158
+ /* @__PURE__ */ jsx(DialogDescription, { className: "sr-only", children: description }),
159
+ /* @__PURE__ */ jsx(
160
+ Command,
161
+ {
162
+ shouldFilter,
163
+ filter,
164
+ className: commandClassName,
165
+ children
166
+ }
167
+ )
168
+ ]
169
+ }
170
+ )
171
+ }
172
+ );
173
+ }
174
+ function CommandInput(_a) {
175
+ var _b = _a, {
176
+ className,
177
+ trailing,
178
+ wrapperClassName,
179
+ placeholder,
180
+ "aria-label": ariaLabel
181
+ } = _b, props = __objRest(_b, [
182
+ "className",
183
+ "trailing",
184
+ "wrapperClassName",
185
+ "placeholder",
186
+ "aria-label"
187
+ ]);
188
+ return /* @__PURE__ */ jsxs(
189
+ "div",
190
+ {
191
+ "data-slot": "command-input-wrapper",
192
+ className: cn(
193
+ "border-border-subtle gap-element flex h-12 items-center border-b px-3",
194
+ wrapperClassName
195
+ ),
196
+ "data-opt-id": "HB1K3",
197
+ "data-opt-slug": "command.CommandInput",
198
+ children: [
199
+ /* @__PURE__ */ jsx(SearchIcon, { "aria-hidden": true, className: "text-text-tertiary size-4 shrink-0" }),
200
+ /* @__PURE__ */ jsx(
201
+ PrimCommandInput,
202
+ __spreadValues({
203
+ className: cn(
204
+ // cmdk 컨벤션 — input 자체는 ring 없이 자연스러운 검색 경험.
205
+ // 포커스 표시는 wrapper(border-b)가 시각적 컨텍스트로 제공.
206
+ "text-text-primary placeholder:text-text-tertiary flex h-full w-full bg-transparent text-sm outline-none focus-visible:ring-0 focus-visible:outline-none",
207
+ "disabled:cursor-not-allowed disabled:opacity-50",
208
+ className
209
+ ),
210
+ placeholder,
211
+ "aria-label": ariaLabel != null ? ariaLabel : typeof placeholder === "string" ? placeholder : "Command search"
212
+ }, props)
213
+ ),
214
+ trailing
215
+ ]
216
+ }
217
+ );
218
+ }
219
+ function CommandList(_a) {
220
+ var _b = _a, {
221
+ className,
222
+ "aria-label": ariaLabel
223
+ } = _b, props = __objRest(_b, [
224
+ "className",
225
+ "aria-label"
226
+ ]);
227
+ return /* @__PURE__ */ jsx(
228
+ PrimCommandList,
229
+ __spreadProps(__spreadValues({
230
+ className: cn(
231
+ "max-h-80 scroll-py-1 overflow-x-hidden overflow-y-auto p-1",
232
+ className
233
+ ),
234
+ "aria-label": ariaLabel != null ? ariaLabel : "Commands"
235
+ }, props), {
236
+ "data-opt-id": "NPXC5",
237
+ "data-opt-slug": "command.CommandList"
238
+ })
239
+ );
240
+ }
241
+ function CommandEmpty(_a) {
242
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
243
+ return /* @__PURE__ */ jsx(
244
+ PrimCommandEmpty,
245
+ __spreadProps(__spreadValues({
246
+ className: cn("text-text-tertiary py-6 text-center text-sm", className)
247
+ }, props), {
248
+ "data-opt-id": "A8BZM",
249
+ "data-opt-slug": "command.CommandEmpty"
250
+ })
251
+ );
252
+ }
253
+ function CommandGroup(_a) {
254
+ var _b = _a, {
255
+ className,
256
+ heading
257
+ } = _b, props = __objRest(_b, [
258
+ "className",
259
+ "heading"
260
+ ]);
261
+ return /* @__PURE__ */ jsx(
262
+ PrimCommandGroup,
263
+ __spreadProps(__spreadValues({
264
+ heading: heading != null ? /* @__PURE__ */ jsx("div", { className: "text-text-tertiary px-2 py-1.5 text-xs font-medium", children: heading }) : void 0,
265
+ className: cn("py-1", className)
266
+ }, props), {
267
+ "data-opt-id": "JPK1Y",
268
+ "data-opt-slug": "command.CommandGroup"
269
+ })
270
+ );
271
+ }
272
+ function CommandSeparator(_a) {
273
+ var _b = _a, {
274
+ className
275
+ } = _b, props = __objRest(_b, [
276
+ "className"
277
+ ]);
278
+ return /* @__PURE__ */ jsx(
279
+ PrimCommandSeparator,
280
+ __spreadProps(__spreadValues({
281
+ className: cn("bg-border-subtle -mx-1 my-1 h-px", className)
282
+ }, props), {
283
+ "data-opt-id": "10P0E",
284
+ "data-opt-slug": "command.CommandSeparator"
285
+ })
286
+ );
287
+ }
288
+ function CommandItem(_a) {
289
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
290
+ return /* @__PURE__ */ jsx(
291
+ PrimCommandItem,
292
+ __spreadProps(__spreadValues({
293
+ className: cn(
294
+ "text-text-primary flex cursor-pointer items-center gap-2 rounded-md px-2 py-2 text-sm select-none",
295
+ "data-[active-item]:bg-accent data-[active-item]:text-accent-fg",
296
+ "data-[disabled]:pointer-events-none data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50",
297
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
298
+ className
299
+ )
300
+ }, props), {
301
+ "data-opt-id": "K674W",
302
+ "data-opt-slug": "command.CommandItem"
303
+ })
304
+ );
305
+ }
306
+ function CommandShortcut(_a) {
307
+ var _b = _a, {
308
+ className
309
+ } = _b, props = __objRest(_b, [
310
+ "className"
311
+ ]);
312
+ return /* @__PURE__ */ jsx(
313
+ "span",
314
+ __spreadProps(__spreadValues({
315
+ "data-slot": "command-shortcut",
316
+ className: cn(
317
+ "text-text-tertiary ml-auto text-xs tracking-widest",
318
+ "data-[active-item]:text-accent-fg/80",
319
+ className
320
+ )
321
+ }, props), {
322
+ "data-opt-id": "75CQK",
323
+ "data-opt-slug": "command.CommandShortcut"
324
+ })
325
+ );
326
+ }
327
+
328
+ // src/shells/command-palette.tsx
329
+ import { useMemo } from "react";
330
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
331
+ var DEFAULT_LABELS = {
332
+ title: "Command Palette",
333
+ description: "\uAC80\uC0C9 \uD6C4 Enter \uB610\uB294 \uD074\uB9AD\uC73C\uB85C \uBA85\uB839\uC744 \uC2E4\uD589\uD558\uC138\uC694.",
334
+ input: "\uBA85\uB839\uC5B4 \uAC80\uC0C9",
335
+ placeholder: "\uBA85\uB839\uC5B4\uB97C \uAC80\uC0C9\uD558\uC138\uC694...",
336
+ list: "\uBA85\uB839\uC5B4 \uBAA9\uB85D",
337
+ empty: "\uACB0\uACFC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
338
+ escape: "ESC"
339
+ };
340
+ function CommandPalette({
341
+ commands,
342
+ open,
343
+ onOpenChange,
344
+ onSelect,
345
+ inline = false,
346
+ labels,
347
+ footer,
348
+ empty,
349
+ inputTrailing,
350
+ className
351
+ }) {
352
+ const resolvedLabels = __spreadValues(__spreadValues({}, DEFAULT_LABELS), labels);
353
+ const hasCommands = commands.length > 0;
354
+ const grouped = useMemo(() => {
355
+ var _a;
356
+ const map = /* @__PURE__ */ new Map();
357
+ for (const cmd of commands) {
358
+ const list = (_a = map.get(cmd.group)) != null ? _a : [];
359
+ list.push(cmd);
360
+ map.set(cmd.group, list);
361
+ }
362
+ return map;
363
+ }, [commands]);
364
+ const handleSelect = (cmdId) => {
365
+ const cmd = commands.find((c) => c.id === cmdId);
366
+ if (!cmd) return;
367
+ onSelect == null ? void 0 : onSelect(cmd);
368
+ onOpenChange(false);
369
+ };
370
+ const handleInputKeyDown = (e) => {
371
+ if (e.key === "Escape" && !inline) {
372
+ e.preventDefault();
373
+ onOpenChange(false);
374
+ }
375
+ };
376
+ const body = /* @__PURE__ */ jsxs2(Fragment, { children: [
377
+ /* @__PURE__ */ jsx2(
378
+ CommandInput,
379
+ {
380
+ "aria-label": resolvedLabels.input,
381
+ placeholder: resolvedLabels.placeholder,
382
+ onKeyDown: handleInputKeyDown,
383
+ trailing: inputTrailing != null ? inputTrailing : !inline ? /* @__PURE__ */ jsx2(Kbd, { children: resolvedLabels.escape }) : void 0
384
+ }
385
+ ),
386
+ /* @__PURE__ */ jsxs2(CommandList, { "aria-label": resolvedLabels.list, children: [
387
+ /* @__PURE__ */ jsx2(CommandEmpty, { children: empty != null ? empty : resolvedLabels.empty }),
388
+ hasCommands ? Array.from(grouped.entries()).map(([group, items]) => /* @__PURE__ */ jsx2(CommandGroup, { heading: group, children: items.map((cmd) => /* @__PURE__ */ jsxs2(
389
+ CommandItem,
390
+ {
391
+ value: cmd.id,
392
+ keywords: [
393
+ cmd.label,
394
+ cmd.group,
395
+ ...cmd.shortcut ? [cmd.shortcut] : []
396
+ ],
397
+ onSelect: handleSelect,
398
+ children: [
399
+ /* @__PURE__ */ jsx2("span", { children: cmd.label }),
400
+ cmd.shortcut && /* @__PURE__ */ jsx2(CommandShortcut, { children: cmd.shortcut })
401
+ ]
402
+ },
403
+ cmd.id
404
+ )) }, group)) : null
405
+ ] }),
406
+ footer
407
+ ] });
408
+ if (inline) {
409
+ if (!open) return null;
410
+ return /* @__PURE__ */ jsx2(
411
+ Command,
412
+ {
413
+ className: cn(
414
+ "border-border bg-surface w-full max-w-lg border shadow-lg",
415
+ className
416
+ ),
417
+ "data-opt-id": "ATTF5",
418
+ "data-opt-slug": "command-palette.CommandPalette",
419
+ children: body
420
+ }
421
+ );
422
+ }
423
+ return /* @__PURE__ */ jsx2(
424
+ CommandDialog,
425
+ {
426
+ open,
427
+ onOpenChange,
428
+ title: resolvedLabels.title,
429
+ description: resolvedLabels.description,
430
+ className,
431
+ "data-opt-id": "ATTF5",
432
+ "data-opt-slug": "command-palette.CommandPalette",
433
+ children: body
434
+ }
435
+ );
436
+ }
437
+
438
+ // src/shells/command-palette-trigger.tsx
439
+ import { useCallback, useEffect, useState } from "react";
440
+ import { jsx as jsx3 } from "react/jsx-runtime";
441
+ function CommandPaletteTrigger({
442
+ commands,
443
+ onSelect,
444
+ labels
445
+ }) {
446
+ if (commands.length === 0) return null;
447
+ const [open, setOpen] = useState(false);
448
+ const handleKeyDown = useCallback((e) => {
449
+ if ((e.metaKey || e.ctrlKey) && e.key === "k") {
450
+ e.preventDefault();
451
+ setOpen(true);
452
+ }
453
+ }, []);
454
+ useEffect(() => {
455
+ document.addEventListener("keydown", handleKeyDown);
456
+ return () => document.removeEventListener("keydown", handleKeyDown);
457
+ }, [handleKeyDown]);
458
+ return /* @__PURE__ */ jsx3(
459
+ CommandPalette,
460
+ {
461
+ commands,
462
+ open,
463
+ onOpenChange: setOpen,
464
+ onSelect,
465
+ labels,
466
+ "data-opt-id": "4FCDB",
467
+ "data-opt-slug": "command-palette-trigger.CommandPaletteTrigger"
468
+ }
469
+ );
470
+ }
471
+
472
+ // src/shells/dashboard-grid.tsx
473
+ import { jsx as jsx4 } from "react/jsx-runtime";
474
+ var defaultLabels = {
475
+ ariaLabel: "\uB300\uC2DC\uBCF4\uB4DC \uD1B5\uACC4"
476
+ };
477
+ var MIN_DASHBOARD_COLUMNS = 1;
478
+ var MAX_DASHBOARD_COLUMNS = 12;
479
+ function normalizeDashboardColumns(columns) {
480
+ if (!Number.isFinite(columns)) return MIN_DASHBOARD_COLUMNS;
481
+ return Math.min(
482
+ MAX_DASHBOARD_COLUMNS,
483
+ Math.max(MIN_DASHBOARD_COLUMNS, Math.floor(columns))
484
+ );
485
+ }
486
+ function DashboardGrid({
487
+ stats,
488
+ columns = 3,
489
+ labels: customLabels
490
+ }) {
491
+ const labels = __spreadValues(__spreadValues({}, defaultLabels), customLabels);
492
+ const columnCount = normalizeDashboardColumns(columns);
493
+ const rowStyle = {
494
+ "--opt-dashboard-grid-columns": `repeat(${columnCount}, minmax(0, 1fr))`
495
+ };
496
+ const rows = [];
497
+ for (let i = 0; i < stats.length; i += columnCount) {
498
+ rows.push(stats.slice(i, i + columnCount));
499
+ }
500
+ return /* @__PURE__ */ jsx4(
501
+ CompositeProvider,
502
+ {
503
+ focusLoop: true,
504
+ focusWrap: true,
505
+ "data-opt-id": "BM7NA",
506
+ "data-opt-slug": "dashboard-grid.DashboardGrid",
507
+ children: /* @__PURE__ */ jsx4(
508
+ Composite,
509
+ {
510
+ role: "grid",
511
+ "aria-label": labels.ariaLabel,
512
+ className: "gap-group flex flex-col",
513
+ children: rows.map((row, ri) => /* @__PURE__ */ jsx4(
514
+ CompositeRow,
515
+ {
516
+ className: "gap-group grid grid-cols-1 sm:[grid-template-columns:var(--opt-dashboard-grid-columns)]",
517
+ style: rowStyle,
518
+ children: row.map((stat) => /* @__PURE__ */ jsx4(
519
+ CompositeItem,
520
+ {
521
+ className: "data-[active-item]:ring-ring data-[active-item]:ring-offset-ring-offset data-[focus-visible]:ring-ring data-[focus-visible]:ring-offset-ring-offset rounded-xl outline-none data-[active-item]:ring-2 data-[active-item]:ring-offset-2 data-[focus-visible]:ring-2 data-[focus-visible]:ring-offset-2",
522
+ render: /* @__PURE__ */ jsx4("div", { role: "gridcell" }),
523
+ children: /* @__PURE__ */ jsx4(StatCard, __spreadValues({}, stat))
524
+ },
525
+ stat.id
526
+ ))
527
+ },
528
+ ri
529
+ ))
530
+ }
531
+ )
532
+ }
533
+ );
534
+ }
535
+
536
+ // src/shells/search-combobox.tsx
537
+ import { matchSorter } from "match-sorter";
538
+ import { useEffect as useEffect2, useId, useMemo as useMemo2, useRef, useState as useState2 } from "react";
539
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
540
+ var defaultLabels2 = {
541
+ noResults: "\uACB0\uACFC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
542
+ loading: "Loading\u2026"
543
+ };
544
+ function SearchCombobox({
545
+ items,
546
+ placeholder = "\uAC80\uC0C9...",
547
+ label = "\uAC80\uC0C9",
548
+ onSelect,
549
+ labels: customLabels,
550
+ async: isAsync = false,
551
+ onSearch,
552
+ debounceMs = 300
553
+ }) {
554
+ const labels = __spreadValues(__spreadValues({}, defaultLabels2), customLabels);
555
+ const inputId = useId();
556
+ const [query, setQuery] = useState2("");
557
+ const [asyncResults, setAsyncResults] = useState2([]);
558
+ const [loading, setLoading] = useState2(false);
559
+ const timerRef = useRef(void 0);
560
+ const searchGenerationRef = useRef(0);
561
+ const syncMatches = useMemo2(() => {
562
+ if (isAsync) return [];
563
+ if (!query) return items;
564
+ return matchSorter(items, query);
565
+ }, [query, items, isAsync]);
566
+ const handleInput = (event) => {
567
+ searchGenerationRef.current += 1;
568
+ setQuery(event.currentTarget.value);
569
+ };
570
+ const handleValueChange = (item) => {
571
+ searchGenerationRef.current += 1;
572
+ setQuery(item);
573
+ onSelect == null ? void 0 : onSelect(item);
574
+ };
575
+ useEffect2(() => {
576
+ const generation = ++searchGenerationRef.current;
577
+ if (timerRef.current) clearTimeout(timerRef.current);
578
+ if (!isAsync || !onSearch) {
579
+ setLoading(false);
580
+ return;
581
+ }
582
+ if (!query) {
583
+ setAsyncResults([]);
584
+ setLoading(false);
585
+ return;
586
+ }
587
+ setLoading(true);
588
+ timerRef.current = setTimeout(async () => {
589
+ try {
590
+ const results = await onSearch(query);
591
+ if (searchGenerationRef.current !== generation) {
592
+ return;
593
+ }
594
+ setAsyncResults(results);
595
+ } finally {
596
+ if (searchGenerationRef.current === generation) {
597
+ setLoading(false);
598
+ }
599
+ }
600
+ }, debounceMs);
601
+ return () => {
602
+ if (timerRef.current) clearTimeout(timerRef.current);
603
+ };
604
+ }, [query, isAsync, onSearch, debounceMs]);
605
+ const matches = isAsync ? asyncResults : syncMatches;
606
+ return /* @__PURE__ */ jsx5(
607
+ "div",
608
+ {
609
+ className: "gap-element flex flex-col",
610
+ "data-opt-id": "KH4TQ",
611
+ "data-opt-slug": "search-combobox.SearchCombobox",
612
+ children: /* @__PURE__ */ jsxs3(ComboboxRoot, { onValueChange: handleValueChange, children: [
613
+ /* @__PURE__ */ jsx5("label", { htmlFor: inputId, className: OPT_TEXT_LABEL, children: label }),
614
+ /* @__PURE__ */ jsx5(
615
+ ComboboxInput,
616
+ {
617
+ id: inputId,
618
+ placeholder,
619
+ className: "max-w-xs",
620
+ "aria-busy": loading || void 0,
621
+ onInput: handleInput
622
+ }
623
+ ),
624
+ /* @__PURE__ */ jsx5(ComboboxPopover, { className: "max-w-xs", children: isAsync && loading ? /* @__PURE__ */ jsxs3(
625
+ "div",
626
+ {
627
+ className: "text-text-secondary gap-element flex items-center px-3 py-2 text-sm",
628
+ role: "status",
629
+ "aria-live": "polite",
630
+ "aria-busy": "true",
631
+ children: [
632
+ /* @__PURE__ */ jsx5(Spinner, { size: "sm", "aria-hidden": "true" }),
633
+ labels.loading
634
+ ]
635
+ }
636
+ ) : matches.length === 0 ? /* @__PURE__ */ jsx5(ComboboxEmpty, { children: labels.noResults }) : matches.map((item) => /* @__PURE__ */ jsx5(ComboboxItem, { value: item, children: item }, item)) })
637
+ ] })
638
+ }
639
+ );
640
+ }
641
+
642
+ // src/shells/status-select.tsx
643
+ import { useState as useState3 } from "react";
644
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
645
+ var defaultLabels3 = {
646
+ selectLabel: "\uC0C1\uD0DC",
647
+ emptyMessage: "\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uC0C1\uD0DC \uC635\uC158\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."
648
+ };
649
+ function StatusSelect({
650
+ options,
651
+ value: controlledValue,
652
+ onChange,
653
+ label,
654
+ labels: customLabels
655
+ }) {
656
+ var _a, _b;
657
+ const labels = __spreadValues(__spreadValues({}, defaultLabels3), customLabels);
658
+ const resolvedLabel = label != null ? label : labels.selectLabel;
659
+ const [internalValue, setInternalValue] = useState3((_b = (_a = options[0]) == null ? void 0 : _a.value) != null ? _b : "");
660
+ const value = controlledValue != null ? controlledValue : internalValue;
661
+ const selected = options.find((s) => s.value === value);
662
+ if (options.length === 0) {
663
+ return /* @__PURE__ */ jsxs4(
664
+ "div",
665
+ {
666
+ className: "gap-element flex flex-col",
667
+ "data-opt-id": "R46E6",
668
+ "data-opt-slug": "status-select.StatusSelect",
669
+ children: [
670
+ /* @__PURE__ */ jsx6("span", { className: OPT_TEXT_LABEL, children: resolvedLabel }),
671
+ /* @__PURE__ */ jsx6("span", { className: "text-text-tertiary text-sm", children: labels.emptyMessage })
672
+ ]
673
+ }
674
+ );
675
+ }
676
+ const handleChange = (v) => {
677
+ var _a2;
678
+ const next = Array.isArray(v) ? (_a2 = v[0]) != null ? _a2 : "" : v;
679
+ if (onChange) onChange(next);
680
+ else setInternalValue(next);
681
+ };
682
+ return /* @__PURE__ */ jsx6(
683
+ "div",
684
+ {
685
+ className: "gap-element flex flex-col",
686
+ "data-opt-id": "R46E6",
687
+ "data-opt-slug": "status-select.StatusSelect",
688
+ children: /* @__PURE__ */ jsxs4(SelectRoot, { value, onValueChange: handleChange, children: [
689
+ /* @__PURE__ */ jsx6(SelectLabel, { className: OPT_TEXT_LABEL, children: resolvedLabel }),
690
+ /* @__PURE__ */ jsxs4(
691
+ SelectTrigger,
692
+ {
693
+ className: `${OPT_INPUT} max-w-xs items-center justify-between ${OPT_FOCUS}`,
694
+ children: [
695
+ /* @__PURE__ */ jsxs4("span", { className: "gap-element flex items-center", children: [
696
+ (selected == null ? void 0 : selected.color) && /* @__PURE__ */ jsx6("span", { className: `h-2 w-2 rounded-full ${selected.color}` }),
697
+ selected == null ? void 0 : selected.label
698
+ ] }),
699
+ /* @__PURE__ */ jsx6(
700
+ "svg",
701
+ {
702
+ className: "text-text-tertiary h-4 w-4",
703
+ fill: "none",
704
+ viewBox: "0 0 24 24",
705
+ stroke: "currentColor",
706
+ strokeWidth: 2,
707
+ "aria-hidden": "true",
708
+ children: /* @__PURE__ */ jsx6(
709
+ "path",
710
+ {
711
+ strokeLinecap: "round",
712
+ strokeLinejoin: "round",
713
+ d: "m19.5 8.25-7.5 7.5-7.5-7.5"
714
+ }
715
+ )
716
+ }
717
+ )
718
+ ]
719
+ }
720
+ ),
721
+ /* @__PURE__ */ jsx6(
722
+ SelectPopover,
723
+ {
724
+ className: `${OPT_SURFACE_POPOVER} ${OPT_ANIMATE_ENTER_EXIT} max-w-xs`,
725
+ children: options.map((option) => /* @__PURE__ */ jsxs4(SelectItem, { value: option.value, children: [
726
+ option.color && /* @__PURE__ */ jsx6("span", { className: `h-2 w-2 rounded-full ${option.color}` }),
727
+ option.label
728
+ ] }, option.value))
729
+ }
730
+ )
731
+ ] })
732
+ }
733
+ );
734
+ }
735
+
736
+ // src/shells/app-menubar.tsx
737
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
738
+ var defaultLabels4 = {
739
+ ariaLabel: "Application menu"
740
+ };
741
+ function MenuSection({ menu }) {
742
+ return /* @__PURE__ */ jsxs5(MenuRoot, { children: [
743
+ /* @__PURE__ */ jsxs5(MenuTrigger, { children: [
744
+ menu.label,
745
+ /* @__PURE__ */ jsx7(MenuButtonArrow, {})
746
+ ] }),
747
+ /* @__PURE__ */ jsx7(MenuPopover, { children: menu.items.map((item, i) => {
748
+ var _a, _b, _c, _d;
749
+ if (item.type === "separator") {
750
+ return /* @__PURE__ */ jsx7(MenuSeparator, {}, `sep-${i}`);
751
+ }
752
+ if (item.type === "checkbox") {
753
+ return /* @__PURE__ */ jsx7(
754
+ MenuItemCheckbox,
755
+ {
756
+ name: (_a = item.name) != null ? _a : item.id,
757
+ value: (_b = item.value) != null ? _b : item.id,
758
+ checked: item.checked,
759
+ onChange: item.onChange,
760
+ children: /* @__PURE__ */ jsxs5("span", { className: "gap-element flex items-center", children: [
761
+ /* @__PURE__ */ jsx7("span", { className: "w-4 text-center", children: item.checked ? "\u2713" : "" }),
762
+ item.label
763
+ ] })
764
+ },
765
+ item.id
766
+ );
767
+ }
768
+ if (item.type === "radio") {
769
+ return /* @__PURE__ */ jsx7(
770
+ MenuItemRadio,
771
+ {
772
+ name: (_c = item.name) != null ? _c : "",
773
+ value: (_d = item.value) != null ? _d : "",
774
+ checked: item.checked,
775
+ onChange: item.onChange,
776
+ children: /* @__PURE__ */ jsxs5("span", { className: "gap-element flex items-center", children: [
777
+ /* @__PURE__ */ jsx7("span", { className: "w-4 text-center", children: item.checked ? "\u25CF" : "" }),
778
+ item.label
779
+ ] })
780
+ },
781
+ item.id
782
+ );
783
+ }
784
+ return /* @__PURE__ */ jsxs5(MenuItem, { onClick: item.onClick, children: [
785
+ /* @__PURE__ */ jsx7("span", { children: item.label }),
786
+ item.shortcut && /* @__PURE__ */ jsx7(Kbd, { children: item.shortcut })
787
+ ] }, item.id);
788
+ }) })
789
+ ] });
790
+ }
791
+ function AppMenubar({ menus, labels: customLabels }) {
792
+ const labels = __spreadValues(__spreadValues({}, defaultLabels4), customLabels);
793
+ if (menus.length === 0) return null;
794
+ return /* @__PURE__ */ jsx7(MenubarRoot, { "data-opt-id": "BGKHW", "data-opt-slug": "app-menubar.AppMenubar", children: /* @__PURE__ */ jsx7(MenubarContainer, { "aria-label": labels.ariaLabel, children: menus.map((menu) => /* @__PURE__ */ jsx7(MenuSection, { menu }, menu.id)) }) });
795
+ }
796
+ AppMenubar.displayName = "AppMenubar";
797
+
798
+ // src/shells/sidebar-nav.tsx
799
+ import Link from "next/link";
800
+ import { usePathname } from "next/navigation";
801
+ import { useEffect as useEffect3, useState as useState4 } from "react";
802
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
803
+ var NAV_ID_PREFIX = "sidebar-nav-";
804
+ function domId(id) {
805
+ return `${NAV_ID_PREFIX}${id}`;
806
+ }
807
+ var sectionStyles = {
808
+ // 한글 키
809
+ \uC2DC\uC791\uD558\uAE30: "text-accent",
810
+ "\uD575\uC2EC \uAC1C\uB150": "text-info-fg",
811
+ "\uAD6C\uCD95\xB7\uC6B4\uC601": "text-success-fg",
812
+ \uB3C4\uAD6C: "text-warning-fg",
813
+ DataGrid: "text-accent",
814
+ "\uCEF4\uD3EC\uB10C\uD2B8 \xB7 Core": "text-accent",
815
+ "\uCEF4\uD3EC\uB10C\uD2B8 \xB7 Visuals": "text-success-fg",
816
+ "\uCEF4\uD3EC\uB10C\uD2B8 \xB7 Shells": "text-warning-fg",
817
+ "\uCEF4\uD3EC\uB10C\uD2B8 \xB7 Surfaces": "text-info-fg",
818
+ "\uAE30\uBCF8 \uCEF4\uD3EC\uB10C\uD2B8": "text-accent",
819
+ "\uBE14\uB85D \uCEF4\uD3EC\uB10C\uD2B8": "text-info-fg",
820
+ "\uC870\uD569 \uCEF4\uD3EC\uB10C\uD2B8": "text-warning-fg",
821
+ \uD15C\uD50C\uB9BF: "text-text-tertiary",
822
+ \uB9B4\uB9AC\uC988: "text-text-tertiary",
823
+ // 영문 키 (하위 호환)
824
+ Primitives: "text-accent",
825
+ Blocks: "text-info-fg",
826
+ Compositions: "text-warning-fg",
827
+ Templates: "text-text-tertiary"
828
+ };
829
+ var defaultSectionStyle = "text-text-secondary";
830
+ var defaultLabels5 = {
831
+ ariaLabel: "\uBB38\uC11C \uB124\uBE44\uAC8C\uC774\uC158"
832
+ };
833
+ function SidebarNav({ items, labels: customLabels }) {
834
+ const labels = __spreadValues(__spreadValues({}, defaultLabels5), customLabels);
835
+ if (items.length === 0) {
836
+ return null;
837
+ }
838
+ const pathname = usePathname();
839
+ const activeItem = items.find((item) => item.href === pathname);
840
+ const [activeId, setActiveId] = useState4(
841
+ activeItem ? domId(activeItem.id) : void 0
842
+ );
843
+ useEffect3(() => {
844
+ const match = items.find((item) => item.href === pathname);
845
+ if (match) {
846
+ setActiveId(domId(match.id));
847
+ }
848
+ }, [pathname, items]);
849
+ return /* @__PURE__ */ jsx8(
850
+ CompositeProvider,
851
+ {
852
+ focusLoop: true,
853
+ orientation: "vertical",
854
+ activeId,
855
+ onActiveIdChange: setActiveId,
856
+ "data-opt-id": "1FZNH",
857
+ "data-opt-slug": "sidebar-nav.SidebarNav",
858
+ children: /* @__PURE__ */ jsx8(
859
+ Composite,
860
+ {
861
+ role: "navigation",
862
+ "aria-label": labels.ariaLabel,
863
+ className: "flex flex-col",
864
+ children: items.map((item, index) => {
865
+ var _a, _b;
866
+ const isSelected = pathname === item.href;
867
+ const isFirstItem = index === 0;
868
+ const sectionTextStyle = item.sectionLabel ? (_a = sectionStyles[item.sectionLabel]) != null ? _a : defaultSectionStyle : defaultSectionStyle;
869
+ return /* @__PURE__ */ jsxs6("div", { children: [
870
+ item.separator && /* @__PURE__ */ jsx8("div", { className: cn("mb-2 pt-2", !isFirstItem && "mt-7"), children: /* @__PURE__ */ jsx8(
871
+ "span",
872
+ {
873
+ className: cn(
874
+ "px-2 text-[11px] font-semibold tracking-wider uppercase",
875
+ sectionTextStyle
876
+ ),
877
+ children: (_b = item.sectionLabel) != null ? _b : ""
878
+ }
879
+ ) }),
880
+ /* @__PURE__ */ jsxs6(
881
+ CompositeItem,
882
+ {
883
+ id: domId(item.id),
884
+ render: /* @__PURE__ */ jsx8(
885
+ Link,
886
+ {
887
+ href: item.href,
888
+ className: cn(
889
+ // Base styles
890
+ "group relative block rounded-md px-2 py-1.5 text-[13px] leading-snug outline-none",
891
+ "transition-all duration-150",
892
+ // Default state
893
+ !isSelected && [
894
+ "text-text-secondary",
895
+ // Hover
896
+ "hover:bg-bg-subtle hover:text-text-primary"
897
+ ],
898
+ // Selected (current page)
899
+ isSelected && [
900
+ "bg-bg-subtle text-text-primary font-medium"
901
+ ],
902
+ // Keyboard navigation focus (roving tabindex active)
903
+ "data-[active-item]:bg-accent-subtle data-[active-item]:text-accent",
904
+ // Focus visible (actual keyboard focus indicator)
905
+ "data-[focus-visible]:ring-ring data-[focus-visible]:ring-2",
906
+ "data-[focus-visible]:ring-offset-ring-offset data-[focus-visible]:ring-offset-2",
907
+ // Combined: selected + keyboard active
908
+ isSelected && [
909
+ "data-[active-item]:bg-accent-subtle data-[active-item]:text-accent"
910
+ ]
911
+ )
912
+ }
913
+ ),
914
+ children: [
915
+ /* @__PURE__ */ jsx8(
916
+ "span",
917
+ {
918
+ className: cn(
919
+ "absolute top-1/2 left-0 h-4 w-0.5 -translate-y-1/2 rounded-full transition-all duration-150",
920
+ // Selected: solid accent bar
921
+ isSelected && "bg-accent",
922
+ // Keyboard active (not selected): lighter bar
923
+ !isSelected && "group-data-[active-item]:bg-accent bg-transparent",
924
+ // Focus visible: ensure bar is visible
925
+ "group-data-[focus-visible]:bg-accent"
926
+ )
927
+ }
928
+ ),
929
+ /* @__PURE__ */ jsx8("span", { className: "pl-2", children: item.label })
930
+ ]
931
+ }
932
+ )
933
+ ] }, item.id);
934
+ })
935
+ }
936
+ )
937
+ }
938
+ );
939
+ }
940
+
941
+ // src/shells/editor-toolbar.tsx
942
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
943
+ var defaultLabels6 = {
944
+ ariaLabel: "\uD14D\uC2A4\uD2B8 \uD3B8\uC9D1\uAE30 \uD234\uBC14"
945
+ };
946
+ function ToolbarButtonWithTooltip({
947
+ button,
948
+ active,
949
+ onAction
950
+ }) {
951
+ return /* @__PURE__ */ jsxs7(TooltipProvider, { children: [
952
+ /* @__PURE__ */ jsx9(TooltipAnchor, { children: /* @__PURE__ */ jsx9(
953
+ ToolbarButton,
954
+ {
955
+ className: `text-text-primary data-[active-item]:bg-bg-subtle data-[focus-visible]:ring-ring rounded-md p-2 transition-colors outline-none data-[focus-visible]:ring-2 ${active ? "bg-bg-muted" : "hover:bg-bg-subtle"}`,
956
+ onClick: () => onAction == null ? void 0 : onAction(button.id),
957
+ children: button.icon
958
+ }
959
+ ) }),
960
+ /* @__PURE__ */ jsx9(Tooltip, { children: button.label })
961
+ ] });
962
+ }
963
+ function EditorToolbar({
964
+ buttons,
965
+ onAction,
966
+ activeButtons = [],
967
+ labels: customLabels
968
+ }) {
969
+ const labels = __spreadValues(__spreadValues({}, defaultLabels6), customLabels);
970
+ const groups = [];
971
+ let current = [];
972
+ for (const btn of buttons) {
973
+ if (btn.separator) {
974
+ groups.push(current);
975
+ current = [];
976
+ }
977
+ current.push(btn);
978
+ }
979
+ if (current.length) groups.push(current);
980
+ return /* @__PURE__ */ jsx9(
981
+ ToolbarRoot,
982
+ {
983
+ "data-opt-id": "Z73K6",
984
+ "data-opt-slug": "editor-toolbar.EditorToolbar",
985
+ children: /* @__PURE__ */ jsx9(ToolbarContainer, { "aria-label": labels.ariaLabel, children: groups.map((group, gi) => /* @__PURE__ */ jsxs7("span", { className: "contents", children: [
986
+ gi > 0 && /* @__PURE__ */ jsx9(ToolbarSeparator, {}),
987
+ group.map((item) => {
988
+ if (item === "separator") return null;
989
+ const btn = item;
990
+ return /* @__PURE__ */ jsx9(
991
+ ToolbarButtonWithTooltip,
992
+ {
993
+ button: btn,
994
+ active: activeButtons.includes(btn.id),
995
+ onAction
996
+ },
997
+ btn.id
998
+ );
999
+ })
1000
+ ] }, gi)) })
1001
+ }
1002
+ );
1003
+ }
1004
+
1005
+ // src/shells/content-tabs.tsx
1006
+ import { useState as useState5 } from "react";
1007
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
1008
+ var defaultLabels7 = {
1009
+ ariaLabel: "\uCF58\uD150\uCE20 \uD0ED",
1010
+ emptyMessage: "\uD45C\uC2DC\uD560 \uD0ED\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."
1011
+ };
1012
+ function ContentTabs({
1013
+ tabs,
1014
+ defaultTabId,
1015
+ labels: customLabels
1016
+ }) {
1017
+ var _a, _b;
1018
+ const labels = __spreadValues(__spreadValues({}, defaultLabels7), customLabels);
1019
+ if (tabs.length === 0) {
1020
+ return /* @__PURE__ */ jsx10(
1021
+ "div",
1022
+ {
1023
+ className: "text-text-secondary text-sm",
1024
+ "data-opt-id": "9W97K",
1025
+ "data-opt-slug": "content-tabs.ContentTabs",
1026
+ children: labels.emptyMessage
1027
+ }
1028
+ );
1029
+ }
1030
+ const [selectedTabId, setSelectedTabId] = useState5(
1031
+ defaultTabId != null ? defaultTabId : (_a = tabs[0]) == null ? void 0 : _a.id
1032
+ );
1033
+ const handleSelectedTabId = (nextId) => {
1034
+ var _a2;
1035
+ setSelectedTabId(nextId != null ? nextId : (_a2 = tabs[0]) == null ? void 0 : _a2.id);
1036
+ };
1037
+ const activeTab = (_b = tabs.find((tab) => tab.id === selectedTabId)) != null ? _b : tabs[0];
1038
+ return /* @__PURE__ */ jsxs8(
1039
+ TabsRoot,
1040
+ {
1041
+ selectedId: selectedTabId,
1042
+ onSelectedIdChange: handleSelectedTabId,
1043
+ "data-opt-id": "9W97K",
1044
+ "data-opt-slug": "content-tabs.ContentTabs",
1045
+ children: [
1046
+ /* @__PURE__ */ jsx10(TabList, { "aria-label": labels.ariaLabel, children: tabs.map((tab) => /* @__PURE__ */ jsx10(Tab, { id: tab.id, children: tab.label }, tab.id)) }),
1047
+ activeTab && /* @__PURE__ */ jsx10(TabPanel, { tabId: activeTab.id, children: activeTab.content })
1048
+ ]
1049
+ }
1050
+ );
1051
+ }
1052
+
1053
+ // src/shells/faq-accordion.tsx
1054
+ import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1055
+ var defaultLabels8 = {
1056
+ ariaLabel: "\uC790\uC8FC \uBB3B\uB294 \uC9C8\uBB38",
1057
+ emptyMessage: "\uB4F1\uB85D\uB41C FAQ\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."
1058
+ };
1059
+ function FaqAccordion({
1060
+ items,
1061
+ labels: customLabels
1062
+ }) {
1063
+ const labels = __spreadValues(__spreadValues({}, defaultLabels8), customLabels);
1064
+ if (items.length === 0) {
1065
+ return /* @__PURE__ */ jsx11(
1066
+ "div",
1067
+ {
1068
+ className: "text-text-secondary text-sm",
1069
+ "data-opt-id": "QRBHV",
1070
+ "data-opt-slug": "faq-accordion.FaqAccordion",
1071
+ children: labels.emptyMessage
1072
+ }
1073
+ );
1074
+ }
1075
+ return /* @__PURE__ */ jsx11(
1076
+ CompositeProvider,
1077
+ {
1078
+ focusLoop: true,
1079
+ orientation: "vertical",
1080
+ "data-opt-id": "QRBHV",
1081
+ "data-opt-slug": "faq-accordion.FaqAccordion",
1082
+ children: /* @__PURE__ */ jsx11(
1083
+ Composite,
1084
+ {
1085
+ role: "region",
1086
+ "aria-label": labels.ariaLabel,
1087
+ className: "divide-border-subtle border-border divide-y rounded-lg border",
1088
+ children: items.map((faq) => /* @__PURE__ */ jsx11(DisclosureRoot, { children: /* @__PURE__ */ jsxs9("div", { children: [
1089
+ /* @__PURE__ */ jsxs9(CompositeItem, { render: /* @__PURE__ */ jsx11(DisclosureTrigger, {}), children: [
1090
+ faq.question,
1091
+ /* @__PURE__ */ jsx11(
1092
+ "svg",
1093
+ {
1094
+ className: "text-text-tertiary h-4 w-4 shrink-0 transition-transform [[data-open]>&]:rotate-180",
1095
+ fill: "none",
1096
+ viewBox: "0 0 24 24",
1097
+ stroke: "currentColor",
1098
+ strokeWidth: 2,
1099
+ "aria-hidden": "true",
1100
+ children: /* @__PURE__ */ jsx11(
1101
+ "path",
1102
+ {
1103
+ strokeLinecap: "round",
1104
+ strokeLinejoin: "round",
1105
+ d: "m19.5 8.25-7.5 7.5-7.5-7.5"
1106
+ }
1107
+ )
1108
+ }
1109
+ )
1110
+ ] }),
1111
+ /* @__PURE__ */ jsx11(DisclosureContent, { children: /* @__PURE__ */ jsx11("p", { className: "text-text-secondary px-4 pt-2 pb-4 text-sm", children: faq.answer }) })
1112
+ ] }) }, faq.id))
1113
+ }
1114
+ )
1115
+ }
1116
+ );
1117
+ }
1118
+
1119
+ // src/shells/settings-form.tsx
1120
+ import { useState as useState6 } from "react";
1121
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1122
+ var defaultLabels9 = {
1123
+ formAriaLabel: "\uC124\uC815 \uD3FC",
1124
+ submitButton: "\uC800\uC7A5",
1125
+ successMessage: "\uC124\uC815\uC774 \uC800\uC7A5\uB418\uC5C8\uC2B5\uB2C8\uB2E4."
1126
+ };
1127
+ function SettingsForm({
1128
+ fields,
1129
+ onSubmit,
1130
+ labels: customLabels
1131
+ }) {
1132
+ var _a;
1133
+ const labels = __spreadValues(__spreadValues({}, defaultLabels9), customLabels);
1134
+ const [submitted, setSubmitted] = useState6(false);
1135
+ if (fields.length === 0) {
1136
+ return /* @__PURE__ */ jsx12(
1137
+ EmptyState,
1138
+ {
1139
+ title: "\uC124\uC815 \uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
1140
+ description: "\uD45C\uC2DC\uD560 \uC124\uC815 \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uBA74 \uC774 \uD3FC\uC774 \uB80C\uB354\uB9C1\uB429\uB2C8\uB2E4.",
1141
+ "data-opt-id": "9TEEJ",
1142
+ "data-opt-slug": "settings-form.SettingsForm"
1143
+ }
1144
+ );
1145
+ }
1146
+ const defaultValues = {};
1147
+ for (const field of fields) {
1148
+ defaultValues[field.name] = (_a = field.defaultValue) != null ? _a : field.type === "switch" || field.type === "checkbox" ? false : field.type === "tags" ? [] : "";
1149
+ }
1150
+ const form = useFormStore({
1151
+ defaultValues,
1152
+ validate: (values) => {
1153
+ var _a2;
1154
+ const errors = {};
1155
+ for (const field of fields) {
1156
+ if (field.required) {
1157
+ const value = values[field.name];
1158
+ if (!value || typeof value === "string" && !value.trim()) {
1159
+ errors[field.name] = (_a2 = field.requiredMessage) != null ? _a2 : `${field.label}\uC744(\uB97C) \uC785\uB825\uD574\uC8FC\uC138\uC694.`;
1160
+ }
1161
+ }
1162
+ if (field.validate) {
1163
+ const value = values[field.name];
1164
+ const error = field.validate(value);
1165
+ if (error) {
1166
+ errors[field.name] = error;
1167
+ }
1168
+ }
1169
+ }
1170
+ return errors;
1171
+ },
1172
+ onSubmit: (values) => {
1173
+ setSubmitted(true);
1174
+ onSubmit == null ? void 0 : onSubmit(values);
1175
+ }
1176
+ });
1177
+ return /* @__PURE__ */ jsxs10(
1178
+ "div",
1179
+ {
1180
+ className: "gap-group flex flex-col",
1181
+ "data-opt-id": "9TEEJ",
1182
+ "data-opt-slug": "settings-form.SettingsForm",
1183
+ children: [
1184
+ /* @__PURE__ */ jsxs10(FormRoot, { store: form, "aria-label": labels.formAriaLabel, children: [
1185
+ fields.map((field) => {
1186
+ var _a2, _b, _c, _d, _e;
1187
+ if (field.visible !== void 0) {
1188
+ const currentValues = form.getValues();
1189
+ const isVisible = typeof field.visible === "function" ? field.visible(currentValues) : field.visible;
1190
+ if (!isVisible) return null;
1191
+ }
1192
+ if (field.type === "tags") {
1193
+ const rawValue = form.getValue(field.name);
1194
+ const tags = Array.isArray(rawValue) ? rawValue : [];
1195
+ return /* @__PURE__ */ jsxs10("div", { className: "gap-element flex flex-col", children: [
1196
+ /* @__PURE__ */ jsx12(FormLabel, { name: field.name, children: field.label }),
1197
+ /* @__PURE__ */ jsx12(
1198
+ TagInput,
1199
+ {
1200
+ value: tags,
1201
+ onChange: (next) => form.setValue(field.name, next),
1202
+ placeholder: field.placeholder
1203
+ }
1204
+ ),
1205
+ /* @__PURE__ */ jsx12(FormError, { name: field.name })
1206
+ ] }, field.name);
1207
+ }
1208
+ if (field.type === "radio") {
1209
+ return /* @__PURE__ */ jsxs10("fieldset", { className: "gap-element flex flex-col", children: [
1210
+ /* @__PURE__ */ jsx12("legend", { className: "text-text-primary text-sm font-medium", children: field.label }),
1211
+ /* @__PURE__ */ jsx12(FormRadioGroup, { children: /* @__PURE__ */ jsx12("div", { className: "gap-group flex", children: (_a2 = field.options) == null ? void 0 : _a2.map((option) => /* @__PURE__ */ jsxs10(
1212
+ "label",
1213
+ {
1214
+ className: "text-text-primary gap-element flex items-center text-sm",
1215
+ children: [
1216
+ /* @__PURE__ */ jsx12(FormRadio, { name: field.name, value: option.value }),
1217
+ option.label
1218
+ ]
1219
+ },
1220
+ option.value
1221
+ )) }) }),
1222
+ /* @__PURE__ */ jsx12(FormError, { name: field.name })
1223
+ ] }, field.name);
1224
+ }
1225
+ if (field.type === "checkbox") {
1226
+ return /* @__PURE__ */ jsxs10("div", { className: "gap-element flex flex-col", children: [
1227
+ /* @__PURE__ */ jsxs10("label", { className: "text-text-primary gap-element flex items-center text-sm", children: [
1228
+ /* @__PURE__ */ jsx12(FormCheckbox, { name: field.name }),
1229
+ /* @__PURE__ */ jsx12("span", { children: field.label })
1230
+ ] }),
1231
+ /* @__PURE__ */ jsx12(FormError, { name: field.name })
1232
+ ] }, field.name);
1233
+ }
1234
+ if (field.type === "textarea") {
1235
+ return /* @__PURE__ */ jsxs10("div", { className: "gap-element flex flex-col", children: [
1236
+ /* @__PURE__ */ jsx12(FormLabel, { name: field.name, children: field.label }),
1237
+ /* @__PURE__ */ jsx12(
1238
+ FormTextarea,
1239
+ {
1240
+ name: field.name,
1241
+ placeholder: field.placeholder,
1242
+ rows: (_b = field.rows) != null ? _b : 4,
1243
+ resize: (_c = field.resize) != null ? _c : "vertical",
1244
+ className: field.font === "mono" ? "font-mono text-sm" : void 0
1245
+ }
1246
+ ),
1247
+ /* @__PURE__ */ jsx12(FormError, { name: field.name })
1248
+ ] }, field.name);
1249
+ }
1250
+ if (field.type === "select") {
1251
+ return /* @__PURE__ */ jsxs10("div", { className: "gap-element flex flex-col", children: [
1252
+ /* @__PURE__ */ jsx12(FormLabel, { name: field.name, children: field.label }),
1253
+ /* @__PURE__ */ jsx12(FormSelect, { name: field.name, placeholder: field.placeholder, children: /* @__PURE__ */ jsx12(FormSelectPopover, { children: (_d = field.options) == null ? void 0 : _d.map((option) => /* @__PURE__ */ jsx12(FormSelectItem, { value: option.value, children: option.label }, option.value)) }) }),
1254
+ /* @__PURE__ */ jsx12(FormError, { name: field.name })
1255
+ ] }, field.name);
1256
+ }
1257
+ if (field.type === "switch") {
1258
+ return /* @__PURE__ */ jsxs10("label", { className: "gap-group flex items-center", children: [
1259
+ /* @__PURE__ */ jsx12(FormSwitch, { name: field.name }),
1260
+ /* @__PURE__ */ jsxs10("div", { className: "flex flex-col", children: [
1261
+ /* @__PURE__ */ jsx12("span", { className: "text-text-primary text-sm font-medium", children: field.label }),
1262
+ field.description && /* @__PURE__ */ jsx12("span", { className: "text-text-secondary text-sm", children: field.description })
1263
+ ] })
1264
+ ] }, field.name);
1265
+ }
1266
+ return /* @__PURE__ */ jsxs10("div", { className: "gap-element flex flex-col", children: [
1267
+ /* @__PURE__ */ jsx12(FormLabel, { name: field.name, children: field.label }),
1268
+ /* @__PURE__ */ jsx12(
1269
+ FormInput,
1270
+ {
1271
+ name: field.name,
1272
+ type: (_e = field.type) != null ? _e : "text",
1273
+ placeholder: field.placeholder
1274
+ }
1275
+ ),
1276
+ /* @__PURE__ */ jsx12(FormError, { name: field.name })
1277
+ ] }, field.name);
1278
+ }),
1279
+ /* @__PURE__ */ jsx12(FormSubmit, { children: labels.submitButton })
1280
+ ] }),
1281
+ submitted && /* @__PURE__ */ jsx12("div", { className: "border-success bg-success-subtle text-success-fg rounded-lg border p-3 text-sm", children: labels.successMessage })
1282
+ ]
1283
+ }
1284
+ );
1285
+ }
1286
+
1287
+ // src/shells/deployment-timeline.tsx
1288
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1289
+ var statusIcon = {
1290
+ success: "\u25CF",
1291
+ pending: "\u25CC",
1292
+ running: "\u27F3",
1293
+ error: "\u2717",
1294
+ skipped: "\u25CB"
1295
+ };
1296
+ var statusColor = {
1297
+ success: "text-success-fg",
1298
+ pending: OPT_TEXT_TERTIARY,
1299
+ running: "text-accent",
1300
+ error: "text-danger-fg",
1301
+ skipped: "text-text-tertiary"
1302
+ };
1303
+ var statusBadge = {
1304
+ success: OPT_BADGE_GREEN,
1305
+ pending: OPT_BADGE_NEUTRAL,
1306
+ running: "inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium bg-info-subtle text-info-fg",
1307
+ error: OPT_BADGE_RED,
1308
+ skipped: OPT_BADGE_NEUTRAL
1309
+ };
1310
+ var statusLabel = {
1311
+ success: "\uC131\uACF5",
1312
+ pending: "\uB300\uAE30",
1313
+ running: "\uC2E4\uD589 \uC911",
1314
+ error: "\uC5D0\uB7EC",
1315
+ skipped: "\uAC74\uB108\uB700"
1316
+ };
1317
+ function DeploymentTimeline({
1318
+ steps,
1319
+ title = "\uBC30\uD3EC \uD0C0\uC784\uB77C\uC778"
1320
+ }) {
1321
+ return /* @__PURE__ */ jsxs11(
1322
+ "div",
1323
+ {
1324
+ className: "gap-group flex flex-col",
1325
+ "data-opt-id": "XXVQ2",
1326
+ "data-opt-slug": "deployment-timeline.DeploymentTimeline",
1327
+ children: [
1328
+ title && /* @__PURE__ */ jsx13("h3", { className: `text-sm font-semibold ${OPT_TEXT_PRIMARY}`, children: title }),
1329
+ /* @__PURE__ */ jsx13(CompositeProvider, { focusLoop: true, orientation: "vertical", children: /* @__PURE__ */ jsx13(Composite, { role: "list", "aria-label": title, className: "relative", children: steps.map((step, i) => {
1330
+ const isLast = i === steps.length - 1;
1331
+ return /* @__PURE__ */ jsxs11(
1332
+ CompositeItem,
1333
+ {
1334
+ className: `group gap-group relative flex w-full scroll-m-2 rounded-lg px-3 py-2 ${OPT_FOCUS} data-[active-item]:bg-accent-subtle`,
1335
+ role: "listitem",
1336
+ children: [
1337
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-col items-center", children: [
1338
+ /* @__PURE__ */ jsx13(
1339
+ "span",
1340
+ {
1341
+ className: `text-lg leading-none ${statusColor[step.status]}`,
1342
+ "aria-label": statusLabel[step.status],
1343
+ children: statusIcon[step.status]
1344
+ }
1345
+ ),
1346
+ !isLast && /* @__PURE__ */ jsx13("div", { className: "bg-border-subtle mt-1 w-px flex-1" })
1347
+ ] }),
1348
+ /* @__PURE__ */ jsxs11("div", { className: "flex-1 pb-3", children: [
1349
+ /* @__PURE__ */ jsxs11("div", { className: "gap-element flex items-center", children: [
1350
+ /* @__PURE__ */ jsx13("span", { className: `text-sm font-medium ${OPT_TEXT_PRIMARY}`, children: step.name }),
1351
+ /* @__PURE__ */ jsx13("span", { className: statusBadge[step.status], children: statusLabel[step.status] }),
1352
+ step.duration && /* @__PURE__ */ jsx13("span", { className: `text-xs ${OPT_TEXT_TERTIARY}`, children: step.duration })
1353
+ ] }),
1354
+ step.detail && /* @__PURE__ */ jsx13("p", { className: `mt-0.5 text-xs ${OPT_TEXT_SECONDARY}`, children: step.detail })
1355
+ ] })
1356
+ ]
1357
+ },
1358
+ step.id
1359
+ );
1360
+ }) }) })
1361
+ ]
1362
+ }
1363
+ );
1364
+ }
1365
+
1366
+ // src/shells/project-switcher.tsx
1367
+ import { matchSorter as matchSorter2 } from "match-sorter";
1368
+ import { useMemo as useMemo3, useState as useState7 } from "react";
1369
+ import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1370
+ var statusDot = {
1371
+ active: OPT_DOT_GREEN,
1372
+ paused: OPT_DOT_YELLOW,
1373
+ error: OPT_DOT_RED
1374
+ };
1375
+ function ProjectSwitcher({
1376
+ projects,
1377
+ value,
1378
+ onChange,
1379
+ placeholder = "\uD504\uB85C\uC81D\uD2B8 \uAC80\uC0C9..."
1380
+ }) {
1381
+ const [search, setSearch] = useState7("");
1382
+ const matches = useMemo3(() => {
1383
+ if (!search) return projects;
1384
+ return matchSorter2(projects, search, { keys: ["name", "framework"] });
1385
+ }, [search, projects]);
1386
+ const selected = projects.find((p) => p.id === value);
1387
+ return /* @__PURE__ */ jsx14(
1388
+ "div",
1389
+ {
1390
+ className: "gap-element flex flex-col",
1391
+ "data-opt-id": "5R3AG",
1392
+ "data-opt-slug": "project-switcher.ProjectSwitcher",
1393
+ children: /* @__PURE__ */ jsxs12(ComboboxRoot, { onValueChange: setSearch, children: [
1394
+ /* @__PURE__ */ jsx14("label", { className: OPT_TEXT_LABEL, children: "\uD504\uB85C\uC81D\uD2B8" }),
1395
+ /* @__PURE__ */ jsx14(
1396
+ ComboboxInput,
1397
+ {
1398
+ placeholder: selected ? selected.name : placeholder,
1399
+ className: OPT_INPUT
1400
+ }
1401
+ ),
1402
+ /* @__PURE__ */ jsxs12(ComboboxPopover, { className: "max-h-72 overflow-y-auto", children: [
1403
+ matches.length === 0 && /* @__PURE__ */ jsx14("p", { className: `py-6 text-center text-sm ${OPT_TEXT_SECONDARY}`, children: "\uACB0\uACFC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." }),
1404
+ matches.map((project) => /* @__PURE__ */ jsxs12(
1405
+ ComboboxItem,
1406
+ {
1407
+ value: project.name,
1408
+ onClick: () => onChange == null ? void 0 : onChange(project.id),
1409
+ className: `gap-element flex scroll-m-2 items-center ${OPT_ITEM_BASE} ${OPT_ACTIVE_ITEM}`,
1410
+ children: [
1411
+ /* @__PURE__ */ jsx14("span", { className: `shrink-0 ${statusDot[project.status]}` }),
1412
+ /* @__PURE__ */ jsx14("span", { className: "flex-1 truncate font-medium", children: project.name }),
1413
+ /* @__PURE__ */ jsx14("span", { className: OPT_BADGE_NEUTRAL, children: project.framework }),
1414
+ project.domain && /* @__PURE__ */ jsx14(
1415
+ "span",
1416
+ {
1417
+ className: `truncate text-xs ${OPT_TEXT_SECONDARY} data-[active-item]:text-accent-fg/70`,
1418
+ children: project.domain
1419
+ }
1420
+ )
1421
+ ]
1422
+ },
1423
+ project.id
1424
+ ))
1425
+ ] })
1426
+ ] })
1427
+ }
1428
+ );
1429
+ }
1430
+ ProjectSwitcher.displayName = "ProjectSwitcher";
1431
+
1432
+ // src/shells/env-panel.tsx
1433
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
1434
+ var defaultLabels10 = {
1435
+ varCount: (count) => `${count}\uAC1C \uBCC0\uC218`,
1436
+ envVarsLabel: (env) => `${env} \uD658\uACBD \uBCC0\uC218`,
1437
+ secretBadge: "secret"
1438
+ };
1439
+ var envDot = {
1440
+ Production: OPT_DOT_RED,
1441
+ Preview: OPT_DOT_YELLOW,
1442
+ Development: OPT_DOT_GREEN
1443
+ };
1444
+ function EnvPanel({ groups, labels: customLabels }) {
1445
+ const labels = __spreadValues(__spreadValues({}, defaultLabels10), customLabels);
1446
+ return /* @__PURE__ */ jsx15(
1447
+ "div",
1448
+ {
1449
+ className: `rounded-lg ${OPT_BORDER} divide-y ${OPT_DIVIDER} bg-surface`,
1450
+ "data-opt-id": "ZBDWK",
1451
+ "data-opt-slug": "env-panel.EnvPanel",
1452
+ children: groups.map((group) => {
1453
+ var _a;
1454
+ return /* @__PURE__ */ jsx15(
1455
+ DisclosureRoot,
1456
+ {
1457
+ defaultOpen: group.env === "Production",
1458
+ children: /* @__PURE__ */ jsxs13("div", { children: [
1459
+ /* @__PURE__ */ jsxs13(DisclosureTrigger, { className: OPT_DISCLOSURE_TRIGGER, children: [
1460
+ /* @__PURE__ */ jsxs13("span", { className: "gap-element flex items-center", children: [
1461
+ /* @__PURE__ */ jsx15(
1462
+ "span",
1463
+ {
1464
+ className: (_a = envDot[group.env]) != null ? _a : "bg-bg-muted h-2 w-2 rounded-full"
1465
+ }
1466
+ ),
1467
+ /* @__PURE__ */ jsx15("span", { className: "font-medium", children: group.env }),
1468
+ /* @__PURE__ */ jsx15("span", { className: `text-xs ${OPT_TEXT_TERTIARY}`, children: labels.varCount(group.vars.length) })
1469
+ ] }),
1470
+ /* @__PURE__ */ jsx15(
1471
+ "svg",
1472
+ {
1473
+ className: "text-text-tertiary h-4 w-4 shrink-0 transition-transform [[data-open]>&]:rotate-180",
1474
+ fill: "none",
1475
+ viewBox: "0 0 24 24",
1476
+ stroke: "currentColor",
1477
+ strokeWidth: 2,
1478
+ "aria-hidden": "true",
1479
+ children: /* @__PURE__ */ jsx15(
1480
+ "path",
1481
+ {
1482
+ strokeLinecap: "round",
1483
+ strokeLinejoin: "round",
1484
+ d: "m19.5 8.25-7.5 7.5-7.5-7.5"
1485
+ }
1486
+ )
1487
+ }
1488
+ )
1489
+ ] }),
1490
+ /* @__PURE__ */ jsx15(DisclosureContent, { className: OPT_DISCLOSURE_CONTENT, children: /* @__PURE__ */ jsx15(CompositeProvider, { focusLoop: true, orientation: "vertical", children: /* @__PURE__ */ jsx15(
1491
+ Composite,
1492
+ {
1493
+ role: "list",
1494
+ "aria-label": labels.envVarsLabel(group.env),
1495
+ className: "px-4 pb-3",
1496
+ children: group.vars.map((v) => /* @__PURE__ */ jsxs13(
1497
+ CompositeItem,
1498
+ {
1499
+ role: "listitem",
1500
+ className: `gap-group flex scroll-m-2 items-center rounded px-2 py-1.5 text-sm ${OPT_FOCUS} data-[active-item]:bg-accent-subtle`,
1501
+ children: [
1502
+ /* @__PURE__ */ jsx15(
1503
+ "span",
1504
+ {
1505
+ className: `w-44 shrink-0 truncate font-mono text-xs font-medium ${OPT_TEXT_PRIMARY}`,
1506
+ children: v.key
1507
+ }
1508
+ ),
1509
+ /* @__PURE__ */ jsx15(
1510
+ "span",
1511
+ {
1512
+ className: `flex-1 truncate font-mono text-xs ${OPT_TEXT_SECONDARY}`,
1513
+ children: v.isSecret ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : v.value
1514
+ }
1515
+ ),
1516
+ v.isSecret && /* @__PURE__ */ jsx15("span", { className: OPT_BADGE_NEUTRAL, children: labels.secretBadge })
1517
+ ]
1518
+ },
1519
+ v.key
1520
+ ))
1521
+ }
1522
+ ) }) })
1523
+ ] })
1524
+ },
1525
+ group.env
1526
+ );
1527
+ })
1528
+ }
1529
+ );
1530
+ }
1531
+
1532
+ // src/shells/branch-select.tsx
1533
+ import { useState as useState8 } from "react";
1534
+ import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
1535
+ var defaultLabels11 = {
1536
+ selectLabel: "\uBE0C\uB79C\uCE58",
1537
+ emptySelectionLabel: "\uC120\uD0DD...",
1538
+ emptyMessage: "\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uBE0C\uB79C\uCE58\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
1539
+ defaultBadge: "default"
1540
+ };
1541
+ function BranchSelect({
1542
+ branches,
1543
+ value: controlledValue,
1544
+ onChange,
1545
+ label,
1546
+ labels: customLabels
1547
+ }) {
1548
+ var _a, _b, _c, _d;
1549
+ const labels = __spreadValues(__spreadValues({}, defaultLabels11), customLabels);
1550
+ const resolvedLabel = label != null ? label : labels.selectLabel;
1551
+ const defaultBranch = branches.find((b) => b.isDefault);
1552
+ const [internalValue, setInternalValue] = useState8(
1553
+ (_c = (_b = defaultBranch == null ? void 0 : defaultBranch.name) != null ? _b : (_a = branches[0]) == null ? void 0 : _a.name) != null ? _c : ""
1554
+ );
1555
+ const value = controlledValue != null ? controlledValue : internalValue;
1556
+ const selected = branches.find((b) => b.name === value);
1557
+ if (branches.length === 0) {
1558
+ return /* @__PURE__ */ jsxs14(
1559
+ "div",
1560
+ {
1561
+ className: "gap-element flex flex-col",
1562
+ "data-opt-id": "7EDEP",
1563
+ "data-opt-slug": "branch-select.BranchSelect",
1564
+ children: [
1565
+ /* @__PURE__ */ jsx16("span", { className: OPT_TEXT_LABEL, children: resolvedLabel }),
1566
+ /* @__PURE__ */ jsx16("span", { className: OPT_TEXT_TERTIARY, children: labels.emptyMessage })
1567
+ ]
1568
+ }
1569
+ );
1570
+ }
1571
+ const handleChange = (v) => {
1572
+ var _a2;
1573
+ const next = Array.isArray(v) ? (_a2 = v[0]) != null ? _a2 : "" : v;
1574
+ if (onChange) onChange(next);
1575
+ else setInternalValue(next);
1576
+ };
1577
+ return /* @__PURE__ */ jsx16(
1578
+ "div",
1579
+ {
1580
+ className: "gap-element flex flex-col",
1581
+ "data-opt-id": "7EDEP",
1582
+ "data-opt-slug": "branch-select.BranchSelect",
1583
+ children: /* @__PURE__ */ jsxs14(SelectRoot, { value, onValueChange: handleChange, children: [
1584
+ /* @__PURE__ */ jsx16(SelectLabel, { className: OPT_TEXT_LABEL, children: resolvedLabel }),
1585
+ /* @__PURE__ */ jsxs14(
1586
+ SelectTrigger,
1587
+ {
1588
+ className: `${OPT_INPUT} max-w-xs items-center justify-between gap-1`,
1589
+ children: [
1590
+ /* @__PURE__ */ jsxs14("span", { className: "gap-element flex items-center truncate", children: [
1591
+ /* @__PURE__ */ jsx16(
1592
+ "svg",
1593
+ {
1594
+ className: "text-text-tertiary h-4 w-4 shrink-0",
1595
+ fill: "none",
1596
+ viewBox: "0 0 24 24",
1597
+ stroke: "currentColor",
1598
+ strokeWidth: 2,
1599
+ "aria-hidden": "true",
1600
+ children: /* @__PURE__ */ jsx16(
1601
+ "path",
1602
+ {
1603
+ strokeLinecap: "round",
1604
+ strokeLinejoin: "round",
1605
+ d: "M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244"
1606
+ }
1607
+ )
1608
+ }
1609
+ ),
1610
+ /* @__PURE__ */ jsx16("span", { className: "truncate", children: (_d = selected == null ? void 0 : selected.name) != null ? _d : labels.emptySelectionLabel }),
1611
+ (selected == null ? void 0 : selected.isDefault) && /* @__PURE__ */ jsx16("span", { className: OPT_BADGE_BLUE, children: labels.defaultBadge })
1612
+ ] }),
1613
+ /* @__PURE__ */ jsx16(
1614
+ "svg",
1615
+ {
1616
+ className: "text-text-tertiary h-4 w-4 shrink-0",
1617
+ fill: "none",
1618
+ viewBox: "0 0 24 24",
1619
+ stroke: "currentColor",
1620
+ strokeWidth: 2,
1621
+ "aria-hidden": "true",
1622
+ children: /* @__PURE__ */ jsx16(
1623
+ "path",
1624
+ {
1625
+ strokeLinecap: "round",
1626
+ strokeLinejoin: "round",
1627
+ d: "m19.5 8.25-7.5 7.5-7.5-7.5"
1628
+ }
1629
+ )
1630
+ }
1631
+ )
1632
+ ]
1633
+ }
1634
+ ),
1635
+ /* @__PURE__ */ jsx16(SelectPopover, { className: "max-w-xs", children: branches.map((branch) => /* @__PURE__ */ jsxs14(
1636
+ SelectItem,
1637
+ {
1638
+ value: branch.name,
1639
+ className: `gap-element flex scroll-m-2 items-center ${OPT_ITEM_BASE} ${OPT_ACTIVE_ITEM}`,
1640
+ children: [
1641
+ /* @__PURE__ */ jsx16("span", { className: "flex-1 truncate", children: branch.name }),
1642
+ branch.isDefault && /* @__PURE__ */ jsx16("span", { className: "bg-accent-subtle text-accent data-[active-item]:bg-accent-fg/20 data-[active-item]:text-accent-fg rounded-full px-2 py-0.5 text-[11px] font-medium", children: labels.defaultBadge }),
1643
+ branch.lastCommit && /* @__PURE__ */ jsx16(
1644
+ "span",
1645
+ {
1646
+ className: `font-mono text-xs ${OPT_TEXT_TERTIARY} data-[active-item]:text-accent-fg/60`,
1647
+ children: branch.lastCommit
1648
+ }
1649
+ ),
1650
+ branch.author && /* @__PURE__ */ jsx16(
1651
+ "span",
1652
+ {
1653
+ className: `text-xs ${OPT_TEXT_TERTIARY} data-[active-item]:text-accent-fg/60`,
1654
+ children: branch.author
1655
+ }
1656
+ )
1657
+ ]
1658
+ },
1659
+ branch.name
1660
+ )) })
1661
+ ] })
1662
+ }
1663
+ );
1664
+ }
1665
+ BranchSelect.displayName = "BranchSelect";
1666
+
1667
+ // src/shells/domain-table.tsx
1668
+ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
1669
+ var defaultLabels12 = {
1670
+ domain: "Domain",
1671
+ status: "Status",
1672
+ ssl: "SSL",
1673
+ redirect: "Redirect",
1674
+ added: "Added",
1675
+ emptyMessage: "No domains configured"
1676
+ };
1677
+ var statusBadge2 = {
1678
+ active: OPT_BADGE_GREEN,
1679
+ pending: OPT_BADGE_YELLOW,
1680
+ error: OPT_BADGE_RED
1681
+ };
1682
+ var sslBadge = {
1683
+ valid: OPT_BADGE_GREEN,
1684
+ pending: OPT_BADGE_YELLOW,
1685
+ expired: OPT_BADGE_RED
1686
+ };
1687
+ var statusLabel2 = {
1688
+ active: "Active",
1689
+ pending: "Pending",
1690
+ error: "Error"
1691
+ };
1692
+ var sslLabel = {
1693
+ valid: "Valid",
1694
+ pending: "Pending",
1695
+ expired: "Expired"
1696
+ };
1697
+ function DomainTable({
1698
+ domains,
1699
+ title = "\uB3C4\uBA54\uC778",
1700
+ labels: customLabels
1701
+ }) {
1702
+ const labels = __spreadValues(__spreadValues({}, defaultLabels12), customLabels);
1703
+ if (domains.length === 0) {
1704
+ return /* @__PURE__ */ jsxs15(
1705
+ "div",
1706
+ {
1707
+ className: "gap-group flex flex-col",
1708
+ "data-opt-id": "A7SRM",
1709
+ "data-opt-slug": "domain-table.DomainTable",
1710
+ children: [
1711
+ title && /* @__PURE__ */ jsx17("h3", { className: `text-sm font-semibold ${OPT_TEXT_PRIMARY}`, children: title }),
1712
+ /* @__PURE__ */ jsx17(
1713
+ "div",
1714
+ {
1715
+ className: `rounded-lg px-4 py-6 text-sm ${OPT_BORDER} ${OPT_TEXT_SECONDARY} bg-surface`,
1716
+ children: labels.emptyMessage
1717
+ }
1718
+ )
1719
+ ]
1720
+ }
1721
+ );
1722
+ }
1723
+ return /* @__PURE__ */ jsxs15(
1724
+ "div",
1725
+ {
1726
+ className: "gap-group flex flex-col",
1727
+ "data-opt-id": "A7SRM",
1728
+ "data-opt-slug": "domain-table.DomainTable",
1729
+ children: [
1730
+ title && /* @__PURE__ */ jsx17("h3", { className: `text-sm font-semibold ${OPT_TEXT_PRIMARY}`, children: title }),
1731
+ /* @__PURE__ */ jsx17("div", { className: `overflow-x-auto rounded-lg ${OPT_BORDER} bg-surface`, children: /* @__PURE__ */ jsx17(CompositeProvider, { focusLoop: true, focusWrap: true, children: /* @__PURE__ */ jsxs15(
1732
+ Composite,
1733
+ {
1734
+ role: "grid",
1735
+ "aria-label": title,
1736
+ className: "w-full",
1737
+ render: /* @__PURE__ */ jsx17("table", {}),
1738
+ children: [
1739
+ /* @__PURE__ */ jsx17("thead", { className: "border-border-subtle bg-surface-inset border-b", children: /* @__PURE__ */ jsxs15("tr", { children: [
1740
+ /* @__PURE__ */ jsx17("th", { className: OPT_TH, children: labels.domain }),
1741
+ /* @__PURE__ */ jsx17("th", { className: OPT_TH, children: labels.status }),
1742
+ /* @__PURE__ */ jsx17("th", { className: OPT_TH, children: labels.ssl }),
1743
+ /* @__PURE__ */ jsx17("th", { className: OPT_TH, children: labels.redirect }),
1744
+ /* @__PURE__ */ jsx17("th", { className: OPT_TH, children: labels.added })
1745
+ ] }) }),
1746
+ /* @__PURE__ */ jsx17("tbody", { className: `divide-y ${OPT_DIVIDER}`, children: domains.map((d) => {
1747
+ var _a, _b;
1748
+ return /* @__PURE__ */ jsxs15(CompositeRow, { render: /* @__PURE__ */ jsx17("tr", {}), children: [
1749
+ /* @__PURE__ */ jsx17(
1750
+ CompositeItem,
1751
+ {
1752
+ render: /* @__PURE__ */ jsx17(
1753
+ "td",
1754
+ {
1755
+ className: `${OPT_TD} font-mono font-medium`,
1756
+ role: "gridcell"
1757
+ }
1758
+ ),
1759
+ className: OPT_TABLE_ROW_ACTIVE,
1760
+ children: d.domain
1761
+ }
1762
+ ),
1763
+ /* @__PURE__ */ jsx17(
1764
+ CompositeItem,
1765
+ {
1766
+ render: /* @__PURE__ */ jsx17("td", { className: OPT_TD, role: "gridcell" }),
1767
+ className: OPT_TABLE_ROW_ACTIVE,
1768
+ children: /* @__PURE__ */ jsx17("span", { className: statusBadge2[d.status], children: statusLabel2[d.status] })
1769
+ }
1770
+ ),
1771
+ /* @__PURE__ */ jsx17(
1772
+ CompositeItem,
1773
+ {
1774
+ render: /* @__PURE__ */ jsx17("td", { className: OPT_TD, role: "gridcell" }),
1775
+ className: OPT_TABLE_ROW_ACTIVE,
1776
+ children: /* @__PURE__ */ jsx17("span", { className: sslBadge[d.ssl], children: sslLabel[d.ssl] })
1777
+ }
1778
+ ),
1779
+ /* @__PURE__ */ jsx17(
1780
+ CompositeItem,
1781
+ {
1782
+ render: /* @__PURE__ */ jsx17("td", { className: OPT_TD, role: "gridcell" }),
1783
+ className: OPT_TABLE_ROW_ACTIVE,
1784
+ children: (_a = d.redirect) != null ? _a : "\u2014"
1785
+ }
1786
+ ),
1787
+ /* @__PURE__ */ jsx17(
1788
+ CompositeItem,
1789
+ {
1790
+ render: /* @__PURE__ */ jsx17("td", { className: OPT_TD_MUTED, role: "gridcell" }),
1791
+ className: OPT_TABLE_ROW_ACTIVE,
1792
+ children: (_b = d.addedAt) != null ? _b : "\u2014"
1793
+ }
1794
+ )
1795
+ ] }, d.domain);
1796
+ }) })
1797
+ ]
1798
+ }
1799
+ ) }) })
1800
+ ]
1801
+ }
1802
+ );
1803
+ }
1804
+
1805
+ // src/shells/data-table.tsx
1806
+ import {
1807
+ useState as useState9,
1808
+ useMemo as useMemo4,
1809
+ useCallback as useCallback2,
1810
+ Fragment as Fragment2
1811
+ } from "react";
1812
+ import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
1813
+ var MIN_COLUMN_WIDTH = 40;
1814
+ var DEFAULT_COLUMN_RESIZE_MAX = 1200;
1815
+ var alignmentClass = {
1816
+ left: "text-left",
1817
+ center: "text-center",
1818
+ right: "text-right"
1819
+ };
1820
+ function getCellValue(row, accessor) {
1821
+ if (typeof accessor === "function") {
1822
+ try {
1823
+ return accessor(row);
1824
+ } catch (e) {
1825
+ return "\u2014";
1826
+ }
1827
+ }
1828
+ const value = row[accessor];
1829
+ if (value === null || value === void 0 || typeof value === "number" && !Number.isFinite(value)) {
1830
+ return "\u2014";
1831
+ }
1832
+ return String(value);
1833
+ }
1834
+ function getRawValue(row, accessor) {
1835
+ if (typeof accessor === "function") {
1836
+ return accessor(row);
1837
+ }
1838
+ return row[accessor];
1839
+ }
1840
+ function isMissingSortValue(value) {
1841
+ return value == null || typeof value === "number" && !Number.isFinite(value);
1842
+ }
1843
+ function compareSortValues(aVal, bVal, dir) {
1844
+ const aMissing = isMissingSortValue(aVal);
1845
+ const bMissing = isMissingSortValue(bVal);
1846
+ if (aMissing && bMissing) return 0;
1847
+ if (aMissing) return 1;
1848
+ if (bMissing) return -1;
1849
+ const cmp = typeof aVal === "number" && typeof bVal === "number" ? aVal - bVal : String(aVal).localeCompare(String(bVal));
1850
+ return dir === "desc" ? -cmp : cmp;
1851
+ }
1852
+ function clampPercent(ratio) {
1853
+ if (!Number.isFinite(ratio)) return 0;
1854
+ return Math.min(100, Math.max(0, Math.round(ratio * 100)));
1855
+ }
1856
+ function getResizeHandleValue(currentWidth, configuredWidth) {
1857
+ if (typeof currentWidth === "number" && Number.isFinite(currentWidth)) {
1858
+ return Math.max(MIN_COLUMN_WIDTH, Math.round(currentWidth));
1859
+ }
1860
+ const pxWidth = configuredWidth == null ? void 0 : configuredWidth.trim().match(/^(\d+(?:\.\d+)?)px$/);
1861
+ if (pxWidth) {
1862
+ return Math.max(MIN_COLUMN_WIDTH, Math.round(Number(pxWidth[1])));
1863
+ }
1864
+ return MIN_COLUMN_WIDTH;
1865
+ }
1866
+ function DataTableProgressBar({
1867
+ value,
1868
+ barColor,
1869
+ label
1870
+ }) {
1871
+ const percent = clampPercent(value);
1872
+ return /* @__PURE__ */ jsx18(
1873
+ "div",
1874
+ {
1875
+ className: "bg-bg-subtle mt-1 h-1.5 w-full overflow-hidden rounded-full",
1876
+ role: "progressbar",
1877
+ "aria-valuenow": percent,
1878
+ "aria-valuemin": 0,
1879
+ "aria-valuemax": 100,
1880
+ "aria-valuetext": `${percent}%`,
1881
+ "aria-label": label,
1882
+ children: /* @__PURE__ */ jsx18(
1883
+ "div",
1884
+ {
1885
+ className: cn("h-full rounded-full transition-all", barColor),
1886
+ style: { width: `${percent}%` }
1887
+ }
1888
+ )
1889
+ }
1890
+ );
1891
+ }
1892
+ function DataTable({
1893
+ data,
1894
+ columns,
1895
+ keyExtractor,
1896
+ title = "Data Table",
1897
+ onRowClick,
1898
+ emptyMessage = "No data available",
1899
+ activeRowId,
1900
+ sortable = false,
1901
+ selectable = false,
1902
+ selectedKeys,
1903
+ onSelectionChange,
1904
+ expandable = false,
1905
+ renderExpanded,
1906
+ pageSize,
1907
+ stickyHeader = false,
1908
+ resizableColumns = false,
1909
+ onSort: onSortExternal,
1910
+ className
1911
+ }) {
1912
+ const [sortCol, setSortCol] = useState9(null);
1913
+ const [sortDir, setSortDir] = useState9(null);
1914
+ const [expandedKeys, setExpandedKeys] = useState9(/* @__PURE__ */ new Set());
1915
+ const [page, setPage] = useState9(0);
1916
+ const [colWidths, setColWidths] = useState9({});
1917
+ const sortedData = useMemo4(() => {
1918
+ if (!sortable || !sortCol || !sortDir) return data;
1919
+ const col = columns.find((c) => c.id === sortCol);
1920
+ if (!col) return data;
1921
+ return [...data].sort((a, b) => {
1922
+ const aVal = getRawValue(a, col.accessor);
1923
+ const bVal = getRawValue(b, col.accessor);
1924
+ return compareSortValues(aVal, bVal, sortDir);
1925
+ });
1926
+ }, [data, sortable, sortCol, sortDir, columns]);
1927
+ const totalPages = pageSize ? Math.ceil(sortedData.length / pageSize) : 1;
1928
+ const pagedData = pageSize ? sortedData.slice(page * pageSize, (page + 1) * pageSize) : sortedData;
1929
+ const handleSort = useCallback2(
1930
+ (colId) => {
1931
+ if (!sortable) return;
1932
+ let nextDir;
1933
+ if (sortCol === colId) {
1934
+ nextDir = sortDir === "asc" ? "desc" : sortDir === "desc" ? null : "asc";
1935
+ setSortDir(nextDir);
1936
+ if (nextDir === null) setSortCol(null);
1937
+ } else {
1938
+ setSortCol(colId);
1939
+ setSortDir("asc");
1940
+ nextDir = "asc";
1941
+ }
1942
+ setPage(0);
1943
+ if (onSortExternal) {
1944
+ onSortExternal(colId, nextDir);
1945
+ }
1946
+ },
1947
+ [sortable, sortCol, sortDir, onSortExternal]
1948
+ );
1949
+ const toggleExpand = useCallback2((key) => {
1950
+ setExpandedKeys((prev) => {
1951
+ const next = new Set(prev);
1952
+ if (next.has(key)) next.delete(key);
1953
+ else next.add(key);
1954
+ return next;
1955
+ });
1956
+ }, []);
1957
+ const selected = selectedKeys != null ? selectedKeys : [];
1958
+ const selectedSet = useMemo4(
1959
+ () => new Set(selectedKeys != null ? selectedKeys : []),
1960
+ [selectedKeys]
1961
+ );
1962
+ const allKeys = data.map(keyExtractor);
1963
+ const allSelected = allKeys.length > 0 && allKeys.every((k) => selectedSet.has(k));
1964
+ const toggleSelectAll = useCallback2(() => {
1965
+ if (!onSelectionChange) return;
1966
+ onSelectionChange(allSelected ? [] : allKeys);
1967
+ }, [allSelected, allKeys, onSelectionChange]);
1968
+ const toggleSelectRow = useCallback2(
1969
+ (key) => {
1970
+ if (!onSelectionChange) return;
1971
+ onSelectionChange(
1972
+ selectedSet.has(key) ? selected.filter((k) => k !== key) : [...selected, key]
1973
+ );
1974
+ },
1975
+ [selected, selectedSet, onSelectionChange]
1976
+ );
1977
+ const sortIndicator = (colId) => {
1978
+ if (sortCol !== colId) return null;
1979
+ return sortDir === "asc" ? " \u25B2" : sortDir === "desc" ? " \u25BC" : null;
1980
+ };
1981
+ const colCount = columns.length + (selectable ? 1 : 0) + (expandable ? 1 : 0);
1982
+ return /* @__PURE__ */ jsxs16(
1983
+ "div",
1984
+ {
1985
+ className: cn("gap-group flex flex-col", className),
1986
+ "data-opt-id": "J1KN4",
1987
+ "data-opt-slug": "data-table.DataTable",
1988
+ children: [
1989
+ title && /* @__PURE__ */ jsx18("h3", { className: cn("text-sm font-semibold", OPT_TEXT_PRIMARY), children: title }),
1990
+ /* @__PURE__ */ jsx18(
1991
+ "div",
1992
+ {
1993
+ className: cn("overflow-x-auto rounded-lg", OPT_BORDER, "bg-surface"),
1994
+ children: data.length === 0 ? /* @__PURE__ */ jsx18(
1995
+ "div",
1996
+ {
1997
+ className: cn("px-4 py-8 text-center text-sm", OPT_TEXT_SECONDARY),
1998
+ children: emptyMessage
1999
+ }
2000
+ ) : /* @__PURE__ */ jsx18(CompositeProvider, { focusLoop: true, focusWrap: true, children: /* @__PURE__ */ jsxs16(
2001
+ Composite,
2002
+ {
2003
+ role: "grid",
2004
+ "aria-label": title,
2005
+ className: "w-full",
2006
+ render: /* @__PURE__ */ jsx18("table", {}),
2007
+ children: [
2008
+ /* @__PURE__ */ jsx18(
2009
+ "thead",
2010
+ {
2011
+ className: cn(
2012
+ "border-border-subtle bg-surface-inset border-b",
2013
+ stickyHeader && "sticky top-0 z-10"
2014
+ ),
2015
+ children: /* @__PURE__ */ jsxs16("tr", { children: [
2016
+ selectable && /* @__PURE__ */ jsx18("th", { className: cn(OPT_TH, "w-10"), children: /* @__PURE__ */ jsx18(
2017
+ Checkbox,
2018
+ {
2019
+ checked: allSelected,
2020
+ onChange: toggleSelectAll,
2021
+ "aria-label": "Select all"
2022
+ }
2023
+ ) }),
2024
+ expandable && /* @__PURE__ */ jsx18("th", { className: cn(OPT_TH, "w-10") }),
2025
+ columns.map((col) => {
2026
+ var _a;
2027
+ const isSortable = sortable && col.sortable !== false;
2028
+ const resizeValue = getResizeHandleValue(
2029
+ colWidths[col.id],
2030
+ col.width
2031
+ );
2032
+ const resizeMax = Math.max(
2033
+ DEFAULT_COLUMN_RESIZE_MAX,
2034
+ resizeValue
2035
+ );
2036
+ return /* @__PURE__ */ jsxs16(
2037
+ "th",
2038
+ {
2039
+ className: cn(
2040
+ OPT_TH,
2041
+ "relative",
2042
+ alignmentClass[(_a = col.align) != null ? _a : "left"],
2043
+ isSortable && "hover:text-text-primary cursor-pointer select-none"
2044
+ ),
2045
+ style: {
2046
+ width: colWidths[col.id] || col.width || void 0
2047
+ },
2048
+ onClick: isSortable ? () => handleSort(col.id) : void 0,
2049
+ "aria-sort": sortCol === col.id && sortDir ? sortDir === "asc" ? "ascending" : "descending" : void 0,
2050
+ children: [
2051
+ /* @__PURE__ */ jsxs16("span", { className: "flex items-center", children: [
2052
+ col.header,
2053
+ sortIndicator(col.id)
2054
+ ] }),
2055
+ resizableColumns && /* @__PURE__ */ jsx18(
2056
+ "span",
2057
+ {
2058
+ role: "separator",
2059
+ "aria-orientation": "vertical",
2060
+ "aria-label": `Resize ${col.header}`,
2061
+ "aria-valuemin": MIN_COLUMN_WIDTH,
2062
+ "aria-valuemax": resizeMax,
2063
+ "aria-valuenow": resizeValue,
2064
+ tabIndex: 0,
2065
+ className: "bg-border absolute top-0 right-0 bottom-0 w-1 cursor-col-resize opacity-0 hover:opacity-100",
2066
+ onKeyDown: (e) => {
2067
+ if (e.key !== "ArrowLeft" && e.key !== "ArrowRight") {
2068
+ return;
2069
+ }
2070
+ e.preventDefault();
2071
+ e.stopPropagation();
2072
+ const delta = e.shiftKey ? 40 : 10;
2073
+ const direction = e.key === "ArrowRight" ? 1 : -1;
2074
+ setColWidths((prev) => {
2075
+ const current = getResizeHandleValue(
2076
+ prev[col.id],
2077
+ col.width
2078
+ );
2079
+ return __spreadProps(__spreadValues({}, prev), {
2080
+ [col.id]: Math.max(
2081
+ MIN_COLUMN_WIDTH,
2082
+ current + direction * delta
2083
+ )
2084
+ });
2085
+ });
2086
+ },
2087
+ onMouseDown: (e) => {
2088
+ e.preventDefault();
2089
+ e.stopPropagation();
2090
+ const startX = e.clientX;
2091
+ const th = e.target.parentElement;
2092
+ const startW = th.offsetWidth;
2093
+ const onMove = (ev) => {
2094
+ const newW = Math.max(
2095
+ MIN_COLUMN_WIDTH,
2096
+ startW + ev.clientX - startX
2097
+ );
2098
+ setColWidths((prev) => __spreadProps(__spreadValues({}, prev), {
2099
+ [col.id]: newW
2100
+ }));
2101
+ };
2102
+ const onUp = () => {
2103
+ document.removeEventListener(
2104
+ "mousemove",
2105
+ onMove
2106
+ );
2107
+ document.removeEventListener("mouseup", onUp);
2108
+ };
2109
+ document.addEventListener("mousemove", onMove);
2110
+ document.addEventListener("mouseup", onUp);
2111
+ }
2112
+ }
2113
+ )
2114
+ ]
2115
+ },
2116
+ col.id
2117
+ );
2118
+ })
2119
+ ] })
2120
+ }
2121
+ ),
2122
+ /* @__PURE__ */ jsx18("tbody", { className: cn("divide-y", OPT_DIVIDER), children: pagedData.map((row) => {
2123
+ const key = keyExtractor(row);
2124
+ const isExpanded = expandedKeys.has(key);
2125
+ const isSelected = selectedSet.has(key);
2126
+ const isActive = activeRowId === key;
2127
+ return /* @__PURE__ */ jsxs16(Fragment2, { children: [
2128
+ /* @__PURE__ */ jsxs16(
2129
+ CompositeRow,
2130
+ {
2131
+ render: /* @__PURE__ */ jsx18(
2132
+ "tr",
2133
+ {
2134
+ className: cn(
2135
+ onRowClick ? "cursor-pointer" : void 0,
2136
+ (isSelected || isActive) && "bg-accent-subtle"
2137
+ ),
2138
+ onClick: onRowClick ? () => onRowClick(row) : void 0
2139
+ }
2140
+ ),
2141
+ children: [
2142
+ selectable && /* @__PURE__ */ jsx18(
2143
+ CompositeItem,
2144
+ {
2145
+ render: /* @__PURE__ */ jsx18(
2146
+ "td",
2147
+ {
2148
+ className: cn(OPT_TD, "w-10"),
2149
+ role: "gridcell"
2150
+ }
2151
+ ),
2152
+ className: OPT_TABLE_ROW_ACTIVE,
2153
+ children: /* @__PURE__ */ jsx18(
2154
+ Checkbox,
2155
+ {
2156
+ checked: isSelected,
2157
+ onChange: () => toggleSelectRow(key),
2158
+ "aria-label": `Select row ${key}`
2159
+ }
2160
+ )
2161
+ }
2162
+ ),
2163
+ expandable && /* @__PURE__ */ jsx18(
2164
+ CompositeItem,
2165
+ {
2166
+ render: /* @__PURE__ */ jsx18(
2167
+ "td",
2168
+ {
2169
+ className: cn(OPT_TD, "w-10"),
2170
+ role: "gridcell"
2171
+ }
2172
+ ),
2173
+ className: OPT_TABLE_ROW_ACTIVE,
2174
+ children: /* @__PURE__ */ jsx18(
2175
+ "button",
2176
+ {
2177
+ type: "button",
2178
+ onClick: (e) => {
2179
+ e.stopPropagation();
2180
+ toggleExpand(key);
2181
+ },
2182
+ className: cn(
2183
+ "rounded p-0.5 text-sm transition-transform",
2184
+ OPT_TEXT_SECONDARY,
2185
+ OPT_FOCUS_VISIBLE,
2186
+ isExpanded && "rotate-90"
2187
+ ),
2188
+ "aria-expanded": isExpanded,
2189
+ "aria-label": "Expand row",
2190
+ children: "\u25B6"
2191
+ }
2192
+ )
2193
+ }
2194
+ ),
2195
+ columns.map((col, colIndex) => {
2196
+ var _a, _b;
2197
+ return /* @__PURE__ */ jsxs16(
2198
+ CompositeItem,
2199
+ {
2200
+ render: /* @__PURE__ */ jsx18(
2201
+ "td",
2202
+ {
2203
+ className: cn(
2204
+ OPT_TD,
2205
+ alignmentClass[(_a = col.align) != null ? _a : "left"],
2206
+ colIndex === 0 ? "font-medium" : ""
2207
+ ),
2208
+ role: "gridcell"
2209
+ }
2210
+ ),
2211
+ className: OPT_TABLE_ROW_ACTIVE,
2212
+ children: [
2213
+ getCellValue(row, col.accessor),
2214
+ col.bar && /* @__PURE__ */ jsx18(
2215
+ DataTableProgressBar,
2216
+ {
2217
+ value: col.bar(row),
2218
+ barColor: (_b = col.barColor) != null ? _b : "bg-accent",
2219
+ label: col.header
2220
+ }
2221
+ )
2222
+ ]
2223
+ },
2224
+ col.id
2225
+ );
2226
+ })
2227
+ ]
2228
+ }
2229
+ ),
2230
+ expandable && isExpanded && renderExpanded && /* @__PURE__ */ jsx18("tr", { children: /* @__PURE__ */ jsx18(
2231
+ "td",
2232
+ {
2233
+ colSpan: colCount,
2234
+ className: "border-border-subtle bg-surface-inset border-t px-4 py-3",
2235
+ children: renderExpanded(row)
2236
+ }
2237
+ ) })
2238
+ ] }, key);
2239
+ }) })
2240
+ ]
2241
+ }
2242
+ ) })
2243
+ }
2244
+ ),
2245
+ pageSize && totalPages > 1 && /* @__PURE__ */ jsx18(
2246
+ Pagination,
2247
+ {
2248
+ totalItems: sortedData.length,
2249
+ pageSize,
2250
+ currentPage: page + 1,
2251
+ onPageChange: (p) => setPage(p - 1)
2252
+ }
2253
+ )
2254
+ ]
2255
+ }
2256
+ );
2257
+ }
2258
+
2259
+ // src/shells/notification-toast.tsx
2260
+ import {
2261
+ createContext,
2262
+ useCallback as useCallback3,
2263
+ useContext,
2264
+ useEffect as useEffect4,
2265
+ useRef as useRef2,
2266
+ useState as useState10,
2267
+ useSyncExternalStore
2268
+ } from "react";
2269
+ import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
2270
+ var toastCounter = 0;
2271
+ var toasts = [];
2272
+ var listeners = /* @__PURE__ */ new Set();
2273
+ function emitChange() {
2274
+ for (const l of listeners) l();
2275
+ }
2276
+ var toastStore = {
2277
+ subscribe(listener) {
2278
+ listeners.add(listener);
2279
+ return () => {
2280
+ listeners.delete(listener);
2281
+ };
2282
+ },
2283
+ getSnapshot() {
2284
+ return toasts;
2285
+ },
2286
+ add(input) {
2287
+ var _a;
2288
+ const id = `toast-${++toastCounter}`;
2289
+ toasts = [...toasts, __spreadValues({ id, variant: (_a = input.variant) != null ? _a : "info" }, input)];
2290
+ emitChange();
2291
+ return id;
2292
+ },
2293
+ dismiss(id) {
2294
+ const next = toasts.filter((t) => t.id !== id);
2295
+ if (next.length !== toasts.length) {
2296
+ toasts = next;
2297
+ emitChange();
2298
+ }
2299
+ },
2300
+ clear() {
2301
+ if (toasts.length) {
2302
+ toasts = [];
2303
+ emitChange();
2304
+ }
2305
+ }
2306
+ };
2307
+ function makeVariant(variant) {
2308
+ return (message, options) => toastStore.add(__spreadProps(__spreadValues({}, options), { message, variant }));
2309
+ }
2310
+ var toast = Object.assign(
2311
+ (options) => toastStore.add(options),
2312
+ {
2313
+ info: makeVariant("info"),
2314
+ success: makeVariant("success"),
2315
+ warning: makeVariant("warning"),
2316
+ error: makeVariant("error"),
2317
+ dismiss: (id) => toastStore.dismiss(id),
2318
+ clear: () => toastStore.clear()
2319
+ }
2320
+ );
2321
+ var ToastContext = createContext(null);
2322
+ var positionStyles = {
2323
+ "top-right": "top-4 right-4",
2324
+ "top-left": "top-4 left-4",
2325
+ "bottom-right": "bottom-4 right-4",
2326
+ "bottom-left": "bottom-4 left-4"
2327
+ };
2328
+ function ToastProvider({
2329
+ children,
2330
+ position = "top-right",
2331
+ defaultDuration = 5e3,
2332
+ stackLimit = 5
2333
+ }) {
2334
+ const allToasts = useSyncExternalStore(
2335
+ toastStore.subscribe,
2336
+ toastStore.getSnapshot,
2337
+ toastStore.getSnapshot
2338
+ );
2339
+ const visibleToasts = allToasts.length > stackLimit ? allToasts.slice(-stackLimit) : allToasts;
2340
+ const addToast = useCallback3(
2341
+ (t) => toastStore.add(t),
2342
+ []
2343
+ );
2344
+ const removeToast = useCallback3((id) => toastStore.dismiss(id), []);
2345
+ return /* @__PURE__ */ jsxs17(
2346
+ ToastContext.Provider,
2347
+ {
2348
+ value: { toasts: visibleToasts, addToast, removeToast },
2349
+ children: [
2350
+ children,
2351
+ /* @__PURE__ */ jsx19(
2352
+ "div",
2353
+ {
2354
+ className: cn(
2355
+ "gap-element fixed z-50 flex flex-col",
2356
+ positionStyles[position]
2357
+ ),
2358
+ role: "region",
2359
+ "aria-label": "Notifications",
2360
+ "data-opt-id": "M0KTS",
2361
+ "data-opt-slug": "notification-toast.ToastProvider",
2362
+ children: visibleToasts.map((t) => /* @__PURE__ */ jsx19(
2363
+ NotificationToast,
2364
+ {
2365
+ toast: t,
2366
+ defaultDuration,
2367
+ onDismiss: () => toastStore.dismiss(t.id)
2368
+ },
2369
+ t.id
2370
+ ))
2371
+ }
2372
+ )
2373
+ ]
2374
+ }
2375
+ );
2376
+ }
2377
+ function useToast() {
2378
+ const context = useContext(ToastContext);
2379
+ if (!context) {
2380
+ throw new Error("useToast must be used within a ToastProvider");
2381
+ }
2382
+ return context;
2383
+ }
2384
+ function NotificationToast({
2385
+ toast: toast2,
2386
+ onDismiss,
2387
+ defaultDuration = 5e3
2388
+ }) {
2389
+ var _a;
2390
+ const duration = (_a = toast2.duration) != null ? _a : defaultDuration;
2391
+ const isUrgent = toast2.variant === "error" || toast2.variant === "warning";
2392
+ const onDismissRef = useRef2(onDismiss);
2393
+ const dismissedRef = useRef2(false);
2394
+ const timerStartedAtRef = useRef2(null);
2395
+ const remainingDurationRef = useRef2(duration);
2396
+ const [paused, setPaused] = useState10(false);
2397
+ useEffect4(() => {
2398
+ onDismissRef.current = onDismiss;
2399
+ }, [onDismiss]);
2400
+ const dismissOnce = useCallback3(() => {
2401
+ if (dismissedRef.current) return;
2402
+ dismissedRef.current = true;
2403
+ onDismissRef.current();
2404
+ }, []);
2405
+ useEffect4(() => {
2406
+ dismissedRef.current = false;
2407
+ remainingDurationRef.current = duration;
2408
+ }, [toast2.id, duration]);
2409
+ useEffect4(() => {
2410
+ if (!duration || duration <= 0 || paused || dismissedRef.current) {
2411
+ return;
2412
+ }
2413
+ timerStartedAtRef.current = Date.now();
2414
+ const timer = setTimeout(() => {
2415
+ dismissOnce();
2416
+ }, remainingDurationRef.current);
2417
+ return () => {
2418
+ clearTimeout(timer);
2419
+ if (timerStartedAtRef.current !== null) {
2420
+ const elapsed = Date.now() - timerStartedAtRef.current;
2421
+ remainingDurationRef.current = Math.max(
2422
+ 0,
2423
+ remainingDurationRef.current - elapsed
2424
+ );
2425
+ timerStartedAtRef.current = null;
2426
+ }
2427
+ };
2428
+ }, [dismissOnce, duration, paused]);
2429
+ return /* @__PURE__ */ jsx19(
2430
+ "div",
2431
+ {
2432
+ className: "animate-in slide-in-from-right-full fade-in w-80 duration-200",
2433
+ role: isUrgent ? "alert" : "status",
2434
+ "aria-live": isUrgent ? "assertive" : "polite",
2435
+ "aria-atomic": "true",
2436
+ onMouseEnter: () => setPaused(true),
2437
+ onMouseLeave: () => setPaused(false),
2438
+ onFocus: () => setPaused(true),
2439
+ onBlur: (event) => {
2440
+ if (!event.currentTarget.contains(event.relatedTarget)) {
2441
+ setPaused(false);
2442
+ }
2443
+ },
2444
+ "data-opt-id": "26YVM",
2445
+ "data-opt-slug": "notification-toast.NotificationToast",
2446
+ children: /* @__PURE__ */ jsxs17(
2447
+ Alert,
2448
+ {
2449
+ variant: toast2.variant,
2450
+ title: toast2.title,
2451
+ dismissible: true,
2452
+ onDismiss: dismissOnce,
2453
+ children: [
2454
+ toast2.message,
2455
+ toast2.actions && toast2.actions.length > 0 && /* @__PURE__ */ jsx19("div", { className: "gap-element mt-2 flex", children: toast2.actions.map((action) => /* @__PURE__ */ jsx19(
2456
+ "button",
2457
+ {
2458
+ type: "button",
2459
+ onClick: action.onClick,
2460
+ className: "text-xs font-medium underline hover:no-underline",
2461
+ children: action.label
2462
+ },
2463
+ action.label
2464
+ )) })
2465
+ ]
2466
+ }
2467
+ )
2468
+ }
2469
+ );
2470
+ }
2471
+
2472
+ // src/shells/failure-list.tsx
2473
+ import { Fragment as Fragment3, jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
2474
+ var defaultLabels13 = {
2475
+ title: "\uCD5C\uADFC \uC2E4\uD328",
2476
+ emptyMessage: "\uC2E4\uD328 \uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4",
2477
+ moreItems: "{count}\uAC1C \uB354 \uBCF4\uAE30",
2478
+ severityLabels: {
2479
+ critical: "\uC2EC\uAC01",
2480
+ error: "\uC624\uB958",
2481
+ warning: "\uACBD\uACE0"
2482
+ },
2483
+ timeAgo: {
2484
+ justNow: "\uBC29\uAE08 \uC804",
2485
+ minutesAgo: "{minutes}\uBD84 \uC804",
2486
+ hoursAgo: "{hours}\uC2DC\uAC04 \uC804",
2487
+ daysAgo: "{days}\uC77C \uC804"
2488
+ }
2489
+ };
2490
+ var severityVariant = {
2491
+ critical: "error",
2492
+ error: "error",
2493
+ warning: "warning"
2494
+ };
2495
+ var severityBorder = {
2496
+ critical: "border-l-2 border-l-danger",
2497
+ error: "border-l-2 border-l-danger",
2498
+ warning: "border-l-2 border-l-warning"
2499
+ };
2500
+ function formatRelativeTime(timestamp, timeLabels, locale) {
2501
+ const now = /* @__PURE__ */ new Date();
2502
+ const date = new Date(timestamp);
2503
+ const diffMs = now.getTime() - date.getTime();
2504
+ const diffMins = Math.floor(diffMs / 6e4);
2505
+ const diffHours = Math.floor(diffMs / 36e5);
2506
+ const diffDays = Math.floor(diffMs / 864e5);
2507
+ if (diffMins < 1) return timeLabels.justNow;
2508
+ if (diffMins < 60)
2509
+ return timeLabels.minutesAgo.replace("{minutes}", String(diffMins));
2510
+ if (diffHours < 24)
2511
+ return timeLabels.hoursAgo.replace("{hours}", String(diffHours));
2512
+ if (diffDays < 7)
2513
+ return timeLabels.daysAgo.replace("{days}", String(diffDays));
2514
+ return date.toLocaleDateString(locale);
2515
+ }
2516
+ function groupBy(items, key) {
2517
+ const map = /* @__PURE__ */ new Map();
2518
+ for (const item of items) {
2519
+ const k = key(item);
2520
+ const existing = map.get(k);
2521
+ if (existing) {
2522
+ existing.push(item);
2523
+ } else {
2524
+ map.set(k, [item]);
2525
+ }
2526
+ }
2527
+ return map;
2528
+ }
2529
+ function FailureItemRow({
2530
+ failure,
2531
+ isLast,
2532
+ onFailureClick,
2533
+ renderActions,
2534
+ mergedSeverityLabels,
2535
+ mergedTimeAgo,
2536
+ locale
2537
+ }) {
2538
+ const itemId = `failure-${failure.id}`;
2539
+ const handleKeyDown = (event) => {
2540
+ if (!onFailureClick) return;
2541
+ if (event.key !== "Enter" && event.key !== " ") return;
2542
+ event.preventDefault();
2543
+ onFailureClick(failure);
2544
+ };
2545
+ return /* @__PURE__ */ jsxs18(
2546
+ CompositeItem,
2547
+ {
2548
+ render: /* @__PURE__ */ jsx20("article", {}),
2549
+ role: "article",
2550
+ "aria-labelledby": itemId,
2551
+ className: cn(
2552
+ "group gap-group relative flex w-full items-start p-3",
2553
+ OPT_FOCUS_VISIBLE,
2554
+ "data-[active-item]:bg-accent-subtle",
2555
+ onFailureClick && "cursor-pointer",
2556
+ !isLast && "border-border-subtle border-b",
2557
+ severityBorder[failure.severity]
2558
+ ),
2559
+ onClick: () => onFailureClick == null ? void 0 : onFailureClick(failure),
2560
+ onKeyDown: handleKeyDown,
2561
+ children: [
2562
+ /* @__PURE__ */ jsxs18("div", { className: "min-w-0 flex-1", children: [
2563
+ /* @__PURE__ */ jsxs18("div", { className: "gap-element flex items-center", children: [
2564
+ /* @__PURE__ */ jsx20(Badge, { variant: severityVariant[failure.severity], size: "sm", children: mergedSeverityLabels[failure.severity] }),
2565
+ /* @__PURE__ */ jsx20(
2566
+ "span",
2567
+ {
2568
+ id: itemId,
2569
+ className: `text-sm font-medium ${OPT_TEXT_PRIMARY} truncate`,
2570
+ children: failure.title
2571
+ }
2572
+ )
2573
+ ] }),
2574
+ failure.message && /* @__PURE__ */ jsx20("p", { className: `mt-1 text-xs ${OPT_TEXT_SECONDARY} line-clamp-2`, children: failure.message }),
2575
+ /* @__PURE__ */ jsx20(
2576
+ "time",
2577
+ {
2578
+ dateTime: failure.timestamp,
2579
+ className: `mt-1 block text-xs ${OPT_TEXT_TERTIARY}`,
2580
+ children: formatRelativeTime(failure.timestamp, mergedTimeAgo, locale)
2581
+ }
2582
+ )
2583
+ ] }),
2584
+ renderActions && /* @__PURE__ */ jsx20("div", { className: "flex shrink-0 items-center gap-1", children: renderActions(failure) })
2585
+ ]
2586
+ },
2587
+ failure.id
2588
+ );
2589
+ }
2590
+ function FailureList({
2591
+ failures,
2592
+ title,
2593
+ maxItems,
2594
+ grouped = false,
2595
+ onFailureClick,
2596
+ onShowMore,
2597
+ labels: customLabels,
2598
+ locale = "ko-KR",
2599
+ loading = false,
2600
+ renderActions,
2601
+ className
2602
+ }) {
2603
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
2604
+ const mergedTimeAgo = {
2605
+ justNow: (_b = (_a = customLabels == null ? void 0 : customLabels.timeAgo) == null ? void 0 : _a.justNow) != null ? _b : defaultLabels13.timeAgo.justNow,
2606
+ minutesAgo: (_d = (_c = customLabels == null ? void 0 : customLabels.timeAgo) == null ? void 0 : _c.minutesAgo) != null ? _d : defaultLabels13.timeAgo.minutesAgo,
2607
+ hoursAgo: (_f = (_e = customLabels == null ? void 0 : customLabels.timeAgo) == null ? void 0 : _e.hoursAgo) != null ? _f : defaultLabels13.timeAgo.hoursAgo,
2608
+ daysAgo: (_h = (_g = customLabels == null ? void 0 : customLabels.timeAgo) == null ? void 0 : _g.daysAgo) != null ? _h : defaultLabels13.timeAgo.daysAgo
2609
+ };
2610
+ const mergedSeverityLabels = {
2611
+ critical: (_j = (_i = customLabels == null ? void 0 : customLabels.severityLabels) == null ? void 0 : _i.critical) != null ? _j : defaultLabels13.severityLabels.critical,
2612
+ error: (_l = (_k = customLabels == null ? void 0 : customLabels.severityLabels) == null ? void 0 : _k.error) != null ? _l : defaultLabels13.severityLabels.error,
2613
+ warning: (_n = (_m = customLabels == null ? void 0 : customLabels.severityLabels) == null ? void 0 : _m.warning) != null ? _n : defaultLabels13.severityLabels.warning
2614
+ };
2615
+ const labels = {
2616
+ title: (_o = customLabels == null ? void 0 : customLabels.title) != null ? _o : defaultLabels13.title,
2617
+ emptyMessage: (_p = customLabels == null ? void 0 : customLabels.emptyMessage) != null ? _p : defaultLabels13.emptyMessage,
2618
+ moreItems: (_q = customLabels == null ? void 0 : customLabels.moreItems) != null ? _q : defaultLabels13.moreItems
2619
+ };
2620
+ const displayTitle = title != null ? title : labels.title;
2621
+ const allItems = maxItems ? failures.slice(0, maxItems) : failures;
2622
+ const remainingCount = maxItems ? Math.max(0, failures.length - maxItems) : 0;
2623
+ const rowProps = {
2624
+ onFailureClick,
2625
+ renderActions,
2626
+ mergedSeverityLabels,
2627
+ mergedTimeAgo,
2628
+ locale
2629
+ };
2630
+ return /* @__PURE__ */ jsx20(
2631
+ "div",
2632
+ {
2633
+ className,
2634
+ "data-opt-id": "ZAM3R",
2635
+ "data-opt-slug": "failure-list.FailureList",
2636
+ children: /* @__PURE__ */ jsxs18("div", { className: "gap-group flex flex-col", children: [
2637
+ displayTitle && /* @__PURE__ */ jsx20("h3", { className: `text-sm font-semibold ${OPT_TEXT_PRIMARY}`, children: displayTitle }),
2638
+ failures.length === 0 ? /* @__PURE__ */ jsx20(
2639
+ "div",
2640
+ {
2641
+ role: "status",
2642
+ className: `py-8 text-center text-sm ${OPT_TEXT_SECONDARY}`,
2643
+ children: labels.emptyMessage
2644
+ }
2645
+ ) : /* @__PURE__ */ jsx20(CompositeProvider, { focusLoop: true, orientation: "vertical", children: /* @__PURE__ */ jsx20(
2646
+ Composite,
2647
+ {
2648
+ role: "feed",
2649
+ "aria-label": displayTitle,
2650
+ "aria-busy": loading,
2651
+ className: `rounded-lg ${OPT_BORDER} bg-surface`,
2652
+ children: grouped ? renderGrouped(
2653
+ allItems,
2654
+ remainingCount,
2655
+ rowProps,
2656
+ labels,
2657
+ onShowMore
2658
+ ) : renderFlat(
2659
+ allItems,
2660
+ remainingCount,
2661
+ rowProps,
2662
+ labels,
2663
+ onShowMore
2664
+ )
2665
+ }
2666
+ ) })
2667
+ ] })
2668
+ }
2669
+ );
2670
+ }
2671
+ function renderFlat(items, remainingCount, rowProps, labels, onShowMore) {
2672
+ return /* @__PURE__ */ jsxs18(Fragment3, { children: [
2673
+ items.map((failure, index) => /* @__PURE__ */ jsx20(
2674
+ FailureItemRow,
2675
+ __spreadValues({
2676
+ failure,
2677
+ isLast: index === items.length - 1 && remainingCount === 0
2678
+ }, rowProps),
2679
+ failure.id
2680
+ )),
2681
+ remainingCount > 0 && /* @__PURE__ */ jsx20(
2682
+ CompositeItem,
2683
+ {
2684
+ render: /* @__PURE__ */ jsx20("button", { type: "button" }),
2685
+ onClick: onShowMore,
2686
+ className: `border-border-subtle w-full border-t p-3 text-center text-xs font-medium ${OPT_TEXT_SECONDARY} ${OPT_FOCUS} hover:text-accent data-[active-item]:bg-accent-subtle transition-colors`,
2687
+ "aria-label": `${remainingCount}\uAC1C\uC758 \uC2E4\uD328 \uB354 \uBCF4\uAE30`,
2688
+ children: labels.moreItems.replace("{count}", String(remainingCount))
2689
+ }
2690
+ )
2691
+ ] });
2692
+ }
2693
+ function renderGrouped(items, remainingCount, rowProps, labels, onShowMore) {
2694
+ const groups = groupBy(items, (f) => {
2695
+ var _a;
2696
+ return (_a = f.group) != null ? _a : "";
2697
+ });
2698
+ const groupEntries = Array.from(groups.entries());
2699
+ return /* @__PURE__ */ jsxs18(Fragment3, { children: [
2700
+ groupEntries.map(([groupName, groupItems], groupIndex) => {
2701
+ const isLastGroup = groupIndex === groupEntries.length - 1 && remainingCount === 0;
2702
+ return /* @__PURE__ */ jsxs18("div", { children: [
2703
+ groupName && /* @__PURE__ */ jsx20(
2704
+ "div",
2705
+ {
2706
+ className: cn(
2707
+ `px-3 py-2 text-xs font-semibold ${OPT_TEXT_TERTIARY} tracking-wider uppercase`,
2708
+ groupIndex > 0 && "border-border-subtle border-t"
2709
+ ),
2710
+ children: groupName
2711
+ }
2712
+ ),
2713
+ groupItems.map((failure, index) => /* @__PURE__ */ jsx20(
2714
+ FailureItemRow,
2715
+ __spreadValues({
2716
+ failure,
2717
+ isLast: isLastGroup && index === groupItems.length - 1
2718
+ }, rowProps),
2719
+ failure.id
2720
+ ))
2721
+ ] }, groupName || "__ungrouped__");
2722
+ }),
2723
+ remainingCount > 0 && /* @__PURE__ */ jsx20(
2724
+ CompositeItem,
2725
+ {
2726
+ render: /* @__PURE__ */ jsx20("button", { type: "button" }),
2727
+ onClick: onShowMore,
2728
+ className: `border-border-subtle w-full border-t p-3 text-center text-xs font-medium ${OPT_TEXT_SECONDARY} ${OPT_FOCUS} hover:text-accent data-[active-item]:bg-accent-subtle transition-colors`,
2729
+ "aria-label": `${remainingCount}\uAC1C\uC758 \uC2E4\uD328 \uB354 \uBCF4\uAE30`,
2730
+ children: labels.moreItems.replace("{count}", String(remainingCount))
2731
+ }
2732
+ )
2733
+ ] });
2734
+ }
2735
+
2736
+ // src/shells/key-value-editor.tsx
2737
+ import { useState as useState11, useCallback as useCallback4, useRef as useRef3 } from "react";
2738
+ import { jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
2739
+ var defaultLabels14 = {
2740
+ keyPlaceholder: "\uD0A4",
2741
+ valuePlaceholder: "\uAC12",
2742
+ addButton: "\uCD94\uAC00",
2743
+ removeAriaLabel: "\uD56D\uBAA9 \uC0AD\uC81C",
2744
+ toggleSecretAriaLabel: "\uBE44\uBC00\uAC12 \uD1A0\uAE00",
2745
+ emptyMessage: "\uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uCD94\uAC00 \uBC84\uD2BC\uC744 \uB20C\uB7EC\uC8FC\uC138\uC694."
2746
+ };
2747
+ var idCounter = 0;
2748
+ function generateId() {
2749
+ return `kv-${++idCounter}`;
2750
+ }
2751
+ function KeyValueEditor({
2752
+ value: controlledValue,
2753
+ defaultValue,
2754
+ onChange,
2755
+ showSecretToggle = false,
2756
+ maxEntries,
2757
+ readOnly = false,
2758
+ labels: customLabels,
2759
+ renderActions,
2760
+ validateKey,
2761
+ className
2762
+ }) {
2763
+ const labels = __spreadValues(__spreadValues({}, defaultLabels14), customLabels);
2764
+ const [internalEntries, setInternalEntries] = useState11(
2765
+ () => defaultValue != null ? defaultValue : []
2766
+ );
2767
+ const entries = controlledValue != null ? controlledValue : internalEntries;
2768
+ const keyInputRef = useRef3(null);
2769
+ const updateEntries = useCallback4(
2770
+ (next) => {
2771
+ if (!controlledValue) {
2772
+ setInternalEntries(next);
2773
+ }
2774
+ onChange == null ? void 0 : onChange(next);
2775
+ },
2776
+ [controlledValue, onChange]
2777
+ );
2778
+ const addEntry = useCallback4(() => {
2779
+ if (maxEntries && entries.length >= maxEntries) return;
2780
+ const newEntry = {
2781
+ id: generateId(),
2782
+ key: "",
2783
+ value: "",
2784
+ isSecret: false
2785
+ };
2786
+ updateEntries([...entries, newEntry]);
2787
+ requestAnimationFrame(() => {
2788
+ var _a, _b, _c;
2789
+ (_c = (_b = (_a = keyInputRef.current) == null ? void 0 : _a.closest("[data-kv-list]")) == null ? void 0 : _b.querySelector(
2790
+ `[data-kv-id="${newEntry.id}"] [data-kv-key]`
2791
+ )) == null ? void 0 : _c.focus();
2792
+ });
2793
+ }, [entries, maxEntries, updateEntries]);
2794
+ const removeEntry = useCallback4(
2795
+ (id) => {
2796
+ updateEntries(entries.filter((e) => e.id !== id));
2797
+ },
2798
+ [entries, updateEntries]
2799
+ );
2800
+ const updateEntry = useCallback4(
2801
+ (id, field, val) => {
2802
+ updateEntries(
2803
+ entries.map((e) => e.id === id ? __spreadProps(__spreadValues({}, e), { [field]: val }) : e)
2804
+ );
2805
+ },
2806
+ [entries, updateEntries]
2807
+ );
2808
+ const toggleSecret = useCallback4(
2809
+ (id) => {
2810
+ updateEntries(
2811
+ entries.map((e) => e.id === id ? __spreadProps(__spreadValues({}, e), { isSecret: !e.isSecret }) : e)
2812
+ );
2813
+ },
2814
+ [entries, updateEntries]
2815
+ );
2816
+ const canAdd = !readOnly && (!maxEntries || entries.length < maxEntries);
2817
+ return /* @__PURE__ */ jsxs19(
2818
+ "div",
2819
+ {
2820
+ className: cn("gap-group flex flex-col", className),
2821
+ "data-opt-id": "NCBWA",
2822
+ "data-opt-slug": "key-value-editor.KeyValueEditor",
2823
+ children: [
2824
+ entries.length === 0 ? /* @__PURE__ */ jsx21("p", { className: "text-text-tertiary py-4 text-center text-sm", children: labels.emptyMessage }) : /* @__PURE__ */ jsx21("div", { className: "gap-element flex flex-col", "data-kv-list": true, children: entries.map((entry, index) => {
2825
+ const keyError = validateKey == null ? void 0 : validateKey(entry.key, entries);
2826
+ return /* @__PURE__ */ jsxs19(
2827
+ "div",
2828
+ {
2829
+ "data-kv-id": entry.id,
2830
+ className: cn(
2831
+ "gap-element flex items-start",
2832
+ "border-border rounded-lg border p-3",
2833
+ "bg-surface"
2834
+ ),
2835
+ children: [
2836
+ /* @__PURE__ */ jsxs19("div", { className: "flex min-w-0 flex-1 flex-col gap-1", children: [
2837
+ /* @__PURE__ */ jsx21(
2838
+ "input",
2839
+ {
2840
+ "data-kv-key": true,
2841
+ ref: index === entries.length - 1 ? keyInputRef : void 0,
2842
+ type: "text",
2843
+ value: entry.key,
2844
+ onChange: (e) => updateEntry(entry.id, "key", e.target.value),
2845
+ placeholder: labels.keyPlaceholder,
2846
+ readOnly,
2847
+ "aria-label": `${labels.keyPlaceholder} ${index + 1}`,
2848
+ className: cn(
2849
+ "border-border bg-bg-subtle text-text-primary placeholder:text-text-tertiary w-full rounded-md border px-3 py-1.5 font-mono text-sm",
2850
+ "focus:border-accent focus-visible:ring-ring focus-visible:ring-offset-ring-offset focus-visible:ring-2 focus-visible:ring-offset-1",
2851
+ readOnly && "cursor-default opacity-70",
2852
+ keyError && "border-error"
2853
+ )
2854
+ }
2855
+ ),
2856
+ keyError && /* @__PURE__ */ jsx21("span", { className: "text-error text-xs", children: keyError })
2857
+ ] }),
2858
+ /* @__PURE__ */ jsx21("span", { className: "text-text-tertiary px-1 py-1.5", children: "=" }),
2859
+ /* @__PURE__ */ jsx21("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx21(
2860
+ "input",
2861
+ {
2862
+ type: entry.isSecret ? "password" : "text",
2863
+ value: entry.value,
2864
+ onChange: (e) => updateEntry(entry.id, "value", e.target.value),
2865
+ placeholder: labels.valuePlaceholder,
2866
+ readOnly,
2867
+ "aria-label": `${labels.valuePlaceholder} ${index + 1}`,
2868
+ className: cn(
2869
+ "border-border bg-bg-subtle text-text-primary placeholder:text-text-tertiary w-full rounded-md border px-3 py-1.5 font-mono text-sm",
2870
+ "focus:border-accent focus-visible:ring-ring focus-visible:ring-offset-ring-offset focus-visible:ring-2 focus-visible:ring-offset-1",
2871
+ readOnly && "cursor-default opacity-70"
2872
+ )
2873
+ }
2874
+ ) }),
2875
+ showSecretToggle && !readOnly && /* @__PURE__ */ jsx21(
2876
+ "button",
2877
+ {
2878
+ type: "button",
2879
+ onClick: () => toggleSecret(entry.id),
2880
+ "aria-label": labels.toggleSecretAriaLabel,
2881
+ "aria-pressed": entry.isSecret,
2882
+ className: "text-text-tertiary hover:text-text-primary shrink-0 rounded p-1.5 transition-colors",
2883
+ children: entry.isSecret ? /* @__PURE__ */ jsx21(
2884
+ "svg",
2885
+ {
2886
+ className: "size-4",
2887
+ fill: "none",
2888
+ viewBox: "0 0 24 24",
2889
+ stroke: "currentColor",
2890
+ strokeWidth: 2,
2891
+ "aria-hidden": "true",
2892
+ children: /* @__PURE__ */ jsx21(
2893
+ "path",
2894
+ {
2895
+ strokeLinecap: "round",
2896
+ strokeLinejoin: "round",
2897
+ d: "M3.98 8.223A10.477 10.477 0 001.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.45 10.45 0 0112 4.5c4.756 0 8.773 3.162 10.065 7.498a10.523 10.523 0 01-4.293 5.774M6.228 6.228L3 3m3.228 3.228l3.65 3.65m7.894 7.894L21 21m-3.228-3.228l-3.65-3.65m0 0a3 3 0 10-4.243-4.243m4.242 4.242L9.88 9.88"
2898
+ }
2899
+ )
2900
+ }
2901
+ ) : /* @__PURE__ */ jsxs19(
2902
+ "svg",
2903
+ {
2904
+ className: "size-4",
2905
+ fill: "none",
2906
+ viewBox: "0 0 24 24",
2907
+ stroke: "currentColor",
2908
+ strokeWidth: 2,
2909
+ "aria-hidden": "true",
2910
+ children: [
2911
+ /* @__PURE__ */ jsx21(
2912
+ "path",
2913
+ {
2914
+ strokeLinecap: "round",
2915
+ strokeLinejoin: "round",
2916
+ d: "M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z"
2917
+ }
2918
+ ),
2919
+ /* @__PURE__ */ jsx21(
2920
+ "path",
2921
+ {
2922
+ strokeLinecap: "round",
2923
+ strokeLinejoin: "round",
2924
+ d: "M15 12a3 3 0 11-6 0 3 3 0 016 0z"
2925
+ }
2926
+ )
2927
+ ]
2928
+ }
2929
+ )
2930
+ }
2931
+ ),
2932
+ renderActions == null ? void 0 : renderActions(entry, index),
2933
+ !readOnly && /* @__PURE__ */ jsx21(
2934
+ "button",
2935
+ {
2936
+ type: "button",
2937
+ onClick: () => removeEntry(entry.id),
2938
+ "aria-label": labels.removeAriaLabel,
2939
+ className: "text-text-tertiary hover:text-error shrink-0 rounded p-1.5 transition-colors",
2940
+ children: /* @__PURE__ */ jsx21(
2941
+ "svg",
2942
+ {
2943
+ className: "size-4",
2944
+ fill: "none",
2945
+ viewBox: "0 0 24 24",
2946
+ stroke: "currentColor",
2947
+ strokeWidth: 2,
2948
+ "aria-hidden": "true",
2949
+ children: /* @__PURE__ */ jsx21(
2950
+ "path",
2951
+ {
2952
+ strokeLinecap: "round",
2953
+ strokeLinejoin: "round",
2954
+ d: "M6 18L18 6M6 6l12 12"
2955
+ }
2956
+ )
2957
+ }
2958
+ )
2959
+ }
2960
+ )
2961
+ ]
2962
+ },
2963
+ entry.id
2964
+ );
2965
+ }) }),
2966
+ canAdd && /* @__PURE__ */ jsx21(
2967
+ "button",
2968
+ {
2969
+ type: "button",
2970
+ onClick: addEntry,
2971
+ className: cn(
2972
+ "border-border text-text-secondary rounded-lg border border-dashed px-4 py-2 text-sm transition-colors",
2973
+ "hover:border-accent hover:text-accent"
2974
+ ),
2975
+ children: labels.addButton
2976
+ }
2977
+ )
2978
+ ]
2979
+ }
2980
+ );
2981
+ }
2982
+
2983
+ // src/shells/workflow-canvas.tsx
2984
+ import * as React from "react";
2985
+ import { jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
2986
+ var statusVariant = {
2987
+ idle: "default",
2988
+ running: "info",
2989
+ success: "success",
2990
+ error: "error"
2991
+ };
2992
+ function clamp(value, min, max) {
2993
+ return Math.max(min, Math.min(max, value));
2994
+ }
2995
+ function WorkflowCanvas({
2996
+ steps,
2997
+ connections = [],
2998
+ rows = 9,
2999
+ cols = 9,
3000
+ cellSize = 72,
3001
+ gap = 12,
3002
+ title = "Workflow Canvas",
3003
+ emptyMessage = "No workflow steps",
3004
+ selectedStepId,
3005
+ onSelectStep,
3006
+ onStepMove,
3007
+ renderStep,
3008
+ className
3009
+ }) {
3010
+ const stepMap = React.useMemo(
3011
+ () => new Map(steps.map((step) => [step.id, step])),
3012
+ [steps]
3013
+ );
3014
+ const width = cols * cellSize + (cols - 1) * gap;
3015
+ const height = rows * cellSize + (rows - 1) * gap;
3016
+ const renderDefaultStep = React.useCallback(
3017
+ (step, selected) => /* @__PURE__ */ jsxs20(
3018
+ "button",
3019
+ {
3020
+ type: "button",
3021
+ onClick: () => onSelectStep == null ? void 0 : onSelectStep(step),
3022
+ className: cn(
3023
+ "group absolute flex h-full w-full flex-col justify-between rounded-lg border p-2 text-left transition-colors",
3024
+ selected ? "border-accent bg-accent-subtle" : "border-border bg-surface hover:bg-bg-subtle"
3025
+ ),
3026
+ children: [
3027
+ /* @__PURE__ */ jsx22("span", { className: "text-text-primary truncate text-xs font-medium", children: step.title }),
3028
+ step.status && /* @__PURE__ */ jsx22(
3029
+ Badge,
3030
+ {
3031
+ variant: statusVariant[step.status],
3032
+ size: "sm",
3033
+ className: "w-fit",
3034
+ children: step.status
3035
+ }
3036
+ )
3037
+ ]
3038
+ }
3039
+ ),
3040
+ [onSelectStep]
3041
+ );
3042
+ const handleStepKeyDown = React.useCallback(
3043
+ (event, step) => {
3044
+ if (!onStepMove) {
3045
+ return;
3046
+ }
3047
+ let nextRow = step.row;
3048
+ let nextCol = step.col;
3049
+ switch (event.key) {
3050
+ case "ArrowUp":
3051
+ nextRow -= 1;
3052
+ break;
3053
+ case "ArrowDown":
3054
+ nextRow += 1;
3055
+ break;
3056
+ case "ArrowLeft":
3057
+ nextCol -= 1;
3058
+ break;
3059
+ case "ArrowRight":
3060
+ nextCol += 1;
3061
+ break;
3062
+ default:
3063
+ return;
3064
+ }
3065
+ event.preventDefault();
3066
+ onStepMove(
3067
+ step.id,
3068
+ clamp(nextRow, 0, rows - 1),
3069
+ clamp(nextCol, 0, cols - 1)
3070
+ );
3071
+ },
3072
+ [onStepMove, rows, cols]
3073
+ );
3074
+ return /* @__PURE__ */ jsxs20(
3075
+ "div",
3076
+ {
3077
+ className: cn("gap-group flex flex-col", className),
3078
+ "data-opt-id": "4TX11",
3079
+ "data-opt-slug": "workflow-canvas.WorkflowCanvas",
3080
+ children: [
3081
+ /* @__PURE__ */ jsx22("h3", { className: "text-text-primary text-sm font-semibold", children: title }),
3082
+ steps.length === 0 ? /* @__PURE__ */ jsx22("div", { className: "text-text-secondary border-border rounded-lg border border-dashed p-8 text-center text-sm", children: emptyMessage }) : /* @__PURE__ */ jsx22("div", { className: "border-border bg-bg-subtle relative overflow-auto rounded-lg border p-3", children: /* @__PURE__ */ jsxs20(
3083
+ "div",
3084
+ {
3085
+ role: "grid",
3086
+ "aria-label": title,
3087
+ className: "relative",
3088
+ style: { width, height },
3089
+ children: [
3090
+ /* @__PURE__ */ jsx22(
3091
+ "div",
3092
+ {
3093
+ className: "grid",
3094
+ style: {
3095
+ gridTemplateColumns: `repeat(${cols}, ${cellSize}px)`,
3096
+ gridTemplateRows: `repeat(${rows}, ${cellSize}px)`,
3097
+ gap
3098
+ },
3099
+ children: Array.from({ length: rows * cols }, (_, i) => /* @__PURE__ */ jsx22(
3100
+ "div",
3101
+ {
3102
+ "aria-hidden": "true",
3103
+ className: "border-border-subtle bg-surface/30 rounded-md border border-dashed"
3104
+ },
3105
+ i
3106
+ ))
3107
+ }
3108
+ ),
3109
+ /* @__PURE__ */ jsx22(
3110
+ "svg",
3111
+ {
3112
+ className: "pointer-events-none absolute inset-0",
3113
+ width,
3114
+ height,
3115
+ "aria-hidden": "true",
3116
+ children: connections.map((connection) => {
3117
+ const from = stepMap.get(connection.from);
3118
+ const to = stepMap.get(connection.to);
3119
+ if (!from || !to) {
3120
+ return null;
3121
+ }
3122
+ const x1 = from.col * (cellSize + gap) + cellSize / 2;
3123
+ const y1 = from.row * (cellSize + gap) + cellSize / 2;
3124
+ const x2 = to.col * (cellSize + gap) + cellSize / 2;
3125
+ const y2 = to.row * (cellSize + gap) + cellSize / 2;
3126
+ return /* @__PURE__ */ jsxs20("g", { children: [
3127
+ /* @__PURE__ */ jsx22(
3128
+ "line",
3129
+ {
3130
+ x1,
3131
+ y1,
3132
+ x2,
3133
+ y2,
3134
+ stroke: "currentColor",
3135
+ strokeOpacity: "0.35",
3136
+ strokeWidth: "2"
3137
+ }
3138
+ ),
3139
+ connection.label && /* @__PURE__ */ jsx22(
3140
+ "text",
3141
+ {
3142
+ x: (x1 + x2) / 2,
3143
+ y: (y1 + y2) / 2 - 4,
3144
+ textAnchor: "middle",
3145
+ className: "fill-text-secondary text-[10px]",
3146
+ children: connection.label
3147
+ }
3148
+ )
3149
+ ] }, `${connection.from}-${connection.to}`);
3150
+ })
3151
+ }
3152
+ ),
3153
+ steps.map((step) => {
3154
+ const selected = selectedStepId === step.id;
3155
+ return /* @__PURE__ */ jsx22(
3156
+ "div",
3157
+ {
3158
+ role: "gridcell",
3159
+ tabIndex: 0,
3160
+ "aria-selected": selected,
3161
+ "aria-label": `${step.title} (${step.row + 1}, ${step.col + 1})`,
3162
+ onKeyDown: (event) => handleStepKeyDown(event, step),
3163
+ className: "focus-visible:ring-ring absolute focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-white focus-visible:outline-none",
3164
+ style: {
3165
+ width: cellSize,
3166
+ height: cellSize,
3167
+ left: step.col * (cellSize + gap),
3168
+ top: step.row * (cellSize + gap)
3169
+ },
3170
+ children: renderStep ? renderStep(step, selected) : renderDefaultStep(step, selected)
3171
+ },
3172
+ step.id
3173
+ );
3174
+ })
3175
+ ]
3176
+ }
3177
+ ) })
3178
+ ]
3179
+ }
3180
+ );
3181
+ }
3182
+ WorkflowCanvas.displayName = "WorkflowCanvas";
3183
+
3184
+ export {
3185
+ Command,
3186
+ CommandDialog,
3187
+ CommandInput,
3188
+ CommandList,
3189
+ CommandEmpty,
3190
+ CommandGroup,
3191
+ CommandSeparator,
3192
+ CommandItem,
3193
+ CommandShortcut,
3194
+ CommandPalette,
3195
+ CommandPaletteTrigger,
3196
+ DashboardGrid,
3197
+ SearchCombobox,
3198
+ StatusSelect,
3199
+ AppMenubar,
3200
+ SidebarNav,
3201
+ EditorToolbar,
3202
+ ContentTabs,
3203
+ FaqAccordion,
3204
+ SettingsForm,
3205
+ DeploymentTimeline,
3206
+ ProjectSwitcher,
3207
+ EnvPanel,
3208
+ BranchSelect,
3209
+ DomainTable,
3210
+ DataTable,
3211
+ toast,
3212
+ ToastProvider,
3213
+ useToast,
3214
+ NotificationToast,
3215
+ FailureList,
3216
+ KeyValueEditor,
3217
+ WorkflowCanvas
3218
+ };