@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
@@ -2718,7 +2718,7 @@ var init_SvgGrid = __esm({
2718
2718
  x,
2719
2719
  y,
2720
2720
  cols = 4,
2721
- rows = 3,
2721
+ rows: rows2 = 3,
2722
2722
  spacing = 20,
2723
2723
  nodeRadius = 3,
2724
2724
  color = "var(--color-primary)",
@@ -2727,7 +2727,7 @@ var init_SvgGrid = __esm({
2727
2727
  highlights = []
2728
2728
  }) => {
2729
2729
  const highlightSet = new Set(highlights);
2730
- return /* @__PURE__ */ jsx("g", { className, opacity, children: Array.from({ length: rows }).map(
2730
+ return /* @__PURE__ */ jsx("g", { className, opacity, children: Array.from({ length: rows2 }).map(
2731
2731
  (_, row) => Array.from({ length: cols }).map((_2, col) => {
2732
2732
  const index = row * cols + col;
2733
2733
  const isHighlighted = highlightSet.has(index);
@@ -3387,7 +3387,7 @@ var init_Input = __esm({
3387
3387
  onClear,
3388
3388
  value,
3389
3389
  options,
3390
- rows = 3,
3390
+ rows: rows2 = 3,
3391
3391
  onChange,
3392
3392
  ...props
3393
3393
  }, ref) => {
@@ -3437,7 +3437,7 @@ var init_Input = __esm({
3437
3437
  ref,
3438
3438
  value,
3439
3439
  onChange,
3440
- rows,
3440
+ rows: rows2,
3441
3441
  className: baseClassName,
3442
3442
  ...props
3443
3443
  }
@@ -5518,66 +5518,6 @@ var init_RangeSlider = __esm({
5518
5518
  RangeSlider.displayName = "RangeSlider";
5519
5519
  }
5520
5520
  });
5521
- function easeOut(t) {
5522
- return t * (2 - t);
5523
- }
5524
- var AnimatedCounter;
5525
- var init_AnimatedCounter = __esm({
5526
- "components/marketing/atoms/AnimatedCounter.tsx"() {
5527
- "use client";
5528
- init_cn();
5529
- init_Typography();
5530
- AnimatedCounter = ({
5531
- value: rawValue,
5532
- duration = 600,
5533
- prefix,
5534
- suffix,
5535
- className
5536
- }) => {
5537
- const numericRaw = typeof rawValue === "number" ? rawValue : Number.parseFloat(String(rawValue ?? ""));
5538
- const value = !Number.isNaN(numericRaw) ? numericRaw : 0;
5539
- const [displayValue, setDisplayValue] = useState(value);
5540
- const previousValueRef = useRef(value);
5541
- const animationFrameRef = useRef(null);
5542
- useEffect(() => {
5543
- const from = previousValueRef.current;
5544
- const to = value;
5545
- previousValueRef.current = value;
5546
- if (from === to) {
5547
- setDisplayValue(to);
5548
- return;
5549
- }
5550
- const startTime = performance.now();
5551
- const diff = to - from;
5552
- function animate(currentTime) {
5553
- const elapsed = currentTime - startTime;
5554
- const progress = Math.min(elapsed / duration, 1);
5555
- const easedProgress = easeOut(progress);
5556
- setDisplayValue(from + diff * easedProgress);
5557
- if (progress < 1) {
5558
- animationFrameRef.current = requestAnimationFrame(animate);
5559
- } else {
5560
- setDisplayValue(to);
5561
- }
5562
- }
5563
- animationFrameRef.current = requestAnimationFrame(animate);
5564
- return () => {
5565
- if (animationFrameRef.current !== null) {
5566
- cancelAnimationFrame(animationFrameRef.current);
5567
- }
5568
- };
5569
- }, [value, duration]);
5570
- const decimalPlaces = Number.isInteger(value) ? 0 : String(value).split(".")[1]?.length ?? 0;
5571
- const formattedValue = displayValue.toFixed(decimalPlaces);
5572
- return /* @__PURE__ */ jsxs(Typography, { variant: "h3", className: cn("tabular-nums", className), children: [
5573
- prefix,
5574
- formattedValue,
5575
- suffix
5576
- ] });
5577
- };
5578
- AnimatedCounter.displayName = "AnimatedCounter";
5579
- }
5580
- });
5581
5521
  function useInfiniteScroll(onLoadMore, options = {}) {
5582
5522
  const { rootMargin = "200px", hasMore = true, isLoading = false } = options;
5583
5523
  const observerRef = useRef(null);
@@ -8059,15 +7999,15 @@ function HeaderSkeleton({ className }) {
8059
7999
  ] })
8060
8000
  ] });
8061
8001
  }
8062
- function TableSkeleton({ rows = 5, columns = 4, className }) {
8002
+ function TableSkeleton({ rows: rows2 = 5, columns = 4, className }) {
8063
8003
  return /* @__PURE__ */ jsxs(VStack, { gap: "none", className: cn("border border-border rounded-lg overflow-hidden", className), children: [
8064
8004
  /* @__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)) }),
8065
- Array.from({ length: rows }).map((_, rowIdx) => /* @__PURE__ */ jsx(
8005
+ Array.from({ length: rows2 }).map((_, rowIdx) => /* @__PURE__ */ jsx(
8066
8006
  HStack,
8067
8007
  {
8068
8008
  className: cn(
8069
8009
  "px-4 py-3",
8070
- rowIdx < rows - 1 && "border-b border-border"
8010
+ rowIdx < rows2 - 1 && "border-b border-border"
8071
8011
  ),
8072
8012
  children: Array.from({ length: columns }).map((_2, colIdx) => /* @__PURE__ */ jsx(SkeletonLine, { className: "flex-1 mx-2" }, colIdx))
8073
8013
  },
@@ -8115,18 +8055,18 @@ function CardSkeleton({ className }) {
8115
8055
  }
8116
8056
  );
8117
8057
  }
8118
- function TextSkeleton({ rows = 3, className }) {
8119
- return /* @__PURE__ */ jsx(VStack, { gap: "sm", className, children: Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsx(
8058
+ function TextSkeleton({ rows: rows2 = 3, className }) {
8059
+ return /* @__PURE__ */ jsx(VStack, { gap: "sm", className, children: Array.from({ length: rows2 }).map((_, i) => /* @__PURE__ */ jsx(
8120
8060
  SkeletonLine,
8121
8061
  {
8122
- className: i === rows - 1 ? "w-2/3" : "w-full"
8062
+ className: i === rows2 - 1 ? "w-2/3" : "w-full"
8123
8063
  },
8124
8064
  i
8125
8065
  )) });
8126
8066
  }
8127
8067
  function Skeleton({
8128
8068
  variant = "text",
8129
- rows,
8069
+ rows: rows2,
8130
8070
  columns,
8131
8071
  fields,
8132
8072
  className
@@ -8136,15 +8076,15 @@ function Skeleton({
8136
8076
  case "header":
8137
8077
  return /* @__PURE__ */ jsx(HeaderSkeleton, { className });
8138
8078
  case "table":
8139
- return /* @__PURE__ */ jsx(TableSkeleton, { rows, columns, className });
8079
+ return /* @__PURE__ */ jsx(TableSkeleton, { rows: rows2, columns, className });
8140
8080
  case "form":
8141
8081
  return /* @__PURE__ */ jsx(FormSkeleton, { fields, className });
8142
8082
  case "card":
8143
8083
  return /* @__PURE__ */ jsx(CardSkeleton, { className });
8144
8084
  case "text":
8145
- return /* @__PURE__ */ jsx(TextSkeleton, { rows, className });
8085
+ return /* @__PURE__ */ jsx(TextSkeleton, { rows: rows2, className });
8146
8086
  default:
8147
- return /* @__PURE__ */ jsx(TextSkeleton, { rows, className });
8087
+ return /* @__PURE__ */ jsx(TextSkeleton, { rows: rows2, className });
8148
8088
  }
8149
8089
  }
8150
8090
  var pulseClass;
@@ -10128,7 +10068,7 @@ var init_MapView = __esm({
10128
10068
  shadowSize: [41, 41]
10129
10069
  });
10130
10070
  L.Marker.prototype.options.icon = defaultIcon;
10131
- const { useEffect: useEffect70, useRef: useRef66, useCallback: useCallback113, useState: useState100 } = React85__default;
10071
+ const { useEffect: useEffect70, useRef: useRef66, useCallback: useCallback114, useState: useState100 } = React85__default;
10132
10072
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
10133
10073
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
10134
10074
  function MapUpdater({ centerLat, centerLng, zoom }) {
@@ -10174,7 +10114,7 @@ var init_MapView = __esm({
10174
10114
  }) {
10175
10115
  const eventBus = useEventBus2();
10176
10116
  const [clickedPosition, setClickedPosition] = useState100(null);
10177
- const handleMapClick = useCallback113((lat, lng) => {
10117
+ const handleMapClick = useCallback114((lat, lng) => {
10178
10118
  if (showClickedPin) {
10179
10119
  setClickedPosition({ lat, lng });
10180
10120
  }
@@ -10183,7 +10123,7 @@ var init_MapView = __esm({
10183
10123
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
10184
10124
  }
10185
10125
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
10186
- const handleMarkerClick = useCallback113((marker) => {
10126
+ const handleMarkerClick = useCallback114((marker) => {
10187
10127
  onMarkerClick?.(marker);
10188
10128
  if (markerClickEvent) {
10189
10129
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -10404,7 +10344,7 @@ function InputPattern({
10404
10344
  function TextareaPattern({
10405
10345
  value = "",
10406
10346
  placeholder,
10407
- rows = 4,
10347
+ rows: rows2 = 4,
10408
10348
  disabled = false,
10409
10349
  fieldError,
10410
10350
  onChange,
@@ -10424,7 +10364,7 @@ function TextareaPattern({
10424
10364
  {
10425
10365
  value: localValue,
10426
10366
  placeholder,
10427
- rows,
10367
+ rows: rows2,
10428
10368
  disabled,
10429
10369
  error: fieldError,
10430
10370
  onChange: handleChange,
@@ -10909,6 +10849,91 @@ var init_ActionPalette = __esm({
10909
10849
  ActionPalette.displayName = "ActionPalette";
10910
10850
  }
10911
10851
  });
10852
+ function parseValue(value) {
10853
+ if (value === "" || value == null) return { num: 0, prefix: "", suffix: "", decimals: 0 };
10854
+ const match = String(value).match(/^([^0-9]*)([0-9]+(?:\.[0-9]+)?)(.*)$/);
10855
+ if (!match) {
10856
+ return { num: 0, prefix: "", suffix: String(value), decimals: 0 };
10857
+ }
10858
+ const numStr = match[2];
10859
+ const decimalIdx = numStr.indexOf(".");
10860
+ const decimals = decimalIdx >= 0 ? numStr.length - decimalIdx - 1 : 0;
10861
+ return {
10862
+ prefix: match[1],
10863
+ num: parseFloat(numStr),
10864
+ suffix: match[3],
10865
+ decimals
10866
+ };
10867
+ }
10868
+ var AnimatedCounter;
10869
+ var init_AnimatedCounter = __esm({
10870
+ "components/core/molecules/AnimatedCounter.tsx"() {
10871
+ "use client";
10872
+ init_cn();
10873
+ init_Box();
10874
+ init_Typography();
10875
+ AnimatedCounter = ({
10876
+ value,
10877
+ label,
10878
+ duration = 1500,
10879
+ className
10880
+ }) => {
10881
+ const ref = useRef(null);
10882
+ const [displayValue, setDisplayValue] = useState("0");
10883
+ const [hasAnimated, setHasAnimated] = useState(false);
10884
+ const animate = useCallback(() => {
10885
+ const { num: num2, prefix, suffix, decimals } = parseValue(value);
10886
+ if (num2 === 0) {
10887
+ setDisplayValue(String(value));
10888
+ return;
10889
+ }
10890
+ const startTime = performance.now();
10891
+ const tick = (now) => {
10892
+ const elapsed = now - startTime;
10893
+ const progress = Math.min(elapsed / duration, 1);
10894
+ const eased = 1 - Math.pow(1 - progress, 3);
10895
+ const current = eased * num2;
10896
+ setDisplayValue(`${prefix}${current.toFixed(decimals)}${suffix}`);
10897
+ if (progress < 1) {
10898
+ requestAnimationFrame(tick);
10899
+ } else {
10900
+ setDisplayValue(String(value));
10901
+ }
10902
+ };
10903
+ requestAnimationFrame(tick);
10904
+ }, [value, duration]);
10905
+ useEffect(() => {
10906
+ if (hasAnimated) return;
10907
+ const el = ref.current;
10908
+ if (!el) return;
10909
+ const observer2 = new IntersectionObserver(
10910
+ (entries) => {
10911
+ if (entries[0].isIntersecting) {
10912
+ setHasAnimated(true);
10913
+ animate();
10914
+ observer2.disconnect();
10915
+ }
10916
+ },
10917
+ { threshold: 0.3 }
10918
+ );
10919
+ observer2.observe(el);
10920
+ return () => observer2.disconnect();
10921
+ }, [hasAnimated, animate]);
10922
+ return /* @__PURE__ */ jsxs(Box, { ref, className: cn("flex flex-col items-center gap-1 p-4", className), children: [
10923
+ /* @__PURE__ */ jsx(
10924
+ Typography,
10925
+ {
10926
+ variant: "h2",
10927
+ className: "text-primary font-bold tabular-nums",
10928
+ children: hasAnimated ? displayValue : "0"
10929
+ }
10930
+ ),
10931
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "muted", className: "text-center", children: label })
10932
+ ] });
10933
+ };
10934
+ AnimatedCounter.displayName = "AnimatedCounter";
10935
+ }
10936
+ });
10912
10937
  var AuthLayout;
10913
10938
  var init_AuthLayout = __esm({
10914
10939
  "components/core/templates/AuthLayout.tsx"() {
@@ -12250,6 +12275,39 @@ var init_IsometricCanvas2 = __esm({
12250
12275
  init_IsometricCanvas();
12251
12276
  }
12252
12277
  });
12278
+
12279
+ // components/game/organisms/boardEntity.ts
12280
+ function boardEntity(entity) {
12281
+ if (!entity) return void 0;
12282
+ return Array.isArray(entity) ? entity[0] : entity;
12283
+ }
12284
+ function str(v) {
12285
+ return v == null ? "" : String(v);
12286
+ }
12287
+ function num(v, fallback = 0) {
12288
+ const n = Number(v);
12289
+ return Number.isFinite(n) ? n : fallback;
12290
+ }
12291
+ function rows(v) {
12292
+ return Array.isArray(v) ? v : [];
12293
+ }
12294
+ function vec2(v) {
12295
+ const o = v ?? {};
12296
+ return { x: num(o.x), y: num(o.y) };
12297
+ }
12298
+ function unitPosition(u) {
12299
+ return vec2(u.position);
12300
+ }
12301
+ function unitTeam(u) {
12302
+ return str(u.team);
12303
+ }
12304
+ function unitHealth(u) {
12305
+ return num(u.health);
12306
+ }
12307
+ var init_boardEntity = __esm({
12308
+ "components/game/organisms/boardEntity.ts"() {
12309
+ }
12310
+ });
12253
12311
  function BattleBoard({
12254
12312
  entity,
12255
12313
  scale = 0.45,
@@ -12276,43 +12334,49 @@ function BattleBoard({
12276
12334
  attackEvent,
12277
12335
  className
12278
12336
  }) {
12279
- const tiles = entity.tiles;
12280
- const features = entity.features ?? [];
12281
- const boardWidth = entity.boardWidth ?? 8;
12282
- const boardHeight = entity.boardHeight ?? 6;
12283
- const assetManifest = entity.assetManifest;
12284
- const backgroundImage = entity.backgroundImage;
12285
- const units = entity.units;
12286
- const selectedUnitId = entity.selectedUnitId;
12287
- const currentPhase = entity.phase;
12288
- const currentTurn = entity.turn;
12289
- const gameResult = entity.gameResult;
12337
+ const board = boardEntity(entity) ?? {};
12338
+ const tiles = Array.isArray(board.tiles) ? board.tiles : [];
12339
+ const features = Array.isArray(board.features) ? board.features : [];
12340
+ const boardWidth = num(board.boardWidth, 8);
12341
+ const boardHeight = num(board.boardHeight, 6);
12342
+ const assetManifest = board.assetManifest;
12343
+ const backgroundImage = board.backgroundImage;
12344
+ const units = rows(board.units);
12345
+ const selectedUnitId = board.selectedUnitId ?? null;
12346
+ const currentPhase = str(board.phase) || "observation";
12347
+ const currentTurn = num(board.turn, 1);
12348
+ const gameResult = board.gameResult ?? null;
12290
12349
  const eventBus = useEventBus();
12291
12350
  const { t } = useTranslate();
12292
12351
  const [hoveredTile, setHoveredTile] = useState(null);
12293
12352
  const [isShaking, setIsShaking] = useState(false);
12294
12353
  const selectedUnit = useMemo(
12295
- () => units.find((u) => u.id === selectedUnitId) ?? null,
12354
+ () => units.find((u) => str(u.id) === selectedUnitId) ?? null,
12296
12355
  [units, selectedUnitId]
12297
12356
  );
12298
12357
  const hoveredUnit = useMemo(() => {
12299
12358
  if (!hoveredTile) return null;
12300
- return units.find(
12301
- (u) => u.position.x === hoveredTile.x && u.position.y === hoveredTile.y && u.health > 0
12302
- ) ?? null;
12359
+ return units.find((u) => {
12360
+ const p2 = unitPosition(u);
12361
+ return p2.x === hoveredTile.x && p2.y === hoveredTile.y && unitHealth(u) > 0;
12362
+ }) ?? null;
12303
12363
  }, [hoveredTile, units]);
12304
- const playerUnits = useMemo(() => units.filter((u) => u.team === "player" && u.health > 0), [units]);
12305
- const enemyUnits = useMemo(() => units.filter((u) => u.team === "enemy" && u.health > 0), [units]);
12364
+ const playerUnits = useMemo(() => units.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0), [units]);
12365
+ const enemyUnits = useMemo(() => units.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0), [units]);
12306
12366
  const validMoves = useMemo(() => {
12307
12367
  if (!selectedUnit || currentPhase !== "movement") return [];
12308
12368
  const moves = [];
12309
- const range = selectedUnit.movement;
12369
+ const range = num(selectedUnit.movement);
12370
+ const origin = unitPosition(selectedUnit);
12310
12371
  for (let dy = -range; dy <= range; dy++) {
12311
12372
  for (let dx = -range; dx <= range; dx++) {
12312
- const nx = selectedUnit.position.x + dx;
12313
- const ny = selectedUnit.position.y + dy;
12373
+ const nx = origin.x + dx;
12374
+ const ny = origin.y + dy;
12314
12375
  const dist = Math.abs(dx) + Math.abs(dy);
12315
- 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)) {
12376
+ if (dist > 0 && dist <= range && nx >= 0 && nx < boardWidth && ny >= 0 && ny < boardHeight && !units.some((u) => {
12377
+ const p2 = unitPosition(u);
12378
+ return p2.x === nx && p2.y === ny && unitHealth(u) > 0;
12379
+ })) {
12316
12380
  moves.push({ x: nx, y: ny });
12317
12381
  }
12318
12382
  }
@@ -12321,11 +12385,14 @@ function BattleBoard({
12321
12385
  }, [selectedUnit, currentPhase, units, boardWidth, boardHeight]);
12322
12386
  const attackTargets = useMemo(() => {
12323
12387
  if (!selectedUnit || currentPhase !== "action") return [];
12324
- return units.filter((u) => u.team !== selectedUnit.team && u.health > 0).filter((u) => {
12325
- const dx = Math.abs(u.position.x - selectedUnit.position.x);
12326
- const dy = Math.abs(u.position.y - selectedUnit.position.y);
12388
+ const sp = unitPosition(selectedUnit);
12389
+ const sTeam = unitTeam(selectedUnit);
12390
+ return units.filter((u) => unitTeam(u) !== sTeam && unitHealth(u) > 0).filter((u) => {
12391
+ const p2 = unitPosition(u);
12392
+ const dx = Math.abs(p2.x - sp.x);
12393
+ const dy = Math.abs(p2.y - sp.y);
12327
12394
  return dx <= 1 && dy <= 1 && dx + dy > 0;
12328
- }).map((u) => u.position);
12395
+ }).map((u) => unitPosition(u));
12329
12396
  }, [selectedUnit, currentPhase, units]);
12330
12397
  const MOVE_SPEED_MS_PER_TILE = 300;
12331
12398
  const movementAnimRef = useRef(null);
@@ -12365,23 +12432,25 @@ function BattleBoard({
12365
12432
  return () => clearInterval(interval);
12366
12433
  }, []);
12367
12434
  const isoUnits = useMemo(() => {
12368
- return units.filter((u) => u.health > 0).map((unit) => {
12369
- const pos = movingPositions.get(unit.id) ?? unit.position;
12435
+ return units.filter((u) => unitHealth(u) > 0).map((unit) => {
12436
+ const id = str(unit.id);
12437
+ const pos = movingPositions.get(id) ?? unitPosition(unit);
12438
+ const unitTraits = Array.isArray(unit.traits) ? unit.traits : void 0;
12370
12439
  return {
12371
- id: unit.id,
12440
+ id,
12372
12441
  position: pos,
12373
- name: unit.name,
12374
- team: unit.team,
12375
- health: unit.health,
12376
- maxHealth: unit.maxHealth,
12377
- unitType: unit.unitType,
12378
- heroId: unit.heroId,
12379
- sprite: unit.sprite,
12380
- traits: unit.traits?.map((t2) => ({
12381
- name: t2.name,
12382
- currentState: t2.currentState,
12383
- states: t2.states,
12384
- cooldown: t2.cooldown ?? 0
12442
+ name: str(unit.name),
12443
+ team: unitTeam(unit),
12444
+ health: unitHealth(unit),
12445
+ maxHealth: num(unit.maxHealth),
12446
+ unitType: unit.unitType == null ? void 0 : str(unit.unitType),
12447
+ heroId: unit.heroId == null ? void 0 : str(unit.heroId),
12448
+ sprite: unit.sprite == null ? void 0 : str(unit.sprite),
12449
+ traits: unitTraits?.map((tr) => ({
12450
+ name: tr.name,
12451
+ currentState: tr.currentState,
12452
+ states: tr.states,
12453
+ cooldown: tr.cooldown ?? 0
12385
12454
  }))
12386
12455
  };
12387
12456
  });
@@ -12393,8 +12462,8 @@ function BattleBoard({
12393
12462
  [scale, baseOffsetX]
12394
12463
  );
12395
12464
  const checkGameEnd = useCallback(() => {
12396
- const pa = units.filter((u) => u.team === "player" && u.health > 0);
12397
- const ea = units.filter((u) => u.team === "enemy" && u.health > 0);
12465
+ const pa = units.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
12466
+ const ea = units.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
12398
12467
  if (pa.length === 0) {
12399
12468
  onGameEnd?.("defeat");
12400
12469
  if (gameEndEvent) {
@@ -12408,21 +12477,22 @@ function BattleBoard({
12408
12477
  }
12409
12478
  }, [units, onGameEnd, gameEndEvent, eventBus]);
12410
12479
  const handleUnitClick = useCallback((unitId) => {
12411
- const unit = units.find((u) => u.id === unitId);
12480
+ const unit = units.find((u) => str(u.id) === unitId);
12412
12481
  if (!unit) return;
12413
12482
  if (unitClickEvent) {
12414
12483
  eventBus.emit(`UI:${unitClickEvent}`, { unitId });
12415
12484
  }
12416
12485
  if (currentPhase === "action" && selectedUnit) {
12417
- if (unit.team === "enemy" && attackTargets.some((t2) => t2.x === unit.position.x && t2.y === unit.position.y)) {
12418
- const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, selectedUnit.attack - unit.defense);
12486
+ const up = unitPosition(unit);
12487
+ if (unitTeam(unit) === "enemy" && attackTargets.some((t2) => t2.x === up.x && t2.y === up.y)) {
12488
+ const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, num(selectedUnit.attack) - num(unit.defense));
12419
12489
  setIsShaking(true);
12420
12490
  setTimeout(() => setIsShaking(false), 300);
12421
12491
  onAttack?.(selectedUnit, unit, damage);
12422
12492
  if (attackEvent) {
12423
12493
  eventBus.emit(`UI:${attackEvent}`, {
12424
- attackerId: selectedUnit.id,
12425
- targetId: unit.id,
12494
+ attackerId: str(selectedUnit.id),
12495
+ targetId: str(unit.id),
12426
12496
  damage
12427
12497
  });
12428
12498
  }
@@ -12437,9 +12507,9 @@ function BattleBoard({
12437
12507
  if (currentPhase === "movement" && selectedUnit) {
12438
12508
  if (movementAnimRef.current) return;
12439
12509
  if (validMoves.some((m) => m.x === x && m.y === y)) {
12440
- const from = { ...selectedUnit.position };
12510
+ const from = { ...unitPosition(selectedUnit) };
12441
12511
  const to = { x, y };
12442
- startMoveAnimation(selectedUnit.id, from, to, () => {
12512
+ startMoveAnimation(str(selectedUnit.id), from, to, () => {
12443
12513
  onUnitMove?.(selectedUnit, to);
12444
12514
  });
12445
12515
  }
@@ -12597,6 +12667,7 @@ var init_BattleBoard = __esm({
12597
12667
  init_Typography();
12598
12668
  init_Stack();
12599
12669
  init_IsometricCanvas2();
12670
+ init_boardEntity();
12600
12671
  init_isometric();
12601
12672
  BattleBoard.displayName = "BattleBoard";
12602
12673
  }
@@ -13819,24 +13890,24 @@ var init_CodeBlock = __esm({
13819
13890
  return;
13820
13891
  }
13821
13892
  lineEls.forEach((el) => {
13822
- const num = parseInt(el.getAttribute("data-line") ?? "-1", 10);
13823
- if (hiddenLines.has(num)) {
13893
+ const num2 = parseInt(el.getAttribute("data-line") ?? "-1", 10);
13894
+ if (hiddenLines.has(num2)) {
13824
13895
  el.style.display = "none";
13825
13896
  return;
13826
13897
  }
13827
13898
  el.style.display = "";
13828
13899
  el.style.position = "relative";
13829
13900
  el.style.paddingLeft = "1.2em";
13830
- const region = foldStartMap.get(num);
13901
+ const region = foldStartMap.get(num2);
13831
13902
  if (!region) return;
13832
- const isCollapsed = collapsed.has(num);
13903
+ const isCollapsed = collapsed.has(num2);
13833
13904
  const toggle = document.createElement("span");
13834
13905
  toggle.className = "fold-toggle";
13835
13906
  toggle.textContent = isCollapsed ? "\u25B6" : "\u25BC";
13836
13907
  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%";
13837
13908
  toggle.addEventListener("click", (e) => {
13838
13909
  e.stopPropagation();
13839
- toggleFoldRef.current(num);
13910
+ toggleFoldRef.current(num2);
13840
13911
  });
13841
13912
  el.insertBefore(toggle, el.firstChild);
13842
13913
  if (isCollapsed) {
@@ -16089,10 +16160,13 @@ var init_BookChapterView = __esm({
16089
16160
  init_cn();
16090
16161
  BookChapterView = ({
16091
16162
  chapter,
16163
+ orbitalSchema,
16092
16164
  direction,
16093
16165
  className
16094
16166
  }) => {
16095
16167
  const { t: _t } = useTranslate();
16168
+ const title = String(chapter.title ?? "");
16169
+ const content = String(chapter.content ?? "");
16096
16170
  return /* @__PURE__ */ jsxs(
16097
16171
  VStack,
16098
16172
  {
@@ -16100,16 +16174,16 @@ var init_BookChapterView = __esm({
16100
16174
  className: cn("px-6 py-8 max-w-4xl mx-auto w-full", className),
16101
16175
  style: { direction },
16102
16176
  children: [
16103
- /* @__PURE__ */ jsx(Typography, { variant: "h1", className: "text-3xl font-bold", children: chapter.title }),
16177
+ /* @__PURE__ */ jsx(Typography, { variant: "h1", className: "text-3xl font-bold", children: title }),
16104
16178
  /* @__PURE__ */ jsx(Divider, {}),
16105
- !!chapter.orbitalSchema && /* @__PURE__ */ jsx(ScaledDiagram, { children: /* @__PURE__ */ jsx(
16179
+ !!orbitalSchema && /* @__PURE__ */ jsx(ScaledDiagram, { children: /* @__PURE__ */ jsx(
16106
16180
  JazariStateMachine,
16107
16181
  {
16108
- schema: chapter.orbitalSchema,
16182
+ schema: orbitalSchema,
16109
16183
  direction
16110
16184
  }
16111
16185
  ) }),
16112
- /* @__PURE__ */ jsx(ContentRenderer, { content: chapter.content, direction })
16186
+ /* @__PURE__ */ jsx(ContentRenderer, { content, direction })
16113
16187
  ]
16114
16188
  }
16115
16189
  );
@@ -16207,7 +16281,7 @@ var init_BookNavBar = __esm({
16207
16281
  BookNavBar = ({
16208
16282
  currentPage,
16209
16283
  totalPages,
16210
- chapterTitle,
16284
+ chapterTitle: chapterTitle2,
16211
16285
  direction,
16212
16286
  className
16213
16287
  }) => {
@@ -16248,12 +16322,12 @@ var init_BookNavBar = __esm({
16248
16322
  )
16249
16323
  ] }),
16250
16324
  /* @__PURE__ */ jsxs(Box, { className: "flex-1 mx-4 max-w-md", children: [
16251
- chapterTitle && /* @__PURE__ */ jsx(
16325
+ chapterTitle2 && /* @__PURE__ */ jsx(
16252
16326
  Typography,
16253
16327
  {
16254
16328
  variant: "caption",
16255
16329
  className: "text-center block truncate text-muted-foreground",
16256
- children: chapterTitle
16330
+ children: chapterTitle2
16257
16331
  }
16258
16332
  ),
16259
16333
  /* @__PURE__ */ jsx(ProgressBar, { value: progress, size: "sm", variant: "primary" })
@@ -16320,31 +16394,35 @@ var init_BookTableOfContents = __esm({
16320
16394
  style: { direction },
16321
16395
  children: [
16322
16396
  /* @__PURE__ */ jsx(Typography, { variant: "h1", className: "text-3xl font-bold text-center mb-4", children: t("book.tableOfContents") }),
16323
- parts.map((part, partIdx) => /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
16324
- /* @__PURE__ */ jsxs(HStack, { gap: "sm", align: "center", children: [
16325
- /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: t("book.partNumber", { number: String(partIdx + 1) }) }),
16326
- /* @__PURE__ */ jsx(Typography, { variant: "h3", className: "font-semibold", children: part.title })
16327
- ] }),
16328
- /* @__PURE__ */ jsx(VStack, { gap: "xs", className: direction === "rtl" ? "pr-6" : "pl-6", children: part.chapters.map((chapter) => {
16329
- const isCurrent = chapter.id === currentChapterId;
16330
- return /* @__PURE__ */ jsx(
16331
- Button,
16332
- {
16333
- variant: "ghost",
16334
- size: "sm",
16335
- action: "BOOK_NAVIGATE",
16336
- actionPayload: { chapterId: chapter.id },
16337
- className: cn(
16338
- "justify-start text-left w-full",
16339
- direction === "rtl" && "text-right",
16340
- isCurrent && "bg-blue-50 dark:bg-blue-950 text-blue-600 dark:text-blue-400"
16341
- ),
16342
- children: /* @__PURE__ */ jsx(Box, { className: "truncate", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: chapter.title }) })
16343
- },
16344
- chapter.id
16345
- );
16346
- }) })
16347
- ] }, partIdx))
16397
+ parts.map((part, partIdx) => {
16398
+ const chapters = Array.isArray(part.chapters) ? part.chapters : [];
16399
+ return /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
16400
+ /* @__PURE__ */ jsxs(HStack, { gap: "sm", align: "center", children: [
16401
+ /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: t("book.partNumber", { number: String(partIdx + 1) }) }),
16402
+ /* @__PURE__ */ jsx(Typography, { variant: "h3", className: "font-semibold", children: String(part.title ?? "") })
16403
+ ] }),
16404
+ /* @__PURE__ */ jsx(VStack, { gap: "xs", className: direction === "rtl" ? "pr-6" : "pl-6", children: chapters.map((chapter) => {
16405
+ const id = chapter.id == null ? "" : String(chapter.id);
16406
+ const isCurrent = id === currentChapterId;
16407
+ return /* @__PURE__ */ jsx(
16408
+ Button,
16409
+ {
16410
+ variant: "ghost",
16411
+ size: "sm",
16412
+ action: "BOOK_NAVIGATE",
16413
+ actionPayload: { chapterId: id },
16414
+ className: cn(
16415
+ "justify-start text-left w-full",
16416
+ direction === "rtl" && "text-right",
16417
+ isCurrent && "bg-blue-50 dark:bg-blue-950 text-blue-600 dark:text-blue-400"
16418
+ ),
16419
+ children: /* @__PURE__ */ jsx(Box, { className: "truncate", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: String(chapter.title ?? "") }) })
16420
+ },
16421
+ id
16422
+ );
16423
+ }) })
16424
+ ] }, partIdx);
16425
+ })
16348
16426
  ]
16349
16427
  }
16350
16428
  );
@@ -16466,27 +16544,41 @@ function resolveFieldMap(fieldMap) {
16466
16544
  function get(obj, key) {
16467
16545
  return obj[key];
16468
16546
  }
16547
+ function asStr(v) {
16548
+ return v == null ? "" : String(v);
16549
+ }
16469
16550
  function mapBookData(raw, fields = IDENTITY_BOOK_FIELDS) {
16470
16551
  const rawParts = get(raw, fields.parts) ?? [];
16471
- return {
16472
- title: get(raw, fields.title) ?? "",
16473
- subtitle: get(raw, fields.subtitle),
16474
- author: get(raw, fields.author),
16475
- coverImageUrl: get(raw, fields.coverImageUrl),
16476
- direction: get(raw, fields.direction) ?? void 0,
16477
- parts: rawParts.map((part) => {
16478
- const rawChapters = get(part, fields.chapters) ?? [];
16479
- return {
16480
- title: get(part, fields.partTitle) ?? "",
16481
- chapters: rawChapters.map((ch) => ({
16482
- id: get(ch, fields.chapterId) ?? "",
16483
- title: get(ch, fields.chapterTitle) ?? "",
16484
- content: get(ch, fields.chapterContent) ?? "",
16485
- orbitalSchema: get(ch, fields.chapterOrbitalSchema)
16486
- }))
16487
- };
16488
- })
16552
+ const direction = get(raw, fields.direction) ?? "ltr";
16553
+ const cover = {
16554
+ title: asStr(get(raw, fields.title)),
16555
+ subtitle: asStr(get(raw, fields.subtitle)),
16556
+ author: asStr(get(raw, fields.author)),
16557
+ coverImageUrl: asStr(get(raw, fields.coverImageUrl)),
16558
+ direction
16489
16559
  };
16560
+ const schemaByChapterId = {};
16561
+ const chapters = [];
16562
+ const parts = rawParts.map((part) => {
16563
+ const rawChapters = get(part, fields.chapters) ?? [];
16564
+ const chapterRows = rawChapters.map((ch) => {
16565
+ const id = asStr(get(ch, fields.chapterId));
16566
+ const schema = get(ch, fields.chapterOrbitalSchema);
16567
+ if (schema) schemaByChapterId[id] = schema;
16568
+ const row = {
16569
+ id,
16570
+ title: asStr(get(ch, fields.chapterTitle)),
16571
+ content: asStr(get(ch, fields.chapterContent))
16572
+ };
16573
+ chapters.push(row);
16574
+ return row;
16575
+ });
16576
+ return {
16577
+ title: asStr(get(part, fields.partTitle)),
16578
+ chapters: chapterRows
16579
+ };
16580
+ });
16581
+ return { cover, direction, parts, chapters, schemaByChapterId };
16490
16582
  }
16491
16583
  var IDENTITY_BOOK_FIELDS, AR_BOOK_FIELDS, FIELD_MAP_REGISTRY;
16492
16584
  var init_types2 = __esm({
@@ -16524,10 +16616,7 @@ var init_types2 = __esm({
16524
16616
  };
16525
16617
  }
16526
16618
  });
16527
- function flattenChapters(book) {
16528
- return book.parts.flatMap((part) => part.chapters);
16529
- }
16530
- var PRINT_STYLES, BookViewer;
16619
+ var chapterId, chapterTitle, PRINT_STYLES, BookViewer;
16531
16620
  var init_BookViewer = __esm({
16532
16621
  "components/marketing/organisms/book/BookViewer.tsx"() {
16533
16622
  init_Box();
@@ -16540,6 +16629,8 @@ var init_BookViewer = __esm({
16540
16629
  init_BookNavBar();
16541
16630
  init_EmptyState();
16542
16631
  init_types2();
16632
+ chapterId = (ch) => ch?.id == null ? void 0 : String(ch.id);
16633
+ chapterTitle = (ch) => ch?.title == null ? void 0 : String(ch.title);
16543
16634
  PRINT_STYLES = `
16544
16635
  @media print {
16545
16636
  .book-viewer-page {
@@ -16568,14 +16659,14 @@ var init_BookViewer = __esm({
16568
16659
  return mapBookData(raw, resolvedFieldMap);
16569
16660
  }, [entity, resolvedFieldMap]);
16570
16661
  const direction = book?.direction ?? "ltr";
16571
- const chapters = useMemo(() => book ? flattenChapters(book) : [], [book]);
16662
+ const chapters = useMemo(() => book ? book.chapters : [], [book]);
16572
16663
  const totalPages = 2 + chapters.length;
16573
16664
  const navigateTo = useCallback(
16574
16665
  (page) => {
16575
16666
  const clamped = Math.max(0, Math.min(page, totalPages - 1));
16576
16667
  setCurrentPage(clamped);
16577
- const chapterId = clamped >= 2 ? chapters[clamped - 2]?.id : void 0;
16578
- eventBus.emit("UI:BOOK_PAGE_CHANGE", { pageIndex: clamped, chapterId });
16668
+ const id = clamped >= 2 ? chapterId(chapters[clamped - 2]) : void 0;
16669
+ eventBus.emit("UI:BOOK_PAGE_CHANGE", { pageIndex: clamped, chapterId: id });
16579
16670
  },
16580
16671
  [totalPages, chapters, eventBus]
16581
16672
  );
@@ -16587,8 +16678,8 @@ var init_BookViewer = __esm({
16587
16678
  eventBus.on("UI:BOOK_PAGE_NEXT", () => navigateTo(currentPage + 1)),
16588
16679
  eventBus.on("UI:BOOK_PRINT", () => window.print()),
16589
16680
  eventBus.on("UI:BOOK_NAVIGATE", (event) => {
16590
- const chapterId = event.payload?.chapterId;
16591
- const idx = chapters.findIndex((ch) => ch.id === chapterId);
16681
+ const targetId = event.payload?.chapterId;
16682
+ const idx = chapters.findIndex((ch) => chapterId(ch) === targetId);
16592
16683
  if (idx >= 0) navigateTo(idx + 2);
16593
16684
  })
16594
16685
  ];
@@ -16605,9 +16696,11 @@ var init_BookViewer = __esm({
16605
16696
  style.remove();
16606
16697
  };
16607
16698
  }, []);
16608
- const currentChapterId = currentPage >= 2 ? chapters[currentPage - 2]?.id : void 0;
16609
- const currentChapterTitle = currentPage >= 2 ? chapters[currentPage - 2]?.title : void 0;
16699
+ const currentChapterId = currentPage >= 2 ? chapterId(chapters[currentPage - 2]) : void 0;
16700
+ const currentChapterTitle = currentPage >= 2 ? chapterTitle(chapters[currentPage - 2]) : void 0;
16610
16701
  if (!book) return /* @__PURE__ */ jsx(EmptyState, { message: t("book.noData") });
16702
+ const cover = book.cover;
16703
+ const coverTitle = String(cover.title ?? "");
16611
16704
  return /* @__PURE__ */ jsxs(VStack, { className: cn("relative h-full overflow-hidden bg-background", className), children: [
16612
16705
  /* @__PURE__ */ jsxs(
16613
16706
  Box,
@@ -16619,10 +16712,10 @@ var init_BookViewer = __esm({
16619
16712
  /* @__PURE__ */ jsx(
16620
16713
  BookCoverPage,
16621
16714
  {
16622
- title: book.title,
16623
- subtitle: book.subtitle,
16624
- author: book.author,
16625
- coverImageUrl: book.coverImageUrl,
16715
+ title: coverTitle,
16716
+ subtitle: String(cover.subtitle ?? "") || void 0,
16717
+ author: String(cover.author ?? "") || void 0,
16718
+ coverImageUrl: String(cover.coverImageUrl ?? "") || void 0,
16626
16719
  direction
16627
16720
  }
16628
16721
  ),
@@ -16633,23 +16726,27 @@ var init_BookViewer = __esm({
16633
16726
  direction
16634
16727
  }
16635
16728
  ),
16636
- chapters.map((chapter) => /* @__PURE__ */ jsx(
16637
- BookChapterView,
16638
- {
16639
- chapter,
16640
- direction
16641
- },
16642
- chapter.id
16643
- ))
16729
+ chapters.map((chapter) => {
16730
+ const id = chapterId(chapter);
16731
+ return /* @__PURE__ */ jsx(
16732
+ BookChapterView,
16733
+ {
16734
+ chapter,
16735
+ orbitalSchema: id ? book.schemaByChapterId[id] : void 0,
16736
+ direction
16737
+ },
16738
+ id
16739
+ );
16740
+ })
16644
16741
  ] }),
16645
16742
  /* @__PURE__ */ jsxs(Box, { className: "print:hidden", children: [
16646
16743
  currentPage === 0 && /* @__PURE__ */ jsx(
16647
16744
  BookCoverPage,
16648
16745
  {
16649
- title: book.title,
16650
- subtitle: book.subtitle,
16651
- author: book.author,
16652
- coverImageUrl: book.coverImageUrl,
16746
+ title: coverTitle,
16747
+ subtitle: String(cover.subtitle ?? "") || void 0,
16748
+ author: String(cover.author ?? "") || void 0,
16749
+ coverImageUrl: String(cover.coverImageUrl ?? "") || void 0,
16653
16750
  direction
16654
16751
  }
16655
16752
  ),
@@ -16665,6 +16762,7 @@ var init_BookViewer = __esm({
16665
16762
  BookChapterView,
16666
16763
  {
16667
16764
  chapter: chapters[currentPage - 2],
16765
+ orbitalSchema: currentChapterId ? book.schemaByChapterId[currentChapterId] : void 0,
16668
16766
  direction
16669
16767
  }
16670
16768
  )
@@ -16677,7 +16775,7 @@ var init_BookViewer = __esm({
16677
16775
  {
16678
16776
  currentPage,
16679
16777
  totalPages,
16680
- chapterTitle: currentPage === 0 ? book.title : currentPage === 1 ? t("book.tableOfContents") : currentChapterTitle,
16778
+ chapterTitle: currentPage === 0 ? coverTitle : currentPage === 1 ? t("book.tableOfContents") : currentChapterTitle,
16681
16779
  direction
16682
16780
  }
16683
16781
  )
@@ -16775,7 +16873,7 @@ var init_Grid = __esm({
16775
16873
  };
16776
16874
  Grid = ({
16777
16875
  cols = 1,
16778
- rows,
16876
+ rows: rows2,
16779
16877
  gap = "md",
16780
16878
  rowGap,
16781
16879
  colGap,
@@ -16787,7 +16885,7 @@ var init_Grid = __esm({
16787
16885
  children,
16788
16886
  as: Component = "div"
16789
16887
  }) => {
16790
- const mergedStyle = rows ? { gridTemplateRows: `repeat(${rows}, minmax(0, 1fr))`, ...style } : style;
16888
+ const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
16791
16889
  return React85__default.createElement(
16792
16890
  Component,
16793
16891
  {
@@ -17503,14 +17601,14 @@ function BuilderBoard({
17503
17601
  }) {
17504
17602
  const { emit } = useEventBus();
17505
17603
  const { t } = useTranslate();
17506
- const resolved = Array.isArray(entity) ? entity[0] : entity;
17604
+ const resolved = boardEntity(entity);
17507
17605
  const [placements, setPlacements] = useState({});
17508
17606
  const [headerError, setHeaderError] = useState(false);
17509
17607
  const [submitted, setSubmitted] = useState(false);
17510
17608
  const [attempts, setAttempts] = useState(0);
17511
17609
  const [showHint, setShowHint] = useState(false);
17512
- const components = resolved?.components ?? [];
17513
- const slots = resolved?.slots ?? [];
17610
+ const components = Array.isArray(resolved?.components) ? resolved.components : [];
17611
+ const slots = Array.isArray(resolved?.slots) ? resolved.slots : [];
17514
17612
  const usedComponentIds = new Set(Object.values(placements));
17515
17613
  const availableComponents = components.filter((c) => !usedComponentIds.has(c.id));
17516
17614
  const [selectedComponent, setSelectedComponent] = useState(null);
@@ -17544,7 +17642,7 @@ function BuilderBoard({
17544
17642
  }, [slots, placements, attempts, completeEvent, emit]);
17545
17643
  const handleReset = () => {
17546
17644
  setSubmitted(false);
17547
- if (attempts >= 2 && resolved?.hint) {
17645
+ if (attempts >= 2 && str(resolved?.hint)) {
17548
17646
  setShowHint(true);
17549
17647
  }
17550
17648
  };
@@ -17557,20 +17655,24 @@ function BuilderBoard({
17557
17655
  };
17558
17656
  const getComponentById = (id) => components.find((c) => c.id === id);
17559
17657
  if (!resolved) return null;
17658
+ const theme = resolved.theme ?? void 0;
17659
+ const themeBackground = theme?.background;
17660
+ const headerImage = str(resolved.headerImage);
17661
+ const hint = str(resolved.hint);
17560
17662
  return /* @__PURE__ */ jsx(
17561
17663
  Box,
17562
17664
  {
17563
17665
  className,
17564
17666
  style: {
17565
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
17667
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
17566
17668
  backgroundSize: "cover",
17567
17669
  backgroundPosition: "center"
17568
17670
  },
17569
17671
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
17570
- 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,
17672
+ 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,
17571
17673
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
17572
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
17573
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description })
17674
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
17675
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) })
17574
17676
  ] }) }),
17575
17677
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
17576
17678
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("builder.components") }),
@@ -17630,9 +17732,9 @@ function BuilderBoard({
17630
17732
  ] }) }),
17631
17733
  submitted && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
17632
17734
  /* @__PURE__ */ jsx(Icon, { icon: allCorrect ? CheckCircle : XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
17633
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("builder.success") : resolved.failMessage ?? t("builder.incorrect") })
17735
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("builder.success") : str(resolved.failMessage) || t("builder.incorrect") })
17634
17736
  ] }) }),
17635
- showHint && resolved.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
17737
+ showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
17636
17738
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
17637
17739
  !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
17638
17740
  /* @__PURE__ */ jsx(Icon, { icon: Wrench, size: "sm" }),
@@ -17651,6 +17753,7 @@ var init_BuilderBoard = __esm({
17651
17753
  "components/game/organisms/puzzles/builder/BuilderBoard.tsx"() {
17652
17754
  init_atoms2();
17653
17755
  init_useEventBus();
17756
+ init_boardEntity();
17654
17757
  BuilderBoard.displayName = "BuilderBoard";
17655
17758
  }
17656
17759
  });
@@ -17988,21 +18091,24 @@ function CalendarGrid({
17988
18091
  eventBus.emit(`UI:${longPressEvent}`, { date: day.toISOString(), time, ...longPressPayload });
17989
18092
  }, 500);
17990
18093
  }, [longPressEvent, longPressPayload, eventBus]);
17991
- const renderEvent = (event) => /* @__PURE__ */ jsx(
17992
- Box,
17993
- {
17994
- rounded: "md",
17995
- padding: "xs",
17996
- border: true,
17997
- className: cn(
17998
- "cursor-pointer hover:shadow-sm transition-shadow text-xs truncate",
17999
- event.color ? event.color : "bg-blue-500/15 border-blue-500/30 text-blue-600"
18000
- ),
18001
- onClick: (e) => handleEventClick(event, e),
18002
- children: /* @__PURE__ */ jsx(Typography, { variant: "small", className: "truncate font-medium", children: event.title })
18003
- },
18004
- event.id
18005
- );
18094
+ const renderEvent = (event) => {
18095
+ const color = event.color;
18096
+ return /* @__PURE__ */ jsx(
18097
+ Box,
18098
+ {
18099
+ rounded: "md",
18100
+ padding: "xs",
18101
+ border: true,
18102
+ className: cn(
18103
+ "cursor-pointer hover:shadow-sm transition-shadow text-xs truncate",
18104
+ color ? color : "bg-blue-500/15 border-blue-500/30 text-blue-600"
18105
+ ),
18106
+ onClick: (e) => handleEventClick(event, e),
18107
+ children: /* @__PURE__ */ jsx(Typography, { variant: "small", className: "truncate font-medium", children: event.title })
18108
+ },
18109
+ event.id
18110
+ );
18111
+ };
18006
18112
  return /* @__PURE__ */ jsxs(
18007
18113
  Box,
18008
18114
  {
@@ -19666,7 +19772,6 @@ var init_CardGrid = __esm({
19666
19772
  alignItems = "stretch",
19667
19773
  className,
19668
19774
  children,
19669
- // EntityDisplayProps
19670
19775
  entity,
19671
19776
  isLoading = false,
19672
19777
  error = null,
@@ -20138,14 +20243,14 @@ var init_CaseStudyOrganism = __esm({
20138
20243
  /* @__PURE__ */ jsx(SimpleGrid, { cols: cols > 0 ? cols : 1, gap: "lg", children: items.map((study) => /* @__PURE__ */ jsx(
20139
20244
  CaseStudyCard,
20140
20245
  {
20141
- title: study.title,
20142
- description: study.description,
20143
- category: study.category,
20144
- categoryColor: study.categoryColor,
20145
- href: study.href,
20146
- linkLabel: study.linkLabel
20246
+ title: String(study.title ?? ""),
20247
+ description: String(study.description ?? ""),
20248
+ category: String(study.category ?? ""),
20249
+ categoryColor: study.categoryColor != null ? String(study.categoryColor) : void 0,
20250
+ href: String(study.href ?? ""),
20251
+ linkLabel: study.linkLabel != null ? String(study.linkLabel) : void 0
20147
20252
  },
20148
- study.id
20253
+ String(study.id ?? "")
20149
20254
  )) })
20150
20255
  ] });
20151
20256
  };
@@ -20168,10 +20273,10 @@ function CastleBoard({
20168
20273
  className
20169
20274
  }) {
20170
20275
  const eventBus = useEventBus();
20171
- const resolved = Array.isArray(entity) ? entity[0] : entity;
20172
- const tiles = resolved?.tiles ?? [];
20173
- const features = resolved?.features ?? [];
20174
- const units = resolved?.units ?? [];
20276
+ const resolved = boardEntity(entity);
20277
+ const tiles = Array.isArray(resolved?.tiles) ? resolved.tiles : [];
20278
+ const features = Array.isArray(resolved?.features) ? resolved.features : [];
20279
+ const units = Array.isArray(resolved?.units) ? resolved.units : [];
20175
20280
  const assetManifest = resolved?.assetManifest;
20176
20281
  const backgroundImage = resolved?.backgroundImage;
20177
20282
  const [hoveredTile, setHoveredTile] = useState(null);
@@ -20199,7 +20304,7 @@ function CastleBoard({
20199
20304
  onFeatureClick?.(feature);
20200
20305
  if (featureClickEvent) {
20201
20306
  eventBus.emit(`UI:${featureClickEvent}`, {
20202
- featureId: feature.id,
20307
+ featureId: feature.id ?? "",
20203
20308
  featureType: feature.type,
20204
20309
  x: feature.x,
20205
20310
  y: feature.y
@@ -20267,6 +20372,7 @@ var init_CastleBoard = __esm({
20267
20372
  init_cn();
20268
20373
  init_useEventBus();
20269
20374
  init_IsometricCanvas2();
20375
+ init_boardEntity();
20270
20376
  init_isometric();
20271
20377
  CastleBoard.displayName = "CastleBoard";
20272
20378
  }
@@ -21077,14 +21183,14 @@ function ClassifierBoard({
21077
21183
  }) {
21078
21184
  const { emit } = useEventBus();
21079
21185
  const { t } = useTranslate();
21080
- const resolved = Array.isArray(entity) ? entity[0] : entity;
21186
+ const resolved = boardEntity(entity);
21081
21187
  const [assignments, setAssignments] = useState({});
21082
21188
  const [headerError, setHeaderError] = useState(false);
21083
21189
  const [submitted, setSubmitted] = useState(false);
21084
21190
  const [attempts, setAttempts] = useState(0);
21085
21191
  const [showHint, setShowHint] = useState(false);
21086
- const items = resolved?.items ?? [];
21087
- const categories = resolved?.categories ?? [];
21192
+ const items = Array.isArray(resolved?.items) ? resolved.items : [];
21193
+ const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
21088
21194
  const unassignedItems = items.filter((item) => !assignments[item.id]);
21089
21195
  const allAssigned = Object.keys(assignments).length === items.length;
21090
21196
  const results = submitted ? items.map((item) => ({
@@ -21116,7 +21222,7 @@ function ClassifierBoard({
21116
21222
  }, [items, assignments, attempts, completeEvent, emit]);
21117
21223
  const handleReset = () => {
21118
21224
  setSubmitted(false);
21119
- if (attempts >= 2 && resolved?.hint) {
21225
+ if (attempts >= 2 && str(resolved?.hint)) {
21120
21226
  setShowHint(true);
21121
21227
  }
21122
21228
  };
@@ -21127,20 +21233,25 @@ function ClassifierBoard({
21127
21233
  setShowHint(false);
21128
21234
  };
21129
21235
  if (!resolved) return null;
21236
+ const theme = resolved.theme ?? void 0;
21237
+ const themeBackground = theme?.background;
21238
+ const headerImage = str(resolved.headerImage);
21239
+ const hint = str(resolved.hint);
21240
+ const failMessage = str(resolved.failMessage);
21130
21241
  return /* @__PURE__ */ jsx(
21131
21242
  Box,
21132
21243
  {
21133
21244
  className,
21134
21245
  style: {
21135
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
21246
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
21136
21247
  backgroundSize: "cover",
21137
21248
  backgroundPosition: "center"
21138
21249
  },
21139
21250
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
21140
- 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,
21251
+ 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,
21141
21252
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
21142
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
21143
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description })
21253
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
21254
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) })
21144
21255
  ] }) }),
21145
21256
  unassignedItems.length > 0 && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
21146
21257
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("classifier.itemsToSort") }),
@@ -21192,10 +21303,10 @@ function ClassifierBoard({
21192
21303
  }) }),
21193
21304
  submitted && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
21194
21305
  /* @__PURE__ */ jsx(Icon, { icon: allCorrect ? CheckCircle : XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
21195
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("classifier.allCorrect") : `${correctCount}/${items.length} ${t("classifier.correct")}` }),
21196
- !allCorrect && resolved.failMessage && /* @__PURE__ */ jsx(Typography, { variant: "body", className: "text-muted-foreground", children: resolved.failMessage })
21306
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("classifier.allCorrect") : `${correctCount}/${items.length} ${t("classifier.correct")}` }),
21307
+ !allCorrect && failMessage && /* @__PURE__ */ jsx(Typography, { variant: "body", className: "text-muted-foreground", children: failMessage })
21197
21308
  ] }) }),
21198
- showHint && resolved.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
21309
+ showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
21199
21310
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
21200
21311
  !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
21201
21312
  /* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
@@ -21214,6 +21325,7 @@ var init_ClassifierBoard = __esm({
21214
21325
  "components/game/organisms/puzzles/classifier/ClassifierBoard.tsx"() {
21215
21326
  init_atoms2();
21216
21327
  init_useEventBus();
21328
+ init_boardEntity();
21217
21329
  ClassifierBoard.displayName = "ClassifierBoard";
21218
21330
  }
21219
21331
  });
@@ -27584,7 +27696,7 @@ function InventoryPanel({
27584
27696
  const slotArray = Array.from({ length: safeSlots }, (_, index) => {
27585
27697
  return safeItems[index] ?? null;
27586
27698
  });
27587
- const rows = Math.ceil(safeSlots / safeColumns);
27699
+ const rows2 = Math.ceil(safeSlots / safeColumns);
27588
27700
  const handleSlotClick = useCallback((index) => {
27589
27701
  if (selectSlotEvent) eventBus.emit(`UI:${selectSlotEvent}`, { index });
27590
27702
  onSelectSlot?.(index);
@@ -27653,7 +27765,7 @@ function InventoryPanel({
27653
27765
  className: "grid gap-1 bg-[var(--color-card)] p-2 rounded-container border border-border",
27654
27766
  style: {
27655
27767
  gridTemplateColumns: `repeat(${safeColumns}, ${slotSize}px)`,
27656
- gridTemplateRows: `repeat(${rows}, ${slotSize}px)`
27768
+ gridTemplateRows: `repeat(${rows2}, ${slotSize}px)`
27657
27769
  },
27658
27770
  children: slotArray.map((item, index) => /* @__PURE__ */ jsx(
27659
27771
  "button",
@@ -31617,11 +31729,11 @@ function LatticeSVG({
31617
31729
  }) {
31618
31730
  const paths = [];
31619
31731
  const cols = 5;
31620
- const rows = Math.ceil(h / (w / cols));
31732
+ const rows2 = Math.ceil(h / (w / cols));
31621
31733
  const cellW = w / cols;
31622
31734
  const cellH = cellW;
31623
31735
  const bulge = cellW * 0.3;
31624
- for (let row = 0; row < rows; row++) {
31736
+ for (let row = 0; row < rows2; row++) {
31625
31737
  for (let col = 0; col < cols; col++) {
31626
31738
  const cx = col * cellW + cellW / 2;
31627
31739
  const cy = row * cellH + cellH / 2;
@@ -32033,7 +32145,7 @@ var init_MatrixQuestion = __esm({
32033
32145
  };
32034
32146
  MatrixQuestion = ({
32035
32147
  title,
32036
- rows,
32148
+ rows: rows2,
32037
32149
  columns = DEFAULT_MATRIX_COLUMNS,
32038
32150
  values,
32039
32151
  onChange,
@@ -32043,7 +32155,7 @@ var init_MatrixQuestion = __esm({
32043
32155
  className
32044
32156
  }) => {
32045
32157
  const styles = sizeStyles13[size];
32046
- const safeRows = rows ?? [];
32158
+ const safeRows = rows2 ?? [];
32047
32159
  const safeValues = values ?? {};
32048
32160
  const eventBus = useEventBus();
32049
32161
  const handleChange = useCallback(
@@ -32671,7 +32783,8 @@ var init_PositionedCanvas = __esm({
32671
32783
  dragRef.current = null;
32672
32784
  setDraggingId(null);
32673
32785
  if (!wasDrag) {
32674
- const next = selectedId === item.id ? null : item.id;
32786
+ const itemId = item.id;
32787
+ const next = selectedId === itemId ? null : itemId;
32675
32788
  onSelect?.(next);
32676
32789
  if (selectEvent) {
32677
32790
  eventBus.emit(`UI:${selectEvent}`, { id: next });
@@ -32706,15 +32819,22 @@ var init_PositionedCanvas = __esm({
32706
32819
  style: { width, height },
32707
32820
  onClick: handleContainerClick,
32708
32821
  children: items.map((item) => {
32822
+ const itemId = item.id;
32823
+ const label = item.label;
32824
+ const x = item.x;
32825
+ const y = item.y;
32826
+ const capacity = item.capacity;
32827
+ const partySize = item.partySize;
32828
+ const serverName = item.serverName;
32709
32829
  const status = item.status ?? "empty";
32710
32830
  const shape = item.shape ?? "round";
32711
- const isSelected = selectedId === item.id;
32712
- const isDragging = draggingId === item.id;
32831
+ const isSelected = selectedId === itemId;
32832
+ const isDragging = draggingId === itemId;
32713
32833
  const statusBadge = STATUS_BADGE[status];
32714
32834
  return /* @__PURE__ */ jsxs(
32715
32835
  Box,
32716
32836
  {
32717
- "data-testid": `item-node-${item.id}`,
32837
+ "data-testid": `item-node-${itemId}`,
32718
32838
  "data-status": status,
32719
32839
  className: cn(
32720
32840
  "absolute flex flex-col items-center justify-center gap-1 border-2 select-none",
@@ -32725,7 +32845,7 @@ var init_PositionedCanvas = __esm({
32725
32845
  isSelected && "outline outline-2 outline-offset-2 outline-primary shadow-md",
32726
32846
  isDragging && "shadow-lg z-10"
32727
32847
  ),
32728
- style: { left: item.x, top: item.y, touchAction: "none" },
32848
+ style: { left: x, top: y, touchAction: "none" },
32729
32849
  onPointerDown: (e) => handlePointerDown(e, item),
32730
32850
  onPointerMove: handlePointerMove,
32731
32851
  onPointerUp: (e) => handlePointerUp(e, item),
@@ -32733,10 +32853,10 @@ var init_PositionedCanvas = __esm({
32733
32853
  children: [
32734
32854
  /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-1", children: [
32735
32855
  getStatusIcon(status),
32736
- /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "semibold", children: item.label })
32856
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "semibold", children: label })
32737
32857
  ] }),
32738
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: item.partySize !== void 0 && status === "seated" ? `${item.partySize}/${item.capacity}` : `Cap ${item.capacity}` }),
32739
- status === "seated" && item.serverName && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "truncate max-w-[80%]", children: item.serverName }),
32858
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: partySize !== void 0 && status === "seated" ? `${partySize}/${capacity}` : `Cap ${capacity}` }),
32859
+ status === "seated" && serverName && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "truncate max-w-[80%]", children: serverName }),
32740
32860
  isSelected && /* @__PURE__ */ jsx(
32741
32861
  Badge,
32742
32862
  {
@@ -32748,7 +32868,7 @@ var init_PositionedCanvas = __esm({
32748
32868
  )
32749
32869
  ]
32750
32870
  },
32751
- item.id
32871
+ itemId
32752
32872
  );
32753
32873
  })
32754
32874
  }
@@ -33520,9 +33640,10 @@ var init_RichBlockEditor = __esm({
33520
33640
  });
33521
33641
  function collectInitiallyCollapsed(nodes, acc) {
33522
33642
  for (const n of nodes) {
33643
+ const replies = n.replies;
33523
33644
  if (n.collapsed) acc.add(n.id);
33524
- if (n.replies && n.replies.length > 0) {
33525
- collectInitiallyCollapsed(n.replies, acc);
33645
+ if (replies && replies.length > 0) {
33646
+ collectInitiallyCollapsed(replies, acc);
33526
33647
  }
33527
33648
  }
33528
33649
  }
@@ -33552,44 +33673,52 @@ var init_ReplyTree = __esm({
33552
33673
  }) => {
33553
33674
  const eventBus = useEventBus();
33554
33675
  const { t } = useTranslate();
33555
- const hasReplies = !!node.replies && node.replies.length > 0;
33556
- const isCollapsed = collapsedSet.has(node.id);
33676
+ const nodeId = node.id;
33677
+ const authorName = node.authorName;
33678
+ const authorAvatarUrl = node.authorAvatarUrl;
33679
+ const content = node.content;
33680
+ const postedAt = node.postedAt;
33681
+ const voteCount = node.voteCount;
33682
+ const userVote = node.userVote;
33683
+ const replies = node.replies;
33684
+ const hasReplies = !!replies && replies.length > 0;
33685
+ const isCollapsed = collapsedSet.has(nodeId);
33557
33686
  const atMaxDepth = depth >= maxDepth;
33558
33687
  const [replyOpen, setReplyOpen] = useState(false);
33559
33688
  const [draft, setDraft] = useState("");
33560
33689
  const handleVote = useCallback(
33561
33690
  (next) => {
33562
- onVote?.(node.id, next);
33563
- if (voteEvent) eventBus.emit(`UI:${voteEvent}`, { nodeId: node.id, vote: next });
33691
+ onVote?.(nodeId, next);
33692
+ if (voteEvent) eventBus.emit(`UI:${voteEvent}`, { nodeId, vote: next });
33564
33693
  },
33565
- [node.id, onVote, voteEvent, eventBus]
33694
+ [nodeId, onVote, voteEvent, eventBus]
33566
33695
  );
33567
33696
  const handleReply = useCallback(() => {
33568
- onReply?.(node.id);
33697
+ onReply?.(nodeId);
33569
33698
  setReplyOpen((open) => !open);
33570
- }, [node.id, onReply]);
33699
+ }, [nodeId, onReply]);
33571
33700
  const handleSubmitReply = useCallback(() => {
33572
- const content = draft.trim();
33573
- if (!content) return;
33574
- if (replyEvent) eventBus.emit(`UI:${replyEvent}`, { parentNodeId: node.id, content });
33701
+ const text = draft.trim();
33702
+ if (!text) return;
33703
+ if (replyEvent) eventBus.emit(`UI:${replyEvent}`, { parentNodeId: nodeId, content: text });
33575
33704
  setDraft("");
33576
33705
  setReplyOpen(false);
33577
- }, [node.id, draft, replyEvent, eventBus]);
33706
+ }, [nodeId, draft, replyEvent, eventBus]);
33578
33707
  const handleCancelReply = useCallback(() => {
33579
33708
  setDraft("");
33580
33709
  setReplyOpen(false);
33581
33710
  }, []);
33582
33711
  const handleFlag = useCallback(() => {
33583
- onFlag?.(node.id);
33584
- if (flagEvent) eventBus.emit(`UI:${flagEvent}`, { nodeId: node.id });
33585
- }, [node.id, onFlag, flagEvent, eventBus]);
33712
+ onFlag?.(nodeId);
33713
+ if (flagEvent) eventBus.emit(`UI:${flagEvent}`, { nodeId });
33714
+ }, [nodeId, onFlag, flagEvent, eventBus]);
33586
33715
  const handleContinue = useCallback(() => {
33587
- onContinueThread?.(node.id);
33588
- if (continueThreadEvent) eventBus.emit(`UI:${continueThreadEvent}`, { nodeId: node.id });
33589
- }, [node.id, onContinueThread, continueThreadEvent, eventBus]);
33716
+ onContinueThread?.(nodeId);
33717
+ if (continueThreadEvent) eventBus.emit(`UI:${continueThreadEvent}`, { nodeId });
33718
+ }, [nodeId, onContinueThread, continueThreadEvent, eventBus]);
33590
33719
  const handleToggle = useCallback(() => {
33591
- toggleCollapse(node.id);
33592
- }, [node.id, toggleCollapse]);
33720
+ toggleCollapse(nodeId);
33721
+ }, [nodeId, toggleCollapse]);
33593
33722
  return /* @__PURE__ */ jsxs(Box, { className: "flex flex-row gap-2 items-stretch min-w-0", children: [
33594
33723
  /* @__PURE__ */ jsxs(Box, { className: "flex flex-col items-center flex-shrink-0 w-6", children: [
33595
33724
  hasReplies ? /* @__PURE__ */ jsx(
@@ -33621,25 +33750,25 @@ var init_ReplyTree = __esm({
33621
33750
  /* @__PURE__ */ jsx(
33622
33751
  Avatar,
33623
33752
  {
33624
- src: node.authorAvatarUrl,
33625
- name: node.authorName,
33753
+ src: authorAvatarUrl,
33754
+ name: authorName,
33626
33755
  size: "sm"
33627
33756
  }
33628
33757
  ),
33629
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "semibold", children: node.authorName }),
33630
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: node.postedAt })
33758
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "semibold", children: authorName }),
33759
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: postedAt })
33631
33760
  ] }),
33632
- /* @__PURE__ */ jsx(Typography, { variant: "body", className: "whitespace-pre-wrap break-words", children: node.content }),
33761
+ /* @__PURE__ */ jsx(Typography, { variant: "body", className: "whitespace-pre-wrap break-words", children: content }),
33633
33762
  showActions && /* @__PURE__ */ jsxs(Box, { className: "flex flex-row gap-2 items-center", children: [
33634
33763
  /* @__PURE__ */ jsx(
33635
33764
  VoteStack,
33636
33765
  {
33637
- count: node.voteCount ?? 0,
33638
- userVote: node.userVote ?? null,
33766
+ count: voteCount ?? 0,
33767
+ userVote: userVote ?? null,
33639
33768
  onVote: handleVote,
33640
33769
  size: "sm",
33641
33770
  variant: "horizontal",
33642
- label: t("replyTree.voteOnReplyBy", { author: node.authorName })
33771
+ label: t("replyTree.voteOnReplyBy", { author: authorName })
33643
33772
  }
33644
33773
  ),
33645
33774
  /* @__PURE__ */ jsx(
@@ -33649,7 +33778,7 @@ var init_ReplyTree = __esm({
33649
33778
  size: "sm",
33650
33779
  leftIcon: "message-square",
33651
33780
  onClick: handleReply,
33652
- "aria-label": t("replyTree.replyTo", { author: node.authorName }),
33781
+ "aria-label": t("replyTree.replyTo", { author: authorName }),
33653
33782
  children: t("replyTree.reply")
33654
33783
  }
33655
33784
  ),
@@ -33660,7 +33789,7 @@ var init_ReplyTree = __esm({
33660
33789
  size: "sm",
33661
33790
  leftIcon: "flag",
33662
33791
  onClick: handleFlag,
33663
- "aria-label": t("replyTree.flagReplyBy", { author: node.authorName }),
33792
+ "aria-label": t("replyTree.flagReplyBy", { author: authorName }),
33664
33793
  children: t("replyTree.flag")
33665
33794
  }
33666
33795
  )
@@ -33672,9 +33801,9 @@ var init_ReplyTree = __esm({
33672
33801
  inputType: "textarea",
33673
33802
  rows: 2,
33674
33803
  value: draft,
33675
- placeholder: t("replyTree.replyToPlaceholder", { author: node.authorName }),
33804
+ placeholder: t("replyTree.replyToPlaceholder", { author: authorName }),
33676
33805
  onChange: (e) => setDraft(e.target.value),
33677
- "aria-label": t("replyTree.replyTo", { author: node.authorName })
33806
+ "aria-label": t("replyTree.replyTo", { author: authorName })
33678
33807
  }
33679
33808
  ),
33680
33809
  /* @__PURE__ */ jsxs(Box, { className: "flex flex-row gap-2 items-center", children: [
@@ -33705,7 +33834,7 @@ var init_ReplyTree = __esm({
33705
33834
  ),
33706
33835
  children: t("replyTree.continueThread")
33707
33836
  }
33708
- ) : /* @__PURE__ */ jsx(Box, { className: "flex flex-col gap-2 mt-1", children: node.replies.map((child) => /* @__PURE__ */ jsx(
33837
+ ) : /* @__PURE__ */ jsx(Box, { className: "flex flex-col gap-2 mt-1", children: replies.map((child) => /* @__PURE__ */ jsx(
33709
33838
  ReplyTreeNode,
33710
33839
  {
33711
33840
  node: child,
@@ -36025,8 +36154,8 @@ var init_WizardContainer = __esm({
36025
36154
  return void 0;
36026
36155
  if (typeof controlledStep === "number") return controlledStep;
36027
36156
  if (typeof controlledStep === "string") return parseInt(controlledStep, 10);
36028
- const num = Number(controlledStep);
36029
- return isNaN(num) ? void 0 : num;
36157
+ const num2 = Number(controlledStep);
36158
+ return isNaN(num2) ? void 0 : num2;
36030
36159
  })();
36031
36160
  const currentStep = normalizedControlledStep !== void 0 ? normalizedControlledStep : internalStep;
36032
36161
  const totalSteps = steps.length;
@@ -37731,7 +37860,7 @@ function DebuggerBoard({
37731
37860
  }) {
37732
37861
  const { emit } = useEventBus();
37733
37862
  const { t } = useTranslate();
37734
- const resolved = Array.isArray(entity) ? entity[0] : entity;
37863
+ const resolved = boardEntity(entity);
37735
37864
  const [flaggedLines, setFlaggedLines] = useState(/* @__PURE__ */ new Set());
37736
37865
  const [headerError, setHeaderError] = useState(false);
37737
37866
  const [submitted, setSubmitted] = useState(false);
@@ -37749,7 +37878,7 @@ function DebuggerBoard({
37749
37878
  return next;
37750
37879
  });
37751
37880
  };
37752
- const lines = resolved?.lines ?? [];
37881
+ const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
37753
37882
  const bugLines = lines.filter((l) => l.isBug);
37754
37883
  const correctFlags = lines.filter((l) => l.isBug && flaggedLines.has(l.id));
37755
37884
  const falseFlags = lines.filter((l) => !l.isBug && flaggedLines.has(l.id));
@@ -37764,7 +37893,7 @@ function DebuggerBoard({
37764
37893
  }, [correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
37765
37894
  const handleReset = () => {
37766
37895
  setSubmitted(false);
37767
- if (attempts >= 2 && resolved?.hint) {
37896
+ if (attempts >= 2 && str(resolved?.hint)) {
37768
37897
  setShowHint(true);
37769
37898
  }
37770
37899
  };
@@ -37775,24 +37904,28 @@ function DebuggerBoard({
37775
37904
  setShowHint(false);
37776
37905
  };
37777
37906
  if (!resolved) return null;
37907
+ const theme = resolved.theme ?? void 0;
37908
+ const themeBackground = theme?.background;
37909
+ const headerImage = str(resolved.headerImage);
37910
+ const hint = str(resolved.hint);
37778
37911
  return /* @__PURE__ */ jsx(
37779
37912
  Box,
37780
37913
  {
37781
37914
  className,
37782
37915
  style: {
37783
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
37916
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
37784
37917
  backgroundSize: "cover",
37785
37918
  backgroundPosition: "center"
37786
37919
  },
37787
37920
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
37788
- 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,
37921
+ 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,
37789
37922
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
37790
37923
  /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
37791
37924
  /* @__PURE__ */ jsx(Icon, { icon: Bug, size: "sm" }),
37792
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title })
37925
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) })
37793
37926
  ] }),
37794
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description }),
37795
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(resolved.bugCount) }) })
37927
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) }),
37928
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(num(resolved.bugCount)) }) })
37796
37929
  ] }) }),
37797
37930
  /* @__PURE__ */ jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsx(VStack, { gap: "none", children: lines.map((line, i) => {
37798
37931
  const isFlagged = flaggedLines.has(line.id);
@@ -37824,7 +37957,7 @@ function DebuggerBoard({
37824
37957
  );
37825
37958
  }) }) }),
37826
37959
  submitted && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
37827
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("debugger.allFound") : `${correctFlags.length}/${bugLines.length} ${t("debugger.bugsFound")}` }),
37960
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("debugger.allFound") : `${correctFlags.length}/${bugLines.length} ${t("debugger.bugsFound")}` }),
37828
37961
  bugLines.map((line) => /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "start", children: [
37829
37962
  /* @__PURE__ */ jsx(
37830
37963
  Icon,
@@ -37840,7 +37973,7 @@ function DebuggerBoard({
37840
37973
  ] })
37841
37974
  ] }, line.id))
37842
37975
  ] }) }),
37843
- showHint && resolved.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
37976
+ showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
37844
37977
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
37845
37978
  !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.size === 0, children: [
37846
37979
  /* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
@@ -37859,6 +37992,7 @@ var init_DebuggerBoard = __esm({
37859
37992
  "components/game/organisms/puzzles/debugger/DebuggerBoard.tsx"() {
37860
37993
  init_atoms2();
37861
37994
  init_useEventBus();
37995
+ init_boardEntity();
37862
37996
  DebuggerBoard.displayName = "DebuggerBoard";
37863
37997
  }
37864
37998
  });
@@ -37896,7 +38030,7 @@ function getBadgeVariant(fieldName, value) {
37896
38030
  return "default";
37897
38031
  }
37898
38032
  function formatFieldLabel(fieldName) {
37899
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase());
38033
+ return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str2) => str2.toUpperCase());
37900
38034
  }
37901
38035
  function formatFieldValue(value, fieldName) {
37902
38036
  if (typeof value === "number") {
@@ -37915,26 +38049,26 @@ function formatFieldValue(value, fieldName) {
37915
38049
  }
37916
38050
  function renderRichFieldValue(value, fieldName, fieldType) {
37917
38051
  if (value === void 0 || value === null) return "\u2014";
37918
- const str = String(value);
38052
+ const str2 = String(value);
37919
38053
  switch (fieldType) {
37920
38054
  case "image":
37921
38055
  case "url": {
37922
- if (str.match(/\.(png|jpe?g|gif|svg|webp|avif)(\?|$)/i) || str.startsWith("data:image/")) {
38056
+ if (str2.match(/\.(png|jpe?g|gif|svg|webp|avif)(\?|$)/i) || str2.startsWith("data:image/")) {
37923
38057
  return /* @__PURE__ */ jsx(Box, { className: "mt-1 max-w-full", children: /* @__PURE__ */ jsx(
37924
38058
  "img",
37925
38059
  {
37926
- src: str,
38060
+ src: str2,
37927
38061
  alt: formatFieldLabel(fieldName),
37928
38062
  className: "max-w-full max-h-64 rounded-md object-contain",
37929
38063
  loading: "lazy"
37930
38064
  }
37931
38065
  ) });
37932
38066
  }
37933
- return str;
38067
+ return str2;
37934
38068
  }
37935
38069
  case "markdown":
37936
38070
  case "richtext":
37937
- return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(Typography, { variant: "body", className: "break-words", children: str }), children: /* @__PURE__ */ jsx(
38071
+ return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(Typography, { variant: "body", className: "break-words", children: str2 }), children: /* @__PURE__ */ jsx(
37938
38072
  Box,
37939
38073
  {
37940
38074
  className: "prose prose-sm max-w-none",
@@ -37952,11 +38086,11 @@ function renderRichFieldValue(value, fieldName, fieldType) {
37952
38086
  "--tw-prose-th-borders": "var(--color-border)",
37953
38087
  "--tw-prose-td-borders": "var(--color-border)"
37954
38088
  },
37955
- children: /* @__PURE__ */ jsx(ReactMarkdown2, { children: str })
38089
+ children: /* @__PURE__ */ jsx(ReactMarkdown2, { children: str2 })
37956
38090
  }
37957
38091
  ) });
37958
38092
  case "code":
37959
- 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 }) }) });
38093
+ 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 }) }) });
37960
38094
  case "html":
37961
38095
  return /* @__PURE__ */ jsx(
37962
38096
  Box,
@@ -37976,12 +38110,12 @@ function renderRichFieldValue(value, fieldName, fieldType) {
37976
38110
  "--tw-prose-th-borders": "var(--color-border)",
37977
38111
  "--tw-prose-td-borders": "var(--color-border)"
37978
38112
  },
37979
- children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: str })
38113
+ children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: str2 })
37980
38114
  }
37981
38115
  );
37982
38116
  case "date":
37983
38117
  case "datetime": {
37984
- const d = new Date(str);
38118
+ const d = new Date(str2);
37985
38119
  if (!isNaN(d.getTime())) {
37986
38120
  return d.toLocaleDateString(void 0, {
37987
38121
  year: "numeric",
@@ -37990,7 +38124,7 @@ function renderRichFieldValue(value, fieldName, fieldType) {
37990
38124
  ...fieldType === "datetime" ? { hour: "2-digit", minute: "2-digit" } : {}
37991
38125
  });
37992
38126
  }
37993
- return str;
38127
+ return str2;
37994
38128
  }
37995
38129
  default:
37996
38130
  return formatFieldValue(value, fieldName);
@@ -38668,6 +38802,40 @@ var init_RuleEditor = __esm({
38668
38802
  RuleEditor.displayName = "RuleEditor";
38669
38803
  }
38670
38804
  });
38805
+
38806
+ // components/game/organisms/puzzles/event-handler/puzzleObject.ts
38807
+ function objId(o) {
38808
+ return o.id == null ? "" : String(o.id);
38809
+ }
38810
+ function objName(o) {
38811
+ return o.name == null ? "" : String(o.name);
38812
+ }
38813
+ function objIcon(o) {
38814
+ return o.icon == null ? "" : String(o.icon);
38815
+ }
38816
+ function objStates(o) {
38817
+ return Array.isArray(o.states) ? o.states : [];
38818
+ }
38819
+ function objCurrentState(o) {
38820
+ return o.currentState == null ? "" : String(o.currentState);
38821
+ }
38822
+ function objAvailableEvents(o) {
38823
+ return Array.isArray(o.availableEvents) ? o.availableEvents : [];
38824
+ }
38825
+ function objAvailableActions(o) {
38826
+ return Array.isArray(o.availableActions) ? o.availableActions : [];
38827
+ }
38828
+ function objRules(o) {
38829
+ return Array.isArray(o.rules) ? o.rules : [];
38830
+ }
38831
+ function objMaxRules(o) {
38832
+ const n = Number(o.maxRules);
38833
+ return Number.isFinite(n) && n > 0 ? n : 3;
38834
+ }
38835
+ var init_puzzleObject = __esm({
38836
+ "components/game/organisms/puzzles/event-handler/puzzleObject.ts"() {
38837
+ }
38838
+ });
38671
38839
  function ObjectRulePanel({
38672
38840
  object,
38673
38841
  onRulesChange,
@@ -38675,55 +38843,63 @@ function ObjectRulePanel({
38675
38843
  className
38676
38844
  }) {
38677
38845
  const { t } = useTranslate();
38678
- const maxRules = object.maxRules || 3;
38679
- const canAdd = object.rules.length < maxRules;
38846
+ const id = objId(object);
38847
+ const name = objName(object);
38848
+ const icon = objIcon(object);
38849
+ const states = objStates(object);
38850
+ const currentState = objCurrentState(object);
38851
+ const availableEvents = objAvailableEvents(object);
38852
+ const availableActions = objAvailableActions(object);
38853
+ const rules = objRules(object);
38854
+ const maxRules = objMaxRules(object);
38855
+ const canAdd = rules.length < maxRules;
38680
38856
  const handleRuleChange = useCallback((index, updatedRule) => {
38681
- const newRules = [...object.rules];
38857
+ const newRules = [...rules];
38682
38858
  newRules[index] = updatedRule;
38683
- onRulesChange(object.id, newRules);
38684
- }, [object.id, object.rules, onRulesChange]);
38859
+ onRulesChange(id, newRules);
38860
+ }, [id, rules, onRulesChange]);
38685
38861
  const handleRuleRemove = useCallback((index) => {
38686
- const newRules = object.rules.filter((_, i) => i !== index);
38687
- onRulesChange(object.id, newRules);
38688
- }, [object.id, object.rules, onRulesChange]);
38862
+ const newRules = rules.filter((_, i) => i !== index);
38863
+ onRulesChange(id, newRules);
38864
+ }, [id, rules, onRulesChange]);
38689
38865
  const handleAddRule = useCallback(() => {
38690
38866
  if (!canAdd || disabled) return;
38691
- const firstEvent = object.availableEvents[0]?.value || "";
38692
- const firstAction = object.availableActions[0]?.value || "";
38867
+ const firstEvent = availableEvents[0]?.value || "";
38868
+ const firstAction = availableActions[0]?.value || "";
38693
38869
  const newRule = {
38694
38870
  id: `rule-${nextRuleId++}`,
38695
38871
  whenEvent: firstEvent,
38696
38872
  thenAction: firstAction
38697
38873
  };
38698
- onRulesChange(object.id, [...object.rules, newRule]);
38699
- }, [canAdd, disabled, object, onRulesChange]);
38874
+ onRulesChange(id, [...rules, newRule]);
38875
+ }, [canAdd, disabled, id, rules, availableEvents, availableActions, onRulesChange]);
38700
38876
  const machine = {
38701
- name: object.name,
38702
- states: object.states,
38703
- currentState: object.currentState,
38704
- transitions: object.rules.map((r) => ({
38705
- from: object.currentState,
38706
- to: object.states.find((s) => s !== object.currentState) || object.currentState,
38877
+ name,
38878
+ states,
38879
+ currentState,
38880
+ transitions: rules.map((r) => ({
38881
+ from: currentState,
38882
+ to: states.find((s) => s !== currentState) || currentState,
38707
38883
  event: r.whenEvent
38708
38884
  }))
38709
38885
  };
38710
38886
  return /* @__PURE__ */ jsxs(VStack, { className: cn("p-4 rounded-lg bg-card border border-border", className), gap: "sm", children: [
38711
38887
  /* @__PURE__ */ jsxs(HStack, { className: "items-center", gap: "sm", children: [
38712
- /* @__PURE__ */ jsx(Typography, { variant: "h5", children: object.icon }),
38888
+ /* @__PURE__ */ jsx(Typography, { variant: "h5", children: icon }),
38713
38889
  /* @__PURE__ */ jsxs(VStack, { gap: "none", children: [
38714
- /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-bold", children: object.name }),
38715
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("eventHandler.state") + ": " + object.currentState })
38890
+ /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-bold", children: name }),
38891
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("eventHandler.state") + ": " + currentState })
38716
38892
  ] })
38717
38893
  ] }),
38718
38894
  /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" }),
38719
38895
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
38720
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.rules", { count: object.rules.length, max: maxRules }) + ":" }),
38721
- object.rules.map((rule, i) => /* @__PURE__ */ jsx(
38896
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.rules", { count: rules.length, max: maxRules }) + ":" }),
38897
+ rules.map((rule, i) => /* @__PURE__ */ jsx(
38722
38898
  RuleEditor,
38723
38899
  {
38724
38900
  rule,
38725
- availableEvents: object.availableEvents,
38726
- availableActions: object.availableActions,
38901
+ availableEvents,
38902
+ availableActions,
38727
38903
  onChange: (r) => handleRuleChange(i, r),
38728
38904
  onRemove: () => handleRuleRemove(i),
38729
38905
  disabled
@@ -38741,6 +38917,7 @@ var init_ObjectRulePanel = __esm({
38741
38917
  init_cn();
38742
38918
  init_TraitStateViewer();
38743
38919
  init_RuleEditor();
38920
+ init_puzzleObject();
38744
38921
  nextRuleId = 1;
38745
38922
  ObjectRulePanel.displayName = "ObjectRulePanel";
38746
38923
  }
@@ -38807,11 +38984,11 @@ function EventHandlerBoard({
38807
38984
  }) {
38808
38985
  const { emit } = useEventBus();
38809
38986
  const { t } = useTranslate();
38810
- const resolved = Array.isArray(entity) ? entity[0] : entity;
38811
- const entityObjects = resolved?.objects ?? [];
38812
- const [objects, setObjects] = useState(entityObjects);
38987
+ const resolved = boardEntity(entity);
38988
+ const entityObjects = rows(resolved?.objects);
38989
+ const [objects, setObjects] = useState(() => [...entityObjects]);
38813
38990
  const [selectedObjectId, setSelectedObjectId] = useState(
38814
- entityObjects[0]?.id || null
38991
+ entityObjects[0] ? objId(entityObjects[0]) : null
38815
38992
  );
38816
38993
  const [headerError, setHeaderError] = useState(false);
38817
38994
  const [playState, setPlayState] = useState("editing");
@@ -38822,10 +38999,10 @@ function EventHandlerBoard({
38822
38999
  useEffect(() => () => {
38823
39000
  if (timerRef.current) clearTimeout(timerRef.current);
38824
39001
  }, []);
38825
- const selectedObject = objects.find((o) => o.id === selectedObjectId) || null;
39002
+ const selectedObject = objects.find((o) => objId(o) === selectedObjectId) || null;
38826
39003
  const handleRulesChange = useCallback((objectId, rules) => {
38827
39004
  setObjects((prev) => prev.map(
38828
- (o) => o.id === objectId ? { ...o, rules } : o
39005
+ (o) => objId(o) === objectId ? { ...o, rules } : o
38829
39006
  ));
38830
39007
  }, []);
38831
39008
  const addLogEntry = useCallback((icon, message, status = "done") => {
@@ -38839,11 +39016,12 @@ function EventHandlerBoard({
38839
39016
  setEventLog([]);
38840
39017
  const allRules = [];
38841
39018
  objects.forEach((obj) => {
38842
- obj.rules.forEach((rule) => {
39019
+ objRules(obj).forEach((rule) => {
38843
39020
  allRules.push({ object: obj, rule });
38844
39021
  });
38845
39022
  });
38846
- const triggers = resolved?.triggerEvents || [];
39023
+ const triggers = Array.isArray(resolved?.triggerEvents) ? resolved.triggerEvents : [];
39024
+ const goalEvent = str(resolved?.goalEvent);
38847
39025
  const eventQueue = [...triggers];
38848
39026
  const firedEvents = /* @__PURE__ */ new Set();
38849
39027
  let stepIdx = 0;
@@ -38872,14 +39050,14 @@ function EventHandlerBoard({
38872
39050
  addLogEntry("\u26A1", t("eventHandler.noListeners", { event: currentEvent }), "done");
38873
39051
  } else {
38874
39052
  matching.forEach(({ object, rule }) => {
38875
- addLogEntry(object.icon, t("eventHandler.heardEvent", { object: object.name, event: currentEvent, action: rule.thenAction }), "done");
39053
+ addLogEntry(objIcon(object), t("eventHandler.heardEvent", { object: objName(object), event: currentEvent, action: rule.thenAction }), "done");
38876
39054
  eventQueue.push(rule.thenAction);
38877
- if (rule.thenAction === resolved?.goalEvent) {
39055
+ if (rule.thenAction === goalEvent) {
38878
39056
  goalReached = true;
38879
39057
  }
38880
39058
  });
38881
39059
  }
38882
- if (currentEvent === resolved?.goalEvent) {
39060
+ if (currentEvent === goalEvent) {
38883
39061
  goalReached = true;
38884
39062
  }
38885
39063
  stepIdx++;
@@ -38897,65 +39075,75 @@ function EventHandlerBoard({
38897
39075
  }, []);
38898
39076
  const handleReset = useCallback(() => {
38899
39077
  if (timerRef.current) clearTimeout(timerRef.current);
38900
- setObjects(resolved?.objects ?? []);
39078
+ const resetObjects = rows(resolved?.objects);
39079
+ setObjects([...resetObjects]);
38901
39080
  setPlayState("editing");
38902
39081
  setEventLog([]);
38903
- setSelectedObjectId((resolved?.objects ?? [])[0]?.id || null);
39082
+ setSelectedObjectId(resetObjects[0] ? objId(resetObjects[0]) : null);
38904
39083
  setAttempts(0);
38905
39084
  }, [resolved?.objects]);
38906
39085
  if (!resolved) return null;
38907
39086
  const objectViewers = objects.map((obj) => {
39087
+ const states = objStates(obj);
39088
+ const currentState = objCurrentState(obj);
38908
39089
  const machine = {
38909
- name: obj.name,
38910
- states: obj.states,
38911
- currentState: obj.currentState,
38912
- transitions: obj.rules.map((r) => ({
38913
- from: obj.currentState,
38914
- to: obj.states.find((s) => s !== obj.currentState) || obj.currentState,
39090
+ name: objName(obj),
39091
+ states,
39092
+ currentState,
39093
+ transitions: objRules(obj).map((r) => ({
39094
+ from: currentState,
39095
+ to: states.find((s) => s !== currentState) || currentState,
38915
39096
  event: r.whenEvent
38916
39097
  }))
38917
39098
  };
38918
39099
  return { obj, machine };
38919
39100
  });
38920
- const showHint = attempts >= 3 && resolved.hint;
39101
+ const hint = str(resolved.hint);
39102
+ const showHint = attempts >= 3 && hint;
39103
+ const theme = resolved.theme ?? void 0;
39104
+ const themeBackground = theme?.background;
39105
+ const headerImage = str(resolved.headerImage);
38921
39106
  const encourageKey = ENCOURAGEMENT_KEYS[Math.min(attempts - 1, ENCOURAGEMENT_KEYS.length - 1)] ?? ENCOURAGEMENT_KEYS[0];
38922
39107
  return /* @__PURE__ */ jsxs(
38923
39108
  VStack,
38924
39109
  {
38925
39110
  className: cn("p-4 gap-6", className),
38926
39111
  style: {
38927
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
39112
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
38928
39113
  backgroundSize: "cover",
38929
39114
  backgroundPosition: "center"
38930
39115
  },
38931
39116
  children: [
38932
- 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,
39117
+ 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,
38933
39118
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
38934
- /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
38935
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description }),
39119
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
39120
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) }),
38936
39121
  /* @__PURE__ */ jsxs(HStack, { className: "items-center p-2 rounded bg-primary/10 border border-primary/30", gap: "xs", children: [
38937
39122
  /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-primary font-bold", children: t("game.goal") + ":" }),
38938
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: resolved.goalCondition })
39123
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: str(resolved.goalCondition) })
38939
39124
  ] })
38940
39125
  ] }),
38941
39126
  /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
38942
39127
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.clickObject") + ":" }),
38943
- /* @__PURE__ */ jsx(HStack, { className: "flex-wrap", gap: "sm", children: objectViewers.map(({ obj, machine }) => /* @__PURE__ */ jsx(
38944
- Box,
38945
- {
38946
- className: cn(
38947
- "p-3 rounded-container border-2 cursor-pointer transition-all hover:scale-105",
38948
- selectedObjectId === obj.id ? "border-primary bg-primary/10" : "border-border bg-card hover:border-muted-foreground"
38949
- ),
38950
- onClick: () => setSelectedObjectId(obj.id),
38951
- children: /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "items-center min-w-[120px]", children: [
38952
- /* @__PURE__ */ jsx(Typography, { variant: "h5", children: obj.icon }),
38953
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground font-medium", children: obj.name }),
38954
- /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" })
38955
- ] })
38956
- },
38957
- obj.id
38958
- )) })
39128
+ /* @__PURE__ */ jsx(HStack, { className: "flex-wrap", gap: "sm", children: objectViewers.map(({ obj, machine }) => {
39129
+ const oid = objId(obj);
39130
+ return /* @__PURE__ */ jsx(
39131
+ Box,
39132
+ {
39133
+ className: cn(
39134
+ "p-3 rounded-container border-2 cursor-pointer transition-all hover:scale-105",
39135
+ selectedObjectId === oid ? "border-primary bg-primary/10" : "border-border bg-card hover:border-muted-foreground"
39136
+ ),
39137
+ onClick: () => setSelectedObjectId(oid),
39138
+ children: /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "items-center min-w-[120px]", children: [
39139
+ /* @__PURE__ */ jsx(Typography, { variant: "h5", children: objIcon(obj) }),
39140
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground font-medium", children: objName(obj) }),
39141
+ /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" })
39142
+ ] })
39143
+ },
39144
+ oid
39145
+ );
39146
+ }) })
38959
39147
  ] }),
38960
39148
  selectedObject && /* @__PURE__ */ jsx(
38961
39149
  ObjectRulePanel,
@@ -38966,12 +39154,12 @@ function EventHandlerBoard({
38966
39154
  }
38967
39155
  ),
38968
39156
  eventLog.length > 0 && /* @__PURE__ */ jsx(EventLog, { entries: eventLog }),
38969
- 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") }) }),
39157
+ 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") }) }),
38970
39158
  playState === "fail" && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
38971
39159
  /* @__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) }) }),
38972
39160
  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: [
38973
39161
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
38974
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
39162
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
38975
39163
  ] }) })
38976
39164
  ] }),
38977
39165
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
@@ -38999,6 +39187,8 @@ var init_EventHandlerBoard = __esm({
38999
39187
  init_TraitStateViewer();
39000
39188
  init_ObjectRulePanel();
39001
39189
  init_EventLog();
39190
+ init_puzzleObject();
39191
+ init_boardEntity();
39002
39192
  ENCOURAGEMENT_KEYS = [
39003
39193
  "puzzle.tryAgain1",
39004
39194
  "puzzle.tryAgain2",
@@ -39087,7 +39277,10 @@ var init_FeatureGridOrganism = __esm({
39087
39277
  );
39088
39278
  useCallback(
39089
39279
  (feature) => {
39090
- eventBus.emit("UI:FEATURE_CLICK", { id: feature.id, href: feature.href ?? "" });
39280
+ eventBus.emit("UI:FEATURE_CLICK", {
39281
+ id: String(feature.id ?? ""),
39282
+ href: String(feature.href ?? "")
39283
+ });
39091
39284
  },
39092
39285
  [eventBus]
39093
39286
  );
@@ -39097,14 +39290,17 @@ var init_FeatureGridOrganism = __esm({
39097
39290
  if (error) {
39098
39291
  return /* @__PURE__ */ jsx(ErrorState, { message: error.message, className });
39099
39292
  }
39100
- const featureCards = items.map((feature) => ({
39101
- icon: feature.icon,
39102
- title: feature.title,
39103
- description: feature.description,
39104
- href: feature.href,
39105
- linkLabel: feature.linkLabel,
39106
- variant: feature.href ? "interactive" : "bordered"
39107
- }));
39293
+ const featureCards = items.map((feature) => {
39294
+ const href = feature.href != null ? String(feature.href) : void 0;
39295
+ return {
39296
+ icon: feature.icon != null ? String(feature.icon) : void 0,
39297
+ title: String(feature.title ?? ""),
39298
+ description: String(feature.description ?? ""),
39299
+ href,
39300
+ linkLabel: feature.linkLabel != null ? String(feature.linkLabel) : void 0,
39301
+ variant: href ? "interactive" : "bordered"
39302
+ };
39303
+ });
39108
39304
  return /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
39109
39305
  (heading || subtitle) && /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
39110
39306
  heading && /* @__PURE__ */ jsx(Typography, { variant: "h2", align: "center", children: heading }),
@@ -40422,22 +40618,24 @@ var init_HeroOrganism = __esm({
40422
40618
  () => Array.isArray(entity) ? entity[0] : entity && typeof entity === "object" ? entity : void 0,
40423
40619
  [entity]
40424
40620
  );
40621
+ const primaryAction = resolved?.primaryAction;
40622
+ const secondaryAction = resolved?.secondaryAction;
40425
40623
  const handlePrimaryClick = useCallback(() => {
40426
- if (resolved?.primaryAction) {
40624
+ if (primaryAction) {
40427
40625
  eventBus.emit("UI:CTA_PRIMARY", {
40428
- label: resolved.primaryAction.label,
40429
- href: resolved.primaryAction.href
40626
+ label: String(primaryAction.label ?? ""),
40627
+ href: String(primaryAction.href ?? "")
40430
40628
  });
40431
40629
  }
40432
- }, [eventBus, resolved]);
40630
+ }, [eventBus, primaryAction]);
40433
40631
  const handleSecondaryClick = useCallback(() => {
40434
- if (resolved?.secondaryAction) {
40632
+ if (secondaryAction) {
40435
40633
  eventBus.emit("UI:CTA_SECONDARY", {
40436
- label: resolved.secondaryAction.label,
40437
- href: resolved.secondaryAction.href
40634
+ label: String(secondaryAction.label ?? ""),
40635
+ href: String(secondaryAction.href ?? "")
40438
40636
  });
40439
40637
  }
40440
- }, [eventBus, resolved]);
40638
+ }, [eventBus, secondaryAction]);
40441
40639
  if (isLoading) {
40442
40640
  return /* @__PURE__ */ jsx(LoadingState, { message: t("common.loading"), className });
40443
40641
  }
@@ -40447,17 +40645,19 @@ var init_HeroOrganism = __esm({
40447
40645
  if (!resolved) {
40448
40646
  return null;
40449
40647
  }
40648
+ const imageRaw = resolved.image;
40649
+ const image = imageRaw ? { src: String(imageRaw.src ?? ""), alt: String(imageRaw.alt ?? "") } : void 0;
40450
40650
  return /* @__PURE__ */ jsxs(
40451
40651
  HeroSection,
40452
40652
  {
40453
- tag: resolved.tag,
40454
- title: resolved.title,
40455
- titleAccent: resolved.titleAccent,
40456
- subtitle: resolved.subtitle,
40457
- primaryAction: resolved.primaryAction ? { label: resolved.primaryAction.label, href: resolved.primaryAction.href } : void 0,
40458
- secondaryAction: resolved.secondaryAction ? { label: resolved.secondaryAction.label, href: resolved.secondaryAction.href } : void 0,
40459
- installCommand: resolved.installCommand,
40460
- image: resolved.image,
40653
+ tag: resolved.tag != null ? String(resolved.tag) : void 0,
40654
+ title: String(resolved.title ?? ""),
40655
+ titleAccent: resolved.titleAccent != null ? String(resolved.titleAccent) : void 0,
40656
+ subtitle: String(resolved.subtitle ?? ""),
40657
+ primaryAction: primaryAction ? { label: String(primaryAction.label ?? ""), href: String(primaryAction.href ?? "") } : void 0,
40658
+ secondaryAction: secondaryAction ? { label: String(secondaryAction.label ?? ""), href: String(secondaryAction.href ?? "") } : void 0,
40659
+ installCommand: resolved.installCommand != null ? String(resolved.installCommand) : void 0,
40660
+ image,
40461
40661
  imagePosition: resolved.imagePosition,
40462
40662
  background: resolved.background,
40463
40663
  className: cn(className),
@@ -40466,8 +40666,8 @@ var init_HeroOrganism = __esm({
40466
40666
  /* @__PURE__ */ jsx(
40467
40667
  _HeroClickInterceptor,
40468
40668
  {
40469
- hasPrimary: !!resolved.primaryAction,
40470
- hasSecondary: !!resolved.secondaryAction,
40669
+ hasPrimary: !!primaryAction,
40670
+ hasSecondary: !!secondaryAction,
40471
40671
  onPrimaryClick: handlePrimaryClick,
40472
40672
  onSecondaryClick: handleSecondaryClick
40473
40673
  }
@@ -40696,7 +40896,7 @@ function formatValue3(value, fieldName) {
40696
40896
  return String(value);
40697
40897
  }
40698
40898
  function formatFieldLabel2(fieldName) {
40699
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).replace(/Id$/, "").trim();
40899
+ return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str2) => str2.toUpperCase()).replace(/Id$/, "").trim();
40700
40900
  }
40701
40901
  var STATUS_STYLES2, StatusBadge, ProgressIndicator, List3;
40702
40902
  var init_List = __esm({
@@ -41543,20 +41743,22 @@ function NegotiatorBoard({
41543
41743
  }) {
41544
41744
  const { emit } = useEventBus();
41545
41745
  const { t } = useTranslate();
41546
- const resolved = Array.isArray(entity) ? entity[0] : entity;
41746
+ const resolved = boardEntity(entity);
41547
41747
  const [history, setHistory] = useState([]);
41548
41748
  const [headerError, setHeaderError] = useState(false);
41549
41749
  const [showHint, setShowHint] = useState(false);
41750
+ const totalRounds = num(resolved?.totalRounds);
41751
+ const targetScore = num(resolved?.targetScore);
41550
41752
  const currentRound = history.length;
41551
- const isComplete = currentRound >= (resolved?.totalRounds ?? 0);
41753
+ const isComplete = currentRound >= totalRounds;
41552
41754
  const playerTotal = history.reduce((s, r) => s + r.playerPayoff, 0);
41553
41755
  const opponentTotal = history.reduce((s, r) => s + r.opponentPayoff, 0);
41554
- const won = isComplete && playerTotal >= (resolved?.targetScore ?? 0);
41555
- const actions = resolved?.actions ?? [];
41556
- const payoffMatrix = resolved?.payoffMatrix ?? [];
41756
+ const won = isComplete && playerTotal >= targetScore;
41757
+ const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
41758
+ const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
41557
41759
  const handleAction = useCallback((actionId) => {
41558
41760
  if (isComplete) return;
41559
- const opponentAction = getOpponentAction(resolved?.opponentStrategy ?? "random", actions, history);
41761
+ const opponentAction = getOpponentAction(str(resolved?.opponentStrategy) || "random", actions, history);
41560
41762
  const payoff = payoffMatrix.find(
41561
41763
  (p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
41562
41764
  );
@@ -41569,42 +41771,46 @@ function NegotiatorBoard({
41569
41771
  };
41570
41772
  const newHistory = [...history, result];
41571
41773
  setHistory(newHistory);
41572
- if (newHistory.length >= (resolved?.totalRounds ?? 0)) {
41774
+ if (newHistory.length >= totalRounds) {
41573
41775
  const total = newHistory.reduce((s, r) => s + r.playerPayoff, 0);
41574
- if (total >= (resolved?.targetScore ?? 0)) {
41776
+ if (total >= targetScore) {
41575
41777
  emit(`UI:${completeEvent}`, { success: true, score: total });
41576
41778
  }
41577
- if (newHistory.length >= 3 && resolved?.hint) {
41779
+ if (newHistory.length >= 3 && str(resolved?.hint)) {
41578
41780
  setShowHint(true);
41579
41781
  }
41580
41782
  }
41581
- }, [isComplete, resolved, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
41783
+ }, [isComplete, resolved, totalRounds, targetScore, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
41582
41784
  const handleReset = () => {
41583
41785
  setHistory([]);
41584
41786
  setShowHint(false);
41585
41787
  };
41586
41788
  const getActionLabel = (id) => actions.find((a) => a.id === id)?.label ?? id;
41587
41789
  if (!resolved) return null;
41790
+ const theme = resolved.theme ?? void 0;
41791
+ const themeBackground = theme?.background;
41792
+ const headerImage = str(resolved.headerImage);
41793
+ const hint = str(resolved.hint);
41588
41794
  return /* @__PURE__ */ jsx(
41589
41795
  Box,
41590
41796
  {
41591
41797
  className,
41592
41798
  style: {
41593
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
41799
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
41594
41800
  backgroundSize: "cover",
41595
41801
  backgroundPosition: "center"
41596
41802
  },
41597
41803
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
41598
- 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,
41804
+ 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,
41599
41805
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
41600
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
41601
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description }),
41806
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
41807
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) }),
41602
41808
  /* @__PURE__ */ jsxs(HStack, { gap: "md", children: [
41603
- /* @__PURE__ */ jsx(Badge, { size: "sm", children: t("negotiator.round", { current: String(currentRound), total: String(resolved.totalRounds) }) }),
41809
+ /* @__PURE__ */ jsx(Badge, { size: "sm", children: t("negotiator.round", { current: String(currentRound), total: String(totalRounds) }) }),
41604
41810
  /* @__PURE__ */ jsxs(Badge, { size: "sm", children: [
41605
41811
  t("negotiator.target"),
41606
41812
  ": ",
41607
- resolved.targetScore
41813
+ targetScore
41608
41814
  ] })
41609
41815
  ] })
41610
41816
  ] }) }),
@@ -41653,16 +41859,16 @@ function NegotiatorBoard({
41653
41859
  ] }) }),
41654
41860
  isComplete && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
41655
41861
  /* @__PURE__ */ jsx(Icon, { icon: CheckCircle, size: "lg", className: won ? "text-success" : "text-error" }),
41656
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: won ? resolved.successMessage ?? t("negotiator.success") : resolved.failMessage ?? t("negotiator.failed") }),
41862
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: won ? str(resolved.successMessage) || t("negotiator.success") : str(resolved.failMessage) || t("negotiator.failed") }),
41657
41863
  /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
41658
41864
  t("negotiator.finalScore"),
41659
41865
  ": ",
41660
41866
  playerTotal,
41661
41867
  "/",
41662
- resolved.targetScore
41868
+ targetScore
41663
41869
  ] })
41664
41870
  ] }) }),
41665
- 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 }) }),
41871
+ showHint && hint && !won && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
41666
41872
  isComplete && !won && /* @__PURE__ */ jsx(HStack, { justify: "center", children: /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleReset, children: t("negotiator.playAgain") }) })
41667
41873
  ] })
41668
41874
  }
@@ -41672,6 +41878,7 @@ var init_NegotiatorBoard = __esm({
41672
41878
  "components/game/organisms/puzzles/negotiator/NegotiatorBoard.tsx"() {
41673
41879
  init_atoms2();
41674
41880
  init_useEventBus();
41881
+ init_boardEntity();
41675
41882
  NegotiatorBoard.displayName = "NegotiatorBoard";
41676
41883
  }
41677
41884
  });
@@ -41707,13 +41914,13 @@ var init_PricingOrganism = __esm({
41707
41914
  return /* @__PURE__ */ jsx(ErrorState, { message: error.message, className });
41708
41915
  }
41709
41916
  const plans = items.map((plan) => ({
41710
- name: plan.name,
41711
- price: plan.price,
41712
- description: plan.description,
41713
- features: plan.features,
41714
- action: { label: plan.actionLabel, href: plan.actionHref },
41715
- highlighted: plan.highlighted,
41716
- badge: plan.badge
41917
+ name: String(plan.name ?? ""),
41918
+ price: String(plan.price ?? ""),
41919
+ description: plan.description != null ? String(plan.description) : void 0,
41920
+ features: (plan.features ?? []).map((f3) => String(f3)),
41921
+ action: { label: String(plan.actionLabel ?? ""), href: String(plan.actionHref ?? "") },
41922
+ highlighted: Boolean(plan.highlighted),
41923
+ badge: plan.badge != null ? String(plan.badge) : void 0
41717
41924
  }));
41718
41925
  return /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
41719
41926
  (heading || subtitle) && /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
@@ -43784,16 +43991,20 @@ function SequencerBoard({
43784
43991
  }) {
43785
43992
  const { emit } = useEventBus();
43786
43993
  const { t } = useTranslate();
43787
- const resolved = Array.isArray(entity) ? entity[0] : entity;
43994
+ const resolved = boardEntity(entity);
43995
+ const maxSlots = num(resolved?.maxSlots);
43996
+ const solutions = Array.isArray(resolved?.solutions) ? resolved.solutions : [];
43997
+ const availableActions = Array.isArray(resolved?.availableActions) ? resolved.availableActions : [];
43998
+ const allowDuplicates = resolved?.allowDuplicates !== false;
43788
43999
  const [headerError, setHeaderError] = useState(false);
43789
44000
  const [slots, setSlots] = useState(
43790
- () => Array.from({ length: resolved?.maxSlots ?? 0 }, () => void 0)
44001
+ () => Array.from({ length: maxSlots }, () => void 0)
43791
44002
  );
43792
44003
  const [playState, setPlayState] = useState("idle");
43793
44004
  const [currentStep, setCurrentStep] = useState(-1);
43794
44005
  const [attempts, setAttempts] = useState(0);
43795
44006
  const [slotFeedback, setSlotFeedback] = useState(
43796
- () => Array.from({ length: resolved?.maxSlots ?? 0 }, () => null)
44007
+ () => Array.from({ length: maxSlots }, () => null)
43797
44008
  );
43798
44009
  const timerRef = useRef(null);
43799
44010
  useEffect(() => () => {
@@ -43827,17 +44038,17 @@ function SequencerBoard({
43827
44038
  }, [emit]);
43828
44039
  const handleReset = useCallback(() => {
43829
44040
  if (timerRef.current) clearTimeout(timerRef.current);
43830
- setSlots(Array.from({ length: resolved?.maxSlots ?? 0 }, () => void 0));
44041
+ setSlots(Array.from({ length: maxSlots }, () => void 0));
43831
44042
  setPlayState("idle");
43832
44043
  setCurrentStep(-1);
43833
44044
  setAttempts(0);
43834
- setSlotFeedback(Array.from({ length: resolved?.maxSlots ?? 0 }, () => null));
43835
- }, [resolved?.maxSlots]);
44045
+ setSlotFeedback(Array.from({ length: maxSlots }, () => null));
44046
+ }, [maxSlots]);
43836
44047
  const filledSlots = slots.filter((s) => !!s);
43837
44048
  const canPlay = filledSlots.length > 0 && playState === "idle";
43838
44049
  const handlePlay = useCallback(() => {
43839
44050
  if (!canPlay) return;
43840
- setSlotFeedback(Array.from({ length: resolved?.maxSlots ?? 0 }, () => null));
44051
+ setSlotFeedback(Array.from({ length: maxSlots }, () => null));
43841
44052
  emit("UI:PLAY_SOUND", { key: "confirm" });
43842
44053
  const sequence = slots.map((s) => s?.id || "");
43843
44054
  if (playEvent) {
@@ -43848,10 +44059,10 @@ function SequencerBoard({
43848
44059
  let step = 0;
43849
44060
  const advance = () => {
43850
44061
  step++;
43851
- if (step >= (resolved?.maxSlots ?? 0)) {
44062
+ if (step >= maxSlots) {
43852
44063
  const playerSeq = slots.map((s) => s?.id);
43853
44064
  const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
43854
- const success = (resolved?.solutions ?? []).some(
44065
+ const success = solutions.some(
43855
44066
  (sol) => sol.length === playerIds.length && sol.every((id, i) => id === playerIds[i])
43856
44067
  );
43857
44068
  if (success) {
@@ -43863,7 +44074,7 @@ function SequencerBoard({
43863
44074
  }
43864
44075
  } else {
43865
44076
  setAttempts((prev) => prev + 1);
43866
- const feedback = computeSlotFeedback(playerSeq, resolved?.solutions ?? []);
44077
+ const feedback = computeSlotFeedback(playerSeq, solutions);
43867
44078
  setSlotFeedback(feedback);
43868
44079
  setPlayState("idle");
43869
44080
  setCurrentStep(-1);
@@ -43881,10 +44092,10 @@ function SequencerBoard({
43881
44092
  }
43882
44093
  };
43883
44094
  timerRef.current = setTimeout(advance, stepDurationMs);
43884
- }, [canPlay, slots, resolved?.maxSlots, resolved?.solutions, stepDurationMs, playEvent, completeEvent, emit]);
44095
+ }, [canPlay, slots, maxSlots, solutions, stepDurationMs, playEvent, completeEvent, emit]);
43885
44096
  const machine = {
43886
- name: resolved?.title ?? "",
43887
- description: resolved?.description ?? "",
44097
+ name: str(resolved?.title),
44098
+ description: str(resolved?.description),
43888
44099
  states: slots.map((s, i) => stepLabel(s, i)),
43889
44100
  currentState: currentStep >= 0 ? stepLabel(slots[currentStep], currentStep) : "__idle__",
43890
44101
  transitions: slots.slice(0, -1).map((s, i) => ({
@@ -43893,37 +44104,41 @@ function SequencerBoard({
43893
44104
  event: "NEXT"
43894
44105
  }))
43895
44106
  };
43896
- const usedIds = resolved?.allowDuplicates === false ? slots.filter(Boolean).map((s) => s?.id || "") : [];
43897
- const showHint = attempts >= 3 && !!resolved?.hint;
44107
+ const usedIds = !allowDuplicates ? slots.filter(Boolean).map((s) => s?.id || "") : [];
44108
+ const hint = str(resolved?.hint);
44109
+ const showHint = attempts >= 3 && !!hint;
43898
44110
  const hasFeedback = slotFeedback.some((f3) => f3 !== null);
43899
44111
  const correctCount = slotFeedback.filter((f3) => f3 === "correct").length;
43900
44112
  const encourageKey = ENCOURAGEMENT_KEYS2[Math.min(attempts - 1, ENCOURAGEMENT_KEYS2.length - 1)] ?? ENCOURAGEMENT_KEYS2[0];
43901
44113
  if (!resolved) return null;
44114
+ const theme = resolved.theme ?? void 0;
44115
+ const themeBackground = theme?.background;
44116
+ const headerImage = str(resolved.headerImage);
43902
44117
  return /* @__PURE__ */ jsxs(
43903
44118
  VStack,
43904
44119
  {
43905
44120
  className: cn("p-4 gap-6", className),
43906
44121
  style: {
43907
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
44122
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
43908
44123
  backgroundSize: "cover",
43909
44124
  backgroundPosition: "center"
43910
44125
  },
43911
44126
  children: [
43912
- 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,
44127
+ 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,
43913
44128
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
43914
- /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
43915
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description })
44129
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
44130
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) })
43916
44131
  ] }),
43917
44132
  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: [
43918
44133
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
43919
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
44134
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
43920
44135
  ] }) }),
43921
44136
  filledSlots.length > 0 && /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "linear", size: "md" }),
43922
44137
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
43923
44138
  /* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between", children: [
43924
44139
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("sequencer.yourSequence") + ":" }),
43925
44140
  hasFeedback && playState === "idle" && /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
43926
- `${correctCount}/${resolved.maxSlots} `,
44141
+ `${correctCount}/${maxSlots} `,
43927
44142
  "\u2705"
43928
44143
  ] })
43929
44144
  ] }),
@@ -43931,7 +44146,7 @@ function SequencerBoard({
43931
44146
  SequenceBar,
43932
44147
  {
43933
44148
  slots,
43934
- maxSlots: resolved.maxSlots,
44149
+ maxSlots,
43935
44150
  onSlotDrop: handleSlotDrop,
43936
44151
  onSlotRemove: handleSlotRemove,
43937
44152
  playing: playState === "playing",
@@ -43945,15 +44160,15 @@ function SequencerBoard({
43945
44160
  playState !== "playing" && /* @__PURE__ */ jsx(
43946
44161
  ActionPalette,
43947
44162
  {
43948
- actions: resolved.availableActions,
44163
+ actions: availableActions,
43949
44164
  usedActionIds: usedIds,
43950
- allowDuplicates: resolved.allowDuplicates !== false,
44165
+ allowDuplicates,
43951
44166
  categoryColors,
43952
44167
  label: t("sequencer.dragActions")
43953
44168
  }
43954
44169
  ),
43955
44170
  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) }) }),
43956
- 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") }) }),
44171
+ 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") }) }),
43957
44172
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
43958
44173
  /* @__PURE__ */ jsx(
43959
44174
  Button,
@@ -43977,6 +44192,7 @@ var init_SequencerBoard = __esm({
43977
44192
  init_cn();
43978
44193
  init_useEventBus();
43979
44194
  init_TraitStateViewer();
44195
+ init_boardEntity();
43980
44196
  init_SequenceBar();
43981
44197
  init_ActionPalette();
43982
44198
  ENCOURAGEMENT_KEYS2 = [
@@ -44026,18 +44242,21 @@ var init_ShowcaseOrganism = __esm({
44026
44242
  heading && /* @__PURE__ */ jsx(Typography, { variant: "h2", align: "center", children: heading }),
44027
44243
  subtitle && /* @__PURE__ */ jsx(Typography, { variant: "body1", color: "muted", align: "center", className: "max-w-2xl", children: subtitle })
44028
44244
  ] }),
44029
- /* @__PURE__ */ jsx(SimpleGrid, { cols: columns, gap: "lg", children: items.map((item) => /* @__PURE__ */ jsx(
44030
- ShowcaseCard,
44031
- {
44032
- title: item.title,
44033
- description: item.description,
44034
- image: item.image,
44035
- href: item.href,
44036
- badge: item.badge,
44037
- accentColor: item.accentColor
44038
- },
44039
- item.id
44040
- )) })
44245
+ /* @__PURE__ */ jsx(SimpleGrid, { cols: columns, gap: "lg", children: items.map((item) => {
44246
+ const imageRaw = item.image;
44247
+ return /* @__PURE__ */ jsx(
44248
+ ShowcaseCard,
44249
+ {
44250
+ title: String(item.title ?? ""),
44251
+ description: item.description != null ? String(item.description) : void 0,
44252
+ image: { src: String(imageRaw?.src ?? ""), alt: String(imageRaw?.alt ?? "") },
44253
+ href: item.href != null ? String(item.href) : void 0,
44254
+ badge: item.badge != null ? String(item.badge) : void 0,
44255
+ accentColor: item.accentColor != null ? String(item.accentColor) : void 0
44256
+ },
44257
+ String(item.id ?? "")
44258
+ );
44259
+ }) })
44041
44260
  ] });
44042
44261
  };
44043
44262
  ShowcaseOrganism.displayName = "ShowcaseOrganism";
@@ -44405,8 +44624,8 @@ function SimulatorBoard({
44405
44624
  }) {
44406
44625
  const { emit } = useEventBus();
44407
44626
  const { t } = useTranslate();
44408
- const resolved = Array.isArray(entity) ? entity[0] : entity;
44409
- const parameters = resolved?.parameters ?? [];
44627
+ const resolved = boardEntity(entity);
44628
+ const parameters = Array.isArray(resolved?.parameters) ? resolved.parameters : [];
44410
44629
  const [values, setValues] = useState(() => {
44411
44630
  const init = {};
44412
44631
  for (const p2 of parameters) {
@@ -44420,15 +44639,15 @@ function SimulatorBoard({
44420
44639
  const [showHint, setShowHint] = useState(false);
44421
44640
  const computeOutput = useCallback((params) => {
44422
44641
  try {
44423
- const fn = new Function("params", `return (${resolved?.computeExpression})`);
44642
+ const fn = new Function("params", `return (${str(resolved?.computeExpression)})`);
44424
44643
  return fn(params);
44425
44644
  } catch {
44426
44645
  return 0;
44427
44646
  }
44428
44647
  }, [resolved?.computeExpression]);
44429
44648
  const output = useMemo(() => computeOutput(values) ?? 0, [computeOutput, values]);
44430
- const targetValue = resolved?.targetValue ?? 0;
44431
- const targetTolerance = resolved?.targetTolerance ?? 0;
44649
+ const targetValue = num(resolved?.targetValue);
44650
+ const targetTolerance = num(resolved?.targetTolerance);
44432
44651
  const isCorrect = Math.abs(output - targetValue) <= targetTolerance;
44433
44652
  const handleParameterChange = (id, value) => {
44434
44653
  if (submitted) return;
@@ -44443,7 +44662,7 @@ function SimulatorBoard({
44443
44662
  };
44444
44663
  const handleReset = () => {
44445
44664
  setSubmitted(false);
44446
- if (attempts >= 2 && resolved?.hint) {
44665
+ if (attempts >= 2 && str(resolved?.hint)) {
44447
44666
  setShowHint(true);
44448
44667
  }
44449
44668
  };
@@ -44458,20 +44677,26 @@ function SimulatorBoard({
44458
44677
  setShowHint(false);
44459
44678
  };
44460
44679
  if (!resolved) return null;
44680
+ const theme = resolved.theme ?? void 0;
44681
+ const themeBackground = theme?.background;
44682
+ const headerImage = str(resolved.headerImage);
44683
+ const hint = str(resolved.hint);
44684
+ const outputLabel = str(resolved.outputLabel);
44685
+ const outputUnit = str(resolved.outputUnit);
44461
44686
  return /* @__PURE__ */ jsx(
44462
44687
  Box,
44463
44688
  {
44464
44689
  className,
44465
44690
  style: {
44466
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
44691
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
44467
44692
  backgroundSize: "cover",
44468
44693
  backgroundPosition: "center"
44469
44694
  },
44470
44695
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
44471
- 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,
44696
+ 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,
44472
44697
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
44473
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
44474
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description })
44698
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
44699
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) })
44475
44700
  ] }) }),
44476
44701
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", children: [
44477
44702
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("simulator.parameters") }),
@@ -44512,28 +44737,28 @@ function SimulatorBoard({
44512
44737
  ] }, param.id))
44513
44738
  ] }) }),
44514
44739
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
44515
- /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: resolved.outputLabel }),
44740
+ /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: outputLabel }),
44516
44741
  /* @__PURE__ */ jsxs(Typography, { variant: "h3", weight: "bold", children: [
44517
44742
  output.toFixed(2),
44518
44743
  " ",
44519
- resolved.outputUnit
44744
+ outputUnit
44520
44745
  ] }),
44521
44746
  submitted && /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
44522
44747
  /* @__PURE__ */ jsx(Icon, { icon: isCorrect ? CheckCircle : XCircle, size: "sm", className: isCorrect ? "text-success" : "text-error" }),
44523
- /* @__PURE__ */ jsx(Typography, { variant: "body", className: isCorrect ? "text-success" : "text-error", children: isCorrect ? resolved.successMessage ?? t("simulator.correct") : resolved.failMessage ?? t("simulator.incorrect") })
44748
+ /* @__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") })
44524
44749
  ] }),
44525
44750
  /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
44526
44751
  t("simulator.target"),
44527
44752
  ": ",
44528
44753
  targetValue,
44529
44754
  " ",
44530
- resolved.outputUnit,
44755
+ outputUnit,
44531
44756
  " (\xB1",
44532
44757
  targetTolerance,
44533
44758
  ")"
44534
44759
  ] })
44535
44760
  ] }) }),
44536
- showHint && resolved.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
44761
+ showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
44537
44762
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
44538
44763
  !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, children: [
44539
44764
  /* @__PURE__ */ jsx(Icon, { icon: Play, size: "sm" }),
@@ -44552,6 +44777,7 @@ var init_SimulatorBoard = __esm({
44552
44777
  "components/game/organisms/puzzles/simulator/SimulatorBoard.tsx"() {
44553
44778
  init_atoms2();
44554
44779
  init_useEventBus();
44780
+ init_boardEntity();
44555
44781
  SimulatorBoard.displayName = "SimulatorBoard";
44556
44782
  }
44557
44783
  });
@@ -44977,22 +45203,25 @@ function VariablePanel({
44977
45203
  return /* @__PURE__ */ jsxs(VStack, { className: cn("p-3 rounded-lg bg-card border border-border", className), gap: "sm", children: [
44978
45204
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("stateArchitect.variables", { name: entityName }) }),
44979
45205
  variables.map((v) => {
44980
- const max = v.max ?? 100;
44981
- const min = v.min ?? 0;
44982
- const pct = Math.round((v.value - min) / (max - min) * 100);
45206
+ const name = v.name == null ? "" : String(v.name);
45207
+ const value = numField(v.value);
45208
+ const max = numField(v.max, 100);
45209
+ const min = numField(v.min, 0);
45210
+ const unit = v.unit == null ? "" : String(v.unit);
45211
+ const pct = Math.round((value - min) / (max - min) * 100);
44983
45212
  const isHigh = pct > 80;
44984
45213
  const isLow = pct < 20;
44985
45214
  return /* @__PURE__ */ jsxs(VStack, { gap: "none", children: [
44986
45215
  /* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between", children: [
44987
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground font-medium", children: v.name }),
45216
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground font-medium", children: name }),
44988
45217
  /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: cn(
44989
45218
  isHigh ? "text-error" : isLow ? "text-warning" : "text-foreground"
44990
45219
  ), children: [
44991
- v.value,
44992
- v.unit || "",
45220
+ value,
45221
+ unit,
44993
45222
  " / ",
44994
45223
  max,
44995
- v.unit || ""
45224
+ unit
44996
45225
  ] })
44997
45226
  ] }),
44998
45227
  /* @__PURE__ */ jsx(
@@ -45003,14 +45232,19 @@ function VariablePanel({
45003
45232
  size: "sm"
45004
45233
  }
45005
45234
  )
45006
- ] }, v.name);
45235
+ ] }, name);
45007
45236
  })
45008
45237
  ] });
45009
45238
  }
45239
+ var numField;
45010
45240
  var init_VariablePanel = __esm({
45011
45241
  "components/game/organisms/puzzles/state-architect/VariablePanel.tsx"() {
45012
45242
  init_atoms2();
45013
45243
  init_cn();
45244
+ numField = (v, fallback = 0) => {
45245
+ const n = Number(v);
45246
+ return Number.isFinite(n) ? n : fallback;
45247
+ };
45014
45248
  VariablePanel.displayName = "VariablePanel";
45015
45249
  }
45016
45250
  });
@@ -45037,14 +45271,21 @@ function StateArchitectBoard({
45037
45271
  }) {
45038
45272
  const { emit } = useEventBus();
45039
45273
  const { t } = useTranslate();
45040
- const resolved = Array.isArray(entity) ? entity[0] : entity;
45041
- const [transitions, setTransitions] = useState(resolved?.transitions ?? []);
45274
+ const resolved = boardEntity(entity);
45275
+ const entityStates = Array.isArray(resolved?.states) ? resolved.states : [];
45276
+ const initialState = str(resolved?.initialState);
45277
+ const entityName = str(resolved?.entityName);
45278
+ const availableEvents = Array.isArray(resolved?.availableEvents) ? resolved.availableEvents : [];
45279
+ const testCases = Array.isArray(resolved?.testCases) ? resolved.testCases : [];
45280
+ const entityTransitions = Array.isArray(resolved?.transitions) ? resolved.transitions : [];
45281
+ const entityVariables = rows(resolved?.variables);
45282
+ const [transitions, setTransitions] = useState(entityTransitions);
45042
45283
  const [headerError, setHeaderError] = useState(false);
45043
45284
  const [playState, setPlayState] = useState("editing");
45044
- const [currentState, setCurrentState] = useState(resolved?.initialState ?? "");
45285
+ const [currentState, setCurrentState] = useState(initialState);
45045
45286
  const [selectedState, setSelectedState] = useState(null);
45046
45287
  const [testResults, setTestResults] = useState([]);
45047
- const [variables, setVariables] = useState(resolved?.variables ?? []);
45288
+ const [variables, setVariables] = useState(() => [...entityVariables]);
45048
45289
  const [attempts, setAttempts] = useState(0);
45049
45290
  const timerRef = useRef(null);
45050
45291
  const [addingFrom, setAddingFrom] = useState(null);
@@ -45053,12 +45294,12 @@ function StateArchitectBoard({
45053
45294
  }, []);
45054
45295
  const GRAPH_W = 500;
45055
45296
  const GRAPH_H = 400;
45056
- const positions = useMemo(() => layoutStates(resolved?.states ?? [], GRAPH_W, GRAPH_H), [resolved?.states]);
45297
+ const positions = useMemo(() => layoutStates(entityStates, GRAPH_W, GRAPH_H), [entityStates]);
45057
45298
  const handleStateClick = useCallback((state) => {
45058
45299
  if (playState !== "editing") return;
45059
45300
  if (addingFrom) {
45060
45301
  if (addingFrom !== state) {
45061
- const event = resolved?.availableEvents[0] || "EVENT";
45302
+ const event = availableEvents[0] || "EVENT";
45062
45303
  const newTrans = {
45063
45304
  id: `t-${nextTransId++}`,
45064
45305
  from: addingFrom,
@@ -45071,7 +45312,7 @@ function StateArchitectBoard({
45071
45312
  } else {
45072
45313
  setSelectedState(state);
45073
45314
  }
45074
- }, [playState, addingFrom, resolved?.availableEvents]);
45315
+ }, [playState, addingFrom, availableEvents]);
45075
45316
  const handleStartAddTransition = useCallback(() => {
45076
45317
  if (!selectedState) return;
45077
45318
  setAddingFrom(selectedState);
@@ -45080,9 +45321,9 @@ function StateArchitectBoard({
45080
45321
  setTransitions((prev) => prev.filter((t2) => t2.id !== transId));
45081
45322
  }, []);
45082
45323
  const machine = useMemo(() => ({
45083
- name: resolved?.entityName ?? "",
45084
- description: resolved?.description ?? "",
45085
- states: resolved?.states ?? [],
45324
+ name: entityName,
45325
+ description: str(resolved?.description),
45326
+ states: entityStates,
45086
45327
  currentState,
45087
45328
  transitions: transitions.map((t2) => ({
45088
45329
  from: t2.from,
@@ -45090,7 +45331,7 @@ function StateArchitectBoard({
45090
45331
  event: t2.event,
45091
45332
  guardHint: t2.guardHint
45092
45333
  }))
45093
- }), [resolved, currentState, transitions]);
45334
+ }), [entityName, resolved, entityStates, currentState, transitions]);
45094
45335
  const handleTest = useCallback(() => {
45095
45336
  if (playState !== "editing") return;
45096
45337
  if (testEvent) emit(`UI:${testEvent}`, {});
@@ -45099,7 +45340,7 @@ function StateArchitectBoard({
45099
45340
  const results = [];
45100
45341
  let testIdx = 0;
45101
45342
  const runNextTest = () => {
45102
- if (testIdx >= (resolved?.testCases.length ?? 0)) {
45343
+ if (testIdx >= testCases.length) {
45103
45344
  const allPassed = results.every((r) => r.passed);
45104
45345
  setPlayState(allPassed ? "success" : "fail");
45105
45346
  setTestResults(results);
@@ -45114,9 +45355,9 @@ function StateArchitectBoard({
45114
45355
  }
45115
45356
  return;
45116
45357
  }
45117
- const testCase = resolved?.testCases[testIdx];
45358
+ const testCase = testCases[testIdx];
45118
45359
  if (!testCase) return;
45119
- let state = resolved.initialState;
45360
+ let state = initialState;
45120
45361
  for (const event of testCase.events) {
45121
45362
  const trans = transitions.find((t2) => t2.from === state && t2.event === event);
45122
45363
  if (trans) {
@@ -45134,53 +45375,57 @@ function StateArchitectBoard({
45134
45375
  timerRef.current = setTimeout(runNextTest, stepDurationMs);
45135
45376
  };
45136
45377
  timerRef.current = setTimeout(runNextTest, stepDurationMs);
45137
- }, [playState, transitions, resolved, stepDurationMs, testEvent, completeEvent, emit]);
45378
+ }, [playState, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
45138
45379
  const handleTryAgain = useCallback(() => {
45139
45380
  if (timerRef.current) clearTimeout(timerRef.current);
45140
45381
  setPlayState("editing");
45141
- setCurrentState(resolved?.initialState ?? "");
45382
+ setCurrentState(initialState);
45142
45383
  setTestResults([]);
45143
- }, [resolved?.initialState]);
45384
+ }, [initialState]);
45144
45385
  const handleReset = useCallback(() => {
45145
45386
  if (timerRef.current) clearTimeout(timerRef.current);
45146
- setTransitions(resolved?.transitions ?? []);
45387
+ setTransitions(entityTransitions);
45147
45388
  setPlayState("editing");
45148
- setCurrentState(resolved?.initialState ?? "");
45389
+ setCurrentState(initialState);
45149
45390
  setTestResults([]);
45150
- setVariables(resolved?.variables ?? []);
45391
+ setVariables([...entityVariables]);
45151
45392
  setSelectedState(null);
45152
45393
  setAddingFrom(null);
45153
45394
  setAttempts(0);
45154
- }, [resolved]);
45395
+ }, [entityTransitions, initialState, entityVariables]);
45155
45396
  const codeData = useMemo(() => ({
45156
- name: resolved?.entityName ?? "",
45157
- states: resolved?.states ?? [],
45158
- initialState: resolved?.initialState ?? "",
45397
+ name: entityName,
45398
+ states: entityStates,
45399
+ initialState,
45159
45400
  transitions: transitions.map((t2) => ({
45160
45401
  from: t2.from,
45161
45402
  to: t2.to,
45162
45403
  event: t2.event,
45163
45404
  ...t2.guardHint ? { guard: t2.guardHint } : {}
45164
45405
  }))
45165
- }), [resolved, transitions]);
45406
+ }), [entityName, entityStates, initialState, transitions]);
45166
45407
  if (!resolved) return null;
45408
+ const theme = resolved.theme ?? void 0;
45409
+ const themeBackground = theme?.background;
45410
+ const headerImage = str(resolved.headerImage);
45411
+ const hint = str(resolved.hint);
45167
45412
  return /* @__PURE__ */ jsxs(
45168
45413
  VStack,
45169
45414
  {
45170
45415
  className: cn("p-4 gap-6", className),
45171
45416
  style: {
45172
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
45417
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
45173
45418
  backgroundSize: "cover",
45174
45419
  backgroundPosition: "center"
45175
45420
  },
45176
45421
  children: [
45177
- 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,
45422
+ 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,
45178
45423
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
45179
- /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
45180
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description }),
45424
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
45425
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) }),
45181
45426
  /* @__PURE__ */ jsxs(HStack, { className: "items-center p-2 rounded bg-warning/10 border border-warning/30", gap: "xs", children: [
45182
45427
  /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-warning font-bold", children: t("game.hint") + ":" }),
45183
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: resolved.hint })
45428
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: hint })
45184
45429
  ] })
45185
45430
  ] }),
45186
45431
  /* @__PURE__ */ jsxs(HStack, { className: "flex-wrap items-start", gap: "lg", children: [
@@ -45228,14 +45473,14 @@ function StateArchitectBoard({
45228
45473
  ]
45229
45474
  }
45230
45475
  ),
45231
- resolved.states.map((state) => /* @__PURE__ */ jsx(
45476
+ entityStates.map((state) => /* @__PURE__ */ jsx(
45232
45477
  StateNode2,
45233
45478
  {
45234
45479
  name: state,
45235
45480
  position: positions[state],
45236
45481
  isCurrent: state === currentState,
45237
45482
  isSelected: state === selectedState,
45238
- isInitial: state === resolved.initialState,
45483
+ isInitial: state === initialState,
45239
45484
  onClick: () => handleStateClick(state)
45240
45485
  },
45241
45486
  state
@@ -45282,7 +45527,7 @@ function StateArchitectBoard({
45282
45527
  /* @__PURE__ */ jsx(
45283
45528
  VariablePanel,
45284
45529
  {
45285
- entityName: resolved.entityName,
45530
+ entityName,
45286
45531
  variables
45287
45532
  }
45288
45533
  ),
@@ -45297,12 +45542,12 @@ function StateArchitectBoard({
45297
45542
  resolved.showCodeView !== false && /* @__PURE__ */ jsx(CodeView, { data: codeData, label: "View Code" })
45298
45543
  ] })
45299
45544
  ] }),
45300
- 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") }) }),
45545
+ 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") }) }),
45301
45546
  playState === "fail" && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
45302
45547
  /* @__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]) }) }),
45303
- 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: [
45548
+ 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: [
45304
45549
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
45305
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
45550
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
45306
45551
  ] }) })
45307
45552
  ] }),
45308
45553
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
@@ -45332,6 +45577,7 @@ var init_StateArchitectBoard = __esm({
45332
45577
  init_TransitionArrow();
45333
45578
  init_VariablePanel();
45334
45579
  init_CodeView();
45580
+ init_boardEntity();
45335
45581
  ENCOURAGEMENT_KEYS3 = [
45336
45582
  "puzzle.tryAgain1",
45337
45583
  "puzzle.tryAgain2",
@@ -45368,8 +45614,8 @@ var init_StatsOrganism = __esm({
45368
45614
  return /* @__PURE__ */ jsx(ErrorState, { message: error.message, className });
45369
45615
  }
45370
45616
  const stats = items.map((item) => ({
45371
- value: item.value,
45372
- label: item.label
45617
+ value: String(item.value ?? ""),
45618
+ label: String(item.label ?? "")
45373
45619
  }));
45374
45620
  return /* @__PURE__ */ jsx(
45375
45621
  StatsGrid,
@@ -45415,10 +45661,10 @@ var init_StepFlowOrganism = __esm({
45415
45661
  return /* @__PURE__ */ jsx(ErrorState, { message: error.message, className });
45416
45662
  }
45417
45663
  const steps = items.map((item) => ({
45418
- number: item.number,
45419
- title: item.title,
45420
- description: item.description,
45421
- icon: item.icon
45664
+ number: item.number != null ? Number(item.number) : void 0,
45665
+ title: String(item.title ?? ""),
45666
+ description: String(item.description ?? ""),
45667
+ icon: item.icon != null ? String(item.icon) : void 0
45422
45668
  }));
45423
45669
  return /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
45424
45670
  (heading || subtitle) && /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
@@ -45591,13 +45837,13 @@ var init_TeamOrganism = __esm({
45591
45837
  /* @__PURE__ */ jsx(SimpleGrid, { cols: cols > 0 ? cols : 1, gap: "lg", children: items.map((member) => /* @__PURE__ */ jsx(
45592
45838
  TeamCard,
45593
45839
  {
45594
- name: member.name,
45595
- nameAr: member.nameAr,
45596
- role: member.role,
45597
- bio: member.bio,
45598
- avatar: member.avatar
45840
+ name: String(member.name ?? ""),
45841
+ nameAr: member.nameAr != null ? String(member.nameAr) : void 0,
45842
+ role: String(member.role ?? ""),
45843
+ bio: String(member.bio ?? ""),
45844
+ avatar: member.avatar != null ? String(member.avatar) : void 0
45599
45845
  },
45600
- member.id
45846
+ String(member.id ?? "")
45601
45847
  )) })
45602
45848
  ] });
45603
45849
  };
@@ -45834,8 +46080,8 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
45834
46080
  const [turn, setTurn] = useState(1);
45835
46081
  const [gameResult, setGameResult] = useState(null);
45836
46082
  const checkGameEnd = useCallback((currentUnits) => {
45837
- const pa = currentUnits.filter((u) => u.team === "player" && u.health > 0);
45838
- const ea = currentUnits.filter((u) => u.team === "enemy" && u.health > 0);
46083
+ const pa = currentUnits.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
46084
+ const ea = currentUnits.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
45839
46085
  if (pa.length === 0) {
45840
46086
  setGameResult("defeat");
45841
46087
  setPhase("game_over");
@@ -45853,34 +46099,36 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
45853
46099
  }
45854
46100
  }, [onGameEnd, gameEndEvent, eventBus]);
45855
46101
  const handleUnitClick = useCallback((unitId) => {
45856
- const unit = units.find((u) => u.id === unitId);
46102
+ const unit = units.find((u) => str(u.id) === unitId);
45857
46103
  if (!unit) return;
45858
46104
  if (unitClickEvent) {
45859
46105
  eventBus.emit(`UI:${unitClickEvent}`, { unitId });
45860
46106
  }
45861
46107
  if (phase === "observation" || phase === "selection") {
45862
- if (unit.team === "player") {
46108
+ if (unitTeam(unit) === "player") {
45863
46109
  setSelectedUnitId(unitId);
45864
46110
  setPhase("movement");
45865
46111
  }
45866
46112
  } else if (phase === "action") {
45867
- const selectedUnit = units.find((u) => u.id === selectedUnitId);
46113
+ const selectedUnit = units.find((u) => str(u.id) === selectedUnitId);
45868
46114
  if (!selectedUnit) return;
45869
- if (unit.team === "enemy") {
45870
- const dx = Math.abs(unit.position.x - selectedUnit.position.x);
45871
- const dy = Math.abs(unit.position.y - selectedUnit.position.y);
46115
+ if (unitTeam(unit) === "enemy") {
46116
+ const up = unitPosition(unit);
46117
+ const sp = unitPosition(selectedUnit);
46118
+ const dx = Math.abs(up.x - sp.x);
46119
+ const dy = Math.abs(up.y - sp.y);
45872
46120
  if (dx <= 1 && dy <= 1 && dx + dy > 0) {
45873
- const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, selectedUnit.attack - unit.defense);
45874
- const newHealth = Math.max(0, unit.health - damage);
46121
+ const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, num(selectedUnit.attack) - num(unit.defense));
46122
+ const newHealth = Math.max(0, unitHealth(unit) - damage);
45875
46123
  const updatedUnits = units.map(
45876
- (u) => u.id === unit.id ? { ...u, health: newHealth } : u
46124
+ (u) => str(u.id) === str(unit.id) ? { ...u, health: newHealth } : u
45877
46125
  );
45878
46126
  setUnits(updatedUnits);
45879
46127
  onAttack?.(selectedUnit, unit, damage);
45880
46128
  if (attackEvent) {
45881
46129
  eventBus.emit(`UI:${attackEvent}`, {
45882
- attackerId: selectedUnit.id,
45883
- targetId: unit.id,
46130
+ attackerId: str(selectedUnit.id),
46131
+ targetId: str(unit.id),
45884
46132
  damage
45885
46133
  });
45886
46134
  }
@@ -45897,16 +46145,20 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
45897
46145
  eventBus.emit(`UI:${tileClickEvent}`, { x, y });
45898
46146
  }
45899
46147
  if (phase === "movement" && selectedUnitId) {
45900
- const selectedUnit = units.find((u) => u.id === selectedUnitId);
46148
+ const selectedUnit = units.find((u) => str(u.id) === selectedUnitId);
45901
46149
  if (!selectedUnit) return;
45902
- const dx = Math.abs(x - selectedUnit.position.x);
45903
- const dy = Math.abs(y - selectedUnit.position.y);
46150
+ const sp = unitPosition(selectedUnit);
46151
+ const dx = Math.abs(x - sp.x);
46152
+ const dy = Math.abs(y - sp.y);
45904
46153
  const dist = dx + dy;
45905
- if (dist > 0 && dist <= selectedUnit.movement) {
45906
- if (!units.some((u) => u.position.x === x && u.position.y === y && u.health > 0)) {
46154
+ if (dist > 0 && dist <= num(selectedUnit.movement)) {
46155
+ if (!units.some((u) => {
46156
+ const p2 = unitPosition(u);
46157
+ return p2.x === x && p2.y === y && unitHealth(u) > 0;
46158
+ })) {
45907
46159
  setUnits(
45908
46160
  (prev) => prev.map(
45909
- (u) => u.id === selectedUnitId ? { ...u, position: { x, y } } : u
46161
+ (u) => str(u.id) === selectedUnitId ? { ...u, position: { x, y } } : u
45910
46162
  )
45911
46163
  );
45912
46164
  setPhase("action");
@@ -45949,12 +46201,13 @@ var init_useBattleState = __esm({
45949
46201
  "components/game/organisms/hooks/useBattleState.ts"() {
45950
46202
  "use client";
45951
46203
  init_useEventBus();
46204
+ init_boardEntity();
45952
46205
  }
45953
46206
  });
45954
46207
  function UncontrolledBattleBoard({ entity, ...rest }) {
45955
- const resolved = Array.isArray(entity) ? entity[0] : entity;
46208
+ const resolved = boardEntity(entity);
45956
46209
  const battleState = useBattleState(
45957
- resolved?.initialUnits ?? [],
46210
+ rows(resolved?.initialUnits),
45958
46211
  {
45959
46212
  tileClickEvent: rest.tileClickEvent,
45960
46213
  unitClickEvent: rest.unitClickEvent,
@@ -45990,10 +46243,23 @@ function UncontrolledBattleBoard({ entity, ...rest }) {
45990
46243
  var init_UncontrolledBattleBoard = __esm({
45991
46244
  "components/game/organisms/UncontrolledBattleBoard.tsx"() {
45992
46245
  init_BattleBoard();
46246
+ init_boardEntity();
45993
46247
  init_useBattleState();
45994
46248
  UncontrolledBattleBoard.displayName = "UncontrolledBattleBoard";
45995
46249
  }
45996
46250
  });
46251
+ function heroPosition(h) {
46252
+ return vec2(h.position);
46253
+ }
46254
+ function heroOwner(h) {
46255
+ return str(h.owner);
46256
+ }
46257
+ function heroMovement(h) {
46258
+ return num(h.movement);
46259
+ }
46260
+ function hexPassable(h) {
46261
+ return h.passable !== false;
46262
+ }
45997
46263
  function defaultIsInRange(from, to, range) {
45998
46264
  return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
45999
46265
  }
@@ -46024,36 +46290,36 @@ function WorldMapBoard({
46024
46290
  className
46025
46291
  }) {
46026
46292
  const eventBus = useEventBus();
46027
- const resolved = Array.isArray(entity) ? entity[0] : entity;
46028
- const hexes = resolved?.hexes ?? [];
46029
- const heroes = resolved?.heroes ?? [];
46030
- const features = resolved?.features ?? [];
46031
- const selectedHeroId = resolved?.selectedHeroId;
46293
+ const resolved = boardEntity(entity);
46294
+ const hexes = rows(resolved?.hexes);
46295
+ const heroes = rows(resolved?.heroes);
46296
+ const features = Array.isArray(resolved?.features) ? resolved.features : [];
46297
+ const selectedHeroId = resolved?.selectedHeroId ?? null;
46032
46298
  const assetManifest = resolved?.assetManifest;
46033
46299
  const backgroundImage = resolved?.backgroundImage;
46034
46300
  const [hoveredTile, setHoveredTile] = useState(null);
46035
46301
  const selectedHero = useMemo(
46036
- () => heroes.find((h) => h.id === selectedHeroId) ?? null,
46302
+ () => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
46037
46303
  [heroes, selectedHeroId]
46038
46304
  );
46039
46305
  const tiles = useMemo(
46040
46306
  () => hexes.map((hex) => ({
46041
- x: hex.x,
46042
- y: hex.y,
46043
- terrain: hex.terrain,
46044
- terrainSprite: hex.terrainSprite
46307
+ x: num(hex.x),
46308
+ y: num(hex.y),
46309
+ terrain: str(hex.terrain),
46310
+ terrainSprite: hex.terrainSprite == null ? void 0 : str(hex.terrainSprite)
46045
46311
  })),
46046
46312
  [hexes]
46047
46313
  );
46048
46314
  const baseUnits = useMemo(
46049
46315
  () => heroes.map((hero) => ({
46050
- id: hero.id,
46051
- position: hero.position,
46052
- name: hero.name,
46053
- team: hero.owner === "enemy" ? "enemy" : "player",
46316
+ id: str(hero.id),
46317
+ position: heroPosition(hero),
46318
+ name: str(hero.name),
46319
+ team: heroOwner(hero) === "enemy" ? "enemy" : "player",
46054
46320
  health: 100,
46055
46321
  maxHealth: 100,
46056
- sprite: hero.sprite
46322
+ sprite: hero.sprite == null ? void 0 : str(hero.sprite)
46057
46323
  })),
46058
46324
  [heroes]
46059
46325
  );
@@ -46094,73 +46360,94 @@ function WorldMapBoard({
46094
46360
  const isoUnits = useMemo(() => {
46095
46361
  if (movingPositions.size === 0) return baseUnits;
46096
46362
  return baseUnits.map((u) => {
46097
- const pos = movingPositions.get(u.id);
46363
+ const pos = u.id == null ? void 0 : movingPositions.get(u.id);
46098
46364
  return pos ? { ...u, position: pos } : u;
46099
46365
  });
46100
46366
  }, [baseUnits, movingPositions]);
46101
46367
  const validMoves = useMemo(() => {
46102
- if (!selectedHero || selectedHero.movement <= 0) return [];
46368
+ if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
46369
+ const sp = heroPosition(selectedHero);
46370
+ const sOwner = heroOwner(selectedHero);
46371
+ const range = heroMovement(selectedHero);
46103
46372
  const moves = [];
46104
46373
  hexes.forEach((hex) => {
46105
- if (hex.passable === false) return;
46106
- if (hex.x === selectedHero.position.x && hex.y === selectedHero.position.y) return;
46107
- if (!isInRange(selectedHero.position, { x: hex.x, y: hex.y }, selectedHero.movement)) return;
46108
- if (heroes.some((h) => h.position.x === hex.x && h.position.y === hex.y && h.owner === selectedHero.owner)) return;
46109
- moves.push({ x: hex.x, y: hex.y });
46374
+ const hx = num(hex.x);
46375
+ const hy = num(hex.y);
46376
+ if (!hexPassable(hex)) return;
46377
+ if (hx === sp.x && hy === sp.y) return;
46378
+ if (!isInRange(sp, { x: hx, y: hy }, range)) return;
46379
+ if (heroes.some((h) => {
46380
+ const hp = heroPosition(h);
46381
+ return hp.x === hx && hp.y === hy && heroOwner(h) === sOwner;
46382
+ })) return;
46383
+ moves.push({ x: hx, y: hy });
46110
46384
  });
46111
46385
  return moves;
46112
46386
  }, [selectedHero, hexes, heroes, isInRange]);
46113
46387
  const attackTargets = useMemo(() => {
46114
- if (!selectedHero || selectedHero.movement <= 0) return [];
46115
- return heroes.filter((h) => h.owner !== selectedHero.owner).filter((h) => isInRange(selectedHero.position, h.position, selectedHero.movement)).map((h) => h.position);
46388
+ if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
46389
+ const sp = heroPosition(selectedHero);
46390
+ const sOwner = heroOwner(selectedHero);
46391
+ const range = heroMovement(selectedHero);
46392
+ return heroes.filter((h) => heroOwner(h) !== sOwner).filter((h) => isInRange(sp, heroPosition(h), range)).map((h) => heroPosition(h));
46116
46393
  }, [selectedHero, heroes, isInRange]);
46117
- const maxY = Math.max(...hexes.map((h) => h.y), 0);
46394
+ const maxY = Math.max(...hexes.map((h) => num(h.y)), 0);
46118
46395
  const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
46119
46396
  const tileToScreen = useCallback(
46120
46397
  (tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
46121
46398
  [scale, baseOffsetX]
46122
46399
  );
46123
46400
  const hoveredHex = useMemo(
46124
- () => hoveredTile ? hexes.find((h) => h.x === hoveredTile.x && h.y === hoveredTile.y) ?? null : null,
46401
+ () => hoveredTile ? hexes.find((h) => num(h.x) === hoveredTile.x && num(h.y) === hoveredTile.y) ?? null : null,
46125
46402
  [hoveredTile, hexes]
46126
46403
  );
46127
46404
  const hoveredHero = useMemo(
46128
- () => hoveredTile ? heroes.find((h) => h.position.x === hoveredTile.x && h.position.y === hoveredTile.y) ?? null : null,
46405
+ () => hoveredTile ? heroes.find((h) => {
46406
+ const hp = heroPosition(h);
46407
+ return hp.x === hoveredTile.x && hp.y === hoveredTile.y;
46408
+ }) ?? null : null,
46129
46409
  [hoveredTile, heroes]
46130
46410
  );
46131
46411
  const handleTileClick = useCallback((x, y) => {
46132
46412
  if (movementAnimRef.current) return;
46133
- const hex = hexes.find((h) => h.x === x && h.y === y);
46413
+ const hex = hexes.find((h) => num(h.x) === x && num(h.y) === y);
46134
46414
  if (!hex) return;
46135
46415
  if (tileClickEvent) {
46136
46416
  eventBus.emit(`UI:${tileClickEvent}`, { x, y });
46137
46417
  }
46138
46418
  if (selectedHero && validMoves.some((m) => m.x === x && m.y === y)) {
46139
- startMoveAnimation(selectedHero.id, { ...selectedHero.position }, { x, y }, () => {
46140
- onHeroMove?.(selectedHero.id, x, y);
46419
+ const heroId = str(selectedHero.id);
46420
+ startMoveAnimation(heroId, { ...heroPosition(selectedHero) }, { x, y }, () => {
46421
+ onHeroMove?.(heroId, x, y);
46141
46422
  if (heroMoveEvent) {
46142
- eventBus.emit(`UI:${heroMoveEvent}`, { heroId: selectedHero.id, toX: x, toY: y });
46423
+ eventBus.emit(`UI:${heroMoveEvent}`, { heroId, toX: x, toY: y });
46143
46424
  }
46144
- if (hex.feature && hex.feature !== "none") {
46145
- onFeatureEnter?.(selectedHero.id, hex);
46425
+ const feature = str(hex.feature);
46426
+ if (feature && feature !== "none") {
46427
+ onFeatureEnter?.(heroId, hex);
46146
46428
  if (featureEnterEvent) {
46147
- eventBus.emit(`UI:${featureEnterEvent}`, { heroId: selectedHero.id, feature: hex.feature, hex });
46429
+ eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex });
46148
46430
  }
46149
46431
  }
46150
46432
  });
46151
46433
  return;
46152
46434
  }
46153
- const enemy = heroes.find((h) => h.position.x === x && h.position.y === y && h.owner === "enemy");
46435
+ const enemy = heroes.find((h) => {
46436
+ const hp = heroPosition(h);
46437
+ return hp.x === x && hp.y === y && heroOwner(h) === "enemy";
46438
+ });
46154
46439
  if (selectedHero && enemy && attackTargets.some((t) => t.x === x && t.y === y)) {
46155
- onBattleEncounter?.(selectedHero.id, enemy.id);
46440
+ const attackerId = str(selectedHero.id);
46441
+ const defenderId = str(enemy.id);
46442
+ onBattleEncounter?.(attackerId, defenderId);
46156
46443
  if (battleEncounterEvent) {
46157
- eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId: selectedHero.id, defenderId: enemy.id });
46444
+ eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId, defenderId });
46158
46445
  }
46159
46446
  }
46160
46447
  }, [hexes, heroes, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
46161
46448
  const handleUnitClick = useCallback((unitId) => {
46162
- const hero = heroes.find((h) => h.id === unitId);
46163
- if (hero && (hero.owner === "player" || allowMoveAllHeroes)) {
46449
+ const hero = heroes.find((h) => str(h.id) === unitId);
46450
+ if (hero && (heroOwner(hero) === "player" || allowMoveAllHeroes)) {
46164
46451
  onHeroSelect?.(unitId);
46165
46452
  if (heroSelectEvent) {
46166
46453
  eventBus.emit(`UI:${heroSelectEvent}`, { heroId: unitId });
@@ -46233,6 +46520,7 @@ var init_WorldMapBoard = __esm({
46233
46520
  init_Stack();
46234
46521
  init_LoadingState();
46235
46522
  init_IsometricCanvas2();
46523
+ init_boardEntity();
46236
46524
  init_isometric();
46237
46525
  WorldMapBoard.displayName = "WorldMapBoard";
46238
46526
  }