@almadar/ui 5.21.12 → 5.22.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/dist/avl/index.cjs +919 -631
  2. package/dist/avl/index.js +919 -631
  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
package/dist/avl/index.js CHANGED
@@ -7130,7 +7130,7 @@ var init_SvgGrid = __esm({
7130
7130
  x,
7131
7131
  y,
7132
7132
  cols = 4,
7133
- rows = 3,
7133
+ rows: rows2 = 3,
7134
7134
  spacing = 20,
7135
7135
  nodeRadius = 3,
7136
7136
  color = "var(--color-primary)",
@@ -7139,7 +7139,7 @@ var init_SvgGrid = __esm({
7139
7139
  highlights = []
7140
7140
  }) => {
7141
7141
  const highlightSet = new Set(highlights);
7142
- return /* @__PURE__ */ jsx("g", { className, opacity, children: Array.from({ length: rows }).map(
7142
+ return /* @__PURE__ */ jsx("g", { className, opacity, children: Array.from({ length: rows2 }).map(
7143
7143
  (_, row) => Array.from({ length: cols }).map((_2, col) => {
7144
7144
  const index = row * cols + col;
7145
7145
  const isHighlighted = highlightSet.has(index);
@@ -7799,7 +7799,7 @@ var init_Input = __esm({
7799
7799
  onClear,
7800
7800
  value,
7801
7801
  options,
7802
- rows = 3,
7802
+ rows: rows2 = 3,
7803
7803
  onChange,
7804
7804
  ...props
7805
7805
  }, ref) => {
@@ -7849,7 +7849,7 @@ var init_Input = __esm({
7849
7849
  ref,
7850
7850
  value,
7851
7851
  onChange,
7852
- rows,
7852
+ rows: rows2,
7853
7853
  className: baseClassName,
7854
7854
  ...props
7855
7855
  }
@@ -9844,66 +9844,6 @@ var init_RangeSlider = __esm({
9844
9844
  RangeSlider.displayName = "RangeSlider";
9845
9845
  }
9846
9846
  });
9847
- function easeOut(t) {
9848
- return t * (2 - t);
9849
- }
9850
- var AnimatedCounter;
9851
- var init_AnimatedCounter = __esm({
9852
- "components/marketing/atoms/AnimatedCounter.tsx"() {
9853
- "use client";
9854
- init_cn();
9855
- init_Typography();
9856
- AnimatedCounter = ({
9857
- value: rawValue,
9858
- duration = 600,
9859
- prefix,
9860
- suffix,
9861
- className
9862
- }) => {
9863
- const numericRaw = typeof rawValue === "number" ? rawValue : Number.parseFloat(String(rawValue ?? ""));
9864
- const value = !Number.isNaN(numericRaw) ? numericRaw : 0;
9865
- const [displayValue, setDisplayValue] = useState(value);
9866
- const previousValueRef = useRef(value);
9867
- const animationFrameRef = useRef(null);
9868
- useEffect(() => {
9869
- const from = previousValueRef.current;
9870
- const to = value;
9871
- previousValueRef.current = value;
9872
- if (from === to) {
9873
- setDisplayValue(to);
9874
- return;
9875
- }
9876
- const startTime = performance.now();
9877
- const diff = to - from;
9878
- function animate(currentTime) {
9879
- const elapsed = currentTime - startTime;
9880
- const progress = Math.min(elapsed / duration, 1);
9881
- const easedProgress = easeOut(progress);
9882
- setDisplayValue(from + diff * easedProgress);
9883
- if (progress < 1) {
9884
- animationFrameRef.current = requestAnimationFrame(animate);
9885
- } else {
9886
- setDisplayValue(to);
9887
- }
9888
- }
9889
- animationFrameRef.current = requestAnimationFrame(animate);
9890
- return () => {
9891
- if (animationFrameRef.current !== null) {
9892
- cancelAnimationFrame(animationFrameRef.current);
9893
- }
9894
- };
9895
- }, [value, duration]);
9896
- const decimalPlaces = Number.isInteger(value) ? 0 : String(value).split(".")[1]?.length ?? 0;
9897
- const formattedValue = displayValue.toFixed(decimalPlaces);
9898
- return /* @__PURE__ */ jsxs(Typography, { variant: "h3", className: cn("tabular-nums", className), children: [
9899
- prefix,
9900
- formattedValue,
9901
- suffix
9902
- ] });
9903
- };
9904
- AnimatedCounter.displayName = "AnimatedCounter";
9905
- }
9906
- });
9907
9847
  function useInfiniteScroll(onLoadMore, options = {}) {
9908
9848
  const { rootMargin = "200px", hasMore = true, isLoading = false } = options;
9909
9849
  const observerRef = useRef(null);
@@ -12387,15 +12327,15 @@ function HeaderSkeleton({ className }) {
12387
12327
  ] })
12388
12328
  ] });
12389
12329
  }
12390
- function TableSkeleton({ rows = 5, columns = 4, className }) {
12330
+ function TableSkeleton({ rows: rows2 = 5, columns = 4, className }) {
12391
12331
  return /* @__PURE__ */ jsxs(VStack, { gap: "none", className: cn("border border-border rounded-lg overflow-hidden", className), children: [
12392
12332
  /* @__PURE__ */ jsx(HStack, { className: "px-4 py-3 bg-muted/30 border-b border-border", children: Array.from({ length: columns }).map((_, i) => /* @__PURE__ */ jsx(SkeletonBlock, { className: "h-4 flex-1 mx-2" }, i)) }),
12393
- Array.from({ length: rows }).map((_, rowIdx) => /* @__PURE__ */ jsx(
12333
+ Array.from({ length: rows2 }).map((_, rowIdx) => /* @__PURE__ */ jsx(
12394
12334
  HStack,
12395
12335
  {
12396
12336
  className: cn(
12397
12337
  "px-4 py-3",
12398
- rowIdx < rows - 1 && "border-b border-border"
12338
+ rowIdx < rows2 - 1 && "border-b border-border"
12399
12339
  ),
12400
12340
  children: Array.from({ length: columns }).map((_2, colIdx) => /* @__PURE__ */ jsx(SkeletonLine, { className: "flex-1 mx-2" }, colIdx))
12401
12341
  },
@@ -12443,18 +12383,18 @@ function CardSkeleton({ className }) {
12443
12383
  }
12444
12384
  );
12445
12385
  }
12446
- function TextSkeleton({ rows = 3, className }) {
12447
- return /* @__PURE__ */ jsx(VStack, { gap: "sm", className, children: Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsx(
12386
+ function TextSkeleton({ rows: rows2 = 3, className }) {
12387
+ return /* @__PURE__ */ jsx(VStack, { gap: "sm", className, children: Array.from({ length: rows2 }).map((_, i) => /* @__PURE__ */ jsx(
12448
12388
  SkeletonLine,
12449
12389
  {
12450
- className: i === rows - 1 ? "w-2/3" : "w-full"
12390
+ className: i === rows2 - 1 ? "w-2/3" : "w-full"
12451
12391
  },
12452
12392
  i
12453
12393
  )) });
12454
12394
  }
12455
12395
  function Skeleton({
12456
12396
  variant = "text",
12457
- rows,
12397
+ rows: rows2,
12458
12398
  columns,
12459
12399
  fields,
12460
12400
  className
@@ -12464,15 +12404,15 @@ function Skeleton({
12464
12404
  case "header":
12465
12405
  return /* @__PURE__ */ jsx(HeaderSkeleton, { className });
12466
12406
  case "table":
12467
- return /* @__PURE__ */ jsx(TableSkeleton, { rows, columns, className });
12407
+ return /* @__PURE__ */ jsx(TableSkeleton, { rows: rows2, columns, className });
12468
12408
  case "form":
12469
12409
  return /* @__PURE__ */ jsx(FormSkeleton, { fields, className });
12470
12410
  case "card":
12471
12411
  return /* @__PURE__ */ jsx(CardSkeleton, { className });
12472
12412
  case "text":
12473
- return /* @__PURE__ */ jsx(TextSkeleton, { rows, className });
12413
+ return /* @__PURE__ */ jsx(TextSkeleton, { rows: rows2, className });
12474
12414
  default:
12475
- return /* @__PURE__ */ jsx(TextSkeleton, { rows, className });
12415
+ return /* @__PURE__ */ jsx(TextSkeleton, { rows: rows2, className });
12476
12416
  }
12477
12417
  }
12478
12418
  var pulseClass;
@@ -14100,7 +14040,7 @@ var init_MapView = __esm({
14100
14040
  shadowSize: [41, 41]
14101
14041
  });
14102
14042
  L.Marker.prototype.options.icon = defaultIcon;
14103
- const { useEffect: useEffect79, useRef: useRef71, useCallback: useCallback122, useState: useState113 } = React97__default;
14043
+ const { useEffect: useEffect79, useRef: useRef71, useCallback: useCallback123, useState: useState113 } = React97__default;
14104
14044
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
14105
14045
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
14106
14046
  function MapUpdater({ centerLat, centerLng, zoom }) {
@@ -14146,7 +14086,7 @@ var init_MapView = __esm({
14146
14086
  }) {
14147
14087
  const eventBus = useEventBus3();
14148
14088
  const [clickedPosition, setClickedPosition] = useState113(null);
14149
- const handleMapClick = useCallback122((lat, lng) => {
14089
+ const handleMapClick = useCallback123((lat, lng) => {
14150
14090
  if (showClickedPin) {
14151
14091
  setClickedPosition({ lat, lng });
14152
14092
  }
@@ -14155,7 +14095,7 @@ var init_MapView = __esm({
14155
14095
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
14156
14096
  }
14157
14097
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
14158
- const handleMarkerClick = useCallback122((marker) => {
14098
+ const handleMarkerClick = useCallback123((marker) => {
14159
14099
  onMarkerClick?.(marker);
14160
14100
  if (markerClickEvent) {
14161
14101
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -14376,7 +14316,7 @@ function InputPattern({
14376
14316
  function TextareaPattern({
14377
14317
  value = "",
14378
14318
  placeholder,
14379
- rows = 4,
14319
+ rows: rows2 = 4,
14380
14320
  disabled = false,
14381
14321
  fieldError,
14382
14322
  onChange,
@@ -14396,7 +14336,7 @@ function TextareaPattern({
14396
14336
  {
14397
14337
  value: localValue,
14398
14338
  placeholder,
14399
- rows,
14339
+ rows: rows2,
14400
14340
  disabled,
14401
14341
  error: fieldError,
14402
14342
  onChange: handleChange,
@@ -14881,6 +14821,91 @@ var init_ActionPalette = __esm({
14881
14821
  ActionPalette.displayName = "ActionPalette";
14882
14822
  }
14883
14823
  });
14824
+ function parseValue(value) {
14825
+ if (value === "" || value == null) return { num: 0, prefix: "", suffix: "", decimals: 0 };
14826
+ const match = String(value).match(/^([^0-9]*)([0-9]+(?:\.[0-9]+)?)(.*)$/);
14827
+ if (!match) {
14828
+ return { num: 0, prefix: "", suffix: String(value), decimals: 0 };
14829
+ }
14830
+ const numStr = match[2];
14831
+ const decimalIdx = numStr.indexOf(".");
14832
+ const decimals = decimalIdx >= 0 ? numStr.length - decimalIdx - 1 : 0;
14833
+ return {
14834
+ prefix: match[1],
14835
+ num: parseFloat(numStr),
14836
+ suffix: match[3],
14837
+ decimals
14838
+ };
14839
+ }
14840
+ var AnimatedCounter;
14841
+ var init_AnimatedCounter = __esm({
14842
+ "components/core/molecules/AnimatedCounter.tsx"() {
14843
+ "use client";
14844
+ init_cn();
14845
+ init_Box();
14846
+ init_Typography();
14847
+ AnimatedCounter = ({
14848
+ value,
14849
+ label,
14850
+ duration = 1500,
14851
+ className
14852
+ }) => {
14853
+ const ref = useRef(null);
14854
+ const [displayValue, setDisplayValue] = useState("0");
14855
+ const [hasAnimated, setHasAnimated] = useState(false);
14856
+ const animate = useCallback(() => {
14857
+ const { num: num2, prefix, suffix, decimals } = parseValue(value);
14858
+ if (num2 === 0) {
14859
+ setDisplayValue(String(value));
14860
+ return;
14861
+ }
14862
+ const startTime = performance.now();
14863
+ const tick = (now2) => {
14864
+ const elapsed = now2 - startTime;
14865
+ const progress = Math.min(elapsed / duration, 1);
14866
+ const eased = 1 - Math.pow(1 - progress, 3);
14867
+ const current = eased * num2;
14868
+ setDisplayValue(`${prefix}${current.toFixed(decimals)}${suffix}`);
14869
+ if (progress < 1) {
14870
+ requestAnimationFrame(tick);
14871
+ } else {
14872
+ setDisplayValue(String(value));
14873
+ }
14874
+ };
14875
+ requestAnimationFrame(tick);
14876
+ }, [value, duration]);
14877
+ useEffect(() => {
14878
+ if (hasAnimated) return;
14879
+ const el = ref.current;
14880
+ if (!el) return;
14881
+ const observer2 = new IntersectionObserver(
14882
+ (entries) => {
14883
+ if (entries[0].isIntersecting) {
14884
+ setHasAnimated(true);
14885
+ animate();
14886
+ observer2.disconnect();
14887
+ }
14888
+ },
14889
+ { threshold: 0.3 }
14890
+ );
14891
+ observer2.observe(el);
14892
+ return () => observer2.disconnect();
14893
+ }, [hasAnimated, animate]);
14894
+ return /* @__PURE__ */ jsxs(Box, { ref, className: cn("flex flex-col items-center gap-1 p-4", className), children: [
14895
+ /* @__PURE__ */ jsx(
14896
+ Typography,
14897
+ {
14898
+ variant: "h2",
14899
+ className: "text-primary font-bold tabular-nums",
14900
+ children: hasAnimated ? displayValue : "0"
14901
+ }
14902
+ ),
14903
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "muted", className: "text-center", children: label })
14904
+ ] });
14905
+ };
14906
+ AnimatedCounter.displayName = "AnimatedCounter";
14907
+ }
14908
+ });
14884
14909
  var AuthLayout;
14885
14910
  var init_AuthLayout = __esm({
14886
14911
  "components/core/templates/AuthLayout.tsx"() {
@@ -16266,6 +16291,39 @@ var init_IsometricCanvas2 = __esm({
16266
16291
  init_IsometricCanvas();
16267
16292
  }
16268
16293
  });
16294
+
16295
+ // components/game/organisms/boardEntity.ts
16296
+ function boardEntity(entity) {
16297
+ if (!entity) return void 0;
16298
+ return Array.isArray(entity) ? entity[0] : entity;
16299
+ }
16300
+ function str(v) {
16301
+ return v == null ? "" : String(v);
16302
+ }
16303
+ function num(v, fallback = 0) {
16304
+ const n = Number(v);
16305
+ return Number.isFinite(n) ? n : fallback;
16306
+ }
16307
+ function rows(v) {
16308
+ return Array.isArray(v) ? v : [];
16309
+ }
16310
+ function vec2(v) {
16311
+ const o = v ?? {};
16312
+ return { x: num(o.x), y: num(o.y) };
16313
+ }
16314
+ function unitPosition(u) {
16315
+ return vec2(u.position);
16316
+ }
16317
+ function unitTeam(u) {
16318
+ return str(u.team);
16319
+ }
16320
+ function unitHealth(u) {
16321
+ return num(u.health);
16322
+ }
16323
+ var init_boardEntity = __esm({
16324
+ "components/game/organisms/boardEntity.ts"() {
16325
+ }
16326
+ });
16269
16327
  function BattleBoard({
16270
16328
  entity,
16271
16329
  scale = 0.45,
@@ -16292,43 +16350,49 @@ function BattleBoard({
16292
16350
  attackEvent,
16293
16351
  className
16294
16352
  }) {
16295
- const tiles = entity.tiles;
16296
- const features = entity.features ?? [];
16297
- const boardWidth = entity.boardWidth ?? 8;
16298
- const boardHeight = entity.boardHeight ?? 6;
16299
- const assetManifest = entity.assetManifest;
16300
- const backgroundImage = entity.backgroundImage;
16301
- const units = entity.units;
16302
- const selectedUnitId = entity.selectedUnitId;
16303
- const currentPhase = entity.phase;
16304
- const currentTurn = entity.turn;
16305
- const gameResult = entity.gameResult;
16353
+ const board = boardEntity(entity) ?? {};
16354
+ const tiles = Array.isArray(board.tiles) ? board.tiles : [];
16355
+ const features = Array.isArray(board.features) ? board.features : [];
16356
+ const boardWidth = num(board.boardWidth, 8);
16357
+ const boardHeight = num(board.boardHeight, 6);
16358
+ const assetManifest = board.assetManifest;
16359
+ const backgroundImage = board.backgroundImage;
16360
+ const units = rows(board.units);
16361
+ const selectedUnitId = board.selectedUnitId ?? null;
16362
+ const currentPhase = str(board.phase) || "observation";
16363
+ const currentTurn = num(board.turn, 1);
16364
+ const gameResult = board.gameResult ?? null;
16306
16365
  const eventBus = useEventBus();
16307
16366
  const { t } = useTranslate();
16308
16367
  const [hoveredTile, setHoveredTile] = useState(null);
16309
16368
  const [isShaking, setIsShaking] = useState(false);
16310
16369
  const selectedUnit = useMemo(
16311
- () => units.find((u) => u.id === selectedUnitId) ?? null,
16370
+ () => units.find((u) => str(u.id) === selectedUnitId) ?? null,
16312
16371
  [units, selectedUnitId]
16313
16372
  );
16314
16373
  const hoveredUnit = useMemo(() => {
16315
16374
  if (!hoveredTile) return null;
16316
- return units.find(
16317
- (u) => u.position.x === hoveredTile.x && u.position.y === hoveredTile.y && u.health > 0
16318
- ) ?? null;
16375
+ return units.find((u) => {
16376
+ const p2 = unitPosition(u);
16377
+ return p2.x === hoveredTile.x && p2.y === hoveredTile.y && unitHealth(u) > 0;
16378
+ }) ?? null;
16319
16379
  }, [hoveredTile, units]);
16320
- const playerUnits = useMemo(() => units.filter((u) => u.team === "player" && u.health > 0), [units]);
16321
- const enemyUnits = useMemo(() => units.filter((u) => u.team === "enemy" && u.health > 0), [units]);
16380
+ const playerUnits = useMemo(() => units.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0), [units]);
16381
+ const enemyUnits = useMemo(() => units.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0), [units]);
16322
16382
  const validMoves = useMemo(() => {
16323
16383
  if (!selectedUnit || currentPhase !== "movement") return [];
16324
16384
  const moves = [];
16325
- const range = selectedUnit.movement;
16385
+ const range = num(selectedUnit.movement);
16386
+ const origin = unitPosition(selectedUnit);
16326
16387
  for (let dy = -range; dy <= range; dy++) {
16327
16388
  for (let dx = -range; dx <= range; dx++) {
16328
- const nx = selectedUnit.position.x + dx;
16329
- const ny = selectedUnit.position.y + dy;
16389
+ const nx = origin.x + dx;
16390
+ const ny = origin.y + dy;
16330
16391
  const dist = Math.abs(dx) + Math.abs(dy);
16331
- if (dist > 0 && dist <= range && nx >= 0 && nx < boardWidth && ny >= 0 && ny < boardHeight && !units.some((u) => u.position.x === nx && u.position.y === ny && u.health > 0)) {
16392
+ if (dist > 0 && dist <= range && nx >= 0 && nx < boardWidth && ny >= 0 && ny < boardHeight && !units.some((u) => {
16393
+ const p2 = unitPosition(u);
16394
+ return p2.x === nx && p2.y === ny && unitHealth(u) > 0;
16395
+ })) {
16332
16396
  moves.push({ x: nx, y: ny });
16333
16397
  }
16334
16398
  }
@@ -16337,11 +16401,14 @@ function BattleBoard({
16337
16401
  }, [selectedUnit, currentPhase, units, boardWidth, boardHeight]);
16338
16402
  const attackTargets = useMemo(() => {
16339
16403
  if (!selectedUnit || currentPhase !== "action") return [];
16340
- return units.filter((u) => u.team !== selectedUnit.team && u.health > 0).filter((u) => {
16341
- const dx = Math.abs(u.position.x - selectedUnit.position.x);
16342
- const dy = Math.abs(u.position.y - selectedUnit.position.y);
16404
+ const sp = unitPosition(selectedUnit);
16405
+ const sTeam = unitTeam(selectedUnit);
16406
+ return units.filter((u) => unitTeam(u) !== sTeam && unitHealth(u) > 0).filter((u) => {
16407
+ const p2 = unitPosition(u);
16408
+ const dx = Math.abs(p2.x - sp.x);
16409
+ const dy = Math.abs(p2.y - sp.y);
16343
16410
  return dx <= 1 && dy <= 1 && dx + dy > 0;
16344
- }).map((u) => u.position);
16411
+ }).map((u) => unitPosition(u));
16345
16412
  }, [selectedUnit, currentPhase, units]);
16346
16413
  const MOVE_SPEED_MS_PER_TILE = 300;
16347
16414
  const movementAnimRef = useRef(null);
@@ -16381,23 +16448,25 @@ function BattleBoard({
16381
16448
  return () => clearInterval(interval);
16382
16449
  }, []);
16383
16450
  const isoUnits = useMemo(() => {
16384
- return units.filter((u) => u.health > 0).map((unit) => {
16385
- const pos = movingPositions.get(unit.id) ?? unit.position;
16451
+ return units.filter((u) => unitHealth(u) > 0).map((unit) => {
16452
+ const id = str(unit.id);
16453
+ const pos = movingPositions.get(id) ?? unitPosition(unit);
16454
+ const unitTraits = Array.isArray(unit.traits) ? unit.traits : void 0;
16386
16455
  return {
16387
- id: unit.id,
16456
+ id,
16388
16457
  position: pos,
16389
- name: unit.name,
16390
- team: unit.team,
16391
- health: unit.health,
16392
- maxHealth: unit.maxHealth,
16393
- unitType: unit.unitType,
16394
- heroId: unit.heroId,
16395
- sprite: unit.sprite,
16396
- traits: unit.traits?.map((t2) => ({
16397
- name: t2.name,
16398
- currentState: t2.currentState,
16399
- states: t2.states,
16400
- cooldown: t2.cooldown ?? 0
16458
+ name: str(unit.name),
16459
+ team: unitTeam(unit),
16460
+ health: unitHealth(unit),
16461
+ maxHealth: num(unit.maxHealth),
16462
+ unitType: unit.unitType == null ? void 0 : str(unit.unitType),
16463
+ heroId: unit.heroId == null ? void 0 : str(unit.heroId),
16464
+ sprite: unit.sprite == null ? void 0 : str(unit.sprite),
16465
+ traits: unitTraits?.map((tr) => ({
16466
+ name: tr.name,
16467
+ currentState: tr.currentState,
16468
+ states: tr.states,
16469
+ cooldown: tr.cooldown ?? 0
16401
16470
  }))
16402
16471
  };
16403
16472
  });
@@ -16409,8 +16478,8 @@ function BattleBoard({
16409
16478
  [scale, baseOffsetX]
16410
16479
  );
16411
16480
  const checkGameEnd = useCallback(() => {
16412
- const pa = units.filter((u) => u.team === "player" && u.health > 0);
16413
- const ea = units.filter((u) => u.team === "enemy" && u.health > 0);
16481
+ const pa = units.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
16482
+ const ea = units.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
16414
16483
  if (pa.length === 0) {
16415
16484
  onGameEnd?.("defeat");
16416
16485
  if (gameEndEvent) {
@@ -16424,21 +16493,22 @@ function BattleBoard({
16424
16493
  }
16425
16494
  }, [units, onGameEnd, gameEndEvent, eventBus]);
16426
16495
  const handleUnitClick = useCallback((unitId) => {
16427
- const unit = units.find((u) => u.id === unitId);
16496
+ const unit = units.find((u) => str(u.id) === unitId);
16428
16497
  if (!unit) return;
16429
16498
  if (unitClickEvent) {
16430
16499
  eventBus.emit(`UI:${unitClickEvent}`, { unitId });
16431
16500
  }
16432
16501
  if (currentPhase === "action" && selectedUnit) {
16433
- if (unit.team === "enemy" && attackTargets.some((t2) => t2.x === unit.position.x && t2.y === unit.position.y)) {
16434
- const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, selectedUnit.attack - unit.defense);
16502
+ const up = unitPosition(unit);
16503
+ if (unitTeam(unit) === "enemy" && attackTargets.some((t2) => t2.x === up.x && t2.y === up.y)) {
16504
+ const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, num(selectedUnit.attack) - num(unit.defense));
16435
16505
  setIsShaking(true);
16436
16506
  setTimeout(() => setIsShaking(false), 300);
16437
16507
  onAttack?.(selectedUnit, unit, damage);
16438
16508
  if (attackEvent) {
16439
16509
  eventBus.emit(`UI:${attackEvent}`, {
16440
- attackerId: selectedUnit.id,
16441
- targetId: unit.id,
16510
+ attackerId: str(selectedUnit.id),
16511
+ targetId: str(unit.id),
16442
16512
  damage
16443
16513
  });
16444
16514
  }
@@ -16453,9 +16523,9 @@ function BattleBoard({
16453
16523
  if (currentPhase === "movement" && selectedUnit) {
16454
16524
  if (movementAnimRef.current) return;
16455
16525
  if (validMoves.some((m) => m.x === x && m.y === y)) {
16456
- const from = { ...selectedUnit.position };
16526
+ const from = { ...unitPosition(selectedUnit) };
16457
16527
  const to = { x, y };
16458
- startMoveAnimation(selectedUnit.id, from, to, () => {
16528
+ startMoveAnimation(str(selectedUnit.id), from, to, () => {
16459
16529
  onUnitMove?.(selectedUnit, to);
16460
16530
  });
16461
16531
  }
@@ -16613,6 +16683,7 @@ var init_BattleBoard = __esm({
16613
16683
  init_Typography();
16614
16684
  init_Stack();
16615
16685
  init_IsometricCanvas2();
16686
+ init_boardEntity();
16616
16687
  init_isometric();
16617
16688
  BattleBoard.displayName = "BattleBoard";
16618
16689
  }
@@ -17006,24 +17077,24 @@ var init_CodeBlock = __esm({
17006
17077
  return;
17007
17078
  }
17008
17079
  lineEls.forEach((el) => {
17009
- const num = parseInt(el.getAttribute("data-line") ?? "-1", 10);
17010
- if (hiddenLines.has(num)) {
17080
+ const num2 = parseInt(el.getAttribute("data-line") ?? "-1", 10);
17081
+ if (hiddenLines.has(num2)) {
17011
17082
  el.style.display = "none";
17012
17083
  return;
17013
17084
  }
17014
17085
  el.style.display = "";
17015
17086
  el.style.position = "relative";
17016
17087
  el.style.paddingLeft = "1.2em";
17017
- const region = foldStartMap.get(num);
17088
+ const region = foldStartMap.get(num2);
17018
17089
  if (!region) return;
17019
- const isCollapsed = collapsed.has(num);
17090
+ const isCollapsed = collapsed.has(num2);
17020
17091
  const toggle = document.createElement("span");
17021
17092
  toggle.className = "fold-toggle";
17022
17093
  toggle.textContent = isCollapsed ? "\u25B6" : "\u25BC";
17023
17094
  toggle.style.cssText = "position:absolute;left:0;top:0;width:1.2em;text-align:center;cursor:pointer;color:#858585;font-size:10px;user-select:none;line-height:inherit;height:100%";
17024
17095
  toggle.addEventListener("click", (e) => {
17025
17096
  e.stopPropagation();
17026
- toggleFoldRef.current(num);
17097
+ toggleFoldRef.current(num2);
17027
17098
  });
17028
17099
  el.insertBefore(toggle, el.firstChild);
17029
17100
  if (isCollapsed) {
@@ -19276,10 +19347,13 @@ var init_BookChapterView = __esm({
19276
19347
  init_cn();
19277
19348
  BookChapterView = ({
19278
19349
  chapter,
19350
+ orbitalSchema,
19279
19351
  direction,
19280
19352
  className
19281
19353
  }) => {
19282
19354
  const { t: _t } = useTranslate();
19355
+ const title = String(chapter.title ?? "");
19356
+ const content = String(chapter.content ?? "");
19283
19357
  return /* @__PURE__ */ jsxs(
19284
19358
  VStack,
19285
19359
  {
@@ -19287,16 +19361,16 @@ var init_BookChapterView = __esm({
19287
19361
  className: cn("px-6 py-8 max-w-4xl mx-auto w-full", className),
19288
19362
  style: { direction },
19289
19363
  children: [
19290
- /* @__PURE__ */ jsx(Typography, { variant: "h1", className: "text-3xl font-bold", children: chapter.title }),
19364
+ /* @__PURE__ */ jsx(Typography, { variant: "h1", className: "text-3xl font-bold", children: title }),
19291
19365
  /* @__PURE__ */ jsx(Divider, {}),
19292
- !!chapter.orbitalSchema && /* @__PURE__ */ jsx(ScaledDiagram, { children: /* @__PURE__ */ jsx(
19366
+ !!orbitalSchema && /* @__PURE__ */ jsx(ScaledDiagram, { children: /* @__PURE__ */ jsx(
19293
19367
  JazariStateMachine,
19294
19368
  {
19295
- schema: chapter.orbitalSchema,
19369
+ schema: orbitalSchema,
19296
19370
  direction
19297
19371
  }
19298
19372
  ) }),
19299
- /* @__PURE__ */ jsx(ContentRenderer, { content: chapter.content, direction })
19373
+ /* @__PURE__ */ jsx(ContentRenderer, { content, direction })
19300
19374
  ]
19301
19375
  }
19302
19376
  );
@@ -19394,7 +19468,7 @@ var init_BookNavBar = __esm({
19394
19468
  BookNavBar = ({
19395
19469
  currentPage,
19396
19470
  totalPages,
19397
- chapterTitle,
19471
+ chapterTitle: chapterTitle2,
19398
19472
  direction,
19399
19473
  className
19400
19474
  }) => {
@@ -19435,12 +19509,12 @@ var init_BookNavBar = __esm({
19435
19509
  )
19436
19510
  ] }),
19437
19511
  /* @__PURE__ */ jsxs(Box, { className: "flex-1 mx-4 max-w-md", children: [
19438
- chapterTitle && /* @__PURE__ */ jsx(
19512
+ chapterTitle2 && /* @__PURE__ */ jsx(
19439
19513
  Typography,
19440
19514
  {
19441
19515
  variant: "caption",
19442
19516
  className: "text-center block truncate text-muted-foreground",
19443
- children: chapterTitle
19517
+ children: chapterTitle2
19444
19518
  }
19445
19519
  ),
19446
19520
  /* @__PURE__ */ jsx(ProgressBar, { value: progress, size: "sm", variant: "primary" })
@@ -19507,31 +19581,35 @@ var init_BookTableOfContents = __esm({
19507
19581
  style: { direction },
19508
19582
  children: [
19509
19583
  /* @__PURE__ */ jsx(Typography, { variant: "h1", className: "text-3xl font-bold text-center mb-4", children: t("book.tableOfContents") }),
19510
- parts.map((part, partIdx) => /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
19511
- /* @__PURE__ */ jsxs(HStack, { gap: "sm", align: "center", children: [
19512
- /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: t("book.partNumber", { number: String(partIdx + 1) }) }),
19513
- /* @__PURE__ */ jsx(Typography, { variant: "h3", className: "font-semibold", children: part.title })
19514
- ] }),
19515
- /* @__PURE__ */ jsx(VStack, { gap: "xs", className: direction === "rtl" ? "pr-6" : "pl-6", children: part.chapters.map((chapter) => {
19516
- const isCurrent = chapter.id === currentChapterId;
19517
- return /* @__PURE__ */ jsx(
19518
- Button,
19519
- {
19520
- variant: "ghost",
19521
- size: "sm",
19522
- action: "BOOK_NAVIGATE",
19523
- actionPayload: { chapterId: chapter.id },
19524
- className: cn(
19525
- "justify-start text-left w-full",
19526
- direction === "rtl" && "text-right",
19527
- isCurrent && "bg-blue-50 dark:bg-blue-950 text-blue-600 dark:text-blue-400"
19528
- ),
19529
- children: /* @__PURE__ */ jsx(Box, { className: "truncate", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: chapter.title }) })
19530
- },
19531
- chapter.id
19532
- );
19533
- }) })
19534
- ] }, partIdx))
19584
+ parts.map((part, partIdx) => {
19585
+ const chapters = Array.isArray(part.chapters) ? part.chapters : [];
19586
+ return /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
19587
+ /* @__PURE__ */ jsxs(HStack, { gap: "sm", align: "center", children: [
19588
+ /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: t("book.partNumber", { number: String(partIdx + 1) }) }),
19589
+ /* @__PURE__ */ jsx(Typography, { variant: "h3", className: "font-semibold", children: String(part.title ?? "") })
19590
+ ] }),
19591
+ /* @__PURE__ */ jsx(VStack, { gap: "xs", className: direction === "rtl" ? "pr-6" : "pl-6", children: chapters.map((chapter) => {
19592
+ const id = chapter.id == null ? "" : String(chapter.id);
19593
+ const isCurrent = id === currentChapterId;
19594
+ return /* @__PURE__ */ jsx(
19595
+ Button,
19596
+ {
19597
+ variant: "ghost",
19598
+ size: "sm",
19599
+ action: "BOOK_NAVIGATE",
19600
+ actionPayload: { chapterId: id },
19601
+ className: cn(
19602
+ "justify-start text-left w-full",
19603
+ direction === "rtl" && "text-right",
19604
+ isCurrent && "bg-blue-50 dark:bg-blue-950 text-blue-600 dark:text-blue-400"
19605
+ ),
19606
+ children: /* @__PURE__ */ jsx(Box, { className: "truncate", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: String(chapter.title ?? "") }) })
19607
+ },
19608
+ id
19609
+ );
19610
+ }) })
19611
+ ] }, partIdx);
19612
+ })
19535
19613
  ]
19536
19614
  }
19537
19615
  );
@@ -19653,27 +19731,41 @@ function resolveFieldMap(fieldMap) {
19653
19731
  function get(obj, key) {
19654
19732
  return obj[key];
19655
19733
  }
19734
+ function asStr(v) {
19735
+ return v == null ? "" : String(v);
19736
+ }
19656
19737
  function mapBookData(raw, fields = IDENTITY_BOOK_FIELDS) {
19657
19738
  const rawParts = get(raw, fields.parts) ?? [];
19658
- return {
19659
- title: get(raw, fields.title) ?? "",
19660
- subtitle: get(raw, fields.subtitle),
19661
- author: get(raw, fields.author),
19662
- coverImageUrl: get(raw, fields.coverImageUrl),
19663
- direction: get(raw, fields.direction) ?? void 0,
19664
- parts: rawParts.map((part) => {
19665
- const rawChapters = get(part, fields.chapters) ?? [];
19666
- return {
19667
- title: get(part, fields.partTitle) ?? "",
19668
- chapters: rawChapters.map((ch) => ({
19669
- id: get(ch, fields.chapterId) ?? "",
19670
- title: get(ch, fields.chapterTitle) ?? "",
19671
- content: get(ch, fields.chapterContent) ?? "",
19672
- orbitalSchema: get(ch, fields.chapterOrbitalSchema)
19673
- }))
19674
- };
19675
- })
19739
+ const direction = get(raw, fields.direction) ?? "ltr";
19740
+ const cover = {
19741
+ title: asStr(get(raw, fields.title)),
19742
+ subtitle: asStr(get(raw, fields.subtitle)),
19743
+ author: asStr(get(raw, fields.author)),
19744
+ coverImageUrl: asStr(get(raw, fields.coverImageUrl)),
19745
+ direction
19676
19746
  };
19747
+ const schemaByChapterId = {};
19748
+ const chapters = [];
19749
+ const parts = rawParts.map((part) => {
19750
+ const rawChapters = get(part, fields.chapters) ?? [];
19751
+ const chapterRows = rawChapters.map((ch) => {
19752
+ const id = asStr(get(ch, fields.chapterId));
19753
+ const schema = get(ch, fields.chapterOrbitalSchema);
19754
+ if (schema) schemaByChapterId[id] = schema;
19755
+ const row = {
19756
+ id,
19757
+ title: asStr(get(ch, fields.chapterTitle)),
19758
+ content: asStr(get(ch, fields.chapterContent))
19759
+ };
19760
+ chapters.push(row);
19761
+ return row;
19762
+ });
19763
+ return {
19764
+ title: asStr(get(part, fields.partTitle)),
19765
+ chapters: chapterRows
19766
+ };
19767
+ });
19768
+ return { cover, direction, parts, chapters, schemaByChapterId };
19677
19769
  }
19678
19770
  var IDENTITY_BOOK_FIELDS, AR_BOOK_FIELDS, FIELD_MAP_REGISTRY;
19679
19771
  var init_types2 = __esm({
@@ -19711,10 +19803,7 @@ var init_types2 = __esm({
19711
19803
  };
19712
19804
  }
19713
19805
  });
19714
- function flattenChapters(book) {
19715
- return book.parts.flatMap((part) => part.chapters);
19716
- }
19717
- var PRINT_STYLES, BookViewer;
19806
+ var chapterId, chapterTitle, PRINT_STYLES, BookViewer;
19718
19807
  var init_BookViewer = __esm({
19719
19808
  "components/marketing/organisms/book/BookViewer.tsx"() {
19720
19809
  init_Box();
@@ -19727,6 +19816,8 @@ var init_BookViewer = __esm({
19727
19816
  init_BookNavBar();
19728
19817
  init_EmptyState();
19729
19818
  init_types2();
19819
+ chapterId = (ch) => ch?.id == null ? void 0 : String(ch.id);
19820
+ chapterTitle = (ch) => ch?.title == null ? void 0 : String(ch.title);
19730
19821
  PRINT_STYLES = `
19731
19822
  @media print {
19732
19823
  .book-viewer-page {
@@ -19755,14 +19846,14 @@ var init_BookViewer = __esm({
19755
19846
  return mapBookData(raw, resolvedFieldMap);
19756
19847
  }, [entity, resolvedFieldMap]);
19757
19848
  const direction = book?.direction ?? "ltr";
19758
- const chapters = useMemo(() => book ? flattenChapters(book) : [], [book]);
19849
+ const chapters = useMemo(() => book ? book.chapters : [], [book]);
19759
19850
  const totalPages = 2 + chapters.length;
19760
19851
  const navigateTo = useCallback(
19761
19852
  (page) => {
19762
19853
  const clamped = Math.max(0, Math.min(page, totalPages - 1));
19763
19854
  setCurrentPage(clamped);
19764
- const chapterId = clamped >= 2 ? chapters[clamped - 2]?.id : void 0;
19765
- eventBus.emit("UI:BOOK_PAGE_CHANGE", { pageIndex: clamped, chapterId });
19855
+ const id = clamped >= 2 ? chapterId(chapters[clamped - 2]) : void 0;
19856
+ eventBus.emit("UI:BOOK_PAGE_CHANGE", { pageIndex: clamped, chapterId: id });
19766
19857
  },
19767
19858
  [totalPages, chapters, eventBus]
19768
19859
  );
@@ -19774,8 +19865,8 @@ var init_BookViewer = __esm({
19774
19865
  eventBus.on("UI:BOOK_PAGE_NEXT", () => navigateTo(currentPage + 1)),
19775
19866
  eventBus.on("UI:BOOK_PRINT", () => window.print()),
19776
19867
  eventBus.on("UI:BOOK_NAVIGATE", (event) => {
19777
- const chapterId = event.payload?.chapterId;
19778
- const idx = chapters.findIndex((ch) => ch.id === chapterId);
19868
+ const targetId = event.payload?.chapterId;
19869
+ const idx = chapters.findIndex((ch) => chapterId(ch) === targetId);
19779
19870
  if (idx >= 0) navigateTo(idx + 2);
19780
19871
  })
19781
19872
  ];
@@ -19792,9 +19883,11 @@ var init_BookViewer = __esm({
19792
19883
  style.remove();
19793
19884
  };
19794
19885
  }, []);
19795
- const currentChapterId = currentPage >= 2 ? chapters[currentPage - 2]?.id : void 0;
19796
- const currentChapterTitle = currentPage >= 2 ? chapters[currentPage - 2]?.title : void 0;
19886
+ const currentChapterId = currentPage >= 2 ? chapterId(chapters[currentPage - 2]) : void 0;
19887
+ const currentChapterTitle = currentPage >= 2 ? chapterTitle(chapters[currentPage - 2]) : void 0;
19797
19888
  if (!book) return /* @__PURE__ */ jsx(EmptyState, { message: t("book.noData") });
19889
+ const cover = book.cover;
19890
+ const coverTitle = String(cover.title ?? "");
19798
19891
  return /* @__PURE__ */ jsxs(VStack, { className: cn("relative h-full overflow-hidden bg-background", className), children: [
19799
19892
  /* @__PURE__ */ jsxs(
19800
19893
  Box,
@@ -19806,10 +19899,10 @@ var init_BookViewer = __esm({
19806
19899
  /* @__PURE__ */ jsx(
19807
19900
  BookCoverPage,
19808
19901
  {
19809
- title: book.title,
19810
- subtitle: book.subtitle,
19811
- author: book.author,
19812
- coverImageUrl: book.coverImageUrl,
19902
+ title: coverTitle,
19903
+ subtitle: String(cover.subtitle ?? "") || void 0,
19904
+ author: String(cover.author ?? "") || void 0,
19905
+ coverImageUrl: String(cover.coverImageUrl ?? "") || void 0,
19813
19906
  direction
19814
19907
  }
19815
19908
  ),
@@ -19820,23 +19913,27 @@ var init_BookViewer = __esm({
19820
19913
  direction
19821
19914
  }
19822
19915
  ),
19823
- chapters.map((chapter) => /* @__PURE__ */ jsx(
19824
- BookChapterView,
19825
- {
19826
- chapter,
19827
- direction
19828
- },
19829
- chapter.id
19830
- ))
19916
+ chapters.map((chapter) => {
19917
+ const id = chapterId(chapter);
19918
+ return /* @__PURE__ */ jsx(
19919
+ BookChapterView,
19920
+ {
19921
+ chapter,
19922
+ orbitalSchema: id ? book.schemaByChapterId[id] : void 0,
19923
+ direction
19924
+ },
19925
+ id
19926
+ );
19927
+ })
19831
19928
  ] }),
19832
19929
  /* @__PURE__ */ jsxs(Box, { className: "print:hidden", children: [
19833
19930
  currentPage === 0 && /* @__PURE__ */ jsx(
19834
19931
  BookCoverPage,
19835
19932
  {
19836
- title: book.title,
19837
- subtitle: book.subtitle,
19838
- author: book.author,
19839
- coverImageUrl: book.coverImageUrl,
19933
+ title: coverTitle,
19934
+ subtitle: String(cover.subtitle ?? "") || void 0,
19935
+ author: String(cover.author ?? "") || void 0,
19936
+ coverImageUrl: String(cover.coverImageUrl ?? "") || void 0,
19840
19937
  direction
19841
19938
  }
19842
19939
  ),
@@ -19852,6 +19949,7 @@ var init_BookViewer = __esm({
19852
19949
  BookChapterView,
19853
19950
  {
19854
19951
  chapter: chapters[currentPage - 2],
19952
+ orbitalSchema: currentChapterId ? book.schemaByChapterId[currentChapterId] : void 0,
19855
19953
  direction
19856
19954
  }
19857
19955
  )
@@ -19864,7 +19962,7 @@ var init_BookViewer = __esm({
19864
19962
  {
19865
19963
  currentPage,
19866
19964
  totalPages,
19867
- chapterTitle: currentPage === 0 ? book.title : currentPage === 1 ? t("book.tableOfContents") : currentChapterTitle,
19965
+ chapterTitle: currentPage === 0 ? coverTitle : currentPage === 1 ? t("book.tableOfContents") : currentChapterTitle,
19868
19966
  direction
19869
19967
  }
19870
19968
  )
@@ -19962,7 +20060,7 @@ var init_Grid = __esm({
19962
20060
  };
19963
20061
  Grid = ({
19964
20062
  cols = 1,
19965
- rows,
20063
+ rows: rows2,
19966
20064
  gap = "md",
19967
20065
  rowGap,
19968
20066
  colGap,
@@ -19974,7 +20072,7 @@ var init_Grid = __esm({
19974
20072
  children,
19975
20073
  as: Component = "div"
19976
20074
  }) => {
19977
- const mergedStyle = rows ? { gridTemplateRows: `repeat(${rows}, minmax(0, 1fr))`, ...style } : style;
20075
+ const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
19978
20076
  return React97__default.createElement(
19979
20077
  Component,
19980
20078
  {
@@ -20690,14 +20788,14 @@ function BuilderBoard({
20690
20788
  }) {
20691
20789
  const { emit } = useEventBus();
20692
20790
  const { t } = useTranslate();
20693
- const resolved = Array.isArray(entity) ? entity[0] : entity;
20791
+ const resolved = boardEntity(entity);
20694
20792
  const [placements, setPlacements] = useState({});
20695
20793
  const [headerError, setHeaderError] = useState(false);
20696
20794
  const [submitted, setSubmitted] = useState(false);
20697
20795
  const [attempts, setAttempts] = useState(0);
20698
20796
  const [showHint, setShowHint] = useState(false);
20699
- const components = resolved?.components ?? [];
20700
- const slots = resolved?.slots ?? [];
20797
+ const components = Array.isArray(resolved?.components) ? resolved.components : [];
20798
+ const slots = Array.isArray(resolved?.slots) ? resolved.slots : [];
20701
20799
  const usedComponentIds = new Set(Object.values(placements));
20702
20800
  const availableComponents = components.filter((c) => !usedComponentIds.has(c.id));
20703
20801
  const [selectedComponent, setSelectedComponent] = useState(null);
@@ -20731,7 +20829,7 @@ function BuilderBoard({
20731
20829
  }, [slots, placements, attempts, completeEvent, emit]);
20732
20830
  const handleReset = () => {
20733
20831
  setSubmitted(false);
20734
- if (attempts >= 2 && resolved?.hint) {
20832
+ if (attempts >= 2 && str(resolved?.hint)) {
20735
20833
  setShowHint(true);
20736
20834
  }
20737
20835
  };
@@ -20744,20 +20842,24 @@ function BuilderBoard({
20744
20842
  };
20745
20843
  const getComponentById = (id) => components.find((c) => c.id === id);
20746
20844
  if (!resolved) return null;
20845
+ const theme = resolved.theme ?? void 0;
20846
+ const themeBackground = theme?.background;
20847
+ const headerImage = str(resolved.headerImage);
20848
+ const hint = str(resolved.hint);
20747
20849
  return /* @__PURE__ */ jsx(
20748
20850
  Box,
20749
20851
  {
20750
20852
  className,
20751
20853
  style: {
20752
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
20854
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
20753
20855
  backgroundSize: "cover",
20754
20856
  backgroundPosition: "center"
20755
20857
  },
20756
20858
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
20757
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
20859
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
20758
20860
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
20759
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
20760
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description })
20861
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
20862
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) })
20761
20863
  ] }) }),
20762
20864
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
20763
20865
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("builder.components") }),
@@ -20817,9 +20919,9 @@ function BuilderBoard({
20817
20919
  ] }) }),
20818
20920
  submitted && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
20819
20921
  /* @__PURE__ */ jsx(Icon, { icon: allCorrect ? CheckCircle : XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
20820
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("builder.success") : resolved.failMessage ?? t("builder.incorrect") })
20922
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("builder.success") : str(resolved.failMessage) || t("builder.incorrect") })
20821
20923
  ] }) }),
20822
- showHint && resolved.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
20924
+ showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
20823
20925
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
20824
20926
  !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
20825
20927
  /* @__PURE__ */ jsx(Icon, { icon: Wrench, size: "sm" }),
@@ -20838,6 +20940,7 @@ var init_BuilderBoard = __esm({
20838
20940
  "components/game/organisms/puzzles/builder/BuilderBoard.tsx"() {
20839
20941
  init_atoms2();
20840
20942
  init_useEventBus();
20943
+ init_boardEntity();
20841
20944
  BuilderBoard.displayName = "BuilderBoard";
20842
20945
  }
20843
20946
  });
@@ -21175,21 +21278,24 @@ function CalendarGrid({
21175
21278
  eventBus.emit(`UI:${longPressEvent}`, { date: day.toISOString(), time, ...longPressPayload });
21176
21279
  }, 500);
21177
21280
  }, [longPressEvent, longPressPayload, eventBus]);
21178
- const renderEvent = (event) => /* @__PURE__ */ jsx(
21179
- Box,
21180
- {
21181
- rounded: "md",
21182
- padding: "xs",
21183
- border: true,
21184
- className: cn(
21185
- "cursor-pointer hover:shadow-sm transition-shadow text-xs truncate",
21186
- event.color ? event.color : "bg-blue-500/15 border-blue-500/30 text-blue-600"
21187
- ),
21188
- onClick: (e) => handleEventClick(event, e),
21189
- children: /* @__PURE__ */ jsx(Typography, { variant: "small", className: "truncate font-medium", children: event.title })
21190
- },
21191
- event.id
21192
- );
21281
+ const renderEvent = (event) => {
21282
+ const color = event.color;
21283
+ return /* @__PURE__ */ jsx(
21284
+ Box,
21285
+ {
21286
+ rounded: "md",
21287
+ padding: "xs",
21288
+ border: true,
21289
+ className: cn(
21290
+ "cursor-pointer hover:shadow-sm transition-shadow text-xs truncate",
21291
+ color ? color : "bg-blue-500/15 border-blue-500/30 text-blue-600"
21292
+ ),
21293
+ onClick: (e) => handleEventClick(event, e),
21294
+ children: /* @__PURE__ */ jsx(Typography, { variant: "small", className: "truncate font-medium", children: event.title })
21295
+ },
21296
+ event.id
21297
+ );
21298
+ };
21193
21299
  return /* @__PURE__ */ jsxs(
21194
21300
  Box,
21195
21301
  {
@@ -22853,7 +22959,6 @@ var init_CardGrid = __esm({
22853
22959
  alignItems = "stretch",
22854
22960
  className,
22855
22961
  children,
22856
- // EntityDisplayProps
22857
22962
  entity,
22858
22963
  isLoading = false,
22859
22964
  error = null,
@@ -23325,14 +23430,14 @@ var init_CaseStudyOrganism = __esm({
23325
23430
  /* @__PURE__ */ jsx(SimpleGrid, { cols: cols > 0 ? cols : 1, gap: "lg", children: items.map((study) => /* @__PURE__ */ jsx(
23326
23431
  CaseStudyCard,
23327
23432
  {
23328
- title: study.title,
23329
- description: study.description,
23330
- category: study.category,
23331
- categoryColor: study.categoryColor,
23332
- href: study.href,
23333
- linkLabel: study.linkLabel
23433
+ title: String(study.title ?? ""),
23434
+ description: String(study.description ?? ""),
23435
+ category: String(study.category ?? ""),
23436
+ categoryColor: study.categoryColor != null ? String(study.categoryColor) : void 0,
23437
+ href: String(study.href ?? ""),
23438
+ linkLabel: study.linkLabel != null ? String(study.linkLabel) : void 0
23334
23439
  },
23335
- study.id
23440
+ String(study.id ?? "")
23336
23441
  )) })
23337
23442
  ] });
23338
23443
  };
@@ -23355,10 +23460,10 @@ function CastleBoard({
23355
23460
  className
23356
23461
  }) {
23357
23462
  const eventBus = useEventBus();
23358
- const resolved = Array.isArray(entity) ? entity[0] : entity;
23359
- const tiles = resolved?.tiles ?? [];
23360
- const features = resolved?.features ?? [];
23361
- const units = resolved?.units ?? [];
23463
+ const resolved = boardEntity(entity);
23464
+ const tiles = Array.isArray(resolved?.tiles) ? resolved.tiles : [];
23465
+ const features = Array.isArray(resolved?.features) ? resolved.features : [];
23466
+ const units = Array.isArray(resolved?.units) ? resolved.units : [];
23362
23467
  const assetManifest = resolved?.assetManifest;
23363
23468
  const backgroundImage = resolved?.backgroundImage;
23364
23469
  const [hoveredTile, setHoveredTile] = useState(null);
@@ -23386,7 +23491,7 @@ function CastleBoard({
23386
23491
  onFeatureClick?.(feature);
23387
23492
  if (featureClickEvent) {
23388
23493
  eventBus.emit(`UI:${featureClickEvent}`, {
23389
- featureId: feature.id,
23494
+ featureId: feature.id ?? "",
23390
23495
  featureType: feature.type,
23391
23496
  x: feature.x,
23392
23497
  y: feature.y
@@ -23454,6 +23559,7 @@ var init_CastleBoard = __esm({
23454
23559
  init_cn();
23455
23560
  init_useEventBus();
23456
23561
  init_IsometricCanvas2();
23562
+ init_boardEntity();
23457
23563
  init_isometric();
23458
23564
  CastleBoard.displayName = "CastleBoard";
23459
23565
  }
@@ -24264,14 +24370,14 @@ function ClassifierBoard({
24264
24370
  }) {
24265
24371
  const { emit } = useEventBus();
24266
24372
  const { t } = useTranslate();
24267
- const resolved = Array.isArray(entity) ? entity[0] : entity;
24373
+ const resolved = boardEntity(entity);
24268
24374
  const [assignments, setAssignments] = useState({});
24269
24375
  const [headerError, setHeaderError] = useState(false);
24270
24376
  const [submitted, setSubmitted] = useState(false);
24271
24377
  const [attempts, setAttempts] = useState(0);
24272
24378
  const [showHint, setShowHint] = useState(false);
24273
- const items = resolved?.items ?? [];
24274
- const categories = resolved?.categories ?? [];
24379
+ const items = Array.isArray(resolved?.items) ? resolved.items : [];
24380
+ const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
24275
24381
  const unassignedItems = items.filter((item) => !assignments[item.id]);
24276
24382
  const allAssigned = Object.keys(assignments).length === items.length;
24277
24383
  const results = submitted ? items.map((item) => ({
@@ -24303,7 +24409,7 @@ function ClassifierBoard({
24303
24409
  }, [items, assignments, attempts, completeEvent, emit]);
24304
24410
  const handleReset = () => {
24305
24411
  setSubmitted(false);
24306
- if (attempts >= 2 && resolved?.hint) {
24412
+ if (attempts >= 2 && str(resolved?.hint)) {
24307
24413
  setShowHint(true);
24308
24414
  }
24309
24415
  };
@@ -24314,20 +24420,25 @@ function ClassifierBoard({
24314
24420
  setShowHint(false);
24315
24421
  };
24316
24422
  if (!resolved) return null;
24423
+ const theme = resolved.theme ?? void 0;
24424
+ const themeBackground = theme?.background;
24425
+ const headerImage = str(resolved.headerImage);
24426
+ const hint = str(resolved.hint);
24427
+ const failMessage = str(resolved.failMessage);
24317
24428
  return /* @__PURE__ */ jsx(
24318
24429
  Box,
24319
24430
  {
24320
24431
  className,
24321
24432
  style: {
24322
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
24433
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
24323
24434
  backgroundSize: "cover",
24324
24435
  backgroundPosition: "center"
24325
24436
  },
24326
24437
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
24327
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
24438
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
24328
24439
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
24329
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
24330
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description })
24440
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
24441
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) })
24331
24442
  ] }) }),
24332
24443
  unassignedItems.length > 0 && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
24333
24444
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("classifier.itemsToSort") }),
@@ -24379,10 +24490,10 @@ function ClassifierBoard({
24379
24490
  }) }),
24380
24491
  submitted && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
24381
24492
  /* @__PURE__ */ jsx(Icon, { icon: allCorrect ? CheckCircle : XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
24382
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("classifier.allCorrect") : `${correctCount}/${items.length} ${t("classifier.correct")}` }),
24383
- !allCorrect && resolved.failMessage && /* @__PURE__ */ jsx(Typography, { variant: "body", className: "text-muted-foreground", children: resolved.failMessage })
24493
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("classifier.allCorrect") : `${correctCount}/${items.length} ${t("classifier.correct")}` }),
24494
+ !allCorrect && failMessage && /* @__PURE__ */ jsx(Typography, { variant: "body", className: "text-muted-foreground", children: failMessage })
24384
24495
  ] }) }),
24385
- showHint && resolved.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
24496
+ showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
24386
24497
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
24387
24498
  !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
24388
24499
  /* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
@@ -24401,6 +24512,7 @@ var init_ClassifierBoard = __esm({
24401
24512
  "components/game/organisms/puzzles/classifier/ClassifierBoard.tsx"() {
24402
24513
  init_atoms2();
24403
24514
  init_useEventBus();
24515
+ init_boardEntity();
24404
24516
  ClassifierBoard.displayName = "ClassifierBoard";
24405
24517
  }
24406
24518
  });
@@ -30771,7 +30883,7 @@ function InventoryPanel({
30771
30883
  const slotArray = Array.from({ length: safeSlots }, (_, index) => {
30772
30884
  return safeItems[index] ?? null;
30773
30885
  });
30774
- const rows = Math.ceil(safeSlots / safeColumns);
30886
+ const rows2 = Math.ceil(safeSlots / safeColumns);
30775
30887
  const handleSlotClick = useCallback((index) => {
30776
30888
  if (selectSlotEvent) eventBus.emit(`UI:${selectSlotEvent}`, { index });
30777
30889
  onSelectSlot?.(index);
@@ -30840,7 +30952,7 @@ function InventoryPanel({
30840
30952
  className: "grid gap-1 bg-[var(--color-card)] p-2 rounded-container border border-border",
30841
30953
  style: {
30842
30954
  gridTemplateColumns: `repeat(${safeColumns}, ${slotSize}px)`,
30843
- gridTemplateRows: `repeat(${rows}, ${slotSize}px)`
30955
+ gridTemplateRows: `repeat(${rows2}, ${slotSize}px)`
30844
30956
  },
30845
30957
  children: slotArray.map((item, index) => /* @__PURE__ */ jsx(
30846
30958
  "button",
@@ -34804,11 +34916,11 @@ function LatticeSVG({
34804
34916
  }) {
34805
34917
  const paths = [];
34806
34918
  const cols = 5;
34807
- const rows = Math.ceil(h / (w / cols));
34919
+ const rows2 = Math.ceil(h / (w / cols));
34808
34920
  const cellW = w / cols;
34809
34921
  const cellH = cellW;
34810
34922
  const bulge = cellW * 0.3;
34811
- for (let row = 0; row < rows; row++) {
34923
+ for (let row = 0; row < rows2; row++) {
34812
34924
  for (let col = 0; col < cols; col++) {
34813
34925
  const cx = col * cellW + cellW / 2;
34814
34926
  const cy = row * cellH + cellH / 2;
@@ -35220,7 +35332,7 @@ var init_MatrixQuestion = __esm({
35220
35332
  };
35221
35333
  MatrixQuestion = ({
35222
35334
  title,
35223
- rows,
35335
+ rows: rows2,
35224
35336
  columns = DEFAULT_MATRIX_COLUMNS,
35225
35337
  values,
35226
35338
  onChange,
@@ -35230,7 +35342,7 @@ var init_MatrixQuestion = __esm({
35230
35342
  className
35231
35343
  }) => {
35232
35344
  const styles = sizeStyles13[size];
35233
- const safeRows = rows ?? [];
35345
+ const safeRows = rows2 ?? [];
35234
35346
  const safeValues = values ?? {};
35235
35347
  const eventBus = useEventBus();
35236
35348
  const handleChange = useCallback(
@@ -35858,7 +35970,8 @@ var init_PositionedCanvas = __esm({
35858
35970
  dragRef.current = null;
35859
35971
  setDraggingId(null);
35860
35972
  if (!wasDrag) {
35861
- const next = selectedId === item.id ? null : item.id;
35973
+ const itemId = item.id;
35974
+ const next = selectedId === itemId ? null : itemId;
35862
35975
  onSelect?.(next);
35863
35976
  if (selectEvent) {
35864
35977
  eventBus.emit(`UI:${selectEvent}`, { id: next });
@@ -35893,15 +36006,22 @@ var init_PositionedCanvas = __esm({
35893
36006
  style: { width, height },
35894
36007
  onClick: handleContainerClick,
35895
36008
  children: items.map((item) => {
36009
+ const itemId = item.id;
36010
+ const label = item.label;
36011
+ const x = item.x;
36012
+ const y = item.y;
36013
+ const capacity = item.capacity;
36014
+ const partySize = item.partySize;
36015
+ const serverName = item.serverName;
35896
36016
  const status = item.status ?? "empty";
35897
36017
  const shape = item.shape ?? "round";
35898
- const isSelected = selectedId === item.id;
35899
- const isDragging = draggingId === item.id;
36018
+ const isSelected = selectedId === itemId;
36019
+ const isDragging = draggingId === itemId;
35900
36020
  const statusBadge = STATUS_BADGE[status];
35901
36021
  return /* @__PURE__ */ jsxs(
35902
36022
  Box,
35903
36023
  {
35904
- "data-testid": `item-node-${item.id}`,
36024
+ "data-testid": `item-node-${itemId}`,
35905
36025
  "data-status": status,
35906
36026
  className: cn(
35907
36027
  "absolute flex flex-col items-center justify-center gap-1 border-2 select-none",
@@ -35912,7 +36032,7 @@ var init_PositionedCanvas = __esm({
35912
36032
  isSelected && "outline outline-2 outline-offset-2 outline-primary shadow-md",
35913
36033
  isDragging && "shadow-lg z-10"
35914
36034
  ),
35915
- style: { left: item.x, top: item.y, touchAction: "none" },
36035
+ style: { left: x, top: y, touchAction: "none" },
35916
36036
  onPointerDown: (e) => handlePointerDown(e, item),
35917
36037
  onPointerMove: handlePointerMove,
35918
36038
  onPointerUp: (e) => handlePointerUp(e, item),
@@ -35920,10 +36040,10 @@ var init_PositionedCanvas = __esm({
35920
36040
  children: [
35921
36041
  /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-1", children: [
35922
36042
  getStatusIcon(status),
35923
- /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "semibold", children: item.label })
36043
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "semibold", children: label })
35924
36044
  ] }),
35925
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: item.partySize !== void 0 && status === "seated" ? `${item.partySize}/${item.capacity}` : `Cap ${item.capacity}` }),
35926
- status === "seated" && item.serverName && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "truncate max-w-[80%]", children: item.serverName }),
36045
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: partySize !== void 0 && status === "seated" ? `${partySize}/${capacity}` : `Cap ${capacity}` }),
36046
+ status === "seated" && serverName && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "truncate max-w-[80%]", children: serverName }),
35927
36047
  isSelected && /* @__PURE__ */ jsx(
35928
36048
  Badge,
35929
36049
  {
@@ -35935,7 +36055,7 @@ var init_PositionedCanvas = __esm({
35935
36055
  )
35936
36056
  ]
35937
36057
  },
35938
- item.id
36058
+ itemId
35939
36059
  );
35940
36060
  })
35941
36061
  }
@@ -36707,9 +36827,10 @@ var init_RichBlockEditor = __esm({
36707
36827
  });
36708
36828
  function collectInitiallyCollapsed(nodes, acc) {
36709
36829
  for (const n of nodes) {
36830
+ const replies = n.replies;
36710
36831
  if (n.collapsed) acc.add(n.id);
36711
- if (n.replies && n.replies.length > 0) {
36712
- collectInitiallyCollapsed(n.replies, acc);
36832
+ if (replies && replies.length > 0) {
36833
+ collectInitiallyCollapsed(replies, acc);
36713
36834
  }
36714
36835
  }
36715
36836
  }
@@ -36739,44 +36860,52 @@ var init_ReplyTree = __esm({
36739
36860
  }) => {
36740
36861
  const eventBus = useEventBus();
36741
36862
  const { t } = useTranslate();
36742
- const hasReplies = !!node.replies && node.replies.length > 0;
36743
- const isCollapsed = collapsedSet.has(node.id);
36863
+ const nodeId = node.id;
36864
+ const authorName = node.authorName;
36865
+ const authorAvatarUrl = node.authorAvatarUrl;
36866
+ const content = node.content;
36867
+ const postedAt = node.postedAt;
36868
+ const voteCount = node.voteCount;
36869
+ const userVote = node.userVote;
36870
+ const replies = node.replies;
36871
+ const hasReplies = !!replies && replies.length > 0;
36872
+ const isCollapsed = collapsedSet.has(nodeId);
36744
36873
  const atMaxDepth = depth >= maxDepth;
36745
36874
  const [replyOpen, setReplyOpen] = useState(false);
36746
36875
  const [draft, setDraft] = useState("");
36747
36876
  const handleVote = useCallback(
36748
36877
  (next) => {
36749
- onVote?.(node.id, next);
36750
- if (voteEvent) eventBus.emit(`UI:${voteEvent}`, { nodeId: node.id, vote: next });
36878
+ onVote?.(nodeId, next);
36879
+ if (voteEvent) eventBus.emit(`UI:${voteEvent}`, { nodeId, vote: next });
36751
36880
  },
36752
- [node.id, onVote, voteEvent, eventBus]
36881
+ [nodeId, onVote, voteEvent, eventBus]
36753
36882
  );
36754
36883
  const handleReply = useCallback(() => {
36755
- onReply?.(node.id);
36884
+ onReply?.(nodeId);
36756
36885
  setReplyOpen((open) => !open);
36757
- }, [node.id, onReply]);
36886
+ }, [nodeId, onReply]);
36758
36887
  const handleSubmitReply = useCallback(() => {
36759
- const content = draft.trim();
36760
- if (!content) return;
36761
- if (replyEvent) eventBus.emit(`UI:${replyEvent}`, { parentNodeId: node.id, content });
36888
+ const text = draft.trim();
36889
+ if (!text) return;
36890
+ if (replyEvent) eventBus.emit(`UI:${replyEvent}`, { parentNodeId: nodeId, content: text });
36762
36891
  setDraft("");
36763
36892
  setReplyOpen(false);
36764
- }, [node.id, draft, replyEvent, eventBus]);
36893
+ }, [nodeId, draft, replyEvent, eventBus]);
36765
36894
  const handleCancelReply = useCallback(() => {
36766
36895
  setDraft("");
36767
36896
  setReplyOpen(false);
36768
36897
  }, []);
36769
36898
  const handleFlag = useCallback(() => {
36770
- onFlag?.(node.id);
36771
- if (flagEvent) eventBus.emit(`UI:${flagEvent}`, { nodeId: node.id });
36772
- }, [node.id, onFlag, flagEvent, eventBus]);
36899
+ onFlag?.(nodeId);
36900
+ if (flagEvent) eventBus.emit(`UI:${flagEvent}`, { nodeId });
36901
+ }, [nodeId, onFlag, flagEvent, eventBus]);
36773
36902
  const handleContinue = useCallback(() => {
36774
- onContinueThread?.(node.id);
36775
- if (continueThreadEvent) eventBus.emit(`UI:${continueThreadEvent}`, { nodeId: node.id });
36776
- }, [node.id, onContinueThread, continueThreadEvent, eventBus]);
36903
+ onContinueThread?.(nodeId);
36904
+ if (continueThreadEvent) eventBus.emit(`UI:${continueThreadEvent}`, { nodeId });
36905
+ }, [nodeId, onContinueThread, continueThreadEvent, eventBus]);
36777
36906
  const handleToggle = useCallback(() => {
36778
- toggleCollapse(node.id);
36779
- }, [node.id, toggleCollapse]);
36907
+ toggleCollapse(nodeId);
36908
+ }, [nodeId, toggleCollapse]);
36780
36909
  return /* @__PURE__ */ jsxs(Box, { className: "flex flex-row gap-2 items-stretch min-w-0", children: [
36781
36910
  /* @__PURE__ */ jsxs(Box, { className: "flex flex-col items-center flex-shrink-0 w-6", children: [
36782
36911
  hasReplies ? /* @__PURE__ */ jsx(
@@ -36808,25 +36937,25 @@ var init_ReplyTree = __esm({
36808
36937
  /* @__PURE__ */ jsx(
36809
36938
  Avatar,
36810
36939
  {
36811
- src: node.authorAvatarUrl,
36812
- name: node.authorName,
36940
+ src: authorAvatarUrl,
36941
+ name: authorName,
36813
36942
  size: "sm"
36814
36943
  }
36815
36944
  ),
36816
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "semibold", children: node.authorName }),
36817
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: node.postedAt })
36945
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "semibold", children: authorName }),
36946
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: postedAt })
36818
36947
  ] }),
36819
- /* @__PURE__ */ jsx(Typography, { variant: "body", className: "whitespace-pre-wrap break-words", children: node.content }),
36948
+ /* @__PURE__ */ jsx(Typography, { variant: "body", className: "whitespace-pre-wrap break-words", children: content }),
36820
36949
  showActions && /* @__PURE__ */ jsxs(Box, { className: "flex flex-row gap-2 items-center", children: [
36821
36950
  /* @__PURE__ */ jsx(
36822
36951
  VoteStack,
36823
36952
  {
36824
- count: node.voteCount ?? 0,
36825
- userVote: node.userVote ?? null,
36953
+ count: voteCount ?? 0,
36954
+ userVote: userVote ?? null,
36826
36955
  onVote: handleVote,
36827
36956
  size: "sm",
36828
36957
  variant: "horizontal",
36829
- label: t("replyTree.voteOnReplyBy", { author: node.authorName })
36958
+ label: t("replyTree.voteOnReplyBy", { author: authorName })
36830
36959
  }
36831
36960
  ),
36832
36961
  /* @__PURE__ */ jsx(
@@ -36836,7 +36965,7 @@ var init_ReplyTree = __esm({
36836
36965
  size: "sm",
36837
36966
  leftIcon: "message-square",
36838
36967
  onClick: handleReply,
36839
- "aria-label": t("replyTree.replyTo", { author: node.authorName }),
36968
+ "aria-label": t("replyTree.replyTo", { author: authorName }),
36840
36969
  children: t("replyTree.reply")
36841
36970
  }
36842
36971
  ),
@@ -36847,7 +36976,7 @@ var init_ReplyTree = __esm({
36847
36976
  size: "sm",
36848
36977
  leftIcon: "flag",
36849
36978
  onClick: handleFlag,
36850
- "aria-label": t("replyTree.flagReplyBy", { author: node.authorName }),
36979
+ "aria-label": t("replyTree.flagReplyBy", { author: authorName }),
36851
36980
  children: t("replyTree.flag")
36852
36981
  }
36853
36982
  )
@@ -36859,9 +36988,9 @@ var init_ReplyTree = __esm({
36859
36988
  inputType: "textarea",
36860
36989
  rows: 2,
36861
36990
  value: draft,
36862
- placeholder: t("replyTree.replyToPlaceholder", { author: node.authorName }),
36991
+ placeholder: t("replyTree.replyToPlaceholder", { author: authorName }),
36863
36992
  onChange: (e) => setDraft(e.target.value),
36864
- "aria-label": t("replyTree.replyTo", { author: node.authorName })
36993
+ "aria-label": t("replyTree.replyTo", { author: authorName })
36865
36994
  }
36866
36995
  ),
36867
36996
  /* @__PURE__ */ jsxs(Box, { className: "flex flex-row gap-2 items-center", children: [
@@ -36892,7 +37021,7 @@ var init_ReplyTree = __esm({
36892
37021
  ),
36893
37022
  children: t("replyTree.continueThread")
36894
37023
  }
36895
- ) : /* @__PURE__ */ jsx(Box, { className: "flex flex-col gap-2 mt-1", children: node.replies.map((child) => /* @__PURE__ */ jsx(
37024
+ ) : /* @__PURE__ */ jsx(Box, { className: "flex flex-col gap-2 mt-1", children: replies.map((child) => /* @__PURE__ */ jsx(
36896
37025
  ReplyTreeNode,
36897
37026
  {
36898
37027
  node: child,
@@ -38803,8 +38932,8 @@ var init_WizardContainer = __esm({
38803
38932
  return void 0;
38804
38933
  if (typeof controlledStep === "number") return controlledStep;
38805
38934
  if (typeof controlledStep === "string") return parseInt(controlledStep, 10);
38806
- const num = Number(controlledStep);
38807
- return isNaN(num) ? void 0 : num;
38935
+ const num2 = Number(controlledStep);
38936
+ return isNaN(num2) ? void 0 : num2;
38808
38937
  })();
38809
38938
  const currentStep = normalizedControlledStep !== void 0 ? normalizedControlledStep : internalStep;
38810
38939
  const totalSteps = steps.length;
@@ -40509,7 +40638,7 @@ function DebuggerBoard({
40509
40638
  }) {
40510
40639
  const { emit } = useEventBus();
40511
40640
  const { t } = useTranslate();
40512
- const resolved = Array.isArray(entity) ? entity[0] : entity;
40641
+ const resolved = boardEntity(entity);
40513
40642
  const [flaggedLines, setFlaggedLines] = useState(/* @__PURE__ */ new Set());
40514
40643
  const [headerError, setHeaderError] = useState(false);
40515
40644
  const [submitted, setSubmitted] = useState(false);
@@ -40527,7 +40656,7 @@ function DebuggerBoard({
40527
40656
  return next;
40528
40657
  });
40529
40658
  };
40530
- const lines = resolved?.lines ?? [];
40659
+ const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
40531
40660
  const bugLines = lines.filter((l) => l.isBug);
40532
40661
  const correctFlags = lines.filter((l) => l.isBug && flaggedLines.has(l.id));
40533
40662
  const falseFlags = lines.filter((l) => !l.isBug && flaggedLines.has(l.id));
@@ -40542,7 +40671,7 @@ function DebuggerBoard({
40542
40671
  }, [correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
40543
40672
  const handleReset = () => {
40544
40673
  setSubmitted(false);
40545
- if (attempts >= 2 && resolved?.hint) {
40674
+ if (attempts >= 2 && str(resolved?.hint)) {
40546
40675
  setShowHint(true);
40547
40676
  }
40548
40677
  };
@@ -40553,24 +40682,28 @@ function DebuggerBoard({
40553
40682
  setShowHint(false);
40554
40683
  };
40555
40684
  if (!resolved) return null;
40685
+ const theme = resolved.theme ?? void 0;
40686
+ const themeBackground = theme?.background;
40687
+ const headerImage = str(resolved.headerImage);
40688
+ const hint = str(resolved.hint);
40556
40689
  return /* @__PURE__ */ jsx(
40557
40690
  Box,
40558
40691
  {
40559
40692
  className,
40560
40693
  style: {
40561
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
40694
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
40562
40695
  backgroundSize: "cover",
40563
40696
  backgroundPosition: "center"
40564
40697
  },
40565
40698
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
40566
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
40699
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
40567
40700
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
40568
40701
  /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
40569
40702
  /* @__PURE__ */ jsx(Icon, { icon: Bug, size: "sm" }),
40570
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title })
40703
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) })
40571
40704
  ] }),
40572
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description }),
40573
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(resolved.bugCount) }) })
40705
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) }),
40706
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(num(resolved.bugCount)) }) })
40574
40707
  ] }) }),
40575
40708
  /* @__PURE__ */ jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsx(VStack, { gap: "none", children: lines.map((line, i) => {
40576
40709
  const isFlagged = flaggedLines.has(line.id);
@@ -40602,7 +40735,7 @@ function DebuggerBoard({
40602
40735
  );
40603
40736
  }) }) }),
40604
40737
  submitted && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
40605
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("debugger.allFound") : `${correctFlags.length}/${bugLines.length} ${t("debugger.bugsFound")}` }),
40738
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("debugger.allFound") : `${correctFlags.length}/${bugLines.length} ${t("debugger.bugsFound")}` }),
40606
40739
  bugLines.map((line) => /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "start", children: [
40607
40740
  /* @__PURE__ */ jsx(
40608
40741
  Icon,
@@ -40618,7 +40751,7 @@ function DebuggerBoard({
40618
40751
  ] })
40619
40752
  ] }, line.id))
40620
40753
  ] }) }),
40621
- showHint && resolved.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
40754
+ showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
40622
40755
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
40623
40756
  !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.size === 0, children: [
40624
40757
  /* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
@@ -40637,6 +40770,7 @@ var init_DebuggerBoard = __esm({
40637
40770
  "components/game/organisms/puzzles/debugger/DebuggerBoard.tsx"() {
40638
40771
  init_atoms2();
40639
40772
  init_useEventBus();
40773
+ init_boardEntity();
40640
40774
  DebuggerBoard.displayName = "DebuggerBoard";
40641
40775
  }
40642
40776
  });
@@ -40674,7 +40808,7 @@ function getBadgeVariant(fieldName, value) {
40674
40808
  return "default";
40675
40809
  }
40676
40810
  function formatFieldLabel(fieldName) {
40677
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase());
40811
+ return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str2) => str2.toUpperCase());
40678
40812
  }
40679
40813
  function formatFieldValue(value, fieldName) {
40680
40814
  if (typeof value === "number") {
@@ -40693,26 +40827,26 @@ function formatFieldValue(value, fieldName) {
40693
40827
  }
40694
40828
  function renderRichFieldValue(value, fieldName, fieldType) {
40695
40829
  if (value === void 0 || value === null) return "\u2014";
40696
- const str = String(value);
40830
+ const str2 = String(value);
40697
40831
  switch (fieldType) {
40698
40832
  case "image":
40699
40833
  case "url": {
40700
- if (str.match(/\.(png|jpe?g|gif|svg|webp|avif)(\?|$)/i) || str.startsWith("data:image/")) {
40834
+ if (str2.match(/\.(png|jpe?g|gif|svg|webp|avif)(\?|$)/i) || str2.startsWith("data:image/")) {
40701
40835
  return /* @__PURE__ */ jsx(Box, { className: "mt-1 max-w-full", children: /* @__PURE__ */ jsx(
40702
40836
  "img",
40703
40837
  {
40704
- src: str,
40838
+ src: str2,
40705
40839
  alt: formatFieldLabel(fieldName),
40706
40840
  className: "max-w-full max-h-64 rounded-md object-contain",
40707
40841
  loading: "lazy"
40708
40842
  }
40709
40843
  ) });
40710
40844
  }
40711
- return str;
40845
+ return str2;
40712
40846
  }
40713
40847
  case "markdown":
40714
40848
  case "richtext":
40715
- return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(Typography, { variant: "body", className: "break-words", children: str }), children: /* @__PURE__ */ jsx(
40849
+ return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(Typography, { variant: "body", className: "break-words", children: str2 }), children: /* @__PURE__ */ jsx(
40716
40850
  Box,
40717
40851
  {
40718
40852
  className: "prose prose-sm max-w-none",
@@ -40730,11 +40864,11 @@ function renderRichFieldValue(value, fieldName, fieldType) {
40730
40864
  "--tw-prose-th-borders": "var(--color-border)",
40731
40865
  "--tw-prose-td-borders": "var(--color-border)"
40732
40866
  },
40733
- children: /* @__PURE__ */ jsx(ReactMarkdown2, { children: str })
40867
+ children: /* @__PURE__ */ jsx(ReactMarkdown2, { children: str2 })
40734
40868
  }
40735
40869
  ) });
40736
40870
  case "code":
40737
- return /* @__PURE__ */ jsx(Box, { className: "mt-1 rounded-md bg-muted p-3 overflow-x-auto", children: /* @__PURE__ */ jsx("pre", { className: "text-sm font-mono whitespace-pre-wrap break-words m-0", children: /* @__PURE__ */ jsx("code", { children: str }) }) });
40871
+ return /* @__PURE__ */ jsx(Box, { className: "mt-1 rounded-md bg-muted p-3 overflow-x-auto", children: /* @__PURE__ */ jsx("pre", { className: "text-sm font-mono whitespace-pre-wrap break-words m-0", children: /* @__PURE__ */ jsx("code", { children: str2 }) }) });
40738
40872
  case "html":
40739
40873
  return /* @__PURE__ */ jsx(
40740
40874
  Box,
@@ -40754,12 +40888,12 @@ function renderRichFieldValue(value, fieldName, fieldType) {
40754
40888
  "--tw-prose-th-borders": "var(--color-border)",
40755
40889
  "--tw-prose-td-borders": "var(--color-border)"
40756
40890
  },
40757
- children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: str })
40891
+ children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: str2 })
40758
40892
  }
40759
40893
  );
40760
40894
  case "date":
40761
40895
  case "datetime": {
40762
- const d = new Date(str);
40896
+ const d = new Date(str2);
40763
40897
  if (!isNaN(d.getTime())) {
40764
40898
  return d.toLocaleDateString(void 0, {
40765
40899
  year: "numeric",
@@ -40768,7 +40902,7 @@ function renderRichFieldValue(value, fieldName, fieldType) {
40768
40902
  ...fieldType === "datetime" ? { hour: "2-digit", minute: "2-digit" } : {}
40769
40903
  });
40770
40904
  }
40771
- return str;
40905
+ return str2;
40772
40906
  }
40773
40907
  default:
40774
40908
  return formatFieldValue(value, fieldName);
@@ -41446,6 +41580,40 @@ var init_RuleEditor = __esm({
41446
41580
  RuleEditor.displayName = "RuleEditor";
41447
41581
  }
41448
41582
  });
41583
+
41584
+ // components/game/organisms/puzzles/event-handler/puzzleObject.ts
41585
+ function objId(o) {
41586
+ return o.id == null ? "" : String(o.id);
41587
+ }
41588
+ function objName(o) {
41589
+ return o.name == null ? "" : String(o.name);
41590
+ }
41591
+ function objIcon(o) {
41592
+ return o.icon == null ? "" : String(o.icon);
41593
+ }
41594
+ function objStates(o) {
41595
+ return Array.isArray(o.states) ? o.states : [];
41596
+ }
41597
+ function objCurrentState(o) {
41598
+ return o.currentState == null ? "" : String(o.currentState);
41599
+ }
41600
+ function objAvailableEvents(o) {
41601
+ return Array.isArray(o.availableEvents) ? o.availableEvents : [];
41602
+ }
41603
+ function objAvailableActions(o) {
41604
+ return Array.isArray(o.availableActions) ? o.availableActions : [];
41605
+ }
41606
+ function objRules(o) {
41607
+ return Array.isArray(o.rules) ? o.rules : [];
41608
+ }
41609
+ function objMaxRules(o) {
41610
+ const n = Number(o.maxRules);
41611
+ return Number.isFinite(n) && n > 0 ? n : 3;
41612
+ }
41613
+ var init_puzzleObject = __esm({
41614
+ "components/game/organisms/puzzles/event-handler/puzzleObject.ts"() {
41615
+ }
41616
+ });
41449
41617
  function ObjectRulePanel({
41450
41618
  object,
41451
41619
  onRulesChange,
@@ -41453,55 +41621,63 @@ function ObjectRulePanel({
41453
41621
  className
41454
41622
  }) {
41455
41623
  const { t } = useTranslate();
41456
- const maxRules = object.maxRules || 3;
41457
- const canAdd = object.rules.length < maxRules;
41624
+ const id = objId(object);
41625
+ const name = objName(object);
41626
+ const icon = objIcon(object);
41627
+ const states = objStates(object);
41628
+ const currentState = objCurrentState(object);
41629
+ const availableEvents = objAvailableEvents(object);
41630
+ const availableActions = objAvailableActions(object);
41631
+ const rules = objRules(object);
41632
+ const maxRules = objMaxRules(object);
41633
+ const canAdd = rules.length < maxRules;
41458
41634
  const handleRuleChange = useCallback((index, updatedRule) => {
41459
- const newRules = [...object.rules];
41635
+ const newRules = [...rules];
41460
41636
  newRules[index] = updatedRule;
41461
- onRulesChange(object.id, newRules);
41462
- }, [object.id, object.rules, onRulesChange]);
41637
+ onRulesChange(id, newRules);
41638
+ }, [id, rules, onRulesChange]);
41463
41639
  const handleRuleRemove = useCallback((index) => {
41464
- const newRules = object.rules.filter((_, i) => i !== index);
41465
- onRulesChange(object.id, newRules);
41466
- }, [object.id, object.rules, onRulesChange]);
41640
+ const newRules = rules.filter((_, i) => i !== index);
41641
+ onRulesChange(id, newRules);
41642
+ }, [id, rules, onRulesChange]);
41467
41643
  const handleAddRule = useCallback(() => {
41468
41644
  if (!canAdd || disabled) return;
41469
- const firstEvent = object.availableEvents[0]?.value || "";
41470
- const firstAction = object.availableActions[0]?.value || "";
41645
+ const firstEvent = availableEvents[0]?.value || "";
41646
+ const firstAction = availableActions[0]?.value || "";
41471
41647
  const newRule = {
41472
41648
  id: `rule-${nextRuleId++}`,
41473
41649
  whenEvent: firstEvent,
41474
41650
  thenAction: firstAction
41475
41651
  };
41476
- onRulesChange(object.id, [...object.rules, newRule]);
41477
- }, [canAdd, disabled, object, onRulesChange]);
41652
+ onRulesChange(id, [...rules, newRule]);
41653
+ }, [canAdd, disabled, id, rules, availableEvents, availableActions, onRulesChange]);
41478
41654
  const machine = {
41479
- name: object.name,
41480
- states: object.states,
41481
- currentState: object.currentState,
41482
- transitions: object.rules.map((r2) => ({
41483
- from: object.currentState,
41484
- to: object.states.find((s) => s !== object.currentState) || object.currentState,
41655
+ name,
41656
+ states,
41657
+ currentState,
41658
+ transitions: rules.map((r2) => ({
41659
+ from: currentState,
41660
+ to: states.find((s) => s !== currentState) || currentState,
41485
41661
  event: r2.whenEvent
41486
41662
  }))
41487
41663
  };
41488
41664
  return /* @__PURE__ */ jsxs(VStack, { className: cn("p-4 rounded-lg bg-card border border-border", className), gap: "sm", children: [
41489
41665
  /* @__PURE__ */ jsxs(HStack, { className: "items-center", gap: "sm", children: [
41490
- /* @__PURE__ */ jsx(Typography, { variant: "h5", children: object.icon }),
41666
+ /* @__PURE__ */ jsx(Typography, { variant: "h5", children: icon }),
41491
41667
  /* @__PURE__ */ jsxs(VStack, { gap: "none", children: [
41492
- /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-bold", children: object.name }),
41493
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("eventHandler.state") + ": " + object.currentState })
41668
+ /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-bold", children: name }),
41669
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("eventHandler.state") + ": " + currentState })
41494
41670
  ] })
41495
41671
  ] }),
41496
41672
  /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" }),
41497
41673
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
41498
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.rules", { count: object.rules.length, max: maxRules }) + ":" }),
41499
- object.rules.map((rule, i) => /* @__PURE__ */ jsx(
41674
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.rules", { count: rules.length, max: maxRules }) + ":" }),
41675
+ rules.map((rule, i) => /* @__PURE__ */ jsx(
41500
41676
  RuleEditor,
41501
41677
  {
41502
41678
  rule,
41503
- availableEvents: object.availableEvents,
41504
- availableActions: object.availableActions,
41679
+ availableEvents,
41680
+ availableActions,
41505
41681
  onChange: (r2) => handleRuleChange(i, r2),
41506
41682
  onRemove: () => handleRuleRemove(i),
41507
41683
  disabled
@@ -41519,6 +41695,7 @@ var init_ObjectRulePanel = __esm({
41519
41695
  init_cn();
41520
41696
  init_TraitStateViewer();
41521
41697
  init_RuleEditor();
41698
+ init_puzzleObject();
41522
41699
  nextRuleId = 1;
41523
41700
  ObjectRulePanel.displayName = "ObjectRulePanel";
41524
41701
  }
@@ -41585,11 +41762,11 @@ function EventHandlerBoard({
41585
41762
  }) {
41586
41763
  const { emit } = useEventBus();
41587
41764
  const { t } = useTranslate();
41588
- const resolved = Array.isArray(entity) ? entity[0] : entity;
41589
- const entityObjects = resolved?.objects ?? [];
41590
- const [objects, setObjects] = useState(entityObjects);
41765
+ const resolved = boardEntity(entity);
41766
+ const entityObjects = rows(resolved?.objects);
41767
+ const [objects, setObjects] = useState(() => [...entityObjects]);
41591
41768
  const [selectedObjectId, setSelectedObjectId] = useState(
41592
- entityObjects[0]?.id || null
41769
+ entityObjects[0] ? objId(entityObjects[0]) : null
41593
41770
  );
41594
41771
  const [headerError, setHeaderError] = useState(false);
41595
41772
  const [playState, setPlayState] = useState("editing");
@@ -41600,10 +41777,10 @@ function EventHandlerBoard({
41600
41777
  useEffect(() => () => {
41601
41778
  if (timerRef.current) clearTimeout(timerRef.current);
41602
41779
  }, []);
41603
- const selectedObject = objects.find((o) => o.id === selectedObjectId) || null;
41780
+ const selectedObject = objects.find((o) => objId(o) === selectedObjectId) || null;
41604
41781
  const handleRulesChange = useCallback((objectId, rules) => {
41605
41782
  setObjects((prev) => prev.map(
41606
- (o) => o.id === objectId ? { ...o, rules } : o
41783
+ (o) => objId(o) === objectId ? { ...o, rules } : o
41607
41784
  ));
41608
41785
  }, []);
41609
41786
  const addLogEntry = useCallback((icon, message, status = "done") => {
@@ -41617,11 +41794,12 @@ function EventHandlerBoard({
41617
41794
  setEventLog([]);
41618
41795
  const allRules = [];
41619
41796
  objects.forEach((obj) => {
41620
- obj.rules.forEach((rule) => {
41797
+ objRules(obj).forEach((rule) => {
41621
41798
  allRules.push({ object: obj, rule });
41622
41799
  });
41623
41800
  });
41624
- const triggers = resolved?.triggerEvents || [];
41801
+ const triggers = Array.isArray(resolved?.triggerEvents) ? resolved.triggerEvents : [];
41802
+ const goalEvent = str(resolved?.goalEvent);
41625
41803
  const eventQueue = [...triggers];
41626
41804
  const firedEvents = /* @__PURE__ */ new Set();
41627
41805
  let stepIdx = 0;
@@ -41650,14 +41828,14 @@ function EventHandlerBoard({
41650
41828
  addLogEntry("\u26A1", t("eventHandler.noListeners", { event: currentEvent }), "done");
41651
41829
  } else {
41652
41830
  matching.forEach(({ object, rule }) => {
41653
- addLogEntry(object.icon, t("eventHandler.heardEvent", { object: object.name, event: currentEvent, action: rule.thenAction }), "done");
41831
+ addLogEntry(objIcon(object), t("eventHandler.heardEvent", { object: objName(object), event: currentEvent, action: rule.thenAction }), "done");
41654
41832
  eventQueue.push(rule.thenAction);
41655
- if (rule.thenAction === resolved?.goalEvent) {
41833
+ if (rule.thenAction === goalEvent) {
41656
41834
  goalReached = true;
41657
41835
  }
41658
41836
  });
41659
41837
  }
41660
- if (currentEvent === resolved?.goalEvent) {
41838
+ if (currentEvent === goalEvent) {
41661
41839
  goalReached = true;
41662
41840
  }
41663
41841
  stepIdx++;
@@ -41675,65 +41853,75 @@ function EventHandlerBoard({
41675
41853
  }, []);
41676
41854
  const handleReset = useCallback(() => {
41677
41855
  if (timerRef.current) clearTimeout(timerRef.current);
41678
- setObjects(resolved?.objects ?? []);
41856
+ const resetObjects = rows(resolved?.objects);
41857
+ setObjects([...resetObjects]);
41679
41858
  setPlayState("editing");
41680
41859
  setEventLog([]);
41681
- setSelectedObjectId((resolved?.objects ?? [])[0]?.id || null);
41860
+ setSelectedObjectId(resetObjects[0] ? objId(resetObjects[0]) : null);
41682
41861
  setAttempts(0);
41683
41862
  }, [resolved?.objects]);
41684
41863
  if (!resolved) return null;
41685
41864
  const objectViewers = objects.map((obj) => {
41865
+ const states = objStates(obj);
41866
+ const currentState = objCurrentState(obj);
41686
41867
  const machine = {
41687
- name: obj.name,
41688
- states: obj.states,
41689
- currentState: obj.currentState,
41690
- transitions: obj.rules.map((r2) => ({
41691
- from: obj.currentState,
41692
- to: obj.states.find((s) => s !== obj.currentState) || obj.currentState,
41868
+ name: objName(obj),
41869
+ states,
41870
+ currentState,
41871
+ transitions: objRules(obj).map((r2) => ({
41872
+ from: currentState,
41873
+ to: states.find((s) => s !== currentState) || currentState,
41693
41874
  event: r2.whenEvent
41694
41875
  }))
41695
41876
  };
41696
41877
  return { obj, machine };
41697
41878
  });
41698
- const showHint = attempts >= 3 && resolved.hint;
41879
+ const hint = str(resolved.hint);
41880
+ const showHint = attempts >= 3 && hint;
41881
+ const theme = resolved.theme ?? void 0;
41882
+ const themeBackground = theme?.background;
41883
+ const headerImage = str(resolved.headerImage);
41699
41884
  const encourageKey = ENCOURAGEMENT_KEYS[Math.min(attempts - 1, ENCOURAGEMENT_KEYS.length - 1)] ?? ENCOURAGEMENT_KEYS[0];
41700
41885
  return /* @__PURE__ */ jsxs(
41701
41886
  VStack,
41702
41887
  {
41703
41888
  className: cn("p-4 gap-6", className),
41704
41889
  style: {
41705
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
41890
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
41706
41891
  backgroundSize: "cover",
41707
41892
  backgroundPosition: "center"
41708
41893
  },
41709
41894
  children: [
41710
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
41895
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
41711
41896
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
41712
- /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
41713
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description }),
41897
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
41898
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) }),
41714
41899
  /* @__PURE__ */ jsxs(HStack, { className: "items-center p-2 rounded bg-primary/10 border border-primary/30", gap: "xs", children: [
41715
41900
  /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-primary font-bold", children: t("game.goal") + ":" }),
41716
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: resolved.goalCondition })
41901
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: str(resolved.goalCondition) })
41717
41902
  ] })
41718
41903
  ] }),
41719
41904
  /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
41720
41905
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.clickObject") + ":" }),
41721
- /* @__PURE__ */ jsx(HStack, { className: "flex-wrap", gap: "sm", children: objectViewers.map(({ obj, machine }) => /* @__PURE__ */ jsx(
41722
- Box,
41723
- {
41724
- className: cn(
41725
- "p-3 rounded-container border-2 cursor-pointer transition-all hover:scale-105",
41726
- selectedObjectId === obj.id ? "border-primary bg-primary/10" : "border-border bg-card hover:border-muted-foreground"
41727
- ),
41728
- onClick: () => setSelectedObjectId(obj.id),
41729
- children: /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "items-center min-w-[120px]", children: [
41730
- /* @__PURE__ */ jsx(Typography, { variant: "h5", children: obj.icon }),
41731
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground font-medium", children: obj.name }),
41732
- /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" })
41733
- ] })
41734
- },
41735
- obj.id
41736
- )) })
41906
+ /* @__PURE__ */ jsx(HStack, { className: "flex-wrap", gap: "sm", children: objectViewers.map(({ obj, machine }) => {
41907
+ const oid = objId(obj);
41908
+ return /* @__PURE__ */ jsx(
41909
+ Box,
41910
+ {
41911
+ className: cn(
41912
+ "p-3 rounded-container border-2 cursor-pointer transition-all hover:scale-105",
41913
+ selectedObjectId === oid ? "border-primary bg-primary/10" : "border-border bg-card hover:border-muted-foreground"
41914
+ ),
41915
+ onClick: () => setSelectedObjectId(oid),
41916
+ children: /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "items-center min-w-[120px]", children: [
41917
+ /* @__PURE__ */ jsx(Typography, { variant: "h5", children: objIcon(obj) }),
41918
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground font-medium", children: objName(obj) }),
41919
+ /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" })
41920
+ ] })
41921
+ },
41922
+ oid
41923
+ );
41924
+ }) })
41737
41925
  ] }),
41738
41926
  selectedObject && /* @__PURE__ */ jsx(
41739
41927
  ObjectRulePanel,
@@ -41744,12 +41932,12 @@ function EventHandlerBoard({
41744
41932
  }
41745
41933
  ),
41746
41934
  eventLog.length > 0 && /* @__PURE__ */ jsx(EventLog, { entries: eventLog }),
41747
- playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: resolved.successMessage || t("eventHandler.chainComplete") }) }),
41935
+ playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("eventHandler.chainComplete") }) }),
41748
41936
  playState === "fail" && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
41749
41937
  /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(encourageKey) }) }),
41750
41938
  showHint && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
41751
41939
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
41752
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
41940
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
41753
41941
  ] }) })
41754
41942
  ] }),
41755
41943
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
@@ -41777,6 +41965,8 @@ var init_EventHandlerBoard = __esm({
41777
41965
  init_TraitStateViewer();
41778
41966
  init_ObjectRulePanel();
41779
41967
  init_EventLog();
41968
+ init_puzzleObject();
41969
+ init_boardEntity();
41780
41970
  ENCOURAGEMENT_KEYS = [
41781
41971
  "puzzle.tryAgain1",
41782
41972
  "puzzle.tryAgain2",
@@ -41865,7 +42055,10 @@ var init_FeatureGridOrganism = __esm({
41865
42055
  );
41866
42056
  useCallback(
41867
42057
  (feature) => {
41868
- eventBus.emit("UI:FEATURE_CLICK", { id: feature.id, href: feature.href ?? "" });
42058
+ eventBus.emit("UI:FEATURE_CLICK", {
42059
+ id: String(feature.id ?? ""),
42060
+ href: String(feature.href ?? "")
42061
+ });
41869
42062
  },
41870
42063
  [eventBus]
41871
42064
  );
@@ -41875,14 +42068,17 @@ var init_FeatureGridOrganism = __esm({
41875
42068
  if (error) {
41876
42069
  return /* @__PURE__ */ jsx(ErrorState, { message: error.message, className });
41877
42070
  }
41878
- const featureCards = items.map((feature) => ({
41879
- icon: feature.icon,
41880
- title: feature.title,
41881
- description: feature.description,
41882
- href: feature.href,
41883
- linkLabel: feature.linkLabel,
41884
- variant: feature.href ? "interactive" : "bordered"
41885
- }));
42071
+ const featureCards = items.map((feature) => {
42072
+ const href = feature.href != null ? String(feature.href) : void 0;
42073
+ return {
42074
+ icon: feature.icon != null ? String(feature.icon) : void 0,
42075
+ title: String(feature.title ?? ""),
42076
+ description: String(feature.description ?? ""),
42077
+ href,
42078
+ linkLabel: feature.linkLabel != null ? String(feature.linkLabel) : void 0,
42079
+ variant: href ? "interactive" : "bordered"
42080
+ };
42081
+ });
41886
42082
  return /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
41887
42083
  (heading || subtitle) && /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
41888
42084
  heading && /* @__PURE__ */ jsx(Typography, { variant: "h2", align: "center", children: heading }),
@@ -43200,22 +43396,24 @@ var init_HeroOrganism = __esm({
43200
43396
  () => Array.isArray(entity) ? entity[0] : entity && typeof entity === "object" ? entity : void 0,
43201
43397
  [entity]
43202
43398
  );
43399
+ const primaryAction = resolved?.primaryAction;
43400
+ const secondaryAction = resolved?.secondaryAction;
43203
43401
  const handlePrimaryClick = useCallback(() => {
43204
- if (resolved?.primaryAction) {
43402
+ if (primaryAction) {
43205
43403
  eventBus.emit("UI:CTA_PRIMARY", {
43206
- label: resolved.primaryAction.label,
43207
- href: resolved.primaryAction.href
43404
+ label: String(primaryAction.label ?? ""),
43405
+ href: String(primaryAction.href ?? "")
43208
43406
  });
43209
43407
  }
43210
- }, [eventBus, resolved]);
43408
+ }, [eventBus, primaryAction]);
43211
43409
  const handleSecondaryClick = useCallback(() => {
43212
- if (resolved?.secondaryAction) {
43410
+ if (secondaryAction) {
43213
43411
  eventBus.emit("UI:CTA_SECONDARY", {
43214
- label: resolved.secondaryAction.label,
43215
- href: resolved.secondaryAction.href
43412
+ label: String(secondaryAction.label ?? ""),
43413
+ href: String(secondaryAction.href ?? "")
43216
43414
  });
43217
43415
  }
43218
- }, [eventBus, resolved]);
43416
+ }, [eventBus, secondaryAction]);
43219
43417
  if (isLoading) {
43220
43418
  return /* @__PURE__ */ jsx(LoadingState, { message: t("common.loading"), className });
43221
43419
  }
@@ -43225,17 +43423,19 @@ var init_HeroOrganism = __esm({
43225
43423
  if (!resolved) {
43226
43424
  return null;
43227
43425
  }
43426
+ const imageRaw = resolved.image;
43427
+ const image = imageRaw ? { src: String(imageRaw.src ?? ""), alt: String(imageRaw.alt ?? "") } : void 0;
43228
43428
  return /* @__PURE__ */ jsxs(
43229
43429
  HeroSection,
43230
43430
  {
43231
- tag: resolved.tag,
43232
- title: resolved.title,
43233
- titleAccent: resolved.titleAccent,
43234
- subtitle: resolved.subtitle,
43235
- primaryAction: resolved.primaryAction ? { label: resolved.primaryAction.label, href: resolved.primaryAction.href } : void 0,
43236
- secondaryAction: resolved.secondaryAction ? { label: resolved.secondaryAction.label, href: resolved.secondaryAction.href } : void 0,
43237
- installCommand: resolved.installCommand,
43238
- image: resolved.image,
43431
+ tag: resolved.tag != null ? String(resolved.tag) : void 0,
43432
+ title: String(resolved.title ?? ""),
43433
+ titleAccent: resolved.titleAccent != null ? String(resolved.titleAccent) : void 0,
43434
+ subtitle: String(resolved.subtitle ?? ""),
43435
+ primaryAction: primaryAction ? { label: String(primaryAction.label ?? ""), href: String(primaryAction.href ?? "") } : void 0,
43436
+ secondaryAction: secondaryAction ? { label: String(secondaryAction.label ?? ""), href: String(secondaryAction.href ?? "") } : void 0,
43437
+ installCommand: resolved.installCommand != null ? String(resolved.installCommand) : void 0,
43438
+ image,
43239
43439
  imagePosition: resolved.imagePosition,
43240
43440
  background: resolved.background,
43241
43441
  className: cn(className),
@@ -43244,8 +43444,8 @@ var init_HeroOrganism = __esm({
43244
43444
  /* @__PURE__ */ jsx(
43245
43445
  _HeroClickInterceptor,
43246
43446
  {
43247
- hasPrimary: !!resolved.primaryAction,
43248
- hasSecondary: !!resolved.secondaryAction,
43447
+ hasPrimary: !!primaryAction,
43448
+ hasSecondary: !!secondaryAction,
43249
43449
  onPrimaryClick: handlePrimaryClick,
43250
43450
  onSecondaryClick: handleSecondaryClick
43251
43451
  }
@@ -43474,7 +43674,7 @@ function formatValue3(value, fieldName) {
43474
43674
  return String(value);
43475
43675
  }
43476
43676
  function formatFieldLabel2(fieldName) {
43477
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).replace(/Id$/, "").trim();
43677
+ return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str2) => str2.toUpperCase()).replace(/Id$/, "").trim();
43478
43678
  }
43479
43679
  var STATUS_STYLES2, StatusBadge, ProgressIndicator, List3;
43480
43680
  var init_List = __esm({
@@ -44321,20 +44521,22 @@ function NegotiatorBoard({
44321
44521
  }) {
44322
44522
  const { emit } = useEventBus();
44323
44523
  const { t } = useTranslate();
44324
- const resolved = Array.isArray(entity) ? entity[0] : entity;
44524
+ const resolved = boardEntity(entity);
44325
44525
  const [history, setHistory] = useState([]);
44326
44526
  const [headerError, setHeaderError] = useState(false);
44327
44527
  const [showHint, setShowHint] = useState(false);
44528
+ const totalRounds = num(resolved?.totalRounds);
44529
+ const targetScore = num(resolved?.targetScore);
44328
44530
  const currentRound = history.length;
44329
- const isComplete = currentRound >= (resolved?.totalRounds ?? 0);
44531
+ const isComplete = currentRound >= totalRounds;
44330
44532
  const playerTotal = history.reduce((s, r2) => s + r2.playerPayoff, 0);
44331
44533
  const opponentTotal = history.reduce((s, r2) => s + r2.opponentPayoff, 0);
44332
- const won = isComplete && playerTotal >= (resolved?.targetScore ?? 0);
44333
- const actions = resolved?.actions ?? [];
44334
- const payoffMatrix = resolved?.payoffMatrix ?? [];
44534
+ const won = isComplete && playerTotal >= targetScore;
44535
+ const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
44536
+ const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
44335
44537
  const handleAction = useCallback((actionId) => {
44336
44538
  if (isComplete) return;
44337
- const opponentAction = getOpponentAction(resolved?.opponentStrategy ?? "random", actions, history);
44539
+ const opponentAction = getOpponentAction(str(resolved?.opponentStrategy) || "random", actions, history);
44338
44540
  const payoff = payoffMatrix.find(
44339
44541
  (p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
44340
44542
  );
@@ -44347,42 +44549,46 @@ function NegotiatorBoard({
44347
44549
  };
44348
44550
  const newHistory = [...history, result];
44349
44551
  setHistory(newHistory);
44350
- if (newHistory.length >= (resolved?.totalRounds ?? 0)) {
44552
+ if (newHistory.length >= totalRounds) {
44351
44553
  const total = newHistory.reduce((s, r2) => s + r2.playerPayoff, 0);
44352
- if (total >= (resolved?.targetScore ?? 0)) {
44554
+ if (total >= targetScore) {
44353
44555
  emit(`UI:${completeEvent}`, { success: true, score: total });
44354
44556
  }
44355
- if (newHistory.length >= 3 && resolved?.hint) {
44557
+ if (newHistory.length >= 3 && str(resolved?.hint)) {
44356
44558
  setShowHint(true);
44357
44559
  }
44358
44560
  }
44359
- }, [isComplete, resolved, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
44561
+ }, [isComplete, resolved, totalRounds, targetScore, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
44360
44562
  const handleReset = () => {
44361
44563
  setHistory([]);
44362
44564
  setShowHint(false);
44363
44565
  };
44364
44566
  const getActionLabel = (id) => actions.find((a) => a.id === id)?.label ?? id;
44365
44567
  if (!resolved) return null;
44568
+ const theme = resolved.theme ?? void 0;
44569
+ const themeBackground = theme?.background;
44570
+ const headerImage = str(resolved.headerImage);
44571
+ const hint = str(resolved.hint);
44366
44572
  return /* @__PURE__ */ jsx(
44367
44573
  Box,
44368
44574
  {
44369
44575
  className,
44370
44576
  style: {
44371
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
44577
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
44372
44578
  backgroundSize: "cover",
44373
44579
  backgroundPosition: "center"
44374
44580
  },
44375
44581
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
44376
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
44582
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
44377
44583
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
44378
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
44379
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description }),
44584
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
44585
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) }),
44380
44586
  /* @__PURE__ */ jsxs(HStack, { gap: "md", children: [
44381
- /* @__PURE__ */ jsx(Badge, { size: "sm", children: t("negotiator.round", { current: String(currentRound), total: String(resolved.totalRounds) }) }),
44587
+ /* @__PURE__ */ jsx(Badge, { size: "sm", children: t("negotiator.round", { current: String(currentRound), total: String(totalRounds) }) }),
44382
44588
  /* @__PURE__ */ jsxs(Badge, { size: "sm", children: [
44383
44589
  t("negotiator.target"),
44384
44590
  ": ",
44385
- resolved.targetScore
44591
+ targetScore
44386
44592
  ] })
44387
44593
  ] })
44388
44594
  ] }) }),
@@ -44431,16 +44637,16 @@ function NegotiatorBoard({
44431
44637
  ] }) }),
44432
44638
  isComplete && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
44433
44639
  /* @__PURE__ */ jsx(Icon, { icon: CheckCircle, size: "lg", className: won ? "text-success" : "text-error" }),
44434
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: won ? resolved.successMessage ?? t("negotiator.success") : resolved.failMessage ?? t("negotiator.failed") }),
44640
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: won ? str(resolved.successMessage) || t("negotiator.success") : str(resolved.failMessage) || t("negotiator.failed") }),
44435
44641
  /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
44436
44642
  t("negotiator.finalScore"),
44437
44643
  ": ",
44438
44644
  playerTotal,
44439
44645
  "/",
44440
- resolved.targetScore
44646
+ targetScore
44441
44647
  ] })
44442
44648
  ] }) }),
44443
- showHint && resolved.hint && !won && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
44649
+ showHint && hint && !won && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
44444
44650
  isComplete && !won && /* @__PURE__ */ jsx(HStack, { justify: "center", children: /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleReset, children: t("negotiator.playAgain") }) })
44445
44651
  ] })
44446
44652
  }
@@ -44450,6 +44656,7 @@ var init_NegotiatorBoard = __esm({
44450
44656
  "components/game/organisms/puzzles/negotiator/NegotiatorBoard.tsx"() {
44451
44657
  init_atoms2();
44452
44658
  init_useEventBus();
44659
+ init_boardEntity();
44453
44660
  NegotiatorBoard.displayName = "NegotiatorBoard";
44454
44661
  }
44455
44662
  });
@@ -44485,13 +44692,13 @@ var init_PricingOrganism = __esm({
44485
44692
  return /* @__PURE__ */ jsx(ErrorState, { message: error.message, className });
44486
44693
  }
44487
44694
  const plans = items.map((plan) => ({
44488
- name: plan.name,
44489
- price: plan.price,
44490
- description: plan.description,
44491
- features: plan.features,
44492
- action: { label: plan.actionLabel, href: plan.actionHref },
44493
- highlighted: plan.highlighted,
44494
- badge: plan.badge
44695
+ name: String(plan.name ?? ""),
44696
+ price: String(plan.price ?? ""),
44697
+ description: plan.description != null ? String(plan.description) : void 0,
44698
+ features: (plan.features ?? []).map((f3) => String(f3)),
44699
+ action: { label: String(plan.actionLabel ?? ""), href: String(plan.actionHref ?? "") },
44700
+ highlighted: Boolean(plan.highlighted),
44701
+ badge: plan.badge != null ? String(plan.badge) : void 0
44495
44702
  }));
44496
44703
  return /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
44497
44704
  (heading || subtitle) && /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
@@ -46581,16 +46788,20 @@ function SequencerBoard({
46581
46788
  }) {
46582
46789
  const { emit } = useEventBus();
46583
46790
  const { t } = useTranslate();
46584
- const resolved = Array.isArray(entity) ? entity[0] : entity;
46791
+ const resolved = boardEntity(entity);
46792
+ const maxSlots = num(resolved?.maxSlots);
46793
+ const solutions = Array.isArray(resolved?.solutions) ? resolved.solutions : [];
46794
+ const availableActions = Array.isArray(resolved?.availableActions) ? resolved.availableActions : [];
46795
+ const allowDuplicates = resolved?.allowDuplicates !== false;
46585
46796
  const [headerError, setHeaderError] = useState(false);
46586
46797
  const [slots, setSlots] = useState(
46587
- () => Array.from({ length: resolved?.maxSlots ?? 0 }, () => void 0)
46798
+ () => Array.from({ length: maxSlots }, () => void 0)
46588
46799
  );
46589
46800
  const [playState, setPlayState] = useState("idle");
46590
46801
  const [currentStep, setCurrentStep] = useState(-1);
46591
46802
  const [attempts, setAttempts] = useState(0);
46592
46803
  const [slotFeedback, setSlotFeedback] = useState(
46593
- () => Array.from({ length: resolved?.maxSlots ?? 0 }, () => null)
46804
+ () => Array.from({ length: maxSlots }, () => null)
46594
46805
  );
46595
46806
  const timerRef = useRef(null);
46596
46807
  useEffect(() => () => {
@@ -46624,17 +46835,17 @@ function SequencerBoard({
46624
46835
  }, [emit]);
46625
46836
  const handleReset = useCallback(() => {
46626
46837
  if (timerRef.current) clearTimeout(timerRef.current);
46627
- setSlots(Array.from({ length: resolved?.maxSlots ?? 0 }, () => void 0));
46838
+ setSlots(Array.from({ length: maxSlots }, () => void 0));
46628
46839
  setPlayState("idle");
46629
46840
  setCurrentStep(-1);
46630
46841
  setAttempts(0);
46631
- setSlotFeedback(Array.from({ length: resolved?.maxSlots ?? 0 }, () => null));
46632
- }, [resolved?.maxSlots]);
46842
+ setSlotFeedback(Array.from({ length: maxSlots }, () => null));
46843
+ }, [maxSlots]);
46633
46844
  const filledSlots = slots.filter((s) => !!s);
46634
46845
  const canPlay = filledSlots.length > 0 && playState === "idle";
46635
46846
  const handlePlay = useCallback(() => {
46636
46847
  if (!canPlay) return;
46637
- setSlotFeedback(Array.from({ length: resolved?.maxSlots ?? 0 }, () => null));
46848
+ setSlotFeedback(Array.from({ length: maxSlots }, () => null));
46638
46849
  emit("UI:PLAY_SOUND", { key: "confirm" });
46639
46850
  const sequence = slots.map((s) => s?.id || "");
46640
46851
  if (playEvent) {
@@ -46645,10 +46856,10 @@ function SequencerBoard({
46645
46856
  let step = 0;
46646
46857
  const advance = () => {
46647
46858
  step++;
46648
- if (step >= (resolved?.maxSlots ?? 0)) {
46859
+ if (step >= maxSlots) {
46649
46860
  const playerSeq = slots.map((s) => s?.id);
46650
46861
  const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
46651
- const success = (resolved?.solutions ?? []).some(
46862
+ const success = solutions.some(
46652
46863
  (sol) => sol.length === playerIds.length && sol.every((id, i) => id === playerIds[i])
46653
46864
  );
46654
46865
  if (success) {
@@ -46660,7 +46871,7 @@ function SequencerBoard({
46660
46871
  }
46661
46872
  } else {
46662
46873
  setAttempts((prev) => prev + 1);
46663
- const feedback = computeSlotFeedback(playerSeq, resolved?.solutions ?? []);
46874
+ const feedback = computeSlotFeedback(playerSeq, solutions);
46664
46875
  setSlotFeedback(feedback);
46665
46876
  setPlayState("idle");
46666
46877
  setCurrentStep(-1);
@@ -46678,10 +46889,10 @@ function SequencerBoard({
46678
46889
  }
46679
46890
  };
46680
46891
  timerRef.current = setTimeout(advance, stepDurationMs);
46681
- }, [canPlay, slots, resolved?.maxSlots, resolved?.solutions, stepDurationMs, playEvent, completeEvent, emit]);
46892
+ }, [canPlay, slots, maxSlots, solutions, stepDurationMs, playEvent, completeEvent, emit]);
46682
46893
  const machine = {
46683
- name: resolved?.title ?? "",
46684
- description: resolved?.description ?? "",
46894
+ name: str(resolved?.title),
46895
+ description: str(resolved?.description),
46685
46896
  states: slots.map((s, i) => stepLabel(s, i)),
46686
46897
  currentState: currentStep >= 0 ? stepLabel(slots[currentStep], currentStep) : "__idle__",
46687
46898
  transitions: slots.slice(0, -1).map((s, i) => ({
@@ -46690,37 +46901,41 @@ function SequencerBoard({
46690
46901
  event: "NEXT"
46691
46902
  }))
46692
46903
  };
46693
- const usedIds = resolved?.allowDuplicates === false ? slots.filter(Boolean).map((s) => s?.id || "") : [];
46694
- const showHint = attempts >= 3 && !!resolved?.hint;
46904
+ const usedIds = !allowDuplicates ? slots.filter(Boolean).map((s) => s?.id || "") : [];
46905
+ const hint = str(resolved?.hint);
46906
+ const showHint = attempts >= 3 && !!hint;
46695
46907
  const hasFeedback = slotFeedback.some((f3) => f3 !== null);
46696
46908
  const correctCount = slotFeedback.filter((f3) => f3 === "correct").length;
46697
46909
  const encourageKey = ENCOURAGEMENT_KEYS2[Math.min(attempts - 1, ENCOURAGEMENT_KEYS2.length - 1)] ?? ENCOURAGEMENT_KEYS2[0];
46698
46910
  if (!resolved) return null;
46911
+ const theme = resolved.theme ?? void 0;
46912
+ const themeBackground = theme?.background;
46913
+ const headerImage = str(resolved.headerImage);
46699
46914
  return /* @__PURE__ */ jsxs(
46700
46915
  VStack,
46701
46916
  {
46702
46917
  className: cn("p-4 gap-6", className),
46703
46918
  style: {
46704
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
46919
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
46705
46920
  backgroundSize: "cover",
46706
46921
  backgroundPosition: "center"
46707
46922
  },
46708
46923
  children: [
46709
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
46924
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
46710
46925
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
46711
- /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
46712
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description })
46926
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
46927
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) })
46713
46928
  ] }),
46714
46929
  showHint && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
46715
46930
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
46716
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
46931
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
46717
46932
  ] }) }),
46718
46933
  filledSlots.length > 0 && /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "linear", size: "md" }),
46719
46934
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
46720
46935
  /* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between", children: [
46721
46936
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("sequencer.yourSequence") + ":" }),
46722
46937
  hasFeedback && playState === "idle" && /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
46723
- `${correctCount}/${resolved.maxSlots} `,
46938
+ `${correctCount}/${maxSlots} `,
46724
46939
  "\u2705"
46725
46940
  ] })
46726
46941
  ] }),
@@ -46728,7 +46943,7 @@ function SequencerBoard({
46728
46943
  SequenceBar,
46729
46944
  {
46730
46945
  slots,
46731
- maxSlots: resolved.maxSlots,
46946
+ maxSlots,
46732
46947
  onSlotDrop: handleSlotDrop,
46733
46948
  onSlotRemove: handleSlotRemove,
46734
46949
  playing: playState === "playing",
@@ -46742,15 +46957,15 @@ function SequencerBoard({
46742
46957
  playState !== "playing" && /* @__PURE__ */ jsx(
46743
46958
  ActionPalette,
46744
46959
  {
46745
- actions: resolved.availableActions,
46960
+ actions: availableActions,
46746
46961
  usedActionIds: usedIds,
46747
- allowDuplicates: resolved.allowDuplicates !== false,
46962
+ allowDuplicates,
46748
46963
  categoryColors,
46749
46964
  label: t("sequencer.dragActions")
46750
46965
  }
46751
46966
  ),
46752
46967
  hasFeedback && playState === "idle" && attempts > 0 && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: t(encourageKey) }) }),
46753
- playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: resolved.successMessage || t("sequencer.levelComplete") }) }),
46968
+ playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("sequencer.levelComplete") }) }),
46754
46969
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
46755
46970
  /* @__PURE__ */ jsx(
46756
46971
  Button,
@@ -46774,6 +46989,7 @@ var init_SequencerBoard = __esm({
46774
46989
  init_cn();
46775
46990
  init_useEventBus();
46776
46991
  init_TraitStateViewer();
46992
+ init_boardEntity();
46777
46993
  init_SequenceBar();
46778
46994
  init_ActionPalette();
46779
46995
  ENCOURAGEMENT_KEYS2 = [
@@ -46823,18 +47039,21 @@ var init_ShowcaseOrganism = __esm({
46823
47039
  heading && /* @__PURE__ */ jsx(Typography, { variant: "h2", align: "center", children: heading }),
46824
47040
  subtitle && /* @__PURE__ */ jsx(Typography, { variant: "body1", color: "muted", align: "center", className: "max-w-2xl", children: subtitle })
46825
47041
  ] }),
46826
- /* @__PURE__ */ jsx(SimpleGrid, { cols: columns, gap: "lg", children: items.map((item) => /* @__PURE__ */ jsx(
46827
- ShowcaseCard,
46828
- {
46829
- title: item.title,
46830
- description: item.description,
46831
- image: item.image,
46832
- href: item.href,
46833
- badge: item.badge,
46834
- accentColor: item.accentColor
46835
- },
46836
- item.id
46837
- )) })
47042
+ /* @__PURE__ */ jsx(SimpleGrid, { cols: columns, gap: "lg", children: items.map((item) => {
47043
+ const imageRaw = item.image;
47044
+ return /* @__PURE__ */ jsx(
47045
+ ShowcaseCard,
47046
+ {
47047
+ title: String(item.title ?? ""),
47048
+ description: item.description != null ? String(item.description) : void 0,
47049
+ image: { src: String(imageRaw?.src ?? ""), alt: String(imageRaw?.alt ?? "") },
47050
+ href: item.href != null ? String(item.href) : void 0,
47051
+ badge: item.badge != null ? String(item.badge) : void 0,
47052
+ accentColor: item.accentColor != null ? String(item.accentColor) : void 0
47053
+ },
47054
+ String(item.id ?? "")
47055
+ );
47056
+ }) })
46838
47057
  ] });
46839
47058
  };
46840
47059
  ShowcaseOrganism.displayName = "ShowcaseOrganism";
@@ -47202,8 +47421,8 @@ function SimulatorBoard({
47202
47421
  }) {
47203
47422
  const { emit } = useEventBus();
47204
47423
  const { t } = useTranslate();
47205
- const resolved = Array.isArray(entity) ? entity[0] : entity;
47206
- const parameters = resolved?.parameters ?? [];
47424
+ const resolved = boardEntity(entity);
47425
+ const parameters = Array.isArray(resolved?.parameters) ? resolved.parameters : [];
47207
47426
  const [values, setValues] = useState(() => {
47208
47427
  const init = {};
47209
47428
  for (const p2 of parameters) {
@@ -47217,15 +47436,15 @@ function SimulatorBoard({
47217
47436
  const [showHint, setShowHint] = useState(false);
47218
47437
  const computeOutput = useCallback((params) => {
47219
47438
  try {
47220
- const fn = new Function("params", `return (${resolved?.computeExpression})`);
47439
+ const fn = new Function("params", `return (${str(resolved?.computeExpression)})`);
47221
47440
  return fn(params);
47222
47441
  } catch {
47223
47442
  return 0;
47224
47443
  }
47225
47444
  }, [resolved?.computeExpression]);
47226
47445
  const output = useMemo(() => computeOutput(values) ?? 0, [computeOutput, values]);
47227
- const targetValue = resolved?.targetValue ?? 0;
47228
- const targetTolerance = resolved?.targetTolerance ?? 0;
47446
+ const targetValue = num(resolved?.targetValue);
47447
+ const targetTolerance = num(resolved?.targetTolerance);
47229
47448
  const isCorrect = Math.abs(output - targetValue) <= targetTolerance;
47230
47449
  const handleParameterChange = (id, value) => {
47231
47450
  if (submitted) return;
@@ -47240,7 +47459,7 @@ function SimulatorBoard({
47240
47459
  };
47241
47460
  const handleReset = () => {
47242
47461
  setSubmitted(false);
47243
- if (attempts >= 2 && resolved?.hint) {
47462
+ if (attempts >= 2 && str(resolved?.hint)) {
47244
47463
  setShowHint(true);
47245
47464
  }
47246
47465
  };
@@ -47255,20 +47474,26 @@ function SimulatorBoard({
47255
47474
  setShowHint(false);
47256
47475
  };
47257
47476
  if (!resolved) return null;
47477
+ const theme = resolved.theme ?? void 0;
47478
+ const themeBackground = theme?.background;
47479
+ const headerImage = str(resolved.headerImage);
47480
+ const hint = str(resolved.hint);
47481
+ const outputLabel = str(resolved.outputLabel);
47482
+ const outputUnit = str(resolved.outputUnit);
47258
47483
  return /* @__PURE__ */ jsx(
47259
47484
  Box,
47260
47485
  {
47261
47486
  className,
47262
47487
  style: {
47263
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
47488
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
47264
47489
  backgroundSize: "cover",
47265
47490
  backgroundPosition: "center"
47266
47491
  },
47267
47492
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
47268
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
47493
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
47269
47494
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
47270
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
47271
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description })
47495
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
47496
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) })
47272
47497
  ] }) }),
47273
47498
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", children: [
47274
47499
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("simulator.parameters") }),
@@ -47309,28 +47534,28 @@ function SimulatorBoard({
47309
47534
  ] }, param.id))
47310
47535
  ] }) }),
47311
47536
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
47312
- /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: resolved.outputLabel }),
47537
+ /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: outputLabel }),
47313
47538
  /* @__PURE__ */ jsxs(Typography, { variant: "h3", weight: "bold", children: [
47314
47539
  output.toFixed(2),
47315
47540
  " ",
47316
- resolved.outputUnit
47541
+ outputUnit
47317
47542
  ] }),
47318
47543
  submitted && /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
47319
47544
  /* @__PURE__ */ jsx(Icon, { icon: isCorrect ? CheckCircle : XCircle, size: "sm", className: isCorrect ? "text-success" : "text-error" }),
47320
- /* @__PURE__ */ jsx(Typography, { variant: "body", className: isCorrect ? "text-success" : "text-error", children: isCorrect ? resolved.successMessage ?? t("simulator.correct") : resolved.failMessage ?? t("simulator.incorrect") })
47545
+ /* @__PURE__ */ jsx(Typography, { variant: "body", className: isCorrect ? "text-success" : "text-error", children: isCorrect ? str(resolved.successMessage) || t("simulator.correct") : str(resolved.failMessage) || t("simulator.incorrect") })
47321
47546
  ] }),
47322
47547
  /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
47323
47548
  t("simulator.target"),
47324
47549
  ": ",
47325
47550
  targetValue,
47326
47551
  " ",
47327
- resolved.outputUnit,
47552
+ outputUnit,
47328
47553
  " (\xB1",
47329
47554
  targetTolerance,
47330
47555
  ")"
47331
47556
  ] })
47332
47557
  ] }) }),
47333
- showHint && resolved.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
47558
+ showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
47334
47559
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
47335
47560
  !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, children: [
47336
47561
  /* @__PURE__ */ jsx(Icon, { icon: Play, size: "sm" }),
@@ -47349,6 +47574,7 @@ var init_SimulatorBoard = __esm({
47349
47574
  "components/game/organisms/puzzles/simulator/SimulatorBoard.tsx"() {
47350
47575
  init_atoms2();
47351
47576
  init_useEventBus();
47577
+ init_boardEntity();
47352
47578
  SimulatorBoard.displayName = "SimulatorBoard";
47353
47579
  }
47354
47580
  });
@@ -47774,22 +48000,25 @@ function VariablePanel({
47774
48000
  return /* @__PURE__ */ jsxs(VStack, { className: cn("p-3 rounded-lg bg-card border border-border", className), gap: "sm", children: [
47775
48001
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("stateArchitect.variables", { name: entityName }) }),
47776
48002
  variables.map((v) => {
47777
- const max = v.max ?? 100;
47778
- const min = v.min ?? 0;
47779
- const pct = Math.round((v.value - min) / (max - min) * 100);
48003
+ const name = v.name == null ? "" : String(v.name);
48004
+ const value = numField(v.value);
48005
+ const max = numField(v.max, 100);
48006
+ const min = numField(v.min, 0);
48007
+ const unit = v.unit == null ? "" : String(v.unit);
48008
+ const pct = Math.round((value - min) / (max - min) * 100);
47780
48009
  const isHigh = pct > 80;
47781
48010
  const isLow = pct < 20;
47782
48011
  return /* @__PURE__ */ jsxs(VStack, { gap: "none", children: [
47783
48012
  /* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between", children: [
47784
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground font-medium", children: v.name }),
48013
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground font-medium", children: name }),
47785
48014
  /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: cn(
47786
48015
  isHigh ? "text-error" : isLow ? "text-warning" : "text-foreground"
47787
48016
  ), children: [
47788
- v.value,
47789
- v.unit || "",
48017
+ value,
48018
+ unit,
47790
48019
  " / ",
47791
48020
  max,
47792
- v.unit || ""
48021
+ unit
47793
48022
  ] })
47794
48023
  ] }),
47795
48024
  /* @__PURE__ */ jsx(
@@ -47800,14 +48029,19 @@ function VariablePanel({
47800
48029
  size: "sm"
47801
48030
  }
47802
48031
  )
47803
- ] }, v.name);
48032
+ ] }, name);
47804
48033
  })
47805
48034
  ] });
47806
48035
  }
48036
+ var numField;
47807
48037
  var init_VariablePanel = __esm({
47808
48038
  "components/game/organisms/puzzles/state-architect/VariablePanel.tsx"() {
47809
48039
  init_atoms2();
47810
48040
  init_cn();
48041
+ numField = (v, fallback = 0) => {
48042
+ const n = Number(v);
48043
+ return Number.isFinite(n) ? n : fallback;
48044
+ };
47811
48045
  VariablePanel.displayName = "VariablePanel";
47812
48046
  }
47813
48047
  });
@@ -47834,14 +48068,21 @@ function StateArchitectBoard({
47834
48068
  }) {
47835
48069
  const { emit } = useEventBus();
47836
48070
  const { t } = useTranslate();
47837
- const resolved = Array.isArray(entity) ? entity[0] : entity;
47838
- const [transitions, setTransitions] = useState(resolved?.transitions ?? []);
48071
+ const resolved = boardEntity(entity);
48072
+ const entityStates = Array.isArray(resolved?.states) ? resolved.states : [];
48073
+ const initialState = str(resolved?.initialState);
48074
+ const entityName = str(resolved?.entityName);
48075
+ const availableEvents = Array.isArray(resolved?.availableEvents) ? resolved.availableEvents : [];
48076
+ const testCases = Array.isArray(resolved?.testCases) ? resolved.testCases : [];
48077
+ const entityTransitions = Array.isArray(resolved?.transitions) ? resolved.transitions : [];
48078
+ const entityVariables = rows(resolved?.variables);
48079
+ const [transitions, setTransitions] = useState(entityTransitions);
47839
48080
  const [headerError, setHeaderError] = useState(false);
47840
48081
  const [playState, setPlayState] = useState("editing");
47841
- const [currentState, setCurrentState] = useState(resolved?.initialState ?? "");
48082
+ const [currentState, setCurrentState] = useState(initialState);
47842
48083
  const [selectedState, setSelectedState] = useState(null);
47843
48084
  const [testResults, setTestResults] = useState([]);
47844
- const [variables, setVariables] = useState(resolved?.variables ?? []);
48085
+ const [variables, setVariables] = useState(() => [...entityVariables]);
47845
48086
  const [attempts, setAttempts] = useState(0);
47846
48087
  const timerRef = useRef(null);
47847
48088
  const [addingFrom, setAddingFrom] = useState(null);
@@ -47850,12 +48091,12 @@ function StateArchitectBoard({
47850
48091
  }, []);
47851
48092
  const GRAPH_W = 500;
47852
48093
  const GRAPH_H = 400;
47853
- const positions = useMemo(() => layoutStates(resolved?.states ?? [], GRAPH_W, GRAPH_H), [resolved?.states]);
48094
+ const positions = useMemo(() => layoutStates(entityStates, GRAPH_W, GRAPH_H), [entityStates]);
47854
48095
  const handleStateClick = useCallback((state) => {
47855
48096
  if (playState !== "editing") return;
47856
48097
  if (addingFrom) {
47857
48098
  if (addingFrom !== state) {
47858
- const event = resolved?.availableEvents[0] || "EVENT";
48099
+ const event = availableEvents[0] || "EVENT";
47859
48100
  const newTrans = {
47860
48101
  id: `t-${nextTransId++}`,
47861
48102
  from: addingFrom,
@@ -47868,7 +48109,7 @@ function StateArchitectBoard({
47868
48109
  } else {
47869
48110
  setSelectedState(state);
47870
48111
  }
47871
- }, [playState, addingFrom, resolved?.availableEvents]);
48112
+ }, [playState, addingFrom, availableEvents]);
47872
48113
  const handleStartAddTransition = useCallback(() => {
47873
48114
  if (!selectedState) return;
47874
48115
  setAddingFrom(selectedState);
@@ -47877,9 +48118,9 @@ function StateArchitectBoard({
47877
48118
  setTransitions((prev) => prev.filter((t2) => t2.id !== transId));
47878
48119
  }, []);
47879
48120
  const machine = useMemo(() => ({
47880
- name: resolved?.entityName ?? "",
47881
- description: resolved?.description ?? "",
47882
- states: resolved?.states ?? [],
48121
+ name: entityName,
48122
+ description: str(resolved?.description),
48123
+ states: entityStates,
47883
48124
  currentState,
47884
48125
  transitions: transitions.map((t2) => ({
47885
48126
  from: t2.from,
@@ -47887,7 +48128,7 @@ function StateArchitectBoard({
47887
48128
  event: t2.event,
47888
48129
  guardHint: t2.guardHint
47889
48130
  }))
47890
- }), [resolved, currentState, transitions]);
48131
+ }), [entityName, resolved, entityStates, currentState, transitions]);
47891
48132
  const handleTest = useCallback(() => {
47892
48133
  if (playState !== "editing") return;
47893
48134
  if (testEvent) emit(`UI:${testEvent}`, {});
@@ -47896,7 +48137,7 @@ function StateArchitectBoard({
47896
48137
  const results = [];
47897
48138
  let testIdx = 0;
47898
48139
  const runNextTest = () => {
47899
- if (testIdx >= (resolved?.testCases.length ?? 0)) {
48140
+ if (testIdx >= testCases.length) {
47900
48141
  const allPassed = results.every((r2) => r2.passed);
47901
48142
  setPlayState(allPassed ? "success" : "fail");
47902
48143
  setTestResults(results);
@@ -47911,9 +48152,9 @@ function StateArchitectBoard({
47911
48152
  }
47912
48153
  return;
47913
48154
  }
47914
- const testCase = resolved?.testCases[testIdx];
48155
+ const testCase = testCases[testIdx];
47915
48156
  if (!testCase) return;
47916
- let state = resolved.initialState;
48157
+ let state = initialState;
47917
48158
  for (const event of testCase.events) {
47918
48159
  const trans = transitions.find((t2) => t2.from === state && t2.event === event);
47919
48160
  if (trans) {
@@ -47931,53 +48172,57 @@ function StateArchitectBoard({
47931
48172
  timerRef.current = setTimeout(runNextTest, stepDurationMs);
47932
48173
  };
47933
48174
  timerRef.current = setTimeout(runNextTest, stepDurationMs);
47934
- }, [playState, transitions, resolved, stepDurationMs, testEvent, completeEvent, emit]);
48175
+ }, [playState, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
47935
48176
  const handleTryAgain = useCallback(() => {
47936
48177
  if (timerRef.current) clearTimeout(timerRef.current);
47937
48178
  setPlayState("editing");
47938
- setCurrentState(resolved?.initialState ?? "");
48179
+ setCurrentState(initialState);
47939
48180
  setTestResults([]);
47940
- }, [resolved?.initialState]);
48181
+ }, [initialState]);
47941
48182
  const handleReset = useCallback(() => {
47942
48183
  if (timerRef.current) clearTimeout(timerRef.current);
47943
- setTransitions(resolved?.transitions ?? []);
48184
+ setTransitions(entityTransitions);
47944
48185
  setPlayState("editing");
47945
- setCurrentState(resolved?.initialState ?? "");
48186
+ setCurrentState(initialState);
47946
48187
  setTestResults([]);
47947
- setVariables(resolved?.variables ?? []);
48188
+ setVariables([...entityVariables]);
47948
48189
  setSelectedState(null);
47949
48190
  setAddingFrom(null);
47950
48191
  setAttempts(0);
47951
- }, [resolved]);
48192
+ }, [entityTransitions, initialState, entityVariables]);
47952
48193
  const codeData = useMemo(() => ({
47953
- name: resolved?.entityName ?? "",
47954
- states: resolved?.states ?? [],
47955
- initialState: resolved?.initialState ?? "",
48194
+ name: entityName,
48195
+ states: entityStates,
48196
+ initialState,
47956
48197
  transitions: transitions.map((t2) => ({
47957
48198
  from: t2.from,
47958
48199
  to: t2.to,
47959
48200
  event: t2.event,
47960
48201
  ...t2.guardHint ? { guard: t2.guardHint } : {}
47961
48202
  }))
47962
- }), [resolved, transitions]);
48203
+ }), [entityName, entityStates, initialState, transitions]);
47963
48204
  if (!resolved) return null;
48205
+ const theme = resolved.theme ?? void 0;
48206
+ const themeBackground = theme?.background;
48207
+ const headerImage = str(resolved.headerImage);
48208
+ const hint = str(resolved.hint);
47964
48209
  return /* @__PURE__ */ jsxs(
47965
48210
  VStack,
47966
48211
  {
47967
48212
  className: cn("p-4 gap-6", className),
47968
48213
  style: {
47969
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
48214
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
47970
48215
  backgroundSize: "cover",
47971
48216
  backgroundPosition: "center"
47972
48217
  },
47973
48218
  children: [
47974
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
48219
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
47975
48220
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
47976
- /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
47977
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description }),
48221
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
48222
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) }),
47978
48223
  /* @__PURE__ */ jsxs(HStack, { className: "items-center p-2 rounded bg-warning/10 border border-warning/30", gap: "xs", children: [
47979
48224
  /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-warning font-bold", children: t("game.hint") + ":" }),
47980
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: resolved.hint })
48225
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: hint })
47981
48226
  ] })
47982
48227
  ] }),
47983
48228
  /* @__PURE__ */ jsxs(HStack, { className: "flex-wrap items-start", gap: "lg", children: [
@@ -48025,14 +48270,14 @@ function StateArchitectBoard({
48025
48270
  ]
48026
48271
  }
48027
48272
  ),
48028
- resolved.states.map((state) => /* @__PURE__ */ jsx(
48273
+ entityStates.map((state) => /* @__PURE__ */ jsx(
48029
48274
  StateNode2,
48030
48275
  {
48031
48276
  name: state,
48032
48277
  position: positions[state],
48033
48278
  isCurrent: state === currentState,
48034
48279
  isSelected: state === selectedState,
48035
- isInitial: state === resolved.initialState,
48280
+ isInitial: state === initialState,
48036
48281
  onClick: () => handleStateClick(state)
48037
48282
  },
48038
48283
  state
@@ -48079,7 +48324,7 @@ function StateArchitectBoard({
48079
48324
  /* @__PURE__ */ jsx(
48080
48325
  VariablePanel,
48081
48326
  {
48082
- entityName: resolved.entityName,
48327
+ entityName,
48083
48328
  variables
48084
48329
  }
48085
48330
  ),
@@ -48094,12 +48339,12 @@ function StateArchitectBoard({
48094
48339
  resolved.showCodeView !== false && /* @__PURE__ */ jsx(CodeView, { data: codeData, label: "View Code" })
48095
48340
  ] })
48096
48341
  ] }),
48097
- playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: resolved.successMessage || t("stateArchitect.allPassed") }) }),
48342
+ playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("stateArchitect.allPassed") }) }),
48098
48343
  playState === "fail" && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
48099
48344
  /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(ENCOURAGEMENT_KEYS3[Math.min(attempts - 1, ENCOURAGEMENT_KEYS3.length - 1)] ?? ENCOURAGEMENT_KEYS3[0]) }) }),
48100
- attempts >= 3 && resolved.hint && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
48345
+ attempts >= 3 && hint && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
48101
48346
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
48102
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
48347
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
48103
48348
  ] }) })
48104
48349
  ] }),
48105
48350
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
@@ -48129,6 +48374,7 @@ var init_StateArchitectBoard = __esm({
48129
48374
  init_TransitionArrow();
48130
48375
  init_VariablePanel();
48131
48376
  init_CodeView();
48377
+ init_boardEntity();
48132
48378
  ENCOURAGEMENT_KEYS3 = [
48133
48379
  "puzzle.tryAgain1",
48134
48380
  "puzzle.tryAgain2",
@@ -48165,8 +48411,8 @@ var init_StatsOrganism = __esm({
48165
48411
  return /* @__PURE__ */ jsx(ErrorState, { message: error.message, className });
48166
48412
  }
48167
48413
  const stats = items.map((item) => ({
48168
- value: item.value,
48169
- label: item.label
48414
+ value: String(item.value ?? ""),
48415
+ label: String(item.label ?? "")
48170
48416
  }));
48171
48417
  return /* @__PURE__ */ jsx(
48172
48418
  StatsGrid,
@@ -48212,10 +48458,10 @@ var init_StepFlowOrganism = __esm({
48212
48458
  return /* @__PURE__ */ jsx(ErrorState, { message: error.message, className });
48213
48459
  }
48214
48460
  const steps = items.map((item) => ({
48215
- number: item.number,
48216
- title: item.title,
48217
- description: item.description,
48218
- icon: item.icon
48461
+ number: item.number != null ? Number(item.number) : void 0,
48462
+ title: String(item.title ?? ""),
48463
+ description: String(item.description ?? ""),
48464
+ icon: item.icon != null ? String(item.icon) : void 0
48219
48465
  }));
48220
48466
  return /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
48221
48467
  (heading || subtitle) && /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
@@ -48388,13 +48634,13 @@ var init_TeamOrganism = __esm({
48388
48634
  /* @__PURE__ */ jsx(SimpleGrid, { cols: cols > 0 ? cols : 1, gap: "lg", children: items.map((member) => /* @__PURE__ */ jsx(
48389
48635
  TeamCard,
48390
48636
  {
48391
- name: member.name,
48392
- nameAr: member.nameAr,
48393
- role: member.role,
48394
- bio: member.bio,
48395
- avatar: member.avatar
48637
+ name: String(member.name ?? ""),
48638
+ nameAr: member.nameAr != null ? String(member.nameAr) : void 0,
48639
+ role: String(member.role ?? ""),
48640
+ bio: String(member.bio ?? ""),
48641
+ avatar: member.avatar != null ? String(member.avatar) : void 0
48396
48642
  },
48397
- member.id
48643
+ String(member.id ?? "")
48398
48644
  )) })
48399
48645
  ] });
48400
48646
  };
@@ -48631,8 +48877,8 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
48631
48877
  const [turn, setTurn] = useState(1);
48632
48878
  const [gameResult, setGameResult] = useState(null);
48633
48879
  const checkGameEnd = useCallback((currentUnits) => {
48634
- const pa = currentUnits.filter((u) => u.team === "player" && u.health > 0);
48635
- const ea = currentUnits.filter((u) => u.team === "enemy" && u.health > 0);
48880
+ const pa = currentUnits.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
48881
+ const ea = currentUnits.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
48636
48882
  if (pa.length === 0) {
48637
48883
  setGameResult("defeat");
48638
48884
  setPhase("game_over");
@@ -48650,34 +48896,36 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
48650
48896
  }
48651
48897
  }, [onGameEnd, gameEndEvent, eventBus]);
48652
48898
  const handleUnitClick = useCallback((unitId) => {
48653
- const unit = units.find((u) => u.id === unitId);
48899
+ const unit = units.find((u) => str(u.id) === unitId);
48654
48900
  if (!unit) return;
48655
48901
  if (unitClickEvent) {
48656
48902
  eventBus.emit(`UI:${unitClickEvent}`, { unitId });
48657
48903
  }
48658
48904
  if (phase === "observation" || phase === "selection") {
48659
- if (unit.team === "player") {
48905
+ if (unitTeam(unit) === "player") {
48660
48906
  setSelectedUnitId(unitId);
48661
48907
  setPhase("movement");
48662
48908
  }
48663
48909
  } else if (phase === "action") {
48664
- const selectedUnit = units.find((u) => u.id === selectedUnitId);
48910
+ const selectedUnit = units.find((u) => str(u.id) === selectedUnitId);
48665
48911
  if (!selectedUnit) return;
48666
- if (unit.team === "enemy") {
48667
- const dx = Math.abs(unit.position.x - selectedUnit.position.x);
48668
- const dy = Math.abs(unit.position.y - selectedUnit.position.y);
48912
+ if (unitTeam(unit) === "enemy") {
48913
+ const up = unitPosition(unit);
48914
+ const sp = unitPosition(selectedUnit);
48915
+ const dx = Math.abs(up.x - sp.x);
48916
+ const dy = Math.abs(up.y - sp.y);
48669
48917
  if (dx <= 1 && dy <= 1 && dx + dy > 0) {
48670
- const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, selectedUnit.attack - unit.defense);
48671
- const newHealth = Math.max(0, unit.health - damage);
48918
+ const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, num(selectedUnit.attack) - num(unit.defense));
48919
+ const newHealth = Math.max(0, unitHealth(unit) - damage);
48672
48920
  const updatedUnits = units.map(
48673
- (u) => u.id === unit.id ? { ...u, health: newHealth } : u
48921
+ (u) => str(u.id) === str(unit.id) ? { ...u, health: newHealth } : u
48674
48922
  );
48675
48923
  setUnits(updatedUnits);
48676
48924
  onAttack?.(selectedUnit, unit, damage);
48677
48925
  if (attackEvent) {
48678
48926
  eventBus.emit(`UI:${attackEvent}`, {
48679
- attackerId: selectedUnit.id,
48680
- targetId: unit.id,
48927
+ attackerId: str(selectedUnit.id),
48928
+ targetId: str(unit.id),
48681
48929
  damage
48682
48930
  });
48683
48931
  }
@@ -48694,16 +48942,20 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
48694
48942
  eventBus.emit(`UI:${tileClickEvent}`, { x, y });
48695
48943
  }
48696
48944
  if (phase === "movement" && selectedUnitId) {
48697
- const selectedUnit = units.find((u) => u.id === selectedUnitId);
48945
+ const selectedUnit = units.find((u) => str(u.id) === selectedUnitId);
48698
48946
  if (!selectedUnit) return;
48699
- const dx = Math.abs(x - selectedUnit.position.x);
48700
- const dy = Math.abs(y - selectedUnit.position.y);
48947
+ const sp = unitPosition(selectedUnit);
48948
+ const dx = Math.abs(x - sp.x);
48949
+ const dy = Math.abs(y - sp.y);
48701
48950
  const dist = dx + dy;
48702
- if (dist > 0 && dist <= selectedUnit.movement) {
48703
- if (!units.some((u) => u.position.x === x && u.position.y === y && u.health > 0)) {
48951
+ if (dist > 0 && dist <= num(selectedUnit.movement)) {
48952
+ if (!units.some((u) => {
48953
+ const p2 = unitPosition(u);
48954
+ return p2.x === x && p2.y === y && unitHealth(u) > 0;
48955
+ })) {
48704
48956
  setUnits(
48705
48957
  (prev) => prev.map(
48706
- (u) => u.id === selectedUnitId ? { ...u, position: { x, y } } : u
48958
+ (u) => str(u.id) === selectedUnitId ? { ...u, position: { x, y } } : u
48707
48959
  )
48708
48960
  );
48709
48961
  setPhase("action");
@@ -48746,12 +48998,13 @@ var init_useBattleState = __esm({
48746
48998
  "components/game/organisms/hooks/useBattleState.ts"() {
48747
48999
  "use client";
48748
49000
  init_useEventBus();
49001
+ init_boardEntity();
48749
49002
  }
48750
49003
  });
48751
49004
  function UncontrolledBattleBoard({ entity, ...rest }) {
48752
- const resolved = Array.isArray(entity) ? entity[0] : entity;
49005
+ const resolved = boardEntity(entity);
48753
49006
  const battleState = useBattleState(
48754
- resolved?.initialUnits ?? [],
49007
+ rows(resolved?.initialUnits),
48755
49008
  {
48756
49009
  tileClickEvent: rest.tileClickEvent,
48757
49010
  unitClickEvent: rest.unitClickEvent,
@@ -48787,10 +49040,23 @@ function UncontrolledBattleBoard({ entity, ...rest }) {
48787
49040
  var init_UncontrolledBattleBoard = __esm({
48788
49041
  "components/game/organisms/UncontrolledBattleBoard.tsx"() {
48789
49042
  init_BattleBoard();
49043
+ init_boardEntity();
48790
49044
  init_useBattleState();
48791
49045
  UncontrolledBattleBoard.displayName = "UncontrolledBattleBoard";
48792
49046
  }
48793
49047
  });
49048
+ function heroPosition(h) {
49049
+ return vec2(h.position);
49050
+ }
49051
+ function heroOwner(h) {
49052
+ return str(h.owner);
49053
+ }
49054
+ function heroMovement(h) {
49055
+ return num(h.movement);
49056
+ }
49057
+ function hexPassable(h) {
49058
+ return h.passable !== false;
49059
+ }
48794
49060
  function defaultIsInRange(from, to, range) {
48795
49061
  return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
48796
49062
  }
@@ -48821,36 +49087,36 @@ function WorldMapBoard({
48821
49087
  className
48822
49088
  }) {
48823
49089
  const eventBus = useEventBus();
48824
- const resolved = Array.isArray(entity) ? entity[0] : entity;
48825
- const hexes = resolved?.hexes ?? [];
48826
- const heroes = resolved?.heroes ?? [];
48827
- const features = resolved?.features ?? [];
48828
- const selectedHeroId = resolved?.selectedHeroId;
49090
+ const resolved = boardEntity(entity);
49091
+ const hexes = rows(resolved?.hexes);
49092
+ const heroes = rows(resolved?.heroes);
49093
+ const features = Array.isArray(resolved?.features) ? resolved.features : [];
49094
+ const selectedHeroId = resolved?.selectedHeroId ?? null;
48829
49095
  const assetManifest = resolved?.assetManifest;
48830
49096
  const backgroundImage = resolved?.backgroundImage;
48831
49097
  const [hoveredTile, setHoveredTile] = useState(null);
48832
49098
  const selectedHero = useMemo(
48833
- () => heroes.find((h) => h.id === selectedHeroId) ?? null,
49099
+ () => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
48834
49100
  [heroes, selectedHeroId]
48835
49101
  );
48836
49102
  const tiles = useMemo(
48837
49103
  () => hexes.map((hex) => ({
48838
- x: hex.x,
48839
- y: hex.y,
48840
- terrain: hex.terrain,
48841
- terrainSprite: hex.terrainSprite
49104
+ x: num(hex.x),
49105
+ y: num(hex.y),
49106
+ terrain: str(hex.terrain),
49107
+ terrainSprite: hex.terrainSprite == null ? void 0 : str(hex.terrainSprite)
48842
49108
  })),
48843
49109
  [hexes]
48844
49110
  );
48845
49111
  const baseUnits = useMemo(
48846
49112
  () => heroes.map((hero) => ({
48847
- id: hero.id,
48848
- position: hero.position,
48849
- name: hero.name,
48850
- team: hero.owner === "enemy" ? "enemy" : "player",
49113
+ id: str(hero.id),
49114
+ position: heroPosition(hero),
49115
+ name: str(hero.name),
49116
+ team: heroOwner(hero) === "enemy" ? "enemy" : "player",
48851
49117
  health: 100,
48852
49118
  maxHealth: 100,
48853
- sprite: hero.sprite
49119
+ sprite: hero.sprite == null ? void 0 : str(hero.sprite)
48854
49120
  })),
48855
49121
  [heroes]
48856
49122
  );
@@ -48891,73 +49157,94 @@ function WorldMapBoard({
48891
49157
  const isoUnits = useMemo(() => {
48892
49158
  if (movingPositions.size === 0) return baseUnits;
48893
49159
  return baseUnits.map((u) => {
48894
- const pos = movingPositions.get(u.id);
49160
+ const pos = u.id == null ? void 0 : movingPositions.get(u.id);
48895
49161
  return pos ? { ...u, position: pos } : u;
48896
49162
  });
48897
49163
  }, [baseUnits, movingPositions]);
48898
49164
  const validMoves = useMemo(() => {
48899
- if (!selectedHero || selectedHero.movement <= 0) return [];
49165
+ if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
49166
+ const sp = heroPosition(selectedHero);
49167
+ const sOwner = heroOwner(selectedHero);
49168
+ const range = heroMovement(selectedHero);
48900
49169
  const moves = [];
48901
49170
  hexes.forEach((hex) => {
48902
- if (hex.passable === false) return;
48903
- if (hex.x === selectedHero.position.x && hex.y === selectedHero.position.y) return;
48904
- if (!isInRange(selectedHero.position, { x: hex.x, y: hex.y }, selectedHero.movement)) return;
48905
- if (heroes.some((h) => h.position.x === hex.x && h.position.y === hex.y && h.owner === selectedHero.owner)) return;
48906
- moves.push({ x: hex.x, y: hex.y });
49171
+ const hx = num(hex.x);
49172
+ const hy = num(hex.y);
49173
+ if (!hexPassable(hex)) return;
49174
+ if (hx === sp.x && hy === sp.y) return;
49175
+ if (!isInRange(sp, { x: hx, y: hy }, range)) return;
49176
+ if (heroes.some((h) => {
49177
+ const hp = heroPosition(h);
49178
+ return hp.x === hx && hp.y === hy && heroOwner(h) === sOwner;
49179
+ })) return;
49180
+ moves.push({ x: hx, y: hy });
48907
49181
  });
48908
49182
  return moves;
48909
49183
  }, [selectedHero, hexes, heroes, isInRange]);
48910
49184
  const attackTargets = useMemo(() => {
48911
- if (!selectedHero || selectedHero.movement <= 0) return [];
48912
- return heroes.filter((h) => h.owner !== selectedHero.owner).filter((h) => isInRange(selectedHero.position, h.position, selectedHero.movement)).map((h) => h.position);
49185
+ if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
49186
+ const sp = heroPosition(selectedHero);
49187
+ const sOwner = heroOwner(selectedHero);
49188
+ const range = heroMovement(selectedHero);
49189
+ return heroes.filter((h) => heroOwner(h) !== sOwner).filter((h) => isInRange(sp, heroPosition(h), range)).map((h) => heroPosition(h));
48913
49190
  }, [selectedHero, heroes, isInRange]);
48914
- const maxY = Math.max(...hexes.map((h) => h.y), 0);
49191
+ const maxY = Math.max(...hexes.map((h) => num(h.y)), 0);
48915
49192
  const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
48916
49193
  const tileToScreen = useCallback(
48917
49194
  (tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
48918
49195
  [scale, baseOffsetX]
48919
49196
  );
48920
49197
  const hoveredHex = useMemo(
48921
- () => hoveredTile ? hexes.find((h) => h.x === hoveredTile.x && h.y === hoveredTile.y) ?? null : null,
49198
+ () => hoveredTile ? hexes.find((h) => num(h.x) === hoveredTile.x && num(h.y) === hoveredTile.y) ?? null : null,
48922
49199
  [hoveredTile, hexes]
48923
49200
  );
48924
49201
  const hoveredHero = useMemo(
48925
- () => hoveredTile ? heroes.find((h) => h.position.x === hoveredTile.x && h.position.y === hoveredTile.y) ?? null : null,
49202
+ () => hoveredTile ? heroes.find((h) => {
49203
+ const hp = heroPosition(h);
49204
+ return hp.x === hoveredTile.x && hp.y === hoveredTile.y;
49205
+ }) ?? null : null,
48926
49206
  [hoveredTile, heroes]
48927
49207
  );
48928
49208
  const handleTileClick = useCallback((x, y) => {
48929
49209
  if (movementAnimRef.current) return;
48930
- const hex = hexes.find((h) => h.x === x && h.y === y);
49210
+ const hex = hexes.find((h) => num(h.x) === x && num(h.y) === y);
48931
49211
  if (!hex) return;
48932
49212
  if (tileClickEvent) {
48933
49213
  eventBus.emit(`UI:${tileClickEvent}`, { x, y });
48934
49214
  }
48935
49215
  if (selectedHero && validMoves.some((m) => m.x === x && m.y === y)) {
48936
- startMoveAnimation(selectedHero.id, { ...selectedHero.position }, { x, y }, () => {
48937
- onHeroMove?.(selectedHero.id, x, y);
49216
+ const heroId = str(selectedHero.id);
49217
+ startMoveAnimation(heroId, { ...heroPosition(selectedHero) }, { x, y }, () => {
49218
+ onHeroMove?.(heroId, x, y);
48938
49219
  if (heroMoveEvent) {
48939
- eventBus.emit(`UI:${heroMoveEvent}`, { heroId: selectedHero.id, toX: x, toY: y });
49220
+ eventBus.emit(`UI:${heroMoveEvent}`, { heroId, toX: x, toY: y });
48940
49221
  }
48941
- if (hex.feature && hex.feature !== "none") {
48942
- onFeatureEnter?.(selectedHero.id, hex);
49222
+ const feature = str(hex.feature);
49223
+ if (feature && feature !== "none") {
49224
+ onFeatureEnter?.(heroId, hex);
48943
49225
  if (featureEnterEvent) {
48944
- eventBus.emit(`UI:${featureEnterEvent}`, { heroId: selectedHero.id, feature: hex.feature, hex });
49226
+ eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex });
48945
49227
  }
48946
49228
  }
48947
49229
  });
48948
49230
  return;
48949
49231
  }
48950
- const enemy = heroes.find((h) => h.position.x === x && h.position.y === y && h.owner === "enemy");
49232
+ const enemy = heroes.find((h) => {
49233
+ const hp = heroPosition(h);
49234
+ return hp.x === x && hp.y === y && heroOwner(h) === "enemy";
49235
+ });
48951
49236
  if (selectedHero && enemy && attackTargets.some((t) => t.x === x && t.y === y)) {
48952
- onBattleEncounter?.(selectedHero.id, enemy.id);
49237
+ const attackerId = str(selectedHero.id);
49238
+ const defenderId = str(enemy.id);
49239
+ onBattleEncounter?.(attackerId, defenderId);
48953
49240
  if (battleEncounterEvent) {
48954
- eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId: selectedHero.id, defenderId: enemy.id });
49241
+ eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId, defenderId });
48955
49242
  }
48956
49243
  }
48957
49244
  }, [hexes, heroes, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
48958
49245
  const handleUnitClick = useCallback((unitId) => {
48959
- const hero = heroes.find((h) => h.id === unitId);
48960
- if (hero && (hero.owner === "player" || allowMoveAllHeroes)) {
49246
+ const hero = heroes.find((h) => str(h.id) === unitId);
49247
+ if (hero && (heroOwner(hero) === "player" || allowMoveAllHeroes)) {
48961
49248
  onHeroSelect?.(unitId);
48962
49249
  if (heroSelectEvent) {
48963
49250
  eventBus.emit(`UI:${heroSelectEvent}`, { heroId: unitId });
@@ -49030,6 +49317,7 @@ var init_WorldMapBoard = __esm({
49030
49317
  init_Stack();
49031
49318
  init_LoadingState();
49032
49319
  init_IsometricCanvas2();
49320
+ init_boardEntity();
49033
49321
  init_isometric();
49034
49322
  WorldMapBoard.displayName = "WorldMapBoard";
49035
49323
  }
@@ -52593,10 +52881,10 @@ function parseApplicationLevel(schema) {
52593
52881
  }
52594
52882
  const count = schema.orbitals.length;
52595
52883
  const cols = Math.ceil(Math.sqrt(count));
52596
- const rows = Math.ceil(count / cols);
52884
+ const rows2 = Math.ceil(count / cols);
52597
52885
  const spacing = 200;
52598
52886
  const gridW = cols * spacing;
52599
- const gridH = rows * spacing;
52887
+ const gridH = rows2 * spacing;
52600
52888
  const originX = (600 - gridW) / 2 + spacing / 2;
52601
52889
  const originY = (400 - gridH) / 2 + spacing / 2;
52602
52890
  schema.orbitals.forEach((orbital, i) => {
@@ -55738,11 +56026,11 @@ function buildMockData(schema) {
55738
56026
  result[entityName] = entity.instances;
55739
56027
  continue;
55740
56028
  }
55741
- const rows = Array.from(
56029
+ const rows2 = Array.from(
55742
56030
  { length: 10 },
55743
56031
  (_, i) => generateEntityRow(entity, i + 1)
55744
56032
  );
55745
- result[entityName] = rows;
56033
+ result[entityName] = rows2;
55746
56034
  }
55747
56035
  for (const orbital of schema.orbitals) {
55748
56036
  for (const traitRef of orbital.traits ?? []) {
@@ -59241,18 +59529,18 @@ function layoutOrbitals(count, containerW, containerH) {
59241
59529
  if (count === 0) return [];
59242
59530
  if (count === 1) return [{ cx: containerW / 2, cy: containerH / 2 }];
59243
59531
  const cols = Math.min(count, Math.ceil(Math.sqrt(count)));
59244
- const rows = Math.ceil(count / cols);
59532
+ const rows2 = Math.ceil(count / cols);
59245
59533
  const edgePad = 24;
59246
59534
  const fitMinCx = UNIT_DISPLAY_W / 2 + edgePad;
59247
59535
  const fitMinCy = UNIT_DISPLAY_H / 2 + edgePad;
59248
59536
  const fitMaxCx = Math.max(fitMinCx, containerW - UNIT_DISPLAY_W / 2 - edgePad);
59249
59537
  const fitMaxCy = Math.max(fitMinCy, containerH - UNIT_DISPLAY_H / 2 - edgePad);
59250
59538
  const fitStepX = cols > 1 ? (fitMaxCx - fitMinCx) / (cols - 1) : 0;
59251
- const fitStepY = rows > 1 ? (fitMaxCy - fitMinCy) / (rows - 1) : 0;
59539
+ const fitStepY = rows2 > 1 ? (fitMaxCy - fitMinCy) / (rows2 - 1) : 0;
59252
59540
  const stepX = Math.min(fitStepX, UNIT_DISPLAY_W * 3.5);
59253
59541
  const stepY = Math.min(fitStepY, UNIT_DISPLAY_H * 3.5);
59254
59542
  const gridW = (cols - 1) * stepX;
59255
- const gridH = (rows - 1) * stepY;
59543
+ const gridH = (rows2 - 1) * stepY;
59256
59544
  const originX = (containerW - gridW) / 2;
59257
59545
  const originY = (containerH - gridH) / 2;
59258
59546
  return Array.from({ length: count }, (_, i) => ({