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