@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
@@ -7179,7 +7179,7 @@ var init_SvgGrid = __esm({
7179
7179
  x,
7180
7180
  y,
7181
7181
  cols = 4,
7182
- rows = 3,
7182
+ rows: rows2 = 3,
7183
7183
  spacing = 20,
7184
7184
  nodeRadius = 3,
7185
7185
  color = "var(--color-primary)",
@@ -7188,7 +7188,7 @@ var init_SvgGrid = __esm({
7188
7188
  highlights = []
7189
7189
  }) => {
7190
7190
  const highlightSet = new Set(highlights);
7191
- return /* @__PURE__ */ jsxRuntime.jsx("g", { className, opacity, children: Array.from({ length: rows }).map(
7191
+ return /* @__PURE__ */ jsxRuntime.jsx("g", { className, opacity, children: Array.from({ length: rows2 }).map(
7192
7192
  (_, row) => Array.from({ length: cols }).map((_2, col) => {
7193
7193
  const index = row * cols + col;
7194
7194
  const isHighlighted = highlightSet.has(index);
@@ -7848,7 +7848,7 @@ var init_Input = __esm({
7848
7848
  onClear,
7849
7849
  value,
7850
7850
  options,
7851
- rows = 3,
7851
+ rows: rows2 = 3,
7852
7852
  onChange,
7853
7853
  ...props
7854
7854
  }, ref) => {
@@ -7898,7 +7898,7 @@ var init_Input = __esm({
7898
7898
  ref,
7899
7899
  value,
7900
7900
  onChange,
7901
- rows,
7901
+ rows: rows2,
7902
7902
  className: baseClassName,
7903
7903
  ...props
7904
7904
  }
@@ -9893,66 +9893,6 @@ var init_RangeSlider = __esm({
9893
9893
  RangeSlider.displayName = "RangeSlider";
9894
9894
  }
9895
9895
  });
9896
- function easeOut(t) {
9897
- return t * (2 - t);
9898
- }
9899
- var AnimatedCounter;
9900
- var init_AnimatedCounter = __esm({
9901
- "components/marketing/atoms/AnimatedCounter.tsx"() {
9902
- "use client";
9903
- init_cn();
9904
- init_Typography();
9905
- AnimatedCounter = ({
9906
- value: rawValue,
9907
- duration = 600,
9908
- prefix,
9909
- suffix,
9910
- className
9911
- }) => {
9912
- const numericRaw = typeof rawValue === "number" ? rawValue : Number.parseFloat(String(rawValue ?? ""));
9913
- const value = !Number.isNaN(numericRaw) ? numericRaw : 0;
9914
- const [displayValue, setDisplayValue] = React97.useState(value);
9915
- const previousValueRef = React97.useRef(value);
9916
- const animationFrameRef = React97.useRef(null);
9917
- React97.useEffect(() => {
9918
- const from = previousValueRef.current;
9919
- const to = value;
9920
- previousValueRef.current = value;
9921
- if (from === to) {
9922
- setDisplayValue(to);
9923
- return;
9924
- }
9925
- const startTime = performance.now();
9926
- const diff = to - from;
9927
- function animate(currentTime) {
9928
- const elapsed = currentTime - startTime;
9929
- const progress = Math.min(elapsed / duration, 1);
9930
- const easedProgress = easeOut(progress);
9931
- setDisplayValue(from + diff * easedProgress);
9932
- if (progress < 1) {
9933
- animationFrameRef.current = requestAnimationFrame(animate);
9934
- } else {
9935
- setDisplayValue(to);
9936
- }
9937
- }
9938
- animationFrameRef.current = requestAnimationFrame(animate);
9939
- return () => {
9940
- if (animationFrameRef.current !== null) {
9941
- cancelAnimationFrame(animationFrameRef.current);
9942
- }
9943
- };
9944
- }, [value, duration]);
9945
- const decimalPlaces = Number.isInteger(value) ? 0 : String(value).split(".")[1]?.length ?? 0;
9946
- const formattedValue = displayValue.toFixed(decimalPlaces);
9947
- return /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "h3", className: cn("tabular-nums", className), children: [
9948
- prefix,
9949
- formattedValue,
9950
- suffix
9951
- ] });
9952
- };
9953
- AnimatedCounter.displayName = "AnimatedCounter";
9954
- }
9955
- });
9956
9896
  function useInfiniteScroll(onLoadMore, options = {}) {
9957
9897
  const { rootMargin = "200px", hasMore = true, isLoading = false } = options;
9958
9898
  const observerRef = React97.useRef(null);
@@ -12436,15 +12376,15 @@ function HeaderSkeleton({ className }) {
12436
12376
  ] })
12437
12377
  ] });
12438
12378
  }
12439
- function TableSkeleton({ rows = 5, columns = 4, className }) {
12379
+ function TableSkeleton({ rows: rows2 = 5, columns = 4, className }) {
12440
12380
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", className: cn("border border-border rounded-lg overflow-hidden", className), children: [
12441
12381
  /* @__PURE__ */ jsxRuntime.jsx(HStack, { className: "px-4 py-3 bg-muted/30 border-b border-border", children: Array.from({ length: columns }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(SkeletonBlock, { className: "h-4 flex-1 mx-2" }, i)) }),
12442
- Array.from({ length: rows }).map((_, rowIdx) => /* @__PURE__ */ jsxRuntime.jsx(
12382
+ Array.from({ length: rows2 }).map((_, rowIdx) => /* @__PURE__ */ jsxRuntime.jsx(
12443
12383
  HStack,
12444
12384
  {
12445
12385
  className: cn(
12446
12386
  "px-4 py-3",
12447
- rowIdx < rows - 1 && "border-b border-border"
12387
+ rowIdx < rows2 - 1 && "border-b border-border"
12448
12388
  ),
12449
12389
  children: Array.from({ length: columns }).map((_2, colIdx) => /* @__PURE__ */ jsxRuntime.jsx(SkeletonLine, { className: "flex-1 mx-2" }, colIdx))
12450
12390
  },
@@ -12492,18 +12432,18 @@ function CardSkeleton({ className }) {
12492
12432
  }
12493
12433
  );
12494
12434
  }
12495
- function TextSkeleton({ rows = 3, className }) {
12496
- return /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "sm", className, children: Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
12435
+ function TextSkeleton({ rows: rows2 = 3, className }) {
12436
+ return /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "sm", className, children: Array.from({ length: rows2 }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
12497
12437
  SkeletonLine,
12498
12438
  {
12499
- className: i === rows - 1 ? "w-2/3" : "w-full"
12439
+ className: i === rows2 - 1 ? "w-2/3" : "w-full"
12500
12440
  },
12501
12441
  i
12502
12442
  )) });
12503
12443
  }
12504
12444
  function Skeleton({
12505
12445
  variant = "text",
12506
- rows,
12446
+ rows: rows2,
12507
12447
  columns,
12508
12448
  fields,
12509
12449
  className
@@ -12513,15 +12453,15 @@ function Skeleton({
12513
12453
  case "header":
12514
12454
  return /* @__PURE__ */ jsxRuntime.jsx(HeaderSkeleton, { className });
12515
12455
  case "table":
12516
- return /* @__PURE__ */ jsxRuntime.jsx(TableSkeleton, { rows, columns, className });
12456
+ return /* @__PURE__ */ jsxRuntime.jsx(TableSkeleton, { rows: rows2, columns, className });
12517
12457
  case "form":
12518
12458
  return /* @__PURE__ */ jsxRuntime.jsx(FormSkeleton, { fields, className });
12519
12459
  case "card":
12520
12460
  return /* @__PURE__ */ jsxRuntime.jsx(CardSkeleton, { className });
12521
12461
  case "text":
12522
- return /* @__PURE__ */ jsxRuntime.jsx(TextSkeleton, { rows, className });
12462
+ return /* @__PURE__ */ jsxRuntime.jsx(TextSkeleton, { rows: rows2, className });
12523
12463
  default:
12524
- return /* @__PURE__ */ jsxRuntime.jsx(TextSkeleton, { rows, className });
12464
+ return /* @__PURE__ */ jsxRuntime.jsx(TextSkeleton, { rows: rows2, className });
12525
12465
  }
12526
12466
  }
12527
12467
  var pulseClass;
@@ -13675,7 +13615,7 @@ var init_Menu = __esm({
13675
13615
  className
13676
13616
  }) => {
13677
13617
  const eventBus = useEventBus();
13678
- const { t } = hooks.useTranslate();
13618
+ const { t, direction } = hooks.useTranslate();
13679
13619
  const [isOpen, setIsOpen] = React97.useState(false);
13680
13620
  const [activeSubMenu, setActiveSubMenu] = React97.useState(null);
13681
13621
  const [triggerRect, setTriggerRect] = React97.useState(null);
@@ -13729,6 +13669,18 @@ var init_Menu = __esm({
13729
13669
  "bottom-start": "top-full left-0 mt-2",
13730
13670
  "bottom-end": "top-full right-0 mt-2"
13731
13671
  };
13672
+ const rtlMirror = {
13673
+ "top-left": "top-right",
13674
+ "top-right": "top-left",
13675
+ "bottom-left": "bottom-right",
13676
+ "bottom-right": "bottom-left",
13677
+ "top-start": "top-end",
13678
+ "top-end": "top-start",
13679
+ "bottom-start": "bottom-end",
13680
+ "bottom-end": "bottom-start"
13681
+ };
13682
+ const effectivePosition = direction === "rtl" ? rtlMirror[position] ?? position : position;
13683
+ const subMenuSideClass = direction === "rtl" ? "right-full mr-2" : "left-full ml-2";
13732
13684
  const triggerChild = React97__namespace.default.isValidElement(trigger) ? trigger : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", as: "span", children: trigger });
13733
13685
  const triggerElement = React97__namespace.default.cloneElement(
13734
13686
  triggerChild,
@@ -13756,7 +13708,7 @@ var init_Menu = __esm({
13756
13708
  onMouseEnter: () => hasSubMenu && setActiveSubMenu(itemId),
13757
13709
  "data-testid": item.event ? `action-${item.event}` : void 0,
13758
13710
  className: cn(
13759
- "w-full flex items-center justify-between gap-3 px-4 py-2 text-left",
13711
+ "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
13760
13712
  "text-sm transition-colors",
13761
13713
  "hover:bg-muted",
13762
13714
  "focus:outline-none focus:bg-muted",
@@ -13775,7 +13727,7 @@ var init_Menu = __esm({
13775
13727
  }
13776
13728
  ),
13777
13729
  item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
13778
- hasSubMenu && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "chevron-right", size: "sm", className: "flex-shrink-0" })
13730
+ hasSubMenu && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: direction === "rtl" ? "chevron-left" : "chevron-right", size: "sm", className: "flex-shrink-0" })
13779
13731
  ] })
13780
13732
  },
13781
13733
  itemId
@@ -13795,7 +13747,8 @@ var init_Menu = __esm({
13795
13747
  Box,
13796
13748
  {
13797
13749
  className: cn(
13798
- "absolute left-full top-0 ml-2 z-50",
13750
+ "absolute top-0 z-50",
13751
+ subMenuSideClass,
13799
13752
  menuContainerStyles
13800
13753
  ),
13801
13754
  children: renderMenuItems(item.subMenu)
@@ -13813,12 +13766,12 @@ var init_Menu = __esm({
13813
13766
  className: cn(
13814
13767
  "absolute z-50",
13815
13768
  menuContainerStyles,
13816
- positionClasses3[position],
13769
+ positionClasses3[effectivePosition],
13817
13770
  className
13818
13771
  ),
13819
13772
  style: {
13820
- left: position.includes("left") ? 0 : "auto",
13821
- right: position.includes("right") ? 0 : "auto"
13773
+ left: effectivePosition.includes("left") ? 0 : "auto",
13774
+ right: effectivePosition.includes("right") ? 0 : "auto"
13822
13775
  },
13823
13776
  role: "menu",
13824
13777
  children: renderMenuItems(items)
@@ -14136,7 +14089,7 @@ var init_MapView = __esm({
14136
14089
  shadowSize: [41, 41]
14137
14090
  });
14138
14091
  L.Marker.prototype.options.icon = defaultIcon;
14139
- const { useEffect: useEffect79, useRef: useRef71, useCallback: useCallback122, useState: useState113 } = React97__namespace.default;
14092
+ const { useEffect: useEffect79, useRef: useRef71, useCallback: useCallback123, useState: useState113 } = React97__namespace.default;
14140
14093
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
14141
14094
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
14142
14095
  function MapUpdater({ centerLat, centerLng, zoom }) {
@@ -14182,7 +14135,7 @@ var init_MapView = __esm({
14182
14135
  }) {
14183
14136
  const eventBus = useEventBus3();
14184
14137
  const [clickedPosition, setClickedPosition] = useState113(null);
14185
- const handleMapClick = useCallback122((lat, lng) => {
14138
+ const handleMapClick = useCallback123((lat, lng) => {
14186
14139
  if (showClickedPin) {
14187
14140
  setClickedPosition({ lat, lng });
14188
14141
  }
@@ -14191,7 +14144,7 @@ var init_MapView = __esm({
14191
14144
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
14192
14145
  }
14193
14146
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
14194
- const handleMarkerClick = useCallback122((marker) => {
14147
+ const handleMarkerClick = useCallback123((marker) => {
14195
14148
  onMarkerClick?.(marker);
14196
14149
  if (markerClickEvent) {
14197
14150
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -14412,7 +14365,7 @@ function InputPattern({
14412
14365
  function TextareaPattern({
14413
14366
  value = "",
14414
14367
  placeholder,
14415
- rows = 4,
14368
+ rows: rows2 = 4,
14416
14369
  disabled = false,
14417
14370
  fieldError,
14418
14371
  onChange,
@@ -14432,7 +14385,7 @@ function TextareaPattern({
14432
14385
  {
14433
14386
  value: localValue,
14434
14387
  placeholder,
14435
- rows,
14388
+ rows: rows2,
14436
14389
  disabled,
14437
14390
  error: fieldError,
14438
14391
  onChange: handleChange,
@@ -14917,6 +14870,91 @@ var init_ActionPalette = __esm({
14917
14870
  ActionPalette.displayName = "ActionPalette";
14918
14871
  }
14919
14872
  });
14873
+ function parseValue(value) {
14874
+ if (value === "" || value == null) return { num: 0, prefix: "", suffix: "", decimals: 0 };
14875
+ const match = String(value).match(/^([^0-9]*)([0-9]+(?:\.[0-9]+)?)(.*)$/);
14876
+ if (!match) {
14877
+ return { num: 0, prefix: "", suffix: String(value), decimals: 0 };
14878
+ }
14879
+ const numStr = match[2];
14880
+ const decimalIdx = numStr.indexOf(".");
14881
+ const decimals = decimalIdx >= 0 ? numStr.length - decimalIdx - 1 : 0;
14882
+ return {
14883
+ prefix: match[1],
14884
+ num: parseFloat(numStr),
14885
+ suffix: match[3],
14886
+ decimals
14887
+ };
14888
+ }
14889
+ var AnimatedCounter;
14890
+ var init_AnimatedCounter = __esm({
14891
+ "components/core/molecules/AnimatedCounter.tsx"() {
14892
+ "use client";
14893
+ init_cn();
14894
+ init_Box();
14895
+ init_Typography();
14896
+ AnimatedCounter = ({
14897
+ value,
14898
+ label,
14899
+ duration = 1500,
14900
+ className
14901
+ }) => {
14902
+ const ref = React97.useRef(null);
14903
+ const [displayValue, setDisplayValue] = React97.useState("0");
14904
+ const [hasAnimated, setHasAnimated] = React97.useState(false);
14905
+ const animate = React97.useCallback(() => {
14906
+ const { num: num2, prefix, suffix, decimals } = parseValue(value);
14907
+ if (num2 === 0) {
14908
+ setDisplayValue(String(value));
14909
+ return;
14910
+ }
14911
+ const startTime = performance.now();
14912
+ const tick = (now2) => {
14913
+ const elapsed = now2 - startTime;
14914
+ const progress = Math.min(elapsed / duration, 1);
14915
+ const eased = 1 - Math.pow(1 - progress, 3);
14916
+ const current = eased * num2;
14917
+ setDisplayValue(`${prefix}${current.toFixed(decimals)}${suffix}`);
14918
+ if (progress < 1) {
14919
+ requestAnimationFrame(tick);
14920
+ } else {
14921
+ setDisplayValue(String(value));
14922
+ }
14923
+ };
14924
+ requestAnimationFrame(tick);
14925
+ }, [value, duration]);
14926
+ React97.useEffect(() => {
14927
+ if (hasAnimated) return;
14928
+ const el = ref.current;
14929
+ if (!el) return;
14930
+ const observer2 = new IntersectionObserver(
14931
+ (entries) => {
14932
+ if (entries[0].isIntersecting) {
14933
+ setHasAnimated(true);
14934
+ animate();
14935
+ observer2.disconnect();
14936
+ }
14937
+ },
14938
+ { threshold: 0.3 }
14939
+ );
14940
+ observer2.observe(el);
14941
+ return () => observer2.disconnect();
14942
+ }, [hasAnimated, animate]);
14943
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { ref, className: cn("flex flex-col items-center gap-1 p-4", className), children: [
14944
+ /* @__PURE__ */ jsxRuntime.jsx(
14945
+ Typography,
14946
+ {
14947
+ variant: "h2",
14948
+ className: "text-primary font-bold tabular-nums",
14949
+ children: hasAnimated ? displayValue : "0"
14950
+ }
14951
+ ),
14952
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", color: "muted", className: "text-center", children: label })
14953
+ ] });
14954
+ };
14955
+ AnimatedCounter.displayName = "AnimatedCounter";
14956
+ }
14957
+ });
14920
14958
  var AuthLayout;
14921
14959
  var init_AuthLayout = __esm({
14922
14960
  "components/core/templates/AuthLayout.tsx"() {
@@ -16302,6 +16340,39 @@ var init_IsometricCanvas2 = __esm({
16302
16340
  init_IsometricCanvas();
16303
16341
  }
16304
16342
  });
16343
+
16344
+ // components/game/organisms/boardEntity.ts
16345
+ function boardEntity(entity) {
16346
+ if (!entity) return void 0;
16347
+ return Array.isArray(entity) ? entity[0] : entity;
16348
+ }
16349
+ function str(v) {
16350
+ return v == null ? "" : String(v);
16351
+ }
16352
+ function num(v, fallback = 0) {
16353
+ const n = Number(v);
16354
+ return Number.isFinite(n) ? n : fallback;
16355
+ }
16356
+ function rows(v) {
16357
+ return Array.isArray(v) ? v : [];
16358
+ }
16359
+ function vec2(v) {
16360
+ const o = v ?? {};
16361
+ return { x: num(o.x), y: num(o.y) };
16362
+ }
16363
+ function unitPosition(u) {
16364
+ return vec2(u.position);
16365
+ }
16366
+ function unitTeam(u) {
16367
+ return str(u.team);
16368
+ }
16369
+ function unitHealth(u) {
16370
+ return num(u.health);
16371
+ }
16372
+ var init_boardEntity = __esm({
16373
+ "components/game/organisms/boardEntity.ts"() {
16374
+ }
16375
+ });
16305
16376
  function BattleBoard({
16306
16377
  entity,
16307
16378
  scale = 0.45,
@@ -16328,43 +16399,49 @@ function BattleBoard({
16328
16399
  attackEvent,
16329
16400
  className
16330
16401
  }) {
16331
- const tiles = entity.tiles;
16332
- const features = entity.features ?? [];
16333
- const boardWidth = entity.boardWidth ?? 8;
16334
- const boardHeight = entity.boardHeight ?? 6;
16335
- const assetManifest = entity.assetManifest;
16336
- const backgroundImage = entity.backgroundImage;
16337
- const units = entity.units;
16338
- const selectedUnitId = entity.selectedUnitId;
16339
- const currentPhase = entity.phase;
16340
- const currentTurn = entity.turn;
16341
- const gameResult = entity.gameResult;
16402
+ const board = boardEntity(entity) ?? {};
16403
+ const tiles = Array.isArray(board.tiles) ? board.tiles : [];
16404
+ const features = Array.isArray(board.features) ? board.features : [];
16405
+ const boardWidth = num(board.boardWidth, 8);
16406
+ const boardHeight = num(board.boardHeight, 6);
16407
+ const assetManifest = board.assetManifest;
16408
+ const backgroundImage = board.backgroundImage;
16409
+ const units = rows(board.units);
16410
+ const selectedUnitId = board.selectedUnitId ?? null;
16411
+ const currentPhase = str(board.phase) || "observation";
16412
+ const currentTurn = num(board.turn, 1);
16413
+ const gameResult = board.gameResult ?? null;
16342
16414
  const eventBus = useEventBus();
16343
16415
  const { t } = hooks.useTranslate();
16344
16416
  const [hoveredTile, setHoveredTile] = React97.useState(null);
16345
16417
  const [isShaking, setIsShaking] = React97.useState(false);
16346
16418
  const selectedUnit = React97.useMemo(
16347
- () => units.find((u) => u.id === selectedUnitId) ?? null,
16419
+ () => units.find((u) => str(u.id) === selectedUnitId) ?? null,
16348
16420
  [units, selectedUnitId]
16349
16421
  );
16350
16422
  const hoveredUnit = React97.useMemo(() => {
16351
16423
  if (!hoveredTile) return null;
16352
- return units.find(
16353
- (u) => u.position.x === hoveredTile.x && u.position.y === hoveredTile.y && u.health > 0
16354
- ) ?? null;
16424
+ return units.find((u) => {
16425
+ const p2 = unitPosition(u);
16426
+ return p2.x === hoveredTile.x && p2.y === hoveredTile.y && unitHealth(u) > 0;
16427
+ }) ?? null;
16355
16428
  }, [hoveredTile, units]);
16356
- const playerUnits = React97.useMemo(() => units.filter((u) => u.team === "player" && u.health > 0), [units]);
16357
- const enemyUnits = React97.useMemo(() => units.filter((u) => u.team === "enemy" && u.health > 0), [units]);
16429
+ const playerUnits = React97.useMemo(() => units.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0), [units]);
16430
+ const enemyUnits = React97.useMemo(() => units.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0), [units]);
16358
16431
  const validMoves = React97.useMemo(() => {
16359
16432
  if (!selectedUnit || currentPhase !== "movement") return [];
16360
16433
  const moves = [];
16361
- const range = selectedUnit.movement;
16434
+ const range = num(selectedUnit.movement);
16435
+ const origin = unitPosition(selectedUnit);
16362
16436
  for (let dy = -range; dy <= range; dy++) {
16363
16437
  for (let dx = -range; dx <= range; dx++) {
16364
- const nx = selectedUnit.position.x + dx;
16365
- const ny = selectedUnit.position.y + dy;
16438
+ const nx = origin.x + dx;
16439
+ const ny = origin.y + dy;
16366
16440
  const dist = Math.abs(dx) + Math.abs(dy);
16367
- 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)) {
16441
+ if (dist > 0 && dist <= range && nx >= 0 && nx < boardWidth && ny >= 0 && ny < boardHeight && !units.some((u) => {
16442
+ const p2 = unitPosition(u);
16443
+ return p2.x === nx && p2.y === ny && unitHealth(u) > 0;
16444
+ })) {
16368
16445
  moves.push({ x: nx, y: ny });
16369
16446
  }
16370
16447
  }
@@ -16373,11 +16450,14 @@ function BattleBoard({
16373
16450
  }, [selectedUnit, currentPhase, units, boardWidth, boardHeight]);
16374
16451
  const attackTargets = React97.useMemo(() => {
16375
16452
  if (!selectedUnit || currentPhase !== "action") return [];
16376
- return units.filter((u) => u.team !== selectedUnit.team && u.health > 0).filter((u) => {
16377
- const dx = Math.abs(u.position.x - selectedUnit.position.x);
16378
- const dy = Math.abs(u.position.y - selectedUnit.position.y);
16453
+ const sp = unitPosition(selectedUnit);
16454
+ const sTeam = unitTeam(selectedUnit);
16455
+ return units.filter((u) => unitTeam(u) !== sTeam && unitHealth(u) > 0).filter((u) => {
16456
+ const p2 = unitPosition(u);
16457
+ const dx = Math.abs(p2.x - sp.x);
16458
+ const dy = Math.abs(p2.y - sp.y);
16379
16459
  return dx <= 1 && dy <= 1 && dx + dy > 0;
16380
- }).map((u) => u.position);
16460
+ }).map((u) => unitPosition(u));
16381
16461
  }, [selectedUnit, currentPhase, units]);
16382
16462
  const MOVE_SPEED_MS_PER_TILE = 300;
16383
16463
  const movementAnimRef = React97.useRef(null);
@@ -16417,23 +16497,25 @@ function BattleBoard({
16417
16497
  return () => clearInterval(interval);
16418
16498
  }, []);
16419
16499
  const isoUnits = React97.useMemo(() => {
16420
- return units.filter((u) => u.health > 0).map((unit) => {
16421
- const pos = movingPositions.get(unit.id) ?? unit.position;
16500
+ return units.filter((u) => unitHealth(u) > 0).map((unit) => {
16501
+ const id = str(unit.id);
16502
+ const pos = movingPositions.get(id) ?? unitPosition(unit);
16503
+ const unitTraits = Array.isArray(unit.traits) ? unit.traits : void 0;
16422
16504
  return {
16423
- id: unit.id,
16505
+ id,
16424
16506
  position: pos,
16425
- name: unit.name,
16426
- team: unit.team,
16427
- health: unit.health,
16428
- maxHealth: unit.maxHealth,
16429
- unitType: unit.unitType,
16430
- heroId: unit.heroId,
16431
- sprite: unit.sprite,
16432
- traits: unit.traits?.map((t2) => ({
16433
- name: t2.name,
16434
- currentState: t2.currentState,
16435
- states: t2.states,
16436
- cooldown: t2.cooldown ?? 0
16507
+ name: str(unit.name),
16508
+ team: unitTeam(unit),
16509
+ health: unitHealth(unit),
16510
+ maxHealth: num(unit.maxHealth),
16511
+ unitType: unit.unitType == null ? void 0 : str(unit.unitType),
16512
+ heroId: unit.heroId == null ? void 0 : str(unit.heroId),
16513
+ sprite: unit.sprite == null ? void 0 : str(unit.sprite),
16514
+ traits: unitTraits?.map((tr) => ({
16515
+ name: tr.name,
16516
+ currentState: tr.currentState,
16517
+ states: tr.states,
16518
+ cooldown: tr.cooldown ?? 0
16437
16519
  }))
16438
16520
  };
16439
16521
  });
@@ -16445,8 +16527,8 @@ function BattleBoard({
16445
16527
  [scale, baseOffsetX]
16446
16528
  );
16447
16529
  const checkGameEnd = React97.useCallback(() => {
16448
- const pa = units.filter((u) => u.team === "player" && u.health > 0);
16449
- const ea = units.filter((u) => u.team === "enemy" && u.health > 0);
16530
+ const pa = units.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
16531
+ const ea = units.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
16450
16532
  if (pa.length === 0) {
16451
16533
  onGameEnd?.("defeat");
16452
16534
  if (gameEndEvent) {
@@ -16460,21 +16542,22 @@ function BattleBoard({
16460
16542
  }
16461
16543
  }, [units, onGameEnd, gameEndEvent, eventBus]);
16462
16544
  const handleUnitClick = React97.useCallback((unitId) => {
16463
- const unit = units.find((u) => u.id === unitId);
16545
+ const unit = units.find((u) => str(u.id) === unitId);
16464
16546
  if (!unit) return;
16465
16547
  if (unitClickEvent) {
16466
16548
  eventBus.emit(`UI:${unitClickEvent}`, { unitId });
16467
16549
  }
16468
16550
  if (currentPhase === "action" && selectedUnit) {
16469
- if (unit.team === "enemy" && attackTargets.some((t2) => t2.x === unit.position.x && t2.y === unit.position.y)) {
16470
- const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, selectedUnit.attack - unit.defense);
16551
+ const up = unitPosition(unit);
16552
+ if (unitTeam(unit) === "enemy" && attackTargets.some((t2) => t2.x === up.x && t2.y === up.y)) {
16553
+ const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, num(selectedUnit.attack) - num(unit.defense));
16471
16554
  setIsShaking(true);
16472
16555
  setTimeout(() => setIsShaking(false), 300);
16473
16556
  onAttack?.(selectedUnit, unit, damage);
16474
16557
  if (attackEvent) {
16475
16558
  eventBus.emit(`UI:${attackEvent}`, {
16476
- attackerId: selectedUnit.id,
16477
- targetId: unit.id,
16559
+ attackerId: str(selectedUnit.id),
16560
+ targetId: str(unit.id),
16478
16561
  damage
16479
16562
  });
16480
16563
  }
@@ -16489,9 +16572,9 @@ function BattleBoard({
16489
16572
  if (currentPhase === "movement" && selectedUnit) {
16490
16573
  if (movementAnimRef.current) return;
16491
16574
  if (validMoves.some((m) => m.x === x && m.y === y)) {
16492
- const from = { ...selectedUnit.position };
16575
+ const from = { ...unitPosition(selectedUnit) };
16493
16576
  const to = { x, y };
16494
- startMoveAnimation(selectedUnit.id, from, to, () => {
16577
+ startMoveAnimation(str(selectedUnit.id), from, to, () => {
16495
16578
  onUnitMove?.(selectedUnit, to);
16496
16579
  });
16497
16580
  }
@@ -16649,6 +16732,7 @@ var init_BattleBoard = __esm({
16649
16732
  init_Typography();
16650
16733
  init_Stack();
16651
16734
  init_IsometricCanvas2();
16735
+ init_boardEntity();
16652
16736
  init_isometric();
16653
16737
  BattleBoard.displayName = "BattleBoard";
16654
16738
  }
@@ -17042,24 +17126,24 @@ var init_CodeBlock = __esm({
17042
17126
  return;
17043
17127
  }
17044
17128
  lineEls.forEach((el) => {
17045
- const num = parseInt(el.getAttribute("data-line") ?? "-1", 10);
17046
- if (hiddenLines.has(num)) {
17129
+ const num2 = parseInt(el.getAttribute("data-line") ?? "-1", 10);
17130
+ if (hiddenLines.has(num2)) {
17047
17131
  el.style.display = "none";
17048
17132
  return;
17049
17133
  }
17050
17134
  el.style.display = "";
17051
17135
  el.style.position = "relative";
17052
17136
  el.style.paddingLeft = "1.2em";
17053
- const region = foldStartMap.get(num);
17137
+ const region = foldStartMap.get(num2);
17054
17138
  if (!region) return;
17055
- const isCollapsed = collapsed.has(num);
17139
+ const isCollapsed = collapsed.has(num2);
17056
17140
  const toggle = document.createElement("span");
17057
17141
  toggle.className = "fold-toggle";
17058
17142
  toggle.textContent = isCollapsed ? "\u25B6" : "\u25BC";
17059
17143
  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%";
17060
17144
  toggle.addEventListener("click", (e) => {
17061
17145
  e.stopPropagation();
17062
- toggleFoldRef.current(num);
17146
+ toggleFoldRef.current(num2);
17063
17147
  });
17064
17148
  el.insertBefore(toggle, el.firstChild);
17065
17149
  if (isCollapsed) {
@@ -19312,10 +19396,13 @@ var init_BookChapterView = __esm({
19312
19396
  init_cn();
19313
19397
  BookChapterView = ({
19314
19398
  chapter,
19399
+ orbitalSchema,
19315
19400
  direction,
19316
19401
  className
19317
19402
  }) => {
19318
19403
  const { t: _t } = hooks.useTranslate();
19404
+ const title = String(chapter.title ?? "");
19405
+ const content = String(chapter.content ?? "");
19319
19406
  return /* @__PURE__ */ jsxRuntime.jsxs(
19320
19407
  VStack,
19321
19408
  {
@@ -19323,16 +19410,16 @@ var init_BookChapterView = __esm({
19323
19410
  className: cn("px-6 py-8 max-w-4xl mx-auto w-full", className),
19324
19411
  style: { direction },
19325
19412
  children: [
19326
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h1", className: "text-3xl font-bold", children: chapter.title }),
19413
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h1", className: "text-3xl font-bold", children: title }),
19327
19414
  /* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
19328
- !!chapter.orbitalSchema && /* @__PURE__ */ jsxRuntime.jsx(ScaledDiagram, { children: /* @__PURE__ */ jsxRuntime.jsx(
19415
+ !!orbitalSchema && /* @__PURE__ */ jsxRuntime.jsx(ScaledDiagram, { children: /* @__PURE__ */ jsxRuntime.jsx(
19329
19416
  JazariStateMachine,
19330
19417
  {
19331
- schema: chapter.orbitalSchema,
19418
+ schema: orbitalSchema,
19332
19419
  direction
19333
19420
  }
19334
19421
  ) }),
19335
- /* @__PURE__ */ jsxRuntime.jsx(ContentRenderer, { content: chapter.content, direction })
19422
+ /* @__PURE__ */ jsxRuntime.jsx(ContentRenderer, { content, direction })
19336
19423
  ]
19337
19424
  }
19338
19425
  );
@@ -19430,7 +19517,7 @@ var init_BookNavBar = __esm({
19430
19517
  BookNavBar = ({
19431
19518
  currentPage,
19432
19519
  totalPages,
19433
- chapterTitle,
19520
+ chapterTitle: chapterTitle2,
19434
19521
  direction,
19435
19522
  className
19436
19523
  }) => {
@@ -19471,12 +19558,12 @@ var init_BookNavBar = __esm({
19471
19558
  )
19472
19559
  ] }),
19473
19560
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex-1 mx-4 max-w-md", children: [
19474
- chapterTitle && /* @__PURE__ */ jsxRuntime.jsx(
19561
+ chapterTitle2 && /* @__PURE__ */ jsxRuntime.jsx(
19475
19562
  Typography,
19476
19563
  {
19477
19564
  variant: "caption",
19478
19565
  className: "text-center block truncate text-muted-foreground",
19479
- children: chapterTitle
19566
+ children: chapterTitle2
19480
19567
  }
19481
19568
  ),
19482
19569
  /* @__PURE__ */ jsxRuntime.jsx(ProgressBar, { value: progress, size: "sm", variant: "primary" })
@@ -19543,31 +19630,35 @@ var init_BookTableOfContents = __esm({
19543
19630
  style: { direction },
19544
19631
  children: [
19545
19632
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h1", className: "text-3xl font-bold text-center mb-4", children: t("book.tableOfContents") }),
19546
- parts.map((part, partIdx) => /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
19547
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", align: "center", children: [
19548
- /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", children: t("book.partNumber", { number: String(partIdx + 1) }) }),
19549
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h3", className: "font-semibold", children: part.title })
19550
- ] }),
19551
- /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "xs", className: direction === "rtl" ? "pr-6" : "pl-6", children: part.chapters.map((chapter) => {
19552
- const isCurrent = chapter.id === currentChapterId;
19553
- return /* @__PURE__ */ jsxRuntime.jsx(
19554
- Button,
19555
- {
19556
- variant: "ghost",
19557
- size: "sm",
19558
- action: "BOOK_NAVIGATE",
19559
- actionPayload: { chapterId: chapter.id },
19560
- className: cn(
19561
- "justify-start text-left w-full",
19562
- direction === "rtl" && "text-right",
19563
- isCurrent && "bg-blue-50 dark:bg-blue-950 text-blue-600 dark:text-blue-400"
19564
- ),
19565
- children: /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "truncate", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: chapter.title }) })
19566
- },
19567
- chapter.id
19568
- );
19569
- }) })
19570
- ] }, partIdx))
19633
+ parts.map((part, partIdx) => {
19634
+ const chapters = Array.isArray(part.chapters) ? part.chapters : [];
19635
+ return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
19636
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", align: "center", children: [
19637
+ /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", children: t("book.partNumber", { number: String(partIdx + 1) }) }),
19638
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h3", className: "font-semibold", children: String(part.title ?? "") })
19639
+ ] }),
19640
+ /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "xs", className: direction === "rtl" ? "pr-6" : "pl-6", children: chapters.map((chapter) => {
19641
+ const id = chapter.id == null ? "" : String(chapter.id);
19642
+ const isCurrent = id === currentChapterId;
19643
+ return /* @__PURE__ */ jsxRuntime.jsx(
19644
+ Button,
19645
+ {
19646
+ variant: "ghost",
19647
+ size: "sm",
19648
+ action: "BOOK_NAVIGATE",
19649
+ actionPayload: { chapterId: id },
19650
+ className: cn(
19651
+ "justify-start text-left w-full",
19652
+ direction === "rtl" && "text-right",
19653
+ isCurrent && "bg-blue-50 dark:bg-blue-950 text-blue-600 dark:text-blue-400"
19654
+ ),
19655
+ children: /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "truncate", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: String(chapter.title ?? "") }) })
19656
+ },
19657
+ id
19658
+ );
19659
+ }) })
19660
+ ] }, partIdx);
19661
+ })
19571
19662
  ]
19572
19663
  }
19573
19664
  );
@@ -19689,27 +19780,41 @@ function resolveFieldMap(fieldMap) {
19689
19780
  function get(obj, key) {
19690
19781
  return obj[key];
19691
19782
  }
19783
+ function asStr(v) {
19784
+ return v == null ? "" : String(v);
19785
+ }
19692
19786
  function mapBookData(raw, fields = IDENTITY_BOOK_FIELDS) {
19693
19787
  const rawParts = get(raw, fields.parts) ?? [];
19694
- return {
19695
- title: get(raw, fields.title) ?? "",
19696
- subtitle: get(raw, fields.subtitle),
19697
- author: get(raw, fields.author),
19698
- coverImageUrl: get(raw, fields.coverImageUrl),
19699
- direction: get(raw, fields.direction) ?? void 0,
19700
- parts: rawParts.map((part) => {
19701
- const rawChapters = get(part, fields.chapters) ?? [];
19702
- return {
19703
- title: get(part, fields.partTitle) ?? "",
19704
- chapters: rawChapters.map((ch) => ({
19705
- id: get(ch, fields.chapterId) ?? "",
19706
- title: get(ch, fields.chapterTitle) ?? "",
19707
- content: get(ch, fields.chapterContent) ?? "",
19708
- orbitalSchema: get(ch, fields.chapterOrbitalSchema)
19709
- }))
19710
- };
19711
- })
19788
+ const direction = get(raw, fields.direction) ?? "ltr";
19789
+ const cover = {
19790
+ title: asStr(get(raw, fields.title)),
19791
+ subtitle: asStr(get(raw, fields.subtitle)),
19792
+ author: asStr(get(raw, fields.author)),
19793
+ coverImageUrl: asStr(get(raw, fields.coverImageUrl)),
19794
+ direction
19712
19795
  };
19796
+ const schemaByChapterId = {};
19797
+ const chapters = [];
19798
+ const parts = rawParts.map((part) => {
19799
+ const rawChapters = get(part, fields.chapters) ?? [];
19800
+ const chapterRows = rawChapters.map((ch) => {
19801
+ const id = asStr(get(ch, fields.chapterId));
19802
+ const schema = get(ch, fields.chapterOrbitalSchema);
19803
+ if (schema) schemaByChapterId[id] = schema;
19804
+ const row = {
19805
+ id,
19806
+ title: asStr(get(ch, fields.chapterTitle)),
19807
+ content: asStr(get(ch, fields.chapterContent))
19808
+ };
19809
+ chapters.push(row);
19810
+ return row;
19811
+ });
19812
+ return {
19813
+ title: asStr(get(part, fields.partTitle)),
19814
+ chapters: chapterRows
19815
+ };
19816
+ });
19817
+ return { cover, direction, parts, chapters, schemaByChapterId };
19713
19818
  }
19714
19819
  var IDENTITY_BOOK_FIELDS, AR_BOOK_FIELDS, FIELD_MAP_REGISTRY;
19715
19820
  var init_types2 = __esm({
@@ -19747,10 +19852,7 @@ var init_types2 = __esm({
19747
19852
  };
19748
19853
  }
19749
19854
  });
19750
- function flattenChapters(book) {
19751
- return book.parts.flatMap((part) => part.chapters);
19752
- }
19753
- var PRINT_STYLES, BookViewer;
19855
+ var chapterId, chapterTitle, PRINT_STYLES, BookViewer;
19754
19856
  var init_BookViewer = __esm({
19755
19857
  "components/marketing/organisms/book/BookViewer.tsx"() {
19756
19858
  init_Box();
@@ -19763,6 +19865,8 @@ var init_BookViewer = __esm({
19763
19865
  init_BookNavBar();
19764
19866
  init_EmptyState();
19765
19867
  init_types2();
19868
+ chapterId = (ch) => ch?.id == null ? void 0 : String(ch.id);
19869
+ chapterTitle = (ch) => ch?.title == null ? void 0 : String(ch.title);
19766
19870
  PRINT_STYLES = `
19767
19871
  @media print {
19768
19872
  .book-viewer-page {
@@ -19791,14 +19895,14 @@ var init_BookViewer = __esm({
19791
19895
  return mapBookData(raw, resolvedFieldMap);
19792
19896
  }, [entity, resolvedFieldMap]);
19793
19897
  const direction = book?.direction ?? "ltr";
19794
- const chapters = React97.useMemo(() => book ? flattenChapters(book) : [], [book]);
19898
+ const chapters = React97.useMemo(() => book ? book.chapters : [], [book]);
19795
19899
  const totalPages = 2 + chapters.length;
19796
19900
  const navigateTo = React97.useCallback(
19797
19901
  (page) => {
19798
19902
  const clamped = Math.max(0, Math.min(page, totalPages - 1));
19799
19903
  setCurrentPage(clamped);
19800
- const chapterId = clamped >= 2 ? chapters[clamped - 2]?.id : void 0;
19801
- eventBus.emit("UI:BOOK_PAGE_CHANGE", { pageIndex: clamped, chapterId });
19904
+ const id = clamped >= 2 ? chapterId(chapters[clamped - 2]) : void 0;
19905
+ eventBus.emit("UI:BOOK_PAGE_CHANGE", { pageIndex: clamped, chapterId: id });
19802
19906
  },
19803
19907
  [totalPages, chapters, eventBus]
19804
19908
  );
@@ -19810,8 +19914,8 @@ var init_BookViewer = __esm({
19810
19914
  eventBus.on("UI:BOOK_PAGE_NEXT", () => navigateTo(currentPage + 1)),
19811
19915
  eventBus.on("UI:BOOK_PRINT", () => window.print()),
19812
19916
  eventBus.on("UI:BOOK_NAVIGATE", (event) => {
19813
- const chapterId = event.payload?.chapterId;
19814
- const idx = chapters.findIndex((ch) => ch.id === chapterId);
19917
+ const targetId = event.payload?.chapterId;
19918
+ const idx = chapters.findIndex((ch) => chapterId(ch) === targetId);
19815
19919
  if (idx >= 0) navigateTo(idx + 2);
19816
19920
  })
19817
19921
  ];
@@ -19828,9 +19932,11 @@ var init_BookViewer = __esm({
19828
19932
  style.remove();
19829
19933
  };
19830
19934
  }, []);
19831
- const currentChapterId = currentPage >= 2 ? chapters[currentPage - 2]?.id : void 0;
19832
- const currentChapterTitle = currentPage >= 2 ? chapters[currentPage - 2]?.title : void 0;
19935
+ const currentChapterId = currentPage >= 2 ? chapterId(chapters[currentPage - 2]) : void 0;
19936
+ const currentChapterTitle = currentPage >= 2 ? chapterTitle(chapters[currentPage - 2]) : void 0;
19833
19937
  if (!book) return /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { message: t("book.noData") });
19938
+ const cover = book.cover;
19939
+ const coverTitle = String(cover.title ?? "");
19834
19940
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("relative h-full overflow-hidden bg-background", className), children: [
19835
19941
  /* @__PURE__ */ jsxRuntime.jsxs(
19836
19942
  Box,
@@ -19842,10 +19948,10 @@ var init_BookViewer = __esm({
19842
19948
  /* @__PURE__ */ jsxRuntime.jsx(
19843
19949
  BookCoverPage,
19844
19950
  {
19845
- title: book.title,
19846
- subtitle: book.subtitle,
19847
- author: book.author,
19848
- coverImageUrl: book.coverImageUrl,
19951
+ title: coverTitle,
19952
+ subtitle: String(cover.subtitle ?? "") || void 0,
19953
+ author: String(cover.author ?? "") || void 0,
19954
+ coverImageUrl: String(cover.coverImageUrl ?? "") || void 0,
19849
19955
  direction
19850
19956
  }
19851
19957
  ),
@@ -19856,23 +19962,27 @@ var init_BookViewer = __esm({
19856
19962
  direction
19857
19963
  }
19858
19964
  ),
19859
- chapters.map((chapter) => /* @__PURE__ */ jsxRuntime.jsx(
19860
- BookChapterView,
19861
- {
19862
- chapter,
19863
- direction
19864
- },
19865
- chapter.id
19866
- ))
19965
+ chapters.map((chapter) => {
19966
+ const id = chapterId(chapter);
19967
+ return /* @__PURE__ */ jsxRuntime.jsx(
19968
+ BookChapterView,
19969
+ {
19970
+ chapter,
19971
+ orbitalSchema: id ? book.schemaByChapterId[id] : void 0,
19972
+ direction
19973
+ },
19974
+ id
19975
+ );
19976
+ })
19867
19977
  ] }),
19868
19978
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "print:hidden", children: [
19869
19979
  currentPage === 0 && /* @__PURE__ */ jsxRuntime.jsx(
19870
19980
  BookCoverPage,
19871
19981
  {
19872
- title: book.title,
19873
- subtitle: book.subtitle,
19874
- author: book.author,
19875
- coverImageUrl: book.coverImageUrl,
19982
+ title: coverTitle,
19983
+ subtitle: String(cover.subtitle ?? "") || void 0,
19984
+ author: String(cover.author ?? "") || void 0,
19985
+ coverImageUrl: String(cover.coverImageUrl ?? "") || void 0,
19876
19986
  direction
19877
19987
  }
19878
19988
  ),
@@ -19888,6 +19998,7 @@ var init_BookViewer = __esm({
19888
19998
  BookChapterView,
19889
19999
  {
19890
20000
  chapter: chapters[currentPage - 2],
20001
+ orbitalSchema: currentChapterId ? book.schemaByChapterId[currentChapterId] : void 0,
19891
20002
  direction
19892
20003
  }
19893
20004
  )
@@ -19900,7 +20011,7 @@ var init_BookViewer = __esm({
19900
20011
  {
19901
20012
  currentPage,
19902
20013
  totalPages,
19903
- chapterTitle: currentPage === 0 ? book.title : currentPage === 1 ? t("book.tableOfContents") : currentChapterTitle,
20014
+ chapterTitle: currentPage === 0 ? coverTitle : currentPage === 1 ? t("book.tableOfContents") : currentChapterTitle,
19904
20015
  direction
19905
20016
  }
19906
20017
  )
@@ -19998,7 +20109,7 @@ var init_Grid = __esm({
19998
20109
  };
19999
20110
  Grid = ({
20000
20111
  cols = 1,
20001
- rows,
20112
+ rows: rows2,
20002
20113
  gap = "md",
20003
20114
  rowGap,
20004
20115
  colGap,
@@ -20010,7 +20121,7 @@ var init_Grid = __esm({
20010
20121
  children,
20011
20122
  as: Component = "div"
20012
20123
  }) => {
20013
- const mergedStyle = rows ? { gridTemplateRows: `repeat(${rows}, minmax(0, 1fr))`, ...style } : style;
20124
+ const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
20014
20125
  return React97__namespace.default.createElement(
20015
20126
  Component,
20016
20127
  {
@@ -20726,14 +20837,14 @@ function BuilderBoard({
20726
20837
  }) {
20727
20838
  const { emit } = useEventBus();
20728
20839
  const { t } = hooks.useTranslate();
20729
- const resolved = Array.isArray(entity) ? entity[0] : entity;
20840
+ const resolved = boardEntity(entity);
20730
20841
  const [placements, setPlacements] = React97.useState({});
20731
20842
  const [headerError, setHeaderError] = React97.useState(false);
20732
20843
  const [submitted, setSubmitted] = React97.useState(false);
20733
20844
  const [attempts, setAttempts] = React97.useState(0);
20734
20845
  const [showHint, setShowHint] = React97.useState(false);
20735
- const components = resolved?.components ?? [];
20736
- const slots = resolved?.slots ?? [];
20846
+ const components = Array.isArray(resolved?.components) ? resolved.components : [];
20847
+ const slots = Array.isArray(resolved?.slots) ? resolved.slots : [];
20737
20848
  const usedComponentIds = new Set(Object.values(placements));
20738
20849
  const availableComponents = components.filter((c) => !usedComponentIds.has(c.id));
20739
20850
  const [selectedComponent, setSelectedComponent] = React97.useState(null);
@@ -20767,7 +20878,7 @@ function BuilderBoard({
20767
20878
  }, [slots, placements, attempts, completeEvent, emit]);
20768
20879
  const handleReset = () => {
20769
20880
  setSubmitted(false);
20770
- if (attempts >= 2 && resolved?.hint) {
20881
+ if (attempts >= 2 && str(resolved?.hint)) {
20771
20882
  setShowHint(true);
20772
20883
  }
20773
20884
  };
@@ -20780,20 +20891,24 @@ function BuilderBoard({
20780
20891
  };
20781
20892
  const getComponentById = (id) => components.find((c) => c.id === id);
20782
20893
  if (!resolved) return null;
20894
+ const theme = resolved.theme ?? void 0;
20895
+ const themeBackground = theme?.background;
20896
+ const headerImage = str(resolved.headerImage);
20897
+ const hint = str(resolved.hint);
20783
20898
  return /* @__PURE__ */ jsxRuntime.jsx(
20784
20899
  Box,
20785
20900
  {
20786
20901
  className,
20787
20902
  style: {
20788
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
20903
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
20789
20904
  backgroundSize: "cover",
20790
20905
  backgroundPosition: "center"
20791
20906
  },
20792
20907
  children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: "p-4", children: [
20793
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
20908
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
20794
20909
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
20795
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
20796
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.description })
20910
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
20911
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str(resolved.description) })
20797
20912
  ] }) }),
20798
20913
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
20799
20914
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("builder.components") }),
@@ -20853,9 +20968,9 @@ function BuilderBoard({
20853
20968
  ] }) }),
20854
20969
  submitted && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", children: [
20855
20970
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: allCorrect ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
20856
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("builder.success") : resolved.failMessage ?? t("builder.incorrect") })
20971
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("builder.success") : str(resolved.failMessage) || t("builder.incorrect") })
20857
20972
  ] }) }),
20858
- showHint && resolved.hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.hint }) }),
20973
+ showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
20859
20974
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
20860
20975
  !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
20861
20976
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Wrench, size: "sm" }),
@@ -20874,6 +20989,7 @@ var init_BuilderBoard = __esm({
20874
20989
  "components/game/organisms/puzzles/builder/BuilderBoard.tsx"() {
20875
20990
  init_atoms2();
20876
20991
  init_useEventBus();
20992
+ init_boardEntity();
20877
20993
  BuilderBoard.displayName = "BuilderBoard";
20878
20994
  }
20879
20995
  });
@@ -21211,21 +21327,24 @@ function CalendarGrid({
21211
21327
  eventBus.emit(`UI:${longPressEvent}`, { date: day.toISOString(), time, ...longPressPayload });
21212
21328
  }, 500);
21213
21329
  }, [longPressEvent, longPressPayload, eventBus]);
21214
- const renderEvent = (event) => /* @__PURE__ */ jsxRuntime.jsx(
21215
- Box,
21216
- {
21217
- rounded: "md",
21218
- padding: "xs",
21219
- border: true,
21220
- className: cn(
21221
- "cursor-pointer hover:shadow-sm transition-shadow text-xs truncate",
21222
- event.color ? event.color : "bg-blue-500/15 border-blue-500/30 text-blue-600"
21223
- ),
21224
- onClick: (e) => handleEventClick(event, e),
21225
- children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "truncate font-medium", children: event.title })
21226
- },
21227
- event.id
21228
- );
21330
+ const renderEvent = (event) => {
21331
+ const color = event.color;
21332
+ return /* @__PURE__ */ jsxRuntime.jsx(
21333
+ Box,
21334
+ {
21335
+ rounded: "md",
21336
+ padding: "xs",
21337
+ border: true,
21338
+ className: cn(
21339
+ "cursor-pointer hover:shadow-sm transition-shadow text-xs truncate",
21340
+ color ? color : "bg-blue-500/15 border-blue-500/30 text-blue-600"
21341
+ ),
21342
+ onClick: (e) => handleEventClick(event, e),
21343
+ children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "truncate font-medium", children: event.title })
21344
+ },
21345
+ event.id
21346
+ );
21347
+ };
21229
21348
  return /* @__PURE__ */ jsxRuntime.jsxs(
21230
21349
  Box,
21231
21350
  {
@@ -22889,7 +23008,6 @@ var init_CardGrid = __esm({
22889
23008
  alignItems = "stretch",
22890
23009
  className,
22891
23010
  children,
22892
- // EntityDisplayProps
22893
23011
  entity,
22894
23012
  isLoading = false,
22895
23013
  error = null,
@@ -23361,14 +23479,14 @@ var init_CaseStudyOrganism = __esm({
23361
23479
  /* @__PURE__ */ jsxRuntime.jsx(SimpleGrid, { cols: cols > 0 ? cols : 1, gap: "lg", children: items.map((study) => /* @__PURE__ */ jsxRuntime.jsx(
23362
23480
  CaseStudyCard,
23363
23481
  {
23364
- title: study.title,
23365
- description: study.description,
23366
- category: study.category,
23367
- categoryColor: study.categoryColor,
23368
- href: study.href,
23369
- linkLabel: study.linkLabel
23482
+ title: String(study.title ?? ""),
23483
+ description: String(study.description ?? ""),
23484
+ category: String(study.category ?? ""),
23485
+ categoryColor: study.categoryColor != null ? String(study.categoryColor) : void 0,
23486
+ href: String(study.href ?? ""),
23487
+ linkLabel: study.linkLabel != null ? String(study.linkLabel) : void 0
23370
23488
  },
23371
- study.id
23489
+ String(study.id ?? "")
23372
23490
  )) })
23373
23491
  ] });
23374
23492
  };
@@ -23391,10 +23509,10 @@ function CastleBoard({
23391
23509
  className
23392
23510
  }) {
23393
23511
  const eventBus = useEventBus();
23394
- const resolved = Array.isArray(entity) ? entity[0] : entity;
23395
- const tiles = resolved?.tiles ?? [];
23396
- const features = resolved?.features ?? [];
23397
- const units = resolved?.units ?? [];
23512
+ const resolved = boardEntity(entity);
23513
+ const tiles = Array.isArray(resolved?.tiles) ? resolved.tiles : [];
23514
+ const features = Array.isArray(resolved?.features) ? resolved.features : [];
23515
+ const units = Array.isArray(resolved?.units) ? resolved.units : [];
23398
23516
  const assetManifest = resolved?.assetManifest;
23399
23517
  const backgroundImage = resolved?.backgroundImage;
23400
23518
  const [hoveredTile, setHoveredTile] = React97.useState(null);
@@ -23422,7 +23540,7 @@ function CastleBoard({
23422
23540
  onFeatureClick?.(feature);
23423
23541
  if (featureClickEvent) {
23424
23542
  eventBus.emit(`UI:${featureClickEvent}`, {
23425
- featureId: feature.id,
23543
+ featureId: feature.id ?? "",
23426
23544
  featureType: feature.type,
23427
23545
  x: feature.x,
23428
23546
  y: feature.y
@@ -23490,6 +23608,7 @@ var init_CastleBoard = __esm({
23490
23608
  init_cn();
23491
23609
  init_useEventBus();
23492
23610
  init_IsometricCanvas2();
23611
+ init_boardEntity();
23493
23612
  init_isometric();
23494
23613
  CastleBoard.displayName = "CastleBoard";
23495
23614
  }
@@ -24300,14 +24419,14 @@ function ClassifierBoard({
24300
24419
  }) {
24301
24420
  const { emit } = useEventBus();
24302
24421
  const { t } = hooks.useTranslate();
24303
- const resolved = Array.isArray(entity) ? entity[0] : entity;
24422
+ const resolved = boardEntity(entity);
24304
24423
  const [assignments, setAssignments] = React97.useState({});
24305
24424
  const [headerError, setHeaderError] = React97.useState(false);
24306
24425
  const [submitted, setSubmitted] = React97.useState(false);
24307
24426
  const [attempts, setAttempts] = React97.useState(0);
24308
24427
  const [showHint, setShowHint] = React97.useState(false);
24309
- const items = resolved?.items ?? [];
24310
- const categories = resolved?.categories ?? [];
24428
+ const items = Array.isArray(resolved?.items) ? resolved.items : [];
24429
+ const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
24311
24430
  const unassignedItems = items.filter((item) => !assignments[item.id]);
24312
24431
  const allAssigned = Object.keys(assignments).length === items.length;
24313
24432
  const results = submitted ? items.map((item) => ({
@@ -24339,7 +24458,7 @@ function ClassifierBoard({
24339
24458
  }, [items, assignments, attempts, completeEvent, emit]);
24340
24459
  const handleReset = () => {
24341
24460
  setSubmitted(false);
24342
- if (attempts >= 2 && resolved?.hint) {
24461
+ if (attempts >= 2 && str(resolved?.hint)) {
24343
24462
  setShowHint(true);
24344
24463
  }
24345
24464
  };
@@ -24350,20 +24469,25 @@ function ClassifierBoard({
24350
24469
  setShowHint(false);
24351
24470
  };
24352
24471
  if (!resolved) return null;
24472
+ const theme = resolved.theme ?? void 0;
24473
+ const themeBackground = theme?.background;
24474
+ const headerImage = str(resolved.headerImage);
24475
+ const hint = str(resolved.hint);
24476
+ const failMessage = str(resolved.failMessage);
24353
24477
  return /* @__PURE__ */ jsxRuntime.jsx(
24354
24478
  Box,
24355
24479
  {
24356
24480
  className,
24357
24481
  style: {
24358
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
24482
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
24359
24483
  backgroundSize: "cover",
24360
24484
  backgroundPosition: "center"
24361
24485
  },
24362
24486
  children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: "p-4", children: [
24363
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
24487
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
24364
24488
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
24365
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
24366
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.description })
24489
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
24490
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str(resolved.description) })
24367
24491
  ] }) }),
24368
24492
  unassignedItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
24369
24493
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("classifier.itemsToSort") }),
@@ -24415,10 +24539,10 @@ function ClassifierBoard({
24415
24539
  }) }),
24416
24540
  submitted && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", children: [
24417
24541
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: allCorrect ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
24418
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("classifier.allCorrect") : `${correctCount}/${items.length} ${t("classifier.correct")}` }),
24419
- !allCorrect && resolved.failMessage && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: "text-muted-foreground", children: resolved.failMessage })
24542
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("classifier.allCorrect") : `${correctCount}/${items.length} ${t("classifier.correct")}` }),
24543
+ !allCorrect && failMessage && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: "text-muted-foreground", children: failMessage })
24420
24544
  ] }) }),
24421
- showHint && resolved.hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.hint }) }),
24545
+ showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
24422
24546
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
24423
24547
  !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
24424
24548
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Send, size: "sm" }),
@@ -24437,6 +24561,7 @@ var init_ClassifierBoard = __esm({
24437
24561
  "components/game/organisms/puzzles/classifier/ClassifierBoard.tsx"() {
24438
24562
  init_atoms2();
24439
24563
  init_useEventBus();
24564
+ init_boardEntity();
24440
24565
  ClassifierBoard.displayName = "ClassifierBoard";
24441
24566
  }
24442
24567
  });
@@ -30807,7 +30932,7 @@ function InventoryPanel({
30807
30932
  const slotArray = Array.from({ length: safeSlots }, (_, index) => {
30808
30933
  return safeItems[index] ?? null;
30809
30934
  });
30810
- const rows = Math.ceil(safeSlots / safeColumns);
30935
+ const rows2 = Math.ceil(safeSlots / safeColumns);
30811
30936
  const handleSlotClick = React97.useCallback((index) => {
30812
30937
  if (selectSlotEvent) eventBus.emit(`UI:${selectSlotEvent}`, { index });
30813
30938
  onSelectSlot?.(index);
@@ -30876,7 +31001,7 @@ function InventoryPanel({
30876
31001
  className: "grid gap-1 bg-[var(--color-card)] p-2 rounded-container border border-border",
30877
31002
  style: {
30878
31003
  gridTemplateColumns: `repeat(${safeColumns}, ${slotSize}px)`,
30879
- gridTemplateRows: `repeat(${rows}, ${slotSize}px)`
31004
+ gridTemplateRows: `repeat(${rows2}, ${slotSize}px)`
30880
31005
  },
30881
31006
  children: slotArray.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
30882
31007
  "button",
@@ -34840,11 +34965,11 @@ function LatticeSVG({
34840
34965
  }) {
34841
34966
  const paths = [];
34842
34967
  const cols = 5;
34843
- const rows = Math.ceil(h / (w / cols));
34968
+ const rows2 = Math.ceil(h / (w / cols));
34844
34969
  const cellW = w / cols;
34845
34970
  const cellH = cellW;
34846
34971
  const bulge = cellW * 0.3;
34847
- for (let row = 0; row < rows; row++) {
34972
+ for (let row = 0; row < rows2; row++) {
34848
34973
  for (let col = 0; col < cols; col++) {
34849
34974
  const cx = col * cellW + cellW / 2;
34850
34975
  const cy = row * cellH + cellH / 2;
@@ -35256,7 +35381,7 @@ var init_MatrixQuestion = __esm({
35256
35381
  };
35257
35382
  MatrixQuestion = ({
35258
35383
  title,
35259
- rows,
35384
+ rows: rows2,
35260
35385
  columns = DEFAULT_MATRIX_COLUMNS,
35261
35386
  values,
35262
35387
  onChange,
@@ -35266,7 +35391,7 @@ var init_MatrixQuestion = __esm({
35266
35391
  className
35267
35392
  }) => {
35268
35393
  const styles = sizeStyles13[size];
35269
- const safeRows = rows ?? [];
35394
+ const safeRows = rows2 ?? [];
35270
35395
  const safeValues = values ?? {};
35271
35396
  const eventBus = useEventBus();
35272
35397
  const handleChange = React97.useCallback(
@@ -35894,7 +36019,8 @@ var init_PositionedCanvas = __esm({
35894
36019
  dragRef.current = null;
35895
36020
  setDraggingId(null);
35896
36021
  if (!wasDrag) {
35897
- const next = selectedId === item.id ? null : item.id;
36022
+ const itemId = item.id;
36023
+ const next = selectedId === itemId ? null : itemId;
35898
36024
  onSelect?.(next);
35899
36025
  if (selectEvent) {
35900
36026
  eventBus.emit(`UI:${selectEvent}`, { id: next });
@@ -35929,15 +36055,22 @@ var init_PositionedCanvas = __esm({
35929
36055
  style: { width, height },
35930
36056
  onClick: handleContainerClick,
35931
36057
  children: items.map((item) => {
36058
+ const itemId = item.id;
36059
+ const label = item.label;
36060
+ const x = item.x;
36061
+ const y = item.y;
36062
+ const capacity = item.capacity;
36063
+ const partySize = item.partySize;
36064
+ const serverName = item.serverName;
35932
36065
  const status = item.status ?? "empty";
35933
36066
  const shape = item.shape ?? "round";
35934
- const isSelected = selectedId === item.id;
35935
- const isDragging = draggingId === item.id;
36067
+ const isSelected = selectedId === itemId;
36068
+ const isDragging = draggingId === itemId;
35936
36069
  const statusBadge = STATUS_BADGE[status];
35937
36070
  return /* @__PURE__ */ jsxRuntime.jsxs(
35938
36071
  Box,
35939
36072
  {
35940
- "data-testid": `item-node-${item.id}`,
36073
+ "data-testid": `item-node-${itemId}`,
35941
36074
  "data-status": status,
35942
36075
  className: cn(
35943
36076
  "absolute flex flex-col items-center justify-center gap-1 border-2 select-none",
@@ -35948,7 +36081,7 @@ var init_PositionedCanvas = __esm({
35948
36081
  isSelected && "outline outline-2 outline-offset-2 outline-primary shadow-md",
35949
36082
  isDragging && "shadow-lg z-10"
35950
36083
  ),
35951
- style: { left: item.x, top: item.y, touchAction: "none" },
36084
+ style: { left: x, top: y, touchAction: "none" },
35952
36085
  onPointerDown: (e) => handlePointerDown(e, item),
35953
36086
  onPointerMove: handlePointerMove,
35954
36087
  onPointerUp: (e) => handlePointerUp(e, item),
@@ -35956,10 +36089,10 @@ var init_PositionedCanvas = __esm({
35956
36089
  children: [
35957
36090
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-1", children: [
35958
36091
  getStatusIcon(status),
35959
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", weight: "semibold", children: item.label })
36092
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", weight: "semibold", children: label })
35960
36093
  ] }),
35961
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", children: item.partySize !== void 0 && status === "seated" ? `${item.partySize}/${item.capacity}` : `Cap ${item.capacity}` }),
35962
- status === "seated" && item.serverName && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", className: "truncate max-w-[80%]", children: item.serverName }),
36094
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", children: partySize !== void 0 && status === "seated" ? `${partySize}/${capacity}` : `Cap ${capacity}` }),
36095
+ status === "seated" && serverName && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", className: "truncate max-w-[80%]", children: serverName }),
35963
36096
  isSelected && /* @__PURE__ */ jsxRuntime.jsx(
35964
36097
  Badge,
35965
36098
  {
@@ -35971,7 +36104,7 @@ var init_PositionedCanvas = __esm({
35971
36104
  )
35972
36105
  ]
35973
36106
  },
35974
- item.id
36107
+ itemId
35975
36108
  );
35976
36109
  })
35977
36110
  }
@@ -36743,9 +36876,10 @@ var init_RichBlockEditor = __esm({
36743
36876
  });
36744
36877
  function collectInitiallyCollapsed(nodes, acc) {
36745
36878
  for (const n of nodes) {
36879
+ const replies = n.replies;
36746
36880
  if (n.collapsed) acc.add(n.id);
36747
- if (n.replies && n.replies.length > 0) {
36748
- collectInitiallyCollapsed(n.replies, acc);
36881
+ if (replies && replies.length > 0) {
36882
+ collectInitiallyCollapsed(replies, acc);
36749
36883
  }
36750
36884
  }
36751
36885
  }
@@ -36775,44 +36909,52 @@ var init_ReplyTree = __esm({
36775
36909
  }) => {
36776
36910
  const eventBus = useEventBus();
36777
36911
  const { t } = hooks.useTranslate();
36778
- const hasReplies = !!node.replies && node.replies.length > 0;
36779
- const isCollapsed = collapsedSet.has(node.id);
36912
+ const nodeId = node.id;
36913
+ const authorName = node.authorName;
36914
+ const authorAvatarUrl = node.authorAvatarUrl;
36915
+ const content = node.content;
36916
+ const postedAt = node.postedAt;
36917
+ const voteCount = node.voteCount;
36918
+ const userVote = node.userVote;
36919
+ const replies = node.replies;
36920
+ const hasReplies = !!replies && replies.length > 0;
36921
+ const isCollapsed = collapsedSet.has(nodeId);
36780
36922
  const atMaxDepth = depth >= maxDepth;
36781
36923
  const [replyOpen, setReplyOpen] = React97.useState(false);
36782
36924
  const [draft, setDraft] = React97.useState("");
36783
36925
  const handleVote = React97.useCallback(
36784
36926
  (next) => {
36785
- onVote?.(node.id, next);
36786
- if (voteEvent) eventBus.emit(`UI:${voteEvent}`, { nodeId: node.id, vote: next });
36927
+ onVote?.(nodeId, next);
36928
+ if (voteEvent) eventBus.emit(`UI:${voteEvent}`, { nodeId, vote: next });
36787
36929
  },
36788
- [node.id, onVote, voteEvent, eventBus]
36930
+ [nodeId, onVote, voteEvent, eventBus]
36789
36931
  );
36790
36932
  const handleReply = React97.useCallback(() => {
36791
- onReply?.(node.id);
36933
+ onReply?.(nodeId);
36792
36934
  setReplyOpen((open) => !open);
36793
- }, [node.id, onReply]);
36935
+ }, [nodeId, onReply]);
36794
36936
  const handleSubmitReply = React97.useCallback(() => {
36795
- const content = draft.trim();
36796
- if (!content) return;
36797
- if (replyEvent) eventBus.emit(`UI:${replyEvent}`, { parentNodeId: node.id, content });
36937
+ const text = draft.trim();
36938
+ if (!text) return;
36939
+ if (replyEvent) eventBus.emit(`UI:${replyEvent}`, { parentNodeId: nodeId, content: text });
36798
36940
  setDraft("");
36799
36941
  setReplyOpen(false);
36800
- }, [node.id, draft, replyEvent, eventBus]);
36942
+ }, [nodeId, draft, replyEvent, eventBus]);
36801
36943
  const handleCancelReply = React97.useCallback(() => {
36802
36944
  setDraft("");
36803
36945
  setReplyOpen(false);
36804
36946
  }, []);
36805
36947
  const handleFlag = React97.useCallback(() => {
36806
- onFlag?.(node.id);
36807
- if (flagEvent) eventBus.emit(`UI:${flagEvent}`, { nodeId: node.id });
36808
- }, [node.id, onFlag, flagEvent, eventBus]);
36948
+ onFlag?.(nodeId);
36949
+ if (flagEvent) eventBus.emit(`UI:${flagEvent}`, { nodeId });
36950
+ }, [nodeId, onFlag, flagEvent, eventBus]);
36809
36951
  const handleContinue = React97.useCallback(() => {
36810
- onContinueThread?.(node.id);
36811
- if (continueThreadEvent) eventBus.emit(`UI:${continueThreadEvent}`, { nodeId: node.id });
36812
- }, [node.id, onContinueThread, continueThreadEvent, eventBus]);
36952
+ onContinueThread?.(nodeId);
36953
+ if (continueThreadEvent) eventBus.emit(`UI:${continueThreadEvent}`, { nodeId });
36954
+ }, [nodeId, onContinueThread, continueThreadEvent, eventBus]);
36813
36955
  const handleToggle = React97.useCallback(() => {
36814
- toggleCollapse(node.id);
36815
- }, [node.id, toggleCollapse]);
36956
+ toggleCollapse(nodeId);
36957
+ }, [nodeId, toggleCollapse]);
36816
36958
  return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex flex-row gap-2 items-stretch min-w-0", children: [
36817
36959
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex flex-col items-center flex-shrink-0 w-6", children: [
36818
36960
  hasReplies ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -36844,25 +36986,25 @@ var init_ReplyTree = __esm({
36844
36986
  /* @__PURE__ */ jsxRuntime.jsx(
36845
36987
  Avatar,
36846
36988
  {
36847
- src: node.authorAvatarUrl,
36848
- name: node.authorName,
36989
+ src: authorAvatarUrl,
36990
+ name: authorName,
36849
36991
  size: "sm"
36850
36992
  }
36851
36993
  ),
36852
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "semibold", children: node.authorName }),
36853
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", children: node.postedAt })
36994
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "semibold", children: authorName }),
36995
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", children: postedAt })
36854
36996
  ] }),
36855
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: "whitespace-pre-wrap break-words", children: node.content }),
36997
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: "whitespace-pre-wrap break-words", children: content }),
36856
36998
  showActions && /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex flex-row gap-2 items-center", children: [
36857
36999
  /* @__PURE__ */ jsxRuntime.jsx(
36858
37000
  VoteStack,
36859
37001
  {
36860
- count: node.voteCount ?? 0,
36861
- userVote: node.userVote ?? null,
37002
+ count: voteCount ?? 0,
37003
+ userVote: userVote ?? null,
36862
37004
  onVote: handleVote,
36863
37005
  size: "sm",
36864
37006
  variant: "horizontal",
36865
- label: t("replyTree.voteOnReplyBy", { author: node.authorName })
37007
+ label: t("replyTree.voteOnReplyBy", { author: authorName })
36866
37008
  }
36867
37009
  ),
36868
37010
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -36872,7 +37014,7 @@ var init_ReplyTree = __esm({
36872
37014
  size: "sm",
36873
37015
  leftIcon: "message-square",
36874
37016
  onClick: handleReply,
36875
- "aria-label": t("replyTree.replyTo", { author: node.authorName }),
37017
+ "aria-label": t("replyTree.replyTo", { author: authorName }),
36876
37018
  children: t("replyTree.reply")
36877
37019
  }
36878
37020
  ),
@@ -36883,7 +37025,7 @@ var init_ReplyTree = __esm({
36883
37025
  size: "sm",
36884
37026
  leftIcon: "flag",
36885
37027
  onClick: handleFlag,
36886
- "aria-label": t("replyTree.flagReplyBy", { author: node.authorName }),
37028
+ "aria-label": t("replyTree.flagReplyBy", { author: authorName }),
36887
37029
  children: t("replyTree.flag")
36888
37030
  }
36889
37031
  )
@@ -36895,9 +37037,9 @@ var init_ReplyTree = __esm({
36895
37037
  inputType: "textarea",
36896
37038
  rows: 2,
36897
37039
  value: draft,
36898
- placeholder: t("replyTree.replyToPlaceholder", { author: node.authorName }),
37040
+ placeholder: t("replyTree.replyToPlaceholder", { author: authorName }),
36899
37041
  onChange: (e) => setDraft(e.target.value),
36900
- "aria-label": t("replyTree.replyTo", { author: node.authorName })
37042
+ "aria-label": t("replyTree.replyTo", { author: authorName })
36901
37043
  }
36902
37044
  ),
36903
37045
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex flex-row gap-2 items-center", children: [
@@ -36928,7 +37070,7 @@ var init_ReplyTree = __esm({
36928
37070
  ),
36929
37071
  children: t("replyTree.continueThread")
36930
37072
  }
36931
- ) : /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex flex-col gap-2 mt-1", children: node.replies.map((child) => /* @__PURE__ */ jsxRuntime.jsx(
37073
+ ) : /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex flex-col gap-2 mt-1", children: replies.map((child) => /* @__PURE__ */ jsxRuntime.jsx(
36932
37074
  ReplyTreeNode,
36933
37075
  {
36934
37076
  node: child,
@@ -38839,8 +38981,8 @@ var init_WizardContainer = __esm({
38839
38981
  return void 0;
38840
38982
  if (typeof controlledStep === "number") return controlledStep;
38841
38983
  if (typeof controlledStep === "string") return parseInt(controlledStep, 10);
38842
- const num = Number(controlledStep);
38843
- return isNaN(num) ? void 0 : num;
38984
+ const num2 = Number(controlledStep);
38985
+ return isNaN(num2) ? void 0 : num2;
38844
38986
  })();
38845
38987
  const currentStep = normalizedControlledStep !== void 0 ? normalizedControlledStep : internalStep;
38846
38988
  const totalSteps = steps.length;
@@ -40545,7 +40687,7 @@ function DebuggerBoard({
40545
40687
  }) {
40546
40688
  const { emit } = useEventBus();
40547
40689
  const { t } = hooks.useTranslate();
40548
- const resolved = Array.isArray(entity) ? entity[0] : entity;
40690
+ const resolved = boardEntity(entity);
40549
40691
  const [flaggedLines, setFlaggedLines] = React97.useState(/* @__PURE__ */ new Set());
40550
40692
  const [headerError, setHeaderError] = React97.useState(false);
40551
40693
  const [submitted, setSubmitted] = React97.useState(false);
@@ -40563,7 +40705,7 @@ function DebuggerBoard({
40563
40705
  return next;
40564
40706
  });
40565
40707
  };
40566
- const lines = resolved?.lines ?? [];
40708
+ const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
40567
40709
  const bugLines = lines.filter((l) => l.isBug);
40568
40710
  const correctFlags = lines.filter((l) => l.isBug && flaggedLines.has(l.id));
40569
40711
  const falseFlags = lines.filter((l) => !l.isBug && flaggedLines.has(l.id));
@@ -40578,7 +40720,7 @@ function DebuggerBoard({
40578
40720
  }, [correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
40579
40721
  const handleReset = () => {
40580
40722
  setSubmitted(false);
40581
- if (attempts >= 2 && resolved?.hint) {
40723
+ if (attempts >= 2 && str(resolved?.hint)) {
40582
40724
  setShowHint(true);
40583
40725
  }
40584
40726
  };
@@ -40589,24 +40731,28 @@ function DebuggerBoard({
40589
40731
  setShowHint(false);
40590
40732
  };
40591
40733
  if (!resolved) return null;
40734
+ const theme = resolved.theme ?? void 0;
40735
+ const themeBackground = theme?.background;
40736
+ const headerImage = str(resolved.headerImage);
40737
+ const hint = str(resolved.hint);
40592
40738
  return /* @__PURE__ */ jsxRuntime.jsx(
40593
40739
  Box,
40594
40740
  {
40595
40741
  className,
40596
40742
  style: {
40597
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
40743
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
40598
40744
  backgroundSize: "cover",
40599
40745
  backgroundPosition: "center"
40600
40746
  },
40601
40747
  children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: "p-4", children: [
40602
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
40748
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
40603
40749
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
40604
40750
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
40605
40751
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Bug, size: "sm" }),
40606
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title })
40752
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) })
40607
40753
  ] }),
40608
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.description }),
40609
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(resolved.bugCount) }) })
40754
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str(resolved.description) }),
40755
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(num(resolved.bugCount)) }) })
40610
40756
  ] }) }),
40611
40757
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "none", children: lines.map((line, i) => {
40612
40758
  const isFlagged = flaggedLines.has(line.id);
@@ -40638,7 +40784,7 @@ function DebuggerBoard({
40638
40784
  );
40639
40785
  }) }) }),
40640
40786
  submitted && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
40641
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("debugger.allFound") : `${correctFlags.length}/${bugLines.length} ${t("debugger.bugsFound")}` }),
40787
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("debugger.allFound") : `${correctFlags.length}/${bugLines.length} ${t("debugger.bugsFound")}` }),
40642
40788
  bugLines.map((line) => /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "start", children: [
40643
40789
  /* @__PURE__ */ jsxRuntime.jsx(
40644
40790
  Icon,
@@ -40654,7 +40800,7 @@ function DebuggerBoard({
40654
40800
  ] })
40655
40801
  ] }, line.id))
40656
40802
  ] }) }),
40657
- showHint && resolved.hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.hint }) }),
40803
+ showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
40658
40804
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
40659
40805
  !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.size === 0, children: [
40660
40806
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Send, size: "sm" }),
@@ -40673,6 +40819,7 @@ var init_DebuggerBoard = __esm({
40673
40819
  "components/game/organisms/puzzles/debugger/DebuggerBoard.tsx"() {
40674
40820
  init_atoms2();
40675
40821
  init_useEventBus();
40822
+ init_boardEntity();
40676
40823
  DebuggerBoard.displayName = "DebuggerBoard";
40677
40824
  }
40678
40825
  });
@@ -40710,7 +40857,7 @@ function getBadgeVariant(fieldName, value) {
40710
40857
  return "default";
40711
40858
  }
40712
40859
  function formatFieldLabel(fieldName) {
40713
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase());
40860
+ return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str2) => str2.toUpperCase());
40714
40861
  }
40715
40862
  function formatFieldValue(value, fieldName) {
40716
40863
  if (typeof value === "number") {
@@ -40729,26 +40876,26 @@ function formatFieldValue(value, fieldName) {
40729
40876
  }
40730
40877
  function renderRichFieldValue(value, fieldName, fieldType) {
40731
40878
  if (value === void 0 || value === null) return "\u2014";
40732
- const str = String(value);
40879
+ const str2 = String(value);
40733
40880
  switch (fieldType) {
40734
40881
  case "image":
40735
40882
  case "url": {
40736
- if (str.match(/\.(png|jpe?g|gif|svg|webp|avif)(\?|$)/i) || str.startsWith("data:image/")) {
40883
+ if (str2.match(/\.(png|jpe?g|gif|svg|webp|avif)(\?|$)/i) || str2.startsWith("data:image/")) {
40737
40884
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mt-1 max-w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
40738
40885
  "img",
40739
40886
  {
40740
- src: str,
40887
+ src: str2,
40741
40888
  alt: formatFieldLabel(fieldName),
40742
40889
  className: "max-w-full max-h-64 rounded-md object-contain",
40743
40890
  loading: "lazy"
40744
40891
  }
40745
40892
  ) });
40746
40893
  }
40747
- return str;
40894
+ return str2;
40748
40895
  }
40749
40896
  case "markdown":
40750
40897
  case "richtext":
40751
- return /* @__PURE__ */ jsxRuntime.jsx(React97.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: "break-words", children: str }), children: /* @__PURE__ */ jsxRuntime.jsx(
40898
+ return /* @__PURE__ */ jsxRuntime.jsx(React97.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: "break-words", children: str2 }), children: /* @__PURE__ */ jsxRuntime.jsx(
40752
40899
  Box,
40753
40900
  {
40754
40901
  className: "prose prose-sm max-w-none",
@@ -40766,11 +40913,11 @@ function renderRichFieldValue(value, fieldName, fieldType) {
40766
40913
  "--tw-prose-th-borders": "var(--color-border)",
40767
40914
  "--tw-prose-td-borders": "var(--color-border)"
40768
40915
  },
40769
- children: /* @__PURE__ */ jsxRuntime.jsx(ReactMarkdown2, { children: str })
40916
+ children: /* @__PURE__ */ jsxRuntime.jsx(ReactMarkdown2, { children: str2 })
40770
40917
  }
40771
40918
  ) });
40772
40919
  case "code":
40773
- return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mt-1 rounded-md bg-muted p-3 overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "text-sm font-mono whitespace-pre-wrap break-words m-0", children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: str }) }) });
40920
+ return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mt-1 rounded-md bg-muted p-3 overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "text-sm font-mono whitespace-pre-wrap break-words m-0", children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: str2 }) }) });
40774
40921
  case "html":
40775
40922
  return /* @__PURE__ */ jsxRuntime.jsx(
40776
40923
  Box,
@@ -40790,12 +40937,12 @@ function renderRichFieldValue(value, fieldName, fieldType) {
40790
40937
  "--tw-prose-th-borders": "var(--color-border)",
40791
40938
  "--tw-prose-td-borders": "var(--color-border)"
40792
40939
  },
40793
- children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str })
40940
+ children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str2 })
40794
40941
  }
40795
40942
  );
40796
40943
  case "date":
40797
40944
  case "datetime": {
40798
- const d = new Date(str);
40945
+ const d = new Date(str2);
40799
40946
  if (!isNaN(d.getTime())) {
40800
40947
  return d.toLocaleDateString(void 0, {
40801
40948
  year: "numeric",
@@ -40804,7 +40951,7 @@ function renderRichFieldValue(value, fieldName, fieldType) {
40804
40951
  ...fieldType === "datetime" ? { hour: "2-digit", minute: "2-digit" } : {}
40805
40952
  });
40806
40953
  }
40807
- return str;
40954
+ return str2;
40808
40955
  }
40809
40956
  default:
40810
40957
  return formatFieldValue(value, fieldName);
@@ -41482,6 +41629,40 @@ var init_RuleEditor = __esm({
41482
41629
  RuleEditor.displayName = "RuleEditor";
41483
41630
  }
41484
41631
  });
41632
+
41633
+ // components/game/organisms/puzzles/event-handler/puzzleObject.ts
41634
+ function objId(o) {
41635
+ return o.id == null ? "" : String(o.id);
41636
+ }
41637
+ function objName(o) {
41638
+ return o.name == null ? "" : String(o.name);
41639
+ }
41640
+ function objIcon(o) {
41641
+ return o.icon == null ? "" : String(o.icon);
41642
+ }
41643
+ function objStates(o) {
41644
+ return Array.isArray(o.states) ? o.states : [];
41645
+ }
41646
+ function objCurrentState(o) {
41647
+ return o.currentState == null ? "" : String(o.currentState);
41648
+ }
41649
+ function objAvailableEvents(o) {
41650
+ return Array.isArray(o.availableEvents) ? o.availableEvents : [];
41651
+ }
41652
+ function objAvailableActions(o) {
41653
+ return Array.isArray(o.availableActions) ? o.availableActions : [];
41654
+ }
41655
+ function objRules(o) {
41656
+ return Array.isArray(o.rules) ? o.rules : [];
41657
+ }
41658
+ function objMaxRules(o) {
41659
+ const n = Number(o.maxRules);
41660
+ return Number.isFinite(n) && n > 0 ? n : 3;
41661
+ }
41662
+ var init_puzzleObject = __esm({
41663
+ "components/game/organisms/puzzles/event-handler/puzzleObject.ts"() {
41664
+ }
41665
+ });
41485
41666
  function ObjectRulePanel({
41486
41667
  object,
41487
41668
  onRulesChange,
@@ -41489,55 +41670,63 @@ function ObjectRulePanel({
41489
41670
  className
41490
41671
  }) {
41491
41672
  const { t } = hooks.useTranslate();
41492
- const maxRules = object.maxRules || 3;
41493
- const canAdd = object.rules.length < maxRules;
41673
+ const id = objId(object);
41674
+ const name = objName(object);
41675
+ const icon = objIcon(object);
41676
+ const states = objStates(object);
41677
+ const currentState = objCurrentState(object);
41678
+ const availableEvents = objAvailableEvents(object);
41679
+ const availableActions = objAvailableActions(object);
41680
+ const rules = objRules(object);
41681
+ const maxRules = objMaxRules(object);
41682
+ const canAdd = rules.length < maxRules;
41494
41683
  const handleRuleChange = React97.useCallback((index, updatedRule) => {
41495
- const newRules = [...object.rules];
41684
+ const newRules = [...rules];
41496
41685
  newRules[index] = updatedRule;
41497
- onRulesChange(object.id, newRules);
41498
- }, [object.id, object.rules, onRulesChange]);
41686
+ onRulesChange(id, newRules);
41687
+ }, [id, rules, onRulesChange]);
41499
41688
  const handleRuleRemove = React97.useCallback((index) => {
41500
- const newRules = object.rules.filter((_, i) => i !== index);
41501
- onRulesChange(object.id, newRules);
41502
- }, [object.id, object.rules, onRulesChange]);
41689
+ const newRules = rules.filter((_, i) => i !== index);
41690
+ onRulesChange(id, newRules);
41691
+ }, [id, rules, onRulesChange]);
41503
41692
  const handleAddRule = React97.useCallback(() => {
41504
41693
  if (!canAdd || disabled) return;
41505
- const firstEvent = object.availableEvents[0]?.value || "";
41506
- const firstAction = object.availableActions[0]?.value || "";
41694
+ const firstEvent = availableEvents[0]?.value || "";
41695
+ const firstAction = availableActions[0]?.value || "";
41507
41696
  const newRule = {
41508
41697
  id: `rule-${nextRuleId++}`,
41509
41698
  whenEvent: firstEvent,
41510
41699
  thenAction: firstAction
41511
41700
  };
41512
- onRulesChange(object.id, [...object.rules, newRule]);
41513
- }, [canAdd, disabled, object, onRulesChange]);
41701
+ onRulesChange(id, [...rules, newRule]);
41702
+ }, [canAdd, disabled, id, rules, availableEvents, availableActions, onRulesChange]);
41514
41703
  const machine = {
41515
- name: object.name,
41516
- states: object.states,
41517
- currentState: object.currentState,
41518
- transitions: object.rules.map((r2) => ({
41519
- from: object.currentState,
41520
- to: object.states.find((s) => s !== object.currentState) || object.currentState,
41704
+ name,
41705
+ states,
41706
+ currentState,
41707
+ transitions: rules.map((r2) => ({
41708
+ from: currentState,
41709
+ to: states.find((s) => s !== currentState) || currentState,
41521
41710
  event: r2.whenEvent
41522
41711
  }))
41523
41712
  };
41524
41713
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("p-4 rounded-lg bg-card border border-border", className), gap: "sm", children: [
41525
41714
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center", gap: "sm", children: [
41526
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", children: object.icon }),
41715
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", children: icon }),
41527
41716
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", children: [
41528
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-foreground font-bold", children: object.name }),
41529
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("eventHandler.state") + ": " + object.currentState })
41717
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-foreground font-bold", children: name }),
41718
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("eventHandler.state") + ": " + currentState })
41530
41719
  ] })
41531
41720
  ] }),
41532
41721
  /* @__PURE__ */ jsxRuntime.jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" }),
41533
41722
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
41534
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.rules", { count: object.rules.length, max: maxRules }) + ":" }),
41535
- object.rules.map((rule, i) => /* @__PURE__ */ jsxRuntime.jsx(
41723
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.rules", { count: rules.length, max: maxRules }) + ":" }),
41724
+ rules.map((rule, i) => /* @__PURE__ */ jsxRuntime.jsx(
41536
41725
  RuleEditor,
41537
41726
  {
41538
41727
  rule,
41539
- availableEvents: object.availableEvents,
41540
- availableActions: object.availableActions,
41728
+ availableEvents,
41729
+ availableActions,
41541
41730
  onChange: (r2) => handleRuleChange(i, r2),
41542
41731
  onRemove: () => handleRuleRemove(i),
41543
41732
  disabled
@@ -41555,6 +41744,7 @@ var init_ObjectRulePanel = __esm({
41555
41744
  init_cn();
41556
41745
  init_TraitStateViewer();
41557
41746
  init_RuleEditor();
41747
+ init_puzzleObject();
41558
41748
  nextRuleId = 1;
41559
41749
  ObjectRulePanel.displayName = "ObjectRulePanel";
41560
41750
  }
@@ -41621,11 +41811,11 @@ function EventHandlerBoard({
41621
41811
  }) {
41622
41812
  const { emit } = useEventBus();
41623
41813
  const { t } = hooks.useTranslate();
41624
- const resolved = Array.isArray(entity) ? entity[0] : entity;
41625
- const entityObjects = resolved?.objects ?? [];
41626
- const [objects, setObjects] = React97.useState(entityObjects);
41814
+ const resolved = boardEntity(entity);
41815
+ const entityObjects = rows(resolved?.objects);
41816
+ const [objects, setObjects] = React97.useState(() => [...entityObjects]);
41627
41817
  const [selectedObjectId, setSelectedObjectId] = React97.useState(
41628
- entityObjects[0]?.id || null
41818
+ entityObjects[0] ? objId(entityObjects[0]) : null
41629
41819
  );
41630
41820
  const [headerError, setHeaderError] = React97.useState(false);
41631
41821
  const [playState, setPlayState] = React97.useState("editing");
@@ -41636,10 +41826,10 @@ function EventHandlerBoard({
41636
41826
  React97.useEffect(() => () => {
41637
41827
  if (timerRef.current) clearTimeout(timerRef.current);
41638
41828
  }, []);
41639
- const selectedObject = objects.find((o) => o.id === selectedObjectId) || null;
41829
+ const selectedObject = objects.find((o) => objId(o) === selectedObjectId) || null;
41640
41830
  const handleRulesChange = React97.useCallback((objectId, rules) => {
41641
41831
  setObjects((prev) => prev.map(
41642
- (o) => o.id === objectId ? { ...o, rules } : o
41832
+ (o) => objId(o) === objectId ? { ...o, rules } : o
41643
41833
  ));
41644
41834
  }, []);
41645
41835
  const addLogEntry = React97.useCallback((icon, message, status = "done") => {
@@ -41653,11 +41843,12 @@ function EventHandlerBoard({
41653
41843
  setEventLog([]);
41654
41844
  const allRules = [];
41655
41845
  objects.forEach((obj) => {
41656
- obj.rules.forEach((rule) => {
41846
+ objRules(obj).forEach((rule) => {
41657
41847
  allRules.push({ object: obj, rule });
41658
41848
  });
41659
41849
  });
41660
- const triggers = resolved?.triggerEvents || [];
41850
+ const triggers = Array.isArray(resolved?.triggerEvents) ? resolved.triggerEvents : [];
41851
+ const goalEvent = str(resolved?.goalEvent);
41661
41852
  const eventQueue = [...triggers];
41662
41853
  const firedEvents = /* @__PURE__ */ new Set();
41663
41854
  let stepIdx = 0;
@@ -41686,14 +41877,14 @@ function EventHandlerBoard({
41686
41877
  addLogEntry("\u26A1", t("eventHandler.noListeners", { event: currentEvent }), "done");
41687
41878
  } else {
41688
41879
  matching.forEach(({ object, rule }) => {
41689
- addLogEntry(object.icon, t("eventHandler.heardEvent", { object: object.name, event: currentEvent, action: rule.thenAction }), "done");
41880
+ addLogEntry(objIcon(object), t("eventHandler.heardEvent", { object: objName(object), event: currentEvent, action: rule.thenAction }), "done");
41690
41881
  eventQueue.push(rule.thenAction);
41691
- if (rule.thenAction === resolved?.goalEvent) {
41882
+ if (rule.thenAction === goalEvent) {
41692
41883
  goalReached = true;
41693
41884
  }
41694
41885
  });
41695
41886
  }
41696
- if (currentEvent === resolved?.goalEvent) {
41887
+ if (currentEvent === goalEvent) {
41697
41888
  goalReached = true;
41698
41889
  }
41699
41890
  stepIdx++;
@@ -41711,65 +41902,75 @@ function EventHandlerBoard({
41711
41902
  }, []);
41712
41903
  const handleReset = React97.useCallback(() => {
41713
41904
  if (timerRef.current) clearTimeout(timerRef.current);
41714
- setObjects(resolved?.objects ?? []);
41905
+ const resetObjects = rows(resolved?.objects);
41906
+ setObjects([...resetObjects]);
41715
41907
  setPlayState("editing");
41716
41908
  setEventLog([]);
41717
- setSelectedObjectId((resolved?.objects ?? [])[0]?.id || null);
41909
+ setSelectedObjectId(resetObjects[0] ? objId(resetObjects[0]) : null);
41718
41910
  setAttempts(0);
41719
41911
  }, [resolved?.objects]);
41720
41912
  if (!resolved) return null;
41721
41913
  const objectViewers = objects.map((obj) => {
41914
+ const states = objStates(obj);
41915
+ const currentState = objCurrentState(obj);
41722
41916
  const machine = {
41723
- name: obj.name,
41724
- states: obj.states,
41725
- currentState: obj.currentState,
41726
- transitions: obj.rules.map((r2) => ({
41727
- from: obj.currentState,
41728
- to: obj.states.find((s) => s !== obj.currentState) || obj.currentState,
41917
+ name: objName(obj),
41918
+ states,
41919
+ currentState,
41920
+ transitions: objRules(obj).map((r2) => ({
41921
+ from: currentState,
41922
+ to: states.find((s) => s !== currentState) || currentState,
41729
41923
  event: r2.whenEvent
41730
41924
  }))
41731
41925
  };
41732
41926
  return { obj, machine };
41733
41927
  });
41734
- const showHint = attempts >= 3 && resolved.hint;
41928
+ const hint = str(resolved.hint);
41929
+ const showHint = attempts >= 3 && hint;
41930
+ const theme = resolved.theme ?? void 0;
41931
+ const themeBackground = theme?.background;
41932
+ const headerImage = str(resolved.headerImage);
41735
41933
  const encourageKey = ENCOURAGEMENT_KEYS[Math.min(attempts - 1, ENCOURAGEMENT_KEYS.length - 1)] ?? ENCOURAGEMENT_KEYS[0];
41736
41934
  return /* @__PURE__ */ jsxRuntime.jsxs(
41737
41935
  VStack,
41738
41936
  {
41739
41937
  className: cn("p-4 gap-6", className),
41740
41938
  style: {
41741
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
41939
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
41742
41940
  backgroundSize: "cover",
41743
41941
  backgroundPosition: "center"
41744
41942
  },
41745
41943
  children: [
41746
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
41944
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
41747
41945
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
41748
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
41749
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description }),
41946
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
41947
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) }),
41750
41948
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center p-2 rounded bg-primary/10 border border-primary/30", gap: "xs", children: [
41751
41949
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-primary font-bold", children: t("game.goal") + ":" }),
41752
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-foreground", children: resolved.goalCondition })
41950
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-foreground", children: str(resolved.goalCondition) })
41753
41951
  ] })
41754
41952
  ] }),
41755
41953
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
41756
41954
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.clickObject") + ":" }),
41757
- /* @__PURE__ */ jsxRuntime.jsx(HStack, { className: "flex-wrap", gap: "sm", children: objectViewers.map(({ obj, machine }) => /* @__PURE__ */ jsxRuntime.jsx(
41758
- Box,
41759
- {
41760
- className: cn(
41761
- "p-3 rounded-container border-2 cursor-pointer transition-all hover:scale-105",
41762
- selectedObjectId === obj.id ? "border-primary bg-primary/10" : "border-border bg-card hover:border-muted-foreground"
41763
- ),
41764
- onClick: () => setSelectedObjectId(obj.id),
41765
- children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "items-center min-w-[120px]", children: [
41766
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", children: obj.icon }),
41767
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground font-medium", children: obj.name }),
41768
- /* @__PURE__ */ jsxRuntime.jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" })
41769
- ] })
41770
- },
41771
- obj.id
41772
- )) })
41955
+ /* @__PURE__ */ jsxRuntime.jsx(HStack, { className: "flex-wrap", gap: "sm", children: objectViewers.map(({ obj, machine }) => {
41956
+ const oid = objId(obj);
41957
+ return /* @__PURE__ */ jsxRuntime.jsx(
41958
+ Box,
41959
+ {
41960
+ className: cn(
41961
+ "p-3 rounded-container border-2 cursor-pointer transition-all hover:scale-105",
41962
+ selectedObjectId === oid ? "border-primary bg-primary/10" : "border-border bg-card hover:border-muted-foreground"
41963
+ ),
41964
+ onClick: () => setSelectedObjectId(oid),
41965
+ children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "items-center min-w-[120px]", children: [
41966
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", children: objIcon(obj) }),
41967
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground font-medium", children: objName(obj) }),
41968
+ /* @__PURE__ */ jsxRuntime.jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" })
41969
+ ] })
41970
+ },
41971
+ oid
41972
+ );
41973
+ }) })
41773
41974
  ] }),
41774
41975
  selectedObject && /* @__PURE__ */ jsxRuntime.jsx(
41775
41976
  ObjectRulePanel,
@@ -41780,12 +41981,12 @@ function EventHandlerBoard({
41780
41981
  }
41781
41982
  ),
41782
41983
  eventLog.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(EventLog, { entries: eventLog }),
41783
- playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: resolved.successMessage || t("eventHandler.chainComplete") }) }),
41984
+ playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("eventHandler.chainComplete") }) }),
41784
41985
  playState === "fail" && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
41785
41986
  /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(encourageKey) }) }),
41786
41987
  showHint && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-start", gap: "xs", children: [
41787
41988
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
41788
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
41989
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
41789
41990
  ] }) })
41790
41991
  ] }),
41791
41992
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
@@ -41813,6 +42014,8 @@ var init_EventHandlerBoard = __esm({
41813
42014
  init_TraitStateViewer();
41814
42015
  init_ObjectRulePanel();
41815
42016
  init_EventLog();
42017
+ init_puzzleObject();
42018
+ init_boardEntity();
41816
42019
  ENCOURAGEMENT_KEYS = [
41817
42020
  "puzzle.tryAgain1",
41818
42021
  "puzzle.tryAgain2",
@@ -41901,7 +42104,10 @@ var init_FeatureGridOrganism = __esm({
41901
42104
  );
41902
42105
  React97.useCallback(
41903
42106
  (feature) => {
41904
- eventBus.emit("UI:FEATURE_CLICK", { id: feature.id, href: feature.href ?? "" });
42107
+ eventBus.emit("UI:FEATURE_CLICK", {
42108
+ id: String(feature.id ?? ""),
42109
+ href: String(feature.href ?? "")
42110
+ });
41905
42111
  },
41906
42112
  [eventBus]
41907
42113
  );
@@ -41911,14 +42117,17 @@ var init_FeatureGridOrganism = __esm({
41911
42117
  if (error) {
41912
42118
  return /* @__PURE__ */ jsxRuntime.jsx(ErrorState, { message: error.message, className });
41913
42119
  }
41914
- const featureCards = items.map((feature) => ({
41915
- icon: feature.icon,
41916
- title: feature.title,
41917
- description: feature.description,
41918
- href: feature.href,
41919
- linkLabel: feature.linkLabel,
41920
- variant: feature.href ? "interactive" : "bordered"
41921
- }));
42120
+ const featureCards = items.map((feature) => {
42121
+ const href = feature.href != null ? String(feature.href) : void 0;
42122
+ return {
42123
+ icon: feature.icon != null ? String(feature.icon) : void 0,
42124
+ title: String(feature.title ?? ""),
42125
+ description: String(feature.description ?? ""),
42126
+ href,
42127
+ linkLabel: feature.linkLabel != null ? String(feature.linkLabel) : void 0,
42128
+ variant: href ? "interactive" : "bordered"
42129
+ };
42130
+ });
41922
42131
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
41923
42132
  (heading || subtitle) && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
41924
42133
  heading && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", align: "center", children: heading }),
@@ -43236,22 +43445,24 @@ var init_HeroOrganism = __esm({
43236
43445
  () => Array.isArray(entity) ? entity[0] : entity && typeof entity === "object" ? entity : void 0,
43237
43446
  [entity]
43238
43447
  );
43448
+ const primaryAction = resolved?.primaryAction;
43449
+ const secondaryAction = resolved?.secondaryAction;
43239
43450
  const handlePrimaryClick = React97.useCallback(() => {
43240
- if (resolved?.primaryAction) {
43451
+ if (primaryAction) {
43241
43452
  eventBus.emit("UI:CTA_PRIMARY", {
43242
- label: resolved.primaryAction.label,
43243
- href: resolved.primaryAction.href
43453
+ label: String(primaryAction.label ?? ""),
43454
+ href: String(primaryAction.href ?? "")
43244
43455
  });
43245
43456
  }
43246
- }, [eventBus, resolved]);
43457
+ }, [eventBus, primaryAction]);
43247
43458
  const handleSecondaryClick = React97.useCallback(() => {
43248
- if (resolved?.secondaryAction) {
43459
+ if (secondaryAction) {
43249
43460
  eventBus.emit("UI:CTA_SECONDARY", {
43250
- label: resolved.secondaryAction.label,
43251
- href: resolved.secondaryAction.href
43461
+ label: String(secondaryAction.label ?? ""),
43462
+ href: String(secondaryAction.href ?? "")
43252
43463
  });
43253
43464
  }
43254
- }, [eventBus, resolved]);
43465
+ }, [eventBus, secondaryAction]);
43255
43466
  if (isLoading) {
43256
43467
  return /* @__PURE__ */ jsxRuntime.jsx(LoadingState, { message: t("common.loading"), className });
43257
43468
  }
@@ -43261,17 +43472,19 @@ var init_HeroOrganism = __esm({
43261
43472
  if (!resolved) {
43262
43473
  return null;
43263
43474
  }
43475
+ const imageRaw = resolved.image;
43476
+ const image = imageRaw ? { src: String(imageRaw.src ?? ""), alt: String(imageRaw.alt ?? "") } : void 0;
43264
43477
  return /* @__PURE__ */ jsxRuntime.jsxs(
43265
43478
  HeroSection,
43266
43479
  {
43267
- tag: resolved.tag,
43268
- title: resolved.title,
43269
- titleAccent: resolved.titleAccent,
43270
- subtitle: resolved.subtitle,
43271
- primaryAction: resolved.primaryAction ? { label: resolved.primaryAction.label, href: resolved.primaryAction.href } : void 0,
43272
- secondaryAction: resolved.secondaryAction ? { label: resolved.secondaryAction.label, href: resolved.secondaryAction.href } : void 0,
43273
- installCommand: resolved.installCommand,
43274
- image: resolved.image,
43480
+ tag: resolved.tag != null ? String(resolved.tag) : void 0,
43481
+ title: String(resolved.title ?? ""),
43482
+ titleAccent: resolved.titleAccent != null ? String(resolved.titleAccent) : void 0,
43483
+ subtitle: String(resolved.subtitle ?? ""),
43484
+ primaryAction: primaryAction ? { label: String(primaryAction.label ?? ""), href: String(primaryAction.href ?? "") } : void 0,
43485
+ secondaryAction: secondaryAction ? { label: String(secondaryAction.label ?? ""), href: String(secondaryAction.href ?? "") } : void 0,
43486
+ installCommand: resolved.installCommand != null ? String(resolved.installCommand) : void 0,
43487
+ image,
43275
43488
  imagePosition: resolved.imagePosition,
43276
43489
  background: resolved.background,
43277
43490
  className: cn(className),
@@ -43280,8 +43493,8 @@ var init_HeroOrganism = __esm({
43280
43493
  /* @__PURE__ */ jsxRuntime.jsx(
43281
43494
  _HeroClickInterceptor,
43282
43495
  {
43283
- hasPrimary: !!resolved.primaryAction,
43284
- hasSecondary: !!resolved.secondaryAction,
43496
+ hasPrimary: !!primaryAction,
43497
+ hasSecondary: !!secondaryAction,
43285
43498
  onPrimaryClick: handlePrimaryClick,
43286
43499
  onSecondaryClick: handleSecondaryClick
43287
43500
  }
@@ -43510,7 +43723,7 @@ function formatValue3(value, fieldName) {
43510
43723
  return String(value);
43511
43724
  }
43512
43725
  function formatFieldLabel2(fieldName) {
43513
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).replace(/Id$/, "").trim();
43726
+ return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str2) => str2.toUpperCase()).replace(/Id$/, "").trim();
43514
43727
  }
43515
43728
  var STATUS_STYLES2, StatusBadge, ProgressIndicator, List3;
43516
43729
  var init_List = __esm({
@@ -44357,20 +44570,22 @@ function NegotiatorBoard({
44357
44570
  }) {
44358
44571
  const { emit } = useEventBus();
44359
44572
  const { t } = hooks.useTranslate();
44360
- const resolved = Array.isArray(entity) ? entity[0] : entity;
44573
+ const resolved = boardEntity(entity);
44361
44574
  const [history, setHistory] = React97.useState([]);
44362
44575
  const [headerError, setHeaderError] = React97.useState(false);
44363
44576
  const [showHint, setShowHint] = React97.useState(false);
44577
+ const totalRounds = num(resolved?.totalRounds);
44578
+ const targetScore = num(resolved?.targetScore);
44364
44579
  const currentRound = history.length;
44365
- const isComplete = currentRound >= (resolved?.totalRounds ?? 0);
44580
+ const isComplete = currentRound >= totalRounds;
44366
44581
  const playerTotal = history.reduce((s, r2) => s + r2.playerPayoff, 0);
44367
44582
  const opponentTotal = history.reduce((s, r2) => s + r2.opponentPayoff, 0);
44368
- const won = isComplete && playerTotal >= (resolved?.targetScore ?? 0);
44369
- const actions = resolved?.actions ?? [];
44370
- const payoffMatrix = resolved?.payoffMatrix ?? [];
44583
+ const won = isComplete && playerTotal >= targetScore;
44584
+ const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
44585
+ const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
44371
44586
  const handleAction = React97.useCallback((actionId) => {
44372
44587
  if (isComplete) return;
44373
- const opponentAction = getOpponentAction(resolved?.opponentStrategy ?? "random", actions, history);
44588
+ const opponentAction = getOpponentAction(str(resolved?.opponentStrategy) || "random", actions, history);
44374
44589
  const payoff = payoffMatrix.find(
44375
44590
  (p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
44376
44591
  );
@@ -44383,42 +44598,46 @@ function NegotiatorBoard({
44383
44598
  };
44384
44599
  const newHistory = [...history, result];
44385
44600
  setHistory(newHistory);
44386
- if (newHistory.length >= (resolved?.totalRounds ?? 0)) {
44601
+ if (newHistory.length >= totalRounds) {
44387
44602
  const total = newHistory.reduce((s, r2) => s + r2.playerPayoff, 0);
44388
- if (total >= (resolved?.targetScore ?? 0)) {
44603
+ if (total >= targetScore) {
44389
44604
  emit(`UI:${completeEvent}`, { success: true, score: total });
44390
44605
  }
44391
- if (newHistory.length >= 3 && resolved?.hint) {
44606
+ if (newHistory.length >= 3 && str(resolved?.hint)) {
44392
44607
  setShowHint(true);
44393
44608
  }
44394
44609
  }
44395
- }, [isComplete, resolved, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
44610
+ }, [isComplete, resolved, totalRounds, targetScore, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
44396
44611
  const handleReset = () => {
44397
44612
  setHistory([]);
44398
44613
  setShowHint(false);
44399
44614
  };
44400
44615
  const getActionLabel = (id) => actions.find((a) => a.id === id)?.label ?? id;
44401
44616
  if (!resolved) return null;
44617
+ const theme = resolved.theme ?? void 0;
44618
+ const themeBackground = theme?.background;
44619
+ const headerImage = str(resolved.headerImage);
44620
+ const hint = str(resolved.hint);
44402
44621
  return /* @__PURE__ */ jsxRuntime.jsx(
44403
44622
  Box,
44404
44623
  {
44405
44624
  className,
44406
44625
  style: {
44407
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
44626
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
44408
44627
  backgroundSize: "cover",
44409
44628
  backgroundPosition: "center"
44410
44629
  },
44411
44630
  children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: "p-4", children: [
44412
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
44631
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
44413
44632
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
44414
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
44415
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.description }),
44633
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
44634
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str(resolved.description) }),
44416
44635
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "md", children: [
44417
- /* @__PURE__ */ jsxRuntime.jsx(Badge, { size: "sm", children: t("negotiator.round", { current: String(currentRound), total: String(resolved.totalRounds) }) }),
44636
+ /* @__PURE__ */ jsxRuntime.jsx(Badge, { size: "sm", children: t("negotiator.round", { current: String(currentRound), total: String(totalRounds) }) }),
44418
44637
  /* @__PURE__ */ jsxRuntime.jsxs(Badge, { size: "sm", children: [
44419
44638
  t("negotiator.target"),
44420
44639
  ": ",
44421
- resolved.targetScore
44640
+ targetScore
44422
44641
  ] })
44423
44642
  ] })
44424
44643
  ] }) }),
@@ -44467,16 +44686,16 @@ function NegotiatorBoard({
44467
44686
  ] }) }),
44468
44687
  isComplete && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", children: [
44469
44688
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.CheckCircle, size: "lg", className: won ? "text-success" : "text-error" }),
44470
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: won ? resolved.successMessage ?? t("negotiator.success") : resolved.failMessage ?? t("negotiator.failed") }),
44689
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: won ? str(resolved.successMessage) || t("negotiator.success") : str(resolved.failMessage) || t("negotiator.failed") }),
44471
44690
  /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
44472
44691
  t("negotiator.finalScore"),
44473
44692
  ": ",
44474
44693
  playerTotal,
44475
44694
  "/",
44476
- resolved.targetScore
44695
+ targetScore
44477
44696
  ] })
44478
44697
  ] }) }),
44479
- showHint && resolved.hint && !won && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.hint }) }),
44698
+ showHint && hint && !won && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
44480
44699
  isComplete && !won && /* @__PURE__ */ jsxRuntime.jsx(HStack, { justify: "center", children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleReset, children: t("negotiator.playAgain") }) })
44481
44700
  ] })
44482
44701
  }
@@ -44486,6 +44705,7 @@ var init_NegotiatorBoard = __esm({
44486
44705
  "components/game/organisms/puzzles/negotiator/NegotiatorBoard.tsx"() {
44487
44706
  init_atoms2();
44488
44707
  init_useEventBus();
44708
+ init_boardEntity();
44489
44709
  NegotiatorBoard.displayName = "NegotiatorBoard";
44490
44710
  }
44491
44711
  });
@@ -44521,13 +44741,13 @@ var init_PricingOrganism = __esm({
44521
44741
  return /* @__PURE__ */ jsxRuntime.jsx(ErrorState, { message: error.message, className });
44522
44742
  }
44523
44743
  const plans = items.map((plan) => ({
44524
- name: plan.name,
44525
- price: plan.price,
44526
- description: plan.description,
44527
- features: plan.features,
44528
- action: { label: plan.actionLabel, href: plan.actionHref },
44529
- highlighted: plan.highlighted,
44530
- badge: plan.badge
44744
+ name: String(plan.name ?? ""),
44745
+ price: String(plan.price ?? ""),
44746
+ description: plan.description != null ? String(plan.description) : void 0,
44747
+ features: (plan.features ?? []).map((f3) => String(f3)),
44748
+ action: { label: String(plan.actionLabel ?? ""), href: String(plan.actionHref ?? "") },
44749
+ highlighted: Boolean(plan.highlighted),
44750
+ badge: plan.badge != null ? String(plan.badge) : void 0
44531
44751
  }));
44532
44752
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
44533
44753
  (heading || subtitle) && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
@@ -46617,16 +46837,20 @@ function SequencerBoard({
46617
46837
  }) {
46618
46838
  const { emit } = useEventBus();
46619
46839
  const { t } = hooks.useTranslate();
46620
- const resolved = Array.isArray(entity) ? entity[0] : entity;
46840
+ const resolved = boardEntity(entity);
46841
+ const maxSlots = num(resolved?.maxSlots);
46842
+ const solutions = Array.isArray(resolved?.solutions) ? resolved.solutions : [];
46843
+ const availableActions = Array.isArray(resolved?.availableActions) ? resolved.availableActions : [];
46844
+ const allowDuplicates = resolved?.allowDuplicates !== false;
46621
46845
  const [headerError, setHeaderError] = React97.useState(false);
46622
46846
  const [slots, setSlots] = React97.useState(
46623
- () => Array.from({ length: resolved?.maxSlots ?? 0 }, () => void 0)
46847
+ () => Array.from({ length: maxSlots }, () => void 0)
46624
46848
  );
46625
46849
  const [playState, setPlayState] = React97.useState("idle");
46626
46850
  const [currentStep, setCurrentStep] = React97.useState(-1);
46627
46851
  const [attempts, setAttempts] = React97.useState(0);
46628
46852
  const [slotFeedback, setSlotFeedback] = React97.useState(
46629
- () => Array.from({ length: resolved?.maxSlots ?? 0 }, () => null)
46853
+ () => Array.from({ length: maxSlots }, () => null)
46630
46854
  );
46631
46855
  const timerRef = React97.useRef(null);
46632
46856
  React97.useEffect(() => () => {
@@ -46660,17 +46884,17 @@ function SequencerBoard({
46660
46884
  }, [emit]);
46661
46885
  const handleReset = React97.useCallback(() => {
46662
46886
  if (timerRef.current) clearTimeout(timerRef.current);
46663
- setSlots(Array.from({ length: resolved?.maxSlots ?? 0 }, () => void 0));
46887
+ setSlots(Array.from({ length: maxSlots }, () => void 0));
46664
46888
  setPlayState("idle");
46665
46889
  setCurrentStep(-1);
46666
46890
  setAttempts(0);
46667
- setSlotFeedback(Array.from({ length: resolved?.maxSlots ?? 0 }, () => null));
46668
- }, [resolved?.maxSlots]);
46891
+ setSlotFeedback(Array.from({ length: maxSlots }, () => null));
46892
+ }, [maxSlots]);
46669
46893
  const filledSlots = slots.filter((s) => !!s);
46670
46894
  const canPlay = filledSlots.length > 0 && playState === "idle";
46671
46895
  const handlePlay = React97.useCallback(() => {
46672
46896
  if (!canPlay) return;
46673
- setSlotFeedback(Array.from({ length: resolved?.maxSlots ?? 0 }, () => null));
46897
+ setSlotFeedback(Array.from({ length: maxSlots }, () => null));
46674
46898
  emit("UI:PLAY_SOUND", { key: "confirm" });
46675
46899
  const sequence = slots.map((s) => s?.id || "");
46676
46900
  if (playEvent) {
@@ -46681,10 +46905,10 @@ function SequencerBoard({
46681
46905
  let step = 0;
46682
46906
  const advance = () => {
46683
46907
  step++;
46684
- if (step >= (resolved?.maxSlots ?? 0)) {
46908
+ if (step >= maxSlots) {
46685
46909
  const playerSeq = slots.map((s) => s?.id);
46686
46910
  const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
46687
- const success = (resolved?.solutions ?? []).some(
46911
+ const success = solutions.some(
46688
46912
  (sol) => sol.length === playerIds.length && sol.every((id, i) => id === playerIds[i])
46689
46913
  );
46690
46914
  if (success) {
@@ -46696,7 +46920,7 @@ function SequencerBoard({
46696
46920
  }
46697
46921
  } else {
46698
46922
  setAttempts((prev) => prev + 1);
46699
- const feedback = computeSlotFeedback(playerSeq, resolved?.solutions ?? []);
46923
+ const feedback = computeSlotFeedback(playerSeq, solutions);
46700
46924
  setSlotFeedback(feedback);
46701
46925
  setPlayState("idle");
46702
46926
  setCurrentStep(-1);
@@ -46714,10 +46938,10 @@ function SequencerBoard({
46714
46938
  }
46715
46939
  };
46716
46940
  timerRef.current = setTimeout(advance, stepDurationMs);
46717
- }, [canPlay, slots, resolved?.maxSlots, resolved?.solutions, stepDurationMs, playEvent, completeEvent, emit]);
46941
+ }, [canPlay, slots, maxSlots, solutions, stepDurationMs, playEvent, completeEvent, emit]);
46718
46942
  const machine = {
46719
- name: resolved?.title ?? "",
46720
- description: resolved?.description ?? "",
46943
+ name: str(resolved?.title),
46944
+ description: str(resolved?.description),
46721
46945
  states: slots.map((s, i) => stepLabel(s, i)),
46722
46946
  currentState: currentStep >= 0 ? stepLabel(slots[currentStep], currentStep) : "__idle__",
46723
46947
  transitions: slots.slice(0, -1).map((s, i) => ({
@@ -46726,37 +46950,41 @@ function SequencerBoard({
46726
46950
  event: "NEXT"
46727
46951
  }))
46728
46952
  };
46729
- const usedIds = resolved?.allowDuplicates === false ? slots.filter(Boolean).map((s) => s?.id || "") : [];
46730
- const showHint = attempts >= 3 && !!resolved?.hint;
46953
+ const usedIds = !allowDuplicates ? slots.filter(Boolean).map((s) => s?.id || "") : [];
46954
+ const hint = str(resolved?.hint);
46955
+ const showHint = attempts >= 3 && !!hint;
46731
46956
  const hasFeedback = slotFeedback.some((f3) => f3 !== null);
46732
46957
  const correctCount = slotFeedback.filter((f3) => f3 === "correct").length;
46733
46958
  const encourageKey = ENCOURAGEMENT_KEYS2[Math.min(attempts - 1, ENCOURAGEMENT_KEYS2.length - 1)] ?? ENCOURAGEMENT_KEYS2[0];
46734
46959
  if (!resolved) return null;
46960
+ const theme = resolved.theme ?? void 0;
46961
+ const themeBackground = theme?.background;
46962
+ const headerImage = str(resolved.headerImage);
46735
46963
  return /* @__PURE__ */ jsxRuntime.jsxs(
46736
46964
  VStack,
46737
46965
  {
46738
46966
  className: cn("p-4 gap-6", className),
46739
46967
  style: {
46740
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
46968
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
46741
46969
  backgroundSize: "cover",
46742
46970
  backgroundPosition: "center"
46743
46971
  },
46744
46972
  children: [
46745
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
46973
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
46746
46974
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
46747
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
46748
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description })
46975
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
46976
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) })
46749
46977
  ] }),
46750
46978
  showHint && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-start", gap: "xs", children: [
46751
46979
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
46752
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
46980
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
46753
46981
  ] }) }),
46754
46982
  filledSlots.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(TraitStateViewer, { trait: machine, variant: "linear", size: "md" }),
46755
46983
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
46756
46984
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center justify-between", children: [
46757
46985
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("sequencer.yourSequence") + ":" }),
46758
46986
  hasFeedback && playState === "idle" && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
46759
- `${correctCount}/${resolved.maxSlots} `,
46987
+ `${correctCount}/${maxSlots} `,
46760
46988
  "\u2705"
46761
46989
  ] })
46762
46990
  ] }),
@@ -46764,7 +46992,7 @@ function SequencerBoard({
46764
46992
  SequenceBar,
46765
46993
  {
46766
46994
  slots,
46767
- maxSlots: resolved.maxSlots,
46995
+ maxSlots,
46768
46996
  onSlotDrop: handleSlotDrop,
46769
46997
  onSlotRemove: handleSlotRemove,
46770
46998
  playing: playState === "playing",
@@ -46778,15 +47006,15 @@ function SequencerBoard({
46778
47006
  playState !== "playing" && /* @__PURE__ */ jsxRuntime.jsx(
46779
47007
  ActionPalette,
46780
47008
  {
46781
- actions: resolved.availableActions,
47009
+ actions: availableActions,
46782
47010
  usedActionIds: usedIds,
46783
- allowDuplicates: resolved.allowDuplicates !== false,
47011
+ allowDuplicates,
46784
47012
  categoryColors,
46785
47013
  label: t("sequencer.dragActions")
46786
47014
  }
46787
47015
  ),
46788
47016
  hasFeedback && playState === "idle" && attempts > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: t(encourageKey) }) }),
46789
- playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: resolved.successMessage || t("sequencer.levelComplete") }) }),
47017
+ playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("sequencer.levelComplete") }) }),
46790
47018
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
46791
47019
  /* @__PURE__ */ jsxRuntime.jsx(
46792
47020
  Button,
@@ -46810,6 +47038,7 @@ var init_SequencerBoard = __esm({
46810
47038
  init_cn();
46811
47039
  init_useEventBus();
46812
47040
  init_TraitStateViewer();
47041
+ init_boardEntity();
46813
47042
  init_SequenceBar();
46814
47043
  init_ActionPalette();
46815
47044
  ENCOURAGEMENT_KEYS2 = [
@@ -46859,18 +47088,21 @@ var init_ShowcaseOrganism = __esm({
46859
47088
  heading && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", align: "center", children: heading }),
46860
47089
  subtitle && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", color: "muted", align: "center", className: "max-w-2xl", children: subtitle })
46861
47090
  ] }),
46862
- /* @__PURE__ */ jsxRuntime.jsx(SimpleGrid, { cols: columns, gap: "lg", children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
46863
- ShowcaseCard,
46864
- {
46865
- title: item.title,
46866
- description: item.description,
46867
- image: item.image,
46868
- href: item.href,
46869
- badge: item.badge,
46870
- accentColor: item.accentColor
46871
- },
46872
- item.id
46873
- )) })
47091
+ /* @__PURE__ */ jsxRuntime.jsx(SimpleGrid, { cols: columns, gap: "lg", children: items.map((item) => {
47092
+ const imageRaw = item.image;
47093
+ return /* @__PURE__ */ jsxRuntime.jsx(
47094
+ ShowcaseCard,
47095
+ {
47096
+ title: String(item.title ?? ""),
47097
+ description: item.description != null ? String(item.description) : void 0,
47098
+ image: { src: String(imageRaw?.src ?? ""), alt: String(imageRaw?.alt ?? "") },
47099
+ href: item.href != null ? String(item.href) : void 0,
47100
+ badge: item.badge != null ? String(item.badge) : void 0,
47101
+ accentColor: item.accentColor != null ? String(item.accentColor) : void 0
47102
+ },
47103
+ String(item.id ?? "")
47104
+ );
47105
+ }) })
46874
47106
  ] });
46875
47107
  };
46876
47108
  ShowcaseOrganism.displayName = "ShowcaseOrganism";
@@ -47238,8 +47470,8 @@ function SimulatorBoard({
47238
47470
  }) {
47239
47471
  const { emit } = useEventBus();
47240
47472
  const { t } = hooks.useTranslate();
47241
- const resolved = Array.isArray(entity) ? entity[0] : entity;
47242
- const parameters = resolved?.parameters ?? [];
47473
+ const resolved = boardEntity(entity);
47474
+ const parameters = Array.isArray(resolved?.parameters) ? resolved.parameters : [];
47243
47475
  const [values, setValues] = React97.useState(() => {
47244
47476
  const init = {};
47245
47477
  for (const p2 of parameters) {
@@ -47253,15 +47485,15 @@ function SimulatorBoard({
47253
47485
  const [showHint, setShowHint] = React97.useState(false);
47254
47486
  const computeOutput = React97.useCallback((params) => {
47255
47487
  try {
47256
- const fn = new Function("params", `return (${resolved?.computeExpression})`);
47488
+ const fn = new Function("params", `return (${str(resolved?.computeExpression)})`);
47257
47489
  return fn(params);
47258
47490
  } catch {
47259
47491
  return 0;
47260
47492
  }
47261
47493
  }, [resolved?.computeExpression]);
47262
47494
  const output = React97.useMemo(() => computeOutput(values) ?? 0, [computeOutput, values]);
47263
- const targetValue = resolved?.targetValue ?? 0;
47264
- const targetTolerance = resolved?.targetTolerance ?? 0;
47495
+ const targetValue = num(resolved?.targetValue);
47496
+ const targetTolerance = num(resolved?.targetTolerance);
47265
47497
  const isCorrect = Math.abs(output - targetValue) <= targetTolerance;
47266
47498
  const handleParameterChange = (id, value) => {
47267
47499
  if (submitted) return;
@@ -47276,7 +47508,7 @@ function SimulatorBoard({
47276
47508
  };
47277
47509
  const handleReset = () => {
47278
47510
  setSubmitted(false);
47279
- if (attempts >= 2 && resolved?.hint) {
47511
+ if (attempts >= 2 && str(resolved?.hint)) {
47280
47512
  setShowHint(true);
47281
47513
  }
47282
47514
  };
@@ -47291,20 +47523,26 @@ function SimulatorBoard({
47291
47523
  setShowHint(false);
47292
47524
  };
47293
47525
  if (!resolved) return null;
47526
+ const theme = resolved.theme ?? void 0;
47527
+ const themeBackground = theme?.background;
47528
+ const headerImage = str(resolved.headerImage);
47529
+ const hint = str(resolved.hint);
47530
+ const outputLabel = str(resolved.outputLabel);
47531
+ const outputUnit = str(resolved.outputUnit);
47294
47532
  return /* @__PURE__ */ jsxRuntime.jsx(
47295
47533
  Box,
47296
47534
  {
47297
47535
  className,
47298
47536
  style: {
47299
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
47537
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
47300
47538
  backgroundSize: "cover",
47301
47539
  backgroundPosition: "center"
47302
47540
  },
47303
47541
  children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: "p-4", children: [
47304
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
47542
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
47305
47543
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
47306
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
47307
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.description })
47544
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
47545
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str(resolved.description) })
47308
47546
  ] }) }),
47309
47547
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", children: [
47310
47548
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("simulator.parameters") }),
@@ -47345,28 +47583,28 @@ function SimulatorBoard({
47345
47583
  ] }, param.id))
47346
47584
  ] }) }),
47347
47585
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", children: [
47348
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: resolved.outputLabel }),
47586
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: outputLabel }),
47349
47587
  /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "h3", weight: "bold", children: [
47350
47588
  output.toFixed(2),
47351
47589
  " ",
47352
- resolved.outputUnit
47590
+ outputUnit
47353
47591
  ] }),
47354
47592
  submitted && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
47355
47593
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: isCorrect ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: isCorrect ? "text-success" : "text-error" }),
47356
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: isCorrect ? "text-success" : "text-error", children: isCorrect ? resolved.successMessage ?? t("simulator.correct") : resolved.failMessage ?? t("simulator.incorrect") })
47594
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: isCorrect ? "text-success" : "text-error", children: isCorrect ? str(resolved.successMessage) || t("simulator.correct") : str(resolved.failMessage) || t("simulator.incorrect") })
47357
47595
  ] }),
47358
47596
  /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
47359
47597
  t("simulator.target"),
47360
47598
  ": ",
47361
47599
  targetValue,
47362
47600
  " ",
47363
- resolved.outputUnit,
47601
+ outputUnit,
47364
47602
  " (\xB1",
47365
47603
  targetTolerance,
47366
47604
  ")"
47367
47605
  ] })
47368
47606
  ] }) }),
47369
- showHint && resolved.hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.hint }) }),
47607
+ showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
47370
47608
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
47371
47609
  !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, children: [
47372
47610
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Play, size: "sm" }),
@@ -47385,6 +47623,7 @@ var init_SimulatorBoard = __esm({
47385
47623
  "components/game/organisms/puzzles/simulator/SimulatorBoard.tsx"() {
47386
47624
  init_atoms2();
47387
47625
  init_useEventBus();
47626
+ init_boardEntity();
47388
47627
  SimulatorBoard.displayName = "SimulatorBoard";
47389
47628
  }
47390
47629
  });
@@ -47810,22 +48049,25 @@ function VariablePanel({
47810
48049
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("p-3 rounded-lg bg-card border border-border", className), gap: "sm", children: [
47811
48050
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("stateArchitect.variables", { name: entityName }) }),
47812
48051
  variables.map((v) => {
47813
- const max = v.max ?? 100;
47814
- const min = v.min ?? 0;
47815
- const pct = Math.round((v.value - min) / (max - min) * 100);
48052
+ const name = v.name == null ? "" : String(v.name);
48053
+ const value = numField(v.value);
48054
+ const max = numField(v.max, 100);
48055
+ const min = numField(v.min, 0);
48056
+ const unit = v.unit == null ? "" : String(v.unit);
48057
+ const pct = Math.round((value - min) / (max - min) * 100);
47816
48058
  const isHigh = pct > 80;
47817
48059
  const isLow = pct < 20;
47818
48060
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", children: [
47819
48061
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center justify-between", children: [
47820
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-foreground font-medium", children: v.name }),
48062
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-foreground font-medium", children: name }),
47821
48063
  /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: cn(
47822
48064
  isHigh ? "text-error" : isLow ? "text-warning" : "text-foreground"
47823
48065
  ), children: [
47824
- v.value,
47825
- v.unit || "",
48066
+ value,
48067
+ unit,
47826
48068
  " / ",
47827
48069
  max,
47828
- v.unit || ""
48070
+ unit
47829
48071
  ] })
47830
48072
  ] }),
47831
48073
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -47836,14 +48078,19 @@ function VariablePanel({
47836
48078
  size: "sm"
47837
48079
  }
47838
48080
  )
47839
- ] }, v.name);
48081
+ ] }, name);
47840
48082
  })
47841
48083
  ] });
47842
48084
  }
48085
+ var numField;
47843
48086
  var init_VariablePanel = __esm({
47844
48087
  "components/game/organisms/puzzles/state-architect/VariablePanel.tsx"() {
47845
48088
  init_atoms2();
47846
48089
  init_cn();
48090
+ numField = (v, fallback = 0) => {
48091
+ const n = Number(v);
48092
+ return Number.isFinite(n) ? n : fallback;
48093
+ };
47847
48094
  VariablePanel.displayName = "VariablePanel";
47848
48095
  }
47849
48096
  });
@@ -47870,14 +48117,21 @@ function StateArchitectBoard({
47870
48117
  }) {
47871
48118
  const { emit } = useEventBus();
47872
48119
  const { t } = hooks.useTranslate();
47873
- const resolved = Array.isArray(entity) ? entity[0] : entity;
47874
- const [transitions, setTransitions] = React97.useState(resolved?.transitions ?? []);
48120
+ const resolved = boardEntity(entity);
48121
+ const entityStates = Array.isArray(resolved?.states) ? resolved.states : [];
48122
+ const initialState = str(resolved?.initialState);
48123
+ const entityName = str(resolved?.entityName);
48124
+ const availableEvents = Array.isArray(resolved?.availableEvents) ? resolved.availableEvents : [];
48125
+ const testCases = Array.isArray(resolved?.testCases) ? resolved.testCases : [];
48126
+ const entityTransitions = Array.isArray(resolved?.transitions) ? resolved.transitions : [];
48127
+ const entityVariables = rows(resolved?.variables);
48128
+ const [transitions, setTransitions] = React97.useState(entityTransitions);
47875
48129
  const [headerError, setHeaderError] = React97.useState(false);
47876
48130
  const [playState, setPlayState] = React97.useState("editing");
47877
- const [currentState, setCurrentState] = React97.useState(resolved?.initialState ?? "");
48131
+ const [currentState, setCurrentState] = React97.useState(initialState);
47878
48132
  const [selectedState, setSelectedState] = React97.useState(null);
47879
48133
  const [testResults, setTestResults] = React97.useState([]);
47880
- const [variables, setVariables] = React97.useState(resolved?.variables ?? []);
48134
+ const [variables, setVariables] = React97.useState(() => [...entityVariables]);
47881
48135
  const [attempts, setAttempts] = React97.useState(0);
47882
48136
  const timerRef = React97.useRef(null);
47883
48137
  const [addingFrom, setAddingFrom] = React97.useState(null);
@@ -47886,12 +48140,12 @@ function StateArchitectBoard({
47886
48140
  }, []);
47887
48141
  const GRAPH_W = 500;
47888
48142
  const GRAPH_H = 400;
47889
- const positions = React97.useMemo(() => layoutStates(resolved?.states ?? [], GRAPH_W, GRAPH_H), [resolved?.states]);
48143
+ const positions = React97.useMemo(() => layoutStates(entityStates, GRAPH_W, GRAPH_H), [entityStates]);
47890
48144
  const handleStateClick = React97.useCallback((state) => {
47891
48145
  if (playState !== "editing") return;
47892
48146
  if (addingFrom) {
47893
48147
  if (addingFrom !== state) {
47894
- const event = resolved?.availableEvents[0] || "EVENT";
48148
+ const event = availableEvents[0] || "EVENT";
47895
48149
  const newTrans = {
47896
48150
  id: `t-${nextTransId++}`,
47897
48151
  from: addingFrom,
@@ -47904,7 +48158,7 @@ function StateArchitectBoard({
47904
48158
  } else {
47905
48159
  setSelectedState(state);
47906
48160
  }
47907
- }, [playState, addingFrom, resolved?.availableEvents]);
48161
+ }, [playState, addingFrom, availableEvents]);
47908
48162
  const handleStartAddTransition = React97.useCallback(() => {
47909
48163
  if (!selectedState) return;
47910
48164
  setAddingFrom(selectedState);
@@ -47913,9 +48167,9 @@ function StateArchitectBoard({
47913
48167
  setTransitions((prev) => prev.filter((t2) => t2.id !== transId));
47914
48168
  }, []);
47915
48169
  const machine = React97.useMemo(() => ({
47916
- name: resolved?.entityName ?? "",
47917
- description: resolved?.description ?? "",
47918
- states: resolved?.states ?? [],
48170
+ name: entityName,
48171
+ description: str(resolved?.description),
48172
+ states: entityStates,
47919
48173
  currentState,
47920
48174
  transitions: transitions.map((t2) => ({
47921
48175
  from: t2.from,
@@ -47923,7 +48177,7 @@ function StateArchitectBoard({
47923
48177
  event: t2.event,
47924
48178
  guardHint: t2.guardHint
47925
48179
  }))
47926
- }), [resolved, currentState, transitions]);
48180
+ }), [entityName, resolved, entityStates, currentState, transitions]);
47927
48181
  const handleTest = React97.useCallback(() => {
47928
48182
  if (playState !== "editing") return;
47929
48183
  if (testEvent) emit(`UI:${testEvent}`, {});
@@ -47932,7 +48186,7 @@ function StateArchitectBoard({
47932
48186
  const results = [];
47933
48187
  let testIdx = 0;
47934
48188
  const runNextTest = () => {
47935
- if (testIdx >= (resolved?.testCases.length ?? 0)) {
48189
+ if (testIdx >= testCases.length) {
47936
48190
  const allPassed = results.every((r2) => r2.passed);
47937
48191
  setPlayState(allPassed ? "success" : "fail");
47938
48192
  setTestResults(results);
@@ -47947,9 +48201,9 @@ function StateArchitectBoard({
47947
48201
  }
47948
48202
  return;
47949
48203
  }
47950
- const testCase = resolved?.testCases[testIdx];
48204
+ const testCase = testCases[testIdx];
47951
48205
  if (!testCase) return;
47952
- let state = resolved.initialState;
48206
+ let state = initialState;
47953
48207
  for (const event of testCase.events) {
47954
48208
  const trans = transitions.find((t2) => t2.from === state && t2.event === event);
47955
48209
  if (trans) {
@@ -47967,53 +48221,57 @@ function StateArchitectBoard({
47967
48221
  timerRef.current = setTimeout(runNextTest, stepDurationMs);
47968
48222
  };
47969
48223
  timerRef.current = setTimeout(runNextTest, stepDurationMs);
47970
- }, [playState, transitions, resolved, stepDurationMs, testEvent, completeEvent, emit]);
48224
+ }, [playState, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
47971
48225
  const handleTryAgain = React97.useCallback(() => {
47972
48226
  if (timerRef.current) clearTimeout(timerRef.current);
47973
48227
  setPlayState("editing");
47974
- setCurrentState(resolved?.initialState ?? "");
48228
+ setCurrentState(initialState);
47975
48229
  setTestResults([]);
47976
- }, [resolved?.initialState]);
48230
+ }, [initialState]);
47977
48231
  const handleReset = React97.useCallback(() => {
47978
48232
  if (timerRef.current) clearTimeout(timerRef.current);
47979
- setTransitions(resolved?.transitions ?? []);
48233
+ setTransitions(entityTransitions);
47980
48234
  setPlayState("editing");
47981
- setCurrentState(resolved?.initialState ?? "");
48235
+ setCurrentState(initialState);
47982
48236
  setTestResults([]);
47983
- setVariables(resolved?.variables ?? []);
48237
+ setVariables([...entityVariables]);
47984
48238
  setSelectedState(null);
47985
48239
  setAddingFrom(null);
47986
48240
  setAttempts(0);
47987
- }, [resolved]);
48241
+ }, [entityTransitions, initialState, entityVariables]);
47988
48242
  const codeData = React97.useMemo(() => ({
47989
- name: resolved?.entityName ?? "",
47990
- states: resolved?.states ?? [],
47991
- initialState: resolved?.initialState ?? "",
48243
+ name: entityName,
48244
+ states: entityStates,
48245
+ initialState,
47992
48246
  transitions: transitions.map((t2) => ({
47993
48247
  from: t2.from,
47994
48248
  to: t2.to,
47995
48249
  event: t2.event,
47996
48250
  ...t2.guardHint ? { guard: t2.guardHint } : {}
47997
48251
  }))
47998
- }), [resolved, transitions]);
48252
+ }), [entityName, entityStates, initialState, transitions]);
47999
48253
  if (!resolved) return null;
48254
+ const theme = resolved.theme ?? void 0;
48255
+ const themeBackground = theme?.background;
48256
+ const headerImage = str(resolved.headerImage);
48257
+ const hint = str(resolved.hint);
48000
48258
  return /* @__PURE__ */ jsxRuntime.jsxs(
48001
48259
  VStack,
48002
48260
  {
48003
48261
  className: cn("p-4 gap-6", className),
48004
48262
  style: {
48005
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
48263
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
48006
48264
  backgroundSize: "cover",
48007
48265
  backgroundPosition: "center"
48008
48266
  },
48009
48267
  children: [
48010
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
48268
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
48011
48269
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
48012
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
48013
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description }),
48270
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
48271
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) }),
48014
48272
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center p-2 rounded bg-warning/10 border border-warning/30", gap: "xs", children: [
48015
48273
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-warning font-bold", children: t("game.hint") + ":" }),
48016
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-foreground", children: resolved.hint })
48274
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-foreground", children: hint })
48017
48275
  ] })
48018
48276
  ] }),
48019
48277
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "flex-wrap items-start", gap: "lg", children: [
@@ -48061,14 +48319,14 @@ function StateArchitectBoard({
48061
48319
  ]
48062
48320
  }
48063
48321
  ),
48064
- resolved.states.map((state) => /* @__PURE__ */ jsxRuntime.jsx(
48322
+ entityStates.map((state) => /* @__PURE__ */ jsxRuntime.jsx(
48065
48323
  StateNode2,
48066
48324
  {
48067
48325
  name: state,
48068
48326
  position: positions[state],
48069
48327
  isCurrent: state === currentState,
48070
48328
  isSelected: state === selectedState,
48071
- isInitial: state === resolved.initialState,
48329
+ isInitial: state === initialState,
48072
48330
  onClick: () => handleStateClick(state)
48073
48331
  },
48074
48332
  state
@@ -48115,7 +48373,7 @@ function StateArchitectBoard({
48115
48373
  /* @__PURE__ */ jsxRuntime.jsx(
48116
48374
  VariablePanel,
48117
48375
  {
48118
- entityName: resolved.entityName,
48376
+ entityName,
48119
48377
  variables
48120
48378
  }
48121
48379
  ),
@@ -48130,12 +48388,12 @@ function StateArchitectBoard({
48130
48388
  resolved.showCodeView !== false && /* @__PURE__ */ jsxRuntime.jsx(CodeView, { data: codeData, label: "View Code" })
48131
48389
  ] })
48132
48390
  ] }),
48133
- playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: resolved.successMessage || t("stateArchitect.allPassed") }) }),
48391
+ playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("stateArchitect.allPassed") }) }),
48134
48392
  playState === "fail" && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
48135
48393
  /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(ENCOURAGEMENT_KEYS3[Math.min(attempts - 1, ENCOURAGEMENT_KEYS3.length - 1)] ?? ENCOURAGEMENT_KEYS3[0]) }) }),
48136
- attempts >= 3 && resolved.hint && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-start", gap: "xs", children: [
48394
+ attempts >= 3 && hint && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-start", gap: "xs", children: [
48137
48395
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
48138
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
48396
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
48139
48397
  ] }) })
48140
48398
  ] }),
48141
48399
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
@@ -48165,6 +48423,7 @@ var init_StateArchitectBoard = __esm({
48165
48423
  init_TransitionArrow();
48166
48424
  init_VariablePanel();
48167
48425
  init_CodeView();
48426
+ init_boardEntity();
48168
48427
  ENCOURAGEMENT_KEYS3 = [
48169
48428
  "puzzle.tryAgain1",
48170
48429
  "puzzle.tryAgain2",
@@ -48201,8 +48460,8 @@ var init_StatsOrganism = __esm({
48201
48460
  return /* @__PURE__ */ jsxRuntime.jsx(ErrorState, { message: error.message, className });
48202
48461
  }
48203
48462
  const stats = items.map((item) => ({
48204
- value: item.value,
48205
- label: item.label
48463
+ value: String(item.value ?? ""),
48464
+ label: String(item.label ?? "")
48206
48465
  }));
48207
48466
  return /* @__PURE__ */ jsxRuntime.jsx(
48208
48467
  StatsGrid,
@@ -48248,10 +48507,10 @@ var init_StepFlowOrganism = __esm({
48248
48507
  return /* @__PURE__ */ jsxRuntime.jsx(ErrorState, { message: error.message, className });
48249
48508
  }
48250
48509
  const steps = items.map((item) => ({
48251
- number: item.number,
48252
- title: item.title,
48253
- description: item.description,
48254
- icon: item.icon
48510
+ number: item.number != null ? Number(item.number) : void 0,
48511
+ title: String(item.title ?? ""),
48512
+ description: String(item.description ?? ""),
48513
+ icon: item.icon != null ? String(item.icon) : void 0
48255
48514
  }));
48256
48515
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
48257
48516
  (heading || subtitle) && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
@@ -48424,13 +48683,13 @@ var init_TeamOrganism = __esm({
48424
48683
  /* @__PURE__ */ jsxRuntime.jsx(SimpleGrid, { cols: cols > 0 ? cols : 1, gap: "lg", children: items.map((member) => /* @__PURE__ */ jsxRuntime.jsx(
48425
48684
  TeamCard,
48426
48685
  {
48427
- name: member.name,
48428
- nameAr: member.nameAr,
48429
- role: member.role,
48430
- bio: member.bio,
48431
- avatar: member.avatar
48686
+ name: String(member.name ?? ""),
48687
+ nameAr: member.nameAr != null ? String(member.nameAr) : void 0,
48688
+ role: String(member.role ?? ""),
48689
+ bio: String(member.bio ?? ""),
48690
+ avatar: member.avatar != null ? String(member.avatar) : void 0
48432
48691
  },
48433
- member.id
48692
+ String(member.id ?? "")
48434
48693
  )) })
48435
48694
  ] });
48436
48695
  };
@@ -48667,8 +48926,8 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
48667
48926
  const [turn, setTurn] = React97.useState(1);
48668
48927
  const [gameResult, setGameResult] = React97.useState(null);
48669
48928
  const checkGameEnd = React97.useCallback((currentUnits) => {
48670
- const pa = currentUnits.filter((u) => u.team === "player" && u.health > 0);
48671
- const ea = currentUnits.filter((u) => u.team === "enemy" && u.health > 0);
48929
+ const pa = currentUnits.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
48930
+ const ea = currentUnits.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
48672
48931
  if (pa.length === 0) {
48673
48932
  setGameResult("defeat");
48674
48933
  setPhase("game_over");
@@ -48686,34 +48945,36 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
48686
48945
  }
48687
48946
  }, [onGameEnd, gameEndEvent, eventBus]);
48688
48947
  const handleUnitClick = React97.useCallback((unitId) => {
48689
- const unit = units.find((u) => u.id === unitId);
48948
+ const unit = units.find((u) => str(u.id) === unitId);
48690
48949
  if (!unit) return;
48691
48950
  if (unitClickEvent) {
48692
48951
  eventBus.emit(`UI:${unitClickEvent}`, { unitId });
48693
48952
  }
48694
48953
  if (phase === "observation" || phase === "selection") {
48695
- if (unit.team === "player") {
48954
+ if (unitTeam(unit) === "player") {
48696
48955
  setSelectedUnitId(unitId);
48697
48956
  setPhase("movement");
48698
48957
  }
48699
48958
  } else if (phase === "action") {
48700
- const selectedUnit = units.find((u) => u.id === selectedUnitId);
48959
+ const selectedUnit = units.find((u) => str(u.id) === selectedUnitId);
48701
48960
  if (!selectedUnit) return;
48702
- if (unit.team === "enemy") {
48703
- const dx = Math.abs(unit.position.x - selectedUnit.position.x);
48704
- const dy = Math.abs(unit.position.y - selectedUnit.position.y);
48961
+ if (unitTeam(unit) === "enemy") {
48962
+ const up = unitPosition(unit);
48963
+ const sp = unitPosition(selectedUnit);
48964
+ const dx = Math.abs(up.x - sp.x);
48965
+ const dy = Math.abs(up.y - sp.y);
48705
48966
  if (dx <= 1 && dy <= 1 && dx + dy > 0) {
48706
- const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, selectedUnit.attack - unit.defense);
48707
- const newHealth = Math.max(0, unit.health - damage);
48967
+ const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, num(selectedUnit.attack) - num(unit.defense));
48968
+ const newHealth = Math.max(0, unitHealth(unit) - damage);
48708
48969
  const updatedUnits = units.map(
48709
- (u) => u.id === unit.id ? { ...u, health: newHealth } : u
48970
+ (u) => str(u.id) === str(unit.id) ? { ...u, health: newHealth } : u
48710
48971
  );
48711
48972
  setUnits(updatedUnits);
48712
48973
  onAttack?.(selectedUnit, unit, damage);
48713
48974
  if (attackEvent) {
48714
48975
  eventBus.emit(`UI:${attackEvent}`, {
48715
- attackerId: selectedUnit.id,
48716
- targetId: unit.id,
48976
+ attackerId: str(selectedUnit.id),
48977
+ targetId: str(unit.id),
48717
48978
  damage
48718
48979
  });
48719
48980
  }
@@ -48730,16 +48991,20 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
48730
48991
  eventBus.emit(`UI:${tileClickEvent}`, { x, y });
48731
48992
  }
48732
48993
  if (phase === "movement" && selectedUnitId) {
48733
- const selectedUnit = units.find((u) => u.id === selectedUnitId);
48994
+ const selectedUnit = units.find((u) => str(u.id) === selectedUnitId);
48734
48995
  if (!selectedUnit) return;
48735
- const dx = Math.abs(x - selectedUnit.position.x);
48736
- const dy = Math.abs(y - selectedUnit.position.y);
48996
+ const sp = unitPosition(selectedUnit);
48997
+ const dx = Math.abs(x - sp.x);
48998
+ const dy = Math.abs(y - sp.y);
48737
48999
  const dist = dx + dy;
48738
- if (dist > 0 && dist <= selectedUnit.movement) {
48739
- if (!units.some((u) => u.position.x === x && u.position.y === y && u.health > 0)) {
49000
+ if (dist > 0 && dist <= num(selectedUnit.movement)) {
49001
+ if (!units.some((u) => {
49002
+ const p2 = unitPosition(u);
49003
+ return p2.x === x && p2.y === y && unitHealth(u) > 0;
49004
+ })) {
48740
49005
  setUnits(
48741
49006
  (prev) => prev.map(
48742
- (u) => u.id === selectedUnitId ? { ...u, position: { x, y } } : u
49007
+ (u) => str(u.id) === selectedUnitId ? { ...u, position: { x, y } } : u
48743
49008
  )
48744
49009
  );
48745
49010
  setPhase("action");
@@ -48782,12 +49047,13 @@ var init_useBattleState = __esm({
48782
49047
  "components/game/organisms/hooks/useBattleState.ts"() {
48783
49048
  "use client";
48784
49049
  init_useEventBus();
49050
+ init_boardEntity();
48785
49051
  }
48786
49052
  });
48787
49053
  function UncontrolledBattleBoard({ entity, ...rest }) {
48788
- const resolved = Array.isArray(entity) ? entity[0] : entity;
49054
+ const resolved = boardEntity(entity);
48789
49055
  const battleState = useBattleState(
48790
- resolved?.initialUnits ?? [],
49056
+ rows(resolved?.initialUnits),
48791
49057
  {
48792
49058
  tileClickEvent: rest.tileClickEvent,
48793
49059
  unitClickEvent: rest.unitClickEvent,
@@ -48823,10 +49089,23 @@ function UncontrolledBattleBoard({ entity, ...rest }) {
48823
49089
  var init_UncontrolledBattleBoard = __esm({
48824
49090
  "components/game/organisms/UncontrolledBattleBoard.tsx"() {
48825
49091
  init_BattleBoard();
49092
+ init_boardEntity();
48826
49093
  init_useBattleState();
48827
49094
  UncontrolledBattleBoard.displayName = "UncontrolledBattleBoard";
48828
49095
  }
48829
49096
  });
49097
+ function heroPosition(h) {
49098
+ return vec2(h.position);
49099
+ }
49100
+ function heroOwner(h) {
49101
+ return str(h.owner);
49102
+ }
49103
+ function heroMovement(h) {
49104
+ return num(h.movement);
49105
+ }
49106
+ function hexPassable(h) {
49107
+ return h.passable !== false;
49108
+ }
48830
49109
  function defaultIsInRange(from, to, range) {
48831
49110
  return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
48832
49111
  }
@@ -48857,36 +49136,36 @@ function WorldMapBoard({
48857
49136
  className
48858
49137
  }) {
48859
49138
  const eventBus = useEventBus();
48860
- const resolved = Array.isArray(entity) ? entity[0] : entity;
48861
- const hexes = resolved?.hexes ?? [];
48862
- const heroes = resolved?.heroes ?? [];
48863
- const features = resolved?.features ?? [];
48864
- const selectedHeroId = resolved?.selectedHeroId;
49139
+ const resolved = boardEntity(entity);
49140
+ const hexes = rows(resolved?.hexes);
49141
+ const heroes = rows(resolved?.heroes);
49142
+ const features = Array.isArray(resolved?.features) ? resolved.features : [];
49143
+ const selectedHeroId = resolved?.selectedHeroId ?? null;
48865
49144
  const assetManifest = resolved?.assetManifest;
48866
49145
  const backgroundImage = resolved?.backgroundImage;
48867
49146
  const [hoveredTile, setHoveredTile] = React97.useState(null);
48868
49147
  const selectedHero = React97.useMemo(
48869
- () => heroes.find((h) => h.id === selectedHeroId) ?? null,
49148
+ () => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
48870
49149
  [heroes, selectedHeroId]
48871
49150
  );
48872
49151
  const tiles = React97.useMemo(
48873
49152
  () => hexes.map((hex) => ({
48874
- x: hex.x,
48875
- y: hex.y,
48876
- terrain: hex.terrain,
48877
- terrainSprite: hex.terrainSprite
49153
+ x: num(hex.x),
49154
+ y: num(hex.y),
49155
+ terrain: str(hex.terrain),
49156
+ terrainSprite: hex.terrainSprite == null ? void 0 : str(hex.terrainSprite)
48878
49157
  })),
48879
49158
  [hexes]
48880
49159
  );
48881
49160
  const baseUnits = React97.useMemo(
48882
49161
  () => heroes.map((hero) => ({
48883
- id: hero.id,
48884
- position: hero.position,
48885
- name: hero.name,
48886
- team: hero.owner === "enemy" ? "enemy" : "player",
49162
+ id: str(hero.id),
49163
+ position: heroPosition(hero),
49164
+ name: str(hero.name),
49165
+ team: heroOwner(hero) === "enemy" ? "enemy" : "player",
48887
49166
  health: 100,
48888
49167
  maxHealth: 100,
48889
- sprite: hero.sprite
49168
+ sprite: hero.sprite == null ? void 0 : str(hero.sprite)
48890
49169
  })),
48891
49170
  [heroes]
48892
49171
  );
@@ -48927,73 +49206,94 @@ function WorldMapBoard({
48927
49206
  const isoUnits = React97.useMemo(() => {
48928
49207
  if (movingPositions.size === 0) return baseUnits;
48929
49208
  return baseUnits.map((u) => {
48930
- const pos = movingPositions.get(u.id);
49209
+ const pos = u.id == null ? void 0 : movingPositions.get(u.id);
48931
49210
  return pos ? { ...u, position: pos } : u;
48932
49211
  });
48933
49212
  }, [baseUnits, movingPositions]);
48934
49213
  const validMoves = React97.useMemo(() => {
48935
- if (!selectedHero || selectedHero.movement <= 0) return [];
49214
+ if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
49215
+ const sp = heroPosition(selectedHero);
49216
+ const sOwner = heroOwner(selectedHero);
49217
+ const range = heroMovement(selectedHero);
48936
49218
  const moves = [];
48937
49219
  hexes.forEach((hex) => {
48938
- if (hex.passable === false) return;
48939
- if (hex.x === selectedHero.position.x && hex.y === selectedHero.position.y) return;
48940
- if (!isInRange(selectedHero.position, { x: hex.x, y: hex.y }, selectedHero.movement)) return;
48941
- if (heroes.some((h) => h.position.x === hex.x && h.position.y === hex.y && h.owner === selectedHero.owner)) return;
48942
- moves.push({ x: hex.x, y: hex.y });
49220
+ const hx = num(hex.x);
49221
+ const hy = num(hex.y);
49222
+ if (!hexPassable(hex)) return;
49223
+ if (hx === sp.x && hy === sp.y) return;
49224
+ if (!isInRange(sp, { x: hx, y: hy }, range)) return;
49225
+ if (heroes.some((h) => {
49226
+ const hp = heroPosition(h);
49227
+ return hp.x === hx && hp.y === hy && heroOwner(h) === sOwner;
49228
+ })) return;
49229
+ moves.push({ x: hx, y: hy });
48943
49230
  });
48944
49231
  return moves;
48945
49232
  }, [selectedHero, hexes, heroes, isInRange]);
48946
49233
  const attackTargets = React97.useMemo(() => {
48947
- if (!selectedHero || selectedHero.movement <= 0) return [];
48948
- return heroes.filter((h) => h.owner !== selectedHero.owner).filter((h) => isInRange(selectedHero.position, h.position, selectedHero.movement)).map((h) => h.position);
49234
+ if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
49235
+ const sp = heroPosition(selectedHero);
49236
+ const sOwner = heroOwner(selectedHero);
49237
+ const range = heroMovement(selectedHero);
49238
+ return heroes.filter((h) => heroOwner(h) !== sOwner).filter((h) => isInRange(sp, heroPosition(h), range)).map((h) => heroPosition(h));
48949
49239
  }, [selectedHero, heroes, isInRange]);
48950
- const maxY = Math.max(...hexes.map((h) => h.y), 0);
49240
+ const maxY = Math.max(...hexes.map((h) => num(h.y)), 0);
48951
49241
  const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
48952
49242
  const tileToScreen = React97.useCallback(
48953
49243
  (tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
48954
49244
  [scale, baseOffsetX]
48955
49245
  );
48956
49246
  const hoveredHex = React97.useMemo(
48957
- () => hoveredTile ? hexes.find((h) => h.x === hoveredTile.x && h.y === hoveredTile.y) ?? null : null,
49247
+ () => hoveredTile ? hexes.find((h) => num(h.x) === hoveredTile.x && num(h.y) === hoveredTile.y) ?? null : null,
48958
49248
  [hoveredTile, hexes]
48959
49249
  );
48960
49250
  const hoveredHero = React97.useMemo(
48961
- () => hoveredTile ? heroes.find((h) => h.position.x === hoveredTile.x && h.position.y === hoveredTile.y) ?? null : null,
49251
+ () => hoveredTile ? heroes.find((h) => {
49252
+ const hp = heroPosition(h);
49253
+ return hp.x === hoveredTile.x && hp.y === hoveredTile.y;
49254
+ }) ?? null : null,
48962
49255
  [hoveredTile, heroes]
48963
49256
  );
48964
49257
  const handleTileClick = React97.useCallback((x, y) => {
48965
49258
  if (movementAnimRef.current) return;
48966
- const hex = hexes.find((h) => h.x === x && h.y === y);
49259
+ const hex = hexes.find((h) => num(h.x) === x && num(h.y) === y);
48967
49260
  if (!hex) return;
48968
49261
  if (tileClickEvent) {
48969
49262
  eventBus.emit(`UI:${tileClickEvent}`, { x, y });
48970
49263
  }
48971
49264
  if (selectedHero && validMoves.some((m) => m.x === x && m.y === y)) {
48972
- startMoveAnimation(selectedHero.id, { ...selectedHero.position }, { x, y }, () => {
48973
- onHeroMove?.(selectedHero.id, x, y);
49265
+ const heroId = str(selectedHero.id);
49266
+ startMoveAnimation(heroId, { ...heroPosition(selectedHero) }, { x, y }, () => {
49267
+ onHeroMove?.(heroId, x, y);
48974
49268
  if (heroMoveEvent) {
48975
- eventBus.emit(`UI:${heroMoveEvent}`, { heroId: selectedHero.id, toX: x, toY: y });
49269
+ eventBus.emit(`UI:${heroMoveEvent}`, { heroId, toX: x, toY: y });
48976
49270
  }
48977
- if (hex.feature && hex.feature !== "none") {
48978
- onFeatureEnter?.(selectedHero.id, hex);
49271
+ const feature = str(hex.feature);
49272
+ if (feature && feature !== "none") {
49273
+ onFeatureEnter?.(heroId, hex);
48979
49274
  if (featureEnterEvent) {
48980
- eventBus.emit(`UI:${featureEnterEvent}`, { heroId: selectedHero.id, feature: hex.feature, hex });
49275
+ eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex });
48981
49276
  }
48982
49277
  }
48983
49278
  });
48984
49279
  return;
48985
49280
  }
48986
- const enemy = heroes.find((h) => h.position.x === x && h.position.y === y && h.owner === "enemy");
49281
+ const enemy = heroes.find((h) => {
49282
+ const hp = heroPosition(h);
49283
+ return hp.x === x && hp.y === y && heroOwner(h) === "enemy";
49284
+ });
48987
49285
  if (selectedHero && enemy && attackTargets.some((t) => t.x === x && t.y === y)) {
48988
- onBattleEncounter?.(selectedHero.id, enemy.id);
49286
+ const attackerId = str(selectedHero.id);
49287
+ const defenderId = str(enemy.id);
49288
+ onBattleEncounter?.(attackerId, defenderId);
48989
49289
  if (battleEncounterEvent) {
48990
- eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId: selectedHero.id, defenderId: enemy.id });
49290
+ eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId, defenderId });
48991
49291
  }
48992
49292
  }
48993
49293
  }, [hexes, heroes, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
48994
49294
  const handleUnitClick = React97.useCallback((unitId) => {
48995
- const hero = heroes.find((h) => h.id === unitId);
48996
- if (hero && (hero.owner === "player" || allowMoveAllHeroes)) {
49295
+ const hero = heroes.find((h) => str(h.id) === unitId);
49296
+ if (hero && (heroOwner(hero) === "player" || allowMoveAllHeroes)) {
48997
49297
  onHeroSelect?.(unitId);
48998
49298
  if (heroSelectEvent) {
48999
49299
  eventBus.emit(`UI:${heroSelectEvent}`, { heroId: unitId });
@@ -49066,6 +49366,7 @@ var init_WorldMapBoard = __esm({
49066
49366
  init_Stack();
49067
49367
  init_LoadingState();
49068
49368
  init_IsometricCanvas2();
49369
+ init_boardEntity();
49069
49370
  init_isometric();
49070
49371
  WorldMapBoard.displayName = "WorldMapBoard";
49071
49372
  }
@@ -52629,10 +52930,10 @@ function parseApplicationLevel(schema) {
52629
52930
  }
52630
52931
  const count = schema.orbitals.length;
52631
52932
  const cols = Math.ceil(Math.sqrt(count));
52632
- const rows = Math.ceil(count / cols);
52933
+ const rows2 = Math.ceil(count / cols);
52633
52934
  const spacing = 200;
52634
52935
  const gridW = cols * spacing;
52635
- const gridH = rows * spacing;
52936
+ const gridH = rows2 * spacing;
52636
52937
  const originX = (600 - gridW) / 2 + spacing / 2;
52637
52938
  const originY = (400 - gridH) / 2 + spacing / 2;
52638
52939
  schema.orbitals.forEach((orbital, i) => {
@@ -55774,11 +56075,11 @@ function buildMockData(schema) {
55774
56075
  result[entityName] = entity.instances;
55775
56076
  continue;
55776
56077
  }
55777
- const rows = Array.from(
56078
+ const rows2 = Array.from(
55778
56079
  { length: 10 },
55779
56080
  (_, i) => generateEntityRow(entity, i + 1)
55780
56081
  );
55781
- result[entityName] = rows;
56082
+ result[entityName] = rows2;
55782
56083
  }
55783
56084
  for (const orbital of schema.orbitals) {
55784
56085
  for (const traitRef of orbital.traits ?? []) {
@@ -59277,18 +59578,18 @@ function layoutOrbitals(count, containerW, containerH) {
59277
59578
  if (count === 0) return [];
59278
59579
  if (count === 1) return [{ cx: containerW / 2, cy: containerH / 2 }];
59279
59580
  const cols = Math.min(count, Math.ceil(Math.sqrt(count)));
59280
- const rows = Math.ceil(count / cols);
59581
+ const rows2 = Math.ceil(count / cols);
59281
59582
  const edgePad = 24;
59282
59583
  const fitMinCx = UNIT_DISPLAY_W / 2 + edgePad;
59283
59584
  const fitMinCy = UNIT_DISPLAY_H / 2 + edgePad;
59284
59585
  const fitMaxCx = Math.max(fitMinCx, containerW - UNIT_DISPLAY_W / 2 - edgePad);
59285
59586
  const fitMaxCy = Math.max(fitMinCy, containerH - UNIT_DISPLAY_H / 2 - edgePad);
59286
59587
  const fitStepX = cols > 1 ? (fitMaxCx - fitMinCx) / (cols - 1) : 0;
59287
- const fitStepY = rows > 1 ? (fitMaxCy - fitMinCy) / (rows - 1) : 0;
59588
+ const fitStepY = rows2 > 1 ? (fitMaxCy - fitMinCy) / (rows2 - 1) : 0;
59288
59589
  const stepX = Math.min(fitStepX, UNIT_DISPLAY_W * 3.5);
59289
59590
  const stepY = Math.min(fitStepY, UNIT_DISPLAY_H * 3.5);
59290
59591
  const gridW = (cols - 1) * stepX;
59291
- const gridH = (rows - 1) * stepY;
59592
+ const gridH = (rows2 - 1) * stepY;
59292
59593
  const originX = (containerW - gridW) / 2;
59293
59594
  const originY = (containerH - gridH) / 2;
59294
59595
  return Array.from({ length: count }, (_, i) => ({