@almadar/ui 5.48.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 (35) hide show
  1. package/dist/avl/index.cjs +2681 -148
  2. package/dist/avl/index.css +504 -0
  3. package/dist/avl/index.js +2681 -149
  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/ControlButton.d.ts +4 -2
  7. package/dist/components/game/atoms/DamageNumber.d.ts +4 -1
  8. package/dist/components/game/atoms/ScoreDisplay.d.ts +4 -1
  9. package/dist/components/game/atoms/TurnIndicator.d.ts +4 -1
  10. package/dist/components/game/molecules/EnemyPlate.d.ts +4 -1
  11. package/dist/components/game/molecules/StatBadge.d.ts +4 -1
  12. package/dist/components/game/molecules/three/index.cjs +1371 -283
  13. package/dist/components/game/molecules/three/index.js +1372 -284
  14. package/dist/components/game/organisms/GameBoard3D.d.ts +62 -0
  15. package/dist/components/game/organisms/PlatformerBoard.d.ts +51 -0
  16. package/dist/components/game/organisms/RoguelikeBoard.d.ts +59 -0
  17. package/dist/components/game/organisms/TowerDefenseBoard.d.ts +64 -0
  18. package/dist/components/game/organisms/index.d.ts +4 -0
  19. package/dist/components/game/templates/GameCanvas3DBattleTemplate.d.ts +22 -35
  20. package/dist/components/game/templates/GameCanvas3DCastleTemplate.d.ts +23 -23
  21. package/dist/components/game/templates/GameCanvas3DWorldMapTemplate.d.ts +27 -29
  22. package/dist/components/game/templates/PlatformerTemplate.d.ts +26 -0
  23. package/dist/components/game/templates/RoguelikeTemplate.d.ts +23 -0
  24. package/dist/components/game/templates/TowerDefenseTemplate.d.ts +43 -0
  25. package/dist/components/index.cjs +2674 -127
  26. package/dist/components/index.css +504 -0
  27. package/dist/components/index.js +2668 -129
  28. package/dist/docs/index.css +504 -0
  29. package/dist/providers/index.cjs +2664 -131
  30. package/dist/providers/index.css +504 -0
  31. package/dist/providers/index.js +2664 -132
  32. package/dist/runtime/index.cjs +2665 -132
  33. package/dist/runtime/index.css +504 -0
  34. package/dist/runtime/index.js +2665 -133
  35. package/package.json +1 -1
@@ -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;
@@ -2833,6 +2835,1313 @@ var GameCanvas3D = React11.forwardRef(
2833
2835
  }
2834
2836
  );
2835
2837
  GameCanvas3D.displayName = "GameCanvas3D";
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;
2853
+ }
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
2947
+ };
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"
3114
+ };
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"
3294
+ };
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"
3479
+ };
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"
3560
+ };
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"
3567
+ };
3568
+ var animationClasses = {
3569
+ none: "",
3570
+ spin: "animate-spin",
3571
+ pulse: "animate-pulse"
3572
+ };
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
+ );
3632
+ };
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(" ")
3682
+ };
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"
3688
+ };
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"
3693
+ };
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(
3713
+ ({
3714
+ className,
3715
+ variant = "primary",
3716
+ size = "md",
3717
+ isLoading = false,
3718
+ disabled,
3719
+ leftIcon,
3720
+ rightIcon,
3721
+ icon: iconProp,
3722
+ iconRight: iconRightProp,
3723
+ action,
3724
+ actionPayload,
3725
+ label,
3726
+ children,
3727
+ onClick,
3728
+ ...props
3729
+ }, ref) => {
3730
+ const eventBus = useEventBus();
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) => {
3736
+ if (action) {
3737
+ eventBus.emit(`UI:${action}`, actionPayload ?? {});
3738
+ }
3739
+ onClick?.(e);
3740
+ };
3741
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3742
+ "button",
3743
+ {
3744
+ ref,
3745
+ disabled: disabled || isLoading,
3746
+ className: cn(
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],
3756
+ className
3757
+ ),
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
+ }
3767
+ );
3768
+ }
3769
+ );
3770
+ Button.displayName = "Button";
3771
+ var variantStyles2 = {
3772
+ h1: "text-4xl font-bold tracking-tight text-foreground",
3773
+ h2: "text-3xl font-bold tracking-tight text-foreground",
3774
+ h3: "text-2xl font-bold text-foreground",
3775
+ h4: "text-xl font-bold text-foreground",
3776
+ h5: "text-lg font-bold text-foreground",
3777
+ h6: "text-base font-bold text-foreground",
3778
+ heading: "text-2xl font-bold text-foreground",
3779
+ subheading: "text-lg font-semibold text-foreground",
3780
+ body1: "text-base font-normal text-foreground",
3781
+ body2: "text-sm font-normal text-foreground",
3782
+ body: "text-base font-normal text-foreground",
3783
+ caption: "text-xs font-normal text-muted-foreground",
3784
+ overline: "text-xs uppercase tracking-wide font-bold text-muted-foreground",
3785
+ small: "text-sm font-normal text-foreground",
3786
+ large: "text-lg font-medium text-foreground",
3787
+ label: "text-sm font-medium text-foreground"
3788
+ };
3789
+ var colorStyles = {
3790
+ primary: "text-foreground",
3791
+ secondary: "text-muted-foreground",
3792
+ muted: "text-muted-foreground",
3793
+ error: "text-error",
3794
+ success: "text-success",
3795
+ warning: "text-warning",
3796
+ inherit: "text-inherit"
3797
+ };
3798
+ var weightStyles = {
3799
+ light: "font-light",
3800
+ normal: "font-normal",
3801
+ medium: "font-medium",
3802
+ semibold: "font-semibold",
3803
+ bold: "font-bold"
3804
+ };
3805
+ var defaultElements = {
3806
+ h1: "h1",
3807
+ h2: "h2",
3808
+ h3: "h3",
3809
+ h4: "h4",
3810
+ h5: "h5",
3811
+ h6: "h6",
3812
+ heading: "h2",
3813
+ subheading: "h3",
3814
+ body1: "p",
3815
+ body2: "p",
3816
+ body: "p",
3817
+ caption: "span",
3818
+ overline: "span",
3819
+ small: "span",
3820
+ large: "p",
3821
+ label: "span"
3822
+ };
3823
+ var typographySizeStyles = {
3824
+ xs: "text-xs",
3825
+ sm: "text-sm",
3826
+ md: "text-base",
3827
+ lg: "text-lg",
3828
+ xl: "text-xl",
3829
+ "2xl": "text-2xl",
3830
+ "3xl": "text-3xl"
3831
+ };
3832
+ var overflowStyles = {
3833
+ visible: "overflow-visible",
3834
+ hidden: "overflow-hidden",
3835
+ wrap: "break-words overflow-hidden",
3836
+ "clamp-2": "overflow-hidden line-clamp-2",
3837
+ "clamp-3": "overflow-hidden line-clamp-3"
3838
+ };
3839
+ var Typography = ({
3840
+ variant: variantProp,
3841
+ level,
3842
+ color = "primary",
3843
+ align,
3844
+ weight,
3845
+ size,
3846
+ truncate = false,
3847
+ overflow,
3848
+ as,
3849
+ id,
3850
+ className,
3851
+ style,
3852
+ content,
3853
+ children
3854
+ }) => {
3855
+ const variant = variantProp ?? (level ? `h${level}` : "body1");
3856
+ const Component2 = as || defaultElements[variant];
3857
+ const Comp = Component2;
3858
+ return /* @__PURE__ */ jsxRuntime.jsx(
3859
+ Comp,
3860
+ {
3861
+ id,
3862
+ className: cn(
3863
+ variantStyles2[variant],
3864
+ colorStyles[color],
3865
+ weight && weightStyles[weight],
3866
+ size && typographySizeStyles[size],
3867
+ align && `text-${align}`,
3868
+ truncate && "truncate overflow-hidden text-ellipsis",
3869
+ overflow && overflowStyles[overflow],
3870
+ className
3871
+ ),
3872
+ style,
3873
+ children: children ?? content
3874
+ }
3875
+ );
3876
+ };
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";
2836
4145
  var paddingStyles = {
2837
4146
  none: "p-0",
2838
4147
  xs: "p-1",
@@ -2923,7 +4232,7 @@ var displayStyles = {
2923
4232
  "inline-flex": "inline-flex",
2924
4233
  grid: "grid"
2925
4234
  };
2926
- var overflowStyles = {
4235
+ var overflowStyles2 = {
2927
4236
  auto: "overflow-auto",
2928
4237
  hidden: "overflow-hidden",
2929
4238
  visible: "overflow-visible",
@@ -3003,7 +4312,7 @@ var Box = React11__default.default.forwardRef(
3003
4312
  display && displayStyles[display],
3004
4313
  fullWidth && "w-full",
3005
4314
  fullHeight && "h-full",
3006
- overflow && overflowStyles[overflow],
4315
+ overflow && overflowStyles2[overflow],
3007
4316
  position && positionStyles[position],
3008
4317
  isClickable && "cursor-pointer",
3009
4318
  className
@@ -3019,191 +4328,6 @@ var Box = React11__default.default.forwardRef(
3019
4328
  }
3020
4329
  );
3021
4330
  Box.displayName = "Box";
3022
- var gapStyles = {
3023
- none: "gap-0",
3024
- xs: "gap-1",
3025
- sm: "gap-2",
3026
- md: "gap-4",
3027
- lg: "gap-6",
3028
- xl: "gap-8",
3029
- "2xl": "gap-12"
3030
- };
3031
- var alignStyles = {
3032
- start: "items-start",
3033
- center: "items-center",
3034
- end: "items-end",
3035
- stretch: "items-stretch",
3036
- baseline: "items-baseline"
3037
- };
3038
- var justifyStyles = {
3039
- start: "justify-start",
3040
- center: "justify-center",
3041
- end: "justify-end",
3042
- between: "justify-between",
3043
- around: "justify-around",
3044
- evenly: "justify-evenly"
3045
- };
3046
- var Stack = ({
3047
- direction = "vertical",
3048
- gap = "md",
3049
- align = "stretch",
3050
- justify = "start",
3051
- wrap = false,
3052
- reverse = false,
3053
- flex = false,
3054
- className,
3055
- style,
3056
- children,
3057
- as: Component2 = "div",
3058
- onClick,
3059
- onKeyDown,
3060
- role,
3061
- tabIndex,
3062
- action,
3063
- actionPayload,
3064
- responsive = false
3065
- }) => {
3066
- const eventBus = useEventBus();
3067
- const handleClick = (e) => {
3068
- if (action) {
3069
- eventBus.emit(`UI:${action}`, actionPayload ?? {});
3070
- }
3071
- onClick?.(e);
3072
- };
3073
- const isHorizontal = direction === "horizontal";
3074
- 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";
3075
- const Comp = Component2;
3076
- return /* @__PURE__ */ jsxRuntime.jsx(
3077
- Comp,
3078
- {
3079
- className: cn(
3080
- "flex",
3081
- directionClass,
3082
- gapStyles[gap],
3083
- alignStyles[align],
3084
- justifyStyles[justify],
3085
- wrap && "flex-wrap",
3086
- flex && "flex-1",
3087
- className
3088
- ),
3089
- style,
3090
- onClick: action || onClick ? handleClick : void 0,
3091
- onKeyDown,
3092
- role,
3093
- tabIndex,
3094
- children
3095
- }
3096
- );
3097
- };
3098
- var VStack = (props) => /* @__PURE__ */ jsxRuntime.jsx(Stack, { direction: "vertical", ...props });
3099
- var HStack = (props) => /* @__PURE__ */ jsxRuntime.jsx(Stack, { direction: "horizontal", ...props });
3100
- var variantStyles = {
3101
- h1: "text-4xl font-bold tracking-tight text-foreground",
3102
- h2: "text-3xl font-bold tracking-tight text-foreground",
3103
- h3: "text-2xl font-bold text-foreground",
3104
- h4: "text-xl font-bold text-foreground",
3105
- h5: "text-lg font-bold text-foreground",
3106
- h6: "text-base font-bold text-foreground",
3107
- heading: "text-2xl font-bold text-foreground",
3108
- subheading: "text-lg font-semibold text-foreground",
3109
- body1: "text-base font-normal text-foreground",
3110
- body2: "text-sm font-normal text-foreground",
3111
- body: "text-base font-normal text-foreground",
3112
- caption: "text-xs font-normal text-muted-foreground",
3113
- overline: "text-xs uppercase tracking-wide font-bold text-muted-foreground",
3114
- small: "text-sm font-normal text-foreground",
3115
- large: "text-lg font-medium text-foreground",
3116
- label: "text-sm font-medium text-foreground"
3117
- };
3118
- var colorStyles = {
3119
- primary: "text-foreground",
3120
- secondary: "text-muted-foreground",
3121
- muted: "text-muted-foreground",
3122
- error: "text-error",
3123
- success: "text-success",
3124
- warning: "text-warning",
3125
- inherit: "text-inherit"
3126
- };
3127
- var weightStyles = {
3128
- light: "font-light",
3129
- normal: "font-normal",
3130
- medium: "font-medium",
3131
- semibold: "font-semibold",
3132
- bold: "font-bold"
3133
- };
3134
- var defaultElements = {
3135
- h1: "h1",
3136
- h2: "h2",
3137
- h3: "h3",
3138
- h4: "h4",
3139
- h5: "h5",
3140
- h6: "h6",
3141
- heading: "h2",
3142
- subheading: "h3",
3143
- body1: "p",
3144
- body2: "p",
3145
- body: "p",
3146
- caption: "span",
3147
- overline: "span",
3148
- small: "span",
3149
- large: "p",
3150
- label: "span"
3151
- };
3152
- var typographySizeStyles = {
3153
- xs: "text-xs",
3154
- sm: "text-sm",
3155
- md: "text-base",
3156
- lg: "text-lg",
3157
- xl: "text-xl",
3158
- "2xl": "text-2xl",
3159
- "3xl": "text-3xl"
3160
- };
3161
- var overflowStyles2 = {
3162
- visible: "overflow-visible",
3163
- hidden: "overflow-hidden",
3164
- wrap: "break-words overflow-hidden",
3165
- "clamp-2": "overflow-hidden line-clamp-2",
3166
- "clamp-3": "overflow-hidden line-clamp-3"
3167
- };
3168
- var Typography = ({
3169
- variant: variantProp,
3170
- level,
3171
- color = "primary",
3172
- align,
3173
- weight,
3174
- size,
3175
- truncate = false,
3176
- overflow,
3177
- as,
3178
- id,
3179
- className,
3180
- style,
3181
- content,
3182
- children
3183
- }) => {
3184
- const variant = variantProp ?? (level ? `h${level}` : "body1");
3185
- const Component2 = as || defaultElements[variant];
3186
- const Comp = Component2;
3187
- return /* @__PURE__ */ jsxRuntime.jsx(
3188
- Comp,
3189
- {
3190
- id,
3191
- className: cn(
3192
- variantStyles[variant],
3193
- colorStyles[color],
3194
- weight && weightStyles[weight],
3195
- size && typographySizeStyles[size],
3196
- align && `text-${align}`,
3197
- truncate && "truncate overflow-hidden text-ellipsis",
3198
- overflow && overflowStyles2[overflow],
3199
- className
3200
- ),
3201
- style,
3202
- children: children ?? content
3203
- }
3204
- );
3205
- };
3206
- Typography.displayName = "Typography";
3207
4331
  var DEFAULT_3D_BATTLE_TILES = [
3208
4332
  { id: "t00", x: 0, y: 0, z: 0, type: "stone", passable: false },
3209
4333
  { id: "t10", x: 1, y: 0, z: 0, type: "stone", passable: false },
@@ -3231,10 +4355,6 @@ var DEFAULT_3D_BATTLE_TILES = [
3231
4355
  { id: "t34", x: 3, y: 4, z: 4, type: "stone", passable: false },
3232
4356
  { id: "t44", x: 4, y: 4, z: 4, type: "stone", passable: false }
3233
4357
  ];
3234
- var DEFAULT_3D_BATTLE_UNITS = [
3235
- { id: "u1", x: 1, y: 1, z: 1, unitType: "warrior", name: "Worker", faction: "player", health: 10, maxHealth: 10 },
3236
- { id: "u2", x: 3, y: 3, z: 3, unitType: "enemy", name: "Guardian", faction: "enemy", health: 8, maxHealth: 10 }
3237
- ];
3238
4358
  var DEFAULT_3D_BATTLE_FEATURES = [
3239
4359
  { id: "f1", x: 2, y: 2, z: 2, type: "gold_mine", color: "#f4c542" },
3240
4360
  { id: "f2", x: 3, y: 1, z: 1, type: "portal", color: "#8b5cf6" }
@@ -3242,52 +4362,44 @@ var DEFAULT_3D_BATTLE_FEATURES = [
3242
4362
  function GameCanvas3DBattleTemplate({
3243
4363
  entity,
3244
4364
  tiles: propTiles = DEFAULT_3D_BATTLE_TILES,
3245
- units: propUnits = DEFAULT_3D_BATTLE_UNITS,
3246
4365
  features: propFeatures = DEFAULT_3D_BATTLE_FEATURES,
3247
4366
  cameraMode = "perspective",
3248
- showGrid = true,
3249
- shadows = true,
3250
4367
  backgroundColor = "#2a1a1a",
3251
4368
  tileClickEvent,
3252
4369
  unitClickEvent,
3253
- unitAttackEvent,
3254
- unitMoveEvent,
3255
4370
  endTurnEvent,
3256
- exitEvent,
3257
- selectedUnitId,
3258
- validMoves,
3259
- attackTargets,
4371
+ cancelEvent,
4372
+ attackEvent,
4373
+ playAgainEvent,
4374
+ gameEndEvent,
3260
4375
  className
3261
4376
  }) {
3262
4377
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
3263
4378
  const tiles = resolved ? Array.isArray(resolved.tiles) ? resolved.tiles : [] : propTiles;
3264
- const units = resolved ? Array.isArray(resolved.units) ? resolved.units : [] : propUnits;
3265
4379
  const features = resolved ? Array.isArray(resolved.features) ? resolved.features : [] : propFeatures;
3266
- const currentTurn = resolved?.currentTurn;
3267
- 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);
3268
4382
  return /* @__PURE__ */ jsxRuntime.jsxs(
3269
4383
  Box,
3270
4384
  {
3271
4385
  className: cn("game-canvas-3d-battle-template block relative w-full min-h-[85vh]", className),
3272
4386
  children: [
3273
4387
  /* @__PURE__ */ jsxRuntime.jsx(
3274
- GameCanvas3D,
4388
+ GameBoard3D,
3275
4389
  {
4390
+ entity,
3276
4391
  tiles,
3277
- units,
3278
4392
  features,
3279
4393
  cameraMode,
3280
- showGrid,
3281
- showCoordinates: false,
3282
- showTileInfo: false,
3283
- shadows,
3284
4394
  backgroundColor,
3285
4395
  tileClickEvent,
3286
4396
  unitClickEvent,
3287
- selectedUnitId,
3288
- validMoves,
3289
- attackTargets,
3290
- 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"
3291
4403
  }
3292
4404
  ),
3293
4405
  currentTurn && /* @__PURE__ */ jsxRuntime.jsxs(
@@ -3337,10 +4449,6 @@ var DEFAULT_3D_CASTLE_TILES = [
3337
4449
  { id: "t34", x: 3, y: 4, z: 4, type: "wall", passable: false },
3338
4450
  { id: "t44", x: 4, y: 4, z: 4, type: "wall", passable: false }
3339
4451
  ];
3340
- var DEFAULT_3D_CASTLE_UNITS = [
3341
- { id: "u1", x: 1, y: 1, z: 1, unitType: "worker", name: "Worker", faction: "player", health: 10, maxHealth: 10 },
3342
- { id: "u2", x: 3, y: 3, z: 3, unitType: "guardian", name: "Guardian", faction: "player", health: 10, maxHealth: 10 }
3343
- ];
3344
4452
  var DEFAULT_3D_CASTLE_FEATURES = [
3345
4453
  { id: "f1", x: 2, y: 2, z: 2, type: "chest", color: "#f4c542" },
3346
4454
  { id: "f2", x: 3, y: 1, z: 1, type: "crystal", color: "#8b5cf6" }
@@ -3348,30 +4456,26 @@ var DEFAULT_3D_CASTLE_FEATURES = [
3348
4456
  function GameCanvas3DCastleTemplate({
3349
4457
  entity,
3350
4458
  tiles: propTiles = DEFAULT_3D_CASTLE_TILES,
3351
- units: propUnits = DEFAULT_3D_CASTLE_UNITS,
3352
4459
  features: propFeatures = DEFAULT_3D_CASTLE_FEATURES,
3353
4460
  cameraMode = "isometric",
3354
- showGrid = true,
3355
- shadows = true,
3356
4461
  backgroundColor = "#1e1e2e",
3357
4462
  buildingClickEvent,
3358
4463
  unitClickEvent,
3359
- buildEvent,
3360
- recruitEvent,
3361
- exitEvent,
3362
- selectedBuildingId,
3363
- selectedTileIds = [],
3364
- availableBuildSites,
4464
+ endTurnEvent,
4465
+ cancelEvent,
4466
+ attackEvent,
4467
+ playAgainEvent,
4468
+ gameEndEvent,
3365
4469
  showHeader = true,
3366
4470
  className
3367
4471
  }) {
3368
4472
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
3369
4473
  const tiles = resolved ? Array.isArray(resolved.tiles) ? resolved.tiles : [] : propTiles;
3370
- const units = resolved ? Array.isArray(resolved.units) ? resolved.units : [] : propUnits;
3371
4474
  const features = resolved ? Array.isArray(resolved.features) ? resolved.features : [] : propFeatures;
3372
4475
  const name = resolved?.name == null ? void 0 : String(resolved.name);
3373
4476
  const level = resolved?.level == null ? void 0 : Number(resolved.level);
3374
4477
  const owner = resolved?.owner == null ? void 0 : String(resolved.owner);
4478
+ const unitCount = resolved && Array.isArray(resolved.units) ? resolved.units.length : 0;
3375
4479
  return /* @__PURE__ */ jsxRuntime.jsxs(
3376
4480
  VStack,
3377
4481
  {
@@ -3386,28 +4490,27 @@ function GameCanvas3DCastleTemplate({
3386
4490
  owner && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "muted", className: "header__owner", children: owner })
3387
4491
  ] }),
3388
4492
  /* @__PURE__ */ jsxRuntime.jsx(
3389
- GameCanvas3D,
4493
+ GameBoard3D,
3390
4494
  {
4495
+ entity,
3391
4496
  tiles,
3392
- units,
3393
4497
  features,
3394
4498
  cameraMode,
3395
- showGrid,
3396
- showCoordinates: false,
3397
- showTileInfo: false,
3398
- shadows,
3399
4499
  backgroundColor,
3400
- featureClickEvent: buildingClickEvent,
4500
+ tileClickEvent: buildingClickEvent,
3401
4501
  unitClickEvent,
3402
- selectedTileIds,
3403
- validMoves: availableBuildSites,
3404
- 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"
3405
4508
  }
3406
4509
  ),
3407
- 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: [
3408
4511
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "garrison-info__label", children: "Garrison:" }),
3409
4512
  /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", weight: "bold", className: "garrison-info__count", children: [
3410
- units.length,
4513
+ unitCount,
3411
4514
  " units"
3412
4515
  ] })
3413
4516
  ] })
@@ -3443,10 +4546,6 @@ var DEFAULT_3D_WORLDMAP_TILES = [
3443
4546
  { id: "t34", x: 3, y: 4, z: 4, type: "mountain", passable: false },
3444
4547
  { id: "t44", x: 4, y: 4, z: 4, type: "mountain", passable: false }
3445
4548
  ];
3446
- var DEFAULT_3D_WORLDMAP_UNITS = [
3447
- { id: "h1", x: 1, y: 1, z: 1, unitType: "hero", name: "Amir", faction: "player", health: 10, maxHealth: 10 },
3448
- { id: "h2", x: 3, y: 3, z: 3, unitType: "scout", name: "Archivist", faction: "player", health: 10, maxHealth: 10 }
3449
- ];
3450
4549
  var DEFAULT_3D_WORLDMAP_FEATURES = [
3451
4550
  { id: "f1", x: 2, y: 2, z: 2, type: "capital", color: "#f4c542" },
3452
4551
  { id: "f2", x: 4, y: 2, z: 2, type: "power_node", color: "#8b5cf6" }
@@ -3454,52 +4553,41 @@ var DEFAULT_3D_WORLDMAP_FEATURES = [
3454
4553
  function GameCanvas3DWorldMapTemplate({
3455
4554
  entity,
3456
4555
  tiles: propTiles = DEFAULT_3D_WORLDMAP_TILES,
3457
- units: propUnits = DEFAULT_3D_WORLDMAP_UNITS,
3458
4556
  features: propFeatures = DEFAULT_3D_WORLDMAP_FEATURES,
3459
4557
  cameraMode = "isometric",
3460
- showGrid = true,
3461
- showCoordinates = true,
3462
- showTileInfo = true,
3463
- shadows = true,
3464
4558
  backgroundColor = "#1a1a2e",
3465
4559
  tileClickEvent,
3466
4560
  unitClickEvent,
3467
- featureClickEvent,
3468
- tileHoverEvent,
3469
- tileLeaveEvent,
3470
- cameraChangeEvent,
3471
- selectedUnitId,
3472
- validMoves,
3473
- attackTargets,
4561
+ endTurnEvent,
4562
+ cancelEvent,
4563
+ attackEvent,
4564
+ playAgainEvent,
4565
+ gameEndEvent,
3474
4566
  className
3475
4567
  }) {
3476
4568
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
3477
4569
  const tiles = resolved ? Array.isArray(resolved.tiles) ? resolved.tiles : [] : propTiles;
3478
- const units = resolved ? Array.isArray(resolved.units) ? resolved.units : [] : propUnits;
3479
4570
  const features = resolved ? Array.isArray(resolved.features) ? resolved.features : [] : propFeatures;
3480
- return /* @__PURE__ */ jsxRuntime.jsx(
3481
- GameCanvas3D,
3482
- {
3483
- tiles,
3484
- units,
3485
- features,
3486
- cameraMode,
3487
- showGrid,
3488
- showCoordinates,
3489
- showTileInfo,
3490
- shadows,
3491
- backgroundColor,
3492
- tileClickEvent,
3493
- unitClickEvent,
3494
- featureClickEvent,
3495
- tileHoverEvent,
3496
- tileLeaveEvent,
3497
- cameraChangeEvent,
3498
- selectedUnitId,
3499
- validMoves,
3500
- attackTargets,
3501
- className
3502
- }
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
+ )
3503
4591
  );
3504
4592
  }
3505
4593
  GameCanvas3DWorldMapTemplate.displayName = "GameCanvas3DWorldMapTemplate";
@@ -3691,10 +4779,10 @@ function parseApplicationLevel(schema) {
3691
4779
  }
3692
4780
  const count = schema.orbitals.length;
3693
4781
  const cols = Math.ceil(Math.sqrt(count));
3694
- const rows = Math.ceil(count / cols);
4782
+ const rows2 = Math.ceil(count / cols);
3695
4783
  const spacing = 200;
3696
4784
  const gridW = cols * spacing;
3697
- const gridH = rows * spacing;
4785
+ const gridH = rows2 * spacing;
3698
4786
  const originX = (600 - gridW) / 2 + spacing / 2;
3699
4787
  const originY = (400 - gridH) / 2 + spacing / 2;
3700
4788
  schema.orbitals.forEach((orbital, i) => {
@@ -4226,7 +5314,7 @@ Avl3DLabel.displayName = "Avl3DLabel";
4226
5314
  var Avl3DTooltip = ({
4227
5315
  position,
4228
5316
  title,
4229
- rows,
5317
+ rows: rows2,
4230
5318
  accentColor = "#5b9bd5"
4231
5319
  }) => {
4232
5320
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -4264,7 +5352,7 @@ var Avl3DTooltip = ({
4264
5352
  children: title
4265
5353
  }
4266
5354
  ),
4267
- rows.map((row) => /* @__PURE__ */ jsxRuntime.jsxs(
5355
+ rows2.map((row) => /* @__PURE__ */ jsxRuntime.jsxs(
4268
5356
  Box,
4269
5357
  {
4270
5358
  style: {