@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
package/dist/avl/index.js CHANGED
@@ -46,7 +46,7 @@ import { GLTFLoader as GLTFLoader$1 } from 'three/examples/jsm/loaders/GLTFLoade
46
46
  import { clone } from 'three/examples/jsm/utils/SkeletonUtils';
47
47
  import { Canvas, useLoader, useFrame, useThree } from '@react-three/fiber';
48
48
  import { Billboard, Grid as Grid$1, OrbitControls } from '@react-three/drei';
49
- import { getPatternDefinition, getComponentForPattern as getComponentForPattern$1, isEntityAwarePattern } from '@almadar/patterns';
49
+ import { getPatternDefinition, getComponentForPattern, isEntityAwarePattern } from '@almadar/patterns';
50
50
  import { OrbitalServerRuntime } from '@almadar/runtime/OrbitalServerRuntime';
51
51
  import { InMemoryPersistence, StateMachineManager, collectDeclaredConfigDefaults, createServerEffectHandlers, EffectExecutor, createContextFromBindings } from '@almadar/runtime';
52
52
 
@@ -11755,6 +11755,8 @@ var init_DialogueBubble = __esm({
11755
11755
  function ChoiceButton({
11756
11756
  text = "Charge forward into the fray",
11757
11757
  index,
11758
+ assetUrl,
11759
+ icon,
11758
11760
  disabled = false,
11759
11761
  selected = false,
11760
11762
  onClick,
@@ -11787,6 +11789,23 @@ function ChoiceButton({
11787
11789
  ]
11788
11790
  }
11789
11791
  ),
11792
+ assetUrl ? /* @__PURE__ */ jsx(
11793
+ "img",
11794
+ {
11795
+ src: assetUrl,
11796
+ alt: "",
11797
+ width: 16,
11798
+ height: 16,
11799
+ style: { imageRendering: "pixelated", objectFit: "contain" },
11800
+ className: "flex-shrink-0"
11801
+ }
11802
+ ) : icon ? /* @__PURE__ */ jsx("span", { className: "flex-shrink-0 text-sm", children: typeof icon === "string" ? (() => {
11803
+ const I = resolveIcon(icon);
11804
+ return I ? /* @__PURE__ */ jsx(I, { className: "w-4 h-4" }) : null;
11805
+ })() : /* @__PURE__ */ (() => {
11806
+ const I = icon;
11807
+ return /* @__PURE__ */ jsx(I, { className: "w-4 h-4" });
11808
+ })() }) : null,
11790
11809
  /* @__PURE__ */ jsx("span", { className: "text-sm leading-snug", children: text })
11791
11810
  ]
11792
11811
  }
@@ -11795,6 +11814,7 @@ function ChoiceButton({
11795
11814
  var init_ChoiceButton = __esm({
11796
11815
  "components/game/atoms/ChoiceButton.tsx"() {
11797
11816
  init_cn();
11817
+ init_Icon();
11798
11818
  ChoiceButton.displayName = "ChoiceButton";
11799
11819
  }
11800
11820
  });
@@ -14676,16 +14696,26 @@ var init_useUnitSpriteAtlas = __esm({
14676
14696
  });
14677
14697
 
14678
14698
  // components/game/organisms/utils/isometric.ts
14679
- function isoToScreen(tileX, tileY, scale, baseOffsetX) {
14699
+ function isoToScreen(tileX, tileY, scale, baseOffsetX, layout = "isometric") {
14680
14700
  const scaledTileWidth = TILE_WIDTH * scale;
14681
14701
  const scaledFloorHeight = FLOOR_HEIGHT * scale;
14702
+ if (layout === "hex") {
14703
+ const screenX2 = tileX * scaledTileWidth + (tileY & 1) * (scaledTileWidth / 2) + baseOffsetX;
14704
+ const screenY2 = tileY * (scaledFloorHeight * 0.75);
14705
+ return { x: screenX2, y: screenY2 };
14706
+ }
14682
14707
  const screenX = (tileX - tileY) * (scaledTileWidth / 2) + baseOffsetX;
14683
14708
  const screenY = (tileX + tileY) * (scaledFloorHeight / 2);
14684
14709
  return { x: screenX, y: screenY };
14685
14710
  }
14686
- function screenToIso(screenX, screenY, scale, baseOffsetX) {
14711
+ function screenToIso(screenX, screenY, scale, baseOffsetX, layout = "isometric") {
14687
14712
  const scaledTileWidth = TILE_WIDTH * scale;
14688
14713
  const scaledFloorHeight = FLOOR_HEIGHT * scale;
14714
+ if (layout === "hex") {
14715
+ const row = Math.round(screenY / (scaledFloorHeight * 0.75));
14716
+ const col = Math.round((screenX - (row & 1) * (scaledTileWidth / 2) - baseOffsetX) / scaledTileWidth);
14717
+ return { x: col, y: row };
14718
+ }
14689
14719
  const adjustedX = screenX - baseOffsetX;
14690
14720
  const tileX = (adjustedX / (scaledTileWidth / 2) + screenY / (scaledFloorHeight / 2)) / 2;
14691
14721
  const tileY = (screenY / (scaledFloorHeight / 2) - adjustedX / (scaledTileWidth / 2)) / 2;
@@ -14735,6 +14765,7 @@ function IsometricCanvas({
14735
14765
  tileHoverEvent,
14736
14766
  tileLeaveEvent,
14737
14767
  // Rendering options
14768
+ tileLayout = "isometric",
14738
14769
  scale = 0.4,
14739
14770
  debug: debug2 = false,
14740
14771
  backgroundImage = "",
@@ -14804,13 +14835,17 @@ function IsometricCanvas({
14804
14835
  );
14805
14836
  const sortedTiles = useMemo(() => {
14806
14837
  const tiles = [...tilesProp];
14807
- tiles.sort((a, b) => {
14808
- const depthA = a.x + a.y;
14809
- const depthB = b.x + b.y;
14810
- return depthA !== depthB ? depthA - depthB : a.y - b.y;
14811
- });
14838
+ if (tileLayout === "hex") {
14839
+ tiles.sort((a, b) => a.y !== b.y ? a.y - b.y : a.x - b.x);
14840
+ } else {
14841
+ tiles.sort((a, b) => {
14842
+ const depthA = a.x + a.y;
14843
+ const depthB = b.x + b.y;
14844
+ return depthA !== depthB ? depthA - depthB : a.y - b.y;
14845
+ });
14846
+ }
14812
14847
  return tiles;
14813
- }, [tilesProp]);
14848
+ }, [tilesProp, tileLayout]);
14814
14849
  const gridWidth = useMemo(() => {
14815
14850
  if (sortedTiles.length === 0) return 0;
14816
14851
  return Math.max(...sortedTiles.map((t2) => t2.x)) + 1;
@@ -14924,18 +14959,19 @@ function IsometricCanvas({
14924
14959
  miniCanvas.width = mW;
14925
14960
  miniCanvas.height = mH;
14926
14961
  mCtx.clearRect(0, 0, mW, mH);
14927
- const allScreenPos = sortedTiles.map((t2) => isoToScreen(t2.x, t2.y, scale, baseOffsetX));
14962
+ const allScreenPos = sortedTiles.map((t2) => isoToScreen(t2.x, t2.y, scale, baseOffsetX, tileLayout));
14928
14963
  const minX = Math.min(...allScreenPos.map((p2) => p2.x));
14929
14964
  const maxX = Math.max(...allScreenPos.map((p2) => p2.x + scaledTileWidth));
14930
14965
  const minY = Math.min(...allScreenPos.map((p2) => p2.y));
14931
- const maxY = Math.max(...allScreenPos.map((p2) => p2.y + scaledTileHeight));
14966
+ const tileBottomExtent = tileLayout === "hex" ? scaledTileWidth : scaledTileHeight;
14967
+ const maxY = Math.max(...allScreenPos.map((p2) => p2.y + tileBottomExtent));
14932
14968
  const worldW = maxX - minX;
14933
14969
  const worldH = maxY - minY;
14934
14970
  const scaleM = Math.min(mW / worldW, mH / worldH) * 0.9;
14935
14971
  const offsetMx = (mW - worldW * scaleM) / 2;
14936
14972
  const offsetMy = (mH - worldH * scaleM) / 2;
14937
14973
  for (const tile of sortedTiles) {
14938
- const pos = isoToScreen(tile.x, tile.y, scale, baseOffsetX);
14974
+ const pos = isoToScreen(tile.x, tile.y, scale, baseOffsetX, tileLayout);
14939
14975
  const mx = (pos.x - minX) * scaleM + offsetMx;
14940
14976
  const my = (pos.y - minY) * scaleM + offsetMy;
14941
14977
  const mTileW = scaledTileWidth * scaleM;
@@ -14951,7 +14987,7 @@ function IsometricCanvas({
14951
14987
  }
14952
14988
  for (const unit of units) {
14953
14989
  if (!unit.position) continue;
14954
- const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX);
14990
+ const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX, tileLayout);
14955
14991
  const mx = (pos.x + scaledTileWidth / 2 - minX) * scaleM + offsetMx;
14956
14992
  const my = (pos.y + scaledTileHeight / 2 - minY) * scaleM + offsetMy;
14957
14993
  mCtx.fillStyle = unit.team === "player" ? "#60a5fa" : unit.team === "enemy" ? "#f87171" : "#9ca3af";
@@ -14967,7 +15003,7 @@ function IsometricCanvas({
14967
15003
  mCtx.strokeStyle = "rgba(255, 255, 255, 0.8)";
14968
15004
  mCtx.lineWidth = 1;
14969
15005
  mCtx.strokeRect(vLeft, vTop, vW, vH);
14970
- }, [showMinimap, sortedTiles, units, scale, baseOffsetX, scaledTileWidth, scaledTileHeight, scaledFloorHeight, viewportSize, cameraRef]);
15006
+ }, [showMinimap, sortedTiles, units, tileLayout, scale, baseOffsetX, scaledTileWidth, scaledTileHeight, scaledFloorHeight, viewportSize, cameraRef]);
14971
15007
  const draw = useCallback((animTime) => {
14972
15008
  const canvas = canvasRef.current;
14973
15009
  if (!canvas) return;
@@ -15007,7 +15043,7 @@ function IsometricCanvas({
15007
15043
  const visTop = cam.y - viewportSize.height / cam.zoom;
15008
15044
  const visBottom = cam.y + viewportSize.height * 2 / cam.zoom;
15009
15045
  for (const tile of sortedTiles) {
15010
- const pos = isoToScreen(tile.x, tile.y, scale, baseOffsetX);
15046
+ const pos = isoToScreen(tile.x, tile.y, scale, baseOffsetX, tileLayout);
15011
15047
  if (pos.x + scaledTileWidth < visLeft || pos.x > visRight || pos.y + scaledTileHeight < visTop || pos.y > visBottom) {
15012
15048
  continue;
15013
15049
  }
@@ -15020,7 +15056,7 @@ function IsometricCanvas({
15020
15056
  const drawW = scaledTileWidth;
15021
15057
  const drawH = scaledTileWidth * (img.naturalHeight / img.naturalWidth);
15022
15058
  const drawX = pos.x;
15023
- const drawY = pos.y + scaledTileHeight - drawH;
15059
+ const drawY = tileLayout === "hex" ? pos.y : pos.y + scaledTileHeight - drawH;
15024
15060
  ctx.drawImage(img, drawX, drawY, drawW, drawH);
15025
15061
  }
15026
15062
  } else {
@@ -15087,12 +15123,13 @@ function IsometricCanvas({
15087
15123
  }
15088
15124
  }
15089
15125
  const sortedFeatures = [...features].sort((a, b) => {
15126
+ if (tileLayout === "hex") return a.y !== b.y ? a.y - b.y : a.x - b.x;
15090
15127
  const depthA = a.x + a.y;
15091
15128
  const depthB = b.x + b.y;
15092
15129
  return depthA !== depthB ? depthA - depthB : a.y - b.y;
15093
15130
  });
15094
15131
  for (const feature of sortedFeatures) {
15095
- const pos = isoToScreen(feature.x, feature.y, scale, baseOffsetX);
15132
+ const pos = isoToScreen(feature.x, feature.y, scale, baseOffsetX, tileLayout);
15096
15133
  if (pos.x + scaledTileWidth < visLeft || pos.x > visRight || pos.y + scaledTileHeight < visTop || pos.y > visBottom) {
15097
15134
  continue;
15098
15135
  }
@@ -15127,12 +15164,13 @@ function IsometricCanvas({
15127
15164
  }
15128
15165
  const unitsWithPosition = units.filter((u) => !!u.position);
15129
15166
  const sortedUnits = [...unitsWithPosition].sort((a, b) => {
15167
+ if (tileLayout === "hex") return a.position.y !== b.position.y ? a.position.y - b.position.y : a.position.x - b.position.x;
15130
15168
  const depthA = a.position.x + a.position.y;
15131
15169
  const depthB = b.position.x + b.position.y;
15132
15170
  return depthA !== depthB ? depthA - depthB : a.position.y - b.position.y;
15133
15171
  });
15134
15172
  for (const unit of sortedUnits) {
15135
- const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX);
15173
+ const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX, tileLayout);
15136
15174
  if (pos.x + scaledTileWidth < visLeft || pos.x > visRight || pos.y + scaledTileHeight < visTop || pos.y > visBottom) {
15137
15175
  continue;
15138
15176
  }
@@ -15152,7 +15190,7 @@ function IsometricCanvas({
15152
15190
  drawH = maxUnitW / ar;
15153
15191
  }
15154
15192
  if (unit.previousPosition && (unit.previousPosition.x !== unit.position.x || unit.previousPosition.y !== unit.position.y)) {
15155
- const ghostPos = isoToScreen(unit.previousPosition.x, unit.previousPosition.y, scale, baseOffsetX);
15193
+ const ghostPos = isoToScreen(unit.previousPosition.x, unit.previousPosition.y, scale, baseOffsetX, tileLayout);
15156
15194
  const ghostCenterX = ghostPos.x + scaledTileWidth / 2;
15157
15195
  const ghostGroundY = ghostPos.y + scaledDiamondTopY + scaledFloorHeight * 0.5;
15158
15196
  ctx.save();
@@ -15281,6 +15319,7 @@ function IsometricCanvas({
15281
15319
  units,
15282
15320
  features,
15283
15321
  selectedUnitId,
15322
+ tileLayout,
15284
15323
  scale,
15285
15324
  debug2,
15286
15325
  resolveTerrainSpriteUrl,
@@ -15309,14 +15348,14 @@ function IsometricCanvas({
15309
15348
  if (!selectedUnitId) return;
15310
15349
  const unit = units.find((u) => u.id === selectedUnitId);
15311
15350
  if (!unit?.position) return;
15312
- const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX);
15351
+ const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX, tileLayout);
15313
15352
  const centerX = pos.x + scaledTileWidth / 2;
15314
15353
  const centerY = pos.y + scaledDiamondTopY + scaledFloorHeight / 2;
15315
15354
  targetCameraRef.current = {
15316
15355
  x: centerX - viewportSize.width / 2,
15317
15356
  y: centerY - viewportSize.height / 2
15318
15357
  };
15319
- }, [selectedUnitId, units, scale, baseOffsetX, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, viewportSize, targetCameraRef]);
15358
+ }, [selectedUnitId, units, tileLayout, scale, baseOffsetX, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, viewportSize, targetCameraRef]);
15320
15359
  useEffect(() => {
15321
15360
  const hasAnimations = units.length > 0 || validMoves.length > 0 || attackTargets.length > 0 || selectedUnitId != null || targetCameraRef.current != null || hasActiveEffects2 || pendingCount > 0 || atlasPending > 0;
15322
15361
  draw(animTimeRef.current);
@@ -15349,13 +15388,13 @@ function IsometricCanvas({
15349
15388
  const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
15350
15389
  const adjustedX = world.x - scaledTileWidth / 2;
15351
15390
  const adjustedY = world.y - scaledDiamondTopY - scaledFloorHeight / 2;
15352
- const isoPos = screenToIso(adjustedX, adjustedY, scale, baseOffsetX);
15391
+ const isoPos = screenToIso(adjustedX, adjustedY, scale, baseOffsetX, tileLayout);
15353
15392
  const tileExists = tilesProp.some((t2) => t2.x === isoPos.x && t2.y === isoPos.y);
15354
15393
  if (tileExists) {
15355
15394
  if (tileHoverEvent) eventBus.emit(`UI:${tileHoverEvent}`, { x: isoPos.x, y: isoPos.y });
15356
15395
  onTileHover?.(isoPos.x, isoPos.y);
15357
15396
  }
15358
- }, [onTileHover, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, tilesProp, tileHoverEvent, eventBus]);
15397
+ }, [onTileHover, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, tileLayout, scale, baseOffsetX, tilesProp, tileHoverEvent, eventBus]);
15359
15398
  const handleCanvasPointerUp = useCallback((e) => {
15360
15399
  singlePointerActiveRef.current = false;
15361
15400
  if (enableCamera) handlePointerUp();
@@ -15364,7 +15403,7 @@ function IsometricCanvas({
15364
15403
  const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
15365
15404
  const adjustedX = world.x - scaledTileWidth / 2;
15366
15405
  const adjustedY = world.y - scaledDiamondTopY - scaledFloorHeight / 2;
15367
- const isoPos = screenToIso(adjustedX, adjustedY, scale, baseOffsetX);
15406
+ const isoPos = screenToIso(adjustedX, adjustedY, scale, baseOffsetX, tileLayout);
15368
15407
  const clickedUnit = units.find((u) => u.position?.x === isoPos.x && u.position?.y === isoPos.y);
15369
15408
  if (clickedUnit && (onUnitClick || unitClickEvent)) {
15370
15409
  if (unitClickEvent) eventBus.emit(`UI:${unitClickEvent}`, { unitId: clickedUnit.id });
@@ -15376,7 +15415,7 @@ function IsometricCanvas({
15376
15415
  onTileClick?.(isoPos.x, isoPos.y);
15377
15416
  }
15378
15417
  }
15379
- }, [enableCamera, handlePointerUp, dragDistance, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, units, tilesProp, onUnitClick, onTileClick, unitClickEvent, tileClickEvent, eventBus]);
15418
+ }, [enableCamera, handlePointerUp, dragDistance, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, tileLayout, scale, baseOffsetX, units, tilesProp, onUnitClick, onTileClick, unitClickEvent, tileClickEvent, eventBus]);
15380
15419
  const handleCanvasPointerLeave = useCallback(() => {
15381
15420
  handleMouseLeave();
15382
15421
  if (tileLeaveEvent) eventBus.emit(`UI:${tileLeaveEvent}`, {});
@@ -30940,6 +30979,7 @@ function PowerupSlots({
30940
30979
  /* @__PURE__ */ jsx(
30941
30980
  ItemSlot,
30942
30981
  {
30982
+ assetUrl: powerup.assetUrl,
30943
30983
  icon: powerup.icon,
30944
30984
  label: powerup.label,
30945
30985
  rarity: "uncommon",
@@ -31189,13 +31229,26 @@ function HealthPanel({
31189
31229
  )
31190
31230
  }
31191
31231
  ),
31192
- effects && effects.length > 0 && /* @__PURE__ */ jsx(Box, { className: "flex items-center gap-1 flex-wrap", children: effects.map((effect, i) => /* @__PURE__ */ jsx(
31232
+ effects && effects.length > 0 && /* @__PURE__ */ jsx(Box, { className: "flex items-center gap-1 flex-wrap", children: effects.map((effect, i) => /* @__PURE__ */ jsxs(
31193
31233
  Badge,
31194
31234
  {
31195
31235
  variant: effectVariantMap[effect.variant ?? "neutral"],
31196
31236
  size: "sm",
31197
- icon: effect.icon,
31198
- children: effect.label
31237
+ icon: effect.assetUrl ? void 0 : effect.icon,
31238
+ children: [
31239
+ effect.assetUrl && /* @__PURE__ */ jsx(
31240
+ "img",
31241
+ {
31242
+ src: effect.assetUrl,
31243
+ alt: "",
31244
+ width: 12,
31245
+ height: 12,
31246
+ style: { imageRendering: "pixelated", objectFit: "contain" },
31247
+ className: "flex-shrink-0 inline-block"
31248
+ }
31249
+ ),
31250
+ effect.label
31251
+ ]
31199
31252
  },
31200
31253
  i
31201
31254
  )) })
@@ -31378,15 +31431,28 @@ function TurnPanel({
31378
31431
  activeTeam
31379
31432
  }
31380
31433
  ),
31381
- actions && actions.length > 0 && /* @__PURE__ */ jsx(Box, { className: "flex items-center gap-1.5 ml-auto", children: actions.map((action, i) => /* @__PURE__ */ jsx(
31434
+ actions && actions.length > 0 && /* @__PURE__ */ jsx(Box, { className: "flex items-center gap-1.5 ml-auto", children: actions.map((action, i) => /* @__PURE__ */ jsxs(
31382
31435
  Button,
31383
31436
  {
31384
31437
  variant: "ghost",
31385
31438
  size: "sm",
31386
31439
  disabled: action.disabled,
31387
- leftIcon: action.icon,
31440
+ leftIcon: action.assetUrl ? void 0 : action.icon,
31388
31441
  onClick: () => handleAction(action.event),
31389
- children: action.label
31442
+ children: [
31443
+ action.assetUrl && /* @__PURE__ */ jsx(
31444
+ "img",
31445
+ {
31446
+ src: action.assetUrl,
31447
+ alt: "",
31448
+ width: 14,
31449
+ height: 14,
31450
+ style: { imageRendering: "pixelated", objectFit: "contain" },
31451
+ className: "flex-shrink-0"
31452
+ }
31453
+ ),
31454
+ action.label
31455
+ ]
31390
31456
  },
31391
31457
  i
31392
31458
  )) })
@@ -45893,9 +45959,9 @@ var init_GameCanvas3D2 = __esm({
45893
45959
  const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
45894
45960
  const hasAtlas = unitAtlasUrl(unit) !== null;
45895
45961
  const initialFrame = hasAtlas ? resolveUnitFrame(unit.id) : null;
45896
- const modelScale = UNIT_BASE_MODEL_SCALE * unitScale;
45897
- const billboardHeight = UNIT_BASE_BILLBOARD_HEIGHT * unitScale;
45898
- const primitiveRadius = UNIT_BASE_PRIMITIVE_RADIUS * unitScale;
45962
+ const modelScale = UNIT_BASE_MODEL_SCALE * unitScale * gridConfig.cellSize;
45963
+ const billboardHeight = UNIT_BASE_BILLBOARD_HEIGHT * unitScale * gridConfig.cellSize;
45964
+ const primitiveRadius = UNIT_BASE_PRIMITIVE_RADIUS * unitScale * gridConfig.cellSize;
45899
45965
  return /* @__PURE__ */ jsxs(
45900
45966
  "group",
45901
45967
  {
@@ -45942,7 +46008,7 @@ var init_GameCanvas3D2 = __esm({
45942
46008
  /* @__PURE__ */ jsx("meshStandardMaterial", { color })
45943
46009
  ] })
45944
46010
  ] }),
45945
- unit.health !== void 0 && unit.maxHealth !== void 0 && /* @__PURE__ */ jsxs("group", { position: [0, 1.2, 0], children: [
46011
+ unit.health !== void 0 && unit.maxHealth !== void 0 && /* @__PURE__ */ jsxs("group", { position: [0, billboardHeight, 0], children: [
45946
46012
  /* @__PURE__ */ jsxs("mesh", { position: [-0.25, 0, 0], children: [
45947
46013
  /* @__PURE__ */ jsx("planeGeometry", { args: [0.5, 0.05] }),
45948
46014
  /* @__PURE__ */ jsx("meshBasicMaterial", { color: 3355443 })
@@ -45971,7 +46037,7 @@ var init_GameCanvas3D2 = __esm({
45971
46037
  }
45972
46038
  );
45973
46039
  },
45974
- [selectedUnitId, handleUnitClick, resolveUnitFrame, unitScale]
46040
+ [selectedUnitId, handleUnitClick, resolveUnitFrame, unitScale, gridConfig]
45975
46041
  );
45976
46042
  const DefaultFeatureRenderer = useCallback(
45977
46043
  ({
@@ -46059,7 +46125,7 @@ var init_GameCanvas3D2 = __esm({
46059
46125
  {
46060
46126
  ref: containerRef,
46061
46127
  className: cn("game-canvas-3d relative w-full overflow-hidden", className),
46062
- style: { height: "85vh" },
46128
+ style: { flex: "1 1 0", minHeight: "400px" },
46063
46129
  "data-orientation": orientation,
46064
46130
  "data-camera-mode": cameraMode,
46065
46131
  "data-overlay": overlay,
@@ -46359,13 +46425,39 @@ var init_GameBoard3D = __esm({
46359
46425
  GameBoard3D.displayName = "GameBoard3D";
46360
46426
  }
46361
46427
  });
46362
- var GameShell;
46428
+ function getSlotContentRenderer2() {
46429
+ if (_scr) return _scr;
46430
+ const mod = (init_UISlotRenderer(), __toCommonJS(UISlotRenderer_exports));
46431
+ _scr = mod.SlotContentRenderer;
46432
+ return _scr;
46433
+ }
46434
+ function resolveDescriptor(value, idPrefix) {
46435
+ if (value === null || value === void 0) return value;
46436
+ if (React93__default.isValidElement(value)) return value;
46437
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
46438
+ if (Array.isArray(value)) {
46439
+ return value.map((item, i) => /* @__PURE__ */ jsx(React93__default.Fragment, { children: resolveDescriptor(item, `${idPrefix}-${i}`) }, i));
46440
+ }
46441
+ if (typeof value === "object") {
46442
+ const rec = value;
46443
+ if (typeof rec.type === "string" && getComponentForPattern(rec.type) !== null) {
46444
+ const { type, props: nestedProps, _id, ...flatProps } = rec;
46445
+ const resolvedProps = nestedProps !== void 0 ? nestedProps : flatProps;
46446
+ const content = { id: _id ?? idPrefix, pattern: type, props: resolvedProps, priority: 0 };
46447
+ const SCR = getSlotContentRenderer2();
46448
+ return /* @__PURE__ */ jsx(SCR, { content, onDismiss: () => void 0 });
46449
+ }
46450
+ }
46451
+ return null;
46452
+ }
46453
+ var _scr, GameShell;
46363
46454
  var init_GameShell = __esm({
46364
46455
  "components/game/templates/GameShell.tsx"() {
46365
46456
  init_cn();
46366
46457
  init_Box();
46367
46458
  init_Stack();
46368
46459
  init_Typography();
46460
+ _scr = null;
46369
46461
  GameShell = ({
46370
46462
  appName = "Game",
46371
46463
  hud,
@@ -46414,7 +46506,7 @@ var init_GameShell = __esm({
46414
46506
  children: appName
46415
46507
  }
46416
46508
  ),
46417
- hud && /* @__PURE__ */ jsx(Box, { className: "game-shell__hud", children: hud })
46509
+ hud && /* @__PURE__ */ jsx(Box, { className: "game-shell__hud", children: resolveDescriptor(hud, "gs-hud") })
46418
46510
  ]
46419
46511
  }
46420
46512
  ),
@@ -46427,7 +46519,7 @@ var init_GameShell = __esm({
46427
46519
  overflow: "hidden",
46428
46520
  position: "relative"
46429
46521
  },
46430
- children
46522
+ children: resolveDescriptor(children, "gs-children")
46431
46523
  }
46432
46524
  )
46433
46525
  ]
@@ -46437,7 +46529,32 @@ var init_GameShell = __esm({
46437
46529
  GameShell.displayName = "GameShell";
46438
46530
  }
46439
46531
  });
46440
- var GameTemplate;
46532
+ function getSlotContentRenderer3() {
46533
+ if (_scr2) return _scr2;
46534
+ const mod = (init_UISlotRenderer(), __toCommonJS(UISlotRenderer_exports));
46535
+ _scr2 = mod.SlotContentRenderer;
46536
+ return _scr2;
46537
+ }
46538
+ function resolveDescriptor2(value, idPrefix) {
46539
+ if (value === null || value === void 0) return value;
46540
+ if (React93__default.isValidElement(value)) return value;
46541
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
46542
+ if (Array.isArray(value)) {
46543
+ return value.map((item, i) => /* @__PURE__ */ jsx(React93__default.Fragment, { children: resolveDescriptor2(item, `${idPrefix}-${i}`) }, i));
46544
+ }
46545
+ if (typeof value === "object") {
46546
+ const rec = value;
46547
+ if (typeof rec.type === "string" && getComponentForPattern(rec.type) !== null) {
46548
+ const { type, props: nestedProps, _id, ...flatProps } = rec;
46549
+ const resolvedProps = nestedProps !== void 0 ? nestedProps : flatProps;
46550
+ const content = { id: _id ?? idPrefix, pattern: type, props: resolvedProps, priority: 0 };
46551
+ const SCR = getSlotContentRenderer3();
46552
+ return /* @__PURE__ */ jsx(SCR, { content, onDismiss: () => void 0 });
46553
+ }
46554
+ }
46555
+ return null;
46556
+ }
46557
+ var _scr2, GameTemplate;
46441
46558
  var init_GameTemplate = __esm({
46442
46559
  "components/game/templates/GameTemplate.tsx"() {
46443
46560
  init_cn();
@@ -46445,6 +46562,7 @@ var init_GameTemplate = __esm({
46445
46562
  init_Stack();
46446
46563
  init_Typography();
46447
46564
  init_Button();
46565
+ _scr2 = null;
46448
46566
  GameTemplate = ({
46449
46567
  entity,
46450
46568
  title = "Game",
@@ -46512,13 +46630,13 @@ var init_GameTemplate = __esm({
46512
46630
  fullWidth: true,
46513
46631
  className: "flex-1 bg-muted",
46514
46632
  children: [
46515
- children,
46633
+ resolveDescriptor2(children, "gt-children"),
46516
46634
  hud && /* @__PURE__ */ jsx(
46517
46635
  Box,
46518
46636
  {
46519
46637
  position: "absolute",
46520
46638
  className: "top-0 left-0 right-0 pointer-events-none",
46521
- children: /* @__PURE__ */ jsx(Box, { padding: "md", className: "pointer-events-auto w-fit", children: hud })
46639
+ children: /* @__PURE__ */ jsx(Box, { padding: "md", className: "pointer-events-auto w-fit", children: resolveDescriptor2(hud, "gt-hud") })
46522
46640
  }
46523
46641
  )
46524
46642
  ]
@@ -46543,7 +46661,7 @@ var init_GameTemplate = __esm({
46543
46661
  children: /* @__PURE__ */ jsx(Typography, { variant: "h6", children: "Debug Panel" })
46544
46662
  }
46545
46663
  ),
46546
- /* @__PURE__ */ jsx(Box, { padding: "md", children: debugPanel })
46664
+ /* @__PURE__ */ jsx(Box, { padding: "md", children: resolveDescriptor2(debugPanel, "gt-debug") })
46547
46665
  ]
46548
46666
  }
46549
46667
  )
@@ -46713,6 +46831,48 @@ var init_HeroOrganism = __esm({
46713
46831
  _HeroClickInterceptor.displayName = "_HeroClickInterceptor";
46714
46832
  }
46715
46833
  });
46834
+ function HexStrategyBoard({
46835
+ tiles,
46836
+ units,
46837
+ features,
46838
+ assetManifest,
46839
+ assetBaseUrl,
46840
+ scale = 0.45,
46841
+ showMinimap = true,
46842
+ enableCamera = true,
46843
+ tileClickEvent,
46844
+ unitClickEvent,
46845
+ isLoading,
46846
+ error,
46847
+ className
46848
+ }) {
46849
+ return /* @__PURE__ */ jsx("div", { className: cn("hex-strategy-board relative w-full h-full", className), children: /* @__PURE__ */ jsx(
46850
+ IsometricCanvas_default,
46851
+ {
46852
+ tileLayout: "hex",
46853
+ tiles,
46854
+ units,
46855
+ features,
46856
+ assetManifest,
46857
+ assetBaseUrl,
46858
+ scale,
46859
+ showMinimap,
46860
+ enableCamera,
46861
+ tileClickEvent,
46862
+ unitClickEvent,
46863
+ isLoading,
46864
+ error
46865
+ }
46866
+ ) });
46867
+ }
46868
+ var init_HexStrategyBoard = __esm({
46869
+ "components/game/organisms/HexStrategyBoard.tsx"() {
46870
+ "use client";
46871
+ init_cn();
46872
+ init_IsometricCanvas();
46873
+ HexStrategyBoard.displayName = "HexStrategyBoard";
46874
+ }
46875
+ });
46716
46876
  var LandingPageTemplate;
46717
46877
  var init_LandingPageTemplate = __esm({
46718
46878
  "components/marketing/templates/LandingPageTemplate.tsx"() {
@@ -54541,6 +54701,7 @@ var init_component_registry_generated = __esm({
54541
54701
  init_HealthPanel();
54542
54702
  init_HeroOrganism();
54543
54703
  init_HeroSection();
54704
+ init_HexStrategyBoard();
54544
54705
  init_Icon();
54545
54706
  init_InfiniteScrollSentinel();
54546
54707
  init_Input();
@@ -54877,6 +55038,7 @@ var init_component_registry_generated = __esm({
54877
55038
  "HealthPanel": HealthPanel,
54878
55039
  "HeroOrganism": HeroOrganism,
54879
55040
  "HeroSection": HeroSection,
55041
+ "HexStrategyBoard": HexStrategyBoard,
54880
55042
  "Icon": Icon,
54881
55043
  "InfiniteScrollSentinel": InfiniteScrollSentinel,
54882
55044
  "Input": Input,
@@ -55078,8 +55240,8 @@ function getSlotFallback(slot, config) {
55078
55240
  const variant = SLOT_SKELETON_MAP[slot] ?? "text";
55079
55241
  return /* @__PURE__ */ jsx(Skeleton, { variant });
55080
55242
  }
55081
- function getComponentForPattern(patternType) {
55082
- const mapping = getComponentForPattern$1(patternType);
55243
+ function getComponentForPattern3(patternType) {
55244
+ const mapping = getComponentForPattern(patternType);
55083
55245
  if (!mapping) {
55084
55246
  return null;
55085
55247
  }
@@ -55601,7 +55763,7 @@ function isPatternConfig(value) {
55601
55763
  if (value instanceof Date) return false;
55602
55764
  if (typeof value === "function") return false;
55603
55765
  const record = value;
55604
- return "type" in record && typeof record.type === "string" && getComponentForPattern$1(record.type) !== null;
55766
+ return "type" in record && typeof record.type === "string" && getComponentForPattern(record.type) !== null;
55605
55767
  }
55606
55768
  function isPlainConfigObject(value) {
55607
55769
  if (React93__default.isValidElement(value)) return false;
@@ -55709,7 +55871,8 @@ function SlotContentRenderer({
55709
55871
  entityDef = schemaCtx.entities.get(linkedEntity);
55710
55872
  }
55711
55873
  }
55712
- const PatternComponent = getComponentForPattern(content.pattern);
55874
+ const orbitalName = schemaCtx && content.sourceTrait !== void 0 ? schemaCtx.orbitalsByTrait.get(content.sourceTrait) : void 0;
55875
+ const PatternComponent = getComponentForPattern3(content.pattern);
55713
55876
  if (PatternComponent) {
55714
55877
  const childrenConfig = content.props.children;
55715
55878
  const isSingleChild = typeof childrenConfig === "string" || typeof childrenConfig === "object" && childrenConfig !== null && !Array.isArray(childrenConfig) && "type" in childrenConfig;
@@ -55807,6 +55970,7 @@ function SlotContentRenderer({
55807
55970
  "data-orb-entity": content.entity,
55808
55971
  "data-orb-path": myPath,
55809
55972
  "data-orb-pattern": content.pattern,
55973
+ "data-orb-orbital": orbitalName,
55810
55974
  children: renderedChildren !== void 0 ? /* @__PURE__ */ jsx(PatternComponent, { ...finalProps, children: renderedChildren }) : /* @__PURE__ */ jsx(PatternComponent, { ...finalProps })
55811
55975
  }
55812
55976
  );
@@ -55826,6 +55990,7 @@ function SlotContentRenderer({
55826
55990
  "data-orb-entity": content.entity,
55827
55991
  "data-orb-path": patternPath ?? "root",
55828
55992
  "data-orb-pattern": content.pattern,
55993
+ "data-orb-orbital": orbitalName,
55829
55994
  children: content.props.children ?? /* @__PURE__ */ jsxs(Box, { className: "p-4 text-sm text-muted-foreground border border-dashed border-border rounded", children: [
55830
55995
  "Unknown pattern: ",
55831
55996
  content.pattern,
@@ -59468,7 +59633,7 @@ function resolveLambdaBindings(body, params, item, index) {
59468
59633
  return body;
59469
59634
  }
59470
59635
  var _slotContentRenderer2 = null;
59471
- function getSlotContentRenderer2() {
59636
+ function getSlotContentRenderer4() {
59472
59637
  if (_slotContentRenderer2) return _slotContentRenderer2;
59473
59638
  const mod = (init_UISlotRenderer(), __toCommonJS(UISlotRenderer_exports));
59474
59639
  _slotContentRenderer2 = mod.SlotContentRenderer;
@@ -59484,7 +59649,7 @@ function makeLambdaFn(params, lambdaBody, callerKey) {
59484
59649
  if (typeof record.type !== "string") {
59485
59650
  return null;
59486
59651
  }
59487
- const SlotContentRenderer2 = getSlotContentRenderer2();
59652
+ const SlotContentRenderer2 = getSlotContentRenderer4();
59488
59653
  const rawChildProps = {};
59489
59654
  for (const [k, v] of Object.entries(record)) {
59490
59655
  if (k !== "type") rawChildProps[k] = v;
@@ -61844,6 +62009,33 @@ function formatPayloadTooltip(fields) {
61844
62009
  );
61845
62010
  return `{ ${parts.join(", ")} }`;
61846
62011
  }
62012
+
62013
+ // components/avl/derive-edit-focus.ts
62014
+ function deriveEditFocusFromElement(el) {
62015
+ const orbitalEl = el.getAttribute("data-orb-orbital") !== null ? el : el.closest("[data-orb-orbital]");
62016
+ const orbital = orbitalEl?.getAttribute("data-orb-orbital") ?? "";
62017
+ if (!orbital) return null;
62018
+ const path = el.getAttribute("data-orb-path") ?? el.getAttribute("data-pattern-path");
62019
+ const patternType = el.getAttribute("data-orb-pattern") ?? el.getAttribute("data-pattern");
62020
+ const trait = el.getAttribute("data-orb-trait") ?? el.getAttribute("data-source-trait");
62021
+ const focus = {
62022
+ level: "node",
62023
+ orbital,
62024
+ label: patternType ?? trait ?? "element"
62025
+ };
62026
+ if (path !== null) focus.path = path;
62027
+ if (trait !== null) focus.trait = trait;
62028
+ if (patternType !== null) focus.patternType = patternType;
62029
+ const transition = el.getAttribute("data-orb-transition");
62030
+ if (transition !== null) focus.transition = transition;
62031
+ const state = el.getAttribute("data-orb-state");
62032
+ if (state !== null) focus.state = state;
62033
+ const slot = el.getAttribute("data-orb-slot");
62034
+ if (slot !== null) focus.slot = slot;
62035
+ const entity = el.getAttribute("data-orb-entity");
62036
+ if (entity !== null) focus.entity = entity;
62037
+ return focus;
62038
+ }
61847
62039
  function entityNameOf(ref) {
61848
62040
  if (!ref) return void 0;
61849
62041
  if (typeof ref === "string") return ref;
@@ -62129,29 +62321,6 @@ function rectRelativeTo(el, container, zoom) {
62129
62321
  height: (u.bottom - u.top) / z
62130
62322
  };
62131
62323
  }
62132
- function buildFocus(el, orbitalName) {
62133
- if (!orbitalName) return null;
62134
- const path = el.getAttribute("data-orb-path") ?? el.getAttribute("data-pattern-path");
62135
- const patternType = el.getAttribute("data-orb-pattern") ?? el.getAttribute("data-pattern");
62136
- const trait = el.getAttribute("data-orb-trait") ?? el.getAttribute("data-source-trait");
62137
- const focus = {
62138
- level: "node",
62139
- orbital: orbitalName,
62140
- label: patternType ?? trait ?? "element"
62141
- };
62142
- if (path !== null) focus.path = path;
62143
- if (trait !== null) focus.trait = trait;
62144
- if (patternType !== null) focus.patternType = patternType;
62145
- const transition = el.getAttribute("data-orb-transition");
62146
- if (transition !== null) focus.transition = transition;
62147
- const state = el.getAttribute("data-orb-state");
62148
- if (state !== null) focus.state = state;
62149
- const slot = el.getAttribute("data-orb-slot");
62150
- if (slot !== null) focus.slot = slot;
62151
- const entity = el.getAttribute("data-orb-entity");
62152
- if (entity !== null) focus.entity = entity;
62153
- return focus;
62154
- }
62155
62324
  var OrbPreviewNodeInner = (props) => {
62156
62325
  const data = props.data;
62157
62326
  const screenSize = useContext(ScreenSizeContext);
@@ -62220,7 +62389,7 @@ var OrbPreviewNodeInner = (props) => {
62220
62389
  nodeData: data,
62221
62390
  rect: rect ?? void 0
62222
62391
  });
62223
- const focus = buildFocus(patternEl, data.orbitalName);
62392
+ const focus = deriveEditFocusFromElement(patternEl);
62224
62393
  if (focus) eventBus.emit("UI:ELEMENT_SELECTED", { focus: { ...focus } });
62225
62394
  } else {
62226
62395
  setSelectedRect(null);
@@ -65461,4 +65630,4 @@ var AvlClickTarget = ({
65461
65630
  };
65462
65631
  AvlClickTarget.displayName = "AvlClickTarget";
65463
65632
 
65464
- export { AVL_FIELD_TYPE_SHAPES, AVL_OPERATOR_COLORS, AvlApplication, AvlBackwardEdge, AvlBehaviorGlyph, AvlBinding, AvlBindingEdge, AvlBindingRef, AvlClickTarget, AvlClosedCircuit, AvlCosmicZoom, AvlEffect, AvlEmitListen, AvlEntity, AvlEvent, AvlEventWireEdge, AvlExprTree, AvlField, AvlFieldType, AvlGuard, AvlLiteral, AvlOperator, AvlOrbital, AvlOrbitalNode, AvlOrbitalUnit, AvlOrbitalsCosmicZoom, AvlPage, AvlPageEdge, AvlPersistence, AvlSExpr, AvlSlotMap, AvlState, AvlStateMachine, AvlSwimLane, AvlTrait, AvlTraitScene, AvlTransition, AvlTransitionEdge, AvlTransitionLane, AvlTransitionScene, BehaviorComposeNode, BehaviorView, CONNECTION_COLORS, CanvasDndProvider, DOMAIN_COLORS, DetailView, EFFECT_CATEGORY_COLORS, EFFECT_TYPE_TO_CATEGORY, EventFlowEdge, FlowCanvas, MiniStateMachine, ModuleCard, OrbInspector, OrbPreviewNode, STATE_COLORS, SystemNode, ZOOM_BAND_THRESHOLDS, ZoomBandContext, ZoomBreadcrumb, ZoomLegend, arcPath, behaviorsToComposeGraph, computeTraitLayout, computeZoomBand, curveControlPoint, edgePath, getStateRole, gridPositions, orbitalToExpandedGraph, parseApplicationLevel, parseOrbitalLevel, parseTraitLevel, parseTransitionLevel, radialPositions, registryEntryToCanvasEntry, ringPositions, schemaToFlowGraph, schemaToOverviewGraph, useCanvasDraggable, useCanvasDroppable, useZoomBand, zoomProgress };
65633
+ export { AVL_FIELD_TYPE_SHAPES, AVL_OPERATOR_COLORS, AvlApplication, AvlBackwardEdge, AvlBehaviorGlyph, AvlBinding, AvlBindingEdge, AvlBindingRef, AvlClickTarget, AvlClosedCircuit, AvlCosmicZoom, AvlEffect, AvlEmitListen, AvlEntity, AvlEvent, AvlEventWireEdge, AvlExprTree, AvlField, AvlFieldType, AvlGuard, AvlLiteral, AvlOperator, AvlOrbital, AvlOrbitalNode, AvlOrbitalUnit, AvlOrbitalsCosmicZoom, AvlPage, AvlPageEdge, AvlPersistence, AvlSExpr, AvlSlotMap, AvlState, AvlStateMachine, AvlSwimLane, AvlTrait, AvlTraitScene, AvlTransition, AvlTransitionEdge, AvlTransitionLane, AvlTransitionScene, BehaviorComposeNode, BehaviorView, CONNECTION_COLORS, CanvasDndProvider, DOMAIN_COLORS, DetailView, EFFECT_CATEGORY_COLORS, EFFECT_TYPE_TO_CATEGORY, EventFlowEdge, FlowCanvas, MiniStateMachine, ModuleCard, OrbInspector, OrbPreviewNode, STATE_COLORS, SystemNode, ZOOM_BAND_THRESHOLDS, ZoomBandContext, ZoomBreadcrumb, ZoomLegend, arcPath, behaviorsToComposeGraph, computeTraitLayout, computeZoomBand, curveControlPoint, deriveEditFocusFromElement, edgePath, getStateRole, gridPositions, orbitalToExpandedGraph, parseApplicationLevel, parseOrbitalLevel, parseTraitLevel, parseTransitionLevel, radialPositions, registryEntryToCanvasEntry, ringPositions, schemaToFlowGraph, schemaToOverviewGraph, useCanvasDraggable, useCanvasDroppable, useZoomBand, zoomProgress };