@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
@@ -7179,7 +7179,7 @@ var init_SvgGrid = __esm({
7179
7179
  x,
7180
7180
  y,
7181
7181
  cols = 4,
7182
- rows = 3,
7182
+ rows: rows2 = 3,
7183
7183
  spacing = 20,
7184
7184
  nodeRadius = 3,
7185
7185
  color = "var(--color-primary)",
@@ -7188,7 +7188,7 @@ var init_SvgGrid = __esm({
7188
7188
  highlights = []
7189
7189
  }) => {
7190
7190
  const highlightSet = new Set(highlights);
7191
- return /* @__PURE__ */ jsxRuntime.jsx("g", { className, opacity, children: Array.from({ length: rows }).map(
7191
+ return /* @__PURE__ */ jsxRuntime.jsx("g", { className, opacity, children: Array.from({ length: rows2 }).map(
7192
7192
  (_, row) => Array.from({ length: cols }).map((_2, col) => {
7193
7193
  const index = row * cols + col;
7194
7194
  const isHighlighted = highlightSet.has(index);
@@ -7848,7 +7848,7 @@ var init_Input = __esm({
7848
7848
  onClear,
7849
7849
  value,
7850
7850
  options,
7851
- rows = 3,
7851
+ rows: rows2 = 3,
7852
7852
  onChange,
7853
7853
  ...props
7854
7854
  }, ref) => {
@@ -7898,7 +7898,7 @@ var init_Input = __esm({
7898
7898
  ref,
7899
7899
  value,
7900
7900
  onChange,
7901
- rows,
7901
+ rows: rows2,
7902
7902
  className: baseClassName,
7903
7903
  ...props
7904
7904
  }
@@ -9893,66 +9893,6 @@ var init_RangeSlider = __esm({
9893
9893
  RangeSlider.displayName = "RangeSlider";
9894
9894
  }
9895
9895
  });
9896
- function easeOut(t) {
9897
- return t * (2 - t);
9898
- }
9899
- var AnimatedCounter;
9900
- var init_AnimatedCounter = __esm({
9901
- "components/marketing/atoms/AnimatedCounter.tsx"() {
9902
- "use client";
9903
- init_cn();
9904
- init_Typography();
9905
- AnimatedCounter = ({
9906
- value: rawValue,
9907
- duration = 600,
9908
- prefix,
9909
- suffix,
9910
- className
9911
- }) => {
9912
- const numericRaw = typeof rawValue === "number" ? rawValue : Number.parseFloat(String(rawValue ?? ""));
9913
- const value = !Number.isNaN(numericRaw) ? numericRaw : 0;
9914
- const [displayValue, setDisplayValue] = React97.useState(value);
9915
- const previousValueRef = React97.useRef(value);
9916
- const animationFrameRef = React97.useRef(null);
9917
- React97.useEffect(() => {
9918
- const from = previousValueRef.current;
9919
- const to = value;
9920
- previousValueRef.current = value;
9921
- if (from === to) {
9922
- setDisplayValue(to);
9923
- return;
9924
- }
9925
- const startTime = performance.now();
9926
- const diff = to - from;
9927
- function animate(currentTime) {
9928
- const elapsed = currentTime - startTime;
9929
- const progress = Math.min(elapsed / duration, 1);
9930
- const easedProgress = easeOut(progress);
9931
- setDisplayValue(from + diff * easedProgress);
9932
- if (progress < 1) {
9933
- animationFrameRef.current = requestAnimationFrame(animate);
9934
- } else {
9935
- setDisplayValue(to);
9936
- }
9937
- }
9938
- animationFrameRef.current = requestAnimationFrame(animate);
9939
- return () => {
9940
- if (animationFrameRef.current !== null) {
9941
- cancelAnimationFrame(animationFrameRef.current);
9942
- }
9943
- };
9944
- }, [value, duration]);
9945
- const decimalPlaces = Number.isInteger(value) ? 0 : String(value).split(".")[1]?.length ?? 0;
9946
- const formattedValue = displayValue.toFixed(decimalPlaces);
9947
- return /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "h3", className: cn("tabular-nums", className), children: [
9948
- prefix,
9949
- formattedValue,
9950
- suffix
9951
- ] });
9952
- };
9953
- AnimatedCounter.displayName = "AnimatedCounter";
9954
- }
9955
- });
9956
9896
  function useInfiniteScroll(onLoadMore, options = {}) {
9957
9897
  const { rootMargin = "200px", hasMore = true, isLoading = false } = options;
9958
9898
  const observerRef = React97.useRef(null);
@@ -12436,15 +12376,15 @@ function HeaderSkeleton({ className }) {
12436
12376
  ] })
12437
12377
  ] });
12438
12378
  }
12439
- function TableSkeleton({ rows = 5, columns = 4, className }) {
12379
+ function TableSkeleton({ rows: rows2 = 5, columns = 4, className }) {
12440
12380
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", className: cn("border border-border rounded-lg overflow-hidden", className), children: [
12441
12381
  /* @__PURE__ */ jsxRuntime.jsx(HStack, { className: "px-4 py-3 bg-muted/30 border-b border-border", children: Array.from({ length: columns }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(SkeletonBlock, { className: "h-4 flex-1 mx-2" }, i)) }),
12442
- Array.from({ length: rows }).map((_, rowIdx) => /* @__PURE__ */ jsxRuntime.jsx(
12382
+ Array.from({ length: rows2 }).map((_, rowIdx) => /* @__PURE__ */ jsxRuntime.jsx(
12443
12383
  HStack,
12444
12384
  {
12445
12385
  className: cn(
12446
12386
  "px-4 py-3",
12447
- rowIdx < rows - 1 && "border-b border-border"
12387
+ rowIdx < rows2 - 1 && "border-b border-border"
12448
12388
  ),
12449
12389
  children: Array.from({ length: columns }).map((_2, colIdx) => /* @__PURE__ */ jsxRuntime.jsx(SkeletonLine, { className: "flex-1 mx-2" }, colIdx))
12450
12390
  },
@@ -12492,18 +12432,18 @@ function CardSkeleton({ className }) {
12492
12432
  }
12493
12433
  );
12494
12434
  }
12495
- function TextSkeleton({ rows = 3, className }) {
12496
- return /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "sm", className, children: Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
12435
+ function TextSkeleton({ rows: rows2 = 3, className }) {
12436
+ return /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "sm", className, children: Array.from({ length: rows2 }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
12497
12437
  SkeletonLine,
12498
12438
  {
12499
- className: i === rows - 1 ? "w-2/3" : "w-full"
12439
+ className: i === rows2 - 1 ? "w-2/3" : "w-full"
12500
12440
  },
12501
12441
  i
12502
12442
  )) });
12503
12443
  }
12504
12444
  function Skeleton({
12505
12445
  variant = "text",
12506
- rows,
12446
+ rows: rows2,
12507
12447
  columns,
12508
12448
  fields,
12509
12449
  className
@@ -12513,15 +12453,15 @@ function Skeleton({
12513
12453
  case "header":
12514
12454
  return /* @__PURE__ */ jsxRuntime.jsx(HeaderSkeleton, { className });
12515
12455
  case "table":
12516
- return /* @__PURE__ */ jsxRuntime.jsx(TableSkeleton, { rows, columns, className });
12456
+ return /* @__PURE__ */ jsxRuntime.jsx(TableSkeleton, { rows: rows2, columns, className });
12517
12457
  case "form":
12518
12458
  return /* @__PURE__ */ jsxRuntime.jsx(FormSkeleton, { fields, className });
12519
12459
  case "card":
12520
12460
  return /* @__PURE__ */ jsxRuntime.jsx(CardSkeleton, { className });
12521
12461
  case "text":
12522
- return /* @__PURE__ */ jsxRuntime.jsx(TextSkeleton, { rows, className });
12462
+ return /* @__PURE__ */ jsxRuntime.jsx(TextSkeleton, { rows: rows2, className });
12523
12463
  default:
12524
- return /* @__PURE__ */ jsxRuntime.jsx(TextSkeleton, { rows, className });
12464
+ return /* @__PURE__ */ jsxRuntime.jsx(TextSkeleton, { rows: rows2, className });
12525
12465
  }
12526
12466
  }
12527
12467
  var pulseClass;
@@ -14149,7 +14089,7 @@ var init_MapView = __esm({
14149
14089
  shadowSize: [41, 41]
14150
14090
  });
14151
14091
  L.Marker.prototype.options.icon = defaultIcon;
14152
- const { useEffect: useEffect79, useRef: useRef71, useCallback: useCallback122, useState: useState113 } = React97__namespace.default;
14092
+ const { useEffect: useEffect79, useRef: useRef71, useCallback: useCallback123, useState: useState113 } = React97__namespace.default;
14153
14093
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
14154
14094
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
14155
14095
  function MapUpdater({ centerLat, centerLng, zoom }) {
@@ -14195,7 +14135,7 @@ var init_MapView = __esm({
14195
14135
  }) {
14196
14136
  const eventBus = useEventBus3();
14197
14137
  const [clickedPosition, setClickedPosition] = useState113(null);
14198
- const handleMapClick = useCallback122((lat, lng) => {
14138
+ const handleMapClick = useCallback123((lat, lng) => {
14199
14139
  if (showClickedPin) {
14200
14140
  setClickedPosition({ lat, lng });
14201
14141
  }
@@ -14204,7 +14144,7 @@ var init_MapView = __esm({
14204
14144
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
14205
14145
  }
14206
14146
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
14207
- const handleMarkerClick = useCallback122((marker) => {
14147
+ const handleMarkerClick = useCallback123((marker) => {
14208
14148
  onMarkerClick?.(marker);
14209
14149
  if (markerClickEvent) {
14210
14150
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -14425,7 +14365,7 @@ function InputPattern({
14425
14365
  function TextareaPattern({
14426
14366
  value = "",
14427
14367
  placeholder,
14428
- rows = 4,
14368
+ rows: rows2 = 4,
14429
14369
  disabled = false,
14430
14370
  fieldError,
14431
14371
  onChange,
@@ -14445,7 +14385,7 @@ function TextareaPattern({
14445
14385
  {
14446
14386
  value: localValue,
14447
14387
  placeholder,
14448
- rows,
14388
+ rows: rows2,
14449
14389
  disabled,
14450
14390
  error: fieldError,
14451
14391
  onChange: handleChange,
@@ -14930,6 +14870,91 @@ var init_ActionPalette = __esm({
14930
14870
  ActionPalette.displayName = "ActionPalette";
14931
14871
  }
14932
14872
  });
14873
+ function parseValue(value) {
14874
+ if (value === "" || value == null) return { num: 0, prefix: "", suffix: "", decimals: 0 };
14875
+ const match = String(value).match(/^([^0-9]*)([0-9]+(?:\.[0-9]+)?)(.*)$/);
14876
+ if (!match) {
14877
+ return { num: 0, prefix: "", suffix: String(value), decimals: 0 };
14878
+ }
14879
+ const numStr = match[2];
14880
+ const decimalIdx = numStr.indexOf(".");
14881
+ const decimals = decimalIdx >= 0 ? numStr.length - decimalIdx - 1 : 0;
14882
+ return {
14883
+ prefix: match[1],
14884
+ num: parseFloat(numStr),
14885
+ suffix: match[3],
14886
+ decimals
14887
+ };
14888
+ }
14889
+ var AnimatedCounter;
14890
+ var init_AnimatedCounter = __esm({
14891
+ "components/core/molecules/AnimatedCounter.tsx"() {
14892
+ "use client";
14893
+ init_cn();
14894
+ init_Box();
14895
+ init_Typography();
14896
+ AnimatedCounter = ({
14897
+ value,
14898
+ label,
14899
+ duration = 1500,
14900
+ className
14901
+ }) => {
14902
+ const ref = React97.useRef(null);
14903
+ const [displayValue, setDisplayValue] = React97.useState("0");
14904
+ const [hasAnimated, setHasAnimated] = React97.useState(false);
14905
+ const animate = React97.useCallback(() => {
14906
+ const { num: num2, prefix, suffix, decimals } = parseValue(value);
14907
+ if (num2 === 0) {
14908
+ setDisplayValue(String(value));
14909
+ return;
14910
+ }
14911
+ const startTime = performance.now();
14912
+ const tick = (now2) => {
14913
+ const elapsed = now2 - startTime;
14914
+ const progress = Math.min(elapsed / duration, 1);
14915
+ const eased = 1 - Math.pow(1 - progress, 3);
14916
+ const current = eased * num2;
14917
+ setDisplayValue(`${prefix}${current.toFixed(decimals)}${suffix}`);
14918
+ if (progress < 1) {
14919
+ requestAnimationFrame(tick);
14920
+ } else {
14921
+ setDisplayValue(String(value));
14922
+ }
14923
+ };
14924
+ requestAnimationFrame(tick);
14925
+ }, [value, duration]);
14926
+ React97.useEffect(() => {
14927
+ if (hasAnimated) return;
14928
+ const el = ref.current;
14929
+ if (!el) return;
14930
+ const observer2 = new IntersectionObserver(
14931
+ (entries) => {
14932
+ if (entries[0].isIntersecting) {
14933
+ setHasAnimated(true);
14934
+ animate();
14935
+ observer2.disconnect();
14936
+ }
14937
+ },
14938
+ { threshold: 0.3 }
14939
+ );
14940
+ observer2.observe(el);
14941
+ return () => observer2.disconnect();
14942
+ }, [hasAnimated, animate]);
14943
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { ref, className: cn("flex flex-col items-center gap-1 p-4", className), children: [
14944
+ /* @__PURE__ */ jsxRuntime.jsx(
14945
+ Typography,
14946
+ {
14947
+ variant: "h2",
14948
+ className: "text-primary font-bold tabular-nums",
14949
+ children: hasAnimated ? displayValue : "0"
14950
+ }
14951
+ ),
14952
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", color: "muted", className: "text-center", children: label })
14953
+ ] });
14954
+ };
14955
+ AnimatedCounter.displayName = "AnimatedCounter";
14956
+ }
14957
+ });
14933
14958
  var AuthLayout;
14934
14959
  var init_AuthLayout = __esm({
14935
14960
  "components/core/templates/AuthLayout.tsx"() {
@@ -16315,6 +16340,39 @@ var init_IsometricCanvas2 = __esm({
16315
16340
  init_IsometricCanvas();
16316
16341
  }
16317
16342
  });
16343
+
16344
+ // components/game/organisms/boardEntity.ts
16345
+ function boardEntity(entity) {
16346
+ if (!entity) return void 0;
16347
+ return Array.isArray(entity) ? entity[0] : entity;
16348
+ }
16349
+ function str(v) {
16350
+ return v == null ? "" : String(v);
16351
+ }
16352
+ function num(v, fallback = 0) {
16353
+ const n = Number(v);
16354
+ return Number.isFinite(n) ? n : fallback;
16355
+ }
16356
+ function rows(v) {
16357
+ return Array.isArray(v) ? v : [];
16358
+ }
16359
+ function vec2(v) {
16360
+ const o = v ?? {};
16361
+ return { x: num(o.x), y: num(o.y) };
16362
+ }
16363
+ function unitPosition(u) {
16364
+ return vec2(u.position);
16365
+ }
16366
+ function unitTeam(u) {
16367
+ return str(u.team);
16368
+ }
16369
+ function unitHealth(u) {
16370
+ return num(u.health);
16371
+ }
16372
+ var init_boardEntity = __esm({
16373
+ "components/game/organisms/boardEntity.ts"() {
16374
+ }
16375
+ });
16318
16376
  function BattleBoard({
16319
16377
  entity,
16320
16378
  scale = 0.45,
@@ -16341,43 +16399,49 @@ function BattleBoard({
16341
16399
  attackEvent,
16342
16400
  className
16343
16401
  }) {
16344
- const tiles = entity.tiles;
16345
- const features = entity.features ?? [];
16346
- const boardWidth = entity.boardWidth ?? 8;
16347
- const boardHeight = entity.boardHeight ?? 6;
16348
- const assetManifest = entity.assetManifest;
16349
- const backgroundImage = entity.backgroundImage;
16350
- const units = entity.units;
16351
- const selectedUnitId = entity.selectedUnitId;
16352
- const currentPhase = entity.phase;
16353
- const currentTurn = entity.turn;
16354
- const gameResult = entity.gameResult;
16402
+ const board = boardEntity(entity) ?? {};
16403
+ const tiles = Array.isArray(board.tiles) ? board.tiles : [];
16404
+ const features = Array.isArray(board.features) ? board.features : [];
16405
+ const boardWidth = num(board.boardWidth, 8);
16406
+ const boardHeight = num(board.boardHeight, 6);
16407
+ const assetManifest = board.assetManifest;
16408
+ const backgroundImage = board.backgroundImage;
16409
+ const units = rows(board.units);
16410
+ const selectedUnitId = board.selectedUnitId ?? null;
16411
+ const currentPhase = str(board.phase) || "observation";
16412
+ const currentTurn = num(board.turn, 1);
16413
+ const gameResult = board.gameResult ?? null;
16355
16414
  const eventBus = useEventBus();
16356
16415
  const { t } = hooks.useTranslate();
16357
16416
  const [hoveredTile, setHoveredTile] = React97.useState(null);
16358
16417
  const [isShaking, setIsShaking] = React97.useState(false);
16359
16418
  const selectedUnit = React97.useMemo(
16360
- () => units.find((u) => u.id === selectedUnitId) ?? null,
16419
+ () => units.find((u) => str(u.id) === selectedUnitId) ?? null,
16361
16420
  [units, selectedUnitId]
16362
16421
  );
16363
16422
  const hoveredUnit = React97.useMemo(() => {
16364
16423
  if (!hoveredTile) return null;
16365
- return units.find(
16366
- (u) => u.position.x === hoveredTile.x && u.position.y === hoveredTile.y && u.health > 0
16367
- ) ?? null;
16424
+ return units.find((u) => {
16425
+ const p2 = unitPosition(u);
16426
+ return p2.x === hoveredTile.x && p2.y === hoveredTile.y && unitHealth(u) > 0;
16427
+ }) ?? null;
16368
16428
  }, [hoveredTile, units]);
16369
- const playerUnits = React97.useMemo(() => units.filter((u) => u.team === "player" && u.health > 0), [units]);
16370
- const enemyUnits = React97.useMemo(() => units.filter((u) => u.team === "enemy" && u.health > 0), [units]);
16429
+ const playerUnits = React97.useMemo(() => units.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0), [units]);
16430
+ const enemyUnits = React97.useMemo(() => units.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0), [units]);
16371
16431
  const validMoves = React97.useMemo(() => {
16372
16432
  if (!selectedUnit || currentPhase !== "movement") return [];
16373
16433
  const moves = [];
16374
- const range = selectedUnit.movement;
16434
+ const range = num(selectedUnit.movement);
16435
+ const origin = unitPosition(selectedUnit);
16375
16436
  for (let dy = -range; dy <= range; dy++) {
16376
16437
  for (let dx = -range; dx <= range; dx++) {
16377
- const nx = selectedUnit.position.x + dx;
16378
- const ny = selectedUnit.position.y + dy;
16438
+ const nx = origin.x + dx;
16439
+ const ny = origin.y + dy;
16379
16440
  const dist = Math.abs(dx) + Math.abs(dy);
16380
- if (dist > 0 && dist <= range && nx >= 0 && nx < boardWidth && ny >= 0 && ny < boardHeight && !units.some((u) => u.position.x === nx && u.position.y === ny && u.health > 0)) {
16441
+ if (dist > 0 && dist <= range && nx >= 0 && nx < boardWidth && ny >= 0 && ny < boardHeight && !units.some((u) => {
16442
+ const p2 = unitPosition(u);
16443
+ return p2.x === nx && p2.y === ny && unitHealth(u) > 0;
16444
+ })) {
16381
16445
  moves.push({ x: nx, y: ny });
16382
16446
  }
16383
16447
  }
@@ -16386,11 +16450,14 @@ function BattleBoard({
16386
16450
  }, [selectedUnit, currentPhase, units, boardWidth, boardHeight]);
16387
16451
  const attackTargets = React97.useMemo(() => {
16388
16452
  if (!selectedUnit || currentPhase !== "action") return [];
16389
- return units.filter((u) => u.team !== selectedUnit.team && u.health > 0).filter((u) => {
16390
- const dx = Math.abs(u.position.x - selectedUnit.position.x);
16391
- const dy = Math.abs(u.position.y - selectedUnit.position.y);
16453
+ const sp = unitPosition(selectedUnit);
16454
+ const sTeam = unitTeam(selectedUnit);
16455
+ return units.filter((u) => unitTeam(u) !== sTeam && unitHealth(u) > 0).filter((u) => {
16456
+ const p2 = unitPosition(u);
16457
+ const dx = Math.abs(p2.x - sp.x);
16458
+ const dy = Math.abs(p2.y - sp.y);
16392
16459
  return dx <= 1 && dy <= 1 && dx + dy > 0;
16393
- }).map((u) => u.position);
16460
+ }).map((u) => unitPosition(u));
16394
16461
  }, [selectedUnit, currentPhase, units]);
16395
16462
  const MOVE_SPEED_MS_PER_TILE = 300;
16396
16463
  const movementAnimRef = React97.useRef(null);
@@ -16430,23 +16497,25 @@ function BattleBoard({
16430
16497
  return () => clearInterval(interval);
16431
16498
  }, []);
16432
16499
  const isoUnits = React97.useMemo(() => {
16433
- return units.filter((u) => u.health > 0).map((unit) => {
16434
- const pos = movingPositions.get(unit.id) ?? unit.position;
16500
+ return units.filter((u) => unitHealth(u) > 0).map((unit) => {
16501
+ const id = str(unit.id);
16502
+ const pos = movingPositions.get(id) ?? unitPosition(unit);
16503
+ const unitTraits = Array.isArray(unit.traits) ? unit.traits : void 0;
16435
16504
  return {
16436
- id: unit.id,
16505
+ id,
16437
16506
  position: pos,
16438
- name: unit.name,
16439
- team: unit.team,
16440
- health: unit.health,
16441
- maxHealth: unit.maxHealth,
16442
- unitType: unit.unitType,
16443
- heroId: unit.heroId,
16444
- sprite: unit.sprite,
16445
- traits: unit.traits?.map((t2) => ({
16446
- name: t2.name,
16447
- currentState: t2.currentState,
16448
- states: t2.states,
16449
- cooldown: t2.cooldown ?? 0
16507
+ name: str(unit.name),
16508
+ team: unitTeam(unit),
16509
+ health: unitHealth(unit),
16510
+ maxHealth: num(unit.maxHealth),
16511
+ unitType: unit.unitType == null ? void 0 : str(unit.unitType),
16512
+ heroId: unit.heroId == null ? void 0 : str(unit.heroId),
16513
+ sprite: unit.sprite == null ? void 0 : str(unit.sprite),
16514
+ traits: unitTraits?.map((tr) => ({
16515
+ name: tr.name,
16516
+ currentState: tr.currentState,
16517
+ states: tr.states,
16518
+ cooldown: tr.cooldown ?? 0
16450
16519
  }))
16451
16520
  };
16452
16521
  });
@@ -16458,8 +16527,8 @@ function BattleBoard({
16458
16527
  [scale, baseOffsetX]
16459
16528
  );
16460
16529
  const checkGameEnd = React97.useCallback(() => {
16461
- const pa = units.filter((u) => u.team === "player" && u.health > 0);
16462
- const ea = units.filter((u) => u.team === "enemy" && u.health > 0);
16530
+ const pa = units.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
16531
+ const ea = units.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
16463
16532
  if (pa.length === 0) {
16464
16533
  onGameEnd?.("defeat");
16465
16534
  if (gameEndEvent) {
@@ -16473,21 +16542,22 @@ function BattleBoard({
16473
16542
  }
16474
16543
  }, [units, onGameEnd, gameEndEvent, eventBus]);
16475
16544
  const handleUnitClick = React97.useCallback((unitId) => {
16476
- const unit = units.find((u) => u.id === unitId);
16545
+ const unit = units.find((u) => str(u.id) === unitId);
16477
16546
  if (!unit) return;
16478
16547
  if (unitClickEvent) {
16479
16548
  eventBus.emit(`UI:${unitClickEvent}`, { unitId });
16480
16549
  }
16481
16550
  if (currentPhase === "action" && selectedUnit) {
16482
- if (unit.team === "enemy" && attackTargets.some((t2) => t2.x === unit.position.x && t2.y === unit.position.y)) {
16483
- const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, selectedUnit.attack - unit.defense);
16551
+ const up = unitPosition(unit);
16552
+ if (unitTeam(unit) === "enemy" && attackTargets.some((t2) => t2.x === up.x && t2.y === up.y)) {
16553
+ const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, num(selectedUnit.attack) - num(unit.defense));
16484
16554
  setIsShaking(true);
16485
16555
  setTimeout(() => setIsShaking(false), 300);
16486
16556
  onAttack?.(selectedUnit, unit, damage);
16487
16557
  if (attackEvent) {
16488
16558
  eventBus.emit(`UI:${attackEvent}`, {
16489
- attackerId: selectedUnit.id,
16490
- targetId: unit.id,
16559
+ attackerId: str(selectedUnit.id),
16560
+ targetId: str(unit.id),
16491
16561
  damage
16492
16562
  });
16493
16563
  }
@@ -16502,9 +16572,9 @@ function BattleBoard({
16502
16572
  if (currentPhase === "movement" && selectedUnit) {
16503
16573
  if (movementAnimRef.current) return;
16504
16574
  if (validMoves.some((m) => m.x === x && m.y === y)) {
16505
- const from = { ...selectedUnit.position };
16575
+ const from = { ...unitPosition(selectedUnit) };
16506
16576
  const to = { x, y };
16507
- startMoveAnimation(selectedUnit.id, from, to, () => {
16577
+ startMoveAnimation(str(selectedUnit.id), from, to, () => {
16508
16578
  onUnitMove?.(selectedUnit, to);
16509
16579
  });
16510
16580
  }
@@ -16662,6 +16732,7 @@ var init_BattleBoard = __esm({
16662
16732
  init_Typography();
16663
16733
  init_Stack();
16664
16734
  init_IsometricCanvas2();
16735
+ init_boardEntity();
16665
16736
  init_isometric();
16666
16737
  BattleBoard.displayName = "BattleBoard";
16667
16738
  }
@@ -17055,24 +17126,24 @@ var init_CodeBlock = __esm({
17055
17126
  return;
17056
17127
  }
17057
17128
  lineEls.forEach((el) => {
17058
- const num = parseInt(el.getAttribute("data-line") ?? "-1", 10);
17059
- if (hiddenLines.has(num)) {
17129
+ const num2 = parseInt(el.getAttribute("data-line") ?? "-1", 10);
17130
+ if (hiddenLines.has(num2)) {
17060
17131
  el.style.display = "none";
17061
17132
  return;
17062
17133
  }
17063
17134
  el.style.display = "";
17064
17135
  el.style.position = "relative";
17065
17136
  el.style.paddingLeft = "1.2em";
17066
- const region = foldStartMap.get(num);
17137
+ const region = foldStartMap.get(num2);
17067
17138
  if (!region) return;
17068
- const isCollapsed = collapsed.has(num);
17139
+ const isCollapsed = collapsed.has(num2);
17069
17140
  const toggle = document.createElement("span");
17070
17141
  toggle.className = "fold-toggle";
17071
17142
  toggle.textContent = isCollapsed ? "\u25B6" : "\u25BC";
17072
17143
  toggle.style.cssText = "position:absolute;left:0;top:0;width:1.2em;text-align:center;cursor:pointer;color:#858585;font-size:10px;user-select:none;line-height:inherit;height:100%";
17073
17144
  toggle.addEventListener("click", (e) => {
17074
17145
  e.stopPropagation();
17075
- toggleFoldRef.current(num);
17146
+ toggleFoldRef.current(num2);
17076
17147
  });
17077
17148
  el.insertBefore(toggle, el.firstChild);
17078
17149
  if (isCollapsed) {
@@ -19325,10 +19396,13 @@ var init_BookChapterView = __esm({
19325
19396
  init_cn();
19326
19397
  BookChapterView = ({
19327
19398
  chapter,
19399
+ orbitalSchema,
19328
19400
  direction,
19329
19401
  className
19330
19402
  }) => {
19331
19403
  const { t: _t } = hooks.useTranslate();
19404
+ const title = String(chapter.title ?? "");
19405
+ const content = String(chapter.content ?? "");
19332
19406
  return /* @__PURE__ */ jsxRuntime.jsxs(
19333
19407
  VStack,
19334
19408
  {
@@ -19336,16 +19410,16 @@ var init_BookChapterView = __esm({
19336
19410
  className: cn("px-6 py-8 max-w-4xl mx-auto w-full", className),
19337
19411
  style: { direction },
19338
19412
  children: [
19339
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h1", className: "text-3xl font-bold", children: chapter.title }),
19413
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h1", className: "text-3xl font-bold", children: title }),
19340
19414
  /* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
19341
- !!chapter.orbitalSchema && /* @__PURE__ */ jsxRuntime.jsx(ScaledDiagram, { children: /* @__PURE__ */ jsxRuntime.jsx(
19415
+ !!orbitalSchema && /* @__PURE__ */ jsxRuntime.jsx(ScaledDiagram, { children: /* @__PURE__ */ jsxRuntime.jsx(
19342
19416
  JazariStateMachine,
19343
19417
  {
19344
- schema: chapter.orbitalSchema,
19418
+ schema: orbitalSchema,
19345
19419
  direction
19346
19420
  }
19347
19421
  ) }),
19348
- /* @__PURE__ */ jsxRuntime.jsx(ContentRenderer, { content: chapter.content, direction })
19422
+ /* @__PURE__ */ jsxRuntime.jsx(ContentRenderer, { content, direction })
19349
19423
  ]
19350
19424
  }
19351
19425
  );
@@ -19443,7 +19517,7 @@ var init_BookNavBar = __esm({
19443
19517
  BookNavBar = ({
19444
19518
  currentPage,
19445
19519
  totalPages,
19446
- chapterTitle,
19520
+ chapterTitle: chapterTitle2,
19447
19521
  direction,
19448
19522
  className
19449
19523
  }) => {
@@ -19484,12 +19558,12 @@ var init_BookNavBar = __esm({
19484
19558
  )
19485
19559
  ] }),
19486
19560
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex-1 mx-4 max-w-md", children: [
19487
- chapterTitle && /* @__PURE__ */ jsxRuntime.jsx(
19561
+ chapterTitle2 && /* @__PURE__ */ jsxRuntime.jsx(
19488
19562
  Typography,
19489
19563
  {
19490
19564
  variant: "caption",
19491
19565
  className: "text-center block truncate text-muted-foreground",
19492
- children: chapterTitle
19566
+ children: chapterTitle2
19493
19567
  }
19494
19568
  ),
19495
19569
  /* @__PURE__ */ jsxRuntime.jsx(ProgressBar, { value: progress, size: "sm", variant: "primary" })
@@ -19556,31 +19630,35 @@ var init_BookTableOfContents = __esm({
19556
19630
  style: { direction },
19557
19631
  children: [
19558
19632
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h1", className: "text-3xl font-bold text-center mb-4", children: t("book.tableOfContents") }),
19559
- parts.map((part, partIdx) => /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
19560
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", align: "center", children: [
19561
- /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", children: t("book.partNumber", { number: String(partIdx + 1) }) }),
19562
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h3", className: "font-semibold", children: part.title })
19563
- ] }),
19564
- /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "xs", className: direction === "rtl" ? "pr-6" : "pl-6", children: part.chapters.map((chapter) => {
19565
- const isCurrent = chapter.id === currentChapterId;
19566
- return /* @__PURE__ */ jsxRuntime.jsx(
19567
- Button,
19568
- {
19569
- variant: "ghost",
19570
- size: "sm",
19571
- action: "BOOK_NAVIGATE",
19572
- actionPayload: { chapterId: chapter.id },
19573
- className: cn(
19574
- "justify-start text-left w-full",
19575
- direction === "rtl" && "text-right",
19576
- isCurrent && "bg-blue-50 dark:bg-blue-950 text-blue-600 dark:text-blue-400"
19577
- ),
19578
- children: /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "truncate", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: chapter.title }) })
19579
- },
19580
- chapter.id
19581
- );
19582
- }) })
19583
- ] }, partIdx))
19633
+ parts.map((part, partIdx) => {
19634
+ const chapters = Array.isArray(part.chapters) ? part.chapters : [];
19635
+ return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
19636
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", align: "center", children: [
19637
+ /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", children: t("book.partNumber", { number: String(partIdx + 1) }) }),
19638
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h3", className: "font-semibold", children: String(part.title ?? "") })
19639
+ ] }),
19640
+ /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "xs", className: direction === "rtl" ? "pr-6" : "pl-6", children: chapters.map((chapter) => {
19641
+ const id = chapter.id == null ? "" : String(chapter.id);
19642
+ const isCurrent = id === currentChapterId;
19643
+ return /* @__PURE__ */ jsxRuntime.jsx(
19644
+ Button,
19645
+ {
19646
+ variant: "ghost",
19647
+ size: "sm",
19648
+ action: "BOOK_NAVIGATE",
19649
+ actionPayload: { chapterId: id },
19650
+ className: cn(
19651
+ "justify-start text-left w-full",
19652
+ direction === "rtl" && "text-right",
19653
+ isCurrent && "bg-blue-50 dark:bg-blue-950 text-blue-600 dark:text-blue-400"
19654
+ ),
19655
+ children: /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "truncate", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: String(chapter.title ?? "") }) })
19656
+ },
19657
+ id
19658
+ );
19659
+ }) })
19660
+ ] }, partIdx);
19661
+ })
19584
19662
  ]
19585
19663
  }
19586
19664
  );
@@ -19702,27 +19780,41 @@ function resolveFieldMap(fieldMap) {
19702
19780
  function get(obj, key) {
19703
19781
  return obj[key];
19704
19782
  }
19783
+ function asStr(v) {
19784
+ return v == null ? "" : String(v);
19785
+ }
19705
19786
  function mapBookData(raw, fields = IDENTITY_BOOK_FIELDS) {
19706
19787
  const rawParts = get(raw, fields.parts) ?? [];
19707
- return {
19708
- title: get(raw, fields.title) ?? "",
19709
- subtitle: get(raw, fields.subtitle),
19710
- author: get(raw, fields.author),
19711
- coverImageUrl: get(raw, fields.coverImageUrl),
19712
- direction: get(raw, fields.direction) ?? void 0,
19713
- parts: rawParts.map((part) => {
19714
- const rawChapters = get(part, fields.chapters) ?? [];
19715
- return {
19716
- title: get(part, fields.partTitle) ?? "",
19717
- chapters: rawChapters.map((ch) => ({
19718
- id: get(ch, fields.chapterId) ?? "",
19719
- title: get(ch, fields.chapterTitle) ?? "",
19720
- content: get(ch, fields.chapterContent) ?? "",
19721
- orbitalSchema: get(ch, fields.chapterOrbitalSchema)
19722
- }))
19723
- };
19724
- })
19788
+ const direction = get(raw, fields.direction) ?? "ltr";
19789
+ const cover = {
19790
+ title: asStr(get(raw, fields.title)),
19791
+ subtitle: asStr(get(raw, fields.subtitle)),
19792
+ author: asStr(get(raw, fields.author)),
19793
+ coverImageUrl: asStr(get(raw, fields.coverImageUrl)),
19794
+ direction
19725
19795
  };
19796
+ const schemaByChapterId = {};
19797
+ const chapters = [];
19798
+ const parts = rawParts.map((part) => {
19799
+ const rawChapters = get(part, fields.chapters) ?? [];
19800
+ const chapterRows = rawChapters.map((ch) => {
19801
+ const id = asStr(get(ch, fields.chapterId));
19802
+ const schema = get(ch, fields.chapterOrbitalSchema);
19803
+ if (schema) schemaByChapterId[id] = schema;
19804
+ const row = {
19805
+ id,
19806
+ title: asStr(get(ch, fields.chapterTitle)),
19807
+ content: asStr(get(ch, fields.chapterContent))
19808
+ };
19809
+ chapters.push(row);
19810
+ return row;
19811
+ });
19812
+ return {
19813
+ title: asStr(get(part, fields.partTitle)),
19814
+ chapters: chapterRows
19815
+ };
19816
+ });
19817
+ return { cover, direction, parts, chapters, schemaByChapterId };
19726
19818
  }
19727
19819
  var IDENTITY_BOOK_FIELDS, AR_BOOK_FIELDS, FIELD_MAP_REGISTRY;
19728
19820
  var init_types2 = __esm({
@@ -19760,10 +19852,7 @@ var init_types2 = __esm({
19760
19852
  };
19761
19853
  }
19762
19854
  });
19763
- function flattenChapters(book) {
19764
- return book.parts.flatMap((part) => part.chapters);
19765
- }
19766
- var PRINT_STYLES, BookViewer;
19855
+ var chapterId, chapterTitle, PRINT_STYLES, BookViewer;
19767
19856
  var init_BookViewer = __esm({
19768
19857
  "components/marketing/organisms/book/BookViewer.tsx"() {
19769
19858
  init_Box();
@@ -19776,6 +19865,8 @@ var init_BookViewer = __esm({
19776
19865
  init_BookNavBar();
19777
19866
  init_EmptyState();
19778
19867
  init_types2();
19868
+ chapterId = (ch) => ch?.id == null ? void 0 : String(ch.id);
19869
+ chapterTitle = (ch) => ch?.title == null ? void 0 : String(ch.title);
19779
19870
  PRINT_STYLES = `
19780
19871
  @media print {
19781
19872
  .book-viewer-page {
@@ -19804,14 +19895,14 @@ var init_BookViewer = __esm({
19804
19895
  return mapBookData(raw, resolvedFieldMap);
19805
19896
  }, [entity, resolvedFieldMap]);
19806
19897
  const direction = book?.direction ?? "ltr";
19807
- const chapters = React97.useMemo(() => book ? flattenChapters(book) : [], [book]);
19898
+ const chapters = React97.useMemo(() => book ? book.chapters : [], [book]);
19808
19899
  const totalPages = 2 + chapters.length;
19809
19900
  const navigateTo = React97.useCallback(
19810
19901
  (page) => {
19811
19902
  const clamped = Math.max(0, Math.min(page, totalPages - 1));
19812
19903
  setCurrentPage(clamped);
19813
- const chapterId = clamped >= 2 ? chapters[clamped - 2]?.id : void 0;
19814
- eventBus.emit("UI:BOOK_PAGE_CHANGE", { pageIndex: clamped, chapterId });
19904
+ const id = clamped >= 2 ? chapterId(chapters[clamped - 2]) : void 0;
19905
+ eventBus.emit("UI:BOOK_PAGE_CHANGE", { pageIndex: clamped, chapterId: id });
19815
19906
  },
19816
19907
  [totalPages, chapters, eventBus]
19817
19908
  );
@@ -19823,8 +19914,8 @@ var init_BookViewer = __esm({
19823
19914
  eventBus.on("UI:BOOK_PAGE_NEXT", () => navigateTo(currentPage + 1)),
19824
19915
  eventBus.on("UI:BOOK_PRINT", () => window.print()),
19825
19916
  eventBus.on("UI:BOOK_NAVIGATE", (event) => {
19826
- const chapterId = event.payload?.chapterId;
19827
- const idx = chapters.findIndex((ch) => ch.id === chapterId);
19917
+ const targetId = event.payload?.chapterId;
19918
+ const idx = chapters.findIndex((ch) => chapterId(ch) === targetId);
19828
19919
  if (idx >= 0) navigateTo(idx + 2);
19829
19920
  })
19830
19921
  ];
@@ -19841,9 +19932,11 @@ var init_BookViewer = __esm({
19841
19932
  style.remove();
19842
19933
  };
19843
19934
  }, []);
19844
- const currentChapterId = currentPage >= 2 ? chapters[currentPage - 2]?.id : void 0;
19845
- const currentChapterTitle = currentPage >= 2 ? chapters[currentPage - 2]?.title : void 0;
19935
+ const currentChapterId = currentPage >= 2 ? chapterId(chapters[currentPage - 2]) : void 0;
19936
+ const currentChapterTitle = currentPage >= 2 ? chapterTitle(chapters[currentPage - 2]) : void 0;
19846
19937
  if (!book) return /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { message: t("book.noData") });
19938
+ const cover = book.cover;
19939
+ const coverTitle = String(cover.title ?? "");
19847
19940
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("relative h-full overflow-hidden bg-background", className), children: [
19848
19941
  /* @__PURE__ */ jsxRuntime.jsxs(
19849
19942
  Box,
@@ -19855,10 +19948,10 @@ var init_BookViewer = __esm({
19855
19948
  /* @__PURE__ */ jsxRuntime.jsx(
19856
19949
  BookCoverPage,
19857
19950
  {
19858
- title: book.title,
19859
- subtitle: book.subtitle,
19860
- author: book.author,
19861
- coverImageUrl: book.coverImageUrl,
19951
+ title: coverTitle,
19952
+ subtitle: String(cover.subtitle ?? "") || void 0,
19953
+ author: String(cover.author ?? "") || void 0,
19954
+ coverImageUrl: String(cover.coverImageUrl ?? "") || void 0,
19862
19955
  direction
19863
19956
  }
19864
19957
  ),
@@ -19869,23 +19962,27 @@ var init_BookViewer = __esm({
19869
19962
  direction
19870
19963
  }
19871
19964
  ),
19872
- chapters.map((chapter) => /* @__PURE__ */ jsxRuntime.jsx(
19873
- BookChapterView,
19874
- {
19875
- chapter,
19876
- direction
19877
- },
19878
- chapter.id
19879
- ))
19965
+ chapters.map((chapter) => {
19966
+ const id = chapterId(chapter);
19967
+ return /* @__PURE__ */ jsxRuntime.jsx(
19968
+ BookChapterView,
19969
+ {
19970
+ chapter,
19971
+ orbitalSchema: id ? book.schemaByChapterId[id] : void 0,
19972
+ direction
19973
+ },
19974
+ id
19975
+ );
19976
+ })
19880
19977
  ] }),
19881
19978
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "print:hidden", children: [
19882
19979
  currentPage === 0 && /* @__PURE__ */ jsxRuntime.jsx(
19883
19980
  BookCoverPage,
19884
19981
  {
19885
- title: book.title,
19886
- subtitle: book.subtitle,
19887
- author: book.author,
19888
- coverImageUrl: book.coverImageUrl,
19982
+ title: coverTitle,
19983
+ subtitle: String(cover.subtitle ?? "") || void 0,
19984
+ author: String(cover.author ?? "") || void 0,
19985
+ coverImageUrl: String(cover.coverImageUrl ?? "") || void 0,
19889
19986
  direction
19890
19987
  }
19891
19988
  ),
@@ -19901,6 +19998,7 @@ var init_BookViewer = __esm({
19901
19998
  BookChapterView,
19902
19999
  {
19903
20000
  chapter: chapters[currentPage - 2],
20001
+ orbitalSchema: currentChapterId ? book.schemaByChapterId[currentChapterId] : void 0,
19904
20002
  direction
19905
20003
  }
19906
20004
  )
@@ -19913,7 +20011,7 @@ var init_BookViewer = __esm({
19913
20011
  {
19914
20012
  currentPage,
19915
20013
  totalPages,
19916
- chapterTitle: currentPage === 0 ? book.title : currentPage === 1 ? t("book.tableOfContents") : currentChapterTitle,
20014
+ chapterTitle: currentPage === 0 ? coverTitle : currentPage === 1 ? t("book.tableOfContents") : currentChapterTitle,
19917
20015
  direction
19918
20016
  }
19919
20017
  )
@@ -20011,7 +20109,7 @@ var init_Grid = __esm({
20011
20109
  };
20012
20110
  Grid = ({
20013
20111
  cols = 1,
20014
- rows,
20112
+ rows: rows2,
20015
20113
  gap = "md",
20016
20114
  rowGap,
20017
20115
  colGap,
@@ -20023,7 +20121,7 @@ var init_Grid = __esm({
20023
20121
  children,
20024
20122
  as: Component = "div"
20025
20123
  }) => {
20026
- const mergedStyle = rows ? { gridTemplateRows: `repeat(${rows}, minmax(0, 1fr))`, ...style } : style;
20124
+ const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
20027
20125
  return React97__namespace.default.createElement(
20028
20126
  Component,
20029
20127
  {
@@ -20739,14 +20837,14 @@ function BuilderBoard({
20739
20837
  }) {
20740
20838
  const { emit } = useEventBus();
20741
20839
  const { t } = hooks.useTranslate();
20742
- const resolved = Array.isArray(entity) ? entity[0] : entity;
20840
+ const resolved = boardEntity(entity);
20743
20841
  const [placements, setPlacements] = React97.useState({});
20744
20842
  const [headerError, setHeaderError] = React97.useState(false);
20745
20843
  const [submitted, setSubmitted] = React97.useState(false);
20746
20844
  const [attempts, setAttempts] = React97.useState(0);
20747
20845
  const [showHint, setShowHint] = React97.useState(false);
20748
- const components = resolved?.components ?? [];
20749
- const slots = resolved?.slots ?? [];
20846
+ const components = Array.isArray(resolved?.components) ? resolved.components : [];
20847
+ const slots = Array.isArray(resolved?.slots) ? resolved.slots : [];
20750
20848
  const usedComponentIds = new Set(Object.values(placements));
20751
20849
  const availableComponents = components.filter((c) => !usedComponentIds.has(c.id));
20752
20850
  const [selectedComponent, setSelectedComponent] = React97.useState(null);
@@ -20780,7 +20878,7 @@ function BuilderBoard({
20780
20878
  }, [slots, placements, attempts, completeEvent, emit]);
20781
20879
  const handleReset = () => {
20782
20880
  setSubmitted(false);
20783
- if (attempts >= 2 && resolved?.hint) {
20881
+ if (attempts >= 2 && str(resolved?.hint)) {
20784
20882
  setShowHint(true);
20785
20883
  }
20786
20884
  };
@@ -20793,20 +20891,24 @@ function BuilderBoard({
20793
20891
  };
20794
20892
  const getComponentById = (id) => components.find((c) => c.id === id);
20795
20893
  if (!resolved) return null;
20894
+ const theme = resolved.theme ?? void 0;
20895
+ const themeBackground = theme?.background;
20896
+ const headerImage = str(resolved.headerImage);
20897
+ const hint = str(resolved.hint);
20796
20898
  return /* @__PURE__ */ jsxRuntime.jsx(
20797
20899
  Box,
20798
20900
  {
20799
20901
  className,
20800
20902
  style: {
20801
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
20903
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
20802
20904
  backgroundSize: "cover",
20803
20905
  backgroundPosition: "center"
20804
20906
  },
20805
20907
  children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: "p-4", children: [
20806
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
20908
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
20807
20909
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
20808
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
20809
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.description })
20910
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
20911
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str(resolved.description) })
20810
20912
  ] }) }),
20811
20913
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
20812
20914
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("builder.components") }),
@@ -20866,9 +20968,9 @@ function BuilderBoard({
20866
20968
  ] }) }),
20867
20969
  submitted && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", children: [
20868
20970
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: allCorrect ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
20869
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("builder.success") : resolved.failMessage ?? t("builder.incorrect") })
20971
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("builder.success") : str(resolved.failMessage) || t("builder.incorrect") })
20870
20972
  ] }) }),
20871
- showHint && resolved.hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.hint }) }),
20973
+ showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
20872
20974
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
20873
20975
  !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
20874
20976
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Wrench, size: "sm" }),
@@ -20887,6 +20989,7 @@ var init_BuilderBoard = __esm({
20887
20989
  "components/game/organisms/puzzles/builder/BuilderBoard.tsx"() {
20888
20990
  init_atoms2();
20889
20991
  init_useEventBus();
20992
+ init_boardEntity();
20890
20993
  BuilderBoard.displayName = "BuilderBoard";
20891
20994
  }
20892
20995
  });
@@ -21224,21 +21327,24 @@ function CalendarGrid({
21224
21327
  eventBus.emit(`UI:${longPressEvent}`, { date: day.toISOString(), time, ...longPressPayload });
21225
21328
  }, 500);
21226
21329
  }, [longPressEvent, longPressPayload, eventBus]);
21227
- const renderEvent = (event) => /* @__PURE__ */ jsxRuntime.jsx(
21228
- Box,
21229
- {
21230
- rounded: "md",
21231
- padding: "xs",
21232
- border: true,
21233
- className: cn(
21234
- "cursor-pointer hover:shadow-sm transition-shadow text-xs truncate",
21235
- event.color ? event.color : "bg-blue-500/15 border-blue-500/30 text-blue-600"
21236
- ),
21237
- onClick: (e) => handleEventClick(event, e),
21238
- children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "truncate font-medium", children: event.title })
21239
- },
21240
- event.id
21241
- );
21330
+ const renderEvent = (event) => {
21331
+ const color = event.color;
21332
+ return /* @__PURE__ */ jsxRuntime.jsx(
21333
+ Box,
21334
+ {
21335
+ rounded: "md",
21336
+ padding: "xs",
21337
+ border: true,
21338
+ className: cn(
21339
+ "cursor-pointer hover:shadow-sm transition-shadow text-xs truncate",
21340
+ color ? color : "bg-blue-500/15 border-blue-500/30 text-blue-600"
21341
+ ),
21342
+ onClick: (e) => handleEventClick(event, e),
21343
+ children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "truncate font-medium", children: event.title })
21344
+ },
21345
+ event.id
21346
+ );
21347
+ };
21242
21348
  return /* @__PURE__ */ jsxRuntime.jsxs(
21243
21349
  Box,
21244
21350
  {
@@ -22902,7 +23008,6 @@ var init_CardGrid = __esm({
22902
23008
  alignItems = "stretch",
22903
23009
  className,
22904
23010
  children,
22905
- // EntityDisplayProps
22906
23011
  entity,
22907
23012
  isLoading = false,
22908
23013
  error = null,
@@ -23374,14 +23479,14 @@ var init_CaseStudyOrganism = __esm({
23374
23479
  /* @__PURE__ */ jsxRuntime.jsx(SimpleGrid, { cols: cols > 0 ? cols : 1, gap: "lg", children: items.map((study) => /* @__PURE__ */ jsxRuntime.jsx(
23375
23480
  CaseStudyCard,
23376
23481
  {
23377
- title: study.title,
23378
- description: study.description,
23379
- category: study.category,
23380
- categoryColor: study.categoryColor,
23381
- href: study.href,
23382
- linkLabel: study.linkLabel
23482
+ title: String(study.title ?? ""),
23483
+ description: String(study.description ?? ""),
23484
+ category: String(study.category ?? ""),
23485
+ categoryColor: study.categoryColor != null ? String(study.categoryColor) : void 0,
23486
+ href: String(study.href ?? ""),
23487
+ linkLabel: study.linkLabel != null ? String(study.linkLabel) : void 0
23383
23488
  },
23384
- study.id
23489
+ String(study.id ?? "")
23385
23490
  )) })
23386
23491
  ] });
23387
23492
  };
@@ -23404,10 +23509,10 @@ function CastleBoard({
23404
23509
  className
23405
23510
  }) {
23406
23511
  const eventBus = useEventBus();
23407
- const resolved = Array.isArray(entity) ? entity[0] : entity;
23408
- const tiles = resolved?.tiles ?? [];
23409
- const features = resolved?.features ?? [];
23410
- const units = resolved?.units ?? [];
23512
+ const resolved = boardEntity(entity);
23513
+ const tiles = Array.isArray(resolved?.tiles) ? resolved.tiles : [];
23514
+ const features = Array.isArray(resolved?.features) ? resolved.features : [];
23515
+ const units = Array.isArray(resolved?.units) ? resolved.units : [];
23411
23516
  const assetManifest = resolved?.assetManifest;
23412
23517
  const backgroundImage = resolved?.backgroundImage;
23413
23518
  const [hoveredTile, setHoveredTile] = React97.useState(null);
@@ -23435,7 +23540,7 @@ function CastleBoard({
23435
23540
  onFeatureClick?.(feature);
23436
23541
  if (featureClickEvent) {
23437
23542
  eventBus.emit(`UI:${featureClickEvent}`, {
23438
- featureId: feature.id,
23543
+ featureId: feature.id ?? "",
23439
23544
  featureType: feature.type,
23440
23545
  x: feature.x,
23441
23546
  y: feature.y
@@ -23503,6 +23608,7 @@ var init_CastleBoard = __esm({
23503
23608
  init_cn();
23504
23609
  init_useEventBus();
23505
23610
  init_IsometricCanvas2();
23611
+ init_boardEntity();
23506
23612
  init_isometric();
23507
23613
  CastleBoard.displayName = "CastleBoard";
23508
23614
  }
@@ -24313,14 +24419,14 @@ function ClassifierBoard({
24313
24419
  }) {
24314
24420
  const { emit } = useEventBus();
24315
24421
  const { t } = hooks.useTranslate();
24316
- const resolved = Array.isArray(entity) ? entity[0] : entity;
24422
+ const resolved = boardEntity(entity);
24317
24423
  const [assignments, setAssignments] = React97.useState({});
24318
24424
  const [headerError, setHeaderError] = React97.useState(false);
24319
24425
  const [submitted, setSubmitted] = React97.useState(false);
24320
24426
  const [attempts, setAttempts] = React97.useState(0);
24321
24427
  const [showHint, setShowHint] = React97.useState(false);
24322
- const items = resolved?.items ?? [];
24323
- const categories = resolved?.categories ?? [];
24428
+ const items = Array.isArray(resolved?.items) ? resolved.items : [];
24429
+ const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
24324
24430
  const unassignedItems = items.filter((item) => !assignments[item.id]);
24325
24431
  const allAssigned = Object.keys(assignments).length === items.length;
24326
24432
  const results = submitted ? items.map((item) => ({
@@ -24352,7 +24458,7 @@ function ClassifierBoard({
24352
24458
  }, [items, assignments, attempts, completeEvent, emit]);
24353
24459
  const handleReset = () => {
24354
24460
  setSubmitted(false);
24355
- if (attempts >= 2 && resolved?.hint) {
24461
+ if (attempts >= 2 && str(resolved?.hint)) {
24356
24462
  setShowHint(true);
24357
24463
  }
24358
24464
  };
@@ -24363,20 +24469,25 @@ function ClassifierBoard({
24363
24469
  setShowHint(false);
24364
24470
  };
24365
24471
  if (!resolved) return null;
24472
+ const theme = resolved.theme ?? void 0;
24473
+ const themeBackground = theme?.background;
24474
+ const headerImage = str(resolved.headerImage);
24475
+ const hint = str(resolved.hint);
24476
+ const failMessage = str(resolved.failMessage);
24366
24477
  return /* @__PURE__ */ jsxRuntime.jsx(
24367
24478
  Box,
24368
24479
  {
24369
24480
  className,
24370
24481
  style: {
24371
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
24482
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
24372
24483
  backgroundSize: "cover",
24373
24484
  backgroundPosition: "center"
24374
24485
  },
24375
24486
  children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: "p-4", children: [
24376
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
24487
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
24377
24488
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
24378
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
24379
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.description })
24489
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
24490
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str(resolved.description) })
24380
24491
  ] }) }),
24381
24492
  unassignedItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
24382
24493
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("classifier.itemsToSort") }),
@@ -24428,10 +24539,10 @@ function ClassifierBoard({
24428
24539
  }) }),
24429
24540
  submitted && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", children: [
24430
24541
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: allCorrect ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
24431
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("classifier.allCorrect") : `${correctCount}/${items.length} ${t("classifier.correct")}` }),
24432
- !allCorrect && resolved.failMessage && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: "text-muted-foreground", children: resolved.failMessage })
24542
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("classifier.allCorrect") : `${correctCount}/${items.length} ${t("classifier.correct")}` }),
24543
+ !allCorrect && failMessage && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: "text-muted-foreground", children: failMessage })
24433
24544
  ] }) }),
24434
- showHint && resolved.hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.hint }) }),
24545
+ showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
24435
24546
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
24436
24547
  !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
24437
24548
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Send, size: "sm" }),
@@ -24450,6 +24561,7 @@ var init_ClassifierBoard = __esm({
24450
24561
  "components/game/organisms/puzzles/classifier/ClassifierBoard.tsx"() {
24451
24562
  init_atoms2();
24452
24563
  init_useEventBus();
24564
+ init_boardEntity();
24453
24565
  ClassifierBoard.displayName = "ClassifierBoard";
24454
24566
  }
24455
24567
  });
@@ -30820,7 +30932,7 @@ function InventoryPanel({
30820
30932
  const slotArray = Array.from({ length: safeSlots }, (_, index) => {
30821
30933
  return safeItems[index] ?? null;
30822
30934
  });
30823
- const rows = Math.ceil(safeSlots / safeColumns);
30935
+ const rows2 = Math.ceil(safeSlots / safeColumns);
30824
30936
  const handleSlotClick = React97.useCallback((index) => {
30825
30937
  if (selectSlotEvent) eventBus.emit(`UI:${selectSlotEvent}`, { index });
30826
30938
  onSelectSlot?.(index);
@@ -30889,7 +31001,7 @@ function InventoryPanel({
30889
31001
  className: "grid gap-1 bg-[var(--color-card)] p-2 rounded-container border border-border",
30890
31002
  style: {
30891
31003
  gridTemplateColumns: `repeat(${safeColumns}, ${slotSize}px)`,
30892
- gridTemplateRows: `repeat(${rows}, ${slotSize}px)`
31004
+ gridTemplateRows: `repeat(${rows2}, ${slotSize}px)`
30893
31005
  },
30894
31006
  children: slotArray.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
30895
31007
  "button",
@@ -34853,11 +34965,11 @@ function LatticeSVG({
34853
34965
  }) {
34854
34966
  const paths = [];
34855
34967
  const cols = 5;
34856
- const rows = Math.ceil(h / (w / cols));
34968
+ const rows2 = Math.ceil(h / (w / cols));
34857
34969
  const cellW = w / cols;
34858
34970
  const cellH = cellW;
34859
34971
  const bulge = cellW * 0.3;
34860
- for (let row = 0; row < rows; row++) {
34972
+ for (let row = 0; row < rows2; row++) {
34861
34973
  for (let col = 0; col < cols; col++) {
34862
34974
  const cx = col * cellW + cellW / 2;
34863
34975
  const cy = row * cellH + cellH / 2;
@@ -35269,7 +35381,7 @@ var init_MatrixQuestion = __esm({
35269
35381
  };
35270
35382
  MatrixQuestion = ({
35271
35383
  title,
35272
- rows,
35384
+ rows: rows2,
35273
35385
  columns = DEFAULT_MATRIX_COLUMNS,
35274
35386
  values,
35275
35387
  onChange,
@@ -35279,7 +35391,7 @@ var init_MatrixQuestion = __esm({
35279
35391
  className
35280
35392
  }) => {
35281
35393
  const styles = sizeStyles13[size];
35282
- const safeRows = rows ?? [];
35394
+ const safeRows = rows2 ?? [];
35283
35395
  const safeValues = values ?? {};
35284
35396
  const eventBus = useEventBus();
35285
35397
  const handleChange = React97.useCallback(
@@ -35907,7 +36019,8 @@ var init_PositionedCanvas = __esm({
35907
36019
  dragRef.current = null;
35908
36020
  setDraggingId(null);
35909
36021
  if (!wasDrag) {
35910
- const next = selectedId === item.id ? null : item.id;
36022
+ const itemId = item.id;
36023
+ const next = selectedId === itemId ? null : itemId;
35911
36024
  onSelect?.(next);
35912
36025
  if (selectEvent) {
35913
36026
  eventBus.emit(`UI:${selectEvent}`, { id: next });
@@ -35942,15 +36055,22 @@ var init_PositionedCanvas = __esm({
35942
36055
  style: { width, height },
35943
36056
  onClick: handleContainerClick,
35944
36057
  children: items.map((item) => {
36058
+ const itemId = item.id;
36059
+ const label = item.label;
36060
+ const x = item.x;
36061
+ const y = item.y;
36062
+ const capacity = item.capacity;
36063
+ const partySize = item.partySize;
36064
+ const serverName = item.serverName;
35945
36065
  const status = item.status ?? "empty";
35946
36066
  const shape = item.shape ?? "round";
35947
- const isSelected = selectedId === item.id;
35948
- const isDragging = draggingId === item.id;
36067
+ const isSelected = selectedId === itemId;
36068
+ const isDragging = draggingId === itemId;
35949
36069
  const statusBadge = STATUS_BADGE[status];
35950
36070
  return /* @__PURE__ */ jsxRuntime.jsxs(
35951
36071
  Box,
35952
36072
  {
35953
- "data-testid": `item-node-${item.id}`,
36073
+ "data-testid": `item-node-${itemId}`,
35954
36074
  "data-status": status,
35955
36075
  className: cn(
35956
36076
  "absolute flex flex-col items-center justify-center gap-1 border-2 select-none",
@@ -35961,7 +36081,7 @@ var init_PositionedCanvas = __esm({
35961
36081
  isSelected && "outline outline-2 outline-offset-2 outline-primary shadow-md",
35962
36082
  isDragging && "shadow-lg z-10"
35963
36083
  ),
35964
- style: { left: item.x, top: item.y, touchAction: "none" },
36084
+ style: { left: x, top: y, touchAction: "none" },
35965
36085
  onPointerDown: (e) => handlePointerDown(e, item),
35966
36086
  onPointerMove: handlePointerMove,
35967
36087
  onPointerUp: (e) => handlePointerUp(e, item),
@@ -35969,10 +36089,10 @@ var init_PositionedCanvas = __esm({
35969
36089
  children: [
35970
36090
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-1", children: [
35971
36091
  getStatusIcon(status),
35972
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", weight: "semibold", children: item.label })
36092
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", weight: "semibold", children: label })
35973
36093
  ] }),
35974
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", children: item.partySize !== void 0 && status === "seated" ? `${item.partySize}/${item.capacity}` : `Cap ${item.capacity}` }),
35975
- status === "seated" && item.serverName && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", className: "truncate max-w-[80%]", children: item.serverName }),
36094
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", children: partySize !== void 0 && status === "seated" ? `${partySize}/${capacity}` : `Cap ${capacity}` }),
36095
+ status === "seated" && serverName && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", className: "truncate max-w-[80%]", children: serverName }),
35976
36096
  isSelected && /* @__PURE__ */ jsxRuntime.jsx(
35977
36097
  Badge,
35978
36098
  {
@@ -35984,7 +36104,7 @@ var init_PositionedCanvas = __esm({
35984
36104
  )
35985
36105
  ]
35986
36106
  },
35987
- item.id
36107
+ itemId
35988
36108
  );
35989
36109
  })
35990
36110
  }
@@ -36756,9 +36876,10 @@ var init_RichBlockEditor = __esm({
36756
36876
  });
36757
36877
  function collectInitiallyCollapsed(nodes, acc) {
36758
36878
  for (const n of nodes) {
36879
+ const replies = n.replies;
36759
36880
  if (n.collapsed) acc.add(n.id);
36760
- if (n.replies && n.replies.length > 0) {
36761
- collectInitiallyCollapsed(n.replies, acc);
36881
+ if (replies && replies.length > 0) {
36882
+ collectInitiallyCollapsed(replies, acc);
36762
36883
  }
36763
36884
  }
36764
36885
  }
@@ -36788,44 +36909,52 @@ var init_ReplyTree = __esm({
36788
36909
  }) => {
36789
36910
  const eventBus = useEventBus();
36790
36911
  const { t } = hooks.useTranslate();
36791
- const hasReplies = !!node.replies && node.replies.length > 0;
36792
- const isCollapsed = collapsedSet.has(node.id);
36912
+ const nodeId = node.id;
36913
+ const authorName = node.authorName;
36914
+ const authorAvatarUrl = node.authorAvatarUrl;
36915
+ const content = node.content;
36916
+ const postedAt = node.postedAt;
36917
+ const voteCount = node.voteCount;
36918
+ const userVote = node.userVote;
36919
+ const replies = node.replies;
36920
+ const hasReplies = !!replies && replies.length > 0;
36921
+ const isCollapsed = collapsedSet.has(nodeId);
36793
36922
  const atMaxDepth = depth >= maxDepth;
36794
36923
  const [replyOpen, setReplyOpen] = React97.useState(false);
36795
36924
  const [draft, setDraft] = React97.useState("");
36796
36925
  const handleVote = React97.useCallback(
36797
36926
  (next) => {
36798
- onVote?.(node.id, next);
36799
- if (voteEvent) eventBus.emit(`UI:${voteEvent}`, { nodeId: node.id, vote: next });
36927
+ onVote?.(nodeId, next);
36928
+ if (voteEvent) eventBus.emit(`UI:${voteEvent}`, { nodeId, vote: next });
36800
36929
  },
36801
- [node.id, onVote, voteEvent, eventBus]
36930
+ [nodeId, onVote, voteEvent, eventBus]
36802
36931
  );
36803
36932
  const handleReply = React97.useCallback(() => {
36804
- onReply?.(node.id);
36933
+ onReply?.(nodeId);
36805
36934
  setReplyOpen((open) => !open);
36806
- }, [node.id, onReply]);
36935
+ }, [nodeId, onReply]);
36807
36936
  const handleSubmitReply = React97.useCallback(() => {
36808
- const content = draft.trim();
36809
- if (!content) return;
36810
- if (replyEvent) eventBus.emit(`UI:${replyEvent}`, { parentNodeId: node.id, content });
36937
+ const text = draft.trim();
36938
+ if (!text) return;
36939
+ if (replyEvent) eventBus.emit(`UI:${replyEvent}`, { parentNodeId: nodeId, content: text });
36811
36940
  setDraft("");
36812
36941
  setReplyOpen(false);
36813
- }, [node.id, draft, replyEvent, eventBus]);
36942
+ }, [nodeId, draft, replyEvent, eventBus]);
36814
36943
  const handleCancelReply = React97.useCallback(() => {
36815
36944
  setDraft("");
36816
36945
  setReplyOpen(false);
36817
36946
  }, []);
36818
36947
  const handleFlag = React97.useCallback(() => {
36819
- onFlag?.(node.id);
36820
- if (flagEvent) eventBus.emit(`UI:${flagEvent}`, { nodeId: node.id });
36821
- }, [node.id, onFlag, flagEvent, eventBus]);
36948
+ onFlag?.(nodeId);
36949
+ if (flagEvent) eventBus.emit(`UI:${flagEvent}`, { nodeId });
36950
+ }, [nodeId, onFlag, flagEvent, eventBus]);
36822
36951
  const handleContinue = React97.useCallback(() => {
36823
- onContinueThread?.(node.id);
36824
- if (continueThreadEvent) eventBus.emit(`UI:${continueThreadEvent}`, { nodeId: node.id });
36825
- }, [node.id, onContinueThread, continueThreadEvent, eventBus]);
36952
+ onContinueThread?.(nodeId);
36953
+ if (continueThreadEvent) eventBus.emit(`UI:${continueThreadEvent}`, { nodeId });
36954
+ }, [nodeId, onContinueThread, continueThreadEvent, eventBus]);
36826
36955
  const handleToggle = React97.useCallback(() => {
36827
- toggleCollapse(node.id);
36828
- }, [node.id, toggleCollapse]);
36956
+ toggleCollapse(nodeId);
36957
+ }, [nodeId, toggleCollapse]);
36829
36958
  return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex flex-row gap-2 items-stretch min-w-0", children: [
36830
36959
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex flex-col items-center flex-shrink-0 w-6", children: [
36831
36960
  hasReplies ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -36857,25 +36986,25 @@ var init_ReplyTree = __esm({
36857
36986
  /* @__PURE__ */ jsxRuntime.jsx(
36858
36987
  Avatar,
36859
36988
  {
36860
- src: node.authorAvatarUrl,
36861
- name: node.authorName,
36989
+ src: authorAvatarUrl,
36990
+ name: authorName,
36862
36991
  size: "sm"
36863
36992
  }
36864
36993
  ),
36865
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "semibold", children: node.authorName }),
36866
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", children: node.postedAt })
36994
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "semibold", children: authorName }),
36995
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", children: postedAt })
36867
36996
  ] }),
36868
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: "whitespace-pre-wrap break-words", children: node.content }),
36997
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: "whitespace-pre-wrap break-words", children: content }),
36869
36998
  showActions && /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex flex-row gap-2 items-center", children: [
36870
36999
  /* @__PURE__ */ jsxRuntime.jsx(
36871
37000
  VoteStack,
36872
37001
  {
36873
- count: node.voteCount ?? 0,
36874
- userVote: node.userVote ?? null,
37002
+ count: voteCount ?? 0,
37003
+ userVote: userVote ?? null,
36875
37004
  onVote: handleVote,
36876
37005
  size: "sm",
36877
37006
  variant: "horizontal",
36878
- label: t("replyTree.voteOnReplyBy", { author: node.authorName })
37007
+ label: t("replyTree.voteOnReplyBy", { author: authorName })
36879
37008
  }
36880
37009
  ),
36881
37010
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -36885,7 +37014,7 @@ var init_ReplyTree = __esm({
36885
37014
  size: "sm",
36886
37015
  leftIcon: "message-square",
36887
37016
  onClick: handleReply,
36888
- "aria-label": t("replyTree.replyTo", { author: node.authorName }),
37017
+ "aria-label": t("replyTree.replyTo", { author: authorName }),
36889
37018
  children: t("replyTree.reply")
36890
37019
  }
36891
37020
  ),
@@ -36896,7 +37025,7 @@ var init_ReplyTree = __esm({
36896
37025
  size: "sm",
36897
37026
  leftIcon: "flag",
36898
37027
  onClick: handleFlag,
36899
- "aria-label": t("replyTree.flagReplyBy", { author: node.authorName }),
37028
+ "aria-label": t("replyTree.flagReplyBy", { author: authorName }),
36900
37029
  children: t("replyTree.flag")
36901
37030
  }
36902
37031
  )
@@ -36908,9 +37037,9 @@ var init_ReplyTree = __esm({
36908
37037
  inputType: "textarea",
36909
37038
  rows: 2,
36910
37039
  value: draft,
36911
- placeholder: t("replyTree.replyToPlaceholder", { author: node.authorName }),
37040
+ placeholder: t("replyTree.replyToPlaceholder", { author: authorName }),
36912
37041
  onChange: (e) => setDraft(e.target.value),
36913
- "aria-label": t("replyTree.replyTo", { author: node.authorName })
37042
+ "aria-label": t("replyTree.replyTo", { author: authorName })
36914
37043
  }
36915
37044
  ),
36916
37045
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex flex-row gap-2 items-center", children: [
@@ -36941,7 +37070,7 @@ var init_ReplyTree = __esm({
36941
37070
  ),
36942
37071
  children: t("replyTree.continueThread")
36943
37072
  }
36944
- ) : /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex flex-col gap-2 mt-1", children: node.replies.map((child) => /* @__PURE__ */ jsxRuntime.jsx(
37073
+ ) : /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex flex-col gap-2 mt-1", children: replies.map((child) => /* @__PURE__ */ jsxRuntime.jsx(
36945
37074
  ReplyTreeNode,
36946
37075
  {
36947
37076
  node: child,
@@ -38852,8 +38981,8 @@ var init_WizardContainer = __esm({
38852
38981
  return void 0;
38853
38982
  if (typeof controlledStep === "number") return controlledStep;
38854
38983
  if (typeof controlledStep === "string") return parseInt(controlledStep, 10);
38855
- const num = Number(controlledStep);
38856
- return isNaN(num) ? void 0 : num;
38984
+ const num2 = Number(controlledStep);
38985
+ return isNaN(num2) ? void 0 : num2;
38857
38986
  })();
38858
38987
  const currentStep = normalizedControlledStep !== void 0 ? normalizedControlledStep : internalStep;
38859
38988
  const totalSteps = steps.length;
@@ -40558,7 +40687,7 @@ function DebuggerBoard({
40558
40687
  }) {
40559
40688
  const { emit } = useEventBus();
40560
40689
  const { t } = hooks.useTranslate();
40561
- const resolved = Array.isArray(entity) ? entity[0] : entity;
40690
+ const resolved = boardEntity(entity);
40562
40691
  const [flaggedLines, setFlaggedLines] = React97.useState(/* @__PURE__ */ new Set());
40563
40692
  const [headerError, setHeaderError] = React97.useState(false);
40564
40693
  const [submitted, setSubmitted] = React97.useState(false);
@@ -40576,7 +40705,7 @@ function DebuggerBoard({
40576
40705
  return next;
40577
40706
  });
40578
40707
  };
40579
- const lines = resolved?.lines ?? [];
40708
+ const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
40580
40709
  const bugLines = lines.filter((l) => l.isBug);
40581
40710
  const correctFlags = lines.filter((l) => l.isBug && flaggedLines.has(l.id));
40582
40711
  const falseFlags = lines.filter((l) => !l.isBug && flaggedLines.has(l.id));
@@ -40591,7 +40720,7 @@ function DebuggerBoard({
40591
40720
  }, [correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
40592
40721
  const handleReset = () => {
40593
40722
  setSubmitted(false);
40594
- if (attempts >= 2 && resolved?.hint) {
40723
+ if (attempts >= 2 && str(resolved?.hint)) {
40595
40724
  setShowHint(true);
40596
40725
  }
40597
40726
  };
@@ -40602,24 +40731,28 @@ function DebuggerBoard({
40602
40731
  setShowHint(false);
40603
40732
  };
40604
40733
  if (!resolved) return null;
40734
+ const theme = resolved.theme ?? void 0;
40735
+ const themeBackground = theme?.background;
40736
+ const headerImage = str(resolved.headerImage);
40737
+ const hint = str(resolved.hint);
40605
40738
  return /* @__PURE__ */ jsxRuntime.jsx(
40606
40739
  Box,
40607
40740
  {
40608
40741
  className,
40609
40742
  style: {
40610
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
40743
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
40611
40744
  backgroundSize: "cover",
40612
40745
  backgroundPosition: "center"
40613
40746
  },
40614
40747
  children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: "p-4", children: [
40615
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
40748
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
40616
40749
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
40617
40750
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
40618
40751
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Bug, size: "sm" }),
40619
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title })
40752
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) })
40620
40753
  ] }),
40621
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.description }),
40622
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(resolved.bugCount) }) })
40754
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str(resolved.description) }),
40755
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(num(resolved.bugCount)) }) })
40623
40756
  ] }) }),
40624
40757
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "none", children: lines.map((line, i) => {
40625
40758
  const isFlagged = flaggedLines.has(line.id);
@@ -40651,7 +40784,7 @@ function DebuggerBoard({
40651
40784
  );
40652
40785
  }) }) }),
40653
40786
  submitted && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
40654
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("debugger.allFound") : `${correctFlags.length}/${bugLines.length} ${t("debugger.bugsFound")}` }),
40787
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("debugger.allFound") : `${correctFlags.length}/${bugLines.length} ${t("debugger.bugsFound")}` }),
40655
40788
  bugLines.map((line) => /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "start", children: [
40656
40789
  /* @__PURE__ */ jsxRuntime.jsx(
40657
40790
  Icon,
@@ -40667,7 +40800,7 @@ function DebuggerBoard({
40667
40800
  ] })
40668
40801
  ] }, line.id))
40669
40802
  ] }) }),
40670
- showHint && resolved.hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.hint }) }),
40803
+ showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
40671
40804
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
40672
40805
  !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.size === 0, children: [
40673
40806
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Send, size: "sm" }),
@@ -40686,6 +40819,7 @@ var init_DebuggerBoard = __esm({
40686
40819
  "components/game/organisms/puzzles/debugger/DebuggerBoard.tsx"() {
40687
40820
  init_atoms2();
40688
40821
  init_useEventBus();
40822
+ init_boardEntity();
40689
40823
  DebuggerBoard.displayName = "DebuggerBoard";
40690
40824
  }
40691
40825
  });
@@ -40723,7 +40857,7 @@ function getBadgeVariant(fieldName, value) {
40723
40857
  return "default";
40724
40858
  }
40725
40859
  function formatFieldLabel(fieldName) {
40726
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase());
40860
+ return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str2) => str2.toUpperCase());
40727
40861
  }
40728
40862
  function formatFieldValue(value, fieldName) {
40729
40863
  if (typeof value === "number") {
@@ -40742,26 +40876,26 @@ function formatFieldValue(value, fieldName) {
40742
40876
  }
40743
40877
  function renderRichFieldValue(value, fieldName, fieldType) {
40744
40878
  if (value === void 0 || value === null) return "\u2014";
40745
- const str = String(value);
40879
+ const str2 = String(value);
40746
40880
  switch (fieldType) {
40747
40881
  case "image":
40748
40882
  case "url": {
40749
- if (str.match(/\.(png|jpe?g|gif|svg|webp|avif)(\?|$)/i) || str.startsWith("data:image/")) {
40883
+ if (str2.match(/\.(png|jpe?g|gif|svg|webp|avif)(\?|$)/i) || str2.startsWith("data:image/")) {
40750
40884
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mt-1 max-w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
40751
40885
  "img",
40752
40886
  {
40753
- src: str,
40887
+ src: str2,
40754
40888
  alt: formatFieldLabel(fieldName),
40755
40889
  className: "max-w-full max-h-64 rounded-md object-contain",
40756
40890
  loading: "lazy"
40757
40891
  }
40758
40892
  ) });
40759
40893
  }
40760
- return str;
40894
+ return str2;
40761
40895
  }
40762
40896
  case "markdown":
40763
40897
  case "richtext":
40764
- return /* @__PURE__ */ jsxRuntime.jsx(React97.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: "break-words", children: str }), children: /* @__PURE__ */ jsxRuntime.jsx(
40898
+ return /* @__PURE__ */ jsxRuntime.jsx(React97.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: "break-words", children: str2 }), children: /* @__PURE__ */ jsxRuntime.jsx(
40765
40899
  Box,
40766
40900
  {
40767
40901
  className: "prose prose-sm max-w-none",
@@ -40779,11 +40913,11 @@ function renderRichFieldValue(value, fieldName, fieldType) {
40779
40913
  "--tw-prose-th-borders": "var(--color-border)",
40780
40914
  "--tw-prose-td-borders": "var(--color-border)"
40781
40915
  },
40782
- children: /* @__PURE__ */ jsxRuntime.jsx(ReactMarkdown2, { children: str })
40916
+ children: /* @__PURE__ */ jsxRuntime.jsx(ReactMarkdown2, { children: str2 })
40783
40917
  }
40784
40918
  ) });
40785
40919
  case "code":
40786
- return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mt-1 rounded-md bg-muted p-3 overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "text-sm font-mono whitespace-pre-wrap break-words m-0", children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: str }) }) });
40920
+ return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mt-1 rounded-md bg-muted p-3 overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "text-sm font-mono whitespace-pre-wrap break-words m-0", children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: str2 }) }) });
40787
40921
  case "html":
40788
40922
  return /* @__PURE__ */ jsxRuntime.jsx(
40789
40923
  Box,
@@ -40803,12 +40937,12 @@ function renderRichFieldValue(value, fieldName, fieldType) {
40803
40937
  "--tw-prose-th-borders": "var(--color-border)",
40804
40938
  "--tw-prose-td-borders": "var(--color-border)"
40805
40939
  },
40806
- children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str })
40940
+ children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str2 })
40807
40941
  }
40808
40942
  );
40809
40943
  case "date":
40810
40944
  case "datetime": {
40811
- const d = new Date(str);
40945
+ const d = new Date(str2);
40812
40946
  if (!isNaN(d.getTime())) {
40813
40947
  return d.toLocaleDateString(void 0, {
40814
40948
  year: "numeric",
@@ -40817,7 +40951,7 @@ function renderRichFieldValue(value, fieldName, fieldType) {
40817
40951
  ...fieldType === "datetime" ? { hour: "2-digit", minute: "2-digit" } : {}
40818
40952
  });
40819
40953
  }
40820
- return str;
40954
+ return str2;
40821
40955
  }
40822
40956
  default:
40823
40957
  return formatFieldValue(value, fieldName);
@@ -41495,6 +41629,40 @@ var init_RuleEditor = __esm({
41495
41629
  RuleEditor.displayName = "RuleEditor";
41496
41630
  }
41497
41631
  });
41632
+
41633
+ // components/game/organisms/puzzles/event-handler/puzzleObject.ts
41634
+ function objId(o) {
41635
+ return o.id == null ? "" : String(o.id);
41636
+ }
41637
+ function objName(o) {
41638
+ return o.name == null ? "" : String(o.name);
41639
+ }
41640
+ function objIcon(o) {
41641
+ return o.icon == null ? "" : String(o.icon);
41642
+ }
41643
+ function objStates(o) {
41644
+ return Array.isArray(o.states) ? o.states : [];
41645
+ }
41646
+ function objCurrentState(o) {
41647
+ return o.currentState == null ? "" : String(o.currentState);
41648
+ }
41649
+ function objAvailableEvents(o) {
41650
+ return Array.isArray(o.availableEvents) ? o.availableEvents : [];
41651
+ }
41652
+ function objAvailableActions(o) {
41653
+ return Array.isArray(o.availableActions) ? o.availableActions : [];
41654
+ }
41655
+ function objRules(o) {
41656
+ return Array.isArray(o.rules) ? o.rules : [];
41657
+ }
41658
+ function objMaxRules(o) {
41659
+ const n = Number(o.maxRules);
41660
+ return Number.isFinite(n) && n > 0 ? n : 3;
41661
+ }
41662
+ var init_puzzleObject = __esm({
41663
+ "components/game/organisms/puzzles/event-handler/puzzleObject.ts"() {
41664
+ }
41665
+ });
41498
41666
  function ObjectRulePanel({
41499
41667
  object,
41500
41668
  onRulesChange,
@@ -41502,55 +41670,63 @@ function ObjectRulePanel({
41502
41670
  className
41503
41671
  }) {
41504
41672
  const { t } = hooks.useTranslate();
41505
- const maxRules = object.maxRules || 3;
41506
- const canAdd = object.rules.length < maxRules;
41673
+ const id = objId(object);
41674
+ const name = objName(object);
41675
+ const icon = objIcon(object);
41676
+ const states = objStates(object);
41677
+ const currentState = objCurrentState(object);
41678
+ const availableEvents = objAvailableEvents(object);
41679
+ const availableActions = objAvailableActions(object);
41680
+ const rules = objRules(object);
41681
+ const maxRules = objMaxRules(object);
41682
+ const canAdd = rules.length < maxRules;
41507
41683
  const handleRuleChange = React97.useCallback((index, updatedRule) => {
41508
- const newRules = [...object.rules];
41684
+ const newRules = [...rules];
41509
41685
  newRules[index] = updatedRule;
41510
- onRulesChange(object.id, newRules);
41511
- }, [object.id, object.rules, onRulesChange]);
41686
+ onRulesChange(id, newRules);
41687
+ }, [id, rules, onRulesChange]);
41512
41688
  const handleRuleRemove = React97.useCallback((index) => {
41513
- const newRules = object.rules.filter((_, i) => i !== index);
41514
- onRulesChange(object.id, newRules);
41515
- }, [object.id, object.rules, onRulesChange]);
41689
+ const newRules = rules.filter((_, i) => i !== index);
41690
+ onRulesChange(id, newRules);
41691
+ }, [id, rules, onRulesChange]);
41516
41692
  const handleAddRule = React97.useCallback(() => {
41517
41693
  if (!canAdd || disabled) return;
41518
- const firstEvent = object.availableEvents[0]?.value || "";
41519
- const firstAction = object.availableActions[0]?.value || "";
41694
+ const firstEvent = availableEvents[0]?.value || "";
41695
+ const firstAction = availableActions[0]?.value || "";
41520
41696
  const newRule = {
41521
41697
  id: `rule-${nextRuleId++}`,
41522
41698
  whenEvent: firstEvent,
41523
41699
  thenAction: firstAction
41524
41700
  };
41525
- onRulesChange(object.id, [...object.rules, newRule]);
41526
- }, [canAdd, disabled, object, onRulesChange]);
41701
+ onRulesChange(id, [...rules, newRule]);
41702
+ }, [canAdd, disabled, id, rules, availableEvents, availableActions, onRulesChange]);
41527
41703
  const machine = {
41528
- name: object.name,
41529
- states: object.states,
41530
- currentState: object.currentState,
41531
- transitions: object.rules.map((r2) => ({
41532
- from: object.currentState,
41533
- to: object.states.find((s) => s !== object.currentState) || object.currentState,
41704
+ name,
41705
+ states,
41706
+ currentState,
41707
+ transitions: rules.map((r2) => ({
41708
+ from: currentState,
41709
+ to: states.find((s) => s !== currentState) || currentState,
41534
41710
  event: r2.whenEvent
41535
41711
  }))
41536
41712
  };
41537
41713
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("p-4 rounded-lg bg-card border border-border", className), gap: "sm", children: [
41538
41714
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center", gap: "sm", children: [
41539
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", children: object.icon }),
41715
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", children: icon }),
41540
41716
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", children: [
41541
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-foreground font-bold", children: object.name }),
41542
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("eventHandler.state") + ": " + object.currentState })
41717
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-foreground font-bold", children: name }),
41718
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("eventHandler.state") + ": " + currentState })
41543
41719
  ] })
41544
41720
  ] }),
41545
41721
  /* @__PURE__ */ jsxRuntime.jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" }),
41546
41722
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
41547
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.rules", { count: object.rules.length, max: maxRules }) + ":" }),
41548
- object.rules.map((rule, i) => /* @__PURE__ */ jsxRuntime.jsx(
41723
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.rules", { count: rules.length, max: maxRules }) + ":" }),
41724
+ rules.map((rule, i) => /* @__PURE__ */ jsxRuntime.jsx(
41549
41725
  RuleEditor,
41550
41726
  {
41551
41727
  rule,
41552
- availableEvents: object.availableEvents,
41553
- availableActions: object.availableActions,
41728
+ availableEvents,
41729
+ availableActions,
41554
41730
  onChange: (r2) => handleRuleChange(i, r2),
41555
41731
  onRemove: () => handleRuleRemove(i),
41556
41732
  disabled
@@ -41568,6 +41744,7 @@ var init_ObjectRulePanel = __esm({
41568
41744
  init_cn();
41569
41745
  init_TraitStateViewer();
41570
41746
  init_RuleEditor();
41747
+ init_puzzleObject();
41571
41748
  nextRuleId = 1;
41572
41749
  ObjectRulePanel.displayName = "ObjectRulePanel";
41573
41750
  }
@@ -41634,11 +41811,11 @@ function EventHandlerBoard({
41634
41811
  }) {
41635
41812
  const { emit } = useEventBus();
41636
41813
  const { t } = hooks.useTranslate();
41637
- const resolved = Array.isArray(entity) ? entity[0] : entity;
41638
- const entityObjects = resolved?.objects ?? [];
41639
- const [objects, setObjects] = React97.useState(entityObjects);
41814
+ const resolved = boardEntity(entity);
41815
+ const entityObjects = rows(resolved?.objects);
41816
+ const [objects, setObjects] = React97.useState(() => [...entityObjects]);
41640
41817
  const [selectedObjectId, setSelectedObjectId] = React97.useState(
41641
- entityObjects[0]?.id || null
41818
+ entityObjects[0] ? objId(entityObjects[0]) : null
41642
41819
  );
41643
41820
  const [headerError, setHeaderError] = React97.useState(false);
41644
41821
  const [playState, setPlayState] = React97.useState("editing");
@@ -41649,10 +41826,10 @@ function EventHandlerBoard({
41649
41826
  React97.useEffect(() => () => {
41650
41827
  if (timerRef.current) clearTimeout(timerRef.current);
41651
41828
  }, []);
41652
- const selectedObject = objects.find((o) => o.id === selectedObjectId) || null;
41829
+ const selectedObject = objects.find((o) => objId(o) === selectedObjectId) || null;
41653
41830
  const handleRulesChange = React97.useCallback((objectId, rules) => {
41654
41831
  setObjects((prev) => prev.map(
41655
- (o) => o.id === objectId ? { ...o, rules } : o
41832
+ (o) => objId(o) === objectId ? { ...o, rules } : o
41656
41833
  ));
41657
41834
  }, []);
41658
41835
  const addLogEntry = React97.useCallback((icon, message, status = "done") => {
@@ -41666,11 +41843,12 @@ function EventHandlerBoard({
41666
41843
  setEventLog([]);
41667
41844
  const allRules = [];
41668
41845
  objects.forEach((obj) => {
41669
- obj.rules.forEach((rule) => {
41846
+ objRules(obj).forEach((rule) => {
41670
41847
  allRules.push({ object: obj, rule });
41671
41848
  });
41672
41849
  });
41673
- const triggers = resolved?.triggerEvents || [];
41850
+ const triggers = Array.isArray(resolved?.triggerEvents) ? resolved.triggerEvents : [];
41851
+ const goalEvent = str(resolved?.goalEvent);
41674
41852
  const eventQueue = [...triggers];
41675
41853
  const firedEvents = /* @__PURE__ */ new Set();
41676
41854
  let stepIdx = 0;
@@ -41699,14 +41877,14 @@ function EventHandlerBoard({
41699
41877
  addLogEntry("\u26A1", t("eventHandler.noListeners", { event: currentEvent }), "done");
41700
41878
  } else {
41701
41879
  matching.forEach(({ object, rule }) => {
41702
- addLogEntry(object.icon, t("eventHandler.heardEvent", { object: object.name, event: currentEvent, action: rule.thenAction }), "done");
41880
+ addLogEntry(objIcon(object), t("eventHandler.heardEvent", { object: objName(object), event: currentEvent, action: rule.thenAction }), "done");
41703
41881
  eventQueue.push(rule.thenAction);
41704
- if (rule.thenAction === resolved?.goalEvent) {
41882
+ if (rule.thenAction === goalEvent) {
41705
41883
  goalReached = true;
41706
41884
  }
41707
41885
  });
41708
41886
  }
41709
- if (currentEvent === resolved?.goalEvent) {
41887
+ if (currentEvent === goalEvent) {
41710
41888
  goalReached = true;
41711
41889
  }
41712
41890
  stepIdx++;
@@ -41724,65 +41902,75 @@ function EventHandlerBoard({
41724
41902
  }, []);
41725
41903
  const handleReset = React97.useCallback(() => {
41726
41904
  if (timerRef.current) clearTimeout(timerRef.current);
41727
- setObjects(resolved?.objects ?? []);
41905
+ const resetObjects = rows(resolved?.objects);
41906
+ setObjects([...resetObjects]);
41728
41907
  setPlayState("editing");
41729
41908
  setEventLog([]);
41730
- setSelectedObjectId((resolved?.objects ?? [])[0]?.id || null);
41909
+ setSelectedObjectId(resetObjects[0] ? objId(resetObjects[0]) : null);
41731
41910
  setAttempts(0);
41732
41911
  }, [resolved?.objects]);
41733
41912
  if (!resolved) return null;
41734
41913
  const objectViewers = objects.map((obj) => {
41914
+ const states = objStates(obj);
41915
+ const currentState = objCurrentState(obj);
41735
41916
  const machine = {
41736
- name: obj.name,
41737
- states: obj.states,
41738
- currentState: obj.currentState,
41739
- transitions: obj.rules.map((r2) => ({
41740
- from: obj.currentState,
41741
- to: obj.states.find((s) => s !== obj.currentState) || obj.currentState,
41917
+ name: objName(obj),
41918
+ states,
41919
+ currentState,
41920
+ transitions: objRules(obj).map((r2) => ({
41921
+ from: currentState,
41922
+ to: states.find((s) => s !== currentState) || currentState,
41742
41923
  event: r2.whenEvent
41743
41924
  }))
41744
41925
  };
41745
41926
  return { obj, machine };
41746
41927
  });
41747
- const showHint = attempts >= 3 && resolved.hint;
41928
+ const hint = str(resolved.hint);
41929
+ const showHint = attempts >= 3 && hint;
41930
+ const theme = resolved.theme ?? void 0;
41931
+ const themeBackground = theme?.background;
41932
+ const headerImage = str(resolved.headerImage);
41748
41933
  const encourageKey = ENCOURAGEMENT_KEYS[Math.min(attempts - 1, ENCOURAGEMENT_KEYS.length - 1)] ?? ENCOURAGEMENT_KEYS[0];
41749
41934
  return /* @__PURE__ */ jsxRuntime.jsxs(
41750
41935
  VStack,
41751
41936
  {
41752
41937
  className: cn("p-4 gap-6", className),
41753
41938
  style: {
41754
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
41939
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
41755
41940
  backgroundSize: "cover",
41756
41941
  backgroundPosition: "center"
41757
41942
  },
41758
41943
  children: [
41759
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
41944
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
41760
41945
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
41761
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
41762
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description }),
41946
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
41947
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) }),
41763
41948
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center p-2 rounded bg-primary/10 border border-primary/30", gap: "xs", children: [
41764
41949
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-primary font-bold", children: t("game.goal") + ":" }),
41765
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-foreground", children: resolved.goalCondition })
41950
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-foreground", children: str(resolved.goalCondition) })
41766
41951
  ] })
41767
41952
  ] }),
41768
41953
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
41769
41954
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.clickObject") + ":" }),
41770
- /* @__PURE__ */ jsxRuntime.jsx(HStack, { className: "flex-wrap", gap: "sm", children: objectViewers.map(({ obj, machine }) => /* @__PURE__ */ jsxRuntime.jsx(
41771
- Box,
41772
- {
41773
- className: cn(
41774
- "p-3 rounded-container border-2 cursor-pointer transition-all hover:scale-105",
41775
- selectedObjectId === obj.id ? "border-primary bg-primary/10" : "border-border bg-card hover:border-muted-foreground"
41776
- ),
41777
- onClick: () => setSelectedObjectId(obj.id),
41778
- children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "items-center min-w-[120px]", children: [
41779
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", children: obj.icon }),
41780
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground font-medium", children: obj.name }),
41781
- /* @__PURE__ */ jsxRuntime.jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" })
41782
- ] })
41783
- },
41784
- obj.id
41785
- )) })
41955
+ /* @__PURE__ */ jsxRuntime.jsx(HStack, { className: "flex-wrap", gap: "sm", children: objectViewers.map(({ obj, machine }) => {
41956
+ const oid = objId(obj);
41957
+ return /* @__PURE__ */ jsxRuntime.jsx(
41958
+ Box,
41959
+ {
41960
+ className: cn(
41961
+ "p-3 rounded-container border-2 cursor-pointer transition-all hover:scale-105",
41962
+ selectedObjectId === oid ? "border-primary bg-primary/10" : "border-border bg-card hover:border-muted-foreground"
41963
+ ),
41964
+ onClick: () => setSelectedObjectId(oid),
41965
+ children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "items-center min-w-[120px]", children: [
41966
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", children: objIcon(obj) }),
41967
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground font-medium", children: objName(obj) }),
41968
+ /* @__PURE__ */ jsxRuntime.jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" })
41969
+ ] })
41970
+ },
41971
+ oid
41972
+ );
41973
+ }) })
41786
41974
  ] }),
41787
41975
  selectedObject && /* @__PURE__ */ jsxRuntime.jsx(
41788
41976
  ObjectRulePanel,
@@ -41793,12 +41981,12 @@ function EventHandlerBoard({
41793
41981
  }
41794
41982
  ),
41795
41983
  eventLog.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(EventLog, { entries: eventLog }),
41796
- playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: resolved.successMessage || t("eventHandler.chainComplete") }) }),
41984
+ playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("eventHandler.chainComplete") }) }),
41797
41985
  playState === "fail" && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
41798
41986
  /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(encourageKey) }) }),
41799
41987
  showHint && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-start", gap: "xs", children: [
41800
41988
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
41801
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
41989
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
41802
41990
  ] }) })
41803
41991
  ] }),
41804
41992
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
@@ -41826,6 +42014,8 @@ var init_EventHandlerBoard = __esm({
41826
42014
  init_TraitStateViewer();
41827
42015
  init_ObjectRulePanel();
41828
42016
  init_EventLog();
42017
+ init_puzzleObject();
42018
+ init_boardEntity();
41829
42019
  ENCOURAGEMENT_KEYS = [
41830
42020
  "puzzle.tryAgain1",
41831
42021
  "puzzle.tryAgain2",
@@ -41914,7 +42104,10 @@ var init_FeatureGridOrganism = __esm({
41914
42104
  );
41915
42105
  React97.useCallback(
41916
42106
  (feature) => {
41917
- eventBus.emit("UI:FEATURE_CLICK", { id: feature.id, href: feature.href ?? "" });
42107
+ eventBus.emit("UI:FEATURE_CLICK", {
42108
+ id: String(feature.id ?? ""),
42109
+ href: String(feature.href ?? "")
42110
+ });
41918
42111
  },
41919
42112
  [eventBus]
41920
42113
  );
@@ -41924,14 +42117,17 @@ var init_FeatureGridOrganism = __esm({
41924
42117
  if (error) {
41925
42118
  return /* @__PURE__ */ jsxRuntime.jsx(ErrorState, { message: error.message, className });
41926
42119
  }
41927
- const featureCards = items.map((feature) => ({
41928
- icon: feature.icon,
41929
- title: feature.title,
41930
- description: feature.description,
41931
- href: feature.href,
41932
- linkLabel: feature.linkLabel,
41933
- variant: feature.href ? "interactive" : "bordered"
41934
- }));
42120
+ const featureCards = items.map((feature) => {
42121
+ const href = feature.href != null ? String(feature.href) : void 0;
42122
+ return {
42123
+ icon: feature.icon != null ? String(feature.icon) : void 0,
42124
+ title: String(feature.title ?? ""),
42125
+ description: String(feature.description ?? ""),
42126
+ href,
42127
+ linkLabel: feature.linkLabel != null ? String(feature.linkLabel) : void 0,
42128
+ variant: href ? "interactive" : "bordered"
42129
+ };
42130
+ });
41935
42131
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
41936
42132
  (heading || subtitle) && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
41937
42133
  heading && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", align: "center", children: heading }),
@@ -43249,22 +43445,24 @@ var init_HeroOrganism = __esm({
43249
43445
  () => Array.isArray(entity) ? entity[0] : entity && typeof entity === "object" ? entity : void 0,
43250
43446
  [entity]
43251
43447
  );
43448
+ const primaryAction = resolved?.primaryAction;
43449
+ const secondaryAction = resolved?.secondaryAction;
43252
43450
  const handlePrimaryClick = React97.useCallback(() => {
43253
- if (resolved?.primaryAction) {
43451
+ if (primaryAction) {
43254
43452
  eventBus.emit("UI:CTA_PRIMARY", {
43255
- label: resolved.primaryAction.label,
43256
- href: resolved.primaryAction.href
43453
+ label: String(primaryAction.label ?? ""),
43454
+ href: String(primaryAction.href ?? "")
43257
43455
  });
43258
43456
  }
43259
- }, [eventBus, resolved]);
43457
+ }, [eventBus, primaryAction]);
43260
43458
  const handleSecondaryClick = React97.useCallback(() => {
43261
- if (resolved?.secondaryAction) {
43459
+ if (secondaryAction) {
43262
43460
  eventBus.emit("UI:CTA_SECONDARY", {
43263
- label: resolved.secondaryAction.label,
43264
- href: resolved.secondaryAction.href
43461
+ label: String(secondaryAction.label ?? ""),
43462
+ href: String(secondaryAction.href ?? "")
43265
43463
  });
43266
43464
  }
43267
- }, [eventBus, resolved]);
43465
+ }, [eventBus, secondaryAction]);
43268
43466
  if (isLoading) {
43269
43467
  return /* @__PURE__ */ jsxRuntime.jsx(LoadingState, { message: t("common.loading"), className });
43270
43468
  }
@@ -43274,17 +43472,19 @@ var init_HeroOrganism = __esm({
43274
43472
  if (!resolved) {
43275
43473
  return null;
43276
43474
  }
43475
+ const imageRaw = resolved.image;
43476
+ const image = imageRaw ? { src: String(imageRaw.src ?? ""), alt: String(imageRaw.alt ?? "") } : void 0;
43277
43477
  return /* @__PURE__ */ jsxRuntime.jsxs(
43278
43478
  HeroSection,
43279
43479
  {
43280
- tag: resolved.tag,
43281
- title: resolved.title,
43282
- titleAccent: resolved.titleAccent,
43283
- subtitle: resolved.subtitle,
43284
- primaryAction: resolved.primaryAction ? { label: resolved.primaryAction.label, href: resolved.primaryAction.href } : void 0,
43285
- secondaryAction: resolved.secondaryAction ? { label: resolved.secondaryAction.label, href: resolved.secondaryAction.href } : void 0,
43286
- installCommand: resolved.installCommand,
43287
- image: resolved.image,
43480
+ tag: resolved.tag != null ? String(resolved.tag) : void 0,
43481
+ title: String(resolved.title ?? ""),
43482
+ titleAccent: resolved.titleAccent != null ? String(resolved.titleAccent) : void 0,
43483
+ subtitle: String(resolved.subtitle ?? ""),
43484
+ primaryAction: primaryAction ? { label: String(primaryAction.label ?? ""), href: String(primaryAction.href ?? "") } : void 0,
43485
+ secondaryAction: secondaryAction ? { label: String(secondaryAction.label ?? ""), href: String(secondaryAction.href ?? "") } : void 0,
43486
+ installCommand: resolved.installCommand != null ? String(resolved.installCommand) : void 0,
43487
+ image,
43288
43488
  imagePosition: resolved.imagePosition,
43289
43489
  background: resolved.background,
43290
43490
  className: cn(className),
@@ -43293,8 +43493,8 @@ var init_HeroOrganism = __esm({
43293
43493
  /* @__PURE__ */ jsxRuntime.jsx(
43294
43494
  _HeroClickInterceptor,
43295
43495
  {
43296
- hasPrimary: !!resolved.primaryAction,
43297
- hasSecondary: !!resolved.secondaryAction,
43496
+ hasPrimary: !!primaryAction,
43497
+ hasSecondary: !!secondaryAction,
43298
43498
  onPrimaryClick: handlePrimaryClick,
43299
43499
  onSecondaryClick: handleSecondaryClick
43300
43500
  }
@@ -43523,7 +43723,7 @@ function formatValue3(value, fieldName) {
43523
43723
  return String(value);
43524
43724
  }
43525
43725
  function formatFieldLabel2(fieldName) {
43526
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).replace(/Id$/, "").trim();
43726
+ return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str2) => str2.toUpperCase()).replace(/Id$/, "").trim();
43527
43727
  }
43528
43728
  var STATUS_STYLES2, StatusBadge, ProgressIndicator, List3;
43529
43729
  var init_List = __esm({
@@ -44370,20 +44570,22 @@ function NegotiatorBoard({
44370
44570
  }) {
44371
44571
  const { emit } = useEventBus();
44372
44572
  const { t } = hooks.useTranslate();
44373
- const resolved = Array.isArray(entity) ? entity[0] : entity;
44573
+ const resolved = boardEntity(entity);
44374
44574
  const [history, setHistory] = React97.useState([]);
44375
44575
  const [headerError, setHeaderError] = React97.useState(false);
44376
44576
  const [showHint, setShowHint] = React97.useState(false);
44577
+ const totalRounds = num(resolved?.totalRounds);
44578
+ const targetScore = num(resolved?.targetScore);
44377
44579
  const currentRound = history.length;
44378
- const isComplete = currentRound >= (resolved?.totalRounds ?? 0);
44580
+ const isComplete = currentRound >= totalRounds;
44379
44581
  const playerTotal = history.reduce((s, r2) => s + r2.playerPayoff, 0);
44380
44582
  const opponentTotal = history.reduce((s, r2) => s + r2.opponentPayoff, 0);
44381
- const won = isComplete && playerTotal >= (resolved?.targetScore ?? 0);
44382
- const actions = resolved?.actions ?? [];
44383
- const payoffMatrix = resolved?.payoffMatrix ?? [];
44583
+ const won = isComplete && playerTotal >= targetScore;
44584
+ const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
44585
+ const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
44384
44586
  const handleAction = React97.useCallback((actionId) => {
44385
44587
  if (isComplete) return;
44386
- const opponentAction = getOpponentAction(resolved?.opponentStrategy ?? "random", actions, history);
44588
+ const opponentAction = getOpponentAction(str(resolved?.opponentStrategy) || "random", actions, history);
44387
44589
  const payoff = payoffMatrix.find(
44388
44590
  (p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
44389
44591
  );
@@ -44396,42 +44598,46 @@ function NegotiatorBoard({
44396
44598
  };
44397
44599
  const newHistory = [...history, result];
44398
44600
  setHistory(newHistory);
44399
- if (newHistory.length >= (resolved?.totalRounds ?? 0)) {
44601
+ if (newHistory.length >= totalRounds) {
44400
44602
  const total = newHistory.reduce((s, r2) => s + r2.playerPayoff, 0);
44401
- if (total >= (resolved?.targetScore ?? 0)) {
44603
+ if (total >= targetScore) {
44402
44604
  emit(`UI:${completeEvent}`, { success: true, score: total });
44403
44605
  }
44404
- if (newHistory.length >= 3 && resolved?.hint) {
44606
+ if (newHistory.length >= 3 && str(resolved?.hint)) {
44405
44607
  setShowHint(true);
44406
44608
  }
44407
44609
  }
44408
- }, [isComplete, resolved, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
44610
+ }, [isComplete, resolved, totalRounds, targetScore, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
44409
44611
  const handleReset = () => {
44410
44612
  setHistory([]);
44411
44613
  setShowHint(false);
44412
44614
  };
44413
44615
  const getActionLabel = (id) => actions.find((a) => a.id === id)?.label ?? id;
44414
44616
  if (!resolved) return null;
44617
+ const theme = resolved.theme ?? void 0;
44618
+ const themeBackground = theme?.background;
44619
+ const headerImage = str(resolved.headerImage);
44620
+ const hint = str(resolved.hint);
44415
44621
  return /* @__PURE__ */ jsxRuntime.jsx(
44416
44622
  Box,
44417
44623
  {
44418
44624
  className,
44419
44625
  style: {
44420
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
44626
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
44421
44627
  backgroundSize: "cover",
44422
44628
  backgroundPosition: "center"
44423
44629
  },
44424
44630
  children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: "p-4", children: [
44425
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
44631
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
44426
44632
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
44427
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
44428
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.description }),
44633
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
44634
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str(resolved.description) }),
44429
44635
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "md", children: [
44430
- /* @__PURE__ */ jsxRuntime.jsx(Badge, { size: "sm", children: t("negotiator.round", { current: String(currentRound), total: String(resolved.totalRounds) }) }),
44636
+ /* @__PURE__ */ jsxRuntime.jsx(Badge, { size: "sm", children: t("negotiator.round", { current: String(currentRound), total: String(totalRounds) }) }),
44431
44637
  /* @__PURE__ */ jsxRuntime.jsxs(Badge, { size: "sm", children: [
44432
44638
  t("negotiator.target"),
44433
44639
  ": ",
44434
- resolved.targetScore
44640
+ targetScore
44435
44641
  ] })
44436
44642
  ] })
44437
44643
  ] }) }),
@@ -44480,16 +44686,16 @@ function NegotiatorBoard({
44480
44686
  ] }) }),
44481
44687
  isComplete && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", children: [
44482
44688
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.CheckCircle, size: "lg", className: won ? "text-success" : "text-error" }),
44483
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: won ? resolved.successMessage ?? t("negotiator.success") : resolved.failMessage ?? t("negotiator.failed") }),
44689
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: won ? str(resolved.successMessage) || t("negotiator.success") : str(resolved.failMessage) || t("negotiator.failed") }),
44484
44690
  /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
44485
44691
  t("negotiator.finalScore"),
44486
44692
  ": ",
44487
44693
  playerTotal,
44488
44694
  "/",
44489
- resolved.targetScore
44695
+ targetScore
44490
44696
  ] })
44491
44697
  ] }) }),
44492
- showHint && resolved.hint && !won && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.hint }) }),
44698
+ showHint && hint && !won && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
44493
44699
  isComplete && !won && /* @__PURE__ */ jsxRuntime.jsx(HStack, { justify: "center", children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleReset, children: t("negotiator.playAgain") }) })
44494
44700
  ] })
44495
44701
  }
@@ -44499,6 +44705,7 @@ var init_NegotiatorBoard = __esm({
44499
44705
  "components/game/organisms/puzzles/negotiator/NegotiatorBoard.tsx"() {
44500
44706
  init_atoms2();
44501
44707
  init_useEventBus();
44708
+ init_boardEntity();
44502
44709
  NegotiatorBoard.displayName = "NegotiatorBoard";
44503
44710
  }
44504
44711
  });
@@ -44534,13 +44741,13 @@ var init_PricingOrganism = __esm({
44534
44741
  return /* @__PURE__ */ jsxRuntime.jsx(ErrorState, { message: error.message, className });
44535
44742
  }
44536
44743
  const plans = items.map((plan) => ({
44537
- name: plan.name,
44538
- price: plan.price,
44539
- description: plan.description,
44540
- features: plan.features,
44541
- action: { label: plan.actionLabel, href: plan.actionHref },
44542
- highlighted: plan.highlighted,
44543
- badge: plan.badge
44744
+ name: String(plan.name ?? ""),
44745
+ price: String(plan.price ?? ""),
44746
+ description: plan.description != null ? String(plan.description) : void 0,
44747
+ features: (plan.features ?? []).map((f3) => String(f3)),
44748
+ action: { label: String(plan.actionLabel ?? ""), href: String(plan.actionHref ?? "") },
44749
+ highlighted: Boolean(plan.highlighted),
44750
+ badge: plan.badge != null ? String(plan.badge) : void 0
44544
44751
  }));
44545
44752
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
44546
44753
  (heading || subtitle) && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
@@ -46630,16 +46837,20 @@ function SequencerBoard({
46630
46837
  }) {
46631
46838
  const { emit } = useEventBus();
46632
46839
  const { t } = hooks.useTranslate();
46633
- const resolved = Array.isArray(entity) ? entity[0] : entity;
46840
+ const resolved = boardEntity(entity);
46841
+ const maxSlots = num(resolved?.maxSlots);
46842
+ const solutions = Array.isArray(resolved?.solutions) ? resolved.solutions : [];
46843
+ const availableActions = Array.isArray(resolved?.availableActions) ? resolved.availableActions : [];
46844
+ const allowDuplicates = resolved?.allowDuplicates !== false;
46634
46845
  const [headerError, setHeaderError] = React97.useState(false);
46635
46846
  const [slots, setSlots] = React97.useState(
46636
- () => Array.from({ length: resolved?.maxSlots ?? 0 }, () => void 0)
46847
+ () => Array.from({ length: maxSlots }, () => void 0)
46637
46848
  );
46638
46849
  const [playState, setPlayState] = React97.useState("idle");
46639
46850
  const [currentStep, setCurrentStep] = React97.useState(-1);
46640
46851
  const [attempts, setAttempts] = React97.useState(0);
46641
46852
  const [slotFeedback, setSlotFeedback] = React97.useState(
46642
- () => Array.from({ length: resolved?.maxSlots ?? 0 }, () => null)
46853
+ () => Array.from({ length: maxSlots }, () => null)
46643
46854
  );
46644
46855
  const timerRef = React97.useRef(null);
46645
46856
  React97.useEffect(() => () => {
@@ -46673,17 +46884,17 @@ function SequencerBoard({
46673
46884
  }, [emit]);
46674
46885
  const handleReset = React97.useCallback(() => {
46675
46886
  if (timerRef.current) clearTimeout(timerRef.current);
46676
- setSlots(Array.from({ length: resolved?.maxSlots ?? 0 }, () => void 0));
46887
+ setSlots(Array.from({ length: maxSlots }, () => void 0));
46677
46888
  setPlayState("idle");
46678
46889
  setCurrentStep(-1);
46679
46890
  setAttempts(0);
46680
- setSlotFeedback(Array.from({ length: resolved?.maxSlots ?? 0 }, () => null));
46681
- }, [resolved?.maxSlots]);
46891
+ setSlotFeedback(Array.from({ length: maxSlots }, () => null));
46892
+ }, [maxSlots]);
46682
46893
  const filledSlots = slots.filter((s) => !!s);
46683
46894
  const canPlay = filledSlots.length > 0 && playState === "idle";
46684
46895
  const handlePlay = React97.useCallback(() => {
46685
46896
  if (!canPlay) return;
46686
- setSlotFeedback(Array.from({ length: resolved?.maxSlots ?? 0 }, () => null));
46897
+ setSlotFeedback(Array.from({ length: maxSlots }, () => null));
46687
46898
  emit("UI:PLAY_SOUND", { key: "confirm" });
46688
46899
  const sequence = slots.map((s) => s?.id || "");
46689
46900
  if (playEvent) {
@@ -46694,10 +46905,10 @@ function SequencerBoard({
46694
46905
  let step = 0;
46695
46906
  const advance = () => {
46696
46907
  step++;
46697
- if (step >= (resolved?.maxSlots ?? 0)) {
46908
+ if (step >= maxSlots) {
46698
46909
  const playerSeq = slots.map((s) => s?.id);
46699
46910
  const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
46700
- const success = (resolved?.solutions ?? []).some(
46911
+ const success = solutions.some(
46701
46912
  (sol) => sol.length === playerIds.length && sol.every((id, i) => id === playerIds[i])
46702
46913
  );
46703
46914
  if (success) {
@@ -46709,7 +46920,7 @@ function SequencerBoard({
46709
46920
  }
46710
46921
  } else {
46711
46922
  setAttempts((prev) => prev + 1);
46712
- const feedback = computeSlotFeedback(playerSeq, resolved?.solutions ?? []);
46923
+ const feedback = computeSlotFeedback(playerSeq, solutions);
46713
46924
  setSlotFeedback(feedback);
46714
46925
  setPlayState("idle");
46715
46926
  setCurrentStep(-1);
@@ -46727,10 +46938,10 @@ function SequencerBoard({
46727
46938
  }
46728
46939
  };
46729
46940
  timerRef.current = setTimeout(advance, stepDurationMs);
46730
- }, [canPlay, slots, resolved?.maxSlots, resolved?.solutions, stepDurationMs, playEvent, completeEvent, emit]);
46941
+ }, [canPlay, slots, maxSlots, solutions, stepDurationMs, playEvent, completeEvent, emit]);
46731
46942
  const machine = {
46732
- name: resolved?.title ?? "",
46733
- description: resolved?.description ?? "",
46943
+ name: str(resolved?.title),
46944
+ description: str(resolved?.description),
46734
46945
  states: slots.map((s, i) => stepLabel(s, i)),
46735
46946
  currentState: currentStep >= 0 ? stepLabel(slots[currentStep], currentStep) : "__idle__",
46736
46947
  transitions: slots.slice(0, -1).map((s, i) => ({
@@ -46739,37 +46950,41 @@ function SequencerBoard({
46739
46950
  event: "NEXT"
46740
46951
  }))
46741
46952
  };
46742
- const usedIds = resolved?.allowDuplicates === false ? slots.filter(Boolean).map((s) => s?.id || "") : [];
46743
- const showHint = attempts >= 3 && !!resolved?.hint;
46953
+ const usedIds = !allowDuplicates ? slots.filter(Boolean).map((s) => s?.id || "") : [];
46954
+ const hint = str(resolved?.hint);
46955
+ const showHint = attempts >= 3 && !!hint;
46744
46956
  const hasFeedback = slotFeedback.some((f3) => f3 !== null);
46745
46957
  const correctCount = slotFeedback.filter((f3) => f3 === "correct").length;
46746
46958
  const encourageKey = ENCOURAGEMENT_KEYS2[Math.min(attempts - 1, ENCOURAGEMENT_KEYS2.length - 1)] ?? ENCOURAGEMENT_KEYS2[0];
46747
46959
  if (!resolved) return null;
46960
+ const theme = resolved.theme ?? void 0;
46961
+ const themeBackground = theme?.background;
46962
+ const headerImage = str(resolved.headerImage);
46748
46963
  return /* @__PURE__ */ jsxRuntime.jsxs(
46749
46964
  VStack,
46750
46965
  {
46751
46966
  className: cn("p-4 gap-6", className),
46752
46967
  style: {
46753
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
46968
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
46754
46969
  backgroundSize: "cover",
46755
46970
  backgroundPosition: "center"
46756
46971
  },
46757
46972
  children: [
46758
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
46973
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
46759
46974
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
46760
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
46761
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description })
46975
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
46976
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) })
46762
46977
  ] }),
46763
46978
  showHint && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-start", gap: "xs", children: [
46764
46979
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
46765
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
46980
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
46766
46981
  ] }) }),
46767
46982
  filledSlots.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(TraitStateViewer, { trait: machine, variant: "linear", size: "md" }),
46768
46983
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
46769
46984
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center justify-between", children: [
46770
46985
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("sequencer.yourSequence") + ":" }),
46771
46986
  hasFeedback && playState === "idle" && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
46772
- `${correctCount}/${resolved.maxSlots} `,
46987
+ `${correctCount}/${maxSlots} `,
46773
46988
  "\u2705"
46774
46989
  ] })
46775
46990
  ] }),
@@ -46777,7 +46992,7 @@ function SequencerBoard({
46777
46992
  SequenceBar,
46778
46993
  {
46779
46994
  slots,
46780
- maxSlots: resolved.maxSlots,
46995
+ maxSlots,
46781
46996
  onSlotDrop: handleSlotDrop,
46782
46997
  onSlotRemove: handleSlotRemove,
46783
46998
  playing: playState === "playing",
@@ -46791,15 +47006,15 @@ function SequencerBoard({
46791
47006
  playState !== "playing" && /* @__PURE__ */ jsxRuntime.jsx(
46792
47007
  ActionPalette,
46793
47008
  {
46794
- actions: resolved.availableActions,
47009
+ actions: availableActions,
46795
47010
  usedActionIds: usedIds,
46796
- allowDuplicates: resolved.allowDuplicates !== false,
47011
+ allowDuplicates,
46797
47012
  categoryColors,
46798
47013
  label: t("sequencer.dragActions")
46799
47014
  }
46800
47015
  ),
46801
47016
  hasFeedback && playState === "idle" && attempts > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: t(encourageKey) }) }),
46802
- playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: resolved.successMessage || t("sequencer.levelComplete") }) }),
47017
+ playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("sequencer.levelComplete") }) }),
46803
47018
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
46804
47019
  /* @__PURE__ */ jsxRuntime.jsx(
46805
47020
  Button,
@@ -46823,6 +47038,7 @@ var init_SequencerBoard = __esm({
46823
47038
  init_cn();
46824
47039
  init_useEventBus();
46825
47040
  init_TraitStateViewer();
47041
+ init_boardEntity();
46826
47042
  init_SequenceBar();
46827
47043
  init_ActionPalette();
46828
47044
  ENCOURAGEMENT_KEYS2 = [
@@ -46872,18 +47088,21 @@ var init_ShowcaseOrganism = __esm({
46872
47088
  heading && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", align: "center", children: heading }),
46873
47089
  subtitle && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", color: "muted", align: "center", className: "max-w-2xl", children: subtitle })
46874
47090
  ] }),
46875
- /* @__PURE__ */ jsxRuntime.jsx(SimpleGrid, { cols: columns, gap: "lg", children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
46876
- ShowcaseCard,
46877
- {
46878
- title: item.title,
46879
- description: item.description,
46880
- image: item.image,
46881
- href: item.href,
46882
- badge: item.badge,
46883
- accentColor: item.accentColor
46884
- },
46885
- item.id
46886
- )) })
47091
+ /* @__PURE__ */ jsxRuntime.jsx(SimpleGrid, { cols: columns, gap: "lg", children: items.map((item) => {
47092
+ const imageRaw = item.image;
47093
+ return /* @__PURE__ */ jsxRuntime.jsx(
47094
+ ShowcaseCard,
47095
+ {
47096
+ title: String(item.title ?? ""),
47097
+ description: item.description != null ? String(item.description) : void 0,
47098
+ image: { src: String(imageRaw?.src ?? ""), alt: String(imageRaw?.alt ?? "") },
47099
+ href: item.href != null ? String(item.href) : void 0,
47100
+ badge: item.badge != null ? String(item.badge) : void 0,
47101
+ accentColor: item.accentColor != null ? String(item.accentColor) : void 0
47102
+ },
47103
+ String(item.id ?? "")
47104
+ );
47105
+ }) })
46887
47106
  ] });
46888
47107
  };
46889
47108
  ShowcaseOrganism.displayName = "ShowcaseOrganism";
@@ -47251,8 +47470,8 @@ function SimulatorBoard({
47251
47470
  }) {
47252
47471
  const { emit } = useEventBus();
47253
47472
  const { t } = hooks.useTranslate();
47254
- const resolved = Array.isArray(entity) ? entity[0] : entity;
47255
- const parameters = resolved?.parameters ?? [];
47473
+ const resolved = boardEntity(entity);
47474
+ const parameters = Array.isArray(resolved?.parameters) ? resolved.parameters : [];
47256
47475
  const [values, setValues] = React97.useState(() => {
47257
47476
  const init = {};
47258
47477
  for (const p2 of parameters) {
@@ -47266,15 +47485,15 @@ function SimulatorBoard({
47266
47485
  const [showHint, setShowHint] = React97.useState(false);
47267
47486
  const computeOutput = React97.useCallback((params) => {
47268
47487
  try {
47269
- const fn = new Function("params", `return (${resolved?.computeExpression})`);
47488
+ const fn = new Function("params", `return (${str(resolved?.computeExpression)})`);
47270
47489
  return fn(params);
47271
47490
  } catch {
47272
47491
  return 0;
47273
47492
  }
47274
47493
  }, [resolved?.computeExpression]);
47275
47494
  const output = React97.useMemo(() => computeOutput(values) ?? 0, [computeOutput, values]);
47276
- const targetValue = resolved?.targetValue ?? 0;
47277
- const targetTolerance = resolved?.targetTolerance ?? 0;
47495
+ const targetValue = num(resolved?.targetValue);
47496
+ const targetTolerance = num(resolved?.targetTolerance);
47278
47497
  const isCorrect = Math.abs(output - targetValue) <= targetTolerance;
47279
47498
  const handleParameterChange = (id, value) => {
47280
47499
  if (submitted) return;
@@ -47289,7 +47508,7 @@ function SimulatorBoard({
47289
47508
  };
47290
47509
  const handleReset = () => {
47291
47510
  setSubmitted(false);
47292
- if (attempts >= 2 && resolved?.hint) {
47511
+ if (attempts >= 2 && str(resolved?.hint)) {
47293
47512
  setShowHint(true);
47294
47513
  }
47295
47514
  };
@@ -47304,20 +47523,26 @@ function SimulatorBoard({
47304
47523
  setShowHint(false);
47305
47524
  };
47306
47525
  if (!resolved) return null;
47526
+ const theme = resolved.theme ?? void 0;
47527
+ const themeBackground = theme?.background;
47528
+ const headerImage = str(resolved.headerImage);
47529
+ const hint = str(resolved.hint);
47530
+ const outputLabel = str(resolved.outputLabel);
47531
+ const outputUnit = str(resolved.outputUnit);
47307
47532
  return /* @__PURE__ */ jsxRuntime.jsx(
47308
47533
  Box,
47309
47534
  {
47310
47535
  className,
47311
47536
  style: {
47312
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
47537
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
47313
47538
  backgroundSize: "cover",
47314
47539
  backgroundPosition: "center"
47315
47540
  },
47316
47541
  children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: "p-4", children: [
47317
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
47542
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
47318
47543
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
47319
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
47320
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.description })
47544
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
47545
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str(resolved.description) })
47321
47546
  ] }) }),
47322
47547
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", children: [
47323
47548
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("simulator.parameters") }),
@@ -47358,28 +47583,28 @@ function SimulatorBoard({
47358
47583
  ] }, param.id))
47359
47584
  ] }) }),
47360
47585
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", children: [
47361
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: resolved.outputLabel }),
47586
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: outputLabel }),
47362
47587
  /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "h3", weight: "bold", children: [
47363
47588
  output.toFixed(2),
47364
47589
  " ",
47365
- resolved.outputUnit
47590
+ outputUnit
47366
47591
  ] }),
47367
47592
  submitted && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
47368
47593
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: isCorrect ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: isCorrect ? "text-success" : "text-error" }),
47369
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: isCorrect ? "text-success" : "text-error", children: isCorrect ? resolved.successMessage ?? t("simulator.correct") : resolved.failMessage ?? t("simulator.incorrect") })
47594
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: isCorrect ? "text-success" : "text-error", children: isCorrect ? str(resolved.successMessage) || t("simulator.correct") : str(resolved.failMessage) || t("simulator.incorrect") })
47370
47595
  ] }),
47371
47596
  /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
47372
47597
  t("simulator.target"),
47373
47598
  ": ",
47374
47599
  targetValue,
47375
47600
  " ",
47376
- resolved.outputUnit,
47601
+ outputUnit,
47377
47602
  " (\xB1",
47378
47603
  targetTolerance,
47379
47604
  ")"
47380
47605
  ] })
47381
47606
  ] }) }),
47382
- showHint && resolved.hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.hint }) }),
47607
+ showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
47383
47608
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
47384
47609
  !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, children: [
47385
47610
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Play, size: "sm" }),
@@ -47398,6 +47623,7 @@ var init_SimulatorBoard = __esm({
47398
47623
  "components/game/organisms/puzzles/simulator/SimulatorBoard.tsx"() {
47399
47624
  init_atoms2();
47400
47625
  init_useEventBus();
47626
+ init_boardEntity();
47401
47627
  SimulatorBoard.displayName = "SimulatorBoard";
47402
47628
  }
47403
47629
  });
@@ -47823,22 +48049,25 @@ function VariablePanel({
47823
48049
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("p-3 rounded-lg bg-card border border-border", className), gap: "sm", children: [
47824
48050
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("stateArchitect.variables", { name: entityName }) }),
47825
48051
  variables.map((v) => {
47826
- const max = v.max ?? 100;
47827
- const min = v.min ?? 0;
47828
- const pct = Math.round((v.value - min) / (max - min) * 100);
48052
+ const name = v.name == null ? "" : String(v.name);
48053
+ const value = numField(v.value);
48054
+ const max = numField(v.max, 100);
48055
+ const min = numField(v.min, 0);
48056
+ const unit = v.unit == null ? "" : String(v.unit);
48057
+ const pct = Math.round((value - min) / (max - min) * 100);
47829
48058
  const isHigh = pct > 80;
47830
48059
  const isLow = pct < 20;
47831
48060
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", children: [
47832
48061
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center justify-between", children: [
47833
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-foreground font-medium", children: v.name }),
48062
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-foreground font-medium", children: name }),
47834
48063
  /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: cn(
47835
48064
  isHigh ? "text-error" : isLow ? "text-warning" : "text-foreground"
47836
48065
  ), children: [
47837
- v.value,
47838
- v.unit || "",
48066
+ value,
48067
+ unit,
47839
48068
  " / ",
47840
48069
  max,
47841
- v.unit || ""
48070
+ unit
47842
48071
  ] })
47843
48072
  ] }),
47844
48073
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -47849,14 +48078,19 @@ function VariablePanel({
47849
48078
  size: "sm"
47850
48079
  }
47851
48080
  )
47852
- ] }, v.name);
48081
+ ] }, name);
47853
48082
  })
47854
48083
  ] });
47855
48084
  }
48085
+ var numField;
47856
48086
  var init_VariablePanel = __esm({
47857
48087
  "components/game/organisms/puzzles/state-architect/VariablePanel.tsx"() {
47858
48088
  init_atoms2();
47859
48089
  init_cn();
48090
+ numField = (v, fallback = 0) => {
48091
+ const n = Number(v);
48092
+ return Number.isFinite(n) ? n : fallback;
48093
+ };
47860
48094
  VariablePanel.displayName = "VariablePanel";
47861
48095
  }
47862
48096
  });
@@ -47883,14 +48117,21 @@ function StateArchitectBoard({
47883
48117
  }) {
47884
48118
  const { emit } = useEventBus();
47885
48119
  const { t } = hooks.useTranslate();
47886
- const resolved = Array.isArray(entity) ? entity[0] : entity;
47887
- const [transitions, setTransitions] = React97.useState(resolved?.transitions ?? []);
48120
+ const resolved = boardEntity(entity);
48121
+ const entityStates = Array.isArray(resolved?.states) ? resolved.states : [];
48122
+ const initialState = str(resolved?.initialState);
48123
+ const entityName = str(resolved?.entityName);
48124
+ const availableEvents = Array.isArray(resolved?.availableEvents) ? resolved.availableEvents : [];
48125
+ const testCases = Array.isArray(resolved?.testCases) ? resolved.testCases : [];
48126
+ const entityTransitions = Array.isArray(resolved?.transitions) ? resolved.transitions : [];
48127
+ const entityVariables = rows(resolved?.variables);
48128
+ const [transitions, setTransitions] = React97.useState(entityTransitions);
47888
48129
  const [headerError, setHeaderError] = React97.useState(false);
47889
48130
  const [playState, setPlayState] = React97.useState("editing");
47890
- const [currentState, setCurrentState] = React97.useState(resolved?.initialState ?? "");
48131
+ const [currentState, setCurrentState] = React97.useState(initialState);
47891
48132
  const [selectedState, setSelectedState] = React97.useState(null);
47892
48133
  const [testResults, setTestResults] = React97.useState([]);
47893
- const [variables, setVariables] = React97.useState(resolved?.variables ?? []);
48134
+ const [variables, setVariables] = React97.useState(() => [...entityVariables]);
47894
48135
  const [attempts, setAttempts] = React97.useState(0);
47895
48136
  const timerRef = React97.useRef(null);
47896
48137
  const [addingFrom, setAddingFrom] = React97.useState(null);
@@ -47899,12 +48140,12 @@ function StateArchitectBoard({
47899
48140
  }, []);
47900
48141
  const GRAPH_W = 500;
47901
48142
  const GRAPH_H = 400;
47902
- const positions = React97.useMemo(() => layoutStates(resolved?.states ?? [], GRAPH_W, GRAPH_H), [resolved?.states]);
48143
+ const positions = React97.useMemo(() => layoutStates(entityStates, GRAPH_W, GRAPH_H), [entityStates]);
47903
48144
  const handleStateClick = React97.useCallback((state) => {
47904
48145
  if (playState !== "editing") return;
47905
48146
  if (addingFrom) {
47906
48147
  if (addingFrom !== state) {
47907
- const event = resolved?.availableEvents[0] || "EVENT";
48148
+ const event = availableEvents[0] || "EVENT";
47908
48149
  const newTrans = {
47909
48150
  id: `t-${nextTransId++}`,
47910
48151
  from: addingFrom,
@@ -47917,7 +48158,7 @@ function StateArchitectBoard({
47917
48158
  } else {
47918
48159
  setSelectedState(state);
47919
48160
  }
47920
- }, [playState, addingFrom, resolved?.availableEvents]);
48161
+ }, [playState, addingFrom, availableEvents]);
47921
48162
  const handleStartAddTransition = React97.useCallback(() => {
47922
48163
  if (!selectedState) return;
47923
48164
  setAddingFrom(selectedState);
@@ -47926,9 +48167,9 @@ function StateArchitectBoard({
47926
48167
  setTransitions((prev) => prev.filter((t2) => t2.id !== transId));
47927
48168
  }, []);
47928
48169
  const machine = React97.useMemo(() => ({
47929
- name: resolved?.entityName ?? "",
47930
- description: resolved?.description ?? "",
47931
- states: resolved?.states ?? [],
48170
+ name: entityName,
48171
+ description: str(resolved?.description),
48172
+ states: entityStates,
47932
48173
  currentState,
47933
48174
  transitions: transitions.map((t2) => ({
47934
48175
  from: t2.from,
@@ -47936,7 +48177,7 @@ function StateArchitectBoard({
47936
48177
  event: t2.event,
47937
48178
  guardHint: t2.guardHint
47938
48179
  }))
47939
- }), [resolved, currentState, transitions]);
48180
+ }), [entityName, resolved, entityStates, currentState, transitions]);
47940
48181
  const handleTest = React97.useCallback(() => {
47941
48182
  if (playState !== "editing") return;
47942
48183
  if (testEvent) emit(`UI:${testEvent}`, {});
@@ -47945,7 +48186,7 @@ function StateArchitectBoard({
47945
48186
  const results = [];
47946
48187
  let testIdx = 0;
47947
48188
  const runNextTest = () => {
47948
- if (testIdx >= (resolved?.testCases.length ?? 0)) {
48189
+ if (testIdx >= testCases.length) {
47949
48190
  const allPassed = results.every((r2) => r2.passed);
47950
48191
  setPlayState(allPassed ? "success" : "fail");
47951
48192
  setTestResults(results);
@@ -47960,9 +48201,9 @@ function StateArchitectBoard({
47960
48201
  }
47961
48202
  return;
47962
48203
  }
47963
- const testCase = resolved?.testCases[testIdx];
48204
+ const testCase = testCases[testIdx];
47964
48205
  if (!testCase) return;
47965
- let state = resolved.initialState;
48206
+ let state = initialState;
47966
48207
  for (const event of testCase.events) {
47967
48208
  const trans = transitions.find((t2) => t2.from === state && t2.event === event);
47968
48209
  if (trans) {
@@ -47980,53 +48221,57 @@ function StateArchitectBoard({
47980
48221
  timerRef.current = setTimeout(runNextTest, stepDurationMs);
47981
48222
  };
47982
48223
  timerRef.current = setTimeout(runNextTest, stepDurationMs);
47983
- }, [playState, transitions, resolved, stepDurationMs, testEvent, completeEvent, emit]);
48224
+ }, [playState, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
47984
48225
  const handleTryAgain = React97.useCallback(() => {
47985
48226
  if (timerRef.current) clearTimeout(timerRef.current);
47986
48227
  setPlayState("editing");
47987
- setCurrentState(resolved?.initialState ?? "");
48228
+ setCurrentState(initialState);
47988
48229
  setTestResults([]);
47989
- }, [resolved?.initialState]);
48230
+ }, [initialState]);
47990
48231
  const handleReset = React97.useCallback(() => {
47991
48232
  if (timerRef.current) clearTimeout(timerRef.current);
47992
- setTransitions(resolved?.transitions ?? []);
48233
+ setTransitions(entityTransitions);
47993
48234
  setPlayState("editing");
47994
- setCurrentState(resolved?.initialState ?? "");
48235
+ setCurrentState(initialState);
47995
48236
  setTestResults([]);
47996
- setVariables(resolved?.variables ?? []);
48237
+ setVariables([...entityVariables]);
47997
48238
  setSelectedState(null);
47998
48239
  setAddingFrom(null);
47999
48240
  setAttempts(0);
48000
- }, [resolved]);
48241
+ }, [entityTransitions, initialState, entityVariables]);
48001
48242
  const codeData = React97.useMemo(() => ({
48002
- name: resolved?.entityName ?? "",
48003
- states: resolved?.states ?? [],
48004
- initialState: resolved?.initialState ?? "",
48243
+ name: entityName,
48244
+ states: entityStates,
48245
+ initialState,
48005
48246
  transitions: transitions.map((t2) => ({
48006
48247
  from: t2.from,
48007
48248
  to: t2.to,
48008
48249
  event: t2.event,
48009
48250
  ...t2.guardHint ? { guard: t2.guardHint } : {}
48010
48251
  }))
48011
- }), [resolved, transitions]);
48252
+ }), [entityName, entityStates, initialState, transitions]);
48012
48253
  if (!resolved) return null;
48254
+ const theme = resolved.theme ?? void 0;
48255
+ const themeBackground = theme?.background;
48256
+ const headerImage = str(resolved.headerImage);
48257
+ const hint = str(resolved.hint);
48013
48258
  return /* @__PURE__ */ jsxRuntime.jsxs(
48014
48259
  VStack,
48015
48260
  {
48016
48261
  className: cn("p-4 gap-6", className),
48017
48262
  style: {
48018
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
48263
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
48019
48264
  backgroundSize: "cover",
48020
48265
  backgroundPosition: "center"
48021
48266
  },
48022
48267
  children: [
48023
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
48268
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
48024
48269
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
48025
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
48026
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description }),
48270
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
48271
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) }),
48027
48272
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center p-2 rounded bg-warning/10 border border-warning/30", gap: "xs", children: [
48028
48273
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-warning font-bold", children: t("game.hint") + ":" }),
48029
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-foreground", children: resolved.hint })
48274
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-foreground", children: hint })
48030
48275
  ] })
48031
48276
  ] }),
48032
48277
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "flex-wrap items-start", gap: "lg", children: [
@@ -48074,14 +48319,14 @@ function StateArchitectBoard({
48074
48319
  ]
48075
48320
  }
48076
48321
  ),
48077
- resolved.states.map((state) => /* @__PURE__ */ jsxRuntime.jsx(
48322
+ entityStates.map((state) => /* @__PURE__ */ jsxRuntime.jsx(
48078
48323
  StateNode2,
48079
48324
  {
48080
48325
  name: state,
48081
48326
  position: positions[state],
48082
48327
  isCurrent: state === currentState,
48083
48328
  isSelected: state === selectedState,
48084
- isInitial: state === resolved.initialState,
48329
+ isInitial: state === initialState,
48085
48330
  onClick: () => handleStateClick(state)
48086
48331
  },
48087
48332
  state
@@ -48128,7 +48373,7 @@ function StateArchitectBoard({
48128
48373
  /* @__PURE__ */ jsxRuntime.jsx(
48129
48374
  VariablePanel,
48130
48375
  {
48131
- entityName: resolved.entityName,
48376
+ entityName,
48132
48377
  variables
48133
48378
  }
48134
48379
  ),
@@ -48143,12 +48388,12 @@ function StateArchitectBoard({
48143
48388
  resolved.showCodeView !== false && /* @__PURE__ */ jsxRuntime.jsx(CodeView, { data: codeData, label: "View Code" })
48144
48389
  ] })
48145
48390
  ] }),
48146
- playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: resolved.successMessage || t("stateArchitect.allPassed") }) }),
48391
+ playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("stateArchitect.allPassed") }) }),
48147
48392
  playState === "fail" && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
48148
48393
  /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(ENCOURAGEMENT_KEYS3[Math.min(attempts - 1, ENCOURAGEMENT_KEYS3.length - 1)] ?? ENCOURAGEMENT_KEYS3[0]) }) }),
48149
- attempts >= 3 && resolved.hint && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-start", gap: "xs", children: [
48394
+ attempts >= 3 && hint && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-start", gap: "xs", children: [
48150
48395
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
48151
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
48396
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
48152
48397
  ] }) })
48153
48398
  ] }),
48154
48399
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
@@ -48178,6 +48423,7 @@ var init_StateArchitectBoard = __esm({
48178
48423
  init_TransitionArrow();
48179
48424
  init_VariablePanel();
48180
48425
  init_CodeView();
48426
+ init_boardEntity();
48181
48427
  ENCOURAGEMENT_KEYS3 = [
48182
48428
  "puzzle.tryAgain1",
48183
48429
  "puzzle.tryAgain2",
@@ -48214,8 +48460,8 @@ var init_StatsOrganism = __esm({
48214
48460
  return /* @__PURE__ */ jsxRuntime.jsx(ErrorState, { message: error.message, className });
48215
48461
  }
48216
48462
  const stats = items.map((item) => ({
48217
- value: item.value,
48218
- label: item.label
48463
+ value: String(item.value ?? ""),
48464
+ label: String(item.label ?? "")
48219
48465
  }));
48220
48466
  return /* @__PURE__ */ jsxRuntime.jsx(
48221
48467
  StatsGrid,
@@ -48261,10 +48507,10 @@ var init_StepFlowOrganism = __esm({
48261
48507
  return /* @__PURE__ */ jsxRuntime.jsx(ErrorState, { message: error.message, className });
48262
48508
  }
48263
48509
  const steps = items.map((item) => ({
48264
- number: item.number,
48265
- title: item.title,
48266
- description: item.description,
48267
- icon: item.icon
48510
+ number: item.number != null ? Number(item.number) : void 0,
48511
+ title: String(item.title ?? ""),
48512
+ description: String(item.description ?? ""),
48513
+ icon: item.icon != null ? String(item.icon) : void 0
48268
48514
  }));
48269
48515
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
48270
48516
  (heading || subtitle) && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
@@ -48437,13 +48683,13 @@ var init_TeamOrganism = __esm({
48437
48683
  /* @__PURE__ */ jsxRuntime.jsx(SimpleGrid, { cols: cols > 0 ? cols : 1, gap: "lg", children: items.map((member) => /* @__PURE__ */ jsxRuntime.jsx(
48438
48684
  TeamCard,
48439
48685
  {
48440
- name: member.name,
48441
- nameAr: member.nameAr,
48442
- role: member.role,
48443
- bio: member.bio,
48444
- avatar: member.avatar
48686
+ name: String(member.name ?? ""),
48687
+ nameAr: member.nameAr != null ? String(member.nameAr) : void 0,
48688
+ role: String(member.role ?? ""),
48689
+ bio: String(member.bio ?? ""),
48690
+ avatar: member.avatar != null ? String(member.avatar) : void 0
48445
48691
  },
48446
- member.id
48692
+ String(member.id ?? "")
48447
48693
  )) })
48448
48694
  ] });
48449
48695
  };
@@ -48680,8 +48926,8 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
48680
48926
  const [turn, setTurn] = React97.useState(1);
48681
48927
  const [gameResult, setGameResult] = React97.useState(null);
48682
48928
  const checkGameEnd = React97.useCallback((currentUnits) => {
48683
- const pa = currentUnits.filter((u) => u.team === "player" && u.health > 0);
48684
- const ea = currentUnits.filter((u) => u.team === "enemy" && u.health > 0);
48929
+ const pa = currentUnits.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
48930
+ const ea = currentUnits.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
48685
48931
  if (pa.length === 0) {
48686
48932
  setGameResult("defeat");
48687
48933
  setPhase("game_over");
@@ -48699,34 +48945,36 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
48699
48945
  }
48700
48946
  }, [onGameEnd, gameEndEvent, eventBus]);
48701
48947
  const handleUnitClick = React97.useCallback((unitId) => {
48702
- const unit = units.find((u) => u.id === unitId);
48948
+ const unit = units.find((u) => str(u.id) === unitId);
48703
48949
  if (!unit) return;
48704
48950
  if (unitClickEvent) {
48705
48951
  eventBus.emit(`UI:${unitClickEvent}`, { unitId });
48706
48952
  }
48707
48953
  if (phase === "observation" || phase === "selection") {
48708
- if (unit.team === "player") {
48954
+ if (unitTeam(unit) === "player") {
48709
48955
  setSelectedUnitId(unitId);
48710
48956
  setPhase("movement");
48711
48957
  }
48712
48958
  } else if (phase === "action") {
48713
- const selectedUnit = units.find((u) => u.id === selectedUnitId);
48959
+ const selectedUnit = units.find((u) => str(u.id) === selectedUnitId);
48714
48960
  if (!selectedUnit) return;
48715
- if (unit.team === "enemy") {
48716
- const dx = Math.abs(unit.position.x - selectedUnit.position.x);
48717
- const dy = Math.abs(unit.position.y - selectedUnit.position.y);
48961
+ if (unitTeam(unit) === "enemy") {
48962
+ const up = unitPosition(unit);
48963
+ const sp = unitPosition(selectedUnit);
48964
+ const dx = Math.abs(up.x - sp.x);
48965
+ const dy = Math.abs(up.y - sp.y);
48718
48966
  if (dx <= 1 && dy <= 1 && dx + dy > 0) {
48719
- const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, selectedUnit.attack - unit.defense);
48720
- const newHealth = Math.max(0, unit.health - damage);
48967
+ const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, num(selectedUnit.attack) - num(unit.defense));
48968
+ const newHealth = Math.max(0, unitHealth(unit) - damage);
48721
48969
  const updatedUnits = units.map(
48722
- (u) => u.id === unit.id ? { ...u, health: newHealth } : u
48970
+ (u) => str(u.id) === str(unit.id) ? { ...u, health: newHealth } : u
48723
48971
  );
48724
48972
  setUnits(updatedUnits);
48725
48973
  onAttack?.(selectedUnit, unit, damage);
48726
48974
  if (attackEvent) {
48727
48975
  eventBus.emit(`UI:${attackEvent}`, {
48728
- attackerId: selectedUnit.id,
48729
- targetId: unit.id,
48976
+ attackerId: str(selectedUnit.id),
48977
+ targetId: str(unit.id),
48730
48978
  damage
48731
48979
  });
48732
48980
  }
@@ -48743,16 +48991,20 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
48743
48991
  eventBus.emit(`UI:${tileClickEvent}`, { x, y });
48744
48992
  }
48745
48993
  if (phase === "movement" && selectedUnitId) {
48746
- const selectedUnit = units.find((u) => u.id === selectedUnitId);
48994
+ const selectedUnit = units.find((u) => str(u.id) === selectedUnitId);
48747
48995
  if (!selectedUnit) return;
48748
- const dx = Math.abs(x - selectedUnit.position.x);
48749
- const dy = Math.abs(y - selectedUnit.position.y);
48996
+ const sp = unitPosition(selectedUnit);
48997
+ const dx = Math.abs(x - sp.x);
48998
+ const dy = Math.abs(y - sp.y);
48750
48999
  const dist = dx + dy;
48751
- if (dist > 0 && dist <= selectedUnit.movement) {
48752
- if (!units.some((u) => u.position.x === x && u.position.y === y && u.health > 0)) {
49000
+ if (dist > 0 && dist <= num(selectedUnit.movement)) {
49001
+ if (!units.some((u) => {
49002
+ const p2 = unitPosition(u);
49003
+ return p2.x === x && p2.y === y && unitHealth(u) > 0;
49004
+ })) {
48753
49005
  setUnits(
48754
49006
  (prev) => prev.map(
48755
- (u) => u.id === selectedUnitId ? { ...u, position: { x, y } } : u
49007
+ (u) => str(u.id) === selectedUnitId ? { ...u, position: { x, y } } : u
48756
49008
  )
48757
49009
  );
48758
49010
  setPhase("action");
@@ -48795,12 +49047,13 @@ var init_useBattleState = __esm({
48795
49047
  "components/game/organisms/hooks/useBattleState.ts"() {
48796
49048
  "use client";
48797
49049
  init_useEventBus();
49050
+ init_boardEntity();
48798
49051
  }
48799
49052
  });
48800
49053
  function UncontrolledBattleBoard({ entity, ...rest }) {
48801
- const resolved = Array.isArray(entity) ? entity[0] : entity;
49054
+ const resolved = boardEntity(entity);
48802
49055
  const battleState = useBattleState(
48803
- resolved?.initialUnits ?? [],
49056
+ rows(resolved?.initialUnits),
48804
49057
  {
48805
49058
  tileClickEvent: rest.tileClickEvent,
48806
49059
  unitClickEvent: rest.unitClickEvent,
@@ -48836,10 +49089,23 @@ function UncontrolledBattleBoard({ entity, ...rest }) {
48836
49089
  var init_UncontrolledBattleBoard = __esm({
48837
49090
  "components/game/organisms/UncontrolledBattleBoard.tsx"() {
48838
49091
  init_BattleBoard();
49092
+ init_boardEntity();
48839
49093
  init_useBattleState();
48840
49094
  UncontrolledBattleBoard.displayName = "UncontrolledBattleBoard";
48841
49095
  }
48842
49096
  });
49097
+ function heroPosition(h) {
49098
+ return vec2(h.position);
49099
+ }
49100
+ function heroOwner(h) {
49101
+ return str(h.owner);
49102
+ }
49103
+ function heroMovement(h) {
49104
+ return num(h.movement);
49105
+ }
49106
+ function hexPassable(h) {
49107
+ return h.passable !== false;
49108
+ }
48843
49109
  function defaultIsInRange(from, to, range) {
48844
49110
  return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
48845
49111
  }
@@ -48870,36 +49136,36 @@ function WorldMapBoard({
48870
49136
  className
48871
49137
  }) {
48872
49138
  const eventBus = useEventBus();
48873
- const resolved = Array.isArray(entity) ? entity[0] : entity;
48874
- const hexes = resolved?.hexes ?? [];
48875
- const heroes = resolved?.heroes ?? [];
48876
- const features = resolved?.features ?? [];
48877
- const selectedHeroId = resolved?.selectedHeroId;
49139
+ const resolved = boardEntity(entity);
49140
+ const hexes = rows(resolved?.hexes);
49141
+ const heroes = rows(resolved?.heroes);
49142
+ const features = Array.isArray(resolved?.features) ? resolved.features : [];
49143
+ const selectedHeroId = resolved?.selectedHeroId ?? null;
48878
49144
  const assetManifest = resolved?.assetManifest;
48879
49145
  const backgroundImage = resolved?.backgroundImage;
48880
49146
  const [hoveredTile, setHoveredTile] = React97.useState(null);
48881
49147
  const selectedHero = React97.useMemo(
48882
- () => heroes.find((h) => h.id === selectedHeroId) ?? null,
49148
+ () => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
48883
49149
  [heroes, selectedHeroId]
48884
49150
  );
48885
49151
  const tiles = React97.useMemo(
48886
49152
  () => hexes.map((hex) => ({
48887
- x: hex.x,
48888
- y: hex.y,
48889
- terrain: hex.terrain,
48890
- terrainSprite: hex.terrainSprite
49153
+ x: num(hex.x),
49154
+ y: num(hex.y),
49155
+ terrain: str(hex.terrain),
49156
+ terrainSprite: hex.terrainSprite == null ? void 0 : str(hex.terrainSprite)
48891
49157
  })),
48892
49158
  [hexes]
48893
49159
  );
48894
49160
  const baseUnits = React97.useMemo(
48895
49161
  () => heroes.map((hero) => ({
48896
- id: hero.id,
48897
- position: hero.position,
48898
- name: hero.name,
48899
- team: hero.owner === "enemy" ? "enemy" : "player",
49162
+ id: str(hero.id),
49163
+ position: heroPosition(hero),
49164
+ name: str(hero.name),
49165
+ team: heroOwner(hero) === "enemy" ? "enemy" : "player",
48900
49166
  health: 100,
48901
49167
  maxHealth: 100,
48902
- sprite: hero.sprite
49168
+ sprite: hero.sprite == null ? void 0 : str(hero.sprite)
48903
49169
  })),
48904
49170
  [heroes]
48905
49171
  );
@@ -48940,73 +49206,94 @@ function WorldMapBoard({
48940
49206
  const isoUnits = React97.useMemo(() => {
48941
49207
  if (movingPositions.size === 0) return baseUnits;
48942
49208
  return baseUnits.map((u) => {
48943
- const pos = movingPositions.get(u.id);
49209
+ const pos = u.id == null ? void 0 : movingPositions.get(u.id);
48944
49210
  return pos ? { ...u, position: pos } : u;
48945
49211
  });
48946
49212
  }, [baseUnits, movingPositions]);
48947
49213
  const validMoves = React97.useMemo(() => {
48948
- if (!selectedHero || selectedHero.movement <= 0) return [];
49214
+ if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
49215
+ const sp = heroPosition(selectedHero);
49216
+ const sOwner = heroOwner(selectedHero);
49217
+ const range = heroMovement(selectedHero);
48949
49218
  const moves = [];
48950
49219
  hexes.forEach((hex) => {
48951
- if (hex.passable === false) return;
48952
- if (hex.x === selectedHero.position.x && hex.y === selectedHero.position.y) return;
48953
- if (!isInRange(selectedHero.position, { x: hex.x, y: hex.y }, selectedHero.movement)) return;
48954
- if (heroes.some((h) => h.position.x === hex.x && h.position.y === hex.y && h.owner === selectedHero.owner)) return;
48955
- moves.push({ x: hex.x, y: hex.y });
49220
+ const hx = num(hex.x);
49221
+ const hy = num(hex.y);
49222
+ if (!hexPassable(hex)) return;
49223
+ if (hx === sp.x && hy === sp.y) return;
49224
+ if (!isInRange(sp, { x: hx, y: hy }, range)) return;
49225
+ if (heroes.some((h) => {
49226
+ const hp = heroPosition(h);
49227
+ return hp.x === hx && hp.y === hy && heroOwner(h) === sOwner;
49228
+ })) return;
49229
+ moves.push({ x: hx, y: hy });
48956
49230
  });
48957
49231
  return moves;
48958
49232
  }, [selectedHero, hexes, heroes, isInRange]);
48959
49233
  const attackTargets = React97.useMemo(() => {
48960
- if (!selectedHero || selectedHero.movement <= 0) return [];
48961
- return heroes.filter((h) => h.owner !== selectedHero.owner).filter((h) => isInRange(selectedHero.position, h.position, selectedHero.movement)).map((h) => h.position);
49234
+ if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
49235
+ const sp = heroPosition(selectedHero);
49236
+ const sOwner = heroOwner(selectedHero);
49237
+ const range = heroMovement(selectedHero);
49238
+ return heroes.filter((h) => heroOwner(h) !== sOwner).filter((h) => isInRange(sp, heroPosition(h), range)).map((h) => heroPosition(h));
48962
49239
  }, [selectedHero, heroes, isInRange]);
48963
- const maxY = Math.max(...hexes.map((h) => h.y), 0);
49240
+ const maxY = Math.max(...hexes.map((h) => num(h.y)), 0);
48964
49241
  const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
48965
49242
  const tileToScreen = React97.useCallback(
48966
49243
  (tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
48967
49244
  [scale, baseOffsetX]
48968
49245
  );
48969
49246
  const hoveredHex = React97.useMemo(
48970
- () => hoveredTile ? hexes.find((h) => h.x === hoveredTile.x && h.y === hoveredTile.y) ?? null : null,
49247
+ () => hoveredTile ? hexes.find((h) => num(h.x) === hoveredTile.x && num(h.y) === hoveredTile.y) ?? null : null,
48971
49248
  [hoveredTile, hexes]
48972
49249
  );
48973
49250
  const hoveredHero = React97.useMemo(
48974
- () => hoveredTile ? heroes.find((h) => h.position.x === hoveredTile.x && h.position.y === hoveredTile.y) ?? null : null,
49251
+ () => hoveredTile ? heroes.find((h) => {
49252
+ const hp = heroPosition(h);
49253
+ return hp.x === hoveredTile.x && hp.y === hoveredTile.y;
49254
+ }) ?? null : null,
48975
49255
  [hoveredTile, heroes]
48976
49256
  );
48977
49257
  const handleTileClick = React97.useCallback((x, y) => {
48978
49258
  if (movementAnimRef.current) return;
48979
- const hex = hexes.find((h) => h.x === x && h.y === y);
49259
+ const hex = hexes.find((h) => num(h.x) === x && num(h.y) === y);
48980
49260
  if (!hex) return;
48981
49261
  if (tileClickEvent) {
48982
49262
  eventBus.emit(`UI:${tileClickEvent}`, { x, y });
48983
49263
  }
48984
49264
  if (selectedHero && validMoves.some((m) => m.x === x && m.y === y)) {
48985
- startMoveAnimation(selectedHero.id, { ...selectedHero.position }, { x, y }, () => {
48986
- onHeroMove?.(selectedHero.id, x, y);
49265
+ const heroId = str(selectedHero.id);
49266
+ startMoveAnimation(heroId, { ...heroPosition(selectedHero) }, { x, y }, () => {
49267
+ onHeroMove?.(heroId, x, y);
48987
49268
  if (heroMoveEvent) {
48988
- eventBus.emit(`UI:${heroMoveEvent}`, { heroId: selectedHero.id, toX: x, toY: y });
49269
+ eventBus.emit(`UI:${heroMoveEvent}`, { heroId, toX: x, toY: y });
48989
49270
  }
48990
- if (hex.feature && hex.feature !== "none") {
48991
- onFeatureEnter?.(selectedHero.id, hex);
49271
+ const feature = str(hex.feature);
49272
+ if (feature && feature !== "none") {
49273
+ onFeatureEnter?.(heroId, hex);
48992
49274
  if (featureEnterEvent) {
48993
- eventBus.emit(`UI:${featureEnterEvent}`, { heroId: selectedHero.id, feature: hex.feature, hex });
49275
+ eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex });
48994
49276
  }
48995
49277
  }
48996
49278
  });
48997
49279
  return;
48998
49280
  }
48999
- const enemy = heroes.find((h) => h.position.x === x && h.position.y === y && h.owner === "enemy");
49281
+ const enemy = heroes.find((h) => {
49282
+ const hp = heroPosition(h);
49283
+ return hp.x === x && hp.y === y && heroOwner(h) === "enemy";
49284
+ });
49000
49285
  if (selectedHero && enemy && attackTargets.some((t) => t.x === x && t.y === y)) {
49001
- onBattleEncounter?.(selectedHero.id, enemy.id);
49286
+ const attackerId = str(selectedHero.id);
49287
+ const defenderId = str(enemy.id);
49288
+ onBattleEncounter?.(attackerId, defenderId);
49002
49289
  if (battleEncounterEvent) {
49003
- eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId: selectedHero.id, defenderId: enemy.id });
49290
+ eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId, defenderId });
49004
49291
  }
49005
49292
  }
49006
49293
  }, [hexes, heroes, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
49007
49294
  const handleUnitClick = React97.useCallback((unitId) => {
49008
- const hero = heroes.find((h) => h.id === unitId);
49009
- if (hero && (hero.owner === "player" || allowMoveAllHeroes)) {
49295
+ const hero = heroes.find((h) => str(h.id) === unitId);
49296
+ if (hero && (heroOwner(hero) === "player" || allowMoveAllHeroes)) {
49010
49297
  onHeroSelect?.(unitId);
49011
49298
  if (heroSelectEvent) {
49012
49299
  eventBus.emit(`UI:${heroSelectEvent}`, { heroId: unitId });
@@ -49079,6 +49366,7 @@ var init_WorldMapBoard = __esm({
49079
49366
  init_Stack();
49080
49367
  init_LoadingState();
49081
49368
  init_IsometricCanvas2();
49369
+ init_boardEntity();
49082
49370
  init_isometric();
49083
49371
  WorldMapBoard.displayName = "WorldMapBoard";
49084
49372
  }
@@ -52642,10 +52930,10 @@ function parseApplicationLevel(schema) {
52642
52930
  }
52643
52931
  const count = schema.orbitals.length;
52644
52932
  const cols = Math.ceil(Math.sqrt(count));
52645
- const rows = Math.ceil(count / cols);
52933
+ const rows2 = Math.ceil(count / cols);
52646
52934
  const spacing = 200;
52647
52935
  const gridW = cols * spacing;
52648
- const gridH = rows * spacing;
52936
+ const gridH = rows2 * spacing;
52649
52937
  const originX = (600 - gridW) / 2 + spacing / 2;
52650
52938
  const originY = (400 - gridH) / 2 + spacing / 2;
52651
52939
  schema.orbitals.forEach((orbital, i) => {
@@ -55787,11 +56075,11 @@ function buildMockData(schema) {
55787
56075
  result[entityName] = entity.instances;
55788
56076
  continue;
55789
56077
  }
55790
- const rows = Array.from(
56078
+ const rows2 = Array.from(
55791
56079
  { length: 10 },
55792
56080
  (_, i) => generateEntityRow(entity, i + 1)
55793
56081
  );
55794
- result[entityName] = rows;
56082
+ result[entityName] = rows2;
55795
56083
  }
55796
56084
  for (const orbital of schema.orbitals) {
55797
56085
  for (const traitRef of orbital.traits ?? []) {
@@ -59290,18 +59578,18 @@ function layoutOrbitals(count, containerW, containerH) {
59290
59578
  if (count === 0) return [];
59291
59579
  if (count === 1) return [{ cx: containerW / 2, cy: containerH / 2 }];
59292
59580
  const cols = Math.min(count, Math.ceil(Math.sqrt(count)));
59293
- const rows = Math.ceil(count / cols);
59581
+ const rows2 = Math.ceil(count / cols);
59294
59582
  const edgePad = 24;
59295
59583
  const fitMinCx = UNIT_DISPLAY_W / 2 + edgePad;
59296
59584
  const fitMinCy = UNIT_DISPLAY_H / 2 + edgePad;
59297
59585
  const fitMaxCx = Math.max(fitMinCx, containerW - UNIT_DISPLAY_W / 2 - edgePad);
59298
59586
  const fitMaxCy = Math.max(fitMinCy, containerH - UNIT_DISPLAY_H / 2 - edgePad);
59299
59587
  const fitStepX = cols > 1 ? (fitMaxCx - fitMinCx) / (cols - 1) : 0;
59300
- const fitStepY = rows > 1 ? (fitMaxCy - fitMinCy) / (rows - 1) : 0;
59588
+ const fitStepY = rows2 > 1 ? (fitMaxCy - fitMinCy) / (rows2 - 1) : 0;
59301
59589
  const stepX = Math.min(fitStepX, UNIT_DISPLAY_W * 3.5);
59302
59590
  const stepY = Math.min(fitStepY, UNIT_DISPLAY_H * 3.5);
59303
59591
  const gridW = (cols - 1) * stepX;
59304
- const gridH = (rows - 1) * stepY;
59592
+ const gridH = (rows2 - 1) * stepY;
59305
59593
  const originX = (containerW - gridW) / 2;
59306
59594
  const originY = (containerH - gridH) / 2;
59307
59595
  return Array.from({ length: count }, (_, i) => ({