@almadar/ui 5.47.0 → 5.49.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 (45) hide show
  1. package/dist/avl/index.cjs +2998 -443
  2. package/dist/avl/index.css +504 -0
  3. package/dist/avl/index.js +2998 -444
  4. package/dist/components/core/templates/index.d.ts +3 -0
  5. package/dist/components/game/atoms/ActionButton.d.ts +4 -1
  6. package/dist/components/game/atoms/ComboCounter.d.ts +4 -1
  7. package/dist/components/game/atoms/ControlButton.d.ts +4 -2
  8. package/dist/components/game/atoms/DamageNumber.d.ts +4 -1
  9. package/dist/components/game/atoms/ResourceCounter.d.ts +4 -1
  10. package/dist/components/game/atoms/ScoreDisplay.d.ts +4 -1
  11. package/dist/components/game/atoms/StateIndicator.d.ts +4 -7
  12. package/dist/components/game/atoms/StatusEffect.d.ts +4 -1
  13. package/dist/components/game/atoms/TurnIndicator.d.ts +4 -1
  14. package/dist/components/game/atoms/WaypointMarker.d.ts +4 -1
  15. package/dist/components/game/molecules/EnemyPlate.d.ts +4 -1
  16. package/dist/components/game/molecules/StatBadge.d.ts +4 -1
  17. package/dist/components/game/molecules/three/index.cjs +1428 -346
  18. package/dist/components/game/molecules/three/index.css +0 -5
  19. package/dist/components/game/molecules/three/index.js +1429 -347
  20. package/dist/components/game/organisms/CastleBoard.d.ts +10 -1
  21. package/dist/components/game/organisms/GameBoard3D.d.ts +62 -0
  22. package/dist/components/game/organisms/PlatformerBoard.d.ts +51 -0
  23. package/dist/components/game/organisms/RoguelikeBoard.d.ts +59 -0
  24. package/dist/components/game/organisms/TowerDefenseBoard.d.ts +64 -0
  25. package/dist/components/game/organisms/index.d.ts +4 -0
  26. package/dist/components/game/organisms/puzzles/event-handler/EventHandlerBoard.d.ts +9 -1
  27. package/dist/components/game/organisms/puzzles/state-architect/StateArchitectBoard.d.ts +14 -1
  28. package/dist/components/game/templates/GameCanvas3DBattleTemplate.d.ts +22 -35
  29. package/dist/components/game/templates/GameCanvas3DCastleTemplate.d.ts +23 -23
  30. package/dist/components/game/templates/GameCanvas3DWorldMapTemplate.d.ts +27 -29
  31. package/dist/components/game/templates/PlatformerTemplate.d.ts +26 -0
  32. package/dist/components/game/templates/RoguelikeTemplate.d.ts +23 -0
  33. package/dist/components/game/templates/TowerDefenseTemplate.d.ts +43 -0
  34. package/dist/components/index.cjs +2991 -422
  35. package/dist/components/index.css +504 -0
  36. package/dist/components/index.js +2985 -424
  37. package/dist/docs/index.css +504 -0
  38. package/dist/providers/index.cjs +2981 -426
  39. package/dist/providers/index.css +504 -0
  40. package/dist/providers/index.js +2981 -427
  41. package/dist/runtime/index.cjs +2982 -427
  42. package/dist/runtime/index.css +504 -0
  43. package/dist/runtime/index.js +2982 -428
  44. package/package.json +1 -1
  45. package/tailwind-preset.cjs +1 -0
@@ -13,6 +13,7 @@ var OBJLoader_js = require('three/examples/jsm/loaders/OBJLoader.js');
13
13
  var providers = require('@almadar/ui/providers');
14
14
  var clsx = require('clsx');
15
15
  var tailwindMerge = require('tailwind-merge');
16
+ var LucideIcons = require('lucide-react');
16
17
  var postprocessing = require('@react-three/postprocessing');
17
18
  var hooks = require('@almadar/ui/hooks');
18
19
 
@@ -38,6 +39,7 @@ function _interopNamespace(e) {
38
39
 
39
40
  var React11__default = /*#__PURE__*/_interopDefault(React11);
40
41
  var THREE6__namespace = /*#__PURE__*/_interopNamespace(THREE6);
42
+ var LucideIcons__namespace = /*#__PURE__*/_interopNamespace(LucideIcons);
41
43
 
42
44
  var __defProp = Object.defineProperty;
43
45
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -2280,6 +2282,9 @@ function preloadFeatures(urls) {
2280
2282
  }
2281
2283
  });
2282
2284
  }
2285
+ function cn(...inputs) {
2286
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
2287
+ }
2283
2288
  var DEFAULT_GRID_CONFIG = {
2284
2289
  cellSize: 1,
2285
2290
  offsetX: 0,
@@ -2713,11 +2718,10 @@ var GameCanvas3D = React11.forwardRef(
2713
2718
  "div",
2714
2719
  {
2715
2720
  ref: containerRef,
2716
- className: `game-canvas-3d ${className || ""}`,
2721
+ className: cn("game-canvas-3d relative w-full h-full min-h-[85vh] overflow-hidden", className),
2717
2722
  "data-orientation": orientation,
2718
2723
  "data-camera-mode": cameraMode,
2719
2724
  "data-overlay": overlay,
2720
- style: { position: "relative", width: "100%", height: "100%", minHeight: "85vh", overflow: "hidden" },
2721
2725
  children: [
2722
2726
  /* @__PURE__ */ jsxRuntime.jsxs(
2723
2727
  fiber.Canvas,
@@ -2831,276 +2835,940 @@ var GameCanvas3D = React11.forwardRef(
2831
2835
  }
2832
2836
  );
2833
2837
  GameCanvas3D.displayName = "GameCanvas3D";
2834
- function cn(...inputs) {
2835
- return tailwindMerge.twMerge(clsx.clsx(inputs));
2838
+ var DEFAULT_FAMILY = "lucide";
2839
+ var VALID_FAMILIES = [
2840
+ "lucide",
2841
+ "phosphor-outline",
2842
+ "phosphor-fill",
2843
+ "phosphor-duotone",
2844
+ "tabler",
2845
+ "fa-solid"
2846
+ ];
2847
+ function getCurrentIconFamily() {
2848
+ if (typeof window === "undefined" || typeof document === "undefined") {
2849
+ return DEFAULT_FAMILY;
2850
+ }
2851
+ const raw = getComputedStyle(document.documentElement).getPropertyValue("--icon-family").trim().replace(/^["']|["']$/g, "");
2852
+ return VALID_FAMILIES.includes(raw) ? raw : DEFAULT_FAMILY;
2836
2853
  }
2837
-
2838
- // components/core/atoms/Box.tsx
2839
- var paddingStyles = {
2840
- none: "p-0",
2841
- xs: "p-1",
2842
- sm: "p-2",
2843
- md: "p-4",
2844
- lg: "p-6",
2845
- xl: "p-8",
2846
- "2xl": "p-12"
2847
- };
2848
- var paddingXStyles = {
2849
- none: "px-0",
2850
- xs: "px-1",
2851
- sm: "px-2",
2852
- md: "px-4",
2853
- lg: "px-6",
2854
- xl: "px-8",
2855
- "2xl": "px-12"
2854
+ var cachedFamily = null;
2855
+ var listeners = /* @__PURE__ */ new Set();
2856
+ var observer = null;
2857
+ function ensureObserver() {
2858
+ if (typeof window === "undefined" || observer) return;
2859
+ observer = new MutationObserver(() => {
2860
+ const next = getCurrentIconFamily();
2861
+ if (next !== cachedFamily) {
2862
+ cachedFamily = next;
2863
+ listeners.forEach((fn) => fn());
2864
+ }
2865
+ });
2866
+ observer.observe(document.documentElement, {
2867
+ attributes: true,
2868
+ attributeFilter: ["data-theme", "style"]
2869
+ });
2870
+ cachedFamily = getCurrentIconFamily();
2871
+ }
2872
+ function subscribeIconFamily(notify) {
2873
+ ensureObserver();
2874
+ listeners.add(notify);
2875
+ return () => {
2876
+ listeners.delete(notify);
2877
+ };
2878
+ }
2879
+ function getIconFamilySnapshot() {
2880
+ if (cachedFamily !== null) return cachedFamily;
2881
+ cachedFamily = getCurrentIconFamily();
2882
+ return cachedFamily;
2883
+ }
2884
+ function getIconFamilyServerSnapshot() {
2885
+ return DEFAULT_FAMILY;
2886
+ }
2887
+ function useIconFamily() {
2888
+ return React11.useSyncExternalStore(
2889
+ subscribeIconFamily,
2890
+ getIconFamilySnapshot,
2891
+ getIconFamilyServerSnapshot
2892
+ );
2893
+ }
2894
+ function kebabToPascal(name) {
2895
+ return name.split("-").map((part) => {
2896
+ if (/^\d+$/.test(part)) return part;
2897
+ return part.charAt(0).toUpperCase() + part.slice(1);
2898
+ }).join("");
2899
+ }
2900
+ var libPromises = /* @__PURE__ */ new Map();
2901
+ function loadLib(key, importer) {
2902
+ let p = libPromises.get(key);
2903
+ if (!p) {
2904
+ p = importer().then((m) => m);
2905
+ libPromises.set(key, p);
2906
+ }
2907
+ return p;
2908
+ }
2909
+ function lazyFamilyIcon(libKey, importer, pick, fallbackName, family) {
2910
+ const Lazy = React11__default.default.lazy(async () => {
2911
+ const lib = await loadLib(libKey, importer);
2912
+ const Comp = pick(lib);
2913
+ if (!Comp) {
2914
+ warnFallback(fallbackName, family);
2915
+ return { default: makeLucideAdapter(fallbackName, true) };
2916
+ }
2917
+ return { default: Comp };
2918
+ });
2919
+ const Wrapped = (props) => /* @__PURE__ */ jsxRuntime.jsx(
2920
+ React11__default.default.Suspense,
2921
+ {
2922
+ fallback: /* @__PURE__ */ jsxRuntime.jsx(
2923
+ "span",
2924
+ {
2925
+ "aria-hidden": true,
2926
+ className: props.className,
2927
+ style: { display: "inline-block", ...props.style }
2928
+ }
2929
+ ),
2930
+ children: /* @__PURE__ */ jsxRuntime.jsx(Lazy, { ...props })
2931
+ }
2932
+ );
2933
+ Wrapped.displayName = `Lazy.${libKey}.${fallbackName}`;
2934
+ return Wrapped;
2935
+ }
2936
+ var lucideAliases = {
2937
+ close: LucideIcons__namespace.X,
2938
+ trash: LucideIcons__namespace.Trash2,
2939
+ loader: LucideIcons__namespace.Loader2,
2940
+ stop: LucideIcons__namespace.Square,
2941
+ volume: LucideIcons__namespace.Volume2,
2942
+ "volume-off": LucideIcons__namespace.VolumeX,
2943
+ refresh: LucideIcons__namespace.RefreshCw,
2944
+ share: LucideIcons__namespace.Share2,
2945
+ "sort-asc": LucideIcons__namespace.ArrowUpNarrowWide,
2946
+ "sort-desc": LucideIcons__namespace.ArrowDownNarrowWide
2856
2947
  };
2857
- var paddingYStyles = {
2858
- none: "py-0",
2859
- xs: "py-1",
2860
- sm: "py-2",
2861
- md: "py-4",
2862
- lg: "py-6",
2863
- xl: "py-8",
2864
- "2xl": "py-12"
2948
+ function resolveLucide(name) {
2949
+ if (lucideAliases[name]) return lucideAliases[name];
2950
+ const pascal = kebabToPascal(name);
2951
+ const lucideMap = LucideIcons__namespace;
2952
+ const direct = lucideMap[pascal];
2953
+ if (direct && typeof direct === "object") return direct;
2954
+ const asIs = lucideMap[name];
2955
+ if (asIs && typeof asIs === "object") return asIs;
2956
+ return LucideIcons__namespace.HelpCircle;
2957
+ }
2958
+ var phosphorAliases = {
2959
+ // lucide name → phosphor PascalCase name
2960
+ // Actions
2961
+ plus: "Plus",
2962
+ minus: "Minus",
2963
+ x: "X",
2964
+ check: "Check",
2965
+ close: "X",
2966
+ edit: "PencilSimple",
2967
+ pencil: "PencilSimple",
2968
+ trash: "Trash",
2969
+ save: "FloppyDisk",
2970
+ copy: "Copy",
2971
+ share: "Share",
2972
+ send: "PaperPlaneRight",
2973
+ download: "DownloadSimple",
2974
+ upload: "UploadSimple",
2975
+ archive: "Archive",
2976
+ refresh: "ArrowsClockwise",
2977
+ loader: "CircleNotch",
2978
+ link: "Link",
2979
+ paperclip: "Paperclip",
2980
+ // Navigation
2981
+ "chevron-down": "CaretDown",
2982
+ "chevron-up": "CaretUp",
2983
+ "chevron-left": "CaretLeft",
2984
+ "chevron-right": "CaretRight",
2985
+ "arrow-up": "ArrowUp",
2986
+ "arrow-down": "ArrowDown",
2987
+ "arrow-left": "ArrowLeft",
2988
+ "arrow-right": "ArrowRight",
2989
+ menu: "List",
2990
+ more: "DotsThree",
2991
+ "more-vertical": "DotsThreeVertical",
2992
+ "more-horizontal": "DotsThree",
2993
+ external: "ArrowSquareOut",
2994
+ "external-link": "ArrowSquareOut",
2995
+ // Files
2996
+ file: "File",
2997
+ "file-text": "FileText",
2998
+ "file-plus": "FilePlus",
2999
+ "file-minus": "FileMinus",
3000
+ folder: "Folder",
3001
+ "folder-open": "FolderOpen",
3002
+ document: "FileText",
3003
+ // Charts
3004
+ "bar-chart": "ChartBar",
3005
+ "bar-chart-2": "ChartBar",
3006
+ "bar-chart-3": "ChartBar",
3007
+ "line-chart": "ChartLine",
3008
+ "pie-chart": "ChartPie",
3009
+ activity: "Pulse",
3010
+ "trending-up": "TrendUp",
3011
+ "trending-down": "TrendDown",
3012
+ // Messages
3013
+ message: "ChatCircle",
3014
+ "message-circle": "ChatCircle",
3015
+ "message-square": "ChatText",
3016
+ "messages-square": "ChatsCircle",
3017
+ comment: "ChatCircle",
3018
+ comments: "ChatsCircle",
3019
+ inbox: "Tray",
3020
+ mail: "Envelope",
3021
+ envelope: "Envelope",
3022
+ // Status
3023
+ "alert-circle": "WarningCircle",
3024
+ "alert-triangle": "Warning",
3025
+ "check-circle": "CheckCircle",
3026
+ "x-circle": "XCircle",
3027
+ info: "Info",
3028
+ "help-circle": "Question",
3029
+ "life-buoy": "Lifebuoy",
3030
+ lifebuoy: "Lifebuoy",
3031
+ warning: "Warning",
3032
+ error: "WarningCircle",
3033
+ // Media
3034
+ image: "Image",
3035
+ video: "VideoCamera",
3036
+ film: "FilmStrip",
3037
+ camera: "Camera",
3038
+ music: "MusicNote",
3039
+ play: "Play",
3040
+ pause: "Pause",
3041
+ stop: "Stop",
3042
+ "skip-forward": "SkipForward",
3043
+ "skip-back": "SkipBack",
3044
+ volume: "SpeakerHigh",
3045
+ "volume-2": "SpeakerHigh",
3046
+ "volume-x": "SpeakerX",
3047
+ mic: "Microphone",
3048
+ "mic-off": "MicrophoneSlash",
3049
+ // People
3050
+ user: "User",
3051
+ users: "Users",
3052
+ "user-plus": "UserPlus",
3053
+ "user-check": "UserCheck",
3054
+ // Time
3055
+ calendar: "Calendar",
3056
+ clock: "Clock",
3057
+ timer: "Timer",
3058
+ // Location
3059
+ map: "MapTrifold",
3060
+ "map-pin": "MapPin",
3061
+ navigation: "NavigationArrow",
3062
+ compass: "Compass",
3063
+ globe: "Globe",
3064
+ target: "Target",
3065
+ // Project / layout
3066
+ kanban: "Kanban",
3067
+ list: "List",
3068
+ table: "Table",
3069
+ grid: "GridFour",
3070
+ layout: "Layout",
3071
+ columns: "Columns",
3072
+ rows: "Rows",
3073
+ // Misc
3074
+ bell: "Bell",
3075
+ bookmark: "Bookmark",
3076
+ briefcase: "Briefcase",
3077
+ flag: "Flag",
3078
+ tag: "Tag",
3079
+ tags: "Tag",
3080
+ star: "Star",
3081
+ heart: "Heart",
3082
+ home: "House",
3083
+ settings: "Gear",
3084
+ eye: "Eye",
3085
+ "eye-off": "EyeSlash",
3086
+ lock: "Lock",
3087
+ unlock: "LockOpen",
3088
+ key: "Key",
3089
+ shield: "Shield",
3090
+ search: "MagnifyingGlass",
3091
+ filter: "Funnel",
3092
+ "sort-asc": "SortAscending",
3093
+ "sort-desc": "SortDescending",
3094
+ zap: "Lightning",
3095
+ sparkles: "Sparkle",
3096
+ // Code
3097
+ code: "Code",
3098
+ terminal: "Terminal",
3099
+ database: "Database",
3100
+ server: "HardDrives",
3101
+ cloud: "Cloud",
3102
+ wifi: "WifiHigh",
3103
+ package: "Package",
3104
+ box: "Package",
3105
+ // Theme
3106
+ sun: "Sun",
3107
+ moon: "Moon",
3108
+ // Phone
3109
+ phone: "Phone",
3110
+ printer: "Printer",
3111
+ // Hierarchy
3112
+ tree: "Tree",
3113
+ network: "Network"
2865
3114
  };
2866
- var marginStyles = {
2867
- none: "m-0",
2868
- xs: "m-1",
2869
- sm: "m-2",
2870
- md: "m-4",
2871
- lg: "m-6",
2872
- xl: "m-8",
2873
- "2xl": "m-12",
2874
- auto: "m-auto"
3115
+ function resolvePhosphor(name, weight, family) {
3116
+ const target = phosphorAliases[name] ?? kebabToPascal(name);
3117
+ return lazyFamilyIcon(
3118
+ "phosphor",
3119
+ () => import('@phosphor-icons/react'),
3120
+ (lib) => {
3121
+ const PhosphorComp = lib[target];
3122
+ if (!PhosphorComp || typeof PhosphorComp !== "object") return null;
3123
+ const Component2 = PhosphorComp;
3124
+ const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
3125
+ Component2,
3126
+ {
3127
+ weight,
3128
+ className: props.className,
3129
+ style: props.style,
3130
+ size: props.size ?? "1em"
3131
+ }
3132
+ );
3133
+ Adapter.displayName = `Phosphor.${target}.${weight}`;
3134
+ return Adapter;
3135
+ },
3136
+ name,
3137
+ family
3138
+ );
3139
+ }
3140
+ var tablerAliases = {
3141
+ // lucide name → tabler suffix (after the `Icon` prefix)
3142
+ // Actions
3143
+ plus: "Plus",
3144
+ minus: "Minus",
3145
+ x: "X",
3146
+ check: "Check",
3147
+ close: "X",
3148
+ edit: "Pencil",
3149
+ pencil: "Pencil",
3150
+ trash: "Trash",
3151
+ save: "DeviceFloppy",
3152
+ copy: "Copy",
3153
+ share: "Share",
3154
+ send: "Send",
3155
+ download: "Download",
3156
+ upload: "Upload",
3157
+ archive: "Archive",
3158
+ refresh: "Refresh",
3159
+ loader: "Loader2",
3160
+ link: "Link",
3161
+ paperclip: "Paperclip",
3162
+ external: "ExternalLink",
3163
+ "external-link": "ExternalLink",
3164
+ // Navigation
3165
+ "chevron-down": "ChevronDown",
3166
+ "chevron-up": "ChevronUp",
3167
+ "chevron-left": "ChevronLeft",
3168
+ "chevron-right": "ChevronRight",
3169
+ "arrow-down": "ArrowDown",
3170
+ "arrow-up": "ArrowUp",
3171
+ "arrow-left": "ArrowLeft",
3172
+ "arrow-right": "ArrowRight",
3173
+ menu: "Menu2",
3174
+ more: "Dots",
3175
+ "more-vertical": "DotsVertical",
3176
+ // Files
3177
+ file: "File",
3178
+ "file-text": "FileText",
3179
+ "file-plus": "FilePlus",
3180
+ "file-check": "FileCheck",
3181
+ "file-minus": "FileMinus",
3182
+ folder: "Folder",
3183
+ "folder-open": "FolderOpen",
3184
+ document: "FileText",
3185
+ // Charts
3186
+ "bar-chart": "ChartBar",
3187
+ "bar-chart-2": "ChartBar",
3188
+ "bar-chart-3": "ChartBar",
3189
+ "line-chart": "ChartLine",
3190
+ "pie-chart": "ChartPie",
3191
+ activity: "Activity",
3192
+ "trending-up": "TrendingUp",
3193
+ "trending-down": "TrendingDown",
3194
+ // Messages
3195
+ message: "Message",
3196
+ "message-circle": "MessageCircle",
3197
+ "message-square": "Message2",
3198
+ "messages-square": "Messages",
3199
+ comment: "Message",
3200
+ comments: "Messages",
3201
+ inbox: "Inbox",
3202
+ mail: "Mail",
3203
+ envelope: "Mail",
3204
+ // Status
3205
+ "alert-circle": "AlertCircle",
3206
+ "alert-triangle": "AlertTriangle",
3207
+ "check-circle": "CircleCheck",
3208
+ "x-circle": "CircleX",
3209
+ info: "InfoCircle",
3210
+ "help-circle": "HelpCircle",
3211
+ "life-buoy": "Lifebuoy",
3212
+ warning: "AlertTriangle",
3213
+ error: "AlertOctagon",
3214
+ // Media
3215
+ image: "Photo",
3216
+ video: "Video",
3217
+ camera: "Camera",
3218
+ music: "Music",
3219
+ play: "PlayerPlay",
3220
+ pause: "PlayerPause",
3221
+ stop: "PlayerStop",
3222
+ "skip-forward": "PlayerSkipForward",
3223
+ "skip-back": "PlayerSkipBack",
3224
+ volume: "Volume",
3225
+ "volume-2": "Volume",
3226
+ "volume-x": "VolumeOff",
3227
+ mic: "Microphone",
3228
+ "mic-off": "MicrophoneOff",
3229
+ // People
3230
+ user: "User",
3231
+ users: "Users",
3232
+ "user-plus": "UserPlus",
3233
+ "user-check": "UserCheck",
3234
+ // Time
3235
+ calendar: "Calendar",
3236
+ clock: "Clock",
3237
+ timer: "Hourglass",
3238
+ // Location
3239
+ map: "Map",
3240
+ "map-pin": "MapPin",
3241
+ navigation: "Navigation",
3242
+ compass: "Compass",
3243
+ globe: "World",
3244
+ target: "Target",
3245
+ // Project / layout
3246
+ kanban: "LayoutKanban",
3247
+ list: "List",
3248
+ table: "Table",
3249
+ grid: "LayoutGrid",
3250
+ layout: "Layout",
3251
+ columns: "LayoutColumns",
3252
+ rows: "LayoutRows",
3253
+ // Misc
3254
+ bell: "Bell",
3255
+ bookmark: "Bookmark",
3256
+ briefcase: "Briefcase",
3257
+ flag: "Flag",
3258
+ tag: "Tag",
3259
+ tags: "Tags",
3260
+ star: "Star",
3261
+ heart: "Heart",
3262
+ home: "Home",
3263
+ settings: "Settings",
3264
+ eye: "Eye",
3265
+ "eye-off": "EyeOff",
3266
+ lock: "Lock",
3267
+ unlock: "LockOpen",
3268
+ key: "Key",
3269
+ shield: "Shield",
3270
+ search: "Search",
3271
+ filter: "Filter",
3272
+ "sort-asc": "SortAscending",
3273
+ "sort-desc": "SortDescending",
3274
+ zap: "Bolt",
3275
+ sparkles: "Sparkles",
3276
+ // Code / data
3277
+ code: "Code",
3278
+ terminal: "Terminal",
3279
+ database: "Database",
3280
+ server: "Server",
3281
+ cloud: "Cloud",
3282
+ wifi: "Wifi",
3283
+ package: "Package",
3284
+ box: "Box",
3285
+ // Theme
3286
+ sun: "Sun",
3287
+ moon: "Moon",
3288
+ // Phone
3289
+ phone: "Phone",
3290
+ printer: "Printer",
3291
+ // Hierarchy
3292
+ tree: "Hierarchy",
3293
+ network: "Network"
2875
3294
  };
2876
- var marginXStyles = {
2877
- none: "mx-0",
2878
- xs: "mx-1",
2879
- sm: "mx-2",
2880
- md: "mx-4",
2881
- lg: "mx-6",
2882
- xl: "mx-8",
2883
- "2xl": "mx-12",
2884
- auto: "mx-auto"
3295
+ function resolveTabler(name, family) {
3296
+ const suffix = tablerAliases[name] ?? kebabToPascal(name);
3297
+ const target = `Icon${suffix}`;
3298
+ return lazyFamilyIcon(
3299
+ "tabler",
3300
+ () => import('@tabler/icons-react'),
3301
+ (lib) => {
3302
+ const TablerComp = lib[target];
3303
+ if (!TablerComp || typeof TablerComp !== "object") return null;
3304
+ const Component2 = TablerComp;
3305
+ const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
3306
+ Component2,
3307
+ {
3308
+ stroke: props.strokeWidth ?? 1.5,
3309
+ className: props.className,
3310
+ style: props.style,
3311
+ size: props.size ?? 24
3312
+ }
3313
+ );
3314
+ Adapter.displayName = `Tabler.${target}`;
3315
+ return Adapter;
3316
+ },
3317
+ name,
3318
+ family
3319
+ );
3320
+ }
3321
+ var faAliases = {
3322
+ // lucide name → fa-solid suffix (after the `Fa` prefix).
3323
+ // react-icons/fa ships FontAwesome 5 — names like `FaFileText` don't exist
3324
+ // (FA renamed to `FaFileAlt`). When you see a console.warn from
3325
+ // [iconFamily] about an unmapped lucide name in this family, add the
3326
+ // closest FA5 sibling here so the fallback stays in-family.
3327
+ search: "Search",
3328
+ close: "Times",
3329
+ x: "Times",
3330
+ loader: "Spinner",
3331
+ refresh: "Sync",
3332
+ "sort-asc": "SortAmountUp",
3333
+ "sort-desc": "SortAmountDown",
3334
+ "chevron-down": "ChevronDown",
3335
+ "chevron-up": "ChevronUp",
3336
+ "chevron-left": "ChevronLeft",
3337
+ "chevron-right": "ChevronRight",
3338
+ "help-circle": "QuestionCircle",
3339
+ "alert-triangle": "ExclamationTriangle",
3340
+ "alert-circle": "ExclamationCircle",
3341
+ "check-circle": "CheckCircle",
3342
+ "x-circle": "TimesCircle",
3343
+ edit: "Edit",
3344
+ pencil: "PencilAlt",
3345
+ trash: "Trash",
3346
+ send: "PaperPlane",
3347
+ share: "ShareAlt",
3348
+ external: "ExternalLinkAlt",
3349
+ plus: "Plus",
3350
+ minus: "Minus",
3351
+ check: "Check",
3352
+ star: "Star",
3353
+ heart: "Heart",
3354
+ home: "Home",
3355
+ user: "User",
3356
+ users: "Users",
3357
+ "user-plus": "UserPlus",
3358
+ "user-check": "UserCheck",
3359
+ settings: "Cog",
3360
+ menu: "Bars",
3361
+ "arrow-up": "ArrowUp",
3362
+ "arrow-down": "ArrowDown",
3363
+ "arrow-left": "ArrowLeft",
3364
+ "arrow-right": "ArrowRight",
3365
+ copy: "Copy",
3366
+ download: "Download",
3367
+ upload: "Upload",
3368
+ filter: "Filter",
3369
+ calendar: "Calendar",
3370
+ clock: "Clock",
3371
+ bell: "Bell",
3372
+ mail: "Envelope",
3373
+ envelope: "Envelope",
3374
+ lock: "Lock",
3375
+ unlock: "LockOpen",
3376
+ eye: "Eye",
3377
+ "eye-off": "EyeSlash",
3378
+ more: "EllipsisH",
3379
+ "more-vertical": "EllipsisV",
3380
+ info: "InfoCircle",
3381
+ warning: "ExclamationTriangle",
3382
+ error: "ExclamationCircle",
3383
+ // Time
3384
+ timer: "Hourglass",
3385
+ // Files (FA renamed FileText → FileAlt)
3386
+ file: "File",
3387
+ "file-text": "FileAlt",
3388
+ "file-plus": "FileMedical",
3389
+ "file-minus": "FileExcel",
3390
+ "file-check": "FileSignature",
3391
+ document: "FileAlt",
3392
+ // Charts (lucide BarChart2 / BarChart3 → FA ChartBar)
3393
+ "bar-chart": "ChartBar",
3394
+ "bar-chart-2": "ChartBar",
3395
+ "bar-chart-3": "ChartBar",
3396
+ "line-chart": "ChartLine",
3397
+ "pie-chart": "ChartPie",
3398
+ activity: "ChartLine",
3399
+ "trending-up": "ChartLine",
3400
+ "trending-down": "ChartLine",
3401
+ // Messages (lucide MessageCircle/MessageSquare → FA CommentDots/CommentAlt)
3402
+ message: "Comment",
3403
+ "message-circle": "CommentDots",
3404
+ "message-square": "CommentAlt",
3405
+ "messages-square": "Comments",
3406
+ comment: "Comment",
3407
+ comments: "Comments",
3408
+ inbox: "Inbox",
3409
+ // Support / help
3410
+ "life-buoy": "LifeRing",
3411
+ lifebuoy: "LifeRing",
3412
+ // Project / kanban (FA has no kanban; closest semantic is Tasks/Columns)
3413
+ kanban: "Tasks",
3414
+ columns: "Columns",
3415
+ rows: "Bars",
3416
+ layout: "ThLarge",
3417
+ grid: "Th",
3418
+ list: "List",
3419
+ table: "Table",
3420
+ // Storage / folders
3421
+ folder: "Folder",
3422
+ "folder-open": "FolderOpen",
3423
+ archive: "Archive",
3424
+ bookmark: "Bookmark",
3425
+ briefcase: "Briefcase",
3426
+ package: "Box",
3427
+ box: "Box",
3428
+ // Map / location
3429
+ map: "Map",
3430
+ "map-pin": "MapMarkerAlt",
3431
+ navigation: "LocationArrow",
3432
+ compass: "Compass",
3433
+ globe: "Globe",
3434
+ target: "Bullseye",
3435
+ // Media
3436
+ image: "Image",
3437
+ video: "Video",
3438
+ film: "Film",
3439
+ camera: "Camera",
3440
+ music: "Music",
3441
+ play: "Play",
3442
+ pause: "Pause",
3443
+ stop: "Stop",
3444
+ "skip-forward": "Forward",
3445
+ "skip-back": "Backward",
3446
+ volume: "VolumeUp",
3447
+ "volume-2": "VolumeUp",
3448
+ "volume-x": "VolumeMute",
3449
+ mic: "Microphone",
3450
+ "mic-off": "MicrophoneSlash",
3451
+ phone: "Phone",
3452
+ // Code / data
3453
+ code: "Code",
3454
+ terminal: "Terminal",
3455
+ database: "Database",
3456
+ server: "Server",
3457
+ cloud: "Cloud",
3458
+ wifi: "Wifi",
3459
+ // Security
3460
+ shield: "ShieldAlt",
3461
+ key: "Key",
3462
+ // Misc actions
3463
+ printer: "Print",
3464
+ save: "Save",
3465
+ link: "Link",
3466
+ unlink: "Unlink",
3467
+ paperclip: "Paperclip",
3468
+ flag: "Flag",
3469
+ tag: "Tag",
3470
+ tags: "Tags",
3471
+ zap: "Bolt",
3472
+ sparkles: "Magic",
3473
+ // Theme
3474
+ sun: "Sun",
3475
+ moon: "Moon",
3476
+ // Hierarchy (FA has no Tree icon for hierarchies; Sitemap is the org-chart icon)
3477
+ tree: "Sitemap",
3478
+ network: "NetworkWired"
2885
3479
  };
2886
- var marginYStyles = {
2887
- none: "my-0",
2888
- xs: "my-1",
2889
- sm: "my-2",
2890
- md: "my-4",
2891
- lg: "my-6",
2892
- xl: "my-8",
2893
- "2xl": "my-12",
2894
- auto: "my-auto"
3480
+ function resolveFa(name, family) {
3481
+ const suffix = faAliases[name] ?? kebabToPascal(name);
3482
+ const target = `Fa${suffix}`;
3483
+ return lazyFamilyIcon(
3484
+ "fa",
3485
+ () => import('react-icons/fa'),
3486
+ (lib) => {
3487
+ const FaComp = lib[target];
3488
+ if (!FaComp || typeof FaComp !== "function") return null;
3489
+ const Component2 = FaComp;
3490
+ const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
3491
+ Component2,
3492
+ {
3493
+ className: props.className,
3494
+ style: props.style,
3495
+ size: props.size ?? "1em"
3496
+ }
3497
+ );
3498
+ Adapter.displayName = `Fa.${target}`;
3499
+ return Adapter;
3500
+ },
3501
+ name,
3502
+ family
3503
+ );
3504
+ }
3505
+ var warned = /* @__PURE__ */ new Set();
3506
+ function warnFallback(name, family) {
3507
+ const key = `${family}::${name}`;
3508
+ if (warned.has(key)) return;
3509
+ warned.add(key);
3510
+ if (typeof console !== "undefined") {
3511
+ console.warn(
3512
+ `[iconFamily] No '${name}' mapping in family '${family}'; falling back to lucide. Add an alias in lib/iconFamily.ts.`
3513
+ );
3514
+ }
3515
+ }
3516
+ function makeLucideAdapter(name, isFallback = false) {
3517
+ const LucideComp = resolveLucide(name);
3518
+ const Adapter = (props) => {
3519
+ const stroke = props.strokeWidth ?? (isFallback ? 2 : void 0);
3520
+ const style = isFallback ? { ...props.style ?? {}, strokeWidth: stroke ?? 2 } : props.style;
3521
+ return /* @__PURE__ */ jsxRuntime.jsx(
3522
+ LucideComp,
3523
+ {
3524
+ className: props.className,
3525
+ strokeWidth: stroke,
3526
+ style,
3527
+ size: props.size
3528
+ }
3529
+ );
3530
+ };
3531
+ Adapter.displayName = `Lucide.${name}${isFallback ? ".fallback" : ""}`;
3532
+ return Adapter;
3533
+ }
3534
+ function resolveIconForFamily(name, family) {
3535
+ switch (family) {
3536
+ case "lucide":
3537
+ return makeLucideAdapter(name, false);
3538
+ // Non-lucide families resolve to a lazy, Suspense-wrapped component that
3539
+ // dynamic-imports the library on first render and falls back to lucide
3540
+ // internally when the family lacks the icon (see lazyFamilyIcon).
3541
+ case "phosphor-outline":
3542
+ return resolvePhosphor(name, "regular", family);
3543
+ case "phosphor-fill":
3544
+ return resolvePhosphor(name, "fill", family);
3545
+ case "phosphor-duotone":
3546
+ return resolvePhosphor(name, "duotone", family);
3547
+ case "tabler":
3548
+ return resolveTabler(name, family);
3549
+ case "fa-solid":
3550
+ return resolveFa(name, family);
3551
+ }
3552
+ }
3553
+ var colorTokenClasses = {
3554
+ primary: "text-primary",
3555
+ secondary: "text-secondary",
3556
+ success: "text-success",
3557
+ warning: "text-warning",
3558
+ error: "text-error",
3559
+ muted: "text-muted-foreground"
2895
3560
  };
2896
- var bgStyles = {
2897
- transparent: "bg-transparent",
2898
- primary: "bg-primary text-primary-foreground",
2899
- secondary: "bg-secondary text-secondary-foreground",
2900
- muted: "bg-muted text-foreground",
2901
- accent: "bg-accent text-accent-foreground",
2902
- surface: "bg-card",
2903
- overlay: "bg-card/80 backdrop-blur-sm"
3561
+ var sizeClasses = {
3562
+ xs: "w-3 h-3",
3563
+ sm: "w-4 h-4",
3564
+ md: "h-icon-default w-icon-default",
3565
+ lg: "w-6 h-6",
3566
+ xl: "w-8 h-8"
2904
3567
  };
2905
- var roundedStyles = {
2906
- none: "rounded-none",
2907
- sm: "rounded-sm",
2908
- md: "rounded-md",
2909
- lg: "rounded-lg",
2910
- xl: "rounded-xl",
2911
- "2xl": "rounded-xl",
2912
- full: "rounded-full"
3568
+ var animationClasses = {
3569
+ none: "",
3570
+ spin: "animate-spin",
3571
+ pulse: "animate-pulse"
2913
3572
  };
2914
- var shadowStyles = {
2915
- none: "shadow-none",
2916
- sm: "shadow-sm",
2917
- md: "shadow",
2918
- lg: "shadow-lg",
2919
- xl: "shadow-lg"
3573
+ var Icon = ({
3574
+ icon,
3575
+ name,
3576
+ size = "md",
3577
+ color,
3578
+ animation = "none",
3579
+ className,
3580
+ strokeWidth,
3581
+ style
3582
+ }) => {
3583
+ const directIcon = typeof icon === "string" ? void 0 : icon;
3584
+ const effectiveName = typeof icon === "string" ? icon : name;
3585
+ const family = useIconFamily();
3586
+ const RenderedComponent = React11__default.default.useMemo(() => {
3587
+ if (directIcon) return null;
3588
+ return effectiveName ? resolveIconForFamily(effectiveName, family) : null;
3589
+ }, [directIcon, effectiveName, family]);
3590
+ const effectiveStrokeWidth = strokeWidth ?? void 0;
3591
+ const inlineStyle = {
3592
+ ...effectiveStrokeWidth === void 0 ? { strokeWidth: "var(--icon-stroke-width, 2)" } : {},
3593
+ ...style
3594
+ };
3595
+ const resolvedColor = color ? color in colorTokenClasses ? colorTokenClasses[color] : color : "text-current";
3596
+ const composedClassName = cn(
3597
+ sizeClasses[size],
3598
+ animationClasses[animation],
3599
+ resolvedColor,
3600
+ className
3601
+ );
3602
+ if (directIcon) {
3603
+ const Direct = directIcon;
3604
+ return /* @__PURE__ */ jsxRuntime.jsx(
3605
+ Direct,
3606
+ {
3607
+ className: composedClassName,
3608
+ strokeWidth: effectiveStrokeWidth,
3609
+ style: inlineStyle
3610
+ }
3611
+ );
3612
+ }
3613
+ if (RenderedComponent) {
3614
+ return /* @__PURE__ */ jsxRuntime.jsx(
3615
+ RenderedComponent,
3616
+ {
3617
+ className: composedClassName,
3618
+ strokeWidth: effectiveStrokeWidth,
3619
+ style: inlineStyle
3620
+ }
3621
+ );
3622
+ }
3623
+ const Fallback = LucideIcons__namespace.HelpCircle;
3624
+ return /* @__PURE__ */ jsxRuntime.jsx(
3625
+ Fallback,
3626
+ {
3627
+ className: composedClassName,
3628
+ strokeWidth: effectiveStrokeWidth,
3629
+ style: inlineStyle
3630
+ }
3631
+ );
2920
3632
  };
2921
- var displayStyles = {
2922
- block: "block",
2923
- inline: "inline",
2924
- "inline-block": "inline-block",
2925
- flex: "flex",
2926
- "inline-flex": "inline-flex",
2927
- grid: "grid"
3633
+ Icon.displayName = "Icon";
3634
+ var variantStyles = {
3635
+ primary: [
3636
+ "bg-primary text-primary-foreground",
3637
+ "border-none",
3638
+ "shadow-sm",
3639
+ "hover:bg-primary-hover hover:shadow-lg",
3640
+ "active:scale-[var(--active-scale)] active:shadow-sm"
3641
+ ].join(" "),
3642
+ secondary: [
3643
+ "bg-transparent text-accent",
3644
+ "border border-accent",
3645
+ "hover:bg-accent hover:text-white hover:border-accent",
3646
+ "active:scale-[var(--active-scale)]"
3647
+ ].join(" "),
3648
+ ghost: [
3649
+ "bg-transparent text-muted-foreground",
3650
+ "border border-transparent",
3651
+ "hover:text-primary-foreground hover:bg-primary hover:border-primary",
3652
+ "active:scale-[var(--active-scale)]"
3653
+ ].join(" "),
3654
+ danger: [
3655
+ "bg-surface text-error",
3656
+ "border-[length:var(--border-width)] border-error",
3657
+ "shadow-sm",
3658
+ "hover:bg-error hover:text-error-foreground hover:shadow-lg",
3659
+ "active:scale-[var(--active-scale)] active:shadow-sm"
3660
+ ].join(" "),
3661
+ success: [
3662
+ "bg-surface text-success",
3663
+ "border-[length:var(--border-width)] border-success",
3664
+ "shadow-sm",
3665
+ "hover:bg-success hover:text-success-foreground hover:shadow-lg",
3666
+ "active:scale-[var(--active-scale)] active:shadow-sm"
3667
+ ].join(" "),
3668
+ warning: [
3669
+ "bg-surface text-warning",
3670
+ "border-[length:var(--border-width)] border-warning",
3671
+ "shadow-sm",
3672
+ "hover:bg-warning hover:text-warning-foreground hover:shadow-lg",
3673
+ "active:scale-[var(--active-scale)] active:shadow-sm"
3674
+ ].join(" "),
3675
+ // "default" is an alias for secondary
3676
+ default: [
3677
+ "bg-secondary text-secondary-foreground",
3678
+ "border-[length:var(--border-width-thin)] border-border",
3679
+ "hover:bg-secondary-hover",
3680
+ "active:scale-[var(--active-scale)]"
3681
+ ].join(" ")
2928
3682
  };
2929
- var overflowStyles = {
2930
- auto: "overflow-auto",
2931
- hidden: "overflow-hidden",
2932
- visible: "overflow-visible",
2933
- scroll: "overflow-scroll"
3683
+ variantStyles.destructive = variantStyles.danger;
3684
+ var sizeStyles = {
3685
+ sm: "h-button-sm px-3 text-sm",
3686
+ md: "h-button-md px-4 text-sm",
3687
+ lg: "h-button-lg px-6 text-base"
2934
3688
  };
2935
- var positionStyles = {
2936
- relative: "relative",
2937
- absolute: "absolute",
2938
- fixed: "fixed",
2939
- sticky: "sticky"
3689
+ var iconSizeStyles = {
3690
+ sm: "h-icon-default w-icon-default",
3691
+ md: "h-icon-default w-icon-default",
3692
+ lg: "h-icon-default w-icon-default"
2940
3693
  };
2941
- var Box = React11__default.default.forwardRef(
3694
+ function resolveIconProp(value, sizeClass) {
3695
+ if (!value) return null;
3696
+ if (typeof value === "string") {
3697
+ return /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: value, className: sizeClass });
3698
+ }
3699
+ if (typeof value === "function") {
3700
+ const IconComp = value;
3701
+ return /* @__PURE__ */ jsxRuntime.jsx(IconComp, { className: sizeClass });
3702
+ }
3703
+ if (React11__default.default.isValidElement(value)) {
3704
+ return value;
3705
+ }
3706
+ if (typeof value === "object" && value !== null && "render" in value) {
3707
+ const IconComp = value;
3708
+ return /* @__PURE__ */ jsxRuntime.jsx(IconComp, { className: sizeClass });
3709
+ }
3710
+ return value;
3711
+ }
3712
+ var Button = React11__default.default.forwardRef(
2942
3713
  ({
2943
- padding,
2944
- paddingX,
2945
- paddingY,
2946
- margin,
2947
- marginX,
2948
- marginY,
2949
- bg = "transparent",
2950
- border = false,
2951
- rounded = "none",
2952
- shadow = "none",
2953
- display,
2954
- fullWidth = false,
2955
- fullHeight = false,
2956
- overflow,
2957
- position,
2958
3714
  className,
2959
- children,
2960
- as: Component2 = "div",
3715
+ variant = "primary",
3716
+ size = "md",
3717
+ isLoading = false,
3718
+ disabled,
3719
+ leftIcon,
3720
+ rightIcon,
3721
+ icon: iconProp,
3722
+ iconRight: iconRightProp,
2961
3723
  action,
2962
3724
  actionPayload,
2963
- hoverEvent,
2964
- maxWidth,
3725
+ label,
3726
+ children,
2965
3727
  onClick,
2966
- onMouseEnter,
2967
- onMouseLeave,
2968
- ...rest
3728
+ ...props
2969
3729
  }, ref) => {
2970
3730
  const eventBus = useEventBus();
2971
- const handleClick = React11.useCallback((e) => {
3731
+ const leftIconValue = leftIcon || iconProp;
3732
+ const rightIconValue = rightIcon || iconRightProp;
3733
+ const resolvedLeftIcon = resolveIconProp(leftIconValue, iconSizeStyles[size]);
3734
+ const resolvedRightIcon = resolveIconProp(rightIconValue, iconSizeStyles[size]);
3735
+ const handleClick = (e) => {
2972
3736
  if (action) {
2973
- e.stopPropagation();
2974
3737
  eventBus.emit(`UI:${action}`, actionPayload ?? {});
2975
3738
  }
2976
3739
  onClick?.(e);
2977
- }, [action, actionPayload, eventBus, onClick]);
2978
- const handleMouseEnter = React11.useCallback((e) => {
2979
- if (hoverEvent) {
2980
- eventBus.emit(`UI:${hoverEvent}`, { hovered: true });
2981
- }
2982
- onMouseEnter?.(e);
2983
- }, [hoverEvent, eventBus, onMouseEnter]);
2984
- const handleMouseLeave = React11.useCallback((e) => {
2985
- if (hoverEvent) {
2986
- eventBus.emit(`UI:${hoverEvent}`, { hovered: false });
2987
- }
2988
- onMouseLeave?.(e);
2989
- }, [hoverEvent, eventBus, onMouseLeave]);
2990
- const isClickable = action || onClick;
2991
- return React11__default.default.createElement(
2992
- Component2,
3740
+ };
3741
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3742
+ "button",
2993
3743
  {
2994
3744
  ref,
3745
+ disabled: disabled || isLoading,
2995
3746
  className: cn(
2996
- padding && paddingStyles[padding],
2997
- paddingX && paddingXStyles[paddingX],
2998
- paddingY && paddingYStyles[paddingY],
2999
- margin && marginStyles[margin],
3000
- marginX && marginXStyles[marginX],
3001
- marginY && marginYStyles[marginY],
3002
- bgStyles[bg],
3003
- border && "border-[length:var(--border-width)] border-border",
3004
- roundedStyles[rounded],
3005
- shadowStyles[shadow],
3006
- display && displayStyles[display],
3007
- fullWidth && "w-full",
3008
- fullHeight && "h-full",
3009
- overflow && overflowStyles[overflow],
3010
- position && positionStyles[position],
3011
- isClickable && "cursor-pointer",
3747
+ "inline-flex items-center justify-center gap-2",
3748
+ "font-medium",
3749
+ "rounded-sm",
3750
+ "cursor-pointer",
3751
+ "transition-all duration-[var(--transition-normal)]",
3752
+ "focus:outline-none focus:ring-[length:var(--focus-ring-width)] focus:ring-ring focus:ring-offset-[length:var(--focus-ring-offset)]",
3753
+ "disabled:opacity-50 disabled:cursor-not-allowed",
3754
+ variantStyles[variant],
3755
+ sizeStyles[size],
3012
3756
  className
3013
3757
  ),
3014
- onClick: isClickable ? handleClick : void 0,
3015
- onMouseEnter: hoverEvent || onMouseEnter ? handleMouseEnter : void 0,
3016
- onMouseLeave: hoverEvent || onMouseLeave ? handleMouseLeave : void 0,
3017
- style: maxWidth ? { maxWidth, ...rest.style } : rest.style,
3018
- ...rest
3019
- },
3020
- children
3758
+ onClick: handleClick,
3759
+ ...props,
3760
+ "data-testid": props["data-testid"] ?? (action ? `action-${action}` : void 0),
3761
+ children: [
3762
+ isLoading ? /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Loader2, { className: "h-icon-default w-icon-default animate-spin" }) : resolvedLeftIcon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", children: resolvedLeftIcon }),
3763
+ children || label,
3764
+ resolvedRightIcon && !isLoading && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", children: resolvedRightIcon })
3765
+ ]
3766
+ }
3021
3767
  );
3022
3768
  }
3023
3769
  );
3024
- Box.displayName = "Box";
3025
- var gapStyles = {
3026
- none: "gap-0",
3027
- xs: "gap-1",
3028
- sm: "gap-2",
3029
- md: "gap-4",
3030
- lg: "gap-6",
3031
- xl: "gap-8",
3032
- "2xl": "gap-12"
3033
- };
3034
- var alignStyles = {
3035
- start: "items-start",
3036
- center: "items-center",
3037
- end: "items-end",
3038
- stretch: "items-stretch",
3039
- baseline: "items-baseline"
3040
- };
3041
- var justifyStyles = {
3042
- start: "justify-start",
3043
- center: "justify-center",
3044
- end: "justify-end",
3045
- between: "justify-between",
3046
- around: "justify-around",
3047
- evenly: "justify-evenly"
3048
- };
3049
- var Stack = ({
3050
- direction = "vertical",
3051
- gap = "md",
3052
- align = "stretch",
3053
- justify = "start",
3054
- wrap = false,
3055
- reverse = false,
3056
- flex = false,
3057
- className,
3058
- style,
3059
- children,
3060
- as: Component2 = "div",
3061
- onClick,
3062
- onKeyDown,
3063
- role,
3064
- tabIndex,
3065
- action,
3066
- actionPayload,
3067
- responsive = false
3068
- }) => {
3069
- const eventBus = useEventBus();
3070
- const handleClick = (e) => {
3071
- if (action) {
3072
- eventBus.emit(`UI:${action}`, actionPayload ?? {});
3073
- }
3074
- onClick?.(e);
3075
- };
3076
- const isHorizontal = direction === "horizontal";
3077
- const directionClass = responsive && isHorizontal ? reverse ? "flex-col-reverse md:flex-row-reverse" : "flex-col md:flex-row" : isHorizontal ? reverse ? "flex-row-reverse" : "flex-row" : reverse ? "flex-col-reverse" : "flex-col";
3078
- const Comp = Component2;
3079
- return /* @__PURE__ */ jsxRuntime.jsx(
3080
- Comp,
3081
- {
3082
- className: cn(
3083
- "flex",
3084
- directionClass,
3085
- gapStyles[gap],
3086
- alignStyles[align],
3087
- justifyStyles[justify],
3088
- wrap && "flex-wrap",
3089
- flex && "flex-1",
3090
- className
3091
- ),
3092
- style,
3093
- onClick: action || onClick ? handleClick : void 0,
3094
- onKeyDown,
3095
- role,
3096
- tabIndex,
3097
- children
3098
- }
3099
- );
3100
- };
3101
- var VStack = (props) => /* @__PURE__ */ jsxRuntime.jsx(Stack, { direction: "vertical", ...props });
3102
- var HStack = (props) => /* @__PURE__ */ jsxRuntime.jsx(Stack, { direction: "horizontal", ...props });
3103
- var variantStyles = {
3770
+ Button.displayName = "Button";
3771
+ var variantStyles2 = {
3104
3772
  h1: "text-4xl font-bold tracking-tight text-foreground",
3105
3773
  h2: "text-3xl font-bold tracking-tight text-foreground",
3106
3774
  h3: "text-2xl font-bold text-foreground",
@@ -3161,7 +3829,7 @@ var typographySizeStyles = {
3161
3829
  "2xl": "text-2xl",
3162
3830
  "3xl": "text-3xl"
3163
3831
  };
3164
- var overflowStyles2 = {
3832
+ var overflowStyles = {
3165
3833
  visible: "overflow-visible",
3166
3834
  hidden: "overflow-hidden",
3167
3835
  wrap: "break-words overflow-hidden",
@@ -3192,13 +3860,13 @@ var Typography = ({
3192
3860
  {
3193
3861
  id,
3194
3862
  className: cn(
3195
- variantStyles[variant],
3863
+ variantStyles2[variant],
3196
3864
  colorStyles[color],
3197
3865
  weight && weightStyles[weight],
3198
3866
  size && typographySizeStyles[size],
3199
3867
  align && `text-${align}`,
3200
3868
  truncate && "truncate overflow-hidden text-ellipsis",
3201
- overflow && overflowStyles2[overflow],
3869
+ overflow && overflowStyles[overflow],
3202
3870
  className
3203
3871
  ),
3204
3872
  style,
@@ -3207,6 +3875,459 @@ var Typography = ({
3207
3875
  );
3208
3876
  };
3209
3877
  Typography.displayName = "Typography";
3878
+ var gapStyles = {
3879
+ none: "gap-0",
3880
+ xs: "gap-1",
3881
+ sm: "gap-2",
3882
+ md: "gap-4",
3883
+ lg: "gap-6",
3884
+ xl: "gap-8",
3885
+ "2xl": "gap-12"
3886
+ };
3887
+ var alignStyles = {
3888
+ start: "items-start",
3889
+ center: "items-center",
3890
+ end: "items-end",
3891
+ stretch: "items-stretch",
3892
+ baseline: "items-baseline"
3893
+ };
3894
+ var justifyStyles = {
3895
+ start: "justify-start",
3896
+ center: "justify-center",
3897
+ end: "justify-end",
3898
+ between: "justify-between",
3899
+ around: "justify-around",
3900
+ evenly: "justify-evenly"
3901
+ };
3902
+ var Stack = ({
3903
+ direction = "vertical",
3904
+ gap = "md",
3905
+ align = "stretch",
3906
+ justify = "start",
3907
+ wrap = false,
3908
+ reverse = false,
3909
+ flex = false,
3910
+ className,
3911
+ style,
3912
+ children,
3913
+ as: Component2 = "div",
3914
+ onClick,
3915
+ onKeyDown,
3916
+ role,
3917
+ tabIndex,
3918
+ action,
3919
+ actionPayload,
3920
+ responsive = false
3921
+ }) => {
3922
+ const eventBus = useEventBus();
3923
+ const handleClick = (e) => {
3924
+ if (action) {
3925
+ eventBus.emit(`UI:${action}`, actionPayload ?? {});
3926
+ }
3927
+ onClick?.(e);
3928
+ };
3929
+ const isHorizontal = direction === "horizontal";
3930
+ const directionClass = responsive && isHorizontal ? reverse ? "flex-col-reverse md:flex-row-reverse" : "flex-col md:flex-row" : isHorizontal ? reverse ? "flex-row-reverse" : "flex-row" : reverse ? "flex-col-reverse" : "flex-col";
3931
+ const Comp = Component2;
3932
+ return /* @__PURE__ */ jsxRuntime.jsx(
3933
+ Comp,
3934
+ {
3935
+ className: cn(
3936
+ "flex",
3937
+ directionClass,
3938
+ gapStyles[gap],
3939
+ alignStyles[align],
3940
+ justifyStyles[justify],
3941
+ wrap && "flex-wrap",
3942
+ flex && "flex-1",
3943
+ className
3944
+ ),
3945
+ style,
3946
+ onClick: action || onClick ? handleClick : void 0,
3947
+ onKeyDown,
3948
+ role,
3949
+ tabIndex,
3950
+ children
3951
+ }
3952
+ );
3953
+ };
3954
+ var VStack = (props) => /* @__PURE__ */ jsxRuntime.jsx(Stack, { direction: "vertical", ...props });
3955
+ var HStack = (props) => /* @__PURE__ */ jsxRuntime.jsx(Stack, { direction: "horizontal", ...props });
3956
+
3957
+ // components/game/organisms/boardEntity.ts
3958
+ function boardEntity(entity) {
3959
+ if (!entity) return void 0;
3960
+ return Array.isArray(entity) ? entity[0] : entity;
3961
+ }
3962
+ function str(v) {
3963
+ return v == null ? "" : String(v);
3964
+ }
3965
+ function num(v, fallback = 0) {
3966
+ const n = Number(v);
3967
+ return Number.isFinite(n) ? n : fallback;
3968
+ }
3969
+ function rows(v) {
3970
+ return Array.isArray(v) ? v : [];
3971
+ }
3972
+ function vec2(v) {
3973
+ const o = v ?? {};
3974
+ return { x: num(o.x), y: num(o.y) };
3975
+ }
3976
+ function unitPosition(u) {
3977
+ return vec2(u.position);
3978
+ }
3979
+ function unitTeam(u) {
3980
+ return str(u.team);
3981
+ }
3982
+ function unitHealth(u) {
3983
+ return num(u.health);
3984
+ }
3985
+ function GameBoard3D({
3986
+ entity,
3987
+ tiles = [],
3988
+ features = [],
3989
+ cameraMode = "perspective",
3990
+ backgroundColor = "#2a1a1a",
3991
+ tileClickEvent,
3992
+ unitClickEvent,
3993
+ attackEvent,
3994
+ endTurnEvent,
3995
+ cancelEvent,
3996
+ playAgainEvent,
3997
+ gameEndEvent,
3998
+ className
3999
+ }) {
4000
+ const row = boardEntity(entity);
4001
+ const eventBus = useEventBus();
4002
+ const entityUnits = row ? rows(row.units) : [];
4003
+ const units = entityUnits;
4004
+ const selectedUnitId = row ? str(row.selectedUnitId) || null : null;
4005
+ const phase = row ? str(row.phase) : "observation";
4006
+ const result = row ? str(row.result) : "none";
4007
+ const validMoves = row && Array.isArray(row.validMoves) ? row.validMoves : [];
4008
+ const attackTargets = row && Array.isArray(row.attackTargets) ? row.attackTargets : [];
4009
+ const turn = row ? num(row.turn) : 0;
4010
+ const currentTeam = row ? str(row.currentTeam) : "player";
4011
+ const isGameOver = result !== "none";
4012
+ const gameEndEmittedRef = React11.useRef(false);
4013
+ React11.useEffect(() => {
4014
+ if ((result === "victory" || result === "defeat") && gameEndEvent) {
4015
+ if (!gameEndEmittedRef.current) {
4016
+ gameEndEmittedRef.current = true;
4017
+ eventBus.emit(`UI:${gameEndEvent}`, { result });
4018
+ }
4019
+ } else {
4020
+ gameEndEmittedRef.current = false;
4021
+ }
4022
+ }, [result, gameEndEvent, eventBus]);
4023
+ const checkGameEnd = React11.useCallback(() => {
4024
+ const alivePlayer = entityUnits.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
4025
+ const aliveEnemy = entityUnits.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
4026
+ if (alivePlayer.length === 0 && gameEndEvent) {
4027
+ eventBus.emit(`UI:${gameEndEvent}`, { result: "defeat" });
4028
+ } else if (aliveEnemy.length === 0 && gameEndEvent) {
4029
+ eventBus.emit(`UI:${gameEndEvent}`, { result: "victory" });
4030
+ }
4031
+ }, [entityUnits, gameEndEvent, eventBus]);
4032
+ const handleUnitClickCallback = React11.useCallback((isoUnit) => {
4033
+ if (phase !== "action" || !selectedUnitId || !attackEvent) return;
4034
+ const attackerRow = entityUnits.find((u) => str(u.id) === selectedUnitId);
4035
+ const targetRow = entityUnits.find((u) => str(u.id) === isoUnit.id);
4036
+ if (!attackerRow || !targetRow) return;
4037
+ if (unitTeam(targetRow) !== "enemy" || unitHealth(targetRow) <= 0) return;
4038
+ const ap = unitPosition(attackerRow);
4039
+ const tp = unitPosition(targetRow);
4040
+ const dx = Math.abs(ap.x - tp.x);
4041
+ const dy = Math.abs(ap.y - tp.y);
4042
+ if (dx <= 1 && dy <= 1 && dx + dy > 0) {
4043
+ const damage = Math.max(1, num(attackerRow.attack) - num(targetRow.defense));
4044
+ eventBus.emit(`UI:${attackEvent}`, {
4045
+ attackerId: str(attackerRow.id),
4046
+ targetId: str(targetRow.id),
4047
+ damage
4048
+ });
4049
+ setTimeout(checkGameEnd, 100);
4050
+ }
4051
+ }, [phase, selectedUnitId, entityUnits, attackEvent, eventBus, checkGameEnd]);
4052
+ const handleEndTurn = React11.useCallback(() => {
4053
+ if (endTurnEvent) eventBus.emit(`UI:${endTurnEvent}`, {});
4054
+ }, [endTurnEvent, eventBus]);
4055
+ const handleCancel = React11.useCallback(() => {
4056
+ if (cancelEvent) eventBus.emit(`UI:${cancelEvent}`, {});
4057
+ }, [cancelEvent, eventBus]);
4058
+ const handlePlayAgain = React11.useCallback(() => {
4059
+ if (playAgainEvent) eventBus.emit(`UI:${playAgainEvent}`, {});
4060
+ }, [playAgainEvent, eventBus]);
4061
+ return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("game-board-3d block w-full min-h-[85vh] relative", className), children: [
4062
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-board-3d__status absolute top-3 left-3 z-10 flex gap-2 items-center", children: [
4063
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "status__phase capitalize", children: phase.replace("_", " ") }),
4064
+ currentTeam && !isGameOver && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", color: "muted", className: "status__team", children: [
4065
+ "\u2014 ",
4066
+ currentTeam === "player" ? "Your Turn" : "Enemy's Turn"
4067
+ ] }),
4068
+ /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", color: "muted", className: "status__turn", children: [
4069
+ "Turn ",
4070
+ turn
4071
+ ] })
4072
+ ] }),
4073
+ /* @__PURE__ */ jsxRuntime.jsx(
4074
+ GameCanvas3D,
4075
+ {
4076
+ tiles,
4077
+ units,
4078
+ features,
4079
+ cameraMode,
4080
+ showGrid: true,
4081
+ showCoordinates: false,
4082
+ showTileInfo: false,
4083
+ shadows: true,
4084
+ backgroundColor,
4085
+ tileClickEvent,
4086
+ unitClickEvent,
4087
+ onUnitClick: handleUnitClickCallback,
4088
+ selectedUnitId,
4089
+ validMoves,
4090
+ attackTargets,
4091
+ className: "game-board-3d__canvas w-full min-h-[85vh]"
4092
+ }
4093
+ ),
4094
+ !isGameOver && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "fixed bottom-6 right-6 z-50", gap: "sm", children: [
4095
+ (phase === "selection" || phase === "movement" || phase === "action") && /* @__PURE__ */ jsxRuntime.jsx(
4096
+ Button,
4097
+ {
4098
+ variant: "secondary",
4099
+ className: "shadow-xl",
4100
+ onClick: handleCancel,
4101
+ children: "Cancel"
4102
+ }
4103
+ ),
4104
+ phase !== "enemy_turn" && /* @__PURE__ */ jsxRuntime.jsx(
4105
+ Button,
4106
+ {
4107
+ variant: "primary",
4108
+ className: "shadow-xl",
4109
+ onClick: handleEndTurn,
4110
+ children: "End Turn"
4111
+ }
4112
+ )
4113
+ ] }),
4114
+ isGameOver && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "game-board-3d__overlay absolute inset-0 z-20 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { align: "center", gap: "md", className: "overlay__card p-8 rounded-xl bg-gray-900/90 shadow-2xl", children: [
4115
+ /* @__PURE__ */ jsxRuntime.jsx(
4116
+ Typography,
4117
+ {
4118
+ variant: "h2",
4119
+ className: cn(
4120
+ "overlay__result",
4121
+ result === "victory" ? "text-yellow-400" : "text-red-500"
4122
+ ),
4123
+ children: result === "victory" ? "Victory!" : "Defeat"
4124
+ }
4125
+ ),
4126
+ /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "body", color: "muted", className: "overlay__turn-count", children: [
4127
+ "Completed in ",
4128
+ turn,
4129
+ " turn",
4130
+ turn !== 1 ? "s" : ""
4131
+ ] }),
4132
+ /* @__PURE__ */ jsxRuntime.jsx(
4133
+ Button,
4134
+ {
4135
+ variant: "primary",
4136
+ className: "px-8 py-3 font-semibold",
4137
+ onClick: handlePlayAgain,
4138
+ children: "Play Again"
4139
+ }
4140
+ )
4141
+ ] }) })
4142
+ ] });
4143
+ }
4144
+ GameBoard3D.displayName = "GameBoard3D";
4145
+ var paddingStyles = {
4146
+ none: "p-0",
4147
+ xs: "p-1",
4148
+ sm: "p-2",
4149
+ md: "p-4",
4150
+ lg: "p-6",
4151
+ xl: "p-8",
4152
+ "2xl": "p-12"
4153
+ };
4154
+ var paddingXStyles = {
4155
+ none: "px-0",
4156
+ xs: "px-1",
4157
+ sm: "px-2",
4158
+ md: "px-4",
4159
+ lg: "px-6",
4160
+ xl: "px-8",
4161
+ "2xl": "px-12"
4162
+ };
4163
+ var paddingYStyles = {
4164
+ none: "py-0",
4165
+ xs: "py-1",
4166
+ sm: "py-2",
4167
+ md: "py-4",
4168
+ lg: "py-6",
4169
+ xl: "py-8",
4170
+ "2xl": "py-12"
4171
+ };
4172
+ var marginStyles = {
4173
+ none: "m-0",
4174
+ xs: "m-1",
4175
+ sm: "m-2",
4176
+ md: "m-4",
4177
+ lg: "m-6",
4178
+ xl: "m-8",
4179
+ "2xl": "m-12",
4180
+ auto: "m-auto"
4181
+ };
4182
+ var marginXStyles = {
4183
+ none: "mx-0",
4184
+ xs: "mx-1",
4185
+ sm: "mx-2",
4186
+ md: "mx-4",
4187
+ lg: "mx-6",
4188
+ xl: "mx-8",
4189
+ "2xl": "mx-12",
4190
+ auto: "mx-auto"
4191
+ };
4192
+ var marginYStyles = {
4193
+ none: "my-0",
4194
+ xs: "my-1",
4195
+ sm: "my-2",
4196
+ md: "my-4",
4197
+ lg: "my-6",
4198
+ xl: "my-8",
4199
+ "2xl": "my-12",
4200
+ auto: "my-auto"
4201
+ };
4202
+ var bgStyles = {
4203
+ transparent: "bg-transparent",
4204
+ primary: "bg-primary text-primary-foreground",
4205
+ secondary: "bg-secondary text-secondary-foreground",
4206
+ muted: "bg-muted text-foreground",
4207
+ accent: "bg-accent text-accent-foreground",
4208
+ surface: "bg-card",
4209
+ overlay: "bg-card/80 backdrop-blur-sm"
4210
+ };
4211
+ var roundedStyles = {
4212
+ none: "rounded-none",
4213
+ sm: "rounded-sm",
4214
+ md: "rounded-md",
4215
+ lg: "rounded-lg",
4216
+ xl: "rounded-xl",
4217
+ "2xl": "rounded-xl",
4218
+ full: "rounded-full"
4219
+ };
4220
+ var shadowStyles = {
4221
+ none: "shadow-none",
4222
+ sm: "shadow-sm",
4223
+ md: "shadow",
4224
+ lg: "shadow-lg",
4225
+ xl: "shadow-lg"
4226
+ };
4227
+ var displayStyles = {
4228
+ block: "block",
4229
+ inline: "inline",
4230
+ "inline-block": "inline-block",
4231
+ flex: "flex",
4232
+ "inline-flex": "inline-flex",
4233
+ grid: "grid"
4234
+ };
4235
+ var overflowStyles2 = {
4236
+ auto: "overflow-auto",
4237
+ hidden: "overflow-hidden",
4238
+ visible: "overflow-visible",
4239
+ scroll: "overflow-scroll"
4240
+ };
4241
+ var positionStyles = {
4242
+ relative: "relative",
4243
+ absolute: "absolute",
4244
+ fixed: "fixed",
4245
+ sticky: "sticky"
4246
+ };
4247
+ var Box = React11__default.default.forwardRef(
4248
+ ({
4249
+ padding,
4250
+ paddingX,
4251
+ paddingY,
4252
+ margin,
4253
+ marginX,
4254
+ marginY,
4255
+ bg = "transparent",
4256
+ border = false,
4257
+ rounded = "none",
4258
+ shadow = "none",
4259
+ display,
4260
+ fullWidth = false,
4261
+ fullHeight = false,
4262
+ overflow,
4263
+ position,
4264
+ className,
4265
+ children,
4266
+ as: Component2 = "div",
4267
+ action,
4268
+ actionPayload,
4269
+ hoverEvent,
4270
+ maxWidth,
4271
+ onClick,
4272
+ onMouseEnter,
4273
+ onMouseLeave,
4274
+ ...rest
4275
+ }, ref) => {
4276
+ const eventBus = useEventBus();
4277
+ const handleClick = React11.useCallback((e) => {
4278
+ if (action) {
4279
+ e.stopPropagation();
4280
+ eventBus.emit(`UI:${action}`, actionPayload ?? {});
4281
+ }
4282
+ onClick?.(e);
4283
+ }, [action, actionPayload, eventBus, onClick]);
4284
+ const handleMouseEnter = React11.useCallback((e) => {
4285
+ if (hoverEvent) {
4286
+ eventBus.emit(`UI:${hoverEvent}`, { hovered: true });
4287
+ }
4288
+ onMouseEnter?.(e);
4289
+ }, [hoverEvent, eventBus, onMouseEnter]);
4290
+ const handleMouseLeave = React11.useCallback((e) => {
4291
+ if (hoverEvent) {
4292
+ eventBus.emit(`UI:${hoverEvent}`, { hovered: false });
4293
+ }
4294
+ onMouseLeave?.(e);
4295
+ }, [hoverEvent, eventBus, onMouseLeave]);
4296
+ const isClickable = action || onClick;
4297
+ return React11__default.default.createElement(
4298
+ Component2,
4299
+ {
4300
+ ref,
4301
+ className: cn(
4302
+ padding && paddingStyles[padding],
4303
+ paddingX && paddingXStyles[paddingX],
4304
+ paddingY && paddingYStyles[paddingY],
4305
+ margin && marginStyles[margin],
4306
+ marginX && marginXStyles[marginX],
4307
+ marginY && marginYStyles[marginY],
4308
+ bgStyles[bg],
4309
+ border && "border-[length:var(--border-width)] border-border",
4310
+ roundedStyles[rounded],
4311
+ shadowStyles[shadow],
4312
+ display && displayStyles[display],
4313
+ fullWidth && "w-full",
4314
+ fullHeight && "h-full",
4315
+ overflow && overflowStyles2[overflow],
4316
+ position && positionStyles[position],
4317
+ isClickable && "cursor-pointer",
4318
+ className
4319
+ ),
4320
+ onClick: isClickable ? handleClick : void 0,
4321
+ onMouseEnter: hoverEvent || onMouseEnter ? handleMouseEnter : void 0,
4322
+ onMouseLeave: hoverEvent || onMouseLeave ? handleMouseLeave : void 0,
4323
+ style: maxWidth ? { maxWidth, ...rest.style } : rest.style,
4324
+ ...rest
4325
+ },
4326
+ children
4327
+ );
4328
+ }
4329
+ );
4330
+ Box.displayName = "Box";
3210
4331
  var DEFAULT_3D_BATTLE_TILES = [
3211
4332
  { id: "t00", x: 0, y: 0, z: 0, type: "stone", passable: false },
3212
4333
  { id: "t10", x: 1, y: 0, z: 0, type: "stone", passable: false },
@@ -3234,10 +4355,6 @@ var DEFAULT_3D_BATTLE_TILES = [
3234
4355
  { id: "t34", x: 3, y: 4, z: 4, type: "stone", passable: false },
3235
4356
  { id: "t44", x: 4, y: 4, z: 4, type: "stone", passable: false }
3236
4357
  ];
3237
- var DEFAULT_3D_BATTLE_UNITS = [
3238
- { id: "u1", x: 1, y: 1, z: 1, unitType: "warrior", name: "Worker", faction: "player", health: 10, maxHealth: 10 },
3239
- { id: "u2", x: 3, y: 3, z: 3, unitType: "enemy", name: "Guardian", faction: "enemy", health: 8, maxHealth: 10 }
3240
- ];
3241
4358
  var DEFAULT_3D_BATTLE_FEATURES = [
3242
4359
  { id: "f1", x: 2, y: 2, z: 2, type: "gold_mine", color: "#f4c542" },
3243
4360
  { id: "f2", x: 3, y: 1, z: 1, type: "portal", color: "#8b5cf6" }
@@ -3245,53 +4362,44 @@ var DEFAULT_3D_BATTLE_FEATURES = [
3245
4362
  function GameCanvas3DBattleTemplate({
3246
4363
  entity,
3247
4364
  tiles: propTiles = DEFAULT_3D_BATTLE_TILES,
3248
- units: propUnits = DEFAULT_3D_BATTLE_UNITS,
3249
4365
  features: propFeatures = DEFAULT_3D_BATTLE_FEATURES,
3250
4366
  cameraMode = "perspective",
3251
- showGrid = true,
3252
- shadows = true,
3253
4367
  backgroundColor = "#2a1a1a",
3254
4368
  tileClickEvent,
3255
4369
  unitClickEvent,
3256
- unitAttackEvent,
3257
- unitMoveEvent,
3258
4370
  endTurnEvent,
3259
- exitEvent,
3260
- selectedUnitId,
3261
- validMoves,
3262
- attackTargets,
4371
+ cancelEvent,
4372
+ attackEvent,
4373
+ playAgainEvent,
4374
+ gameEndEvent,
3263
4375
  className
3264
4376
  }) {
3265
4377
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
3266
4378
  const tiles = resolved ? Array.isArray(resolved.tiles) ? resolved.tiles : [] : propTiles;
3267
- const units = resolved ? Array.isArray(resolved.units) ? resolved.units : [] : propUnits;
3268
4379
  const features = resolved ? Array.isArray(resolved.features) ? resolved.features : [] : propFeatures;
3269
- const currentTurn = resolved?.currentTurn;
3270
- const round = resolved?.round == null ? void 0 : Number(resolved.round);
4380
+ const currentTurn = resolved?.currentTeam;
4381
+ const round = resolved?.turn == null ? void 0 : Number(resolved.turn);
3271
4382
  return /* @__PURE__ */ jsxRuntime.jsxs(
3272
4383
  Box,
3273
4384
  {
3274
- className: cn("game-canvas-3d-battle-template", className),
3275
- style: { display: "block", position: "relative", width: "100%", minHeight: "85vh" },
4385
+ className: cn("game-canvas-3d-battle-template block relative w-full min-h-[85vh]", className),
3276
4386
  children: [
3277
4387
  /* @__PURE__ */ jsxRuntime.jsx(
3278
- GameCanvas3D,
4388
+ GameBoard3D,
3279
4389
  {
4390
+ entity,
3280
4391
  tiles,
3281
- units,
3282
4392
  features,
3283
4393
  cameraMode,
3284
- showGrid,
3285
- showCoordinates: false,
3286
- showTileInfo: false,
3287
- shadows,
3288
4394
  backgroundColor,
3289
4395
  tileClickEvent,
3290
4396
  unitClickEvent,
3291
- selectedUnitId,
3292
- validMoves,
3293
- attackTargets,
3294
- className: "game-canvas-3d-battle-template__canvas"
4397
+ endTurnEvent,
4398
+ cancelEvent,
4399
+ attackEvent,
4400
+ playAgainEvent,
4401
+ gameEndEvent,
4402
+ className: "game-canvas-3d-battle-template__board"
3295
4403
  }
3296
4404
  ),
3297
4405
  currentTurn && /* @__PURE__ */ jsxRuntime.jsxs(
@@ -3299,8 +4407,7 @@ function GameCanvas3DBattleTemplate({
3299
4407
  {
3300
4408
  gap: "sm",
3301
4409
  align: "center",
3302
- className: cn("battle-template__turn-indicator", `battle-template__turn-indicator--${currentTurn}`),
3303
- style: { position: "absolute", top: "12px", right: "12px", zIndex: 10 },
4410
+ className: cn("battle-template__turn-indicator absolute top-3 right-3 z-10", `battle-template__turn-indicator--${currentTurn}`),
3304
4411
  children: [
3305
4412
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: "turn-indicator__label", children: currentTurn === "player" ? "Your Turn" : "Enemy's Turn" }),
3306
4413
  round != null && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", className: "turn-indicator__round", children: [
@@ -3342,10 +4449,6 @@ var DEFAULT_3D_CASTLE_TILES = [
3342
4449
  { id: "t34", x: 3, y: 4, z: 4, type: "wall", passable: false },
3343
4450
  { id: "t44", x: 4, y: 4, z: 4, type: "wall", passable: false }
3344
4451
  ];
3345
- var DEFAULT_3D_CASTLE_UNITS = [
3346
- { id: "u1", x: 1, y: 1, z: 1, unitType: "worker", name: "Worker", faction: "player", health: 10, maxHealth: 10 },
3347
- { id: "u2", x: 3, y: 3, z: 3, unitType: "guardian", name: "Guardian", faction: "player", health: 10, maxHealth: 10 }
3348
- ];
3349
4452
  var DEFAULT_3D_CASTLE_FEATURES = [
3350
4453
  { id: "f1", x: 2, y: 2, z: 2, type: "chest", color: "#f4c542" },
3351
4454
  { id: "f2", x: 3, y: 1, z: 1, type: "crystal", color: "#8b5cf6" }
@@ -3353,35 +4456,30 @@ var DEFAULT_3D_CASTLE_FEATURES = [
3353
4456
  function GameCanvas3DCastleTemplate({
3354
4457
  entity,
3355
4458
  tiles: propTiles = DEFAULT_3D_CASTLE_TILES,
3356
- units: propUnits = DEFAULT_3D_CASTLE_UNITS,
3357
4459
  features: propFeatures = DEFAULT_3D_CASTLE_FEATURES,
3358
4460
  cameraMode = "isometric",
3359
- showGrid = true,
3360
- shadows = true,
3361
4461
  backgroundColor = "#1e1e2e",
3362
4462
  buildingClickEvent,
3363
4463
  unitClickEvent,
3364
- buildEvent,
3365
- recruitEvent,
3366
- exitEvent,
3367
- selectedBuildingId,
3368
- selectedTileIds = [],
3369
- availableBuildSites,
4464
+ endTurnEvent,
4465
+ cancelEvent,
4466
+ attackEvent,
4467
+ playAgainEvent,
4468
+ gameEndEvent,
3370
4469
  showHeader = true,
3371
4470
  className
3372
4471
  }) {
3373
4472
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
3374
4473
  const tiles = resolved ? Array.isArray(resolved.tiles) ? resolved.tiles : [] : propTiles;
3375
- const units = resolved ? Array.isArray(resolved.units) ? resolved.units : [] : propUnits;
3376
4474
  const features = resolved ? Array.isArray(resolved.features) ? resolved.features : [] : propFeatures;
3377
4475
  const name = resolved?.name == null ? void 0 : String(resolved.name);
3378
4476
  const level = resolved?.level == null ? void 0 : Number(resolved.level);
3379
4477
  const owner = resolved?.owner == null ? void 0 : String(resolved.owner);
4478
+ const unitCount = resolved && Array.isArray(resolved.units) ? resolved.units.length : 0;
3380
4479
  return /* @__PURE__ */ jsxRuntime.jsxs(
3381
4480
  VStack,
3382
4481
  {
3383
- className: cn("game-canvas-3d-castle-template", className),
3384
- style: { display: "block", width: "100%", minHeight: "85vh" },
4482
+ className: cn("game-canvas-3d-castle-template block w-full min-h-[85vh]", className),
3385
4483
  children: [
3386
4484
  showHeader && name && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "md", align: "center", className: "castle-template__header", children: [
3387
4485
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", className: "header__name", children: name }),
@@ -3392,28 +4490,27 @@ function GameCanvas3DCastleTemplate({
3392
4490
  owner && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "muted", className: "header__owner", children: owner })
3393
4491
  ] }),
3394
4492
  /* @__PURE__ */ jsxRuntime.jsx(
3395
- GameCanvas3D,
4493
+ GameBoard3D,
3396
4494
  {
4495
+ entity,
3397
4496
  tiles,
3398
- units,
3399
4497
  features,
3400
4498
  cameraMode,
3401
- showGrid,
3402
- showCoordinates: false,
3403
- showTileInfo: false,
3404
- shadows,
3405
4499
  backgroundColor,
3406
- featureClickEvent: buildingClickEvent,
4500
+ tileClickEvent: buildingClickEvent,
3407
4501
  unitClickEvent,
3408
- selectedTileIds,
3409
- validMoves: availableBuildSites,
3410
- className: "game-canvas-3d-castle-template__canvas"
4502
+ endTurnEvent,
4503
+ cancelEvent,
4504
+ attackEvent,
4505
+ playAgainEvent,
4506
+ gameEndEvent,
4507
+ className: "game-canvas-3d-castle-template__board"
3411
4508
  }
3412
4509
  ),
3413
- units.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", align: "center", className: "castle-template__garrison-info", children: [
4510
+ unitCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", align: "center", className: "castle-template__garrison-info", children: [
3414
4511
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "garrison-info__label", children: "Garrison:" }),
3415
4512
  /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", weight: "bold", className: "garrison-info__count", children: [
3416
- units.length,
4513
+ unitCount,
3417
4514
  " units"
3418
4515
  ] })
3419
4516
  ] })
@@ -3449,10 +4546,6 @@ var DEFAULT_3D_WORLDMAP_TILES = [
3449
4546
  { id: "t34", x: 3, y: 4, z: 4, type: "mountain", passable: false },
3450
4547
  { id: "t44", x: 4, y: 4, z: 4, type: "mountain", passable: false }
3451
4548
  ];
3452
- var DEFAULT_3D_WORLDMAP_UNITS = [
3453
- { id: "h1", x: 1, y: 1, z: 1, unitType: "hero", name: "Amir", faction: "player", health: 10, maxHealth: 10 },
3454
- { id: "h2", x: 3, y: 3, z: 3, unitType: "scout", name: "Archivist", faction: "player", health: 10, maxHealth: 10 }
3455
- ];
3456
4549
  var DEFAULT_3D_WORLDMAP_FEATURES = [
3457
4550
  { id: "f1", x: 2, y: 2, z: 2, type: "capital", color: "#f4c542" },
3458
4551
  { id: "f2", x: 4, y: 2, z: 2, type: "power_node", color: "#8b5cf6" }
@@ -3460,52 +4553,41 @@ var DEFAULT_3D_WORLDMAP_FEATURES = [
3460
4553
  function GameCanvas3DWorldMapTemplate({
3461
4554
  entity,
3462
4555
  tiles: propTiles = DEFAULT_3D_WORLDMAP_TILES,
3463
- units: propUnits = DEFAULT_3D_WORLDMAP_UNITS,
3464
4556
  features: propFeatures = DEFAULT_3D_WORLDMAP_FEATURES,
3465
4557
  cameraMode = "isometric",
3466
- showGrid = true,
3467
- showCoordinates = true,
3468
- showTileInfo = true,
3469
- shadows = true,
3470
4558
  backgroundColor = "#1a1a2e",
3471
4559
  tileClickEvent,
3472
4560
  unitClickEvent,
3473
- featureClickEvent,
3474
- tileHoverEvent,
3475
- tileLeaveEvent,
3476
- cameraChangeEvent,
3477
- selectedUnitId,
3478
- validMoves,
3479
- attackTargets,
4561
+ endTurnEvent,
4562
+ cancelEvent,
4563
+ attackEvent,
4564
+ playAgainEvent,
4565
+ gameEndEvent,
3480
4566
  className
3481
4567
  }) {
3482
4568
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
3483
4569
  const tiles = resolved ? Array.isArray(resolved.tiles) ? resolved.tiles : [] : propTiles;
3484
- const units = resolved ? Array.isArray(resolved.units) ? resolved.units : [] : propUnits;
3485
4570
  const features = resolved ? Array.isArray(resolved.features) ? resolved.features : [] : propFeatures;
3486
- return /* @__PURE__ */ jsxRuntime.jsx(
3487
- GameCanvas3D,
3488
- {
3489
- tiles,
3490
- units,
3491
- features,
3492
- cameraMode,
3493
- showGrid,
3494
- showCoordinates,
3495
- showTileInfo,
3496
- shadows,
3497
- backgroundColor,
3498
- tileClickEvent,
3499
- unitClickEvent,
3500
- featureClickEvent,
3501
- tileHoverEvent,
3502
- tileLeaveEvent,
3503
- cameraChangeEvent,
3504
- selectedUnitId,
3505
- validMoves,
3506
- attackTargets,
3507
- className
3508
- }
4571
+ return (
4572
+ /* GameBoard3D reads selectedUnitId/validMoves/attackTargets from entity */
4573
+ /* @__PURE__ */ jsxRuntime.jsx(
4574
+ GameBoard3D,
4575
+ {
4576
+ entity,
4577
+ tiles,
4578
+ features,
4579
+ cameraMode,
4580
+ backgroundColor,
4581
+ tileClickEvent,
4582
+ unitClickEvent,
4583
+ endTurnEvent,
4584
+ cancelEvent,
4585
+ attackEvent,
4586
+ playAgainEvent,
4587
+ gameEndEvent,
4588
+ className
4589
+ }
4590
+ )
3509
4591
  );
3510
4592
  }
3511
4593
  GameCanvas3DWorldMapTemplate.displayName = "GameCanvas3DWorldMapTemplate";
@@ -3697,10 +4779,10 @@ function parseApplicationLevel(schema) {
3697
4779
  }
3698
4780
  const count = schema.orbitals.length;
3699
4781
  const cols = Math.ceil(Math.sqrt(count));
3700
- const rows = Math.ceil(count / cols);
4782
+ const rows2 = Math.ceil(count / cols);
3701
4783
  const spacing = 200;
3702
4784
  const gridW = cols * spacing;
3703
- const gridH = rows * spacing;
4785
+ const gridH = rows2 * spacing;
3704
4786
  const originX = (600 - gridW) / 2 + spacing / 2;
3705
4787
  const originY = (400 - gridH) / 2 + spacing / 2;
3706
4788
  schema.orbitals.forEach((orbital, i) => {
@@ -4232,7 +5314,7 @@ Avl3DLabel.displayName = "Avl3DLabel";
4232
5314
  var Avl3DTooltip = ({
4233
5315
  position,
4234
5316
  title,
4235
- rows,
5317
+ rows: rows2,
4236
5318
  accentColor = "#5b9bd5"
4237
5319
  }) => {
4238
5320
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -4270,7 +5352,7 @@ var Avl3DTooltip = ({
4270
5352
  children: title
4271
5353
  }
4272
5354
  ),
4273
- rows.map((row) => /* @__PURE__ */ jsxRuntime.jsxs(
5355
+ rows2.map((row) => /* @__PURE__ */ jsxRuntime.jsxs(
4274
5356
  Box,
4275
5357
  {
4276
5358
  style: {