@almadar/ui 5.64.0 → 5.67.0

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 (31) hide show
  1. package/dist/avl/index.cjs +241 -71
  2. package/dist/avl/index.d.cts +10 -2
  3. package/dist/avl/index.d.ts +1 -0
  4. package/dist/avl/index.js +244 -75
  5. package/dist/components/avl/derive-edit-focus.d.ts +8 -0
  6. package/dist/components/core/organisms/ChatBar.d.ts +31 -0
  7. package/dist/components/core/organisms/SubagentTracePanel.d.ts +50 -0
  8. package/dist/components/core/organisms/index.d.ts +3 -0
  9. package/dist/components/core/organisms/trace-edit-focus.d.ts +14 -0
  10. package/dist/components/game/atoms/ChoiceButton.d.ts +7 -1
  11. package/dist/components/game/molecules/HealthPanel.d.ts +3 -0
  12. package/dist/components/game/molecules/IsometricCanvas.d.ts +4 -1
  13. package/dist/components/game/molecules/PowerupSlots.d.ts +3 -0
  14. package/dist/components/game/molecules/TurnPanel.d.ts +3 -0
  15. package/dist/components/game/molecules/index.d.ts +1 -0
  16. package/dist/components/game/molecules/three/index.cjs +6 -6
  17. package/dist/components/game/molecules/three/index.js +6 -6
  18. package/dist/components/game/organisms/HexStrategyBoard.d.ts +37 -0
  19. package/dist/components/game/organisms/index.d.ts +2 -0
  20. package/dist/components/game/organisms/utils/isometric.d.ts +20 -7
  21. package/dist/components/index.cjs +1241 -53
  22. package/dist/components/index.js +1243 -60
  23. package/dist/hooks/index.cjs +94 -0
  24. package/dist/hooks/index.d.ts +1 -0
  25. package/dist/hooks/index.js +95 -2
  26. package/dist/hooks/useAgentTrace.d.ts +85 -0
  27. package/dist/providers/index.cjs +210 -45
  28. package/dist/providers/index.js +213 -48
  29. package/dist/runtime/index.cjs +212 -47
  30. package/dist/runtime/index.js +215 -50
  31. package/package.json +2 -2
@@ -48,7 +48,7 @@ import { GLTFLoader as GLTFLoader$1 } from 'three/examples/jsm/loaders/GLTFLoade
48
48
  import { clone } from 'three/examples/jsm/utils/SkeletonUtils';
49
49
  import { Canvas, useLoader, useFrame, useThree } from '@react-three/fiber';
50
50
  import { Billboard, Grid as Grid$1, OrbitControls } from '@react-three/drei';
51
- import { getPatternDefinition, getComponentForPattern as getComponentForPattern$1 } from '@almadar/patterns';
51
+ import { getPatternDefinition, getComponentForPattern } from '@almadar/patterns';
52
52
  import { StateMachineManager, collectDeclaredConfigDefaults, createServerEffectHandlers, EffectExecutor, createContextFromBindings, InMemoryPersistence } from '@almadar/runtime';
53
53
  import { OrbitalServerRuntime } from '@almadar/runtime/OrbitalServerRuntime';
54
54
 
@@ -8016,6 +8016,8 @@ var init_DialogueBubble = __esm({
8016
8016
  function ChoiceButton({
8017
8017
  text = "Charge forward into the fray",
8018
8018
  index,
8019
+ assetUrl,
8020
+ icon,
8019
8021
  disabled = false,
8020
8022
  selected = false,
8021
8023
  onClick,
@@ -8048,6 +8050,23 @@ function ChoiceButton({
8048
8050
  ]
8049
8051
  }
8050
8052
  ),
8053
+ assetUrl ? /* @__PURE__ */ jsx(
8054
+ "img",
8055
+ {
8056
+ src: assetUrl,
8057
+ alt: "",
8058
+ width: 16,
8059
+ height: 16,
8060
+ style: { imageRendering: "pixelated", objectFit: "contain" },
8061
+ className: "flex-shrink-0"
8062
+ }
8063
+ ) : icon ? /* @__PURE__ */ jsx("span", { className: "flex-shrink-0 text-sm", children: typeof icon === "string" ? (() => {
8064
+ const I = resolveIcon(icon);
8065
+ return I ? /* @__PURE__ */ jsx(I, { className: "w-4 h-4" }) : null;
8066
+ })() : /* @__PURE__ */ (() => {
8067
+ const I = icon;
8068
+ return /* @__PURE__ */ jsx(I, { className: "w-4 h-4" });
8069
+ })() }) : null,
8051
8070
  /* @__PURE__ */ jsx("span", { className: "text-sm leading-snug", children: text })
8052
8071
  ]
8053
8072
  }
@@ -8056,6 +8075,7 @@ function ChoiceButton({
8056
8075
  var init_ChoiceButton = __esm({
8057
8076
  "components/game/atoms/ChoiceButton.tsx"() {
8058
8077
  init_cn();
8078
+ init_Icon();
8059
8079
  ChoiceButton.displayName = "ChoiceButton";
8060
8080
  }
8061
8081
  });
@@ -10937,16 +10957,26 @@ var init_useUnitSpriteAtlas = __esm({
10937
10957
  });
10938
10958
 
10939
10959
  // components/game/organisms/utils/isometric.ts
10940
- function isoToScreen(tileX, tileY, scale, baseOffsetX) {
10960
+ function isoToScreen(tileX, tileY, scale, baseOffsetX, layout = "isometric") {
10941
10961
  const scaledTileWidth = TILE_WIDTH * scale;
10942
10962
  const scaledFloorHeight = FLOOR_HEIGHT * scale;
10963
+ if (layout === "hex") {
10964
+ const screenX2 = tileX * scaledTileWidth + (tileY & 1) * (scaledTileWidth / 2) + baseOffsetX;
10965
+ const screenY2 = tileY * (scaledFloorHeight * 0.75);
10966
+ return { x: screenX2, y: screenY2 };
10967
+ }
10943
10968
  const screenX = (tileX - tileY) * (scaledTileWidth / 2) + baseOffsetX;
10944
10969
  const screenY = (tileX + tileY) * (scaledFloorHeight / 2);
10945
10970
  return { x: screenX, y: screenY };
10946
10971
  }
10947
- function screenToIso(screenX, screenY, scale, baseOffsetX) {
10972
+ function screenToIso(screenX, screenY, scale, baseOffsetX, layout = "isometric") {
10948
10973
  const scaledTileWidth = TILE_WIDTH * scale;
10949
10974
  const scaledFloorHeight = FLOOR_HEIGHT * scale;
10975
+ if (layout === "hex") {
10976
+ const row = Math.round(screenY / (scaledFloorHeight * 0.75));
10977
+ const col = Math.round((screenX - (row & 1) * (scaledTileWidth / 2) - baseOffsetX) / scaledTileWidth);
10978
+ return { x: col, y: row };
10979
+ }
10950
10980
  const adjustedX = screenX - baseOffsetX;
10951
10981
  const tileX = (adjustedX / (scaledTileWidth / 2) + screenY / (scaledFloorHeight / 2)) / 2;
10952
10982
  const tileY = (screenY / (scaledFloorHeight / 2) - adjustedX / (scaledTileWidth / 2)) / 2;
@@ -10996,6 +11026,7 @@ function IsometricCanvas({
10996
11026
  tileHoverEvent,
10997
11027
  tileLeaveEvent,
10998
11028
  // Rendering options
11029
+ tileLayout = "isometric",
10999
11030
  scale = 0.4,
11000
11031
  debug: debug2 = false,
11001
11032
  backgroundImage = "",
@@ -11065,13 +11096,17 @@ function IsometricCanvas({
11065
11096
  );
11066
11097
  const sortedTiles = useMemo(() => {
11067
11098
  const tiles = [...tilesProp];
11068
- tiles.sort((a, b) => {
11069
- const depthA = a.x + a.y;
11070
- const depthB = b.x + b.y;
11071
- return depthA !== depthB ? depthA - depthB : a.y - b.y;
11072
- });
11099
+ if (tileLayout === "hex") {
11100
+ tiles.sort((a, b) => a.y !== b.y ? a.y - b.y : a.x - b.x);
11101
+ } else {
11102
+ tiles.sort((a, b) => {
11103
+ const depthA = a.x + a.y;
11104
+ const depthB = b.x + b.y;
11105
+ return depthA !== depthB ? depthA - depthB : a.y - b.y;
11106
+ });
11107
+ }
11073
11108
  return tiles;
11074
- }, [tilesProp]);
11109
+ }, [tilesProp, tileLayout]);
11075
11110
  const gridWidth = useMemo(() => {
11076
11111
  if (sortedTiles.length === 0) return 0;
11077
11112
  return Math.max(...sortedTiles.map((t2) => t2.x)) + 1;
@@ -11185,18 +11220,19 @@ function IsometricCanvas({
11185
11220
  miniCanvas.width = mW;
11186
11221
  miniCanvas.height = mH;
11187
11222
  mCtx.clearRect(0, 0, mW, mH);
11188
- const allScreenPos = sortedTiles.map((t2) => isoToScreen(t2.x, t2.y, scale, baseOffsetX));
11223
+ const allScreenPos = sortedTiles.map((t2) => isoToScreen(t2.x, t2.y, scale, baseOffsetX, tileLayout));
11189
11224
  const minX = Math.min(...allScreenPos.map((p2) => p2.x));
11190
11225
  const maxX = Math.max(...allScreenPos.map((p2) => p2.x + scaledTileWidth));
11191
11226
  const minY = Math.min(...allScreenPos.map((p2) => p2.y));
11192
- const maxY = Math.max(...allScreenPos.map((p2) => p2.y + scaledTileHeight));
11227
+ const tileBottomExtent = tileLayout === "hex" ? scaledTileWidth : scaledTileHeight;
11228
+ const maxY = Math.max(...allScreenPos.map((p2) => p2.y + tileBottomExtent));
11193
11229
  const worldW = maxX - minX;
11194
11230
  const worldH = maxY - minY;
11195
11231
  const scaleM = Math.min(mW / worldW, mH / worldH) * 0.9;
11196
11232
  const offsetMx = (mW - worldW * scaleM) / 2;
11197
11233
  const offsetMy = (mH - worldH * scaleM) / 2;
11198
11234
  for (const tile of sortedTiles) {
11199
- const pos = isoToScreen(tile.x, tile.y, scale, baseOffsetX);
11235
+ const pos = isoToScreen(tile.x, tile.y, scale, baseOffsetX, tileLayout);
11200
11236
  const mx = (pos.x - minX) * scaleM + offsetMx;
11201
11237
  const my = (pos.y - minY) * scaleM + offsetMy;
11202
11238
  const mTileW = scaledTileWidth * scaleM;
@@ -11212,7 +11248,7 @@ function IsometricCanvas({
11212
11248
  }
11213
11249
  for (const unit of units) {
11214
11250
  if (!unit.position) continue;
11215
- const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX);
11251
+ const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX, tileLayout);
11216
11252
  const mx = (pos.x + scaledTileWidth / 2 - minX) * scaleM + offsetMx;
11217
11253
  const my = (pos.y + scaledTileHeight / 2 - minY) * scaleM + offsetMy;
11218
11254
  mCtx.fillStyle = unit.team === "player" ? "#60a5fa" : unit.team === "enemy" ? "#f87171" : "#9ca3af";
@@ -11228,7 +11264,7 @@ function IsometricCanvas({
11228
11264
  mCtx.strokeStyle = "rgba(255, 255, 255, 0.8)";
11229
11265
  mCtx.lineWidth = 1;
11230
11266
  mCtx.strokeRect(vLeft, vTop, vW, vH);
11231
- }, [showMinimap, sortedTiles, units, scale, baseOffsetX, scaledTileWidth, scaledTileHeight, scaledFloorHeight, viewportSize, cameraRef]);
11267
+ }, [showMinimap, sortedTiles, units, tileLayout, scale, baseOffsetX, scaledTileWidth, scaledTileHeight, scaledFloorHeight, viewportSize, cameraRef]);
11232
11268
  const draw = useCallback((animTime) => {
11233
11269
  const canvas = canvasRef.current;
11234
11270
  if (!canvas) return;
@@ -11268,7 +11304,7 @@ function IsometricCanvas({
11268
11304
  const visTop = cam.y - viewportSize.height / cam.zoom;
11269
11305
  const visBottom = cam.y + viewportSize.height * 2 / cam.zoom;
11270
11306
  for (const tile of sortedTiles) {
11271
- const pos = isoToScreen(tile.x, tile.y, scale, baseOffsetX);
11307
+ const pos = isoToScreen(tile.x, tile.y, scale, baseOffsetX, tileLayout);
11272
11308
  if (pos.x + scaledTileWidth < visLeft || pos.x > visRight || pos.y + scaledTileHeight < visTop || pos.y > visBottom) {
11273
11309
  continue;
11274
11310
  }
@@ -11281,7 +11317,7 @@ function IsometricCanvas({
11281
11317
  const drawW = scaledTileWidth;
11282
11318
  const drawH = scaledTileWidth * (img.naturalHeight / img.naturalWidth);
11283
11319
  const drawX = pos.x;
11284
- const drawY = pos.y + scaledTileHeight - drawH;
11320
+ const drawY = tileLayout === "hex" ? pos.y : pos.y + scaledTileHeight - drawH;
11285
11321
  ctx.drawImage(img, drawX, drawY, drawW, drawH);
11286
11322
  }
11287
11323
  } else {
@@ -11348,12 +11384,13 @@ function IsometricCanvas({
11348
11384
  }
11349
11385
  }
11350
11386
  const sortedFeatures = [...features].sort((a, b) => {
11387
+ if (tileLayout === "hex") return a.y !== b.y ? a.y - b.y : a.x - b.x;
11351
11388
  const depthA = a.x + a.y;
11352
11389
  const depthB = b.x + b.y;
11353
11390
  return depthA !== depthB ? depthA - depthB : a.y - b.y;
11354
11391
  });
11355
11392
  for (const feature of sortedFeatures) {
11356
- const pos = isoToScreen(feature.x, feature.y, scale, baseOffsetX);
11393
+ const pos = isoToScreen(feature.x, feature.y, scale, baseOffsetX, tileLayout);
11357
11394
  if (pos.x + scaledTileWidth < visLeft || pos.x > visRight || pos.y + scaledTileHeight < visTop || pos.y > visBottom) {
11358
11395
  continue;
11359
11396
  }
@@ -11388,12 +11425,13 @@ function IsometricCanvas({
11388
11425
  }
11389
11426
  const unitsWithPosition = units.filter((u) => !!u.position);
11390
11427
  const sortedUnits = [...unitsWithPosition].sort((a, b) => {
11428
+ if (tileLayout === "hex") return a.position.y !== b.position.y ? a.position.y - b.position.y : a.position.x - b.position.x;
11391
11429
  const depthA = a.position.x + a.position.y;
11392
11430
  const depthB = b.position.x + b.position.y;
11393
11431
  return depthA !== depthB ? depthA - depthB : a.position.y - b.position.y;
11394
11432
  });
11395
11433
  for (const unit of sortedUnits) {
11396
- const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX);
11434
+ const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX, tileLayout);
11397
11435
  if (pos.x + scaledTileWidth < visLeft || pos.x > visRight || pos.y + scaledTileHeight < visTop || pos.y > visBottom) {
11398
11436
  continue;
11399
11437
  }
@@ -11413,7 +11451,7 @@ function IsometricCanvas({
11413
11451
  drawH = maxUnitW / ar;
11414
11452
  }
11415
11453
  if (unit.previousPosition && (unit.previousPosition.x !== unit.position.x || unit.previousPosition.y !== unit.position.y)) {
11416
- const ghostPos = isoToScreen(unit.previousPosition.x, unit.previousPosition.y, scale, baseOffsetX);
11454
+ const ghostPos = isoToScreen(unit.previousPosition.x, unit.previousPosition.y, scale, baseOffsetX, tileLayout);
11417
11455
  const ghostCenterX = ghostPos.x + scaledTileWidth / 2;
11418
11456
  const ghostGroundY = ghostPos.y + scaledDiamondTopY + scaledFloorHeight * 0.5;
11419
11457
  ctx.save();
@@ -11542,6 +11580,7 @@ function IsometricCanvas({
11542
11580
  units,
11543
11581
  features,
11544
11582
  selectedUnitId,
11583
+ tileLayout,
11545
11584
  scale,
11546
11585
  debug2,
11547
11586
  resolveTerrainSpriteUrl,
@@ -11570,14 +11609,14 @@ function IsometricCanvas({
11570
11609
  if (!selectedUnitId) return;
11571
11610
  const unit = units.find((u) => u.id === selectedUnitId);
11572
11611
  if (!unit?.position) return;
11573
- const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX);
11612
+ const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX, tileLayout);
11574
11613
  const centerX = pos.x + scaledTileWidth / 2;
11575
11614
  const centerY = pos.y + scaledDiamondTopY + scaledFloorHeight / 2;
11576
11615
  targetCameraRef.current = {
11577
11616
  x: centerX - viewportSize.width / 2,
11578
11617
  y: centerY - viewportSize.height / 2
11579
11618
  };
11580
- }, [selectedUnitId, units, scale, baseOffsetX, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, viewportSize, targetCameraRef]);
11619
+ }, [selectedUnitId, units, tileLayout, scale, baseOffsetX, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, viewportSize, targetCameraRef]);
11581
11620
  useEffect(() => {
11582
11621
  const hasAnimations = units.length > 0 || validMoves.length > 0 || attackTargets.length > 0 || selectedUnitId != null || targetCameraRef.current != null || hasActiveEffects2 || pendingCount > 0 || atlasPending > 0;
11583
11622
  draw(animTimeRef.current);
@@ -11610,13 +11649,13 @@ function IsometricCanvas({
11610
11649
  const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
11611
11650
  const adjustedX = world.x - scaledTileWidth / 2;
11612
11651
  const adjustedY = world.y - scaledDiamondTopY - scaledFloorHeight / 2;
11613
- const isoPos = screenToIso(adjustedX, adjustedY, scale, baseOffsetX);
11652
+ const isoPos = screenToIso(adjustedX, adjustedY, scale, baseOffsetX, tileLayout);
11614
11653
  const tileExists = tilesProp.some((t2) => t2.x === isoPos.x && t2.y === isoPos.y);
11615
11654
  if (tileExists) {
11616
11655
  if (tileHoverEvent) eventBus.emit(`UI:${tileHoverEvent}`, { x: isoPos.x, y: isoPos.y });
11617
11656
  onTileHover?.(isoPos.x, isoPos.y);
11618
11657
  }
11619
- }, [onTileHover, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, tilesProp, tileHoverEvent, eventBus]);
11658
+ }, [onTileHover, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, tileLayout, scale, baseOffsetX, tilesProp, tileHoverEvent, eventBus]);
11620
11659
  const handleCanvasPointerUp = useCallback((e) => {
11621
11660
  singlePointerActiveRef.current = false;
11622
11661
  if (enableCamera) handlePointerUp();
@@ -11625,7 +11664,7 @@ function IsometricCanvas({
11625
11664
  const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
11626
11665
  const adjustedX = world.x - scaledTileWidth / 2;
11627
11666
  const adjustedY = world.y - scaledDiamondTopY - scaledFloorHeight / 2;
11628
- const isoPos = screenToIso(adjustedX, adjustedY, scale, baseOffsetX);
11667
+ const isoPos = screenToIso(adjustedX, adjustedY, scale, baseOffsetX, tileLayout);
11629
11668
  const clickedUnit = units.find((u) => u.position?.x === isoPos.x && u.position?.y === isoPos.y);
11630
11669
  if (clickedUnit && (onUnitClick || unitClickEvent)) {
11631
11670
  if (unitClickEvent) eventBus.emit(`UI:${unitClickEvent}`, { unitId: clickedUnit.id });
@@ -11637,7 +11676,7 @@ function IsometricCanvas({
11637
11676
  onTileClick?.(isoPos.x, isoPos.y);
11638
11677
  }
11639
11678
  }
11640
- }, [enableCamera, handlePointerUp, dragDistance, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, units, tilesProp, onUnitClick, onTileClick, unitClickEvent, tileClickEvent, eventBus]);
11679
+ }, [enableCamera, handlePointerUp, dragDistance, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, tileLayout, scale, baseOffsetX, units, tilesProp, onUnitClick, onTileClick, unitClickEvent, tileClickEvent, eventBus]);
11641
11680
  const handleCanvasPointerLeave = useCallback(() => {
11642
11681
  handleMouseLeave();
11643
11682
  if (tileLeaveEvent) eventBus.emit(`UI:${tileLeaveEvent}`, {});
@@ -28030,6 +28069,7 @@ function PowerupSlots({
28030
28069
  /* @__PURE__ */ jsx(
28031
28070
  ItemSlot,
28032
28071
  {
28072
+ assetUrl: powerup.assetUrl,
28033
28073
  icon: powerup.icon,
28034
28074
  label: powerup.label,
28035
28075
  rarity: "uncommon",
@@ -28279,13 +28319,26 @@ function HealthPanel({
28279
28319
  )
28280
28320
  }
28281
28321
  ),
28282
- effects && effects.length > 0 && /* @__PURE__ */ jsx(Box, { className: "flex items-center gap-1 flex-wrap", children: effects.map((effect, i) => /* @__PURE__ */ jsx(
28322
+ effects && effects.length > 0 && /* @__PURE__ */ jsx(Box, { className: "flex items-center gap-1 flex-wrap", children: effects.map((effect, i) => /* @__PURE__ */ jsxs(
28283
28323
  Badge,
28284
28324
  {
28285
28325
  variant: effectVariantMap[effect.variant ?? "neutral"],
28286
28326
  size: "sm",
28287
- icon: effect.icon,
28288
- children: effect.label
28327
+ icon: effect.assetUrl ? void 0 : effect.icon,
28328
+ children: [
28329
+ effect.assetUrl && /* @__PURE__ */ jsx(
28330
+ "img",
28331
+ {
28332
+ src: effect.assetUrl,
28333
+ alt: "",
28334
+ width: 12,
28335
+ height: 12,
28336
+ style: { imageRendering: "pixelated", objectFit: "contain" },
28337
+ className: "flex-shrink-0 inline-block"
28338
+ }
28339
+ ),
28340
+ effect.label
28341
+ ]
28289
28342
  },
28290
28343
  i
28291
28344
  )) })
@@ -28468,15 +28521,28 @@ function TurnPanel({
28468
28521
  activeTeam
28469
28522
  }
28470
28523
  ),
28471
- actions && actions.length > 0 && /* @__PURE__ */ jsx(Box, { className: "flex items-center gap-1.5 ml-auto", children: actions.map((action, i) => /* @__PURE__ */ jsx(
28524
+ actions && actions.length > 0 && /* @__PURE__ */ jsx(Box, { className: "flex items-center gap-1.5 ml-auto", children: actions.map((action, i) => /* @__PURE__ */ jsxs(
28472
28525
  Button,
28473
28526
  {
28474
28527
  variant: "ghost",
28475
28528
  size: "sm",
28476
28529
  disabled: action.disabled,
28477
- leftIcon: action.icon,
28530
+ leftIcon: action.assetUrl ? void 0 : action.icon,
28478
28531
  onClick: () => handleAction(action.event),
28479
- children: action.label
28532
+ children: [
28533
+ action.assetUrl && /* @__PURE__ */ jsx(
28534
+ "img",
28535
+ {
28536
+ src: action.assetUrl,
28537
+ alt: "",
28538
+ width: 14,
28539
+ height: 14,
28540
+ style: { imageRendering: "pixelated", objectFit: "contain" },
28541
+ className: "flex-shrink-0"
28542
+ }
28543
+ ),
28544
+ action.label
28545
+ ]
28480
28546
  },
28481
28547
  i
28482
28548
  )) })
@@ -43392,9 +43458,9 @@ var init_GameCanvas3D2 = __esm({
43392
43458
  const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
43393
43459
  const hasAtlas = unitAtlasUrl(unit) !== null;
43394
43460
  const initialFrame = hasAtlas ? resolveUnitFrame(unit.id) : null;
43395
- const modelScale = UNIT_BASE_MODEL_SCALE * unitScale;
43396
- const billboardHeight = UNIT_BASE_BILLBOARD_HEIGHT * unitScale;
43397
- const primitiveRadius = UNIT_BASE_PRIMITIVE_RADIUS * unitScale;
43461
+ const modelScale = UNIT_BASE_MODEL_SCALE * unitScale * gridConfig.cellSize;
43462
+ const billboardHeight = UNIT_BASE_BILLBOARD_HEIGHT * unitScale * gridConfig.cellSize;
43463
+ const primitiveRadius = UNIT_BASE_PRIMITIVE_RADIUS * unitScale * gridConfig.cellSize;
43398
43464
  return /* @__PURE__ */ jsxs(
43399
43465
  "group",
43400
43466
  {
@@ -43441,7 +43507,7 @@ var init_GameCanvas3D2 = __esm({
43441
43507
  /* @__PURE__ */ jsx("meshStandardMaterial", { color })
43442
43508
  ] })
43443
43509
  ] }),
43444
- unit.health !== void 0 && unit.maxHealth !== void 0 && /* @__PURE__ */ jsxs("group", { position: [0, 1.2, 0], children: [
43510
+ unit.health !== void 0 && unit.maxHealth !== void 0 && /* @__PURE__ */ jsxs("group", { position: [0, billboardHeight, 0], children: [
43445
43511
  /* @__PURE__ */ jsxs("mesh", { position: [-0.25, 0, 0], children: [
43446
43512
  /* @__PURE__ */ jsx("planeGeometry", { args: [0.5, 0.05] }),
43447
43513
  /* @__PURE__ */ jsx("meshBasicMaterial", { color: 3355443 })
@@ -43470,7 +43536,7 @@ var init_GameCanvas3D2 = __esm({
43470
43536
  }
43471
43537
  );
43472
43538
  },
43473
- [selectedUnitId, handleUnitClick, resolveUnitFrame, unitScale]
43539
+ [selectedUnitId, handleUnitClick, resolveUnitFrame, unitScale, gridConfig]
43474
43540
  );
43475
43541
  const DefaultFeatureRenderer = useCallback(
43476
43542
  ({
@@ -43558,7 +43624,7 @@ var init_GameCanvas3D2 = __esm({
43558
43624
  {
43559
43625
  ref: containerRef,
43560
43626
  className: cn("game-canvas-3d relative w-full overflow-hidden", className),
43561
- style: { height: "85vh" },
43627
+ style: { flex: "1 1 0", minHeight: "400px" },
43562
43628
  "data-orientation": orientation,
43563
43629
  "data-camera-mode": cameraMode,
43564
43630
  "data-overlay": overlay,
@@ -43858,13 +43924,39 @@ var init_GameBoard3D = __esm({
43858
43924
  GameBoard3D.displayName = "GameBoard3D";
43859
43925
  }
43860
43926
  });
43861
- var GameShell;
43927
+ function getSlotContentRenderer2() {
43928
+ if (_scr) return _scr;
43929
+ const mod = (init_UISlotRenderer(), __toCommonJS(UISlotRenderer_exports));
43930
+ _scr = mod.SlotContentRenderer;
43931
+ return _scr;
43932
+ }
43933
+ function resolveDescriptor(value, idPrefix) {
43934
+ if (value === null || value === void 0) return value;
43935
+ if (React84__default.isValidElement(value)) return value;
43936
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
43937
+ if (Array.isArray(value)) {
43938
+ return value.map((item, i) => /* @__PURE__ */ jsx(React84__default.Fragment, { children: resolveDescriptor(item, `${idPrefix}-${i}`) }, i));
43939
+ }
43940
+ if (typeof value === "object") {
43941
+ const rec = value;
43942
+ if (typeof rec.type === "string" && getComponentForPattern(rec.type) !== null) {
43943
+ const { type, props: nestedProps, _id, ...flatProps } = rec;
43944
+ const resolvedProps = nestedProps !== void 0 ? nestedProps : flatProps;
43945
+ const content = { id: _id ?? idPrefix, pattern: type, props: resolvedProps, priority: 0 };
43946
+ const SCR = getSlotContentRenderer2();
43947
+ return /* @__PURE__ */ jsx(SCR, { content, onDismiss: () => void 0 });
43948
+ }
43949
+ }
43950
+ return null;
43951
+ }
43952
+ var _scr, GameShell;
43862
43953
  var init_GameShell = __esm({
43863
43954
  "components/game/templates/GameShell.tsx"() {
43864
43955
  init_cn();
43865
43956
  init_Box();
43866
43957
  init_Stack();
43867
43958
  init_Typography();
43959
+ _scr = null;
43868
43960
  GameShell = ({
43869
43961
  appName = "Game",
43870
43962
  hud,
@@ -43913,7 +44005,7 @@ var init_GameShell = __esm({
43913
44005
  children: appName
43914
44006
  }
43915
44007
  ),
43916
- hud && /* @__PURE__ */ jsx(Box, { className: "game-shell__hud", children: hud })
44008
+ hud && /* @__PURE__ */ jsx(Box, { className: "game-shell__hud", children: resolveDescriptor(hud, "gs-hud") })
43917
44009
  ]
43918
44010
  }
43919
44011
  ),
@@ -43926,7 +44018,7 @@ var init_GameShell = __esm({
43926
44018
  overflow: "hidden",
43927
44019
  position: "relative"
43928
44020
  },
43929
- children
44021
+ children: resolveDescriptor(children, "gs-children")
43930
44022
  }
43931
44023
  )
43932
44024
  ]
@@ -43936,7 +44028,32 @@ var init_GameShell = __esm({
43936
44028
  GameShell.displayName = "GameShell";
43937
44029
  }
43938
44030
  });
43939
- var GameTemplate;
44031
+ function getSlotContentRenderer3() {
44032
+ if (_scr2) return _scr2;
44033
+ const mod = (init_UISlotRenderer(), __toCommonJS(UISlotRenderer_exports));
44034
+ _scr2 = mod.SlotContentRenderer;
44035
+ return _scr2;
44036
+ }
44037
+ function resolveDescriptor2(value, idPrefix) {
44038
+ if (value === null || value === void 0) return value;
44039
+ if (React84__default.isValidElement(value)) return value;
44040
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
44041
+ if (Array.isArray(value)) {
44042
+ return value.map((item, i) => /* @__PURE__ */ jsx(React84__default.Fragment, { children: resolveDescriptor2(item, `${idPrefix}-${i}`) }, i));
44043
+ }
44044
+ if (typeof value === "object") {
44045
+ const rec = value;
44046
+ if (typeof rec.type === "string" && getComponentForPattern(rec.type) !== null) {
44047
+ const { type, props: nestedProps, _id, ...flatProps } = rec;
44048
+ const resolvedProps = nestedProps !== void 0 ? nestedProps : flatProps;
44049
+ const content = { id: _id ?? idPrefix, pattern: type, props: resolvedProps, priority: 0 };
44050
+ const SCR = getSlotContentRenderer3();
44051
+ return /* @__PURE__ */ jsx(SCR, { content, onDismiss: () => void 0 });
44052
+ }
44053
+ }
44054
+ return null;
44055
+ }
44056
+ var _scr2, GameTemplate;
43940
44057
  var init_GameTemplate = __esm({
43941
44058
  "components/game/templates/GameTemplate.tsx"() {
43942
44059
  init_cn();
@@ -43944,6 +44061,7 @@ var init_GameTemplate = __esm({
43944
44061
  init_Stack();
43945
44062
  init_Typography();
43946
44063
  init_Button();
44064
+ _scr2 = null;
43947
44065
  GameTemplate = ({
43948
44066
  entity,
43949
44067
  title = "Game",
@@ -44011,13 +44129,13 @@ var init_GameTemplate = __esm({
44011
44129
  fullWidth: true,
44012
44130
  className: "flex-1 bg-muted",
44013
44131
  children: [
44014
- children,
44132
+ resolveDescriptor2(children, "gt-children"),
44015
44133
  hud && /* @__PURE__ */ jsx(
44016
44134
  Box,
44017
44135
  {
44018
44136
  position: "absolute",
44019
44137
  className: "top-0 left-0 right-0 pointer-events-none",
44020
- children: /* @__PURE__ */ jsx(Box, { padding: "md", className: "pointer-events-auto w-fit", children: hud })
44138
+ children: /* @__PURE__ */ jsx(Box, { padding: "md", className: "pointer-events-auto w-fit", children: resolveDescriptor2(hud, "gt-hud") })
44021
44139
  }
44022
44140
  )
44023
44141
  ]
@@ -44042,7 +44160,7 @@ var init_GameTemplate = __esm({
44042
44160
  children: /* @__PURE__ */ jsx(Typography, { variant: "h6", children: "Debug Panel" })
44043
44161
  }
44044
44162
  ),
44045
- /* @__PURE__ */ jsx(Box, { padding: "md", children: debugPanel })
44163
+ /* @__PURE__ */ jsx(Box, { padding: "md", children: resolveDescriptor2(debugPanel, "gt-debug") })
44046
44164
  ]
44047
44165
  }
44048
44166
  )
@@ -44212,6 +44330,48 @@ var init_HeroOrganism = __esm({
44212
44330
  _HeroClickInterceptor.displayName = "_HeroClickInterceptor";
44213
44331
  }
44214
44332
  });
44333
+ function HexStrategyBoard({
44334
+ tiles,
44335
+ units,
44336
+ features,
44337
+ assetManifest,
44338
+ assetBaseUrl,
44339
+ scale = 0.45,
44340
+ showMinimap = true,
44341
+ enableCamera = true,
44342
+ tileClickEvent,
44343
+ unitClickEvent,
44344
+ isLoading,
44345
+ error,
44346
+ className
44347
+ }) {
44348
+ return /* @__PURE__ */ jsx("div", { className: cn("hex-strategy-board relative w-full h-full", className), children: /* @__PURE__ */ jsx(
44349
+ IsometricCanvas_default,
44350
+ {
44351
+ tileLayout: "hex",
44352
+ tiles,
44353
+ units,
44354
+ features,
44355
+ assetManifest,
44356
+ assetBaseUrl,
44357
+ scale,
44358
+ showMinimap,
44359
+ enableCamera,
44360
+ tileClickEvent,
44361
+ unitClickEvent,
44362
+ isLoading,
44363
+ error
44364
+ }
44365
+ ) });
44366
+ }
44367
+ var init_HexStrategyBoard = __esm({
44368
+ "components/game/organisms/HexStrategyBoard.tsx"() {
44369
+ "use client";
44370
+ init_cn();
44371
+ init_IsometricCanvas();
44372
+ HexStrategyBoard.displayName = "HexStrategyBoard";
44373
+ }
44374
+ });
44215
44375
  var LandingPageTemplate;
44216
44376
  var init_LandingPageTemplate = __esm({
44217
44377
  "components/marketing/templates/LandingPageTemplate.tsx"() {
@@ -52040,6 +52200,7 @@ var init_component_registry_generated = __esm({
52040
52200
  init_HealthPanel();
52041
52201
  init_HeroOrganism();
52042
52202
  init_HeroSection();
52203
+ init_HexStrategyBoard();
52043
52204
  init_Icon();
52044
52205
  init_InfiniteScrollSentinel();
52045
52206
  init_Input();
@@ -52376,6 +52537,7 @@ var init_component_registry_generated = __esm({
52376
52537
  "HealthPanel": HealthPanel,
52377
52538
  "HeroOrganism": HeroOrganism,
52378
52539
  "HeroSection": HeroSection,
52540
+ "HexStrategyBoard": HexStrategyBoard,
52379
52541
  "Icon": Icon,
52380
52542
  "InfiniteScrollSentinel": InfiniteScrollSentinel,
52381
52543
  "Input": Input,
@@ -52577,8 +52739,8 @@ function getSlotFallback(slot, config) {
52577
52739
  const variant = SLOT_SKELETON_MAP[slot] ?? "text";
52578
52740
  return /* @__PURE__ */ jsx(Skeleton, { variant });
52579
52741
  }
52580
- function getComponentForPattern(patternType) {
52581
- const mapping = getComponentForPattern$1(patternType);
52742
+ function getComponentForPattern3(patternType) {
52743
+ const mapping = getComponentForPattern(patternType);
52582
52744
  if (!mapping) {
52583
52745
  return null;
52584
52746
  }
@@ -53100,7 +53262,7 @@ function isPatternConfig(value) {
53100
53262
  if (value instanceof Date) return false;
53101
53263
  if (typeof value === "function") return false;
53102
53264
  const record = value;
53103
- return "type" in record && typeof record.type === "string" && getComponentForPattern$1(record.type) !== null;
53265
+ return "type" in record && typeof record.type === "string" && getComponentForPattern(record.type) !== null;
53104
53266
  }
53105
53267
  function isPlainConfigObject(value) {
53106
53268
  if (React84__default.isValidElement(value)) return false;
@@ -53208,7 +53370,8 @@ function SlotContentRenderer({
53208
53370
  entityDef = schemaCtx.entities.get(linkedEntity);
53209
53371
  }
53210
53372
  }
53211
- const PatternComponent = getComponentForPattern(content.pattern);
53373
+ const orbitalName = schemaCtx && content.sourceTrait !== void 0 ? schemaCtx.orbitalsByTrait.get(content.sourceTrait) : void 0;
53374
+ const PatternComponent = getComponentForPattern3(content.pattern);
53212
53375
  if (PatternComponent) {
53213
53376
  const childrenConfig = content.props.children;
53214
53377
  const isSingleChild = typeof childrenConfig === "string" || typeof childrenConfig === "object" && childrenConfig !== null && !Array.isArray(childrenConfig) && "type" in childrenConfig;
@@ -53306,6 +53469,7 @@ function SlotContentRenderer({
53306
53469
  "data-orb-entity": content.entity,
53307
53470
  "data-orb-path": myPath,
53308
53471
  "data-orb-pattern": content.pattern,
53472
+ "data-orb-orbital": orbitalName,
53309
53473
  children: renderedChildren !== void 0 ? /* @__PURE__ */ jsx(PatternComponent, { ...finalProps, children: renderedChildren }) : /* @__PURE__ */ jsx(PatternComponent, { ...finalProps })
53310
53474
  }
53311
53475
  );
@@ -53325,6 +53489,7 @@ function SlotContentRenderer({
53325
53489
  "data-orb-entity": content.entity,
53326
53490
  "data-orb-path": patternPath ?? "root",
53327
53491
  "data-orb-pattern": content.pattern,
53492
+ "data-orb-orbital": orbitalName,
53328
53493
  children: content.props.children ?? /* @__PURE__ */ jsxs(Box, { className: "p-4 text-sm text-muted-foreground border border-dashed border-border rounded", children: [
53329
53494
  "Unknown pattern: ",
53330
53495
  content.pattern,
@@ -53574,7 +53739,7 @@ function resolveLambdaBindings(body, params, item, index) {
53574
53739
  return body;
53575
53740
  }
53576
53741
  var _slotContentRenderer2 = null;
53577
- function getSlotContentRenderer2() {
53742
+ function getSlotContentRenderer4() {
53578
53743
  if (_slotContentRenderer2) return _slotContentRenderer2;
53579
53744
  const mod = (init_UISlotRenderer(), __toCommonJS(UISlotRenderer_exports));
53580
53745
  _slotContentRenderer2 = mod.SlotContentRenderer;
@@ -53590,7 +53755,7 @@ function makeLambdaFn(params, lambdaBody, callerKey) {
53590
53755
  if (typeof record.type !== "string") {
53591
53756
  return null;
53592
53757
  }
53593
- const SlotContentRenderer2 = getSlotContentRenderer2();
53758
+ const SlotContentRenderer2 = getSlotContentRenderer4();
53594
53759
  const rawChildProps = {};
53595
53760
  for (const [k, v] of Object.entries(record)) {
53596
53761
  if (k !== "type") rawChildProps[k] = v;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "5.64.0",
3
+ "version": "5.67.0",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -134,7 +134,7 @@
134
134
  "typecheck": "tsc --noEmit"
135
135
  },
136
136
  "dependencies": {
137
- "@almadar/core": "^10.5.0",
137
+ "@almadar/core": "^10.6.0",
138
138
  "@almadar/evaluator": ">=2.9.2",
139
139
  "@almadar/logger": "^1.3.0",
140
140
  "@almadar/patterns": ">=2.35.0",