@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,8 +45,7 @@ import { useSortable, arrayMove, sortableKeyboardCoordinates, SortableContext, r
45
45
  import { CSS } from '@dnd-kit/utilities';
46
46
  import { useNodeId, ReactFlowProvider, Handle, Position } from '@xyflow/react';
47
47
  import { useUISlots } from '@almadar/ui/context';
48
- import { getPatternDefinition, getComponentForPattern as getComponentForPattern$1 } from '@almadar/patterns';
49
- import { context, Canvas } from '@react-three/fiber';
48
+ import { getPatternDefinition as getPatternDefinition$1, getComponentForPattern as getComponentForPattern$1 } from '@almadar/patterns';
50
49
 
51
50
  var __defProp = Object.defineProperty;
52
51
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -2139,6 +2138,8 @@ var init_Input = __esm({
2139
2138
  className,
2140
2139
  inputType,
2141
2140
  type: htmlType,
2141
+ label,
2142
+ helperText,
2142
2143
  error,
2143
2144
  leftIcon,
2144
2145
  rightIcon,
@@ -2194,82 +2195,95 @@ var init_Input = __esm({
2194
2195
  onClear?.();
2195
2196
  }
2196
2197
  };
2198
+ const wrapField = (field) => /* @__PURE__ */ jsxs("div", { className: "w-full", children: [
2199
+ label && /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-foreground mb-1", children: label }),
2200
+ field,
2201
+ (helperText || error) && /* @__PURE__ */ jsx("p", { className: cn("mt-1 text-xs", error ? "text-error" : "text-muted-foreground"), children: error ?? helperText })
2202
+ ] });
2197
2203
  if (type === "select") {
2198
- return /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
2199
- resolvedLeftIcon && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
2200
- /* @__PURE__ */ jsxs(
2201
- "select",
2204
+ return wrapField(
2205
+ /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
2206
+ resolvedLeftIcon && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
2207
+ /* @__PURE__ */ jsxs(
2208
+ "select",
2209
+ {
2210
+ ref,
2211
+ value,
2212
+ onChange: handleChange,
2213
+ className: cn(baseClassName, "appearance-none pr-10", className),
2214
+ ...props,
2215
+ children: [
2216
+ /* @__PURE__ */ jsx("option", { value: "", children: t("form.selectPlaceholder", { label: "" }) }),
2217
+ options?.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
2218
+ ]
2219
+ }
2220
+ ),
2221
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none text-muted-foreground", children: /* @__PURE__ */ jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default" }) })
2222
+ ] })
2223
+ );
2224
+ }
2225
+ if (type === "textarea") {
2226
+ return wrapField(
2227
+ /* @__PURE__ */ jsx("div", { className: "relative w-full", children: /* @__PURE__ */ jsx(
2228
+ "textarea",
2202
2229
  {
2203
2230
  ref,
2204
2231
  value,
2205
2232
  onChange: handleChange,
2206
- className: cn(baseClassName, "appearance-none pr-10", className),
2207
- ...props,
2208
- children: [
2209
- /* @__PURE__ */ jsx("option", { value: "", children: t("form.selectPlaceholder", { label: "" }) }),
2210
- options?.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
2211
- ]
2233
+ rows: rows2,
2234
+ className: baseClassName,
2235
+ ...props
2212
2236
  }
2213
- ),
2214
- /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none text-muted-foreground", children: /* @__PURE__ */ jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default" }) })
2215
- ] });
2216
- }
2217
- if (type === "textarea") {
2218
- return /* @__PURE__ */ jsx("div", { className: "relative w-full", children: /* @__PURE__ */ jsx(
2219
- "textarea",
2220
- {
2221
- ref,
2222
- value,
2223
- onChange: handleChange,
2224
- rows: rows2,
2225
- className: baseClassName,
2226
- ...props
2227
- }
2228
- ) });
2237
+ ) })
2238
+ );
2229
2239
  }
2230
2240
  if (type === "checkbox") {
2231
- return /* @__PURE__ */ jsx(
2232
- "input",
2233
- {
2234
- ref,
2235
- type: "checkbox",
2236
- checked: props.checked,
2237
- onChange: handleChange,
2238
- className: cn(
2239
- "h-icon-default w-icon-default rounded-sm",
2240
- "border-border",
2241
- "text-primary focus:ring-ring",
2242
- "disabled:opacity-50 disabled:cursor-not-allowed",
2243
- className
2244
- ),
2245
- ...props
2246
- }
2241
+ return wrapField(
2242
+ /* @__PURE__ */ jsx(
2243
+ "input",
2244
+ {
2245
+ ref,
2246
+ type: "checkbox",
2247
+ checked: props.checked,
2248
+ onChange: handleChange,
2249
+ className: cn(
2250
+ "h-icon-default w-icon-default rounded-sm",
2251
+ "border-border",
2252
+ "text-primary focus:ring-ring",
2253
+ "disabled:opacity-50 disabled:cursor-not-allowed",
2254
+ className
2255
+ ),
2256
+ ...props
2257
+ }
2258
+ )
2247
2259
  );
2248
2260
  }
2249
- return /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
2250
- resolvedLeftIcon && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
2251
- /* @__PURE__ */ jsx(
2252
- "input",
2253
- {
2254
- ref,
2255
- type,
2256
- value,
2257
- onChange: handleChange,
2258
- className: baseClassName,
2259
- ...props
2260
- }
2261
- ),
2262
- showClearButton && /* @__PURE__ */ jsx(
2263
- "button",
2264
- {
2265
- type: "button",
2266
- onClick: handleClear,
2267
- className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground hover:text-foreground",
2268
- children: /* @__PURE__ */ jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
2269
- }
2270
- ),
2271
- rightIcon && !showClearButton && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground", children: resolveIconNode(rightIcon, iconCls) })
2272
- ] });
2261
+ return wrapField(
2262
+ /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
2263
+ resolvedLeftIcon && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
2264
+ /* @__PURE__ */ jsx(
2265
+ "input",
2266
+ {
2267
+ ref,
2268
+ type,
2269
+ value,
2270
+ onChange: handleChange,
2271
+ className: baseClassName,
2272
+ ...props
2273
+ }
2274
+ ),
2275
+ showClearButton && /* @__PURE__ */ jsx(
2276
+ "button",
2277
+ {
2278
+ type: "button",
2279
+ onClick: handleClear,
2280
+ className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground hover:text-foreground",
2281
+ children: /* @__PURE__ */ jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
2282
+ }
2283
+ ),
2284
+ rightIcon && !showClearButton && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground", children: resolveIconNode(rightIcon, iconCls) })
2285
+ ] })
2286
+ );
2273
2287
  }
2274
2288
  );
2275
2289
  Input.displayName = "Input";
@@ -2340,6 +2354,190 @@ var init_Textarea = __esm({
2340
2354
  Textarea.displayName = "Textarea";
2341
2355
  }
2342
2356
  });
2357
+ function flatOptions(opts, groups) {
2358
+ const flat = opts ?? [];
2359
+ const grp = (groups ?? []).flatMap((g) => g.options);
2360
+ return [...flat, ...grp];
2361
+ }
2362
+ function NativeSelect({
2363
+ className,
2364
+ options,
2365
+ groups,
2366
+ placeholder,
2367
+ error,
2368
+ onChange,
2369
+ value,
2370
+ ...props
2371
+ }) {
2372
+ const eventBus = useEventBus();
2373
+ const handleChange = (e) => {
2374
+ if (typeof onChange === "string") {
2375
+ eventBus.emit(`UI:${onChange}`, { value: e.target.value });
2376
+ } else {
2377
+ onChange?.(e.target.value);
2378
+ }
2379
+ };
2380
+ return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
2381
+ /* @__PURE__ */ jsxs(
2382
+ "select",
2383
+ {
2384
+ onChange: handleChange,
2385
+ value,
2386
+ className: cn(
2387
+ "block w-full border-[length:var(--border-width)] shadow-sm appearance-none",
2388
+ "px-3 py-2 pr-10 text-sm text-foreground font-medium",
2389
+ "bg-card",
2390
+ "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
2391
+ "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
2392
+ error ? "border-error focus:border-error" : "border-border focus:border-primary",
2393
+ className
2394
+ ),
2395
+ ...props,
2396
+ children: [
2397
+ placeholder && /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: placeholder }),
2398
+ options?.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)),
2399
+ groups?.map((group) => /* @__PURE__ */ jsx("optgroup", { label: group.label, children: group.options.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)) }, group.label))
2400
+ ]
2401
+ }
2402
+ ),
2403
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default text-foreground" }) })
2404
+ ] });
2405
+ }
2406
+ function RichSelect({
2407
+ className,
2408
+ options,
2409
+ groups,
2410
+ placeholder,
2411
+ error,
2412
+ onChange,
2413
+ value,
2414
+ multiple,
2415
+ searchable,
2416
+ clearable,
2417
+ disabled
2418
+ }) {
2419
+ const eventBus = useEventBus();
2420
+ const [open, setOpen] = useState(false);
2421
+ const [search, setSearch] = useState("");
2422
+ const containerRef = useRef(null);
2423
+ const selected = multiple ? Array.isArray(value) ? value : value ? [value] : [] : value ? [value] : [];
2424
+ const all = flatOptions(options, groups);
2425
+ const filtered = searchable && search ? all.filter((o) => o.label.toLowerCase().includes(search.toLowerCase())) : all;
2426
+ const toggle = (optValue) => {
2427
+ let next;
2428
+ if (multiple) {
2429
+ next = selected.includes(optValue) ? selected.filter((v) => v !== optValue) : [...selected, optValue];
2430
+ } else {
2431
+ next = optValue;
2432
+ setOpen(false);
2433
+ }
2434
+ if (typeof onChange === "string") {
2435
+ eventBus.emit(`UI:${onChange}`, { value: next });
2436
+ } else {
2437
+ onChange?.(next);
2438
+ }
2439
+ };
2440
+ const clear = (e) => {
2441
+ e.stopPropagation();
2442
+ const next = multiple ? [] : "";
2443
+ if (typeof onChange === "string") {
2444
+ eventBus.emit(`UI:${onChange}`, { value: next });
2445
+ } else {
2446
+ onChange?.(next);
2447
+ }
2448
+ };
2449
+ useEffect(() => {
2450
+ const handler = (e) => {
2451
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
2452
+ setOpen(false);
2453
+ setSearch("");
2454
+ }
2455
+ };
2456
+ document.addEventListener("mousedown", handler);
2457
+ return () => document.removeEventListener("mousedown", handler);
2458
+ }, []);
2459
+ const displayLabel = selected.length === 0 ? placeholder ?? "" : multiple ? `${selected.length} selected` : all.find((o) => o.value === selected[0])?.label ?? selected[0];
2460
+ const hasValue = selected.length > 0;
2461
+ const renderOptions = (opts) => opts.map((opt) => /* @__PURE__ */ jsxs(
2462
+ "button",
2463
+ {
2464
+ type: "button",
2465
+ disabled: opt.disabled,
2466
+ onClick: () => !opt.disabled && toggle(opt.value),
2467
+ className: cn(
2468
+ "w-full flex items-center justify-between px-3 py-1.5 text-sm text-start",
2469
+ "hover:bg-muted transition-colors",
2470
+ "disabled:opacity-50 disabled:cursor-not-allowed",
2471
+ selected.includes(opt.value) && "text-primary font-medium"
2472
+ ),
2473
+ children: [
2474
+ /* @__PURE__ */ jsx("span", { children: opt.label }),
2475
+ selected.includes(opt.value) && /* @__PURE__ */ jsx(Icon, { name: "check", className: "h-icon-default w-icon-default" })
2476
+ ]
2477
+ },
2478
+ opt.value
2479
+ ));
2480
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: cn("relative w-full", className), children: [
2481
+ /* @__PURE__ */ jsx(
2482
+ "button",
2483
+ {
2484
+ type: "button",
2485
+ disabled,
2486
+ onClick: () => !disabled && setOpen((o) => !o),
2487
+ className: cn(
2488
+ "block w-full border-[length:var(--border-width)] shadow-sm",
2489
+ "px-3 py-2 pr-10 text-sm text-start font-medium",
2490
+ "bg-card rounded-sm",
2491
+ "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
2492
+ "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
2493
+ error ? "border-error focus:border-error" : "border-border focus:border-primary",
2494
+ !hasValue && "text-muted-foreground"
2495
+ ),
2496
+ children: displayLabel
2497
+ }
2498
+ ),
2499
+ /* @__PURE__ */ jsxs("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center gap-1 pointer-events-none", children: [
2500
+ clearable && hasValue && /* @__PURE__ */ jsx(
2501
+ "button",
2502
+ {
2503
+ type: "button",
2504
+ onClick: clear,
2505
+ className: "pointer-events-auto text-muted-foreground hover:text-foreground",
2506
+ children: /* @__PURE__ */ jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
2507
+ }
2508
+ ),
2509
+ /* @__PURE__ */ jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default text-foreground" })
2510
+ ] }),
2511
+ open && /* @__PURE__ */ jsxs("div", { className: cn(
2512
+ "absolute z-50 mt-1 w-full",
2513
+ "bg-card border-[length:var(--border-width)] border-border",
2514
+ "rounded-sm shadow-elevation-popover py-1 max-h-60 overflow-y-auto"
2515
+ ), children: [
2516
+ searchable && /* @__PURE__ */ jsx("div", { className: "px-2 pb-1 border-b border-border", children: /* @__PURE__ */ jsx(
2517
+ "input",
2518
+ {
2519
+ autoFocus: true,
2520
+ type: "text",
2521
+ value: search,
2522
+ onChange: (e) => setSearch(e.target.value),
2523
+ placeholder: "Search\u2026",
2524
+ className: cn(
2525
+ "w-full px-2 py-1 text-sm bg-transparent",
2526
+ "focus:outline-none text-foreground placeholder:text-muted-foreground"
2527
+ )
2528
+ }
2529
+ ) }),
2530
+ groups && groups.length > 0 ? groups.map((g) => {
2531
+ const groupFiltered = searchable && search ? g.options.filter((o) => o.label.toLowerCase().includes(search.toLowerCase())) : g.options;
2532
+ if (groupFiltered.length === 0) return null;
2533
+ return /* @__PURE__ */ jsxs("div", { children: [
2534
+ /* @__PURE__ */ jsx("div", { className: "px-3 py-1 text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: g.label }),
2535
+ renderOptions(groupFiltered)
2536
+ ] }, g.label);
2537
+ }) : renderOptions(filtered)
2538
+ ] })
2539
+ ] });
2540
+ }
2343
2541
  var Select;
2344
2542
  var init_Select = __esm({
2345
2543
  "components/core/atoms/Select.tsx"() {
@@ -2347,47 +2545,12 @@ var init_Select = __esm({
2347
2545
  init_Icon();
2348
2546
  init_useEventBus();
2349
2547
  Select = React74__default.forwardRef(
2350
- ({ className, options, placeholder, error, onChange, ...props }, ref) => {
2351
- const eventBus = useEventBus();
2352
- const handleChange = (e) => {
2353
- if (typeof onChange === "string") {
2354
- eventBus.emit(`UI:${onChange}`, { value: e.target.value });
2355
- } else {
2356
- onChange?.(e);
2357
- }
2358
- };
2359
- return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
2360
- /* @__PURE__ */ jsxs(
2361
- "select",
2362
- {
2363
- ref,
2364
- onChange: handleChange,
2365
- className: cn(
2366
- "block w-full border-[length:var(--border-width)] shadow-sm appearance-none",
2367
- "px-3 py-2 pr-10 text-sm text-foreground font-medium",
2368
- "bg-card",
2369
- "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
2370
- "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
2371
- error ? "border-error focus:border-error" : "border-border focus:border-primary",
2372
- className
2373
- ),
2374
- ...props,
2375
- children: [
2376
- placeholder && /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: placeholder }),
2377
- options.map((option) => /* @__PURE__ */ jsx(
2378
- "option",
2379
- {
2380
- value: option.value,
2381
- disabled: option.disabled,
2382
- children: option.label
2383
- },
2384
- option.value
2385
- ))
2386
- ]
2387
- }
2388
- ),
2389
- /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default text-foreground" }) })
2390
- ] });
2548
+ (props, _ref) => {
2549
+ const { multiple, searchable, clearable } = props;
2550
+ if (multiple || searchable || clearable) {
2551
+ return /* @__PURE__ */ jsx(RichSelect, { ...props });
2552
+ }
2553
+ return /* @__PURE__ */ jsx(NativeSelect, { ...props });
2391
2554
  }
2392
2555
  );
2393
2556
  Select.displayName = "Select";
@@ -2441,11 +2604,54 @@ var init_Checkbox = __esm({
2441
2604
  Checkbox.displayName = "Checkbox";
2442
2605
  }
2443
2606
  });
2607
+ var sizeStyles2, Spinner;
2608
+ var init_Spinner = __esm({
2609
+ "components/core/atoms/Spinner.tsx"() {
2610
+ init_cn();
2611
+ init_Icon();
2612
+ sizeStyles2 = {
2613
+ xs: "h-3 w-3",
2614
+ sm: "h-icon-default w-icon-default",
2615
+ md: "h-6 w-6",
2616
+ lg: "h-8 w-8"
2617
+ };
2618
+ Spinner = React74__default.forwardRef(
2619
+ ({ className, size = "md", overlay, ...props }, ref) => {
2620
+ if (overlay) {
2621
+ return /* @__PURE__ */ jsx(
2622
+ "div",
2623
+ {
2624
+ ref,
2625
+ className: cn(
2626
+ "absolute inset-0 z-10 flex items-center justify-center",
2627
+ "bg-background/60 backdrop-blur-sm",
2628
+ className
2629
+ ),
2630
+ ...props,
2631
+ children: /* @__PURE__ */ jsx(Icon, { name: "loader", className: cn("animate-spin text-foreground", sizeStyles2[size]) })
2632
+ }
2633
+ );
2634
+ }
2635
+ return /* @__PURE__ */ jsx(
2636
+ "div",
2637
+ {
2638
+ ref,
2639
+ className: cn("text-foreground", className),
2640
+ ...props,
2641
+ children: /* @__PURE__ */ jsx(Icon, { name: "loader", className: cn("animate-spin", sizeStyles2[size]) })
2642
+ }
2643
+ );
2644
+ }
2645
+ );
2646
+ Spinner.displayName = "Spinner";
2647
+ }
2648
+ });
2444
2649
  var variantStyles2, paddingStyles, shadowStyles, lookStyles, Card, CardHeader, CardTitle, CardContent, CardBody, CardFooter;
2445
2650
  var init_Card = __esm({
2446
2651
  "components/core/atoms/Card.tsx"() {
2447
2652
  init_cn();
2448
2653
  init_useEventBus();
2654
+ init_Spinner();
2449
2655
  variantStyles2 = {
2450
2656
  default: [
2451
2657
  "bg-card",
@@ -2510,6 +2716,7 @@ var init_Card = __esm({
2510
2716
  look = "elevated",
2511
2717
  children,
2512
2718
  action,
2719
+ loading,
2513
2720
  onClick,
2514
2721
  ...props
2515
2722
  }, ref) => {
@@ -2523,7 +2730,7 @@ var init_Card = __esm({
2523
2730
  {
2524
2731
  ref,
2525
2732
  className: cn(
2526
- "rounded-container",
2733
+ "rounded-container relative",
2527
2734
  "transition-all duration-[var(--transition-normal)]",
2528
2735
  variantStyles2[variant],
2529
2736
  paddingStyles[padding],
@@ -2534,6 +2741,7 @@ var init_Card = __esm({
2534
2741
  onClick: handleClick,
2535
2742
  ...props,
2536
2743
  children: [
2744
+ loading && /* @__PURE__ */ jsx(Spinner, { overlay: true, size: "md" }),
2537
2745
  (title || subtitle) && /* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
2538
2746
  title && /* @__PURE__ */ jsx("h3", { className: "text-lg text-card-foreground font-bold", children: title }),
2539
2747
  subtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1", children: subtitle })
@@ -2575,7 +2783,7 @@ var init_Card = __esm({
2575
2783
  CardFooter.displayName = "CardFooter";
2576
2784
  }
2577
2785
  });
2578
- var variantStyles3, sizeStyles2, Badge;
2786
+ var variantStyles3, sizeStyles3, Badge;
2579
2787
  var init_Badge = __esm({
2580
2788
  "components/core/atoms/Badge.tsx"() {
2581
2789
  init_cn();
@@ -2612,7 +2820,7 @@ var init_Badge = __esm({
2612
2820
  "border-[length:var(--border-width-thin)] border-border"
2613
2821
  ].join(" ")
2614
2822
  };
2615
- sizeStyles2 = {
2823
+ sizeStyles3 = {
2616
2824
  sm: "px-2 py-0.5 text-xs",
2617
2825
  md: "px-2.5 py-1 text-sm",
2618
2826
  lg: "px-3 py-1.5 text-base"
@@ -2632,7 +2840,7 @@ var init_Badge = __esm({
2632
2840
  className: cn(
2633
2841
  "inline-flex items-center gap-1 font-bold rounded-sm",
2634
2842
  variantStyles3[variant],
2635
- sizeStyles2[size],
2843
+ sizeStyles3[size],
2636
2844
  onRemove && "pr-1",
2637
2845
  className
2638
2846
  ),
@@ -2666,7 +2874,7 @@ var init_Badge = __esm({
2666
2874
  Badge.displayName = "Badge";
2667
2875
  }
2668
2876
  });
2669
- var variantStyles4, sizeStyles3, iconSizes, FilterPill;
2877
+ var variantStyles4, sizeStyles4, iconSizes, FilterPill;
2670
2878
  var init_FilterPill = __esm({
2671
2879
  "components/core/atoms/FilterPill.tsx"() {
2672
2880
  init_cn();
@@ -2700,7 +2908,7 @@ var init_FilterPill = __esm({
2700
2908
  "border-[length:var(--border-width-thin)] border-border"
2701
2909
  ].join(" ")
2702
2910
  };
2703
- sizeStyles3 = {
2911
+ sizeStyles4 = {
2704
2912
  sm: "px-2 py-0.5 text-xs",
2705
2913
  md: "px-2.5 py-1 text-sm",
2706
2914
  lg: "px-3 py-1.5 text-base"
@@ -2744,7 +2952,7 @@ var init_FilterPill = __esm({
2744
2952
  className: cn(
2745
2953
  "inline-flex items-center gap-1 font-bold rounded-pill",
2746
2954
  variantStyles4[variant],
2747
- sizeStyles3[size],
2955
+ sizeStyles4[size],
2748
2956
  (onClick || clickEvent) && "cursor-pointer",
2749
2957
  className
2750
2958
  ),
@@ -2776,33 +2984,6 @@ var init_FilterPill = __esm({
2776
2984
  FilterPill.displayName = "FilterPill";
2777
2985
  }
2778
2986
  });
2779
- var sizeStyles4, Spinner;
2780
- var init_Spinner = __esm({
2781
- "components/core/atoms/Spinner.tsx"() {
2782
- init_cn();
2783
- init_Icon();
2784
- sizeStyles4 = {
2785
- xs: "h-3 w-3",
2786
- sm: "h-icon-default w-icon-default",
2787
- md: "h-6 w-6",
2788
- lg: "h-8 w-8"
2789
- };
2790
- Spinner = React74__default.forwardRef(
2791
- ({ className, size = "md", ...props }, ref) => {
2792
- return /* @__PURE__ */ jsx(
2793
- "div",
2794
- {
2795
- ref,
2796
- className: cn("text-foreground", className),
2797
- ...props,
2798
- children: /* @__PURE__ */ jsx(Icon, { name: "loader", className: cn("animate-spin", sizeStyles4[size]) })
2799
- }
2800
- );
2801
- }
2802
- );
2803
- Spinner.displayName = "Spinner";
2804
- }
2805
- });
2806
2987
  function generateInitials(name) {
2807
2988
  const parts = name.trim().split(/\s+/);
2808
2989
  if (parts.length === 1) {
@@ -7081,11 +7262,15 @@ var init_Skeleton = __esm({
7081
7262
  function getKnownPatterns() {
7082
7263
  return Object.keys(componentMapping);
7083
7264
  }
7084
- var componentMapping;
7265
+ function getPatternDefinition(type) {
7266
+ return patternRegistry[type];
7267
+ }
7268
+ var componentMapping, patternRegistry;
7085
7269
  var init_pattern_resolver = __esm({
7086
7270
  "renderer/pattern-resolver.ts"() {
7087
7271
  createLogger("almadar:ui:pattern-resolver");
7088
7272
  componentMapping = {};
7273
+ patternRegistry = {};
7089
7274
  }
7090
7275
  });
7091
7276
 
@@ -15020,30 +15205,30 @@ var init_BranchingLogicBuilder = __esm({
15020
15205
  if (!sourceQuestion?.optionValues) return [];
15021
15206
  return sourceQuestion.optionValues.map((v) => ({ value: v, label: v }));
15022
15207
  }, [sourceQuestion]);
15023
- const handleSource = (e) => {
15024
- onChange({ ...rule, sourceQuestionId: e.target.value });
15208
+ const handleSource = (v) => {
15209
+ onChange({ ...rule, sourceQuestionId: v });
15025
15210
  };
15026
- const handleOperator = (e) => {
15027
- const next = e.target.value;
15211
+ const handleOperator = (v) => {
15212
+ const next = v;
15028
15213
  const nextValue = next === "in" && !Array.isArray(rule.value) ? rule.value ? [rule.value] : [] : next !== "in" && Array.isArray(rule.value) ? rule.value[0] ?? "" : rule.value;
15029
15214
  onChange({ ...rule, operator: next, value: nextValue });
15030
15215
  };
15031
15216
  const handleScalarValue = (e) => {
15032
15217
  onChange({ ...rule, value: e.target.value });
15033
15218
  };
15034
- const handleAddChip = (e) => {
15035
- const v = e.target.value;
15036
- if (!v) return;
15219
+ const handleAddChip = (v) => {
15220
+ const val = v;
15221
+ if (!val) return;
15037
15222
  const current = Array.isArray(rule.value) ? rule.value : [];
15038
- if (current.includes(v)) return;
15039
- onChange({ ...rule, value: [...current, v] });
15223
+ if (current.includes(val)) return;
15224
+ onChange({ ...rule, value: [...current, val] });
15040
15225
  };
15041
15226
  const handleRemoveChip = (chip) => {
15042
15227
  const current = Array.isArray(rule.value) ? rule.value : [];
15043
15228
  onChange({ ...rule, value: current.filter((c) => c !== chip) });
15044
15229
  };
15045
- const handleTarget = (e) => {
15046
- onChange({ ...rule, targetQuestionId: e.target.value });
15230
+ const handleTarget = (v) => {
15231
+ onChange({ ...rule, targetQuestionId: v });
15047
15232
  };
15048
15233
  const isMulti = rule.operator === "in";
15049
15234
  const chips = Array.isArray(rule.value) ? rule.value : [];
@@ -15124,7 +15309,7 @@ var init_BranchingLogicBuilder = __esm({
15124
15309
  options: valueOptions,
15125
15310
  value: scalarValue,
15126
15311
  placeholder: t("branchingLogic.selectValue"),
15127
- onChange: (e) => onChange({ ...rule, value: e.target.value }),
15312
+ onChange: (v) => onChange({ ...rule, value: v }),
15128
15313
  disabled: readOnly
15129
15314
  }
15130
15315
  ) : /* @__PURE__ */ jsx(
@@ -17556,7 +17741,7 @@ var init_Pagination = __esm({
17556
17741
  Select,
17557
17742
  {
17558
17743
  value: String(pageSize),
17559
- onChange: (e) => handlePageSizeChange(Number(e.target.value)),
17744
+ onChange: (v) => handlePageSizeChange(Number(v)),
17560
17745
  options: pageSizeOptions.map((size) => ({
17561
17746
  value: String(size),
17562
17747
  label: String(size)
@@ -20710,7 +20895,84 @@ var init_DashboardLayout = __esm({
20710
20895
  NavLinkBottom.displayName = "NavLinkBottom";
20711
20896
  }
20712
20897
  });
20713
- var Menu;
20898
+ function computeMenuStyle(position, triggerRect) {
20899
+ const isTop = position.startsWith("top");
20900
+ const isRight = position.endsWith("right") || position.endsWith("end");
20901
+ if (isTop) {
20902
+ return {
20903
+ top: triggerRect.top - MENU_GAP,
20904
+ transform: "translateY(-100%)",
20905
+ ...isRight ? { right: window.innerWidth - triggerRect.right } : { left: triggerRect.left }
20906
+ };
20907
+ }
20908
+ return {
20909
+ top: triggerRect.bottom + MENU_GAP,
20910
+ ...isRight ? { right: window.innerWidth - triggerRect.right } : { left: triggerRect.left }
20911
+ };
20912
+ }
20913
+ function SubMenu({
20914
+ items,
20915
+ itemRef,
20916
+ direction,
20917
+ eventBus
20918
+ }) {
20919
+ const [rect, setRect] = useState(null);
20920
+ useEffect(() => {
20921
+ if (itemRef) {
20922
+ setRect(itemRef.getBoundingClientRect());
20923
+ }
20924
+ }, [itemRef]);
20925
+ if (!rect) return null;
20926
+ const isRtl = direction === "rtl";
20927
+ const style = {
20928
+ top: rect.top,
20929
+ ...isRtl ? { right: window.innerWidth - rect.left } : { left: rect.right }
20930
+ };
20931
+ const panel = /* @__PURE__ */ jsx(
20932
+ "div",
20933
+ {
20934
+ className: cn("fixed z-50", menuContainerStyles),
20935
+ style,
20936
+ children: items.map((item, index) => {
20937
+ const isDivider = item.id === "divider" || item.label === "divider";
20938
+ const itemId = item.id ?? `item-${item.label.toLowerCase().replace(/\s+/g, "-")}-${index}`;
20939
+ const isDanger = item.variant === "danger";
20940
+ if (isDivider) {
20941
+ return /* @__PURE__ */ jsx(Divider, { className: "my-1" }, `divider-${index}`);
20942
+ }
20943
+ return /* @__PURE__ */ jsxs(
20944
+ Box,
20945
+ {
20946
+ as: "button",
20947
+ onClick: () => {
20948
+ if (item.disabled) return;
20949
+ if (item.event) eventBus.emit(`UI:${item.event}`, { itemId, label: item.label });
20950
+ item.onClick?.();
20951
+ },
20952
+ "aria-disabled": item.disabled || void 0,
20953
+ "data-testid": item.event ? `action-${item.event}` : void 0,
20954
+ className: cn(
20955
+ "w-full flex items-center gap-3 px-4 py-2 text-start",
20956
+ "text-sm transition-colors",
20957
+ "hover:bg-muted focus:outline-none focus:bg-muted",
20958
+ "disabled:opacity-50 disabled:cursor-not-allowed",
20959
+ item.disabled && "cursor-not-allowed",
20960
+ isDanger && "text-error hover:bg-error/10"
20961
+ ),
20962
+ children: [
20963
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: item.icon, size: "sm", className: "flex-shrink-0" }) : /* @__PURE__ */ jsx(Icon, { icon: item.icon, size: "sm", className: "flex-shrink-0" })),
20964
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: cn("flex-1", isDanger && "text-red-600"), children: item.label }),
20965
+ item.badge !== void 0 && /* @__PURE__ */ jsx("span", { className: "ml-auto text-xs font-medium", children: item.badge })
20966
+ ]
20967
+ },
20968
+ itemId
20969
+ );
20970
+ })
20971
+ }
20972
+ );
20973
+ return typeof document !== "undefined" ? createPortal(panel, document.body) : panel;
20974
+ }
20975
+ var MENU_GAP, menuContainerStyles, Menu;
20714
20976
  var init_Menu = __esm({
20715
20977
  "components/core/molecules/Menu.tsx"() {
20716
20978
  "use client";
@@ -20721,16 +20983,27 @@ var init_Menu = __esm({
20721
20983
  init_Badge();
20722
20984
  init_cn();
20723
20985
  init_useEventBus();
20986
+ MENU_GAP = 4;
20987
+ menuContainerStyles = cn(
20988
+ "bg-card",
20989
+ "border-[length:var(--border-width)] border-border",
20990
+ "shadow-elevation-popover",
20991
+ "rounded-sm",
20992
+ "min-w-0 sm:min-w-[200px] max-w-[calc(100vw-1rem)] py-1"
20993
+ );
20724
20994
  Menu = ({
20725
20995
  trigger,
20726
20996
  items,
20727
20997
  position = "bottom-left",
20728
- className
20998
+ className,
20999
+ header,
21000
+ footer
20729
21001
  }) => {
20730
21002
  const eventBus = useEventBus();
20731
- const { t, direction } = useTranslate();
21003
+ const { direction } = useTranslate();
20732
21004
  const [isOpen, setIsOpen] = useState(false);
20733
21005
  const [activeSubMenu, setActiveSubMenu] = useState(null);
21006
+ const [activeSubMenuRef, setActiveSubMenuRef] = useState(null);
20734
21007
  const [triggerRect, setTriggerRect] = useState(null);
20735
21008
  const triggerRef = useRef(null);
20736
21009
  const menuRef = useRef(null);
@@ -20745,13 +21018,14 @@ var init_Menu = __esm({
20745
21018
  }
20746
21019
  setIsOpen(!isOpen);
20747
21020
  setActiveSubMenu(null);
21021
+ setActiveSubMenuRef(null);
20748
21022
  };
20749
- const handleItemClick = (item) => {
21023
+ const handleItemClick = (item, itemId) => {
20750
21024
  if (item.disabled) return;
20751
21025
  if (item.subMenu && item.subMenu.length > 0) {
20752
- setActiveSubMenu(item.id ?? null);
21026
+ setActiveSubMenu(itemId);
20753
21027
  } else {
20754
- if (item.event) eventBus.emit(`UI:${item.event}`, { itemId: item.id, label: item.label });
21028
+ if (item.event) eventBus.emit(`UI:${item.event}`, { itemId, label: item.label });
20755
21029
  item.onClick?.();
20756
21030
  setIsOpen(false);
20757
21031
  }
@@ -20766,22 +21040,12 @@ var init_Menu = __esm({
20766
21040
  if (isOpen && menuRef.current && !menuRef.current.contains(e.target) && triggerRef.current && !triggerRef.current.contains(e.target)) {
20767
21041
  setIsOpen(false);
20768
21042
  setActiveSubMenu(null);
21043
+ setActiveSubMenuRef(null);
20769
21044
  }
20770
21045
  };
20771
21046
  document.addEventListener("mousedown", handleClickOutside);
20772
21047
  return () => document.removeEventListener("mousedown", handleClickOutside);
20773
21048
  }, [isOpen]);
20774
- const positionClasses = {
20775
- "top-left": "bottom-full left-0 mb-2",
20776
- "top-right": "bottom-full right-0 mb-2",
20777
- "bottom-left": "top-full left-0 mt-2",
20778
- "bottom-right": "top-full right-0 mt-2",
20779
- // Aliases for pattern compatibility
20780
- "top-start": "bottom-full left-0 mb-2",
20781
- "top-end": "bottom-full right-0 mb-2",
20782
- "bottom-start": "top-full left-0 mt-2",
20783
- "bottom-end": "top-full right-0 mt-2"
20784
- };
20785
21049
  const rtlMirror = {
20786
21050
  "top-left": "top-right",
20787
21051
  "top-right": "top-left",
@@ -20793,7 +21057,6 @@ var init_Menu = __esm({
20793
21057
  "bottom-end": "bottom-start"
20794
21058
  };
20795
21059
  const effectivePosition = direction === "rtl" ? rtlMirror[position] ?? position : position;
20796
- const subMenuSideClass = direction === "rtl" ? "right-full mr-2" : "left-full ml-2";
20797
21060
  const triggerChild = React74__default.isValidElement(trigger) ? trigger : /* @__PURE__ */ jsx(Typography, { variant: "small", as: "span", children: trigger });
20798
21061
  const triggerElement = React74__default.cloneElement(
20799
21062
  triggerChild,
@@ -20802,94 +21065,87 @@ var init_Menu = __esm({
20802
21065
  onClick: handleToggle
20803
21066
  }
20804
21067
  );
20805
- const menuContainerStyles = cn(
20806
- "bg-card",
20807
- "border-[length:var(--border-width)] border-border",
20808
- "shadow-elevation-popover",
20809
- "rounded-sm",
20810
- "min-w-0 sm:min-w-[200px] max-w-[calc(100vw-1rem)] py-1"
20811
- );
20812
- const renderMenuItem = (item, hasSubMenu, index) => {
21068
+ const renderMenuItems = (menuItems) => menuItems.map((item, index) => {
21069
+ const isDivider = item.id === "divider" || item.label === "divider";
20813
21070
  const itemId = item.id ?? `item-${item.label.toLowerCase().replace(/\s+/g, "-")}-${index}`;
21071
+ const hasSubMenu = !!(item.subMenu && item.subMenu.length > 0);
20814
21072
  const isDanger = item.variant === "danger";
20815
- return /* @__PURE__ */ jsx(
20816
- Box,
20817
- {
20818
- as: "button",
20819
- onClick: () => !item.disabled && handleItemClick({ ...item, id: itemId }),
20820
- "aria-disabled": item.disabled || void 0,
20821
- onMouseEnter: () => hasSubMenu && setActiveSubMenu(itemId),
20822
- "data-testid": item.event ? `action-${item.event}` : void 0,
20823
- className: cn(
20824
- "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
20825
- "text-sm transition-colors",
20826
- "hover:bg-muted",
20827
- "focus:outline-none focus:bg-muted",
20828
- "disabled:opacity-50 disabled:cursor-not-allowed",
20829
- item.disabled && "cursor-not-allowed",
20830
- isDanger && "text-error hover:bg-error/10"
20831
- ),
20832
- children: /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
20833
- item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: item.icon, size: "sm", className: "flex-shrink-0" }) : /* @__PURE__ */ jsx(Icon, { icon: item.icon, size: "sm", className: "flex-shrink-0" })),
20834
- /* @__PURE__ */ jsx(
20835
- Typography,
20836
- {
20837
- variant: "small",
20838
- className: cn("flex-1", isDanger && "text-red-600"),
20839
- children: item.label
21073
+ if (isDivider) {
21074
+ return /* @__PURE__ */ jsx(Divider, { className: "my-1" }, `divider-${index}`);
21075
+ }
21076
+ return /* @__PURE__ */ jsxs(Box, { children: [
21077
+ /* @__PURE__ */ jsx(
21078
+ Box,
21079
+ {
21080
+ as: "button",
21081
+ onClick: () => handleItemClick({ ...item, id: itemId }, itemId),
21082
+ "aria-disabled": item.disabled || void 0,
21083
+ onMouseEnter: (e) => {
21084
+ if (hasSubMenu) {
21085
+ setActiveSubMenu(itemId);
21086
+ setActiveSubMenuRef(e.currentTarget);
20840
21087
  }
21088
+ },
21089
+ "data-testid": item.event ? `action-${item.event}` : void 0,
21090
+ className: cn(
21091
+ "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
21092
+ "text-sm transition-colors",
21093
+ "hover:bg-muted",
21094
+ "focus:outline-none focus:bg-muted",
21095
+ "disabled:opacity-50 disabled:cursor-not-allowed",
21096
+ item.disabled && "cursor-not-allowed",
21097
+ isDanger && "text-error hover:bg-error/10"
20841
21098
  ),
20842
- item.badge !== void 0 && /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
20843
- hasSubMenu && /* @__PURE__ */ jsx(Icon, { name: direction === "rtl" ? "chevron-left" : "chevron-right", size: "sm", className: "flex-shrink-0" })
20844
- ] })
20845
- },
20846
- itemId
20847
- );
20848
- };
20849
- const renderMenuItems = (menuItems) => {
20850
- return menuItems.map((item, index) => {
20851
- const hasSubMenu = item.subMenu && item.subMenu.length > 0;
20852
- const isDivider = item.id === "divider" || item.label === "divider";
20853
- const itemId = item.id ?? `item-${item.label.toLowerCase().replace(/\s+/g, "-")}-${index}`;
20854
- if (isDivider) {
20855
- return /* @__PURE__ */ jsx(Divider, { className: "my-1" }, `divider-${index}`);
20856
- }
20857
- return /* @__PURE__ */ jsxs(Box, { children: [
20858
- renderMenuItem(item, !!hasSubMenu, index),
20859
- hasSubMenu && activeSubMenu === itemId && item.subMenu && /* @__PURE__ */ jsx(
20860
- Box,
20861
- {
20862
- className: cn(
20863
- "absolute top-0 z-50",
20864
- subMenuSideClass,
20865
- menuContainerStyles
21099
+ children: /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
21100
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: item.icon, size: "sm", className: "flex-shrink-0" }) : /* @__PURE__ */ jsx(Icon, { icon: item.icon, size: "sm", className: "flex-shrink-0" })),
21101
+ /* @__PURE__ */ jsx(
21102
+ Typography,
21103
+ {
21104
+ variant: "small",
21105
+ className: cn("flex-1", isDanger && "text-red-600"),
21106
+ children: item.label
21107
+ }
20866
21108
  ),
20867
- children: renderMenuItems(item.subMenu)
20868
- }
20869
- )
20870
- ] }, itemId);
20871
- });
20872
- };
20873
- return /* @__PURE__ */ jsxs(Box, { className: "relative", children: [
21109
+ item.badge !== void 0 && /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
21110
+ hasSubMenu && /* @__PURE__ */ jsx(
21111
+ Icon,
21112
+ {
21113
+ name: direction === "rtl" ? "chevron-left" : "chevron-right",
21114
+ size: "sm",
21115
+ className: "flex-shrink-0"
21116
+ }
21117
+ )
21118
+ ] })
21119
+ }
21120
+ ),
21121
+ hasSubMenu && activeSubMenu === itemId && item.subMenu && /* @__PURE__ */ jsx(
21122
+ SubMenu,
21123
+ {
21124
+ items: item.subMenu,
21125
+ itemRef: activeSubMenuRef,
21126
+ direction,
21127
+ eventBus
21128
+ }
21129
+ )
21130
+ ] }, itemId);
21131
+ });
21132
+ const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxs(
21133
+ "div",
21134
+ {
21135
+ ref: menuRef,
21136
+ className: cn("fixed z-50", menuContainerStyles, className),
21137
+ style: computeMenuStyle(effectivePosition, triggerRect),
21138
+ role: "menu",
21139
+ children: [
21140
+ header && /* @__PURE__ */ jsx("div", { className: "px-4 py-2 border-b border-border", children: header }),
21141
+ renderMenuItems(items),
21142
+ footer && /* @__PURE__ */ jsx("div", { className: "px-4 py-2 border-t border-border", children: footer })
21143
+ ]
21144
+ }
21145
+ ) : null;
21146
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
20874
21147
  triggerElement,
20875
- isOpen && triggerRect && /* @__PURE__ */ jsx(
20876
- Box,
20877
- {
20878
- ref: menuRef,
20879
- className: cn(
20880
- "absolute z-50",
20881
- menuContainerStyles,
20882
- positionClasses[effectivePosition],
20883
- className
20884
- ),
20885
- style: {
20886
- left: effectivePosition.includes("left") ? 0 : "auto",
20887
- right: effectivePosition.includes("right") ? 0 : "auto"
20888
- },
20889
- role: "menu",
20890
- children: renderMenuItems(items)
20891
- }
20892
- )
21148
+ panel && typeof document !== "undefined" ? createPortal(panel, document.body) : panel
20893
21149
  ] });
20894
21150
  };
20895
21151
  Menu.displayName = "Menu";
@@ -22299,11 +22555,15 @@ function KindSelect({
22299
22555
  function ValueNode({
22300
22556
  value,
22301
22557
  onChange,
22302
- depth
22558
+ depth,
22559
+ readonly
22303
22560
  }) {
22304
22561
  const kind = kindOf(value);
22305
22562
  if (kind === "object" || kind === "array") {
22306
- return /* @__PURE__ */ jsx(ContainerNode, { value, onChange, depth });
22563
+ return /* @__PURE__ */ jsx(ContainerNode, { value, onChange, depth, readonly });
22564
+ }
22565
+ if (readonly) {
22566
+ return /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "font-mono text-foreground", children: value === null ? "null" : String(value) });
22307
22567
  }
22308
22568
  return /* @__PURE__ */ jsx(ScalarControl, { value, onChange });
22309
22569
  }
@@ -22315,15 +22575,19 @@ function Row({
22315
22575
  onValue,
22316
22576
  onRenameKey,
22317
22577
  onChangeKind,
22318
- onRemove
22578
+ onRemove,
22579
+ readonly
22319
22580
  }) {
22320
22581
  const [keyDraft, setKeyDraft] = React74__default.useState(rowKey);
22321
22582
  React74__default.useEffect(() => setKeyDraft(rowKey), [rowKey]);
22322
22583
  const container = isObj(value) || isArr(value);
22323
22584
  return /* @__PURE__ */ jsxs(VStack, { gap: "none", className: "group w-max min-w-full", children: [
22324
22585
  /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", className: "py-0.5 w-max", children: [
22325
- /* @__PURE__ */ jsx(KindSelect, { kind: kindOf(value), onChange: onChangeKind }),
22326
- isArrayItem ? /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", className: "w-6 shrink-0 font-mono", children: rowKey }) : /* @__PURE__ */ jsx("div", { className: "w-20 shrink-0", children: /* @__PURE__ */ jsx(
22586
+ readonly ? /* @__PURE__ */ jsx("span", { className: cn(
22587
+ "h-6 rounded-sm bg-muted text-muted-foreground text-[10px] font-mono px-1 flex items-center",
22588
+ "border-[length:var(--border-width-thin)] border-border"
22589
+ ), children: TYPE_LABEL[kindOf(value)] }) : /* @__PURE__ */ jsx(KindSelect, { kind: kindOf(value), onChange: onChangeKind }),
22590
+ isArrayItem ? /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", className: "w-6 shrink-0 font-mono", children: rowKey }) : readonly ? /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", className: "w-20 shrink-0 font-mono truncate", children: rowKey }) : /* @__PURE__ */ jsx("div", { className: "w-20 shrink-0", children: /* @__PURE__ */ jsx(
22327
22591
  Input,
22328
22592
  {
22329
22593
  inputType: "text",
@@ -22336,8 +22600,8 @@ function Row({
22336
22600
  className: "font-mono"
22337
22601
  }
22338
22602
  ) }),
22339
- !container && /* @__PURE__ */ jsx("div", { className: "w-48 shrink-0", children: /* @__PURE__ */ jsx(ValueNode, { value, onChange: onValue, depth: depth + 1 }) }),
22340
- /* @__PURE__ */ jsx(
22603
+ !container && /* @__PURE__ */ jsx("div", { className: "w-48 shrink-0", children: /* @__PURE__ */ jsx(ValueNode, { value, onChange: onValue, depth: depth + 1, readonly }) }),
22604
+ !readonly && /* @__PURE__ */ jsx(
22341
22605
  Button,
22342
22606
  {
22343
22607
  variant: "ghost",
@@ -22349,13 +22613,14 @@ function Row({
22349
22613
  }
22350
22614
  )
22351
22615
  ] }),
22352
- container && /* @__PURE__ */ jsx("div", { className: "pl-2", children: /* @__PURE__ */ jsx(ValueNode, { value, onChange: onValue, depth: depth + 1 }) })
22616
+ container && /* @__PURE__ */ jsx("div", { className: "pl-2", children: /* @__PURE__ */ jsx(ValueNode, { value, onChange: onValue, depth: depth + 1, readonly }) })
22353
22617
  ] });
22354
22618
  }
22355
22619
  function ContainerNode({
22356
22620
  value,
22357
22621
  onChange,
22358
- depth
22622
+ depth,
22623
+ readonly
22359
22624
  }) {
22360
22625
  const [open, setOpen] = React74__default.useState(depth < 2);
22361
22626
  const array = isArr(value);
@@ -22428,11 +22693,12 @@ function ContainerNode({
22428
22693
  onValue: (next) => array ? setArrValue(idx, next) : setObjValue(k, next),
22429
22694
  onRenameKey: (nk) => renameKey(k, nk),
22430
22695
  onChangeKind: (kind) => array ? changeArrKind(idx, kind) : changeObjKind(k, kind),
22431
- onRemove: () => array ? removeArrIdx(idx) : removeObjKey(k)
22696
+ onRemove: () => array ? removeArrIdx(idx) : removeObjKey(k),
22697
+ readonly
22432
22698
  },
22433
22699
  array ? idx : k
22434
22700
  )),
22435
- /* @__PURE__ */ jsx(
22701
+ !readonly && /* @__PURE__ */ jsx(
22436
22702
  Button,
22437
22703
  {
22438
22704
  variant: "ghost",
@@ -22470,9 +22736,586 @@ var init_JsonTreeEditor = __esm({
22470
22736
  null: "\u2014"
22471
22737
  };
22472
22738
  KIND_OPTIONS = ["string", "number", "boolean", "object", "array"];
22473
- JsonTreeEditor = ({ value, onChange, className }) => {
22739
+ JsonTreeEditor = ({ value, onChange, className, readonly }) => {
22474
22740
  const root = value ?? "";
22475
- return /* @__PURE__ */ 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__ */ jsx(ValueNode, { value: root, onChange, depth: 0 }) });
22741
+ return /* @__PURE__ */ 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__ */ jsx(ValueNode, { value: root, onChange, depth: 0, readonly }) });
22742
+ };
22743
+ }
22744
+ });
22745
+ var FormSection, FormLayout, FormActions;
22746
+ var init_FormSection = __esm({
22747
+ "components/core/molecules/FormSection.tsx"() {
22748
+ "use client";
22749
+ init_cn();
22750
+ init_atoms2();
22751
+ init_Box();
22752
+ init_Typography();
22753
+ init_Button();
22754
+ init_Stack();
22755
+ init_Icon();
22756
+ init_useEventBus();
22757
+ FormSection = ({
22758
+ title,
22759
+ description,
22760
+ children,
22761
+ collapsible = false,
22762
+ defaultCollapsed = false,
22763
+ card = false,
22764
+ columns = 1,
22765
+ className
22766
+ }) => {
22767
+ const [collapsed, setCollapsed] = React74__default.useState(defaultCollapsed);
22768
+ const { t } = useTranslate();
22769
+ const eventBus = useEventBus();
22770
+ const gridClass = {
22771
+ 1: "grid-cols-1",
22772
+ 2: "grid-cols-1 md:grid-cols-2",
22773
+ 3: "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
22774
+ }[columns];
22775
+ React74__default.useCallback(() => {
22776
+ if (collapsible) {
22777
+ setCollapsed((prev) => !prev);
22778
+ eventBus.emit("UI:TOGGLE_COLLAPSE", { collapsed: !collapsed });
22779
+ }
22780
+ }, [collapsible, collapsed, eventBus]);
22781
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
22782
+ (title || description) && /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "mb-4", children: [
22783
+ title && /* @__PURE__ */ jsxs(
22784
+ HStack,
22785
+ {
22786
+ justify: "between",
22787
+ align: "center",
22788
+ className: cn(collapsible && "cursor-pointer"),
22789
+ action: collapsible ? "TOGGLE_COLLAPSE" : void 0,
22790
+ children: [
22791
+ /* @__PURE__ */ jsx(Typography, { variant: "h3", weight: "semibold", children: title }),
22792
+ collapsible && /* @__PURE__ */ jsx(
22793
+ Button,
22794
+ {
22795
+ variant: "ghost",
22796
+ size: "sm",
22797
+ action: "TOGGLE_COLLAPSE",
22798
+ children: /* @__PURE__ */ jsx(
22799
+ Icon,
22800
+ {
22801
+ icon: ChevronDown,
22802
+ size: "sm",
22803
+ className: cn(
22804
+ "text-muted-foreground transition-transform",
22805
+ collapsed && "rotate-180"
22806
+ )
22807
+ }
22808
+ )
22809
+ }
22810
+ )
22811
+ ]
22812
+ }
22813
+ ),
22814
+ description && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: description })
22815
+ ] }),
22816
+ (!collapsible || !collapsed) && /* @__PURE__ */ jsx(Box, { className: cn("grid gap-4", gridClass), children })
22817
+ ] });
22818
+ if (card) {
22819
+ return /* @__PURE__ */ jsx(Card, { className: cn("p-6", className), children: content });
22820
+ }
22821
+ return /* @__PURE__ */ jsx(Box, { className, children: content });
22822
+ };
22823
+ FormSection.displayName = "FormSection";
22824
+ FormLayout = ({
22825
+ children,
22826
+ dividers = true,
22827
+ className
22828
+ }) => {
22829
+ return /* @__PURE__ */ jsx(
22830
+ VStack,
22831
+ {
22832
+ gap: "lg",
22833
+ className: cn(
22834
+ dividers && "[&>*+*]:pt-8 [&>*+*]:border-t [&>*+*]:border-border",
22835
+ className
22836
+ ),
22837
+ children
22838
+ }
22839
+ );
22840
+ };
22841
+ FormLayout.displayName = "FormLayout";
22842
+ FormActions = ({
22843
+ children,
22844
+ sticky = false,
22845
+ align = "right",
22846
+ className
22847
+ }) => {
22848
+ const alignClass2 = {
22849
+ left: "justify-start",
22850
+ right: "justify-end",
22851
+ between: "justify-between",
22852
+ center: "justify-center"
22853
+ }[align];
22854
+ return /* @__PURE__ */ jsx(
22855
+ HStack,
22856
+ {
22857
+ gap: "sm",
22858
+ align: "center",
22859
+ className: cn(
22860
+ "pt-6 border-t border-border",
22861
+ alignClass2,
22862
+ sticky && "sticky bottom-0 bg-card py-4 -mx-6 px-6 shadow-[0_-4px_6px_-1px_rgb(0,0,0,0.05)]",
22863
+ className
22864
+ ),
22865
+ children
22866
+ }
22867
+ );
22868
+ };
22869
+ FormActions.displayName = "FormActions";
22870
+ }
22871
+ });
22872
+ var ALL_CATEGORY, GridPicker;
22873
+ var init_GridPicker = __esm({
22874
+ "components/core/molecules/GridPicker.tsx"() {
22875
+ "use client";
22876
+ init_cn();
22877
+ init_Input();
22878
+ init_Badge();
22879
+ init_Stack();
22880
+ ALL_CATEGORY = "__all__";
22881
+ GridPicker = ({
22882
+ items,
22883
+ value,
22884
+ onChange,
22885
+ categories,
22886
+ searchPlaceholder,
22887
+ renderThumbnail,
22888
+ cellSize = 32,
22889
+ className
22890
+ }) => {
22891
+ const [search, setSearch] = useState("");
22892
+ const [activeCategory, setActiveCategory] = useState(ALL_CATEGORY);
22893
+ const gridRef = useRef(null);
22894
+ const categoryChips = useMemo(() => {
22895
+ if (categories !== void 0) return categories;
22896
+ const seen = [];
22897
+ for (const item of items) {
22898
+ if (!seen.includes(item.category)) seen.push(item.category);
22899
+ }
22900
+ return seen;
22901
+ }, [categories, items]);
22902
+ const filtered = useMemo(() => {
22903
+ const needle = search.trim().toLowerCase();
22904
+ return items.filter((item) => {
22905
+ const matchesCategory = activeCategory === ALL_CATEGORY || item.category === activeCategory;
22906
+ const matchesSearch = needle === "" || item.label.toLowerCase().includes(needle);
22907
+ return matchesCategory && matchesSearch;
22908
+ });
22909
+ }, [items, search, activeCategory]);
22910
+ const select = useCallback(
22911
+ (item) => {
22912
+ onChange(item.id);
22913
+ },
22914
+ [onChange]
22915
+ );
22916
+ const handleKeyDown = useCallback(
22917
+ (e, index) => {
22918
+ const cells = gridRef.current?.querySelectorAll(
22919
+ "[data-gridpicker-cell]"
22920
+ );
22921
+ if (cells === void 0 || cells.length === 0) return;
22922
+ const columns = (() => {
22923
+ const grid = gridRef.current;
22924
+ if (grid === null) return 1;
22925
+ const style = window.getComputedStyle(grid);
22926
+ const cols = style.gridTemplateColumns.split(" ").filter(Boolean).length;
22927
+ return cols > 0 ? cols : 1;
22928
+ })();
22929
+ let next = -1;
22930
+ if (e.key === "ArrowRight") next = index + 1;
22931
+ else if (e.key === "ArrowLeft") next = index - 1;
22932
+ else if (e.key === "ArrowDown") next = index + columns;
22933
+ else if (e.key === "ArrowUp") next = index - columns;
22934
+ else if (e.key === "Enter" || e.key === " ") {
22935
+ e.preventDefault();
22936
+ select(filtered[index]);
22937
+ return;
22938
+ } else {
22939
+ return;
22940
+ }
22941
+ e.preventDefault();
22942
+ if (next >= 0 && next < cells.length) {
22943
+ cells[next].focus();
22944
+ }
22945
+ },
22946
+ [filtered, select]
22947
+ );
22948
+ return /* @__PURE__ */ jsxs(VStack, { gap: "sm", className: cn("w-full", className), children: [
22949
+ /* @__PURE__ */ jsx(
22950
+ Input,
22951
+ {
22952
+ type: "search",
22953
+ icon: "search",
22954
+ value: search,
22955
+ placeholder: searchPlaceholder,
22956
+ clearable: true,
22957
+ onClear: () => setSearch(""),
22958
+ onChange: (e) => setSearch(e.target.value)
22959
+ }
22960
+ ),
22961
+ categoryChips.length > 0 && /* @__PURE__ */ jsxs(HStack, { gap: "xs", wrap: true, children: [
22962
+ /* @__PURE__ */ jsx(
22963
+ Badge,
22964
+ {
22965
+ variant: activeCategory === ALL_CATEGORY ? "primary" : "neutral",
22966
+ size: "sm",
22967
+ role: "button",
22968
+ tabIndex: 0,
22969
+ "aria-pressed": activeCategory === ALL_CATEGORY,
22970
+ className: "cursor-pointer",
22971
+ onClick: () => setActiveCategory(ALL_CATEGORY),
22972
+ onKeyDown: (e) => {
22973
+ if (e.key === "Enter" || e.key === " ") {
22974
+ e.preventDefault();
22975
+ setActiveCategory(ALL_CATEGORY);
22976
+ }
22977
+ },
22978
+ children: "All"
22979
+ }
22980
+ ),
22981
+ categoryChips.map((category) => /* @__PURE__ */ jsx(
22982
+ Badge,
22983
+ {
22984
+ variant: activeCategory === category ? "primary" : "neutral",
22985
+ size: "sm",
22986
+ role: "button",
22987
+ tabIndex: 0,
22988
+ "aria-pressed": activeCategory === category,
22989
+ className: "cursor-pointer",
22990
+ onClick: () => setActiveCategory(category),
22991
+ onKeyDown: (e) => {
22992
+ if (e.key === "Enter" || e.key === " ") {
22993
+ e.preventDefault();
22994
+ setActiveCategory(category);
22995
+ }
22996
+ },
22997
+ children: category
22998
+ },
22999
+ category
23000
+ ))
23001
+ ] }),
23002
+ /* @__PURE__ */ jsx(
23003
+ "div",
23004
+ {
23005
+ ref: gridRef,
23006
+ role: "listbox",
23007
+ className: "grid gap-1 overflow-y-auto max-h-64 p-1",
23008
+ style: {
23009
+ gridTemplateColumns: `repeat(auto-fill, minmax(${cellSize}px, 1fr))`
23010
+ },
23011
+ children: filtered.map((item, index) => {
23012
+ const selected = item.id === value;
23013
+ return /* @__PURE__ */ jsx(
23014
+ "button",
23015
+ {
23016
+ type: "button",
23017
+ role: "option",
23018
+ "aria-selected": selected,
23019
+ "aria-label": item.label,
23020
+ title: item.label,
23021
+ "data-gridpicker-cell": true,
23022
+ tabIndex: selected || value === void 0 && index === 0 ? 0 : -1,
23023
+ onClick: () => select(item),
23024
+ onKeyDown: (e) => handleKeyDown(e, index),
23025
+ className: cn(
23026
+ "flex items-center justify-center rounded-sm",
23027
+ "transition-colors hover:bg-muted",
23028
+ "focus:outline-none focus:ring-1 focus:ring-ring",
23029
+ selected && "bg-primary/10 ring-1 ring-primary"
23030
+ ),
23031
+ style: { width: cellSize, height: cellSize },
23032
+ children: renderThumbnail(item)
23033
+ },
23034
+ item.id
23035
+ );
23036
+ })
23037
+ }
23038
+ )
23039
+ ] });
23040
+ };
23041
+ GridPicker.displayName = "GridPicker";
23042
+ }
23043
+ });
23044
+ function pascalToKebab(name) {
23045
+ return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z])([A-Z][a-z])/g, "$1-$2").toLowerCase();
23046
+ }
23047
+ function kebabToPascal3(name) {
23048
+ return name.split("-").map((part) => /^\d+$/.test(part) ? part : part.charAt(0).toUpperCase() + part.slice(1)).join("");
23049
+ }
23050
+ var ICON_ITEMS, IconPicker;
23051
+ var init_IconPicker = __esm({
23052
+ "components/core/molecules/IconPicker.tsx"() {
23053
+ "use client";
23054
+ init_Icon();
23055
+ init_GridPicker();
23056
+ ICON_ITEMS = (() => {
23057
+ const items = [];
23058
+ for (const [exportName, candidate] of Object.entries(LucideIcons2)) {
23059
+ if (!/^[A-Z]/.test(exportName)) continue;
23060
+ if (exportName.endsWith("Icon")) continue;
23061
+ if (exportName.startsWith("Lucide")) continue;
23062
+ const isComponent = candidate !== null && (typeof candidate === "object" || typeof candidate === "function") && "$$typeof" in candidate;
23063
+ if (!isComponent) continue;
23064
+ const kebab = pascalToKebab(exportName);
23065
+ if (kebabToPascal3(kebab) !== exportName) continue;
23066
+ items.push({ id: kebab, label: kebab, category: "icons" });
23067
+ }
23068
+ return items;
23069
+ })();
23070
+ IconPicker = ({ value, onChange, className }) => {
23071
+ const items = useMemo(() => ICON_ITEMS, []);
23072
+ return /* @__PURE__ */ jsx(
23073
+ GridPicker,
23074
+ {
23075
+ items,
23076
+ value,
23077
+ onChange,
23078
+ searchPlaceholder: "Search icons\u2026",
23079
+ renderThumbnail: (it) => /* @__PURE__ */ jsx(Icon, { name: it.id }),
23080
+ cellSize: 32,
23081
+ className
23082
+ }
23083
+ );
23084
+ };
23085
+ IconPicker.displayName = "IconPicker";
23086
+ }
23087
+ });
23088
+ function iconForKind(kind) {
23089
+ if (kind === "audio") return "music";
23090
+ if (kind === "model") return "box";
23091
+ return "file";
23092
+ }
23093
+ var THUMB_PX, IMAGE_KINDS, AssetPicker;
23094
+ var init_AssetPicker = __esm({
23095
+ "components/core/molecules/AssetPicker.tsx"() {
23096
+ "use client";
23097
+ init_GridPicker();
23098
+ init_Icon();
23099
+ THUMB_PX = 32;
23100
+ IMAGE_KINDS = /* @__PURE__ */ new Set([
23101
+ "image",
23102
+ "spritesheet",
23103
+ "scene",
23104
+ "portrait"
23105
+ ]);
23106
+ AssetPicker = ({
23107
+ assets,
23108
+ value,
23109
+ onChange,
23110
+ className
23111
+ }) => {
23112
+ const byUrl = useMemo(() => {
23113
+ const map = /* @__PURE__ */ new Map();
23114
+ for (const entry of assets) map.set(entry.url, entry);
23115
+ return map;
23116
+ }, [assets]);
23117
+ const items = useMemo(
23118
+ () => assets.map((entry) => ({
23119
+ id: entry.url,
23120
+ label: entry.name,
23121
+ category: entry.category
23122
+ })),
23123
+ [assets]
23124
+ );
23125
+ const categories = useMemo(() => {
23126
+ const seen = [];
23127
+ for (const entry of assets) {
23128
+ if (!seen.includes(entry.category)) seen.push(entry.category);
23129
+ }
23130
+ return seen;
23131
+ }, [assets]);
23132
+ const renderThumbnail = useCallback(
23133
+ (item) => {
23134
+ const entry = byUrl.get(item.id);
23135
+ if (entry === void 0) return null;
23136
+ if (IMAGE_KINDS.has(entry.kind)) {
23137
+ return /* @__PURE__ */ jsx(
23138
+ "img",
23139
+ {
23140
+ src: entry.thumbnailUrl ?? entry.url,
23141
+ alt: entry.name,
23142
+ loading: "lazy",
23143
+ width: THUMB_PX,
23144
+ height: THUMB_PX,
23145
+ style: { width: THUMB_PX, height: THUMB_PX, objectFit: "cover" }
23146
+ }
23147
+ );
23148
+ }
23149
+ return /* @__PURE__ */ jsx(Icon, { name: iconForKind(entry.kind), size: "sm" });
23150
+ },
23151
+ [byUrl]
23152
+ );
23153
+ return /* @__PURE__ */ jsx(
23154
+ GridPicker,
23155
+ {
23156
+ items,
23157
+ value,
23158
+ onChange,
23159
+ categories,
23160
+ renderThumbnail,
23161
+ cellSize: THUMB_PX,
23162
+ className
23163
+ }
23164
+ );
23165
+ };
23166
+ AssetPicker.displayName = "AssetPicker";
23167
+ }
23168
+ });
23169
+ function currentValue(decl, override) {
23170
+ return override !== void 0 ? override : decl.default;
23171
+ }
23172
+ function TextLikeControl({
23173
+ field,
23174
+ numeric,
23175
+ value,
23176
+ onCommit
23177
+ }) {
23178
+ const initial = value === void 0 || value === null ? "" : String(value);
23179
+ const [draft, setDraft] = React74__default.useState(initial);
23180
+ React74__default.useEffect(() => setDraft(initial), [initial]);
23181
+ const commit = () => {
23182
+ if (numeric) {
23183
+ const n = draft.trim() === "" ? 0 : Number(draft);
23184
+ onCommit(field, Number.isNaN(n) ? 0 : n);
23185
+ } else {
23186
+ onCommit(field, draft);
23187
+ }
23188
+ };
23189
+ return /* @__PURE__ */ jsx(
23190
+ Input,
23191
+ {
23192
+ inputType: numeric ? "number" : "text",
23193
+ value: draft,
23194
+ onChange: (e) => setDraft(e.target.value),
23195
+ onBlur: commit,
23196
+ onKeyDown: (e) => {
23197
+ if (e.key === "Enter") commit();
23198
+ }
23199
+ }
23200
+ );
23201
+ }
23202
+ function isTraitConfigObject(v) {
23203
+ return v !== null && v !== void 0 && typeof v === "object" && !Array.isArray(v);
23204
+ }
23205
+ function FieldControl({
23206
+ name,
23207
+ decl,
23208
+ value,
23209
+ onChange,
23210
+ assets
23211
+ }) {
23212
+ let control;
23213
+ const stringValue = typeof value === "string" ? value : void 0;
23214
+ const effectiveValue = value ?? decl.default;
23215
+ if (decl.type === "icon") {
23216
+ control = /* @__PURE__ */ jsx(IconPicker, { value: stringValue, onChange: (icon) => onChange(name, icon) });
23217
+ } else if (decl.type === "asset") {
23218
+ control = /* @__PURE__ */ jsx(
23219
+ AssetPicker,
23220
+ {
23221
+ assets: assets ?? [],
23222
+ value: stringValue,
23223
+ onChange: (url) => onChange(name, url)
23224
+ }
23225
+ );
23226
+ } else if (decl.type === "boolean") {
23227
+ control = /* @__PURE__ */ jsx(Switch, { checked: value === true, onChange: (c) => onChange(name, c) });
23228
+ } else if (decl.type === "string" && decl.values !== void 0 && decl.values.length > 0) {
23229
+ control = /* @__PURE__ */ jsx(
23230
+ Select,
23231
+ {
23232
+ options: decl.values.map((v) => ({ value: v, label: v })),
23233
+ value: typeof value === "string" ? value : "",
23234
+ onChange: (v) => onChange(name, v)
23235
+ }
23236
+ );
23237
+ } else if (decl.type === "number") {
23238
+ control = /* @__PURE__ */ jsx(TextLikeControl, { field: name, numeric: true, value, onCommit: onChange });
23239
+ } else if (decl.type === "string") {
23240
+ control = /* @__PURE__ */ jsx(TextLikeControl, { field: name, numeric: false, value, onCommit: onChange });
23241
+ } else if (decl.type === "node") {
23242
+ control = /* @__PURE__ */ jsx(NodeSlotEditor, { value: effectiveValue, onChange: (next) => onChange(name, next) });
23243
+ } else if (decl.type.startsWith("[") || Array.isArray(effectiveValue)) {
23244
+ const arr = Array.isArray(effectiveValue) ? effectiveValue : [];
23245
+ control = /* @__PURE__ */ jsx(JsonTreeEditor, { value: arr, onChange: (next) => onChange(name, next) });
23246
+ } else if (decl.type === "object" || decl.type.startsWith("Map ") || !SCALAR_TYPES.has(decl.type) && isTraitConfigObject(effectiveValue)) {
23247
+ const obj = isTraitConfigObject(effectiveValue) ? effectiveValue : {};
23248
+ control = /* @__PURE__ */ jsx(JsonTreeEditor, { value: obj, onChange: (next) => onChange(name, next) });
23249
+ } else {
23250
+ control = /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "muted", children: [
23251
+ decl.type,
23252
+ " \u2014 edit in source"
23253
+ ] });
23254
+ }
23255
+ return /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
23256
+ /* @__PURE__ */ jsx(Typography, { variant: "label", children: decl.label ?? name }),
23257
+ control,
23258
+ decl.description !== void 0 && decl.description !== "" && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: decl.description })
23259
+ ] });
23260
+ }
23261
+ var TIER_ORDER, SCALAR_TYPES, PropertyInspector;
23262
+ var init_PropertyInspector = __esm({
23263
+ "components/core/molecules/PropertyInspector.tsx"() {
23264
+ "use client";
23265
+ init_cn();
23266
+ init_Stack();
23267
+ init_Typography();
23268
+ init_Button();
23269
+ init_Switch();
23270
+ init_Select();
23271
+ init_Input();
23272
+ init_FormSection();
23273
+ init_IconPicker();
23274
+ init_AssetPicker();
23275
+ init_JsonTreeEditor();
23276
+ init_NodeSlotEditor();
23277
+ TIER_ORDER = ["presentation", "domain", "policy", "infra", "internal"];
23278
+ SCALAR_TYPES = /* @__PURE__ */ new Set(["string", "number", "boolean", "icon", "asset"]);
23279
+ PropertyInspector = ({
23280
+ config,
23281
+ values,
23282
+ onChange,
23283
+ onReset,
23284
+ title,
23285
+ className,
23286
+ assets
23287
+ }) => {
23288
+ const fields = Object.entries(config);
23289
+ const byTier = /* @__PURE__ */ new Map();
23290
+ for (const [name, decl] of fields) {
23291
+ const tier = decl.tier ?? "presentation";
23292
+ const arr = byTier.get(tier) ?? [];
23293
+ arr.push([name, decl]);
23294
+ byTier.set(tier, arr);
23295
+ }
23296
+ const tiers = [...byTier.keys()].sort((a, b) => {
23297
+ const ia = TIER_ORDER.indexOf(a);
23298
+ const ib = TIER_ORDER.indexOf(b);
23299
+ return (ia === -1 ? 99 : ia) - (ib === -1 ? 99 : ib);
23300
+ });
23301
+ return /* @__PURE__ */ jsxs(VStack, { gap: "sm", className: cn("w-full", className), children: [
23302
+ /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", children: [
23303
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", weight: "bold", children: title ?? "Config" }),
23304
+ onReset !== void 0 && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", icon: "rotate-ccw", label: "Reset", onClick: onReset })
23305
+ ] }),
23306
+ fields.length === 0 && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: "No configurable properties." }),
23307
+ tiers.map((tier) => /* @__PURE__ */ jsx(FormSection, { title: tier, collapsible: true, defaultCollapsed: tier !== "presentation", children: /* @__PURE__ */ jsx(VStack, { gap: "sm", children: byTier.get(tier)?.map(([name, decl]) => /* @__PURE__ */ jsx(
23308
+ FieldControl,
23309
+ {
23310
+ name,
23311
+ decl,
23312
+ value: currentValue(decl, values?.[name]),
23313
+ onChange,
23314
+ assets
23315
+ },
23316
+ name
23317
+ )) }) }, tier))
23318
+ ] });
22476
23319
  };
22477
23320
  }
22478
23321
  });
@@ -22494,6 +23337,7 @@ var init_NodeSlotEditor = __esm({
22494
23337
  init_Select();
22495
23338
  init_Typography();
22496
23339
  init_JsonTreeEditor();
23340
+ init_PropertyInspector();
22497
23341
  init_pattern_resolver();
22498
23342
  init_cn();
22499
23343
  isObj2 = (v) => v !== null && v !== void 0 && typeof v === "object" && !Array.isArray(v);
@@ -22518,18 +23362,42 @@ var init_NodeSlotEditor = __esm({
22518
23362
  const pattern = { type: nextType, ...nextProps };
22519
23363
  onChange(wasArray || value === void 0 ? [pattern] : pattern);
22520
23364
  };
23365
+ const schemaEntries = React74__default.useMemo(() => {
23366
+ if (!type) return [];
23367
+ const def = getPatternDefinition(type);
23368
+ if (!def?.propsSchema) return [];
23369
+ return Object.entries(def.propsSchema).map(([propName, propDef]) => {
23370
+ const decl = {
23371
+ type: propDef.types?.[0] ?? "string",
23372
+ description: propDef.description,
23373
+ label: propName,
23374
+ ...propDef.enumValues && propDef.enumValues.length > 0 ? { values: propDef.enumValues } : {}
23375
+ };
23376
+ return [propName, decl];
23377
+ });
23378
+ }, [type]);
23379
+ const hasSchema = schemaEntries.length > 0;
22521
23380
  return /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: cn("w-full", className), children: [
22522
23381
  /* @__PURE__ */ jsx(
22523
23382
  Select,
22524
23383
  {
22525
23384
  options,
22526
23385
  value: type,
22527
- onChange: (e) => emit(e.target.value, props)
23386
+ onChange: (v) => emit(v, props)
22528
23387
  }
22529
23388
  ),
22530
23389
  type !== "" && /* @__PURE__ */ jsxs(VStack, { gap: "none", className: "pl-1", children: [
22531
23390
  /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: "props" }),
22532
- /* @__PURE__ */ jsx(JsonTreeEditor, { value: props, onChange: (next) => emit(type, isObj2(next) ? next : {}) })
23391
+ hasSchema ? /* @__PURE__ */ jsx(VStack, { gap: "xs", children: schemaEntries.map(([propName, decl]) => /* @__PURE__ */ jsx(
23392
+ FieldControl,
23393
+ {
23394
+ name: propName,
23395
+ decl,
23396
+ value: props[propName],
23397
+ onChange: (field, val) => emit(type, { ...props, [field]: val })
23398
+ },
23399
+ propName
23400
+ )) }) : /* @__PURE__ */ jsx(JsonTreeEditor, { value: props, onChange: (next) => emit(type, isObj2(next) ? next : {}) })
22533
23401
  ] })
22534
23402
  ] });
22535
23403
  };
@@ -22969,7 +23837,7 @@ var init_FilterGroup = __esm({
22969
23837
  Select,
22970
23838
  {
22971
23839
  value: selectedValues[filter.field] || "all",
22972
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
23840
+ onChange: (v) => handleFilterSelect(filter.field, v),
22973
23841
  options: [
22974
23842
  { value: "all", label: t("filterGroup.all") },
22975
23843
  ...filter.options?.map((opt) => ({
@@ -23052,7 +23920,7 @@ var init_FilterGroup = __esm({
23052
23920
  Select,
23053
23921
  {
23054
23922
  value: selectedValues[filter.field] || "all",
23055
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
23923
+ onChange: (v) => handleFilterSelect(filter.field, v),
23056
23924
  options: [
23057
23925
  { value: "all", label: t("filterGroup.allOf", { label: filter.label }) },
23058
23926
  ...filter.options?.map((opt) => ({
@@ -23170,7 +24038,7 @@ var init_FilterGroup = __esm({
23170
24038
  Select,
23171
24039
  {
23172
24040
  value: selectedValues[filter.field] || "all",
23173
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
24041
+ onChange: (v) => handleFilterSelect(filter.field, v),
23174
24042
  options: [
23175
24043
  { value: "all", label: t("filterGroup.all") },
23176
24044
  ...filter.options?.map((opt) => ({
@@ -25112,303 +25980,6 @@ var init_FlipCard = __esm({
25112
25980
  FlipCard.displayName = "FlipCard";
25113
25981
  }
25114
25982
  });
25115
- var ALL_CATEGORY, GridPicker;
25116
- var init_GridPicker = __esm({
25117
- "components/core/molecules/GridPicker.tsx"() {
25118
- "use client";
25119
- init_cn();
25120
- init_Input();
25121
- init_Badge();
25122
- init_Stack();
25123
- ALL_CATEGORY = "__all__";
25124
- GridPicker = ({
25125
- items,
25126
- value,
25127
- onChange,
25128
- categories,
25129
- searchPlaceholder,
25130
- renderThumbnail,
25131
- cellSize = 32,
25132
- className
25133
- }) => {
25134
- const [search, setSearch] = useState("");
25135
- const [activeCategory, setActiveCategory] = useState(ALL_CATEGORY);
25136
- const gridRef = useRef(null);
25137
- const categoryChips = useMemo(() => {
25138
- if (categories !== void 0) return categories;
25139
- const seen = [];
25140
- for (const item of items) {
25141
- if (!seen.includes(item.category)) seen.push(item.category);
25142
- }
25143
- return seen;
25144
- }, [categories, items]);
25145
- const filtered = useMemo(() => {
25146
- const needle = search.trim().toLowerCase();
25147
- return items.filter((item) => {
25148
- const matchesCategory = activeCategory === ALL_CATEGORY || item.category === activeCategory;
25149
- const matchesSearch = needle === "" || item.label.toLowerCase().includes(needle);
25150
- return matchesCategory && matchesSearch;
25151
- });
25152
- }, [items, search, activeCategory]);
25153
- const select = useCallback(
25154
- (item) => {
25155
- onChange(item.id);
25156
- },
25157
- [onChange]
25158
- );
25159
- const handleKeyDown = useCallback(
25160
- (e, index) => {
25161
- const cells = gridRef.current?.querySelectorAll(
25162
- "[data-gridpicker-cell]"
25163
- );
25164
- if (cells === void 0 || cells.length === 0) return;
25165
- const columns = (() => {
25166
- const grid = gridRef.current;
25167
- if (grid === null) return 1;
25168
- const style = window.getComputedStyle(grid);
25169
- const cols = style.gridTemplateColumns.split(" ").filter(Boolean).length;
25170
- return cols > 0 ? cols : 1;
25171
- })();
25172
- let next = -1;
25173
- if (e.key === "ArrowRight") next = index + 1;
25174
- else if (e.key === "ArrowLeft") next = index - 1;
25175
- else if (e.key === "ArrowDown") next = index + columns;
25176
- else if (e.key === "ArrowUp") next = index - columns;
25177
- else if (e.key === "Enter" || e.key === " ") {
25178
- e.preventDefault();
25179
- select(filtered[index]);
25180
- return;
25181
- } else {
25182
- return;
25183
- }
25184
- e.preventDefault();
25185
- if (next >= 0 && next < cells.length) {
25186
- cells[next].focus();
25187
- }
25188
- },
25189
- [filtered, select]
25190
- );
25191
- return /* @__PURE__ */ jsxs(VStack, { gap: "sm", className: cn("w-full", className), children: [
25192
- /* @__PURE__ */ jsx(
25193
- Input,
25194
- {
25195
- type: "search",
25196
- icon: "search",
25197
- value: search,
25198
- placeholder: searchPlaceholder,
25199
- clearable: true,
25200
- onClear: () => setSearch(""),
25201
- onChange: (e) => setSearch(e.target.value)
25202
- }
25203
- ),
25204
- categoryChips.length > 0 && /* @__PURE__ */ jsxs(HStack, { gap: "xs", wrap: true, children: [
25205
- /* @__PURE__ */ jsx(
25206
- Badge,
25207
- {
25208
- variant: activeCategory === ALL_CATEGORY ? "primary" : "neutral",
25209
- size: "sm",
25210
- role: "button",
25211
- tabIndex: 0,
25212
- "aria-pressed": activeCategory === ALL_CATEGORY,
25213
- className: "cursor-pointer",
25214
- onClick: () => setActiveCategory(ALL_CATEGORY),
25215
- onKeyDown: (e) => {
25216
- if (e.key === "Enter" || e.key === " ") {
25217
- e.preventDefault();
25218
- setActiveCategory(ALL_CATEGORY);
25219
- }
25220
- },
25221
- children: "All"
25222
- }
25223
- ),
25224
- categoryChips.map((category) => /* @__PURE__ */ jsx(
25225
- Badge,
25226
- {
25227
- variant: activeCategory === category ? "primary" : "neutral",
25228
- size: "sm",
25229
- role: "button",
25230
- tabIndex: 0,
25231
- "aria-pressed": activeCategory === category,
25232
- className: "cursor-pointer",
25233
- onClick: () => setActiveCategory(category),
25234
- onKeyDown: (e) => {
25235
- if (e.key === "Enter" || e.key === " ") {
25236
- e.preventDefault();
25237
- setActiveCategory(category);
25238
- }
25239
- },
25240
- children: category
25241
- },
25242
- category
25243
- ))
25244
- ] }),
25245
- /* @__PURE__ */ jsx(
25246
- "div",
25247
- {
25248
- ref: gridRef,
25249
- role: "listbox",
25250
- className: "grid gap-1 overflow-y-auto max-h-64 p-1",
25251
- style: {
25252
- gridTemplateColumns: `repeat(auto-fill, minmax(${cellSize}px, 1fr))`
25253
- },
25254
- children: filtered.map((item, index) => {
25255
- const selected = item.id === value;
25256
- return /* @__PURE__ */ jsx(
25257
- "button",
25258
- {
25259
- type: "button",
25260
- role: "option",
25261
- "aria-selected": selected,
25262
- "aria-label": item.label,
25263
- title: item.label,
25264
- "data-gridpicker-cell": true,
25265
- tabIndex: selected || value === void 0 && index === 0 ? 0 : -1,
25266
- onClick: () => select(item),
25267
- onKeyDown: (e) => handleKeyDown(e, index),
25268
- className: cn(
25269
- "flex items-center justify-center rounded-sm",
25270
- "transition-colors hover:bg-muted",
25271
- "focus:outline-none focus:ring-1 focus:ring-ring",
25272
- selected && "bg-primary/10 ring-1 ring-primary"
25273
- ),
25274
- style: { width: cellSize, height: cellSize },
25275
- children: renderThumbnail(item)
25276
- },
25277
- item.id
25278
- );
25279
- })
25280
- }
25281
- )
25282
- ] });
25283
- };
25284
- GridPicker.displayName = "GridPicker";
25285
- }
25286
- });
25287
- function iconForKind(kind) {
25288
- if (kind === "audio") return "music";
25289
- if (kind === "model") return "box";
25290
- return "file";
25291
- }
25292
- var THUMB_PX, IMAGE_KINDS, AssetPicker;
25293
- var init_AssetPicker = __esm({
25294
- "components/core/molecules/AssetPicker.tsx"() {
25295
- "use client";
25296
- init_GridPicker();
25297
- init_Icon();
25298
- THUMB_PX = 32;
25299
- IMAGE_KINDS = /* @__PURE__ */ new Set([
25300
- "image",
25301
- "spritesheet",
25302
- "scene",
25303
- "portrait"
25304
- ]);
25305
- AssetPicker = ({
25306
- assets,
25307
- value,
25308
- onChange,
25309
- className
25310
- }) => {
25311
- const byUrl = useMemo(() => {
25312
- const map = /* @__PURE__ */ new Map();
25313
- for (const entry of assets) map.set(entry.url, entry);
25314
- return map;
25315
- }, [assets]);
25316
- const items = useMemo(
25317
- () => assets.map((entry) => ({
25318
- id: entry.url,
25319
- label: entry.name,
25320
- category: entry.category
25321
- })),
25322
- [assets]
25323
- );
25324
- const categories = useMemo(() => {
25325
- const seen = [];
25326
- for (const entry of assets) {
25327
- if (!seen.includes(entry.category)) seen.push(entry.category);
25328
- }
25329
- return seen;
25330
- }, [assets]);
25331
- const renderThumbnail = useCallback(
25332
- (item) => {
25333
- const entry = byUrl.get(item.id);
25334
- if (entry === void 0) return null;
25335
- if (IMAGE_KINDS.has(entry.kind)) {
25336
- return /* @__PURE__ */ jsx(
25337
- "img",
25338
- {
25339
- src: entry.thumbnailUrl ?? entry.url,
25340
- alt: entry.name,
25341
- loading: "lazy",
25342
- width: THUMB_PX,
25343
- height: THUMB_PX,
25344
- style: { width: THUMB_PX, height: THUMB_PX, objectFit: "cover" }
25345
- }
25346
- );
25347
- }
25348
- return /* @__PURE__ */ jsx(Icon, { name: iconForKind(entry.kind), size: "sm" });
25349
- },
25350
- [byUrl]
25351
- );
25352
- return /* @__PURE__ */ jsx(
25353
- GridPicker,
25354
- {
25355
- items,
25356
- value,
25357
- onChange,
25358
- categories,
25359
- renderThumbnail,
25360
- cellSize: THUMB_PX,
25361
- className
25362
- }
25363
- );
25364
- };
25365
- AssetPicker.displayName = "AssetPicker";
25366
- }
25367
- });
25368
- function pascalToKebab(name) {
25369
- return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z])([A-Z][a-z])/g, "$1-$2").toLowerCase();
25370
- }
25371
- function kebabToPascal3(name) {
25372
- return name.split("-").map((part) => /^\d+$/.test(part) ? part : part.charAt(0).toUpperCase() + part.slice(1)).join("");
25373
- }
25374
- var ICON_ITEMS, IconPicker;
25375
- var init_IconPicker = __esm({
25376
- "components/core/molecules/IconPicker.tsx"() {
25377
- "use client";
25378
- init_Icon();
25379
- init_GridPicker();
25380
- ICON_ITEMS = (() => {
25381
- const items = [];
25382
- for (const [exportName, candidate] of Object.entries(LucideIcons2)) {
25383
- if (!/^[A-Z]/.test(exportName)) continue;
25384
- if (exportName.endsWith("Icon")) continue;
25385
- if (exportName.startsWith("Lucide")) continue;
25386
- const isComponent = candidate !== null && (typeof candidate === "object" || typeof candidate === "function") && "$$typeof" in candidate;
25387
- if (!isComponent) continue;
25388
- const kebab = pascalToKebab(exportName);
25389
- if (kebabToPascal3(kebab) !== exportName) continue;
25390
- items.push({ id: kebab, label: kebab, category: "icons" });
25391
- }
25392
- return items;
25393
- })();
25394
- IconPicker = ({ value, onChange, className }) => {
25395
- const items = useMemo(() => ICON_ITEMS, []);
25396
- return /* @__PURE__ */ jsx(
25397
- GridPicker,
25398
- {
25399
- items,
25400
- value,
25401
- onChange,
25402
- searchPlaceholder: "Search icons\u2026",
25403
- renderThumbnail: (it) => /* @__PURE__ */ jsx(Icon, { name: it.id }),
25404
- cellSize: 32,
25405
- className
25406
- }
25407
- );
25408
- };
25409
- IconPicker.displayName = "IconPicker";
25410
- }
25411
- });
25412
25983
  function toISODate(d) {
25413
25984
  return d.toISOString().slice(0, 10);
25414
25985
  }
@@ -28185,13 +28756,13 @@ var init_MapView = __esm({
28185
28756
  shadowSize: [41, 41]
28186
28757
  });
28187
28758
  L.Marker.prototype.options.icon = defaultIcon;
28188
- const { useEffect: useEffect71, useRef: useRef68, useCallback: useCallback114, useState: useState100 } = React74__default;
28759
+ const { useEffect: useEffect72, useRef: useRef69, useCallback: useCallback114, useState: useState101 } = React74__default;
28189
28760
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
28190
28761
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
28191
28762
  function MapUpdater({ centerLat, centerLng, zoom }) {
28192
28763
  const map = useMap();
28193
- const prevRef = useRef68({ centerLat, centerLng, zoom });
28194
- useEffect71(() => {
28764
+ const prevRef = useRef69({ centerLat, centerLng, zoom });
28765
+ useEffect72(() => {
28195
28766
  const prev = prevRef.current;
28196
28767
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
28197
28768
  map.setView([centerLat, centerLng], zoom);
@@ -28202,7 +28773,7 @@ var init_MapView = __esm({
28202
28773
  }
28203
28774
  function MapClickHandler({ onMapClick }) {
28204
28775
  const map = useMap();
28205
- useEffect71(() => {
28776
+ useEffect72(() => {
28206
28777
  if (!onMapClick) return;
28207
28778
  const handler = (e) => {
28208
28779
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -28230,7 +28801,7 @@ var init_MapView = __esm({
28230
28801
  showAttribution = true
28231
28802
  }) {
28232
28803
  const eventBus = useEventBus2();
28233
- const [clickedPosition, setClickedPosition] = useState100(null);
28804
+ const [clickedPosition, setClickedPosition] = useState101(null);
28234
28805
  const handleMapClick = useCallback114((lat, lng) => {
28235
28806
  if (showClickedPin) {
28236
28807
  setClickedPosition({ lat, lng });
@@ -33666,8 +34237,8 @@ var init_VersionDiff = __esm({
33666
34237
  return { added, removed };
33667
34238
  }, [diff]);
33668
34239
  const handleBeforeChange = useCallback(
33669
- (e) => {
33670
- const id = e.target.value;
34240
+ (v) => {
34241
+ const id = v;
33671
34242
  setInternalBefore(id);
33672
34243
  onSelectBefore?.(id);
33673
34244
  if (selectBeforeEvent) eventBus.emit(`UI:${selectBeforeEvent}`, { id });
@@ -33675,8 +34246,8 @@ var init_VersionDiff = __esm({
33675
34246
  [onSelectBefore, selectBeforeEvent, eventBus]
33676
34247
  );
33677
34248
  const handleAfterChange = useCallback(
33678
- (e) => {
33679
- const id = e.target.value;
34249
+ (v) => {
34250
+ const id = v;
33680
34251
  setInternalAfter(id);
33681
34252
  onSelectAfter?.(id);
33682
34253
  if (selectAfterEvent) eventBus.emit(`UI:${selectAfterEvent}`, { id });
@@ -35099,286 +35670,6 @@ var init_PageHeader = __esm({
35099
35670
  PageHeader.displayName = "PageHeader";
35100
35671
  }
35101
35672
  });
35102
- var FormSection, FormLayout, FormActions;
35103
- var init_FormSection = __esm({
35104
- "components/core/molecules/FormSection.tsx"() {
35105
- "use client";
35106
- init_cn();
35107
- init_atoms2();
35108
- init_Box();
35109
- init_Typography();
35110
- init_Button();
35111
- init_Stack();
35112
- init_Icon();
35113
- init_useEventBus();
35114
- FormSection = ({
35115
- title,
35116
- description,
35117
- children,
35118
- collapsible = false,
35119
- defaultCollapsed = false,
35120
- card = false,
35121
- columns = 1,
35122
- className
35123
- }) => {
35124
- const [collapsed, setCollapsed] = React74__default.useState(defaultCollapsed);
35125
- const { t } = useTranslate();
35126
- const eventBus = useEventBus();
35127
- const gridClass = {
35128
- 1: "grid-cols-1",
35129
- 2: "grid-cols-1 md:grid-cols-2",
35130
- 3: "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
35131
- }[columns];
35132
- React74__default.useCallback(() => {
35133
- if (collapsible) {
35134
- setCollapsed((prev) => !prev);
35135
- eventBus.emit("UI:TOGGLE_COLLAPSE", { collapsed: !collapsed });
35136
- }
35137
- }, [collapsible, collapsed, eventBus]);
35138
- const content = /* @__PURE__ */ jsxs(Fragment, { children: [
35139
- (title || description) && /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "mb-4", children: [
35140
- title && /* @__PURE__ */ jsxs(
35141
- HStack,
35142
- {
35143
- justify: "between",
35144
- align: "center",
35145
- className: cn(collapsible && "cursor-pointer"),
35146
- action: collapsible ? "TOGGLE_COLLAPSE" : void 0,
35147
- children: [
35148
- /* @__PURE__ */ jsx(Typography, { variant: "h3", weight: "semibold", children: title }),
35149
- collapsible && /* @__PURE__ */ jsx(
35150
- Button,
35151
- {
35152
- variant: "ghost",
35153
- size: "sm",
35154
- action: "TOGGLE_COLLAPSE",
35155
- children: /* @__PURE__ */ jsx(
35156
- Icon,
35157
- {
35158
- icon: ChevronDown,
35159
- size: "sm",
35160
- className: cn(
35161
- "text-muted-foreground transition-transform",
35162
- collapsed && "rotate-180"
35163
- )
35164
- }
35165
- )
35166
- }
35167
- )
35168
- ]
35169
- }
35170
- ),
35171
- description && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: description })
35172
- ] }),
35173
- (!collapsible || !collapsed) && /* @__PURE__ */ jsx(Box, { className: cn("grid gap-4", gridClass), children })
35174
- ] });
35175
- if (card) {
35176
- return /* @__PURE__ */ jsx(Card, { className: cn("p-6", className), children: content });
35177
- }
35178
- return /* @__PURE__ */ jsx(Box, { className, children: content });
35179
- };
35180
- FormSection.displayName = "FormSection";
35181
- FormLayout = ({
35182
- children,
35183
- dividers = true,
35184
- className
35185
- }) => {
35186
- return /* @__PURE__ */ jsx(
35187
- VStack,
35188
- {
35189
- gap: "lg",
35190
- className: cn(
35191
- dividers && "[&>*+*]:pt-8 [&>*+*]:border-t [&>*+*]:border-border",
35192
- className
35193
- ),
35194
- children
35195
- }
35196
- );
35197
- };
35198
- FormLayout.displayName = "FormLayout";
35199
- FormActions = ({
35200
- children,
35201
- sticky = false,
35202
- align = "right",
35203
- className
35204
- }) => {
35205
- const alignClass2 = {
35206
- left: "justify-start",
35207
- right: "justify-end",
35208
- between: "justify-between",
35209
- center: "justify-center"
35210
- }[align];
35211
- return /* @__PURE__ */ jsx(
35212
- HStack,
35213
- {
35214
- gap: "sm",
35215
- align: "center",
35216
- className: cn(
35217
- "pt-6 border-t border-border",
35218
- alignClass2,
35219
- sticky && "sticky bottom-0 bg-card py-4 -mx-6 px-6 shadow-[0_-4px_6px_-1px_rgb(0,0,0,0.05)]",
35220
- className
35221
- ),
35222
- children
35223
- }
35224
- );
35225
- };
35226
- FormActions.displayName = "FormActions";
35227
- }
35228
- });
35229
- function currentValue(decl, override) {
35230
- return override !== void 0 ? override : decl.default;
35231
- }
35232
- function TextLikeControl({
35233
- field,
35234
- numeric,
35235
- value,
35236
- onCommit
35237
- }) {
35238
- const initial = value === void 0 || value === null ? "" : String(value);
35239
- const [draft, setDraft] = React74__default.useState(initial);
35240
- React74__default.useEffect(() => setDraft(initial), [initial]);
35241
- const commit = () => {
35242
- if (numeric) {
35243
- const n = draft.trim() === "" ? 0 : Number(draft);
35244
- onCommit(field, Number.isNaN(n) ? 0 : n);
35245
- } else {
35246
- onCommit(field, draft);
35247
- }
35248
- };
35249
- return /* @__PURE__ */ jsx(
35250
- Input,
35251
- {
35252
- inputType: numeric ? "number" : "text",
35253
- value: draft,
35254
- onChange: (e) => setDraft(e.target.value),
35255
- onBlur: commit,
35256
- onKeyDown: (e) => {
35257
- if (e.key === "Enter") commit();
35258
- }
35259
- }
35260
- );
35261
- }
35262
- function isTraitConfigObject(v) {
35263
- return v !== null && v !== void 0 && typeof v === "object" && !Array.isArray(v);
35264
- }
35265
- function FieldControl({
35266
- name,
35267
- decl,
35268
- value,
35269
- onChange,
35270
- assets
35271
- }) {
35272
- let control;
35273
- const stringValue = typeof value === "string" ? value : void 0;
35274
- const effectiveValue = value ?? decl.default;
35275
- if (decl.type === "icon") {
35276
- control = /* @__PURE__ */ jsx(IconPicker, { value: stringValue, onChange: (icon) => onChange(name, icon) });
35277
- } else if (decl.type === "asset") {
35278
- control = /* @__PURE__ */ jsx(
35279
- AssetPicker,
35280
- {
35281
- assets: assets ?? [],
35282
- value: stringValue,
35283
- onChange: (url) => onChange(name, url)
35284
- }
35285
- );
35286
- } else if (decl.type === "boolean") {
35287
- control = /* @__PURE__ */ jsx(Switch, { checked: value === true, onChange: (c) => onChange(name, c) });
35288
- } else if (decl.type === "string" && decl.values !== void 0 && decl.values.length > 0) {
35289
- control = /* @__PURE__ */ jsx(
35290
- Select,
35291
- {
35292
- options: decl.values.map((v) => ({ value: v, label: v })),
35293
- value: typeof value === "string" ? value : "",
35294
- onChange: (e) => onChange(name, e.target.value)
35295
- }
35296
- );
35297
- } else if (decl.type === "number") {
35298
- control = /* @__PURE__ */ jsx(TextLikeControl, { field: name, numeric: true, value, onCommit: onChange });
35299
- } else if (decl.type === "string") {
35300
- control = /* @__PURE__ */ jsx(TextLikeControl, { field: name, numeric: false, value, onCommit: onChange });
35301
- } else if (decl.type === "node") {
35302
- control = /* @__PURE__ */ jsx(NodeSlotEditor, { value: effectiveValue, onChange: (next) => onChange(name, next) });
35303
- } else if (decl.type.startsWith("[") || Array.isArray(effectiveValue)) {
35304
- const arr = Array.isArray(effectiveValue) ? effectiveValue : [];
35305
- control = /* @__PURE__ */ jsx(JsonTreeEditor, { value: arr, onChange: (next) => onChange(name, next) });
35306
- } else if (decl.type === "object" || decl.type.startsWith("Map ") || !SCALAR_TYPES.has(decl.type) && isTraitConfigObject(effectiveValue)) {
35307
- const obj = isTraitConfigObject(effectiveValue) ? effectiveValue : {};
35308
- control = /* @__PURE__ */ jsx(JsonTreeEditor, { value: obj, onChange: (next) => onChange(name, next) });
35309
- } else {
35310
- control = /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "muted", children: [
35311
- decl.type,
35312
- " \u2014 edit in source"
35313
- ] });
35314
- }
35315
- return /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
35316
- /* @__PURE__ */ jsx(Typography, { variant: "label", children: decl.label ?? name }),
35317
- control,
35318
- decl.description !== void 0 && decl.description !== "" && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: decl.description })
35319
- ] });
35320
- }
35321
- var TIER_ORDER, SCALAR_TYPES, PropertyInspector;
35322
- var init_PropertyInspector = __esm({
35323
- "components/core/molecules/PropertyInspector.tsx"() {
35324
- "use client";
35325
- init_cn();
35326
- init_Stack();
35327
- init_Typography();
35328
- init_Button();
35329
- init_Switch();
35330
- init_Select();
35331
- init_Input();
35332
- init_FormSection();
35333
- init_IconPicker();
35334
- init_AssetPicker();
35335
- init_JsonTreeEditor();
35336
- init_NodeSlotEditor();
35337
- TIER_ORDER = ["presentation", "domain", "policy", "infra", "internal"];
35338
- SCALAR_TYPES = /* @__PURE__ */ new Set(["string", "number", "boolean", "icon", "asset"]);
35339
- PropertyInspector = ({
35340
- config,
35341
- values,
35342
- onChange,
35343
- onReset,
35344
- title,
35345
- className,
35346
- assets
35347
- }) => {
35348
- const fields = Object.entries(config);
35349
- const byTier = /* @__PURE__ */ new Map();
35350
- for (const [name, decl] of fields) {
35351
- const tier = decl.tier ?? "presentation";
35352
- const arr = byTier.get(tier) ?? [];
35353
- arr.push([name, decl]);
35354
- byTier.set(tier, arr);
35355
- }
35356
- const tiers = [...byTier.keys()].sort((a, b) => {
35357
- const ia = TIER_ORDER.indexOf(a);
35358
- const ib = TIER_ORDER.indexOf(b);
35359
- return (ia === -1 ? 99 : ia) - (ib === -1 ? 99 : ib);
35360
- });
35361
- return /* @__PURE__ */ jsxs(VStack, { gap: "sm", className: cn("w-full", className), children: [
35362
- /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", children: [
35363
- /* @__PURE__ */ jsx(Typography, { variant: "caption", weight: "bold", children: title ?? "Config" }),
35364
- onReset !== void 0 && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", icon: "rotate-ccw", label: "Reset", onClick: onReset })
35365
- ] }),
35366
- fields.length === 0 && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: "No configurable properties." }),
35367
- tiers.map((tier) => /* @__PURE__ */ jsx(FormSection, { title: tier, collapsible: true, defaultCollapsed: tier !== "presentation", children: /* @__PURE__ */ jsx(VStack, { gap: "sm", children: byTier.get(tier)?.map(([name, decl]) => /* @__PURE__ */ jsx(
35368
- FieldControl,
35369
- {
35370
- name,
35371
- decl,
35372
- value: currentValue(decl, values?.[name]),
35373
- onChange,
35374
- assets
35375
- },
35376
- name
35377
- )) }) }, tier))
35378
- ] });
35379
- };
35380
- }
35381
- });
35382
35673
  var lookStyles8, Header;
35383
35674
  var init_Header = __esm({
35384
35675
  "components/core/molecules/Header.tsx"() {
@@ -36734,7 +37025,6 @@ var init_DocumentViewer = __esm({
36734
37025
  showPrint = false,
36735
37026
  actions,
36736
37027
  documents,
36737
- entity,
36738
37028
  isLoading = false,
36739
37029
  error,
36740
37030
  className
@@ -38803,11 +39093,11 @@ function RuleEditor({
38803
39093
  className
38804
39094
  }) {
38805
39095
  const { t } = useTranslate();
38806
- const handleWhenChange = useCallback((e) => {
38807
- onChange({ ...rule, whenEvent: e.target.value });
39096
+ const handleWhenChange = useCallback((v) => {
39097
+ onChange({ ...rule, whenEvent: v });
38808
39098
  }, [rule, onChange]);
38809
- const handleThenChange = useCallback((e) => {
38810
- onChange({ ...rule, thenAction: e.target.value });
39099
+ const handleThenChange = useCallback((v) => {
39100
+ onChange({ ...rule, thenAction: v });
38811
39101
  }, [rule, onChange]);
38812
39102
  return /* @__PURE__ */ jsxs(HStack, { className: cn("items-center p-2 rounded-lg bg-muted/50 border border-border", className), gap: "sm", children: [
38813
39103
  /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-primary font-bold whitespace-nowrap", children: t("eventHandler.when") }),
@@ -39883,7 +40173,7 @@ var init_Form = __esm({
39883
40173
  ...commonProps,
39884
40174
  options,
39885
40175
  value: String(currentValue2),
39886
- onChange: (e) => handleChange(fieldName, e.target.value),
40176
+ onChange: (v) => handleChange(fieldName, v),
39887
40177
  placeholder: field.placeholder || `Select ${label}...`
39888
40178
  }
39889
40179
  );
@@ -43904,7 +44194,7 @@ function TraitSlot({
43904
44194
  size = "md",
43905
44195
  showTooltip = true,
43906
44196
  categoryColors,
43907
- tooltipFrameUrl,
44197
+ tooltipFrameUrl = "",
43908
44198
  className,
43909
44199
  feedback,
43910
44200
  onItemDrop,
@@ -47122,18 +47412,32 @@ var init_XPBar = __esm({
47122
47412
  }
47123
47413
  });
47124
47414
  function lazyThree(name, loader) {
47125
- const Lazy = React74__default.lazy(() => loader().then((m) => ({ default: m[name] })));
47415
+ const Lazy = React74__default.lazy(
47416
+ () => loader().then((m) => {
47417
+ const Resolved = m[name];
47418
+ if (!Resolved) {
47419
+ throw new Error(
47420
+ `[@almadar/ui] 3D component "${name}" was not found in the three subpath bundle.`
47421
+ );
47422
+ }
47423
+ return { default: Resolved };
47424
+ })
47425
+ );
47126
47426
  function ThreeWrapper(props) {
47127
47427
  return React74__default.createElement(
47128
- React74__default.Suspense,
47129
- { fallback: null },
47130
- React74__default.createElement(Lazy, props)
47428
+ ThreeBoundary,
47429
+ { name },
47430
+ React74__default.createElement(
47431
+ React74__default.Suspense,
47432
+ { fallback: null },
47433
+ React74__default.createElement(Lazy, props)
47434
+ )
47131
47435
  );
47132
47436
  }
47133
47437
  ThreeWrapper.displayName = `Lazy(${name})`;
47134
47438
  return ThreeWrapper;
47135
47439
  }
47136
- var FeatureRenderer, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
47440
+ var ThreeBoundary, FeatureRenderer, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
47137
47441
  var init_component_registry_generated = __esm({
47138
47442
  "components/core/organisms/component-registry.generated.ts"() {
47139
47443
  init_AboutPageTemplate();
@@ -47419,6 +47723,28 @@ var init_component_registry_generated = __esm({
47419
47723
  init_WorldMapBoard();
47420
47724
  init_WorldMapTemplate();
47421
47725
  init_XPBar();
47726
+ ThreeBoundary = class extends React74__default.Component {
47727
+ constructor() {
47728
+ super(...arguments);
47729
+ __publicField(this, "state", { failed: false });
47730
+ }
47731
+ static getDerivedStateFromError() {
47732
+ return { failed: true };
47733
+ }
47734
+ render() {
47735
+ if (this.state.failed) {
47736
+ return React74__default.createElement(
47737
+ "div",
47738
+ {
47739
+ "data-testid": "three-unavailable",
47740
+ style: { padding: 16, fontSize: 13, lineHeight: 1.5, opacity: 0.7 }
47741
+ },
47742
+ `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.`
47743
+ );
47744
+ }
47745
+ return this.props.children;
47746
+ }
47747
+ };
47422
47748
  FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/molecules/game/three'));
47423
47749
  GameCanvas3D = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
47424
47750
  GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
@@ -48402,7 +48728,7 @@ function SlotContentRenderer({
48402
48728
  const { children: _childrenConfig, ...restPropsNoChildren } = content.props;
48403
48729
  const restProps = childrenIsRenderFn ? { ...restPropsNoChildren, children: incomingChildren } : restPropsNoChildren;
48404
48730
  const renderedProps = renderPatternProps(restProps, onDismiss);
48405
- const patternDef = getPatternDefinition(content.pattern);
48731
+ const patternDef = getPatternDefinition$1(content.pattern);
48406
48732
  const propsSchema = patternDef?.propsSchema;
48407
48733
  if (propsSchema) {
48408
48734
  for (const [propKey, propValue] of Object.entries(renderedProps)) {
@@ -49701,195 +50027,6 @@ init_StepFlowOrganism();
49701
50027
  init_ShowcaseOrganism();
49702
50028
  init_TeamOrganism();
49703
50029
  init_CaseStudyOrganism();
49704
- var DEFAULT_FEATURE_CONFIGS = {
49705
- tree: { color: 2263842, height: 1.5, scale: 1, geometry: "tree" },
49706
- rock: { color: 8421504, height: 0.5, scale: 0.8, geometry: "rock" },
49707
- bush: { color: 3329330, height: 0.4, scale: 0.6, geometry: "bush" },
49708
- house: { color: 9127187, height: 1.2, scale: 1.2, geometry: "house" },
49709
- tower: { color: 6908265, height: 2.5, scale: 1, geometry: "tower" },
49710
- wall: { color: 8421504, height: 1, scale: 1, geometry: "wall" },
49711
- mountain: { color: 5597999, height: 2, scale: 1.5, geometry: "mountain" },
49712
- hill: { color: 7048739, height: 0.8, scale: 1.2, geometry: "hill" },
49713
- water: { color: 4491468, height: 0.1, scale: 1, geometry: "water" },
49714
- chest: { color: 16766720, height: 0.3, scale: 0.4, geometry: "chest" },
49715
- sign: { color: 9127187, height: 0.8, scale: 0.3, geometry: "sign" },
49716
- portal: { color: 10040012, height: 1.5, scale: 1, geometry: "portal" }
49717
- };
49718
- function TreeFeature({ height, color }) {
49719
- return /* @__PURE__ */ jsxs(Fragment, { children: [
49720
- /* @__PURE__ */ jsxs("mesh", { position: [0, height * 0.3, 0], children: [
49721
- /* @__PURE__ */ jsx("cylinderGeometry", { args: [0.08, 0.1, height * 0.6, 6] }),
49722
- /* @__PURE__ */ jsx("meshStandardMaterial", { color: 9127187 })
49723
- ] }),
49724
- /* @__PURE__ */ jsxs("mesh", { position: [0, height * 0.7, 0], children: [
49725
- /* @__PURE__ */ jsx("coneGeometry", { args: [0.4, height * 0.5, 8] }),
49726
- /* @__PURE__ */ jsx("meshStandardMaterial", { color })
49727
- ] }),
49728
- /* @__PURE__ */ jsxs("mesh", { position: [0, height * 0.9, 0], children: [
49729
- /* @__PURE__ */ jsx("coneGeometry", { args: [0.3, height * 0.4, 8] }),
49730
- /* @__PURE__ */ jsx("meshStandardMaterial", { color })
49731
- ] }),
49732
- /* @__PURE__ */ jsxs("mesh", { position: [0, height * 1.05, 0], children: [
49733
- /* @__PURE__ */ jsx("coneGeometry", { args: [0.15, height * 0.25, 8] }),
49734
- /* @__PURE__ */ jsx("meshStandardMaterial", { color })
49735
- ] })
49736
- ] });
49737
- }
49738
- function RockFeature({ height, color }) {
49739
- return /* @__PURE__ */ jsxs("mesh", { position: [0, height * 0.4, 0], children: [
49740
- /* @__PURE__ */ jsx("dodecahedronGeometry", { args: [height * 0.5, 0] }),
49741
- /* @__PURE__ */ jsx("meshStandardMaterial", { color, roughness: 0.9 })
49742
- ] });
49743
- }
49744
- function BushFeature({ height, color }) {
49745
- return /* @__PURE__ */ jsxs(Fragment, { children: [
49746
- /* @__PURE__ */ jsxs("mesh", { position: [0, height * 0.3, 0], children: [
49747
- /* @__PURE__ */ jsx("sphereGeometry", { args: [height * 0.4, 8, 8] }),
49748
- /* @__PURE__ */ jsx("meshStandardMaterial", { color })
49749
- ] }),
49750
- /* @__PURE__ */ jsxs("mesh", { position: [0.1, height * 0.4, 0.1], children: [
49751
- /* @__PURE__ */ jsx("sphereGeometry", { args: [height * 0.25, 8, 8] }),
49752
- /* @__PURE__ */ jsx("meshStandardMaterial", { color })
49753
- ] })
49754
- ] });
49755
- }
49756
- function HouseFeature({ height, color }) {
49757
- return /* @__PURE__ */ jsxs(Fragment, { children: [
49758
- /* @__PURE__ */ jsxs("mesh", { position: [0, height * 0.4, 0], children: [
49759
- /* @__PURE__ */ jsx("boxGeometry", { args: [0.8, height * 0.8, 0.8] }),
49760
- /* @__PURE__ */ jsx("meshStandardMaterial", { color: 13808780 })
49761
- ] }),
49762
- /* @__PURE__ */ jsxs("mesh", { position: [0, height * 0.9, 0], children: [
49763
- /* @__PURE__ */ jsx("coneGeometry", { args: [0.6, height * 0.4, 4] }),
49764
- /* @__PURE__ */ jsx("meshStandardMaterial", { color })
49765
- ] }),
49766
- /* @__PURE__ */ jsxs("mesh", { position: [0, height * 0.25, 0.41], children: [
49767
- /* @__PURE__ */ jsx("planeGeometry", { args: [0.25, height * 0.5] }),
49768
- /* @__PURE__ */ jsx("meshStandardMaterial", { color: 4863784 })
49769
- ] })
49770
- ] });
49771
- }
49772
- function TowerFeature({ height, color }) {
49773
- return /* @__PURE__ */ jsxs(Fragment, { children: [
49774
- /* @__PURE__ */ jsxs("mesh", { position: [0, height * 0.5, 0], children: [
49775
- /* @__PURE__ */ jsx("cylinderGeometry", { args: [0.3, 0.35, height, 8] }),
49776
- /* @__PURE__ */ jsx("meshStandardMaterial", { color })
49777
- ] }),
49778
- /* @__PURE__ */ jsxs("mesh", { position: [0, height + 0.05, 0], children: [
49779
- /* @__PURE__ */ jsx("cylinderGeometry", { args: [0.35, 0.35, 0.1, 8] }),
49780
- /* @__PURE__ */ jsx("meshStandardMaterial", { color })
49781
- ] })
49782
- ] });
49783
- }
49784
- function ChestFeature({ height, color }) {
49785
- return /* @__PURE__ */ jsxs(Fragment, { children: [
49786
- /* @__PURE__ */ jsxs("mesh", { position: [0, height * 0.5, 0], children: [
49787
- /* @__PURE__ */ jsx("boxGeometry", { args: [0.3, height, 0.2] }),
49788
- /* @__PURE__ */ jsx("meshStandardMaterial", { color, metalness: 0.6, roughness: 0.3 })
49789
- ] }),
49790
- /* @__PURE__ */ jsxs("mesh", { position: [0, height + 0.05, 0], children: [
49791
- /* @__PURE__ */ jsx("cylinderGeometry", { args: [0.15, 0.15, 0.3, 8, 1, false, 0, Math.PI] }),
49792
- /* @__PURE__ */ jsx("meshStandardMaterial", { color, metalness: 0.6, roughness: 0.3 })
49793
- ] })
49794
- ] });
49795
- }
49796
- function DefaultFeature({ height, color }) {
49797
- return /* @__PURE__ */ jsxs("mesh", { position: [0, height * 0.5, 0], children: [
49798
- /* @__PURE__ */ jsx("boxGeometry", { args: [0.5, height, 0.5] }),
49799
- /* @__PURE__ */ jsx("meshStandardMaterial", { color })
49800
- ] });
49801
- }
49802
- function FeatureVisual({
49803
- feature,
49804
- position,
49805
- isSelected,
49806
- onClick,
49807
- onHover
49808
- }) {
49809
- const config = DEFAULT_FEATURE_CONFIGS[feature.type] || {
49810
- color: 8947848,
49811
- height: 0.5,
49812
- scale: 1,
49813
- geometry: "default"
49814
- };
49815
- const color = feature.color ? parseInt(feature.color.replace("#", ""), 16) : config.color;
49816
- const renderGeometry = () => {
49817
- switch (config.geometry) {
49818
- case "tree":
49819
- return /* @__PURE__ */ jsx(TreeFeature, { height: config.height, color });
49820
- case "rock":
49821
- return /* @__PURE__ */ jsx(RockFeature, { height: config.height, color });
49822
- case "bush":
49823
- return /* @__PURE__ */ jsx(BushFeature, { height: config.height, color });
49824
- case "house":
49825
- return /* @__PURE__ */ jsx(HouseFeature, { height: config.height, color });
49826
- case "tower":
49827
- return /* @__PURE__ */ jsx(TowerFeature, { height: config.height, color });
49828
- case "chest":
49829
- return /* @__PURE__ */ jsx(ChestFeature, { height: config.height, color });
49830
- default:
49831
- return /* @__PURE__ */ jsx(DefaultFeature, { height: config.height, color });
49832
- }
49833
- };
49834
- return /* @__PURE__ */ jsxs(
49835
- "group",
49836
- {
49837
- position,
49838
- scale: config.scale,
49839
- onClick,
49840
- onPointerEnter: () => onHover(true),
49841
- onPointerLeave: () => onHover(false),
49842
- userData: { type: "feature", featureId: feature.id, featureType: feature.type },
49843
- children: [
49844
- isSelected && /* @__PURE__ */ jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
49845
- /* @__PURE__ */ jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
49846
- /* @__PURE__ */ jsx("meshBasicMaterial", { color: "#ffff00", transparent: true, opacity: 0.8 })
49847
- ] }),
49848
- /* @__PURE__ */ jsxs("mesh", { position: [0, 0.01, 0], rotation: [-Math.PI / 2, 0, 0], children: [
49849
- /* @__PURE__ */ jsx("circleGeometry", { args: [0.35, 16] }),
49850
- /* @__PURE__ */ jsx("meshBasicMaterial", { color: "#000000", transparent: true, opacity: 0.2 })
49851
- ] }),
49852
- renderGeometry()
49853
- ]
49854
- }
49855
- );
49856
- }
49857
- function FeatureGroup({
49858
- features,
49859
- cellSize = 1,
49860
- offsetX = 0,
49861
- offsetZ = 0,
49862
- onFeatureClick,
49863
- onFeatureHover,
49864
- selectedFeatureIds = []
49865
- }) {
49866
- return /* @__PURE__ */ jsx("group", { children: features.map((feature) => {
49867
- const x = (feature.x - offsetX) * cellSize;
49868
- const z = ((feature.z ?? feature.y ?? 0) - offsetZ) * cellSize;
49869
- const y = (feature.elevation ?? 0) * 0.1;
49870
- const isSelected = feature.id ? selectedFeatureIds.includes(feature.id) : false;
49871
- return /* @__PURE__ */ jsx(
49872
- FeatureVisual,
49873
- {
49874
- feature,
49875
- position: [x, y, z],
49876
- isSelected,
49877
- onClick: () => onFeatureClick?.(feature),
49878
- onHover: (hovered) => onFeatureHover?.(hovered ? feature : null)
49879
- },
49880
- feature.id ?? `feature-${feature.x}-${feature.y}`
49881
- );
49882
- }) });
49883
- }
49884
- function FeatureRenderer2(props) {
49885
- const insideCanvas = useContext(context) != null;
49886
- if (insideCanvas) return /* @__PURE__ */ jsx(FeatureGroup, { ...props });
49887
- return /* @__PURE__ */ jsxs(Canvas, { camera: { position: [4, 4, 6], fov: 50 }, style: { width: "100%", height: 360 }, children: [
49888
- /* @__PURE__ */ jsx("ambientLight", { intensity: 0.6 }),
49889
- /* @__PURE__ */ jsx("directionalLight", { position: [5, 8, 5], intensity: 0.8 }),
49890
- /* @__PURE__ */ jsx(FeatureGroup, { ...props })
49891
- ] });
49892
- }
49893
50030
 
49894
50031
  // components/core/templates/index.ts
49895
50032
  init_DashboardLayout();
@@ -49909,4 +50046,4 @@ init_AboutPageTemplate();
49909
50046
  // components/index.ts
49910
50047
  init_cn();
49911
50048
 
49912
- export { ALL_PRESETS, AR_BOOK_FIELDS, AboutPageTemplate, Accordion, ActionButton, ActionButtons, Card2 as ActionCard, ActionPalette, ActionTile, Alert, AnimatedCounter, AnimatedGraphic, AnimatedReveal, ArticleSection, Aside, AssetPicker, AuthLayout, Avatar, Badge, BattleBoard, BattleTemplate, BehaviorView, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, Box, BranchingLogicBuilder, Breadcrumb, BuilderBoard, Button, ButtonGroup, CTABanner, CalendarGrid, CanvasEffect, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, CaseStudyCard, CaseStudyOrganism, CastleBoard, CastleTemplate, Center, Chart, ChartLegend, Checkbox, ChoiceButton, ClassifierBoard, Coachmark, CodeBlock, CollapsibleSection, CombatLog, ComboCounter, CommunityLinks, ConditionalWrapper, ConfettiEffect, ConfirmDialog, Container, ContentRenderer, ContentSection, ControlButton, CounterTemplate, CraftingRecipe, DEFAULT_LIKERT_OPTIONS, DEFAULT_MATRIX_COLUMNS, DIAMOND_TOP_Y, DPad, DamageNumber, DashboardGrid, DashboardLayout, DataGrid, DataList, DataTable, DateRangePicker, DateRangeSelector, DayCell, DebuggerBoard, DetailPanel, Dialog, DialogueBox, DialogueBubble, Divider, DocBreadcrumb, DocPagination, DocSearch, DocSidebar, DocTOC, DocumentViewer, StateMachineView as DomStateMachineVisualizer, Drawer, DrawerSlot, EdgeDecoration, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, EmptyState, EnemyPlate, EntityDisplayEvents, ErrorBoundary, ErrorState, EventHandlerBoard, EventLog, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FeatureCard, FeatureDetailPageTemplate, FeatureGrid, FeatureGridOrganism, FeatureRenderer2 as FeatureRenderer, FileTree, FilterGroup, FilterPill, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormActions, FormField, FormLayout, FormSection, FormSectionHeader, GameAudioContext, GameAudioProvider, GameAudioToggle, GameCanvas2D, GameHud, GameMenu, GameOverScreen, GameShell, GameTemplate, GenericAppTemplate, GeometricPattern, GradientDivider, GraphCanvas, GraphView, Grid, GridPicker, HStack, Header, HealthBar, HealthPanel, HeroOrganism, HeroSection, IDENTITY_BOOK_FIELDS, Icon, IconPicker, InfiniteScrollSentinel, Input, InputGroup, InstallBox, InventoryGrid, InventoryPanel, IsometricCanvas, ItemSlot, JazariStateMachine, JsonTreeEditor, Label, LandingPageTemplate, LawReferenceTooltip, Lightbox, LikertScale, LineChart2 as LineChart, List3 as List, LoadingState, MapView, MarkdownContent, MarketingFooter, MarketingStatCard, MasterDetail, MasterDetailLayout, MatrixQuestion, MediaGallery, Menu, Meter, MiniMap, Modal, ModalSlot, ModuleCard, Navigation, NegotiatorBoard, NodeSlotEditor, NotifyListener, NumberStepper, ObjectRulePanel, OnboardingSpotlight, OptionConstraintGroup, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, Overlay, PageHeader, Pagination, PatternTile, PhysicsManager, PlatformerCanvas, Popover, PositionedCanvas, PowerupSlots, PricingCard, PricingGrid, PricingOrganism, PricingPageTemplate, ProgressBar, ProgressDots, PropertyInspector, PullQuote, PullToRefresh, QrScanner, QuestTracker, QuizBlock, Radio, RangeSlider, RelationSelect, RepeatableFormSection, ReplyTree, ResourceBar, ResourceCounter, RichBlockEditor, RuleEditor, RuntimeDebugger, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, ScaledDiagram, ScoreBoard, ScoreDisplay, SearchInput, Section, SectionHeader, Select, SequenceBar, SequencerBoard, ServiceCatalog, ShowcaseCard, ShowcaseOrganism, SidePanel, Sidebar, SignaturePad, SimpleGrid, SimulationCanvas, SimulationControls, SimulationGraph, SimulatorBoard, Skeleton, SlotContentRenderer, SocialProof, SortableList, Spacer, Sparkline, Spinner, Split, SplitPane, SplitSection, Sprite, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateArchitectBoard, StateIndicator, StateJsonView, StateMachineView, StateNode2 as StateNode, StatsGrid, StatsOrganism, StatusBar, StatusDot, StatusEffect, StepFlow, StepFlowOrganism, SvgBranch, SvgConnection, SvgFlow, SvgGrid, SvgLobe, SvgMesh, SvgMorph, SvgNode, SvgPulse, SvgRing, SvgShield, SvgStack, SwipeableRow, Switch, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, TableView, Tabs, TagCloud, TagInput, TeamCard, TeamOrganism, TerrainPalette, TextHighlight, Textarea, ThemeSelector, ThemeToggle, TimeSlotCell, Timeline, TimerDisplay, Toast, ToastSlot, Tooltip, TraitFrame, TraitSlot, TraitStateViewer, TransitionArrow, TrendIndicator, TurnIndicator, TurnPanel, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UncontrolledBattleBoard, UnitCommandBar, UploadDropZone, VStack, VariablePanel, VersionDiff, ViolationAlert, VoteStack, WaypointMarker, WizardContainer, WizardNavigation, WizardProgress, WorldMapBoard, WorldMapTemplate, XPBar, applyTemporaryEffect, calculateAttackTargets, calculateDamage, calculateValidMoves, cn, combatAnimations, combatClasses, combatEffects, createInitialGameState, createUnitAnimationState, drawSprite, generateCombatMessage, getCurrentFrame, getTileDimensions, inferDirection, isoToScreen, mapBookData, pendulum, projectileMotion, resolveFieldMap, resolveFrame, resolveSheetDirection, screenToIso, springOscillator, tickAnimationState, toCodeLanguage, transitionAnimation, useAnchorRect, useBattleState, useCamera, useGameAudio, useGameAudioContext, useImageCache, usePhysics2D, useSpriteAnimations };
50049
+ export { ALL_PRESETS, AR_BOOK_FIELDS, AboutPageTemplate, Accordion, ActionButton, ActionButtons, Card2 as ActionCard, ActionPalette, ActionTile, Alert, AnimatedCounter, AnimatedGraphic, AnimatedReveal, ArticleSection, Aside, AssetPicker, AuthLayout, Avatar, Badge, BattleBoard, BattleTemplate, BehaviorView, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, Box, BranchingLogicBuilder, Breadcrumb, BuilderBoard, Button, ButtonGroup, CTABanner, CalendarGrid, CanvasEffect, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, CaseStudyCard, CaseStudyOrganism, CastleBoard, CastleTemplate, Center, Chart, ChartLegend, Checkbox, ChoiceButton, ClassifierBoard, Coachmark, CodeBlock, CollapsibleSection, CombatLog, ComboCounter, CommunityLinks, ConditionalWrapper, ConfettiEffect, ConfirmDialog, Container, ContentRenderer, ContentSection, ControlButton, CounterTemplate, CraftingRecipe, DEFAULT_LIKERT_OPTIONS, DEFAULT_MATRIX_COLUMNS, DIAMOND_TOP_Y, DPad, DamageNumber, DashboardGrid, DashboardLayout, DataGrid, DataList, DataTable, DateRangePicker, DateRangeSelector, DayCell, DebuggerBoard, DetailPanel, Dialog, DialogueBox, DialogueBubble, Divider, DocBreadcrumb, DocPagination, DocSearch, DocSidebar, DocTOC, DocumentViewer, StateMachineView as DomStateMachineVisualizer, Drawer, DrawerSlot, EdgeDecoration, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, EmptyState, EnemyPlate, EntityDisplayEvents, ErrorBoundary, ErrorState, EventHandlerBoard, EventLog, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FeatureCard, FeatureDetailPageTemplate, FeatureGrid, FeatureGridOrganism, FileTree, FilterGroup, FilterPill, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormActions, FormField, FormLayout, FormSection, FormSectionHeader, GameAudioContext, GameAudioProvider, GameAudioToggle, GameCanvas2D, GameHud, GameMenu, GameOverScreen, GameShell, GameTemplate, GenericAppTemplate, GeometricPattern, GradientDivider, GraphCanvas, GraphView, Grid, GridPicker, HStack, Header, HealthBar, HealthPanel, HeroOrganism, HeroSection, IDENTITY_BOOK_FIELDS, Icon, IconPicker, InfiniteScrollSentinel, Input, InputGroup, InstallBox, InventoryGrid, InventoryPanel, IsometricCanvas, ItemSlot, JazariStateMachine, JsonTreeEditor, Label, LandingPageTemplate, LawReferenceTooltip, Lightbox, LikertScale, LineChart2 as LineChart, List3 as List, LoadingState, MapView, MarkdownContent, MarketingFooter, MarketingStatCard, MasterDetail, MasterDetailLayout, MatrixQuestion, MediaGallery, Menu, Meter, MiniMap, Modal, ModalSlot, ModuleCard, Navigation, NegotiatorBoard, NodeSlotEditor, NotifyListener, NumberStepper, ObjectRulePanel, OnboardingSpotlight, OptionConstraintGroup, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, Overlay, PageHeader, Pagination, PatternTile, PhysicsManager, PlatformerCanvas, Popover, PositionedCanvas, PowerupSlots, PricingCard, PricingGrid, PricingOrganism, PricingPageTemplate, ProgressBar, ProgressDots, PropertyInspector, PullQuote, PullToRefresh, QrScanner, QuestTracker, QuizBlock, Radio, RangeSlider, RelationSelect, RepeatableFormSection, ReplyTree, ResourceBar, ResourceCounter, RichBlockEditor, RuleEditor, RuntimeDebugger, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, ScaledDiagram, ScoreBoard, ScoreDisplay, SearchInput, Section, SectionHeader, Select, SequenceBar, SequencerBoard, ServiceCatalog, ShowcaseCard, ShowcaseOrganism, SidePanel, Sidebar, SignaturePad, SimpleGrid, SimulationCanvas, SimulationControls, SimulationGraph, SimulatorBoard, Skeleton, SlotContentRenderer, SocialProof, SortableList, Spacer, Sparkline, Spinner, Split, SplitPane, SplitSection, Sprite, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateArchitectBoard, StateIndicator, StateJsonView, StateMachineView, StateNode2 as StateNode, StatsGrid, StatsOrganism, StatusBar, StatusDot, StatusEffect, StepFlow, StepFlowOrganism, SvgBranch, SvgConnection, SvgFlow, SvgGrid, SvgLobe, SvgMesh, SvgMorph, SvgNode, SvgPulse, SvgRing, SvgShield, SvgStack, SwipeableRow, Switch, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, TableView, Tabs, TagCloud, TagInput, TeamCard, TeamOrganism, TerrainPalette, TextHighlight, Textarea, ThemeSelector, ThemeToggle, TimeSlotCell, Timeline, TimerDisplay, Toast, ToastSlot, Tooltip, TraitFrame, TraitSlot, TraitStateViewer, TransitionArrow, TrendIndicator, TurnIndicator, TurnPanel, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UncontrolledBattleBoard, UnitCommandBar, UploadDropZone, VStack, VariablePanel, VersionDiff, ViolationAlert, VoteStack, WaypointMarker, WizardContainer, WizardNavigation, WizardProgress, WorldMapBoard, WorldMapTemplate, XPBar, applyTemporaryEffect, calculateAttackTargets, calculateDamage, calculateValidMoves, cn, combatAnimations, combatClasses, combatEffects, createInitialGameState, createUnitAnimationState, drawSprite, generateCombatMessage, getCurrentFrame, getTileDimensions, inferDirection, isoToScreen, mapBookData, pendulum, projectileMotion, resolveFieldMap, resolveFrame, resolveSheetDirection, screenToIso, springOscillator, tickAnimationState, toCodeLanguage, transitionAnimation, useAnchorRect, useBattleState, useCamera, useGameAudio, useGameAudioContext, useImageCache, usePhysics2D, useSpriteAnimations };