@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
@@ -2503,7 +2503,7 @@ var init_SvgGrid = __esm({
2503
2503
  x,
2504
2504
  y,
2505
2505
  cols = 4,
2506
- rows = 3,
2506
+ rows: rows2 = 3,
2507
2507
  spacing = 20,
2508
2508
  nodeRadius = 3,
2509
2509
  color = "var(--color-primary)",
@@ -2512,7 +2512,7 @@ var init_SvgGrid = __esm({
2512
2512
  highlights = []
2513
2513
  }) => {
2514
2514
  const highlightSet = new Set(highlights);
2515
- return /* @__PURE__ */ jsxRuntime.jsx("g", { className, opacity, children: Array.from({ length: rows }).map(
2515
+ return /* @__PURE__ */ jsxRuntime.jsx("g", { className, opacity, children: Array.from({ length: rows2 }).map(
2516
2516
  (_, row) => Array.from({ length: cols }).map((_2, col) => {
2517
2517
  const index = row * cols + col;
2518
2518
  const isHighlighted = highlightSet.has(index);
@@ -3172,7 +3172,7 @@ var init_Input = __esm({
3172
3172
  onClear,
3173
3173
  value,
3174
3174
  options,
3175
- rows = 3,
3175
+ rows: rows2 = 3,
3176
3176
  onChange,
3177
3177
  ...props
3178
3178
  }, ref) => {
@@ -3222,7 +3222,7 @@ var init_Input = __esm({
3222
3222
  ref,
3223
3223
  value,
3224
3224
  onChange,
3225
- rows,
3225
+ rows: rows2,
3226
3226
  className: baseClassName,
3227
3227
  ...props
3228
3228
  }
@@ -5478,66 +5478,6 @@ var init_RangeSlider = __esm({
5478
5478
  RangeSlider.displayName = "RangeSlider";
5479
5479
  }
5480
5480
  });
5481
- function easeOut(t) {
5482
- return t * (2 - t);
5483
- }
5484
- var AnimatedCounter;
5485
- var init_AnimatedCounter = __esm({
5486
- "components/marketing/atoms/AnimatedCounter.tsx"() {
5487
- "use client";
5488
- init_cn();
5489
- init_Typography();
5490
- AnimatedCounter = ({
5491
- value: rawValue,
5492
- duration = 600,
5493
- prefix,
5494
- suffix,
5495
- className
5496
- }) => {
5497
- const numericRaw = typeof rawValue === "number" ? rawValue : Number.parseFloat(String(rawValue ?? ""));
5498
- const value = !Number.isNaN(numericRaw) ? numericRaw : 0;
5499
- const [displayValue, setDisplayValue] = React84.useState(value);
5500
- const previousValueRef = React84.useRef(value);
5501
- const animationFrameRef = React84.useRef(null);
5502
- React84.useEffect(() => {
5503
- const from = previousValueRef.current;
5504
- const to = value;
5505
- previousValueRef.current = value;
5506
- if (from === to) {
5507
- setDisplayValue(to);
5508
- return;
5509
- }
5510
- const startTime = performance.now();
5511
- const diff = to - from;
5512
- function animate(currentTime) {
5513
- const elapsed = currentTime - startTime;
5514
- const progress = Math.min(elapsed / duration, 1);
5515
- const easedProgress = easeOut(progress);
5516
- setDisplayValue(from + diff * easedProgress);
5517
- if (progress < 1) {
5518
- animationFrameRef.current = requestAnimationFrame(animate);
5519
- } else {
5520
- setDisplayValue(to);
5521
- }
5522
- }
5523
- animationFrameRef.current = requestAnimationFrame(animate);
5524
- return () => {
5525
- if (animationFrameRef.current !== null) {
5526
- cancelAnimationFrame(animationFrameRef.current);
5527
- }
5528
- };
5529
- }, [value, duration]);
5530
- const decimalPlaces = Number.isInteger(value) ? 0 : String(value).split(".")[1]?.length ?? 0;
5531
- const formattedValue = displayValue.toFixed(decimalPlaces);
5532
- return /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "h3", className: cn("tabular-nums", className), children: [
5533
- prefix,
5534
- formattedValue,
5535
- suffix
5536
- ] });
5537
- };
5538
- AnimatedCounter.displayName = "AnimatedCounter";
5539
- }
5540
- });
5541
5481
  function useInfiniteScroll(onLoadMore, options = {}) {
5542
5482
  const { rootMargin = "200px", hasMore = true, isLoading = false } = options;
5543
5483
  const observerRef = React84.useRef(null);
@@ -8019,15 +7959,15 @@ function HeaderSkeleton({ className }) {
8019
7959
  ] })
8020
7960
  ] });
8021
7961
  }
8022
- function TableSkeleton({ rows = 5, columns = 4, className }) {
7962
+ function TableSkeleton({ rows: rows2 = 5, columns = 4, className }) {
8023
7963
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", className: cn("border border-border rounded-lg overflow-hidden", className), children: [
8024
7964
  /* @__PURE__ */ jsxRuntime.jsx(HStack, { className: "px-4 py-3 bg-muted/30 border-b border-border", children: Array.from({ length: columns }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(SkeletonBlock, { className: "h-4 flex-1 mx-2" }, i)) }),
8025
- Array.from({ length: rows }).map((_, rowIdx) => /* @__PURE__ */ jsxRuntime.jsx(
7965
+ Array.from({ length: rows2 }).map((_, rowIdx) => /* @__PURE__ */ jsxRuntime.jsx(
8026
7966
  HStack,
8027
7967
  {
8028
7968
  className: cn(
8029
7969
  "px-4 py-3",
8030
- rowIdx < rows - 1 && "border-b border-border"
7970
+ rowIdx < rows2 - 1 && "border-b border-border"
8031
7971
  ),
8032
7972
  children: Array.from({ length: columns }).map((_2, colIdx) => /* @__PURE__ */ jsxRuntime.jsx(SkeletonLine, { className: "flex-1 mx-2" }, colIdx))
8033
7973
  },
@@ -8075,18 +8015,18 @@ function CardSkeleton({ className }) {
8075
8015
  }
8076
8016
  );
8077
8017
  }
8078
- function TextSkeleton({ rows = 3, className }) {
8079
- return /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "sm", className, children: Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
8018
+ function TextSkeleton({ rows: rows2 = 3, className }) {
8019
+ return /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "sm", className, children: Array.from({ length: rows2 }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
8080
8020
  SkeletonLine,
8081
8021
  {
8082
- className: i === rows - 1 ? "w-2/3" : "w-full"
8022
+ className: i === rows2 - 1 ? "w-2/3" : "w-full"
8083
8023
  },
8084
8024
  i
8085
8025
  )) });
8086
8026
  }
8087
8027
  function Skeleton({
8088
8028
  variant = "text",
8089
- rows,
8029
+ rows: rows2,
8090
8030
  columns,
8091
8031
  fields,
8092
8032
  className
@@ -8096,15 +8036,15 @@ function Skeleton({
8096
8036
  case "header":
8097
8037
  return /* @__PURE__ */ jsxRuntime.jsx(HeaderSkeleton, { className });
8098
8038
  case "table":
8099
- return /* @__PURE__ */ jsxRuntime.jsx(TableSkeleton, { rows, columns, className });
8039
+ return /* @__PURE__ */ jsxRuntime.jsx(TableSkeleton, { rows: rows2, columns, className });
8100
8040
  case "form":
8101
8041
  return /* @__PURE__ */ jsxRuntime.jsx(FormSkeleton, { fields, className });
8102
8042
  case "card":
8103
8043
  return /* @__PURE__ */ jsxRuntime.jsx(CardSkeleton, { className });
8104
8044
  case "text":
8105
- return /* @__PURE__ */ jsxRuntime.jsx(TextSkeleton, { rows, className });
8045
+ return /* @__PURE__ */ jsxRuntime.jsx(TextSkeleton, { rows: rows2, className });
8106
8046
  default:
8107
- return /* @__PURE__ */ jsxRuntime.jsx(TextSkeleton, { rows, className });
8047
+ return /* @__PURE__ */ jsxRuntime.jsx(TextSkeleton, { rows: rows2, className });
8108
8048
  }
8109
8049
  }
8110
8050
  var pulseClass;
@@ -9258,7 +9198,7 @@ var init_Menu = __esm({
9258
9198
  className
9259
9199
  }) => {
9260
9200
  const eventBus = useEventBus();
9261
- const { t } = hooks.useTranslate();
9201
+ const { t, direction } = hooks.useTranslate();
9262
9202
  const [isOpen, setIsOpen] = React84.useState(false);
9263
9203
  const [activeSubMenu, setActiveSubMenu] = React84.useState(null);
9264
9204
  const [triggerRect, setTriggerRect] = React84.useState(null);
@@ -9312,6 +9252,18 @@ var init_Menu = __esm({
9312
9252
  "bottom-start": "top-full left-0 mt-2",
9313
9253
  "bottom-end": "top-full right-0 mt-2"
9314
9254
  };
9255
+ const rtlMirror = {
9256
+ "top-left": "top-right",
9257
+ "top-right": "top-left",
9258
+ "bottom-left": "bottom-right",
9259
+ "bottom-right": "bottom-left",
9260
+ "top-start": "top-end",
9261
+ "top-end": "top-start",
9262
+ "bottom-start": "bottom-end",
9263
+ "bottom-end": "bottom-start"
9264
+ };
9265
+ const effectivePosition = direction === "rtl" ? rtlMirror[position] ?? position : position;
9266
+ const subMenuSideClass = direction === "rtl" ? "right-full mr-2" : "left-full ml-2";
9315
9267
  const triggerChild = React84__namespace.default.isValidElement(trigger) ? trigger : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", as: "span", children: trigger });
9316
9268
  const triggerElement = React84__namespace.default.cloneElement(
9317
9269
  triggerChild,
@@ -9339,7 +9291,7 @@ var init_Menu = __esm({
9339
9291
  onMouseEnter: () => hasSubMenu && setActiveSubMenu(itemId),
9340
9292
  "data-testid": item.event ? `action-${item.event}` : void 0,
9341
9293
  className: cn(
9342
- "w-full flex items-center justify-between gap-3 px-4 py-2 text-left",
9294
+ "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
9343
9295
  "text-sm transition-colors",
9344
9296
  "hover:bg-muted",
9345
9297
  "focus:outline-none focus:bg-muted",
@@ -9358,7 +9310,7 @@ var init_Menu = __esm({
9358
9310
  }
9359
9311
  ),
9360
9312
  item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
9361
- hasSubMenu && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "chevron-right", size: "sm", className: "flex-shrink-0" })
9313
+ hasSubMenu && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: direction === "rtl" ? "chevron-left" : "chevron-right", size: "sm", className: "flex-shrink-0" })
9362
9314
  ] })
9363
9315
  },
9364
9316
  itemId
@@ -9378,7 +9330,8 @@ var init_Menu = __esm({
9378
9330
  Box,
9379
9331
  {
9380
9332
  className: cn(
9381
- "absolute left-full top-0 ml-2 z-50",
9333
+ "absolute top-0 z-50",
9334
+ subMenuSideClass,
9382
9335
  menuContainerStyles
9383
9336
  ),
9384
9337
  children: renderMenuItems(item.subMenu)
@@ -9396,12 +9349,12 @@ var init_Menu = __esm({
9396
9349
  className: cn(
9397
9350
  "absolute z-50",
9398
9351
  menuContainerStyles,
9399
- positionClasses3[position],
9352
+ positionClasses3[effectivePosition],
9400
9353
  className
9401
9354
  ),
9402
9355
  style: {
9403
- left: position.includes("left") ? 0 : "auto",
9404
- right: position.includes("right") ? 0 : "auto"
9356
+ left: effectivePosition.includes("left") ? 0 : "auto",
9357
+ right: effectivePosition.includes("right") ? 0 : "auto"
9405
9358
  },
9406
9359
  role: "menu",
9407
9360
  children: renderMenuItems(items)
@@ -9719,7 +9672,7 @@ var init_MapView = __esm({
9719
9672
  shadowSize: [41, 41]
9720
9673
  });
9721
9674
  L.Marker.prototype.options.icon = defaultIcon;
9722
- const { useEffect: useEffect71, useRef: useRef66, useCallback: useCallback113, useState: useState103 } = React84__namespace.default;
9675
+ const { useEffect: useEffect71, useRef: useRef66, useCallback: useCallback114, useState: useState103 } = React84__namespace.default;
9723
9676
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
9724
9677
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
9725
9678
  function MapUpdater({ centerLat, centerLng, zoom }) {
@@ -9765,7 +9718,7 @@ var init_MapView = __esm({
9765
9718
  }) {
9766
9719
  const eventBus = useEventBus3();
9767
9720
  const [clickedPosition, setClickedPosition] = useState103(null);
9768
- const handleMapClick = useCallback113((lat, lng) => {
9721
+ const handleMapClick = useCallback114((lat, lng) => {
9769
9722
  if (showClickedPin) {
9770
9723
  setClickedPosition({ lat, lng });
9771
9724
  }
@@ -9774,7 +9727,7 @@ var init_MapView = __esm({
9774
9727
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
9775
9728
  }
9776
9729
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
9777
- const handleMarkerClick = useCallback113((marker) => {
9730
+ const handleMarkerClick = useCallback114((marker) => {
9778
9731
  onMarkerClick?.(marker);
9779
9732
  if (markerClickEvent) {
9780
9733
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -9995,7 +9948,7 @@ function InputPattern({
9995
9948
  function TextareaPattern({
9996
9949
  value = "",
9997
9950
  placeholder,
9998
- rows = 4,
9951
+ rows: rows2 = 4,
9999
9952
  disabled = false,
10000
9953
  fieldError,
10001
9954
  onChange,
@@ -10015,7 +9968,7 @@ function TextareaPattern({
10015
9968
  {
10016
9969
  value: localValue,
10017
9970
  placeholder,
10018
- rows,
9971
+ rows: rows2,
10019
9972
  disabled,
10020
9973
  error: fieldError,
10021
9974
  onChange: handleChange,
@@ -10500,6 +10453,91 @@ var init_ActionPalette = __esm({
10500
10453
  ActionPalette.displayName = "ActionPalette";
10501
10454
  }
10502
10455
  });
10456
+ function parseValue(value) {
10457
+ if (value === "" || value == null) return { num: 0, prefix: "", suffix: "", decimals: 0 };
10458
+ const match = String(value).match(/^([^0-9]*)([0-9]+(?:\.[0-9]+)?)(.*)$/);
10459
+ if (!match) {
10460
+ return { num: 0, prefix: "", suffix: String(value), decimals: 0 };
10461
+ }
10462
+ const numStr = match[2];
10463
+ const decimalIdx = numStr.indexOf(".");
10464
+ const decimals = decimalIdx >= 0 ? numStr.length - decimalIdx - 1 : 0;
10465
+ return {
10466
+ prefix: match[1],
10467
+ num: parseFloat(numStr),
10468
+ suffix: match[3],
10469
+ decimals
10470
+ };
10471
+ }
10472
+ var AnimatedCounter;
10473
+ var init_AnimatedCounter = __esm({
10474
+ "components/core/molecules/AnimatedCounter.tsx"() {
10475
+ "use client";
10476
+ init_cn();
10477
+ init_Box();
10478
+ init_Typography();
10479
+ AnimatedCounter = ({
10480
+ value,
10481
+ label,
10482
+ duration = 1500,
10483
+ className
10484
+ }) => {
10485
+ const ref = React84.useRef(null);
10486
+ const [displayValue, setDisplayValue] = React84.useState("0");
10487
+ const [hasAnimated, setHasAnimated] = React84.useState(false);
10488
+ const animate = React84.useCallback(() => {
10489
+ const { num: num2, prefix, suffix, decimals } = parseValue(value);
10490
+ if (num2 === 0) {
10491
+ setDisplayValue(String(value));
10492
+ return;
10493
+ }
10494
+ const startTime = performance.now();
10495
+ const tick = (now2) => {
10496
+ const elapsed = now2 - startTime;
10497
+ const progress = Math.min(elapsed / duration, 1);
10498
+ const eased = 1 - Math.pow(1 - progress, 3);
10499
+ const current = eased * num2;
10500
+ setDisplayValue(`${prefix}${current.toFixed(decimals)}${suffix}`);
10501
+ if (progress < 1) {
10502
+ requestAnimationFrame(tick);
10503
+ } else {
10504
+ setDisplayValue(String(value));
10505
+ }
10506
+ };
10507
+ requestAnimationFrame(tick);
10508
+ }, [value, duration]);
10509
+ React84.useEffect(() => {
10510
+ if (hasAnimated) return;
10511
+ const el = ref.current;
10512
+ if (!el) return;
10513
+ const observer2 = new IntersectionObserver(
10514
+ (entries) => {
10515
+ if (entries[0].isIntersecting) {
10516
+ setHasAnimated(true);
10517
+ animate();
10518
+ observer2.disconnect();
10519
+ }
10520
+ },
10521
+ { threshold: 0.3 }
10522
+ );
10523
+ observer2.observe(el);
10524
+ return () => observer2.disconnect();
10525
+ }, [hasAnimated, animate]);
10526
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { ref, className: cn("flex flex-col items-center gap-1 p-4", className), children: [
10527
+ /* @__PURE__ */ jsxRuntime.jsx(
10528
+ Typography,
10529
+ {
10530
+ variant: "h2",
10531
+ className: "text-primary font-bold tabular-nums",
10532
+ children: hasAnimated ? displayValue : "0"
10533
+ }
10534
+ ),
10535
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", color: "muted", className: "text-center", children: label })
10536
+ ] });
10537
+ };
10538
+ AnimatedCounter.displayName = "AnimatedCounter";
10539
+ }
10540
+ });
10503
10541
  var AuthLayout;
10504
10542
  var init_AuthLayout = __esm({
10505
10543
  "components/core/templates/AuthLayout.tsx"() {
@@ -11853,6 +11891,39 @@ var init_IsometricCanvas2 = __esm({
11853
11891
  init_IsometricCanvas();
11854
11892
  }
11855
11893
  });
11894
+
11895
+ // components/game/organisms/boardEntity.ts
11896
+ function boardEntity(entity) {
11897
+ if (!entity) return void 0;
11898
+ return Array.isArray(entity) ? entity[0] : entity;
11899
+ }
11900
+ function str(v) {
11901
+ return v == null ? "" : String(v);
11902
+ }
11903
+ function num(v, fallback = 0) {
11904
+ const n = Number(v);
11905
+ return Number.isFinite(n) ? n : fallback;
11906
+ }
11907
+ function rows(v) {
11908
+ return Array.isArray(v) ? v : [];
11909
+ }
11910
+ function vec2(v) {
11911
+ const o = v ?? {};
11912
+ return { x: num(o.x), y: num(o.y) };
11913
+ }
11914
+ function unitPosition(u) {
11915
+ return vec2(u.position);
11916
+ }
11917
+ function unitTeam(u) {
11918
+ return str(u.team);
11919
+ }
11920
+ function unitHealth(u) {
11921
+ return num(u.health);
11922
+ }
11923
+ var init_boardEntity = __esm({
11924
+ "components/game/organisms/boardEntity.ts"() {
11925
+ }
11926
+ });
11856
11927
  function BattleBoard({
11857
11928
  entity,
11858
11929
  scale = 0.45,
@@ -11879,43 +11950,49 @@ function BattleBoard({
11879
11950
  attackEvent,
11880
11951
  className
11881
11952
  }) {
11882
- const tiles = entity.tiles;
11883
- const features = entity.features ?? [];
11884
- const boardWidth = entity.boardWidth ?? 8;
11885
- const boardHeight = entity.boardHeight ?? 6;
11886
- const assetManifest = entity.assetManifest;
11887
- const backgroundImage = entity.backgroundImage;
11888
- const units = entity.units;
11889
- const selectedUnitId = entity.selectedUnitId;
11890
- const currentPhase = entity.phase;
11891
- const currentTurn = entity.turn;
11892
- const gameResult = entity.gameResult;
11953
+ const board = boardEntity(entity) ?? {};
11954
+ const tiles = Array.isArray(board.tiles) ? board.tiles : [];
11955
+ const features = Array.isArray(board.features) ? board.features : [];
11956
+ const boardWidth = num(board.boardWidth, 8);
11957
+ const boardHeight = num(board.boardHeight, 6);
11958
+ const assetManifest = board.assetManifest;
11959
+ const backgroundImage = board.backgroundImage;
11960
+ const units = rows(board.units);
11961
+ const selectedUnitId = board.selectedUnitId ?? null;
11962
+ const currentPhase = str(board.phase) || "observation";
11963
+ const currentTurn = num(board.turn, 1);
11964
+ const gameResult = board.gameResult ?? null;
11893
11965
  const eventBus = useEventBus();
11894
11966
  const { t } = hooks.useTranslate();
11895
11967
  const [hoveredTile, setHoveredTile] = React84.useState(null);
11896
11968
  const [isShaking, setIsShaking] = React84.useState(false);
11897
11969
  const selectedUnit = React84.useMemo(
11898
- () => units.find((u) => u.id === selectedUnitId) ?? null,
11970
+ () => units.find((u) => str(u.id) === selectedUnitId) ?? null,
11899
11971
  [units, selectedUnitId]
11900
11972
  );
11901
11973
  const hoveredUnit = React84.useMemo(() => {
11902
11974
  if (!hoveredTile) return null;
11903
- return units.find(
11904
- (u) => u.position.x === hoveredTile.x && u.position.y === hoveredTile.y && u.health > 0
11905
- ) ?? null;
11975
+ return units.find((u) => {
11976
+ const p2 = unitPosition(u);
11977
+ return p2.x === hoveredTile.x && p2.y === hoveredTile.y && unitHealth(u) > 0;
11978
+ }) ?? null;
11906
11979
  }, [hoveredTile, units]);
11907
- const playerUnits = React84.useMemo(() => units.filter((u) => u.team === "player" && u.health > 0), [units]);
11908
- const enemyUnits = React84.useMemo(() => units.filter((u) => u.team === "enemy" && u.health > 0), [units]);
11980
+ const playerUnits = React84.useMemo(() => units.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0), [units]);
11981
+ const enemyUnits = React84.useMemo(() => units.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0), [units]);
11909
11982
  const validMoves = React84.useMemo(() => {
11910
11983
  if (!selectedUnit || currentPhase !== "movement") return [];
11911
11984
  const moves = [];
11912
- const range = selectedUnit.movement;
11985
+ const range = num(selectedUnit.movement);
11986
+ const origin = unitPosition(selectedUnit);
11913
11987
  for (let dy = -range; dy <= range; dy++) {
11914
11988
  for (let dx = -range; dx <= range; dx++) {
11915
- const nx = selectedUnit.position.x + dx;
11916
- const ny = selectedUnit.position.y + dy;
11989
+ const nx = origin.x + dx;
11990
+ const ny = origin.y + dy;
11917
11991
  const dist = Math.abs(dx) + Math.abs(dy);
11918
- 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)) {
11992
+ if (dist > 0 && dist <= range && nx >= 0 && nx < boardWidth && ny >= 0 && ny < boardHeight && !units.some((u) => {
11993
+ const p2 = unitPosition(u);
11994
+ return p2.x === nx && p2.y === ny && unitHealth(u) > 0;
11995
+ })) {
11919
11996
  moves.push({ x: nx, y: ny });
11920
11997
  }
11921
11998
  }
@@ -11924,11 +12001,14 @@ function BattleBoard({
11924
12001
  }, [selectedUnit, currentPhase, units, boardWidth, boardHeight]);
11925
12002
  const attackTargets = React84.useMemo(() => {
11926
12003
  if (!selectedUnit || currentPhase !== "action") return [];
11927
- return units.filter((u) => u.team !== selectedUnit.team && u.health > 0).filter((u) => {
11928
- const dx = Math.abs(u.position.x - selectedUnit.position.x);
11929
- const dy = Math.abs(u.position.y - selectedUnit.position.y);
12004
+ const sp = unitPosition(selectedUnit);
12005
+ const sTeam = unitTeam(selectedUnit);
12006
+ return units.filter((u) => unitTeam(u) !== sTeam && unitHealth(u) > 0).filter((u) => {
12007
+ const p2 = unitPosition(u);
12008
+ const dx = Math.abs(p2.x - sp.x);
12009
+ const dy = Math.abs(p2.y - sp.y);
11930
12010
  return dx <= 1 && dy <= 1 && dx + dy > 0;
11931
- }).map((u) => u.position);
12011
+ }).map((u) => unitPosition(u));
11932
12012
  }, [selectedUnit, currentPhase, units]);
11933
12013
  const MOVE_SPEED_MS_PER_TILE = 300;
11934
12014
  const movementAnimRef = React84.useRef(null);
@@ -11968,23 +12048,25 @@ function BattleBoard({
11968
12048
  return () => clearInterval(interval);
11969
12049
  }, []);
11970
12050
  const isoUnits = React84.useMemo(() => {
11971
- return units.filter((u) => u.health > 0).map((unit) => {
11972
- const pos = movingPositions.get(unit.id) ?? unit.position;
12051
+ return units.filter((u) => unitHealth(u) > 0).map((unit) => {
12052
+ const id = str(unit.id);
12053
+ const pos = movingPositions.get(id) ?? unitPosition(unit);
12054
+ const unitTraits = Array.isArray(unit.traits) ? unit.traits : void 0;
11973
12055
  return {
11974
- id: unit.id,
12056
+ id,
11975
12057
  position: pos,
11976
- name: unit.name,
11977
- team: unit.team,
11978
- health: unit.health,
11979
- maxHealth: unit.maxHealth,
11980
- unitType: unit.unitType,
11981
- heroId: unit.heroId,
11982
- sprite: unit.sprite,
11983
- traits: unit.traits?.map((t2) => ({
11984
- name: t2.name,
11985
- currentState: t2.currentState,
11986
- states: t2.states,
11987
- cooldown: t2.cooldown ?? 0
12058
+ name: str(unit.name),
12059
+ team: unitTeam(unit),
12060
+ health: unitHealth(unit),
12061
+ maxHealth: num(unit.maxHealth),
12062
+ unitType: unit.unitType == null ? void 0 : str(unit.unitType),
12063
+ heroId: unit.heroId == null ? void 0 : str(unit.heroId),
12064
+ sprite: unit.sprite == null ? void 0 : str(unit.sprite),
12065
+ traits: unitTraits?.map((tr) => ({
12066
+ name: tr.name,
12067
+ currentState: tr.currentState,
12068
+ states: tr.states,
12069
+ cooldown: tr.cooldown ?? 0
11988
12070
  }))
11989
12071
  };
11990
12072
  });
@@ -11996,8 +12078,8 @@ function BattleBoard({
11996
12078
  [scale, baseOffsetX]
11997
12079
  );
11998
12080
  const checkGameEnd = React84.useCallback(() => {
11999
- const pa = units.filter((u) => u.team === "player" && u.health > 0);
12000
- const ea = units.filter((u) => u.team === "enemy" && u.health > 0);
12081
+ const pa = units.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
12082
+ const ea = units.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
12001
12083
  if (pa.length === 0) {
12002
12084
  onGameEnd?.("defeat");
12003
12085
  if (gameEndEvent) {
@@ -12011,21 +12093,22 @@ function BattleBoard({
12011
12093
  }
12012
12094
  }, [units, onGameEnd, gameEndEvent, eventBus]);
12013
12095
  const handleUnitClick = React84.useCallback((unitId) => {
12014
- const unit = units.find((u) => u.id === unitId);
12096
+ const unit = units.find((u) => str(u.id) === unitId);
12015
12097
  if (!unit) return;
12016
12098
  if (unitClickEvent) {
12017
12099
  eventBus.emit(`UI:${unitClickEvent}`, { unitId });
12018
12100
  }
12019
12101
  if (currentPhase === "action" && selectedUnit) {
12020
- if (unit.team === "enemy" && attackTargets.some((t2) => t2.x === unit.position.x && t2.y === unit.position.y)) {
12021
- const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, selectedUnit.attack - unit.defense);
12102
+ const up = unitPosition(unit);
12103
+ if (unitTeam(unit) === "enemy" && attackTargets.some((t2) => t2.x === up.x && t2.y === up.y)) {
12104
+ const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, num(selectedUnit.attack) - num(unit.defense));
12022
12105
  setIsShaking(true);
12023
12106
  setTimeout(() => setIsShaking(false), 300);
12024
12107
  onAttack?.(selectedUnit, unit, damage);
12025
12108
  if (attackEvent) {
12026
12109
  eventBus.emit(`UI:${attackEvent}`, {
12027
- attackerId: selectedUnit.id,
12028
- targetId: unit.id,
12110
+ attackerId: str(selectedUnit.id),
12111
+ targetId: str(unit.id),
12029
12112
  damage
12030
12113
  });
12031
12114
  }
@@ -12040,9 +12123,9 @@ function BattleBoard({
12040
12123
  if (currentPhase === "movement" && selectedUnit) {
12041
12124
  if (movementAnimRef.current) return;
12042
12125
  if (validMoves.some((m) => m.x === x && m.y === y)) {
12043
- const from = { ...selectedUnit.position };
12126
+ const from = { ...unitPosition(selectedUnit) };
12044
12127
  const to = { x, y };
12045
- startMoveAnimation(selectedUnit.id, from, to, () => {
12128
+ startMoveAnimation(str(selectedUnit.id), from, to, () => {
12046
12129
  onUnitMove?.(selectedUnit, to);
12047
12130
  });
12048
12131
  }
@@ -12200,6 +12283,7 @@ var init_BattleBoard = __esm({
12200
12283
  init_Typography();
12201
12284
  init_Stack();
12202
12285
  init_IsometricCanvas2();
12286
+ init_boardEntity();
12203
12287
  init_isometric();
12204
12288
  BattleBoard.displayName = "BattleBoard";
12205
12289
  }
@@ -13422,24 +13506,24 @@ var init_CodeBlock = __esm({
13422
13506
  return;
13423
13507
  }
13424
13508
  lineEls.forEach((el) => {
13425
- const num = parseInt(el.getAttribute("data-line") ?? "-1", 10);
13426
- if (hiddenLines.has(num)) {
13509
+ const num2 = parseInt(el.getAttribute("data-line") ?? "-1", 10);
13510
+ if (hiddenLines.has(num2)) {
13427
13511
  el.style.display = "none";
13428
13512
  return;
13429
13513
  }
13430
13514
  el.style.display = "";
13431
13515
  el.style.position = "relative";
13432
13516
  el.style.paddingLeft = "1.2em";
13433
- const region = foldStartMap.get(num);
13517
+ const region = foldStartMap.get(num2);
13434
13518
  if (!region) return;
13435
- const isCollapsed = collapsed.has(num);
13519
+ const isCollapsed = collapsed.has(num2);
13436
13520
  const toggle = document.createElement("span");
13437
13521
  toggle.className = "fold-toggle";
13438
13522
  toggle.textContent = isCollapsed ? "\u25B6" : "\u25BC";
13439
13523
  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%";
13440
13524
  toggle.addEventListener("click", (e) => {
13441
13525
  e.stopPropagation();
13442
- toggleFoldRef.current(num);
13526
+ toggleFoldRef.current(num2);
13443
13527
  });
13444
13528
  el.insertBefore(toggle, el.firstChild);
13445
13529
  if (isCollapsed) {
@@ -15692,10 +15776,13 @@ var init_BookChapterView = __esm({
15692
15776
  init_cn();
15693
15777
  BookChapterView = ({
15694
15778
  chapter,
15779
+ orbitalSchema,
15695
15780
  direction,
15696
15781
  className
15697
15782
  }) => {
15698
15783
  const { t: _t } = hooks.useTranslate();
15784
+ const title = String(chapter.title ?? "");
15785
+ const content = String(chapter.content ?? "");
15699
15786
  return /* @__PURE__ */ jsxRuntime.jsxs(
15700
15787
  VStack,
15701
15788
  {
@@ -15703,16 +15790,16 @@ var init_BookChapterView = __esm({
15703
15790
  className: cn("px-6 py-8 max-w-4xl mx-auto w-full", className),
15704
15791
  style: { direction },
15705
15792
  children: [
15706
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h1", className: "text-3xl font-bold", children: chapter.title }),
15793
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h1", className: "text-3xl font-bold", children: title }),
15707
15794
  /* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
15708
- !!chapter.orbitalSchema && /* @__PURE__ */ jsxRuntime.jsx(ScaledDiagram, { children: /* @__PURE__ */ jsxRuntime.jsx(
15795
+ !!orbitalSchema && /* @__PURE__ */ jsxRuntime.jsx(ScaledDiagram, { children: /* @__PURE__ */ jsxRuntime.jsx(
15709
15796
  JazariStateMachine,
15710
15797
  {
15711
- schema: chapter.orbitalSchema,
15798
+ schema: orbitalSchema,
15712
15799
  direction
15713
15800
  }
15714
15801
  ) }),
15715
- /* @__PURE__ */ jsxRuntime.jsx(ContentRenderer, { content: chapter.content, direction })
15802
+ /* @__PURE__ */ jsxRuntime.jsx(ContentRenderer, { content, direction })
15716
15803
  ]
15717
15804
  }
15718
15805
  );
@@ -15810,7 +15897,7 @@ var init_BookNavBar = __esm({
15810
15897
  BookNavBar = ({
15811
15898
  currentPage,
15812
15899
  totalPages,
15813
- chapterTitle,
15900
+ chapterTitle: chapterTitle2,
15814
15901
  direction,
15815
15902
  className
15816
15903
  }) => {
@@ -15851,12 +15938,12 @@ var init_BookNavBar = __esm({
15851
15938
  )
15852
15939
  ] }),
15853
15940
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex-1 mx-4 max-w-md", children: [
15854
- chapterTitle && /* @__PURE__ */ jsxRuntime.jsx(
15941
+ chapterTitle2 && /* @__PURE__ */ jsxRuntime.jsx(
15855
15942
  Typography,
15856
15943
  {
15857
15944
  variant: "caption",
15858
15945
  className: "text-center block truncate text-muted-foreground",
15859
- children: chapterTitle
15946
+ children: chapterTitle2
15860
15947
  }
15861
15948
  ),
15862
15949
  /* @__PURE__ */ jsxRuntime.jsx(ProgressBar, { value: progress, size: "sm", variant: "primary" })
@@ -15923,31 +16010,35 @@ var init_BookTableOfContents = __esm({
15923
16010
  style: { direction },
15924
16011
  children: [
15925
16012
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h1", className: "text-3xl font-bold text-center mb-4", children: t("book.tableOfContents") }),
15926
- parts.map((part, partIdx) => /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
15927
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", align: "center", children: [
15928
- /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", children: t("book.partNumber", { number: String(partIdx + 1) }) }),
15929
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h3", className: "font-semibold", children: part.title })
15930
- ] }),
15931
- /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "xs", className: direction === "rtl" ? "pr-6" : "pl-6", children: part.chapters.map((chapter) => {
15932
- const isCurrent = chapter.id === currentChapterId;
15933
- return /* @__PURE__ */ jsxRuntime.jsx(
15934
- Button,
15935
- {
15936
- variant: "ghost",
15937
- size: "sm",
15938
- action: "BOOK_NAVIGATE",
15939
- actionPayload: { chapterId: chapter.id },
15940
- className: cn(
15941
- "justify-start text-left w-full",
15942
- direction === "rtl" && "text-right",
15943
- isCurrent && "bg-blue-50 dark:bg-blue-950 text-blue-600 dark:text-blue-400"
15944
- ),
15945
- children: /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "truncate", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: chapter.title }) })
15946
- },
15947
- chapter.id
15948
- );
15949
- }) })
15950
- ] }, partIdx))
16013
+ parts.map((part, partIdx) => {
16014
+ const chapters = Array.isArray(part.chapters) ? part.chapters : [];
16015
+ return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
16016
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", align: "center", children: [
16017
+ /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", children: t("book.partNumber", { number: String(partIdx + 1) }) }),
16018
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h3", className: "font-semibold", children: String(part.title ?? "") })
16019
+ ] }),
16020
+ /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "xs", className: direction === "rtl" ? "pr-6" : "pl-6", children: chapters.map((chapter) => {
16021
+ const id = chapter.id == null ? "" : String(chapter.id);
16022
+ const isCurrent = id === currentChapterId;
16023
+ return /* @__PURE__ */ jsxRuntime.jsx(
16024
+ Button,
16025
+ {
16026
+ variant: "ghost",
16027
+ size: "sm",
16028
+ action: "BOOK_NAVIGATE",
16029
+ actionPayload: { chapterId: id },
16030
+ className: cn(
16031
+ "justify-start text-left w-full",
16032
+ direction === "rtl" && "text-right",
16033
+ isCurrent && "bg-blue-50 dark:bg-blue-950 text-blue-600 dark:text-blue-400"
16034
+ ),
16035
+ children: /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "truncate", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: String(chapter.title ?? "") }) })
16036
+ },
16037
+ id
16038
+ );
16039
+ }) })
16040
+ ] }, partIdx);
16041
+ })
15951
16042
  ]
15952
16043
  }
15953
16044
  );
@@ -16069,27 +16160,41 @@ function resolveFieldMap(fieldMap) {
16069
16160
  function get(obj, key) {
16070
16161
  return obj[key];
16071
16162
  }
16163
+ function asStr(v) {
16164
+ return v == null ? "" : String(v);
16165
+ }
16072
16166
  function mapBookData(raw, fields = IDENTITY_BOOK_FIELDS) {
16073
16167
  const rawParts = get(raw, fields.parts) ?? [];
16074
- return {
16075
- title: get(raw, fields.title) ?? "",
16076
- subtitle: get(raw, fields.subtitle),
16077
- author: get(raw, fields.author),
16078
- coverImageUrl: get(raw, fields.coverImageUrl),
16079
- direction: get(raw, fields.direction) ?? void 0,
16080
- parts: rawParts.map((part) => {
16081
- const rawChapters = get(part, fields.chapters) ?? [];
16082
- return {
16083
- title: get(part, fields.partTitle) ?? "",
16084
- chapters: rawChapters.map((ch) => ({
16085
- id: get(ch, fields.chapterId) ?? "",
16086
- title: get(ch, fields.chapterTitle) ?? "",
16087
- content: get(ch, fields.chapterContent) ?? "",
16088
- orbitalSchema: get(ch, fields.chapterOrbitalSchema)
16089
- }))
16090
- };
16091
- })
16168
+ const direction = get(raw, fields.direction) ?? "ltr";
16169
+ const cover = {
16170
+ title: asStr(get(raw, fields.title)),
16171
+ subtitle: asStr(get(raw, fields.subtitle)),
16172
+ author: asStr(get(raw, fields.author)),
16173
+ coverImageUrl: asStr(get(raw, fields.coverImageUrl)),
16174
+ direction
16092
16175
  };
16176
+ const schemaByChapterId = {};
16177
+ const chapters = [];
16178
+ const parts = rawParts.map((part) => {
16179
+ const rawChapters = get(part, fields.chapters) ?? [];
16180
+ const chapterRows = rawChapters.map((ch) => {
16181
+ const id = asStr(get(ch, fields.chapterId));
16182
+ const schema = get(ch, fields.chapterOrbitalSchema);
16183
+ if (schema) schemaByChapterId[id] = schema;
16184
+ const row = {
16185
+ id,
16186
+ title: asStr(get(ch, fields.chapterTitle)),
16187
+ content: asStr(get(ch, fields.chapterContent))
16188
+ };
16189
+ chapters.push(row);
16190
+ return row;
16191
+ });
16192
+ return {
16193
+ title: asStr(get(part, fields.partTitle)),
16194
+ chapters: chapterRows
16195
+ };
16196
+ });
16197
+ return { cover, direction, parts, chapters, schemaByChapterId };
16093
16198
  }
16094
16199
  var IDENTITY_BOOK_FIELDS, AR_BOOK_FIELDS, FIELD_MAP_REGISTRY;
16095
16200
  var init_types2 = __esm({
@@ -16127,10 +16232,7 @@ var init_types2 = __esm({
16127
16232
  };
16128
16233
  }
16129
16234
  });
16130
- function flattenChapters(book) {
16131
- return book.parts.flatMap((part) => part.chapters);
16132
- }
16133
- var PRINT_STYLES, BookViewer;
16235
+ var chapterId, chapterTitle, PRINT_STYLES, BookViewer;
16134
16236
  var init_BookViewer = __esm({
16135
16237
  "components/marketing/organisms/book/BookViewer.tsx"() {
16136
16238
  init_Box();
@@ -16143,6 +16245,8 @@ var init_BookViewer = __esm({
16143
16245
  init_BookNavBar();
16144
16246
  init_EmptyState();
16145
16247
  init_types2();
16248
+ chapterId = (ch) => ch?.id == null ? void 0 : String(ch.id);
16249
+ chapterTitle = (ch) => ch?.title == null ? void 0 : String(ch.title);
16146
16250
  PRINT_STYLES = `
16147
16251
  @media print {
16148
16252
  .book-viewer-page {
@@ -16171,14 +16275,14 @@ var init_BookViewer = __esm({
16171
16275
  return mapBookData(raw, resolvedFieldMap);
16172
16276
  }, [entity, resolvedFieldMap]);
16173
16277
  const direction = book?.direction ?? "ltr";
16174
- const chapters = React84.useMemo(() => book ? flattenChapters(book) : [], [book]);
16278
+ const chapters = React84.useMemo(() => book ? book.chapters : [], [book]);
16175
16279
  const totalPages = 2 + chapters.length;
16176
16280
  const navigateTo = React84.useCallback(
16177
16281
  (page) => {
16178
16282
  const clamped = Math.max(0, Math.min(page, totalPages - 1));
16179
16283
  setCurrentPage(clamped);
16180
- const chapterId = clamped >= 2 ? chapters[clamped - 2]?.id : void 0;
16181
- eventBus.emit("UI:BOOK_PAGE_CHANGE", { pageIndex: clamped, chapterId });
16284
+ const id = clamped >= 2 ? chapterId(chapters[clamped - 2]) : void 0;
16285
+ eventBus.emit("UI:BOOK_PAGE_CHANGE", { pageIndex: clamped, chapterId: id });
16182
16286
  },
16183
16287
  [totalPages, chapters, eventBus]
16184
16288
  );
@@ -16190,8 +16294,8 @@ var init_BookViewer = __esm({
16190
16294
  eventBus.on("UI:BOOK_PAGE_NEXT", () => navigateTo(currentPage + 1)),
16191
16295
  eventBus.on("UI:BOOK_PRINT", () => window.print()),
16192
16296
  eventBus.on("UI:BOOK_NAVIGATE", (event) => {
16193
- const chapterId = event.payload?.chapterId;
16194
- const idx = chapters.findIndex((ch) => ch.id === chapterId);
16297
+ const targetId = event.payload?.chapterId;
16298
+ const idx = chapters.findIndex((ch) => chapterId(ch) === targetId);
16195
16299
  if (idx >= 0) navigateTo(idx + 2);
16196
16300
  })
16197
16301
  ];
@@ -16208,9 +16312,11 @@ var init_BookViewer = __esm({
16208
16312
  style.remove();
16209
16313
  };
16210
16314
  }, []);
16211
- const currentChapterId = currentPage >= 2 ? chapters[currentPage - 2]?.id : void 0;
16212
- const currentChapterTitle = currentPage >= 2 ? chapters[currentPage - 2]?.title : void 0;
16315
+ const currentChapterId = currentPage >= 2 ? chapterId(chapters[currentPage - 2]) : void 0;
16316
+ const currentChapterTitle = currentPage >= 2 ? chapterTitle(chapters[currentPage - 2]) : void 0;
16213
16317
  if (!book) return /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { message: t("book.noData") });
16318
+ const cover = book.cover;
16319
+ const coverTitle = String(cover.title ?? "");
16214
16320
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("relative h-full overflow-hidden bg-background", className), children: [
16215
16321
  /* @__PURE__ */ jsxRuntime.jsxs(
16216
16322
  Box,
@@ -16222,10 +16328,10 @@ var init_BookViewer = __esm({
16222
16328
  /* @__PURE__ */ jsxRuntime.jsx(
16223
16329
  BookCoverPage,
16224
16330
  {
16225
- title: book.title,
16226
- subtitle: book.subtitle,
16227
- author: book.author,
16228
- coverImageUrl: book.coverImageUrl,
16331
+ title: coverTitle,
16332
+ subtitle: String(cover.subtitle ?? "") || void 0,
16333
+ author: String(cover.author ?? "") || void 0,
16334
+ coverImageUrl: String(cover.coverImageUrl ?? "") || void 0,
16229
16335
  direction
16230
16336
  }
16231
16337
  ),
@@ -16236,23 +16342,27 @@ var init_BookViewer = __esm({
16236
16342
  direction
16237
16343
  }
16238
16344
  ),
16239
- chapters.map((chapter) => /* @__PURE__ */ jsxRuntime.jsx(
16240
- BookChapterView,
16241
- {
16242
- chapter,
16243
- direction
16244
- },
16245
- chapter.id
16246
- ))
16345
+ chapters.map((chapter) => {
16346
+ const id = chapterId(chapter);
16347
+ return /* @__PURE__ */ jsxRuntime.jsx(
16348
+ BookChapterView,
16349
+ {
16350
+ chapter,
16351
+ orbitalSchema: id ? book.schemaByChapterId[id] : void 0,
16352
+ direction
16353
+ },
16354
+ id
16355
+ );
16356
+ })
16247
16357
  ] }),
16248
16358
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "print:hidden", children: [
16249
16359
  currentPage === 0 && /* @__PURE__ */ jsxRuntime.jsx(
16250
16360
  BookCoverPage,
16251
16361
  {
16252
- title: book.title,
16253
- subtitle: book.subtitle,
16254
- author: book.author,
16255
- coverImageUrl: book.coverImageUrl,
16362
+ title: coverTitle,
16363
+ subtitle: String(cover.subtitle ?? "") || void 0,
16364
+ author: String(cover.author ?? "") || void 0,
16365
+ coverImageUrl: String(cover.coverImageUrl ?? "") || void 0,
16256
16366
  direction
16257
16367
  }
16258
16368
  ),
@@ -16268,6 +16378,7 @@ var init_BookViewer = __esm({
16268
16378
  BookChapterView,
16269
16379
  {
16270
16380
  chapter: chapters[currentPage - 2],
16381
+ orbitalSchema: currentChapterId ? book.schemaByChapterId[currentChapterId] : void 0,
16271
16382
  direction
16272
16383
  }
16273
16384
  )
@@ -16280,7 +16391,7 @@ var init_BookViewer = __esm({
16280
16391
  {
16281
16392
  currentPage,
16282
16393
  totalPages,
16283
- chapterTitle: currentPage === 0 ? book.title : currentPage === 1 ? t("book.tableOfContents") : currentChapterTitle,
16394
+ chapterTitle: currentPage === 0 ? coverTitle : currentPage === 1 ? t("book.tableOfContents") : currentChapterTitle,
16284
16395
  direction
16285
16396
  }
16286
16397
  )
@@ -16378,7 +16489,7 @@ var init_Grid = __esm({
16378
16489
  };
16379
16490
  Grid = ({
16380
16491
  cols = 1,
16381
- rows,
16492
+ rows: rows2,
16382
16493
  gap = "md",
16383
16494
  rowGap,
16384
16495
  colGap,
@@ -16390,7 +16501,7 @@ var init_Grid = __esm({
16390
16501
  children,
16391
16502
  as: Component = "div"
16392
16503
  }) => {
16393
- const mergedStyle = rows ? { gridTemplateRows: `repeat(${rows}, minmax(0, 1fr))`, ...style } : style;
16504
+ const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
16394
16505
  return React84__namespace.default.createElement(
16395
16506
  Component,
16396
16507
  {
@@ -17106,14 +17217,14 @@ function BuilderBoard({
17106
17217
  }) {
17107
17218
  const { emit } = useEventBus();
17108
17219
  const { t } = hooks.useTranslate();
17109
- const resolved = Array.isArray(entity) ? entity[0] : entity;
17220
+ const resolved = boardEntity(entity);
17110
17221
  const [placements, setPlacements] = React84.useState({});
17111
17222
  const [headerError, setHeaderError] = React84.useState(false);
17112
17223
  const [submitted, setSubmitted] = React84.useState(false);
17113
17224
  const [attempts, setAttempts] = React84.useState(0);
17114
17225
  const [showHint, setShowHint] = React84.useState(false);
17115
- const components = resolved?.components ?? [];
17116
- const slots = resolved?.slots ?? [];
17226
+ const components = Array.isArray(resolved?.components) ? resolved.components : [];
17227
+ const slots = Array.isArray(resolved?.slots) ? resolved.slots : [];
17117
17228
  const usedComponentIds = new Set(Object.values(placements));
17118
17229
  const availableComponents = components.filter((c) => !usedComponentIds.has(c.id));
17119
17230
  const [selectedComponent, setSelectedComponent] = React84.useState(null);
@@ -17147,7 +17258,7 @@ function BuilderBoard({
17147
17258
  }, [slots, placements, attempts, completeEvent, emit]);
17148
17259
  const handleReset = () => {
17149
17260
  setSubmitted(false);
17150
- if (attempts >= 2 && resolved?.hint) {
17261
+ if (attempts >= 2 && str(resolved?.hint)) {
17151
17262
  setShowHint(true);
17152
17263
  }
17153
17264
  };
@@ -17160,20 +17271,24 @@ function BuilderBoard({
17160
17271
  };
17161
17272
  const getComponentById = (id) => components.find((c) => c.id === id);
17162
17273
  if (!resolved) return null;
17274
+ const theme = resolved.theme ?? void 0;
17275
+ const themeBackground = theme?.background;
17276
+ const headerImage = str(resolved.headerImage);
17277
+ const hint = str(resolved.hint);
17163
17278
  return /* @__PURE__ */ jsxRuntime.jsx(
17164
17279
  Box,
17165
17280
  {
17166
17281
  className,
17167
17282
  style: {
17168
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
17283
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
17169
17284
  backgroundSize: "cover",
17170
17285
  backgroundPosition: "center"
17171
17286
  },
17172
17287
  children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: "p-4", children: [
17173
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
17288
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
17174
17289
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
17175
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
17176
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.description })
17290
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
17291
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str(resolved.description) })
17177
17292
  ] }) }),
17178
17293
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
17179
17294
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("builder.components") }),
@@ -17233,9 +17348,9 @@ function BuilderBoard({
17233
17348
  ] }) }),
17234
17349
  submitted && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", children: [
17235
17350
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: allCorrect ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
17236
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("builder.success") : resolved.failMessage ?? t("builder.incorrect") })
17351
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("builder.success") : str(resolved.failMessage) || t("builder.incorrect") })
17237
17352
  ] }) }),
17238
- showHint && resolved.hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.hint }) }),
17353
+ showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
17239
17354
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
17240
17355
  !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
17241
17356
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Wrench, size: "sm" }),
@@ -17254,6 +17369,7 @@ var init_BuilderBoard = __esm({
17254
17369
  "components/game/organisms/puzzles/builder/BuilderBoard.tsx"() {
17255
17370
  init_atoms2();
17256
17371
  init_useEventBus();
17372
+ init_boardEntity();
17257
17373
  BuilderBoard.displayName = "BuilderBoard";
17258
17374
  }
17259
17375
  });
@@ -17591,21 +17707,24 @@ function CalendarGrid({
17591
17707
  eventBus.emit(`UI:${longPressEvent}`, { date: day.toISOString(), time, ...longPressPayload });
17592
17708
  }, 500);
17593
17709
  }, [longPressEvent, longPressPayload, eventBus]);
17594
- const renderEvent = (event) => /* @__PURE__ */ jsxRuntime.jsx(
17595
- Box,
17596
- {
17597
- rounded: "md",
17598
- padding: "xs",
17599
- border: true,
17600
- className: cn(
17601
- "cursor-pointer hover:shadow-sm transition-shadow text-xs truncate",
17602
- event.color ? event.color : "bg-blue-500/15 border-blue-500/30 text-blue-600"
17603
- ),
17604
- onClick: (e) => handleEventClick(event, e),
17605
- children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "truncate font-medium", children: event.title })
17606
- },
17607
- event.id
17608
- );
17710
+ const renderEvent = (event) => {
17711
+ const color = event.color;
17712
+ return /* @__PURE__ */ jsxRuntime.jsx(
17713
+ Box,
17714
+ {
17715
+ rounded: "md",
17716
+ padding: "xs",
17717
+ border: true,
17718
+ className: cn(
17719
+ "cursor-pointer hover:shadow-sm transition-shadow text-xs truncate",
17720
+ color ? color : "bg-blue-500/15 border-blue-500/30 text-blue-600"
17721
+ ),
17722
+ onClick: (e) => handleEventClick(event, e),
17723
+ children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "truncate font-medium", children: event.title })
17724
+ },
17725
+ event.id
17726
+ );
17727
+ };
17609
17728
  return /* @__PURE__ */ jsxRuntime.jsxs(
17610
17729
  Box,
17611
17730
  {
@@ -19269,7 +19388,6 @@ var init_CardGrid = __esm({
19269
19388
  alignItems = "stretch",
19270
19389
  className,
19271
19390
  children,
19272
- // EntityDisplayProps
19273
19391
  entity,
19274
19392
  isLoading = false,
19275
19393
  error = null,
@@ -19741,14 +19859,14 @@ var init_CaseStudyOrganism = __esm({
19741
19859
  /* @__PURE__ */ jsxRuntime.jsx(SimpleGrid, { cols: cols > 0 ? cols : 1, gap: "lg", children: items.map((study) => /* @__PURE__ */ jsxRuntime.jsx(
19742
19860
  CaseStudyCard,
19743
19861
  {
19744
- title: study.title,
19745
- description: study.description,
19746
- category: study.category,
19747
- categoryColor: study.categoryColor,
19748
- href: study.href,
19749
- linkLabel: study.linkLabel
19862
+ title: String(study.title ?? ""),
19863
+ description: String(study.description ?? ""),
19864
+ category: String(study.category ?? ""),
19865
+ categoryColor: study.categoryColor != null ? String(study.categoryColor) : void 0,
19866
+ href: String(study.href ?? ""),
19867
+ linkLabel: study.linkLabel != null ? String(study.linkLabel) : void 0
19750
19868
  },
19751
- study.id
19869
+ String(study.id ?? "")
19752
19870
  )) })
19753
19871
  ] });
19754
19872
  };
@@ -19771,10 +19889,10 @@ function CastleBoard({
19771
19889
  className
19772
19890
  }) {
19773
19891
  const eventBus = useEventBus();
19774
- const resolved = Array.isArray(entity) ? entity[0] : entity;
19775
- const tiles = resolved?.tiles ?? [];
19776
- const features = resolved?.features ?? [];
19777
- const units = resolved?.units ?? [];
19892
+ const resolved = boardEntity(entity);
19893
+ const tiles = Array.isArray(resolved?.tiles) ? resolved.tiles : [];
19894
+ const features = Array.isArray(resolved?.features) ? resolved.features : [];
19895
+ const units = Array.isArray(resolved?.units) ? resolved.units : [];
19778
19896
  const assetManifest = resolved?.assetManifest;
19779
19897
  const backgroundImage = resolved?.backgroundImage;
19780
19898
  const [hoveredTile, setHoveredTile] = React84.useState(null);
@@ -19802,7 +19920,7 @@ function CastleBoard({
19802
19920
  onFeatureClick?.(feature);
19803
19921
  if (featureClickEvent) {
19804
19922
  eventBus.emit(`UI:${featureClickEvent}`, {
19805
- featureId: feature.id,
19923
+ featureId: feature.id ?? "",
19806
19924
  featureType: feature.type,
19807
19925
  x: feature.x,
19808
19926
  y: feature.y
@@ -19870,6 +19988,7 @@ var init_CastleBoard = __esm({
19870
19988
  init_cn();
19871
19989
  init_useEventBus();
19872
19990
  init_IsometricCanvas2();
19991
+ init_boardEntity();
19873
19992
  init_isometric();
19874
19993
  CastleBoard.displayName = "CastleBoard";
19875
19994
  }
@@ -20680,14 +20799,14 @@ function ClassifierBoard({
20680
20799
  }) {
20681
20800
  const { emit } = useEventBus();
20682
20801
  const { t } = hooks.useTranslate();
20683
- const resolved = Array.isArray(entity) ? entity[0] : entity;
20802
+ const resolved = boardEntity(entity);
20684
20803
  const [assignments, setAssignments] = React84.useState({});
20685
20804
  const [headerError, setHeaderError] = React84.useState(false);
20686
20805
  const [submitted, setSubmitted] = React84.useState(false);
20687
20806
  const [attempts, setAttempts] = React84.useState(0);
20688
20807
  const [showHint, setShowHint] = React84.useState(false);
20689
- const items = resolved?.items ?? [];
20690
- const categories = resolved?.categories ?? [];
20808
+ const items = Array.isArray(resolved?.items) ? resolved.items : [];
20809
+ const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
20691
20810
  const unassignedItems = items.filter((item) => !assignments[item.id]);
20692
20811
  const allAssigned = Object.keys(assignments).length === items.length;
20693
20812
  const results = submitted ? items.map((item) => ({
@@ -20719,7 +20838,7 @@ function ClassifierBoard({
20719
20838
  }, [items, assignments, attempts, completeEvent, emit]);
20720
20839
  const handleReset = () => {
20721
20840
  setSubmitted(false);
20722
- if (attempts >= 2 && resolved?.hint) {
20841
+ if (attempts >= 2 && str(resolved?.hint)) {
20723
20842
  setShowHint(true);
20724
20843
  }
20725
20844
  };
@@ -20730,20 +20849,25 @@ function ClassifierBoard({
20730
20849
  setShowHint(false);
20731
20850
  };
20732
20851
  if (!resolved) return null;
20852
+ const theme = resolved.theme ?? void 0;
20853
+ const themeBackground = theme?.background;
20854
+ const headerImage = str(resolved.headerImage);
20855
+ const hint = str(resolved.hint);
20856
+ const failMessage = str(resolved.failMessage);
20733
20857
  return /* @__PURE__ */ jsxRuntime.jsx(
20734
20858
  Box,
20735
20859
  {
20736
20860
  className,
20737
20861
  style: {
20738
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
20862
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
20739
20863
  backgroundSize: "cover",
20740
20864
  backgroundPosition: "center"
20741
20865
  },
20742
20866
  children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: "p-4", children: [
20743
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
20867
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
20744
20868
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
20745
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
20746
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.description })
20869
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
20870
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str(resolved.description) })
20747
20871
  ] }) }),
20748
20872
  unassignedItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
20749
20873
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("classifier.itemsToSort") }),
@@ -20795,10 +20919,10 @@ function ClassifierBoard({
20795
20919
  }) }),
20796
20920
  submitted && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", children: [
20797
20921
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: allCorrect ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
20798
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("classifier.allCorrect") : `${correctCount}/${items.length} ${t("classifier.correct")}` }),
20799
- !allCorrect && resolved.failMessage && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: "text-muted-foreground", children: resolved.failMessage })
20922
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("classifier.allCorrect") : `${correctCount}/${items.length} ${t("classifier.correct")}` }),
20923
+ !allCorrect && failMessage && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: "text-muted-foreground", children: failMessage })
20800
20924
  ] }) }),
20801
- showHint && resolved.hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.hint }) }),
20925
+ showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
20802
20926
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
20803
20927
  !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
20804
20928
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Send, size: "sm" }),
@@ -20817,6 +20941,7 @@ var init_ClassifierBoard = __esm({
20817
20941
  "components/game/organisms/puzzles/classifier/ClassifierBoard.tsx"() {
20818
20942
  init_atoms2();
20819
20943
  init_useEventBus();
20944
+ init_boardEntity();
20820
20945
  ClassifierBoard.displayName = "ClassifierBoard";
20821
20946
  }
20822
20947
  });
@@ -27187,7 +27312,7 @@ function InventoryPanel({
27187
27312
  const slotArray = Array.from({ length: safeSlots }, (_, index) => {
27188
27313
  return safeItems[index] ?? null;
27189
27314
  });
27190
- const rows = Math.ceil(safeSlots / safeColumns);
27315
+ const rows2 = Math.ceil(safeSlots / safeColumns);
27191
27316
  const handleSlotClick = React84.useCallback((index) => {
27192
27317
  if (selectSlotEvent) eventBus.emit(`UI:${selectSlotEvent}`, { index });
27193
27318
  onSelectSlot?.(index);
@@ -27256,7 +27381,7 @@ function InventoryPanel({
27256
27381
  className: "grid gap-1 bg-[var(--color-card)] p-2 rounded-container border border-border",
27257
27382
  style: {
27258
27383
  gridTemplateColumns: `repeat(${safeColumns}, ${slotSize}px)`,
27259
- gridTemplateRows: `repeat(${rows}, ${slotSize}px)`
27384
+ gridTemplateRows: `repeat(${rows2}, ${slotSize}px)`
27260
27385
  },
27261
27386
  children: slotArray.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
27262
27387
  "button",
@@ -31220,11 +31345,11 @@ function LatticeSVG({
31220
31345
  }) {
31221
31346
  const paths = [];
31222
31347
  const cols = 5;
31223
- const rows = Math.ceil(h / (w / cols));
31348
+ const rows2 = Math.ceil(h / (w / cols));
31224
31349
  const cellW = w / cols;
31225
31350
  const cellH = cellW;
31226
31351
  const bulge = cellW * 0.3;
31227
- for (let row = 0; row < rows; row++) {
31352
+ for (let row = 0; row < rows2; row++) {
31228
31353
  for (let col = 0; col < cols; col++) {
31229
31354
  const cx = col * cellW + cellW / 2;
31230
31355
  const cy = row * cellH + cellH / 2;
@@ -31636,7 +31761,7 @@ var init_MatrixQuestion = __esm({
31636
31761
  };
31637
31762
  MatrixQuestion = ({
31638
31763
  title,
31639
- rows,
31764
+ rows: rows2,
31640
31765
  columns = DEFAULT_MATRIX_COLUMNS,
31641
31766
  values,
31642
31767
  onChange,
@@ -31646,7 +31771,7 @@ var init_MatrixQuestion = __esm({
31646
31771
  className
31647
31772
  }) => {
31648
31773
  const styles = sizeStyles13[size];
31649
- const safeRows = rows ?? [];
31774
+ const safeRows = rows2 ?? [];
31650
31775
  const safeValues = values ?? {};
31651
31776
  const eventBus = useEventBus();
31652
31777
  const handleChange = React84.useCallback(
@@ -32274,7 +32399,8 @@ var init_PositionedCanvas = __esm({
32274
32399
  dragRef.current = null;
32275
32400
  setDraggingId(null);
32276
32401
  if (!wasDrag) {
32277
- const next = selectedId === item.id ? null : item.id;
32402
+ const itemId = item.id;
32403
+ const next = selectedId === itemId ? null : itemId;
32278
32404
  onSelect?.(next);
32279
32405
  if (selectEvent) {
32280
32406
  eventBus.emit(`UI:${selectEvent}`, { id: next });
@@ -32309,15 +32435,22 @@ var init_PositionedCanvas = __esm({
32309
32435
  style: { width, height },
32310
32436
  onClick: handleContainerClick,
32311
32437
  children: items.map((item) => {
32438
+ const itemId = item.id;
32439
+ const label = item.label;
32440
+ const x = item.x;
32441
+ const y = item.y;
32442
+ const capacity = item.capacity;
32443
+ const partySize = item.partySize;
32444
+ const serverName = item.serverName;
32312
32445
  const status = item.status ?? "empty";
32313
32446
  const shape = item.shape ?? "round";
32314
- const isSelected = selectedId === item.id;
32315
- const isDragging = draggingId === item.id;
32447
+ const isSelected = selectedId === itemId;
32448
+ const isDragging = draggingId === itemId;
32316
32449
  const statusBadge = STATUS_BADGE[status];
32317
32450
  return /* @__PURE__ */ jsxRuntime.jsxs(
32318
32451
  Box,
32319
32452
  {
32320
- "data-testid": `item-node-${item.id}`,
32453
+ "data-testid": `item-node-${itemId}`,
32321
32454
  "data-status": status,
32322
32455
  className: cn(
32323
32456
  "absolute flex flex-col items-center justify-center gap-1 border-2 select-none",
@@ -32328,7 +32461,7 @@ var init_PositionedCanvas = __esm({
32328
32461
  isSelected && "outline outline-2 outline-offset-2 outline-primary shadow-md",
32329
32462
  isDragging && "shadow-lg z-10"
32330
32463
  ),
32331
- style: { left: item.x, top: item.y, touchAction: "none" },
32464
+ style: { left: x, top: y, touchAction: "none" },
32332
32465
  onPointerDown: (e) => handlePointerDown(e, item),
32333
32466
  onPointerMove: handlePointerMove,
32334
32467
  onPointerUp: (e) => handlePointerUp(e, item),
@@ -32336,10 +32469,10 @@ var init_PositionedCanvas = __esm({
32336
32469
  children: [
32337
32470
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-1", children: [
32338
32471
  getStatusIcon(status),
32339
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", weight: "semibold", children: item.label })
32472
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", weight: "semibold", children: label })
32340
32473
  ] }),
32341
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", children: item.partySize !== void 0 && status === "seated" ? `${item.partySize}/${item.capacity}` : `Cap ${item.capacity}` }),
32342
- status === "seated" && item.serverName && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", className: "truncate max-w-[80%]", children: item.serverName }),
32474
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", children: partySize !== void 0 && status === "seated" ? `${partySize}/${capacity}` : `Cap ${capacity}` }),
32475
+ status === "seated" && serverName && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", className: "truncate max-w-[80%]", children: serverName }),
32343
32476
  isSelected && /* @__PURE__ */ jsxRuntime.jsx(
32344
32477
  Badge,
32345
32478
  {
@@ -32351,7 +32484,7 @@ var init_PositionedCanvas = __esm({
32351
32484
  )
32352
32485
  ]
32353
32486
  },
32354
- item.id
32487
+ itemId
32355
32488
  );
32356
32489
  })
32357
32490
  }
@@ -33123,9 +33256,10 @@ var init_RichBlockEditor = __esm({
33123
33256
  });
33124
33257
  function collectInitiallyCollapsed(nodes, acc) {
33125
33258
  for (const n of nodes) {
33259
+ const replies = n.replies;
33126
33260
  if (n.collapsed) acc.add(n.id);
33127
- if (n.replies && n.replies.length > 0) {
33128
- collectInitiallyCollapsed(n.replies, acc);
33261
+ if (replies && replies.length > 0) {
33262
+ collectInitiallyCollapsed(replies, acc);
33129
33263
  }
33130
33264
  }
33131
33265
  }
@@ -33155,44 +33289,52 @@ var init_ReplyTree = __esm({
33155
33289
  }) => {
33156
33290
  const eventBus = useEventBus();
33157
33291
  const { t } = hooks.useTranslate();
33158
- const hasReplies = !!node.replies && node.replies.length > 0;
33159
- const isCollapsed = collapsedSet.has(node.id);
33292
+ const nodeId = node.id;
33293
+ const authorName = node.authorName;
33294
+ const authorAvatarUrl = node.authorAvatarUrl;
33295
+ const content = node.content;
33296
+ const postedAt = node.postedAt;
33297
+ const voteCount = node.voteCount;
33298
+ const userVote = node.userVote;
33299
+ const replies = node.replies;
33300
+ const hasReplies = !!replies && replies.length > 0;
33301
+ const isCollapsed = collapsedSet.has(nodeId);
33160
33302
  const atMaxDepth = depth >= maxDepth;
33161
33303
  const [replyOpen, setReplyOpen] = React84.useState(false);
33162
33304
  const [draft, setDraft] = React84.useState("");
33163
33305
  const handleVote = React84.useCallback(
33164
33306
  (next) => {
33165
- onVote?.(node.id, next);
33166
- if (voteEvent) eventBus.emit(`UI:${voteEvent}`, { nodeId: node.id, vote: next });
33307
+ onVote?.(nodeId, next);
33308
+ if (voteEvent) eventBus.emit(`UI:${voteEvent}`, { nodeId, vote: next });
33167
33309
  },
33168
- [node.id, onVote, voteEvent, eventBus]
33310
+ [nodeId, onVote, voteEvent, eventBus]
33169
33311
  );
33170
33312
  const handleReply = React84.useCallback(() => {
33171
- onReply?.(node.id);
33313
+ onReply?.(nodeId);
33172
33314
  setReplyOpen((open) => !open);
33173
- }, [node.id, onReply]);
33315
+ }, [nodeId, onReply]);
33174
33316
  const handleSubmitReply = React84.useCallback(() => {
33175
- const content = draft.trim();
33176
- if (!content) return;
33177
- if (replyEvent) eventBus.emit(`UI:${replyEvent}`, { parentNodeId: node.id, content });
33317
+ const text = draft.trim();
33318
+ if (!text) return;
33319
+ if (replyEvent) eventBus.emit(`UI:${replyEvent}`, { parentNodeId: nodeId, content: text });
33178
33320
  setDraft("");
33179
33321
  setReplyOpen(false);
33180
- }, [node.id, draft, replyEvent, eventBus]);
33322
+ }, [nodeId, draft, replyEvent, eventBus]);
33181
33323
  const handleCancelReply = React84.useCallback(() => {
33182
33324
  setDraft("");
33183
33325
  setReplyOpen(false);
33184
33326
  }, []);
33185
33327
  const handleFlag = React84.useCallback(() => {
33186
- onFlag?.(node.id);
33187
- if (flagEvent) eventBus.emit(`UI:${flagEvent}`, { nodeId: node.id });
33188
- }, [node.id, onFlag, flagEvent, eventBus]);
33328
+ onFlag?.(nodeId);
33329
+ if (flagEvent) eventBus.emit(`UI:${flagEvent}`, { nodeId });
33330
+ }, [nodeId, onFlag, flagEvent, eventBus]);
33189
33331
  const handleContinue = React84.useCallback(() => {
33190
- onContinueThread?.(node.id);
33191
- if (continueThreadEvent) eventBus.emit(`UI:${continueThreadEvent}`, { nodeId: node.id });
33192
- }, [node.id, onContinueThread, continueThreadEvent, eventBus]);
33332
+ onContinueThread?.(nodeId);
33333
+ if (continueThreadEvent) eventBus.emit(`UI:${continueThreadEvent}`, { nodeId });
33334
+ }, [nodeId, onContinueThread, continueThreadEvent, eventBus]);
33193
33335
  const handleToggle = React84.useCallback(() => {
33194
- toggleCollapse(node.id);
33195
- }, [node.id, toggleCollapse]);
33336
+ toggleCollapse(nodeId);
33337
+ }, [nodeId, toggleCollapse]);
33196
33338
  return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex flex-row gap-2 items-stretch min-w-0", children: [
33197
33339
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex flex-col items-center flex-shrink-0 w-6", children: [
33198
33340
  hasReplies ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -33224,25 +33366,25 @@ var init_ReplyTree = __esm({
33224
33366
  /* @__PURE__ */ jsxRuntime.jsx(
33225
33367
  Avatar,
33226
33368
  {
33227
- src: node.authorAvatarUrl,
33228
- name: node.authorName,
33369
+ src: authorAvatarUrl,
33370
+ name: authorName,
33229
33371
  size: "sm"
33230
33372
  }
33231
33373
  ),
33232
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "semibold", children: node.authorName }),
33233
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", children: node.postedAt })
33374
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "semibold", children: authorName }),
33375
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", children: postedAt })
33234
33376
  ] }),
33235
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: "whitespace-pre-wrap break-words", children: node.content }),
33377
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: "whitespace-pre-wrap break-words", children: content }),
33236
33378
  showActions && /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex flex-row gap-2 items-center", children: [
33237
33379
  /* @__PURE__ */ jsxRuntime.jsx(
33238
33380
  VoteStack,
33239
33381
  {
33240
- count: node.voteCount ?? 0,
33241
- userVote: node.userVote ?? null,
33382
+ count: voteCount ?? 0,
33383
+ userVote: userVote ?? null,
33242
33384
  onVote: handleVote,
33243
33385
  size: "sm",
33244
33386
  variant: "horizontal",
33245
- label: t("replyTree.voteOnReplyBy", { author: node.authorName })
33387
+ label: t("replyTree.voteOnReplyBy", { author: authorName })
33246
33388
  }
33247
33389
  ),
33248
33390
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -33252,7 +33394,7 @@ var init_ReplyTree = __esm({
33252
33394
  size: "sm",
33253
33395
  leftIcon: "message-square",
33254
33396
  onClick: handleReply,
33255
- "aria-label": t("replyTree.replyTo", { author: node.authorName }),
33397
+ "aria-label": t("replyTree.replyTo", { author: authorName }),
33256
33398
  children: t("replyTree.reply")
33257
33399
  }
33258
33400
  ),
@@ -33263,7 +33405,7 @@ var init_ReplyTree = __esm({
33263
33405
  size: "sm",
33264
33406
  leftIcon: "flag",
33265
33407
  onClick: handleFlag,
33266
- "aria-label": t("replyTree.flagReplyBy", { author: node.authorName }),
33408
+ "aria-label": t("replyTree.flagReplyBy", { author: authorName }),
33267
33409
  children: t("replyTree.flag")
33268
33410
  }
33269
33411
  )
@@ -33275,9 +33417,9 @@ var init_ReplyTree = __esm({
33275
33417
  inputType: "textarea",
33276
33418
  rows: 2,
33277
33419
  value: draft,
33278
- placeholder: t("replyTree.replyToPlaceholder", { author: node.authorName }),
33420
+ placeholder: t("replyTree.replyToPlaceholder", { author: authorName }),
33279
33421
  onChange: (e) => setDraft(e.target.value),
33280
- "aria-label": t("replyTree.replyTo", { author: node.authorName })
33422
+ "aria-label": t("replyTree.replyTo", { author: authorName })
33281
33423
  }
33282
33424
  ),
33283
33425
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex flex-row gap-2 items-center", children: [
@@ -33308,7 +33450,7 @@ var init_ReplyTree = __esm({
33308
33450
  ),
33309
33451
  children: t("replyTree.continueThread")
33310
33452
  }
33311
- ) : /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex flex-col gap-2 mt-1", children: node.replies.map((child) => /* @__PURE__ */ jsxRuntime.jsx(
33453
+ ) : /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex flex-col gap-2 mt-1", children: replies.map((child) => /* @__PURE__ */ jsxRuntime.jsx(
33312
33454
  ReplyTreeNode,
33313
33455
  {
33314
33456
  node: child,
@@ -35628,8 +35770,8 @@ var init_WizardContainer = __esm({
35628
35770
  return void 0;
35629
35771
  if (typeof controlledStep === "number") return controlledStep;
35630
35772
  if (typeof controlledStep === "string") return parseInt(controlledStep, 10);
35631
- const num = Number(controlledStep);
35632
- return isNaN(num) ? void 0 : num;
35773
+ const num2 = Number(controlledStep);
35774
+ return isNaN(num2) ? void 0 : num2;
35633
35775
  })();
35634
35776
  const currentStep = normalizedControlledStep !== void 0 ? normalizedControlledStep : internalStep;
35635
35777
  const totalSteps = steps.length;
@@ -37334,7 +37476,7 @@ function DebuggerBoard({
37334
37476
  }) {
37335
37477
  const { emit } = useEventBus();
37336
37478
  const { t } = hooks.useTranslate();
37337
- const resolved = Array.isArray(entity) ? entity[0] : entity;
37479
+ const resolved = boardEntity(entity);
37338
37480
  const [flaggedLines, setFlaggedLines] = React84.useState(/* @__PURE__ */ new Set());
37339
37481
  const [headerError, setHeaderError] = React84.useState(false);
37340
37482
  const [submitted, setSubmitted] = React84.useState(false);
@@ -37352,7 +37494,7 @@ function DebuggerBoard({
37352
37494
  return next;
37353
37495
  });
37354
37496
  };
37355
- const lines = resolved?.lines ?? [];
37497
+ const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
37356
37498
  const bugLines = lines.filter((l) => l.isBug);
37357
37499
  const correctFlags = lines.filter((l) => l.isBug && flaggedLines.has(l.id));
37358
37500
  const falseFlags = lines.filter((l) => !l.isBug && flaggedLines.has(l.id));
@@ -37367,7 +37509,7 @@ function DebuggerBoard({
37367
37509
  }, [correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
37368
37510
  const handleReset = () => {
37369
37511
  setSubmitted(false);
37370
- if (attempts >= 2 && resolved?.hint) {
37512
+ if (attempts >= 2 && str(resolved?.hint)) {
37371
37513
  setShowHint(true);
37372
37514
  }
37373
37515
  };
@@ -37378,24 +37520,28 @@ function DebuggerBoard({
37378
37520
  setShowHint(false);
37379
37521
  };
37380
37522
  if (!resolved) return null;
37523
+ const theme = resolved.theme ?? void 0;
37524
+ const themeBackground = theme?.background;
37525
+ const headerImage = str(resolved.headerImage);
37526
+ const hint = str(resolved.hint);
37381
37527
  return /* @__PURE__ */ jsxRuntime.jsx(
37382
37528
  Box,
37383
37529
  {
37384
37530
  className,
37385
37531
  style: {
37386
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
37532
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
37387
37533
  backgroundSize: "cover",
37388
37534
  backgroundPosition: "center"
37389
37535
  },
37390
37536
  children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: "p-4", children: [
37391
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
37537
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
37392
37538
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
37393
37539
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
37394
37540
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Bug, size: "sm" }),
37395
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title })
37541
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) })
37396
37542
  ] }),
37397
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.description }),
37398
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(resolved.bugCount) }) })
37543
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str(resolved.description) }),
37544
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(num(resolved.bugCount)) }) })
37399
37545
  ] }) }),
37400
37546
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "none", children: lines.map((line, i) => {
37401
37547
  const isFlagged = flaggedLines.has(line.id);
@@ -37427,7 +37573,7 @@ function DebuggerBoard({
37427
37573
  );
37428
37574
  }) }) }),
37429
37575
  submitted && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
37430
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? resolved.successMessage ?? t("debugger.allFound") : `${correctFlags.length}/${bugLines.length} ${t("debugger.bugsFound")}` }),
37576
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("debugger.allFound") : `${correctFlags.length}/${bugLines.length} ${t("debugger.bugsFound")}` }),
37431
37577
  bugLines.map((line) => /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "start", children: [
37432
37578
  /* @__PURE__ */ jsxRuntime.jsx(
37433
37579
  Icon,
@@ -37443,7 +37589,7 @@ function DebuggerBoard({
37443
37589
  ] })
37444
37590
  ] }, line.id))
37445
37591
  ] }) }),
37446
- showHint && resolved.hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.hint }) }),
37592
+ showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
37447
37593
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
37448
37594
  !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.size === 0, children: [
37449
37595
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Send, size: "sm" }),
@@ -37462,6 +37608,7 @@ var init_DebuggerBoard = __esm({
37462
37608
  "components/game/organisms/puzzles/debugger/DebuggerBoard.tsx"() {
37463
37609
  init_atoms2();
37464
37610
  init_useEventBus();
37611
+ init_boardEntity();
37465
37612
  DebuggerBoard.displayName = "DebuggerBoard";
37466
37613
  }
37467
37614
  });
@@ -37499,7 +37646,7 @@ function getBadgeVariant(fieldName, value) {
37499
37646
  return "default";
37500
37647
  }
37501
37648
  function formatFieldLabel(fieldName) {
37502
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase());
37649
+ return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str2) => str2.toUpperCase());
37503
37650
  }
37504
37651
  function formatFieldValue(value, fieldName) {
37505
37652
  if (typeof value === "number") {
@@ -37518,26 +37665,26 @@ function formatFieldValue(value, fieldName) {
37518
37665
  }
37519
37666
  function renderRichFieldValue(value, fieldName, fieldType) {
37520
37667
  if (value === void 0 || value === null) return "\u2014";
37521
- const str = String(value);
37668
+ const str2 = String(value);
37522
37669
  switch (fieldType) {
37523
37670
  case "image":
37524
37671
  case "url": {
37525
- if (str.match(/\.(png|jpe?g|gif|svg|webp|avif)(\?|$)/i) || str.startsWith("data:image/")) {
37672
+ if (str2.match(/\.(png|jpe?g|gif|svg|webp|avif)(\?|$)/i) || str2.startsWith("data:image/")) {
37526
37673
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mt-1 max-w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
37527
37674
  "img",
37528
37675
  {
37529
- src: str,
37676
+ src: str2,
37530
37677
  alt: formatFieldLabel(fieldName),
37531
37678
  className: "max-w-full max-h-64 rounded-md object-contain",
37532
37679
  loading: "lazy"
37533
37680
  }
37534
37681
  ) });
37535
37682
  }
37536
- return str;
37683
+ return str2;
37537
37684
  }
37538
37685
  case "markdown":
37539
37686
  case "richtext":
37540
- return /* @__PURE__ */ jsxRuntime.jsx(React84.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: "break-words", children: str }), children: /* @__PURE__ */ jsxRuntime.jsx(
37687
+ return /* @__PURE__ */ jsxRuntime.jsx(React84.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: "break-words", children: str2 }), children: /* @__PURE__ */ jsxRuntime.jsx(
37541
37688
  Box,
37542
37689
  {
37543
37690
  className: "prose prose-sm max-w-none",
@@ -37555,11 +37702,11 @@ function renderRichFieldValue(value, fieldName, fieldType) {
37555
37702
  "--tw-prose-th-borders": "var(--color-border)",
37556
37703
  "--tw-prose-td-borders": "var(--color-border)"
37557
37704
  },
37558
- children: /* @__PURE__ */ jsxRuntime.jsx(ReactMarkdown2, { children: str })
37705
+ children: /* @__PURE__ */ jsxRuntime.jsx(ReactMarkdown2, { children: str2 })
37559
37706
  }
37560
37707
  ) });
37561
37708
  case "code":
37562
- return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mt-1 rounded-md bg-muted p-3 overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "text-sm font-mono whitespace-pre-wrap break-words m-0", children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: str }) }) });
37709
+ return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mt-1 rounded-md bg-muted p-3 overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "text-sm font-mono whitespace-pre-wrap break-words m-0", children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: str2 }) }) });
37563
37710
  case "html":
37564
37711
  return /* @__PURE__ */ jsxRuntime.jsx(
37565
37712
  Box,
@@ -37579,12 +37726,12 @@ function renderRichFieldValue(value, fieldName, fieldType) {
37579
37726
  "--tw-prose-th-borders": "var(--color-border)",
37580
37727
  "--tw-prose-td-borders": "var(--color-border)"
37581
37728
  },
37582
- children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str })
37729
+ children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str2 })
37583
37730
  }
37584
37731
  );
37585
37732
  case "date":
37586
37733
  case "datetime": {
37587
- const d = new Date(str);
37734
+ const d = new Date(str2);
37588
37735
  if (!isNaN(d.getTime())) {
37589
37736
  return d.toLocaleDateString(void 0, {
37590
37737
  year: "numeric",
@@ -37593,7 +37740,7 @@ function renderRichFieldValue(value, fieldName, fieldType) {
37593
37740
  ...fieldType === "datetime" ? { hour: "2-digit", minute: "2-digit" } : {}
37594
37741
  });
37595
37742
  }
37596
- return str;
37743
+ return str2;
37597
37744
  }
37598
37745
  default:
37599
37746
  return formatFieldValue(value, fieldName);
@@ -38271,6 +38418,40 @@ var init_RuleEditor = __esm({
38271
38418
  RuleEditor.displayName = "RuleEditor";
38272
38419
  }
38273
38420
  });
38421
+
38422
+ // components/game/organisms/puzzles/event-handler/puzzleObject.ts
38423
+ function objId(o) {
38424
+ return o.id == null ? "" : String(o.id);
38425
+ }
38426
+ function objName(o) {
38427
+ return o.name == null ? "" : String(o.name);
38428
+ }
38429
+ function objIcon(o) {
38430
+ return o.icon == null ? "" : String(o.icon);
38431
+ }
38432
+ function objStates(o) {
38433
+ return Array.isArray(o.states) ? o.states : [];
38434
+ }
38435
+ function objCurrentState(o) {
38436
+ return o.currentState == null ? "" : String(o.currentState);
38437
+ }
38438
+ function objAvailableEvents(o) {
38439
+ return Array.isArray(o.availableEvents) ? o.availableEvents : [];
38440
+ }
38441
+ function objAvailableActions(o) {
38442
+ return Array.isArray(o.availableActions) ? o.availableActions : [];
38443
+ }
38444
+ function objRules(o) {
38445
+ return Array.isArray(o.rules) ? o.rules : [];
38446
+ }
38447
+ function objMaxRules(o) {
38448
+ const n = Number(o.maxRules);
38449
+ return Number.isFinite(n) && n > 0 ? n : 3;
38450
+ }
38451
+ var init_puzzleObject = __esm({
38452
+ "components/game/organisms/puzzles/event-handler/puzzleObject.ts"() {
38453
+ }
38454
+ });
38274
38455
  function ObjectRulePanel({
38275
38456
  object,
38276
38457
  onRulesChange,
@@ -38278,55 +38459,63 @@ function ObjectRulePanel({
38278
38459
  className
38279
38460
  }) {
38280
38461
  const { t } = hooks.useTranslate();
38281
- const maxRules = object.maxRules || 3;
38282
- const canAdd = object.rules.length < maxRules;
38462
+ const id = objId(object);
38463
+ const name = objName(object);
38464
+ const icon = objIcon(object);
38465
+ const states = objStates(object);
38466
+ const currentState = objCurrentState(object);
38467
+ const availableEvents = objAvailableEvents(object);
38468
+ const availableActions = objAvailableActions(object);
38469
+ const rules = objRules(object);
38470
+ const maxRules = objMaxRules(object);
38471
+ const canAdd = rules.length < maxRules;
38283
38472
  const handleRuleChange = React84.useCallback((index, updatedRule) => {
38284
- const newRules = [...object.rules];
38473
+ const newRules = [...rules];
38285
38474
  newRules[index] = updatedRule;
38286
- onRulesChange(object.id, newRules);
38287
- }, [object.id, object.rules, onRulesChange]);
38475
+ onRulesChange(id, newRules);
38476
+ }, [id, rules, onRulesChange]);
38288
38477
  const handleRuleRemove = React84.useCallback((index) => {
38289
- const newRules = object.rules.filter((_, i) => i !== index);
38290
- onRulesChange(object.id, newRules);
38291
- }, [object.id, object.rules, onRulesChange]);
38478
+ const newRules = rules.filter((_, i) => i !== index);
38479
+ onRulesChange(id, newRules);
38480
+ }, [id, rules, onRulesChange]);
38292
38481
  const handleAddRule = React84.useCallback(() => {
38293
38482
  if (!canAdd || disabled) return;
38294
- const firstEvent = object.availableEvents[0]?.value || "";
38295
- const firstAction = object.availableActions[0]?.value || "";
38483
+ const firstEvent = availableEvents[0]?.value || "";
38484
+ const firstAction = availableActions[0]?.value || "";
38296
38485
  const newRule = {
38297
38486
  id: `rule-${nextRuleId++}`,
38298
38487
  whenEvent: firstEvent,
38299
38488
  thenAction: firstAction
38300
38489
  };
38301
- onRulesChange(object.id, [...object.rules, newRule]);
38302
- }, [canAdd, disabled, object, onRulesChange]);
38490
+ onRulesChange(id, [...rules, newRule]);
38491
+ }, [canAdd, disabled, id, rules, availableEvents, availableActions, onRulesChange]);
38303
38492
  const machine = {
38304
- name: object.name,
38305
- states: object.states,
38306
- currentState: object.currentState,
38307
- transitions: object.rules.map((r) => ({
38308
- from: object.currentState,
38309
- to: object.states.find((s) => s !== object.currentState) || object.currentState,
38493
+ name,
38494
+ states,
38495
+ currentState,
38496
+ transitions: rules.map((r) => ({
38497
+ from: currentState,
38498
+ to: states.find((s) => s !== currentState) || currentState,
38310
38499
  event: r.whenEvent
38311
38500
  }))
38312
38501
  };
38313
38502
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("p-4 rounded-lg bg-card border border-border", className), gap: "sm", children: [
38314
38503
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center", gap: "sm", children: [
38315
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", children: object.icon }),
38504
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", children: icon }),
38316
38505
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", children: [
38317
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-foreground font-bold", children: object.name }),
38318
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("eventHandler.state") + ": " + object.currentState })
38506
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-foreground font-bold", children: name }),
38507
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("eventHandler.state") + ": " + currentState })
38319
38508
  ] })
38320
38509
  ] }),
38321
38510
  /* @__PURE__ */ jsxRuntime.jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" }),
38322
38511
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
38323
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.rules", { count: object.rules.length, max: maxRules }) + ":" }),
38324
- object.rules.map((rule, i) => /* @__PURE__ */ jsxRuntime.jsx(
38512
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.rules", { count: rules.length, max: maxRules }) + ":" }),
38513
+ rules.map((rule, i) => /* @__PURE__ */ jsxRuntime.jsx(
38325
38514
  RuleEditor,
38326
38515
  {
38327
38516
  rule,
38328
- availableEvents: object.availableEvents,
38329
- availableActions: object.availableActions,
38517
+ availableEvents,
38518
+ availableActions,
38330
38519
  onChange: (r) => handleRuleChange(i, r),
38331
38520
  onRemove: () => handleRuleRemove(i),
38332
38521
  disabled
@@ -38344,6 +38533,7 @@ var init_ObjectRulePanel = __esm({
38344
38533
  init_cn();
38345
38534
  init_TraitStateViewer();
38346
38535
  init_RuleEditor();
38536
+ init_puzzleObject();
38347
38537
  nextRuleId = 1;
38348
38538
  ObjectRulePanel.displayName = "ObjectRulePanel";
38349
38539
  }
@@ -38410,11 +38600,11 @@ function EventHandlerBoard({
38410
38600
  }) {
38411
38601
  const { emit } = useEventBus();
38412
38602
  const { t } = hooks.useTranslate();
38413
- const resolved = Array.isArray(entity) ? entity[0] : entity;
38414
- const entityObjects = resolved?.objects ?? [];
38415
- const [objects, setObjects] = React84.useState(entityObjects);
38603
+ const resolved = boardEntity(entity);
38604
+ const entityObjects = rows(resolved?.objects);
38605
+ const [objects, setObjects] = React84.useState(() => [...entityObjects]);
38416
38606
  const [selectedObjectId, setSelectedObjectId] = React84.useState(
38417
- entityObjects[0]?.id || null
38607
+ entityObjects[0] ? objId(entityObjects[0]) : null
38418
38608
  );
38419
38609
  const [headerError, setHeaderError] = React84.useState(false);
38420
38610
  const [playState, setPlayState] = React84.useState("editing");
@@ -38425,10 +38615,10 @@ function EventHandlerBoard({
38425
38615
  React84.useEffect(() => () => {
38426
38616
  if (timerRef.current) clearTimeout(timerRef.current);
38427
38617
  }, []);
38428
- const selectedObject = objects.find((o) => o.id === selectedObjectId) || null;
38618
+ const selectedObject = objects.find((o) => objId(o) === selectedObjectId) || null;
38429
38619
  const handleRulesChange = React84.useCallback((objectId, rules) => {
38430
38620
  setObjects((prev) => prev.map(
38431
- (o) => o.id === objectId ? { ...o, rules } : o
38621
+ (o) => objId(o) === objectId ? { ...o, rules } : o
38432
38622
  ));
38433
38623
  }, []);
38434
38624
  const addLogEntry = React84.useCallback((icon, message, status = "done") => {
@@ -38442,11 +38632,12 @@ function EventHandlerBoard({
38442
38632
  setEventLog([]);
38443
38633
  const allRules = [];
38444
38634
  objects.forEach((obj) => {
38445
- obj.rules.forEach((rule) => {
38635
+ objRules(obj).forEach((rule) => {
38446
38636
  allRules.push({ object: obj, rule });
38447
38637
  });
38448
38638
  });
38449
- const triggers = resolved?.triggerEvents || [];
38639
+ const triggers = Array.isArray(resolved?.triggerEvents) ? resolved.triggerEvents : [];
38640
+ const goalEvent = str(resolved?.goalEvent);
38450
38641
  const eventQueue = [...triggers];
38451
38642
  const firedEvents = /* @__PURE__ */ new Set();
38452
38643
  let stepIdx = 0;
@@ -38475,14 +38666,14 @@ function EventHandlerBoard({
38475
38666
  addLogEntry("\u26A1", t("eventHandler.noListeners", { event: currentEvent }), "done");
38476
38667
  } else {
38477
38668
  matching.forEach(({ object, rule }) => {
38478
- addLogEntry(object.icon, t("eventHandler.heardEvent", { object: object.name, event: currentEvent, action: rule.thenAction }), "done");
38669
+ addLogEntry(objIcon(object), t("eventHandler.heardEvent", { object: objName(object), event: currentEvent, action: rule.thenAction }), "done");
38479
38670
  eventQueue.push(rule.thenAction);
38480
- if (rule.thenAction === resolved?.goalEvent) {
38671
+ if (rule.thenAction === goalEvent) {
38481
38672
  goalReached = true;
38482
38673
  }
38483
38674
  });
38484
38675
  }
38485
- if (currentEvent === resolved?.goalEvent) {
38676
+ if (currentEvent === goalEvent) {
38486
38677
  goalReached = true;
38487
38678
  }
38488
38679
  stepIdx++;
@@ -38500,65 +38691,75 @@ function EventHandlerBoard({
38500
38691
  }, []);
38501
38692
  const handleReset = React84.useCallback(() => {
38502
38693
  if (timerRef.current) clearTimeout(timerRef.current);
38503
- setObjects(resolved?.objects ?? []);
38694
+ const resetObjects = rows(resolved?.objects);
38695
+ setObjects([...resetObjects]);
38504
38696
  setPlayState("editing");
38505
38697
  setEventLog([]);
38506
- setSelectedObjectId((resolved?.objects ?? [])[0]?.id || null);
38698
+ setSelectedObjectId(resetObjects[0] ? objId(resetObjects[0]) : null);
38507
38699
  setAttempts(0);
38508
38700
  }, [resolved?.objects]);
38509
38701
  if (!resolved) return null;
38510
38702
  const objectViewers = objects.map((obj) => {
38703
+ const states = objStates(obj);
38704
+ const currentState = objCurrentState(obj);
38511
38705
  const machine = {
38512
- name: obj.name,
38513
- states: obj.states,
38514
- currentState: obj.currentState,
38515
- transitions: obj.rules.map((r) => ({
38516
- from: obj.currentState,
38517
- to: obj.states.find((s) => s !== obj.currentState) || obj.currentState,
38706
+ name: objName(obj),
38707
+ states,
38708
+ currentState,
38709
+ transitions: objRules(obj).map((r) => ({
38710
+ from: currentState,
38711
+ to: states.find((s) => s !== currentState) || currentState,
38518
38712
  event: r.whenEvent
38519
38713
  }))
38520
38714
  };
38521
38715
  return { obj, machine };
38522
38716
  });
38523
- const showHint = attempts >= 3 && resolved.hint;
38717
+ const hint = str(resolved.hint);
38718
+ const showHint = attempts >= 3 && hint;
38719
+ const theme = resolved.theme ?? void 0;
38720
+ const themeBackground = theme?.background;
38721
+ const headerImage = str(resolved.headerImage);
38524
38722
  const encourageKey = ENCOURAGEMENT_KEYS[Math.min(attempts - 1, ENCOURAGEMENT_KEYS.length - 1)] ?? ENCOURAGEMENT_KEYS[0];
38525
38723
  return /* @__PURE__ */ jsxRuntime.jsxs(
38526
38724
  VStack,
38527
38725
  {
38528
38726
  className: cn("p-4 gap-6", className),
38529
38727
  style: {
38530
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
38728
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
38531
38729
  backgroundSize: "cover",
38532
38730
  backgroundPosition: "center"
38533
38731
  },
38534
38732
  children: [
38535
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
38733
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
38536
38734
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
38537
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
38538
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description }),
38735
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
38736
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) }),
38539
38737
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center p-2 rounded bg-primary/10 border border-primary/30", gap: "xs", children: [
38540
38738
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-primary font-bold", children: t("game.goal") + ":" }),
38541
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-foreground", children: resolved.goalCondition })
38739
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-foreground", children: str(resolved.goalCondition) })
38542
38740
  ] })
38543
38741
  ] }),
38544
38742
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
38545
38743
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.clickObject") + ":" }),
38546
- /* @__PURE__ */ jsxRuntime.jsx(HStack, { className: "flex-wrap", gap: "sm", children: objectViewers.map(({ obj, machine }) => /* @__PURE__ */ jsxRuntime.jsx(
38547
- Box,
38548
- {
38549
- className: cn(
38550
- "p-3 rounded-container border-2 cursor-pointer transition-all hover:scale-105",
38551
- selectedObjectId === obj.id ? "border-primary bg-primary/10" : "border-border bg-card hover:border-muted-foreground"
38552
- ),
38553
- onClick: () => setSelectedObjectId(obj.id),
38554
- children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "items-center min-w-[120px]", children: [
38555
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", children: obj.icon }),
38556
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground font-medium", children: obj.name }),
38557
- /* @__PURE__ */ jsxRuntime.jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" })
38558
- ] })
38559
- },
38560
- obj.id
38561
- )) })
38744
+ /* @__PURE__ */ jsxRuntime.jsx(HStack, { className: "flex-wrap", gap: "sm", children: objectViewers.map(({ obj, machine }) => {
38745
+ const oid = objId(obj);
38746
+ return /* @__PURE__ */ jsxRuntime.jsx(
38747
+ Box,
38748
+ {
38749
+ className: cn(
38750
+ "p-3 rounded-container border-2 cursor-pointer transition-all hover:scale-105",
38751
+ selectedObjectId === oid ? "border-primary bg-primary/10" : "border-border bg-card hover:border-muted-foreground"
38752
+ ),
38753
+ onClick: () => setSelectedObjectId(oid),
38754
+ children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "items-center min-w-[120px]", children: [
38755
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", children: objIcon(obj) }),
38756
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground font-medium", children: objName(obj) }),
38757
+ /* @__PURE__ */ jsxRuntime.jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" })
38758
+ ] })
38759
+ },
38760
+ oid
38761
+ );
38762
+ }) })
38562
38763
  ] }),
38563
38764
  selectedObject && /* @__PURE__ */ jsxRuntime.jsx(
38564
38765
  ObjectRulePanel,
@@ -38569,12 +38770,12 @@ function EventHandlerBoard({
38569
38770
  }
38570
38771
  ),
38571
38772
  eventLog.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(EventLog, { entries: eventLog }),
38572
- playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: resolved.successMessage || t("eventHandler.chainComplete") }) }),
38773
+ playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("eventHandler.chainComplete") }) }),
38573
38774
  playState === "fail" && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
38574
38775
  /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(encourageKey) }) }),
38575
38776
  showHint && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-start", gap: "xs", children: [
38576
38777
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
38577
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
38778
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
38578
38779
  ] }) })
38579
38780
  ] }),
38580
38781
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
@@ -38602,6 +38803,8 @@ var init_EventHandlerBoard = __esm({
38602
38803
  init_TraitStateViewer();
38603
38804
  init_ObjectRulePanel();
38604
38805
  init_EventLog();
38806
+ init_puzzleObject();
38807
+ init_boardEntity();
38605
38808
  ENCOURAGEMENT_KEYS = [
38606
38809
  "puzzle.tryAgain1",
38607
38810
  "puzzle.tryAgain2",
@@ -38690,7 +38893,10 @@ var init_FeatureGridOrganism = __esm({
38690
38893
  );
38691
38894
  React84.useCallback(
38692
38895
  (feature) => {
38693
- eventBus.emit("UI:FEATURE_CLICK", { id: feature.id, href: feature.href ?? "" });
38896
+ eventBus.emit("UI:FEATURE_CLICK", {
38897
+ id: String(feature.id ?? ""),
38898
+ href: String(feature.href ?? "")
38899
+ });
38694
38900
  },
38695
38901
  [eventBus]
38696
38902
  );
@@ -38700,14 +38906,17 @@ var init_FeatureGridOrganism = __esm({
38700
38906
  if (error) {
38701
38907
  return /* @__PURE__ */ jsxRuntime.jsx(ErrorState, { message: error.message, className });
38702
38908
  }
38703
- const featureCards = items.map((feature) => ({
38704
- icon: feature.icon,
38705
- title: feature.title,
38706
- description: feature.description,
38707
- href: feature.href,
38708
- linkLabel: feature.linkLabel,
38709
- variant: feature.href ? "interactive" : "bordered"
38710
- }));
38909
+ const featureCards = items.map((feature) => {
38910
+ const href = feature.href != null ? String(feature.href) : void 0;
38911
+ return {
38912
+ icon: feature.icon != null ? String(feature.icon) : void 0,
38913
+ title: String(feature.title ?? ""),
38914
+ description: String(feature.description ?? ""),
38915
+ href,
38916
+ linkLabel: feature.linkLabel != null ? String(feature.linkLabel) : void 0,
38917
+ variant: href ? "interactive" : "bordered"
38918
+ };
38919
+ });
38711
38920
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
38712
38921
  (heading || subtitle) && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
38713
38922
  heading && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", align: "center", children: heading }),
@@ -40025,22 +40234,24 @@ var init_HeroOrganism = __esm({
40025
40234
  () => Array.isArray(entity) ? entity[0] : entity && typeof entity === "object" ? entity : void 0,
40026
40235
  [entity]
40027
40236
  );
40237
+ const primaryAction = resolved?.primaryAction;
40238
+ const secondaryAction = resolved?.secondaryAction;
40028
40239
  const handlePrimaryClick = React84.useCallback(() => {
40029
- if (resolved?.primaryAction) {
40240
+ if (primaryAction) {
40030
40241
  eventBus.emit("UI:CTA_PRIMARY", {
40031
- label: resolved.primaryAction.label,
40032
- href: resolved.primaryAction.href
40242
+ label: String(primaryAction.label ?? ""),
40243
+ href: String(primaryAction.href ?? "")
40033
40244
  });
40034
40245
  }
40035
- }, [eventBus, resolved]);
40246
+ }, [eventBus, primaryAction]);
40036
40247
  const handleSecondaryClick = React84.useCallback(() => {
40037
- if (resolved?.secondaryAction) {
40248
+ if (secondaryAction) {
40038
40249
  eventBus.emit("UI:CTA_SECONDARY", {
40039
- label: resolved.secondaryAction.label,
40040
- href: resolved.secondaryAction.href
40250
+ label: String(secondaryAction.label ?? ""),
40251
+ href: String(secondaryAction.href ?? "")
40041
40252
  });
40042
40253
  }
40043
- }, [eventBus, resolved]);
40254
+ }, [eventBus, secondaryAction]);
40044
40255
  if (isLoading) {
40045
40256
  return /* @__PURE__ */ jsxRuntime.jsx(LoadingState, { message: t("common.loading"), className });
40046
40257
  }
@@ -40050,17 +40261,19 @@ var init_HeroOrganism = __esm({
40050
40261
  if (!resolved) {
40051
40262
  return null;
40052
40263
  }
40264
+ const imageRaw = resolved.image;
40265
+ const image = imageRaw ? { src: String(imageRaw.src ?? ""), alt: String(imageRaw.alt ?? "") } : void 0;
40053
40266
  return /* @__PURE__ */ jsxRuntime.jsxs(
40054
40267
  HeroSection,
40055
40268
  {
40056
- tag: resolved.tag,
40057
- title: resolved.title,
40058
- titleAccent: resolved.titleAccent,
40059
- subtitle: resolved.subtitle,
40060
- primaryAction: resolved.primaryAction ? { label: resolved.primaryAction.label, href: resolved.primaryAction.href } : void 0,
40061
- secondaryAction: resolved.secondaryAction ? { label: resolved.secondaryAction.label, href: resolved.secondaryAction.href } : void 0,
40062
- installCommand: resolved.installCommand,
40063
- image: resolved.image,
40269
+ tag: resolved.tag != null ? String(resolved.tag) : void 0,
40270
+ title: String(resolved.title ?? ""),
40271
+ titleAccent: resolved.titleAccent != null ? String(resolved.titleAccent) : void 0,
40272
+ subtitle: String(resolved.subtitle ?? ""),
40273
+ primaryAction: primaryAction ? { label: String(primaryAction.label ?? ""), href: String(primaryAction.href ?? "") } : void 0,
40274
+ secondaryAction: secondaryAction ? { label: String(secondaryAction.label ?? ""), href: String(secondaryAction.href ?? "") } : void 0,
40275
+ installCommand: resolved.installCommand != null ? String(resolved.installCommand) : void 0,
40276
+ image,
40064
40277
  imagePosition: resolved.imagePosition,
40065
40278
  background: resolved.background,
40066
40279
  className: cn(className),
@@ -40069,8 +40282,8 @@ var init_HeroOrganism = __esm({
40069
40282
  /* @__PURE__ */ jsxRuntime.jsx(
40070
40283
  _HeroClickInterceptor,
40071
40284
  {
40072
- hasPrimary: !!resolved.primaryAction,
40073
- hasSecondary: !!resolved.secondaryAction,
40285
+ hasPrimary: !!primaryAction,
40286
+ hasSecondary: !!secondaryAction,
40074
40287
  onPrimaryClick: handlePrimaryClick,
40075
40288
  onSecondaryClick: handleSecondaryClick
40076
40289
  }
@@ -40299,7 +40512,7 @@ function formatValue3(value, fieldName) {
40299
40512
  return String(value);
40300
40513
  }
40301
40514
  function formatFieldLabel2(fieldName) {
40302
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).replace(/Id$/, "").trim();
40515
+ return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str2) => str2.toUpperCase()).replace(/Id$/, "").trim();
40303
40516
  }
40304
40517
  var STATUS_STYLES2, StatusBadge, ProgressIndicator, List3;
40305
40518
  var init_List = __esm({
@@ -41146,20 +41359,22 @@ function NegotiatorBoard({
41146
41359
  }) {
41147
41360
  const { emit } = useEventBus();
41148
41361
  const { t } = hooks.useTranslate();
41149
- const resolved = Array.isArray(entity) ? entity[0] : entity;
41362
+ const resolved = boardEntity(entity);
41150
41363
  const [history, setHistory] = React84.useState([]);
41151
41364
  const [headerError, setHeaderError] = React84.useState(false);
41152
41365
  const [showHint, setShowHint] = React84.useState(false);
41366
+ const totalRounds = num(resolved?.totalRounds);
41367
+ const targetScore = num(resolved?.targetScore);
41153
41368
  const currentRound = history.length;
41154
- const isComplete = currentRound >= (resolved?.totalRounds ?? 0);
41369
+ const isComplete = currentRound >= totalRounds;
41155
41370
  const playerTotal = history.reduce((s, r) => s + r.playerPayoff, 0);
41156
41371
  const opponentTotal = history.reduce((s, r) => s + r.opponentPayoff, 0);
41157
- const won = isComplete && playerTotal >= (resolved?.targetScore ?? 0);
41158
- const actions = resolved?.actions ?? [];
41159
- const payoffMatrix = resolved?.payoffMatrix ?? [];
41372
+ const won = isComplete && playerTotal >= targetScore;
41373
+ const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
41374
+ const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
41160
41375
  const handleAction = React84.useCallback((actionId) => {
41161
41376
  if (isComplete) return;
41162
- const opponentAction = getOpponentAction(resolved?.opponentStrategy ?? "random", actions, history);
41377
+ const opponentAction = getOpponentAction(str(resolved?.opponentStrategy) || "random", actions, history);
41163
41378
  const payoff = payoffMatrix.find(
41164
41379
  (p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
41165
41380
  );
@@ -41172,42 +41387,46 @@ function NegotiatorBoard({
41172
41387
  };
41173
41388
  const newHistory = [...history, result];
41174
41389
  setHistory(newHistory);
41175
- if (newHistory.length >= (resolved?.totalRounds ?? 0)) {
41390
+ if (newHistory.length >= totalRounds) {
41176
41391
  const total = newHistory.reduce((s, r) => s + r.playerPayoff, 0);
41177
- if (total >= (resolved?.targetScore ?? 0)) {
41392
+ if (total >= targetScore) {
41178
41393
  emit(`UI:${completeEvent}`, { success: true, score: total });
41179
41394
  }
41180
- if (newHistory.length >= 3 && resolved?.hint) {
41395
+ if (newHistory.length >= 3 && str(resolved?.hint)) {
41181
41396
  setShowHint(true);
41182
41397
  }
41183
41398
  }
41184
- }, [isComplete, resolved, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
41399
+ }, [isComplete, resolved, totalRounds, targetScore, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
41185
41400
  const handleReset = () => {
41186
41401
  setHistory([]);
41187
41402
  setShowHint(false);
41188
41403
  };
41189
41404
  const getActionLabel = (id) => actions.find((a) => a.id === id)?.label ?? id;
41190
41405
  if (!resolved) return null;
41406
+ const theme = resolved.theme ?? void 0;
41407
+ const themeBackground = theme?.background;
41408
+ const headerImage = str(resolved.headerImage);
41409
+ const hint = str(resolved.hint);
41191
41410
  return /* @__PURE__ */ jsxRuntime.jsx(
41192
41411
  Box,
41193
41412
  {
41194
41413
  className,
41195
41414
  style: {
41196
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
41415
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
41197
41416
  backgroundSize: "cover",
41198
41417
  backgroundPosition: "center"
41199
41418
  },
41200
41419
  children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: "p-4", children: [
41201
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
41420
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
41202
41421
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
41203
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
41204
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.description }),
41422
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
41423
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str(resolved.description) }),
41205
41424
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "md", children: [
41206
- /* @__PURE__ */ jsxRuntime.jsx(Badge, { size: "sm", children: t("negotiator.round", { current: String(currentRound), total: String(resolved.totalRounds) }) }),
41425
+ /* @__PURE__ */ jsxRuntime.jsx(Badge, { size: "sm", children: t("negotiator.round", { current: String(currentRound), total: String(totalRounds) }) }),
41207
41426
  /* @__PURE__ */ jsxRuntime.jsxs(Badge, { size: "sm", children: [
41208
41427
  t("negotiator.target"),
41209
41428
  ": ",
41210
- resolved.targetScore
41429
+ targetScore
41211
41430
  ] })
41212
41431
  ] })
41213
41432
  ] }) }),
@@ -41256,16 +41475,16 @@ function NegotiatorBoard({
41256
41475
  ] }) }),
41257
41476
  isComplete && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", children: [
41258
41477
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.CheckCircle, size: "lg", className: won ? "text-success" : "text-error" }),
41259
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: won ? resolved.successMessage ?? t("negotiator.success") : resolved.failMessage ?? t("negotiator.failed") }),
41478
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: won ? str(resolved.successMessage) || t("negotiator.success") : str(resolved.failMessage) || t("negotiator.failed") }),
41260
41479
  /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
41261
41480
  t("negotiator.finalScore"),
41262
41481
  ": ",
41263
41482
  playerTotal,
41264
41483
  "/",
41265
- resolved.targetScore
41484
+ targetScore
41266
41485
  ] })
41267
41486
  ] }) }),
41268
- showHint && resolved.hint && !won && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.hint }) }),
41487
+ showHint && hint && !won && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
41269
41488
  isComplete && !won && /* @__PURE__ */ jsxRuntime.jsx(HStack, { justify: "center", children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleReset, children: t("negotiator.playAgain") }) })
41270
41489
  ] })
41271
41490
  }
@@ -41275,6 +41494,7 @@ var init_NegotiatorBoard = __esm({
41275
41494
  "components/game/organisms/puzzles/negotiator/NegotiatorBoard.tsx"() {
41276
41495
  init_atoms2();
41277
41496
  init_useEventBus();
41497
+ init_boardEntity();
41278
41498
  NegotiatorBoard.displayName = "NegotiatorBoard";
41279
41499
  }
41280
41500
  });
@@ -41310,13 +41530,13 @@ var init_PricingOrganism = __esm({
41310
41530
  return /* @__PURE__ */ jsxRuntime.jsx(ErrorState, { message: error.message, className });
41311
41531
  }
41312
41532
  const plans = items.map((plan) => ({
41313
- name: plan.name,
41314
- price: plan.price,
41315
- description: plan.description,
41316
- features: plan.features,
41317
- action: { label: plan.actionLabel, href: plan.actionHref },
41318
- highlighted: plan.highlighted,
41319
- badge: plan.badge
41533
+ name: String(plan.name ?? ""),
41534
+ price: String(plan.price ?? ""),
41535
+ description: plan.description != null ? String(plan.description) : void 0,
41536
+ features: (plan.features ?? []).map((f3) => String(f3)),
41537
+ action: { label: String(plan.actionLabel ?? ""), href: String(plan.actionHref ?? "") },
41538
+ highlighted: Boolean(plan.highlighted),
41539
+ badge: plan.badge != null ? String(plan.badge) : void 0
41320
41540
  }));
41321
41541
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
41322
41542
  (heading || subtitle) && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
@@ -43406,16 +43626,20 @@ function SequencerBoard({
43406
43626
  }) {
43407
43627
  const { emit } = useEventBus();
43408
43628
  const { t } = hooks.useTranslate();
43409
- const resolved = Array.isArray(entity) ? entity[0] : entity;
43629
+ const resolved = boardEntity(entity);
43630
+ const maxSlots = num(resolved?.maxSlots);
43631
+ const solutions = Array.isArray(resolved?.solutions) ? resolved.solutions : [];
43632
+ const availableActions = Array.isArray(resolved?.availableActions) ? resolved.availableActions : [];
43633
+ const allowDuplicates = resolved?.allowDuplicates !== false;
43410
43634
  const [headerError, setHeaderError] = React84.useState(false);
43411
43635
  const [slots, setSlots] = React84.useState(
43412
- () => Array.from({ length: resolved?.maxSlots ?? 0 }, () => void 0)
43636
+ () => Array.from({ length: maxSlots }, () => void 0)
43413
43637
  );
43414
43638
  const [playState, setPlayState] = React84.useState("idle");
43415
43639
  const [currentStep, setCurrentStep] = React84.useState(-1);
43416
43640
  const [attempts, setAttempts] = React84.useState(0);
43417
43641
  const [slotFeedback, setSlotFeedback] = React84.useState(
43418
- () => Array.from({ length: resolved?.maxSlots ?? 0 }, () => null)
43642
+ () => Array.from({ length: maxSlots }, () => null)
43419
43643
  );
43420
43644
  const timerRef = React84.useRef(null);
43421
43645
  React84.useEffect(() => () => {
@@ -43449,17 +43673,17 @@ function SequencerBoard({
43449
43673
  }, [emit]);
43450
43674
  const handleReset = React84.useCallback(() => {
43451
43675
  if (timerRef.current) clearTimeout(timerRef.current);
43452
- setSlots(Array.from({ length: resolved?.maxSlots ?? 0 }, () => void 0));
43676
+ setSlots(Array.from({ length: maxSlots }, () => void 0));
43453
43677
  setPlayState("idle");
43454
43678
  setCurrentStep(-1);
43455
43679
  setAttempts(0);
43456
- setSlotFeedback(Array.from({ length: resolved?.maxSlots ?? 0 }, () => null));
43457
- }, [resolved?.maxSlots]);
43680
+ setSlotFeedback(Array.from({ length: maxSlots }, () => null));
43681
+ }, [maxSlots]);
43458
43682
  const filledSlots = slots.filter((s) => !!s);
43459
43683
  const canPlay = filledSlots.length > 0 && playState === "idle";
43460
43684
  const handlePlay = React84.useCallback(() => {
43461
43685
  if (!canPlay) return;
43462
- setSlotFeedback(Array.from({ length: resolved?.maxSlots ?? 0 }, () => null));
43686
+ setSlotFeedback(Array.from({ length: maxSlots }, () => null));
43463
43687
  emit("UI:PLAY_SOUND", { key: "confirm" });
43464
43688
  const sequence = slots.map((s) => s?.id || "");
43465
43689
  if (playEvent) {
@@ -43470,10 +43694,10 @@ function SequencerBoard({
43470
43694
  let step = 0;
43471
43695
  const advance = () => {
43472
43696
  step++;
43473
- if (step >= (resolved?.maxSlots ?? 0)) {
43697
+ if (step >= maxSlots) {
43474
43698
  const playerSeq = slots.map((s) => s?.id);
43475
43699
  const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
43476
- const success = (resolved?.solutions ?? []).some(
43700
+ const success = solutions.some(
43477
43701
  (sol) => sol.length === playerIds.length && sol.every((id, i) => id === playerIds[i])
43478
43702
  );
43479
43703
  if (success) {
@@ -43485,7 +43709,7 @@ function SequencerBoard({
43485
43709
  }
43486
43710
  } else {
43487
43711
  setAttempts((prev) => prev + 1);
43488
- const feedback = computeSlotFeedback(playerSeq, resolved?.solutions ?? []);
43712
+ const feedback = computeSlotFeedback(playerSeq, solutions);
43489
43713
  setSlotFeedback(feedback);
43490
43714
  setPlayState("idle");
43491
43715
  setCurrentStep(-1);
@@ -43503,10 +43727,10 @@ function SequencerBoard({
43503
43727
  }
43504
43728
  };
43505
43729
  timerRef.current = setTimeout(advance, stepDurationMs);
43506
- }, [canPlay, slots, resolved?.maxSlots, resolved?.solutions, stepDurationMs, playEvent, completeEvent, emit]);
43730
+ }, [canPlay, slots, maxSlots, solutions, stepDurationMs, playEvent, completeEvent, emit]);
43507
43731
  const machine = {
43508
- name: resolved?.title ?? "",
43509
- description: resolved?.description ?? "",
43732
+ name: str(resolved?.title),
43733
+ description: str(resolved?.description),
43510
43734
  states: slots.map((s, i) => stepLabel(s, i)),
43511
43735
  currentState: currentStep >= 0 ? stepLabel(slots[currentStep], currentStep) : "__idle__",
43512
43736
  transitions: slots.slice(0, -1).map((s, i) => ({
@@ -43515,37 +43739,41 @@ function SequencerBoard({
43515
43739
  event: "NEXT"
43516
43740
  }))
43517
43741
  };
43518
- const usedIds = resolved?.allowDuplicates === false ? slots.filter(Boolean).map((s) => s?.id || "") : [];
43519
- const showHint = attempts >= 3 && !!resolved?.hint;
43742
+ const usedIds = !allowDuplicates ? slots.filter(Boolean).map((s) => s?.id || "") : [];
43743
+ const hint = str(resolved?.hint);
43744
+ const showHint = attempts >= 3 && !!hint;
43520
43745
  const hasFeedback = slotFeedback.some((f3) => f3 !== null);
43521
43746
  const correctCount = slotFeedback.filter((f3) => f3 === "correct").length;
43522
43747
  const encourageKey = ENCOURAGEMENT_KEYS2[Math.min(attempts - 1, ENCOURAGEMENT_KEYS2.length - 1)] ?? ENCOURAGEMENT_KEYS2[0];
43523
43748
  if (!resolved) return null;
43749
+ const theme = resolved.theme ?? void 0;
43750
+ const themeBackground = theme?.background;
43751
+ const headerImage = str(resolved.headerImage);
43524
43752
  return /* @__PURE__ */ jsxRuntime.jsxs(
43525
43753
  VStack,
43526
43754
  {
43527
43755
  className: cn("p-4 gap-6", className),
43528
43756
  style: {
43529
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
43757
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
43530
43758
  backgroundSize: "cover",
43531
43759
  backgroundPosition: "center"
43532
43760
  },
43533
43761
  children: [
43534
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
43762
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
43535
43763
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
43536
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
43537
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description })
43764
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
43765
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) })
43538
43766
  ] }),
43539
43767
  showHint && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-start", gap: "xs", children: [
43540
43768
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
43541
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
43769
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
43542
43770
  ] }) }),
43543
43771
  filledSlots.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(TraitStateViewer, { trait: machine, variant: "linear", size: "md" }),
43544
43772
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
43545
43773
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center justify-between", children: [
43546
43774
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("sequencer.yourSequence") + ":" }),
43547
43775
  hasFeedback && playState === "idle" && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
43548
- `${correctCount}/${resolved.maxSlots} `,
43776
+ `${correctCount}/${maxSlots} `,
43549
43777
  "\u2705"
43550
43778
  ] })
43551
43779
  ] }),
@@ -43553,7 +43781,7 @@ function SequencerBoard({
43553
43781
  SequenceBar,
43554
43782
  {
43555
43783
  slots,
43556
- maxSlots: resolved.maxSlots,
43784
+ maxSlots,
43557
43785
  onSlotDrop: handleSlotDrop,
43558
43786
  onSlotRemove: handleSlotRemove,
43559
43787
  playing: playState === "playing",
@@ -43567,15 +43795,15 @@ function SequencerBoard({
43567
43795
  playState !== "playing" && /* @__PURE__ */ jsxRuntime.jsx(
43568
43796
  ActionPalette,
43569
43797
  {
43570
- actions: resolved.availableActions,
43798
+ actions: availableActions,
43571
43799
  usedActionIds: usedIds,
43572
- allowDuplicates: resolved.allowDuplicates !== false,
43800
+ allowDuplicates,
43573
43801
  categoryColors,
43574
43802
  label: t("sequencer.dragActions")
43575
43803
  }
43576
43804
  ),
43577
43805
  hasFeedback && playState === "idle" && attempts > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: t(encourageKey) }) }),
43578
- playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: resolved.successMessage || t("sequencer.levelComplete") }) }),
43806
+ playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("sequencer.levelComplete") }) }),
43579
43807
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
43580
43808
  /* @__PURE__ */ jsxRuntime.jsx(
43581
43809
  Button,
@@ -43599,6 +43827,7 @@ var init_SequencerBoard = __esm({
43599
43827
  init_cn();
43600
43828
  init_useEventBus();
43601
43829
  init_TraitStateViewer();
43830
+ init_boardEntity();
43602
43831
  init_SequenceBar();
43603
43832
  init_ActionPalette();
43604
43833
  ENCOURAGEMENT_KEYS2 = [
@@ -43648,18 +43877,21 @@ var init_ShowcaseOrganism = __esm({
43648
43877
  heading && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", align: "center", children: heading }),
43649
43878
  subtitle && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", color: "muted", align: "center", className: "max-w-2xl", children: subtitle })
43650
43879
  ] }),
43651
- /* @__PURE__ */ jsxRuntime.jsx(SimpleGrid, { cols: columns, gap: "lg", children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
43652
- ShowcaseCard,
43653
- {
43654
- title: item.title,
43655
- description: item.description,
43656
- image: item.image,
43657
- href: item.href,
43658
- badge: item.badge,
43659
- accentColor: item.accentColor
43660
- },
43661
- item.id
43662
- )) })
43880
+ /* @__PURE__ */ jsxRuntime.jsx(SimpleGrid, { cols: columns, gap: "lg", children: items.map((item) => {
43881
+ const imageRaw = item.image;
43882
+ return /* @__PURE__ */ jsxRuntime.jsx(
43883
+ ShowcaseCard,
43884
+ {
43885
+ title: String(item.title ?? ""),
43886
+ description: item.description != null ? String(item.description) : void 0,
43887
+ image: { src: String(imageRaw?.src ?? ""), alt: String(imageRaw?.alt ?? "") },
43888
+ href: item.href != null ? String(item.href) : void 0,
43889
+ badge: item.badge != null ? String(item.badge) : void 0,
43890
+ accentColor: item.accentColor != null ? String(item.accentColor) : void 0
43891
+ },
43892
+ String(item.id ?? "")
43893
+ );
43894
+ }) })
43663
43895
  ] });
43664
43896
  };
43665
43897
  ShowcaseOrganism.displayName = "ShowcaseOrganism";
@@ -44027,8 +44259,8 @@ function SimulatorBoard({
44027
44259
  }) {
44028
44260
  const { emit } = useEventBus();
44029
44261
  const { t } = hooks.useTranslate();
44030
- const resolved = Array.isArray(entity) ? entity[0] : entity;
44031
- const parameters = resolved?.parameters ?? [];
44262
+ const resolved = boardEntity(entity);
44263
+ const parameters = Array.isArray(resolved?.parameters) ? resolved.parameters : [];
44032
44264
  const [values, setValues] = React84.useState(() => {
44033
44265
  const init = {};
44034
44266
  for (const p2 of parameters) {
@@ -44042,15 +44274,15 @@ function SimulatorBoard({
44042
44274
  const [showHint, setShowHint] = React84.useState(false);
44043
44275
  const computeOutput = React84.useCallback((params) => {
44044
44276
  try {
44045
- const fn = new Function("params", `return (${resolved?.computeExpression})`);
44277
+ const fn = new Function("params", `return (${str(resolved?.computeExpression)})`);
44046
44278
  return fn(params);
44047
44279
  } catch {
44048
44280
  return 0;
44049
44281
  }
44050
44282
  }, [resolved?.computeExpression]);
44051
44283
  const output = React84.useMemo(() => computeOutput(values) ?? 0, [computeOutput, values]);
44052
- const targetValue = resolved?.targetValue ?? 0;
44053
- const targetTolerance = resolved?.targetTolerance ?? 0;
44284
+ const targetValue = num(resolved?.targetValue);
44285
+ const targetTolerance = num(resolved?.targetTolerance);
44054
44286
  const isCorrect = Math.abs(output - targetValue) <= targetTolerance;
44055
44287
  const handleParameterChange = (id, value) => {
44056
44288
  if (submitted) return;
@@ -44065,7 +44297,7 @@ function SimulatorBoard({
44065
44297
  };
44066
44298
  const handleReset = () => {
44067
44299
  setSubmitted(false);
44068
- if (attempts >= 2 && resolved?.hint) {
44300
+ if (attempts >= 2 && str(resolved?.hint)) {
44069
44301
  setShowHint(true);
44070
44302
  }
44071
44303
  };
@@ -44080,20 +44312,26 @@ function SimulatorBoard({
44080
44312
  setShowHint(false);
44081
44313
  };
44082
44314
  if (!resolved) return null;
44315
+ const theme = resolved.theme ?? void 0;
44316
+ const themeBackground = theme?.background;
44317
+ const headerImage = str(resolved.headerImage);
44318
+ const hint = str(resolved.hint);
44319
+ const outputLabel = str(resolved.outputLabel);
44320
+ const outputUnit = str(resolved.outputUnit);
44083
44321
  return /* @__PURE__ */ jsxRuntime.jsx(
44084
44322
  Box,
44085
44323
  {
44086
44324
  className,
44087
44325
  style: {
44088
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
44326
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
44089
44327
  backgroundSize: "cover",
44090
44328
  backgroundPosition: "center"
44091
44329
  },
44092
44330
  children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: "p-4", children: [
44093
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
44331
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
44094
44332
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
44095
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: resolved.title }),
44096
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.description })
44333
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) }),
44334
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str(resolved.description) })
44097
44335
  ] }) }),
44098
44336
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", children: [
44099
44337
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("simulator.parameters") }),
@@ -44134,28 +44372,28 @@ function SimulatorBoard({
44134
44372
  ] }, param.id))
44135
44373
  ] }) }),
44136
44374
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", children: [
44137
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: resolved.outputLabel }),
44375
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: outputLabel }),
44138
44376
  /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "h3", weight: "bold", children: [
44139
44377
  output.toFixed(2),
44140
44378
  " ",
44141
- resolved.outputUnit
44379
+ outputUnit
44142
44380
  ] }),
44143
44381
  submitted && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
44144
44382
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: isCorrect ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: isCorrect ? "text-success" : "text-error" }),
44145
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: isCorrect ? "text-success" : "text-error", children: isCorrect ? resolved.successMessage ?? t("simulator.correct") : resolved.failMessage ?? t("simulator.incorrect") })
44383
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: isCorrect ? "text-success" : "text-error", children: isCorrect ? str(resolved.successMessage) || t("simulator.correct") : str(resolved.failMessage) || t("simulator.incorrect") })
44146
44384
  ] }),
44147
44385
  /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
44148
44386
  t("simulator.target"),
44149
44387
  ": ",
44150
44388
  targetValue,
44151
44389
  " ",
44152
- resolved.outputUnit,
44390
+ outputUnit,
44153
44391
  " (\xB1",
44154
44392
  targetTolerance,
44155
44393
  ")"
44156
44394
  ] })
44157
44395
  ] }) }),
44158
- showHint && resolved.hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: resolved.hint }) }),
44396
+ showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
44159
44397
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
44160
44398
  !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, children: [
44161
44399
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Play, size: "sm" }),
@@ -44174,6 +44412,7 @@ var init_SimulatorBoard = __esm({
44174
44412
  "components/game/organisms/puzzles/simulator/SimulatorBoard.tsx"() {
44175
44413
  init_atoms2();
44176
44414
  init_useEventBus();
44415
+ init_boardEntity();
44177
44416
  SimulatorBoard.displayName = "SimulatorBoard";
44178
44417
  }
44179
44418
  });
@@ -44599,22 +44838,25 @@ function VariablePanel({
44599
44838
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("p-3 rounded-lg bg-card border border-border", className), gap: "sm", children: [
44600
44839
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("stateArchitect.variables", { name: entityName }) }),
44601
44840
  variables.map((v) => {
44602
- const max = v.max ?? 100;
44603
- const min = v.min ?? 0;
44604
- const pct = Math.round((v.value - min) / (max - min) * 100);
44841
+ const name = v.name == null ? "" : String(v.name);
44842
+ const value = numField(v.value);
44843
+ const max = numField(v.max, 100);
44844
+ const min = numField(v.min, 0);
44845
+ const unit = v.unit == null ? "" : String(v.unit);
44846
+ const pct = Math.round((value - min) / (max - min) * 100);
44605
44847
  const isHigh = pct > 80;
44606
44848
  const isLow = pct < 20;
44607
44849
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", children: [
44608
44850
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center justify-between", children: [
44609
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-foreground font-medium", children: v.name }),
44851
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-foreground font-medium", children: name }),
44610
44852
  /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: cn(
44611
44853
  isHigh ? "text-error" : isLow ? "text-warning" : "text-foreground"
44612
44854
  ), children: [
44613
- v.value,
44614
- v.unit || "",
44855
+ value,
44856
+ unit,
44615
44857
  " / ",
44616
44858
  max,
44617
- v.unit || ""
44859
+ unit
44618
44860
  ] })
44619
44861
  ] }),
44620
44862
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -44625,14 +44867,19 @@ function VariablePanel({
44625
44867
  size: "sm"
44626
44868
  }
44627
44869
  )
44628
- ] }, v.name);
44870
+ ] }, name);
44629
44871
  })
44630
44872
  ] });
44631
44873
  }
44874
+ var numField;
44632
44875
  var init_VariablePanel = __esm({
44633
44876
  "components/game/organisms/puzzles/state-architect/VariablePanel.tsx"() {
44634
44877
  init_atoms2();
44635
44878
  init_cn();
44879
+ numField = (v, fallback = 0) => {
44880
+ const n = Number(v);
44881
+ return Number.isFinite(n) ? n : fallback;
44882
+ };
44636
44883
  VariablePanel.displayName = "VariablePanel";
44637
44884
  }
44638
44885
  });
@@ -44659,14 +44906,21 @@ function StateArchitectBoard({
44659
44906
  }) {
44660
44907
  const { emit } = useEventBus();
44661
44908
  const { t } = hooks.useTranslate();
44662
- const resolved = Array.isArray(entity) ? entity[0] : entity;
44663
- const [transitions, setTransitions] = React84.useState(resolved?.transitions ?? []);
44909
+ const resolved = boardEntity(entity);
44910
+ const entityStates = Array.isArray(resolved?.states) ? resolved.states : [];
44911
+ const initialState = str(resolved?.initialState);
44912
+ const entityName = str(resolved?.entityName);
44913
+ const availableEvents = Array.isArray(resolved?.availableEvents) ? resolved.availableEvents : [];
44914
+ const testCases = Array.isArray(resolved?.testCases) ? resolved.testCases : [];
44915
+ const entityTransitions = Array.isArray(resolved?.transitions) ? resolved.transitions : [];
44916
+ const entityVariables = rows(resolved?.variables);
44917
+ const [transitions, setTransitions] = React84.useState(entityTransitions);
44664
44918
  const [headerError, setHeaderError] = React84.useState(false);
44665
44919
  const [playState, setPlayState] = React84.useState("editing");
44666
- const [currentState, setCurrentState] = React84.useState(resolved?.initialState ?? "");
44920
+ const [currentState, setCurrentState] = React84.useState(initialState);
44667
44921
  const [selectedState, setSelectedState] = React84.useState(null);
44668
44922
  const [testResults, setTestResults] = React84.useState([]);
44669
- const [variables, setVariables] = React84.useState(resolved?.variables ?? []);
44923
+ const [variables, setVariables] = React84.useState(() => [...entityVariables]);
44670
44924
  const [attempts, setAttempts] = React84.useState(0);
44671
44925
  const timerRef = React84.useRef(null);
44672
44926
  const [addingFrom, setAddingFrom] = React84.useState(null);
@@ -44675,12 +44929,12 @@ function StateArchitectBoard({
44675
44929
  }, []);
44676
44930
  const GRAPH_W = 500;
44677
44931
  const GRAPH_H = 400;
44678
- const positions = React84.useMemo(() => layoutStates(resolved?.states ?? [], GRAPH_W, GRAPH_H), [resolved?.states]);
44932
+ const positions = React84.useMemo(() => layoutStates(entityStates, GRAPH_W, GRAPH_H), [entityStates]);
44679
44933
  const handleStateClick = React84.useCallback((state) => {
44680
44934
  if (playState !== "editing") return;
44681
44935
  if (addingFrom) {
44682
44936
  if (addingFrom !== state) {
44683
- const event = resolved?.availableEvents[0] || "EVENT";
44937
+ const event = availableEvents[0] || "EVENT";
44684
44938
  const newTrans = {
44685
44939
  id: `t-${nextTransId++}`,
44686
44940
  from: addingFrom,
@@ -44693,7 +44947,7 @@ function StateArchitectBoard({
44693
44947
  } else {
44694
44948
  setSelectedState(state);
44695
44949
  }
44696
- }, [playState, addingFrom, resolved?.availableEvents]);
44950
+ }, [playState, addingFrom, availableEvents]);
44697
44951
  const handleStartAddTransition = React84.useCallback(() => {
44698
44952
  if (!selectedState) return;
44699
44953
  setAddingFrom(selectedState);
@@ -44702,9 +44956,9 @@ function StateArchitectBoard({
44702
44956
  setTransitions((prev) => prev.filter((t2) => t2.id !== transId));
44703
44957
  }, []);
44704
44958
  const machine = React84.useMemo(() => ({
44705
- name: resolved?.entityName ?? "",
44706
- description: resolved?.description ?? "",
44707
- states: resolved?.states ?? [],
44959
+ name: entityName,
44960
+ description: str(resolved?.description),
44961
+ states: entityStates,
44708
44962
  currentState,
44709
44963
  transitions: transitions.map((t2) => ({
44710
44964
  from: t2.from,
@@ -44712,7 +44966,7 @@ function StateArchitectBoard({
44712
44966
  event: t2.event,
44713
44967
  guardHint: t2.guardHint
44714
44968
  }))
44715
- }), [resolved, currentState, transitions]);
44969
+ }), [entityName, resolved, entityStates, currentState, transitions]);
44716
44970
  const handleTest = React84.useCallback(() => {
44717
44971
  if (playState !== "editing") return;
44718
44972
  if (testEvent) emit(`UI:${testEvent}`, {});
@@ -44721,7 +44975,7 @@ function StateArchitectBoard({
44721
44975
  const results = [];
44722
44976
  let testIdx = 0;
44723
44977
  const runNextTest = () => {
44724
- if (testIdx >= (resolved?.testCases.length ?? 0)) {
44978
+ if (testIdx >= testCases.length) {
44725
44979
  const allPassed = results.every((r) => r.passed);
44726
44980
  setPlayState(allPassed ? "success" : "fail");
44727
44981
  setTestResults(results);
@@ -44736,9 +44990,9 @@ function StateArchitectBoard({
44736
44990
  }
44737
44991
  return;
44738
44992
  }
44739
- const testCase = resolved?.testCases[testIdx];
44993
+ const testCase = testCases[testIdx];
44740
44994
  if (!testCase) return;
44741
- let state = resolved.initialState;
44995
+ let state = initialState;
44742
44996
  for (const event of testCase.events) {
44743
44997
  const trans = transitions.find((t2) => t2.from === state && t2.event === event);
44744
44998
  if (trans) {
@@ -44756,53 +45010,57 @@ function StateArchitectBoard({
44756
45010
  timerRef.current = setTimeout(runNextTest, stepDurationMs);
44757
45011
  };
44758
45012
  timerRef.current = setTimeout(runNextTest, stepDurationMs);
44759
- }, [playState, transitions, resolved, stepDurationMs, testEvent, completeEvent, emit]);
45013
+ }, [playState, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
44760
45014
  const handleTryAgain = React84.useCallback(() => {
44761
45015
  if (timerRef.current) clearTimeout(timerRef.current);
44762
45016
  setPlayState("editing");
44763
- setCurrentState(resolved?.initialState ?? "");
45017
+ setCurrentState(initialState);
44764
45018
  setTestResults([]);
44765
- }, [resolved?.initialState]);
45019
+ }, [initialState]);
44766
45020
  const handleReset = React84.useCallback(() => {
44767
45021
  if (timerRef.current) clearTimeout(timerRef.current);
44768
- setTransitions(resolved?.transitions ?? []);
45022
+ setTransitions(entityTransitions);
44769
45023
  setPlayState("editing");
44770
- setCurrentState(resolved?.initialState ?? "");
45024
+ setCurrentState(initialState);
44771
45025
  setTestResults([]);
44772
- setVariables(resolved?.variables ?? []);
45026
+ setVariables([...entityVariables]);
44773
45027
  setSelectedState(null);
44774
45028
  setAddingFrom(null);
44775
45029
  setAttempts(0);
44776
- }, [resolved]);
45030
+ }, [entityTransitions, initialState, entityVariables]);
44777
45031
  const codeData = React84.useMemo(() => ({
44778
- name: resolved?.entityName ?? "",
44779
- states: resolved?.states ?? [],
44780
- initialState: resolved?.initialState ?? "",
45032
+ name: entityName,
45033
+ states: entityStates,
45034
+ initialState,
44781
45035
  transitions: transitions.map((t2) => ({
44782
45036
  from: t2.from,
44783
45037
  to: t2.to,
44784
45038
  event: t2.event,
44785
45039
  ...t2.guardHint ? { guard: t2.guardHint } : {}
44786
45040
  }))
44787
- }), [resolved, transitions]);
45041
+ }), [entityName, entityStates, initialState, transitions]);
44788
45042
  if (!resolved) return null;
45043
+ const theme = resolved.theme ?? void 0;
45044
+ const themeBackground = theme?.background;
45045
+ const headerImage = str(resolved.headerImage);
45046
+ const hint = str(resolved.hint);
44789
45047
  return /* @__PURE__ */ jsxRuntime.jsxs(
44790
45048
  VStack,
44791
45049
  {
44792
45050
  className: cn("p-4 gap-6", className),
44793
45051
  style: {
44794
- backgroundImage: resolved.theme?.background ? `url(${resolved.theme.background})` : void 0,
45052
+ backgroundImage: themeBackground ? `url(${themeBackground})` : void 0,
44795
45053
  backgroundSize: "cover",
44796
45054
  backgroundPosition: "center"
44797
45055
  },
44798
45056
  children: [
44799
- resolved.headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: resolved.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : resolved.headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
45057
+ headerImage && !headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 overflow-hidden rounded-container", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : headerImage && headerError ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-32 rounded-container bg-gradient-to-br from-muted to-accent opacity-60" }) : null,
44800
45058
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
44801
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: "text-foreground", children: resolved.title }),
44802
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: resolved.description }),
45059
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: "text-foreground", children: str(resolved.title) }),
45060
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: str(resolved.description) }),
44803
45061
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center p-2 rounded bg-warning/10 border border-warning/30", gap: "xs", children: [
44804
45062
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-warning font-bold", children: t("game.hint") + ":" }),
44805
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-foreground", children: resolved.hint })
45063
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-foreground", children: hint })
44806
45064
  ] })
44807
45065
  ] }),
44808
45066
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "flex-wrap items-start", gap: "lg", children: [
@@ -44850,14 +45108,14 @@ function StateArchitectBoard({
44850
45108
  ]
44851
45109
  }
44852
45110
  ),
44853
- resolved.states.map((state) => /* @__PURE__ */ jsxRuntime.jsx(
45111
+ entityStates.map((state) => /* @__PURE__ */ jsxRuntime.jsx(
44854
45112
  StateNode2,
44855
45113
  {
44856
45114
  name: state,
44857
45115
  position: positions[state],
44858
45116
  isCurrent: state === currentState,
44859
45117
  isSelected: state === selectedState,
44860
- isInitial: state === resolved.initialState,
45118
+ isInitial: state === initialState,
44861
45119
  onClick: () => handleStateClick(state)
44862
45120
  },
44863
45121
  state
@@ -44904,7 +45162,7 @@ function StateArchitectBoard({
44904
45162
  /* @__PURE__ */ jsxRuntime.jsx(
44905
45163
  VariablePanel,
44906
45164
  {
44907
- entityName: resolved.entityName,
45165
+ entityName,
44908
45166
  variables
44909
45167
  }
44910
45168
  ),
@@ -44919,12 +45177,12 @@ function StateArchitectBoard({
44919
45177
  resolved.showCodeView !== false && /* @__PURE__ */ jsxRuntime.jsx(CodeView, { data: codeData, label: "View Code" })
44920
45178
  ] })
44921
45179
  ] }),
44922
- playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: resolved.successMessage || t("stateArchitect.allPassed") }) }),
45180
+ playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("stateArchitect.allPassed") }) }),
44923
45181
  playState === "fail" && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
44924
45182
  /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(ENCOURAGEMENT_KEYS3[Math.min(attempts - 1, ENCOURAGEMENT_KEYS3.length - 1)] ?? ENCOURAGEMENT_KEYS3[0]) }) }),
44925
- attempts >= 3 && resolved.hint && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-start", gap: "xs", children: [
45183
+ attempts >= 3 && hint && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-start", gap: "xs", children: [
44926
45184
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
44927
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: resolved.hint })
45185
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
44928
45186
  ] }) })
44929
45187
  ] }),
44930
45188
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
@@ -44954,6 +45212,7 @@ var init_StateArchitectBoard = __esm({
44954
45212
  init_TransitionArrow();
44955
45213
  init_VariablePanel();
44956
45214
  init_CodeView();
45215
+ init_boardEntity();
44957
45216
  ENCOURAGEMENT_KEYS3 = [
44958
45217
  "puzzle.tryAgain1",
44959
45218
  "puzzle.tryAgain2",
@@ -44990,8 +45249,8 @@ var init_StatsOrganism = __esm({
44990
45249
  return /* @__PURE__ */ jsxRuntime.jsx(ErrorState, { message: error.message, className });
44991
45250
  }
44992
45251
  const stats = items.map((item) => ({
44993
- value: item.value,
44994
- label: item.label
45252
+ value: String(item.value ?? ""),
45253
+ label: String(item.label ?? "")
44995
45254
  }));
44996
45255
  return /* @__PURE__ */ jsxRuntime.jsx(
44997
45256
  StatsGrid,
@@ -45037,10 +45296,10 @@ var init_StepFlowOrganism = __esm({
45037
45296
  return /* @__PURE__ */ jsxRuntime.jsx(ErrorState, { message: error.message, className });
45038
45297
  }
45039
45298
  const steps = items.map((item) => ({
45040
- number: item.number,
45041
- title: item.title,
45042
- description: item.description,
45043
- icon: item.icon
45299
+ number: item.number != null ? Number(item.number) : void 0,
45300
+ title: String(item.title ?? ""),
45301
+ description: String(item.description ?? ""),
45302
+ icon: item.icon != null ? String(item.icon) : void 0
45044
45303
  }));
45045
45304
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "lg", className: cn("w-full", className), children: [
45046
45305
  (heading || subtitle) && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", className: "w-full", children: [
@@ -45213,13 +45472,13 @@ var init_TeamOrganism = __esm({
45213
45472
  /* @__PURE__ */ jsxRuntime.jsx(SimpleGrid, { cols: cols > 0 ? cols : 1, gap: "lg", children: items.map((member) => /* @__PURE__ */ jsxRuntime.jsx(
45214
45473
  TeamCard,
45215
45474
  {
45216
- name: member.name,
45217
- nameAr: member.nameAr,
45218
- role: member.role,
45219
- bio: member.bio,
45220
- avatar: member.avatar
45475
+ name: String(member.name ?? ""),
45476
+ nameAr: member.nameAr != null ? String(member.nameAr) : void 0,
45477
+ role: String(member.role ?? ""),
45478
+ bio: String(member.bio ?? ""),
45479
+ avatar: member.avatar != null ? String(member.avatar) : void 0
45221
45480
  },
45222
- member.id
45481
+ String(member.id ?? "")
45223
45482
  )) })
45224
45483
  ] });
45225
45484
  };
@@ -45456,8 +45715,8 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
45456
45715
  const [turn, setTurn] = React84.useState(1);
45457
45716
  const [gameResult, setGameResult] = React84.useState(null);
45458
45717
  const checkGameEnd = React84.useCallback((currentUnits) => {
45459
- const pa = currentUnits.filter((u) => u.team === "player" && u.health > 0);
45460
- const ea = currentUnits.filter((u) => u.team === "enemy" && u.health > 0);
45718
+ const pa = currentUnits.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
45719
+ const ea = currentUnits.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
45461
45720
  if (pa.length === 0) {
45462
45721
  setGameResult("defeat");
45463
45722
  setPhase("game_over");
@@ -45475,34 +45734,36 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
45475
45734
  }
45476
45735
  }, [onGameEnd, gameEndEvent, eventBus]);
45477
45736
  const handleUnitClick = React84.useCallback((unitId) => {
45478
- const unit = units.find((u) => u.id === unitId);
45737
+ const unit = units.find((u) => str(u.id) === unitId);
45479
45738
  if (!unit) return;
45480
45739
  if (unitClickEvent) {
45481
45740
  eventBus.emit(`UI:${unitClickEvent}`, { unitId });
45482
45741
  }
45483
45742
  if (phase === "observation" || phase === "selection") {
45484
- if (unit.team === "player") {
45743
+ if (unitTeam(unit) === "player") {
45485
45744
  setSelectedUnitId(unitId);
45486
45745
  setPhase("movement");
45487
45746
  }
45488
45747
  } else if (phase === "action") {
45489
- const selectedUnit = units.find((u) => u.id === selectedUnitId);
45748
+ const selectedUnit = units.find((u) => str(u.id) === selectedUnitId);
45490
45749
  if (!selectedUnit) return;
45491
- if (unit.team === "enemy") {
45492
- const dx = Math.abs(unit.position.x - selectedUnit.position.x);
45493
- const dy = Math.abs(unit.position.y - selectedUnit.position.y);
45750
+ if (unitTeam(unit) === "enemy") {
45751
+ const up = unitPosition(unit);
45752
+ const sp = unitPosition(selectedUnit);
45753
+ const dx = Math.abs(up.x - sp.x);
45754
+ const dy = Math.abs(up.y - sp.y);
45494
45755
  if (dx <= 1 && dy <= 1 && dx + dy > 0) {
45495
- const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, selectedUnit.attack - unit.defense);
45496
- const newHealth = Math.max(0, unit.health - damage);
45756
+ const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, num(selectedUnit.attack) - num(unit.defense));
45757
+ const newHealth = Math.max(0, unitHealth(unit) - damage);
45497
45758
  const updatedUnits = units.map(
45498
- (u) => u.id === unit.id ? { ...u, health: newHealth } : u
45759
+ (u) => str(u.id) === str(unit.id) ? { ...u, health: newHealth } : u
45499
45760
  );
45500
45761
  setUnits(updatedUnits);
45501
45762
  onAttack?.(selectedUnit, unit, damage);
45502
45763
  if (attackEvent) {
45503
45764
  eventBus.emit(`UI:${attackEvent}`, {
45504
- attackerId: selectedUnit.id,
45505
- targetId: unit.id,
45765
+ attackerId: str(selectedUnit.id),
45766
+ targetId: str(unit.id),
45506
45767
  damage
45507
45768
  });
45508
45769
  }
@@ -45519,16 +45780,20 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
45519
45780
  eventBus.emit(`UI:${tileClickEvent}`, { x, y });
45520
45781
  }
45521
45782
  if (phase === "movement" && selectedUnitId) {
45522
- const selectedUnit = units.find((u) => u.id === selectedUnitId);
45783
+ const selectedUnit = units.find((u) => str(u.id) === selectedUnitId);
45523
45784
  if (!selectedUnit) return;
45524
- const dx = Math.abs(x - selectedUnit.position.x);
45525
- const dy = Math.abs(y - selectedUnit.position.y);
45785
+ const sp = unitPosition(selectedUnit);
45786
+ const dx = Math.abs(x - sp.x);
45787
+ const dy = Math.abs(y - sp.y);
45526
45788
  const dist = dx + dy;
45527
- if (dist > 0 && dist <= selectedUnit.movement) {
45528
- if (!units.some((u) => u.position.x === x && u.position.y === y && u.health > 0)) {
45789
+ if (dist > 0 && dist <= num(selectedUnit.movement)) {
45790
+ if (!units.some((u) => {
45791
+ const p2 = unitPosition(u);
45792
+ return p2.x === x && p2.y === y && unitHealth(u) > 0;
45793
+ })) {
45529
45794
  setUnits(
45530
45795
  (prev) => prev.map(
45531
- (u) => u.id === selectedUnitId ? { ...u, position: { x, y } } : u
45796
+ (u) => str(u.id) === selectedUnitId ? { ...u, position: { x, y } } : u
45532
45797
  )
45533
45798
  );
45534
45799
  setPhase("action");
@@ -45571,12 +45836,13 @@ var init_useBattleState = __esm({
45571
45836
  "components/game/organisms/hooks/useBattleState.ts"() {
45572
45837
  "use client";
45573
45838
  init_useEventBus();
45839
+ init_boardEntity();
45574
45840
  }
45575
45841
  });
45576
45842
  function UncontrolledBattleBoard({ entity, ...rest }) {
45577
- const resolved = Array.isArray(entity) ? entity[0] : entity;
45843
+ const resolved = boardEntity(entity);
45578
45844
  const battleState = useBattleState(
45579
- resolved?.initialUnits ?? [],
45845
+ rows(resolved?.initialUnits),
45580
45846
  {
45581
45847
  tileClickEvent: rest.tileClickEvent,
45582
45848
  unitClickEvent: rest.unitClickEvent,
@@ -45612,10 +45878,23 @@ function UncontrolledBattleBoard({ entity, ...rest }) {
45612
45878
  var init_UncontrolledBattleBoard = __esm({
45613
45879
  "components/game/organisms/UncontrolledBattleBoard.tsx"() {
45614
45880
  init_BattleBoard();
45881
+ init_boardEntity();
45615
45882
  init_useBattleState();
45616
45883
  UncontrolledBattleBoard.displayName = "UncontrolledBattleBoard";
45617
45884
  }
45618
45885
  });
45886
+ function heroPosition(h) {
45887
+ return vec2(h.position);
45888
+ }
45889
+ function heroOwner(h) {
45890
+ return str(h.owner);
45891
+ }
45892
+ function heroMovement(h) {
45893
+ return num(h.movement);
45894
+ }
45895
+ function hexPassable(h) {
45896
+ return h.passable !== false;
45897
+ }
45619
45898
  function defaultIsInRange(from, to, range) {
45620
45899
  return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
45621
45900
  }
@@ -45646,36 +45925,36 @@ function WorldMapBoard({
45646
45925
  className
45647
45926
  }) {
45648
45927
  const eventBus = useEventBus();
45649
- const resolved = Array.isArray(entity) ? entity[0] : entity;
45650
- const hexes = resolved?.hexes ?? [];
45651
- const heroes = resolved?.heroes ?? [];
45652
- const features = resolved?.features ?? [];
45653
- const selectedHeroId = resolved?.selectedHeroId;
45928
+ const resolved = boardEntity(entity);
45929
+ const hexes = rows(resolved?.hexes);
45930
+ const heroes = rows(resolved?.heroes);
45931
+ const features = Array.isArray(resolved?.features) ? resolved.features : [];
45932
+ const selectedHeroId = resolved?.selectedHeroId ?? null;
45654
45933
  const assetManifest = resolved?.assetManifest;
45655
45934
  const backgroundImage = resolved?.backgroundImage;
45656
45935
  const [hoveredTile, setHoveredTile] = React84.useState(null);
45657
45936
  const selectedHero = React84.useMemo(
45658
- () => heroes.find((h) => h.id === selectedHeroId) ?? null,
45937
+ () => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
45659
45938
  [heroes, selectedHeroId]
45660
45939
  );
45661
45940
  const tiles = React84.useMemo(
45662
45941
  () => hexes.map((hex) => ({
45663
- x: hex.x,
45664
- y: hex.y,
45665
- terrain: hex.terrain,
45666
- terrainSprite: hex.terrainSprite
45942
+ x: num(hex.x),
45943
+ y: num(hex.y),
45944
+ terrain: str(hex.terrain),
45945
+ terrainSprite: hex.terrainSprite == null ? void 0 : str(hex.terrainSprite)
45667
45946
  })),
45668
45947
  [hexes]
45669
45948
  );
45670
45949
  const baseUnits = React84.useMemo(
45671
45950
  () => heroes.map((hero) => ({
45672
- id: hero.id,
45673
- position: hero.position,
45674
- name: hero.name,
45675
- team: hero.owner === "enemy" ? "enemy" : "player",
45951
+ id: str(hero.id),
45952
+ position: heroPosition(hero),
45953
+ name: str(hero.name),
45954
+ team: heroOwner(hero) === "enemy" ? "enemy" : "player",
45676
45955
  health: 100,
45677
45956
  maxHealth: 100,
45678
- sprite: hero.sprite
45957
+ sprite: hero.sprite == null ? void 0 : str(hero.sprite)
45679
45958
  })),
45680
45959
  [heroes]
45681
45960
  );
@@ -45716,73 +45995,94 @@ function WorldMapBoard({
45716
45995
  const isoUnits = React84.useMemo(() => {
45717
45996
  if (movingPositions.size === 0) return baseUnits;
45718
45997
  return baseUnits.map((u) => {
45719
- const pos = movingPositions.get(u.id);
45998
+ const pos = u.id == null ? void 0 : movingPositions.get(u.id);
45720
45999
  return pos ? { ...u, position: pos } : u;
45721
46000
  });
45722
46001
  }, [baseUnits, movingPositions]);
45723
46002
  const validMoves = React84.useMemo(() => {
45724
- if (!selectedHero || selectedHero.movement <= 0) return [];
46003
+ if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
46004
+ const sp = heroPosition(selectedHero);
46005
+ const sOwner = heroOwner(selectedHero);
46006
+ const range = heroMovement(selectedHero);
45725
46007
  const moves = [];
45726
46008
  hexes.forEach((hex) => {
45727
- if (hex.passable === false) return;
45728
- if (hex.x === selectedHero.position.x && hex.y === selectedHero.position.y) return;
45729
- if (!isInRange(selectedHero.position, { x: hex.x, y: hex.y }, selectedHero.movement)) return;
45730
- if (heroes.some((h) => h.position.x === hex.x && h.position.y === hex.y && h.owner === selectedHero.owner)) return;
45731
- moves.push({ x: hex.x, y: hex.y });
46009
+ const hx = num(hex.x);
46010
+ const hy = num(hex.y);
46011
+ if (!hexPassable(hex)) return;
46012
+ if (hx === sp.x && hy === sp.y) return;
46013
+ if (!isInRange(sp, { x: hx, y: hy }, range)) return;
46014
+ if (heroes.some((h) => {
46015
+ const hp = heroPosition(h);
46016
+ return hp.x === hx && hp.y === hy && heroOwner(h) === sOwner;
46017
+ })) return;
46018
+ moves.push({ x: hx, y: hy });
45732
46019
  });
45733
46020
  return moves;
45734
46021
  }, [selectedHero, hexes, heroes, isInRange]);
45735
46022
  const attackTargets = React84.useMemo(() => {
45736
- if (!selectedHero || selectedHero.movement <= 0) return [];
45737
- return heroes.filter((h) => h.owner !== selectedHero.owner).filter((h) => isInRange(selectedHero.position, h.position, selectedHero.movement)).map((h) => h.position);
46023
+ if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
46024
+ const sp = heroPosition(selectedHero);
46025
+ const sOwner = heroOwner(selectedHero);
46026
+ const range = heroMovement(selectedHero);
46027
+ return heroes.filter((h) => heroOwner(h) !== sOwner).filter((h) => isInRange(sp, heroPosition(h), range)).map((h) => heroPosition(h));
45738
46028
  }, [selectedHero, heroes, isInRange]);
45739
- const maxY = Math.max(...hexes.map((h) => h.y), 0);
46029
+ const maxY = Math.max(...hexes.map((h) => num(h.y)), 0);
45740
46030
  const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
45741
46031
  const tileToScreen = React84.useCallback(
45742
46032
  (tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
45743
46033
  [scale, baseOffsetX]
45744
46034
  );
45745
46035
  const hoveredHex = React84.useMemo(
45746
- () => hoveredTile ? hexes.find((h) => h.x === hoveredTile.x && h.y === hoveredTile.y) ?? null : null,
46036
+ () => hoveredTile ? hexes.find((h) => num(h.x) === hoveredTile.x && num(h.y) === hoveredTile.y) ?? null : null,
45747
46037
  [hoveredTile, hexes]
45748
46038
  );
45749
46039
  const hoveredHero = React84.useMemo(
45750
- () => hoveredTile ? heroes.find((h) => h.position.x === hoveredTile.x && h.position.y === hoveredTile.y) ?? null : null,
46040
+ () => hoveredTile ? heroes.find((h) => {
46041
+ const hp = heroPosition(h);
46042
+ return hp.x === hoveredTile.x && hp.y === hoveredTile.y;
46043
+ }) ?? null : null,
45751
46044
  [hoveredTile, heroes]
45752
46045
  );
45753
46046
  const handleTileClick = React84.useCallback((x, y) => {
45754
46047
  if (movementAnimRef.current) return;
45755
- const hex = hexes.find((h) => h.x === x && h.y === y);
46048
+ const hex = hexes.find((h) => num(h.x) === x && num(h.y) === y);
45756
46049
  if (!hex) return;
45757
46050
  if (tileClickEvent) {
45758
46051
  eventBus.emit(`UI:${tileClickEvent}`, { x, y });
45759
46052
  }
45760
46053
  if (selectedHero && validMoves.some((m) => m.x === x && m.y === y)) {
45761
- startMoveAnimation(selectedHero.id, { ...selectedHero.position }, { x, y }, () => {
45762
- onHeroMove?.(selectedHero.id, x, y);
46054
+ const heroId = str(selectedHero.id);
46055
+ startMoveAnimation(heroId, { ...heroPosition(selectedHero) }, { x, y }, () => {
46056
+ onHeroMove?.(heroId, x, y);
45763
46057
  if (heroMoveEvent) {
45764
- eventBus.emit(`UI:${heroMoveEvent}`, { heroId: selectedHero.id, toX: x, toY: y });
46058
+ eventBus.emit(`UI:${heroMoveEvent}`, { heroId, toX: x, toY: y });
45765
46059
  }
45766
- if (hex.feature && hex.feature !== "none") {
45767
- onFeatureEnter?.(selectedHero.id, hex);
46060
+ const feature = str(hex.feature);
46061
+ if (feature && feature !== "none") {
46062
+ onFeatureEnter?.(heroId, hex);
45768
46063
  if (featureEnterEvent) {
45769
- eventBus.emit(`UI:${featureEnterEvent}`, { heroId: selectedHero.id, feature: hex.feature, hex });
46064
+ eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex });
45770
46065
  }
45771
46066
  }
45772
46067
  });
45773
46068
  return;
45774
46069
  }
45775
- const enemy = heroes.find((h) => h.position.x === x && h.position.y === y && h.owner === "enemy");
46070
+ const enemy = heroes.find((h) => {
46071
+ const hp = heroPosition(h);
46072
+ return hp.x === x && hp.y === y && heroOwner(h) === "enemy";
46073
+ });
45776
46074
  if (selectedHero && enemy && attackTargets.some((t) => t.x === x && t.y === y)) {
45777
- onBattleEncounter?.(selectedHero.id, enemy.id);
46075
+ const attackerId = str(selectedHero.id);
46076
+ const defenderId = str(enemy.id);
46077
+ onBattleEncounter?.(attackerId, defenderId);
45778
46078
  if (battleEncounterEvent) {
45779
- eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId: selectedHero.id, defenderId: enemy.id });
46079
+ eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId, defenderId });
45780
46080
  }
45781
46081
  }
45782
46082
  }, [hexes, heroes, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
45783
46083
  const handleUnitClick = React84.useCallback((unitId) => {
45784
- const hero = heroes.find((h) => h.id === unitId);
45785
- if (hero && (hero.owner === "player" || allowMoveAllHeroes)) {
46084
+ const hero = heroes.find((h) => str(h.id) === unitId);
46085
+ if (hero && (heroOwner(hero) === "player" || allowMoveAllHeroes)) {
45786
46086
  onHeroSelect?.(unitId);
45787
46087
  if (heroSelectEvent) {
45788
46088
  eventBus.emit(`UI:${heroSelectEvent}`, { heroId: unitId });
@@ -45855,6 +46155,7 @@ var init_WorldMapBoard = __esm({
45855
46155
  init_Stack();
45856
46156
  init_LoadingState();
45857
46157
  init_IsometricCanvas2();
46158
+ init_boardEntity();
45858
46159
  init_isometric();
45859
46160
  WorldMapBoard.displayName = "WorldMapBoard";
45860
46161
  }
@@ -49083,11 +49384,11 @@ function buildMockData(schema) {
49083
49384
  result[entityName] = entity.instances;
49084
49385
  continue;
49085
49386
  }
49086
- const rows = Array.from(
49387
+ const rows2 = Array.from(
49087
49388
  { length: 10 },
49088
49389
  (_, i) => generateEntityRow(entity, i + 1)
49089
49390
  );
49090
- result[entityName] = rows;
49391
+ result[entityName] = rows2;
49091
49392
  }
49092
49393
  for (const orbital of schema.orbitals) {
49093
49394
  for (const traitRef of orbital.traits ?? []) {