@almadar/ui 5.61.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 +3127 -1681
  2. package/dist/avl/index.js +1948 -502
  3. package/dist/components/core/molecules/GraphCanvas.d.ts +4 -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 +219 -115
  9. package/dist/components/game/molecules/three/index.js +219 -115
  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 +2993 -1529
  27. package/dist/components/index.js +1961 -506
  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 +2936 -1490
  35. package/dist/providers/index.js +1924 -478
  36. package/dist/runtime/index.cjs +2992 -1546
  37. package/dist/runtime/index.js +1928 -482
  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,5 +1,5 @@
1
- import * as React81 from 'react';
2
- import React81__default, { createContext, useMemo, useContext, useRef, useEffect, useCallback, Suspense, useState, useSyncExternalStore, useLayoutEffect, lazy, useId, forwardRef, useImperativeHandle, Component } from 'react';
1
+ import * as React84 from 'react';
2
+ import React84__default, { createContext, useMemo, useContext, useRef, useEffect, useCallback, Suspense, useState, useSyncExternalStore, useLayoutEffect, lazy, useId, forwardRef, useImperativeHandle, Component } from 'react';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
  import { clsx } from 'clsx';
5
5
  import { twMerge } from 'tailwind-merge';
@@ -396,7 +396,7 @@ var init_Box = __esm({
396
396
  fixed: "fixed",
397
397
  sticky: "sticky"
398
398
  };
399
- Box = React81__default.forwardRef(
399
+ Box = React84__default.forwardRef(
400
400
  ({
401
401
  padding,
402
402
  paddingX,
@@ -446,7 +446,7 @@ var init_Box = __esm({
446
446
  onMouseLeave?.(e);
447
447
  }, [hoverEvent, eventBus, onMouseLeave]);
448
448
  const isClickable = action || onClick;
449
- return React81__default.createElement(
449
+ return React84__default.createElement(
450
450
  Component2,
451
451
  {
452
452
  ref,
@@ -541,7 +541,7 @@ function loadLib(key, importer) {
541
541
  return p2;
542
542
  }
543
543
  function lazyFamilyIcon(libKey, importer, pick, fallbackName, family) {
544
- const Lazy = React81__default.lazy(async () => {
544
+ const Lazy = React84__default.lazy(async () => {
545
545
  const lib = await loadLib(libKey, importer);
546
546
  const Comp = pick(lib);
547
547
  if (!Comp) {
@@ -551,7 +551,7 @@ function lazyFamilyIcon(libKey, importer, pick, fallbackName, family) {
551
551
  return { default: Comp };
552
552
  });
553
553
  const Wrapped = (props) => /* @__PURE__ */ jsx(
554
- React81__default.Suspense,
554
+ React84__default.Suspense,
555
555
  {
556
556
  fallback: /* @__PURE__ */ jsx(
557
557
  "span",
@@ -1277,7 +1277,7 @@ var init_Icon = __esm({
1277
1277
  const directIcon = typeof icon === "string" ? void 0 : icon;
1278
1278
  const effectiveName = typeof icon === "string" ? icon : name;
1279
1279
  const family = useIconFamily();
1280
- const RenderedComponent = React81__default.useMemo(() => {
1280
+ const RenderedComponent = React84__default.useMemo(() => {
1281
1281
  if (directIcon) return null;
1282
1282
  return effectiveName ? resolveIconForFamily(effectiveName, family) : null;
1283
1283
  }, [directIcon, effectiveName, family]);
@@ -1336,7 +1336,7 @@ function resolveIconProp(value, sizeClass) {
1336
1336
  const IconComp = value;
1337
1337
  return /* @__PURE__ */ jsx(IconComp, { className: sizeClass });
1338
1338
  }
1339
- if (React81__default.isValidElement(value)) {
1339
+ if (React84__default.isValidElement(value)) {
1340
1340
  return value;
1341
1341
  }
1342
1342
  if (typeof value === "object" && value !== null && "render" in value) {
@@ -1412,7 +1412,7 @@ var init_Button = __esm({
1412
1412
  md: "h-icon-default w-icon-default",
1413
1413
  lg: "h-icon-default w-icon-default"
1414
1414
  };
1415
- Button = React81__default.forwardRef(
1415
+ Button = React84__default.forwardRef(
1416
1416
  ({
1417
1417
  className,
1418
1418
  variant = "primary",
@@ -1477,7 +1477,7 @@ var Dialog;
1477
1477
  var init_Dialog = __esm({
1478
1478
  "components/core/atoms/Dialog.tsx"() {
1479
1479
  init_cn();
1480
- Dialog = React81__default.forwardRef(
1480
+ Dialog = React84__default.forwardRef(
1481
1481
  ({
1482
1482
  role = "dialog",
1483
1483
  "aria-modal": ariaModal = true,
@@ -2091,7 +2091,7 @@ var init_Badge = __esm({
2091
2091
  md: "px-2.5 py-1 text-sm",
2092
2092
  lg: "px-3 py-1.5 text-base"
2093
2093
  };
2094
- Badge = React81__default.forwardRef(
2094
+ Badge = React84__default.forwardRef(
2095
2095
  ({ className, variant = "default", size = "sm", amount, label, icon, children, onRemove, removeLabel, ...props }, ref) => {
2096
2096
  const iconSizes3 = {
2097
2097
  sm: "h-icon-default w-icon-default",
@@ -2427,7 +2427,7 @@ var init_SvgFlow = __esm({
2427
2427
  width = 100,
2428
2428
  height = 100
2429
2429
  }) => {
2430
- const markerId = React81__default.useMemo(() => {
2430
+ const markerId = React84__default.useMemo(() => {
2431
2431
  flowIdCounter += 1;
2432
2432
  return `almadar-flow-arrow-${flowIdCounter}`;
2433
2433
  }, []);
@@ -3020,7 +3020,7 @@ var init_SvgRing = __esm({
3020
3020
  width = 100,
3021
3021
  height = 100
3022
3022
  }) => {
3023
- const gradientId = React81__default.useMemo(() => {
3023
+ const gradientId = React84__default.useMemo(() => {
3024
3024
  ringIdCounter += 1;
3025
3025
  return `almadar-ring-glow-${ringIdCounter}`;
3026
3026
  }, []);
@@ -3201,7 +3201,7 @@ var init_Input = __esm({
3201
3201
  init_cn();
3202
3202
  init_Icon();
3203
3203
  init_useEventBus();
3204
- Input = React81__default.forwardRef(
3204
+ Input = React84__default.forwardRef(
3205
3205
  ({
3206
3206
  className,
3207
3207
  inputType,
@@ -3361,7 +3361,7 @@ var Label;
3361
3361
  var init_Label = __esm({
3362
3362
  "components/core/atoms/Label.tsx"() {
3363
3363
  init_cn();
3364
- Label = React81__default.forwardRef(
3364
+ Label = React84__default.forwardRef(
3365
3365
  ({ className, required, children, ...props }, ref) => {
3366
3366
  return /* @__PURE__ */ jsxs(
3367
3367
  "label",
@@ -3388,7 +3388,7 @@ var init_Textarea = __esm({
3388
3388
  "components/core/atoms/Textarea.tsx"() {
3389
3389
  init_cn();
3390
3390
  init_useEventBus();
3391
- Textarea = React81__default.forwardRef(
3391
+ Textarea = React84__default.forwardRef(
3392
3392
  ({ className, error, onChange, ...props }, ref) => {
3393
3393
  const eventBus = useEventBus();
3394
3394
  const handleChange = (e) => {
@@ -3627,7 +3627,7 @@ var init_Select = __esm({
3627
3627
  init_cn();
3628
3628
  init_Icon();
3629
3629
  init_useEventBus();
3630
- Select = React81__default.forwardRef(
3630
+ Select = React84__default.forwardRef(
3631
3631
  (props, _ref) => {
3632
3632
  const { multiple, searchable, clearable } = props;
3633
3633
  if (multiple || searchable || clearable) {
@@ -3644,7 +3644,7 @@ var init_Checkbox = __esm({
3644
3644
  "components/core/atoms/Checkbox.tsx"() {
3645
3645
  init_cn();
3646
3646
  init_useEventBus();
3647
- Checkbox = React81__default.forwardRef(
3647
+ Checkbox = React84__default.forwardRef(
3648
3648
  ({ className, label, id, onChange, ...props }, ref) => {
3649
3649
  const inputId = id || `checkbox-${Math.random().toString(36).substr(2, 9)}`;
3650
3650
  const eventBus = useEventBus();
@@ -3698,7 +3698,7 @@ var init_Spinner = __esm({
3698
3698
  md: "h-6 w-6",
3699
3699
  lg: "h-8 w-8"
3700
3700
  };
3701
- Spinner = React81__default.forwardRef(
3701
+ Spinner = React84__default.forwardRef(
3702
3702
  ({ className, size = "md", overlay, ...props }, ref) => {
3703
3703
  if (overlay) {
3704
3704
  return /* @__PURE__ */ jsx(
@@ -3788,7 +3788,7 @@ var init_Card = __esm({
3788
3788
  chip: "shadow-none rounded-pill border-[length:var(--border-width)] border-border",
3789
3789
  "tile-image-first": "p-0 overflow-hidden"
3790
3790
  };
3791
- Card = React81__default.forwardRef(
3791
+ Card = React84__default.forwardRef(
3792
3792
  ({
3793
3793
  className,
3794
3794
  variant = "bordered",
@@ -3836,9 +3836,9 @@ var init_Card = __esm({
3836
3836
  }
3837
3837
  );
3838
3838
  Card.displayName = "Card";
3839
- CardHeader = React81__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("mb-4", className), ...props }));
3839
+ CardHeader = React84__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("mb-4", className), ...props }));
3840
3840
  CardHeader.displayName = "CardHeader";
3841
- CardTitle = React81__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3841
+ CardTitle = React84__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3842
3842
  "h3",
3843
3843
  {
3844
3844
  ref,
@@ -3851,11 +3851,11 @@ var init_Card = __esm({
3851
3851
  }
3852
3852
  ));
3853
3853
  CardTitle.displayName = "CardTitle";
3854
- CardContent = React81__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("", className), ...props }));
3854
+ CardContent = React84__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("", className), ...props }));
3855
3855
  CardContent.displayName = "CardContent";
3856
3856
  CardBody = CardContent;
3857
3857
  CardBody.displayName = "CardBody";
3858
- CardFooter = React81__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3858
+ CardFooter = React84__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3859
3859
  "div",
3860
3860
  {
3861
3861
  ref,
@@ -3910,7 +3910,7 @@ var init_FilterPill = __esm({
3910
3910
  md: "w-3.5 h-3.5",
3911
3911
  lg: "w-4 h-4"
3912
3912
  };
3913
- FilterPill = React81__default.forwardRef(
3913
+ FilterPill = React84__default.forwardRef(
3914
3914
  ({
3915
3915
  className,
3916
3916
  variant = "default",
@@ -4039,8 +4039,8 @@ var init_Avatar = __esm({
4039
4039
  actionPayload
4040
4040
  }) => {
4041
4041
  const eventBus = useEventBus();
4042
- const [imgFailed, setImgFailed] = React81__default.useState(false);
4043
- React81__default.useEffect(() => {
4042
+ const [imgFailed, setImgFailed] = React84__default.useState(false);
4043
+ React84__default.useEffect(() => {
4044
4044
  setImgFailed(false);
4045
4045
  }, [src]);
4046
4046
  const initials = providedInitials ?? (name ? generateInitials(name) : void 0);
@@ -4425,7 +4425,7 @@ var init_Radio = __esm({
4425
4425
  md: "w-2.5 h-2.5",
4426
4426
  lg: "w-3 h-3"
4427
4427
  };
4428
- Radio = React81__default.forwardRef(
4428
+ Radio = React84__default.forwardRef(
4429
4429
  ({
4430
4430
  label,
4431
4431
  helperText,
@@ -4442,12 +4442,12 @@ var init_Radio = __esm({
4442
4442
  onChange,
4443
4443
  ...props
4444
4444
  }, ref) => {
4445
- const reactId = React81__default.useId();
4445
+ const reactId = React84__default.useId();
4446
4446
  const baseId = id || `radio-${reactId}`;
4447
4447
  const hasError = !!error;
4448
4448
  const eventBus = useEventBus();
4449
- const [selected, setSelected] = React81__default.useState(value);
4450
- React81__default.useEffect(() => {
4449
+ const [selected, setSelected] = React84__default.useState(value);
4450
+ React84__default.useEffect(() => {
4451
4451
  if (value !== void 0) setSelected(value);
4452
4452
  }, [value]);
4453
4453
  const pick = (next, e) => {
@@ -4629,7 +4629,7 @@ var init_Switch = __esm({
4629
4629
  "components/core/atoms/Switch.tsx"() {
4630
4630
  "use client";
4631
4631
  init_cn();
4632
- Switch = React81.forwardRef(
4632
+ Switch = React84.forwardRef(
4633
4633
  ({
4634
4634
  checked,
4635
4635
  defaultChecked = false,
@@ -4640,10 +4640,10 @@ var init_Switch = __esm({
4640
4640
  name,
4641
4641
  className
4642
4642
  }, ref) => {
4643
- const [isChecked, setIsChecked] = React81.useState(
4643
+ const [isChecked, setIsChecked] = React84.useState(
4644
4644
  checked !== void 0 ? checked : defaultChecked
4645
4645
  );
4646
- React81.useEffect(() => {
4646
+ React84.useEffect(() => {
4647
4647
  if (checked !== void 0) {
4648
4648
  setIsChecked(checked);
4649
4649
  }
@@ -4998,7 +4998,7 @@ var Aside;
4998
4998
  var init_Aside = __esm({
4999
4999
  "components/core/atoms/Aside.tsx"() {
5000
5000
  init_cn();
5001
- Aside = React81__default.forwardRef(
5001
+ Aside = React84__default.forwardRef(
5002
5002
  ({ className, children, ...rest }, ref) => /* @__PURE__ */ jsx("aside", { ref, className: cn(className), ...rest, children })
5003
5003
  );
5004
5004
  Aside.displayName = "Aside";
@@ -5076,8 +5076,8 @@ var init_LawReferenceTooltip = __esm({
5076
5076
  className
5077
5077
  }) => {
5078
5078
  const { t } = useTranslate();
5079
- const [isVisible, setIsVisible] = React81__default.useState(false);
5080
- const timeoutRef = React81__default.useRef(null);
5079
+ const [isVisible, setIsVisible] = React84__default.useState(false);
5080
+ const timeoutRef = React84__default.useRef(null);
5081
5081
  const handleMouseEnter = () => {
5082
5082
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
5083
5083
  timeoutRef.current = setTimeout(() => setIsVisible(true), 200);
@@ -5086,7 +5086,7 @@ var init_LawReferenceTooltip = __esm({
5086
5086
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
5087
5087
  setIsVisible(false);
5088
5088
  };
5089
- React81__default.useEffect(() => {
5089
+ React84__default.useEffect(() => {
5090
5090
  return () => {
5091
5091
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
5092
5092
  };
@@ -5296,7 +5296,7 @@ var init_StatusDot = __esm({
5296
5296
  md: "w-2.5 h-2.5",
5297
5297
  lg: "w-3 h-3"
5298
5298
  };
5299
- StatusDot = React81__default.forwardRef(
5299
+ StatusDot = React84__default.forwardRef(
5300
5300
  ({ className, status = "offline", pulse = false, size = "md", label, ...props }, ref) => {
5301
5301
  return /* @__PURE__ */ jsx(
5302
5302
  "span",
@@ -5350,7 +5350,7 @@ var init_TrendIndicator = __esm({
5350
5350
  down: "trending-down",
5351
5351
  flat: "arrow-right"
5352
5352
  };
5353
- TrendIndicator = React81__default.forwardRef(
5353
+ TrendIndicator = React84__default.forwardRef(
5354
5354
  ({
5355
5355
  className,
5356
5356
  value,
@@ -5417,7 +5417,7 @@ var init_RangeSlider = __esm({
5417
5417
  md: "w-4 h-4",
5418
5418
  lg: "w-5 h-5"
5419
5419
  };
5420
- RangeSlider = React81__default.forwardRef(
5420
+ RangeSlider = React84__default.forwardRef(
5421
5421
  ({
5422
5422
  className,
5423
5423
  min = 0,
@@ -5925,7 +5925,7 @@ var init_ContentSection = __esm({
5925
5925
  md: "py-16",
5926
5926
  lg: "py-24"
5927
5927
  };
5928
- ContentSection = React81__default.forwardRef(
5928
+ ContentSection = React84__default.forwardRef(
5929
5929
  ({ children, background = "default", padding = "lg", id, className }, ref) => {
5930
5930
  return /* @__PURE__ */ jsx(
5931
5931
  Box,
@@ -6459,7 +6459,7 @@ var init_AnimatedReveal = __esm({
6459
6459
  "scale-up": { opacity: 1, transform: "scale(1) translateY(0)" },
6460
6460
  "none": {}
6461
6461
  };
6462
- AnimatedReveal = React81__default.forwardRef(
6462
+ AnimatedReveal = React84__default.forwardRef(
6463
6463
  ({
6464
6464
  trigger = "scroll",
6465
6465
  animation = "fade-up",
@@ -6619,7 +6619,7 @@ var init_AnimatedGraphic = __esm({
6619
6619
  "components/marketing/atoms/AnimatedGraphic.tsx"() {
6620
6620
  "use client";
6621
6621
  init_cn();
6622
- AnimatedGraphic = React81__default.forwardRef(
6622
+ AnimatedGraphic = React84__default.forwardRef(
6623
6623
  ({
6624
6624
  src,
6625
6625
  svgContent,
@@ -6642,7 +6642,7 @@ var init_AnimatedGraphic = __esm({
6642
6642
  const fetchedSvg = useFetchedSvg(svgContent ? void 0 : src);
6643
6643
  const resolvedSvg = svgContent ?? fetchedSvg;
6644
6644
  const prevAnimateRef = useRef(animate);
6645
- const setRef = React81__default.useCallback(
6645
+ const setRef = React84__default.useCallback(
6646
6646
  (node) => {
6647
6647
  containerRef.current = node;
6648
6648
  if (typeof ref === "function") ref(node);
@@ -6868,9 +6868,9 @@ function ScoreDisplay({
6868
6868
  ...rest
6869
6869
  }) {
6870
6870
  const resolvedValue = typeof value === "number" && !Number.isNaN(value) ? value : typeof rest.score === "number" && !Number.isNaN(rest.score) ? rest.score : 0;
6871
- const [displayValue, setDisplayValue] = React81.useState(resolvedValue);
6872
- const [isAnimating, setIsAnimating] = React81.useState(false);
6873
- React81.useEffect(() => {
6871
+ const [displayValue, setDisplayValue] = React84.useState(resolvedValue);
6872
+ const [isAnimating, setIsAnimating] = React84.useState(false);
6873
+ React84.useEffect(() => {
6874
6874
  if (!animated || displayValue === resolvedValue) {
6875
6875
  setDisplayValue(resolvedValue);
6876
6876
  return;
@@ -6953,9 +6953,9 @@ function ControlButton({
6953
6953
  className
6954
6954
  }) {
6955
6955
  const eventBus = useEventBus();
6956
- const [isPressed, setIsPressed] = React81.useState(false);
6956
+ const [isPressed, setIsPressed] = React84.useState(false);
6957
6957
  const actualPressed = pressed ?? isPressed;
6958
- const handlePointerDown = React81.useCallback(
6958
+ const handlePointerDown = React84.useCallback(
6959
6959
  (e) => {
6960
6960
  e.preventDefault();
6961
6961
  if (disabled) return;
@@ -6965,7 +6965,7 @@ function ControlButton({
6965
6965
  },
6966
6966
  [disabled, pressEvent, eventBus, onPress]
6967
6967
  );
6968
- const handlePointerUp = React81.useCallback(
6968
+ const handlePointerUp = React84.useCallback(
6969
6969
  (e) => {
6970
6970
  e.preventDefault();
6971
6971
  if (disabled) return;
@@ -6975,7 +6975,7 @@ function ControlButton({
6975
6975
  },
6976
6976
  [disabled, releaseEvent, eventBus, onRelease]
6977
6977
  );
6978
- const handlePointerLeave = React81.useCallback(
6978
+ const handlePointerLeave = React84.useCallback(
6979
6979
  (e) => {
6980
6980
  if (isPressed) {
6981
6981
  setIsPressed(false);
@@ -7596,8 +7596,8 @@ function XPBar({
7596
7596
  }) {
7597
7597
  const sizes = sizeMap9[size];
7598
7598
  const percentage = max > 0 ? Math.max(0, Math.min(100, current / max * 100)) : 0;
7599
- const [fillWidth, setFillWidth] = React81.useState(animated ? 0 : percentage);
7600
- React81.useEffect(() => {
7599
+ const [fillWidth, setFillWidth] = React84.useState(animated ? 0 : percentage);
7600
+ React84.useEffect(() => {
7601
7601
  if (!animated) {
7602
7602
  setFillWidth(percentage);
7603
7603
  return;
@@ -8085,9 +8085,9 @@ function MiniMap({
8085
8085
  viewportRect = DEFAULT_VIEWPORT,
8086
8086
  className
8087
8087
  }) {
8088
- const canvasRef = React81.useRef(null);
8089
- const frameRef = React81.useRef(0);
8090
- React81.useEffect(() => {
8088
+ const canvasRef = React84.useRef(null);
8089
+ const frameRef = React84.useRef(0);
8090
+ React84.useEffect(() => {
8091
8091
  const canvas = canvasRef.current;
8092
8092
  if (!canvas) return;
8093
8093
  const ctx = canvas.getContext("2d");
@@ -8276,7 +8276,7 @@ var init_ErrorBoundary = __esm({
8276
8276
  }
8277
8277
  );
8278
8278
  };
8279
- ErrorBoundary = class extends React81__default.Component {
8279
+ ErrorBoundary = class extends React84__default.Component {
8280
8280
  constructor(props) {
8281
8281
  super(props);
8282
8282
  __publicField(this, "reset", () => {
@@ -9311,8 +9311,8 @@ function ActionButtons({
9311
9311
  disabled
9312
9312
  }) {
9313
9313
  const eventBus = useEventBus();
9314
- const [activeButtons, setActiveButtons] = React81.useState(/* @__PURE__ */ new Set());
9315
- const handlePress = React81.useCallback(
9314
+ const [activeButtons, setActiveButtons] = React84.useState(/* @__PURE__ */ new Set());
9315
+ const handlePress = React84.useCallback(
9316
9316
  (id) => {
9317
9317
  setActiveButtons((prev) => new Set(prev).add(id));
9318
9318
  if (actionEvent) eventBus.emit(`UI:${actionEvent}`, { id, pressed: true });
@@ -9320,7 +9320,7 @@ function ActionButtons({
9320
9320
  },
9321
9321
  [actionEvent, eventBus, onAction]
9322
9322
  );
9323
- const handleRelease = React81.useCallback(
9323
+ const handleRelease = React84.useCallback(
9324
9324
  (id) => {
9325
9325
  setActiveButtons((prev) => {
9326
9326
  const next = new Set(prev);
@@ -9957,8 +9957,8 @@ var init_AnimatedCounter = __esm({
9957
9957
  const [displayValue, setDisplayValue] = useState("0");
9958
9958
  const [hasAnimated, setHasAnimated] = useState(false);
9959
9959
  const animate = useCallback(() => {
9960
- const { num: num2, prefix, suffix, decimals } = parseValue(value);
9961
- if (num2 === 0) {
9960
+ const { num: num3, prefix, suffix, decimals } = parseValue(value);
9961
+ if (num3 === 0) {
9962
9962
  setDisplayValue(String(value));
9963
9963
  return;
9964
9964
  }
@@ -9967,7 +9967,7 @@ var init_AnimatedCounter = __esm({
9967
9967
  const elapsed = now2 - startTime;
9968
9968
  const progress = Math.min(elapsed / duration, 1);
9969
9969
  const eased = 1 - Math.pow(1 - progress, 3);
9970
- const current = eased * num2;
9970
+ const current = eased * num3;
9971
9971
  setDisplayValue(`${prefix}${current.toFixed(decimals)}${suffix}`);
9972
9972
  if (progress < 1) {
9973
9973
  requestAnimationFrame(tick);
@@ -10797,7 +10797,7 @@ function IsometricCanvas({
10797
10797
  // Tuning
10798
10798
  diamondTopY: diamondTopYProp,
10799
10799
  // Remote asset loading
10800
- assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/",
10800
+ assetBaseUrl,
10801
10801
  assetManifest
10802
10802
  }) {
10803
10803
  const tilesProp = Array.isArray(_tilesPropRaw) ? _tilesPropRaw : [];
@@ -10876,7 +10876,7 @@ function IsometricCanvas({
10876
10876
  const attackTargetSet = useMemo(() => {
10877
10877
  return new Set(attackTargets.map((p2) => `${p2.x},${p2.y}`));
10878
10878
  }, [attackTargets]);
10879
- const resolveManifestUrl = useCallback((relativePath) => {
10879
+ const resolveManifestUrl7 = useCallback((relativePath) => {
10880
10880
  if (!relativePath) return void 0;
10881
10881
  if (assetBaseUrl) return `${assetBaseUrl.replace(/\/$/, "")}${relativePath.startsWith("/") ? "" : "/"}${relativePath}`;
10882
10882
  return relativePath;
@@ -10889,7 +10889,7 @@ function IsometricCanvas({
10889
10889
  const url = getTerrainSprite(tile.terrain ?? "");
10890
10890
  if (url) urls.push(url);
10891
10891
  } else {
10892
- const url = resolveManifestUrl(assetManifest?.terrains?.[tile.terrain ?? ""]);
10892
+ const url = resolveManifestUrl7(assetManifest?.terrains?.[tile.terrain ?? ""]);
10893
10893
  if (url) urls.push(url);
10894
10894
  }
10895
10895
  }
@@ -10899,7 +10899,7 @@ function IsometricCanvas({
10899
10899
  const url = getFeatureSprite(feature.type);
10900
10900
  if (url) urls.push(url);
10901
10901
  } else {
10902
- const url = resolveManifestUrl(assetManifest?.features?.[feature.type]);
10902
+ const url = resolveManifestUrl7(assetManifest?.features?.[feature.type]);
10903
10903
  if (url) urls.push(url);
10904
10904
  }
10905
10905
  }
@@ -10909,13 +10909,13 @@ function IsometricCanvas({
10909
10909
  const url = getUnitSprite(unit);
10910
10910
  if (url) urls.push(url);
10911
10911
  } else if (unit.unitType) {
10912
- const url = resolveManifestUrl(assetManifest?.units?.[unit.unitType]);
10912
+ const url = resolveManifestUrl7(assetManifest?.units?.[unit.unitType]);
10913
10913
  if (url) urls.push(url);
10914
10914
  }
10915
10915
  }
10916
10916
  if (assetManifest?.effects) {
10917
10917
  for (const path of Object.values(assetManifest.effects)) {
10918
- const url = resolveManifestUrl(path);
10918
+ const url = resolveManifestUrl7(path);
10919
10919
  if (url) urls.push(url);
10920
10920
  }
10921
10921
  }
@@ -10923,7 +10923,7 @@ function IsometricCanvas({
10923
10923
  if (atlasSheetUrls.length) urls.push(...atlasSheetUrls);
10924
10924
  if (backgroundImage) urls.push(backgroundImage);
10925
10925
  return [...new Set(urls.filter(Boolean))];
10926
- }, [sortedTiles, features, units, getTerrainSprite, getFeatureSprite, getUnitSprite, effectSpriteUrls, atlasSheetUrls, backgroundImage, assetManifest, resolveManifestUrl]);
10926
+ }, [sortedTiles, features, units, getTerrainSprite, getFeatureSprite, getUnitSprite, effectSpriteUrls, atlasSheetUrls, backgroundImage, assetManifest, resolveManifestUrl7]);
10927
10927
  const { getImage, pendingCount } = useImageCache(spriteUrls);
10928
10928
  useEffect(() => {
10929
10929
  if (typeof window === "undefined") return;
@@ -10947,14 +10947,14 @@ function IsometricCanvas({
10947
10947
  lerpToTarget
10948
10948
  } = useCamera();
10949
10949
  const resolveTerrainSpriteUrl = useCallback((tile) => {
10950
- return tile.terrainSprite || getTerrainSprite?.(tile.terrain ?? "") || resolveManifestUrl(assetManifest?.terrains?.[tile.terrain ?? ""]);
10951
- }, [getTerrainSprite, assetManifest, resolveManifestUrl]);
10950
+ return tile.terrainSprite || getTerrainSprite?.(tile.terrain ?? "") || resolveManifestUrl7(assetManifest?.terrains?.[tile.terrain ?? ""]);
10951
+ }, [getTerrainSprite, assetManifest, resolveManifestUrl7]);
10952
10952
  const resolveFeatureSpriteUrl = useCallback((featureType) => {
10953
- return getFeatureSprite?.(featureType) || resolveManifestUrl(assetManifest?.features?.[featureType]);
10954
- }, [getFeatureSprite, assetManifest, resolveManifestUrl]);
10953
+ return getFeatureSprite?.(featureType) || resolveManifestUrl7(assetManifest?.features?.[featureType]);
10954
+ }, [getFeatureSprite, assetManifest, resolveManifestUrl7]);
10955
10955
  const resolveUnitSpriteUrl = useCallback((unit) => {
10956
- return unit.sprite || getUnitSprite?.(unit) || (unit.unitType ? resolveManifestUrl(assetManifest?.units?.[unit.unitType]) : void 0);
10957
- }, [getUnitSprite, assetManifest, resolveManifestUrl]);
10956
+ return unit.sprite || getUnitSprite?.(unit) || (unit.unitType ? resolveManifestUrl7(assetManifest?.units?.[unit.unitType]) : void 0);
10957
+ }, [getUnitSprite, assetManifest, resolveManifestUrl7]);
10958
10958
  const drawMinimap = useCallback(() => {
10959
10959
  if (!showMinimap) return;
10960
10960
  const miniCanvas = minimapRef.current;
@@ -11056,7 +11056,15 @@ function IsometricCanvas({
11056
11056
  const spriteUrl = resolveTerrainSpriteUrl(tile);
11057
11057
  const img = spriteUrl ? getImage(spriteUrl) : null;
11058
11058
  if (img) {
11059
- ctx.drawImage(img, pos.x, pos.y, scaledTileWidth, scaledTileHeight);
11059
+ if (img.naturalWidth === 0) {
11060
+ ctx.drawImage(img, pos.x, pos.y, scaledTileWidth, scaledTileHeight);
11061
+ } else {
11062
+ const drawW = scaledTileWidth;
11063
+ const drawH = scaledTileWidth * (img.naturalHeight / img.naturalWidth);
11064
+ const drawX = pos.x;
11065
+ const drawY = pos.y + scaledTileHeight - drawH;
11066
+ ctx.drawImage(img, drawX, drawY, drawW, drawH);
11067
+ }
11060
11068
  } else {
11061
11069
  const centerX = pos.x + scaledTileWidth / 2;
11062
11070
  const topY = pos.y + scaledDiamondTopY;
@@ -13315,7 +13323,7 @@ var init_CodeBlock = __esm({
13315
13323
  };
13316
13324
  LINE_PROPS_FN = (n) => ({ "data-line": String(n - 1) });
13317
13325
  HIDDEN_LINE_NUMBERS = { display: "none" };
13318
- CodeBlock = React81__default.memo(
13326
+ CodeBlock = React84__default.memo(
13319
13327
  ({
13320
13328
  code: rawCode,
13321
13329
  language = "text",
@@ -13484,24 +13492,24 @@ var init_CodeBlock = __esm({
13484
13492
  return;
13485
13493
  }
13486
13494
  lineEls.forEach((el) => {
13487
- const num2 = parseInt(el.getAttribute("data-line") ?? "-1", 10);
13488
- if (hiddenLines.has(num2)) {
13495
+ const num3 = parseInt(el.getAttribute("data-line") ?? "-1", 10);
13496
+ if (hiddenLines.has(num3)) {
13489
13497
  el.style.display = "none";
13490
13498
  return;
13491
13499
  }
13492
13500
  el.style.display = "";
13493
13501
  el.style.position = "relative";
13494
13502
  el.style.paddingLeft = "1.2em";
13495
- const region = foldStartMap.get(num2);
13503
+ const region = foldStartMap.get(num3);
13496
13504
  if (!region) return;
13497
- const isCollapsed = collapsed.has(num2);
13505
+ const isCollapsed = collapsed.has(num3);
13498
13506
  const toggle = document.createElement("span");
13499
13507
  toggle.className = "fold-toggle";
13500
13508
  toggle.textContent = isCollapsed ? "\u25B6" : "\u25BC";
13501
13509
  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%";
13502
13510
  toggle.addEventListener("click", (e) => {
13503
13511
  e.stopPropagation();
13504
- toggleFoldRef.current(num2);
13512
+ toggleFoldRef.current(num3);
13505
13513
  });
13506
13514
  el.insertBefore(toggle, el.firstChild);
13507
13515
  if (isCollapsed) {
@@ -13902,7 +13910,7 @@ var init_MarkdownContent = __esm({
13902
13910
  init_Box();
13903
13911
  init_CodeBlock();
13904
13912
  init_cn();
13905
- MarkdownContent = React81__default.memo(
13913
+ MarkdownContent = React84__default.memo(
13906
13914
  ({ content, direction = "ltr", className }) => {
13907
13915
  const { t: _t } = useTranslate();
13908
13916
  const safeContent = typeof content === "string" ? content : String(content ?? "");
@@ -15219,7 +15227,7 @@ var init_StateMachineView = __esm({
15219
15227
  style: { top: title ? 30 : 0 },
15220
15228
  children: [
15221
15229
  entity && /* @__PURE__ */ jsx(EntityBox, { entity, config }),
15222
- states.map((state) => renderStateNode ? /* @__PURE__ */ jsx(React81__default.Fragment, { children: renderStateNode(state, config) }, state.id) : /* @__PURE__ */ jsx(
15230
+ states.map((state) => renderStateNode ? /* @__PURE__ */ jsx(React84__default.Fragment, { children: renderStateNode(state, config) }, state.id) : /* @__PURE__ */ jsx(
15223
15231
  StateNode,
15224
15232
  {
15225
15233
  state,
@@ -19120,8 +19128,8 @@ function EmojiEffect({
19120
19128
  );
19121
19129
  }
19122
19130
  function CanvasEffect({
19123
- effectSpriteUrl = "https://almadar-kflow-assets.web.app/shared/effects/gas/gas00.png",
19124
- assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/effects/",
19131
+ effectSpriteUrl,
19132
+ assetBaseUrl,
19125
19133
  ...props
19126
19134
  }) {
19127
19135
  const eventBus = useEventBus();
@@ -19164,6 +19172,380 @@ var init_CanvasEffect = __esm({
19164
19172
  CanvasEffect.displayName = "CanvasEffect";
19165
19173
  }
19166
19174
  });
19175
+ function CardHand({
19176
+ cards = DEFAULT_CARDS,
19177
+ selectedId = "",
19178
+ size = "md",
19179
+ onCardClick,
19180
+ cardClickEvent,
19181
+ emptyLabel = "No cards in hand",
19182
+ className
19183
+ }) {
19184
+ const eventBus = useEventBus();
19185
+ const handleClick = React84.useCallback(
19186
+ (card) => {
19187
+ if (card.disabled) return;
19188
+ onCardClick?.(card.id);
19189
+ if (cardClickEvent) {
19190
+ eventBus.emit(`UI:${cardClickEvent}`, { cardId: card.id });
19191
+ }
19192
+ },
19193
+ [onCardClick, cardClickEvent, eventBus]
19194
+ );
19195
+ if (cards.length === 0) {
19196
+ return /* @__PURE__ */ jsx(Box, { className: cn("flex items-center justify-center p-4", className), children: /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: emptyLabel }) });
19197
+ }
19198
+ const art = artSizeMap[size];
19199
+ return /* @__PURE__ */ jsx(Box, { className: cn("flex flex-wrap gap-2 justify-center items-end", className), children: cards.map((card) => {
19200
+ const selected = selectedId === card.id;
19201
+ return /* @__PURE__ */ jsxs(
19202
+ "button",
19203
+ {
19204
+ type: "button",
19205
+ onClick: () => handleClick(card),
19206
+ disabled: card.disabled,
19207
+ title: card.title,
19208
+ className: cn(
19209
+ "card-hand-card relative flex flex-col items-center rounded-interactive border-2",
19210
+ "bg-card/90 px-1.5 pt-1.5 pb-1 transition-all duration-150",
19211
+ cardSizeMap[size],
19212
+ card.disabled ? "border-border opacity-50 cursor-not-allowed" : "border-accent hover:brightness-125 hover:-translate-y-1 cursor-pointer",
19213
+ selected && "ring-2 ring-foreground ring-offset-1 ring-offset-background -translate-y-1"
19214
+ ),
19215
+ children: [
19216
+ card.cost != null && /* @__PURE__ */ jsx(
19217
+ "span",
19218
+ {
19219
+ className: cn(
19220
+ "absolute -top-2 -left-2 flex items-center justify-center",
19221
+ "min-w-[22px] h-[22px] rounded-full px-1",
19222
+ "bg-info text-foreground text-xs font-bold border border-background"
19223
+ ),
19224
+ children: card.cost
19225
+ }
19226
+ ),
19227
+ /* @__PURE__ */ jsx(Box, { className: "flex-1 flex items-center justify-center w-full", children: card.iconUrl ? /* @__PURE__ */ jsx(
19228
+ "img",
19229
+ {
19230
+ src: card.iconUrl,
19231
+ alt: card.title ?? card.id,
19232
+ width: art,
19233
+ height: art,
19234
+ style: { imageRendering: "pixelated", objectFit: "contain" },
19235
+ className: "flex-shrink-0"
19236
+ }
19237
+ ) : /* @__PURE__ */ jsx(
19238
+ Box,
19239
+ {
19240
+ className: "rounded bg-muted/40",
19241
+ style: { width: art, height: art }
19242
+ }
19243
+ ) }),
19244
+ card.title != null && /* @__PURE__ */ jsx(
19245
+ Typography,
19246
+ {
19247
+ variant: "small",
19248
+ className: "w-full truncate text-center text-[10px] leading-tight font-semibold",
19249
+ children: card.title
19250
+ }
19251
+ ),
19252
+ (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: [
19253
+ /* @__PURE__ */ jsx("span", { className: "text-error", children: card.attack != null ? `\u2694${card.attack}` : "" }),
19254
+ /* @__PURE__ */ jsx("span", { className: "text-info", children: card.defense != null ? `\u{1F6E1}${card.defense}` : "" })
19255
+ ] })
19256
+ ]
19257
+ },
19258
+ card.id
19259
+ );
19260
+ }) });
19261
+ }
19262
+ var cardSizeMap, artSizeMap, DEFAULT_CARDS;
19263
+ var init_CardHand = __esm({
19264
+ "components/game/molecules/CardHand.tsx"() {
19265
+ "use client";
19266
+ init_cn();
19267
+ init_useEventBus();
19268
+ init_Box();
19269
+ init_Typography();
19270
+ cardSizeMap = {
19271
+ sm: "w-16 h-24",
19272
+ md: "w-20 h-28",
19273
+ lg: "w-24 h-36"
19274
+ };
19275
+ artSizeMap = {
19276
+ sm: 40,
19277
+ md: 52,
19278
+ lg: 64
19279
+ };
19280
+ DEFAULT_CARDS = [
19281
+ { id: "card-1", title: "Brawler", cost: 1, attack: 2, defense: 1 },
19282
+ { id: "card-2", title: "Guardian", cost: 2, attack: 1, defense: 4 },
19283
+ { id: "card-3", title: "Striker", cost: 3, attack: 5, defense: 2 }
19284
+ ];
19285
+ CardHand.displayName = "CardHand";
19286
+ }
19287
+ });
19288
+ function resolveManifestUrl(manifest, relative) {
19289
+ if (relative == null) return void 0;
19290
+ if (/^https?:\/\//.test(relative)) return relative;
19291
+ const base = manifest?.baseUrl;
19292
+ if (base == null) return relative;
19293
+ const cleanBase = base.replace(/\/$/, "");
19294
+ const cleanRel = relative.replace(/^\//, "");
19295
+ return `${cleanBase}/${cleanRel}`;
19296
+ }
19297
+ function rowToCard(r) {
19298
+ return {
19299
+ id: str(r.id),
19300
+ iconKey: r.iconKey == null ? void 0 : str(r.iconKey),
19301
+ title: r.title == null ? void 0 : str(r.title),
19302
+ cost: r.cost == null ? void 0 : num(r.cost),
19303
+ attack: r.attack == null ? void 0 : num(r.attack),
19304
+ defense: r.defense == null ? void 0 : num(r.defense)
19305
+ };
19306
+ }
19307
+ function toHandCards(cards, manifest, mana) {
19308
+ return cards.map((c) => {
19309
+ const key = c.iconKey ?? c.id;
19310
+ const iconUrl = resolveManifestUrl(manifest, manifest?.cards?.[key]);
19311
+ return {
19312
+ id: c.id,
19313
+ iconUrl,
19314
+ title: c.title,
19315
+ cost: c.cost,
19316
+ attack: c.attack,
19317
+ defense: c.defense,
19318
+ disabled: c.cost != null && c.cost > mana
19319
+ };
19320
+ });
19321
+ }
19322
+ function CardBattlerBoard({
19323
+ entity,
19324
+ deck: propDeck,
19325
+ hand: propHand,
19326
+ board: propBoard,
19327
+ mana: propMana,
19328
+ maxMana: propMaxMana,
19329
+ turn: propTurn,
19330
+ result: propResult,
19331
+ assetManifest: propAssetManifest,
19332
+ playCardEvent,
19333
+ endTurnEvent,
19334
+ playAgainEvent,
19335
+ gameEndEvent,
19336
+ className
19337
+ }) {
19338
+ const board = boardEntity(entity) ?? {};
19339
+ const eventBus = useEventBus();
19340
+ const { t } = useTranslate();
19341
+ const assetManifest = propAssetManifest ?? board.assetManifest;
19342
+ const entityHand = useMemo(() => rows(board.hand).map(rowToCard), [board.hand]);
19343
+ const rawHand = propHand ?? entityHand;
19344
+ const hand = rawHand.length > 0 ? rawHand : DEFAULT_HAND;
19345
+ const entityBoard = useMemo(() => rows(board.board).map(rowToCard), [board.board]);
19346
+ const playedCards = propBoard ?? entityBoard;
19347
+ const entityDeck = useMemo(() => rows(board.deck).map(rowToCard), [board.deck]);
19348
+ const deck = propDeck ?? entityDeck;
19349
+ const mana = propMana ?? num(board.mana, 3);
19350
+ const maxMana = propMaxMana ?? num(board.maxMana, 3);
19351
+ const turn = propTurn ?? num(board.turn, 1);
19352
+ const result = propResult ?? (str(board.result) || "none");
19353
+ const handCards = useMemo(() => toHandCards(hand, assetManifest, mana), [hand, assetManifest, mana]);
19354
+ const boardCards = useMemo(() => toHandCards(playedCards, assetManifest, mana), [playedCards, assetManifest, mana]);
19355
+ const emittedGameEnd = useRef(false);
19356
+ if (result !== "none" && !emittedGameEnd.current) {
19357
+ emittedGameEnd.current = true;
19358
+ if (gameEndEvent) {
19359
+ eventBus.emit(`UI:${gameEndEvent}`, { result });
19360
+ }
19361
+ }
19362
+ if (result === "none") {
19363
+ emittedGameEnd.current = false;
19364
+ }
19365
+ const handlePlayCard = useCallback((cardId) => {
19366
+ if (result !== "none") return;
19367
+ if (playCardEvent) {
19368
+ eventBus.emit(`UI:${playCardEvent}`, { cardId });
19369
+ }
19370
+ }, [result, playCardEvent, eventBus]);
19371
+ const handleEndTurn = useCallback(() => {
19372
+ if (result !== "none") return;
19373
+ if (endTurnEvent) {
19374
+ eventBus.emit(`UI:${endTurnEvent}`, {});
19375
+ }
19376
+ }, [result, endTurnEvent, eventBus]);
19377
+ const handlePlayAgain = useCallback(() => {
19378
+ if (playAgainEvent) {
19379
+ eventBus.emit(`UI:${playAgainEvent}`, {});
19380
+ }
19381
+ }, [playAgainEvent, eventBus]);
19382
+ return /* @__PURE__ */ jsxs(VStack, { className: cn("card-battler-board relative min-h-[600px] bg-background", className), gap: "none", children: [
19383
+ /* @__PURE__ */ jsxs(
19384
+ HStack,
19385
+ {
19386
+ className: "px-4 py-2 border-b border-border bg-surface",
19387
+ gap: "lg",
19388
+ align: "center",
19389
+ children: [
19390
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
19391
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("cb.mana") ?? "Mana" }),
19392
+ /* @__PURE__ */ jsxs(Typography, { variant: "body2", weight: "bold", className: "text-info", children: [
19393
+ mana,
19394
+ " / ",
19395
+ maxMana
19396
+ ] })
19397
+ ] }),
19398
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
19399
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("cb.turn") ?? "Turn" }),
19400
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", children: turn })
19401
+ ] }),
19402
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
19403
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("cb.deck") ?? "Deck" }),
19404
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", children: deck.length })
19405
+ ] }),
19406
+ /* @__PURE__ */ jsx(Box, { className: "ml-auto", children: result === "none" && /* @__PURE__ */ jsx(Button, { variant: "primary", size: "sm", onClick: handleEndTurn, children: t("cb.endTurn") ?? "End Turn" }) })
19407
+ ]
19408
+ }
19409
+ ),
19410
+ /* @__PURE__ */ jsxs(Box, { className: "flex-1 relative p-4 overflow-auto", children: [
19411
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", className: "mb-2 uppercase tracking-wide", children: t("cb.battlefield") ?? "Battlefield" }),
19412
+ boardCards.length > 0 ? /* @__PURE__ */ jsx(
19413
+ CardHand,
19414
+ {
19415
+ cards: boardCards,
19416
+ size: "md",
19417
+ className: "justify-start"
19418
+ }
19419
+ ) : /* @__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" }) })
19420
+ ] }),
19421
+ /* @__PURE__ */ jsxs(Box, { className: "border-t border-border bg-surface px-4 py-3", children: [
19422
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", className: "mb-2 uppercase tracking-wide", children: t("cb.hand") ?? "Your Hand" }),
19423
+ /* @__PURE__ */ jsx(
19424
+ CardHand,
19425
+ {
19426
+ cards: handCards,
19427
+ size: "lg",
19428
+ onCardClick: handlePlayCard,
19429
+ emptyLabel: t("cb.emptyHand") ?? "No cards in hand"
19430
+ }
19431
+ )
19432
+ ] }),
19433
+ 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: [
19434
+ /* @__PURE__ */ jsx(
19435
+ Typography,
19436
+ {
19437
+ variant: "h2",
19438
+ className: cn(
19439
+ "text-4xl font-black tracking-widest uppercase",
19440
+ result === "won" ? "text-warning" : "text-error"
19441
+ ),
19442
+ children: result === "won" ? t("cb.victory") ?? "Victory!" : t("cb.defeat") ?? "Defeat!"
19443
+ }
19444
+ ),
19445
+ /* @__PURE__ */ jsx(
19446
+ Button,
19447
+ {
19448
+ variant: "primary",
19449
+ className: "px-8 py-3 font-semibold",
19450
+ onClick: handlePlayAgain,
19451
+ children: t("cb.playAgain") ?? "Play Again"
19452
+ }
19453
+ )
19454
+ ] }) })
19455
+ ] });
19456
+ }
19457
+ var DEFAULT_HAND;
19458
+ var init_CardBattlerBoard = __esm({
19459
+ "components/game/organisms/CardBattlerBoard.tsx"() {
19460
+ "use client";
19461
+ init_cn();
19462
+ init_useEventBus();
19463
+ init_Box();
19464
+ init_Button();
19465
+ init_Typography();
19466
+ init_Stack();
19467
+ init_CardHand();
19468
+ init_boardEntity();
19469
+ DEFAULT_HAND = [
19470
+ { id: "c1", iconKey: "bear", title: "Bear", cost: 3, attack: 5, defense: 4 },
19471
+ { id: "c2", iconKey: "owl", title: "Owl", cost: 1, attack: 2, defense: 1 },
19472
+ { id: "c3", iconKey: "gorilla", title: "Gorilla", cost: 4, attack: 6, defense: 5 },
19473
+ { id: "c4", iconKey: "frog", title: "Frog", cost: 1, attack: 1, defense: 2 },
19474
+ { id: "c5", iconKey: "snake", title: "Snake", cost: 2, attack: 4, defense: 1 }
19475
+ ];
19476
+ CardBattlerBoard.displayName = "CardBattlerBoard";
19477
+ }
19478
+ });
19479
+ function isEntityRow(value) {
19480
+ return value != null && typeof value === "object" && !Array.isArray(value);
19481
+ }
19482
+ function CardBattlerTemplate({
19483
+ entity,
19484
+ title = "Card Battler",
19485
+ deck,
19486
+ hand,
19487
+ board,
19488
+ mana,
19489
+ maxMana,
19490
+ turn,
19491
+ result,
19492
+ assetManifest,
19493
+ playCardEvent,
19494
+ endTurnEvent,
19495
+ playAgainEvent,
19496
+ gameEndEvent,
19497
+ className
19498
+ }) {
19499
+ const resolved = isEntityRow(entity) ? entity : void 0;
19500
+ return /* @__PURE__ */ jsxs(
19501
+ Box,
19502
+ {
19503
+ display: "flex",
19504
+ fullHeight: true,
19505
+ className: cn("card-battler-template flex-col", className),
19506
+ children: [
19507
+ /* @__PURE__ */ jsx(
19508
+ HStack,
19509
+ {
19510
+ gap: "sm",
19511
+ align: "center",
19512
+ className: "px-4 py-3 border-b-2 border-border bg-surface shrink-0",
19513
+ children: /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title })
19514
+ }
19515
+ ),
19516
+ /* @__PURE__ */ jsx(Box, { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsx(
19517
+ CardBattlerBoard,
19518
+ {
19519
+ entity: resolved,
19520
+ deck,
19521
+ hand,
19522
+ board,
19523
+ mana,
19524
+ maxMana,
19525
+ turn,
19526
+ result,
19527
+ assetManifest,
19528
+ playCardEvent,
19529
+ endTurnEvent,
19530
+ playAgainEvent,
19531
+ gameEndEvent,
19532
+ className: "h-full"
19533
+ }
19534
+ ) })
19535
+ ]
19536
+ }
19537
+ );
19538
+ }
19539
+ var init_CardBattlerTemplate = __esm({
19540
+ "components/game/templates/CardBattlerTemplate.tsx"() {
19541
+ init_cn();
19542
+ init_Box();
19543
+ init_Stack();
19544
+ init_Typography();
19545
+ init_CardBattlerBoard();
19546
+ CardBattlerTemplate.displayName = "CardBattlerTemplate";
19547
+ }
19548
+ });
19167
19549
 
19168
19550
  // lib/getNestedValue.ts
19169
19551
  function getNestedValue(obj, path) {
@@ -20946,6 +21328,315 @@ var init_ChartLegend = __esm({
20946
21328
  ChartLegend.displayName = "ChartLegend";
20947
21329
  }
20948
21330
  });
21331
+ function resolveManifestUrl2(manifest, relative) {
21332
+ if (relative == null) return void 0;
21333
+ if (/^https?:\/\//.test(relative)) return relative;
21334
+ const base = manifest?.baseUrl;
21335
+ if (base == null) return relative;
21336
+ const cleanBase = base.replace(/\/$/, "");
21337
+ const cleanRel = relative.replace(/^\//, "");
21338
+ return `${cleanBase}/${cleanRel}`;
21339
+ }
21340
+ function buildDefaultCBTiles() {
21341
+ const tiles = [];
21342
+ for (let y = 0; y < CB_GRID_H; y++) {
21343
+ for (let x = 0; x < CB_GRID_W; x++) {
21344
+ const variant = (x * 3 + y * 5 + (x ^ y)) % 3;
21345
+ const terrainKey = ["grass", "stone", "dirt"][variant];
21346
+ tiles.push({ x, y, terrain: terrainKey, passable: true });
21347
+ }
21348
+ }
21349
+ return tiles;
21350
+ }
21351
+ function tilesToIso(tiles, manifest) {
21352
+ return tiles.map((t) => {
21353
+ const key = t.terrain ?? "grass";
21354
+ const sprite = t.terrainSprite ?? resolveManifestUrl2(manifest, manifest?.terrains?.[key]) ?? resolveManifestUrl2(manifest, manifest?.terrains?.grass);
21355
+ return {
21356
+ x: t.x,
21357
+ y: t.y,
21358
+ terrain: t.terrain,
21359
+ terrainSprite: sprite,
21360
+ passable: t.passable
21361
+ };
21362
+ });
21363
+ }
21364
+ function buildingsToFeatures(buildings, manifest) {
21365
+ return buildings.map((b) => ({
21366
+ id: b.id,
21367
+ x: b.x,
21368
+ y: b.y,
21369
+ type: b.type,
21370
+ sprite: resolveManifestUrl2(manifest, manifest?.features?.[b.type])
21371
+ }));
21372
+ }
21373
+ function CityBuilderBoard({
21374
+ entity,
21375
+ tiles: propTiles,
21376
+ buildings: propBuildings,
21377
+ assetManifest: propAssetManifest,
21378
+ buildTypes: propBuildTypes,
21379
+ pendingBuildType: propPendingBuildType,
21380
+ gold: propGold,
21381
+ wood: propWood,
21382
+ population: propPopulation,
21383
+ result: propResult,
21384
+ scale = 0.3,
21385
+ unitScale = 0.6,
21386
+ spriteHeightRatio = 1.5,
21387
+ spriteMaxWidthRatio = 0.7,
21388
+ selectBuildTypeEvent,
21389
+ placeBuildingEvent,
21390
+ gameEndEvent,
21391
+ className
21392
+ }) {
21393
+ const board = boardEntity(entity) ?? {};
21394
+ const eventBus = useEventBus();
21395
+ const { t } = useTranslate();
21396
+ const assetManifest = propAssetManifest ?? board.assetManifest;
21397
+ const entityTiles = useMemo(
21398
+ () => rows(board.tiles).map((r) => ({
21399
+ x: num(r.x),
21400
+ y: num(r.y),
21401
+ terrain: r.terrain == null ? void 0 : str(r.terrain),
21402
+ terrainSprite: r.terrainSprite == null ? void 0 : str(r.terrainSprite),
21403
+ passable: r.passable !== false
21404
+ })),
21405
+ [board.tiles]
21406
+ );
21407
+ const rawTiles = propTiles ?? entityTiles;
21408
+ const tiles = rawTiles.length >= CB_GRID_W * CB_GRID_H ? rawTiles : DEFAULT_CB_TILES;
21409
+ const entityBuildings = useMemo(
21410
+ () => rows(board.buildings).map((r) => ({
21411
+ id: str(r.id),
21412
+ x: num(r.x),
21413
+ y: num(r.y),
21414
+ type: str(r.type)
21415
+ })),
21416
+ [board.buildings]
21417
+ );
21418
+ const buildings = propBuildings ?? entityBuildings;
21419
+ const buildTypes = propBuildTypes ?? DEFAULT_BUILD_TYPES;
21420
+ const gold = propGold ?? num(board.gold, 100);
21421
+ const wood = propWood ?? num(board.wood, 80);
21422
+ const population = propPopulation ?? num(board.population, 0);
21423
+ const pendingBuildType = propPendingBuildType ?? (str(board.pendingBuildType) || buildTypes[0]?.type || "");
21424
+ const result = propResult ?? (str(board.result) || "none");
21425
+ const [hoveredTile, setHoveredTile] = useState(null);
21426
+ const emittedGameEnd = useRef(false);
21427
+ if (result !== "none" && !emittedGameEnd.current) {
21428
+ emittedGameEnd.current = true;
21429
+ if (gameEndEvent) {
21430
+ eventBus.emit(`UI:${gameEndEvent}`, { result });
21431
+ }
21432
+ }
21433
+ if (result === "none") {
21434
+ emittedGameEnd.current = false;
21435
+ }
21436
+ const pendingCost = useMemo(
21437
+ () => buildTypes.find((b) => b.type === pendingBuildType)?.cost ?? 0,
21438
+ [buildTypes, pendingBuildType]
21439
+ );
21440
+ const buildingPositions = useMemo(
21441
+ () => new Set(buildings.map((b) => `${b.x},${b.y}`)),
21442
+ [buildings]
21443
+ );
21444
+ const validMoves = useMemo(() => {
21445
+ if (result !== "none" || gold < pendingCost) return [];
21446
+ return tiles.filter(
21447
+ (t2) => t2.passable !== false && !buildingPositions.has(`${t2.x},${t2.y}`)
21448
+ ).map((t2) => ({ x: t2.x, y: t2.y }));
21449
+ }, [tiles, buildingPositions, result, gold, pendingCost]);
21450
+ const isoTiles = useMemo(() => tilesToIso(tiles, assetManifest), [tiles, assetManifest]);
21451
+ const buildingFeatures = useMemo(() => buildingsToFeatures(buildings, assetManifest), [buildings, assetManifest]);
21452
+ const handleTileClick = useCallback((x, y) => {
21453
+ if (result !== "none") return;
21454
+ if (buildingPositions.has(`${x},${y}`)) return;
21455
+ if (gold < pendingCost) return;
21456
+ if (placeBuildingEvent) {
21457
+ eventBus.emit(`UI:${placeBuildingEvent}`, { x, y });
21458
+ }
21459
+ }, [result, buildingPositions, gold, pendingCost, placeBuildingEvent, eventBus]);
21460
+ const handleSelectBuildType = useCallback((buildType) => {
21461
+ if (selectBuildTypeEvent) {
21462
+ eventBus.emit(`UI:${selectBuildTypeEvent}`, { buildType });
21463
+ }
21464
+ }, [selectBuildTypeEvent, eventBus]);
21465
+ const canBuild = gold >= pendingCost && result === "none";
21466
+ return /* @__PURE__ */ jsxs(VStack, { className: cn("city-builder-board relative min-h-[600px] bg-background", className), gap: "none", children: [
21467
+ /* @__PURE__ */ jsxs(
21468
+ HStack,
21469
+ {
21470
+ className: "px-4 py-2 border-b border-border bg-surface",
21471
+ gap: "lg",
21472
+ align: "center",
21473
+ children: [
21474
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
21475
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("cb.gold") ?? "Gold" }),
21476
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", className: "text-warning", children: gold })
21477
+ ] }),
21478
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
21479
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("cb.wood") ?? "Wood" }),
21480
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", className: "text-success", children: wood })
21481
+ ] }),
21482
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
21483
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("cb.population") ?? "Population" }),
21484
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", children: population })
21485
+ ] }),
21486
+ /* @__PURE__ */ jsx(HStack, { gap: "xs", align: "center", className: "ml-auto", children: buildTypes.map((b) => /* @__PURE__ */ jsxs(
21487
+ Button,
21488
+ {
21489
+ variant: b.type === pendingBuildType ? "primary" : "secondary",
21490
+ size: "sm",
21491
+ disabled: gold < b.cost || result !== "none",
21492
+ onClick: () => handleSelectBuildType(b.type),
21493
+ children: [
21494
+ b.label,
21495
+ " (",
21496
+ b.cost,
21497
+ ")"
21498
+ ]
21499
+ },
21500
+ b.type
21501
+ )) })
21502
+ ]
21503
+ }
21504
+ ),
21505
+ /* @__PURE__ */ jsxs(Box, { className: "relative flex-1", children: [
21506
+ /* @__PURE__ */ jsx(
21507
+ IsometricCanvas_default,
21508
+ {
21509
+ tiles: isoTiles,
21510
+ features: buildingFeatures,
21511
+ assetBaseUrl: assetManifest?.baseUrl,
21512
+ assetManifest,
21513
+ validMoves,
21514
+ hoveredTile,
21515
+ onTileClick: handleTileClick,
21516
+ onTileHover: (x, y) => setHoveredTile({ x, y }),
21517
+ onTileLeave: () => setHoveredTile(null),
21518
+ scale,
21519
+ unitScale,
21520
+ spriteHeightRatio,
21521
+ spriteMaxWidthRatio
21522
+ }
21523
+ ),
21524
+ 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)` }) })
21525
+ ] }),
21526
+ 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: [
21527
+ /* @__PURE__ */ jsx(
21528
+ Typography,
21529
+ {
21530
+ variant: "h2",
21531
+ className: cn(
21532
+ "text-4xl font-black tracking-widest uppercase",
21533
+ result === "won" ? "text-warning" : "text-error"
21534
+ ),
21535
+ children: result === "won" ? t("cb.victory") ?? "City Thrives!" : t("cb.defeat") ?? "City Collapsed!"
21536
+ }
21537
+ ),
21538
+ /* @__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." })
21539
+ ] }) })
21540
+ ] });
21541
+ }
21542
+ var CB_GRID_W, CB_GRID_H, DEFAULT_BUILD_TYPES, DEFAULT_CB_TILES;
21543
+ var init_CityBuilderBoard = __esm({
21544
+ "components/game/organisms/CityBuilderBoard.tsx"() {
21545
+ "use client";
21546
+ init_cn();
21547
+ init_useEventBus();
21548
+ init_Box();
21549
+ init_Button();
21550
+ init_Typography();
21551
+ init_Stack();
21552
+ init_IsometricCanvas();
21553
+ init_boardEntity();
21554
+ CB_GRID_W = 12;
21555
+ CB_GRID_H = 12;
21556
+ DEFAULT_BUILD_TYPES = [
21557
+ { type: "house", label: "House", cost: 20 },
21558
+ { type: "farm", label: "Farm", cost: 15 },
21559
+ { type: "mine", label: "Mine", cost: 25 },
21560
+ { type: "barracks", label: "Barracks", cost: 40 }
21561
+ ];
21562
+ DEFAULT_CB_TILES = buildDefaultCBTiles();
21563
+ CityBuilderBoard.displayName = "CityBuilderBoard";
21564
+ }
21565
+ });
21566
+ function isEntityRow2(value) {
21567
+ return value != null && typeof value === "object" && !Array.isArray(value);
21568
+ }
21569
+ function CityBuilderTemplate({
21570
+ entity,
21571
+ title = "City Builder",
21572
+ tiles,
21573
+ buildings,
21574
+ buildTypes,
21575
+ pendingBuildType,
21576
+ assetManifest,
21577
+ gold,
21578
+ wood,
21579
+ population,
21580
+ result,
21581
+ scale,
21582
+ unitScale,
21583
+ selectBuildTypeEvent,
21584
+ placeBuildingEvent,
21585
+ gameEndEvent,
21586
+ className
21587
+ }) {
21588
+ const resolved = isEntityRow2(entity) ? entity : void 0;
21589
+ return /* @__PURE__ */ jsxs(
21590
+ Box,
21591
+ {
21592
+ display: "flex",
21593
+ fullHeight: true,
21594
+ className: cn("city-builder-template flex-col", className),
21595
+ children: [
21596
+ /* @__PURE__ */ jsx(
21597
+ HStack,
21598
+ {
21599
+ gap: "sm",
21600
+ align: "center",
21601
+ className: "px-4 py-3 border-b-2 border-border bg-surface shrink-0",
21602
+ children: /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title })
21603
+ }
21604
+ ),
21605
+ /* @__PURE__ */ jsx(Box, { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsx(
21606
+ CityBuilderBoard,
21607
+ {
21608
+ entity: resolved,
21609
+ tiles,
21610
+ buildings,
21611
+ buildTypes,
21612
+ pendingBuildType,
21613
+ assetManifest,
21614
+ gold,
21615
+ wood,
21616
+ population,
21617
+ result,
21618
+ scale,
21619
+ unitScale,
21620
+ selectBuildTypeEvent,
21621
+ placeBuildingEvent,
21622
+ gameEndEvent,
21623
+ className: "h-full"
21624
+ }
21625
+ ) })
21626
+ ]
21627
+ }
21628
+ );
21629
+ }
21630
+ var init_CityBuilderTemplate = __esm({
21631
+ "components/game/templates/CityBuilderTemplate.tsx"() {
21632
+ init_cn();
21633
+ init_Box();
21634
+ init_Stack();
21635
+ init_Typography();
21636
+ init_CityBuilderBoard();
21637
+ CityBuilderTemplate.displayName = "CityBuilderTemplate";
21638
+ }
21639
+ });
20949
21640
  function ClassifierBoard({
20950
21641
  entity,
20951
21642
  completeEvent = "PUZZLE_COMPLETE",
@@ -21760,7 +22451,7 @@ function CraftingRecipe({
21760
22451
  className
21761
22452
  }) {
21762
22453
  const eventBus = useEventBus();
21763
- const handleCraft = React81.useCallback(() => {
22454
+ const handleCraft = React84.useCallback(() => {
21764
22455
  onCraft?.();
21765
22456
  if (craftEvent) {
21766
22457
  eventBus.emit(craftEvent, { output: output.label });
@@ -21777,7 +22468,7 @@ function CraftingRecipe({
21777
22468
  children: [
21778
22469
  /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-wrap items-center", children: inputs.map((ingredient, index) => {
21779
22470
  const hasSufficient = ingredient.available >= ingredient.required;
21780
- return /* @__PURE__ */ jsxs(React81.Fragment, { children: [
22471
+ return /* @__PURE__ */ jsxs(React84.Fragment, { children: [
21781
22472
  /* @__PURE__ */ jsx(Box, { className: "relative", children: /* @__PURE__ */ jsx(
21782
22473
  ItemSlot,
21783
22474
  {
@@ -21849,8 +22540,8 @@ function DPad({
21849
22540
  }) {
21850
22541
  const eventBus = useEventBus();
21851
22542
  const sizes = sizeMap15[size];
21852
- const [activeDirections, setActiveDirections] = React81.useState(/* @__PURE__ */ new Set());
21853
- const handlePress = React81.useCallback(
22543
+ const [activeDirections, setActiveDirections] = React84.useState(/* @__PURE__ */ new Set());
22544
+ const handlePress = React84.useCallback(
21854
22545
  (direction) => {
21855
22546
  setActiveDirections((prev) => new Set(prev).add(direction));
21856
22547
  if (directionEvent) eventBus.emit(`UI:${directionEvent}`, { direction, pressed: true });
@@ -21858,7 +22549,7 @@ function DPad({
21858
22549
  },
21859
22550
  [directionEvent, eventBus, onDirection]
21860
22551
  );
21861
- const handleRelease = React81.useCallback(
22552
+ const handleRelease = React84.useCallback(
21862
22553
  (direction) => {
21863
22554
  setActiveDirections((prev) => {
21864
22555
  const next = new Set(prev);
@@ -22665,8 +23356,8 @@ var init_Menu = __esm({
22665
23356
  "bottom-end": "bottom-start"
22666
23357
  };
22667
23358
  const effectivePosition = direction === "rtl" ? rtlMirror[position] ?? position : position;
22668
- const triggerChild = React81__default.isValidElement(trigger) ? trigger : /* @__PURE__ */ jsx(Typography, { variant: "small", as: "span", children: trigger });
22669
- const triggerElement = React81__default.cloneElement(
23359
+ const triggerChild = React84__default.isValidElement(trigger) ? trigger : /* @__PURE__ */ jsx(Typography, { variant: "small", as: "span", children: trigger });
23360
+ const triggerElement = React84__default.cloneElement(
22670
23361
  triggerChild,
22671
23362
  {
22672
23363
  ref: triggerRef,
@@ -22800,14 +23491,14 @@ function useDataDnd(args) {
22800
23491
  const isZone = Boolean(dragGroup || accepts || sortable);
22801
23492
  const enabled = isZone || Boolean(dndRoot);
22802
23493
  const eventBus = useEventBus();
22803
- const parentRoot = React81__default.useContext(RootCtx);
23494
+ const parentRoot = React84__default.useContext(RootCtx);
22804
23495
  const isRoot = enabled && parentRoot === null;
22805
- const zoneId = React81__default.useId();
23496
+ const zoneId = React84__default.useId();
22806
23497
  const ownGroup = dragGroup ?? accepts ?? zoneId;
22807
- const [optimisticOrders, setOptimisticOrders] = React81__default.useState(() => /* @__PURE__ */ new Map());
22808
- const optimisticOrdersRef = React81__default.useRef(optimisticOrders);
23498
+ const [optimisticOrders, setOptimisticOrders] = React84__default.useState(() => /* @__PURE__ */ new Map());
23499
+ const optimisticOrdersRef = React84__default.useRef(optimisticOrders);
22809
23500
  optimisticOrdersRef.current = optimisticOrders;
22810
- const clearOptimisticOrder = React81__default.useCallback((group) => {
23501
+ const clearOptimisticOrder = React84__default.useCallback((group) => {
22811
23502
  setOptimisticOrders((prev) => {
22812
23503
  if (!prev.has(group)) return prev;
22813
23504
  const next = new Map(prev);
@@ -22832,7 +23523,7 @@ function useDataDnd(args) {
22832
23523
  const raw = it[dndItemIdField];
22833
23524
  return String(raw ?? `__idx_${idx}`);
22834
23525
  }).join("|");
22835
- const itemIds = React81__default.useMemo(
23526
+ const itemIds = React84__default.useMemo(
22836
23527
  () => orderedItems.map((it, idx) => {
22837
23528
  const raw = it[dndItemIdField];
22838
23529
  return raw ?? `__idx_${idx}`;
@@ -22840,7 +23531,7 @@ function useDataDnd(args) {
22840
23531
  [itemIdsSignature]
22841
23532
  );
22842
23533
  const itemsContentSig = items.map((it, idx) => String(it[dndItemIdField] ?? `__${idx}`)).join("|");
22843
- React81__default.useEffect(() => {
23534
+ React84__default.useEffect(() => {
22844
23535
  const root = isRoot ? null : parentRoot;
22845
23536
  if (root) {
22846
23537
  root.clearOptimisticOrder(ownGroup);
@@ -22848,20 +23539,20 @@ function useDataDnd(args) {
22848
23539
  clearOptimisticOrder(ownGroup);
22849
23540
  }
22850
23541
  }, [itemsContentSig, ownGroup]);
22851
- const zonesRef = React81__default.useRef(/* @__PURE__ */ new Map());
22852
- const registerZone = React81__default.useCallback((zoneId2, meta2) => {
23542
+ const zonesRef = React84__default.useRef(/* @__PURE__ */ new Map());
23543
+ const registerZone = React84__default.useCallback((zoneId2, meta2) => {
22853
23544
  zonesRef.current.set(zoneId2, meta2);
22854
23545
  }, []);
22855
- const unregisterZone = React81__default.useCallback((zoneId2) => {
23546
+ const unregisterZone = React84__default.useCallback((zoneId2) => {
22856
23547
  zonesRef.current.delete(zoneId2);
22857
23548
  }, []);
22858
- const [activeDrag, setActiveDrag] = React81__default.useState(null);
22859
- const [overZoneGroup, setOverZoneGroup] = React81__default.useState(null);
22860
- const meta = React81__default.useMemo(
23549
+ const [activeDrag, setActiveDrag] = React84__default.useState(null);
23550
+ const [overZoneGroup, setOverZoneGroup] = React84__default.useState(null);
23551
+ const meta = React84__default.useMemo(
22861
23552
  () => ({ group: ownGroup, dropEvent, reorderEvent, positionEvent, itemIds, rawItems: items, idField: dndItemIdField }),
22862
23553
  [ownGroup, dropEvent, reorderEvent, positionEvent, itemIds, items, dndItemIdField]
22863
23554
  );
22864
- React81__default.useEffect(() => {
23555
+ React84__default.useEffect(() => {
22865
23556
  const target = isRoot ? null : parentRoot;
22866
23557
  if (!target) {
22867
23558
  zonesRef.current.set(zoneId, meta);
@@ -22880,7 +23571,7 @@ function useDataDnd(args) {
22880
23571
  }, [parentRoot, isRoot, zoneId, meta]);
22881
23572
  const sensors = useAlmadarDndSensors(true);
22882
23573
  const collisionDetection = almadarDndCollisionDetection;
22883
- const findZoneByItem = React81__default.useCallback(
23574
+ const findZoneByItem = React84__default.useCallback(
22884
23575
  (id) => {
22885
23576
  for (const z of zonesRef.current.values()) {
22886
23577
  if (z.itemIds.includes(id)) return z;
@@ -22889,7 +23580,7 @@ function useDataDnd(args) {
22889
23580
  },
22890
23581
  []
22891
23582
  );
22892
- React81__default.useCallback(
23583
+ React84__default.useCallback(
22893
23584
  (group) => {
22894
23585
  for (const z of zonesRef.current.values()) {
22895
23586
  if (z.group === group) return z;
@@ -22898,7 +23589,7 @@ function useDataDnd(args) {
22898
23589
  },
22899
23590
  []
22900
23591
  );
22901
- const handleDragEnd = React81__default.useCallback(
23592
+ const handleDragEnd = React84__default.useCallback(
22902
23593
  (event) => {
22903
23594
  const { active, over } = event;
22904
23595
  const activeIdStr = String(active.id);
@@ -22989,8 +23680,8 @@ function useDataDnd(args) {
22989
23680
  },
22990
23681
  [eventBus]
22991
23682
  );
22992
- const sortableData = React81__default.useMemo(() => ({ dndGroup: ownGroup }), [ownGroup]);
22993
- const SortableItem = React81__default.useCallback(
23683
+ const sortableData = React84__default.useMemo(() => ({ dndGroup: ownGroup }), [ownGroup]);
23684
+ const SortableItem = React84__default.useCallback(
22994
23685
  ({ id, children }) => {
22995
23686
  const {
22996
23687
  attributes,
@@ -23030,7 +23721,7 @@ function useDataDnd(args) {
23030
23721
  id: droppableId,
23031
23722
  data: sortableData
23032
23723
  });
23033
- const ctx = React81__default.useContext(RootCtx);
23724
+ const ctx = React84__default.useContext(RootCtx);
23034
23725
  const activeDrag2 = ctx?.activeDrag ?? null;
23035
23726
  const overZoneGroup2 = ctx?.overZoneGroup ?? null;
23036
23727
  const isThisZoneOver = overZoneGroup2 === ownGroup;
@@ -23045,7 +23736,7 @@ function useDataDnd(args) {
23045
23736
  showForeignPlaceholder,
23046
23737
  ctxAvailable: ctx != null
23047
23738
  });
23048
- React81__default.useEffect(() => {
23739
+ React84__default.useEffect(() => {
23049
23740
  dndLog.info("dropzone:isOver:change", { droppableId, group: ownGroup, isOver, isThisZoneOver, showForeignPlaceholder, activeDragSourceGroup: activeDrag2?.sourceGroup ?? null });
23050
23741
  }, [droppableId, isOver, isThisZoneOver, showForeignPlaceholder]);
23051
23742
  return /* @__PURE__ */ jsx(
@@ -23059,11 +23750,11 @@ function useDataDnd(args) {
23059
23750
  }
23060
23751
  );
23061
23752
  };
23062
- const rootContextValue = React81__default.useMemo(
23753
+ const rootContextValue = React84__default.useMemo(
23063
23754
  () => ({ registerZone, unregisterZone, activeDrag, overZoneGroup, optimisticOrders, clearOptimisticOrder }),
23064
23755
  [registerZone, unregisterZone, activeDrag, overZoneGroup, optimisticOrders, clearOptimisticOrder]
23065
23756
  );
23066
- const handleDragStart = React81__default.useCallback((event) => {
23757
+ const handleDragStart = React84__default.useCallback((event) => {
23067
23758
  const sourceZone = findZoneByItem(event.active.id);
23068
23759
  const rect = event.active.rect.current.initial;
23069
23760
  const height = rect?.height && rect.height > 0 ? rect.height : 64;
@@ -23082,7 +23773,7 @@ function useDataDnd(args) {
23082
23773
  isRoot
23083
23774
  });
23084
23775
  }, [findZoneByItem, isRoot, zoneId]);
23085
- const handleDragOver = React81__default.useCallback((event) => {
23776
+ const handleDragOver = React84__default.useCallback((event) => {
23086
23777
  const { active, over } = event;
23087
23778
  const overData = over?.data?.current;
23088
23779
  const overGroup = overData?.dndGroup ?? null;
@@ -23152,7 +23843,7 @@ function useDataDnd(args) {
23152
23843
  return next;
23153
23844
  });
23154
23845
  }, []);
23155
- const handleDragCancel = React81__default.useCallback((event) => {
23846
+ const handleDragCancel = React84__default.useCallback((event) => {
23156
23847
  setActiveDrag(null);
23157
23848
  setOverZoneGroup(null);
23158
23849
  dndLog.warn("dragCancel", {
@@ -23160,12 +23851,12 @@ function useDataDnd(args) {
23160
23851
  reason: "dnd-kit cancelled the drag (escape key, pointer interrupted, or external)"
23161
23852
  });
23162
23853
  }, []);
23163
- const handleDragEndWithCleanup = React81__default.useCallback((event) => {
23854
+ const handleDragEndWithCleanup = React84__default.useCallback((event) => {
23164
23855
  handleDragEnd(event);
23165
23856
  setActiveDrag(null);
23166
23857
  setOverZoneGroup(null);
23167
23858
  }, [handleDragEnd]);
23168
- const wrapContainer = React81__default.useCallback(
23859
+ const wrapContainer = React84__default.useCallback(
23169
23860
  (children) => {
23170
23861
  if (!enabled) return children;
23171
23862
  const strategy = layout === "grid" ? rectSortingStrategy : verticalListSortingStrategy;
@@ -23219,7 +23910,7 @@ var init_useDataDnd = __esm({
23219
23910
  init_useAlmadarDndCollision();
23220
23911
  init_Box();
23221
23912
  dndLog = createLogger("almadar:ui:dnd");
23222
- RootCtx = React81__default.createContext(null);
23913
+ RootCtx = React84__default.createContext(null);
23223
23914
  }
23224
23915
  });
23225
23916
  function renderIconInput(icon, props) {
@@ -23745,7 +24436,7 @@ function DataList({
23745
24436
  }) {
23746
24437
  const eventBus = useEventBus();
23747
24438
  const { t } = useTranslate();
23748
- const [visibleCount, setVisibleCount] = React81__default.useState(pageSize || Infinity);
24439
+ const [visibleCount, setVisibleCount] = React84__default.useState(pageSize || Infinity);
23749
24440
  const fieldDefs = fields ?? columns ?? [];
23750
24441
  const allDataRaw = Array.isArray(entity) ? entity : entity ? [entity] : [];
23751
24442
  const dnd = useDataDnd({
@@ -23764,7 +24455,7 @@ function DataList({
23764
24455
  const data = pageSize > 0 ? allData.slice(0, visibleCount) : allData;
23765
24456
  const hasMoreLocal = pageSize > 0 && visibleCount < allData.length;
23766
24457
  const hasRenderProp = typeof children === "function";
23767
- React81__default.useEffect(() => {
24458
+ React84__default.useEffect(() => {
23768
24459
  const renderItemTypeOf = typeof schemaRenderItem;
23769
24460
  const childrenTypeOf = typeof children;
23770
24461
  if (data.length > 0 && !hasRenderProp) {
@@ -23869,7 +24560,7 @@ function DataList({
23869
24560
  const items2 = data.map((item) => item);
23870
24561
  const groups2 = groupBy ? groupData(items2, groupBy) : [{ label: "", items: items2 }];
23871
24562
  const contentField = titleField?.name ?? fieldDefs[0]?.name ?? "";
23872
- return /* @__PURE__ */ jsx(VStack, { gap: "sm", className: cn("py-2", className), children: groups2.map((group, gi) => /* @__PURE__ */ jsxs(React81__default.Fragment, { children: [
24563
+ return /* @__PURE__ */ jsx(VStack, { gap: "sm", className: cn("py-2", className), children: groups2.map((group, gi) => /* @__PURE__ */ jsxs(React84__default.Fragment, { children: [
23873
24564
  group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: "my-2" }),
23874
24565
  group.items.map((itemData, index) => {
23875
24566
  const id = itemData.id || `${gi}-${index}`;
@@ -24010,7 +24701,7 @@ function DataList({
24010
24701
  className
24011
24702
  ),
24012
24703
  children: [
24013
- groups.map((group, gi) => /* @__PURE__ */ jsxs(React81__default.Fragment, { children: [
24704
+ groups.map((group, gi) => /* @__PURE__ */ jsxs(React84__default.Fragment, { children: [
24014
24705
  group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: gi > 0 ? "mt-4" : "mt-0" }),
24015
24706
  group.items.map(
24016
24707
  (itemData, index) => renderItem(itemData, index, gi === groups.length - 1 && index === group.items.length - 1)
@@ -24926,7 +25617,7 @@ var init_Grid = __esm({
24926
25617
  as: Component2 = "div"
24927
25618
  }) => {
24928
25619
  const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
24929
- return React81__default.createElement(
25620
+ return React84__default.createElement(
24930
25621
  Component2,
24931
25622
  {
24932
25623
  className: cn(
@@ -25114,8 +25805,8 @@ var init_Popover = __esm({
25114
25805
  onMouseEnter: handleOpen,
25115
25806
  onMouseLeave: handleClose
25116
25807
  };
25117
- const childElement = React81__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
25118
- const triggerElement = React81__default.cloneElement(
25808
+ const childElement = React84__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
25809
+ const triggerElement = React84__default.cloneElement(
25119
25810
  childElement,
25120
25811
  {
25121
25812
  ref: triggerRef,
@@ -25705,8 +26396,8 @@ var init_Tooltip = __esm({
25705
26396
  if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);
25706
26397
  };
25707
26398
  }, []);
25708
- const triggerElement = React81__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
25709
- const trigger = React81__default.cloneElement(triggerElement, {
26399
+ const triggerElement = React84__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
26400
+ const trigger = React84__default.cloneElement(triggerElement, {
25710
26401
  ref: triggerRef,
25711
26402
  onMouseEnter: handleMouseEnter,
25712
26403
  onMouseLeave: handleMouseLeave,
@@ -25792,7 +26483,7 @@ var init_WizardProgress = __esm({
25792
26483
  children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: normalizedSteps.map((step, index) => {
25793
26484
  const isActive = index === currentStep;
25794
26485
  const isCompleted = index < currentStep;
25795
- return /* @__PURE__ */ jsxs(React81__default.Fragment, { children: [
26486
+ return /* @__PURE__ */ jsxs(React84__default.Fragment, { children: [
25796
26487
  /* @__PURE__ */ jsx(
25797
26488
  "button",
25798
26489
  {
@@ -26753,7 +27444,7 @@ var init_ProgressDots = __esm({
26753
27444
  }
26754
27445
  });
26755
27446
  function StatBadge({
26756
- assetUrl = DEFAULT_ASSET_URL12,
27447
+ assetUrl,
26757
27448
  label,
26758
27449
  value = 0,
26759
27450
  max,
@@ -26820,7 +27511,7 @@ function StatBadge({
26820
27511
  }
26821
27512
  );
26822
27513
  }
26823
- var sizeMap17, variantMap2, DEFAULT_ASSET_URL12;
27514
+ var sizeMap17, variantMap2;
26824
27515
  var init_StatBadge = __esm({
26825
27516
  "components/game/molecules/StatBadge.tsx"() {
26826
27517
  init_cn();
@@ -26839,7 +27530,6 @@ var init_StatBadge = __esm({
26839
27530
  warning: "bg-warning/15 border-warning/40 text-foreground",
26840
27531
  danger: "bg-error/15 border-error/40 text-foreground"
26841
27532
  };
26842
- DEFAULT_ASSET_URL12 = "https://almadar-kflow-assets.web.app/shared/effects/particles/light_01.png";
26843
27533
  StatBadge.displayName = "StatBadge";
26844
27534
  }
26845
27535
  });
@@ -26856,7 +27546,7 @@ function InventoryGrid({
26856
27546
  const eventBus = useEventBus();
26857
27547
  const slotCount = totalSlots ?? items.length;
26858
27548
  const emptySlotCount = Math.max(0, slotCount - items.length);
26859
- const handleSelect = React81.useCallback(
27549
+ const handleSelect = React84.useCallback(
26860
27550
  (id) => {
26861
27551
  onSelect?.(id);
26862
27552
  if (selectEvent) {
@@ -27087,35 +27777,37 @@ function GameCanvas2D({
27087
27777
  drawEvent,
27088
27778
  fps = 60,
27089
27779
  backgroundImage = "",
27090
- assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/",
27780
+ assetBaseUrl,
27091
27781
  className
27092
27782
  }) {
27093
- const canvasRef = React81.useRef(null);
27094
- const rafRef = React81.useRef(0);
27095
- const frameRef = React81.useRef(0);
27096
- const lastTimeRef = React81.useRef(0);
27097
- const imageCache = React81.useRef(/* @__PURE__ */ new Map());
27783
+ const canvasRef = React84.useRef(null);
27784
+ const rafRef = React84.useRef(0);
27785
+ const frameRef = React84.useRef(0);
27786
+ const lastTimeRef = React84.useRef(0);
27787
+ const imageCache = React84.useRef(/* @__PURE__ */ new Map());
27098
27788
  const emit = useEmitEvent();
27099
- const onDrawRef = React81.useRef(onDraw);
27789
+ const onDrawRef = React84.useRef(onDraw);
27100
27790
  onDrawRef.current = onDraw;
27101
- const onTickRef = React81.useRef(onTick);
27791
+ const onTickRef = React84.useRef(onTick);
27102
27792
  onTickRef.current = onTick;
27103
- const tickEventRef = React81.useRef(tickEvent);
27793
+ const tickEventRef = React84.useRef(tickEvent);
27104
27794
  tickEventRef.current = tickEvent;
27105
- const drawEventRef = React81.useRef(drawEvent);
27795
+ const drawEventRef = React84.useRef(drawEvent);
27106
27796
  drawEventRef.current = drawEvent;
27107
- const emitRef = React81.useRef(emit);
27797
+ const emitRef = React84.useRef(emit);
27108
27798
  emitRef.current = emit;
27109
- const assetBaseUrlRef = React81.useRef(assetBaseUrl);
27799
+ const assetBaseUrlRef = React84.useRef(assetBaseUrl);
27110
27800
  assetBaseUrlRef.current = assetBaseUrl;
27111
- const backgroundImageRef = React81.useRef(backgroundImage);
27801
+ const backgroundImageRef = React84.useRef(backgroundImage);
27112
27802
  backgroundImageRef.current = backgroundImage;
27113
- const widthRef = React81.useRef(width);
27803
+ const widthRef = React84.useRef(width);
27114
27804
  widthRef.current = width;
27115
- const heightRef = React81.useRef(height);
27805
+ const heightRef = React84.useRef(height);
27116
27806
  heightRef.current = height;
27117
- const loadImage = React81.useCallback((url) => {
27118
- const fullUrl = url.startsWith("http") ? url : `${assetBaseUrlRef.current}${url}`;
27807
+ const loadImage = React84.useCallback((url) => {
27808
+ const base = assetBaseUrlRef.current;
27809
+ if (!url.startsWith("http") && !base) return null;
27810
+ const fullUrl = url.startsWith("http") ? url : `${base}${url}`;
27119
27811
  const cached = imageCache.current.get(fullUrl);
27120
27812
  if (cached?.complete && cached.naturalWidth > 0) return cached;
27121
27813
  if (!cached) {
@@ -27126,7 +27818,7 @@ function GameCanvas2D({
27126
27818
  }
27127
27819
  return null;
27128
27820
  }, []);
27129
- React81.useEffect(() => {
27821
+ React84.useEffect(() => {
27130
27822
  const canvas = canvasRef.current;
27131
27823
  if (!canvas) return;
27132
27824
  const ctx = canvas.getContext("2d");
@@ -27438,7 +28130,7 @@ function TurnPanel({
27438
28130
  className
27439
28131
  }) {
27440
28132
  const eventBus = useEventBus();
27441
- const handleAction = React81.useCallback(
28133
+ const handleAction = React84.useCallback(
27442
28134
  (event) => {
27443
28135
  if (event) {
27444
28136
  eventBus.emit(event, { turn: currentTurn, phase, activeTeam });
@@ -27496,7 +28188,7 @@ var init_TurnPanel = __esm({
27496
28188
  }
27497
28189
  });
27498
28190
  function EnemyPlate({
27499
- assetUrl = DEFAULT_ASSET_URL13,
28191
+ assetUrl,
27500
28192
  name = "Shadow Guard",
27501
28193
  health = 80,
27502
28194
  maxHealth = 100,
@@ -27580,7 +28272,7 @@ function EnemyPlate({
27580
28272
  }
27581
28273
  );
27582
28274
  }
27583
- var effectVariantMap2, DEFAULT_ENEMY_EFFECTS, DEFAULT_ASSET_URL13;
28275
+ var effectVariantMap2, DEFAULT_ENEMY_EFFECTS;
27584
28276
  var init_EnemyPlate = __esm({
27585
28277
  "components/game/molecules/EnemyPlate.tsx"() {
27586
28278
  "use client";
@@ -27597,7 +28289,6 @@ var init_EnemyPlate = __esm({
27597
28289
  DEFAULT_ENEMY_EFFECTS = [
27598
28290
  { icon: "flame", label: "Burn", variant: "debuff" }
27599
28291
  ];
27600
- DEFAULT_ASSET_URL13 = "https://almadar-kflow-assets.web.app/shared/portraits/shadow-legion.png";
27601
28292
  EnemyPlate.displayName = "EnemyPlate";
27602
28293
  }
27603
28294
  });
@@ -27607,7 +28298,7 @@ function UnitCommandBar({
27607
28298
  className
27608
28299
  }) {
27609
28300
  const eventBus = useEventBus();
27610
- const handleCommand = React81.useCallback(
28301
+ const handleCommand = React84.useCallback(
27611
28302
  (event) => {
27612
28303
  if (event) {
27613
28304
  eventBus.emit(event, { unitId: selectedUnitId });
@@ -27748,6 +28439,9 @@ function DialogueBox({
27748
28439
  completeEvent,
27749
28440
  choiceEvent,
27750
28441
  advanceEvent,
28442
+ backgroundImage,
28443
+ portraitUrl,
28444
+ portraitScale = 1,
27751
28445
  className
27752
28446
  }) {
27753
28447
  const eventBus = useEventBus();
@@ -27863,64 +28557,98 @@ function DialogueBox({
27863
28557
  }
27864
28558
  }, [isTyping, skipTypewriter, dialogue.choices, selectedChoice, onChoice, onAdvance, choiceEvent, advanceEvent, eventBus]);
27865
28559
  const enabledChoices = dialogue.choices?.filter((c) => !c.disabled) ?? [];
27866
- return /* @__PURE__ */ jsx(
27867
- "div",
27868
- {
27869
- className: cn(
27870
- "fixed left-0 right-0 z-40",
27871
- position === "top" ? "top-0" : "bottom-0",
27872
- className
27873
- ),
27874
- onClick: handleClick,
27875
- onKeyDown: handleKeyDown,
27876
- tabIndex: 0,
27877
- role: "dialog",
27878
- "aria-label": "Dialogue",
27879
- 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: [
27880
- dialogue.portrait && /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 p-4 border-r border-border", children: /* @__PURE__ */ jsx(
28560
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
28561
+ backgroundImage && /* @__PURE__ */ jsx(
28562
+ "div",
28563
+ {
28564
+ className: "fixed inset-0 z-20 bg-center bg-cover bg-no-repeat",
28565
+ style: { backgroundImage: `url(${backgroundImage})` },
28566
+ "aria-hidden": "true"
28567
+ }
28568
+ ),
28569
+ portraitUrl && /* @__PURE__ */ jsx(
28570
+ "div",
28571
+ {
28572
+ className: cn(
28573
+ "fixed left-1/2 -translate-x-1/2 z-30 pointer-events-none flex items-end",
28574
+ position === "top" ? "top-0" : "bottom-0"
28575
+ ),
28576
+ style: { height: `${60 * portraitScale}vh` },
28577
+ children: /* @__PURE__ */ jsx(
27881
28578
  "img",
27882
28579
  {
27883
- src: dialogue.portrait,
28580
+ src: portraitUrl,
27884
28581
  alt: dialogue.speaker,
27885
- className: "w-24 h-24 object-contain",
27886
- style: { imageRendering: "pixelated" }
28582
+ className: "h-full w-auto object-contain drop-shadow-2xl"
27887
28583
  }
27888
- ) }),
27889
- /* @__PURE__ */ jsxs("div", { className: "flex-1 p-4", children: [
27890
- /* @__PURE__ */ jsx("div", { className: "text-warning font-bold mb-2", children: dialogue.speaker }),
27891
- /* @__PURE__ */ jsxs("div", { className: "text-[var(--color-foreground)] text-lg leading-relaxed min-h-[60px]", children: [
27892
- displayedText,
27893
- isTyping && /* @__PURE__ */ jsx("span", { className: "animate-pulse", children: "\u258C" })
27894
- ] }),
27895
- !isTyping && enabledChoices.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-2", children: enabledChoices.map((choice, index) => /* @__PURE__ */ jsxs(
27896
- "button",
28584
+ )
28585
+ }
28586
+ ),
28587
+ /* @__PURE__ */ jsx(
28588
+ "div",
28589
+ {
28590
+ className: cn(
28591
+ "fixed left-0 right-0 z-40",
28592
+ position === "top" ? "top-0" : "bottom-0",
28593
+ className
28594
+ ),
28595
+ onClick: handleClick,
28596
+ onKeyDown: handleKeyDown,
28597
+ tabIndex: 0,
28598
+ role: "dialog",
28599
+ "aria-label": "Dialogue",
28600
+ children: /* @__PURE__ */ jsx("div", { className: cn(
28601
+ "mx-4 my-4 border-2 rounded-container overflow-hidden",
28602
+ backgroundImage ? "bg-black/80 backdrop-blur-sm text-white border-white/20" : "bg-[var(--color-card)] bg-opacity-95 border-border"
28603
+ ), children: /* @__PURE__ */ jsxs("div", { className: "flex", children: [
28604
+ dialogue.portrait && /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 p-4 border-r border-border", children: /* @__PURE__ */ jsx(
28605
+ "img",
27897
28606
  {
27898
- type: "button",
27899
- className: cn(
27900
- "block w-full text-left px-4 py-2 rounded-interactive transition-colors",
27901
- "hover:bg-[var(--color-surface,#374151)] focus:outline-none focus:ring-2 focus:ring-warning",
27902
- selectedChoice === index ? "bg-[var(--color-surface,#374151)] text-warning" : "bg-[var(--color-card)] text-[var(--color-foreground)]"
27903
- ),
27904
- onClick: (e) => {
27905
- e.stopPropagation();
27906
- if (choiceEvent) eventBus.emit(`UI:${choiceEvent}`, { choice });
27907
- onChoice?.(choice);
28607
+ src: dialogue.portrait,
28608
+ alt: dialogue.speaker,
28609
+ className: "w-24 h-24 object-contain",
28610
+ style: { imageRendering: "pixelated" }
28611
+ }
28612
+ ) }),
28613
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 p-4", children: [
28614
+ /* @__PURE__ */ jsx("div", { className: "text-warning font-bold mb-2", children: dialogue.speaker }),
28615
+ /* @__PURE__ */ jsxs("div", { className: cn(
28616
+ "text-lg leading-relaxed min-h-[60px]",
28617
+ backgroundImage ? "text-white" : "text-[var(--color-foreground)]"
28618
+ ), children: [
28619
+ displayedText,
28620
+ isTyping && /* @__PURE__ */ jsx("span", { className: "animate-pulse", children: "\u258C" })
28621
+ ] }),
28622
+ !isTyping && enabledChoices.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-2", children: enabledChoices.map((choice, index) => /* @__PURE__ */ jsxs(
28623
+ "button",
28624
+ {
28625
+ type: "button",
28626
+ className: cn(
28627
+ "block w-full text-left px-4 py-2 rounded-interactive transition-colors",
28628
+ "hover:bg-[var(--color-surface,#374151)] focus:outline-none focus:ring-2 focus:ring-warning",
28629
+ 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)]"
28630
+ ),
28631
+ onClick: (e) => {
28632
+ e.stopPropagation();
28633
+ if (choiceEvent) eventBus.emit(`UI:${choiceEvent}`, { choice });
28634
+ onChoice?.(choice);
28635
+ },
28636
+ children: [
28637
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground mr-2", children: [
28638
+ index + 1,
28639
+ "."
28640
+ ] }),
28641
+ choice.text
28642
+ ]
27908
28643
  },
27909
- children: [
27910
- /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground mr-2", children: [
27911
- index + 1,
27912
- "."
27913
- ] }),
27914
- choice.text
27915
- ]
27916
- },
27917
- index
27918
- )) }),
27919
- !isTyping && !dialogue.choices?.length && /* @__PURE__ */ jsx("div", { className: "mt-4 text-muted-foreground text-sm animate-pulse", children: "Press SPACE or click to continue..." })
27920
- ] })
27921
- ] }) })
27922
- }
27923
- );
28644
+ index
28645
+ )) }),
28646
+ !isTyping && !dialogue.choices?.length && /* @__PURE__ */ jsx("div", { className: "mt-4 text-muted-foreground text-sm animate-pulse", children: "Press SPACE or click to continue..." })
28647
+ ] })
28648
+ ] }) })
28649
+ }
28650
+ )
28651
+ ] });
27924
28652
  }
27925
28653
  var DEFAULT_DIALOGUE;
27926
28654
  var init_DialogueBox = __esm({
@@ -28121,7 +28849,7 @@ function GameMenu({
28121
28849
  } catch {
28122
28850
  }
28123
28851
  const eventBus = eventBusProp || eventBusFromHook;
28124
- const handleOptionClick = React81.useCallback(
28852
+ const handleOptionClick = React84.useCallback(
28125
28853
  (option) => {
28126
28854
  if (option.event && eventBus) {
28127
28855
  eventBus.emit(`UI:${option.event}`, { option });
@@ -28240,7 +28968,7 @@ function GameOverScreen({
28240
28968
  } catch {
28241
28969
  }
28242
28970
  const eventBus = eventBusProp || eventBusFromHook;
28243
- const handleActionClick = React81.useCallback(
28971
+ const handleActionClick = React84.useCallback(
28244
28972
  (action) => {
28245
28973
  if (action.event && eventBus) {
28246
28974
  eventBus.emit(`UI:${action.event}`, { action });
@@ -28447,10 +29175,10 @@ function PlatformerCanvas({
28447
29175
  canvasHeight = 400,
28448
29176
  followCamera = true,
28449
29177
  bgColor = "#5c94fc",
28450
- playerSprite = "https://almadar-kflow-assets.web.app/shared/platformer/characters/platformChar_idle.png",
29178
+ playerSprite,
28451
29179
  tileSprites,
28452
29180
  backgroundImage = "",
28453
- assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/platformer/",
29181
+ assetBaseUrl,
28454
29182
  leftEvent = "MOVE_LEFT",
28455
29183
  rightEvent = "MOVE_RIGHT",
28456
29184
  jumpEvent = "JUMP",
@@ -28464,6 +29192,7 @@ function PlatformerCanvas({
28464
29192
  const imageCache = useRef(/* @__PURE__ */ new Map());
28465
29193
  const [loadedImages, setLoadedImages] = useState(/* @__PURE__ */ new Set());
28466
29194
  const loadImage = useCallback((url) => {
29195
+ if (!url.startsWith("http") && !assetBaseUrl) return null;
28467
29196
  const fullUrl = url.startsWith("http") ? url : `${assetBaseUrl}${url}`;
28468
29197
  const cached = imageCache.current.get(fullUrl);
28469
29198
  if (cached?.complete && cached.naturalWidth > 0) {
@@ -29162,13 +29891,13 @@ var init_MapView = __esm({
29162
29891
  shadowSize: [41, 41]
29163
29892
  });
29164
29893
  L.Marker.prototype.options.icon = defaultIcon;
29165
- const { useEffect: useEffect82, useRef: useRef75, useCallback: useCallback119, useState: useState113 } = React81__default;
29894
+ const { useEffect: useEffect83, useRef: useRef78, useCallback: useCallback124, useState: useState114 } = React84__default;
29166
29895
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
29167
29896
  const { useEventBus: useEventBus4 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
29168
29897
  function MapUpdater({ centerLat, centerLng, zoom }) {
29169
29898
  const map = useMap();
29170
- const prevRef = useRef75({ centerLat, centerLng, zoom });
29171
- useEffect82(() => {
29899
+ const prevRef = useRef78({ centerLat, centerLng, zoom });
29900
+ useEffect83(() => {
29172
29901
  const prev = prevRef.current;
29173
29902
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
29174
29903
  map.setView([centerLat, centerLng], zoom);
@@ -29179,7 +29908,7 @@ var init_MapView = __esm({
29179
29908
  }
29180
29909
  function MapClickHandler({ onMapClick }) {
29181
29910
  const map = useMap();
29182
- useEffect82(() => {
29911
+ useEffect83(() => {
29183
29912
  if (!onMapClick) return;
29184
29913
  const handler = (e) => {
29185
29914
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -29207,8 +29936,8 @@ var init_MapView = __esm({
29207
29936
  showAttribution = true
29208
29937
  }) {
29209
29938
  const eventBus = useEventBus4();
29210
- const [clickedPosition, setClickedPosition] = useState113(null);
29211
- const handleMapClick = useCallback119((lat, lng) => {
29939
+ const [clickedPosition, setClickedPosition] = useState114(null);
29940
+ const handleMapClick = useCallback124((lat, lng) => {
29212
29941
  if (showClickedPin) {
29213
29942
  setClickedPosition({ lat, lng });
29214
29943
  }
@@ -29217,7 +29946,7 @@ var init_MapView = __esm({
29217
29946
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
29218
29947
  }
29219
29948
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
29220
- const handleMarkerClick = useCallback119((marker) => {
29949
+ const handleMarkerClick = useCallback124((marker) => {
29221
29950
  onMarkerClick?.(marker);
29222
29951
  if (markerClickEvent) {
29223
29952
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -30068,8 +30797,8 @@ function TableView({
30068
30797
  }) {
30069
30798
  const eventBus = useEventBus();
30070
30799
  const { t } = useTranslate();
30071
- const [visibleCount, setVisibleCount] = React81__default.useState(pageSize > 0 ? pageSize : Infinity);
30072
- const [localSelected, setLocalSelected] = React81__default.useState(/* @__PURE__ */ new Set());
30800
+ const [visibleCount, setVisibleCount] = React84__default.useState(pageSize > 0 ? pageSize : Infinity);
30801
+ const [localSelected, setLocalSelected] = React84__default.useState(/* @__PURE__ */ new Set());
30073
30802
  const colDefs = columns ?? fields ?? [];
30074
30803
  const allDataRaw = Array.isArray(entity) ? entity : entity ? [entity] : [];
30075
30804
  const dnd = useDataDnd({
@@ -30264,12 +30993,12 @@ function TableView({
30264
30993
  ]
30265
30994
  }
30266
30995
  );
30267
- return dnd.isZone ? /* @__PURE__ */ jsx(dnd.SortableItem, { id: row[idField] ?? id, children: rowInner }, id) : /* @__PURE__ */ jsx(React81__default.Fragment, { children: rowInner }, id);
30996
+ return dnd.isZone ? /* @__PURE__ */ jsx(dnd.SortableItem, { id: row[idField] ?? id, children: rowInner }, id) : /* @__PURE__ */ jsx(React84__default.Fragment, { children: rowInner }, id);
30268
30997
  };
30269
30998
  const items = data.map((row) => row);
30270
30999
  const groups = groupBy ? groupData2(items, groupBy) : [{ label: "", items }];
30271
31000
  let runningIndex = 0;
30272
- const body = /* @__PURE__ */ jsx(Box, { role: "rowgroup", children: groups.map((group, gi) => /* @__PURE__ */ jsxs(React81__default.Fragment, { children: [
31001
+ const body = /* @__PURE__ */ jsx(Box, { role: "rowgroup", children: groups.map((group, gi) => /* @__PURE__ */ jsxs(React84__default.Fragment, { children: [
30273
31002
  group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: gi > 0 ? "mt-3" : "mt-0" }),
30274
31003
  group.items.map((row) => renderRow(row, runningIndex++))
30275
31004
  ] }, gi)) });
@@ -31626,7 +32355,7 @@ var init_StepFlow = __esm({
31626
32355
  className
31627
32356
  }) => {
31628
32357
  if (orientation === "vertical") {
31629
- return /* @__PURE__ */ jsx(VStack, { gap: "none", className: cn("w-full", className), children: steps.map((step, index) => /* @__PURE__ */ jsx(React81__default.Fragment, { children: /* @__PURE__ */ jsxs(HStack, { gap: "md", align: "start", className: "w-full", children: [
32358
+ return /* @__PURE__ */ jsx(VStack, { gap: "none", className: cn("w-full", className), children: steps.map((step, index) => /* @__PURE__ */ jsx(React84__default.Fragment, { children: /* @__PURE__ */ jsxs(HStack, { gap: "md", align: "start", className: "w-full", children: [
31630
32359
  /* @__PURE__ */ jsxs(VStack, { gap: "none", align: "center", children: [
31631
32360
  /* @__PURE__ */ jsx(StepCircle, { step, index }),
31632
32361
  showConnectors && index < steps.length - 1 && /* @__PURE__ */ jsx(Box, { className: "w-px h-8 bg-border" })
@@ -31637,7 +32366,7 @@ var init_StepFlow = __esm({
31637
32366
  ] })
31638
32367
  ] }) }, index)) });
31639
32368
  }
31640
- 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(React81__default.Fragment, { children: [
32369
+ 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(React84__default.Fragment, { children: [
31641
32370
  /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", className: "flex-1 w-full md:w-auto", children: [
31642
32371
  /* @__PURE__ */ jsx(StepCircle, { step, index }),
31643
32372
  /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-center", children: step.title }),
@@ -32622,7 +33351,7 @@ var init_LikertScale = __esm({
32622
33351
  md: "text-base",
32623
33352
  lg: "text-lg"
32624
33353
  };
32625
- LikertScale = React81__default.forwardRef(
33354
+ LikertScale = React84__default.forwardRef(
32626
33355
  ({
32627
33356
  question,
32628
33357
  options = DEFAULT_LIKERT_OPTIONS,
@@ -32634,7 +33363,7 @@ var init_LikertScale = __esm({
32634
33363
  variant = "radios",
32635
33364
  className
32636
33365
  }, ref) => {
32637
- const groupId = React81__default.useId();
33366
+ const groupId = React84__default.useId();
32638
33367
  const eventBus = useEventBus();
32639
33368
  const handleSelect = useCallback(
32640
33369
  (next) => {
@@ -34916,7 +35645,7 @@ var init_DocBreadcrumb = __esm({
34916
35645
  "aria-label": t("aria.breadcrumb"),
34917
35646
  children: /* @__PURE__ */ jsx(HStack, { gap: "xs", align: "center", wrap: true, children: items.map((item, idx) => {
34918
35647
  const isLast = idx === items.length - 1;
34919
- return /* @__PURE__ */ jsxs(React81__default.Fragment, { children: [
35648
+ return /* @__PURE__ */ jsxs(React84__default.Fragment, { children: [
34920
35649
  idx > 0 && /* @__PURE__ */ jsx(
34921
35650
  Icon,
34922
35651
  {
@@ -35785,7 +36514,7 @@ var init_MiniStateMachine = __esm({
35785
36514
  const x = 2 + i * (NODE_W + GAP + ARROW_W + GAP);
35786
36515
  const tc = transitionCounts[s.name] ?? 0;
35787
36516
  const role = getStateRole(s.name, s.isInitial, s.isTerminal, tc, maxTC);
35788
- return /* @__PURE__ */ jsxs(React81__default.Fragment, { children: [
36517
+ return /* @__PURE__ */ jsxs(React84__default.Fragment, { children: [
35789
36518
  /* @__PURE__ */ jsx(
35790
36519
  AvlState,
35791
36520
  {
@@ -35989,7 +36718,7 @@ var init_PageHeader = __esm({
35989
36718
  info: "bg-info/10 text-info"
35990
36719
  };
35991
36720
  return /* @__PURE__ */ jsxs(Box, { className: cn("mb-6", className), children: [
35992
- 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(React81__default.Fragment, { children: [
36721
+ 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(React84__default.Fragment, { children: [
35993
36722
  idx > 0 && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: "/" }),
35994
36723
  crumb.href ? /* @__PURE__ */ jsx(
35995
36724
  "a",
@@ -36671,8 +37400,8 @@ var init_WizardContainer = __esm({
36671
37400
  return void 0;
36672
37401
  if (typeof controlledStep === "number") return controlledStep;
36673
37402
  if (typeof controlledStep === "string") return parseInt(controlledStep, 10);
36674
- const num2 = Number(controlledStep);
36675
- return isNaN(num2) ? void 0 : num2;
37403
+ const num3 = Number(controlledStep);
37404
+ return isNaN(num3) ? void 0 : num3;
36676
37405
  })();
36677
37406
  const currentStep = normalizedControlledStep !== void 0 ? normalizedControlledStep : internalStep;
36678
37407
  const totalSteps = steps.length;
@@ -36718,7 +37447,7 @@ var init_WizardContainer = __esm({
36718
37447
  const isCompleted = index < currentStep;
36719
37448
  const stepKey = step.id ?? step.tabId ?? `step-${index}`;
36720
37449
  const stepTitle = step.title ?? step.name ?? `Step ${index + 1}`;
36721
- return /* @__PURE__ */ jsxs(React81__default.Fragment, { children: [
37450
+ return /* @__PURE__ */ jsxs(React84__default.Fragment, { children: [
36722
37451
  /* @__PURE__ */ jsx(
36723
37452
  Button,
36724
37453
  {
@@ -37650,6 +38379,8 @@ var init_GraphCanvas = __esm({
37650
38379
  selectedNodeId,
37651
38380
  repulsion = 800,
37652
38381
  linkDistance = 100,
38382
+ nodeSpacing = 28,
38383
+ linkOpacity = 0.18,
37653
38384
  layout = "force",
37654
38385
  entity,
37655
38386
  isLoading = false,
@@ -37809,6 +38540,25 @@ var init_GraphCanvas = __esm({
37809
38540
  node.x = Math.max(30, Math.min(w - 30, node.x));
37810
38541
  node.y = Math.max(30, Math.min(h - 30, node.y));
37811
38542
  }
38543
+ for (let i = 0; i < nodes.length; i++) {
38544
+ for (let j = i + 1; j < nodes.length; j++) {
38545
+ const a = nodes[i];
38546
+ const b = nodes[j];
38547
+ const dx = b.x - a.x;
38548
+ const dy = b.y - a.y;
38549
+ const dist = Math.sqrt(dx * dx + dy * dy) || 1;
38550
+ const minDist = (a.size || 8) + (b.size || 8) + nodeSpacing;
38551
+ if (dist < minDist) {
38552
+ const push2 = (minDist - dist) / 2;
38553
+ const ux = dx / dist;
38554
+ const uy = dy / dist;
38555
+ a.x = Math.max(30, Math.min(w - 30, a.x - ux * push2));
38556
+ a.y = Math.max(30, Math.min(h - 30, a.y - uy * push2));
38557
+ b.x = Math.max(30, Math.min(w - 30, b.x + ux * push2));
38558
+ b.y = Math.max(30, Math.min(h - 30, b.y + uy * push2));
38559
+ }
38560
+ }
38561
+ }
37812
38562
  iterations++;
37813
38563
  forceUpdate((n) => n + 1);
37814
38564
  if (iterations < maxIterations) {
@@ -37822,7 +38572,7 @@ var init_GraphCanvas = __esm({
37822
38572
  return () => {
37823
38573
  cancelAnimationFrame(animRef.current);
37824
38574
  };
37825
- }, [propNodes, propEdges, layout, repulsion, linkDistance, logicalW, height]);
38575
+ }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, logicalW, height]);
37826
38576
  useEffect(() => {
37827
38577
  const canvas = canvasRef.current;
37828
38578
  if (!canvas) return;
@@ -37851,11 +38601,11 @@ var init_GraphCanvas = __esm({
37851
38601
  const target = nodes.find((n) => n.id === edge.target);
37852
38602
  if (!source || !target) continue;
37853
38603
  const incident = !!hoveredNode && (edge.source === hoveredNode || edge.target === hoveredNode);
37854
- ctx.globalAlpha = hoveredNode ? incident ? 1 : 0.12 : 1;
38604
+ ctx.globalAlpha = hoveredNode ? incident ? 1 : 0.05 : linkOpacity;
37855
38605
  ctx.beginPath();
37856
38606
  ctx.moveTo(source.x, source.y);
37857
38607
  ctx.lineTo(target.x, target.y);
37858
- ctx.strokeStyle = incident ? accentColor : edge.color || "#88888866";
38608
+ ctx.strokeStyle = incident ? accentColor : edge.color || "#888888";
37859
38609
  ctx.lineWidth = incident ? 2 : edge.weight ? Math.max(1, edge.weight) : 1;
37860
38610
  ctx.stroke();
37861
38611
  if (edge.label && showLabels) {
@@ -39219,7 +39969,7 @@ var init_DetailPanel = __esm({
39219
39969
  }
39220
39970
  });
39221
39971
  function extractTitle(children) {
39222
- if (!React81__default.isValidElement(children)) return void 0;
39972
+ if (!React84__default.isValidElement(children)) return void 0;
39223
39973
  const props = children.props;
39224
39974
  if (typeof props.title === "string") {
39225
39975
  return props.title;
@@ -39274,7 +40024,7 @@ function LinearView({
39274
40024
  /* @__PURE__ */ jsx(HStack, { className: "flex-wrap items-center", gap: "xs", children: trait.states.map((state, i) => {
39275
40025
  const isDone = i < currentIdx;
39276
40026
  const isCurrent = i === currentIdx;
39277
- return /* @__PURE__ */ jsxs(React81__default.Fragment, { children: [
40027
+ return /* @__PURE__ */ jsxs(React84__default.Fragment, { children: [
39278
40028
  i > 0 && /* @__PURE__ */ jsx(
39279
40029
  Typography,
39280
40030
  {
@@ -40224,12 +40974,12 @@ var init_Form = __esm({
40224
40974
  const isSchemaEntity = isOrbitalEntitySchema(entity);
40225
40975
  const resolvedEntity = isSchemaEntity ? entity : void 0;
40226
40976
  const entityName = typeof entity === "string" ? entity : resolvedEntity?.name;
40227
- const normalizedInitialData = React81__default.useMemo(() => {
40977
+ const normalizedInitialData = React84__default.useMemo(() => {
40228
40978
  const entityRowAsInitial = isPlainEntityRow(entity) ? entity : void 0;
40229
40979
  const callerInitial = initialData !== null && typeof initialData === "object" && !Array.isArray(initialData) ? initialData : {};
40230
40980
  return entityRowAsInitial !== void 0 ? { ...entityRowAsInitial, ...callerInitial } : callerInitial;
40231
40981
  }, [entity, initialData]);
40232
- const entityDerivedFields = React81__default.useMemo(() => {
40982
+ const entityDerivedFields = React84__default.useMemo(() => {
40233
40983
  if (fields && fields.length > 0) return void 0;
40234
40984
  if (!resolvedEntity) return void 0;
40235
40985
  return resolvedEntity.fields.map(
@@ -40249,16 +40999,16 @@ var init_Form = __esm({
40249
40999
  const conditionalFields = typeof conditionalFieldsRaw === "boolean" ? {} : conditionalFieldsRaw;
40250
41000
  const hiddenCalculations = typeof hiddenCalculationsRaw === "boolean" ? [] : hiddenCalculationsRaw;
40251
41001
  const violationTriggers = typeof violationTriggersRaw === "boolean" ? [] : violationTriggersRaw;
40252
- const [formData, setFormData] = React81__default.useState(
41002
+ const [formData, setFormData] = React84__default.useState(
40253
41003
  normalizedInitialData
40254
41004
  );
40255
- const [collapsedSections, setCollapsedSections] = React81__default.useState(
41005
+ const [collapsedSections, setCollapsedSections] = React84__default.useState(
40256
41006
  /* @__PURE__ */ new Set()
40257
41007
  );
40258
- const [submitError, setSubmitError] = React81__default.useState(null);
40259
- const formRef = React81__default.useRef(null);
41008
+ const [submitError, setSubmitError] = React84__default.useState(null);
41009
+ const formRef = React84__default.useRef(null);
40260
41010
  const formMode = props.mode;
40261
- const mountedRef = React81__default.useRef(false);
41011
+ const mountedRef = React84__default.useRef(false);
40262
41012
  if (!mountedRef.current) {
40263
41013
  mountedRef.current = true;
40264
41014
  debug("forms", "mount", {
@@ -40271,7 +41021,7 @@ var init_Form = __esm({
40271
41021
  });
40272
41022
  }
40273
41023
  const shouldShowCancel = showCancel ?? (fields && fields.length > 0);
40274
- const evalContext = React81__default.useMemo(
41024
+ const evalContext = React84__default.useMemo(
40275
41025
  () => ({
40276
41026
  formValues: formData,
40277
41027
  globalVariables: externalContext?.globalVariables ?? {},
@@ -40280,7 +41030,7 @@ var init_Form = __esm({
40280
41030
  }),
40281
41031
  [formData, externalContext]
40282
41032
  );
40283
- React81__default.useEffect(() => {
41033
+ React84__default.useEffect(() => {
40284
41034
  debug("forms", "initialData-sync", {
40285
41035
  mode: formMode,
40286
41036
  normalizedInitialData,
@@ -40291,7 +41041,7 @@ var init_Form = __esm({
40291
41041
  setFormData(normalizedInitialData);
40292
41042
  }
40293
41043
  }, [normalizedInitialData]);
40294
- const processCalculations = React81__default.useCallback(
41044
+ const processCalculations = React84__default.useCallback(
40295
41045
  (changedFieldId, newFormData) => {
40296
41046
  if (!hiddenCalculations.length) return;
40297
41047
  const context = {
@@ -40316,7 +41066,7 @@ var init_Form = __esm({
40316
41066
  },
40317
41067
  [hiddenCalculations, externalContext, eventBus]
40318
41068
  );
40319
- const checkViolations = React81__default.useCallback(
41069
+ const checkViolations = React84__default.useCallback(
40320
41070
  (changedFieldId, newFormData) => {
40321
41071
  if (!violationTriggers.length) return;
40322
41072
  const context = {
@@ -40354,7 +41104,7 @@ var init_Form = __esm({
40354
41104
  processCalculations(name, newFormData);
40355
41105
  checkViolations(name, newFormData);
40356
41106
  };
40357
- const isFieldVisible = React81__default.useCallback(
41107
+ const isFieldVisible = React84__default.useCallback(
40358
41108
  (fieldName) => {
40359
41109
  const condition = conditionalFields[fieldName];
40360
41110
  if (!condition) return true;
@@ -40362,7 +41112,7 @@ var init_Form = __esm({
40362
41112
  },
40363
41113
  [conditionalFields, evalContext]
40364
41114
  );
40365
- const isSectionVisible = React81__default.useCallback(
41115
+ const isSectionVisible = React84__default.useCallback(
40366
41116
  (section) => {
40367
41117
  if (!section.condition) return true;
40368
41118
  return Boolean(evaluateFormExpression(section.condition, evalContext));
@@ -40438,7 +41188,7 @@ var init_Form = __esm({
40438
41188
  eventBus.emit(`UI:${onCancel}`);
40439
41189
  }
40440
41190
  };
40441
- const renderField = React81__default.useCallback(
41191
+ const renderField = React84__default.useCallback(
40442
41192
  (field) => {
40443
41193
  const fieldName = field.name || field.field;
40444
41194
  if (!fieldName) return null;
@@ -40459,7 +41209,7 @@ var init_Form = __esm({
40459
41209
  [formData, isFieldVisible, relationsData, relationsLoading, isLoading]
40460
41210
  );
40461
41211
  const effectiveFields = entityDerivedFields ?? fields;
40462
- const normalizedFields = React81__default.useMemo(() => {
41212
+ const normalizedFields = React84__default.useMemo(() => {
40463
41213
  if (!effectiveFields || effectiveFields.length === 0) return [];
40464
41214
  return effectiveFields.map((field) => {
40465
41215
  if (typeof field === "string") {
@@ -40482,7 +41232,7 @@ var init_Form = __esm({
40482
41232
  return field;
40483
41233
  });
40484
41234
  }, [effectiveFields, resolvedEntity]);
40485
- const schemaFields = React81__default.useMemo(() => {
41235
+ const schemaFields = React84__default.useMemo(() => {
40486
41236
  if (normalizedFields.length === 0) return null;
40487
41237
  if (isDebugEnabled()) {
40488
41238
  debugGroup(`Form: ${entityName || "unknown"}`);
@@ -40492,7 +41242,7 @@ var init_Form = __esm({
40492
41242
  }
40493
41243
  return normalizedFields.map(renderField).filter(Boolean);
40494
41244
  }, [normalizedFields, renderField, entityName, conditionalFields]);
40495
- const sectionElements = React81__default.useMemo(() => {
41245
+ const sectionElements = React84__default.useMemo(() => {
40496
41246
  if (!sections || sections.length === 0) return null;
40497
41247
  return sections.map((section) => {
40498
41248
  if (!isSectionVisible(section)) {
@@ -41812,12 +42562,18 @@ function ModelLoader({
41812
42562
  });
41813
42563
  return cloned;
41814
42564
  }, [loadedModel, castShadow, receiveShadow]);
42565
+ const normFactor = useMemo(() => {
42566
+ if (!model) return 1;
42567
+ const box = new THREE3.Box3().setFromObject(model);
42568
+ const size = new THREE3.Vector3();
42569
+ box.getSize(size);
42570
+ const maxDim = Math.max(size.x, size.y, size.z);
42571
+ return maxDim > 0 && Number.isFinite(maxDim) ? 1 / maxDim : 1;
42572
+ }, [model]);
41815
42573
  const scaleArray = useMemo(() => {
41816
- if (typeof scale === "number") {
41817
- return [scale, scale, scale];
41818
- }
41819
- return scale;
41820
- }, [scale]);
42574
+ const base = typeof scale === "number" ? [scale, scale, scale] : scale;
42575
+ return [base[0] * normFactor, base[1] * normFactor, base[2] * normFactor];
42576
+ }, [scale, normFactor]);
41821
42577
  const rotationRad = useMemo(() => {
41822
42578
  return [
41823
42579
  rotation[0] * Math.PI / 180,
@@ -42028,6 +42784,29 @@ var init_GameCanvas3D2 = __esm({
42028
42784
  const controlsRef = useRef(null);
42029
42785
  const [hoveredTile, setHoveredTile] = useState(null);
42030
42786
  const [internalError, setInternalError] = useState(null);
42787
+ useEffect(() => {
42788
+ const el = containerRef.current;
42789
+ if (!el) return;
42790
+ let node = el;
42791
+ let depth = 0;
42792
+ while (node && depth < 8) {
42793
+ const cs = window.getComputedStyle(node);
42794
+ const rect = node.getBoundingClientRect();
42795
+ console.log("[almadar:ui:game:3d-height]", {
42796
+ depth,
42797
+ tag: node.tagName,
42798
+ id: node.id || void 0,
42799
+ className: node.className?.slice?.(0, 60) || void 0,
42800
+ rectHeight: rect.height,
42801
+ computedHeight: cs.height,
42802
+ computedMinHeight: cs.minHeight,
42803
+ computedFlex: cs.flex,
42804
+ computedOverflow: cs.overflow
42805
+ });
42806
+ node = node.parentElement;
42807
+ depth++;
42808
+ }
42809
+ }, []);
42031
42810
  const { sheetUrls: atlasSheetUrls, resolveUnitFrame } = useUnitSpriteAtlas(units);
42032
42811
  const preloadUrls = useMemo(() => [...preloadAssets, ...atlasSheetUrls], [preloadAssets, atlasSheetUrls]);
42033
42812
  const { isLoading: assetsLoading, progress, loaded, total } = useAssetLoader({
@@ -42417,7 +43196,7 @@ var init_GameCanvas3D2 = __esm({
42417
43196
  {
42418
43197
  ref: containerRef,
42419
43198
  className: cn("game-canvas-3d relative w-full overflow-hidden", className),
42420
- style: { minHeight: "85vh" },
43199
+ style: { height: "85vh" },
42421
43200
  "data-orientation": orientation,
42422
43201
  "data-camera-mode": cameraMode,
42423
43202
  "data-overlay": overlay,
@@ -42432,7 +43211,7 @@ var init_GameCanvas3D2 = __esm({
42432
43211
  near: 0.1,
42433
43212
  far: 1e3
42434
43213
  },
42435
- style: { background: backgroundColor, height: "85vh", width: "100%" },
43214
+ style: { background: backgroundColor, position: "absolute", inset: 0 },
42436
43215
  onClick: (e) => {
42437
43216
  if (e.target === e.currentTarget) {
42438
43217
  eventHandlers.handleCanvasClick(e);
@@ -43403,7 +44182,7 @@ var init_List = __esm({
43403
44182
  if (entity && typeof entity === "object" && "id" in entity) return [entity];
43404
44183
  return [];
43405
44184
  }, [entity]);
43406
- const getItemActions = React81__default.useCallback(
44185
+ const getItemActions = React84__default.useCallback(
43407
44186
  (item) => {
43408
44187
  if (!itemActions) return [];
43409
44188
  if (typeof itemActions === "function") {
@@ -43879,7 +44658,7 @@ var init_MediaGallery = __esm({
43879
44658
  [selectable, selectedItems, selectionEvent, eventBus]
43880
44659
  );
43881
44660
  const entityData = Array.isArray(entity) ? entity : [];
43882
- const items = React81__default.useMemo(() => {
44661
+ const items = React84__default.useMemo(() => {
43883
44662
  if (propItems) return propItems;
43884
44663
  if (entityData.length === 0) return [];
43885
44664
  return entityData.map((record, idx) => ({
@@ -44040,7 +44819,7 @@ var init_MediaGallery = __esm({
44040
44819
  }
44041
44820
  });
44042
44821
  function extractTitle2(children) {
44043
- if (!React81__default.isValidElement(children)) return void 0;
44822
+ if (!React84__default.isValidElement(children)) return void 0;
44044
44823
  const props = children.props;
44045
44824
  if (typeof props.title === "string") {
44046
44825
  return props.title;
@@ -44616,31 +45395,33 @@ var init_PricingPageTemplate = __esm({
44616
45395
  PricingPageTemplate.displayName = "PricingPageTemplate";
44617
45396
  }
44618
45397
  });
44619
- function dungeonTerrain(x, y, stairsX, stairsY) {
45398
+ function resolveManifestUrl3(manifest, relative) {
45399
+ if (relative == null) return void 0;
45400
+ if (/^https?:\/\//.test(relative)) return relative;
45401
+ const base = manifest?.baseUrl;
45402
+ if (base == null) return relative;
45403
+ const cleanBase = base.replace(/\/$/, "");
45404
+ const cleanRel = relative.replace(/^\//, "");
45405
+ return `${cleanBase}/${cleanRel}`;
45406
+ }
45407
+ function dungeonTerrain(x, y, stairsX, stairsY, manifest) {
44620
45408
  const isBorder = x === 0 || y === 0 || x === DUNGEON_GRID_W - 1 || y === DUNGEON_GRID_H - 1;
44621
45409
  const isPillar = x % 4 === 0 && y % 4 === 0 && !isBorder;
44622
45410
  const isWall = isBorder || isPillar;
45411
+ const terrains = manifest?.terrains;
44623
45412
  if (x === stairsX && y === stairsY) {
44624
- return { terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` };
45413
+ return { terrain: "stairs", passable: true, terrainSprite: resolveManifestUrl3(manifest, terrains?.stairs) };
44625
45414
  }
44626
45415
  if (isWall) {
44627
- return { terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` };
44628
- }
44629
- const variant = (x * 3 + y * 7) % 5;
44630
- const sprites = [
44631
- `${CDN4}/isometric-dungeon/Isometric/dirt_E.png`,
44632
- `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png`,
44633
- `${CDN4}/isometric-dungeon/Isometric/planks_E.png`,
44634
- `${CDN4}/isometric-dungeon/Isometric/stoneTile_E.png`,
44635
- `${CDN4}/isometric-dungeon/Isometric/dirt_E.png`
44636
- ];
44637
- return { terrain: "floor", passable: true, terrainSprite: sprites[variant] };
45416
+ return { terrain: "wall", passable: false, terrainSprite: resolveManifestUrl3(manifest, terrains?.wall) };
45417
+ }
45418
+ return { terrain: "floor", passable: true, terrainSprite: resolveManifestUrl3(manifest, terrains?.floor) };
44638
45419
  }
44639
- function buildDefaultDungeonTiles(stairsX, stairsY) {
45420
+ function buildDefaultDungeonTiles(stairsX, stairsY, manifest) {
44640
45421
  const tiles = [];
44641
45422
  for (let y = 0; y < DUNGEON_GRID_H; y++) {
44642
45423
  for (let x = 0; x < DUNGEON_GRID_W; x++) {
44643
- tiles.push({ x, y, ...dungeonTerrain(x, y, stairsX, stairsY) });
45424
+ tiles.push({ x, y, ...dungeonTerrain(x, y, stairsX, stairsY, manifest) });
44644
45425
  }
44645
45426
  }
44646
45427
  return tiles;
@@ -44670,20 +45451,48 @@ function RoguelikeBoard({
44670
45451
  className
44671
45452
  }) {
44672
45453
  const board = boardEntity(entity) ?? {};
44673
- const resolvedStairsX = propStairsX ?? num(board.stairsX, 14);
44674
- const resolvedStairsY = propStairsY ?? num(board.stairsY, 14);
44675
- const tiles = propTiles ?? (Array.isArray(board.tiles) && board.tiles.length >= DUNGEON_GRID_W * DUNGEON_GRID_H ? board.tiles : buildDefaultDungeonTiles(resolvedStairsX, resolvedStairsY));
44676
- const enemies = propEnemies ?? (Array.isArray(board.enemies) ? board.enemies : DEFAULT_ENEMIES);
44677
- const items = propItems ?? (Array.isArray(board.items) ? board.items : DEFAULT_ITEMS);
44678
- const player = propPlayer ?? board.player ?? { x: 1, y: 1 };
45454
+ const assetManifest = propAssetManifest ?? board.assetManifest;
45455
+ const stairsX = propStairsX ?? num(board.stairsX, 14);
45456
+ const stairsY = propStairsY ?? num(board.stairsY, 14);
45457
+ const entityTiles = useMemo(
45458
+ () => rows(board.tiles).map((r) => ({
45459
+ x: num(r.x),
45460
+ y: num(r.y),
45461
+ terrain: r.terrain == null ? void 0 : str(r.terrain),
45462
+ terrainSprite: r.terrainSprite == null ? void 0 : str(r.terrainSprite),
45463
+ passable: r.passable !== false
45464
+ })),
45465
+ [board.tiles]
45466
+ );
45467
+ const tiles = propTiles ?? (entityTiles.length >= DUNGEON_GRID_W * DUNGEON_GRID_H ? entityTiles : buildDefaultDungeonTiles(stairsX, stairsY, assetManifest));
45468
+ const entityEnemies = useMemo(
45469
+ () => rows(board.enemies).map((r) => ({
45470
+ id: str(r.id),
45471
+ x: num(r.x),
45472
+ y: num(r.y),
45473
+ hp: num(r.hp),
45474
+ attack: num(r.attack)
45475
+ })),
45476
+ [board.enemies]
45477
+ );
45478
+ const enemies = propEnemies ?? (entityEnemies.length > 0 ? entityEnemies : DEFAULT_ENEMIES);
45479
+ const entityItems = useMemo(
45480
+ () => rows(board.items).map((r) => ({
45481
+ id: str(r.id),
45482
+ x: num(r.x),
45483
+ y: num(r.y),
45484
+ kind: str(r.kind) || "health_potion"
45485
+ })),
45486
+ [board.items]
45487
+ );
45488
+ const items = propItems ?? (entityItems.length > 0 ? entityItems : DEFAULT_ITEMS);
45489
+ const player = propPlayer ?? (board.player == null ? { x: 1, y: 1 } : vec2(board.player));
44679
45490
  const playerHp = propPlayerHp ?? num(board.playerHp, 10);
44680
45491
  const playerMaxHp = propPlayerMaxHp ?? num(board.playerMaxHp, 10);
44681
45492
  const playerAttack = propPlayerAttack ?? num(board.playerAttack, 3);
44682
45493
  const depth = propDepth ?? num(board.depth, 1);
44683
45494
  const result = propResult ?? (str(board.result) || "none");
44684
45495
  const phase = propPhase ?? (str(board.phase) || "player_turn");
44685
- propStairsX ?? num(board.stairsX, 14);
44686
- propStairsY ?? num(board.stairsY, 14);
44687
45496
  const eventBus = useEventBus();
44688
45497
  const { t } = useTranslate();
44689
45498
  const [hoveredTile, setHoveredTile] = useState(null);
@@ -44730,7 +45539,7 @@ function RoguelikeBoard({
44730
45539
  health: playerHp,
44731
45540
  maxHealth: playerMaxHp,
44732
45541
  unitType: "player",
44733
- sprite: `${CDN4}/isometric-dungeon/Characters/Male/Male_0_Idle0.png`
45542
+ sprite: resolveManifestUrl3(assetManifest, assetManifest?.units?.player)
44734
45543
  },
44735
45544
  ...liveEnemies.map((e) => ({
44736
45545
  id: e.id,
@@ -44740,19 +45549,19 @@ function RoguelikeBoard({
44740
45549
  health: e.hp,
44741
45550
  maxHealth: 6,
44742
45551
  unitType: "enemy",
44743
- sprite: `${CDN4}/sprite-sheets/shadow-legion-sprite-sheet-sw.png`
45552
+ sprite: resolveManifestUrl3(assetManifest, assetManifest?.units?.enemy)
44744
45553
  }))
44745
45554
  ];
44746
- }, [player, enemies, playerHp, playerMaxHp, t]);
45555
+ }, [player, enemies, playerHp, playerMaxHp, assetManifest, t]);
44747
45556
  const isoFeatures = useMemo(
44748
45557
  () => items.map((it) => ({
44749
45558
  id: it.id,
44750
45559
  x: it.x,
44751
45560
  y: it.y,
44752
45561
  type: it.kind,
44753
- sprite: FEATURE_SPRITE[it.kind] ?? FEATURE_SPRITE.health_potion
45562
+ sprite: resolveManifestUrl3(assetManifest, assetManifest?.features?.[it.kind])
44754
45563
  })),
44755
- [items]
45564
+ [items, assetManifest]
44756
45565
  );
44757
45566
  const validMoves = useMemo(() => {
44758
45567
  if (result !== "none" || phase !== "player_turn") return [];
@@ -44811,8 +45620,8 @@ function RoguelikeBoard({
44811
45620
  onTileHover: (x, y) => setHoveredTile({ x, y }),
44812
45621
  onTileLeave: () => setHoveredTile(null),
44813
45622
  scale,
44814
- assetBaseUrl: propAssetManifest?.baseUrl ?? CDN4,
44815
- assetManifest: propAssetManifest,
45623
+ assetBaseUrl: assetManifest?.baseUrl,
45624
+ assetManifest,
44816
45625
  unitScale,
44817
45626
  spriteHeightRatio,
44818
45627
  spriteMaxWidthRatio
@@ -44859,7 +45668,7 @@ function RoguelikeBoard({
44859
45668
  ] }) })
44860
45669
  ] });
44861
45670
  }
44862
- var CDN4, DUNGEON_GRID_W, DUNGEON_GRID_H, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
45671
+ var DUNGEON_GRID_W, DUNGEON_GRID_H, DEFAULT_ENEMIES, DEFAULT_ITEMS;
44863
45672
  var init_RoguelikeBoard = __esm({
44864
45673
  "components/game/organisms/RoguelikeBoard.tsx"() {
44865
45674
  "use client";
@@ -44871,10 +45680,8 @@ var init_RoguelikeBoard = __esm({
44871
45680
  init_Stack();
44872
45681
  init_IsometricCanvas();
44873
45682
  init_boardEntity();
44874
- CDN4 = "https://almadar-kflow-assets.web.app/shared";
44875
45683
  DUNGEON_GRID_W = 16;
44876
45684
  DUNGEON_GRID_H = 16;
44877
- buildDefaultDungeonTiles(14, 14);
44878
45685
  DEFAULT_ENEMIES = [
44879
45686
  { id: "e1", x: 5, y: 2, hp: 5, attack: 2 },
44880
45687
  { id: "e2", x: 9, y: 6, hp: 4, attack: 1 },
@@ -44886,11 +45693,6 @@ var init_RoguelikeBoard = __esm({
44886
45693
  { id: "i2", x: 7, y: 9, kind: "sword" },
44887
45694
  { id: "i3", x: 11, y: 5, kind: "shield" }
44888
45695
  ];
44889
- FEATURE_SPRITE = {
44890
- health_potion: `${CDN4}/isometric-dungeon/Isometric/chestClosed_E.png`,
44891
- sword: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`,
44892
- shield: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`
44893
- };
44894
45696
  RoguelikeBoard.displayName = "RoguelikeBoard";
44895
45697
  }
44896
45698
  });
@@ -44928,40 +45730,40 @@ function RoguelikeTemplate({
44928
45730
  }
44929
45731
  );
44930
45732
  }
44931
- var CDN5, DEFAULT_ROGUELIKE_TILES, DEFAULT_ROGUELIKE_MANIFEST;
45733
+ var CDN4, DEFAULT_ROGUELIKE_TILES, DEFAULT_ROGUELIKE_MANIFEST;
44932
45734
  var init_RoguelikeTemplate = __esm({
44933
45735
  "components/game/templates/RoguelikeTemplate.tsx"() {
44934
45736
  init_RoguelikeBoard();
44935
- CDN5 = "https://almadar-kflow-assets.web.app/shared";
45737
+ CDN4 = "https://almadar-kflow-assets.web.app/shared";
44936
45738
  DEFAULT_ROGUELIKE_TILES = [
44937
- { x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
44938
- { x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
44939
- { x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
44940
- { x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
44941
- { x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
44942
- { x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
44943
- { x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
44944
- { x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
44945
- { x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
44946
- { x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
44947
- { x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
44948
- { x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
44949
- { x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
44950
- { x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
44951
- { x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
44952
- { x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
44953
- { x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
44954
- { x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
44955
- { x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stairs_E.png` },
44956
- { x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
44957
- { x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
44958
- { x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
44959
- { x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
44960
- { x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
44961
- { x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` }
45739
+ { x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45740
+ { x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45741
+ { x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45742
+ { x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45743
+ { x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45744
+ { x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45745
+ { x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
45746
+ { x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
45747
+ { x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
45748
+ { x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45749
+ { x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45750
+ { x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
45751
+ { x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
45752
+ { x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
45753
+ { x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45754
+ { x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45755
+ { x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
45756
+ { x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
45757
+ { x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` },
45758
+ { x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45759
+ { x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45760
+ { x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45761
+ { x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45762
+ { x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45763
+ { x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` }
44962
45764
  ];
44963
45765
  DEFAULT_ROGUELIKE_MANIFEST = {
44964
- baseUrl: CDN5,
45766
+ baseUrl: CDN4,
44965
45767
  terrains: {
44966
45768
  floor: "/isometric-dungeon/Isometric/dirt_E.png",
44967
45769
  wall: "/isometric-dungeon/Isometric/stoneInset_E.png",
@@ -45087,7 +45889,7 @@ var init_debugRegistry = __esm({
45087
45889
  }
45088
45890
  });
45089
45891
  function useDebugData() {
45090
- const [data, setData] = React81.useState(() => ({
45892
+ const [data, setData] = React84.useState(() => ({
45091
45893
  traits: [],
45092
45894
  ticks: [],
45093
45895
  guards: [],
@@ -45101,7 +45903,7 @@ function useDebugData() {
45101
45903
  },
45102
45904
  lastUpdate: Date.now()
45103
45905
  }));
45104
- React81.useEffect(() => {
45906
+ React84.useEffect(() => {
45105
45907
  const updateData = () => {
45106
45908
  setData({
45107
45909
  traits: getAllTraits(),
@@ -45210,12 +46012,12 @@ function layoutGraph(states, transitions, initialState, width, height) {
45210
46012
  return positions;
45211
46013
  }
45212
46014
  function WalkMinimap() {
45213
- const [walkStep, setWalkStep] = React81.useState(null);
45214
- const [traits2, setTraits] = React81.useState([]);
45215
- const [coveredEdges, setCoveredEdges] = React81.useState([]);
45216
- const [completedTraits, setCompletedTraits] = React81.useState(/* @__PURE__ */ new Set());
45217
- const prevTraitRef = React81.useRef(null);
45218
- React81.useEffect(() => {
46015
+ const [walkStep, setWalkStep] = React84.useState(null);
46016
+ const [traits2, setTraits] = React84.useState([]);
46017
+ const [coveredEdges, setCoveredEdges] = React84.useState([]);
46018
+ const [completedTraits, setCompletedTraits] = React84.useState(/* @__PURE__ */ new Set());
46019
+ const prevTraitRef = React84.useRef(null);
46020
+ React84.useEffect(() => {
45219
46021
  const interval = setInterval(() => {
45220
46022
  const w = window;
45221
46023
  const step = w.__orbitalWalkStep;
@@ -45651,15 +46453,15 @@ var init_EntitiesTab = __esm({
45651
46453
  });
45652
46454
  function EventFlowTab({ events: events2 }) {
45653
46455
  const { t } = useTranslate();
45654
- const [filter, setFilter] = React81.useState("all");
45655
- const containerRef = React81.useRef(null);
45656
- const [autoScroll, setAutoScroll] = React81.useState(true);
45657
- React81.useEffect(() => {
46456
+ const [filter, setFilter] = React84.useState("all");
46457
+ const containerRef = React84.useRef(null);
46458
+ const [autoScroll, setAutoScroll] = React84.useState(true);
46459
+ React84.useEffect(() => {
45658
46460
  if (autoScroll && containerRef.current) {
45659
46461
  containerRef.current.scrollTop = containerRef.current.scrollHeight;
45660
46462
  }
45661
46463
  }, [events2.length, autoScroll]);
45662
- const filteredEvents = React81.useMemo(() => {
46464
+ const filteredEvents = React84.useMemo(() => {
45663
46465
  if (filter === "all") return events2;
45664
46466
  return events2.filter((e) => e.type === filter);
45665
46467
  }, [events2, filter]);
@@ -45775,7 +46577,7 @@ var init_EventFlowTab = __esm({
45775
46577
  });
45776
46578
  function GuardsPanel({ guards }) {
45777
46579
  const { t } = useTranslate();
45778
- const [filter, setFilter] = React81.useState("all");
46580
+ const [filter, setFilter] = React84.useState("all");
45779
46581
  if (guards.length === 0) {
45780
46582
  return /* @__PURE__ */ jsx(
45781
46583
  EmptyState,
@@ -45788,7 +46590,7 @@ function GuardsPanel({ guards }) {
45788
46590
  }
45789
46591
  const passedCount = guards.filter((g) => g.result).length;
45790
46592
  const failedCount = guards.length - passedCount;
45791
- const filteredGuards = React81.useMemo(() => {
46593
+ const filteredGuards = React84.useMemo(() => {
45792
46594
  if (filter === "all") return guards;
45793
46595
  if (filter === "passed") return guards.filter((g) => g.result);
45794
46596
  return guards.filter((g) => !g.result);
@@ -45951,10 +46753,10 @@ function EffectBadge({ effect }) {
45951
46753
  }
45952
46754
  function TransitionTimeline({ transitions }) {
45953
46755
  const { t } = useTranslate();
45954
- const containerRef = React81.useRef(null);
45955
- const [autoScroll, setAutoScroll] = React81.useState(true);
45956
- const [expandedId, setExpandedId] = React81.useState(null);
45957
- React81.useEffect(() => {
46756
+ const containerRef = React84.useRef(null);
46757
+ const [autoScroll, setAutoScroll] = React84.useState(true);
46758
+ const [expandedId, setExpandedId] = React84.useState(null);
46759
+ React84.useEffect(() => {
45958
46760
  if (autoScroll && containerRef.current) {
45959
46761
  containerRef.current.scrollTop = containerRef.current.scrollHeight;
45960
46762
  }
@@ -46234,9 +47036,9 @@ function getAllEvents(traits2) {
46234
47036
  function EventDispatcherTab({ traits: traits2, schema }) {
46235
47037
  const eventBus = useEventBus();
46236
47038
  const { t } = useTranslate();
46237
- const [log14, setLog] = React81.useState([]);
46238
- const prevStatesRef = React81.useRef(/* @__PURE__ */ new Map());
46239
- React81.useEffect(() => {
47039
+ const [log14, setLog] = React84.useState([]);
47040
+ const prevStatesRef = React84.useRef(/* @__PURE__ */ new Map());
47041
+ React84.useEffect(() => {
46240
47042
  for (const trait of traits2) {
46241
47043
  const prev = prevStatesRef.current.get(trait.id);
46242
47044
  if (prev && prev !== trait.currentState) {
@@ -46405,10 +47207,10 @@ function VerifyModePanel({
46405
47207
  localCount
46406
47208
  }) {
46407
47209
  const { t } = useTranslate();
46408
- const [expanded, setExpanded] = React81.useState(true);
46409
- const scrollRef = React81.useRef(null);
46410
- const prevCountRef = React81.useRef(0);
46411
- React81.useEffect(() => {
47210
+ const [expanded, setExpanded] = React84.useState(true);
47211
+ const scrollRef = React84.useRef(null);
47212
+ const prevCountRef = React84.useRef(0);
47213
+ React84.useEffect(() => {
46412
47214
  if (expanded && transitions.length > prevCountRef.current && scrollRef.current) {
46413
47215
  scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
46414
47216
  }
@@ -46465,10 +47267,10 @@ function RuntimeDebugger({
46465
47267
  schema
46466
47268
  }) {
46467
47269
  const { t } = useTranslate();
46468
- const [isCollapsed, setIsCollapsed] = React81.useState(mode === "verify" ? true : defaultCollapsed);
46469
- const [isVisible, setIsVisible] = React81.useState(mode === "inline" || mode === "verify" || isDebugEnabled2());
47270
+ const [isCollapsed, setIsCollapsed] = React84.useState(mode === "verify" ? true : defaultCollapsed);
47271
+ const [isVisible, setIsVisible] = React84.useState(mode === "inline" || mode === "verify" || isDebugEnabled2());
46470
47272
  const debugData = useDebugData();
46471
- React81.useEffect(() => {
47273
+ React84.useEffect(() => {
46472
47274
  if (mode === "inline") return;
46473
47275
  return onDebugToggle((enabled) => {
46474
47276
  setIsVisible(enabled);
@@ -46477,7 +47279,7 @@ function RuntimeDebugger({
46477
47279
  }
46478
47280
  });
46479
47281
  }, [mode]);
46480
- React81.useEffect(() => {
47282
+ React84.useEffect(() => {
46481
47283
  if (mode === "inline") return;
46482
47284
  const handleKeyDown = (e) => {
46483
47285
  if (e.key === "`" && isVisible) {
@@ -47037,7 +47839,7 @@ function SequenceBar({
47037
47839
  onSlotRemove(index);
47038
47840
  }, [onSlotRemove, playing]);
47039
47841
  const paddedSlots = Array.from({ length: maxSlots }, (_, i) => slots[i]);
47040
- return /* @__PURE__ */ jsx(HStack, { className: cn("items-center", className), gap: "sm", children: paddedSlots.map((slot, i) => /* @__PURE__ */ jsxs(React81__default.Fragment, { children: [
47842
+ return /* @__PURE__ */ jsx(HStack, { className: cn("items-center", className), gap: "sm", children: paddedSlots.map((slot, i) => /* @__PURE__ */ jsxs(React84__default.Fragment, { children: [
47041
47843
  i > 0 && /* @__PURE__ */ jsx(
47042
47844
  Typography,
47043
47845
  {
@@ -47926,7 +48728,7 @@ var init_StatCard = __esm({
47926
48728
  const labelToUse = propLabel ?? propTitle;
47927
48729
  const eventBus = useEventBus();
47928
48730
  const { t } = useTranslate();
47929
- const handleActionClick = React81__default.useCallback(() => {
48731
+ const handleActionClick = React84__default.useCallback(() => {
47930
48732
  if (action?.event) {
47931
48733
  eventBus.emit(`UI:${action.event}`, {});
47932
48734
  }
@@ -47937,7 +48739,7 @@ var init_StatCard = __esm({
47937
48739
  const data = Array.isArray(entity) ? entity : entity ? [entity] : [];
47938
48740
  const isLoading = externalLoading ?? false;
47939
48741
  const error = externalError;
47940
- const computeMetricValue = React81__default.useCallback(
48742
+ const computeMetricValue = React84__default.useCallback(
47941
48743
  (metric, items) => {
47942
48744
  if (metric.value !== void 0) {
47943
48745
  return metric.value;
@@ -47976,7 +48778,7 @@ var init_StatCard = __esm({
47976
48778
  },
47977
48779
  []
47978
48780
  );
47979
- const schemaStats = React81__default.useMemo(() => {
48781
+ const schemaStats = React84__default.useMemo(() => {
47980
48782
  if (!metrics || metrics.length === 0) return null;
47981
48783
  return metrics.map((metric) => ({
47982
48784
  label: metric.label,
@@ -47984,7 +48786,7 @@ var init_StatCard = __esm({
47984
48786
  format: metric.format
47985
48787
  }));
47986
48788
  }, [metrics, data, computeMetricValue]);
47987
- const calculatedTrend = React81__default.useMemo(() => {
48789
+ const calculatedTrend = React84__default.useMemo(() => {
47988
48790
  if (manualTrend !== void 0) return manualTrend;
47989
48791
  if (previousValue === void 0 || currentValue === void 0)
47990
48792
  return void 0;
@@ -48950,7 +49752,7 @@ var init_Timeline = __esm({
48950
49752
  }) => {
48951
49753
  const { t } = useTranslate();
48952
49754
  const entityData = Array.isArray(entity) ? entity : [];
48953
- const items = React81__default.useMemo(() => {
49755
+ const items = React84__default.useMemo(() => {
48954
49756
  if (propItems) return propItems;
48955
49757
  if (entityData.length === 0) return [];
48956
49758
  return entityData.map((record, idx) => {
@@ -49057,7 +49859,7 @@ var init_Timeline = __esm({
49057
49859
  }
49058
49860
  });
49059
49861
  function extractToastProps(children) {
49060
- if (!React81__default.isValidElement(children)) {
49862
+ if (!React84__default.isValidElement(children)) {
49061
49863
  if (typeof children === "string") {
49062
49864
  return { message: children };
49063
49865
  }
@@ -49095,7 +49897,7 @@ var init_ToastSlot = __esm({
49095
49897
  eventBus.emit("UI:CLOSE");
49096
49898
  };
49097
49899
  if (!isVisible) return null;
49098
- const isCustomContent = React81__default.isValidElement(children) && !message;
49900
+ const isCustomContent = React84__default.isValidElement(children) && !message;
49099
49901
  return /* @__PURE__ */ jsx(Box, { className: "fixed bottom-4 right-4 z-50", children: isCustomContent ? children : /* @__PURE__ */ jsx(
49100
49902
  Toast,
49101
49903
  {
@@ -49111,82 +49913,442 @@ var init_ToastSlot = __esm({
49111
49913
  ToastSlot.displayName = "ToastSlot";
49112
49914
  }
49113
49915
  });
49916
+ function resolveManifestUrl4(manifest, relative) {
49917
+ if (relative == null || relative === "") return void 0;
49918
+ if (/^https?:\/\//.test(relative)) return relative;
49919
+ const base = manifest?.baseUrl;
49920
+ if (base == null) return relative;
49921
+ const cleanBase = base.replace(/\/$/, "");
49922
+ const cleanRel = relative.replace(/^\//, "");
49923
+ return `${cleanBase}/${cleanRel}`;
49924
+ }
49925
+ function readPlayer(v) {
49926
+ if (v == null || typeof v !== "object") return void 0;
49927
+ const o = v;
49928
+ return { x: num(o.x), y: num(o.y) };
49929
+ }
49930
+ function TopDownShooterBoard({
49931
+ entity,
49932
+ player: propPlayer,
49933
+ enemies: propEnemies,
49934
+ projectiles: propProjectiles,
49935
+ score: propScore,
49936
+ wave: propWave,
49937
+ lives: propLives,
49938
+ result: propResult,
49939
+ assetManifest: propAssetManifest,
49940
+ width = DEFAULT_WIDTH,
49941
+ height = DEFAULT_HEIGHT,
49942
+ spriteSize = 32,
49943
+ moveEvent = "MOVE",
49944
+ fireEvent = "FIRE",
49945
+ playAgainEvent = "PLAY_AGAIN",
49946
+ gameEndEvent = "GAME_END",
49947
+ className
49948
+ }) {
49949
+ const board = boardEntity(entity) ?? {};
49950
+ const eventBus = useEventBus();
49951
+ const { t } = useTranslate();
49952
+ const assetManifest = propAssetManifest ?? board.assetManifest;
49953
+ const player = propPlayer ?? readPlayer(board.player) ?? { x: width / 2, y: height - spriteSize };
49954
+ const enemies = useMemo(
49955
+ () => propEnemies ?? rows(board.enemies).map((r) => ({
49956
+ id: str(r.id),
49957
+ x: num(r.x),
49958
+ y: num(r.y),
49959
+ hp: num(r.hp, 1)
49960
+ })),
49961
+ [propEnemies, board.enemies]
49962
+ );
49963
+ const projectiles = useMemo(
49964
+ () => propProjectiles ?? rows(board.projectiles).map((r) => ({
49965
+ id: str(r.id),
49966
+ x: num(r.x),
49967
+ y: num(r.y),
49968
+ vx: num(r.vx),
49969
+ vy: num(r.vy)
49970
+ })),
49971
+ [propProjectiles, board.projectiles]
49972
+ );
49973
+ const score = propScore ?? num(board.score, 0);
49974
+ const wave = propWave ?? num(board.wave, 1);
49975
+ const lives = propLives ?? num(board.lives, 3);
49976
+ const result = propResult ?? (str(board.result) || "none");
49977
+ const playerSprite = resolveManifestUrl4(assetManifest, assetManifest?.units?.player);
49978
+ const enemySprite = resolveManifestUrl4(assetManifest, assetManifest?.units?.enemy);
49979
+ const projectileSprite = resolveManifestUrl4(assetManifest, assetManifest?.features?.projectile);
49980
+ const backgroundSprite = resolveManifestUrl4(assetManifest, assetManifest?.background);
49981
+ const spriteUrls = useMemo(
49982
+ () => [playerSprite, enemySprite, projectileSprite].filter((u) => u != null),
49983
+ [playerSprite, enemySprite, projectileSprite]
49984
+ );
49985
+ const { getImage } = useImageCache(spriteUrls);
49986
+ const sceneRef = useRef({ player, enemies, projectiles, playerSprite, enemySprite, projectileSprite });
49987
+ sceneRef.current = { player, enemies, projectiles, playerSprite, enemySprite, projectileSprite };
49988
+ boardLog2.debug("shooter-resolve", {
49989
+ player: { x: player.x, y: player.y },
49990
+ enemyCount: enemies.length,
49991
+ projectileCount: projectiles.length,
49992
+ wave,
49993
+ lives,
49994
+ result,
49995
+ hasPlayerSprite: playerSprite != null
49996
+ });
49997
+ const onDraw = useCallback(
49998
+ (ctx, _frame) => {
49999
+ const scene = sceneRef.current;
50000
+ ctx.fillStyle = "#10141c";
50001
+ ctx.fillRect(0, 0, width, height);
50002
+ const half = spriteSize / 2;
50003
+ for (const p2 of scene.projectiles) {
50004
+ const img = scene.projectileSprite ? getImage(scene.projectileSprite) : void 0;
50005
+ if (img) {
50006
+ ctx.drawImage(img, p2.x - half / 2, p2.y - half / 2, spriteSize / 2, spriteSize / 2);
50007
+ } else {
50008
+ ctx.fillStyle = "#ffd34d";
50009
+ ctx.beginPath();
50010
+ ctx.arc(p2.x, p2.y, spriteSize / 6, 0, Math.PI * 2);
50011
+ ctx.fill();
50012
+ }
50013
+ }
50014
+ for (const e of scene.enemies) {
50015
+ const img = scene.enemySprite ? getImage(scene.enemySprite) : void 0;
50016
+ if (img) {
50017
+ ctx.drawImage(img, e.x - half, e.y - half, spriteSize, spriteSize);
50018
+ } else {
50019
+ ctx.fillStyle = "#e0524d";
50020
+ ctx.fillRect(e.x - half, e.y - half, spriteSize, spriteSize);
50021
+ }
50022
+ }
50023
+ const pImg = scene.playerSprite ? getImage(scene.playerSprite) : void 0;
50024
+ if (pImg) {
50025
+ ctx.drawImage(pImg, scene.player.x - half, scene.player.y - half, spriteSize, spriteSize);
50026
+ } else {
50027
+ ctx.fillStyle = "#4da3ff";
50028
+ ctx.beginPath();
50029
+ ctx.moveTo(scene.player.x, scene.player.y - half);
50030
+ ctx.lineTo(scene.player.x + half, scene.player.y + half);
50031
+ ctx.lineTo(scene.player.x - half, scene.player.y + half);
50032
+ ctx.closePath();
50033
+ ctx.fill();
50034
+ }
50035
+ },
50036
+ [getImage, width, height, spriteSize]
50037
+ );
50038
+ const moveEventRef = useRef(moveEvent);
50039
+ moveEventRef.current = moveEvent;
50040
+ const fireEventRef = useRef(fireEvent);
50041
+ fireEventRef.current = fireEvent;
50042
+ const resultRef = useRef(result);
50043
+ resultRef.current = result;
50044
+ const playerRef = useRef(player);
50045
+ playerRef.current = player;
50046
+ useEffect(() => {
50047
+ function onKeyDown(e) {
50048
+ if (resultRef.current !== "none") return;
50049
+ let dx = 0;
50050
+ let dy = 0;
50051
+ switch (e.key) {
50052
+ case "ArrowUp":
50053
+ case "w":
50054
+ dy = -1;
50055
+ break;
50056
+ case "ArrowDown":
50057
+ case "s":
50058
+ dy = 1;
50059
+ break;
50060
+ case "ArrowLeft":
50061
+ case "a":
50062
+ dx = -1;
50063
+ break;
50064
+ case "ArrowRight":
50065
+ case "d":
50066
+ dx = 1;
50067
+ break;
50068
+ case " ": {
50069
+ e.preventDefault();
50070
+ const evt = fireEventRef.current;
50071
+ if (evt) {
50072
+ eventBus.emit(`UI:${evt}`, { tx: playerRef.current.x, ty: 0 });
50073
+ }
50074
+ return;
50075
+ }
50076
+ default:
50077
+ return;
50078
+ }
50079
+ e.preventDefault();
50080
+ const moveEvt = moveEventRef.current;
50081
+ if (moveEvt) {
50082
+ eventBus.emit(`UI:${moveEvt}`, { dx, dy });
50083
+ }
50084
+ }
50085
+ window.addEventListener("keydown", onKeyDown);
50086
+ return () => window.removeEventListener("keydown", onKeyDown);
50087
+ }, [eventBus]);
50088
+ const handleFireClick = useCallback(
50089
+ (e) => {
50090
+ if (result !== "none") return;
50091
+ const evt = fireEventRef.current;
50092
+ if (!evt) return;
50093
+ const rect = e.currentTarget.getBoundingClientRect();
50094
+ const tx = e.clientX - rect.left;
50095
+ const ty = e.clientY - rect.top;
50096
+ eventBus.emit(`UI:${evt}`, { tx, ty });
50097
+ },
50098
+ [result, eventBus]
50099
+ );
50100
+ const handlePlayAgain = useCallback(() => {
50101
+ if (playAgainEvent) {
50102
+ eventBus.emit(`UI:${playAgainEvent}`, {});
50103
+ }
50104
+ }, [playAgainEvent, eventBus]);
50105
+ const gameEndEmittedRef = useRef(false);
50106
+ useEffect(() => {
50107
+ if ((result === "won" || result === "lost") && gameEndEvent) {
50108
+ if (!gameEndEmittedRef.current) {
50109
+ gameEndEmittedRef.current = true;
50110
+ eventBus.emit(`UI:${gameEndEvent}`, { result });
50111
+ }
50112
+ } else {
50113
+ gameEndEmittedRef.current = false;
50114
+ }
50115
+ }, [result, gameEndEvent, eventBus]);
50116
+ const isGameOver = result === "won" || result === "lost";
50117
+ return /* @__PURE__ */ jsxs(VStack, { className: cn("top-down-shooter-board relative bg-background", className), gap: "none", children: [
50118
+ /* @__PURE__ */ jsxs(HStack, { className: "px-4 py-2 border-b border-border bg-surface", gap: "lg", align: "center", children: [
50119
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
50120
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("shooter.score") ?? "Score" }),
50121
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", className: "text-warning", children: score })
50122
+ ] }),
50123
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
50124
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("shooter.wave") ?? "Wave" }),
50125
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", children: wave })
50126
+ ] }),
50127
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
50128
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("shooter.lives") ?? "Lives" }),
50129
+ /* @__PURE__ */ jsx(
50130
+ Typography,
50131
+ {
50132
+ variant: "body2",
50133
+ weight: "bold",
50134
+ className: lives <= 1 ? "text-error" : "text-success",
50135
+ children: lives
50136
+ }
50137
+ )
50138
+ ] })
50139
+ ] }),
50140
+ /* @__PURE__ */ jsxs(Box, { className: "relative", onClick: handleFireClick, children: [
50141
+ /* @__PURE__ */ jsx(
50142
+ GameCanvas2D,
50143
+ {
50144
+ width,
50145
+ height,
50146
+ backgroundImage: backgroundSprite,
50147
+ assetBaseUrl: assetManifest?.baseUrl,
50148
+ onDraw
50149
+ }
50150
+ ),
50151
+ 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: [
50152
+ /* @__PURE__ */ jsx(
50153
+ Typography,
50154
+ {
50155
+ variant: "h2",
50156
+ className: cn(
50157
+ "text-4xl font-black tracking-widest uppercase",
50158
+ result === "won" ? "text-warning" : "text-error"
50159
+ ),
50160
+ children: result === "won" ? t("shooter.victory") ?? "Victory!" : t("shooter.defeat") ?? "Defeat!"
50161
+ }
50162
+ ),
50163
+ /* @__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." }),
50164
+ /* @__PURE__ */ jsx(
50165
+ Button,
50166
+ {
50167
+ variant: "primary",
50168
+ className: "px-8 py-3 font-semibold",
50169
+ onClick: handlePlayAgain,
50170
+ children: t("shooter.playAgain") ?? "Play Again"
50171
+ }
50172
+ )
50173
+ ] }) })
50174
+ ] })
50175
+ ] });
50176
+ }
50177
+ var boardLog2, DEFAULT_WIDTH, DEFAULT_HEIGHT;
50178
+ var init_TopDownShooterBoard = __esm({
50179
+ "components/game/organisms/TopDownShooterBoard.tsx"() {
50180
+ "use client";
50181
+ init_cn();
50182
+ init_useEventBus();
50183
+ init_Box();
50184
+ init_Button();
50185
+ init_Typography();
50186
+ init_Stack();
50187
+ init_GameCanvas2D();
50188
+ init_useImageCache();
50189
+ init_boardEntity();
50190
+ boardLog2 = createLogger("almadar:ui:game:top-down-shooter-board");
50191
+ DEFAULT_WIDTH = 640;
50192
+ DEFAULT_HEIGHT = 480;
50193
+ TopDownShooterBoard.displayName = "TopDownShooterBoard";
50194
+ }
50195
+ });
50196
+ function isEntityRow3(value) {
50197
+ return value != null && typeof value === "object" && !Array.isArray(value);
50198
+ }
50199
+ function TopDownShooterTemplate({
50200
+ entity,
50201
+ title = "Arena Shooter",
50202
+ player,
50203
+ enemies,
50204
+ projectiles,
50205
+ score,
50206
+ wave,
50207
+ lives,
50208
+ result,
50209
+ assetManifest,
50210
+ width,
50211
+ height,
50212
+ spriteSize,
50213
+ moveEvent,
50214
+ fireEvent,
50215
+ playAgainEvent,
50216
+ gameEndEvent,
50217
+ className
50218
+ }) {
50219
+ const resolved = isEntityRow3(entity) ? entity : void 0;
50220
+ return /* @__PURE__ */ jsxs(
50221
+ Box,
50222
+ {
50223
+ display: "flex",
50224
+ fullHeight: true,
50225
+ className: cn("top-down-shooter-template flex-col", className),
50226
+ children: [
50227
+ /* @__PURE__ */ jsx(
50228
+ HStack,
50229
+ {
50230
+ gap: "sm",
50231
+ align: "center",
50232
+ className: "px-4 py-3 border-b-2 border-border bg-surface shrink-0",
50233
+ children: /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title })
50234
+ }
50235
+ ),
50236
+ /* @__PURE__ */ jsx(Box, { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsx(
50237
+ TopDownShooterBoard,
50238
+ {
50239
+ entity: resolved,
50240
+ player,
50241
+ enemies,
50242
+ projectiles,
50243
+ score,
50244
+ wave,
50245
+ lives,
50246
+ result,
50247
+ assetManifest,
50248
+ width,
50249
+ height,
50250
+ spriteSize,
50251
+ moveEvent,
50252
+ fireEvent,
50253
+ playAgainEvent,
50254
+ gameEndEvent,
50255
+ className: "h-full"
50256
+ }
50257
+ ) })
50258
+ ]
50259
+ }
50260
+ );
50261
+ }
50262
+ var init_TopDownShooterTemplate = __esm({
50263
+ "components/game/templates/TopDownShooterTemplate.tsx"() {
50264
+ init_cn();
50265
+ init_Box();
50266
+ init_Stack();
50267
+ init_Typography();
50268
+ init_TopDownShooterBoard();
50269
+ TopDownShooterTemplate.displayName = "TopDownShooterTemplate";
50270
+ }
50271
+ });
50272
+ function resolveManifestUrl5(manifest, relative) {
50273
+ if (relative == null) return void 0;
50274
+ if (/^https?:\/\//.test(relative)) return relative;
50275
+ const base = manifest?.baseUrl;
50276
+ if (base == null) return relative;
50277
+ const cleanBase = base.replace(/\/$/, "");
50278
+ const cleanRel = relative.replace(/^\//, "");
50279
+ return `${cleanBase}/${cleanRel}`;
50280
+ }
49114
50281
  function buildDefaultTDTiles() {
49115
50282
  const pathSet = new Set(DEFAULT_TD_PATH.map((p2) => `${p2.x},${p2.y}`));
49116
50283
  const tiles = [];
49117
50284
  for (let y = 0; y < TD_GRID_H; y++) {
49118
50285
  for (let x = 0; x < TD_GRID_W; x++) {
49119
50286
  if (pathSet.has(`${x},${y}`)) {
49120
- tiles.push({ x, y, terrain: "path", passable: false, terrainSprite: TERRAIN_SPRITES.path });
50287
+ tiles.push({ x, y, terrain: "path", passable: false });
49121
50288
  } else {
49122
50289
  const variant = (x * 3 + y * 5 + (x ^ y)) % 3;
49123
- const terrainKey = ["ground", "grass", "dirt"][variant];
49124
- tiles.push({ x, y, terrain: terrainKey, passable: true, terrainSprite: TERRAIN_SPRITES[terrainKey] ?? TERRAIN_SPRITES.ground });
50290
+ const terrainKey = ["ground", "grass", "stone"][variant];
50291
+ tiles.push({ x, y, terrain: terrainKey, passable: true });
49125
50292
  }
49126
50293
  }
49127
50294
  }
49128
50295
  return tiles;
49129
50296
  }
49130
- function tilesToIso(tiles) {
49131
- return tiles.map((t) => ({
49132
- x: t.x,
49133
- y: t.y,
49134
- terrain: t.terrain,
49135
- terrainSprite: t.terrainSprite ?? TERRAIN_SPRITES[t.terrain ?? "ground"],
49136
- passable: t.passable
49137
- }));
49138
- }
49139
- function towersToUnits(towers) {
49140
- return towers.map((t) => ({
49141
- id: t.id,
49142
- position: { x: t.x, y: t.y },
49143
- name: "Tower",
49144
- team: "player",
49145
- sprite: TOWER_SPRITE,
49146
- unitType: "guardian",
49147
- health: 1,
49148
- maxHealth: 1
49149
- }));
50297
+ function tilesToIso2(tiles, manifest) {
50298
+ return tiles.map((t) => {
50299
+ const key = t.terrain ?? "ground";
50300
+ const sprite = t.terrainSprite ?? resolveManifestUrl5(manifest, manifest?.terrains?.[key]) ?? resolveManifestUrl5(manifest, manifest?.terrains?.ground);
50301
+ return {
50302
+ x: t.x,
50303
+ y: t.y,
50304
+ terrain: t.terrain,
50305
+ terrainSprite: sprite,
50306
+ passable: t.passable
50307
+ };
50308
+ });
49150
50309
  }
49151
- function creepsToUnits(creeps) {
50310
+ function creepsToUnits(creeps, manifest) {
50311
+ const sprite = resolveManifestUrl5(manifest, manifest?.units?.creep);
49152
50312
  return creeps.map((c) => ({
49153
50313
  id: c.id,
49154
50314
  position: { x: c.x, y: c.y },
49155
50315
  name: "Creep",
49156
50316
  team: "enemy",
49157
- sprite: CREEP_SPRITE,
49158
- unitType: "scrapper",
50317
+ sprite,
50318
+ unitType: "creep",
49159
50319
  health: c.hp,
49160
50320
  maxHealth: c.maxHp
49161
50321
  }));
49162
50322
  }
49163
- function pathToFeatures(path) {
49164
- return path.map((p2, i) => ({
49165
- id: `path-${i}`,
49166
- x: p2.x,
49167
- y: p2.y,
49168
- type: "path-marker",
49169
- sprite: `${CDN6}world-map/road_straight.png`
49170
- }));
49171
- }
49172
- function heroToUnit(hero) {
50323
+ function heroToUnit(hero, manifest) {
49173
50324
  return {
49174
50325
  id: hero.id,
49175
50326
  position: { x: hero.x, y: hero.y },
49176
50327
  name: "Hero",
49177
50328
  team: "player",
49178
- sprite: HERO_SPRITE,
49179
- unitType: "amir",
50329
+ sprite: resolveManifestUrl5(manifest, manifest?.units?.hero),
50330
+ unitType: "hero",
49180
50331
  health: 1,
49181
50332
  maxHealth: 1
49182
50333
  };
49183
50334
  }
50335
+ function towersToFeatures(towers, manifest) {
50336
+ const sprite = resolveManifestUrl5(manifest, manifest?.features?.tower);
50337
+ return towers.map((t) => ({
50338
+ id: t.id,
50339
+ x: t.x,
50340
+ y: t.y,
50341
+ type: "tower",
50342
+ sprite
50343
+ }));
50344
+ }
49184
50345
  function TowerDefenseBoard({
49185
50346
  entity,
49186
50347
  tiles: propTiles,
49187
50348
  path: propPath,
49188
50349
  towers: propTowers,
49189
50350
  creeps: propCreeps,
50351
+ assetManifest: propAssetManifest,
49190
50352
  hero: propHero,
49191
50353
  gold: propGold,
49192
50354
  lives: propLives,
@@ -49209,12 +50371,51 @@ function TowerDefenseBoard({
49209
50371
  const board = boardEntity(entity) ?? {};
49210
50372
  const eventBus = useEventBus();
49211
50373
  const { t } = useTranslate();
49212
- const rawTiles = propTiles ?? rows(board.tiles);
50374
+ const assetManifest = propAssetManifest ?? board.assetManifest;
50375
+ const entityTiles = useMemo(
50376
+ () => rows(board.tiles).map((r) => ({
50377
+ x: num(r.x),
50378
+ y: num(r.y),
50379
+ terrain: r.terrain == null ? void 0 : str(r.terrain),
50380
+ terrainSprite: r.terrainSprite == null ? void 0 : str(r.terrainSprite),
50381
+ passable: r.passable !== false
50382
+ })),
50383
+ [board.tiles]
50384
+ );
50385
+ const rawTiles = propTiles ?? entityTiles;
49213
50386
  const tiles = rawTiles.length >= TD_GRID_W * TD_GRID_H ? rawTiles : DEFAULT_TD_TILES;
49214
- const rawPath = propPath ?? rows(board.path);
50387
+ const entityPath = useMemo(
50388
+ () => rows(board.path).map((r) => ({ x: num(r.x), y: num(r.y) })),
50389
+ [board.path]
50390
+ );
50391
+ const rawPath = propPath ?? entityPath;
49215
50392
  const path = rawPath.length > 0 ? rawPath : DEFAULT_TD_PATH;
49216
- const towers = propTowers ?? rows(board.towers);
49217
- const creeps = propCreeps ?? rows(board.creeps);
50393
+ const entityTowers = useMemo(
50394
+ () => rows(board.towers).map((r) => ({
50395
+ id: str(r.id),
50396
+ x: num(r.x),
50397
+ y: num(r.y),
50398
+ range: num(r.range),
50399
+ damage: num(r.damage),
50400
+ cooldown: num(r.cooldown),
50401
+ lastFiredAt: r.lastFiredAt == null ? void 0 : num(r.lastFiredAt)
50402
+ })),
50403
+ [board.towers]
50404
+ );
50405
+ const towers = propTowers ?? entityTowers;
50406
+ const entityCreeps = useMemo(
50407
+ () => rows(board.creeps).map((r) => ({
50408
+ id: str(r.id),
50409
+ x: num(r.x),
50410
+ y: num(r.y),
50411
+ hp: num(r.hp),
50412
+ maxHp: num(r.maxHp),
50413
+ pathIndex: num(r.pathIndex),
50414
+ speed: num(r.speed)
50415
+ })),
50416
+ [board.creeps]
50417
+ );
50418
+ const creeps = propCreeps ?? entityCreeps;
49218
50419
  const hero = propHero ?? { id: "hero", x: 8, y: 8 };
49219
50420
  const gold = propGold ?? num(board.gold, 100);
49220
50421
  const lives = propLives ?? num(board.lives, 20);
@@ -49266,12 +50467,11 @@ function TowerDefenseBoard({
49266
50467
  (t2) => t2.passable !== false && !towerPositions.has(`${t2.x},${t2.y}`) && !pathPositions.has(`${t2.x},${t2.y}`)
49267
50468
  ).map((t2) => ({ x: t2.x, y: t2.y }));
49268
50469
  }, [tiles, towerPositions, pathPositions, result, gold, towerCost]);
49269
- const isoTiles = useMemo(() => tilesToIso(tiles), [tiles]);
49270
- const towerUnits = useMemo(() => towersToUnits(towers), [towers]);
49271
- const creepUnits = useMemo(() => creepsToUnits(creeps), [creeps]);
49272
- const heroUnit = useMemo(() => heroToUnit(hero), [hero]);
49273
- const isoUnits = useMemo(() => [...towerUnits, ...creepUnits, heroUnit], [towerUnits, creepUnits, heroUnit]);
49274
- const pathFeatures = useMemo(() => pathToFeatures(path), [path]);
50470
+ const isoTiles = useMemo(() => tilesToIso2(tiles, assetManifest), [tiles, assetManifest]);
50471
+ const creepUnits = useMemo(() => creepsToUnits(creeps, assetManifest), [creeps, assetManifest]);
50472
+ const heroUnit = useMemo(() => heroToUnit(hero, assetManifest), [hero, assetManifest]);
50473
+ const isoUnits = useMemo(() => [...creepUnits, heroUnit], [creepUnits, heroUnit]);
50474
+ const towerFeatures = useMemo(() => towersToFeatures(towers, assetManifest), [towers, assetManifest]);
49275
50475
  const handleTileClick = useCallback((x, y) => {
49276
50476
  if (result !== "none") return;
49277
50477
  if (pathPositions.has(`${x},${y}`)) return;
@@ -49330,7 +50530,9 @@ function TowerDefenseBoard({
49330
50530
  {
49331
50531
  tiles: isoTiles,
49332
50532
  units: isoUnits,
49333
- features: pathFeatures,
50533
+ features: towerFeatures,
50534
+ assetBaseUrl: assetManifest?.baseUrl,
50535
+ assetManifest,
49334
50536
  validMoves,
49335
50537
  hoveredTile,
49336
50538
  onTileClick: handleTileClick,
@@ -49369,7 +50571,7 @@ function TowerDefenseBoard({
49369
50571
  ] }) })
49370
50572
  ] });
49371
50573
  }
49372
- var CDN6, TD_GRID_W, TD_GRID_H, TERRAIN_SPRITES, DEFAULT_TD_PATH, DEFAULT_TD_TILES, TOWER_SPRITE, CREEP_SPRITE, HERO_SPRITE;
50574
+ var TD_GRID_W, TD_GRID_H, DEFAULT_TD_PATH, DEFAULT_TD_TILES;
49373
50575
  var init_TowerDefenseBoard = __esm({
49374
50576
  "components/game/organisms/TowerDefenseBoard.tsx"() {
49375
50577
  "use client";
@@ -49381,16 +50583,8 @@ var init_TowerDefenseBoard = __esm({
49381
50583
  init_Stack();
49382
50584
  init_IsometricCanvas();
49383
50585
  init_boardEntity();
49384
- CDN6 = "https://almadar-kflow-assets.web.app/shared/";
49385
50586
  TD_GRID_W = 16;
49386
50587
  TD_GRID_H = 16;
49387
- TERRAIN_SPRITES = {
49388
- ground: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_01.png`,
49389
- path: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_09.png`,
49390
- wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`,
49391
- grass: `${CDN6}isometric-dungeon/Isometric/dirt_E.png`,
49392
- dirt: `${CDN6}isometric-dungeon/Isometric/dirtTiles_E.png`
49393
- };
49394
50588
  DEFAULT_TD_PATH = [
49395
50589
  { x: 2, y: 0 },
49396
50590
  { x: 2, y: 1 },
@@ -49443,9 +50637,6 @@ var init_TowerDefenseBoard = __esm({
49443
50637
  { x: 13, y: 15 }
49444
50638
  ];
49445
50639
  DEFAULT_TD_TILES = buildDefaultTDTiles();
49446
- TOWER_SPRITE = `${CDN6}sprite-sheets/guardian-sprite-sheet-se.png`;
49447
- CREEP_SPRITE = `${CDN6}sprite-sheets/scrapper-sprite-sheet-se.png`;
49448
- HERO_SPRITE = `${CDN6}sprite-sheets/amir-sprite-sheet-se.png`;
49449
50640
  TowerDefenseBoard.displayName = "TowerDefenseBoard";
49450
50641
  }
49451
50642
  });
@@ -49713,6 +50904,214 @@ var init_UncontrolledBattleBoard = __esm({
49713
50904
  UncontrolledBattleBoard.displayName = "UncontrolledBattleBoard";
49714
50905
  }
49715
50906
  });
50907
+ function resolveManifestUrl6(manifest, relative) {
50908
+ if (relative == null) return void 0;
50909
+ if (/^https?:\/\//.test(relative)) return relative;
50910
+ const base = manifest?.baseUrl;
50911
+ if (base == null) return relative;
50912
+ const cleanBase = base.replace(/\/$/, "");
50913
+ const cleanRel = relative.replace(/^\//, "");
50914
+ return `${cleanBase}/${cleanRel}`;
50915
+ }
50916
+ function VisualNovelBoard({
50917
+ entity,
50918
+ nodes: propNodes,
50919
+ currentNodeId: propCurrentNodeId,
50920
+ assetManifest: propAssetManifest,
50921
+ typewriterSpeed = 30,
50922
+ portraitScale = 1,
50923
+ chooseEvent,
50924
+ advanceEvent,
50925
+ restartEvent,
50926
+ className
50927
+ }) {
50928
+ const board = boardEntity(entity) ?? {};
50929
+ const eventBus = useEventBus();
50930
+ const assetManifest = propAssetManifest ?? board.assetManifest;
50931
+ const entityNodes = useMemo(
50932
+ () => rows(board.nodes).map((r) => ({
50933
+ id: str(r.id),
50934
+ speaker: str(r.speaker),
50935
+ text: str(r.text),
50936
+ backgroundKey: r.backgroundKey == null ? void 0 : str(r.backgroundKey),
50937
+ portraitKey: r.portraitKey == null ? void 0 : str(r.portraitKey),
50938
+ choices: rows(r.choices).map((c) => ({
50939
+ label: str(c.label),
50940
+ nextId: str(c.nextId)
50941
+ }))
50942
+ })),
50943
+ [board.nodes]
50944
+ );
50945
+ const rawNodes = propNodes ?? entityNodes;
50946
+ const nodes = rawNodes.length > 0 ? rawNodes : DEFAULT_NODES;
50947
+ const currentNodeId = propCurrentNodeId ?? (str(board.currentNodeId) || nodes[0]?.id);
50948
+ const currentNode = nodes.find((n) => n.id === currentNodeId) ?? nodes[0];
50949
+ const backgroundImage = useMemo(
50950
+ () => resolveManifestUrl6(assetManifest, assetManifest?.backgrounds?.[currentNode?.backgroundKey ?? ""]),
50951
+ [assetManifest, currentNode?.backgroundKey]
50952
+ );
50953
+ const portraitUrl = useMemo(
50954
+ () => resolveManifestUrl6(assetManifest, assetManifest?.portraits?.[currentNode?.portraitKey ?? ""]),
50955
+ [assetManifest, currentNode?.portraitKey]
50956
+ );
50957
+ const dialogueChoices = useMemo(
50958
+ () => (currentNode?.choices ?? []).map((c) => ({ text: c.label, next: c.nextId })),
50959
+ [currentNode?.choices]
50960
+ );
50961
+ const handleChoice = useCallback(
50962
+ (choice) => {
50963
+ const index = dialogueChoices.findIndex((c) => c.next === choice.next && c.text === choice.text);
50964
+ if (chooseEvent) {
50965
+ eventBus.emit(`UI:${chooseEvent}`, { choiceIndex: index });
50966
+ }
50967
+ },
50968
+ [dialogueChoices, chooseEvent, eventBus]
50969
+ );
50970
+ const handleAdvance = useCallback(() => {
50971
+ if (advanceEvent) {
50972
+ eventBus.emit(`UI:${advanceEvent}`, {});
50973
+ }
50974
+ }, [advanceEvent, eventBus]);
50975
+ const handleRestart = useCallback(() => {
50976
+ if (restartEvent) {
50977
+ eventBus.emit(`UI:${restartEvent}`, {});
50978
+ }
50979
+ }, [restartEvent, eventBus]);
50980
+ if (!currentNode) {
50981
+ return /* @__PURE__ */ jsxs(VStack, { className: cn("visual-novel-board relative min-h-[600px] bg-background items-center justify-center", className), gap: "lg", children: [
50982
+ /* @__PURE__ */ jsx(Typography, { variant: "h3", color: "muted", children: "No dialogue nodes" }),
50983
+ /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleRestart, children: "Restart" })
50984
+ ] });
50985
+ }
50986
+ return /* @__PURE__ */ jsx(Box, { className: cn("visual-novel-board relative min-h-[600px] bg-background overflow-hidden", className), children: /* @__PURE__ */ jsx(
50987
+ DialogueBox,
50988
+ {
50989
+ dialogue: {
50990
+ speaker: currentNode.speaker,
50991
+ text: currentNode.text,
50992
+ choices: dialogueChoices
50993
+ },
50994
+ typewriterSpeed,
50995
+ position: "bottom",
50996
+ backgroundImage,
50997
+ portraitUrl,
50998
+ portraitScale,
50999
+ onChoice: handleChoice,
51000
+ onAdvance: handleAdvance
51001
+ }
51002
+ ) });
51003
+ }
51004
+ var DEFAULT_NODES;
51005
+ var init_VisualNovelBoard = __esm({
51006
+ "components/game/organisms/VisualNovelBoard.tsx"() {
51007
+ "use client";
51008
+ init_cn();
51009
+ init_useEventBus();
51010
+ init_Box();
51011
+ init_Typography();
51012
+ init_Stack();
51013
+ init_Button();
51014
+ init_DialogueBox();
51015
+ init_boardEntity();
51016
+ DEFAULT_NODES = [
51017
+ {
51018
+ id: "start",
51019
+ speaker: "Narrator",
51020
+ text: "The corridor stretches into shadow. A lone sentinel blocks your path.",
51021
+ backgroundKey: "corridor",
51022
+ portraitKey: "guide",
51023
+ choices: [
51024
+ { label: "Approach the sentinel.", nextId: "meet" },
51025
+ { label: "Turn back.", nextId: "retreat" }
51026
+ ]
51027
+ },
51028
+ {
51029
+ id: "meet",
51030
+ speaker: "Sentinel",
51031
+ text: "You carry the old marks. Speak your purpose, traveler.",
51032
+ backgroundKey: "forge",
51033
+ portraitKey: "rival",
51034
+ choices: [
51035
+ { label: "I seek the core.", nextId: "end" },
51036
+ { label: "I am only passing through.", nextId: "retreat" }
51037
+ ]
51038
+ },
51039
+ {
51040
+ id: "retreat",
51041
+ speaker: "Narrator",
51042
+ text: "You withdraw into the dark. The path will wait for braver feet.",
51043
+ backgroundKey: "corridor",
51044
+ choices: [{ label: "Begin again.", nextId: "start" }]
51045
+ },
51046
+ {
51047
+ id: "end",
51048
+ speaker: "Sentinel",
51049
+ text: "Then the way is open. Walk it well.",
51050
+ backgroundKey: "core",
51051
+ portraitKey: "rival",
51052
+ choices: [{ label: "Begin again.", nextId: "start" }]
51053
+ }
51054
+ ];
51055
+ VisualNovelBoard.displayName = "VisualNovelBoard";
51056
+ }
51057
+ });
51058
+ function VisualNovelTemplate({
51059
+ entity,
51060
+ title = "Visual Novel",
51061
+ nodes,
51062
+ currentNodeId,
51063
+ typewriterSpeed,
51064
+ portraitScale,
51065
+ chooseEvent,
51066
+ advanceEvent,
51067
+ restartEvent,
51068
+ className
51069
+ }) {
51070
+ const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
51071
+ return /* @__PURE__ */ jsxs(
51072
+ Box,
51073
+ {
51074
+ display: "flex",
51075
+ fullHeight: true,
51076
+ className: cn("visual-novel-template flex-col", className),
51077
+ children: [
51078
+ /* @__PURE__ */ jsx(
51079
+ HStack,
51080
+ {
51081
+ gap: "sm",
51082
+ align: "center",
51083
+ className: "px-4 py-3 border-b-2 border-border bg-surface shrink-0",
51084
+ children: /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title })
51085
+ }
51086
+ ),
51087
+ /* @__PURE__ */ jsx(Box, { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsx(
51088
+ VisualNovelBoard,
51089
+ {
51090
+ entity: resolved,
51091
+ nodes,
51092
+ currentNodeId,
51093
+ typewriterSpeed,
51094
+ portraitScale,
51095
+ chooseEvent,
51096
+ advanceEvent,
51097
+ restartEvent,
51098
+ className: "h-full"
51099
+ }
51100
+ ) })
51101
+ ]
51102
+ }
51103
+ );
51104
+ }
51105
+ var init_VisualNovelTemplate = __esm({
51106
+ "components/game/templates/VisualNovelTemplate.tsx"() {
51107
+ init_cn();
51108
+ init_Box();
51109
+ init_Stack();
51110
+ init_Typography();
51111
+ init_VisualNovelBoard();
51112
+ VisualNovelTemplate.displayName = "VisualNovelTemplate";
51113
+ }
51114
+ });
49716
51115
  function heroPosition(h) {
49717
51116
  if ("position" in h && h.position != null) return h.position;
49718
51117
  return vec2(h.position);
@@ -50053,48 +51452,48 @@ function WorldMapTemplate({
50053
51452
  }
50054
51453
  );
50055
51454
  }
50056
- var CDN7, DEFAULT_WORLDMAP_TILES, DEFAULT_WORLDMAP_UNITS, DEFAULT_WORLDMAP_FEATURES, DEFAULT_WORLDMAP_MANIFEST;
51455
+ var CDN5, DEFAULT_WORLDMAP_TILES, DEFAULT_WORLDMAP_UNITS, DEFAULT_WORLDMAP_FEATURES, DEFAULT_WORLDMAP_MANIFEST;
50057
51456
  var init_WorldMapTemplate = __esm({
50058
51457
  "components/game/templates/WorldMapTemplate.tsx"() {
50059
51458
  init_WorldMapBoard();
50060
- CDN7 = "https://almadar-kflow-assets.web.app/shared";
51459
+ CDN5 = "https://almadar-kflow-assets.web.app/shared";
50061
51460
  DEFAULT_WORLDMAP_TILES = [
50062
- { x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50063
- { x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50064
- { x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
50065
- { x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50066
- { x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50067
- { x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50068
- { x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50069
- { x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50070
- { x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50071
- { x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
50072
- { x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
50073
- { x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50074
- { x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
50075
- { x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50076
- { x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50077
- { x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50078
- { x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50079
- { x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50080
- { x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
50081
- { x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50082
- { x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50083
- { x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
50084
- { x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50085
- { x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50086
- { x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` }
51461
+ { x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51462
+ { x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51463
+ { x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
51464
+ { x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51465
+ { x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51466
+ { x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51467
+ { x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51468
+ { x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51469
+ { x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51470
+ { x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
51471
+ { x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
51472
+ { x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51473
+ { x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
51474
+ { x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51475
+ { x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51476
+ { x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51477
+ { x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51478
+ { x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51479
+ { x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
51480
+ { x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51481
+ { x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51482
+ { x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
51483
+ { x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51484
+ { x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51485
+ { x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` }
50087
51486
  ];
50088
51487
  DEFAULT_WORLDMAP_UNITS = [
50089
- { 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` },
50090
- { 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` }
51488
+ { 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` },
51489
+ { 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` }
50091
51490
  ];
50092
51491
  DEFAULT_WORLDMAP_FEATURES = [
50093
- { id: "f1", x: 2, y: 2, type: "capital", sprite: `${CDN7}/scenes/world/capital.png` },
50094
- { id: "f2", x: 4, y: 2, type: "power_node", sprite: `${CDN7}/world-map/power_node.png` }
51492
+ { id: "f1", x: 2, y: 2, type: "capital", sprite: `${CDN5}/scenes/world/capital.png` },
51493
+ { id: "f2", x: 4, y: 2, type: "power_node", sprite: `${CDN5}/world-map/power_node.png` }
50095
51494
  ];
50096
51495
  DEFAULT_WORLDMAP_MANIFEST = {
50097
- baseUrl: CDN7,
51496
+ baseUrl: CDN5,
50098
51497
  terrains: {
50099
51498
  grass: "/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png",
50100
51499
  water: "/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png",
@@ -50114,7 +51513,7 @@ var init_WorldMapTemplate = __esm({
50114
51513
  }
50115
51514
  });
50116
51515
  function lazyThree(name, loader) {
50117
- const Lazy = React81__default.lazy(
51516
+ const Lazy = React84__default.lazy(
50118
51517
  () => loader().then((m) => {
50119
51518
  const Resolved = m[name];
50120
51519
  if (!Resolved) {
@@ -50126,13 +51525,13 @@ function lazyThree(name, loader) {
50126
51525
  })
50127
51526
  );
50128
51527
  function ThreeWrapper(props) {
50129
- return React81__default.createElement(
51528
+ return React84__default.createElement(
50130
51529
  ThreeBoundary,
50131
51530
  { name },
50132
- React81__default.createElement(
50133
- React81__default.Suspense,
51531
+ React84__default.createElement(
51532
+ React84__default.Suspense,
50134
51533
  { fallback: null },
50135
- React81__default.createElement(Lazy, props)
51534
+ React84__default.createElement(Lazy, props)
50136
51535
  )
50137
51536
  );
50138
51537
  }
@@ -50176,7 +51575,10 @@ var init_component_registry_generated = __esm({
50176
51575
  init_CalendarGrid();
50177
51576
  init_CanvasEffect();
50178
51577
  init_Card();
51578
+ init_CardBattlerBoard();
51579
+ init_CardBattlerTemplate();
50179
51580
  init_CardGrid();
51581
+ init_CardHand();
50180
51582
  init_Carousel();
50181
51583
  init_CaseStudyCard();
50182
51584
  init_CaseStudyOrganism();
@@ -50187,6 +51589,8 @@ var init_component_registry_generated = __esm({
50187
51589
  init_ChartLegend();
50188
51590
  init_Checkbox();
50189
51591
  init_ChoiceButton();
51592
+ init_CityBuilderBoard();
51593
+ init_CityBuilderTemplate();
50190
51594
  init_ClassifierBoard();
50191
51595
  init_CodeBlock();
50192
51596
  init_CodeRunnerPanel();
@@ -50407,6 +51811,8 @@ var init_component_registry_generated = __esm({
50407
51811
  init_Toast();
50408
51812
  init_ToastSlot();
50409
51813
  init_Tooltip();
51814
+ init_TopDownShooterBoard();
51815
+ init_TopDownShooterTemplate();
50410
51816
  init_TowerDefenseBoard();
50411
51817
  init_TowerDefenseTemplate();
50412
51818
  init_TraitFrame();
@@ -50422,6 +51828,8 @@ var init_component_registry_generated = __esm({
50422
51828
  init_UploadDropZone();
50423
51829
  init_VersionDiff();
50424
51830
  init_ViolationAlert();
51831
+ init_VisualNovelBoard();
51832
+ init_VisualNovelTemplate();
50425
51833
  init_VoteStack();
50426
51834
  init_WaypointMarker();
50427
51835
  init_WizardContainer();
@@ -50430,7 +51838,7 @@ var init_component_registry_generated = __esm({
50430
51838
  init_WorldMapBoard();
50431
51839
  init_WorldMapTemplate();
50432
51840
  init_XPBar();
50433
- ThreeBoundary = class extends React81__default.Component {
51841
+ ThreeBoundary = class extends React84__default.Component {
50434
51842
  constructor() {
50435
51843
  super(...arguments);
50436
51844
  __publicField(this, "state", { failed: false });
@@ -50440,7 +51848,7 @@ var init_component_registry_generated = __esm({
50440
51848
  }
50441
51849
  render() {
50442
51850
  if (this.state.failed) {
50443
- return React81__default.createElement(
51851
+ return React84__default.createElement(
50444
51852
  "div",
50445
51853
  {
50446
51854
  "data-testid": "three-unavailable",
@@ -50496,7 +51904,10 @@ var init_component_registry_generated = __esm({
50496
51904
  "CalendarGrid": CalendarGrid,
50497
51905
  "CanvasEffect": CanvasEffect,
50498
51906
  "Card": Card,
51907
+ "CardBattlerBoard": CardBattlerBoard,
51908
+ "CardBattlerTemplate": CardBattlerTemplate,
50499
51909
  "CardGrid": CardGrid,
51910
+ "CardHand": CardHand,
50500
51911
  "Carousel": Carousel,
50501
51912
  "CaseStudyCard": CaseStudyCard,
50502
51913
  "CaseStudyOrganism": CaseStudyOrganism,
@@ -50507,6 +51918,8 @@ var init_component_registry_generated = __esm({
50507
51918
  "ChartLegend": ChartLegend,
50508
51919
  "Checkbox": Checkbox,
50509
51920
  "ChoiceButton": ChoiceButton,
51921
+ "CityBuilderBoard": CityBuilderBoard,
51922
+ "CityBuilderTemplate": CityBuilderTemplate,
50510
51923
  "ClassifierBoard": ClassifierBoard,
50511
51924
  "CodeBlock": CodeBlock,
50512
51925
  "CodeRunnerPanel": CodeRunnerPanel,
@@ -50739,6 +52152,8 @@ var init_component_registry_generated = __esm({
50739
52152
  "Toast": Toast,
50740
52153
  "ToastSlot": ToastSlot,
50741
52154
  "Tooltip": Tooltip,
52155
+ "TopDownShooterBoard": TopDownShooterBoard,
52156
+ "TopDownShooterTemplate": TopDownShooterTemplate,
50742
52157
  "TowerDefenseBoard": TowerDefenseBoard,
50743
52158
  "TowerDefenseTemplate": TowerDefenseTemplate,
50744
52159
  "TraitFrame": TraitFrame,
@@ -50755,6 +52170,8 @@ var init_component_registry_generated = __esm({
50755
52170
  "VStack": VStack,
50756
52171
  "VersionDiff": VersionDiff,
50757
52172
  "ViolationAlert": ViolationAlert,
52173
+ "VisualNovelBoard": VisualNovelBoard,
52174
+ "VisualNovelTemplate": VisualNovelTemplate,
50758
52175
  "VoteStack": VoteStack,
50759
52176
  "WaypointMarker": WaypointMarker,
50760
52177
  "WizardContainer": WizardContainer,
@@ -50781,7 +52198,7 @@ function SuspenseConfigProvider({
50781
52198
  config,
50782
52199
  children
50783
52200
  }) {
50784
- return React81__default.createElement(
52201
+ return React84__default.createElement(
50785
52202
  SuspenseConfigContext.Provider,
50786
52203
  { value: config },
50787
52204
  children
@@ -51271,7 +52688,7 @@ function renderPatternChildren(children, onDismiss, parentId = "root", parentPat
51271
52688
  const key = `${parentId}-${index}-trait:${traitName}`;
51272
52689
  return /* @__PURE__ */ jsx(TraitFrame, { traitName }, key);
51273
52690
  }
51274
- return /* @__PURE__ */ jsx(React81__default.Fragment, { children: child }, `${parentId}-${index}`);
52691
+ return /* @__PURE__ */ jsx(React84__default.Fragment, { children: child }, `${parentId}-${index}`);
51275
52692
  }
51276
52693
  if (!child || typeof child !== "object") return null;
51277
52694
  const childId = `${parentId}-${index}`;
@@ -51311,14 +52728,14 @@ function isPatternConfig(value) {
51311
52728
  if (value === null || value === void 0) return false;
51312
52729
  if (typeof value !== "object") return false;
51313
52730
  if (Array.isArray(value)) return false;
51314
- if (React81__default.isValidElement(value)) return false;
52731
+ if (React84__default.isValidElement(value)) return false;
51315
52732
  if (value instanceof Date) return false;
51316
52733
  if (typeof value === "function") return false;
51317
52734
  const record = value;
51318
52735
  return "type" in record && typeof record.type === "string" && getComponentForPattern$1(record.type) !== null;
51319
52736
  }
51320
52737
  function isPlainConfigObject(value) {
51321
- if (React81__default.isValidElement(value)) return false;
52738
+ if (React84__default.isValidElement(value)) return false;
51322
52739
  if (value instanceof Date) return false;
51323
52740
  const proto = Object.getPrototypeOf(value);
51324
52741
  return proto === Object.prototype || proto === null;
@@ -51439,6 +52856,22 @@ function SlotContentRenderer({
51439
52856
  const childrenIsRenderFn = typeof incomingChildren === "function";
51440
52857
  const { children: _childrenConfig, ...restPropsNoChildren } = content.props;
51441
52858
  const restProps = childrenIsRenderFn ? { ...restPropsNoChildren, children: incomingChildren } : restPropsNoChildren;
52859
+ const nodeSlotOverrides = {};
52860
+ for (const slotKey of CONTENT_NODE_SLOTS) {
52861
+ const slotVal = restProps[slotKey];
52862
+ if (slotVal === void 0 || slotVal === null) continue;
52863
+ if (React84__default.isValidElement(slotVal) || typeof slotVal === "string" || typeof slotVal === "number" || typeof slotVal === "boolean") continue;
52864
+ if (Array.isArray(slotVal) || typeof slotVal === "object" && "type" in slotVal) {
52865
+ nodeSlotOverrides[slotKey] = renderPatternChildren(
52866
+ slotVal,
52867
+ onDismiss,
52868
+ `${content.id}-${slotKey}`,
52869
+ `${myPath}.${slotKey}`,
52870
+ content.sourceTrait,
52871
+ { slot: content.slot, transitionEvent: content.transitionEvent, fromState: content.fromState, entity: content.entity }
52872
+ );
52873
+ }
52874
+ }
51442
52875
  const renderedProps = renderPatternProps(restProps, onDismiss);
51443
52876
  const patternDef = getPatternDefinition(content.pattern);
51444
52877
  const propsSchema = patternDef?.propsSchema;
@@ -51469,6 +52902,9 @@ function SlotContentRenderer({
51469
52902
  }
51470
52903
  }
51471
52904
  const finalProps = renderedProps;
52905
+ for (const [k, v] of Object.entries(nodeSlotOverrides)) {
52906
+ finalProps[k] = v;
52907
+ }
51472
52908
  const resolvedItems = Array.isArray(
51473
52909
  finalProps.entity
51474
52910
  ) ? finalProps.entity : null;
@@ -51589,7 +53025,7 @@ function UISlotRenderer({
51589
53025
  }
51590
53026
  return wrapped;
51591
53027
  }
51592
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, FORM_PATTERNS, SELF_OVERLAY_PATTERNS, PATTERNS_WITH_CHILDREN;
53028
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, FORM_PATTERNS, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
51593
53029
  var init_UISlotRenderer = __esm({
51594
53030
  "components/core/organisms/UISlotRenderer.tsx"() {
51595
53031
  "use client";
@@ -51626,6 +53062,16 @@ var init_UISlotRenderer = __esm({
51626
53062
  "wizard-step"
51627
53063
  ]);
51628
53064
  SELF_OVERLAY_PATTERNS = /* @__PURE__ */ new Set(["modal", "confirm-dialog"]);
53065
+ CONTENT_NODE_SLOTS = /* @__PURE__ */ new Set([
53066
+ "logo",
53067
+ "master",
53068
+ "detail",
53069
+ "trigger",
53070
+ "content",
53071
+ "addons",
53072
+ "hud",
53073
+ "fallback"
53074
+ ]);
51629
53075
  PATTERNS_WITH_CHILDREN = /* @__PURE__ */ new Set([
51630
53076
  "stack",
51631
53077
  "vstack",
@@ -51750,7 +53196,7 @@ function resolveLambdaBindings(body, params, item, index) {
51750
53196
  if (Array.isArray(body)) {
51751
53197
  return body.map((b) => recur(b));
51752
53198
  }
51753
- if (body !== null && typeof body === "object" && !React81__default.isValidElement(body) && !(body instanceof Date) && typeof body !== "function") {
53199
+ if (body !== null && typeof body === "object" && !React84__default.isValidElement(body) && !(body instanceof Date) && typeof body !== "function") {
51754
53200
  const out = {};
51755
53201
  for (const [k, v] of Object.entries(body)) {
51756
53202
  out[k] = recur(v);
@@ -51769,7 +53215,7 @@ function getSlotContentRenderer2() {
51769
53215
  function makeLambdaFn(params, lambdaBody, callerKey) {
51770
53216
  return (item, index) => {
51771
53217
  const resolvedBody = resolveLambdaBindings(lambdaBody, params, item, index);
51772
- if (resolvedBody === null || typeof resolvedBody !== "object" || Array.isArray(resolvedBody) || typeof resolvedBody === "function" || React81__default.isValidElement(resolvedBody) || resolvedBody instanceof Date) {
53218
+ if (resolvedBody === null || typeof resolvedBody !== "object" || Array.isArray(resolvedBody) || typeof resolvedBody === "function" || React84__default.isValidElement(resolvedBody) || resolvedBody instanceof Date) {
51773
53219
  return null;
51774
53220
  }
51775
53221
  const record = resolvedBody;
@@ -51788,7 +53234,7 @@ function makeLambdaFn(params, lambdaBody, callerKey) {
51788
53234
  props: childProps,
51789
53235
  priority: 0
51790
53236
  };
51791
- return React81__default.createElement(SlotContentRenderer2, { content: childContent });
53237
+ return React84__default.createElement(SlotContentRenderer2, { content: childContent });
51792
53238
  };
51793
53239
  }
51794
53240
  function convertNode(node, callerKey) {
@@ -51807,7 +53253,7 @@ function convertNode(node, callerKey) {
51807
53253
  });
51808
53254
  return anyChanged ? mapped : node;
51809
53255
  }
51810
- if (typeof node === "object" && !React81__default.isValidElement(node) && !(node instanceof Date)) {
53256
+ if (typeof node === "object" && !React84__default.isValidElement(node) && !(node instanceof Date)) {
51811
53257
  return convertObjectProps(node);
51812
53258
  }
51813
53259
  return node;