@almadar/ui 5.60.0 → 5.62.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/avl/index.cjs +3233 -1757
  2. package/dist/avl/index.js +2057 -581
  3. package/dist/components/core/molecules/GraphCanvas.d.ts +6 -0
  4. package/dist/components/core/templates/index.d.ts +4 -0
  5. package/dist/components/game/molecules/CardHand.d.ts +35 -0
  6. package/dist/components/game/molecules/DialogueBox.d.ts +8 -2
  7. package/dist/components/game/molecules/index.d.ts +1 -0
  8. package/dist/components/game/molecules/three/index.cjs +229 -120
  9. package/dist/components/game/molecules/three/index.js +229 -120
  10. package/dist/components/game/organisms/CardBattlerBoard.d.ts +46 -0
  11. package/dist/components/game/organisms/CityBuilderBoard.d.ts +63 -0
  12. package/dist/components/game/organisms/TopDownShooterBoard.d.ts +64 -0
  13. package/dist/components/game/organisms/TowerDefenseBoard.d.ts +10 -1
  14. package/dist/components/game/organisms/TraitSlot.d.ts +1 -3
  15. package/dist/components/game/organisms/VisualNovelBoard.d.ts +53 -0
  16. package/dist/components/game/organisms/index.d.ts +4 -0
  17. package/dist/components/game/templates/CardBattlerTemplate.d.ts +35 -0
  18. package/dist/components/game/templates/CityBuilderTemplate.d.ts +40 -0
  19. package/dist/components/game/templates/GameCanvas3DBattleTemplate.d.ts +5 -2
  20. package/dist/components/game/templates/GameCanvas3DCastleTemplate.d.ts +5 -2
  21. package/dist/components/game/templates/GameCanvas3DWorldMapTemplate.d.ts +5 -2
  22. package/dist/components/game/templates/TopDownShooterTemplate.d.ts +42 -0
  23. package/dist/components/game/templates/VisualNovelTemplate.d.ts +28 -0
  24. package/dist/components/game/templates/game3dAssetManifest.d.ts +50 -0
  25. package/dist/components/game/templates/index.d.ts +4 -0
  26. package/dist/components/index.cjs +3048 -1547
  27. package/dist/components/index.js +2019 -527
  28. package/dist/docs/index.cjs +105 -1
  29. package/dist/docs/index.js +105 -1
  30. package/dist/hooks/index.cjs +105 -1
  31. package/dist/hooks/index.js +105 -1
  32. package/dist/locales/index.cjs +315 -3
  33. package/dist/locales/index.js +315 -3
  34. package/dist/providers/index.cjs +3034 -1558
  35. package/dist/providers/index.js +2025 -549
  36. package/dist/runtime/index.cjs +3085 -1609
  37. package/dist/runtime/index.js +2024 -548
  38. package/locales/ar.json +105 -1
  39. package/locales/en.json +105 -1
  40. package/locales/sl.json +105 -1
  41. package/package.json +1 -1
@@ -1,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", () => {
@@ -8379,16 +8379,6 @@ var init_Skeleton = __esm({
8379
8379
  Skeleton.displayName = "Skeleton";
8380
8380
  }
8381
8381
  });
8382
- function isKnownPattern(type) {
8383
- return type in componentMapping;
8384
- }
8385
- var componentMapping;
8386
- var init_pattern_resolver = __esm({
8387
- "renderer/pattern-resolver.ts"() {
8388
- createLogger("almadar:ui:pattern-resolver");
8389
- componentMapping = {};
8390
- }
8391
- });
8392
8382
  function executeClientEffects(effects, config) {
8393
8383
  if (!effects || effects.length === 0) {
8394
8384
  return;
@@ -8397,7 +8387,7 @@ function executeClientEffects(effects, config) {
8397
8387
  try {
8398
8388
  executeEffect(effect, config);
8399
8389
  } catch (error) {
8400
- log4.error("Error executing effect", () => ({
8390
+ log3.error("Error executing effect", () => ({
8401
8391
  effect: JSON.stringify(effect),
8402
8392
  error: error instanceof Error ? error : String(error)
8403
8393
  }));
@@ -8429,7 +8419,7 @@ function executeEffect(effect, config) {
8429
8419
  break;
8430
8420
  }
8431
8421
  default:
8432
- log4.warn("Unknown effect type", { effectType: String(effectType) });
8422
+ log3.warn("Unknown effect type", { effectType: String(effectType) });
8433
8423
  }
8434
8424
  }
8435
8425
  function executeRenderUI(slot, patternConfig, config) {
@@ -8444,10 +8434,10 @@ function executeNotify(message, options, config) {
8444
8434
  function executeEmit(event, payload, config) {
8445
8435
  config.eventBus.emit(event, payload);
8446
8436
  }
8447
- var log4;
8437
+ var log3;
8448
8438
  var init_client_effect_executor = __esm({
8449
8439
  "renderer/client-effect-executor.ts"() {
8450
- log4 = createLogger("almadar:ui:effects:client");
8440
+ log3 = createLogger("almadar:ui:effects:client");
8451
8441
  }
8452
8442
  });
8453
8443
 
@@ -8608,12 +8598,12 @@ function useOfflineExecutor(options) {
8608
8598
  clearQueue
8609
8599
  };
8610
8600
  }
8611
- var log5, effectIdCounter, OfflineExecutor;
8601
+ var log4, effectIdCounter, OfflineExecutor;
8612
8602
  var init_offline_executor = __esm({
8613
8603
  "renderer/offline-executor.ts"() {
8614
8604
  "use client";
8615
8605
  init_client_effect_executor();
8616
- log5 = createLogger("almadar:ui:effects:offline");
8606
+ log4 = createLogger("almadar:ui:effects:offline");
8617
8607
  effectIdCounter = 0;
8618
8608
  OfflineExecutor = class {
8619
8609
  constructor(config) {
@@ -8667,7 +8657,7 @@ var init_offline_executor = __esm({
8667
8657
  this.state.syncQueue = JSON.parse(stored);
8668
8658
  }
8669
8659
  } catch (error) {
8670
- log5.warn("Failed to load sync queue", { error: error instanceof Error ? error : String(error) });
8660
+ log4.warn("Failed to load sync queue", { error: error instanceof Error ? error : String(error) });
8671
8661
  }
8672
8662
  }
8673
8663
  /**
@@ -8678,7 +8668,7 @@ var init_offline_executor = __esm({
8678
8668
  try {
8679
8669
  this.storage.setItem("orbital-offline-queue", JSON.stringify(this.state.syncQueue));
8680
8670
  } catch (error) {
8681
- log5.warn("Failed to save sync queue", { error: error instanceof Error ? error : String(error) });
8671
+ log4.warn("Failed to save sync queue", { error: error instanceof Error ? error : String(error) });
8682
8672
  }
8683
8673
  }
8684
8674
  /**
@@ -8748,7 +8738,7 @@ var init_offline_executor = __esm({
8748
8738
  this.queueForSync(type, { args, event, payload });
8749
8739
  break;
8750
8740
  default:
8751
- log5.warn("Unknown effect type", { type });
8741
+ log4.warn("Unknown effect type", { type });
8752
8742
  }
8753
8743
  }
8754
8744
  }
@@ -8853,7 +8843,6 @@ var init_offline_executor = __esm({
8853
8843
  // renderer/index.ts
8854
8844
  var init_renderer = __esm({
8855
8845
  "renderer/index.ts"() {
8856
- init_pattern_resolver();
8857
8846
  init_slot_definitions();
8858
8847
  }
8859
8848
  });
@@ -9636,8 +9625,8 @@ function ActionButtons({
9636
9625
  disabled
9637
9626
  }) {
9638
9627
  const eventBus = useEventBus();
9639
- const [activeButtons, setActiveButtons] = React82.useState(/* @__PURE__ */ new Set());
9640
- const handlePress = React82.useCallback(
9628
+ const [activeButtons, setActiveButtons] = React85.useState(/* @__PURE__ */ new Set());
9629
+ const handlePress = React85.useCallback(
9641
9630
  (id) => {
9642
9631
  setActiveButtons((prev) => new Set(prev).add(id));
9643
9632
  if (actionEvent) eventBus.emit(`UI:${actionEvent}`, { id, pressed: true });
@@ -9645,7 +9634,7 @@ function ActionButtons({
9645
9634
  },
9646
9635
  [actionEvent, eventBus, onAction]
9647
9636
  );
9648
- const handleRelease = React82.useCallback(
9637
+ const handleRelease = React85.useCallback(
9649
9638
  (id) => {
9650
9639
  setActiveButtons((prev) => {
9651
9640
  const next = new Set(prev);
@@ -10282,8 +10271,8 @@ var init_AnimatedCounter = __esm({
10282
10271
  const [displayValue, setDisplayValue] = useState("0");
10283
10272
  const [hasAnimated, setHasAnimated] = useState(false);
10284
10273
  const animate = useCallback(() => {
10285
- const { num: num2, prefix, suffix, decimals } = parseValue(value);
10286
- if (num2 === 0) {
10274
+ const { num: num3, prefix, suffix, decimals } = parseValue(value);
10275
+ if (num3 === 0) {
10287
10276
  setDisplayValue(String(value));
10288
10277
  return;
10289
10278
  }
@@ -10292,7 +10281,7 @@ var init_AnimatedCounter = __esm({
10292
10281
  const elapsed = now - startTime;
10293
10282
  const progress = Math.min(elapsed / duration, 1);
10294
10283
  const eased = 1 - Math.pow(1 - progress, 3);
10295
- const current = eased * num2;
10284
+ const current = eased * num3;
10296
10285
  setDisplayValue(`${prefix}${current.toFixed(decimals)}${suffix}`);
10297
10286
  if (progress < 1) {
10298
10287
  requestAnimationFrame(tick);
@@ -10557,7 +10546,7 @@ function recordTransition(trace) {
10557
10546
  ...trace,
10558
10547
  id: `t-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
10559
10548
  };
10560
- log6.debug("transition:recorded", { trait: trace.traitName, from: trace.from, to: trace.to, event: trace.event, effectCount: trace.effects.length });
10549
+ log5.debug("transition:recorded", { trait: trace.traitName, from: trace.from, to: trace.to, event: trace.event, effectCount: trace.effects.length });
10561
10550
  getState().transitions.push(entry);
10562
10551
  if (getState().transitions.length > MAX_TRANSITIONS) {
10563
10552
  getState().transitions.shift();
@@ -10626,7 +10615,7 @@ function getTraitSnapshots() {
10626
10615
  try {
10627
10616
  snapshots.push(getter());
10628
10617
  } catch (err) {
10629
- log6.error("traitSnapshot getter failed", { trait: traitName, err: String(err) });
10618
+ log5.error("traitSnapshot getter failed", { trait: traitName, err: String(err) });
10630
10619
  }
10631
10620
  }
10632
10621
  return snapshots;
@@ -10689,12 +10678,12 @@ function waitForTransition(event, timeoutMs = 1e4) {
10689
10678
  }
10690
10679
  function bindEventBus(eventBus) {
10691
10680
  if (typeof window === "undefined") return;
10692
- log6.info("bindEventBus", { hasOnAny: !!eventBus.onAny });
10681
+ log5.info("bindEventBus", { hasOnAny: !!eventBus.onAny });
10693
10682
  exposeOnWindow();
10694
10683
  if (window.__orbitalVerification) {
10695
10684
  window.__orbitalVerification.sendEvent = (event, payload, traitScope) => {
10696
10685
  const prefixed = event.startsWith("UI:") ? event : traitScope ? `UI:${traitScope}.${event}` : `UI:${event}`;
10697
- log6.debug("sendEvent", { event: prefixed, traitScope, payloadKeys: payload ? Object.keys(payload) : [] });
10686
+ log5.debug("sendEvent", { event: prefixed, traitScope, payloadKeys: payload ? Object.keys(payload) : [] });
10698
10687
  eventBus.emit(prefixed, payload);
10699
10688
  };
10700
10689
  const eventLog = [];
@@ -10743,10 +10732,10 @@ function updateAssetStatus(url, status) {
10743
10732
  window.__orbitalVerification.assetStatus[url] = status;
10744
10733
  }
10745
10734
  }
10746
- var log6, MAX_TRANSITIONS;
10735
+ var log5, MAX_TRANSITIONS;
10747
10736
  var init_verificationRegistry = __esm({
10748
10737
  "lib/verificationRegistry.ts"() {
10749
- log6 = createLogger("almadar:bridge");
10738
+ log5 = createLogger("almadar:bridge");
10750
10739
  MAX_TRANSITIONS = 500;
10751
10740
  exposeOnWindow();
10752
10741
  }
@@ -11110,7 +11099,7 @@ function IsometricCanvas({
11110
11099
  // Tuning
11111
11100
  diamondTopY: diamondTopYProp,
11112
11101
  // Remote asset loading
11113
- assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/",
11102
+ assetBaseUrl,
11114
11103
  assetManifest
11115
11104
  }) {
11116
11105
  const tilesProp = Array.isArray(_tilesPropRaw) ? _tilesPropRaw : [];
@@ -11189,7 +11178,7 @@ function IsometricCanvas({
11189
11178
  const attackTargetSet = useMemo(() => {
11190
11179
  return new Set(attackTargets.map((p2) => `${p2.x},${p2.y}`));
11191
11180
  }, [attackTargets]);
11192
- const resolveManifestUrl = useCallback((relativePath) => {
11181
+ const resolveManifestUrl7 = useCallback((relativePath) => {
11193
11182
  if (!relativePath) return void 0;
11194
11183
  if (assetBaseUrl) return `${assetBaseUrl.replace(/\/$/, "")}${relativePath.startsWith("/") ? "" : "/"}${relativePath}`;
11195
11184
  return relativePath;
@@ -11202,7 +11191,7 @@ function IsometricCanvas({
11202
11191
  const url = getTerrainSprite(tile.terrain ?? "");
11203
11192
  if (url) urls.push(url);
11204
11193
  } else {
11205
- const url = resolveManifestUrl(assetManifest?.terrains?.[tile.terrain ?? ""]);
11194
+ const url = resolveManifestUrl7(assetManifest?.terrains?.[tile.terrain ?? ""]);
11206
11195
  if (url) urls.push(url);
11207
11196
  }
11208
11197
  }
@@ -11212,7 +11201,7 @@ function IsometricCanvas({
11212
11201
  const url = getFeatureSprite(feature.type);
11213
11202
  if (url) urls.push(url);
11214
11203
  } else {
11215
- const url = resolveManifestUrl(assetManifest?.features?.[feature.type]);
11204
+ const url = resolveManifestUrl7(assetManifest?.features?.[feature.type]);
11216
11205
  if (url) urls.push(url);
11217
11206
  }
11218
11207
  }
@@ -11222,13 +11211,13 @@ function IsometricCanvas({
11222
11211
  const url = getUnitSprite(unit);
11223
11212
  if (url) urls.push(url);
11224
11213
  } else if (unit.unitType) {
11225
- const url = resolveManifestUrl(assetManifest?.units?.[unit.unitType]);
11214
+ const url = resolveManifestUrl7(assetManifest?.units?.[unit.unitType]);
11226
11215
  if (url) urls.push(url);
11227
11216
  }
11228
11217
  }
11229
11218
  if (assetManifest?.effects) {
11230
11219
  for (const path of Object.values(assetManifest.effects)) {
11231
- const url = resolveManifestUrl(path);
11220
+ const url = resolveManifestUrl7(path);
11232
11221
  if (url) urls.push(url);
11233
11222
  }
11234
11223
  }
@@ -11236,7 +11225,7 @@ function IsometricCanvas({
11236
11225
  if (atlasSheetUrls.length) urls.push(...atlasSheetUrls);
11237
11226
  if (backgroundImage) urls.push(backgroundImage);
11238
11227
  return [...new Set(urls.filter(Boolean))];
11239
- }, [sortedTiles, features, units, getTerrainSprite, getFeatureSprite, getUnitSprite, effectSpriteUrls, atlasSheetUrls, backgroundImage, assetManifest, resolveManifestUrl]);
11228
+ }, [sortedTiles, features, units, getTerrainSprite, getFeatureSprite, getUnitSprite, effectSpriteUrls, atlasSheetUrls, backgroundImage, assetManifest, resolveManifestUrl7]);
11240
11229
  const { getImage, pendingCount } = useImageCache(spriteUrls);
11241
11230
  useEffect(() => {
11242
11231
  if (typeof window === "undefined") return;
@@ -11260,14 +11249,14 @@ function IsometricCanvas({
11260
11249
  lerpToTarget
11261
11250
  } = useCamera();
11262
11251
  const resolveTerrainSpriteUrl = useCallback((tile) => {
11263
- return tile.terrainSprite || getTerrainSprite?.(tile.terrain ?? "") || resolveManifestUrl(assetManifest?.terrains?.[tile.terrain ?? ""]);
11264
- }, [getTerrainSprite, assetManifest, resolveManifestUrl]);
11252
+ return tile.terrainSprite || getTerrainSprite?.(tile.terrain ?? "") || resolveManifestUrl7(assetManifest?.terrains?.[tile.terrain ?? ""]);
11253
+ }, [getTerrainSprite, assetManifest, resolveManifestUrl7]);
11265
11254
  const resolveFeatureSpriteUrl = useCallback((featureType) => {
11266
- return getFeatureSprite?.(featureType) || resolveManifestUrl(assetManifest?.features?.[featureType]);
11267
- }, [getFeatureSprite, assetManifest, resolveManifestUrl]);
11255
+ return getFeatureSprite?.(featureType) || resolveManifestUrl7(assetManifest?.features?.[featureType]);
11256
+ }, [getFeatureSprite, assetManifest, resolveManifestUrl7]);
11268
11257
  const resolveUnitSpriteUrl = useCallback((unit) => {
11269
- return unit.sprite || getUnitSprite?.(unit) || (unit.unitType ? resolveManifestUrl(assetManifest?.units?.[unit.unitType]) : void 0);
11270
- }, [getUnitSprite, assetManifest, resolveManifestUrl]);
11258
+ return unit.sprite || getUnitSprite?.(unit) || (unit.unitType ? resolveManifestUrl7(assetManifest?.units?.[unit.unitType]) : void 0);
11259
+ }, [getUnitSprite, assetManifest, resolveManifestUrl7]);
11271
11260
  const drawMinimap = useCallback(() => {
11272
11261
  if (!showMinimap) return;
11273
11262
  const miniCanvas = minimapRef.current;
@@ -11369,7 +11358,15 @@ function IsometricCanvas({
11369
11358
  const spriteUrl = resolveTerrainSpriteUrl(tile);
11370
11359
  const img = spriteUrl ? getImage(spriteUrl) : null;
11371
11360
  if (img) {
11372
- 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
+ }
11373
11370
  } else {
11374
11371
  const centerX = pos.x + scaledTileWidth / 2;
11375
11372
  const topY = pos.y + scaledDiamondTopY;
@@ -13075,7 +13072,7 @@ var init_avl_elk_layout = __esm({
13075
13072
  elk = new ELK();
13076
13073
  }
13077
13074
  });
13078
- var log7, SWIM_GUTTER, CENTER_W, BehaviorView;
13075
+ var log6, SWIM_GUTTER, CENTER_W, BehaviorView;
13079
13076
  var init_BehaviorView = __esm({
13080
13077
  "components/avl/molecules/BehaviorView.tsx"() {
13081
13078
  "use client";
@@ -13084,7 +13081,7 @@ var init_BehaviorView = __esm({
13084
13081
  init_AvlSwimLane();
13085
13082
  init_types();
13086
13083
  init_avl_elk_layout();
13087
- log7 = createLogger("almadar:ui:avl:behavior-view");
13084
+ log6 = createLogger("almadar:ui:avl:behavior-view");
13088
13085
  SWIM_GUTTER = 120;
13089
13086
  CENTER_W = 360;
13090
13087
  BehaviorView = ({ data }) => {
@@ -13095,7 +13092,7 @@ var init_BehaviorView = __esm({
13095
13092
  const dataKey = useMemo(() => JSON.stringify(traitData), [traitData]);
13096
13093
  useEffect(() => {
13097
13094
  if (!traitData) return;
13098
- computeTraitLayout(traitData).then(setLayout).catch((err) => log7.error("compute-trait-layout-failed", { error: err instanceof Error ? err : String(err) }));
13095
+ computeTraitLayout(traitData).then(setLayout).catch((err) => log6.error("compute-trait-layout-failed", { error: err instanceof Error ? err : String(err) }));
13099
13096
  }, [dataKey]);
13100
13097
  if (!traitData) {
13101
13098
  return /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-[var(--color-border)] bg-[var(--color-card)] p-4 text-center text-[var(--color-muted-foreground)] text-sm", children: t("avl.noTraitData") });
@@ -13506,7 +13503,7 @@ function generateDiff(oldVal, newVal) {
13506
13503
  }
13507
13504
  return diff;
13508
13505
  }
13509
- var orbStyleOverrides, orbStyle, loloStyleOverrides, loloStyle, log8, CODE_LANGUAGES, CODE_LANGUAGE_SET, DIFF_STYLES, LINE_PROPS_FN, HIDDEN_LINE_NUMBERS, CodeBlock;
13506
+ var orbStyleOverrides, orbStyle, loloStyleOverrides, loloStyle, log7, CODE_LANGUAGES, CODE_LANGUAGE_SET, DIFF_STYLES, LINE_PROPS_FN, HIDDEN_LINE_NUMBERS, CodeBlock;
13510
13507
  var init_CodeBlock = __esm({
13511
13508
  "components/core/molecules/markdown/CodeBlock.tsx"() {
13512
13509
  init_cn();
@@ -13589,7 +13586,7 @@ var init_CodeBlock = __esm({
13589
13586
  "lolo-op-async": { color: ORB_COLORS.dark.async }
13590
13587
  };
13591
13588
  loloStyle = { ...dark, ...loloStyleOverrides };
13592
- log8 = createLogger("almadar:ui:markdown-code");
13589
+ log7 = createLogger("almadar:ui:markdown-code");
13593
13590
  CODE_LANGUAGES = [
13594
13591
  "text",
13595
13592
  "json",
@@ -13628,7 +13625,7 @@ var init_CodeBlock = __esm({
13628
13625
  };
13629
13626
  LINE_PROPS_FN = (n) => ({ "data-line": String(n - 1) });
13630
13627
  HIDDEN_LINE_NUMBERS = { display: "none" };
13631
- CodeBlock = React82__default.memo(
13628
+ CodeBlock = React85__default.memo(
13632
13629
  ({
13633
13630
  code: rawCode,
13634
13631
  language = "text",
@@ -13797,24 +13794,24 @@ var init_CodeBlock = __esm({
13797
13794
  return;
13798
13795
  }
13799
13796
  lineEls.forEach((el) => {
13800
- const num2 = parseInt(el.getAttribute("data-line") ?? "-1", 10);
13801
- if (hiddenLines.has(num2)) {
13797
+ const num3 = parseInt(el.getAttribute("data-line") ?? "-1", 10);
13798
+ if (hiddenLines.has(num3)) {
13802
13799
  el.style.display = "none";
13803
13800
  return;
13804
13801
  }
13805
13802
  el.style.display = "";
13806
13803
  el.style.position = "relative";
13807
13804
  el.style.paddingLeft = "1.2em";
13808
- const region = foldStartMap.get(num2);
13805
+ const region = foldStartMap.get(num3);
13809
13806
  if (!region) return;
13810
- const isCollapsed = collapsed.has(num2);
13807
+ const isCollapsed = collapsed.has(num3);
13811
13808
  const toggle = document.createElement("span");
13812
13809
  toggle.className = "fold-toggle";
13813
13810
  toggle.textContent = isCollapsed ? "\u25B6" : "\u25BC";
13814
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%";
13815
13812
  toggle.addEventListener("click", (e) => {
13816
13813
  e.stopPropagation();
13817
- toggleFoldRef.current(num2);
13814
+ toggleFoldRef.current(num3);
13818
13815
  });
13819
13816
  el.insertBefore(toggle, el.firstChild);
13820
13817
  if (isCollapsed) {
@@ -13853,7 +13850,7 @@ var init_CodeBlock = __esm({
13853
13850
  eventBus.emit("UI:COPY_CODE", { language: activeLanguage, success: true });
13854
13851
  setTimeout(() => setCopied(false), 2e3);
13855
13852
  } catch (err) {
13856
- log8.error("Failed to copy code", { error: err instanceof Error ? err : String(err) });
13853
+ log7.error("Failed to copy code", { error: err instanceof Error ? err : String(err) });
13857
13854
  eventBus.emit("UI:COPY_CODE", { language: activeLanguage, success: false });
13858
13855
  }
13859
13856
  };
@@ -14215,7 +14212,7 @@ var init_MarkdownContent = __esm({
14215
14212
  init_Box();
14216
14213
  init_CodeBlock();
14217
14214
  init_cn();
14218
- MarkdownContent = React82__default.memo(
14215
+ MarkdownContent = React85__default.memo(
14219
14216
  ({ content, direction = "ltr", className }) => {
14220
14217
  const { t: _t } = useTranslate();
14221
14218
  const safeContent = typeof content === "string" ? content : String(content ?? "");
@@ -15532,7 +15529,7 @@ var init_StateMachineView = __esm({
15532
15529
  style: { top: title ? 30 : 0 },
15533
15530
  children: [
15534
15531
  entity && /* @__PURE__ */ jsx(EntityBox, { entity, config }),
15535
- 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(
15536
15533
  StateNode,
15537
15534
  {
15538
15535
  state,
@@ -17734,14 +17731,14 @@ function useSafeEventBus2() {
17734
17731
  } };
17735
17732
  }
17736
17733
  }
17737
- var log9, lookStyles4, ButtonGroup;
17734
+ var log8, lookStyles4, ButtonGroup;
17738
17735
  var init_ButtonGroup = __esm({
17739
17736
  "components/core/molecules/ButtonGroup.tsx"() {
17740
17737
  "use client";
17741
17738
  init_cn();
17742
17739
  init_atoms2();
17743
17740
  init_useEventBus();
17744
- log9 = createLogger("almadar:ui:button-group");
17741
+ log8 = createLogger("almadar:ui:button-group");
17745
17742
  lookStyles4 = {
17746
17743
  "right-aligned-buttons": "",
17747
17744
  "floating-bar": "fixed bottom-section left-1/2 -translate-x-1/2 shadow-elevation-toast bg-card p-card-sm rounded-container",
@@ -17822,7 +17819,7 @@ var init_ButtonGroup = __esm({
17822
17819
  {
17823
17820
  variant: "ghost",
17824
17821
  onClick: () => {
17825
- log9.debug("Filter clicked", { field: filter.field });
17822
+ log8.debug("Filter clicked", { field: filter.field });
17826
17823
  },
17827
17824
  children: filter.label
17828
17825
  },
@@ -19433,8 +19430,8 @@ function EmojiEffect({
19433
19430
  );
19434
19431
  }
19435
19432
  function CanvasEffect({
19436
- effectSpriteUrl = "https://almadar-kflow-assets.web.app/shared/effects/gas/gas00.png",
19437
- assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/effects/",
19433
+ effectSpriteUrl,
19434
+ assetBaseUrl,
19438
19435
  ...props
19439
19436
  }) {
19440
19437
  const eventBus = useEventBus();
@@ -19477,6 +19474,380 @@ var init_CanvasEffect = __esm({
19477
19474
  CanvasEffect.displayName = "CanvasEffect";
19478
19475
  }
19479
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
+ });
19480
19851
 
19481
19852
  // lib/getNestedValue.ts
19482
19853
  function getNestedValue(obj, path) {
@@ -21259,6 +21630,315 @@ var init_ChartLegend = __esm({
21259
21630
  ChartLegend.displayName = "ChartLegend";
21260
21631
  }
21261
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
+ });
21262
21942
  function ClassifierBoard({
21263
21943
  entity,
21264
21944
  completeEvent = "PUZZLE_COMPLETE",
@@ -22073,7 +22753,7 @@ function CraftingRecipe({
22073
22753
  className
22074
22754
  }) {
22075
22755
  const eventBus = useEventBus();
22076
- const handleCraft = React82.useCallback(() => {
22756
+ const handleCraft = React85.useCallback(() => {
22077
22757
  onCraft?.();
22078
22758
  if (craftEvent) {
22079
22759
  eventBus.emit(craftEvent, { output: output.label });
@@ -22090,7 +22770,7 @@ function CraftingRecipe({
22090
22770
  children: [
22091
22771
  /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-wrap items-center", children: inputs.map((ingredient, index) => {
22092
22772
  const hasSufficient = ingredient.available >= ingredient.required;
22093
- return /* @__PURE__ */ jsxs(React82.Fragment, { children: [
22773
+ return /* @__PURE__ */ jsxs(React85.Fragment, { children: [
22094
22774
  /* @__PURE__ */ jsx(Box, { className: "relative", children: /* @__PURE__ */ jsx(
22095
22775
  ItemSlot,
22096
22776
  {
@@ -22162,8 +22842,8 @@ function DPad({
22162
22842
  }) {
22163
22843
  const eventBus = useEventBus();
22164
22844
  const sizes = sizeMap15[size];
22165
- const [activeDirections, setActiveDirections] = React82.useState(/* @__PURE__ */ new Set());
22166
- const handlePress = React82.useCallback(
22845
+ const [activeDirections, setActiveDirections] = React85.useState(/* @__PURE__ */ new Set());
22846
+ const handlePress = React85.useCallback(
22167
22847
  (direction) => {
22168
22848
  setActiveDirections((prev) => new Set(prev).add(direction));
22169
22849
  if (directionEvent) eventBus.emit(`UI:${directionEvent}`, { direction, pressed: true });
@@ -22171,7 +22851,7 @@ function DPad({
22171
22851
  },
22172
22852
  [directionEvent, eventBus, onDirection]
22173
22853
  );
22174
- const handleRelease = React82.useCallback(
22854
+ const handleRelease = React85.useCallback(
22175
22855
  (direction) => {
22176
22856
  setActiveDirections((prev) => {
22177
22857
  const next = new Set(prev);
@@ -22978,8 +23658,8 @@ var init_Menu = __esm({
22978
23658
  "bottom-end": "bottom-start"
22979
23659
  };
22980
23660
  const effectivePosition = direction === "rtl" ? rtlMirror[position] ?? position : position;
22981
- const triggerChild = React82__default.isValidElement(trigger) ? trigger : /* @__PURE__ */ jsx(Typography, { variant: "small", as: "span", children: trigger });
22982
- 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(
22983
23663
  triggerChild,
22984
23664
  {
22985
23665
  ref: triggerRef,
@@ -23113,14 +23793,14 @@ function useDataDnd(args) {
23113
23793
  const isZone = Boolean(dragGroup || accepts || sortable);
23114
23794
  const enabled = isZone || Boolean(dndRoot);
23115
23795
  const eventBus = useEventBus();
23116
- const parentRoot = React82__default.useContext(RootCtx);
23796
+ const parentRoot = React85__default.useContext(RootCtx);
23117
23797
  const isRoot = enabled && parentRoot === null;
23118
- const zoneId = React82__default.useId();
23798
+ const zoneId = React85__default.useId();
23119
23799
  const ownGroup = dragGroup ?? accepts ?? zoneId;
23120
- const [optimisticOrders, setOptimisticOrders] = React82__default.useState(() => /* @__PURE__ */ new Map());
23121
- const optimisticOrdersRef = React82__default.useRef(optimisticOrders);
23800
+ const [optimisticOrders, setOptimisticOrders] = React85__default.useState(() => /* @__PURE__ */ new Map());
23801
+ const optimisticOrdersRef = React85__default.useRef(optimisticOrders);
23122
23802
  optimisticOrdersRef.current = optimisticOrders;
23123
- const clearOptimisticOrder = React82__default.useCallback((group) => {
23803
+ const clearOptimisticOrder = React85__default.useCallback((group) => {
23124
23804
  setOptimisticOrders((prev) => {
23125
23805
  if (!prev.has(group)) return prev;
23126
23806
  const next = new Map(prev);
@@ -23145,7 +23825,7 @@ function useDataDnd(args) {
23145
23825
  const raw = it[dndItemIdField];
23146
23826
  return String(raw ?? `__idx_${idx}`);
23147
23827
  }).join("|");
23148
- const itemIds = React82__default.useMemo(
23828
+ const itemIds = React85__default.useMemo(
23149
23829
  () => orderedItems.map((it, idx) => {
23150
23830
  const raw = it[dndItemIdField];
23151
23831
  return raw ?? `__idx_${idx}`;
@@ -23153,7 +23833,7 @@ function useDataDnd(args) {
23153
23833
  [itemIdsSignature]
23154
23834
  );
23155
23835
  const itemsContentSig = items.map((it, idx) => String(it[dndItemIdField] ?? `__${idx}`)).join("|");
23156
- React82__default.useEffect(() => {
23836
+ React85__default.useEffect(() => {
23157
23837
  const root = isRoot ? null : parentRoot;
23158
23838
  if (root) {
23159
23839
  root.clearOptimisticOrder(ownGroup);
@@ -23161,20 +23841,20 @@ function useDataDnd(args) {
23161
23841
  clearOptimisticOrder(ownGroup);
23162
23842
  }
23163
23843
  }, [itemsContentSig, ownGroup]);
23164
- const zonesRef = React82__default.useRef(/* @__PURE__ */ new Map());
23165
- const registerZone = React82__default.useCallback((zoneId2, meta2) => {
23844
+ const zonesRef = React85__default.useRef(/* @__PURE__ */ new Map());
23845
+ const registerZone = React85__default.useCallback((zoneId2, meta2) => {
23166
23846
  zonesRef.current.set(zoneId2, meta2);
23167
23847
  }, []);
23168
- const unregisterZone = React82__default.useCallback((zoneId2) => {
23848
+ const unregisterZone = React85__default.useCallback((zoneId2) => {
23169
23849
  zonesRef.current.delete(zoneId2);
23170
23850
  }, []);
23171
- const [activeDrag, setActiveDrag] = React82__default.useState(null);
23172
- const [overZoneGroup, setOverZoneGroup] = React82__default.useState(null);
23173
- 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(
23174
23854
  () => ({ group: ownGroup, dropEvent, reorderEvent, positionEvent, itemIds, rawItems: items, idField: dndItemIdField }),
23175
23855
  [ownGroup, dropEvent, reorderEvent, positionEvent, itemIds, items, dndItemIdField]
23176
23856
  );
23177
- React82__default.useEffect(() => {
23857
+ React85__default.useEffect(() => {
23178
23858
  const target = isRoot ? null : parentRoot;
23179
23859
  if (!target) {
23180
23860
  zonesRef.current.set(zoneId, meta);
@@ -23193,7 +23873,7 @@ function useDataDnd(args) {
23193
23873
  }, [parentRoot, isRoot, zoneId, meta]);
23194
23874
  const sensors = useAlmadarDndSensors(true);
23195
23875
  const collisionDetection = almadarDndCollisionDetection;
23196
- const findZoneByItem = React82__default.useCallback(
23876
+ const findZoneByItem = React85__default.useCallback(
23197
23877
  (id) => {
23198
23878
  for (const z of zonesRef.current.values()) {
23199
23879
  if (z.itemIds.includes(id)) return z;
@@ -23202,7 +23882,7 @@ function useDataDnd(args) {
23202
23882
  },
23203
23883
  []
23204
23884
  );
23205
- React82__default.useCallback(
23885
+ React85__default.useCallback(
23206
23886
  (group) => {
23207
23887
  for (const z of zonesRef.current.values()) {
23208
23888
  if (z.group === group) return z;
@@ -23211,7 +23891,7 @@ function useDataDnd(args) {
23211
23891
  },
23212
23892
  []
23213
23893
  );
23214
- const handleDragEnd = React82__default.useCallback(
23894
+ const handleDragEnd = React85__default.useCallback(
23215
23895
  (event) => {
23216
23896
  const { active, over } = event;
23217
23897
  const activeIdStr = String(active.id);
@@ -23302,8 +23982,8 @@ function useDataDnd(args) {
23302
23982
  },
23303
23983
  [eventBus]
23304
23984
  );
23305
- const sortableData = React82__default.useMemo(() => ({ dndGroup: ownGroup }), [ownGroup]);
23306
- const SortableItem = React82__default.useCallback(
23985
+ const sortableData = React85__default.useMemo(() => ({ dndGroup: ownGroup }), [ownGroup]);
23986
+ const SortableItem = React85__default.useCallback(
23307
23987
  ({ id, children }) => {
23308
23988
  const {
23309
23989
  attributes,
@@ -23343,7 +24023,7 @@ function useDataDnd(args) {
23343
24023
  id: droppableId,
23344
24024
  data: sortableData
23345
24025
  });
23346
- const ctx = React82__default.useContext(RootCtx);
24026
+ const ctx = React85__default.useContext(RootCtx);
23347
24027
  const activeDrag2 = ctx?.activeDrag ?? null;
23348
24028
  const overZoneGroup2 = ctx?.overZoneGroup ?? null;
23349
24029
  const isThisZoneOver = overZoneGroup2 === ownGroup;
@@ -23358,7 +24038,7 @@ function useDataDnd(args) {
23358
24038
  showForeignPlaceholder,
23359
24039
  ctxAvailable: ctx != null
23360
24040
  });
23361
- React82__default.useEffect(() => {
24041
+ React85__default.useEffect(() => {
23362
24042
  dndLog.info("dropzone:isOver:change", { droppableId, group: ownGroup, isOver, isThisZoneOver, showForeignPlaceholder, activeDragSourceGroup: activeDrag2?.sourceGroup ?? null });
23363
24043
  }, [droppableId, isOver, isThisZoneOver, showForeignPlaceholder]);
23364
24044
  return /* @__PURE__ */ jsx(
@@ -23372,11 +24052,11 @@ function useDataDnd(args) {
23372
24052
  }
23373
24053
  );
23374
24054
  };
23375
- const rootContextValue = React82__default.useMemo(
24055
+ const rootContextValue = React85__default.useMemo(
23376
24056
  () => ({ registerZone, unregisterZone, activeDrag, overZoneGroup, optimisticOrders, clearOptimisticOrder }),
23377
24057
  [registerZone, unregisterZone, activeDrag, overZoneGroup, optimisticOrders, clearOptimisticOrder]
23378
24058
  );
23379
- const handleDragStart = React82__default.useCallback((event) => {
24059
+ const handleDragStart = React85__default.useCallback((event) => {
23380
24060
  const sourceZone = findZoneByItem(event.active.id);
23381
24061
  const rect = event.active.rect.current.initial;
23382
24062
  const height = rect?.height && rect.height > 0 ? rect.height : 64;
@@ -23395,7 +24075,7 @@ function useDataDnd(args) {
23395
24075
  isRoot
23396
24076
  });
23397
24077
  }, [findZoneByItem, isRoot, zoneId]);
23398
- const handleDragOver = React82__default.useCallback((event) => {
24078
+ const handleDragOver = React85__default.useCallback((event) => {
23399
24079
  const { active, over } = event;
23400
24080
  const overData = over?.data?.current;
23401
24081
  const overGroup = overData?.dndGroup ?? null;
@@ -23465,7 +24145,7 @@ function useDataDnd(args) {
23465
24145
  return next;
23466
24146
  });
23467
24147
  }, []);
23468
- const handleDragCancel = React82__default.useCallback((event) => {
24148
+ const handleDragCancel = React85__default.useCallback((event) => {
23469
24149
  setActiveDrag(null);
23470
24150
  setOverZoneGroup(null);
23471
24151
  dndLog.warn("dragCancel", {
@@ -23473,12 +24153,12 @@ function useDataDnd(args) {
23473
24153
  reason: "dnd-kit cancelled the drag (escape key, pointer interrupted, or external)"
23474
24154
  });
23475
24155
  }, []);
23476
- const handleDragEndWithCleanup = React82__default.useCallback((event) => {
24156
+ const handleDragEndWithCleanup = React85__default.useCallback((event) => {
23477
24157
  handleDragEnd(event);
23478
24158
  setActiveDrag(null);
23479
24159
  setOverZoneGroup(null);
23480
24160
  }, [handleDragEnd]);
23481
- const wrapContainer = React82__default.useCallback(
24161
+ const wrapContainer = React85__default.useCallback(
23482
24162
  (children) => {
23483
24163
  if (!enabled) return children;
23484
24164
  const strategy = layout === "grid" ? rectSortingStrategy : verticalListSortingStrategy;
@@ -23532,7 +24212,7 @@ var init_useDataDnd = __esm({
23532
24212
  init_useAlmadarDndCollision();
23533
24213
  init_Box();
23534
24214
  dndLog = createLogger("almadar:ui:dnd");
23535
- RootCtx = React82__default.createContext(null);
24215
+ RootCtx = React85__default.createContext(null);
23536
24216
  }
23537
24217
  });
23538
24218
  function renderIconInput(icon, props) {
@@ -24058,7 +24738,7 @@ function DataList({
24058
24738
  }) {
24059
24739
  const eventBus = useEventBus();
24060
24740
  const { t } = useTranslate();
24061
- const [visibleCount, setVisibleCount] = React82__default.useState(pageSize || Infinity);
24741
+ const [visibleCount, setVisibleCount] = React85__default.useState(pageSize || Infinity);
24062
24742
  const fieldDefs = fields ?? columns ?? [];
24063
24743
  const allDataRaw = Array.isArray(entity) ? entity : entity ? [entity] : [];
24064
24744
  const dnd = useDataDnd({
@@ -24077,7 +24757,7 @@ function DataList({
24077
24757
  const data = pageSize > 0 ? allData.slice(0, visibleCount) : allData;
24078
24758
  const hasMoreLocal = pageSize > 0 && visibleCount < allData.length;
24079
24759
  const hasRenderProp = typeof children === "function";
24080
- React82__default.useEffect(() => {
24760
+ React85__default.useEffect(() => {
24081
24761
  const renderItemTypeOf = typeof schemaRenderItem;
24082
24762
  const childrenTypeOf = typeof children;
24083
24763
  if (data.length > 0 && !hasRenderProp) {
@@ -24182,7 +24862,7 @@ function DataList({
24182
24862
  const items2 = data.map((item) => item);
24183
24863
  const groups2 = groupBy ? groupData(items2, groupBy) : [{ label: "", items: items2 }];
24184
24864
  const contentField = titleField?.name ?? fieldDefs[0]?.name ?? "";
24185
- 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: [
24186
24866
  group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: "my-2" }),
24187
24867
  group.items.map((itemData, index) => {
24188
24868
  const id = itemData.id || `${gi}-${index}`;
@@ -24323,7 +25003,7 @@ function DataList({
24323
25003
  className
24324
25004
  ),
24325
25005
  children: [
24326
- groups.map((group, gi) => /* @__PURE__ */ jsxs(React82__default.Fragment, { children: [
25006
+ groups.map((group, gi) => /* @__PURE__ */ jsxs(React85__default.Fragment, { children: [
24327
25007
  group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: gi > 0 ? "mt-4" : "mt-0" }),
24328
25008
  group.items.map(
24329
25009
  (itemData, index) => renderItem(itemData, index, gi === groups.length - 1 && index === group.items.length - 1)
@@ -25239,7 +25919,7 @@ var init_Grid = __esm({
25239
25919
  as: Component2 = "div"
25240
25920
  }) => {
25241
25921
  const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
25242
- return React82__default.createElement(
25922
+ return React85__default.createElement(
25243
25923
  Component2,
25244
25924
  {
25245
25925
  className: cn(
@@ -25427,8 +26107,8 @@ var init_Popover = __esm({
25427
26107
  onMouseEnter: handleOpen,
25428
26108
  onMouseLeave: handleClose
25429
26109
  };
25430
- const childElement = React82__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
25431
- const triggerElement = React82__default.cloneElement(
26110
+ const childElement = React85__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
26111
+ const triggerElement = React85__default.cloneElement(
25432
26112
  childElement,
25433
26113
  {
25434
26114
  ref: triggerRef,
@@ -25484,9 +26164,9 @@ function debug(...args) {
25484
26164
  const [first, ...rest] = args;
25485
26165
  const message = typeof first === "string" ? first : "<debug>";
25486
26166
  if (rest.length === 0 && typeof first === "string") {
25487
- log10.debug(message);
26167
+ log9.debug(message);
25488
26168
  } else {
25489
- log10.debug(message, { args: rest.length > 0 ? formatArgs(rest) : formatArgs([first]) });
26169
+ log9.debug(message, { args: rest.length > 0 ? formatArgs(rest) : formatArgs([first]) });
25490
26170
  }
25491
26171
  }
25492
26172
  function debugGroup(label) {
@@ -25514,11 +26194,11 @@ function toLogMetaValue(v) {
25514
26194
  }
25515
26195
  return String(v);
25516
26196
  }
25517
- var NAMESPACE, log10;
26197
+ var NAMESPACE, log9;
25518
26198
  var init_debug = __esm({
25519
26199
  "lib/debug.ts"() {
25520
26200
  NAMESPACE = "almadar:ui:debug";
25521
- log10 = createLogger(NAMESPACE);
26201
+ log9 = createLogger(NAMESPACE);
25522
26202
  createLogger("almadar:ui:debug:input");
25523
26203
  createLogger("almadar:ui:debug:collision");
25524
26204
  createLogger("almadar:ui:debug:physics");
@@ -26018,8 +26698,8 @@ var init_Tooltip = __esm({
26018
26698
  if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);
26019
26699
  };
26020
26700
  }, []);
26021
- const triggerElement = React82__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
26022
- 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, {
26023
26703
  ref: triggerRef,
26024
26704
  onMouseEnter: handleMouseEnter,
26025
26705
  onMouseLeave: handleMouseLeave,
@@ -26105,7 +26785,7 @@ var init_WizardProgress = __esm({
26105
26785
  children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: normalizedSteps.map((step, index) => {
26106
26786
  const isActive = index === currentStep;
26107
26787
  const isCompleted = index < currentStep;
26108
- return /* @__PURE__ */ jsxs(React82__default.Fragment, { children: [
26788
+ return /* @__PURE__ */ jsxs(React85__default.Fragment, { children: [
26109
26789
  /* @__PURE__ */ jsx(
26110
26790
  "button",
26111
26791
  {
@@ -27066,7 +27746,7 @@ var init_ProgressDots = __esm({
27066
27746
  }
27067
27747
  });
27068
27748
  function StatBadge({
27069
- assetUrl = DEFAULT_ASSET_URL12,
27749
+ assetUrl,
27070
27750
  label,
27071
27751
  value = 0,
27072
27752
  max,
@@ -27133,7 +27813,7 @@ function StatBadge({
27133
27813
  }
27134
27814
  );
27135
27815
  }
27136
- var sizeMap17, variantMap2, DEFAULT_ASSET_URL12;
27816
+ var sizeMap17, variantMap2;
27137
27817
  var init_StatBadge = __esm({
27138
27818
  "components/game/molecules/StatBadge.tsx"() {
27139
27819
  init_cn();
@@ -27152,7 +27832,6 @@ var init_StatBadge = __esm({
27152
27832
  warning: "bg-warning/15 border-warning/40 text-foreground",
27153
27833
  danger: "bg-error/15 border-error/40 text-foreground"
27154
27834
  };
27155
- DEFAULT_ASSET_URL12 = "https://almadar-kflow-assets.web.app/shared/effects/particles/light_01.png";
27156
27835
  StatBadge.displayName = "StatBadge";
27157
27836
  }
27158
27837
  });
@@ -27169,7 +27848,7 @@ function InventoryGrid({
27169
27848
  const eventBus = useEventBus();
27170
27849
  const slotCount = totalSlots ?? items.length;
27171
27850
  const emptySlotCount = Math.max(0, slotCount - items.length);
27172
- const handleSelect = React82.useCallback(
27851
+ const handleSelect = React85.useCallback(
27173
27852
  (id) => {
27174
27853
  onSelect?.(id);
27175
27854
  if (selectEvent) {
@@ -27400,35 +28079,37 @@ function GameCanvas2D({
27400
28079
  drawEvent,
27401
28080
  fps = 60,
27402
28081
  backgroundImage = "",
27403
- assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/",
28082
+ assetBaseUrl,
27404
28083
  className
27405
28084
  }) {
27406
- const canvasRef = React82.useRef(null);
27407
- const rafRef = React82.useRef(0);
27408
- const frameRef = React82.useRef(0);
27409
- const lastTimeRef = React82.useRef(0);
27410
- 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());
27411
28090
  const emit = useEmitEvent();
27412
- const onDrawRef = React82.useRef(onDraw);
28091
+ const onDrawRef = React85.useRef(onDraw);
27413
28092
  onDrawRef.current = onDraw;
27414
- const onTickRef = React82.useRef(onTick);
28093
+ const onTickRef = React85.useRef(onTick);
27415
28094
  onTickRef.current = onTick;
27416
- const tickEventRef = React82.useRef(tickEvent);
28095
+ const tickEventRef = React85.useRef(tickEvent);
27417
28096
  tickEventRef.current = tickEvent;
27418
- const drawEventRef = React82.useRef(drawEvent);
28097
+ const drawEventRef = React85.useRef(drawEvent);
27419
28098
  drawEventRef.current = drawEvent;
27420
- const emitRef = React82.useRef(emit);
28099
+ const emitRef = React85.useRef(emit);
27421
28100
  emitRef.current = emit;
27422
- const assetBaseUrlRef = React82.useRef(assetBaseUrl);
28101
+ const assetBaseUrlRef = React85.useRef(assetBaseUrl);
27423
28102
  assetBaseUrlRef.current = assetBaseUrl;
27424
- const backgroundImageRef = React82.useRef(backgroundImage);
28103
+ const backgroundImageRef = React85.useRef(backgroundImage);
27425
28104
  backgroundImageRef.current = backgroundImage;
27426
- const widthRef = React82.useRef(width);
28105
+ const widthRef = React85.useRef(width);
27427
28106
  widthRef.current = width;
27428
- const heightRef = React82.useRef(height);
28107
+ const heightRef = React85.useRef(height);
27429
28108
  heightRef.current = height;
27430
- const loadImage = React82.useCallback((url) => {
27431
- 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}`;
27432
28113
  const cached = imageCache.current.get(fullUrl);
27433
28114
  if (cached?.complete && cached.naturalWidth > 0) return cached;
27434
28115
  if (!cached) {
@@ -27439,7 +28120,7 @@ function GameCanvas2D({
27439
28120
  }
27440
28121
  return null;
27441
28122
  }, []);
27442
- React82.useEffect(() => {
28123
+ React85.useEffect(() => {
27443
28124
  const canvas = canvasRef.current;
27444
28125
  if (!canvas) return;
27445
28126
  const ctx = canvas.getContext("2d");
@@ -27751,7 +28432,7 @@ function TurnPanel({
27751
28432
  className
27752
28433
  }) {
27753
28434
  const eventBus = useEventBus();
27754
- const handleAction = React82.useCallback(
28435
+ const handleAction = React85.useCallback(
27755
28436
  (event) => {
27756
28437
  if (event) {
27757
28438
  eventBus.emit(event, { turn: currentTurn, phase, activeTeam });
@@ -27809,7 +28490,7 @@ var init_TurnPanel = __esm({
27809
28490
  }
27810
28491
  });
27811
28492
  function EnemyPlate({
27812
- assetUrl = DEFAULT_ASSET_URL13,
28493
+ assetUrl,
27813
28494
  name = "Shadow Guard",
27814
28495
  health = 80,
27815
28496
  maxHealth = 100,
@@ -27893,7 +28574,7 @@ function EnemyPlate({
27893
28574
  }
27894
28575
  );
27895
28576
  }
27896
- var effectVariantMap2, DEFAULT_ENEMY_EFFECTS, DEFAULT_ASSET_URL13;
28577
+ var effectVariantMap2, DEFAULT_ENEMY_EFFECTS;
27897
28578
  var init_EnemyPlate = __esm({
27898
28579
  "components/game/molecules/EnemyPlate.tsx"() {
27899
28580
  "use client";
@@ -27910,7 +28591,6 @@ var init_EnemyPlate = __esm({
27910
28591
  DEFAULT_ENEMY_EFFECTS = [
27911
28592
  { icon: "flame", label: "Burn", variant: "debuff" }
27912
28593
  ];
27913
- DEFAULT_ASSET_URL13 = "https://almadar-kflow-assets.web.app/shared/portraits/shadow-legion.png";
27914
28594
  EnemyPlate.displayName = "EnemyPlate";
27915
28595
  }
27916
28596
  });
@@ -27920,7 +28600,7 @@ function UnitCommandBar({
27920
28600
  className
27921
28601
  }) {
27922
28602
  const eventBus = useEventBus();
27923
- const handleCommand = React82.useCallback(
28603
+ const handleCommand = React85.useCallback(
27924
28604
  (event) => {
27925
28605
  if (event) {
27926
28606
  eventBus.emit(event, { unitId: selectedUnitId });
@@ -28061,6 +28741,9 @@ function DialogueBox({
28061
28741
  completeEvent,
28062
28742
  choiceEvent,
28063
28743
  advanceEvent,
28744
+ backgroundImage,
28745
+ portraitUrl,
28746
+ portraitScale = 1,
28064
28747
  className
28065
28748
  }) {
28066
28749
  const eventBus = useEventBus();
@@ -28176,64 +28859,98 @@ function DialogueBox({
28176
28859
  }
28177
28860
  }, [isTyping, skipTypewriter, dialogue.choices, selectedChoice, onChoice, onAdvance, choiceEvent, advanceEvent, eventBus]);
28178
28861
  const enabledChoices = dialogue.choices?.filter((c) => !c.disabled) ?? [];
28179
- return /* @__PURE__ */ jsx(
28180
- "div",
28181
- {
28182
- className: cn(
28183
- "fixed left-0 right-0 z-40",
28184
- position === "top" ? "top-0" : "bottom-0",
28185
- className
28186
- ),
28187
- onClick: handleClick,
28188
- onKeyDown: handleKeyDown,
28189
- tabIndex: 0,
28190
- role: "dialog",
28191
- "aria-label": "Dialogue",
28192
- 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: [
28193
- 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(
28194
28880
  "img",
28195
28881
  {
28196
- src: dialogue.portrait,
28882
+ src: portraitUrl,
28197
28883
  alt: dialogue.speaker,
28198
- className: "w-24 h-24 object-contain",
28199
- style: { imageRendering: "pixelated" }
28884
+ className: "h-full w-auto object-contain drop-shadow-2xl"
28200
28885
  }
28201
- ) }),
28202
- /* @__PURE__ */ jsxs("div", { className: "flex-1 p-4", children: [
28203
- /* @__PURE__ */ jsx("div", { className: "text-warning font-bold mb-2", children: dialogue.speaker }),
28204
- /* @__PURE__ */ jsxs("div", { className: "text-[var(--color-foreground)] text-lg leading-relaxed min-h-[60px]", children: [
28205
- displayedText,
28206
- isTyping && /* @__PURE__ */ jsx("span", { className: "animate-pulse", children: "\u258C" })
28207
- ] }),
28208
- !isTyping && enabledChoices.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-2", children: enabledChoices.map((choice, index) => /* @__PURE__ */ jsxs(
28209
- "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",
28210
28908
  {
28211
- type: "button",
28212
- className: cn(
28213
- "block w-full text-left px-4 py-2 rounded-interactive transition-colors",
28214
- "hover:bg-[var(--color-surface,#374151)] focus:outline-none focus:ring-2 focus:ring-warning",
28215
- selectedChoice === index ? "bg-[var(--color-surface,#374151)] text-warning" : "bg-[var(--color-card)] text-[var(--color-foreground)]"
28216
- ),
28217
- onClick: (e) => {
28218
- e.stopPropagation();
28219
- if (choiceEvent) eventBus.emit(`UI:${choiceEvent}`, { choice });
28220
- 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
+ ]
28221
28945
  },
28222
- children: [
28223
- /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground mr-2", children: [
28224
- index + 1,
28225
- "."
28226
- ] }),
28227
- choice.text
28228
- ]
28229
- },
28230
- index
28231
- )) }),
28232
- !isTyping && !dialogue.choices?.length && /* @__PURE__ */ jsx("div", { className: "mt-4 text-muted-foreground text-sm animate-pulse", children: "Press SPACE or click to continue..." })
28233
- ] })
28234
- ] }) })
28235
- }
28236
- );
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
+ ] });
28237
28954
  }
28238
28955
  var DEFAULT_DIALOGUE;
28239
28956
  var init_DialogueBox = __esm({
@@ -28434,7 +29151,7 @@ function GameMenu({
28434
29151
  } catch {
28435
29152
  }
28436
29153
  const eventBus = eventBusProp || eventBusFromHook;
28437
- const handleOptionClick = React82.useCallback(
29154
+ const handleOptionClick = React85.useCallback(
28438
29155
  (option) => {
28439
29156
  if (option.event && eventBus) {
28440
29157
  eventBus.emit(`UI:${option.event}`, { option });
@@ -28553,7 +29270,7 @@ function GameOverScreen({
28553
29270
  } catch {
28554
29271
  }
28555
29272
  const eventBus = eventBusProp || eventBusFromHook;
28556
- const handleActionClick = React82.useCallback(
29273
+ const handleActionClick = React85.useCallback(
28557
29274
  (action) => {
28558
29275
  if (action.event && eventBus) {
28559
29276
  eventBus.emit(`UI:${action.event}`, { action });
@@ -28760,10 +29477,10 @@ function PlatformerCanvas({
28760
29477
  canvasHeight = 400,
28761
29478
  followCamera = true,
28762
29479
  bgColor = "#5c94fc",
28763
- playerSprite = "https://almadar-kflow-assets.web.app/shared/platformer/characters/platformChar_idle.png",
29480
+ playerSprite,
28764
29481
  tileSprites,
28765
29482
  backgroundImage = "",
28766
- assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/platformer/",
29483
+ assetBaseUrl,
28767
29484
  leftEvent = "MOVE_LEFT",
28768
29485
  rightEvent = "MOVE_RIGHT",
28769
29486
  jumpEvent = "JUMP",
@@ -28777,6 +29494,7 @@ function PlatformerCanvas({
28777
29494
  const imageCache = useRef(/* @__PURE__ */ new Map());
28778
29495
  const [loadedImages, setLoadedImages] = useState(/* @__PURE__ */ new Set());
28779
29496
  const loadImage = useCallback((url) => {
29497
+ if (!url.startsWith("http") && !assetBaseUrl) return null;
28780
29498
  const fullUrl = url.startsWith("http") ? url : `${assetBaseUrl}${url}`;
28781
29499
  const cached = imageCache.current.get(fullUrl);
28782
29500
  if (cached?.complete && cached.naturalWidth > 0) {
@@ -29475,13 +30193,13 @@ var init_MapView = __esm({
29475
30193
  shadowSize: [41, 41]
29476
30194
  });
29477
30195
  L.Marker.prototype.options.icon = defaultIcon;
29478
- const { useEffect: useEffect80, useRef: useRef75, useCallback: useCallback118, useState: useState109 } = React82__default;
30196
+ const { useEffect: useEffect81, useRef: useRef78, useCallback: useCallback123, useState: useState110 } = React85__default;
29479
30197
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
29480
30198
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
29481
30199
  function MapUpdater({ centerLat, centerLng, zoom }) {
29482
30200
  const map = useMap();
29483
- const prevRef = useRef75({ centerLat, centerLng, zoom });
29484
- useEffect80(() => {
30201
+ const prevRef = useRef78({ centerLat, centerLng, zoom });
30202
+ useEffect81(() => {
29485
30203
  const prev = prevRef.current;
29486
30204
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
29487
30205
  map.setView([centerLat, centerLng], zoom);
@@ -29492,7 +30210,7 @@ var init_MapView = __esm({
29492
30210
  }
29493
30211
  function MapClickHandler({ onMapClick }) {
29494
30212
  const map = useMap();
29495
- useEffect80(() => {
30213
+ useEffect81(() => {
29496
30214
  if (!onMapClick) return;
29497
30215
  const handler = (e) => {
29498
30216
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -29520,8 +30238,8 @@ var init_MapView = __esm({
29520
30238
  showAttribution = true
29521
30239
  }) {
29522
30240
  const eventBus = useEventBus3();
29523
- const [clickedPosition, setClickedPosition] = useState109(null);
29524
- const handleMapClick = useCallback118((lat, lng) => {
30241
+ const [clickedPosition, setClickedPosition] = useState110(null);
30242
+ const handleMapClick = useCallback123((lat, lng) => {
29525
30243
  if (showClickedPin) {
29526
30244
  setClickedPosition({ lat, lng });
29527
30245
  }
@@ -29530,7 +30248,7 @@ var init_MapView = __esm({
29530
30248
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
29531
30249
  }
29532
30250
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
29533
- const handleMarkerClick = useCallback118((marker) => {
30251
+ const handleMarkerClick = useCallback123((marker) => {
29534
30252
  onMarkerClick?.(marker);
29535
30253
  if (markerClickEvent) {
29536
30254
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -30381,8 +31099,8 @@ function TableView({
30381
31099
  }) {
30382
31100
  const eventBus = useEventBus();
30383
31101
  const { t } = useTranslate();
30384
- const [visibleCount, setVisibleCount] = React82__default.useState(pageSize > 0 ? pageSize : Infinity);
30385
- 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());
30386
31104
  const colDefs = columns ?? fields ?? [];
30387
31105
  const allDataRaw = Array.isArray(entity) ? entity : entity ? [entity] : [];
30388
31106
  const dnd = useDataDnd({
@@ -30577,12 +31295,12 @@ function TableView({
30577
31295
  ]
30578
31296
  }
30579
31297
  );
30580
- 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);
30581
31299
  };
30582
31300
  const items = data.map((row) => row);
30583
31301
  const groups = groupBy ? groupData2(items, groupBy) : [{ label: "", items }];
30584
31302
  let runningIndex = 0;
30585
- 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: [
30586
31304
  group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: gi > 0 ? "mt-3" : "mt-0" }),
30587
31305
  group.items.map((row) => renderRow(row, runningIndex++))
30588
31306
  ] }, gi)) });
@@ -31939,7 +32657,7 @@ var init_StepFlow = __esm({
31939
32657
  className
31940
32658
  }) => {
31941
32659
  if (orientation === "vertical") {
31942
- 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: [
31943
32661
  /* @__PURE__ */ jsxs(VStack, { gap: "none", align: "center", children: [
31944
32662
  /* @__PURE__ */ jsx(StepCircle, { step, index }),
31945
32663
  showConnectors && index < steps.length - 1 && /* @__PURE__ */ jsx(Box, { className: "w-px h-8 bg-border" })
@@ -31950,7 +32668,7 @@ var init_StepFlow = __esm({
31950
32668
  ] })
31951
32669
  ] }) }, index)) });
31952
32670
  }
31953
- 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: [
31954
32672
  /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", className: "flex-1 w-full md:w-auto", children: [
31955
32673
  /* @__PURE__ */ jsx(StepCircle, { step, index }),
31956
32674
  /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-center", children: step.title }),
@@ -32935,7 +33653,7 @@ var init_LikertScale = __esm({
32935
33653
  md: "text-base",
32936
33654
  lg: "text-lg"
32937
33655
  };
32938
- LikertScale = React82__default.forwardRef(
33656
+ LikertScale = React85__default.forwardRef(
32939
33657
  ({
32940
33658
  question,
32941
33659
  options = DEFAULT_LIKERT_OPTIONS,
@@ -32947,7 +33665,7 @@ var init_LikertScale = __esm({
32947
33665
  variant = "radios",
32948
33666
  className
32949
33667
  }, ref) => {
32950
- const groupId = React82__default.useId();
33668
+ const groupId = React85__default.useId();
32951
33669
  const eventBus = useEventBus();
32952
33670
  const handleSelect = useCallback(
32953
33671
  (next) => {
@@ -35229,7 +35947,7 @@ var init_DocBreadcrumb = __esm({
35229
35947
  "aria-label": t("aria.breadcrumb"),
35230
35948
  children: /* @__PURE__ */ jsx(HStack, { gap: "xs", align: "center", wrap: true, children: items.map((item, idx) => {
35231
35949
  const isLast = idx === items.length - 1;
35232
- return /* @__PURE__ */ jsxs(React82__default.Fragment, { children: [
35950
+ return /* @__PURE__ */ jsxs(React85__default.Fragment, { children: [
35233
35951
  idx > 0 && /* @__PURE__ */ jsx(
35234
35952
  Icon,
35235
35953
  {
@@ -36098,7 +36816,7 @@ var init_MiniStateMachine = __esm({
36098
36816
  const x = 2 + i * (NODE_W + GAP + ARROW_W + GAP);
36099
36817
  const tc = transitionCounts[s.name] ?? 0;
36100
36818
  const role = getStateRole(s.name, s.isInitial, s.isTerminal, tc, maxTC);
36101
- return /* @__PURE__ */ jsxs(React82__default.Fragment, { children: [
36819
+ return /* @__PURE__ */ jsxs(React85__default.Fragment, { children: [
36102
36820
  /* @__PURE__ */ jsx(
36103
36821
  AvlState,
36104
36822
  {
@@ -36302,7 +37020,7 @@ var init_PageHeader = __esm({
36302
37020
  info: "bg-info/10 text-info"
36303
37021
  };
36304
37022
  return /* @__PURE__ */ jsxs(Box, { className: cn("mb-6", className), children: [
36305
- 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: [
36306
37024
  idx > 0 && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: "/" }),
36307
37025
  crumb.href ? /* @__PURE__ */ jsx(
36308
37026
  "a",
@@ -36984,8 +37702,8 @@ var init_WizardContainer = __esm({
36984
37702
  return void 0;
36985
37703
  if (typeof controlledStep === "number") return controlledStep;
36986
37704
  if (typeof controlledStep === "string") return parseInt(controlledStep, 10);
36987
- const num2 = Number(controlledStep);
36988
- return isNaN(num2) ? void 0 : num2;
37705
+ const num3 = Number(controlledStep);
37706
+ return isNaN(num3) ? void 0 : num3;
36989
37707
  })();
36990
37708
  const currentStep = normalizedControlledStep !== void 0 ? normalizedControlledStep : internalStep;
36991
37709
  const totalSteps = steps.length;
@@ -37031,7 +37749,7 @@ var init_WizardContainer = __esm({
37031
37749
  const isCompleted = index < currentStep;
37032
37750
  const stepKey = step.id ?? step.tabId ?? `step-${index}`;
37033
37751
  const stepTitle = step.title ?? step.name ?? `Step ${index + 1}`;
37034
- return /* @__PURE__ */ jsxs(React82__default.Fragment, { children: [
37752
+ return /* @__PURE__ */ jsxs(React85__default.Fragment, { children: [
37035
37753
  /* @__PURE__ */ jsx(
37036
37754
  Button,
37037
37755
  {
@@ -37958,10 +38676,13 @@ var init_GraphCanvas = __esm({
37958
38676
  draggable = true,
37959
38677
  actions,
37960
38678
  onNodeClick,
38679
+ onNodeDoubleClick,
37961
38680
  nodeClickEvent,
37962
38681
  selectedNodeId,
37963
38682
  repulsion = 800,
37964
38683
  linkDistance = 100,
38684
+ nodeSpacing = 28,
38685
+ linkOpacity = 0.18,
37965
38686
  layout = "force",
37966
38687
  entity,
37967
38688
  isLoading = false,
@@ -37977,6 +38698,19 @@ var init_GraphCanvas = __esm({
37977
38698
  const [hoveredNode, setHoveredNode] = useState(null);
37978
38699
  const nodesRef = useRef([]);
37979
38700
  const [, forceUpdate] = useState(0);
38701
+ const [logicalW, setLogicalW] = useState(800);
38702
+ useEffect(() => {
38703
+ const canvas = canvasRef.current;
38704
+ if (!canvas || typeof ResizeObserver === "undefined") return;
38705
+ const measure = () => {
38706
+ const width = Math.round(canvas.clientWidth);
38707
+ if (width > 0) setLogicalW(width);
38708
+ };
38709
+ measure();
38710
+ const ro = new ResizeObserver(measure);
38711
+ ro.observe(canvas);
38712
+ return () => ro.disconnect();
38713
+ }, []);
37980
38714
  const interactionRef = useRef({
37981
38715
  mode: "none",
37982
38716
  dragNodeId: null,
@@ -37989,10 +38723,8 @@ var init_GraphCanvas = __esm({
37989
38723
  const canvas = canvasRef.current;
37990
38724
  if (!canvas) return null;
37991
38725
  const rect = canvas.getBoundingClientRect();
37992
- const scaleX = rect.width > 0 ? canvas.width / rect.width : 1;
37993
- const scaleY = rect.height > 0 ? canvas.height / rect.height : 1;
37994
- const screenX = (e.clientX - rect.left) * scaleX;
37995
- const screenY = (e.clientY - rect.top) * scaleY;
38726
+ const screenX = e.clientX - rect.left;
38727
+ const screenY = e.clientY - rect.top;
37996
38728
  return {
37997
38729
  screenX,
37998
38730
  screenY,
@@ -38032,8 +38764,8 @@ var init_GraphCanvas = __esm({
38032
38764
  useEffect(() => {
38033
38765
  const canvas = canvasRef.current;
38034
38766
  if (!canvas || propNodes.length === 0) return;
38035
- const w = canvas.width;
38036
- const h = canvas.height;
38767
+ const w = logicalW;
38768
+ const h = height;
38037
38769
  const simNodes = propNodes.map((n, idx) => {
38038
38770
  let x = n.x ?? 0;
38039
38771
  let y = n.y ?? 0;
@@ -38110,6 +38842,25 @@ var init_GraphCanvas = __esm({
38110
38842
  node.x = Math.max(30, Math.min(w - 30, node.x));
38111
38843
  node.y = Math.max(30, Math.min(h - 30, node.y));
38112
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
+ }
38113
38864
  iterations++;
38114
38865
  forceUpdate((n) => n + 1);
38115
38866
  if (iterations < maxIterations) {
@@ -38123,29 +38874,41 @@ var init_GraphCanvas = __esm({
38123
38874
  return () => {
38124
38875
  cancelAnimationFrame(animRef.current);
38125
38876
  };
38126
- }, [propNodes, propEdges, layout, repulsion, linkDistance]);
38877
+ }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, logicalW, height]);
38127
38878
  useEffect(() => {
38128
38879
  const canvas = canvasRef.current;
38129
38880
  if (!canvas) return;
38130
38881
  const ctx = canvas.getContext("2d");
38131
38882
  if (!ctx) return;
38132
- const w = canvas.width;
38133
- const h = canvas.height;
38883
+ const w = logicalW;
38884
+ const h = height;
38134
38885
  const nodes = nodesRef.current;
38135
38886
  const accentColor = resolveColor2("var(--color-accent)", canvas);
38887
+ const dpr = typeof window !== "undefined" && window.devicePixelRatio || 1;
38888
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
38136
38889
  ctx.clearRect(0, 0, w, h);
38137
38890
  ctx.save();
38138
38891
  ctx.translate(offset.x, offset.y);
38139
38892
  ctx.scale(zoom, zoom);
38893
+ const neighbors = /* @__PURE__ */ new Set();
38894
+ if (hoveredNode) {
38895
+ neighbors.add(hoveredNode);
38896
+ for (const edge of propEdges) {
38897
+ if (edge.source === hoveredNode) neighbors.add(String(edge.target));
38898
+ else if (edge.target === hoveredNode) neighbors.add(String(edge.source));
38899
+ }
38900
+ }
38140
38901
  for (const edge of propEdges) {
38141
38902
  const source = nodes.find((n) => n.id === edge.source);
38142
38903
  const target = nodes.find((n) => n.id === edge.target);
38143
38904
  if (!source || !target) continue;
38905
+ const incident = !!hoveredNode && (edge.source === hoveredNode || edge.target === hoveredNode);
38906
+ ctx.globalAlpha = hoveredNode ? incident ? 1 : 0.05 : linkOpacity;
38144
38907
  ctx.beginPath();
38145
38908
  ctx.moveTo(source.x, source.y);
38146
38909
  ctx.lineTo(target.x, target.y);
38147
- ctx.strokeStyle = edge.color || "#88888866";
38148
- ctx.lineWidth = edge.weight ? Math.max(1, edge.weight) : 1;
38910
+ ctx.strokeStyle = incident ? accentColor : edge.color || "#888888";
38911
+ ctx.lineWidth = incident ? 2 : edge.weight ? Math.max(1, edge.weight) : 1;
38149
38912
  ctx.stroke();
38150
38913
  if (edge.label && showLabels) {
38151
38914
  const mx = (source.x + target.x) / 2;
@@ -38156,11 +38919,13 @@ var init_GraphCanvas = __esm({
38156
38919
  ctx.fillText(edge.label, mx, my - 4);
38157
38920
  }
38158
38921
  }
38922
+ ctx.globalAlpha = 1;
38159
38923
  for (const node of nodes) {
38160
38924
  const size = node.size || 8;
38161
38925
  const color = resolveColor2(node.color || getGroupColor(node.group, groups), canvas);
38162
38926
  const isHovered = hoveredNode === node.id;
38163
38927
  const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
38928
+ ctx.globalAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : 1;
38164
38929
  const radius = isSelected ? size + 4 : isHovered ? size + 2 : size;
38165
38930
  ctx.beginPath();
38166
38931
  ctx.arc(node.x, node.y, radius, 0, Math.PI * 2);
@@ -38280,6 +39045,15 @@ var init_GraphCanvas = __esm({
38280
39045
  interactionRef.current.dragNodeId = null;
38281
39046
  setHoveredNode(null);
38282
39047
  }, []);
39048
+ const handleDoubleClick = useCallback(
39049
+ (e) => {
39050
+ const coords = toCoords(e);
39051
+ if (!coords) return;
39052
+ const node = nodeAt(coords.graphX, coords.graphY);
39053
+ if (node) onNodeDoubleClick?.(node);
39054
+ },
39055
+ [toCoords, nodeAt, onNodeDoubleClick]
39056
+ );
38283
39057
  if (isLoading) {
38284
39058
  return /* @__PURE__ */ jsx(LoadingState, { message: t("common.loading"), className });
38285
39059
  }
@@ -38337,15 +39111,16 @@ var init_GraphCanvas = __esm({
38337
39111
  "canvas",
38338
39112
  {
38339
39113
  ref: canvasRef,
38340
- width: 800,
38341
- height,
39114
+ width: Math.round(logicalW * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
39115
+ height: Math.round(height * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
38342
39116
  className: "w-full cursor-grab active:cursor-grabbing",
38343
39117
  style: { height },
38344
39118
  onWheel: handleWheel,
38345
39119
  onMouseDown: handleMouseDown,
38346
39120
  onMouseMove: handleMouseMove,
38347
39121
  onMouseUp: handleMouseUp,
38348
- onMouseLeave: handleMouseLeave
39122
+ onMouseLeave: handleMouseLeave,
39123
+ onDoubleClick: handleDoubleClick
38349
39124
  }
38350
39125
  ) }),
38351
39126
  groups.length > 1 && /* @__PURE__ */ jsx(HStack, { gap: "md", className: "px-4 py-2 border-t border-border", wrap: true, children: groups.map((group, idx) => /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
@@ -39496,7 +40271,7 @@ var init_DetailPanel = __esm({
39496
40271
  }
39497
40272
  });
39498
40273
  function extractTitle(children) {
39499
- if (!React82__default.isValidElement(children)) return void 0;
40274
+ if (!React85__default.isValidElement(children)) return void 0;
39500
40275
  const props = children.props;
39501
40276
  if (typeof props.title === "string") {
39502
40277
  return props.title;
@@ -39551,7 +40326,7 @@ function LinearView({
39551
40326
  /* @__PURE__ */ jsx(HStack, { className: "flex-wrap items-center", gap: "xs", children: trait.states.map((state, i) => {
39552
40327
  const isDone = i < currentIdx;
39553
40328
  const isCurrent = i === currentIdx;
39554
- return /* @__PURE__ */ jsxs(React82__default.Fragment, { children: [
40329
+ return /* @__PURE__ */ jsxs(React85__default.Fragment, { children: [
39555
40330
  i > 0 && /* @__PURE__ */ jsx(
39556
40331
  Typography,
39557
40332
  {
@@ -40501,12 +41276,12 @@ var init_Form = __esm({
40501
41276
  const isSchemaEntity = isOrbitalEntitySchema(entity);
40502
41277
  const resolvedEntity = isSchemaEntity ? entity : void 0;
40503
41278
  const entityName = typeof entity === "string" ? entity : resolvedEntity?.name;
40504
- const normalizedInitialData = React82__default.useMemo(() => {
41279
+ const normalizedInitialData = React85__default.useMemo(() => {
40505
41280
  const entityRowAsInitial = isPlainEntityRow(entity) ? entity : void 0;
40506
41281
  const callerInitial = initialData !== null && typeof initialData === "object" && !Array.isArray(initialData) ? initialData : {};
40507
41282
  return entityRowAsInitial !== void 0 ? { ...entityRowAsInitial, ...callerInitial } : callerInitial;
40508
41283
  }, [entity, initialData]);
40509
- const entityDerivedFields = React82__default.useMemo(() => {
41284
+ const entityDerivedFields = React85__default.useMemo(() => {
40510
41285
  if (fields && fields.length > 0) return void 0;
40511
41286
  if (!resolvedEntity) return void 0;
40512
41287
  return resolvedEntity.fields.map(
@@ -40526,16 +41301,16 @@ var init_Form = __esm({
40526
41301
  const conditionalFields = typeof conditionalFieldsRaw === "boolean" ? {} : conditionalFieldsRaw;
40527
41302
  const hiddenCalculations = typeof hiddenCalculationsRaw === "boolean" ? [] : hiddenCalculationsRaw;
40528
41303
  const violationTriggers = typeof violationTriggersRaw === "boolean" ? [] : violationTriggersRaw;
40529
- const [formData, setFormData] = React82__default.useState(
41304
+ const [formData, setFormData] = React85__default.useState(
40530
41305
  normalizedInitialData
40531
41306
  );
40532
- const [collapsedSections, setCollapsedSections] = React82__default.useState(
41307
+ const [collapsedSections, setCollapsedSections] = React85__default.useState(
40533
41308
  /* @__PURE__ */ new Set()
40534
41309
  );
40535
- const [submitError, setSubmitError] = React82__default.useState(null);
40536
- const formRef = React82__default.useRef(null);
41310
+ const [submitError, setSubmitError] = React85__default.useState(null);
41311
+ const formRef = React85__default.useRef(null);
40537
41312
  const formMode = props.mode;
40538
- const mountedRef = React82__default.useRef(false);
41313
+ const mountedRef = React85__default.useRef(false);
40539
41314
  if (!mountedRef.current) {
40540
41315
  mountedRef.current = true;
40541
41316
  debug("forms", "mount", {
@@ -40548,7 +41323,7 @@ var init_Form = __esm({
40548
41323
  });
40549
41324
  }
40550
41325
  const shouldShowCancel = showCancel ?? (fields && fields.length > 0);
40551
- const evalContext = React82__default.useMemo(
41326
+ const evalContext = React85__default.useMemo(
40552
41327
  () => ({
40553
41328
  formValues: formData,
40554
41329
  globalVariables: externalContext?.globalVariables ?? {},
@@ -40557,7 +41332,7 @@ var init_Form = __esm({
40557
41332
  }),
40558
41333
  [formData, externalContext]
40559
41334
  );
40560
- React82__default.useEffect(() => {
41335
+ React85__default.useEffect(() => {
40561
41336
  debug("forms", "initialData-sync", {
40562
41337
  mode: formMode,
40563
41338
  normalizedInitialData,
@@ -40568,7 +41343,7 @@ var init_Form = __esm({
40568
41343
  setFormData(normalizedInitialData);
40569
41344
  }
40570
41345
  }, [normalizedInitialData]);
40571
- const processCalculations = React82__default.useCallback(
41346
+ const processCalculations = React85__default.useCallback(
40572
41347
  (changedFieldId, newFormData) => {
40573
41348
  if (!hiddenCalculations.length) return;
40574
41349
  const context = {
@@ -40593,7 +41368,7 @@ var init_Form = __esm({
40593
41368
  },
40594
41369
  [hiddenCalculations, externalContext, eventBus]
40595
41370
  );
40596
- const checkViolations = React82__default.useCallback(
41371
+ const checkViolations = React85__default.useCallback(
40597
41372
  (changedFieldId, newFormData) => {
40598
41373
  if (!violationTriggers.length) return;
40599
41374
  const context = {
@@ -40631,7 +41406,7 @@ var init_Form = __esm({
40631
41406
  processCalculations(name, newFormData);
40632
41407
  checkViolations(name, newFormData);
40633
41408
  };
40634
- const isFieldVisible = React82__default.useCallback(
41409
+ const isFieldVisible = React85__default.useCallback(
40635
41410
  (fieldName) => {
40636
41411
  const condition = conditionalFields[fieldName];
40637
41412
  if (!condition) return true;
@@ -40639,7 +41414,7 @@ var init_Form = __esm({
40639
41414
  },
40640
41415
  [conditionalFields, evalContext]
40641
41416
  );
40642
- const isSectionVisible = React82__default.useCallback(
41417
+ const isSectionVisible = React85__default.useCallback(
40643
41418
  (section) => {
40644
41419
  if (!section.condition) return true;
40645
41420
  return Boolean(evaluateFormExpression(section.condition, evalContext));
@@ -40715,7 +41490,7 @@ var init_Form = __esm({
40715
41490
  eventBus.emit(`UI:${onCancel}`);
40716
41491
  }
40717
41492
  };
40718
- const renderField = React82__default.useCallback(
41493
+ const renderField = React85__default.useCallback(
40719
41494
  (field) => {
40720
41495
  const fieldName = field.name || field.field;
40721
41496
  if (!fieldName) return null;
@@ -40736,7 +41511,7 @@ var init_Form = __esm({
40736
41511
  [formData, isFieldVisible, relationsData, relationsLoading, isLoading]
40737
41512
  );
40738
41513
  const effectiveFields = entityDerivedFields ?? fields;
40739
- const normalizedFields = React82__default.useMemo(() => {
41514
+ const normalizedFields = React85__default.useMemo(() => {
40740
41515
  if (!effectiveFields || effectiveFields.length === 0) return [];
40741
41516
  return effectiveFields.map((field) => {
40742
41517
  if (typeof field === "string") {
@@ -40759,7 +41534,7 @@ var init_Form = __esm({
40759
41534
  return field;
40760
41535
  });
40761
41536
  }, [effectiveFields, resolvedEntity]);
40762
- const schemaFields = React82__default.useMemo(() => {
41537
+ const schemaFields = React85__default.useMemo(() => {
40763
41538
  if (normalizedFields.length === 0) return null;
40764
41539
  if (isDebugEnabled()) {
40765
41540
  debugGroup(`Form: ${entityName || "unknown"}`);
@@ -40769,7 +41544,7 @@ var init_Form = __esm({
40769
41544
  }
40770
41545
  return normalizedFields.map(renderField).filter(Boolean);
40771
41546
  }, [normalizedFields, renderField, entityName, conditionalFields]);
40772
- const sectionElements = React82__default.useMemo(() => {
41547
+ const sectionElements = React85__default.useMemo(() => {
40773
41548
  if (!sections || sections.length === 0) return null;
40774
41549
  return sections.map((section) => {
40775
41550
  if (!isSectionVisible(section)) {
@@ -41939,11 +42714,11 @@ var init_Canvas3DErrorBoundary = __esm({
41939
42714
  "components/game/molecules/three/components/Canvas3DErrorBoundary.css"() {
41940
42715
  }
41941
42716
  });
41942
- var log11, Canvas3DErrorBoundary;
42717
+ var log10, Canvas3DErrorBoundary;
41943
42718
  var init_Canvas3DErrorBoundary2 = __esm({
41944
42719
  "components/game/molecules/three/components/Canvas3DErrorBoundary.tsx"() {
41945
42720
  init_Canvas3DErrorBoundary();
41946
- log11 = createLogger("almadar:ui:game:canvas3d:error-boundary");
42721
+ log10 = createLogger("almadar:ui:game:canvas3d:error-boundary");
41947
42722
  Canvas3DErrorBoundary = class extends Component {
41948
42723
  constructor(props) {
41949
42724
  super(props);
@@ -41971,8 +42746,8 @@ var init_Canvas3DErrorBoundary2 = __esm({
41971
42746
  componentDidCatch(error, errorInfo) {
41972
42747
  this.setState({ errorInfo });
41973
42748
  this.props.onError?.(error, errorInfo);
41974
- log11.error("Error caught", { error });
41975
- log11.error("Component stack", { componentStack: errorInfo.componentStack ?? "<none>" });
42749
+ log10.error("Error caught", { error });
42750
+ log10.error("Component stack", { componentStack: errorInfo.componentStack ?? "<none>" });
41976
42751
  }
41977
42752
  render() {
41978
42753
  if (this.state.hasError) {
@@ -42035,7 +42810,7 @@ function useGLTFModel(url, resourceBasePath) {
42035
42810
  setState({ model: null, isLoading: false, error: null });
42036
42811
  return;
42037
42812
  }
42038
- log12.debug("Loading", { url });
42813
+ log11.debug("Loading", { url });
42039
42814
  setState((prev) => ({ ...prev, isLoading: true, error: null }));
42040
42815
  const assetRoot = resourceBasePath || detectAssetRoot2(url);
42041
42816
  const loader = new GLTFLoader$1();
@@ -42043,7 +42818,7 @@ function useGLTFModel(url, resourceBasePath) {
42043
42818
  loader.load(
42044
42819
  url,
42045
42820
  (gltf) => {
42046
- log12.debug("Loaded", { url });
42821
+ log11.debug("Loaded", { url });
42047
42822
  setState({
42048
42823
  model: gltf.scene,
42049
42824
  isLoading: false,
@@ -42052,7 +42827,7 @@ function useGLTFModel(url, resourceBasePath) {
42052
42827
  },
42053
42828
  void 0,
42054
42829
  (err) => {
42055
- log12.warn("Failed", { url, error: err instanceof Error ? err : String(err) });
42830
+ log11.warn("Failed", { url, error: err instanceof Error ? err : String(err) });
42056
42831
  setState({
42057
42832
  model: null,
42058
42833
  isLoading: false,
@@ -42089,12 +42864,18 @@ function ModelLoader({
42089
42864
  });
42090
42865
  return cloned;
42091
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]);
42092
42875
  const scaleArray = useMemo(() => {
42093
- if (typeof scale === "number") {
42094
- return [scale, scale, scale];
42095
- }
42096
- return scale;
42097
- }, [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]);
42098
42879
  const rotationRad = useMemo(() => {
42099
42880
  return [
42100
42881
  rotation[0] * Math.PI / 180,
@@ -42168,11 +42949,11 @@ function ModelLoader({
42168
42949
  }
42169
42950
  );
42170
42951
  }
42171
- var log12;
42952
+ var log11;
42172
42953
  var init_ModelLoader = __esm({
42173
42954
  "components/game/molecules/three/components/ModelLoader.tsx"() {
42174
42955
  "use client";
42175
- log12 = createLogger("almadar:ui:game:model-loader");
42956
+ log11 = createLogger("almadar:ui:game:model-loader");
42176
42957
  }
42177
42958
  });
42178
42959
 
@@ -42305,6 +43086,29 @@ var init_GameCanvas3D2 = __esm({
42305
43086
  const controlsRef = useRef(null);
42306
43087
  const [hoveredTile, setHoveredTile] = useState(null);
42307
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
+ }, []);
42308
43112
  const { sheetUrls: atlasSheetUrls, resolveUnitFrame } = useUnitSpriteAtlas(units);
42309
43113
  const preloadUrls = useMemo(() => [...preloadAssets, ...atlasSheetUrls], [preloadAssets, atlasSheetUrls]);
42310
43114
  const { isLoading: assetsLoading, progress, loaded, total } = useAssetLoader({
@@ -42433,24 +43237,28 @@ var init_GameCanvas3D2 = __esm({
42433
43237
  const cameraConfig = useMemo(() => {
42434
43238
  const size = Math.max(
42435
43239
  gridBounds.maxX - gridBounds.minX,
42436
- gridBounds.maxZ - gridBounds.minZ
43240
+ gridBounds.maxZ - gridBounds.minZ,
43241
+ 4
43242
+ // minimum framing distance so a tiny board isn't zoomed in
42437
43243
  );
42438
- const distance = size * 1.5;
43244
+ const cx = (gridBounds.minX + gridBounds.maxX) / 2;
43245
+ const cz = (gridBounds.minZ + gridBounds.maxZ) / 2;
43246
+ const d = size * 1.5;
42439
43247
  switch (cameraMode) {
42440
43248
  case "isometric":
42441
43249
  return {
42442
- position: [distance, distance * 0.8, distance],
43250
+ position: [cx + d, d * 0.8, cz + d],
42443
43251
  fov: 45
42444
43252
  };
42445
43253
  case "top-down":
42446
43254
  return {
42447
- position: [0, distance * 2, 0],
43255
+ position: [cx, d * 2, cz],
42448
43256
  fov: 45
42449
43257
  };
42450
43258
  case "perspective":
42451
43259
  default:
42452
43260
  return {
42453
- position: [distance, distance, distance],
43261
+ position: [cx + d, d, cz + d],
42454
43262
  fov: 45
42455
43263
  };
42456
43264
  }
@@ -42689,7 +43497,8 @@ var init_GameCanvas3D2 = __esm({
42689
43497
  "div",
42690
43498
  {
42691
43499
  ref: containerRef,
42692
- className: cn("game-canvas-3d relative w-full h-full min-h-[85vh] overflow-hidden", className),
43500
+ className: cn("game-canvas-3d relative w-full overflow-hidden", className),
43501
+ style: { height: "85vh" },
42693
43502
  "data-orientation": orientation,
42694
43503
  "data-camera-mode": cameraMode,
42695
43504
  "data-overlay": overlay,
@@ -42704,7 +43513,7 @@ var init_GameCanvas3D2 = __esm({
42704
43513
  near: 0.1,
42705
43514
  far: 1e3
42706
43515
  },
42707
- style: { background: backgroundColor },
43516
+ style: { background: backgroundColor, position: "absolute", inset: 0 },
42708
43517
  onClick: (e) => {
42709
43518
  if (e.target === e.currentTarget) {
42710
43519
  eventHandlers.handleCanvasClick(e);
@@ -43675,7 +44484,7 @@ var init_List = __esm({
43675
44484
  if (entity && typeof entity === "object" && "id" in entity) return [entity];
43676
44485
  return [];
43677
44486
  }, [entity]);
43678
- const getItemActions = React82__default.useCallback(
44487
+ const getItemActions = React85__default.useCallback(
43679
44488
  (item) => {
43680
44489
  if (!itemActions) return [];
43681
44490
  if (typeof itemActions === "function") {
@@ -44151,7 +44960,7 @@ var init_MediaGallery = __esm({
44151
44960
  [selectable, selectedItems, selectionEvent, eventBus]
44152
44961
  );
44153
44962
  const entityData = Array.isArray(entity) ? entity : [];
44154
- const items = React82__default.useMemo(() => {
44963
+ const items = React85__default.useMemo(() => {
44155
44964
  if (propItems) return propItems;
44156
44965
  if (entityData.length === 0) return [];
44157
44966
  return entityData.map((record, idx) => ({
@@ -44312,7 +45121,7 @@ var init_MediaGallery = __esm({
44312
45121
  }
44313
45122
  });
44314
45123
  function extractTitle2(children) {
44315
- if (!React82__default.isValidElement(children)) return void 0;
45124
+ if (!React85__default.isValidElement(children)) return void 0;
44316
45125
  const props = children.props;
44317
45126
  if (typeof props.title === "string") {
44318
45127
  return props.title;
@@ -44888,31 +45697,33 @@ var init_PricingPageTemplate = __esm({
44888
45697
  PricingPageTemplate.displayName = "PricingPageTemplate";
44889
45698
  }
44890
45699
  });
44891
- 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) {
44892
45710
  const isBorder = x === 0 || y === 0 || x === DUNGEON_GRID_W - 1 || y === DUNGEON_GRID_H - 1;
44893
45711
  const isPillar = x % 4 === 0 && y % 4 === 0 && !isBorder;
44894
45712
  const isWall = isBorder || isPillar;
45713
+ const terrains = manifest?.terrains;
44895
45714
  if (x === stairsX && y === stairsY) {
44896
- return { terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` };
45715
+ return { terrain: "stairs", passable: true, terrainSprite: resolveManifestUrl3(manifest, terrains?.stairs) };
44897
45716
  }
44898
45717
  if (isWall) {
44899
- return { terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` };
44900
- }
44901
- const variant = (x * 3 + y * 7) % 5;
44902
- const sprites = [
44903
- `${CDN4}/isometric-dungeon/Isometric/dirt_E.png`,
44904
- `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png`,
44905
- `${CDN4}/isometric-dungeon/Isometric/planks_E.png`,
44906
- `${CDN4}/isometric-dungeon/Isometric/stoneTile_E.png`,
44907
- `${CDN4}/isometric-dungeon/Isometric/dirt_E.png`
44908
- ];
44909
- 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) };
44910
45721
  }
44911
- function buildDefaultDungeonTiles(stairsX, stairsY) {
45722
+ function buildDefaultDungeonTiles(stairsX, stairsY, manifest) {
44912
45723
  const tiles = [];
44913
45724
  for (let y = 0; y < DUNGEON_GRID_H; y++) {
44914
45725
  for (let x = 0; x < DUNGEON_GRID_W; x++) {
44915
- tiles.push({ x, y, ...dungeonTerrain(x, y, stairsX, stairsY) });
45726
+ tiles.push({ x, y, ...dungeonTerrain(x, y, stairsX, stairsY, manifest) });
44916
45727
  }
44917
45728
  }
44918
45729
  return tiles;
@@ -44942,20 +45753,48 @@ function RoguelikeBoard({
44942
45753
  className
44943
45754
  }) {
44944
45755
  const board = boardEntity(entity) ?? {};
44945
- const resolvedStairsX = propStairsX ?? num(board.stairsX, 14);
44946
- const resolvedStairsY = propStairsY ?? num(board.stairsY, 14);
44947
- const tiles = propTiles ?? (Array.isArray(board.tiles) && board.tiles.length >= DUNGEON_GRID_W * DUNGEON_GRID_H ? board.tiles : buildDefaultDungeonTiles(resolvedStairsX, resolvedStairsY));
44948
- const enemies = propEnemies ?? (Array.isArray(board.enemies) ? board.enemies : DEFAULT_ENEMIES);
44949
- const items = propItems ?? (Array.isArray(board.items) ? board.items : DEFAULT_ITEMS);
44950
- 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));
44951
45792
  const playerHp = propPlayerHp ?? num(board.playerHp, 10);
44952
45793
  const playerMaxHp = propPlayerMaxHp ?? num(board.playerMaxHp, 10);
44953
45794
  const playerAttack = propPlayerAttack ?? num(board.playerAttack, 3);
44954
45795
  const depth = propDepth ?? num(board.depth, 1);
44955
45796
  const result = propResult ?? (str(board.result) || "none");
44956
45797
  const phase = propPhase ?? (str(board.phase) || "player_turn");
44957
- propStairsX ?? num(board.stairsX, 14);
44958
- propStairsY ?? num(board.stairsY, 14);
44959
45798
  const eventBus = useEventBus();
44960
45799
  const { t } = useTranslate();
44961
45800
  const [hoveredTile, setHoveredTile] = useState(null);
@@ -45002,7 +45841,7 @@ function RoguelikeBoard({
45002
45841
  health: playerHp,
45003
45842
  maxHealth: playerMaxHp,
45004
45843
  unitType: "player",
45005
- sprite: `${CDN4}/isometric-dungeon/Characters/Male/Male_0_Idle0.png`
45844
+ sprite: resolveManifestUrl3(assetManifest, assetManifest?.units?.player)
45006
45845
  },
45007
45846
  ...liveEnemies.map((e) => ({
45008
45847
  id: e.id,
@@ -45012,19 +45851,19 @@ function RoguelikeBoard({
45012
45851
  health: e.hp,
45013
45852
  maxHealth: 6,
45014
45853
  unitType: "enemy",
45015
- sprite: `${CDN4}/sprite-sheets/shadow-legion-sprite-sheet-sw.png`
45854
+ sprite: resolveManifestUrl3(assetManifest, assetManifest?.units?.enemy)
45016
45855
  }))
45017
45856
  ];
45018
- }, [player, enemies, playerHp, playerMaxHp, t]);
45857
+ }, [player, enemies, playerHp, playerMaxHp, assetManifest, t]);
45019
45858
  const isoFeatures = useMemo(
45020
45859
  () => items.map((it) => ({
45021
45860
  id: it.id,
45022
45861
  x: it.x,
45023
45862
  y: it.y,
45024
45863
  type: it.kind,
45025
- sprite: FEATURE_SPRITE[it.kind] ?? FEATURE_SPRITE.health_potion
45864
+ sprite: resolveManifestUrl3(assetManifest, assetManifest?.features?.[it.kind])
45026
45865
  })),
45027
- [items]
45866
+ [items, assetManifest]
45028
45867
  );
45029
45868
  const validMoves = useMemo(() => {
45030
45869
  if (result !== "none" || phase !== "player_turn") return [];
@@ -45083,8 +45922,8 @@ function RoguelikeBoard({
45083
45922
  onTileHover: (x, y) => setHoveredTile({ x, y }),
45084
45923
  onTileLeave: () => setHoveredTile(null),
45085
45924
  scale,
45086
- assetBaseUrl: propAssetManifest?.baseUrl ?? CDN4,
45087
- assetManifest: propAssetManifest,
45925
+ assetBaseUrl: assetManifest?.baseUrl,
45926
+ assetManifest,
45088
45927
  unitScale,
45089
45928
  spriteHeightRatio,
45090
45929
  spriteMaxWidthRatio
@@ -45131,7 +45970,7 @@ function RoguelikeBoard({
45131
45970
  ] }) })
45132
45971
  ] });
45133
45972
  }
45134
- 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;
45135
45974
  var init_RoguelikeBoard = __esm({
45136
45975
  "components/game/organisms/RoguelikeBoard.tsx"() {
45137
45976
  "use client";
@@ -45143,10 +45982,8 @@ var init_RoguelikeBoard = __esm({
45143
45982
  init_Stack();
45144
45983
  init_IsometricCanvas();
45145
45984
  init_boardEntity();
45146
- CDN4 = "https://almadar-kflow-assets.web.app/shared";
45147
45985
  DUNGEON_GRID_W = 16;
45148
45986
  DUNGEON_GRID_H = 16;
45149
- buildDefaultDungeonTiles(14, 14);
45150
45987
  DEFAULT_ENEMIES = [
45151
45988
  { id: "e1", x: 5, y: 2, hp: 5, attack: 2 },
45152
45989
  { id: "e2", x: 9, y: 6, hp: 4, attack: 1 },
@@ -45158,11 +45995,6 @@ var init_RoguelikeBoard = __esm({
45158
45995
  { id: "i2", x: 7, y: 9, kind: "sword" },
45159
45996
  { id: "i3", x: 11, y: 5, kind: "shield" }
45160
45997
  ];
45161
- FEATURE_SPRITE = {
45162
- health_potion: `${CDN4}/isometric-dungeon/Isometric/chestClosed_E.png`,
45163
- sword: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`,
45164
- shield: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`
45165
- };
45166
45998
  RoguelikeBoard.displayName = "RoguelikeBoard";
45167
45999
  }
45168
46000
  });
@@ -45200,40 +46032,40 @@ function RoguelikeTemplate({
45200
46032
  }
45201
46033
  );
45202
46034
  }
45203
- var CDN5, DEFAULT_ROGUELIKE_TILES, DEFAULT_ROGUELIKE_MANIFEST;
46035
+ var CDN4, DEFAULT_ROGUELIKE_TILES, DEFAULT_ROGUELIKE_MANIFEST;
45204
46036
  var init_RoguelikeTemplate = __esm({
45205
46037
  "components/game/templates/RoguelikeTemplate.tsx"() {
45206
46038
  init_RoguelikeBoard();
45207
- CDN5 = "https://almadar-kflow-assets.web.app/shared";
46039
+ CDN4 = "https://almadar-kflow-assets.web.app/shared";
45208
46040
  DEFAULT_ROGUELIKE_TILES = [
45209
- { x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45210
- { x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45211
- { x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45212
- { x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45213
- { x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45214
- { x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45215
- { x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
45216
- { x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
45217
- { x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
45218
- { x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45219
- { x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45220
- { x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
45221
- { x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
45222
- { x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
45223
- { x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45224
- { x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45225
- { x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
45226
- { x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
45227
- { x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stairs_E.png` },
45228
- { x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45229
- { x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45230
- { x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45231
- { x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45232
- { x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45233
- { 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` }
45234
46066
  ];
45235
46067
  DEFAULT_ROGUELIKE_MANIFEST = {
45236
- baseUrl: CDN5,
46068
+ baseUrl: CDN4,
45237
46069
  terrains: {
45238
46070
  floor: "/isometric-dungeon/Isometric/dirt_E.png",
45239
46071
  wall: "/isometric-dungeon/Isometric/stoneInset_E.png",
@@ -45340,7 +46172,7 @@ var init_debugRegistry = __esm({
45340
46172
  }
45341
46173
  });
45342
46174
  function useDebugData() {
45343
- const [data, setData] = React82.useState(() => ({
46175
+ const [data, setData] = React85.useState(() => ({
45344
46176
  traits: [],
45345
46177
  ticks: [],
45346
46178
  guards: [],
@@ -45354,7 +46186,7 @@ function useDebugData() {
45354
46186
  },
45355
46187
  lastUpdate: Date.now()
45356
46188
  }));
45357
- React82.useEffect(() => {
46189
+ React85.useEffect(() => {
45358
46190
  const updateData = () => {
45359
46191
  setData({
45360
46192
  traits: getAllTraits(),
@@ -45463,12 +46295,12 @@ function layoutGraph(states, transitions, initialState, width, height) {
45463
46295
  return positions;
45464
46296
  }
45465
46297
  function WalkMinimap() {
45466
- const [walkStep, setWalkStep] = React82.useState(null);
45467
- const [traits2, setTraits] = React82.useState([]);
45468
- const [coveredEdges, setCoveredEdges] = React82.useState([]);
45469
- const [completedTraits, setCompletedTraits] = React82.useState(/* @__PURE__ */ new Set());
45470
- const prevTraitRef = React82.useRef(null);
45471
- 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(() => {
45472
46304
  const interval = setInterval(() => {
45473
46305
  const w = window;
45474
46306
  const step = w.__orbitalWalkStep;
@@ -45904,15 +46736,15 @@ var init_EntitiesTab = __esm({
45904
46736
  });
45905
46737
  function EventFlowTab({ events: events2 }) {
45906
46738
  const { t } = useTranslate();
45907
- const [filter, setFilter] = React82.useState("all");
45908
- const containerRef = React82.useRef(null);
45909
- const [autoScroll, setAutoScroll] = React82.useState(true);
45910
- 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(() => {
45911
46743
  if (autoScroll && containerRef.current) {
45912
46744
  containerRef.current.scrollTop = containerRef.current.scrollHeight;
45913
46745
  }
45914
46746
  }, [events2.length, autoScroll]);
45915
- const filteredEvents = React82.useMemo(() => {
46747
+ const filteredEvents = React85.useMemo(() => {
45916
46748
  if (filter === "all") return events2;
45917
46749
  return events2.filter((e) => e.type === filter);
45918
46750
  }, [events2, filter]);
@@ -46028,7 +46860,7 @@ var init_EventFlowTab = __esm({
46028
46860
  });
46029
46861
  function GuardsPanel({ guards }) {
46030
46862
  const { t } = useTranslate();
46031
- const [filter, setFilter] = React82.useState("all");
46863
+ const [filter, setFilter] = React85.useState("all");
46032
46864
  if (guards.length === 0) {
46033
46865
  return /* @__PURE__ */ jsx(
46034
46866
  EmptyState,
@@ -46041,7 +46873,7 @@ function GuardsPanel({ guards }) {
46041
46873
  }
46042
46874
  const passedCount = guards.filter((g) => g.result).length;
46043
46875
  const failedCount = guards.length - passedCount;
46044
- const filteredGuards = React82.useMemo(() => {
46876
+ const filteredGuards = React85.useMemo(() => {
46045
46877
  if (filter === "all") return guards;
46046
46878
  if (filter === "passed") return guards.filter((g) => g.result);
46047
46879
  return guards.filter((g) => !g.result);
@@ -46204,10 +47036,10 @@ function EffectBadge({ effect }) {
46204
47036
  }
46205
47037
  function TransitionTimeline({ transitions }) {
46206
47038
  const { t } = useTranslate();
46207
- const containerRef = React82.useRef(null);
46208
- const [autoScroll, setAutoScroll] = React82.useState(true);
46209
- const [expandedId, setExpandedId] = React82.useState(null);
46210
- 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(() => {
46211
47043
  if (autoScroll && containerRef.current) {
46212
47044
  containerRef.current.scrollTop = containerRef.current.scrollHeight;
46213
47045
  }
@@ -46487,9 +47319,9 @@ function getAllEvents(traits2) {
46487
47319
  function EventDispatcherTab({ traits: traits2, schema }) {
46488
47320
  const eventBus = useEventBus();
46489
47321
  const { t } = useTranslate();
46490
- const [log14, setLog] = React82.useState([]);
46491
- const prevStatesRef = React82.useRef(/* @__PURE__ */ new Map());
46492
- React82.useEffect(() => {
47322
+ const [log13, setLog] = React85.useState([]);
47323
+ const prevStatesRef = React85.useRef(/* @__PURE__ */ new Map());
47324
+ React85.useEffect(() => {
46493
47325
  for (const trait of traits2) {
46494
47326
  const prev = prevStatesRef.current.get(trait.id);
46495
47327
  if (prev && prev !== trait.currentState) {
@@ -46551,9 +47383,9 @@ function EventDispatcherTab({ traits: traits2, schema }) {
46551
47383
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.otherEvents") }),
46552
47384
  /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: unavailableEvents.map((event) => /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", className: "opacity-50", children: event }, event)) })
46553
47385
  ] }),
46554
- log14.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
47386
+ log13.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
46555
47387
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.recentTransitions") }),
46556
- /* @__PURE__ */ jsx(Stack, { gap: "xs", children: log14.map((entry, i) => /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
47388
+ /* @__PURE__ */ jsx(Stack, { gap: "xs", children: log13.map((entry, i) => /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
46557
47389
  /* @__PURE__ */ jsx("span", { className: "text-purple-400", children: entry.traitName }),
46558
47390
  " ",
46559
47391
  /* @__PURE__ */ jsx("span", { className: "text-gray-500", children: entry.from }),
@@ -46658,10 +47490,10 @@ function VerifyModePanel({
46658
47490
  localCount
46659
47491
  }) {
46660
47492
  const { t } = useTranslate();
46661
- const [expanded, setExpanded] = React82.useState(true);
46662
- const scrollRef = React82.useRef(null);
46663
- const prevCountRef = React82.useRef(0);
46664
- React82.useEffect(() => {
47493
+ const [expanded, setExpanded] = React85.useState(true);
47494
+ const scrollRef = React85.useRef(null);
47495
+ const prevCountRef = React85.useRef(0);
47496
+ React85.useEffect(() => {
46665
47497
  if (expanded && transitions.length > prevCountRef.current && scrollRef.current) {
46666
47498
  scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
46667
47499
  }
@@ -46718,10 +47550,10 @@ function RuntimeDebugger({
46718
47550
  schema
46719
47551
  }) {
46720
47552
  const { t } = useTranslate();
46721
- const [isCollapsed, setIsCollapsed] = React82.useState(mode === "verify" ? true : defaultCollapsed);
46722
- 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());
46723
47555
  const debugData = useDebugData();
46724
- React82.useEffect(() => {
47556
+ React85.useEffect(() => {
46725
47557
  if (mode === "inline") return;
46726
47558
  return onDebugToggle((enabled) => {
46727
47559
  setIsVisible(enabled);
@@ -46730,7 +47562,7 @@ function RuntimeDebugger({
46730
47562
  }
46731
47563
  });
46732
47564
  }, [mode]);
46733
- React82.useEffect(() => {
47565
+ React85.useEffect(() => {
46734
47566
  if (mode === "inline") return;
46735
47567
  const handleKeyDown = (e) => {
46736
47568
  if (e.key === "`" && isVisible) {
@@ -47290,7 +48122,7 @@ function SequenceBar({
47290
48122
  onSlotRemove(index);
47291
48123
  }, [onSlotRemove, playing]);
47292
48124
  const paddedSlots = Array.from({ length: maxSlots }, (_, i) => slots[i]);
47293
- 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: [
47294
48126
  i > 0 && /* @__PURE__ */ jsx(
47295
48127
  Typography,
47296
48128
  {
@@ -48179,7 +49011,7 @@ var init_StatCard = __esm({
48179
49011
  const labelToUse = propLabel ?? propTitle;
48180
49012
  const eventBus = useEventBus();
48181
49013
  const { t } = useTranslate();
48182
- const handleActionClick = React82__default.useCallback(() => {
49014
+ const handleActionClick = React85__default.useCallback(() => {
48183
49015
  if (action?.event) {
48184
49016
  eventBus.emit(`UI:${action.event}`, {});
48185
49017
  }
@@ -48190,7 +49022,7 @@ var init_StatCard = __esm({
48190
49022
  const data = Array.isArray(entity) ? entity : entity ? [entity] : [];
48191
49023
  const isLoading = externalLoading ?? false;
48192
49024
  const error = externalError;
48193
- const computeMetricValue = React82__default.useCallback(
49025
+ const computeMetricValue = React85__default.useCallback(
48194
49026
  (metric, items) => {
48195
49027
  if (metric.value !== void 0) {
48196
49028
  return metric.value;
@@ -48229,7 +49061,7 @@ var init_StatCard = __esm({
48229
49061
  },
48230
49062
  []
48231
49063
  );
48232
- const schemaStats = React82__default.useMemo(() => {
49064
+ const schemaStats = React85__default.useMemo(() => {
48233
49065
  if (!metrics || metrics.length === 0) return null;
48234
49066
  return metrics.map((metric) => ({
48235
49067
  label: metric.label,
@@ -48237,7 +49069,7 @@ var init_StatCard = __esm({
48237
49069
  format: metric.format
48238
49070
  }));
48239
49071
  }, [metrics, data, computeMetricValue]);
48240
- const calculatedTrend = React82__default.useMemo(() => {
49072
+ const calculatedTrend = React85__default.useMemo(() => {
48241
49073
  if (manualTrend !== void 0) return manualTrend;
48242
49074
  if (previousValue === void 0 || currentValue === void 0)
48243
49075
  return void 0;
@@ -49203,7 +50035,7 @@ var init_Timeline = __esm({
49203
50035
  }) => {
49204
50036
  const { t } = useTranslate();
49205
50037
  const entityData = Array.isArray(entity) ? entity : [];
49206
- const items = React82__default.useMemo(() => {
50038
+ const items = React85__default.useMemo(() => {
49207
50039
  if (propItems) return propItems;
49208
50040
  if (entityData.length === 0) return [];
49209
50041
  return entityData.map((record, idx) => {
@@ -49310,7 +50142,7 @@ var init_Timeline = __esm({
49310
50142
  }
49311
50143
  });
49312
50144
  function extractToastProps(children) {
49313
- if (!React82__default.isValidElement(children)) {
50145
+ if (!React85__default.isValidElement(children)) {
49314
50146
  if (typeof children === "string") {
49315
50147
  return { message: children };
49316
50148
  }
@@ -49348,7 +50180,7 @@ var init_ToastSlot = __esm({
49348
50180
  eventBus.emit("UI:CLOSE");
49349
50181
  };
49350
50182
  if (!isVisible) return null;
49351
- const isCustomContent = React82__default.isValidElement(children) && !message;
50183
+ const isCustomContent = React85__default.isValidElement(children) && !message;
49352
50184
  return /* @__PURE__ */ jsx(Box, { className: "fixed bottom-4 right-4 z-50", children: isCustomContent ? children : /* @__PURE__ */ jsx(
49353
50185
  Toast,
49354
50186
  {
@@ -49364,82 +50196,442 @@ var init_ToastSlot = __esm({
49364
50196
  ToastSlot.displayName = "ToastSlot";
49365
50197
  }
49366
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
+ }
49367
50564
  function buildDefaultTDTiles() {
49368
50565
  const pathSet = new Set(DEFAULT_TD_PATH.map((p2) => `${p2.x},${p2.y}`));
49369
50566
  const tiles = [];
49370
50567
  for (let y = 0; y < TD_GRID_H; y++) {
49371
50568
  for (let x = 0; x < TD_GRID_W; x++) {
49372
50569
  if (pathSet.has(`${x},${y}`)) {
49373
- tiles.push({ x, y, terrain: "path", passable: false, terrainSprite: TERRAIN_SPRITES.path });
50570
+ tiles.push({ x, y, terrain: "path", passable: false });
49374
50571
  } else {
49375
50572
  const variant = (x * 3 + y * 5 + (x ^ y)) % 3;
49376
- const terrainKey = ["ground", "grass", "dirt"][variant];
49377
- 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 });
49378
50575
  }
49379
50576
  }
49380
50577
  }
49381
50578
  return tiles;
49382
50579
  }
49383
- function tilesToIso(tiles) {
49384
- return tiles.map((t) => ({
49385
- x: t.x,
49386
- y: t.y,
49387
- terrain: t.terrain,
49388
- terrainSprite: t.terrainSprite ?? TERRAIN_SPRITES[t.terrain ?? "ground"],
49389
- passable: t.passable
49390
- }));
49391
- }
49392
- function towersToUnits(towers) {
49393
- return towers.map((t) => ({
49394
- id: t.id,
49395
- position: { x: t.x, y: t.y },
49396
- name: "Tower",
49397
- team: "player",
49398
- sprite: TOWER_SPRITE,
49399
- unitType: "guardian",
49400
- health: 1,
49401
- maxHealth: 1
49402
- }));
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
+ });
49403
50592
  }
49404
- function creepsToUnits(creeps) {
50593
+ function creepsToUnits(creeps, manifest) {
50594
+ const sprite = resolveManifestUrl5(manifest, manifest?.units?.creep);
49405
50595
  return creeps.map((c) => ({
49406
50596
  id: c.id,
49407
50597
  position: { x: c.x, y: c.y },
49408
50598
  name: "Creep",
49409
50599
  team: "enemy",
49410
- sprite: CREEP_SPRITE,
49411
- unitType: "scrapper",
50600
+ sprite,
50601
+ unitType: "creep",
49412
50602
  health: c.hp,
49413
50603
  maxHealth: c.maxHp
49414
50604
  }));
49415
50605
  }
49416
- function pathToFeatures(path) {
49417
- return path.map((p2, i) => ({
49418
- id: `path-${i}`,
49419
- x: p2.x,
49420
- y: p2.y,
49421
- type: "path-marker",
49422
- sprite: `${CDN6}world-map/road_straight.png`
49423
- }));
49424
- }
49425
- function heroToUnit(hero) {
50606
+ function heroToUnit(hero, manifest) {
49426
50607
  return {
49427
50608
  id: hero.id,
49428
50609
  position: { x: hero.x, y: hero.y },
49429
50610
  name: "Hero",
49430
50611
  team: "player",
49431
- sprite: HERO_SPRITE,
49432
- unitType: "amir",
50612
+ sprite: resolveManifestUrl5(manifest, manifest?.units?.hero),
50613
+ unitType: "hero",
49433
50614
  health: 1,
49434
50615
  maxHealth: 1
49435
50616
  };
49436
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
+ }
49437
50628
  function TowerDefenseBoard({
49438
50629
  entity,
49439
50630
  tiles: propTiles,
49440
50631
  path: propPath,
49441
50632
  towers: propTowers,
49442
50633
  creeps: propCreeps,
50634
+ assetManifest: propAssetManifest,
49443
50635
  hero: propHero,
49444
50636
  gold: propGold,
49445
50637
  lives: propLives,
@@ -49462,12 +50654,51 @@ function TowerDefenseBoard({
49462
50654
  const board = boardEntity(entity) ?? {};
49463
50655
  const eventBus = useEventBus();
49464
50656
  const { t } = useTranslate();
49465
- 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;
49466
50669
  const tiles = rawTiles.length >= TD_GRID_W * TD_GRID_H ? rawTiles : DEFAULT_TD_TILES;
49467
- 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;
49468
50675
  const path = rawPath.length > 0 ? rawPath : DEFAULT_TD_PATH;
49469
- const towers = propTowers ?? rows(board.towers);
49470
- 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;
49471
50702
  const hero = propHero ?? { id: "hero", x: 8, y: 8 };
49472
50703
  const gold = propGold ?? num(board.gold, 100);
49473
50704
  const lives = propLives ?? num(board.lives, 20);
@@ -49519,12 +50750,11 @@ function TowerDefenseBoard({
49519
50750
  (t2) => t2.passable !== false && !towerPositions.has(`${t2.x},${t2.y}`) && !pathPositions.has(`${t2.x},${t2.y}`)
49520
50751
  ).map((t2) => ({ x: t2.x, y: t2.y }));
49521
50752
  }, [tiles, towerPositions, pathPositions, result, gold, towerCost]);
49522
- const isoTiles = useMemo(() => tilesToIso(tiles), [tiles]);
49523
- const towerUnits = useMemo(() => towersToUnits(towers), [towers]);
49524
- const creepUnits = useMemo(() => creepsToUnits(creeps), [creeps]);
49525
- const heroUnit = useMemo(() => heroToUnit(hero), [hero]);
49526
- const isoUnits = useMemo(() => [...towerUnits, ...creepUnits, heroUnit], [towerUnits, creepUnits, heroUnit]);
49527
- 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]);
49528
50758
  const handleTileClick = useCallback((x, y) => {
49529
50759
  if (result !== "none") return;
49530
50760
  if (pathPositions.has(`${x},${y}`)) return;
@@ -49583,7 +50813,9 @@ function TowerDefenseBoard({
49583
50813
  {
49584
50814
  tiles: isoTiles,
49585
50815
  units: isoUnits,
49586
- features: pathFeatures,
50816
+ features: towerFeatures,
50817
+ assetBaseUrl: assetManifest?.baseUrl,
50818
+ assetManifest,
49587
50819
  validMoves,
49588
50820
  hoveredTile,
49589
50821
  onTileClick: handleTileClick,
@@ -49622,7 +50854,7 @@ function TowerDefenseBoard({
49622
50854
  ] }) })
49623
50855
  ] });
49624
50856
  }
49625
- 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;
49626
50858
  var init_TowerDefenseBoard = __esm({
49627
50859
  "components/game/organisms/TowerDefenseBoard.tsx"() {
49628
50860
  "use client";
@@ -49634,16 +50866,8 @@ var init_TowerDefenseBoard = __esm({
49634
50866
  init_Stack();
49635
50867
  init_IsometricCanvas();
49636
50868
  init_boardEntity();
49637
- CDN6 = "https://almadar-kflow-assets.web.app/shared/";
49638
50869
  TD_GRID_W = 16;
49639
50870
  TD_GRID_H = 16;
49640
- TERRAIN_SPRITES = {
49641
- ground: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_01.png`,
49642
- path: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_09.png`,
49643
- wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`,
49644
- grass: `${CDN6}isometric-dungeon/Isometric/dirt_E.png`,
49645
- dirt: `${CDN6}isometric-dungeon/Isometric/dirtTiles_E.png`
49646
- };
49647
50871
  DEFAULT_TD_PATH = [
49648
50872
  { x: 2, y: 0 },
49649
50873
  { x: 2, y: 1 },
@@ -49696,9 +50920,6 @@ var init_TowerDefenseBoard = __esm({
49696
50920
  { x: 13, y: 15 }
49697
50921
  ];
49698
50922
  DEFAULT_TD_TILES = buildDefaultTDTiles();
49699
- TOWER_SPRITE = `${CDN6}sprite-sheets/guardian-sprite-sheet-se.png`;
49700
- CREEP_SPRITE = `${CDN6}sprite-sheets/scrapper-sprite-sheet-se.png`;
49701
- HERO_SPRITE = `${CDN6}sprite-sheets/amir-sprite-sheet-se.png`;
49702
50923
  TowerDefenseBoard.displayName = "TowerDefenseBoard";
49703
50924
  }
49704
50925
  });
@@ -49966,6 +51187,214 @@ var init_UncontrolledBattleBoard = __esm({
49966
51187
  UncontrolledBattleBoard.displayName = "UncontrolledBattleBoard";
49967
51188
  }
49968
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
+ });
49969
51398
  function heroPosition(h) {
49970
51399
  if ("position" in h && h.position != null) return h.position;
49971
51400
  return vec2(h.position);
@@ -50306,48 +51735,48 @@ function WorldMapTemplate({
50306
51735
  }
50307
51736
  );
50308
51737
  }
50309
- 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;
50310
51739
  var init_WorldMapTemplate = __esm({
50311
51740
  "components/game/templates/WorldMapTemplate.tsx"() {
50312
51741
  init_WorldMapBoard();
50313
- CDN7 = "https://almadar-kflow-assets.web.app/shared";
51742
+ CDN5 = "https://almadar-kflow-assets.web.app/shared";
50314
51743
  DEFAULT_WORLDMAP_TILES = [
50315
- { x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50316
- { x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50317
- { x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
50318
- { x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50319
- { x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50320
- { x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50321
- { x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50322
- { x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50323
- { x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50324
- { x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
50325
- { x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
50326
- { x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50327
- { x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
50328
- { x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50329
- { x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50330
- { x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50331
- { x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50332
- { x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50333
- { x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
50334
- { x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50335
- { x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50336
- { x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
50337
- { x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50338
- { x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50339
- { 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` }
50340
51769
  ];
50341
51770
  DEFAULT_WORLDMAP_UNITS = [
50342
- { 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` },
50343
- { 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` }
50344
51773
  ];
50345
51774
  DEFAULT_WORLDMAP_FEATURES = [
50346
- { id: "f1", x: 2, y: 2, type: "capital", sprite: `${CDN7}/scenes/world/capital.png` },
50347
- { 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` }
50348
51777
  ];
50349
51778
  DEFAULT_WORLDMAP_MANIFEST = {
50350
- baseUrl: CDN7,
51779
+ baseUrl: CDN5,
50351
51780
  terrains: {
50352
51781
  grass: "/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png",
50353
51782
  water: "/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png",
@@ -50367,7 +51796,7 @@ var init_WorldMapTemplate = __esm({
50367
51796
  }
50368
51797
  });
50369
51798
  function lazyThree(name, loader) {
50370
- const Lazy = React82__default.lazy(
51799
+ const Lazy = React85__default.lazy(
50371
51800
  () => loader().then((m) => {
50372
51801
  const Resolved = m[name];
50373
51802
  if (!Resolved) {
@@ -50379,13 +51808,13 @@ function lazyThree(name, loader) {
50379
51808
  })
50380
51809
  );
50381
51810
  function ThreeWrapper(props) {
50382
- return React82__default.createElement(
51811
+ return React85__default.createElement(
50383
51812
  ThreeBoundary,
50384
51813
  { name },
50385
- React82__default.createElement(
50386
- React82__default.Suspense,
51814
+ React85__default.createElement(
51815
+ React85__default.Suspense,
50387
51816
  { fallback: null },
50388
- React82__default.createElement(Lazy, props)
51817
+ React85__default.createElement(Lazy, props)
50389
51818
  )
50390
51819
  );
50391
51820
  }
@@ -50429,7 +51858,10 @@ var init_component_registry_generated = __esm({
50429
51858
  init_CalendarGrid();
50430
51859
  init_CanvasEffect();
50431
51860
  init_Card();
51861
+ init_CardBattlerBoard();
51862
+ init_CardBattlerTemplate();
50432
51863
  init_CardGrid();
51864
+ init_CardHand();
50433
51865
  init_Carousel();
50434
51866
  init_CaseStudyCard();
50435
51867
  init_CaseStudyOrganism();
@@ -50440,6 +51872,8 @@ var init_component_registry_generated = __esm({
50440
51872
  init_ChartLegend();
50441
51873
  init_Checkbox();
50442
51874
  init_ChoiceButton();
51875
+ init_CityBuilderBoard();
51876
+ init_CityBuilderTemplate();
50443
51877
  init_ClassifierBoard();
50444
51878
  init_CodeBlock();
50445
51879
  init_CodeRunnerPanel();
@@ -50660,6 +52094,8 @@ var init_component_registry_generated = __esm({
50660
52094
  init_Toast();
50661
52095
  init_ToastSlot();
50662
52096
  init_Tooltip();
52097
+ init_TopDownShooterBoard();
52098
+ init_TopDownShooterTemplate();
50663
52099
  init_TowerDefenseBoard();
50664
52100
  init_TowerDefenseTemplate();
50665
52101
  init_TraitFrame();
@@ -50675,6 +52111,8 @@ var init_component_registry_generated = __esm({
50675
52111
  init_UploadDropZone();
50676
52112
  init_VersionDiff();
50677
52113
  init_ViolationAlert();
52114
+ init_VisualNovelBoard();
52115
+ init_VisualNovelTemplate();
50678
52116
  init_VoteStack();
50679
52117
  init_WaypointMarker();
50680
52118
  init_WizardContainer();
@@ -50683,7 +52121,7 @@ var init_component_registry_generated = __esm({
50683
52121
  init_WorldMapBoard();
50684
52122
  init_WorldMapTemplate();
50685
52123
  init_XPBar();
50686
- ThreeBoundary = class extends React82__default.Component {
52124
+ ThreeBoundary = class extends React85__default.Component {
50687
52125
  constructor() {
50688
52126
  super(...arguments);
50689
52127
  __publicField(this, "state", { failed: false });
@@ -50693,7 +52131,7 @@ var init_component_registry_generated = __esm({
50693
52131
  }
50694
52132
  render() {
50695
52133
  if (this.state.failed) {
50696
- return React82__default.createElement(
52134
+ return React85__default.createElement(
50697
52135
  "div",
50698
52136
  {
50699
52137
  "data-testid": "three-unavailable",
@@ -50749,7 +52187,10 @@ var init_component_registry_generated = __esm({
50749
52187
  "CalendarGrid": CalendarGrid,
50750
52188
  "CanvasEffect": CanvasEffect,
50751
52189
  "Card": Card,
52190
+ "CardBattlerBoard": CardBattlerBoard,
52191
+ "CardBattlerTemplate": CardBattlerTemplate,
50752
52192
  "CardGrid": CardGrid,
52193
+ "CardHand": CardHand,
50753
52194
  "Carousel": Carousel,
50754
52195
  "CaseStudyCard": CaseStudyCard,
50755
52196
  "CaseStudyOrganism": CaseStudyOrganism,
@@ -50760,6 +52201,8 @@ var init_component_registry_generated = __esm({
50760
52201
  "ChartLegend": ChartLegend,
50761
52202
  "Checkbox": Checkbox,
50762
52203
  "ChoiceButton": ChoiceButton,
52204
+ "CityBuilderBoard": CityBuilderBoard,
52205
+ "CityBuilderTemplate": CityBuilderTemplate,
50763
52206
  "ClassifierBoard": ClassifierBoard,
50764
52207
  "CodeBlock": CodeBlock,
50765
52208
  "CodeRunnerPanel": CodeRunnerPanel,
@@ -50992,6 +52435,8 @@ var init_component_registry_generated = __esm({
50992
52435
  "Toast": Toast,
50993
52436
  "ToastSlot": ToastSlot,
50994
52437
  "Tooltip": Tooltip,
52438
+ "TopDownShooterBoard": TopDownShooterBoard,
52439
+ "TopDownShooterTemplate": TopDownShooterTemplate,
50995
52440
  "TowerDefenseBoard": TowerDefenseBoard,
50996
52441
  "TowerDefenseTemplate": TowerDefenseTemplate,
50997
52442
  "TraitFrame": TraitFrame,
@@ -51008,6 +52453,8 @@ var init_component_registry_generated = __esm({
51008
52453
  "VStack": VStack,
51009
52454
  "VersionDiff": VersionDiff,
51010
52455
  "ViolationAlert": ViolationAlert,
52456
+ "VisualNovelBoard": VisualNovelBoard,
52457
+ "VisualNovelTemplate": VisualNovelTemplate,
51011
52458
  "VoteStack": VoteStack,
51012
52459
  "WaypointMarker": WaypointMarker,
51013
52460
  "WizardContainer": WizardContainer,
@@ -51034,7 +52481,7 @@ function SuspenseConfigProvider({
51034
52481
  config,
51035
52482
  children
51036
52483
  }) {
51037
- return React82__default.createElement(
52484
+ return React85__default.createElement(
51038
52485
  SuspenseConfigContext.Provider,
51039
52486
  { value: config },
51040
52487
  children
@@ -51524,7 +52971,7 @@ function renderPatternChildren(children, onDismiss, parentId = "root", parentPat
51524
52971
  const key = `${parentId}-${index}-trait:${traitName}`;
51525
52972
  return /* @__PURE__ */ jsx(TraitFrame, { traitName }, key);
51526
52973
  }
51527
- return /* @__PURE__ */ jsx(React82__default.Fragment, { children: child }, `${parentId}-${index}`);
52974
+ return /* @__PURE__ */ jsx(React85__default.Fragment, { children: child }, `${parentId}-${index}`);
51528
52975
  }
51529
52976
  if (!child || typeof child !== "object") return null;
51530
52977
  const childId = `${parentId}-${index}`;
@@ -51564,14 +53011,14 @@ function isPatternConfig(value) {
51564
53011
  if (value === null || value === void 0) return false;
51565
53012
  if (typeof value !== "object") return false;
51566
53013
  if (Array.isArray(value)) return false;
51567
- if (React82__default.isValidElement(value)) return false;
53014
+ if (React85__default.isValidElement(value)) return false;
51568
53015
  if (value instanceof Date) return false;
51569
53016
  if (typeof value === "function") return false;
51570
53017
  const record = value;
51571
- return "type" in record && typeof record.type === "string" && isKnownPattern(record.type);
53018
+ return "type" in record && typeof record.type === "string" && getComponentForPattern$1(record.type) !== null;
51572
53019
  }
51573
53020
  function isPlainConfigObject(value) {
51574
- if (React82__default.isValidElement(value)) return false;
53021
+ if (React85__default.isValidElement(value)) return false;
51575
53022
  if (value instanceof Date) return false;
51576
53023
  const proto = Object.getPrototypeOf(value);
51577
53024
  return proto === Object.prototype || proto === null;
@@ -51692,6 +53139,22 @@ function SlotContentRenderer({
51692
53139
  const childrenIsRenderFn = typeof incomingChildren === "function";
51693
53140
  const { children: _childrenConfig, ...restPropsNoChildren } = content.props;
51694
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
+ }
51695
53158
  const renderedProps = renderPatternProps(restProps, onDismiss);
51696
53159
  const patternDef = getPatternDefinition(content.pattern);
51697
53160
  const propsSchema = patternDef?.propsSchema;
@@ -51722,6 +53185,9 @@ function SlotContentRenderer({
51722
53185
  }
51723
53186
  }
51724
53187
  const finalProps = renderedProps;
53188
+ for (const [k, v] of Object.entries(nodeSlotOverrides)) {
53189
+ finalProps[k] = v;
53190
+ }
51725
53191
  const resolvedItems = Array.isArray(
51726
53192
  finalProps.entity
51727
53193
  ) ? finalProps.entity : null;
@@ -51842,7 +53308,7 @@ function UISlotRenderer({
51842
53308
  }
51843
53309
  return wrapped;
51844
53310
  }
51845
- 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;
51846
53312
  var init_UISlotRenderer = __esm({
51847
53313
  "components/core/organisms/UISlotRenderer.tsx"() {
51848
53314
  "use client";
@@ -51879,6 +53345,16 @@ var init_UISlotRenderer = __esm({
51879
53345
  "wizard-step"
51880
53346
  ]);
51881
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
+ ]);
51882
53358
  PATTERNS_WITH_CHILDREN = /* @__PURE__ */ new Set([
51883
53359
  "stack",
51884
53360
  "vstack",
@@ -52162,7 +53638,7 @@ init_UISlotRenderer();
52162
53638
  // providers/VerificationProvider.tsx
52163
53639
  init_useEventBus();
52164
53640
  init_verificationRegistry();
52165
- var log13 = createLogger("almadar:verify");
53641
+ var log12 = createLogger("almadar:verify");
52166
53642
  var DISPATCH_SUFFIX = ":DISPATCH";
52167
53643
  var SUCCESS_SUFFIX = ":SUCCESS";
52168
53644
  var ERROR_SUFFIX = ":ERROR";
@@ -52209,7 +53685,7 @@ function VerificationProvider({
52209
53685
  const verificationProviderLifecycleListener = (evt) => {
52210
53686
  const parsed = parseLifecycleEvent(evt.type);
52211
53687
  if (!parsed) return;
52212
- log13.debug("lifecycle:event", { kind: parsed.kind, traitName: parsed.traitName, event: parsed.event, type: evt.type });
53688
+ log12.debug("lifecycle:event", { kind: parsed.kind, traitName: parsed.traitName, event: parsed.event, type: evt.type });
52213
53689
  const payload = evt.payload ?? {};
52214
53690
  if (parsed.kind === "dispatch") {
52215
53691
  const key = `${parsed.traitName}:${String(payload["event"] ?? "")}`;
@@ -52264,7 +53740,7 @@ function VerificationProvider({
52264
53740
  },
52265
53741
  timestamp: Date.now()
52266
53742
  });
52267
- log13.debug("transition:success", { trait: parsed.traitName, event: parsed.event, from: pending?.from, to: newState, effectCount: effects.length });
53743
+ log12.debug("transition:success", { trait: parsed.traitName, event: parsed.event, from: pending?.from, to: newState, effectCount: effects.length });
52268
53744
  } else if (parsed.kind === "error" && parsed.event) {
52269
53745
  const key = `${parsed.traitName}:${parsed.event}`;
52270
53746
  const pending = pendingRef.current.get(key);
@@ -52294,7 +53770,7 @@ function VerificationProvider({
52294
53770
  },
52295
53771
  timestamp: Date.now()
52296
53772
  });
52297
- log13.warn("transition:error", { trait: parsed.traitName, event: parsed.event, from: fromState, error: errorMsg });
53773
+ log12.warn("transition:error", { trait: parsed.traitName, event: parsed.event, from: fromState, error: errorMsg });
52298
53774
  }
52299
53775
  };
52300
53776
  Object.defineProperty(verificationProviderLifecycleListener, "name", {