@almadar/ui 5.48.0 → 5.49.1

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 +4173 -1650
  2. package/dist/avl/index.css +504 -0
  3. package/dist/avl/index.js +3101 -579
  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 +3837 -1467
  26. package/dist/components/index.css +504 -0
  27. package/dist/components/index.js +2908 -546
  28. package/dist/docs/index.css +504 -0
  29. package/dist/providers/index.cjs +3826 -1603
  30. package/dist/providers/index.css +504 -0
  31. package/dist/providers/index.js +2915 -693
  32. package/dist/runtime/index.cjs +4025 -1502
  33. package/dist/runtime/index.css +504 -0
  34. package/dist/runtime/index.js +3068 -546
  35. package/package.json +1 -1
@@ -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
 
@@ -2809,6 +2811,1313 @@ var GameCanvas3D = forwardRef(
2809
2811
  }
2810
2812
  );
2811
2813
  GameCanvas3D.displayName = "GameCanvas3D";
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;
2829
+ }
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
2923
+ };
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"
3090
+ };
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"
3270
+ };
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"
3455
+ };
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"
3536
+ };
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"
3543
+ };
3544
+ var animationClasses = {
3545
+ none: "",
3546
+ spin: "animate-spin",
3547
+ pulse: "animate-pulse"
3548
+ };
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
+ );
3608
+ };
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(" ")
3658
+ };
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"
3664
+ };
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"
3669
+ };
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(
3689
+ ({
3690
+ className,
3691
+ variant = "primary",
3692
+ size = "md",
3693
+ isLoading = false,
3694
+ disabled,
3695
+ leftIcon,
3696
+ rightIcon,
3697
+ icon: iconProp,
3698
+ iconRight: iconRightProp,
3699
+ action,
3700
+ actionPayload,
3701
+ label,
3702
+ children,
3703
+ onClick,
3704
+ ...props
3705
+ }, ref) => {
3706
+ const eventBus = useEventBus();
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) => {
3712
+ if (action) {
3713
+ eventBus.emit(`UI:${action}`, actionPayload ?? {});
3714
+ }
3715
+ onClick?.(e);
3716
+ };
3717
+ return /* @__PURE__ */ jsxs(
3718
+ "button",
3719
+ {
3720
+ ref,
3721
+ disabled: disabled || isLoading,
3722
+ className: cn(
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],
3732
+ className
3733
+ ),
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
+ }
3743
+ );
3744
+ }
3745
+ );
3746
+ Button.displayName = "Button";
3747
+ var variantStyles2 = {
3748
+ h1: "text-4xl font-bold tracking-tight text-foreground",
3749
+ h2: "text-3xl font-bold tracking-tight text-foreground",
3750
+ h3: "text-2xl font-bold text-foreground",
3751
+ h4: "text-xl font-bold text-foreground",
3752
+ h5: "text-lg font-bold text-foreground",
3753
+ h6: "text-base font-bold text-foreground",
3754
+ heading: "text-2xl font-bold text-foreground",
3755
+ subheading: "text-lg font-semibold text-foreground",
3756
+ body1: "text-base font-normal text-foreground",
3757
+ body2: "text-sm font-normal text-foreground",
3758
+ body: "text-base font-normal text-foreground",
3759
+ caption: "text-xs font-normal text-muted-foreground",
3760
+ overline: "text-xs uppercase tracking-wide font-bold text-muted-foreground",
3761
+ small: "text-sm font-normal text-foreground",
3762
+ large: "text-lg font-medium text-foreground",
3763
+ label: "text-sm font-medium text-foreground"
3764
+ };
3765
+ var colorStyles = {
3766
+ primary: "text-foreground",
3767
+ secondary: "text-muted-foreground",
3768
+ muted: "text-muted-foreground",
3769
+ error: "text-error",
3770
+ success: "text-success",
3771
+ warning: "text-warning",
3772
+ inherit: "text-inherit"
3773
+ };
3774
+ var weightStyles = {
3775
+ light: "font-light",
3776
+ normal: "font-normal",
3777
+ medium: "font-medium",
3778
+ semibold: "font-semibold",
3779
+ bold: "font-bold"
3780
+ };
3781
+ var defaultElements = {
3782
+ h1: "h1",
3783
+ h2: "h2",
3784
+ h3: "h3",
3785
+ h4: "h4",
3786
+ h5: "h5",
3787
+ h6: "h6",
3788
+ heading: "h2",
3789
+ subheading: "h3",
3790
+ body1: "p",
3791
+ body2: "p",
3792
+ body: "p",
3793
+ caption: "span",
3794
+ overline: "span",
3795
+ small: "span",
3796
+ large: "p",
3797
+ label: "span"
3798
+ };
3799
+ var typographySizeStyles = {
3800
+ xs: "text-xs",
3801
+ sm: "text-sm",
3802
+ md: "text-base",
3803
+ lg: "text-lg",
3804
+ xl: "text-xl",
3805
+ "2xl": "text-2xl",
3806
+ "3xl": "text-3xl"
3807
+ };
3808
+ var overflowStyles = {
3809
+ visible: "overflow-visible",
3810
+ hidden: "overflow-hidden",
3811
+ wrap: "break-words overflow-hidden",
3812
+ "clamp-2": "overflow-hidden line-clamp-2",
3813
+ "clamp-3": "overflow-hidden line-clamp-3"
3814
+ };
3815
+ var Typography = ({
3816
+ variant: variantProp,
3817
+ level,
3818
+ color = "primary",
3819
+ align,
3820
+ weight,
3821
+ size,
3822
+ truncate = false,
3823
+ overflow,
3824
+ as,
3825
+ id,
3826
+ className,
3827
+ style,
3828
+ content,
3829
+ children
3830
+ }) => {
3831
+ const variant = variantProp ?? (level ? `h${level}` : "body1");
3832
+ const Component2 = as || defaultElements[variant];
3833
+ const Comp = Component2;
3834
+ return /* @__PURE__ */ jsx(
3835
+ Comp,
3836
+ {
3837
+ id,
3838
+ className: cn(
3839
+ variantStyles2[variant],
3840
+ colorStyles[color],
3841
+ weight && weightStyles[weight],
3842
+ size && typographySizeStyles[size],
3843
+ align && `text-${align}`,
3844
+ truncate && "truncate overflow-hidden text-ellipsis",
3845
+ overflow && overflowStyles[overflow],
3846
+ className
3847
+ ),
3848
+ style,
3849
+ children: children ?? content
3850
+ }
3851
+ );
3852
+ };
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";
2812
4121
  var paddingStyles = {
2813
4122
  none: "p-0",
2814
4123
  xs: "p-1",
@@ -2899,7 +4208,7 @@ var displayStyles = {
2899
4208
  "inline-flex": "inline-flex",
2900
4209
  grid: "grid"
2901
4210
  };
2902
- var overflowStyles = {
4211
+ var overflowStyles2 = {
2903
4212
  auto: "overflow-auto",
2904
4213
  hidden: "overflow-hidden",
2905
4214
  visible: "overflow-visible",
@@ -2979,7 +4288,7 @@ var Box = React11.forwardRef(
2979
4288
  display && displayStyles[display],
2980
4289
  fullWidth && "w-full",
2981
4290
  fullHeight && "h-full",
2982
- overflow && overflowStyles[overflow],
4291
+ overflow && overflowStyles2[overflow],
2983
4292
  position && positionStyles[position],
2984
4293
  isClickable && "cursor-pointer",
2985
4294
  className
@@ -2995,191 +4304,6 @@ var Box = React11.forwardRef(
2995
4304
  }
2996
4305
  );
2997
4306
  Box.displayName = "Box";
2998
- var gapStyles = {
2999
- none: "gap-0",
3000
- xs: "gap-1",
3001
- sm: "gap-2",
3002
- md: "gap-4",
3003
- lg: "gap-6",
3004
- xl: "gap-8",
3005
- "2xl": "gap-12"
3006
- };
3007
- var alignStyles = {
3008
- start: "items-start",
3009
- center: "items-center",
3010
- end: "items-end",
3011
- stretch: "items-stretch",
3012
- baseline: "items-baseline"
3013
- };
3014
- var justifyStyles = {
3015
- start: "justify-start",
3016
- center: "justify-center",
3017
- end: "justify-end",
3018
- between: "justify-between",
3019
- around: "justify-around",
3020
- evenly: "justify-evenly"
3021
- };
3022
- var Stack = ({
3023
- direction = "vertical",
3024
- gap = "md",
3025
- align = "stretch",
3026
- justify = "start",
3027
- wrap = false,
3028
- reverse = false,
3029
- flex = false,
3030
- className,
3031
- style,
3032
- children,
3033
- as: Component2 = "div",
3034
- onClick,
3035
- onKeyDown,
3036
- role,
3037
- tabIndex,
3038
- action,
3039
- actionPayload,
3040
- responsive = false
3041
- }) => {
3042
- const eventBus = useEventBus();
3043
- const handleClick = (e) => {
3044
- if (action) {
3045
- eventBus.emit(`UI:${action}`, actionPayload ?? {});
3046
- }
3047
- onClick?.(e);
3048
- };
3049
- const isHorizontal = direction === "horizontal";
3050
- 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";
3051
- const Comp = Component2;
3052
- return /* @__PURE__ */ jsx(
3053
- Comp,
3054
- {
3055
- className: cn(
3056
- "flex",
3057
- directionClass,
3058
- gapStyles[gap],
3059
- alignStyles[align],
3060
- justifyStyles[justify],
3061
- wrap && "flex-wrap",
3062
- flex && "flex-1",
3063
- className
3064
- ),
3065
- style,
3066
- onClick: action || onClick ? handleClick : void 0,
3067
- onKeyDown,
3068
- role,
3069
- tabIndex,
3070
- children
3071
- }
3072
- );
3073
- };
3074
- var VStack = (props) => /* @__PURE__ */ jsx(Stack, { direction: "vertical", ...props });
3075
- var HStack = (props) => /* @__PURE__ */ jsx(Stack, { direction: "horizontal", ...props });
3076
- var variantStyles = {
3077
- h1: "text-4xl font-bold tracking-tight text-foreground",
3078
- h2: "text-3xl font-bold tracking-tight text-foreground",
3079
- h3: "text-2xl font-bold text-foreground",
3080
- h4: "text-xl font-bold text-foreground",
3081
- h5: "text-lg font-bold text-foreground",
3082
- h6: "text-base font-bold text-foreground",
3083
- heading: "text-2xl font-bold text-foreground",
3084
- subheading: "text-lg font-semibold text-foreground",
3085
- body1: "text-base font-normal text-foreground",
3086
- body2: "text-sm font-normal text-foreground",
3087
- body: "text-base font-normal text-foreground",
3088
- caption: "text-xs font-normal text-muted-foreground",
3089
- overline: "text-xs uppercase tracking-wide font-bold text-muted-foreground",
3090
- small: "text-sm font-normal text-foreground",
3091
- large: "text-lg font-medium text-foreground",
3092
- label: "text-sm font-medium text-foreground"
3093
- };
3094
- var colorStyles = {
3095
- primary: "text-foreground",
3096
- secondary: "text-muted-foreground",
3097
- muted: "text-muted-foreground",
3098
- error: "text-error",
3099
- success: "text-success",
3100
- warning: "text-warning",
3101
- inherit: "text-inherit"
3102
- };
3103
- var weightStyles = {
3104
- light: "font-light",
3105
- normal: "font-normal",
3106
- medium: "font-medium",
3107
- semibold: "font-semibold",
3108
- bold: "font-bold"
3109
- };
3110
- var defaultElements = {
3111
- h1: "h1",
3112
- h2: "h2",
3113
- h3: "h3",
3114
- h4: "h4",
3115
- h5: "h5",
3116
- h6: "h6",
3117
- heading: "h2",
3118
- subheading: "h3",
3119
- body1: "p",
3120
- body2: "p",
3121
- body: "p",
3122
- caption: "span",
3123
- overline: "span",
3124
- small: "span",
3125
- large: "p",
3126
- label: "span"
3127
- };
3128
- var typographySizeStyles = {
3129
- xs: "text-xs",
3130
- sm: "text-sm",
3131
- md: "text-base",
3132
- lg: "text-lg",
3133
- xl: "text-xl",
3134
- "2xl": "text-2xl",
3135
- "3xl": "text-3xl"
3136
- };
3137
- var overflowStyles2 = {
3138
- visible: "overflow-visible",
3139
- hidden: "overflow-hidden",
3140
- wrap: "break-words overflow-hidden",
3141
- "clamp-2": "overflow-hidden line-clamp-2",
3142
- "clamp-3": "overflow-hidden line-clamp-3"
3143
- };
3144
- var Typography = ({
3145
- variant: variantProp,
3146
- level,
3147
- color = "primary",
3148
- align,
3149
- weight,
3150
- size,
3151
- truncate = false,
3152
- overflow,
3153
- as,
3154
- id,
3155
- className,
3156
- style,
3157
- content,
3158
- children
3159
- }) => {
3160
- const variant = variantProp ?? (level ? `h${level}` : "body1");
3161
- const Component2 = as || defaultElements[variant];
3162
- const Comp = Component2;
3163
- return /* @__PURE__ */ jsx(
3164
- Comp,
3165
- {
3166
- id,
3167
- className: cn(
3168
- variantStyles[variant],
3169
- colorStyles[color],
3170
- weight && weightStyles[weight],
3171
- size && typographySizeStyles[size],
3172
- align && `text-${align}`,
3173
- truncate && "truncate overflow-hidden text-ellipsis",
3174
- overflow && overflowStyles2[overflow],
3175
- className
3176
- ),
3177
- style,
3178
- children: children ?? content
3179
- }
3180
- );
3181
- };
3182
- Typography.displayName = "Typography";
3183
4307
  var DEFAULT_3D_BATTLE_TILES = [
3184
4308
  { id: "t00", x: 0, y: 0, z: 0, type: "stone", passable: false },
3185
4309
  { id: "t10", x: 1, y: 0, z: 0, type: "stone", passable: false },
@@ -3207,10 +4331,6 @@ var DEFAULT_3D_BATTLE_TILES = [
3207
4331
  { id: "t34", x: 3, y: 4, z: 4, type: "stone", passable: false },
3208
4332
  { id: "t44", x: 4, y: 4, z: 4, type: "stone", passable: false }
3209
4333
  ];
3210
- var DEFAULT_3D_BATTLE_UNITS = [
3211
- { id: "u1", x: 1, y: 1, z: 1, unitType: "warrior", name: "Worker", faction: "player", health: 10, maxHealth: 10 },
3212
- { id: "u2", x: 3, y: 3, z: 3, unitType: "enemy", name: "Guardian", faction: "enemy", health: 8, maxHealth: 10 }
3213
- ];
3214
4334
  var DEFAULT_3D_BATTLE_FEATURES = [
3215
4335
  { id: "f1", x: 2, y: 2, z: 2, type: "gold_mine", color: "#f4c542" },
3216
4336
  { id: "f2", x: 3, y: 1, z: 1, type: "portal", color: "#8b5cf6" }
@@ -3218,52 +4338,44 @@ var DEFAULT_3D_BATTLE_FEATURES = [
3218
4338
  function GameCanvas3DBattleTemplate({
3219
4339
  entity,
3220
4340
  tiles: propTiles = DEFAULT_3D_BATTLE_TILES,
3221
- units: propUnits = DEFAULT_3D_BATTLE_UNITS,
3222
4341
  features: propFeatures = DEFAULT_3D_BATTLE_FEATURES,
3223
4342
  cameraMode = "perspective",
3224
- showGrid = true,
3225
- shadows = true,
3226
4343
  backgroundColor = "#2a1a1a",
3227
4344
  tileClickEvent,
3228
4345
  unitClickEvent,
3229
- unitAttackEvent,
3230
- unitMoveEvent,
3231
4346
  endTurnEvent,
3232
- exitEvent,
3233
- selectedUnitId,
3234
- validMoves,
3235
- attackTargets,
4347
+ cancelEvent,
4348
+ attackEvent,
4349
+ playAgainEvent,
4350
+ gameEndEvent,
3236
4351
  className
3237
4352
  }) {
3238
4353
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
3239
4354
  const tiles = resolved ? Array.isArray(resolved.tiles) ? resolved.tiles : [] : propTiles;
3240
- const units = resolved ? Array.isArray(resolved.units) ? resolved.units : [] : propUnits;
3241
4355
  const features = resolved ? Array.isArray(resolved.features) ? resolved.features : [] : propFeatures;
3242
- const currentTurn = resolved?.currentTurn;
3243
- 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);
3244
4358
  return /* @__PURE__ */ jsxs(
3245
4359
  Box,
3246
4360
  {
3247
4361
  className: cn("game-canvas-3d-battle-template block relative w-full min-h-[85vh]", className),
3248
4362
  children: [
3249
4363
  /* @__PURE__ */ jsx(
3250
- GameCanvas3D,
4364
+ GameBoard3D,
3251
4365
  {
4366
+ entity,
3252
4367
  tiles,
3253
- units,
3254
4368
  features,
3255
4369
  cameraMode,
3256
- showGrid,
3257
- showCoordinates: false,
3258
- showTileInfo: false,
3259
- shadows,
3260
4370
  backgroundColor,
3261
4371
  tileClickEvent,
3262
4372
  unitClickEvent,
3263
- selectedUnitId,
3264
- validMoves,
3265
- attackTargets,
3266
- 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"
3267
4379
  }
3268
4380
  ),
3269
4381
  currentTurn && /* @__PURE__ */ jsxs(
@@ -3313,10 +4425,6 @@ var DEFAULT_3D_CASTLE_TILES = [
3313
4425
  { id: "t34", x: 3, y: 4, z: 4, type: "wall", passable: false },
3314
4426
  { id: "t44", x: 4, y: 4, z: 4, type: "wall", passable: false }
3315
4427
  ];
3316
- var DEFAULT_3D_CASTLE_UNITS = [
3317
- { id: "u1", x: 1, y: 1, z: 1, unitType: "worker", name: "Worker", faction: "player", health: 10, maxHealth: 10 },
3318
- { id: "u2", x: 3, y: 3, z: 3, unitType: "guardian", name: "Guardian", faction: "player", health: 10, maxHealth: 10 }
3319
- ];
3320
4428
  var DEFAULT_3D_CASTLE_FEATURES = [
3321
4429
  { id: "f1", x: 2, y: 2, z: 2, type: "chest", color: "#f4c542" },
3322
4430
  { id: "f2", x: 3, y: 1, z: 1, type: "crystal", color: "#8b5cf6" }
@@ -3324,30 +4432,26 @@ var DEFAULT_3D_CASTLE_FEATURES = [
3324
4432
  function GameCanvas3DCastleTemplate({
3325
4433
  entity,
3326
4434
  tiles: propTiles = DEFAULT_3D_CASTLE_TILES,
3327
- units: propUnits = DEFAULT_3D_CASTLE_UNITS,
3328
4435
  features: propFeatures = DEFAULT_3D_CASTLE_FEATURES,
3329
4436
  cameraMode = "isometric",
3330
- showGrid = true,
3331
- shadows = true,
3332
4437
  backgroundColor = "#1e1e2e",
3333
4438
  buildingClickEvent,
3334
4439
  unitClickEvent,
3335
- buildEvent,
3336
- recruitEvent,
3337
- exitEvent,
3338
- selectedBuildingId,
3339
- selectedTileIds = [],
3340
- availableBuildSites,
4440
+ endTurnEvent,
4441
+ cancelEvent,
4442
+ attackEvent,
4443
+ playAgainEvent,
4444
+ gameEndEvent,
3341
4445
  showHeader = true,
3342
4446
  className
3343
4447
  }) {
3344
4448
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
3345
4449
  const tiles = resolved ? Array.isArray(resolved.tiles) ? resolved.tiles : [] : propTiles;
3346
- const units = resolved ? Array.isArray(resolved.units) ? resolved.units : [] : propUnits;
3347
4450
  const features = resolved ? Array.isArray(resolved.features) ? resolved.features : [] : propFeatures;
3348
4451
  const name = resolved?.name == null ? void 0 : String(resolved.name);
3349
4452
  const level = resolved?.level == null ? void 0 : Number(resolved.level);
3350
4453
  const owner = resolved?.owner == null ? void 0 : String(resolved.owner);
4454
+ const unitCount = resolved && Array.isArray(resolved.units) ? resolved.units.length : 0;
3351
4455
  return /* @__PURE__ */ jsxs(
3352
4456
  VStack,
3353
4457
  {
@@ -3362,28 +4466,27 @@ function GameCanvas3DCastleTemplate({
3362
4466
  owner && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", className: "header__owner", children: owner })
3363
4467
  ] }),
3364
4468
  /* @__PURE__ */ jsx(
3365
- GameCanvas3D,
4469
+ GameBoard3D,
3366
4470
  {
4471
+ entity,
3367
4472
  tiles,
3368
- units,
3369
4473
  features,
3370
4474
  cameraMode,
3371
- showGrid,
3372
- showCoordinates: false,
3373
- showTileInfo: false,
3374
- shadows,
3375
4475
  backgroundColor,
3376
- featureClickEvent: buildingClickEvent,
4476
+ tileClickEvent: buildingClickEvent,
3377
4477
  unitClickEvent,
3378
- selectedTileIds,
3379
- validMoves: availableBuildSites,
3380
- 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"
3381
4484
  }
3382
4485
  ),
3383
- 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: [
3384
4487
  /* @__PURE__ */ jsx(Typography, { variant: "small", className: "garrison-info__label", children: "Garrison:" }),
3385
4488
  /* @__PURE__ */ jsxs(Typography, { variant: "small", weight: "bold", className: "garrison-info__count", children: [
3386
- units.length,
4489
+ unitCount,
3387
4490
  " units"
3388
4491
  ] })
3389
4492
  ] })
@@ -3419,10 +4522,6 @@ var DEFAULT_3D_WORLDMAP_TILES = [
3419
4522
  { id: "t34", x: 3, y: 4, z: 4, type: "mountain", passable: false },
3420
4523
  { id: "t44", x: 4, y: 4, z: 4, type: "mountain", passable: false }
3421
4524
  ];
3422
- var DEFAULT_3D_WORLDMAP_UNITS = [
3423
- { id: "h1", x: 1, y: 1, z: 1, unitType: "hero", name: "Amir", faction: "player", health: 10, maxHealth: 10 },
3424
- { id: "h2", x: 3, y: 3, z: 3, unitType: "scout", name: "Archivist", faction: "player", health: 10, maxHealth: 10 }
3425
- ];
3426
4525
  var DEFAULT_3D_WORLDMAP_FEATURES = [
3427
4526
  { id: "f1", x: 2, y: 2, z: 2, type: "capital", color: "#f4c542" },
3428
4527
  { id: "f2", x: 4, y: 2, z: 2, type: "power_node", color: "#8b5cf6" }
@@ -3430,52 +4529,41 @@ var DEFAULT_3D_WORLDMAP_FEATURES = [
3430
4529
  function GameCanvas3DWorldMapTemplate({
3431
4530
  entity,
3432
4531
  tiles: propTiles = DEFAULT_3D_WORLDMAP_TILES,
3433
- units: propUnits = DEFAULT_3D_WORLDMAP_UNITS,
3434
4532
  features: propFeatures = DEFAULT_3D_WORLDMAP_FEATURES,
3435
4533
  cameraMode = "isometric",
3436
- showGrid = true,
3437
- showCoordinates = true,
3438
- showTileInfo = true,
3439
- shadows = true,
3440
4534
  backgroundColor = "#1a1a2e",
3441
4535
  tileClickEvent,
3442
4536
  unitClickEvent,
3443
- featureClickEvent,
3444
- tileHoverEvent,
3445
- tileLeaveEvent,
3446
- cameraChangeEvent,
3447
- selectedUnitId,
3448
- validMoves,
3449
- attackTargets,
4537
+ endTurnEvent,
4538
+ cancelEvent,
4539
+ attackEvent,
4540
+ playAgainEvent,
4541
+ gameEndEvent,
3450
4542
  className
3451
4543
  }) {
3452
4544
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
3453
4545
  const tiles = resolved ? Array.isArray(resolved.tiles) ? resolved.tiles : [] : propTiles;
3454
- const units = resolved ? Array.isArray(resolved.units) ? resolved.units : [] : propUnits;
3455
4546
  const features = resolved ? Array.isArray(resolved.features) ? resolved.features : [] : propFeatures;
3456
- return /* @__PURE__ */ jsx(
3457
- GameCanvas3D,
3458
- {
3459
- tiles,
3460
- units,
3461
- features,
3462
- cameraMode,
3463
- showGrid,
3464
- showCoordinates,
3465
- showTileInfo,
3466
- shadows,
3467
- backgroundColor,
3468
- tileClickEvent,
3469
- unitClickEvent,
3470
- featureClickEvent,
3471
- tileHoverEvent,
3472
- tileLeaveEvent,
3473
- cameraChangeEvent,
3474
- selectedUnitId,
3475
- validMoves,
3476
- attackTargets,
3477
- className
3478
- }
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
+ )
3479
4567
  );
3480
4568
  }
3481
4569
  GameCanvas3DWorldMapTemplate.displayName = "GameCanvas3DWorldMapTemplate";
@@ -3667,10 +4755,10 @@ function parseApplicationLevel(schema) {
3667
4755
  }
3668
4756
  const count = schema.orbitals.length;
3669
4757
  const cols = Math.ceil(Math.sqrt(count));
3670
- const rows = Math.ceil(count / cols);
4758
+ const rows2 = Math.ceil(count / cols);
3671
4759
  const spacing = 200;
3672
4760
  const gridW = cols * spacing;
3673
- const gridH = rows * spacing;
4761
+ const gridH = rows2 * spacing;
3674
4762
  const originX = (600 - gridW) / 2 + spacing / 2;
3675
4763
  const originY = (400 - gridH) / 2 + spacing / 2;
3676
4764
  schema.orbitals.forEach((orbital, i) => {
@@ -4202,7 +5290,7 @@ Avl3DLabel.displayName = "Avl3DLabel";
4202
5290
  var Avl3DTooltip = ({
4203
5291
  position,
4204
5292
  title,
4205
- rows,
5293
+ rows: rows2,
4206
5294
  accentColor = "#5b9bd5"
4207
5295
  }) => {
4208
5296
  return /* @__PURE__ */ jsx(
@@ -4240,7 +5328,7 @@ var Avl3DTooltip = ({
4240
5328
  children: title
4241
5329
  }
4242
5330
  ),
4243
- rows.map((row) => /* @__PURE__ */ jsxs(
5331
+ rows2.map((row) => /* @__PURE__ */ jsxs(
4244
5332
  Box,
4245
5333
  {
4246
5334
  style: {