@almadar/ui 5.60.0 → 5.62.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 (41) hide show
  1. package/dist/avl/index.cjs +3233 -1757
  2. package/dist/avl/index.js +2057 -581
  3. package/dist/components/core/molecules/GraphCanvas.d.ts +6 -0
  4. package/dist/components/core/templates/index.d.ts +4 -0
  5. package/dist/components/game/molecules/CardHand.d.ts +35 -0
  6. package/dist/components/game/molecules/DialogueBox.d.ts +8 -2
  7. package/dist/components/game/molecules/index.d.ts +1 -0
  8. package/dist/components/game/molecules/three/index.cjs +229 -120
  9. package/dist/components/game/molecules/three/index.js +229 -120
  10. package/dist/components/game/organisms/CardBattlerBoard.d.ts +46 -0
  11. package/dist/components/game/organisms/CityBuilderBoard.d.ts +63 -0
  12. package/dist/components/game/organisms/TopDownShooterBoard.d.ts +64 -0
  13. package/dist/components/game/organisms/TowerDefenseBoard.d.ts +10 -1
  14. package/dist/components/game/organisms/TraitSlot.d.ts +1 -3
  15. package/dist/components/game/organisms/VisualNovelBoard.d.ts +53 -0
  16. package/dist/components/game/organisms/index.d.ts +4 -0
  17. package/dist/components/game/templates/CardBattlerTemplate.d.ts +35 -0
  18. package/dist/components/game/templates/CityBuilderTemplate.d.ts +40 -0
  19. package/dist/components/game/templates/GameCanvas3DBattleTemplate.d.ts +5 -2
  20. package/dist/components/game/templates/GameCanvas3DCastleTemplate.d.ts +5 -2
  21. package/dist/components/game/templates/GameCanvas3DWorldMapTemplate.d.ts +5 -2
  22. package/dist/components/game/templates/TopDownShooterTemplate.d.ts +42 -0
  23. package/dist/components/game/templates/VisualNovelTemplate.d.ts +28 -0
  24. package/dist/components/game/templates/game3dAssetManifest.d.ts +50 -0
  25. package/dist/components/game/templates/index.d.ts +4 -0
  26. package/dist/components/index.cjs +3048 -1547
  27. package/dist/components/index.js +2019 -527
  28. package/dist/docs/index.cjs +105 -1
  29. package/dist/docs/index.js +105 -1
  30. package/dist/hooks/index.cjs +105 -1
  31. package/dist/hooks/index.js +105 -1
  32. package/dist/locales/index.cjs +315 -3
  33. package/dist/locales/index.js +315 -3
  34. package/dist/providers/index.cjs +3034 -1558
  35. package/dist/providers/index.js +2025 -549
  36. package/dist/runtime/index.cjs +3085 -1609
  37. package/dist/runtime/index.js +2024 -548
  38. package/locales/ar.json +105 -1
  39. package/locales/en.json +105 -1
  40. package/locales/sl.json +105 -1
  41. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
- import * as React76 from 'react';
3
- import React76__default, { useContext, useMemo, useRef, useEffect, useCallback, useState, Suspense, lazy, createContext, useLayoutEffect, useId, forwardRef, useImperativeHandle, useSyncExternalStore, Component } from 'react';
2
+ import * as React79 from 'react';
3
+ import React79__default, { useContext, useMemo, useRef, useEffect, useCallback, useState, Suspense, lazy, createContext, useLayoutEffect, useId, forwardRef, useImperativeHandle, useSyncExternalStore, Component } from 'react';
4
4
  import { clsx } from 'clsx';
5
5
  import { twMerge } from 'tailwind-merge';
6
6
  import { EventBusContext, useTraitScope, TraitScopeProvider } from '@almadar/ui/providers';
@@ -233,7 +233,7 @@ var init_SvgFlow = __esm({
233
233
  width = 100,
234
234
  height = 100
235
235
  }) => {
236
- const markerId = React76__default.useMemo(() => {
236
+ const markerId = React79__default.useMemo(() => {
237
237
  flowIdCounter += 1;
238
238
  return `almadar-flow-arrow-${flowIdCounter}`;
239
239
  }, []);
@@ -826,7 +826,7 @@ var init_SvgRing = __esm({
826
826
  width = 100,
827
827
  height = 100
828
828
  }) => {
829
- const gradientId = React76__default.useMemo(() => {
829
+ const gradientId = React79__default.useMemo(() => {
830
830
  ringIdCounter += 1;
831
831
  return `almadar-ring-glow-${ringIdCounter}`;
832
832
  }, []);
@@ -1238,7 +1238,7 @@ function loadLib(key, importer) {
1238
1238
  return p2;
1239
1239
  }
1240
1240
  function lazyFamilyIcon(libKey, importer, pick, fallbackName, family) {
1241
- const Lazy = React76__default.lazy(async () => {
1241
+ const Lazy = React79__default.lazy(async () => {
1242
1242
  const lib = await loadLib(libKey, importer);
1243
1243
  const Comp = pick(lib);
1244
1244
  if (!Comp) {
@@ -1248,7 +1248,7 @@ function lazyFamilyIcon(libKey, importer, pick, fallbackName, family) {
1248
1248
  return { default: Comp };
1249
1249
  });
1250
1250
  const Wrapped = (props) => /* @__PURE__ */ jsx(
1251
- React76__default.Suspense,
1251
+ React79__default.Suspense,
1252
1252
  {
1253
1253
  fallback: /* @__PURE__ */ jsx(
1254
1254
  "span",
@@ -1974,7 +1974,7 @@ var init_Icon = __esm({
1974
1974
  const directIcon = typeof icon === "string" ? void 0 : icon;
1975
1975
  const effectiveName = typeof icon === "string" ? icon : name;
1976
1976
  const family = useIconFamily();
1977
- const RenderedComponent = React76__default.useMemo(() => {
1977
+ const RenderedComponent = React79__default.useMemo(() => {
1978
1978
  if (directIcon) return null;
1979
1979
  return effectiveName ? resolveIconForFamily(effectiveName, family) : null;
1980
1980
  }, [directIcon, effectiveName, family]);
@@ -2033,7 +2033,7 @@ function resolveIconProp(value, sizeClass) {
2033
2033
  const IconComp = value;
2034
2034
  return /* @__PURE__ */ jsx(IconComp, { className: sizeClass });
2035
2035
  }
2036
- if (React76__default.isValidElement(value)) {
2036
+ if (React79__default.isValidElement(value)) {
2037
2037
  return value;
2038
2038
  }
2039
2039
  if (typeof value === "object" && value !== null && "render" in value) {
@@ -2109,7 +2109,7 @@ var init_Button = __esm({
2109
2109
  md: "h-icon-default w-icon-default",
2110
2110
  lg: "h-icon-default w-icon-default"
2111
2111
  };
2112
- Button = React76__default.forwardRef(
2112
+ Button = React79__default.forwardRef(
2113
2113
  ({
2114
2114
  className,
2115
2115
  variant = "primary",
@@ -2176,7 +2176,7 @@ var init_Input = __esm({
2176
2176
  init_cn();
2177
2177
  init_Icon();
2178
2178
  init_useEventBus();
2179
- Input = React76__default.forwardRef(
2179
+ Input = React79__default.forwardRef(
2180
2180
  ({
2181
2181
  className,
2182
2182
  inputType,
@@ -2336,7 +2336,7 @@ var Label;
2336
2336
  var init_Label = __esm({
2337
2337
  "components/core/atoms/Label.tsx"() {
2338
2338
  init_cn();
2339
- Label = React76__default.forwardRef(
2339
+ Label = React79__default.forwardRef(
2340
2340
  ({ className, required, children, ...props }, ref) => {
2341
2341
  return /* @__PURE__ */ jsxs(
2342
2342
  "label",
@@ -2363,7 +2363,7 @@ var init_Textarea = __esm({
2363
2363
  "components/core/atoms/Textarea.tsx"() {
2364
2364
  init_cn();
2365
2365
  init_useEventBus();
2366
- Textarea = React76__default.forwardRef(
2366
+ Textarea = React79__default.forwardRef(
2367
2367
  ({ className, error, onChange, ...props }, ref) => {
2368
2368
  const eventBus = useEventBus();
2369
2369
  const handleChange = (e) => {
@@ -2602,7 +2602,7 @@ var init_Select = __esm({
2602
2602
  init_cn();
2603
2603
  init_Icon();
2604
2604
  init_useEventBus();
2605
- Select = React76__default.forwardRef(
2605
+ Select = React79__default.forwardRef(
2606
2606
  (props, _ref) => {
2607
2607
  const { multiple, searchable, clearable } = props;
2608
2608
  if (multiple || searchable || clearable) {
@@ -2619,7 +2619,7 @@ var init_Checkbox = __esm({
2619
2619
  "components/core/atoms/Checkbox.tsx"() {
2620
2620
  init_cn();
2621
2621
  init_useEventBus();
2622
- Checkbox = React76__default.forwardRef(
2622
+ Checkbox = React79__default.forwardRef(
2623
2623
  ({ className, label, id, onChange, ...props }, ref) => {
2624
2624
  const inputId = id || `checkbox-${Math.random().toString(36).substr(2, 9)}`;
2625
2625
  const eventBus = useEventBus();
@@ -2673,7 +2673,7 @@ var init_Spinner = __esm({
2673
2673
  md: "h-6 w-6",
2674
2674
  lg: "h-8 w-8"
2675
2675
  };
2676
- Spinner = React76__default.forwardRef(
2676
+ Spinner = React79__default.forwardRef(
2677
2677
  ({ className, size = "md", overlay, ...props }, ref) => {
2678
2678
  if (overlay) {
2679
2679
  return /* @__PURE__ */ jsx(
@@ -2763,7 +2763,7 @@ var init_Card = __esm({
2763
2763
  chip: "shadow-none rounded-pill border-[length:var(--border-width)] border-border",
2764
2764
  "tile-image-first": "p-0 overflow-hidden"
2765
2765
  };
2766
- Card = React76__default.forwardRef(
2766
+ Card = React79__default.forwardRef(
2767
2767
  ({
2768
2768
  className,
2769
2769
  variant = "bordered",
@@ -2811,9 +2811,9 @@ var init_Card = __esm({
2811
2811
  }
2812
2812
  );
2813
2813
  Card.displayName = "Card";
2814
- CardHeader = React76__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("mb-4", className), ...props }));
2814
+ CardHeader = React79__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("mb-4", className), ...props }));
2815
2815
  CardHeader.displayName = "CardHeader";
2816
- CardTitle = React76__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2816
+ CardTitle = React79__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2817
2817
  "h3",
2818
2818
  {
2819
2819
  ref,
@@ -2826,11 +2826,11 @@ var init_Card = __esm({
2826
2826
  }
2827
2827
  ));
2828
2828
  CardTitle.displayName = "CardTitle";
2829
- CardContent = React76__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("", className), ...props }));
2829
+ CardContent = React79__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("", className), ...props }));
2830
2830
  CardContent.displayName = "CardContent";
2831
2831
  CardBody = CardContent;
2832
2832
  CardBody.displayName = "CardBody";
2833
- CardFooter = React76__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2833
+ CardFooter = React79__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2834
2834
  "div",
2835
2835
  {
2836
2836
  ref,
@@ -2883,7 +2883,7 @@ var init_Badge = __esm({
2883
2883
  md: "px-2.5 py-1 text-sm",
2884
2884
  lg: "px-3 py-1.5 text-base"
2885
2885
  };
2886
- Badge = React76__default.forwardRef(
2886
+ Badge = React79__default.forwardRef(
2887
2887
  ({ className, variant = "default", size = "sm", amount, label, icon, children, onRemove, removeLabel, ...props }, ref) => {
2888
2888
  const iconSizes3 = {
2889
2889
  sm: "h-icon-default w-icon-default",
@@ -2976,7 +2976,7 @@ var init_FilterPill = __esm({
2976
2976
  md: "w-3.5 h-3.5",
2977
2977
  lg: "w-4 h-4"
2978
2978
  };
2979
- FilterPill = React76__default.forwardRef(
2979
+ FilterPill = React79__default.forwardRef(
2980
2980
  ({
2981
2981
  className,
2982
2982
  variant = "default",
@@ -3105,8 +3105,8 @@ var init_Avatar = __esm({
3105
3105
  actionPayload
3106
3106
  }) => {
3107
3107
  const eventBus = useEventBus();
3108
- const [imgFailed, setImgFailed] = React76__default.useState(false);
3109
- React76__default.useEffect(() => {
3108
+ const [imgFailed, setImgFailed] = React79__default.useState(false);
3109
+ React79__default.useEffect(() => {
3110
3110
  setImgFailed(false);
3111
3111
  }, [src]);
3112
3112
  const initials = providedInitials ?? (name ? generateInitials(name) : void 0);
@@ -3310,7 +3310,7 @@ var init_Box = __esm({
3310
3310
  fixed: "fixed",
3311
3311
  sticky: "sticky"
3312
3312
  };
3313
- Box = React76__default.forwardRef(
3313
+ Box = React79__default.forwardRef(
3314
3314
  ({
3315
3315
  padding,
3316
3316
  paddingX,
@@ -3360,7 +3360,7 @@ var init_Box = __esm({
3360
3360
  onMouseLeave?.(e);
3361
3361
  }, [hoverEvent, eventBus, onMouseLeave]);
3362
3362
  const isClickable = action || onClick;
3363
- return React76__default.createElement(
3363
+ return React79__default.createElement(
3364
3364
  Component2,
3365
3365
  {
3366
3366
  ref,
@@ -3685,7 +3685,7 @@ var init_Radio = __esm({
3685
3685
  md: "w-2.5 h-2.5",
3686
3686
  lg: "w-3 h-3"
3687
3687
  };
3688
- Radio = React76__default.forwardRef(
3688
+ Radio = React79__default.forwardRef(
3689
3689
  ({
3690
3690
  label,
3691
3691
  helperText,
@@ -3702,12 +3702,12 @@ var init_Radio = __esm({
3702
3702
  onChange,
3703
3703
  ...props
3704
3704
  }, ref) => {
3705
- const reactId = React76__default.useId();
3705
+ const reactId = React79__default.useId();
3706
3706
  const baseId = id || `radio-${reactId}`;
3707
3707
  const hasError = !!error;
3708
3708
  const eventBus = useEventBus();
3709
- const [selected, setSelected] = React76__default.useState(value);
3710
- React76__default.useEffect(() => {
3709
+ const [selected, setSelected] = React79__default.useState(value);
3710
+ React79__default.useEffect(() => {
3711
3711
  if (value !== void 0) setSelected(value);
3712
3712
  }, [value]);
3713
3713
  const pick = (next, e) => {
@@ -3889,7 +3889,7 @@ var init_Switch = __esm({
3889
3889
  "components/core/atoms/Switch.tsx"() {
3890
3890
  "use client";
3891
3891
  init_cn();
3892
- Switch = React76.forwardRef(
3892
+ Switch = React79.forwardRef(
3893
3893
  ({
3894
3894
  checked,
3895
3895
  defaultChecked = false,
@@ -3900,10 +3900,10 @@ var init_Switch = __esm({
3900
3900
  name,
3901
3901
  className
3902
3902
  }, ref) => {
3903
- const [isChecked, setIsChecked] = React76.useState(
3903
+ const [isChecked, setIsChecked] = React79.useState(
3904
3904
  checked !== void 0 ? checked : defaultChecked
3905
3905
  );
3906
- React76.useEffect(() => {
3906
+ React79.useEffect(() => {
3907
3907
  if (checked !== void 0) {
3908
3908
  setIsChecked(checked);
3909
3909
  }
@@ -4555,7 +4555,7 @@ var Dialog;
4555
4555
  var init_Dialog = __esm({
4556
4556
  "components/core/atoms/Dialog.tsx"() {
4557
4557
  init_cn();
4558
- Dialog = React76__default.forwardRef(
4558
+ Dialog = React79__default.forwardRef(
4559
4559
  ({
4560
4560
  role = "dialog",
4561
4561
  "aria-modal": ariaModal = true,
@@ -4581,7 +4581,7 @@ var Aside;
4581
4581
  var init_Aside = __esm({
4582
4582
  "components/core/atoms/Aside.tsx"() {
4583
4583
  init_cn();
4584
- Aside = React76__default.forwardRef(
4584
+ Aside = React79__default.forwardRef(
4585
4585
  ({ className, children, ...rest }, ref) => /* @__PURE__ */ jsx("aside", { ref, className: cn(className), ...rest, children })
4586
4586
  );
4587
4587
  Aside.displayName = "Aside";
@@ -4659,8 +4659,8 @@ var init_LawReferenceTooltip = __esm({
4659
4659
  className
4660
4660
  }) => {
4661
4661
  const { t } = useTranslate();
4662
- const [isVisible, setIsVisible] = React76__default.useState(false);
4663
- const timeoutRef = React76__default.useRef(null);
4662
+ const [isVisible, setIsVisible] = React79__default.useState(false);
4663
+ const timeoutRef = React79__default.useRef(null);
4664
4664
  const handleMouseEnter = () => {
4665
4665
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
4666
4666
  timeoutRef.current = setTimeout(() => setIsVisible(true), 200);
@@ -4669,7 +4669,7 @@ var init_LawReferenceTooltip = __esm({
4669
4669
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
4670
4670
  setIsVisible(false);
4671
4671
  };
4672
- React76__default.useEffect(() => {
4672
+ React79__default.useEffect(() => {
4673
4673
  return () => {
4674
4674
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
4675
4675
  };
@@ -4879,7 +4879,7 @@ var init_StatusDot = __esm({
4879
4879
  md: "w-2.5 h-2.5",
4880
4880
  lg: "w-3 h-3"
4881
4881
  };
4882
- StatusDot = React76__default.forwardRef(
4882
+ StatusDot = React79__default.forwardRef(
4883
4883
  ({ className, status = "offline", pulse = false, size = "md", label, ...props }, ref) => {
4884
4884
  return /* @__PURE__ */ jsx(
4885
4885
  "span",
@@ -4933,7 +4933,7 @@ var init_TrendIndicator = __esm({
4933
4933
  down: "trending-down",
4934
4934
  flat: "arrow-right"
4935
4935
  };
4936
- TrendIndicator = React76__default.forwardRef(
4936
+ TrendIndicator = React79__default.forwardRef(
4937
4937
  ({
4938
4938
  className,
4939
4939
  value,
@@ -5000,7 +5000,7 @@ var init_RangeSlider = __esm({
5000
5000
  md: "w-4 h-4",
5001
5001
  lg: "w-5 h-5"
5002
5002
  };
5003
- RangeSlider = React76__default.forwardRef(
5003
+ RangeSlider = React79__default.forwardRef(
5004
5004
  ({
5005
5005
  className,
5006
5006
  min = 0,
@@ -5596,7 +5596,7 @@ var init_ContentSection = __esm({
5596
5596
  md: "py-16",
5597
5597
  lg: "py-24"
5598
5598
  };
5599
- ContentSection = React76__default.forwardRef(
5599
+ ContentSection = React79__default.forwardRef(
5600
5600
  ({ children, background = "default", padding = "lg", id, className }, ref) => {
5601
5601
  return /* @__PURE__ */ jsx(
5602
5602
  Box,
@@ -6130,7 +6130,7 @@ var init_AnimatedReveal = __esm({
6130
6130
  "scale-up": { opacity: 1, transform: "scale(1) translateY(0)" },
6131
6131
  "none": {}
6132
6132
  };
6133
- AnimatedReveal = React76__default.forwardRef(
6133
+ AnimatedReveal = React79__default.forwardRef(
6134
6134
  ({
6135
6135
  trigger = "scroll",
6136
6136
  animation = "fade-up",
@@ -6290,7 +6290,7 @@ var init_AnimatedGraphic = __esm({
6290
6290
  "components/marketing/atoms/AnimatedGraphic.tsx"() {
6291
6291
  "use client";
6292
6292
  init_cn();
6293
- AnimatedGraphic = React76__default.forwardRef(
6293
+ AnimatedGraphic = React79__default.forwardRef(
6294
6294
  ({
6295
6295
  src,
6296
6296
  svgContent,
@@ -6313,7 +6313,7 @@ var init_AnimatedGraphic = __esm({
6313
6313
  const fetchedSvg = useFetchedSvg(svgContent ? void 0 : src);
6314
6314
  const resolvedSvg = svgContent ?? fetchedSvg;
6315
6315
  const prevAnimateRef = useRef(animate);
6316
- const setRef = React76__default.useCallback(
6316
+ const setRef = React79__default.useCallback(
6317
6317
  (node) => {
6318
6318
  containerRef.current = node;
6319
6319
  if (typeof ref === "function") ref(node);
@@ -6995,7 +6995,7 @@ var init_ErrorBoundary = __esm({
6995
6995
  }
6996
6996
  );
6997
6997
  };
6998
- ErrorBoundary = class extends React76__default.Component {
6998
+ ErrorBoundary = class extends React79__default.Component {
6999
6999
  constructor(props) {
7000
7000
  super(props);
7001
7001
  __publicField(this, "reset", () => {
@@ -7140,9 +7140,6 @@ var init_Skeleton = __esm({
7140
7140
  Skeleton.displayName = "Skeleton";
7141
7141
  }
7142
7142
  });
7143
- function isKnownPattern(type) {
7144
- return type in componentMapping;
7145
- }
7146
7143
  function getKnownPatterns() {
7147
7144
  return Object.keys(componentMapping);
7148
7145
  }
@@ -7238,7 +7235,6 @@ var init_slot_definitions = __esm({
7238
7235
  // renderer/index.ts
7239
7236
  var init_renderer = __esm({
7240
7237
  "renderer/index.ts"() {
7241
- init_pattern_resolver();
7242
7238
  init_slot_definitions();
7243
7239
  }
7244
7240
  });
@@ -8119,9 +8115,9 @@ function ControlButton({
8119
8115
  className
8120
8116
  }) {
8121
8117
  const eventBus = useEventBus();
8122
- const [isPressed, setIsPressed] = React76.useState(false);
8118
+ const [isPressed, setIsPressed] = React79.useState(false);
8123
8119
  const actualPressed = pressed ?? isPressed;
8124
- const handlePointerDown = React76.useCallback(
8120
+ const handlePointerDown = React79.useCallback(
8125
8121
  (e) => {
8126
8122
  e.preventDefault();
8127
8123
  if (disabled) return;
@@ -8131,7 +8127,7 @@ function ControlButton({
8131
8127
  },
8132
8128
  [disabled, pressEvent, eventBus, onPress]
8133
8129
  );
8134
- const handlePointerUp = React76.useCallback(
8130
+ const handlePointerUp = React79.useCallback(
8135
8131
  (e) => {
8136
8132
  e.preventDefault();
8137
8133
  if (disabled) return;
@@ -8141,7 +8137,7 @@ function ControlButton({
8141
8137
  },
8142
8138
  [disabled, releaseEvent, eventBus, onRelease]
8143
8139
  );
8144
- const handlePointerLeave = React76.useCallback(
8140
+ const handlePointerLeave = React79.useCallback(
8145
8141
  (e) => {
8146
8142
  if (isPressed) {
8147
8143
  setIsPressed(false);
@@ -8229,8 +8225,8 @@ function ActionButtons({
8229
8225
  disabled
8230
8226
  }) {
8231
8227
  const eventBus = useEventBus();
8232
- const [activeButtons, setActiveButtons] = React76.useState(/* @__PURE__ */ new Set());
8233
- const handlePress = React76.useCallback(
8228
+ const [activeButtons, setActiveButtons] = React79.useState(/* @__PURE__ */ new Set());
8229
+ const handlePress = React79.useCallback(
8234
8230
  (id) => {
8235
8231
  setActiveButtons((prev) => new Set(prev).add(id));
8236
8232
  if (actionEvent) eventBus.emit(`UI:${actionEvent}`, { id, pressed: true });
@@ -8238,7 +8234,7 @@ function ActionButtons({
8238
8234
  },
8239
8235
  [actionEvent, eventBus, onAction]
8240
8236
  );
8241
- const handleRelease = React76.useCallback(
8237
+ const handleRelease = React79.useCallback(
8242
8238
  (id) => {
8243
8239
  setActiveButtons((prev) => {
8244
8240
  const next = new Set(prev);
@@ -8875,8 +8871,8 @@ var init_AnimatedCounter2 = __esm({
8875
8871
  const [displayValue, setDisplayValue] = useState("0");
8876
8872
  const [hasAnimated, setHasAnimated] = useState(false);
8877
8873
  const animate = useCallback(() => {
8878
- const { num: num2, prefix, suffix, decimals } = parseValue(value);
8879
- if (num2 === 0) {
8874
+ const { num: num3, prefix, suffix, decimals } = parseValue(value);
8875
+ if (num3 === 0) {
8880
8876
  setDisplayValue(String(value));
8881
8877
  return;
8882
8878
  }
@@ -8885,7 +8881,7 @@ var init_AnimatedCounter2 = __esm({
8885
8881
  const elapsed = now - startTime;
8886
8882
  const progress = Math.min(elapsed / duration, 1);
8887
8883
  const eased = 1 - Math.pow(1 - progress, 3);
8888
- const current = eased * num2;
8884
+ const current = eased * num3;
8889
8885
  setDisplayValue(`${prefix}${current.toFixed(decimals)}${suffix}`);
8890
8886
  if (progress < 1) {
8891
8887
  requestAnimationFrame(tick);
@@ -9741,7 +9737,7 @@ function IsometricCanvas({
9741
9737
  // Tuning
9742
9738
  diamondTopY: diamondTopYProp,
9743
9739
  // Remote asset loading
9744
- assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/",
9740
+ assetBaseUrl,
9745
9741
  assetManifest
9746
9742
  }) {
9747
9743
  const tilesProp = Array.isArray(_tilesPropRaw) ? _tilesPropRaw : [];
@@ -9820,7 +9816,7 @@ function IsometricCanvas({
9820
9816
  const attackTargetSet = useMemo(() => {
9821
9817
  return new Set(attackTargets.map((p2) => `${p2.x},${p2.y}`));
9822
9818
  }, [attackTargets]);
9823
- const resolveManifestUrl = useCallback((relativePath) => {
9819
+ const resolveManifestUrl7 = useCallback((relativePath) => {
9824
9820
  if (!relativePath) return void 0;
9825
9821
  if (assetBaseUrl) return `${assetBaseUrl.replace(/\/$/, "")}${relativePath.startsWith("/") ? "" : "/"}${relativePath}`;
9826
9822
  return relativePath;
@@ -9833,7 +9829,7 @@ function IsometricCanvas({
9833
9829
  const url = getTerrainSprite(tile.terrain ?? "");
9834
9830
  if (url) urls.push(url);
9835
9831
  } else {
9836
- const url = resolveManifestUrl(assetManifest?.terrains?.[tile.terrain ?? ""]);
9832
+ const url = resolveManifestUrl7(assetManifest?.terrains?.[tile.terrain ?? ""]);
9837
9833
  if (url) urls.push(url);
9838
9834
  }
9839
9835
  }
@@ -9843,7 +9839,7 @@ function IsometricCanvas({
9843
9839
  const url = getFeatureSprite(feature.type);
9844
9840
  if (url) urls.push(url);
9845
9841
  } else {
9846
- const url = resolveManifestUrl(assetManifest?.features?.[feature.type]);
9842
+ const url = resolveManifestUrl7(assetManifest?.features?.[feature.type]);
9847
9843
  if (url) urls.push(url);
9848
9844
  }
9849
9845
  }
@@ -9853,13 +9849,13 @@ function IsometricCanvas({
9853
9849
  const url = getUnitSprite(unit);
9854
9850
  if (url) urls.push(url);
9855
9851
  } else if (unit.unitType) {
9856
- const url = resolveManifestUrl(assetManifest?.units?.[unit.unitType]);
9852
+ const url = resolveManifestUrl7(assetManifest?.units?.[unit.unitType]);
9857
9853
  if (url) urls.push(url);
9858
9854
  }
9859
9855
  }
9860
9856
  if (assetManifest?.effects) {
9861
9857
  for (const path of Object.values(assetManifest.effects)) {
9862
- const url = resolveManifestUrl(path);
9858
+ const url = resolveManifestUrl7(path);
9863
9859
  if (url) urls.push(url);
9864
9860
  }
9865
9861
  }
@@ -9867,7 +9863,7 @@ function IsometricCanvas({
9867
9863
  if (atlasSheetUrls.length) urls.push(...atlasSheetUrls);
9868
9864
  if (backgroundImage) urls.push(backgroundImage);
9869
9865
  return [...new Set(urls.filter(Boolean))];
9870
- }, [sortedTiles, features, units, getTerrainSprite, getFeatureSprite, getUnitSprite, effectSpriteUrls, atlasSheetUrls, backgroundImage, assetManifest, resolveManifestUrl]);
9866
+ }, [sortedTiles, features, units, getTerrainSprite, getFeatureSprite, getUnitSprite, effectSpriteUrls, atlasSheetUrls, backgroundImage, assetManifest, resolveManifestUrl7]);
9871
9867
  const { getImage, pendingCount } = useImageCache(spriteUrls);
9872
9868
  useEffect(() => {
9873
9869
  if (typeof window === "undefined") return;
@@ -9892,14 +9888,14 @@ function IsometricCanvas({
9892
9888
  lerpToTarget
9893
9889
  } = useCamera();
9894
9890
  const resolveTerrainSpriteUrl = useCallback((tile) => {
9895
- return tile.terrainSprite || getTerrainSprite?.(tile.terrain ?? "") || resolveManifestUrl(assetManifest?.terrains?.[tile.terrain ?? ""]);
9896
- }, [getTerrainSprite, assetManifest, resolveManifestUrl]);
9891
+ return tile.terrainSprite || getTerrainSprite?.(tile.terrain ?? "") || resolveManifestUrl7(assetManifest?.terrains?.[tile.terrain ?? ""]);
9892
+ }, [getTerrainSprite, assetManifest, resolveManifestUrl7]);
9897
9893
  const resolveFeatureSpriteUrl = useCallback((featureType) => {
9898
- return getFeatureSprite?.(featureType) || resolveManifestUrl(assetManifest?.features?.[featureType]);
9899
- }, [getFeatureSprite, assetManifest, resolveManifestUrl]);
9894
+ return getFeatureSprite?.(featureType) || resolveManifestUrl7(assetManifest?.features?.[featureType]);
9895
+ }, [getFeatureSprite, assetManifest, resolveManifestUrl7]);
9900
9896
  const resolveUnitSpriteUrl = useCallback((unit) => {
9901
- return unit.sprite || getUnitSprite?.(unit) || (unit.unitType ? resolveManifestUrl(assetManifest?.units?.[unit.unitType]) : void 0);
9902
- }, [getUnitSprite, assetManifest, resolveManifestUrl]);
9897
+ return unit.sprite || getUnitSprite?.(unit) || (unit.unitType ? resolveManifestUrl7(assetManifest?.units?.[unit.unitType]) : void 0);
9898
+ }, [getUnitSprite, assetManifest, resolveManifestUrl7]);
9903
9899
  const drawMinimap = useCallback(() => {
9904
9900
  if (!showMinimap) return;
9905
9901
  const miniCanvas = minimapRef.current;
@@ -10001,7 +9997,15 @@ function IsometricCanvas({
10001
9997
  const spriteUrl = resolveTerrainSpriteUrl(tile);
10002
9998
  const img = spriteUrl ? getImage(spriteUrl) : null;
10003
9999
  if (img) {
10004
- ctx.drawImage(img, pos.x, pos.y, scaledTileWidth, scaledTileHeight);
10000
+ if (img.naturalWidth === 0) {
10001
+ ctx.drawImage(img, pos.x, pos.y, scaledTileWidth, scaledTileHeight);
10002
+ } else {
10003
+ const drawW = scaledTileWidth;
10004
+ const drawH = scaledTileWidth * (img.naturalHeight / img.naturalWidth);
10005
+ const drawX = pos.x;
10006
+ const drawY = pos.y + scaledTileHeight - drawH;
10007
+ ctx.drawImage(img, drawX, drawY, drawW, drawH);
10008
+ }
10005
10009
  } else {
10006
10010
  const centerX = pos.x + scaledTileWidth / 2;
10007
10011
  const topY = pos.y + scaledDiamondTopY;
@@ -12260,7 +12264,7 @@ var init_CodeBlock = __esm({
12260
12264
  };
12261
12265
  LINE_PROPS_FN = (n) => ({ "data-line": String(n - 1) });
12262
12266
  HIDDEN_LINE_NUMBERS = { display: "none" };
12263
- CodeBlock = React76__default.memo(
12267
+ CodeBlock = React79__default.memo(
12264
12268
  ({
12265
12269
  code: rawCode,
12266
12270
  language = "text",
@@ -12429,24 +12433,24 @@ var init_CodeBlock = __esm({
12429
12433
  return;
12430
12434
  }
12431
12435
  lineEls.forEach((el) => {
12432
- const num2 = parseInt(el.getAttribute("data-line") ?? "-1", 10);
12433
- if (hiddenLines.has(num2)) {
12436
+ const num3 = parseInt(el.getAttribute("data-line") ?? "-1", 10);
12437
+ if (hiddenLines.has(num3)) {
12434
12438
  el.style.display = "none";
12435
12439
  return;
12436
12440
  }
12437
12441
  el.style.display = "";
12438
12442
  el.style.position = "relative";
12439
12443
  el.style.paddingLeft = "1.2em";
12440
- const region = foldStartMap.get(num2);
12444
+ const region = foldStartMap.get(num3);
12441
12445
  if (!region) return;
12442
- const isCollapsed = collapsed.has(num2);
12446
+ const isCollapsed = collapsed.has(num3);
12443
12447
  const toggle = document.createElement("span");
12444
12448
  toggle.className = "fold-toggle";
12445
12449
  toggle.textContent = isCollapsed ? "\u25B6" : "\u25BC";
12446
12450
  toggle.style.cssText = "position:absolute;left:0;top:0;width:1.2em;text-align:center;cursor:pointer;color:#858585;font-size:10px;user-select:none;line-height:inherit;height:100%";
12447
12451
  toggle.addEventListener("click", (e) => {
12448
12452
  e.stopPropagation();
12449
- toggleFoldRef.current(num2);
12453
+ toggleFoldRef.current(num3);
12450
12454
  });
12451
12455
  el.insertBefore(toggle, el.firstChild);
12452
12456
  if (isCollapsed) {
@@ -12847,7 +12851,7 @@ var init_MarkdownContent = __esm({
12847
12851
  init_Box();
12848
12852
  init_CodeBlock();
12849
12853
  init_cn();
12850
- MarkdownContent = React76__default.memo(
12854
+ MarkdownContent = React79__default.memo(
12851
12855
  ({ content, direction = "ltr", className }) => {
12852
12856
  const { t: _t } = useTranslate();
12853
12857
  const safeContent = typeof content === "string" ? content : String(content ?? "");
@@ -14164,7 +14168,7 @@ var init_StateMachineView = __esm({
14164
14168
  style: { top: title ? 30 : 0 },
14165
14169
  children: [
14166
14170
  entity && /* @__PURE__ */ jsx(EntityBox, { entity, config }),
14167
- states.map((state) => renderStateNode ? /* @__PURE__ */ jsx(React76__default.Fragment, { children: renderStateNode(state, config) }, state.id) : /* @__PURE__ */ jsx(
14171
+ states.map((state) => renderStateNode ? /* @__PURE__ */ jsx(React79__default.Fragment, { children: renderStateNode(state, config) }, state.id) : /* @__PURE__ */ jsx(
14168
14172
  StateNode,
14169
14173
  {
14170
14174
  state,
@@ -18065,8 +18069,8 @@ function EmojiEffect({
18065
18069
  );
18066
18070
  }
18067
18071
  function CanvasEffect({
18068
- effectSpriteUrl = "https://almadar-kflow-assets.web.app/shared/effects/gas/gas00.png",
18069
- assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/effects/",
18072
+ effectSpriteUrl,
18073
+ assetBaseUrl,
18070
18074
  ...props
18071
18075
  }) {
18072
18076
  const eventBus = useEventBus();
@@ -18109,6 +18113,380 @@ var init_CanvasEffect = __esm({
18109
18113
  CanvasEffect.displayName = "CanvasEffect";
18110
18114
  }
18111
18115
  });
18116
+ function CardHand({
18117
+ cards = DEFAULT_CARDS,
18118
+ selectedId = "",
18119
+ size = "md",
18120
+ onCardClick,
18121
+ cardClickEvent,
18122
+ emptyLabel = "No cards in hand",
18123
+ className
18124
+ }) {
18125
+ const eventBus = useEventBus();
18126
+ const handleClick = React79.useCallback(
18127
+ (card) => {
18128
+ if (card.disabled) return;
18129
+ onCardClick?.(card.id);
18130
+ if (cardClickEvent) {
18131
+ eventBus.emit(`UI:${cardClickEvent}`, { cardId: card.id });
18132
+ }
18133
+ },
18134
+ [onCardClick, cardClickEvent, eventBus]
18135
+ );
18136
+ if (cards.length === 0) {
18137
+ return /* @__PURE__ */ jsx(Box, { className: cn("flex items-center justify-center p-4", className), children: /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: emptyLabel }) });
18138
+ }
18139
+ const art = artSizeMap[size];
18140
+ return /* @__PURE__ */ jsx(Box, { className: cn("flex flex-wrap gap-2 justify-center items-end", className), children: cards.map((card) => {
18141
+ const selected = selectedId === card.id;
18142
+ return /* @__PURE__ */ jsxs(
18143
+ "button",
18144
+ {
18145
+ type: "button",
18146
+ onClick: () => handleClick(card),
18147
+ disabled: card.disabled,
18148
+ title: card.title,
18149
+ className: cn(
18150
+ "card-hand-card relative flex flex-col items-center rounded-interactive border-2",
18151
+ "bg-card/90 px-1.5 pt-1.5 pb-1 transition-all duration-150",
18152
+ cardSizeMap[size],
18153
+ card.disabled ? "border-border opacity-50 cursor-not-allowed" : "border-accent hover:brightness-125 hover:-translate-y-1 cursor-pointer",
18154
+ selected && "ring-2 ring-foreground ring-offset-1 ring-offset-background -translate-y-1"
18155
+ ),
18156
+ children: [
18157
+ card.cost != null && /* @__PURE__ */ jsx(
18158
+ "span",
18159
+ {
18160
+ className: cn(
18161
+ "absolute -top-2 -left-2 flex items-center justify-center",
18162
+ "min-w-[22px] h-[22px] rounded-full px-1",
18163
+ "bg-info text-foreground text-xs font-bold border border-background"
18164
+ ),
18165
+ children: card.cost
18166
+ }
18167
+ ),
18168
+ /* @__PURE__ */ jsx(Box, { className: "flex-1 flex items-center justify-center w-full", children: card.iconUrl ? /* @__PURE__ */ jsx(
18169
+ "img",
18170
+ {
18171
+ src: card.iconUrl,
18172
+ alt: card.title ?? card.id,
18173
+ width: art,
18174
+ height: art,
18175
+ style: { imageRendering: "pixelated", objectFit: "contain" },
18176
+ className: "flex-shrink-0"
18177
+ }
18178
+ ) : /* @__PURE__ */ jsx(
18179
+ Box,
18180
+ {
18181
+ className: "rounded bg-muted/40",
18182
+ style: { width: art, height: art }
18183
+ }
18184
+ ) }),
18185
+ card.title != null && /* @__PURE__ */ jsx(
18186
+ Typography,
18187
+ {
18188
+ variant: "small",
18189
+ className: "w-full truncate text-center text-[10px] leading-tight font-semibold",
18190
+ children: card.title
18191
+ }
18192
+ ),
18193
+ (card.attack != null || card.defense != null) && /* @__PURE__ */ jsxs(Box, { className: "flex w-full items-center justify-between px-0.5 text-[10px] font-bold leading-none", children: [
18194
+ /* @__PURE__ */ jsx("span", { className: "text-error", children: card.attack != null ? `\u2694${card.attack}` : "" }),
18195
+ /* @__PURE__ */ jsx("span", { className: "text-info", children: card.defense != null ? `\u{1F6E1}${card.defense}` : "" })
18196
+ ] })
18197
+ ]
18198
+ },
18199
+ card.id
18200
+ );
18201
+ }) });
18202
+ }
18203
+ var cardSizeMap, artSizeMap, DEFAULT_CARDS;
18204
+ var init_CardHand = __esm({
18205
+ "components/game/molecules/CardHand.tsx"() {
18206
+ "use client";
18207
+ init_cn();
18208
+ init_useEventBus();
18209
+ init_Box();
18210
+ init_Typography();
18211
+ cardSizeMap = {
18212
+ sm: "w-16 h-24",
18213
+ md: "w-20 h-28",
18214
+ lg: "w-24 h-36"
18215
+ };
18216
+ artSizeMap = {
18217
+ sm: 40,
18218
+ md: 52,
18219
+ lg: 64
18220
+ };
18221
+ DEFAULT_CARDS = [
18222
+ { id: "card-1", title: "Brawler", cost: 1, attack: 2, defense: 1 },
18223
+ { id: "card-2", title: "Guardian", cost: 2, attack: 1, defense: 4 },
18224
+ { id: "card-3", title: "Striker", cost: 3, attack: 5, defense: 2 }
18225
+ ];
18226
+ CardHand.displayName = "CardHand";
18227
+ }
18228
+ });
18229
+ function resolveManifestUrl(manifest, relative) {
18230
+ if (relative == null) return void 0;
18231
+ if (/^https?:\/\//.test(relative)) return relative;
18232
+ const base = manifest?.baseUrl;
18233
+ if (base == null) return relative;
18234
+ const cleanBase = base.replace(/\/$/, "");
18235
+ const cleanRel = relative.replace(/^\//, "");
18236
+ return `${cleanBase}/${cleanRel}`;
18237
+ }
18238
+ function rowToCard(r) {
18239
+ return {
18240
+ id: str(r.id),
18241
+ iconKey: r.iconKey == null ? void 0 : str(r.iconKey),
18242
+ title: r.title == null ? void 0 : str(r.title),
18243
+ cost: r.cost == null ? void 0 : num(r.cost),
18244
+ attack: r.attack == null ? void 0 : num(r.attack),
18245
+ defense: r.defense == null ? void 0 : num(r.defense)
18246
+ };
18247
+ }
18248
+ function toHandCards(cards, manifest, mana) {
18249
+ return cards.map((c) => {
18250
+ const key = c.iconKey ?? c.id;
18251
+ const iconUrl = resolveManifestUrl(manifest, manifest?.cards?.[key]);
18252
+ return {
18253
+ id: c.id,
18254
+ iconUrl,
18255
+ title: c.title,
18256
+ cost: c.cost,
18257
+ attack: c.attack,
18258
+ defense: c.defense,
18259
+ disabled: c.cost != null && c.cost > mana
18260
+ };
18261
+ });
18262
+ }
18263
+ function CardBattlerBoard({
18264
+ entity,
18265
+ deck: propDeck,
18266
+ hand: propHand,
18267
+ board: propBoard,
18268
+ mana: propMana,
18269
+ maxMana: propMaxMana,
18270
+ turn: propTurn,
18271
+ result: propResult,
18272
+ assetManifest: propAssetManifest,
18273
+ playCardEvent,
18274
+ endTurnEvent,
18275
+ playAgainEvent,
18276
+ gameEndEvent,
18277
+ className
18278
+ }) {
18279
+ const board = boardEntity(entity) ?? {};
18280
+ const eventBus = useEventBus();
18281
+ const { t } = useTranslate();
18282
+ const assetManifest = propAssetManifest ?? board.assetManifest;
18283
+ const entityHand = useMemo(() => rows(board.hand).map(rowToCard), [board.hand]);
18284
+ const rawHand = propHand ?? entityHand;
18285
+ const hand = rawHand.length > 0 ? rawHand : DEFAULT_HAND;
18286
+ const entityBoard = useMemo(() => rows(board.board).map(rowToCard), [board.board]);
18287
+ const playedCards = propBoard ?? entityBoard;
18288
+ const entityDeck = useMemo(() => rows(board.deck).map(rowToCard), [board.deck]);
18289
+ const deck = propDeck ?? entityDeck;
18290
+ const mana = propMana ?? num(board.mana, 3);
18291
+ const maxMana = propMaxMana ?? num(board.maxMana, 3);
18292
+ const turn = propTurn ?? num(board.turn, 1);
18293
+ const result = propResult ?? (str(board.result) || "none");
18294
+ const handCards = useMemo(() => toHandCards(hand, assetManifest, mana), [hand, assetManifest, mana]);
18295
+ const boardCards = useMemo(() => toHandCards(playedCards, assetManifest, mana), [playedCards, assetManifest, mana]);
18296
+ const emittedGameEnd = useRef(false);
18297
+ if (result !== "none" && !emittedGameEnd.current) {
18298
+ emittedGameEnd.current = true;
18299
+ if (gameEndEvent) {
18300
+ eventBus.emit(`UI:${gameEndEvent}`, { result });
18301
+ }
18302
+ }
18303
+ if (result === "none") {
18304
+ emittedGameEnd.current = false;
18305
+ }
18306
+ const handlePlayCard = useCallback((cardId) => {
18307
+ if (result !== "none") return;
18308
+ if (playCardEvent) {
18309
+ eventBus.emit(`UI:${playCardEvent}`, { cardId });
18310
+ }
18311
+ }, [result, playCardEvent, eventBus]);
18312
+ const handleEndTurn = useCallback(() => {
18313
+ if (result !== "none") return;
18314
+ if (endTurnEvent) {
18315
+ eventBus.emit(`UI:${endTurnEvent}`, {});
18316
+ }
18317
+ }, [result, endTurnEvent, eventBus]);
18318
+ const handlePlayAgain = useCallback(() => {
18319
+ if (playAgainEvent) {
18320
+ eventBus.emit(`UI:${playAgainEvent}`, {});
18321
+ }
18322
+ }, [playAgainEvent, eventBus]);
18323
+ return /* @__PURE__ */ jsxs(VStack, { className: cn("card-battler-board relative min-h-[600px] bg-background", className), gap: "none", children: [
18324
+ /* @__PURE__ */ jsxs(
18325
+ HStack,
18326
+ {
18327
+ className: "px-4 py-2 border-b border-border bg-surface",
18328
+ gap: "lg",
18329
+ align: "center",
18330
+ children: [
18331
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
18332
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("cb.mana") ?? "Mana" }),
18333
+ /* @__PURE__ */ jsxs(Typography, { variant: "body2", weight: "bold", className: "text-info", children: [
18334
+ mana,
18335
+ " / ",
18336
+ maxMana
18337
+ ] })
18338
+ ] }),
18339
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
18340
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("cb.turn") ?? "Turn" }),
18341
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", children: turn })
18342
+ ] }),
18343
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
18344
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("cb.deck") ?? "Deck" }),
18345
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", children: deck.length })
18346
+ ] }),
18347
+ /* @__PURE__ */ jsx(Box, { className: "ml-auto", children: result === "none" && /* @__PURE__ */ jsx(Button, { variant: "primary", size: "sm", onClick: handleEndTurn, children: t("cb.endTurn") ?? "End Turn" }) })
18348
+ ]
18349
+ }
18350
+ ),
18351
+ /* @__PURE__ */ jsxs(Box, { className: "flex-1 relative p-4 overflow-auto", children: [
18352
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", className: "mb-2 uppercase tracking-wide", children: t("cb.battlefield") ?? "Battlefield" }),
18353
+ boardCards.length > 0 ? /* @__PURE__ */ jsx(
18354
+ CardHand,
18355
+ {
18356
+ cards: boardCards,
18357
+ size: "md",
18358
+ className: "justify-start"
18359
+ }
18360
+ ) : /* @__PURE__ */ jsx(Box, { className: "flex items-center justify-center h-32 rounded-interactive border-2 border-dashed border-border", children: /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("cb.emptyBoard") ?? "Play a card to begin" }) })
18361
+ ] }),
18362
+ /* @__PURE__ */ jsxs(Box, { className: "border-t border-border bg-surface px-4 py-3", children: [
18363
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", className: "mb-2 uppercase tracking-wide", children: t("cb.hand") ?? "Your Hand" }),
18364
+ /* @__PURE__ */ jsx(
18365
+ CardHand,
18366
+ {
18367
+ cards: handCards,
18368
+ size: "lg",
18369
+ onCardClick: handlePlayCard,
18370
+ emptyLabel: t("cb.emptyHand") ?? "No cards in hand"
18371
+ }
18372
+ )
18373
+ ] }),
18374
+ result !== "none" && /* @__PURE__ */ jsx(Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm", children: /* @__PURE__ */ jsxs(VStack, { className: "text-center p-8", gap: "lg", children: [
18375
+ /* @__PURE__ */ jsx(
18376
+ Typography,
18377
+ {
18378
+ variant: "h2",
18379
+ className: cn(
18380
+ "text-4xl font-black tracking-widest uppercase",
18381
+ result === "won" ? "text-warning" : "text-error"
18382
+ ),
18383
+ children: result === "won" ? t("cb.victory") ?? "Victory!" : t("cb.defeat") ?? "Defeat!"
18384
+ }
18385
+ ),
18386
+ /* @__PURE__ */ jsx(
18387
+ Button,
18388
+ {
18389
+ variant: "primary",
18390
+ className: "px-8 py-3 font-semibold",
18391
+ onClick: handlePlayAgain,
18392
+ children: t("cb.playAgain") ?? "Play Again"
18393
+ }
18394
+ )
18395
+ ] }) })
18396
+ ] });
18397
+ }
18398
+ var DEFAULT_HAND;
18399
+ var init_CardBattlerBoard = __esm({
18400
+ "components/game/organisms/CardBattlerBoard.tsx"() {
18401
+ "use client";
18402
+ init_cn();
18403
+ init_useEventBus();
18404
+ init_Box();
18405
+ init_Button();
18406
+ init_Typography();
18407
+ init_Stack();
18408
+ init_CardHand();
18409
+ init_boardEntity();
18410
+ DEFAULT_HAND = [
18411
+ { id: "c1", iconKey: "bear", title: "Bear", cost: 3, attack: 5, defense: 4 },
18412
+ { id: "c2", iconKey: "owl", title: "Owl", cost: 1, attack: 2, defense: 1 },
18413
+ { id: "c3", iconKey: "gorilla", title: "Gorilla", cost: 4, attack: 6, defense: 5 },
18414
+ { id: "c4", iconKey: "frog", title: "Frog", cost: 1, attack: 1, defense: 2 },
18415
+ { id: "c5", iconKey: "snake", title: "Snake", cost: 2, attack: 4, defense: 1 }
18416
+ ];
18417
+ CardBattlerBoard.displayName = "CardBattlerBoard";
18418
+ }
18419
+ });
18420
+ function isEntityRow(value) {
18421
+ return value != null && typeof value === "object" && !Array.isArray(value);
18422
+ }
18423
+ function CardBattlerTemplate({
18424
+ entity,
18425
+ title = "Card Battler",
18426
+ deck,
18427
+ hand,
18428
+ board,
18429
+ mana,
18430
+ maxMana,
18431
+ turn,
18432
+ result,
18433
+ assetManifest,
18434
+ playCardEvent,
18435
+ endTurnEvent,
18436
+ playAgainEvent,
18437
+ gameEndEvent,
18438
+ className
18439
+ }) {
18440
+ const resolved = isEntityRow(entity) ? entity : void 0;
18441
+ return /* @__PURE__ */ jsxs(
18442
+ Box,
18443
+ {
18444
+ display: "flex",
18445
+ fullHeight: true,
18446
+ className: cn("card-battler-template flex-col", className),
18447
+ children: [
18448
+ /* @__PURE__ */ jsx(
18449
+ HStack,
18450
+ {
18451
+ gap: "sm",
18452
+ align: "center",
18453
+ className: "px-4 py-3 border-b-2 border-border bg-surface shrink-0",
18454
+ children: /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title })
18455
+ }
18456
+ ),
18457
+ /* @__PURE__ */ jsx(Box, { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsx(
18458
+ CardBattlerBoard,
18459
+ {
18460
+ entity: resolved,
18461
+ deck,
18462
+ hand,
18463
+ board,
18464
+ mana,
18465
+ maxMana,
18466
+ turn,
18467
+ result,
18468
+ assetManifest,
18469
+ playCardEvent,
18470
+ endTurnEvent,
18471
+ playAgainEvent,
18472
+ gameEndEvent,
18473
+ className: "h-full"
18474
+ }
18475
+ ) })
18476
+ ]
18477
+ }
18478
+ );
18479
+ }
18480
+ var init_CardBattlerTemplate = __esm({
18481
+ "components/game/templates/CardBattlerTemplate.tsx"() {
18482
+ init_cn();
18483
+ init_Box();
18484
+ init_Stack();
18485
+ init_Typography();
18486
+ init_CardBattlerBoard();
18487
+ CardBattlerTemplate.displayName = "CardBattlerTemplate";
18488
+ }
18489
+ });
18112
18490
 
18113
18491
  // lib/getNestedValue.ts
18114
18492
  function getNestedValue(obj, path) {
@@ -19937,6 +20315,315 @@ var init_ChoiceButton = __esm({
19937
20315
  ChoiceButton.displayName = "ChoiceButton";
19938
20316
  }
19939
20317
  });
20318
+ function resolveManifestUrl2(manifest, relative) {
20319
+ if (relative == null) return void 0;
20320
+ if (/^https?:\/\//.test(relative)) return relative;
20321
+ const base = manifest?.baseUrl;
20322
+ if (base == null) return relative;
20323
+ const cleanBase = base.replace(/\/$/, "");
20324
+ const cleanRel = relative.replace(/^\//, "");
20325
+ return `${cleanBase}/${cleanRel}`;
20326
+ }
20327
+ function buildDefaultCBTiles() {
20328
+ const tiles = [];
20329
+ for (let y = 0; y < CB_GRID_H; y++) {
20330
+ for (let x = 0; x < CB_GRID_W; x++) {
20331
+ const variant = (x * 3 + y * 5 + (x ^ y)) % 3;
20332
+ const terrainKey = ["grass", "stone", "dirt"][variant];
20333
+ tiles.push({ x, y, terrain: terrainKey, passable: true });
20334
+ }
20335
+ }
20336
+ return tiles;
20337
+ }
20338
+ function tilesToIso(tiles, manifest) {
20339
+ return tiles.map((t) => {
20340
+ const key = t.terrain ?? "grass";
20341
+ const sprite = t.terrainSprite ?? resolveManifestUrl2(manifest, manifest?.terrains?.[key]) ?? resolveManifestUrl2(manifest, manifest?.terrains?.grass);
20342
+ return {
20343
+ x: t.x,
20344
+ y: t.y,
20345
+ terrain: t.terrain,
20346
+ terrainSprite: sprite,
20347
+ passable: t.passable
20348
+ };
20349
+ });
20350
+ }
20351
+ function buildingsToFeatures(buildings, manifest) {
20352
+ return buildings.map((b) => ({
20353
+ id: b.id,
20354
+ x: b.x,
20355
+ y: b.y,
20356
+ type: b.type,
20357
+ sprite: resolveManifestUrl2(manifest, manifest?.features?.[b.type])
20358
+ }));
20359
+ }
20360
+ function CityBuilderBoard({
20361
+ entity,
20362
+ tiles: propTiles,
20363
+ buildings: propBuildings,
20364
+ assetManifest: propAssetManifest,
20365
+ buildTypes: propBuildTypes,
20366
+ pendingBuildType: propPendingBuildType,
20367
+ gold: propGold,
20368
+ wood: propWood,
20369
+ population: propPopulation,
20370
+ result: propResult,
20371
+ scale = 0.3,
20372
+ unitScale = 0.6,
20373
+ spriteHeightRatio = 1.5,
20374
+ spriteMaxWidthRatio = 0.7,
20375
+ selectBuildTypeEvent,
20376
+ placeBuildingEvent,
20377
+ gameEndEvent,
20378
+ className
20379
+ }) {
20380
+ const board = boardEntity(entity) ?? {};
20381
+ const eventBus = useEventBus();
20382
+ const { t } = useTranslate();
20383
+ const assetManifest = propAssetManifest ?? board.assetManifest;
20384
+ const entityTiles = useMemo(
20385
+ () => rows(board.tiles).map((r) => ({
20386
+ x: num(r.x),
20387
+ y: num(r.y),
20388
+ terrain: r.terrain == null ? void 0 : str(r.terrain),
20389
+ terrainSprite: r.terrainSprite == null ? void 0 : str(r.terrainSprite),
20390
+ passable: r.passable !== false
20391
+ })),
20392
+ [board.tiles]
20393
+ );
20394
+ const rawTiles = propTiles ?? entityTiles;
20395
+ const tiles = rawTiles.length >= CB_GRID_W * CB_GRID_H ? rawTiles : DEFAULT_CB_TILES;
20396
+ const entityBuildings = useMemo(
20397
+ () => rows(board.buildings).map((r) => ({
20398
+ id: str(r.id),
20399
+ x: num(r.x),
20400
+ y: num(r.y),
20401
+ type: str(r.type)
20402
+ })),
20403
+ [board.buildings]
20404
+ );
20405
+ const buildings = propBuildings ?? entityBuildings;
20406
+ const buildTypes = propBuildTypes ?? DEFAULT_BUILD_TYPES;
20407
+ const gold = propGold ?? num(board.gold, 100);
20408
+ const wood = propWood ?? num(board.wood, 80);
20409
+ const population = propPopulation ?? num(board.population, 0);
20410
+ const pendingBuildType = propPendingBuildType ?? (str(board.pendingBuildType) || buildTypes[0]?.type || "");
20411
+ const result = propResult ?? (str(board.result) || "none");
20412
+ const [hoveredTile, setHoveredTile] = useState(null);
20413
+ const emittedGameEnd = useRef(false);
20414
+ if (result !== "none" && !emittedGameEnd.current) {
20415
+ emittedGameEnd.current = true;
20416
+ if (gameEndEvent) {
20417
+ eventBus.emit(`UI:${gameEndEvent}`, { result });
20418
+ }
20419
+ }
20420
+ if (result === "none") {
20421
+ emittedGameEnd.current = false;
20422
+ }
20423
+ const pendingCost = useMemo(
20424
+ () => buildTypes.find((b) => b.type === pendingBuildType)?.cost ?? 0,
20425
+ [buildTypes, pendingBuildType]
20426
+ );
20427
+ const buildingPositions = useMemo(
20428
+ () => new Set(buildings.map((b) => `${b.x},${b.y}`)),
20429
+ [buildings]
20430
+ );
20431
+ const validMoves = useMemo(() => {
20432
+ if (result !== "none" || gold < pendingCost) return [];
20433
+ return tiles.filter(
20434
+ (t2) => t2.passable !== false && !buildingPositions.has(`${t2.x},${t2.y}`)
20435
+ ).map((t2) => ({ x: t2.x, y: t2.y }));
20436
+ }, [tiles, buildingPositions, result, gold, pendingCost]);
20437
+ const isoTiles = useMemo(() => tilesToIso(tiles, assetManifest), [tiles, assetManifest]);
20438
+ const buildingFeatures = useMemo(() => buildingsToFeatures(buildings, assetManifest), [buildings, assetManifest]);
20439
+ const handleTileClick = useCallback((x, y) => {
20440
+ if (result !== "none") return;
20441
+ if (buildingPositions.has(`${x},${y}`)) return;
20442
+ if (gold < pendingCost) return;
20443
+ if (placeBuildingEvent) {
20444
+ eventBus.emit(`UI:${placeBuildingEvent}`, { x, y });
20445
+ }
20446
+ }, [result, buildingPositions, gold, pendingCost, placeBuildingEvent, eventBus]);
20447
+ const handleSelectBuildType = useCallback((buildType) => {
20448
+ if (selectBuildTypeEvent) {
20449
+ eventBus.emit(`UI:${selectBuildTypeEvent}`, { buildType });
20450
+ }
20451
+ }, [selectBuildTypeEvent, eventBus]);
20452
+ const canBuild = gold >= pendingCost && result === "none";
20453
+ return /* @__PURE__ */ jsxs(VStack, { className: cn("city-builder-board relative min-h-[600px] bg-background", className), gap: "none", children: [
20454
+ /* @__PURE__ */ jsxs(
20455
+ HStack,
20456
+ {
20457
+ className: "px-4 py-2 border-b border-border bg-surface",
20458
+ gap: "lg",
20459
+ align: "center",
20460
+ children: [
20461
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
20462
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("cb.gold") ?? "Gold" }),
20463
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", className: "text-warning", children: gold })
20464
+ ] }),
20465
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
20466
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("cb.wood") ?? "Wood" }),
20467
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", className: "text-success", children: wood })
20468
+ ] }),
20469
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
20470
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("cb.population") ?? "Population" }),
20471
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", children: population })
20472
+ ] }),
20473
+ /* @__PURE__ */ jsx(HStack, { gap: "xs", align: "center", className: "ml-auto", children: buildTypes.map((b) => /* @__PURE__ */ jsxs(
20474
+ Button,
20475
+ {
20476
+ variant: b.type === pendingBuildType ? "primary" : "secondary",
20477
+ size: "sm",
20478
+ disabled: gold < b.cost || result !== "none",
20479
+ onClick: () => handleSelectBuildType(b.type),
20480
+ children: [
20481
+ b.label,
20482
+ " (",
20483
+ b.cost,
20484
+ ")"
20485
+ ]
20486
+ },
20487
+ b.type
20488
+ )) })
20489
+ ]
20490
+ }
20491
+ ),
20492
+ /* @__PURE__ */ jsxs(Box, { className: "relative flex-1", children: [
20493
+ /* @__PURE__ */ jsx(
20494
+ IsometricCanvas_default,
20495
+ {
20496
+ tiles: isoTiles,
20497
+ features: buildingFeatures,
20498
+ assetBaseUrl: assetManifest?.baseUrl,
20499
+ assetManifest,
20500
+ validMoves,
20501
+ hoveredTile,
20502
+ onTileClick: handleTileClick,
20503
+ onTileHover: (x, y) => setHoveredTile({ x, y }),
20504
+ onTileLeave: () => setHoveredTile(null),
20505
+ scale,
20506
+ unitScale,
20507
+ spriteHeightRatio,
20508
+ spriteMaxWidthRatio
20509
+ }
20510
+ ),
20511
+ hoveredTile && canBuild && validMoves.some((m) => m.x === hoveredTile.x && m.y === hoveredTile.y) && /* @__PURE__ */ jsx(Box, { className: "absolute bottom-4 left-1/2 -translate-x-1/2 z-10 bg-background/80 rounded px-3 py-1 backdrop-blur-sm pointer-events-none", children: /* @__PURE__ */ jsx(Typography, { variant: "small", children: t("cb.placeBuilding") ?? `Build ${pendingBuildType} (${pendingCost} gold)` }) })
20512
+ ] }),
20513
+ result !== "none" && /* @__PURE__ */ jsx(Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm", children: /* @__PURE__ */ jsxs(VStack, { className: "text-center p-8", gap: "lg", children: [
20514
+ /* @__PURE__ */ jsx(
20515
+ Typography,
20516
+ {
20517
+ variant: "h2",
20518
+ className: cn(
20519
+ "text-4xl font-black tracking-widest uppercase",
20520
+ result === "won" ? "text-warning" : "text-error"
20521
+ ),
20522
+ children: result === "won" ? t("cb.victory") ?? "City Thrives!" : t("cb.defeat") ?? "City Collapsed!"
20523
+ }
20524
+ ),
20525
+ /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-muted-foreground", children: result === "won" ? t("cb.reachedGoal") ?? `Your city reached a population of ${population}.` : t("cb.bankrupt") ?? "Your city ran out of resources." })
20526
+ ] }) })
20527
+ ] });
20528
+ }
20529
+ var CB_GRID_W, CB_GRID_H, DEFAULT_BUILD_TYPES, DEFAULT_CB_TILES;
20530
+ var init_CityBuilderBoard = __esm({
20531
+ "components/game/organisms/CityBuilderBoard.tsx"() {
20532
+ "use client";
20533
+ init_cn();
20534
+ init_useEventBus();
20535
+ init_Box();
20536
+ init_Button();
20537
+ init_Typography();
20538
+ init_Stack();
20539
+ init_IsometricCanvas();
20540
+ init_boardEntity();
20541
+ CB_GRID_W = 12;
20542
+ CB_GRID_H = 12;
20543
+ DEFAULT_BUILD_TYPES = [
20544
+ { type: "house", label: "House", cost: 20 },
20545
+ { type: "farm", label: "Farm", cost: 15 },
20546
+ { type: "mine", label: "Mine", cost: 25 },
20547
+ { type: "barracks", label: "Barracks", cost: 40 }
20548
+ ];
20549
+ DEFAULT_CB_TILES = buildDefaultCBTiles();
20550
+ CityBuilderBoard.displayName = "CityBuilderBoard";
20551
+ }
20552
+ });
20553
+ function isEntityRow2(value) {
20554
+ return value != null && typeof value === "object" && !Array.isArray(value);
20555
+ }
20556
+ function CityBuilderTemplate({
20557
+ entity,
20558
+ title = "City Builder",
20559
+ tiles,
20560
+ buildings,
20561
+ buildTypes,
20562
+ pendingBuildType,
20563
+ assetManifest,
20564
+ gold,
20565
+ wood,
20566
+ population,
20567
+ result,
20568
+ scale,
20569
+ unitScale,
20570
+ selectBuildTypeEvent,
20571
+ placeBuildingEvent,
20572
+ gameEndEvent,
20573
+ className
20574
+ }) {
20575
+ const resolved = isEntityRow2(entity) ? entity : void 0;
20576
+ return /* @__PURE__ */ jsxs(
20577
+ Box,
20578
+ {
20579
+ display: "flex",
20580
+ fullHeight: true,
20581
+ className: cn("city-builder-template flex-col", className),
20582
+ children: [
20583
+ /* @__PURE__ */ jsx(
20584
+ HStack,
20585
+ {
20586
+ gap: "sm",
20587
+ align: "center",
20588
+ className: "px-4 py-3 border-b-2 border-border bg-surface shrink-0",
20589
+ children: /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title })
20590
+ }
20591
+ ),
20592
+ /* @__PURE__ */ jsx(Box, { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsx(
20593
+ CityBuilderBoard,
20594
+ {
20595
+ entity: resolved,
20596
+ tiles,
20597
+ buildings,
20598
+ buildTypes,
20599
+ pendingBuildType,
20600
+ assetManifest,
20601
+ gold,
20602
+ wood,
20603
+ population,
20604
+ result,
20605
+ scale,
20606
+ unitScale,
20607
+ selectBuildTypeEvent,
20608
+ placeBuildingEvent,
20609
+ gameEndEvent,
20610
+ className: "h-full"
20611
+ }
20612
+ ) })
20613
+ ]
20614
+ }
20615
+ );
20616
+ }
20617
+ var init_CityBuilderTemplate = __esm({
20618
+ "components/game/templates/CityBuilderTemplate.tsx"() {
20619
+ init_cn();
20620
+ init_Box();
20621
+ init_Stack();
20622
+ init_Typography();
20623
+ init_CityBuilderBoard();
20624
+ CityBuilderTemplate.displayName = "CityBuilderTemplate";
20625
+ }
20626
+ });
19940
20627
  function ClassifierBoard({
19941
20628
  entity,
19942
20629
  completeEvent = "PUZZLE_COMPLETE",
@@ -20915,7 +21602,7 @@ function CraftingRecipe({
20915
21602
  className
20916
21603
  }) {
20917
21604
  const eventBus = useEventBus();
20918
- const handleCraft = React76.useCallback(() => {
21605
+ const handleCraft = React79.useCallback(() => {
20919
21606
  onCraft?.();
20920
21607
  if (craftEvent) {
20921
21608
  eventBus.emit(craftEvent, { output: output.label });
@@ -20932,7 +21619,7 @@ function CraftingRecipe({
20932
21619
  children: [
20933
21620
  /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-wrap items-center", children: inputs.map((ingredient, index) => {
20934
21621
  const hasSufficient = ingredient.available >= ingredient.required;
20935
- return /* @__PURE__ */ jsxs(React76.Fragment, { children: [
21622
+ return /* @__PURE__ */ jsxs(React79.Fragment, { children: [
20936
21623
  /* @__PURE__ */ jsx(Box, { className: "relative", children: /* @__PURE__ */ jsx(
20937
21624
  ItemSlot,
20938
21625
  {
@@ -21004,8 +21691,8 @@ function DPad({
21004
21691
  }) {
21005
21692
  const eventBus = useEventBus();
21006
21693
  const sizes = sizeMap6[size];
21007
- const [activeDirections, setActiveDirections] = React76.useState(/* @__PURE__ */ new Set());
21008
- const handlePress = React76.useCallback(
21694
+ const [activeDirections, setActiveDirections] = React79.useState(/* @__PURE__ */ new Set());
21695
+ const handlePress = React79.useCallback(
21009
21696
  (direction) => {
21010
21697
  setActiveDirections((prev) => new Set(prev).add(direction));
21011
21698
  if (directionEvent) eventBus.emit(`UI:${directionEvent}`, { direction, pressed: true });
@@ -21013,7 +21700,7 @@ function DPad({
21013
21700
  },
21014
21701
  [directionEvent, eventBus, onDirection]
21015
21702
  );
21016
- const handleRelease = React76.useCallback(
21703
+ const handleRelease = React79.useCallback(
21017
21704
  (direction) => {
21018
21705
  setActiveDirections((prev) => {
21019
21706
  const next = new Set(prev);
@@ -21884,8 +22571,8 @@ var init_Menu = __esm({
21884
22571
  "bottom-end": "bottom-start"
21885
22572
  };
21886
22573
  const effectivePosition = direction === "rtl" ? rtlMirror[position] ?? position : position;
21887
- const triggerChild = React76__default.isValidElement(trigger) ? trigger : /* @__PURE__ */ jsx(Typography, { variant: "small", as: "span", children: trigger });
21888
- const triggerElement = React76__default.cloneElement(
22574
+ const triggerChild = React79__default.isValidElement(trigger) ? trigger : /* @__PURE__ */ jsx(Typography, { variant: "small", as: "span", children: trigger });
22575
+ const triggerElement = React79__default.cloneElement(
21889
22576
  triggerChild,
21890
22577
  {
21891
22578
  ref: triggerRef,
@@ -22019,14 +22706,14 @@ function useDataDnd(args) {
22019
22706
  const isZone = Boolean(dragGroup || accepts || sortable);
22020
22707
  const enabled = isZone || Boolean(dndRoot);
22021
22708
  const eventBus = useEventBus();
22022
- const parentRoot = React76__default.useContext(RootCtx);
22709
+ const parentRoot = React79__default.useContext(RootCtx);
22023
22710
  const isRoot = enabled && parentRoot === null;
22024
- const zoneId = React76__default.useId();
22711
+ const zoneId = React79__default.useId();
22025
22712
  const ownGroup = dragGroup ?? accepts ?? zoneId;
22026
- const [optimisticOrders, setOptimisticOrders] = React76__default.useState(() => /* @__PURE__ */ new Map());
22027
- const optimisticOrdersRef = React76__default.useRef(optimisticOrders);
22713
+ const [optimisticOrders, setOptimisticOrders] = React79__default.useState(() => /* @__PURE__ */ new Map());
22714
+ const optimisticOrdersRef = React79__default.useRef(optimisticOrders);
22028
22715
  optimisticOrdersRef.current = optimisticOrders;
22029
- const clearOptimisticOrder = React76__default.useCallback((group) => {
22716
+ const clearOptimisticOrder = React79__default.useCallback((group) => {
22030
22717
  setOptimisticOrders((prev) => {
22031
22718
  if (!prev.has(group)) return prev;
22032
22719
  const next = new Map(prev);
@@ -22051,7 +22738,7 @@ function useDataDnd(args) {
22051
22738
  const raw = it[dndItemIdField];
22052
22739
  return String(raw ?? `__idx_${idx}`);
22053
22740
  }).join("|");
22054
- const itemIds = React76__default.useMemo(
22741
+ const itemIds = React79__default.useMemo(
22055
22742
  () => orderedItems.map((it, idx) => {
22056
22743
  const raw = it[dndItemIdField];
22057
22744
  return raw ?? `__idx_${idx}`;
@@ -22059,7 +22746,7 @@ function useDataDnd(args) {
22059
22746
  [itemIdsSignature]
22060
22747
  );
22061
22748
  const itemsContentSig = items.map((it, idx) => String(it[dndItemIdField] ?? `__${idx}`)).join("|");
22062
- React76__default.useEffect(() => {
22749
+ React79__default.useEffect(() => {
22063
22750
  const root = isRoot ? null : parentRoot;
22064
22751
  if (root) {
22065
22752
  root.clearOptimisticOrder(ownGroup);
@@ -22067,20 +22754,20 @@ function useDataDnd(args) {
22067
22754
  clearOptimisticOrder(ownGroup);
22068
22755
  }
22069
22756
  }, [itemsContentSig, ownGroup]);
22070
- const zonesRef = React76__default.useRef(/* @__PURE__ */ new Map());
22071
- const registerZone = React76__default.useCallback((zoneId2, meta2) => {
22757
+ const zonesRef = React79__default.useRef(/* @__PURE__ */ new Map());
22758
+ const registerZone = React79__default.useCallback((zoneId2, meta2) => {
22072
22759
  zonesRef.current.set(zoneId2, meta2);
22073
22760
  }, []);
22074
- const unregisterZone = React76__default.useCallback((zoneId2) => {
22761
+ const unregisterZone = React79__default.useCallback((zoneId2) => {
22075
22762
  zonesRef.current.delete(zoneId2);
22076
22763
  }, []);
22077
- const [activeDrag, setActiveDrag] = React76__default.useState(null);
22078
- const [overZoneGroup, setOverZoneGroup] = React76__default.useState(null);
22079
- const meta = React76__default.useMemo(
22764
+ const [activeDrag, setActiveDrag] = React79__default.useState(null);
22765
+ const [overZoneGroup, setOverZoneGroup] = React79__default.useState(null);
22766
+ const meta = React79__default.useMemo(
22080
22767
  () => ({ group: ownGroup, dropEvent, reorderEvent, positionEvent, itemIds, rawItems: items, idField: dndItemIdField }),
22081
22768
  [ownGroup, dropEvent, reorderEvent, positionEvent, itemIds, items, dndItemIdField]
22082
22769
  );
22083
- React76__default.useEffect(() => {
22770
+ React79__default.useEffect(() => {
22084
22771
  const target = isRoot ? null : parentRoot;
22085
22772
  if (!target) {
22086
22773
  zonesRef.current.set(zoneId, meta);
@@ -22099,7 +22786,7 @@ function useDataDnd(args) {
22099
22786
  }, [parentRoot, isRoot, zoneId, meta]);
22100
22787
  const sensors = useAlmadarDndSensors(true);
22101
22788
  const collisionDetection = almadarDndCollisionDetection;
22102
- const findZoneByItem = React76__default.useCallback(
22789
+ const findZoneByItem = React79__default.useCallback(
22103
22790
  (id) => {
22104
22791
  for (const z of zonesRef.current.values()) {
22105
22792
  if (z.itemIds.includes(id)) return z;
@@ -22108,7 +22795,7 @@ function useDataDnd(args) {
22108
22795
  },
22109
22796
  []
22110
22797
  );
22111
- React76__default.useCallback(
22798
+ React79__default.useCallback(
22112
22799
  (group) => {
22113
22800
  for (const z of zonesRef.current.values()) {
22114
22801
  if (z.group === group) return z;
@@ -22117,7 +22804,7 @@ function useDataDnd(args) {
22117
22804
  },
22118
22805
  []
22119
22806
  );
22120
- const handleDragEnd = React76__default.useCallback(
22807
+ const handleDragEnd = React79__default.useCallback(
22121
22808
  (event) => {
22122
22809
  const { active, over } = event;
22123
22810
  const activeIdStr = String(active.id);
@@ -22208,8 +22895,8 @@ function useDataDnd(args) {
22208
22895
  },
22209
22896
  [eventBus]
22210
22897
  );
22211
- const sortableData = React76__default.useMemo(() => ({ dndGroup: ownGroup }), [ownGroup]);
22212
- const SortableItem = React76__default.useCallback(
22898
+ const sortableData = React79__default.useMemo(() => ({ dndGroup: ownGroup }), [ownGroup]);
22899
+ const SortableItem = React79__default.useCallback(
22213
22900
  ({ id, children }) => {
22214
22901
  const {
22215
22902
  attributes,
@@ -22249,7 +22936,7 @@ function useDataDnd(args) {
22249
22936
  id: droppableId,
22250
22937
  data: sortableData
22251
22938
  });
22252
- const ctx = React76__default.useContext(RootCtx);
22939
+ const ctx = React79__default.useContext(RootCtx);
22253
22940
  const activeDrag2 = ctx?.activeDrag ?? null;
22254
22941
  const overZoneGroup2 = ctx?.overZoneGroup ?? null;
22255
22942
  const isThisZoneOver = overZoneGroup2 === ownGroup;
@@ -22264,7 +22951,7 @@ function useDataDnd(args) {
22264
22951
  showForeignPlaceholder,
22265
22952
  ctxAvailable: ctx != null
22266
22953
  });
22267
- React76__default.useEffect(() => {
22954
+ React79__default.useEffect(() => {
22268
22955
  dndLog.info("dropzone:isOver:change", { droppableId, group: ownGroup, isOver, isThisZoneOver, showForeignPlaceholder, activeDragSourceGroup: activeDrag2?.sourceGroup ?? null });
22269
22956
  }, [droppableId, isOver, isThisZoneOver, showForeignPlaceholder]);
22270
22957
  return /* @__PURE__ */ jsx(
@@ -22278,11 +22965,11 @@ function useDataDnd(args) {
22278
22965
  }
22279
22966
  );
22280
22967
  };
22281
- const rootContextValue = React76__default.useMemo(
22968
+ const rootContextValue = React79__default.useMemo(
22282
22969
  () => ({ registerZone, unregisterZone, activeDrag, overZoneGroup, optimisticOrders, clearOptimisticOrder }),
22283
22970
  [registerZone, unregisterZone, activeDrag, overZoneGroup, optimisticOrders, clearOptimisticOrder]
22284
22971
  );
22285
- const handleDragStart = React76__default.useCallback((event) => {
22972
+ const handleDragStart = React79__default.useCallback((event) => {
22286
22973
  const sourceZone = findZoneByItem(event.active.id);
22287
22974
  const rect = event.active.rect.current.initial;
22288
22975
  const height = rect?.height && rect.height > 0 ? rect.height : 64;
@@ -22301,7 +22988,7 @@ function useDataDnd(args) {
22301
22988
  isRoot
22302
22989
  });
22303
22990
  }, [findZoneByItem, isRoot, zoneId]);
22304
- const handleDragOver = React76__default.useCallback((event) => {
22991
+ const handleDragOver = React79__default.useCallback((event) => {
22305
22992
  const { active, over } = event;
22306
22993
  const overData = over?.data?.current;
22307
22994
  const overGroup = overData?.dndGroup ?? null;
@@ -22371,7 +23058,7 @@ function useDataDnd(args) {
22371
23058
  return next;
22372
23059
  });
22373
23060
  }, []);
22374
- const handleDragCancel = React76__default.useCallback((event) => {
23061
+ const handleDragCancel = React79__default.useCallback((event) => {
22375
23062
  setActiveDrag(null);
22376
23063
  setOverZoneGroup(null);
22377
23064
  dndLog.warn("dragCancel", {
@@ -22379,12 +23066,12 @@ function useDataDnd(args) {
22379
23066
  reason: "dnd-kit cancelled the drag (escape key, pointer interrupted, or external)"
22380
23067
  });
22381
23068
  }, []);
22382
- const handleDragEndWithCleanup = React76__default.useCallback((event) => {
23069
+ const handleDragEndWithCleanup = React79__default.useCallback((event) => {
22383
23070
  handleDragEnd(event);
22384
23071
  setActiveDrag(null);
22385
23072
  setOverZoneGroup(null);
22386
23073
  }, [handleDragEnd]);
22387
- const wrapContainer = React76__default.useCallback(
23074
+ const wrapContainer = React79__default.useCallback(
22388
23075
  (children) => {
22389
23076
  if (!enabled) return children;
22390
23077
  const strategy = layout === "grid" ? rectSortingStrategy : verticalListSortingStrategy;
@@ -22438,7 +23125,7 @@ var init_useDataDnd = __esm({
22438
23125
  init_useAlmadarDndCollision();
22439
23126
  init_Box();
22440
23127
  dndLog = createLogger("almadar:ui:dnd");
22441
- RootCtx = React76__default.createContext(null);
23128
+ RootCtx = React79__default.createContext(null);
22442
23129
  }
22443
23130
  });
22444
23131
  function renderIconInput(icon, props) {
@@ -22964,7 +23651,7 @@ function DataList({
22964
23651
  }) {
22965
23652
  const eventBus = useEventBus();
22966
23653
  const { t } = useTranslate();
22967
- const [visibleCount, setVisibleCount] = React76__default.useState(pageSize || Infinity);
23654
+ const [visibleCount, setVisibleCount] = React79__default.useState(pageSize || Infinity);
22968
23655
  const fieldDefs = fields ?? columns ?? [];
22969
23656
  const allDataRaw = Array.isArray(entity) ? entity : entity ? [entity] : [];
22970
23657
  const dnd = useDataDnd({
@@ -22983,7 +23670,7 @@ function DataList({
22983
23670
  const data = pageSize > 0 ? allData.slice(0, visibleCount) : allData;
22984
23671
  const hasMoreLocal = pageSize > 0 && visibleCount < allData.length;
22985
23672
  const hasRenderProp = typeof children === "function";
22986
- React76__default.useEffect(() => {
23673
+ React79__default.useEffect(() => {
22987
23674
  const renderItemTypeOf = typeof schemaRenderItem;
22988
23675
  const childrenTypeOf = typeof children;
22989
23676
  if (data.length > 0 && !hasRenderProp) {
@@ -23088,7 +23775,7 @@ function DataList({
23088
23775
  const items2 = data.map((item) => item);
23089
23776
  const groups2 = groupBy ? groupData(items2, groupBy) : [{ label: "", items: items2 }];
23090
23777
  const contentField = titleField?.name ?? fieldDefs[0]?.name ?? "";
23091
- return /* @__PURE__ */ jsx(VStack, { gap: "sm", className: cn("py-2", className), children: groups2.map((group, gi) => /* @__PURE__ */ jsxs(React76__default.Fragment, { children: [
23778
+ return /* @__PURE__ */ jsx(VStack, { gap: "sm", className: cn("py-2", className), children: groups2.map((group, gi) => /* @__PURE__ */ jsxs(React79__default.Fragment, { children: [
23092
23779
  group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: "my-2" }),
23093
23780
  group.items.map((itemData, index) => {
23094
23781
  const id = itemData.id || `${gi}-${index}`;
@@ -23229,7 +23916,7 @@ function DataList({
23229
23916
  className
23230
23917
  ),
23231
23918
  children: [
23232
- groups.map((group, gi) => /* @__PURE__ */ jsxs(React76__default.Fragment, { children: [
23919
+ groups.map((group, gi) => /* @__PURE__ */ jsxs(React79__default.Fragment, { children: [
23233
23920
  group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: gi > 0 ? "mt-4" : "mt-0" }),
23234
23921
  group.items.map(
23235
23922
  (itemData, index) => renderItem(itemData, index, gi === groups.length - 1 && index === group.items.length - 1)
@@ -23336,8 +24023,8 @@ function ScalarControl({
23336
24023
  }
23337
24024
  const numeric = typeof value === "number";
23338
24025
  const initial = value === null ? "" : String(value);
23339
- const [draft, setDraft] = React76__default.useState(initial);
23340
- React76__default.useEffect(() => setDraft(initial), [initial]);
24026
+ const [draft, setDraft] = React79__default.useState(initial);
24027
+ React79__default.useEffect(() => setDraft(initial), [initial]);
23341
24028
  const commit = () => {
23342
24029
  if (numeric) {
23343
24030
  const n = draft.trim() === "" ? 0 : Number(draft);
@@ -23405,8 +24092,8 @@ function Row({
23405
24092
  onRemove,
23406
24093
  readonly
23407
24094
  }) {
23408
- const [keyDraft, setKeyDraft] = React76__default.useState(rowKey);
23409
- React76__default.useEffect(() => setKeyDraft(rowKey), [rowKey]);
24095
+ const [keyDraft, setKeyDraft] = React79__default.useState(rowKey);
24096
+ React79__default.useEffect(() => setKeyDraft(rowKey), [rowKey]);
23410
24097
  const container = isObj(value) || isArr(value);
23411
24098
  return /* @__PURE__ */ jsxs(VStack, { gap: "none", className: "group w-max min-w-full", children: [
23412
24099
  /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", className: "py-0.5 w-max", children: [
@@ -23449,7 +24136,7 @@ function ContainerNode({
23449
24136
  depth,
23450
24137
  readonly
23451
24138
  }) {
23452
- const [open, setOpen] = React76__default.useState(depth < 2);
24139
+ const [open, setOpen] = React79__default.useState(depth < 2);
23453
24140
  const array = isArr(value);
23454
24141
  const entries = array ? value.map((v, i) => [String(i), v]) : Object.entries(value);
23455
24142
  const setObjValue = (key, next) => {
@@ -23591,7 +24278,7 @@ var init_FormSection = __esm({
23591
24278
  columns = 1,
23592
24279
  className
23593
24280
  }) => {
23594
- const [collapsed, setCollapsed] = React76__default.useState(defaultCollapsed);
24281
+ const [collapsed, setCollapsed] = React79__default.useState(defaultCollapsed);
23595
24282
  const { t } = useTranslate();
23596
24283
  const eventBus = useEventBus();
23597
24284
  const gridClass = {
@@ -23599,7 +24286,7 @@ var init_FormSection = __esm({
23599
24286
  2: "grid-cols-1 md:grid-cols-2",
23600
24287
  3: "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
23601
24288
  }[columns];
23602
- React76__default.useCallback(() => {
24289
+ React79__default.useCallback(() => {
23603
24290
  if (collapsible) {
23604
24291
  setCollapsed((prev) => !prev);
23605
24292
  eventBus.emit("UI:TOGGLE_COLLAPSE", { collapsed: !collapsed });
@@ -24007,8 +24694,8 @@ function TextLikeControl({
24007
24694
  onCommit
24008
24695
  }) {
24009
24696
  const initial = value === void 0 || value === null ? "" : String(value);
24010
- const [draft, setDraft] = React76__default.useState(initial);
24011
- React76__default.useEffect(() => setDraft(initial), [initial]);
24697
+ const [draft, setDraft] = React79__default.useState(initial);
24698
+ React79__default.useEffect(() => setDraft(initial), [initial]);
24012
24699
  const commit = () => {
24013
24700
  if (numeric) {
24014
24701
  const n = draft.trim() === "" ? 0 : Number(draft);
@@ -24174,14 +24861,14 @@ var init_NodeSlotEditor = __esm({
24174
24861
  isObj2 = (v) => v !== null && v !== void 0 && typeof v === "object" && !Array.isArray(v);
24175
24862
  NodeSlotEditor = ({ value, onChange, className }) => {
24176
24863
  const { type, props, wasArray } = normalize(value);
24177
- const patterns = React76__default.useMemo(() => {
24864
+ const patterns = React79__default.useMemo(() => {
24178
24865
  try {
24179
24866
  return [...getKnownPatterns()].sort();
24180
24867
  } catch {
24181
24868
  return [];
24182
24869
  }
24183
24870
  }, []);
24184
- const options = React76__default.useMemo(
24871
+ const options = React79__default.useMemo(
24185
24872
  () => [{ value: "", label: "\u2014 none \u2014" }, ...patterns.map((p2) => ({ value: p2, label: p2 }))],
24186
24873
  [patterns]
24187
24874
  );
@@ -24193,7 +24880,7 @@ var init_NodeSlotEditor = __esm({
24193
24880
  const pattern = { type: nextType, ...nextProps };
24194
24881
  onChange(wasArray || value === void 0 ? [pattern] : pattern);
24195
24882
  };
24196
- const schemaEntries = React76__default.useMemo(() => {
24883
+ const schemaEntries = React79__default.useMemo(() => {
24197
24884
  if (!type) return [];
24198
24885
  const def = getPatternDefinition(type);
24199
24886
  if (!def?.propsSchema) return [];
@@ -25087,7 +25774,7 @@ var init_Grid = __esm({
25087
25774
  as: Component2 = "div"
25088
25775
  }) => {
25089
25776
  const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
25090
- return React76__default.createElement(
25777
+ return React79__default.createElement(
25091
25778
  Component2,
25092
25779
  {
25093
25780
  className: cn(
@@ -25275,8 +25962,8 @@ var init_Popover = __esm({
25275
25962
  onMouseEnter: handleOpen,
25276
25963
  onMouseLeave: handleClose
25277
25964
  };
25278
- const childElement = React76__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
25279
- const triggerElement = React76__default.cloneElement(
25965
+ const childElement = React79__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
25966
+ const triggerElement = React79__default.cloneElement(
25280
25967
  childElement,
25281
25968
  {
25282
25969
  ref: triggerRef,
@@ -26116,8 +26803,8 @@ var init_Tooltip = __esm({
26116
26803
  if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);
26117
26804
  };
26118
26805
  }, []);
26119
- const triggerElement = React76__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
26120
- const trigger = React76__default.cloneElement(triggerElement, {
26806
+ const triggerElement = React79__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
26807
+ const trigger = React79__default.cloneElement(triggerElement, {
26121
26808
  ref: triggerRef,
26122
26809
  onMouseEnter: handleMouseEnter,
26123
26810
  onMouseLeave: handleMouseLeave,
@@ -26203,7 +26890,7 @@ var init_WizardProgress = __esm({
26203
26890
  children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: normalizedSteps.map((step, index) => {
26204
26891
  const isActive = index === currentStep;
26205
26892
  const isCompleted = index < currentStep;
26206
- return /* @__PURE__ */ jsxs(React76__default.Fragment, { children: [
26893
+ return /* @__PURE__ */ jsxs(React79__default.Fragment, { children: [
26207
26894
  /* @__PURE__ */ jsx(
26208
26895
  "button",
26209
26896
  {
@@ -27248,9 +27935,9 @@ function ScoreDisplay({
27248
27935
  ...rest
27249
27936
  }) {
27250
27937
  const resolvedValue = typeof value === "number" && !Number.isNaN(value) ? value : typeof rest.score === "number" && !Number.isNaN(rest.score) ? rest.score : 0;
27251
- const [displayValue, setDisplayValue] = React76.useState(resolvedValue);
27252
- const [isAnimating, setIsAnimating] = React76.useState(false);
27253
- React76.useEffect(() => {
27938
+ const [displayValue, setDisplayValue] = React79.useState(resolvedValue);
27939
+ const [isAnimating, setIsAnimating] = React79.useState(false);
27940
+ React79.useEffect(() => {
27254
27941
  if (!animated || displayValue === resolvedValue) {
27255
27942
  setDisplayValue(resolvedValue);
27256
27943
  return;
@@ -27318,7 +28005,7 @@ var init_ScoreDisplay = __esm({
27318
28005
  }
27319
28006
  });
27320
28007
  function StatBadge({
27321
- assetUrl = DEFAULT_ASSET_URL7,
28008
+ assetUrl,
27322
28009
  label,
27323
28010
  value = 0,
27324
28011
  max,
@@ -27385,7 +28072,7 @@ function StatBadge({
27385
28072
  }
27386
28073
  );
27387
28074
  }
27388
- var sizeMap11, variantMap2, DEFAULT_ASSET_URL7;
28075
+ var sizeMap11, variantMap2;
27389
28076
  var init_StatBadge = __esm({
27390
28077
  "components/game/molecules/StatBadge.tsx"() {
27391
28078
  init_cn();
@@ -27404,7 +28091,6 @@ var init_StatBadge = __esm({
27404
28091
  warning: "bg-warning/15 border-warning/40 text-foreground",
27405
28092
  danger: "bg-error/15 border-error/40 text-foreground"
27406
28093
  };
27407
- DEFAULT_ASSET_URL7 = "https://almadar-kflow-assets.web.app/shared/effects/particles/light_01.png";
27408
28094
  StatBadge.displayName = "StatBadge";
27409
28095
  }
27410
28096
  });
@@ -27421,7 +28107,7 @@ function InventoryGrid({
27421
28107
  const eventBus = useEventBus();
27422
28108
  const slotCount = totalSlots ?? items.length;
27423
28109
  const emptySlotCount = Math.max(0, slotCount - items.length);
27424
- const handleSelect = React76.useCallback(
28110
+ const handleSelect = React79.useCallback(
27425
28111
  (id) => {
27426
28112
  onSelect?.(id);
27427
28113
  if (selectEvent) {
@@ -27488,7 +28174,7 @@ var init_InventoryGrid = __esm({
27488
28174
  }
27489
28175
  });
27490
28176
  function WaypointMarker({
27491
- assetUrl = DEFAULT_ASSET_URL8,
28177
+ assetUrl = DEFAULT_ASSET_URL7,
27492
28178
  label,
27493
28179
  icon,
27494
28180
  active = true,
@@ -27554,12 +28240,12 @@ function WaypointMarker({
27554
28240
  )
27555
28241
  ] });
27556
28242
  }
27557
- var DEFAULT_ASSET_URL8, sizeMap12, checkIcon;
28243
+ var DEFAULT_ASSET_URL7, sizeMap12, checkIcon;
27558
28244
  var init_WaypointMarker = __esm({
27559
28245
  "components/game/atoms/WaypointMarker.tsx"() {
27560
28246
  init_cn();
27561
28247
  init_Icon();
27562
- DEFAULT_ASSET_URL8 = "https://almadar-kflow-assets.web.app/shared/world-map/battle_marker.png";
28248
+ DEFAULT_ASSET_URL7 = "https://almadar-kflow-assets.web.app/shared/world-map/battle_marker.png";
27563
28249
  sizeMap12 = {
27564
28250
  sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1", img: 12 },
27565
28251
  md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5", img: 18 },
@@ -27734,35 +28420,37 @@ function GameCanvas2D({
27734
28420
  drawEvent,
27735
28421
  fps = 60,
27736
28422
  backgroundImage = "",
27737
- assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/",
28423
+ assetBaseUrl,
27738
28424
  className
27739
28425
  }) {
27740
- const canvasRef = React76.useRef(null);
27741
- const rafRef = React76.useRef(0);
27742
- const frameRef = React76.useRef(0);
27743
- const lastTimeRef = React76.useRef(0);
27744
- const imageCache = React76.useRef(/* @__PURE__ */ new Map());
28426
+ const canvasRef = React79.useRef(null);
28427
+ const rafRef = React79.useRef(0);
28428
+ const frameRef = React79.useRef(0);
28429
+ const lastTimeRef = React79.useRef(0);
28430
+ const imageCache = React79.useRef(/* @__PURE__ */ new Map());
27745
28431
  const emit = useEmitEvent();
27746
- const onDrawRef = React76.useRef(onDraw);
28432
+ const onDrawRef = React79.useRef(onDraw);
27747
28433
  onDrawRef.current = onDraw;
27748
- const onTickRef = React76.useRef(onTick);
28434
+ const onTickRef = React79.useRef(onTick);
27749
28435
  onTickRef.current = onTick;
27750
- const tickEventRef = React76.useRef(tickEvent);
28436
+ const tickEventRef = React79.useRef(tickEvent);
27751
28437
  tickEventRef.current = tickEvent;
27752
- const drawEventRef = React76.useRef(drawEvent);
28438
+ const drawEventRef = React79.useRef(drawEvent);
27753
28439
  drawEventRef.current = drawEvent;
27754
- const emitRef = React76.useRef(emit);
28440
+ const emitRef = React79.useRef(emit);
27755
28441
  emitRef.current = emit;
27756
- const assetBaseUrlRef = React76.useRef(assetBaseUrl);
28442
+ const assetBaseUrlRef = React79.useRef(assetBaseUrl);
27757
28443
  assetBaseUrlRef.current = assetBaseUrl;
27758
- const backgroundImageRef = React76.useRef(backgroundImage);
28444
+ const backgroundImageRef = React79.useRef(backgroundImage);
27759
28445
  backgroundImageRef.current = backgroundImage;
27760
- const widthRef = React76.useRef(width);
28446
+ const widthRef = React79.useRef(width);
27761
28447
  widthRef.current = width;
27762
- const heightRef = React76.useRef(height);
28448
+ const heightRef = React79.useRef(height);
27763
28449
  heightRef.current = height;
27764
- const loadImage = React76.useCallback((url) => {
27765
- const fullUrl = url.startsWith("http") ? url : `${assetBaseUrlRef.current}${url}`;
28450
+ const loadImage = React79.useCallback((url) => {
28451
+ const base = assetBaseUrlRef.current;
28452
+ if (!url.startsWith("http") && !base) return null;
28453
+ const fullUrl = url.startsWith("http") ? url : `${base}${url}`;
27766
28454
  const cached = imageCache.current.get(fullUrl);
27767
28455
  if (cached?.complete && cached.naturalWidth > 0) return cached;
27768
28456
  if (!cached) {
@@ -27773,7 +28461,7 @@ function GameCanvas2D({
27773
28461
  }
27774
28462
  return null;
27775
28463
  }, []);
27776
- React76.useEffect(() => {
28464
+ React79.useEffect(() => {
27777
28465
  const canvas = canvasRef.current;
27778
28466
  if (!canvas) return;
27779
28467
  const ctx = canvas.getContext("2d");
@@ -28077,7 +28765,7 @@ var init_ResourceBar = __esm({
28077
28765
  }
28078
28766
  });
28079
28767
  function TurnIndicator({
28080
- assetUrl = DEFAULT_ASSET_URL9,
28768
+ assetUrl = DEFAULT_ASSET_URL8,
28081
28769
  currentTurn = 1,
28082
28770
  maxTurns,
28083
28771
  activeTeam,
@@ -28127,7 +28815,7 @@ function TurnIndicator({
28127
28815
  }
28128
28816
  );
28129
28817
  }
28130
- var sizeMap14, DEFAULT_ASSET_URL9;
28818
+ var sizeMap14, DEFAULT_ASSET_URL8;
28131
28819
  var init_TurnIndicator = __esm({
28132
28820
  "components/game/atoms/TurnIndicator.tsx"() {
28133
28821
  init_cn();
@@ -28136,7 +28824,7 @@ var init_TurnIndicator = __esm({
28136
28824
  md: { wrapper: "text-sm gap-2 px-3 py-1", dot: "w-2 h-2" },
28137
28825
  lg: { wrapper: "text-base gap-2.5 px-4 py-1.5", dot: "w-2.5 h-2.5" }
28138
28826
  };
28139
- DEFAULT_ASSET_URL9 = "https://almadar-kflow-assets.web.app/shared/effects/particles/symbol_01.png";
28827
+ DEFAULT_ASSET_URL8 = "https://almadar-kflow-assets.web.app/shared/effects/particles/symbol_01.png";
28140
28828
  TurnIndicator.displayName = "TurnIndicator";
28141
28829
  }
28142
28830
  });
@@ -28149,7 +28837,7 @@ function TurnPanel({
28149
28837
  className
28150
28838
  }) {
28151
28839
  const eventBus = useEventBus();
28152
- const handleAction = React76.useCallback(
28840
+ const handleAction = React79.useCallback(
28153
28841
  (event) => {
28154
28842
  if (event) {
28155
28843
  eventBus.emit(event, { turn: currentTurn, phase, activeTeam });
@@ -28207,7 +28895,7 @@ var init_TurnPanel = __esm({
28207
28895
  }
28208
28896
  });
28209
28897
  function EnemyPlate({
28210
- assetUrl = DEFAULT_ASSET_URL10,
28898
+ assetUrl,
28211
28899
  name = "Shadow Guard",
28212
28900
  health = 80,
28213
28901
  maxHealth = 100,
@@ -28291,7 +28979,7 @@ function EnemyPlate({
28291
28979
  }
28292
28980
  );
28293
28981
  }
28294
- var effectVariantMap2, DEFAULT_ENEMY_EFFECTS, DEFAULT_ASSET_URL10;
28982
+ var effectVariantMap2, DEFAULT_ENEMY_EFFECTS;
28295
28983
  var init_EnemyPlate = __esm({
28296
28984
  "components/game/molecules/EnemyPlate.tsx"() {
28297
28985
  "use client";
@@ -28308,7 +28996,6 @@ var init_EnemyPlate = __esm({
28308
28996
  DEFAULT_ENEMY_EFFECTS = [
28309
28997
  { icon: "flame", label: "Burn", variant: "debuff" }
28310
28998
  ];
28311
- DEFAULT_ASSET_URL10 = "https://almadar-kflow-assets.web.app/shared/portraits/shadow-legion.png";
28312
28999
  EnemyPlate.displayName = "EnemyPlate";
28313
29000
  }
28314
29001
  });
@@ -28318,7 +29005,7 @@ function UnitCommandBar({
28318
29005
  className
28319
29006
  }) {
28320
29007
  const eventBus = useEventBus();
28321
- const handleCommand = React76.useCallback(
29008
+ const handleCommand = React79.useCallback(
28322
29009
  (event) => {
28323
29010
  if (event) {
28324
29011
  eventBus.emit(event, { unitId: selectedUnitId });
@@ -28459,6 +29146,9 @@ function DialogueBox({
28459
29146
  completeEvent,
28460
29147
  choiceEvent,
28461
29148
  advanceEvent,
29149
+ backgroundImage,
29150
+ portraitUrl,
29151
+ portraitScale = 1,
28462
29152
  className
28463
29153
  }) {
28464
29154
  const eventBus = useEventBus();
@@ -28574,64 +29264,98 @@ function DialogueBox({
28574
29264
  }
28575
29265
  }, [isTyping, skipTypewriter, dialogue.choices, selectedChoice, onChoice, onAdvance, choiceEvent, advanceEvent, eventBus]);
28576
29266
  const enabledChoices = dialogue.choices?.filter((c) => !c.disabled) ?? [];
28577
- return /* @__PURE__ */ jsx(
28578
- "div",
28579
- {
28580
- className: cn(
28581
- "fixed left-0 right-0 z-40",
28582
- position === "top" ? "top-0" : "bottom-0",
28583
- className
28584
- ),
28585
- onClick: handleClick,
28586
- onKeyDown: handleKeyDown,
28587
- tabIndex: 0,
28588
- role: "dialog",
28589
- "aria-label": "Dialogue",
28590
- children: /* @__PURE__ */ jsx("div", { className: "mx-4 my-4 bg-[var(--color-card)] bg-opacity-95 border-2 border-border rounded-container overflow-hidden", children: /* @__PURE__ */ jsxs("div", { className: "flex", children: [
28591
- dialogue.portrait && /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 p-4 border-r border-border", children: /* @__PURE__ */ jsx(
29267
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
29268
+ backgroundImage && /* @__PURE__ */ jsx(
29269
+ "div",
29270
+ {
29271
+ className: "fixed inset-0 z-20 bg-center bg-cover bg-no-repeat",
29272
+ style: { backgroundImage: `url(${backgroundImage})` },
29273
+ "aria-hidden": "true"
29274
+ }
29275
+ ),
29276
+ portraitUrl && /* @__PURE__ */ jsx(
29277
+ "div",
29278
+ {
29279
+ className: cn(
29280
+ "fixed left-1/2 -translate-x-1/2 z-30 pointer-events-none flex items-end",
29281
+ position === "top" ? "top-0" : "bottom-0"
29282
+ ),
29283
+ style: { height: `${60 * portraitScale}vh` },
29284
+ children: /* @__PURE__ */ jsx(
28592
29285
  "img",
28593
29286
  {
28594
- src: dialogue.portrait,
29287
+ src: portraitUrl,
28595
29288
  alt: dialogue.speaker,
28596
- className: "w-24 h-24 object-contain",
28597
- style: { imageRendering: "pixelated" }
29289
+ className: "h-full w-auto object-contain drop-shadow-2xl"
28598
29290
  }
28599
- ) }),
28600
- /* @__PURE__ */ jsxs("div", { className: "flex-1 p-4", children: [
28601
- /* @__PURE__ */ jsx("div", { className: "text-warning font-bold mb-2", children: dialogue.speaker }),
28602
- /* @__PURE__ */ jsxs("div", { className: "text-[var(--color-foreground)] text-lg leading-relaxed min-h-[60px]", children: [
28603
- displayedText,
28604
- isTyping && /* @__PURE__ */ jsx("span", { className: "animate-pulse", children: "\u258C" })
28605
- ] }),
28606
- !isTyping && enabledChoices.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-2", children: enabledChoices.map((choice, index) => /* @__PURE__ */ jsxs(
28607
- "button",
29291
+ )
29292
+ }
29293
+ ),
29294
+ /* @__PURE__ */ jsx(
29295
+ "div",
29296
+ {
29297
+ className: cn(
29298
+ "fixed left-0 right-0 z-40",
29299
+ position === "top" ? "top-0" : "bottom-0",
29300
+ className
29301
+ ),
29302
+ onClick: handleClick,
29303
+ onKeyDown: handleKeyDown,
29304
+ tabIndex: 0,
29305
+ role: "dialog",
29306
+ "aria-label": "Dialogue",
29307
+ children: /* @__PURE__ */ jsx("div", { className: cn(
29308
+ "mx-4 my-4 border-2 rounded-container overflow-hidden",
29309
+ backgroundImage ? "bg-black/80 backdrop-blur-sm text-white border-white/20" : "bg-[var(--color-card)] bg-opacity-95 border-border"
29310
+ ), children: /* @__PURE__ */ jsxs("div", { className: "flex", children: [
29311
+ dialogue.portrait && /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 p-4 border-r border-border", children: /* @__PURE__ */ jsx(
29312
+ "img",
28608
29313
  {
28609
- type: "button",
28610
- className: cn(
28611
- "block w-full text-left px-4 py-2 rounded-interactive transition-colors",
28612
- "hover:bg-[var(--color-surface,#374151)] focus:outline-none focus:ring-2 focus:ring-warning",
28613
- selectedChoice === index ? "bg-[var(--color-surface,#374151)] text-warning" : "bg-[var(--color-card)] text-[var(--color-foreground)]"
28614
- ),
28615
- onClick: (e) => {
28616
- e.stopPropagation();
28617
- if (choiceEvent) eventBus.emit(`UI:${choiceEvent}`, { choice });
28618
- onChoice?.(choice);
29314
+ src: dialogue.portrait,
29315
+ alt: dialogue.speaker,
29316
+ className: "w-24 h-24 object-contain",
29317
+ style: { imageRendering: "pixelated" }
29318
+ }
29319
+ ) }),
29320
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 p-4", children: [
29321
+ /* @__PURE__ */ jsx("div", { className: "text-warning font-bold mb-2", children: dialogue.speaker }),
29322
+ /* @__PURE__ */ jsxs("div", { className: cn(
29323
+ "text-lg leading-relaxed min-h-[60px]",
29324
+ backgroundImage ? "text-white" : "text-[var(--color-foreground)]"
29325
+ ), children: [
29326
+ displayedText,
29327
+ isTyping && /* @__PURE__ */ jsx("span", { className: "animate-pulse", children: "\u258C" })
29328
+ ] }),
29329
+ !isTyping && enabledChoices.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-2", children: enabledChoices.map((choice, index) => /* @__PURE__ */ jsxs(
29330
+ "button",
29331
+ {
29332
+ type: "button",
29333
+ className: cn(
29334
+ "block w-full text-left px-4 py-2 rounded-interactive transition-colors",
29335
+ "hover:bg-[var(--color-surface,#374151)] focus:outline-none focus:ring-2 focus:ring-warning",
29336
+ selectedChoice === index ? "bg-[var(--color-surface,#374151)] text-warning" : backgroundImage ? "bg-white/10 text-white hover:bg-white/20" : "bg-[var(--color-card)] text-[var(--color-foreground)]"
29337
+ ),
29338
+ onClick: (e) => {
29339
+ e.stopPropagation();
29340
+ if (choiceEvent) eventBus.emit(`UI:${choiceEvent}`, { choice });
29341
+ onChoice?.(choice);
29342
+ },
29343
+ children: [
29344
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground mr-2", children: [
29345
+ index + 1,
29346
+ "."
29347
+ ] }),
29348
+ choice.text
29349
+ ]
28619
29350
  },
28620
- children: [
28621
- /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground mr-2", children: [
28622
- index + 1,
28623
- "."
28624
- ] }),
28625
- choice.text
28626
- ]
28627
- },
28628
- index
28629
- )) }),
28630
- !isTyping && !dialogue.choices?.length && /* @__PURE__ */ jsx("div", { className: "mt-4 text-muted-foreground text-sm animate-pulse", children: "Press SPACE or click to continue..." })
28631
- ] })
28632
- ] }) })
28633
- }
28634
- );
29351
+ index
29352
+ )) }),
29353
+ !isTyping && !dialogue.choices?.length && /* @__PURE__ */ jsx("div", { className: "mt-4 text-muted-foreground text-sm animate-pulse", children: "Press SPACE or click to continue..." })
29354
+ ] })
29355
+ ] }) })
29356
+ }
29357
+ )
29358
+ ] });
28635
29359
  }
28636
29360
  var DEFAULT_DIALOGUE;
28637
29361
  var init_DialogueBox = __esm({
@@ -28832,7 +29556,7 @@ function GameMenu({
28832
29556
  } catch {
28833
29557
  }
28834
29558
  const eventBus = eventBusProp || eventBusFromHook;
28835
- const handleOptionClick = React76.useCallback(
29559
+ const handleOptionClick = React79.useCallback(
28836
29560
  (option) => {
28837
29561
  if (option.event && eventBus) {
28838
29562
  eventBus.emit(`UI:${option.event}`, { option });
@@ -28951,7 +29675,7 @@ function GameOverScreen({
28951
29675
  } catch {
28952
29676
  }
28953
29677
  const eventBus = eventBusProp || eventBusFromHook;
28954
- const handleActionClick = React76.useCallback(
29678
+ const handleActionClick = React79.useCallback(
28955
29679
  (action) => {
28956
29680
  if (action.event && eventBus) {
28957
29681
  eventBus.emit(`UI:${action.event}`, { action });
@@ -29158,10 +29882,10 @@ function PlatformerCanvas({
29158
29882
  canvasHeight = 400,
29159
29883
  followCamera = true,
29160
29884
  bgColor = "#5c94fc",
29161
- playerSprite = "https://almadar-kflow-assets.web.app/shared/platformer/characters/platformChar_idle.png",
29885
+ playerSprite,
29162
29886
  tileSprites,
29163
29887
  backgroundImage = "",
29164
- assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/platformer/",
29888
+ assetBaseUrl,
29165
29889
  leftEvent = "MOVE_LEFT",
29166
29890
  rightEvent = "MOVE_RIGHT",
29167
29891
  jumpEvent = "JUMP",
@@ -29175,6 +29899,7 @@ function PlatformerCanvas({
29175
29899
  const imageCache = useRef(/* @__PURE__ */ new Map());
29176
29900
  const [loadedImages, setLoadedImages] = useState(/* @__PURE__ */ new Set());
29177
29901
  const loadImage = useCallback((url) => {
29902
+ if (!url.startsWith("http") && !assetBaseUrl) return null;
29178
29903
  const fullUrl = url.startsWith("http") ? url : `${assetBaseUrl}${url}`;
29179
29904
  const cached = imageCache.current.get(fullUrl);
29180
29905
  if (cached?.complete && cached.naturalWidth > 0) {
@@ -29513,6 +30238,7 @@ var init_molecules = __esm({
29513
30238
  init_ActionButtons();
29514
30239
  init_StatBadge();
29515
30240
  init_InventoryGrid();
30241
+ init_CardHand();
29516
30242
  init_QuestTracker();
29517
30243
  init_CraftingRecipe();
29518
30244
  init_PowerupSlots();
@@ -29895,13 +30621,13 @@ var init_MapView = __esm({
29895
30621
  shadowSize: [41, 41]
29896
30622
  });
29897
30623
  L.Marker.prototype.options.icon = defaultIcon;
29898
- const { useEffect: useEffect81, useRef: useRef78, useCallback: useCallback120, useState: useState110 } = React76__default;
30624
+ const { useEffect: useEffect82, useRef: useRef81, useCallback: useCallback125, useState: useState111 } = React79__default;
29899
30625
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
29900
30626
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
29901
30627
  function MapUpdater({ centerLat, centerLng, zoom }) {
29902
30628
  const map = useMap();
29903
- const prevRef = useRef78({ centerLat, centerLng, zoom });
29904
- useEffect81(() => {
30629
+ const prevRef = useRef81({ centerLat, centerLng, zoom });
30630
+ useEffect82(() => {
29905
30631
  const prev = prevRef.current;
29906
30632
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
29907
30633
  map.setView([centerLat, centerLng], zoom);
@@ -29912,7 +30638,7 @@ var init_MapView = __esm({
29912
30638
  }
29913
30639
  function MapClickHandler({ onMapClick }) {
29914
30640
  const map = useMap();
29915
- useEffect81(() => {
30641
+ useEffect82(() => {
29916
30642
  if (!onMapClick) return;
29917
30643
  const handler = (e) => {
29918
30644
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -29940,8 +30666,8 @@ var init_MapView = __esm({
29940
30666
  showAttribution = true
29941
30667
  }) {
29942
30668
  const eventBus = useEventBus3();
29943
- const [clickedPosition, setClickedPosition] = useState110(null);
29944
- const handleMapClick = useCallback120((lat, lng) => {
30669
+ const [clickedPosition, setClickedPosition] = useState111(null);
30670
+ const handleMapClick = useCallback125((lat, lng) => {
29945
30671
  if (showClickedPin) {
29946
30672
  setClickedPosition({ lat, lng });
29947
30673
  }
@@ -29950,7 +30676,7 @@ var init_MapView = __esm({
29950
30676
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
29951
30677
  }
29952
30678
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
29953
- const handleMarkerClick = useCallback120((marker) => {
30679
+ const handleMarkerClick = useCallback125((marker) => {
29954
30680
  onMarkerClick?.(marker);
29955
30681
  if (markerClickEvent) {
29956
30682
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -30801,8 +31527,8 @@ function TableView({
30801
31527
  }) {
30802
31528
  const eventBus = useEventBus();
30803
31529
  const { t } = useTranslate();
30804
- const [visibleCount, setVisibleCount] = React76__default.useState(pageSize > 0 ? pageSize : Infinity);
30805
- const [localSelected, setLocalSelected] = React76__default.useState(/* @__PURE__ */ new Set());
31530
+ const [visibleCount, setVisibleCount] = React79__default.useState(pageSize > 0 ? pageSize : Infinity);
31531
+ const [localSelected, setLocalSelected] = React79__default.useState(/* @__PURE__ */ new Set());
30806
31532
  const colDefs = columns ?? fields ?? [];
30807
31533
  const allDataRaw = Array.isArray(entity) ? entity : entity ? [entity] : [];
30808
31534
  const dnd = useDataDnd({
@@ -30997,12 +31723,12 @@ function TableView({
30997
31723
  ]
30998
31724
  }
30999
31725
  );
31000
- return dnd.isZone ? /* @__PURE__ */ jsx(dnd.SortableItem, { id: row[idField] ?? id, children: rowInner }, id) : /* @__PURE__ */ jsx(React76__default.Fragment, { children: rowInner }, id);
31726
+ return dnd.isZone ? /* @__PURE__ */ jsx(dnd.SortableItem, { id: row[idField] ?? id, children: rowInner }, id) : /* @__PURE__ */ jsx(React79__default.Fragment, { children: rowInner }, id);
31001
31727
  };
31002
31728
  const items = data.map((row) => row);
31003
31729
  const groups = groupBy ? groupData2(items, groupBy) : [{ label: "", items }];
31004
31730
  let runningIndex = 0;
31005
- const body = /* @__PURE__ */ jsx(Box, { role: "rowgroup", children: groups.map((group, gi) => /* @__PURE__ */ jsxs(React76__default.Fragment, { children: [
31731
+ const body = /* @__PURE__ */ jsx(Box, { role: "rowgroup", children: groups.map((group, gi) => /* @__PURE__ */ jsxs(React79__default.Fragment, { children: [
31006
31732
  group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: gi > 0 ? "mt-3" : "mt-0" }),
31007
31733
  group.items.map((row) => renderRow(row, runningIndex++))
31008
31734
  ] }, gi)) });
@@ -32359,7 +33085,7 @@ var init_StepFlow = __esm({
32359
33085
  className
32360
33086
  }) => {
32361
33087
  if (orientation === "vertical") {
32362
- return /* @__PURE__ */ jsx(VStack, { gap: "none", className: cn("w-full", className), children: steps.map((step, index) => /* @__PURE__ */ jsx(React76__default.Fragment, { children: /* @__PURE__ */ jsxs(HStack, { gap: "md", align: "start", className: "w-full", children: [
33088
+ return /* @__PURE__ */ jsx(VStack, { gap: "none", className: cn("w-full", className), children: steps.map((step, index) => /* @__PURE__ */ jsx(React79__default.Fragment, { children: /* @__PURE__ */ jsxs(HStack, { gap: "md", align: "start", className: "w-full", children: [
32363
33089
  /* @__PURE__ */ jsxs(VStack, { gap: "none", align: "center", children: [
32364
33090
  /* @__PURE__ */ jsx(StepCircle, { step, index }),
32365
33091
  showConnectors && index < steps.length - 1 && /* @__PURE__ */ jsx(Box, { className: "w-px h-8 bg-border" })
@@ -32370,7 +33096,7 @@ var init_StepFlow = __esm({
32370
33096
  ] })
32371
33097
  ] }) }, index)) });
32372
33098
  }
32373
- return /* @__PURE__ */ jsx(Box, { className: cn("w-full flex flex-col md:flex-row items-start gap-0", className), children: steps.map((step, index) => /* @__PURE__ */ jsxs(React76__default.Fragment, { children: [
33099
+ return /* @__PURE__ */ jsx(Box, { className: cn("w-full flex flex-col md:flex-row items-start gap-0", className), children: steps.map((step, index) => /* @__PURE__ */ jsxs(React79__default.Fragment, { children: [
32374
33100
  /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", className: "flex-1 w-full md:w-auto", children: [
32375
33101
  /* @__PURE__ */ jsx(StepCircle, { step, index }),
32376
33102
  /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-center", children: step.title }),
@@ -33355,7 +34081,7 @@ var init_LikertScale = __esm({
33355
34081
  md: "text-base",
33356
34082
  lg: "text-lg"
33357
34083
  };
33358
- LikertScale = React76__default.forwardRef(
34084
+ LikertScale = React79__default.forwardRef(
33359
34085
  ({
33360
34086
  question,
33361
34087
  options = DEFAULT_LIKERT_OPTIONS,
@@ -33367,7 +34093,7 @@ var init_LikertScale = __esm({
33367
34093
  variant = "radios",
33368
34094
  className
33369
34095
  }, ref) => {
33370
- const groupId = React76__default.useId();
34096
+ const groupId = React79__default.useId();
33371
34097
  const eventBus = useEventBus();
33372
34098
  const handleSelect = useCallback(
33373
34099
  (next) => {
@@ -35649,7 +36375,7 @@ var init_DocBreadcrumb = __esm({
35649
36375
  "aria-label": t("aria.breadcrumb"),
35650
36376
  children: /* @__PURE__ */ jsx(HStack, { gap: "xs", align: "center", wrap: true, children: items.map((item, idx) => {
35651
36377
  const isLast = idx === items.length - 1;
35652
- return /* @__PURE__ */ jsxs(React76__default.Fragment, { children: [
36378
+ return /* @__PURE__ */ jsxs(React79__default.Fragment, { children: [
35653
36379
  idx > 0 && /* @__PURE__ */ jsx(
35654
36380
  Icon,
35655
36381
  {
@@ -36518,7 +37244,7 @@ var init_MiniStateMachine = __esm({
36518
37244
  const x = 2 + i * (NODE_W + GAP2 + ARROW_W + GAP2);
36519
37245
  const tc = transitionCounts[s.name] ?? 0;
36520
37246
  const role = getStateRole(s.name, s.isInitial, s.isTerminal, tc, maxTC);
36521
- return /* @__PURE__ */ jsxs(React76__default.Fragment, { children: [
37247
+ return /* @__PURE__ */ jsxs(React79__default.Fragment, { children: [
36522
37248
  /* @__PURE__ */ jsx(
36523
37249
  AvlState,
36524
37250
  {
@@ -36722,7 +37448,7 @@ var init_PageHeader = __esm({
36722
37448
  info: "bg-info/10 text-info"
36723
37449
  };
36724
37450
  return /* @__PURE__ */ jsxs(Box, { className: cn("mb-6", className), children: [
36725
- breadcrumbs && breadcrumbs.length > 0 && /* @__PURE__ */ jsx(Box, { as: "nav", className: "mb-4", children: /* @__PURE__ */ jsx(Box, { as: "ol", className: "flex items-center gap-2 text-sm", children: breadcrumbs.map((crumb, idx) => /* @__PURE__ */ jsxs(React76__default.Fragment, { children: [
37451
+ breadcrumbs && breadcrumbs.length > 0 && /* @__PURE__ */ jsx(Box, { as: "nav", className: "mb-4", children: /* @__PURE__ */ jsx(Box, { as: "ol", className: "flex items-center gap-2 text-sm", children: breadcrumbs.map((crumb, idx) => /* @__PURE__ */ jsxs(React79__default.Fragment, { children: [
36726
37452
  idx > 0 && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: "/" }),
36727
37453
  crumb.href ? /* @__PURE__ */ jsx(
36728
37454
  "a",
@@ -37404,8 +38130,8 @@ var init_WizardContainer = __esm({
37404
38130
  return void 0;
37405
38131
  if (typeof controlledStep === "number") return controlledStep;
37406
38132
  if (typeof controlledStep === "string") return parseInt(controlledStep, 10);
37407
- const num2 = Number(controlledStep);
37408
- return isNaN(num2) ? void 0 : num2;
38133
+ const num3 = Number(controlledStep);
38134
+ return isNaN(num3) ? void 0 : num3;
37409
38135
  })();
37410
38136
  const currentStep = normalizedControlledStep !== void 0 ? normalizedControlledStep : internalStep;
37411
38137
  const totalSteps = steps.length;
@@ -37451,7 +38177,7 @@ var init_WizardContainer = __esm({
37451
38177
  const isCompleted = index < currentStep;
37452
38178
  const stepKey = step.id ?? step.tabId ?? `step-${index}`;
37453
38179
  const stepTitle = step.title ?? step.name ?? `Step ${index + 1}`;
37454
- return /* @__PURE__ */ jsxs(React76__default.Fragment, { children: [
38180
+ return /* @__PURE__ */ jsxs(React79__default.Fragment, { children: [
37455
38181
  /* @__PURE__ */ jsx(
37456
38182
  Button,
37457
38183
  {
@@ -38378,10 +39104,13 @@ var init_GraphCanvas = __esm({
38378
39104
  draggable = true,
38379
39105
  actions,
38380
39106
  onNodeClick,
39107
+ onNodeDoubleClick,
38381
39108
  nodeClickEvent,
38382
39109
  selectedNodeId,
38383
39110
  repulsion = 800,
38384
39111
  linkDistance = 100,
39112
+ nodeSpacing = 28,
39113
+ linkOpacity = 0.18,
38385
39114
  layout = "force",
38386
39115
  entity,
38387
39116
  isLoading = false,
@@ -38397,6 +39126,19 @@ var init_GraphCanvas = __esm({
38397
39126
  const [hoveredNode, setHoveredNode] = useState(null);
38398
39127
  const nodesRef = useRef([]);
38399
39128
  const [, forceUpdate] = useState(0);
39129
+ const [logicalW, setLogicalW] = useState(800);
39130
+ useEffect(() => {
39131
+ const canvas = canvasRef.current;
39132
+ if (!canvas || typeof ResizeObserver === "undefined") return;
39133
+ const measure = () => {
39134
+ const width = Math.round(canvas.clientWidth);
39135
+ if (width > 0) setLogicalW(width);
39136
+ };
39137
+ measure();
39138
+ const ro = new ResizeObserver(measure);
39139
+ ro.observe(canvas);
39140
+ return () => ro.disconnect();
39141
+ }, []);
38400
39142
  const interactionRef = useRef({
38401
39143
  mode: "none",
38402
39144
  dragNodeId: null,
@@ -38409,10 +39151,8 @@ var init_GraphCanvas = __esm({
38409
39151
  const canvas = canvasRef.current;
38410
39152
  if (!canvas) return null;
38411
39153
  const rect = canvas.getBoundingClientRect();
38412
- const scaleX = rect.width > 0 ? canvas.width / rect.width : 1;
38413
- const scaleY = rect.height > 0 ? canvas.height / rect.height : 1;
38414
- const screenX = (e.clientX - rect.left) * scaleX;
38415
- const screenY = (e.clientY - rect.top) * scaleY;
39154
+ const screenX = e.clientX - rect.left;
39155
+ const screenY = e.clientY - rect.top;
38416
39156
  return {
38417
39157
  screenX,
38418
39158
  screenY,
@@ -38452,8 +39192,8 @@ var init_GraphCanvas = __esm({
38452
39192
  useEffect(() => {
38453
39193
  const canvas = canvasRef.current;
38454
39194
  if (!canvas || propNodes.length === 0) return;
38455
- const w = canvas.width;
38456
- const h = canvas.height;
39195
+ const w = logicalW;
39196
+ const h = height;
38457
39197
  const simNodes = propNodes.map((n, idx) => {
38458
39198
  let x = n.x ?? 0;
38459
39199
  let y = n.y ?? 0;
@@ -38530,6 +39270,25 @@ var init_GraphCanvas = __esm({
38530
39270
  node.x = Math.max(30, Math.min(w - 30, node.x));
38531
39271
  node.y = Math.max(30, Math.min(h - 30, node.y));
38532
39272
  }
39273
+ for (let i = 0; i < nodes.length; i++) {
39274
+ for (let j = i + 1; j < nodes.length; j++) {
39275
+ const a = nodes[i];
39276
+ const b = nodes[j];
39277
+ const dx = b.x - a.x;
39278
+ const dy = b.y - a.y;
39279
+ const dist = Math.sqrt(dx * dx + dy * dy) || 1;
39280
+ const minDist = (a.size || 8) + (b.size || 8) + nodeSpacing;
39281
+ if (dist < minDist) {
39282
+ const push = (minDist - dist) / 2;
39283
+ const ux = dx / dist;
39284
+ const uy = dy / dist;
39285
+ a.x = Math.max(30, Math.min(w - 30, a.x - ux * push));
39286
+ a.y = Math.max(30, Math.min(h - 30, a.y - uy * push));
39287
+ b.x = Math.max(30, Math.min(w - 30, b.x + ux * push));
39288
+ b.y = Math.max(30, Math.min(h - 30, b.y + uy * push));
39289
+ }
39290
+ }
39291
+ }
38533
39292
  iterations++;
38534
39293
  forceUpdate((n) => n + 1);
38535
39294
  if (iterations < maxIterations) {
@@ -38543,29 +39302,41 @@ var init_GraphCanvas = __esm({
38543
39302
  return () => {
38544
39303
  cancelAnimationFrame(animRef.current);
38545
39304
  };
38546
- }, [propNodes, propEdges, layout, repulsion, linkDistance]);
39305
+ }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, logicalW, height]);
38547
39306
  useEffect(() => {
38548
39307
  const canvas = canvasRef.current;
38549
39308
  if (!canvas) return;
38550
39309
  const ctx = canvas.getContext("2d");
38551
39310
  if (!ctx) return;
38552
- const w = canvas.width;
38553
- const h = canvas.height;
39311
+ const w = logicalW;
39312
+ const h = height;
38554
39313
  const nodes = nodesRef.current;
38555
39314
  const accentColor = resolveColor2("var(--color-accent)", canvas);
39315
+ const dpr = typeof window !== "undefined" && window.devicePixelRatio || 1;
39316
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
38556
39317
  ctx.clearRect(0, 0, w, h);
38557
39318
  ctx.save();
38558
39319
  ctx.translate(offset.x, offset.y);
38559
39320
  ctx.scale(zoom, zoom);
39321
+ const neighbors = /* @__PURE__ */ new Set();
39322
+ if (hoveredNode) {
39323
+ neighbors.add(hoveredNode);
39324
+ for (const edge of propEdges) {
39325
+ if (edge.source === hoveredNode) neighbors.add(String(edge.target));
39326
+ else if (edge.target === hoveredNode) neighbors.add(String(edge.source));
39327
+ }
39328
+ }
38560
39329
  for (const edge of propEdges) {
38561
39330
  const source = nodes.find((n) => n.id === edge.source);
38562
39331
  const target = nodes.find((n) => n.id === edge.target);
38563
39332
  if (!source || !target) continue;
39333
+ const incident = !!hoveredNode && (edge.source === hoveredNode || edge.target === hoveredNode);
39334
+ ctx.globalAlpha = hoveredNode ? incident ? 1 : 0.05 : linkOpacity;
38564
39335
  ctx.beginPath();
38565
39336
  ctx.moveTo(source.x, source.y);
38566
39337
  ctx.lineTo(target.x, target.y);
38567
- ctx.strokeStyle = edge.color || "#88888866";
38568
- ctx.lineWidth = edge.weight ? Math.max(1, edge.weight) : 1;
39338
+ ctx.strokeStyle = incident ? accentColor : edge.color || "#888888";
39339
+ ctx.lineWidth = incident ? 2 : edge.weight ? Math.max(1, edge.weight) : 1;
38569
39340
  ctx.stroke();
38570
39341
  if (edge.label && showLabels) {
38571
39342
  const mx = (source.x + target.x) / 2;
@@ -38576,11 +39347,13 @@ var init_GraphCanvas = __esm({
38576
39347
  ctx.fillText(edge.label, mx, my - 4);
38577
39348
  }
38578
39349
  }
39350
+ ctx.globalAlpha = 1;
38579
39351
  for (const node of nodes) {
38580
39352
  const size = node.size || 8;
38581
39353
  const color = resolveColor2(node.color || getGroupColor(node.group, groups), canvas);
38582
39354
  const isHovered = hoveredNode === node.id;
38583
39355
  const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
39356
+ ctx.globalAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : 1;
38584
39357
  const radius = isSelected ? size + 4 : isHovered ? size + 2 : size;
38585
39358
  ctx.beginPath();
38586
39359
  ctx.arc(node.x, node.y, radius, 0, Math.PI * 2);
@@ -38700,6 +39473,15 @@ var init_GraphCanvas = __esm({
38700
39473
  interactionRef.current.dragNodeId = null;
38701
39474
  setHoveredNode(null);
38702
39475
  }, []);
39476
+ const handleDoubleClick = useCallback(
39477
+ (e) => {
39478
+ const coords = toCoords(e);
39479
+ if (!coords) return;
39480
+ const node = nodeAt(coords.graphX, coords.graphY);
39481
+ if (node) onNodeDoubleClick?.(node);
39482
+ },
39483
+ [toCoords, nodeAt, onNodeDoubleClick]
39484
+ );
38703
39485
  if (isLoading) {
38704
39486
  return /* @__PURE__ */ jsx(LoadingState, { message: t("common.loading"), className });
38705
39487
  }
@@ -38757,15 +39539,16 @@ var init_GraphCanvas = __esm({
38757
39539
  "canvas",
38758
39540
  {
38759
39541
  ref: canvasRef,
38760
- width: 800,
38761
- height,
39542
+ width: Math.round(logicalW * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
39543
+ height: Math.round(height * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
38762
39544
  className: "w-full cursor-grab active:cursor-grabbing",
38763
39545
  style: { height },
38764
39546
  onWheel: handleWheel,
38765
39547
  onMouseDown: handleMouseDown,
38766
39548
  onMouseMove: handleMouseMove,
38767
39549
  onMouseUp: handleMouseUp,
38768
- onMouseLeave: handleMouseLeave
39550
+ onMouseLeave: handleMouseLeave,
39551
+ onDoubleClick: handleDoubleClick
38769
39552
  }
38770
39553
  ) }),
38771
39554
  groups.length > 1 && /* @__PURE__ */ jsx(HStack, { gap: "md", className: "px-4 py-2 border-t border-border", wrap: true, children: groups.map((group, idx) => /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
@@ -40166,7 +40949,7 @@ var init_DialogueBubble = __esm({
40166
40949
  }
40167
40950
  });
40168
40951
  function extractTitle(children) {
40169
- if (!React76__default.isValidElement(children)) return void 0;
40952
+ if (!React79__default.isValidElement(children)) return void 0;
40170
40953
  const props = children.props;
40171
40954
  if (typeof props.title === "string") {
40172
40955
  return props.title;
@@ -40211,7 +40994,7 @@ var init_DrawerSlot = __esm({
40211
40994
  }
40212
40995
  });
40213
40996
  function StateIndicator({
40214
- assetUrl = DEFAULT_ASSET_URL11,
40997
+ assetUrl = DEFAULT_ASSET_URL9,
40215
40998
  state = "idle",
40216
40999
  label,
40217
41000
  size = "md",
@@ -40250,13 +41033,13 @@ function StateIndicator({
40250
41033
  }
40251
41034
  );
40252
41035
  }
40253
- var DEFAULT_ASSET_URL11, DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
41036
+ var DEFAULT_ASSET_URL9, DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
40254
41037
  var init_StateIndicator = __esm({
40255
41038
  "components/game/atoms/StateIndicator.tsx"() {
40256
41039
  init_Box();
40257
41040
  init_Icon();
40258
41041
  init_cn();
40259
- DEFAULT_ASSET_URL11 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
41042
+ DEFAULT_ASSET_URL9 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
40260
41043
  DEFAULT_STATE_STYLES = {
40261
41044
  idle: { icon: "\u23F8", bgClass: "bg-muted" },
40262
41045
  active: { icon: "\u25B6", bgClass: "bg-success" },
@@ -40291,7 +41074,7 @@ function LinearView({
40291
41074
  /* @__PURE__ */ jsx(HStack, { className: "flex-wrap items-center", gap: "xs", children: trait.states.map((state, i) => {
40292
41075
  const isDone = i < currentIdx;
40293
41076
  const isCurrent = i === currentIdx;
40294
- return /* @__PURE__ */ jsxs(React76__default.Fragment, { children: [
41077
+ return /* @__PURE__ */ jsxs(React79__default.Fragment, { children: [
40295
41078
  i > 0 && /* @__PURE__ */ jsx(
40296
41079
  Typography,
40297
41080
  {
@@ -41241,12 +42024,12 @@ var init_Form = __esm({
41241
42024
  const isSchemaEntity = isOrbitalEntitySchema(entity);
41242
42025
  const resolvedEntity = isSchemaEntity ? entity : void 0;
41243
42026
  const entityName = typeof entity === "string" ? entity : resolvedEntity?.name;
41244
- const normalizedInitialData = React76__default.useMemo(() => {
42027
+ const normalizedInitialData = React79__default.useMemo(() => {
41245
42028
  const entityRowAsInitial = isPlainEntityRow(entity) ? entity : void 0;
41246
42029
  const callerInitial = initialData !== null && typeof initialData === "object" && !Array.isArray(initialData) ? initialData : {};
41247
42030
  return entityRowAsInitial !== void 0 ? { ...entityRowAsInitial, ...callerInitial } : callerInitial;
41248
42031
  }, [entity, initialData]);
41249
- const entityDerivedFields = React76__default.useMemo(() => {
42032
+ const entityDerivedFields = React79__default.useMemo(() => {
41250
42033
  if (fields && fields.length > 0) return void 0;
41251
42034
  if (!resolvedEntity) return void 0;
41252
42035
  return resolvedEntity.fields.map(
@@ -41266,16 +42049,16 @@ var init_Form = __esm({
41266
42049
  const conditionalFields = typeof conditionalFieldsRaw === "boolean" ? {} : conditionalFieldsRaw;
41267
42050
  const hiddenCalculations = typeof hiddenCalculationsRaw === "boolean" ? [] : hiddenCalculationsRaw;
41268
42051
  const violationTriggers = typeof violationTriggersRaw === "boolean" ? [] : violationTriggersRaw;
41269
- const [formData, setFormData] = React76__default.useState(
42052
+ const [formData, setFormData] = React79__default.useState(
41270
42053
  normalizedInitialData
41271
42054
  );
41272
- const [collapsedSections, setCollapsedSections] = React76__default.useState(
42055
+ const [collapsedSections, setCollapsedSections] = React79__default.useState(
41273
42056
  /* @__PURE__ */ new Set()
41274
42057
  );
41275
- const [submitError, setSubmitError] = React76__default.useState(null);
41276
- const formRef = React76__default.useRef(null);
42058
+ const [submitError, setSubmitError] = React79__default.useState(null);
42059
+ const formRef = React79__default.useRef(null);
41277
42060
  const formMode = props.mode;
41278
- const mountedRef = React76__default.useRef(false);
42061
+ const mountedRef = React79__default.useRef(false);
41279
42062
  if (!mountedRef.current) {
41280
42063
  mountedRef.current = true;
41281
42064
  debug("forms", "mount", {
@@ -41288,7 +42071,7 @@ var init_Form = __esm({
41288
42071
  });
41289
42072
  }
41290
42073
  const shouldShowCancel = showCancel ?? (fields && fields.length > 0);
41291
- const evalContext = React76__default.useMemo(
42074
+ const evalContext = React79__default.useMemo(
41292
42075
  () => ({
41293
42076
  formValues: formData,
41294
42077
  globalVariables: externalContext?.globalVariables ?? {},
@@ -41297,7 +42080,7 @@ var init_Form = __esm({
41297
42080
  }),
41298
42081
  [formData, externalContext]
41299
42082
  );
41300
- React76__default.useEffect(() => {
42083
+ React79__default.useEffect(() => {
41301
42084
  debug("forms", "initialData-sync", {
41302
42085
  mode: formMode,
41303
42086
  normalizedInitialData,
@@ -41308,7 +42091,7 @@ var init_Form = __esm({
41308
42091
  setFormData(normalizedInitialData);
41309
42092
  }
41310
42093
  }, [normalizedInitialData]);
41311
- const processCalculations = React76__default.useCallback(
42094
+ const processCalculations = React79__default.useCallback(
41312
42095
  (changedFieldId, newFormData) => {
41313
42096
  if (!hiddenCalculations.length) return;
41314
42097
  const context = {
@@ -41333,7 +42116,7 @@ var init_Form = __esm({
41333
42116
  },
41334
42117
  [hiddenCalculations, externalContext, eventBus]
41335
42118
  );
41336
- const checkViolations = React76__default.useCallback(
42119
+ const checkViolations = React79__default.useCallback(
41337
42120
  (changedFieldId, newFormData) => {
41338
42121
  if (!violationTriggers.length) return;
41339
42122
  const context = {
@@ -41371,7 +42154,7 @@ var init_Form = __esm({
41371
42154
  processCalculations(name, newFormData);
41372
42155
  checkViolations(name, newFormData);
41373
42156
  };
41374
- const isFieldVisible = React76__default.useCallback(
42157
+ const isFieldVisible = React79__default.useCallback(
41375
42158
  (fieldName) => {
41376
42159
  const condition = conditionalFields[fieldName];
41377
42160
  if (!condition) return true;
@@ -41379,7 +42162,7 @@ var init_Form = __esm({
41379
42162
  },
41380
42163
  [conditionalFields, evalContext]
41381
42164
  );
41382
- const isSectionVisible = React76__default.useCallback(
42165
+ const isSectionVisible = React79__default.useCallback(
41383
42166
  (section) => {
41384
42167
  if (!section.condition) return true;
41385
42168
  return Boolean(evaluateFormExpression(section.condition, evalContext));
@@ -41455,7 +42238,7 @@ var init_Form = __esm({
41455
42238
  eventBus.emit(`UI:${onCancel}`);
41456
42239
  }
41457
42240
  };
41458
- const renderField = React76__default.useCallback(
42241
+ const renderField = React79__default.useCallback(
41459
42242
  (field) => {
41460
42243
  const fieldName = field.name || field.field;
41461
42244
  if (!fieldName) return null;
@@ -41476,7 +42259,7 @@ var init_Form = __esm({
41476
42259
  [formData, isFieldVisible, relationsData, relationsLoading, isLoading]
41477
42260
  );
41478
42261
  const effectiveFields = entityDerivedFields ?? fields;
41479
- const normalizedFields = React76__default.useMemo(() => {
42262
+ const normalizedFields = React79__default.useMemo(() => {
41480
42263
  if (!effectiveFields || effectiveFields.length === 0) return [];
41481
42264
  return effectiveFields.map((field) => {
41482
42265
  if (typeof field === "string") {
@@ -41499,7 +42282,7 @@ var init_Form = __esm({
41499
42282
  return field;
41500
42283
  });
41501
42284
  }, [effectiveFields, resolvedEntity]);
41502
- const schemaFields = React76__default.useMemo(() => {
42285
+ const schemaFields = React79__default.useMemo(() => {
41503
42286
  if (normalizedFields.length === 0) return null;
41504
42287
  if (isDebugEnabled()) {
41505
42288
  debugGroup(`Form: ${entityName || "unknown"}`);
@@ -41509,7 +42292,7 @@ var init_Form = __esm({
41509
42292
  }
41510
42293
  return normalizedFields.map(renderField).filter(Boolean);
41511
42294
  }, [normalizedFields, renderField, entityName, conditionalFields]);
41512
- const sectionElements = React76__default.useMemo(() => {
42295
+ const sectionElements = React79__default.useMemo(() => {
41513
42296
  if (!sections || sections.length === 0) return null;
41514
42297
  return sections.map((section) => {
41515
42298
  if (!isSectionVisible(section)) {
@@ -42836,12 +43619,18 @@ function ModelLoader({
42836
43619
  });
42837
43620
  return cloned;
42838
43621
  }, [loadedModel, castShadow, receiveShadow]);
43622
+ const normFactor = useMemo(() => {
43623
+ if (!model) return 1;
43624
+ const box = new THREE3.Box3().setFromObject(model);
43625
+ const size = new THREE3.Vector3();
43626
+ box.getSize(size);
43627
+ const maxDim = Math.max(size.x, size.y, size.z);
43628
+ return maxDim > 0 && Number.isFinite(maxDim) ? 1 / maxDim : 1;
43629
+ }, [model]);
42839
43630
  const scaleArray = useMemo(() => {
42840
- if (typeof scale === "number") {
42841
- return [scale, scale, scale];
42842
- }
42843
- return scale;
42844
- }, [scale]);
43631
+ const base = typeof scale === "number" ? [scale, scale, scale] : scale;
43632
+ return [base[0] * normFactor, base[1] * normFactor, base[2] * normFactor];
43633
+ }, [scale, normFactor]);
42845
43634
  const rotationRad = useMemo(() => {
42846
43635
  return [
42847
43636
  rotation[0] * Math.PI / 180,
@@ -43052,6 +43841,29 @@ var init_GameCanvas3D2 = __esm({
43052
43841
  const controlsRef = useRef(null);
43053
43842
  const [hoveredTile, setHoveredTile] = useState(null);
43054
43843
  const [internalError, setInternalError] = useState(null);
43844
+ useEffect(() => {
43845
+ const el = containerRef.current;
43846
+ if (!el) return;
43847
+ let node = el;
43848
+ let depth = 0;
43849
+ while (node && depth < 8) {
43850
+ const cs = window.getComputedStyle(node);
43851
+ const rect = node.getBoundingClientRect();
43852
+ console.log("[almadar:ui:game:3d-height]", {
43853
+ depth,
43854
+ tag: node.tagName,
43855
+ id: node.id || void 0,
43856
+ className: node.className?.slice?.(0, 60) || void 0,
43857
+ rectHeight: rect.height,
43858
+ computedHeight: cs.height,
43859
+ computedMinHeight: cs.minHeight,
43860
+ computedFlex: cs.flex,
43861
+ computedOverflow: cs.overflow
43862
+ });
43863
+ node = node.parentElement;
43864
+ depth++;
43865
+ }
43866
+ }, []);
43055
43867
  const { sheetUrls: atlasSheetUrls, resolveUnitFrame } = useUnitSpriteAtlas(units);
43056
43868
  const preloadUrls = useMemo(() => [...preloadAssets, ...atlasSheetUrls], [preloadAssets, atlasSheetUrls]);
43057
43869
  const { isLoading: assetsLoading, progress, loaded, total } = useAssetLoader({
@@ -43180,24 +43992,28 @@ var init_GameCanvas3D2 = __esm({
43180
43992
  const cameraConfig = useMemo(() => {
43181
43993
  const size = Math.max(
43182
43994
  gridBounds.maxX - gridBounds.minX,
43183
- gridBounds.maxZ - gridBounds.minZ
43995
+ gridBounds.maxZ - gridBounds.minZ,
43996
+ 4
43997
+ // minimum framing distance so a tiny board isn't zoomed in
43184
43998
  );
43185
- const distance = size * 1.5;
43999
+ const cx = (gridBounds.minX + gridBounds.maxX) / 2;
44000
+ const cz = (gridBounds.minZ + gridBounds.maxZ) / 2;
44001
+ const d = size * 1.5;
43186
44002
  switch (cameraMode) {
43187
44003
  case "isometric":
43188
44004
  return {
43189
- position: [distance, distance * 0.8, distance],
44005
+ position: [cx + d, d * 0.8, cz + d],
43190
44006
  fov: 45
43191
44007
  };
43192
44008
  case "top-down":
43193
44009
  return {
43194
- position: [0, distance * 2, 0],
44010
+ position: [cx, d * 2, cz],
43195
44011
  fov: 45
43196
44012
  };
43197
44013
  case "perspective":
43198
44014
  default:
43199
44015
  return {
43200
- position: [distance, distance, distance],
44016
+ position: [cx + d, d, cz + d],
43201
44017
  fov: 45
43202
44018
  };
43203
44019
  }
@@ -43436,7 +44252,8 @@ var init_GameCanvas3D2 = __esm({
43436
44252
  "div",
43437
44253
  {
43438
44254
  ref: containerRef,
43439
- className: cn("game-canvas-3d relative w-full h-full min-h-[85vh] overflow-hidden", className),
44255
+ className: cn("game-canvas-3d relative w-full overflow-hidden", className),
44256
+ style: { height: "85vh" },
43440
44257
  "data-orientation": orientation,
43441
44258
  "data-camera-mode": cameraMode,
43442
44259
  "data-overlay": overlay,
@@ -43451,7 +44268,7 @@ var init_GameCanvas3D2 = __esm({
43451
44268
  near: 0.1,
43452
44269
  far: 1e3
43453
44270
  },
43454
- style: { background: backgroundColor },
44271
+ style: { background: backgroundColor, position: "absolute", inset: 0 },
43455
44272
  onClick: (e) => {
43456
44273
  if (e.target === e.currentTarget) {
43457
44274
  eventHandlers.handleCanvasClick(e);
@@ -44422,7 +45239,7 @@ var init_List = __esm({
44422
45239
  if (entity && typeof entity === "object" && "id" in entity) return [entity];
44423
45240
  return [];
44424
45241
  }, [entity]);
44425
- const getItemActions = React76__default.useCallback(
45242
+ const getItemActions = React79__default.useCallback(
44426
45243
  (item) => {
44427
45244
  if (!itemActions) return [];
44428
45245
  if (typeof itemActions === "function") {
@@ -44898,7 +45715,7 @@ var init_MediaGallery = __esm({
44898
45715
  [selectable, selectedItems, selectionEvent, eventBus]
44899
45716
  );
44900
45717
  const entityData = Array.isArray(entity) ? entity : [];
44901
- const items = React76__default.useMemo(() => {
45718
+ const items = React79__default.useMemo(() => {
44902
45719
  if (propItems) return propItems;
44903
45720
  if (entityData.length === 0) return [];
44904
45721
  return entityData.map((record, idx) => ({
@@ -45068,9 +45885,9 @@ function MiniMap({
45068
45885
  viewportRect = DEFAULT_VIEWPORT,
45069
45886
  className
45070
45887
  }) {
45071
- const canvasRef = React76.useRef(null);
45072
- const frameRef = React76.useRef(0);
45073
- React76.useEffect(() => {
45888
+ const canvasRef = React79.useRef(null);
45889
+ const frameRef = React79.useRef(0);
45890
+ React79.useEffect(() => {
45074
45891
  const canvas = canvasRef.current;
45075
45892
  if (!canvas) return;
45076
45893
  const ctx = canvas.getContext("2d");
@@ -45167,7 +45984,7 @@ var init_MiniMap = __esm({
45167
45984
  }
45168
45985
  });
45169
45986
  function extractTitle2(children) {
45170
- if (!React76__default.isValidElement(children)) return void 0;
45987
+ if (!React79__default.isValidElement(children)) return void 0;
45171
45988
  const props = children.props;
45172
45989
  if (typeof props.title === "string") {
45173
45990
  return props.title;
@@ -45744,7 +46561,7 @@ var init_PricingPageTemplate = __esm({
45744
46561
  }
45745
46562
  });
45746
46563
  function ResourceCounter({
45747
- assetUrl = DEFAULT_ASSET_URL12,
46564
+ assetUrl = DEFAULT_ASSET_URL10,
45748
46565
  icon,
45749
46566
  label = "Gold",
45750
46567
  value = 250,
@@ -45787,7 +46604,7 @@ function ResourceCounter({
45787
46604
  }
45788
46605
  );
45789
46606
  }
45790
- var colorTokenClasses2, DEFAULT_ASSET_URL12, sizeMap15;
46607
+ var colorTokenClasses2, DEFAULT_ASSET_URL10, sizeMap15;
45791
46608
  var init_ResourceCounter = __esm({
45792
46609
  "components/game/atoms/ResourceCounter.tsx"() {
45793
46610
  init_cn();
@@ -45800,7 +46617,7 @@ var init_ResourceCounter = __esm({
45800
46617
  error: "text-error",
45801
46618
  muted: "text-muted-foreground"
45802
46619
  };
45803
- DEFAULT_ASSET_URL12 = "https://almadar-kflow-assets.web.app/shared/world-map/gold_mine.png";
46620
+ DEFAULT_ASSET_URL10 = "https://almadar-kflow-assets.web.app/shared/world-map/gold_mine.png";
45804
46621
  sizeMap15 = {
45805
46622
  sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm", img: 16 },
45806
46623
  md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base", img: 20 },
@@ -45809,31 +46626,33 @@ var init_ResourceCounter = __esm({
45809
46626
  ResourceCounter.displayName = "ResourceCounter";
45810
46627
  }
45811
46628
  });
45812
- function dungeonTerrain(x, y, stairsX, stairsY) {
46629
+ function resolveManifestUrl3(manifest, relative) {
46630
+ if (relative == null) return void 0;
46631
+ if (/^https?:\/\//.test(relative)) return relative;
46632
+ const base = manifest?.baseUrl;
46633
+ if (base == null) return relative;
46634
+ const cleanBase = base.replace(/\/$/, "");
46635
+ const cleanRel = relative.replace(/^\//, "");
46636
+ return `${cleanBase}/${cleanRel}`;
46637
+ }
46638
+ function dungeonTerrain(x, y, stairsX, stairsY, manifest) {
45813
46639
  const isBorder = x === 0 || y === 0 || x === DUNGEON_GRID_W - 1 || y === DUNGEON_GRID_H - 1;
45814
46640
  const isPillar = x % 4 === 0 && y % 4 === 0 && !isBorder;
45815
46641
  const isWall = isBorder || isPillar;
46642
+ const terrains = manifest?.terrains;
45816
46643
  if (x === stairsX && y === stairsY) {
45817
- return { terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` };
46644
+ return { terrain: "stairs", passable: true, terrainSprite: resolveManifestUrl3(manifest, terrains?.stairs) };
45818
46645
  }
45819
46646
  if (isWall) {
45820
- return { terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` };
45821
- }
45822
- const variant = (x * 3 + y * 7) % 5;
45823
- const sprites = [
45824
- `${CDN4}/isometric-dungeon/Isometric/dirt_E.png`,
45825
- `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png`,
45826
- `${CDN4}/isometric-dungeon/Isometric/planks_E.png`,
45827
- `${CDN4}/isometric-dungeon/Isometric/stoneTile_E.png`,
45828
- `${CDN4}/isometric-dungeon/Isometric/dirt_E.png`
45829
- ];
45830
- return { terrain: "floor", passable: true, terrainSprite: sprites[variant] };
46647
+ return { terrain: "wall", passable: false, terrainSprite: resolveManifestUrl3(manifest, terrains?.wall) };
46648
+ }
46649
+ return { terrain: "floor", passable: true, terrainSprite: resolveManifestUrl3(manifest, terrains?.floor) };
45831
46650
  }
45832
- function buildDefaultDungeonTiles(stairsX, stairsY) {
46651
+ function buildDefaultDungeonTiles(stairsX, stairsY, manifest) {
45833
46652
  const tiles = [];
45834
46653
  for (let y = 0; y < DUNGEON_GRID_H; y++) {
45835
46654
  for (let x = 0; x < DUNGEON_GRID_W; x++) {
45836
- tiles.push({ x, y, ...dungeonTerrain(x, y, stairsX, stairsY) });
46655
+ tiles.push({ x, y, ...dungeonTerrain(x, y, stairsX, stairsY, manifest) });
45837
46656
  }
45838
46657
  }
45839
46658
  return tiles;
@@ -45863,20 +46682,48 @@ function RoguelikeBoard({
45863
46682
  className
45864
46683
  }) {
45865
46684
  const board = boardEntity(entity) ?? {};
45866
- const resolvedStairsX = propStairsX ?? num(board.stairsX, 14);
45867
- const resolvedStairsY = propStairsY ?? num(board.stairsY, 14);
45868
- const tiles = propTiles ?? (Array.isArray(board.tiles) && board.tiles.length >= DUNGEON_GRID_W * DUNGEON_GRID_H ? board.tiles : buildDefaultDungeonTiles(resolvedStairsX, resolvedStairsY));
45869
- const enemies = propEnemies ?? (Array.isArray(board.enemies) ? board.enemies : DEFAULT_ENEMIES);
45870
- const items = propItems ?? (Array.isArray(board.items) ? board.items : DEFAULT_ITEMS);
45871
- const player = propPlayer ?? board.player ?? { x: 1, y: 1 };
46685
+ const assetManifest = propAssetManifest ?? board.assetManifest;
46686
+ const stairsX = propStairsX ?? num(board.stairsX, 14);
46687
+ const stairsY = propStairsY ?? num(board.stairsY, 14);
46688
+ const entityTiles = useMemo(
46689
+ () => rows(board.tiles).map((r) => ({
46690
+ x: num(r.x),
46691
+ y: num(r.y),
46692
+ terrain: r.terrain == null ? void 0 : str(r.terrain),
46693
+ terrainSprite: r.terrainSprite == null ? void 0 : str(r.terrainSprite),
46694
+ passable: r.passable !== false
46695
+ })),
46696
+ [board.tiles]
46697
+ );
46698
+ const tiles = propTiles ?? (entityTiles.length >= DUNGEON_GRID_W * DUNGEON_GRID_H ? entityTiles : buildDefaultDungeonTiles(stairsX, stairsY, assetManifest));
46699
+ const entityEnemies = useMemo(
46700
+ () => rows(board.enemies).map((r) => ({
46701
+ id: str(r.id),
46702
+ x: num(r.x),
46703
+ y: num(r.y),
46704
+ hp: num(r.hp),
46705
+ attack: num(r.attack)
46706
+ })),
46707
+ [board.enemies]
46708
+ );
46709
+ const enemies = propEnemies ?? (entityEnemies.length > 0 ? entityEnemies : DEFAULT_ENEMIES);
46710
+ const entityItems = useMemo(
46711
+ () => rows(board.items).map((r) => ({
46712
+ id: str(r.id),
46713
+ x: num(r.x),
46714
+ y: num(r.y),
46715
+ kind: str(r.kind) || "health_potion"
46716
+ })),
46717
+ [board.items]
46718
+ );
46719
+ const items = propItems ?? (entityItems.length > 0 ? entityItems : DEFAULT_ITEMS);
46720
+ const player = propPlayer ?? (board.player == null ? { x: 1, y: 1 } : vec2(board.player));
45872
46721
  const playerHp = propPlayerHp ?? num(board.playerHp, 10);
45873
46722
  const playerMaxHp = propPlayerMaxHp ?? num(board.playerMaxHp, 10);
45874
46723
  const playerAttack = propPlayerAttack ?? num(board.playerAttack, 3);
45875
46724
  const depth = propDepth ?? num(board.depth, 1);
45876
46725
  const result = propResult ?? (str(board.result) || "none");
45877
46726
  const phase = propPhase ?? (str(board.phase) || "player_turn");
45878
- propStairsX ?? num(board.stairsX, 14);
45879
- propStairsY ?? num(board.stairsY, 14);
45880
46727
  const eventBus = useEventBus();
45881
46728
  const { t } = useTranslate();
45882
46729
  const [hoveredTile, setHoveredTile] = useState(null);
@@ -45923,7 +46770,7 @@ function RoguelikeBoard({
45923
46770
  health: playerHp,
45924
46771
  maxHealth: playerMaxHp,
45925
46772
  unitType: "player",
45926
- sprite: `${CDN4}/isometric-dungeon/Characters/Male/Male_0_Idle0.png`
46773
+ sprite: resolveManifestUrl3(assetManifest, assetManifest?.units?.player)
45927
46774
  },
45928
46775
  ...liveEnemies.map((e) => ({
45929
46776
  id: e.id,
@@ -45933,19 +46780,19 @@ function RoguelikeBoard({
45933
46780
  health: e.hp,
45934
46781
  maxHealth: 6,
45935
46782
  unitType: "enemy",
45936
- sprite: `${CDN4}/sprite-sheets/shadow-legion-sprite-sheet-sw.png`
46783
+ sprite: resolveManifestUrl3(assetManifest, assetManifest?.units?.enemy)
45937
46784
  }))
45938
46785
  ];
45939
- }, [player, enemies, playerHp, playerMaxHp, t]);
46786
+ }, [player, enemies, playerHp, playerMaxHp, assetManifest, t]);
45940
46787
  const isoFeatures = useMemo(
45941
46788
  () => items.map((it) => ({
45942
46789
  id: it.id,
45943
46790
  x: it.x,
45944
46791
  y: it.y,
45945
46792
  type: it.kind,
45946
- sprite: FEATURE_SPRITE[it.kind] ?? FEATURE_SPRITE.health_potion
46793
+ sprite: resolveManifestUrl3(assetManifest, assetManifest?.features?.[it.kind])
45947
46794
  })),
45948
- [items]
46795
+ [items, assetManifest]
45949
46796
  );
45950
46797
  const validMoves = useMemo(() => {
45951
46798
  if (result !== "none" || phase !== "player_turn") return [];
@@ -46004,8 +46851,8 @@ function RoguelikeBoard({
46004
46851
  onTileHover: (x, y) => setHoveredTile({ x, y }),
46005
46852
  onTileLeave: () => setHoveredTile(null),
46006
46853
  scale,
46007
- assetBaseUrl: propAssetManifest?.baseUrl ?? CDN4,
46008
- assetManifest: propAssetManifest,
46854
+ assetBaseUrl: assetManifest?.baseUrl,
46855
+ assetManifest,
46009
46856
  unitScale,
46010
46857
  spriteHeightRatio,
46011
46858
  spriteMaxWidthRatio
@@ -46052,7 +46899,7 @@ function RoguelikeBoard({
46052
46899
  ] }) })
46053
46900
  ] });
46054
46901
  }
46055
- var CDN4, DUNGEON_GRID_W, DUNGEON_GRID_H, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
46902
+ var DUNGEON_GRID_W, DUNGEON_GRID_H, DEFAULT_ENEMIES, DEFAULT_ITEMS;
46056
46903
  var init_RoguelikeBoard = __esm({
46057
46904
  "components/game/organisms/RoguelikeBoard.tsx"() {
46058
46905
  "use client";
@@ -46064,10 +46911,8 @@ var init_RoguelikeBoard = __esm({
46064
46911
  init_Stack();
46065
46912
  init_IsometricCanvas();
46066
46913
  init_boardEntity();
46067
- CDN4 = "https://almadar-kflow-assets.web.app/shared";
46068
46914
  DUNGEON_GRID_W = 16;
46069
46915
  DUNGEON_GRID_H = 16;
46070
- buildDefaultDungeonTiles(14, 14);
46071
46916
  DEFAULT_ENEMIES = [
46072
46917
  { id: "e1", x: 5, y: 2, hp: 5, attack: 2 },
46073
46918
  { id: "e2", x: 9, y: 6, hp: 4, attack: 1 },
@@ -46079,11 +46924,6 @@ var init_RoguelikeBoard = __esm({
46079
46924
  { id: "i2", x: 7, y: 9, kind: "sword" },
46080
46925
  { id: "i3", x: 11, y: 5, kind: "shield" }
46081
46926
  ];
46082
- FEATURE_SPRITE = {
46083
- health_potion: `${CDN4}/isometric-dungeon/Isometric/chestClosed_E.png`,
46084
- sword: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`,
46085
- shield: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`
46086
- };
46087
46927
  RoguelikeBoard.displayName = "RoguelikeBoard";
46088
46928
  }
46089
46929
  });
@@ -46121,40 +46961,40 @@ function RoguelikeTemplate({
46121
46961
  }
46122
46962
  );
46123
46963
  }
46124
- var CDN5, DEFAULT_ROGUELIKE_TILES, DEFAULT_ROGUELIKE_MANIFEST;
46964
+ var CDN4, DEFAULT_ROGUELIKE_TILES, DEFAULT_ROGUELIKE_MANIFEST;
46125
46965
  var init_RoguelikeTemplate = __esm({
46126
46966
  "components/game/templates/RoguelikeTemplate.tsx"() {
46127
46967
  init_RoguelikeBoard();
46128
- CDN5 = "https://almadar-kflow-assets.web.app/shared";
46968
+ CDN4 = "https://almadar-kflow-assets.web.app/shared";
46129
46969
  DEFAULT_ROGUELIKE_TILES = [
46130
- { x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
46131
- { x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
46132
- { x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
46133
- { x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
46134
- { x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
46135
- { x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
46136
- { x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
46137
- { x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
46138
- { x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
46139
- { x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
46140
- { x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
46141
- { x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
46142
- { x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
46143
- { x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
46144
- { x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
46145
- { x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
46146
- { x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
46147
- { x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
46148
- { x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stairs_E.png` },
46149
- { x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
46150
- { x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
46151
- { x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
46152
- { x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
46153
- { x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
46154
- { x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` }
46970
+ { x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46971
+ { x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46972
+ { x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46973
+ { x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46974
+ { x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46975
+ { x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46976
+ { x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
46977
+ { x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
46978
+ { x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
46979
+ { x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46980
+ { x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46981
+ { x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
46982
+ { x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
46983
+ { x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
46984
+ { x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46985
+ { x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46986
+ { x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
46987
+ { x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
46988
+ { x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` },
46989
+ { x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46990
+ { x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46991
+ { x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46992
+ { x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46993
+ { x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46994
+ { x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` }
46155
46995
  ];
46156
46996
  DEFAULT_ROGUELIKE_MANIFEST = {
46157
- baseUrl: CDN5,
46997
+ baseUrl: CDN4,
46158
46998
  terrains: {
46159
46999
  floor: "/isometric-dungeon/Isometric/dirt_E.png",
46160
47000
  wall: "/isometric-dungeon/Isometric/stoneInset_E.png",
@@ -46261,7 +47101,7 @@ var init_debugRegistry = __esm({
46261
47101
  }
46262
47102
  });
46263
47103
  function useDebugData() {
46264
- const [data, setData] = React76.useState(() => ({
47104
+ const [data, setData] = React79.useState(() => ({
46265
47105
  traits: [],
46266
47106
  ticks: [],
46267
47107
  guards: [],
@@ -46275,7 +47115,7 @@ function useDebugData() {
46275
47115
  },
46276
47116
  lastUpdate: Date.now()
46277
47117
  }));
46278
- React76.useEffect(() => {
47118
+ React79.useEffect(() => {
46279
47119
  const updateData = () => {
46280
47120
  setData({
46281
47121
  traits: getAllTraits(),
@@ -46384,12 +47224,12 @@ function layoutGraph(states, transitions, initialState, width, height) {
46384
47224
  return positions;
46385
47225
  }
46386
47226
  function WalkMinimap() {
46387
- const [walkStep, setWalkStep] = React76.useState(null);
46388
- const [traits2, setTraits] = React76.useState([]);
46389
- const [coveredEdges, setCoveredEdges] = React76.useState([]);
46390
- const [completedTraits, setCompletedTraits] = React76.useState(/* @__PURE__ */ new Set());
46391
- const prevTraitRef = React76.useRef(null);
46392
- React76.useEffect(() => {
47227
+ const [walkStep, setWalkStep] = React79.useState(null);
47228
+ const [traits2, setTraits] = React79.useState([]);
47229
+ const [coveredEdges, setCoveredEdges] = React79.useState([]);
47230
+ const [completedTraits, setCompletedTraits] = React79.useState(/* @__PURE__ */ new Set());
47231
+ const prevTraitRef = React79.useRef(null);
47232
+ React79.useEffect(() => {
46393
47233
  const interval = setInterval(() => {
46394
47234
  const w = window;
46395
47235
  const step = w.__orbitalWalkStep;
@@ -46825,15 +47665,15 @@ var init_EntitiesTab = __esm({
46825
47665
  });
46826
47666
  function EventFlowTab({ events: events2 }) {
46827
47667
  const { t } = useTranslate();
46828
- const [filter, setFilter] = React76.useState("all");
46829
- const containerRef = React76.useRef(null);
46830
- const [autoScroll, setAutoScroll] = React76.useState(true);
46831
- React76.useEffect(() => {
47668
+ const [filter, setFilter] = React79.useState("all");
47669
+ const containerRef = React79.useRef(null);
47670
+ const [autoScroll, setAutoScroll] = React79.useState(true);
47671
+ React79.useEffect(() => {
46832
47672
  if (autoScroll && containerRef.current) {
46833
47673
  containerRef.current.scrollTop = containerRef.current.scrollHeight;
46834
47674
  }
46835
47675
  }, [events2.length, autoScroll]);
46836
- const filteredEvents = React76.useMemo(() => {
47676
+ const filteredEvents = React79.useMemo(() => {
46837
47677
  if (filter === "all") return events2;
46838
47678
  return events2.filter((e) => e.type === filter);
46839
47679
  }, [events2, filter]);
@@ -46949,7 +47789,7 @@ var init_EventFlowTab = __esm({
46949
47789
  });
46950
47790
  function GuardsPanel({ guards }) {
46951
47791
  const { t } = useTranslate();
46952
- const [filter, setFilter] = React76.useState("all");
47792
+ const [filter, setFilter] = React79.useState("all");
46953
47793
  if (guards.length === 0) {
46954
47794
  return /* @__PURE__ */ jsx(
46955
47795
  EmptyState,
@@ -46962,7 +47802,7 @@ function GuardsPanel({ guards }) {
46962
47802
  }
46963
47803
  const passedCount = guards.filter((g) => g.result).length;
46964
47804
  const failedCount = guards.length - passedCount;
46965
- const filteredGuards = React76.useMemo(() => {
47805
+ const filteredGuards = React79.useMemo(() => {
46966
47806
  if (filter === "all") return guards;
46967
47807
  if (filter === "passed") return guards.filter((g) => g.result);
46968
47808
  return guards.filter((g) => !g.result);
@@ -47125,10 +47965,10 @@ function EffectBadge({ effect }) {
47125
47965
  }
47126
47966
  function TransitionTimeline({ transitions }) {
47127
47967
  const { t } = useTranslate();
47128
- const containerRef = React76.useRef(null);
47129
- const [autoScroll, setAutoScroll] = React76.useState(true);
47130
- const [expandedId, setExpandedId] = React76.useState(null);
47131
- React76.useEffect(() => {
47968
+ const containerRef = React79.useRef(null);
47969
+ const [autoScroll, setAutoScroll] = React79.useState(true);
47970
+ const [expandedId, setExpandedId] = React79.useState(null);
47971
+ React79.useEffect(() => {
47132
47972
  if (autoScroll && containerRef.current) {
47133
47973
  containerRef.current.scrollTop = containerRef.current.scrollHeight;
47134
47974
  }
@@ -47408,9 +48248,9 @@ function getAllEvents(traits2) {
47408
48248
  function EventDispatcherTab({ traits: traits2, schema }) {
47409
48249
  const eventBus = useEventBus();
47410
48250
  const { t } = useTranslate();
47411
- const [log10, setLog] = React76.useState([]);
47412
- const prevStatesRef = React76.useRef(/* @__PURE__ */ new Map());
47413
- React76.useEffect(() => {
48251
+ const [log10, setLog] = React79.useState([]);
48252
+ const prevStatesRef = React79.useRef(/* @__PURE__ */ new Map());
48253
+ React79.useEffect(() => {
47414
48254
  for (const trait of traits2) {
47415
48255
  const prev = prevStatesRef.current.get(trait.id);
47416
48256
  if (prev && prev !== trait.currentState) {
@@ -47579,10 +48419,10 @@ function VerifyModePanel({
47579
48419
  localCount
47580
48420
  }) {
47581
48421
  const { t } = useTranslate();
47582
- const [expanded, setExpanded] = React76.useState(true);
47583
- const scrollRef = React76.useRef(null);
47584
- const prevCountRef = React76.useRef(0);
47585
- React76.useEffect(() => {
48422
+ const [expanded, setExpanded] = React79.useState(true);
48423
+ const scrollRef = React79.useRef(null);
48424
+ const prevCountRef = React79.useRef(0);
48425
+ React79.useEffect(() => {
47586
48426
  if (expanded && transitions.length > prevCountRef.current && scrollRef.current) {
47587
48427
  scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
47588
48428
  }
@@ -47639,10 +48479,10 @@ function RuntimeDebugger({
47639
48479
  schema
47640
48480
  }) {
47641
48481
  const { t } = useTranslate();
47642
- const [isCollapsed, setIsCollapsed] = React76.useState(mode === "verify" ? true : defaultCollapsed);
47643
- const [isVisible, setIsVisible] = React76.useState(mode === "inline" || mode === "verify" || isDebugEnabled2());
48482
+ const [isCollapsed, setIsCollapsed] = React79.useState(mode === "verify" ? true : defaultCollapsed);
48483
+ const [isVisible, setIsVisible] = React79.useState(mode === "inline" || mode === "verify" || isDebugEnabled2());
47644
48484
  const debugData = useDebugData();
47645
- React76.useEffect(() => {
48485
+ React79.useEffect(() => {
47646
48486
  if (mode === "inline") return;
47647
48487
  return onDebugToggle((enabled) => {
47648
48488
  setIsVisible(enabled);
@@ -47651,7 +48491,7 @@ function RuntimeDebugger({
47651
48491
  }
47652
48492
  });
47653
48493
  }, [mode]);
47654
- React76.useEffect(() => {
48494
+ React79.useEffect(() => {
47655
48495
  if (mode === "inline") return;
47656
48496
  const handleKeyDown = (e) => {
47657
48497
  if (e.key === "`" && isVisible) {
@@ -48211,7 +49051,7 @@ function SequenceBar({
48211
49051
  onSlotRemove(index);
48212
49052
  }, [onSlotRemove, playing]);
48213
49053
  const paddedSlots = Array.from({ length: maxSlots }, (_, i) => slots[i]);
48214
- return /* @__PURE__ */ jsx(HStack, { className: cn("items-center", className), gap: "sm", children: paddedSlots.map((slot, i) => /* @__PURE__ */ jsxs(React76__default.Fragment, { children: [
49054
+ return /* @__PURE__ */ jsx(HStack, { className: cn("items-center", className), gap: "sm", children: paddedSlots.map((slot, i) => /* @__PURE__ */ jsxs(React79__default.Fragment, { children: [
48215
49055
  i > 0 && /* @__PURE__ */ jsx(
48216
49056
  Typography,
48217
49057
  {
@@ -49207,7 +50047,7 @@ var init_StatCard = __esm({
49207
50047
  const labelToUse = propLabel ?? propTitle;
49208
50048
  const eventBus = useEventBus();
49209
50049
  const { t } = useTranslate();
49210
- const handleActionClick = React76__default.useCallback(() => {
50050
+ const handleActionClick = React79__default.useCallback(() => {
49211
50051
  if (action?.event) {
49212
50052
  eventBus.emit(`UI:${action.event}`, {});
49213
50053
  }
@@ -49218,7 +50058,7 @@ var init_StatCard = __esm({
49218
50058
  const data = Array.isArray(entity) ? entity : entity ? [entity] : [];
49219
50059
  const isLoading = externalLoading ?? false;
49220
50060
  const error = externalError;
49221
- const computeMetricValue = React76__default.useCallback(
50061
+ const computeMetricValue = React79__default.useCallback(
49222
50062
  (metric, items) => {
49223
50063
  if (metric.value !== void 0) {
49224
50064
  return metric.value;
@@ -49257,7 +50097,7 @@ var init_StatCard = __esm({
49257
50097
  },
49258
50098
  []
49259
50099
  );
49260
- const schemaStats = React76__default.useMemo(() => {
50100
+ const schemaStats = React79__default.useMemo(() => {
49261
50101
  if (!metrics || metrics.length === 0) return null;
49262
50102
  return metrics.map((metric) => ({
49263
50103
  label: metric.label,
@@ -49265,7 +50105,7 @@ var init_StatCard = __esm({
49265
50105
  format: metric.format
49266
50106
  }));
49267
50107
  }, [metrics, data, computeMetricValue]);
49268
- const calculatedTrend = React76__default.useMemo(() => {
50108
+ const calculatedTrend = React79__default.useMemo(() => {
49269
50109
  if (manualTrend !== void 0) return manualTrend;
49270
50110
  if (previousValue === void 0 || currentValue2 === void 0)
49271
50111
  return void 0;
@@ -49966,7 +50806,7 @@ function formatDuration(seconds) {
49966
50806
  return `${Math.round(seconds)}s`;
49967
50807
  }
49968
50808
  function StatusEffect({
49969
- assetUrl = DEFAULT_ASSET_URL13,
50809
+ assetUrl = DEFAULT_ASSET_URL11,
49970
50810
  icon = "shield",
49971
50811
  label = "Shield",
49972
50812
  duration = 30,
@@ -50024,12 +50864,12 @@ function StatusEffect({
50024
50864
  label && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground mt-0.5 text-center whitespace-nowrap", children: label })
50025
50865
  ] });
50026
50866
  }
50027
- var DEFAULT_ASSET_URL13, sizeMap16, variantStyles9;
50867
+ var DEFAULT_ASSET_URL11, sizeMap16, variantStyles9;
50028
50868
  var init_StatusEffect = __esm({
50029
50869
  "components/game/atoms/StatusEffect.tsx"() {
50030
50870
  init_cn();
50031
50871
  init_Icon();
50032
- DEFAULT_ASSET_URL13 = "https://almadar-kflow-assets.web.app/shared/effects/particles/flame_01.png";
50872
+ DEFAULT_ASSET_URL11 = "https://almadar-kflow-assets.web.app/shared/effects/particles/flame_01.png";
50033
50873
  sizeMap16 = {
50034
50874
  sm: { container: "w-8 h-8", icon: "text-sm", badge: "text-xs -top-1 -right-1 w-4 h-4", timer: "text-[9px]", img: 20 },
50035
50875
  md: { container: "w-10 h-10", icon: "text-base", badge: "text-xs -top-1 -right-1 w-5 h-5", timer: "text-xs", img: 28 },
@@ -50315,7 +51155,7 @@ var init_Timeline = __esm({
50315
51155
  }) => {
50316
51156
  const { t } = useTranslate();
50317
51157
  const entityData = Array.isArray(entity) ? entity : [];
50318
- const items = React76__default.useMemo(() => {
51158
+ const items = React79__default.useMemo(() => {
50319
51159
  if (propItems) return propItems;
50320
51160
  if (entityData.length === 0) return [];
50321
51161
  return entityData.map((record, idx) => {
@@ -50472,7 +51312,7 @@ var init_TimerDisplay = __esm({
50472
51312
  }
50473
51313
  });
50474
51314
  function extractToastProps(children) {
50475
- if (!React76__default.isValidElement(children)) {
51315
+ if (!React79__default.isValidElement(children)) {
50476
51316
  if (typeof children === "string") {
50477
51317
  return { message: children };
50478
51318
  }
@@ -50510,7 +51350,7 @@ var init_ToastSlot = __esm({
50510
51350
  eventBus.emit("UI:CLOSE");
50511
51351
  };
50512
51352
  if (!isVisible) return null;
50513
- const isCustomContent = React76__default.isValidElement(children) && !message;
51353
+ const isCustomContent = React79__default.isValidElement(children) && !message;
50514
51354
  return /* @__PURE__ */ jsx(Box, { className: "fixed bottom-4 right-4 z-50", children: isCustomContent ? children : /* @__PURE__ */ jsx(
50515
51355
  Toast,
50516
51356
  {
@@ -50526,82 +51366,442 @@ var init_ToastSlot = __esm({
50526
51366
  ToastSlot.displayName = "ToastSlot";
50527
51367
  }
50528
51368
  });
51369
+ function resolveManifestUrl4(manifest, relative) {
51370
+ if (relative == null || relative === "") return void 0;
51371
+ if (/^https?:\/\//.test(relative)) return relative;
51372
+ const base = manifest?.baseUrl;
51373
+ if (base == null) return relative;
51374
+ const cleanBase = base.replace(/\/$/, "");
51375
+ const cleanRel = relative.replace(/^\//, "");
51376
+ return `${cleanBase}/${cleanRel}`;
51377
+ }
51378
+ function readPlayer(v) {
51379
+ if (v == null || typeof v !== "object") return void 0;
51380
+ const o = v;
51381
+ return { x: num(o.x), y: num(o.y) };
51382
+ }
51383
+ function TopDownShooterBoard({
51384
+ entity,
51385
+ player: propPlayer,
51386
+ enemies: propEnemies,
51387
+ projectiles: propProjectiles,
51388
+ score: propScore,
51389
+ wave: propWave,
51390
+ lives: propLives,
51391
+ result: propResult,
51392
+ assetManifest: propAssetManifest,
51393
+ width = DEFAULT_WIDTH,
51394
+ height = DEFAULT_HEIGHT,
51395
+ spriteSize = 32,
51396
+ moveEvent = "MOVE",
51397
+ fireEvent = "FIRE",
51398
+ playAgainEvent = "PLAY_AGAIN",
51399
+ gameEndEvent = "GAME_END",
51400
+ className
51401
+ }) {
51402
+ const board = boardEntity(entity) ?? {};
51403
+ const eventBus = useEventBus();
51404
+ const { t } = useTranslate();
51405
+ const assetManifest = propAssetManifest ?? board.assetManifest;
51406
+ const player = propPlayer ?? readPlayer(board.player) ?? { x: width / 2, y: height - spriteSize };
51407
+ const enemies = useMemo(
51408
+ () => propEnemies ?? rows(board.enemies).map((r) => ({
51409
+ id: str(r.id),
51410
+ x: num(r.x),
51411
+ y: num(r.y),
51412
+ hp: num(r.hp, 1)
51413
+ })),
51414
+ [propEnemies, board.enemies]
51415
+ );
51416
+ const projectiles = useMemo(
51417
+ () => propProjectiles ?? rows(board.projectiles).map((r) => ({
51418
+ id: str(r.id),
51419
+ x: num(r.x),
51420
+ y: num(r.y),
51421
+ vx: num(r.vx),
51422
+ vy: num(r.vy)
51423
+ })),
51424
+ [propProjectiles, board.projectiles]
51425
+ );
51426
+ const score = propScore ?? num(board.score, 0);
51427
+ const wave = propWave ?? num(board.wave, 1);
51428
+ const lives = propLives ?? num(board.lives, 3);
51429
+ const result = propResult ?? (str(board.result) || "none");
51430
+ const playerSprite = resolveManifestUrl4(assetManifest, assetManifest?.units?.player);
51431
+ const enemySprite = resolveManifestUrl4(assetManifest, assetManifest?.units?.enemy);
51432
+ const projectileSprite = resolveManifestUrl4(assetManifest, assetManifest?.features?.projectile);
51433
+ const backgroundSprite = resolveManifestUrl4(assetManifest, assetManifest?.background);
51434
+ const spriteUrls = useMemo(
51435
+ () => [playerSprite, enemySprite, projectileSprite].filter((u) => u != null),
51436
+ [playerSprite, enemySprite, projectileSprite]
51437
+ );
51438
+ const { getImage } = useImageCache(spriteUrls);
51439
+ const sceneRef = useRef({ player, enemies, projectiles, playerSprite, enemySprite, projectileSprite });
51440
+ sceneRef.current = { player, enemies, projectiles, playerSprite, enemySprite, projectileSprite };
51441
+ boardLog2.debug("shooter-resolve", {
51442
+ player: { x: player.x, y: player.y },
51443
+ enemyCount: enemies.length,
51444
+ projectileCount: projectiles.length,
51445
+ wave,
51446
+ lives,
51447
+ result,
51448
+ hasPlayerSprite: playerSprite != null
51449
+ });
51450
+ const onDraw = useCallback(
51451
+ (ctx, _frame) => {
51452
+ const scene = sceneRef.current;
51453
+ ctx.fillStyle = "#10141c";
51454
+ ctx.fillRect(0, 0, width, height);
51455
+ const half = spriteSize / 2;
51456
+ for (const p2 of scene.projectiles) {
51457
+ const img = scene.projectileSprite ? getImage(scene.projectileSprite) : void 0;
51458
+ if (img) {
51459
+ ctx.drawImage(img, p2.x - half / 2, p2.y - half / 2, spriteSize / 2, spriteSize / 2);
51460
+ } else {
51461
+ ctx.fillStyle = "#ffd34d";
51462
+ ctx.beginPath();
51463
+ ctx.arc(p2.x, p2.y, spriteSize / 6, 0, Math.PI * 2);
51464
+ ctx.fill();
51465
+ }
51466
+ }
51467
+ for (const e of scene.enemies) {
51468
+ const img = scene.enemySprite ? getImage(scene.enemySprite) : void 0;
51469
+ if (img) {
51470
+ ctx.drawImage(img, e.x - half, e.y - half, spriteSize, spriteSize);
51471
+ } else {
51472
+ ctx.fillStyle = "#e0524d";
51473
+ ctx.fillRect(e.x - half, e.y - half, spriteSize, spriteSize);
51474
+ }
51475
+ }
51476
+ const pImg = scene.playerSprite ? getImage(scene.playerSprite) : void 0;
51477
+ if (pImg) {
51478
+ ctx.drawImage(pImg, scene.player.x - half, scene.player.y - half, spriteSize, spriteSize);
51479
+ } else {
51480
+ ctx.fillStyle = "#4da3ff";
51481
+ ctx.beginPath();
51482
+ ctx.moveTo(scene.player.x, scene.player.y - half);
51483
+ ctx.lineTo(scene.player.x + half, scene.player.y + half);
51484
+ ctx.lineTo(scene.player.x - half, scene.player.y + half);
51485
+ ctx.closePath();
51486
+ ctx.fill();
51487
+ }
51488
+ },
51489
+ [getImage, width, height, spriteSize]
51490
+ );
51491
+ const moveEventRef = useRef(moveEvent);
51492
+ moveEventRef.current = moveEvent;
51493
+ const fireEventRef = useRef(fireEvent);
51494
+ fireEventRef.current = fireEvent;
51495
+ const resultRef = useRef(result);
51496
+ resultRef.current = result;
51497
+ const playerRef = useRef(player);
51498
+ playerRef.current = player;
51499
+ useEffect(() => {
51500
+ function onKeyDown(e) {
51501
+ if (resultRef.current !== "none") return;
51502
+ let dx = 0;
51503
+ let dy = 0;
51504
+ switch (e.key) {
51505
+ case "ArrowUp":
51506
+ case "w":
51507
+ dy = -1;
51508
+ break;
51509
+ case "ArrowDown":
51510
+ case "s":
51511
+ dy = 1;
51512
+ break;
51513
+ case "ArrowLeft":
51514
+ case "a":
51515
+ dx = -1;
51516
+ break;
51517
+ case "ArrowRight":
51518
+ case "d":
51519
+ dx = 1;
51520
+ break;
51521
+ case " ": {
51522
+ e.preventDefault();
51523
+ const evt = fireEventRef.current;
51524
+ if (evt) {
51525
+ eventBus.emit(`UI:${evt}`, { tx: playerRef.current.x, ty: 0 });
51526
+ }
51527
+ return;
51528
+ }
51529
+ default:
51530
+ return;
51531
+ }
51532
+ e.preventDefault();
51533
+ const moveEvt = moveEventRef.current;
51534
+ if (moveEvt) {
51535
+ eventBus.emit(`UI:${moveEvt}`, { dx, dy });
51536
+ }
51537
+ }
51538
+ window.addEventListener("keydown", onKeyDown);
51539
+ return () => window.removeEventListener("keydown", onKeyDown);
51540
+ }, [eventBus]);
51541
+ const handleFireClick = useCallback(
51542
+ (e) => {
51543
+ if (result !== "none") return;
51544
+ const evt = fireEventRef.current;
51545
+ if (!evt) return;
51546
+ const rect = e.currentTarget.getBoundingClientRect();
51547
+ const tx = e.clientX - rect.left;
51548
+ const ty = e.clientY - rect.top;
51549
+ eventBus.emit(`UI:${evt}`, { tx, ty });
51550
+ },
51551
+ [result, eventBus]
51552
+ );
51553
+ const handlePlayAgain = useCallback(() => {
51554
+ if (playAgainEvent) {
51555
+ eventBus.emit(`UI:${playAgainEvent}`, {});
51556
+ }
51557
+ }, [playAgainEvent, eventBus]);
51558
+ const gameEndEmittedRef = useRef(false);
51559
+ useEffect(() => {
51560
+ if ((result === "won" || result === "lost") && gameEndEvent) {
51561
+ if (!gameEndEmittedRef.current) {
51562
+ gameEndEmittedRef.current = true;
51563
+ eventBus.emit(`UI:${gameEndEvent}`, { result });
51564
+ }
51565
+ } else {
51566
+ gameEndEmittedRef.current = false;
51567
+ }
51568
+ }, [result, gameEndEvent, eventBus]);
51569
+ const isGameOver = result === "won" || result === "lost";
51570
+ return /* @__PURE__ */ jsxs(VStack, { className: cn("top-down-shooter-board relative bg-background", className), gap: "none", children: [
51571
+ /* @__PURE__ */ jsxs(HStack, { className: "px-4 py-2 border-b border-border bg-surface", gap: "lg", align: "center", children: [
51572
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
51573
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("shooter.score") ?? "Score" }),
51574
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", className: "text-warning", children: score })
51575
+ ] }),
51576
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
51577
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("shooter.wave") ?? "Wave" }),
51578
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", children: wave })
51579
+ ] }),
51580
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
51581
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("shooter.lives") ?? "Lives" }),
51582
+ /* @__PURE__ */ jsx(
51583
+ Typography,
51584
+ {
51585
+ variant: "body2",
51586
+ weight: "bold",
51587
+ className: lives <= 1 ? "text-error" : "text-success",
51588
+ children: lives
51589
+ }
51590
+ )
51591
+ ] })
51592
+ ] }),
51593
+ /* @__PURE__ */ jsxs(Box, { className: "relative", onClick: handleFireClick, children: [
51594
+ /* @__PURE__ */ jsx(
51595
+ GameCanvas2D,
51596
+ {
51597
+ width,
51598
+ height,
51599
+ backgroundImage: backgroundSprite,
51600
+ assetBaseUrl: assetManifest?.baseUrl,
51601
+ onDraw
51602
+ }
51603
+ ),
51604
+ isGameOver && /* @__PURE__ */ jsx(Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm rounded-container", children: /* @__PURE__ */ jsxs(VStack, { className: "text-center p-8", gap: "lg", children: [
51605
+ /* @__PURE__ */ jsx(
51606
+ Typography,
51607
+ {
51608
+ variant: "h2",
51609
+ className: cn(
51610
+ "text-4xl font-black tracking-widest uppercase",
51611
+ result === "won" ? "text-warning" : "text-error"
51612
+ ),
51613
+ children: result === "won" ? t("shooter.victory") ?? "Victory!" : t("shooter.defeat") ?? "Defeat!"
51614
+ }
51615
+ ),
51616
+ /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-muted-foreground", children: result === "won" ? t("shooter.cleared") ?? `Cleared wave ${wave} \u2014 score ${score}` : t("shooter.overrun") ?? "You were overrun." }),
51617
+ /* @__PURE__ */ jsx(
51618
+ Button,
51619
+ {
51620
+ variant: "primary",
51621
+ className: "px-8 py-3 font-semibold",
51622
+ onClick: handlePlayAgain,
51623
+ children: t("shooter.playAgain") ?? "Play Again"
51624
+ }
51625
+ )
51626
+ ] }) })
51627
+ ] })
51628
+ ] });
51629
+ }
51630
+ var boardLog2, DEFAULT_WIDTH, DEFAULT_HEIGHT;
51631
+ var init_TopDownShooterBoard = __esm({
51632
+ "components/game/organisms/TopDownShooterBoard.tsx"() {
51633
+ "use client";
51634
+ init_cn();
51635
+ init_useEventBus();
51636
+ init_Box();
51637
+ init_Button();
51638
+ init_Typography();
51639
+ init_Stack();
51640
+ init_GameCanvas2D();
51641
+ init_useImageCache();
51642
+ init_boardEntity();
51643
+ boardLog2 = createLogger("almadar:ui:game:top-down-shooter-board");
51644
+ DEFAULT_WIDTH = 640;
51645
+ DEFAULT_HEIGHT = 480;
51646
+ TopDownShooterBoard.displayName = "TopDownShooterBoard";
51647
+ }
51648
+ });
51649
+ function isEntityRow3(value) {
51650
+ return value != null && typeof value === "object" && !Array.isArray(value);
51651
+ }
51652
+ function TopDownShooterTemplate({
51653
+ entity,
51654
+ title = "Arena Shooter",
51655
+ player,
51656
+ enemies,
51657
+ projectiles,
51658
+ score,
51659
+ wave,
51660
+ lives,
51661
+ result,
51662
+ assetManifest,
51663
+ width,
51664
+ height,
51665
+ spriteSize,
51666
+ moveEvent,
51667
+ fireEvent,
51668
+ playAgainEvent,
51669
+ gameEndEvent,
51670
+ className
51671
+ }) {
51672
+ const resolved = isEntityRow3(entity) ? entity : void 0;
51673
+ return /* @__PURE__ */ jsxs(
51674
+ Box,
51675
+ {
51676
+ display: "flex",
51677
+ fullHeight: true,
51678
+ className: cn("top-down-shooter-template flex-col", className),
51679
+ children: [
51680
+ /* @__PURE__ */ jsx(
51681
+ HStack,
51682
+ {
51683
+ gap: "sm",
51684
+ align: "center",
51685
+ className: "px-4 py-3 border-b-2 border-border bg-surface shrink-0",
51686
+ children: /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title })
51687
+ }
51688
+ ),
51689
+ /* @__PURE__ */ jsx(Box, { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsx(
51690
+ TopDownShooterBoard,
51691
+ {
51692
+ entity: resolved,
51693
+ player,
51694
+ enemies,
51695
+ projectiles,
51696
+ score,
51697
+ wave,
51698
+ lives,
51699
+ result,
51700
+ assetManifest,
51701
+ width,
51702
+ height,
51703
+ spriteSize,
51704
+ moveEvent,
51705
+ fireEvent,
51706
+ playAgainEvent,
51707
+ gameEndEvent,
51708
+ className: "h-full"
51709
+ }
51710
+ ) })
51711
+ ]
51712
+ }
51713
+ );
51714
+ }
51715
+ var init_TopDownShooterTemplate = __esm({
51716
+ "components/game/templates/TopDownShooterTemplate.tsx"() {
51717
+ init_cn();
51718
+ init_Box();
51719
+ init_Stack();
51720
+ init_Typography();
51721
+ init_TopDownShooterBoard();
51722
+ TopDownShooterTemplate.displayName = "TopDownShooterTemplate";
51723
+ }
51724
+ });
51725
+ function resolveManifestUrl5(manifest, relative) {
51726
+ if (relative == null) return void 0;
51727
+ if (/^https?:\/\//.test(relative)) return relative;
51728
+ const base = manifest?.baseUrl;
51729
+ if (base == null) return relative;
51730
+ const cleanBase = base.replace(/\/$/, "");
51731
+ const cleanRel = relative.replace(/^\//, "");
51732
+ return `${cleanBase}/${cleanRel}`;
51733
+ }
50529
51734
  function buildDefaultTDTiles() {
50530
51735
  const pathSet = new Set(DEFAULT_TD_PATH.map((p2) => `${p2.x},${p2.y}`));
50531
51736
  const tiles = [];
50532
51737
  for (let y = 0; y < TD_GRID_H; y++) {
50533
51738
  for (let x = 0; x < TD_GRID_W; x++) {
50534
51739
  if (pathSet.has(`${x},${y}`)) {
50535
- tiles.push({ x, y, terrain: "path", passable: false, terrainSprite: TERRAIN_SPRITES.path });
51740
+ tiles.push({ x, y, terrain: "path", passable: false });
50536
51741
  } else {
50537
51742
  const variant = (x * 3 + y * 5 + (x ^ y)) % 3;
50538
- const terrainKey = ["ground", "grass", "dirt"][variant];
50539
- tiles.push({ x, y, terrain: terrainKey, passable: true, terrainSprite: TERRAIN_SPRITES[terrainKey] ?? TERRAIN_SPRITES.ground });
51743
+ const terrainKey = ["ground", "grass", "stone"][variant];
51744
+ tiles.push({ x, y, terrain: terrainKey, passable: true });
50540
51745
  }
50541
51746
  }
50542
51747
  }
50543
51748
  return tiles;
50544
51749
  }
50545
- function tilesToIso(tiles) {
50546
- return tiles.map((t) => ({
50547
- x: t.x,
50548
- y: t.y,
50549
- terrain: t.terrain,
50550
- terrainSprite: t.terrainSprite ?? TERRAIN_SPRITES[t.terrain ?? "ground"],
50551
- passable: t.passable
50552
- }));
50553
- }
50554
- function towersToUnits(towers) {
50555
- return towers.map((t) => ({
50556
- id: t.id,
50557
- position: { x: t.x, y: t.y },
50558
- name: "Tower",
50559
- team: "player",
50560
- sprite: TOWER_SPRITE,
50561
- unitType: "guardian",
50562
- health: 1,
50563
- maxHealth: 1
50564
- }));
51750
+ function tilesToIso2(tiles, manifest) {
51751
+ return tiles.map((t) => {
51752
+ const key = t.terrain ?? "ground";
51753
+ const sprite = t.terrainSprite ?? resolveManifestUrl5(manifest, manifest?.terrains?.[key]) ?? resolveManifestUrl5(manifest, manifest?.terrains?.ground);
51754
+ return {
51755
+ x: t.x,
51756
+ y: t.y,
51757
+ terrain: t.terrain,
51758
+ terrainSprite: sprite,
51759
+ passable: t.passable
51760
+ };
51761
+ });
50565
51762
  }
50566
- function creepsToUnits(creeps) {
51763
+ function creepsToUnits(creeps, manifest) {
51764
+ const sprite = resolveManifestUrl5(manifest, manifest?.units?.creep);
50567
51765
  return creeps.map((c) => ({
50568
51766
  id: c.id,
50569
51767
  position: { x: c.x, y: c.y },
50570
51768
  name: "Creep",
50571
51769
  team: "enemy",
50572
- sprite: CREEP_SPRITE,
50573
- unitType: "scrapper",
51770
+ sprite,
51771
+ unitType: "creep",
50574
51772
  health: c.hp,
50575
51773
  maxHealth: c.maxHp
50576
51774
  }));
50577
51775
  }
50578
- function pathToFeatures(path) {
50579
- return path.map((p2, i) => ({
50580
- id: `path-${i}`,
50581
- x: p2.x,
50582
- y: p2.y,
50583
- type: "path-marker",
50584
- sprite: `${CDN6}world-map/road_straight.png`
50585
- }));
50586
- }
50587
- function heroToUnit(hero) {
51776
+ function heroToUnit(hero, manifest) {
50588
51777
  return {
50589
51778
  id: hero.id,
50590
51779
  position: { x: hero.x, y: hero.y },
50591
51780
  name: "Hero",
50592
51781
  team: "player",
50593
- sprite: HERO_SPRITE,
50594
- unitType: "amir",
51782
+ sprite: resolveManifestUrl5(manifest, manifest?.units?.hero),
51783
+ unitType: "hero",
50595
51784
  health: 1,
50596
51785
  maxHealth: 1
50597
51786
  };
50598
51787
  }
51788
+ function towersToFeatures(towers, manifest) {
51789
+ const sprite = resolveManifestUrl5(manifest, manifest?.features?.tower);
51790
+ return towers.map((t) => ({
51791
+ id: t.id,
51792
+ x: t.x,
51793
+ y: t.y,
51794
+ type: "tower",
51795
+ sprite
51796
+ }));
51797
+ }
50599
51798
  function TowerDefenseBoard({
50600
51799
  entity,
50601
51800
  tiles: propTiles,
50602
51801
  path: propPath,
50603
51802
  towers: propTowers,
50604
51803
  creeps: propCreeps,
51804
+ assetManifest: propAssetManifest,
50605
51805
  hero: propHero,
50606
51806
  gold: propGold,
50607
51807
  lives: propLives,
@@ -50624,12 +51824,51 @@ function TowerDefenseBoard({
50624
51824
  const board = boardEntity(entity) ?? {};
50625
51825
  const eventBus = useEventBus();
50626
51826
  const { t } = useTranslate();
50627
- const rawTiles = propTiles ?? rows(board.tiles);
51827
+ const assetManifest = propAssetManifest ?? board.assetManifest;
51828
+ const entityTiles = useMemo(
51829
+ () => rows(board.tiles).map((r) => ({
51830
+ x: num(r.x),
51831
+ y: num(r.y),
51832
+ terrain: r.terrain == null ? void 0 : str(r.terrain),
51833
+ terrainSprite: r.terrainSprite == null ? void 0 : str(r.terrainSprite),
51834
+ passable: r.passable !== false
51835
+ })),
51836
+ [board.tiles]
51837
+ );
51838
+ const rawTiles = propTiles ?? entityTiles;
50628
51839
  const tiles = rawTiles.length >= TD_GRID_W * TD_GRID_H ? rawTiles : DEFAULT_TD_TILES;
50629
- const rawPath = propPath ?? rows(board.path);
51840
+ const entityPath = useMemo(
51841
+ () => rows(board.path).map((r) => ({ x: num(r.x), y: num(r.y) })),
51842
+ [board.path]
51843
+ );
51844
+ const rawPath = propPath ?? entityPath;
50630
51845
  const path = rawPath.length > 0 ? rawPath : DEFAULT_TD_PATH;
50631
- const towers = propTowers ?? rows(board.towers);
50632
- const creeps = propCreeps ?? rows(board.creeps);
51846
+ const entityTowers = useMemo(
51847
+ () => rows(board.towers).map((r) => ({
51848
+ id: str(r.id),
51849
+ x: num(r.x),
51850
+ y: num(r.y),
51851
+ range: num(r.range),
51852
+ damage: num(r.damage),
51853
+ cooldown: num(r.cooldown),
51854
+ lastFiredAt: r.lastFiredAt == null ? void 0 : num(r.lastFiredAt)
51855
+ })),
51856
+ [board.towers]
51857
+ );
51858
+ const towers = propTowers ?? entityTowers;
51859
+ const entityCreeps = useMemo(
51860
+ () => rows(board.creeps).map((r) => ({
51861
+ id: str(r.id),
51862
+ x: num(r.x),
51863
+ y: num(r.y),
51864
+ hp: num(r.hp),
51865
+ maxHp: num(r.maxHp),
51866
+ pathIndex: num(r.pathIndex),
51867
+ speed: num(r.speed)
51868
+ })),
51869
+ [board.creeps]
51870
+ );
51871
+ const creeps = propCreeps ?? entityCreeps;
50633
51872
  const hero = propHero ?? { id: "hero", x: 8, y: 8 };
50634
51873
  const gold = propGold ?? num(board.gold, 100);
50635
51874
  const lives = propLives ?? num(board.lives, 20);
@@ -50681,12 +51920,11 @@ function TowerDefenseBoard({
50681
51920
  (t2) => t2.passable !== false && !towerPositions.has(`${t2.x},${t2.y}`) && !pathPositions.has(`${t2.x},${t2.y}`)
50682
51921
  ).map((t2) => ({ x: t2.x, y: t2.y }));
50683
51922
  }, [tiles, towerPositions, pathPositions, result, gold, towerCost]);
50684
- const isoTiles = useMemo(() => tilesToIso(tiles), [tiles]);
50685
- const towerUnits = useMemo(() => towersToUnits(towers), [towers]);
50686
- const creepUnits = useMemo(() => creepsToUnits(creeps), [creeps]);
50687
- const heroUnit = useMemo(() => heroToUnit(hero), [hero]);
50688
- const isoUnits = useMemo(() => [...towerUnits, ...creepUnits, heroUnit], [towerUnits, creepUnits, heroUnit]);
50689
- const pathFeatures = useMemo(() => pathToFeatures(path), [path]);
51923
+ const isoTiles = useMemo(() => tilesToIso2(tiles, assetManifest), [tiles, assetManifest]);
51924
+ const creepUnits = useMemo(() => creepsToUnits(creeps, assetManifest), [creeps, assetManifest]);
51925
+ const heroUnit = useMemo(() => heroToUnit(hero, assetManifest), [hero, assetManifest]);
51926
+ const isoUnits = useMemo(() => [...creepUnits, heroUnit], [creepUnits, heroUnit]);
51927
+ const towerFeatures = useMemo(() => towersToFeatures(towers, assetManifest), [towers, assetManifest]);
50690
51928
  const handleTileClick = useCallback((x, y) => {
50691
51929
  if (result !== "none") return;
50692
51930
  if (pathPositions.has(`${x},${y}`)) return;
@@ -50745,7 +51983,9 @@ function TowerDefenseBoard({
50745
51983
  {
50746
51984
  tiles: isoTiles,
50747
51985
  units: isoUnits,
50748
- features: pathFeatures,
51986
+ features: towerFeatures,
51987
+ assetBaseUrl: assetManifest?.baseUrl,
51988
+ assetManifest,
50749
51989
  validMoves,
50750
51990
  hoveredTile,
50751
51991
  onTileClick: handleTileClick,
@@ -50784,7 +52024,7 @@ function TowerDefenseBoard({
50784
52024
  ] }) })
50785
52025
  ] });
50786
52026
  }
50787
- var CDN6, TD_GRID_W, TD_GRID_H, TERRAIN_SPRITES, DEFAULT_TD_PATH, DEFAULT_TD_TILES, TOWER_SPRITE, CREEP_SPRITE, HERO_SPRITE;
52027
+ var TD_GRID_W, TD_GRID_H, DEFAULT_TD_PATH, DEFAULT_TD_TILES;
50788
52028
  var init_TowerDefenseBoard = __esm({
50789
52029
  "components/game/organisms/TowerDefenseBoard.tsx"() {
50790
52030
  "use client";
@@ -50796,16 +52036,8 @@ var init_TowerDefenseBoard = __esm({
50796
52036
  init_Stack();
50797
52037
  init_IsometricCanvas();
50798
52038
  init_boardEntity();
50799
- CDN6 = "https://almadar-kflow-assets.web.app/shared/";
50800
52039
  TD_GRID_W = 16;
50801
52040
  TD_GRID_H = 16;
50802
- TERRAIN_SPRITES = {
50803
- ground: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_01.png`,
50804
- path: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_09.png`,
50805
- wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`,
50806
- grass: `${CDN6}isometric-dungeon/Isometric/dirt_E.png`,
50807
- dirt: `${CDN6}isometric-dungeon/Isometric/dirtTiles_E.png`
50808
- };
50809
52041
  DEFAULT_TD_PATH = [
50810
52042
  { x: 2, y: 0 },
50811
52043
  { x: 2, y: 1 },
@@ -50858,9 +52090,6 @@ var init_TowerDefenseBoard = __esm({
50858
52090
  { x: 13, y: 15 }
50859
52091
  ];
50860
52092
  DEFAULT_TD_TILES = buildDefaultTDTiles();
50861
- TOWER_SPRITE = `${CDN6}sprite-sheets/guardian-sprite-sheet-se.png`;
50862
- CREEP_SPRITE = `${CDN6}sprite-sheets/scrapper-sprite-sheet-se.png`;
50863
- HERO_SPRITE = `${CDN6}sprite-sheets/amir-sprite-sheet-se.png`;
50864
52093
  TowerDefenseBoard.displayName = "TowerDefenseBoard";
50865
52094
  }
50866
52095
  });
@@ -51128,6 +52357,214 @@ var init_UncontrolledBattleBoard = __esm({
51128
52357
  UncontrolledBattleBoard.displayName = "UncontrolledBattleBoard";
51129
52358
  }
51130
52359
  });
52360
+ function resolveManifestUrl6(manifest, relative) {
52361
+ if (relative == null) return void 0;
52362
+ if (/^https?:\/\//.test(relative)) return relative;
52363
+ const base = manifest?.baseUrl;
52364
+ if (base == null) return relative;
52365
+ const cleanBase = base.replace(/\/$/, "");
52366
+ const cleanRel = relative.replace(/^\//, "");
52367
+ return `${cleanBase}/${cleanRel}`;
52368
+ }
52369
+ function VisualNovelBoard({
52370
+ entity,
52371
+ nodes: propNodes,
52372
+ currentNodeId: propCurrentNodeId,
52373
+ assetManifest: propAssetManifest,
52374
+ typewriterSpeed = 30,
52375
+ portraitScale = 1,
52376
+ chooseEvent,
52377
+ advanceEvent,
52378
+ restartEvent,
52379
+ className
52380
+ }) {
52381
+ const board = boardEntity(entity) ?? {};
52382
+ const eventBus = useEventBus();
52383
+ const assetManifest = propAssetManifest ?? board.assetManifest;
52384
+ const entityNodes = useMemo(
52385
+ () => rows(board.nodes).map((r) => ({
52386
+ id: str(r.id),
52387
+ speaker: str(r.speaker),
52388
+ text: str(r.text),
52389
+ backgroundKey: r.backgroundKey == null ? void 0 : str(r.backgroundKey),
52390
+ portraitKey: r.portraitKey == null ? void 0 : str(r.portraitKey),
52391
+ choices: rows(r.choices).map((c) => ({
52392
+ label: str(c.label),
52393
+ nextId: str(c.nextId)
52394
+ }))
52395
+ })),
52396
+ [board.nodes]
52397
+ );
52398
+ const rawNodes = propNodes ?? entityNodes;
52399
+ const nodes = rawNodes.length > 0 ? rawNodes : DEFAULT_NODES;
52400
+ const currentNodeId = propCurrentNodeId ?? (str(board.currentNodeId) || nodes[0]?.id);
52401
+ const currentNode = nodes.find((n) => n.id === currentNodeId) ?? nodes[0];
52402
+ const backgroundImage = useMemo(
52403
+ () => resolveManifestUrl6(assetManifest, assetManifest?.backgrounds?.[currentNode?.backgroundKey ?? ""]),
52404
+ [assetManifest, currentNode?.backgroundKey]
52405
+ );
52406
+ const portraitUrl = useMemo(
52407
+ () => resolveManifestUrl6(assetManifest, assetManifest?.portraits?.[currentNode?.portraitKey ?? ""]),
52408
+ [assetManifest, currentNode?.portraitKey]
52409
+ );
52410
+ const dialogueChoices = useMemo(
52411
+ () => (currentNode?.choices ?? []).map((c) => ({ text: c.label, next: c.nextId })),
52412
+ [currentNode?.choices]
52413
+ );
52414
+ const handleChoice = useCallback(
52415
+ (choice) => {
52416
+ const index = dialogueChoices.findIndex((c) => c.next === choice.next && c.text === choice.text);
52417
+ if (chooseEvent) {
52418
+ eventBus.emit(`UI:${chooseEvent}`, { choiceIndex: index });
52419
+ }
52420
+ },
52421
+ [dialogueChoices, chooseEvent, eventBus]
52422
+ );
52423
+ const handleAdvance = useCallback(() => {
52424
+ if (advanceEvent) {
52425
+ eventBus.emit(`UI:${advanceEvent}`, {});
52426
+ }
52427
+ }, [advanceEvent, eventBus]);
52428
+ const handleRestart = useCallback(() => {
52429
+ if (restartEvent) {
52430
+ eventBus.emit(`UI:${restartEvent}`, {});
52431
+ }
52432
+ }, [restartEvent, eventBus]);
52433
+ if (!currentNode) {
52434
+ return /* @__PURE__ */ jsxs(VStack, { className: cn("visual-novel-board relative min-h-[600px] bg-background items-center justify-center", className), gap: "lg", children: [
52435
+ /* @__PURE__ */ jsx(Typography, { variant: "h3", color: "muted", children: "No dialogue nodes" }),
52436
+ /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleRestart, children: "Restart" })
52437
+ ] });
52438
+ }
52439
+ return /* @__PURE__ */ jsx(Box, { className: cn("visual-novel-board relative min-h-[600px] bg-background overflow-hidden", className), children: /* @__PURE__ */ jsx(
52440
+ DialogueBox,
52441
+ {
52442
+ dialogue: {
52443
+ speaker: currentNode.speaker,
52444
+ text: currentNode.text,
52445
+ choices: dialogueChoices
52446
+ },
52447
+ typewriterSpeed,
52448
+ position: "bottom",
52449
+ backgroundImage,
52450
+ portraitUrl,
52451
+ portraitScale,
52452
+ onChoice: handleChoice,
52453
+ onAdvance: handleAdvance
52454
+ }
52455
+ ) });
52456
+ }
52457
+ var DEFAULT_NODES;
52458
+ var init_VisualNovelBoard = __esm({
52459
+ "components/game/organisms/VisualNovelBoard.tsx"() {
52460
+ "use client";
52461
+ init_cn();
52462
+ init_useEventBus();
52463
+ init_Box();
52464
+ init_Typography();
52465
+ init_Stack();
52466
+ init_Button();
52467
+ init_DialogueBox();
52468
+ init_boardEntity();
52469
+ DEFAULT_NODES = [
52470
+ {
52471
+ id: "start",
52472
+ speaker: "Narrator",
52473
+ text: "The corridor stretches into shadow. A lone sentinel blocks your path.",
52474
+ backgroundKey: "corridor",
52475
+ portraitKey: "guide",
52476
+ choices: [
52477
+ { label: "Approach the sentinel.", nextId: "meet" },
52478
+ { label: "Turn back.", nextId: "retreat" }
52479
+ ]
52480
+ },
52481
+ {
52482
+ id: "meet",
52483
+ speaker: "Sentinel",
52484
+ text: "You carry the old marks. Speak your purpose, traveler.",
52485
+ backgroundKey: "forge",
52486
+ portraitKey: "rival",
52487
+ choices: [
52488
+ { label: "I seek the core.", nextId: "end" },
52489
+ { label: "I am only passing through.", nextId: "retreat" }
52490
+ ]
52491
+ },
52492
+ {
52493
+ id: "retreat",
52494
+ speaker: "Narrator",
52495
+ text: "You withdraw into the dark. The path will wait for braver feet.",
52496
+ backgroundKey: "corridor",
52497
+ choices: [{ label: "Begin again.", nextId: "start" }]
52498
+ },
52499
+ {
52500
+ id: "end",
52501
+ speaker: "Sentinel",
52502
+ text: "Then the way is open. Walk it well.",
52503
+ backgroundKey: "core",
52504
+ portraitKey: "rival",
52505
+ choices: [{ label: "Begin again.", nextId: "start" }]
52506
+ }
52507
+ ];
52508
+ VisualNovelBoard.displayName = "VisualNovelBoard";
52509
+ }
52510
+ });
52511
+ function VisualNovelTemplate({
52512
+ entity,
52513
+ title = "Visual Novel",
52514
+ nodes,
52515
+ currentNodeId,
52516
+ typewriterSpeed,
52517
+ portraitScale,
52518
+ chooseEvent,
52519
+ advanceEvent,
52520
+ restartEvent,
52521
+ className
52522
+ }) {
52523
+ const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
52524
+ return /* @__PURE__ */ jsxs(
52525
+ Box,
52526
+ {
52527
+ display: "flex",
52528
+ fullHeight: true,
52529
+ className: cn("visual-novel-template flex-col", className),
52530
+ children: [
52531
+ /* @__PURE__ */ jsx(
52532
+ HStack,
52533
+ {
52534
+ gap: "sm",
52535
+ align: "center",
52536
+ className: "px-4 py-3 border-b-2 border-border bg-surface shrink-0",
52537
+ children: /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title })
52538
+ }
52539
+ ),
52540
+ /* @__PURE__ */ jsx(Box, { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsx(
52541
+ VisualNovelBoard,
52542
+ {
52543
+ entity: resolved,
52544
+ nodes,
52545
+ currentNodeId,
52546
+ typewriterSpeed,
52547
+ portraitScale,
52548
+ chooseEvent,
52549
+ advanceEvent,
52550
+ restartEvent,
52551
+ className: "h-full"
52552
+ }
52553
+ ) })
52554
+ ]
52555
+ }
52556
+ );
52557
+ }
52558
+ var init_VisualNovelTemplate = __esm({
52559
+ "components/game/templates/VisualNovelTemplate.tsx"() {
52560
+ init_cn();
52561
+ init_Box();
52562
+ init_Stack();
52563
+ init_Typography();
52564
+ init_VisualNovelBoard();
52565
+ VisualNovelTemplate.displayName = "VisualNovelTemplate";
52566
+ }
52567
+ });
51131
52568
  function heroPosition(h) {
51132
52569
  if ("position" in h && h.position != null) return h.position;
51133
52570
  return vec2(h.position);
@@ -51468,48 +52905,48 @@ function WorldMapTemplate({
51468
52905
  }
51469
52906
  );
51470
52907
  }
51471
- var CDN7, DEFAULT_WORLDMAP_TILES, DEFAULT_WORLDMAP_UNITS, DEFAULT_WORLDMAP_FEATURES, DEFAULT_WORLDMAP_MANIFEST;
52908
+ var CDN5, DEFAULT_WORLDMAP_TILES, DEFAULT_WORLDMAP_UNITS, DEFAULT_WORLDMAP_FEATURES, DEFAULT_WORLDMAP_MANIFEST;
51472
52909
  var init_WorldMapTemplate = __esm({
51473
52910
  "components/game/templates/WorldMapTemplate.tsx"() {
51474
52911
  init_WorldMapBoard();
51475
- CDN7 = "https://almadar-kflow-assets.web.app/shared";
52912
+ CDN5 = "https://almadar-kflow-assets.web.app/shared";
51476
52913
  DEFAULT_WORLDMAP_TILES = [
51477
- { x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51478
- { x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51479
- { x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
51480
- { x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51481
- { x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51482
- { x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51483
- { x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51484
- { x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51485
- { x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51486
- { x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
51487
- { x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
51488
- { x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51489
- { x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
51490
- { x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51491
- { x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51492
- { x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51493
- { x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51494
- { x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51495
- { x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
51496
- { x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51497
- { x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51498
- { x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
51499
- { x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51500
- { x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51501
- { x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` }
52914
+ { x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
52915
+ { x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
52916
+ { x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
52917
+ { x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
52918
+ { x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
52919
+ { x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
52920
+ { x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
52921
+ { x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
52922
+ { x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
52923
+ { x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
52924
+ { x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
52925
+ { x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
52926
+ { x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
52927
+ { x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
52928
+ { x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
52929
+ { x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
52930
+ { x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
52931
+ { x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
52932
+ { x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
52933
+ { x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
52934
+ { x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
52935
+ { x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
52936
+ { x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
52937
+ { x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
52938
+ { x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` }
51502
52939
  ];
51503
52940
  DEFAULT_WORLDMAP_UNITS = [
51504
- { id: "h1", position: { x: 1, y: 1 }, unitType: "hero", name: "Amir", team: "player", health: 10, maxHealth: 10, sprite: `${CDN7}/sprite-sheets/amir-sprite-sheet-se.png` },
51505
- { id: "h2", position: { x: 3, y: 3 }, unitType: "scout", name: "Archivist", team: "player", health: 10, maxHealth: 10, sprite: `${CDN7}/sprite-sheets/archivist-sprite-sheet-se.png` }
52941
+ { id: "h1", position: { x: 1, y: 1 }, unitType: "hero", name: "Amir", team: "player", health: 10, maxHealth: 10, sprite: `${CDN5}/sprite-sheets/amir-sprite-sheet-se.png` },
52942
+ { id: "h2", position: { x: 3, y: 3 }, unitType: "scout", name: "Archivist", team: "player", health: 10, maxHealth: 10, sprite: `${CDN5}/sprite-sheets/archivist-sprite-sheet-se.png` }
51506
52943
  ];
51507
52944
  DEFAULT_WORLDMAP_FEATURES = [
51508
- { id: "f1", x: 2, y: 2, type: "capital", sprite: `${CDN7}/scenes/world/capital.png` },
51509
- { id: "f2", x: 4, y: 2, type: "power_node", sprite: `${CDN7}/world-map/power_node.png` }
52945
+ { id: "f1", x: 2, y: 2, type: "capital", sprite: `${CDN5}/scenes/world/capital.png` },
52946
+ { id: "f2", x: 4, y: 2, type: "power_node", sprite: `${CDN5}/world-map/power_node.png` }
51510
52947
  ];
51511
52948
  DEFAULT_WORLDMAP_MANIFEST = {
51512
- baseUrl: CDN7,
52949
+ baseUrl: CDN5,
51513
52950
  terrains: {
51514
52951
  grass: "/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png",
51515
52952
  water: "/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png",
@@ -51539,8 +52976,8 @@ function XPBar({
51539
52976
  }) {
51540
52977
  const sizes = sizeMap18[size];
51541
52978
  const percentage = max > 0 ? Math.max(0, Math.min(100, current / max * 100)) : 0;
51542
- const [fillWidth, setFillWidth] = React76.useState(animated ? 0 : percentage);
51543
- React76.useEffect(() => {
52979
+ const [fillWidth, setFillWidth] = React79.useState(animated ? 0 : percentage);
52980
+ React79.useEffect(() => {
51544
52981
  if (!animated) {
51545
52982
  setFillWidth(percentage);
51546
52983
  return;
@@ -51613,7 +53050,7 @@ var init_XPBar = __esm({
51613
53050
  }
51614
53051
  });
51615
53052
  function lazyThree(name, loader) {
51616
- const Lazy = React76__default.lazy(
53053
+ const Lazy = React79__default.lazy(
51617
53054
  () => loader().then((m) => {
51618
53055
  const Resolved = m[name];
51619
53056
  if (!Resolved) {
@@ -51625,13 +53062,13 @@ function lazyThree(name, loader) {
51625
53062
  })
51626
53063
  );
51627
53064
  function ThreeWrapper(props) {
51628
- return React76__default.createElement(
53065
+ return React79__default.createElement(
51629
53066
  ThreeBoundary,
51630
53067
  { name },
51631
- React76__default.createElement(
51632
- React76__default.Suspense,
53068
+ React79__default.createElement(
53069
+ React79__default.Suspense,
51633
53070
  { fallback: null },
51634
- React76__default.createElement(Lazy, props)
53071
+ React79__default.createElement(Lazy, props)
51635
53072
  )
51636
53073
  );
51637
53074
  }
@@ -51675,7 +53112,10 @@ var init_component_registry_generated = __esm({
51675
53112
  init_CalendarGrid();
51676
53113
  init_CanvasEffect();
51677
53114
  init_Card();
53115
+ init_CardBattlerBoard();
53116
+ init_CardBattlerTemplate();
51678
53117
  init_CardGrid();
53118
+ init_CardHand();
51679
53119
  init_Carousel();
51680
53120
  init_CaseStudyCard();
51681
53121
  init_CaseStudyOrganism();
@@ -51686,6 +53126,8 @@ var init_component_registry_generated = __esm({
51686
53126
  init_ChartLegend();
51687
53127
  init_Checkbox();
51688
53128
  init_ChoiceButton();
53129
+ init_CityBuilderBoard();
53130
+ init_CityBuilderTemplate();
51689
53131
  init_ClassifierBoard();
51690
53132
  init_CodeBlock();
51691
53133
  init_CodeRunnerPanel();
@@ -51906,6 +53348,8 @@ var init_component_registry_generated = __esm({
51906
53348
  init_Toast();
51907
53349
  init_ToastSlot();
51908
53350
  init_Tooltip();
53351
+ init_TopDownShooterBoard();
53352
+ init_TopDownShooterTemplate();
51909
53353
  init_TowerDefenseBoard();
51910
53354
  init_TowerDefenseTemplate();
51911
53355
  init_TraitFrame();
@@ -51921,6 +53365,8 @@ var init_component_registry_generated = __esm({
51921
53365
  init_UploadDropZone();
51922
53366
  init_VersionDiff();
51923
53367
  init_ViolationAlert();
53368
+ init_VisualNovelBoard();
53369
+ init_VisualNovelTemplate();
51924
53370
  init_VoteStack();
51925
53371
  init_WaypointMarker();
51926
53372
  init_WizardContainer();
@@ -51929,7 +53375,7 @@ var init_component_registry_generated = __esm({
51929
53375
  init_WorldMapBoard();
51930
53376
  init_WorldMapTemplate();
51931
53377
  init_XPBar();
51932
- ThreeBoundary = class extends React76__default.Component {
53378
+ ThreeBoundary = class extends React79__default.Component {
51933
53379
  constructor() {
51934
53380
  super(...arguments);
51935
53381
  __publicField(this, "state", { failed: false });
@@ -51939,7 +53385,7 @@ var init_component_registry_generated = __esm({
51939
53385
  }
51940
53386
  render() {
51941
53387
  if (this.state.failed) {
51942
- return React76__default.createElement(
53388
+ return React79__default.createElement(
51943
53389
  "div",
51944
53390
  {
51945
53391
  "data-testid": "three-unavailable",
@@ -51995,7 +53441,10 @@ var init_component_registry_generated = __esm({
51995
53441
  "CalendarGrid": CalendarGrid,
51996
53442
  "CanvasEffect": CanvasEffect,
51997
53443
  "Card": Card,
53444
+ "CardBattlerBoard": CardBattlerBoard,
53445
+ "CardBattlerTemplate": CardBattlerTemplate,
51998
53446
  "CardGrid": CardGrid,
53447
+ "CardHand": CardHand,
51999
53448
  "Carousel": Carousel,
52000
53449
  "CaseStudyCard": CaseStudyCard,
52001
53450
  "CaseStudyOrganism": CaseStudyOrganism,
@@ -52006,6 +53455,8 @@ var init_component_registry_generated = __esm({
52006
53455
  "ChartLegend": ChartLegend,
52007
53456
  "Checkbox": Checkbox,
52008
53457
  "ChoiceButton": ChoiceButton,
53458
+ "CityBuilderBoard": CityBuilderBoard,
53459
+ "CityBuilderTemplate": CityBuilderTemplate,
52009
53460
  "ClassifierBoard": ClassifierBoard,
52010
53461
  "CodeBlock": CodeBlock,
52011
53462
  "CodeRunnerPanel": CodeRunnerPanel,
@@ -52238,6 +53689,8 @@ var init_component_registry_generated = __esm({
52238
53689
  "Toast": Toast,
52239
53690
  "ToastSlot": ToastSlot,
52240
53691
  "Tooltip": Tooltip,
53692
+ "TopDownShooterBoard": TopDownShooterBoard,
53693
+ "TopDownShooterTemplate": TopDownShooterTemplate,
52241
53694
  "TowerDefenseBoard": TowerDefenseBoard,
52242
53695
  "TowerDefenseTemplate": TowerDefenseTemplate,
52243
53696
  "TraitFrame": TraitFrame,
@@ -52254,6 +53707,8 @@ var init_component_registry_generated = __esm({
52254
53707
  "VStack": VStack,
52255
53708
  "VersionDiff": VersionDiff,
52256
53709
  "ViolationAlert": ViolationAlert,
53710
+ "VisualNovelBoard": VisualNovelBoard,
53711
+ "VisualNovelTemplate": VisualNovelTemplate,
52257
53712
  "VoteStack": VoteStack,
52258
53713
  "WaypointMarker": WaypointMarker,
52259
53714
  "WizardContainer": WizardContainer,
@@ -52280,7 +53735,7 @@ function SuspenseConfigProvider({
52280
53735
  config,
52281
53736
  children
52282
53737
  }) {
52283
- return React76__default.createElement(
53738
+ return React79__default.createElement(
52284
53739
  SuspenseConfigContext.Provider,
52285
53740
  { value: config },
52286
53741
  children
@@ -52770,7 +54225,7 @@ function renderPatternChildren(children, onDismiss, parentId = "root", parentPat
52770
54225
  const key = `${parentId}-${index}-trait:${traitName}`;
52771
54226
  return /* @__PURE__ */ jsx(TraitFrame, { traitName }, key);
52772
54227
  }
52773
- return /* @__PURE__ */ jsx(React76__default.Fragment, { children: child }, `${parentId}-${index}`);
54228
+ return /* @__PURE__ */ jsx(React79__default.Fragment, { children: child }, `${parentId}-${index}`);
52774
54229
  }
52775
54230
  if (!child || typeof child !== "object") return null;
52776
54231
  const childId = `${parentId}-${index}`;
@@ -52810,14 +54265,14 @@ function isPatternConfig(value) {
52810
54265
  if (value === null || value === void 0) return false;
52811
54266
  if (typeof value !== "object") return false;
52812
54267
  if (Array.isArray(value)) return false;
52813
- if (React76__default.isValidElement(value)) return false;
54268
+ if (React79__default.isValidElement(value)) return false;
52814
54269
  if (value instanceof Date) return false;
52815
54270
  if (typeof value === "function") return false;
52816
54271
  const record = value;
52817
- return "type" in record && typeof record.type === "string" && isKnownPattern(record.type);
54272
+ return "type" in record && typeof record.type === "string" && getComponentForPattern$1(record.type) !== null;
52818
54273
  }
52819
54274
  function isPlainConfigObject(value) {
52820
- if (React76__default.isValidElement(value)) return false;
54275
+ if (React79__default.isValidElement(value)) return false;
52821
54276
  if (value instanceof Date) return false;
52822
54277
  const proto = Object.getPrototypeOf(value);
52823
54278
  return proto === Object.prototype || proto === null;
@@ -52938,6 +54393,22 @@ function SlotContentRenderer({
52938
54393
  const childrenIsRenderFn = typeof incomingChildren === "function";
52939
54394
  const { children: _childrenConfig, ...restPropsNoChildren } = content.props;
52940
54395
  const restProps = childrenIsRenderFn ? { ...restPropsNoChildren, children: incomingChildren } : restPropsNoChildren;
54396
+ const nodeSlotOverrides = {};
54397
+ for (const slotKey of CONTENT_NODE_SLOTS) {
54398
+ const slotVal = restProps[slotKey];
54399
+ if (slotVal === void 0 || slotVal === null) continue;
54400
+ if (React79__default.isValidElement(slotVal) || typeof slotVal === "string" || typeof slotVal === "number" || typeof slotVal === "boolean") continue;
54401
+ if (Array.isArray(slotVal) || typeof slotVal === "object" && "type" in slotVal) {
54402
+ nodeSlotOverrides[slotKey] = renderPatternChildren(
54403
+ slotVal,
54404
+ onDismiss,
54405
+ `${content.id}-${slotKey}`,
54406
+ `${myPath}.${slotKey}`,
54407
+ content.sourceTrait,
54408
+ { slot: content.slot, transitionEvent: content.transitionEvent, fromState: content.fromState, entity: content.entity }
54409
+ );
54410
+ }
54411
+ }
52941
54412
  const renderedProps = renderPatternProps(restProps, onDismiss);
52942
54413
  const patternDef = getPatternDefinition$1(content.pattern);
52943
54414
  const propsSchema = patternDef?.propsSchema;
@@ -52968,6 +54439,9 @@ function SlotContentRenderer({
52968
54439
  }
52969
54440
  }
52970
54441
  const finalProps = renderedProps;
54442
+ for (const [k, v] of Object.entries(nodeSlotOverrides)) {
54443
+ finalProps[k] = v;
54444
+ }
52971
54445
  const resolvedItems = Array.isArray(
52972
54446
  finalProps.entity
52973
54447
  ) ? finalProps.entity : null;
@@ -53088,7 +54562,7 @@ function UISlotRenderer({
53088
54562
  }
53089
54563
  return wrapped;
53090
54564
  }
53091
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, FORM_PATTERNS, SELF_OVERLAY_PATTERNS, PATTERNS_WITH_CHILDREN;
54565
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, FORM_PATTERNS, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
53092
54566
  var init_UISlotRenderer = __esm({
53093
54567
  "components/core/organisms/UISlotRenderer.tsx"() {
53094
54568
  "use client";
@@ -53125,6 +54599,16 @@ var init_UISlotRenderer = __esm({
53125
54599
  "wizard-step"
53126
54600
  ]);
53127
54601
  SELF_OVERLAY_PATTERNS = /* @__PURE__ */ new Set(["modal", "confirm-dialog"]);
54602
+ CONTENT_NODE_SLOTS = /* @__PURE__ */ new Set([
54603
+ "logo",
54604
+ "master",
54605
+ "detail",
54606
+ "trigger",
54607
+ "content",
54608
+ "addons",
54609
+ "hud",
54610
+ "fallback"
54611
+ ]);
53128
54612
  PATTERNS_WITH_CHILDREN = /* @__PURE__ */ new Set([
53129
54613
  "stack",
53130
54614
  "vstack",
@@ -53701,7 +55185,11 @@ init_CastleBoard();
53701
55185
  init_PlatformerBoard();
53702
55186
  init_TowerDefenseBoard();
53703
55187
  init_RoguelikeBoard();
55188
+ init_TopDownShooterBoard();
55189
+ init_CityBuilderBoard();
53704
55190
  init_GameBoard3D();
55191
+ init_VisualNovelBoard();
55192
+ init_CardBattlerBoard();
53705
55193
  init_TraitStateViewer();
53706
55194
  init_TraitSlot();
53707
55195
 
@@ -54277,6 +55765,10 @@ init_WorldMapTemplate();
54277
55765
  init_PlatformerTemplate();
54278
55766
  init_TowerDefenseTemplate();
54279
55767
  init_RoguelikeTemplate();
55768
+ init_TopDownShooterTemplate();
55769
+ init_CityBuilderTemplate();
55770
+ init_VisualNovelTemplate();
55771
+ init_CardBattlerTemplate();
54280
55772
  init_LandingPageTemplate();
54281
55773
  init_PricingPageTemplate();
54282
55774
  init_FeatureDetailPageTemplate();
@@ -54285,4 +55777,4 @@ init_AboutPageTemplate();
54285
55777
  // components/index.ts
54286
55778
  init_cn();
54287
55779
 
54288
- export { ALL_PRESETS, AR_BOOK_FIELDS, AboutPageTemplate, Accordion, ActionButton, ActionButtons, Card2 as ActionCard, ActionPalette, ActionTile, ActivationBlock, Alert, AnimatedCounter, AnimatedGraphic, AnimatedReveal, ArticleSection, Aside, AssetPicker, AuthLayout, Avatar, Badge, BattleBoard, BattleTemplate, BehaviorView, BloomQuizBlock, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, Box, BranchingLogicBuilder, Breadcrumb, BuilderBoard, Button, ButtonGroup, CTABanner, CalendarGrid, CanvasEffect, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, CaseStudyCard, CaseStudyOrganism, CastleBoard, CastleTemplate, Center, Chart, ChartLegend, Checkbox, ChoiceButton, ClassifierBoard, Coachmark, CodeBlock, CodeRunnerPanel, CollapsibleSection, CombatLog, ComboCounter, CommunityLinks, ConditionalWrapper, ConfettiEffect, ConfirmDialog, ConnectionBlock, Container, ContentRenderer, ContentSection, ControlButton, CounterTemplate, CraftingRecipe, DEFAULT_LIKERT_OPTIONS, DEFAULT_MATRIX_COLUMNS, DIAMOND_TOP_Y, DPad, DamageNumber, DashboardGrid, DashboardLayout, DataGrid, DataList, DataTable, DateRangePicker, DateRangeSelector, DayCell, DebuggerBoard, DetailPanel, Dialog, DialogueBox, DialogueBubble, Divider, DocBreadcrumb, DocPagination, DocSearch, DocSidebar, DocTOC, DocumentViewer, StateMachineView as DomStateMachineVisualizer, Drawer, DrawerSlot, EdgeDecoration, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, EmptyState, EnemyPlate, EntityDisplayEvents, ErrorBoundary, ErrorState, EventHandlerBoard, EventLog, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FeatureCard, FeatureDetailPageTemplate, FeatureGrid, FeatureGridOrganism, FileTree, FilterGroup, FilterPill, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormActions, FormField, FormLayout, FormSection, FormSectionHeader, GameAudioContext, GameAudioProvider, GameAudioToggle, GameBoard3D, GameCanvas2D, GameHud, GameMenu, GameOverScreen, GameShell, GameTemplate, GenericAppTemplate, GeometricPattern, GradientDivider, GraphCanvas, GraphView, Grid, GridPicker, HStack, Header, HealthBar, HealthPanel, HeroOrganism, HeroSection, IDENTITY_BOOK_FIELDS, Icon, IconPicker, InfiniteScrollSentinel, Input, InputGroup, InstallBox, InventoryGrid, InventoryPanel, IsometricCanvas, ItemSlot, JazariStateMachine, JsonTreeEditor, Label, LandingPageTemplate, LawReferenceTooltip, Lightbox, LikertScale, LineChart2 as LineChart, List3 as List, LoadingState, MapView, MarkdownContent, MarketingFooter, MarketingStatCard, MasterDetail, MasterDetailLayout, MatrixQuestion, MediaGallery, Menu, Meter, MiniMap, Modal, ModalSlot, ModuleCard, Navigation, NegotiatorBoard, NodeSlotEditor, NotifyListener, NumberStepper, ObjectRulePanel, OnboardingSpotlight, OptionConstraintGroup, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, Overlay, PageHeader, Pagination, PatternTile, PhysicsManager, PlatformerBoard, PlatformerCanvas, PlatformerTemplate, Popover, PositionedCanvas, PowerupSlots, PricingCard, PricingGrid, PricingOrganism, PricingPageTemplate, ProgressBar, ProgressDots, PropertyInspector, PullQuote, PullToRefresh, QrScanner, QuestTracker, QuizBlock, Radio, RangeSlider, ReflectionBlock, RelationSelect, RepeatableFormSection, ReplyTree, ResourceBar, ResourceCounter, RichBlockEditor, RoguelikeBoard, RoguelikeTemplate, RuleEditor, RuntimeDebugger, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, ScaledDiagram, ScoreBoard, ScoreDisplay, SearchInput, Section, SectionHeader, SegmentRenderer, Select, SequenceBar, SequencerBoard, ServiceCatalog, ShowcaseCard, ShowcaseOrganism, SidePanel, Sidebar, SignaturePad, SimpleGrid, SimulationCanvas, SimulationControls, SimulationGraph, SimulatorBoard, Skeleton, SlotContentRenderer, SocialProof, SortableList, Spacer, Sparkline, Spinner, Split, SplitPane, SplitSection, Sprite, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateArchitectBoard, StateIndicator, StateJsonView, StateMachineView, StateNode2 as StateNode, StatsGrid, StatsOrganism, StatusBar, StatusDot, StatusEffect, StepFlow, StepFlowOrganism, SvgBranch, SvgConnection, SvgFlow, SvgGrid, SvgLobe, SvgMesh, SvgMorph, SvgNode, SvgPulse, SvgRing, SvgShield, SvgStack, SwipeableRow, Switch, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, TableView, Tabs, TagCloud, TagInput, TeamCard, TeamOrganism, TerrainPalette, TextHighlight, Textarea, ThemeSelector, ThemeToggle, TimeSlotCell, Timeline, TimerDisplay, Toast, ToastSlot, Tooltip, TowerDefenseBoard, TowerDefenseTemplate, TraitFrame, TraitSlot, TraitStateViewer, TransitionArrow, TrendIndicator, TurnIndicator, TurnPanel, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UncontrolledBattleBoard, UnitCommandBar, UploadDropZone, VStack, VariablePanel, VersionDiff, ViolationAlert, VoteStack, WaypointMarker, WizardContainer, WizardNavigation, WizardProgress, WorldMapBoard, WorldMapTemplate, XPBar, applyTemporaryEffect, calculateAttackTargets, calculateDamage, calculateValidMoves, cn, combatAnimations, combatClasses, combatEffects, createInitialGameState, createUnitAnimationState, drawSprite, generateCombatMessage, getCurrentFrame, getTileDimensions, inferDirection, isoToScreen, mapBookData, parseLessonSegments, parseMarkdownWithCodeBlocks, pendulum, projectileMotion, resolveFieldMap, resolveFrame, resolveSheetDirection, screenToIso, springOscillator, tickAnimationState, toCodeLanguage, transitionAnimation, useAnchorRect, useBattleState, useCamera, useGameAudio, useGameAudioContext, useImageCache, usePhysics2D, useSpriteAnimations };
55780
+ export { ALL_PRESETS, AR_BOOK_FIELDS, AboutPageTemplate, Accordion, ActionButton, ActionButtons, Card2 as ActionCard, ActionPalette, ActionTile, ActivationBlock, Alert, AnimatedCounter, AnimatedGraphic, AnimatedReveal, ArticleSection, Aside, AssetPicker, AuthLayout, Avatar, Badge, BattleBoard, BattleTemplate, BehaviorView, BloomQuizBlock, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, Box, BranchingLogicBuilder, Breadcrumb, BuilderBoard, Button, ButtonGroup, CTABanner, CalendarGrid, CanvasEffect, Card, CardBattlerBoard, CardBattlerTemplate, CardBody, CardContent, CardFooter, CardGrid, CardHand, CardHeader, CardTitle, Carousel, CaseStudyCard, CaseStudyOrganism, CastleBoard, CastleTemplate, Center, Chart, ChartLegend, Checkbox, ChoiceButton, CityBuilderBoard, CityBuilderTemplate, ClassifierBoard, Coachmark, CodeBlock, CodeRunnerPanel, CollapsibleSection, CombatLog, ComboCounter, CommunityLinks, ConditionalWrapper, ConfettiEffect, ConfirmDialog, ConnectionBlock, Container, ContentRenderer, ContentSection, ControlButton, CounterTemplate, CraftingRecipe, DEFAULT_LIKERT_OPTIONS, DEFAULT_MATRIX_COLUMNS, DIAMOND_TOP_Y, DPad, DamageNumber, DashboardGrid, DashboardLayout, DataGrid, DataList, DataTable, DateRangePicker, DateRangeSelector, DayCell, DebuggerBoard, DetailPanel, Dialog, DialogueBox, DialogueBubble, Divider, DocBreadcrumb, DocPagination, DocSearch, DocSidebar, DocTOC, DocumentViewer, StateMachineView as DomStateMachineVisualizer, Drawer, DrawerSlot, EdgeDecoration, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, EmptyState, EnemyPlate, EntityDisplayEvents, ErrorBoundary, ErrorState, EventHandlerBoard, EventLog, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FeatureCard, FeatureDetailPageTemplate, FeatureGrid, FeatureGridOrganism, FileTree, FilterGroup, FilterPill, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormActions, FormField, FormLayout, FormSection, FormSectionHeader, GameAudioContext, GameAudioProvider, GameAudioToggle, GameBoard3D, GameCanvas2D, GameHud, GameMenu, GameOverScreen, GameShell, GameTemplate, GenericAppTemplate, GeometricPattern, GradientDivider, GraphCanvas, GraphView, Grid, GridPicker, HStack, Header, HealthBar, HealthPanel, HeroOrganism, HeroSection, IDENTITY_BOOK_FIELDS, Icon, IconPicker, InfiniteScrollSentinel, Input, InputGroup, InstallBox, InventoryGrid, InventoryPanel, IsometricCanvas, ItemSlot, JazariStateMachine, JsonTreeEditor, Label, LandingPageTemplate, LawReferenceTooltip, Lightbox, LikertScale, LineChart2 as LineChart, List3 as List, LoadingState, MapView, MarkdownContent, MarketingFooter, MarketingStatCard, MasterDetail, MasterDetailLayout, MatrixQuestion, MediaGallery, Menu, Meter, MiniMap, Modal, ModalSlot, ModuleCard, Navigation, NegotiatorBoard, NodeSlotEditor, NotifyListener, NumberStepper, ObjectRulePanel, OnboardingSpotlight, OptionConstraintGroup, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, Overlay, PageHeader, Pagination, PatternTile, PhysicsManager, PlatformerBoard, PlatformerCanvas, PlatformerTemplate, Popover, PositionedCanvas, PowerupSlots, PricingCard, PricingGrid, PricingOrganism, PricingPageTemplate, ProgressBar, ProgressDots, PropertyInspector, PullQuote, PullToRefresh, QrScanner, QuestTracker, QuizBlock, Radio, RangeSlider, ReflectionBlock, RelationSelect, RepeatableFormSection, ReplyTree, ResourceBar, ResourceCounter, RichBlockEditor, RoguelikeBoard, RoguelikeTemplate, RuleEditor, RuntimeDebugger, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, ScaledDiagram, ScoreBoard, ScoreDisplay, SearchInput, Section, SectionHeader, SegmentRenderer, Select, SequenceBar, SequencerBoard, ServiceCatalog, ShowcaseCard, ShowcaseOrganism, SidePanel, Sidebar, SignaturePad, SimpleGrid, SimulationCanvas, SimulationControls, SimulationGraph, SimulatorBoard, Skeleton, SlotContentRenderer, SocialProof, SortableList, Spacer, Sparkline, Spinner, Split, SplitPane, SplitSection, Sprite, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateArchitectBoard, StateIndicator, StateJsonView, StateMachineView, StateNode2 as StateNode, StatsGrid, StatsOrganism, StatusBar, StatusDot, StatusEffect, StepFlow, StepFlowOrganism, SvgBranch, SvgConnection, SvgFlow, SvgGrid, SvgLobe, SvgMesh, SvgMorph, SvgNode, SvgPulse, SvgRing, SvgShield, SvgStack, SwipeableRow, Switch, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, TableView, Tabs, TagCloud, TagInput, TeamCard, TeamOrganism, TerrainPalette, TextHighlight, Textarea, ThemeSelector, ThemeToggle, TimeSlotCell, Timeline, TimerDisplay, Toast, ToastSlot, Tooltip, TopDownShooterBoard, TopDownShooterTemplate, TowerDefenseBoard, TowerDefenseTemplate, TraitFrame, TraitSlot, TraitStateViewer, TransitionArrow, TrendIndicator, TurnIndicator, TurnPanel, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UncontrolledBattleBoard, UnitCommandBar, UploadDropZone, VStack, VariablePanel, VersionDiff, ViolationAlert, VisualNovelBoard, VisualNovelTemplate, VoteStack, WaypointMarker, WizardContainer, WizardNavigation, WizardProgress, WorldMapBoard, WorldMapTemplate, XPBar, applyTemporaryEffect, calculateAttackTargets, calculateDamage, calculateValidMoves, cn, combatAnimations, combatClasses, combatEffects, createInitialGameState, createUnitAnimationState, drawSprite, generateCombatMessage, getCurrentFrame, getTileDimensions, inferDirection, isoToScreen, mapBookData, parseLessonSegments, parseMarkdownWithCodeBlocks, pendulum, projectileMotion, resolveFieldMap, resolveFrame, resolveSheetDirection, screenToIso, springOscillator, tickAnimationState, toCodeLanguage, transitionAnimation, useAnchorRect, useBattleState, useCamera, useGameAudio, useGameAudioContext, useImageCache, usePhysics2D, useSpriteAnimations };