@almadar/ui 5.65.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.
@@ -2821,6 +2821,99 @@ function useRenderInterpolation(options = {}) {
2821
2821
  }, []);
2822
2822
  return { onSnapshot, getInterpolated, startLoop };
2823
2823
  }
2824
+ var EMPTY_STATE = {
2825
+ activities: [],
2826
+ subagents: [],
2827
+ coordinatorMessages: [],
2828
+ status: "idle"
2829
+ };
2830
+ function upsertSubagent(subagents, id, patch) {
2831
+ const idx = subagents.findIndex((s) => s.id === id);
2832
+ if (idx === -1) {
2833
+ const next = {
2834
+ id,
2835
+ name: patch.name ?? id,
2836
+ role: patch.role ?? "",
2837
+ status: patch.status ?? "running",
2838
+ task: patch.task ?? "",
2839
+ messages: []
2840
+ };
2841
+ if (patch.orbitalName !== void 0) next.orbitalName = patch.orbitalName;
2842
+ if (patch.parentId !== void 0) next.parentId = patch.parentId;
2843
+ if (patch.durationMs !== void 0) next.durationMs = patch.durationMs;
2844
+ return [...subagents, next];
2845
+ }
2846
+ const merged = { ...subagents[idx] };
2847
+ if (patch.name !== void 0) merged.name = patch.name;
2848
+ if (patch.role !== void 0) merged.role = patch.role;
2849
+ if (patch.orbitalName !== void 0) merged.orbitalName = patch.orbitalName;
2850
+ if (patch.parentId !== void 0) merged.parentId = patch.parentId;
2851
+ if (patch.status !== void 0) merged.status = patch.status;
2852
+ if (patch.task !== void 0) merged.task = patch.task;
2853
+ if (patch.durationMs !== void 0) merged.durationMs = patch.durationMs;
2854
+ const out = subagents.slice();
2855
+ out[idx] = merged;
2856
+ return out;
2857
+ }
2858
+ function appendSubagentMessage(subagents, id, message) {
2859
+ const idx = subagents.findIndex((s) => s.id === id);
2860
+ if (idx === -1) return subagents;
2861
+ const out = subagents.slice();
2862
+ out[idx] = { ...out[idx], messages: [...out[idx].messages, message] };
2863
+ return out;
2864
+ }
2865
+ function setCoordinatorMessage(messages, index, message) {
2866
+ if (index < messages.length) {
2867
+ const out = messages.slice();
2868
+ out[index] = message;
2869
+ return out;
2870
+ }
2871
+ return [...messages, message];
2872
+ }
2873
+ function reduce(state, update) {
2874
+ switch (update.kind) {
2875
+ case "activity":
2876
+ return { ...state, activities: [...state.activities, update.activity] };
2877
+ case "subagent-upsert":
2878
+ return { ...state, subagents: upsertSubagent(state.subagents, update.id, update.subagent) };
2879
+ case "subagent-message":
2880
+ return { ...state, subagents: appendSubagentMessage(state.subagents, update.id, update.message) };
2881
+ case "coordinator-message":
2882
+ return {
2883
+ ...state,
2884
+ coordinatorMessages: setCoordinatorMessage(state.coordinatorMessages, update.index, update.message)
2885
+ };
2886
+ case "status":
2887
+ return { ...state, status: update.status };
2888
+ case "reset":
2889
+ return EMPTY_STATE;
2890
+ }
2891
+ }
2892
+ function traceReducer(state, action) {
2893
+ if (action.type === "apply") return reduce(state, action.update);
2894
+ return action.updates.reduce(reduce, state);
2895
+ }
2896
+ function useAgentTrace() {
2897
+ const [state, dispatch] = react.useReducer(traceReducer, EMPTY_STATE);
2898
+ const apply = react.useCallback((update) => {
2899
+ dispatch({ type: "apply", update });
2900
+ }, []);
2901
+ const applyAll = react.useCallback((updates) => {
2902
+ dispatch({ type: "applyAll", updates });
2903
+ }, []);
2904
+ const reset = react.useCallback(() => {
2905
+ dispatch({ type: "apply", update: { kind: "reset" } });
2906
+ }, []);
2907
+ return {
2908
+ activities: state.activities,
2909
+ subagents: state.subagents,
2910
+ coordinatorMessages: state.coordinatorMessages,
2911
+ status: state.status,
2912
+ apply,
2913
+ applyAll,
2914
+ reset
2915
+ };
2916
+ }
2824
2917
  var API_BASE = typeof process !== "undefined" && process.env?.VITE_API_URL ? process.env.VITE_API_URL : "http://localhost:3000";
2825
2918
  function getUserId() {
2826
2919
  return localStorage.getItem("userId") || "anonymous";
@@ -2912,6 +3005,7 @@ exports.spawnEntity = spawnEntity;
2912
3005
  exports.updateEntity = updateEntity;
2913
3006
  exports.updateSingleton = updateSingleton;
2914
3007
  exports.useAgentChat = useAgentChat;
3008
+ exports.useAgentTrace = useAgentTrace;
2915
3009
  exports.useAuthContext = useAuthContext;
2916
3010
  exports.useCanvasGestures = useCanvasGestures;
2917
3011
  exports.useCompile = useCompile;
@@ -29,4 +29,5 @@ export { useTapReveal, type TapRevealOptions, type TapRevealResult, } from './us
29
29
  export { useDraggable, ALMADAR_DND_MIME, type DraggablePayload, type UseDraggableOptions, type UseDraggableResult, } from './useDraggable';
30
30
  export { useDropZone, type UseDropZoneOptions, type UseDropZoneResult, } from './useDropZone';
31
31
  export { useRenderInterpolation, type Positioned, type RenderInterpolationOptions, type RenderInterpolationHandle, } from './useRenderInterpolation';
32
+ export { useAgentTrace, type UseAgentTraceResult, type AgentTraceState, type TraceStatus, type TraceUpdate, type SubagentUpsert, } from './useAgentTrace';
32
33
  export { useGitHubStatus, useConnectGitHub, useDisconnectGitHub, useGitHubRepos, useGitHubRepo, useGitHubBranches, type GitHubStatus, type GitHubRepo, } from './useGitHub';
@@ -1,4 +1,4 @@
1
- import { createContext, useCallback, useState, useEffect, useMemo, useContext, useRef, useSyncExternalStore } from 'react';
1
+ import { createContext, useCallback, useState, useEffect, useMemo, useContext, useRef, useSyncExternalStore, useReducer } from 'react';
2
2
  import { createLogger } from '@almadar/logger';
3
3
  import { EventBusContext, useTraitScope } from '@almadar/ui/providers';
4
4
  import { useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
@@ -2819,6 +2819,99 @@ function useRenderInterpolation(options = {}) {
2819
2819
  }, []);
2820
2820
  return { onSnapshot, getInterpolated, startLoop };
2821
2821
  }
2822
+ var EMPTY_STATE = {
2823
+ activities: [],
2824
+ subagents: [],
2825
+ coordinatorMessages: [],
2826
+ status: "idle"
2827
+ };
2828
+ function upsertSubagent(subagents, id, patch) {
2829
+ const idx = subagents.findIndex((s) => s.id === id);
2830
+ if (idx === -1) {
2831
+ const next = {
2832
+ id,
2833
+ name: patch.name ?? id,
2834
+ role: patch.role ?? "",
2835
+ status: patch.status ?? "running",
2836
+ task: patch.task ?? "",
2837
+ messages: []
2838
+ };
2839
+ if (patch.orbitalName !== void 0) next.orbitalName = patch.orbitalName;
2840
+ if (patch.parentId !== void 0) next.parentId = patch.parentId;
2841
+ if (patch.durationMs !== void 0) next.durationMs = patch.durationMs;
2842
+ return [...subagents, next];
2843
+ }
2844
+ const merged = { ...subagents[idx] };
2845
+ if (patch.name !== void 0) merged.name = patch.name;
2846
+ if (patch.role !== void 0) merged.role = patch.role;
2847
+ if (patch.orbitalName !== void 0) merged.orbitalName = patch.orbitalName;
2848
+ if (patch.parentId !== void 0) merged.parentId = patch.parentId;
2849
+ if (patch.status !== void 0) merged.status = patch.status;
2850
+ if (patch.task !== void 0) merged.task = patch.task;
2851
+ if (patch.durationMs !== void 0) merged.durationMs = patch.durationMs;
2852
+ const out = subagents.slice();
2853
+ out[idx] = merged;
2854
+ return out;
2855
+ }
2856
+ function appendSubagentMessage(subagents, id, message) {
2857
+ const idx = subagents.findIndex((s) => s.id === id);
2858
+ if (idx === -1) return subagents;
2859
+ const out = subagents.slice();
2860
+ out[idx] = { ...out[idx], messages: [...out[idx].messages, message] };
2861
+ return out;
2862
+ }
2863
+ function setCoordinatorMessage(messages, index, message) {
2864
+ if (index < messages.length) {
2865
+ const out = messages.slice();
2866
+ out[index] = message;
2867
+ return out;
2868
+ }
2869
+ return [...messages, message];
2870
+ }
2871
+ function reduce(state, update) {
2872
+ switch (update.kind) {
2873
+ case "activity":
2874
+ return { ...state, activities: [...state.activities, update.activity] };
2875
+ case "subagent-upsert":
2876
+ return { ...state, subagents: upsertSubagent(state.subagents, update.id, update.subagent) };
2877
+ case "subagent-message":
2878
+ return { ...state, subagents: appendSubagentMessage(state.subagents, update.id, update.message) };
2879
+ case "coordinator-message":
2880
+ return {
2881
+ ...state,
2882
+ coordinatorMessages: setCoordinatorMessage(state.coordinatorMessages, update.index, update.message)
2883
+ };
2884
+ case "status":
2885
+ return { ...state, status: update.status };
2886
+ case "reset":
2887
+ return EMPTY_STATE;
2888
+ }
2889
+ }
2890
+ function traceReducer(state, action) {
2891
+ if (action.type === "apply") return reduce(state, action.update);
2892
+ return action.updates.reduce(reduce, state);
2893
+ }
2894
+ function useAgentTrace() {
2895
+ const [state, dispatch] = useReducer(traceReducer, EMPTY_STATE);
2896
+ const apply = useCallback((update) => {
2897
+ dispatch({ type: "apply", update });
2898
+ }, []);
2899
+ const applyAll = useCallback((updates) => {
2900
+ dispatch({ type: "applyAll", updates });
2901
+ }, []);
2902
+ const reset = useCallback(() => {
2903
+ dispatch({ type: "apply", update: { kind: "reset" } });
2904
+ }, []);
2905
+ return {
2906
+ activities: state.activities,
2907
+ subagents: state.subagents,
2908
+ coordinatorMessages: state.coordinatorMessages,
2909
+ status: state.status,
2910
+ apply,
2911
+ applyAll,
2912
+ reset
2913
+ };
2914
+ }
2822
2915
  var API_BASE = typeof process !== "undefined" && process.env?.VITE_API_URL ? process.env.VITE_API_URL : "http://localhost:3000";
2823
2916
  function getUserId() {
2824
2917
  return localStorage.getItem("userId") || "anonymous";
@@ -2895,4 +2988,4 @@ function useGitHubBranches(owner, repo, enabled = true) {
2895
2988
  });
2896
2989
  }
2897
2990
 
2898
- export { ALMADAR_DND_MIME, DEFAULT_SLOTS, I18nProvider, clearEntities, createTranslate, getAllEntities, getByType, getEntity, getSingleton, parseQueryBinding, removeEntity, spawnEntity, updateEntity, updateSingleton, useAgentChat, useAuthContext, useCanvasGestures, useCompile, useConnectGitHub, useDeepAgentGeneration, useDisconnectGitHub, useDragReorder, useDraggable, useDropZone, useEmitEvent, useEntities, useEntitiesByType, useEntity as useEntityById, useEventBus, useEventListener, useExtensions, useFileEditor, useFileSystem, useGitHubBranches, useGitHubRepo, useGitHubRepos, useGitHubStatus, useInfiniteScroll, useInput, useLongPress, useOrbitalHistory, usePhysics, usePinchZoom, usePlayer, usePreview, usePullToRefresh, useQuerySingleton, useRenderInterpolation, useSingletonEntity, useSwipeGesture, useTapReveal, useTraitListens, useTranslate, useUIEvents, useUISlotManager, useValidation };
2991
+ export { ALMADAR_DND_MIME, DEFAULT_SLOTS, I18nProvider, clearEntities, createTranslate, getAllEntities, getByType, getEntity, getSingleton, parseQueryBinding, removeEntity, spawnEntity, updateEntity, updateSingleton, useAgentChat, useAgentTrace, useAuthContext, useCanvasGestures, useCompile, useConnectGitHub, useDeepAgentGeneration, useDisconnectGitHub, useDragReorder, useDraggable, useDropZone, useEmitEvent, useEntities, useEntitiesByType, useEntity as useEntityById, useEventBus, useEventListener, useExtensions, useFileEditor, useFileSystem, useGitHubBranches, useGitHubRepo, useGitHubRepos, useGitHubStatus, useInfiniteScroll, useInput, useLongPress, useOrbitalHistory, usePhysics, usePinchZoom, usePlayer, usePreview, usePullToRefresh, useQuerySingleton, useRenderInterpolation, useSingletonEntity, useSwipeGesture, useTapReveal, useTraitListens, useTranslate, useUIEvents, useUISlotManager, useValidation };
@@ -0,0 +1,85 @@
1
+ import type { TraceActivity, TraceSubagent, TraceSubagentMessage, TraceChatMessage } from '@almadar/core';
2
+ /** Coarse run status surfaced by the trace UI (drives ChatBar's action button). */
3
+ export type TraceStatus = 'idle' | 'running' | 'paused' | 'complete' | 'error';
4
+ /** The accumulated view-model the trace components render. */
5
+ export interface AgentTraceState {
6
+ activities: TraceActivity[];
7
+ subagents: TraceSubagent[];
8
+ coordinatorMessages: TraceChatMessage[];
9
+ status: TraceStatus;
10
+ }
11
+ /**
12
+ * A pre-mapped update the runner feeds in. Each variant is a view-model delta —
13
+ * no raw SSE/transport shapes. Subagents upsert by `id`; coordinator messages
14
+ * upsert by index (the runner owns ordering); activities append.
15
+ */
16
+ export type TraceUpdate =
17
+ /** Append one already-mapped activity to the page-level stream. */
18
+ {
19
+ kind: 'activity';
20
+ activity: TraceActivity;
21
+ }
22
+ /**
23
+ * Upsert a subagent by `id`. A partial patch merges into the existing record
24
+ * (status/duration/task changes); a full record inserts when `id` is new.
25
+ */
26
+ | {
27
+ kind: 'subagent-upsert';
28
+ id: string;
29
+ subagent: SubagentUpsert;
30
+ }
31
+ /** Append a progress message to a known subagent's running log. */
32
+ | {
33
+ kind: 'subagent-message';
34
+ id: string;
35
+ message: TraceSubagentMessage;
36
+ }
37
+ /**
38
+ * Set a coordinator message at `index`. The runner emits messages in order;
39
+ * a known index replaces (e.g. streaming content grew), a new index appends.
40
+ */
41
+ | {
42
+ kind: 'coordinator-message';
43
+ index: number;
44
+ message: TraceChatMessage;
45
+ }
46
+ /** Set the coarse run status. */
47
+ | {
48
+ kind: 'status';
49
+ status: TraceStatus;
50
+ }
51
+ /** Reset the whole view-model (new turn). */
52
+ | {
53
+ kind: 'reset';
54
+ };
55
+ /**
56
+ * A subagent upsert payload: every field optional except `name`/`role`/`task`
57
+ * are required only when the id is new. Partial patches merge into the existing
58
+ * record; that's why the fields are all optional here and the reducer fills
59
+ * defaults on first insert.
60
+ */
61
+ export interface SubagentUpsert {
62
+ name?: string;
63
+ role?: string;
64
+ orbitalName?: string;
65
+ parentId?: string;
66
+ status?: TraceSubagent['status'];
67
+ task?: string;
68
+ durationMs?: number;
69
+ }
70
+ /** What the hook returns: the state plus an `apply` to feed it updates. */
71
+ export interface UseAgentTraceResult extends AgentTraceState {
72
+ /** Apply one pre-mapped view-model update (from the runner). */
73
+ apply: (update: TraceUpdate) => void;
74
+ /** Apply a batch of updates in order (single render). */
75
+ applyAll: (updates: readonly TraceUpdate[]) => void;
76
+ /** Reset to the empty view-model. */
77
+ reset: () => void;
78
+ }
79
+ /**
80
+ * Accumulate agent-trace view-model from pre-mapped updates.
81
+ *
82
+ * The consumer maps SSE/runner events to `TraceUpdate`s upstream and calls
83
+ * `apply`/`applyAll`; this hook holds nothing but the accumulated render state.
84
+ */
85
+ export declare function useAgentTrace(): UseAgentTraceResult;
@@ -8021,6 +8021,8 @@ var init_DialogueBubble = __esm({
8021
8021
  function ChoiceButton({
8022
8022
  text = "Charge forward into the fray",
8023
8023
  index,
8024
+ assetUrl,
8025
+ icon,
8024
8026
  disabled = false,
8025
8027
  selected = false,
8026
8028
  onClick,
@@ -8053,6 +8055,23 @@ function ChoiceButton({
8053
8055
  ]
8054
8056
  }
8055
8057
  ),
8058
+ assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
8059
+ "img",
8060
+ {
8061
+ src: assetUrl,
8062
+ alt: "",
8063
+ width: 16,
8064
+ height: 16,
8065
+ style: { imageRendering: "pixelated", objectFit: "contain" },
8066
+ className: "flex-shrink-0"
8067
+ }
8068
+ ) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0 text-sm", children: typeof icon === "string" ? (() => {
8069
+ const I = resolveIcon(icon);
8070
+ return I ? /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-4 h-4" }) : null;
8071
+ })() : /* @__PURE__ */ (() => {
8072
+ const I = icon;
8073
+ return /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-4 h-4" });
8074
+ })() }) : null,
8056
8075
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm leading-snug", children: text })
8057
8076
  ]
8058
8077
  }
@@ -8061,6 +8080,7 @@ function ChoiceButton({
8061
8080
  var init_ChoiceButton = __esm({
8062
8081
  "components/game/atoms/ChoiceButton.tsx"() {
8063
8082
  init_cn();
8083
+ init_Icon();
8064
8084
  ChoiceButton.displayName = "ChoiceButton";
8065
8085
  }
8066
8086
  });
@@ -11553,7 +11573,8 @@ function IsometricCanvas({
11553
11573
  const minX = Math.min(...allScreenPos.map((p2) => p2.x));
11554
11574
  const maxX = Math.max(...allScreenPos.map((p2) => p2.x + scaledTileWidth));
11555
11575
  const minY = Math.min(...allScreenPos.map((p2) => p2.y));
11556
- const maxY = Math.max(...allScreenPos.map((p2) => p2.y + scaledTileHeight));
11576
+ const tileBottomExtent = tileLayout === "hex" ? scaledTileWidth : scaledTileHeight;
11577
+ const maxY = Math.max(...allScreenPos.map((p2) => p2.y + tileBottomExtent));
11557
11578
  const worldW = maxX - minX;
11558
11579
  const worldH = maxY - minY;
11559
11580
  const scaleM = Math.min(mW / worldW, mH / worldH) * 0.9;
@@ -11645,7 +11666,7 @@ function IsometricCanvas({
11645
11666
  const drawW = scaledTileWidth;
11646
11667
  const drawH = scaledTileWidth * (img.naturalHeight / img.naturalWidth);
11647
11668
  const drawX = pos.x;
11648
- const drawY = pos.y + scaledTileHeight - drawH;
11669
+ const drawY = tileLayout === "hex" ? pos.y : pos.y + scaledTileHeight - drawH;
11649
11670
  ctx.drawImage(img, drawX, drawY, drawW, drawH);
11650
11671
  }
11651
11672
  } else {
@@ -28397,6 +28418,7 @@ function PowerupSlots({
28397
28418
  /* @__PURE__ */ jsxRuntime.jsx(
28398
28419
  ItemSlot,
28399
28420
  {
28421
+ assetUrl: powerup.assetUrl,
28400
28422
  icon: powerup.icon,
28401
28423
  label: powerup.label,
28402
28424
  rarity: "uncommon",
@@ -28646,13 +28668,26 @@ function HealthPanel({
28646
28668
  )
28647
28669
  }
28648
28670
  ),
28649
- effects && effects.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex items-center gap-1 flex-wrap", children: effects.map((effect, i) => /* @__PURE__ */ jsxRuntime.jsx(
28671
+ effects && effects.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex items-center gap-1 flex-wrap", children: effects.map((effect, i) => /* @__PURE__ */ jsxRuntime.jsxs(
28650
28672
  Badge,
28651
28673
  {
28652
28674
  variant: effectVariantMap[effect.variant ?? "neutral"],
28653
28675
  size: "sm",
28654
- icon: effect.icon,
28655
- children: effect.label
28676
+ icon: effect.assetUrl ? void 0 : effect.icon,
28677
+ children: [
28678
+ effect.assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
28679
+ "img",
28680
+ {
28681
+ src: effect.assetUrl,
28682
+ alt: "",
28683
+ width: 12,
28684
+ height: 12,
28685
+ style: { imageRendering: "pixelated", objectFit: "contain" },
28686
+ className: "flex-shrink-0 inline-block"
28687
+ }
28688
+ ),
28689
+ effect.label
28690
+ ]
28656
28691
  },
28657
28692
  i
28658
28693
  )) })
@@ -28835,15 +28870,28 @@ function TurnPanel({
28835
28870
  activeTeam
28836
28871
  }
28837
28872
  ),
28838
- 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(
28873
+ 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(
28839
28874
  Button,
28840
28875
  {
28841
28876
  variant: "ghost",
28842
28877
  size: "sm",
28843
28878
  disabled: action.disabled,
28844
- leftIcon: action.icon,
28879
+ leftIcon: action.assetUrl ? void 0 : action.icon,
28845
28880
  onClick: () => handleAction(action.event),
28846
- children: action.label
28881
+ children: [
28882
+ action.assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
28883
+ "img",
28884
+ {
28885
+ src: action.assetUrl,
28886
+ alt: "",
28887
+ width: 14,
28888
+ height: 14,
28889
+ style: { imageRendering: "pixelated", objectFit: "contain" },
28890
+ className: "flex-shrink-0"
28891
+ }
28892
+ ),
28893
+ action.label
28894
+ ]
28847
28895
  },
28848
28896
  i
28849
28897
  )) })
@@ -43759,9 +43807,9 @@ var init_GameCanvas3D2 = __esm({
43759
43807
  const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
43760
43808
  const hasAtlas = unitAtlasUrl(unit) !== null;
43761
43809
  const initialFrame = hasAtlas ? resolveUnitFrame(unit.id) : null;
43762
- const modelScale = UNIT_BASE_MODEL_SCALE * unitScale;
43763
- const billboardHeight = UNIT_BASE_BILLBOARD_HEIGHT * unitScale;
43764
- const primitiveRadius = UNIT_BASE_PRIMITIVE_RADIUS * unitScale;
43810
+ const modelScale = UNIT_BASE_MODEL_SCALE * unitScale * gridConfig.cellSize;
43811
+ const billboardHeight = UNIT_BASE_BILLBOARD_HEIGHT * unitScale * gridConfig.cellSize;
43812
+ const primitiveRadius = UNIT_BASE_PRIMITIVE_RADIUS * unitScale * gridConfig.cellSize;
43765
43813
  return /* @__PURE__ */ jsxRuntime.jsxs(
43766
43814
  "group",
43767
43815
  {
@@ -43808,7 +43856,7 @@ var init_GameCanvas3D2 = __esm({
43808
43856
  /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
43809
43857
  ] })
43810
43858
  ] }),
43811
- unit.health !== void 0 && unit.maxHealth !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("group", { position: [0, 1.2, 0], children: [
43859
+ unit.health !== void 0 && unit.maxHealth !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("group", { position: [0, billboardHeight, 0], children: [
43812
43860
  /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [-0.25, 0, 0], children: [
43813
43861
  /* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [0.5, 0.05] }),
43814
43862
  /* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: 3355443 })
@@ -43837,7 +43885,7 @@ var init_GameCanvas3D2 = __esm({
43837
43885
  }
43838
43886
  );
43839
43887
  },
43840
- [selectedUnitId, handleUnitClick, resolveUnitFrame, unitScale]
43888
+ [selectedUnitId, handleUnitClick, resolveUnitFrame, unitScale, gridConfig]
43841
43889
  );
43842
43890
  const DefaultFeatureRenderer = React85.useCallback(
43843
43891
  ({
@@ -43925,7 +43973,7 @@ var init_GameCanvas3D2 = __esm({
43925
43973
  {
43926
43974
  ref: containerRef,
43927
43975
  className: cn("game-canvas-3d relative w-full overflow-hidden", className),
43928
- style: { height: "85vh" },
43976
+ style: { flex: "1 1 0", minHeight: "400px" },
43929
43977
  "data-orientation": orientation,
43930
43978
  "data-camera-mode": cameraMode,
43931
43979
  "data-overlay": overlay,
@@ -44225,13 +44273,39 @@ var init_GameBoard3D = __esm({
44225
44273
  GameBoard3D.displayName = "GameBoard3D";
44226
44274
  }
44227
44275
  });
44228
- var GameShell;
44276
+ function getSlotContentRenderer2() {
44277
+ if (_scr) return _scr;
44278
+ const mod = (init_UISlotRenderer(), __toCommonJS(UISlotRenderer_exports));
44279
+ _scr = mod.SlotContentRenderer;
44280
+ return _scr;
44281
+ }
44282
+ function resolveDescriptor(value, idPrefix) {
44283
+ if (value === null || value === void 0) return value;
44284
+ if (React85__namespace.default.isValidElement(value)) return value;
44285
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
44286
+ if (Array.isArray(value)) {
44287
+ return value.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(React85__namespace.default.Fragment, { children: resolveDescriptor(item, `${idPrefix}-${i}`) }, i));
44288
+ }
44289
+ if (typeof value === "object") {
44290
+ const rec = value;
44291
+ if (typeof rec.type === "string" && patterns.getComponentForPattern(rec.type) !== null) {
44292
+ const { type, props: nestedProps, _id, ...flatProps } = rec;
44293
+ const resolvedProps = nestedProps !== void 0 ? nestedProps : flatProps;
44294
+ const content = { id: _id ?? idPrefix, pattern: type, props: resolvedProps, priority: 0 };
44295
+ const SCR = getSlotContentRenderer2();
44296
+ return /* @__PURE__ */ jsxRuntime.jsx(SCR, { content, onDismiss: () => void 0 });
44297
+ }
44298
+ }
44299
+ return null;
44300
+ }
44301
+ var _scr, GameShell;
44229
44302
  var init_GameShell = __esm({
44230
44303
  "components/game/templates/GameShell.tsx"() {
44231
44304
  init_cn();
44232
44305
  init_Box();
44233
44306
  init_Stack();
44234
44307
  init_Typography();
44308
+ _scr = null;
44235
44309
  GameShell = ({
44236
44310
  appName = "Game",
44237
44311
  hud,
@@ -44280,7 +44354,7 @@ var init_GameShell = __esm({
44280
44354
  children: appName
44281
44355
  }
44282
44356
  ),
44283
- hud && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "game-shell__hud", children: hud })
44357
+ hud && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "game-shell__hud", children: resolveDescriptor(hud, "gs-hud") })
44284
44358
  ]
44285
44359
  }
44286
44360
  ),
@@ -44293,7 +44367,7 @@ var init_GameShell = __esm({
44293
44367
  overflow: "hidden",
44294
44368
  position: "relative"
44295
44369
  },
44296
- children
44370
+ children: resolveDescriptor(children, "gs-children")
44297
44371
  }
44298
44372
  )
44299
44373
  ]
@@ -44303,7 +44377,32 @@ var init_GameShell = __esm({
44303
44377
  GameShell.displayName = "GameShell";
44304
44378
  }
44305
44379
  });
44306
- var GameTemplate;
44380
+ function getSlotContentRenderer3() {
44381
+ if (_scr2) return _scr2;
44382
+ const mod = (init_UISlotRenderer(), __toCommonJS(UISlotRenderer_exports));
44383
+ _scr2 = mod.SlotContentRenderer;
44384
+ return _scr2;
44385
+ }
44386
+ function resolveDescriptor2(value, idPrefix) {
44387
+ if (value === null || value === void 0) return value;
44388
+ if (React85__namespace.default.isValidElement(value)) return value;
44389
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
44390
+ if (Array.isArray(value)) {
44391
+ return value.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(React85__namespace.default.Fragment, { children: resolveDescriptor2(item, `${idPrefix}-${i}`) }, i));
44392
+ }
44393
+ if (typeof value === "object") {
44394
+ const rec = value;
44395
+ if (typeof rec.type === "string" && patterns.getComponentForPattern(rec.type) !== null) {
44396
+ const { type, props: nestedProps, _id, ...flatProps } = rec;
44397
+ const resolvedProps = nestedProps !== void 0 ? nestedProps : flatProps;
44398
+ const content = { id: _id ?? idPrefix, pattern: type, props: resolvedProps, priority: 0 };
44399
+ const SCR = getSlotContentRenderer3();
44400
+ return /* @__PURE__ */ jsxRuntime.jsx(SCR, { content, onDismiss: () => void 0 });
44401
+ }
44402
+ }
44403
+ return null;
44404
+ }
44405
+ var _scr2, GameTemplate;
44307
44406
  var init_GameTemplate = __esm({
44308
44407
  "components/game/templates/GameTemplate.tsx"() {
44309
44408
  init_cn();
@@ -44311,6 +44410,7 @@ var init_GameTemplate = __esm({
44311
44410
  init_Stack();
44312
44411
  init_Typography();
44313
44412
  init_Button();
44413
+ _scr2 = null;
44314
44414
  GameTemplate = ({
44315
44415
  entity,
44316
44416
  title = "Game",
@@ -44378,13 +44478,13 @@ var init_GameTemplate = __esm({
44378
44478
  fullWidth: true,
44379
44479
  className: "flex-1 bg-muted",
44380
44480
  children: [
44381
- children,
44481
+ resolveDescriptor2(children, "gt-children"),
44382
44482
  hud && /* @__PURE__ */ jsxRuntime.jsx(
44383
44483
  Box,
44384
44484
  {
44385
44485
  position: "absolute",
44386
44486
  className: "top-0 left-0 right-0 pointer-events-none",
44387
- children: /* @__PURE__ */ jsxRuntime.jsx(Box, { padding: "md", className: "pointer-events-auto w-fit", children: hud })
44487
+ children: /* @__PURE__ */ jsxRuntime.jsx(Box, { padding: "md", className: "pointer-events-auto w-fit", children: resolveDescriptor2(hud, "gt-hud") })
44388
44488
  }
44389
44489
  )
44390
44490
  ]
@@ -44409,7 +44509,7 @@ var init_GameTemplate = __esm({
44409
44509
  children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h6", children: "Debug Panel" })
44410
44510
  }
44411
44511
  ),
44412
- /* @__PURE__ */ jsxRuntime.jsx(Box, { padding: "md", children: debugPanel })
44512
+ /* @__PURE__ */ jsxRuntime.jsx(Box, { padding: "md", children: resolveDescriptor2(debugPanel, "gt-debug") })
44413
44513
  ]
44414
44514
  }
44415
44515
  )
@@ -52969,7 +53069,7 @@ function getSlotFallback(slot, config) {
52969
53069
  const variant = SLOT_SKELETON_MAP[slot] ?? "text";
52970
53070
  return /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { variant });
52971
53071
  }
52972
- function getComponentForPattern(patternType) {
53072
+ function getComponentForPattern3(patternType) {
52973
53073
  const mapping = patterns.getComponentForPattern(patternType);
52974
53074
  if (!mapping) {
52975
53075
  return null;
@@ -53600,7 +53700,8 @@ function SlotContentRenderer({
53600
53700
  entityDef = schemaCtx.entities.get(linkedEntity);
53601
53701
  }
53602
53702
  }
53603
- const PatternComponent = getComponentForPattern(content.pattern);
53703
+ const orbitalName = schemaCtx && content.sourceTrait !== void 0 ? schemaCtx.orbitalsByTrait.get(content.sourceTrait) : void 0;
53704
+ const PatternComponent = getComponentForPattern3(content.pattern);
53604
53705
  if (PatternComponent) {
53605
53706
  const childrenConfig = content.props.children;
53606
53707
  const isSingleChild = typeof childrenConfig === "string" || typeof childrenConfig === "object" && childrenConfig !== null && !Array.isArray(childrenConfig) && "type" in childrenConfig;
@@ -53698,6 +53799,7 @@ function SlotContentRenderer({
53698
53799
  "data-orb-entity": content.entity,
53699
53800
  "data-orb-path": myPath,
53700
53801
  "data-orb-pattern": content.pattern,
53802
+ "data-orb-orbital": orbitalName,
53701
53803
  children: renderedChildren !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(PatternComponent, { ...finalProps, children: renderedChildren }) : /* @__PURE__ */ jsxRuntime.jsx(PatternComponent, { ...finalProps })
53702
53804
  }
53703
53805
  );
@@ -53717,6 +53819,7 @@ function SlotContentRenderer({
53717
53819
  "data-orb-entity": content.entity,
53718
53820
  "data-orb-path": patternPath ?? "root",
53719
53821
  "data-orb-pattern": content.pattern,
53822
+ "data-orb-orbital": orbitalName,
53720
53823
  children: content.props.children ?? /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "p-4 text-sm text-muted-foreground border border-dashed border-border rounded", children: [
53721
53824
  "Unknown pattern: ",
53722
53825
  content.pattern,