@almadar/ui 5.29.0 → 5.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/avl/index.cjs +933 -6749
  2. package/dist/avl/index.js +934 -6750
  3. package/dist/components/core/atoms/Card.d.ts +2 -0
  4. package/dist/components/core/atoms/Input.d.ts +2 -0
  5. package/dist/components/core/atoms/Select.d.ts +18 -6
  6. package/dist/components/core/atoms/Spinner.d.ts +2 -0
  7. package/dist/components/core/atoms/index.d.ts +1 -1
  8. package/dist/components/core/molecules/DocumentViewer.d.ts +0 -2
  9. package/dist/components/core/molecules/Header.d.ts +0 -4
  10. package/dist/components/core/molecules/JsonTreeEditor.d.ts +3 -8
  11. package/dist/components/core/molecules/Menu.d.ts +4 -0
  12. package/dist/components/core/molecules/Navigation.d.ts +0 -2
  13. package/dist/components/core/molecules/PageHeader.d.ts +0 -2
  14. package/dist/components/core/molecules/PropertyInspector.d.ts +8 -1
  15. package/dist/components/core/molecules/index.d.ts +1 -1
  16. package/dist/components/core/organisms/index.d.ts +0 -1
  17. package/dist/components/core/templates/index.d.ts +0 -3
  18. package/dist/components/game/molecules/index.d.ts +0 -1
  19. package/dist/components/game/molecules/three/index.cjs +27 -2
  20. package/dist/components/game/molecules/three/index.js +27 -2
  21. package/dist/components/game/molecules/three/patterns.d.ts +20 -0
  22. package/dist/components/game/organisms/TraitSlot.d.ts +3 -1
  23. package/dist/components/game/organisms/types/isometric.d.ts +2 -0
  24. package/dist/components/index.cjs +1198 -1062
  25. package/dist/components/index.js +1201 -1064
  26. package/dist/docs/index.cjs +205 -172
  27. package/dist/docs/index.d.cts +4 -0
  28. package/dist/docs/index.js +146 -113
  29. package/dist/marketing/index.cjs +91 -54
  30. package/dist/marketing/index.d.cts +2 -0
  31. package/dist/marketing/index.js +58 -21
  32. package/dist/providers/index.cjs +563 -275
  33. package/dist/providers/index.js +563 -275
  34. package/dist/renderer/pattern-resolver.d.ts +2 -5
  35. package/dist/runtime/index.cjs +563 -275
  36. package/dist/runtime/index.js +563 -275
  37. package/package.json +16 -2
@@ -45,7 +45,6 @@ var utilities = require('@dnd-kit/utilities');
45
45
  var react = require('@xyflow/react');
46
46
  var context = require('@almadar/ui/context');
47
47
  var patterns = require('@almadar/patterns');
48
- var fiber = require('@react-three/fiber');
49
48
 
50
49
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
51
50
 
@@ -2187,6 +2186,8 @@ var init_Input = __esm({
2187
2186
  className,
2188
2187
  inputType,
2189
2188
  type: htmlType,
2189
+ label,
2190
+ helperText,
2190
2191
  error,
2191
2192
  leftIcon,
2192
2193
  rightIcon,
@@ -2242,82 +2243,95 @@ var init_Input = __esm({
2242
2243
  onClear?.();
2243
2244
  }
2244
2245
  };
2246
+ const wrapField = (field) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full", children: [
2247
+ label && /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-foreground mb-1", children: label }),
2248
+ field,
2249
+ (helperText || error) && /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn("mt-1 text-xs", error ? "text-error" : "text-muted-foreground"), children: error ?? helperText })
2250
+ ] });
2245
2251
  if (type === "select") {
2246
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
2247
- resolvedLeftIcon && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
2248
- /* @__PURE__ */ jsxRuntime.jsxs(
2249
- "select",
2252
+ return wrapField(
2253
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
2254
+ resolvedLeftIcon && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
2255
+ /* @__PURE__ */ jsxRuntime.jsxs(
2256
+ "select",
2257
+ {
2258
+ ref,
2259
+ value,
2260
+ onChange: handleChange,
2261
+ className: cn(baseClassName, "appearance-none pr-10", className),
2262
+ ...props,
2263
+ children: [
2264
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: t("form.selectPlaceholder", { label: "" }) }),
2265
+ options?.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.value, children: opt.label }, opt.value))
2266
+ ]
2267
+ }
2268
+ ),
2269
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: "chevron-down", className: "h-icon-default w-icon-default" }) })
2270
+ ] })
2271
+ );
2272
+ }
2273
+ if (type === "textarea") {
2274
+ return wrapField(
2275
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
2276
+ "textarea",
2250
2277
  {
2251
2278
  ref,
2252
2279
  value,
2253
2280
  onChange: handleChange,
2254
- className: cn(baseClassName, "appearance-none pr-10", className),
2255
- ...props,
2256
- children: [
2257
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: t("form.selectPlaceholder", { label: "" }) }),
2258
- options?.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.value, children: opt.label }, opt.value))
2259
- ]
2281
+ rows: rows2,
2282
+ className: baseClassName,
2283
+ ...props
2260
2284
  }
2261
- ),
2262
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: "chevron-down", className: "h-icon-default w-icon-default" }) })
2263
- ] });
2264
- }
2265
- if (type === "textarea") {
2266
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
2267
- "textarea",
2268
- {
2269
- ref,
2270
- value,
2271
- onChange: handleChange,
2272
- rows: rows2,
2273
- className: baseClassName,
2274
- ...props
2275
- }
2276
- ) });
2285
+ ) })
2286
+ );
2277
2287
  }
2278
2288
  if (type === "checkbox") {
2279
- return /* @__PURE__ */ jsxRuntime.jsx(
2280
- "input",
2281
- {
2282
- ref,
2283
- type: "checkbox",
2284
- checked: props.checked,
2285
- onChange: handleChange,
2286
- className: cn(
2287
- "h-icon-default w-icon-default rounded-sm",
2288
- "border-border",
2289
- "text-primary focus:ring-ring",
2290
- "disabled:opacity-50 disabled:cursor-not-allowed",
2291
- className
2292
- ),
2293
- ...props
2294
- }
2289
+ return wrapField(
2290
+ /* @__PURE__ */ jsxRuntime.jsx(
2291
+ "input",
2292
+ {
2293
+ ref,
2294
+ type: "checkbox",
2295
+ checked: props.checked,
2296
+ onChange: handleChange,
2297
+ className: cn(
2298
+ "h-icon-default w-icon-default rounded-sm",
2299
+ "border-border",
2300
+ "text-primary focus:ring-ring",
2301
+ "disabled:opacity-50 disabled:cursor-not-allowed",
2302
+ className
2303
+ ),
2304
+ ...props
2305
+ }
2306
+ )
2295
2307
  );
2296
2308
  }
2297
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
2298
- resolvedLeftIcon && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
2299
- /* @__PURE__ */ jsxRuntime.jsx(
2300
- "input",
2301
- {
2302
- ref,
2303
- type,
2304
- value,
2305
- onChange: handleChange,
2306
- className: baseClassName,
2307
- ...props
2308
- }
2309
- ),
2310
- showClearButton && /* @__PURE__ */ jsxRuntime.jsx(
2311
- "button",
2312
- {
2313
- type: "button",
2314
- onClick: handleClear,
2315
- className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground hover:text-foreground",
2316
- children: /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: "x", className: "h-icon-default w-icon-default" })
2317
- }
2318
- ),
2319
- rightIcon && !showClearButton && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground", children: resolveIconNode(rightIcon, iconCls) })
2320
- ] });
2309
+ return wrapField(
2310
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
2311
+ resolvedLeftIcon && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
2312
+ /* @__PURE__ */ jsxRuntime.jsx(
2313
+ "input",
2314
+ {
2315
+ ref,
2316
+ type,
2317
+ value,
2318
+ onChange: handleChange,
2319
+ className: baseClassName,
2320
+ ...props
2321
+ }
2322
+ ),
2323
+ showClearButton && /* @__PURE__ */ jsxRuntime.jsx(
2324
+ "button",
2325
+ {
2326
+ type: "button",
2327
+ onClick: handleClear,
2328
+ className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground hover:text-foreground",
2329
+ children: /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: "x", className: "h-icon-default w-icon-default" })
2330
+ }
2331
+ ),
2332
+ rightIcon && !showClearButton && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground", children: resolveIconNode(rightIcon, iconCls) })
2333
+ ] })
2334
+ );
2321
2335
  }
2322
2336
  );
2323
2337
  exports.Input.displayName = "Input";
@@ -2388,6 +2402,190 @@ var init_Textarea = __esm({
2388
2402
  exports.Textarea.displayName = "Textarea";
2389
2403
  }
2390
2404
  });
2405
+ function flatOptions(opts, groups) {
2406
+ const flat = opts ?? [];
2407
+ const grp = (groups ?? []).flatMap((g) => g.options);
2408
+ return [...flat, ...grp];
2409
+ }
2410
+ function NativeSelect({
2411
+ className,
2412
+ options,
2413
+ groups,
2414
+ placeholder,
2415
+ error,
2416
+ onChange,
2417
+ value,
2418
+ ...props
2419
+ }) {
2420
+ const eventBus = useEventBus();
2421
+ const handleChange = (e) => {
2422
+ if (typeof onChange === "string") {
2423
+ eventBus.emit(`UI:${onChange}`, { value: e.target.value });
2424
+ } else {
2425
+ onChange?.(e.target.value);
2426
+ }
2427
+ };
2428
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
2429
+ /* @__PURE__ */ jsxRuntime.jsxs(
2430
+ "select",
2431
+ {
2432
+ onChange: handleChange,
2433
+ value,
2434
+ className: cn(
2435
+ "block w-full border-[length:var(--border-width)] shadow-sm appearance-none",
2436
+ "px-3 py-2 pr-10 text-sm text-foreground font-medium",
2437
+ "bg-card",
2438
+ "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
2439
+ "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
2440
+ error ? "border-error focus:border-error" : "border-border focus:border-primary",
2441
+ className
2442
+ ),
2443
+ ...props,
2444
+ children: [
2445
+ placeholder && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: placeholder }),
2446
+ options?.map((option) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)),
2447
+ groups?.map((group) => /* @__PURE__ */ jsxRuntime.jsx("optgroup", { label: group.label, children: group.options.map((option) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)) }, group.label))
2448
+ ]
2449
+ }
2450
+ ),
2451
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: "chevron-down", className: "h-icon-default w-icon-default text-foreground" }) })
2452
+ ] });
2453
+ }
2454
+ function RichSelect({
2455
+ className,
2456
+ options,
2457
+ groups,
2458
+ placeholder,
2459
+ error,
2460
+ onChange,
2461
+ value,
2462
+ multiple,
2463
+ searchable,
2464
+ clearable,
2465
+ disabled
2466
+ }) {
2467
+ const eventBus = useEventBus();
2468
+ const [open, setOpen] = React74.useState(false);
2469
+ const [search, setSearch] = React74.useState("");
2470
+ const containerRef = React74.useRef(null);
2471
+ const selected = multiple ? Array.isArray(value) ? value : value ? [value] : [] : value ? [value] : [];
2472
+ const all = flatOptions(options, groups);
2473
+ const filtered = searchable && search ? all.filter((o) => o.label.toLowerCase().includes(search.toLowerCase())) : all;
2474
+ const toggle = (optValue) => {
2475
+ let next;
2476
+ if (multiple) {
2477
+ next = selected.includes(optValue) ? selected.filter((v) => v !== optValue) : [...selected, optValue];
2478
+ } else {
2479
+ next = optValue;
2480
+ setOpen(false);
2481
+ }
2482
+ if (typeof onChange === "string") {
2483
+ eventBus.emit(`UI:${onChange}`, { value: next });
2484
+ } else {
2485
+ onChange?.(next);
2486
+ }
2487
+ };
2488
+ const clear = (e) => {
2489
+ e.stopPropagation();
2490
+ const next = multiple ? [] : "";
2491
+ if (typeof onChange === "string") {
2492
+ eventBus.emit(`UI:${onChange}`, { value: next });
2493
+ } else {
2494
+ onChange?.(next);
2495
+ }
2496
+ };
2497
+ React74.useEffect(() => {
2498
+ const handler = (e) => {
2499
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
2500
+ setOpen(false);
2501
+ setSearch("");
2502
+ }
2503
+ };
2504
+ document.addEventListener("mousedown", handler);
2505
+ return () => document.removeEventListener("mousedown", handler);
2506
+ }, []);
2507
+ const displayLabel = selected.length === 0 ? placeholder ?? "" : multiple ? `${selected.length} selected` : all.find((o) => o.value === selected[0])?.label ?? selected[0];
2508
+ const hasValue = selected.length > 0;
2509
+ const renderOptions = (opts) => opts.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
2510
+ "button",
2511
+ {
2512
+ type: "button",
2513
+ disabled: opt.disabled,
2514
+ onClick: () => !opt.disabled && toggle(opt.value),
2515
+ className: cn(
2516
+ "w-full flex items-center justify-between px-3 py-1.5 text-sm text-start",
2517
+ "hover:bg-muted transition-colors",
2518
+ "disabled:opacity-50 disabled:cursor-not-allowed",
2519
+ selected.includes(opt.value) && "text-primary font-medium"
2520
+ ),
2521
+ children: [
2522
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: opt.label }),
2523
+ selected.includes(opt.value) && /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: "check", className: "h-icon-default w-icon-default" })
2524
+ ]
2525
+ },
2526
+ opt.value
2527
+ ));
2528
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, className: cn("relative w-full", className), children: [
2529
+ /* @__PURE__ */ jsxRuntime.jsx(
2530
+ "button",
2531
+ {
2532
+ type: "button",
2533
+ disabled,
2534
+ onClick: () => !disabled && setOpen((o) => !o),
2535
+ className: cn(
2536
+ "block w-full border-[length:var(--border-width)] shadow-sm",
2537
+ "px-3 py-2 pr-10 text-sm text-start font-medium",
2538
+ "bg-card rounded-sm",
2539
+ "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
2540
+ "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
2541
+ error ? "border-error focus:border-error" : "border-border focus:border-primary",
2542
+ !hasValue && "text-muted-foreground"
2543
+ ),
2544
+ children: displayLabel
2545
+ }
2546
+ ),
2547
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center gap-1 pointer-events-none", children: [
2548
+ clearable && hasValue && /* @__PURE__ */ jsxRuntime.jsx(
2549
+ "button",
2550
+ {
2551
+ type: "button",
2552
+ onClick: clear,
2553
+ className: "pointer-events-auto text-muted-foreground hover:text-foreground",
2554
+ children: /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: "x", className: "h-icon-default w-icon-default" })
2555
+ }
2556
+ ),
2557
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: "chevron-down", className: "h-icon-default w-icon-default text-foreground" })
2558
+ ] }),
2559
+ open && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(
2560
+ "absolute z-50 mt-1 w-full",
2561
+ "bg-card border-[length:var(--border-width)] border-border",
2562
+ "rounded-sm shadow-elevation-popover py-1 max-h-60 overflow-y-auto"
2563
+ ), children: [
2564
+ searchable && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 pb-1 border-b border-border", children: /* @__PURE__ */ jsxRuntime.jsx(
2565
+ "input",
2566
+ {
2567
+ autoFocus: true,
2568
+ type: "text",
2569
+ value: search,
2570
+ onChange: (e) => setSearch(e.target.value),
2571
+ placeholder: "Search\u2026",
2572
+ className: cn(
2573
+ "w-full px-2 py-1 text-sm bg-transparent",
2574
+ "focus:outline-none text-foreground placeholder:text-muted-foreground"
2575
+ )
2576
+ }
2577
+ ) }),
2578
+ groups && groups.length > 0 ? groups.map((g) => {
2579
+ const groupFiltered = searchable && search ? g.options.filter((o) => o.label.toLowerCase().includes(search.toLowerCase())) : g.options;
2580
+ if (groupFiltered.length === 0) return null;
2581
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2582
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-1 text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: g.label }),
2583
+ renderOptions(groupFiltered)
2584
+ ] }, g.label);
2585
+ }) : renderOptions(filtered)
2586
+ ] })
2587
+ ] });
2588
+ }
2391
2589
  exports.Select = void 0;
2392
2590
  var init_Select = __esm({
2393
2591
  "components/core/atoms/Select.tsx"() {
@@ -2395,47 +2593,12 @@ var init_Select = __esm({
2395
2593
  init_Icon();
2396
2594
  init_useEventBus();
2397
2595
  exports.Select = React74__namespace.default.forwardRef(
2398
- ({ className, options, placeholder, error, onChange, ...props }, ref) => {
2399
- const eventBus = useEventBus();
2400
- const handleChange = (e) => {
2401
- if (typeof onChange === "string") {
2402
- eventBus.emit(`UI:${onChange}`, { value: e.target.value });
2403
- } else {
2404
- onChange?.(e);
2405
- }
2406
- };
2407
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
2408
- /* @__PURE__ */ jsxRuntime.jsxs(
2409
- "select",
2410
- {
2411
- ref,
2412
- onChange: handleChange,
2413
- className: cn(
2414
- "block w-full border-[length:var(--border-width)] shadow-sm appearance-none",
2415
- "px-3 py-2 pr-10 text-sm text-foreground font-medium",
2416
- "bg-card",
2417
- "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
2418
- "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
2419
- error ? "border-error focus:border-error" : "border-border focus:border-primary",
2420
- className
2421
- ),
2422
- ...props,
2423
- children: [
2424
- placeholder && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: placeholder }),
2425
- options.map((option) => /* @__PURE__ */ jsxRuntime.jsx(
2426
- "option",
2427
- {
2428
- value: option.value,
2429
- disabled: option.disabled,
2430
- children: option.label
2431
- },
2432
- option.value
2433
- ))
2434
- ]
2435
- }
2436
- ),
2437
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: "chevron-down", className: "h-icon-default w-icon-default text-foreground" }) })
2438
- ] });
2596
+ (props, _ref) => {
2597
+ const { multiple, searchable, clearable } = props;
2598
+ if (multiple || searchable || clearable) {
2599
+ return /* @__PURE__ */ jsxRuntime.jsx(RichSelect, { ...props });
2600
+ }
2601
+ return /* @__PURE__ */ jsxRuntime.jsx(NativeSelect, { ...props });
2439
2602
  }
2440
2603
  );
2441
2604
  exports.Select.displayName = "Select";
@@ -2489,11 +2652,54 @@ var init_Checkbox = __esm({
2489
2652
  exports.Checkbox.displayName = "Checkbox";
2490
2653
  }
2491
2654
  });
2655
+ var sizeStyles2; exports.Spinner = void 0;
2656
+ var init_Spinner = __esm({
2657
+ "components/core/atoms/Spinner.tsx"() {
2658
+ init_cn();
2659
+ init_Icon();
2660
+ sizeStyles2 = {
2661
+ xs: "h-3 w-3",
2662
+ sm: "h-icon-default w-icon-default",
2663
+ md: "h-6 w-6",
2664
+ lg: "h-8 w-8"
2665
+ };
2666
+ exports.Spinner = React74__namespace.default.forwardRef(
2667
+ ({ className, size = "md", overlay, ...props }, ref) => {
2668
+ if (overlay) {
2669
+ return /* @__PURE__ */ jsxRuntime.jsx(
2670
+ "div",
2671
+ {
2672
+ ref,
2673
+ className: cn(
2674
+ "absolute inset-0 z-10 flex items-center justify-center",
2675
+ "bg-background/60 backdrop-blur-sm",
2676
+ className
2677
+ ),
2678
+ ...props,
2679
+ children: /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: "loader", className: cn("animate-spin text-foreground", sizeStyles2[size]) })
2680
+ }
2681
+ );
2682
+ }
2683
+ return /* @__PURE__ */ jsxRuntime.jsx(
2684
+ "div",
2685
+ {
2686
+ ref,
2687
+ className: cn("text-foreground", className),
2688
+ ...props,
2689
+ children: /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: "loader", className: cn("animate-spin", sizeStyles2[size]) })
2690
+ }
2691
+ );
2692
+ }
2693
+ );
2694
+ exports.Spinner.displayName = "Spinner";
2695
+ }
2696
+ });
2492
2697
  var variantStyles2, paddingStyles, shadowStyles, lookStyles; exports.Card = void 0; exports.CardHeader = void 0; exports.CardTitle = void 0; exports.CardContent = void 0; exports.CardBody = void 0; exports.CardFooter = void 0;
2493
2698
  var init_Card = __esm({
2494
2699
  "components/core/atoms/Card.tsx"() {
2495
2700
  init_cn();
2496
2701
  init_useEventBus();
2702
+ init_Spinner();
2497
2703
  variantStyles2 = {
2498
2704
  default: [
2499
2705
  "bg-card",
@@ -2558,6 +2764,7 @@ var init_Card = __esm({
2558
2764
  look = "elevated",
2559
2765
  children,
2560
2766
  action,
2767
+ loading,
2561
2768
  onClick,
2562
2769
  ...props
2563
2770
  }, ref) => {
@@ -2571,7 +2778,7 @@ var init_Card = __esm({
2571
2778
  {
2572
2779
  ref,
2573
2780
  className: cn(
2574
- "rounded-container",
2781
+ "rounded-container relative",
2575
2782
  "transition-all duration-[var(--transition-normal)]",
2576
2783
  variantStyles2[variant],
2577
2784
  paddingStyles[padding],
@@ -2582,6 +2789,7 @@ var init_Card = __esm({
2582
2789
  onClick: handleClick,
2583
2790
  ...props,
2584
2791
  children: [
2792
+ loading && /* @__PURE__ */ jsxRuntime.jsx(exports.Spinner, { overlay: true, size: "md" }),
2585
2793
  (title || subtitle) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-4", children: [
2586
2794
  title && /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg text-card-foreground font-bold", children: title }),
2587
2795
  subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mt-1", children: subtitle })
@@ -2623,7 +2831,7 @@ var init_Card = __esm({
2623
2831
  exports.CardFooter.displayName = "CardFooter";
2624
2832
  }
2625
2833
  });
2626
- var variantStyles3, sizeStyles2; exports.Badge = void 0;
2834
+ var variantStyles3, sizeStyles3; exports.Badge = void 0;
2627
2835
  var init_Badge = __esm({
2628
2836
  "components/core/atoms/Badge.tsx"() {
2629
2837
  init_cn();
@@ -2660,7 +2868,7 @@ var init_Badge = __esm({
2660
2868
  "border-[length:var(--border-width-thin)] border-border"
2661
2869
  ].join(" ")
2662
2870
  };
2663
- sizeStyles2 = {
2871
+ sizeStyles3 = {
2664
2872
  sm: "px-2 py-0.5 text-xs",
2665
2873
  md: "px-2.5 py-1 text-sm",
2666
2874
  lg: "px-3 py-1.5 text-base"
@@ -2680,7 +2888,7 @@ var init_Badge = __esm({
2680
2888
  className: cn(
2681
2889
  "inline-flex items-center gap-1 font-bold rounded-sm",
2682
2890
  variantStyles3[variant],
2683
- sizeStyles2[size],
2891
+ sizeStyles3[size],
2684
2892
  onRemove && "pr-1",
2685
2893
  className
2686
2894
  ),
@@ -2714,7 +2922,7 @@ var init_Badge = __esm({
2714
2922
  exports.Badge.displayName = "Badge";
2715
2923
  }
2716
2924
  });
2717
- var variantStyles4, sizeStyles3, iconSizes; exports.FilterPill = void 0;
2925
+ var variantStyles4, sizeStyles4, iconSizes; exports.FilterPill = void 0;
2718
2926
  var init_FilterPill = __esm({
2719
2927
  "components/core/atoms/FilterPill.tsx"() {
2720
2928
  init_cn();
@@ -2748,7 +2956,7 @@ var init_FilterPill = __esm({
2748
2956
  "border-[length:var(--border-width-thin)] border-border"
2749
2957
  ].join(" ")
2750
2958
  };
2751
- sizeStyles3 = {
2959
+ sizeStyles4 = {
2752
2960
  sm: "px-2 py-0.5 text-xs",
2753
2961
  md: "px-2.5 py-1 text-sm",
2754
2962
  lg: "px-3 py-1.5 text-base"
@@ -2792,7 +3000,7 @@ var init_FilterPill = __esm({
2792
3000
  className: cn(
2793
3001
  "inline-flex items-center gap-1 font-bold rounded-pill",
2794
3002
  variantStyles4[variant],
2795
- sizeStyles3[size],
3003
+ sizeStyles4[size],
2796
3004
  (onClick || clickEvent) && "cursor-pointer",
2797
3005
  className
2798
3006
  ),
@@ -2824,33 +3032,6 @@ var init_FilterPill = __esm({
2824
3032
  exports.FilterPill.displayName = "FilterPill";
2825
3033
  }
2826
3034
  });
2827
- var sizeStyles4; exports.Spinner = void 0;
2828
- var init_Spinner = __esm({
2829
- "components/core/atoms/Spinner.tsx"() {
2830
- init_cn();
2831
- init_Icon();
2832
- sizeStyles4 = {
2833
- xs: "h-3 w-3",
2834
- sm: "h-icon-default w-icon-default",
2835
- md: "h-6 w-6",
2836
- lg: "h-8 w-8"
2837
- };
2838
- exports.Spinner = React74__namespace.default.forwardRef(
2839
- ({ className, size = "md", ...props }, ref) => {
2840
- return /* @__PURE__ */ jsxRuntime.jsx(
2841
- "div",
2842
- {
2843
- ref,
2844
- className: cn("text-foreground", className),
2845
- ...props,
2846
- children: /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: "loader", className: cn("animate-spin", sizeStyles4[size]) })
2847
- }
2848
- );
2849
- }
2850
- );
2851
- exports.Spinner.displayName = "Spinner";
2852
- }
2853
- });
2854
3035
  function generateInitials(name) {
2855
3036
  const parts = name.trim().split(/\s+/);
2856
3037
  if (parts.length === 1) {
@@ -7129,11 +7310,15 @@ var init_Skeleton = __esm({
7129
7310
  function getKnownPatterns() {
7130
7311
  return Object.keys(componentMapping);
7131
7312
  }
7132
- var componentMapping;
7313
+ function getPatternDefinition(type) {
7314
+ return patternRegistry[type];
7315
+ }
7316
+ var componentMapping, patternRegistry;
7133
7317
  var init_pattern_resolver = __esm({
7134
7318
  "renderer/pattern-resolver.ts"() {
7135
7319
  logger.createLogger("almadar:ui:pattern-resolver");
7136
7320
  componentMapping = {};
7321
+ patternRegistry = {};
7137
7322
  }
7138
7323
  });
7139
7324
 
@@ -15068,30 +15253,30 @@ var init_BranchingLogicBuilder = __esm({
15068
15253
  if (!sourceQuestion?.optionValues) return [];
15069
15254
  return sourceQuestion.optionValues.map((v) => ({ value: v, label: v }));
15070
15255
  }, [sourceQuestion]);
15071
- const handleSource = (e) => {
15072
- onChange({ ...rule, sourceQuestionId: e.target.value });
15256
+ const handleSource = (v) => {
15257
+ onChange({ ...rule, sourceQuestionId: v });
15073
15258
  };
15074
- const handleOperator = (e) => {
15075
- const next = e.target.value;
15259
+ const handleOperator = (v) => {
15260
+ const next = v;
15076
15261
  const nextValue = next === "in" && !Array.isArray(rule.value) ? rule.value ? [rule.value] : [] : next !== "in" && Array.isArray(rule.value) ? rule.value[0] ?? "" : rule.value;
15077
15262
  onChange({ ...rule, operator: next, value: nextValue });
15078
15263
  };
15079
15264
  const handleScalarValue = (e) => {
15080
15265
  onChange({ ...rule, value: e.target.value });
15081
15266
  };
15082
- const handleAddChip = (e) => {
15083
- const v = e.target.value;
15084
- if (!v) return;
15267
+ const handleAddChip = (v) => {
15268
+ const val = v;
15269
+ if (!val) return;
15085
15270
  const current = Array.isArray(rule.value) ? rule.value : [];
15086
- if (current.includes(v)) return;
15087
- onChange({ ...rule, value: [...current, v] });
15271
+ if (current.includes(val)) return;
15272
+ onChange({ ...rule, value: [...current, val] });
15088
15273
  };
15089
15274
  const handleRemoveChip = (chip) => {
15090
15275
  const current = Array.isArray(rule.value) ? rule.value : [];
15091
15276
  onChange({ ...rule, value: current.filter((c) => c !== chip) });
15092
15277
  };
15093
- const handleTarget = (e) => {
15094
- onChange({ ...rule, targetQuestionId: e.target.value });
15278
+ const handleTarget = (v) => {
15279
+ onChange({ ...rule, targetQuestionId: v });
15095
15280
  };
15096
15281
  const isMulti = rule.operator === "in";
15097
15282
  const chips = Array.isArray(rule.value) ? rule.value : [];
@@ -15172,7 +15357,7 @@ var init_BranchingLogicBuilder = __esm({
15172
15357
  options: valueOptions,
15173
15358
  value: scalarValue,
15174
15359
  placeholder: t("branchingLogic.selectValue"),
15175
- onChange: (e) => onChange({ ...rule, value: e.target.value }),
15360
+ onChange: (v) => onChange({ ...rule, value: v }),
15176
15361
  disabled: readOnly
15177
15362
  }
15178
15363
  ) : /* @__PURE__ */ jsxRuntime.jsx(
@@ -17604,7 +17789,7 @@ var init_Pagination = __esm({
17604
17789
  exports.Select,
17605
17790
  {
17606
17791
  value: String(pageSize),
17607
- onChange: (e) => handlePageSizeChange(Number(e.target.value)),
17792
+ onChange: (v) => handlePageSizeChange(Number(v)),
17608
17793
  options: pageSizeOptions.map((size) => ({
17609
17794
  value: String(size),
17610
17795
  label: String(size)
@@ -20758,7 +20943,84 @@ var init_DashboardLayout = __esm({
20758
20943
  NavLinkBottom.displayName = "NavLinkBottom";
20759
20944
  }
20760
20945
  });
20761
- exports.Menu = void 0;
20946
+ function computeMenuStyle(position, triggerRect) {
20947
+ const isTop = position.startsWith("top");
20948
+ const isRight = position.endsWith("right") || position.endsWith("end");
20949
+ if (isTop) {
20950
+ return {
20951
+ top: triggerRect.top - MENU_GAP,
20952
+ transform: "translateY(-100%)",
20953
+ ...isRight ? { right: window.innerWidth - triggerRect.right } : { left: triggerRect.left }
20954
+ };
20955
+ }
20956
+ return {
20957
+ top: triggerRect.bottom + MENU_GAP,
20958
+ ...isRight ? { right: window.innerWidth - triggerRect.right } : { left: triggerRect.left }
20959
+ };
20960
+ }
20961
+ function SubMenu({
20962
+ items,
20963
+ itemRef,
20964
+ direction,
20965
+ eventBus
20966
+ }) {
20967
+ const [rect, setRect] = React74.useState(null);
20968
+ React74.useEffect(() => {
20969
+ if (itemRef) {
20970
+ setRect(itemRef.getBoundingClientRect());
20971
+ }
20972
+ }, [itemRef]);
20973
+ if (!rect) return null;
20974
+ const isRtl = direction === "rtl";
20975
+ const style = {
20976
+ top: rect.top,
20977
+ ...isRtl ? { right: window.innerWidth - rect.left } : { left: rect.right }
20978
+ };
20979
+ const panel = /* @__PURE__ */ jsxRuntime.jsx(
20980
+ "div",
20981
+ {
20982
+ className: cn("fixed z-50", menuContainerStyles),
20983
+ style,
20984
+ children: items.map((item, index) => {
20985
+ const isDivider = item.id === "divider" || item.label === "divider";
20986
+ const itemId = item.id ?? `item-${item.label.toLowerCase().replace(/\s+/g, "-")}-${index}`;
20987
+ const isDanger = item.variant === "danger";
20988
+ if (isDivider) {
20989
+ return /* @__PURE__ */ jsxRuntime.jsx(exports.Divider, { className: "my-1" }, `divider-${index}`);
20990
+ }
20991
+ return /* @__PURE__ */ jsxRuntime.jsxs(
20992
+ exports.Box,
20993
+ {
20994
+ as: "button",
20995
+ onClick: () => {
20996
+ if (item.disabled) return;
20997
+ if (item.event) eventBus.emit(`UI:${item.event}`, { itemId, label: item.label });
20998
+ item.onClick?.();
20999
+ },
21000
+ "aria-disabled": item.disabled || void 0,
21001
+ "data-testid": item.event ? `action-${item.event}` : void 0,
21002
+ className: cn(
21003
+ "w-full flex items-center gap-3 px-4 py-2 text-start",
21004
+ "text-sm transition-colors",
21005
+ "hover:bg-muted focus:outline-none focus:bg-muted",
21006
+ "disabled:opacity-50 disabled:cursor-not-allowed",
21007
+ item.disabled && "cursor-not-allowed",
21008
+ isDanger && "text-error hover:bg-error/10"
21009
+ ),
21010
+ children: [
21011
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: item.icon, size: "sm", className: "flex-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: item.icon, size: "sm", className: "flex-shrink-0" })),
21012
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", className: cn("flex-1", isDanger && "text-red-600"), children: item.label }),
21013
+ item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto text-xs font-medium", children: item.badge })
21014
+ ]
21015
+ },
21016
+ itemId
21017
+ );
21018
+ })
21019
+ }
21020
+ );
21021
+ return typeof document !== "undefined" ? reactDom.createPortal(panel, document.body) : panel;
21022
+ }
21023
+ var MENU_GAP, menuContainerStyles; exports.Menu = void 0;
20762
21024
  var init_Menu = __esm({
20763
21025
  "components/core/molecules/Menu.tsx"() {
20764
21026
  "use client";
@@ -20769,16 +21031,27 @@ var init_Menu = __esm({
20769
21031
  init_Badge();
20770
21032
  init_cn();
20771
21033
  init_useEventBus();
21034
+ MENU_GAP = 4;
21035
+ menuContainerStyles = cn(
21036
+ "bg-card",
21037
+ "border-[length:var(--border-width)] border-border",
21038
+ "shadow-elevation-popover",
21039
+ "rounded-sm",
21040
+ "min-w-0 sm:min-w-[200px] max-w-[calc(100vw-1rem)] py-1"
21041
+ );
20772
21042
  exports.Menu = ({
20773
21043
  trigger,
20774
21044
  items,
20775
21045
  position = "bottom-left",
20776
- className
21046
+ className,
21047
+ header,
21048
+ footer
20777
21049
  }) => {
20778
21050
  const eventBus = useEventBus();
20779
- const { t, direction } = hooks.useTranslate();
21051
+ const { direction } = hooks.useTranslate();
20780
21052
  const [isOpen, setIsOpen] = React74.useState(false);
20781
21053
  const [activeSubMenu, setActiveSubMenu] = React74.useState(null);
21054
+ const [activeSubMenuRef, setActiveSubMenuRef] = React74.useState(null);
20782
21055
  const [triggerRect, setTriggerRect] = React74.useState(null);
20783
21056
  const triggerRef = React74.useRef(null);
20784
21057
  const menuRef = React74.useRef(null);
@@ -20793,13 +21066,14 @@ var init_Menu = __esm({
20793
21066
  }
20794
21067
  setIsOpen(!isOpen);
20795
21068
  setActiveSubMenu(null);
21069
+ setActiveSubMenuRef(null);
20796
21070
  };
20797
- const handleItemClick = (item) => {
21071
+ const handleItemClick = (item, itemId) => {
20798
21072
  if (item.disabled) return;
20799
21073
  if (item.subMenu && item.subMenu.length > 0) {
20800
- setActiveSubMenu(item.id ?? null);
21074
+ setActiveSubMenu(itemId);
20801
21075
  } else {
20802
- if (item.event) eventBus.emit(`UI:${item.event}`, { itemId: item.id, label: item.label });
21076
+ if (item.event) eventBus.emit(`UI:${item.event}`, { itemId, label: item.label });
20803
21077
  item.onClick?.();
20804
21078
  setIsOpen(false);
20805
21079
  }
@@ -20814,22 +21088,12 @@ var init_Menu = __esm({
20814
21088
  if (isOpen && menuRef.current && !menuRef.current.contains(e.target) && triggerRef.current && !triggerRef.current.contains(e.target)) {
20815
21089
  setIsOpen(false);
20816
21090
  setActiveSubMenu(null);
21091
+ setActiveSubMenuRef(null);
20817
21092
  }
20818
21093
  };
20819
21094
  document.addEventListener("mousedown", handleClickOutside);
20820
21095
  return () => document.removeEventListener("mousedown", handleClickOutside);
20821
21096
  }, [isOpen]);
20822
- const positionClasses = {
20823
- "top-left": "bottom-full left-0 mb-2",
20824
- "top-right": "bottom-full right-0 mb-2",
20825
- "bottom-left": "top-full left-0 mt-2",
20826
- "bottom-right": "top-full right-0 mt-2",
20827
- // Aliases for pattern compatibility
20828
- "top-start": "bottom-full left-0 mb-2",
20829
- "top-end": "bottom-full right-0 mb-2",
20830
- "bottom-start": "top-full left-0 mt-2",
20831
- "bottom-end": "top-full right-0 mt-2"
20832
- };
20833
21097
  const rtlMirror = {
20834
21098
  "top-left": "top-right",
20835
21099
  "top-right": "top-left",
@@ -20841,7 +21105,6 @@ var init_Menu = __esm({
20841
21105
  "bottom-end": "bottom-start"
20842
21106
  };
20843
21107
  const effectivePosition = direction === "rtl" ? rtlMirror[position] ?? position : position;
20844
- const subMenuSideClass = direction === "rtl" ? "right-full mr-2" : "left-full ml-2";
20845
21108
  const triggerChild = React74__namespace.default.isValidElement(trigger) ? trigger : /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", as: "span", children: trigger });
20846
21109
  const triggerElement = React74__namespace.default.cloneElement(
20847
21110
  triggerChild,
@@ -20850,94 +21113,87 @@ var init_Menu = __esm({
20850
21113
  onClick: handleToggle
20851
21114
  }
20852
21115
  );
20853
- const menuContainerStyles = cn(
20854
- "bg-card",
20855
- "border-[length:var(--border-width)] border-border",
20856
- "shadow-elevation-popover",
20857
- "rounded-sm",
20858
- "min-w-0 sm:min-w-[200px] max-w-[calc(100vw-1rem)] py-1"
20859
- );
20860
- const renderMenuItem = (item, hasSubMenu, index) => {
21116
+ const renderMenuItems = (menuItems) => menuItems.map((item, index) => {
21117
+ const isDivider = item.id === "divider" || item.label === "divider";
20861
21118
  const itemId = item.id ?? `item-${item.label.toLowerCase().replace(/\s+/g, "-")}-${index}`;
21119
+ const hasSubMenu = !!(item.subMenu && item.subMenu.length > 0);
20862
21120
  const isDanger = item.variant === "danger";
20863
- return /* @__PURE__ */ jsxRuntime.jsx(
20864
- exports.Box,
20865
- {
20866
- as: "button",
20867
- onClick: () => !item.disabled && handleItemClick({ ...item, id: itemId }),
20868
- "aria-disabled": item.disabled || void 0,
20869
- onMouseEnter: () => hasSubMenu && setActiveSubMenu(itemId),
20870
- "data-testid": item.event ? `action-${item.event}` : void 0,
20871
- className: cn(
20872
- "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
20873
- "text-sm transition-colors",
20874
- "hover:bg-muted",
20875
- "focus:outline-none focus:bg-muted",
20876
- "disabled:opacity-50 disabled:cursor-not-allowed",
20877
- item.disabled && "cursor-not-allowed",
20878
- isDanger && "text-error hover:bg-error/10"
20879
- ),
20880
- children: /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
20881
- item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: item.icon, size: "sm", className: "flex-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: item.icon, size: "sm", className: "flex-shrink-0" })),
20882
- /* @__PURE__ */ jsxRuntime.jsx(
20883
- exports.Typography,
20884
- {
20885
- variant: "small",
20886
- className: cn("flex-1", isDanger && "text-red-600"),
20887
- children: item.label
21121
+ if (isDivider) {
21122
+ return /* @__PURE__ */ jsxRuntime.jsx(exports.Divider, { className: "my-1" }, `divider-${index}`);
21123
+ }
21124
+ return /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { children: [
21125
+ /* @__PURE__ */ jsxRuntime.jsx(
21126
+ exports.Box,
21127
+ {
21128
+ as: "button",
21129
+ onClick: () => handleItemClick({ ...item, id: itemId }, itemId),
21130
+ "aria-disabled": item.disabled || void 0,
21131
+ onMouseEnter: (e) => {
21132
+ if (hasSubMenu) {
21133
+ setActiveSubMenu(itemId);
21134
+ setActiveSubMenuRef(e.currentTarget);
20888
21135
  }
21136
+ },
21137
+ "data-testid": item.event ? `action-${item.event}` : void 0,
21138
+ className: cn(
21139
+ "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
21140
+ "text-sm transition-colors",
21141
+ "hover:bg-muted",
21142
+ "focus:outline-none focus:bg-muted",
21143
+ "disabled:opacity-50 disabled:cursor-not-allowed",
21144
+ item.disabled && "cursor-not-allowed",
21145
+ isDanger && "text-error hover:bg-error/10"
20889
21146
  ),
20890
- item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: "default", size: "sm", children: item.badge }),
20891
- hasSubMenu && /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: direction === "rtl" ? "chevron-left" : "chevron-right", size: "sm", className: "flex-shrink-0" })
20892
- ] })
20893
- },
20894
- itemId
20895
- );
20896
- };
20897
- const renderMenuItems = (menuItems) => {
20898
- return menuItems.map((item, index) => {
20899
- const hasSubMenu = item.subMenu && item.subMenu.length > 0;
20900
- const isDivider = item.id === "divider" || item.label === "divider";
20901
- const itemId = item.id ?? `item-${item.label.toLowerCase().replace(/\s+/g, "-")}-${index}`;
20902
- if (isDivider) {
20903
- return /* @__PURE__ */ jsxRuntime.jsx(exports.Divider, { className: "my-1" }, `divider-${index}`);
20904
- }
20905
- return /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { children: [
20906
- renderMenuItem(item, !!hasSubMenu, index),
20907
- hasSubMenu && activeSubMenu === itemId && item.subMenu && /* @__PURE__ */ jsxRuntime.jsx(
20908
- exports.Box,
20909
- {
20910
- className: cn(
20911
- "absolute top-0 z-50",
20912
- subMenuSideClass,
20913
- menuContainerStyles
21147
+ children: /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
21148
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: item.icon, size: "sm", className: "flex-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: item.icon, size: "sm", className: "flex-shrink-0" })),
21149
+ /* @__PURE__ */ jsxRuntime.jsx(
21150
+ exports.Typography,
21151
+ {
21152
+ variant: "small",
21153
+ className: cn("flex-1", isDanger && "text-red-600"),
21154
+ children: item.label
21155
+ }
20914
21156
  ),
20915
- children: renderMenuItems(item.subMenu)
20916
- }
20917
- )
20918
- ] }, itemId);
20919
- });
20920
- };
20921
- return /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "relative", children: [
21157
+ item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: "default", size: "sm", children: item.badge }),
21158
+ hasSubMenu && /* @__PURE__ */ jsxRuntime.jsx(
21159
+ exports.Icon,
21160
+ {
21161
+ name: direction === "rtl" ? "chevron-left" : "chevron-right",
21162
+ size: "sm",
21163
+ className: "flex-shrink-0"
21164
+ }
21165
+ )
21166
+ ] })
21167
+ }
21168
+ ),
21169
+ hasSubMenu && activeSubMenu === itemId && item.subMenu && /* @__PURE__ */ jsxRuntime.jsx(
21170
+ SubMenu,
21171
+ {
21172
+ items: item.subMenu,
21173
+ itemRef: activeSubMenuRef,
21174
+ direction,
21175
+ eventBus
21176
+ }
21177
+ )
21178
+ ] }, itemId);
21179
+ });
21180
+ const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxRuntime.jsxs(
21181
+ "div",
21182
+ {
21183
+ ref: menuRef,
21184
+ className: cn("fixed z-50", menuContainerStyles, className),
21185
+ style: computeMenuStyle(effectivePosition, triggerRect),
21186
+ role: "menu",
21187
+ children: [
21188
+ header && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-2 border-b border-border", children: header }),
21189
+ renderMenuItems(items),
21190
+ footer && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-2 border-t border-border", children: footer })
21191
+ ]
21192
+ }
21193
+ ) : null;
21194
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
20922
21195
  triggerElement,
20923
- isOpen && triggerRect && /* @__PURE__ */ jsxRuntime.jsx(
20924
- exports.Box,
20925
- {
20926
- ref: menuRef,
20927
- className: cn(
20928
- "absolute z-50",
20929
- menuContainerStyles,
20930
- positionClasses[effectivePosition],
20931
- className
20932
- ),
20933
- style: {
20934
- left: effectivePosition.includes("left") ? 0 : "auto",
20935
- right: effectivePosition.includes("right") ? 0 : "auto"
20936
- },
20937
- role: "menu",
20938
- children: renderMenuItems(items)
20939
- }
20940
- )
21196
+ panel && typeof document !== "undefined" ? reactDom.createPortal(panel, document.body) : panel
20941
21197
  ] });
20942
21198
  };
20943
21199
  exports.Menu.displayName = "Menu";
@@ -22347,11 +22603,15 @@ function KindSelect({
22347
22603
  function ValueNode({
22348
22604
  value,
22349
22605
  onChange,
22350
- depth
22606
+ depth,
22607
+ readonly
22351
22608
  }) {
22352
22609
  const kind = kindOf(value);
22353
22610
  if (kind === "object" || kind === "array") {
22354
- return /* @__PURE__ */ jsxRuntime.jsx(ContainerNode, { value, onChange, depth });
22611
+ return /* @__PURE__ */ jsxRuntime.jsx(ContainerNode, { value, onChange, depth, readonly });
22612
+ }
22613
+ if (readonly) {
22614
+ return /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", className: "font-mono text-foreground", children: value === null ? "null" : String(value) });
22355
22615
  }
22356
22616
  return /* @__PURE__ */ jsxRuntime.jsx(ScalarControl, { value, onChange });
22357
22617
  }
@@ -22363,15 +22623,19 @@ function Row({
22363
22623
  onValue,
22364
22624
  onRenameKey,
22365
22625
  onChangeKind,
22366
- onRemove
22626
+ onRemove,
22627
+ readonly
22367
22628
  }) {
22368
22629
  const [keyDraft, setKeyDraft] = React74__namespace.default.useState(rowKey);
22369
22630
  React74__namespace.default.useEffect(() => setKeyDraft(rowKey), [rowKey]);
22370
22631
  const container = isObj(value) || isArr(value);
22371
22632
  return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "none", className: "group w-max min-w-full", children: [
22372
22633
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", align: "center", className: "py-0.5 w-max", children: [
22373
- /* @__PURE__ */ jsxRuntime.jsx(KindSelect, { kind: kindOf(value), onChange: onChangeKind }),
22374
- isArrayItem ? /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "muted", className: "w-6 shrink-0 font-mono", children: rowKey }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-20 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
22634
+ readonly ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(
22635
+ "h-6 rounded-sm bg-muted text-muted-foreground text-[10px] font-mono px-1 flex items-center",
22636
+ "border-[length:var(--border-width-thin)] border-border"
22637
+ ), children: TYPE_LABEL[kindOf(value)] }) : /* @__PURE__ */ jsxRuntime.jsx(KindSelect, { kind: kindOf(value), onChange: onChangeKind }),
22638
+ isArrayItem ? /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "muted", className: "w-6 shrink-0 font-mono", children: rowKey }) : readonly ? /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "muted", className: "w-20 shrink-0 font-mono truncate", children: rowKey }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-20 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
22375
22639
  exports.Input,
22376
22640
  {
22377
22641
  inputType: "text",
@@ -22384,8 +22648,8 @@ function Row({
22384
22648
  className: "font-mono"
22385
22649
  }
22386
22650
  ) }),
22387
- !container && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-48 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(ValueNode, { value, onChange: onValue, depth: depth + 1 }) }),
22388
- /* @__PURE__ */ jsxRuntime.jsx(
22651
+ !container && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-48 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(ValueNode, { value, onChange: onValue, depth: depth + 1, readonly }) }),
22652
+ !readonly && /* @__PURE__ */ jsxRuntime.jsx(
22389
22653
  exports.Button,
22390
22654
  {
22391
22655
  variant: "ghost",
@@ -22397,13 +22661,14 @@ function Row({
22397
22661
  }
22398
22662
  )
22399
22663
  ] }),
22400
- container && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pl-2", children: /* @__PURE__ */ jsxRuntime.jsx(ValueNode, { value, onChange: onValue, depth: depth + 1 }) })
22664
+ container && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pl-2", children: /* @__PURE__ */ jsxRuntime.jsx(ValueNode, { value, onChange: onValue, depth: depth + 1, readonly }) })
22401
22665
  ] });
22402
22666
  }
22403
22667
  function ContainerNode({
22404
22668
  value,
22405
22669
  onChange,
22406
- depth
22670
+ depth,
22671
+ readonly
22407
22672
  }) {
22408
22673
  const [open, setOpen] = React74__namespace.default.useState(depth < 2);
22409
22674
  const array = isArr(value);
@@ -22476,11 +22741,12 @@ function ContainerNode({
22476
22741
  onValue: (next) => array ? setArrValue(idx, next) : setObjValue(k, next),
22477
22742
  onRenameKey: (nk) => renameKey(k, nk),
22478
22743
  onChangeKind: (kind) => array ? changeArrKind(idx, kind) : changeObjKind(k, kind),
22479
- onRemove: () => array ? removeArrIdx(idx) : removeObjKey(k)
22744
+ onRemove: () => array ? removeArrIdx(idx) : removeObjKey(k),
22745
+ readonly
22480
22746
  },
22481
22747
  array ? idx : k
22482
22748
  )),
22483
- /* @__PURE__ */ jsxRuntime.jsx(
22749
+ !readonly && /* @__PURE__ */ jsxRuntime.jsx(
22484
22750
  exports.Button,
22485
22751
  {
22486
22752
  variant: "ghost",
@@ -22518,9 +22784,586 @@ var init_JsonTreeEditor = __esm({
22518
22784
  null: "\u2014"
22519
22785
  };
22520
22786
  KIND_OPTIONS = ["string", "number", "boolean", "object", "array"];
22521
- exports.JsonTreeEditor = ({ value, onChange, className }) => {
22787
+ exports.JsonTreeEditor = ({ value, onChange, className, readonly }) => {
22522
22788
  const root = value ?? "";
22523
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("w-full overflow-x-auto rounded-sm bg-card/40 p-2 border-[length:var(--border-width-thin)] border-border", className), children: /* @__PURE__ */ jsxRuntime.jsx(ValueNode, { value: root, onChange, depth: 0 }) });
22789
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("w-full overflow-x-auto rounded-sm bg-card/40 p-2 border-[length:var(--border-width-thin)] border-border", className), children: /* @__PURE__ */ jsxRuntime.jsx(ValueNode, { value: root, onChange, depth: 0, readonly }) });
22790
+ };
22791
+ }
22792
+ });
22793
+ exports.FormSection = void 0; exports.FormLayout = void 0; exports.FormActions = void 0;
22794
+ var init_FormSection = __esm({
22795
+ "components/core/molecules/FormSection.tsx"() {
22796
+ "use client";
22797
+ init_cn();
22798
+ init_atoms2();
22799
+ init_Box();
22800
+ init_Typography();
22801
+ init_Button();
22802
+ init_Stack();
22803
+ init_Icon();
22804
+ init_useEventBus();
22805
+ exports.FormSection = ({
22806
+ title,
22807
+ description,
22808
+ children,
22809
+ collapsible = false,
22810
+ defaultCollapsed = false,
22811
+ card = false,
22812
+ columns = 1,
22813
+ className
22814
+ }) => {
22815
+ const [collapsed, setCollapsed] = React74__namespace.default.useState(defaultCollapsed);
22816
+ const { t } = hooks.useTranslate();
22817
+ const eventBus = useEventBus();
22818
+ const gridClass = {
22819
+ 1: "grid-cols-1",
22820
+ 2: "grid-cols-1 md:grid-cols-2",
22821
+ 3: "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
22822
+ }[columns];
22823
+ React74__namespace.default.useCallback(() => {
22824
+ if (collapsible) {
22825
+ setCollapsed((prev) => !prev);
22826
+ eventBus.emit("UI:TOGGLE_COLLAPSE", { collapsed: !collapsed });
22827
+ }
22828
+ }, [collapsible, collapsed, eventBus]);
22829
+ const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
22830
+ (title || description) && /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", className: "mb-4", children: [
22831
+ title && /* @__PURE__ */ jsxRuntime.jsxs(
22832
+ exports.HStack,
22833
+ {
22834
+ justify: "between",
22835
+ align: "center",
22836
+ className: cn(collapsible && "cursor-pointer"),
22837
+ action: collapsible ? "TOGGLE_COLLAPSE" : void 0,
22838
+ children: [
22839
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h3", weight: "semibold", children: title }),
22840
+ collapsible && /* @__PURE__ */ jsxRuntime.jsx(
22841
+ exports.Button,
22842
+ {
22843
+ variant: "ghost",
22844
+ size: "sm",
22845
+ action: "TOGGLE_COLLAPSE",
22846
+ children: /* @__PURE__ */ jsxRuntime.jsx(
22847
+ exports.Icon,
22848
+ {
22849
+ icon: LucideIcons2.ChevronDown,
22850
+ size: "sm",
22851
+ className: cn(
22852
+ "text-muted-foreground transition-transform",
22853
+ collapsed && "rotate-180"
22854
+ )
22855
+ }
22856
+ )
22857
+ }
22858
+ )
22859
+ ]
22860
+ }
22861
+ ),
22862
+ description && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", color: "secondary", children: description })
22863
+ ] }),
22864
+ (!collapsible || !collapsed) && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: cn("grid gap-4", gridClass), children })
22865
+ ] });
22866
+ if (card) {
22867
+ return /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: cn("p-6", className), children: content });
22868
+ }
22869
+ return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className, children: content });
22870
+ };
22871
+ exports.FormSection.displayName = "FormSection";
22872
+ exports.FormLayout = ({
22873
+ children,
22874
+ dividers = true,
22875
+ className
22876
+ }) => {
22877
+ return /* @__PURE__ */ jsxRuntime.jsx(
22878
+ exports.VStack,
22879
+ {
22880
+ gap: "lg",
22881
+ className: cn(
22882
+ dividers && "[&>*+*]:pt-8 [&>*+*]:border-t [&>*+*]:border-border",
22883
+ className
22884
+ ),
22885
+ children
22886
+ }
22887
+ );
22888
+ };
22889
+ exports.FormLayout.displayName = "FormLayout";
22890
+ exports.FormActions = ({
22891
+ children,
22892
+ sticky = false,
22893
+ align = "right",
22894
+ className
22895
+ }) => {
22896
+ const alignClass2 = {
22897
+ left: "justify-start",
22898
+ right: "justify-end",
22899
+ between: "justify-between",
22900
+ center: "justify-center"
22901
+ }[align];
22902
+ return /* @__PURE__ */ jsxRuntime.jsx(
22903
+ exports.HStack,
22904
+ {
22905
+ gap: "sm",
22906
+ align: "center",
22907
+ className: cn(
22908
+ "pt-6 border-t border-border",
22909
+ alignClass2,
22910
+ sticky && "sticky bottom-0 bg-card py-4 -mx-6 px-6 shadow-[0_-4px_6px_-1px_rgb(0,0,0,0.05)]",
22911
+ className
22912
+ ),
22913
+ children
22914
+ }
22915
+ );
22916
+ };
22917
+ exports.FormActions.displayName = "FormActions";
22918
+ }
22919
+ });
22920
+ var ALL_CATEGORY; exports.GridPicker = void 0;
22921
+ var init_GridPicker = __esm({
22922
+ "components/core/molecules/GridPicker.tsx"() {
22923
+ "use client";
22924
+ init_cn();
22925
+ init_Input();
22926
+ init_Badge();
22927
+ init_Stack();
22928
+ ALL_CATEGORY = "__all__";
22929
+ exports.GridPicker = ({
22930
+ items,
22931
+ value,
22932
+ onChange,
22933
+ categories,
22934
+ searchPlaceholder,
22935
+ renderThumbnail,
22936
+ cellSize = 32,
22937
+ className
22938
+ }) => {
22939
+ const [search, setSearch] = React74.useState("");
22940
+ const [activeCategory, setActiveCategory] = React74.useState(ALL_CATEGORY);
22941
+ const gridRef = React74.useRef(null);
22942
+ const categoryChips = React74.useMemo(() => {
22943
+ if (categories !== void 0) return categories;
22944
+ const seen = [];
22945
+ for (const item of items) {
22946
+ if (!seen.includes(item.category)) seen.push(item.category);
22947
+ }
22948
+ return seen;
22949
+ }, [categories, items]);
22950
+ const filtered = React74.useMemo(() => {
22951
+ const needle = search.trim().toLowerCase();
22952
+ return items.filter((item) => {
22953
+ const matchesCategory = activeCategory === ALL_CATEGORY || item.category === activeCategory;
22954
+ const matchesSearch = needle === "" || item.label.toLowerCase().includes(needle);
22955
+ return matchesCategory && matchesSearch;
22956
+ });
22957
+ }, [items, search, activeCategory]);
22958
+ const select = React74.useCallback(
22959
+ (item) => {
22960
+ onChange(item.id);
22961
+ },
22962
+ [onChange]
22963
+ );
22964
+ const handleKeyDown = React74.useCallback(
22965
+ (e, index) => {
22966
+ const cells = gridRef.current?.querySelectorAll(
22967
+ "[data-gridpicker-cell]"
22968
+ );
22969
+ if (cells === void 0 || cells.length === 0) return;
22970
+ const columns = (() => {
22971
+ const grid = gridRef.current;
22972
+ if (grid === null) return 1;
22973
+ const style = window.getComputedStyle(grid);
22974
+ const cols = style.gridTemplateColumns.split(" ").filter(Boolean).length;
22975
+ return cols > 0 ? cols : 1;
22976
+ })();
22977
+ let next = -1;
22978
+ if (e.key === "ArrowRight") next = index + 1;
22979
+ else if (e.key === "ArrowLeft") next = index - 1;
22980
+ else if (e.key === "ArrowDown") next = index + columns;
22981
+ else if (e.key === "ArrowUp") next = index - columns;
22982
+ else if (e.key === "Enter" || e.key === " ") {
22983
+ e.preventDefault();
22984
+ select(filtered[index]);
22985
+ return;
22986
+ } else {
22987
+ return;
22988
+ }
22989
+ e.preventDefault();
22990
+ if (next >= 0 && next < cells.length) {
22991
+ cells[next].focus();
22992
+ }
22993
+ },
22994
+ [filtered, select]
22995
+ );
22996
+ return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "sm", className: cn("w-full", className), children: [
22997
+ /* @__PURE__ */ jsxRuntime.jsx(
22998
+ exports.Input,
22999
+ {
23000
+ type: "search",
23001
+ icon: "search",
23002
+ value: search,
23003
+ placeholder: searchPlaceholder,
23004
+ clearable: true,
23005
+ onClear: () => setSearch(""),
23006
+ onChange: (e) => setSearch(e.target.value)
23007
+ }
23008
+ ),
23009
+ categoryChips.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", wrap: true, children: [
23010
+ /* @__PURE__ */ jsxRuntime.jsx(
23011
+ exports.Badge,
23012
+ {
23013
+ variant: activeCategory === ALL_CATEGORY ? "primary" : "neutral",
23014
+ size: "sm",
23015
+ role: "button",
23016
+ tabIndex: 0,
23017
+ "aria-pressed": activeCategory === ALL_CATEGORY,
23018
+ className: "cursor-pointer",
23019
+ onClick: () => setActiveCategory(ALL_CATEGORY),
23020
+ onKeyDown: (e) => {
23021
+ if (e.key === "Enter" || e.key === " ") {
23022
+ e.preventDefault();
23023
+ setActiveCategory(ALL_CATEGORY);
23024
+ }
23025
+ },
23026
+ children: "All"
23027
+ }
23028
+ ),
23029
+ categoryChips.map((category) => /* @__PURE__ */ jsxRuntime.jsx(
23030
+ exports.Badge,
23031
+ {
23032
+ variant: activeCategory === category ? "primary" : "neutral",
23033
+ size: "sm",
23034
+ role: "button",
23035
+ tabIndex: 0,
23036
+ "aria-pressed": activeCategory === category,
23037
+ className: "cursor-pointer",
23038
+ onClick: () => setActiveCategory(category),
23039
+ onKeyDown: (e) => {
23040
+ if (e.key === "Enter" || e.key === " ") {
23041
+ e.preventDefault();
23042
+ setActiveCategory(category);
23043
+ }
23044
+ },
23045
+ children: category
23046
+ },
23047
+ category
23048
+ ))
23049
+ ] }),
23050
+ /* @__PURE__ */ jsxRuntime.jsx(
23051
+ "div",
23052
+ {
23053
+ ref: gridRef,
23054
+ role: "listbox",
23055
+ className: "grid gap-1 overflow-y-auto max-h-64 p-1",
23056
+ style: {
23057
+ gridTemplateColumns: `repeat(auto-fill, minmax(${cellSize}px, 1fr))`
23058
+ },
23059
+ children: filtered.map((item, index) => {
23060
+ const selected = item.id === value;
23061
+ return /* @__PURE__ */ jsxRuntime.jsx(
23062
+ "button",
23063
+ {
23064
+ type: "button",
23065
+ role: "option",
23066
+ "aria-selected": selected,
23067
+ "aria-label": item.label,
23068
+ title: item.label,
23069
+ "data-gridpicker-cell": true,
23070
+ tabIndex: selected || value === void 0 && index === 0 ? 0 : -1,
23071
+ onClick: () => select(item),
23072
+ onKeyDown: (e) => handleKeyDown(e, index),
23073
+ className: cn(
23074
+ "flex items-center justify-center rounded-sm",
23075
+ "transition-colors hover:bg-muted",
23076
+ "focus:outline-none focus:ring-1 focus:ring-ring",
23077
+ selected && "bg-primary/10 ring-1 ring-primary"
23078
+ ),
23079
+ style: { width: cellSize, height: cellSize },
23080
+ children: renderThumbnail(item)
23081
+ },
23082
+ item.id
23083
+ );
23084
+ })
23085
+ }
23086
+ )
23087
+ ] });
23088
+ };
23089
+ exports.GridPicker.displayName = "GridPicker";
23090
+ }
23091
+ });
23092
+ function pascalToKebab(name) {
23093
+ return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z])([A-Z][a-z])/g, "$1-$2").toLowerCase();
23094
+ }
23095
+ function kebabToPascal3(name) {
23096
+ return name.split("-").map((part) => /^\d+$/.test(part) ? part : part.charAt(0).toUpperCase() + part.slice(1)).join("");
23097
+ }
23098
+ var ICON_ITEMS; exports.IconPicker = void 0;
23099
+ var init_IconPicker = __esm({
23100
+ "components/core/molecules/IconPicker.tsx"() {
23101
+ "use client";
23102
+ init_Icon();
23103
+ init_GridPicker();
23104
+ ICON_ITEMS = (() => {
23105
+ const items = [];
23106
+ for (const [exportName, candidate] of Object.entries(LucideIcons2__namespace)) {
23107
+ if (!/^[A-Z]/.test(exportName)) continue;
23108
+ if (exportName.endsWith("Icon")) continue;
23109
+ if (exportName.startsWith("Lucide")) continue;
23110
+ const isComponent = candidate !== null && (typeof candidate === "object" || typeof candidate === "function") && "$$typeof" in candidate;
23111
+ if (!isComponent) continue;
23112
+ const kebab = pascalToKebab(exportName);
23113
+ if (kebabToPascal3(kebab) !== exportName) continue;
23114
+ items.push({ id: kebab, label: kebab, category: "icons" });
23115
+ }
23116
+ return items;
23117
+ })();
23118
+ exports.IconPicker = ({ value, onChange, className }) => {
23119
+ const items = React74.useMemo(() => ICON_ITEMS, []);
23120
+ return /* @__PURE__ */ jsxRuntime.jsx(
23121
+ exports.GridPicker,
23122
+ {
23123
+ items,
23124
+ value,
23125
+ onChange,
23126
+ searchPlaceholder: "Search icons\u2026",
23127
+ renderThumbnail: (it) => /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: it.id }),
23128
+ cellSize: 32,
23129
+ className
23130
+ }
23131
+ );
23132
+ };
23133
+ exports.IconPicker.displayName = "IconPicker";
23134
+ }
23135
+ });
23136
+ function iconForKind(kind) {
23137
+ if (kind === "audio") return "music";
23138
+ if (kind === "model") return "box";
23139
+ return "file";
23140
+ }
23141
+ var THUMB_PX, IMAGE_KINDS; exports.AssetPicker = void 0;
23142
+ var init_AssetPicker = __esm({
23143
+ "components/core/molecules/AssetPicker.tsx"() {
23144
+ "use client";
23145
+ init_GridPicker();
23146
+ init_Icon();
23147
+ THUMB_PX = 32;
23148
+ IMAGE_KINDS = /* @__PURE__ */ new Set([
23149
+ "image",
23150
+ "spritesheet",
23151
+ "scene",
23152
+ "portrait"
23153
+ ]);
23154
+ exports.AssetPicker = ({
23155
+ assets,
23156
+ value,
23157
+ onChange,
23158
+ className
23159
+ }) => {
23160
+ const byUrl = React74.useMemo(() => {
23161
+ const map = /* @__PURE__ */ new Map();
23162
+ for (const entry of assets) map.set(entry.url, entry);
23163
+ return map;
23164
+ }, [assets]);
23165
+ const items = React74.useMemo(
23166
+ () => assets.map((entry) => ({
23167
+ id: entry.url,
23168
+ label: entry.name,
23169
+ category: entry.category
23170
+ })),
23171
+ [assets]
23172
+ );
23173
+ const categories = React74.useMemo(() => {
23174
+ const seen = [];
23175
+ for (const entry of assets) {
23176
+ if (!seen.includes(entry.category)) seen.push(entry.category);
23177
+ }
23178
+ return seen;
23179
+ }, [assets]);
23180
+ const renderThumbnail = React74.useCallback(
23181
+ (item) => {
23182
+ const entry = byUrl.get(item.id);
23183
+ if (entry === void 0) return null;
23184
+ if (IMAGE_KINDS.has(entry.kind)) {
23185
+ return /* @__PURE__ */ jsxRuntime.jsx(
23186
+ "img",
23187
+ {
23188
+ src: entry.thumbnailUrl ?? entry.url,
23189
+ alt: entry.name,
23190
+ loading: "lazy",
23191
+ width: THUMB_PX,
23192
+ height: THUMB_PX,
23193
+ style: { width: THUMB_PX, height: THUMB_PX, objectFit: "cover" }
23194
+ }
23195
+ );
23196
+ }
23197
+ return /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: iconForKind(entry.kind), size: "sm" });
23198
+ },
23199
+ [byUrl]
23200
+ );
23201
+ return /* @__PURE__ */ jsxRuntime.jsx(
23202
+ exports.GridPicker,
23203
+ {
23204
+ items,
23205
+ value,
23206
+ onChange,
23207
+ categories,
23208
+ renderThumbnail,
23209
+ cellSize: THUMB_PX,
23210
+ className
23211
+ }
23212
+ );
23213
+ };
23214
+ exports.AssetPicker.displayName = "AssetPicker";
23215
+ }
23216
+ });
23217
+ function currentValue(decl, override) {
23218
+ return override !== void 0 ? override : decl.default;
23219
+ }
23220
+ function TextLikeControl({
23221
+ field,
23222
+ numeric,
23223
+ value,
23224
+ onCommit
23225
+ }) {
23226
+ const initial = value === void 0 || value === null ? "" : String(value);
23227
+ const [draft, setDraft] = React74__namespace.default.useState(initial);
23228
+ React74__namespace.default.useEffect(() => setDraft(initial), [initial]);
23229
+ const commit = () => {
23230
+ if (numeric) {
23231
+ const n = draft.trim() === "" ? 0 : Number(draft);
23232
+ onCommit(field, Number.isNaN(n) ? 0 : n);
23233
+ } else {
23234
+ onCommit(field, draft);
23235
+ }
23236
+ };
23237
+ return /* @__PURE__ */ jsxRuntime.jsx(
23238
+ exports.Input,
23239
+ {
23240
+ inputType: numeric ? "number" : "text",
23241
+ value: draft,
23242
+ onChange: (e) => setDraft(e.target.value),
23243
+ onBlur: commit,
23244
+ onKeyDown: (e) => {
23245
+ if (e.key === "Enter") commit();
23246
+ }
23247
+ }
23248
+ );
23249
+ }
23250
+ function isTraitConfigObject(v) {
23251
+ return v !== null && v !== void 0 && typeof v === "object" && !Array.isArray(v);
23252
+ }
23253
+ function FieldControl({
23254
+ name,
23255
+ decl,
23256
+ value,
23257
+ onChange,
23258
+ assets
23259
+ }) {
23260
+ let control;
23261
+ const stringValue = typeof value === "string" ? value : void 0;
23262
+ const effectiveValue = value ?? decl.default;
23263
+ if (decl.type === "icon") {
23264
+ control = /* @__PURE__ */ jsxRuntime.jsx(exports.IconPicker, { value: stringValue, onChange: (icon) => onChange(name, icon) });
23265
+ } else if (decl.type === "asset") {
23266
+ control = /* @__PURE__ */ jsxRuntime.jsx(
23267
+ exports.AssetPicker,
23268
+ {
23269
+ assets: assets ?? [],
23270
+ value: stringValue,
23271
+ onChange: (url) => onChange(name, url)
23272
+ }
23273
+ );
23274
+ } else if (decl.type === "boolean") {
23275
+ control = /* @__PURE__ */ jsxRuntime.jsx(exports.Switch, { checked: value === true, onChange: (c) => onChange(name, c) });
23276
+ } else if (decl.type === "string" && decl.values !== void 0 && decl.values.length > 0) {
23277
+ control = /* @__PURE__ */ jsxRuntime.jsx(
23278
+ exports.Select,
23279
+ {
23280
+ options: decl.values.map((v) => ({ value: v, label: v })),
23281
+ value: typeof value === "string" ? value : "",
23282
+ onChange: (v) => onChange(name, v)
23283
+ }
23284
+ );
23285
+ } else if (decl.type === "number") {
23286
+ control = /* @__PURE__ */ jsxRuntime.jsx(TextLikeControl, { field: name, numeric: true, value, onCommit: onChange });
23287
+ } else if (decl.type === "string") {
23288
+ control = /* @__PURE__ */ jsxRuntime.jsx(TextLikeControl, { field: name, numeric: false, value, onCommit: onChange });
23289
+ } else if (decl.type === "node") {
23290
+ control = /* @__PURE__ */ jsxRuntime.jsx(exports.NodeSlotEditor, { value: effectiveValue, onChange: (next) => onChange(name, next) });
23291
+ } else if (decl.type.startsWith("[") || Array.isArray(effectiveValue)) {
23292
+ const arr = Array.isArray(effectiveValue) ? effectiveValue : [];
23293
+ control = /* @__PURE__ */ jsxRuntime.jsx(exports.JsonTreeEditor, { value: arr, onChange: (next) => onChange(name, next) });
23294
+ } else if (decl.type === "object" || decl.type.startsWith("Map ") || !SCALAR_TYPES.has(decl.type) && isTraitConfigObject(effectiveValue)) {
23295
+ const obj = isTraitConfigObject(effectiveValue) ? effectiveValue : {};
23296
+ control = /* @__PURE__ */ jsxRuntime.jsx(exports.JsonTreeEditor, { value: obj, onChange: (next) => onChange(name, next) });
23297
+ } else {
23298
+ control = /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "caption", color: "muted", children: [
23299
+ decl.type,
23300
+ " \u2014 edit in source"
23301
+ ] });
23302
+ }
23303
+ return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", children: [
23304
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "label", children: decl.label ?? name }),
23305
+ control,
23306
+ decl.description !== void 0 && decl.description !== "" && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "muted", children: decl.description })
23307
+ ] });
23308
+ }
23309
+ var TIER_ORDER, SCALAR_TYPES; exports.PropertyInspector = void 0;
23310
+ var init_PropertyInspector = __esm({
23311
+ "components/core/molecules/PropertyInspector.tsx"() {
23312
+ "use client";
23313
+ init_cn();
23314
+ init_Stack();
23315
+ init_Typography();
23316
+ init_Button();
23317
+ init_Switch();
23318
+ init_Select();
23319
+ init_Input();
23320
+ init_FormSection();
23321
+ init_IconPicker();
23322
+ init_AssetPicker();
23323
+ init_JsonTreeEditor();
23324
+ init_NodeSlotEditor();
23325
+ TIER_ORDER = ["presentation", "domain", "policy", "infra", "internal"];
23326
+ SCALAR_TYPES = /* @__PURE__ */ new Set(["string", "number", "boolean", "icon", "asset"]);
23327
+ exports.PropertyInspector = ({
23328
+ config,
23329
+ values,
23330
+ onChange,
23331
+ onReset,
23332
+ title,
23333
+ className,
23334
+ assets
23335
+ }) => {
23336
+ const fields = Object.entries(config);
23337
+ const byTier = /* @__PURE__ */ new Map();
23338
+ for (const [name, decl] of fields) {
23339
+ const tier = decl.tier ?? "presentation";
23340
+ const arr = byTier.get(tier) ?? [];
23341
+ arr.push([name, decl]);
23342
+ byTier.set(tier, arr);
23343
+ }
23344
+ const tiers = [...byTier.keys()].sort((a, b) => {
23345
+ const ia = TIER_ORDER.indexOf(a);
23346
+ const ib = TIER_ORDER.indexOf(b);
23347
+ return (ia === -1 ? 99 : ia) - (ib === -1 ? 99 : ib);
23348
+ });
23349
+ return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "sm", className: cn("w-full", className), children: [
23350
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { justify: "between", align: "center", children: [
23351
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", weight: "bold", children: title ?? "Config" }),
23352
+ onReset !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "ghost", size: "sm", icon: "rotate-ccw", label: "Reset", onClick: onReset })
23353
+ ] }),
23354
+ fields.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "muted", children: "No configurable properties." }),
23355
+ tiers.map((tier) => /* @__PURE__ */ jsxRuntime.jsx(exports.FormSection, { title: tier, collapsible: true, defaultCollapsed: tier !== "presentation", children: /* @__PURE__ */ jsxRuntime.jsx(exports.VStack, { gap: "sm", children: byTier.get(tier)?.map(([name, decl]) => /* @__PURE__ */ jsxRuntime.jsx(
23356
+ FieldControl,
23357
+ {
23358
+ name,
23359
+ decl,
23360
+ value: currentValue(decl, values?.[name]),
23361
+ onChange,
23362
+ assets
23363
+ },
23364
+ name
23365
+ )) }) }, tier))
23366
+ ] });
22524
23367
  };
22525
23368
  }
22526
23369
  });
@@ -22542,6 +23385,7 @@ var init_NodeSlotEditor = __esm({
22542
23385
  init_Select();
22543
23386
  init_Typography();
22544
23387
  init_JsonTreeEditor();
23388
+ init_PropertyInspector();
22545
23389
  init_pattern_resolver();
22546
23390
  init_cn();
22547
23391
  isObj2 = (v) => v !== null && v !== void 0 && typeof v === "object" && !Array.isArray(v);
@@ -22566,18 +23410,42 @@ var init_NodeSlotEditor = __esm({
22566
23410
  const pattern = { type: nextType, ...nextProps };
22567
23411
  onChange(wasArray || value === void 0 ? [pattern] : pattern);
22568
23412
  };
23413
+ const schemaEntries = React74__namespace.default.useMemo(() => {
23414
+ if (!type) return [];
23415
+ const def = getPatternDefinition(type);
23416
+ if (!def?.propsSchema) return [];
23417
+ return Object.entries(def.propsSchema).map(([propName, propDef]) => {
23418
+ const decl = {
23419
+ type: propDef.types?.[0] ?? "string",
23420
+ description: propDef.description,
23421
+ label: propName,
23422
+ ...propDef.enumValues && propDef.enumValues.length > 0 ? { values: propDef.enumValues } : {}
23423
+ };
23424
+ return [propName, decl];
23425
+ });
23426
+ }, [type]);
23427
+ const hasSchema = schemaEntries.length > 0;
22569
23428
  return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", className: cn("w-full", className), children: [
22570
23429
  /* @__PURE__ */ jsxRuntime.jsx(
22571
23430
  exports.Select,
22572
23431
  {
22573
23432
  options,
22574
23433
  value: type,
22575
- onChange: (e) => emit(e.target.value, props)
23434
+ onChange: (v) => emit(v, props)
22576
23435
  }
22577
23436
  ),
22578
23437
  type !== "" && /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "none", className: "pl-1", children: [
22579
23438
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "muted", children: "props" }),
22580
- /* @__PURE__ */ jsxRuntime.jsx(exports.JsonTreeEditor, { value: props, onChange: (next) => emit(type, isObj2(next) ? next : {}) })
23439
+ hasSchema ? /* @__PURE__ */ jsxRuntime.jsx(exports.VStack, { gap: "xs", children: schemaEntries.map(([propName, decl]) => /* @__PURE__ */ jsxRuntime.jsx(
23440
+ FieldControl,
23441
+ {
23442
+ name: propName,
23443
+ decl,
23444
+ value: props[propName],
23445
+ onChange: (field, val) => emit(type, { ...props, [field]: val })
23446
+ },
23447
+ propName
23448
+ )) }) : /* @__PURE__ */ jsxRuntime.jsx(exports.JsonTreeEditor, { value: props, onChange: (next) => emit(type, isObj2(next) ? next : {}) })
22581
23449
  ] })
22582
23450
  ] });
22583
23451
  };
@@ -23017,7 +23885,7 @@ var init_FilterGroup = __esm({
23017
23885
  exports.Select,
23018
23886
  {
23019
23887
  value: selectedValues[filter.field] || "all",
23020
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
23888
+ onChange: (v) => handleFilterSelect(filter.field, v),
23021
23889
  options: [
23022
23890
  { value: "all", label: t("filterGroup.all") },
23023
23891
  ...filter.options?.map((opt) => ({
@@ -23100,7 +23968,7 @@ var init_FilterGroup = __esm({
23100
23968
  exports.Select,
23101
23969
  {
23102
23970
  value: selectedValues[filter.field] || "all",
23103
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
23971
+ onChange: (v) => handleFilterSelect(filter.field, v),
23104
23972
  options: [
23105
23973
  { value: "all", label: t("filterGroup.allOf", { label: filter.label }) },
23106
23974
  ...filter.options?.map((opt) => ({
@@ -23218,7 +24086,7 @@ var init_FilterGroup = __esm({
23218
24086
  exports.Select,
23219
24087
  {
23220
24088
  value: selectedValues[filter.field] || "all",
23221
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
24089
+ onChange: (v) => handleFilterSelect(filter.field, v),
23222
24090
  options: [
23223
24091
  { value: "all", label: t("filterGroup.all") },
23224
24092
  ...filter.options?.map((opt) => ({
@@ -25160,303 +26028,6 @@ var init_FlipCard = __esm({
25160
26028
  exports.FlipCard.displayName = "FlipCard";
25161
26029
  }
25162
26030
  });
25163
- var ALL_CATEGORY; exports.GridPicker = void 0;
25164
- var init_GridPicker = __esm({
25165
- "components/core/molecules/GridPicker.tsx"() {
25166
- "use client";
25167
- init_cn();
25168
- init_Input();
25169
- init_Badge();
25170
- init_Stack();
25171
- ALL_CATEGORY = "__all__";
25172
- exports.GridPicker = ({
25173
- items,
25174
- value,
25175
- onChange,
25176
- categories,
25177
- searchPlaceholder,
25178
- renderThumbnail,
25179
- cellSize = 32,
25180
- className
25181
- }) => {
25182
- const [search, setSearch] = React74.useState("");
25183
- const [activeCategory, setActiveCategory] = React74.useState(ALL_CATEGORY);
25184
- const gridRef = React74.useRef(null);
25185
- const categoryChips = React74.useMemo(() => {
25186
- if (categories !== void 0) return categories;
25187
- const seen = [];
25188
- for (const item of items) {
25189
- if (!seen.includes(item.category)) seen.push(item.category);
25190
- }
25191
- return seen;
25192
- }, [categories, items]);
25193
- const filtered = React74.useMemo(() => {
25194
- const needle = search.trim().toLowerCase();
25195
- return items.filter((item) => {
25196
- const matchesCategory = activeCategory === ALL_CATEGORY || item.category === activeCategory;
25197
- const matchesSearch = needle === "" || item.label.toLowerCase().includes(needle);
25198
- return matchesCategory && matchesSearch;
25199
- });
25200
- }, [items, search, activeCategory]);
25201
- const select = React74.useCallback(
25202
- (item) => {
25203
- onChange(item.id);
25204
- },
25205
- [onChange]
25206
- );
25207
- const handleKeyDown = React74.useCallback(
25208
- (e, index) => {
25209
- const cells = gridRef.current?.querySelectorAll(
25210
- "[data-gridpicker-cell]"
25211
- );
25212
- if (cells === void 0 || cells.length === 0) return;
25213
- const columns = (() => {
25214
- const grid = gridRef.current;
25215
- if (grid === null) return 1;
25216
- const style = window.getComputedStyle(grid);
25217
- const cols = style.gridTemplateColumns.split(" ").filter(Boolean).length;
25218
- return cols > 0 ? cols : 1;
25219
- })();
25220
- let next = -1;
25221
- if (e.key === "ArrowRight") next = index + 1;
25222
- else if (e.key === "ArrowLeft") next = index - 1;
25223
- else if (e.key === "ArrowDown") next = index + columns;
25224
- else if (e.key === "ArrowUp") next = index - columns;
25225
- else if (e.key === "Enter" || e.key === " ") {
25226
- e.preventDefault();
25227
- select(filtered[index]);
25228
- return;
25229
- } else {
25230
- return;
25231
- }
25232
- e.preventDefault();
25233
- if (next >= 0 && next < cells.length) {
25234
- cells[next].focus();
25235
- }
25236
- },
25237
- [filtered, select]
25238
- );
25239
- return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "sm", className: cn("w-full", className), children: [
25240
- /* @__PURE__ */ jsxRuntime.jsx(
25241
- exports.Input,
25242
- {
25243
- type: "search",
25244
- icon: "search",
25245
- value: search,
25246
- placeholder: searchPlaceholder,
25247
- clearable: true,
25248
- onClear: () => setSearch(""),
25249
- onChange: (e) => setSearch(e.target.value)
25250
- }
25251
- ),
25252
- categoryChips.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", wrap: true, children: [
25253
- /* @__PURE__ */ jsxRuntime.jsx(
25254
- exports.Badge,
25255
- {
25256
- variant: activeCategory === ALL_CATEGORY ? "primary" : "neutral",
25257
- size: "sm",
25258
- role: "button",
25259
- tabIndex: 0,
25260
- "aria-pressed": activeCategory === ALL_CATEGORY,
25261
- className: "cursor-pointer",
25262
- onClick: () => setActiveCategory(ALL_CATEGORY),
25263
- onKeyDown: (e) => {
25264
- if (e.key === "Enter" || e.key === " ") {
25265
- e.preventDefault();
25266
- setActiveCategory(ALL_CATEGORY);
25267
- }
25268
- },
25269
- children: "All"
25270
- }
25271
- ),
25272
- categoryChips.map((category) => /* @__PURE__ */ jsxRuntime.jsx(
25273
- exports.Badge,
25274
- {
25275
- variant: activeCategory === category ? "primary" : "neutral",
25276
- size: "sm",
25277
- role: "button",
25278
- tabIndex: 0,
25279
- "aria-pressed": activeCategory === category,
25280
- className: "cursor-pointer",
25281
- onClick: () => setActiveCategory(category),
25282
- onKeyDown: (e) => {
25283
- if (e.key === "Enter" || e.key === " ") {
25284
- e.preventDefault();
25285
- setActiveCategory(category);
25286
- }
25287
- },
25288
- children: category
25289
- },
25290
- category
25291
- ))
25292
- ] }),
25293
- /* @__PURE__ */ jsxRuntime.jsx(
25294
- "div",
25295
- {
25296
- ref: gridRef,
25297
- role: "listbox",
25298
- className: "grid gap-1 overflow-y-auto max-h-64 p-1",
25299
- style: {
25300
- gridTemplateColumns: `repeat(auto-fill, minmax(${cellSize}px, 1fr))`
25301
- },
25302
- children: filtered.map((item, index) => {
25303
- const selected = item.id === value;
25304
- return /* @__PURE__ */ jsxRuntime.jsx(
25305
- "button",
25306
- {
25307
- type: "button",
25308
- role: "option",
25309
- "aria-selected": selected,
25310
- "aria-label": item.label,
25311
- title: item.label,
25312
- "data-gridpicker-cell": true,
25313
- tabIndex: selected || value === void 0 && index === 0 ? 0 : -1,
25314
- onClick: () => select(item),
25315
- onKeyDown: (e) => handleKeyDown(e, index),
25316
- className: cn(
25317
- "flex items-center justify-center rounded-sm",
25318
- "transition-colors hover:bg-muted",
25319
- "focus:outline-none focus:ring-1 focus:ring-ring",
25320
- selected && "bg-primary/10 ring-1 ring-primary"
25321
- ),
25322
- style: { width: cellSize, height: cellSize },
25323
- children: renderThumbnail(item)
25324
- },
25325
- item.id
25326
- );
25327
- })
25328
- }
25329
- )
25330
- ] });
25331
- };
25332
- exports.GridPicker.displayName = "GridPicker";
25333
- }
25334
- });
25335
- function iconForKind(kind) {
25336
- if (kind === "audio") return "music";
25337
- if (kind === "model") return "box";
25338
- return "file";
25339
- }
25340
- var THUMB_PX, IMAGE_KINDS; exports.AssetPicker = void 0;
25341
- var init_AssetPicker = __esm({
25342
- "components/core/molecules/AssetPicker.tsx"() {
25343
- "use client";
25344
- init_GridPicker();
25345
- init_Icon();
25346
- THUMB_PX = 32;
25347
- IMAGE_KINDS = /* @__PURE__ */ new Set([
25348
- "image",
25349
- "spritesheet",
25350
- "scene",
25351
- "portrait"
25352
- ]);
25353
- exports.AssetPicker = ({
25354
- assets,
25355
- value,
25356
- onChange,
25357
- className
25358
- }) => {
25359
- const byUrl = React74.useMemo(() => {
25360
- const map = /* @__PURE__ */ new Map();
25361
- for (const entry of assets) map.set(entry.url, entry);
25362
- return map;
25363
- }, [assets]);
25364
- const items = React74.useMemo(
25365
- () => assets.map((entry) => ({
25366
- id: entry.url,
25367
- label: entry.name,
25368
- category: entry.category
25369
- })),
25370
- [assets]
25371
- );
25372
- const categories = React74.useMemo(() => {
25373
- const seen = [];
25374
- for (const entry of assets) {
25375
- if (!seen.includes(entry.category)) seen.push(entry.category);
25376
- }
25377
- return seen;
25378
- }, [assets]);
25379
- const renderThumbnail = React74.useCallback(
25380
- (item) => {
25381
- const entry = byUrl.get(item.id);
25382
- if (entry === void 0) return null;
25383
- if (IMAGE_KINDS.has(entry.kind)) {
25384
- return /* @__PURE__ */ jsxRuntime.jsx(
25385
- "img",
25386
- {
25387
- src: entry.thumbnailUrl ?? entry.url,
25388
- alt: entry.name,
25389
- loading: "lazy",
25390
- width: THUMB_PX,
25391
- height: THUMB_PX,
25392
- style: { width: THUMB_PX, height: THUMB_PX, objectFit: "cover" }
25393
- }
25394
- );
25395
- }
25396
- return /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: iconForKind(entry.kind), size: "sm" });
25397
- },
25398
- [byUrl]
25399
- );
25400
- return /* @__PURE__ */ jsxRuntime.jsx(
25401
- exports.GridPicker,
25402
- {
25403
- items,
25404
- value,
25405
- onChange,
25406
- categories,
25407
- renderThumbnail,
25408
- cellSize: THUMB_PX,
25409
- className
25410
- }
25411
- );
25412
- };
25413
- exports.AssetPicker.displayName = "AssetPicker";
25414
- }
25415
- });
25416
- function pascalToKebab(name) {
25417
- return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z])([A-Z][a-z])/g, "$1-$2").toLowerCase();
25418
- }
25419
- function kebabToPascal3(name) {
25420
- return name.split("-").map((part) => /^\d+$/.test(part) ? part : part.charAt(0).toUpperCase() + part.slice(1)).join("");
25421
- }
25422
- var ICON_ITEMS; exports.IconPicker = void 0;
25423
- var init_IconPicker = __esm({
25424
- "components/core/molecules/IconPicker.tsx"() {
25425
- "use client";
25426
- init_Icon();
25427
- init_GridPicker();
25428
- ICON_ITEMS = (() => {
25429
- const items = [];
25430
- for (const [exportName, candidate] of Object.entries(LucideIcons2__namespace)) {
25431
- if (!/^[A-Z]/.test(exportName)) continue;
25432
- if (exportName.endsWith("Icon")) continue;
25433
- if (exportName.startsWith("Lucide")) continue;
25434
- const isComponent = candidate !== null && (typeof candidate === "object" || typeof candidate === "function") && "$$typeof" in candidate;
25435
- if (!isComponent) continue;
25436
- const kebab = pascalToKebab(exportName);
25437
- if (kebabToPascal3(kebab) !== exportName) continue;
25438
- items.push({ id: kebab, label: kebab, category: "icons" });
25439
- }
25440
- return items;
25441
- })();
25442
- exports.IconPicker = ({ value, onChange, className }) => {
25443
- const items = React74.useMemo(() => ICON_ITEMS, []);
25444
- return /* @__PURE__ */ jsxRuntime.jsx(
25445
- exports.GridPicker,
25446
- {
25447
- items,
25448
- value,
25449
- onChange,
25450
- searchPlaceholder: "Search icons\u2026",
25451
- renderThumbnail: (it) => /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: it.id }),
25452
- cellSize: 32,
25453
- className
25454
- }
25455
- );
25456
- };
25457
- exports.IconPicker.displayName = "IconPicker";
25458
- }
25459
- });
25460
26031
  function toISODate(d) {
25461
26032
  return d.toISOString().slice(0, 10);
25462
26033
  }
@@ -28233,13 +28804,13 @@ var init_MapView = __esm({
28233
28804
  shadowSize: [41, 41]
28234
28805
  });
28235
28806
  L.Marker.prototype.options.icon = defaultIcon;
28236
- const { useEffect: useEffect71, useRef: useRef68, useCallback: useCallback114, useState: useState100 } = React74__namespace.default;
28807
+ const { useEffect: useEffect72, useRef: useRef69, useCallback: useCallback114, useState: useState101 } = React74__namespace.default;
28237
28808
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
28238
28809
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
28239
28810
  function MapUpdater({ centerLat, centerLng, zoom }) {
28240
28811
  const map = useMap();
28241
- const prevRef = useRef68({ centerLat, centerLng, zoom });
28242
- useEffect71(() => {
28812
+ const prevRef = useRef69({ centerLat, centerLng, zoom });
28813
+ useEffect72(() => {
28243
28814
  const prev = prevRef.current;
28244
28815
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
28245
28816
  map.setView([centerLat, centerLng], zoom);
@@ -28250,7 +28821,7 @@ var init_MapView = __esm({
28250
28821
  }
28251
28822
  function MapClickHandler({ onMapClick }) {
28252
28823
  const map = useMap();
28253
- useEffect71(() => {
28824
+ useEffect72(() => {
28254
28825
  if (!onMapClick) return;
28255
28826
  const handler = (e) => {
28256
28827
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -28278,7 +28849,7 @@ var init_MapView = __esm({
28278
28849
  showAttribution = true
28279
28850
  }) {
28280
28851
  const eventBus = useEventBus2();
28281
- const [clickedPosition, setClickedPosition] = useState100(null);
28852
+ const [clickedPosition, setClickedPosition] = useState101(null);
28282
28853
  const handleMapClick = useCallback114((lat, lng) => {
28283
28854
  if (showClickedPin) {
28284
28855
  setClickedPosition({ lat, lng });
@@ -33714,8 +34285,8 @@ var init_VersionDiff = __esm({
33714
34285
  return { added, removed };
33715
34286
  }, [diff]);
33716
34287
  const handleBeforeChange = React74.useCallback(
33717
- (e) => {
33718
- const id = e.target.value;
34288
+ (v) => {
34289
+ const id = v;
33719
34290
  setInternalBefore(id);
33720
34291
  onSelectBefore?.(id);
33721
34292
  if (selectBeforeEvent) eventBus.emit(`UI:${selectBeforeEvent}`, { id });
@@ -33723,8 +34294,8 @@ var init_VersionDiff = __esm({
33723
34294
  [onSelectBefore, selectBeforeEvent, eventBus]
33724
34295
  );
33725
34296
  const handleAfterChange = React74.useCallback(
33726
- (e) => {
33727
- const id = e.target.value;
34297
+ (v) => {
34298
+ const id = v;
33728
34299
  setInternalAfter(id);
33729
34300
  onSelectAfter?.(id);
33730
34301
  if (selectAfterEvent) eventBus.emit(`UI:${selectAfterEvent}`, { id });
@@ -35147,286 +35718,6 @@ var init_PageHeader = __esm({
35147
35718
  exports.PageHeader.displayName = "PageHeader";
35148
35719
  }
35149
35720
  });
35150
- exports.FormSection = void 0; exports.FormLayout = void 0; exports.FormActions = void 0;
35151
- var init_FormSection = __esm({
35152
- "components/core/molecules/FormSection.tsx"() {
35153
- "use client";
35154
- init_cn();
35155
- init_atoms2();
35156
- init_Box();
35157
- init_Typography();
35158
- init_Button();
35159
- init_Stack();
35160
- init_Icon();
35161
- init_useEventBus();
35162
- exports.FormSection = ({
35163
- title,
35164
- description,
35165
- children,
35166
- collapsible = false,
35167
- defaultCollapsed = false,
35168
- card = false,
35169
- columns = 1,
35170
- className
35171
- }) => {
35172
- const [collapsed, setCollapsed] = React74__namespace.default.useState(defaultCollapsed);
35173
- const { t } = hooks.useTranslate();
35174
- const eventBus = useEventBus();
35175
- const gridClass = {
35176
- 1: "grid-cols-1",
35177
- 2: "grid-cols-1 md:grid-cols-2",
35178
- 3: "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
35179
- }[columns];
35180
- React74__namespace.default.useCallback(() => {
35181
- if (collapsible) {
35182
- setCollapsed((prev) => !prev);
35183
- eventBus.emit("UI:TOGGLE_COLLAPSE", { collapsed: !collapsed });
35184
- }
35185
- }, [collapsible, collapsed, eventBus]);
35186
- const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
35187
- (title || description) && /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", className: "mb-4", children: [
35188
- title && /* @__PURE__ */ jsxRuntime.jsxs(
35189
- exports.HStack,
35190
- {
35191
- justify: "between",
35192
- align: "center",
35193
- className: cn(collapsible && "cursor-pointer"),
35194
- action: collapsible ? "TOGGLE_COLLAPSE" : void 0,
35195
- children: [
35196
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h3", weight: "semibold", children: title }),
35197
- collapsible && /* @__PURE__ */ jsxRuntime.jsx(
35198
- exports.Button,
35199
- {
35200
- variant: "ghost",
35201
- size: "sm",
35202
- action: "TOGGLE_COLLAPSE",
35203
- children: /* @__PURE__ */ jsxRuntime.jsx(
35204
- exports.Icon,
35205
- {
35206
- icon: LucideIcons2.ChevronDown,
35207
- size: "sm",
35208
- className: cn(
35209
- "text-muted-foreground transition-transform",
35210
- collapsed && "rotate-180"
35211
- )
35212
- }
35213
- )
35214
- }
35215
- )
35216
- ]
35217
- }
35218
- ),
35219
- description && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", color: "secondary", children: description })
35220
- ] }),
35221
- (!collapsible || !collapsed) && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: cn("grid gap-4", gridClass), children })
35222
- ] });
35223
- if (card) {
35224
- return /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: cn("p-6", className), children: content });
35225
- }
35226
- return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className, children: content });
35227
- };
35228
- exports.FormSection.displayName = "FormSection";
35229
- exports.FormLayout = ({
35230
- children,
35231
- dividers = true,
35232
- className
35233
- }) => {
35234
- return /* @__PURE__ */ jsxRuntime.jsx(
35235
- exports.VStack,
35236
- {
35237
- gap: "lg",
35238
- className: cn(
35239
- dividers && "[&>*+*]:pt-8 [&>*+*]:border-t [&>*+*]:border-border",
35240
- className
35241
- ),
35242
- children
35243
- }
35244
- );
35245
- };
35246
- exports.FormLayout.displayName = "FormLayout";
35247
- exports.FormActions = ({
35248
- children,
35249
- sticky = false,
35250
- align = "right",
35251
- className
35252
- }) => {
35253
- const alignClass2 = {
35254
- left: "justify-start",
35255
- right: "justify-end",
35256
- between: "justify-between",
35257
- center: "justify-center"
35258
- }[align];
35259
- return /* @__PURE__ */ jsxRuntime.jsx(
35260
- exports.HStack,
35261
- {
35262
- gap: "sm",
35263
- align: "center",
35264
- className: cn(
35265
- "pt-6 border-t border-border",
35266
- alignClass2,
35267
- sticky && "sticky bottom-0 bg-card py-4 -mx-6 px-6 shadow-[0_-4px_6px_-1px_rgb(0,0,0,0.05)]",
35268
- className
35269
- ),
35270
- children
35271
- }
35272
- );
35273
- };
35274
- exports.FormActions.displayName = "FormActions";
35275
- }
35276
- });
35277
- function currentValue(decl, override) {
35278
- return override !== void 0 ? override : decl.default;
35279
- }
35280
- function TextLikeControl({
35281
- field,
35282
- numeric,
35283
- value,
35284
- onCommit
35285
- }) {
35286
- const initial = value === void 0 || value === null ? "" : String(value);
35287
- const [draft, setDraft] = React74__namespace.default.useState(initial);
35288
- React74__namespace.default.useEffect(() => setDraft(initial), [initial]);
35289
- const commit = () => {
35290
- if (numeric) {
35291
- const n = draft.trim() === "" ? 0 : Number(draft);
35292
- onCommit(field, Number.isNaN(n) ? 0 : n);
35293
- } else {
35294
- onCommit(field, draft);
35295
- }
35296
- };
35297
- return /* @__PURE__ */ jsxRuntime.jsx(
35298
- exports.Input,
35299
- {
35300
- inputType: numeric ? "number" : "text",
35301
- value: draft,
35302
- onChange: (e) => setDraft(e.target.value),
35303
- onBlur: commit,
35304
- onKeyDown: (e) => {
35305
- if (e.key === "Enter") commit();
35306
- }
35307
- }
35308
- );
35309
- }
35310
- function isTraitConfigObject(v) {
35311
- return v !== null && v !== void 0 && typeof v === "object" && !Array.isArray(v);
35312
- }
35313
- function FieldControl({
35314
- name,
35315
- decl,
35316
- value,
35317
- onChange,
35318
- assets
35319
- }) {
35320
- let control;
35321
- const stringValue = typeof value === "string" ? value : void 0;
35322
- const effectiveValue = value ?? decl.default;
35323
- if (decl.type === "icon") {
35324
- control = /* @__PURE__ */ jsxRuntime.jsx(exports.IconPicker, { value: stringValue, onChange: (icon) => onChange(name, icon) });
35325
- } else if (decl.type === "asset") {
35326
- control = /* @__PURE__ */ jsxRuntime.jsx(
35327
- exports.AssetPicker,
35328
- {
35329
- assets: assets ?? [],
35330
- value: stringValue,
35331
- onChange: (url) => onChange(name, url)
35332
- }
35333
- );
35334
- } else if (decl.type === "boolean") {
35335
- control = /* @__PURE__ */ jsxRuntime.jsx(exports.Switch, { checked: value === true, onChange: (c) => onChange(name, c) });
35336
- } else if (decl.type === "string" && decl.values !== void 0 && decl.values.length > 0) {
35337
- control = /* @__PURE__ */ jsxRuntime.jsx(
35338
- exports.Select,
35339
- {
35340
- options: decl.values.map((v) => ({ value: v, label: v })),
35341
- value: typeof value === "string" ? value : "",
35342
- onChange: (e) => onChange(name, e.target.value)
35343
- }
35344
- );
35345
- } else if (decl.type === "number") {
35346
- control = /* @__PURE__ */ jsxRuntime.jsx(TextLikeControl, { field: name, numeric: true, value, onCommit: onChange });
35347
- } else if (decl.type === "string") {
35348
- control = /* @__PURE__ */ jsxRuntime.jsx(TextLikeControl, { field: name, numeric: false, value, onCommit: onChange });
35349
- } else if (decl.type === "node") {
35350
- control = /* @__PURE__ */ jsxRuntime.jsx(exports.NodeSlotEditor, { value: effectiveValue, onChange: (next) => onChange(name, next) });
35351
- } else if (decl.type.startsWith("[") || Array.isArray(effectiveValue)) {
35352
- const arr = Array.isArray(effectiveValue) ? effectiveValue : [];
35353
- control = /* @__PURE__ */ jsxRuntime.jsx(exports.JsonTreeEditor, { value: arr, onChange: (next) => onChange(name, next) });
35354
- } else if (decl.type === "object" || decl.type.startsWith("Map ") || !SCALAR_TYPES.has(decl.type) && isTraitConfigObject(effectiveValue)) {
35355
- const obj = isTraitConfigObject(effectiveValue) ? effectiveValue : {};
35356
- control = /* @__PURE__ */ jsxRuntime.jsx(exports.JsonTreeEditor, { value: obj, onChange: (next) => onChange(name, next) });
35357
- } else {
35358
- control = /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "caption", color: "muted", children: [
35359
- decl.type,
35360
- " \u2014 edit in source"
35361
- ] });
35362
- }
35363
- return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", children: [
35364
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "label", children: decl.label ?? name }),
35365
- control,
35366
- decl.description !== void 0 && decl.description !== "" && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "muted", children: decl.description })
35367
- ] });
35368
- }
35369
- var TIER_ORDER, SCALAR_TYPES; exports.PropertyInspector = void 0;
35370
- var init_PropertyInspector = __esm({
35371
- "components/core/molecules/PropertyInspector.tsx"() {
35372
- "use client";
35373
- init_cn();
35374
- init_Stack();
35375
- init_Typography();
35376
- init_Button();
35377
- init_Switch();
35378
- init_Select();
35379
- init_Input();
35380
- init_FormSection();
35381
- init_IconPicker();
35382
- init_AssetPicker();
35383
- init_JsonTreeEditor();
35384
- init_NodeSlotEditor();
35385
- TIER_ORDER = ["presentation", "domain", "policy", "infra", "internal"];
35386
- SCALAR_TYPES = /* @__PURE__ */ new Set(["string", "number", "boolean", "icon", "asset"]);
35387
- exports.PropertyInspector = ({
35388
- config,
35389
- values,
35390
- onChange,
35391
- onReset,
35392
- title,
35393
- className,
35394
- assets
35395
- }) => {
35396
- const fields = Object.entries(config);
35397
- const byTier = /* @__PURE__ */ new Map();
35398
- for (const [name, decl] of fields) {
35399
- const tier = decl.tier ?? "presentation";
35400
- const arr = byTier.get(tier) ?? [];
35401
- arr.push([name, decl]);
35402
- byTier.set(tier, arr);
35403
- }
35404
- const tiers = [...byTier.keys()].sort((a, b) => {
35405
- const ia = TIER_ORDER.indexOf(a);
35406
- const ib = TIER_ORDER.indexOf(b);
35407
- return (ia === -1 ? 99 : ia) - (ib === -1 ? 99 : ib);
35408
- });
35409
- return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "sm", className: cn("w-full", className), children: [
35410
- /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { justify: "between", align: "center", children: [
35411
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", weight: "bold", children: title ?? "Config" }),
35412
- onReset !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "ghost", size: "sm", icon: "rotate-ccw", label: "Reset", onClick: onReset })
35413
- ] }),
35414
- fields.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "muted", children: "No configurable properties." }),
35415
- tiers.map((tier) => /* @__PURE__ */ jsxRuntime.jsx(exports.FormSection, { title: tier, collapsible: true, defaultCollapsed: tier !== "presentation", children: /* @__PURE__ */ jsxRuntime.jsx(exports.VStack, { gap: "sm", children: byTier.get(tier)?.map(([name, decl]) => /* @__PURE__ */ jsxRuntime.jsx(
35416
- FieldControl,
35417
- {
35418
- name,
35419
- decl,
35420
- value: currentValue(decl, values?.[name]),
35421
- onChange,
35422
- assets
35423
- },
35424
- name
35425
- )) }) }, tier))
35426
- ] });
35427
- };
35428
- }
35429
- });
35430
35721
  var lookStyles8; exports.Header = void 0;
35431
35722
  var init_Header = __esm({
35432
35723
  "components/core/molecules/Header.tsx"() {
@@ -36782,7 +37073,6 @@ var init_DocumentViewer = __esm({
36782
37073
  showPrint = false,
36783
37074
  actions,
36784
37075
  documents,
36785
- entity,
36786
37076
  isLoading = false,
36787
37077
  error,
36788
37078
  className
@@ -38851,11 +39141,11 @@ function RuleEditor({
38851
39141
  className
38852
39142
  }) {
38853
39143
  const { t } = hooks.useTranslate();
38854
- const handleWhenChange = React74.useCallback((e) => {
38855
- onChange({ ...rule, whenEvent: e.target.value });
39144
+ const handleWhenChange = React74.useCallback((v) => {
39145
+ onChange({ ...rule, whenEvent: v });
38856
39146
  }, [rule, onChange]);
38857
- const handleThenChange = React74.useCallback((e) => {
38858
- onChange({ ...rule, thenAction: e.target.value });
39147
+ const handleThenChange = React74.useCallback((v) => {
39148
+ onChange({ ...rule, thenAction: v });
38859
39149
  }, [rule, onChange]);
38860
39150
  return /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { className: cn("items-center p-2 rounded-lg bg-muted/50 border border-border", className), gap: "sm", children: [
38861
39151
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", className: "text-primary font-bold whitespace-nowrap", children: t("eventHandler.when") }),
@@ -39931,7 +40221,7 @@ var init_Form = __esm({
39931
40221
  ...commonProps,
39932
40222
  options,
39933
40223
  value: String(currentValue2),
39934
- onChange: (e) => handleChange(fieldName, e.target.value),
40224
+ onChange: (v) => handleChange(fieldName, v),
39935
40225
  placeholder: field.placeholder || `Select ${label}...`
39936
40226
  }
39937
40227
  );
@@ -43952,7 +44242,7 @@ function TraitSlot({
43952
44242
  size = "md",
43953
44243
  showTooltip = true,
43954
44244
  categoryColors,
43955
- tooltipFrameUrl,
44245
+ tooltipFrameUrl = "",
43956
44246
  className,
43957
44247
  feedback,
43958
44248
  onItemDrop,
@@ -47170,18 +47460,32 @@ var init_XPBar = __esm({
47170
47460
  }
47171
47461
  });
47172
47462
  function lazyThree(name, loader) {
47173
- const Lazy = React74__namespace.default.lazy(() => loader().then((m) => ({ default: m[name] })));
47463
+ const Lazy = React74__namespace.default.lazy(
47464
+ () => loader().then((m) => {
47465
+ const Resolved = m[name];
47466
+ if (!Resolved) {
47467
+ throw new Error(
47468
+ `[@almadar/ui] 3D component "${name}" was not found in the three subpath bundle.`
47469
+ );
47470
+ }
47471
+ return { default: Resolved };
47472
+ })
47473
+ );
47174
47474
  function ThreeWrapper(props) {
47175
47475
  return React74__namespace.default.createElement(
47176
- React74__namespace.default.Suspense,
47177
- { fallback: null },
47178
- React74__namespace.default.createElement(Lazy, props)
47476
+ ThreeBoundary,
47477
+ { name },
47478
+ React74__namespace.default.createElement(
47479
+ React74__namespace.default.Suspense,
47480
+ { fallback: null },
47481
+ React74__namespace.default.createElement(Lazy, props)
47482
+ )
47179
47483
  );
47180
47484
  }
47181
47485
  ThreeWrapper.displayName = `Lazy(${name})`;
47182
47486
  return ThreeWrapper;
47183
47487
  }
47184
- var FeatureRenderer, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
47488
+ var ThreeBoundary, FeatureRenderer, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
47185
47489
  var init_component_registry_generated = __esm({
47186
47490
  "components/core/organisms/component-registry.generated.ts"() {
47187
47491
  init_AboutPageTemplate();
@@ -47467,6 +47771,28 @@ var init_component_registry_generated = __esm({
47467
47771
  init_WorldMapBoard();
47468
47772
  init_WorldMapTemplate();
47469
47773
  init_XPBar();
47774
+ ThreeBoundary = class extends React74__namespace.default.Component {
47775
+ constructor() {
47776
+ super(...arguments);
47777
+ __publicField(this, "state", { failed: false });
47778
+ }
47779
+ static getDerivedStateFromError() {
47780
+ return { failed: true };
47781
+ }
47782
+ render() {
47783
+ if (this.state.failed) {
47784
+ return React74__namespace.default.createElement(
47785
+ "div",
47786
+ {
47787
+ "data-testid": "three-unavailable",
47788
+ style: { padding: 16, fontSize: 13, lineHeight: 1.5, opacity: 0.7 }
47789
+ },
47790
+ `3D pattern "${this.props.name}" requires three.js. Install the optional peers three + @react-three/fiber + @react-three/drei (matching the host React major) to render it.`
47791
+ );
47792
+ }
47793
+ return this.props.children;
47794
+ }
47795
+ };
47470
47796
  FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/molecules/game/three'));
47471
47797
  GameCanvas3D = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
47472
47798
  GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
@@ -49749,195 +50075,6 @@ init_StepFlowOrganism();
49749
50075
  init_ShowcaseOrganism();
49750
50076
  init_TeamOrganism();
49751
50077
  init_CaseStudyOrganism();
49752
- var DEFAULT_FEATURE_CONFIGS = {
49753
- tree: { color: 2263842, height: 1.5, scale: 1, geometry: "tree" },
49754
- rock: { color: 8421504, height: 0.5, scale: 0.8, geometry: "rock" },
49755
- bush: { color: 3329330, height: 0.4, scale: 0.6, geometry: "bush" },
49756
- house: { color: 9127187, height: 1.2, scale: 1.2, geometry: "house" },
49757
- tower: { color: 6908265, height: 2.5, scale: 1, geometry: "tower" },
49758
- wall: { color: 8421504, height: 1, scale: 1, geometry: "wall" },
49759
- mountain: { color: 5597999, height: 2, scale: 1.5, geometry: "mountain" },
49760
- hill: { color: 7048739, height: 0.8, scale: 1.2, geometry: "hill" },
49761
- water: { color: 4491468, height: 0.1, scale: 1, geometry: "water" },
49762
- chest: { color: 16766720, height: 0.3, scale: 0.4, geometry: "chest" },
49763
- sign: { color: 9127187, height: 0.8, scale: 0.3, geometry: "sign" },
49764
- portal: { color: 10040012, height: 1.5, scale: 1, geometry: "portal" }
49765
- };
49766
- function TreeFeature({ height, color }) {
49767
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
49768
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 0.3, 0], children: [
49769
- /* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.08, 0.1, height * 0.6, 6] }),
49770
- /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 9127187 })
49771
- ] }),
49772
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 0.7, 0], children: [
49773
- /* @__PURE__ */ jsxRuntime.jsx("coneGeometry", { args: [0.4, height * 0.5, 8] }),
49774
- /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
49775
- ] }),
49776
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 0.9, 0], children: [
49777
- /* @__PURE__ */ jsxRuntime.jsx("coneGeometry", { args: [0.3, height * 0.4, 8] }),
49778
- /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
49779
- ] }),
49780
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 1.05, 0], children: [
49781
- /* @__PURE__ */ jsxRuntime.jsx("coneGeometry", { args: [0.15, height * 0.25, 8] }),
49782
- /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
49783
- ] })
49784
- ] });
49785
- }
49786
- function RockFeature({ height, color }) {
49787
- return /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 0.4, 0], children: [
49788
- /* @__PURE__ */ jsxRuntime.jsx("dodecahedronGeometry", { args: [height * 0.5, 0] }),
49789
- /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color, roughness: 0.9 })
49790
- ] });
49791
- }
49792
- function BushFeature({ height, color }) {
49793
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
49794
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 0.3, 0], children: [
49795
- /* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [height * 0.4, 8, 8] }),
49796
- /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
49797
- ] }),
49798
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0.1, height * 0.4, 0.1], children: [
49799
- /* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [height * 0.25, 8, 8] }),
49800
- /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
49801
- ] })
49802
- ] });
49803
- }
49804
- function HouseFeature({ height, color }) {
49805
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
49806
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 0.4, 0], children: [
49807
- /* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [0.8, height * 0.8, 0.8] }),
49808
- /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 13808780 })
49809
- ] }),
49810
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 0.9, 0], children: [
49811
- /* @__PURE__ */ jsxRuntime.jsx("coneGeometry", { args: [0.6, height * 0.4, 4] }),
49812
- /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
49813
- ] }),
49814
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 0.25, 0.41], children: [
49815
- /* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [0.25, height * 0.5] }),
49816
- /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 4863784 })
49817
- ] })
49818
- ] });
49819
- }
49820
- function TowerFeature({ height, color }) {
49821
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
49822
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 0.5, 0], children: [
49823
- /* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.3, 0.35, height, 8] }),
49824
- /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
49825
- ] }),
49826
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height + 0.05, 0], children: [
49827
- /* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.35, 0.35, 0.1, 8] }),
49828
- /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
49829
- ] })
49830
- ] });
49831
- }
49832
- function ChestFeature({ height, color }) {
49833
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
49834
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 0.5, 0], children: [
49835
- /* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [0.3, height, 0.2] }),
49836
- /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color, metalness: 0.6, roughness: 0.3 })
49837
- ] }),
49838
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height + 0.05, 0], children: [
49839
- /* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.15, 0.15, 0.3, 8, 1, false, 0, Math.PI] }),
49840
- /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color, metalness: 0.6, roughness: 0.3 })
49841
- ] })
49842
- ] });
49843
- }
49844
- function DefaultFeature({ height, color }) {
49845
- return /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 0.5, 0], children: [
49846
- /* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [0.5, height, 0.5] }),
49847
- /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
49848
- ] });
49849
- }
49850
- function FeatureVisual({
49851
- feature,
49852
- position,
49853
- isSelected,
49854
- onClick,
49855
- onHover
49856
- }) {
49857
- const config = DEFAULT_FEATURE_CONFIGS[feature.type] || {
49858
- color: 8947848,
49859
- height: 0.5,
49860
- scale: 1,
49861
- geometry: "default"
49862
- };
49863
- const color = feature.color ? parseInt(feature.color.replace("#", ""), 16) : config.color;
49864
- const renderGeometry = () => {
49865
- switch (config.geometry) {
49866
- case "tree":
49867
- return /* @__PURE__ */ jsxRuntime.jsx(TreeFeature, { height: config.height, color });
49868
- case "rock":
49869
- return /* @__PURE__ */ jsxRuntime.jsx(RockFeature, { height: config.height, color });
49870
- case "bush":
49871
- return /* @__PURE__ */ jsxRuntime.jsx(BushFeature, { height: config.height, color });
49872
- case "house":
49873
- return /* @__PURE__ */ jsxRuntime.jsx(HouseFeature, { height: config.height, color });
49874
- case "tower":
49875
- return /* @__PURE__ */ jsxRuntime.jsx(TowerFeature, { height: config.height, color });
49876
- case "chest":
49877
- return /* @__PURE__ */ jsxRuntime.jsx(ChestFeature, { height: config.height, color });
49878
- default:
49879
- return /* @__PURE__ */ jsxRuntime.jsx(DefaultFeature, { height: config.height, color });
49880
- }
49881
- };
49882
- return /* @__PURE__ */ jsxRuntime.jsxs(
49883
- "group",
49884
- {
49885
- position,
49886
- scale: config.scale,
49887
- onClick,
49888
- onPointerEnter: () => onHover(true),
49889
- onPointerLeave: () => onHover(false),
49890
- userData: { type: "feature", featureId: feature.id, featureType: feature.type },
49891
- children: [
49892
- isSelected && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
49893
- /* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
49894
- /* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#ffff00", transparent: true, opacity: 0.8 })
49895
- ] }),
49896
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.01, 0], rotation: [-Math.PI / 2, 0, 0], children: [
49897
- /* @__PURE__ */ jsxRuntime.jsx("circleGeometry", { args: [0.35, 16] }),
49898
- /* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#000000", transparent: true, opacity: 0.2 })
49899
- ] }),
49900
- renderGeometry()
49901
- ]
49902
- }
49903
- );
49904
- }
49905
- function FeatureGroup({
49906
- features,
49907
- cellSize = 1,
49908
- offsetX = 0,
49909
- offsetZ = 0,
49910
- onFeatureClick,
49911
- onFeatureHover,
49912
- selectedFeatureIds = []
49913
- }) {
49914
- return /* @__PURE__ */ jsxRuntime.jsx("group", { children: features.map((feature) => {
49915
- const x = (feature.x - offsetX) * cellSize;
49916
- const z = ((feature.z ?? feature.y ?? 0) - offsetZ) * cellSize;
49917
- const y = (feature.elevation ?? 0) * 0.1;
49918
- const isSelected = feature.id ? selectedFeatureIds.includes(feature.id) : false;
49919
- return /* @__PURE__ */ jsxRuntime.jsx(
49920
- FeatureVisual,
49921
- {
49922
- feature,
49923
- position: [x, y, z],
49924
- isSelected,
49925
- onClick: () => onFeatureClick?.(feature),
49926
- onHover: (hovered) => onFeatureHover?.(hovered ? feature : null)
49927
- },
49928
- feature.id ?? `feature-${feature.x}-${feature.y}`
49929
- );
49930
- }) });
49931
- }
49932
- function FeatureRenderer2(props) {
49933
- const insideCanvas = React74.useContext(fiber.context) != null;
49934
- if (insideCanvas) return /* @__PURE__ */ jsxRuntime.jsx(FeatureGroup, { ...props });
49935
- return /* @__PURE__ */ jsxRuntime.jsxs(fiber.Canvas, { camera: { position: [4, 4, 6], fov: 50 }, style: { width: "100%", height: 360 }, children: [
49936
- /* @__PURE__ */ jsxRuntime.jsx("ambientLight", { intensity: 0.6 }),
49937
- /* @__PURE__ */ jsxRuntime.jsx("directionalLight", { position: [5, 8, 5], intensity: 0.8 }),
49938
- /* @__PURE__ */ jsxRuntime.jsx(FeatureGroup, { ...props })
49939
- ] });
49940
- }
49941
50078
 
49942
50079
  // components/core/templates/index.ts
49943
50080
  init_DashboardLayout();
@@ -49997,7 +50134,6 @@ exports.EnemyPlate = EnemyPlate;
49997
50134
  exports.EventHandlerBoard = EventHandlerBoard;
49998
50135
  exports.EventLog = EventLog;
49999
50136
  exports.FEATURE_TYPES = FEATURE_TYPES;
50000
- exports.FeatureRenderer = FeatureRenderer2;
50001
50137
  exports.GameAudioProvider = GameAudioProvider;
50002
50138
  exports.GameAudioToggle = GameAudioToggle;
50003
50139
  exports.GameCanvas2D = GameCanvas2D;