@showwhat/configurator 1.0.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.
package/dist/index.js ADDED
@@ -0,0 +1,3741 @@
1
+ // src/utils/cn.ts
2
+ import { clsx } from "clsx";
3
+ import { twMerge } from "tailwind-merge";
4
+ function cn(...inputs) {
5
+ return twMerge(clsx(inputs));
6
+ }
7
+
8
+ // src/utils/id.ts
9
+ var AUTO_ID_PREFIX = "_tmp:";
10
+ function isAutoId(id) {
11
+ return id != null && id.startsWith(AUTO_ID_PREFIX);
12
+ }
13
+ function ensureIds(items) {
14
+ let changed = false;
15
+ const result = items.map((item) => {
16
+ if (item.id) return item;
17
+ changed = true;
18
+ return { ...item, id: `${AUTO_ID_PREFIX}${crypto.randomUUID()}` };
19
+ });
20
+ return changed ? result : items;
21
+ }
22
+ function removeId(item) {
23
+ const copy = { ...item };
24
+ delete copy.id;
25
+ return copy;
26
+ }
27
+ function stripId(item) {
28
+ if (!isAutoId(item.id)) return item;
29
+ return removeId(item);
30
+ }
31
+ function stripConditionIds(conditions) {
32
+ return conditions.map((c) => {
33
+ const rec = c;
34
+ const stripped = isAutoId(rec.id) ? removeId(rec) : rec;
35
+ if ((c.type === "and" || c.type === "or") && "conditions" in c) {
36
+ return {
37
+ ...stripped,
38
+ conditions: stripConditionIds(c.conditions)
39
+ };
40
+ }
41
+ return stripped;
42
+ });
43
+ }
44
+ function stripAutoIds(definitions) {
45
+ const result = {};
46
+ for (const [key, def] of Object.entries(definitions)) {
47
+ const cleaned = stripId(def);
48
+ result[key] = {
49
+ ...cleaned,
50
+ variations: cleaned.variations.map((v) => {
51
+ const sv = stripId(v);
52
+ if (sv.conditions) {
53
+ return { ...sv, conditions: stripConditionIds(sv.conditions) };
54
+ }
55
+ return sv;
56
+ })
57
+ };
58
+ }
59
+ return result;
60
+ }
61
+
62
+ // src/components/condition-builder/StringConditionEditor.tsx
63
+ import { useCallback as useCallback2, useMemo } from "react";
64
+
65
+ // src/components/condition-builder/ConditionRow.tsx
66
+ import { jsx } from "react/jsx-runtime";
67
+ function ConditionRow({ children }) {
68
+ return /* @__PURE__ */ jsx(
69
+ "div",
70
+ {
71
+ className: "grid flex-1 items-start gap-2",
72
+ style: { gridTemplateColumns: "140px 72px 1fr" },
73
+ children
74
+ }
75
+ );
76
+ }
77
+
78
+ // src/components/ui/input.tsx
79
+ import { jsx as jsx2 } from "react/jsx-runtime";
80
+ function Input({ className, type, ...props }) {
81
+ return /* @__PURE__ */ jsx2(
82
+ "input",
83
+ {
84
+ type,
85
+ "data-slot": "input",
86
+ className: cn(
87
+ "h-9 w-full min-w-0 rounded border border-border/70 bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30",
88
+ "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
89
+ "aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
90
+ className
91
+ ),
92
+ ...props
93
+ }
94
+ );
95
+ }
96
+
97
+ // src/components/condition-builder/KeyInput.tsx
98
+ import { jsx as jsx3 } from "react/jsx-runtime";
99
+ function KeyInput({ value, onChange, placeholder, disabled }) {
100
+ return /* @__PURE__ */ jsx3(
101
+ Input,
102
+ {
103
+ className: "h-8 font-mono text-sm",
104
+ value,
105
+ placeholder: placeholder ?? "key",
106
+ disabled,
107
+ onChange: onChange ? (e) => onChange(e.target.value) : void 0,
108
+ readOnly: !onChange
109
+ }
110
+ );
111
+ }
112
+
113
+ // src/components/ui/select.tsx
114
+ import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
115
+ import { Select as SelectPrimitive } from "radix-ui";
116
+ import { jsx as jsx4, jsxs } from "react/jsx-runtime";
117
+ function Select({ ...props }) {
118
+ return /* @__PURE__ */ jsx4(SelectPrimitive.Root, { "data-slot": "select", ...props });
119
+ }
120
+ function SelectGroup({ ...props }) {
121
+ return /* @__PURE__ */ jsx4(SelectPrimitive.Group, { "data-slot": "select-group", ...props });
122
+ }
123
+ function SelectValue({ ...props }) {
124
+ return /* @__PURE__ */ jsx4(SelectPrimitive.Value, { "data-slot": "select-value", ...props });
125
+ }
126
+ function SelectTrigger({
127
+ className,
128
+ size = "default",
129
+ children,
130
+ ...props
131
+ }) {
132
+ return /* @__PURE__ */ jsxs(
133
+ SelectPrimitive.Trigger,
134
+ {
135
+ "data-slot": "select-trigger",
136
+ "data-size": size,
137
+ className: cn(
138
+ "flex w-fit items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
139
+ className
140
+ ),
141
+ ...props,
142
+ children: [
143
+ children,
144
+ /* @__PURE__ */ jsx4(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx4(ChevronDownIcon, { className: "size-4 opacity-50" }) })
145
+ ]
146
+ }
147
+ );
148
+ }
149
+ function SelectContent({
150
+ className,
151
+ children,
152
+ position = "item-aligned",
153
+ align = "center",
154
+ ...props
155
+ }) {
156
+ return /* @__PURE__ */ jsx4(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
157
+ SelectPrimitive.Content,
158
+ {
159
+ "data-slot": "select-content",
160
+ className: cn(
161
+ "relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
162
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
163
+ className
164
+ ),
165
+ position,
166
+ align,
167
+ ...props,
168
+ children: [
169
+ /* @__PURE__ */ jsx4(SelectScrollUpButton, {}),
170
+ /* @__PURE__ */ jsx4(
171
+ SelectPrimitive.Viewport,
172
+ {
173
+ className: cn(
174
+ "p-1",
175
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
176
+ ),
177
+ children
178
+ }
179
+ ),
180
+ /* @__PURE__ */ jsx4(SelectScrollDownButton, {})
181
+ ]
182
+ }
183
+ ) });
184
+ }
185
+ function SelectLabel({ className, ...props }) {
186
+ return /* @__PURE__ */ jsx4(
187
+ SelectPrimitive.Label,
188
+ {
189
+ "data-slot": "select-label",
190
+ className: cn("px-2 py-1.5 text-xs text-muted-foreground", className),
191
+ ...props
192
+ }
193
+ );
194
+ }
195
+ function SelectItem({
196
+ className,
197
+ children,
198
+ ...props
199
+ }) {
200
+ return /* @__PURE__ */ jsxs(
201
+ SelectPrimitive.Item,
202
+ {
203
+ "data-slot": "select-item",
204
+ className: cn(
205
+ "relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
206
+ className
207
+ ),
208
+ ...props,
209
+ children: [
210
+ /* @__PURE__ */ jsx4(
211
+ "span",
212
+ {
213
+ "data-slot": "select-item-indicator",
214
+ className: "absolute right-2 flex size-3.5 items-center justify-center",
215
+ children: /* @__PURE__ */ jsx4(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx4(CheckIcon, { className: "size-4" }) })
216
+ }
217
+ ),
218
+ /* @__PURE__ */ jsx4(SelectPrimitive.ItemText, { children })
219
+ ]
220
+ }
221
+ );
222
+ }
223
+ function SelectSeparator({
224
+ className,
225
+ ...props
226
+ }) {
227
+ return /* @__PURE__ */ jsx4(
228
+ SelectPrimitive.Separator,
229
+ {
230
+ "data-slot": "select-separator",
231
+ className: cn("pointer-events-none -mx-1 my-1 h-px bg-border", className),
232
+ ...props
233
+ }
234
+ );
235
+ }
236
+ function SelectScrollUpButton({
237
+ className,
238
+ ...props
239
+ }) {
240
+ return /* @__PURE__ */ jsx4(
241
+ SelectPrimitive.ScrollUpButton,
242
+ {
243
+ "data-slot": "select-scroll-up-button",
244
+ className: cn("flex cursor-default items-center justify-center py-1", className),
245
+ ...props,
246
+ children: /* @__PURE__ */ jsx4(ChevronUpIcon, { className: "size-4" })
247
+ }
248
+ );
249
+ }
250
+ function SelectScrollDownButton({
251
+ className,
252
+ ...props
253
+ }) {
254
+ return /* @__PURE__ */ jsx4(
255
+ SelectPrimitive.ScrollDownButton,
256
+ {
257
+ "data-slot": "select-scroll-down-button",
258
+ className: cn("flex cursor-default items-center justify-center py-1", className),
259
+ ...props,
260
+ children: /* @__PURE__ */ jsx4(ChevronDownIcon, { className: "size-4" })
261
+ }
262
+ );
263
+ }
264
+
265
+ // src/components/condition-builder/OperatorSelect.tsx
266
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
267
+ function OperatorSelect({ value, onChange, options, disabled }) {
268
+ return /* @__PURE__ */ jsxs2(Select, { value, onValueChange: onChange, disabled, children: [
269
+ /* @__PURE__ */ jsx5(SelectTrigger, { className: "h-8 font-mono text-xs", disabled, children: /* @__PURE__ */ jsx5(SelectValue, {}) }),
270
+ /* @__PURE__ */ jsx5(SelectContent, { children: options.map((opt) => /* @__PURE__ */ jsx5(SelectItem, { value: opt.value, children: opt.label }, opt.value)) })
271
+ ] });
272
+ }
273
+
274
+ // src/components/condition-builder/TagInput.tsx
275
+ import { useCallback, useState } from "react";
276
+
277
+ // src/components/ui/badge.tsx
278
+ import { cva } from "class-variance-authority";
279
+ import { Slot } from "radix-ui";
280
+ import { jsx as jsx6 } from "react/jsx-runtime";
281
+ var badgeVariants = cva(
282
+ "inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3",
283
+ {
284
+ variants: {
285
+ variant: {
286
+ default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
287
+ secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
288
+ destructive: "bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90",
289
+ outline: "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
290
+ ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
291
+ link: "text-primary underline-offset-4 [a&]:hover:underline"
292
+ }
293
+ },
294
+ defaultVariants: {
295
+ variant: "default"
296
+ }
297
+ }
298
+ );
299
+ function Badge({
300
+ className,
301
+ variant = "default",
302
+ asChild = false,
303
+ ...props
304
+ }) {
305
+ const Comp = asChild ? Slot.Root : "span";
306
+ return /* @__PURE__ */ jsx6(
307
+ Comp,
308
+ {
309
+ "data-slot": "badge",
310
+ "data-variant": variant,
311
+ className: cn(badgeVariants({ variant }), className),
312
+ ...props
313
+ }
314
+ );
315
+ }
316
+
317
+ // src/components/condition-builder/TagInput.tsx
318
+ import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
319
+ function TagInput({ value, onChange, placeholder }) {
320
+ const values = Array.isArray(value) ? value.filter(Boolean) : value ? [value] : [];
321
+ const [text, setText] = useState("");
322
+ const emit = useCallback(
323
+ (next2) => {
324
+ onChange(next2.length === 1 ? next2[0] : next2);
325
+ },
326
+ [onChange]
327
+ );
328
+ const addValues = useCallback(
329
+ (raw) => {
330
+ const cleaned = raw.map((s) => s.trim()).filter(Boolean);
331
+ const unique = cleaned.filter((s) => !values.includes(s));
332
+ if (unique.length > 0) {
333
+ emit([...values, ...unique]);
334
+ }
335
+ },
336
+ [values, emit]
337
+ );
338
+ const removeValue = useCallback(
339
+ (index) => {
340
+ const next2 = values.filter((_, i) => i !== index);
341
+ emit(next2.length === 0 ? [] : next2);
342
+ },
343
+ [values, emit]
344
+ );
345
+ const handleKeyDown = useCallback(
346
+ (e) => {
347
+ if (e.key === "Enter" || e.key === "Tab" && text.trim()) {
348
+ e.preventDefault();
349
+ addValues([text]);
350
+ setText("");
351
+ } else if (e.key === "Backspace" && text === "" && values.length > 0) {
352
+ removeValue(values.length - 1);
353
+ }
354
+ },
355
+ [text, values, addValues, removeValue]
356
+ );
357
+ const handlePaste = useCallback(
358
+ (e) => {
359
+ const pasted = e.clipboardData.getData("text");
360
+ if (pasted.includes("\n")) {
361
+ e.preventDefault();
362
+ addValues(pasted.split("\n"));
363
+ setText("");
364
+ }
365
+ },
366
+ [addValues]
367
+ );
368
+ return /* @__PURE__ */ jsxs3("div", { className: "border-input focus-within:border-ring focus-within:ring-ring/50 flex min-h-9 flex-1 flex-wrap items-center gap-1 rounded-md border px-2 py-1 focus-within:ring-[3px]", children: [
369
+ values.map((v, i) => /* @__PURE__ */ jsxs3(Badge, { variant: "outline", className: "bg-muted gap-1 font-mono text-xs", children: [
370
+ v,
371
+ /* @__PURE__ */ jsx7(
372
+ "button",
373
+ {
374
+ type: "button",
375
+ className: "text-muted-foreground hover:text-foreground ml-0.5 cursor-pointer leading-none",
376
+ onClick: () => removeValue(i),
377
+ "aria-label": `Remove ${v}`,
378
+ children: "\xD7"
379
+ }
380
+ )
381
+ ] }, `${v}-${i}`)),
382
+ /* @__PURE__ */ jsx7(
383
+ "input",
384
+ {
385
+ className: "min-w-[80px] flex-1 bg-transparent py-0.5 font-mono text-sm outline-none placeholder:text-muted-foreground",
386
+ value: text,
387
+ placeholder: values.length === 0 ? placeholder ?? "type and press Enter" : "",
388
+ onChange: (e) => setText(e.target.value),
389
+ onKeyDown: handleKeyDown,
390
+ onPaste: handlePaste
391
+ }
392
+ )
393
+ ] });
394
+ }
395
+
396
+ // src/components/condition-builder/condition-builders.ts
397
+ function buildAndCondition(conditions, id) {
398
+ return id ? { id, type: "and", conditions } : { type: "and", conditions };
399
+ }
400
+ function buildOrCondition(conditions, id) {
401
+ return id ? { id, type: "or", conditions } : { type: "or", conditions };
402
+ }
403
+ function buildCustomCondition(fields) {
404
+ return fields;
405
+ }
406
+
407
+ // src/components/condition-builder/StringConditionEditor.tsx
408
+ import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
409
+ var OP_OPTIONS = [
410
+ { value: "eq", label: "eq" },
411
+ { value: "neq", label: "neq" },
412
+ { value: "in", label: "in" },
413
+ { value: "nin", label: "nin" },
414
+ { value: "regex", label: "regex" }
415
+ ];
416
+ var meta = {
417
+ type: "string",
418
+ label: "String",
419
+ description: "Match a context key against string value(s)",
420
+ defaults: { type: "string", key: "", op: "eq", value: "" }
421
+ };
422
+ function StringConditionEditor({ condition, onChange }) {
423
+ const rec = useMemo(() => condition, [condition]);
424
+ const update = useCallback2(
425
+ (field, value) => {
426
+ onChange(buildCustomCondition({ ...rec, [field]: value, type: "string" }));
427
+ },
428
+ [rec, onChange]
429
+ );
430
+ const handleOpChange = useCallback2(
431
+ (newOp) => {
432
+ const isArrayOp = newOp === "in" || newOp === "nin";
433
+ const currentValue = rec.value;
434
+ const coercedValue = isArrayOp ? Array.isArray(currentValue) ? currentValue : currentValue ? [String(currentValue)] : [] : Array.isArray(currentValue) ? currentValue[0] ?? "" : currentValue;
435
+ onChange(buildCustomCondition({ ...rec, op: newOp, value: coercedValue, type: "string" }));
436
+ },
437
+ [rec, onChange]
438
+ );
439
+ const op = rec.op;
440
+ const isArray = op === "in" || op === "nin";
441
+ const isRegex = op === "regex";
442
+ return /* @__PURE__ */ jsxs4(ConditionRow, { children: [
443
+ /* @__PURE__ */ jsx8(
444
+ KeyInput,
445
+ {
446
+ value: String(rec.key ?? ""),
447
+ onChange: (v) => update("key", v),
448
+ placeholder: "e.g. userId"
449
+ }
450
+ ),
451
+ /* @__PURE__ */ jsx8(
452
+ OperatorSelect,
453
+ {
454
+ value: String(rec.op ?? "eq"),
455
+ onChange: handleOpChange,
456
+ options: OP_OPTIONS
457
+ }
458
+ ),
459
+ isArray ? /* @__PURE__ */ jsx8(
460
+ TagInput,
461
+ {
462
+ value: rec.value ?? "",
463
+ onChange: (v) => update("value", v),
464
+ placeholder: "e.g. user-123"
465
+ }
466
+ ) : isRegex ? /* @__PURE__ */ jsx8(
467
+ Input,
468
+ {
469
+ className: "h-8 font-mono text-sm",
470
+ value: String(rec.value ?? ""),
471
+ placeholder: "e.g. ^test.*$",
472
+ onChange: (e) => update("value", e.target.value)
473
+ }
474
+ ) : /* @__PURE__ */ jsx8(
475
+ Input,
476
+ {
477
+ className: "h-8 text-sm",
478
+ value: String(rec.value ?? ""),
479
+ placeholder: "e.g. user-123",
480
+ onChange: (e) => update("value", e.target.value)
481
+ }
482
+ )
483
+ ] });
484
+ }
485
+
486
+ // src/components/condition-builder/NumberConditionEditor.tsx
487
+ import { useCallback as useCallback4, useMemo as useMemo2 } from "react";
488
+
489
+ // src/components/condition-builder/NumberTagInput.tsx
490
+ import { useCallback as useCallback3, useState as useState2 } from "react";
491
+ import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
492
+ function NumberTagInput({ value, onChange, placeholder }) {
493
+ const values = Array.isArray(value) ? value : [value];
494
+ const [text, setText] = useState2("");
495
+ const emit = useCallback3(
496
+ (next2) => {
497
+ onChange(next2.length === 1 ? next2[0] : next2);
498
+ },
499
+ [onChange]
500
+ );
501
+ const addValues = useCallback3(
502
+ (raw) => {
503
+ const parsed = raw.map((s) => s.trim()).filter(Boolean).map(Number).filter((n) => !Number.isNaN(n));
504
+ const unique = parsed.filter((n) => !values.includes(n));
505
+ if (unique.length > 0) {
506
+ emit([...values, ...unique]);
507
+ }
508
+ },
509
+ [values, emit]
510
+ );
511
+ const removeValue = useCallback3(
512
+ (index) => {
513
+ const next2 = values.filter((_, i) => i !== index);
514
+ emit(next2.length === 0 ? [] : next2);
515
+ },
516
+ [values, emit]
517
+ );
518
+ const handleKeyDown = useCallback3(
519
+ (e) => {
520
+ if (e.key === "Enter" || e.key === "Tab" && text.trim()) {
521
+ e.preventDefault();
522
+ addValues([text]);
523
+ setText("");
524
+ } else if (e.key === "Backspace" && text === "" && values.length > 0) {
525
+ removeValue(values.length - 1);
526
+ }
527
+ },
528
+ [text, values, addValues, removeValue]
529
+ );
530
+ const handlePaste = useCallback3(
531
+ (e) => {
532
+ const pasted = e.clipboardData.getData("text");
533
+ if (pasted.includes("\n")) {
534
+ e.preventDefault();
535
+ addValues(pasted.split("\n"));
536
+ setText("");
537
+ }
538
+ },
539
+ [addValues]
540
+ );
541
+ return /* @__PURE__ */ jsxs5("div", { className: "border-input focus-within:border-ring focus-within:ring-ring/50 flex min-h-9 flex-1 flex-wrap items-center gap-1 rounded-md border px-2 py-1 focus-within:ring-[3px]", children: [
542
+ values.map((v, i) => /* @__PURE__ */ jsxs5(Badge, { variant: "outline", className: "bg-muted gap-1 font-mono text-xs", children: [
543
+ v,
544
+ /* @__PURE__ */ jsx9(
545
+ "button",
546
+ {
547
+ type: "button",
548
+ className: "text-muted-foreground hover:text-foreground ml-0.5 cursor-pointer leading-none",
549
+ onClick: () => removeValue(i),
550
+ "aria-label": `Remove ${v}`,
551
+ children: "\xD7"
552
+ }
553
+ )
554
+ ] }, `${v}-${i}`)),
555
+ /* @__PURE__ */ jsx9(
556
+ "input",
557
+ {
558
+ className: "min-w-[80px] flex-1 bg-transparent py-0.5 font-mono text-sm outline-none placeholder:text-muted-foreground",
559
+ type: "number",
560
+ value: text,
561
+ placeholder: values.length === 0 ? placeholder ?? "type and press Enter" : "",
562
+ onChange: (e) => setText(e.target.value),
563
+ onKeyDown: handleKeyDown,
564
+ onPaste: handlePaste
565
+ }
566
+ )
567
+ ] });
568
+ }
569
+
570
+ // src/components/condition-builder/NumberConditionEditor.tsx
571
+ import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
572
+ var OP_OPTIONS2 = [
573
+ { value: "eq", label: "eq" },
574
+ { value: "neq", label: "neq" },
575
+ { value: "gt", label: "gt" },
576
+ { value: "gte", label: "gte" },
577
+ { value: "lt", label: "lt" },
578
+ { value: "lte", label: "lte" },
579
+ { value: "in", label: "in" },
580
+ { value: "nin", label: "nin" }
581
+ ];
582
+ var meta2 = {
583
+ type: "number",
584
+ label: "Number",
585
+ description: "Compare a context key against a number or list of numbers",
586
+ defaults: { type: "number", key: "", op: "eq", value: 0 }
587
+ };
588
+ function NumberConditionEditor({ condition, onChange }) {
589
+ const rec = useMemo2(() => condition, [condition]);
590
+ const update = useCallback4(
591
+ (field, value) => {
592
+ onChange(buildCustomCondition({ ...rec, [field]: value, type: "number" }));
593
+ },
594
+ [rec, onChange]
595
+ );
596
+ const handleOpChange = useCallback4(
597
+ (newOp) => {
598
+ const isArrayOp = newOp === "in" || newOp === "nin";
599
+ const currentValue = rec.value;
600
+ const coercedValue = isArrayOp ? Array.isArray(currentValue) ? currentValue : currentValue !== void 0 && currentValue !== "" ? [Number(currentValue)] : [] : Array.isArray(currentValue) ? currentValue[0] ?? 0 : currentValue;
601
+ onChange(buildCustomCondition({ ...rec, op: newOp, value: coercedValue, type: "number" }));
602
+ },
603
+ [rec, onChange]
604
+ );
605
+ const op = rec.op;
606
+ const isArray = op === "in" || op === "nin";
607
+ return /* @__PURE__ */ jsxs6(ConditionRow, { children: [
608
+ /* @__PURE__ */ jsx10(
609
+ KeyInput,
610
+ {
611
+ value: String(rec.key ?? ""),
612
+ onChange: (v) => update("key", v),
613
+ placeholder: "e.g. score"
614
+ }
615
+ ),
616
+ /* @__PURE__ */ jsx10(
617
+ OperatorSelect,
618
+ {
619
+ value: String(rec.op ?? "eq"),
620
+ onChange: handleOpChange,
621
+ options: OP_OPTIONS2
622
+ }
623
+ ),
624
+ isArray ? /* @__PURE__ */ jsx10(
625
+ NumberTagInput,
626
+ {
627
+ value: rec.value ?? [],
628
+ onChange: (v) => update("value", v),
629
+ placeholder: "e.g. 200"
630
+ }
631
+ ) : /* @__PURE__ */ jsx10(
632
+ Input,
633
+ {
634
+ type: "number",
635
+ className: "h-8 font-mono text-sm",
636
+ value: rec.value !== void 0 ? String(rec.value) : "",
637
+ placeholder: "e.g. 100",
638
+ onChange: (e) => update("value", e.target.value === "" ? "" : Number(e.target.value))
639
+ }
640
+ )
641
+ ] });
642
+ }
643
+
644
+ // src/components/condition-builder/DatetimeConditionEditor.tsx
645
+ import { useCallback as useCallback5, useMemo as useMemo3 } from "react";
646
+
647
+ // src/components/common/DateTimeInput.tsx
648
+ import { useRef, useState as useState3 } from "react";
649
+ import { Calendar, Code } from "lucide-react";
650
+ import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
651
+ function toLocalDatetime(iso) {
652
+ if (!iso) return "";
653
+ const d = new Date(iso);
654
+ if (Number.isNaN(d.getTime())) return "";
655
+ const pad = (n) => String(n).padStart(2, "0");
656
+ const date = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
657
+ const time = `${pad(d.getHours())}:${pad(d.getMinutes())}`;
658
+ return `${date}T${time}`;
659
+ }
660
+ function fromLocalDatetime(local) {
661
+ if (!local) return "";
662
+ const d = new Date(local);
663
+ if (Number.isNaN(d.getTime())) return local;
664
+ return d.toISOString();
665
+ }
666
+ function DateTimeInput({ value, onChange }) {
667
+ const [rawValue, setRawValue] = useState3(value);
668
+ const [showRaw, setShowRaw] = useState3(false);
669
+ const prevValueRef = useRef(value);
670
+ if (prevValueRef.current !== value) {
671
+ prevValueRef.current = value;
672
+ setRawValue(value);
673
+ }
674
+ if (showRaw) {
675
+ return /* @__PURE__ */ jsxs7("div", { className: "flex gap-1", children: [
676
+ /* @__PURE__ */ jsx11(
677
+ Input,
678
+ {
679
+ className: "h-8 flex-1 font-mono text-xs",
680
+ value: rawValue,
681
+ placeholder: "ISO 8601 datetime",
682
+ onChange: (e) => {
683
+ setRawValue(e.target.value);
684
+ onChange(e.target.value);
685
+ }
686
+ }
687
+ ),
688
+ /* @__PURE__ */ jsx11(
689
+ "button",
690
+ {
691
+ type: "button",
692
+ className: "flex items-center justify-center w-8 h-8 text-muted-foreground hover:text-foreground",
693
+ "aria-label": "Switch to date picker",
694
+ onClick: () => setShowRaw(false),
695
+ children: /* @__PURE__ */ jsx11(Calendar, { className: "h-3.5 w-3.5" })
696
+ }
697
+ )
698
+ ] });
699
+ }
700
+ return /* @__PURE__ */ jsxs7("div", { className: "flex gap-1", children: [
701
+ /* @__PURE__ */ jsx11(
702
+ Input,
703
+ {
704
+ className: "h-8 flex-1 text-xs",
705
+ type: "datetime-local",
706
+ value: toLocalDatetime(value),
707
+ onChange: (e) => onChange(fromLocalDatetime(e.target.value))
708
+ }
709
+ ),
710
+ /* @__PURE__ */ jsx11(
711
+ "button",
712
+ {
713
+ type: "button",
714
+ className: "flex items-center justify-center w-8 h-8 text-muted-foreground hover:text-foreground",
715
+ "aria-label": "Switch to raw input",
716
+ onClick: () => {
717
+ setRawValue(value);
718
+ setShowRaw(true);
719
+ },
720
+ children: /* @__PURE__ */ jsx11(Code, { className: "h-3.5 w-3.5" })
721
+ }
722
+ )
723
+ ] });
724
+ }
725
+
726
+ // src/components/condition-builder/DatetimeConditionEditor.tsx
727
+ import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
728
+ var OP_OPTIONS3 = [
729
+ { value: "eq", label: "eq" },
730
+ { value: "gt", label: "gt" },
731
+ { value: "gte", label: "gte" },
732
+ { value: "lt", label: "lt" },
733
+ { value: "lte", label: "lte" }
734
+ ];
735
+ var meta3 = {
736
+ type: "datetime",
737
+ label: "Datetime",
738
+ description: "Compare a context key against a date/time",
739
+ defaults: { type: "datetime", key: "", op: "eq", value: (/* @__PURE__ */ new Date()).toISOString() }
740
+ };
741
+ function DatetimeConditionEditor({ condition, onChange }) {
742
+ const rec = useMemo3(() => condition, [condition]);
743
+ const update = useCallback5(
744
+ (field, value) => {
745
+ onChange(buildCustomCondition({ ...rec, [field]: value, type: "datetime" }));
746
+ },
747
+ [rec, onChange]
748
+ );
749
+ return /* @__PURE__ */ jsxs8(ConditionRow, { children: [
750
+ /* @__PURE__ */ jsx12(
751
+ KeyInput,
752
+ {
753
+ value: String(rec.key ?? ""),
754
+ onChange: (v) => update("key", v),
755
+ placeholder: "e.g. at"
756
+ }
757
+ ),
758
+ /* @__PURE__ */ jsx12(
759
+ OperatorSelect,
760
+ {
761
+ value: String(rec.op ?? "eq"),
762
+ onChange: (v) => update("op", v),
763
+ options: OP_OPTIONS3
764
+ }
765
+ ),
766
+ /* @__PURE__ */ jsx12(DateTimeInput, { value: String(rec.value ?? ""), onChange: (v) => update("value", v) })
767
+ ] });
768
+ }
769
+
770
+ // src/components/condition-builder/BoolConditionEditor.tsx
771
+ import { useCallback as useCallback6, useMemo as useMemo4 } from "react";
772
+ import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
773
+ var OP_OPTIONS4 = [{ value: "eq", label: "eq" }];
774
+ var meta4 = {
775
+ type: "bool",
776
+ label: "Boolean",
777
+ description: "Match a context key against a boolean",
778
+ defaults: { type: "bool", key: "", value: true }
779
+ };
780
+ function BoolConditionEditor({ condition, onChange }) {
781
+ const rec = useMemo4(() => condition, [condition]);
782
+ const update = useCallback6(
783
+ (field, value) => {
784
+ onChange(buildCustomCondition({ ...rec, [field]: value, type: "bool" }));
785
+ },
786
+ [rec, onChange]
787
+ );
788
+ return /* @__PURE__ */ jsxs9(ConditionRow, { children: [
789
+ /* @__PURE__ */ jsx13(
790
+ KeyInput,
791
+ {
792
+ value: String(rec.key ?? ""),
793
+ onChange: (v) => update("key", v),
794
+ placeholder: "e.g. isAdmin"
795
+ }
796
+ ),
797
+ /* @__PURE__ */ jsx13(OperatorSelect, { value: "eq", options: OP_OPTIONS4, disabled: true }),
798
+ /* @__PURE__ */ jsxs9(
799
+ Select,
800
+ {
801
+ value: String(rec.value ?? "true"),
802
+ onValueChange: (v) => update("value", v === "true"),
803
+ children: [
804
+ /* @__PURE__ */ jsx13(SelectTrigger, { className: "h-8 text-sm", children: /* @__PURE__ */ jsx13(SelectValue, {}) }),
805
+ /* @__PURE__ */ jsxs9(SelectContent, { children: [
806
+ /* @__PURE__ */ jsx13(SelectItem, { value: "true", children: "true" }),
807
+ /* @__PURE__ */ jsx13(SelectItem, { value: "false", children: "false" })
808
+ ] })
809
+ ]
810
+ }
811
+ )
812
+ ] });
813
+ }
814
+
815
+ // src/components/condition-builder/EnvConditionEditor.tsx
816
+ import { useCallback as useCallback7, useMemo as useMemo5 } from "react";
817
+ import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
818
+ var OP_OPTIONS5 = [{ value: "eq", label: "eq" }];
819
+ var meta5 = {
820
+ type: "env",
821
+ label: "Environment",
822
+ description: "Match the environment name",
823
+ defaults: { type: "env", value: "" }
824
+ };
825
+ function EnvConditionEditor({ condition, onChange }) {
826
+ const rec = useMemo5(() => condition, [condition]);
827
+ const handleChange = useCallback7(
828
+ (value) => {
829
+ onChange(buildCustomCondition({ ...rec, value, type: "env" }));
830
+ },
831
+ [rec, onChange]
832
+ );
833
+ return /* @__PURE__ */ jsxs10(ConditionRow, { children: [
834
+ /* @__PURE__ */ jsx14(KeyInput, { value: "env", disabled: true }),
835
+ /* @__PURE__ */ jsx14(OperatorSelect, { value: "eq", options: OP_OPTIONS5, disabled: true }),
836
+ /* @__PURE__ */ jsx14(
837
+ TagInput,
838
+ {
839
+ value: rec.value ?? "",
840
+ onChange: handleChange,
841
+ placeholder: "e.g. production"
842
+ }
843
+ )
844
+ ] });
845
+ }
846
+
847
+ // src/components/condition-builder/StartAtConditionEditor.tsx
848
+ import { useMemo as useMemo6 } from "react";
849
+ import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
850
+ var OP_OPTIONS6 = [{ value: "gte", label: "gte" }];
851
+ var meta6 = {
852
+ type: "startAt",
853
+ label: "Start At",
854
+ description: "Active after a specific date/time",
855
+ defaults: { type: "startAt", value: (/* @__PURE__ */ new Date()).toISOString() }
856
+ };
857
+ function StartAtConditionEditor({ condition, onChange }) {
858
+ const rec = useMemo6(() => condition, [condition]);
859
+ return /* @__PURE__ */ jsxs11(ConditionRow, { children: [
860
+ /* @__PURE__ */ jsx15(KeyInput, { value: "at", disabled: true }),
861
+ /* @__PURE__ */ jsx15(OperatorSelect, { value: "gte", options: OP_OPTIONS6, disabled: true }),
862
+ /* @__PURE__ */ jsx15(
863
+ DateTimeInput,
864
+ {
865
+ value: String(rec.value ?? ""),
866
+ onChange: (v) => onChange(buildCustomCondition({ ...rec, value: v, type: "startAt" }))
867
+ }
868
+ )
869
+ ] });
870
+ }
871
+
872
+ // src/components/condition-builder/EndAtConditionEditor.tsx
873
+ import { useMemo as useMemo7 } from "react";
874
+ import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
875
+ var OP_OPTIONS7 = [{ value: "lt", label: "lt" }];
876
+ var meta7 = {
877
+ type: "endAt",
878
+ label: "End At",
879
+ description: "Active before a specific date/time",
880
+ defaults: { type: "endAt", value: (/* @__PURE__ */ new Date()).toISOString() }
881
+ };
882
+ function EndAtConditionEditor({ condition, onChange }) {
883
+ const rec = useMemo7(() => condition, [condition]);
884
+ return /* @__PURE__ */ jsxs12(ConditionRow, { children: [
885
+ /* @__PURE__ */ jsx16(KeyInput, { value: "at", disabled: true }),
886
+ /* @__PURE__ */ jsx16(OperatorSelect, { value: "lt", options: OP_OPTIONS7, disabled: true }),
887
+ /* @__PURE__ */ jsx16(
888
+ DateTimeInput,
889
+ {
890
+ value: String(rec.value ?? ""),
891
+ onChange: (v) => onChange(buildCustomCondition({ ...rec, value: v, type: "endAt" }))
892
+ }
893
+ )
894
+ ] });
895
+ }
896
+
897
+ // src/components/condition-builder/condition-registry.ts
898
+ var BUILTIN_CONDITION_TYPES = [
899
+ meta,
900
+ meta2,
901
+ meta3,
902
+ meta4,
903
+ meta5,
904
+ meta6,
905
+ meta7
906
+ ];
907
+ var CONDITION_TYPE_MAP = new Map(BUILTIN_CONDITION_TYPES.map((m) => [m.type, m]));
908
+ function getConditionMeta(type) {
909
+ return CONDITION_TYPE_MAP.get(type);
910
+ }
911
+
912
+ // src/components/ui/button.tsx
913
+ import { cva as cva2 } from "class-variance-authority";
914
+ import { Slot as Slot2 } from "radix-ui";
915
+ import { jsx as jsx17 } from "react/jsx-runtime";
916
+ var buttonVariants = cva2(
917
+ "inline-flex shrink-0 items-center justify-center gap-2 rounded text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 hover:cursor-pointer",
918
+ {
919
+ variants: {
920
+ variant: {
921
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
922
+ destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
923
+ outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
924
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
925
+ ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
926
+ link: "text-primary underline-offset-4 hover:underline"
927
+ },
928
+ size: {
929
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
930
+ xs: "h-6 gap-1 rounded px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
931
+ sm: "h-8 gap-1.5 rounded px-3 has-[>svg]:px-2.5",
932
+ lg: "h-10 rounded px-6 has-[>svg]:px-4",
933
+ icon: "size-9",
934
+ "icon-xs": "size-6 rounded [&_svg:not([class*='size-'])]:size-3",
935
+ "icon-sm": "size-8",
936
+ "icon-lg": "size-10"
937
+ }
938
+ },
939
+ defaultVariants: {
940
+ variant: "default",
941
+ size: "default"
942
+ }
943
+ }
944
+ );
945
+ function Button({
946
+ className,
947
+ variant = "default",
948
+ size = "default",
949
+ asChild = false,
950
+ ref,
951
+ ...props
952
+ }) {
953
+ const Comp = asChild ? Slot2.Root : "button";
954
+ return /* @__PURE__ */ jsx17(
955
+ Comp,
956
+ {
957
+ ref,
958
+ "data-slot": "button",
959
+ "data-variant": variant,
960
+ "data-size": size,
961
+ className: cn(buttonVariants({ variant, size, className })),
962
+ ...props
963
+ }
964
+ );
965
+ }
966
+
967
+ // src/components/ui/separator.tsx
968
+ import { Separator as SeparatorPrimitive } from "radix-ui";
969
+ import { jsx as jsx18 } from "react/jsx-runtime";
970
+ function Separator({
971
+ className,
972
+ orientation = "horizontal",
973
+ decorative = true,
974
+ ...props
975
+ }) {
976
+ return /* @__PURE__ */ jsx18(
977
+ SeparatorPrimitive.Root,
978
+ {
979
+ "data-slot": "separator",
980
+ decorative,
981
+ orientation,
982
+ className: cn(
983
+ "shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
984
+ className
985
+ ),
986
+ ...props
987
+ }
988
+ );
989
+ }
990
+
991
+ // src/components/ui/scroll-area.tsx
992
+ import { ScrollArea as ScrollAreaPrimitive } from "radix-ui";
993
+ import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
994
+ function ScrollArea({
995
+ className,
996
+ children,
997
+ ...props
998
+ }) {
999
+ return /* @__PURE__ */ jsxs13(
1000
+ ScrollAreaPrimitive.Root,
1001
+ {
1002
+ "data-slot": "scroll-area",
1003
+ className: cn("relative", className),
1004
+ ...props,
1005
+ children: [
1006
+ /* @__PURE__ */ jsx19(
1007
+ ScrollAreaPrimitive.Viewport,
1008
+ {
1009
+ "data-slot": "scroll-area-viewport",
1010
+ className: "size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1",
1011
+ children
1012
+ }
1013
+ ),
1014
+ /* @__PURE__ */ jsx19(ScrollBar, {}),
1015
+ /* @__PURE__ */ jsx19(ScrollAreaPrimitive.Corner, {})
1016
+ ]
1017
+ }
1018
+ );
1019
+ }
1020
+ function ScrollBar({
1021
+ className,
1022
+ orientation = "vertical",
1023
+ ...props
1024
+ }) {
1025
+ return /* @__PURE__ */ jsx19(
1026
+ ScrollAreaPrimitive.ScrollAreaScrollbar,
1027
+ {
1028
+ "data-slot": "scroll-area-scrollbar",
1029
+ orientation,
1030
+ className: cn(
1031
+ "flex touch-none p-px transition-colors select-none",
1032
+ orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent",
1033
+ orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent",
1034
+ className
1035
+ ),
1036
+ ...props,
1037
+ children: /* @__PURE__ */ jsx19(
1038
+ ScrollAreaPrimitive.ScrollAreaThumb,
1039
+ {
1040
+ "data-slot": "scroll-area-thumb",
1041
+ className: "relative flex-1 rounded-full bg-border"
1042
+ }
1043
+ )
1044
+ }
1045
+ );
1046
+ }
1047
+
1048
+ // src/components/ui/dialog.tsx
1049
+ import { XIcon } from "lucide-react";
1050
+ import { Dialog as DialogPrimitive } from "radix-ui";
1051
+ import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
1052
+ function Dialog({ ...props }) {
1053
+ return /* @__PURE__ */ jsx20(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
1054
+ }
1055
+ function DialogTrigger({ ...props }) {
1056
+ return /* @__PURE__ */ jsx20(DialogPrimitive.Trigger, { "data-slot": "dialog-trigger", ...props });
1057
+ }
1058
+ function DialogPortal({ ...props }) {
1059
+ return /* @__PURE__ */ jsx20(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
1060
+ }
1061
+ function DialogClose({ ...props }) {
1062
+ return /* @__PURE__ */ jsx20(DialogPrimitive.Close, { "data-slot": "dialog-close", ...props });
1063
+ }
1064
+ function DialogOverlay({
1065
+ className,
1066
+ ...props
1067
+ }) {
1068
+ return /* @__PURE__ */ jsx20(
1069
+ DialogPrimitive.Overlay,
1070
+ {
1071
+ "data-slot": "dialog-overlay",
1072
+ className: cn(
1073
+ "fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
1074
+ className
1075
+ ),
1076
+ ...props
1077
+ }
1078
+ );
1079
+ }
1080
+ function DialogContent({
1081
+ className,
1082
+ children,
1083
+ showCloseButton = true,
1084
+ ...props
1085
+ }) {
1086
+ return /* @__PURE__ */ jsxs14(DialogPortal, { "data-slot": "dialog-portal", children: [
1087
+ /* @__PURE__ */ jsx20(DialogOverlay, {}),
1088
+ /* @__PURE__ */ jsxs14(
1089
+ DialogPrimitive.Content,
1090
+ {
1091
+ "data-slot": "dialog-content",
1092
+ className: cn(
1093
+ "fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg",
1094
+ className
1095
+ ),
1096
+ ...props,
1097
+ children: [
1098
+ children,
1099
+ showCloseButton && /* @__PURE__ */ jsxs14(
1100
+ DialogPrimitive.Close,
1101
+ {
1102
+ "data-slot": "dialog-close",
1103
+ className: "absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
1104
+ children: [
1105
+ /* @__PURE__ */ jsx20(XIcon, {}),
1106
+ /* @__PURE__ */ jsx20("span", { className: "sr-only", children: "Close" })
1107
+ ]
1108
+ }
1109
+ )
1110
+ ]
1111
+ }
1112
+ )
1113
+ ] });
1114
+ }
1115
+ function DialogHeader({ className, ...props }) {
1116
+ return /* @__PURE__ */ jsx20(
1117
+ "div",
1118
+ {
1119
+ "data-slot": "dialog-header",
1120
+ className: cn("flex flex-col gap-2 text-center sm:text-left", className),
1121
+ ...props
1122
+ }
1123
+ );
1124
+ }
1125
+ function DialogFooter({
1126
+ className,
1127
+ showCloseButton = false,
1128
+ children,
1129
+ ...props
1130
+ }) {
1131
+ return /* @__PURE__ */ jsxs14(
1132
+ "div",
1133
+ {
1134
+ "data-slot": "dialog-footer",
1135
+ className: cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className),
1136
+ ...props,
1137
+ children: [
1138
+ children,
1139
+ showCloseButton && /* @__PURE__ */ jsx20(DialogPrimitive.Close, { asChild: true, children: /* @__PURE__ */ jsx20(Button, { variant: "outline", children: "Close" }) })
1140
+ ]
1141
+ }
1142
+ );
1143
+ }
1144
+ function DialogTitle({ className, ...props }) {
1145
+ return /* @__PURE__ */ jsx20(
1146
+ DialogPrimitive.Title,
1147
+ {
1148
+ "data-slot": "dialog-title",
1149
+ className: cn("text-lg leading-none font-semibold", className),
1150
+ ...props
1151
+ }
1152
+ );
1153
+ }
1154
+ function DialogDescription({
1155
+ className,
1156
+ ...props
1157
+ }) {
1158
+ return /* @__PURE__ */ jsx20(
1159
+ DialogPrimitive.Description,
1160
+ {
1161
+ "data-slot": "dialog-description",
1162
+ className: cn("text-sm text-muted-foreground", className),
1163
+ ...props
1164
+ }
1165
+ );
1166
+ }
1167
+
1168
+ // src/components/ui/dropdown-menu.tsx
1169
+ import { CheckIcon as CheckIcon2, ChevronRightIcon, CircleIcon } from "lucide-react";
1170
+ import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui";
1171
+ import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
1172
+ function DropdownMenu({ ...props }) {
1173
+ return /* @__PURE__ */ jsx21(DropdownMenuPrimitive.Root, { "data-slot": "dropdown-menu", ...props });
1174
+ }
1175
+ function DropdownMenuTrigger({
1176
+ ...props
1177
+ }) {
1178
+ return /* @__PURE__ */ jsx21(DropdownMenuPrimitive.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
1179
+ }
1180
+ function DropdownMenuContent({
1181
+ className,
1182
+ sideOffset = 4,
1183
+ ...props
1184
+ }) {
1185
+ return /* @__PURE__ */ jsx21(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx21(
1186
+ DropdownMenuPrimitive.Content,
1187
+ {
1188
+ "data-slot": "dropdown-menu-content",
1189
+ sideOffset,
1190
+ className: cn(
1191
+ "z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
1192
+ className
1193
+ ),
1194
+ ...props
1195
+ }
1196
+ ) });
1197
+ }
1198
+ function DropdownMenuItem({
1199
+ className,
1200
+ inset,
1201
+ variant = "default",
1202
+ ...props
1203
+ }) {
1204
+ return /* @__PURE__ */ jsx21(
1205
+ DropdownMenuPrimitive.Item,
1206
+ {
1207
+ "data-slot": "dropdown-menu-item",
1208
+ "data-inset": inset,
1209
+ "data-variant": variant,
1210
+ className: cn(
1211
+ "relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:text-destructive!",
1212
+ className
1213
+ ),
1214
+ ...props
1215
+ }
1216
+ );
1217
+ }
1218
+ function DropdownMenuSeparator({
1219
+ className,
1220
+ ...props
1221
+ }) {
1222
+ return /* @__PURE__ */ jsx21(
1223
+ DropdownMenuPrimitive.Separator,
1224
+ {
1225
+ "data-slot": "dropdown-menu-separator",
1226
+ className: cn("-mx-1 my-1 h-px bg-border", className),
1227
+ ...props
1228
+ }
1229
+ );
1230
+ }
1231
+
1232
+ // src/components/ui/label.tsx
1233
+ import { Label as LabelPrimitive } from "radix-ui";
1234
+ import { jsx as jsx22 } from "react/jsx-runtime";
1235
+ function Label({ className, ...props }) {
1236
+ return /* @__PURE__ */ jsx22(
1237
+ LabelPrimitive.Root,
1238
+ {
1239
+ "data-slot": "label",
1240
+ className: cn(
1241
+ "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
1242
+ className
1243
+ ),
1244
+ ...props
1245
+ }
1246
+ );
1247
+ }
1248
+
1249
+ // src/components/ui/switch.tsx
1250
+ import { Switch as SwitchPrimitive } from "radix-ui";
1251
+ import { jsx as jsx23 } from "react/jsx-runtime";
1252
+ function Switch({ className, ...props }) {
1253
+ return /* @__PURE__ */ jsx23(
1254
+ SwitchPrimitive.Root,
1255
+ {
1256
+ "data-slot": "switch",
1257
+ className: cn(
1258
+ "peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-xs transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
1259
+ className
1260
+ ),
1261
+ ...props,
1262
+ children: /* @__PURE__ */ jsx23(
1263
+ SwitchPrimitive.Thumb,
1264
+ {
1265
+ className: cn(
1266
+ "pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
1267
+ )
1268
+ }
1269
+ )
1270
+ }
1271
+ );
1272
+ }
1273
+
1274
+ // src/components/ui/textarea.tsx
1275
+ import { jsx as jsx24 } from "react/jsx-runtime";
1276
+ function Textarea({ className, ...props }) {
1277
+ return /* @__PURE__ */ jsx24(
1278
+ "textarea",
1279
+ {
1280
+ "data-slot": "textarea",
1281
+ className: cn(
1282
+ "flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:ring-destructive/40",
1283
+ className
1284
+ ),
1285
+ ...props
1286
+ }
1287
+ );
1288
+ }
1289
+
1290
+ // src/components/ui/popover.tsx
1291
+ import { Popover as PopoverPrimitive } from "radix-ui";
1292
+ import { jsx as jsx25 } from "react/jsx-runtime";
1293
+ function Popover({ ...props }) {
1294
+ return /* @__PURE__ */ jsx25(PopoverPrimitive.Root, { "data-slot": "popover", ...props });
1295
+ }
1296
+ function PopoverTrigger({ ...props }) {
1297
+ return /* @__PURE__ */ jsx25(PopoverPrimitive.Trigger, { "data-slot": "popover-trigger", ...props });
1298
+ }
1299
+ function PopoverContent({
1300
+ className,
1301
+ align = "center",
1302
+ sideOffset = 4,
1303
+ ...props
1304
+ }) {
1305
+ return /* @__PURE__ */ jsx25(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx25(
1306
+ PopoverPrimitive.Content,
1307
+ {
1308
+ "data-slot": "popover-content",
1309
+ align,
1310
+ sideOffset,
1311
+ className: cn(
1312
+ "z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
1313
+ className
1314
+ ),
1315
+ ...props
1316
+ }
1317
+ ) });
1318
+ }
1319
+
1320
+ // src/components/ui/tabs.tsx
1321
+ import { Tabs as TabsPrimitive } from "radix-ui";
1322
+ import { jsx as jsx26 } from "react/jsx-runtime";
1323
+ function Tabs({ ...props }) {
1324
+ return /* @__PURE__ */ jsx26(TabsPrimitive.Root, { "data-slot": "tabs", ...props });
1325
+ }
1326
+ function TabsList({ className, ...props }) {
1327
+ return /* @__PURE__ */ jsx26(
1328
+ TabsPrimitive.List,
1329
+ {
1330
+ "data-slot": "tabs-list",
1331
+ className: cn(
1332
+ "inline-flex items-center justify-center gap-1 rounded-lg bg-muted p-1 text-muted-foreground data-[orientation=vertical]:flex-col data-[orientation=vertical]:items-stretch data-[orientation=vertical]:justify-start data-[orientation=vertical]:gap-0 data-[orientation=vertical]:rounded-none data-[orientation=vertical]:bg-transparent data-[orientation=vertical]:p-0",
1333
+ className
1334
+ ),
1335
+ ...props
1336
+ }
1337
+ );
1338
+ }
1339
+ function TabsTrigger({ className, ...props }) {
1340
+ return /* @__PURE__ */ jsx26(
1341
+ TabsPrimitive.Trigger,
1342
+ {
1343
+ "data-slot": "tabs-trigger",
1344
+ className: cn(
1345
+ "inline-flex items-center justify-center gap-2 rounded-md px-3 py-1.5 text-sm font-medium whitespace-nowrap transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm data-[orientation=vertical]:justify-start data-[orientation=vertical]:rounded-none data-[orientation=vertical]:border-l-2 data-[orientation=vertical]:border-l-transparent data-[orientation=vertical]:px-3 data-[orientation=vertical]:py-2.5 data-[orientation=vertical]:text-sm data-[orientation=vertical]:font-normal data-[orientation=vertical]:text-muted-foreground data-[orientation=vertical]:shadow-none data-[orientation=vertical]:hover:bg-muted data-[orientation=vertical]:data-[state=active]:border-l-primary data-[orientation=vertical]:data-[state=active]:bg-accent data-[orientation=vertical]:data-[state=active]:font-medium data-[orientation=vertical]:data-[state=active]:text-accent-foreground",
1346
+ className
1347
+ ),
1348
+ ...props
1349
+ }
1350
+ );
1351
+ }
1352
+ function TabsContent({ className, ...props }) {
1353
+ return /* @__PURE__ */ jsx26(
1354
+ TabsPrimitive.Content,
1355
+ {
1356
+ "data-slot": "tabs-content",
1357
+ className: cn(
1358
+ "flex-1 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
1359
+ className
1360
+ ),
1361
+ ...props
1362
+ }
1363
+ );
1364
+ }
1365
+
1366
+ // src/components/common/ValueInput.tsx
1367
+ import { useRef as useRef2, useState as useState4 } from "react";
1368
+ import { Fragment, jsx as jsx27, jsxs as jsxs16 } from "react/jsx-runtime";
1369
+ function detectType(value) {
1370
+ if (typeof value === "boolean") return "boolean";
1371
+ if (typeof value === "number") return "number";
1372
+ if (typeof value === "object" && value !== null) return "json";
1373
+ return "string";
1374
+ }
1375
+ function ValueInput({ value, onChange, placeholder }) {
1376
+ const [type, setType] = useState4(() => detectType(value));
1377
+ const [jsonText, setJsonText] = useState4(
1378
+ () => type === "json" ? JSON.stringify(value, null, 2) : ""
1379
+ );
1380
+ const [jsonError, setJsonError] = useState4(null);
1381
+ const prevValueRef = useRef2(value);
1382
+ if (prevValueRef.current !== value) {
1383
+ prevValueRef.current = value;
1384
+ const newType = detectType(value);
1385
+ if (newType !== type) {
1386
+ setType(newType);
1387
+ }
1388
+ if (newType === "json") {
1389
+ try {
1390
+ setJsonText(JSON.stringify(value, null, 2));
1391
+ setJsonError(null);
1392
+ } catch {
1393
+ }
1394
+ }
1395
+ }
1396
+ function handleTypeChange(newType) {
1397
+ const t = newType;
1398
+ setType(t);
1399
+ setJsonError(null);
1400
+ switch (t) {
1401
+ case "string":
1402
+ onChange(String(value ?? ""));
1403
+ break;
1404
+ case "number":
1405
+ onChange(Number(value) || 0);
1406
+ break;
1407
+ case "boolean":
1408
+ onChange(Boolean(value));
1409
+ break;
1410
+ case "json":
1411
+ try {
1412
+ const text = JSON.stringify(value, null, 2);
1413
+ setJsonText(text);
1414
+ onChange(value);
1415
+ } catch {
1416
+ setJsonText("{}");
1417
+ onChange({});
1418
+ }
1419
+ break;
1420
+ }
1421
+ }
1422
+ function handleJsonBlur() {
1423
+ try {
1424
+ const parsed = JSON.parse(jsonText);
1425
+ setJsonError(null);
1426
+ onChange(parsed);
1427
+ } catch {
1428
+ setJsonError("Invalid JSON");
1429
+ }
1430
+ }
1431
+ function handlePrettify() {
1432
+ try {
1433
+ const parsed = JSON.parse(jsonText);
1434
+ setJsonText(JSON.stringify(parsed, null, 2));
1435
+ setJsonError(null);
1436
+ } catch {
1437
+ setJsonError("Invalid JSON");
1438
+ }
1439
+ }
1440
+ return /* @__PURE__ */ jsxs16("div", { className: "flex items-start gap-2", children: [
1441
+ /* @__PURE__ */ jsxs16(Select, { value: type, onValueChange: handleTypeChange, children: [
1442
+ /* @__PURE__ */ jsx27(SelectTrigger, { className: "h-9 w-[110px] shrink-0 text-sm", children: /* @__PURE__ */ jsx27(SelectValue, {}) }),
1443
+ /* @__PURE__ */ jsxs16(SelectContent, { children: [
1444
+ /* @__PURE__ */ jsx27(SelectItem, { value: "string", children: "String" }),
1445
+ /* @__PURE__ */ jsx27(SelectItem, { value: "number", children: "Number" }),
1446
+ /* @__PURE__ */ jsx27(SelectItem, { value: "boolean", children: "Boolean" }),
1447
+ /* @__PURE__ */ jsx27(SelectItem, { value: "json", children: "JSON" })
1448
+ ] })
1449
+ ] }),
1450
+ /* @__PURE__ */ jsxs16("div", { className: "flex-1", children: [
1451
+ type === "string" && /* @__PURE__ */ jsx27(
1452
+ Input,
1453
+ {
1454
+ className: "h-9 font-mono text-sm",
1455
+ value: String(value ?? ""),
1456
+ placeholder,
1457
+ onChange: (e) => onChange(e.target.value)
1458
+ }
1459
+ ),
1460
+ type === "number" && /* @__PURE__ */ jsx27(
1461
+ Input,
1462
+ {
1463
+ className: "h-9 font-mono text-sm",
1464
+ type: "number",
1465
+ value: String(value ?? 0),
1466
+ onChange: (e) => onChange(Number(e.target.value))
1467
+ }
1468
+ ),
1469
+ type === "boolean" && /* @__PURE__ */ jsxs16(Select, { value: String(Boolean(value)), onValueChange: (v) => onChange(v === "true"), children: [
1470
+ /* @__PURE__ */ jsx27(SelectTrigger, { className: "h-9 text-sm", children: /* @__PURE__ */ jsx27(SelectValue, {}) }),
1471
+ /* @__PURE__ */ jsxs16(SelectContent, { children: [
1472
+ /* @__PURE__ */ jsx27(SelectItem, { value: "true", children: "true" }),
1473
+ /* @__PURE__ */ jsx27(SelectItem, { value: "false", children: "false" })
1474
+ ] })
1475
+ ] }),
1476
+ type === "json" && /* @__PURE__ */ jsxs16(Fragment, { children: [
1477
+ /* @__PURE__ */ jsx27(
1478
+ Textarea,
1479
+ {
1480
+ className: "font-mono text-sm",
1481
+ rows: 4,
1482
+ value: jsonText,
1483
+ onChange: (e) => setJsonText(e.target.value),
1484
+ onBlur: handleJsonBlur
1485
+ }
1486
+ ),
1487
+ /* @__PURE__ */ jsxs16("div", { className: "mt-1 flex items-center gap-2", children: [
1488
+ jsonError && /* @__PURE__ */ jsx27("p", { className: "text-xs text-destructive", children: jsonError }),
1489
+ /* @__PURE__ */ jsx27(
1490
+ Button,
1491
+ {
1492
+ type: "button",
1493
+ variant: "ghost",
1494
+ size: "sm",
1495
+ className: "ml-auto h-6 px-2 text-xs",
1496
+ onClick: handlePrettify,
1497
+ children: "Prettify"
1498
+ }
1499
+ )
1500
+ ] })
1501
+ ] })
1502
+ ] })
1503
+ ] });
1504
+ }
1505
+
1506
+ // src/components/common/ValidationMessage.tsx
1507
+ import { jsx as jsx28, jsxs as jsxs17 } from "react/jsx-runtime";
1508
+ function ValidationMessage({ errors }) {
1509
+ if (!errors || errors.length === 0) return null;
1510
+ return /* @__PURE__ */ jsx28("div", { className: "space-y-1", children: errors.map((err, i) => /* @__PURE__ */ jsxs17("p", { className: "text-xs text-destructive", children: [
1511
+ err.path.length > 0 && /* @__PURE__ */ jsxs17("span", { className: "font-mono text-destructive/70", children: [
1512
+ err.path.join("."),
1513
+ " "
1514
+ ] }),
1515
+ err.message
1516
+ ] }, i)) });
1517
+ }
1518
+
1519
+ // src/components/common/ThemeToggle.tsx
1520
+ import { Moon, Sun, Monitor } from "lucide-react";
1521
+ import { jsx as jsx29 } from "react/jsx-runtime";
1522
+ var icons = {
1523
+ light: Sun,
1524
+ dark: Moon,
1525
+ system: Monitor
1526
+ };
1527
+ var next = {
1528
+ light: "dark",
1529
+ dark: "system",
1530
+ system: "light"
1531
+ };
1532
+ function ThemeToggle({ theme, onToggle }) {
1533
+ const Icon = icons[theme];
1534
+ return /* @__PURE__ */ jsx29(
1535
+ Button,
1536
+ {
1537
+ variant: "ghost",
1538
+ size: "icon",
1539
+ onClick: () => onToggle(next[theme]),
1540
+ "aria-label": `Switch to ${next[theme]} theme`,
1541
+ children: /* @__PURE__ */ jsx29(Icon, { className: "h-4 w-4" })
1542
+ }
1543
+ );
1544
+ }
1545
+
1546
+ // src/components/common/ErrorBoundary.tsx
1547
+ import { Component } from "react";
1548
+ import { jsx as jsx30, jsxs as jsxs18 } from "react/jsx-runtime";
1549
+ function defaultOnError(error, info) {
1550
+ console.error("ErrorBoundary caught an error:", error, info);
1551
+ }
1552
+ var ErrorBoundary = class extends Component {
1553
+ constructor(props) {
1554
+ super(props);
1555
+ this.state = { error: null };
1556
+ }
1557
+ static getDerivedStateFromError(error) {
1558
+ return { error };
1559
+ }
1560
+ componentDidCatch(error, info) {
1561
+ const onError = this.props.onError ?? defaultOnError;
1562
+ onError(error, info);
1563
+ }
1564
+ handleRetry = () => {
1565
+ this.setState({ error: null });
1566
+ };
1567
+ render() {
1568
+ if (this.state.error) {
1569
+ if (this.props.fallback) {
1570
+ return this.props.fallback;
1571
+ }
1572
+ return /* @__PURE__ */ jsxs18(
1573
+ "div",
1574
+ {
1575
+ role: "alert",
1576
+ className: "flex flex-col items-center justify-center gap-3 p-6 text-center",
1577
+ children: [
1578
+ /* @__PURE__ */ jsx30("p", { className: "text-sm font-medium text-destructive", children: "Something went wrong" }),
1579
+ /* @__PURE__ */ jsx30("p", { className: "text-xs text-muted-foreground", children: this.state.error.message }),
1580
+ /* @__PURE__ */ jsx30(
1581
+ "button",
1582
+ {
1583
+ type: "button",
1584
+ className: "rounded-md border border-border bg-background px-3 py-1.5 text-sm font-medium hover:bg-accent",
1585
+ onClick: this.handleRetry,
1586
+ children: "Try again"
1587
+ }
1588
+ )
1589
+ ]
1590
+ }
1591
+ );
1592
+ }
1593
+ return this.props.children;
1594
+ }
1595
+ };
1596
+
1597
+ // src/components/ui/alert-dialog.tsx
1598
+ import { AlertDialog as AlertDialogPrimitive } from "radix-ui";
1599
+ import { jsx as jsx31, jsxs as jsxs19 } from "react/jsx-runtime";
1600
+ function AlertDialog({ ...props }) {
1601
+ return /* @__PURE__ */ jsx31(AlertDialogPrimitive.Root, { "data-slot": "alert-dialog", ...props });
1602
+ }
1603
+ function AlertDialogTrigger({
1604
+ ...props
1605
+ }) {
1606
+ return /* @__PURE__ */ jsx31(AlertDialogPrimitive.Trigger, { "data-slot": "alert-dialog-trigger", ...props });
1607
+ }
1608
+ function AlertDialogPortal({ ...props }) {
1609
+ return /* @__PURE__ */ jsx31(AlertDialogPrimitive.Portal, { "data-slot": "alert-dialog-portal", ...props });
1610
+ }
1611
+ function AlertDialogOverlay({
1612
+ className,
1613
+ ...props
1614
+ }) {
1615
+ return /* @__PURE__ */ jsx31(
1616
+ AlertDialogPrimitive.Overlay,
1617
+ {
1618
+ "data-slot": "alert-dialog-overlay",
1619
+ className: cn(
1620
+ "fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
1621
+ className
1622
+ ),
1623
+ ...props
1624
+ }
1625
+ );
1626
+ }
1627
+ function AlertDialogContent({
1628
+ className,
1629
+ ...props
1630
+ }) {
1631
+ return /* @__PURE__ */ jsxs19(AlertDialogPortal, { children: [
1632
+ /* @__PURE__ */ jsx31(AlertDialogOverlay, {}),
1633
+ /* @__PURE__ */ jsx31(
1634
+ AlertDialogPrimitive.Content,
1635
+ {
1636
+ "data-slot": "alert-dialog-content",
1637
+ className: cn(
1638
+ "fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg",
1639
+ className
1640
+ ),
1641
+ ...props
1642
+ }
1643
+ )
1644
+ ] });
1645
+ }
1646
+ function AlertDialogHeader({ className, ...props }) {
1647
+ return /* @__PURE__ */ jsx31(
1648
+ "div",
1649
+ {
1650
+ "data-slot": "alert-dialog-header",
1651
+ className: cn("flex flex-col gap-2 text-center sm:text-left", className),
1652
+ ...props
1653
+ }
1654
+ );
1655
+ }
1656
+ function AlertDialogFooter({ className, ...props }) {
1657
+ return /* @__PURE__ */ jsx31(
1658
+ "div",
1659
+ {
1660
+ "data-slot": "alert-dialog-footer",
1661
+ className: cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className),
1662
+ ...props
1663
+ }
1664
+ );
1665
+ }
1666
+ function AlertDialogTitle({
1667
+ className,
1668
+ ...props
1669
+ }) {
1670
+ return /* @__PURE__ */ jsx31(
1671
+ AlertDialogPrimitive.Title,
1672
+ {
1673
+ "data-slot": "alert-dialog-title",
1674
+ className: cn("text-lg font-semibold", className),
1675
+ ...props
1676
+ }
1677
+ );
1678
+ }
1679
+ function AlertDialogDescription({
1680
+ className,
1681
+ ...props
1682
+ }) {
1683
+ return /* @__PURE__ */ jsx31(
1684
+ AlertDialogPrimitive.Description,
1685
+ {
1686
+ "data-slot": "alert-dialog-description",
1687
+ className: cn("text-sm text-muted-foreground", className),
1688
+ ...props
1689
+ }
1690
+ );
1691
+ }
1692
+ function AlertDialogAction({
1693
+ className,
1694
+ ...props
1695
+ }) {
1696
+ return /* @__PURE__ */ jsx31(AlertDialogPrimitive.Action, { className: cn(buttonVariants(), className), ...props });
1697
+ }
1698
+ function AlertDialogCancel({
1699
+ className,
1700
+ ...props
1701
+ }) {
1702
+ return /* @__PURE__ */ jsx31(
1703
+ AlertDialogPrimitive.Cancel,
1704
+ {
1705
+ className: cn(buttonVariants({ variant: "outline" }), className),
1706
+ ...props
1707
+ }
1708
+ );
1709
+ }
1710
+
1711
+ // src/components/common/ConfirmDialog.tsx
1712
+ import { jsx as jsx32, jsxs as jsxs20 } from "react/jsx-runtime";
1713
+ function ConfirmDialog({
1714
+ title,
1715
+ description,
1716
+ actionLabel,
1717
+ onConfirm,
1718
+ open,
1719
+ onOpenChange,
1720
+ children
1721
+ }) {
1722
+ return /* @__PURE__ */ jsxs20(AlertDialog, { open, onOpenChange, children: [
1723
+ /* @__PURE__ */ jsx32(AlertDialogTrigger, { asChild: true, children }),
1724
+ /* @__PURE__ */ jsxs20(AlertDialogContent, { children: [
1725
+ /* @__PURE__ */ jsxs20(AlertDialogHeader, { children: [
1726
+ /* @__PURE__ */ jsx32(AlertDialogTitle, { children: title }),
1727
+ /* @__PURE__ */ jsx32(AlertDialogDescription, { children: description })
1728
+ ] }),
1729
+ /* @__PURE__ */ jsxs20(AlertDialogFooter, { children: [
1730
+ /* @__PURE__ */ jsx32(AlertDialogCancel, { children: "Cancel" }),
1731
+ /* @__PURE__ */ jsx32(
1732
+ AlertDialogAction,
1733
+ {
1734
+ className: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
1735
+ onClick: onConfirm,
1736
+ children: actionLabel
1737
+ }
1738
+ )
1739
+ ] })
1740
+ ] })
1741
+ ] });
1742
+ }
1743
+
1744
+ // src/components/condition-builder/ConditionBuilder.tsx
1745
+ import { Fragment as Fragment5 } from "react";
1746
+
1747
+ // src/components/condition-builder/ConditionBlock.tsx
1748
+ import { memo as memo2 } from "react";
1749
+ import { isAndCondition, isOrCondition } from "showwhat";
1750
+ import { X as X2 } from "lucide-react";
1751
+
1752
+ // src/components/condition-builder/CustomConditionEditor.tsx
1753
+ import { useCallback as useCallback8, useRef as useRef3, useState as useState5 } from "react";
1754
+ import { jsx as jsx33, jsxs as jsxs21 } from "react/jsx-runtime";
1755
+ function extractArgs(condition) {
1756
+ return Object.fromEntries(Object.entries(condition).filter(([k]) => k !== "type" && k !== "id"));
1757
+ }
1758
+ function argsToText(args) {
1759
+ if (Object.keys(args).length === 0) return "";
1760
+ return JSON.stringify(args, null, 2);
1761
+ }
1762
+ function CustomConditionEditor({ condition, onChange }) {
1763
+ const rec = condition;
1764
+ const [text, setText] = useState5(() => argsToText(extractArgs(rec)));
1765
+ const [jsonError, setJsonError] = useState5(null);
1766
+ const focusedRef = useRef3(false);
1767
+ const prevConditionRef = useRef3(condition);
1768
+ if (prevConditionRef.current !== condition) {
1769
+ prevConditionRef.current = condition;
1770
+ if (!focusedRef.current) {
1771
+ const derived = argsToText(extractArgs(rec));
1772
+ if (derived !== text) {
1773
+ setText(derived);
1774
+ setJsonError(null);
1775
+ }
1776
+ }
1777
+ }
1778
+ const handleArgsBlur = useCallback8(() => {
1779
+ focusedRef.current = false;
1780
+ const trimmed = text.trim();
1781
+ if (trimmed === "") {
1782
+ setJsonError(null);
1783
+ onChange({ type: rec.type, ...rec.id ? { id: rec.id } : {} });
1784
+ return;
1785
+ }
1786
+ try {
1787
+ const parsed = JSON.parse(trimmed);
1788
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
1789
+ setJsonError("Args must be a JSON object");
1790
+ return;
1791
+ }
1792
+ setJsonError(null);
1793
+ onChange({
1794
+ type: rec.type,
1795
+ ...rec.id ? { id: rec.id } : {},
1796
+ ...parsed
1797
+ });
1798
+ } catch {
1799
+ setJsonError("Invalid JSON");
1800
+ }
1801
+ }, [text, rec.type, rec.id, onChange]);
1802
+ const handleTypeChange = useCallback8(
1803
+ (e) => {
1804
+ onChange({ ...rec, type: e.target.value });
1805
+ },
1806
+ [rec, onChange]
1807
+ );
1808
+ return /* @__PURE__ */ jsxs21("div", { className: "flex-1 space-y-2", children: [
1809
+ /* @__PURE__ */ jsxs21("div", { className: "space-y-1", children: [
1810
+ /* @__PURE__ */ jsx33(Label, { className: "text-xs text-muted-foreground", children: "Type" }),
1811
+ /* @__PURE__ */ jsx33(
1812
+ Input,
1813
+ {
1814
+ className: "h-8 font-mono text-sm",
1815
+ value: String(rec.type ?? ""),
1816
+ placeholder: "e.g. geoLocation, percentage",
1817
+ onChange: handleTypeChange
1818
+ }
1819
+ )
1820
+ ] }),
1821
+ /* @__PURE__ */ jsxs21("div", { className: "space-y-1", children: [
1822
+ /* @__PURE__ */ jsx33(Label, { className: "text-xs text-muted-foreground", children: "Args" }),
1823
+ /* @__PURE__ */ jsx33(
1824
+ Textarea,
1825
+ {
1826
+ className: "font-mono text-sm",
1827
+ rows: 3,
1828
+ value: text,
1829
+ placeholder: 'e.g. {"region": "us-east", "threshold": 50} (optional)',
1830
+ onChange: (e) => {
1831
+ setText(e.target.value);
1832
+ setJsonError(null);
1833
+ },
1834
+ onFocus: () => {
1835
+ focusedRef.current = true;
1836
+ },
1837
+ onBlur: handleArgsBlur
1838
+ }
1839
+ ),
1840
+ jsonError && /* @__PURE__ */ jsx33("p", { className: "mt-1 text-xs text-destructive", children: jsonError })
1841
+ ] })
1842
+ ] });
1843
+ }
1844
+
1845
+ // src/components/condition-builder/ConditionExtensionsContext.tsx
1846
+ import { createContext, useContext } from "react";
1847
+ var ConditionExtensionsContext = createContext(null);
1848
+ var ConditionExtensionsProvider = ConditionExtensionsContext.Provider;
1849
+ function useConditionExtensions() {
1850
+ return useContext(ConditionExtensionsContext);
1851
+ }
1852
+
1853
+ // src/components/condition-builder/ConditionValueEditor.tsx
1854
+ import { jsx as jsx34 } from "react/jsx-runtime";
1855
+ function ConditionValueEditor({ condition, onChange }) {
1856
+ const extensions = useConditionExtensions();
1857
+ switch (condition.type) {
1858
+ case "string":
1859
+ return /* @__PURE__ */ jsx34(StringConditionEditor, { condition, onChange });
1860
+ case "number":
1861
+ return /* @__PURE__ */ jsx34(NumberConditionEditor, { condition, onChange });
1862
+ case "datetime":
1863
+ return /* @__PURE__ */ jsx34(DatetimeConditionEditor, { condition, onChange });
1864
+ case "bool":
1865
+ return /* @__PURE__ */ jsx34(BoolConditionEditor, { condition, onChange });
1866
+ case "env":
1867
+ return /* @__PURE__ */ jsx34(EnvConditionEditor, { condition, onChange });
1868
+ case "startAt":
1869
+ return /* @__PURE__ */ jsx34(StartAtConditionEditor, { condition, onChange });
1870
+ case "endAt":
1871
+ return /* @__PURE__ */ jsx34(EndAtConditionEditor, { condition, onChange });
1872
+ default: {
1873
+ const OverrideEditor = extensions?.editorOverrides.get(condition.type);
1874
+ if (OverrideEditor) {
1875
+ return /* @__PURE__ */ jsx34(OverrideEditor, { condition, onChange });
1876
+ }
1877
+ return /* @__PURE__ */ jsx34(CustomConditionEditor, { condition, onChange });
1878
+ }
1879
+ }
1880
+ }
1881
+
1882
+ // src/components/condition-builder/ConditionGroup.tsx
1883
+ import { X } from "lucide-react";
1884
+
1885
+ // src/components/condition-builder/AddConditionMenu.tsx
1886
+ import { Plus } from "lucide-react";
1887
+
1888
+ // src/components/condition-builder/utils.ts
1889
+ function buildDefaultCondition(type, id, extraTypes) {
1890
+ if (type === "and") {
1891
+ return buildAndCondition([], id);
1892
+ }
1893
+ if (type === "or") {
1894
+ return buildOrCondition([], id);
1895
+ }
1896
+ if (type === "__custom") {
1897
+ return buildCustomCondition({ type: "", ...id ? { id } : {} });
1898
+ }
1899
+ const meta8 = BUILTIN_CONDITION_TYPES.find((m) => m.type === type) ?? extraTypes?.find((m) => m.type === type);
1900
+ if (meta8) {
1901
+ return buildCustomCondition({
1902
+ ...meta8.defaults,
1903
+ type: meta8.type,
1904
+ ...id ? { id } : {}
1905
+ });
1906
+ }
1907
+ return buildCustomCondition({ type: "", ...id ? { id } : {} });
1908
+ }
1909
+
1910
+ // src/components/condition-builder/AddConditionMenu.tsx
1911
+ import { Fragment as Fragment2, jsx as jsx35, jsxs as jsxs22 } from "react/jsx-runtime";
1912
+ function AddConditionMenu({ onAdd }) {
1913
+ const extensions = useConditionExtensions();
1914
+ const extraTypes = extensions?.extraConditionTypes ?? [];
1915
+ const primitives = BUILTIN_CONDITION_TYPES.filter(
1916
+ (m) => ["string", "number", "datetime", "bool"].includes(m.type)
1917
+ );
1918
+ const sugar = BUILTIN_CONDITION_TYPES.filter((m) => ["env", "startAt", "endAt"].includes(m.type));
1919
+ return /* @__PURE__ */ jsxs22(DropdownMenu, { children: [
1920
+ /* @__PURE__ */ jsxs22(DropdownMenuTrigger, { className: "inline-flex shrink-0 items-center justify-center gap-1.5 rounded px-3 h-8 text-sm font-medium hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", children: [
1921
+ /* @__PURE__ */ jsx35(Plus, { className: "mr-1 h-3.5 w-3.5" }),
1922
+ "Add condition"
1923
+ ] }),
1924
+ /* @__PURE__ */ jsxs22(DropdownMenuContent, { align: "start", children: [
1925
+ primitives.map((meta8) => /* @__PURE__ */ jsx35(DropdownMenuItem, { onSelect: () => onAdd(meta8.type), children: meta8.label }, meta8.type)),
1926
+ /* @__PURE__ */ jsx35(DropdownMenuSeparator, {}),
1927
+ sugar.map((meta8) => /* @__PURE__ */ jsx35(DropdownMenuItem, { onSelect: () => onAdd(meta8.type), children: meta8.label }, meta8.type)),
1928
+ extraTypes.length > 0 && /* @__PURE__ */ jsxs22(Fragment2, { children: [
1929
+ /* @__PURE__ */ jsx35(DropdownMenuSeparator, {}),
1930
+ extraTypes.map((meta8) => /* @__PURE__ */ jsx35(DropdownMenuItem, { onSelect: () => onAdd(meta8.type), children: meta8.label }, meta8.type))
1931
+ ] }),
1932
+ /* @__PURE__ */ jsx35(DropdownMenuSeparator, {}),
1933
+ /* @__PURE__ */ jsx35(DropdownMenuItem, { onSelect: () => onAdd("and"), children: "AND Group" }),
1934
+ /* @__PURE__ */ jsx35(DropdownMenuItem, { onSelect: () => onAdd("or"), children: "OR Group" }),
1935
+ /* @__PURE__ */ jsx35(DropdownMenuSeparator, {}),
1936
+ /* @__PURE__ */ jsx35(DropdownMenuItem, { onSelect: () => onAdd("__custom"), children: "Custom" })
1937
+ ] })
1938
+ ] });
1939
+ }
1940
+
1941
+ // src/components/condition-builder/MoveButtons.tsx
1942
+ import { ChevronDown, ChevronUp } from "lucide-react";
1943
+ import { Fragment as Fragment3, jsx as jsx36, jsxs as jsxs23 } from "react/jsx-runtime";
1944
+ function MoveButtons({ onMoveUp, onMoveDown, size = "h-8 w-8" }) {
1945
+ if (!onMoveUp && !onMoveDown) return null;
1946
+ return /* @__PURE__ */ jsxs23(Fragment3, { children: [
1947
+ /* @__PURE__ */ jsx36(
1948
+ Button,
1949
+ {
1950
+ variant: "ghost",
1951
+ size: "icon",
1952
+ className: `${size} text-muted-foreground hover:text-foreground`,
1953
+ disabled: !onMoveUp,
1954
+ onClick: onMoveUp,
1955
+ "aria-label": "Move up",
1956
+ children: /* @__PURE__ */ jsx36(ChevronUp, { className: "h-3.5 w-3.5" })
1957
+ }
1958
+ ),
1959
+ /* @__PURE__ */ jsx36(
1960
+ Button,
1961
+ {
1962
+ variant: "ghost",
1963
+ size: "icon",
1964
+ className: `${size} text-muted-foreground hover:text-foreground`,
1965
+ disabled: !onMoveDown,
1966
+ onClick: onMoveDown,
1967
+ "aria-label": "Move down",
1968
+ children: /* @__PURE__ */ jsx36(ChevronDown, { className: "h-3.5 w-3.5" })
1969
+ }
1970
+ )
1971
+ ] });
1972
+ }
1973
+
1974
+ // src/utils/validation-errors.ts
1975
+ function filterErrorsByPath(errors, pathKey, index) {
1976
+ if (!errors) return void 0;
1977
+ const filtered = errors.filter((err) => err.path[0] === pathKey && err.path[1] === index);
1978
+ if (filtered.length === 0) return void 0;
1979
+ return filtered.map((err) => ({ ...err, path: err.path.slice(2) }));
1980
+ }
1981
+
1982
+ // src/components/condition-builder/useConditionArray.ts
1983
+ import { useCallback as useCallback9, useMemo as useMemo8 } from "react";
1984
+ function useConditionArray(conditions, onChange) {
1985
+ const extensions = useConditionExtensions();
1986
+ const extraTypes = extensions?.extraConditionTypes;
1987
+ const withIds = useMemo8(() => ensureIds(conditions), [conditions]);
1988
+ const handleConditionChange = useCallback9(
1989
+ (index, updated) => {
1990
+ const next2 = [...withIds];
1991
+ next2[index] = updated;
1992
+ onChange(next2);
1993
+ },
1994
+ [withIds, onChange]
1995
+ );
1996
+ const handleConditionRemove = useCallback9(
1997
+ (index) => {
1998
+ onChange(withIds.filter((_, i) => i !== index));
1999
+ },
2000
+ [withIds, onChange]
2001
+ );
2002
+ const handleAddCondition = useCallback9(
2003
+ (type) => {
2004
+ const newCondition = buildDefaultCondition(
2005
+ type,
2006
+ `${AUTO_ID_PREFIX}${crypto.randomUUID()}`,
2007
+ extraTypes
2008
+ );
2009
+ onChange([...withIds, newCondition]);
2010
+ },
2011
+ [withIds, onChange, extraTypes]
2012
+ );
2013
+ const handleMoveUp = useCallback9(
2014
+ (index) => {
2015
+ if (index <= 0) return;
2016
+ const next2 = [...withIds];
2017
+ [next2[index - 1], next2[index]] = [next2[index], next2[index - 1]];
2018
+ onChange(next2);
2019
+ },
2020
+ [withIds, onChange]
2021
+ );
2022
+ const handleMoveDown = useCallback9(
2023
+ (index) => {
2024
+ if (index >= withIds.length - 1) return;
2025
+ const next2 = [...withIds];
2026
+ [next2[index], next2[index + 1]] = [next2[index + 1], next2[index]];
2027
+ onChange(next2);
2028
+ },
2029
+ [withIds, onChange]
2030
+ );
2031
+ return {
2032
+ conditions: withIds,
2033
+ handleConditionChange,
2034
+ handleConditionRemove,
2035
+ handleMoveUp,
2036
+ handleMoveDown,
2037
+ handleAddCondition
2038
+ };
2039
+ }
2040
+
2041
+ // src/components/condition-builder/ConditionGroup.tsx
2042
+ import { Fragment as Fragment4, memo } from "react";
2043
+ import { jsx as jsx37, jsxs as jsxs24 } from "react/jsx-runtime";
2044
+ var ConditionGroup = memo(function ConditionGroup2({
2045
+ type,
2046
+ conditions: rawConditions,
2047
+ onChange,
2048
+ onRemove,
2049
+ onMoveUp,
2050
+ onMoveDown,
2051
+ depth = 0,
2052
+ errors
2053
+ }) {
2054
+ const {
2055
+ conditions,
2056
+ handleConditionChange,
2057
+ handleConditionRemove,
2058
+ handleMoveUp,
2059
+ handleMoveDown,
2060
+ handleAddCondition
2061
+ } = useConditionArray(rawConditions, onChange);
2062
+ return /* @__PURE__ */ jsxs24(
2063
+ "div",
2064
+ {
2065
+ className: cn(
2066
+ "border-l-3 pl-3 pr-2 py-2 rounded-r-md",
2067
+ type === "and" ? "border-primary/30" : "border-amber-500/30"
2068
+ ),
2069
+ style: {
2070
+ backgroundColor: `oklch(from var(--color-muted) l c h / ${Math.min(0.2 + depth * 0.1, 0.5)})`
2071
+ },
2072
+ children: [
2073
+ /* @__PURE__ */ jsxs24("div", { className: "mb-2 flex items-center gap-2", children: [
2074
+ /* @__PURE__ */ jsxs24(
2075
+ Badge,
2076
+ {
2077
+ variant: "outline",
2078
+ className: "select-none font-mono text-xs bg-muted/50 text-muted-foreground border-border",
2079
+ children: [
2080
+ "L",
2081
+ depth,
2082
+ " | ",
2083
+ type.toUpperCase()
2084
+ ]
2085
+ }
2086
+ ),
2087
+ /* @__PURE__ */ jsxs24("span", { className: "text-sm text-muted-foreground", children: [
2088
+ conditions.length,
2089
+ " condition",
2090
+ conditions.length !== 1 ? "s" : ""
2091
+ ] }),
2092
+ /* @__PURE__ */ jsx37("div", { className: "flex-1" }),
2093
+ /* @__PURE__ */ jsxs24("div", { className: "flex shrink-0 gap-0.5", children: [
2094
+ /* @__PURE__ */ jsx37(MoveButtons, { onMoveUp, onMoveDown, size: "h-6 w-6" }),
2095
+ /* @__PURE__ */ jsx37(
2096
+ Button,
2097
+ {
2098
+ variant: "ghost",
2099
+ size: "icon",
2100
+ className: "h-6 w-6 text-destructive/60 hover:bg-destructive/10 hover:text-destructive",
2101
+ "aria-label": "Remove condition group",
2102
+ onClick: onRemove,
2103
+ children: /* @__PURE__ */ jsx37(X, { className: "h-3.5 w-3.5" })
2104
+ }
2105
+ )
2106
+ ] })
2107
+ ] }),
2108
+ /* @__PURE__ */ jsx37("div", { className: "space-y-1.5", children: conditions.map((c, i) => /* @__PURE__ */ jsxs24(Fragment4, { children: [
2109
+ i > 0 && /* @__PURE__ */ jsx37("div", { className: "flex", children: /* @__PURE__ */ jsx37(
2110
+ Badge,
2111
+ {
2112
+ variant: "outline",
2113
+ className: cn(
2114
+ "select-none font-mono text-xs",
2115
+ type === "and" ? "bg-primary/10 text-primary border-primary/20" : "bg-amber-500/10 text-amber-600 border-amber-500/20 dark:text-amber-400"
2116
+ ),
2117
+ children: type.toUpperCase()
2118
+ }
2119
+ ) }),
2120
+ /* @__PURE__ */ jsx37(
2121
+ ConditionBlock,
2122
+ {
2123
+ condition: c,
2124
+ onChange: (updated) => handleConditionChange(i, updated),
2125
+ onRemove: () => handleConditionRemove(i),
2126
+ onMoveUp: i > 0 ? () => handleMoveUp(i) : void 0,
2127
+ onMoveDown: i < conditions.length - 1 ? () => handleMoveDown(i) : void 0,
2128
+ depth: depth + 1,
2129
+ errors: filterErrorsByPath(errors, "conditions", i)
2130
+ }
2131
+ )
2132
+ ] }, c.id)) }),
2133
+ /* @__PURE__ */ jsx37("div", { className: "mt-2", children: /* @__PURE__ */ jsx37(AddConditionMenu, { onAdd: handleAddCondition }) })
2134
+ ]
2135
+ }
2136
+ );
2137
+ });
2138
+
2139
+ // src/components/condition-builder/ConditionBlock.tsx
2140
+ import { jsx as jsx38, jsxs as jsxs25 } from "react/jsx-runtime";
2141
+ var GENERIC_ZOD_ERROR = "Invalid input";
2142
+ var ConditionBlock = memo2(function ConditionBlock2({
2143
+ condition,
2144
+ onChange,
2145
+ onRemove,
2146
+ onMoveUp,
2147
+ onMoveDown,
2148
+ depth = 0,
2149
+ errors
2150
+ }) {
2151
+ function handleChange(updated) {
2152
+ onChange(updated);
2153
+ }
2154
+ if (isAndCondition(condition) || isOrCondition(condition)) {
2155
+ return /* @__PURE__ */ jsx38(
2156
+ ConditionGroup,
2157
+ {
2158
+ type: condition.type,
2159
+ conditions: condition.conditions,
2160
+ onChange: (conditions) => handleChange(
2161
+ condition.type === "and" ? buildAndCondition(conditions, condition.id) : buildOrCondition(conditions, condition.id)
2162
+ ),
2163
+ onRemove,
2164
+ onMoveUp,
2165
+ onMoveDown,
2166
+ depth,
2167
+ errors
2168
+ }
2169
+ );
2170
+ }
2171
+ const extensions = useConditionExtensions();
2172
+ const meta8 = getConditionMeta(condition.type) ?? extensions?.extraConditionTypes.find((m) => m.type === condition.type);
2173
+ const label = meta8?.label ?? (condition.type || "Custom");
2174
+ return /* @__PURE__ */ jsxs25("div", { className: "border border-border bg-card p-2 space-y-2", children: [
2175
+ /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2", children: [
2176
+ /* @__PURE__ */ jsx38("span", { className: "flex h-8 shrink-0 items-center text-sm font-medium text-muted-foreground", children: label }),
2177
+ /* @__PURE__ */ jsx38("div", { className: "flex-1" }),
2178
+ /* @__PURE__ */ jsxs25("div", { className: "flex shrink-0 gap-0.5", children: [
2179
+ /* @__PURE__ */ jsx38(MoveButtons, { onMoveUp, onMoveDown }),
2180
+ /* @__PURE__ */ jsx38(
2181
+ Button,
2182
+ {
2183
+ variant: "ghost",
2184
+ size: "icon",
2185
+ className: "h-8 w-8 text-destructive/60 hover:bg-destructive/10 hover:text-destructive",
2186
+ onClick: onRemove,
2187
+ "aria-label": "Remove condition",
2188
+ children: /* @__PURE__ */ jsx38(X2, { className: "h-3.5 w-3.5" })
2189
+ }
2190
+ )
2191
+ ] })
2192
+ ] }),
2193
+ /* @__PURE__ */ jsx38(ConditionValueEditor, { condition, onChange: handleChange }),
2194
+ (errors?.length ?? 0) > 0 && /* @__PURE__ */ jsx38("div", { className: "space-y-0.5", children: errors.map((err, i) => {
2195
+ const field = err.path.length > 0 ? err.path.join(".") : null;
2196
+ const msg = err.message === GENERIC_ZOD_ERROR ? "Invalid condition \u2014 check required fields" : err.message;
2197
+ return /* @__PURE__ */ jsxs25("p", { className: "text-xs text-destructive", children: [
2198
+ field && /* @__PURE__ */ jsxs25("span", { className: "font-mono text-destructive/70", children: [
2199
+ field,
2200
+ ": "
2201
+ ] }),
2202
+ msg
2203
+ ] }, i);
2204
+ }) })
2205
+ ] });
2206
+ });
2207
+
2208
+ // src/components/condition-builder/ConditionBuilder.tsx
2209
+ import { jsx as jsx39, jsxs as jsxs26 } from "react/jsx-runtime";
2210
+ function ConditionBuilder({
2211
+ conditions: rawConditions,
2212
+ onChange,
2213
+ validationErrors
2214
+ }) {
2215
+ const {
2216
+ conditions,
2217
+ handleConditionChange,
2218
+ handleConditionRemove,
2219
+ handleMoveUp,
2220
+ handleMoveDown,
2221
+ handleAddCondition
2222
+ } = useConditionArray(rawConditions, onChange);
2223
+ return /* @__PURE__ */ jsx39(ErrorBoundary, { children: /* @__PURE__ */ jsxs26("div", { className: "space-y-1.5", children: [
2224
+ conditions.length > 0 && /* @__PURE__ */ jsxs26("div", { className: "border-l-3 pl-3 py-2 bg-muted/20 rounded-r-md border-primary/30", children: [
2225
+ /* @__PURE__ */ jsx39("div", { className: "mb-2 flex items-center gap-2", children: /* @__PURE__ */ jsxs26("span", { className: "text-sm text-muted-foreground", children: [
2226
+ conditions.length,
2227
+ " condition",
2228
+ conditions.length !== 1 ? "s" : ""
2229
+ ] }) }),
2230
+ /* @__PURE__ */ jsx39("div", { className: "space-y-1.5", children: conditions.map((c, i) => /* @__PURE__ */ jsxs26(Fragment5, { children: [
2231
+ i > 0 && /* @__PURE__ */ jsx39("div", { className: "flex", children: /* @__PURE__ */ jsx39(
2232
+ Badge,
2233
+ {
2234
+ variant: "outline",
2235
+ className: "select-none font-mono text-xs bg-primary/10 text-primary border-primary/20",
2236
+ children: "AND"
2237
+ }
2238
+ ) }),
2239
+ /* @__PURE__ */ jsx39(
2240
+ ConditionBlock,
2241
+ {
2242
+ condition: c,
2243
+ onChange: (updated) => handleConditionChange(i, updated),
2244
+ onRemove: () => handleConditionRemove(i),
2245
+ onMoveUp: i > 0 ? () => handleMoveUp(i) : void 0,
2246
+ onMoveDown: i < conditions.length - 1 ? () => handleMoveDown(i) : void 0,
2247
+ depth: 1,
2248
+ errors: filterErrorsByPath(validationErrors, "conditions", i)
2249
+ }
2250
+ )
2251
+ ] }, c.id)) })
2252
+ ] }),
2253
+ /* @__PURE__ */ jsx39(AddConditionMenu, { onAdd: handleAddCondition })
2254
+ ] }) });
2255
+ }
2256
+
2257
+ // src/components/variation-editor/VariationCard.tsx
2258
+ import { memo as memo3, useState as useState6 } from "react";
2259
+ import { Collapsible as CollapsiblePrimitive } from "radix-ui";
2260
+ import { ChevronRight, Eye, GripVertical, Trash2 } from "lucide-react";
2261
+
2262
+ // src/utils/condition-summary.ts
2263
+ import { isAndCondition as isAndCondition2, isOrCondition as isOrCondition2 } from "showwhat";
2264
+ function formatLeafOperator(c) {
2265
+ const opField = "op" in c ? String(c.op) : "";
2266
+ const raw = "value" in c ? c.value : "";
2267
+ const opSymbols = {
2268
+ eq: "=",
2269
+ neq: "!=",
2270
+ in: "in",
2271
+ nin: "not in",
2272
+ regex: "~",
2273
+ gt: ">",
2274
+ gte: ">=",
2275
+ lt: "<",
2276
+ lte: "<="
2277
+ };
2278
+ const opSymbol = opSymbols[opField] ?? "=";
2279
+ if (c.type === "string" || c.type === "env") {
2280
+ const vals = Array.isArray(raw) ? raw.map(String) : [String(raw)];
2281
+ if (opField === "regex") {
2282
+ return { op: "~", val: vals.map((v) => `/${v}/`).join(", ") };
2283
+ }
2284
+ return { op: opSymbol, val: vals.map((v) => `"${v}"`).join(", ") };
2285
+ }
2286
+ if (c.type === "number") {
2287
+ if (Array.isArray(raw)) {
2288
+ return { op: opSymbol, val: raw.map(String).join(", ") };
2289
+ }
2290
+ return { op: opSymbol, val: String(raw) };
2291
+ }
2292
+ if (c.type === "bool") {
2293
+ return { op: "=", val: String(raw) };
2294
+ }
2295
+ if (c.type === "datetime") {
2296
+ return { op: opSymbol, val: `"${String(raw)}"` };
2297
+ }
2298
+ if (c.type === "startAt") return { op: ">=", val: `"${String(raw)}"` };
2299
+ if (c.type === "endAt") return { op: "<", val: `"${String(raw)}"` };
2300
+ const value = raw ? String(raw) : "";
2301
+ return { op: "=", val: value ? `"${value}"` : "" };
2302
+ }
2303
+ function formatOne(c, indent) {
2304
+ const prefix = " ".repeat(indent);
2305
+ if (isAndCondition2(c)) {
2306
+ if (c.conditions.length === 0) return [`${prefix}(empty AND group)`];
2307
+ const lines = [];
2308
+ c.conditions.forEach((sub, i) => {
2309
+ const subLines = formatOne(sub, indent + 1);
2310
+ if (i > 0) lines.push(`${prefix} AND`);
2311
+ lines.push(...subLines);
2312
+ });
2313
+ return [`${prefix}(`, ...lines, `${prefix})`];
2314
+ }
2315
+ if (isOrCondition2(c)) {
2316
+ if (c.conditions.length === 0) return [`${prefix}(empty OR group)`];
2317
+ const lines = [];
2318
+ c.conditions.forEach((sub, i) => {
2319
+ const subLines = formatOne(sub, indent + 1);
2320
+ if (i > 0) lines.push(`${prefix} OR`);
2321
+ lines.push(...subLines);
2322
+ });
2323
+ return [`${prefix}(`, ...lines, `${prefix})`];
2324
+ }
2325
+ const meta8 = getConditionMeta(c.type);
2326
+ const label = meta8?.label ?? c.type;
2327
+ const { op, val } = formatLeafOperator(c);
2328
+ const key = "key" in c ? String(c.key) : "";
2329
+ if (key && val) return [`${prefix}${key} ${op} ${val}`];
2330
+ if (val) return [`${prefix}${label} ${op} ${val}`];
2331
+ return [`${prefix}${label}`];
2332
+ }
2333
+ function formatConditionSummary(conditions) {
2334
+ if (conditions.length === 0) return null;
2335
+ const lines = [];
2336
+ conditions.forEach((c, i) => {
2337
+ if (i > 0) lines.push(" AND");
2338
+ lines.push(...formatOne(c, 1));
2339
+ });
2340
+ return `When ${lines.join("\n").trimStart()}`;
2341
+ }
2342
+
2343
+ // src/components/variation-editor/VariationCard.tsx
2344
+ import { jsx as jsx40, jsxs as jsxs27 } from "react/jsx-runtime";
2345
+ var VariationCard = memo3(function VariationCard2({
2346
+ variation,
2347
+ index,
2348
+ validationErrors,
2349
+ onChange,
2350
+ onRemove,
2351
+ dragHandleProps
2352
+ }) {
2353
+ const [open, setOpen] = useState6(false);
2354
+ const conditionCount = variation.conditions?.length ?? 0;
2355
+ return /* @__PURE__ */ jsx40(CollapsiblePrimitive.Root, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs27("div", { className: "rounded-lg border border-border/50 bg-card transition-colors hover:border-primary/30", children: [
2356
+ /* @__PURE__ */ jsxs27("div", { className: "flex items-center gap-2 px-3 py-2.5", children: [
2357
+ /* @__PURE__ */ jsx40(
2358
+ "button",
2359
+ {
2360
+ type: "button",
2361
+ className: "cursor-grab touch-none text-muted-foreground/50 hover:text-muted-foreground",
2362
+ "aria-label": "Drag to reorder",
2363
+ ...dragHandleProps,
2364
+ children: /* @__PURE__ */ jsx40(GripVertical, { className: "h-4 w-4" })
2365
+ }
2366
+ ),
2367
+ /* @__PURE__ */ jsx40(CollapsiblePrimitive.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs27(
2368
+ "button",
2369
+ {
2370
+ type: "button",
2371
+ className: "flex flex-1 items-center gap-2 text-left hover:cursor-pointer",
2372
+ children: [
2373
+ /* @__PURE__ */ jsx40(Badge, { variant: "secondary", className: "font-mono text-xs", children: index }),
2374
+ /* @__PURE__ */ jsx40("span", { className: "flex-1 truncate text-sm text-muted-foreground", children: variation.description || String(variation.value ?? "") }),
2375
+ conditionCount > 0 && /* @__PURE__ */ jsxs27("span", { className: "text-xs text-muted-foreground/60", children: [
2376
+ conditionCount,
2377
+ " ",
2378
+ conditionCount === 1 ? "condition" : "conditions"
2379
+ ] }),
2380
+ /* @__PURE__ */ jsx40(
2381
+ ChevronRight,
2382
+ {
2383
+ className: `h-4 w-4 text-muted-foreground/60 transition-transform ${open ? "rotate-90" : ""}`
2384
+ }
2385
+ )
2386
+ ]
2387
+ }
2388
+ ) })
2389
+ ] }),
2390
+ /* @__PURE__ */ jsx40(CollapsiblePrimitive.Content, { children: /* @__PURE__ */ jsxs27("div", { className: "space-y-4 border-t border-border/40 px-4 py-4", children: [
2391
+ /* @__PURE__ */ jsxs27("div", { className: "flex items-center justify-between", children: [
2392
+ /* @__PURE__ */ jsx40(Label, { className: "text-xs font-medium uppercase tracking-wider text-muted-foreground", children: "Value" }),
2393
+ /* @__PURE__ */ jsx40(
2394
+ ConfirmDialog,
2395
+ {
2396
+ title: "Remove variation?",
2397
+ description: `This will delete variation ${index} and all its conditions. This action cannot be undone.`,
2398
+ actionLabel: "Remove",
2399
+ onConfirm: onRemove,
2400
+ children: /* @__PURE__ */ jsx40(
2401
+ Button,
2402
+ {
2403
+ variant: "ghost",
2404
+ size: "icon-xs",
2405
+ className: "shrink-0 text-destructive/60 hover:bg-destructive/10 hover:text-destructive",
2406
+ "aria-label": "Remove variation",
2407
+ children: /* @__PURE__ */ jsx40(Trash2, { className: "h-3.5 w-3.5" })
2408
+ }
2409
+ )
2410
+ }
2411
+ )
2412
+ ] }),
2413
+ /* @__PURE__ */ jsx40("div", { children: /* @__PURE__ */ jsx40(
2414
+ ValueInput,
2415
+ {
2416
+ value: variation.value,
2417
+ onChange: (value) => onChange({ ...variation, value })
2418
+ }
2419
+ ) }),
2420
+ /* @__PURE__ */ jsx40(
2421
+ "input",
2422
+ {
2423
+ className: "w-full border-none bg-transparent text-sm text-muted-foreground placeholder:text-muted-foreground/50 focus:text-foreground focus:outline-none",
2424
+ value: variation.description ?? "",
2425
+ placeholder: "Add a description (optional)...",
2426
+ onChange: (e) => onChange({
2427
+ ...variation,
2428
+ description: e.target.value || void 0
2429
+ })
2430
+ }
2431
+ ),
2432
+ /* @__PURE__ */ jsx40("div", { className: "border-t border-border/40 pt-4", children: /* @__PURE__ */ jsxs27("div", { className: "rounded-lg border border-border/40 bg-muted/30 p-3", children: [
2433
+ /* @__PURE__ */ jsxs27("div", { className: "mb-1.5 flex items-center gap-1.5", children: [
2434
+ /* @__PURE__ */ jsx40(Label, { className: "text-sm font-medium", children: "Conditions" }),
2435
+ conditionCount > 0 && /* @__PURE__ */ jsxs27(Dialog, { children: [
2436
+ /* @__PURE__ */ jsx40(DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsx40(
2437
+ "button",
2438
+ {
2439
+ type: "button",
2440
+ className: "inline-flex items-center justify-center rounded p-0.5 text-muted-foreground/60 hover:text-muted-foreground",
2441
+ children: /* @__PURE__ */ jsx40(Eye, { className: "h-3.5 w-3.5" })
2442
+ }
2443
+ ) }),
2444
+ /* @__PURE__ */ jsxs27(DialogContent, { children: [
2445
+ /* @__PURE__ */ jsxs27(DialogHeader, { children: [
2446
+ /* @__PURE__ */ jsx40(DialogTitle, { children: "Condition Summary" }),
2447
+ /* @__PURE__ */ jsx40(DialogDescription, { children: "Evaluation logic for this variation" })
2448
+ ] }),
2449
+ /* @__PURE__ */ jsx40("pre", { className: "rounded-md bg-muted p-4 font-mono text-xs whitespace-pre overflow-auto max-h-80", children: formatConditionSummary(variation.conditions ?? []) })
2450
+ ] })
2451
+ ] })
2452
+ ] }),
2453
+ /* @__PURE__ */ jsx40(
2454
+ ConditionBuilder,
2455
+ {
2456
+ conditions: variation.conditions ?? [],
2457
+ onChange: (conditions) => onChange({
2458
+ ...variation,
2459
+ conditions: conditions.length > 0 ? conditions : void 0
2460
+ }),
2461
+ validationErrors
2462
+ }
2463
+ )
2464
+ ] }) }),
2465
+ /* @__PURE__ */ jsx40(
2466
+ ValidationMessage,
2467
+ {
2468
+ errors: validationErrors?.filter((err) => err.path[0] !== "conditions")
2469
+ }
2470
+ )
2471
+ ] }) })
2472
+ ] }) });
2473
+ });
2474
+
2475
+ // src/components/variation-editor/VariationList.tsx
2476
+ import { useCallback as useCallback10, useMemo as useMemo9 } from "react";
2477
+ import {
2478
+ DndContext,
2479
+ closestCenter,
2480
+ KeyboardSensor,
2481
+ PointerSensor,
2482
+ useSensor,
2483
+ useSensors
2484
+ } from "@dnd-kit/core";
2485
+ import {
2486
+ SortableContext,
2487
+ verticalListSortingStrategy,
2488
+ useSortable,
2489
+ arrayMove
2490
+ } from "@dnd-kit/sortable";
2491
+ import { CSS } from "@dnd-kit/utilities";
2492
+ import { jsx as jsx41 } from "react/jsx-runtime";
2493
+ function SortableVariation({
2494
+ id,
2495
+ variation,
2496
+ index,
2497
+ validationErrors,
2498
+ onChange,
2499
+ onRemove
2500
+ }) {
2501
+ const {
2502
+ attributes,
2503
+ listeners,
2504
+ setNodeRef,
2505
+ setActivatorNodeRef,
2506
+ transform,
2507
+ transition,
2508
+ isDragging
2509
+ } = useSortable({ id });
2510
+ const style = {
2511
+ transform: CSS.Transform.toString(transform),
2512
+ transition,
2513
+ opacity: isDragging ? 0.5 : 1
2514
+ };
2515
+ return /* @__PURE__ */ jsx41(
2516
+ "div",
2517
+ {
2518
+ ref: setNodeRef,
2519
+ style: {
2520
+ ...style,
2521
+ animationDelay: `${index * 50}ms`
2522
+ },
2523
+ className: "animate-fade-up",
2524
+ children: /* @__PURE__ */ jsx41(
2525
+ VariationCard,
2526
+ {
2527
+ variation,
2528
+ index,
2529
+ validationErrors,
2530
+ onChange,
2531
+ onRemove,
2532
+ dragHandleProps: { ref: setActivatorNodeRef, ...listeners, ...attributes }
2533
+ }
2534
+ )
2535
+ }
2536
+ );
2537
+ }
2538
+ function VariationList({
2539
+ variations: rawVariations,
2540
+ validationErrors,
2541
+ onChange
2542
+ }) {
2543
+ const sensors = useSensors(
2544
+ useSensor(PointerSensor, { activationConstraint: { distance: 8 } }),
2545
+ useSensor(KeyboardSensor)
2546
+ );
2547
+ const variations = useMemo9(() => ensureIds(rawVariations), [rawVariations]);
2548
+ const sortableIds = useMemo9(() => variations.map((v) => v.id), [variations]);
2549
+ const handleDragEnd = useCallback10(
2550
+ (event) => {
2551
+ const { active, over } = event;
2552
+ if (!over || active.id === over.id) return;
2553
+ const oldIndex = sortableIds.indexOf(String(active.id));
2554
+ const newIndex = sortableIds.indexOf(String(over.id));
2555
+ if (oldIndex === -1 || newIndex === -1) return;
2556
+ onChange(arrayMove(variations, oldIndex, newIndex));
2557
+ },
2558
+ [sortableIds, variations, onChange]
2559
+ );
2560
+ const handleVariationChange = useCallback10(
2561
+ (index, updated) => {
2562
+ const next2 = [...variations];
2563
+ next2[index] = updated;
2564
+ onChange(next2);
2565
+ },
2566
+ [variations, onChange]
2567
+ );
2568
+ const handleRemove = useCallback10(
2569
+ (index) => {
2570
+ onChange(variations.filter((_, i) => i !== index));
2571
+ },
2572
+ [variations, onChange]
2573
+ );
2574
+ return /* @__PURE__ */ jsx41("div", { className: "space-y-3", children: /* @__PURE__ */ jsx41(DndContext, { sensors, collisionDetection: closestCenter, onDragEnd: handleDragEnd, children: /* @__PURE__ */ jsx41(SortableContext, { items: sortableIds, strategy: verticalListSortingStrategy, children: variations.map((v, i) => /* @__PURE__ */ jsx41(
2575
+ SortableVariation,
2576
+ {
2577
+ id: v.id,
2578
+ variation: v,
2579
+ index: i,
2580
+ validationErrors: filterErrorsByPath(validationErrors, "variations", i),
2581
+ onChange: (updated) => handleVariationChange(i, updated),
2582
+ onRemove: () => handleRemove(i)
2583
+ },
2584
+ v.id
2585
+ )) }) }) });
2586
+ }
2587
+
2588
+ // src/components/definition-editor/DefinitionEditor.tsx
2589
+ import { useRef as useRef4, useState as useState7 } from "react";
2590
+ import { AlertTriangle, Plus as Plus2, Save, Undo2 } from "lucide-react";
2591
+ import { jsx as jsx42, jsxs as jsxs28 } from "react/jsx-runtime";
2592
+ function DefinitionEditor({
2593
+ definitionKey,
2594
+ definition,
2595
+ validationErrors,
2596
+ isDirty,
2597
+ isPending,
2598
+ onUpdate,
2599
+ onRename,
2600
+ onSave,
2601
+ onDiscard
2602
+ }) {
2603
+ const [editingKey, setEditingKey] = useState7(false);
2604
+ const [keyDraft, setKeyDraft] = useState7(definitionKey);
2605
+ const prevKeyRef = useRef4(definitionKey);
2606
+ if (prevKeyRef.current !== definitionKey) {
2607
+ prevKeyRef.current = definitionKey;
2608
+ setKeyDraft(definitionKey);
2609
+ }
2610
+ async function handleKeySubmit() {
2611
+ const trimmed = keyDraft.trim();
2612
+ if (trimmed && trimmed !== definitionKey) {
2613
+ try {
2614
+ await onRename(trimmed);
2615
+ setEditingKey(false);
2616
+ } catch {
2617
+ }
2618
+ } else {
2619
+ setKeyDraft(definitionKey);
2620
+ setEditingKey(false);
2621
+ }
2622
+ }
2623
+ function handleAddVariation() {
2624
+ onUpdate({
2625
+ ...definition,
2626
+ variations: [...definition.variations, { id: crypto.randomUUID(), value: "" }]
2627
+ });
2628
+ }
2629
+ const errorCount = validationErrors?.length ?? 0;
2630
+ return /* @__PURE__ */ jsxs28("div", { className: "flex h-full flex-col overflow-hidden", children: [
2631
+ /* @__PURE__ */ jsxs28("div", { className: "flex shrink-0 items-center justify-end gap-2 border-b border-border bg-muted/30 px-4 py-2", children: [
2632
+ /* @__PURE__ */ jsxs28(
2633
+ Button,
2634
+ {
2635
+ variant: isDirty ? "default" : "ghost",
2636
+ size: "sm",
2637
+ disabled: !isDirty || isPending,
2638
+ onClick: onSave,
2639
+ children: [
2640
+ /* @__PURE__ */ jsx42(Save, { className: "mr-1.5 h-4 w-4" }),
2641
+ "Save"
2642
+ ]
2643
+ }
2644
+ ),
2645
+ onDiscard ? /* @__PURE__ */ jsx42(
2646
+ ConfirmDialog,
2647
+ {
2648
+ title: "Discard changes?",
2649
+ description: "This will revert all unsaved changes to this definition. This action cannot be undone.",
2650
+ actionLabel: "Discard",
2651
+ onConfirm: onDiscard,
2652
+ children: /* @__PURE__ */ jsxs28(Button, { variant: "ghost", size: "sm", disabled: !isDirty || isPending, children: [
2653
+ /* @__PURE__ */ jsx42(Undo2, { className: "mr-1.5 h-4 w-4" }),
2654
+ "Discard"
2655
+ ] })
2656
+ }
2657
+ ) : /* @__PURE__ */ jsxs28(Button, { variant: "ghost", size: "sm", disabled: true, children: [
2658
+ /* @__PURE__ */ jsx42(Undo2, { className: "mr-1.5 h-4 w-4" }),
2659
+ "Discard"
2660
+ ] })
2661
+ ] }),
2662
+ errorCount > 0 && /* @__PURE__ */ jsx42("div", { className: "shrink-0 border-b border-destructive/30 bg-destructive/10 px-4 py-2", children: /* @__PURE__ */ jsxs28("p", { className: "flex items-center gap-1.5 text-xs font-medium text-destructive", children: [
2663
+ /* @__PURE__ */ jsx42(AlertTriangle, { className: "h-3.5 w-3.5" }),
2664
+ errorCount,
2665
+ " validation error",
2666
+ errorCount !== 1 ? "s" : "",
2667
+ " \u2014 fix the highlighted fields below"
2668
+ ] }) }),
2669
+ /* @__PURE__ */ jsx42("div", { className: "shrink-0 border-b border-border/50 mx-auto w-full max-w-3xl px-8 pt-8 pb-4 space-y-3", children: /* @__PURE__ */ jsxs28("div", { className: "space-y-3", children: [
2670
+ /* @__PURE__ */ jsxs28("div", { className: "flex items-start justify-between gap-4", children: [
2671
+ /* @__PURE__ */ jsx42("div", { className: "flex-1", children: editingKey ? /* @__PURE__ */ jsx42(
2672
+ Input,
2673
+ {
2674
+ className: "h-auto border-none bg-transparent px-0 font-mono text-2xl font-semibold shadow-none focus-visible:ring-0",
2675
+ value: keyDraft,
2676
+ autoFocus: true,
2677
+ onChange: (e) => setKeyDraft(e.target.value),
2678
+ onBlur: handleKeySubmit,
2679
+ onKeyDown: (e) => {
2680
+ if (e.key === "Enter") handleKeySubmit();
2681
+ if (e.key === "Escape") {
2682
+ setKeyDraft(definitionKey);
2683
+ setEditingKey(false);
2684
+ }
2685
+ }
2686
+ }
2687
+ ) : /* @__PURE__ */ jsx42(
2688
+ "button",
2689
+ {
2690
+ type: "button",
2691
+ className: "block w-full rounded px-0 py-0 text-left font-mono text-2xl font-semibold hover:text-foreground/70",
2692
+ "aria-label": "Rename definition key",
2693
+ onClick: () => {
2694
+ setKeyDraft(definitionKey);
2695
+ setEditingKey(true);
2696
+ },
2697
+ children: definitionKey
2698
+ }
2699
+ ) }),
2700
+ /* @__PURE__ */ jsxs28("div", { className: "flex shrink-0 items-center gap-2 pt-1.5", children: [
2701
+ /* @__PURE__ */ jsx42(Label, { htmlFor: "definition-active", className: "text-xs text-muted-foreground", children: "Active" }),
2702
+ /* @__PURE__ */ jsx42(
2703
+ Switch,
2704
+ {
2705
+ id: "definition-active",
2706
+ checked: definition.active !== false,
2707
+ onCheckedChange: (checked) => onUpdate({
2708
+ ...definition,
2709
+ active: checked ? void 0 : false
2710
+ })
2711
+ }
2712
+ )
2713
+ ] })
2714
+ ] }),
2715
+ /* @__PURE__ */ jsx42(
2716
+ "input",
2717
+ {
2718
+ className: "w-full border-none bg-transparent text-sm text-muted-foreground placeholder:text-muted-foreground/50 focus:text-foreground focus:outline-none",
2719
+ value: definition.description ?? "",
2720
+ placeholder: "Add a description (optional)...",
2721
+ "aria-label": "Definition description",
2722
+ onChange: (e) => onUpdate({
2723
+ ...definition,
2724
+ description: e.target.value || void 0
2725
+ })
2726
+ }
2727
+ )
2728
+ ] }) }),
2729
+ /* @__PURE__ */ jsx42("div", { className: "min-h-0 flex-1 overflow-auto", children: /* @__PURE__ */ jsx42("div", { className: "mx-auto w-full max-w-3xl px-8 py-8 space-y-4", children: /* @__PURE__ */ jsxs28("div", { className: "space-y-4", children: [
2730
+ /* @__PURE__ */ jsxs28("div", { className: "flex items-center justify-between", children: [
2731
+ /* @__PURE__ */ jsx42("span", { className: "text-xs font-medium uppercase tracking-wider text-muted-foreground", children: "Variations" }),
2732
+ /* @__PURE__ */ jsxs28(
2733
+ Button,
2734
+ {
2735
+ variant: "ghost",
2736
+ size: "sm",
2737
+ className: "h-7 text-xs text-muted-foreground",
2738
+ onClick: handleAddVariation,
2739
+ children: [
2740
+ /* @__PURE__ */ jsx42(Plus2, { className: "mr-1 h-3 w-3" }),
2741
+ "Add"
2742
+ ]
2743
+ }
2744
+ )
2745
+ ] }),
2746
+ /* @__PURE__ */ jsx42(
2747
+ VariationList,
2748
+ {
2749
+ variations: definition.variations,
2750
+ validationErrors,
2751
+ onChange: (variations) => onUpdate({ ...definition, variations })
2752
+ }
2753
+ ),
2754
+ definition.variations.length > 2 && /* @__PURE__ */ jsxs28(
2755
+ "button",
2756
+ {
2757
+ type: "button",
2758
+ className: "flex w-full items-center justify-center gap-1.5 rounded-lg border border-dashed border-border/60 py-2.5 text-xs text-muted-foreground hover:border-border hover:text-foreground",
2759
+ onClick: handleAddVariation,
2760
+ children: [
2761
+ /* @__PURE__ */ jsx42(Plus2, { className: "h-3 w-3" }),
2762
+ "Add variation"
2763
+ ]
2764
+ }
2765
+ )
2766
+ ] }) }) })
2767
+ ] });
2768
+ }
2769
+
2770
+ // src/components/definition-list/DefinitionList.tsx
2771
+ import { useState as useState9 } from "react";
2772
+ import { Plus as Plus3, Search, X as X3 } from "lucide-react";
2773
+
2774
+ // src/components/definition-list/DefinitionListItem.tsx
2775
+ import { memo as memo4, useState as useState8 } from "react";
2776
+ import { Trash2 as Trash22 } from "lucide-react";
2777
+ import { Fragment as Fragment6, jsx as jsx43, jsxs as jsxs29 } from "react/jsx-runtime";
2778
+ var DefinitionListItem = memo4(function DefinitionListItem2({
2779
+ definitionKey,
2780
+ variationCount,
2781
+ isActive,
2782
+ hasErrors,
2783
+ isSelected,
2784
+ isDirty,
2785
+ onSelect,
2786
+ onRemove
2787
+ }) {
2788
+ const [confirmOpen, setConfirmOpen] = useState8(false);
2789
+ return /* @__PURE__ */ jsxs29(
2790
+ "div",
2791
+ {
2792
+ tabIndex: 0,
2793
+ className: cn(
2794
+ "group flex w-full items-center gap-2 rounded border-l-2 px-3 py-2.5 text-left text-sm transition-colors cursor-pointer",
2795
+ isSelected ? "border-l-primary bg-accent text-accent-foreground" : "border-l-transparent hover:bg-muted"
2796
+ ),
2797
+ onClick: onSelect,
2798
+ onKeyDown: (e) => {
2799
+ if (e.key === "Enter" || e.key === " ") {
2800
+ e.preventDefault();
2801
+ onSelect();
2802
+ }
2803
+ },
2804
+ children: [
2805
+ /* @__PURE__ */ jsx43(
2806
+ "span",
2807
+ {
2808
+ role: "status",
2809
+ "aria-label": `${definitionKey} is ${hasErrors ? "error" : isActive ? "active" : "inactive"}${isDirty ? ", unsaved changes" : ""}`,
2810
+ className: cn(
2811
+ "h-2 w-2 shrink-0 rounded-full border-[1.5px]",
2812
+ hasErrors ? isDirty ? "border-status-error bg-transparent" : "border-status-error bg-status-error" : isActive ? isDirty ? "border-status-active bg-transparent" : "border-status-active bg-status-active" : isDirty ? "border-status-inactive bg-transparent" : "border-status-inactive bg-status-inactive"
2813
+ )
2814
+ }
2815
+ ),
2816
+ /* @__PURE__ */ jsx43("span", { className: "flex-1 truncate font-mono text-sm", children: definitionKey }),
2817
+ /* @__PURE__ */ jsx43(Badge, { variant: "secondary", className: "text-xs tabular-nums", children: variationCount }),
2818
+ /* @__PURE__ */ jsx43(
2819
+ ConfirmDialog,
2820
+ {
2821
+ open: confirmOpen,
2822
+ onOpenChange: setConfirmOpen,
2823
+ title: "Delete definition?",
2824
+ description: /* @__PURE__ */ jsxs29(Fragment6, { children: [
2825
+ "This will permanently delete ",
2826
+ /* @__PURE__ */ jsx43("strong", { className: "font-mono", children: definitionKey }),
2827
+ " and all its variations. This action cannot be undone."
2828
+ ] }),
2829
+ actionLabel: "Delete",
2830
+ onConfirm: onRemove,
2831
+ children: /* @__PURE__ */ jsx43(
2832
+ Button,
2833
+ {
2834
+ variant: "ghost",
2835
+ size: "icon-xs",
2836
+ className: "text-destructive/60 opacity-0 hover:bg-destructive/10 hover:text-destructive group-hover:opacity-100",
2837
+ "aria-label": `Remove ${definitionKey}`,
2838
+ onClick: (e) => {
2839
+ e.stopPropagation();
2840
+ setConfirmOpen(true);
2841
+ },
2842
+ children: /* @__PURE__ */ jsx43(Trash22, { className: "h-3.5 w-3.5" })
2843
+ }
2844
+ )
2845
+ }
2846
+ )
2847
+ ]
2848
+ }
2849
+ );
2850
+ });
2851
+
2852
+ // src/components/definition-list/DefinitionList.tsx
2853
+ import { jsx as jsx44, jsxs as jsxs30 } from "react/jsx-runtime";
2854
+ function DefinitionList({
2855
+ definitions,
2856
+ selectedKey,
2857
+ validationErrors,
2858
+ dirtyKeys,
2859
+ onSelect,
2860
+ onAdd,
2861
+ onRemove
2862
+ }) {
2863
+ const [search, setSearch] = useState9("");
2864
+ const [adding, setAdding] = useState9(false);
2865
+ const [newKey, setNewKey] = useState9("");
2866
+ const keys = Object.keys(definitions).filter(
2867
+ (k) => k.toLowerCase().includes(search.toLowerCase())
2868
+ );
2869
+ async function handleAdd() {
2870
+ const trimmed = newKey.trim();
2871
+ if (trimmed && !(trimmed in definitions)) {
2872
+ try {
2873
+ await onAdd(trimmed);
2874
+ setNewKey("");
2875
+ setAdding(false);
2876
+ } catch {
2877
+ }
2878
+ }
2879
+ }
2880
+ return /* @__PURE__ */ jsxs30("div", { className: "flex h-full flex-col", children: [
2881
+ /* @__PURE__ */ jsx44("div", { className: "p-3", children: /* @__PURE__ */ jsxs30("div", { className: "relative", children: [
2882
+ /* @__PURE__ */ jsx44(Search, { className: "absolute left-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }),
2883
+ /* @__PURE__ */ jsx44(
2884
+ Input,
2885
+ {
2886
+ className: "h-9 pl-8 text-sm",
2887
+ placeholder: "Search definitions...",
2888
+ value: search,
2889
+ onChange: (e) => setSearch(e.target.value)
2890
+ }
2891
+ )
2892
+ ] }) }),
2893
+ /* @__PURE__ */ jsx44(ScrollArea, { className: "flex-1", children: /* @__PURE__ */ jsxs30("div", { className: "px-2", children: [
2894
+ keys.map((key) => /* @__PURE__ */ jsx44(
2895
+ DefinitionListItem,
2896
+ {
2897
+ definitionKey: key,
2898
+ variationCount: definitions[key].variations.length,
2899
+ isActive: definitions[key].active !== false,
2900
+ hasErrors: !!(validationErrors?.[key] && validationErrors[key].length > 0),
2901
+ isDirty: dirtyKeys?.includes(key),
2902
+ isSelected: key === selectedKey,
2903
+ onSelect: () => onSelect(key),
2904
+ onRemove: () => onRemove(key)
2905
+ },
2906
+ key
2907
+ )),
2908
+ keys.length === 0 && /* @__PURE__ */ jsx44("p", { className: "px-3 py-8 text-center text-sm text-muted-foreground", children: search ? "No definitions match" : "No definitions" })
2909
+ ] }) }),
2910
+ /* @__PURE__ */ jsx44("div", { className: "border-t border-border p-3", children: adding ? /* @__PURE__ */ jsxs30("div", { className: "space-y-2", children: [
2911
+ /* @__PURE__ */ jsx44(
2912
+ Input,
2913
+ {
2914
+ className: "h-9 w-full font-mono text-sm",
2915
+ placeholder: "definition-key",
2916
+ value: newKey,
2917
+ autoFocus: true,
2918
+ onChange: (e) => setNewKey(e.target.value),
2919
+ onKeyDown: (e) => {
2920
+ if (e.key === "Enter") handleAdd();
2921
+ if (e.key === "Escape") setAdding(false);
2922
+ }
2923
+ }
2924
+ ),
2925
+ /* @__PURE__ */ jsxs30("div", { className: "flex gap-2", children: [
2926
+ /* @__PURE__ */ jsx44(Button, { size: "sm", className: "h-8 flex-1", onClick: handleAdd, children: "Save" }),
2927
+ /* @__PURE__ */ jsx44(
2928
+ Button,
2929
+ {
2930
+ variant: "ghost",
2931
+ size: "sm",
2932
+ className: "h-8",
2933
+ "aria-label": "Cancel adding definition",
2934
+ onClick: () => {
2935
+ setAdding(false);
2936
+ setNewKey("");
2937
+ },
2938
+ children: /* @__PURE__ */ jsx44(X3, { className: "h-4 w-4" })
2939
+ }
2940
+ )
2941
+ ] })
2942
+ ] }) : /* @__PURE__ */ jsxs30(
2943
+ Button,
2944
+ {
2945
+ variant: "outline",
2946
+ size: "sm",
2947
+ className: "h-9 w-full",
2948
+ "aria-label": "Add new definition",
2949
+ onClick: () => setAdding(true),
2950
+ children: [
2951
+ /* @__PURE__ */ jsx44(Plus3, { className: "mr-1.5 h-4 w-4" }),
2952
+ "New definition"
2953
+ ]
2954
+ }
2955
+ ) })
2956
+ ] });
2957
+ }
2958
+
2959
+ // src/components/condition-builder/preset-ui.tsx
2960
+ import { useCallback as useCallback11, useMemo as useMemo10 } from "react";
2961
+ import { PRIMITIVE_TYPES } from "showwhat";
2962
+ import { jsx as jsx45, jsxs as jsxs31 } from "react/jsx-runtime";
2963
+ var TYPE_DEFAULTS = {
2964
+ string: { op: "eq", value: "" },
2965
+ number: { op: "eq", value: 0 },
2966
+ bool: { value: true },
2967
+ datetime: { op: "eq", value: (/* @__PURE__ */ new Date()).toISOString() }
2968
+ };
2969
+ function capitalize(s) {
2970
+ return s.charAt(0).toUpperCase() + s.slice(1);
2971
+ }
2972
+ function createPresetConditionMeta(presets) {
2973
+ return Object.entries(presets).map(([name, preset]) => {
2974
+ const isBuiltin = PRIMITIVE_TYPES.has(preset.type);
2975
+ const description = isBuiltin ? `Match the ${preset.key} key (${preset.type})` : `Custom ${preset.type} condition`;
2976
+ const baseDefaults = isBuiltin ? { ...TYPE_DEFAULTS[preset.type], key: preset.key } : {};
2977
+ return {
2978
+ type: name,
2979
+ label: capitalize(name),
2980
+ description,
2981
+ defaults: { ...baseDefaults, ...preset.defaults, type: name }
2982
+ };
2983
+ });
2984
+ }
2985
+ function createPresetEditor(presetName, builtinType, presetKey) {
2986
+ function PresetConditionEditor({ condition, onChange }) {
2987
+ const rec = useMemo10(() => condition, [condition]);
2988
+ const update = useCallback11(
2989
+ (field, value) => {
2990
+ onChange(
2991
+ buildCustomCondition({ ...rec, [field]: value, key: presetKey, type: presetName })
2992
+ );
2993
+ },
2994
+ [rec, onChange]
2995
+ );
2996
+ switch (builtinType) {
2997
+ case "string": {
2998
+ const op = rec.op;
2999
+ const isArray = op === "in" || op === "nin";
3000
+ const isRegex = op === "regex";
3001
+ const handleOpChange = (newOp) => {
3002
+ const isArrayOp = newOp === "in" || newOp === "nin";
3003
+ const currentValue = rec.value;
3004
+ const coercedValue = isArrayOp ? Array.isArray(currentValue) ? currentValue : currentValue ? [String(currentValue)] : [] : Array.isArray(currentValue) ? currentValue[0] ?? "" : currentValue;
3005
+ onChange(
3006
+ buildCustomCondition({
3007
+ ...rec,
3008
+ op: newOp,
3009
+ value: coercedValue,
3010
+ key: presetKey,
3011
+ type: presetName
3012
+ })
3013
+ );
3014
+ };
3015
+ return /* @__PURE__ */ jsxs31(ConditionRow, { children: [
3016
+ /* @__PURE__ */ jsx45(KeyInput, { value: presetKey, disabled: true }),
3017
+ /* @__PURE__ */ jsx45(
3018
+ OperatorSelect,
3019
+ {
3020
+ value: String(rec.op ?? "eq"),
3021
+ onChange: handleOpChange,
3022
+ options: OP_OPTIONS
3023
+ }
3024
+ ),
3025
+ isArray ? /* @__PURE__ */ jsx45(
3026
+ TagInput,
3027
+ {
3028
+ value: rec.value ?? "",
3029
+ onChange: (v) => update("value", v),
3030
+ placeholder: `e.g. ${presetKey} value`
3031
+ }
3032
+ ) : isRegex ? /* @__PURE__ */ jsx45(
3033
+ Input,
3034
+ {
3035
+ className: "h-8 font-mono text-sm",
3036
+ value: String(rec.value ?? ""),
3037
+ placeholder: "e.g. ^test.*$",
3038
+ onChange: (e) => update("value", e.target.value)
3039
+ }
3040
+ ) : /* @__PURE__ */ jsx45(
3041
+ Input,
3042
+ {
3043
+ className: "h-8 text-sm",
3044
+ value: String(rec.value ?? ""),
3045
+ placeholder: `e.g. ${presetKey} value`,
3046
+ onChange: (e) => update("value", e.target.value)
3047
+ }
3048
+ )
3049
+ ] });
3050
+ }
3051
+ case "number": {
3052
+ const numOp = rec.op;
3053
+ const isNumArray = numOp === "in" || numOp === "nin";
3054
+ const handleNumOpChange = (newOp) => {
3055
+ const isArrayOp = newOp === "in" || newOp === "nin";
3056
+ const currentValue = rec.value;
3057
+ const coercedValue = isArrayOp ? Array.isArray(currentValue) ? currentValue : currentValue !== void 0 && currentValue !== "" ? [Number(currentValue)] : [] : Array.isArray(currentValue) ? currentValue[0] ?? 0 : currentValue;
3058
+ onChange(
3059
+ buildCustomCondition({
3060
+ ...rec,
3061
+ op: newOp,
3062
+ value: coercedValue,
3063
+ key: presetKey,
3064
+ type: presetName
3065
+ })
3066
+ );
3067
+ };
3068
+ return /* @__PURE__ */ jsxs31(ConditionRow, { children: [
3069
+ /* @__PURE__ */ jsx45(KeyInput, { value: presetKey, disabled: true }),
3070
+ /* @__PURE__ */ jsx45(
3071
+ OperatorSelect,
3072
+ {
3073
+ value: String(rec.op ?? "eq"),
3074
+ onChange: handleNumOpChange,
3075
+ options: OP_OPTIONS2
3076
+ }
3077
+ ),
3078
+ isNumArray ? /* @__PURE__ */ jsx45(
3079
+ NumberTagInput,
3080
+ {
3081
+ value: rec.value ?? [],
3082
+ onChange: (v) => update("value", v),
3083
+ placeholder: `e.g. ${presetKey} value`
3084
+ }
3085
+ ) : /* @__PURE__ */ jsx45(
3086
+ Input,
3087
+ {
3088
+ type: "number",
3089
+ className: "h-8 font-mono text-sm",
3090
+ value: rec.value !== void 0 ? String(rec.value) : "",
3091
+ placeholder: "e.g. 100",
3092
+ onChange: (e) => update("value", e.target.value === "" ? "" : Number(e.target.value))
3093
+ }
3094
+ )
3095
+ ] });
3096
+ }
3097
+ case "bool":
3098
+ return /* @__PURE__ */ jsxs31(ConditionRow, { children: [
3099
+ /* @__PURE__ */ jsx45(KeyInput, { value: presetKey, disabled: true }),
3100
+ /* @__PURE__ */ jsx45(OperatorSelect, { value: "eq", options: OP_OPTIONS4, disabled: true }),
3101
+ /* @__PURE__ */ jsxs31(
3102
+ Select,
3103
+ {
3104
+ value: String(rec.value ?? "true"),
3105
+ onValueChange: (v) => update("value", v === "true"),
3106
+ children: [
3107
+ /* @__PURE__ */ jsx45(SelectTrigger, { className: "h-8 text-sm", children: /* @__PURE__ */ jsx45(SelectValue, {}) }),
3108
+ /* @__PURE__ */ jsxs31(SelectContent, { children: [
3109
+ /* @__PURE__ */ jsx45(SelectItem, { value: "true", children: "true" }),
3110
+ /* @__PURE__ */ jsx45(SelectItem, { value: "false", children: "false" })
3111
+ ] })
3112
+ ]
3113
+ }
3114
+ )
3115
+ ] });
3116
+ case "datetime":
3117
+ return /* @__PURE__ */ jsxs31(ConditionRow, { children: [
3118
+ /* @__PURE__ */ jsx45(KeyInput, { value: presetKey, disabled: true }),
3119
+ /* @__PURE__ */ jsx45(
3120
+ OperatorSelect,
3121
+ {
3122
+ value: String(rec.op ?? "eq"),
3123
+ onChange: (v) => update("op", v),
3124
+ options: OP_OPTIONS3
3125
+ }
3126
+ ),
3127
+ /* @__PURE__ */ jsx45(DateTimeInput, { value: String(rec.value ?? ""), onChange: (v) => update("value", v) })
3128
+ ] });
3129
+ }
3130
+ return null;
3131
+ }
3132
+ PresetConditionEditor.displayName = `PresetConditionEditor(${presetName})`;
3133
+ return PresetConditionEditor;
3134
+ }
3135
+ function createPresetUI(presets) {
3136
+ const extraConditionTypes = createPresetConditionMeta(presets);
3137
+ const editorOverrides = /* @__PURE__ */ new Map();
3138
+ for (const [name, preset] of Object.entries(presets)) {
3139
+ if (PRIMITIVE_TYPES.has(preset.type) && preset.key) {
3140
+ editorOverrides.set(name, createPresetEditor(name, preset.type, preset.key));
3141
+ }
3142
+ }
3143
+ return { extraConditionTypes, editorOverrides };
3144
+ }
3145
+
3146
+ // src/configurator/Configurator.tsx
3147
+ import { useMemo as useMemo12 } from "react";
3148
+
3149
+ // src/configurator/context.ts
3150
+ import { createContext as createContext2, useCallback as useCallback12, useContext as useContext2, useSyncExternalStore, useState as useState10 } from "react";
3151
+ var StoreSourceContext = createContext2(null);
3152
+ var ActionStateContext = createContext2(null);
3153
+ function useStoreSource() {
3154
+ const source = useContext2(StoreSourceContext);
3155
+ if (source) return source;
3156
+ throw new Error("useConfiguratorStore must be used within a <Configurator> component");
3157
+ }
3158
+ function useConfiguratorStore() {
3159
+ const source = useStoreSource();
3160
+ return useSyncExternalStore(source.subscribe, source.getSnapshot);
3161
+ }
3162
+ function useActionState() {
3163
+ const ctx = useContext2(ActionStateContext);
3164
+ if (ctx) return ctx;
3165
+ throw new Error("useActionState must be used within a <Configurator> component");
3166
+ }
3167
+ function useStoreRef() {
3168
+ const source = useStoreSource();
3169
+ return useCallback12(() => source.getSnapshot(), [source]);
3170
+ }
3171
+ function useActionRunner() {
3172
+ const [state, setState] = useState10({ pending: false, error: null });
3173
+ const run = useCallback12(async (action) => {
3174
+ setState({ pending: true, error: null });
3175
+ try {
3176
+ await action();
3177
+ setState({ pending: false, error: null });
3178
+ } catch (err) {
3179
+ setState({ pending: false, error: err instanceof Error ? err : new Error(String(err)) });
3180
+ throw err;
3181
+ }
3182
+ }, []);
3183
+ const clearError = useCallback12(() => setState((s) => ({ ...s, error: null })), []);
3184
+ return {
3185
+ actionState: state,
3186
+ runAction: run,
3187
+ clearError
3188
+ };
3189
+ }
3190
+
3191
+ // src/configurator/useConfiguratorSelector.ts
3192
+ import { useContext as useContext3, useCallback as useCallback13, useSyncExternalStore as useSyncExternalStore2 } from "react";
3193
+ function useConfiguratorSelector(selector) {
3194
+ const source = useContext3(StoreSourceContext);
3195
+ if (!source) {
3196
+ throw new Error("useConfiguratorSelector must be used within a <Configurator> component");
3197
+ }
3198
+ const getSnapshot = useCallback13(() => selector(source.getSnapshot()), [source, selector]);
3199
+ return useSyncExternalStore2(source.subscribe, getSnapshot);
3200
+ }
3201
+
3202
+ // src/configurator/PreviewPanel.tsx
3203
+ import { useCallback as useCallback14, useEffect, useMemo as useMemo11, useRef as useRef5, useState as useState11 } from "react";
3204
+ import { ChevronRight as ChevronRight2, Eye as Eye2, Loader2, Maximize2, Play } from "lucide-react";
3205
+ import { resolve } from "showwhat";
3206
+ import { DefinitionInactiveError, DefinitionNotFoundError, VariationNotFoundError } from "showwhat";
3207
+
3208
+ // src/configurator/selectors.ts
3209
+ var selectDefinitions = (s) => s.definitions;
3210
+ var selectSelectedKey = (s) => s.selectedKey;
3211
+ var selectValidationErrors = (s) => s.validationErrors;
3212
+ var selectDirtyKeys = (s) => s.dirtyKeys;
3213
+ var selectRevision = (s) => s.revision;
3214
+
3215
+ // src/configurator/fallback-context.ts
3216
+ import { createContext as createContext3, useContext as useContext4 } from "react";
3217
+ var FallbackEvaluatorContext = createContext3(null);
3218
+ var FallbackEvaluatorProvider = FallbackEvaluatorContext.Provider;
3219
+ function useFallbackEvaluator() {
3220
+ return useContext4(FallbackEvaluatorContext);
3221
+ }
3222
+
3223
+ // src/configurator/PreviewPanel.tsx
3224
+ import { Fragment as Fragment7, jsx as jsx46, jsxs as jsxs32 } from "react/jsx-runtime";
3225
+ function ResultBadge({ result, onViewMeta }) {
3226
+ switch (result.status) {
3227
+ case "success":
3228
+ return /* @__PURE__ */ jsxs32("div", { className: "space-y-2", children: [
3229
+ /* @__PURE__ */ jsxs32("div", { className: "flex items-center justify-between", children: [
3230
+ /* @__PURE__ */ jsx46(Badge, { className: "bg-status-active/15 text-green-700 hover:bg-status-active/15 dark:text-green-400", children: "Matched" }),
3231
+ onViewMeta ? /* @__PURE__ */ jsxs32(
3232
+ "button",
3233
+ {
3234
+ type: "button",
3235
+ className: "flex items-center gap-1 text-xs text-muted-foreground/60 hover:text-muted-foreground hover:cursor-pointer",
3236
+ onClick: onViewMeta,
3237
+ "aria-label": "View evaluation meta",
3238
+ children: [
3239
+ /* @__PURE__ */ jsx46(Eye2, { className: "size-3" }),
3240
+ /* @__PURE__ */ jsx46("span", { children: "Details" }),
3241
+ /* @__PURE__ */ jsx46("span", { children: "|" }),
3242
+ /* @__PURE__ */ jsxs32("span", { children: [
3243
+ "Variation #",
3244
+ result.meta.variation.index
3245
+ ] })
3246
+ ]
3247
+ }
3248
+ ) : /* @__PURE__ */ jsxs32("span", { className: "text-xs text-muted-foreground", children: [
3249
+ "Variation #",
3250
+ result.meta.variation.index
3251
+ ] })
3252
+ ] }),
3253
+ /* @__PURE__ */ jsx46("div", { className: "rounded-md border border-border bg-background p-2", children: /* @__PURE__ */ jsx46("pre", { className: "whitespace-pre-wrap break-all font-mono text-xs", children: typeof result.value === "string" ? result.value : JSON.stringify(result.value, null, 2) }) })
3254
+ ] });
3255
+ case "inactive":
3256
+ return /* @__PURE__ */ jsxs32("div", { className: "space-y-2", children: [
3257
+ /* @__PURE__ */ jsx46(Badge, { className: "bg-amber-500/15 text-amber-600 hover:bg-amber-500/15", children: "Inactive" }),
3258
+ /* @__PURE__ */ jsx46("p", { className: "text-xs text-muted-foreground", children: result.message })
3259
+ ] });
3260
+ case "no-match":
3261
+ return /* @__PURE__ */ jsxs32("div", { className: "space-y-2", children: [
3262
+ /* @__PURE__ */ jsx46(Badge, { className: "bg-orange-500/15 text-orange-600 hover:bg-orange-500/15", children: "No Match" }),
3263
+ /* @__PURE__ */ jsx46("p", { className: "text-xs text-muted-foreground", children: result.message })
3264
+ ] });
3265
+ case "error":
3266
+ return /* @__PURE__ */ jsxs32("div", { className: "space-y-2", children: [
3267
+ /* @__PURE__ */ jsx46(Badge, { variant: "destructive", children: "Error" }),
3268
+ /* @__PURE__ */ jsx46("p", { className: "text-xs text-destructive", children: result.message })
3269
+ ] });
3270
+ }
3271
+ }
3272
+ function parseContextJson(text) {
3273
+ const trimmed = text.trim();
3274
+ if (!trimmed) return {};
3275
+ const parsed = JSON.parse(trimmed);
3276
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
3277
+ throw new SyntaxError("Context must be a JSON object");
3278
+ }
3279
+ const context = {};
3280
+ for (const [key, value] of Object.entries(parsed)) {
3281
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
3282
+ context[key] = value;
3283
+ }
3284
+ }
3285
+ return context;
3286
+ }
3287
+ function parseEvaluatorOverrides(text) {
3288
+ const overrides = {};
3289
+ for (const line of text.split("\n")) {
3290
+ const trimmed = line.trim();
3291
+ if (!trimmed) continue;
3292
+ const idx = trimmed.indexOf(":");
3293
+ if (idx < 1) continue;
3294
+ const type = trimmed.slice(0, idx).trim();
3295
+ const value = trimmed.slice(idx + 1).trim().toLowerCase();
3296
+ if (type && (value === "true" || value === "false")) {
3297
+ overrides[type] = value === "true";
3298
+ }
3299
+ }
3300
+ return overrides;
3301
+ }
3302
+ function JsonEditorDialog({
3303
+ open,
3304
+ onOpenChange,
3305
+ value,
3306
+ onSave
3307
+ }) {
3308
+ const [draft, setDraft] = useState11(value);
3309
+ const [formatError, setFormatError] = useState11(null);
3310
+ useEffect(() => {
3311
+ if (open) {
3312
+ setDraft(value);
3313
+ setFormatError(null);
3314
+ }
3315
+ }, [open, value]);
3316
+ function handleFormat() {
3317
+ const trimmed = draft.trim();
3318
+ if (!trimmed) return;
3319
+ try {
3320
+ const parsed = JSON.parse(trimmed);
3321
+ setDraft(JSON.stringify(parsed, null, 2));
3322
+ setFormatError(null);
3323
+ } catch {
3324
+ setFormatError("Invalid JSON");
3325
+ }
3326
+ }
3327
+ function handleSave() {
3328
+ onSave(draft);
3329
+ onOpenChange(false);
3330
+ }
3331
+ return /* @__PURE__ */ jsx46(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs32(DialogContent, { className: "sm:max-w-2xl", children: [
3332
+ /* @__PURE__ */ jsxs32(DialogHeader, { children: [
3333
+ /* @__PURE__ */ jsx46(DialogTitle, { children: "Edit Context" }),
3334
+ /* @__PURE__ */ jsx46(DialogDescription, { children: "JSON object with context values for resolving the definition." })
3335
+ ] }),
3336
+ /* @__PURE__ */ jsx46(
3337
+ Textarea,
3338
+ {
3339
+ placeholder: '{\n "env": "production",\n "region": "us-east-1"\n}',
3340
+ value: draft,
3341
+ onChange: (e) => {
3342
+ setDraft(e.target.value);
3343
+ setFormatError(null);
3344
+ },
3345
+ className: `min-h-64 font-mono text-sm ${formatError ? "border-destructive" : ""}`,
3346
+ rows: 14
3347
+ }
3348
+ ),
3349
+ formatError && /* @__PURE__ */ jsx46("p", { className: "text-xs text-destructive", children: formatError }),
3350
+ /* @__PURE__ */ jsxs32(DialogFooter, { children: [
3351
+ /* @__PURE__ */ jsx46(Button, { variant: "outline", size: "sm", onClick: handleFormat, children: "Format" }),
3352
+ /* @__PURE__ */ jsx46(Button, { size: "sm", onClick: handleSave, children: "Apply" })
3353
+ ] })
3354
+ ] }) });
3355
+ }
3356
+ function PreviewPanel() {
3357
+ const definitions = useConfiguratorSelector(selectDefinitions);
3358
+ const selectedKey = useConfiguratorSelector(selectSelectedKey);
3359
+ const externalFallback = useFallbackEvaluator();
3360
+ const [contextText, setContextText] = useState11("");
3361
+ const [evaluatorText, setEvaluatorText] = useState11("");
3362
+ const [contextError, setContextError] = useState11(null);
3363
+ const [simulatorOpen, setSimulatorOpen] = useState11(false);
3364
+ const [jsonEditorOpen, setJsonEditorOpen] = useState11(false);
3365
+ const [previewResult, setPreviewResult] = useState11(null);
3366
+ const [isResolving, setIsResolving] = useState11(false);
3367
+ const [metaDialogOpen, setMetaDialogOpen] = useState11(false);
3368
+ const abortControllerRef = useRef5(null);
3369
+ useEffect(() => {
3370
+ abortControllerRef.current?.abort();
3371
+ abortControllerRef.current = null;
3372
+ setPreviewResult(null);
3373
+ return () => {
3374
+ abortControllerRef.current?.abort();
3375
+ };
3376
+ }, [selectedKey]);
3377
+ const fallback = useMemo11(() => {
3378
+ const overrides = parseEvaluatorOverrides(evaluatorText);
3379
+ const hasOverrides = Object.keys(overrides).length > 0;
3380
+ if (!hasOverrides && !externalFallback) return void 0;
3381
+ return async (args) => {
3382
+ const type = args.condition.type;
3383
+ if (type in overrides) return overrides[type];
3384
+ if (externalFallback) return externalFallback(args);
3385
+ return false;
3386
+ };
3387
+ }, [evaluatorText, externalFallback]);
3388
+ const handleResolve = useCallback14(async () => {
3389
+ if (!selectedKey || !definitions[selectedKey]) {
3390
+ setPreviewResult({ status: "error", message: "No definition selected" });
3391
+ return;
3392
+ }
3393
+ abortControllerRef.current?.abort();
3394
+ const controller = new AbortController();
3395
+ abortControllerRef.current = controller;
3396
+ setIsResolving(true);
3397
+ setContextError(null);
3398
+ let context;
3399
+ try {
3400
+ context = parseContextJson(contextText);
3401
+ } catch {
3402
+ setPreviewResult({ status: "error", message: "Invalid JSON in context" });
3403
+ setIsResolving(false);
3404
+ return;
3405
+ }
3406
+ try {
3407
+ const result = await resolve({
3408
+ definitions: { [selectedKey]: definitions[selectedKey] },
3409
+ context,
3410
+ options: fallback ? { fallback } : void 0
3411
+ });
3412
+ if (controller.signal.aborted) return;
3413
+ const resolution = result[selectedKey];
3414
+ setPreviewResult({
3415
+ status: "success",
3416
+ value: resolution.value,
3417
+ meta: resolution.meta
3418
+ });
3419
+ } catch (err) {
3420
+ if (controller.signal.aborted) return;
3421
+ if (err instanceof DefinitionInactiveError) {
3422
+ setPreviewResult({
3423
+ status: "inactive",
3424
+ message: `"${selectedKey}" is inactive`
3425
+ });
3426
+ } else if (err instanceof VariationNotFoundError) {
3427
+ setPreviewResult({
3428
+ status: "no-match",
3429
+ message: "No variation matched the given context"
3430
+ });
3431
+ } else if (err instanceof DefinitionNotFoundError) {
3432
+ setPreviewResult({
3433
+ status: "error",
3434
+ message: `Definition "${selectedKey}" not found`
3435
+ });
3436
+ } else {
3437
+ setPreviewResult({
3438
+ status: "error",
3439
+ message: err instanceof Error ? err.message : "Unknown error"
3440
+ });
3441
+ }
3442
+ } finally {
3443
+ if (!controller.signal.aborted) {
3444
+ setIsResolving(false);
3445
+ }
3446
+ }
3447
+ }, [selectedKey, definitions, contextText, fallback]);
3448
+ return /* @__PURE__ */ jsxs32("div", { className: "flex w-80 shrink-0 flex-col border-l border-border bg-muted/30", children: [
3449
+ /* @__PURE__ */ jsx46("div", { className: "flex h-12 items-center border-b border-border px-3", children: /* @__PURE__ */ jsx46("span", { className: "text-sm font-semibold", children: "Preview" }) }),
3450
+ /* @__PURE__ */ jsx46(ScrollArea, { className: "flex-1", children: /* @__PURE__ */ jsxs32("div", { className: "space-y-4 p-3", children: [
3451
+ /* @__PURE__ */ jsxs32("div", { children: [
3452
+ /* @__PURE__ */ jsx46(Label, { className: "text-xs text-muted-foreground", children: "Definition" }),
3453
+ /* @__PURE__ */ jsx46("p", { className: "mt-0.5 truncate font-mono text-sm", children: selectedKey ?? "None selected" })
3454
+ ] }),
3455
+ /* @__PURE__ */ jsx46(Separator, {}),
3456
+ /* @__PURE__ */ jsxs32("div", { children: [
3457
+ /* @__PURE__ */ jsxs32("div", { className: "flex items-center justify-between", children: [
3458
+ /* @__PURE__ */ jsx46(Label, { className: "text-xs text-muted-foreground", children: "Context" }),
3459
+ /* @__PURE__ */ jsxs32(
3460
+ "button",
3461
+ {
3462
+ type: "button",
3463
+ className: "flex items-center gap-1 text-[10px] text-muted-foreground hover:text-foreground",
3464
+ onClick: () => setJsonEditorOpen(true),
3465
+ children: [
3466
+ /* @__PURE__ */ jsx46(Maximize2, { className: "h-3 w-3" }),
3467
+ "Edit"
3468
+ ]
3469
+ }
3470
+ )
3471
+ ] }),
3472
+ /* @__PURE__ */ jsx46(
3473
+ "button",
3474
+ {
3475
+ type: "button",
3476
+ className: `mt-1.5 w-full cursor-pointer rounded-md border bg-transparent px-3 py-2 text-left transition-colors hover:border-ring ${contextError ? "border-destructive" : "border-input"}`,
3477
+ onClick: () => setJsonEditorOpen(true),
3478
+ children: contextText.trim() ? /* @__PURE__ */ jsx46("pre", { className: "max-h-28 overflow-hidden font-mono text-xs text-foreground", children: contextText }) : /* @__PURE__ */ jsx46("span", { className: "font-mono text-xs text-muted-foreground", children: '{ "env": "production" }' })
3479
+ }
3480
+ ),
3481
+ contextError && /* @__PURE__ */ jsx46("p", { className: "mt-1 text-[10px] text-destructive", children: contextError }),
3482
+ /* @__PURE__ */ jsx46(
3483
+ JsonEditorDialog,
3484
+ {
3485
+ open: jsonEditorOpen,
3486
+ onOpenChange: setJsonEditorOpen,
3487
+ value: contextText,
3488
+ onSave: (v) => {
3489
+ setContextText(v);
3490
+ setContextError(null);
3491
+ }
3492
+ }
3493
+ )
3494
+ ] }),
3495
+ /* @__PURE__ */ jsx46(Separator, {}),
3496
+ /* @__PURE__ */ jsxs32("div", { children: [
3497
+ /* @__PURE__ */ jsxs32(
3498
+ "button",
3499
+ {
3500
+ type: "button",
3501
+ className: "flex w-full items-center gap-1 text-xs text-muted-foreground hover:text-foreground",
3502
+ onClick: () => setSimulatorOpen((o) => !o),
3503
+ children: [
3504
+ /* @__PURE__ */ jsx46(
3505
+ ChevronRight2,
3506
+ {
3507
+ className: `h-3.5 w-3.5 transition-transform ${simulatorOpen ? "rotate-90" : ""}`
3508
+ }
3509
+ ),
3510
+ "Condition Simulator"
3511
+ ]
3512
+ }
3513
+ ),
3514
+ simulatorOpen && /* @__PURE__ */ jsxs32("div", { className: "mt-2", children: [
3515
+ /* @__PURE__ */ jsx46(
3516
+ Textarea,
3517
+ {
3518
+ placeholder: "tier:true\ngeo:false",
3519
+ value: evaluatorText,
3520
+ onChange: (e) => setEvaluatorText(e.target.value),
3521
+ className: "min-h-20 font-mono text-xs",
3522
+ rows: 6
3523
+ }
3524
+ ),
3525
+ /* @__PURE__ */ jsx46("p", { className: "mt-1 text-[10px] text-muted-foreground", children: "Simulate unregistered condition types. One type:true|false per line" })
3526
+ ] })
3527
+ ] }),
3528
+ /* @__PURE__ */ jsxs32(
3529
+ Button,
3530
+ {
3531
+ size: "sm",
3532
+ className: "w-full",
3533
+ onClick: handleResolve,
3534
+ disabled: !selectedKey || isResolving,
3535
+ children: [
3536
+ isResolving ? /* @__PURE__ */ jsx46(Loader2, { className: "mr-1.5 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx46(Play, { className: "mr-1.5 h-4 w-4" }),
3537
+ "Resolve"
3538
+ ]
3539
+ }
3540
+ ),
3541
+ previewResult && /* @__PURE__ */ jsxs32(Fragment7, { children: [
3542
+ /* @__PURE__ */ jsx46(Separator, {}),
3543
+ /* @__PURE__ */ jsxs32("div", { className: "animate-slide-in-right", children: [
3544
+ /* @__PURE__ */ jsx46(Label, { className: "text-xs text-muted-foreground", children: "Result" }),
3545
+ /* @__PURE__ */ jsx46("div", { className: "mt-1.5", children: /* @__PURE__ */ jsx46(
3546
+ ResultBadge,
3547
+ {
3548
+ result: previewResult,
3549
+ onViewMeta: previewResult.status === "success" ? () => setMetaDialogOpen(true) : void 0
3550
+ }
3551
+ ) }),
3552
+ previewResult.status === "success" && /* @__PURE__ */ jsx46(Dialog, { open: metaDialogOpen, onOpenChange: setMetaDialogOpen, children: /* @__PURE__ */ jsxs32(DialogContent, { className: "sm:max-w-lg", children: [
3553
+ /* @__PURE__ */ jsxs32(DialogHeader, { children: [
3554
+ /* @__PURE__ */ jsx46(DialogTitle, { children: "Evaluation Meta" }),
3555
+ /* @__PURE__ */ jsx46(DialogDescription, { children: "Full resolution metadata from the last evaluation." })
3556
+ ] }),
3557
+ /* @__PURE__ */ jsx46(ScrollArea, { className: "max-h-96", children: /* @__PURE__ */ jsx46("pre", { className: "whitespace-pre-wrap break-all font-mono text-xs", children: JSON.stringify(previewResult.meta, null, 2) }) })
3558
+ ] }) })
3559
+ ] })
3560
+ ] })
3561
+ ] }) })
3562
+ ] });
3563
+ }
3564
+
3565
+ // src/configurator/Configurator.tsx
3566
+ import { Fragment as Fragment8, jsx as jsx47, jsxs as jsxs33 } from "react/jsx-runtime";
3567
+ function useNormalizedSource(store) {
3568
+ return useMemo12(() => {
3569
+ if (isStoreSource(store)) {
3570
+ return store;
3571
+ }
3572
+ const snapshot = store;
3573
+ return {
3574
+ getSnapshot: () => snapshot,
3575
+ subscribe: () => () => {
3576
+ }
3577
+ };
3578
+ }, [store]);
3579
+ }
3580
+ function isStoreSource(obj) {
3581
+ return typeof obj === "object" && obj !== null && "getSnapshot" in obj && "subscribe" in obj && typeof obj.getSnapshot === "function" && typeof obj.subscribe === "function";
3582
+ }
3583
+ function ErrorBanner() {
3584
+ const { actionState, clearError } = useActionState();
3585
+ if (!actionState.error) return null;
3586
+ return /* @__PURE__ */ jsxs33(
3587
+ "div",
3588
+ {
3589
+ role: "alert",
3590
+ className: "flex items-center justify-between border-b border-destructive/30 bg-destructive/10 px-4 py-2",
3591
+ children: [
3592
+ /* @__PURE__ */ jsxs33("p", { className: "text-xs font-medium text-destructive", children: [
3593
+ "Action failed: ",
3594
+ actionState.error.message
3595
+ ] }),
3596
+ /* @__PURE__ */ jsx47("button", { type: "button", className: "text-xs text-destructive underline", onClick: clearError, children: "Dismiss" })
3597
+ ]
3598
+ }
3599
+ );
3600
+ }
3601
+ function EditorLayout({ emptyState }) {
3602
+ const definitions = useConfiguratorSelector(selectDefinitions);
3603
+ const selectedKey = useConfiguratorSelector(selectSelectedKey);
3604
+ const validationErrors = useConfiguratorSelector(selectValidationErrors);
3605
+ const dirtyKeys = useConfiguratorSelector(selectDirtyKeys);
3606
+ const revision = useConfiguratorSelector(selectRevision);
3607
+ const getStore = useStoreRef();
3608
+ const { actionState, runAction } = useActionState();
3609
+ const selectedDefinition = selectedKey ? definitions[selectedKey] : null;
3610
+ if (Object.keys(definitions).length === 0 && emptyState) {
3611
+ return /* @__PURE__ */ jsx47(Fragment8, { children: emptyState });
3612
+ }
3613
+ return /* @__PURE__ */ jsxs33("div", { className: "flex h-full flex-col", children: [
3614
+ /* @__PURE__ */ jsx47(ErrorBanner, {}),
3615
+ /* @__PURE__ */ jsxs33("div", { className: "flex flex-1 overflow-hidden", children: [
3616
+ /* @__PURE__ */ jsx47("div", { className: "w-72 shrink-0 border-r border-border bg-muted/30", children: /* @__PURE__ */ jsx47(
3617
+ DefinitionList,
3618
+ {
3619
+ definitions,
3620
+ selectedKey,
3621
+ validationErrors,
3622
+ dirtyKeys,
3623
+ onSelect: (key) => {
3624
+ runAction(() => getStore().selectDefinition(key)).catch(() => {
3625
+ });
3626
+ },
3627
+ onAdd: (key) => runAction(() => getStore().addDefinition(key)),
3628
+ onRemove: (key) => {
3629
+ runAction(() => getStore().removeDefinition(key)).catch(() => {
3630
+ });
3631
+ }
3632
+ }
3633
+ ) }),
3634
+ /* @__PURE__ */ jsx47("div", { className: "flex-1 overflow-hidden bg-background", children: /* @__PURE__ */ jsx47(ErrorBoundary, { children: selectedDefinition && selectedKey ? /* @__PURE__ */ jsx47("div", { className: "h-full animate-fade-up", children: /* @__PURE__ */ jsx47(
3635
+ DefinitionEditor,
3636
+ {
3637
+ definitionKey: selectedKey,
3638
+ definition: selectedDefinition,
3639
+ validationErrors: validationErrors[selectedKey],
3640
+ isDirty: dirtyKeys.includes(selectedKey),
3641
+ isPending: actionState.pending,
3642
+ onUpdate: (def) => {
3643
+ getStore().updateDefinition(selectedKey, def).catch(() => {
3644
+ });
3645
+ },
3646
+ onRename: (newKey) => runAction(() => getStore().renameDefinition(selectedKey, newKey)),
3647
+ onSave: () => {
3648
+ runAction(() => getStore().saveDefinition(selectedKey)).catch(() => {
3649
+ });
3650
+ },
3651
+ onDiscard: () => {
3652
+ runAction(() => getStore().discardDefinition(selectedKey)).catch(() => {
3653
+ });
3654
+ }
3655
+ },
3656
+ `${selectedKey}-${revision}`
3657
+ ) }, `anim-${selectedKey}-${revision}`) : /* @__PURE__ */ jsx47("div", { className: "flex h-full items-center justify-center text-sm text-muted-foreground", children: "Select a definition to edit" }) }) }),
3658
+ /* @__PURE__ */ jsx47(ErrorBoundary, { children: /* @__PURE__ */ jsx47(PreviewPanel, {}) })
3659
+ ] })
3660
+ ] });
3661
+ }
3662
+ function Configurator({
3663
+ store,
3664
+ className,
3665
+ emptyState,
3666
+ conditionExtensions,
3667
+ fallbackEvaluator
3668
+ }) {
3669
+ const runner = useActionRunner();
3670
+ const storeSource = useNormalizedSource(store);
3671
+ return /* @__PURE__ */ jsx47(StoreSourceContext.Provider, { value: storeSource, children: /* @__PURE__ */ jsx47(ActionStateContext.Provider, { value: runner, children: /* @__PURE__ */ jsx47(ConditionExtensionsProvider, { value: conditionExtensions ?? null, children: /* @__PURE__ */ jsx47(FallbackEvaluatorProvider, { value: fallbackEvaluator ?? null, children: /* @__PURE__ */ jsx47("div", { className, children: /* @__PURE__ */ jsx47(EditorLayout, { emptyState }) }) }) }) }) });
3672
+ }
3673
+ export {
3674
+ AUTO_ID_PREFIX,
3675
+ ActionStateContext,
3676
+ BUILTIN_CONDITION_TYPES,
3677
+ Badge,
3678
+ Button,
3679
+ CONDITION_TYPE_MAP,
3680
+ ConditionBuilder,
3681
+ Configurator,
3682
+ ConfirmDialog,
3683
+ DateTimeInput,
3684
+ DefinitionEditor,
3685
+ DefinitionList,
3686
+ Dialog,
3687
+ DialogClose,
3688
+ DialogContent,
3689
+ DialogDescription,
3690
+ DialogFooter,
3691
+ DialogHeader,
3692
+ DialogTitle,
3693
+ DialogTrigger,
3694
+ DropdownMenu,
3695
+ DropdownMenuContent,
3696
+ DropdownMenuItem,
3697
+ DropdownMenuSeparator,
3698
+ DropdownMenuTrigger,
3699
+ ErrorBoundary,
3700
+ Input,
3701
+ Label,
3702
+ Popover,
3703
+ PopoverContent,
3704
+ PopoverTrigger,
3705
+ ScrollArea,
3706
+ ScrollBar,
3707
+ Select,
3708
+ SelectContent,
3709
+ SelectGroup,
3710
+ SelectItem,
3711
+ SelectLabel,
3712
+ SelectSeparator,
3713
+ SelectTrigger,
3714
+ SelectValue,
3715
+ Separator,
3716
+ StoreSourceContext,
3717
+ Switch,
3718
+ Tabs,
3719
+ TabsContent,
3720
+ TabsList,
3721
+ TabsTrigger,
3722
+ Textarea,
3723
+ ThemeToggle,
3724
+ ValidationMessage,
3725
+ ValueInput,
3726
+ VariationCard,
3727
+ VariationList,
3728
+ badgeVariants,
3729
+ buttonVariants,
3730
+ cn,
3731
+ createPresetConditionMeta,
3732
+ createPresetUI,
3733
+ getConditionMeta,
3734
+ isAutoId,
3735
+ stripAutoIds,
3736
+ useActionState,
3737
+ useConfiguratorSelector,
3738
+ useConfiguratorStore,
3739
+ useStoreRef
3740
+ };
3741
+ //# sourceMappingURL=index.js.map