@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 React82 from 'react';
2
- import React82__default, { createContext, useContext, useMemo, useRef, useEffect, useCallback, Suspense, useState, useLayoutEffect, lazy, useId, useSyncExternalStore, forwardRef, useImperativeHandle, Component } from 'react';
1
+ import * as React85 from 'react';
2
+ import React85__default, { createContext, useContext, useMemo, useRef, useEffect, useCallback, Suspense, useState, useLayoutEffect, lazy, useId, useSyncExternalStore, forwardRef, useImperativeHandle, Component } from 'react';
3
3
  import { EventBusContext, useTraitScope, TraitScopeProvider } from '@almadar/ui/providers';
4
4
  import { createLogger, isLogLevelEnabled } from '@almadar/logger';
5
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -354,7 +354,7 @@ var init_Box = __esm({
354
354
  fixed: "fixed",
355
355
  sticky: "sticky"
356
356
  };
357
- Box = React82__default.forwardRef(
357
+ Box = React85__default.forwardRef(
358
358
  ({
359
359
  padding,
360
360
  paddingX,
@@ -404,7 +404,7 @@ var init_Box = __esm({
404
404
  onMouseLeave?.(e);
405
405
  }, [hoverEvent, eventBus, onMouseLeave]);
406
406
  const isClickable = action || onClick;
407
- return React82__default.createElement(
407
+ return React85__default.createElement(
408
408
  Component2,
409
409
  {
410
410
  ref,
@@ -499,7 +499,7 @@ function loadLib(key, importer) {
499
499
  return p2;
500
500
  }
501
501
  function lazyFamilyIcon(libKey, importer, pick, fallbackName, family) {
502
- const Lazy = React82__default.lazy(async () => {
502
+ const Lazy = React85__default.lazy(async () => {
503
503
  const lib = await loadLib(libKey, importer);
504
504
  const Comp = pick(lib);
505
505
  if (!Comp) {
@@ -509,7 +509,7 @@ function lazyFamilyIcon(libKey, importer, pick, fallbackName, family) {
509
509
  return { default: Comp };
510
510
  });
511
511
  const Wrapped = (props) => /* @__PURE__ */ jsx(
512
- React82__default.Suspense,
512
+ React85__default.Suspense,
513
513
  {
514
514
  fallback: /* @__PURE__ */ jsx(
515
515
  "span",
@@ -1235,7 +1235,7 @@ var init_Icon = __esm({
1235
1235
  const directIcon = typeof icon === "string" ? void 0 : icon;
1236
1236
  const effectiveName = typeof icon === "string" ? icon : name;
1237
1237
  const family = useIconFamily();
1238
- const RenderedComponent = React82__default.useMemo(() => {
1238
+ const RenderedComponent = React85__default.useMemo(() => {
1239
1239
  if (directIcon) return null;
1240
1240
  return effectiveName ? resolveIconForFamily(effectiveName, family) : null;
1241
1241
  }, [directIcon, effectiveName, family]);
@@ -1294,7 +1294,7 @@ function resolveIconProp(value, sizeClass) {
1294
1294
  const IconComp = value;
1295
1295
  return /* @__PURE__ */ jsx(IconComp, { className: sizeClass });
1296
1296
  }
1297
- if (React82__default.isValidElement(value)) {
1297
+ if (React85__default.isValidElement(value)) {
1298
1298
  return value;
1299
1299
  }
1300
1300
  if (typeof value === "object" && value !== null && "render" in value) {
@@ -1370,7 +1370,7 @@ var init_Button = __esm({
1370
1370
  md: "h-icon-default w-icon-default",
1371
1371
  lg: "h-icon-default w-icon-default"
1372
1372
  };
1373
- Button = React82__default.forwardRef(
1373
+ Button = React85__default.forwardRef(
1374
1374
  ({
1375
1375
  className,
1376
1376
  variant = "primary",
@@ -1435,7 +1435,7 @@ var Dialog;
1435
1435
  var init_Dialog = __esm({
1436
1436
  "components/core/atoms/Dialog.tsx"() {
1437
1437
  init_cn();
1438
- Dialog = React82__default.forwardRef(
1438
+ Dialog = React85__default.forwardRef(
1439
1439
  ({
1440
1440
  role = "dialog",
1441
1441
  "aria-modal": ariaModal = true,
@@ -2049,7 +2049,7 @@ var init_Badge = __esm({
2049
2049
  md: "px-2.5 py-1 text-sm",
2050
2050
  lg: "px-3 py-1.5 text-base"
2051
2051
  };
2052
- Badge = React82__default.forwardRef(
2052
+ Badge = React85__default.forwardRef(
2053
2053
  ({ className, variant = "default", size = "sm", amount, label, icon, children, onRemove, removeLabel, ...props }, ref) => {
2054
2054
  const iconSizes3 = {
2055
2055
  sm: "h-icon-default w-icon-default",
@@ -2385,7 +2385,7 @@ var init_SvgFlow = __esm({
2385
2385
  width = 100,
2386
2386
  height = 100
2387
2387
  }) => {
2388
- const markerId = React82__default.useMemo(() => {
2388
+ const markerId = React85__default.useMemo(() => {
2389
2389
  flowIdCounter += 1;
2390
2390
  return `almadar-flow-arrow-${flowIdCounter}`;
2391
2391
  }, []);
@@ -2978,7 +2978,7 @@ var init_SvgRing = __esm({
2978
2978
  width = 100,
2979
2979
  height = 100
2980
2980
  }) => {
2981
- const gradientId = React82__default.useMemo(() => {
2981
+ const gradientId = React85__default.useMemo(() => {
2982
2982
  ringIdCounter += 1;
2983
2983
  return `almadar-ring-glow-${ringIdCounter}`;
2984
2984
  }, []);
@@ -3159,7 +3159,7 @@ var init_Input = __esm({
3159
3159
  init_cn();
3160
3160
  init_Icon();
3161
3161
  init_useEventBus();
3162
- Input = React82__default.forwardRef(
3162
+ Input = React85__default.forwardRef(
3163
3163
  ({
3164
3164
  className,
3165
3165
  inputType,
@@ -3319,7 +3319,7 @@ var Label;
3319
3319
  var init_Label = __esm({
3320
3320
  "components/core/atoms/Label.tsx"() {
3321
3321
  init_cn();
3322
- Label = React82__default.forwardRef(
3322
+ Label = React85__default.forwardRef(
3323
3323
  ({ className, required, children, ...props }, ref) => {
3324
3324
  return /* @__PURE__ */ jsxs(
3325
3325
  "label",
@@ -3346,7 +3346,7 @@ var init_Textarea = __esm({
3346
3346
  "components/core/atoms/Textarea.tsx"() {
3347
3347
  init_cn();
3348
3348
  init_useEventBus();
3349
- Textarea = React82__default.forwardRef(
3349
+ Textarea = React85__default.forwardRef(
3350
3350
  ({ className, error, onChange, ...props }, ref) => {
3351
3351
  const eventBus = useEventBus();
3352
3352
  const handleChange = (e) => {
@@ -3585,7 +3585,7 @@ var init_Select = __esm({
3585
3585
  init_cn();
3586
3586
  init_Icon();
3587
3587
  init_useEventBus();
3588
- Select = React82__default.forwardRef(
3588
+ Select = React85__default.forwardRef(
3589
3589
  (props, _ref) => {
3590
3590
  const { multiple, searchable, clearable } = props;
3591
3591
  if (multiple || searchable || clearable) {
@@ -3602,7 +3602,7 @@ var init_Checkbox = __esm({
3602
3602
  "components/core/atoms/Checkbox.tsx"() {
3603
3603
  init_cn();
3604
3604
  init_useEventBus();
3605
- Checkbox = React82__default.forwardRef(
3605
+ Checkbox = React85__default.forwardRef(
3606
3606
  ({ className, label, id, onChange, ...props }, ref) => {
3607
3607
  const inputId = id || `checkbox-${Math.random().toString(36).substr(2, 9)}`;
3608
3608
  const eventBus = useEventBus();
@@ -3656,7 +3656,7 @@ var init_Spinner = __esm({
3656
3656
  md: "h-6 w-6",
3657
3657
  lg: "h-8 w-8"
3658
3658
  };
3659
- Spinner = React82__default.forwardRef(
3659
+ Spinner = React85__default.forwardRef(
3660
3660
  ({ className, size = "md", overlay, ...props }, ref) => {
3661
3661
  if (overlay) {
3662
3662
  return /* @__PURE__ */ jsx(
@@ -3746,7 +3746,7 @@ var init_Card = __esm({
3746
3746
  chip: "shadow-none rounded-pill border-[length:var(--border-width)] border-border",
3747
3747
  "tile-image-first": "p-0 overflow-hidden"
3748
3748
  };
3749
- Card = React82__default.forwardRef(
3749
+ Card = React85__default.forwardRef(
3750
3750
  ({
3751
3751
  className,
3752
3752
  variant = "bordered",
@@ -3794,9 +3794,9 @@ var init_Card = __esm({
3794
3794
  }
3795
3795
  );
3796
3796
  Card.displayName = "Card";
3797
- CardHeader = React82__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("mb-4", className), ...props }));
3797
+ CardHeader = React85__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("mb-4", className), ...props }));
3798
3798
  CardHeader.displayName = "CardHeader";
3799
- CardTitle = React82__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3799
+ CardTitle = React85__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3800
3800
  "h3",
3801
3801
  {
3802
3802
  ref,
@@ -3809,11 +3809,11 @@ var init_Card = __esm({
3809
3809
  }
3810
3810
  ));
3811
3811
  CardTitle.displayName = "CardTitle";
3812
- CardContent = React82__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("", className), ...props }));
3812
+ CardContent = React85__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("", className), ...props }));
3813
3813
  CardContent.displayName = "CardContent";
3814
3814
  CardBody = CardContent;
3815
3815
  CardBody.displayName = "CardBody";
3816
- CardFooter = React82__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3816
+ CardFooter = React85__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3817
3817
  "div",
3818
3818
  {
3819
3819
  ref,
@@ -3868,7 +3868,7 @@ var init_FilterPill = __esm({
3868
3868
  md: "w-3.5 h-3.5",
3869
3869
  lg: "w-4 h-4"
3870
3870
  };
3871
- FilterPill = React82__default.forwardRef(
3871
+ FilterPill = React85__default.forwardRef(
3872
3872
  ({
3873
3873
  className,
3874
3874
  variant = "default",
@@ -3997,8 +3997,8 @@ var init_Avatar = __esm({
3997
3997
  actionPayload
3998
3998
  }) => {
3999
3999
  const eventBus = useEventBus();
4000
- const [imgFailed, setImgFailed] = React82__default.useState(false);
4001
- React82__default.useEffect(() => {
4000
+ const [imgFailed, setImgFailed] = React85__default.useState(false);
4001
+ React85__default.useEffect(() => {
4002
4002
  setImgFailed(false);
4003
4003
  }, [src]);
4004
4004
  const initials = providedInitials ?? (name ? generateInitials(name) : void 0);
@@ -4383,7 +4383,7 @@ var init_Radio = __esm({
4383
4383
  md: "w-2.5 h-2.5",
4384
4384
  lg: "w-3 h-3"
4385
4385
  };
4386
- Radio = React82__default.forwardRef(
4386
+ Radio = React85__default.forwardRef(
4387
4387
  ({
4388
4388
  label,
4389
4389
  helperText,
@@ -4400,12 +4400,12 @@ var init_Radio = __esm({
4400
4400
  onChange,
4401
4401
  ...props
4402
4402
  }, ref) => {
4403
- const reactId = React82__default.useId();
4403
+ const reactId = React85__default.useId();
4404
4404
  const baseId = id || `radio-${reactId}`;
4405
4405
  const hasError = !!error;
4406
4406
  const eventBus = useEventBus();
4407
- const [selected, setSelected] = React82__default.useState(value);
4408
- React82__default.useEffect(() => {
4407
+ const [selected, setSelected] = React85__default.useState(value);
4408
+ React85__default.useEffect(() => {
4409
4409
  if (value !== void 0) setSelected(value);
4410
4410
  }, [value]);
4411
4411
  const pick = (next, e) => {
@@ -4587,7 +4587,7 @@ var init_Switch = __esm({
4587
4587
  "components/core/atoms/Switch.tsx"() {
4588
4588
  "use client";
4589
4589
  init_cn();
4590
- Switch = React82.forwardRef(
4590
+ Switch = React85.forwardRef(
4591
4591
  ({
4592
4592
  checked,
4593
4593
  defaultChecked = false,
@@ -4598,10 +4598,10 @@ var init_Switch = __esm({
4598
4598
  name,
4599
4599
  className
4600
4600
  }, ref) => {
4601
- const [isChecked, setIsChecked] = React82.useState(
4601
+ const [isChecked, setIsChecked] = React85.useState(
4602
4602
  checked !== void 0 ? checked : defaultChecked
4603
4603
  );
4604
- React82.useEffect(() => {
4604
+ React85.useEffect(() => {
4605
4605
  if (checked !== void 0) {
4606
4606
  setIsChecked(checked);
4607
4607
  }
@@ -4956,7 +4956,7 @@ var Aside;
4956
4956
  var init_Aside = __esm({
4957
4957
  "components/core/atoms/Aside.tsx"() {
4958
4958
  init_cn();
4959
- Aside = React82__default.forwardRef(
4959
+ Aside = React85__default.forwardRef(
4960
4960
  ({ className, children, ...rest }, ref) => /* @__PURE__ */ jsx("aside", { ref, className: cn(className), ...rest, children })
4961
4961
  );
4962
4962
  Aside.displayName = "Aside";
@@ -5034,8 +5034,8 @@ var init_LawReferenceTooltip = __esm({
5034
5034
  className
5035
5035
  }) => {
5036
5036
  const { t } = useTranslate();
5037
- const [isVisible, setIsVisible] = React82__default.useState(false);
5038
- const timeoutRef = React82__default.useRef(null);
5037
+ const [isVisible, setIsVisible] = React85__default.useState(false);
5038
+ const timeoutRef = React85__default.useRef(null);
5039
5039
  const handleMouseEnter = () => {
5040
5040
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
5041
5041
  timeoutRef.current = setTimeout(() => setIsVisible(true), 200);
@@ -5044,7 +5044,7 @@ var init_LawReferenceTooltip = __esm({
5044
5044
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
5045
5045
  setIsVisible(false);
5046
5046
  };
5047
- React82__default.useEffect(() => {
5047
+ React85__default.useEffect(() => {
5048
5048
  return () => {
5049
5049
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
5050
5050
  };
@@ -5254,7 +5254,7 @@ var init_StatusDot = __esm({
5254
5254
  md: "w-2.5 h-2.5",
5255
5255
  lg: "w-3 h-3"
5256
5256
  };
5257
- StatusDot = React82__default.forwardRef(
5257
+ StatusDot = React85__default.forwardRef(
5258
5258
  ({ className, status = "offline", pulse = false, size = "md", label, ...props }, ref) => {
5259
5259
  return /* @__PURE__ */ jsx(
5260
5260
  "span",
@@ -5308,7 +5308,7 @@ var init_TrendIndicator = __esm({
5308
5308
  down: "trending-down",
5309
5309
  flat: "arrow-right"
5310
5310
  };
5311
- TrendIndicator = React82__default.forwardRef(
5311
+ TrendIndicator = React85__default.forwardRef(
5312
5312
  ({
5313
5313
  className,
5314
5314
  value,
@@ -5375,7 +5375,7 @@ var init_RangeSlider = __esm({
5375
5375
  md: "w-4 h-4",
5376
5376
  lg: "w-5 h-5"
5377
5377
  };
5378
- RangeSlider = React82__default.forwardRef(
5378
+ RangeSlider = React85__default.forwardRef(
5379
5379
  ({
5380
5380
  className,
5381
5381
  min = 0,
@@ -5883,7 +5883,7 @@ var init_ContentSection = __esm({
5883
5883
  md: "py-16",
5884
5884
  lg: "py-24"
5885
5885
  };
5886
- ContentSection = React82__default.forwardRef(
5886
+ ContentSection = React85__default.forwardRef(
5887
5887
  ({ children, background = "default", padding = "lg", id, className }, ref) => {
5888
5888
  return /* @__PURE__ */ jsx(
5889
5889
  Box,
@@ -6417,7 +6417,7 @@ var init_AnimatedReveal = __esm({
6417
6417
  "scale-up": { opacity: 1, transform: "scale(1) translateY(0)" },
6418
6418
  "none": {}
6419
6419
  };
6420
- AnimatedReveal = React82__default.forwardRef(
6420
+ AnimatedReveal = React85__default.forwardRef(
6421
6421
  ({
6422
6422
  trigger = "scroll",
6423
6423
  animation = "fade-up",
@@ -6577,7 +6577,7 @@ var init_AnimatedGraphic = __esm({
6577
6577
  "components/marketing/atoms/AnimatedGraphic.tsx"() {
6578
6578
  "use client";
6579
6579
  init_cn();
6580
- AnimatedGraphic = React82__default.forwardRef(
6580
+ AnimatedGraphic = React85__default.forwardRef(
6581
6581
  ({
6582
6582
  src,
6583
6583
  svgContent,
@@ -6600,7 +6600,7 @@ var init_AnimatedGraphic = __esm({
6600
6600
  const fetchedSvg = useFetchedSvg(svgContent ? void 0 : src);
6601
6601
  const resolvedSvg = svgContent ?? fetchedSvg;
6602
6602
  const prevAnimateRef = useRef(animate);
6603
- const setRef = React82__default.useCallback(
6603
+ const setRef = React85__default.useCallback(
6604
6604
  (node) => {
6605
6605
  containerRef.current = node;
6606
6606
  if (typeof ref === "function") ref(node);
@@ -6826,9 +6826,9 @@ function ScoreDisplay({
6826
6826
  ...rest
6827
6827
  }) {
6828
6828
  const resolvedValue = typeof value === "number" && !Number.isNaN(value) ? value : typeof rest.score === "number" && !Number.isNaN(rest.score) ? rest.score : 0;
6829
- const [displayValue, setDisplayValue] = React82.useState(resolvedValue);
6830
- const [isAnimating, setIsAnimating] = React82.useState(false);
6831
- React82.useEffect(() => {
6829
+ const [displayValue, setDisplayValue] = React85.useState(resolvedValue);
6830
+ const [isAnimating, setIsAnimating] = React85.useState(false);
6831
+ React85.useEffect(() => {
6832
6832
  if (!animated || displayValue === resolvedValue) {
6833
6833
  setDisplayValue(resolvedValue);
6834
6834
  return;
@@ -6911,9 +6911,9 @@ function ControlButton({
6911
6911
  className
6912
6912
  }) {
6913
6913
  const eventBus = useEventBus();
6914
- const [isPressed, setIsPressed] = React82.useState(false);
6914
+ const [isPressed, setIsPressed] = React85.useState(false);
6915
6915
  const actualPressed = pressed ?? isPressed;
6916
- const handlePointerDown = React82.useCallback(
6916
+ const handlePointerDown = React85.useCallback(
6917
6917
  (e) => {
6918
6918
  e.preventDefault();
6919
6919
  if (disabled) return;
@@ -6923,7 +6923,7 @@ function ControlButton({
6923
6923
  },
6924
6924
  [disabled, pressEvent, eventBus, onPress]
6925
6925
  );
6926
- const handlePointerUp = React82.useCallback(
6926
+ const handlePointerUp = React85.useCallback(
6927
6927
  (e) => {
6928
6928
  e.preventDefault();
6929
6929
  if (disabled) return;
@@ -6933,7 +6933,7 @@ function ControlButton({
6933
6933
  },
6934
6934
  [disabled, releaseEvent, eventBus, onRelease]
6935
6935
  );
6936
- const handlePointerLeave = React82.useCallback(
6936
+ const handlePointerLeave = React85.useCallback(
6937
6937
  (e) => {
6938
6938
  if (isPressed) {
6939
6939
  setIsPressed(false);
@@ -7554,8 +7554,8 @@ function XPBar({
7554
7554
  }) {
7555
7555
  const sizes = sizeMap9[size];
7556
7556
  const percentage = max > 0 ? Math.max(0, Math.min(100, current / max * 100)) : 0;
7557
- const [fillWidth, setFillWidth] = React82.useState(animated ? 0 : percentage);
7558
- React82.useEffect(() => {
7557
+ const [fillWidth, setFillWidth] = React85.useState(animated ? 0 : percentage);
7558
+ React85.useEffect(() => {
7559
7559
  if (!animated) {
7560
7560
  setFillWidth(percentage);
7561
7561
  return;
@@ -8043,9 +8043,9 @@ function MiniMap({
8043
8043
  viewportRect = DEFAULT_VIEWPORT,
8044
8044
  className
8045
8045
  }) {
8046
- const canvasRef = React82.useRef(null);
8047
- const frameRef = React82.useRef(0);
8048
- React82.useEffect(() => {
8046
+ const canvasRef = React85.useRef(null);
8047
+ const frameRef = React85.useRef(0);
8048
+ React85.useEffect(() => {
8049
8049
  const canvas = canvasRef.current;
8050
8050
  if (!canvas) return;
8051
8051
  const ctx = canvas.getContext("2d");
@@ -8234,7 +8234,7 @@ var init_ErrorBoundary = __esm({
8234
8234
  }
8235
8235
  );
8236
8236
  };
8237
- ErrorBoundary = class extends React82__default.Component {
8237
+ ErrorBoundary = class extends React85__default.Component {
8238
8238
  constructor(props) {
8239
8239
  super(props);
8240
8240
  __publicField(this, "reset", () => {
@@ -9625,8 +9625,8 @@ function ActionButtons({
9625
9625
  disabled
9626
9626
  }) {
9627
9627
  const eventBus = useEventBus();
9628
- const [activeButtons, setActiveButtons] = React82.useState(/* @__PURE__ */ new Set());
9629
- const handlePress = React82.useCallback(
9628
+ const [activeButtons, setActiveButtons] = React85.useState(/* @__PURE__ */ new Set());
9629
+ const handlePress = React85.useCallback(
9630
9630
  (id) => {
9631
9631
  setActiveButtons((prev) => new Set(prev).add(id));
9632
9632
  if (actionEvent) eventBus.emit(`UI:${actionEvent}`, { id, pressed: true });
@@ -9634,7 +9634,7 @@ function ActionButtons({
9634
9634
  },
9635
9635
  [actionEvent, eventBus, onAction]
9636
9636
  );
9637
- const handleRelease = React82.useCallback(
9637
+ const handleRelease = React85.useCallback(
9638
9638
  (id) => {
9639
9639
  setActiveButtons((prev) => {
9640
9640
  const next = new Set(prev);
@@ -10271,8 +10271,8 @@ var init_AnimatedCounter = __esm({
10271
10271
  const [displayValue, setDisplayValue] = useState("0");
10272
10272
  const [hasAnimated, setHasAnimated] = useState(false);
10273
10273
  const animate = useCallback(() => {
10274
- const { num: num2, prefix, suffix, decimals } = parseValue(value);
10275
- if (num2 === 0) {
10274
+ const { num: num3, prefix, suffix, decimals } = parseValue(value);
10275
+ if (num3 === 0) {
10276
10276
  setDisplayValue(String(value));
10277
10277
  return;
10278
10278
  }
@@ -10281,7 +10281,7 @@ var init_AnimatedCounter = __esm({
10281
10281
  const elapsed = now - startTime;
10282
10282
  const progress = Math.min(elapsed / duration, 1);
10283
10283
  const eased = 1 - Math.pow(1 - progress, 3);
10284
- const current = eased * num2;
10284
+ const current = eased * num3;
10285
10285
  setDisplayValue(`${prefix}${current.toFixed(decimals)}${suffix}`);
10286
10286
  if (progress < 1) {
10287
10287
  requestAnimationFrame(tick);
@@ -11099,7 +11099,7 @@ function IsometricCanvas({
11099
11099
  // Tuning
11100
11100
  diamondTopY: diamondTopYProp,
11101
11101
  // Remote asset loading
11102
- assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/",
11102
+ assetBaseUrl,
11103
11103
  assetManifest
11104
11104
  }) {
11105
11105
  const tilesProp = Array.isArray(_tilesPropRaw) ? _tilesPropRaw : [];
@@ -11178,7 +11178,7 @@ function IsometricCanvas({
11178
11178
  const attackTargetSet = useMemo(() => {
11179
11179
  return new Set(attackTargets.map((p2) => `${p2.x},${p2.y}`));
11180
11180
  }, [attackTargets]);
11181
- const resolveManifestUrl = useCallback((relativePath) => {
11181
+ const resolveManifestUrl7 = useCallback((relativePath) => {
11182
11182
  if (!relativePath) return void 0;
11183
11183
  if (assetBaseUrl) return `${assetBaseUrl.replace(/\/$/, "")}${relativePath.startsWith("/") ? "" : "/"}${relativePath}`;
11184
11184
  return relativePath;
@@ -11191,7 +11191,7 @@ function IsometricCanvas({
11191
11191
  const url = getTerrainSprite(tile.terrain ?? "");
11192
11192
  if (url) urls.push(url);
11193
11193
  } else {
11194
- const url = resolveManifestUrl(assetManifest?.terrains?.[tile.terrain ?? ""]);
11194
+ const url = resolveManifestUrl7(assetManifest?.terrains?.[tile.terrain ?? ""]);
11195
11195
  if (url) urls.push(url);
11196
11196
  }
11197
11197
  }
@@ -11201,7 +11201,7 @@ function IsometricCanvas({
11201
11201
  const url = getFeatureSprite(feature.type);
11202
11202
  if (url) urls.push(url);
11203
11203
  } else {
11204
- const url = resolveManifestUrl(assetManifest?.features?.[feature.type]);
11204
+ const url = resolveManifestUrl7(assetManifest?.features?.[feature.type]);
11205
11205
  if (url) urls.push(url);
11206
11206
  }
11207
11207
  }
@@ -11211,13 +11211,13 @@ function IsometricCanvas({
11211
11211
  const url = getUnitSprite(unit);
11212
11212
  if (url) urls.push(url);
11213
11213
  } else if (unit.unitType) {
11214
- const url = resolveManifestUrl(assetManifest?.units?.[unit.unitType]);
11214
+ const url = resolveManifestUrl7(assetManifest?.units?.[unit.unitType]);
11215
11215
  if (url) urls.push(url);
11216
11216
  }
11217
11217
  }
11218
11218
  if (assetManifest?.effects) {
11219
11219
  for (const path of Object.values(assetManifest.effects)) {
11220
- const url = resolveManifestUrl(path);
11220
+ const url = resolveManifestUrl7(path);
11221
11221
  if (url) urls.push(url);
11222
11222
  }
11223
11223
  }
@@ -11225,7 +11225,7 @@ function IsometricCanvas({
11225
11225
  if (atlasSheetUrls.length) urls.push(...atlasSheetUrls);
11226
11226
  if (backgroundImage) urls.push(backgroundImage);
11227
11227
  return [...new Set(urls.filter(Boolean))];
11228
- }, [sortedTiles, features, units, getTerrainSprite, getFeatureSprite, getUnitSprite, effectSpriteUrls, atlasSheetUrls, backgroundImage, assetManifest, resolveManifestUrl]);
11228
+ }, [sortedTiles, features, units, getTerrainSprite, getFeatureSprite, getUnitSprite, effectSpriteUrls, atlasSheetUrls, backgroundImage, assetManifest, resolveManifestUrl7]);
11229
11229
  const { getImage, pendingCount } = useImageCache(spriteUrls);
11230
11230
  useEffect(() => {
11231
11231
  if (typeof window === "undefined") return;
@@ -11249,14 +11249,14 @@ function IsometricCanvas({
11249
11249
  lerpToTarget
11250
11250
  } = useCamera();
11251
11251
  const resolveTerrainSpriteUrl = useCallback((tile) => {
11252
- return tile.terrainSprite || getTerrainSprite?.(tile.terrain ?? "") || resolveManifestUrl(assetManifest?.terrains?.[tile.terrain ?? ""]);
11253
- }, [getTerrainSprite, assetManifest, resolveManifestUrl]);
11252
+ return tile.terrainSprite || getTerrainSprite?.(tile.terrain ?? "") || resolveManifestUrl7(assetManifest?.terrains?.[tile.terrain ?? ""]);
11253
+ }, [getTerrainSprite, assetManifest, resolveManifestUrl7]);
11254
11254
  const resolveFeatureSpriteUrl = useCallback((featureType) => {
11255
- return getFeatureSprite?.(featureType) || resolveManifestUrl(assetManifest?.features?.[featureType]);
11256
- }, [getFeatureSprite, assetManifest, resolveManifestUrl]);
11255
+ return getFeatureSprite?.(featureType) || resolveManifestUrl7(assetManifest?.features?.[featureType]);
11256
+ }, [getFeatureSprite, assetManifest, resolveManifestUrl7]);
11257
11257
  const resolveUnitSpriteUrl = useCallback((unit) => {
11258
- return unit.sprite || getUnitSprite?.(unit) || (unit.unitType ? resolveManifestUrl(assetManifest?.units?.[unit.unitType]) : void 0);
11259
- }, [getUnitSprite, assetManifest, resolveManifestUrl]);
11258
+ return unit.sprite || getUnitSprite?.(unit) || (unit.unitType ? resolveManifestUrl7(assetManifest?.units?.[unit.unitType]) : void 0);
11259
+ }, [getUnitSprite, assetManifest, resolveManifestUrl7]);
11260
11260
  const drawMinimap = useCallback(() => {
11261
11261
  if (!showMinimap) return;
11262
11262
  const miniCanvas = minimapRef.current;
@@ -11358,7 +11358,15 @@ function IsometricCanvas({
11358
11358
  const spriteUrl = resolveTerrainSpriteUrl(tile);
11359
11359
  const img = spriteUrl ? getImage(spriteUrl) : null;
11360
11360
  if (img) {
11361
- ctx.drawImage(img, pos.x, pos.y, scaledTileWidth, scaledTileHeight);
11361
+ if (img.naturalWidth === 0) {
11362
+ ctx.drawImage(img, pos.x, pos.y, scaledTileWidth, scaledTileHeight);
11363
+ } else {
11364
+ const drawW = scaledTileWidth;
11365
+ const drawH = scaledTileWidth * (img.naturalHeight / img.naturalWidth);
11366
+ const drawX = pos.x;
11367
+ const drawY = pos.y + scaledTileHeight - drawH;
11368
+ ctx.drawImage(img, drawX, drawY, drawW, drawH);
11369
+ }
11362
11370
  } else {
11363
11371
  const centerX = pos.x + scaledTileWidth / 2;
11364
11372
  const topY = pos.y + scaledDiamondTopY;
@@ -13617,7 +13625,7 @@ var init_CodeBlock = __esm({
13617
13625
  };
13618
13626
  LINE_PROPS_FN = (n) => ({ "data-line": String(n - 1) });
13619
13627
  HIDDEN_LINE_NUMBERS = { display: "none" };
13620
- CodeBlock = React82__default.memo(
13628
+ CodeBlock = React85__default.memo(
13621
13629
  ({
13622
13630
  code: rawCode,
13623
13631
  language = "text",
@@ -13786,24 +13794,24 @@ var init_CodeBlock = __esm({
13786
13794
  return;
13787
13795
  }
13788
13796
  lineEls.forEach((el) => {
13789
- const num2 = parseInt(el.getAttribute("data-line") ?? "-1", 10);
13790
- if (hiddenLines.has(num2)) {
13797
+ const num3 = parseInt(el.getAttribute("data-line") ?? "-1", 10);
13798
+ if (hiddenLines.has(num3)) {
13791
13799
  el.style.display = "none";
13792
13800
  return;
13793
13801
  }
13794
13802
  el.style.display = "";
13795
13803
  el.style.position = "relative";
13796
13804
  el.style.paddingLeft = "1.2em";
13797
- const region = foldStartMap.get(num2);
13805
+ const region = foldStartMap.get(num3);
13798
13806
  if (!region) return;
13799
- const isCollapsed = collapsed.has(num2);
13807
+ const isCollapsed = collapsed.has(num3);
13800
13808
  const toggle = document.createElement("span");
13801
13809
  toggle.className = "fold-toggle";
13802
13810
  toggle.textContent = isCollapsed ? "\u25B6" : "\u25BC";
13803
13811
  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%";
13804
13812
  toggle.addEventListener("click", (e) => {
13805
13813
  e.stopPropagation();
13806
- toggleFoldRef.current(num2);
13814
+ toggleFoldRef.current(num3);
13807
13815
  });
13808
13816
  el.insertBefore(toggle, el.firstChild);
13809
13817
  if (isCollapsed) {
@@ -14204,7 +14212,7 @@ var init_MarkdownContent = __esm({
14204
14212
  init_Box();
14205
14213
  init_CodeBlock();
14206
14214
  init_cn();
14207
- MarkdownContent = React82__default.memo(
14215
+ MarkdownContent = React85__default.memo(
14208
14216
  ({ content, direction = "ltr", className }) => {
14209
14217
  const { t: _t } = useTranslate();
14210
14218
  const safeContent = typeof content === "string" ? content : String(content ?? "");
@@ -15521,7 +15529,7 @@ var init_StateMachineView = __esm({
15521
15529
  style: { top: title ? 30 : 0 },
15522
15530
  children: [
15523
15531
  entity && /* @__PURE__ */ jsx(EntityBox, { entity, config }),
15524
- states.map((state) => renderStateNode ? /* @__PURE__ */ jsx(React82__default.Fragment, { children: renderStateNode(state, config) }, state.id) : /* @__PURE__ */ jsx(
15532
+ states.map((state) => renderStateNode ? /* @__PURE__ */ jsx(React85__default.Fragment, { children: renderStateNode(state, config) }, state.id) : /* @__PURE__ */ jsx(
15525
15533
  StateNode,
15526
15534
  {
15527
15535
  state,
@@ -19422,8 +19430,8 @@ function EmojiEffect({
19422
19430
  );
19423
19431
  }
19424
19432
  function CanvasEffect({
19425
- effectSpriteUrl = "https://almadar-kflow-assets.web.app/shared/effects/gas/gas00.png",
19426
- assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/effects/",
19433
+ effectSpriteUrl,
19434
+ assetBaseUrl,
19427
19435
  ...props
19428
19436
  }) {
19429
19437
  const eventBus = useEventBus();
@@ -19466,6 +19474,380 @@ var init_CanvasEffect = __esm({
19466
19474
  CanvasEffect.displayName = "CanvasEffect";
19467
19475
  }
19468
19476
  });
19477
+ function CardHand({
19478
+ cards = DEFAULT_CARDS,
19479
+ selectedId = "",
19480
+ size = "md",
19481
+ onCardClick,
19482
+ cardClickEvent,
19483
+ emptyLabel = "No cards in hand",
19484
+ className
19485
+ }) {
19486
+ const eventBus = useEventBus();
19487
+ const handleClick = React85.useCallback(
19488
+ (card) => {
19489
+ if (card.disabled) return;
19490
+ onCardClick?.(card.id);
19491
+ if (cardClickEvent) {
19492
+ eventBus.emit(`UI:${cardClickEvent}`, { cardId: card.id });
19493
+ }
19494
+ },
19495
+ [onCardClick, cardClickEvent, eventBus]
19496
+ );
19497
+ if (cards.length === 0) {
19498
+ return /* @__PURE__ */ jsx(Box, { className: cn("flex items-center justify-center p-4", className), children: /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: emptyLabel }) });
19499
+ }
19500
+ const art = artSizeMap[size];
19501
+ return /* @__PURE__ */ jsx(Box, { className: cn("flex flex-wrap gap-2 justify-center items-end", className), children: cards.map((card) => {
19502
+ const selected = selectedId === card.id;
19503
+ return /* @__PURE__ */ jsxs(
19504
+ "button",
19505
+ {
19506
+ type: "button",
19507
+ onClick: () => handleClick(card),
19508
+ disabled: card.disabled,
19509
+ title: card.title,
19510
+ className: cn(
19511
+ "card-hand-card relative flex flex-col items-center rounded-interactive border-2",
19512
+ "bg-card/90 px-1.5 pt-1.5 pb-1 transition-all duration-150",
19513
+ cardSizeMap[size],
19514
+ card.disabled ? "border-border opacity-50 cursor-not-allowed" : "border-accent hover:brightness-125 hover:-translate-y-1 cursor-pointer",
19515
+ selected && "ring-2 ring-foreground ring-offset-1 ring-offset-background -translate-y-1"
19516
+ ),
19517
+ children: [
19518
+ card.cost != null && /* @__PURE__ */ jsx(
19519
+ "span",
19520
+ {
19521
+ className: cn(
19522
+ "absolute -top-2 -left-2 flex items-center justify-center",
19523
+ "min-w-[22px] h-[22px] rounded-full px-1",
19524
+ "bg-info text-foreground text-xs font-bold border border-background"
19525
+ ),
19526
+ children: card.cost
19527
+ }
19528
+ ),
19529
+ /* @__PURE__ */ jsx(Box, { className: "flex-1 flex items-center justify-center w-full", children: card.iconUrl ? /* @__PURE__ */ jsx(
19530
+ "img",
19531
+ {
19532
+ src: card.iconUrl,
19533
+ alt: card.title ?? card.id,
19534
+ width: art,
19535
+ height: art,
19536
+ style: { imageRendering: "pixelated", objectFit: "contain" },
19537
+ className: "flex-shrink-0"
19538
+ }
19539
+ ) : /* @__PURE__ */ jsx(
19540
+ Box,
19541
+ {
19542
+ className: "rounded bg-muted/40",
19543
+ style: { width: art, height: art }
19544
+ }
19545
+ ) }),
19546
+ card.title != null && /* @__PURE__ */ jsx(
19547
+ Typography,
19548
+ {
19549
+ variant: "small",
19550
+ className: "w-full truncate text-center text-[10px] leading-tight font-semibold",
19551
+ children: card.title
19552
+ }
19553
+ ),
19554
+ (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: [
19555
+ /* @__PURE__ */ jsx("span", { className: "text-error", children: card.attack != null ? `\u2694${card.attack}` : "" }),
19556
+ /* @__PURE__ */ jsx("span", { className: "text-info", children: card.defense != null ? `\u{1F6E1}${card.defense}` : "" })
19557
+ ] })
19558
+ ]
19559
+ },
19560
+ card.id
19561
+ );
19562
+ }) });
19563
+ }
19564
+ var cardSizeMap, artSizeMap, DEFAULT_CARDS;
19565
+ var init_CardHand = __esm({
19566
+ "components/game/molecules/CardHand.tsx"() {
19567
+ "use client";
19568
+ init_cn();
19569
+ init_useEventBus();
19570
+ init_Box();
19571
+ init_Typography();
19572
+ cardSizeMap = {
19573
+ sm: "w-16 h-24",
19574
+ md: "w-20 h-28",
19575
+ lg: "w-24 h-36"
19576
+ };
19577
+ artSizeMap = {
19578
+ sm: 40,
19579
+ md: 52,
19580
+ lg: 64
19581
+ };
19582
+ DEFAULT_CARDS = [
19583
+ { id: "card-1", title: "Brawler", cost: 1, attack: 2, defense: 1 },
19584
+ { id: "card-2", title: "Guardian", cost: 2, attack: 1, defense: 4 },
19585
+ { id: "card-3", title: "Striker", cost: 3, attack: 5, defense: 2 }
19586
+ ];
19587
+ CardHand.displayName = "CardHand";
19588
+ }
19589
+ });
19590
+ function resolveManifestUrl(manifest, relative) {
19591
+ if (relative == null) return void 0;
19592
+ if (/^https?:\/\//.test(relative)) return relative;
19593
+ const base = manifest?.baseUrl;
19594
+ if (base == null) return relative;
19595
+ const cleanBase = base.replace(/\/$/, "");
19596
+ const cleanRel = relative.replace(/^\//, "");
19597
+ return `${cleanBase}/${cleanRel}`;
19598
+ }
19599
+ function rowToCard(r) {
19600
+ return {
19601
+ id: str(r.id),
19602
+ iconKey: r.iconKey == null ? void 0 : str(r.iconKey),
19603
+ title: r.title == null ? void 0 : str(r.title),
19604
+ cost: r.cost == null ? void 0 : num(r.cost),
19605
+ attack: r.attack == null ? void 0 : num(r.attack),
19606
+ defense: r.defense == null ? void 0 : num(r.defense)
19607
+ };
19608
+ }
19609
+ function toHandCards(cards, manifest, mana) {
19610
+ return cards.map((c) => {
19611
+ const key = c.iconKey ?? c.id;
19612
+ const iconUrl = resolveManifestUrl(manifest, manifest?.cards?.[key]);
19613
+ return {
19614
+ id: c.id,
19615
+ iconUrl,
19616
+ title: c.title,
19617
+ cost: c.cost,
19618
+ attack: c.attack,
19619
+ defense: c.defense,
19620
+ disabled: c.cost != null && c.cost > mana
19621
+ };
19622
+ });
19623
+ }
19624
+ function CardBattlerBoard({
19625
+ entity,
19626
+ deck: propDeck,
19627
+ hand: propHand,
19628
+ board: propBoard,
19629
+ mana: propMana,
19630
+ maxMana: propMaxMana,
19631
+ turn: propTurn,
19632
+ result: propResult,
19633
+ assetManifest: propAssetManifest,
19634
+ playCardEvent,
19635
+ endTurnEvent,
19636
+ playAgainEvent,
19637
+ gameEndEvent,
19638
+ className
19639
+ }) {
19640
+ const board = boardEntity(entity) ?? {};
19641
+ const eventBus = useEventBus();
19642
+ const { t } = useTranslate();
19643
+ const assetManifest = propAssetManifest ?? board.assetManifest;
19644
+ const entityHand = useMemo(() => rows(board.hand).map(rowToCard), [board.hand]);
19645
+ const rawHand = propHand ?? entityHand;
19646
+ const hand = rawHand.length > 0 ? rawHand : DEFAULT_HAND;
19647
+ const entityBoard = useMemo(() => rows(board.board).map(rowToCard), [board.board]);
19648
+ const playedCards = propBoard ?? entityBoard;
19649
+ const entityDeck = useMemo(() => rows(board.deck).map(rowToCard), [board.deck]);
19650
+ const deck = propDeck ?? entityDeck;
19651
+ const mana = propMana ?? num(board.mana, 3);
19652
+ const maxMana = propMaxMana ?? num(board.maxMana, 3);
19653
+ const turn = propTurn ?? num(board.turn, 1);
19654
+ const result = propResult ?? (str(board.result) || "none");
19655
+ const handCards = useMemo(() => toHandCards(hand, assetManifest, mana), [hand, assetManifest, mana]);
19656
+ const boardCards = useMemo(() => toHandCards(playedCards, assetManifest, mana), [playedCards, assetManifest, mana]);
19657
+ const emittedGameEnd = useRef(false);
19658
+ if (result !== "none" && !emittedGameEnd.current) {
19659
+ emittedGameEnd.current = true;
19660
+ if (gameEndEvent) {
19661
+ eventBus.emit(`UI:${gameEndEvent}`, { result });
19662
+ }
19663
+ }
19664
+ if (result === "none") {
19665
+ emittedGameEnd.current = false;
19666
+ }
19667
+ const handlePlayCard = useCallback((cardId) => {
19668
+ if (result !== "none") return;
19669
+ if (playCardEvent) {
19670
+ eventBus.emit(`UI:${playCardEvent}`, { cardId });
19671
+ }
19672
+ }, [result, playCardEvent, eventBus]);
19673
+ const handleEndTurn = useCallback(() => {
19674
+ if (result !== "none") return;
19675
+ if (endTurnEvent) {
19676
+ eventBus.emit(`UI:${endTurnEvent}`, {});
19677
+ }
19678
+ }, [result, endTurnEvent, eventBus]);
19679
+ const handlePlayAgain = useCallback(() => {
19680
+ if (playAgainEvent) {
19681
+ eventBus.emit(`UI:${playAgainEvent}`, {});
19682
+ }
19683
+ }, [playAgainEvent, eventBus]);
19684
+ return /* @__PURE__ */ jsxs(VStack, { className: cn("card-battler-board relative min-h-[600px] bg-background", className), gap: "none", children: [
19685
+ /* @__PURE__ */ jsxs(
19686
+ HStack,
19687
+ {
19688
+ className: "px-4 py-2 border-b border-border bg-surface",
19689
+ gap: "lg",
19690
+ align: "center",
19691
+ children: [
19692
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
19693
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("cb.mana") ?? "Mana" }),
19694
+ /* @__PURE__ */ jsxs(Typography, { variant: "body2", weight: "bold", className: "text-info", children: [
19695
+ mana,
19696
+ " / ",
19697
+ maxMana
19698
+ ] })
19699
+ ] }),
19700
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
19701
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("cb.turn") ?? "Turn" }),
19702
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", children: turn })
19703
+ ] }),
19704
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
19705
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("cb.deck") ?? "Deck" }),
19706
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", children: deck.length })
19707
+ ] }),
19708
+ /* @__PURE__ */ jsx(Box, { className: "ml-auto", children: result === "none" && /* @__PURE__ */ jsx(Button, { variant: "primary", size: "sm", onClick: handleEndTurn, children: t("cb.endTurn") ?? "End Turn" }) })
19709
+ ]
19710
+ }
19711
+ ),
19712
+ /* @__PURE__ */ jsxs(Box, { className: "flex-1 relative p-4 overflow-auto", children: [
19713
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", className: "mb-2 uppercase tracking-wide", children: t("cb.battlefield") ?? "Battlefield" }),
19714
+ boardCards.length > 0 ? /* @__PURE__ */ jsx(
19715
+ CardHand,
19716
+ {
19717
+ cards: boardCards,
19718
+ size: "md",
19719
+ className: "justify-start"
19720
+ }
19721
+ ) : /* @__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" }) })
19722
+ ] }),
19723
+ /* @__PURE__ */ jsxs(Box, { className: "border-t border-border bg-surface px-4 py-3", children: [
19724
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", className: "mb-2 uppercase tracking-wide", children: t("cb.hand") ?? "Your Hand" }),
19725
+ /* @__PURE__ */ jsx(
19726
+ CardHand,
19727
+ {
19728
+ cards: handCards,
19729
+ size: "lg",
19730
+ onCardClick: handlePlayCard,
19731
+ emptyLabel: t("cb.emptyHand") ?? "No cards in hand"
19732
+ }
19733
+ )
19734
+ ] }),
19735
+ 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: [
19736
+ /* @__PURE__ */ jsx(
19737
+ Typography,
19738
+ {
19739
+ variant: "h2",
19740
+ className: cn(
19741
+ "text-4xl font-black tracking-widest uppercase",
19742
+ result === "won" ? "text-warning" : "text-error"
19743
+ ),
19744
+ children: result === "won" ? t("cb.victory") ?? "Victory!" : t("cb.defeat") ?? "Defeat!"
19745
+ }
19746
+ ),
19747
+ /* @__PURE__ */ jsx(
19748
+ Button,
19749
+ {
19750
+ variant: "primary",
19751
+ className: "px-8 py-3 font-semibold",
19752
+ onClick: handlePlayAgain,
19753
+ children: t("cb.playAgain") ?? "Play Again"
19754
+ }
19755
+ )
19756
+ ] }) })
19757
+ ] });
19758
+ }
19759
+ var DEFAULT_HAND;
19760
+ var init_CardBattlerBoard = __esm({
19761
+ "components/game/organisms/CardBattlerBoard.tsx"() {
19762
+ "use client";
19763
+ init_cn();
19764
+ init_useEventBus();
19765
+ init_Box();
19766
+ init_Button();
19767
+ init_Typography();
19768
+ init_Stack();
19769
+ init_CardHand();
19770
+ init_boardEntity();
19771
+ DEFAULT_HAND = [
19772
+ { id: "c1", iconKey: "bear", title: "Bear", cost: 3, attack: 5, defense: 4 },
19773
+ { id: "c2", iconKey: "owl", title: "Owl", cost: 1, attack: 2, defense: 1 },
19774
+ { id: "c3", iconKey: "gorilla", title: "Gorilla", cost: 4, attack: 6, defense: 5 },
19775
+ { id: "c4", iconKey: "frog", title: "Frog", cost: 1, attack: 1, defense: 2 },
19776
+ { id: "c5", iconKey: "snake", title: "Snake", cost: 2, attack: 4, defense: 1 }
19777
+ ];
19778
+ CardBattlerBoard.displayName = "CardBattlerBoard";
19779
+ }
19780
+ });
19781
+ function isEntityRow(value) {
19782
+ return value != null && typeof value === "object" && !Array.isArray(value);
19783
+ }
19784
+ function CardBattlerTemplate({
19785
+ entity,
19786
+ title = "Card Battler",
19787
+ deck,
19788
+ hand,
19789
+ board,
19790
+ mana,
19791
+ maxMana,
19792
+ turn,
19793
+ result,
19794
+ assetManifest,
19795
+ playCardEvent,
19796
+ endTurnEvent,
19797
+ playAgainEvent,
19798
+ gameEndEvent,
19799
+ className
19800
+ }) {
19801
+ const resolved = isEntityRow(entity) ? entity : void 0;
19802
+ return /* @__PURE__ */ jsxs(
19803
+ Box,
19804
+ {
19805
+ display: "flex",
19806
+ fullHeight: true,
19807
+ className: cn("card-battler-template flex-col", className),
19808
+ children: [
19809
+ /* @__PURE__ */ jsx(
19810
+ HStack,
19811
+ {
19812
+ gap: "sm",
19813
+ align: "center",
19814
+ className: "px-4 py-3 border-b-2 border-border bg-surface shrink-0",
19815
+ children: /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title })
19816
+ }
19817
+ ),
19818
+ /* @__PURE__ */ jsx(Box, { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsx(
19819
+ CardBattlerBoard,
19820
+ {
19821
+ entity: resolved,
19822
+ deck,
19823
+ hand,
19824
+ board,
19825
+ mana,
19826
+ maxMana,
19827
+ turn,
19828
+ result,
19829
+ assetManifest,
19830
+ playCardEvent,
19831
+ endTurnEvent,
19832
+ playAgainEvent,
19833
+ gameEndEvent,
19834
+ className: "h-full"
19835
+ }
19836
+ ) })
19837
+ ]
19838
+ }
19839
+ );
19840
+ }
19841
+ var init_CardBattlerTemplate = __esm({
19842
+ "components/game/templates/CardBattlerTemplate.tsx"() {
19843
+ init_cn();
19844
+ init_Box();
19845
+ init_Stack();
19846
+ init_Typography();
19847
+ init_CardBattlerBoard();
19848
+ CardBattlerTemplate.displayName = "CardBattlerTemplate";
19849
+ }
19850
+ });
19469
19851
 
19470
19852
  // lib/getNestedValue.ts
19471
19853
  function getNestedValue(obj, path) {
@@ -21248,6 +21630,315 @@ var init_ChartLegend = __esm({
21248
21630
  ChartLegend.displayName = "ChartLegend";
21249
21631
  }
21250
21632
  });
21633
+ function resolveManifestUrl2(manifest, relative) {
21634
+ if (relative == null) return void 0;
21635
+ if (/^https?:\/\//.test(relative)) return relative;
21636
+ const base = manifest?.baseUrl;
21637
+ if (base == null) return relative;
21638
+ const cleanBase = base.replace(/\/$/, "");
21639
+ const cleanRel = relative.replace(/^\//, "");
21640
+ return `${cleanBase}/${cleanRel}`;
21641
+ }
21642
+ function buildDefaultCBTiles() {
21643
+ const tiles = [];
21644
+ for (let y = 0; y < CB_GRID_H; y++) {
21645
+ for (let x = 0; x < CB_GRID_W; x++) {
21646
+ const variant = (x * 3 + y * 5 + (x ^ y)) % 3;
21647
+ const terrainKey = ["grass", "stone", "dirt"][variant];
21648
+ tiles.push({ x, y, terrain: terrainKey, passable: true });
21649
+ }
21650
+ }
21651
+ return tiles;
21652
+ }
21653
+ function tilesToIso(tiles, manifest) {
21654
+ return tiles.map((t) => {
21655
+ const key = t.terrain ?? "grass";
21656
+ const sprite = t.terrainSprite ?? resolveManifestUrl2(manifest, manifest?.terrains?.[key]) ?? resolveManifestUrl2(manifest, manifest?.terrains?.grass);
21657
+ return {
21658
+ x: t.x,
21659
+ y: t.y,
21660
+ terrain: t.terrain,
21661
+ terrainSprite: sprite,
21662
+ passable: t.passable
21663
+ };
21664
+ });
21665
+ }
21666
+ function buildingsToFeatures(buildings, manifest) {
21667
+ return buildings.map((b) => ({
21668
+ id: b.id,
21669
+ x: b.x,
21670
+ y: b.y,
21671
+ type: b.type,
21672
+ sprite: resolveManifestUrl2(manifest, manifest?.features?.[b.type])
21673
+ }));
21674
+ }
21675
+ function CityBuilderBoard({
21676
+ entity,
21677
+ tiles: propTiles,
21678
+ buildings: propBuildings,
21679
+ assetManifest: propAssetManifest,
21680
+ buildTypes: propBuildTypes,
21681
+ pendingBuildType: propPendingBuildType,
21682
+ gold: propGold,
21683
+ wood: propWood,
21684
+ population: propPopulation,
21685
+ result: propResult,
21686
+ scale = 0.3,
21687
+ unitScale = 0.6,
21688
+ spriteHeightRatio = 1.5,
21689
+ spriteMaxWidthRatio = 0.7,
21690
+ selectBuildTypeEvent,
21691
+ placeBuildingEvent,
21692
+ gameEndEvent,
21693
+ className
21694
+ }) {
21695
+ const board = boardEntity(entity) ?? {};
21696
+ const eventBus = useEventBus();
21697
+ const { t } = useTranslate();
21698
+ const assetManifest = propAssetManifest ?? board.assetManifest;
21699
+ const entityTiles = useMemo(
21700
+ () => rows(board.tiles).map((r) => ({
21701
+ x: num(r.x),
21702
+ y: num(r.y),
21703
+ terrain: r.terrain == null ? void 0 : str(r.terrain),
21704
+ terrainSprite: r.terrainSprite == null ? void 0 : str(r.terrainSprite),
21705
+ passable: r.passable !== false
21706
+ })),
21707
+ [board.tiles]
21708
+ );
21709
+ const rawTiles = propTiles ?? entityTiles;
21710
+ const tiles = rawTiles.length >= CB_GRID_W * CB_GRID_H ? rawTiles : DEFAULT_CB_TILES;
21711
+ const entityBuildings = useMemo(
21712
+ () => rows(board.buildings).map((r) => ({
21713
+ id: str(r.id),
21714
+ x: num(r.x),
21715
+ y: num(r.y),
21716
+ type: str(r.type)
21717
+ })),
21718
+ [board.buildings]
21719
+ );
21720
+ const buildings = propBuildings ?? entityBuildings;
21721
+ const buildTypes = propBuildTypes ?? DEFAULT_BUILD_TYPES;
21722
+ const gold = propGold ?? num(board.gold, 100);
21723
+ const wood = propWood ?? num(board.wood, 80);
21724
+ const population = propPopulation ?? num(board.population, 0);
21725
+ const pendingBuildType = propPendingBuildType ?? (str(board.pendingBuildType) || buildTypes[0]?.type || "");
21726
+ const result = propResult ?? (str(board.result) || "none");
21727
+ const [hoveredTile, setHoveredTile] = useState(null);
21728
+ const emittedGameEnd = useRef(false);
21729
+ if (result !== "none" && !emittedGameEnd.current) {
21730
+ emittedGameEnd.current = true;
21731
+ if (gameEndEvent) {
21732
+ eventBus.emit(`UI:${gameEndEvent}`, { result });
21733
+ }
21734
+ }
21735
+ if (result === "none") {
21736
+ emittedGameEnd.current = false;
21737
+ }
21738
+ const pendingCost = useMemo(
21739
+ () => buildTypes.find((b) => b.type === pendingBuildType)?.cost ?? 0,
21740
+ [buildTypes, pendingBuildType]
21741
+ );
21742
+ const buildingPositions = useMemo(
21743
+ () => new Set(buildings.map((b) => `${b.x},${b.y}`)),
21744
+ [buildings]
21745
+ );
21746
+ const validMoves = useMemo(() => {
21747
+ if (result !== "none" || gold < pendingCost) return [];
21748
+ return tiles.filter(
21749
+ (t2) => t2.passable !== false && !buildingPositions.has(`${t2.x},${t2.y}`)
21750
+ ).map((t2) => ({ x: t2.x, y: t2.y }));
21751
+ }, [tiles, buildingPositions, result, gold, pendingCost]);
21752
+ const isoTiles = useMemo(() => tilesToIso(tiles, assetManifest), [tiles, assetManifest]);
21753
+ const buildingFeatures = useMemo(() => buildingsToFeatures(buildings, assetManifest), [buildings, assetManifest]);
21754
+ const handleTileClick = useCallback((x, y) => {
21755
+ if (result !== "none") return;
21756
+ if (buildingPositions.has(`${x},${y}`)) return;
21757
+ if (gold < pendingCost) return;
21758
+ if (placeBuildingEvent) {
21759
+ eventBus.emit(`UI:${placeBuildingEvent}`, { x, y });
21760
+ }
21761
+ }, [result, buildingPositions, gold, pendingCost, placeBuildingEvent, eventBus]);
21762
+ const handleSelectBuildType = useCallback((buildType) => {
21763
+ if (selectBuildTypeEvent) {
21764
+ eventBus.emit(`UI:${selectBuildTypeEvent}`, { buildType });
21765
+ }
21766
+ }, [selectBuildTypeEvent, eventBus]);
21767
+ const canBuild = gold >= pendingCost && result === "none";
21768
+ return /* @__PURE__ */ jsxs(VStack, { className: cn("city-builder-board relative min-h-[600px] bg-background", className), gap: "none", children: [
21769
+ /* @__PURE__ */ jsxs(
21770
+ HStack,
21771
+ {
21772
+ className: "px-4 py-2 border-b border-border bg-surface",
21773
+ gap: "lg",
21774
+ align: "center",
21775
+ children: [
21776
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
21777
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("cb.gold") ?? "Gold" }),
21778
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", className: "text-warning", children: gold })
21779
+ ] }),
21780
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
21781
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("cb.wood") ?? "Wood" }),
21782
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", className: "text-success", children: wood })
21783
+ ] }),
21784
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
21785
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("cb.population") ?? "Population" }),
21786
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", children: population })
21787
+ ] }),
21788
+ /* @__PURE__ */ jsx(HStack, { gap: "xs", align: "center", className: "ml-auto", children: buildTypes.map((b) => /* @__PURE__ */ jsxs(
21789
+ Button,
21790
+ {
21791
+ variant: b.type === pendingBuildType ? "primary" : "secondary",
21792
+ size: "sm",
21793
+ disabled: gold < b.cost || result !== "none",
21794
+ onClick: () => handleSelectBuildType(b.type),
21795
+ children: [
21796
+ b.label,
21797
+ " (",
21798
+ b.cost,
21799
+ ")"
21800
+ ]
21801
+ },
21802
+ b.type
21803
+ )) })
21804
+ ]
21805
+ }
21806
+ ),
21807
+ /* @__PURE__ */ jsxs(Box, { className: "relative flex-1", children: [
21808
+ /* @__PURE__ */ jsx(
21809
+ IsometricCanvas_default,
21810
+ {
21811
+ tiles: isoTiles,
21812
+ features: buildingFeatures,
21813
+ assetBaseUrl: assetManifest?.baseUrl,
21814
+ assetManifest,
21815
+ validMoves,
21816
+ hoveredTile,
21817
+ onTileClick: handleTileClick,
21818
+ onTileHover: (x, y) => setHoveredTile({ x, y }),
21819
+ onTileLeave: () => setHoveredTile(null),
21820
+ scale,
21821
+ unitScale,
21822
+ spriteHeightRatio,
21823
+ spriteMaxWidthRatio
21824
+ }
21825
+ ),
21826
+ 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)` }) })
21827
+ ] }),
21828
+ 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: [
21829
+ /* @__PURE__ */ jsx(
21830
+ Typography,
21831
+ {
21832
+ variant: "h2",
21833
+ className: cn(
21834
+ "text-4xl font-black tracking-widest uppercase",
21835
+ result === "won" ? "text-warning" : "text-error"
21836
+ ),
21837
+ children: result === "won" ? t("cb.victory") ?? "City Thrives!" : t("cb.defeat") ?? "City Collapsed!"
21838
+ }
21839
+ ),
21840
+ /* @__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." })
21841
+ ] }) })
21842
+ ] });
21843
+ }
21844
+ var CB_GRID_W, CB_GRID_H, DEFAULT_BUILD_TYPES, DEFAULT_CB_TILES;
21845
+ var init_CityBuilderBoard = __esm({
21846
+ "components/game/organisms/CityBuilderBoard.tsx"() {
21847
+ "use client";
21848
+ init_cn();
21849
+ init_useEventBus();
21850
+ init_Box();
21851
+ init_Button();
21852
+ init_Typography();
21853
+ init_Stack();
21854
+ init_IsometricCanvas();
21855
+ init_boardEntity();
21856
+ CB_GRID_W = 12;
21857
+ CB_GRID_H = 12;
21858
+ DEFAULT_BUILD_TYPES = [
21859
+ { type: "house", label: "House", cost: 20 },
21860
+ { type: "farm", label: "Farm", cost: 15 },
21861
+ { type: "mine", label: "Mine", cost: 25 },
21862
+ { type: "barracks", label: "Barracks", cost: 40 }
21863
+ ];
21864
+ DEFAULT_CB_TILES = buildDefaultCBTiles();
21865
+ CityBuilderBoard.displayName = "CityBuilderBoard";
21866
+ }
21867
+ });
21868
+ function isEntityRow2(value) {
21869
+ return value != null && typeof value === "object" && !Array.isArray(value);
21870
+ }
21871
+ function CityBuilderTemplate({
21872
+ entity,
21873
+ title = "City Builder",
21874
+ tiles,
21875
+ buildings,
21876
+ buildTypes,
21877
+ pendingBuildType,
21878
+ assetManifest,
21879
+ gold,
21880
+ wood,
21881
+ population,
21882
+ result,
21883
+ scale,
21884
+ unitScale,
21885
+ selectBuildTypeEvent,
21886
+ placeBuildingEvent,
21887
+ gameEndEvent,
21888
+ className
21889
+ }) {
21890
+ const resolved = isEntityRow2(entity) ? entity : void 0;
21891
+ return /* @__PURE__ */ jsxs(
21892
+ Box,
21893
+ {
21894
+ display: "flex",
21895
+ fullHeight: true,
21896
+ className: cn("city-builder-template flex-col", className),
21897
+ children: [
21898
+ /* @__PURE__ */ jsx(
21899
+ HStack,
21900
+ {
21901
+ gap: "sm",
21902
+ align: "center",
21903
+ className: "px-4 py-3 border-b-2 border-border bg-surface shrink-0",
21904
+ children: /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title })
21905
+ }
21906
+ ),
21907
+ /* @__PURE__ */ jsx(Box, { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsx(
21908
+ CityBuilderBoard,
21909
+ {
21910
+ entity: resolved,
21911
+ tiles,
21912
+ buildings,
21913
+ buildTypes,
21914
+ pendingBuildType,
21915
+ assetManifest,
21916
+ gold,
21917
+ wood,
21918
+ population,
21919
+ result,
21920
+ scale,
21921
+ unitScale,
21922
+ selectBuildTypeEvent,
21923
+ placeBuildingEvent,
21924
+ gameEndEvent,
21925
+ className: "h-full"
21926
+ }
21927
+ ) })
21928
+ ]
21929
+ }
21930
+ );
21931
+ }
21932
+ var init_CityBuilderTemplate = __esm({
21933
+ "components/game/templates/CityBuilderTemplate.tsx"() {
21934
+ init_cn();
21935
+ init_Box();
21936
+ init_Stack();
21937
+ init_Typography();
21938
+ init_CityBuilderBoard();
21939
+ CityBuilderTemplate.displayName = "CityBuilderTemplate";
21940
+ }
21941
+ });
21251
21942
  function ClassifierBoard({
21252
21943
  entity,
21253
21944
  completeEvent = "PUZZLE_COMPLETE",
@@ -22062,7 +22753,7 @@ function CraftingRecipe({
22062
22753
  className
22063
22754
  }) {
22064
22755
  const eventBus = useEventBus();
22065
- const handleCraft = React82.useCallback(() => {
22756
+ const handleCraft = React85.useCallback(() => {
22066
22757
  onCraft?.();
22067
22758
  if (craftEvent) {
22068
22759
  eventBus.emit(craftEvent, { output: output.label });
@@ -22079,7 +22770,7 @@ function CraftingRecipe({
22079
22770
  children: [
22080
22771
  /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-wrap items-center", children: inputs.map((ingredient, index) => {
22081
22772
  const hasSufficient = ingredient.available >= ingredient.required;
22082
- return /* @__PURE__ */ jsxs(React82.Fragment, { children: [
22773
+ return /* @__PURE__ */ jsxs(React85.Fragment, { children: [
22083
22774
  /* @__PURE__ */ jsx(Box, { className: "relative", children: /* @__PURE__ */ jsx(
22084
22775
  ItemSlot,
22085
22776
  {
@@ -22151,8 +22842,8 @@ function DPad({
22151
22842
  }) {
22152
22843
  const eventBus = useEventBus();
22153
22844
  const sizes = sizeMap15[size];
22154
- const [activeDirections, setActiveDirections] = React82.useState(/* @__PURE__ */ new Set());
22155
- const handlePress = React82.useCallback(
22845
+ const [activeDirections, setActiveDirections] = React85.useState(/* @__PURE__ */ new Set());
22846
+ const handlePress = React85.useCallback(
22156
22847
  (direction) => {
22157
22848
  setActiveDirections((prev) => new Set(prev).add(direction));
22158
22849
  if (directionEvent) eventBus.emit(`UI:${directionEvent}`, { direction, pressed: true });
@@ -22160,7 +22851,7 @@ function DPad({
22160
22851
  },
22161
22852
  [directionEvent, eventBus, onDirection]
22162
22853
  );
22163
- const handleRelease = React82.useCallback(
22854
+ const handleRelease = React85.useCallback(
22164
22855
  (direction) => {
22165
22856
  setActiveDirections((prev) => {
22166
22857
  const next = new Set(prev);
@@ -22967,8 +23658,8 @@ var init_Menu = __esm({
22967
23658
  "bottom-end": "bottom-start"
22968
23659
  };
22969
23660
  const effectivePosition = direction === "rtl" ? rtlMirror[position] ?? position : position;
22970
- const triggerChild = React82__default.isValidElement(trigger) ? trigger : /* @__PURE__ */ jsx(Typography, { variant: "small", as: "span", children: trigger });
22971
- const triggerElement = React82__default.cloneElement(
23661
+ const triggerChild = React85__default.isValidElement(trigger) ? trigger : /* @__PURE__ */ jsx(Typography, { variant: "small", as: "span", children: trigger });
23662
+ const triggerElement = React85__default.cloneElement(
22972
23663
  triggerChild,
22973
23664
  {
22974
23665
  ref: triggerRef,
@@ -23102,14 +23793,14 @@ function useDataDnd(args) {
23102
23793
  const isZone = Boolean(dragGroup || accepts || sortable);
23103
23794
  const enabled = isZone || Boolean(dndRoot);
23104
23795
  const eventBus = useEventBus();
23105
- const parentRoot = React82__default.useContext(RootCtx);
23796
+ const parentRoot = React85__default.useContext(RootCtx);
23106
23797
  const isRoot = enabled && parentRoot === null;
23107
- const zoneId = React82__default.useId();
23798
+ const zoneId = React85__default.useId();
23108
23799
  const ownGroup = dragGroup ?? accepts ?? zoneId;
23109
- const [optimisticOrders, setOptimisticOrders] = React82__default.useState(() => /* @__PURE__ */ new Map());
23110
- const optimisticOrdersRef = React82__default.useRef(optimisticOrders);
23800
+ const [optimisticOrders, setOptimisticOrders] = React85__default.useState(() => /* @__PURE__ */ new Map());
23801
+ const optimisticOrdersRef = React85__default.useRef(optimisticOrders);
23111
23802
  optimisticOrdersRef.current = optimisticOrders;
23112
- const clearOptimisticOrder = React82__default.useCallback((group) => {
23803
+ const clearOptimisticOrder = React85__default.useCallback((group) => {
23113
23804
  setOptimisticOrders((prev) => {
23114
23805
  if (!prev.has(group)) return prev;
23115
23806
  const next = new Map(prev);
@@ -23134,7 +23825,7 @@ function useDataDnd(args) {
23134
23825
  const raw = it[dndItemIdField];
23135
23826
  return String(raw ?? `__idx_${idx}`);
23136
23827
  }).join("|");
23137
- const itemIds = React82__default.useMemo(
23828
+ const itemIds = React85__default.useMemo(
23138
23829
  () => orderedItems.map((it, idx) => {
23139
23830
  const raw = it[dndItemIdField];
23140
23831
  return raw ?? `__idx_${idx}`;
@@ -23142,7 +23833,7 @@ function useDataDnd(args) {
23142
23833
  [itemIdsSignature]
23143
23834
  );
23144
23835
  const itemsContentSig = items.map((it, idx) => String(it[dndItemIdField] ?? `__${idx}`)).join("|");
23145
- React82__default.useEffect(() => {
23836
+ React85__default.useEffect(() => {
23146
23837
  const root = isRoot ? null : parentRoot;
23147
23838
  if (root) {
23148
23839
  root.clearOptimisticOrder(ownGroup);
@@ -23150,20 +23841,20 @@ function useDataDnd(args) {
23150
23841
  clearOptimisticOrder(ownGroup);
23151
23842
  }
23152
23843
  }, [itemsContentSig, ownGroup]);
23153
- const zonesRef = React82__default.useRef(/* @__PURE__ */ new Map());
23154
- const registerZone = React82__default.useCallback((zoneId2, meta2) => {
23844
+ const zonesRef = React85__default.useRef(/* @__PURE__ */ new Map());
23845
+ const registerZone = React85__default.useCallback((zoneId2, meta2) => {
23155
23846
  zonesRef.current.set(zoneId2, meta2);
23156
23847
  }, []);
23157
- const unregisterZone = React82__default.useCallback((zoneId2) => {
23848
+ const unregisterZone = React85__default.useCallback((zoneId2) => {
23158
23849
  zonesRef.current.delete(zoneId2);
23159
23850
  }, []);
23160
- const [activeDrag, setActiveDrag] = React82__default.useState(null);
23161
- const [overZoneGroup, setOverZoneGroup] = React82__default.useState(null);
23162
- const meta = React82__default.useMemo(
23851
+ const [activeDrag, setActiveDrag] = React85__default.useState(null);
23852
+ const [overZoneGroup, setOverZoneGroup] = React85__default.useState(null);
23853
+ const meta = React85__default.useMemo(
23163
23854
  () => ({ group: ownGroup, dropEvent, reorderEvent, positionEvent, itemIds, rawItems: items, idField: dndItemIdField }),
23164
23855
  [ownGroup, dropEvent, reorderEvent, positionEvent, itemIds, items, dndItemIdField]
23165
23856
  );
23166
- React82__default.useEffect(() => {
23857
+ React85__default.useEffect(() => {
23167
23858
  const target = isRoot ? null : parentRoot;
23168
23859
  if (!target) {
23169
23860
  zonesRef.current.set(zoneId, meta);
@@ -23182,7 +23873,7 @@ function useDataDnd(args) {
23182
23873
  }, [parentRoot, isRoot, zoneId, meta]);
23183
23874
  const sensors = useAlmadarDndSensors(true);
23184
23875
  const collisionDetection = almadarDndCollisionDetection;
23185
- const findZoneByItem = React82__default.useCallback(
23876
+ const findZoneByItem = React85__default.useCallback(
23186
23877
  (id) => {
23187
23878
  for (const z of zonesRef.current.values()) {
23188
23879
  if (z.itemIds.includes(id)) return z;
@@ -23191,7 +23882,7 @@ function useDataDnd(args) {
23191
23882
  },
23192
23883
  []
23193
23884
  );
23194
- React82__default.useCallback(
23885
+ React85__default.useCallback(
23195
23886
  (group) => {
23196
23887
  for (const z of zonesRef.current.values()) {
23197
23888
  if (z.group === group) return z;
@@ -23200,7 +23891,7 @@ function useDataDnd(args) {
23200
23891
  },
23201
23892
  []
23202
23893
  );
23203
- const handleDragEnd = React82__default.useCallback(
23894
+ const handleDragEnd = React85__default.useCallback(
23204
23895
  (event) => {
23205
23896
  const { active, over } = event;
23206
23897
  const activeIdStr = String(active.id);
@@ -23291,8 +23982,8 @@ function useDataDnd(args) {
23291
23982
  },
23292
23983
  [eventBus]
23293
23984
  );
23294
- const sortableData = React82__default.useMemo(() => ({ dndGroup: ownGroup }), [ownGroup]);
23295
- const SortableItem = React82__default.useCallback(
23985
+ const sortableData = React85__default.useMemo(() => ({ dndGroup: ownGroup }), [ownGroup]);
23986
+ const SortableItem = React85__default.useCallback(
23296
23987
  ({ id, children }) => {
23297
23988
  const {
23298
23989
  attributes,
@@ -23332,7 +24023,7 @@ function useDataDnd(args) {
23332
24023
  id: droppableId,
23333
24024
  data: sortableData
23334
24025
  });
23335
- const ctx = React82__default.useContext(RootCtx);
24026
+ const ctx = React85__default.useContext(RootCtx);
23336
24027
  const activeDrag2 = ctx?.activeDrag ?? null;
23337
24028
  const overZoneGroup2 = ctx?.overZoneGroup ?? null;
23338
24029
  const isThisZoneOver = overZoneGroup2 === ownGroup;
@@ -23347,7 +24038,7 @@ function useDataDnd(args) {
23347
24038
  showForeignPlaceholder,
23348
24039
  ctxAvailable: ctx != null
23349
24040
  });
23350
- React82__default.useEffect(() => {
24041
+ React85__default.useEffect(() => {
23351
24042
  dndLog.info("dropzone:isOver:change", { droppableId, group: ownGroup, isOver, isThisZoneOver, showForeignPlaceholder, activeDragSourceGroup: activeDrag2?.sourceGroup ?? null });
23352
24043
  }, [droppableId, isOver, isThisZoneOver, showForeignPlaceholder]);
23353
24044
  return /* @__PURE__ */ jsx(
@@ -23361,11 +24052,11 @@ function useDataDnd(args) {
23361
24052
  }
23362
24053
  );
23363
24054
  };
23364
- const rootContextValue = React82__default.useMemo(
24055
+ const rootContextValue = React85__default.useMemo(
23365
24056
  () => ({ registerZone, unregisterZone, activeDrag, overZoneGroup, optimisticOrders, clearOptimisticOrder }),
23366
24057
  [registerZone, unregisterZone, activeDrag, overZoneGroup, optimisticOrders, clearOptimisticOrder]
23367
24058
  );
23368
- const handleDragStart = React82__default.useCallback((event) => {
24059
+ const handleDragStart = React85__default.useCallback((event) => {
23369
24060
  const sourceZone = findZoneByItem(event.active.id);
23370
24061
  const rect = event.active.rect.current.initial;
23371
24062
  const height = rect?.height && rect.height > 0 ? rect.height : 64;
@@ -23384,7 +24075,7 @@ function useDataDnd(args) {
23384
24075
  isRoot
23385
24076
  });
23386
24077
  }, [findZoneByItem, isRoot, zoneId]);
23387
- const handleDragOver = React82__default.useCallback((event) => {
24078
+ const handleDragOver = React85__default.useCallback((event) => {
23388
24079
  const { active, over } = event;
23389
24080
  const overData = over?.data?.current;
23390
24081
  const overGroup = overData?.dndGroup ?? null;
@@ -23454,7 +24145,7 @@ function useDataDnd(args) {
23454
24145
  return next;
23455
24146
  });
23456
24147
  }, []);
23457
- const handleDragCancel = React82__default.useCallback((event) => {
24148
+ const handleDragCancel = React85__default.useCallback((event) => {
23458
24149
  setActiveDrag(null);
23459
24150
  setOverZoneGroup(null);
23460
24151
  dndLog.warn("dragCancel", {
@@ -23462,12 +24153,12 @@ function useDataDnd(args) {
23462
24153
  reason: "dnd-kit cancelled the drag (escape key, pointer interrupted, or external)"
23463
24154
  });
23464
24155
  }, []);
23465
- const handleDragEndWithCleanup = React82__default.useCallback((event) => {
24156
+ const handleDragEndWithCleanup = React85__default.useCallback((event) => {
23466
24157
  handleDragEnd(event);
23467
24158
  setActiveDrag(null);
23468
24159
  setOverZoneGroup(null);
23469
24160
  }, [handleDragEnd]);
23470
- const wrapContainer = React82__default.useCallback(
24161
+ const wrapContainer = React85__default.useCallback(
23471
24162
  (children) => {
23472
24163
  if (!enabled) return children;
23473
24164
  const strategy = layout === "grid" ? rectSortingStrategy : verticalListSortingStrategy;
@@ -23521,7 +24212,7 @@ var init_useDataDnd = __esm({
23521
24212
  init_useAlmadarDndCollision();
23522
24213
  init_Box();
23523
24214
  dndLog = createLogger("almadar:ui:dnd");
23524
- RootCtx = React82__default.createContext(null);
24215
+ RootCtx = React85__default.createContext(null);
23525
24216
  }
23526
24217
  });
23527
24218
  function renderIconInput(icon, props) {
@@ -24047,7 +24738,7 @@ function DataList({
24047
24738
  }) {
24048
24739
  const eventBus = useEventBus();
24049
24740
  const { t } = useTranslate();
24050
- const [visibleCount, setVisibleCount] = React82__default.useState(pageSize || Infinity);
24741
+ const [visibleCount, setVisibleCount] = React85__default.useState(pageSize || Infinity);
24051
24742
  const fieldDefs = fields ?? columns ?? [];
24052
24743
  const allDataRaw = Array.isArray(entity) ? entity : entity ? [entity] : [];
24053
24744
  const dnd = useDataDnd({
@@ -24066,7 +24757,7 @@ function DataList({
24066
24757
  const data = pageSize > 0 ? allData.slice(0, visibleCount) : allData;
24067
24758
  const hasMoreLocal = pageSize > 0 && visibleCount < allData.length;
24068
24759
  const hasRenderProp = typeof children === "function";
24069
- React82__default.useEffect(() => {
24760
+ React85__default.useEffect(() => {
24070
24761
  const renderItemTypeOf = typeof schemaRenderItem;
24071
24762
  const childrenTypeOf = typeof children;
24072
24763
  if (data.length > 0 && !hasRenderProp) {
@@ -24171,7 +24862,7 @@ function DataList({
24171
24862
  const items2 = data.map((item) => item);
24172
24863
  const groups2 = groupBy ? groupData(items2, groupBy) : [{ label: "", items: items2 }];
24173
24864
  const contentField = titleField?.name ?? fieldDefs[0]?.name ?? "";
24174
- return /* @__PURE__ */ jsx(VStack, { gap: "sm", className: cn("py-2", className), children: groups2.map((group, gi) => /* @__PURE__ */ jsxs(React82__default.Fragment, { children: [
24865
+ return /* @__PURE__ */ jsx(VStack, { gap: "sm", className: cn("py-2", className), children: groups2.map((group, gi) => /* @__PURE__ */ jsxs(React85__default.Fragment, { children: [
24175
24866
  group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: "my-2" }),
24176
24867
  group.items.map((itemData, index) => {
24177
24868
  const id = itemData.id || `${gi}-${index}`;
@@ -24312,7 +25003,7 @@ function DataList({
24312
25003
  className
24313
25004
  ),
24314
25005
  children: [
24315
- groups.map((group, gi) => /* @__PURE__ */ jsxs(React82__default.Fragment, { children: [
25006
+ groups.map((group, gi) => /* @__PURE__ */ jsxs(React85__default.Fragment, { children: [
24316
25007
  group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: gi > 0 ? "mt-4" : "mt-0" }),
24317
25008
  group.items.map(
24318
25009
  (itemData, index) => renderItem(itemData, index, gi === groups.length - 1 && index === group.items.length - 1)
@@ -25228,7 +25919,7 @@ var init_Grid = __esm({
25228
25919
  as: Component2 = "div"
25229
25920
  }) => {
25230
25921
  const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
25231
- return React82__default.createElement(
25922
+ return React85__default.createElement(
25232
25923
  Component2,
25233
25924
  {
25234
25925
  className: cn(
@@ -25416,8 +26107,8 @@ var init_Popover = __esm({
25416
26107
  onMouseEnter: handleOpen,
25417
26108
  onMouseLeave: handleClose
25418
26109
  };
25419
- const childElement = React82__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
25420
- const triggerElement = React82__default.cloneElement(
26110
+ const childElement = React85__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
26111
+ const triggerElement = React85__default.cloneElement(
25421
26112
  childElement,
25422
26113
  {
25423
26114
  ref: triggerRef,
@@ -26007,8 +26698,8 @@ var init_Tooltip = __esm({
26007
26698
  if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);
26008
26699
  };
26009
26700
  }, []);
26010
- const triggerElement = React82__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
26011
- const trigger = React82__default.cloneElement(triggerElement, {
26701
+ const triggerElement = React85__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
26702
+ const trigger = React85__default.cloneElement(triggerElement, {
26012
26703
  ref: triggerRef,
26013
26704
  onMouseEnter: handleMouseEnter,
26014
26705
  onMouseLeave: handleMouseLeave,
@@ -26094,7 +26785,7 @@ var init_WizardProgress = __esm({
26094
26785
  children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: normalizedSteps.map((step, index) => {
26095
26786
  const isActive = index === currentStep;
26096
26787
  const isCompleted = index < currentStep;
26097
- return /* @__PURE__ */ jsxs(React82__default.Fragment, { children: [
26788
+ return /* @__PURE__ */ jsxs(React85__default.Fragment, { children: [
26098
26789
  /* @__PURE__ */ jsx(
26099
26790
  "button",
26100
26791
  {
@@ -27055,7 +27746,7 @@ var init_ProgressDots = __esm({
27055
27746
  }
27056
27747
  });
27057
27748
  function StatBadge({
27058
- assetUrl = DEFAULT_ASSET_URL12,
27749
+ assetUrl,
27059
27750
  label,
27060
27751
  value = 0,
27061
27752
  max,
@@ -27122,7 +27813,7 @@ function StatBadge({
27122
27813
  }
27123
27814
  );
27124
27815
  }
27125
- var sizeMap17, variantMap2, DEFAULT_ASSET_URL12;
27816
+ var sizeMap17, variantMap2;
27126
27817
  var init_StatBadge = __esm({
27127
27818
  "components/game/molecules/StatBadge.tsx"() {
27128
27819
  init_cn();
@@ -27141,7 +27832,6 @@ var init_StatBadge = __esm({
27141
27832
  warning: "bg-warning/15 border-warning/40 text-foreground",
27142
27833
  danger: "bg-error/15 border-error/40 text-foreground"
27143
27834
  };
27144
- DEFAULT_ASSET_URL12 = "https://almadar-kflow-assets.web.app/shared/effects/particles/light_01.png";
27145
27835
  StatBadge.displayName = "StatBadge";
27146
27836
  }
27147
27837
  });
@@ -27158,7 +27848,7 @@ function InventoryGrid({
27158
27848
  const eventBus = useEventBus();
27159
27849
  const slotCount = totalSlots ?? items.length;
27160
27850
  const emptySlotCount = Math.max(0, slotCount - items.length);
27161
- const handleSelect = React82.useCallback(
27851
+ const handleSelect = React85.useCallback(
27162
27852
  (id) => {
27163
27853
  onSelect?.(id);
27164
27854
  if (selectEvent) {
@@ -27389,35 +28079,37 @@ function GameCanvas2D({
27389
28079
  drawEvent,
27390
28080
  fps = 60,
27391
28081
  backgroundImage = "",
27392
- assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/",
28082
+ assetBaseUrl,
27393
28083
  className
27394
28084
  }) {
27395
- const canvasRef = React82.useRef(null);
27396
- const rafRef = React82.useRef(0);
27397
- const frameRef = React82.useRef(0);
27398
- const lastTimeRef = React82.useRef(0);
27399
- const imageCache = React82.useRef(/* @__PURE__ */ new Map());
28085
+ const canvasRef = React85.useRef(null);
28086
+ const rafRef = React85.useRef(0);
28087
+ const frameRef = React85.useRef(0);
28088
+ const lastTimeRef = React85.useRef(0);
28089
+ const imageCache = React85.useRef(/* @__PURE__ */ new Map());
27400
28090
  const emit = useEmitEvent();
27401
- const onDrawRef = React82.useRef(onDraw);
28091
+ const onDrawRef = React85.useRef(onDraw);
27402
28092
  onDrawRef.current = onDraw;
27403
- const onTickRef = React82.useRef(onTick);
28093
+ const onTickRef = React85.useRef(onTick);
27404
28094
  onTickRef.current = onTick;
27405
- const tickEventRef = React82.useRef(tickEvent);
28095
+ const tickEventRef = React85.useRef(tickEvent);
27406
28096
  tickEventRef.current = tickEvent;
27407
- const drawEventRef = React82.useRef(drawEvent);
28097
+ const drawEventRef = React85.useRef(drawEvent);
27408
28098
  drawEventRef.current = drawEvent;
27409
- const emitRef = React82.useRef(emit);
28099
+ const emitRef = React85.useRef(emit);
27410
28100
  emitRef.current = emit;
27411
- const assetBaseUrlRef = React82.useRef(assetBaseUrl);
28101
+ const assetBaseUrlRef = React85.useRef(assetBaseUrl);
27412
28102
  assetBaseUrlRef.current = assetBaseUrl;
27413
- const backgroundImageRef = React82.useRef(backgroundImage);
28103
+ const backgroundImageRef = React85.useRef(backgroundImage);
27414
28104
  backgroundImageRef.current = backgroundImage;
27415
- const widthRef = React82.useRef(width);
28105
+ const widthRef = React85.useRef(width);
27416
28106
  widthRef.current = width;
27417
- const heightRef = React82.useRef(height);
28107
+ const heightRef = React85.useRef(height);
27418
28108
  heightRef.current = height;
27419
- const loadImage = React82.useCallback((url) => {
27420
- const fullUrl = url.startsWith("http") ? url : `${assetBaseUrlRef.current}${url}`;
28109
+ const loadImage = React85.useCallback((url) => {
28110
+ const base = assetBaseUrlRef.current;
28111
+ if (!url.startsWith("http") && !base) return null;
28112
+ const fullUrl = url.startsWith("http") ? url : `${base}${url}`;
27421
28113
  const cached = imageCache.current.get(fullUrl);
27422
28114
  if (cached?.complete && cached.naturalWidth > 0) return cached;
27423
28115
  if (!cached) {
@@ -27428,7 +28120,7 @@ function GameCanvas2D({
27428
28120
  }
27429
28121
  return null;
27430
28122
  }, []);
27431
- React82.useEffect(() => {
28123
+ React85.useEffect(() => {
27432
28124
  const canvas = canvasRef.current;
27433
28125
  if (!canvas) return;
27434
28126
  const ctx = canvas.getContext("2d");
@@ -27740,7 +28432,7 @@ function TurnPanel({
27740
28432
  className
27741
28433
  }) {
27742
28434
  const eventBus = useEventBus();
27743
- const handleAction = React82.useCallback(
28435
+ const handleAction = React85.useCallback(
27744
28436
  (event) => {
27745
28437
  if (event) {
27746
28438
  eventBus.emit(event, { turn: currentTurn, phase, activeTeam });
@@ -27798,7 +28490,7 @@ var init_TurnPanel = __esm({
27798
28490
  }
27799
28491
  });
27800
28492
  function EnemyPlate({
27801
- assetUrl = DEFAULT_ASSET_URL13,
28493
+ assetUrl,
27802
28494
  name = "Shadow Guard",
27803
28495
  health = 80,
27804
28496
  maxHealth = 100,
@@ -27882,7 +28574,7 @@ function EnemyPlate({
27882
28574
  }
27883
28575
  );
27884
28576
  }
27885
- var effectVariantMap2, DEFAULT_ENEMY_EFFECTS, DEFAULT_ASSET_URL13;
28577
+ var effectVariantMap2, DEFAULT_ENEMY_EFFECTS;
27886
28578
  var init_EnemyPlate = __esm({
27887
28579
  "components/game/molecules/EnemyPlate.tsx"() {
27888
28580
  "use client";
@@ -27899,7 +28591,6 @@ var init_EnemyPlate = __esm({
27899
28591
  DEFAULT_ENEMY_EFFECTS = [
27900
28592
  { icon: "flame", label: "Burn", variant: "debuff" }
27901
28593
  ];
27902
- DEFAULT_ASSET_URL13 = "https://almadar-kflow-assets.web.app/shared/portraits/shadow-legion.png";
27903
28594
  EnemyPlate.displayName = "EnemyPlate";
27904
28595
  }
27905
28596
  });
@@ -27909,7 +28600,7 @@ function UnitCommandBar({
27909
28600
  className
27910
28601
  }) {
27911
28602
  const eventBus = useEventBus();
27912
- const handleCommand = React82.useCallback(
28603
+ const handleCommand = React85.useCallback(
27913
28604
  (event) => {
27914
28605
  if (event) {
27915
28606
  eventBus.emit(event, { unitId: selectedUnitId });
@@ -28050,6 +28741,9 @@ function DialogueBox({
28050
28741
  completeEvent,
28051
28742
  choiceEvent,
28052
28743
  advanceEvent,
28744
+ backgroundImage,
28745
+ portraitUrl,
28746
+ portraitScale = 1,
28053
28747
  className
28054
28748
  }) {
28055
28749
  const eventBus = useEventBus();
@@ -28165,64 +28859,98 @@ function DialogueBox({
28165
28859
  }
28166
28860
  }, [isTyping, skipTypewriter, dialogue.choices, selectedChoice, onChoice, onAdvance, choiceEvent, advanceEvent, eventBus]);
28167
28861
  const enabledChoices = dialogue.choices?.filter((c) => !c.disabled) ?? [];
28168
- return /* @__PURE__ */ jsx(
28169
- "div",
28170
- {
28171
- className: cn(
28172
- "fixed left-0 right-0 z-40",
28173
- position === "top" ? "top-0" : "bottom-0",
28174
- className
28175
- ),
28176
- onClick: handleClick,
28177
- onKeyDown: handleKeyDown,
28178
- tabIndex: 0,
28179
- role: "dialog",
28180
- "aria-label": "Dialogue",
28181
- 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: [
28182
- dialogue.portrait && /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 p-4 border-r border-border", children: /* @__PURE__ */ jsx(
28862
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
28863
+ backgroundImage && /* @__PURE__ */ jsx(
28864
+ "div",
28865
+ {
28866
+ className: "fixed inset-0 z-20 bg-center bg-cover bg-no-repeat",
28867
+ style: { backgroundImage: `url(${backgroundImage})` },
28868
+ "aria-hidden": "true"
28869
+ }
28870
+ ),
28871
+ portraitUrl && /* @__PURE__ */ jsx(
28872
+ "div",
28873
+ {
28874
+ className: cn(
28875
+ "fixed left-1/2 -translate-x-1/2 z-30 pointer-events-none flex items-end",
28876
+ position === "top" ? "top-0" : "bottom-0"
28877
+ ),
28878
+ style: { height: `${60 * portraitScale}vh` },
28879
+ children: /* @__PURE__ */ jsx(
28183
28880
  "img",
28184
28881
  {
28185
- src: dialogue.portrait,
28882
+ src: portraitUrl,
28186
28883
  alt: dialogue.speaker,
28187
- className: "w-24 h-24 object-contain",
28188
- style: { imageRendering: "pixelated" }
28884
+ className: "h-full w-auto object-contain drop-shadow-2xl"
28189
28885
  }
28190
- ) }),
28191
- /* @__PURE__ */ jsxs("div", { className: "flex-1 p-4", children: [
28192
- /* @__PURE__ */ jsx("div", { className: "text-warning font-bold mb-2", children: dialogue.speaker }),
28193
- /* @__PURE__ */ jsxs("div", { className: "text-[var(--color-foreground)] text-lg leading-relaxed min-h-[60px]", children: [
28194
- displayedText,
28195
- isTyping && /* @__PURE__ */ jsx("span", { className: "animate-pulse", children: "\u258C" })
28196
- ] }),
28197
- !isTyping && enabledChoices.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-2", children: enabledChoices.map((choice, index) => /* @__PURE__ */ jsxs(
28198
- "button",
28886
+ )
28887
+ }
28888
+ ),
28889
+ /* @__PURE__ */ jsx(
28890
+ "div",
28891
+ {
28892
+ className: cn(
28893
+ "fixed left-0 right-0 z-40",
28894
+ position === "top" ? "top-0" : "bottom-0",
28895
+ className
28896
+ ),
28897
+ onClick: handleClick,
28898
+ onKeyDown: handleKeyDown,
28899
+ tabIndex: 0,
28900
+ role: "dialog",
28901
+ "aria-label": "Dialogue",
28902
+ children: /* @__PURE__ */ jsx("div", { className: cn(
28903
+ "mx-4 my-4 border-2 rounded-container overflow-hidden",
28904
+ backgroundImage ? "bg-black/80 backdrop-blur-sm text-white border-white/20" : "bg-[var(--color-card)] bg-opacity-95 border-border"
28905
+ ), children: /* @__PURE__ */ jsxs("div", { className: "flex", children: [
28906
+ dialogue.portrait && /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 p-4 border-r border-border", children: /* @__PURE__ */ jsx(
28907
+ "img",
28199
28908
  {
28200
- type: "button",
28201
- className: cn(
28202
- "block w-full text-left px-4 py-2 rounded-interactive transition-colors",
28203
- "hover:bg-[var(--color-surface,#374151)] focus:outline-none focus:ring-2 focus:ring-warning",
28204
- selectedChoice === index ? "bg-[var(--color-surface,#374151)] text-warning" : "bg-[var(--color-card)] text-[var(--color-foreground)]"
28205
- ),
28206
- onClick: (e) => {
28207
- e.stopPropagation();
28208
- if (choiceEvent) eventBus.emit(`UI:${choiceEvent}`, { choice });
28209
- onChoice?.(choice);
28909
+ src: dialogue.portrait,
28910
+ alt: dialogue.speaker,
28911
+ className: "w-24 h-24 object-contain",
28912
+ style: { imageRendering: "pixelated" }
28913
+ }
28914
+ ) }),
28915
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 p-4", children: [
28916
+ /* @__PURE__ */ jsx("div", { className: "text-warning font-bold mb-2", children: dialogue.speaker }),
28917
+ /* @__PURE__ */ jsxs("div", { className: cn(
28918
+ "text-lg leading-relaxed min-h-[60px]",
28919
+ backgroundImage ? "text-white" : "text-[var(--color-foreground)]"
28920
+ ), children: [
28921
+ displayedText,
28922
+ isTyping && /* @__PURE__ */ jsx("span", { className: "animate-pulse", children: "\u258C" })
28923
+ ] }),
28924
+ !isTyping && enabledChoices.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-2", children: enabledChoices.map((choice, index) => /* @__PURE__ */ jsxs(
28925
+ "button",
28926
+ {
28927
+ type: "button",
28928
+ className: cn(
28929
+ "block w-full text-left px-4 py-2 rounded-interactive transition-colors",
28930
+ "hover:bg-[var(--color-surface,#374151)] focus:outline-none focus:ring-2 focus:ring-warning",
28931
+ 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)]"
28932
+ ),
28933
+ onClick: (e) => {
28934
+ e.stopPropagation();
28935
+ if (choiceEvent) eventBus.emit(`UI:${choiceEvent}`, { choice });
28936
+ onChoice?.(choice);
28937
+ },
28938
+ children: [
28939
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground mr-2", children: [
28940
+ index + 1,
28941
+ "."
28942
+ ] }),
28943
+ choice.text
28944
+ ]
28210
28945
  },
28211
- children: [
28212
- /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground mr-2", children: [
28213
- index + 1,
28214
- "."
28215
- ] }),
28216
- choice.text
28217
- ]
28218
- },
28219
- index
28220
- )) }),
28221
- !isTyping && !dialogue.choices?.length && /* @__PURE__ */ jsx("div", { className: "mt-4 text-muted-foreground text-sm animate-pulse", children: "Press SPACE or click to continue..." })
28222
- ] })
28223
- ] }) })
28224
- }
28225
- );
28946
+ index
28947
+ )) }),
28948
+ !isTyping && !dialogue.choices?.length && /* @__PURE__ */ jsx("div", { className: "mt-4 text-muted-foreground text-sm animate-pulse", children: "Press SPACE or click to continue..." })
28949
+ ] })
28950
+ ] }) })
28951
+ }
28952
+ )
28953
+ ] });
28226
28954
  }
28227
28955
  var DEFAULT_DIALOGUE;
28228
28956
  var init_DialogueBox = __esm({
@@ -28423,7 +29151,7 @@ function GameMenu({
28423
29151
  } catch {
28424
29152
  }
28425
29153
  const eventBus = eventBusProp || eventBusFromHook;
28426
- const handleOptionClick = React82.useCallback(
29154
+ const handleOptionClick = React85.useCallback(
28427
29155
  (option) => {
28428
29156
  if (option.event && eventBus) {
28429
29157
  eventBus.emit(`UI:${option.event}`, { option });
@@ -28542,7 +29270,7 @@ function GameOverScreen({
28542
29270
  } catch {
28543
29271
  }
28544
29272
  const eventBus = eventBusProp || eventBusFromHook;
28545
- const handleActionClick = React82.useCallback(
29273
+ const handleActionClick = React85.useCallback(
28546
29274
  (action) => {
28547
29275
  if (action.event && eventBus) {
28548
29276
  eventBus.emit(`UI:${action.event}`, { action });
@@ -28749,10 +29477,10 @@ function PlatformerCanvas({
28749
29477
  canvasHeight = 400,
28750
29478
  followCamera = true,
28751
29479
  bgColor = "#5c94fc",
28752
- playerSprite = "https://almadar-kflow-assets.web.app/shared/platformer/characters/platformChar_idle.png",
29480
+ playerSprite,
28753
29481
  tileSprites,
28754
29482
  backgroundImage = "",
28755
- assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/platformer/",
29483
+ assetBaseUrl,
28756
29484
  leftEvent = "MOVE_LEFT",
28757
29485
  rightEvent = "MOVE_RIGHT",
28758
29486
  jumpEvent = "JUMP",
@@ -28766,6 +29494,7 @@ function PlatformerCanvas({
28766
29494
  const imageCache = useRef(/* @__PURE__ */ new Map());
28767
29495
  const [loadedImages, setLoadedImages] = useState(/* @__PURE__ */ new Set());
28768
29496
  const loadImage = useCallback((url) => {
29497
+ if (!url.startsWith("http") && !assetBaseUrl) return null;
28769
29498
  const fullUrl = url.startsWith("http") ? url : `${assetBaseUrl}${url}`;
28770
29499
  const cached = imageCache.current.get(fullUrl);
28771
29500
  if (cached?.complete && cached.naturalWidth > 0) {
@@ -29464,13 +30193,13 @@ var init_MapView = __esm({
29464
30193
  shadowSize: [41, 41]
29465
30194
  });
29466
30195
  L.Marker.prototype.options.icon = defaultIcon;
29467
- const { useEffect: useEffect80, useRef: useRef75, useCallback: useCallback118, useState: useState109 } = React82__default;
30196
+ const { useEffect: useEffect81, useRef: useRef78, useCallback: useCallback123, useState: useState110 } = React85__default;
29468
30197
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
29469
30198
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
29470
30199
  function MapUpdater({ centerLat, centerLng, zoom }) {
29471
30200
  const map = useMap();
29472
- const prevRef = useRef75({ centerLat, centerLng, zoom });
29473
- useEffect80(() => {
30201
+ const prevRef = useRef78({ centerLat, centerLng, zoom });
30202
+ useEffect81(() => {
29474
30203
  const prev = prevRef.current;
29475
30204
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
29476
30205
  map.setView([centerLat, centerLng], zoom);
@@ -29481,7 +30210,7 @@ var init_MapView = __esm({
29481
30210
  }
29482
30211
  function MapClickHandler({ onMapClick }) {
29483
30212
  const map = useMap();
29484
- useEffect80(() => {
30213
+ useEffect81(() => {
29485
30214
  if (!onMapClick) return;
29486
30215
  const handler = (e) => {
29487
30216
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -29509,8 +30238,8 @@ var init_MapView = __esm({
29509
30238
  showAttribution = true
29510
30239
  }) {
29511
30240
  const eventBus = useEventBus3();
29512
- const [clickedPosition, setClickedPosition] = useState109(null);
29513
- const handleMapClick = useCallback118((lat, lng) => {
30241
+ const [clickedPosition, setClickedPosition] = useState110(null);
30242
+ const handleMapClick = useCallback123((lat, lng) => {
29514
30243
  if (showClickedPin) {
29515
30244
  setClickedPosition({ lat, lng });
29516
30245
  }
@@ -29519,7 +30248,7 @@ var init_MapView = __esm({
29519
30248
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
29520
30249
  }
29521
30250
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
29522
- const handleMarkerClick = useCallback118((marker) => {
30251
+ const handleMarkerClick = useCallback123((marker) => {
29523
30252
  onMarkerClick?.(marker);
29524
30253
  if (markerClickEvent) {
29525
30254
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -30370,8 +31099,8 @@ function TableView({
30370
31099
  }) {
30371
31100
  const eventBus = useEventBus();
30372
31101
  const { t } = useTranslate();
30373
- const [visibleCount, setVisibleCount] = React82__default.useState(pageSize > 0 ? pageSize : Infinity);
30374
- const [localSelected, setLocalSelected] = React82__default.useState(/* @__PURE__ */ new Set());
31102
+ const [visibleCount, setVisibleCount] = React85__default.useState(pageSize > 0 ? pageSize : Infinity);
31103
+ const [localSelected, setLocalSelected] = React85__default.useState(/* @__PURE__ */ new Set());
30375
31104
  const colDefs = columns ?? fields ?? [];
30376
31105
  const allDataRaw = Array.isArray(entity) ? entity : entity ? [entity] : [];
30377
31106
  const dnd = useDataDnd({
@@ -30566,12 +31295,12 @@ function TableView({
30566
31295
  ]
30567
31296
  }
30568
31297
  );
30569
- return dnd.isZone ? /* @__PURE__ */ jsx(dnd.SortableItem, { id: row[idField] ?? id, children: rowInner }, id) : /* @__PURE__ */ jsx(React82__default.Fragment, { children: rowInner }, id);
31298
+ return dnd.isZone ? /* @__PURE__ */ jsx(dnd.SortableItem, { id: row[idField] ?? id, children: rowInner }, id) : /* @__PURE__ */ jsx(React85__default.Fragment, { children: rowInner }, id);
30570
31299
  };
30571
31300
  const items = data.map((row) => row);
30572
31301
  const groups = groupBy ? groupData2(items, groupBy) : [{ label: "", items }];
30573
31302
  let runningIndex = 0;
30574
- const body = /* @__PURE__ */ jsx(Box, { role: "rowgroup", children: groups.map((group, gi) => /* @__PURE__ */ jsxs(React82__default.Fragment, { children: [
31303
+ const body = /* @__PURE__ */ jsx(Box, { role: "rowgroup", children: groups.map((group, gi) => /* @__PURE__ */ jsxs(React85__default.Fragment, { children: [
30575
31304
  group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: gi > 0 ? "mt-3" : "mt-0" }),
30576
31305
  group.items.map((row) => renderRow(row, runningIndex++))
30577
31306
  ] }, gi)) });
@@ -31928,7 +32657,7 @@ var init_StepFlow = __esm({
31928
32657
  className
31929
32658
  }) => {
31930
32659
  if (orientation === "vertical") {
31931
- return /* @__PURE__ */ jsx(VStack, { gap: "none", className: cn("w-full", className), children: steps.map((step, index) => /* @__PURE__ */ jsx(React82__default.Fragment, { children: /* @__PURE__ */ jsxs(HStack, { gap: "md", align: "start", className: "w-full", children: [
32660
+ return /* @__PURE__ */ jsx(VStack, { gap: "none", className: cn("w-full", className), children: steps.map((step, index) => /* @__PURE__ */ jsx(React85__default.Fragment, { children: /* @__PURE__ */ jsxs(HStack, { gap: "md", align: "start", className: "w-full", children: [
31932
32661
  /* @__PURE__ */ jsxs(VStack, { gap: "none", align: "center", children: [
31933
32662
  /* @__PURE__ */ jsx(StepCircle, { step, index }),
31934
32663
  showConnectors && index < steps.length - 1 && /* @__PURE__ */ jsx(Box, { className: "w-px h-8 bg-border" })
@@ -31939,7 +32668,7 @@ var init_StepFlow = __esm({
31939
32668
  ] })
31940
32669
  ] }) }, index)) });
31941
32670
  }
31942
- 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(React82__default.Fragment, { children: [
32671
+ 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(React85__default.Fragment, { children: [
31943
32672
  /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", className: "flex-1 w-full md:w-auto", children: [
31944
32673
  /* @__PURE__ */ jsx(StepCircle, { step, index }),
31945
32674
  /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-center", children: step.title }),
@@ -32924,7 +33653,7 @@ var init_LikertScale = __esm({
32924
33653
  md: "text-base",
32925
33654
  lg: "text-lg"
32926
33655
  };
32927
- LikertScale = React82__default.forwardRef(
33656
+ LikertScale = React85__default.forwardRef(
32928
33657
  ({
32929
33658
  question,
32930
33659
  options = DEFAULT_LIKERT_OPTIONS,
@@ -32936,7 +33665,7 @@ var init_LikertScale = __esm({
32936
33665
  variant = "radios",
32937
33666
  className
32938
33667
  }, ref) => {
32939
- const groupId = React82__default.useId();
33668
+ const groupId = React85__default.useId();
32940
33669
  const eventBus = useEventBus();
32941
33670
  const handleSelect = useCallback(
32942
33671
  (next) => {
@@ -35218,7 +35947,7 @@ var init_DocBreadcrumb = __esm({
35218
35947
  "aria-label": t("aria.breadcrumb"),
35219
35948
  children: /* @__PURE__ */ jsx(HStack, { gap: "xs", align: "center", wrap: true, children: items.map((item, idx) => {
35220
35949
  const isLast = idx === items.length - 1;
35221
- return /* @__PURE__ */ jsxs(React82__default.Fragment, { children: [
35950
+ return /* @__PURE__ */ jsxs(React85__default.Fragment, { children: [
35222
35951
  idx > 0 && /* @__PURE__ */ jsx(
35223
35952
  Icon,
35224
35953
  {
@@ -36087,7 +36816,7 @@ var init_MiniStateMachine = __esm({
36087
36816
  const x = 2 + i * (NODE_W + GAP + ARROW_W + GAP);
36088
36817
  const tc = transitionCounts[s.name] ?? 0;
36089
36818
  const role = getStateRole(s.name, s.isInitial, s.isTerminal, tc, maxTC);
36090
- return /* @__PURE__ */ jsxs(React82__default.Fragment, { children: [
36819
+ return /* @__PURE__ */ jsxs(React85__default.Fragment, { children: [
36091
36820
  /* @__PURE__ */ jsx(
36092
36821
  AvlState,
36093
36822
  {
@@ -36291,7 +37020,7 @@ var init_PageHeader = __esm({
36291
37020
  info: "bg-info/10 text-info"
36292
37021
  };
36293
37022
  return /* @__PURE__ */ jsxs(Box, { className: cn("mb-6", className), children: [
36294
- 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(React82__default.Fragment, { children: [
37023
+ 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(React85__default.Fragment, { children: [
36295
37024
  idx > 0 && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: "/" }),
36296
37025
  crumb.href ? /* @__PURE__ */ jsx(
36297
37026
  "a",
@@ -36973,8 +37702,8 @@ var init_WizardContainer = __esm({
36973
37702
  return void 0;
36974
37703
  if (typeof controlledStep === "number") return controlledStep;
36975
37704
  if (typeof controlledStep === "string") return parseInt(controlledStep, 10);
36976
- const num2 = Number(controlledStep);
36977
- return isNaN(num2) ? void 0 : num2;
37705
+ const num3 = Number(controlledStep);
37706
+ return isNaN(num3) ? void 0 : num3;
36978
37707
  })();
36979
37708
  const currentStep = normalizedControlledStep !== void 0 ? normalizedControlledStep : internalStep;
36980
37709
  const totalSteps = steps.length;
@@ -37020,7 +37749,7 @@ var init_WizardContainer = __esm({
37020
37749
  const isCompleted = index < currentStep;
37021
37750
  const stepKey = step.id ?? step.tabId ?? `step-${index}`;
37022
37751
  const stepTitle = step.title ?? step.name ?? `Step ${index + 1}`;
37023
- return /* @__PURE__ */ jsxs(React82__default.Fragment, { children: [
37752
+ return /* @__PURE__ */ jsxs(React85__default.Fragment, { children: [
37024
37753
  /* @__PURE__ */ jsx(
37025
37754
  Button,
37026
37755
  {
@@ -37952,6 +38681,8 @@ var init_GraphCanvas = __esm({
37952
38681
  selectedNodeId,
37953
38682
  repulsion = 800,
37954
38683
  linkDistance = 100,
38684
+ nodeSpacing = 28,
38685
+ linkOpacity = 0.18,
37955
38686
  layout = "force",
37956
38687
  entity,
37957
38688
  isLoading = false,
@@ -38111,6 +38842,25 @@ var init_GraphCanvas = __esm({
38111
38842
  node.x = Math.max(30, Math.min(w - 30, node.x));
38112
38843
  node.y = Math.max(30, Math.min(h - 30, node.y));
38113
38844
  }
38845
+ for (let i = 0; i < nodes.length; i++) {
38846
+ for (let j = i + 1; j < nodes.length; j++) {
38847
+ const a = nodes[i];
38848
+ const b = nodes[j];
38849
+ const dx = b.x - a.x;
38850
+ const dy = b.y - a.y;
38851
+ const dist = Math.sqrt(dx * dx + dy * dy) || 1;
38852
+ const minDist = (a.size || 8) + (b.size || 8) + nodeSpacing;
38853
+ if (dist < minDist) {
38854
+ const push = (minDist - dist) / 2;
38855
+ const ux = dx / dist;
38856
+ const uy = dy / dist;
38857
+ a.x = Math.max(30, Math.min(w - 30, a.x - ux * push));
38858
+ a.y = Math.max(30, Math.min(h - 30, a.y - uy * push));
38859
+ b.x = Math.max(30, Math.min(w - 30, b.x + ux * push));
38860
+ b.y = Math.max(30, Math.min(h - 30, b.y + uy * push));
38861
+ }
38862
+ }
38863
+ }
38114
38864
  iterations++;
38115
38865
  forceUpdate((n) => n + 1);
38116
38866
  if (iterations < maxIterations) {
@@ -38124,7 +38874,7 @@ var init_GraphCanvas = __esm({
38124
38874
  return () => {
38125
38875
  cancelAnimationFrame(animRef.current);
38126
38876
  };
38127
- }, [propNodes, propEdges, layout, repulsion, linkDistance, logicalW, height]);
38877
+ }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, logicalW, height]);
38128
38878
  useEffect(() => {
38129
38879
  const canvas = canvasRef.current;
38130
38880
  if (!canvas) return;
@@ -38153,11 +38903,11 @@ var init_GraphCanvas = __esm({
38153
38903
  const target = nodes.find((n) => n.id === edge.target);
38154
38904
  if (!source || !target) continue;
38155
38905
  const incident = !!hoveredNode && (edge.source === hoveredNode || edge.target === hoveredNode);
38156
- ctx.globalAlpha = hoveredNode ? incident ? 1 : 0.12 : 1;
38906
+ ctx.globalAlpha = hoveredNode ? incident ? 1 : 0.05 : linkOpacity;
38157
38907
  ctx.beginPath();
38158
38908
  ctx.moveTo(source.x, source.y);
38159
38909
  ctx.lineTo(target.x, target.y);
38160
- ctx.strokeStyle = incident ? accentColor : edge.color || "#88888866";
38910
+ ctx.strokeStyle = incident ? accentColor : edge.color || "#888888";
38161
38911
  ctx.lineWidth = incident ? 2 : edge.weight ? Math.max(1, edge.weight) : 1;
38162
38912
  ctx.stroke();
38163
38913
  if (edge.label && showLabels) {
@@ -39521,7 +40271,7 @@ var init_DetailPanel = __esm({
39521
40271
  }
39522
40272
  });
39523
40273
  function extractTitle(children) {
39524
- if (!React82__default.isValidElement(children)) return void 0;
40274
+ if (!React85__default.isValidElement(children)) return void 0;
39525
40275
  const props = children.props;
39526
40276
  if (typeof props.title === "string") {
39527
40277
  return props.title;
@@ -39576,7 +40326,7 @@ function LinearView({
39576
40326
  /* @__PURE__ */ jsx(HStack, { className: "flex-wrap items-center", gap: "xs", children: trait.states.map((state, i) => {
39577
40327
  const isDone = i < currentIdx;
39578
40328
  const isCurrent = i === currentIdx;
39579
- return /* @__PURE__ */ jsxs(React82__default.Fragment, { children: [
40329
+ return /* @__PURE__ */ jsxs(React85__default.Fragment, { children: [
39580
40330
  i > 0 && /* @__PURE__ */ jsx(
39581
40331
  Typography,
39582
40332
  {
@@ -40526,12 +41276,12 @@ var init_Form = __esm({
40526
41276
  const isSchemaEntity = isOrbitalEntitySchema(entity);
40527
41277
  const resolvedEntity = isSchemaEntity ? entity : void 0;
40528
41278
  const entityName = typeof entity === "string" ? entity : resolvedEntity?.name;
40529
- const normalizedInitialData = React82__default.useMemo(() => {
41279
+ const normalizedInitialData = React85__default.useMemo(() => {
40530
41280
  const entityRowAsInitial = isPlainEntityRow(entity) ? entity : void 0;
40531
41281
  const callerInitial = initialData !== null && typeof initialData === "object" && !Array.isArray(initialData) ? initialData : {};
40532
41282
  return entityRowAsInitial !== void 0 ? { ...entityRowAsInitial, ...callerInitial } : callerInitial;
40533
41283
  }, [entity, initialData]);
40534
- const entityDerivedFields = React82__default.useMemo(() => {
41284
+ const entityDerivedFields = React85__default.useMemo(() => {
40535
41285
  if (fields && fields.length > 0) return void 0;
40536
41286
  if (!resolvedEntity) return void 0;
40537
41287
  return resolvedEntity.fields.map(
@@ -40551,16 +41301,16 @@ var init_Form = __esm({
40551
41301
  const conditionalFields = typeof conditionalFieldsRaw === "boolean" ? {} : conditionalFieldsRaw;
40552
41302
  const hiddenCalculations = typeof hiddenCalculationsRaw === "boolean" ? [] : hiddenCalculationsRaw;
40553
41303
  const violationTriggers = typeof violationTriggersRaw === "boolean" ? [] : violationTriggersRaw;
40554
- const [formData, setFormData] = React82__default.useState(
41304
+ const [formData, setFormData] = React85__default.useState(
40555
41305
  normalizedInitialData
40556
41306
  );
40557
- const [collapsedSections, setCollapsedSections] = React82__default.useState(
41307
+ const [collapsedSections, setCollapsedSections] = React85__default.useState(
40558
41308
  /* @__PURE__ */ new Set()
40559
41309
  );
40560
- const [submitError, setSubmitError] = React82__default.useState(null);
40561
- const formRef = React82__default.useRef(null);
41310
+ const [submitError, setSubmitError] = React85__default.useState(null);
41311
+ const formRef = React85__default.useRef(null);
40562
41312
  const formMode = props.mode;
40563
- const mountedRef = React82__default.useRef(false);
41313
+ const mountedRef = React85__default.useRef(false);
40564
41314
  if (!mountedRef.current) {
40565
41315
  mountedRef.current = true;
40566
41316
  debug("forms", "mount", {
@@ -40573,7 +41323,7 @@ var init_Form = __esm({
40573
41323
  });
40574
41324
  }
40575
41325
  const shouldShowCancel = showCancel ?? (fields && fields.length > 0);
40576
- const evalContext = React82__default.useMemo(
41326
+ const evalContext = React85__default.useMemo(
40577
41327
  () => ({
40578
41328
  formValues: formData,
40579
41329
  globalVariables: externalContext?.globalVariables ?? {},
@@ -40582,7 +41332,7 @@ var init_Form = __esm({
40582
41332
  }),
40583
41333
  [formData, externalContext]
40584
41334
  );
40585
- React82__default.useEffect(() => {
41335
+ React85__default.useEffect(() => {
40586
41336
  debug("forms", "initialData-sync", {
40587
41337
  mode: formMode,
40588
41338
  normalizedInitialData,
@@ -40593,7 +41343,7 @@ var init_Form = __esm({
40593
41343
  setFormData(normalizedInitialData);
40594
41344
  }
40595
41345
  }, [normalizedInitialData]);
40596
- const processCalculations = React82__default.useCallback(
41346
+ const processCalculations = React85__default.useCallback(
40597
41347
  (changedFieldId, newFormData) => {
40598
41348
  if (!hiddenCalculations.length) return;
40599
41349
  const context = {
@@ -40618,7 +41368,7 @@ var init_Form = __esm({
40618
41368
  },
40619
41369
  [hiddenCalculations, externalContext, eventBus]
40620
41370
  );
40621
- const checkViolations = React82__default.useCallback(
41371
+ const checkViolations = React85__default.useCallback(
40622
41372
  (changedFieldId, newFormData) => {
40623
41373
  if (!violationTriggers.length) return;
40624
41374
  const context = {
@@ -40656,7 +41406,7 @@ var init_Form = __esm({
40656
41406
  processCalculations(name, newFormData);
40657
41407
  checkViolations(name, newFormData);
40658
41408
  };
40659
- const isFieldVisible = React82__default.useCallback(
41409
+ const isFieldVisible = React85__default.useCallback(
40660
41410
  (fieldName) => {
40661
41411
  const condition = conditionalFields[fieldName];
40662
41412
  if (!condition) return true;
@@ -40664,7 +41414,7 @@ var init_Form = __esm({
40664
41414
  },
40665
41415
  [conditionalFields, evalContext]
40666
41416
  );
40667
- const isSectionVisible = React82__default.useCallback(
41417
+ const isSectionVisible = React85__default.useCallback(
40668
41418
  (section) => {
40669
41419
  if (!section.condition) return true;
40670
41420
  return Boolean(evaluateFormExpression(section.condition, evalContext));
@@ -40740,7 +41490,7 @@ var init_Form = __esm({
40740
41490
  eventBus.emit(`UI:${onCancel}`);
40741
41491
  }
40742
41492
  };
40743
- const renderField = React82__default.useCallback(
41493
+ const renderField = React85__default.useCallback(
40744
41494
  (field) => {
40745
41495
  const fieldName = field.name || field.field;
40746
41496
  if (!fieldName) return null;
@@ -40761,7 +41511,7 @@ var init_Form = __esm({
40761
41511
  [formData, isFieldVisible, relationsData, relationsLoading, isLoading]
40762
41512
  );
40763
41513
  const effectiveFields = entityDerivedFields ?? fields;
40764
- const normalizedFields = React82__default.useMemo(() => {
41514
+ const normalizedFields = React85__default.useMemo(() => {
40765
41515
  if (!effectiveFields || effectiveFields.length === 0) return [];
40766
41516
  return effectiveFields.map((field) => {
40767
41517
  if (typeof field === "string") {
@@ -40784,7 +41534,7 @@ var init_Form = __esm({
40784
41534
  return field;
40785
41535
  });
40786
41536
  }, [effectiveFields, resolvedEntity]);
40787
- const schemaFields = React82__default.useMemo(() => {
41537
+ const schemaFields = React85__default.useMemo(() => {
40788
41538
  if (normalizedFields.length === 0) return null;
40789
41539
  if (isDebugEnabled()) {
40790
41540
  debugGroup(`Form: ${entityName || "unknown"}`);
@@ -40794,7 +41544,7 @@ var init_Form = __esm({
40794
41544
  }
40795
41545
  return normalizedFields.map(renderField).filter(Boolean);
40796
41546
  }, [normalizedFields, renderField, entityName, conditionalFields]);
40797
- const sectionElements = React82__default.useMemo(() => {
41547
+ const sectionElements = React85__default.useMemo(() => {
40798
41548
  if (!sections || sections.length === 0) return null;
40799
41549
  return sections.map((section) => {
40800
41550
  if (!isSectionVisible(section)) {
@@ -42114,12 +42864,18 @@ function ModelLoader({
42114
42864
  });
42115
42865
  return cloned;
42116
42866
  }, [loadedModel, castShadow, receiveShadow]);
42867
+ const normFactor = useMemo(() => {
42868
+ if (!model) return 1;
42869
+ const box = new THREE3.Box3().setFromObject(model);
42870
+ const size = new THREE3.Vector3();
42871
+ box.getSize(size);
42872
+ const maxDim = Math.max(size.x, size.y, size.z);
42873
+ return maxDim > 0 && Number.isFinite(maxDim) ? 1 / maxDim : 1;
42874
+ }, [model]);
42117
42875
  const scaleArray = useMemo(() => {
42118
- if (typeof scale === "number") {
42119
- return [scale, scale, scale];
42120
- }
42121
- return scale;
42122
- }, [scale]);
42876
+ const base = typeof scale === "number" ? [scale, scale, scale] : scale;
42877
+ return [base[0] * normFactor, base[1] * normFactor, base[2] * normFactor];
42878
+ }, [scale, normFactor]);
42123
42879
  const rotationRad = useMemo(() => {
42124
42880
  return [
42125
42881
  rotation[0] * Math.PI / 180,
@@ -42330,6 +43086,29 @@ var init_GameCanvas3D2 = __esm({
42330
43086
  const controlsRef = useRef(null);
42331
43087
  const [hoveredTile, setHoveredTile] = useState(null);
42332
43088
  const [internalError, setInternalError] = useState(null);
43089
+ useEffect(() => {
43090
+ const el = containerRef.current;
43091
+ if (!el) return;
43092
+ let node = el;
43093
+ let depth = 0;
43094
+ while (node && depth < 8) {
43095
+ const cs = window.getComputedStyle(node);
43096
+ const rect = node.getBoundingClientRect();
43097
+ console.log("[almadar:ui:game:3d-height]", {
43098
+ depth,
43099
+ tag: node.tagName,
43100
+ id: node.id || void 0,
43101
+ className: node.className?.slice?.(0, 60) || void 0,
43102
+ rectHeight: rect.height,
43103
+ computedHeight: cs.height,
43104
+ computedMinHeight: cs.minHeight,
43105
+ computedFlex: cs.flex,
43106
+ computedOverflow: cs.overflow
43107
+ });
43108
+ node = node.parentElement;
43109
+ depth++;
43110
+ }
43111
+ }, []);
42333
43112
  const { sheetUrls: atlasSheetUrls, resolveUnitFrame } = useUnitSpriteAtlas(units);
42334
43113
  const preloadUrls = useMemo(() => [...preloadAssets, ...atlasSheetUrls], [preloadAssets, atlasSheetUrls]);
42335
43114
  const { isLoading: assetsLoading, progress, loaded, total } = useAssetLoader({
@@ -42719,7 +43498,7 @@ var init_GameCanvas3D2 = __esm({
42719
43498
  {
42720
43499
  ref: containerRef,
42721
43500
  className: cn("game-canvas-3d relative w-full overflow-hidden", className),
42722
- style: { minHeight: "85vh" },
43501
+ style: { height: "85vh" },
42723
43502
  "data-orientation": orientation,
42724
43503
  "data-camera-mode": cameraMode,
42725
43504
  "data-overlay": overlay,
@@ -42734,7 +43513,7 @@ var init_GameCanvas3D2 = __esm({
42734
43513
  near: 0.1,
42735
43514
  far: 1e3
42736
43515
  },
42737
- style: { background: backgroundColor, height: "85vh", width: "100%" },
43516
+ style: { background: backgroundColor, position: "absolute", inset: 0 },
42738
43517
  onClick: (e) => {
42739
43518
  if (e.target === e.currentTarget) {
42740
43519
  eventHandlers.handleCanvasClick(e);
@@ -43705,7 +44484,7 @@ var init_List = __esm({
43705
44484
  if (entity && typeof entity === "object" && "id" in entity) return [entity];
43706
44485
  return [];
43707
44486
  }, [entity]);
43708
- const getItemActions = React82__default.useCallback(
44487
+ const getItemActions = React85__default.useCallback(
43709
44488
  (item) => {
43710
44489
  if (!itemActions) return [];
43711
44490
  if (typeof itemActions === "function") {
@@ -44181,7 +44960,7 @@ var init_MediaGallery = __esm({
44181
44960
  [selectable, selectedItems, selectionEvent, eventBus]
44182
44961
  );
44183
44962
  const entityData = Array.isArray(entity) ? entity : [];
44184
- const items = React82__default.useMemo(() => {
44963
+ const items = React85__default.useMemo(() => {
44185
44964
  if (propItems) return propItems;
44186
44965
  if (entityData.length === 0) return [];
44187
44966
  return entityData.map((record, idx) => ({
@@ -44342,7 +45121,7 @@ var init_MediaGallery = __esm({
44342
45121
  }
44343
45122
  });
44344
45123
  function extractTitle2(children) {
44345
- if (!React82__default.isValidElement(children)) return void 0;
45124
+ if (!React85__default.isValidElement(children)) return void 0;
44346
45125
  const props = children.props;
44347
45126
  if (typeof props.title === "string") {
44348
45127
  return props.title;
@@ -44918,31 +45697,33 @@ var init_PricingPageTemplate = __esm({
44918
45697
  PricingPageTemplate.displayName = "PricingPageTemplate";
44919
45698
  }
44920
45699
  });
44921
- function dungeonTerrain(x, y, stairsX, stairsY) {
45700
+ function resolveManifestUrl3(manifest, relative) {
45701
+ if (relative == null) return void 0;
45702
+ if (/^https?:\/\//.test(relative)) return relative;
45703
+ const base = manifest?.baseUrl;
45704
+ if (base == null) return relative;
45705
+ const cleanBase = base.replace(/\/$/, "");
45706
+ const cleanRel = relative.replace(/^\//, "");
45707
+ return `${cleanBase}/${cleanRel}`;
45708
+ }
45709
+ function dungeonTerrain(x, y, stairsX, stairsY, manifest) {
44922
45710
  const isBorder = x === 0 || y === 0 || x === DUNGEON_GRID_W - 1 || y === DUNGEON_GRID_H - 1;
44923
45711
  const isPillar = x % 4 === 0 && y % 4 === 0 && !isBorder;
44924
45712
  const isWall = isBorder || isPillar;
45713
+ const terrains = manifest?.terrains;
44925
45714
  if (x === stairsX && y === stairsY) {
44926
- return { terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` };
45715
+ return { terrain: "stairs", passable: true, terrainSprite: resolveManifestUrl3(manifest, terrains?.stairs) };
44927
45716
  }
44928
45717
  if (isWall) {
44929
- return { terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` };
44930
- }
44931
- const variant = (x * 3 + y * 7) % 5;
44932
- const sprites = [
44933
- `${CDN4}/isometric-dungeon/Isometric/dirt_E.png`,
44934
- `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png`,
44935
- `${CDN4}/isometric-dungeon/Isometric/planks_E.png`,
44936
- `${CDN4}/isometric-dungeon/Isometric/stoneTile_E.png`,
44937
- `${CDN4}/isometric-dungeon/Isometric/dirt_E.png`
44938
- ];
44939
- return { terrain: "floor", passable: true, terrainSprite: sprites[variant] };
45718
+ return { terrain: "wall", passable: false, terrainSprite: resolveManifestUrl3(manifest, terrains?.wall) };
45719
+ }
45720
+ return { terrain: "floor", passable: true, terrainSprite: resolveManifestUrl3(manifest, terrains?.floor) };
44940
45721
  }
44941
- function buildDefaultDungeonTiles(stairsX, stairsY) {
45722
+ function buildDefaultDungeonTiles(stairsX, stairsY, manifest) {
44942
45723
  const tiles = [];
44943
45724
  for (let y = 0; y < DUNGEON_GRID_H; y++) {
44944
45725
  for (let x = 0; x < DUNGEON_GRID_W; x++) {
44945
- tiles.push({ x, y, ...dungeonTerrain(x, y, stairsX, stairsY) });
45726
+ tiles.push({ x, y, ...dungeonTerrain(x, y, stairsX, stairsY, manifest) });
44946
45727
  }
44947
45728
  }
44948
45729
  return tiles;
@@ -44972,20 +45753,48 @@ function RoguelikeBoard({
44972
45753
  className
44973
45754
  }) {
44974
45755
  const board = boardEntity(entity) ?? {};
44975
- const resolvedStairsX = propStairsX ?? num(board.stairsX, 14);
44976
- const resolvedStairsY = propStairsY ?? num(board.stairsY, 14);
44977
- const tiles = propTiles ?? (Array.isArray(board.tiles) && board.tiles.length >= DUNGEON_GRID_W * DUNGEON_GRID_H ? board.tiles : buildDefaultDungeonTiles(resolvedStairsX, resolvedStairsY));
44978
- const enemies = propEnemies ?? (Array.isArray(board.enemies) ? board.enemies : DEFAULT_ENEMIES);
44979
- const items = propItems ?? (Array.isArray(board.items) ? board.items : DEFAULT_ITEMS);
44980
- const player = propPlayer ?? board.player ?? { x: 1, y: 1 };
45756
+ const assetManifest = propAssetManifest ?? board.assetManifest;
45757
+ const stairsX = propStairsX ?? num(board.stairsX, 14);
45758
+ const stairsY = propStairsY ?? num(board.stairsY, 14);
45759
+ const entityTiles = useMemo(
45760
+ () => rows(board.tiles).map((r) => ({
45761
+ x: num(r.x),
45762
+ y: num(r.y),
45763
+ terrain: r.terrain == null ? void 0 : str(r.terrain),
45764
+ terrainSprite: r.terrainSprite == null ? void 0 : str(r.terrainSprite),
45765
+ passable: r.passable !== false
45766
+ })),
45767
+ [board.tiles]
45768
+ );
45769
+ const tiles = propTiles ?? (entityTiles.length >= DUNGEON_GRID_W * DUNGEON_GRID_H ? entityTiles : buildDefaultDungeonTiles(stairsX, stairsY, assetManifest));
45770
+ const entityEnemies = useMemo(
45771
+ () => rows(board.enemies).map((r) => ({
45772
+ id: str(r.id),
45773
+ x: num(r.x),
45774
+ y: num(r.y),
45775
+ hp: num(r.hp),
45776
+ attack: num(r.attack)
45777
+ })),
45778
+ [board.enemies]
45779
+ );
45780
+ const enemies = propEnemies ?? (entityEnemies.length > 0 ? entityEnemies : DEFAULT_ENEMIES);
45781
+ const entityItems = useMemo(
45782
+ () => rows(board.items).map((r) => ({
45783
+ id: str(r.id),
45784
+ x: num(r.x),
45785
+ y: num(r.y),
45786
+ kind: str(r.kind) || "health_potion"
45787
+ })),
45788
+ [board.items]
45789
+ );
45790
+ const items = propItems ?? (entityItems.length > 0 ? entityItems : DEFAULT_ITEMS);
45791
+ const player = propPlayer ?? (board.player == null ? { x: 1, y: 1 } : vec2(board.player));
44981
45792
  const playerHp = propPlayerHp ?? num(board.playerHp, 10);
44982
45793
  const playerMaxHp = propPlayerMaxHp ?? num(board.playerMaxHp, 10);
44983
45794
  const playerAttack = propPlayerAttack ?? num(board.playerAttack, 3);
44984
45795
  const depth = propDepth ?? num(board.depth, 1);
44985
45796
  const result = propResult ?? (str(board.result) || "none");
44986
45797
  const phase = propPhase ?? (str(board.phase) || "player_turn");
44987
- propStairsX ?? num(board.stairsX, 14);
44988
- propStairsY ?? num(board.stairsY, 14);
44989
45798
  const eventBus = useEventBus();
44990
45799
  const { t } = useTranslate();
44991
45800
  const [hoveredTile, setHoveredTile] = useState(null);
@@ -45032,7 +45841,7 @@ function RoguelikeBoard({
45032
45841
  health: playerHp,
45033
45842
  maxHealth: playerMaxHp,
45034
45843
  unitType: "player",
45035
- sprite: `${CDN4}/isometric-dungeon/Characters/Male/Male_0_Idle0.png`
45844
+ sprite: resolveManifestUrl3(assetManifest, assetManifest?.units?.player)
45036
45845
  },
45037
45846
  ...liveEnemies.map((e) => ({
45038
45847
  id: e.id,
@@ -45042,19 +45851,19 @@ function RoguelikeBoard({
45042
45851
  health: e.hp,
45043
45852
  maxHealth: 6,
45044
45853
  unitType: "enemy",
45045
- sprite: `${CDN4}/sprite-sheets/shadow-legion-sprite-sheet-sw.png`
45854
+ sprite: resolveManifestUrl3(assetManifest, assetManifest?.units?.enemy)
45046
45855
  }))
45047
45856
  ];
45048
- }, [player, enemies, playerHp, playerMaxHp, t]);
45857
+ }, [player, enemies, playerHp, playerMaxHp, assetManifest, t]);
45049
45858
  const isoFeatures = useMemo(
45050
45859
  () => items.map((it) => ({
45051
45860
  id: it.id,
45052
45861
  x: it.x,
45053
45862
  y: it.y,
45054
45863
  type: it.kind,
45055
- sprite: FEATURE_SPRITE[it.kind] ?? FEATURE_SPRITE.health_potion
45864
+ sprite: resolveManifestUrl3(assetManifest, assetManifest?.features?.[it.kind])
45056
45865
  })),
45057
- [items]
45866
+ [items, assetManifest]
45058
45867
  );
45059
45868
  const validMoves = useMemo(() => {
45060
45869
  if (result !== "none" || phase !== "player_turn") return [];
@@ -45113,8 +45922,8 @@ function RoguelikeBoard({
45113
45922
  onTileHover: (x, y) => setHoveredTile({ x, y }),
45114
45923
  onTileLeave: () => setHoveredTile(null),
45115
45924
  scale,
45116
- assetBaseUrl: propAssetManifest?.baseUrl ?? CDN4,
45117
- assetManifest: propAssetManifest,
45925
+ assetBaseUrl: assetManifest?.baseUrl,
45926
+ assetManifest,
45118
45927
  unitScale,
45119
45928
  spriteHeightRatio,
45120
45929
  spriteMaxWidthRatio
@@ -45161,7 +45970,7 @@ function RoguelikeBoard({
45161
45970
  ] }) })
45162
45971
  ] });
45163
45972
  }
45164
- var CDN4, DUNGEON_GRID_W, DUNGEON_GRID_H, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
45973
+ var DUNGEON_GRID_W, DUNGEON_GRID_H, DEFAULT_ENEMIES, DEFAULT_ITEMS;
45165
45974
  var init_RoguelikeBoard = __esm({
45166
45975
  "components/game/organisms/RoguelikeBoard.tsx"() {
45167
45976
  "use client";
@@ -45173,10 +45982,8 @@ var init_RoguelikeBoard = __esm({
45173
45982
  init_Stack();
45174
45983
  init_IsometricCanvas();
45175
45984
  init_boardEntity();
45176
- CDN4 = "https://almadar-kflow-assets.web.app/shared";
45177
45985
  DUNGEON_GRID_W = 16;
45178
45986
  DUNGEON_GRID_H = 16;
45179
- buildDefaultDungeonTiles(14, 14);
45180
45987
  DEFAULT_ENEMIES = [
45181
45988
  { id: "e1", x: 5, y: 2, hp: 5, attack: 2 },
45182
45989
  { id: "e2", x: 9, y: 6, hp: 4, attack: 1 },
@@ -45188,11 +45995,6 @@ var init_RoguelikeBoard = __esm({
45188
45995
  { id: "i2", x: 7, y: 9, kind: "sword" },
45189
45996
  { id: "i3", x: 11, y: 5, kind: "shield" }
45190
45997
  ];
45191
- FEATURE_SPRITE = {
45192
- health_potion: `${CDN4}/isometric-dungeon/Isometric/chestClosed_E.png`,
45193
- sword: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`,
45194
- shield: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`
45195
- };
45196
45998
  RoguelikeBoard.displayName = "RoguelikeBoard";
45197
45999
  }
45198
46000
  });
@@ -45230,40 +46032,40 @@ function RoguelikeTemplate({
45230
46032
  }
45231
46033
  );
45232
46034
  }
45233
- var CDN5, DEFAULT_ROGUELIKE_TILES, DEFAULT_ROGUELIKE_MANIFEST;
46035
+ var CDN4, DEFAULT_ROGUELIKE_TILES, DEFAULT_ROGUELIKE_MANIFEST;
45234
46036
  var init_RoguelikeTemplate = __esm({
45235
46037
  "components/game/templates/RoguelikeTemplate.tsx"() {
45236
46038
  init_RoguelikeBoard();
45237
- CDN5 = "https://almadar-kflow-assets.web.app/shared";
46039
+ CDN4 = "https://almadar-kflow-assets.web.app/shared";
45238
46040
  DEFAULT_ROGUELIKE_TILES = [
45239
- { x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45240
- { x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45241
- { x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45242
- { x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45243
- { x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45244
- { x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45245
- { x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
45246
- { x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
45247
- { x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
45248
- { x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45249
- { x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45250
- { x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
45251
- { x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
45252
- { x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
45253
- { x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45254
- { x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45255
- { x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
45256
- { x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
45257
- { x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stairs_E.png` },
45258
- { x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45259
- { x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45260
- { x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45261
- { x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45262
- { x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45263
- { x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` }
46041
+ { x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46042
+ { x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46043
+ { x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46044
+ { x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46045
+ { x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46046
+ { x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46047
+ { x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
46048
+ { x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
46049
+ { x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
46050
+ { x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46051
+ { x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46052
+ { x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
46053
+ { x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
46054
+ { x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
46055
+ { x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46056
+ { x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46057
+ { x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
46058
+ { x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
46059
+ { x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` },
46060
+ { x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46061
+ { x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46062
+ { x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46063
+ { x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46064
+ { x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
46065
+ { x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` }
45264
46066
  ];
45265
46067
  DEFAULT_ROGUELIKE_MANIFEST = {
45266
- baseUrl: CDN5,
46068
+ baseUrl: CDN4,
45267
46069
  terrains: {
45268
46070
  floor: "/isometric-dungeon/Isometric/dirt_E.png",
45269
46071
  wall: "/isometric-dungeon/Isometric/stoneInset_E.png",
@@ -45370,7 +46172,7 @@ var init_debugRegistry = __esm({
45370
46172
  }
45371
46173
  });
45372
46174
  function useDebugData() {
45373
- const [data, setData] = React82.useState(() => ({
46175
+ const [data, setData] = React85.useState(() => ({
45374
46176
  traits: [],
45375
46177
  ticks: [],
45376
46178
  guards: [],
@@ -45384,7 +46186,7 @@ function useDebugData() {
45384
46186
  },
45385
46187
  lastUpdate: Date.now()
45386
46188
  }));
45387
- React82.useEffect(() => {
46189
+ React85.useEffect(() => {
45388
46190
  const updateData = () => {
45389
46191
  setData({
45390
46192
  traits: getAllTraits(),
@@ -45493,12 +46295,12 @@ function layoutGraph(states, transitions, initialState, width, height) {
45493
46295
  return positions;
45494
46296
  }
45495
46297
  function WalkMinimap() {
45496
- const [walkStep, setWalkStep] = React82.useState(null);
45497
- const [traits2, setTraits] = React82.useState([]);
45498
- const [coveredEdges, setCoveredEdges] = React82.useState([]);
45499
- const [completedTraits, setCompletedTraits] = React82.useState(/* @__PURE__ */ new Set());
45500
- const prevTraitRef = React82.useRef(null);
45501
- React82.useEffect(() => {
46298
+ const [walkStep, setWalkStep] = React85.useState(null);
46299
+ const [traits2, setTraits] = React85.useState([]);
46300
+ const [coveredEdges, setCoveredEdges] = React85.useState([]);
46301
+ const [completedTraits, setCompletedTraits] = React85.useState(/* @__PURE__ */ new Set());
46302
+ const prevTraitRef = React85.useRef(null);
46303
+ React85.useEffect(() => {
45502
46304
  const interval = setInterval(() => {
45503
46305
  const w = window;
45504
46306
  const step = w.__orbitalWalkStep;
@@ -45934,15 +46736,15 @@ var init_EntitiesTab = __esm({
45934
46736
  });
45935
46737
  function EventFlowTab({ events: events2 }) {
45936
46738
  const { t } = useTranslate();
45937
- const [filter, setFilter] = React82.useState("all");
45938
- const containerRef = React82.useRef(null);
45939
- const [autoScroll, setAutoScroll] = React82.useState(true);
45940
- React82.useEffect(() => {
46739
+ const [filter, setFilter] = React85.useState("all");
46740
+ const containerRef = React85.useRef(null);
46741
+ const [autoScroll, setAutoScroll] = React85.useState(true);
46742
+ React85.useEffect(() => {
45941
46743
  if (autoScroll && containerRef.current) {
45942
46744
  containerRef.current.scrollTop = containerRef.current.scrollHeight;
45943
46745
  }
45944
46746
  }, [events2.length, autoScroll]);
45945
- const filteredEvents = React82.useMemo(() => {
46747
+ const filteredEvents = React85.useMemo(() => {
45946
46748
  if (filter === "all") return events2;
45947
46749
  return events2.filter((e) => e.type === filter);
45948
46750
  }, [events2, filter]);
@@ -46058,7 +46860,7 @@ var init_EventFlowTab = __esm({
46058
46860
  });
46059
46861
  function GuardsPanel({ guards }) {
46060
46862
  const { t } = useTranslate();
46061
- const [filter, setFilter] = React82.useState("all");
46863
+ const [filter, setFilter] = React85.useState("all");
46062
46864
  if (guards.length === 0) {
46063
46865
  return /* @__PURE__ */ jsx(
46064
46866
  EmptyState,
@@ -46071,7 +46873,7 @@ function GuardsPanel({ guards }) {
46071
46873
  }
46072
46874
  const passedCount = guards.filter((g) => g.result).length;
46073
46875
  const failedCount = guards.length - passedCount;
46074
- const filteredGuards = React82.useMemo(() => {
46876
+ const filteredGuards = React85.useMemo(() => {
46075
46877
  if (filter === "all") return guards;
46076
46878
  if (filter === "passed") return guards.filter((g) => g.result);
46077
46879
  return guards.filter((g) => !g.result);
@@ -46234,10 +47036,10 @@ function EffectBadge({ effect }) {
46234
47036
  }
46235
47037
  function TransitionTimeline({ transitions }) {
46236
47038
  const { t } = useTranslate();
46237
- const containerRef = React82.useRef(null);
46238
- const [autoScroll, setAutoScroll] = React82.useState(true);
46239
- const [expandedId, setExpandedId] = React82.useState(null);
46240
- React82.useEffect(() => {
47039
+ const containerRef = React85.useRef(null);
47040
+ const [autoScroll, setAutoScroll] = React85.useState(true);
47041
+ const [expandedId, setExpandedId] = React85.useState(null);
47042
+ React85.useEffect(() => {
46241
47043
  if (autoScroll && containerRef.current) {
46242
47044
  containerRef.current.scrollTop = containerRef.current.scrollHeight;
46243
47045
  }
@@ -46517,9 +47319,9 @@ function getAllEvents(traits2) {
46517
47319
  function EventDispatcherTab({ traits: traits2, schema }) {
46518
47320
  const eventBus = useEventBus();
46519
47321
  const { t } = useTranslate();
46520
- const [log13, setLog] = React82.useState([]);
46521
- const prevStatesRef = React82.useRef(/* @__PURE__ */ new Map());
46522
- React82.useEffect(() => {
47322
+ const [log13, setLog] = React85.useState([]);
47323
+ const prevStatesRef = React85.useRef(/* @__PURE__ */ new Map());
47324
+ React85.useEffect(() => {
46523
47325
  for (const trait of traits2) {
46524
47326
  const prev = prevStatesRef.current.get(trait.id);
46525
47327
  if (prev && prev !== trait.currentState) {
@@ -46688,10 +47490,10 @@ function VerifyModePanel({
46688
47490
  localCount
46689
47491
  }) {
46690
47492
  const { t } = useTranslate();
46691
- const [expanded, setExpanded] = React82.useState(true);
46692
- const scrollRef = React82.useRef(null);
46693
- const prevCountRef = React82.useRef(0);
46694
- React82.useEffect(() => {
47493
+ const [expanded, setExpanded] = React85.useState(true);
47494
+ const scrollRef = React85.useRef(null);
47495
+ const prevCountRef = React85.useRef(0);
47496
+ React85.useEffect(() => {
46695
47497
  if (expanded && transitions.length > prevCountRef.current && scrollRef.current) {
46696
47498
  scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
46697
47499
  }
@@ -46748,10 +47550,10 @@ function RuntimeDebugger({
46748
47550
  schema
46749
47551
  }) {
46750
47552
  const { t } = useTranslate();
46751
- const [isCollapsed, setIsCollapsed] = React82.useState(mode === "verify" ? true : defaultCollapsed);
46752
- const [isVisible, setIsVisible] = React82.useState(mode === "inline" || mode === "verify" || isDebugEnabled2());
47553
+ const [isCollapsed, setIsCollapsed] = React85.useState(mode === "verify" ? true : defaultCollapsed);
47554
+ const [isVisible, setIsVisible] = React85.useState(mode === "inline" || mode === "verify" || isDebugEnabled2());
46753
47555
  const debugData = useDebugData();
46754
- React82.useEffect(() => {
47556
+ React85.useEffect(() => {
46755
47557
  if (mode === "inline") return;
46756
47558
  return onDebugToggle((enabled) => {
46757
47559
  setIsVisible(enabled);
@@ -46760,7 +47562,7 @@ function RuntimeDebugger({
46760
47562
  }
46761
47563
  });
46762
47564
  }, [mode]);
46763
- React82.useEffect(() => {
47565
+ React85.useEffect(() => {
46764
47566
  if (mode === "inline") return;
46765
47567
  const handleKeyDown = (e) => {
46766
47568
  if (e.key === "`" && isVisible) {
@@ -47320,7 +48122,7 @@ function SequenceBar({
47320
48122
  onSlotRemove(index);
47321
48123
  }, [onSlotRemove, playing]);
47322
48124
  const paddedSlots = Array.from({ length: maxSlots }, (_, i) => slots[i]);
47323
- return /* @__PURE__ */ jsx(HStack, { className: cn("items-center", className), gap: "sm", children: paddedSlots.map((slot, i) => /* @__PURE__ */ jsxs(React82__default.Fragment, { children: [
48125
+ return /* @__PURE__ */ jsx(HStack, { className: cn("items-center", className), gap: "sm", children: paddedSlots.map((slot, i) => /* @__PURE__ */ jsxs(React85__default.Fragment, { children: [
47324
48126
  i > 0 && /* @__PURE__ */ jsx(
47325
48127
  Typography,
47326
48128
  {
@@ -48209,7 +49011,7 @@ var init_StatCard = __esm({
48209
49011
  const labelToUse = propLabel ?? propTitle;
48210
49012
  const eventBus = useEventBus();
48211
49013
  const { t } = useTranslate();
48212
- const handleActionClick = React82__default.useCallback(() => {
49014
+ const handleActionClick = React85__default.useCallback(() => {
48213
49015
  if (action?.event) {
48214
49016
  eventBus.emit(`UI:${action.event}`, {});
48215
49017
  }
@@ -48220,7 +49022,7 @@ var init_StatCard = __esm({
48220
49022
  const data = Array.isArray(entity) ? entity : entity ? [entity] : [];
48221
49023
  const isLoading = externalLoading ?? false;
48222
49024
  const error = externalError;
48223
- const computeMetricValue = React82__default.useCallback(
49025
+ const computeMetricValue = React85__default.useCallback(
48224
49026
  (metric, items) => {
48225
49027
  if (metric.value !== void 0) {
48226
49028
  return metric.value;
@@ -48259,7 +49061,7 @@ var init_StatCard = __esm({
48259
49061
  },
48260
49062
  []
48261
49063
  );
48262
- const schemaStats = React82__default.useMemo(() => {
49064
+ const schemaStats = React85__default.useMemo(() => {
48263
49065
  if (!metrics || metrics.length === 0) return null;
48264
49066
  return metrics.map((metric) => ({
48265
49067
  label: metric.label,
@@ -48267,7 +49069,7 @@ var init_StatCard = __esm({
48267
49069
  format: metric.format
48268
49070
  }));
48269
49071
  }, [metrics, data, computeMetricValue]);
48270
- const calculatedTrend = React82__default.useMemo(() => {
49072
+ const calculatedTrend = React85__default.useMemo(() => {
48271
49073
  if (manualTrend !== void 0) return manualTrend;
48272
49074
  if (previousValue === void 0 || currentValue === void 0)
48273
49075
  return void 0;
@@ -49233,7 +50035,7 @@ var init_Timeline = __esm({
49233
50035
  }) => {
49234
50036
  const { t } = useTranslate();
49235
50037
  const entityData = Array.isArray(entity) ? entity : [];
49236
- const items = React82__default.useMemo(() => {
50038
+ const items = React85__default.useMemo(() => {
49237
50039
  if (propItems) return propItems;
49238
50040
  if (entityData.length === 0) return [];
49239
50041
  return entityData.map((record, idx) => {
@@ -49340,7 +50142,7 @@ var init_Timeline = __esm({
49340
50142
  }
49341
50143
  });
49342
50144
  function extractToastProps(children) {
49343
- if (!React82__default.isValidElement(children)) {
50145
+ if (!React85__default.isValidElement(children)) {
49344
50146
  if (typeof children === "string") {
49345
50147
  return { message: children };
49346
50148
  }
@@ -49378,7 +50180,7 @@ var init_ToastSlot = __esm({
49378
50180
  eventBus.emit("UI:CLOSE");
49379
50181
  };
49380
50182
  if (!isVisible) return null;
49381
- const isCustomContent = React82__default.isValidElement(children) && !message;
50183
+ const isCustomContent = React85__default.isValidElement(children) && !message;
49382
50184
  return /* @__PURE__ */ jsx(Box, { className: "fixed bottom-4 right-4 z-50", children: isCustomContent ? children : /* @__PURE__ */ jsx(
49383
50185
  Toast,
49384
50186
  {
@@ -49394,82 +50196,442 @@ var init_ToastSlot = __esm({
49394
50196
  ToastSlot.displayName = "ToastSlot";
49395
50197
  }
49396
50198
  });
50199
+ function resolveManifestUrl4(manifest, relative) {
50200
+ if (relative == null || relative === "") return void 0;
50201
+ if (/^https?:\/\//.test(relative)) return relative;
50202
+ const base = manifest?.baseUrl;
50203
+ if (base == null) return relative;
50204
+ const cleanBase = base.replace(/\/$/, "");
50205
+ const cleanRel = relative.replace(/^\//, "");
50206
+ return `${cleanBase}/${cleanRel}`;
50207
+ }
50208
+ function readPlayer(v) {
50209
+ if (v == null || typeof v !== "object") return void 0;
50210
+ const o = v;
50211
+ return { x: num(o.x), y: num(o.y) };
50212
+ }
50213
+ function TopDownShooterBoard({
50214
+ entity,
50215
+ player: propPlayer,
50216
+ enemies: propEnemies,
50217
+ projectiles: propProjectiles,
50218
+ score: propScore,
50219
+ wave: propWave,
50220
+ lives: propLives,
50221
+ result: propResult,
50222
+ assetManifest: propAssetManifest,
50223
+ width = DEFAULT_WIDTH,
50224
+ height = DEFAULT_HEIGHT,
50225
+ spriteSize = 32,
50226
+ moveEvent = "MOVE",
50227
+ fireEvent = "FIRE",
50228
+ playAgainEvent = "PLAY_AGAIN",
50229
+ gameEndEvent = "GAME_END",
50230
+ className
50231
+ }) {
50232
+ const board = boardEntity(entity) ?? {};
50233
+ const eventBus = useEventBus();
50234
+ const { t } = useTranslate();
50235
+ const assetManifest = propAssetManifest ?? board.assetManifest;
50236
+ const player = propPlayer ?? readPlayer(board.player) ?? { x: width / 2, y: height - spriteSize };
50237
+ const enemies = useMemo(
50238
+ () => propEnemies ?? rows(board.enemies).map((r) => ({
50239
+ id: str(r.id),
50240
+ x: num(r.x),
50241
+ y: num(r.y),
50242
+ hp: num(r.hp, 1)
50243
+ })),
50244
+ [propEnemies, board.enemies]
50245
+ );
50246
+ const projectiles = useMemo(
50247
+ () => propProjectiles ?? rows(board.projectiles).map((r) => ({
50248
+ id: str(r.id),
50249
+ x: num(r.x),
50250
+ y: num(r.y),
50251
+ vx: num(r.vx),
50252
+ vy: num(r.vy)
50253
+ })),
50254
+ [propProjectiles, board.projectiles]
50255
+ );
50256
+ const score = propScore ?? num(board.score, 0);
50257
+ const wave = propWave ?? num(board.wave, 1);
50258
+ const lives = propLives ?? num(board.lives, 3);
50259
+ const result = propResult ?? (str(board.result) || "none");
50260
+ const playerSprite = resolveManifestUrl4(assetManifest, assetManifest?.units?.player);
50261
+ const enemySprite = resolveManifestUrl4(assetManifest, assetManifest?.units?.enemy);
50262
+ const projectileSprite = resolveManifestUrl4(assetManifest, assetManifest?.features?.projectile);
50263
+ const backgroundSprite = resolveManifestUrl4(assetManifest, assetManifest?.background);
50264
+ const spriteUrls = useMemo(
50265
+ () => [playerSprite, enemySprite, projectileSprite].filter((u) => u != null),
50266
+ [playerSprite, enemySprite, projectileSprite]
50267
+ );
50268
+ const { getImage } = useImageCache(spriteUrls);
50269
+ const sceneRef = useRef({ player, enemies, projectiles, playerSprite, enemySprite, projectileSprite });
50270
+ sceneRef.current = { player, enemies, projectiles, playerSprite, enemySprite, projectileSprite };
50271
+ boardLog2.debug("shooter-resolve", {
50272
+ player: { x: player.x, y: player.y },
50273
+ enemyCount: enemies.length,
50274
+ projectileCount: projectiles.length,
50275
+ wave,
50276
+ lives,
50277
+ result,
50278
+ hasPlayerSprite: playerSprite != null
50279
+ });
50280
+ const onDraw = useCallback(
50281
+ (ctx, _frame) => {
50282
+ const scene = sceneRef.current;
50283
+ ctx.fillStyle = "#10141c";
50284
+ ctx.fillRect(0, 0, width, height);
50285
+ const half = spriteSize / 2;
50286
+ for (const p2 of scene.projectiles) {
50287
+ const img = scene.projectileSprite ? getImage(scene.projectileSprite) : void 0;
50288
+ if (img) {
50289
+ ctx.drawImage(img, p2.x - half / 2, p2.y - half / 2, spriteSize / 2, spriteSize / 2);
50290
+ } else {
50291
+ ctx.fillStyle = "#ffd34d";
50292
+ ctx.beginPath();
50293
+ ctx.arc(p2.x, p2.y, spriteSize / 6, 0, Math.PI * 2);
50294
+ ctx.fill();
50295
+ }
50296
+ }
50297
+ for (const e of scene.enemies) {
50298
+ const img = scene.enemySprite ? getImage(scene.enemySprite) : void 0;
50299
+ if (img) {
50300
+ ctx.drawImage(img, e.x - half, e.y - half, spriteSize, spriteSize);
50301
+ } else {
50302
+ ctx.fillStyle = "#e0524d";
50303
+ ctx.fillRect(e.x - half, e.y - half, spriteSize, spriteSize);
50304
+ }
50305
+ }
50306
+ const pImg = scene.playerSprite ? getImage(scene.playerSprite) : void 0;
50307
+ if (pImg) {
50308
+ ctx.drawImage(pImg, scene.player.x - half, scene.player.y - half, spriteSize, spriteSize);
50309
+ } else {
50310
+ ctx.fillStyle = "#4da3ff";
50311
+ ctx.beginPath();
50312
+ ctx.moveTo(scene.player.x, scene.player.y - half);
50313
+ ctx.lineTo(scene.player.x + half, scene.player.y + half);
50314
+ ctx.lineTo(scene.player.x - half, scene.player.y + half);
50315
+ ctx.closePath();
50316
+ ctx.fill();
50317
+ }
50318
+ },
50319
+ [getImage, width, height, spriteSize]
50320
+ );
50321
+ const moveEventRef = useRef(moveEvent);
50322
+ moveEventRef.current = moveEvent;
50323
+ const fireEventRef = useRef(fireEvent);
50324
+ fireEventRef.current = fireEvent;
50325
+ const resultRef = useRef(result);
50326
+ resultRef.current = result;
50327
+ const playerRef = useRef(player);
50328
+ playerRef.current = player;
50329
+ useEffect(() => {
50330
+ function onKeyDown(e) {
50331
+ if (resultRef.current !== "none") return;
50332
+ let dx = 0;
50333
+ let dy = 0;
50334
+ switch (e.key) {
50335
+ case "ArrowUp":
50336
+ case "w":
50337
+ dy = -1;
50338
+ break;
50339
+ case "ArrowDown":
50340
+ case "s":
50341
+ dy = 1;
50342
+ break;
50343
+ case "ArrowLeft":
50344
+ case "a":
50345
+ dx = -1;
50346
+ break;
50347
+ case "ArrowRight":
50348
+ case "d":
50349
+ dx = 1;
50350
+ break;
50351
+ case " ": {
50352
+ e.preventDefault();
50353
+ const evt = fireEventRef.current;
50354
+ if (evt) {
50355
+ eventBus.emit(`UI:${evt}`, { tx: playerRef.current.x, ty: 0 });
50356
+ }
50357
+ return;
50358
+ }
50359
+ default:
50360
+ return;
50361
+ }
50362
+ e.preventDefault();
50363
+ const moveEvt = moveEventRef.current;
50364
+ if (moveEvt) {
50365
+ eventBus.emit(`UI:${moveEvt}`, { dx, dy });
50366
+ }
50367
+ }
50368
+ window.addEventListener("keydown", onKeyDown);
50369
+ return () => window.removeEventListener("keydown", onKeyDown);
50370
+ }, [eventBus]);
50371
+ const handleFireClick = useCallback(
50372
+ (e) => {
50373
+ if (result !== "none") return;
50374
+ const evt = fireEventRef.current;
50375
+ if (!evt) return;
50376
+ const rect = e.currentTarget.getBoundingClientRect();
50377
+ const tx = e.clientX - rect.left;
50378
+ const ty = e.clientY - rect.top;
50379
+ eventBus.emit(`UI:${evt}`, { tx, ty });
50380
+ },
50381
+ [result, eventBus]
50382
+ );
50383
+ const handlePlayAgain = useCallback(() => {
50384
+ if (playAgainEvent) {
50385
+ eventBus.emit(`UI:${playAgainEvent}`, {});
50386
+ }
50387
+ }, [playAgainEvent, eventBus]);
50388
+ const gameEndEmittedRef = useRef(false);
50389
+ useEffect(() => {
50390
+ if ((result === "won" || result === "lost") && gameEndEvent) {
50391
+ if (!gameEndEmittedRef.current) {
50392
+ gameEndEmittedRef.current = true;
50393
+ eventBus.emit(`UI:${gameEndEvent}`, { result });
50394
+ }
50395
+ } else {
50396
+ gameEndEmittedRef.current = false;
50397
+ }
50398
+ }, [result, gameEndEvent, eventBus]);
50399
+ const isGameOver = result === "won" || result === "lost";
50400
+ return /* @__PURE__ */ jsxs(VStack, { className: cn("top-down-shooter-board relative bg-background", className), gap: "none", children: [
50401
+ /* @__PURE__ */ jsxs(HStack, { className: "px-4 py-2 border-b border-border bg-surface", gap: "lg", align: "center", children: [
50402
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
50403
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("shooter.score") ?? "Score" }),
50404
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", className: "text-warning", children: score })
50405
+ ] }),
50406
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
50407
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("shooter.wave") ?? "Wave" }),
50408
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", children: wave })
50409
+ ] }),
50410
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
50411
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("shooter.lives") ?? "Lives" }),
50412
+ /* @__PURE__ */ jsx(
50413
+ Typography,
50414
+ {
50415
+ variant: "body2",
50416
+ weight: "bold",
50417
+ className: lives <= 1 ? "text-error" : "text-success",
50418
+ children: lives
50419
+ }
50420
+ )
50421
+ ] })
50422
+ ] }),
50423
+ /* @__PURE__ */ jsxs(Box, { className: "relative", onClick: handleFireClick, children: [
50424
+ /* @__PURE__ */ jsx(
50425
+ GameCanvas2D,
50426
+ {
50427
+ width,
50428
+ height,
50429
+ backgroundImage: backgroundSprite,
50430
+ assetBaseUrl: assetManifest?.baseUrl,
50431
+ onDraw
50432
+ }
50433
+ ),
50434
+ 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: [
50435
+ /* @__PURE__ */ jsx(
50436
+ Typography,
50437
+ {
50438
+ variant: "h2",
50439
+ className: cn(
50440
+ "text-4xl font-black tracking-widest uppercase",
50441
+ result === "won" ? "text-warning" : "text-error"
50442
+ ),
50443
+ children: result === "won" ? t("shooter.victory") ?? "Victory!" : t("shooter.defeat") ?? "Defeat!"
50444
+ }
50445
+ ),
50446
+ /* @__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." }),
50447
+ /* @__PURE__ */ jsx(
50448
+ Button,
50449
+ {
50450
+ variant: "primary",
50451
+ className: "px-8 py-3 font-semibold",
50452
+ onClick: handlePlayAgain,
50453
+ children: t("shooter.playAgain") ?? "Play Again"
50454
+ }
50455
+ )
50456
+ ] }) })
50457
+ ] })
50458
+ ] });
50459
+ }
50460
+ var boardLog2, DEFAULT_WIDTH, DEFAULT_HEIGHT;
50461
+ var init_TopDownShooterBoard = __esm({
50462
+ "components/game/organisms/TopDownShooterBoard.tsx"() {
50463
+ "use client";
50464
+ init_cn();
50465
+ init_useEventBus();
50466
+ init_Box();
50467
+ init_Button();
50468
+ init_Typography();
50469
+ init_Stack();
50470
+ init_GameCanvas2D();
50471
+ init_useImageCache();
50472
+ init_boardEntity();
50473
+ boardLog2 = createLogger("almadar:ui:game:top-down-shooter-board");
50474
+ DEFAULT_WIDTH = 640;
50475
+ DEFAULT_HEIGHT = 480;
50476
+ TopDownShooterBoard.displayName = "TopDownShooterBoard";
50477
+ }
50478
+ });
50479
+ function isEntityRow3(value) {
50480
+ return value != null && typeof value === "object" && !Array.isArray(value);
50481
+ }
50482
+ function TopDownShooterTemplate({
50483
+ entity,
50484
+ title = "Arena Shooter",
50485
+ player,
50486
+ enemies,
50487
+ projectiles,
50488
+ score,
50489
+ wave,
50490
+ lives,
50491
+ result,
50492
+ assetManifest,
50493
+ width,
50494
+ height,
50495
+ spriteSize,
50496
+ moveEvent,
50497
+ fireEvent,
50498
+ playAgainEvent,
50499
+ gameEndEvent,
50500
+ className
50501
+ }) {
50502
+ const resolved = isEntityRow3(entity) ? entity : void 0;
50503
+ return /* @__PURE__ */ jsxs(
50504
+ Box,
50505
+ {
50506
+ display: "flex",
50507
+ fullHeight: true,
50508
+ className: cn("top-down-shooter-template flex-col", className),
50509
+ children: [
50510
+ /* @__PURE__ */ jsx(
50511
+ HStack,
50512
+ {
50513
+ gap: "sm",
50514
+ align: "center",
50515
+ className: "px-4 py-3 border-b-2 border-border bg-surface shrink-0",
50516
+ children: /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title })
50517
+ }
50518
+ ),
50519
+ /* @__PURE__ */ jsx(Box, { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsx(
50520
+ TopDownShooterBoard,
50521
+ {
50522
+ entity: resolved,
50523
+ player,
50524
+ enemies,
50525
+ projectiles,
50526
+ score,
50527
+ wave,
50528
+ lives,
50529
+ result,
50530
+ assetManifest,
50531
+ width,
50532
+ height,
50533
+ spriteSize,
50534
+ moveEvent,
50535
+ fireEvent,
50536
+ playAgainEvent,
50537
+ gameEndEvent,
50538
+ className: "h-full"
50539
+ }
50540
+ ) })
50541
+ ]
50542
+ }
50543
+ );
50544
+ }
50545
+ var init_TopDownShooterTemplate = __esm({
50546
+ "components/game/templates/TopDownShooterTemplate.tsx"() {
50547
+ init_cn();
50548
+ init_Box();
50549
+ init_Stack();
50550
+ init_Typography();
50551
+ init_TopDownShooterBoard();
50552
+ TopDownShooterTemplate.displayName = "TopDownShooterTemplate";
50553
+ }
50554
+ });
50555
+ function resolveManifestUrl5(manifest, relative) {
50556
+ if (relative == null) return void 0;
50557
+ if (/^https?:\/\//.test(relative)) return relative;
50558
+ const base = manifest?.baseUrl;
50559
+ if (base == null) return relative;
50560
+ const cleanBase = base.replace(/\/$/, "");
50561
+ const cleanRel = relative.replace(/^\//, "");
50562
+ return `${cleanBase}/${cleanRel}`;
50563
+ }
49397
50564
  function buildDefaultTDTiles() {
49398
50565
  const pathSet = new Set(DEFAULT_TD_PATH.map((p2) => `${p2.x},${p2.y}`));
49399
50566
  const tiles = [];
49400
50567
  for (let y = 0; y < TD_GRID_H; y++) {
49401
50568
  for (let x = 0; x < TD_GRID_W; x++) {
49402
50569
  if (pathSet.has(`${x},${y}`)) {
49403
- tiles.push({ x, y, terrain: "path", passable: false, terrainSprite: TERRAIN_SPRITES.path });
50570
+ tiles.push({ x, y, terrain: "path", passable: false });
49404
50571
  } else {
49405
50572
  const variant = (x * 3 + y * 5 + (x ^ y)) % 3;
49406
- const terrainKey = ["ground", "grass", "dirt"][variant];
49407
- tiles.push({ x, y, terrain: terrainKey, passable: true, terrainSprite: TERRAIN_SPRITES[terrainKey] ?? TERRAIN_SPRITES.ground });
50573
+ const terrainKey = ["ground", "grass", "stone"][variant];
50574
+ tiles.push({ x, y, terrain: terrainKey, passable: true });
49408
50575
  }
49409
50576
  }
49410
50577
  }
49411
50578
  return tiles;
49412
50579
  }
49413
- function tilesToIso(tiles) {
49414
- return tiles.map((t) => ({
49415
- x: t.x,
49416
- y: t.y,
49417
- terrain: t.terrain,
49418
- terrainSprite: t.terrainSprite ?? TERRAIN_SPRITES[t.terrain ?? "ground"],
49419
- passable: t.passable
49420
- }));
49421
- }
49422
- function towersToUnits(towers) {
49423
- return towers.map((t) => ({
49424
- id: t.id,
49425
- position: { x: t.x, y: t.y },
49426
- name: "Tower",
49427
- team: "player",
49428
- sprite: TOWER_SPRITE,
49429
- unitType: "guardian",
49430
- health: 1,
49431
- maxHealth: 1
49432
- }));
50580
+ function tilesToIso2(tiles, manifest) {
50581
+ return tiles.map((t) => {
50582
+ const key = t.terrain ?? "ground";
50583
+ const sprite = t.terrainSprite ?? resolveManifestUrl5(manifest, manifest?.terrains?.[key]) ?? resolveManifestUrl5(manifest, manifest?.terrains?.ground);
50584
+ return {
50585
+ x: t.x,
50586
+ y: t.y,
50587
+ terrain: t.terrain,
50588
+ terrainSprite: sprite,
50589
+ passable: t.passable
50590
+ };
50591
+ });
49433
50592
  }
49434
- function creepsToUnits(creeps) {
50593
+ function creepsToUnits(creeps, manifest) {
50594
+ const sprite = resolveManifestUrl5(manifest, manifest?.units?.creep);
49435
50595
  return creeps.map((c) => ({
49436
50596
  id: c.id,
49437
50597
  position: { x: c.x, y: c.y },
49438
50598
  name: "Creep",
49439
50599
  team: "enemy",
49440
- sprite: CREEP_SPRITE,
49441
- unitType: "scrapper",
50600
+ sprite,
50601
+ unitType: "creep",
49442
50602
  health: c.hp,
49443
50603
  maxHealth: c.maxHp
49444
50604
  }));
49445
50605
  }
49446
- function pathToFeatures(path) {
49447
- return path.map((p2, i) => ({
49448
- id: `path-${i}`,
49449
- x: p2.x,
49450
- y: p2.y,
49451
- type: "path-marker",
49452
- sprite: `${CDN6}world-map/road_straight.png`
49453
- }));
49454
- }
49455
- function heroToUnit(hero) {
50606
+ function heroToUnit(hero, manifest) {
49456
50607
  return {
49457
50608
  id: hero.id,
49458
50609
  position: { x: hero.x, y: hero.y },
49459
50610
  name: "Hero",
49460
50611
  team: "player",
49461
- sprite: HERO_SPRITE,
49462
- unitType: "amir",
50612
+ sprite: resolveManifestUrl5(manifest, manifest?.units?.hero),
50613
+ unitType: "hero",
49463
50614
  health: 1,
49464
50615
  maxHealth: 1
49465
50616
  };
49466
50617
  }
50618
+ function towersToFeatures(towers, manifest) {
50619
+ const sprite = resolveManifestUrl5(manifest, manifest?.features?.tower);
50620
+ return towers.map((t) => ({
50621
+ id: t.id,
50622
+ x: t.x,
50623
+ y: t.y,
50624
+ type: "tower",
50625
+ sprite
50626
+ }));
50627
+ }
49467
50628
  function TowerDefenseBoard({
49468
50629
  entity,
49469
50630
  tiles: propTiles,
49470
50631
  path: propPath,
49471
50632
  towers: propTowers,
49472
50633
  creeps: propCreeps,
50634
+ assetManifest: propAssetManifest,
49473
50635
  hero: propHero,
49474
50636
  gold: propGold,
49475
50637
  lives: propLives,
@@ -49492,12 +50654,51 @@ function TowerDefenseBoard({
49492
50654
  const board = boardEntity(entity) ?? {};
49493
50655
  const eventBus = useEventBus();
49494
50656
  const { t } = useTranslate();
49495
- const rawTiles = propTiles ?? rows(board.tiles);
50657
+ const assetManifest = propAssetManifest ?? board.assetManifest;
50658
+ const entityTiles = useMemo(
50659
+ () => rows(board.tiles).map((r) => ({
50660
+ x: num(r.x),
50661
+ y: num(r.y),
50662
+ terrain: r.terrain == null ? void 0 : str(r.terrain),
50663
+ terrainSprite: r.terrainSprite == null ? void 0 : str(r.terrainSprite),
50664
+ passable: r.passable !== false
50665
+ })),
50666
+ [board.tiles]
50667
+ );
50668
+ const rawTiles = propTiles ?? entityTiles;
49496
50669
  const tiles = rawTiles.length >= TD_GRID_W * TD_GRID_H ? rawTiles : DEFAULT_TD_TILES;
49497
- const rawPath = propPath ?? rows(board.path);
50670
+ const entityPath = useMemo(
50671
+ () => rows(board.path).map((r) => ({ x: num(r.x), y: num(r.y) })),
50672
+ [board.path]
50673
+ );
50674
+ const rawPath = propPath ?? entityPath;
49498
50675
  const path = rawPath.length > 0 ? rawPath : DEFAULT_TD_PATH;
49499
- const towers = propTowers ?? rows(board.towers);
49500
- const creeps = propCreeps ?? rows(board.creeps);
50676
+ const entityTowers = useMemo(
50677
+ () => rows(board.towers).map((r) => ({
50678
+ id: str(r.id),
50679
+ x: num(r.x),
50680
+ y: num(r.y),
50681
+ range: num(r.range),
50682
+ damage: num(r.damage),
50683
+ cooldown: num(r.cooldown),
50684
+ lastFiredAt: r.lastFiredAt == null ? void 0 : num(r.lastFiredAt)
50685
+ })),
50686
+ [board.towers]
50687
+ );
50688
+ const towers = propTowers ?? entityTowers;
50689
+ const entityCreeps = useMemo(
50690
+ () => rows(board.creeps).map((r) => ({
50691
+ id: str(r.id),
50692
+ x: num(r.x),
50693
+ y: num(r.y),
50694
+ hp: num(r.hp),
50695
+ maxHp: num(r.maxHp),
50696
+ pathIndex: num(r.pathIndex),
50697
+ speed: num(r.speed)
50698
+ })),
50699
+ [board.creeps]
50700
+ );
50701
+ const creeps = propCreeps ?? entityCreeps;
49501
50702
  const hero = propHero ?? { id: "hero", x: 8, y: 8 };
49502
50703
  const gold = propGold ?? num(board.gold, 100);
49503
50704
  const lives = propLives ?? num(board.lives, 20);
@@ -49549,12 +50750,11 @@ function TowerDefenseBoard({
49549
50750
  (t2) => t2.passable !== false && !towerPositions.has(`${t2.x},${t2.y}`) && !pathPositions.has(`${t2.x},${t2.y}`)
49550
50751
  ).map((t2) => ({ x: t2.x, y: t2.y }));
49551
50752
  }, [tiles, towerPositions, pathPositions, result, gold, towerCost]);
49552
- const isoTiles = useMemo(() => tilesToIso(tiles), [tiles]);
49553
- const towerUnits = useMemo(() => towersToUnits(towers), [towers]);
49554
- const creepUnits = useMemo(() => creepsToUnits(creeps), [creeps]);
49555
- const heroUnit = useMemo(() => heroToUnit(hero), [hero]);
49556
- const isoUnits = useMemo(() => [...towerUnits, ...creepUnits, heroUnit], [towerUnits, creepUnits, heroUnit]);
49557
- const pathFeatures = useMemo(() => pathToFeatures(path), [path]);
50753
+ const isoTiles = useMemo(() => tilesToIso2(tiles, assetManifest), [tiles, assetManifest]);
50754
+ const creepUnits = useMemo(() => creepsToUnits(creeps, assetManifest), [creeps, assetManifest]);
50755
+ const heroUnit = useMemo(() => heroToUnit(hero, assetManifest), [hero, assetManifest]);
50756
+ const isoUnits = useMemo(() => [...creepUnits, heroUnit], [creepUnits, heroUnit]);
50757
+ const towerFeatures = useMemo(() => towersToFeatures(towers, assetManifest), [towers, assetManifest]);
49558
50758
  const handleTileClick = useCallback((x, y) => {
49559
50759
  if (result !== "none") return;
49560
50760
  if (pathPositions.has(`${x},${y}`)) return;
@@ -49613,7 +50813,9 @@ function TowerDefenseBoard({
49613
50813
  {
49614
50814
  tiles: isoTiles,
49615
50815
  units: isoUnits,
49616
- features: pathFeatures,
50816
+ features: towerFeatures,
50817
+ assetBaseUrl: assetManifest?.baseUrl,
50818
+ assetManifest,
49617
50819
  validMoves,
49618
50820
  hoveredTile,
49619
50821
  onTileClick: handleTileClick,
@@ -49652,7 +50854,7 @@ function TowerDefenseBoard({
49652
50854
  ] }) })
49653
50855
  ] });
49654
50856
  }
49655
- var CDN6, TD_GRID_W, TD_GRID_H, TERRAIN_SPRITES, DEFAULT_TD_PATH, DEFAULT_TD_TILES, TOWER_SPRITE, CREEP_SPRITE, HERO_SPRITE;
50857
+ var TD_GRID_W, TD_GRID_H, DEFAULT_TD_PATH, DEFAULT_TD_TILES;
49656
50858
  var init_TowerDefenseBoard = __esm({
49657
50859
  "components/game/organisms/TowerDefenseBoard.tsx"() {
49658
50860
  "use client";
@@ -49664,16 +50866,8 @@ var init_TowerDefenseBoard = __esm({
49664
50866
  init_Stack();
49665
50867
  init_IsometricCanvas();
49666
50868
  init_boardEntity();
49667
- CDN6 = "https://almadar-kflow-assets.web.app/shared/";
49668
50869
  TD_GRID_W = 16;
49669
50870
  TD_GRID_H = 16;
49670
- TERRAIN_SPRITES = {
49671
- ground: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_01.png`,
49672
- path: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_09.png`,
49673
- wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`,
49674
- grass: `${CDN6}isometric-dungeon/Isometric/dirt_E.png`,
49675
- dirt: `${CDN6}isometric-dungeon/Isometric/dirtTiles_E.png`
49676
- };
49677
50871
  DEFAULT_TD_PATH = [
49678
50872
  { x: 2, y: 0 },
49679
50873
  { x: 2, y: 1 },
@@ -49726,9 +50920,6 @@ var init_TowerDefenseBoard = __esm({
49726
50920
  { x: 13, y: 15 }
49727
50921
  ];
49728
50922
  DEFAULT_TD_TILES = buildDefaultTDTiles();
49729
- TOWER_SPRITE = `${CDN6}sprite-sheets/guardian-sprite-sheet-se.png`;
49730
- CREEP_SPRITE = `${CDN6}sprite-sheets/scrapper-sprite-sheet-se.png`;
49731
- HERO_SPRITE = `${CDN6}sprite-sheets/amir-sprite-sheet-se.png`;
49732
50923
  TowerDefenseBoard.displayName = "TowerDefenseBoard";
49733
50924
  }
49734
50925
  });
@@ -49996,6 +51187,214 @@ var init_UncontrolledBattleBoard = __esm({
49996
51187
  UncontrolledBattleBoard.displayName = "UncontrolledBattleBoard";
49997
51188
  }
49998
51189
  });
51190
+ function resolveManifestUrl6(manifest, relative) {
51191
+ if (relative == null) return void 0;
51192
+ if (/^https?:\/\//.test(relative)) return relative;
51193
+ const base = manifest?.baseUrl;
51194
+ if (base == null) return relative;
51195
+ const cleanBase = base.replace(/\/$/, "");
51196
+ const cleanRel = relative.replace(/^\//, "");
51197
+ return `${cleanBase}/${cleanRel}`;
51198
+ }
51199
+ function VisualNovelBoard({
51200
+ entity,
51201
+ nodes: propNodes,
51202
+ currentNodeId: propCurrentNodeId,
51203
+ assetManifest: propAssetManifest,
51204
+ typewriterSpeed = 30,
51205
+ portraitScale = 1,
51206
+ chooseEvent,
51207
+ advanceEvent,
51208
+ restartEvent,
51209
+ className
51210
+ }) {
51211
+ const board = boardEntity(entity) ?? {};
51212
+ const eventBus = useEventBus();
51213
+ const assetManifest = propAssetManifest ?? board.assetManifest;
51214
+ const entityNodes = useMemo(
51215
+ () => rows(board.nodes).map((r) => ({
51216
+ id: str(r.id),
51217
+ speaker: str(r.speaker),
51218
+ text: str(r.text),
51219
+ backgroundKey: r.backgroundKey == null ? void 0 : str(r.backgroundKey),
51220
+ portraitKey: r.portraitKey == null ? void 0 : str(r.portraitKey),
51221
+ choices: rows(r.choices).map((c) => ({
51222
+ label: str(c.label),
51223
+ nextId: str(c.nextId)
51224
+ }))
51225
+ })),
51226
+ [board.nodes]
51227
+ );
51228
+ const rawNodes = propNodes ?? entityNodes;
51229
+ const nodes = rawNodes.length > 0 ? rawNodes : DEFAULT_NODES;
51230
+ const currentNodeId = propCurrentNodeId ?? (str(board.currentNodeId) || nodes[0]?.id);
51231
+ const currentNode = nodes.find((n) => n.id === currentNodeId) ?? nodes[0];
51232
+ const backgroundImage = useMemo(
51233
+ () => resolveManifestUrl6(assetManifest, assetManifest?.backgrounds?.[currentNode?.backgroundKey ?? ""]),
51234
+ [assetManifest, currentNode?.backgroundKey]
51235
+ );
51236
+ const portraitUrl = useMemo(
51237
+ () => resolveManifestUrl6(assetManifest, assetManifest?.portraits?.[currentNode?.portraitKey ?? ""]),
51238
+ [assetManifest, currentNode?.portraitKey]
51239
+ );
51240
+ const dialogueChoices = useMemo(
51241
+ () => (currentNode?.choices ?? []).map((c) => ({ text: c.label, next: c.nextId })),
51242
+ [currentNode?.choices]
51243
+ );
51244
+ const handleChoice = useCallback(
51245
+ (choice) => {
51246
+ const index = dialogueChoices.findIndex((c) => c.next === choice.next && c.text === choice.text);
51247
+ if (chooseEvent) {
51248
+ eventBus.emit(`UI:${chooseEvent}`, { choiceIndex: index });
51249
+ }
51250
+ },
51251
+ [dialogueChoices, chooseEvent, eventBus]
51252
+ );
51253
+ const handleAdvance = useCallback(() => {
51254
+ if (advanceEvent) {
51255
+ eventBus.emit(`UI:${advanceEvent}`, {});
51256
+ }
51257
+ }, [advanceEvent, eventBus]);
51258
+ const handleRestart = useCallback(() => {
51259
+ if (restartEvent) {
51260
+ eventBus.emit(`UI:${restartEvent}`, {});
51261
+ }
51262
+ }, [restartEvent, eventBus]);
51263
+ if (!currentNode) {
51264
+ return /* @__PURE__ */ jsxs(VStack, { className: cn("visual-novel-board relative min-h-[600px] bg-background items-center justify-center", className), gap: "lg", children: [
51265
+ /* @__PURE__ */ jsx(Typography, { variant: "h3", color: "muted", children: "No dialogue nodes" }),
51266
+ /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleRestart, children: "Restart" })
51267
+ ] });
51268
+ }
51269
+ return /* @__PURE__ */ jsx(Box, { className: cn("visual-novel-board relative min-h-[600px] bg-background overflow-hidden", className), children: /* @__PURE__ */ jsx(
51270
+ DialogueBox,
51271
+ {
51272
+ dialogue: {
51273
+ speaker: currentNode.speaker,
51274
+ text: currentNode.text,
51275
+ choices: dialogueChoices
51276
+ },
51277
+ typewriterSpeed,
51278
+ position: "bottom",
51279
+ backgroundImage,
51280
+ portraitUrl,
51281
+ portraitScale,
51282
+ onChoice: handleChoice,
51283
+ onAdvance: handleAdvance
51284
+ }
51285
+ ) });
51286
+ }
51287
+ var DEFAULT_NODES;
51288
+ var init_VisualNovelBoard = __esm({
51289
+ "components/game/organisms/VisualNovelBoard.tsx"() {
51290
+ "use client";
51291
+ init_cn();
51292
+ init_useEventBus();
51293
+ init_Box();
51294
+ init_Typography();
51295
+ init_Stack();
51296
+ init_Button();
51297
+ init_DialogueBox();
51298
+ init_boardEntity();
51299
+ DEFAULT_NODES = [
51300
+ {
51301
+ id: "start",
51302
+ speaker: "Narrator",
51303
+ text: "The corridor stretches into shadow. A lone sentinel blocks your path.",
51304
+ backgroundKey: "corridor",
51305
+ portraitKey: "guide",
51306
+ choices: [
51307
+ { label: "Approach the sentinel.", nextId: "meet" },
51308
+ { label: "Turn back.", nextId: "retreat" }
51309
+ ]
51310
+ },
51311
+ {
51312
+ id: "meet",
51313
+ speaker: "Sentinel",
51314
+ text: "You carry the old marks. Speak your purpose, traveler.",
51315
+ backgroundKey: "forge",
51316
+ portraitKey: "rival",
51317
+ choices: [
51318
+ { label: "I seek the core.", nextId: "end" },
51319
+ { label: "I am only passing through.", nextId: "retreat" }
51320
+ ]
51321
+ },
51322
+ {
51323
+ id: "retreat",
51324
+ speaker: "Narrator",
51325
+ text: "You withdraw into the dark. The path will wait for braver feet.",
51326
+ backgroundKey: "corridor",
51327
+ choices: [{ label: "Begin again.", nextId: "start" }]
51328
+ },
51329
+ {
51330
+ id: "end",
51331
+ speaker: "Sentinel",
51332
+ text: "Then the way is open. Walk it well.",
51333
+ backgroundKey: "core",
51334
+ portraitKey: "rival",
51335
+ choices: [{ label: "Begin again.", nextId: "start" }]
51336
+ }
51337
+ ];
51338
+ VisualNovelBoard.displayName = "VisualNovelBoard";
51339
+ }
51340
+ });
51341
+ function VisualNovelTemplate({
51342
+ entity,
51343
+ title = "Visual Novel",
51344
+ nodes,
51345
+ currentNodeId,
51346
+ typewriterSpeed,
51347
+ portraitScale,
51348
+ chooseEvent,
51349
+ advanceEvent,
51350
+ restartEvent,
51351
+ className
51352
+ }) {
51353
+ const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
51354
+ return /* @__PURE__ */ jsxs(
51355
+ Box,
51356
+ {
51357
+ display: "flex",
51358
+ fullHeight: true,
51359
+ className: cn("visual-novel-template flex-col", className),
51360
+ children: [
51361
+ /* @__PURE__ */ jsx(
51362
+ HStack,
51363
+ {
51364
+ gap: "sm",
51365
+ align: "center",
51366
+ className: "px-4 py-3 border-b-2 border-border bg-surface shrink-0",
51367
+ children: /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title })
51368
+ }
51369
+ ),
51370
+ /* @__PURE__ */ jsx(Box, { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsx(
51371
+ VisualNovelBoard,
51372
+ {
51373
+ entity: resolved,
51374
+ nodes,
51375
+ currentNodeId,
51376
+ typewriterSpeed,
51377
+ portraitScale,
51378
+ chooseEvent,
51379
+ advanceEvent,
51380
+ restartEvent,
51381
+ className: "h-full"
51382
+ }
51383
+ ) })
51384
+ ]
51385
+ }
51386
+ );
51387
+ }
51388
+ var init_VisualNovelTemplate = __esm({
51389
+ "components/game/templates/VisualNovelTemplate.tsx"() {
51390
+ init_cn();
51391
+ init_Box();
51392
+ init_Stack();
51393
+ init_Typography();
51394
+ init_VisualNovelBoard();
51395
+ VisualNovelTemplate.displayName = "VisualNovelTemplate";
51396
+ }
51397
+ });
49999
51398
  function heroPosition(h) {
50000
51399
  if ("position" in h && h.position != null) return h.position;
50001
51400
  return vec2(h.position);
@@ -50336,48 +51735,48 @@ function WorldMapTemplate({
50336
51735
  }
50337
51736
  );
50338
51737
  }
50339
- var CDN7, DEFAULT_WORLDMAP_TILES, DEFAULT_WORLDMAP_UNITS, DEFAULT_WORLDMAP_FEATURES, DEFAULT_WORLDMAP_MANIFEST;
51738
+ var CDN5, DEFAULT_WORLDMAP_TILES, DEFAULT_WORLDMAP_UNITS, DEFAULT_WORLDMAP_FEATURES, DEFAULT_WORLDMAP_MANIFEST;
50340
51739
  var init_WorldMapTemplate = __esm({
50341
51740
  "components/game/templates/WorldMapTemplate.tsx"() {
50342
51741
  init_WorldMapBoard();
50343
- CDN7 = "https://almadar-kflow-assets.web.app/shared";
51742
+ CDN5 = "https://almadar-kflow-assets.web.app/shared";
50344
51743
  DEFAULT_WORLDMAP_TILES = [
50345
- { x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50346
- { x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50347
- { x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
50348
- { x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50349
- { x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50350
- { x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50351
- { x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50352
- { x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50353
- { x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50354
- { x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
50355
- { x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
50356
- { x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50357
- { x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
50358
- { x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50359
- { x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50360
- { x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50361
- { x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50362
- { x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50363
- { x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
50364
- { x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50365
- { x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50366
- { x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
50367
- { x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50368
- { x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50369
- { x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` }
51744
+ { x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51745
+ { x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51746
+ { x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
51747
+ { x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51748
+ { x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51749
+ { x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51750
+ { x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51751
+ { x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51752
+ { x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51753
+ { x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
51754
+ { x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
51755
+ { x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51756
+ { x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
51757
+ { x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51758
+ { x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51759
+ { x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51760
+ { x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51761
+ { x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51762
+ { x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
51763
+ { x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51764
+ { x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51765
+ { x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
51766
+ { x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
51767
+ { x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
51768
+ { x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN5}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` }
50370
51769
  ];
50371
51770
  DEFAULT_WORLDMAP_UNITS = [
50372
- { 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` },
50373
- { 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` }
51771
+ { 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` },
51772
+ { 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` }
50374
51773
  ];
50375
51774
  DEFAULT_WORLDMAP_FEATURES = [
50376
- { id: "f1", x: 2, y: 2, type: "capital", sprite: `${CDN7}/scenes/world/capital.png` },
50377
- { id: "f2", x: 4, y: 2, type: "power_node", sprite: `${CDN7}/world-map/power_node.png` }
51775
+ { id: "f1", x: 2, y: 2, type: "capital", sprite: `${CDN5}/scenes/world/capital.png` },
51776
+ { id: "f2", x: 4, y: 2, type: "power_node", sprite: `${CDN5}/world-map/power_node.png` }
50378
51777
  ];
50379
51778
  DEFAULT_WORLDMAP_MANIFEST = {
50380
- baseUrl: CDN7,
51779
+ baseUrl: CDN5,
50381
51780
  terrains: {
50382
51781
  grass: "/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png",
50383
51782
  water: "/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png",
@@ -50397,7 +51796,7 @@ var init_WorldMapTemplate = __esm({
50397
51796
  }
50398
51797
  });
50399
51798
  function lazyThree(name, loader) {
50400
- const Lazy = React82__default.lazy(
51799
+ const Lazy = React85__default.lazy(
50401
51800
  () => loader().then((m) => {
50402
51801
  const Resolved = m[name];
50403
51802
  if (!Resolved) {
@@ -50409,13 +51808,13 @@ function lazyThree(name, loader) {
50409
51808
  })
50410
51809
  );
50411
51810
  function ThreeWrapper(props) {
50412
- return React82__default.createElement(
51811
+ return React85__default.createElement(
50413
51812
  ThreeBoundary,
50414
51813
  { name },
50415
- React82__default.createElement(
50416
- React82__default.Suspense,
51814
+ React85__default.createElement(
51815
+ React85__default.Suspense,
50417
51816
  { fallback: null },
50418
- React82__default.createElement(Lazy, props)
51817
+ React85__default.createElement(Lazy, props)
50419
51818
  )
50420
51819
  );
50421
51820
  }
@@ -50459,7 +51858,10 @@ var init_component_registry_generated = __esm({
50459
51858
  init_CalendarGrid();
50460
51859
  init_CanvasEffect();
50461
51860
  init_Card();
51861
+ init_CardBattlerBoard();
51862
+ init_CardBattlerTemplate();
50462
51863
  init_CardGrid();
51864
+ init_CardHand();
50463
51865
  init_Carousel();
50464
51866
  init_CaseStudyCard();
50465
51867
  init_CaseStudyOrganism();
@@ -50470,6 +51872,8 @@ var init_component_registry_generated = __esm({
50470
51872
  init_ChartLegend();
50471
51873
  init_Checkbox();
50472
51874
  init_ChoiceButton();
51875
+ init_CityBuilderBoard();
51876
+ init_CityBuilderTemplate();
50473
51877
  init_ClassifierBoard();
50474
51878
  init_CodeBlock();
50475
51879
  init_CodeRunnerPanel();
@@ -50690,6 +52094,8 @@ var init_component_registry_generated = __esm({
50690
52094
  init_Toast();
50691
52095
  init_ToastSlot();
50692
52096
  init_Tooltip();
52097
+ init_TopDownShooterBoard();
52098
+ init_TopDownShooterTemplate();
50693
52099
  init_TowerDefenseBoard();
50694
52100
  init_TowerDefenseTemplate();
50695
52101
  init_TraitFrame();
@@ -50705,6 +52111,8 @@ var init_component_registry_generated = __esm({
50705
52111
  init_UploadDropZone();
50706
52112
  init_VersionDiff();
50707
52113
  init_ViolationAlert();
52114
+ init_VisualNovelBoard();
52115
+ init_VisualNovelTemplate();
50708
52116
  init_VoteStack();
50709
52117
  init_WaypointMarker();
50710
52118
  init_WizardContainer();
@@ -50713,7 +52121,7 @@ var init_component_registry_generated = __esm({
50713
52121
  init_WorldMapBoard();
50714
52122
  init_WorldMapTemplate();
50715
52123
  init_XPBar();
50716
- ThreeBoundary = class extends React82__default.Component {
52124
+ ThreeBoundary = class extends React85__default.Component {
50717
52125
  constructor() {
50718
52126
  super(...arguments);
50719
52127
  __publicField(this, "state", { failed: false });
@@ -50723,7 +52131,7 @@ var init_component_registry_generated = __esm({
50723
52131
  }
50724
52132
  render() {
50725
52133
  if (this.state.failed) {
50726
- return React82__default.createElement(
52134
+ return React85__default.createElement(
50727
52135
  "div",
50728
52136
  {
50729
52137
  "data-testid": "three-unavailable",
@@ -50779,7 +52187,10 @@ var init_component_registry_generated = __esm({
50779
52187
  "CalendarGrid": CalendarGrid,
50780
52188
  "CanvasEffect": CanvasEffect,
50781
52189
  "Card": Card,
52190
+ "CardBattlerBoard": CardBattlerBoard,
52191
+ "CardBattlerTemplate": CardBattlerTemplate,
50782
52192
  "CardGrid": CardGrid,
52193
+ "CardHand": CardHand,
50783
52194
  "Carousel": Carousel,
50784
52195
  "CaseStudyCard": CaseStudyCard,
50785
52196
  "CaseStudyOrganism": CaseStudyOrganism,
@@ -50790,6 +52201,8 @@ var init_component_registry_generated = __esm({
50790
52201
  "ChartLegend": ChartLegend,
50791
52202
  "Checkbox": Checkbox,
50792
52203
  "ChoiceButton": ChoiceButton,
52204
+ "CityBuilderBoard": CityBuilderBoard,
52205
+ "CityBuilderTemplate": CityBuilderTemplate,
50793
52206
  "ClassifierBoard": ClassifierBoard,
50794
52207
  "CodeBlock": CodeBlock,
50795
52208
  "CodeRunnerPanel": CodeRunnerPanel,
@@ -51022,6 +52435,8 @@ var init_component_registry_generated = __esm({
51022
52435
  "Toast": Toast,
51023
52436
  "ToastSlot": ToastSlot,
51024
52437
  "Tooltip": Tooltip,
52438
+ "TopDownShooterBoard": TopDownShooterBoard,
52439
+ "TopDownShooterTemplate": TopDownShooterTemplate,
51025
52440
  "TowerDefenseBoard": TowerDefenseBoard,
51026
52441
  "TowerDefenseTemplate": TowerDefenseTemplate,
51027
52442
  "TraitFrame": TraitFrame,
@@ -51038,6 +52453,8 @@ var init_component_registry_generated = __esm({
51038
52453
  "VStack": VStack,
51039
52454
  "VersionDiff": VersionDiff,
51040
52455
  "ViolationAlert": ViolationAlert,
52456
+ "VisualNovelBoard": VisualNovelBoard,
52457
+ "VisualNovelTemplate": VisualNovelTemplate,
51041
52458
  "VoteStack": VoteStack,
51042
52459
  "WaypointMarker": WaypointMarker,
51043
52460
  "WizardContainer": WizardContainer,
@@ -51064,7 +52481,7 @@ function SuspenseConfigProvider({
51064
52481
  config,
51065
52482
  children
51066
52483
  }) {
51067
- return React82__default.createElement(
52484
+ return React85__default.createElement(
51068
52485
  SuspenseConfigContext.Provider,
51069
52486
  { value: config },
51070
52487
  children
@@ -51554,7 +52971,7 @@ function renderPatternChildren(children, onDismiss, parentId = "root", parentPat
51554
52971
  const key = `${parentId}-${index}-trait:${traitName}`;
51555
52972
  return /* @__PURE__ */ jsx(TraitFrame, { traitName }, key);
51556
52973
  }
51557
- return /* @__PURE__ */ jsx(React82__default.Fragment, { children: child }, `${parentId}-${index}`);
52974
+ return /* @__PURE__ */ jsx(React85__default.Fragment, { children: child }, `${parentId}-${index}`);
51558
52975
  }
51559
52976
  if (!child || typeof child !== "object") return null;
51560
52977
  const childId = `${parentId}-${index}`;
@@ -51594,14 +53011,14 @@ function isPatternConfig(value) {
51594
53011
  if (value === null || value === void 0) return false;
51595
53012
  if (typeof value !== "object") return false;
51596
53013
  if (Array.isArray(value)) return false;
51597
- if (React82__default.isValidElement(value)) return false;
53014
+ if (React85__default.isValidElement(value)) return false;
51598
53015
  if (value instanceof Date) return false;
51599
53016
  if (typeof value === "function") return false;
51600
53017
  const record = value;
51601
53018
  return "type" in record && typeof record.type === "string" && getComponentForPattern$1(record.type) !== null;
51602
53019
  }
51603
53020
  function isPlainConfigObject(value) {
51604
- if (React82__default.isValidElement(value)) return false;
53021
+ if (React85__default.isValidElement(value)) return false;
51605
53022
  if (value instanceof Date) return false;
51606
53023
  const proto = Object.getPrototypeOf(value);
51607
53024
  return proto === Object.prototype || proto === null;
@@ -51722,6 +53139,22 @@ function SlotContentRenderer({
51722
53139
  const childrenIsRenderFn = typeof incomingChildren === "function";
51723
53140
  const { children: _childrenConfig, ...restPropsNoChildren } = content.props;
51724
53141
  const restProps = childrenIsRenderFn ? { ...restPropsNoChildren, children: incomingChildren } : restPropsNoChildren;
53142
+ const nodeSlotOverrides = {};
53143
+ for (const slotKey of CONTENT_NODE_SLOTS) {
53144
+ const slotVal = restProps[slotKey];
53145
+ if (slotVal === void 0 || slotVal === null) continue;
53146
+ if (React85__default.isValidElement(slotVal) || typeof slotVal === "string" || typeof slotVal === "number" || typeof slotVal === "boolean") continue;
53147
+ if (Array.isArray(slotVal) || typeof slotVal === "object" && "type" in slotVal) {
53148
+ nodeSlotOverrides[slotKey] = renderPatternChildren(
53149
+ slotVal,
53150
+ onDismiss,
53151
+ `${content.id}-${slotKey}`,
53152
+ `${myPath}.${slotKey}`,
53153
+ content.sourceTrait,
53154
+ { slot: content.slot, transitionEvent: content.transitionEvent, fromState: content.fromState, entity: content.entity }
53155
+ );
53156
+ }
53157
+ }
51725
53158
  const renderedProps = renderPatternProps(restProps, onDismiss);
51726
53159
  const patternDef = getPatternDefinition(content.pattern);
51727
53160
  const propsSchema = patternDef?.propsSchema;
@@ -51752,6 +53185,9 @@ function SlotContentRenderer({
51752
53185
  }
51753
53186
  }
51754
53187
  const finalProps = renderedProps;
53188
+ for (const [k, v] of Object.entries(nodeSlotOverrides)) {
53189
+ finalProps[k] = v;
53190
+ }
51755
53191
  const resolvedItems = Array.isArray(
51756
53192
  finalProps.entity
51757
53193
  ) ? finalProps.entity : null;
@@ -51872,7 +53308,7 @@ function UISlotRenderer({
51872
53308
  }
51873
53309
  return wrapped;
51874
53310
  }
51875
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, FORM_PATTERNS, SELF_OVERLAY_PATTERNS, PATTERNS_WITH_CHILDREN;
53311
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, FORM_PATTERNS, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
51876
53312
  var init_UISlotRenderer = __esm({
51877
53313
  "components/core/organisms/UISlotRenderer.tsx"() {
51878
53314
  "use client";
@@ -51909,6 +53345,16 @@ var init_UISlotRenderer = __esm({
51909
53345
  "wizard-step"
51910
53346
  ]);
51911
53347
  SELF_OVERLAY_PATTERNS = /* @__PURE__ */ new Set(["modal", "confirm-dialog"]);
53348
+ CONTENT_NODE_SLOTS = /* @__PURE__ */ new Set([
53349
+ "logo",
53350
+ "master",
53351
+ "detail",
53352
+ "trigger",
53353
+ "content",
53354
+ "addons",
53355
+ "hud",
53356
+ "fallback"
53357
+ ]);
51912
53358
  PATTERNS_WITH_CHILDREN = /* @__PURE__ */ new Set([
51913
53359
  "stack",
51914
53360
  "vstack",