@almadar/ui 5.21.11 → 5.22.2

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 (87) hide show
  1. package/dist/avl/index.cjs +939 -638
  2. package/dist/avl/index.js +939 -638
  3. package/dist/components/core/molecules/CalendarGrid.d.ts +3 -10
  4. package/dist/components/core/molecules/ContentRenderer.d.ts +2 -2
  5. package/dist/components/core/molecules/DataGrid.d.ts +11 -20
  6. package/dist/components/core/molecules/DataList.d.ts +9 -15
  7. package/dist/components/core/molecules/FormSection.d.ts +4 -4
  8. package/dist/components/core/molecules/PositionedCanvas.d.ts +4 -17
  9. package/dist/components/core/molecules/ReplyTree.d.ts +2 -13
  10. package/dist/components/core/molecules/RichBlockEditor.d.ts +3 -6
  11. package/dist/components/core/molecules/SortableList.d.ts +7 -5
  12. package/dist/components/core/molecules/TableView.d.ts +7 -7
  13. package/dist/components/core/molecules/index.d.ts +3 -3
  14. package/dist/components/core/molecules/useDataDnd.d.ts +5 -5
  15. package/dist/components/core/organisms/CardGrid.d.ts +5 -2
  16. package/dist/components/core/organisms/CaseStudyOrganism.d.ts +4 -3
  17. package/dist/components/core/organisms/DataTable.d.ts +4 -2
  18. package/dist/components/core/organisms/DetailPanel.d.ts +6 -6
  19. package/dist/components/core/organisms/FeatureGridOrganism.d.ts +4 -3
  20. package/dist/components/core/organisms/HeroOrganism.d.ts +4 -5
  21. package/dist/components/core/organisms/List.d.ts +5 -2
  22. package/dist/components/core/organisms/MasterDetail.d.ts +4 -2
  23. package/dist/components/core/organisms/MediaGallery.d.ts +4 -2
  24. package/dist/components/core/organisms/ShowcaseOrganism.d.ts +4 -3
  25. package/dist/components/core/organisms/StatCard.d.ts +5 -2
  26. package/dist/components/core/organisms/StepFlowOrganism.d.ts +4 -3
  27. package/dist/components/core/organisms/Timeline.d.ts +2 -2
  28. package/dist/components/core/organisms/book/index.d.ts +1 -1
  29. package/dist/components/core/organisms/book/types.d.ts +28 -48
  30. package/dist/components/core/organisms/index.d.ts +1 -2
  31. package/dist/components/core/organisms/layout/DashboardGrid.d.ts +2 -2
  32. package/dist/components/core/organisms/marketing-types.d.ts +5 -94
  33. package/dist/components/core/organisms/types.d.ts +9 -27
  34. package/dist/components/core/templates/index.d.ts +6 -6
  35. package/dist/components/game/organisms/BattleBoard.d.ts +14 -90
  36. package/dist/components/game/organisms/CastleBoard.d.ts +7 -21
  37. package/dist/components/game/organisms/UncontrolledBattleBoard.d.ts +2 -7
  38. package/dist/components/game/organisms/WorldMapBoard.d.ts +13 -59
  39. package/dist/components/game/organisms/boardEntity.d.ts +44 -0
  40. package/dist/components/game/organisms/hooks/useBattleState.d.ts +7 -7
  41. package/dist/components/game/organisms/index.d.ts +3 -3
  42. package/dist/components/game/organisms/puzzles/builder/BuilderBoard.d.ts +7 -20
  43. package/dist/components/game/organisms/puzzles/builder/index.d.ts +1 -1
  44. package/dist/components/game/organisms/puzzles/classifier/ClassifierBoard.d.ts +7 -20
  45. package/dist/components/game/organisms/puzzles/classifier/index.d.ts +1 -1
  46. package/dist/components/game/organisms/puzzles/debugger/DebuggerBoard.d.ts +6 -22
  47. package/dist/components/game/organisms/puzzles/debugger/index.d.ts +1 -1
  48. package/dist/components/game/organisms/puzzles/event-handler/EventHandlerBoard.d.ts +6 -33
  49. package/dist/components/game/organisms/puzzles/event-handler/ObjectRulePanel.d.ts +3 -21
  50. package/dist/components/game/organisms/puzzles/event-handler/index.d.ts +2 -2
  51. package/dist/components/game/organisms/puzzles/event-handler/puzzleObject.d.ts +21 -0
  52. package/dist/components/game/organisms/puzzles/negotiator/NegotiatorBoard.d.ts +8 -24
  53. package/dist/components/game/organisms/puzzles/negotiator/index.d.ts +1 -1
  54. package/dist/components/game/organisms/puzzles/sequencer/ActionTile.d.ts +2 -2
  55. package/dist/components/game/organisms/puzzles/sequencer/SequencerBoard.d.ts +7 -36
  56. package/dist/components/game/organisms/puzzles/sequencer/index.d.ts +1 -1
  57. package/dist/components/game/organisms/puzzles/simulator/SimulatorBoard.d.ts +6 -25
  58. package/dist/components/game/organisms/puzzles/simulator/index.d.ts +1 -1
  59. package/dist/components/game/organisms/puzzles/state-architect/StateArchitectBoard.d.ts +7 -40
  60. package/dist/components/game/organisms/puzzles/state-architect/VariablePanel.d.ts +3 -9
  61. package/dist/components/game/organisms/puzzles/state-architect/index.d.ts +2 -2
  62. package/dist/components/game/organisms/three/index.cjs +35 -21
  63. package/dist/components/game/organisms/three/index.js +35 -21
  64. package/dist/components/game/templates/BattleTemplate.d.ts +2 -3
  65. package/dist/components/game/templates/CastleTemplate.d.ts +2 -3
  66. package/dist/components/game/templates/GameCanvas3DBattleTemplate.d.ts +1 -16
  67. package/dist/components/game/templates/GameCanvas3DCastleTemplate.d.ts +1 -18
  68. package/dist/components/game/templates/GameCanvas3DWorldMapTemplate.d.ts +1 -14
  69. package/dist/components/game/templates/GameTemplate.d.ts +1 -6
  70. package/dist/components/game/templates/WorldMapTemplate.d.ts +2 -3
  71. package/dist/components/index.cjs +2036 -1675
  72. package/dist/components/index.js +1148 -787
  73. package/dist/components/marketing/organisms/PricingOrganism.d.ts +4 -3
  74. package/dist/components/marketing/organisms/StatsOrganism.d.ts +4 -3
  75. package/dist/components/marketing/organisms/TeamOrganism.d.ts +4 -3
  76. package/dist/components/marketing/organisms/book/BookChapterView.d.ts +5 -2
  77. package/dist/components/marketing/organisms/book/BookTableOfContents.d.ts +3 -2
  78. package/dist/components/marketing/organisms/book/BookViewer.d.ts +4 -4
  79. package/dist/components/marketing/templates/AboutPageTemplate.d.ts +32 -6
  80. package/dist/components/marketing/templates/FeatureDetailPageTemplate.d.ts +14 -4
  81. package/dist/components/marketing/templates/LandingPageTemplate.d.ts +47 -9
  82. package/dist/components/marketing/templates/PricingPageTemplate.d.ts +23 -5
  83. package/dist/providers/index.cjs +932 -631
  84. package/dist/providers/index.js +932 -631
  85. package/dist/runtime/index.cjs +934 -633
  86. package/dist/runtime/index.js +934 -633
  87. package/package.json +2 -2
package/dist/avl/index.js CHANGED
@@ -7130,7 +7130,7 @@ var init_SvgGrid = __esm({
7130
7130
  x,
7131
7131
  y,
7132
7132
  cols = 4,
7133
- rows = 3,
7133
+ rows: rows2 = 3,
7134
7134
  spacing = 20,
7135
7135
  nodeRadius = 3,
7136
7136
  color = "var(--color-primary)",
@@ -7139,7 +7139,7 @@ var init_SvgGrid = __esm({
7139
7139
  highlights = []
7140
7140
  }) => {
7141
7141
  const highlightSet = new Set(highlights);
7142
- return /* @__PURE__ */ jsx("g", { className, opacity, children: Array.from({ length: rows }).map(
7142
+ return /* @__PURE__ */ jsx("g", { className, opacity, children: Array.from({ length: rows2 }).map(
7143
7143
  (_, row) => Array.from({ length: cols }).map((_2, col) => {
7144
7144
  const index = row * cols + col;
7145
7145
  const isHighlighted = highlightSet.has(index);
@@ -7799,7 +7799,7 @@ var init_Input = __esm({
7799
7799
  onClear,
7800
7800
  value,
7801
7801
  options,
7802
- rows = 3,
7802
+ rows: rows2 = 3,
7803
7803
  onChange,
7804
7804
  ...props
7805
7805
  }, ref) => {
@@ -7849,7 +7849,7 @@ var init_Input = __esm({
7849
7849
  ref,
7850
7850
  value,
7851
7851
  onChange,
7852
- rows,
7852
+ rows: rows2,
7853
7853
  className: baseClassName,
7854
7854
  ...props
7855
7855
  }
@@ -9844,66 +9844,6 @@ var init_RangeSlider = __esm({
9844
9844
  RangeSlider.displayName = "RangeSlider";
9845
9845
  }
9846
9846
  });
9847
- function easeOut(t) {
9848
- return t * (2 - t);
9849
- }
9850
- var AnimatedCounter;
9851
- var init_AnimatedCounter = __esm({
9852
- "components/marketing/atoms/AnimatedCounter.tsx"() {
9853
- "use client";
9854
- init_cn();
9855
- init_Typography();
9856
- AnimatedCounter = ({
9857
- value: rawValue,
9858
- duration = 600,
9859
- prefix,
9860
- suffix,
9861
- className
9862
- }) => {
9863
- const numericRaw = typeof rawValue === "number" ? rawValue : Number.parseFloat(String(rawValue ?? ""));
9864
- const value = !Number.isNaN(numericRaw) ? numericRaw : 0;
9865
- const [displayValue, setDisplayValue] = useState(value);
9866
- const previousValueRef = useRef(value);
9867
- const animationFrameRef = useRef(null);
9868
- useEffect(() => {
9869
- const from = previousValueRef.current;
9870
- const to = value;
9871
- previousValueRef.current = value;
9872
- if (from === to) {
9873
- setDisplayValue(to);
9874
- return;
9875
- }
9876
- const startTime = performance.now();
9877
- const diff = to - from;
9878
- function animate(currentTime) {
9879
- const elapsed = currentTime - startTime;
9880
- const progress = Math.min(elapsed / duration, 1);
9881
- const easedProgress = easeOut(progress);
9882
- setDisplayValue(from + diff * easedProgress);
9883
- if (progress < 1) {
9884
- animationFrameRef.current = requestAnimationFrame(animate);
9885
- } else {
9886
- setDisplayValue(to);
9887
- }
9888
- }
9889
- animationFrameRef.current = requestAnimationFrame(animate);
9890
- return () => {
9891
- if (animationFrameRef.current !== null) {
9892
- cancelAnimationFrame(animationFrameRef.current);
9893
- }
9894
- };
9895
- }, [value, duration]);
9896
- const decimalPlaces = Number.isInteger(value) ? 0 : String(value).split(".")[1]?.length ?? 0;
9897
- const formattedValue = displayValue.toFixed(decimalPlaces);
9898
- return /* @__PURE__ */ jsxs(Typography, { variant: "h3", className: cn("tabular-nums", className), children: [
9899
- prefix,
9900
- formattedValue,
9901
- suffix
9902
- ] });
9903
- };
9904
- AnimatedCounter.displayName = "AnimatedCounter";
9905
- }
9906
- });
9907
9847
  function useInfiniteScroll(onLoadMore, options = {}) {
9908
9848
  const { rootMargin = "200px", hasMore = true, isLoading = false } = options;
9909
9849
  const observerRef = useRef(null);
@@ -12387,15 +12327,15 @@ function HeaderSkeleton({ className }) {
12387
12327
  ] })
12388
12328
  ] });
12389
12329
  }
12390
- function TableSkeleton({ rows = 5, columns = 4, className }) {
12330
+ function TableSkeleton({ rows: rows2 = 5, columns = 4, className }) {
12391
12331
  return /* @__PURE__ */ jsxs(VStack, { gap: "none", className: cn("border border-border rounded-lg overflow-hidden", className), children: [
12392
12332
  /* @__PURE__ */ jsx(HStack, { className: "px-4 py-3 bg-muted/30 border-b border-border", children: Array.from({ length: columns }).map((_, i) => /* @__PURE__ */ jsx(SkeletonBlock, { className: "h-4 flex-1 mx-2" }, i)) }),
12393
- Array.from({ length: rows }).map((_, rowIdx) => /* @__PURE__ */ jsx(
12333
+ Array.from({ length: rows2 }).map((_, rowIdx) => /* @__PURE__ */ jsx(
12394
12334
  HStack,
12395
12335
  {
12396
12336
  className: cn(
12397
12337
  "px-4 py-3",
12398
- rowIdx < rows - 1 && "border-b border-border"
12338
+ rowIdx < rows2 - 1 && "border-b border-border"
12399
12339
  ),
12400
12340
  children: Array.from({ length: columns }).map((_2, colIdx) => /* @__PURE__ */ jsx(SkeletonLine, { className: "flex-1 mx-2" }, colIdx))
12401
12341
  },
@@ -12443,18 +12383,18 @@ function CardSkeleton({ className }) {
12443
12383
  }
12444
12384
  );
12445
12385
  }
12446
- function TextSkeleton({ rows = 3, className }) {
12447
- return /* @__PURE__ */ jsx(VStack, { gap: "sm", className, children: Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsx(
12386
+ function TextSkeleton({ rows: rows2 = 3, className }) {
12387
+ return /* @__PURE__ */ jsx(VStack, { gap: "sm", className, children: Array.from({ length: rows2 }).map((_, i) => /* @__PURE__ */ jsx(
12448
12388
  SkeletonLine,
12449
12389
  {
12450
- className: i === rows - 1 ? "w-2/3" : "w-full"
12390
+ className: i === rows2 - 1 ? "w-2/3" : "w-full"
12451
12391
  },
12452
12392
  i
12453
12393
  )) });
12454
12394
  }
12455
12395
  function Skeleton({
12456
12396
  variant = "text",
12457
- rows,
12397
+ rows: rows2,
12458
12398
  columns,
12459
12399
  fields,
12460
12400
  className
@@ -12464,15 +12404,15 @@ function Skeleton({
12464
12404
  case "header":
12465
12405
  return /* @__PURE__ */ jsx(HeaderSkeleton, { className });
12466
12406
  case "table":
12467
- return /* @__PURE__ */ jsx(TableSkeleton, { rows, columns, className });
12407
+ return /* @__PURE__ */ jsx(TableSkeleton, { rows: rows2, columns, className });
12468
12408
  case "form":
12469
12409
  return /* @__PURE__ */ jsx(FormSkeleton, { fields, className });
12470
12410
  case "card":
12471
12411
  return /* @__PURE__ */ jsx(CardSkeleton, { className });
12472
12412
  case "text":
12473
- return /* @__PURE__ */ jsx(TextSkeleton, { rows, className });
12413
+ return /* @__PURE__ */ jsx(TextSkeleton, { rows: rows2, className });
12474
12414
  default:
12475
- return /* @__PURE__ */ jsx(TextSkeleton, { rows, className });
12415
+ return /* @__PURE__ */ jsx(TextSkeleton, { rows: rows2, className });
12476
12416
  }
12477
12417
  }
12478
12418
  var pulseClass;
@@ -13626,7 +13566,7 @@ var init_Menu = __esm({
13626
13566
  className
13627
13567
  }) => {
13628
13568
  const eventBus = useEventBus();
13629
- const { t } = useTranslate();
13569
+ const { t, direction } = useTranslate();
13630
13570
  const [isOpen, setIsOpen] = useState(false);
13631
13571
  const [activeSubMenu, setActiveSubMenu] = useState(null);
13632
13572
  const [triggerRect, setTriggerRect] = useState(null);
@@ -13680,6 +13620,18 @@ var init_Menu = __esm({
13680
13620
  "bottom-start": "top-full left-0 mt-2",
13681
13621
  "bottom-end": "top-full right-0 mt-2"
13682
13622
  };
13623
+ const rtlMirror = {
13624
+ "top-left": "top-right",
13625
+ "top-right": "top-left",
13626
+ "bottom-left": "bottom-right",
13627
+ "bottom-right": "bottom-left",
13628
+ "top-start": "top-end",
13629
+ "top-end": "top-start",
13630
+ "bottom-start": "bottom-end",
13631
+ "bottom-end": "bottom-start"
13632
+ };
13633
+ const effectivePosition = direction === "rtl" ? rtlMirror[position] ?? position : position;
13634
+ const subMenuSideClass = direction === "rtl" ? "right-full mr-2" : "left-full ml-2";
13683
13635
  const triggerChild = React97__default.isValidElement(trigger) ? trigger : /* @__PURE__ */ jsx(Typography, { variant: "small", as: "span", children: trigger });
13684
13636
  const triggerElement = React97__default.cloneElement(
13685
13637
  triggerChild,
@@ -13707,7 +13659,7 @@ var init_Menu = __esm({
13707
13659
  onMouseEnter: () => hasSubMenu && setActiveSubMenu(itemId),
13708
13660
  "data-testid": item.event ? `action-${item.event}` : void 0,
13709
13661
  className: cn(
13710
- "w-full flex items-center justify-between gap-3 px-4 py-2 text-left",
13662
+ "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
13711
13663
  "text-sm transition-colors",
13712
13664
  "hover:bg-muted",
13713
13665
  "focus:outline-none focus:bg-muted",
@@ -13726,7 +13678,7 @@ var init_Menu = __esm({
13726
13678
  }
13727
13679
  ),
13728
13680
  item.badge !== void 0 && /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
13729
- hasSubMenu && /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: "sm", className: "flex-shrink-0" })
13681
+ hasSubMenu && /* @__PURE__ */ jsx(Icon, { name: direction === "rtl" ? "chevron-left" : "chevron-right", size: "sm", className: "flex-shrink-0" })
13730
13682
  ] })
13731
13683
  },
13732
13684
  itemId
@@ -13746,7 +13698,8 @@ var init_Menu = __esm({
13746
13698
  Box,
13747
13699
  {
13748
13700
  className: cn(
13749
- "absolute left-full top-0 ml-2 z-50",
13701
+ "absolute top-0 z-50",
13702
+ subMenuSideClass,
13750
13703
  menuContainerStyles
13751
13704
  ),
13752
13705
  children: renderMenuItems(item.subMenu)
@@ -13764,12 +13717,12 @@ var init_Menu = __esm({
13764
13717
  className: cn(
13765
13718
  "absolute z-50",
13766
13719
  menuContainerStyles,
13767
- positionClasses3[position],
13720
+ positionClasses3[effectivePosition],
13768
13721
  className
13769
13722
  ),
13770
13723
  style: {
13771
- left: position.includes("left") ? 0 : "auto",
13772
- right: position.includes("right") ? 0 : "auto"
13724
+ left: effectivePosition.includes("left") ? 0 : "auto",
13725
+ right: effectivePosition.includes("right") ? 0 : "auto"
13773
13726
  },
13774
13727
  role: "menu",
13775
13728
  children: renderMenuItems(items)
@@ -14087,7 +14040,7 @@ var init_MapView = __esm({
14087
14040
  shadowSize: [41, 41]
14088
14041
  });
14089
14042
  L.Marker.prototype.options.icon = defaultIcon;
14090
- const { useEffect: useEffect79, useRef: useRef71, useCallback: useCallback122, useState: useState113 } = React97__default;
14043
+ const { useEffect: useEffect79, useRef: useRef71, useCallback: useCallback123, useState: useState113 } = React97__default;
14091
14044
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
14092
14045
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
14093
14046
  function MapUpdater({ centerLat, centerLng, zoom }) {
@@ -14133,7 +14086,7 @@ var init_MapView = __esm({
14133
14086
  }) {
14134
14087
  const eventBus = useEventBus3();
14135
14088
  const [clickedPosition, setClickedPosition] = useState113(null);
14136
- const handleMapClick = useCallback122((lat, lng) => {
14089
+ const handleMapClick = useCallback123((lat, lng) => {
14137
14090
  if (showClickedPin) {
14138
14091
  setClickedPosition({ lat, lng });
14139
14092
  }
@@ -14142,7 +14095,7 @@ var init_MapView = __esm({
14142
14095
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
14143
14096
  }
14144
14097
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
14145
- const handleMarkerClick = useCallback122((marker) => {
14098
+ const handleMarkerClick = useCallback123((marker) => {
14146
14099
  onMarkerClick?.(marker);
14147
14100
  if (markerClickEvent) {
14148
14101
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -14363,7 +14316,7 @@ function InputPattern({
14363
14316
  function TextareaPattern({
14364
14317
  value = "",
14365
14318
  placeholder,
14366
- rows = 4,
14319
+ rows: rows2 = 4,
14367
14320
  disabled = false,
14368
14321
  fieldError,
14369
14322
  onChange,
@@ -14383,7 +14336,7 @@ function TextareaPattern({
14383
14336
  {
14384
14337
  value: localValue,
14385
14338
  placeholder,
14386
- rows,
14339
+ rows: rows2,
14387
14340
  disabled,
14388
14341
  error: fieldError,
14389
14342
  onChange: handleChange,
@@ -14868,6 +14821,91 @@ var init_ActionPalette = __esm({
14868
14821
  ActionPalette.displayName = "ActionPalette";
14869
14822
  }
14870
14823
  });
14824
+ function parseValue(value) {
14825
+ if (value === "" || value == null) return { num: 0, prefix: "", suffix: "", decimals: 0 };
14826
+ const match = String(value).match(/^([^0-9]*)([0-9]+(?:\.[0-9]+)?)(.*)$/);
14827
+ if (!match) {
14828
+ return { num: 0, prefix: "", suffix: String(value), decimals: 0 };
14829
+ }
14830
+ const numStr = match[2];
14831
+ const decimalIdx = numStr.indexOf(".");
14832
+ const decimals = decimalIdx >= 0 ? numStr.length - decimalIdx - 1 : 0;
14833
+ return {
14834
+ prefix: match[1],
14835
+ num: parseFloat(numStr),
14836
+ suffix: match[3],
14837
+ decimals
14838
+ };
14839
+ }
14840
+ var AnimatedCounter;
14841
+ var init_AnimatedCounter = __esm({
14842
+ "components/core/molecules/AnimatedCounter.tsx"() {
14843
+ "use client";
14844
+ init_cn();
14845
+ init_Box();
14846
+ init_Typography();
14847
+ AnimatedCounter = ({
14848
+ value,
14849
+ label,
14850
+ duration = 1500,
14851
+ className
14852
+ }) => {
14853
+ const ref = useRef(null);
14854
+ const [displayValue, setDisplayValue] = useState("0");
14855
+ const [hasAnimated, setHasAnimated] = useState(false);
14856
+ const animate = useCallback(() => {
14857
+ const { num: num2, prefix, suffix, decimals } = parseValue(value);
14858
+ if (num2 === 0) {
14859
+ setDisplayValue(String(value));
14860
+ return;
14861
+ }
14862
+ const startTime = performance.now();
14863
+ const tick = (now2) => {
14864
+ const elapsed = now2 - startTime;
14865
+ const progress = Math.min(elapsed / duration, 1);
14866
+ const eased = 1 - Math.pow(1 - progress, 3);
14867
+ const current = eased * num2;
14868
+ setDisplayValue(`${prefix}${current.toFixed(decimals)}${suffix}`);
14869
+ if (progress < 1) {
14870
+ requestAnimationFrame(tick);
14871
+ } else {
14872
+ setDisplayValue(String(value));
14873
+ }
14874
+ };
14875
+ requestAnimationFrame(tick);
14876
+ }, [value, duration]);
14877
+ useEffect(() => {
14878
+ if (hasAnimated) return;
14879
+ const el = ref.current;
14880
+ if (!el) return;
14881
+ const observer2 = new IntersectionObserver(
14882
+ (entries) => {
14883
+ if (entries[0].isIntersecting) {
14884
+ setHasAnimated(true);
14885
+ animate();
14886
+ observer2.disconnect();
14887
+ }
14888
+ },
14889
+ { threshold: 0.3 }
14890
+ );
14891
+ observer2.observe(el);
14892
+ return () => observer2.disconnect();
14893
+ }, [hasAnimated, animate]);
14894
+ return /* @__PURE__ */ jsxs(Box, { ref, className: cn("flex flex-col items-center gap-1 p-4", className), children: [
14895
+ /* @__PURE__ */ jsx(
14896
+ Typography,
14897
+ {
14898
+ variant: "h2",
14899
+ className: "text-primary font-bold tabular-nums",
14900
+ children: hasAnimated ? displayValue : "0"
14901
+ }
14902
+ ),
14903
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "muted", className: "text-center", children: label })
14904
+ ] });
14905
+ };
14906
+ AnimatedCounter.displayName = "AnimatedCounter";
14907
+ }
14908
+ });
14871
14909
  var AuthLayout;
14872
14910
  var init_AuthLayout = __esm({
14873
14911
  "components/core/templates/AuthLayout.tsx"() {
@@ -16253,6 +16291,39 @@ var init_IsometricCanvas2 = __esm({
16253
16291
  init_IsometricCanvas();
16254
16292
  }
16255
16293
  });
16294
+
16295
+ // components/game/organisms/boardEntity.ts
16296
+ function boardEntity(entity) {
16297
+ if (!entity) return void 0;
16298
+ return Array.isArray(entity) ? entity[0] : entity;
16299
+ }
16300
+ function str(v) {
16301
+ return v == null ? "" : String(v);
16302
+ }
16303
+ function num(v, fallback = 0) {
16304
+ const n = Number(v);
16305
+ return Number.isFinite(n) ? n : fallback;
16306
+ }
16307
+ function rows(v) {
16308
+ return Array.isArray(v) ? v : [];
16309
+ }
16310
+ function vec2(v) {
16311
+ const o = v ?? {};
16312
+ return { x: num(o.x), y: num(o.y) };
16313
+ }
16314
+ function unitPosition(u) {
16315
+ return vec2(u.position);
16316
+ }
16317
+ function unitTeam(u) {
16318
+ return str(u.team);
16319
+ }
16320
+ function unitHealth(u) {
16321
+ return num(u.health);
16322
+ }
16323
+ var init_boardEntity = __esm({
16324
+ "components/game/organisms/boardEntity.ts"() {
16325
+ }
16326
+ });
16256
16327
  function BattleBoard({
16257
16328
  entity,
16258
16329
  scale = 0.45,
@@ -16279,43 +16350,49 @@ function BattleBoard({
16279
16350
  attackEvent,
16280
16351
  className
16281
16352
  }) {
16282
- const tiles = entity.tiles;
16283
- const features = entity.features ?? [];
16284
- const boardWidth = entity.boardWidth ?? 8;
16285
- const boardHeight = entity.boardHeight ?? 6;
16286
- const assetManifest = entity.assetManifest;
16287
- const backgroundImage = entity.backgroundImage;
16288
- const units = entity.units;
16289
- const selectedUnitId = entity.selectedUnitId;
16290
- const currentPhase = entity.phase;
16291
- const currentTurn = entity.turn;
16292
- const gameResult = entity.gameResult;
16353
+ const board = boardEntity(entity) ?? {};
16354
+ const tiles = Array.isArray(board.tiles) ? board.tiles : [];
16355
+ const features = Array.isArray(board.features) ? board.features : [];
16356
+ const boardWidth = num(board.boardWidth, 8);
16357
+ const boardHeight = num(board.boardHeight, 6);
16358
+ const assetManifest = board.assetManifest;
16359
+ const backgroundImage = board.backgroundImage;
16360
+ const units = rows(board.units);
16361
+ const selectedUnitId = board.selectedUnitId ?? null;
16362
+ const currentPhase = str(board.phase) || "observation";
16363
+ const currentTurn = num(board.turn, 1);
16364
+ const gameResult = board.gameResult ?? null;
16293
16365
  const eventBus = useEventBus();
16294
16366
  const { t } = useTranslate();
16295
16367
  const [hoveredTile, setHoveredTile] = useState(null);
16296
16368
  const [isShaking, setIsShaking] = useState(false);
16297
16369
  const selectedUnit = useMemo(
16298
- () => units.find((u) => u.id === selectedUnitId) ?? null,
16370
+ () => units.find((u) => str(u.id) === selectedUnitId) ?? null,
16299
16371
  [units, selectedUnitId]
16300
16372
  );
16301
16373
  const hoveredUnit = useMemo(() => {
16302
16374
  if (!hoveredTile) return null;
16303
- return units.find(
16304
- (u) => u.position.x === hoveredTile.x && u.position.y === hoveredTile.y && u.health > 0
16305
- ) ?? null;
16375
+ return units.find((u) => {
16376
+ const p2 = unitPosition(u);
16377
+ return p2.x === hoveredTile.x && p2.y === hoveredTile.y && unitHealth(u) > 0;
16378
+ }) ?? null;
16306
16379
  }, [hoveredTile, units]);
16307
- const playerUnits = useMemo(() => units.filter((u) => u.team === "player" && u.health > 0), [units]);
16308
- const enemyUnits = useMemo(() => units.filter((u) => u.team === "enemy" && u.health > 0), [units]);
16380
+ const playerUnits = useMemo(() => units.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0), [units]);
16381
+ const enemyUnits = useMemo(() => units.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0), [units]);
16309
16382
  const validMoves = useMemo(() => {
16310
16383
  if (!selectedUnit || currentPhase !== "movement") return [];
16311
16384
  const moves = [];
16312
- const range = selectedUnit.movement;
16385
+ const range = num(selectedUnit.movement);
16386
+ const origin = unitPosition(selectedUnit);
16313
16387
  for (let dy = -range; dy <= range; dy++) {
16314
16388
  for (let dx = -range; dx <= range; dx++) {
16315
- const nx = selectedUnit.position.x + dx;
16316
- const ny = selectedUnit.position.y + dy;
16389
+ const nx = origin.x + dx;
16390
+ const ny = origin.y + dy;
16317
16391
  const dist = Math.abs(dx) + Math.abs(dy);
16318
- if (dist > 0 && dist <= range && nx >= 0 && nx < boardWidth && ny >= 0 && ny < boardHeight && !units.some((u) => u.position.x === nx && u.position.y === ny && u.health > 0)) {
16392
+ if (dist > 0 && dist <= range && nx >= 0 && nx < boardWidth && ny >= 0 && ny < boardHeight && !units.some((u) => {
16393
+ const p2 = unitPosition(u);
16394
+ return p2.x === nx && p2.y === ny && unitHealth(u) > 0;
16395
+ })) {
16319
16396
  moves.push({ x: nx, y: ny });
16320
16397
  }
16321
16398
  }
@@ -16324,11 +16401,14 @@ function BattleBoard({
16324
16401
  }, [selectedUnit, currentPhase, units, boardWidth, boardHeight]);
16325
16402
  const attackTargets = useMemo(() => {
16326
16403
  if (!selectedUnit || currentPhase !== "action") return [];
16327
- return units.filter((u) => u.team !== selectedUnit.team && u.health > 0).filter((u) => {
16328
- const dx = Math.abs(u.position.x - selectedUnit.position.x);
16329
- const dy = Math.abs(u.position.y - selectedUnit.position.y);
16404
+ const sp = unitPosition(selectedUnit);
16405
+ const sTeam = unitTeam(selectedUnit);
16406
+ return units.filter((u) => unitTeam(u) !== sTeam && unitHealth(u) > 0).filter((u) => {
16407
+ const p2 = unitPosition(u);
16408
+ const dx = Math.abs(p2.x - sp.x);
16409
+ const dy = Math.abs(p2.y - sp.y);
16330
16410
  return dx <= 1 && dy <= 1 && dx + dy > 0;
16331
- }).map((u) => u.position);
16411
+ }).map((u) => unitPosition(u));
16332
16412
  }, [selectedUnit, currentPhase, units]);
16333
16413
  const MOVE_SPEED_MS_PER_TILE = 300;
16334
16414
  const movementAnimRef = useRef(null);
@@ -16368,23 +16448,25 @@ function BattleBoard({
16368
16448
  return () => clearInterval(interval);
16369
16449
  }, []);
16370
16450
  const isoUnits = useMemo(() => {
16371
- return units.filter((u) => u.health > 0).map((unit) => {
16372
- const pos = movingPositions.get(unit.id) ?? unit.position;
16451
+ return units.filter((u) => unitHealth(u) > 0).map((unit) => {
16452
+ const id = str(unit.id);
16453
+ const pos = movingPositions.get(id) ?? unitPosition(unit);
16454
+ const unitTraits = Array.isArray(unit.traits) ? unit.traits : void 0;
16373
16455
  return {
16374
- id: unit.id,
16456
+ id,
16375
16457
  position: pos,
16376
- name: unit.name,
16377
- team: unit.team,
16378
- health: unit.health,
16379
- maxHealth: unit.maxHealth,
16380
- unitType: unit.unitType,
16381
- heroId: unit.heroId,
16382
- sprite: unit.sprite,
16383
- traits: unit.traits?.map((t2) => ({
16384
- name: t2.name,
16385
- currentState: t2.currentState,
16386
- states: t2.states,
16387
- cooldown: t2.cooldown ?? 0
16458
+ name: str(unit.name),
16459
+ team: unitTeam(unit),
16460
+ health: unitHealth(unit),
16461
+ maxHealth: num(unit.maxHealth),
16462
+ unitType: unit.unitType == null ? void 0 : str(unit.unitType),
16463
+ heroId: unit.heroId == null ? void 0 : str(unit.heroId),
16464
+ sprite: unit.sprite == null ? void 0 : str(unit.sprite),
16465
+ traits: unitTraits?.map((tr) => ({
16466
+ name: tr.name,
16467
+ currentState: tr.currentState,
16468
+ states: tr.states,
16469
+ cooldown: tr.cooldown ?? 0
16388
16470
  }))
16389
16471
  };
16390
16472
  });
@@ -16396,8 +16478,8 @@ function BattleBoard({
16396
16478
  [scale, baseOffsetX]
16397
16479
  );
16398
16480
  const checkGameEnd = useCallback(() => {
16399
- const pa = units.filter((u) => u.team === "player" && u.health > 0);
16400
- const ea = units.filter((u) => u.team === "enemy" && u.health > 0);
16481
+ const pa = units.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
16482
+ const ea = units.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
16401
16483
  if (pa.length === 0) {
16402
16484
  onGameEnd?.("defeat");
16403
16485
  if (gameEndEvent) {
@@ -16411,21 +16493,22 @@ function BattleBoard({
16411
16493
  }
16412
16494
  }, [units, onGameEnd, gameEndEvent, eventBus]);
16413
16495
  const handleUnitClick = useCallback((unitId) => {
16414
- const unit = units.find((u) => u.id === unitId);
16496
+ const unit = units.find((u) => str(u.id) === unitId);
16415
16497
  if (!unit) return;
16416
16498
  if (unitClickEvent) {
16417
16499
  eventBus.emit(`UI:${unitClickEvent}`, { unitId });
16418
16500
  }
16419
16501
  if (currentPhase === "action" && selectedUnit) {
16420
- if (unit.team === "enemy" && attackTargets.some((t2) => t2.x === unit.position.x && t2.y === unit.position.y)) {
16421
- const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, selectedUnit.attack - unit.defense);
16502
+ const up = unitPosition(unit);
16503
+ if (unitTeam(unit) === "enemy" && attackTargets.some((t2) => t2.x === up.x && t2.y === up.y)) {
16504
+ const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, num(selectedUnit.attack) - num(unit.defense));
16422
16505
  setIsShaking(true);
16423
16506
  setTimeout(() => setIsShaking(false), 300);
16424
16507
  onAttack?.(selectedUnit, unit, damage);
16425
16508
  if (attackEvent) {
16426
16509
  eventBus.emit(`UI:${attackEvent}`, {
16427
- attackerId: selectedUnit.id,
16428
- targetId: unit.id,
16510
+ attackerId: str(selectedUnit.id),
16511
+ targetId: str(unit.id),
16429
16512
  damage
16430
16513
  });
16431
16514
  }
@@ -16440,9 +16523,9 @@ function BattleBoard({
16440
16523
  if (currentPhase === "movement" && selectedUnit) {
16441
16524
  if (movementAnimRef.current) return;
16442
16525
  if (validMoves.some((m) => m.x === x && m.y === y)) {
16443
- const from = { ...selectedUnit.position };
16526
+ const from = { ...unitPosition(selectedUnit) };
16444
16527
  const to = { x, y };
16445
- startMoveAnimation(selectedUnit.id, from, to, () => {
16528
+ startMoveAnimation(str(selectedUnit.id), from, to, () => {
16446
16529
  onUnitMove?.(selectedUnit, to);
16447
16530
  });
16448
16531
  }
@@ -16600,6 +16683,7 @@ var init_BattleBoard = __esm({
16600
16683
  init_Typography();
16601
16684
  init_Stack();
16602
16685
  init_IsometricCanvas2();
16686
+ init_boardEntity();
16603
16687
  init_isometric();
16604
16688
  BattleBoard.displayName = "BattleBoard";
16605
16689
  }
@@ -16993,24 +17077,24 @@ var init_CodeBlock = __esm({
16993
17077
  return;
16994
17078
  }
16995
17079
  lineEls.forEach((el) => {
16996
- const num = parseInt(el.getAttribute("data-line") ?? "-1", 10);
16997
- if (hiddenLines.has(num)) {
17080
+ const num2 = parseInt(el.getAttribute("data-line") ?? "-1", 10);
17081
+ if (hiddenLines.has(num2)) {
16998
17082
  el.style.display = "none";
16999
17083
  return;
17000
17084
  }
17001
17085
  el.style.display = "";
17002
17086
  el.style.position = "relative";
17003
17087
  el.style.paddingLeft = "1.2em";
17004
- const region = foldStartMap.get(num);
17088
+ const region = foldStartMap.get(num2);
17005
17089
  if (!region) return;
17006
- const isCollapsed = collapsed.has(num);
17090
+ const isCollapsed = collapsed.has(num2);
17007
17091
  const toggle = document.createElement("span");
17008
17092
  toggle.className = "fold-toggle";
17009
17093
  toggle.textContent = isCollapsed ? "\u25B6" : "\u25BC";
17010
17094
  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%";
17011
17095
  toggle.addEventListener("click", (e) => {
17012
17096
  e.stopPropagation();
17013
- toggleFoldRef.current(num);
17097
+ toggleFoldRef.current(num2);
17014
17098
  });
17015
17099
  el.insertBefore(toggle, el.firstChild);
17016
17100
  if (isCollapsed) {
@@ -19263,10 +19347,13 @@ var init_BookChapterView = __esm({
19263
19347
  init_cn();
19264
19348
  BookChapterView = ({
19265
19349
  chapter,
19350
+ orbitalSchema,
19266
19351
  direction,
19267
19352
  className
19268
19353
  }) => {
19269
19354
  const { t: _t } = useTranslate();
19355
+ const title = String(chapter.title ?? "");
19356
+ const content = String(chapter.content ?? "");
19270
19357
  return /* @__PURE__ */ jsxs(
19271
19358
  VStack,
19272
19359
  {
@@ -19274,16 +19361,16 @@ var init_BookChapterView = __esm({
19274
19361
  className: cn("px-6 py-8 max-w-4xl mx-auto w-full", className),
19275
19362
  style: { direction },
19276
19363
  children: [
19277
- /* @__PURE__ */ jsx(Typography, { variant: "h1", className: "text-3xl font-bold", children: chapter.title }),
19364
+ /* @__PURE__ */ jsx(Typography, { variant: "h1", className: "text-3xl font-bold", children: title }),
19278
19365
  /* @__PURE__ */ jsx(Divider, {}),
19279
- !!chapter.orbitalSchema && /* @__PURE__ */ jsx(ScaledDiagram, { children: /* @__PURE__ */ jsx(
19366
+ !!orbitalSchema && /* @__PURE__ */ jsx(ScaledDiagram, { children: /* @__PURE__ */ jsx(
19280
19367
  JazariStateMachine,
19281
19368
  {
19282
- schema: chapter.orbitalSchema,
19369
+ schema: orbitalSchema,
19283
19370
  direction
19284
19371
  }
19285
19372
  ) }),
19286
- /* @__PURE__ */ jsx(ContentRenderer, { content: chapter.content, direction })
19373
+ /* @__PURE__ */ jsx(ContentRenderer, { content, direction })
19287
19374
  ]
19288
19375
  }
19289
19376
  );
@@ -19381,7 +19468,7 @@ var init_BookNavBar = __esm({
19381
19468
  BookNavBar = ({
19382
19469
  currentPage,
19383
19470
  totalPages,
19384
- chapterTitle,
19471
+ chapterTitle: chapterTitle2,
19385
19472
  direction,
19386
19473
  className
19387
19474
  }) => {
@@ -19422,12 +19509,12 @@ var init_BookNavBar = __esm({
19422
19509
  )
19423
19510
  ] }),
19424
19511
  /* @__PURE__ */ jsxs(Box, { className: "flex-1 mx-4 max-w-md", children: [
19425
- chapterTitle && /* @__PURE__ */ jsx(
19512
+ chapterTitle2 && /* @__PURE__ */ jsx(
19426
19513
  Typography,
19427
19514
  {
19428
19515
  variant: "caption",
19429
19516
  className: "text-center block truncate text-muted-foreground",
19430
- children: chapterTitle
19517
+ children: chapterTitle2
19431
19518
  }
19432
19519
  ),
19433
19520
  /* @__PURE__ */ jsx(ProgressBar, { value: progress, size: "sm", variant: "primary" })
@@ -19494,31 +19581,35 @@ var init_BookTableOfContents = __esm({
19494
19581
  style: { direction },
19495
19582
  children: [
19496
19583
  /* @__PURE__ */ jsx(Typography, { variant: "h1", className: "text-3xl font-bold text-center mb-4", children: t("book.tableOfContents") }),
19497
- parts.map((part, partIdx) => /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
19498
- /* @__PURE__ */ jsxs(HStack, { gap: "sm", align: "center", children: [
19499
- /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: t("book.partNumber", { number: String(partIdx + 1) }) }),
19500
- /* @__PURE__ */ jsx(Typography, { variant: "h3", className: "font-semibold", children: part.title })
19501
- ] }),
19502
- /* @__PURE__ */ jsx(VStack, { gap: "xs", className: direction === "rtl" ? "pr-6" : "pl-6", children: part.chapters.map((chapter) => {
19503
- const isCurrent = chapter.id === currentChapterId;
19504
- return /* @__PURE__ */ jsx(
19505
- Button,
19506
- {
19507
- variant: "ghost",
19508
- size: "sm",
19509
- action: "BOOK_NAVIGATE",
19510
- actionPayload: { chapterId: chapter.id },
19511
- className: cn(
19512
- "justify-start text-left w-full",
19513
- direction === "rtl" && "text-right",
19514
- isCurrent && "bg-blue-50 dark:bg-blue-950 text-blue-600 dark:text-blue-400"
19515
- ),
19516
- children: /* @__PURE__ */ jsx(Box, { className: "truncate", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: chapter.title }) })
19517
- },
19518
- chapter.id
19519
- );
19520
- }) })
19521
- ] }, partIdx))
19584
+ parts.map((part, partIdx) => {
19585
+ const chapters = Array.isArray(part.chapters) ? part.chapters : [];
19586
+ return /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
19587
+ /* @__PURE__ */ jsxs(HStack, { gap: "sm", align: "center", children: [
19588
+ /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: t("book.partNumber", { number: String(partIdx + 1) }) }),
19589
+ /* @__PURE__ */ jsx(Typography, { variant: "h3", className: "font-semibold", children: String(part.title ?? "") })
19590
+ ] }),
19591
+ /* @__PURE__ */ jsx(VStack, { gap: "xs", className: direction === "rtl" ? "pr-6" : "pl-6", children: chapters.map((chapter) => {
19592
+ const id = chapter.id == null ? "" : String(chapter.id);
19593
+ const isCurrent = id === currentChapterId;
19594
+ return /* @__PURE__ */ jsx(
19595
+ Button,
19596
+ {
19597
+ variant: "ghost",
19598
+ size: "sm",
19599
+ action: "BOOK_NAVIGATE",
19600
+ actionPayload: { chapterId: id },
19601
+ className: cn(
19602
+ "justify-start text-left w-full",
19603
+ direction === "rtl" && "text-right",
19604
+ isCurrent && "bg-blue-50 dark:bg-blue-950 text-blue-600 dark:text-blue-400"
19605
+ ),
19606
+ children: /* @__PURE__ */ jsx(Box, { className: "truncate", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: String(chapter.title ?? "") }) })
19607
+ },
19608
+ id
19609
+ );
19610
+ }) })
19611
+ ] }, partIdx);
19612
+ })
19522
19613
  ]
19523
19614
  }
19524
19615
  );
@@ -19640,27 +19731,41 @@ function resolveFieldMap(fieldMap) {
19640
19731
  function get(obj, key) {
19641
19732
  return obj[key];
19642
19733
  }
19734
+ function asStr(v) {
19735
+ return v == null ? "" : String(v);
19736
+ }
19643
19737
  function mapBookData(raw, fields = IDENTITY_BOOK_FIELDS) {
19644
19738
  const rawParts = get(raw, fields.parts) ?? [];
19645
- return {
19646
- title: get(raw, fields.title) ?? "",
19647
- subtitle: get(raw, fields.subtitle),
19648
- author: get(raw, fields.author),
19649
- coverImageUrl: get(raw, fields.coverImageUrl),
19650
- direction: get(raw, fields.direction) ?? void 0,
19651
- parts: rawParts.map((part) => {
19652
- const rawChapters = get(part, fields.chapters) ?? [];
19653
- return {
19654
- title: get(part, fields.partTitle) ?? "",
19655
- chapters: rawChapters.map((ch) => ({
19656
- id: get(ch, fields.chapterId) ?? "",
19657
- title: get(ch, fields.chapterTitle) ?? "",
19658
- content: get(ch, fields.chapterContent) ?? "",
19659
- orbitalSchema: get(ch, fields.chapterOrbitalSchema)
19660
- }))
19661
- };
19662
- })
19739
+ const direction = get(raw, fields.direction) ?? "ltr";
19740
+ const cover = {
19741
+ title: asStr(get(raw, fields.title)),
19742
+ subtitle: asStr(get(raw, fields.subtitle)),
19743
+ author: asStr(get(raw, fields.author)),
19744
+ coverImageUrl: asStr(get(raw, fields.coverImageUrl)),
19745
+ direction
19663
19746
  };
19747
+ const schemaByChapterId = {};
19748
+ const chapters = [];
19749
+ const parts = rawParts.map((part) => {
19750
+ const rawChapters = get(part, fields.chapters) ?? [];
19751
+ const chapterRows = rawChapters.map((ch) => {
19752
+ const id = asStr(get(ch, fields.chapterId));
19753
+ const schema = get(ch, fields.chapterOrbitalSchema);
19754
+ if (schema) schemaByChapterId[id] = schema;
19755
+ const row = {
19756
+ id,
19757
+ title: asStr(get(ch, fields.chapterTitle)),
19758
+ content: asStr(get(ch, fields.chapterContent))
19759
+ };
19760
+ chapters.push(row);
19761
+ return row;
19762
+ });
19763
+ return {
19764
+ title: asStr(get(part, fields.partTitle)),
19765
+ chapters: chapterRows
19766
+ };
19767
+ });
19768
+ return { cover, direction, parts, chapters, schemaByChapterId };
19664
19769
  }
19665
19770
  var IDENTITY_BOOK_FIELDS, AR_BOOK_FIELDS, FIELD_MAP_REGISTRY;
19666
19771
  var init_types2 = __esm({
@@ -19698,10 +19803,7 @@ var init_types2 = __esm({
19698
19803
  };
19699
19804
  }
19700
19805
  });
19701
- function flattenChapters(book) {
19702
- return book.parts.flatMap((part) => part.chapters);
19703
- }
19704
- var PRINT_STYLES, BookViewer;
19806
+ var chapterId, chapterTitle, PRINT_STYLES, BookViewer;
19705
19807
  var init_BookViewer = __esm({
19706
19808
  "components/marketing/organisms/book/BookViewer.tsx"() {
19707
19809
  init_Box();
@@ -19714,6 +19816,8 @@ var init_BookViewer = __esm({
19714
19816
  init_BookNavBar();
19715
19817
  init_EmptyState();
19716
19818
  init_types2();
19819
+ chapterId = (ch) => ch?.id == null ? void 0 : String(ch.id);
19820
+ chapterTitle = (ch) => ch?.title == null ? void 0 : String(ch.title);
19717
19821
  PRINT_STYLES = `
19718
19822
  @media print {
19719
19823
  .book-viewer-page {
@@ -19742,14 +19846,14 @@ var init_BookViewer = __esm({
19742
19846
  return mapBookData(raw, resolvedFieldMap);
19743
19847
  }, [entity, resolvedFieldMap]);
19744
19848
  const direction = book?.direction ?? "ltr";
19745
- const chapters = useMemo(() => book ? flattenChapters(book) : [], [book]);
19849
+ const chapters = useMemo(() => book ? book.chapters : [], [book]);
19746
19850
  const totalPages = 2 + chapters.length;
19747
19851
  const navigateTo = useCallback(
19748
19852
  (page) => {
19749
19853
  const clamped = Math.max(0, Math.min(page, totalPages - 1));
19750
19854
  setCurrentPage(clamped);
19751
- const chapterId = clamped >= 2 ? chapters[clamped - 2]?.id : void 0;
19752
- eventBus.emit("UI:BOOK_PAGE_CHANGE", { pageIndex: clamped, chapterId });
19855
+ const id = clamped >= 2 ? chapterId(chapters[clamped - 2]) : void 0;
19856
+ eventBus.emit("UI:BOOK_PAGE_CHANGE", { pageIndex: clamped, chapterId: id });
19753
19857
  },
19754
19858
  [totalPages, chapters, eventBus]
19755
19859
  );
@@ -19761,8 +19865,8 @@ var init_BookViewer = __esm({
19761
19865
  eventBus.on("UI:BOOK_PAGE_NEXT", () => navigateTo(currentPage + 1)),
19762
19866
  eventBus.on("UI:BOOK_PRINT", () => window.print()),
19763
19867
  eventBus.on("UI:BOOK_NAVIGATE", (event) => {
19764
- const chapterId = event.payload?.chapterId;
19765
- const idx = chapters.findIndex((ch) => ch.id === chapterId);
19868
+ const targetId = event.payload?.chapterId;
19869
+ const idx = chapters.findIndex((ch) => chapterId(ch) === targetId);
19766
19870
  if (idx >= 0) navigateTo(idx + 2);
19767
19871
  })
19768
19872
  ];
@@ -19779,9 +19883,11 @@ var init_BookViewer = __esm({
19779
19883
  style.remove();
19780
19884
  };
19781
19885
  }, []);
19782
- const currentChapterId = currentPage >= 2 ? chapters[currentPage - 2]?.id : void 0;
19783
- const currentChapterTitle = currentPage >= 2 ? chapters[currentPage - 2]?.title : void 0;
19886
+ const currentChapterId = currentPage >= 2 ? chapterId(chapters[currentPage - 2]) : void 0;
19887
+ const currentChapterTitle = currentPage >= 2 ? chapterTitle(chapters[currentPage - 2]) : void 0;
19784
19888
  if (!book) return /* @__PURE__ */ jsx(EmptyState, { message: t("book.noData") });
19889
+ const cover = book.cover;
19890
+ const coverTitle = String(cover.title ?? "");
19785
19891
  return /* @__PURE__ */ jsxs(VStack, { className: cn("relative h-full overflow-hidden bg-background", className), children: [
19786
19892
  /* @__PURE__ */ jsxs(
19787
19893
  Box,
@@ -19793,10 +19899,10 @@ var init_BookViewer = __esm({
19793
19899
  /* @__PURE__ */ jsx(
19794
19900
  BookCoverPage,
19795
19901
  {
19796
- title: book.title,
19797
- subtitle: book.subtitle,
19798
- author: book.author,
19799
- coverImageUrl: book.coverImageUrl,
19902
+ title: coverTitle,
19903
+ subtitle: String(cover.subtitle ?? "") || void 0,
19904
+ author: String(cover.author ?? "") || void 0,
19905
+ coverImageUrl: String(cover.coverImageUrl ?? "") || void 0,
19800
19906
  direction
19801
19907
  }
19802
19908
  ),
@@ -19807,23 +19913,27 @@ var init_BookViewer = __esm({
19807
19913
  direction
19808
19914
  }
19809
19915
  ),
19810
- chapters.map((chapter) => /* @__PURE__ */ jsx(
19811
- BookChapterView,
19812
- {
19813
- chapter,
19814
- direction
19815
- },
19816
- chapter.id
19817
- ))
19916
+ chapters.map((chapter) => {
19917
+ const id = chapterId(chapter);
19918
+ return /* @__PURE__ */ jsx(
19919
+ BookChapterView,
19920
+ {
19921
+ chapter,
19922
+ orbitalSchema: id ? book.schemaByChapterId[id] : void 0,
19923
+ direction
19924
+ },
19925
+ id
19926
+ );
19927
+ })
19818
19928
  ] }),
19819
19929
  /* @__PURE__ */ jsxs(Box, { className: "print:hidden", children: [
19820
19930
  currentPage === 0 && /* @__PURE__ */ jsx(
19821
19931
  BookCoverPage,
19822
19932
  {
19823
- title: book.title,
19824
- subtitle: book.subtitle,
19825
- author: book.author,
19826
- coverImageUrl: book.coverImageUrl,
19933
+ title: coverTitle,
19934
+ subtitle: String(cover.subtitle ?? "") || void 0,
19935
+ author: String(cover.author ?? "") || void 0,
19936
+ coverImageUrl: String(cover.coverImageUrl ?? "") || void 0,
19827
19937
  direction
19828
19938
  }
19829
19939
  ),
@@ -19839,6 +19949,7 @@ var init_BookViewer = __esm({
19839
19949
  BookChapterView,
19840
19950
  {
19841
19951
  chapter: chapters[currentPage - 2],
19952
+ orbitalSchema: currentChapterId ? book.schemaByChapterId[currentChapterId] : void 0,
19842
19953
  direction
19843
19954
  }
19844
19955
  )
@@ -19851,7 +19962,7 @@ var init_BookViewer = __esm({
19851
19962
  {
19852
19963
  currentPage,
19853
19964
  totalPages,
19854
- chapterTitle: currentPage === 0 ? book.title : currentPage === 1 ? t("book.tableOfContents") : currentChapterTitle,
19965
+ chapterTitle: currentPage === 0 ? coverTitle : currentPage === 1 ? t("book.tableOfContents") : currentChapterTitle,
19855
19966
  direction
19856
19967
  }
19857
19968
  )
@@ -19949,7 +20060,7 @@ var init_Grid = __esm({
19949
20060
  };
19950
20061
  Grid = ({
19951
20062
  cols = 1,
19952
- rows,
20063
+ rows: rows2,
19953
20064
  gap = "md",
19954
20065
  rowGap,
19955
20066
  colGap,
@@ -19961,7 +20072,7 @@ var init_Grid = __esm({
19961
20072
  children,
19962
20073
  as: Component = "div"
19963
20074
  }) => {
19964
- const mergedStyle = rows ? { gridTemplateRows: `repeat(${rows}, minmax(0, 1fr))`, ...style } : style;
20075
+ const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
19965
20076
  return React97__default.createElement(
19966
20077
  Component,
19967
20078
  {
@@ -20677,14 +20788,14 @@ function BuilderBoard({
20677
20788
  }) {
20678
20789
  const { emit } = useEventBus();
20679
20790
  const { t } = useTranslate();
20680
- const resolved = Array.isArray(entity) ? entity[0] : entity;
20791
+ const resolved = boardEntity(entity);
20681
20792
  const [placements, setPlacements] = useState({});
20682
20793
  const [headerError, setHeaderError] = useState(false);
20683
20794
  const [submitted, setSubmitted] = useState(false);
20684
20795
  const [attempts, setAttempts] = useState(0);
20685
20796
  const [showHint, setShowHint] = useState(false);
20686
- const components = resolved?.components ?? [];
20687
- const slots = resolved?.slots ?? [];
20797
+ const components = Array.isArray(resolved?.components) ? resolved.components : [];
20798
+ const slots = Array.isArray(resolved?.slots) ? resolved.slots : [];
20688
20799
  const usedComponentIds = new Set(Object.values(placements));
20689
20800
  const availableComponents = components.filter((c) => !usedComponentIds.has(c.id));
20690
20801
  const [selectedComponent, setSelectedComponent] = useState(null);
@@ -20718,7 +20829,7 @@ function BuilderBoard({
20718
20829
  }, [slots, placements, attempts, completeEvent, emit]);
20719
20830
  const handleReset = () => {
20720
20831
  setSubmitted(false);
20721
- if (attempts >= 2 && resolved?.hint) {
20832
+ if (attempts >= 2 && str(resolved?.hint)) {
20722
20833
  setShowHint(true);
20723
20834
  }
20724
20835
  };
@@ -20731,20 +20842,24 @@ function BuilderBoard({
20731
20842
  };
20732
20843
  const getComponentById = (id) => components.find((c) => c.id === id);
20733
20844
  if (!resolved) return null;
20845
+ const theme = resolved.theme ?? void 0;
20846
+ const themeBackground = theme?.background;
20847
+ const headerImage = str(resolved.headerImage);
20848
+ const hint = str(resolved.hint);
20734
20849
  return /* @__PURE__ */ jsx(
20735
20850
  Box,
20736
20851
  {
20737
20852
  className,
20738
20853
  style: {
20739
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
20854
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
20740
20855
  backgroundSize: "cover",
20741
20856
  backgroundPosition: "center"
20742
20857
  },
20743
20858
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
20744
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
20859
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
20745
20860
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
20746
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
20747
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description })
20861
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
20862
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) })
20748
20863
  ] }) }),
20749
20864
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
20750
20865
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("builder.components") }),
@@ -20804,9 +20919,9 @@ function BuilderBoard({
20804
20919
  ] }) }),
20805
20920
  submitted && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
20806
20921
  /* @__PURE__ */ jsx(Icon, { icon: allCorrect ? CheckCircle : XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
20807
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("builder.success") : resolved.failMessage ?? t("builder.incorrect") })
20922
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("builder.success") : str(resolved.failMessage) || t("builder.incorrect") })
20808
20923
  ] }) }),
20809
- showHint && resolved.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
20924
+ showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
20810
20925
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
20811
20926
  !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
20812
20927
  /* @__PURE__ */ jsx(Icon, { icon: Wrench, size: "sm" }),
@@ -20825,6 +20940,7 @@ var init_BuilderBoard = __esm({
20825
20940
  "components/game/organisms/puzzles/builder/BuilderBoard.tsx"() {
20826
20941
  init_atoms2();
20827
20942
  init_useEventBus();
20943
+ init_boardEntity();
20828
20944
  BuilderBoard.displayName = "BuilderBoard";
20829
20945
  }
20830
20946
  });
@@ -21162,21 +21278,24 @@ function CalendarGrid({
21162
21278
  eventBus.emit(`UI:${longPressEvent}`, { date: day.toISOString(), time, ...longPressPayload });
21163
21279
  }, 500);
21164
21280
  }, [longPressEvent, longPressPayload, eventBus]);
21165
- const renderEvent = (event) => /* @__PURE__ */ jsx(
21166
- Box,
21167
- {
21168
- rounded: "md",
21169
- padding: "xs",
21170
- border: true,
21171
- className: cn(
21172
- "cursor-pointer hover:shadow-sm transition-shadow text-xs truncate",
21173
- event.color ? event.color : "bg-blue-500/15 border-blue-500/30 text-blue-600"
21174
- ),
21175
- onClick: (e) => handleEventClick(event, e),
21176
- children: /* @__PURE__ */ jsx(Typography, { variant: "small", className: "truncate font-medium", children: event.title })
21177
- },
21178
- event.id
21179
- );
21281
+ const renderEvent = (event) => {
21282
+ const color = event.color;
21283
+ return /* @__PURE__ */ jsx(
21284
+ Box,
21285
+ {
21286
+ rounded: "md",
21287
+ padding: "xs",
21288
+ border: true,
21289
+ className: cn(
21290
+ "cursor-pointer hover:shadow-sm transition-shadow text-xs truncate",
21291
+ color ? color : "bg-blue-500/15 border-blue-500/30 text-blue-600"
21292
+ ),
21293
+ onClick: (e) => handleEventClick(event, e),
21294
+ children: /* @__PURE__ */ jsx(Typography, { variant: "small", className: "truncate font-medium", children: event.title })
21295
+ },
21296
+ event.id
21297
+ );
21298
+ };
21180
21299
  return /* @__PURE__ */ jsxs(
21181
21300
  Box,
21182
21301
  {
@@ -22840,7 +22959,6 @@ var init_CardGrid = __esm({
22840
22959
  alignItems = "stretch",
22841
22960
  className,
22842
22961
  children,
22843
- // EntityDisplayProps
22844
22962
  entity,
22845
22963
  isLoading = false,
22846
22964
  error = null,
@@ -23312,14 +23430,14 @@ var init_CaseStudyOrganism = __esm({
23312
23430
  /* @__PURE__ */ jsx(SimpleGrid, { cols: cols > 0 ? cols : 1, gap: "lg", children: items.map((study) => /* @__PURE__ */ jsx(
23313
23431
  CaseStudyCard,
23314
23432
  {
23315
- title: study.title,
23316
- description: study.description,
23317
- category: study.category,
23318
- categoryColor: study.categoryColor,
23319
- href: study.href,
23320
- linkLabel: study.linkLabel
23433
+ title: String(study.title ?? ""),
23434
+ description: String(study.description ?? ""),
23435
+ category: String(study.category ?? ""),
23436
+ categoryColor: study.categoryColor != null ? String(study.categoryColor) : void 0,
23437
+ href: String(study.href ?? ""),
23438
+ linkLabel: study.linkLabel != null ? String(study.linkLabel) : void 0
23321
23439
  },
23322
- study.id
23440
+ String(study.id ?? "")
23323
23441
  )) })
23324
23442
  ] });
23325
23443
  };
@@ -23342,10 +23460,10 @@ function CastleBoard({
23342
23460
  className
23343
23461
  }) {
23344
23462
  const eventBus = useEventBus();
23345
- const resolved = Array.isArray(entity) ? entity[0] : entity;
23346
- const tiles = resolved?.tiles ?? [];
23347
- const features = resolved?.features ?? [];
23348
- const units = resolved?.units ?? [];
23463
+ const resolved = boardEntity(entity);
23464
+ const tiles = Array.isArray(resolved?.tiles) ? resolved.tiles : [];
23465
+ const features = Array.isArray(resolved?.features) ? resolved.features : [];
23466
+ const units = Array.isArray(resolved?.units) ? resolved.units : [];
23349
23467
  const assetManifest = resolved?.assetManifest;
23350
23468
  const backgroundImage = resolved?.backgroundImage;
23351
23469
  const [hoveredTile, setHoveredTile] = useState(null);
@@ -23373,7 +23491,7 @@ function CastleBoard({
23373
23491
  onFeatureClick?.(feature);
23374
23492
  if (featureClickEvent) {
23375
23493
  eventBus.emit(`UI:${featureClickEvent}`, {
23376
- featureId: feature.id,
23494
+ featureId: feature.id ?? "",
23377
23495
  featureType: feature.type,
23378
23496
  x: feature.x,
23379
23497
  y: feature.y
@@ -23441,6 +23559,7 @@ var init_CastleBoard = __esm({
23441
23559
  init_cn();
23442
23560
  init_useEventBus();
23443
23561
  init_IsometricCanvas2();
23562
+ init_boardEntity();
23444
23563
  init_isometric();
23445
23564
  CastleBoard.displayName = "CastleBoard";
23446
23565
  }
@@ -24251,14 +24370,14 @@ function ClassifierBoard({
24251
24370
  }) {
24252
24371
  const { emit } = useEventBus();
24253
24372
  const { t } = useTranslate();
24254
- const resolved = Array.isArray(entity) ? entity[0] : entity;
24373
+ const resolved = boardEntity(entity);
24255
24374
  const [assignments, setAssignments] = useState({});
24256
24375
  const [headerError, setHeaderError] = useState(false);
24257
24376
  const [submitted, setSubmitted] = useState(false);
24258
24377
  const [attempts, setAttempts] = useState(0);
24259
24378
  const [showHint, setShowHint] = useState(false);
24260
- const items = resolved?.items ?? [];
24261
- const categories = resolved?.categories ?? [];
24379
+ const items = Array.isArray(resolved?.items) ? resolved.items : [];
24380
+ const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
24262
24381
  const unassignedItems = items.filter((item) => !assignments[item.id]);
24263
24382
  const allAssigned = Object.keys(assignments).length === items.length;
24264
24383
  const results = submitted ? items.map((item) => ({
@@ -24290,7 +24409,7 @@ function ClassifierBoard({
24290
24409
  }, [items, assignments, attempts, completeEvent, emit]);
24291
24410
  const handleReset = () => {
24292
24411
  setSubmitted(false);
24293
- if (attempts >= 2 && resolved?.hint) {
24412
+ if (attempts >= 2 && str(resolved?.hint)) {
24294
24413
  setShowHint(true);
24295
24414
  }
24296
24415
  };
@@ -24301,20 +24420,25 @@ function ClassifierBoard({
24301
24420
  setShowHint(false);
24302
24421
  };
24303
24422
  if (!resolved) return null;
24423
+ const theme = resolved.theme ?? void 0;
24424
+ const themeBackground = theme?.background;
24425
+ const headerImage = str(resolved.headerImage);
24426
+ const hint = str(resolved.hint);
24427
+ const failMessage = str(resolved.failMessage);
24304
24428
  return /* @__PURE__ */ jsx(
24305
24429
  Box,
24306
24430
  {
24307
24431
  className,
24308
24432
  style: {
24309
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
24433
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
24310
24434
  backgroundSize: "cover",
24311
24435
  backgroundPosition: "center"
24312
24436
  },
24313
24437
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
24314
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
24438
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
24315
24439
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
24316
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
24317
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description })
24440
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
24441
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) })
24318
24442
  ] }) }),
24319
24443
  unassignedItems.length > 0 && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
24320
24444
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("classifier.itemsToSort") }),
@@ -24366,10 +24490,10 @@ function ClassifierBoard({
24366
24490
  }) }),
24367
24491
  submitted && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
24368
24492
  /* @__PURE__ */ jsx(Icon, { icon: allCorrect ? CheckCircle : XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
24369
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("classifier.allCorrect") : `${correctCount}/${items.length} ${t("classifier.correct")}` }),
24370
- !allCorrect && resolved.failMessage && /* @__PURE__ */ jsx(Typography, { variant: "body", className: "text-muted-foreground", children: resolved.failMessage })
24493
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("classifier.allCorrect") : `${correctCount}/${items.length} ${t("classifier.correct")}` }),
24494
+ !allCorrect && failMessage && /* @__PURE__ */ jsx(Typography, { variant: "body", className: "text-muted-foreground", children: failMessage })
24371
24495
  ] }) }),
24372
- showHint && resolved.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
24496
+ showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
24373
24497
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
24374
24498
  !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
24375
24499
  /* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
@@ -24388,6 +24512,7 @@ var init_ClassifierBoard = __esm({
24388
24512
  "components/game/organisms/puzzles/classifier/ClassifierBoard.tsx"() {
24389
24513
  init_atoms2();
24390
24514
  init_useEventBus();
24515
+ init_boardEntity();
24391
24516
  ClassifierBoard.displayName = "ClassifierBoard";
24392
24517
  }
24393
24518
  });
@@ -30758,7 +30883,7 @@ function InventoryPanel({
30758
30883
  const slotArray = Array.from({ length: safeSlots }, (_, index) => {
30759
30884
  return safeItems[index] ?? null;
30760
30885
  });
30761
- const rows = Math.ceil(safeSlots / safeColumns);
30886
+ const rows2 = Math.ceil(safeSlots / safeColumns);
30762
30887
  const handleSlotClick = useCallback((index) => {
30763
30888
  if (selectSlotEvent) eventBus.emit(`UI:${selectSlotEvent}`, { index });
30764
30889
  onSelectSlot?.(index);
@@ -30827,7 +30952,7 @@ function InventoryPanel({
30827
30952
  className: "grid gap-1 bg-[var(--color-card)] p-2 rounded-container border border-border",
30828
30953
  style: {
30829
30954
  gridTemplateColumns: `repeat(${safeColumns}, ${slotSize}px)`,
30830
- gridTemplateRows: `repeat(${rows}, ${slotSize}px)`
30955
+ gridTemplateRows: `repeat(${rows2}, ${slotSize}px)`
30831
30956
  },
30832
30957
  children: slotArray.map((item, index) => /* @__PURE__ */ jsx(
30833
30958
  "button",
@@ -34791,11 +34916,11 @@ function LatticeSVG({
34791
34916
  }) {
34792
34917
  const paths = [];
34793
34918
  const cols = 5;
34794
- const rows = Math.ceil(h / (w / cols));
34919
+ const rows2 = Math.ceil(h / (w / cols));
34795
34920
  const cellW = w / cols;
34796
34921
  const cellH = cellW;
34797
34922
  const bulge = cellW * 0.3;
34798
- for (let row = 0; row < rows; row++) {
34923
+ for (let row = 0; row < rows2; row++) {
34799
34924
  for (let col = 0; col < cols; col++) {
34800
34925
  const cx = col * cellW + cellW / 2;
34801
34926
  const cy = row * cellH + cellH / 2;
@@ -35207,7 +35332,7 @@ var init_MatrixQuestion = __esm({
35207
35332
  };
35208
35333
  MatrixQuestion = ({
35209
35334
  title,
35210
- rows,
35335
+ rows: rows2,
35211
35336
  columns = DEFAULT_MATRIX_COLUMNS,
35212
35337
  values,
35213
35338
  onChange,
@@ -35217,7 +35342,7 @@ var init_MatrixQuestion = __esm({
35217
35342
  className
35218
35343
  }) => {
35219
35344
  const styles = sizeStyles13[size];
35220
- const safeRows = rows ?? [];
35345
+ const safeRows = rows2 ?? [];
35221
35346
  const safeValues = values ?? {};
35222
35347
  const eventBus = useEventBus();
35223
35348
  const handleChange = useCallback(
@@ -35845,7 +35970,8 @@ var init_PositionedCanvas = __esm({
35845
35970
  dragRef.current = null;
35846
35971
  setDraggingId(null);
35847
35972
  if (!wasDrag) {
35848
- const next = selectedId === item.id ? null : item.id;
35973
+ const itemId = item.id;
35974
+ const next = selectedId === itemId ? null : itemId;
35849
35975
  onSelect?.(next);
35850
35976
  if (selectEvent) {
35851
35977
  eventBus.emit(`UI:${selectEvent}`, { id: next });
@@ -35880,15 +36006,22 @@ var init_PositionedCanvas = __esm({
35880
36006
  style: { width, height },
35881
36007
  onClick: handleContainerClick,
35882
36008
  children: items.map((item) => {
36009
+ const itemId = item.id;
36010
+ const label = item.label;
36011
+ const x = item.x;
36012
+ const y = item.y;
36013
+ const capacity = item.capacity;
36014
+ const partySize = item.partySize;
36015
+ const serverName = item.serverName;
35883
36016
  const status = item.status ?? "empty";
35884
36017
  const shape = item.shape ?? "round";
35885
- const isSelected = selectedId === item.id;
35886
- const isDragging = draggingId === item.id;
36018
+ const isSelected = selectedId === itemId;
36019
+ const isDragging = draggingId === itemId;
35887
36020
  const statusBadge = STATUS_BADGE[status];
35888
36021
  return /* @__PURE__ */ jsxs(
35889
36022
  Box,
35890
36023
  {
35891
- "data-testid": `item-node-${item.id}`,
36024
+ "data-testid": `item-node-${itemId}`,
35892
36025
  "data-status": status,
35893
36026
  className: cn(
35894
36027
  "absolute flex flex-col items-center justify-center gap-1 border-2 select-none",
@@ -35899,7 +36032,7 @@ var init_PositionedCanvas = __esm({
35899
36032
  isSelected && "outline outline-2 outline-offset-2 outline-primary shadow-md",
35900
36033
  isDragging && "shadow-lg z-10"
35901
36034
  ),
35902
- style: { left: item.x, top: item.y, touchAction: "none" },
36035
+ style: { left: x, top: y, touchAction: "none" },
35903
36036
  onPointerDown: (e) => handlePointerDown(e, item),
35904
36037
  onPointerMove: handlePointerMove,
35905
36038
  onPointerUp: (e) => handlePointerUp(e, item),
@@ -35907,10 +36040,10 @@ var init_PositionedCanvas = __esm({
35907
36040
  children: [
35908
36041
  /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-1", children: [
35909
36042
  getStatusIcon(status),
35910
- /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "semibold", children: item.label })
36043
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "semibold", children: label })
35911
36044
  ] }),
35912
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: item.partySize !== void 0 && status === "seated" ? `${item.partySize}/${item.capacity}` : `Cap ${item.capacity}` }),
35913
- status === "seated" && item.serverName && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "truncate max-w-[80%]", children: item.serverName }),
36045
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: partySize !== void 0 && status === "seated" ? `${partySize}/${capacity}` : `Cap ${capacity}` }),
36046
+ status === "seated" && serverName && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "truncate max-w-[80%]", children: serverName }),
35914
36047
  isSelected && /* @__PURE__ */ jsx(
35915
36048
  Badge,
35916
36049
  {
@@ -35922,7 +36055,7 @@ var init_PositionedCanvas = __esm({
35922
36055
  )
35923
36056
  ]
35924
36057
  },
35925
- item.id
36058
+ itemId
35926
36059
  );
35927
36060
  })
35928
36061
  }
@@ -36694,9 +36827,10 @@ var init_RichBlockEditor = __esm({
36694
36827
  });
36695
36828
  function collectInitiallyCollapsed(nodes, acc) {
36696
36829
  for (const n of nodes) {
36830
+ const replies = n.replies;
36697
36831
  if (n.collapsed) acc.add(n.id);
36698
- if (n.replies && n.replies.length > 0) {
36699
- collectInitiallyCollapsed(n.replies, acc);
36832
+ if (replies && replies.length > 0) {
36833
+ collectInitiallyCollapsed(replies, acc);
36700
36834
  }
36701
36835
  }
36702
36836
  }
@@ -36726,44 +36860,52 @@ var init_ReplyTree = __esm({
36726
36860
  }) => {
36727
36861
  const eventBus = useEventBus();
36728
36862
  const { t } = useTranslate();
36729
- const hasReplies = !!node.replies && node.replies.length > 0;
36730
- const isCollapsed = collapsedSet.has(node.id);
36863
+ const nodeId = node.id;
36864
+ const authorName = node.authorName;
36865
+ const authorAvatarUrl = node.authorAvatarUrl;
36866
+ const content = node.content;
36867
+ const postedAt = node.postedAt;
36868
+ const voteCount = node.voteCount;
36869
+ const userVote = node.userVote;
36870
+ const replies = node.replies;
36871
+ const hasReplies = !!replies && replies.length > 0;
36872
+ const isCollapsed = collapsedSet.has(nodeId);
36731
36873
  const atMaxDepth = depth >= maxDepth;
36732
36874
  const [replyOpen, setReplyOpen] = useState(false);
36733
36875
  const [draft, setDraft] = useState("");
36734
36876
  const handleVote = useCallback(
36735
36877
  (next) => {
36736
- onVote?.(node.id, next);
36737
- if (voteEvent) eventBus.emit(`UI:${voteEvent}`, { nodeId: node.id, vote: next });
36878
+ onVote?.(nodeId, next);
36879
+ if (voteEvent) eventBus.emit(`UI:${voteEvent}`, { nodeId, vote: next });
36738
36880
  },
36739
- [node.id, onVote, voteEvent, eventBus]
36881
+ [nodeId, onVote, voteEvent, eventBus]
36740
36882
  );
36741
36883
  const handleReply = useCallback(() => {
36742
- onReply?.(node.id);
36884
+ onReply?.(nodeId);
36743
36885
  setReplyOpen((open) => !open);
36744
- }, [node.id, onReply]);
36886
+ }, [nodeId, onReply]);
36745
36887
  const handleSubmitReply = useCallback(() => {
36746
- const content = draft.trim();
36747
- if (!content) return;
36748
- if (replyEvent) eventBus.emit(`UI:${replyEvent}`, { parentNodeId: node.id, content });
36888
+ const text = draft.trim();
36889
+ if (!text) return;
36890
+ if (replyEvent) eventBus.emit(`UI:${replyEvent}`, { parentNodeId: nodeId, content: text });
36749
36891
  setDraft("");
36750
36892
  setReplyOpen(false);
36751
- }, [node.id, draft, replyEvent, eventBus]);
36893
+ }, [nodeId, draft, replyEvent, eventBus]);
36752
36894
  const handleCancelReply = useCallback(() => {
36753
36895
  setDraft("");
36754
36896
  setReplyOpen(false);
36755
36897
  }, []);
36756
36898
  const handleFlag = useCallback(() => {
36757
- onFlag?.(node.id);
36758
- if (flagEvent) eventBus.emit(`UI:${flagEvent}`, { nodeId: node.id });
36759
- }, [node.id, onFlag, flagEvent, eventBus]);
36899
+ onFlag?.(nodeId);
36900
+ if (flagEvent) eventBus.emit(`UI:${flagEvent}`, { nodeId });
36901
+ }, [nodeId, onFlag, flagEvent, eventBus]);
36760
36902
  const handleContinue = useCallback(() => {
36761
- onContinueThread?.(node.id);
36762
- if (continueThreadEvent) eventBus.emit(`UI:${continueThreadEvent}`, { nodeId: node.id });
36763
- }, [node.id, onContinueThread, continueThreadEvent, eventBus]);
36903
+ onContinueThread?.(nodeId);
36904
+ if (continueThreadEvent) eventBus.emit(`UI:${continueThreadEvent}`, { nodeId });
36905
+ }, [nodeId, onContinueThread, continueThreadEvent, eventBus]);
36764
36906
  const handleToggle = useCallback(() => {
36765
- toggleCollapse(node.id);
36766
- }, [node.id, toggleCollapse]);
36907
+ toggleCollapse(nodeId);
36908
+ }, [nodeId, toggleCollapse]);
36767
36909
  return /* @__PURE__ */ jsxs(Box, { className: "flex flex-row gap-2 items-stretch min-w-0", children: [
36768
36910
  /* @__PURE__ */ jsxs(Box, { className: "flex flex-col items-center flex-shrink-0 w-6", children: [
36769
36911
  hasReplies ? /* @__PURE__ */ jsx(
@@ -36795,25 +36937,25 @@ var init_ReplyTree = __esm({
36795
36937
  /* @__PURE__ */ jsx(
36796
36938
  Avatar,
36797
36939
  {
36798
- src: node.authorAvatarUrl,
36799
- name: node.authorName,
36940
+ src: authorAvatarUrl,
36941
+ name: authorName,
36800
36942
  size: "sm"
36801
36943
  }
36802
36944
  ),
36803
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "semibold", children: node.authorName }),
36804
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: node.postedAt })
36945
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "semibold", children: authorName }),
36946
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: postedAt })
36805
36947
  ] }),
36806
- /* @__PURE__ */ jsx(Typography, { variant: "body", className: "whitespace-pre-wrap break-words", children: node.content }),
36948
+ /* @__PURE__ */ jsx(Typography, { variant: "body", className: "whitespace-pre-wrap break-words", children: content }),
36807
36949
  showActions && /* @__PURE__ */ jsxs(Box, { className: "flex flex-row gap-2 items-center", children: [
36808
36950
  /* @__PURE__ */ jsx(
36809
36951
  VoteStack,
36810
36952
  {
36811
- count: node.voteCount ?? 0,
36812
- userVote: node.userVote ?? null,
36953
+ count: voteCount ?? 0,
36954
+ userVote: userVote ?? null,
36813
36955
  onVote: handleVote,
36814
36956
  size: "sm",
36815
36957
  variant: "horizontal",
36816
- label: t("replyTree.voteOnReplyBy", { author: node.authorName })
36958
+ label: t("replyTree.voteOnReplyBy", { author: authorName })
36817
36959
  }
36818
36960
  ),
36819
36961
  /* @__PURE__ */ jsx(
@@ -36823,7 +36965,7 @@ var init_ReplyTree = __esm({
36823
36965
  size: "sm",
36824
36966
  leftIcon: "message-square",
36825
36967
  onClick: handleReply,
36826
- "aria-label": t("replyTree.replyTo", { author: node.authorName }),
36968
+ "aria-label": t("replyTree.replyTo", { author: authorName }),
36827
36969
  children: t("replyTree.reply")
36828
36970
  }
36829
36971
  ),
@@ -36834,7 +36976,7 @@ var init_ReplyTree = __esm({
36834
36976
  size: "sm",
36835
36977
  leftIcon: "flag",
36836
36978
  onClick: handleFlag,
36837
- "aria-label": t("replyTree.flagReplyBy", { author: node.authorName }),
36979
+ "aria-label": t("replyTree.flagReplyBy", { author: authorName }),
36838
36980
  children: t("replyTree.flag")
36839
36981
  }
36840
36982
  )
@@ -36846,9 +36988,9 @@ var init_ReplyTree = __esm({
36846
36988
  inputType: "textarea",
36847
36989
  rows: 2,
36848
36990
  value: draft,
36849
- placeholder: t("replyTree.replyToPlaceholder", { author: node.authorName }),
36991
+ placeholder: t("replyTree.replyToPlaceholder", { author: authorName }),
36850
36992
  onChange: (e) => setDraft(e.target.value),
36851
- "aria-label": t("replyTree.replyTo", { author: node.authorName })
36993
+ "aria-label": t("replyTree.replyTo", { author: authorName })
36852
36994
  }
36853
36995
  ),
36854
36996
  /* @__PURE__ */ jsxs(Box, { className: "flex flex-row gap-2 items-center", children: [
@@ -36879,7 +37021,7 @@ var init_ReplyTree = __esm({
36879
37021
  ),
36880
37022
  children: t("replyTree.continueThread")
36881
37023
  }
36882
- ) : /* @__PURE__ */ jsx(Box, { className: "flex flex-col gap-2 mt-1", children: node.replies.map((child) => /* @__PURE__ */ jsx(
37024
+ ) : /* @__PURE__ */ jsx(Box, { className: "flex flex-col gap-2 mt-1", children: replies.map((child) => /* @__PURE__ */ jsx(
36883
37025
  ReplyTreeNode,
36884
37026
  {
36885
37027
  node: child,
@@ -38790,8 +38932,8 @@ var init_WizardContainer = __esm({
38790
38932
  return void 0;
38791
38933
  if (typeof controlledStep === "number") return controlledStep;
38792
38934
  if (typeof controlledStep === "string") return parseInt(controlledStep, 10);
38793
- const num = Number(controlledStep);
38794
- return isNaN(num) ? void 0 : num;
38935
+ const num2 = Number(controlledStep);
38936
+ return isNaN(num2) ? void 0 : num2;
38795
38937
  })();
38796
38938
  const currentStep = normalizedControlledStep !== void 0 ? normalizedControlledStep : internalStep;
38797
38939
  const totalSteps = steps.length;
@@ -40496,7 +40638,7 @@ function DebuggerBoard({
40496
40638
  }) {
40497
40639
  const { emit } = useEventBus();
40498
40640
  const { t } = useTranslate();
40499
- const resolved = Array.isArray(entity) ? entity[0] : entity;
40641
+ const resolved = boardEntity(entity);
40500
40642
  const [flaggedLines, setFlaggedLines] = useState(/* @__PURE__ */ new Set());
40501
40643
  const [headerError, setHeaderError] = useState(false);
40502
40644
  const [submitted, setSubmitted] = useState(false);
@@ -40514,7 +40656,7 @@ function DebuggerBoard({
40514
40656
  return next;
40515
40657
  });
40516
40658
  };
40517
- const lines = resolved?.lines ?? [];
40659
+ const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
40518
40660
  const bugLines = lines.filter((l) => l.isBug);
40519
40661
  const correctFlags = lines.filter((l) => l.isBug && flaggedLines.has(l.id));
40520
40662
  const falseFlags = lines.filter((l) => !l.isBug && flaggedLines.has(l.id));
@@ -40529,7 +40671,7 @@ function DebuggerBoard({
40529
40671
  }, [correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
40530
40672
  const handleReset = () => {
40531
40673
  setSubmitted(false);
40532
- if (attempts >= 2 && resolved?.hint) {
40674
+ if (attempts >= 2 && str(resolved?.hint)) {
40533
40675
  setShowHint(true);
40534
40676
  }
40535
40677
  };
@@ -40540,24 +40682,28 @@ function DebuggerBoard({
40540
40682
  setShowHint(false);
40541
40683
  };
40542
40684
  if (!resolved) return null;
40685
+ const theme = resolved.theme ?? void 0;
40686
+ const themeBackground = theme?.background;
40687
+ const headerImage = str(resolved.headerImage);
40688
+ const hint = str(resolved.hint);
40543
40689
  return /* @__PURE__ */ jsx(
40544
40690
  Box,
40545
40691
  {
40546
40692
  className,
40547
40693
  style: {
40548
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
40694
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
40549
40695
  backgroundSize: "cover",
40550
40696
  backgroundPosition: "center"
40551
40697
  },
40552
40698
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
40553
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
40699
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
40554
40700
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
40555
40701
  /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
40556
40702
  /* @__PURE__ */ jsx(Icon, { icon: Bug, size: "sm" }),
40557
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title })
40703
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) })
40558
40704
  ] }),
40559
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description }),
40560
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(resolved.bugCount) }) })
40705
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) }),
40706
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(num(resolved.bugCount)) }) })
40561
40707
  ] }) }),
40562
40708
  /* @__PURE__ */ jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsx(VStack, { gap: "none", children: lines.map((line, i) => {
40563
40709
  const isFlagged = flaggedLines.has(line.id);
@@ -40589,7 +40735,7 @@ function DebuggerBoard({
40589
40735
  );
40590
40736
  }) }) }),
40591
40737
  submitted && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
40592
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("debugger.allFound") : `${correctFlags.length}/${bugLines.length} ${t("debugger.bugsFound")}` }),
40738
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("debugger.allFound") : `${correctFlags.length}/${bugLines.length} ${t("debugger.bugsFound")}` }),
40593
40739
  bugLines.map((line) => /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "start", children: [
40594
40740
  /* @__PURE__ */ jsx(
40595
40741
  Icon,
@@ -40605,7 +40751,7 @@ function DebuggerBoard({
40605
40751
  ] })
40606
40752
  ] }, line.id))
40607
40753
  ] }) }),
40608
- showHint && resolved.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
40754
+ showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
40609
40755
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
40610
40756
  !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.size === 0, children: [
40611
40757
  /* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
@@ -40624,6 +40770,7 @@ var init_DebuggerBoard = __esm({
40624
40770
  "components/game/organisms/puzzles/debugger/DebuggerBoard.tsx"() {
40625
40771
  init_atoms2();
40626
40772
  init_useEventBus();
40773
+ init_boardEntity();
40627
40774
  DebuggerBoard.displayName = "DebuggerBoard";
40628
40775
  }
40629
40776
  });
@@ -40661,7 +40808,7 @@ function getBadgeVariant(fieldName, value) {
40661
40808
  return "default";
40662
40809
  }
40663
40810
  function formatFieldLabel(fieldName) {
40664
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase());
40811
+ return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str2) => str2.toUpperCase());
40665
40812
  }
40666
40813
  function formatFieldValue(value, fieldName) {
40667
40814
  if (typeof value === "number") {
@@ -40680,26 +40827,26 @@ function formatFieldValue(value, fieldName) {
40680
40827
  }
40681
40828
  function renderRichFieldValue(value, fieldName, fieldType) {
40682
40829
  if (value === void 0 || value === null) return "\u2014";
40683
- const str = String(value);
40830
+ const str2 = String(value);
40684
40831
  switch (fieldType) {
40685
40832
  case "image":
40686
40833
  case "url": {
40687
- if (str.match(/\.(png|jpe?g|gif|svg|webp|avif)(\?|$)/i) || str.startsWith("data:image/")) {
40834
+ if (str2.match(/\.(png|jpe?g|gif|svg|webp|avif)(\?|$)/i) || str2.startsWith("data:image/")) {
40688
40835
  return /* @__PURE__ */ jsx(Box, { className: "mt-1 max-w-full", children: /* @__PURE__ */ jsx(
40689
40836
  "img",
40690
40837
  {
40691
- src: str,
40838
+ src: str2,
40692
40839
  alt: formatFieldLabel(fieldName),
40693
40840
  className: "max-w-full max-h-64 rounded-md object-contain",
40694
40841
  loading: "lazy"
40695
40842
  }
40696
40843
  ) });
40697
40844
  }
40698
- return str;
40845
+ return str2;
40699
40846
  }
40700
40847
  case "markdown":
40701
40848
  case "richtext":
40702
- return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(Typography, { variant: "body", className: "break-words", children: str }), children: /* @__PURE__ */ jsx(
40849
+ return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(Typography, { variant: "body", className: "break-words", children: str2 }), children: /* @__PURE__ */ jsx(
40703
40850
  Box,
40704
40851
  {
40705
40852
  className: "prose prose-sm max-w-none",
@@ -40717,11 +40864,11 @@ function renderRichFieldValue(value, fieldName, fieldType) {
40717
40864
  "--tw-prose-th-borders": "var(--color-border)",
40718
40865
  "--tw-prose-td-borders": "var(--color-border)"
40719
40866
  },
40720
- children: /* @__PURE__ */ jsx(ReactMarkdown2, { children: str })
40867
+ children: /* @__PURE__ */ jsx(ReactMarkdown2, { children: str2 })
40721
40868
  }
40722
40869
  ) });
40723
40870
  case "code":
40724
- return /* @__PURE__ */ jsx(Box, { className: "mt-1 rounded-md bg-muted p-3 overflow-x-auto", children: /* @__PURE__ */ jsx("pre", { className: "text-sm font-mono whitespace-pre-wrap break-words m-0", children: /* @__PURE__ */ jsx("code", { children: str }) }) });
40871
+ return /* @__PURE__ */ jsx(Box, { className: "mt-1 rounded-md bg-muted p-3 overflow-x-auto", children: /* @__PURE__ */ jsx("pre", { className: "text-sm font-mono whitespace-pre-wrap break-words m-0", children: /* @__PURE__ */ jsx("code", { children: str2 }) }) });
40725
40872
  case "html":
40726
40873
  return /* @__PURE__ */ jsx(
40727
40874
  Box,
@@ -40741,12 +40888,12 @@ function renderRichFieldValue(value, fieldName, fieldType) {
40741
40888
  "--tw-prose-th-borders": "var(--color-border)",
40742
40889
  "--tw-prose-td-borders": "var(--color-border)"
40743
40890
  },
40744
- children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: str })
40891
+ children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: str2 })
40745
40892
  }
40746
40893
  );
40747
40894
  case "date":
40748
40895
  case "datetime": {
40749
- const d = new Date(str);
40896
+ const d = new Date(str2);
40750
40897
  if (!isNaN(d.getTime())) {
40751
40898
  return d.toLocaleDateString(void 0, {
40752
40899
  year: "numeric",
@@ -40755,7 +40902,7 @@ function renderRichFieldValue(value, fieldName, fieldType) {
40755
40902
  ...fieldType === "datetime" ? { hour: "2-digit", minute: "2-digit" } : {}
40756
40903
  });
40757
40904
  }
40758
- return str;
40905
+ return str2;
40759
40906
  }
40760
40907
  default:
40761
40908
  return formatFieldValue(value, fieldName);
@@ -41433,6 +41580,40 @@ var init_RuleEditor = __esm({
41433
41580
  RuleEditor.displayName = "RuleEditor";
41434
41581
  }
41435
41582
  });
41583
+
41584
+ // components/game/organisms/puzzles/event-handler/puzzleObject.ts
41585
+ function objId(o) {
41586
+ return o.id == null ? "" : String(o.id);
41587
+ }
41588
+ function objName(o) {
41589
+ return o.name == null ? "" : String(o.name);
41590
+ }
41591
+ function objIcon(o) {
41592
+ return o.icon == null ? "" : String(o.icon);
41593
+ }
41594
+ function objStates(o) {
41595
+ return Array.isArray(o.states) ? o.states : [];
41596
+ }
41597
+ function objCurrentState(o) {
41598
+ return o.currentState == null ? "" : String(o.currentState);
41599
+ }
41600
+ function objAvailableEvents(o) {
41601
+ return Array.isArray(o.availableEvents) ? o.availableEvents : [];
41602
+ }
41603
+ function objAvailableActions(o) {
41604
+ return Array.isArray(o.availableActions) ? o.availableActions : [];
41605
+ }
41606
+ function objRules(o) {
41607
+ return Array.isArray(o.rules) ? o.rules : [];
41608
+ }
41609
+ function objMaxRules(o) {
41610
+ const n = Number(o.maxRules);
41611
+ return Number.isFinite(n) && n > 0 ? n : 3;
41612
+ }
41613
+ var init_puzzleObject = __esm({
41614
+ "components/game/organisms/puzzles/event-handler/puzzleObject.ts"() {
41615
+ }
41616
+ });
41436
41617
  function ObjectRulePanel({
41437
41618
  object,
41438
41619
  onRulesChange,
@@ -41440,55 +41621,63 @@ function ObjectRulePanel({
41440
41621
  className
41441
41622
  }) {
41442
41623
  const { t } = useTranslate();
41443
- const maxRules = object.maxRules || 3;
41444
- const canAdd = object.rules.length < maxRules;
41624
+ const id = objId(object);
41625
+ const name = objName(object);
41626
+ const icon = objIcon(object);
41627
+ const states = objStates(object);
41628
+ const currentState = objCurrentState(object);
41629
+ const availableEvents = objAvailableEvents(object);
41630
+ const availableActions = objAvailableActions(object);
41631
+ const rules = objRules(object);
41632
+ const maxRules = objMaxRules(object);
41633
+ const canAdd = rules.length < maxRules;
41445
41634
  const handleRuleChange = useCallback((index, updatedRule) => {
41446
- const newRules = [...object.rules];
41635
+ const newRules = [...rules];
41447
41636
  newRules[index] = updatedRule;
41448
- onRulesChange(object.id, newRules);
41449
- }, [object.id, object.rules, onRulesChange]);
41637
+ onRulesChange(id, newRules);
41638
+ }, [id, rules, onRulesChange]);
41450
41639
  const handleRuleRemove = useCallback((index) => {
41451
- const newRules = object.rules.filter((_, i) => i !== index);
41452
- onRulesChange(object.id, newRules);
41453
- }, [object.id, object.rules, onRulesChange]);
41640
+ const newRules = rules.filter((_, i) => i !== index);
41641
+ onRulesChange(id, newRules);
41642
+ }, [id, rules, onRulesChange]);
41454
41643
  const handleAddRule = useCallback(() => {
41455
41644
  if (!canAdd || disabled) return;
41456
- const firstEvent = object.availableEvents[0]?.value || "";
41457
- const firstAction = object.availableActions[0]?.value || "";
41645
+ const firstEvent = availableEvents[0]?.value || "";
41646
+ const firstAction = availableActions[0]?.value || "";
41458
41647
  const newRule = {
41459
41648
  id: `rule-${nextRuleId++}`,
41460
41649
  whenEvent: firstEvent,
41461
41650
  thenAction: firstAction
41462
41651
  };
41463
- onRulesChange(object.id, [...object.rules, newRule]);
41464
- }, [canAdd, disabled, object, onRulesChange]);
41652
+ onRulesChange(id, [...rules, newRule]);
41653
+ }, [canAdd, disabled, id, rules, availableEvents, availableActions, onRulesChange]);
41465
41654
  const machine = {
41466
- name: object.name,
41467
- states: object.states,
41468
- currentState: object.currentState,
41469
- transitions: object.rules.map((r2) => ({
41470
- from: object.currentState,
41471
- to: object.states.find((s) => s !== object.currentState) || object.currentState,
41655
+ name,
41656
+ states,
41657
+ currentState,
41658
+ transitions: rules.map((r2) => ({
41659
+ from: currentState,
41660
+ to: states.find((s) => s !== currentState) || currentState,
41472
41661
  event: r2.whenEvent
41473
41662
  }))
41474
41663
  };
41475
41664
  return /* @__PURE__ */ jsxs(VStack, { className: cn("p-4 rounded-lg bg-card border border-border", className), gap: "sm", children: [
41476
41665
  /* @__PURE__ */ jsxs(HStack, { className: "items-center", gap: "sm", children: [
41477
- /* @__PURE__ */ jsx(Typography, { variant: "h5", children: object.icon }),
41666
+ /* @__PURE__ */ jsx(Typography, { variant: "h5", children: icon }),
41478
41667
  /* @__PURE__ */ jsxs(VStack, { gap: "none", children: [
41479
- /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-bold", children: object.name }),
41480
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("eventHandler.state") + ": " + object.currentState })
41668
+ /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-bold", children: name }),
41669
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("eventHandler.state") + ": " + currentState })
41481
41670
  ] })
41482
41671
  ] }),
41483
41672
  /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" }),
41484
41673
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
41485
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.rules", { count: object.rules.length, max: maxRules }) + ":" }),
41486
- object.rules.map((rule, i) => /* @__PURE__ */ jsx(
41674
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.rules", { count: rules.length, max: maxRules }) + ":" }),
41675
+ rules.map((rule, i) => /* @__PURE__ */ jsx(
41487
41676
  RuleEditor,
41488
41677
  {
41489
41678
  rule,
41490
- availableEvents: object.availableEvents,
41491
- availableActions: object.availableActions,
41679
+ availableEvents,
41680
+ availableActions,
41492
41681
  onChange: (r2) => handleRuleChange(i, r2),
41493
41682
  onRemove: () => handleRuleRemove(i),
41494
41683
  disabled
@@ -41506,6 +41695,7 @@ var init_ObjectRulePanel = __esm({
41506
41695
  init_cn();
41507
41696
  init_TraitStateViewer();
41508
41697
  init_RuleEditor();
41698
+ init_puzzleObject();
41509
41699
  nextRuleId = 1;
41510
41700
  ObjectRulePanel.displayName = "ObjectRulePanel";
41511
41701
  }
@@ -41572,11 +41762,11 @@ function EventHandlerBoard({
41572
41762
  }) {
41573
41763
  const { emit } = useEventBus();
41574
41764
  const { t } = useTranslate();
41575
- const resolved = Array.isArray(entity) ? entity[0] : entity;
41576
- const entityObjects = resolved?.objects ?? [];
41577
- const [objects, setObjects] = useState(entityObjects);
41765
+ const resolved = boardEntity(entity);
41766
+ const entityObjects = rows(resolved?.objects);
41767
+ const [objects, setObjects] = useState(() => [...entityObjects]);
41578
41768
  const [selectedObjectId, setSelectedObjectId] = useState(
41579
- entityObjects[0]?.id || null
41769
+ entityObjects[0] ? objId(entityObjects[0]) : null
41580
41770
  );
41581
41771
  const [headerError, setHeaderError] = useState(false);
41582
41772
  const [playState, setPlayState] = useState("editing");
@@ -41587,10 +41777,10 @@ function EventHandlerBoard({
41587
41777
  useEffect(() => () => {
41588
41778
  if (timerRef.current) clearTimeout(timerRef.current);
41589
41779
  }, []);
41590
- const selectedObject = objects.find((o) => o.id === selectedObjectId) || null;
41780
+ const selectedObject = objects.find((o) => objId(o) === selectedObjectId) || null;
41591
41781
  const handleRulesChange = useCallback((objectId, rules) => {
41592
41782
  setObjects((prev) => prev.map(
41593
- (o) => o.id === objectId ? { ...o, rules } : o
41783
+ (o) => objId(o) === objectId ? { ...o, rules } : o
41594
41784
  ));
41595
41785
  }, []);
41596
41786
  const addLogEntry = useCallback((icon, message, status = "done") => {
@@ -41604,11 +41794,12 @@ function EventHandlerBoard({
41604
41794
  setEventLog([]);
41605
41795
  const allRules = [];
41606
41796
  objects.forEach((obj) => {
41607
- obj.rules.forEach((rule) => {
41797
+ objRules(obj).forEach((rule) => {
41608
41798
  allRules.push({ object: obj, rule });
41609
41799
  });
41610
41800
  });
41611
- const triggers = resolved?.triggerEvents || [];
41801
+ const triggers = Array.isArray(resolved?.triggerEvents) ? resolved.triggerEvents : [];
41802
+ const goalEvent = str(resolved?.goalEvent);
41612
41803
  const eventQueue = [...triggers];
41613
41804
  const firedEvents = /* @__PURE__ */ new Set();
41614
41805
  let stepIdx = 0;
@@ -41637,14 +41828,14 @@ function EventHandlerBoard({
41637
41828
  addLogEntry("\u26A1", t("eventHandler.noListeners", { event: currentEvent }), "done");
41638
41829
  } else {
41639
41830
  matching.forEach(({ object, rule }) => {
41640
- addLogEntry(object.icon, t("eventHandler.heardEvent", { object: object.name, event: currentEvent, action: rule.thenAction }), "done");
41831
+ addLogEntry(objIcon(object), t("eventHandler.heardEvent", { object: objName(object), event: currentEvent, action: rule.thenAction }), "done");
41641
41832
  eventQueue.push(rule.thenAction);
41642
- if (rule.thenAction === resolved?.goalEvent) {
41833
+ if (rule.thenAction === goalEvent) {
41643
41834
  goalReached = true;
41644
41835
  }
41645
41836
  });
41646
41837
  }
41647
- if (currentEvent === resolved?.goalEvent) {
41838
+ if (currentEvent === goalEvent) {
41648
41839
  goalReached = true;
41649
41840
  }
41650
41841
  stepIdx++;
@@ -41662,65 +41853,75 @@ function EventHandlerBoard({
41662
41853
  }, []);
41663
41854
  const handleReset = useCallback(() => {
41664
41855
  if (timerRef.current) clearTimeout(timerRef.current);
41665
- setObjects(resolved?.objects ?? []);
41856
+ const resetObjects = rows(resolved?.objects);
41857
+ setObjects([...resetObjects]);
41666
41858
  setPlayState("editing");
41667
41859
  setEventLog([]);
41668
- setSelectedObjectId((resolved?.objects ?? [])[0]?.id || null);
41860
+ setSelectedObjectId(resetObjects[0] ? objId(resetObjects[0]) : null);
41669
41861
  setAttempts(0);
41670
41862
  }, [resolved?.objects]);
41671
41863
  if (!resolved) return null;
41672
41864
  const objectViewers = objects.map((obj) => {
41865
+ const states = objStates(obj);
41866
+ const currentState = objCurrentState(obj);
41673
41867
  const machine = {
41674
- name: obj.name,
41675
- states: obj.states,
41676
- currentState: obj.currentState,
41677
- transitions: obj.rules.map((r2) => ({
41678
- from: obj.currentState,
41679
- to: obj.states.find((s) => s !== obj.currentState) || obj.currentState,
41868
+ name: objName(obj),
41869
+ states,
41870
+ currentState,
41871
+ transitions: objRules(obj).map((r2) => ({
41872
+ from: currentState,
41873
+ to: states.find((s) => s !== currentState) || currentState,
41680
41874
  event: r2.whenEvent
41681
41875
  }))
41682
41876
  };
41683
41877
  return { obj, machine };
41684
41878
  });
41685
- const showHint = attempts >= 3 && resolved.hint;
41879
+ const hint = str(resolved.hint);
41880
+ const showHint = attempts >= 3 && hint;
41881
+ const theme = resolved.theme ?? void 0;
41882
+ const themeBackground = theme?.background;
41883
+ const headerImage = str(resolved.headerImage);
41686
41884
  const encourageKey = ENCOURAGEMENT_KEYS[Math.min(attempts - 1, ENCOURAGEMENT_KEYS.length - 1)] ?? ENCOURAGEMENT_KEYS[0];
41687
41885
  return /* @__PURE__ */ jsxs(
41688
41886
  VStack,
41689
41887
  {
41690
41888
  className: cn("p-4 gap-6", className),
41691
41889
  style: {
41692
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
41890
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
41693
41891
  backgroundSize: "cover",
41694
41892
  backgroundPosition: "center"
41695
41893
  },
41696
41894
  children: [
41697
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
41895
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
41698
41896
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
41699
- /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
41700
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description }),
41897
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
41898
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) }),
41701
41899
  /* @__PURE__ */ jsxs(HStack, { className: "items-center p-2 rounded bg-primary/10 border border-primary/30", gap: "xs", children: [
41702
41900
  /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-primary font-bold", children: t("game.goal") + ":" }),
41703
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: resolved.goalCondition })
41901
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: str(resolved.goalCondition) })
41704
41902
  ] })
41705
41903
  ] }),
41706
41904
  /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
41707
41905
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.clickObject") + ":" }),
41708
- /* @__PURE__ */ jsx(HStack, { className: "flex-wrap", gap: "sm", children: objectViewers.map(({ obj, machine }) => /* @__PURE__ */ jsx(
41709
- Box,
41710
- {
41711
- className: cn(
41712
- "p-3 rounded-container border-2 cursor-pointer transition-all hover:scale-105",
41713
- selectedObjectId === obj.id ? "border-primary bg-primary/10" : "border-border bg-card hover:border-muted-foreground"
41714
- ),
41715
- onClick: () => setSelectedObjectId(obj.id),
41716
- children: /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "items-center min-w-[120px]", children: [
41717
- /* @__PURE__ */ jsx(Typography, { variant: "h5", children: obj.icon }),
41718
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground font-medium", children: obj.name }),
41719
- /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" })
41720
- ] })
41721
- },
41722
- obj.id
41723
- )) })
41906
+ /* @__PURE__ */ jsx(HStack, { className: "flex-wrap", gap: "sm", children: objectViewers.map(({ obj, machine }) => {
41907
+ const oid = objId(obj);
41908
+ return /* @__PURE__ */ jsx(
41909
+ Box,
41910
+ {
41911
+ className: cn(
41912
+ "p-3 rounded-container border-2 cursor-pointer transition-all hover:scale-105",
41913
+ selectedObjectId === oid ? "border-primary bg-primary/10" : "border-border bg-card hover:border-muted-foreground"
41914
+ ),
41915
+ onClick: () => setSelectedObjectId(oid),
41916
+ children: /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "items-center min-w-[120px]", children: [
41917
+ /* @__PURE__ */ jsx(Typography, { variant: "h5", children: objIcon(obj) }),
41918
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground font-medium", children: objName(obj) }),
41919
+ /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" })
41920
+ ] })
41921
+ },
41922
+ oid
41923
+ );
41924
+ }) })
41724
41925
  ] }),
41725
41926
  selectedObject && /* @__PURE__ */ jsx(
41726
41927
  ObjectRulePanel,
@@ -41731,12 +41932,12 @@ function EventHandlerBoard({
41731
41932
  }
41732
41933
  ),
41733
41934
  eventLog.length > 0 && /* @__PURE__ */ jsx(EventLog, { entries: eventLog }),
41734
- playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: resolved.successMessage || t("eventHandler.chainComplete") }) }),
41935
+ playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("eventHandler.chainComplete") }) }),
41735
41936
  playState === "fail" && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
41736
41937
  /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(encourageKey) }) }),
41737
41938
  showHint && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
41738
41939
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
41739
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
41940
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
41740
41941
  ] }) })
41741
41942
  ] }),
41742
41943
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
@@ -41764,6 +41965,8 @@ var init_EventHandlerBoard = __esm({
41764
41965
  init_TraitStateViewer();
41765
41966
  init_ObjectRulePanel();
41766
41967
  init_EventLog();
41968
+ init_puzzleObject();
41969
+ init_boardEntity();
41767
41970
  ENCOURAGEMENT_KEYS = [
41768
41971
  "puzzle.tryAgain1",
41769
41972
  "puzzle.tryAgain2",
@@ -41852,7 +42055,10 @@ var init_FeatureGridOrganism = __esm({
41852
42055
  );
41853
42056
  useCallback(
41854
42057
  (feature) => {
41855
- eventBus.emit("UI:FEATURE_CLICK", { id: feature.id, href: feature.href ?? "" });
42058
+ eventBus.emit("UI:FEATURE_CLICK", {
42059
+ id: String(feature.id ?? ""),
42060
+ href: String(feature.href ?? "")
42061
+ });
41856
42062
  },
41857
42063
  [eventBus]
41858
42064
  );
@@ -41862,14 +42068,17 @@ var init_FeatureGridOrganism = __esm({
41862
42068
  if (error) {
41863
42069
  return /* @__PURE__ */ jsx(ErrorState, { message: error.message, className });
41864
42070
  }
41865
- const featureCards = items.map((feature) => ({
41866
- icon: feature.icon,
41867
- title: feature.title,
41868
- description: feature.description,
41869
- href: feature.href,
41870
- linkLabel: feature.linkLabel,
41871
- variant: feature.href ? "interactive" : "bordered"
41872
- }));
42071
+ const featureCards = items.map((feature) => {
42072
+ const href = feature.href != null ? String(feature.href) : void 0;
42073
+ return {
42074
+ icon: feature.icon != null ? String(feature.icon) : void 0,
42075
+ title: String(feature.title ?? ""),
42076
+ description: String(feature.description ?? ""),
42077
+ href,
42078
+ linkLabel: feature.linkLabel != null ? String(feature.linkLabel) : void 0,
42079
+ variant: href ? "interactive" : "bordered"
42080
+ };
42081
+ });
41873
42082
  return /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
41874
42083
  (heading || subtitle) && /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
41875
42084
  heading && /* @__PURE__ */ jsx(Typography, { variant: "h2", align: "center", children: heading }),
@@ -43187,22 +43396,24 @@ var init_HeroOrganism = __esm({
43187
43396
  () => Array.isArray(entity) ? entity[0] : entity && typeof entity === "object" ? entity : void 0,
43188
43397
  [entity]
43189
43398
  );
43399
+ const primaryAction = resolved?.primaryAction;
43400
+ const secondaryAction = resolved?.secondaryAction;
43190
43401
  const handlePrimaryClick = useCallback(() => {
43191
- if (resolved?.primaryAction) {
43402
+ if (primaryAction) {
43192
43403
  eventBus.emit("UI:CTA_PRIMARY", {
43193
- label: resolved.primaryAction.label,
43194
- href: resolved.primaryAction.href
43404
+ label: String(primaryAction.label ?? ""),
43405
+ href: String(primaryAction.href ?? "")
43195
43406
  });
43196
43407
  }
43197
- }, [eventBus, resolved]);
43408
+ }, [eventBus, primaryAction]);
43198
43409
  const handleSecondaryClick = useCallback(() => {
43199
- if (resolved?.secondaryAction) {
43410
+ if (secondaryAction) {
43200
43411
  eventBus.emit("UI:CTA_SECONDARY", {
43201
- label: resolved.secondaryAction.label,
43202
- href: resolved.secondaryAction.href
43412
+ label: String(secondaryAction.label ?? ""),
43413
+ href: String(secondaryAction.href ?? "")
43203
43414
  });
43204
43415
  }
43205
- }, [eventBus, resolved]);
43416
+ }, [eventBus, secondaryAction]);
43206
43417
  if (isLoading) {
43207
43418
  return /* @__PURE__ */ jsx(LoadingState, { message: t("common.loading"), className });
43208
43419
  }
@@ -43212,17 +43423,19 @@ var init_HeroOrganism = __esm({
43212
43423
  if (!resolved) {
43213
43424
  return null;
43214
43425
  }
43426
+ const imageRaw = resolved.image;
43427
+ const image = imageRaw ? { src: String(imageRaw.src ?? ""), alt: String(imageRaw.alt ?? "") } : void 0;
43215
43428
  return /* @__PURE__ */ jsxs(
43216
43429
  HeroSection,
43217
43430
  {
43218
- tag: resolved.tag,
43219
- title: resolved.title,
43220
- titleAccent: resolved.titleAccent,
43221
- subtitle: resolved.subtitle,
43222
- primaryAction: resolved.primaryAction ? { label: resolved.primaryAction.label, href: resolved.primaryAction.href } : void 0,
43223
- secondaryAction: resolved.secondaryAction ? { label: resolved.secondaryAction.label, href: resolved.secondaryAction.href } : void 0,
43224
- installCommand: resolved.installCommand,
43225
- image: resolved.image,
43431
+ tag: resolved.tag != null ? String(resolved.tag) : void 0,
43432
+ title: String(resolved.title ?? ""),
43433
+ titleAccent: resolved.titleAccent != null ? String(resolved.titleAccent) : void 0,
43434
+ subtitle: String(resolved.subtitle ?? ""),
43435
+ primaryAction: primaryAction ? { label: String(primaryAction.label ?? ""), href: String(primaryAction.href ?? "") } : void 0,
43436
+ secondaryAction: secondaryAction ? { label: String(secondaryAction.label ?? ""), href: String(secondaryAction.href ?? "") } : void 0,
43437
+ installCommand: resolved.installCommand != null ? String(resolved.installCommand) : void 0,
43438
+ image,
43226
43439
  imagePosition: resolved.imagePosition,
43227
43440
  background: resolved.background,
43228
43441
  className: cn(className),
@@ -43231,8 +43444,8 @@ var init_HeroOrganism = __esm({
43231
43444
  /* @__PURE__ */ jsx(
43232
43445
  _HeroClickInterceptor,
43233
43446
  {
43234
- hasPrimary: !!resolved.primaryAction,
43235
- hasSecondary: !!resolved.secondaryAction,
43447
+ hasPrimary: !!primaryAction,
43448
+ hasSecondary: !!secondaryAction,
43236
43449
  onPrimaryClick: handlePrimaryClick,
43237
43450
  onSecondaryClick: handleSecondaryClick
43238
43451
  }
@@ -43461,7 +43674,7 @@ function formatValue3(value, fieldName) {
43461
43674
  return String(value);
43462
43675
  }
43463
43676
  function formatFieldLabel2(fieldName) {
43464
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).replace(/Id$/, "").trim();
43677
+ return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str2) => str2.toUpperCase()).replace(/Id$/, "").trim();
43465
43678
  }
43466
43679
  var STATUS_STYLES2, StatusBadge, ProgressIndicator, List3;
43467
43680
  var init_List = __esm({
@@ -44308,20 +44521,22 @@ function NegotiatorBoard({
44308
44521
  }) {
44309
44522
  const { emit } = useEventBus();
44310
44523
  const { t } = useTranslate();
44311
- const resolved = Array.isArray(entity) ? entity[0] : entity;
44524
+ const resolved = boardEntity(entity);
44312
44525
  const [history, setHistory] = useState([]);
44313
44526
  const [headerError, setHeaderError] = useState(false);
44314
44527
  const [showHint, setShowHint] = useState(false);
44528
+ const totalRounds = num(resolved?.totalRounds);
44529
+ const targetScore = num(resolved?.targetScore);
44315
44530
  const currentRound = history.length;
44316
- const isComplete = currentRound >= (resolved?.totalRounds ?? 0);
44531
+ const isComplete = currentRound >= totalRounds;
44317
44532
  const playerTotal = history.reduce((s, r2) => s + r2.playerPayoff, 0);
44318
44533
  const opponentTotal = history.reduce((s, r2) => s + r2.opponentPayoff, 0);
44319
- const won = isComplete && playerTotal >= (resolved?.targetScore ?? 0);
44320
- const actions = resolved?.actions ?? [];
44321
- const payoffMatrix = resolved?.payoffMatrix ?? [];
44534
+ const won = isComplete && playerTotal >= targetScore;
44535
+ const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
44536
+ const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
44322
44537
  const handleAction = useCallback((actionId) => {
44323
44538
  if (isComplete) return;
44324
- const opponentAction = getOpponentAction(resolved?.opponentStrategy ?? "random", actions, history);
44539
+ const opponentAction = getOpponentAction(str(resolved?.opponentStrategy) || "random", actions, history);
44325
44540
  const payoff = payoffMatrix.find(
44326
44541
  (p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
44327
44542
  );
@@ -44334,42 +44549,46 @@ function NegotiatorBoard({
44334
44549
  };
44335
44550
  const newHistory = [...history, result];
44336
44551
  setHistory(newHistory);
44337
- if (newHistory.length >= (resolved?.totalRounds ?? 0)) {
44552
+ if (newHistory.length >= totalRounds) {
44338
44553
  const total = newHistory.reduce((s, r2) => s + r2.playerPayoff, 0);
44339
- if (total >= (resolved?.targetScore ?? 0)) {
44554
+ if (total >= targetScore) {
44340
44555
  emit(`UI:${completeEvent}`, { success: true, score: total });
44341
44556
  }
44342
- if (newHistory.length >= 3 && resolved?.hint) {
44557
+ if (newHistory.length >= 3 && str(resolved?.hint)) {
44343
44558
  setShowHint(true);
44344
44559
  }
44345
44560
  }
44346
- }, [isComplete, resolved, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
44561
+ }, [isComplete, resolved, totalRounds, targetScore, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
44347
44562
  const handleReset = () => {
44348
44563
  setHistory([]);
44349
44564
  setShowHint(false);
44350
44565
  };
44351
44566
  const getActionLabel = (id) => actions.find((a) => a.id === id)?.label ?? id;
44352
44567
  if (!resolved) return null;
44568
+ const theme = resolved.theme ?? void 0;
44569
+ const themeBackground = theme?.background;
44570
+ const headerImage = str(resolved.headerImage);
44571
+ const hint = str(resolved.hint);
44353
44572
  return /* @__PURE__ */ jsx(
44354
44573
  Box,
44355
44574
  {
44356
44575
  className,
44357
44576
  style: {
44358
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
44577
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
44359
44578
  backgroundSize: "cover",
44360
44579
  backgroundPosition: "center"
44361
44580
  },
44362
44581
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
44363
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
44582
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
44364
44583
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
44365
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
44366
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description }),
44584
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
44585
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) }),
44367
44586
  /* @__PURE__ */ jsxs(HStack, { gap: "md", children: [
44368
- /* @__PURE__ */ jsx(Badge, { size: "sm", children: t("negotiator.round", { current: String(currentRound), total: String(resolved.totalRounds) }) }),
44587
+ /* @__PURE__ */ jsx(Badge, { size: "sm", children: t("negotiator.round", { current: String(currentRound), total: String(totalRounds) }) }),
44369
44588
  /* @__PURE__ */ jsxs(Badge, { size: "sm", children: [
44370
44589
  t("negotiator.target"),
44371
44590
  ": ",
44372
- resolved.targetScore
44591
+ targetScore
44373
44592
  ] })
44374
44593
  ] })
44375
44594
  ] }) }),
@@ -44418,16 +44637,16 @@ function NegotiatorBoard({
44418
44637
  ] }) }),
44419
44638
  isComplete && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
44420
44639
  /* @__PURE__ */ jsx(Icon, { icon: CheckCircle, size: "lg", className: won ? "text-success" : "text-error" }),
44421
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: won ? resolved.successMessage ?? t("negotiator.success") : resolved.failMessage ?? t("negotiator.failed") }),
44640
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: won ? str(resolved.successMessage) || t("negotiator.success") : str(resolved.failMessage) || t("negotiator.failed") }),
44422
44641
  /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
44423
44642
  t("negotiator.finalScore"),
44424
44643
  ": ",
44425
44644
  playerTotal,
44426
44645
  "/",
44427
- resolved.targetScore
44646
+ targetScore
44428
44647
  ] })
44429
44648
  ] }) }),
44430
- showHint && resolved.hint && !won && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
44649
+ showHint && hint && !won && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
44431
44650
  isComplete && !won && /* @__PURE__ */ jsx(HStack, { justify: "center", children: /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleReset, children: t("negotiator.playAgain") }) })
44432
44651
  ] })
44433
44652
  }
@@ -44437,6 +44656,7 @@ var init_NegotiatorBoard = __esm({
44437
44656
  "components/game/organisms/puzzles/negotiator/NegotiatorBoard.tsx"() {
44438
44657
  init_atoms2();
44439
44658
  init_useEventBus();
44659
+ init_boardEntity();
44440
44660
  NegotiatorBoard.displayName = "NegotiatorBoard";
44441
44661
  }
44442
44662
  });
@@ -44472,13 +44692,13 @@ var init_PricingOrganism = __esm({
44472
44692
  return /* @__PURE__ */ jsx(ErrorState, { message: error.message, className });
44473
44693
  }
44474
44694
  const plans = items.map((plan) => ({
44475
- name: plan.name,
44476
- price: plan.price,
44477
- description: plan.description,
44478
- features: plan.features,
44479
- action: { label: plan.actionLabel, href: plan.actionHref },
44480
- highlighted: plan.highlighted,
44481
- badge: plan.badge
44695
+ name: String(plan.name ?? ""),
44696
+ price: String(plan.price ?? ""),
44697
+ description: plan.description != null ? String(plan.description) : void 0,
44698
+ features: (plan.features ?? []).map((f3) => String(f3)),
44699
+ action: { label: String(plan.actionLabel ?? ""), href: String(plan.actionHref ?? "") },
44700
+ highlighted: Boolean(plan.highlighted),
44701
+ badge: plan.badge != null ? String(plan.badge) : void 0
44482
44702
  }));
44483
44703
  return /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
44484
44704
  (heading || subtitle) && /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
@@ -46568,16 +46788,20 @@ function SequencerBoard({
46568
46788
  }) {
46569
46789
  const { emit } = useEventBus();
46570
46790
  const { t } = useTranslate();
46571
- const resolved = Array.isArray(entity) ? entity[0] : entity;
46791
+ const resolved = boardEntity(entity);
46792
+ const maxSlots = num(resolved?.maxSlots);
46793
+ const solutions = Array.isArray(resolved?.solutions) ? resolved.solutions : [];
46794
+ const availableActions = Array.isArray(resolved?.availableActions) ? resolved.availableActions : [];
46795
+ const allowDuplicates = resolved?.allowDuplicates !== false;
46572
46796
  const [headerError, setHeaderError] = useState(false);
46573
46797
  const [slots, setSlots] = useState(
46574
- () => Array.from({ length: resolved?.maxSlots ?? 0 }, () => void 0)
46798
+ () => Array.from({ length: maxSlots }, () => void 0)
46575
46799
  );
46576
46800
  const [playState, setPlayState] = useState("idle");
46577
46801
  const [currentStep, setCurrentStep] = useState(-1);
46578
46802
  const [attempts, setAttempts] = useState(0);
46579
46803
  const [slotFeedback, setSlotFeedback] = useState(
46580
- () => Array.from({ length: resolved?.maxSlots ?? 0 }, () => null)
46804
+ () => Array.from({ length: maxSlots }, () => null)
46581
46805
  );
46582
46806
  const timerRef = useRef(null);
46583
46807
  useEffect(() => () => {
@@ -46611,17 +46835,17 @@ function SequencerBoard({
46611
46835
  }, [emit]);
46612
46836
  const handleReset = useCallback(() => {
46613
46837
  if (timerRef.current) clearTimeout(timerRef.current);
46614
- setSlots(Array.from({ length: resolved?.maxSlots ?? 0 }, () => void 0));
46838
+ setSlots(Array.from({ length: maxSlots }, () => void 0));
46615
46839
  setPlayState("idle");
46616
46840
  setCurrentStep(-1);
46617
46841
  setAttempts(0);
46618
- setSlotFeedback(Array.from({ length: resolved?.maxSlots ?? 0 }, () => null));
46619
- }, [resolved?.maxSlots]);
46842
+ setSlotFeedback(Array.from({ length: maxSlots }, () => null));
46843
+ }, [maxSlots]);
46620
46844
  const filledSlots = slots.filter((s) => !!s);
46621
46845
  const canPlay = filledSlots.length > 0 && playState === "idle";
46622
46846
  const handlePlay = useCallback(() => {
46623
46847
  if (!canPlay) return;
46624
- setSlotFeedback(Array.from({ length: resolved?.maxSlots ?? 0 }, () => null));
46848
+ setSlotFeedback(Array.from({ length: maxSlots }, () => null));
46625
46849
  emit("UI:PLAY_SOUND", { key: "confirm" });
46626
46850
  const sequence = slots.map((s) => s?.id || "");
46627
46851
  if (playEvent) {
@@ -46632,10 +46856,10 @@ function SequencerBoard({
46632
46856
  let step = 0;
46633
46857
  const advance = () => {
46634
46858
  step++;
46635
- if (step >= (resolved?.maxSlots ?? 0)) {
46859
+ if (step >= maxSlots) {
46636
46860
  const playerSeq = slots.map((s) => s?.id);
46637
46861
  const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
46638
- const success = (resolved?.solutions ?? []).some(
46862
+ const success = solutions.some(
46639
46863
  (sol) => sol.length === playerIds.length && sol.every((id, i) => id === playerIds[i])
46640
46864
  );
46641
46865
  if (success) {
@@ -46647,7 +46871,7 @@ function SequencerBoard({
46647
46871
  }
46648
46872
  } else {
46649
46873
  setAttempts((prev) => prev + 1);
46650
- const feedback = computeSlotFeedback(playerSeq, resolved?.solutions ?? []);
46874
+ const feedback = computeSlotFeedback(playerSeq, solutions);
46651
46875
  setSlotFeedback(feedback);
46652
46876
  setPlayState("idle");
46653
46877
  setCurrentStep(-1);
@@ -46665,10 +46889,10 @@ function SequencerBoard({
46665
46889
  }
46666
46890
  };
46667
46891
  timerRef.current = setTimeout(advance, stepDurationMs);
46668
- }, [canPlay, slots, resolved?.maxSlots, resolved?.solutions, stepDurationMs, playEvent, completeEvent, emit]);
46892
+ }, [canPlay, slots, maxSlots, solutions, stepDurationMs, playEvent, completeEvent, emit]);
46669
46893
  const machine = {
46670
- name: resolved?.title ?? "",
46671
- description: resolved?.description ?? "",
46894
+ name: str(resolved?.title),
46895
+ description: str(resolved?.description),
46672
46896
  states: slots.map((s, i) => stepLabel(s, i)),
46673
46897
  currentState: currentStep >= 0 ? stepLabel(slots[currentStep], currentStep) : "__idle__",
46674
46898
  transitions: slots.slice(0, -1).map((s, i) => ({
@@ -46677,37 +46901,41 @@ function SequencerBoard({
46677
46901
  event: "NEXT"
46678
46902
  }))
46679
46903
  };
46680
- const usedIds = resolved?.allowDuplicates === false ? slots.filter(Boolean).map((s) => s?.id || "") : [];
46681
- const showHint = attempts >= 3 && !!resolved?.hint;
46904
+ const usedIds = !allowDuplicates ? slots.filter(Boolean).map((s) => s?.id || "") : [];
46905
+ const hint = str(resolved?.hint);
46906
+ const showHint = attempts >= 3 && !!hint;
46682
46907
  const hasFeedback = slotFeedback.some((f3) => f3 !== null);
46683
46908
  const correctCount = slotFeedback.filter((f3) => f3 === "correct").length;
46684
46909
  const encourageKey = ENCOURAGEMENT_KEYS2[Math.min(attempts - 1, ENCOURAGEMENT_KEYS2.length - 1)] ?? ENCOURAGEMENT_KEYS2[0];
46685
46910
  if (!resolved) return null;
46911
+ const theme = resolved.theme ?? void 0;
46912
+ const themeBackground = theme?.background;
46913
+ const headerImage = str(resolved.headerImage);
46686
46914
  return /* @__PURE__ */ jsxs(
46687
46915
  VStack,
46688
46916
  {
46689
46917
  className: cn("p-4 gap-6", className),
46690
46918
  style: {
46691
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
46919
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
46692
46920
  backgroundSize: "cover",
46693
46921
  backgroundPosition: "center"
46694
46922
  },
46695
46923
  children: [
46696
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
46924
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
46697
46925
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
46698
- /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
46699
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description })
46926
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
46927
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) })
46700
46928
  ] }),
46701
46929
  showHint && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
46702
46930
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
46703
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
46931
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
46704
46932
  ] }) }),
46705
46933
  filledSlots.length > 0 && /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "linear", size: "md" }),
46706
46934
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
46707
46935
  /* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between", children: [
46708
46936
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("sequencer.yourSequence") + ":" }),
46709
46937
  hasFeedback && playState === "idle" && /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
46710
- `${correctCount}/${resolved.maxSlots} `,
46938
+ `${correctCount}/${maxSlots} `,
46711
46939
  "\u2705"
46712
46940
  ] })
46713
46941
  ] }),
@@ -46715,7 +46943,7 @@ function SequencerBoard({
46715
46943
  SequenceBar,
46716
46944
  {
46717
46945
  slots,
46718
- maxSlots: resolved.maxSlots,
46946
+ maxSlots,
46719
46947
  onSlotDrop: handleSlotDrop,
46720
46948
  onSlotRemove: handleSlotRemove,
46721
46949
  playing: playState === "playing",
@@ -46729,15 +46957,15 @@ function SequencerBoard({
46729
46957
  playState !== "playing" && /* @__PURE__ */ jsx(
46730
46958
  ActionPalette,
46731
46959
  {
46732
- actions: resolved.availableActions,
46960
+ actions: availableActions,
46733
46961
  usedActionIds: usedIds,
46734
- allowDuplicates: resolved.allowDuplicates !== false,
46962
+ allowDuplicates,
46735
46963
  categoryColors,
46736
46964
  label: t("sequencer.dragActions")
46737
46965
  }
46738
46966
  ),
46739
46967
  hasFeedback && playState === "idle" && attempts > 0 && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: t(encourageKey) }) }),
46740
- playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: resolved.successMessage || t("sequencer.levelComplete") }) }),
46968
+ playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("sequencer.levelComplete") }) }),
46741
46969
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
46742
46970
  /* @__PURE__ */ jsx(
46743
46971
  Button,
@@ -46761,6 +46989,7 @@ var init_SequencerBoard = __esm({
46761
46989
  init_cn();
46762
46990
  init_useEventBus();
46763
46991
  init_TraitStateViewer();
46992
+ init_boardEntity();
46764
46993
  init_SequenceBar();
46765
46994
  init_ActionPalette();
46766
46995
  ENCOURAGEMENT_KEYS2 = [
@@ -46810,18 +47039,21 @@ var init_ShowcaseOrganism = __esm({
46810
47039
  heading && /* @__PURE__ */ jsx(Typography, { variant: "h2", align: "center", children: heading }),
46811
47040
  subtitle && /* @__PURE__ */ jsx(Typography, { variant: "body1", color: "muted", align: "center", className: "max-w-2xl", children: subtitle })
46812
47041
  ] }),
46813
- /* @__PURE__ */ jsx(SimpleGrid, { cols: columns, gap: "lg", children: items.map((item) => /* @__PURE__ */ jsx(
46814
- ShowcaseCard,
46815
- {
46816
- title: item.title,
46817
- description: item.description,
46818
- image: item.image,
46819
- href: item.href,
46820
- badge: item.badge,
46821
- accentColor: item.accentColor
46822
- },
46823
- item.id
46824
- )) })
47042
+ /* @__PURE__ */ jsx(SimpleGrid, { cols: columns, gap: "lg", children: items.map((item) => {
47043
+ const imageRaw = item.image;
47044
+ return /* @__PURE__ */ jsx(
47045
+ ShowcaseCard,
47046
+ {
47047
+ title: String(item.title ?? ""),
47048
+ description: item.description != null ? String(item.description) : void 0,
47049
+ image: { src: String(imageRaw?.src ?? ""), alt: String(imageRaw?.alt ?? "") },
47050
+ href: item.href != null ? String(item.href) : void 0,
47051
+ badge: item.badge != null ? String(item.badge) : void 0,
47052
+ accentColor: item.accentColor != null ? String(item.accentColor) : void 0
47053
+ },
47054
+ String(item.id ?? "")
47055
+ );
47056
+ }) })
46825
47057
  ] });
46826
47058
  };
46827
47059
  ShowcaseOrganism.displayName = "ShowcaseOrganism";
@@ -47189,8 +47421,8 @@ function SimulatorBoard({
47189
47421
  }) {
47190
47422
  const { emit } = useEventBus();
47191
47423
  const { t } = useTranslate();
47192
- const resolved = Array.isArray(entity) ? entity[0] : entity;
47193
- const parameters = resolved?.parameters ?? [];
47424
+ const resolved = boardEntity(entity);
47425
+ const parameters = Array.isArray(resolved?.parameters) ? resolved.parameters : [];
47194
47426
  const [values, setValues] = useState(() => {
47195
47427
  const init = {};
47196
47428
  for (const p2 of parameters) {
@@ -47204,15 +47436,15 @@ function SimulatorBoard({
47204
47436
  const [showHint, setShowHint] = useState(false);
47205
47437
  const computeOutput = useCallback((params) => {
47206
47438
  try {
47207
- const fn = new Function("params", `return (${resolved?.computeExpression})`);
47439
+ const fn = new Function("params", `return (${str(resolved?.computeExpression)})`);
47208
47440
  return fn(params);
47209
47441
  } catch {
47210
47442
  return 0;
47211
47443
  }
47212
47444
  }, [resolved?.computeExpression]);
47213
47445
  const output = useMemo(() => computeOutput(values) ?? 0, [computeOutput, values]);
47214
- const targetValue = resolved?.targetValue ?? 0;
47215
- const targetTolerance = resolved?.targetTolerance ?? 0;
47446
+ const targetValue = num(resolved?.targetValue);
47447
+ const targetTolerance = num(resolved?.targetTolerance);
47216
47448
  const isCorrect = Math.abs(output - targetValue) <= targetTolerance;
47217
47449
  const handleParameterChange = (id, value) => {
47218
47450
  if (submitted) return;
@@ -47227,7 +47459,7 @@ function SimulatorBoard({
47227
47459
  };
47228
47460
  const handleReset = () => {
47229
47461
  setSubmitted(false);
47230
- if (attempts >= 2 && resolved?.hint) {
47462
+ if (attempts >= 2 && str(resolved?.hint)) {
47231
47463
  setShowHint(true);
47232
47464
  }
47233
47465
  };
@@ -47242,20 +47474,26 @@ function SimulatorBoard({
47242
47474
  setShowHint(false);
47243
47475
  };
47244
47476
  if (!resolved) return null;
47477
+ const theme = resolved.theme ?? void 0;
47478
+ const themeBackground = theme?.background;
47479
+ const headerImage = str(resolved.headerImage);
47480
+ const hint = str(resolved.hint);
47481
+ const outputLabel = str(resolved.outputLabel);
47482
+ const outputUnit = str(resolved.outputUnit);
47245
47483
  return /* @__PURE__ */ jsx(
47246
47484
  Box,
47247
47485
  {
47248
47486
  className,
47249
47487
  style: {
47250
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
47488
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
47251
47489
  backgroundSize: "cover",
47252
47490
  backgroundPosition: "center"
47253
47491
  },
47254
47492
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
47255
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
47493
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
47256
47494
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
47257
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
47258
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description })
47495
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
47496
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) })
47259
47497
  ] }) }),
47260
47498
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", children: [
47261
47499
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("simulator.parameters") }),
@@ -47296,28 +47534,28 @@ function SimulatorBoard({
47296
47534
  ] }, param.id))
47297
47535
  ] }) }),
47298
47536
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
47299
- /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: resolved.outputLabel }),
47537
+ /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: outputLabel }),
47300
47538
  /* @__PURE__ */ jsxs(Typography, { variant: "h3", weight: "bold", children: [
47301
47539
  output.toFixed(2),
47302
47540
  " ",
47303
- resolved.outputUnit
47541
+ outputUnit
47304
47542
  ] }),
47305
47543
  submitted && /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
47306
47544
  /* @__PURE__ */ jsx(Icon, { icon: isCorrect ? CheckCircle : XCircle, size: "sm", className: isCorrect ? "text-success" : "text-error" }),
47307
- /* @__PURE__ */ jsx(Typography, { variant: "body", className: isCorrect ? "text-success" : "text-error", children: isCorrect ? resolved.successMessage ?? t("simulator.correct") : resolved.failMessage ?? t("simulator.incorrect") })
47545
+ /* @__PURE__ */ jsx(Typography, { variant: "body", className: isCorrect ? "text-success" : "text-error", children: isCorrect ? str(resolved.successMessage) || t("simulator.correct") : str(resolved.failMessage) || t("simulator.incorrect") })
47308
47546
  ] }),
47309
47547
  /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
47310
47548
  t("simulator.target"),
47311
47549
  ": ",
47312
47550
  targetValue,
47313
47551
  " ",
47314
- resolved.outputUnit,
47552
+ outputUnit,
47315
47553
  " (\xB1",
47316
47554
  targetTolerance,
47317
47555
  ")"
47318
47556
  ] })
47319
47557
  ] }) }),
47320
- showHint && resolved.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
47558
+ showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
47321
47559
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
47322
47560
  !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, children: [
47323
47561
  /* @__PURE__ */ jsx(Icon, { icon: Play, size: "sm" }),
@@ -47336,6 +47574,7 @@ var init_SimulatorBoard = __esm({
47336
47574
  "components/game/organisms/puzzles/simulator/SimulatorBoard.tsx"() {
47337
47575
  init_atoms2();
47338
47576
  init_useEventBus();
47577
+ init_boardEntity();
47339
47578
  SimulatorBoard.displayName = "SimulatorBoard";
47340
47579
  }
47341
47580
  });
@@ -47761,22 +48000,25 @@ function VariablePanel({
47761
48000
  return /* @__PURE__ */ jsxs(VStack, { className: cn("p-3 rounded-lg bg-card border border-border", className), gap: "sm", children: [
47762
48001
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("stateArchitect.variables", { name: entityName }) }),
47763
48002
  variables.map((v) => {
47764
- const max = v.max ?? 100;
47765
- const min = v.min ?? 0;
47766
- const pct = Math.round((v.value - min) / (max - min) * 100);
48003
+ const name = v.name == null ? "" : String(v.name);
48004
+ const value = numField(v.value);
48005
+ const max = numField(v.max, 100);
48006
+ const min = numField(v.min, 0);
48007
+ const unit = v.unit == null ? "" : String(v.unit);
48008
+ const pct = Math.round((value - min) / (max - min) * 100);
47767
48009
  const isHigh = pct > 80;
47768
48010
  const isLow = pct < 20;
47769
48011
  return /* @__PURE__ */ jsxs(VStack, { gap: "none", children: [
47770
48012
  /* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between", children: [
47771
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground font-medium", children: v.name }),
48013
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground font-medium", children: name }),
47772
48014
  /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: cn(
47773
48015
  isHigh ? "text-error" : isLow ? "text-warning" : "text-foreground"
47774
48016
  ), children: [
47775
- v.value,
47776
- v.unit || "",
48017
+ value,
48018
+ unit,
47777
48019
  " / ",
47778
48020
  max,
47779
- v.unit || ""
48021
+ unit
47780
48022
  ] })
47781
48023
  ] }),
47782
48024
  /* @__PURE__ */ jsx(
@@ -47787,14 +48029,19 @@ function VariablePanel({
47787
48029
  size: "sm"
47788
48030
  }
47789
48031
  )
47790
- ] }, v.name);
48032
+ ] }, name);
47791
48033
  })
47792
48034
  ] });
47793
48035
  }
48036
+ var numField;
47794
48037
  var init_VariablePanel = __esm({
47795
48038
  "components/game/organisms/puzzles/state-architect/VariablePanel.tsx"() {
47796
48039
  init_atoms2();
47797
48040
  init_cn();
48041
+ numField = (v, fallback = 0) => {
48042
+ const n = Number(v);
48043
+ return Number.isFinite(n) ? n : fallback;
48044
+ };
47798
48045
  VariablePanel.displayName = "VariablePanel";
47799
48046
  }
47800
48047
  });
@@ -47821,14 +48068,21 @@ function StateArchitectBoard({
47821
48068
  }) {
47822
48069
  const { emit } = useEventBus();
47823
48070
  const { t } = useTranslate();
47824
- const resolved = Array.isArray(entity) ? entity[0] : entity;
47825
- const [transitions, setTransitions] = useState(resolved?.transitions ?? []);
48071
+ const resolved = boardEntity(entity);
48072
+ const entityStates = Array.isArray(resolved?.states) ? resolved.states : [];
48073
+ const initialState = str(resolved?.initialState);
48074
+ const entityName = str(resolved?.entityName);
48075
+ const availableEvents = Array.isArray(resolved?.availableEvents) ? resolved.availableEvents : [];
48076
+ const testCases = Array.isArray(resolved?.testCases) ? resolved.testCases : [];
48077
+ const entityTransitions = Array.isArray(resolved?.transitions) ? resolved.transitions : [];
48078
+ const entityVariables = rows(resolved?.variables);
48079
+ const [transitions, setTransitions] = useState(entityTransitions);
47826
48080
  const [headerError, setHeaderError] = useState(false);
47827
48081
  const [playState, setPlayState] = useState("editing");
47828
- const [currentState, setCurrentState] = useState(resolved?.initialState ?? "");
48082
+ const [currentState, setCurrentState] = useState(initialState);
47829
48083
  const [selectedState, setSelectedState] = useState(null);
47830
48084
  const [testResults, setTestResults] = useState([]);
47831
- const [variables, setVariables] = useState(resolved?.variables ?? []);
48085
+ const [variables, setVariables] = useState(() => [...entityVariables]);
47832
48086
  const [attempts, setAttempts] = useState(0);
47833
48087
  const timerRef = useRef(null);
47834
48088
  const [addingFrom, setAddingFrom] = useState(null);
@@ -47837,12 +48091,12 @@ function StateArchitectBoard({
47837
48091
  }, []);
47838
48092
  const GRAPH_W = 500;
47839
48093
  const GRAPH_H = 400;
47840
- const positions = useMemo(() => layoutStates(resolved?.states ?? [], GRAPH_W, GRAPH_H), [resolved?.states]);
48094
+ const positions = useMemo(() => layoutStates(entityStates, GRAPH_W, GRAPH_H), [entityStates]);
47841
48095
  const handleStateClick = useCallback((state) => {
47842
48096
  if (playState !== "editing") return;
47843
48097
  if (addingFrom) {
47844
48098
  if (addingFrom !== state) {
47845
- const event = resolved?.availableEvents[0] || "EVENT";
48099
+ const event = availableEvents[0] || "EVENT";
47846
48100
  const newTrans = {
47847
48101
  id: `t-${nextTransId++}`,
47848
48102
  from: addingFrom,
@@ -47855,7 +48109,7 @@ function StateArchitectBoard({
47855
48109
  } else {
47856
48110
  setSelectedState(state);
47857
48111
  }
47858
- }, [playState, addingFrom, resolved?.availableEvents]);
48112
+ }, [playState, addingFrom, availableEvents]);
47859
48113
  const handleStartAddTransition = useCallback(() => {
47860
48114
  if (!selectedState) return;
47861
48115
  setAddingFrom(selectedState);
@@ -47864,9 +48118,9 @@ function StateArchitectBoard({
47864
48118
  setTransitions((prev) => prev.filter((t2) => t2.id !== transId));
47865
48119
  }, []);
47866
48120
  const machine = useMemo(() => ({
47867
- name: resolved?.entityName ?? "",
47868
- description: resolved?.description ?? "",
47869
- states: resolved?.states ?? [],
48121
+ name: entityName,
48122
+ description: str(resolved?.description),
48123
+ states: entityStates,
47870
48124
  currentState,
47871
48125
  transitions: transitions.map((t2) => ({
47872
48126
  from: t2.from,
@@ -47874,7 +48128,7 @@ function StateArchitectBoard({
47874
48128
  event: t2.event,
47875
48129
  guardHint: t2.guardHint
47876
48130
  }))
47877
- }), [resolved, currentState, transitions]);
48131
+ }), [entityName, resolved, entityStates, currentState, transitions]);
47878
48132
  const handleTest = useCallback(() => {
47879
48133
  if (playState !== "editing") return;
47880
48134
  if (testEvent) emit(`UI:${testEvent}`, {});
@@ -47883,7 +48137,7 @@ function StateArchitectBoard({
47883
48137
  const results = [];
47884
48138
  let testIdx = 0;
47885
48139
  const runNextTest = () => {
47886
- if (testIdx >= (resolved?.testCases.length ?? 0)) {
48140
+ if (testIdx >= testCases.length) {
47887
48141
  const allPassed = results.every((r2) => r2.passed);
47888
48142
  setPlayState(allPassed ? "success" : "fail");
47889
48143
  setTestResults(results);
@@ -47898,9 +48152,9 @@ function StateArchitectBoard({
47898
48152
  }
47899
48153
  return;
47900
48154
  }
47901
- const testCase = resolved?.testCases[testIdx];
48155
+ const testCase = testCases[testIdx];
47902
48156
  if (!testCase) return;
47903
- let state = resolved.initialState;
48157
+ let state = initialState;
47904
48158
  for (const event of testCase.events) {
47905
48159
  const trans = transitions.find((t2) => t2.from === state && t2.event === event);
47906
48160
  if (trans) {
@@ -47918,53 +48172,57 @@ function StateArchitectBoard({
47918
48172
  timerRef.current = setTimeout(runNextTest, stepDurationMs);
47919
48173
  };
47920
48174
  timerRef.current = setTimeout(runNextTest, stepDurationMs);
47921
- }, [playState, transitions, resolved, stepDurationMs, testEvent, completeEvent, emit]);
48175
+ }, [playState, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
47922
48176
  const handleTryAgain = useCallback(() => {
47923
48177
  if (timerRef.current) clearTimeout(timerRef.current);
47924
48178
  setPlayState("editing");
47925
- setCurrentState(resolved?.initialState ?? "");
48179
+ setCurrentState(initialState);
47926
48180
  setTestResults([]);
47927
- }, [resolved?.initialState]);
48181
+ }, [initialState]);
47928
48182
  const handleReset = useCallback(() => {
47929
48183
  if (timerRef.current) clearTimeout(timerRef.current);
47930
- setTransitions(resolved?.transitions ?? []);
48184
+ setTransitions(entityTransitions);
47931
48185
  setPlayState("editing");
47932
- setCurrentState(resolved?.initialState ?? "");
48186
+ setCurrentState(initialState);
47933
48187
  setTestResults([]);
47934
- setVariables(resolved?.variables ?? []);
48188
+ setVariables([...entityVariables]);
47935
48189
  setSelectedState(null);
47936
48190
  setAddingFrom(null);
47937
48191
  setAttempts(0);
47938
- }, [resolved]);
48192
+ }, [entityTransitions, initialState, entityVariables]);
47939
48193
  const codeData = useMemo(() => ({
47940
- name: resolved?.entityName ?? "",
47941
- states: resolved?.states ?? [],
47942
- initialState: resolved?.initialState ?? "",
48194
+ name: entityName,
48195
+ states: entityStates,
48196
+ initialState,
47943
48197
  transitions: transitions.map((t2) => ({
47944
48198
  from: t2.from,
47945
48199
  to: t2.to,
47946
48200
  event: t2.event,
47947
48201
  ...t2.guardHint ? { guard: t2.guardHint } : {}
47948
48202
  }))
47949
- }), [resolved, transitions]);
48203
+ }), [entityName, entityStates, initialState, transitions]);
47950
48204
  if (!resolved) return null;
48205
+ const theme = resolved.theme ?? void 0;
48206
+ const themeBackground = theme?.background;
48207
+ const headerImage = str(resolved.headerImage);
48208
+ const hint = str(resolved.hint);
47951
48209
  return /* @__PURE__ */ jsxs(
47952
48210
  VStack,
47953
48211
  {
47954
48212
  className: cn("p-4 gap-6", className),
47955
48213
  style: {
47956
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
48214
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
47957
48215
  backgroundSize: "cover",
47958
48216
  backgroundPosition: "center"
47959
48217
  },
47960
48218
  children: [
47961
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
48219
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
47962
48220
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
47963
- /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
47964
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description }),
48221
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
48222
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) }),
47965
48223
  /* @__PURE__ */ jsxs(HStack, { className: "items-center p-2 rounded bg-warning/10 border border-warning/30", gap: "xs", children: [
47966
48224
  /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-warning font-bold", children: t("game.hint") + ":" }),
47967
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: resolved.hint })
48225
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: hint })
47968
48226
  ] })
47969
48227
  ] }),
47970
48228
  /* @__PURE__ */ jsxs(HStack, { className: "flex-wrap items-start", gap: "lg", children: [
@@ -48012,14 +48270,14 @@ function StateArchitectBoard({
48012
48270
  ]
48013
48271
  }
48014
48272
  ),
48015
- resolved.states.map((state) => /* @__PURE__ */ jsx(
48273
+ entityStates.map((state) => /* @__PURE__ */ jsx(
48016
48274
  StateNode2,
48017
48275
  {
48018
48276
  name: state,
48019
48277
  position: positions[state],
48020
48278
  isCurrent: state === currentState,
48021
48279
  isSelected: state === selectedState,
48022
- isInitial: state === resolved.initialState,
48280
+ isInitial: state === initialState,
48023
48281
  onClick: () => handleStateClick(state)
48024
48282
  },
48025
48283
  state
@@ -48066,7 +48324,7 @@ function StateArchitectBoard({
48066
48324
  /* @__PURE__ */ jsx(
48067
48325
  VariablePanel,
48068
48326
  {
48069
- entityName: resolved.entityName,
48327
+ entityName,
48070
48328
  variables
48071
48329
  }
48072
48330
  ),
@@ -48081,12 +48339,12 @@ function StateArchitectBoard({
48081
48339
  resolved.showCodeView !== false && /* @__PURE__ */ jsx(CodeView, { data: codeData, label: "View Code" })
48082
48340
  ] })
48083
48341
  ] }),
48084
- playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: resolved.successMessage || t("stateArchitect.allPassed") }) }),
48342
+ playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("stateArchitect.allPassed") }) }),
48085
48343
  playState === "fail" && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
48086
48344
  /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(ENCOURAGEMENT_KEYS3[Math.min(attempts - 1, ENCOURAGEMENT_KEYS3.length - 1)] ?? ENCOURAGEMENT_KEYS3[0]) }) }),
48087
- attempts >= 3 && resolved.hint && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
48345
+ attempts >= 3 && hint && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
48088
48346
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
48089
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
48347
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
48090
48348
  ] }) })
48091
48349
  ] }),
48092
48350
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
@@ -48116,6 +48374,7 @@ var init_StateArchitectBoard = __esm({
48116
48374
  init_TransitionArrow();
48117
48375
  init_VariablePanel();
48118
48376
  init_CodeView();
48377
+ init_boardEntity();
48119
48378
  ENCOURAGEMENT_KEYS3 = [
48120
48379
  "puzzle.tryAgain1",
48121
48380
  "puzzle.tryAgain2",
@@ -48152,8 +48411,8 @@ var init_StatsOrganism = __esm({
48152
48411
  return /* @__PURE__ */ jsx(ErrorState, { message: error.message, className });
48153
48412
  }
48154
48413
  const stats = items.map((item) => ({
48155
- value: item.value,
48156
- label: item.label
48414
+ value: String(item.value ?? ""),
48415
+ label: String(item.label ?? "")
48157
48416
  }));
48158
48417
  return /* @__PURE__ */ jsx(
48159
48418
  StatsGrid,
@@ -48199,10 +48458,10 @@ var init_StepFlowOrganism = __esm({
48199
48458
  return /* @__PURE__ */ jsx(ErrorState, { message: error.message, className });
48200
48459
  }
48201
48460
  const steps = items.map((item) => ({
48202
- number: item.number,
48203
- title: item.title,
48204
- description: item.description,
48205
- icon: item.icon
48461
+ number: item.number != null ? Number(item.number) : void 0,
48462
+ title: String(item.title ?? ""),
48463
+ description: String(item.description ?? ""),
48464
+ icon: item.icon != null ? String(item.icon) : void 0
48206
48465
  }));
48207
48466
  return /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
48208
48467
  (heading || subtitle) && /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
@@ -48375,13 +48634,13 @@ var init_TeamOrganism = __esm({
48375
48634
  /* @__PURE__ */ jsx(SimpleGrid, { cols: cols > 0 ? cols : 1, gap: "lg", children: items.map((member) => /* @__PURE__ */ jsx(
48376
48635
  TeamCard,
48377
48636
  {
48378
- name: member.name,
48379
- nameAr: member.nameAr,
48380
- role: member.role,
48381
- bio: member.bio,
48382
- avatar: member.avatar
48637
+ name: String(member.name ?? ""),
48638
+ nameAr: member.nameAr != null ? String(member.nameAr) : void 0,
48639
+ role: String(member.role ?? ""),
48640
+ bio: String(member.bio ?? ""),
48641
+ avatar: member.avatar != null ? String(member.avatar) : void 0
48383
48642
  },
48384
- member.id
48643
+ String(member.id ?? "")
48385
48644
  )) })
48386
48645
  ] });
48387
48646
  };
@@ -48618,8 +48877,8 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
48618
48877
  const [turn, setTurn] = useState(1);
48619
48878
  const [gameResult, setGameResult] = useState(null);
48620
48879
  const checkGameEnd = useCallback((currentUnits) => {
48621
- const pa = currentUnits.filter((u) => u.team === "player" && u.health > 0);
48622
- const ea = currentUnits.filter((u) => u.team === "enemy" && u.health > 0);
48880
+ const pa = currentUnits.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
48881
+ const ea = currentUnits.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
48623
48882
  if (pa.length === 0) {
48624
48883
  setGameResult("defeat");
48625
48884
  setPhase("game_over");
@@ -48637,34 +48896,36 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
48637
48896
  }
48638
48897
  }, [onGameEnd, gameEndEvent, eventBus]);
48639
48898
  const handleUnitClick = useCallback((unitId) => {
48640
- const unit = units.find((u) => u.id === unitId);
48899
+ const unit = units.find((u) => str(u.id) === unitId);
48641
48900
  if (!unit) return;
48642
48901
  if (unitClickEvent) {
48643
48902
  eventBus.emit(`UI:${unitClickEvent}`, { unitId });
48644
48903
  }
48645
48904
  if (phase === "observation" || phase === "selection") {
48646
- if (unit.team === "player") {
48905
+ if (unitTeam(unit) === "player") {
48647
48906
  setSelectedUnitId(unitId);
48648
48907
  setPhase("movement");
48649
48908
  }
48650
48909
  } else if (phase === "action") {
48651
- const selectedUnit = units.find((u) => u.id === selectedUnitId);
48910
+ const selectedUnit = units.find((u) => str(u.id) === selectedUnitId);
48652
48911
  if (!selectedUnit) return;
48653
- if (unit.team === "enemy") {
48654
- const dx = Math.abs(unit.position.x - selectedUnit.position.x);
48655
- const dy = Math.abs(unit.position.y - selectedUnit.position.y);
48912
+ if (unitTeam(unit) === "enemy") {
48913
+ const up = unitPosition(unit);
48914
+ const sp = unitPosition(selectedUnit);
48915
+ const dx = Math.abs(up.x - sp.x);
48916
+ const dy = Math.abs(up.y - sp.y);
48656
48917
  if (dx <= 1 && dy <= 1 && dx + dy > 0) {
48657
- const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, selectedUnit.attack - unit.defense);
48658
- const newHealth = Math.max(0, unit.health - damage);
48918
+ const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, num(selectedUnit.attack) - num(unit.defense));
48919
+ const newHealth = Math.max(0, unitHealth(unit) - damage);
48659
48920
  const updatedUnits = units.map(
48660
- (u) => u.id === unit.id ? { ...u, health: newHealth } : u
48921
+ (u) => str(u.id) === str(unit.id) ? { ...u, health: newHealth } : u
48661
48922
  );
48662
48923
  setUnits(updatedUnits);
48663
48924
  onAttack?.(selectedUnit, unit, damage);
48664
48925
  if (attackEvent) {
48665
48926
  eventBus.emit(`UI:${attackEvent}`, {
48666
- attackerId: selectedUnit.id,
48667
- targetId: unit.id,
48927
+ attackerId: str(selectedUnit.id),
48928
+ targetId: str(unit.id),
48668
48929
  damage
48669
48930
  });
48670
48931
  }
@@ -48681,16 +48942,20 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
48681
48942
  eventBus.emit(`UI:${tileClickEvent}`, { x, y });
48682
48943
  }
48683
48944
  if (phase === "movement" && selectedUnitId) {
48684
- const selectedUnit = units.find((u) => u.id === selectedUnitId);
48945
+ const selectedUnit = units.find((u) => str(u.id) === selectedUnitId);
48685
48946
  if (!selectedUnit) return;
48686
- const dx = Math.abs(x - selectedUnit.position.x);
48687
- const dy = Math.abs(y - selectedUnit.position.y);
48947
+ const sp = unitPosition(selectedUnit);
48948
+ const dx = Math.abs(x - sp.x);
48949
+ const dy = Math.abs(y - sp.y);
48688
48950
  const dist = dx + dy;
48689
- if (dist > 0 && dist <= selectedUnit.movement) {
48690
- if (!units.some((u) => u.position.x === x && u.position.y === y && u.health > 0)) {
48951
+ if (dist > 0 && dist <= num(selectedUnit.movement)) {
48952
+ if (!units.some((u) => {
48953
+ const p2 = unitPosition(u);
48954
+ return p2.x === x && p2.y === y && unitHealth(u) > 0;
48955
+ })) {
48691
48956
  setUnits(
48692
48957
  (prev) => prev.map(
48693
- (u) => u.id === selectedUnitId ? { ...u, position: { x, y } } : u
48958
+ (u) => str(u.id) === selectedUnitId ? { ...u, position: { x, y } } : u
48694
48959
  )
48695
48960
  );
48696
48961
  setPhase("action");
@@ -48733,12 +48998,13 @@ var init_useBattleState = __esm({
48733
48998
  "components/game/organisms/hooks/useBattleState.ts"() {
48734
48999
  "use client";
48735
49000
  init_useEventBus();
49001
+ init_boardEntity();
48736
49002
  }
48737
49003
  });
48738
49004
  function UncontrolledBattleBoard({ entity, ...rest }) {
48739
- const resolved = Array.isArray(entity) ? entity[0] : entity;
49005
+ const resolved = boardEntity(entity);
48740
49006
  const battleState = useBattleState(
48741
- resolved?.initialUnits ?? [],
49007
+ rows(resolved?.initialUnits),
48742
49008
  {
48743
49009
  tileClickEvent: rest.tileClickEvent,
48744
49010
  unitClickEvent: rest.unitClickEvent,
@@ -48774,10 +49040,23 @@ function UncontrolledBattleBoard({ entity, ...rest }) {
48774
49040
  var init_UncontrolledBattleBoard = __esm({
48775
49041
  "components/game/organisms/UncontrolledBattleBoard.tsx"() {
48776
49042
  init_BattleBoard();
49043
+ init_boardEntity();
48777
49044
  init_useBattleState();
48778
49045
  UncontrolledBattleBoard.displayName = "UncontrolledBattleBoard";
48779
49046
  }
48780
49047
  });
49048
+ function heroPosition(h) {
49049
+ return vec2(h.position);
49050
+ }
49051
+ function heroOwner(h) {
49052
+ return str(h.owner);
49053
+ }
49054
+ function heroMovement(h) {
49055
+ return num(h.movement);
49056
+ }
49057
+ function hexPassable(h) {
49058
+ return h.passable !== false;
49059
+ }
48781
49060
  function defaultIsInRange(from, to, range) {
48782
49061
  return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
48783
49062
  }
@@ -48808,36 +49087,36 @@ function WorldMapBoard({
48808
49087
  className
48809
49088
  }) {
48810
49089
  const eventBus = useEventBus();
48811
- const resolved = Array.isArray(entity) ? entity[0] : entity;
48812
- const hexes = resolved?.hexes ?? [];
48813
- const heroes = resolved?.heroes ?? [];
48814
- const features = resolved?.features ?? [];
48815
- const selectedHeroId = resolved?.selectedHeroId;
49090
+ const resolved = boardEntity(entity);
49091
+ const hexes = rows(resolved?.hexes);
49092
+ const heroes = rows(resolved?.heroes);
49093
+ const features = Array.isArray(resolved?.features) ? resolved.features : [];
49094
+ const selectedHeroId = resolved?.selectedHeroId ?? null;
48816
49095
  const assetManifest = resolved?.assetManifest;
48817
49096
  const backgroundImage = resolved?.backgroundImage;
48818
49097
  const [hoveredTile, setHoveredTile] = useState(null);
48819
49098
  const selectedHero = useMemo(
48820
- () => heroes.find((h) => h.id === selectedHeroId) ?? null,
49099
+ () => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
48821
49100
  [heroes, selectedHeroId]
48822
49101
  );
48823
49102
  const tiles = useMemo(
48824
49103
  () => hexes.map((hex) => ({
48825
- x: hex.x,
48826
- y: hex.y,
48827
- terrain: hex.terrain,
48828
- terrainSprite: hex.terrainSprite
49104
+ x: num(hex.x),
49105
+ y: num(hex.y),
49106
+ terrain: str(hex.terrain),
49107
+ terrainSprite: hex.terrainSprite == null ? void 0 : str(hex.terrainSprite)
48829
49108
  })),
48830
49109
  [hexes]
48831
49110
  );
48832
49111
  const baseUnits = useMemo(
48833
49112
  () => heroes.map((hero) => ({
48834
- id: hero.id,
48835
- position: hero.position,
48836
- name: hero.name,
48837
- team: hero.owner === "enemy" ? "enemy" : "player",
49113
+ id: str(hero.id),
49114
+ position: heroPosition(hero),
49115
+ name: str(hero.name),
49116
+ team: heroOwner(hero) === "enemy" ? "enemy" : "player",
48838
49117
  health: 100,
48839
49118
  maxHealth: 100,
48840
- sprite: hero.sprite
49119
+ sprite: hero.sprite == null ? void 0 : str(hero.sprite)
48841
49120
  })),
48842
49121
  [heroes]
48843
49122
  );
@@ -48878,73 +49157,94 @@ function WorldMapBoard({
48878
49157
  const isoUnits = useMemo(() => {
48879
49158
  if (movingPositions.size === 0) return baseUnits;
48880
49159
  return baseUnits.map((u) => {
48881
- const pos = movingPositions.get(u.id);
49160
+ const pos = u.id == null ? void 0 : movingPositions.get(u.id);
48882
49161
  return pos ? { ...u, position: pos } : u;
48883
49162
  });
48884
49163
  }, [baseUnits, movingPositions]);
48885
49164
  const validMoves = useMemo(() => {
48886
- if (!selectedHero || selectedHero.movement <= 0) return [];
49165
+ if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
49166
+ const sp = heroPosition(selectedHero);
49167
+ const sOwner = heroOwner(selectedHero);
49168
+ const range = heroMovement(selectedHero);
48887
49169
  const moves = [];
48888
49170
  hexes.forEach((hex) => {
48889
- if (hex.passable === false) return;
48890
- if (hex.x === selectedHero.position.x && hex.y === selectedHero.position.y) return;
48891
- if (!isInRange(selectedHero.position, { x: hex.x, y: hex.y }, selectedHero.movement)) return;
48892
- if (heroes.some((h) => h.position.x === hex.x && h.position.y === hex.y && h.owner === selectedHero.owner)) return;
48893
- moves.push({ x: hex.x, y: hex.y });
49171
+ const hx = num(hex.x);
49172
+ const hy = num(hex.y);
49173
+ if (!hexPassable(hex)) return;
49174
+ if (hx === sp.x && hy === sp.y) return;
49175
+ if (!isInRange(sp, { x: hx, y: hy }, range)) return;
49176
+ if (heroes.some((h) => {
49177
+ const hp = heroPosition(h);
49178
+ return hp.x === hx && hp.y === hy && heroOwner(h) === sOwner;
49179
+ })) return;
49180
+ moves.push({ x: hx, y: hy });
48894
49181
  });
48895
49182
  return moves;
48896
49183
  }, [selectedHero, hexes, heroes, isInRange]);
48897
49184
  const attackTargets = useMemo(() => {
48898
- if (!selectedHero || selectedHero.movement <= 0) return [];
48899
- return heroes.filter((h) => h.owner !== selectedHero.owner).filter((h) => isInRange(selectedHero.position, h.position, selectedHero.movement)).map((h) => h.position);
49185
+ if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
49186
+ const sp = heroPosition(selectedHero);
49187
+ const sOwner = heroOwner(selectedHero);
49188
+ const range = heroMovement(selectedHero);
49189
+ return heroes.filter((h) => heroOwner(h) !== sOwner).filter((h) => isInRange(sp, heroPosition(h), range)).map((h) => heroPosition(h));
48900
49190
  }, [selectedHero, heroes, isInRange]);
48901
- const maxY = Math.max(...hexes.map((h) => h.y), 0);
49191
+ const maxY = Math.max(...hexes.map((h) => num(h.y)), 0);
48902
49192
  const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
48903
49193
  const tileToScreen = useCallback(
48904
49194
  (tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
48905
49195
  [scale, baseOffsetX]
48906
49196
  );
48907
49197
  const hoveredHex = useMemo(
48908
- () => hoveredTile ? hexes.find((h) => h.x === hoveredTile.x && h.y === hoveredTile.y) ?? null : null,
49198
+ () => hoveredTile ? hexes.find((h) => num(h.x) === hoveredTile.x && num(h.y) === hoveredTile.y) ?? null : null,
48909
49199
  [hoveredTile, hexes]
48910
49200
  );
48911
49201
  const hoveredHero = useMemo(
48912
- () => hoveredTile ? heroes.find((h) => h.position.x === hoveredTile.x && h.position.y === hoveredTile.y) ?? null : null,
49202
+ () => hoveredTile ? heroes.find((h) => {
49203
+ const hp = heroPosition(h);
49204
+ return hp.x === hoveredTile.x && hp.y === hoveredTile.y;
49205
+ }) ?? null : null,
48913
49206
  [hoveredTile, heroes]
48914
49207
  );
48915
49208
  const handleTileClick = useCallback((x, y) => {
48916
49209
  if (movementAnimRef.current) return;
48917
- const hex = hexes.find((h) => h.x === x && h.y === y);
49210
+ const hex = hexes.find((h) => num(h.x) === x && num(h.y) === y);
48918
49211
  if (!hex) return;
48919
49212
  if (tileClickEvent) {
48920
49213
  eventBus.emit(`UI:${tileClickEvent}`, { x, y });
48921
49214
  }
48922
49215
  if (selectedHero && validMoves.some((m) => m.x === x && m.y === y)) {
48923
- startMoveAnimation(selectedHero.id, { ...selectedHero.position }, { x, y }, () => {
48924
- onHeroMove?.(selectedHero.id, x, y);
49216
+ const heroId = str(selectedHero.id);
49217
+ startMoveAnimation(heroId, { ...heroPosition(selectedHero) }, { x, y }, () => {
49218
+ onHeroMove?.(heroId, x, y);
48925
49219
  if (heroMoveEvent) {
48926
- eventBus.emit(`UI:${heroMoveEvent}`, { heroId: selectedHero.id, toX: x, toY: y });
49220
+ eventBus.emit(`UI:${heroMoveEvent}`, { heroId, toX: x, toY: y });
48927
49221
  }
48928
- if (hex.feature && hex.feature !== "none") {
48929
- onFeatureEnter?.(selectedHero.id, hex);
49222
+ const feature = str(hex.feature);
49223
+ if (feature && feature !== "none") {
49224
+ onFeatureEnter?.(heroId, hex);
48930
49225
  if (featureEnterEvent) {
48931
- eventBus.emit(`UI:${featureEnterEvent}`, { heroId: selectedHero.id, feature: hex.feature, hex });
49226
+ eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex });
48932
49227
  }
48933
49228
  }
48934
49229
  });
48935
49230
  return;
48936
49231
  }
48937
- const enemy = heroes.find((h) => h.position.x === x && h.position.y === y && h.owner === "enemy");
49232
+ const enemy = heroes.find((h) => {
49233
+ const hp = heroPosition(h);
49234
+ return hp.x === x && hp.y === y && heroOwner(h) === "enemy";
49235
+ });
48938
49236
  if (selectedHero && enemy && attackTargets.some((t) => t.x === x && t.y === y)) {
48939
- onBattleEncounter?.(selectedHero.id, enemy.id);
49237
+ const attackerId = str(selectedHero.id);
49238
+ const defenderId = str(enemy.id);
49239
+ onBattleEncounter?.(attackerId, defenderId);
48940
49240
  if (battleEncounterEvent) {
48941
- eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId: selectedHero.id, defenderId: enemy.id });
49241
+ eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId, defenderId });
48942
49242
  }
48943
49243
  }
48944
49244
  }, [hexes, heroes, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
48945
49245
  const handleUnitClick = useCallback((unitId) => {
48946
- const hero = heroes.find((h) => h.id === unitId);
48947
- if (hero && (hero.owner === "player" || allowMoveAllHeroes)) {
49246
+ const hero = heroes.find((h) => str(h.id) === unitId);
49247
+ if (hero && (heroOwner(hero) === "player" || allowMoveAllHeroes)) {
48948
49248
  onHeroSelect?.(unitId);
48949
49249
  if (heroSelectEvent) {
48950
49250
  eventBus.emit(`UI:${heroSelectEvent}`, { heroId: unitId });
@@ -49017,6 +49317,7 @@ var init_WorldMapBoard = __esm({
49017
49317
  init_Stack();
49018
49318
  init_LoadingState();
49019
49319
  init_IsometricCanvas2();
49320
+ init_boardEntity();
49020
49321
  init_isometric();
49021
49322
  WorldMapBoard.displayName = "WorldMapBoard";
49022
49323
  }
@@ -52580,10 +52881,10 @@ function parseApplicationLevel(schema) {
52580
52881
  }
52581
52882
  const count = schema.orbitals.length;
52582
52883
  const cols = Math.ceil(Math.sqrt(count));
52583
- const rows = Math.ceil(count / cols);
52884
+ const rows2 = Math.ceil(count / cols);
52584
52885
  const spacing = 200;
52585
52886
  const gridW = cols * spacing;
52586
- const gridH = rows * spacing;
52887
+ const gridH = rows2 * spacing;
52587
52888
  const originX = (600 - gridW) / 2 + spacing / 2;
52588
52889
  const originY = (400 - gridH) / 2 + spacing / 2;
52589
52890
  schema.orbitals.forEach((orbital, i) => {
@@ -55725,11 +56026,11 @@ function buildMockData(schema) {
55725
56026
  result[entityName] = entity.instances;
55726
56027
  continue;
55727
56028
  }
55728
- const rows = Array.from(
56029
+ const rows2 = Array.from(
55729
56030
  { length: 10 },
55730
56031
  (_, i) => generateEntityRow(entity, i + 1)
55731
56032
  );
55732
- result[entityName] = rows;
56033
+ result[entityName] = rows2;
55733
56034
  }
55734
56035
  for (const orbital of schema.orbitals) {
55735
56036
  for (const traitRef of orbital.traits ?? []) {
@@ -59228,18 +59529,18 @@ function layoutOrbitals(count, containerW, containerH) {
59228
59529
  if (count === 0) return [];
59229
59530
  if (count === 1) return [{ cx: containerW / 2, cy: containerH / 2 }];
59230
59531
  const cols = Math.min(count, Math.ceil(Math.sqrt(count)));
59231
- const rows = Math.ceil(count / cols);
59532
+ const rows2 = Math.ceil(count / cols);
59232
59533
  const edgePad = 24;
59233
59534
  const fitMinCx = UNIT_DISPLAY_W / 2 + edgePad;
59234
59535
  const fitMinCy = UNIT_DISPLAY_H / 2 + edgePad;
59235
59536
  const fitMaxCx = Math.max(fitMinCx, containerW - UNIT_DISPLAY_W / 2 - edgePad);
59236
59537
  const fitMaxCy = Math.max(fitMinCy, containerH - UNIT_DISPLAY_H / 2 - edgePad);
59237
59538
  const fitStepX = cols > 1 ? (fitMaxCx - fitMinCx) / (cols - 1) : 0;
59238
- const fitStepY = rows > 1 ? (fitMaxCy - fitMinCy) / (rows - 1) : 0;
59539
+ const fitStepY = rows2 > 1 ? (fitMaxCy - fitMinCy) / (rows2 - 1) : 0;
59239
59540
  const stepX = Math.min(fitStepX, UNIT_DISPLAY_W * 3.5);
59240
59541
  const stepY = Math.min(fitStepY, UNIT_DISPLAY_H * 3.5);
59241
59542
  const gridW = (cols - 1) * stepX;
59242
- const gridH = (rows - 1) * stepY;
59543
+ const gridH = (rows2 - 1) * stepY;
59243
59544
  const originX = (containerW - gridW) / 2;
59244
59545
  const originY = (containerH - gridH) / 2;
59245
59546
  return Array.from({ length: count }, (_, i) => ({