@almadar/ui 5.21.12 → 5.22.3

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 +2432 -3173
  2. package/dist/avl/index.js +1373 -2114
  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 +2016 -1668
  72. package/dist/components/index.js +1128 -780
  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 +912 -624
  84. package/dist/providers/index.js +912 -624
  85. package/dist/runtime/index.cjs +914 -626
  86. package/dist/runtime/index.js +914 -626
  87. package/package.json +2 -2
@@ -2454,7 +2454,7 @@ var init_SvgGrid = __esm({
2454
2454
  x,
2455
2455
  y,
2456
2456
  cols = 4,
2457
- rows = 3,
2457
+ rows: rows2 = 3,
2458
2458
  spacing = 20,
2459
2459
  nodeRadius = 3,
2460
2460
  color = "var(--color-primary)",
@@ -2463,7 +2463,7 @@ var init_SvgGrid = __esm({
2463
2463
  highlights = []
2464
2464
  }) => {
2465
2465
  const highlightSet = new Set(highlights);
2466
- return /* @__PURE__ */ jsx("g", { className, opacity, children: Array.from({ length: rows }).map(
2466
+ return /* @__PURE__ */ jsx("g", { className, opacity, children: Array.from({ length: rows2 }).map(
2467
2467
  (_, row) => Array.from({ length: cols }).map((_2, col) => {
2468
2468
  const index = row * cols + col;
2469
2469
  const isHighlighted = highlightSet.has(index);
@@ -3123,7 +3123,7 @@ var init_Input = __esm({
3123
3123
  onClear,
3124
3124
  value,
3125
3125
  options,
3126
- rows = 3,
3126
+ rows: rows2 = 3,
3127
3127
  onChange,
3128
3128
  ...props
3129
3129
  }, ref) => {
@@ -3173,7 +3173,7 @@ var init_Input = __esm({
3173
3173
  ref,
3174
3174
  value,
3175
3175
  onChange,
3176
- rows,
3176
+ rows: rows2,
3177
3177
  className: baseClassName,
3178
3178
  ...props
3179
3179
  }
@@ -5429,66 +5429,6 @@ var init_RangeSlider = __esm({
5429
5429
  RangeSlider.displayName = "RangeSlider";
5430
5430
  }
5431
5431
  });
5432
- function easeOut(t) {
5433
- return t * (2 - t);
5434
- }
5435
- var AnimatedCounter;
5436
- var init_AnimatedCounter = __esm({
5437
- "components/marketing/atoms/AnimatedCounter.tsx"() {
5438
- "use client";
5439
- init_cn();
5440
- init_Typography();
5441
- AnimatedCounter = ({
5442
- value: rawValue,
5443
- duration = 600,
5444
- prefix,
5445
- suffix,
5446
- className
5447
- }) => {
5448
- const numericRaw = typeof rawValue === "number" ? rawValue : Number.parseFloat(String(rawValue ?? ""));
5449
- const value = !Number.isNaN(numericRaw) ? numericRaw : 0;
5450
- const [displayValue, setDisplayValue] = useState(value);
5451
- const previousValueRef = useRef(value);
5452
- const animationFrameRef = useRef(null);
5453
- useEffect(() => {
5454
- const from = previousValueRef.current;
5455
- const to = value;
5456
- previousValueRef.current = value;
5457
- if (from === to) {
5458
- setDisplayValue(to);
5459
- return;
5460
- }
5461
- const startTime = performance.now();
5462
- const diff = to - from;
5463
- function animate(currentTime) {
5464
- const elapsed = currentTime - startTime;
5465
- const progress = Math.min(elapsed / duration, 1);
5466
- const easedProgress = easeOut(progress);
5467
- setDisplayValue(from + diff * easedProgress);
5468
- if (progress < 1) {
5469
- animationFrameRef.current = requestAnimationFrame(animate);
5470
- } else {
5471
- setDisplayValue(to);
5472
- }
5473
- }
5474
- animationFrameRef.current = requestAnimationFrame(animate);
5475
- return () => {
5476
- if (animationFrameRef.current !== null) {
5477
- cancelAnimationFrame(animationFrameRef.current);
5478
- }
5479
- };
5480
- }, [value, duration]);
5481
- const decimalPlaces = Number.isInteger(value) ? 0 : String(value).split(".")[1]?.length ?? 0;
5482
- const formattedValue = displayValue.toFixed(decimalPlaces);
5483
- return /* @__PURE__ */ jsxs(Typography, { variant: "h3", className: cn("tabular-nums", className), children: [
5484
- prefix,
5485
- formattedValue,
5486
- suffix
5487
- ] });
5488
- };
5489
- AnimatedCounter.displayName = "AnimatedCounter";
5490
- }
5491
- });
5492
5432
  function useInfiniteScroll(onLoadMore, options = {}) {
5493
5433
  const { rootMargin = "200px", hasMore = true, isLoading = false } = options;
5494
5434
  const observerRef = useRef(null);
@@ -7970,15 +7910,15 @@ function HeaderSkeleton({ className }) {
7970
7910
  ] })
7971
7911
  ] });
7972
7912
  }
7973
- function TableSkeleton({ rows = 5, columns = 4, className }) {
7913
+ function TableSkeleton({ rows: rows2 = 5, columns = 4, className }) {
7974
7914
  return /* @__PURE__ */ jsxs(VStack, { gap: "none", className: cn("border border-border rounded-lg overflow-hidden", className), children: [
7975
7915
  /* @__PURE__ */ jsx(HStack, { className: "px-4 py-3 bg-muted/30 border-b border-border", children: Array.from({ length: columns }).map((_, i) => /* @__PURE__ */ jsx(SkeletonBlock, { className: "h-4 flex-1 mx-2" }, i)) }),
7976
- Array.from({ length: rows }).map((_, rowIdx) => /* @__PURE__ */ jsx(
7916
+ Array.from({ length: rows2 }).map((_, rowIdx) => /* @__PURE__ */ jsx(
7977
7917
  HStack,
7978
7918
  {
7979
7919
  className: cn(
7980
7920
  "px-4 py-3",
7981
- rowIdx < rows - 1 && "border-b border-border"
7921
+ rowIdx < rows2 - 1 && "border-b border-border"
7982
7922
  ),
7983
7923
  children: Array.from({ length: columns }).map((_2, colIdx) => /* @__PURE__ */ jsx(SkeletonLine, { className: "flex-1 mx-2" }, colIdx))
7984
7924
  },
@@ -8026,18 +7966,18 @@ function CardSkeleton({ className }) {
8026
7966
  }
8027
7967
  );
8028
7968
  }
8029
- function TextSkeleton({ rows = 3, className }) {
8030
- return /* @__PURE__ */ jsx(VStack, { gap: "sm", className, children: Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsx(
7969
+ function TextSkeleton({ rows: rows2 = 3, className }) {
7970
+ return /* @__PURE__ */ jsx(VStack, { gap: "sm", className, children: Array.from({ length: rows2 }).map((_, i) => /* @__PURE__ */ jsx(
8031
7971
  SkeletonLine,
8032
7972
  {
8033
- className: i === rows - 1 ? "w-2/3" : "w-full"
7973
+ className: i === rows2 - 1 ? "w-2/3" : "w-full"
8034
7974
  },
8035
7975
  i
8036
7976
  )) });
8037
7977
  }
8038
7978
  function Skeleton({
8039
7979
  variant = "text",
8040
- rows,
7980
+ rows: rows2,
8041
7981
  columns,
8042
7982
  fields,
8043
7983
  className
@@ -8047,15 +7987,15 @@ function Skeleton({
8047
7987
  case "header":
8048
7988
  return /* @__PURE__ */ jsx(HeaderSkeleton, { className });
8049
7989
  case "table":
8050
- return /* @__PURE__ */ jsx(TableSkeleton, { rows, columns, className });
7990
+ return /* @__PURE__ */ jsx(TableSkeleton, { rows: rows2, columns, className });
8051
7991
  case "form":
8052
7992
  return /* @__PURE__ */ jsx(FormSkeleton, { fields, className });
8053
7993
  case "card":
8054
7994
  return /* @__PURE__ */ jsx(CardSkeleton, { className });
8055
7995
  case "text":
8056
- return /* @__PURE__ */ jsx(TextSkeleton, { rows, className });
7996
+ return /* @__PURE__ */ jsx(TextSkeleton, { rows: rows2, className });
8057
7997
  default:
8058
- return /* @__PURE__ */ jsx(TextSkeleton, { rows, className });
7998
+ return /* @__PURE__ */ jsx(TextSkeleton, { rows: rows2, className });
8059
7999
  }
8060
8000
  }
8061
8001
  var pulseClass;
@@ -9683,7 +9623,7 @@ var init_MapView = __esm({
9683
9623
  shadowSize: [41, 41]
9684
9624
  });
9685
9625
  L.Marker.prototype.options.icon = defaultIcon;
9686
- const { useEffect: useEffect71, useRef: useRef66, useCallback: useCallback113, useState: useState103 } = React84__default;
9626
+ const { useEffect: useEffect71, useRef: useRef66, useCallback: useCallback114, useState: useState103 } = React84__default;
9687
9627
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
9688
9628
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
9689
9629
  function MapUpdater({ centerLat, centerLng, zoom }) {
@@ -9729,7 +9669,7 @@ var init_MapView = __esm({
9729
9669
  }) {
9730
9670
  const eventBus = useEventBus3();
9731
9671
  const [clickedPosition, setClickedPosition] = useState103(null);
9732
- const handleMapClick = useCallback113((lat, lng) => {
9672
+ const handleMapClick = useCallback114((lat, lng) => {
9733
9673
  if (showClickedPin) {
9734
9674
  setClickedPosition({ lat, lng });
9735
9675
  }
@@ -9738,7 +9678,7 @@ var init_MapView = __esm({
9738
9678
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
9739
9679
  }
9740
9680
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
9741
- const handleMarkerClick = useCallback113((marker) => {
9681
+ const handleMarkerClick = useCallback114((marker) => {
9742
9682
  onMarkerClick?.(marker);
9743
9683
  if (markerClickEvent) {
9744
9684
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -9959,7 +9899,7 @@ function InputPattern({
9959
9899
  function TextareaPattern({
9960
9900
  value = "",
9961
9901
  placeholder,
9962
- rows = 4,
9902
+ rows: rows2 = 4,
9963
9903
  disabled = false,
9964
9904
  fieldError,
9965
9905
  onChange,
@@ -9979,7 +9919,7 @@ function TextareaPattern({
9979
9919
  {
9980
9920
  value: localValue,
9981
9921
  placeholder,
9982
- rows,
9922
+ rows: rows2,
9983
9923
  disabled,
9984
9924
  error: fieldError,
9985
9925
  onChange: handleChange,
@@ -10464,6 +10404,91 @@ var init_ActionPalette = __esm({
10464
10404
  ActionPalette.displayName = "ActionPalette";
10465
10405
  }
10466
10406
  });
10407
+ function parseValue(value) {
10408
+ if (value === "" || value == null) return { num: 0, prefix: "", suffix: "", decimals: 0 };
10409
+ const match = String(value).match(/^([^0-9]*)([0-9]+(?:\.[0-9]+)?)(.*)$/);
10410
+ if (!match) {
10411
+ return { num: 0, prefix: "", suffix: String(value), decimals: 0 };
10412
+ }
10413
+ const numStr = match[2];
10414
+ const decimalIdx = numStr.indexOf(".");
10415
+ const decimals = decimalIdx >= 0 ? numStr.length - decimalIdx - 1 : 0;
10416
+ return {
10417
+ prefix: match[1],
10418
+ num: parseFloat(numStr),
10419
+ suffix: match[3],
10420
+ decimals
10421
+ };
10422
+ }
10423
+ var AnimatedCounter;
10424
+ var init_AnimatedCounter = __esm({
10425
+ "components/core/molecules/AnimatedCounter.tsx"() {
10426
+ "use client";
10427
+ init_cn();
10428
+ init_Box();
10429
+ init_Typography();
10430
+ AnimatedCounter = ({
10431
+ value,
10432
+ label,
10433
+ duration = 1500,
10434
+ className
10435
+ }) => {
10436
+ const ref = useRef(null);
10437
+ const [displayValue, setDisplayValue] = useState("0");
10438
+ const [hasAnimated, setHasAnimated] = useState(false);
10439
+ const animate = useCallback(() => {
10440
+ const { num: num2, prefix, suffix, decimals } = parseValue(value);
10441
+ if (num2 === 0) {
10442
+ setDisplayValue(String(value));
10443
+ return;
10444
+ }
10445
+ const startTime = performance.now();
10446
+ const tick = (now2) => {
10447
+ const elapsed = now2 - startTime;
10448
+ const progress = Math.min(elapsed / duration, 1);
10449
+ const eased = 1 - Math.pow(1 - progress, 3);
10450
+ const current = eased * num2;
10451
+ setDisplayValue(`${prefix}${current.toFixed(decimals)}${suffix}`);
10452
+ if (progress < 1) {
10453
+ requestAnimationFrame(tick);
10454
+ } else {
10455
+ setDisplayValue(String(value));
10456
+ }
10457
+ };
10458
+ requestAnimationFrame(tick);
10459
+ }, [value, duration]);
10460
+ useEffect(() => {
10461
+ if (hasAnimated) return;
10462
+ const el = ref.current;
10463
+ if (!el) return;
10464
+ const observer2 = new IntersectionObserver(
10465
+ (entries) => {
10466
+ if (entries[0].isIntersecting) {
10467
+ setHasAnimated(true);
10468
+ animate();
10469
+ observer2.disconnect();
10470
+ }
10471
+ },
10472
+ { threshold: 0.3 }
10473
+ );
10474
+ observer2.observe(el);
10475
+ return () => observer2.disconnect();
10476
+ }, [hasAnimated, animate]);
10477
+ return /* @__PURE__ */ jsxs(Box, { ref, className: cn("flex flex-col items-center gap-1 p-4", className), children: [
10478
+ /* @__PURE__ */ jsx(
10479
+ Typography,
10480
+ {
10481
+ variant: "h2",
10482
+ className: "text-primary font-bold tabular-nums",
10483
+ children: hasAnimated ? displayValue : "0"
10484
+ }
10485
+ ),
10486
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "muted", className: "text-center", children: label })
10487
+ ] });
10488
+ };
10489
+ AnimatedCounter.displayName = "AnimatedCounter";
10490
+ }
10491
+ });
10467
10492
  var AuthLayout;
10468
10493
  var init_AuthLayout = __esm({
10469
10494
  "components/core/templates/AuthLayout.tsx"() {
@@ -11817,6 +11842,39 @@ var init_IsometricCanvas2 = __esm({
11817
11842
  init_IsometricCanvas();
11818
11843
  }
11819
11844
  });
11845
+
11846
+ // components/game/organisms/boardEntity.ts
11847
+ function boardEntity(entity) {
11848
+ if (!entity) return void 0;
11849
+ return Array.isArray(entity) ? entity[0] : entity;
11850
+ }
11851
+ function str(v) {
11852
+ return v == null ? "" : String(v);
11853
+ }
11854
+ function num(v, fallback = 0) {
11855
+ const n = Number(v);
11856
+ return Number.isFinite(n) ? n : fallback;
11857
+ }
11858
+ function rows(v) {
11859
+ return Array.isArray(v) ? v : [];
11860
+ }
11861
+ function vec2(v) {
11862
+ const o = v ?? {};
11863
+ return { x: num(o.x), y: num(o.y) };
11864
+ }
11865
+ function unitPosition(u) {
11866
+ return vec2(u.position);
11867
+ }
11868
+ function unitTeam(u) {
11869
+ return str(u.team);
11870
+ }
11871
+ function unitHealth(u) {
11872
+ return num(u.health);
11873
+ }
11874
+ var init_boardEntity = __esm({
11875
+ "components/game/organisms/boardEntity.ts"() {
11876
+ }
11877
+ });
11820
11878
  function BattleBoard({
11821
11879
  entity,
11822
11880
  scale = 0.45,
@@ -11843,43 +11901,49 @@ function BattleBoard({
11843
11901
  attackEvent,
11844
11902
  className
11845
11903
  }) {
11846
- const tiles = entity.tiles;
11847
- const features = entity.features ?? [];
11848
- const boardWidth = entity.boardWidth ?? 8;
11849
- const boardHeight = entity.boardHeight ?? 6;
11850
- const assetManifest = entity.assetManifest;
11851
- const backgroundImage = entity.backgroundImage;
11852
- const units = entity.units;
11853
- const selectedUnitId = entity.selectedUnitId;
11854
- const currentPhase = entity.phase;
11855
- const currentTurn = entity.turn;
11856
- const gameResult = entity.gameResult;
11904
+ const board = boardEntity(entity) ?? {};
11905
+ const tiles = Array.isArray(board.tiles) ? board.tiles : [];
11906
+ const features = Array.isArray(board.features) ? board.features : [];
11907
+ const boardWidth = num(board.boardWidth, 8);
11908
+ const boardHeight = num(board.boardHeight, 6);
11909
+ const assetManifest = board.assetManifest;
11910
+ const backgroundImage = board.backgroundImage;
11911
+ const units = rows(board.units);
11912
+ const selectedUnitId = board.selectedUnitId ?? null;
11913
+ const currentPhase = str(board.phase) || "observation";
11914
+ const currentTurn = num(board.turn, 1);
11915
+ const gameResult = board.gameResult ?? null;
11857
11916
  const eventBus = useEventBus();
11858
11917
  const { t } = useTranslate();
11859
11918
  const [hoveredTile, setHoveredTile] = useState(null);
11860
11919
  const [isShaking, setIsShaking] = useState(false);
11861
11920
  const selectedUnit = useMemo(
11862
- () => units.find((u) => u.id === selectedUnitId) ?? null,
11921
+ () => units.find((u) => str(u.id) === selectedUnitId) ?? null,
11863
11922
  [units, selectedUnitId]
11864
11923
  );
11865
11924
  const hoveredUnit = useMemo(() => {
11866
11925
  if (!hoveredTile) return null;
11867
- return units.find(
11868
- (u) => u.position.x === hoveredTile.x && u.position.y === hoveredTile.y && u.health > 0
11869
- ) ?? null;
11926
+ return units.find((u) => {
11927
+ const p2 = unitPosition(u);
11928
+ return p2.x === hoveredTile.x && p2.y === hoveredTile.y && unitHealth(u) > 0;
11929
+ }) ?? null;
11870
11930
  }, [hoveredTile, units]);
11871
- const playerUnits = useMemo(() => units.filter((u) => u.team === "player" && u.health > 0), [units]);
11872
- const enemyUnits = useMemo(() => units.filter((u) => u.team === "enemy" && u.health > 0), [units]);
11931
+ const playerUnits = useMemo(() => units.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0), [units]);
11932
+ const enemyUnits = useMemo(() => units.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0), [units]);
11873
11933
  const validMoves = useMemo(() => {
11874
11934
  if (!selectedUnit || currentPhase !== "movement") return [];
11875
11935
  const moves = [];
11876
- const range = selectedUnit.movement;
11936
+ const range = num(selectedUnit.movement);
11937
+ const origin = unitPosition(selectedUnit);
11877
11938
  for (let dy = -range; dy <= range; dy++) {
11878
11939
  for (let dx = -range; dx <= range; dx++) {
11879
- const nx = selectedUnit.position.x + dx;
11880
- const ny = selectedUnit.position.y + dy;
11940
+ const nx = origin.x + dx;
11941
+ const ny = origin.y + dy;
11881
11942
  const dist = Math.abs(dx) + Math.abs(dy);
11882
- 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)) {
11943
+ if (dist > 0 && dist <= range && nx >= 0 && nx < boardWidth && ny >= 0 && ny < boardHeight && !units.some((u) => {
11944
+ const p2 = unitPosition(u);
11945
+ return p2.x === nx && p2.y === ny && unitHealth(u) > 0;
11946
+ })) {
11883
11947
  moves.push({ x: nx, y: ny });
11884
11948
  }
11885
11949
  }
@@ -11888,11 +11952,14 @@ function BattleBoard({
11888
11952
  }, [selectedUnit, currentPhase, units, boardWidth, boardHeight]);
11889
11953
  const attackTargets = useMemo(() => {
11890
11954
  if (!selectedUnit || currentPhase !== "action") return [];
11891
- return units.filter((u) => u.team !== selectedUnit.team && u.health > 0).filter((u) => {
11892
- const dx = Math.abs(u.position.x - selectedUnit.position.x);
11893
- const dy = Math.abs(u.position.y - selectedUnit.position.y);
11955
+ const sp = unitPosition(selectedUnit);
11956
+ const sTeam = unitTeam(selectedUnit);
11957
+ return units.filter((u) => unitTeam(u) !== sTeam && unitHealth(u) > 0).filter((u) => {
11958
+ const p2 = unitPosition(u);
11959
+ const dx = Math.abs(p2.x - sp.x);
11960
+ const dy = Math.abs(p2.y - sp.y);
11894
11961
  return dx <= 1 && dy <= 1 && dx + dy > 0;
11895
- }).map((u) => u.position);
11962
+ }).map((u) => unitPosition(u));
11896
11963
  }, [selectedUnit, currentPhase, units]);
11897
11964
  const MOVE_SPEED_MS_PER_TILE = 300;
11898
11965
  const movementAnimRef = useRef(null);
@@ -11932,23 +11999,25 @@ function BattleBoard({
11932
11999
  return () => clearInterval(interval);
11933
12000
  }, []);
11934
12001
  const isoUnits = useMemo(() => {
11935
- return units.filter((u) => u.health > 0).map((unit) => {
11936
- const pos = movingPositions.get(unit.id) ?? unit.position;
12002
+ return units.filter((u) => unitHealth(u) > 0).map((unit) => {
12003
+ const id = str(unit.id);
12004
+ const pos = movingPositions.get(id) ?? unitPosition(unit);
12005
+ const unitTraits = Array.isArray(unit.traits) ? unit.traits : void 0;
11937
12006
  return {
11938
- id: unit.id,
12007
+ id,
11939
12008
  position: pos,
11940
- name: unit.name,
11941
- team: unit.team,
11942
- health: unit.health,
11943
- maxHealth: unit.maxHealth,
11944
- unitType: unit.unitType,
11945
- heroId: unit.heroId,
11946
- sprite: unit.sprite,
11947
- traits: unit.traits?.map((t2) => ({
11948
- name: t2.name,
11949
- currentState: t2.currentState,
11950
- states: t2.states,
11951
- cooldown: t2.cooldown ?? 0
12009
+ name: str(unit.name),
12010
+ team: unitTeam(unit),
12011
+ health: unitHealth(unit),
12012
+ maxHealth: num(unit.maxHealth),
12013
+ unitType: unit.unitType == null ? void 0 : str(unit.unitType),
12014
+ heroId: unit.heroId == null ? void 0 : str(unit.heroId),
12015
+ sprite: unit.sprite == null ? void 0 : str(unit.sprite),
12016
+ traits: unitTraits?.map((tr) => ({
12017
+ name: tr.name,
12018
+ currentState: tr.currentState,
12019
+ states: tr.states,
12020
+ cooldown: tr.cooldown ?? 0
11952
12021
  }))
11953
12022
  };
11954
12023
  });
@@ -11960,8 +12029,8 @@ function BattleBoard({
11960
12029
  [scale, baseOffsetX]
11961
12030
  );
11962
12031
  const checkGameEnd = useCallback(() => {
11963
- const pa = units.filter((u) => u.team === "player" && u.health > 0);
11964
- const ea = units.filter((u) => u.team === "enemy" && u.health > 0);
12032
+ const pa = units.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
12033
+ const ea = units.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
11965
12034
  if (pa.length === 0) {
11966
12035
  onGameEnd?.("defeat");
11967
12036
  if (gameEndEvent) {
@@ -11975,21 +12044,22 @@ function BattleBoard({
11975
12044
  }
11976
12045
  }, [units, onGameEnd, gameEndEvent, eventBus]);
11977
12046
  const handleUnitClick = useCallback((unitId) => {
11978
- const unit = units.find((u) => u.id === unitId);
12047
+ const unit = units.find((u) => str(u.id) === unitId);
11979
12048
  if (!unit) return;
11980
12049
  if (unitClickEvent) {
11981
12050
  eventBus.emit(`UI:${unitClickEvent}`, { unitId });
11982
12051
  }
11983
12052
  if (currentPhase === "action" && selectedUnit) {
11984
- if (unit.team === "enemy" && attackTargets.some((t2) => t2.x === unit.position.x && t2.y === unit.position.y)) {
11985
- const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, selectedUnit.attack - unit.defense);
12053
+ const up = unitPosition(unit);
12054
+ if (unitTeam(unit) === "enemy" && attackTargets.some((t2) => t2.x === up.x && t2.y === up.y)) {
12055
+ const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, num(selectedUnit.attack) - num(unit.defense));
11986
12056
  setIsShaking(true);
11987
12057
  setTimeout(() => setIsShaking(false), 300);
11988
12058
  onAttack?.(selectedUnit, unit, damage);
11989
12059
  if (attackEvent) {
11990
12060
  eventBus.emit(`UI:${attackEvent}`, {
11991
- attackerId: selectedUnit.id,
11992
- targetId: unit.id,
12061
+ attackerId: str(selectedUnit.id),
12062
+ targetId: str(unit.id),
11993
12063
  damage
11994
12064
  });
11995
12065
  }
@@ -12004,9 +12074,9 @@ function BattleBoard({
12004
12074
  if (currentPhase === "movement" && selectedUnit) {
12005
12075
  if (movementAnimRef.current) return;
12006
12076
  if (validMoves.some((m) => m.x === x && m.y === y)) {
12007
- const from = { ...selectedUnit.position };
12077
+ const from = { ...unitPosition(selectedUnit) };
12008
12078
  const to = { x, y };
12009
- startMoveAnimation(selectedUnit.id, from, to, () => {
12079
+ startMoveAnimation(str(selectedUnit.id), from, to, () => {
12010
12080
  onUnitMove?.(selectedUnit, to);
12011
12081
  });
12012
12082
  }
@@ -12164,6 +12234,7 @@ var init_BattleBoard = __esm({
12164
12234
  init_Typography();
12165
12235
  init_Stack();
12166
12236
  init_IsometricCanvas2();
12237
+ init_boardEntity();
12167
12238
  init_isometric();
12168
12239
  BattleBoard.displayName = "BattleBoard";
12169
12240
  }
@@ -13386,24 +13457,24 @@ var init_CodeBlock = __esm({
13386
13457
  return;
13387
13458
  }
13388
13459
  lineEls.forEach((el) => {
13389
- const num = parseInt(el.getAttribute("data-line") ?? "-1", 10);
13390
- if (hiddenLines.has(num)) {
13460
+ const num2 = parseInt(el.getAttribute("data-line") ?? "-1", 10);
13461
+ if (hiddenLines.has(num2)) {
13391
13462
  el.style.display = "none";
13392
13463
  return;
13393
13464
  }
13394
13465
  el.style.display = "";
13395
13466
  el.style.position = "relative";
13396
13467
  el.style.paddingLeft = "1.2em";
13397
- const region = foldStartMap.get(num);
13468
+ const region = foldStartMap.get(num2);
13398
13469
  if (!region) return;
13399
- const isCollapsed = collapsed.has(num);
13470
+ const isCollapsed = collapsed.has(num2);
13400
13471
  const toggle = document.createElement("span");
13401
13472
  toggle.className = "fold-toggle";
13402
13473
  toggle.textContent = isCollapsed ? "\u25B6" : "\u25BC";
13403
13474
  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%";
13404
13475
  toggle.addEventListener("click", (e) => {
13405
13476
  e.stopPropagation();
13406
- toggleFoldRef.current(num);
13477
+ toggleFoldRef.current(num2);
13407
13478
  });
13408
13479
  el.insertBefore(toggle, el.firstChild);
13409
13480
  if (isCollapsed) {
@@ -15656,10 +15727,13 @@ var init_BookChapterView = __esm({
15656
15727
  init_cn();
15657
15728
  BookChapterView = ({
15658
15729
  chapter,
15730
+ orbitalSchema,
15659
15731
  direction,
15660
15732
  className
15661
15733
  }) => {
15662
15734
  const { t: _t } = useTranslate();
15735
+ const title = String(chapter.title ?? "");
15736
+ const content = String(chapter.content ?? "");
15663
15737
  return /* @__PURE__ */ jsxs(
15664
15738
  VStack,
15665
15739
  {
@@ -15667,16 +15741,16 @@ var init_BookChapterView = __esm({
15667
15741
  className: cn("px-6 py-8 max-w-4xl mx-auto w-full", className),
15668
15742
  style: { direction },
15669
15743
  children: [
15670
- /* @__PURE__ */ jsx(Typography, { variant: "h1", className: "text-3xl font-bold", children: chapter.title }),
15744
+ /* @__PURE__ */ jsx(Typography, { variant: "h1", className: "text-3xl font-bold", children: title }),
15671
15745
  /* @__PURE__ */ jsx(Divider, {}),
15672
- !!chapter.orbitalSchema && /* @__PURE__ */ jsx(ScaledDiagram, { children: /* @__PURE__ */ jsx(
15746
+ !!orbitalSchema && /* @__PURE__ */ jsx(ScaledDiagram, { children: /* @__PURE__ */ jsx(
15673
15747
  JazariStateMachine,
15674
15748
  {
15675
- schema: chapter.orbitalSchema,
15749
+ schema: orbitalSchema,
15676
15750
  direction
15677
15751
  }
15678
15752
  ) }),
15679
- /* @__PURE__ */ jsx(ContentRenderer, { content: chapter.content, direction })
15753
+ /* @__PURE__ */ jsx(ContentRenderer, { content, direction })
15680
15754
  ]
15681
15755
  }
15682
15756
  );
@@ -15774,7 +15848,7 @@ var init_BookNavBar = __esm({
15774
15848
  BookNavBar = ({
15775
15849
  currentPage,
15776
15850
  totalPages,
15777
- chapterTitle,
15851
+ chapterTitle: chapterTitle2,
15778
15852
  direction,
15779
15853
  className
15780
15854
  }) => {
@@ -15815,12 +15889,12 @@ var init_BookNavBar = __esm({
15815
15889
  )
15816
15890
  ] }),
15817
15891
  /* @__PURE__ */ jsxs(Box, { className: "flex-1 mx-4 max-w-md", children: [
15818
- chapterTitle && /* @__PURE__ */ jsx(
15892
+ chapterTitle2 && /* @__PURE__ */ jsx(
15819
15893
  Typography,
15820
15894
  {
15821
15895
  variant: "caption",
15822
15896
  className: "text-center block truncate text-muted-foreground",
15823
- children: chapterTitle
15897
+ children: chapterTitle2
15824
15898
  }
15825
15899
  ),
15826
15900
  /* @__PURE__ */ jsx(ProgressBar, { value: progress, size: "sm", variant: "primary" })
@@ -15887,31 +15961,35 @@ var init_BookTableOfContents = __esm({
15887
15961
  style: { direction },
15888
15962
  children: [
15889
15963
  /* @__PURE__ */ jsx(Typography, { variant: "h1", className: "text-3xl font-bold text-center mb-4", children: t("book.tableOfContents") }),
15890
- parts.map((part, partIdx) => /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
15891
- /* @__PURE__ */ jsxs(HStack, { gap: "sm", align: "center", children: [
15892
- /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: t("book.partNumber", { number: String(partIdx + 1) }) }),
15893
- /* @__PURE__ */ jsx(Typography, { variant: "h3", className: "font-semibold", children: part.title })
15894
- ] }),
15895
- /* @__PURE__ */ jsx(VStack, { gap: "xs", className: direction === "rtl" ? "pr-6" : "pl-6", children: part.chapters.map((chapter) => {
15896
- const isCurrent = chapter.id === currentChapterId;
15897
- return /* @__PURE__ */ jsx(
15898
- Button,
15899
- {
15900
- variant: "ghost",
15901
- size: "sm",
15902
- action: "BOOK_NAVIGATE",
15903
- actionPayload: { chapterId: chapter.id },
15904
- className: cn(
15905
- "justify-start text-left w-full",
15906
- direction === "rtl" && "text-right",
15907
- isCurrent && "bg-blue-50 dark:bg-blue-950 text-blue-600 dark:text-blue-400"
15908
- ),
15909
- children: /* @__PURE__ */ jsx(Box, { className: "truncate", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: chapter.title }) })
15910
- },
15911
- chapter.id
15912
- );
15913
- }) })
15914
- ] }, partIdx))
15964
+ parts.map((part, partIdx) => {
15965
+ const chapters = Array.isArray(part.chapters) ? part.chapters : [];
15966
+ return /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
15967
+ /* @__PURE__ */ jsxs(HStack, { gap: "sm", align: "center", children: [
15968
+ /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: t("book.partNumber", { number: String(partIdx + 1) }) }),
15969
+ /* @__PURE__ */ jsx(Typography, { variant: "h3", className: "font-semibold", children: String(part.title ?? "") })
15970
+ ] }),
15971
+ /* @__PURE__ */ jsx(VStack, { gap: "xs", className: direction === "rtl" ? "pr-6" : "pl-6", children: chapters.map((chapter) => {
15972
+ const id = chapter.id == null ? "" : String(chapter.id);
15973
+ const isCurrent = id === currentChapterId;
15974
+ return /* @__PURE__ */ jsx(
15975
+ Button,
15976
+ {
15977
+ variant: "ghost",
15978
+ size: "sm",
15979
+ action: "BOOK_NAVIGATE",
15980
+ actionPayload: { chapterId: id },
15981
+ className: cn(
15982
+ "justify-start text-left w-full",
15983
+ direction === "rtl" && "text-right",
15984
+ isCurrent && "bg-blue-50 dark:bg-blue-950 text-blue-600 dark:text-blue-400"
15985
+ ),
15986
+ children: /* @__PURE__ */ jsx(Box, { className: "truncate", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: String(chapter.title ?? "") }) })
15987
+ },
15988
+ id
15989
+ );
15990
+ }) })
15991
+ ] }, partIdx);
15992
+ })
15915
15993
  ]
15916
15994
  }
15917
15995
  );
@@ -16033,27 +16111,41 @@ function resolveFieldMap(fieldMap) {
16033
16111
  function get(obj, key) {
16034
16112
  return obj[key];
16035
16113
  }
16114
+ function asStr(v) {
16115
+ return v == null ? "" : String(v);
16116
+ }
16036
16117
  function mapBookData(raw, fields = IDENTITY_BOOK_FIELDS) {
16037
16118
  const rawParts = get(raw, fields.parts) ?? [];
16038
- return {
16039
- title: get(raw, fields.title) ?? "",
16040
- subtitle: get(raw, fields.subtitle),
16041
- author: get(raw, fields.author),
16042
- coverImageUrl: get(raw, fields.coverImageUrl),
16043
- direction: get(raw, fields.direction) ?? void 0,
16044
- parts: rawParts.map((part) => {
16045
- const rawChapters = get(part, fields.chapters) ?? [];
16046
- return {
16047
- title: get(part, fields.partTitle) ?? "",
16048
- chapters: rawChapters.map((ch) => ({
16049
- id: get(ch, fields.chapterId) ?? "",
16050
- title: get(ch, fields.chapterTitle) ?? "",
16051
- content: get(ch, fields.chapterContent) ?? "",
16052
- orbitalSchema: get(ch, fields.chapterOrbitalSchema)
16053
- }))
16054
- };
16055
- })
16119
+ const direction = get(raw, fields.direction) ?? "ltr";
16120
+ const cover = {
16121
+ title: asStr(get(raw, fields.title)),
16122
+ subtitle: asStr(get(raw, fields.subtitle)),
16123
+ author: asStr(get(raw, fields.author)),
16124
+ coverImageUrl: asStr(get(raw, fields.coverImageUrl)),
16125
+ direction
16056
16126
  };
16127
+ const schemaByChapterId = {};
16128
+ const chapters = [];
16129
+ const parts = rawParts.map((part) => {
16130
+ const rawChapters = get(part, fields.chapters) ?? [];
16131
+ const chapterRows = rawChapters.map((ch) => {
16132
+ const id = asStr(get(ch, fields.chapterId));
16133
+ const schema = get(ch, fields.chapterOrbitalSchema);
16134
+ if (schema) schemaByChapterId[id] = schema;
16135
+ const row = {
16136
+ id,
16137
+ title: asStr(get(ch, fields.chapterTitle)),
16138
+ content: asStr(get(ch, fields.chapterContent))
16139
+ };
16140
+ chapters.push(row);
16141
+ return row;
16142
+ });
16143
+ return {
16144
+ title: asStr(get(part, fields.partTitle)),
16145
+ chapters: chapterRows
16146
+ };
16147
+ });
16148
+ return { cover, direction, parts, chapters, schemaByChapterId };
16057
16149
  }
16058
16150
  var IDENTITY_BOOK_FIELDS, AR_BOOK_FIELDS, FIELD_MAP_REGISTRY;
16059
16151
  var init_types2 = __esm({
@@ -16091,10 +16183,7 @@ var init_types2 = __esm({
16091
16183
  };
16092
16184
  }
16093
16185
  });
16094
- function flattenChapters(book) {
16095
- return book.parts.flatMap((part) => part.chapters);
16096
- }
16097
- var PRINT_STYLES, BookViewer;
16186
+ var chapterId, chapterTitle, PRINT_STYLES, BookViewer;
16098
16187
  var init_BookViewer = __esm({
16099
16188
  "components/marketing/organisms/book/BookViewer.tsx"() {
16100
16189
  init_Box();
@@ -16107,6 +16196,8 @@ var init_BookViewer = __esm({
16107
16196
  init_BookNavBar();
16108
16197
  init_EmptyState();
16109
16198
  init_types2();
16199
+ chapterId = (ch) => ch?.id == null ? void 0 : String(ch.id);
16200
+ chapterTitle = (ch) => ch?.title == null ? void 0 : String(ch.title);
16110
16201
  PRINT_STYLES = `
16111
16202
  @media print {
16112
16203
  .book-viewer-page {
@@ -16135,14 +16226,14 @@ var init_BookViewer = __esm({
16135
16226
  return mapBookData(raw, resolvedFieldMap);
16136
16227
  }, [entity, resolvedFieldMap]);
16137
16228
  const direction = book?.direction ?? "ltr";
16138
- const chapters = useMemo(() => book ? flattenChapters(book) : [], [book]);
16229
+ const chapters = useMemo(() => book ? book.chapters : [], [book]);
16139
16230
  const totalPages = 2 + chapters.length;
16140
16231
  const navigateTo = useCallback(
16141
16232
  (page) => {
16142
16233
  const clamped = Math.max(0, Math.min(page, totalPages - 1));
16143
16234
  setCurrentPage(clamped);
16144
- const chapterId = clamped >= 2 ? chapters[clamped - 2]?.id : void 0;
16145
- eventBus.emit("UI:BOOK_PAGE_CHANGE", { pageIndex: clamped, chapterId });
16235
+ const id = clamped >= 2 ? chapterId(chapters[clamped - 2]) : void 0;
16236
+ eventBus.emit("UI:BOOK_PAGE_CHANGE", { pageIndex: clamped, chapterId: id });
16146
16237
  },
16147
16238
  [totalPages, chapters, eventBus]
16148
16239
  );
@@ -16154,8 +16245,8 @@ var init_BookViewer = __esm({
16154
16245
  eventBus.on("UI:BOOK_PAGE_NEXT", () => navigateTo(currentPage + 1)),
16155
16246
  eventBus.on("UI:BOOK_PRINT", () => window.print()),
16156
16247
  eventBus.on("UI:BOOK_NAVIGATE", (event) => {
16157
- const chapterId = event.payload?.chapterId;
16158
- const idx = chapters.findIndex((ch) => ch.id === chapterId);
16248
+ const targetId = event.payload?.chapterId;
16249
+ const idx = chapters.findIndex((ch) => chapterId(ch) === targetId);
16159
16250
  if (idx >= 0) navigateTo(idx + 2);
16160
16251
  })
16161
16252
  ];
@@ -16172,9 +16263,11 @@ var init_BookViewer = __esm({
16172
16263
  style.remove();
16173
16264
  };
16174
16265
  }, []);
16175
- const currentChapterId = currentPage >= 2 ? chapters[currentPage - 2]?.id : void 0;
16176
- const currentChapterTitle = currentPage >= 2 ? chapters[currentPage - 2]?.title : void 0;
16266
+ const currentChapterId = currentPage >= 2 ? chapterId(chapters[currentPage - 2]) : void 0;
16267
+ const currentChapterTitle = currentPage >= 2 ? chapterTitle(chapters[currentPage - 2]) : void 0;
16177
16268
  if (!book) return /* @__PURE__ */ jsx(EmptyState, { message: t("book.noData") });
16269
+ const cover = book.cover;
16270
+ const coverTitle = String(cover.title ?? "");
16178
16271
  return /* @__PURE__ */ jsxs(VStack, { className: cn("relative h-full overflow-hidden bg-background", className), children: [
16179
16272
  /* @__PURE__ */ jsxs(
16180
16273
  Box,
@@ -16186,10 +16279,10 @@ var init_BookViewer = __esm({
16186
16279
  /* @__PURE__ */ jsx(
16187
16280
  BookCoverPage,
16188
16281
  {
16189
- title: book.title,
16190
- subtitle: book.subtitle,
16191
- author: book.author,
16192
- coverImageUrl: book.coverImageUrl,
16282
+ title: coverTitle,
16283
+ subtitle: String(cover.subtitle ?? "") || void 0,
16284
+ author: String(cover.author ?? "") || void 0,
16285
+ coverImageUrl: String(cover.coverImageUrl ?? "") || void 0,
16193
16286
  direction
16194
16287
  }
16195
16288
  ),
@@ -16200,23 +16293,27 @@ var init_BookViewer = __esm({
16200
16293
  direction
16201
16294
  }
16202
16295
  ),
16203
- chapters.map((chapter) => /* @__PURE__ */ jsx(
16204
- BookChapterView,
16205
- {
16206
- chapter,
16207
- direction
16208
- },
16209
- chapter.id
16210
- ))
16296
+ chapters.map((chapter) => {
16297
+ const id = chapterId(chapter);
16298
+ return /* @__PURE__ */ jsx(
16299
+ BookChapterView,
16300
+ {
16301
+ chapter,
16302
+ orbitalSchema: id ? book.schemaByChapterId[id] : void 0,
16303
+ direction
16304
+ },
16305
+ id
16306
+ );
16307
+ })
16211
16308
  ] }),
16212
16309
  /* @__PURE__ */ jsxs(Box, { className: "print:hidden", children: [
16213
16310
  currentPage === 0 && /* @__PURE__ */ jsx(
16214
16311
  BookCoverPage,
16215
16312
  {
16216
- title: book.title,
16217
- subtitle: book.subtitle,
16218
- author: book.author,
16219
- coverImageUrl: book.coverImageUrl,
16313
+ title: coverTitle,
16314
+ subtitle: String(cover.subtitle ?? "") || void 0,
16315
+ author: String(cover.author ?? "") || void 0,
16316
+ coverImageUrl: String(cover.coverImageUrl ?? "") || void 0,
16220
16317
  direction
16221
16318
  }
16222
16319
  ),
@@ -16232,6 +16329,7 @@ var init_BookViewer = __esm({
16232
16329
  BookChapterView,
16233
16330
  {
16234
16331
  chapter: chapters[currentPage - 2],
16332
+ orbitalSchema: currentChapterId ? book.schemaByChapterId[currentChapterId] : void 0,
16235
16333
  direction
16236
16334
  }
16237
16335
  )
@@ -16244,7 +16342,7 @@ var init_BookViewer = __esm({
16244
16342
  {
16245
16343
  currentPage,
16246
16344
  totalPages,
16247
- chapterTitle: currentPage === 0 ? book.title : currentPage === 1 ? t("book.tableOfContents") : currentChapterTitle,
16345
+ chapterTitle: currentPage === 0 ? coverTitle : currentPage === 1 ? t("book.tableOfContents") : currentChapterTitle,
16248
16346
  direction
16249
16347
  }
16250
16348
  )
@@ -16342,7 +16440,7 @@ var init_Grid = __esm({
16342
16440
  };
16343
16441
  Grid = ({
16344
16442
  cols = 1,
16345
- rows,
16443
+ rows: rows2,
16346
16444
  gap = "md",
16347
16445
  rowGap,
16348
16446
  colGap,
@@ -16354,7 +16452,7 @@ var init_Grid = __esm({
16354
16452
  children,
16355
16453
  as: Component = "div"
16356
16454
  }) => {
16357
- const mergedStyle = rows ? { gridTemplateRows: `repeat(${rows}, minmax(0, 1fr))`, ...style } : style;
16455
+ const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
16358
16456
  return React84__default.createElement(
16359
16457
  Component,
16360
16458
  {
@@ -17070,14 +17168,14 @@ function BuilderBoard({
17070
17168
  }) {
17071
17169
  const { emit } = useEventBus();
17072
17170
  const { t } = useTranslate();
17073
- const resolved = Array.isArray(entity) ? entity[0] : entity;
17171
+ const resolved = boardEntity(entity);
17074
17172
  const [placements, setPlacements] = useState({});
17075
17173
  const [headerError, setHeaderError] = useState(false);
17076
17174
  const [submitted, setSubmitted] = useState(false);
17077
17175
  const [attempts, setAttempts] = useState(0);
17078
17176
  const [showHint, setShowHint] = useState(false);
17079
- const components = resolved?.components ?? [];
17080
- const slots = resolved?.slots ?? [];
17177
+ const components = Array.isArray(resolved?.components) ? resolved.components : [];
17178
+ const slots = Array.isArray(resolved?.slots) ? resolved.slots : [];
17081
17179
  const usedComponentIds = new Set(Object.values(placements));
17082
17180
  const availableComponents = components.filter((c) => !usedComponentIds.has(c.id));
17083
17181
  const [selectedComponent, setSelectedComponent] = useState(null);
@@ -17111,7 +17209,7 @@ function BuilderBoard({
17111
17209
  }, [slots, placements, attempts, completeEvent, emit]);
17112
17210
  const handleReset = () => {
17113
17211
  setSubmitted(false);
17114
- if (attempts >= 2 && resolved?.hint) {
17212
+ if (attempts >= 2 && str(resolved?.hint)) {
17115
17213
  setShowHint(true);
17116
17214
  }
17117
17215
  };
@@ -17124,20 +17222,24 @@ function BuilderBoard({
17124
17222
  };
17125
17223
  const getComponentById = (id) => components.find((c) => c.id === id);
17126
17224
  if (!resolved) return null;
17225
+ const theme = resolved.theme ?? void 0;
17226
+ const themeBackground = theme?.background;
17227
+ const headerImage = str(resolved.headerImage);
17228
+ const hint = str(resolved.hint);
17127
17229
  return /* @__PURE__ */ jsx(
17128
17230
  Box,
17129
17231
  {
17130
17232
  className,
17131
17233
  style: {
17132
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
17234
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
17133
17235
  backgroundSize: "cover",
17134
17236
  backgroundPosition: "center"
17135
17237
  },
17136
17238
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
17137
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
17239
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
17138
17240
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
17139
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
17140
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description })
17241
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
17242
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) })
17141
17243
  ] }) }),
17142
17244
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
17143
17245
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("builder.components") }),
@@ -17197,9 +17299,9 @@ function BuilderBoard({
17197
17299
  ] }) }),
17198
17300
  submitted && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
17199
17301
  /* @__PURE__ */ jsx(Icon, { icon: allCorrect ? CheckCircle : XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
17200
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("builder.success") : resolved.failMessage ?? t("builder.incorrect") })
17302
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("builder.success") : str(resolved.failMessage) || t("builder.incorrect") })
17201
17303
  ] }) }),
17202
- showHint && resolved.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
17304
+ showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
17203
17305
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
17204
17306
  !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
17205
17307
  /* @__PURE__ */ jsx(Icon, { icon: Wrench, size: "sm" }),
@@ -17218,6 +17320,7 @@ var init_BuilderBoard = __esm({
17218
17320
  "components/game/organisms/puzzles/builder/BuilderBoard.tsx"() {
17219
17321
  init_atoms2();
17220
17322
  init_useEventBus();
17323
+ init_boardEntity();
17221
17324
  BuilderBoard.displayName = "BuilderBoard";
17222
17325
  }
17223
17326
  });
@@ -17555,21 +17658,24 @@ function CalendarGrid({
17555
17658
  eventBus.emit(`UI:${longPressEvent}`, { date: day.toISOString(), time, ...longPressPayload });
17556
17659
  }, 500);
17557
17660
  }, [longPressEvent, longPressPayload, eventBus]);
17558
- const renderEvent = (event) => /* @__PURE__ */ jsx(
17559
- Box,
17560
- {
17561
- rounded: "md",
17562
- padding: "xs",
17563
- border: true,
17564
- className: cn(
17565
- "cursor-pointer hover:shadow-sm transition-shadow text-xs truncate",
17566
- event.color ? event.color : "bg-blue-500/15 border-blue-500/30 text-blue-600"
17567
- ),
17568
- onClick: (e) => handleEventClick(event, e),
17569
- children: /* @__PURE__ */ jsx(Typography, { variant: "small", className: "truncate font-medium", children: event.title })
17570
- },
17571
- event.id
17572
- );
17661
+ const renderEvent = (event) => {
17662
+ const color = event.color;
17663
+ return /* @__PURE__ */ jsx(
17664
+ Box,
17665
+ {
17666
+ rounded: "md",
17667
+ padding: "xs",
17668
+ border: true,
17669
+ className: cn(
17670
+ "cursor-pointer hover:shadow-sm transition-shadow text-xs truncate",
17671
+ color ? color : "bg-blue-500/15 border-blue-500/30 text-blue-600"
17672
+ ),
17673
+ onClick: (e) => handleEventClick(event, e),
17674
+ children: /* @__PURE__ */ jsx(Typography, { variant: "small", className: "truncate font-medium", children: event.title })
17675
+ },
17676
+ event.id
17677
+ );
17678
+ };
17573
17679
  return /* @__PURE__ */ jsxs(
17574
17680
  Box,
17575
17681
  {
@@ -19233,7 +19339,6 @@ var init_CardGrid = __esm({
19233
19339
  alignItems = "stretch",
19234
19340
  className,
19235
19341
  children,
19236
- // EntityDisplayProps
19237
19342
  entity,
19238
19343
  isLoading = false,
19239
19344
  error = null,
@@ -19705,14 +19810,14 @@ var init_CaseStudyOrganism = __esm({
19705
19810
  /* @__PURE__ */ jsx(SimpleGrid, { cols: cols > 0 ? cols : 1, gap: "lg", children: items.map((study) => /* @__PURE__ */ jsx(
19706
19811
  CaseStudyCard,
19707
19812
  {
19708
- title: study.title,
19709
- description: study.description,
19710
- category: study.category,
19711
- categoryColor: study.categoryColor,
19712
- href: study.href,
19713
- linkLabel: study.linkLabel
19813
+ title: String(study.title ?? ""),
19814
+ description: String(study.description ?? ""),
19815
+ category: String(study.category ?? ""),
19816
+ categoryColor: study.categoryColor != null ? String(study.categoryColor) : void 0,
19817
+ href: String(study.href ?? ""),
19818
+ linkLabel: study.linkLabel != null ? String(study.linkLabel) : void 0
19714
19819
  },
19715
- study.id
19820
+ String(study.id ?? "")
19716
19821
  )) })
19717
19822
  ] });
19718
19823
  };
@@ -19735,10 +19840,10 @@ function CastleBoard({
19735
19840
  className
19736
19841
  }) {
19737
19842
  const eventBus = useEventBus();
19738
- const resolved = Array.isArray(entity) ? entity[0] : entity;
19739
- const tiles = resolved?.tiles ?? [];
19740
- const features = resolved?.features ?? [];
19741
- const units = resolved?.units ?? [];
19843
+ const resolved = boardEntity(entity);
19844
+ const tiles = Array.isArray(resolved?.tiles) ? resolved.tiles : [];
19845
+ const features = Array.isArray(resolved?.features) ? resolved.features : [];
19846
+ const units = Array.isArray(resolved?.units) ? resolved.units : [];
19742
19847
  const assetManifest = resolved?.assetManifest;
19743
19848
  const backgroundImage = resolved?.backgroundImage;
19744
19849
  const [hoveredTile, setHoveredTile] = useState(null);
@@ -19766,7 +19871,7 @@ function CastleBoard({
19766
19871
  onFeatureClick?.(feature);
19767
19872
  if (featureClickEvent) {
19768
19873
  eventBus.emit(`UI:${featureClickEvent}`, {
19769
- featureId: feature.id,
19874
+ featureId: feature.id ?? "",
19770
19875
  featureType: feature.type,
19771
19876
  x: feature.x,
19772
19877
  y: feature.y
@@ -19834,6 +19939,7 @@ var init_CastleBoard = __esm({
19834
19939
  init_cn();
19835
19940
  init_useEventBus();
19836
19941
  init_IsometricCanvas2();
19942
+ init_boardEntity();
19837
19943
  init_isometric();
19838
19944
  CastleBoard.displayName = "CastleBoard";
19839
19945
  }
@@ -20644,14 +20750,14 @@ function ClassifierBoard({
20644
20750
  }) {
20645
20751
  const { emit } = useEventBus();
20646
20752
  const { t } = useTranslate();
20647
- const resolved = Array.isArray(entity) ? entity[0] : entity;
20753
+ const resolved = boardEntity(entity);
20648
20754
  const [assignments, setAssignments] = useState({});
20649
20755
  const [headerError, setHeaderError] = useState(false);
20650
20756
  const [submitted, setSubmitted] = useState(false);
20651
20757
  const [attempts, setAttempts] = useState(0);
20652
20758
  const [showHint, setShowHint] = useState(false);
20653
- const items = resolved?.items ?? [];
20654
- const categories = resolved?.categories ?? [];
20759
+ const items = Array.isArray(resolved?.items) ? resolved.items : [];
20760
+ const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
20655
20761
  const unassignedItems = items.filter((item) => !assignments[item.id]);
20656
20762
  const allAssigned = Object.keys(assignments).length === items.length;
20657
20763
  const results = submitted ? items.map((item) => ({
@@ -20683,7 +20789,7 @@ function ClassifierBoard({
20683
20789
  }, [items, assignments, attempts, completeEvent, emit]);
20684
20790
  const handleReset = () => {
20685
20791
  setSubmitted(false);
20686
- if (attempts >= 2 && resolved?.hint) {
20792
+ if (attempts >= 2 && str(resolved?.hint)) {
20687
20793
  setShowHint(true);
20688
20794
  }
20689
20795
  };
@@ -20694,20 +20800,25 @@ function ClassifierBoard({
20694
20800
  setShowHint(false);
20695
20801
  };
20696
20802
  if (!resolved) return null;
20803
+ const theme = resolved.theme ?? void 0;
20804
+ const themeBackground = theme?.background;
20805
+ const headerImage = str(resolved.headerImage);
20806
+ const hint = str(resolved.hint);
20807
+ const failMessage = str(resolved.failMessage);
20697
20808
  return /* @__PURE__ */ jsx(
20698
20809
  Box,
20699
20810
  {
20700
20811
  className,
20701
20812
  style: {
20702
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
20813
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
20703
20814
  backgroundSize: "cover",
20704
20815
  backgroundPosition: "center"
20705
20816
  },
20706
20817
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
20707
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
20818
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
20708
20819
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
20709
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
20710
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description })
20820
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
20821
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) })
20711
20822
  ] }) }),
20712
20823
  unassignedItems.length > 0 && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
20713
20824
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("classifier.itemsToSort") }),
@@ -20759,10 +20870,10 @@ function ClassifierBoard({
20759
20870
  }) }),
20760
20871
  submitted && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
20761
20872
  /* @__PURE__ */ jsx(Icon, { icon: allCorrect ? CheckCircle : XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
20762
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("classifier.allCorrect") : `${correctCount}/${items.length} ${t("classifier.correct")}` }),
20763
- !allCorrect && resolved.failMessage && /* @__PURE__ */ jsx(Typography, { variant: "body", className: "text-muted-foreground", children: resolved.failMessage })
20873
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("classifier.allCorrect") : `${correctCount}/${items.length} ${t("classifier.correct")}` }),
20874
+ !allCorrect && failMessage && /* @__PURE__ */ jsx(Typography, { variant: "body", className: "text-muted-foreground", children: failMessage })
20764
20875
  ] }) }),
20765
- showHint && resolved.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
20876
+ showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
20766
20877
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
20767
20878
  !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
20768
20879
  /* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
@@ -20781,6 +20892,7 @@ var init_ClassifierBoard = __esm({
20781
20892
  "components/game/organisms/puzzles/classifier/ClassifierBoard.tsx"() {
20782
20893
  init_atoms2();
20783
20894
  init_useEventBus();
20895
+ init_boardEntity();
20784
20896
  ClassifierBoard.displayName = "ClassifierBoard";
20785
20897
  }
20786
20898
  });
@@ -27151,7 +27263,7 @@ function InventoryPanel({
27151
27263
  const slotArray = Array.from({ length: safeSlots }, (_, index) => {
27152
27264
  return safeItems[index] ?? null;
27153
27265
  });
27154
- const rows = Math.ceil(safeSlots / safeColumns);
27266
+ const rows2 = Math.ceil(safeSlots / safeColumns);
27155
27267
  const handleSlotClick = useCallback((index) => {
27156
27268
  if (selectSlotEvent) eventBus.emit(`UI:${selectSlotEvent}`, { index });
27157
27269
  onSelectSlot?.(index);
@@ -27220,7 +27332,7 @@ function InventoryPanel({
27220
27332
  className: "grid gap-1 bg-[var(--color-card)] p-2 rounded-container border border-border",
27221
27333
  style: {
27222
27334
  gridTemplateColumns: `repeat(${safeColumns}, ${slotSize}px)`,
27223
- gridTemplateRows: `repeat(${rows}, ${slotSize}px)`
27335
+ gridTemplateRows: `repeat(${rows2}, ${slotSize}px)`
27224
27336
  },
27225
27337
  children: slotArray.map((item, index) => /* @__PURE__ */ jsx(
27226
27338
  "button",
@@ -31184,11 +31296,11 @@ function LatticeSVG({
31184
31296
  }) {
31185
31297
  const paths = [];
31186
31298
  const cols = 5;
31187
- const rows = Math.ceil(h / (w / cols));
31299
+ const rows2 = Math.ceil(h / (w / cols));
31188
31300
  const cellW = w / cols;
31189
31301
  const cellH = cellW;
31190
31302
  const bulge = cellW * 0.3;
31191
- for (let row = 0; row < rows; row++) {
31303
+ for (let row = 0; row < rows2; row++) {
31192
31304
  for (let col = 0; col < cols; col++) {
31193
31305
  const cx = col * cellW + cellW / 2;
31194
31306
  const cy = row * cellH + cellH / 2;
@@ -31600,7 +31712,7 @@ var init_MatrixQuestion = __esm({
31600
31712
  };
31601
31713
  MatrixQuestion = ({
31602
31714
  title,
31603
- rows,
31715
+ rows: rows2,
31604
31716
  columns = DEFAULT_MATRIX_COLUMNS,
31605
31717
  values,
31606
31718
  onChange,
@@ -31610,7 +31722,7 @@ var init_MatrixQuestion = __esm({
31610
31722
  className
31611
31723
  }) => {
31612
31724
  const styles = sizeStyles13[size];
31613
- const safeRows = rows ?? [];
31725
+ const safeRows = rows2 ?? [];
31614
31726
  const safeValues = values ?? {};
31615
31727
  const eventBus = useEventBus();
31616
31728
  const handleChange = useCallback(
@@ -32238,7 +32350,8 @@ var init_PositionedCanvas = __esm({
32238
32350
  dragRef.current = null;
32239
32351
  setDraggingId(null);
32240
32352
  if (!wasDrag) {
32241
- const next = selectedId === item.id ? null : item.id;
32353
+ const itemId = item.id;
32354
+ const next = selectedId === itemId ? null : itemId;
32242
32355
  onSelect?.(next);
32243
32356
  if (selectEvent) {
32244
32357
  eventBus.emit(`UI:${selectEvent}`, { id: next });
@@ -32273,15 +32386,22 @@ var init_PositionedCanvas = __esm({
32273
32386
  style: { width, height },
32274
32387
  onClick: handleContainerClick,
32275
32388
  children: items.map((item) => {
32389
+ const itemId = item.id;
32390
+ const label = item.label;
32391
+ const x = item.x;
32392
+ const y = item.y;
32393
+ const capacity = item.capacity;
32394
+ const partySize = item.partySize;
32395
+ const serverName = item.serverName;
32276
32396
  const status = item.status ?? "empty";
32277
32397
  const shape = item.shape ?? "round";
32278
- const isSelected = selectedId === item.id;
32279
- const isDragging = draggingId === item.id;
32398
+ const isSelected = selectedId === itemId;
32399
+ const isDragging = draggingId === itemId;
32280
32400
  const statusBadge = STATUS_BADGE[status];
32281
32401
  return /* @__PURE__ */ jsxs(
32282
32402
  Box,
32283
32403
  {
32284
- "data-testid": `item-node-${item.id}`,
32404
+ "data-testid": `item-node-${itemId}`,
32285
32405
  "data-status": status,
32286
32406
  className: cn(
32287
32407
  "absolute flex flex-col items-center justify-center gap-1 border-2 select-none",
@@ -32292,7 +32412,7 @@ var init_PositionedCanvas = __esm({
32292
32412
  isSelected && "outline outline-2 outline-offset-2 outline-primary shadow-md",
32293
32413
  isDragging && "shadow-lg z-10"
32294
32414
  ),
32295
- style: { left: item.x, top: item.y, touchAction: "none" },
32415
+ style: { left: x, top: y, touchAction: "none" },
32296
32416
  onPointerDown: (e) => handlePointerDown(e, item),
32297
32417
  onPointerMove: handlePointerMove,
32298
32418
  onPointerUp: (e) => handlePointerUp(e, item),
@@ -32300,10 +32420,10 @@ var init_PositionedCanvas = __esm({
32300
32420
  children: [
32301
32421
  /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-1", children: [
32302
32422
  getStatusIcon(status),
32303
- /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "semibold", children: item.label })
32423
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "semibold", children: label })
32304
32424
  ] }),
32305
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: item.partySize !== void 0 && status === "seated" ? `${item.partySize}/${item.capacity}` : `Cap ${item.capacity}` }),
32306
- status === "seated" && item.serverName && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "truncate max-w-[80%]", children: item.serverName }),
32425
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: partySize !== void 0 && status === "seated" ? `${partySize}/${capacity}` : `Cap ${capacity}` }),
32426
+ status === "seated" && serverName && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "truncate max-w-[80%]", children: serverName }),
32307
32427
  isSelected && /* @__PURE__ */ jsx(
32308
32428
  Badge,
32309
32429
  {
@@ -32315,7 +32435,7 @@ var init_PositionedCanvas = __esm({
32315
32435
  )
32316
32436
  ]
32317
32437
  },
32318
- item.id
32438
+ itemId
32319
32439
  );
32320
32440
  })
32321
32441
  }
@@ -33087,9 +33207,10 @@ var init_RichBlockEditor = __esm({
33087
33207
  });
33088
33208
  function collectInitiallyCollapsed(nodes, acc) {
33089
33209
  for (const n of nodes) {
33210
+ const replies = n.replies;
33090
33211
  if (n.collapsed) acc.add(n.id);
33091
- if (n.replies && n.replies.length > 0) {
33092
- collectInitiallyCollapsed(n.replies, acc);
33212
+ if (replies && replies.length > 0) {
33213
+ collectInitiallyCollapsed(replies, acc);
33093
33214
  }
33094
33215
  }
33095
33216
  }
@@ -33119,44 +33240,52 @@ var init_ReplyTree = __esm({
33119
33240
  }) => {
33120
33241
  const eventBus = useEventBus();
33121
33242
  const { t } = useTranslate();
33122
- const hasReplies = !!node.replies && node.replies.length > 0;
33123
- const isCollapsed = collapsedSet.has(node.id);
33243
+ const nodeId = node.id;
33244
+ const authorName = node.authorName;
33245
+ const authorAvatarUrl = node.authorAvatarUrl;
33246
+ const content = node.content;
33247
+ const postedAt = node.postedAt;
33248
+ const voteCount = node.voteCount;
33249
+ const userVote = node.userVote;
33250
+ const replies = node.replies;
33251
+ const hasReplies = !!replies && replies.length > 0;
33252
+ const isCollapsed = collapsedSet.has(nodeId);
33124
33253
  const atMaxDepth = depth >= maxDepth;
33125
33254
  const [replyOpen, setReplyOpen] = useState(false);
33126
33255
  const [draft, setDraft] = useState("");
33127
33256
  const handleVote = useCallback(
33128
33257
  (next) => {
33129
- onVote?.(node.id, next);
33130
- if (voteEvent) eventBus.emit(`UI:${voteEvent}`, { nodeId: node.id, vote: next });
33258
+ onVote?.(nodeId, next);
33259
+ if (voteEvent) eventBus.emit(`UI:${voteEvent}`, { nodeId, vote: next });
33131
33260
  },
33132
- [node.id, onVote, voteEvent, eventBus]
33261
+ [nodeId, onVote, voteEvent, eventBus]
33133
33262
  );
33134
33263
  const handleReply = useCallback(() => {
33135
- onReply?.(node.id);
33264
+ onReply?.(nodeId);
33136
33265
  setReplyOpen((open) => !open);
33137
- }, [node.id, onReply]);
33266
+ }, [nodeId, onReply]);
33138
33267
  const handleSubmitReply = useCallback(() => {
33139
- const content = draft.trim();
33140
- if (!content) return;
33141
- if (replyEvent) eventBus.emit(`UI:${replyEvent}`, { parentNodeId: node.id, content });
33268
+ const text = draft.trim();
33269
+ if (!text) return;
33270
+ if (replyEvent) eventBus.emit(`UI:${replyEvent}`, { parentNodeId: nodeId, content: text });
33142
33271
  setDraft("");
33143
33272
  setReplyOpen(false);
33144
- }, [node.id, draft, replyEvent, eventBus]);
33273
+ }, [nodeId, draft, replyEvent, eventBus]);
33145
33274
  const handleCancelReply = useCallback(() => {
33146
33275
  setDraft("");
33147
33276
  setReplyOpen(false);
33148
33277
  }, []);
33149
33278
  const handleFlag = useCallback(() => {
33150
- onFlag?.(node.id);
33151
- if (flagEvent) eventBus.emit(`UI:${flagEvent}`, { nodeId: node.id });
33152
- }, [node.id, onFlag, flagEvent, eventBus]);
33279
+ onFlag?.(nodeId);
33280
+ if (flagEvent) eventBus.emit(`UI:${flagEvent}`, { nodeId });
33281
+ }, [nodeId, onFlag, flagEvent, eventBus]);
33153
33282
  const handleContinue = useCallback(() => {
33154
- onContinueThread?.(node.id);
33155
- if (continueThreadEvent) eventBus.emit(`UI:${continueThreadEvent}`, { nodeId: node.id });
33156
- }, [node.id, onContinueThread, continueThreadEvent, eventBus]);
33283
+ onContinueThread?.(nodeId);
33284
+ if (continueThreadEvent) eventBus.emit(`UI:${continueThreadEvent}`, { nodeId });
33285
+ }, [nodeId, onContinueThread, continueThreadEvent, eventBus]);
33157
33286
  const handleToggle = useCallback(() => {
33158
- toggleCollapse(node.id);
33159
- }, [node.id, toggleCollapse]);
33287
+ toggleCollapse(nodeId);
33288
+ }, [nodeId, toggleCollapse]);
33160
33289
  return /* @__PURE__ */ jsxs(Box, { className: "flex flex-row gap-2 items-stretch min-w-0", children: [
33161
33290
  /* @__PURE__ */ jsxs(Box, { className: "flex flex-col items-center flex-shrink-0 w-6", children: [
33162
33291
  hasReplies ? /* @__PURE__ */ jsx(
@@ -33188,25 +33317,25 @@ var init_ReplyTree = __esm({
33188
33317
  /* @__PURE__ */ jsx(
33189
33318
  Avatar,
33190
33319
  {
33191
- src: node.authorAvatarUrl,
33192
- name: node.authorName,
33320
+ src: authorAvatarUrl,
33321
+ name: authorName,
33193
33322
  size: "sm"
33194
33323
  }
33195
33324
  ),
33196
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "semibold", children: node.authorName }),
33197
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: node.postedAt })
33325
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "semibold", children: authorName }),
33326
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: postedAt })
33198
33327
  ] }),
33199
- /* @__PURE__ */ jsx(Typography, { variant: "body", className: "whitespace-pre-wrap break-words", children: node.content }),
33328
+ /* @__PURE__ */ jsx(Typography, { variant: "body", className: "whitespace-pre-wrap break-words", children: content }),
33200
33329
  showActions && /* @__PURE__ */ jsxs(Box, { className: "flex flex-row gap-2 items-center", children: [
33201
33330
  /* @__PURE__ */ jsx(
33202
33331
  VoteStack,
33203
33332
  {
33204
- count: node.voteCount ?? 0,
33205
- userVote: node.userVote ?? null,
33333
+ count: voteCount ?? 0,
33334
+ userVote: userVote ?? null,
33206
33335
  onVote: handleVote,
33207
33336
  size: "sm",
33208
33337
  variant: "horizontal",
33209
- label: t("replyTree.voteOnReplyBy", { author: node.authorName })
33338
+ label: t("replyTree.voteOnReplyBy", { author: authorName })
33210
33339
  }
33211
33340
  ),
33212
33341
  /* @__PURE__ */ jsx(
@@ -33216,7 +33345,7 @@ var init_ReplyTree = __esm({
33216
33345
  size: "sm",
33217
33346
  leftIcon: "message-square",
33218
33347
  onClick: handleReply,
33219
- "aria-label": t("replyTree.replyTo", { author: node.authorName }),
33348
+ "aria-label": t("replyTree.replyTo", { author: authorName }),
33220
33349
  children: t("replyTree.reply")
33221
33350
  }
33222
33351
  ),
@@ -33227,7 +33356,7 @@ var init_ReplyTree = __esm({
33227
33356
  size: "sm",
33228
33357
  leftIcon: "flag",
33229
33358
  onClick: handleFlag,
33230
- "aria-label": t("replyTree.flagReplyBy", { author: node.authorName }),
33359
+ "aria-label": t("replyTree.flagReplyBy", { author: authorName }),
33231
33360
  children: t("replyTree.flag")
33232
33361
  }
33233
33362
  )
@@ -33239,9 +33368,9 @@ var init_ReplyTree = __esm({
33239
33368
  inputType: "textarea",
33240
33369
  rows: 2,
33241
33370
  value: draft,
33242
- placeholder: t("replyTree.replyToPlaceholder", { author: node.authorName }),
33371
+ placeholder: t("replyTree.replyToPlaceholder", { author: authorName }),
33243
33372
  onChange: (e) => setDraft(e.target.value),
33244
- "aria-label": t("replyTree.replyTo", { author: node.authorName })
33373
+ "aria-label": t("replyTree.replyTo", { author: authorName })
33245
33374
  }
33246
33375
  ),
33247
33376
  /* @__PURE__ */ jsxs(Box, { className: "flex flex-row gap-2 items-center", children: [
@@ -33272,7 +33401,7 @@ var init_ReplyTree = __esm({
33272
33401
  ),
33273
33402
  children: t("replyTree.continueThread")
33274
33403
  }
33275
- ) : /* @__PURE__ */ jsx(Box, { className: "flex flex-col gap-2 mt-1", children: node.replies.map((child) => /* @__PURE__ */ jsx(
33404
+ ) : /* @__PURE__ */ jsx(Box, { className: "flex flex-col gap-2 mt-1", children: replies.map((child) => /* @__PURE__ */ jsx(
33276
33405
  ReplyTreeNode,
33277
33406
  {
33278
33407
  node: child,
@@ -35592,8 +35721,8 @@ var init_WizardContainer = __esm({
35592
35721
  return void 0;
35593
35722
  if (typeof controlledStep === "number") return controlledStep;
35594
35723
  if (typeof controlledStep === "string") return parseInt(controlledStep, 10);
35595
- const num = Number(controlledStep);
35596
- return isNaN(num) ? void 0 : num;
35724
+ const num2 = Number(controlledStep);
35725
+ return isNaN(num2) ? void 0 : num2;
35597
35726
  })();
35598
35727
  const currentStep = normalizedControlledStep !== void 0 ? normalizedControlledStep : internalStep;
35599
35728
  const totalSteps = steps.length;
@@ -37298,7 +37427,7 @@ function DebuggerBoard({
37298
37427
  }) {
37299
37428
  const { emit } = useEventBus();
37300
37429
  const { t } = useTranslate();
37301
- const resolved = Array.isArray(entity) ? entity[0] : entity;
37430
+ const resolved = boardEntity(entity);
37302
37431
  const [flaggedLines, setFlaggedLines] = useState(/* @__PURE__ */ new Set());
37303
37432
  const [headerError, setHeaderError] = useState(false);
37304
37433
  const [submitted, setSubmitted] = useState(false);
@@ -37316,7 +37445,7 @@ function DebuggerBoard({
37316
37445
  return next;
37317
37446
  });
37318
37447
  };
37319
- const lines = resolved?.lines ?? [];
37448
+ const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
37320
37449
  const bugLines = lines.filter((l) => l.isBug);
37321
37450
  const correctFlags = lines.filter((l) => l.isBug && flaggedLines.has(l.id));
37322
37451
  const falseFlags = lines.filter((l) => !l.isBug && flaggedLines.has(l.id));
@@ -37331,7 +37460,7 @@ function DebuggerBoard({
37331
37460
  }, [correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
37332
37461
  const handleReset = () => {
37333
37462
  setSubmitted(false);
37334
- if (attempts >= 2 && resolved?.hint) {
37463
+ if (attempts >= 2 && str(resolved?.hint)) {
37335
37464
  setShowHint(true);
37336
37465
  }
37337
37466
  };
@@ -37342,24 +37471,28 @@ function DebuggerBoard({
37342
37471
  setShowHint(false);
37343
37472
  };
37344
37473
  if (!resolved) return null;
37474
+ const theme = resolved.theme ?? void 0;
37475
+ const themeBackground = theme?.background;
37476
+ const headerImage = str(resolved.headerImage);
37477
+ const hint = str(resolved.hint);
37345
37478
  return /* @__PURE__ */ jsx(
37346
37479
  Box,
37347
37480
  {
37348
37481
  className,
37349
37482
  style: {
37350
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
37483
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
37351
37484
  backgroundSize: "cover",
37352
37485
  backgroundPosition: "center"
37353
37486
  },
37354
37487
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
37355
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
37488
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
37356
37489
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
37357
37490
  /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
37358
37491
  /* @__PURE__ */ jsx(Icon, { icon: Bug, size: "sm" }),
37359
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title })
37492
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) })
37360
37493
  ] }),
37361
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description }),
37362
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(resolved.bugCount) }) })
37494
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) }),
37495
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(num(resolved.bugCount)) }) })
37363
37496
  ] }) }),
37364
37497
  /* @__PURE__ */ jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsx(VStack, { gap: "none", children: lines.map((line, i) => {
37365
37498
  const isFlagged = flaggedLines.has(line.id);
@@ -37391,7 +37524,7 @@ function DebuggerBoard({
37391
37524
  );
37392
37525
  }) }) }),
37393
37526
  submitted && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
37394
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("debugger.allFound") : `${correctFlags.length}/${bugLines.length} ${t("debugger.bugsFound")}` }),
37527
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("debugger.allFound") : `${correctFlags.length}/${bugLines.length} ${t("debugger.bugsFound")}` }),
37395
37528
  bugLines.map((line) => /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "start", children: [
37396
37529
  /* @__PURE__ */ jsx(
37397
37530
  Icon,
@@ -37407,7 +37540,7 @@ function DebuggerBoard({
37407
37540
  ] })
37408
37541
  ] }, line.id))
37409
37542
  ] }) }),
37410
- showHint && resolved.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
37543
+ showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
37411
37544
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
37412
37545
  !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.size === 0, children: [
37413
37546
  /* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
@@ -37426,6 +37559,7 @@ var init_DebuggerBoard = __esm({
37426
37559
  "components/game/organisms/puzzles/debugger/DebuggerBoard.tsx"() {
37427
37560
  init_atoms2();
37428
37561
  init_useEventBus();
37562
+ init_boardEntity();
37429
37563
  DebuggerBoard.displayName = "DebuggerBoard";
37430
37564
  }
37431
37565
  });
@@ -37463,7 +37597,7 @@ function getBadgeVariant(fieldName, value) {
37463
37597
  return "default";
37464
37598
  }
37465
37599
  function formatFieldLabel(fieldName) {
37466
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase());
37600
+ return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str2) => str2.toUpperCase());
37467
37601
  }
37468
37602
  function formatFieldValue(value, fieldName) {
37469
37603
  if (typeof value === "number") {
@@ -37482,26 +37616,26 @@ function formatFieldValue(value, fieldName) {
37482
37616
  }
37483
37617
  function renderRichFieldValue(value, fieldName, fieldType) {
37484
37618
  if (value === void 0 || value === null) return "\u2014";
37485
- const str = String(value);
37619
+ const str2 = String(value);
37486
37620
  switch (fieldType) {
37487
37621
  case "image":
37488
37622
  case "url": {
37489
- if (str.match(/\.(png|jpe?g|gif|svg|webp|avif)(\?|$)/i) || str.startsWith("data:image/")) {
37623
+ if (str2.match(/\.(png|jpe?g|gif|svg|webp|avif)(\?|$)/i) || str2.startsWith("data:image/")) {
37490
37624
  return /* @__PURE__ */ jsx(Box, { className: "mt-1 max-w-full", children: /* @__PURE__ */ jsx(
37491
37625
  "img",
37492
37626
  {
37493
- src: str,
37627
+ src: str2,
37494
37628
  alt: formatFieldLabel(fieldName),
37495
37629
  className: "max-w-full max-h-64 rounded-md object-contain",
37496
37630
  loading: "lazy"
37497
37631
  }
37498
37632
  ) });
37499
37633
  }
37500
- return str;
37634
+ return str2;
37501
37635
  }
37502
37636
  case "markdown":
37503
37637
  case "richtext":
37504
- return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(Typography, { variant: "body", className: "break-words", children: str }), children: /* @__PURE__ */ jsx(
37638
+ return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(Typography, { variant: "body", className: "break-words", children: str2 }), children: /* @__PURE__ */ jsx(
37505
37639
  Box,
37506
37640
  {
37507
37641
  className: "prose prose-sm max-w-none",
@@ -37519,11 +37653,11 @@ function renderRichFieldValue(value, fieldName, fieldType) {
37519
37653
  "--tw-prose-th-borders": "var(--color-border)",
37520
37654
  "--tw-prose-td-borders": "var(--color-border)"
37521
37655
  },
37522
- children: /* @__PURE__ */ jsx(ReactMarkdown2, { children: str })
37656
+ children: /* @__PURE__ */ jsx(ReactMarkdown2, { children: str2 })
37523
37657
  }
37524
37658
  ) });
37525
37659
  case "code":
37526
- return /* @__PURE__ */ jsx(Box, { className: "mt-1 rounded-md bg-muted p-3 overflow-x-auto", children: /* @__PURE__ */ jsx("pre", { className: "text-sm font-mono whitespace-pre-wrap break-words m-0", children: /* @__PURE__ */ jsx("code", { children: str }) }) });
37660
+ return /* @__PURE__ */ jsx(Box, { className: "mt-1 rounded-md bg-muted p-3 overflow-x-auto", children: /* @__PURE__ */ jsx("pre", { className: "text-sm font-mono whitespace-pre-wrap break-words m-0", children: /* @__PURE__ */ jsx("code", { children: str2 }) }) });
37527
37661
  case "html":
37528
37662
  return /* @__PURE__ */ jsx(
37529
37663
  Box,
@@ -37543,12 +37677,12 @@ function renderRichFieldValue(value, fieldName, fieldType) {
37543
37677
  "--tw-prose-th-borders": "var(--color-border)",
37544
37678
  "--tw-prose-td-borders": "var(--color-border)"
37545
37679
  },
37546
- children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: str })
37680
+ children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: str2 })
37547
37681
  }
37548
37682
  );
37549
37683
  case "date":
37550
37684
  case "datetime": {
37551
- const d = new Date(str);
37685
+ const d = new Date(str2);
37552
37686
  if (!isNaN(d.getTime())) {
37553
37687
  return d.toLocaleDateString(void 0, {
37554
37688
  year: "numeric",
@@ -37557,7 +37691,7 @@ function renderRichFieldValue(value, fieldName, fieldType) {
37557
37691
  ...fieldType === "datetime" ? { hour: "2-digit", minute: "2-digit" } : {}
37558
37692
  });
37559
37693
  }
37560
- return str;
37694
+ return str2;
37561
37695
  }
37562
37696
  default:
37563
37697
  return formatFieldValue(value, fieldName);
@@ -38235,6 +38369,40 @@ var init_RuleEditor = __esm({
38235
38369
  RuleEditor.displayName = "RuleEditor";
38236
38370
  }
38237
38371
  });
38372
+
38373
+ // components/game/organisms/puzzles/event-handler/puzzleObject.ts
38374
+ function objId(o) {
38375
+ return o.id == null ? "" : String(o.id);
38376
+ }
38377
+ function objName(o) {
38378
+ return o.name == null ? "" : String(o.name);
38379
+ }
38380
+ function objIcon(o) {
38381
+ return o.icon == null ? "" : String(o.icon);
38382
+ }
38383
+ function objStates(o) {
38384
+ return Array.isArray(o.states) ? o.states : [];
38385
+ }
38386
+ function objCurrentState(o) {
38387
+ return o.currentState == null ? "" : String(o.currentState);
38388
+ }
38389
+ function objAvailableEvents(o) {
38390
+ return Array.isArray(o.availableEvents) ? o.availableEvents : [];
38391
+ }
38392
+ function objAvailableActions(o) {
38393
+ return Array.isArray(o.availableActions) ? o.availableActions : [];
38394
+ }
38395
+ function objRules(o) {
38396
+ return Array.isArray(o.rules) ? o.rules : [];
38397
+ }
38398
+ function objMaxRules(o) {
38399
+ const n = Number(o.maxRules);
38400
+ return Number.isFinite(n) && n > 0 ? n : 3;
38401
+ }
38402
+ var init_puzzleObject = __esm({
38403
+ "components/game/organisms/puzzles/event-handler/puzzleObject.ts"() {
38404
+ }
38405
+ });
38238
38406
  function ObjectRulePanel({
38239
38407
  object,
38240
38408
  onRulesChange,
@@ -38242,55 +38410,63 @@ function ObjectRulePanel({
38242
38410
  className
38243
38411
  }) {
38244
38412
  const { t } = useTranslate();
38245
- const maxRules = object.maxRules || 3;
38246
- const canAdd = object.rules.length < maxRules;
38413
+ const id = objId(object);
38414
+ const name = objName(object);
38415
+ const icon = objIcon(object);
38416
+ const states = objStates(object);
38417
+ const currentState = objCurrentState(object);
38418
+ const availableEvents = objAvailableEvents(object);
38419
+ const availableActions = objAvailableActions(object);
38420
+ const rules = objRules(object);
38421
+ const maxRules = objMaxRules(object);
38422
+ const canAdd = rules.length < maxRules;
38247
38423
  const handleRuleChange = useCallback((index, updatedRule) => {
38248
- const newRules = [...object.rules];
38424
+ const newRules = [...rules];
38249
38425
  newRules[index] = updatedRule;
38250
- onRulesChange(object.id, newRules);
38251
- }, [object.id, object.rules, onRulesChange]);
38426
+ onRulesChange(id, newRules);
38427
+ }, [id, rules, onRulesChange]);
38252
38428
  const handleRuleRemove = useCallback((index) => {
38253
- const newRules = object.rules.filter((_, i) => i !== index);
38254
- onRulesChange(object.id, newRules);
38255
- }, [object.id, object.rules, onRulesChange]);
38429
+ const newRules = rules.filter((_, i) => i !== index);
38430
+ onRulesChange(id, newRules);
38431
+ }, [id, rules, onRulesChange]);
38256
38432
  const handleAddRule = useCallback(() => {
38257
38433
  if (!canAdd || disabled) return;
38258
- const firstEvent = object.availableEvents[0]?.value || "";
38259
- const firstAction = object.availableActions[0]?.value || "";
38434
+ const firstEvent = availableEvents[0]?.value || "";
38435
+ const firstAction = availableActions[0]?.value || "";
38260
38436
  const newRule = {
38261
38437
  id: `rule-${nextRuleId++}`,
38262
38438
  whenEvent: firstEvent,
38263
38439
  thenAction: firstAction
38264
38440
  };
38265
- onRulesChange(object.id, [...object.rules, newRule]);
38266
- }, [canAdd, disabled, object, onRulesChange]);
38441
+ onRulesChange(id, [...rules, newRule]);
38442
+ }, [canAdd, disabled, id, rules, availableEvents, availableActions, onRulesChange]);
38267
38443
  const machine = {
38268
- name: object.name,
38269
- states: object.states,
38270
- currentState: object.currentState,
38271
- transitions: object.rules.map((r) => ({
38272
- from: object.currentState,
38273
- to: object.states.find((s) => s !== object.currentState) || object.currentState,
38444
+ name,
38445
+ states,
38446
+ currentState,
38447
+ transitions: rules.map((r) => ({
38448
+ from: currentState,
38449
+ to: states.find((s) => s !== currentState) || currentState,
38274
38450
  event: r.whenEvent
38275
38451
  }))
38276
38452
  };
38277
38453
  return /* @__PURE__ */ jsxs(VStack, { className: cn("p-4 rounded-lg bg-card border border-border", className), gap: "sm", children: [
38278
38454
  /* @__PURE__ */ jsxs(HStack, { className: "items-center", gap: "sm", children: [
38279
- /* @__PURE__ */ jsx(Typography, { variant: "h5", children: object.icon }),
38455
+ /* @__PURE__ */ jsx(Typography, { variant: "h5", children: icon }),
38280
38456
  /* @__PURE__ */ jsxs(VStack, { gap: "none", children: [
38281
- /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-bold", children: object.name }),
38282
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("eventHandler.state") + ": " + object.currentState })
38457
+ /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-bold", children: name }),
38458
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("eventHandler.state") + ": " + currentState })
38283
38459
  ] })
38284
38460
  ] }),
38285
38461
  /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" }),
38286
38462
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
38287
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.rules", { count: object.rules.length, max: maxRules }) + ":" }),
38288
- object.rules.map((rule, i) => /* @__PURE__ */ jsx(
38463
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.rules", { count: rules.length, max: maxRules }) + ":" }),
38464
+ rules.map((rule, i) => /* @__PURE__ */ jsx(
38289
38465
  RuleEditor,
38290
38466
  {
38291
38467
  rule,
38292
- availableEvents: object.availableEvents,
38293
- availableActions: object.availableActions,
38468
+ availableEvents,
38469
+ availableActions,
38294
38470
  onChange: (r) => handleRuleChange(i, r),
38295
38471
  onRemove: () => handleRuleRemove(i),
38296
38472
  disabled
@@ -38308,6 +38484,7 @@ var init_ObjectRulePanel = __esm({
38308
38484
  init_cn();
38309
38485
  init_TraitStateViewer();
38310
38486
  init_RuleEditor();
38487
+ init_puzzleObject();
38311
38488
  nextRuleId = 1;
38312
38489
  ObjectRulePanel.displayName = "ObjectRulePanel";
38313
38490
  }
@@ -38374,11 +38551,11 @@ function EventHandlerBoard({
38374
38551
  }) {
38375
38552
  const { emit } = useEventBus();
38376
38553
  const { t } = useTranslate();
38377
- const resolved = Array.isArray(entity) ? entity[0] : entity;
38378
- const entityObjects = resolved?.objects ?? [];
38379
- const [objects, setObjects] = useState(entityObjects);
38554
+ const resolved = boardEntity(entity);
38555
+ const entityObjects = rows(resolved?.objects);
38556
+ const [objects, setObjects] = useState(() => [...entityObjects]);
38380
38557
  const [selectedObjectId, setSelectedObjectId] = useState(
38381
- entityObjects[0]?.id || null
38558
+ entityObjects[0] ? objId(entityObjects[0]) : null
38382
38559
  );
38383
38560
  const [headerError, setHeaderError] = useState(false);
38384
38561
  const [playState, setPlayState] = useState("editing");
@@ -38389,10 +38566,10 @@ function EventHandlerBoard({
38389
38566
  useEffect(() => () => {
38390
38567
  if (timerRef.current) clearTimeout(timerRef.current);
38391
38568
  }, []);
38392
- const selectedObject = objects.find((o) => o.id === selectedObjectId) || null;
38569
+ const selectedObject = objects.find((o) => objId(o) === selectedObjectId) || null;
38393
38570
  const handleRulesChange = useCallback((objectId, rules) => {
38394
38571
  setObjects((prev) => prev.map(
38395
- (o) => o.id === objectId ? { ...o, rules } : o
38572
+ (o) => objId(o) === objectId ? { ...o, rules } : o
38396
38573
  ));
38397
38574
  }, []);
38398
38575
  const addLogEntry = useCallback((icon, message, status = "done") => {
@@ -38406,11 +38583,12 @@ function EventHandlerBoard({
38406
38583
  setEventLog([]);
38407
38584
  const allRules = [];
38408
38585
  objects.forEach((obj) => {
38409
- obj.rules.forEach((rule) => {
38586
+ objRules(obj).forEach((rule) => {
38410
38587
  allRules.push({ object: obj, rule });
38411
38588
  });
38412
38589
  });
38413
- const triggers = resolved?.triggerEvents || [];
38590
+ const triggers = Array.isArray(resolved?.triggerEvents) ? resolved.triggerEvents : [];
38591
+ const goalEvent = str(resolved?.goalEvent);
38414
38592
  const eventQueue = [...triggers];
38415
38593
  const firedEvents = /* @__PURE__ */ new Set();
38416
38594
  let stepIdx = 0;
@@ -38439,14 +38617,14 @@ function EventHandlerBoard({
38439
38617
  addLogEntry("\u26A1", t("eventHandler.noListeners", { event: currentEvent }), "done");
38440
38618
  } else {
38441
38619
  matching.forEach(({ object, rule }) => {
38442
- addLogEntry(object.icon, t("eventHandler.heardEvent", { object: object.name, event: currentEvent, action: rule.thenAction }), "done");
38620
+ addLogEntry(objIcon(object), t("eventHandler.heardEvent", { object: objName(object), event: currentEvent, action: rule.thenAction }), "done");
38443
38621
  eventQueue.push(rule.thenAction);
38444
- if (rule.thenAction === resolved?.goalEvent) {
38622
+ if (rule.thenAction === goalEvent) {
38445
38623
  goalReached = true;
38446
38624
  }
38447
38625
  });
38448
38626
  }
38449
- if (currentEvent === resolved?.goalEvent) {
38627
+ if (currentEvent === goalEvent) {
38450
38628
  goalReached = true;
38451
38629
  }
38452
38630
  stepIdx++;
@@ -38464,65 +38642,75 @@ function EventHandlerBoard({
38464
38642
  }, []);
38465
38643
  const handleReset = useCallback(() => {
38466
38644
  if (timerRef.current) clearTimeout(timerRef.current);
38467
- setObjects(resolved?.objects ?? []);
38645
+ const resetObjects = rows(resolved?.objects);
38646
+ setObjects([...resetObjects]);
38468
38647
  setPlayState("editing");
38469
38648
  setEventLog([]);
38470
- setSelectedObjectId((resolved?.objects ?? [])[0]?.id || null);
38649
+ setSelectedObjectId(resetObjects[0] ? objId(resetObjects[0]) : null);
38471
38650
  setAttempts(0);
38472
38651
  }, [resolved?.objects]);
38473
38652
  if (!resolved) return null;
38474
38653
  const objectViewers = objects.map((obj) => {
38654
+ const states = objStates(obj);
38655
+ const currentState = objCurrentState(obj);
38475
38656
  const machine = {
38476
- name: obj.name,
38477
- states: obj.states,
38478
- currentState: obj.currentState,
38479
- transitions: obj.rules.map((r) => ({
38480
- from: obj.currentState,
38481
- to: obj.states.find((s) => s !== obj.currentState) || obj.currentState,
38657
+ name: objName(obj),
38658
+ states,
38659
+ currentState,
38660
+ transitions: objRules(obj).map((r) => ({
38661
+ from: currentState,
38662
+ to: states.find((s) => s !== currentState) || currentState,
38482
38663
  event: r.whenEvent
38483
38664
  }))
38484
38665
  };
38485
38666
  return { obj, machine };
38486
38667
  });
38487
- const showHint = attempts >= 3 && resolved.hint;
38668
+ const hint = str(resolved.hint);
38669
+ const showHint = attempts >= 3 && hint;
38670
+ const theme = resolved.theme ?? void 0;
38671
+ const themeBackground = theme?.background;
38672
+ const headerImage = str(resolved.headerImage);
38488
38673
  const encourageKey = ENCOURAGEMENT_KEYS[Math.min(attempts - 1, ENCOURAGEMENT_KEYS.length - 1)] ?? ENCOURAGEMENT_KEYS[0];
38489
38674
  return /* @__PURE__ */ jsxs(
38490
38675
  VStack,
38491
38676
  {
38492
38677
  className: cn("p-4 gap-6", className),
38493
38678
  style: {
38494
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
38679
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
38495
38680
  backgroundSize: "cover",
38496
38681
  backgroundPosition: "center"
38497
38682
  },
38498
38683
  children: [
38499
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
38684
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
38500
38685
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
38501
- /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
38502
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description }),
38686
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
38687
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) }),
38503
38688
  /* @__PURE__ */ jsxs(HStack, { className: "items-center p-2 rounded bg-primary/10 border border-primary/30", gap: "xs", children: [
38504
38689
  /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-primary font-bold", children: t("game.goal") + ":" }),
38505
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: resolved.goalCondition })
38690
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: str(resolved.goalCondition) })
38506
38691
  ] })
38507
38692
  ] }),
38508
38693
  /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
38509
38694
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.clickObject") + ":" }),
38510
- /* @__PURE__ */ jsx(HStack, { className: "flex-wrap", gap: "sm", children: objectViewers.map(({ obj, machine }) => /* @__PURE__ */ jsx(
38511
- Box,
38512
- {
38513
- className: cn(
38514
- "p-3 rounded-container border-2 cursor-pointer transition-all hover:scale-105",
38515
- selectedObjectId === obj.id ? "border-primary bg-primary/10" : "border-border bg-card hover:border-muted-foreground"
38516
- ),
38517
- onClick: () => setSelectedObjectId(obj.id),
38518
- children: /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "items-center min-w-[120px]", children: [
38519
- /* @__PURE__ */ jsx(Typography, { variant: "h5", children: obj.icon }),
38520
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground font-medium", children: obj.name }),
38521
- /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" })
38522
- ] })
38523
- },
38524
- obj.id
38525
- )) })
38695
+ /* @__PURE__ */ jsx(HStack, { className: "flex-wrap", gap: "sm", children: objectViewers.map(({ obj, machine }) => {
38696
+ const oid = objId(obj);
38697
+ return /* @__PURE__ */ jsx(
38698
+ Box,
38699
+ {
38700
+ className: cn(
38701
+ "p-3 rounded-container border-2 cursor-pointer transition-all hover:scale-105",
38702
+ selectedObjectId === oid ? "border-primary bg-primary/10" : "border-border bg-card hover:border-muted-foreground"
38703
+ ),
38704
+ onClick: () => setSelectedObjectId(oid),
38705
+ children: /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "items-center min-w-[120px]", children: [
38706
+ /* @__PURE__ */ jsx(Typography, { variant: "h5", children: objIcon(obj) }),
38707
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground font-medium", children: objName(obj) }),
38708
+ /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" })
38709
+ ] })
38710
+ },
38711
+ oid
38712
+ );
38713
+ }) })
38526
38714
  ] }),
38527
38715
  selectedObject && /* @__PURE__ */ jsx(
38528
38716
  ObjectRulePanel,
@@ -38533,12 +38721,12 @@ function EventHandlerBoard({
38533
38721
  }
38534
38722
  ),
38535
38723
  eventLog.length > 0 && /* @__PURE__ */ jsx(EventLog, { entries: eventLog }),
38536
- playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: resolved.successMessage || t("eventHandler.chainComplete") }) }),
38724
+ playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("eventHandler.chainComplete") }) }),
38537
38725
  playState === "fail" && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
38538
38726
  /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(encourageKey) }) }),
38539
38727
  showHint && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
38540
38728
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
38541
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
38729
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
38542
38730
  ] }) })
38543
38731
  ] }),
38544
38732
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
@@ -38566,6 +38754,8 @@ var init_EventHandlerBoard = __esm({
38566
38754
  init_TraitStateViewer();
38567
38755
  init_ObjectRulePanel();
38568
38756
  init_EventLog();
38757
+ init_puzzleObject();
38758
+ init_boardEntity();
38569
38759
  ENCOURAGEMENT_KEYS = [
38570
38760
  "puzzle.tryAgain1",
38571
38761
  "puzzle.tryAgain2",
@@ -38654,7 +38844,10 @@ var init_FeatureGridOrganism = __esm({
38654
38844
  );
38655
38845
  useCallback(
38656
38846
  (feature) => {
38657
- eventBus.emit("UI:FEATURE_CLICK", { id: feature.id, href: feature.href ?? "" });
38847
+ eventBus.emit("UI:FEATURE_CLICK", {
38848
+ id: String(feature.id ?? ""),
38849
+ href: String(feature.href ?? "")
38850
+ });
38658
38851
  },
38659
38852
  [eventBus]
38660
38853
  );
@@ -38664,14 +38857,17 @@ var init_FeatureGridOrganism = __esm({
38664
38857
  if (error) {
38665
38858
  return /* @__PURE__ */ jsx(ErrorState, { message: error.message, className });
38666
38859
  }
38667
- const featureCards = items.map((feature) => ({
38668
- icon: feature.icon,
38669
- title: feature.title,
38670
- description: feature.description,
38671
- href: feature.href,
38672
- linkLabel: feature.linkLabel,
38673
- variant: feature.href ? "interactive" : "bordered"
38674
- }));
38860
+ const featureCards = items.map((feature) => {
38861
+ const href = feature.href != null ? String(feature.href) : void 0;
38862
+ return {
38863
+ icon: feature.icon != null ? String(feature.icon) : void 0,
38864
+ title: String(feature.title ?? ""),
38865
+ description: String(feature.description ?? ""),
38866
+ href,
38867
+ linkLabel: feature.linkLabel != null ? String(feature.linkLabel) : void 0,
38868
+ variant: href ? "interactive" : "bordered"
38869
+ };
38870
+ });
38675
38871
  return /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
38676
38872
  (heading || subtitle) && /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
38677
38873
  heading && /* @__PURE__ */ jsx(Typography, { variant: "h2", align: "center", children: heading }),
@@ -39989,22 +40185,24 @@ var init_HeroOrganism = __esm({
39989
40185
  () => Array.isArray(entity) ? entity[0] : entity && typeof entity === "object" ? entity : void 0,
39990
40186
  [entity]
39991
40187
  );
40188
+ const primaryAction = resolved?.primaryAction;
40189
+ const secondaryAction = resolved?.secondaryAction;
39992
40190
  const handlePrimaryClick = useCallback(() => {
39993
- if (resolved?.primaryAction) {
40191
+ if (primaryAction) {
39994
40192
  eventBus.emit("UI:CTA_PRIMARY", {
39995
- label: resolved.primaryAction.label,
39996
- href: resolved.primaryAction.href
40193
+ label: String(primaryAction.label ?? ""),
40194
+ href: String(primaryAction.href ?? "")
39997
40195
  });
39998
40196
  }
39999
- }, [eventBus, resolved]);
40197
+ }, [eventBus, primaryAction]);
40000
40198
  const handleSecondaryClick = useCallback(() => {
40001
- if (resolved?.secondaryAction) {
40199
+ if (secondaryAction) {
40002
40200
  eventBus.emit("UI:CTA_SECONDARY", {
40003
- label: resolved.secondaryAction.label,
40004
- href: resolved.secondaryAction.href
40201
+ label: String(secondaryAction.label ?? ""),
40202
+ href: String(secondaryAction.href ?? "")
40005
40203
  });
40006
40204
  }
40007
- }, [eventBus, resolved]);
40205
+ }, [eventBus, secondaryAction]);
40008
40206
  if (isLoading) {
40009
40207
  return /* @__PURE__ */ jsx(LoadingState, { message: t("common.loading"), className });
40010
40208
  }
@@ -40014,17 +40212,19 @@ var init_HeroOrganism = __esm({
40014
40212
  if (!resolved) {
40015
40213
  return null;
40016
40214
  }
40215
+ const imageRaw = resolved.image;
40216
+ const image = imageRaw ? { src: String(imageRaw.src ?? ""), alt: String(imageRaw.alt ?? "") } : void 0;
40017
40217
  return /* @__PURE__ */ jsxs(
40018
40218
  HeroSection,
40019
40219
  {
40020
- tag: resolved.tag,
40021
- title: resolved.title,
40022
- titleAccent: resolved.titleAccent,
40023
- subtitle: resolved.subtitle,
40024
- primaryAction: resolved.primaryAction ? { label: resolved.primaryAction.label, href: resolved.primaryAction.href } : void 0,
40025
- secondaryAction: resolved.secondaryAction ? { label: resolved.secondaryAction.label, href: resolved.secondaryAction.href } : void 0,
40026
- installCommand: resolved.installCommand,
40027
- image: resolved.image,
40220
+ tag: resolved.tag != null ? String(resolved.tag) : void 0,
40221
+ title: String(resolved.title ?? ""),
40222
+ titleAccent: resolved.titleAccent != null ? String(resolved.titleAccent) : void 0,
40223
+ subtitle: String(resolved.subtitle ?? ""),
40224
+ primaryAction: primaryAction ? { label: String(primaryAction.label ?? ""), href: String(primaryAction.href ?? "") } : void 0,
40225
+ secondaryAction: secondaryAction ? { label: String(secondaryAction.label ?? ""), href: String(secondaryAction.href ?? "") } : void 0,
40226
+ installCommand: resolved.installCommand != null ? String(resolved.installCommand) : void 0,
40227
+ image,
40028
40228
  imagePosition: resolved.imagePosition,
40029
40229
  background: resolved.background,
40030
40230
  className: cn(className),
@@ -40033,8 +40233,8 @@ var init_HeroOrganism = __esm({
40033
40233
  /* @__PURE__ */ jsx(
40034
40234
  _HeroClickInterceptor,
40035
40235
  {
40036
- hasPrimary: !!resolved.primaryAction,
40037
- hasSecondary: !!resolved.secondaryAction,
40236
+ hasPrimary: !!primaryAction,
40237
+ hasSecondary: !!secondaryAction,
40038
40238
  onPrimaryClick: handlePrimaryClick,
40039
40239
  onSecondaryClick: handleSecondaryClick
40040
40240
  }
@@ -40263,7 +40463,7 @@ function formatValue3(value, fieldName) {
40263
40463
  return String(value);
40264
40464
  }
40265
40465
  function formatFieldLabel2(fieldName) {
40266
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).replace(/Id$/, "").trim();
40466
+ return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str2) => str2.toUpperCase()).replace(/Id$/, "").trim();
40267
40467
  }
40268
40468
  var STATUS_STYLES2, StatusBadge, ProgressIndicator, List3;
40269
40469
  var init_List = __esm({
@@ -41110,20 +41310,22 @@ function NegotiatorBoard({
41110
41310
  }) {
41111
41311
  const { emit } = useEventBus();
41112
41312
  const { t } = useTranslate();
41113
- const resolved = Array.isArray(entity) ? entity[0] : entity;
41313
+ const resolved = boardEntity(entity);
41114
41314
  const [history, setHistory] = useState([]);
41115
41315
  const [headerError, setHeaderError] = useState(false);
41116
41316
  const [showHint, setShowHint] = useState(false);
41317
+ const totalRounds = num(resolved?.totalRounds);
41318
+ const targetScore = num(resolved?.targetScore);
41117
41319
  const currentRound = history.length;
41118
- const isComplete = currentRound >= (resolved?.totalRounds ?? 0);
41320
+ const isComplete = currentRound >= totalRounds;
41119
41321
  const playerTotal = history.reduce((s, r) => s + r.playerPayoff, 0);
41120
41322
  const opponentTotal = history.reduce((s, r) => s + r.opponentPayoff, 0);
41121
- const won = isComplete && playerTotal >= (resolved?.targetScore ?? 0);
41122
- const actions = resolved?.actions ?? [];
41123
- const payoffMatrix = resolved?.payoffMatrix ?? [];
41323
+ const won = isComplete && playerTotal >= targetScore;
41324
+ const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
41325
+ const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
41124
41326
  const handleAction = useCallback((actionId) => {
41125
41327
  if (isComplete) return;
41126
- const opponentAction = getOpponentAction(resolved?.opponentStrategy ?? "random", actions, history);
41328
+ const opponentAction = getOpponentAction(str(resolved?.opponentStrategy) || "random", actions, history);
41127
41329
  const payoff = payoffMatrix.find(
41128
41330
  (p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
41129
41331
  );
@@ -41136,42 +41338,46 @@ function NegotiatorBoard({
41136
41338
  };
41137
41339
  const newHistory = [...history, result];
41138
41340
  setHistory(newHistory);
41139
- if (newHistory.length >= (resolved?.totalRounds ?? 0)) {
41341
+ if (newHistory.length >= totalRounds) {
41140
41342
  const total = newHistory.reduce((s, r) => s + r.playerPayoff, 0);
41141
- if (total >= (resolved?.targetScore ?? 0)) {
41343
+ if (total >= targetScore) {
41142
41344
  emit(`UI:${completeEvent}`, { success: true, score: total });
41143
41345
  }
41144
- if (newHistory.length >= 3 && resolved?.hint) {
41346
+ if (newHistory.length >= 3 && str(resolved?.hint)) {
41145
41347
  setShowHint(true);
41146
41348
  }
41147
41349
  }
41148
- }, [isComplete, resolved, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
41350
+ }, [isComplete, resolved, totalRounds, targetScore, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
41149
41351
  const handleReset = () => {
41150
41352
  setHistory([]);
41151
41353
  setShowHint(false);
41152
41354
  };
41153
41355
  const getActionLabel = (id) => actions.find((a) => a.id === id)?.label ?? id;
41154
41356
  if (!resolved) return null;
41357
+ const theme = resolved.theme ?? void 0;
41358
+ const themeBackground = theme?.background;
41359
+ const headerImage = str(resolved.headerImage);
41360
+ const hint = str(resolved.hint);
41155
41361
  return /* @__PURE__ */ jsx(
41156
41362
  Box,
41157
41363
  {
41158
41364
  className,
41159
41365
  style: {
41160
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
41366
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
41161
41367
  backgroundSize: "cover",
41162
41368
  backgroundPosition: "center"
41163
41369
  },
41164
41370
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
41165
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
41371
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
41166
41372
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
41167
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
41168
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description }),
41373
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
41374
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) }),
41169
41375
  /* @__PURE__ */ jsxs(HStack, { gap: "md", children: [
41170
- /* @__PURE__ */ jsx(Badge, { size: "sm", children: t("negotiator.round", { current: String(currentRound), total: String(resolved.totalRounds) }) }),
41376
+ /* @__PURE__ */ jsx(Badge, { size: "sm", children: t("negotiator.round", { current: String(currentRound), total: String(totalRounds) }) }),
41171
41377
  /* @__PURE__ */ jsxs(Badge, { size: "sm", children: [
41172
41378
  t("negotiator.target"),
41173
41379
  ": ",
41174
- resolved.targetScore
41380
+ targetScore
41175
41381
  ] })
41176
41382
  ] })
41177
41383
  ] }) }),
@@ -41220,16 +41426,16 @@ function NegotiatorBoard({
41220
41426
  ] }) }),
41221
41427
  isComplete && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
41222
41428
  /* @__PURE__ */ jsx(Icon, { icon: CheckCircle, size: "lg", className: won ? "text-success" : "text-error" }),
41223
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: won ? resolved.successMessage ?? t("negotiator.success") : resolved.failMessage ?? t("negotiator.failed") }),
41429
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: won ? str(resolved.successMessage) || t("negotiator.success") : str(resolved.failMessage) || t("negotiator.failed") }),
41224
41430
  /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
41225
41431
  t("negotiator.finalScore"),
41226
41432
  ": ",
41227
41433
  playerTotal,
41228
41434
  "/",
41229
- resolved.targetScore
41435
+ targetScore
41230
41436
  ] })
41231
41437
  ] }) }),
41232
- showHint && resolved.hint && !won && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
41438
+ showHint && hint && !won && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
41233
41439
  isComplete && !won && /* @__PURE__ */ jsx(HStack, { justify: "center", children: /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleReset, children: t("negotiator.playAgain") }) })
41234
41440
  ] })
41235
41441
  }
@@ -41239,6 +41445,7 @@ var init_NegotiatorBoard = __esm({
41239
41445
  "components/game/organisms/puzzles/negotiator/NegotiatorBoard.tsx"() {
41240
41446
  init_atoms2();
41241
41447
  init_useEventBus();
41448
+ init_boardEntity();
41242
41449
  NegotiatorBoard.displayName = "NegotiatorBoard";
41243
41450
  }
41244
41451
  });
@@ -41274,13 +41481,13 @@ var init_PricingOrganism = __esm({
41274
41481
  return /* @__PURE__ */ jsx(ErrorState, { message: error.message, className });
41275
41482
  }
41276
41483
  const plans = items.map((plan) => ({
41277
- name: plan.name,
41278
- price: plan.price,
41279
- description: plan.description,
41280
- features: plan.features,
41281
- action: { label: plan.actionLabel, href: plan.actionHref },
41282
- highlighted: plan.highlighted,
41283
- badge: plan.badge
41484
+ name: String(plan.name ?? ""),
41485
+ price: String(plan.price ?? ""),
41486
+ description: plan.description != null ? String(plan.description) : void 0,
41487
+ features: (plan.features ?? []).map((f3) => String(f3)),
41488
+ action: { label: String(plan.actionLabel ?? ""), href: String(plan.actionHref ?? "") },
41489
+ highlighted: Boolean(plan.highlighted),
41490
+ badge: plan.badge != null ? String(plan.badge) : void 0
41284
41491
  }));
41285
41492
  return /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
41286
41493
  (heading || subtitle) && /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
@@ -43370,16 +43577,20 @@ function SequencerBoard({
43370
43577
  }) {
43371
43578
  const { emit } = useEventBus();
43372
43579
  const { t } = useTranslate();
43373
- const resolved = Array.isArray(entity) ? entity[0] : entity;
43580
+ const resolved = boardEntity(entity);
43581
+ const maxSlots = num(resolved?.maxSlots);
43582
+ const solutions = Array.isArray(resolved?.solutions) ? resolved.solutions : [];
43583
+ const availableActions = Array.isArray(resolved?.availableActions) ? resolved.availableActions : [];
43584
+ const allowDuplicates = resolved?.allowDuplicates !== false;
43374
43585
  const [headerError, setHeaderError] = useState(false);
43375
43586
  const [slots, setSlots] = useState(
43376
- () => Array.from({ length: resolved?.maxSlots ?? 0 }, () => void 0)
43587
+ () => Array.from({ length: maxSlots }, () => void 0)
43377
43588
  );
43378
43589
  const [playState, setPlayState] = useState("idle");
43379
43590
  const [currentStep, setCurrentStep] = useState(-1);
43380
43591
  const [attempts, setAttempts] = useState(0);
43381
43592
  const [slotFeedback, setSlotFeedback] = useState(
43382
- () => Array.from({ length: resolved?.maxSlots ?? 0 }, () => null)
43593
+ () => Array.from({ length: maxSlots }, () => null)
43383
43594
  );
43384
43595
  const timerRef = useRef(null);
43385
43596
  useEffect(() => () => {
@@ -43413,17 +43624,17 @@ function SequencerBoard({
43413
43624
  }, [emit]);
43414
43625
  const handleReset = useCallback(() => {
43415
43626
  if (timerRef.current) clearTimeout(timerRef.current);
43416
- setSlots(Array.from({ length: resolved?.maxSlots ?? 0 }, () => void 0));
43627
+ setSlots(Array.from({ length: maxSlots }, () => void 0));
43417
43628
  setPlayState("idle");
43418
43629
  setCurrentStep(-1);
43419
43630
  setAttempts(0);
43420
- setSlotFeedback(Array.from({ length: resolved?.maxSlots ?? 0 }, () => null));
43421
- }, [resolved?.maxSlots]);
43631
+ setSlotFeedback(Array.from({ length: maxSlots }, () => null));
43632
+ }, [maxSlots]);
43422
43633
  const filledSlots = slots.filter((s) => !!s);
43423
43634
  const canPlay = filledSlots.length > 0 && playState === "idle";
43424
43635
  const handlePlay = useCallback(() => {
43425
43636
  if (!canPlay) return;
43426
- setSlotFeedback(Array.from({ length: resolved?.maxSlots ?? 0 }, () => null));
43637
+ setSlotFeedback(Array.from({ length: maxSlots }, () => null));
43427
43638
  emit("UI:PLAY_SOUND", { key: "confirm" });
43428
43639
  const sequence = slots.map((s) => s?.id || "");
43429
43640
  if (playEvent) {
@@ -43434,10 +43645,10 @@ function SequencerBoard({
43434
43645
  let step = 0;
43435
43646
  const advance = () => {
43436
43647
  step++;
43437
- if (step >= (resolved?.maxSlots ?? 0)) {
43648
+ if (step >= maxSlots) {
43438
43649
  const playerSeq = slots.map((s) => s?.id);
43439
43650
  const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
43440
- const success = (resolved?.solutions ?? []).some(
43651
+ const success = solutions.some(
43441
43652
  (sol) => sol.length === playerIds.length && sol.every((id, i) => id === playerIds[i])
43442
43653
  );
43443
43654
  if (success) {
@@ -43449,7 +43660,7 @@ function SequencerBoard({
43449
43660
  }
43450
43661
  } else {
43451
43662
  setAttempts((prev) => prev + 1);
43452
- const feedback = computeSlotFeedback(playerSeq, resolved?.solutions ?? []);
43663
+ const feedback = computeSlotFeedback(playerSeq, solutions);
43453
43664
  setSlotFeedback(feedback);
43454
43665
  setPlayState("idle");
43455
43666
  setCurrentStep(-1);
@@ -43467,10 +43678,10 @@ function SequencerBoard({
43467
43678
  }
43468
43679
  };
43469
43680
  timerRef.current = setTimeout(advance, stepDurationMs);
43470
- }, [canPlay, slots, resolved?.maxSlots, resolved?.solutions, stepDurationMs, playEvent, completeEvent, emit]);
43681
+ }, [canPlay, slots, maxSlots, solutions, stepDurationMs, playEvent, completeEvent, emit]);
43471
43682
  const machine = {
43472
- name: resolved?.title ?? "",
43473
- description: resolved?.description ?? "",
43683
+ name: str(resolved?.title),
43684
+ description: str(resolved?.description),
43474
43685
  states: slots.map((s, i) => stepLabel(s, i)),
43475
43686
  currentState: currentStep >= 0 ? stepLabel(slots[currentStep], currentStep) : "__idle__",
43476
43687
  transitions: slots.slice(0, -1).map((s, i) => ({
@@ -43479,37 +43690,41 @@ function SequencerBoard({
43479
43690
  event: "NEXT"
43480
43691
  }))
43481
43692
  };
43482
- const usedIds = resolved?.allowDuplicates === false ? slots.filter(Boolean).map((s) => s?.id || "") : [];
43483
- const showHint = attempts >= 3 && !!resolved?.hint;
43693
+ const usedIds = !allowDuplicates ? slots.filter(Boolean).map((s) => s?.id || "") : [];
43694
+ const hint = str(resolved?.hint);
43695
+ const showHint = attempts >= 3 && !!hint;
43484
43696
  const hasFeedback = slotFeedback.some((f3) => f3 !== null);
43485
43697
  const correctCount = slotFeedback.filter((f3) => f3 === "correct").length;
43486
43698
  const encourageKey = ENCOURAGEMENT_KEYS2[Math.min(attempts - 1, ENCOURAGEMENT_KEYS2.length - 1)] ?? ENCOURAGEMENT_KEYS2[0];
43487
43699
  if (!resolved) return null;
43700
+ const theme = resolved.theme ?? void 0;
43701
+ const themeBackground = theme?.background;
43702
+ const headerImage = str(resolved.headerImage);
43488
43703
  return /* @__PURE__ */ jsxs(
43489
43704
  VStack,
43490
43705
  {
43491
43706
  className: cn("p-4 gap-6", className),
43492
43707
  style: {
43493
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
43708
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
43494
43709
  backgroundSize: "cover",
43495
43710
  backgroundPosition: "center"
43496
43711
  },
43497
43712
  children: [
43498
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
43713
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
43499
43714
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
43500
- /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
43501
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description })
43715
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
43716
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) })
43502
43717
  ] }),
43503
43718
  showHint && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
43504
43719
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
43505
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
43720
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
43506
43721
  ] }) }),
43507
43722
  filledSlots.length > 0 && /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "linear", size: "md" }),
43508
43723
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
43509
43724
  /* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between", children: [
43510
43725
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("sequencer.yourSequence") + ":" }),
43511
43726
  hasFeedback && playState === "idle" && /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
43512
- `${correctCount}/${resolved.maxSlots} `,
43727
+ `${correctCount}/${maxSlots} `,
43513
43728
  "\u2705"
43514
43729
  ] })
43515
43730
  ] }),
@@ -43517,7 +43732,7 @@ function SequencerBoard({
43517
43732
  SequenceBar,
43518
43733
  {
43519
43734
  slots,
43520
- maxSlots: resolved.maxSlots,
43735
+ maxSlots,
43521
43736
  onSlotDrop: handleSlotDrop,
43522
43737
  onSlotRemove: handleSlotRemove,
43523
43738
  playing: playState === "playing",
@@ -43531,15 +43746,15 @@ function SequencerBoard({
43531
43746
  playState !== "playing" && /* @__PURE__ */ jsx(
43532
43747
  ActionPalette,
43533
43748
  {
43534
- actions: resolved.availableActions,
43749
+ actions: availableActions,
43535
43750
  usedActionIds: usedIds,
43536
- allowDuplicates: resolved.allowDuplicates !== false,
43751
+ allowDuplicates,
43537
43752
  categoryColors,
43538
43753
  label: t("sequencer.dragActions")
43539
43754
  }
43540
43755
  ),
43541
43756
  hasFeedback && playState === "idle" && attempts > 0 && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: t(encourageKey) }) }),
43542
- playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: resolved.successMessage || t("sequencer.levelComplete") }) }),
43757
+ playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("sequencer.levelComplete") }) }),
43543
43758
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
43544
43759
  /* @__PURE__ */ jsx(
43545
43760
  Button,
@@ -43563,6 +43778,7 @@ var init_SequencerBoard = __esm({
43563
43778
  init_cn();
43564
43779
  init_useEventBus();
43565
43780
  init_TraitStateViewer();
43781
+ init_boardEntity();
43566
43782
  init_SequenceBar();
43567
43783
  init_ActionPalette();
43568
43784
  ENCOURAGEMENT_KEYS2 = [
@@ -43612,18 +43828,21 @@ var init_ShowcaseOrganism = __esm({
43612
43828
  heading && /* @__PURE__ */ jsx(Typography, { variant: "h2", align: "center", children: heading }),
43613
43829
  subtitle && /* @__PURE__ */ jsx(Typography, { variant: "body1", color: "muted", align: "center", className: "max-w-2xl", children: subtitle })
43614
43830
  ] }),
43615
- /* @__PURE__ */ jsx(SimpleGrid, { cols: columns, gap: "lg", children: items.map((item) => /* @__PURE__ */ jsx(
43616
- ShowcaseCard,
43617
- {
43618
- title: item.title,
43619
- description: item.description,
43620
- image: item.image,
43621
- href: item.href,
43622
- badge: item.badge,
43623
- accentColor: item.accentColor
43624
- },
43625
- item.id
43626
- )) })
43831
+ /* @__PURE__ */ jsx(SimpleGrid, { cols: columns, gap: "lg", children: items.map((item) => {
43832
+ const imageRaw = item.image;
43833
+ return /* @__PURE__ */ jsx(
43834
+ ShowcaseCard,
43835
+ {
43836
+ title: String(item.title ?? ""),
43837
+ description: item.description != null ? String(item.description) : void 0,
43838
+ image: { src: String(imageRaw?.src ?? ""), alt: String(imageRaw?.alt ?? "") },
43839
+ href: item.href != null ? String(item.href) : void 0,
43840
+ badge: item.badge != null ? String(item.badge) : void 0,
43841
+ accentColor: item.accentColor != null ? String(item.accentColor) : void 0
43842
+ },
43843
+ String(item.id ?? "")
43844
+ );
43845
+ }) })
43627
43846
  ] });
43628
43847
  };
43629
43848
  ShowcaseOrganism.displayName = "ShowcaseOrganism";
@@ -43991,8 +44210,8 @@ function SimulatorBoard({
43991
44210
  }) {
43992
44211
  const { emit } = useEventBus();
43993
44212
  const { t } = useTranslate();
43994
- const resolved = Array.isArray(entity) ? entity[0] : entity;
43995
- const parameters = resolved?.parameters ?? [];
44213
+ const resolved = boardEntity(entity);
44214
+ const parameters = Array.isArray(resolved?.parameters) ? resolved.parameters : [];
43996
44215
  const [values, setValues] = useState(() => {
43997
44216
  const init = {};
43998
44217
  for (const p2 of parameters) {
@@ -44006,15 +44225,15 @@ function SimulatorBoard({
44006
44225
  const [showHint, setShowHint] = useState(false);
44007
44226
  const computeOutput = useCallback((params) => {
44008
44227
  try {
44009
- const fn = new Function("params", `return (${resolved?.computeExpression})`);
44228
+ const fn = new Function("params", `return (${str(resolved?.computeExpression)})`);
44010
44229
  return fn(params);
44011
44230
  } catch {
44012
44231
  return 0;
44013
44232
  }
44014
44233
  }, [resolved?.computeExpression]);
44015
44234
  const output = useMemo(() => computeOutput(values) ?? 0, [computeOutput, values]);
44016
- const targetValue = resolved?.targetValue ?? 0;
44017
- const targetTolerance = resolved?.targetTolerance ?? 0;
44235
+ const targetValue = num(resolved?.targetValue);
44236
+ const targetTolerance = num(resolved?.targetTolerance);
44018
44237
  const isCorrect = Math.abs(output - targetValue) <= targetTolerance;
44019
44238
  const handleParameterChange = (id, value) => {
44020
44239
  if (submitted) return;
@@ -44029,7 +44248,7 @@ function SimulatorBoard({
44029
44248
  };
44030
44249
  const handleReset = () => {
44031
44250
  setSubmitted(false);
44032
- if (attempts >= 2 && resolved?.hint) {
44251
+ if (attempts >= 2 && str(resolved?.hint)) {
44033
44252
  setShowHint(true);
44034
44253
  }
44035
44254
  };
@@ -44044,20 +44263,26 @@ function SimulatorBoard({
44044
44263
  setShowHint(false);
44045
44264
  };
44046
44265
  if (!resolved) return null;
44266
+ const theme = resolved.theme ?? void 0;
44267
+ const themeBackground = theme?.background;
44268
+ const headerImage = str(resolved.headerImage);
44269
+ const hint = str(resolved.hint);
44270
+ const outputLabel = str(resolved.outputLabel);
44271
+ const outputUnit = str(resolved.outputUnit);
44047
44272
  return /* @__PURE__ */ jsx(
44048
44273
  Box,
44049
44274
  {
44050
44275
  className,
44051
44276
  style: {
44052
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
44277
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
44053
44278
  backgroundSize: "cover",
44054
44279
  backgroundPosition: "center"
44055
44280
  },
44056
44281
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
44057
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
44282
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
44058
44283
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
44059
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
44060
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description })
44284
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
44285
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) })
44061
44286
  ] }) }),
44062
44287
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", children: [
44063
44288
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("simulator.parameters") }),
@@ -44098,28 +44323,28 @@ function SimulatorBoard({
44098
44323
  ] }, param.id))
44099
44324
  ] }) }),
44100
44325
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
44101
- /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: resolved.outputLabel }),
44326
+ /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: outputLabel }),
44102
44327
  /* @__PURE__ */ jsxs(Typography, { variant: "h3", weight: "bold", children: [
44103
44328
  output.toFixed(2),
44104
44329
  " ",
44105
- resolved.outputUnit
44330
+ outputUnit
44106
44331
  ] }),
44107
44332
  submitted && /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
44108
44333
  /* @__PURE__ */ jsx(Icon, { icon: isCorrect ? CheckCircle : XCircle, size: "sm", className: isCorrect ? "text-success" : "text-error" }),
44109
- /* @__PURE__ */ jsx(Typography, { variant: "body", className: isCorrect ? "text-success" : "text-error", children: isCorrect ? resolved.successMessage ?? t("simulator.correct") : resolved.failMessage ?? t("simulator.incorrect") })
44334
+ /* @__PURE__ */ jsx(Typography, { variant: "body", className: isCorrect ? "text-success" : "text-error", children: isCorrect ? str(resolved.successMessage) || t("simulator.correct") : str(resolved.failMessage) || t("simulator.incorrect") })
44110
44335
  ] }),
44111
44336
  /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
44112
44337
  t("simulator.target"),
44113
44338
  ": ",
44114
44339
  targetValue,
44115
44340
  " ",
44116
- resolved.outputUnit,
44341
+ outputUnit,
44117
44342
  " (\xB1",
44118
44343
  targetTolerance,
44119
44344
  ")"
44120
44345
  ] })
44121
44346
  ] }) }),
44122
- showHint && resolved.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
44347
+ showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
44123
44348
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
44124
44349
  !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, children: [
44125
44350
  /* @__PURE__ */ jsx(Icon, { icon: Play, size: "sm" }),
@@ -44138,6 +44363,7 @@ var init_SimulatorBoard = __esm({
44138
44363
  "components/game/organisms/puzzles/simulator/SimulatorBoard.tsx"() {
44139
44364
  init_atoms2();
44140
44365
  init_useEventBus();
44366
+ init_boardEntity();
44141
44367
  SimulatorBoard.displayName = "SimulatorBoard";
44142
44368
  }
44143
44369
  });
@@ -44563,22 +44789,25 @@ function VariablePanel({
44563
44789
  return /* @__PURE__ */ jsxs(VStack, { className: cn("p-3 rounded-lg bg-card border border-border", className), gap: "sm", children: [
44564
44790
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("stateArchitect.variables", { name: entityName }) }),
44565
44791
  variables.map((v) => {
44566
- const max = v.max ?? 100;
44567
- const min = v.min ?? 0;
44568
- const pct = Math.round((v.value - min) / (max - min) * 100);
44792
+ const name = v.name == null ? "" : String(v.name);
44793
+ const value = numField(v.value);
44794
+ const max = numField(v.max, 100);
44795
+ const min = numField(v.min, 0);
44796
+ const unit = v.unit == null ? "" : String(v.unit);
44797
+ const pct = Math.round((value - min) / (max - min) * 100);
44569
44798
  const isHigh = pct > 80;
44570
44799
  const isLow = pct < 20;
44571
44800
  return /* @__PURE__ */ jsxs(VStack, { gap: "none", children: [
44572
44801
  /* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between", children: [
44573
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground font-medium", children: v.name }),
44802
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground font-medium", children: name }),
44574
44803
  /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: cn(
44575
44804
  isHigh ? "text-error" : isLow ? "text-warning" : "text-foreground"
44576
44805
  ), children: [
44577
- v.value,
44578
- v.unit || "",
44806
+ value,
44807
+ unit,
44579
44808
  " / ",
44580
44809
  max,
44581
- v.unit || ""
44810
+ unit
44582
44811
  ] })
44583
44812
  ] }),
44584
44813
  /* @__PURE__ */ jsx(
@@ -44589,14 +44818,19 @@ function VariablePanel({
44589
44818
  size: "sm"
44590
44819
  }
44591
44820
  )
44592
- ] }, v.name);
44821
+ ] }, name);
44593
44822
  })
44594
44823
  ] });
44595
44824
  }
44825
+ var numField;
44596
44826
  var init_VariablePanel = __esm({
44597
44827
  "components/game/organisms/puzzles/state-architect/VariablePanel.tsx"() {
44598
44828
  init_atoms2();
44599
44829
  init_cn();
44830
+ numField = (v, fallback = 0) => {
44831
+ const n = Number(v);
44832
+ return Number.isFinite(n) ? n : fallback;
44833
+ };
44600
44834
  VariablePanel.displayName = "VariablePanel";
44601
44835
  }
44602
44836
  });
@@ -44623,14 +44857,21 @@ function StateArchitectBoard({
44623
44857
  }) {
44624
44858
  const { emit } = useEventBus();
44625
44859
  const { t } = useTranslate();
44626
- const resolved = Array.isArray(entity) ? entity[0] : entity;
44627
- const [transitions, setTransitions] = useState(resolved?.transitions ?? []);
44860
+ const resolved = boardEntity(entity);
44861
+ const entityStates = Array.isArray(resolved?.states) ? resolved.states : [];
44862
+ const initialState = str(resolved?.initialState);
44863
+ const entityName = str(resolved?.entityName);
44864
+ const availableEvents = Array.isArray(resolved?.availableEvents) ? resolved.availableEvents : [];
44865
+ const testCases = Array.isArray(resolved?.testCases) ? resolved.testCases : [];
44866
+ const entityTransitions = Array.isArray(resolved?.transitions) ? resolved.transitions : [];
44867
+ const entityVariables = rows(resolved?.variables);
44868
+ const [transitions, setTransitions] = useState(entityTransitions);
44628
44869
  const [headerError, setHeaderError] = useState(false);
44629
44870
  const [playState, setPlayState] = useState("editing");
44630
- const [currentState, setCurrentState] = useState(resolved?.initialState ?? "");
44871
+ const [currentState, setCurrentState] = useState(initialState);
44631
44872
  const [selectedState, setSelectedState] = useState(null);
44632
44873
  const [testResults, setTestResults] = useState([]);
44633
- const [variables, setVariables] = useState(resolved?.variables ?? []);
44874
+ const [variables, setVariables] = useState(() => [...entityVariables]);
44634
44875
  const [attempts, setAttempts] = useState(0);
44635
44876
  const timerRef = useRef(null);
44636
44877
  const [addingFrom, setAddingFrom] = useState(null);
@@ -44639,12 +44880,12 @@ function StateArchitectBoard({
44639
44880
  }, []);
44640
44881
  const GRAPH_W = 500;
44641
44882
  const GRAPH_H = 400;
44642
- const positions = useMemo(() => layoutStates(resolved?.states ?? [], GRAPH_W, GRAPH_H), [resolved?.states]);
44883
+ const positions = useMemo(() => layoutStates(entityStates, GRAPH_W, GRAPH_H), [entityStates]);
44643
44884
  const handleStateClick = useCallback((state) => {
44644
44885
  if (playState !== "editing") return;
44645
44886
  if (addingFrom) {
44646
44887
  if (addingFrom !== state) {
44647
- const event = resolved?.availableEvents[0] || "EVENT";
44888
+ const event = availableEvents[0] || "EVENT";
44648
44889
  const newTrans = {
44649
44890
  id: `t-${nextTransId++}`,
44650
44891
  from: addingFrom,
@@ -44657,7 +44898,7 @@ function StateArchitectBoard({
44657
44898
  } else {
44658
44899
  setSelectedState(state);
44659
44900
  }
44660
- }, [playState, addingFrom, resolved?.availableEvents]);
44901
+ }, [playState, addingFrom, availableEvents]);
44661
44902
  const handleStartAddTransition = useCallback(() => {
44662
44903
  if (!selectedState) return;
44663
44904
  setAddingFrom(selectedState);
@@ -44666,9 +44907,9 @@ function StateArchitectBoard({
44666
44907
  setTransitions((prev) => prev.filter((t2) => t2.id !== transId));
44667
44908
  }, []);
44668
44909
  const machine = useMemo(() => ({
44669
- name: resolved?.entityName ?? "",
44670
- description: resolved?.description ?? "",
44671
- states: resolved?.states ?? [],
44910
+ name: entityName,
44911
+ description: str(resolved?.description),
44912
+ states: entityStates,
44672
44913
  currentState,
44673
44914
  transitions: transitions.map((t2) => ({
44674
44915
  from: t2.from,
@@ -44676,7 +44917,7 @@ function StateArchitectBoard({
44676
44917
  event: t2.event,
44677
44918
  guardHint: t2.guardHint
44678
44919
  }))
44679
- }), [resolved, currentState, transitions]);
44920
+ }), [entityName, resolved, entityStates, currentState, transitions]);
44680
44921
  const handleTest = useCallback(() => {
44681
44922
  if (playState !== "editing") return;
44682
44923
  if (testEvent) emit(`UI:${testEvent}`, {});
@@ -44685,7 +44926,7 @@ function StateArchitectBoard({
44685
44926
  const results = [];
44686
44927
  let testIdx = 0;
44687
44928
  const runNextTest = () => {
44688
- if (testIdx >= (resolved?.testCases.length ?? 0)) {
44929
+ if (testIdx >= testCases.length) {
44689
44930
  const allPassed = results.every((r) => r.passed);
44690
44931
  setPlayState(allPassed ? "success" : "fail");
44691
44932
  setTestResults(results);
@@ -44700,9 +44941,9 @@ function StateArchitectBoard({
44700
44941
  }
44701
44942
  return;
44702
44943
  }
44703
- const testCase = resolved?.testCases[testIdx];
44944
+ const testCase = testCases[testIdx];
44704
44945
  if (!testCase) return;
44705
- let state = resolved.initialState;
44946
+ let state = initialState;
44706
44947
  for (const event of testCase.events) {
44707
44948
  const trans = transitions.find((t2) => t2.from === state && t2.event === event);
44708
44949
  if (trans) {
@@ -44720,53 +44961,57 @@ function StateArchitectBoard({
44720
44961
  timerRef.current = setTimeout(runNextTest, stepDurationMs);
44721
44962
  };
44722
44963
  timerRef.current = setTimeout(runNextTest, stepDurationMs);
44723
- }, [playState, transitions, resolved, stepDurationMs, testEvent, completeEvent, emit]);
44964
+ }, [playState, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
44724
44965
  const handleTryAgain = useCallback(() => {
44725
44966
  if (timerRef.current) clearTimeout(timerRef.current);
44726
44967
  setPlayState("editing");
44727
- setCurrentState(resolved?.initialState ?? "");
44968
+ setCurrentState(initialState);
44728
44969
  setTestResults([]);
44729
- }, [resolved?.initialState]);
44970
+ }, [initialState]);
44730
44971
  const handleReset = useCallback(() => {
44731
44972
  if (timerRef.current) clearTimeout(timerRef.current);
44732
- setTransitions(resolved?.transitions ?? []);
44973
+ setTransitions(entityTransitions);
44733
44974
  setPlayState("editing");
44734
- setCurrentState(resolved?.initialState ?? "");
44975
+ setCurrentState(initialState);
44735
44976
  setTestResults([]);
44736
- setVariables(resolved?.variables ?? []);
44977
+ setVariables([...entityVariables]);
44737
44978
  setSelectedState(null);
44738
44979
  setAddingFrom(null);
44739
44980
  setAttempts(0);
44740
- }, [resolved]);
44981
+ }, [entityTransitions, initialState, entityVariables]);
44741
44982
  const codeData = useMemo(() => ({
44742
- name: resolved?.entityName ?? "",
44743
- states: resolved?.states ?? [],
44744
- initialState: resolved?.initialState ?? "",
44983
+ name: entityName,
44984
+ states: entityStates,
44985
+ initialState,
44745
44986
  transitions: transitions.map((t2) => ({
44746
44987
  from: t2.from,
44747
44988
  to: t2.to,
44748
44989
  event: t2.event,
44749
44990
  ...t2.guardHint ? { guard: t2.guardHint } : {}
44750
44991
  }))
44751
- }), [resolved, transitions]);
44992
+ }), [entityName, entityStates, initialState, transitions]);
44752
44993
  if (!resolved) return null;
44994
+ const theme = resolved.theme ?? void 0;
44995
+ const themeBackground = theme?.background;
44996
+ const headerImage = str(resolved.headerImage);
44997
+ const hint = str(resolved.hint);
44753
44998
  return /* @__PURE__ */ jsxs(
44754
44999
  VStack,
44755
45000
  {
44756
45001
  className: cn("p-4 gap-6", className),
44757
45002
  style: {
44758
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
45003
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
44759
45004
  backgroundSize: "cover",
44760
45005
  backgroundPosition: "center"
44761
45006
  },
44762
45007
  children: [
44763
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
45008
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
44764
45009
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
44765
- /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
44766
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description }),
45010
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
45011
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) }),
44767
45012
  /* @__PURE__ */ jsxs(HStack, { className: "items-center p-2 rounded bg-warning/10 border border-warning/30", gap: "xs", children: [
44768
45013
  /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-warning font-bold", children: t("game.hint") + ":" }),
44769
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: resolved.hint })
45014
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: hint })
44770
45015
  ] })
44771
45016
  ] }),
44772
45017
  /* @__PURE__ */ jsxs(HStack, { className: "flex-wrap items-start", gap: "lg", children: [
@@ -44814,14 +45059,14 @@ function StateArchitectBoard({
44814
45059
  ]
44815
45060
  }
44816
45061
  ),
44817
- resolved.states.map((state) => /* @__PURE__ */ jsx(
45062
+ entityStates.map((state) => /* @__PURE__ */ jsx(
44818
45063
  StateNode2,
44819
45064
  {
44820
45065
  name: state,
44821
45066
  position: positions[state],
44822
45067
  isCurrent: state === currentState,
44823
45068
  isSelected: state === selectedState,
44824
- isInitial: state === resolved.initialState,
45069
+ isInitial: state === initialState,
44825
45070
  onClick: () => handleStateClick(state)
44826
45071
  },
44827
45072
  state
@@ -44868,7 +45113,7 @@ function StateArchitectBoard({
44868
45113
  /* @__PURE__ */ jsx(
44869
45114
  VariablePanel,
44870
45115
  {
44871
- entityName: resolved.entityName,
45116
+ entityName,
44872
45117
  variables
44873
45118
  }
44874
45119
  ),
@@ -44883,12 +45128,12 @@ function StateArchitectBoard({
44883
45128
  resolved.showCodeView !== false && /* @__PURE__ */ jsx(CodeView, { data: codeData, label: "View Code" })
44884
45129
  ] })
44885
45130
  ] }),
44886
- playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: resolved.successMessage || t("stateArchitect.allPassed") }) }),
45131
+ playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("stateArchitect.allPassed") }) }),
44887
45132
  playState === "fail" && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
44888
45133
  /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(ENCOURAGEMENT_KEYS3[Math.min(attempts - 1, ENCOURAGEMENT_KEYS3.length - 1)] ?? ENCOURAGEMENT_KEYS3[0]) }) }),
44889
- attempts >= 3 && resolved.hint && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
45134
+ attempts >= 3 && hint && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
44890
45135
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
44891
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
45136
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
44892
45137
  ] }) })
44893
45138
  ] }),
44894
45139
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
@@ -44918,6 +45163,7 @@ var init_StateArchitectBoard = __esm({
44918
45163
  init_TransitionArrow();
44919
45164
  init_VariablePanel();
44920
45165
  init_CodeView();
45166
+ init_boardEntity();
44921
45167
  ENCOURAGEMENT_KEYS3 = [
44922
45168
  "puzzle.tryAgain1",
44923
45169
  "puzzle.tryAgain2",
@@ -44954,8 +45200,8 @@ var init_StatsOrganism = __esm({
44954
45200
  return /* @__PURE__ */ jsx(ErrorState, { message: error.message, className });
44955
45201
  }
44956
45202
  const stats = items.map((item) => ({
44957
- value: item.value,
44958
- label: item.label
45203
+ value: String(item.value ?? ""),
45204
+ label: String(item.label ?? "")
44959
45205
  }));
44960
45206
  return /* @__PURE__ */ jsx(
44961
45207
  StatsGrid,
@@ -45001,10 +45247,10 @@ var init_StepFlowOrganism = __esm({
45001
45247
  return /* @__PURE__ */ jsx(ErrorState, { message: error.message, className });
45002
45248
  }
45003
45249
  const steps = items.map((item) => ({
45004
- number: item.number,
45005
- title: item.title,
45006
- description: item.description,
45007
- icon: item.icon
45250
+ number: item.number != null ? Number(item.number) : void 0,
45251
+ title: String(item.title ?? ""),
45252
+ description: String(item.description ?? ""),
45253
+ icon: item.icon != null ? String(item.icon) : void 0
45008
45254
  }));
45009
45255
  return /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
45010
45256
  (heading || subtitle) && /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
@@ -45177,13 +45423,13 @@ var init_TeamOrganism = __esm({
45177
45423
  /* @__PURE__ */ jsx(SimpleGrid, { cols: cols > 0 ? cols : 1, gap: "lg", children: items.map((member) => /* @__PURE__ */ jsx(
45178
45424
  TeamCard,
45179
45425
  {
45180
- name: member.name,
45181
- nameAr: member.nameAr,
45182
- role: member.role,
45183
- bio: member.bio,
45184
- avatar: member.avatar
45426
+ name: String(member.name ?? ""),
45427
+ nameAr: member.nameAr != null ? String(member.nameAr) : void 0,
45428
+ role: String(member.role ?? ""),
45429
+ bio: String(member.bio ?? ""),
45430
+ avatar: member.avatar != null ? String(member.avatar) : void 0
45185
45431
  },
45186
- member.id
45432
+ String(member.id ?? "")
45187
45433
  )) })
45188
45434
  ] });
45189
45435
  };
@@ -45420,8 +45666,8 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
45420
45666
  const [turn, setTurn] = useState(1);
45421
45667
  const [gameResult, setGameResult] = useState(null);
45422
45668
  const checkGameEnd = useCallback((currentUnits) => {
45423
- const pa = currentUnits.filter((u) => u.team === "player" && u.health > 0);
45424
- const ea = currentUnits.filter((u) => u.team === "enemy" && u.health > 0);
45669
+ const pa = currentUnits.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
45670
+ const ea = currentUnits.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
45425
45671
  if (pa.length === 0) {
45426
45672
  setGameResult("defeat");
45427
45673
  setPhase("game_over");
@@ -45439,34 +45685,36 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
45439
45685
  }
45440
45686
  }, [onGameEnd, gameEndEvent, eventBus]);
45441
45687
  const handleUnitClick = useCallback((unitId) => {
45442
- const unit = units.find((u) => u.id === unitId);
45688
+ const unit = units.find((u) => str(u.id) === unitId);
45443
45689
  if (!unit) return;
45444
45690
  if (unitClickEvent) {
45445
45691
  eventBus.emit(`UI:${unitClickEvent}`, { unitId });
45446
45692
  }
45447
45693
  if (phase === "observation" || phase === "selection") {
45448
- if (unit.team === "player") {
45694
+ if (unitTeam(unit) === "player") {
45449
45695
  setSelectedUnitId(unitId);
45450
45696
  setPhase("movement");
45451
45697
  }
45452
45698
  } else if (phase === "action") {
45453
- const selectedUnit = units.find((u) => u.id === selectedUnitId);
45699
+ const selectedUnit = units.find((u) => str(u.id) === selectedUnitId);
45454
45700
  if (!selectedUnit) return;
45455
- if (unit.team === "enemy") {
45456
- const dx = Math.abs(unit.position.x - selectedUnit.position.x);
45457
- const dy = Math.abs(unit.position.y - selectedUnit.position.y);
45701
+ if (unitTeam(unit) === "enemy") {
45702
+ const up = unitPosition(unit);
45703
+ const sp = unitPosition(selectedUnit);
45704
+ const dx = Math.abs(up.x - sp.x);
45705
+ const dy = Math.abs(up.y - sp.y);
45458
45706
  if (dx <= 1 && dy <= 1 && dx + dy > 0) {
45459
- const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, selectedUnit.attack - unit.defense);
45460
- const newHealth = Math.max(0, unit.health - damage);
45707
+ const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, num(selectedUnit.attack) - num(unit.defense));
45708
+ const newHealth = Math.max(0, unitHealth(unit) - damage);
45461
45709
  const updatedUnits = units.map(
45462
- (u) => u.id === unit.id ? { ...u, health: newHealth } : u
45710
+ (u) => str(u.id) === str(unit.id) ? { ...u, health: newHealth } : u
45463
45711
  );
45464
45712
  setUnits(updatedUnits);
45465
45713
  onAttack?.(selectedUnit, unit, damage);
45466
45714
  if (attackEvent) {
45467
45715
  eventBus.emit(`UI:${attackEvent}`, {
45468
- attackerId: selectedUnit.id,
45469
- targetId: unit.id,
45716
+ attackerId: str(selectedUnit.id),
45717
+ targetId: str(unit.id),
45470
45718
  damage
45471
45719
  });
45472
45720
  }
@@ -45483,16 +45731,20 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
45483
45731
  eventBus.emit(`UI:${tileClickEvent}`, { x, y });
45484
45732
  }
45485
45733
  if (phase === "movement" && selectedUnitId) {
45486
- const selectedUnit = units.find((u) => u.id === selectedUnitId);
45734
+ const selectedUnit = units.find((u) => str(u.id) === selectedUnitId);
45487
45735
  if (!selectedUnit) return;
45488
- const dx = Math.abs(x - selectedUnit.position.x);
45489
- const dy = Math.abs(y - selectedUnit.position.y);
45736
+ const sp = unitPosition(selectedUnit);
45737
+ const dx = Math.abs(x - sp.x);
45738
+ const dy = Math.abs(y - sp.y);
45490
45739
  const dist = dx + dy;
45491
- if (dist > 0 && dist <= selectedUnit.movement) {
45492
- if (!units.some((u) => u.position.x === x && u.position.y === y && u.health > 0)) {
45740
+ if (dist > 0 && dist <= num(selectedUnit.movement)) {
45741
+ if (!units.some((u) => {
45742
+ const p2 = unitPosition(u);
45743
+ return p2.x === x && p2.y === y && unitHealth(u) > 0;
45744
+ })) {
45493
45745
  setUnits(
45494
45746
  (prev) => prev.map(
45495
- (u) => u.id === selectedUnitId ? { ...u, position: { x, y } } : u
45747
+ (u) => str(u.id) === selectedUnitId ? { ...u, position: { x, y } } : u
45496
45748
  )
45497
45749
  );
45498
45750
  setPhase("action");
@@ -45535,12 +45787,13 @@ var init_useBattleState = __esm({
45535
45787
  "components/game/organisms/hooks/useBattleState.ts"() {
45536
45788
  "use client";
45537
45789
  init_useEventBus();
45790
+ init_boardEntity();
45538
45791
  }
45539
45792
  });
45540
45793
  function UncontrolledBattleBoard({ entity, ...rest }) {
45541
- const resolved = Array.isArray(entity) ? entity[0] : entity;
45794
+ const resolved = boardEntity(entity);
45542
45795
  const battleState = useBattleState(
45543
- resolved?.initialUnits ?? [],
45796
+ rows(resolved?.initialUnits),
45544
45797
  {
45545
45798
  tileClickEvent: rest.tileClickEvent,
45546
45799
  unitClickEvent: rest.unitClickEvent,
@@ -45576,10 +45829,23 @@ function UncontrolledBattleBoard({ entity, ...rest }) {
45576
45829
  var init_UncontrolledBattleBoard = __esm({
45577
45830
  "components/game/organisms/UncontrolledBattleBoard.tsx"() {
45578
45831
  init_BattleBoard();
45832
+ init_boardEntity();
45579
45833
  init_useBattleState();
45580
45834
  UncontrolledBattleBoard.displayName = "UncontrolledBattleBoard";
45581
45835
  }
45582
45836
  });
45837
+ function heroPosition(h) {
45838
+ return vec2(h.position);
45839
+ }
45840
+ function heroOwner(h) {
45841
+ return str(h.owner);
45842
+ }
45843
+ function heroMovement(h) {
45844
+ return num(h.movement);
45845
+ }
45846
+ function hexPassable(h) {
45847
+ return h.passable !== false;
45848
+ }
45583
45849
  function defaultIsInRange(from, to, range) {
45584
45850
  return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
45585
45851
  }
@@ -45610,36 +45876,36 @@ function WorldMapBoard({
45610
45876
  className
45611
45877
  }) {
45612
45878
  const eventBus = useEventBus();
45613
- const resolved = Array.isArray(entity) ? entity[0] : entity;
45614
- const hexes = resolved?.hexes ?? [];
45615
- const heroes = resolved?.heroes ?? [];
45616
- const features = resolved?.features ?? [];
45617
- const selectedHeroId = resolved?.selectedHeroId;
45879
+ const resolved = boardEntity(entity);
45880
+ const hexes = rows(resolved?.hexes);
45881
+ const heroes = rows(resolved?.heroes);
45882
+ const features = Array.isArray(resolved?.features) ? resolved.features : [];
45883
+ const selectedHeroId = resolved?.selectedHeroId ?? null;
45618
45884
  const assetManifest = resolved?.assetManifest;
45619
45885
  const backgroundImage = resolved?.backgroundImage;
45620
45886
  const [hoveredTile, setHoveredTile] = useState(null);
45621
45887
  const selectedHero = useMemo(
45622
- () => heroes.find((h) => h.id === selectedHeroId) ?? null,
45888
+ () => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
45623
45889
  [heroes, selectedHeroId]
45624
45890
  );
45625
45891
  const tiles = useMemo(
45626
45892
  () => hexes.map((hex) => ({
45627
- x: hex.x,
45628
- y: hex.y,
45629
- terrain: hex.terrain,
45630
- terrainSprite: hex.terrainSprite
45893
+ x: num(hex.x),
45894
+ y: num(hex.y),
45895
+ terrain: str(hex.terrain),
45896
+ terrainSprite: hex.terrainSprite == null ? void 0 : str(hex.terrainSprite)
45631
45897
  })),
45632
45898
  [hexes]
45633
45899
  );
45634
45900
  const baseUnits = useMemo(
45635
45901
  () => heroes.map((hero) => ({
45636
- id: hero.id,
45637
- position: hero.position,
45638
- name: hero.name,
45639
- team: hero.owner === "enemy" ? "enemy" : "player",
45902
+ id: str(hero.id),
45903
+ position: heroPosition(hero),
45904
+ name: str(hero.name),
45905
+ team: heroOwner(hero) === "enemy" ? "enemy" : "player",
45640
45906
  health: 100,
45641
45907
  maxHealth: 100,
45642
- sprite: hero.sprite
45908
+ sprite: hero.sprite == null ? void 0 : str(hero.sprite)
45643
45909
  })),
45644
45910
  [heroes]
45645
45911
  );
@@ -45680,73 +45946,94 @@ function WorldMapBoard({
45680
45946
  const isoUnits = useMemo(() => {
45681
45947
  if (movingPositions.size === 0) return baseUnits;
45682
45948
  return baseUnits.map((u) => {
45683
- const pos = movingPositions.get(u.id);
45949
+ const pos = u.id == null ? void 0 : movingPositions.get(u.id);
45684
45950
  return pos ? { ...u, position: pos } : u;
45685
45951
  });
45686
45952
  }, [baseUnits, movingPositions]);
45687
45953
  const validMoves = useMemo(() => {
45688
- if (!selectedHero || selectedHero.movement <= 0) return [];
45954
+ if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
45955
+ const sp = heroPosition(selectedHero);
45956
+ const sOwner = heroOwner(selectedHero);
45957
+ const range = heroMovement(selectedHero);
45689
45958
  const moves = [];
45690
45959
  hexes.forEach((hex) => {
45691
- if (hex.passable === false) return;
45692
- if (hex.x === selectedHero.position.x && hex.y === selectedHero.position.y) return;
45693
- if (!isInRange(selectedHero.position, { x: hex.x, y: hex.y }, selectedHero.movement)) return;
45694
- if (heroes.some((h) => h.position.x === hex.x && h.position.y === hex.y && h.owner === selectedHero.owner)) return;
45695
- moves.push({ x: hex.x, y: hex.y });
45960
+ const hx = num(hex.x);
45961
+ const hy = num(hex.y);
45962
+ if (!hexPassable(hex)) return;
45963
+ if (hx === sp.x && hy === sp.y) return;
45964
+ if (!isInRange(sp, { x: hx, y: hy }, range)) return;
45965
+ if (heroes.some((h) => {
45966
+ const hp = heroPosition(h);
45967
+ return hp.x === hx && hp.y === hy && heroOwner(h) === sOwner;
45968
+ })) return;
45969
+ moves.push({ x: hx, y: hy });
45696
45970
  });
45697
45971
  return moves;
45698
45972
  }, [selectedHero, hexes, heroes, isInRange]);
45699
45973
  const attackTargets = useMemo(() => {
45700
- if (!selectedHero || selectedHero.movement <= 0) return [];
45701
- return heroes.filter((h) => h.owner !== selectedHero.owner).filter((h) => isInRange(selectedHero.position, h.position, selectedHero.movement)).map((h) => h.position);
45974
+ if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
45975
+ const sp = heroPosition(selectedHero);
45976
+ const sOwner = heroOwner(selectedHero);
45977
+ const range = heroMovement(selectedHero);
45978
+ return heroes.filter((h) => heroOwner(h) !== sOwner).filter((h) => isInRange(sp, heroPosition(h), range)).map((h) => heroPosition(h));
45702
45979
  }, [selectedHero, heroes, isInRange]);
45703
- const maxY = Math.max(...hexes.map((h) => h.y), 0);
45980
+ const maxY = Math.max(...hexes.map((h) => num(h.y)), 0);
45704
45981
  const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
45705
45982
  const tileToScreen = useCallback(
45706
45983
  (tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
45707
45984
  [scale, baseOffsetX]
45708
45985
  );
45709
45986
  const hoveredHex = useMemo(
45710
- () => hoveredTile ? hexes.find((h) => h.x === hoveredTile.x && h.y === hoveredTile.y) ?? null : null,
45987
+ () => hoveredTile ? hexes.find((h) => num(h.x) === hoveredTile.x && num(h.y) === hoveredTile.y) ?? null : null,
45711
45988
  [hoveredTile, hexes]
45712
45989
  );
45713
45990
  const hoveredHero = useMemo(
45714
- () => hoveredTile ? heroes.find((h) => h.position.x === hoveredTile.x && h.position.y === hoveredTile.y) ?? null : null,
45991
+ () => hoveredTile ? heroes.find((h) => {
45992
+ const hp = heroPosition(h);
45993
+ return hp.x === hoveredTile.x && hp.y === hoveredTile.y;
45994
+ }) ?? null : null,
45715
45995
  [hoveredTile, heroes]
45716
45996
  );
45717
45997
  const handleTileClick = useCallback((x, y) => {
45718
45998
  if (movementAnimRef.current) return;
45719
- const hex = hexes.find((h) => h.x === x && h.y === y);
45999
+ const hex = hexes.find((h) => num(h.x) === x && num(h.y) === y);
45720
46000
  if (!hex) return;
45721
46001
  if (tileClickEvent) {
45722
46002
  eventBus.emit(`UI:${tileClickEvent}`, { x, y });
45723
46003
  }
45724
46004
  if (selectedHero && validMoves.some((m) => m.x === x && m.y === y)) {
45725
- startMoveAnimation(selectedHero.id, { ...selectedHero.position }, { x, y }, () => {
45726
- onHeroMove?.(selectedHero.id, x, y);
46005
+ const heroId = str(selectedHero.id);
46006
+ startMoveAnimation(heroId, { ...heroPosition(selectedHero) }, { x, y }, () => {
46007
+ onHeroMove?.(heroId, x, y);
45727
46008
  if (heroMoveEvent) {
45728
- eventBus.emit(`UI:${heroMoveEvent}`, { heroId: selectedHero.id, toX: x, toY: y });
46009
+ eventBus.emit(`UI:${heroMoveEvent}`, { heroId, toX: x, toY: y });
45729
46010
  }
45730
- if (hex.feature && hex.feature !== "none") {
45731
- onFeatureEnter?.(selectedHero.id, hex);
46011
+ const feature = str(hex.feature);
46012
+ if (feature && feature !== "none") {
46013
+ onFeatureEnter?.(heroId, hex);
45732
46014
  if (featureEnterEvent) {
45733
- eventBus.emit(`UI:${featureEnterEvent}`, { heroId: selectedHero.id, feature: hex.feature, hex });
46015
+ eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex });
45734
46016
  }
45735
46017
  }
45736
46018
  });
45737
46019
  return;
45738
46020
  }
45739
- const enemy = heroes.find((h) => h.position.x === x && h.position.y === y && h.owner === "enemy");
46021
+ const enemy = heroes.find((h) => {
46022
+ const hp = heroPosition(h);
46023
+ return hp.x === x && hp.y === y && heroOwner(h) === "enemy";
46024
+ });
45740
46025
  if (selectedHero && enemy && attackTargets.some((t) => t.x === x && t.y === y)) {
45741
- onBattleEncounter?.(selectedHero.id, enemy.id);
46026
+ const attackerId = str(selectedHero.id);
46027
+ const defenderId = str(enemy.id);
46028
+ onBattleEncounter?.(attackerId, defenderId);
45742
46029
  if (battleEncounterEvent) {
45743
- eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId: selectedHero.id, defenderId: enemy.id });
46030
+ eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId, defenderId });
45744
46031
  }
45745
46032
  }
45746
46033
  }, [hexes, heroes, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
45747
46034
  const handleUnitClick = useCallback((unitId) => {
45748
- const hero = heroes.find((h) => h.id === unitId);
45749
- if (hero && (hero.owner === "player" || allowMoveAllHeroes)) {
46035
+ const hero = heroes.find((h) => str(h.id) === unitId);
46036
+ if (hero && (heroOwner(hero) === "player" || allowMoveAllHeroes)) {
45750
46037
  onHeroSelect?.(unitId);
45751
46038
  if (heroSelectEvent) {
45752
46039
  eventBus.emit(`UI:${heroSelectEvent}`, { heroId: unitId });
@@ -45819,6 +46106,7 @@ var init_WorldMapBoard = __esm({
45819
46106
  init_Stack();
45820
46107
  init_LoadingState();
45821
46108
  init_IsometricCanvas2();
46109
+ init_boardEntity();
45822
46110
  init_isometric();
45823
46111
  WorldMapBoard.displayName = "WorldMapBoard";
45824
46112
  }
@@ -49047,11 +49335,11 @@ function buildMockData(schema) {
49047
49335
  result[entityName] = entity.instances;
49048
49336
  continue;
49049
49337
  }
49050
- const rows = Array.from(
49338
+ const rows2 = Array.from(
49051
49339
  { length: 10 },
49052
49340
  (_, i) => generateEntityRow(entity, i + 1)
49053
49341
  );
49054
- result[entityName] = rows;
49342
+ result[entityName] = rows2;
49055
49343
  }
49056
49344
  for (const orbital of schema.orbitals) {
49057
49345
  for (const traitRef of orbital.traits ?? []) {