@almadar/ui 5.21.11 → 5.22.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/dist/avl/index.cjs +939 -638
  2. package/dist/avl/index.js +939 -638
  3. package/dist/components/core/molecules/CalendarGrid.d.ts +3 -10
  4. package/dist/components/core/molecules/ContentRenderer.d.ts +2 -2
  5. package/dist/components/core/molecules/DataGrid.d.ts +11 -20
  6. package/dist/components/core/molecules/DataList.d.ts +9 -15
  7. package/dist/components/core/molecules/FormSection.d.ts +4 -4
  8. package/dist/components/core/molecules/PositionedCanvas.d.ts +4 -17
  9. package/dist/components/core/molecules/ReplyTree.d.ts +2 -13
  10. package/dist/components/core/molecules/RichBlockEditor.d.ts +3 -6
  11. package/dist/components/core/molecules/SortableList.d.ts +7 -5
  12. package/dist/components/core/molecules/TableView.d.ts +7 -7
  13. package/dist/components/core/molecules/index.d.ts +3 -3
  14. package/dist/components/core/molecules/useDataDnd.d.ts +5 -5
  15. package/dist/components/core/organisms/CardGrid.d.ts +5 -2
  16. package/dist/components/core/organisms/CaseStudyOrganism.d.ts +4 -3
  17. package/dist/components/core/organisms/DataTable.d.ts +4 -2
  18. package/dist/components/core/organisms/DetailPanel.d.ts +6 -6
  19. package/dist/components/core/organisms/FeatureGridOrganism.d.ts +4 -3
  20. package/dist/components/core/organisms/HeroOrganism.d.ts +4 -5
  21. package/dist/components/core/organisms/List.d.ts +5 -2
  22. package/dist/components/core/organisms/MasterDetail.d.ts +4 -2
  23. package/dist/components/core/organisms/MediaGallery.d.ts +4 -2
  24. package/dist/components/core/organisms/ShowcaseOrganism.d.ts +4 -3
  25. package/dist/components/core/organisms/StatCard.d.ts +5 -2
  26. package/dist/components/core/organisms/StepFlowOrganism.d.ts +4 -3
  27. package/dist/components/core/organisms/Timeline.d.ts +2 -2
  28. package/dist/components/core/organisms/book/index.d.ts +1 -1
  29. package/dist/components/core/organisms/book/types.d.ts +28 -48
  30. package/dist/components/core/organisms/index.d.ts +1 -2
  31. package/dist/components/core/organisms/layout/DashboardGrid.d.ts +2 -2
  32. package/dist/components/core/organisms/marketing-types.d.ts +5 -94
  33. package/dist/components/core/organisms/types.d.ts +9 -27
  34. package/dist/components/core/templates/index.d.ts +6 -6
  35. package/dist/components/game/organisms/BattleBoard.d.ts +14 -90
  36. package/dist/components/game/organisms/CastleBoard.d.ts +7 -21
  37. package/dist/components/game/organisms/UncontrolledBattleBoard.d.ts +2 -7
  38. package/dist/components/game/organisms/WorldMapBoard.d.ts +13 -59
  39. package/dist/components/game/organisms/boardEntity.d.ts +44 -0
  40. package/dist/components/game/organisms/hooks/useBattleState.d.ts +7 -7
  41. package/dist/components/game/organisms/index.d.ts +3 -3
  42. package/dist/components/game/organisms/puzzles/builder/BuilderBoard.d.ts +7 -20
  43. package/dist/components/game/organisms/puzzles/builder/index.d.ts +1 -1
  44. package/dist/components/game/organisms/puzzles/classifier/ClassifierBoard.d.ts +7 -20
  45. package/dist/components/game/organisms/puzzles/classifier/index.d.ts +1 -1
  46. package/dist/components/game/organisms/puzzles/debugger/DebuggerBoard.d.ts +6 -22
  47. package/dist/components/game/organisms/puzzles/debugger/index.d.ts +1 -1
  48. package/dist/components/game/organisms/puzzles/event-handler/EventHandlerBoard.d.ts +6 -33
  49. package/dist/components/game/organisms/puzzles/event-handler/ObjectRulePanel.d.ts +3 -21
  50. package/dist/components/game/organisms/puzzles/event-handler/index.d.ts +2 -2
  51. package/dist/components/game/organisms/puzzles/event-handler/puzzleObject.d.ts +21 -0
  52. package/dist/components/game/organisms/puzzles/negotiator/NegotiatorBoard.d.ts +8 -24
  53. package/dist/components/game/organisms/puzzles/negotiator/index.d.ts +1 -1
  54. package/dist/components/game/organisms/puzzles/sequencer/ActionTile.d.ts +2 -2
  55. package/dist/components/game/organisms/puzzles/sequencer/SequencerBoard.d.ts +7 -36
  56. package/dist/components/game/organisms/puzzles/sequencer/index.d.ts +1 -1
  57. package/dist/components/game/organisms/puzzles/simulator/SimulatorBoard.d.ts +6 -25
  58. package/dist/components/game/organisms/puzzles/simulator/index.d.ts +1 -1
  59. package/dist/components/game/organisms/puzzles/state-architect/StateArchitectBoard.d.ts +7 -40
  60. package/dist/components/game/organisms/puzzles/state-architect/VariablePanel.d.ts +3 -9
  61. package/dist/components/game/organisms/puzzles/state-architect/index.d.ts +2 -2
  62. package/dist/components/game/organisms/three/index.cjs +35 -21
  63. package/dist/components/game/organisms/three/index.js +35 -21
  64. package/dist/components/game/templates/BattleTemplate.d.ts +2 -3
  65. package/dist/components/game/templates/CastleTemplate.d.ts +2 -3
  66. package/dist/components/game/templates/GameCanvas3DBattleTemplate.d.ts +1 -16
  67. package/dist/components/game/templates/GameCanvas3DCastleTemplate.d.ts +1 -18
  68. package/dist/components/game/templates/GameCanvas3DWorldMapTemplate.d.ts +1 -14
  69. package/dist/components/game/templates/GameTemplate.d.ts +1 -6
  70. package/dist/components/game/templates/WorldMapTemplate.d.ts +2 -3
  71. package/dist/components/index.cjs +2036 -1675
  72. package/dist/components/index.js +1148 -787
  73. package/dist/components/marketing/organisms/PricingOrganism.d.ts +4 -3
  74. package/dist/components/marketing/organisms/StatsOrganism.d.ts +4 -3
  75. package/dist/components/marketing/organisms/TeamOrganism.d.ts +4 -3
  76. package/dist/components/marketing/organisms/book/BookChapterView.d.ts +5 -2
  77. package/dist/components/marketing/organisms/book/BookTableOfContents.d.ts +3 -2
  78. package/dist/components/marketing/organisms/book/BookViewer.d.ts +4 -4
  79. package/dist/components/marketing/templates/AboutPageTemplate.d.ts +32 -6
  80. package/dist/components/marketing/templates/FeatureDetailPageTemplate.d.ts +14 -4
  81. package/dist/components/marketing/templates/LandingPageTemplate.d.ts +47 -9
  82. package/dist/components/marketing/templates/PricingPageTemplate.d.ts +23 -5
  83. package/dist/providers/index.cjs +932 -631
  84. package/dist/providers/index.js +932 -631
  85. package/dist/runtime/index.cjs +934 -633
  86. package/dist/runtime/index.js +934 -633
  87. package/package.json +2 -2
@@ -2454,7 +2454,7 @@ var init_SvgGrid = __esm({
2454
2454
  x,
2455
2455
  y,
2456
2456
  cols = 4,
2457
- rows = 3,
2457
+ rows: rows2 = 3,
2458
2458
  spacing = 20,
2459
2459
  nodeRadius = 3,
2460
2460
  color = "var(--color-primary)",
@@ -2463,7 +2463,7 @@ var init_SvgGrid = __esm({
2463
2463
  highlights = []
2464
2464
  }) => {
2465
2465
  const highlightSet = new Set(highlights);
2466
- return /* @__PURE__ */ jsx("g", { className, opacity, children: Array.from({ length: rows }).map(
2466
+ return /* @__PURE__ */ jsx("g", { className, opacity, children: Array.from({ length: rows2 }).map(
2467
2467
  (_, row) => Array.from({ length: cols }).map((_2, col) => {
2468
2468
  const index = row * cols + col;
2469
2469
  const isHighlighted = highlightSet.has(index);
@@ -3123,7 +3123,7 @@ var init_Input = __esm({
3123
3123
  onClear,
3124
3124
  value,
3125
3125
  options,
3126
- rows = 3,
3126
+ rows: rows2 = 3,
3127
3127
  onChange,
3128
3128
  ...props
3129
3129
  }, ref) => {
@@ -3173,7 +3173,7 @@ var init_Input = __esm({
3173
3173
  ref,
3174
3174
  value,
3175
3175
  onChange,
3176
- rows,
3176
+ rows: rows2,
3177
3177
  className: baseClassName,
3178
3178
  ...props
3179
3179
  }
@@ -5429,66 +5429,6 @@ var init_RangeSlider = __esm({
5429
5429
  RangeSlider.displayName = "RangeSlider";
5430
5430
  }
5431
5431
  });
5432
- function easeOut(t) {
5433
- return t * (2 - t);
5434
- }
5435
- var AnimatedCounter;
5436
- var init_AnimatedCounter = __esm({
5437
- "components/marketing/atoms/AnimatedCounter.tsx"() {
5438
- "use client";
5439
- init_cn();
5440
- init_Typography();
5441
- AnimatedCounter = ({
5442
- value: rawValue,
5443
- duration = 600,
5444
- prefix,
5445
- suffix,
5446
- className
5447
- }) => {
5448
- const numericRaw = typeof rawValue === "number" ? rawValue : Number.parseFloat(String(rawValue ?? ""));
5449
- const value = !Number.isNaN(numericRaw) ? numericRaw : 0;
5450
- const [displayValue, setDisplayValue] = useState(value);
5451
- const previousValueRef = useRef(value);
5452
- const animationFrameRef = useRef(null);
5453
- useEffect(() => {
5454
- const from = previousValueRef.current;
5455
- const to = value;
5456
- previousValueRef.current = value;
5457
- if (from === to) {
5458
- setDisplayValue(to);
5459
- return;
5460
- }
5461
- const startTime = performance.now();
5462
- const diff = to - from;
5463
- function animate(currentTime) {
5464
- const elapsed = currentTime - startTime;
5465
- const progress = Math.min(elapsed / duration, 1);
5466
- const easedProgress = easeOut(progress);
5467
- setDisplayValue(from + diff * easedProgress);
5468
- if (progress < 1) {
5469
- animationFrameRef.current = requestAnimationFrame(animate);
5470
- } else {
5471
- setDisplayValue(to);
5472
- }
5473
- }
5474
- animationFrameRef.current = requestAnimationFrame(animate);
5475
- return () => {
5476
- if (animationFrameRef.current !== null) {
5477
- cancelAnimationFrame(animationFrameRef.current);
5478
- }
5479
- };
5480
- }, [value, duration]);
5481
- const decimalPlaces = Number.isInteger(value) ? 0 : String(value).split(".")[1]?.length ?? 0;
5482
- const formattedValue = displayValue.toFixed(decimalPlaces);
5483
- return /* @__PURE__ */ jsxs(Typography, { variant: "h3", className: cn("tabular-nums", className), children: [
5484
- prefix,
5485
- formattedValue,
5486
- suffix
5487
- ] });
5488
- };
5489
- AnimatedCounter.displayName = "AnimatedCounter";
5490
- }
5491
- });
5492
5432
  function useInfiniteScroll(onLoadMore, options = {}) {
5493
5433
  const { rootMargin = "200px", hasMore = true, isLoading = false } = options;
5494
5434
  const observerRef = useRef(null);
@@ -7970,15 +7910,15 @@ function HeaderSkeleton({ className }) {
7970
7910
  ] })
7971
7911
  ] });
7972
7912
  }
7973
- function TableSkeleton({ rows = 5, columns = 4, className }) {
7913
+ function TableSkeleton({ rows: rows2 = 5, columns = 4, className }) {
7974
7914
  return /* @__PURE__ */ jsxs(VStack, { gap: "none", className: cn("border border-border rounded-lg overflow-hidden", className), children: [
7975
7915
  /* @__PURE__ */ jsx(HStack, { className: "px-4 py-3 bg-muted/30 border-b border-border", children: Array.from({ length: columns }).map((_, i) => /* @__PURE__ */ jsx(SkeletonBlock, { className: "h-4 flex-1 mx-2" }, i)) }),
7976
- Array.from({ length: rows }).map((_, rowIdx) => /* @__PURE__ */ jsx(
7916
+ Array.from({ length: rows2 }).map((_, rowIdx) => /* @__PURE__ */ jsx(
7977
7917
  HStack,
7978
7918
  {
7979
7919
  className: cn(
7980
7920
  "px-4 py-3",
7981
- rowIdx < rows - 1 && "border-b border-border"
7921
+ rowIdx < rows2 - 1 && "border-b border-border"
7982
7922
  ),
7983
7923
  children: Array.from({ length: columns }).map((_2, colIdx) => /* @__PURE__ */ jsx(SkeletonLine, { className: "flex-1 mx-2" }, colIdx))
7984
7924
  },
@@ -8026,18 +7966,18 @@ function CardSkeleton({ className }) {
8026
7966
  }
8027
7967
  );
8028
7968
  }
8029
- function TextSkeleton({ rows = 3, className }) {
8030
- return /* @__PURE__ */ jsx(VStack, { gap: "sm", className, children: Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsx(
7969
+ function TextSkeleton({ rows: rows2 = 3, className }) {
7970
+ return /* @__PURE__ */ jsx(VStack, { gap: "sm", className, children: Array.from({ length: rows2 }).map((_, i) => /* @__PURE__ */ jsx(
8031
7971
  SkeletonLine,
8032
7972
  {
8033
- className: i === rows - 1 ? "w-2/3" : "w-full"
7973
+ className: i === rows2 - 1 ? "w-2/3" : "w-full"
8034
7974
  },
8035
7975
  i
8036
7976
  )) });
8037
7977
  }
8038
7978
  function Skeleton({
8039
7979
  variant = "text",
8040
- rows,
7980
+ rows: rows2,
8041
7981
  columns,
8042
7982
  fields,
8043
7983
  className
@@ -8047,15 +7987,15 @@ function Skeleton({
8047
7987
  case "header":
8048
7988
  return /* @__PURE__ */ jsx(HeaderSkeleton, { className });
8049
7989
  case "table":
8050
- return /* @__PURE__ */ jsx(TableSkeleton, { rows, columns, className });
7990
+ return /* @__PURE__ */ jsx(TableSkeleton, { rows: rows2, columns, className });
8051
7991
  case "form":
8052
7992
  return /* @__PURE__ */ jsx(FormSkeleton, { fields, className });
8053
7993
  case "card":
8054
7994
  return /* @__PURE__ */ jsx(CardSkeleton, { className });
8055
7995
  case "text":
8056
- return /* @__PURE__ */ jsx(TextSkeleton, { rows, className });
7996
+ return /* @__PURE__ */ jsx(TextSkeleton, { rows: rows2, className });
8057
7997
  default:
8058
- return /* @__PURE__ */ jsx(TextSkeleton, { rows, className });
7998
+ return /* @__PURE__ */ jsx(TextSkeleton, { rows: rows2, className });
8059
7999
  }
8060
8000
  }
8061
8001
  var pulseClass;
@@ -9209,7 +9149,7 @@ var init_Menu = __esm({
9209
9149
  className
9210
9150
  }) => {
9211
9151
  const eventBus = useEventBus();
9212
- const { t } = useTranslate();
9152
+ const { t, direction } = useTranslate();
9213
9153
  const [isOpen, setIsOpen] = useState(false);
9214
9154
  const [activeSubMenu, setActiveSubMenu] = useState(null);
9215
9155
  const [triggerRect, setTriggerRect] = useState(null);
@@ -9263,6 +9203,18 @@ var init_Menu = __esm({
9263
9203
  "bottom-start": "top-full left-0 mt-2",
9264
9204
  "bottom-end": "top-full right-0 mt-2"
9265
9205
  };
9206
+ const rtlMirror = {
9207
+ "top-left": "top-right",
9208
+ "top-right": "top-left",
9209
+ "bottom-left": "bottom-right",
9210
+ "bottom-right": "bottom-left",
9211
+ "top-start": "top-end",
9212
+ "top-end": "top-start",
9213
+ "bottom-start": "bottom-end",
9214
+ "bottom-end": "bottom-start"
9215
+ };
9216
+ const effectivePosition = direction === "rtl" ? rtlMirror[position] ?? position : position;
9217
+ const subMenuSideClass = direction === "rtl" ? "right-full mr-2" : "left-full ml-2";
9266
9218
  const triggerChild = React84__default.isValidElement(trigger) ? trigger : /* @__PURE__ */ jsx(Typography, { variant: "small", as: "span", children: trigger });
9267
9219
  const triggerElement = React84__default.cloneElement(
9268
9220
  triggerChild,
@@ -9290,7 +9242,7 @@ var init_Menu = __esm({
9290
9242
  onMouseEnter: () => hasSubMenu && setActiveSubMenu(itemId),
9291
9243
  "data-testid": item.event ? `action-${item.event}` : void 0,
9292
9244
  className: cn(
9293
- "w-full flex items-center justify-between gap-3 px-4 py-2 text-left",
9245
+ "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
9294
9246
  "text-sm transition-colors",
9295
9247
  "hover:bg-muted",
9296
9248
  "focus:outline-none focus:bg-muted",
@@ -9309,7 +9261,7 @@ var init_Menu = __esm({
9309
9261
  }
9310
9262
  ),
9311
9263
  item.badge !== void 0 && /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
9312
- hasSubMenu && /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: "sm", className: "flex-shrink-0" })
9264
+ hasSubMenu && /* @__PURE__ */ jsx(Icon, { name: direction === "rtl" ? "chevron-left" : "chevron-right", size: "sm", className: "flex-shrink-0" })
9313
9265
  ] })
9314
9266
  },
9315
9267
  itemId
@@ -9329,7 +9281,8 @@ var init_Menu = __esm({
9329
9281
  Box,
9330
9282
  {
9331
9283
  className: cn(
9332
- "absolute left-full top-0 ml-2 z-50",
9284
+ "absolute top-0 z-50",
9285
+ subMenuSideClass,
9333
9286
  menuContainerStyles
9334
9287
  ),
9335
9288
  children: renderMenuItems(item.subMenu)
@@ -9347,12 +9300,12 @@ var init_Menu = __esm({
9347
9300
  className: cn(
9348
9301
  "absolute z-50",
9349
9302
  menuContainerStyles,
9350
- positionClasses3[position],
9303
+ positionClasses3[effectivePosition],
9351
9304
  className
9352
9305
  ),
9353
9306
  style: {
9354
- left: position.includes("left") ? 0 : "auto",
9355
- right: position.includes("right") ? 0 : "auto"
9307
+ left: effectivePosition.includes("left") ? 0 : "auto",
9308
+ right: effectivePosition.includes("right") ? 0 : "auto"
9356
9309
  },
9357
9310
  role: "menu",
9358
9311
  children: renderMenuItems(items)
@@ -9670,7 +9623,7 @@ var init_MapView = __esm({
9670
9623
  shadowSize: [41, 41]
9671
9624
  });
9672
9625
  L.Marker.prototype.options.icon = defaultIcon;
9673
- const { useEffect: useEffect71, useRef: useRef66, useCallback: useCallback113, useState: useState103 } = React84__default;
9626
+ const { useEffect: useEffect71, useRef: useRef66, useCallback: useCallback114, useState: useState103 } = React84__default;
9674
9627
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
9675
9628
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
9676
9629
  function MapUpdater({ centerLat, centerLng, zoom }) {
@@ -9716,7 +9669,7 @@ var init_MapView = __esm({
9716
9669
  }) {
9717
9670
  const eventBus = useEventBus3();
9718
9671
  const [clickedPosition, setClickedPosition] = useState103(null);
9719
- const handleMapClick = useCallback113((lat, lng) => {
9672
+ const handleMapClick = useCallback114((lat, lng) => {
9720
9673
  if (showClickedPin) {
9721
9674
  setClickedPosition({ lat, lng });
9722
9675
  }
@@ -9725,7 +9678,7 @@ var init_MapView = __esm({
9725
9678
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
9726
9679
  }
9727
9680
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
9728
- const handleMarkerClick = useCallback113((marker) => {
9681
+ const handleMarkerClick = useCallback114((marker) => {
9729
9682
  onMarkerClick?.(marker);
9730
9683
  if (markerClickEvent) {
9731
9684
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -9946,7 +9899,7 @@ function InputPattern({
9946
9899
  function TextareaPattern({
9947
9900
  value = "",
9948
9901
  placeholder,
9949
- rows = 4,
9902
+ rows: rows2 = 4,
9950
9903
  disabled = false,
9951
9904
  fieldError,
9952
9905
  onChange,
@@ -9966,7 +9919,7 @@ function TextareaPattern({
9966
9919
  {
9967
9920
  value: localValue,
9968
9921
  placeholder,
9969
- rows,
9922
+ rows: rows2,
9970
9923
  disabled,
9971
9924
  error: fieldError,
9972
9925
  onChange: handleChange,
@@ -10451,6 +10404,91 @@ var init_ActionPalette = __esm({
10451
10404
  ActionPalette.displayName = "ActionPalette";
10452
10405
  }
10453
10406
  });
10407
+ function parseValue(value) {
10408
+ if (value === "" || value == null) return { num: 0, prefix: "", suffix: "", decimals: 0 };
10409
+ const match = String(value).match(/^([^0-9]*)([0-9]+(?:\.[0-9]+)?)(.*)$/);
10410
+ if (!match) {
10411
+ return { num: 0, prefix: "", suffix: String(value), decimals: 0 };
10412
+ }
10413
+ const numStr = match[2];
10414
+ const decimalIdx = numStr.indexOf(".");
10415
+ const decimals = decimalIdx >= 0 ? numStr.length - decimalIdx - 1 : 0;
10416
+ return {
10417
+ prefix: match[1],
10418
+ num: parseFloat(numStr),
10419
+ suffix: match[3],
10420
+ decimals
10421
+ };
10422
+ }
10423
+ var AnimatedCounter;
10424
+ var init_AnimatedCounter = __esm({
10425
+ "components/core/molecules/AnimatedCounter.tsx"() {
10426
+ "use client";
10427
+ init_cn();
10428
+ init_Box();
10429
+ init_Typography();
10430
+ AnimatedCounter = ({
10431
+ value,
10432
+ label,
10433
+ duration = 1500,
10434
+ className
10435
+ }) => {
10436
+ const ref = useRef(null);
10437
+ const [displayValue, setDisplayValue] = useState("0");
10438
+ const [hasAnimated, setHasAnimated] = useState(false);
10439
+ const animate = useCallback(() => {
10440
+ const { num: num2, prefix, suffix, decimals } = parseValue(value);
10441
+ if (num2 === 0) {
10442
+ setDisplayValue(String(value));
10443
+ return;
10444
+ }
10445
+ const startTime = performance.now();
10446
+ const tick = (now2) => {
10447
+ const elapsed = now2 - startTime;
10448
+ const progress = Math.min(elapsed / duration, 1);
10449
+ const eased = 1 - Math.pow(1 - progress, 3);
10450
+ const current = eased * num2;
10451
+ setDisplayValue(`${prefix}${current.toFixed(decimals)}${suffix}`);
10452
+ if (progress < 1) {
10453
+ requestAnimationFrame(tick);
10454
+ } else {
10455
+ setDisplayValue(String(value));
10456
+ }
10457
+ };
10458
+ requestAnimationFrame(tick);
10459
+ }, [value, duration]);
10460
+ useEffect(() => {
10461
+ if (hasAnimated) return;
10462
+ const el = ref.current;
10463
+ if (!el) return;
10464
+ const observer2 = new IntersectionObserver(
10465
+ (entries) => {
10466
+ if (entries[0].isIntersecting) {
10467
+ setHasAnimated(true);
10468
+ animate();
10469
+ observer2.disconnect();
10470
+ }
10471
+ },
10472
+ { threshold: 0.3 }
10473
+ );
10474
+ observer2.observe(el);
10475
+ return () => observer2.disconnect();
10476
+ }, [hasAnimated, animate]);
10477
+ return /* @__PURE__ */ jsxs(Box, { ref, className: cn("flex flex-col items-center gap-1 p-4", className), children: [
10478
+ /* @__PURE__ */ jsx(
10479
+ Typography,
10480
+ {
10481
+ variant: "h2",
10482
+ className: "text-primary font-bold tabular-nums",
10483
+ children: hasAnimated ? displayValue : "0"
10484
+ }
10485
+ ),
10486
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "muted", className: "text-center", children: label })
10487
+ ] });
10488
+ };
10489
+ AnimatedCounter.displayName = "AnimatedCounter";
10490
+ }
10491
+ });
10454
10492
  var AuthLayout;
10455
10493
  var init_AuthLayout = __esm({
10456
10494
  "components/core/templates/AuthLayout.tsx"() {
@@ -11804,6 +11842,39 @@ var init_IsometricCanvas2 = __esm({
11804
11842
  init_IsometricCanvas();
11805
11843
  }
11806
11844
  });
11845
+
11846
+ // components/game/organisms/boardEntity.ts
11847
+ function boardEntity(entity) {
11848
+ if (!entity) return void 0;
11849
+ return Array.isArray(entity) ? entity[0] : entity;
11850
+ }
11851
+ function str(v) {
11852
+ return v == null ? "" : String(v);
11853
+ }
11854
+ function num(v, fallback = 0) {
11855
+ const n = Number(v);
11856
+ return Number.isFinite(n) ? n : fallback;
11857
+ }
11858
+ function rows(v) {
11859
+ return Array.isArray(v) ? v : [];
11860
+ }
11861
+ function vec2(v) {
11862
+ const o = v ?? {};
11863
+ return { x: num(o.x), y: num(o.y) };
11864
+ }
11865
+ function unitPosition(u) {
11866
+ return vec2(u.position);
11867
+ }
11868
+ function unitTeam(u) {
11869
+ return str(u.team);
11870
+ }
11871
+ function unitHealth(u) {
11872
+ return num(u.health);
11873
+ }
11874
+ var init_boardEntity = __esm({
11875
+ "components/game/organisms/boardEntity.ts"() {
11876
+ }
11877
+ });
11807
11878
  function BattleBoard({
11808
11879
  entity,
11809
11880
  scale = 0.45,
@@ -11830,43 +11901,49 @@ function BattleBoard({
11830
11901
  attackEvent,
11831
11902
  className
11832
11903
  }) {
11833
- const tiles = entity.tiles;
11834
- const features = entity.features ?? [];
11835
- const boardWidth = entity.boardWidth ?? 8;
11836
- const boardHeight = entity.boardHeight ?? 6;
11837
- const assetManifest = entity.assetManifest;
11838
- const backgroundImage = entity.backgroundImage;
11839
- const units = entity.units;
11840
- const selectedUnitId = entity.selectedUnitId;
11841
- const currentPhase = entity.phase;
11842
- const currentTurn = entity.turn;
11843
- const gameResult = entity.gameResult;
11904
+ const board = boardEntity(entity) ?? {};
11905
+ const tiles = Array.isArray(board.tiles) ? board.tiles : [];
11906
+ const features = Array.isArray(board.features) ? board.features : [];
11907
+ const boardWidth = num(board.boardWidth, 8);
11908
+ const boardHeight = num(board.boardHeight, 6);
11909
+ const assetManifest = board.assetManifest;
11910
+ const backgroundImage = board.backgroundImage;
11911
+ const units = rows(board.units);
11912
+ const selectedUnitId = board.selectedUnitId ?? null;
11913
+ const currentPhase = str(board.phase) || "observation";
11914
+ const currentTurn = num(board.turn, 1);
11915
+ const gameResult = board.gameResult ?? null;
11844
11916
  const eventBus = useEventBus();
11845
11917
  const { t } = useTranslate();
11846
11918
  const [hoveredTile, setHoveredTile] = useState(null);
11847
11919
  const [isShaking, setIsShaking] = useState(false);
11848
11920
  const selectedUnit = useMemo(
11849
- () => units.find((u) => u.id === selectedUnitId) ?? null,
11921
+ () => units.find((u) => str(u.id) === selectedUnitId) ?? null,
11850
11922
  [units, selectedUnitId]
11851
11923
  );
11852
11924
  const hoveredUnit = useMemo(() => {
11853
11925
  if (!hoveredTile) return null;
11854
- return units.find(
11855
- (u) => u.position.x === hoveredTile.x && u.position.y === hoveredTile.y && u.health > 0
11856
- ) ?? null;
11926
+ return units.find((u) => {
11927
+ const p2 = unitPosition(u);
11928
+ return p2.x === hoveredTile.x && p2.y === hoveredTile.y && unitHealth(u) > 0;
11929
+ }) ?? null;
11857
11930
  }, [hoveredTile, units]);
11858
- const playerUnits = useMemo(() => units.filter((u) => u.team === "player" && u.health > 0), [units]);
11859
- const enemyUnits = useMemo(() => units.filter((u) => u.team === "enemy" && u.health > 0), [units]);
11931
+ const playerUnits = useMemo(() => units.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0), [units]);
11932
+ const enemyUnits = useMemo(() => units.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0), [units]);
11860
11933
  const validMoves = useMemo(() => {
11861
11934
  if (!selectedUnit || currentPhase !== "movement") return [];
11862
11935
  const moves = [];
11863
- const range = selectedUnit.movement;
11936
+ const range = num(selectedUnit.movement);
11937
+ const origin = unitPosition(selectedUnit);
11864
11938
  for (let dy = -range; dy <= range; dy++) {
11865
11939
  for (let dx = -range; dx <= range; dx++) {
11866
- const nx = selectedUnit.position.x + dx;
11867
- const ny = selectedUnit.position.y + dy;
11940
+ const nx = origin.x + dx;
11941
+ const ny = origin.y + dy;
11868
11942
  const dist = Math.abs(dx) + Math.abs(dy);
11869
- if (dist > 0 && dist <= range && nx >= 0 && nx < boardWidth && ny >= 0 && ny < boardHeight && !units.some((u) => u.position.x === nx && u.position.y === ny && u.health > 0)) {
11943
+ if (dist > 0 && dist <= range && nx >= 0 && nx < boardWidth && ny >= 0 && ny < boardHeight && !units.some((u) => {
11944
+ const p2 = unitPosition(u);
11945
+ return p2.x === nx && p2.y === ny && unitHealth(u) > 0;
11946
+ })) {
11870
11947
  moves.push({ x: nx, y: ny });
11871
11948
  }
11872
11949
  }
@@ -11875,11 +11952,14 @@ function BattleBoard({
11875
11952
  }, [selectedUnit, currentPhase, units, boardWidth, boardHeight]);
11876
11953
  const attackTargets = useMemo(() => {
11877
11954
  if (!selectedUnit || currentPhase !== "action") return [];
11878
- return units.filter((u) => u.team !== selectedUnit.team && u.health > 0).filter((u) => {
11879
- const dx = Math.abs(u.position.x - selectedUnit.position.x);
11880
- const dy = Math.abs(u.position.y - selectedUnit.position.y);
11955
+ const sp = unitPosition(selectedUnit);
11956
+ const sTeam = unitTeam(selectedUnit);
11957
+ return units.filter((u) => unitTeam(u) !== sTeam && unitHealth(u) > 0).filter((u) => {
11958
+ const p2 = unitPosition(u);
11959
+ const dx = Math.abs(p2.x - sp.x);
11960
+ const dy = Math.abs(p2.y - sp.y);
11881
11961
  return dx <= 1 && dy <= 1 && dx + dy > 0;
11882
- }).map((u) => u.position);
11962
+ }).map((u) => unitPosition(u));
11883
11963
  }, [selectedUnit, currentPhase, units]);
11884
11964
  const MOVE_SPEED_MS_PER_TILE = 300;
11885
11965
  const movementAnimRef = useRef(null);
@@ -11919,23 +11999,25 @@ function BattleBoard({
11919
11999
  return () => clearInterval(interval);
11920
12000
  }, []);
11921
12001
  const isoUnits = useMemo(() => {
11922
- return units.filter((u) => u.health > 0).map((unit) => {
11923
- const pos = movingPositions.get(unit.id) ?? unit.position;
12002
+ return units.filter((u) => unitHealth(u) > 0).map((unit) => {
12003
+ const id = str(unit.id);
12004
+ const pos = movingPositions.get(id) ?? unitPosition(unit);
12005
+ const unitTraits = Array.isArray(unit.traits) ? unit.traits : void 0;
11924
12006
  return {
11925
- id: unit.id,
12007
+ id,
11926
12008
  position: pos,
11927
- name: unit.name,
11928
- team: unit.team,
11929
- health: unit.health,
11930
- maxHealth: unit.maxHealth,
11931
- unitType: unit.unitType,
11932
- heroId: unit.heroId,
11933
- sprite: unit.sprite,
11934
- traits: unit.traits?.map((t2) => ({
11935
- name: t2.name,
11936
- currentState: t2.currentState,
11937
- states: t2.states,
11938
- cooldown: t2.cooldown ?? 0
12009
+ name: str(unit.name),
12010
+ team: unitTeam(unit),
12011
+ health: unitHealth(unit),
12012
+ maxHealth: num(unit.maxHealth),
12013
+ unitType: unit.unitType == null ? void 0 : str(unit.unitType),
12014
+ heroId: unit.heroId == null ? void 0 : str(unit.heroId),
12015
+ sprite: unit.sprite == null ? void 0 : str(unit.sprite),
12016
+ traits: unitTraits?.map((tr) => ({
12017
+ name: tr.name,
12018
+ currentState: tr.currentState,
12019
+ states: tr.states,
12020
+ cooldown: tr.cooldown ?? 0
11939
12021
  }))
11940
12022
  };
11941
12023
  });
@@ -11947,8 +12029,8 @@ function BattleBoard({
11947
12029
  [scale, baseOffsetX]
11948
12030
  );
11949
12031
  const checkGameEnd = useCallback(() => {
11950
- const pa = units.filter((u) => u.team === "player" && u.health > 0);
11951
- const ea = units.filter((u) => u.team === "enemy" && u.health > 0);
12032
+ const pa = units.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
12033
+ const ea = units.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
11952
12034
  if (pa.length === 0) {
11953
12035
  onGameEnd?.("defeat");
11954
12036
  if (gameEndEvent) {
@@ -11962,21 +12044,22 @@ function BattleBoard({
11962
12044
  }
11963
12045
  }, [units, onGameEnd, gameEndEvent, eventBus]);
11964
12046
  const handleUnitClick = useCallback((unitId) => {
11965
- const unit = units.find((u) => u.id === unitId);
12047
+ const unit = units.find((u) => str(u.id) === unitId);
11966
12048
  if (!unit) return;
11967
12049
  if (unitClickEvent) {
11968
12050
  eventBus.emit(`UI:${unitClickEvent}`, { unitId });
11969
12051
  }
11970
12052
  if (currentPhase === "action" && selectedUnit) {
11971
- if (unit.team === "enemy" && attackTargets.some((t2) => t2.x === unit.position.x && t2.y === unit.position.y)) {
11972
- const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, selectedUnit.attack - unit.defense);
12053
+ const up = unitPosition(unit);
12054
+ if (unitTeam(unit) === "enemy" && attackTargets.some((t2) => t2.x === up.x && t2.y === up.y)) {
12055
+ const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, num(selectedUnit.attack) - num(unit.defense));
11973
12056
  setIsShaking(true);
11974
12057
  setTimeout(() => setIsShaking(false), 300);
11975
12058
  onAttack?.(selectedUnit, unit, damage);
11976
12059
  if (attackEvent) {
11977
12060
  eventBus.emit(`UI:${attackEvent}`, {
11978
- attackerId: selectedUnit.id,
11979
- targetId: unit.id,
12061
+ attackerId: str(selectedUnit.id),
12062
+ targetId: str(unit.id),
11980
12063
  damage
11981
12064
  });
11982
12065
  }
@@ -11991,9 +12074,9 @@ function BattleBoard({
11991
12074
  if (currentPhase === "movement" && selectedUnit) {
11992
12075
  if (movementAnimRef.current) return;
11993
12076
  if (validMoves.some((m) => m.x === x && m.y === y)) {
11994
- const from = { ...selectedUnit.position };
12077
+ const from = { ...unitPosition(selectedUnit) };
11995
12078
  const to = { x, y };
11996
- startMoveAnimation(selectedUnit.id, from, to, () => {
12079
+ startMoveAnimation(str(selectedUnit.id), from, to, () => {
11997
12080
  onUnitMove?.(selectedUnit, to);
11998
12081
  });
11999
12082
  }
@@ -12151,6 +12234,7 @@ var init_BattleBoard = __esm({
12151
12234
  init_Typography();
12152
12235
  init_Stack();
12153
12236
  init_IsometricCanvas2();
12237
+ init_boardEntity();
12154
12238
  init_isometric();
12155
12239
  BattleBoard.displayName = "BattleBoard";
12156
12240
  }
@@ -13373,24 +13457,24 @@ var init_CodeBlock = __esm({
13373
13457
  return;
13374
13458
  }
13375
13459
  lineEls.forEach((el) => {
13376
- const num = parseInt(el.getAttribute("data-line") ?? "-1", 10);
13377
- if (hiddenLines.has(num)) {
13460
+ const num2 = parseInt(el.getAttribute("data-line") ?? "-1", 10);
13461
+ if (hiddenLines.has(num2)) {
13378
13462
  el.style.display = "none";
13379
13463
  return;
13380
13464
  }
13381
13465
  el.style.display = "";
13382
13466
  el.style.position = "relative";
13383
13467
  el.style.paddingLeft = "1.2em";
13384
- const region = foldStartMap.get(num);
13468
+ const region = foldStartMap.get(num2);
13385
13469
  if (!region) return;
13386
- const isCollapsed = collapsed.has(num);
13470
+ const isCollapsed = collapsed.has(num2);
13387
13471
  const toggle = document.createElement("span");
13388
13472
  toggle.className = "fold-toggle";
13389
13473
  toggle.textContent = isCollapsed ? "\u25B6" : "\u25BC";
13390
13474
  toggle.style.cssText = "position:absolute;left:0;top:0;width:1.2em;text-align:center;cursor:pointer;color:#858585;font-size:10px;user-select:none;line-height:inherit;height:100%";
13391
13475
  toggle.addEventListener("click", (e) => {
13392
13476
  e.stopPropagation();
13393
- toggleFoldRef.current(num);
13477
+ toggleFoldRef.current(num2);
13394
13478
  });
13395
13479
  el.insertBefore(toggle, el.firstChild);
13396
13480
  if (isCollapsed) {
@@ -15643,10 +15727,13 @@ var init_BookChapterView = __esm({
15643
15727
  init_cn();
15644
15728
  BookChapterView = ({
15645
15729
  chapter,
15730
+ orbitalSchema,
15646
15731
  direction,
15647
15732
  className
15648
15733
  }) => {
15649
15734
  const { t: _t } = useTranslate();
15735
+ const title = String(chapter.title ?? "");
15736
+ const content = String(chapter.content ?? "");
15650
15737
  return /* @__PURE__ */ jsxs(
15651
15738
  VStack,
15652
15739
  {
@@ -15654,16 +15741,16 @@ var init_BookChapterView = __esm({
15654
15741
  className: cn("px-6 py-8 max-w-4xl mx-auto w-full", className),
15655
15742
  style: { direction },
15656
15743
  children: [
15657
- /* @__PURE__ */ jsx(Typography, { variant: "h1", className: "text-3xl font-bold", children: chapter.title }),
15744
+ /* @__PURE__ */ jsx(Typography, { variant: "h1", className: "text-3xl font-bold", children: title }),
15658
15745
  /* @__PURE__ */ jsx(Divider, {}),
15659
- !!chapter.orbitalSchema && /* @__PURE__ */ jsx(ScaledDiagram, { children: /* @__PURE__ */ jsx(
15746
+ !!orbitalSchema && /* @__PURE__ */ jsx(ScaledDiagram, { children: /* @__PURE__ */ jsx(
15660
15747
  JazariStateMachine,
15661
15748
  {
15662
- schema: chapter.orbitalSchema,
15749
+ schema: orbitalSchema,
15663
15750
  direction
15664
15751
  }
15665
15752
  ) }),
15666
- /* @__PURE__ */ jsx(ContentRenderer, { content: chapter.content, direction })
15753
+ /* @__PURE__ */ jsx(ContentRenderer, { content, direction })
15667
15754
  ]
15668
15755
  }
15669
15756
  );
@@ -15761,7 +15848,7 @@ var init_BookNavBar = __esm({
15761
15848
  BookNavBar = ({
15762
15849
  currentPage,
15763
15850
  totalPages,
15764
- chapterTitle,
15851
+ chapterTitle: chapterTitle2,
15765
15852
  direction,
15766
15853
  className
15767
15854
  }) => {
@@ -15802,12 +15889,12 @@ var init_BookNavBar = __esm({
15802
15889
  )
15803
15890
  ] }),
15804
15891
  /* @__PURE__ */ jsxs(Box, { className: "flex-1 mx-4 max-w-md", children: [
15805
- chapterTitle && /* @__PURE__ */ jsx(
15892
+ chapterTitle2 && /* @__PURE__ */ jsx(
15806
15893
  Typography,
15807
15894
  {
15808
15895
  variant: "caption",
15809
15896
  className: "text-center block truncate text-muted-foreground",
15810
- children: chapterTitle
15897
+ children: chapterTitle2
15811
15898
  }
15812
15899
  ),
15813
15900
  /* @__PURE__ */ jsx(ProgressBar, { value: progress, size: "sm", variant: "primary" })
@@ -15874,31 +15961,35 @@ var init_BookTableOfContents = __esm({
15874
15961
  style: { direction },
15875
15962
  children: [
15876
15963
  /* @__PURE__ */ jsx(Typography, { variant: "h1", className: "text-3xl font-bold text-center mb-4", children: t("book.tableOfContents") }),
15877
- parts.map((part, partIdx) => /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
15878
- /* @__PURE__ */ jsxs(HStack, { gap: "sm", align: "center", children: [
15879
- /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: t("book.partNumber", { number: String(partIdx + 1) }) }),
15880
- /* @__PURE__ */ jsx(Typography, { variant: "h3", className: "font-semibold", children: part.title })
15881
- ] }),
15882
- /* @__PURE__ */ jsx(VStack, { gap: "xs", className: direction === "rtl" ? "pr-6" : "pl-6", children: part.chapters.map((chapter) => {
15883
- const isCurrent = chapter.id === currentChapterId;
15884
- return /* @__PURE__ */ jsx(
15885
- Button,
15886
- {
15887
- variant: "ghost",
15888
- size: "sm",
15889
- action: "BOOK_NAVIGATE",
15890
- actionPayload: { chapterId: chapter.id },
15891
- className: cn(
15892
- "justify-start text-left w-full",
15893
- direction === "rtl" && "text-right",
15894
- isCurrent && "bg-blue-50 dark:bg-blue-950 text-blue-600 dark:text-blue-400"
15895
- ),
15896
- children: /* @__PURE__ */ jsx(Box, { className: "truncate", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: chapter.title }) })
15897
- },
15898
- chapter.id
15899
- );
15900
- }) })
15901
- ] }, partIdx))
15964
+ parts.map((part, partIdx) => {
15965
+ const chapters = Array.isArray(part.chapters) ? part.chapters : [];
15966
+ return /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
15967
+ /* @__PURE__ */ jsxs(HStack, { gap: "sm", align: "center", children: [
15968
+ /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: t("book.partNumber", { number: String(partIdx + 1) }) }),
15969
+ /* @__PURE__ */ jsx(Typography, { variant: "h3", className: "font-semibold", children: String(part.title ?? "") })
15970
+ ] }),
15971
+ /* @__PURE__ */ jsx(VStack, { gap: "xs", className: direction === "rtl" ? "pr-6" : "pl-6", children: chapters.map((chapter) => {
15972
+ const id = chapter.id == null ? "" : String(chapter.id);
15973
+ const isCurrent = id === currentChapterId;
15974
+ return /* @__PURE__ */ jsx(
15975
+ Button,
15976
+ {
15977
+ variant: "ghost",
15978
+ size: "sm",
15979
+ action: "BOOK_NAVIGATE",
15980
+ actionPayload: { chapterId: id },
15981
+ className: cn(
15982
+ "justify-start text-left w-full",
15983
+ direction === "rtl" && "text-right",
15984
+ isCurrent && "bg-blue-50 dark:bg-blue-950 text-blue-600 dark:text-blue-400"
15985
+ ),
15986
+ children: /* @__PURE__ */ jsx(Box, { className: "truncate", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: String(chapter.title ?? "") }) })
15987
+ },
15988
+ id
15989
+ );
15990
+ }) })
15991
+ ] }, partIdx);
15992
+ })
15902
15993
  ]
15903
15994
  }
15904
15995
  );
@@ -16020,27 +16111,41 @@ function resolveFieldMap(fieldMap) {
16020
16111
  function get(obj, key) {
16021
16112
  return obj[key];
16022
16113
  }
16114
+ function asStr(v) {
16115
+ return v == null ? "" : String(v);
16116
+ }
16023
16117
  function mapBookData(raw, fields = IDENTITY_BOOK_FIELDS) {
16024
16118
  const rawParts = get(raw, fields.parts) ?? [];
16025
- return {
16026
- title: get(raw, fields.title) ?? "",
16027
- subtitle: get(raw, fields.subtitle),
16028
- author: get(raw, fields.author),
16029
- coverImageUrl: get(raw, fields.coverImageUrl),
16030
- direction: get(raw, fields.direction) ?? void 0,
16031
- parts: rawParts.map((part) => {
16032
- const rawChapters = get(part, fields.chapters) ?? [];
16033
- return {
16034
- title: get(part, fields.partTitle) ?? "",
16035
- chapters: rawChapters.map((ch) => ({
16036
- id: get(ch, fields.chapterId) ?? "",
16037
- title: get(ch, fields.chapterTitle) ?? "",
16038
- content: get(ch, fields.chapterContent) ?? "",
16039
- orbitalSchema: get(ch, fields.chapterOrbitalSchema)
16040
- }))
16041
- };
16042
- })
16119
+ const direction = get(raw, fields.direction) ?? "ltr";
16120
+ const cover = {
16121
+ title: asStr(get(raw, fields.title)),
16122
+ subtitle: asStr(get(raw, fields.subtitle)),
16123
+ author: asStr(get(raw, fields.author)),
16124
+ coverImageUrl: asStr(get(raw, fields.coverImageUrl)),
16125
+ direction
16043
16126
  };
16127
+ const schemaByChapterId = {};
16128
+ const chapters = [];
16129
+ const parts = rawParts.map((part) => {
16130
+ const rawChapters = get(part, fields.chapters) ?? [];
16131
+ const chapterRows = rawChapters.map((ch) => {
16132
+ const id = asStr(get(ch, fields.chapterId));
16133
+ const schema = get(ch, fields.chapterOrbitalSchema);
16134
+ if (schema) schemaByChapterId[id] = schema;
16135
+ const row = {
16136
+ id,
16137
+ title: asStr(get(ch, fields.chapterTitle)),
16138
+ content: asStr(get(ch, fields.chapterContent))
16139
+ };
16140
+ chapters.push(row);
16141
+ return row;
16142
+ });
16143
+ return {
16144
+ title: asStr(get(part, fields.partTitle)),
16145
+ chapters: chapterRows
16146
+ };
16147
+ });
16148
+ return { cover, direction, parts, chapters, schemaByChapterId };
16044
16149
  }
16045
16150
  var IDENTITY_BOOK_FIELDS, AR_BOOK_FIELDS, FIELD_MAP_REGISTRY;
16046
16151
  var init_types2 = __esm({
@@ -16078,10 +16183,7 @@ var init_types2 = __esm({
16078
16183
  };
16079
16184
  }
16080
16185
  });
16081
- function flattenChapters(book) {
16082
- return book.parts.flatMap((part) => part.chapters);
16083
- }
16084
- var PRINT_STYLES, BookViewer;
16186
+ var chapterId, chapterTitle, PRINT_STYLES, BookViewer;
16085
16187
  var init_BookViewer = __esm({
16086
16188
  "components/marketing/organisms/book/BookViewer.tsx"() {
16087
16189
  init_Box();
@@ -16094,6 +16196,8 @@ var init_BookViewer = __esm({
16094
16196
  init_BookNavBar();
16095
16197
  init_EmptyState();
16096
16198
  init_types2();
16199
+ chapterId = (ch) => ch?.id == null ? void 0 : String(ch.id);
16200
+ chapterTitle = (ch) => ch?.title == null ? void 0 : String(ch.title);
16097
16201
  PRINT_STYLES = `
16098
16202
  @media print {
16099
16203
  .book-viewer-page {
@@ -16122,14 +16226,14 @@ var init_BookViewer = __esm({
16122
16226
  return mapBookData(raw, resolvedFieldMap);
16123
16227
  }, [entity, resolvedFieldMap]);
16124
16228
  const direction = book?.direction ?? "ltr";
16125
- const chapters = useMemo(() => book ? flattenChapters(book) : [], [book]);
16229
+ const chapters = useMemo(() => book ? book.chapters : [], [book]);
16126
16230
  const totalPages = 2 + chapters.length;
16127
16231
  const navigateTo = useCallback(
16128
16232
  (page) => {
16129
16233
  const clamped = Math.max(0, Math.min(page, totalPages - 1));
16130
16234
  setCurrentPage(clamped);
16131
- const chapterId = clamped >= 2 ? chapters[clamped - 2]?.id : void 0;
16132
- eventBus.emit("UI:BOOK_PAGE_CHANGE", { pageIndex: clamped, chapterId });
16235
+ const id = clamped >= 2 ? chapterId(chapters[clamped - 2]) : void 0;
16236
+ eventBus.emit("UI:BOOK_PAGE_CHANGE", { pageIndex: clamped, chapterId: id });
16133
16237
  },
16134
16238
  [totalPages, chapters, eventBus]
16135
16239
  );
@@ -16141,8 +16245,8 @@ var init_BookViewer = __esm({
16141
16245
  eventBus.on("UI:BOOK_PAGE_NEXT", () => navigateTo(currentPage + 1)),
16142
16246
  eventBus.on("UI:BOOK_PRINT", () => window.print()),
16143
16247
  eventBus.on("UI:BOOK_NAVIGATE", (event) => {
16144
- const chapterId = event.payload?.chapterId;
16145
- const idx = chapters.findIndex((ch) => ch.id === chapterId);
16248
+ const targetId = event.payload?.chapterId;
16249
+ const idx = chapters.findIndex((ch) => chapterId(ch) === targetId);
16146
16250
  if (idx >= 0) navigateTo(idx + 2);
16147
16251
  })
16148
16252
  ];
@@ -16159,9 +16263,11 @@ var init_BookViewer = __esm({
16159
16263
  style.remove();
16160
16264
  };
16161
16265
  }, []);
16162
- const currentChapterId = currentPage >= 2 ? chapters[currentPage - 2]?.id : void 0;
16163
- const currentChapterTitle = currentPage >= 2 ? chapters[currentPage - 2]?.title : void 0;
16266
+ const currentChapterId = currentPage >= 2 ? chapterId(chapters[currentPage - 2]) : void 0;
16267
+ const currentChapterTitle = currentPage >= 2 ? chapterTitle(chapters[currentPage - 2]) : void 0;
16164
16268
  if (!book) return /* @__PURE__ */ jsx(EmptyState, { message: t("book.noData") });
16269
+ const cover = book.cover;
16270
+ const coverTitle = String(cover.title ?? "");
16165
16271
  return /* @__PURE__ */ jsxs(VStack, { className: cn("relative h-full overflow-hidden bg-background", className), children: [
16166
16272
  /* @__PURE__ */ jsxs(
16167
16273
  Box,
@@ -16173,10 +16279,10 @@ var init_BookViewer = __esm({
16173
16279
  /* @__PURE__ */ jsx(
16174
16280
  BookCoverPage,
16175
16281
  {
16176
- title: book.title,
16177
- subtitle: book.subtitle,
16178
- author: book.author,
16179
- coverImageUrl: book.coverImageUrl,
16282
+ title: coverTitle,
16283
+ subtitle: String(cover.subtitle ?? "") || void 0,
16284
+ author: String(cover.author ?? "") || void 0,
16285
+ coverImageUrl: String(cover.coverImageUrl ?? "") || void 0,
16180
16286
  direction
16181
16287
  }
16182
16288
  ),
@@ -16187,23 +16293,27 @@ var init_BookViewer = __esm({
16187
16293
  direction
16188
16294
  }
16189
16295
  ),
16190
- chapters.map((chapter) => /* @__PURE__ */ jsx(
16191
- BookChapterView,
16192
- {
16193
- chapter,
16194
- direction
16195
- },
16196
- chapter.id
16197
- ))
16296
+ chapters.map((chapter) => {
16297
+ const id = chapterId(chapter);
16298
+ return /* @__PURE__ */ jsx(
16299
+ BookChapterView,
16300
+ {
16301
+ chapter,
16302
+ orbitalSchema: id ? book.schemaByChapterId[id] : void 0,
16303
+ direction
16304
+ },
16305
+ id
16306
+ );
16307
+ })
16198
16308
  ] }),
16199
16309
  /* @__PURE__ */ jsxs(Box, { className: "print:hidden", children: [
16200
16310
  currentPage === 0 && /* @__PURE__ */ jsx(
16201
16311
  BookCoverPage,
16202
16312
  {
16203
- title: book.title,
16204
- subtitle: book.subtitle,
16205
- author: book.author,
16206
- coverImageUrl: book.coverImageUrl,
16313
+ title: coverTitle,
16314
+ subtitle: String(cover.subtitle ?? "") || void 0,
16315
+ author: String(cover.author ?? "") || void 0,
16316
+ coverImageUrl: String(cover.coverImageUrl ?? "") || void 0,
16207
16317
  direction
16208
16318
  }
16209
16319
  ),
@@ -16219,6 +16329,7 @@ var init_BookViewer = __esm({
16219
16329
  BookChapterView,
16220
16330
  {
16221
16331
  chapter: chapters[currentPage - 2],
16332
+ orbitalSchema: currentChapterId ? book.schemaByChapterId[currentChapterId] : void 0,
16222
16333
  direction
16223
16334
  }
16224
16335
  )
@@ -16231,7 +16342,7 @@ var init_BookViewer = __esm({
16231
16342
  {
16232
16343
  currentPage,
16233
16344
  totalPages,
16234
- chapterTitle: currentPage === 0 ? book.title : currentPage === 1 ? t("book.tableOfContents") : currentChapterTitle,
16345
+ chapterTitle: currentPage === 0 ? coverTitle : currentPage === 1 ? t("book.tableOfContents") : currentChapterTitle,
16235
16346
  direction
16236
16347
  }
16237
16348
  )
@@ -16329,7 +16440,7 @@ var init_Grid = __esm({
16329
16440
  };
16330
16441
  Grid = ({
16331
16442
  cols = 1,
16332
- rows,
16443
+ rows: rows2,
16333
16444
  gap = "md",
16334
16445
  rowGap,
16335
16446
  colGap,
@@ -16341,7 +16452,7 @@ var init_Grid = __esm({
16341
16452
  children,
16342
16453
  as: Component = "div"
16343
16454
  }) => {
16344
- const mergedStyle = rows ? { gridTemplateRows: `repeat(${rows}, minmax(0, 1fr))`, ...style } : style;
16455
+ const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
16345
16456
  return React84__default.createElement(
16346
16457
  Component,
16347
16458
  {
@@ -17057,14 +17168,14 @@ function BuilderBoard({
17057
17168
  }) {
17058
17169
  const { emit } = useEventBus();
17059
17170
  const { t } = useTranslate();
17060
- const resolved = Array.isArray(entity) ? entity[0] : entity;
17171
+ const resolved = boardEntity(entity);
17061
17172
  const [placements, setPlacements] = useState({});
17062
17173
  const [headerError, setHeaderError] = useState(false);
17063
17174
  const [submitted, setSubmitted] = useState(false);
17064
17175
  const [attempts, setAttempts] = useState(0);
17065
17176
  const [showHint, setShowHint] = useState(false);
17066
- const components = resolved?.components ?? [];
17067
- const slots = resolved?.slots ?? [];
17177
+ const components = Array.isArray(resolved?.components) ? resolved.components : [];
17178
+ const slots = Array.isArray(resolved?.slots) ? resolved.slots : [];
17068
17179
  const usedComponentIds = new Set(Object.values(placements));
17069
17180
  const availableComponents = components.filter((c) => !usedComponentIds.has(c.id));
17070
17181
  const [selectedComponent, setSelectedComponent] = useState(null);
@@ -17098,7 +17209,7 @@ function BuilderBoard({
17098
17209
  }, [slots, placements, attempts, completeEvent, emit]);
17099
17210
  const handleReset = () => {
17100
17211
  setSubmitted(false);
17101
- if (attempts >= 2 && resolved?.hint) {
17212
+ if (attempts >= 2 && str(resolved?.hint)) {
17102
17213
  setShowHint(true);
17103
17214
  }
17104
17215
  };
@@ -17111,20 +17222,24 @@ function BuilderBoard({
17111
17222
  };
17112
17223
  const getComponentById = (id) => components.find((c) => c.id === id);
17113
17224
  if (!resolved) return null;
17225
+ const theme = resolved.theme ?? void 0;
17226
+ const themeBackground = theme?.background;
17227
+ const headerImage = str(resolved.headerImage);
17228
+ const hint = str(resolved.hint);
17114
17229
  return /* @__PURE__ */ jsx(
17115
17230
  Box,
17116
17231
  {
17117
17232
  className,
17118
17233
  style: {
17119
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
17234
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
17120
17235
  backgroundSize: "cover",
17121
17236
  backgroundPosition: "center"
17122
17237
  },
17123
17238
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
17124
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
17239
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
17125
17240
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
17126
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
17127
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description })
17241
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
17242
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) })
17128
17243
  ] }) }),
17129
17244
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
17130
17245
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("builder.components") }),
@@ -17184,9 +17299,9 @@ function BuilderBoard({
17184
17299
  ] }) }),
17185
17300
  submitted && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
17186
17301
  /* @__PURE__ */ jsx(Icon, { icon: allCorrect ? CheckCircle : XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
17187
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("builder.success") : resolved.failMessage ?? t("builder.incorrect") })
17302
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("builder.success") : str(resolved.failMessage) || t("builder.incorrect") })
17188
17303
  ] }) }),
17189
- showHint && resolved.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
17304
+ showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
17190
17305
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
17191
17306
  !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
17192
17307
  /* @__PURE__ */ jsx(Icon, { icon: Wrench, size: "sm" }),
@@ -17205,6 +17320,7 @@ var init_BuilderBoard = __esm({
17205
17320
  "components/game/organisms/puzzles/builder/BuilderBoard.tsx"() {
17206
17321
  init_atoms2();
17207
17322
  init_useEventBus();
17323
+ init_boardEntity();
17208
17324
  BuilderBoard.displayName = "BuilderBoard";
17209
17325
  }
17210
17326
  });
@@ -17542,21 +17658,24 @@ function CalendarGrid({
17542
17658
  eventBus.emit(`UI:${longPressEvent}`, { date: day.toISOString(), time, ...longPressPayload });
17543
17659
  }, 500);
17544
17660
  }, [longPressEvent, longPressPayload, eventBus]);
17545
- const renderEvent = (event) => /* @__PURE__ */ jsx(
17546
- Box,
17547
- {
17548
- rounded: "md",
17549
- padding: "xs",
17550
- border: true,
17551
- className: cn(
17552
- "cursor-pointer hover:shadow-sm transition-shadow text-xs truncate",
17553
- event.color ? event.color : "bg-blue-500/15 border-blue-500/30 text-blue-600"
17554
- ),
17555
- onClick: (e) => handleEventClick(event, e),
17556
- children: /* @__PURE__ */ jsx(Typography, { variant: "small", className: "truncate font-medium", children: event.title })
17557
- },
17558
- event.id
17559
- );
17661
+ const renderEvent = (event) => {
17662
+ const color = event.color;
17663
+ return /* @__PURE__ */ jsx(
17664
+ Box,
17665
+ {
17666
+ rounded: "md",
17667
+ padding: "xs",
17668
+ border: true,
17669
+ className: cn(
17670
+ "cursor-pointer hover:shadow-sm transition-shadow text-xs truncate",
17671
+ color ? color : "bg-blue-500/15 border-blue-500/30 text-blue-600"
17672
+ ),
17673
+ onClick: (e) => handleEventClick(event, e),
17674
+ children: /* @__PURE__ */ jsx(Typography, { variant: "small", className: "truncate font-medium", children: event.title })
17675
+ },
17676
+ event.id
17677
+ );
17678
+ };
17560
17679
  return /* @__PURE__ */ jsxs(
17561
17680
  Box,
17562
17681
  {
@@ -19220,7 +19339,6 @@ var init_CardGrid = __esm({
19220
19339
  alignItems = "stretch",
19221
19340
  className,
19222
19341
  children,
19223
- // EntityDisplayProps
19224
19342
  entity,
19225
19343
  isLoading = false,
19226
19344
  error = null,
@@ -19692,14 +19810,14 @@ var init_CaseStudyOrganism = __esm({
19692
19810
  /* @__PURE__ */ jsx(SimpleGrid, { cols: cols > 0 ? cols : 1, gap: "lg", children: items.map((study) => /* @__PURE__ */ jsx(
19693
19811
  CaseStudyCard,
19694
19812
  {
19695
- title: study.title,
19696
- description: study.description,
19697
- category: study.category,
19698
- categoryColor: study.categoryColor,
19699
- href: study.href,
19700
- linkLabel: study.linkLabel
19813
+ title: String(study.title ?? ""),
19814
+ description: String(study.description ?? ""),
19815
+ category: String(study.category ?? ""),
19816
+ categoryColor: study.categoryColor != null ? String(study.categoryColor) : void 0,
19817
+ href: String(study.href ?? ""),
19818
+ linkLabel: study.linkLabel != null ? String(study.linkLabel) : void 0
19701
19819
  },
19702
- study.id
19820
+ String(study.id ?? "")
19703
19821
  )) })
19704
19822
  ] });
19705
19823
  };
@@ -19722,10 +19840,10 @@ function CastleBoard({
19722
19840
  className
19723
19841
  }) {
19724
19842
  const eventBus = useEventBus();
19725
- const resolved = Array.isArray(entity) ? entity[0] : entity;
19726
- const tiles = resolved?.tiles ?? [];
19727
- const features = resolved?.features ?? [];
19728
- const units = resolved?.units ?? [];
19843
+ const resolved = boardEntity(entity);
19844
+ const tiles = Array.isArray(resolved?.tiles) ? resolved.tiles : [];
19845
+ const features = Array.isArray(resolved?.features) ? resolved.features : [];
19846
+ const units = Array.isArray(resolved?.units) ? resolved.units : [];
19729
19847
  const assetManifest = resolved?.assetManifest;
19730
19848
  const backgroundImage = resolved?.backgroundImage;
19731
19849
  const [hoveredTile, setHoveredTile] = useState(null);
@@ -19753,7 +19871,7 @@ function CastleBoard({
19753
19871
  onFeatureClick?.(feature);
19754
19872
  if (featureClickEvent) {
19755
19873
  eventBus.emit(`UI:${featureClickEvent}`, {
19756
- featureId: feature.id,
19874
+ featureId: feature.id ?? "",
19757
19875
  featureType: feature.type,
19758
19876
  x: feature.x,
19759
19877
  y: feature.y
@@ -19821,6 +19939,7 @@ var init_CastleBoard = __esm({
19821
19939
  init_cn();
19822
19940
  init_useEventBus();
19823
19941
  init_IsometricCanvas2();
19942
+ init_boardEntity();
19824
19943
  init_isometric();
19825
19944
  CastleBoard.displayName = "CastleBoard";
19826
19945
  }
@@ -20631,14 +20750,14 @@ function ClassifierBoard({
20631
20750
  }) {
20632
20751
  const { emit } = useEventBus();
20633
20752
  const { t } = useTranslate();
20634
- const resolved = Array.isArray(entity) ? entity[0] : entity;
20753
+ const resolved = boardEntity(entity);
20635
20754
  const [assignments, setAssignments] = useState({});
20636
20755
  const [headerError, setHeaderError] = useState(false);
20637
20756
  const [submitted, setSubmitted] = useState(false);
20638
20757
  const [attempts, setAttempts] = useState(0);
20639
20758
  const [showHint, setShowHint] = useState(false);
20640
- const items = resolved?.items ?? [];
20641
- const categories = resolved?.categories ?? [];
20759
+ const items = Array.isArray(resolved?.items) ? resolved.items : [];
20760
+ const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
20642
20761
  const unassignedItems = items.filter((item) => !assignments[item.id]);
20643
20762
  const allAssigned = Object.keys(assignments).length === items.length;
20644
20763
  const results = submitted ? items.map((item) => ({
@@ -20670,7 +20789,7 @@ function ClassifierBoard({
20670
20789
  }, [items, assignments, attempts, completeEvent, emit]);
20671
20790
  const handleReset = () => {
20672
20791
  setSubmitted(false);
20673
- if (attempts >= 2 && resolved?.hint) {
20792
+ if (attempts >= 2 && str(resolved?.hint)) {
20674
20793
  setShowHint(true);
20675
20794
  }
20676
20795
  };
@@ -20681,20 +20800,25 @@ function ClassifierBoard({
20681
20800
  setShowHint(false);
20682
20801
  };
20683
20802
  if (!resolved) return null;
20803
+ const theme = resolved.theme ?? void 0;
20804
+ const themeBackground = theme?.background;
20805
+ const headerImage = str(resolved.headerImage);
20806
+ const hint = str(resolved.hint);
20807
+ const failMessage = str(resolved.failMessage);
20684
20808
  return /* @__PURE__ */ jsx(
20685
20809
  Box,
20686
20810
  {
20687
20811
  className,
20688
20812
  style: {
20689
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
20813
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
20690
20814
  backgroundSize: "cover",
20691
20815
  backgroundPosition: "center"
20692
20816
  },
20693
20817
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
20694
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
20818
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
20695
20819
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
20696
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
20697
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description })
20820
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
20821
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) })
20698
20822
  ] }) }),
20699
20823
  unassignedItems.length > 0 && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
20700
20824
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("classifier.itemsToSort") }),
@@ -20746,10 +20870,10 @@ function ClassifierBoard({
20746
20870
  }) }),
20747
20871
  submitted && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
20748
20872
  /* @__PURE__ */ jsx(Icon, { icon: allCorrect ? CheckCircle : XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
20749
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("classifier.allCorrect") : `${correctCount}/${items.length} ${t("classifier.correct")}` }),
20750
- !allCorrect && resolved.failMessage && /* @__PURE__ */ jsx(Typography, { variant: "body", className: "text-muted-foreground", children: resolved.failMessage })
20873
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("classifier.allCorrect") : `${correctCount}/${items.length} ${t("classifier.correct")}` }),
20874
+ !allCorrect && failMessage && /* @__PURE__ */ jsx(Typography, { variant: "body", className: "text-muted-foreground", children: failMessage })
20751
20875
  ] }) }),
20752
- showHint && resolved.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
20876
+ showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
20753
20877
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
20754
20878
  !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
20755
20879
  /* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
@@ -20768,6 +20892,7 @@ var init_ClassifierBoard = __esm({
20768
20892
  "components/game/organisms/puzzles/classifier/ClassifierBoard.tsx"() {
20769
20893
  init_atoms2();
20770
20894
  init_useEventBus();
20895
+ init_boardEntity();
20771
20896
  ClassifierBoard.displayName = "ClassifierBoard";
20772
20897
  }
20773
20898
  });
@@ -27138,7 +27263,7 @@ function InventoryPanel({
27138
27263
  const slotArray = Array.from({ length: safeSlots }, (_, index) => {
27139
27264
  return safeItems[index] ?? null;
27140
27265
  });
27141
- const rows = Math.ceil(safeSlots / safeColumns);
27266
+ const rows2 = Math.ceil(safeSlots / safeColumns);
27142
27267
  const handleSlotClick = useCallback((index) => {
27143
27268
  if (selectSlotEvent) eventBus.emit(`UI:${selectSlotEvent}`, { index });
27144
27269
  onSelectSlot?.(index);
@@ -27207,7 +27332,7 @@ function InventoryPanel({
27207
27332
  className: "grid gap-1 bg-[var(--color-card)] p-2 rounded-container border border-border",
27208
27333
  style: {
27209
27334
  gridTemplateColumns: `repeat(${safeColumns}, ${slotSize}px)`,
27210
- gridTemplateRows: `repeat(${rows}, ${slotSize}px)`
27335
+ gridTemplateRows: `repeat(${rows2}, ${slotSize}px)`
27211
27336
  },
27212
27337
  children: slotArray.map((item, index) => /* @__PURE__ */ jsx(
27213
27338
  "button",
@@ -31171,11 +31296,11 @@ function LatticeSVG({
31171
31296
  }) {
31172
31297
  const paths = [];
31173
31298
  const cols = 5;
31174
- const rows = Math.ceil(h / (w / cols));
31299
+ const rows2 = Math.ceil(h / (w / cols));
31175
31300
  const cellW = w / cols;
31176
31301
  const cellH = cellW;
31177
31302
  const bulge = cellW * 0.3;
31178
- for (let row = 0; row < rows; row++) {
31303
+ for (let row = 0; row < rows2; row++) {
31179
31304
  for (let col = 0; col < cols; col++) {
31180
31305
  const cx = col * cellW + cellW / 2;
31181
31306
  const cy = row * cellH + cellH / 2;
@@ -31587,7 +31712,7 @@ var init_MatrixQuestion = __esm({
31587
31712
  };
31588
31713
  MatrixQuestion = ({
31589
31714
  title,
31590
- rows,
31715
+ rows: rows2,
31591
31716
  columns = DEFAULT_MATRIX_COLUMNS,
31592
31717
  values,
31593
31718
  onChange,
@@ -31597,7 +31722,7 @@ var init_MatrixQuestion = __esm({
31597
31722
  className
31598
31723
  }) => {
31599
31724
  const styles = sizeStyles13[size];
31600
- const safeRows = rows ?? [];
31725
+ const safeRows = rows2 ?? [];
31601
31726
  const safeValues = values ?? {};
31602
31727
  const eventBus = useEventBus();
31603
31728
  const handleChange = useCallback(
@@ -32225,7 +32350,8 @@ var init_PositionedCanvas = __esm({
32225
32350
  dragRef.current = null;
32226
32351
  setDraggingId(null);
32227
32352
  if (!wasDrag) {
32228
- const next = selectedId === item.id ? null : item.id;
32353
+ const itemId = item.id;
32354
+ const next = selectedId === itemId ? null : itemId;
32229
32355
  onSelect?.(next);
32230
32356
  if (selectEvent) {
32231
32357
  eventBus.emit(`UI:${selectEvent}`, { id: next });
@@ -32260,15 +32386,22 @@ var init_PositionedCanvas = __esm({
32260
32386
  style: { width, height },
32261
32387
  onClick: handleContainerClick,
32262
32388
  children: items.map((item) => {
32389
+ const itemId = item.id;
32390
+ const label = item.label;
32391
+ const x = item.x;
32392
+ const y = item.y;
32393
+ const capacity = item.capacity;
32394
+ const partySize = item.partySize;
32395
+ const serverName = item.serverName;
32263
32396
  const status = item.status ?? "empty";
32264
32397
  const shape = item.shape ?? "round";
32265
- const isSelected = selectedId === item.id;
32266
- const isDragging = draggingId === item.id;
32398
+ const isSelected = selectedId === itemId;
32399
+ const isDragging = draggingId === itemId;
32267
32400
  const statusBadge = STATUS_BADGE[status];
32268
32401
  return /* @__PURE__ */ jsxs(
32269
32402
  Box,
32270
32403
  {
32271
- "data-testid": `item-node-${item.id}`,
32404
+ "data-testid": `item-node-${itemId}`,
32272
32405
  "data-status": status,
32273
32406
  className: cn(
32274
32407
  "absolute flex flex-col items-center justify-center gap-1 border-2 select-none",
@@ -32279,7 +32412,7 @@ var init_PositionedCanvas = __esm({
32279
32412
  isSelected && "outline outline-2 outline-offset-2 outline-primary shadow-md",
32280
32413
  isDragging && "shadow-lg z-10"
32281
32414
  ),
32282
- style: { left: item.x, top: item.y, touchAction: "none" },
32415
+ style: { left: x, top: y, touchAction: "none" },
32283
32416
  onPointerDown: (e) => handlePointerDown(e, item),
32284
32417
  onPointerMove: handlePointerMove,
32285
32418
  onPointerUp: (e) => handlePointerUp(e, item),
@@ -32287,10 +32420,10 @@ var init_PositionedCanvas = __esm({
32287
32420
  children: [
32288
32421
  /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-1", children: [
32289
32422
  getStatusIcon(status),
32290
- /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "semibold", children: item.label })
32423
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "semibold", children: label })
32291
32424
  ] }),
32292
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: item.partySize !== void 0 && status === "seated" ? `${item.partySize}/${item.capacity}` : `Cap ${item.capacity}` }),
32293
- status === "seated" && item.serverName && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "truncate max-w-[80%]", children: item.serverName }),
32425
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: partySize !== void 0 && status === "seated" ? `${partySize}/${capacity}` : `Cap ${capacity}` }),
32426
+ status === "seated" && serverName && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "truncate max-w-[80%]", children: serverName }),
32294
32427
  isSelected && /* @__PURE__ */ jsx(
32295
32428
  Badge,
32296
32429
  {
@@ -32302,7 +32435,7 @@ var init_PositionedCanvas = __esm({
32302
32435
  )
32303
32436
  ]
32304
32437
  },
32305
- item.id
32438
+ itemId
32306
32439
  );
32307
32440
  })
32308
32441
  }
@@ -33074,9 +33207,10 @@ var init_RichBlockEditor = __esm({
33074
33207
  });
33075
33208
  function collectInitiallyCollapsed(nodes, acc) {
33076
33209
  for (const n of nodes) {
33210
+ const replies = n.replies;
33077
33211
  if (n.collapsed) acc.add(n.id);
33078
- if (n.replies && n.replies.length > 0) {
33079
- collectInitiallyCollapsed(n.replies, acc);
33212
+ if (replies && replies.length > 0) {
33213
+ collectInitiallyCollapsed(replies, acc);
33080
33214
  }
33081
33215
  }
33082
33216
  }
@@ -33106,44 +33240,52 @@ var init_ReplyTree = __esm({
33106
33240
  }) => {
33107
33241
  const eventBus = useEventBus();
33108
33242
  const { t } = useTranslate();
33109
- const hasReplies = !!node.replies && node.replies.length > 0;
33110
- const isCollapsed = collapsedSet.has(node.id);
33243
+ const nodeId = node.id;
33244
+ const authorName = node.authorName;
33245
+ const authorAvatarUrl = node.authorAvatarUrl;
33246
+ const content = node.content;
33247
+ const postedAt = node.postedAt;
33248
+ const voteCount = node.voteCount;
33249
+ const userVote = node.userVote;
33250
+ const replies = node.replies;
33251
+ const hasReplies = !!replies && replies.length > 0;
33252
+ const isCollapsed = collapsedSet.has(nodeId);
33111
33253
  const atMaxDepth = depth >= maxDepth;
33112
33254
  const [replyOpen, setReplyOpen] = useState(false);
33113
33255
  const [draft, setDraft] = useState("");
33114
33256
  const handleVote = useCallback(
33115
33257
  (next) => {
33116
- onVote?.(node.id, next);
33117
- if (voteEvent) eventBus.emit(`UI:${voteEvent}`, { nodeId: node.id, vote: next });
33258
+ onVote?.(nodeId, next);
33259
+ if (voteEvent) eventBus.emit(`UI:${voteEvent}`, { nodeId, vote: next });
33118
33260
  },
33119
- [node.id, onVote, voteEvent, eventBus]
33261
+ [nodeId, onVote, voteEvent, eventBus]
33120
33262
  );
33121
33263
  const handleReply = useCallback(() => {
33122
- onReply?.(node.id);
33264
+ onReply?.(nodeId);
33123
33265
  setReplyOpen((open) => !open);
33124
- }, [node.id, onReply]);
33266
+ }, [nodeId, onReply]);
33125
33267
  const handleSubmitReply = useCallback(() => {
33126
- const content = draft.trim();
33127
- if (!content) return;
33128
- if (replyEvent) eventBus.emit(`UI:${replyEvent}`, { parentNodeId: node.id, content });
33268
+ const text = draft.trim();
33269
+ if (!text) return;
33270
+ if (replyEvent) eventBus.emit(`UI:${replyEvent}`, { parentNodeId: nodeId, content: text });
33129
33271
  setDraft("");
33130
33272
  setReplyOpen(false);
33131
- }, [node.id, draft, replyEvent, eventBus]);
33273
+ }, [nodeId, draft, replyEvent, eventBus]);
33132
33274
  const handleCancelReply = useCallback(() => {
33133
33275
  setDraft("");
33134
33276
  setReplyOpen(false);
33135
33277
  }, []);
33136
33278
  const handleFlag = useCallback(() => {
33137
- onFlag?.(node.id);
33138
- if (flagEvent) eventBus.emit(`UI:${flagEvent}`, { nodeId: node.id });
33139
- }, [node.id, onFlag, flagEvent, eventBus]);
33279
+ onFlag?.(nodeId);
33280
+ if (flagEvent) eventBus.emit(`UI:${flagEvent}`, { nodeId });
33281
+ }, [nodeId, onFlag, flagEvent, eventBus]);
33140
33282
  const handleContinue = useCallback(() => {
33141
- onContinueThread?.(node.id);
33142
- if (continueThreadEvent) eventBus.emit(`UI:${continueThreadEvent}`, { nodeId: node.id });
33143
- }, [node.id, onContinueThread, continueThreadEvent, eventBus]);
33283
+ onContinueThread?.(nodeId);
33284
+ if (continueThreadEvent) eventBus.emit(`UI:${continueThreadEvent}`, { nodeId });
33285
+ }, [nodeId, onContinueThread, continueThreadEvent, eventBus]);
33144
33286
  const handleToggle = useCallback(() => {
33145
- toggleCollapse(node.id);
33146
- }, [node.id, toggleCollapse]);
33287
+ toggleCollapse(nodeId);
33288
+ }, [nodeId, toggleCollapse]);
33147
33289
  return /* @__PURE__ */ jsxs(Box, { className: "flex flex-row gap-2 items-stretch min-w-0", children: [
33148
33290
  /* @__PURE__ */ jsxs(Box, { className: "flex flex-col items-center flex-shrink-0 w-6", children: [
33149
33291
  hasReplies ? /* @__PURE__ */ jsx(
@@ -33175,25 +33317,25 @@ var init_ReplyTree = __esm({
33175
33317
  /* @__PURE__ */ jsx(
33176
33318
  Avatar,
33177
33319
  {
33178
- src: node.authorAvatarUrl,
33179
- name: node.authorName,
33320
+ src: authorAvatarUrl,
33321
+ name: authorName,
33180
33322
  size: "sm"
33181
33323
  }
33182
33324
  ),
33183
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "semibold", children: node.authorName }),
33184
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: node.postedAt })
33325
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "semibold", children: authorName }),
33326
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: postedAt })
33185
33327
  ] }),
33186
- /* @__PURE__ */ jsx(Typography, { variant: "body", className: "whitespace-pre-wrap break-words", children: node.content }),
33328
+ /* @__PURE__ */ jsx(Typography, { variant: "body", className: "whitespace-pre-wrap break-words", children: content }),
33187
33329
  showActions && /* @__PURE__ */ jsxs(Box, { className: "flex flex-row gap-2 items-center", children: [
33188
33330
  /* @__PURE__ */ jsx(
33189
33331
  VoteStack,
33190
33332
  {
33191
- count: node.voteCount ?? 0,
33192
- userVote: node.userVote ?? null,
33333
+ count: voteCount ?? 0,
33334
+ userVote: userVote ?? null,
33193
33335
  onVote: handleVote,
33194
33336
  size: "sm",
33195
33337
  variant: "horizontal",
33196
- label: t("replyTree.voteOnReplyBy", { author: node.authorName })
33338
+ label: t("replyTree.voteOnReplyBy", { author: authorName })
33197
33339
  }
33198
33340
  ),
33199
33341
  /* @__PURE__ */ jsx(
@@ -33203,7 +33345,7 @@ var init_ReplyTree = __esm({
33203
33345
  size: "sm",
33204
33346
  leftIcon: "message-square",
33205
33347
  onClick: handleReply,
33206
- "aria-label": t("replyTree.replyTo", { author: node.authorName }),
33348
+ "aria-label": t("replyTree.replyTo", { author: authorName }),
33207
33349
  children: t("replyTree.reply")
33208
33350
  }
33209
33351
  ),
@@ -33214,7 +33356,7 @@ var init_ReplyTree = __esm({
33214
33356
  size: "sm",
33215
33357
  leftIcon: "flag",
33216
33358
  onClick: handleFlag,
33217
- "aria-label": t("replyTree.flagReplyBy", { author: node.authorName }),
33359
+ "aria-label": t("replyTree.flagReplyBy", { author: authorName }),
33218
33360
  children: t("replyTree.flag")
33219
33361
  }
33220
33362
  )
@@ -33226,9 +33368,9 @@ var init_ReplyTree = __esm({
33226
33368
  inputType: "textarea",
33227
33369
  rows: 2,
33228
33370
  value: draft,
33229
- placeholder: t("replyTree.replyToPlaceholder", { author: node.authorName }),
33371
+ placeholder: t("replyTree.replyToPlaceholder", { author: authorName }),
33230
33372
  onChange: (e) => setDraft(e.target.value),
33231
- "aria-label": t("replyTree.replyTo", { author: node.authorName })
33373
+ "aria-label": t("replyTree.replyTo", { author: authorName })
33232
33374
  }
33233
33375
  ),
33234
33376
  /* @__PURE__ */ jsxs(Box, { className: "flex flex-row gap-2 items-center", children: [
@@ -33259,7 +33401,7 @@ var init_ReplyTree = __esm({
33259
33401
  ),
33260
33402
  children: t("replyTree.continueThread")
33261
33403
  }
33262
- ) : /* @__PURE__ */ jsx(Box, { className: "flex flex-col gap-2 mt-1", children: node.replies.map((child) => /* @__PURE__ */ jsx(
33404
+ ) : /* @__PURE__ */ jsx(Box, { className: "flex flex-col gap-2 mt-1", children: replies.map((child) => /* @__PURE__ */ jsx(
33263
33405
  ReplyTreeNode,
33264
33406
  {
33265
33407
  node: child,
@@ -35579,8 +35721,8 @@ var init_WizardContainer = __esm({
35579
35721
  return void 0;
35580
35722
  if (typeof controlledStep === "number") return controlledStep;
35581
35723
  if (typeof controlledStep === "string") return parseInt(controlledStep, 10);
35582
- const num = Number(controlledStep);
35583
- return isNaN(num) ? void 0 : num;
35724
+ const num2 = Number(controlledStep);
35725
+ return isNaN(num2) ? void 0 : num2;
35584
35726
  })();
35585
35727
  const currentStep = normalizedControlledStep !== void 0 ? normalizedControlledStep : internalStep;
35586
35728
  const totalSteps = steps.length;
@@ -37285,7 +37427,7 @@ function DebuggerBoard({
37285
37427
  }) {
37286
37428
  const { emit } = useEventBus();
37287
37429
  const { t } = useTranslate();
37288
- const resolved = Array.isArray(entity) ? entity[0] : entity;
37430
+ const resolved = boardEntity(entity);
37289
37431
  const [flaggedLines, setFlaggedLines] = useState(/* @__PURE__ */ new Set());
37290
37432
  const [headerError, setHeaderError] = useState(false);
37291
37433
  const [submitted, setSubmitted] = useState(false);
@@ -37303,7 +37445,7 @@ function DebuggerBoard({
37303
37445
  return next;
37304
37446
  });
37305
37447
  };
37306
- const lines = resolved?.lines ?? [];
37448
+ const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
37307
37449
  const bugLines = lines.filter((l) => l.isBug);
37308
37450
  const correctFlags = lines.filter((l) => l.isBug && flaggedLines.has(l.id));
37309
37451
  const falseFlags = lines.filter((l) => !l.isBug && flaggedLines.has(l.id));
@@ -37318,7 +37460,7 @@ function DebuggerBoard({
37318
37460
  }, [correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
37319
37461
  const handleReset = () => {
37320
37462
  setSubmitted(false);
37321
- if (attempts >= 2 && resolved?.hint) {
37463
+ if (attempts >= 2 && str(resolved?.hint)) {
37322
37464
  setShowHint(true);
37323
37465
  }
37324
37466
  };
@@ -37329,24 +37471,28 @@ function DebuggerBoard({
37329
37471
  setShowHint(false);
37330
37472
  };
37331
37473
  if (!resolved) return null;
37474
+ const theme = resolved.theme ?? void 0;
37475
+ const themeBackground = theme?.background;
37476
+ const headerImage = str(resolved.headerImage);
37477
+ const hint = str(resolved.hint);
37332
37478
  return /* @__PURE__ */ jsx(
37333
37479
  Box,
37334
37480
  {
37335
37481
  className,
37336
37482
  style: {
37337
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
37483
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
37338
37484
  backgroundSize: "cover",
37339
37485
  backgroundPosition: "center"
37340
37486
  },
37341
37487
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
37342
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
37488
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
37343
37489
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
37344
37490
  /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
37345
37491
  /* @__PURE__ */ jsx(Icon, { icon: Bug, size: "sm" }),
37346
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title })
37492
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) })
37347
37493
  ] }),
37348
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description }),
37349
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(resolved.bugCount) }) })
37494
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) }),
37495
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(num(resolved.bugCount)) }) })
37350
37496
  ] }) }),
37351
37497
  /* @__PURE__ */ jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsx(VStack, { gap: "none", children: lines.map((line, i) => {
37352
37498
  const isFlagged = flaggedLines.has(line.id);
@@ -37378,7 +37524,7 @@ function DebuggerBoard({
37378
37524
  );
37379
37525
  }) }) }),
37380
37526
  submitted && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
37381
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("debugger.allFound") : `${correctFlags.length}/${bugLines.length} ${t("debugger.bugsFound")}` }),
37527
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("debugger.allFound") : `${correctFlags.length}/${bugLines.length} ${t("debugger.bugsFound")}` }),
37382
37528
  bugLines.map((line) => /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "start", children: [
37383
37529
  /* @__PURE__ */ jsx(
37384
37530
  Icon,
@@ -37394,7 +37540,7 @@ function DebuggerBoard({
37394
37540
  ] })
37395
37541
  ] }, line.id))
37396
37542
  ] }) }),
37397
- showHint && resolved.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
37543
+ showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
37398
37544
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
37399
37545
  !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.size === 0, children: [
37400
37546
  /* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
@@ -37413,6 +37559,7 @@ var init_DebuggerBoard = __esm({
37413
37559
  "components/game/organisms/puzzles/debugger/DebuggerBoard.tsx"() {
37414
37560
  init_atoms2();
37415
37561
  init_useEventBus();
37562
+ init_boardEntity();
37416
37563
  DebuggerBoard.displayName = "DebuggerBoard";
37417
37564
  }
37418
37565
  });
@@ -37450,7 +37597,7 @@ function getBadgeVariant(fieldName, value) {
37450
37597
  return "default";
37451
37598
  }
37452
37599
  function formatFieldLabel(fieldName) {
37453
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase());
37600
+ return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str2) => str2.toUpperCase());
37454
37601
  }
37455
37602
  function formatFieldValue(value, fieldName) {
37456
37603
  if (typeof value === "number") {
@@ -37469,26 +37616,26 @@ function formatFieldValue(value, fieldName) {
37469
37616
  }
37470
37617
  function renderRichFieldValue(value, fieldName, fieldType) {
37471
37618
  if (value === void 0 || value === null) return "\u2014";
37472
- const str = String(value);
37619
+ const str2 = String(value);
37473
37620
  switch (fieldType) {
37474
37621
  case "image":
37475
37622
  case "url": {
37476
- if (str.match(/\.(png|jpe?g|gif|svg|webp|avif)(\?|$)/i) || str.startsWith("data:image/")) {
37623
+ if (str2.match(/\.(png|jpe?g|gif|svg|webp|avif)(\?|$)/i) || str2.startsWith("data:image/")) {
37477
37624
  return /* @__PURE__ */ jsx(Box, { className: "mt-1 max-w-full", children: /* @__PURE__ */ jsx(
37478
37625
  "img",
37479
37626
  {
37480
- src: str,
37627
+ src: str2,
37481
37628
  alt: formatFieldLabel(fieldName),
37482
37629
  className: "max-w-full max-h-64 rounded-md object-contain",
37483
37630
  loading: "lazy"
37484
37631
  }
37485
37632
  ) });
37486
37633
  }
37487
- return str;
37634
+ return str2;
37488
37635
  }
37489
37636
  case "markdown":
37490
37637
  case "richtext":
37491
- return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(Typography, { variant: "body", className: "break-words", children: str }), children: /* @__PURE__ */ jsx(
37638
+ return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(Typography, { variant: "body", className: "break-words", children: str2 }), children: /* @__PURE__ */ jsx(
37492
37639
  Box,
37493
37640
  {
37494
37641
  className: "prose prose-sm max-w-none",
@@ -37506,11 +37653,11 @@ function renderRichFieldValue(value, fieldName, fieldType) {
37506
37653
  "--tw-prose-th-borders": "var(--color-border)",
37507
37654
  "--tw-prose-td-borders": "var(--color-border)"
37508
37655
  },
37509
- children: /* @__PURE__ */ jsx(ReactMarkdown2, { children: str })
37656
+ children: /* @__PURE__ */ jsx(ReactMarkdown2, { children: str2 })
37510
37657
  }
37511
37658
  ) });
37512
37659
  case "code":
37513
- return /* @__PURE__ */ jsx(Box, { className: "mt-1 rounded-md bg-muted p-3 overflow-x-auto", children: /* @__PURE__ */ jsx("pre", { className: "text-sm font-mono whitespace-pre-wrap break-words m-0", children: /* @__PURE__ */ jsx("code", { children: str }) }) });
37660
+ return /* @__PURE__ */ jsx(Box, { className: "mt-1 rounded-md bg-muted p-3 overflow-x-auto", children: /* @__PURE__ */ jsx("pre", { className: "text-sm font-mono whitespace-pre-wrap break-words m-0", children: /* @__PURE__ */ jsx("code", { children: str2 }) }) });
37514
37661
  case "html":
37515
37662
  return /* @__PURE__ */ jsx(
37516
37663
  Box,
@@ -37530,12 +37677,12 @@ function renderRichFieldValue(value, fieldName, fieldType) {
37530
37677
  "--tw-prose-th-borders": "var(--color-border)",
37531
37678
  "--tw-prose-td-borders": "var(--color-border)"
37532
37679
  },
37533
- children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: str })
37680
+ children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: str2 })
37534
37681
  }
37535
37682
  );
37536
37683
  case "date":
37537
37684
  case "datetime": {
37538
- const d = new Date(str);
37685
+ const d = new Date(str2);
37539
37686
  if (!isNaN(d.getTime())) {
37540
37687
  return d.toLocaleDateString(void 0, {
37541
37688
  year: "numeric",
@@ -37544,7 +37691,7 @@ function renderRichFieldValue(value, fieldName, fieldType) {
37544
37691
  ...fieldType === "datetime" ? { hour: "2-digit", minute: "2-digit" } : {}
37545
37692
  });
37546
37693
  }
37547
- return str;
37694
+ return str2;
37548
37695
  }
37549
37696
  default:
37550
37697
  return formatFieldValue(value, fieldName);
@@ -38222,6 +38369,40 @@ var init_RuleEditor = __esm({
38222
38369
  RuleEditor.displayName = "RuleEditor";
38223
38370
  }
38224
38371
  });
38372
+
38373
+ // components/game/organisms/puzzles/event-handler/puzzleObject.ts
38374
+ function objId(o) {
38375
+ return o.id == null ? "" : String(o.id);
38376
+ }
38377
+ function objName(o) {
38378
+ return o.name == null ? "" : String(o.name);
38379
+ }
38380
+ function objIcon(o) {
38381
+ return o.icon == null ? "" : String(o.icon);
38382
+ }
38383
+ function objStates(o) {
38384
+ return Array.isArray(o.states) ? o.states : [];
38385
+ }
38386
+ function objCurrentState(o) {
38387
+ return o.currentState == null ? "" : String(o.currentState);
38388
+ }
38389
+ function objAvailableEvents(o) {
38390
+ return Array.isArray(o.availableEvents) ? o.availableEvents : [];
38391
+ }
38392
+ function objAvailableActions(o) {
38393
+ return Array.isArray(o.availableActions) ? o.availableActions : [];
38394
+ }
38395
+ function objRules(o) {
38396
+ return Array.isArray(o.rules) ? o.rules : [];
38397
+ }
38398
+ function objMaxRules(o) {
38399
+ const n = Number(o.maxRules);
38400
+ return Number.isFinite(n) && n > 0 ? n : 3;
38401
+ }
38402
+ var init_puzzleObject = __esm({
38403
+ "components/game/organisms/puzzles/event-handler/puzzleObject.ts"() {
38404
+ }
38405
+ });
38225
38406
  function ObjectRulePanel({
38226
38407
  object,
38227
38408
  onRulesChange,
@@ -38229,55 +38410,63 @@ function ObjectRulePanel({
38229
38410
  className
38230
38411
  }) {
38231
38412
  const { t } = useTranslate();
38232
- const maxRules = object.maxRules || 3;
38233
- const canAdd = object.rules.length < maxRules;
38413
+ const id = objId(object);
38414
+ const name = objName(object);
38415
+ const icon = objIcon(object);
38416
+ const states = objStates(object);
38417
+ const currentState = objCurrentState(object);
38418
+ const availableEvents = objAvailableEvents(object);
38419
+ const availableActions = objAvailableActions(object);
38420
+ const rules = objRules(object);
38421
+ const maxRules = objMaxRules(object);
38422
+ const canAdd = rules.length < maxRules;
38234
38423
  const handleRuleChange = useCallback((index, updatedRule) => {
38235
- const newRules = [...object.rules];
38424
+ const newRules = [...rules];
38236
38425
  newRules[index] = updatedRule;
38237
- onRulesChange(object.id, newRules);
38238
- }, [object.id, object.rules, onRulesChange]);
38426
+ onRulesChange(id, newRules);
38427
+ }, [id, rules, onRulesChange]);
38239
38428
  const handleRuleRemove = useCallback((index) => {
38240
- const newRules = object.rules.filter((_, i) => i !== index);
38241
- onRulesChange(object.id, newRules);
38242
- }, [object.id, object.rules, onRulesChange]);
38429
+ const newRules = rules.filter((_, i) => i !== index);
38430
+ onRulesChange(id, newRules);
38431
+ }, [id, rules, onRulesChange]);
38243
38432
  const handleAddRule = useCallback(() => {
38244
38433
  if (!canAdd || disabled) return;
38245
- const firstEvent = object.availableEvents[0]?.value || "";
38246
- const firstAction = object.availableActions[0]?.value || "";
38434
+ const firstEvent = availableEvents[0]?.value || "";
38435
+ const firstAction = availableActions[0]?.value || "";
38247
38436
  const newRule = {
38248
38437
  id: `rule-${nextRuleId++}`,
38249
38438
  whenEvent: firstEvent,
38250
38439
  thenAction: firstAction
38251
38440
  };
38252
- onRulesChange(object.id, [...object.rules, newRule]);
38253
- }, [canAdd, disabled, object, onRulesChange]);
38441
+ onRulesChange(id, [...rules, newRule]);
38442
+ }, [canAdd, disabled, id, rules, availableEvents, availableActions, onRulesChange]);
38254
38443
  const machine = {
38255
- name: object.name,
38256
- states: object.states,
38257
- currentState: object.currentState,
38258
- transitions: object.rules.map((r) => ({
38259
- from: object.currentState,
38260
- to: object.states.find((s) => s !== object.currentState) || object.currentState,
38444
+ name,
38445
+ states,
38446
+ currentState,
38447
+ transitions: rules.map((r) => ({
38448
+ from: currentState,
38449
+ to: states.find((s) => s !== currentState) || currentState,
38261
38450
  event: r.whenEvent
38262
38451
  }))
38263
38452
  };
38264
38453
  return /* @__PURE__ */ jsxs(VStack, { className: cn("p-4 rounded-lg bg-card border border-border", className), gap: "sm", children: [
38265
38454
  /* @__PURE__ */ jsxs(HStack, { className: "items-center", gap: "sm", children: [
38266
- /* @__PURE__ */ jsx(Typography, { variant: "h5", children: object.icon }),
38455
+ /* @__PURE__ */ jsx(Typography, { variant: "h5", children: icon }),
38267
38456
  /* @__PURE__ */ jsxs(VStack, { gap: "none", children: [
38268
- /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-bold", children: object.name }),
38269
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("eventHandler.state") + ": " + object.currentState })
38457
+ /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-bold", children: name }),
38458
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("eventHandler.state") + ": " + currentState })
38270
38459
  ] })
38271
38460
  ] }),
38272
38461
  /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" }),
38273
38462
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
38274
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.rules", { count: object.rules.length, max: maxRules }) + ":" }),
38275
- object.rules.map((rule, i) => /* @__PURE__ */ jsx(
38463
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.rules", { count: rules.length, max: maxRules }) + ":" }),
38464
+ rules.map((rule, i) => /* @__PURE__ */ jsx(
38276
38465
  RuleEditor,
38277
38466
  {
38278
38467
  rule,
38279
- availableEvents: object.availableEvents,
38280
- availableActions: object.availableActions,
38468
+ availableEvents,
38469
+ availableActions,
38281
38470
  onChange: (r) => handleRuleChange(i, r),
38282
38471
  onRemove: () => handleRuleRemove(i),
38283
38472
  disabled
@@ -38295,6 +38484,7 @@ var init_ObjectRulePanel = __esm({
38295
38484
  init_cn();
38296
38485
  init_TraitStateViewer();
38297
38486
  init_RuleEditor();
38487
+ init_puzzleObject();
38298
38488
  nextRuleId = 1;
38299
38489
  ObjectRulePanel.displayName = "ObjectRulePanel";
38300
38490
  }
@@ -38361,11 +38551,11 @@ function EventHandlerBoard({
38361
38551
  }) {
38362
38552
  const { emit } = useEventBus();
38363
38553
  const { t } = useTranslate();
38364
- const resolved = Array.isArray(entity) ? entity[0] : entity;
38365
- const entityObjects = resolved?.objects ?? [];
38366
- const [objects, setObjects] = useState(entityObjects);
38554
+ const resolved = boardEntity(entity);
38555
+ const entityObjects = rows(resolved?.objects);
38556
+ const [objects, setObjects] = useState(() => [...entityObjects]);
38367
38557
  const [selectedObjectId, setSelectedObjectId] = useState(
38368
- entityObjects[0]?.id || null
38558
+ entityObjects[0] ? objId(entityObjects[0]) : null
38369
38559
  );
38370
38560
  const [headerError, setHeaderError] = useState(false);
38371
38561
  const [playState, setPlayState] = useState("editing");
@@ -38376,10 +38566,10 @@ function EventHandlerBoard({
38376
38566
  useEffect(() => () => {
38377
38567
  if (timerRef.current) clearTimeout(timerRef.current);
38378
38568
  }, []);
38379
- const selectedObject = objects.find((o) => o.id === selectedObjectId) || null;
38569
+ const selectedObject = objects.find((o) => objId(o) === selectedObjectId) || null;
38380
38570
  const handleRulesChange = useCallback((objectId, rules) => {
38381
38571
  setObjects((prev) => prev.map(
38382
- (o) => o.id === objectId ? { ...o, rules } : o
38572
+ (o) => objId(o) === objectId ? { ...o, rules } : o
38383
38573
  ));
38384
38574
  }, []);
38385
38575
  const addLogEntry = useCallback((icon, message, status = "done") => {
@@ -38393,11 +38583,12 @@ function EventHandlerBoard({
38393
38583
  setEventLog([]);
38394
38584
  const allRules = [];
38395
38585
  objects.forEach((obj) => {
38396
- obj.rules.forEach((rule) => {
38586
+ objRules(obj).forEach((rule) => {
38397
38587
  allRules.push({ object: obj, rule });
38398
38588
  });
38399
38589
  });
38400
- const triggers = resolved?.triggerEvents || [];
38590
+ const triggers = Array.isArray(resolved?.triggerEvents) ? resolved.triggerEvents : [];
38591
+ const goalEvent = str(resolved?.goalEvent);
38401
38592
  const eventQueue = [...triggers];
38402
38593
  const firedEvents = /* @__PURE__ */ new Set();
38403
38594
  let stepIdx = 0;
@@ -38426,14 +38617,14 @@ function EventHandlerBoard({
38426
38617
  addLogEntry("\u26A1", t("eventHandler.noListeners", { event: currentEvent }), "done");
38427
38618
  } else {
38428
38619
  matching.forEach(({ object, rule }) => {
38429
- addLogEntry(object.icon, t("eventHandler.heardEvent", { object: object.name, event: currentEvent, action: rule.thenAction }), "done");
38620
+ addLogEntry(objIcon(object), t("eventHandler.heardEvent", { object: objName(object), event: currentEvent, action: rule.thenAction }), "done");
38430
38621
  eventQueue.push(rule.thenAction);
38431
- if (rule.thenAction === resolved?.goalEvent) {
38622
+ if (rule.thenAction === goalEvent) {
38432
38623
  goalReached = true;
38433
38624
  }
38434
38625
  });
38435
38626
  }
38436
- if (currentEvent === resolved?.goalEvent) {
38627
+ if (currentEvent === goalEvent) {
38437
38628
  goalReached = true;
38438
38629
  }
38439
38630
  stepIdx++;
@@ -38451,65 +38642,75 @@ function EventHandlerBoard({
38451
38642
  }, []);
38452
38643
  const handleReset = useCallback(() => {
38453
38644
  if (timerRef.current) clearTimeout(timerRef.current);
38454
- setObjects(resolved?.objects ?? []);
38645
+ const resetObjects = rows(resolved?.objects);
38646
+ setObjects([...resetObjects]);
38455
38647
  setPlayState("editing");
38456
38648
  setEventLog([]);
38457
- setSelectedObjectId((resolved?.objects ?? [])[0]?.id || null);
38649
+ setSelectedObjectId(resetObjects[0] ? objId(resetObjects[0]) : null);
38458
38650
  setAttempts(0);
38459
38651
  }, [resolved?.objects]);
38460
38652
  if (!resolved) return null;
38461
38653
  const objectViewers = objects.map((obj) => {
38654
+ const states = objStates(obj);
38655
+ const currentState = objCurrentState(obj);
38462
38656
  const machine = {
38463
- name: obj.name,
38464
- states: obj.states,
38465
- currentState: obj.currentState,
38466
- transitions: obj.rules.map((r) => ({
38467
- from: obj.currentState,
38468
- to: obj.states.find((s) => s !== obj.currentState) || obj.currentState,
38657
+ name: objName(obj),
38658
+ states,
38659
+ currentState,
38660
+ transitions: objRules(obj).map((r) => ({
38661
+ from: currentState,
38662
+ to: states.find((s) => s !== currentState) || currentState,
38469
38663
  event: r.whenEvent
38470
38664
  }))
38471
38665
  };
38472
38666
  return { obj, machine };
38473
38667
  });
38474
- const showHint = attempts >= 3 && resolved.hint;
38668
+ const hint = str(resolved.hint);
38669
+ const showHint = attempts >= 3 && hint;
38670
+ const theme = resolved.theme ?? void 0;
38671
+ const themeBackground = theme?.background;
38672
+ const headerImage = str(resolved.headerImage);
38475
38673
  const encourageKey = ENCOURAGEMENT_KEYS[Math.min(attempts - 1, ENCOURAGEMENT_KEYS.length - 1)] ?? ENCOURAGEMENT_KEYS[0];
38476
38674
  return /* @__PURE__ */ jsxs(
38477
38675
  VStack,
38478
38676
  {
38479
38677
  className: cn("p-4 gap-6", className),
38480
38678
  style: {
38481
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
38679
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
38482
38680
  backgroundSize: "cover",
38483
38681
  backgroundPosition: "center"
38484
38682
  },
38485
38683
  children: [
38486
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
38684
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
38487
38685
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
38488
- /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
38489
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description }),
38686
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
38687
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) }),
38490
38688
  /* @__PURE__ */ jsxs(HStack, { className: "items-center p-2 rounded bg-primary/10 border border-primary/30", gap: "xs", children: [
38491
38689
  /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-primary font-bold", children: t("game.goal") + ":" }),
38492
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: resolved.goalCondition })
38690
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: str(resolved.goalCondition) })
38493
38691
  ] })
38494
38692
  ] }),
38495
38693
  /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
38496
38694
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.clickObject") + ":" }),
38497
- /* @__PURE__ */ jsx(HStack, { className: "flex-wrap", gap: "sm", children: objectViewers.map(({ obj, machine }) => /* @__PURE__ */ jsx(
38498
- Box,
38499
- {
38500
- className: cn(
38501
- "p-3 rounded-container border-2 cursor-pointer transition-all hover:scale-105",
38502
- selectedObjectId === obj.id ? "border-primary bg-primary/10" : "border-border bg-card hover:border-muted-foreground"
38503
- ),
38504
- onClick: () => setSelectedObjectId(obj.id),
38505
- children: /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "items-center min-w-[120px]", children: [
38506
- /* @__PURE__ */ jsx(Typography, { variant: "h5", children: obj.icon }),
38507
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground font-medium", children: obj.name }),
38508
- /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" })
38509
- ] })
38510
- },
38511
- obj.id
38512
- )) })
38695
+ /* @__PURE__ */ jsx(HStack, { className: "flex-wrap", gap: "sm", children: objectViewers.map(({ obj, machine }) => {
38696
+ const oid = objId(obj);
38697
+ return /* @__PURE__ */ jsx(
38698
+ Box,
38699
+ {
38700
+ className: cn(
38701
+ "p-3 rounded-container border-2 cursor-pointer transition-all hover:scale-105",
38702
+ selectedObjectId === oid ? "border-primary bg-primary/10" : "border-border bg-card hover:border-muted-foreground"
38703
+ ),
38704
+ onClick: () => setSelectedObjectId(oid),
38705
+ children: /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "items-center min-w-[120px]", children: [
38706
+ /* @__PURE__ */ jsx(Typography, { variant: "h5", children: objIcon(obj) }),
38707
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground font-medium", children: objName(obj) }),
38708
+ /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" })
38709
+ ] })
38710
+ },
38711
+ oid
38712
+ );
38713
+ }) })
38513
38714
  ] }),
38514
38715
  selectedObject && /* @__PURE__ */ jsx(
38515
38716
  ObjectRulePanel,
@@ -38520,12 +38721,12 @@ function EventHandlerBoard({
38520
38721
  }
38521
38722
  ),
38522
38723
  eventLog.length > 0 && /* @__PURE__ */ jsx(EventLog, { entries: eventLog }),
38523
- playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: resolved.successMessage || t("eventHandler.chainComplete") }) }),
38724
+ playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("eventHandler.chainComplete") }) }),
38524
38725
  playState === "fail" && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
38525
38726
  /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(encourageKey) }) }),
38526
38727
  showHint && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
38527
38728
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
38528
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
38729
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
38529
38730
  ] }) })
38530
38731
  ] }),
38531
38732
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
@@ -38553,6 +38754,8 @@ var init_EventHandlerBoard = __esm({
38553
38754
  init_TraitStateViewer();
38554
38755
  init_ObjectRulePanel();
38555
38756
  init_EventLog();
38757
+ init_puzzleObject();
38758
+ init_boardEntity();
38556
38759
  ENCOURAGEMENT_KEYS = [
38557
38760
  "puzzle.tryAgain1",
38558
38761
  "puzzle.tryAgain2",
@@ -38641,7 +38844,10 @@ var init_FeatureGridOrganism = __esm({
38641
38844
  );
38642
38845
  useCallback(
38643
38846
  (feature) => {
38644
- eventBus.emit("UI:FEATURE_CLICK", { id: feature.id, href: feature.href ?? "" });
38847
+ eventBus.emit("UI:FEATURE_CLICK", {
38848
+ id: String(feature.id ?? ""),
38849
+ href: String(feature.href ?? "")
38850
+ });
38645
38851
  },
38646
38852
  [eventBus]
38647
38853
  );
@@ -38651,14 +38857,17 @@ var init_FeatureGridOrganism = __esm({
38651
38857
  if (error) {
38652
38858
  return /* @__PURE__ */ jsx(ErrorState, { message: error.message, className });
38653
38859
  }
38654
- const featureCards = items.map((feature) => ({
38655
- icon: feature.icon,
38656
- title: feature.title,
38657
- description: feature.description,
38658
- href: feature.href,
38659
- linkLabel: feature.linkLabel,
38660
- variant: feature.href ? "interactive" : "bordered"
38661
- }));
38860
+ const featureCards = items.map((feature) => {
38861
+ const href = feature.href != null ? String(feature.href) : void 0;
38862
+ return {
38863
+ icon: feature.icon != null ? String(feature.icon) : void 0,
38864
+ title: String(feature.title ?? ""),
38865
+ description: String(feature.description ?? ""),
38866
+ href,
38867
+ linkLabel: feature.linkLabel != null ? String(feature.linkLabel) : void 0,
38868
+ variant: href ? "interactive" : "bordered"
38869
+ };
38870
+ });
38662
38871
  return /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
38663
38872
  (heading || subtitle) && /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
38664
38873
  heading && /* @__PURE__ */ jsx(Typography, { variant: "h2", align: "center", children: heading }),
@@ -39976,22 +40185,24 @@ var init_HeroOrganism = __esm({
39976
40185
  () => Array.isArray(entity) ? entity[0] : entity && typeof entity === "object" ? entity : void 0,
39977
40186
  [entity]
39978
40187
  );
40188
+ const primaryAction = resolved?.primaryAction;
40189
+ const secondaryAction = resolved?.secondaryAction;
39979
40190
  const handlePrimaryClick = useCallback(() => {
39980
- if (resolved?.primaryAction) {
40191
+ if (primaryAction) {
39981
40192
  eventBus.emit("UI:CTA_PRIMARY", {
39982
- label: resolved.primaryAction.label,
39983
- href: resolved.primaryAction.href
40193
+ label: String(primaryAction.label ?? ""),
40194
+ href: String(primaryAction.href ?? "")
39984
40195
  });
39985
40196
  }
39986
- }, [eventBus, resolved]);
40197
+ }, [eventBus, primaryAction]);
39987
40198
  const handleSecondaryClick = useCallback(() => {
39988
- if (resolved?.secondaryAction) {
40199
+ if (secondaryAction) {
39989
40200
  eventBus.emit("UI:CTA_SECONDARY", {
39990
- label: resolved.secondaryAction.label,
39991
- href: resolved.secondaryAction.href
40201
+ label: String(secondaryAction.label ?? ""),
40202
+ href: String(secondaryAction.href ?? "")
39992
40203
  });
39993
40204
  }
39994
- }, [eventBus, resolved]);
40205
+ }, [eventBus, secondaryAction]);
39995
40206
  if (isLoading) {
39996
40207
  return /* @__PURE__ */ jsx(LoadingState, { message: t("common.loading"), className });
39997
40208
  }
@@ -40001,17 +40212,19 @@ var init_HeroOrganism = __esm({
40001
40212
  if (!resolved) {
40002
40213
  return null;
40003
40214
  }
40215
+ const imageRaw = resolved.image;
40216
+ const image = imageRaw ? { src: String(imageRaw.src ?? ""), alt: String(imageRaw.alt ?? "") } : void 0;
40004
40217
  return /* @__PURE__ */ jsxs(
40005
40218
  HeroSection,
40006
40219
  {
40007
- tag: resolved.tag,
40008
- title: resolved.title,
40009
- titleAccent: resolved.titleAccent,
40010
- subtitle: resolved.subtitle,
40011
- primaryAction: resolved.primaryAction ? { label: resolved.primaryAction.label, href: resolved.primaryAction.href } : void 0,
40012
- secondaryAction: resolved.secondaryAction ? { label: resolved.secondaryAction.label, href: resolved.secondaryAction.href } : void 0,
40013
- installCommand: resolved.installCommand,
40014
- image: resolved.image,
40220
+ tag: resolved.tag != null ? String(resolved.tag) : void 0,
40221
+ title: String(resolved.title ?? ""),
40222
+ titleAccent: resolved.titleAccent != null ? String(resolved.titleAccent) : void 0,
40223
+ subtitle: String(resolved.subtitle ?? ""),
40224
+ primaryAction: primaryAction ? { label: String(primaryAction.label ?? ""), href: String(primaryAction.href ?? "") } : void 0,
40225
+ secondaryAction: secondaryAction ? { label: String(secondaryAction.label ?? ""), href: String(secondaryAction.href ?? "") } : void 0,
40226
+ installCommand: resolved.installCommand != null ? String(resolved.installCommand) : void 0,
40227
+ image,
40015
40228
  imagePosition: resolved.imagePosition,
40016
40229
  background: resolved.background,
40017
40230
  className: cn(className),
@@ -40020,8 +40233,8 @@ var init_HeroOrganism = __esm({
40020
40233
  /* @__PURE__ */ jsx(
40021
40234
  _HeroClickInterceptor,
40022
40235
  {
40023
- hasPrimary: !!resolved.primaryAction,
40024
- hasSecondary: !!resolved.secondaryAction,
40236
+ hasPrimary: !!primaryAction,
40237
+ hasSecondary: !!secondaryAction,
40025
40238
  onPrimaryClick: handlePrimaryClick,
40026
40239
  onSecondaryClick: handleSecondaryClick
40027
40240
  }
@@ -40250,7 +40463,7 @@ function formatValue3(value, fieldName) {
40250
40463
  return String(value);
40251
40464
  }
40252
40465
  function formatFieldLabel2(fieldName) {
40253
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).replace(/Id$/, "").trim();
40466
+ return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str2) => str2.toUpperCase()).replace(/Id$/, "").trim();
40254
40467
  }
40255
40468
  var STATUS_STYLES2, StatusBadge, ProgressIndicator, List3;
40256
40469
  var init_List = __esm({
@@ -41097,20 +41310,22 @@ function NegotiatorBoard({
41097
41310
  }) {
41098
41311
  const { emit } = useEventBus();
41099
41312
  const { t } = useTranslate();
41100
- const resolved = Array.isArray(entity) ? entity[0] : entity;
41313
+ const resolved = boardEntity(entity);
41101
41314
  const [history, setHistory] = useState([]);
41102
41315
  const [headerError, setHeaderError] = useState(false);
41103
41316
  const [showHint, setShowHint] = useState(false);
41317
+ const totalRounds = num(resolved?.totalRounds);
41318
+ const targetScore = num(resolved?.targetScore);
41104
41319
  const currentRound = history.length;
41105
- const isComplete = currentRound >= (resolved?.totalRounds ?? 0);
41320
+ const isComplete = currentRound >= totalRounds;
41106
41321
  const playerTotal = history.reduce((s, r) => s + r.playerPayoff, 0);
41107
41322
  const opponentTotal = history.reduce((s, r) => s + r.opponentPayoff, 0);
41108
- const won = isComplete && playerTotal >= (resolved?.targetScore ?? 0);
41109
- const actions = resolved?.actions ?? [];
41110
- const payoffMatrix = resolved?.payoffMatrix ?? [];
41323
+ const won = isComplete && playerTotal >= targetScore;
41324
+ const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
41325
+ const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
41111
41326
  const handleAction = useCallback((actionId) => {
41112
41327
  if (isComplete) return;
41113
- const opponentAction = getOpponentAction(resolved?.opponentStrategy ?? "random", actions, history);
41328
+ const opponentAction = getOpponentAction(str(resolved?.opponentStrategy) || "random", actions, history);
41114
41329
  const payoff = payoffMatrix.find(
41115
41330
  (p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
41116
41331
  );
@@ -41123,42 +41338,46 @@ function NegotiatorBoard({
41123
41338
  };
41124
41339
  const newHistory = [...history, result];
41125
41340
  setHistory(newHistory);
41126
- if (newHistory.length >= (resolved?.totalRounds ?? 0)) {
41341
+ if (newHistory.length >= totalRounds) {
41127
41342
  const total = newHistory.reduce((s, r) => s + r.playerPayoff, 0);
41128
- if (total >= (resolved?.targetScore ?? 0)) {
41343
+ if (total >= targetScore) {
41129
41344
  emit(`UI:${completeEvent}`, { success: true, score: total });
41130
41345
  }
41131
- if (newHistory.length >= 3 && resolved?.hint) {
41346
+ if (newHistory.length >= 3 && str(resolved?.hint)) {
41132
41347
  setShowHint(true);
41133
41348
  }
41134
41349
  }
41135
- }, [isComplete, resolved, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
41350
+ }, [isComplete, resolved, totalRounds, targetScore, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
41136
41351
  const handleReset = () => {
41137
41352
  setHistory([]);
41138
41353
  setShowHint(false);
41139
41354
  };
41140
41355
  const getActionLabel = (id) => actions.find((a) => a.id === id)?.label ?? id;
41141
41356
  if (!resolved) return null;
41357
+ const theme = resolved.theme ?? void 0;
41358
+ const themeBackground = theme?.background;
41359
+ const headerImage = str(resolved.headerImage);
41360
+ const hint = str(resolved.hint);
41142
41361
  return /* @__PURE__ */ jsx(
41143
41362
  Box,
41144
41363
  {
41145
41364
  className,
41146
41365
  style: {
41147
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
41366
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
41148
41367
  backgroundSize: "cover",
41149
41368
  backgroundPosition: "center"
41150
41369
  },
41151
41370
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
41152
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
41371
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
41153
41372
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
41154
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
41155
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description }),
41373
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
41374
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) }),
41156
41375
  /* @__PURE__ */ jsxs(HStack, { gap: "md", children: [
41157
- /* @__PURE__ */ jsx(Badge, { size: "sm", children: t("negotiator.round", { current: String(currentRound), total: String(resolved.totalRounds) }) }),
41376
+ /* @__PURE__ */ jsx(Badge, { size: "sm", children: t("negotiator.round", { current: String(currentRound), total: String(totalRounds) }) }),
41158
41377
  /* @__PURE__ */ jsxs(Badge, { size: "sm", children: [
41159
41378
  t("negotiator.target"),
41160
41379
  ": ",
41161
- resolved.targetScore
41380
+ targetScore
41162
41381
  ] })
41163
41382
  ] })
41164
41383
  ] }) }),
@@ -41207,16 +41426,16 @@ function NegotiatorBoard({
41207
41426
  ] }) }),
41208
41427
  isComplete && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
41209
41428
  /* @__PURE__ */ jsx(Icon, { icon: CheckCircle, size: "lg", className: won ? "text-success" : "text-error" }),
41210
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: won ? resolved.successMessage ?? t("negotiator.success") : resolved.failMessage ?? t("negotiator.failed") }),
41429
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: won ? str(resolved.successMessage) || t("negotiator.success") : str(resolved.failMessage) || t("negotiator.failed") }),
41211
41430
  /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
41212
41431
  t("negotiator.finalScore"),
41213
41432
  ": ",
41214
41433
  playerTotal,
41215
41434
  "/",
41216
- resolved.targetScore
41435
+ targetScore
41217
41436
  ] })
41218
41437
  ] }) }),
41219
- showHint && resolved.hint && !won && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
41438
+ showHint && hint && !won && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
41220
41439
  isComplete && !won && /* @__PURE__ */ jsx(HStack, { justify: "center", children: /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleReset, children: t("negotiator.playAgain") }) })
41221
41440
  ] })
41222
41441
  }
@@ -41226,6 +41445,7 @@ var init_NegotiatorBoard = __esm({
41226
41445
  "components/game/organisms/puzzles/negotiator/NegotiatorBoard.tsx"() {
41227
41446
  init_atoms2();
41228
41447
  init_useEventBus();
41448
+ init_boardEntity();
41229
41449
  NegotiatorBoard.displayName = "NegotiatorBoard";
41230
41450
  }
41231
41451
  });
@@ -41261,13 +41481,13 @@ var init_PricingOrganism = __esm({
41261
41481
  return /* @__PURE__ */ jsx(ErrorState, { message: error.message, className });
41262
41482
  }
41263
41483
  const plans = items.map((plan) => ({
41264
- name: plan.name,
41265
- price: plan.price,
41266
- description: plan.description,
41267
- features: plan.features,
41268
- action: { label: plan.actionLabel, href: plan.actionHref },
41269
- highlighted: plan.highlighted,
41270
- badge: plan.badge
41484
+ name: String(plan.name ?? ""),
41485
+ price: String(plan.price ?? ""),
41486
+ description: plan.description != null ? String(plan.description) : void 0,
41487
+ features: (plan.features ?? []).map((f3) => String(f3)),
41488
+ action: { label: String(plan.actionLabel ?? ""), href: String(plan.actionHref ?? "") },
41489
+ highlighted: Boolean(plan.highlighted),
41490
+ badge: plan.badge != null ? String(plan.badge) : void 0
41271
41491
  }));
41272
41492
  return /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
41273
41493
  (heading || subtitle) && /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
@@ -43357,16 +43577,20 @@ function SequencerBoard({
43357
43577
  }) {
43358
43578
  const { emit } = useEventBus();
43359
43579
  const { t } = useTranslate();
43360
- const resolved = Array.isArray(entity) ? entity[0] : entity;
43580
+ const resolved = boardEntity(entity);
43581
+ const maxSlots = num(resolved?.maxSlots);
43582
+ const solutions = Array.isArray(resolved?.solutions) ? resolved.solutions : [];
43583
+ const availableActions = Array.isArray(resolved?.availableActions) ? resolved.availableActions : [];
43584
+ const allowDuplicates = resolved?.allowDuplicates !== false;
43361
43585
  const [headerError, setHeaderError] = useState(false);
43362
43586
  const [slots, setSlots] = useState(
43363
- () => Array.from({ length: resolved?.maxSlots ?? 0 }, () => void 0)
43587
+ () => Array.from({ length: maxSlots }, () => void 0)
43364
43588
  );
43365
43589
  const [playState, setPlayState] = useState("idle");
43366
43590
  const [currentStep, setCurrentStep] = useState(-1);
43367
43591
  const [attempts, setAttempts] = useState(0);
43368
43592
  const [slotFeedback, setSlotFeedback] = useState(
43369
- () => Array.from({ length: resolved?.maxSlots ?? 0 }, () => null)
43593
+ () => Array.from({ length: maxSlots }, () => null)
43370
43594
  );
43371
43595
  const timerRef = useRef(null);
43372
43596
  useEffect(() => () => {
@@ -43400,17 +43624,17 @@ function SequencerBoard({
43400
43624
  }, [emit]);
43401
43625
  const handleReset = useCallback(() => {
43402
43626
  if (timerRef.current) clearTimeout(timerRef.current);
43403
- setSlots(Array.from({ length: resolved?.maxSlots ?? 0 }, () => void 0));
43627
+ setSlots(Array.from({ length: maxSlots }, () => void 0));
43404
43628
  setPlayState("idle");
43405
43629
  setCurrentStep(-1);
43406
43630
  setAttempts(0);
43407
- setSlotFeedback(Array.from({ length: resolved?.maxSlots ?? 0 }, () => null));
43408
- }, [resolved?.maxSlots]);
43631
+ setSlotFeedback(Array.from({ length: maxSlots }, () => null));
43632
+ }, [maxSlots]);
43409
43633
  const filledSlots = slots.filter((s) => !!s);
43410
43634
  const canPlay = filledSlots.length > 0 && playState === "idle";
43411
43635
  const handlePlay = useCallback(() => {
43412
43636
  if (!canPlay) return;
43413
- setSlotFeedback(Array.from({ length: resolved?.maxSlots ?? 0 }, () => null));
43637
+ setSlotFeedback(Array.from({ length: maxSlots }, () => null));
43414
43638
  emit("UI:PLAY_SOUND", { key: "confirm" });
43415
43639
  const sequence = slots.map((s) => s?.id || "");
43416
43640
  if (playEvent) {
@@ -43421,10 +43645,10 @@ function SequencerBoard({
43421
43645
  let step = 0;
43422
43646
  const advance = () => {
43423
43647
  step++;
43424
- if (step >= (resolved?.maxSlots ?? 0)) {
43648
+ if (step >= maxSlots) {
43425
43649
  const playerSeq = slots.map((s) => s?.id);
43426
43650
  const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
43427
- const success = (resolved?.solutions ?? []).some(
43651
+ const success = solutions.some(
43428
43652
  (sol) => sol.length === playerIds.length && sol.every((id, i) => id === playerIds[i])
43429
43653
  );
43430
43654
  if (success) {
@@ -43436,7 +43660,7 @@ function SequencerBoard({
43436
43660
  }
43437
43661
  } else {
43438
43662
  setAttempts((prev) => prev + 1);
43439
- const feedback = computeSlotFeedback(playerSeq, resolved?.solutions ?? []);
43663
+ const feedback = computeSlotFeedback(playerSeq, solutions);
43440
43664
  setSlotFeedback(feedback);
43441
43665
  setPlayState("idle");
43442
43666
  setCurrentStep(-1);
@@ -43454,10 +43678,10 @@ function SequencerBoard({
43454
43678
  }
43455
43679
  };
43456
43680
  timerRef.current = setTimeout(advance, stepDurationMs);
43457
- }, [canPlay, slots, resolved?.maxSlots, resolved?.solutions, stepDurationMs, playEvent, completeEvent, emit]);
43681
+ }, [canPlay, slots, maxSlots, solutions, stepDurationMs, playEvent, completeEvent, emit]);
43458
43682
  const machine = {
43459
- name: resolved?.title ?? "",
43460
- description: resolved?.description ?? "",
43683
+ name: str(resolved?.title),
43684
+ description: str(resolved?.description),
43461
43685
  states: slots.map((s, i) => stepLabel(s, i)),
43462
43686
  currentState: currentStep >= 0 ? stepLabel(slots[currentStep], currentStep) : "__idle__",
43463
43687
  transitions: slots.slice(0, -1).map((s, i) => ({
@@ -43466,37 +43690,41 @@ function SequencerBoard({
43466
43690
  event: "NEXT"
43467
43691
  }))
43468
43692
  };
43469
- const usedIds = resolved?.allowDuplicates === false ? slots.filter(Boolean).map((s) => s?.id || "") : [];
43470
- const showHint = attempts >= 3 && !!resolved?.hint;
43693
+ const usedIds = !allowDuplicates ? slots.filter(Boolean).map((s) => s?.id || "") : [];
43694
+ const hint = str(resolved?.hint);
43695
+ const showHint = attempts >= 3 && !!hint;
43471
43696
  const hasFeedback = slotFeedback.some((f3) => f3 !== null);
43472
43697
  const correctCount = slotFeedback.filter((f3) => f3 === "correct").length;
43473
43698
  const encourageKey = ENCOURAGEMENT_KEYS2[Math.min(attempts - 1, ENCOURAGEMENT_KEYS2.length - 1)] ?? ENCOURAGEMENT_KEYS2[0];
43474
43699
  if (!resolved) return null;
43700
+ const theme = resolved.theme ?? void 0;
43701
+ const themeBackground = theme?.background;
43702
+ const headerImage = str(resolved.headerImage);
43475
43703
  return /* @__PURE__ */ jsxs(
43476
43704
  VStack,
43477
43705
  {
43478
43706
  className: cn("p-4 gap-6", className),
43479
43707
  style: {
43480
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
43708
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
43481
43709
  backgroundSize: "cover",
43482
43710
  backgroundPosition: "center"
43483
43711
  },
43484
43712
  children: [
43485
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
43713
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
43486
43714
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
43487
- /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
43488
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description })
43715
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
43716
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) })
43489
43717
  ] }),
43490
43718
  showHint && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
43491
43719
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
43492
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
43720
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
43493
43721
  ] }) }),
43494
43722
  filledSlots.length > 0 && /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "linear", size: "md" }),
43495
43723
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
43496
43724
  /* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between", children: [
43497
43725
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("sequencer.yourSequence") + ":" }),
43498
43726
  hasFeedback && playState === "idle" && /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
43499
- `${correctCount}/${resolved.maxSlots} `,
43727
+ `${correctCount}/${maxSlots} `,
43500
43728
  "\u2705"
43501
43729
  ] })
43502
43730
  ] }),
@@ -43504,7 +43732,7 @@ function SequencerBoard({
43504
43732
  SequenceBar,
43505
43733
  {
43506
43734
  slots,
43507
- maxSlots: resolved.maxSlots,
43735
+ maxSlots,
43508
43736
  onSlotDrop: handleSlotDrop,
43509
43737
  onSlotRemove: handleSlotRemove,
43510
43738
  playing: playState === "playing",
@@ -43518,15 +43746,15 @@ function SequencerBoard({
43518
43746
  playState !== "playing" && /* @__PURE__ */ jsx(
43519
43747
  ActionPalette,
43520
43748
  {
43521
- actions: resolved.availableActions,
43749
+ actions: availableActions,
43522
43750
  usedActionIds: usedIds,
43523
- allowDuplicates: resolved.allowDuplicates !== false,
43751
+ allowDuplicates,
43524
43752
  categoryColors,
43525
43753
  label: t("sequencer.dragActions")
43526
43754
  }
43527
43755
  ),
43528
43756
  hasFeedback && playState === "idle" && attempts > 0 && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: t(encourageKey) }) }),
43529
- playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: resolved.successMessage || t("sequencer.levelComplete") }) }),
43757
+ playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("sequencer.levelComplete") }) }),
43530
43758
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
43531
43759
  /* @__PURE__ */ jsx(
43532
43760
  Button,
@@ -43550,6 +43778,7 @@ var init_SequencerBoard = __esm({
43550
43778
  init_cn();
43551
43779
  init_useEventBus();
43552
43780
  init_TraitStateViewer();
43781
+ init_boardEntity();
43553
43782
  init_SequenceBar();
43554
43783
  init_ActionPalette();
43555
43784
  ENCOURAGEMENT_KEYS2 = [
@@ -43599,18 +43828,21 @@ var init_ShowcaseOrganism = __esm({
43599
43828
  heading && /* @__PURE__ */ jsx(Typography, { variant: "h2", align: "center", children: heading }),
43600
43829
  subtitle && /* @__PURE__ */ jsx(Typography, { variant: "body1", color: "muted", align: "center", className: "max-w-2xl", children: subtitle })
43601
43830
  ] }),
43602
- /* @__PURE__ */ jsx(SimpleGrid, { cols: columns, gap: "lg", children: items.map((item) => /* @__PURE__ */ jsx(
43603
- ShowcaseCard,
43604
- {
43605
- title: item.title,
43606
- description: item.description,
43607
- image: item.image,
43608
- href: item.href,
43609
- badge: item.badge,
43610
- accentColor: item.accentColor
43611
- },
43612
- item.id
43613
- )) })
43831
+ /* @__PURE__ */ jsx(SimpleGrid, { cols: columns, gap: "lg", children: items.map((item) => {
43832
+ const imageRaw = item.image;
43833
+ return /* @__PURE__ */ jsx(
43834
+ ShowcaseCard,
43835
+ {
43836
+ title: String(item.title ?? ""),
43837
+ description: item.description != null ? String(item.description) : void 0,
43838
+ image: { src: String(imageRaw?.src ?? ""), alt: String(imageRaw?.alt ?? "") },
43839
+ href: item.href != null ? String(item.href) : void 0,
43840
+ badge: item.badge != null ? String(item.badge) : void 0,
43841
+ accentColor: item.accentColor != null ? String(item.accentColor) : void 0
43842
+ },
43843
+ String(item.id ?? "")
43844
+ );
43845
+ }) })
43614
43846
  ] });
43615
43847
  };
43616
43848
  ShowcaseOrganism.displayName = "ShowcaseOrganism";
@@ -43978,8 +44210,8 @@ function SimulatorBoard({
43978
44210
  }) {
43979
44211
  const { emit } = useEventBus();
43980
44212
  const { t } = useTranslate();
43981
- const resolved = Array.isArray(entity) ? entity[0] : entity;
43982
- const parameters = resolved?.parameters ?? [];
44213
+ const resolved = boardEntity(entity);
44214
+ const parameters = Array.isArray(resolved?.parameters) ? resolved.parameters : [];
43983
44215
  const [values, setValues] = useState(() => {
43984
44216
  const init = {};
43985
44217
  for (const p2 of parameters) {
@@ -43993,15 +44225,15 @@ function SimulatorBoard({
43993
44225
  const [showHint, setShowHint] = useState(false);
43994
44226
  const computeOutput = useCallback((params) => {
43995
44227
  try {
43996
- const fn = new Function("params", `return (${resolved?.computeExpression})`);
44228
+ const fn = new Function("params", `return (${str(resolved?.computeExpression)})`);
43997
44229
  return fn(params);
43998
44230
  } catch {
43999
44231
  return 0;
44000
44232
  }
44001
44233
  }, [resolved?.computeExpression]);
44002
44234
  const output = useMemo(() => computeOutput(values) ?? 0, [computeOutput, values]);
44003
- const targetValue = resolved?.targetValue ?? 0;
44004
- const targetTolerance = resolved?.targetTolerance ?? 0;
44235
+ const targetValue = num(resolved?.targetValue);
44236
+ const targetTolerance = num(resolved?.targetTolerance);
44005
44237
  const isCorrect = Math.abs(output - targetValue) <= targetTolerance;
44006
44238
  const handleParameterChange = (id, value) => {
44007
44239
  if (submitted) return;
@@ -44016,7 +44248,7 @@ function SimulatorBoard({
44016
44248
  };
44017
44249
  const handleReset = () => {
44018
44250
  setSubmitted(false);
44019
- if (attempts >= 2 && resolved?.hint) {
44251
+ if (attempts >= 2 && str(resolved?.hint)) {
44020
44252
  setShowHint(true);
44021
44253
  }
44022
44254
  };
@@ -44031,20 +44263,26 @@ function SimulatorBoard({
44031
44263
  setShowHint(false);
44032
44264
  };
44033
44265
  if (!resolved) return null;
44266
+ const theme = resolved.theme ?? void 0;
44267
+ const themeBackground = theme?.background;
44268
+ const headerImage = str(resolved.headerImage);
44269
+ const hint = str(resolved.hint);
44270
+ const outputLabel = str(resolved.outputLabel);
44271
+ const outputUnit = str(resolved.outputUnit);
44034
44272
  return /* @__PURE__ */ jsx(
44035
44273
  Box,
44036
44274
  {
44037
44275
  className,
44038
44276
  style: {
44039
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
44277
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
44040
44278
  backgroundSize: "cover",
44041
44279
  backgroundPosition: "center"
44042
44280
  },
44043
44281
  children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
44044
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
44282
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
44045
44283
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
44046
- /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
44047
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.description })
44284
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
44285
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) })
44048
44286
  ] }) }),
44049
44287
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", children: [
44050
44288
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("simulator.parameters") }),
@@ -44085,28 +44323,28 @@ function SimulatorBoard({
44085
44323
  ] }, param.id))
44086
44324
  ] }) }),
44087
44325
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
44088
- /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: resolved.outputLabel }),
44326
+ /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: outputLabel }),
44089
44327
  /* @__PURE__ */ jsxs(Typography, { variant: "h3", weight: "bold", children: [
44090
44328
  output.toFixed(2),
44091
44329
  " ",
44092
- resolved.outputUnit
44330
+ outputUnit
44093
44331
  ] }),
44094
44332
  submitted && /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
44095
44333
  /* @__PURE__ */ jsx(Icon, { icon: isCorrect ? CheckCircle : XCircle, size: "sm", className: isCorrect ? "text-success" : "text-error" }),
44096
- /* @__PURE__ */ jsx(Typography, { variant: "body", className: isCorrect ? "text-success" : "text-error", children: isCorrect ? resolved.successMessage ?? t("simulator.correct") : resolved.failMessage ?? t("simulator.incorrect") })
44334
+ /* @__PURE__ */ jsx(Typography, { variant: "body", className: isCorrect ? "text-success" : "text-error", children: isCorrect ? str(resolved.successMessage) || t("simulator.correct") : str(resolved.failMessage) || t("simulator.incorrect") })
44097
44335
  ] }),
44098
44336
  /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
44099
44337
  t("simulator.target"),
44100
44338
  ": ",
44101
44339
  targetValue,
44102
44340
  " ",
44103
- resolved.outputUnit,
44341
+ outputUnit,
44104
44342
  " (\xB1",
44105
44343
  targetTolerance,
44106
44344
  ")"
44107
44345
  ] })
44108
44346
  ] }) }),
44109
- showHint && resolved.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: resolved.hint }) }),
44347
+ showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
44110
44348
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
44111
44349
  !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, children: [
44112
44350
  /* @__PURE__ */ jsx(Icon, { icon: Play, size: "sm" }),
@@ -44125,6 +44363,7 @@ var init_SimulatorBoard = __esm({
44125
44363
  "components/game/organisms/puzzles/simulator/SimulatorBoard.tsx"() {
44126
44364
  init_atoms2();
44127
44365
  init_useEventBus();
44366
+ init_boardEntity();
44128
44367
  SimulatorBoard.displayName = "SimulatorBoard";
44129
44368
  }
44130
44369
  });
@@ -44550,22 +44789,25 @@ function VariablePanel({
44550
44789
  return /* @__PURE__ */ jsxs(VStack, { className: cn("p-3 rounded-lg bg-card border border-border", className), gap: "sm", children: [
44551
44790
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("stateArchitect.variables", { name: entityName }) }),
44552
44791
  variables.map((v) => {
44553
- const max = v.max ?? 100;
44554
- const min = v.min ?? 0;
44555
- const pct = Math.round((v.value - min) / (max - min) * 100);
44792
+ const name = v.name == null ? "" : String(v.name);
44793
+ const value = numField(v.value);
44794
+ const max = numField(v.max, 100);
44795
+ const min = numField(v.min, 0);
44796
+ const unit = v.unit == null ? "" : String(v.unit);
44797
+ const pct = Math.round((value - min) / (max - min) * 100);
44556
44798
  const isHigh = pct > 80;
44557
44799
  const isLow = pct < 20;
44558
44800
  return /* @__PURE__ */ jsxs(VStack, { gap: "none", children: [
44559
44801
  /* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between", children: [
44560
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground font-medium", children: v.name }),
44802
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground font-medium", children: name }),
44561
44803
  /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: cn(
44562
44804
  isHigh ? "text-error" : isLow ? "text-warning" : "text-foreground"
44563
44805
  ), children: [
44564
- v.value,
44565
- v.unit || "",
44806
+ value,
44807
+ unit,
44566
44808
  " / ",
44567
44809
  max,
44568
- v.unit || ""
44810
+ unit
44569
44811
  ] })
44570
44812
  ] }),
44571
44813
  /* @__PURE__ */ jsx(
@@ -44576,14 +44818,19 @@ function VariablePanel({
44576
44818
  size: "sm"
44577
44819
  }
44578
44820
  )
44579
- ] }, v.name);
44821
+ ] }, name);
44580
44822
  })
44581
44823
  ] });
44582
44824
  }
44825
+ var numField;
44583
44826
  var init_VariablePanel = __esm({
44584
44827
  "components/game/organisms/puzzles/state-architect/VariablePanel.tsx"() {
44585
44828
  init_atoms2();
44586
44829
  init_cn();
44830
+ numField = (v, fallback = 0) => {
44831
+ const n = Number(v);
44832
+ return Number.isFinite(n) ? n : fallback;
44833
+ };
44587
44834
  VariablePanel.displayName = "VariablePanel";
44588
44835
  }
44589
44836
  });
@@ -44610,14 +44857,21 @@ function StateArchitectBoard({
44610
44857
  }) {
44611
44858
  const { emit } = useEventBus();
44612
44859
  const { t } = useTranslate();
44613
- const resolved = Array.isArray(entity) ? entity[0] : entity;
44614
- const [transitions, setTransitions] = useState(resolved?.transitions ?? []);
44860
+ const resolved = boardEntity(entity);
44861
+ const entityStates = Array.isArray(resolved?.states) ? resolved.states : [];
44862
+ const initialState = str(resolved?.initialState);
44863
+ const entityName = str(resolved?.entityName);
44864
+ const availableEvents = Array.isArray(resolved?.availableEvents) ? resolved.availableEvents : [];
44865
+ const testCases = Array.isArray(resolved?.testCases) ? resolved.testCases : [];
44866
+ const entityTransitions = Array.isArray(resolved?.transitions) ? resolved.transitions : [];
44867
+ const entityVariables = rows(resolved?.variables);
44868
+ const [transitions, setTransitions] = useState(entityTransitions);
44615
44869
  const [headerError, setHeaderError] = useState(false);
44616
44870
  const [playState, setPlayState] = useState("editing");
44617
- const [currentState, setCurrentState] = useState(resolved?.initialState ?? "");
44871
+ const [currentState, setCurrentState] = useState(initialState);
44618
44872
  const [selectedState, setSelectedState] = useState(null);
44619
44873
  const [testResults, setTestResults] = useState([]);
44620
- const [variables, setVariables] = useState(resolved?.variables ?? []);
44874
+ const [variables, setVariables] = useState(() => [...entityVariables]);
44621
44875
  const [attempts, setAttempts] = useState(0);
44622
44876
  const timerRef = useRef(null);
44623
44877
  const [addingFrom, setAddingFrom] = useState(null);
@@ -44626,12 +44880,12 @@ function StateArchitectBoard({
44626
44880
  }, []);
44627
44881
  const GRAPH_W = 500;
44628
44882
  const GRAPH_H = 400;
44629
- const positions = useMemo(() => layoutStates(resolved?.states ?? [], GRAPH_W, GRAPH_H), [resolved?.states]);
44883
+ const positions = useMemo(() => layoutStates(entityStates, GRAPH_W, GRAPH_H), [entityStates]);
44630
44884
  const handleStateClick = useCallback((state) => {
44631
44885
  if (playState !== "editing") return;
44632
44886
  if (addingFrom) {
44633
44887
  if (addingFrom !== state) {
44634
- const event = resolved?.availableEvents[0] || "EVENT";
44888
+ const event = availableEvents[0] || "EVENT";
44635
44889
  const newTrans = {
44636
44890
  id: `t-${nextTransId++}`,
44637
44891
  from: addingFrom,
@@ -44644,7 +44898,7 @@ function StateArchitectBoard({
44644
44898
  } else {
44645
44899
  setSelectedState(state);
44646
44900
  }
44647
- }, [playState, addingFrom, resolved?.availableEvents]);
44901
+ }, [playState, addingFrom, availableEvents]);
44648
44902
  const handleStartAddTransition = useCallback(() => {
44649
44903
  if (!selectedState) return;
44650
44904
  setAddingFrom(selectedState);
@@ -44653,9 +44907,9 @@ function StateArchitectBoard({
44653
44907
  setTransitions((prev) => prev.filter((t2) => t2.id !== transId));
44654
44908
  }, []);
44655
44909
  const machine = useMemo(() => ({
44656
- name: resolved?.entityName ?? "",
44657
- description: resolved?.description ?? "",
44658
- states: resolved?.states ?? [],
44910
+ name: entityName,
44911
+ description: str(resolved?.description),
44912
+ states: entityStates,
44659
44913
  currentState,
44660
44914
  transitions: transitions.map((t2) => ({
44661
44915
  from: t2.from,
@@ -44663,7 +44917,7 @@ function StateArchitectBoard({
44663
44917
  event: t2.event,
44664
44918
  guardHint: t2.guardHint
44665
44919
  }))
44666
- }), [resolved, currentState, transitions]);
44920
+ }), [entityName, resolved, entityStates, currentState, transitions]);
44667
44921
  const handleTest = useCallback(() => {
44668
44922
  if (playState !== "editing") return;
44669
44923
  if (testEvent) emit(`UI:${testEvent}`, {});
@@ -44672,7 +44926,7 @@ function StateArchitectBoard({
44672
44926
  const results = [];
44673
44927
  let testIdx = 0;
44674
44928
  const runNextTest = () => {
44675
- if (testIdx >= (resolved?.testCases.length ?? 0)) {
44929
+ if (testIdx >= testCases.length) {
44676
44930
  const allPassed = results.every((r) => r.passed);
44677
44931
  setPlayState(allPassed ? "success" : "fail");
44678
44932
  setTestResults(results);
@@ -44687,9 +44941,9 @@ function StateArchitectBoard({
44687
44941
  }
44688
44942
  return;
44689
44943
  }
44690
- const testCase = resolved?.testCases[testIdx];
44944
+ const testCase = testCases[testIdx];
44691
44945
  if (!testCase) return;
44692
- let state = resolved.initialState;
44946
+ let state = initialState;
44693
44947
  for (const event of testCase.events) {
44694
44948
  const trans = transitions.find((t2) => t2.from === state && t2.event === event);
44695
44949
  if (trans) {
@@ -44707,53 +44961,57 @@ function StateArchitectBoard({
44707
44961
  timerRef.current = setTimeout(runNextTest, stepDurationMs);
44708
44962
  };
44709
44963
  timerRef.current = setTimeout(runNextTest, stepDurationMs);
44710
- }, [playState, transitions, resolved, stepDurationMs, testEvent, completeEvent, emit]);
44964
+ }, [playState, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
44711
44965
  const handleTryAgain = useCallback(() => {
44712
44966
  if (timerRef.current) clearTimeout(timerRef.current);
44713
44967
  setPlayState("editing");
44714
- setCurrentState(resolved?.initialState ?? "");
44968
+ setCurrentState(initialState);
44715
44969
  setTestResults([]);
44716
- }, [resolved?.initialState]);
44970
+ }, [initialState]);
44717
44971
  const handleReset = useCallback(() => {
44718
44972
  if (timerRef.current) clearTimeout(timerRef.current);
44719
- setTransitions(resolved?.transitions ?? []);
44973
+ setTransitions(entityTransitions);
44720
44974
  setPlayState("editing");
44721
- setCurrentState(resolved?.initialState ?? "");
44975
+ setCurrentState(initialState);
44722
44976
  setTestResults([]);
44723
- setVariables(resolved?.variables ?? []);
44977
+ setVariables([...entityVariables]);
44724
44978
  setSelectedState(null);
44725
44979
  setAddingFrom(null);
44726
44980
  setAttempts(0);
44727
- }, [resolved]);
44981
+ }, [entityTransitions, initialState, entityVariables]);
44728
44982
  const codeData = useMemo(() => ({
44729
- name: resolved?.entityName ?? "",
44730
- states: resolved?.states ?? [],
44731
- initialState: resolved?.initialState ?? "",
44983
+ name: entityName,
44984
+ states: entityStates,
44985
+ initialState,
44732
44986
  transitions: transitions.map((t2) => ({
44733
44987
  from: t2.from,
44734
44988
  to: t2.to,
44735
44989
  event: t2.event,
44736
44990
  ...t2.guardHint ? { guard: t2.guardHint } : {}
44737
44991
  }))
44738
- }), [resolved, transitions]);
44992
+ }), [entityName, entityStates, initialState, transitions]);
44739
44993
  if (!resolved) return null;
44994
+ const theme = resolved.theme ?? void 0;
44995
+ const themeBackground = theme?.background;
44996
+ const headerImage = str(resolved.headerImage);
44997
+ const hint = str(resolved.hint);
44740
44998
  return /* @__PURE__ */ jsxs(
44741
44999
  VStack,
44742
45000
  {
44743
45001
  className: cn("p-4 gap-6", className),
44744
45002
  style: {
44745
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
45003
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
44746
45004
  backgroundSize: "cover",
44747
45005
  backgroundPosition: "center"
44748
45006
  },
44749
45007
  children: [
44750
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
45008
+ headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
44751
45009
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
44752
- /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
44753
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description }),
45010
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
45011
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) }),
44754
45012
  /* @__PURE__ */ jsxs(HStack, { className: "items-center p-2 rounded bg-warning/10 border border-warning/30", gap: "xs", children: [
44755
45013
  /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-warning font-bold", children: t("game.hint") + ":" }),
44756
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: resolved.hint })
45014
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: hint })
44757
45015
  ] })
44758
45016
  ] }),
44759
45017
  /* @__PURE__ */ jsxs(HStack, { className: "flex-wrap items-start", gap: "lg", children: [
@@ -44801,14 +45059,14 @@ function StateArchitectBoard({
44801
45059
  ]
44802
45060
  }
44803
45061
  ),
44804
- resolved.states.map((state) => /* @__PURE__ */ jsx(
45062
+ entityStates.map((state) => /* @__PURE__ */ jsx(
44805
45063
  StateNode2,
44806
45064
  {
44807
45065
  name: state,
44808
45066
  position: positions[state],
44809
45067
  isCurrent: state === currentState,
44810
45068
  isSelected: state === selectedState,
44811
- isInitial: state === resolved.initialState,
45069
+ isInitial: state === initialState,
44812
45070
  onClick: () => handleStateClick(state)
44813
45071
  },
44814
45072
  state
@@ -44855,7 +45113,7 @@ function StateArchitectBoard({
44855
45113
  /* @__PURE__ */ jsx(
44856
45114
  VariablePanel,
44857
45115
  {
44858
- entityName: resolved.entityName,
45116
+ entityName,
44859
45117
  variables
44860
45118
  }
44861
45119
  ),
@@ -44870,12 +45128,12 @@ function StateArchitectBoard({
44870
45128
  resolved.showCodeView !== false && /* @__PURE__ */ jsx(CodeView, { data: codeData, label: "View Code" })
44871
45129
  ] })
44872
45130
  ] }),
44873
- playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: resolved.successMessage || t("stateArchitect.allPassed") }) }),
45131
+ playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("stateArchitect.allPassed") }) }),
44874
45132
  playState === "fail" && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
44875
45133
  /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(ENCOURAGEMENT_KEYS3[Math.min(attempts - 1, ENCOURAGEMENT_KEYS3.length - 1)] ?? ENCOURAGEMENT_KEYS3[0]) }) }),
44876
- attempts >= 3 && resolved.hint && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
45134
+ attempts >= 3 && hint && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
44877
45135
  /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
44878
- /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
45136
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
44879
45137
  ] }) })
44880
45138
  ] }),
44881
45139
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
@@ -44905,6 +45163,7 @@ var init_StateArchitectBoard = __esm({
44905
45163
  init_TransitionArrow();
44906
45164
  init_VariablePanel();
44907
45165
  init_CodeView();
45166
+ init_boardEntity();
44908
45167
  ENCOURAGEMENT_KEYS3 = [
44909
45168
  "puzzle.tryAgain1",
44910
45169
  "puzzle.tryAgain2",
@@ -44941,8 +45200,8 @@ var init_StatsOrganism = __esm({
44941
45200
  return /* @__PURE__ */ jsx(ErrorState, { message: error.message, className });
44942
45201
  }
44943
45202
  const stats = items.map((item) => ({
44944
- value: item.value,
44945
- label: item.label
45203
+ value: String(item.value ?? ""),
45204
+ label: String(item.label ?? "")
44946
45205
  }));
44947
45206
  return /* @__PURE__ */ jsx(
44948
45207
  StatsGrid,
@@ -44988,10 +45247,10 @@ var init_StepFlowOrganism = __esm({
44988
45247
  return /* @__PURE__ */ jsx(ErrorState, { message: error.message, className });
44989
45248
  }
44990
45249
  const steps = items.map((item) => ({
44991
- number: item.number,
44992
- title: item.title,
44993
- description: item.description,
44994
- icon: item.icon
45250
+ number: item.number != null ? Number(item.number) : void 0,
45251
+ title: String(item.title ?? ""),
45252
+ description: String(item.description ?? ""),
45253
+ icon: item.icon != null ? String(item.icon) : void 0
44995
45254
  }));
44996
45255
  return /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
44997
45256
  (heading || subtitle) && /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
@@ -45164,13 +45423,13 @@ var init_TeamOrganism = __esm({
45164
45423
  /* @__PURE__ */ jsx(SimpleGrid, { cols: cols > 0 ? cols : 1, gap: "lg", children: items.map((member) => /* @__PURE__ */ jsx(
45165
45424
  TeamCard,
45166
45425
  {
45167
- name: member.name,
45168
- nameAr: member.nameAr,
45169
- role: member.role,
45170
- bio: member.bio,
45171
- avatar: member.avatar
45426
+ name: String(member.name ?? ""),
45427
+ nameAr: member.nameAr != null ? String(member.nameAr) : void 0,
45428
+ role: String(member.role ?? ""),
45429
+ bio: String(member.bio ?? ""),
45430
+ avatar: member.avatar != null ? String(member.avatar) : void 0
45172
45431
  },
45173
- member.id
45432
+ String(member.id ?? "")
45174
45433
  )) })
45175
45434
  ] });
45176
45435
  };
@@ -45407,8 +45666,8 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
45407
45666
  const [turn, setTurn] = useState(1);
45408
45667
  const [gameResult, setGameResult] = useState(null);
45409
45668
  const checkGameEnd = useCallback((currentUnits) => {
45410
- const pa = currentUnits.filter((u) => u.team === "player" && u.health > 0);
45411
- const ea = currentUnits.filter((u) => u.team === "enemy" && u.health > 0);
45669
+ const pa = currentUnits.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
45670
+ const ea = currentUnits.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
45412
45671
  if (pa.length === 0) {
45413
45672
  setGameResult("defeat");
45414
45673
  setPhase("game_over");
@@ -45426,34 +45685,36 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
45426
45685
  }
45427
45686
  }, [onGameEnd, gameEndEvent, eventBus]);
45428
45687
  const handleUnitClick = useCallback((unitId) => {
45429
- const unit = units.find((u) => u.id === unitId);
45688
+ const unit = units.find((u) => str(u.id) === unitId);
45430
45689
  if (!unit) return;
45431
45690
  if (unitClickEvent) {
45432
45691
  eventBus.emit(`UI:${unitClickEvent}`, { unitId });
45433
45692
  }
45434
45693
  if (phase === "observation" || phase === "selection") {
45435
- if (unit.team === "player") {
45694
+ if (unitTeam(unit) === "player") {
45436
45695
  setSelectedUnitId(unitId);
45437
45696
  setPhase("movement");
45438
45697
  }
45439
45698
  } else if (phase === "action") {
45440
- const selectedUnit = units.find((u) => u.id === selectedUnitId);
45699
+ const selectedUnit = units.find((u) => str(u.id) === selectedUnitId);
45441
45700
  if (!selectedUnit) return;
45442
- if (unit.team === "enemy") {
45443
- const dx = Math.abs(unit.position.x - selectedUnit.position.x);
45444
- const dy = Math.abs(unit.position.y - selectedUnit.position.y);
45701
+ if (unitTeam(unit) === "enemy") {
45702
+ const up = unitPosition(unit);
45703
+ const sp = unitPosition(selectedUnit);
45704
+ const dx = Math.abs(up.x - sp.x);
45705
+ const dy = Math.abs(up.y - sp.y);
45445
45706
  if (dx <= 1 && dy <= 1 && dx + dy > 0) {
45446
- const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, selectedUnit.attack - unit.defense);
45447
- const newHealth = Math.max(0, unit.health - damage);
45707
+ const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, num(selectedUnit.attack) - num(unit.defense));
45708
+ const newHealth = Math.max(0, unitHealth(unit) - damage);
45448
45709
  const updatedUnits = units.map(
45449
- (u) => u.id === unit.id ? { ...u, health: newHealth } : u
45710
+ (u) => str(u.id) === str(unit.id) ? { ...u, health: newHealth } : u
45450
45711
  );
45451
45712
  setUnits(updatedUnits);
45452
45713
  onAttack?.(selectedUnit, unit, damage);
45453
45714
  if (attackEvent) {
45454
45715
  eventBus.emit(`UI:${attackEvent}`, {
45455
- attackerId: selectedUnit.id,
45456
- targetId: unit.id,
45716
+ attackerId: str(selectedUnit.id),
45717
+ targetId: str(unit.id),
45457
45718
  damage
45458
45719
  });
45459
45720
  }
@@ -45470,16 +45731,20 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
45470
45731
  eventBus.emit(`UI:${tileClickEvent}`, { x, y });
45471
45732
  }
45472
45733
  if (phase === "movement" && selectedUnitId) {
45473
- const selectedUnit = units.find((u) => u.id === selectedUnitId);
45734
+ const selectedUnit = units.find((u) => str(u.id) === selectedUnitId);
45474
45735
  if (!selectedUnit) return;
45475
- const dx = Math.abs(x - selectedUnit.position.x);
45476
- const dy = Math.abs(y - selectedUnit.position.y);
45736
+ const sp = unitPosition(selectedUnit);
45737
+ const dx = Math.abs(x - sp.x);
45738
+ const dy = Math.abs(y - sp.y);
45477
45739
  const dist = dx + dy;
45478
- if (dist > 0 && dist <= selectedUnit.movement) {
45479
- if (!units.some((u) => u.position.x === x && u.position.y === y && u.health > 0)) {
45740
+ if (dist > 0 && dist <= num(selectedUnit.movement)) {
45741
+ if (!units.some((u) => {
45742
+ const p2 = unitPosition(u);
45743
+ return p2.x === x && p2.y === y && unitHealth(u) > 0;
45744
+ })) {
45480
45745
  setUnits(
45481
45746
  (prev) => prev.map(
45482
- (u) => u.id === selectedUnitId ? { ...u, position: { x, y } } : u
45747
+ (u) => str(u.id) === selectedUnitId ? { ...u, position: { x, y } } : u
45483
45748
  )
45484
45749
  );
45485
45750
  setPhase("action");
@@ -45522,12 +45787,13 @@ var init_useBattleState = __esm({
45522
45787
  "components/game/organisms/hooks/useBattleState.ts"() {
45523
45788
  "use client";
45524
45789
  init_useEventBus();
45790
+ init_boardEntity();
45525
45791
  }
45526
45792
  });
45527
45793
  function UncontrolledBattleBoard({ entity, ...rest }) {
45528
- const resolved = Array.isArray(entity) ? entity[0] : entity;
45794
+ const resolved = boardEntity(entity);
45529
45795
  const battleState = useBattleState(
45530
- resolved?.initialUnits ?? [],
45796
+ rows(resolved?.initialUnits),
45531
45797
  {
45532
45798
  tileClickEvent: rest.tileClickEvent,
45533
45799
  unitClickEvent: rest.unitClickEvent,
@@ -45563,10 +45829,23 @@ function UncontrolledBattleBoard({ entity, ...rest }) {
45563
45829
  var init_UncontrolledBattleBoard = __esm({
45564
45830
  "components/game/organisms/UncontrolledBattleBoard.tsx"() {
45565
45831
  init_BattleBoard();
45832
+ init_boardEntity();
45566
45833
  init_useBattleState();
45567
45834
  UncontrolledBattleBoard.displayName = "UncontrolledBattleBoard";
45568
45835
  }
45569
45836
  });
45837
+ function heroPosition(h) {
45838
+ return vec2(h.position);
45839
+ }
45840
+ function heroOwner(h) {
45841
+ return str(h.owner);
45842
+ }
45843
+ function heroMovement(h) {
45844
+ return num(h.movement);
45845
+ }
45846
+ function hexPassable(h) {
45847
+ return h.passable !== false;
45848
+ }
45570
45849
  function defaultIsInRange(from, to, range) {
45571
45850
  return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
45572
45851
  }
@@ -45597,36 +45876,36 @@ function WorldMapBoard({
45597
45876
  className
45598
45877
  }) {
45599
45878
  const eventBus = useEventBus();
45600
- const resolved = Array.isArray(entity) ? entity[0] : entity;
45601
- const hexes = resolved?.hexes ?? [];
45602
- const heroes = resolved?.heroes ?? [];
45603
- const features = resolved?.features ?? [];
45604
- const selectedHeroId = resolved?.selectedHeroId;
45879
+ const resolved = boardEntity(entity);
45880
+ const hexes = rows(resolved?.hexes);
45881
+ const heroes = rows(resolved?.heroes);
45882
+ const features = Array.isArray(resolved?.features) ? resolved.features : [];
45883
+ const selectedHeroId = resolved?.selectedHeroId ?? null;
45605
45884
  const assetManifest = resolved?.assetManifest;
45606
45885
  const backgroundImage = resolved?.backgroundImage;
45607
45886
  const [hoveredTile, setHoveredTile] = useState(null);
45608
45887
  const selectedHero = useMemo(
45609
- () => heroes.find((h) => h.id === selectedHeroId) ?? null,
45888
+ () => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
45610
45889
  [heroes, selectedHeroId]
45611
45890
  );
45612
45891
  const tiles = useMemo(
45613
45892
  () => hexes.map((hex) => ({
45614
- x: hex.x,
45615
- y: hex.y,
45616
- terrain: hex.terrain,
45617
- terrainSprite: hex.terrainSprite
45893
+ x: num(hex.x),
45894
+ y: num(hex.y),
45895
+ terrain: str(hex.terrain),
45896
+ terrainSprite: hex.terrainSprite == null ? void 0 : str(hex.terrainSprite)
45618
45897
  })),
45619
45898
  [hexes]
45620
45899
  );
45621
45900
  const baseUnits = useMemo(
45622
45901
  () => heroes.map((hero) => ({
45623
- id: hero.id,
45624
- position: hero.position,
45625
- name: hero.name,
45626
- team: hero.owner === "enemy" ? "enemy" : "player",
45902
+ id: str(hero.id),
45903
+ position: heroPosition(hero),
45904
+ name: str(hero.name),
45905
+ team: heroOwner(hero) === "enemy" ? "enemy" : "player",
45627
45906
  health: 100,
45628
45907
  maxHealth: 100,
45629
- sprite: hero.sprite
45908
+ sprite: hero.sprite == null ? void 0 : str(hero.sprite)
45630
45909
  })),
45631
45910
  [heroes]
45632
45911
  );
@@ -45667,73 +45946,94 @@ function WorldMapBoard({
45667
45946
  const isoUnits = useMemo(() => {
45668
45947
  if (movingPositions.size === 0) return baseUnits;
45669
45948
  return baseUnits.map((u) => {
45670
- const pos = movingPositions.get(u.id);
45949
+ const pos = u.id == null ? void 0 : movingPositions.get(u.id);
45671
45950
  return pos ? { ...u, position: pos } : u;
45672
45951
  });
45673
45952
  }, [baseUnits, movingPositions]);
45674
45953
  const validMoves = useMemo(() => {
45675
- if (!selectedHero || selectedHero.movement <= 0) return [];
45954
+ if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
45955
+ const sp = heroPosition(selectedHero);
45956
+ const sOwner = heroOwner(selectedHero);
45957
+ const range = heroMovement(selectedHero);
45676
45958
  const moves = [];
45677
45959
  hexes.forEach((hex) => {
45678
- if (hex.passable === false) return;
45679
- if (hex.x === selectedHero.position.x && hex.y === selectedHero.position.y) return;
45680
- if (!isInRange(selectedHero.position, { x: hex.x, y: hex.y }, selectedHero.movement)) return;
45681
- if (heroes.some((h) => h.position.x === hex.x && h.position.y === hex.y && h.owner === selectedHero.owner)) return;
45682
- moves.push({ x: hex.x, y: hex.y });
45960
+ const hx = num(hex.x);
45961
+ const hy = num(hex.y);
45962
+ if (!hexPassable(hex)) return;
45963
+ if (hx === sp.x && hy === sp.y) return;
45964
+ if (!isInRange(sp, { x: hx, y: hy }, range)) return;
45965
+ if (heroes.some((h) => {
45966
+ const hp = heroPosition(h);
45967
+ return hp.x === hx && hp.y === hy && heroOwner(h) === sOwner;
45968
+ })) return;
45969
+ moves.push({ x: hx, y: hy });
45683
45970
  });
45684
45971
  return moves;
45685
45972
  }, [selectedHero, hexes, heroes, isInRange]);
45686
45973
  const attackTargets = useMemo(() => {
45687
- if (!selectedHero || selectedHero.movement <= 0) return [];
45688
- return heroes.filter((h) => h.owner !== selectedHero.owner).filter((h) => isInRange(selectedHero.position, h.position, selectedHero.movement)).map((h) => h.position);
45974
+ if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
45975
+ const sp = heroPosition(selectedHero);
45976
+ const sOwner = heroOwner(selectedHero);
45977
+ const range = heroMovement(selectedHero);
45978
+ return heroes.filter((h) => heroOwner(h) !== sOwner).filter((h) => isInRange(sp, heroPosition(h), range)).map((h) => heroPosition(h));
45689
45979
  }, [selectedHero, heroes, isInRange]);
45690
- const maxY = Math.max(...hexes.map((h) => h.y), 0);
45980
+ const maxY = Math.max(...hexes.map((h) => num(h.y)), 0);
45691
45981
  const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
45692
45982
  const tileToScreen = useCallback(
45693
45983
  (tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
45694
45984
  [scale, baseOffsetX]
45695
45985
  );
45696
45986
  const hoveredHex = useMemo(
45697
- () => hoveredTile ? hexes.find((h) => h.x === hoveredTile.x && h.y === hoveredTile.y) ?? null : null,
45987
+ () => hoveredTile ? hexes.find((h) => num(h.x) === hoveredTile.x && num(h.y) === hoveredTile.y) ?? null : null,
45698
45988
  [hoveredTile, hexes]
45699
45989
  );
45700
45990
  const hoveredHero = useMemo(
45701
- () => hoveredTile ? heroes.find((h) => h.position.x === hoveredTile.x && h.position.y === hoveredTile.y) ?? null : null,
45991
+ () => hoveredTile ? heroes.find((h) => {
45992
+ const hp = heroPosition(h);
45993
+ return hp.x === hoveredTile.x && hp.y === hoveredTile.y;
45994
+ }) ?? null : null,
45702
45995
  [hoveredTile, heroes]
45703
45996
  );
45704
45997
  const handleTileClick = useCallback((x, y) => {
45705
45998
  if (movementAnimRef.current) return;
45706
- const hex = hexes.find((h) => h.x === x && h.y === y);
45999
+ const hex = hexes.find((h) => num(h.x) === x && num(h.y) === y);
45707
46000
  if (!hex) return;
45708
46001
  if (tileClickEvent) {
45709
46002
  eventBus.emit(`UI:${tileClickEvent}`, { x, y });
45710
46003
  }
45711
46004
  if (selectedHero && validMoves.some((m) => m.x === x && m.y === y)) {
45712
- startMoveAnimation(selectedHero.id, { ...selectedHero.position }, { x, y }, () => {
45713
- onHeroMove?.(selectedHero.id, x, y);
46005
+ const heroId = str(selectedHero.id);
46006
+ startMoveAnimation(heroId, { ...heroPosition(selectedHero) }, { x, y }, () => {
46007
+ onHeroMove?.(heroId, x, y);
45714
46008
  if (heroMoveEvent) {
45715
- eventBus.emit(`UI:${heroMoveEvent}`, { heroId: selectedHero.id, toX: x, toY: y });
46009
+ eventBus.emit(`UI:${heroMoveEvent}`, { heroId, toX: x, toY: y });
45716
46010
  }
45717
- if (hex.feature && hex.feature !== "none") {
45718
- onFeatureEnter?.(selectedHero.id, hex);
46011
+ const feature = str(hex.feature);
46012
+ if (feature && feature !== "none") {
46013
+ onFeatureEnter?.(heroId, hex);
45719
46014
  if (featureEnterEvent) {
45720
- eventBus.emit(`UI:${featureEnterEvent}`, { heroId: selectedHero.id, feature: hex.feature, hex });
46015
+ eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex });
45721
46016
  }
45722
46017
  }
45723
46018
  });
45724
46019
  return;
45725
46020
  }
45726
- const enemy = heroes.find((h) => h.position.x === x && h.position.y === y && h.owner === "enemy");
46021
+ const enemy = heroes.find((h) => {
46022
+ const hp = heroPosition(h);
46023
+ return hp.x === x && hp.y === y && heroOwner(h) === "enemy";
46024
+ });
45727
46025
  if (selectedHero && enemy && attackTargets.some((t) => t.x === x && t.y === y)) {
45728
- onBattleEncounter?.(selectedHero.id, enemy.id);
46026
+ const attackerId = str(selectedHero.id);
46027
+ const defenderId = str(enemy.id);
46028
+ onBattleEncounter?.(attackerId, defenderId);
45729
46029
  if (battleEncounterEvent) {
45730
- eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId: selectedHero.id, defenderId: enemy.id });
46030
+ eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId, defenderId });
45731
46031
  }
45732
46032
  }
45733
46033
  }, [hexes, heroes, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
45734
46034
  const handleUnitClick = useCallback((unitId) => {
45735
- const hero = heroes.find((h) => h.id === unitId);
45736
- if (hero && (hero.owner === "player" || allowMoveAllHeroes)) {
46035
+ const hero = heroes.find((h) => str(h.id) === unitId);
46036
+ if (hero && (heroOwner(hero) === "player" || allowMoveAllHeroes)) {
45737
46037
  onHeroSelect?.(unitId);
45738
46038
  if (heroSelectEvent) {
45739
46039
  eventBus.emit(`UI:${heroSelectEvent}`, { heroId: unitId });
@@ -45806,6 +46106,7 @@ var init_WorldMapBoard = __esm({
45806
46106
  init_Stack();
45807
46107
  init_LoadingState();
45808
46108
  init_IsometricCanvas2();
46109
+ init_boardEntity();
45809
46110
  init_isometric();
45810
46111
  WorldMapBoard.displayName = "WorldMapBoard";
45811
46112
  }
@@ -49034,11 +49335,11 @@ function buildMockData(schema) {
49034
49335
  result[entityName] = entity.instances;
49035
49336
  continue;
49036
49337
  }
49037
- const rows = Array.from(
49338
+ const rows2 = Array.from(
49038
49339
  { length: 10 },
49039
49340
  (_, i) => generateEntityRow(entity, i + 1)
49040
49341
  );
49041
- result[entityName] = rows;
49342
+ result[entityName] = rows2;
49042
49343
  }
49043
49344
  for (const orbital of schema.orbitals) {
49044
49345
  for (const traitRef of orbital.traits ?? []) {