@almadar/ui 5.28.5 → 5.30.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 (55) hide show
  1. package/dist/avl/index.cjs +178 -113
  2. package/dist/avl/index.js +178 -113
  3. package/dist/components/core/molecules/DocumentViewer.d.ts +0 -2
  4. package/dist/components/core/molecules/Header.d.ts +0 -4
  5. package/dist/components/core/molecules/Navigation.d.ts +0 -2
  6. package/dist/components/core/molecules/PageHeader.d.ts +0 -2
  7. package/dist/components/core/molecules/PropertyInspector.d.ts +8 -1
  8. package/dist/components/core/organisms/index.d.ts +1 -1
  9. package/dist/components/core/templates/index.d.ts +3 -0
  10. package/dist/components/game/{organisms → molecules}/GameCanvas3D.d.ts +1 -3
  11. package/dist/components/game/molecules/index.d.ts +1 -0
  12. package/dist/components/game/{organisms → molecules}/three/hooks/useGameCanvas3DEvents.d.ts +1 -1
  13. package/dist/components/game/{organisms → molecules}/three/index.cjs +29 -4
  14. package/dist/components/game/{organisms → molecules}/three/index.css +3 -3
  15. package/dist/components/game/{organisms → molecules}/three/index.js +29 -4
  16. package/dist/components/game/{organisms → molecules}/three/renderers/FeatureRenderer.d.ts +1 -1
  17. package/dist/components/game/{organisms → molecules}/three/renderers/FeatureRenderer3D.d.ts +1 -1
  18. package/dist/components/game/{organisms → molecules}/three/renderers/TileRenderer.d.ts +1 -1
  19. package/dist/components/game/{organisms → molecules}/three/renderers/UnitRenderer.d.ts +1 -1
  20. package/dist/components/game/organisms/TraitSlot.d.ts +3 -1
  21. package/dist/components/game/organisms/index.d.ts +0 -9
  22. package/dist/components/game/organisms/types/isometric.d.ts +2 -0
  23. package/dist/components/index.cjs +786 -692
  24. package/dist/components/index.js +788 -694
  25. package/dist/providers/index.cjs +178 -113
  26. package/dist/providers/index.js +178 -113
  27. package/dist/renderer/pattern-resolver.d.ts +2 -5
  28. package/dist/runtime/index.cjs +178 -113
  29. package/dist/runtime/index.js +178 -113
  30. package/package.json +9 -4
  31. package/dist/components/game/organisms/CombatLog.d.ts +0 -2
  32. package/dist/components/game/organisms/DialogueBox.d.ts +0 -2
  33. package/dist/components/game/organisms/GameHud.d.ts +0 -2
  34. package/dist/components/game/organisms/GameMenu.d.ts +0 -2
  35. package/dist/components/game/organisms/GameOverScreen.d.ts +0 -2
  36. package/dist/components/game/organisms/InventoryPanel.d.ts +0 -2
  37. package/dist/components/game/organisms/IsometricCanvas.d.ts +0 -3
  38. package/dist/components/game/organisms/PlatformerCanvas.d.ts +0 -2
  39. /package/dist/components/game/{organisms → molecules}/three/Camera3D.d.ts +0 -0
  40. /package/dist/components/game/{organisms → molecules}/three/Lighting3D.d.ts +0 -0
  41. /package/dist/components/game/{organisms → molecules}/three/Scene3D.d.ts +0 -0
  42. /package/dist/components/game/{organisms → molecules}/three/components/Canvas3DErrorBoundary.d.ts +0 -0
  43. /package/dist/components/game/{organisms → molecules}/three/components/Canvas3DLoadingState.d.ts +0 -0
  44. /package/dist/components/game/{organisms → molecules}/three/components/ModelLoader.d.ts +0 -0
  45. /package/dist/components/game/{organisms → molecules}/three/components/PhysicsObject3D.d.ts +0 -0
  46. /package/dist/components/game/{organisms → molecules}/three/components/index.d.ts +0 -0
  47. /package/dist/components/game/{organisms → molecules}/three/hooks/useAssetLoader.d.ts +0 -0
  48. /package/dist/components/game/{organisms → molecules}/three/hooks/useRaycaster.d.ts +0 -0
  49. /package/dist/components/game/{organisms → molecules}/three/hooks/useSceneGraph.d.ts +0 -0
  50. /package/dist/components/game/{organisms → molecules}/three/hooks/useThree.d.ts +0 -0
  51. /package/dist/components/game/{organisms → molecules}/three/index.d.ts +0 -0
  52. /package/dist/components/game/{organisms → molecules}/three/loaders/AssetLoader.d.ts +0 -0
  53. /package/dist/components/game/{organisms → molecules}/three/renderers/index.d.ts +0 -0
  54. /package/dist/components/game/{organisms → molecules}/three/utils/culling.d.ts +0 -0
  55. /package/dist/components/game/{organisms → molecules}/three/utils/grid3D.d.ts +0 -0
@@ -45,7 +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';
48
+ import { getPatternDefinition as getPatternDefinition$1, getComponentForPattern as getComponentForPattern$1 } from '@almadar/patterns';
49
49
  import { context, Canvas } from '@react-three/fiber';
50
50
 
51
51
  var __defProp = Object.defineProperty;
@@ -7081,11 +7081,15 @@ var init_Skeleton = __esm({
7081
7081
  function getKnownPatterns() {
7082
7082
  return Object.keys(componentMapping);
7083
7083
  }
7084
- var componentMapping;
7084
+ function getPatternDefinition(type) {
7085
+ return patternRegistry[type];
7086
+ }
7087
+ var componentMapping, patternRegistry;
7085
7088
  var init_pattern_resolver = __esm({
7086
7089
  "renderer/pattern-resolver.ts"() {
7087
7090
  createLogger("almadar:ui:pattern-resolver");
7088
7091
  componentMapping = {};
7092
+ patternRegistry = {};
7089
7093
  }
7090
7094
  });
7091
7095
 
@@ -9998,13 +10002,6 @@ var init_IsometricCanvas = __esm({
9998
10002
  }
9999
10003
  });
10000
10004
 
10001
- // components/game/organisms/IsometricCanvas.tsx
10002
- var init_IsometricCanvas2 = __esm({
10003
- "components/game/organisms/IsometricCanvas.tsx"() {
10004
- init_IsometricCanvas();
10005
- }
10006
- });
10007
-
10008
10005
  // components/game/organisms/boardEntity.ts
10009
10006
  function boardEntity(entity) {
10010
10007
  if (!entity) return void 0;
@@ -10400,7 +10397,7 @@ var init_BattleBoard = __esm({
10400
10397
  init_Button();
10401
10398
  init_Typography();
10402
10399
  init_Stack();
10403
- init_IsometricCanvas2();
10400
+ init_IsometricCanvas();
10404
10401
  init_boardEntity();
10405
10402
  init_isometric();
10406
10403
  BattleBoard.displayName = "BattleBoard";
@@ -18315,7 +18312,7 @@ var init_CastleBoard = __esm({
18315
18312
  "use client";
18316
18313
  init_cn();
18317
18314
  init_useEventBus();
18318
- init_IsometricCanvas2();
18315
+ init_IsometricCanvas();
18319
18316
  init_boardEntity();
18320
18317
  init_isometric();
18321
18318
  CastleBoard.displayName = "CastleBoard";
@@ -20717,7 +20714,84 @@ var init_DashboardLayout = __esm({
20717
20714
  NavLinkBottom.displayName = "NavLinkBottom";
20718
20715
  }
20719
20716
  });
20720
- var Menu;
20717
+ function computeMenuStyle(position, triggerRect) {
20718
+ const isTop = position.startsWith("top");
20719
+ const isRight = position.endsWith("right") || position.endsWith("end");
20720
+ if (isTop) {
20721
+ return {
20722
+ top: triggerRect.top - MENU_GAP,
20723
+ transform: "translateY(-100%)",
20724
+ ...isRight ? { right: window.innerWidth - triggerRect.right } : { left: triggerRect.left }
20725
+ };
20726
+ }
20727
+ return {
20728
+ top: triggerRect.bottom + MENU_GAP,
20729
+ ...isRight ? { right: window.innerWidth - triggerRect.right } : { left: triggerRect.left }
20730
+ };
20731
+ }
20732
+ function SubMenu({
20733
+ items,
20734
+ itemRef,
20735
+ direction,
20736
+ eventBus
20737
+ }) {
20738
+ const [rect, setRect] = useState(null);
20739
+ useEffect(() => {
20740
+ if (itemRef) {
20741
+ setRect(itemRef.getBoundingClientRect());
20742
+ }
20743
+ }, [itemRef]);
20744
+ if (!rect) return null;
20745
+ const isRtl = direction === "rtl";
20746
+ const style = {
20747
+ top: rect.top,
20748
+ ...isRtl ? { right: window.innerWidth - rect.left } : { left: rect.right }
20749
+ };
20750
+ const panel = /* @__PURE__ */ jsx(
20751
+ "div",
20752
+ {
20753
+ className: cn("fixed z-50", menuContainerStyles),
20754
+ style,
20755
+ children: items.map((item, index) => {
20756
+ const isDivider = item.id === "divider" || item.label === "divider";
20757
+ const itemId = item.id ?? `item-${item.label.toLowerCase().replace(/\s+/g, "-")}-${index}`;
20758
+ const isDanger = item.variant === "danger";
20759
+ if (isDivider) {
20760
+ return /* @__PURE__ */ jsx(Divider, { className: "my-1" }, `divider-${index}`);
20761
+ }
20762
+ return /* @__PURE__ */ jsxs(
20763
+ Box,
20764
+ {
20765
+ as: "button",
20766
+ onClick: () => {
20767
+ if (item.disabled) return;
20768
+ if (item.event) eventBus.emit(`UI:${item.event}`, { itemId, label: item.label });
20769
+ item.onClick?.();
20770
+ },
20771
+ "aria-disabled": item.disabled || void 0,
20772
+ "data-testid": item.event ? `action-${item.event}` : void 0,
20773
+ className: cn(
20774
+ "w-full flex items-center gap-3 px-4 py-2 text-start",
20775
+ "text-sm transition-colors",
20776
+ "hover:bg-muted focus:outline-none focus:bg-muted",
20777
+ "disabled:opacity-50 disabled:cursor-not-allowed",
20778
+ item.disabled && "cursor-not-allowed",
20779
+ isDanger && "text-error hover:bg-error/10"
20780
+ ),
20781
+ children: [
20782
+ 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" })),
20783
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: cn("flex-1", isDanger && "text-red-600"), children: item.label }),
20784
+ item.badge !== void 0 && /* @__PURE__ */ jsx("span", { className: "ml-auto text-xs font-medium", children: item.badge })
20785
+ ]
20786
+ },
20787
+ itemId
20788
+ );
20789
+ })
20790
+ }
20791
+ );
20792
+ return typeof document !== "undefined" ? createPortal(panel, document.body) : panel;
20793
+ }
20794
+ var MENU_GAP, menuContainerStyles, Menu;
20721
20795
  var init_Menu = __esm({
20722
20796
  "components/core/molecules/Menu.tsx"() {
20723
20797
  "use client";
@@ -20728,6 +20802,14 @@ var init_Menu = __esm({
20728
20802
  init_Badge();
20729
20803
  init_cn();
20730
20804
  init_useEventBus();
20805
+ MENU_GAP = 4;
20806
+ menuContainerStyles = cn(
20807
+ "bg-card",
20808
+ "border-[length:var(--border-width)] border-border",
20809
+ "shadow-elevation-popover",
20810
+ "rounded-sm",
20811
+ "min-w-0 sm:min-w-[200px] max-w-[calc(100vw-1rem)] py-1"
20812
+ );
20731
20813
  Menu = ({
20732
20814
  trigger,
20733
20815
  items,
@@ -20735,9 +20817,10 @@ var init_Menu = __esm({
20735
20817
  className
20736
20818
  }) => {
20737
20819
  const eventBus = useEventBus();
20738
- const { t, direction } = useTranslate();
20820
+ const { direction } = useTranslate();
20739
20821
  const [isOpen, setIsOpen] = useState(false);
20740
20822
  const [activeSubMenu, setActiveSubMenu] = useState(null);
20823
+ const [activeSubMenuRef, setActiveSubMenuRef] = useState(null);
20741
20824
  const [triggerRect, setTriggerRect] = useState(null);
20742
20825
  const triggerRef = useRef(null);
20743
20826
  const menuRef = useRef(null);
@@ -20752,13 +20835,14 @@ var init_Menu = __esm({
20752
20835
  }
20753
20836
  setIsOpen(!isOpen);
20754
20837
  setActiveSubMenu(null);
20838
+ setActiveSubMenuRef(null);
20755
20839
  };
20756
- const handleItemClick = (item) => {
20840
+ const handleItemClick = (item, itemId) => {
20757
20841
  if (item.disabled) return;
20758
20842
  if (item.subMenu && item.subMenu.length > 0) {
20759
- setActiveSubMenu(item.id ?? null);
20843
+ setActiveSubMenu(itemId);
20760
20844
  } else {
20761
- if (item.event) eventBus.emit(`UI:${item.event}`, { itemId: item.id, label: item.label });
20845
+ if (item.event) eventBus.emit(`UI:${item.event}`, { itemId, label: item.label });
20762
20846
  item.onClick?.();
20763
20847
  setIsOpen(false);
20764
20848
  }
@@ -20773,22 +20857,12 @@ var init_Menu = __esm({
20773
20857
  if (isOpen && menuRef.current && !menuRef.current.contains(e.target) && triggerRef.current && !triggerRef.current.contains(e.target)) {
20774
20858
  setIsOpen(false);
20775
20859
  setActiveSubMenu(null);
20860
+ setActiveSubMenuRef(null);
20776
20861
  }
20777
20862
  };
20778
20863
  document.addEventListener("mousedown", handleClickOutside);
20779
20864
  return () => document.removeEventListener("mousedown", handleClickOutside);
20780
20865
  }, [isOpen]);
20781
- const positionClasses = {
20782
- "top-left": "bottom-full left-0 mb-2",
20783
- "top-right": "bottom-full right-0 mb-2",
20784
- "bottom-left": "top-full left-0 mt-2",
20785
- "bottom-right": "top-full right-0 mt-2",
20786
- // Aliases for pattern compatibility
20787
- "top-start": "bottom-full left-0 mb-2",
20788
- "top-end": "bottom-full right-0 mb-2",
20789
- "bottom-start": "top-full left-0 mt-2",
20790
- "bottom-end": "top-full right-0 mt-2"
20791
- };
20792
20866
  const rtlMirror = {
20793
20867
  "top-left": "top-right",
20794
20868
  "top-right": "top-left",
@@ -20800,7 +20874,6 @@ var init_Menu = __esm({
20800
20874
  "bottom-end": "bottom-start"
20801
20875
  };
20802
20876
  const effectivePosition = direction === "rtl" ? rtlMirror[position] ?? position : position;
20803
- const subMenuSideClass = direction === "rtl" ? "right-full mr-2" : "left-full ml-2";
20804
20877
  const triggerChild = React74__default.isValidElement(trigger) ? trigger : /* @__PURE__ */ jsx(Typography, { variant: "small", as: "span", children: trigger });
20805
20878
  const triggerElement = React74__default.cloneElement(
20806
20879
  triggerChild,
@@ -20809,94 +20882,83 @@ var init_Menu = __esm({
20809
20882
  onClick: handleToggle
20810
20883
  }
20811
20884
  );
20812
- const menuContainerStyles = cn(
20813
- "bg-card",
20814
- "border-[length:var(--border-width)] border-border",
20815
- "shadow-elevation-popover",
20816
- "rounded-sm",
20817
- "min-w-0 sm:min-w-[200px] max-w-[calc(100vw-1rem)] py-1"
20818
- );
20819
- const renderMenuItem = (item, hasSubMenu, index) => {
20885
+ const renderMenuItems = (menuItems) => menuItems.map((item, index) => {
20886
+ const isDivider = item.id === "divider" || item.label === "divider";
20820
20887
  const itemId = item.id ?? `item-${item.label.toLowerCase().replace(/\s+/g, "-")}-${index}`;
20888
+ const hasSubMenu = !!(item.subMenu && item.subMenu.length > 0);
20821
20889
  const isDanger = item.variant === "danger";
20822
- return /* @__PURE__ */ jsx(
20823
- Box,
20824
- {
20825
- as: "button",
20826
- onClick: () => !item.disabled && handleItemClick({ ...item, id: itemId }),
20827
- "aria-disabled": item.disabled || void 0,
20828
- onMouseEnter: () => hasSubMenu && setActiveSubMenu(itemId),
20829
- "data-testid": item.event ? `action-${item.event}` : void 0,
20830
- className: cn(
20831
- "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
20832
- "text-sm transition-colors",
20833
- "hover:bg-muted",
20834
- "focus:outline-none focus:bg-muted",
20835
- "disabled:opacity-50 disabled:cursor-not-allowed",
20836
- item.disabled && "cursor-not-allowed",
20837
- isDanger && "text-error hover:bg-error/10"
20838
- ),
20839
- children: /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
20840
- 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" })),
20841
- /* @__PURE__ */ jsx(
20842
- Typography,
20843
- {
20844
- variant: "small",
20845
- className: cn("flex-1", isDanger && "text-red-600"),
20846
- children: item.label
20890
+ if (isDivider) {
20891
+ return /* @__PURE__ */ jsx(Divider, { className: "my-1" }, `divider-${index}`);
20892
+ }
20893
+ return /* @__PURE__ */ jsxs(Box, { children: [
20894
+ /* @__PURE__ */ jsx(
20895
+ Box,
20896
+ {
20897
+ as: "button",
20898
+ onClick: () => handleItemClick({ ...item, id: itemId }, itemId),
20899
+ "aria-disabled": item.disabled || void 0,
20900
+ onMouseEnter: (e) => {
20901
+ if (hasSubMenu) {
20902
+ setActiveSubMenu(itemId);
20903
+ setActiveSubMenuRef(e.currentTarget);
20847
20904
  }
20905
+ },
20906
+ "data-testid": item.event ? `action-${item.event}` : void 0,
20907
+ className: cn(
20908
+ "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
20909
+ "text-sm transition-colors",
20910
+ "hover:bg-muted",
20911
+ "focus:outline-none focus:bg-muted",
20912
+ "disabled:opacity-50 disabled:cursor-not-allowed",
20913
+ item.disabled && "cursor-not-allowed",
20914
+ isDanger && "text-error hover:bg-error/10"
20848
20915
  ),
20849
- item.badge !== void 0 && /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
20850
- hasSubMenu && /* @__PURE__ */ jsx(Icon, { name: direction === "rtl" ? "chevron-left" : "chevron-right", size: "sm", className: "flex-shrink-0" })
20851
- ] })
20852
- },
20853
- itemId
20854
- );
20855
- };
20856
- const renderMenuItems = (menuItems) => {
20857
- return menuItems.map((item, index) => {
20858
- const hasSubMenu = item.subMenu && item.subMenu.length > 0;
20859
- const isDivider = item.id === "divider" || item.label === "divider";
20860
- const itemId = item.id ?? `item-${item.label.toLowerCase().replace(/\s+/g, "-")}-${index}`;
20861
- if (isDivider) {
20862
- return /* @__PURE__ */ jsx(Divider, { className: "my-1" }, `divider-${index}`);
20863
- }
20864
- return /* @__PURE__ */ jsxs(Box, { children: [
20865
- renderMenuItem(item, !!hasSubMenu, index),
20866
- hasSubMenu && activeSubMenu === itemId && item.subMenu && /* @__PURE__ */ jsx(
20867
- Box,
20868
- {
20869
- className: cn(
20870
- "absolute top-0 z-50",
20871
- subMenuSideClass,
20872
- menuContainerStyles
20916
+ children: /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
20917
+ 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" })),
20918
+ /* @__PURE__ */ jsx(
20919
+ Typography,
20920
+ {
20921
+ variant: "small",
20922
+ className: cn("flex-1", isDanger && "text-red-600"),
20923
+ children: item.label
20924
+ }
20873
20925
  ),
20874
- children: renderMenuItems(item.subMenu)
20875
- }
20876
- )
20877
- ] }, itemId);
20878
- });
20879
- };
20880
- return /* @__PURE__ */ jsxs(Box, { className: "relative", children: [
20926
+ item.badge !== void 0 && /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
20927
+ hasSubMenu && /* @__PURE__ */ jsx(
20928
+ Icon,
20929
+ {
20930
+ name: direction === "rtl" ? "chevron-left" : "chevron-right",
20931
+ size: "sm",
20932
+ className: "flex-shrink-0"
20933
+ }
20934
+ )
20935
+ ] })
20936
+ }
20937
+ ),
20938
+ hasSubMenu && activeSubMenu === itemId && item.subMenu && /* @__PURE__ */ jsx(
20939
+ SubMenu,
20940
+ {
20941
+ items: item.subMenu,
20942
+ itemRef: activeSubMenuRef,
20943
+ direction,
20944
+ eventBus
20945
+ }
20946
+ )
20947
+ ] }, itemId);
20948
+ });
20949
+ const panel = isOpen && triggerRect ? /* @__PURE__ */ jsx(
20950
+ "div",
20951
+ {
20952
+ ref: menuRef,
20953
+ className: cn("fixed z-50", menuContainerStyles, className),
20954
+ style: computeMenuStyle(effectivePosition, triggerRect),
20955
+ role: "menu",
20956
+ children: renderMenuItems(items)
20957
+ }
20958
+ ) : null;
20959
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
20881
20960
  triggerElement,
20882
- isOpen && triggerRect && /* @__PURE__ */ jsx(
20883
- Box,
20884
- {
20885
- ref: menuRef,
20886
- className: cn(
20887
- "absolute z-50",
20888
- menuContainerStyles,
20889
- positionClasses[effectivePosition],
20890
- className
20891
- ),
20892
- style: {
20893
- left: effectivePosition.includes("left") ? 0 : "auto",
20894
- right: effectivePosition.includes("right") ? 0 : "auto"
20895
- },
20896
- role: "menu",
20897
- children: renderMenuItems(items)
20898
- }
20899
- )
20961
+ panel && typeof document !== "undefined" ? createPortal(panel, document.body) : panel
20900
20962
  ] });
20901
20963
  };
20902
20964
  Menu.displayName = "Menu";
@@ -22483,6 +22545,583 @@ var init_JsonTreeEditor = __esm({
22483
22545
  };
22484
22546
  }
22485
22547
  });
22548
+ var FormSection, FormLayout, FormActions;
22549
+ var init_FormSection = __esm({
22550
+ "components/core/molecules/FormSection.tsx"() {
22551
+ "use client";
22552
+ init_cn();
22553
+ init_atoms2();
22554
+ init_Box();
22555
+ init_Typography();
22556
+ init_Button();
22557
+ init_Stack();
22558
+ init_Icon();
22559
+ init_useEventBus();
22560
+ FormSection = ({
22561
+ title,
22562
+ description,
22563
+ children,
22564
+ collapsible = false,
22565
+ defaultCollapsed = false,
22566
+ card = false,
22567
+ columns = 1,
22568
+ className
22569
+ }) => {
22570
+ const [collapsed, setCollapsed] = React74__default.useState(defaultCollapsed);
22571
+ const { t } = useTranslate();
22572
+ const eventBus = useEventBus();
22573
+ const gridClass = {
22574
+ 1: "grid-cols-1",
22575
+ 2: "grid-cols-1 md:grid-cols-2",
22576
+ 3: "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
22577
+ }[columns];
22578
+ React74__default.useCallback(() => {
22579
+ if (collapsible) {
22580
+ setCollapsed((prev) => !prev);
22581
+ eventBus.emit("UI:TOGGLE_COLLAPSE", { collapsed: !collapsed });
22582
+ }
22583
+ }, [collapsible, collapsed, eventBus]);
22584
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
22585
+ (title || description) && /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "mb-4", children: [
22586
+ title && /* @__PURE__ */ jsxs(
22587
+ HStack,
22588
+ {
22589
+ justify: "between",
22590
+ align: "center",
22591
+ className: cn(collapsible && "cursor-pointer"),
22592
+ action: collapsible ? "TOGGLE_COLLAPSE" : void 0,
22593
+ children: [
22594
+ /* @__PURE__ */ jsx(Typography, { variant: "h3", weight: "semibold", children: title }),
22595
+ collapsible && /* @__PURE__ */ jsx(
22596
+ Button,
22597
+ {
22598
+ variant: "ghost",
22599
+ size: "sm",
22600
+ action: "TOGGLE_COLLAPSE",
22601
+ children: /* @__PURE__ */ jsx(
22602
+ Icon,
22603
+ {
22604
+ icon: ChevronDown,
22605
+ size: "sm",
22606
+ className: cn(
22607
+ "text-muted-foreground transition-transform",
22608
+ collapsed && "rotate-180"
22609
+ )
22610
+ }
22611
+ )
22612
+ }
22613
+ )
22614
+ ]
22615
+ }
22616
+ ),
22617
+ description && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: description })
22618
+ ] }),
22619
+ (!collapsible || !collapsed) && /* @__PURE__ */ jsx(Box, { className: cn("grid gap-4", gridClass), children })
22620
+ ] });
22621
+ if (card) {
22622
+ return /* @__PURE__ */ jsx(Card, { className: cn("p-6", className), children: content });
22623
+ }
22624
+ return /* @__PURE__ */ jsx(Box, { className, children: content });
22625
+ };
22626
+ FormSection.displayName = "FormSection";
22627
+ FormLayout = ({
22628
+ children,
22629
+ dividers = true,
22630
+ className
22631
+ }) => {
22632
+ return /* @__PURE__ */ jsx(
22633
+ VStack,
22634
+ {
22635
+ gap: "lg",
22636
+ className: cn(
22637
+ dividers && "[&>*+*]:pt-8 [&>*+*]:border-t [&>*+*]:border-border",
22638
+ className
22639
+ ),
22640
+ children
22641
+ }
22642
+ );
22643
+ };
22644
+ FormLayout.displayName = "FormLayout";
22645
+ FormActions = ({
22646
+ children,
22647
+ sticky = false,
22648
+ align = "right",
22649
+ className
22650
+ }) => {
22651
+ const alignClass2 = {
22652
+ left: "justify-start",
22653
+ right: "justify-end",
22654
+ between: "justify-between",
22655
+ center: "justify-center"
22656
+ }[align];
22657
+ return /* @__PURE__ */ jsx(
22658
+ HStack,
22659
+ {
22660
+ gap: "sm",
22661
+ align: "center",
22662
+ className: cn(
22663
+ "pt-6 border-t border-border",
22664
+ alignClass2,
22665
+ sticky && "sticky bottom-0 bg-card py-4 -mx-6 px-6 shadow-[0_-4px_6px_-1px_rgb(0,0,0,0.05)]",
22666
+ className
22667
+ ),
22668
+ children
22669
+ }
22670
+ );
22671
+ };
22672
+ FormActions.displayName = "FormActions";
22673
+ }
22674
+ });
22675
+ var ALL_CATEGORY, GridPicker;
22676
+ var init_GridPicker = __esm({
22677
+ "components/core/molecules/GridPicker.tsx"() {
22678
+ "use client";
22679
+ init_cn();
22680
+ init_Input();
22681
+ init_Badge();
22682
+ init_Stack();
22683
+ ALL_CATEGORY = "__all__";
22684
+ GridPicker = ({
22685
+ items,
22686
+ value,
22687
+ onChange,
22688
+ categories,
22689
+ searchPlaceholder,
22690
+ renderThumbnail,
22691
+ cellSize = 32,
22692
+ className
22693
+ }) => {
22694
+ const [search, setSearch] = useState("");
22695
+ const [activeCategory, setActiveCategory] = useState(ALL_CATEGORY);
22696
+ const gridRef = useRef(null);
22697
+ const categoryChips = useMemo(() => {
22698
+ if (categories !== void 0) return categories;
22699
+ const seen = [];
22700
+ for (const item of items) {
22701
+ if (!seen.includes(item.category)) seen.push(item.category);
22702
+ }
22703
+ return seen;
22704
+ }, [categories, items]);
22705
+ const filtered = useMemo(() => {
22706
+ const needle = search.trim().toLowerCase();
22707
+ return items.filter((item) => {
22708
+ const matchesCategory = activeCategory === ALL_CATEGORY || item.category === activeCategory;
22709
+ const matchesSearch = needle === "" || item.label.toLowerCase().includes(needle);
22710
+ return matchesCategory && matchesSearch;
22711
+ });
22712
+ }, [items, search, activeCategory]);
22713
+ const select = useCallback(
22714
+ (item) => {
22715
+ onChange(item.id);
22716
+ },
22717
+ [onChange]
22718
+ );
22719
+ const handleKeyDown = useCallback(
22720
+ (e, index) => {
22721
+ const cells = gridRef.current?.querySelectorAll(
22722
+ "[data-gridpicker-cell]"
22723
+ );
22724
+ if (cells === void 0 || cells.length === 0) return;
22725
+ const columns = (() => {
22726
+ const grid = gridRef.current;
22727
+ if (grid === null) return 1;
22728
+ const style = window.getComputedStyle(grid);
22729
+ const cols = style.gridTemplateColumns.split(" ").filter(Boolean).length;
22730
+ return cols > 0 ? cols : 1;
22731
+ })();
22732
+ let next = -1;
22733
+ if (e.key === "ArrowRight") next = index + 1;
22734
+ else if (e.key === "ArrowLeft") next = index - 1;
22735
+ else if (e.key === "ArrowDown") next = index + columns;
22736
+ else if (e.key === "ArrowUp") next = index - columns;
22737
+ else if (e.key === "Enter" || e.key === " ") {
22738
+ e.preventDefault();
22739
+ select(filtered[index]);
22740
+ return;
22741
+ } else {
22742
+ return;
22743
+ }
22744
+ e.preventDefault();
22745
+ if (next >= 0 && next < cells.length) {
22746
+ cells[next].focus();
22747
+ }
22748
+ },
22749
+ [filtered, select]
22750
+ );
22751
+ return /* @__PURE__ */ jsxs(VStack, { gap: "sm", className: cn("w-full", className), children: [
22752
+ /* @__PURE__ */ jsx(
22753
+ Input,
22754
+ {
22755
+ type: "search",
22756
+ icon: "search",
22757
+ value: search,
22758
+ placeholder: searchPlaceholder,
22759
+ clearable: true,
22760
+ onClear: () => setSearch(""),
22761
+ onChange: (e) => setSearch(e.target.value)
22762
+ }
22763
+ ),
22764
+ categoryChips.length > 0 && /* @__PURE__ */ jsxs(HStack, { gap: "xs", wrap: true, children: [
22765
+ /* @__PURE__ */ jsx(
22766
+ Badge,
22767
+ {
22768
+ variant: activeCategory === ALL_CATEGORY ? "primary" : "neutral",
22769
+ size: "sm",
22770
+ role: "button",
22771
+ tabIndex: 0,
22772
+ "aria-pressed": activeCategory === ALL_CATEGORY,
22773
+ className: "cursor-pointer",
22774
+ onClick: () => setActiveCategory(ALL_CATEGORY),
22775
+ onKeyDown: (e) => {
22776
+ if (e.key === "Enter" || e.key === " ") {
22777
+ e.preventDefault();
22778
+ setActiveCategory(ALL_CATEGORY);
22779
+ }
22780
+ },
22781
+ children: "All"
22782
+ }
22783
+ ),
22784
+ categoryChips.map((category) => /* @__PURE__ */ jsx(
22785
+ Badge,
22786
+ {
22787
+ variant: activeCategory === category ? "primary" : "neutral",
22788
+ size: "sm",
22789
+ role: "button",
22790
+ tabIndex: 0,
22791
+ "aria-pressed": activeCategory === category,
22792
+ className: "cursor-pointer",
22793
+ onClick: () => setActiveCategory(category),
22794
+ onKeyDown: (e) => {
22795
+ if (e.key === "Enter" || e.key === " ") {
22796
+ e.preventDefault();
22797
+ setActiveCategory(category);
22798
+ }
22799
+ },
22800
+ children: category
22801
+ },
22802
+ category
22803
+ ))
22804
+ ] }),
22805
+ /* @__PURE__ */ jsx(
22806
+ "div",
22807
+ {
22808
+ ref: gridRef,
22809
+ role: "listbox",
22810
+ className: "grid gap-1 overflow-y-auto max-h-64 p-1",
22811
+ style: {
22812
+ gridTemplateColumns: `repeat(auto-fill, minmax(${cellSize}px, 1fr))`
22813
+ },
22814
+ children: filtered.map((item, index) => {
22815
+ const selected = item.id === value;
22816
+ return /* @__PURE__ */ jsx(
22817
+ "button",
22818
+ {
22819
+ type: "button",
22820
+ role: "option",
22821
+ "aria-selected": selected,
22822
+ "aria-label": item.label,
22823
+ title: item.label,
22824
+ "data-gridpicker-cell": true,
22825
+ tabIndex: selected || value === void 0 && index === 0 ? 0 : -1,
22826
+ onClick: () => select(item),
22827
+ onKeyDown: (e) => handleKeyDown(e, index),
22828
+ className: cn(
22829
+ "flex items-center justify-center rounded-sm",
22830
+ "transition-colors hover:bg-muted",
22831
+ "focus:outline-none focus:ring-1 focus:ring-ring",
22832
+ selected && "bg-primary/10 ring-1 ring-primary"
22833
+ ),
22834
+ style: { width: cellSize, height: cellSize },
22835
+ children: renderThumbnail(item)
22836
+ },
22837
+ item.id
22838
+ );
22839
+ })
22840
+ }
22841
+ )
22842
+ ] });
22843
+ };
22844
+ GridPicker.displayName = "GridPicker";
22845
+ }
22846
+ });
22847
+ function pascalToKebab(name) {
22848
+ return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z])([A-Z][a-z])/g, "$1-$2").toLowerCase();
22849
+ }
22850
+ function kebabToPascal3(name) {
22851
+ return name.split("-").map((part) => /^\d+$/.test(part) ? part : part.charAt(0).toUpperCase() + part.slice(1)).join("");
22852
+ }
22853
+ var ICON_ITEMS, IconPicker;
22854
+ var init_IconPicker = __esm({
22855
+ "components/core/molecules/IconPicker.tsx"() {
22856
+ "use client";
22857
+ init_Icon();
22858
+ init_GridPicker();
22859
+ ICON_ITEMS = (() => {
22860
+ const items = [];
22861
+ for (const [exportName, candidate] of Object.entries(LucideIcons2)) {
22862
+ if (!/^[A-Z]/.test(exportName)) continue;
22863
+ if (exportName.endsWith("Icon")) continue;
22864
+ if (exportName.startsWith("Lucide")) continue;
22865
+ const isComponent = candidate !== null && (typeof candidate === "object" || typeof candidate === "function") && "$$typeof" in candidate;
22866
+ if (!isComponent) continue;
22867
+ const kebab = pascalToKebab(exportName);
22868
+ if (kebabToPascal3(kebab) !== exportName) continue;
22869
+ items.push({ id: kebab, label: kebab, category: "icons" });
22870
+ }
22871
+ return items;
22872
+ })();
22873
+ IconPicker = ({ value, onChange, className }) => {
22874
+ const items = useMemo(() => ICON_ITEMS, []);
22875
+ return /* @__PURE__ */ jsx(
22876
+ GridPicker,
22877
+ {
22878
+ items,
22879
+ value,
22880
+ onChange,
22881
+ searchPlaceholder: "Search icons\u2026",
22882
+ renderThumbnail: (it) => /* @__PURE__ */ jsx(Icon, { name: it.id }),
22883
+ cellSize: 32,
22884
+ className
22885
+ }
22886
+ );
22887
+ };
22888
+ IconPicker.displayName = "IconPicker";
22889
+ }
22890
+ });
22891
+ function iconForKind(kind) {
22892
+ if (kind === "audio") return "music";
22893
+ if (kind === "model") return "box";
22894
+ return "file";
22895
+ }
22896
+ var THUMB_PX, IMAGE_KINDS, AssetPicker;
22897
+ var init_AssetPicker = __esm({
22898
+ "components/core/molecules/AssetPicker.tsx"() {
22899
+ "use client";
22900
+ init_GridPicker();
22901
+ init_Icon();
22902
+ THUMB_PX = 32;
22903
+ IMAGE_KINDS = /* @__PURE__ */ new Set([
22904
+ "image",
22905
+ "spritesheet",
22906
+ "scene",
22907
+ "portrait"
22908
+ ]);
22909
+ AssetPicker = ({
22910
+ assets,
22911
+ value,
22912
+ onChange,
22913
+ className
22914
+ }) => {
22915
+ const byUrl = useMemo(() => {
22916
+ const map = /* @__PURE__ */ new Map();
22917
+ for (const entry of assets) map.set(entry.url, entry);
22918
+ return map;
22919
+ }, [assets]);
22920
+ const items = useMemo(
22921
+ () => assets.map((entry) => ({
22922
+ id: entry.url,
22923
+ label: entry.name,
22924
+ category: entry.category
22925
+ })),
22926
+ [assets]
22927
+ );
22928
+ const categories = useMemo(() => {
22929
+ const seen = [];
22930
+ for (const entry of assets) {
22931
+ if (!seen.includes(entry.category)) seen.push(entry.category);
22932
+ }
22933
+ return seen;
22934
+ }, [assets]);
22935
+ const renderThumbnail = useCallback(
22936
+ (item) => {
22937
+ const entry = byUrl.get(item.id);
22938
+ if (entry === void 0) return null;
22939
+ if (IMAGE_KINDS.has(entry.kind)) {
22940
+ return /* @__PURE__ */ jsx(
22941
+ "img",
22942
+ {
22943
+ src: entry.thumbnailUrl ?? entry.url,
22944
+ alt: entry.name,
22945
+ loading: "lazy",
22946
+ width: THUMB_PX,
22947
+ height: THUMB_PX,
22948
+ style: { width: THUMB_PX, height: THUMB_PX, objectFit: "cover" }
22949
+ }
22950
+ );
22951
+ }
22952
+ return /* @__PURE__ */ jsx(Icon, { name: iconForKind(entry.kind), size: "sm" });
22953
+ },
22954
+ [byUrl]
22955
+ );
22956
+ return /* @__PURE__ */ jsx(
22957
+ GridPicker,
22958
+ {
22959
+ items,
22960
+ value,
22961
+ onChange,
22962
+ categories,
22963
+ renderThumbnail,
22964
+ cellSize: THUMB_PX,
22965
+ className
22966
+ }
22967
+ );
22968
+ };
22969
+ AssetPicker.displayName = "AssetPicker";
22970
+ }
22971
+ });
22972
+ function currentValue(decl, override) {
22973
+ return override !== void 0 ? override : decl.default;
22974
+ }
22975
+ function TextLikeControl({
22976
+ field,
22977
+ numeric,
22978
+ value,
22979
+ onCommit
22980
+ }) {
22981
+ const initial = value === void 0 || value === null ? "" : String(value);
22982
+ const [draft, setDraft] = React74__default.useState(initial);
22983
+ React74__default.useEffect(() => setDraft(initial), [initial]);
22984
+ const commit = () => {
22985
+ if (numeric) {
22986
+ const n = draft.trim() === "" ? 0 : Number(draft);
22987
+ onCommit(field, Number.isNaN(n) ? 0 : n);
22988
+ } else {
22989
+ onCommit(field, draft);
22990
+ }
22991
+ };
22992
+ return /* @__PURE__ */ jsx(
22993
+ Input,
22994
+ {
22995
+ inputType: numeric ? "number" : "text",
22996
+ value: draft,
22997
+ onChange: (e) => setDraft(e.target.value),
22998
+ onBlur: commit,
22999
+ onKeyDown: (e) => {
23000
+ if (e.key === "Enter") commit();
23001
+ }
23002
+ }
23003
+ );
23004
+ }
23005
+ function isTraitConfigObject(v) {
23006
+ return v !== null && v !== void 0 && typeof v === "object" && !Array.isArray(v);
23007
+ }
23008
+ function FieldControl({
23009
+ name,
23010
+ decl,
23011
+ value,
23012
+ onChange,
23013
+ assets
23014
+ }) {
23015
+ let control;
23016
+ const stringValue = typeof value === "string" ? value : void 0;
23017
+ const effectiveValue = value ?? decl.default;
23018
+ if (decl.type === "icon") {
23019
+ control = /* @__PURE__ */ jsx(IconPicker, { value: stringValue, onChange: (icon) => onChange(name, icon) });
23020
+ } else if (decl.type === "asset") {
23021
+ control = /* @__PURE__ */ jsx(
23022
+ AssetPicker,
23023
+ {
23024
+ assets: assets ?? [],
23025
+ value: stringValue,
23026
+ onChange: (url) => onChange(name, url)
23027
+ }
23028
+ );
23029
+ } else if (decl.type === "boolean") {
23030
+ control = /* @__PURE__ */ jsx(Switch, { checked: value === true, onChange: (c) => onChange(name, c) });
23031
+ } else if (decl.type === "string" && decl.values !== void 0 && decl.values.length > 0) {
23032
+ control = /* @__PURE__ */ jsx(
23033
+ Select,
23034
+ {
23035
+ options: decl.values.map((v) => ({ value: v, label: v })),
23036
+ value: typeof value === "string" ? value : "",
23037
+ onChange: (e) => onChange(name, e.target.value)
23038
+ }
23039
+ );
23040
+ } else if (decl.type === "number") {
23041
+ control = /* @__PURE__ */ jsx(TextLikeControl, { field: name, numeric: true, value, onCommit: onChange });
23042
+ } else if (decl.type === "string") {
23043
+ control = /* @__PURE__ */ jsx(TextLikeControl, { field: name, numeric: false, value, onCommit: onChange });
23044
+ } else if (decl.type === "node") {
23045
+ control = /* @__PURE__ */ jsx(NodeSlotEditor, { value: effectiveValue, onChange: (next) => onChange(name, next) });
23046
+ } else if (decl.type.startsWith("[") || Array.isArray(effectiveValue)) {
23047
+ const arr = Array.isArray(effectiveValue) ? effectiveValue : [];
23048
+ control = /* @__PURE__ */ jsx(JsonTreeEditor, { value: arr, onChange: (next) => onChange(name, next) });
23049
+ } else if (decl.type === "object" || decl.type.startsWith("Map ") || !SCALAR_TYPES.has(decl.type) && isTraitConfigObject(effectiveValue)) {
23050
+ const obj = isTraitConfigObject(effectiveValue) ? effectiveValue : {};
23051
+ control = /* @__PURE__ */ jsx(JsonTreeEditor, { value: obj, onChange: (next) => onChange(name, next) });
23052
+ } else {
23053
+ control = /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "muted", children: [
23054
+ decl.type,
23055
+ " \u2014 edit in source"
23056
+ ] });
23057
+ }
23058
+ return /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
23059
+ /* @__PURE__ */ jsx(Typography, { variant: "label", children: decl.label ?? name }),
23060
+ control,
23061
+ decl.description !== void 0 && decl.description !== "" && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: decl.description })
23062
+ ] });
23063
+ }
23064
+ var TIER_ORDER, SCALAR_TYPES, PropertyInspector;
23065
+ var init_PropertyInspector = __esm({
23066
+ "components/core/molecules/PropertyInspector.tsx"() {
23067
+ "use client";
23068
+ init_cn();
23069
+ init_Stack();
23070
+ init_Typography();
23071
+ init_Button();
23072
+ init_Switch();
23073
+ init_Select();
23074
+ init_Input();
23075
+ init_FormSection();
23076
+ init_IconPicker();
23077
+ init_AssetPicker();
23078
+ init_JsonTreeEditor();
23079
+ init_NodeSlotEditor();
23080
+ TIER_ORDER = ["presentation", "domain", "policy", "infra", "internal"];
23081
+ SCALAR_TYPES = /* @__PURE__ */ new Set(["string", "number", "boolean", "icon", "asset"]);
23082
+ PropertyInspector = ({
23083
+ config,
23084
+ values,
23085
+ onChange,
23086
+ onReset,
23087
+ title,
23088
+ className,
23089
+ assets
23090
+ }) => {
23091
+ const fields = Object.entries(config);
23092
+ const byTier = /* @__PURE__ */ new Map();
23093
+ for (const [name, decl] of fields) {
23094
+ const tier = decl.tier ?? "presentation";
23095
+ const arr = byTier.get(tier) ?? [];
23096
+ arr.push([name, decl]);
23097
+ byTier.set(tier, arr);
23098
+ }
23099
+ const tiers = [...byTier.keys()].sort((a, b) => {
23100
+ const ia = TIER_ORDER.indexOf(a);
23101
+ const ib = TIER_ORDER.indexOf(b);
23102
+ return (ia === -1 ? 99 : ia) - (ib === -1 ? 99 : ib);
23103
+ });
23104
+ return /* @__PURE__ */ jsxs(VStack, { gap: "sm", className: cn("w-full", className), children: [
23105
+ /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", children: [
23106
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", weight: "bold", children: title ?? "Config" }),
23107
+ onReset !== void 0 && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", icon: "rotate-ccw", label: "Reset", onClick: onReset })
23108
+ ] }),
23109
+ fields.length === 0 && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: "No configurable properties." }),
23110
+ 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(
23111
+ FieldControl,
23112
+ {
23113
+ name,
23114
+ decl,
23115
+ value: currentValue(decl, values?.[name]),
23116
+ onChange,
23117
+ assets
23118
+ },
23119
+ name
23120
+ )) }) }, tier))
23121
+ ] });
23122
+ };
23123
+ }
23124
+ });
22486
23125
  function normalize(value) {
22487
23126
  const wasArray = Array.isArray(value);
22488
23127
  const pattern = wasArray ? value[0] : value;
@@ -22501,6 +23140,7 @@ var init_NodeSlotEditor = __esm({
22501
23140
  init_Select();
22502
23141
  init_Typography();
22503
23142
  init_JsonTreeEditor();
23143
+ init_PropertyInspector();
22504
23144
  init_pattern_resolver();
22505
23145
  init_cn();
22506
23146
  isObj2 = (v) => v !== null && v !== void 0 && typeof v === "object" && !Array.isArray(v);
@@ -22525,6 +23165,21 @@ var init_NodeSlotEditor = __esm({
22525
23165
  const pattern = { type: nextType, ...nextProps };
22526
23166
  onChange(wasArray || value === void 0 ? [pattern] : pattern);
22527
23167
  };
23168
+ const schemaEntries = React74__default.useMemo(() => {
23169
+ if (!type) return [];
23170
+ const def = getPatternDefinition(type);
23171
+ if (!def?.propsSchema) return [];
23172
+ return Object.entries(def.propsSchema).map(([propName, propDef]) => {
23173
+ const decl = {
23174
+ type: propDef.types?.[0] ?? "string",
23175
+ description: propDef.description,
23176
+ label: propName,
23177
+ ...propDef.enumValues && propDef.enumValues.length > 0 ? { values: propDef.enumValues } : {}
23178
+ };
23179
+ return [propName, decl];
23180
+ });
23181
+ }, [type]);
23182
+ const hasSchema = schemaEntries.length > 0;
22528
23183
  return /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: cn("w-full", className), children: [
22529
23184
  /* @__PURE__ */ jsx(
22530
23185
  Select,
@@ -22536,7 +23191,16 @@ var init_NodeSlotEditor = __esm({
22536
23191
  ),
22537
23192
  type !== "" && /* @__PURE__ */ jsxs(VStack, { gap: "none", className: "pl-1", children: [
22538
23193
  /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: "props" }),
22539
- /* @__PURE__ */ jsx(JsonTreeEditor, { value: props, onChange: (next) => emit(type, isObj2(next) ? next : {}) })
23194
+ hasSchema ? /* @__PURE__ */ jsx(VStack, { gap: "xs", children: schemaEntries.map(([propName, decl]) => /* @__PURE__ */ jsx(
23195
+ FieldControl,
23196
+ {
23197
+ name: propName,
23198
+ decl,
23199
+ value: props[propName],
23200
+ onChange: (field, val) => emit(type, { ...props, [field]: val })
23201
+ },
23202
+ propName
23203
+ )) }) : /* @__PURE__ */ jsx(JsonTreeEditor, { value: props, onChange: (next) => emit(type, isObj2(next) ? next : {}) })
22540
23204
  ] })
22541
23205
  ] });
22542
23206
  };
@@ -25119,303 +25783,6 @@ var init_FlipCard = __esm({
25119
25783
  FlipCard.displayName = "FlipCard";
25120
25784
  }
25121
25785
  });
25122
- var ALL_CATEGORY, GridPicker;
25123
- var init_GridPicker = __esm({
25124
- "components/core/molecules/GridPicker.tsx"() {
25125
- "use client";
25126
- init_cn();
25127
- init_Input();
25128
- init_Badge();
25129
- init_Stack();
25130
- ALL_CATEGORY = "__all__";
25131
- GridPicker = ({
25132
- items,
25133
- value,
25134
- onChange,
25135
- categories,
25136
- searchPlaceholder,
25137
- renderThumbnail,
25138
- cellSize = 32,
25139
- className
25140
- }) => {
25141
- const [search, setSearch] = useState("");
25142
- const [activeCategory, setActiveCategory] = useState(ALL_CATEGORY);
25143
- const gridRef = useRef(null);
25144
- const categoryChips = useMemo(() => {
25145
- if (categories !== void 0) return categories;
25146
- const seen = [];
25147
- for (const item of items) {
25148
- if (!seen.includes(item.category)) seen.push(item.category);
25149
- }
25150
- return seen;
25151
- }, [categories, items]);
25152
- const filtered = useMemo(() => {
25153
- const needle = search.trim().toLowerCase();
25154
- return items.filter((item) => {
25155
- const matchesCategory = activeCategory === ALL_CATEGORY || item.category === activeCategory;
25156
- const matchesSearch = needle === "" || item.label.toLowerCase().includes(needle);
25157
- return matchesCategory && matchesSearch;
25158
- });
25159
- }, [items, search, activeCategory]);
25160
- const select = useCallback(
25161
- (item) => {
25162
- onChange(item.id);
25163
- },
25164
- [onChange]
25165
- );
25166
- const handleKeyDown = useCallback(
25167
- (e, index) => {
25168
- const cells = gridRef.current?.querySelectorAll(
25169
- "[data-gridpicker-cell]"
25170
- );
25171
- if (cells === void 0 || cells.length === 0) return;
25172
- const columns = (() => {
25173
- const grid = gridRef.current;
25174
- if (grid === null) return 1;
25175
- const style = window.getComputedStyle(grid);
25176
- const cols = style.gridTemplateColumns.split(" ").filter(Boolean).length;
25177
- return cols > 0 ? cols : 1;
25178
- })();
25179
- let next = -1;
25180
- if (e.key === "ArrowRight") next = index + 1;
25181
- else if (e.key === "ArrowLeft") next = index - 1;
25182
- else if (e.key === "ArrowDown") next = index + columns;
25183
- else if (e.key === "ArrowUp") next = index - columns;
25184
- else if (e.key === "Enter" || e.key === " ") {
25185
- e.preventDefault();
25186
- select(filtered[index]);
25187
- return;
25188
- } else {
25189
- return;
25190
- }
25191
- e.preventDefault();
25192
- if (next >= 0 && next < cells.length) {
25193
- cells[next].focus();
25194
- }
25195
- },
25196
- [filtered, select]
25197
- );
25198
- return /* @__PURE__ */ jsxs(VStack, { gap: "sm", className: cn("w-full", className), children: [
25199
- /* @__PURE__ */ jsx(
25200
- Input,
25201
- {
25202
- type: "search",
25203
- icon: "search",
25204
- value: search,
25205
- placeholder: searchPlaceholder,
25206
- clearable: true,
25207
- onClear: () => setSearch(""),
25208
- onChange: (e) => setSearch(e.target.value)
25209
- }
25210
- ),
25211
- categoryChips.length > 0 && /* @__PURE__ */ jsxs(HStack, { gap: "xs", wrap: true, children: [
25212
- /* @__PURE__ */ jsx(
25213
- Badge,
25214
- {
25215
- variant: activeCategory === ALL_CATEGORY ? "primary" : "neutral",
25216
- size: "sm",
25217
- role: "button",
25218
- tabIndex: 0,
25219
- "aria-pressed": activeCategory === ALL_CATEGORY,
25220
- className: "cursor-pointer",
25221
- onClick: () => setActiveCategory(ALL_CATEGORY),
25222
- onKeyDown: (e) => {
25223
- if (e.key === "Enter" || e.key === " ") {
25224
- e.preventDefault();
25225
- setActiveCategory(ALL_CATEGORY);
25226
- }
25227
- },
25228
- children: "All"
25229
- }
25230
- ),
25231
- categoryChips.map((category) => /* @__PURE__ */ jsx(
25232
- Badge,
25233
- {
25234
- variant: activeCategory === category ? "primary" : "neutral",
25235
- size: "sm",
25236
- role: "button",
25237
- tabIndex: 0,
25238
- "aria-pressed": activeCategory === category,
25239
- className: "cursor-pointer",
25240
- onClick: () => setActiveCategory(category),
25241
- onKeyDown: (e) => {
25242
- if (e.key === "Enter" || e.key === " ") {
25243
- e.preventDefault();
25244
- setActiveCategory(category);
25245
- }
25246
- },
25247
- children: category
25248
- },
25249
- category
25250
- ))
25251
- ] }),
25252
- /* @__PURE__ */ jsx(
25253
- "div",
25254
- {
25255
- ref: gridRef,
25256
- role: "listbox",
25257
- className: "grid gap-1 overflow-y-auto max-h-64 p-1",
25258
- style: {
25259
- gridTemplateColumns: `repeat(auto-fill, minmax(${cellSize}px, 1fr))`
25260
- },
25261
- children: filtered.map((item, index) => {
25262
- const selected = item.id === value;
25263
- return /* @__PURE__ */ jsx(
25264
- "button",
25265
- {
25266
- type: "button",
25267
- role: "option",
25268
- "aria-selected": selected,
25269
- "aria-label": item.label,
25270
- title: item.label,
25271
- "data-gridpicker-cell": true,
25272
- tabIndex: selected || value === void 0 && index === 0 ? 0 : -1,
25273
- onClick: () => select(item),
25274
- onKeyDown: (e) => handleKeyDown(e, index),
25275
- className: cn(
25276
- "flex items-center justify-center rounded-sm",
25277
- "transition-colors hover:bg-muted",
25278
- "focus:outline-none focus:ring-1 focus:ring-ring",
25279
- selected && "bg-primary/10 ring-1 ring-primary"
25280
- ),
25281
- style: { width: cellSize, height: cellSize },
25282
- children: renderThumbnail(item)
25283
- },
25284
- item.id
25285
- );
25286
- })
25287
- }
25288
- )
25289
- ] });
25290
- };
25291
- GridPicker.displayName = "GridPicker";
25292
- }
25293
- });
25294
- function iconForKind(kind) {
25295
- if (kind === "audio") return "music";
25296
- if (kind === "model") return "box";
25297
- return "file";
25298
- }
25299
- var THUMB_PX, IMAGE_KINDS, AssetPicker;
25300
- var init_AssetPicker = __esm({
25301
- "components/core/molecules/AssetPicker.tsx"() {
25302
- "use client";
25303
- init_GridPicker();
25304
- init_Icon();
25305
- THUMB_PX = 32;
25306
- IMAGE_KINDS = /* @__PURE__ */ new Set([
25307
- "image",
25308
- "spritesheet",
25309
- "scene",
25310
- "portrait"
25311
- ]);
25312
- AssetPicker = ({
25313
- assets,
25314
- value,
25315
- onChange,
25316
- className
25317
- }) => {
25318
- const byUrl = useMemo(() => {
25319
- const map = /* @__PURE__ */ new Map();
25320
- for (const entry of assets) map.set(entry.url, entry);
25321
- return map;
25322
- }, [assets]);
25323
- const items = useMemo(
25324
- () => assets.map((entry) => ({
25325
- id: entry.url,
25326
- label: entry.name,
25327
- category: entry.category
25328
- })),
25329
- [assets]
25330
- );
25331
- const categories = useMemo(() => {
25332
- const seen = [];
25333
- for (const entry of assets) {
25334
- if (!seen.includes(entry.category)) seen.push(entry.category);
25335
- }
25336
- return seen;
25337
- }, [assets]);
25338
- const renderThumbnail = useCallback(
25339
- (item) => {
25340
- const entry = byUrl.get(item.id);
25341
- if (entry === void 0) return null;
25342
- if (IMAGE_KINDS.has(entry.kind)) {
25343
- return /* @__PURE__ */ jsx(
25344
- "img",
25345
- {
25346
- src: entry.thumbnailUrl ?? entry.url,
25347
- alt: entry.name,
25348
- loading: "lazy",
25349
- width: THUMB_PX,
25350
- height: THUMB_PX,
25351
- style: { width: THUMB_PX, height: THUMB_PX, objectFit: "cover" }
25352
- }
25353
- );
25354
- }
25355
- return /* @__PURE__ */ jsx(Icon, { name: iconForKind(entry.kind), size: "sm" });
25356
- },
25357
- [byUrl]
25358
- );
25359
- return /* @__PURE__ */ jsx(
25360
- GridPicker,
25361
- {
25362
- items,
25363
- value,
25364
- onChange,
25365
- categories,
25366
- renderThumbnail,
25367
- cellSize: THUMB_PX,
25368
- className
25369
- }
25370
- );
25371
- };
25372
- AssetPicker.displayName = "AssetPicker";
25373
- }
25374
- });
25375
- function pascalToKebab(name) {
25376
- return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z])([A-Z][a-z])/g, "$1-$2").toLowerCase();
25377
- }
25378
- function kebabToPascal3(name) {
25379
- return name.split("-").map((part) => /^\d+$/.test(part) ? part : part.charAt(0).toUpperCase() + part.slice(1)).join("");
25380
- }
25381
- var ICON_ITEMS, IconPicker;
25382
- var init_IconPicker = __esm({
25383
- "components/core/molecules/IconPicker.tsx"() {
25384
- "use client";
25385
- init_Icon();
25386
- init_GridPicker();
25387
- ICON_ITEMS = (() => {
25388
- const items = [];
25389
- for (const [exportName, candidate] of Object.entries(LucideIcons2)) {
25390
- if (!/^[A-Z]/.test(exportName)) continue;
25391
- if (exportName.endsWith("Icon")) continue;
25392
- if (exportName.startsWith("Lucide")) continue;
25393
- const isComponent = candidate !== null && (typeof candidate === "object" || typeof candidate === "function") && "$$typeof" in candidate;
25394
- if (!isComponent) continue;
25395
- const kebab = pascalToKebab(exportName);
25396
- if (kebabToPascal3(kebab) !== exportName) continue;
25397
- items.push({ id: kebab, label: kebab, category: "icons" });
25398
- }
25399
- return items;
25400
- })();
25401
- IconPicker = ({ value, onChange, className }) => {
25402
- const items = useMemo(() => ICON_ITEMS, []);
25403
- return /* @__PURE__ */ jsx(
25404
- GridPicker,
25405
- {
25406
- items,
25407
- value,
25408
- onChange,
25409
- searchPlaceholder: "Search icons\u2026",
25410
- renderThumbnail: (it) => /* @__PURE__ */ jsx(Icon, { name: it.id }),
25411
- cellSize: 32,
25412
- className
25413
- }
25414
- );
25415
- };
25416
- IconPicker.displayName = "IconPicker";
25417
- }
25418
- });
25419
25786
  function toISODate(d) {
25420
25787
  return d.toISOString().slice(0, 10);
25421
25788
  }
@@ -35106,286 +35473,6 @@ var init_PageHeader = __esm({
35106
35473
  PageHeader.displayName = "PageHeader";
35107
35474
  }
35108
35475
  });
35109
- var FormSection, FormLayout, FormActions;
35110
- var init_FormSection = __esm({
35111
- "components/core/molecules/FormSection.tsx"() {
35112
- "use client";
35113
- init_cn();
35114
- init_atoms2();
35115
- init_Box();
35116
- init_Typography();
35117
- init_Button();
35118
- init_Stack();
35119
- init_Icon();
35120
- init_useEventBus();
35121
- FormSection = ({
35122
- title,
35123
- description,
35124
- children,
35125
- collapsible = false,
35126
- defaultCollapsed = false,
35127
- card = false,
35128
- columns = 1,
35129
- className
35130
- }) => {
35131
- const [collapsed, setCollapsed] = React74__default.useState(defaultCollapsed);
35132
- const { t } = useTranslate();
35133
- const eventBus = useEventBus();
35134
- const gridClass = {
35135
- 1: "grid-cols-1",
35136
- 2: "grid-cols-1 md:grid-cols-2",
35137
- 3: "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
35138
- }[columns];
35139
- React74__default.useCallback(() => {
35140
- if (collapsible) {
35141
- setCollapsed((prev) => !prev);
35142
- eventBus.emit("UI:TOGGLE_COLLAPSE", { collapsed: !collapsed });
35143
- }
35144
- }, [collapsible, collapsed, eventBus]);
35145
- const content = /* @__PURE__ */ jsxs(Fragment, { children: [
35146
- (title || description) && /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "mb-4", children: [
35147
- title && /* @__PURE__ */ jsxs(
35148
- HStack,
35149
- {
35150
- justify: "between",
35151
- align: "center",
35152
- className: cn(collapsible && "cursor-pointer"),
35153
- action: collapsible ? "TOGGLE_COLLAPSE" : void 0,
35154
- children: [
35155
- /* @__PURE__ */ jsx(Typography, { variant: "h3", weight: "semibold", children: title }),
35156
- collapsible && /* @__PURE__ */ jsx(
35157
- Button,
35158
- {
35159
- variant: "ghost",
35160
- size: "sm",
35161
- action: "TOGGLE_COLLAPSE",
35162
- children: /* @__PURE__ */ jsx(
35163
- Icon,
35164
- {
35165
- icon: ChevronDown,
35166
- size: "sm",
35167
- className: cn(
35168
- "text-muted-foreground transition-transform",
35169
- collapsed && "rotate-180"
35170
- )
35171
- }
35172
- )
35173
- }
35174
- )
35175
- ]
35176
- }
35177
- ),
35178
- description && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: description })
35179
- ] }),
35180
- (!collapsible || !collapsed) && /* @__PURE__ */ jsx(Box, { className: cn("grid gap-4", gridClass), children })
35181
- ] });
35182
- if (card) {
35183
- return /* @__PURE__ */ jsx(Card, { className: cn("p-6", className), children: content });
35184
- }
35185
- return /* @__PURE__ */ jsx(Box, { className, children: content });
35186
- };
35187
- FormSection.displayName = "FormSection";
35188
- FormLayout = ({
35189
- children,
35190
- dividers = true,
35191
- className
35192
- }) => {
35193
- return /* @__PURE__ */ jsx(
35194
- VStack,
35195
- {
35196
- gap: "lg",
35197
- className: cn(
35198
- dividers && "[&>*+*]:pt-8 [&>*+*]:border-t [&>*+*]:border-border",
35199
- className
35200
- ),
35201
- children
35202
- }
35203
- );
35204
- };
35205
- FormLayout.displayName = "FormLayout";
35206
- FormActions = ({
35207
- children,
35208
- sticky = false,
35209
- align = "right",
35210
- className
35211
- }) => {
35212
- const alignClass2 = {
35213
- left: "justify-start",
35214
- right: "justify-end",
35215
- between: "justify-between",
35216
- center: "justify-center"
35217
- }[align];
35218
- return /* @__PURE__ */ jsx(
35219
- HStack,
35220
- {
35221
- gap: "sm",
35222
- align: "center",
35223
- className: cn(
35224
- "pt-6 border-t border-border",
35225
- alignClass2,
35226
- sticky && "sticky bottom-0 bg-card py-4 -mx-6 px-6 shadow-[0_-4px_6px_-1px_rgb(0,0,0,0.05)]",
35227
- className
35228
- ),
35229
- children
35230
- }
35231
- );
35232
- };
35233
- FormActions.displayName = "FormActions";
35234
- }
35235
- });
35236
- function currentValue(decl, override) {
35237
- return override !== void 0 ? override : decl.default;
35238
- }
35239
- function TextLikeControl({
35240
- field,
35241
- numeric,
35242
- value,
35243
- onCommit
35244
- }) {
35245
- const initial = value === void 0 || value === null ? "" : String(value);
35246
- const [draft, setDraft] = React74__default.useState(initial);
35247
- React74__default.useEffect(() => setDraft(initial), [initial]);
35248
- const commit = () => {
35249
- if (numeric) {
35250
- const n = draft.trim() === "" ? 0 : Number(draft);
35251
- onCommit(field, Number.isNaN(n) ? 0 : n);
35252
- } else {
35253
- onCommit(field, draft);
35254
- }
35255
- };
35256
- return /* @__PURE__ */ jsx(
35257
- Input,
35258
- {
35259
- inputType: numeric ? "number" : "text",
35260
- value: draft,
35261
- onChange: (e) => setDraft(e.target.value),
35262
- onBlur: commit,
35263
- onKeyDown: (e) => {
35264
- if (e.key === "Enter") commit();
35265
- }
35266
- }
35267
- );
35268
- }
35269
- function isTraitConfigObject(v) {
35270
- return v !== null && v !== void 0 && typeof v === "object" && !Array.isArray(v);
35271
- }
35272
- function FieldControl({
35273
- name,
35274
- decl,
35275
- value,
35276
- onChange,
35277
- assets
35278
- }) {
35279
- let control;
35280
- const stringValue = typeof value === "string" ? value : void 0;
35281
- const effectiveValue = value ?? decl.default;
35282
- if (decl.type === "icon") {
35283
- control = /* @__PURE__ */ jsx(IconPicker, { value: stringValue, onChange: (icon) => onChange(name, icon) });
35284
- } else if (decl.type === "asset") {
35285
- control = /* @__PURE__ */ jsx(
35286
- AssetPicker,
35287
- {
35288
- assets: assets ?? [],
35289
- value: stringValue,
35290
- onChange: (url) => onChange(name, url)
35291
- }
35292
- );
35293
- } else if (decl.type === "boolean") {
35294
- control = /* @__PURE__ */ jsx(Switch, { checked: value === true, onChange: (c) => onChange(name, c) });
35295
- } else if (decl.type === "string" && decl.values !== void 0 && decl.values.length > 0) {
35296
- control = /* @__PURE__ */ jsx(
35297
- Select,
35298
- {
35299
- options: decl.values.map((v) => ({ value: v, label: v })),
35300
- value: typeof value === "string" ? value : "",
35301
- onChange: (e) => onChange(name, e.target.value)
35302
- }
35303
- );
35304
- } else if (decl.type === "number") {
35305
- control = /* @__PURE__ */ jsx(TextLikeControl, { field: name, numeric: true, value, onCommit: onChange });
35306
- } else if (decl.type === "string") {
35307
- control = /* @__PURE__ */ jsx(TextLikeControl, { field: name, numeric: false, value, onCommit: onChange });
35308
- } else if (decl.type === "node") {
35309
- control = /* @__PURE__ */ jsx(NodeSlotEditor, { value: effectiveValue, onChange: (next) => onChange(name, next) });
35310
- } else if (decl.type.startsWith("[") || Array.isArray(effectiveValue)) {
35311
- const arr = Array.isArray(effectiveValue) ? effectiveValue : [];
35312
- control = /* @__PURE__ */ jsx(JsonTreeEditor, { value: arr, onChange: (next) => onChange(name, next) });
35313
- } else if (decl.type === "object" || decl.type.startsWith("Map ") || !SCALAR_TYPES.has(decl.type) && isTraitConfigObject(effectiveValue)) {
35314
- const obj = isTraitConfigObject(effectiveValue) ? effectiveValue : {};
35315
- control = /* @__PURE__ */ jsx(JsonTreeEditor, { value: obj, onChange: (next) => onChange(name, next) });
35316
- } else {
35317
- control = /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "muted", children: [
35318
- decl.type,
35319
- " \u2014 edit in source"
35320
- ] });
35321
- }
35322
- return /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
35323
- /* @__PURE__ */ jsx(Typography, { variant: "label", children: decl.label ?? name }),
35324
- control,
35325
- decl.description !== void 0 && decl.description !== "" && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: decl.description })
35326
- ] });
35327
- }
35328
- var TIER_ORDER, SCALAR_TYPES, PropertyInspector;
35329
- var init_PropertyInspector = __esm({
35330
- "components/core/molecules/PropertyInspector.tsx"() {
35331
- "use client";
35332
- init_cn();
35333
- init_Stack();
35334
- init_Typography();
35335
- init_Button();
35336
- init_Switch();
35337
- init_Select();
35338
- init_Input();
35339
- init_FormSection();
35340
- init_IconPicker();
35341
- init_AssetPicker();
35342
- init_JsonTreeEditor();
35343
- init_NodeSlotEditor();
35344
- TIER_ORDER = ["presentation", "domain", "policy", "infra", "internal"];
35345
- SCALAR_TYPES = /* @__PURE__ */ new Set(["string", "number", "boolean", "icon", "asset"]);
35346
- PropertyInspector = ({
35347
- config,
35348
- values,
35349
- onChange,
35350
- onReset,
35351
- title,
35352
- className,
35353
- assets
35354
- }) => {
35355
- const fields = Object.entries(config);
35356
- const byTier = /* @__PURE__ */ new Map();
35357
- for (const [name, decl] of fields) {
35358
- const tier = decl.tier ?? "presentation";
35359
- const arr = byTier.get(tier) ?? [];
35360
- arr.push([name, decl]);
35361
- byTier.set(tier, arr);
35362
- }
35363
- const tiers = [...byTier.keys()].sort((a, b) => {
35364
- const ia = TIER_ORDER.indexOf(a);
35365
- const ib = TIER_ORDER.indexOf(b);
35366
- return (ia === -1 ? 99 : ia) - (ib === -1 ? 99 : ib);
35367
- });
35368
- return /* @__PURE__ */ jsxs(VStack, { gap: "sm", className: cn("w-full", className), children: [
35369
- /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", children: [
35370
- /* @__PURE__ */ jsx(Typography, { variant: "caption", weight: "bold", children: title ?? "Config" }),
35371
- onReset !== void 0 && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", icon: "rotate-ccw", label: "Reset", onClick: onReset })
35372
- ] }),
35373
- fields.length === 0 && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: "No configurable properties." }),
35374
- 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(
35375
- FieldControl,
35376
- {
35377
- name,
35378
- decl,
35379
- value: currentValue(decl, values?.[name]),
35380
- onChange,
35381
- assets
35382
- },
35383
- name
35384
- )) }) }, tier))
35385
- ] });
35386
- };
35387
- }
35388
- });
35389
35476
  var lookStyles8, Header;
35390
35477
  var init_Header = __esm({
35391
35478
  "components/core/molecules/Header.tsx"() {
@@ -36741,7 +36828,6 @@ var init_DocumentViewer = __esm({
36741
36828
  showPrint = false,
36742
36829
  actions,
36743
36830
  documents,
36744
- entity,
36745
36831
  isLoading = false,
36746
36832
  error,
36747
36833
  className
@@ -43911,7 +43997,7 @@ function TraitSlot({
43911
43997
  size = "md",
43912
43998
  showTooltip = true,
43913
43999
  categoryColors,
43914
- tooltipFrameUrl,
44000
+ tooltipFrameUrl = "",
43915
44001
  className,
43916
44002
  feedback,
43917
44003
  onItemDrop,
@@ -47000,7 +47086,7 @@ var init_WorldMapBoard = __esm({
47000
47086
  init_useEventBus();
47001
47087
  init_Stack();
47002
47088
  init_LoadingState();
47003
- init_IsometricCanvas2();
47089
+ init_IsometricCanvas();
47004
47090
  init_boardEntity();
47005
47091
  init_isometric();
47006
47092
  WorldMapBoard.displayName = "WorldMapBoard";
@@ -47140,7 +47226,7 @@ function lazyThree(name, loader) {
47140
47226
  ThreeWrapper.displayName = `Lazy(${name})`;
47141
47227
  return ThreeWrapper;
47142
47228
  }
47143
- var FeatureRenderer, COMPONENT_REGISTRY;
47229
+ var FeatureRenderer, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
47144
47230
  var init_component_registry_generated = __esm({
47145
47231
  "components/core/organisms/component-registry.generated.ts"() {
47146
47232
  init_AboutPageTemplate();
@@ -47426,7 +47512,11 @@ var init_component_registry_generated = __esm({
47426
47512
  init_WorldMapBoard();
47427
47513
  init_WorldMapTemplate();
47428
47514
  init_XPBar();
47429
- FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/organisms/game/three'));
47515
+ FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/molecules/game/three'));
47516
+ GameCanvas3D = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
47517
+ GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
47518
+ GameCanvas3DCastleTemplate = lazyThree("GameCanvas3DCastleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
47519
+ GameCanvas3DWorldMapTemplate = lazyThree("GameCanvas3DWorldMapTemplate", () => import('@almadar/ui/components/molecules/game/three'));
47430
47520
  COMPONENT_REGISTRY = {
47431
47521
  "AboutPageTemplate": AboutPageTemplate,
47432
47522
  "Accordion": Accordion,
@@ -47539,6 +47629,10 @@ var init_component_registry_generated = __esm({
47539
47629
  "GameAudioProvider": GameAudioProvider,
47540
47630
  "GameAudioToggle": GameAudioToggle,
47541
47631
  "GameCanvas2D": GameCanvas2D,
47632
+ "GameCanvas3D": GameCanvas3D,
47633
+ "GameCanvas3DBattleTemplate": GameCanvas3DBattleTemplate,
47634
+ "GameCanvas3DCastleTemplate": GameCanvas3DCastleTemplate,
47635
+ "GameCanvas3DWorldMapTemplate": GameCanvas3DWorldMapTemplate,
47542
47636
  "GameHud": GameHud,
47543
47637
  "GameMenu": GameMenu,
47544
47638
  "GameOverScreen": GameOverScreen,
@@ -48401,7 +48495,7 @@ function SlotContentRenderer({
48401
48495
  const { children: _childrenConfig, ...restPropsNoChildren } = content.props;
48402
48496
  const restProps = childrenIsRenderFn ? { ...restPropsNoChildren, children: incomingChildren } : restPropsNoChildren;
48403
48497
  const renderedProps = renderPatternProps(restProps, onDismiss);
48404
- const patternDef = getPatternDefinition(content.pattern);
48498
+ const patternDef = getPatternDefinition$1(content.pattern);
48405
48499
  const propsSchema = patternDef?.propsSchema;
48406
48500
  if (propsSchema) {
48407
48501
  for (const [propKey, propValue] of Object.entries(renderedProps)) {