@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
@@ -11802,6 +11802,8 @@ var init_DialogueBubble = __esm({
11802
11802
  function ChoiceButton({
11803
11803
  text = "Charge forward into the fray",
11804
11804
  index,
11805
+ assetUrl,
11806
+ icon,
11805
11807
  disabled = false,
11806
11808
  selected = false,
11807
11809
  onClick,
@@ -11834,6 +11836,23 @@ function ChoiceButton({
11834
11836
  ]
11835
11837
  }
11836
11838
  ),
11839
+ assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
11840
+ "img",
11841
+ {
11842
+ src: assetUrl,
11843
+ alt: "",
11844
+ width: 16,
11845
+ height: 16,
11846
+ style: { imageRendering: "pixelated", objectFit: "contain" },
11847
+ className: "flex-shrink-0"
11848
+ }
11849
+ ) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0 text-sm", children: typeof icon === "string" ? (() => {
11850
+ const I = resolveIcon(icon);
11851
+ return I ? /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-4 h-4" }) : null;
11852
+ })() : /* @__PURE__ */ (() => {
11853
+ const I = icon;
11854
+ return /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-4 h-4" });
11855
+ })() }) : null,
11837
11856
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm leading-snug", children: text })
11838
11857
  ]
11839
11858
  }
@@ -11842,6 +11861,7 @@ function ChoiceButton({
11842
11861
  var init_ChoiceButton = __esm({
11843
11862
  "components/game/atoms/ChoiceButton.tsx"() {
11844
11863
  init_cn();
11864
+ init_Icon();
11845
11865
  ChoiceButton.displayName = "ChoiceButton";
11846
11866
  }
11847
11867
  });
@@ -14723,16 +14743,26 @@ var init_useUnitSpriteAtlas = __esm({
14723
14743
  });
14724
14744
 
14725
14745
  // components/game/organisms/utils/isometric.ts
14726
- function isoToScreen(tileX, tileY, scale, baseOffsetX) {
14746
+ function isoToScreen(tileX, tileY, scale, baseOffsetX, layout = "isometric") {
14727
14747
  const scaledTileWidth = TILE_WIDTH * scale;
14728
14748
  const scaledFloorHeight = FLOOR_HEIGHT * scale;
14749
+ if (layout === "hex") {
14750
+ const screenX2 = tileX * scaledTileWidth + (tileY & 1) * (scaledTileWidth / 2) + baseOffsetX;
14751
+ const screenY2 = tileY * (scaledFloorHeight * 0.75);
14752
+ return { x: screenX2, y: screenY2 };
14753
+ }
14729
14754
  const screenX = (tileX - tileY) * (scaledTileWidth / 2) + baseOffsetX;
14730
14755
  const screenY = (tileX + tileY) * (scaledFloorHeight / 2);
14731
14756
  return { x: screenX, y: screenY };
14732
14757
  }
14733
- function screenToIso(screenX, screenY, scale, baseOffsetX) {
14758
+ function screenToIso(screenX, screenY, scale, baseOffsetX, layout = "isometric") {
14734
14759
  const scaledTileWidth = TILE_WIDTH * scale;
14735
14760
  const scaledFloorHeight = FLOOR_HEIGHT * scale;
14761
+ if (layout === "hex") {
14762
+ const row = Math.round(screenY / (scaledFloorHeight * 0.75));
14763
+ const col = Math.round((screenX - (row & 1) * (scaledTileWidth / 2) - baseOffsetX) / scaledTileWidth);
14764
+ return { x: col, y: row };
14765
+ }
14736
14766
  const adjustedX = screenX - baseOffsetX;
14737
14767
  const tileX = (adjustedX / (scaledTileWidth / 2) + screenY / (scaledFloorHeight / 2)) / 2;
14738
14768
  const tileY = (screenY / (scaledFloorHeight / 2) - adjustedX / (scaledTileWidth / 2)) / 2;
@@ -14782,6 +14812,7 @@ function IsometricCanvas({
14782
14812
  tileHoverEvent,
14783
14813
  tileLeaveEvent,
14784
14814
  // Rendering options
14815
+ tileLayout = "isometric",
14785
14816
  scale = 0.4,
14786
14817
  debug: debug2 = false,
14787
14818
  backgroundImage = "",
@@ -14851,13 +14882,17 @@ function IsometricCanvas({
14851
14882
  );
14852
14883
  const sortedTiles = React93.useMemo(() => {
14853
14884
  const tiles = [...tilesProp];
14854
- tiles.sort((a, b) => {
14855
- const depthA = a.x + a.y;
14856
- const depthB = b.x + b.y;
14857
- return depthA !== depthB ? depthA - depthB : a.y - b.y;
14858
- });
14885
+ if (tileLayout === "hex") {
14886
+ tiles.sort((a, b) => a.y !== b.y ? a.y - b.y : a.x - b.x);
14887
+ } else {
14888
+ tiles.sort((a, b) => {
14889
+ const depthA = a.x + a.y;
14890
+ const depthB = b.x + b.y;
14891
+ return depthA !== depthB ? depthA - depthB : a.y - b.y;
14892
+ });
14893
+ }
14859
14894
  return tiles;
14860
- }, [tilesProp]);
14895
+ }, [tilesProp, tileLayout]);
14861
14896
  const gridWidth = React93.useMemo(() => {
14862
14897
  if (sortedTiles.length === 0) return 0;
14863
14898
  return Math.max(...sortedTiles.map((t2) => t2.x)) + 1;
@@ -14971,18 +15006,19 @@ function IsometricCanvas({
14971
15006
  miniCanvas.width = mW;
14972
15007
  miniCanvas.height = mH;
14973
15008
  mCtx.clearRect(0, 0, mW, mH);
14974
- const allScreenPos = sortedTiles.map((t2) => isoToScreen(t2.x, t2.y, scale, baseOffsetX));
15009
+ const allScreenPos = sortedTiles.map((t2) => isoToScreen(t2.x, t2.y, scale, baseOffsetX, tileLayout));
14975
15010
  const minX = Math.min(...allScreenPos.map((p2) => p2.x));
14976
15011
  const maxX = Math.max(...allScreenPos.map((p2) => p2.x + scaledTileWidth));
14977
15012
  const minY = Math.min(...allScreenPos.map((p2) => p2.y));
14978
- const maxY = Math.max(...allScreenPos.map((p2) => p2.y + scaledTileHeight));
15013
+ const tileBottomExtent = tileLayout === "hex" ? scaledTileWidth : scaledTileHeight;
15014
+ const maxY = Math.max(...allScreenPos.map((p2) => p2.y + tileBottomExtent));
14979
15015
  const worldW = maxX - minX;
14980
15016
  const worldH = maxY - minY;
14981
15017
  const scaleM = Math.min(mW / worldW, mH / worldH) * 0.9;
14982
15018
  const offsetMx = (mW - worldW * scaleM) / 2;
14983
15019
  const offsetMy = (mH - worldH * scaleM) / 2;
14984
15020
  for (const tile of sortedTiles) {
14985
- const pos = isoToScreen(tile.x, tile.y, scale, baseOffsetX);
15021
+ const pos = isoToScreen(tile.x, tile.y, scale, baseOffsetX, tileLayout);
14986
15022
  const mx = (pos.x - minX) * scaleM + offsetMx;
14987
15023
  const my = (pos.y - minY) * scaleM + offsetMy;
14988
15024
  const mTileW = scaledTileWidth * scaleM;
@@ -14998,7 +15034,7 @@ function IsometricCanvas({
14998
15034
  }
14999
15035
  for (const unit of units) {
15000
15036
  if (!unit.position) continue;
15001
- const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX);
15037
+ const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX, tileLayout);
15002
15038
  const mx = (pos.x + scaledTileWidth / 2 - minX) * scaleM + offsetMx;
15003
15039
  const my = (pos.y + scaledTileHeight / 2 - minY) * scaleM + offsetMy;
15004
15040
  mCtx.fillStyle = unit.team === "player" ? "#60a5fa" : unit.team === "enemy" ? "#f87171" : "#9ca3af";
@@ -15014,7 +15050,7 @@ function IsometricCanvas({
15014
15050
  mCtx.strokeStyle = "rgba(255, 255, 255, 0.8)";
15015
15051
  mCtx.lineWidth = 1;
15016
15052
  mCtx.strokeRect(vLeft, vTop, vW, vH);
15017
- }, [showMinimap, sortedTiles, units, scale, baseOffsetX, scaledTileWidth, scaledTileHeight, scaledFloorHeight, viewportSize, cameraRef]);
15053
+ }, [showMinimap, sortedTiles, units, tileLayout, scale, baseOffsetX, scaledTileWidth, scaledTileHeight, scaledFloorHeight, viewportSize, cameraRef]);
15018
15054
  const draw = React93.useCallback((animTime) => {
15019
15055
  const canvas = canvasRef.current;
15020
15056
  if (!canvas) return;
@@ -15054,7 +15090,7 @@ function IsometricCanvas({
15054
15090
  const visTop = cam.y - viewportSize.height / cam.zoom;
15055
15091
  const visBottom = cam.y + viewportSize.height * 2 / cam.zoom;
15056
15092
  for (const tile of sortedTiles) {
15057
- const pos = isoToScreen(tile.x, tile.y, scale, baseOffsetX);
15093
+ const pos = isoToScreen(tile.x, tile.y, scale, baseOffsetX, tileLayout);
15058
15094
  if (pos.x + scaledTileWidth < visLeft || pos.x > visRight || pos.y + scaledTileHeight < visTop || pos.y > visBottom) {
15059
15095
  continue;
15060
15096
  }
@@ -15067,7 +15103,7 @@ function IsometricCanvas({
15067
15103
  const drawW = scaledTileWidth;
15068
15104
  const drawH = scaledTileWidth * (img.naturalHeight / img.naturalWidth);
15069
15105
  const drawX = pos.x;
15070
- const drawY = pos.y + scaledTileHeight - drawH;
15106
+ const drawY = tileLayout === "hex" ? pos.y : pos.y + scaledTileHeight - drawH;
15071
15107
  ctx.drawImage(img, drawX, drawY, drawW, drawH);
15072
15108
  }
15073
15109
  } else {
@@ -15134,12 +15170,13 @@ function IsometricCanvas({
15134
15170
  }
15135
15171
  }
15136
15172
  const sortedFeatures = [...features].sort((a, b) => {
15173
+ if (tileLayout === "hex") return a.y !== b.y ? a.y - b.y : a.x - b.x;
15137
15174
  const depthA = a.x + a.y;
15138
15175
  const depthB = b.x + b.y;
15139
15176
  return depthA !== depthB ? depthA - depthB : a.y - b.y;
15140
15177
  });
15141
15178
  for (const feature of sortedFeatures) {
15142
- const pos = isoToScreen(feature.x, feature.y, scale, baseOffsetX);
15179
+ const pos = isoToScreen(feature.x, feature.y, scale, baseOffsetX, tileLayout);
15143
15180
  if (pos.x + scaledTileWidth < visLeft || pos.x > visRight || pos.y + scaledTileHeight < visTop || pos.y > visBottom) {
15144
15181
  continue;
15145
15182
  }
@@ -15174,12 +15211,13 @@ function IsometricCanvas({
15174
15211
  }
15175
15212
  const unitsWithPosition = units.filter((u) => !!u.position);
15176
15213
  const sortedUnits = [...unitsWithPosition].sort((a, b) => {
15214
+ if (tileLayout === "hex") return a.position.y !== b.position.y ? a.position.y - b.position.y : a.position.x - b.position.x;
15177
15215
  const depthA = a.position.x + a.position.y;
15178
15216
  const depthB = b.position.x + b.position.y;
15179
15217
  return depthA !== depthB ? depthA - depthB : a.position.y - b.position.y;
15180
15218
  });
15181
15219
  for (const unit of sortedUnits) {
15182
- const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX);
15220
+ const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX, tileLayout);
15183
15221
  if (pos.x + scaledTileWidth < visLeft || pos.x > visRight || pos.y + scaledTileHeight < visTop || pos.y > visBottom) {
15184
15222
  continue;
15185
15223
  }
@@ -15199,7 +15237,7 @@ function IsometricCanvas({
15199
15237
  drawH = maxUnitW / ar;
15200
15238
  }
15201
15239
  if (unit.previousPosition && (unit.previousPosition.x !== unit.position.x || unit.previousPosition.y !== unit.position.y)) {
15202
- const ghostPos = isoToScreen(unit.previousPosition.x, unit.previousPosition.y, scale, baseOffsetX);
15240
+ const ghostPos = isoToScreen(unit.previousPosition.x, unit.previousPosition.y, scale, baseOffsetX, tileLayout);
15203
15241
  const ghostCenterX = ghostPos.x + scaledTileWidth / 2;
15204
15242
  const ghostGroundY = ghostPos.y + scaledDiamondTopY + scaledFloorHeight * 0.5;
15205
15243
  ctx.save();
@@ -15328,6 +15366,7 @@ function IsometricCanvas({
15328
15366
  units,
15329
15367
  features,
15330
15368
  selectedUnitId,
15369
+ tileLayout,
15331
15370
  scale,
15332
15371
  debug2,
15333
15372
  resolveTerrainSpriteUrl,
@@ -15356,14 +15395,14 @@ function IsometricCanvas({
15356
15395
  if (!selectedUnitId) return;
15357
15396
  const unit = units.find((u) => u.id === selectedUnitId);
15358
15397
  if (!unit?.position) return;
15359
- const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX);
15398
+ const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX, tileLayout);
15360
15399
  const centerX = pos.x + scaledTileWidth / 2;
15361
15400
  const centerY = pos.y + scaledDiamondTopY + scaledFloorHeight / 2;
15362
15401
  targetCameraRef.current = {
15363
15402
  x: centerX - viewportSize.width / 2,
15364
15403
  y: centerY - viewportSize.height / 2
15365
15404
  };
15366
- }, [selectedUnitId, units, scale, baseOffsetX, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, viewportSize, targetCameraRef]);
15405
+ }, [selectedUnitId, units, tileLayout, scale, baseOffsetX, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, viewportSize, targetCameraRef]);
15367
15406
  React93.useEffect(() => {
15368
15407
  const hasAnimations = units.length > 0 || validMoves.length > 0 || attackTargets.length > 0 || selectedUnitId != null || targetCameraRef.current != null || hasActiveEffects2 || pendingCount > 0 || atlasPending > 0;
15369
15408
  draw(animTimeRef.current);
@@ -15396,13 +15435,13 @@ function IsometricCanvas({
15396
15435
  const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
15397
15436
  const adjustedX = world.x - scaledTileWidth / 2;
15398
15437
  const adjustedY = world.y - scaledDiamondTopY - scaledFloorHeight / 2;
15399
- const isoPos = screenToIso(adjustedX, adjustedY, scale, baseOffsetX);
15438
+ const isoPos = screenToIso(adjustedX, adjustedY, scale, baseOffsetX, tileLayout);
15400
15439
  const tileExists = tilesProp.some((t2) => t2.x === isoPos.x && t2.y === isoPos.y);
15401
15440
  if (tileExists) {
15402
15441
  if (tileHoverEvent) eventBus.emit(`UI:${tileHoverEvent}`, { x: isoPos.x, y: isoPos.y });
15403
15442
  onTileHover?.(isoPos.x, isoPos.y);
15404
15443
  }
15405
- }, [onTileHover, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, tilesProp, tileHoverEvent, eventBus]);
15444
+ }, [onTileHover, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, tileLayout, scale, baseOffsetX, tilesProp, tileHoverEvent, eventBus]);
15406
15445
  const handleCanvasPointerUp = React93.useCallback((e) => {
15407
15446
  singlePointerActiveRef.current = false;
15408
15447
  if (enableCamera) handlePointerUp();
@@ -15411,7 +15450,7 @@ function IsometricCanvas({
15411
15450
  const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
15412
15451
  const adjustedX = world.x - scaledTileWidth / 2;
15413
15452
  const adjustedY = world.y - scaledDiamondTopY - scaledFloorHeight / 2;
15414
- const isoPos = screenToIso(adjustedX, adjustedY, scale, baseOffsetX);
15453
+ const isoPos = screenToIso(adjustedX, adjustedY, scale, baseOffsetX, tileLayout);
15415
15454
  const clickedUnit = units.find((u) => u.position?.x === isoPos.x && u.position?.y === isoPos.y);
15416
15455
  if (clickedUnit && (onUnitClick || unitClickEvent)) {
15417
15456
  if (unitClickEvent) eventBus.emit(`UI:${unitClickEvent}`, { unitId: clickedUnit.id });
@@ -15423,7 +15462,7 @@ function IsometricCanvas({
15423
15462
  onTileClick?.(isoPos.x, isoPos.y);
15424
15463
  }
15425
15464
  }
15426
- }, [enableCamera, handlePointerUp, dragDistance, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, units, tilesProp, onUnitClick, onTileClick, unitClickEvent, tileClickEvent, eventBus]);
15465
+ }, [enableCamera, handlePointerUp, dragDistance, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, tileLayout, scale, baseOffsetX, units, tilesProp, onUnitClick, onTileClick, unitClickEvent, tileClickEvent, eventBus]);
15427
15466
  const handleCanvasPointerLeave = React93.useCallback(() => {
15428
15467
  handleMouseLeave();
15429
15468
  if (tileLeaveEvent) eventBus.emit(`UI:${tileLeaveEvent}`, {});
@@ -30987,6 +31026,7 @@ function PowerupSlots({
30987
31026
  /* @__PURE__ */ jsxRuntime.jsx(
30988
31027
  ItemSlot,
30989
31028
  {
31029
+ assetUrl: powerup.assetUrl,
30990
31030
  icon: powerup.icon,
30991
31031
  label: powerup.label,
30992
31032
  rarity: "uncommon",
@@ -31236,13 +31276,26 @@ function HealthPanel({
31236
31276
  )
31237
31277
  }
31238
31278
  ),
31239
- effects && effects.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex items-center gap-1 flex-wrap", children: effects.map((effect, i) => /* @__PURE__ */ jsxRuntime.jsx(
31279
+ effects && effects.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex items-center gap-1 flex-wrap", children: effects.map((effect, i) => /* @__PURE__ */ jsxRuntime.jsxs(
31240
31280
  Badge,
31241
31281
  {
31242
31282
  variant: effectVariantMap[effect.variant ?? "neutral"],
31243
31283
  size: "sm",
31244
- icon: effect.icon,
31245
- children: effect.label
31284
+ icon: effect.assetUrl ? void 0 : effect.icon,
31285
+ children: [
31286
+ effect.assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
31287
+ "img",
31288
+ {
31289
+ src: effect.assetUrl,
31290
+ alt: "",
31291
+ width: 12,
31292
+ height: 12,
31293
+ style: { imageRendering: "pixelated", objectFit: "contain" },
31294
+ className: "flex-shrink-0 inline-block"
31295
+ }
31296
+ ),
31297
+ effect.label
31298
+ ]
31246
31299
  },
31247
31300
  i
31248
31301
  )) })
@@ -31425,15 +31478,28 @@ function TurnPanel({
31425
31478
  activeTeam
31426
31479
  }
31427
31480
  ),
31428
- actions && actions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex items-center gap-1.5 ml-auto", children: actions.map((action, i) => /* @__PURE__ */ jsxRuntime.jsx(
31481
+ actions && actions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex items-center gap-1.5 ml-auto", children: actions.map((action, i) => /* @__PURE__ */ jsxRuntime.jsxs(
31429
31482
  Button,
31430
31483
  {
31431
31484
  variant: "ghost",
31432
31485
  size: "sm",
31433
31486
  disabled: action.disabled,
31434
- leftIcon: action.icon,
31487
+ leftIcon: action.assetUrl ? void 0 : action.icon,
31435
31488
  onClick: () => handleAction(action.event),
31436
- children: action.label
31489
+ children: [
31490
+ action.assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
31491
+ "img",
31492
+ {
31493
+ src: action.assetUrl,
31494
+ alt: "",
31495
+ width: 14,
31496
+ height: 14,
31497
+ style: { imageRendering: "pixelated", objectFit: "contain" },
31498
+ className: "flex-shrink-0"
31499
+ }
31500
+ ),
31501
+ action.label
31502
+ ]
31437
31503
  },
31438
31504
  i
31439
31505
  )) })
@@ -45940,9 +46006,9 @@ var init_GameCanvas3D2 = __esm({
45940
46006
  const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
45941
46007
  const hasAtlas = unitAtlasUrl(unit) !== null;
45942
46008
  const initialFrame = hasAtlas ? resolveUnitFrame(unit.id) : null;
45943
- const modelScale = UNIT_BASE_MODEL_SCALE * unitScale;
45944
- const billboardHeight = UNIT_BASE_BILLBOARD_HEIGHT * unitScale;
45945
- const primitiveRadius = UNIT_BASE_PRIMITIVE_RADIUS * unitScale;
46009
+ const modelScale = UNIT_BASE_MODEL_SCALE * unitScale * gridConfig.cellSize;
46010
+ const billboardHeight = UNIT_BASE_BILLBOARD_HEIGHT * unitScale * gridConfig.cellSize;
46011
+ const primitiveRadius = UNIT_BASE_PRIMITIVE_RADIUS * unitScale * gridConfig.cellSize;
45946
46012
  return /* @__PURE__ */ jsxRuntime.jsxs(
45947
46013
  "group",
45948
46014
  {
@@ -45989,7 +46055,7 @@ var init_GameCanvas3D2 = __esm({
45989
46055
  /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
45990
46056
  ] })
45991
46057
  ] }),
45992
- unit.health !== void 0 && unit.maxHealth !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("group", { position: [0, 1.2, 0], children: [
46058
+ unit.health !== void 0 && unit.maxHealth !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("group", { position: [0, billboardHeight, 0], children: [
45993
46059
  /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [-0.25, 0, 0], children: [
45994
46060
  /* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [0.5, 0.05] }),
45995
46061
  /* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: 3355443 })
@@ -46018,7 +46084,7 @@ var init_GameCanvas3D2 = __esm({
46018
46084
  }
46019
46085
  );
46020
46086
  },
46021
- [selectedUnitId, handleUnitClick, resolveUnitFrame, unitScale]
46087
+ [selectedUnitId, handleUnitClick, resolveUnitFrame, unitScale, gridConfig]
46022
46088
  );
46023
46089
  const DefaultFeatureRenderer = React93.useCallback(
46024
46090
  ({
@@ -46106,7 +46172,7 @@ var init_GameCanvas3D2 = __esm({
46106
46172
  {
46107
46173
  ref: containerRef,
46108
46174
  className: cn("game-canvas-3d relative w-full overflow-hidden", className),
46109
- style: { height: "85vh" },
46175
+ style: { flex: "1 1 0", minHeight: "400px" },
46110
46176
  "data-orientation": orientation,
46111
46177
  "data-camera-mode": cameraMode,
46112
46178
  "data-overlay": overlay,
@@ -46406,13 +46472,39 @@ var init_GameBoard3D = __esm({
46406
46472
  GameBoard3D.displayName = "GameBoard3D";
46407
46473
  }
46408
46474
  });
46409
- var GameShell;
46475
+ function getSlotContentRenderer2() {
46476
+ if (_scr) return _scr;
46477
+ const mod = (init_UISlotRenderer(), __toCommonJS(UISlotRenderer_exports));
46478
+ _scr = mod.SlotContentRenderer;
46479
+ return _scr;
46480
+ }
46481
+ function resolveDescriptor(value, idPrefix) {
46482
+ if (value === null || value === void 0) return value;
46483
+ if (React93__namespace.default.isValidElement(value)) return value;
46484
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
46485
+ if (Array.isArray(value)) {
46486
+ return value.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(React93__namespace.default.Fragment, { children: resolveDescriptor(item, `${idPrefix}-${i}`) }, i));
46487
+ }
46488
+ if (typeof value === "object") {
46489
+ const rec = value;
46490
+ if (typeof rec.type === "string" && patterns.getComponentForPattern(rec.type) !== null) {
46491
+ const { type, props: nestedProps, _id, ...flatProps } = rec;
46492
+ const resolvedProps = nestedProps !== void 0 ? nestedProps : flatProps;
46493
+ const content = { id: _id ?? idPrefix, pattern: type, props: resolvedProps, priority: 0 };
46494
+ const SCR = getSlotContentRenderer2();
46495
+ return /* @__PURE__ */ jsxRuntime.jsx(SCR, { content, onDismiss: () => void 0 });
46496
+ }
46497
+ }
46498
+ return null;
46499
+ }
46500
+ var _scr, GameShell;
46410
46501
  var init_GameShell = __esm({
46411
46502
  "components/game/templates/GameShell.tsx"() {
46412
46503
  init_cn();
46413
46504
  init_Box();
46414
46505
  init_Stack();
46415
46506
  init_Typography();
46507
+ _scr = null;
46416
46508
  GameShell = ({
46417
46509
  appName = "Game",
46418
46510
  hud,
@@ -46461,7 +46553,7 @@ var init_GameShell = __esm({
46461
46553
  children: appName
46462
46554
  }
46463
46555
  ),
46464
- hud && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "game-shell__hud", children: hud })
46556
+ hud && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "game-shell__hud", children: resolveDescriptor(hud, "gs-hud") })
46465
46557
  ]
46466
46558
  }
46467
46559
  ),
@@ -46474,7 +46566,7 @@ var init_GameShell = __esm({
46474
46566
  overflow: "hidden",
46475
46567
  position: "relative"
46476
46568
  },
46477
- children
46569
+ children: resolveDescriptor(children, "gs-children")
46478
46570
  }
46479
46571
  )
46480
46572
  ]
@@ -46484,7 +46576,32 @@ var init_GameShell = __esm({
46484
46576
  GameShell.displayName = "GameShell";
46485
46577
  }
46486
46578
  });
46487
- var GameTemplate;
46579
+ function getSlotContentRenderer3() {
46580
+ if (_scr2) return _scr2;
46581
+ const mod = (init_UISlotRenderer(), __toCommonJS(UISlotRenderer_exports));
46582
+ _scr2 = mod.SlotContentRenderer;
46583
+ return _scr2;
46584
+ }
46585
+ function resolveDescriptor2(value, idPrefix) {
46586
+ if (value === null || value === void 0) return value;
46587
+ if (React93__namespace.default.isValidElement(value)) return value;
46588
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
46589
+ if (Array.isArray(value)) {
46590
+ return value.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(React93__namespace.default.Fragment, { children: resolveDescriptor2(item, `${idPrefix}-${i}`) }, i));
46591
+ }
46592
+ if (typeof value === "object") {
46593
+ const rec = value;
46594
+ if (typeof rec.type === "string" && patterns.getComponentForPattern(rec.type) !== null) {
46595
+ const { type, props: nestedProps, _id, ...flatProps } = rec;
46596
+ const resolvedProps = nestedProps !== void 0 ? nestedProps : flatProps;
46597
+ const content = { id: _id ?? idPrefix, pattern: type, props: resolvedProps, priority: 0 };
46598
+ const SCR = getSlotContentRenderer3();
46599
+ return /* @__PURE__ */ jsxRuntime.jsx(SCR, { content, onDismiss: () => void 0 });
46600
+ }
46601
+ }
46602
+ return null;
46603
+ }
46604
+ var _scr2, GameTemplate;
46488
46605
  var init_GameTemplate = __esm({
46489
46606
  "components/game/templates/GameTemplate.tsx"() {
46490
46607
  init_cn();
@@ -46492,6 +46609,7 @@ var init_GameTemplate = __esm({
46492
46609
  init_Stack();
46493
46610
  init_Typography();
46494
46611
  init_Button();
46612
+ _scr2 = null;
46495
46613
  GameTemplate = ({
46496
46614
  entity,
46497
46615
  title = "Game",
@@ -46559,13 +46677,13 @@ var init_GameTemplate = __esm({
46559
46677
  fullWidth: true,
46560
46678
  className: "flex-1 bg-muted",
46561
46679
  children: [
46562
- children,
46680
+ resolveDescriptor2(children, "gt-children"),
46563
46681
  hud && /* @__PURE__ */ jsxRuntime.jsx(
46564
46682
  Box,
46565
46683
  {
46566
46684
  position: "absolute",
46567
46685
  className: "top-0 left-0 right-0 pointer-events-none",
46568
- children: /* @__PURE__ */ jsxRuntime.jsx(Box, { padding: "md", className: "pointer-events-auto w-fit", children: hud })
46686
+ children: /* @__PURE__ */ jsxRuntime.jsx(Box, { padding: "md", className: "pointer-events-auto w-fit", children: resolveDescriptor2(hud, "gt-hud") })
46569
46687
  }
46570
46688
  )
46571
46689
  ]
@@ -46590,7 +46708,7 @@ var init_GameTemplate = __esm({
46590
46708
  children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h6", children: "Debug Panel" })
46591
46709
  }
46592
46710
  ),
46593
- /* @__PURE__ */ jsxRuntime.jsx(Box, { padding: "md", children: debugPanel })
46711
+ /* @__PURE__ */ jsxRuntime.jsx(Box, { padding: "md", children: resolveDescriptor2(debugPanel, "gt-debug") })
46594
46712
  ]
46595
46713
  }
46596
46714
  )
@@ -46760,6 +46878,48 @@ var init_HeroOrganism = __esm({
46760
46878
  _HeroClickInterceptor.displayName = "_HeroClickInterceptor";
46761
46879
  }
46762
46880
  });
46881
+ function HexStrategyBoard({
46882
+ tiles,
46883
+ units,
46884
+ features,
46885
+ assetManifest,
46886
+ assetBaseUrl,
46887
+ scale = 0.45,
46888
+ showMinimap = true,
46889
+ enableCamera = true,
46890
+ tileClickEvent,
46891
+ unitClickEvent,
46892
+ isLoading,
46893
+ error,
46894
+ className
46895
+ }) {
46896
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("hex-strategy-board relative w-full h-full", className), children: /* @__PURE__ */ jsxRuntime.jsx(
46897
+ IsometricCanvas_default,
46898
+ {
46899
+ tileLayout: "hex",
46900
+ tiles,
46901
+ units,
46902
+ features,
46903
+ assetManifest,
46904
+ assetBaseUrl,
46905
+ scale,
46906
+ showMinimap,
46907
+ enableCamera,
46908
+ tileClickEvent,
46909
+ unitClickEvent,
46910
+ isLoading,
46911
+ error
46912
+ }
46913
+ ) });
46914
+ }
46915
+ var init_HexStrategyBoard = __esm({
46916
+ "components/game/organisms/HexStrategyBoard.tsx"() {
46917
+ "use client";
46918
+ init_cn();
46919
+ init_IsometricCanvas();
46920
+ HexStrategyBoard.displayName = "HexStrategyBoard";
46921
+ }
46922
+ });
46763
46923
  var LandingPageTemplate;
46764
46924
  var init_LandingPageTemplate = __esm({
46765
46925
  "components/marketing/templates/LandingPageTemplate.tsx"() {
@@ -54588,6 +54748,7 @@ var init_component_registry_generated = __esm({
54588
54748
  init_HealthPanel();
54589
54749
  init_HeroOrganism();
54590
54750
  init_HeroSection();
54751
+ init_HexStrategyBoard();
54591
54752
  init_Icon();
54592
54753
  init_InfiniteScrollSentinel();
54593
54754
  init_Input();
@@ -54924,6 +55085,7 @@ var init_component_registry_generated = __esm({
54924
55085
  "HealthPanel": HealthPanel,
54925
55086
  "HeroOrganism": HeroOrganism,
54926
55087
  "HeroSection": HeroSection,
55088
+ "HexStrategyBoard": HexStrategyBoard,
54927
55089
  "Icon": Icon,
54928
55090
  "InfiniteScrollSentinel": InfiniteScrollSentinel,
54929
55091
  "Input": Input,
@@ -55125,7 +55287,7 @@ function getSlotFallback(slot, config) {
55125
55287
  const variant = SLOT_SKELETON_MAP[slot] ?? "text";
55126
55288
  return /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { variant });
55127
55289
  }
55128
- function getComponentForPattern(patternType) {
55290
+ function getComponentForPattern3(patternType) {
55129
55291
  const mapping = patterns.getComponentForPattern(patternType);
55130
55292
  if (!mapping) {
55131
55293
  return null;
@@ -55756,7 +55918,8 @@ function SlotContentRenderer({
55756
55918
  entityDef = schemaCtx.entities.get(linkedEntity);
55757
55919
  }
55758
55920
  }
55759
- const PatternComponent = getComponentForPattern(content.pattern);
55921
+ const orbitalName = schemaCtx && content.sourceTrait !== void 0 ? schemaCtx.orbitalsByTrait.get(content.sourceTrait) : void 0;
55922
+ const PatternComponent = getComponentForPattern3(content.pattern);
55760
55923
  if (PatternComponent) {
55761
55924
  const childrenConfig = content.props.children;
55762
55925
  const isSingleChild = typeof childrenConfig === "string" || typeof childrenConfig === "object" && childrenConfig !== null && !Array.isArray(childrenConfig) && "type" in childrenConfig;
@@ -55854,6 +56017,7 @@ function SlotContentRenderer({
55854
56017
  "data-orb-entity": content.entity,
55855
56018
  "data-orb-path": myPath,
55856
56019
  "data-orb-pattern": content.pattern,
56020
+ "data-orb-orbital": orbitalName,
55857
56021
  children: renderedChildren !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(PatternComponent, { ...finalProps, children: renderedChildren }) : /* @__PURE__ */ jsxRuntime.jsx(PatternComponent, { ...finalProps })
55858
56022
  }
55859
56023
  );
@@ -55873,6 +56037,7 @@ function SlotContentRenderer({
55873
56037
  "data-orb-entity": content.entity,
55874
56038
  "data-orb-path": patternPath ?? "root",
55875
56039
  "data-orb-pattern": content.pattern,
56040
+ "data-orb-orbital": orbitalName,
55876
56041
  children: content.props.children ?? /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "p-4 text-sm text-muted-foreground border border-dashed border-border rounded", children: [
55877
56042
  "Unknown pattern: ",
55878
56043
  content.pattern,
@@ -59515,7 +59680,7 @@ function resolveLambdaBindings(body, params, item, index) {
59515
59680
  return body;
59516
59681
  }
59517
59682
  var _slotContentRenderer2 = null;
59518
- function getSlotContentRenderer2() {
59683
+ function getSlotContentRenderer4() {
59519
59684
  if (_slotContentRenderer2) return _slotContentRenderer2;
59520
59685
  const mod = (init_UISlotRenderer(), __toCommonJS(UISlotRenderer_exports));
59521
59686
  _slotContentRenderer2 = mod.SlotContentRenderer;
@@ -59531,7 +59696,7 @@ function makeLambdaFn(params, lambdaBody, callerKey) {
59531
59696
  if (typeof record.type !== "string") {
59532
59697
  return null;
59533
59698
  }
59534
- const SlotContentRenderer2 = getSlotContentRenderer2();
59699
+ const SlotContentRenderer2 = getSlotContentRenderer4();
59535
59700
  const rawChildProps = {};
59536
59701
  for (const [k, v] of Object.entries(record)) {
59537
59702
  if (k !== "type") rawChildProps[k] = v;
@@ -61891,6 +62056,33 @@ function formatPayloadTooltip(fields) {
61891
62056
  );
61892
62057
  return `{ ${parts.join(", ")} }`;
61893
62058
  }
62059
+
62060
+ // components/avl/derive-edit-focus.ts
62061
+ function deriveEditFocusFromElement(el) {
62062
+ const orbitalEl = el.getAttribute("data-orb-orbital") !== null ? el : el.closest("[data-orb-orbital]");
62063
+ const orbital = orbitalEl?.getAttribute("data-orb-orbital") ?? "";
62064
+ if (!orbital) return null;
62065
+ const path = el.getAttribute("data-orb-path") ?? el.getAttribute("data-pattern-path");
62066
+ const patternType = el.getAttribute("data-orb-pattern") ?? el.getAttribute("data-pattern");
62067
+ const trait = el.getAttribute("data-orb-trait") ?? el.getAttribute("data-source-trait");
62068
+ const focus = {
62069
+ level: "node",
62070
+ orbital,
62071
+ label: patternType ?? trait ?? "element"
62072
+ };
62073
+ if (path !== null) focus.path = path;
62074
+ if (trait !== null) focus.trait = trait;
62075
+ if (patternType !== null) focus.patternType = patternType;
62076
+ const transition = el.getAttribute("data-orb-transition");
62077
+ if (transition !== null) focus.transition = transition;
62078
+ const state = el.getAttribute("data-orb-state");
62079
+ if (state !== null) focus.state = state;
62080
+ const slot = el.getAttribute("data-orb-slot");
62081
+ if (slot !== null) focus.slot = slot;
62082
+ const entity = el.getAttribute("data-orb-entity");
62083
+ if (entity !== null) focus.entity = entity;
62084
+ return focus;
62085
+ }
61894
62086
  function entityNameOf(ref) {
61895
62087
  if (!ref) return void 0;
61896
62088
  if (typeof ref === "string") return ref;
@@ -62176,29 +62368,6 @@ function rectRelativeTo(el, container, zoom) {
62176
62368
  height: (u.bottom - u.top) / z
62177
62369
  };
62178
62370
  }
62179
- function buildFocus(el, orbitalName) {
62180
- if (!orbitalName) return null;
62181
- const path = el.getAttribute("data-orb-path") ?? el.getAttribute("data-pattern-path");
62182
- const patternType = el.getAttribute("data-orb-pattern") ?? el.getAttribute("data-pattern");
62183
- const trait = el.getAttribute("data-orb-trait") ?? el.getAttribute("data-source-trait");
62184
- const focus = {
62185
- level: "node",
62186
- orbital: orbitalName,
62187
- label: patternType ?? trait ?? "element"
62188
- };
62189
- if (path !== null) focus.path = path;
62190
- if (trait !== null) focus.trait = trait;
62191
- if (patternType !== null) focus.patternType = patternType;
62192
- const transition = el.getAttribute("data-orb-transition");
62193
- if (transition !== null) focus.transition = transition;
62194
- const state = el.getAttribute("data-orb-state");
62195
- if (state !== null) focus.state = state;
62196
- const slot = el.getAttribute("data-orb-slot");
62197
- if (slot !== null) focus.slot = slot;
62198
- const entity = el.getAttribute("data-orb-entity");
62199
- if (entity !== null) focus.entity = entity;
62200
- return focus;
62201
- }
62202
62371
  var OrbPreviewNodeInner = (props) => {
62203
62372
  const data = props.data;
62204
62373
  const screenSize = React93.useContext(ScreenSizeContext);
@@ -62267,7 +62436,7 @@ var OrbPreviewNodeInner = (props) => {
62267
62436
  nodeData: data,
62268
62437
  rect: rect ?? void 0
62269
62438
  });
62270
- const focus = buildFocus(patternEl, data.orbitalName);
62439
+ const focus = deriveEditFocusFromElement(patternEl);
62271
62440
  if (focus) eventBus.emit("UI:ELEMENT_SELECTED", { focus: { ...focus } });
62272
62441
  } else {
62273
62442
  setSelectedRect(null);
@@ -65557,6 +65726,7 @@ exports.behaviorsToComposeGraph = behaviorsToComposeGraph;
65557
65726
  exports.computeTraitLayout = computeTraitLayout;
65558
65727
  exports.computeZoomBand = computeZoomBand;
65559
65728
  exports.curveControlPoint = curveControlPoint;
65729
+ exports.deriveEditFocusFromElement = deriveEditFocusFromElement;
65560
65730
  exports.edgePath = edgePath;
65561
65731
  exports.getStateRole = getStateRole;
65562
65732
  exports.gridPositions = gridPositions;