@reopt-ai/opt-ui 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/COMPONENT_CATALOG.md +83 -38
  2. package/README.md +28 -7
  3. package/dist/app.css +146 -0
  4. package/dist/core/index.cjs +164 -324
  5. package/dist/core/index.d.cts +3 -6
  6. package/dist/core/index.d.ts +3 -6
  7. package/dist/core/index.js +3 -323
  8. package/dist/docs/01-getting-started.md +25 -5
  9. package/dist/docs/02-components/01-core.md +36 -28
  10. package/dist/docs/02-components/02-visuals.md +1 -1
  11. package/dist/docs/02-components/03-shells.md +40 -12
  12. package/dist/docs/02-components/04-surfaces.md +1 -1
  13. package/dist/docs/02-components/index.md +1 -1
  14. package/dist/docs/03-recipes/03-layouts.md +16 -12
  15. package/dist/docs/05-migration/01-breaking-changes.md +28 -0
  16. package/dist/id-registry.cjs +1899 -1872
  17. package/dist/id-registry.d.cts +13 -10
  18. package/dist/id-registry.d.cts.map +1 -0
  19. package/dist/id-registry.d.ts +13 -10
  20. package/dist/id-registry.d.ts.map +1 -0
  21. package/dist/id-registry.js +1897 -1871
  22. package/dist/id-registry.js.map +1 -0
  23. package/dist/id-registry.json +28 -28
  24. package/dist/index.cjs +8294 -9790
  25. package/dist/index.d.cts +1486 -1442
  26. package/dist/index.d.cts.map +1 -0
  27. package/dist/index.d.ts +1486 -1442
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +7933 -9777
  30. package/dist/index.js.map +1 -0
  31. package/dist/meta.cjs +7461 -6875
  32. package/dist/meta.d.cts.map +1 -0
  33. package/dist/meta.d.ts.map +1 -0
  34. package/dist/meta.js +7460 -6846
  35. package/dist/meta.js.map +1 -0
  36. package/dist/pagination-By7Ozv3U.js +3788 -0
  37. package/dist/pagination-By7Ozv3U.js.map +1 -0
  38. package/dist/pagination-Uv3CnGJz.cjs +4677 -0
  39. package/dist/shader-surface-BK1GMt5_.d.cts +1788 -0
  40. package/dist/shader-surface-BK1GMt5_.d.cts.map +1 -0
  41. package/dist/shader-surface-BWhO2xkk.js +3715 -0
  42. package/dist/shader-surface-BWhO2xkk.js.map +1 -0
  43. package/dist/shader-surface-DIVWtpFl.d.ts +1788 -0
  44. package/dist/shader-surface-DIVWtpFl.d.ts.map +1 -0
  45. package/dist/shader-surface-DxLLfrE6.cjs +4108 -0
  46. package/dist/shells/index.cjs +51 -65
  47. package/dist/shells/index.d.cts +4 -5
  48. package/dist/shells/index.d.ts +4 -5
  49. package/dist/shells/index.js +3 -64
  50. package/dist/tailwind.css +49 -0
  51. package/dist/theme/server.cjs +257 -228
  52. package/dist/theme/server.d.cts +48 -60
  53. package/dist/theme/server.d.cts.map +1 -0
  54. package/dist/theme/server.d.ts +48 -60
  55. package/dist/theme/server.d.ts.map +1 -0
  56. package/dist/theme/server.js +234 -189
  57. package/dist/theme/server.js.map +1 -0
  58. package/dist/types-D0FlcYnM.d.cts +301 -0
  59. package/dist/types-D0FlcYnM.d.cts.map +1 -0
  60. package/dist/types-D0FlcYnM.d.ts +301 -0
  61. package/dist/types-D0FlcYnM.d.ts.map +1 -0
  62. package/dist/visuals/index.cjs +10 -4
  63. package/dist/visuals/index.d.cts +2 -1
  64. package/dist/visuals/index.d.ts +2 -1
  65. package/dist/visuals/index.js +1 -2
  66. package/dist/workflow-canvas-3Jeevkgy.cjs +2599 -0
  67. package/dist/workflow-canvas-BGBAcjf-.js +2403 -0
  68. package/dist/workflow-canvas-BGBAcjf-.js.map +1 -0
  69. package/dist/workflow-canvas-Bar_O2SQ.d.ts +494 -0
  70. package/dist/workflow-canvas-Bar_O2SQ.d.ts.map +1 -0
  71. package/dist/workflow-canvas-DghDRtHn.d.cts +494 -0
  72. package/dist/workflow-canvas-DghDRtHn.d.cts.map +1 -0
  73. package/package.json +11 -6
  74. package/dist/chunk-3GWWZKX7.js +0 -38
  75. package/dist/chunk-3QFYBBL6.cjs +0 -4759
  76. package/dist/chunk-3XD4HIFL.js +0 -3241
  77. package/dist/chunk-AFF2HPE5.cjs +0 -5008
  78. package/dist/chunk-NXZJIHEZ.cjs +0 -3241
  79. package/dist/chunk-ONE3C5RV.cjs +0 -38
  80. package/dist/chunk-RBM2RNC2.js +0 -5008
  81. package/dist/chunk-VBGPEW45.js +0 -4759
  82. package/dist/index-BZ_lBlO1.d.ts +0 -474
  83. package/dist/index-BuvxoWHf.d.cts +0 -474
  84. package/dist/index-DWyqDIkH.d.cts +0 -1689
  85. package/dist/index-Uyijm14M.d.ts +0 -1689
  86. package/dist/types-D4-0lwaE.d.cts +0 -298
  87. package/dist/types-D4-0lwaE.d.ts +0 -298
@@ -0,0 +1,2599 @@
1
+ "use client";
2
+ const require_pagination = require("./pagination-Uv3CnGJz.cjs");
3
+ let react_jsx_runtime = require("react/jsx-runtime");
4
+ let react = require("react");
5
+ react = require_pagination.__toESM(react, 1);
6
+ let _reopt_ai_opt_ui_primitives = require("@reopt-ai/opt-ui-primitives");
7
+ let lucide_react = require("lucide-react");
8
+ let match_sorter = require("match-sorter");
9
+ let next_link = require("next/link");
10
+ next_link = require_pagination.__toESM(next_link, 1);
11
+ let next_navigation = require("next/navigation");
12
+ //#region src/core/command.tsx
13
+ const _excluded = ["className"];
14
+ const _excluded2 = [
15
+ "className",
16
+ "trailing",
17
+ "wrapperClassName",
18
+ "placeholder",
19
+ "aria-label"
20
+ ];
21
+ const _excluded3 = ["className", "aria-label"];
22
+ const _excluded4 = ["className"];
23
+ const _excluded5 = ["className", "heading"];
24
+ const _excluded6 = ["className"];
25
+ const _excluded7 = ["className"];
26
+ const _excluded8 = ["className"];
27
+ /** Renders the `Command` component. */
28
+ function Command(_ref) {
29
+ let { className } = _ref, props = require_pagination._objectWithoutProperties(_ref, _excluded);
30
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CommandRoot, require_pagination._objectSpread2(require_pagination._objectSpread2({ className: require_pagination.cn("bg-surface text-text-primary flex w-full flex-col overflow-hidden rounded-xl", className) }, props), {}, {
31
+ "data-opt-id": "4FYBW",
32
+ "data-opt-slug": "command.Command"
33
+ }));
34
+ }
35
+ /** Renders a Command in a dialog overlay, à la Cmd+K palette. */
36
+ function CommandDialog({ open, onOpenChange, title = "Command Palette", description = "검색 후 Enter 또는 클릭으로 명령을 실행하세요.", className, commandClassName, shouldFilter, filter, children }) {
37
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.DialogRoot, {
38
+ open,
39
+ onOpenChange,
40
+ "data-opt-id": "DVRSX",
41
+ "data-opt-slug": "command.CommandDialog",
42
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_pagination.DialogPanel, {
43
+ className: require_pagination.cn("top-[20%] left-1/2 -translate-x-1/2", "w-full max-w-lg overflow-hidden p-0 shadow-2xl", className),
44
+ children: [
45
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.DialogHeading, {
46
+ className: "sr-only",
47
+ children: title
48
+ }),
49
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.DialogDescription, {
50
+ className: "sr-only",
51
+ children: description
52
+ }),
53
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Command, {
54
+ shouldFilter,
55
+ filter,
56
+ className: commandClassName,
57
+ children
58
+ })
59
+ ]
60
+ })
61
+ });
62
+ }
63
+ /** Renders the `CommandInput` component with a leading search icon. */
64
+ function CommandInput(_ref2) {
65
+ let { className, trailing, wrapperClassName, placeholder, "aria-label": ariaLabel } = _ref2, props = require_pagination._objectWithoutProperties(_ref2, _excluded2);
66
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
67
+ "data-slot": "command-input-wrapper",
68
+ className: require_pagination.cn("border-border-subtle gap-element flex h-12 items-center border-b px-3", wrapperClassName),
69
+ "data-opt-id": "HB1K3",
70
+ "data-opt-slug": "command.CommandInput",
71
+ children: [
72
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.SearchIcon, {
73
+ "aria-hidden": true,
74
+ className: "text-text-tertiary size-4 shrink-0"
75
+ }),
76
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CommandInput, require_pagination._objectSpread2({
77
+ className: require_pagination.cn("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", "disabled:cursor-not-allowed disabled:opacity-50", className),
78
+ placeholder,
79
+ "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : typeof placeholder === "string" ? placeholder : "Command search"
80
+ }, props)),
81
+ trailing
82
+ ]
83
+ });
84
+ }
85
+ /** Renders the `CommandList` component. */
86
+ function CommandList(_ref3) {
87
+ let { className, "aria-label": ariaLabel } = _ref3, props = require_pagination._objectWithoutProperties(_ref3, _excluded3);
88
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CommandList, require_pagination._objectSpread2(require_pagination._objectSpread2({
89
+ className: require_pagination.cn("max-h-80 scroll-py-1 overflow-x-hidden overflow-y-auto p-1", className),
90
+ "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : "Commands"
91
+ }, props), {}, {
92
+ "data-opt-id": "NPXC5",
93
+ "data-opt-slug": "command.CommandList"
94
+ }));
95
+ }
96
+ /** Renders the `CommandEmpty` component. */
97
+ function CommandEmpty(_ref4) {
98
+ let { className } = _ref4, props = require_pagination._objectWithoutProperties(_ref4, _excluded4);
99
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CommandEmpty, require_pagination._objectSpread2(require_pagination._objectSpread2({ className: require_pagination.cn("text-text-tertiary py-6 text-center text-sm", className) }, props), {}, {
100
+ "data-opt-id": "A8BZM",
101
+ "data-opt-slug": "command.CommandEmpty"
102
+ }));
103
+ }
104
+ /** Renders the `CommandGroup` component with an optional heading. */
105
+ function CommandGroup(_ref5) {
106
+ let { className, heading } = _ref5, props = require_pagination._objectWithoutProperties(_ref5, _excluded5);
107
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CommandGroup, require_pagination._objectSpread2(require_pagination._objectSpread2({
108
+ heading: heading != null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
109
+ className: "text-text-tertiary px-2 py-1.5 text-xs font-medium",
110
+ children: heading
111
+ }) : void 0,
112
+ className: require_pagination.cn("py-1", className)
113
+ }, props), {}, {
114
+ "data-opt-id": "JPK1Y",
115
+ "data-opt-slug": "command.CommandGroup"
116
+ }));
117
+ }
118
+ /** Renders the `CommandSeparator` component. */
119
+ function CommandSeparator(_ref6) {
120
+ let { className } = _ref6, props = require_pagination._objectWithoutProperties(_ref6, _excluded6);
121
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CommandSeparator, require_pagination._objectSpread2(require_pagination._objectSpread2({ className: require_pagination.cn("bg-border-subtle -mx-1 my-1 h-px", className) }, props), {}, {
122
+ "data-opt-id": "10P0E",
123
+ "data-opt-slug": "command.CommandSeparator"
124
+ }));
125
+ }
126
+ /** Renders the `CommandItem` component. */
127
+ function CommandItem(_ref7) {
128
+ let { className } = _ref7, props = require_pagination._objectWithoutProperties(_ref7, _excluded7);
129
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CommandItem, require_pagination._objectSpread2(require_pagination._objectSpread2({ className: require_pagination.cn("text-text-primary flex cursor-pointer items-center gap-2 rounded-md px-2 py-2 text-sm select-none", "data-[active-item]:bg-accent data-[active-item]:text-accent-fg", "data-[disabled]:pointer-events-none data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50", "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className) }, props), {}, {
130
+ "data-opt-id": "K674W",
131
+ "data-opt-slug": "command.CommandItem"
132
+ }));
133
+ }
134
+ /** Renders the `CommandShortcut` component (right-aligned hint). */
135
+ function CommandShortcut(_ref8) {
136
+ let { className } = _ref8, props = require_pagination._objectWithoutProperties(_ref8, _excluded8);
137
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", require_pagination._objectSpread2(require_pagination._objectSpread2({
138
+ "data-slot": "command-shortcut",
139
+ className: require_pagination.cn("text-text-tertiary ml-auto text-xs tracking-widest", "data-[active-item]:text-accent-fg/80", className)
140
+ }, props), {}, {
141
+ "data-opt-id": "75CQK",
142
+ "data-opt-slug": "command.CommandShortcut"
143
+ }));
144
+ }
145
+ //#endregion
146
+ //#region src/shells/command-palette.tsx
147
+ const DEFAULT_LABELS = {
148
+ title: "Command Palette",
149
+ description: "검색 후 Enter 또는 클릭으로 명령을 실행하세요.",
150
+ input: "명령어 검색",
151
+ placeholder: "명령어를 검색하세요...",
152
+ list: "명령어 목록",
153
+ empty: "결과가 없습니다.",
154
+ escape: "ESC"
155
+ };
156
+ /** Renders the command palette component. */
157
+ function CommandPalette({ commands, open, onOpenChange, onSelect, inline = false, labels, footer, empty, inputTrailing, className }) {
158
+ const resolvedLabels = require_pagination._objectSpread2(require_pagination._objectSpread2({}, DEFAULT_LABELS), labels);
159
+ const hasCommands = commands.length > 0;
160
+ const grouped = (0, react.useMemo)(() => {
161
+ const map = /* @__PURE__ */ new Map();
162
+ for (const cmd of commands) {
163
+ var _map$get;
164
+ const list = (_map$get = map.get(cmd.group)) !== null && _map$get !== void 0 ? _map$get : [];
165
+ list.push(cmd);
166
+ map.set(cmd.group, list);
167
+ }
168
+ return map;
169
+ }, [commands]);
170
+ const handleSelect = (cmdId) => {
171
+ const cmd = commands.find((c) => c.id === cmdId);
172
+ if (!cmd) return;
173
+ onSelect === null || onSelect === void 0 || onSelect(cmd);
174
+ onOpenChange(false);
175
+ };
176
+ const handleInputKeyDown = (e) => {
177
+ if (e.key === "Escape" && !inline) {
178
+ e.preventDefault();
179
+ onOpenChange(false);
180
+ }
181
+ };
182
+ const body = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
183
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommandInput, {
184
+ "aria-label": resolvedLabels.input,
185
+ placeholder: resolvedLabels.placeholder,
186
+ onKeyDown: handleInputKeyDown,
187
+ trailing: inputTrailing !== null && inputTrailing !== void 0 ? inputTrailing : !inline ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.Kbd, { children: resolvedLabels.escape }) : void 0
188
+ }),
189
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CommandList, {
190
+ "aria-label": resolvedLabels.list,
191
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommandEmpty, { children: empty !== null && empty !== void 0 ? empty : resolvedLabels.empty }), hasCommands ? Array.from(grouped.entries()).map(([group, items]) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommandGroup, {
192
+ heading: group,
193
+ children: items.map((cmd) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CommandItem, {
194
+ value: cmd.id,
195
+ keywords: [
196
+ cmd.label,
197
+ cmd.group,
198
+ ...cmd.shortcut ? [cmd.shortcut] : []
199
+ ],
200
+ onSelect: handleSelect,
201
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: cmd.label }), cmd.shortcut && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommandShortcut, { children: cmd.shortcut })]
202
+ }, cmd.id))
203
+ }, group)) : null]
204
+ }),
205
+ footer
206
+ ] });
207
+ if (inline) {
208
+ if (!open) return null;
209
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Command, {
210
+ className: require_pagination.cn("border-border bg-surface w-full max-w-lg border shadow-lg", className),
211
+ "data-opt-id": "ATTF5",
212
+ "data-opt-slug": "command-palette.CommandPalette",
213
+ children: body
214
+ });
215
+ }
216
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommandDialog, {
217
+ open,
218
+ onOpenChange,
219
+ title: resolvedLabels.title,
220
+ description: resolvedLabels.description,
221
+ className,
222
+ "data-opt-id": "ATTF5",
223
+ "data-opt-slug": "command-palette.CommandPalette",
224
+ children: body
225
+ });
226
+ }
227
+ //#endregion
228
+ //#region src/shells/command-palette-trigger.tsx
229
+ /** Renders the command palette trigger component. */
230
+ function CommandPaletteTrigger({ commands, onSelect, labels }) {
231
+ if (commands.length === 0) return null;
232
+ const [open, setOpen] = (0, react.useState)(false);
233
+ const handleKeyDown = (0, react.useCallback)((e) => {
234
+ if ((e.metaKey || e.ctrlKey) && e.key === "k") {
235
+ e.preventDefault();
236
+ setOpen(true);
237
+ }
238
+ }, []);
239
+ (0, react.useEffect)(() => {
240
+ document.addEventListener("keydown", handleKeyDown);
241
+ return () => document.removeEventListener("keydown", handleKeyDown);
242
+ }, [handleKeyDown]);
243
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommandPalette, {
244
+ commands,
245
+ open,
246
+ onOpenChange: setOpen,
247
+ onSelect,
248
+ labels,
249
+ "data-opt-id": "4FCDB",
250
+ "data-opt-slug": "command-palette-trigger.CommandPaletteTrigger"
251
+ });
252
+ }
253
+ //#endregion
254
+ //#region src/shells/dashboard-grid.tsx
255
+ const defaultLabels$13 = { ariaLabel: "대시보드 통계" };
256
+ const MIN_DASHBOARD_COLUMNS = 1;
257
+ const MAX_DASHBOARD_COLUMNS = 12;
258
+ function normalizeDashboardColumns(columns) {
259
+ if (!Number.isFinite(columns)) return MIN_DASHBOARD_COLUMNS;
260
+ return Math.min(MAX_DASHBOARD_COLUMNS, Math.max(MIN_DASHBOARD_COLUMNS, Math.floor(columns)));
261
+ }
262
+ /** Renders the dashboard grid component. */
263
+ function DashboardGrid({ stats, columns = 3, labels: customLabels }) {
264
+ const labels = require_pagination._objectSpread2(require_pagination._objectSpread2({}, defaultLabels$13), customLabels);
265
+ const columnCount = normalizeDashboardColumns(columns);
266
+ const rowStyle = { "--opt-dashboard-grid-columns": `repeat(${columnCount}, minmax(0, 1fr))` };
267
+ const rows = [];
268
+ for (let i = 0; i < stats.length; i += columnCount) rows.push(stats.slice(i, i + columnCount));
269
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CompositeProvider, {
270
+ focusLoop: true,
271
+ focusWrap: true,
272
+ "data-opt-id": "BM7NA",
273
+ "data-opt-slug": "dashboard-grid.DashboardGrid",
274
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.Composite, {
275
+ role: "grid",
276
+ "aria-label": labels.ariaLabel,
277
+ className: "gap-group flex flex-col",
278
+ children: rows.map((row, ri) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CompositeRow, {
279
+ className: "gap-group grid grid-cols-1 sm:[grid-template-columns:var(--opt-dashboard-grid-columns)]",
280
+ style: rowStyle,
281
+ children: row.map((stat) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CompositeItem, {
282
+ 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",
283
+ render: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { role: "gridcell" }),
284
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.StatCard, require_pagination._objectSpread2({}, stat))
285
+ }, stat.id))
286
+ }, ri))
287
+ })
288
+ });
289
+ }
290
+ //#endregion
291
+ //#region src/shells/search-combobox.tsx
292
+ const defaultLabels$12 = {
293
+ noResults: "결과가 없습니다.",
294
+ loading: "Loading…"
295
+ };
296
+ /** Renders the search combobox component. */
297
+ function SearchCombobox({ items, placeholder = "검색...", label = "검색", onSelect, labels: customLabels, async: isAsync = false, onSearch, debounceMs = 300 }) {
298
+ const labels = require_pagination._objectSpread2(require_pagination._objectSpread2({}, defaultLabels$12), customLabels);
299
+ const inputId = (0, react.useId)();
300
+ const [query, setQuery] = (0, react.useState)("");
301
+ const [asyncResults, setAsyncResults] = (0, react.useState)([]);
302
+ const [loading, setLoading] = (0, react.useState)(false);
303
+ const timerRef = (0, react.useRef)(void 0);
304
+ const searchGenerationRef = (0, react.useRef)(0);
305
+ const syncMatches = (0, react.useMemo)(() => {
306
+ if (isAsync) return [];
307
+ if (!query) return items;
308
+ return (0, match_sorter.matchSorter)(items, query);
309
+ }, [
310
+ query,
311
+ items,
312
+ isAsync
313
+ ]);
314
+ const handleInput = (event) => {
315
+ searchGenerationRef.current += 1;
316
+ setQuery(event.currentTarget.value);
317
+ };
318
+ const handleValueChange = (item) => {
319
+ searchGenerationRef.current += 1;
320
+ setQuery(item);
321
+ onSelect === null || onSelect === void 0 || onSelect(item);
322
+ };
323
+ (0, react.useEffect)(() => {
324
+ const generation = ++searchGenerationRef.current;
325
+ if (timerRef.current) clearTimeout(timerRef.current);
326
+ if (!isAsync || !onSearch) {
327
+ setLoading(false);
328
+ return;
329
+ }
330
+ if (!query) {
331
+ setAsyncResults([]);
332
+ setLoading(false);
333
+ return;
334
+ }
335
+ setLoading(true);
336
+ timerRef.current = setTimeout(async () => {
337
+ try {
338
+ const results = await onSearch(query);
339
+ if (searchGenerationRef.current !== generation) return;
340
+ setAsyncResults(results);
341
+ } finally {
342
+ if (searchGenerationRef.current === generation) setLoading(false);
343
+ }
344
+ }, debounceMs);
345
+ return () => {
346
+ if (timerRef.current) clearTimeout(timerRef.current);
347
+ };
348
+ }, [
349
+ query,
350
+ isAsync,
351
+ onSearch,
352
+ debounceMs
353
+ ]);
354
+ const matches = isAsync ? asyncResults : syncMatches;
355
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
356
+ className: "gap-element flex flex-col",
357
+ "data-opt-id": "KH4TQ",
358
+ "data-opt-slug": "search-combobox.SearchCombobox",
359
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_pagination.ComboboxRoot, {
360
+ onValueChange: handleValueChange,
361
+ children: [
362
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
363
+ htmlFor: inputId,
364
+ className: require_pagination.OPT_TEXT_LABEL,
365
+ children: label
366
+ }),
367
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.ComboboxInput, {
368
+ id: inputId,
369
+ placeholder,
370
+ className: "max-w-xs",
371
+ "aria-busy": loading || void 0,
372
+ onInput: handleInput
373
+ }),
374
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.ComboboxPopover, {
375
+ className: "max-w-xs",
376
+ children: isAsync && loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
377
+ className: "text-text-secondary gap-element flex items-center px-3 py-2 text-sm",
378
+ role: "status",
379
+ "aria-live": "polite",
380
+ "aria-busy": "true",
381
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.Spinner, {
382
+ size: "sm",
383
+ "aria-hidden": "true"
384
+ }), labels.loading]
385
+ }) : matches.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.ComboboxEmpty, { children: labels.noResults }) : matches.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.ComboboxItem, {
386
+ value: item,
387
+ children: item
388
+ }, item))
389
+ })
390
+ ]
391
+ })
392
+ });
393
+ }
394
+ //#endregion
395
+ //#region src/shells/status-select.tsx
396
+ const defaultLabels$11 = {
397
+ selectLabel: "상태",
398
+ emptyMessage: "사용 가능한 상태 옵션이 없습니다."
399
+ };
400
+ /** Renders the status select component. */
401
+ function StatusSelect({ options, value: controlledValue, onChange, label, labels: customLabels }) {
402
+ var _options$0$value, _options$;
403
+ const labels = require_pagination._objectSpread2(require_pagination._objectSpread2({}, defaultLabels$11), customLabels);
404
+ const resolvedLabel = label !== null && label !== void 0 ? label : labels.selectLabel;
405
+ const [internalValue, setInternalValue] = (0, react.useState)((_options$0$value = (_options$ = options[0]) === null || _options$ === void 0 ? void 0 : _options$.value) !== null && _options$0$value !== void 0 ? _options$0$value : "");
406
+ const value = controlledValue !== null && controlledValue !== void 0 ? controlledValue : internalValue;
407
+ const selected = options.find((s) => s.value === value);
408
+ if (options.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
409
+ className: "gap-element flex flex-col",
410
+ "data-opt-id": "R46E6",
411
+ "data-opt-slug": "status-select.StatusSelect",
412
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
413
+ className: require_pagination.OPT_TEXT_LABEL,
414
+ children: resolvedLabel
415
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
416
+ className: "text-text-tertiary text-sm",
417
+ children: labels.emptyMessage
418
+ })]
419
+ });
420
+ const handleChange = (v) => {
421
+ var _v$;
422
+ const next = Array.isArray(v) ? (_v$ = v[0]) !== null && _v$ !== void 0 ? _v$ : "" : v;
423
+ if (onChange) onChange(next);
424
+ else setInternalValue(next);
425
+ };
426
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
427
+ className: "gap-element flex flex-col",
428
+ "data-opt-id": "R46E6",
429
+ "data-opt-slug": "status-select.StatusSelect",
430
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_pagination.SelectRoot, {
431
+ value,
432
+ onValueChange: handleChange,
433
+ children: [
434
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.SelectLabel, {
435
+ className: require_pagination.OPT_TEXT_LABEL,
436
+ children: resolvedLabel
437
+ }),
438
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_pagination.SelectTrigger, {
439
+ className: `${require_pagination.OPT_INPUT} max-w-xs items-center justify-between ${require_pagination.OPT_FOCUS}`,
440
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
441
+ className: "gap-element flex items-center",
442
+ children: [(selected === null || selected === void 0 ? void 0 : selected.color) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: `h-2 w-2 rounded-full ${selected.color}` }), selected === null || selected === void 0 ? void 0 : selected.label]
443
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
444
+ className: "text-text-tertiary h-4 w-4",
445
+ fill: "none",
446
+ viewBox: "0 0 24 24",
447
+ stroke: "currentColor",
448
+ strokeWidth: 2,
449
+ "aria-hidden": "true",
450
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
451
+ strokeLinecap: "round",
452
+ strokeLinejoin: "round",
453
+ d: "m19.5 8.25-7.5 7.5-7.5-7.5"
454
+ })
455
+ })]
456
+ }),
457
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.SelectPopover, {
458
+ className: `${require_pagination.OPT_SURFACE_POPOVER} ${require_pagination.OPT_ANIMATE_ENTER_EXIT} max-w-xs`,
459
+ children: options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_pagination.SelectItem, {
460
+ value: option.value,
461
+ children: [option.color && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: `h-2 w-2 rounded-full ${option.color}` }), option.label]
462
+ }, option.value))
463
+ })
464
+ ]
465
+ })
466
+ });
467
+ }
468
+ //#endregion
469
+ //#region src/shells/app-menubar.tsx
470
+ const defaultLabels$10 = { ariaLabel: "Application menu" };
471
+ function MenuSection({ menu }) {
472
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_pagination.MenuRoot, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_pagination.MenuTrigger, { children: [menu.label, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.MenuButtonArrow, {})] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.MenuPopover, { children: menu.items.map((item, i) => {
473
+ if (item.type === "separator") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.MenuSeparator, {}, `sep-${i}`);
474
+ if (item.type === "checkbox") {
475
+ var _item$name, _item$value;
476
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.MenuItemCheckbox, {
477
+ name: (_item$name = item.name) !== null && _item$name !== void 0 ? _item$name : item.id,
478
+ value: (_item$value = item.value) !== null && _item$value !== void 0 ? _item$value : item.id,
479
+ checked: item.checked,
480
+ onChange: item.onChange,
481
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
482
+ className: "gap-element flex items-center",
483
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
484
+ className: "w-4 text-center",
485
+ children: item.checked ? "✓" : ""
486
+ }), item.label]
487
+ })
488
+ }, item.id);
489
+ }
490
+ if (item.type === "radio") {
491
+ var _item$name2, _item$value2;
492
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.MenuItemRadio, {
493
+ name: (_item$name2 = item.name) !== null && _item$name2 !== void 0 ? _item$name2 : "",
494
+ value: (_item$value2 = item.value) !== null && _item$value2 !== void 0 ? _item$value2 : "",
495
+ checked: item.checked,
496
+ onChange: item.onChange,
497
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
498
+ className: "gap-element flex items-center",
499
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
500
+ className: "w-4 text-center",
501
+ children: item.checked ? "●" : ""
502
+ }), item.label]
503
+ })
504
+ }, item.id);
505
+ }
506
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_pagination.MenuItem, {
507
+ onClick: item.onClick,
508
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: item.label }), item.shortcut && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.Kbd, { children: item.shortcut })]
509
+ }, item.id);
510
+ }) })] });
511
+ }
512
+ /** Renders the app menubar component. */
513
+ function AppMenubar({ menus, labels: customLabels }) {
514
+ const labels = require_pagination._objectSpread2(require_pagination._objectSpread2({}, defaultLabels$10), customLabels);
515
+ if (menus.length === 0) return null;
516
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.MenubarRoot, {
517
+ "data-opt-id": "BGKHW",
518
+ "data-opt-slug": "app-menubar.AppMenubar",
519
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.MenubarContainer, {
520
+ "aria-label": labels.ariaLabel,
521
+ children: menus.map((menu) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MenuSection, { menu }, menu.id))
522
+ })
523
+ });
524
+ }
525
+ AppMenubar.displayName = "AppMenubar";
526
+ //#endregion
527
+ //#region src/shells/sidebar-nav.tsx
528
+ const NAV_ID_PREFIX = "sidebar-nav-";
529
+ function domId(id) {
530
+ return `${NAV_ID_PREFIX}${id}`;
531
+ }
532
+ const sectionStyles = {
533
+ 시작하기: "text-accent",
534
+ "핵심 개념": "text-info-fg",
535
+ 구축·운영: "text-success-fg",
536
+ 도구: "text-warning-fg",
537
+ DataGrid: "text-accent",
538
+ "컴포넌트 · Core": "text-accent",
539
+ "컴포넌트 · Visuals": "text-success-fg",
540
+ "컴포넌트 · Shells": "text-warning-fg",
541
+ "컴포넌트 · Surfaces": "text-info-fg",
542
+ "기본 컴포넌트": "text-accent",
543
+ "블록 컴포넌트": "text-info-fg",
544
+ "조합 컴포넌트": "text-warning-fg",
545
+ 템플릿: "text-text-tertiary",
546
+ 릴리즈: "text-text-tertiary",
547
+ Primitives: "text-accent",
548
+ Blocks: "text-info-fg",
549
+ Compositions: "text-warning-fg",
550
+ Templates: "text-text-tertiary"
551
+ };
552
+ const defaultSectionStyle = "text-text-secondary";
553
+ const defaultLabels$9 = { ariaLabel: "문서 네비게이션" };
554
+ /** Renders the sidebar nav component. */
555
+ function SidebarNav({ items, labels: customLabels }) {
556
+ const labels = require_pagination._objectSpread2(require_pagination._objectSpread2({}, defaultLabels$9), customLabels);
557
+ if (items.length === 0) return null;
558
+ const pathname = (0, next_navigation.usePathname)();
559
+ const activeItem = items.find((item) => item.href === pathname);
560
+ const [activeId, setActiveId] = (0, react.useState)(activeItem ? domId(activeItem.id) : void 0);
561
+ (0, react.useEffect)(() => {
562
+ const match = items.find((item) => item.href === pathname);
563
+ if (match) setActiveId(domId(match.id));
564
+ }, [pathname, items]);
565
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CompositeProvider, {
566
+ focusLoop: true,
567
+ orientation: "vertical",
568
+ activeId,
569
+ onActiveIdChange: setActiveId,
570
+ "data-opt-id": "1FZNH",
571
+ "data-opt-slug": "sidebar-nav.SidebarNav",
572
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.Composite, {
573
+ role: "navigation",
574
+ "aria-label": labels.ariaLabel,
575
+ className: "flex flex-col",
576
+ children: items.map((item, index) => {
577
+ var _sectionStyles$item$s, _item$sectionLabel;
578
+ const isSelected = pathname === item.href;
579
+ const isFirstItem = index === 0;
580
+ const sectionTextStyle = item.sectionLabel ? (_sectionStyles$item$s = sectionStyles[item.sectionLabel]) !== null && _sectionStyles$item$s !== void 0 ? _sectionStyles$item$s : defaultSectionStyle : defaultSectionStyle;
581
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [item.separator && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
582
+ className: require_pagination.cn("mb-2 pt-2", !isFirstItem && "mt-7"),
583
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
584
+ className: require_pagination.cn("px-2 text-[11px] font-semibold tracking-wider uppercase", sectionTextStyle),
585
+ children: (_item$sectionLabel = item.sectionLabel) !== null && _item$sectionLabel !== void 0 ? _item$sectionLabel : ""
586
+ })
587
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_reopt_ai_opt_ui_primitives.CompositeItem, {
588
+ id: domId(item.id),
589
+ render: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(next_link.default, {
590
+ href: item.href,
591
+ className: require_pagination.cn("group relative block rounded-md px-2 py-1.5 text-[13px] leading-snug outline-none", "transition-all duration-150", !isSelected && ["text-text-secondary", "hover:bg-bg-subtle hover:text-text-primary"], isSelected && ["bg-bg-subtle text-text-primary font-medium"], "data-[active-item]:bg-accent-subtle data-[active-item]:text-accent", "data-[focus-visible]:ring-ring data-[focus-visible]:ring-2", "data-[focus-visible]:ring-offset-ring-offset data-[focus-visible]:ring-offset-2", isSelected && ["data-[active-item]:bg-accent-subtle data-[active-item]:text-accent"])
592
+ }),
593
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: require_pagination.cn("absolute top-1/2 left-0 h-4 w-0.5 -translate-y-1/2 rounded-full transition-all duration-150", isSelected && "bg-accent", !isSelected && "group-data-[active-item]:bg-accent bg-transparent", "group-data-[focus-visible]:bg-accent") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
594
+ className: "pl-2",
595
+ children: item.label
596
+ })]
597
+ })] }, item.id);
598
+ })
599
+ })
600
+ });
601
+ }
602
+ //#endregion
603
+ //#region src/shells/editor-toolbar.tsx
604
+ const defaultLabels$8 = { ariaLabel: "텍스트 편집기 툴바" };
605
+ function ToolbarButtonWithTooltip({ button, active, onAction }) {
606
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_pagination.TooltipProvider, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.TooltipAnchor, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.ToolbarButton, {
607
+ 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"}`,
608
+ onClick: () => onAction === null || onAction === void 0 ? void 0 : onAction(button.id),
609
+ children: button.icon
610
+ }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.Tooltip, { children: button.label })] });
611
+ }
612
+ /** Renders the editor toolbar component. */
613
+ function EditorToolbar({ buttons, onAction, activeButtons = [], labels: customLabels }) {
614
+ const labels = require_pagination._objectSpread2(require_pagination._objectSpread2({}, defaultLabels$8), customLabels);
615
+ const groups = [];
616
+ let current = [];
617
+ for (const btn of buttons) {
618
+ if (btn.separator) {
619
+ groups.push(current);
620
+ current = [];
621
+ }
622
+ current.push(btn);
623
+ }
624
+ if (current.length) groups.push(current);
625
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.ToolbarRoot, {
626
+ "data-opt-id": "Z73K6",
627
+ "data-opt-slug": "editor-toolbar.EditorToolbar",
628
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.ToolbarContainer, {
629
+ "aria-label": labels.ariaLabel,
630
+ children: groups.map((group, gi) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
631
+ className: "contents",
632
+ children: [gi > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.ToolbarSeparator, {}), group.map((item) => {
633
+ if (item === "separator") return null;
634
+ const btn = item;
635
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarButtonWithTooltip, {
636
+ button: btn,
637
+ active: activeButtons.includes(btn.id),
638
+ onAction
639
+ }, btn.id);
640
+ })]
641
+ }, gi))
642
+ })
643
+ });
644
+ }
645
+ //#endregion
646
+ //#region src/shells/content-tabs.tsx
647
+ const defaultLabels$7 = {
648
+ ariaLabel: "콘텐츠 탭",
649
+ emptyMessage: "표시할 탭이 없습니다."
650
+ };
651
+ /** Renders the content tabs component. */
652
+ function ContentTabs({ tabs, defaultTabId, labels: customLabels }) {
653
+ var _tabs$, _tabs$find;
654
+ const labels = require_pagination._objectSpread2(require_pagination._objectSpread2({}, defaultLabels$7), customLabels);
655
+ if (tabs.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
656
+ className: "text-text-secondary text-sm",
657
+ "data-opt-id": "9W97K",
658
+ "data-opt-slug": "content-tabs.ContentTabs",
659
+ children: labels.emptyMessage
660
+ });
661
+ const [selectedTabId, setSelectedTabId] = (0, react.useState)(defaultTabId !== null && defaultTabId !== void 0 ? defaultTabId : (_tabs$ = tabs[0]) === null || _tabs$ === void 0 ? void 0 : _tabs$.id);
662
+ const handleSelectedTabId = (nextId) => {
663
+ var _tabs$2;
664
+ setSelectedTabId(nextId !== null && nextId !== void 0 ? nextId : (_tabs$2 = tabs[0]) === null || _tabs$2 === void 0 ? void 0 : _tabs$2.id);
665
+ };
666
+ const activeTab = (_tabs$find = tabs.find((tab) => tab.id === selectedTabId)) !== null && _tabs$find !== void 0 ? _tabs$find : tabs[0];
667
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_pagination.TabsRoot, {
668
+ selectedId: selectedTabId,
669
+ onSelectedIdChange: handleSelectedTabId,
670
+ "data-opt-id": "9W97K",
671
+ "data-opt-slug": "content-tabs.ContentTabs",
672
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.TabList, {
673
+ "aria-label": labels.ariaLabel,
674
+ children: tabs.map((tab) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.Tab, {
675
+ id: tab.id,
676
+ children: tab.label
677
+ }, tab.id))
678
+ }), activeTab && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.TabPanel, {
679
+ tabId: activeTab.id,
680
+ children: activeTab.content
681
+ })]
682
+ });
683
+ }
684
+ //#endregion
685
+ //#region src/shells/faq-accordion.tsx
686
+ const defaultLabels$6 = {
687
+ ariaLabel: "자주 묻는 질문",
688
+ emptyMessage: "등록된 FAQ가 없습니다."
689
+ };
690
+ /** Renders the faq accordion component. */
691
+ function FaqAccordion({ items, labels: customLabels }) {
692
+ const labels = require_pagination._objectSpread2(require_pagination._objectSpread2({}, defaultLabels$6), customLabels);
693
+ if (items.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
694
+ className: "text-text-secondary text-sm",
695
+ "data-opt-id": "QRBHV",
696
+ "data-opt-slug": "faq-accordion.FaqAccordion",
697
+ children: labels.emptyMessage
698
+ });
699
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CompositeProvider, {
700
+ focusLoop: true,
701
+ orientation: "vertical",
702
+ "data-opt-id": "QRBHV",
703
+ "data-opt-slug": "faq-accordion.FaqAccordion",
704
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.Composite, {
705
+ role: "region",
706
+ "aria-label": labels.ariaLabel,
707
+ className: "divide-border-subtle border-border divide-y rounded-lg border",
708
+ children: items.map((faq) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.DisclosureRoot, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_reopt_ai_opt_ui_primitives.CompositeItem, {
709
+ render: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.DisclosureTrigger, {}),
710
+ children: [faq.question, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
711
+ className: "text-text-tertiary h-4 w-4 shrink-0 transition-transform [[data-open]>&]:rotate-180",
712
+ fill: "none",
713
+ viewBox: "0 0 24 24",
714
+ stroke: "currentColor",
715
+ strokeWidth: 2,
716
+ "aria-hidden": "true",
717
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
718
+ strokeLinecap: "round",
719
+ strokeLinejoin: "round",
720
+ d: "m19.5 8.25-7.5 7.5-7.5-7.5"
721
+ })
722
+ })]
723
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.DisclosureContent, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
724
+ className: "text-text-secondary px-4 pt-2 pb-4 text-sm",
725
+ children: faq.answer
726
+ }) })] }) }, faq.id))
727
+ })
728
+ });
729
+ }
730
+ //#endregion
731
+ //#region src/shells/settings-form.tsx
732
+ const defaultLabels$5 = {
733
+ formAriaLabel: "설정 폼",
734
+ submitButton: "저장",
735
+ successMessage: "설정이 저장되었습니다."
736
+ };
737
+ /** Renders the settings form component. */
738
+ function SettingsForm({ fields, onSubmit, labels: customLabels }) {
739
+ const labels = require_pagination._objectSpread2(require_pagination._objectSpread2({}, defaultLabels$5), customLabels);
740
+ const [submitted, setSubmitted] = (0, react.useState)(false);
741
+ if (fields.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.EmptyState, {
742
+ title: "설정 항목이 없습니다.",
743
+ description: "표시할 설정 필드를 추가하면 이 폼이 렌더링됩니다.",
744
+ "data-opt-id": "9TEEJ",
745
+ "data-opt-slug": "settings-form.SettingsForm"
746
+ });
747
+ const defaultValues = {};
748
+ for (const field of fields) {
749
+ var _field$defaultValue;
750
+ defaultValues[field.name] = (_field$defaultValue = field.defaultValue) !== null && _field$defaultValue !== void 0 ? _field$defaultValue : field.type === "switch" || field.type === "checkbox" ? false : field.type === "tags" ? [] : "";
751
+ }
752
+ const form = (0, _reopt_ai_opt_ui_primitives.useFormStore)({
753
+ defaultValues,
754
+ validate: (values) => {
755
+ const errors = {};
756
+ for (const field of fields) {
757
+ if (field.required) {
758
+ const value = values[field.name];
759
+ if (!value || typeof value === "string" && !value.trim()) {
760
+ var _field$requiredMessag;
761
+ errors[field.name] = (_field$requiredMessag = field.requiredMessage) !== null && _field$requiredMessag !== void 0 ? _field$requiredMessag : `${field.label}을(를) 입력해주세요.`;
762
+ }
763
+ }
764
+ if (field.validate) {
765
+ const value = values[field.name];
766
+ const error = field.validate(value);
767
+ if (error) errors[field.name] = error;
768
+ }
769
+ }
770
+ return errors;
771
+ },
772
+ onSubmit: (values) => {
773
+ setSubmitted(true);
774
+ onSubmit === null || onSubmit === void 0 || onSubmit(values);
775
+ }
776
+ });
777
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
778
+ className: "gap-group flex flex-col",
779
+ "data-opt-id": "9TEEJ",
780
+ "data-opt-slug": "settings-form.SettingsForm",
781
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_pagination.FormRoot, {
782
+ store: form,
783
+ "aria-label": labels.formAriaLabel,
784
+ children: [fields.map((field) => {
785
+ var _field$type;
786
+ if (field.visible !== void 0) {
787
+ const currentValues = form.getValues();
788
+ if (!(typeof field.visible === "function" ? field.visible(currentValues) : field.visible)) return null;
789
+ }
790
+ if (field.type === "tags") {
791
+ const rawValue = form.getValue(field.name);
792
+ const tags = Array.isArray(rawValue) ? rawValue : [];
793
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
794
+ className: "gap-element flex flex-col",
795
+ children: [
796
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.FormLabel, {
797
+ name: field.name,
798
+ children: field.label
799
+ }),
800
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.TagInput, {
801
+ value: tags,
802
+ onChange: (next) => form.setValue(field.name, next),
803
+ placeholder: field.placeholder
804
+ }),
805
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.FormError, { name: field.name })
806
+ ]
807
+ }, field.name);
808
+ }
809
+ if (field.type === "radio") {
810
+ var _field$options;
811
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
812
+ className: "gap-element flex flex-col",
813
+ children: [
814
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("legend", {
815
+ className: "text-text-primary text-sm font-medium",
816
+ children: field.label
817
+ }),
818
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.FormRadioGroup, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
819
+ className: "gap-group flex",
820
+ children: (_field$options = field.options) === null || _field$options === void 0 ? void 0 : _field$options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
821
+ className: "text-text-primary gap-element flex items-center text-sm",
822
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.FormRadio, {
823
+ name: field.name,
824
+ value: option.value
825
+ }), option.label]
826
+ }, option.value))
827
+ }) }),
828
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.FormError, { name: field.name })
829
+ ]
830
+ }, field.name);
831
+ }
832
+ if (field.type === "checkbox") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
833
+ className: "gap-element flex flex-col",
834
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
835
+ className: "text-text-primary gap-element flex items-center text-sm",
836
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.FormCheckbox, { name: field.name }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: field.label })]
837
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.FormError, { name: field.name })]
838
+ }, field.name);
839
+ if (field.type === "textarea") {
840
+ var _field$rows, _field$resize;
841
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
842
+ className: "gap-element flex flex-col",
843
+ children: [
844
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.FormLabel, {
845
+ name: field.name,
846
+ children: field.label
847
+ }),
848
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.FormTextarea, {
849
+ name: field.name,
850
+ placeholder: field.placeholder,
851
+ rows: (_field$rows = field.rows) !== null && _field$rows !== void 0 ? _field$rows : 4,
852
+ resize: (_field$resize = field.resize) !== null && _field$resize !== void 0 ? _field$resize : "vertical",
853
+ className: field.font === "mono" ? "font-mono text-sm" : void 0
854
+ }),
855
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.FormError, { name: field.name })
856
+ ]
857
+ }, field.name);
858
+ }
859
+ if (field.type === "select") {
860
+ var _field$options2;
861
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
862
+ className: "gap-element flex flex-col",
863
+ children: [
864
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.FormLabel, {
865
+ name: field.name,
866
+ children: field.label
867
+ }),
868
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.FormSelect, {
869
+ name: field.name,
870
+ placeholder: field.placeholder,
871
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.FormSelectPopover, { children: (_field$options2 = field.options) === null || _field$options2 === void 0 ? void 0 : _field$options2.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.FormSelectItem, {
872
+ value: option.value,
873
+ children: option.label
874
+ }, option.value)) })
875
+ }),
876
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.FormError, { name: field.name })
877
+ ]
878
+ }, field.name);
879
+ }
880
+ if (field.type === "switch") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
881
+ className: "gap-group flex items-center",
882
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.FormSwitch, { name: field.name }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
883
+ className: "flex flex-col",
884
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
885
+ className: "text-text-primary text-sm font-medium",
886
+ children: field.label
887
+ }), field.description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
888
+ className: "text-text-secondary text-sm",
889
+ children: field.description
890
+ })]
891
+ })]
892
+ }, field.name);
893
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
894
+ className: "gap-element flex flex-col",
895
+ children: [
896
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.FormLabel, {
897
+ name: field.name,
898
+ children: field.label
899
+ }),
900
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.FormInput, {
901
+ name: field.name,
902
+ type: (_field$type = field.type) !== null && _field$type !== void 0 ? _field$type : "text",
903
+ placeholder: field.placeholder
904
+ }),
905
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.FormError, { name: field.name })
906
+ ]
907
+ }, field.name);
908
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.FormSubmit, { children: labels.submitButton })]
909
+ }), submitted && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
910
+ className: "border-success bg-success-subtle text-success-fg rounded-lg border p-3 text-sm",
911
+ children: labels.successMessage
912
+ })]
913
+ });
914
+ }
915
+ //#endregion
916
+ //#region src/shells/deployment-timeline.tsx
917
+ const statusIcon = {
918
+ success: "●",
919
+ pending: "◌",
920
+ running: "⟳",
921
+ error: "✗",
922
+ skipped: "○"
923
+ };
924
+ const statusColor = {
925
+ success: "text-success-fg",
926
+ pending: require_pagination.OPT_TEXT_TERTIARY,
927
+ running: "text-accent",
928
+ error: "text-danger-fg",
929
+ skipped: "text-text-tertiary"
930
+ };
931
+ const statusBadge$1 = {
932
+ success: require_pagination.OPT_BADGE_GREEN,
933
+ pending: require_pagination.OPT_BADGE_NEUTRAL,
934
+ running: "inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium bg-info-subtle text-info-fg",
935
+ error: require_pagination.OPT_BADGE_RED,
936
+ skipped: require_pagination.OPT_BADGE_NEUTRAL
937
+ };
938
+ const statusLabel$1 = {
939
+ success: "성공",
940
+ pending: "대기",
941
+ running: "실행 중",
942
+ error: "에러",
943
+ skipped: "건너뜀"
944
+ };
945
+ /** Renders the deployment timeline component. */
946
+ function DeploymentTimeline({ steps, title = "배포 타임라인" }) {
947
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
948
+ className: "gap-group flex flex-col",
949
+ "data-opt-id": "XXVQ2",
950
+ "data-opt-slug": "deployment-timeline.DeploymentTimeline",
951
+ children: [title && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
952
+ className: `text-sm font-semibold text-text-primary`,
953
+ children: title
954
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CompositeProvider, {
955
+ focusLoop: true,
956
+ orientation: "vertical",
957
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.Composite, {
958
+ role: "list",
959
+ "aria-label": title,
960
+ className: "relative",
961
+ children: steps.map((step, i) => {
962
+ const isLast = i === steps.length - 1;
963
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_reopt_ai_opt_ui_primitives.CompositeItem, {
964
+ className: `group gap-group relative flex w-full scroll-m-2 rounded-lg px-3 py-2 ${require_pagination.OPT_FOCUS} data-[active-item]:bg-accent-subtle`,
965
+ role: "listitem",
966
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
967
+ className: "flex flex-col items-center",
968
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
969
+ className: `text-lg leading-none ${statusColor[step.status]}`,
970
+ "aria-label": statusLabel$1[step.status],
971
+ children: statusIcon[step.status]
972
+ }), !isLast && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "bg-border-subtle mt-1 w-px flex-1" })]
973
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
974
+ className: "flex-1 pb-3",
975
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
976
+ className: "gap-element flex items-center",
977
+ children: [
978
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
979
+ className: `text-sm font-medium ${require_pagination.OPT_TEXT_PRIMARY}`,
980
+ children: step.name
981
+ }),
982
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
983
+ className: statusBadge$1[step.status],
984
+ children: statusLabel$1[step.status]
985
+ }),
986
+ step.duration && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
987
+ className: `text-xs text-text-tertiary`,
988
+ children: step.duration
989
+ })
990
+ ]
991
+ }), step.detail && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
992
+ className: `mt-0.5 text-xs text-text-secondary`,
993
+ children: step.detail
994
+ })]
995
+ })]
996
+ }, step.id);
997
+ })
998
+ })
999
+ })]
1000
+ });
1001
+ }
1002
+ //#endregion
1003
+ //#region src/shells/project-switcher.tsx
1004
+ const statusDot = {
1005
+ active: require_pagination.OPT_DOT_GREEN,
1006
+ paused: require_pagination.OPT_DOT_YELLOW,
1007
+ error: require_pagination.OPT_DOT_RED
1008
+ };
1009
+ /** Renders the project switcher component. */
1010
+ function ProjectSwitcher({ projects, value, onChange, placeholder = "프로젝트 검색..." }) {
1011
+ const [search, setSearch] = (0, react.useState)("");
1012
+ const matches = (0, react.useMemo)(() => {
1013
+ if (!search) return projects;
1014
+ return (0, match_sorter.matchSorter)(projects, search, { keys: ["name", "framework"] });
1015
+ }, [search, projects]);
1016
+ const selected = projects.find((p) => p.id === value);
1017
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1018
+ className: "gap-element flex flex-col",
1019
+ "data-opt-id": "5R3AG",
1020
+ "data-opt-slug": "project-switcher.ProjectSwitcher",
1021
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_pagination.ComboboxRoot, {
1022
+ onValueChange: setSearch,
1023
+ children: [
1024
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
1025
+ className: require_pagination.OPT_TEXT_LABEL,
1026
+ children: "프로젝트"
1027
+ }),
1028
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.ComboboxInput, {
1029
+ placeholder: selected ? selected.name : placeholder,
1030
+ className: require_pagination.OPT_INPUT
1031
+ }),
1032
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_pagination.ComboboxPopover, {
1033
+ className: "max-h-72 overflow-y-auto",
1034
+ children: [matches.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1035
+ className: `py-6 text-center text-sm text-text-secondary`,
1036
+ children: "결과가 없습니다."
1037
+ }), matches.map((project) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_pagination.ComboboxItem, {
1038
+ value: project.name,
1039
+ onClick: () => onChange === null || onChange === void 0 ? void 0 : onChange(project.id),
1040
+ className: `gap-element flex scroll-m-2 items-center ${require_pagination.OPT_ITEM_BASE} ${require_pagination.OPT_ACTIVE_ITEM}`,
1041
+ children: [
1042
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: `shrink-0 ${statusDot[project.status]}` }),
1043
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1044
+ className: "flex-1 truncate font-medium",
1045
+ children: project.name
1046
+ }),
1047
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1048
+ className: require_pagination.OPT_BADGE_NEUTRAL,
1049
+ children: project.framework
1050
+ }),
1051
+ project.domain && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1052
+ className: `truncate text-xs text-text-secondary data-[active-item]:text-accent-fg/70`,
1053
+ children: project.domain
1054
+ })
1055
+ ]
1056
+ }, project.id))]
1057
+ })
1058
+ ]
1059
+ })
1060
+ });
1061
+ }
1062
+ ProjectSwitcher.displayName = "ProjectSwitcher";
1063
+ //#endregion
1064
+ //#region src/shells/env-panel.tsx
1065
+ const defaultLabels$4 = {
1066
+ varCount: (count) => `${count}개 변수`,
1067
+ envVarsLabel: (env) => `${env} 환경 변수`,
1068
+ secretBadge: "secret"
1069
+ };
1070
+ const envDot = {
1071
+ Production: require_pagination.OPT_DOT_RED,
1072
+ Preview: require_pagination.OPT_DOT_YELLOW,
1073
+ Development: require_pagination.OPT_DOT_GREEN
1074
+ };
1075
+ /** Renders the env panel component. */
1076
+ function EnvPanel({ groups, labels: customLabels }) {
1077
+ const labels = require_pagination._objectSpread2(require_pagination._objectSpread2({}, defaultLabels$4), customLabels);
1078
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1079
+ className: `rounded-lg ${require_pagination.OPT_BORDER} divide-y ${require_pagination.OPT_DIVIDER} bg-surface`,
1080
+ "data-opt-id": "ZBDWK",
1081
+ "data-opt-slug": "env-panel.EnvPanel",
1082
+ children: groups.map((group) => {
1083
+ var _envDot$group$env;
1084
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.DisclosureRoot, {
1085
+ defaultOpen: group.env === "Production",
1086
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_pagination.DisclosureTrigger, {
1087
+ className: require_pagination.OPT_DISCLOSURE_TRIGGER,
1088
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1089
+ className: "gap-element flex items-center",
1090
+ children: [
1091
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: (_envDot$group$env = envDot[group.env]) !== null && _envDot$group$env !== void 0 ? _envDot$group$env : "bg-bg-muted h-2 w-2 rounded-full" }),
1092
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1093
+ className: "font-medium",
1094
+ children: group.env
1095
+ }),
1096
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1097
+ className: `text-xs ${require_pagination.OPT_TEXT_TERTIARY}`,
1098
+ children: labels.varCount(group.vars.length)
1099
+ })
1100
+ ]
1101
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1102
+ className: "text-text-tertiary h-4 w-4 shrink-0 transition-transform [[data-open]>&]:rotate-180",
1103
+ fill: "none",
1104
+ viewBox: "0 0 24 24",
1105
+ stroke: "currentColor",
1106
+ strokeWidth: 2,
1107
+ "aria-hidden": "true",
1108
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1109
+ strokeLinecap: "round",
1110
+ strokeLinejoin: "round",
1111
+ d: "m19.5 8.25-7.5 7.5-7.5-7.5"
1112
+ })
1113
+ })]
1114
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.DisclosureContent, {
1115
+ className: require_pagination.OPT_DISCLOSURE_CONTENT,
1116
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CompositeProvider, {
1117
+ focusLoop: true,
1118
+ orientation: "vertical",
1119
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.Composite, {
1120
+ role: "list",
1121
+ "aria-label": labels.envVarsLabel(group.env),
1122
+ className: "px-4 pb-3",
1123
+ children: group.vars.map((v) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_reopt_ai_opt_ui_primitives.CompositeItem, {
1124
+ role: "listitem",
1125
+ className: `gap-group flex scroll-m-2 items-center rounded px-2 py-1.5 text-sm ${require_pagination.OPT_FOCUS} data-[active-item]:bg-accent-subtle`,
1126
+ children: [
1127
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1128
+ className: `w-44 shrink-0 truncate font-mono text-xs font-medium ${require_pagination.OPT_TEXT_PRIMARY}`,
1129
+ children: v.key
1130
+ }),
1131
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1132
+ className: `flex-1 truncate font-mono text-xs ${require_pagination.OPT_TEXT_SECONDARY}`,
1133
+ children: v.isSecret ? "••••••••" : v.value
1134
+ }),
1135
+ v.isSecret && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1136
+ className: require_pagination.OPT_BADGE_NEUTRAL,
1137
+ children: labels.secretBadge
1138
+ })
1139
+ ]
1140
+ }, v.key))
1141
+ })
1142
+ })
1143
+ })] })
1144
+ }, group.env);
1145
+ })
1146
+ });
1147
+ }
1148
+ //#endregion
1149
+ //#region src/shells/branch-select.tsx
1150
+ const defaultLabels$3 = {
1151
+ selectLabel: "브랜치",
1152
+ emptySelectionLabel: "선택...",
1153
+ emptyMessage: "사용 가능한 브랜치가 없습니다.",
1154
+ defaultBadge: "default"
1155
+ };
1156
+ /** Renders the branch select component. */
1157
+ function BranchSelect({ branches, value: controlledValue, onChange, label, labels: customLabels }) {
1158
+ var _ref, _defaultBranch$name, _branches$, _selected$name;
1159
+ const labels = require_pagination._objectSpread2(require_pagination._objectSpread2({}, defaultLabels$3), customLabels);
1160
+ const resolvedLabel = label !== null && label !== void 0 ? label : labels.selectLabel;
1161
+ const defaultBranch = branches.find((b) => b.isDefault);
1162
+ const [internalValue, setInternalValue] = (0, react.useState)((_ref = (_defaultBranch$name = defaultBranch === null || defaultBranch === void 0 ? void 0 : defaultBranch.name) !== null && _defaultBranch$name !== void 0 ? _defaultBranch$name : (_branches$ = branches[0]) === null || _branches$ === void 0 ? void 0 : _branches$.name) !== null && _ref !== void 0 ? _ref : "");
1163
+ const value = controlledValue !== null && controlledValue !== void 0 ? controlledValue : internalValue;
1164
+ const selected = branches.find((b) => b.name === value);
1165
+ if (branches.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1166
+ className: "gap-element flex flex-col",
1167
+ "data-opt-id": "7EDEP",
1168
+ "data-opt-slug": "branch-select.BranchSelect",
1169
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1170
+ className: require_pagination.OPT_TEXT_LABEL,
1171
+ children: resolvedLabel
1172
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1173
+ className: require_pagination.OPT_TEXT_TERTIARY,
1174
+ children: labels.emptyMessage
1175
+ })]
1176
+ });
1177
+ const handleChange = (v) => {
1178
+ var _v$;
1179
+ const next = Array.isArray(v) ? (_v$ = v[0]) !== null && _v$ !== void 0 ? _v$ : "" : v;
1180
+ if (onChange) onChange(next);
1181
+ else setInternalValue(next);
1182
+ };
1183
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1184
+ className: "gap-element flex flex-col",
1185
+ "data-opt-id": "7EDEP",
1186
+ "data-opt-slug": "branch-select.BranchSelect",
1187
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_pagination.SelectRoot, {
1188
+ value,
1189
+ onValueChange: handleChange,
1190
+ children: [
1191
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.SelectLabel, {
1192
+ className: require_pagination.OPT_TEXT_LABEL,
1193
+ children: resolvedLabel
1194
+ }),
1195
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_pagination.SelectTrigger, {
1196
+ className: `${require_pagination.OPT_INPUT} max-w-xs items-center justify-between gap-1`,
1197
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1198
+ className: "gap-element flex items-center truncate",
1199
+ children: [
1200
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1201
+ className: "text-text-tertiary h-4 w-4 shrink-0",
1202
+ fill: "none",
1203
+ viewBox: "0 0 24 24",
1204
+ stroke: "currentColor",
1205
+ strokeWidth: 2,
1206
+ "aria-hidden": "true",
1207
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1208
+ strokeLinecap: "round",
1209
+ strokeLinejoin: "round",
1210
+ 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"
1211
+ })
1212
+ }),
1213
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1214
+ className: "truncate",
1215
+ children: (_selected$name = selected === null || selected === void 0 ? void 0 : selected.name) !== null && _selected$name !== void 0 ? _selected$name : labels.emptySelectionLabel
1216
+ }),
1217
+ (selected === null || selected === void 0 ? void 0 : selected.isDefault) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1218
+ className: require_pagination.OPT_BADGE_BLUE,
1219
+ children: labels.defaultBadge
1220
+ })
1221
+ ]
1222
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1223
+ className: "text-text-tertiary h-4 w-4 shrink-0",
1224
+ fill: "none",
1225
+ viewBox: "0 0 24 24",
1226
+ stroke: "currentColor",
1227
+ strokeWidth: 2,
1228
+ "aria-hidden": "true",
1229
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1230
+ strokeLinecap: "round",
1231
+ strokeLinejoin: "round",
1232
+ d: "m19.5 8.25-7.5 7.5-7.5-7.5"
1233
+ })
1234
+ })]
1235
+ }),
1236
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.SelectPopover, {
1237
+ className: "max-w-xs",
1238
+ children: branches.map((branch) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_pagination.SelectItem, {
1239
+ value: branch.name,
1240
+ className: `gap-element flex scroll-m-2 items-center ${require_pagination.OPT_ITEM_BASE} ${require_pagination.OPT_ACTIVE_ITEM}`,
1241
+ children: [
1242
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1243
+ className: "flex-1 truncate",
1244
+ children: branch.name
1245
+ }),
1246
+ branch.isDefault && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1247
+ 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",
1248
+ children: labels.defaultBadge
1249
+ }),
1250
+ branch.lastCommit && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1251
+ className: `font-mono text-xs text-text-tertiary data-[active-item]:text-accent-fg/60`,
1252
+ children: branch.lastCommit
1253
+ }),
1254
+ branch.author && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1255
+ className: `text-xs text-text-tertiary data-[active-item]:text-accent-fg/60`,
1256
+ children: branch.author
1257
+ })
1258
+ ]
1259
+ }, branch.name))
1260
+ })
1261
+ ]
1262
+ })
1263
+ });
1264
+ }
1265
+ BranchSelect.displayName = "BranchSelect";
1266
+ //#endregion
1267
+ //#region src/shells/domain-table.tsx
1268
+ const defaultLabels$2 = {
1269
+ domain: "Domain",
1270
+ status: "Status",
1271
+ ssl: "SSL",
1272
+ redirect: "Redirect",
1273
+ added: "Added",
1274
+ emptyMessage: "No domains configured"
1275
+ };
1276
+ const statusBadge = {
1277
+ active: require_pagination.OPT_BADGE_GREEN,
1278
+ pending: require_pagination.OPT_BADGE_YELLOW,
1279
+ error: require_pagination.OPT_BADGE_RED
1280
+ };
1281
+ const sslBadge = {
1282
+ valid: require_pagination.OPT_BADGE_GREEN,
1283
+ pending: require_pagination.OPT_BADGE_YELLOW,
1284
+ expired: require_pagination.OPT_BADGE_RED
1285
+ };
1286
+ const statusLabel = {
1287
+ active: "Active",
1288
+ pending: "Pending",
1289
+ error: "Error"
1290
+ };
1291
+ const sslLabel = {
1292
+ valid: "Valid",
1293
+ pending: "Pending",
1294
+ expired: "Expired"
1295
+ };
1296
+ /** Renders the domain table component. */
1297
+ function DomainTable({ domains, title = "도메인", labels: customLabels }) {
1298
+ const labels = require_pagination._objectSpread2(require_pagination._objectSpread2({}, defaultLabels$2), customLabels);
1299
+ if (domains.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1300
+ className: "gap-group flex flex-col",
1301
+ "data-opt-id": "A7SRM",
1302
+ "data-opt-slug": "domain-table.DomainTable",
1303
+ children: [title && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
1304
+ className: `text-sm font-semibold text-text-primary`,
1305
+ children: title
1306
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1307
+ className: `rounded-lg px-4 py-6 text-sm ${require_pagination.OPT_BORDER} ${require_pagination.OPT_TEXT_SECONDARY} bg-surface`,
1308
+ children: labels.emptyMessage
1309
+ })]
1310
+ });
1311
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1312
+ className: "gap-group flex flex-col",
1313
+ "data-opt-id": "A7SRM",
1314
+ "data-opt-slug": "domain-table.DomainTable",
1315
+ children: [title && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
1316
+ className: `text-sm font-semibold text-text-primary`,
1317
+ children: title
1318
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1319
+ className: `overflow-x-auto rounded-lg ${require_pagination.OPT_BORDER} bg-surface`,
1320
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CompositeProvider, {
1321
+ focusLoop: true,
1322
+ focusWrap: true,
1323
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_reopt_ai_opt_ui_primitives.Composite, {
1324
+ role: "grid",
1325
+ "aria-label": title,
1326
+ className: "w-full",
1327
+ render: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("table", {}),
1328
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", {
1329
+ className: "border-border-subtle bg-surface-inset border-b",
1330
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
1331
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {
1332
+ className: require_pagination.OPT_TH,
1333
+ children: labels.domain
1334
+ }),
1335
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {
1336
+ className: require_pagination.OPT_TH,
1337
+ children: labels.status
1338
+ }),
1339
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {
1340
+ className: require_pagination.OPT_TH,
1341
+ children: labels.ssl
1342
+ }),
1343
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {
1344
+ className: require_pagination.OPT_TH,
1345
+ children: labels.redirect
1346
+ }),
1347
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {
1348
+ className: require_pagination.OPT_TH,
1349
+ children: labels.added
1350
+ })
1351
+ ] })
1352
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", {
1353
+ className: `divide-y ${require_pagination.OPT_DIVIDER}`,
1354
+ children: domains.map((d) => {
1355
+ var _d$redirect, _d$addedAt;
1356
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_reopt_ai_opt_ui_primitives.CompositeRow, {
1357
+ render: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", {}),
1358
+ children: [
1359
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CompositeItem, {
1360
+ render: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
1361
+ className: `${require_pagination.OPT_TD} font-mono font-medium`,
1362
+ role: "gridcell"
1363
+ }),
1364
+ className: require_pagination.OPT_TABLE_ROW_ACTIVE,
1365
+ children: d.domain
1366
+ }),
1367
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CompositeItem, {
1368
+ render: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
1369
+ className: require_pagination.OPT_TD,
1370
+ role: "gridcell"
1371
+ }),
1372
+ className: require_pagination.OPT_TABLE_ROW_ACTIVE,
1373
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1374
+ className: statusBadge[d.status],
1375
+ children: statusLabel[d.status]
1376
+ })
1377
+ }),
1378
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CompositeItem, {
1379
+ render: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
1380
+ className: require_pagination.OPT_TD,
1381
+ role: "gridcell"
1382
+ }),
1383
+ className: require_pagination.OPT_TABLE_ROW_ACTIVE,
1384
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1385
+ className: sslBadge[d.ssl],
1386
+ children: sslLabel[d.ssl]
1387
+ })
1388
+ }),
1389
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CompositeItem, {
1390
+ render: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
1391
+ className: require_pagination.OPT_TD,
1392
+ role: "gridcell"
1393
+ }),
1394
+ className: require_pagination.OPT_TABLE_ROW_ACTIVE,
1395
+ children: (_d$redirect = d.redirect) !== null && _d$redirect !== void 0 ? _d$redirect : "—"
1396
+ }),
1397
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CompositeItem, {
1398
+ render: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
1399
+ className: require_pagination.OPT_TD_MUTED,
1400
+ role: "gridcell"
1401
+ }),
1402
+ className: require_pagination.OPT_TABLE_ROW_ACTIVE,
1403
+ children: (_d$addedAt = d.addedAt) !== null && _d$addedAt !== void 0 ? _d$addedAt : "—"
1404
+ })
1405
+ ]
1406
+ }, d.domain);
1407
+ })
1408
+ })]
1409
+ })
1410
+ })
1411
+ })]
1412
+ });
1413
+ }
1414
+ //#endregion
1415
+ //#region src/shells/data-table.tsx
1416
+ const MIN_COLUMN_WIDTH = 40;
1417
+ const DEFAULT_COLUMN_RESIZE_MAX = 1200;
1418
+ const alignmentClass = {
1419
+ left: "text-left",
1420
+ center: "text-center",
1421
+ right: "text-right"
1422
+ };
1423
+ function getCellValue(row, accessor) {
1424
+ if (typeof accessor === "function") try {
1425
+ return accessor(row);
1426
+ } catch (_unused) {
1427
+ return "—";
1428
+ }
1429
+ const value = row[accessor];
1430
+ if (value === null || value === void 0 || typeof value === "number" && !Number.isFinite(value)) return "—";
1431
+ return String(value);
1432
+ }
1433
+ function getRawValue(row, accessor) {
1434
+ if (typeof accessor === "function") return accessor(row);
1435
+ return row[accessor];
1436
+ }
1437
+ function isMissingSortValue(value) {
1438
+ return value == null || typeof value === "number" && !Number.isFinite(value);
1439
+ }
1440
+ function compareSortValues(aVal, bVal, dir) {
1441
+ const aMissing = isMissingSortValue(aVal);
1442
+ const bMissing = isMissingSortValue(bVal);
1443
+ if (aMissing && bMissing) return 0;
1444
+ if (aMissing) return 1;
1445
+ if (bMissing) return -1;
1446
+ const cmp = typeof aVal === "number" && typeof bVal === "number" ? aVal - bVal : String(aVal).localeCompare(String(bVal));
1447
+ return dir === "desc" ? -cmp : cmp;
1448
+ }
1449
+ function clampPercent(ratio) {
1450
+ if (!Number.isFinite(ratio)) return 0;
1451
+ return Math.min(100, Math.max(0, Math.round(ratio * 100)));
1452
+ }
1453
+ function getResizeHandleValue(currentWidth, configuredWidth) {
1454
+ if (typeof currentWidth === "number" && Number.isFinite(currentWidth)) return Math.max(MIN_COLUMN_WIDTH, Math.round(currentWidth));
1455
+ const pxWidth = configuredWidth === null || configuredWidth === void 0 ? void 0 : configuredWidth.trim().match(/^(\d+(?:\.\d+)?)px$/);
1456
+ if (pxWidth) return Math.max(MIN_COLUMN_WIDTH, Math.round(Number(pxWidth[1])));
1457
+ return MIN_COLUMN_WIDTH;
1458
+ }
1459
+ function DataTableProgressBar({ value, barColor, label }) {
1460
+ const percent = clampPercent(value);
1461
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1462
+ className: "bg-bg-subtle mt-1 h-1.5 w-full overflow-hidden rounded-full",
1463
+ role: "progressbar",
1464
+ "aria-valuenow": percent,
1465
+ "aria-valuemin": 0,
1466
+ "aria-valuemax": 100,
1467
+ "aria-valuetext": `${percent}%`,
1468
+ "aria-label": label,
1469
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1470
+ className: require_pagination.cn("h-full rounded-full transition-all", barColor),
1471
+ style: { width: `${percent}%` }
1472
+ })
1473
+ });
1474
+ }
1475
+ /** Renders the data table component. */
1476
+ function DataTable({ data, columns, keyExtractor, title = "Data Table", onRowClick, emptyMessage = "No data available", activeRowId, sortable = false, selectable = false, selectedKeys, onSelectionChange, expandable = false, renderExpanded, pageSize, stickyHeader = false, resizableColumns = false, onSort: onSortExternal, className }) {
1477
+ const [sortCol, setSortCol] = (0, react.useState)(null);
1478
+ const [sortDir, setSortDir] = (0, react.useState)(null);
1479
+ const [expandedKeys, setExpandedKeys] = (0, react.useState)(/* @__PURE__ */ new Set());
1480
+ const [page, setPage] = (0, react.useState)(0);
1481
+ const [colWidths, setColWidths] = (0, react.useState)({});
1482
+ const sortedData = (0, react.useMemo)(() => {
1483
+ if (!sortable || !sortCol || !sortDir) return data;
1484
+ const col = columns.find((c) => c.id === sortCol);
1485
+ if (!col) return data;
1486
+ return [...data].sort((a, b) => {
1487
+ return compareSortValues(getRawValue(a, col.accessor), getRawValue(b, col.accessor), sortDir);
1488
+ });
1489
+ }, [
1490
+ data,
1491
+ sortable,
1492
+ sortCol,
1493
+ sortDir,
1494
+ columns
1495
+ ]);
1496
+ const totalPages = pageSize ? Math.ceil(sortedData.length / pageSize) : 1;
1497
+ const pagedData = pageSize ? sortedData.slice(page * pageSize, (page + 1) * pageSize) : sortedData;
1498
+ const handleSort = (0, react.useCallback)((colId) => {
1499
+ if (!sortable) return;
1500
+ let nextDir;
1501
+ if (sortCol === colId) {
1502
+ nextDir = sortDir === "asc" ? "desc" : sortDir === "desc" ? null : "asc";
1503
+ setSortDir(nextDir);
1504
+ if (nextDir === null) setSortCol(null);
1505
+ } else {
1506
+ setSortCol(colId);
1507
+ setSortDir("asc");
1508
+ nextDir = "asc";
1509
+ }
1510
+ setPage(0);
1511
+ if (onSortExternal) onSortExternal(colId, nextDir);
1512
+ }, [
1513
+ sortable,
1514
+ sortCol,
1515
+ sortDir,
1516
+ onSortExternal
1517
+ ]);
1518
+ const toggleExpand = (0, react.useCallback)((key) => {
1519
+ setExpandedKeys((prev) => {
1520
+ const next = new Set(prev);
1521
+ if (next.has(key)) next.delete(key);
1522
+ else next.add(key);
1523
+ return next;
1524
+ });
1525
+ }, []);
1526
+ const selected = selectedKeys !== null && selectedKeys !== void 0 ? selectedKeys : [];
1527
+ const selectedSet = (0, react.useMemo)(() => new Set(selectedKeys !== null && selectedKeys !== void 0 ? selectedKeys : []), [selectedKeys]);
1528
+ const allKeys = data.map(keyExtractor);
1529
+ const allSelected = allKeys.length > 0 && allKeys.every((k) => selectedSet.has(k));
1530
+ const toggleSelectAll = (0, react.useCallback)(() => {
1531
+ if (!onSelectionChange) return;
1532
+ onSelectionChange(allSelected ? [] : allKeys);
1533
+ }, [
1534
+ allSelected,
1535
+ allKeys,
1536
+ onSelectionChange
1537
+ ]);
1538
+ const toggleSelectRow = (0, react.useCallback)((key) => {
1539
+ if (!onSelectionChange) return;
1540
+ onSelectionChange(selectedSet.has(key) ? selected.filter((k) => k !== key) : [...selected, key]);
1541
+ }, [
1542
+ selected,
1543
+ selectedSet,
1544
+ onSelectionChange
1545
+ ]);
1546
+ const sortIndicator = (colId) => {
1547
+ if (sortCol !== colId) return null;
1548
+ return sortDir === "asc" ? " ▲" : sortDir === "desc" ? " ▼" : null;
1549
+ };
1550
+ const colCount = columns.length + (selectable ? 1 : 0) + (expandable ? 1 : 0);
1551
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1552
+ className: require_pagination.cn("gap-group flex flex-col", className),
1553
+ "data-opt-id": "J1KN4",
1554
+ "data-opt-slug": "data-table.DataTable",
1555
+ children: [
1556
+ title && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
1557
+ className: require_pagination.cn("text-sm font-semibold", "text-text-primary"),
1558
+ children: title
1559
+ }),
1560
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1561
+ className: require_pagination.cn("overflow-x-auto rounded-lg", require_pagination.OPT_BORDER, "bg-surface"),
1562
+ children: data.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1563
+ className: require_pagination.cn("px-4 py-8 text-center text-sm", require_pagination.OPT_TEXT_SECONDARY),
1564
+ children: emptyMessage
1565
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CompositeProvider, {
1566
+ focusLoop: true,
1567
+ focusWrap: true,
1568
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_reopt_ai_opt_ui_primitives.Composite, {
1569
+ role: "grid",
1570
+ "aria-label": title,
1571
+ className: "w-full",
1572
+ render: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("table", {}),
1573
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", {
1574
+ className: require_pagination.cn("border-border-subtle bg-surface-inset border-b", stickyHeader && "sticky top-0 z-10"),
1575
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
1576
+ selectable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {
1577
+ className: require_pagination.cn("px-3 py-2.5 text-left text-xs font-semibold uppercase tracking-wider text-text-tertiary", "w-10"),
1578
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.Checkbox, {
1579
+ checked: allSelected,
1580
+ onChange: toggleSelectAll,
1581
+ "aria-label": "Select all"
1582
+ })
1583
+ }),
1584
+ expandable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { className: require_pagination.cn("px-3 py-2.5 text-left text-xs font-semibold uppercase tracking-wider text-text-tertiary", "w-10") }),
1585
+ columns.map((col) => {
1586
+ var _col$align;
1587
+ const isSortable = sortable && col.sortable !== false;
1588
+ const resizeValue = getResizeHandleValue(colWidths[col.id], col.width);
1589
+ const resizeMax = Math.max(DEFAULT_COLUMN_RESIZE_MAX, resizeValue);
1590
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("th", {
1591
+ className: require_pagination.cn(require_pagination.OPT_TH, "relative", alignmentClass[(_col$align = col.align) !== null && _col$align !== void 0 ? _col$align : "left"], isSortable && "hover:text-text-primary cursor-pointer select-none", isSortable && "outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-ring-offset"),
1592
+ style: { width: colWidths[col.id] || col.width || void 0 },
1593
+ onClick: isSortable ? () => handleSort(col.id) : void 0,
1594
+ onKeyDown: (event) => {
1595
+ if (!isSortable || event.key !== "Enter" && event.key !== " ") return;
1596
+ event.preventDefault();
1597
+ handleSort(col.id);
1598
+ },
1599
+ tabIndex: isSortable ? 0 : void 0,
1600
+ "aria-sort": sortCol === col.id && sortDir ? sortDir === "asc" ? "ascending" : "descending" : void 0,
1601
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1602
+ className: "flex items-center",
1603
+ children: [col.header, sortIndicator(col.id)]
1604
+ }), resizableColumns && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1605
+ role: "separator",
1606
+ "aria-orientation": "vertical",
1607
+ "aria-label": `Resize ${col.header}`,
1608
+ "aria-valuemin": MIN_COLUMN_WIDTH,
1609
+ "aria-valuemax": resizeMax,
1610
+ "aria-valuenow": resizeValue,
1611
+ "aria-valuetext": `${resizeValue} pixels`,
1612
+ tabIndex: 0,
1613
+ className: "bg-border absolute top-0 right-0 bottom-0 w-1 cursor-col-resize opacity-0 hover:opacity-100",
1614
+ onKeyDown: (e) => {
1615
+ if (e.key !== "ArrowLeft" && e.key !== "ArrowRight" && e.key !== "Home" && e.key !== "End") return;
1616
+ e.preventDefault();
1617
+ e.stopPropagation();
1618
+ if (e.key === "Home" || e.key === "End") {
1619
+ setColWidths((prev) => require_pagination._objectSpread2(require_pagination._objectSpread2({}, prev), {}, { [col.id]: e.key === "Home" ? MIN_COLUMN_WIDTH : resizeMax }));
1620
+ return;
1621
+ }
1622
+ const delta = e.shiftKey ? 40 : 10;
1623
+ const direction = e.key === "ArrowRight" ? 1 : -1;
1624
+ setColWidths((prev) => {
1625
+ const current = getResizeHandleValue(prev[col.id], col.width);
1626
+ return require_pagination._objectSpread2(require_pagination._objectSpread2({}, prev), {}, { [col.id]: Math.max(MIN_COLUMN_WIDTH, current + direction * delta) });
1627
+ });
1628
+ },
1629
+ onMouseDown: (e) => {
1630
+ e.preventDefault();
1631
+ e.stopPropagation();
1632
+ const startX = e.clientX;
1633
+ const startW = e.target.parentElement.offsetWidth;
1634
+ const onMove = (ev) => {
1635
+ const newW = Math.max(MIN_COLUMN_WIDTH, startW + ev.clientX - startX);
1636
+ setColWidths((prev) => require_pagination._objectSpread2(require_pagination._objectSpread2({}, prev), {}, { [col.id]: newW }));
1637
+ };
1638
+ const onUp = () => {
1639
+ document.removeEventListener("mousemove", onMove);
1640
+ document.removeEventListener("mouseup", onUp);
1641
+ };
1642
+ document.addEventListener("mousemove", onMove);
1643
+ document.addEventListener("mouseup", onUp);
1644
+ }
1645
+ })]
1646
+ }, col.id);
1647
+ })
1648
+ ] })
1649
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", {
1650
+ className: require_pagination.cn("divide-y", require_pagination.OPT_DIVIDER),
1651
+ children: pagedData.map((row) => {
1652
+ const key = keyExtractor(row);
1653
+ const isExpanded = expandedKeys.has(key);
1654
+ const isSelected = selectedSet.has(key);
1655
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_reopt_ai_opt_ui_primitives.CompositeRow, {
1656
+ render: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", {
1657
+ className: require_pagination.cn(onRowClick ? "cursor-pointer" : void 0, (isSelected || activeRowId === key) && "bg-accent-subtle"),
1658
+ onClick: onRowClick ? () => onRowClick(row) : void 0
1659
+ }),
1660
+ children: [
1661
+ selectable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CompositeItem, {
1662
+ render: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
1663
+ className: require_pagination.cn("px-3 py-2.5 text-sm text-text-primary whitespace-nowrap", "w-10"),
1664
+ role: "gridcell"
1665
+ }),
1666
+ className: "outline-none data-[active-item]:bg-accent-subtle data-[focus-visible]:outline-2 data-[focus-visible]:outline-ring data-[focus-visible]:-outline-offset-1",
1667
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.Checkbox, {
1668
+ checked: isSelected,
1669
+ onChange: () => toggleSelectRow(key),
1670
+ "aria-label": `Select row ${key}`
1671
+ })
1672
+ }),
1673
+ expandable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CompositeItem, {
1674
+ render: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
1675
+ className: require_pagination.cn("px-3 py-2.5 text-sm text-text-primary whitespace-nowrap", "w-10"),
1676
+ role: "gridcell"
1677
+ }),
1678
+ className: "outline-none data-[active-item]:bg-accent-subtle data-[focus-visible]:outline-2 data-[focus-visible]:outline-ring data-[focus-visible]:-outline-offset-1",
1679
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1680
+ type: "button",
1681
+ onClick: (e) => {
1682
+ e.stopPropagation();
1683
+ toggleExpand(key);
1684
+ },
1685
+ className: require_pagination.cn("rounded p-0.5 text-sm transition-transform", "text-text-secondary", "outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-ring-offset", isExpanded && "rotate-90"),
1686
+ "aria-expanded": isExpanded,
1687
+ "aria-label": `${isExpanded ? "Collapse" : "Expand"} row ${key}`,
1688
+ children: "▶"
1689
+ })
1690
+ }),
1691
+ columns.map((col, colIndex) => {
1692
+ var _col$align2, _col$barColor;
1693
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_reopt_ai_opt_ui_primitives.CompositeItem, {
1694
+ render: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
1695
+ className: require_pagination.cn(require_pagination.OPT_TD, alignmentClass[(_col$align2 = col.align) !== null && _col$align2 !== void 0 ? _col$align2 : "left"], colIndex === 0 ? "font-medium" : ""),
1696
+ role: "gridcell"
1697
+ }),
1698
+ className: require_pagination.OPT_TABLE_ROW_ACTIVE,
1699
+ onKeyDown: (event) => {
1700
+ if (!onRowClick || event.target !== event.currentTarget || event.key !== "Enter" && event.key !== " ") return;
1701
+ event.preventDefault();
1702
+ onRowClick(row);
1703
+ },
1704
+ children: [getCellValue(row, col.accessor), col.bar && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataTableProgressBar, {
1705
+ value: col.bar(row),
1706
+ barColor: (_col$barColor = col.barColor) !== null && _col$barColor !== void 0 ? _col$barColor : "bg-accent",
1707
+ label: col.header
1708
+ })]
1709
+ }, col.id);
1710
+ })
1711
+ ]
1712
+ }), expandable && isExpanded && renderExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
1713
+ colSpan: colCount,
1714
+ className: "border-border-subtle bg-surface-inset border-t px-4 py-3",
1715
+ children: renderExpanded(row)
1716
+ }) })] }, key);
1717
+ })
1718
+ })]
1719
+ })
1720
+ })
1721
+ }),
1722
+ pageSize && totalPages > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.Pagination, {
1723
+ totalItems: sortedData.length,
1724
+ pageSize,
1725
+ currentPage: page + 1,
1726
+ onPageChange: (p) => setPage(p - 1)
1727
+ })
1728
+ ]
1729
+ });
1730
+ }
1731
+ //#endregion
1732
+ //#region src/shells/notification-toast.tsx
1733
+ let toastCounter = 0;
1734
+ let toasts = [];
1735
+ const listeners = /* @__PURE__ */ new Set();
1736
+ function emitChange() {
1737
+ for (const l of listeners) l();
1738
+ }
1739
+ const toastStore = {
1740
+ subscribe(listener) {
1741
+ listeners.add(listener);
1742
+ return () => {
1743
+ listeners.delete(listener);
1744
+ };
1745
+ },
1746
+ getSnapshot() {
1747
+ return toasts;
1748
+ },
1749
+ add(input) {
1750
+ var _input$variant;
1751
+ const id = `toast-${++toastCounter}`;
1752
+ toasts = [...toasts, require_pagination._objectSpread2({
1753
+ id,
1754
+ variant: (_input$variant = input.variant) !== null && _input$variant !== void 0 ? _input$variant : "info"
1755
+ }, input)];
1756
+ emitChange();
1757
+ return id;
1758
+ },
1759
+ dismiss(id) {
1760
+ const next = toasts.filter((t) => t.id !== id);
1761
+ if (next.length !== toasts.length) {
1762
+ toasts = next;
1763
+ emitChange();
1764
+ }
1765
+ },
1766
+ clear() {
1767
+ if (toasts.length) {
1768
+ toasts = [];
1769
+ emitChange();
1770
+ }
1771
+ }
1772
+ };
1773
+ function makeVariant(variant) {
1774
+ return (message, options) => toastStore.add(require_pagination._objectSpread2(require_pagination._objectSpread2({}, options), {}, {
1775
+ message,
1776
+ variant
1777
+ }));
1778
+ }
1779
+ /**
1780
+ * Imperative toast API. Call from anywhere (no hook required):
1781
+ * `toast.success("Saved")`, `toast.error("Failed", { title: "Oops" })`,
1782
+ * `toast.dismiss(id)`, `toast.clear()`. Requires a mounted `ToastProvider`.
1783
+ */
1784
+ const toast = Object.assign((options) => toastStore.add(options), {
1785
+ info: makeVariant("info"),
1786
+ success: makeVariant("success"),
1787
+ warning: makeVariant("warning"),
1788
+ error: makeVariant("error"),
1789
+ dismiss: (id) => toastStore.dismiss(id),
1790
+ clear: () => toastStore.clear()
1791
+ });
1792
+ const ToastContext = (0, react.createContext)(null);
1793
+ const positionStyles = {
1794
+ "top-right": "top-4 right-4",
1795
+ "top-left": "top-4 left-4",
1796
+ "bottom-right": "bottom-4 right-4",
1797
+ "bottom-left": "bottom-4 left-4"
1798
+ };
1799
+ /** Renders the toast provider component. */
1800
+ function ToastProvider({ children, position = "top-right", defaultDuration = 5e3, stackLimit = 5 }) {
1801
+ const allToasts = (0, react.useSyncExternalStore)(toastStore.subscribe, toastStore.getSnapshot, toastStore.getSnapshot);
1802
+ const visibleToasts = allToasts.length > stackLimit ? allToasts.slice(-stackLimit) : allToasts;
1803
+ const addToast = (0, react.useCallback)((t) => toastStore.add(t), []);
1804
+ const removeToast = (0, react.useCallback)((id) => toastStore.dismiss(id), []);
1805
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ToastContext.Provider, {
1806
+ value: {
1807
+ toasts: visibleToasts,
1808
+ addToast,
1809
+ removeToast
1810
+ },
1811
+ children: [children, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1812
+ className: require_pagination.cn("gap-element fixed z-50 flex flex-col", positionStyles[position]),
1813
+ role: "region",
1814
+ "aria-label": "Notifications",
1815
+ "data-opt-id": "M0KTS",
1816
+ "data-opt-slug": "notification-toast.ToastProvider",
1817
+ children: visibleToasts.map((t) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NotificationToast, {
1818
+ toast: t,
1819
+ defaultDuration,
1820
+ onDismiss: () => toastStore.dismiss(t.id)
1821
+ }, t.id))
1822
+ })]
1823
+ });
1824
+ }
1825
+ /** Provides toast. */
1826
+ function useToast() {
1827
+ const context = (0, react.useContext)(ToastContext);
1828
+ if (!context) throw new Error("useToast must be used within a ToastProvider");
1829
+ return context;
1830
+ }
1831
+ /** Renders the notification toast component. */
1832
+ function NotificationToast({ toast, onDismiss, defaultDuration = 5e3 }) {
1833
+ var _toast$duration;
1834
+ const duration = (_toast$duration = toast.duration) !== null && _toast$duration !== void 0 ? _toast$duration : defaultDuration;
1835
+ const isUrgent = toast.variant === "error" || toast.variant === "warning";
1836
+ const onDismissRef = (0, react.useRef)(onDismiss);
1837
+ const dismissedRef = (0, react.useRef)(false);
1838
+ const timerStartedAtRef = (0, react.useRef)(null);
1839
+ const remainingDurationRef = (0, react.useRef)(duration);
1840
+ const [paused, setPaused] = (0, react.useState)(false);
1841
+ (0, react.useEffect)(() => {
1842
+ onDismissRef.current = onDismiss;
1843
+ }, [onDismiss]);
1844
+ const dismissOnce = (0, react.useCallback)(() => {
1845
+ if (dismissedRef.current) return;
1846
+ dismissedRef.current = true;
1847
+ onDismissRef.current();
1848
+ }, []);
1849
+ (0, react.useEffect)(() => {
1850
+ dismissedRef.current = false;
1851
+ remainingDurationRef.current = duration;
1852
+ }, [toast.id, duration]);
1853
+ (0, react.useEffect)(() => {
1854
+ if (!duration || duration <= 0 || paused || dismissedRef.current) return;
1855
+ timerStartedAtRef.current = Date.now();
1856
+ const timer = setTimeout(() => {
1857
+ dismissOnce();
1858
+ }, remainingDurationRef.current);
1859
+ return () => {
1860
+ clearTimeout(timer);
1861
+ if (timerStartedAtRef.current !== null) {
1862
+ const elapsed = Date.now() - timerStartedAtRef.current;
1863
+ remainingDurationRef.current = Math.max(0, remainingDurationRef.current - elapsed);
1864
+ timerStartedAtRef.current = null;
1865
+ }
1866
+ };
1867
+ }, [
1868
+ dismissOnce,
1869
+ duration,
1870
+ paused
1871
+ ]);
1872
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1873
+ className: "animate-in slide-in-from-right-full fade-in w-80 duration-200",
1874
+ role: isUrgent ? "alert" : "status",
1875
+ "aria-live": isUrgent ? "assertive" : "polite",
1876
+ "aria-atomic": "true",
1877
+ onMouseEnter: () => setPaused(true),
1878
+ onMouseLeave: () => setPaused(false),
1879
+ onFocus: () => setPaused(true),
1880
+ onBlur: (event) => {
1881
+ if (!event.currentTarget.contains(event.relatedTarget)) setPaused(false);
1882
+ },
1883
+ "data-opt-id": "26YVM",
1884
+ "data-opt-slug": "notification-toast.NotificationToast",
1885
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_pagination.Alert, {
1886
+ variant: toast.variant,
1887
+ title: toast.title,
1888
+ dismissible: true,
1889
+ onDismiss: dismissOnce,
1890
+ children: [toast.message, toast.actions && toast.actions.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1891
+ className: "gap-element mt-2 flex",
1892
+ children: toast.actions.map((action) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1893
+ type: "button",
1894
+ onClick: action.onClick,
1895
+ className: "text-xs font-medium underline hover:no-underline",
1896
+ children: action.label
1897
+ }, action.label))
1898
+ })]
1899
+ })
1900
+ });
1901
+ }
1902
+ //#endregion
1903
+ //#region src/shells/failure-list.tsx
1904
+ const defaultLabels$1 = {
1905
+ title: "최근 실패",
1906
+ emptyMessage: "실패 항목이 없습니다",
1907
+ moreItems: "{count}개 더 보기",
1908
+ severityLabels: {
1909
+ critical: "심각",
1910
+ error: "오류",
1911
+ warning: "경고"
1912
+ },
1913
+ timeAgo: {
1914
+ justNow: "방금 전",
1915
+ minutesAgo: "{minutes}분 전",
1916
+ hoursAgo: "{hours}시간 전",
1917
+ daysAgo: "{days}일 전"
1918
+ }
1919
+ };
1920
+ const severityVariant = {
1921
+ critical: "error",
1922
+ error: "error",
1923
+ warning: "warning"
1924
+ };
1925
+ const severityBorder = {
1926
+ critical: "border-l-2 border-l-danger",
1927
+ error: "border-l-2 border-l-danger",
1928
+ warning: "border-l-2 border-l-warning"
1929
+ };
1930
+ function formatRelativeTime(timestamp, timeLabels, locale) {
1931
+ const now = /* @__PURE__ */ new Date();
1932
+ const date = new Date(timestamp);
1933
+ const diffMs = now.getTime() - date.getTime();
1934
+ const diffMins = Math.floor(diffMs / 6e4);
1935
+ const diffHours = Math.floor(diffMs / 36e5);
1936
+ const diffDays = Math.floor(diffMs / 864e5);
1937
+ if (diffMins < 1) return timeLabels.justNow;
1938
+ if (diffMins < 60) return timeLabels.minutesAgo.replace("{minutes}", String(diffMins));
1939
+ if (diffHours < 24) return timeLabels.hoursAgo.replace("{hours}", String(diffHours));
1940
+ if (diffDays < 7) return timeLabels.daysAgo.replace("{days}", String(diffDays));
1941
+ return date.toLocaleDateString(locale);
1942
+ }
1943
+ function groupBy(items, key) {
1944
+ const map = /* @__PURE__ */ new Map();
1945
+ for (const item of items) {
1946
+ const k = key(item);
1947
+ const existing = map.get(k);
1948
+ if (existing) existing.push(item);
1949
+ else map.set(k, [item]);
1950
+ }
1951
+ return map;
1952
+ }
1953
+ function FailureItemRow({ failure, isLast, onFailureClick, renderActions, mergedSeverityLabels, mergedTimeAgo, locale }) {
1954
+ const itemId = `failure-${failure.id}`;
1955
+ const handleKeyDown = (event) => {
1956
+ if (!onFailureClick) return;
1957
+ if (event.key !== "Enter" && event.key !== " ") return;
1958
+ event.preventDefault();
1959
+ onFailureClick(failure);
1960
+ };
1961
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_reopt_ai_opt_ui_primitives.CompositeItem, {
1962
+ render: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("article", {}),
1963
+ role: "article",
1964
+ "aria-labelledby": itemId,
1965
+ className: require_pagination.cn("group gap-group relative flex w-full items-start p-3", require_pagination.OPT_FOCUS_VISIBLE, "data-[active-item]:bg-accent-subtle", onFailureClick && "cursor-pointer", !isLast && "border-border-subtle border-b", severityBorder[failure.severity]),
1966
+ onClick: () => onFailureClick === null || onFailureClick === void 0 ? void 0 : onFailureClick(failure),
1967
+ onKeyDown: handleKeyDown,
1968
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1969
+ className: "min-w-0 flex-1",
1970
+ children: [
1971
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1972
+ className: "gap-element flex items-center",
1973
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.Badge, {
1974
+ variant: severityVariant[failure.severity],
1975
+ size: "sm",
1976
+ children: mergedSeverityLabels[failure.severity]
1977
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1978
+ id: itemId,
1979
+ className: `text-sm font-medium ${require_pagination.OPT_TEXT_PRIMARY} truncate`,
1980
+ children: failure.title
1981
+ })]
1982
+ }),
1983
+ failure.message && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1984
+ className: `mt-1 text-xs text-text-secondary line-clamp-2`,
1985
+ children: failure.message
1986
+ }),
1987
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("time", {
1988
+ dateTime: failure.timestamp,
1989
+ className: `mt-1 block text-xs ${require_pagination.OPT_TEXT_TERTIARY}`,
1990
+ children: formatRelativeTime(failure.timestamp, mergedTimeAgo, locale)
1991
+ })
1992
+ ]
1993
+ }), renderActions && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1994
+ className: "flex shrink-0 items-center gap-1",
1995
+ children: renderActions(failure)
1996
+ })]
1997
+ }, failure.id);
1998
+ }
1999
+ /** Renders the failure list component. */
2000
+ function FailureList({ failures, title, maxItems, grouped = false, onFailureClick, onShowMore, labels: customLabels, locale = "ko-KR", loading = false, renderActions, className }) {
2001
+ var _customLabels$timeAgo, _customLabels$timeAgo2, _customLabels$timeAgo3, _customLabels$timeAgo4, _customLabels$timeAgo5, _customLabels$timeAgo6, _customLabels$timeAgo7, _customLabels$timeAgo8, _customLabels$severit, _customLabels$severit2, _customLabels$severit3, _customLabels$severit4, _customLabels$severit5, _customLabels$severit6, _customLabels$title, _customLabels$emptyMe, _customLabels$moreIte;
2002
+ const mergedTimeAgo = {
2003
+ justNow: (_customLabels$timeAgo = customLabels === null || customLabels === void 0 || (_customLabels$timeAgo2 = customLabels.timeAgo) === null || _customLabels$timeAgo2 === void 0 ? void 0 : _customLabels$timeAgo2.justNow) !== null && _customLabels$timeAgo !== void 0 ? _customLabels$timeAgo : defaultLabels$1.timeAgo.justNow,
2004
+ minutesAgo: (_customLabels$timeAgo3 = customLabels === null || customLabels === void 0 || (_customLabels$timeAgo4 = customLabels.timeAgo) === null || _customLabels$timeAgo4 === void 0 ? void 0 : _customLabels$timeAgo4.minutesAgo) !== null && _customLabels$timeAgo3 !== void 0 ? _customLabels$timeAgo3 : defaultLabels$1.timeAgo.minutesAgo,
2005
+ hoursAgo: (_customLabels$timeAgo5 = customLabels === null || customLabels === void 0 || (_customLabels$timeAgo6 = customLabels.timeAgo) === null || _customLabels$timeAgo6 === void 0 ? void 0 : _customLabels$timeAgo6.hoursAgo) !== null && _customLabels$timeAgo5 !== void 0 ? _customLabels$timeAgo5 : defaultLabels$1.timeAgo.hoursAgo,
2006
+ daysAgo: (_customLabels$timeAgo7 = customLabels === null || customLabels === void 0 || (_customLabels$timeAgo8 = customLabels.timeAgo) === null || _customLabels$timeAgo8 === void 0 ? void 0 : _customLabels$timeAgo8.daysAgo) !== null && _customLabels$timeAgo7 !== void 0 ? _customLabels$timeAgo7 : defaultLabels$1.timeAgo.daysAgo
2007
+ };
2008
+ const mergedSeverityLabels = {
2009
+ critical: (_customLabels$severit = customLabels === null || customLabels === void 0 || (_customLabels$severit2 = customLabels.severityLabels) === null || _customLabels$severit2 === void 0 ? void 0 : _customLabels$severit2.critical) !== null && _customLabels$severit !== void 0 ? _customLabels$severit : defaultLabels$1.severityLabels.critical,
2010
+ error: (_customLabels$severit3 = customLabels === null || customLabels === void 0 || (_customLabels$severit4 = customLabels.severityLabels) === null || _customLabels$severit4 === void 0 ? void 0 : _customLabels$severit4.error) !== null && _customLabels$severit3 !== void 0 ? _customLabels$severit3 : defaultLabels$1.severityLabels.error,
2011
+ warning: (_customLabels$severit5 = customLabels === null || customLabels === void 0 || (_customLabels$severit6 = customLabels.severityLabels) === null || _customLabels$severit6 === void 0 ? void 0 : _customLabels$severit6.warning) !== null && _customLabels$severit5 !== void 0 ? _customLabels$severit5 : defaultLabels$1.severityLabels.warning
2012
+ };
2013
+ const labels = {
2014
+ title: (_customLabels$title = customLabels === null || customLabels === void 0 ? void 0 : customLabels.title) !== null && _customLabels$title !== void 0 ? _customLabels$title : defaultLabels$1.title,
2015
+ emptyMessage: (_customLabels$emptyMe = customLabels === null || customLabels === void 0 ? void 0 : customLabels.emptyMessage) !== null && _customLabels$emptyMe !== void 0 ? _customLabels$emptyMe : defaultLabels$1.emptyMessage,
2016
+ moreItems: (_customLabels$moreIte = customLabels === null || customLabels === void 0 ? void 0 : customLabels.moreItems) !== null && _customLabels$moreIte !== void 0 ? _customLabels$moreIte : defaultLabels$1.moreItems
2017
+ };
2018
+ const displayTitle = title !== null && title !== void 0 ? title : labels.title;
2019
+ const allItems = maxItems ? failures.slice(0, maxItems) : failures;
2020
+ const remainingCount = maxItems ? Math.max(0, failures.length - maxItems) : 0;
2021
+ const rowProps = {
2022
+ onFailureClick,
2023
+ renderActions,
2024
+ mergedSeverityLabels,
2025
+ mergedTimeAgo,
2026
+ locale
2027
+ };
2028
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2029
+ className,
2030
+ "data-opt-id": "ZAM3R",
2031
+ "data-opt-slug": "failure-list.FailureList",
2032
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2033
+ className: "gap-group flex flex-col",
2034
+ children: [displayTitle && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
2035
+ className: `text-sm font-semibold text-text-primary`,
2036
+ children: displayTitle
2037
+ }), failures.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2038
+ role: "status",
2039
+ className: `py-8 text-center text-sm ${require_pagination.OPT_TEXT_SECONDARY}`,
2040
+ children: labels.emptyMessage
2041
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CompositeProvider, {
2042
+ focusLoop: true,
2043
+ orientation: "vertical",
2044
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.Composite, {
2045
+ role: "feed",
2046
+ "aria-label": displayTitle,
2047
+ "aria-busy": loading,
2048
+ className: `rounded-lg ${require_pagination.OPT_BORDER} bg-surface`,
2049
+ children: grouped ? renderGrouped(allItems, remainingCount, rowProps, labels, onShowMore) : renderFlat(allItems, remainingCount, rowProps, labels, onShowMore)
2050
+ })
2051
+ })]
2052
+ })
2053
+ });
2054
+ }
2055
+ function renderFlat(items, remainingCount, rowProps, labels, onShowMore) {
2056
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [items.map((failure, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FailureItemRow, require_pagination._objectSpread2({
2057
+ failure,
2058
+ isLast: index === items.length - 1 && remainingCount === 0
2059
+ }, rowProps), failure.id)), remainingCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CompositeItem, {
2060
+ render: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", { type: "button" }),
2061
+ onClick: onShowMore,
2062
+ className: `border-border-subtle w-full border-t p-3 text-center text-xs font-medium text-text-secondary outline-none data-[focus-visible]:outline-2 data-[focus-visible]:outline-ring data-[focus-visible]:-outline-offset-1 hover:text-accent data-[active-item]:bg-accent-subtle transition-colors`,
2063
+ "aria-label": `${remainingCount}개의 실패 더 보기`,
2064
+ children: labels.moreItems.replace("{count}", String(remainingCount))
2065
+ })] });
2066
+ }
2067
+ function renderGrouped(items, remainingCount, rowProps, labels, onShowMore) {
2068
+ const groups = groupBy(items, (f) => {
2069
+ var _f$group;
2070
+ return (_f$group = f.group) !== null && _f$group !== void 0 ? _f$group : "";
2071
+ });
2072
+ const groupEntries = Array.from(groups.entries());
2073
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [groupEntries.map(([groupName, groupItems], groupIndex) => {
2074
+ const isLastGroup = groupIndex === groupEntries.length - 1 && remainingCount === 0;
2075
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [groupName && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2076
+ className: require_pagination.cn(`px-3 py-2 text-xs font-semibold text-text-tertiary tracking-wider uppercase`, groupIndex > 0 && "border-border-subtle border-t"),
2077
+ children: groupName
2078
+ }), groupItems.map((failure, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FailureItemRow, require_pagination._objectSpread2({
2079
+ failure,
2080
+ isLast: isLastGroup && index === groupItems.length - 1
2081
+ }, rowProps), failure.id))] }, groupName || "__ungrouped__");
2082
+ }), remainingCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_reopt_ai_opt_ui_primitives.CompositeItem, {
2083
+ render: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", { type: "button" }),
2084
+ onClick: onShowMore,
2085
+ className: `border-border-subtle w-full border-t p-3 text-center text-xs font-medium text-text-secondary outline-none data-[focus-visible]:outline-2 data-[focus-visible]:outline-ring data-[focus-visible]:-outline-offset-1 hover:text-accent data-[active-item]:bg-accent-subtle transition-colors`,
2086
+ "aria-label": `${remainingCount}개의 실패 더 보기`,
2087
+ children: labels.moreItems.replace("{count}", String(remainingCount))
2088
+ })] });
2089
+ }
2090
+ //#endregion
2091
+ //#region src/shells/key-value-editor.tsx
2092
+ const defaultLabels = {
2093
+ keyPlaceholder: "키",
2094
+ valuePlaceholder: "값",
2095
+ addButton: "추가",
2096
+ removeAriaLabel: "항목 삭제",
2097
+ toggleSecretAriaLabel: "비밀값 토글",
2098
+ emptyMessage: "항목이 없습니다. 추가 버튼을 눌러주세요."
2099
+ };
2100
+ let idCounter = 0;
2101
+ function generateId() {
2102
+ return `kv-${++idCounter}`;
2103
+ }
2104
+ /** Renders the key value editor component. */
2105
+ function KeyValueEditor({ value: controlledValue, defaultValue, onChange, showSecretToggle = false, maxEntries, readOnly = false, labels: customLabels, renderActions, validateKey, className }) {
2106
+ const labels = require_pagination._objectSpread2(require_pagination._objectSpread2({}, defaultLabels), customLabels);
2107
+ const [internalEntries, setInternalEntries] = (0, react.useState)(() => defaultValue !== null && defaultValue !== void 0 ? defaultValue : []);
2108
+ const entries = controlledValue !== null && controlledValue !== void 0 ? controlledValue : internalEntries;
2109
+ const keyInputRef = (0, react.useRef)(null);
2110
+ const updateEntries = (0, react.useCallback)((next) => {
2111
+ if (!controlledValue) setInternalEntries(next);
2112
+ onChange === null || onChange === void 0 || onChange(next);
2113
+ }, [controlledValue, onChange]);
2114
+ const addEntry = (0, react.useCallback)(() => {
2115
+ if (maxEntries && entries.length >= maxEntries) return;
2116
+ const newEntry = {
2117
+ id: generateId(),
2118
+ key: "",
2119
+ value: "",
2120
+ isSecret: false
2121
+ };
2122
+ updateEntries([...entries, newEntry]);
2123
+ requestAnimationFrame(() => {
2124
+ var _keyInputRef$current;
2125
+ (_keyInputRef$current = keyInputRef.current) === null || _keyInputRef$current === void 0 || (_keyInputRef$current = _keyInputRef$current.closest("[data-kv-list]")) === null || _keyInputRef$current === void 0 || (_keyInputRef$current = _keyInputRef$current.querySelector(`[data-kv-id="${newEntry.id}"] [data-kv-key]`)) === null || _keyInputRef$current === void 0 || _keyInputRef$current.focus();
2126
+ });
2127
+ }, [
2128
+ entries,
2129
+ maxEntries,
2130
+ updateEntries
2131
+ ]);
2132
+ const removeEntry = (0, react.useCallback)((id) => {
2133
+ updateEntries(entries.filter((e) => e.id !== id));
2134
+ }, [entries, updateEntries]);
2135
+ const updateEntry = (0, react.useCallback)((id, field, val) => {
2136
+ updateEntries(entries.map((e) => e.id === id ? require_pagination._objectSpread2(require_pagination._objectSpread2({}, e), {}, { [field]: val }) : e));
2137
+ }, [entries, updateEntries]);
2138
+ const toggleSecret = (0, react.useCallback)((id) => {
2139
+ updateEntries(entries.map((e) => e.id === id ? require_pagination._objectSpread2(require_pagination._objectSpread2({}, e), {}, { isSecret: !e.isSecret }) : e));
2140
+ }, [entries, updateEntries]);
2141
+ const canAdd = !readOnly && (!maxEntries || entries.length < maxEntries);
2142
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2143
+ className: require_pagination.cn("gap-group flex flex-col", className),
2144
+ "data-opt-id": "NCBWA",
2145
+ "data-opt-slug": "key-value-editor.KeyValueEditor",
2146
+ children: [entries.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2147
+ className: "text-text-tertiary py-4 text-center text-sm",
2148
+ children: labels.emptyMessage
2149
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2150
+ className: "gap-element flex flex-col",
2151
+ "data-kv-list": true,
2152
+ children: entries.map((entry, index) => {
2153
+ const keyError = validateKey === null || validateKey === void 0 ? void 0 : validateKey(entry.key, entries);
2154
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2155
+ "data-kv-id": entry.id,
2156
+ className: require_pagination.cn("gap-element flex items-start", "border-border rounded-lg border p-3", "bg-surface"),
2157
+ children: [
2158
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2159
+ className: "flex min-w-0 flex-1 flex-col gap-1",
2160
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2161
+ "data-kv-key": true,
2162
+ ref: index === entries.length - 1 ? keyInputRef : void 0,
2163
+ type: "text",
2164
+ value: entry.key,
2165
+ onChange: (e) => updateEntry(entry.id, "key", e.target.value),
2166
+ placeholder: labels.keyPlaceholder,
2167
+ readOnly,
2168
+ "aria-label": `${labels.keyPlaceholder} ${index + 1}`,
2169
+ className: require_pagination.cn("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", "focus:border-accent focus-visible:ring-ring focus-visible:ring-offset-ring-offset focus-visible:ring-2 focus-visible:ring-offset-1", readOnly && "cursor-default opacity-70", keyError && "border-error")
2170
+ }), keyError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2171
+ className: "text-error text-xs",
2172
+ children: keyError
2173
+ })]
2174
+ }),
2175
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2176
+ className: "text-text-tertiary px-1 py-1.5",
2177
+ children: "="
2178
+ }),
2179
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2180
+ className: "min-w-0 flex-1",
2181
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2182
+ type: entry.isSecret ? "password" : "text",
2183
+ value: entry.value,
2184
+ onChange: (e) => updateEntry(entry.id, "value", e.target.value),
2185
+ placeholder: labels.valuePlaceholder,
2186
+ readOnly,
2187
+ "aria-label": `${labels.valuePlaceholder} ${index + 1}`,
2188
+ className: require_pagination.cn("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", "focus:border-accent focus-visible:ring-ring focus-visible:ring-offset-ring-offset focus-visible:ring-2 focus-visible:ring-offset-1", readOnly && "cursor-default opacity-70")
2189
+ })
2190
+ }),
2191
+ showSecretToggle && !readOnly && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2192
+ type: "button",
2193
+ onClick: () => toggleSecret(entry.id),
2194
+ "aria-label": labels.toggleSecretAriaLabel,
2195
+ "aria-pressed": entry.isSecret,
2196
+ className: "text-text-tertiary hover:text-text-primary shrink-0 rounded p-1.5 transition-colors",
2197
+ children: entry.isSecret ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
2198
+ className: "size-4",
2199
+ fill: "none",
2200
+ viewBox: "0 0 24 24",
2201
+ stroke: "currentColor",
2202
+ strokeWidth: 2,
2203
+ "aria-hidden": "true",
2204
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
2205
+ strokeLinecap: "round",
2206
+ strokeLinejoin: "round",
2207
+ 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"
2208
+ })
2209
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
2210
+ className: "size-4",
2211
+ fill: "none",
2212
+ viewBox: "0 0 24 24",
2213
+ stroke: "currentColor",
2214
+ strokeWidth: 2,
2215
+ "aria-hidden": "true",
2216
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
2217
+ strokeLinecap: "round",
2218
+ strokeLinejoin: "round",
2219
+ 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"
2220
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
2221
+ strokeLinecap: "round",
2222
+ strokeLinejoin: "round",
2223
+ d: "M15 12a3 3 0 11-6 0 3 3 0 016 0z"
2224
+ })]
2225
+ })
2226
+ }),
2227
+ renderActions === null || renderActions === void 0 ? void 0 : renderActions(entry, index),
2228
+ !readOnly && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2229
+ type: "button",
2230
+ onClick: () => removeEntry(entry.id),
2231
+ "aria-label": labels.removeAriaLabel,
2232
+ className: "text-text-tertiary hover:text-error shrink-0 rounded p-1.5 transition-colors",
2233
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
2234
+ className: "size-4",
2235
+ fill: "none",
2236
+ viewBox: "0 0 24 24",
2237
+ stroke: "currentColor",
2238
+ strokeWidth: 2,
2239
+ "aria-hidden": "true",
2240
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
2241
+ strokeLinecap: "round",
2242
+ strokeLinejoin: "round",
2243
+ d: "M6 18L18 6M6 6l12 12"
2244
+ })
2245
+ })
2246
+ })
2247
+ ]
2248
+ }, entry.id);
2249
+ })
2250
+ }), canAdd && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2251
+ type: "button",
2252
+ onClick: addEntry,
2253
+ className: require_pagination.cn("border-border text-text-secondary rounded-lg border border-dashed px-4 py-2 text-sm transition-colors", "hover:border-accent hover:text-accent"),
2254
+ children: labels.addButton
2255
+ })]
2256
+ });
2257
+ }
2258
+ //#endregion
2259
+ //#region src/shells/workflow-canvas.tsx
2260
+ const statusVariant = {
2261
+ idle: "default",
2262
+ running: "info",
2263
+ success: "success",
2264
+ error: "error"
2265
+ };
2266
+ function clamp(value, min, max) {
2267
+ return Math.max(min, Math.min(max, value));
2268
+ }
2269
+ /** Renders the workflow canvas component. */
2270
+ function WorkflowCanvas({ steps, connections = [], rows = 9, cols = 9, cellSize = 72, gap = 12, title = "Workflow Canvas", emptyMessage = "No workflow steps", selectedStepId, onSelectStep, onStepMove, renderStep, className }) {
2271
+ const stepMap = react.useMemo(() => new Map(steps.map((step) => [step.id, step])), [steps]);
2272
+ const width = cols * cellSize + (cols - 1) * gap;
2273
+ const height = rows * cellSize + (rows - 1) * gap;
2274
+ const renderDefaultStep = react.useCallback((step, selected) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2275
+ type: "button",
2276
+ onClick: () => onSelectStep === null || onSelectStep === void 0 ? void 0 : onSelectStep(step),
2277
+ className: require_pagination.cn("group absolute flex h-full w-full flex-col justify-between rounded-lg border p-2 text-left transition-colors", selected ? "border-accent bg-accent-subtle" : "border-border bg-surface hover:bg-bg-subtle"),
2278
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2279
+ className: "text-text-primary truncate text-xs font-medium",
2280
+ children: step.title
2281
+ }), step.status && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_pagination.Badge, {
2282
+ variant: statusVariant[step.status],
2283
+ size: "sm",
2284
+ className: "w-fit",
2285
+ children: step.status
2286
+ })]
2287
+ }), [onSelectStep]);
2288
+ const handleStepKeyDown = react.useCallback((event, step) => {
2289
+ if (!onStepMove) return;
2290
+ let nextRow = step.row;
2291
+ let nextCol = step.col;
2292
+ switch (event.key) {
2293
+ case "ArrowUp":
2294
+ nextRow -= 1;
2295
+ break;
2296
+ case "ArrowDown":
2297
+ nextRow += 1;
2298
+ break;
2299
+ case "ArrowLeft":
2300
+ nextCol -= 1;
2301
+ break;
2302
+ case "ArrowRight":
2303
+ nextCol += 1;
2304
+ break;
2305
+ default: return;
2306
+ }
2307
+ event.preventDefault();
2308
+ onStepMove(step.id, clamp(nextRow, 0, rows - 1), clamp(nextCol, 0, cols - 1));
2309
+ }, [
2310
+ onStepMove,
2311
+ rows,
2312
+ cols
2313
+ ]);
2314
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2315
+ className: require_pagination.cn("gap-group flex flex-col", className),
2316
+ "data-opt-id": "4TX11",
2317
+ "data-opt-slug": "workflow-canvas.WorkflowCanvas",
2318
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
2319
+ className: "text-text-primary text-sm font-semibold",
2320
+ children: title
2321
+ }), steps.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2322
+ className: "text-text-secondary border-border rounded-lg border border-dashed p-8 text-center text-sm",
2323
+ children: emptyMessage
2324
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2325
+ className: "border-border bg-bg-subtle relative overflow-auto rounded-lg border p-3",
2326
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2327
+ role: "grid",
2328
+ "aria-label": title,
2329
+ className: "relative",
2330
+ style: {
2331
+ width,
2332
+ height
2333
+ },
2334
+ children: [
2335
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2336
+ className: "grid",
2337
+ style: {
2338
+ gridTemplateColumns: `repeat(${cols}, ${cellSize}px)`,
2339
+ gridTemplateRows: `repeat(${rows}, ${cellSize}px)`,
2340
+ gap
2341
+ },
2342
+ children: Array.from({ length: rows * cols }, (_, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2343
+ "aria-hidden": "true",
2344
+ className: "border-border-subtle bg-surface/30 rounded-md border border-dashed"
2345
+ }, i))
2346
+ }),
2347
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
2348
+ className: "pointer-events-none absolute inset-0",
2349
+ width,
2350
+ height,
2351
+ "aria-hidden": "true",
2352
+ children: connections.map((connection) => {
2353
+ const from = stepMap.get(connection.from);
2354
+ const to = stepMap.get(connection.to);
2355
+ if (!from || !to) return null;
2356
+ const x1 = from.col * (cellSize + gap) + cellSize / 2;
2357
+ const y1 = from.row * (cellSize + gap) + cellSize / 2;
2358
+ const x2 = to.col * (cellSize + gap) + cellSize / 2;
2359
+ const y2 = to.row * (cellSize + gap) + cellSize / 2;
2360
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
2361
+ x1,
2362
+ y1,
2363
+ x2,
2364
+ y2,
2365
+ stroke: "currentColor",
2366
+ strokeOpacity: "0.35",
2367
+ strokeWidth: "2"
2368
+ }), connection.label && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("text", {
2369
+ x: (x1 + x2) / 2,
2370
+ y: (y1 + y2) / 2 - 4,
2371
+ textAnchor: "middle",
2372
+ className: "fill-text-secondary text-[10px]",
2373
+ children: connection.label
2374
+ })] }, `${connection.from}-${connection.to}`);
2375
+ })
2376
+ }),
2377
+ steps.map((step) => {
2378
+ const selected = selectedStepId === step.id;
2379
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2380
+ role: "gridcell",
2381
+ tabIndex: 0,
2382
+ "aria-selected": selected,
2383
+ "aria-label": `${step.title} (${step.row + 1}, ${step.col + 1})`,
2384
+ onKeyDown: (event) => handleStepKeyDown(event, step),
2385
+ className: "focus-visible:ring-ring absolute focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-white focus-visible:outline-none",
2386
+ style: {
2387
+ width: cellSize,
2388
+ height: cellSize,
2389
+ left: step.col * (cellSize + gap),
2390
+ top: step.row * (cellSize + gap)
2391
+ },
2392
+ children: renderStep ? renderStep(step, selected) : renderDefaultStep(step, selected)
2393
+ }, step.id);
2394
+ })
2395
+ ]
2396
+ })
2397
+ })]
2398
+ });
2399
+ }
2400
+ WorkflowCanvas.displayName = "WorkflowCanvas";
2401
+ //#endregion
2402
+ Object.defineProperty(exports, "AppMenubar", {
2403
+ enumerable: true,
2404
+ get: function() {
2405
+ return AppMenubar;
2406
+ }
2407
+ });
2408
+ Object.defineProperty(exports, "BranchSelect", {
2409
+ enumerable: true,
2410
+ get: function() {
2411
+ return BranchSelect;
2412
+ }
2413
+ });
2414
+ Object.defineProperty(exports, "Command", {
2415
+ enumerable: true,
2416
+ get: function() {
2417
+ return Command;
2418
+ }
2419
+ });
2420
+ Object.defineProperty(exports, "CommandDialog", {
2421
+ enumerable: true,
2422
+ get: function() {
2423
+ return CommandDialog;
2424
+ }
2425
+ });
2426
+ Object.defineProperty(exports, "CommandEmpty", {
2427
+ enumerable: true,
2428
+ get: function() {
2429
+ return CommandEmpty;
2430
+ }
2431
+ });
2432
+ Object.defineProperty(exports, "CommandGroup", {
2433
+ enumerable: true,
2434
+ get: function() {
2435
+ return CommandGroup;
2436
+ }
2437
+ });
2438
+ Object.defineProperty(exports, "CommandInput", {
2439
+ enumerable: true,
2440
+ get: function() {
2441
+ return CommandInput;
2442
+ }
2443
+ });
2444
+ Object.defineProperty(exports, "CommandItem", {
2445
+ enumerable: true,
2446
+ get: function() {
2447
+ return CommandItem;
2448
+ }
2449
+ });
2450
+ Object.defineProperty(exports, "CommandList", {
2451
+ enumerable: true,
2452
+ get: function() {
2453
+ return CommandList;
2454
+ }
2455
+ });
2456
+ Object.defineProperty(exports, "CommandPalette", {
2457
+ enumerable: true,
2458
+ get: function() {
2459
+ return CommandPalette;
2460
+ }
2461
+ });
2462
+ Object.defineProperty(exports, "CommandPaletteTrigger", {
2463
+ enumerable: true,
2464
+ get: function() {
2465
+ return CommandPaletteTrigger;
2466
+ }
2467
+ });
2468
+ Object.defineProperty(exports, "CommandSeparator", {
2469
+ enumerable: true,
2470
+ get: function() {
2471
+ return CommandSeparator;
2472
+ }
2473
+ });
2474
+ Object.defineProperty(exports, "CommandShortcut", {
2475
+ enumerable: true,
2476
+ get: function() {
2477
+ return CommandShortcut;
2478
+ }
2479
+ });
2480
+ Object.defineProperty(exports, "ContentTabs", {
2481
+ enumerable: true,
2482
+ get: function() {
2483
+ return ContentTabs;
2484
+ }
2485
+ });
2486
+ Object.defineProperty(exports, "DashboardGrid", {
2487
+ enumerable: true,
2488
+ get: function() {
2489
+ return DashboardGrid;
2490
+ }
2491
+ });
2492
+ Object.defineProperty(exports, "DataTable", {
2493
+ enumerable: true,
2494
+ get: function() {
2495
+ return DataTable;
2496
+ }
2497
+ });
2498
+ Object.defineProperty(exports, "DeploymentTimeline", {
2499
+ enumerable: true,
2500
+ get: function() {
2501
+ return DeploymentTimeline;
2502
+ }
2503
+ });
2504
+ Object.defineProperty(exports, "DomainTable", {
2505
+ enumerable: true,
2506
+ get: function() {
2507
+ return DomainTable;
2508
+ }
2509
+ });
2510
+ Object.defineProperty(exports, "EditorToolbar", {
2511
+ enumerable: true,
2512
+ get: function() {
2513
+ return EditorToolbar;
2514
+ }
2515
+ });
2516
+ Object.defineProperty(exports, "EnvPanel", {
2517
+ enumerable: true,
2518
+ get: function() {
2519
+ return EnvPanel;
2520
+ }
2521
+ });
2522
+ Object.defineProperty(exports, "FailureList", {
2523
+ enumerable: true,
2524
+ get: function() {
2525
+ return FailureList;
2526
+ }
2527
+ });
2528
+ Object.defineProperty(exports, "FaqAccordion", {
2529
+ enumerable: true,
2530
+ get: function() {
2531
+ return FaqAccordion;
2532
+ }
2533
+ });
2534
+ Object.defineProperty(exports, "KeyValueEditor", {
2535
+ enumerable: true,
2536
+ get: function() {
2537
+ return KeyValueEditor;
2538
+ }
2539
+ });
2540
+ Object.defineProperty(exports, "NotificationToast", {
2541
+ enumerable: true,
2542
+ get: function() {
2543
+ return NotificationToast;
2544
+ }
2545
+ });
2546
+ Object.defineProperty(exports, "ProjectSwitcher", {
2547
+ enumerable: true,
2548
+ get: function() {
2549
+ return ProjectSwitcher;
2550
+ }
2551
+ });
2552
+ Object.defineProperty(exports, "SearchCombobox", {
2553
+ enumerable: true,
2554
+ get: function() {
2555
+ return SearchCombobox;
2556
+ }
2557
+ });
2558
+ Object.defineProperty(exports, "SettingsForm", {
2559
+ enumerable: true,
2560
+ get: function() {
2561
+ return SettingsForm;
2562
+ }
2563
+ });
2564
+ Object.defineProperty(exports, "SidebarNav", {
2565
+ enumerable: true,
2566
+ get: function() {
2567
+ return SidebarNav;
2568
+ }
2569
+ });
2570
+ Object.defineProperty(exports, "StatusSelect", {
2571
+ enumerable: true,
2572
+ get: function() {
2573
+ return StatusSelect;
2574
+ }
2575
+ });
2576
+ Object.defineProperty(exports, "ToastProvider", {
2577
+ enumerable: true,
2578
+ get: function() {
2579
+ return ToastProvider;
2580
+ }
2581
+ });
2582
+ Object.defineProperty(exports, "WorkflowCanvas", {
2583
+ enumerable: true,
2584
+ get: function() {
2585
+ return WorkflowCanvas;
2586
+ }
2587
+ });
2588
+ Object.defineProperty(exports, "toast", {
2589
+ enumerable: true,
2590
+ get: function() {
2591
+ return toast;
2592
+ }
2593
+ });
2594
+ Object.defineProperty(exports, "useToast", {
2595
+ enumerable: true,
2596
+ get: function() {
2597
+ return useToast;
2598
+ }
2599
+ });