@almadar/ui 5.54.0 → 5.56.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.
@@ -10,7 +10,7 @@ import { Loader2, X, Lightbulb, CheckCircle, List, Printer, ChevronRight, Chevro
10
10
  import { createPortal } from 'react-dom';
11
11
  import { useTranslate, useEventBus as useEventBus$1 } from '@almadar/ui/hooks';
12
12
  import { useUISlots, UISlotProvider, useTheme } from '@almadar/ui/context';
13
- import { evaluate, createMinimalContext } from '@almadar/evaluator';
13
+ import { evaluateGuard, evaluate, createMinimalContext } from '@almadar/evaluator';
14
14
  import { Link, Outlet, useLocation } from 'react-router-dom';
15
15
  import ELK from 'elkjs/lib/elk.bundled.js';
16
16
  import SyntaxHighlighter from 'react-syntax-highlighter/dist/esm/prism-light.js';
@@ -48,7 +48,7 @@ import { GLTFLoader as GLTFLoader$1 } from 'three/examples/jsm/loaders/GLTFLoade
48
48
  import { Canvas, useLoader, useFrame, useThree } from '@react-three/fiber';
49
49
  import { Billboard, Grid as Grid$1, OrbitControls } from '@react-three/drei';
50
50
  import { getPatternDefinition, getComponentForPattern as getComponentForPattern$1 } from '@almadar/patterns';
51
- import { StateMachineManager, collectDeclaredConfigDefaults, createServerEffectHandlers, EffectExecutor, interpolateValue, createContextFromBindings, InMemoryPersistence } from '@almadar/runtime';
51
+ import { StateMachineManager, collectDeclaredConfigDefaults, createServerEffectHandlers, EffectExecutor, createContextFromBindings, InMemoryPersistence } from '@almadar/runtime';
52
52
  import { OrbitalServerRuntime } from '@almadar/runtime/OrbitalServerRuntime';
53
53
 
54
54
  var __defProp = Object.defineProperty;
@@ -8421,6 +8421,16 @@ var init_Skeleton = __esm({
8421
8421
  Skeleton.displayName = "Skeleton";
8422
8422
  }
8423
8423
  });
8424
+ function isKnownPattern(type) {
8425
+ return type in componentMapping;
8426
+ }
8427
+ var componentMapping;
8428
+ var init_pattern_resolver = __esm({
8429
+ "renderer/pattern-resolver.ts"() {
8430
+ createLogger("almadar:ui:pattern-resolver");
8431
+ componentMapping = {};
8432
+ }
8433
+ });
8424
8434
 
8425
8435
  // renderer/slot-definitions.ts
8426
8436
  function isPortalSlot(slot) {
@@ -8529,6 +8539,7 @@ var init_navigation = __esm({
8529
8539
  // renderer/index.ts
8530
8540
  var init_renderer = __esm({
8531
8541
  "renderer/index.ts"() {
8542
+ init_pattern_resolver();
8532
8543
  init_slot_definitions();
8533
8544
  }
8534
8545
  });
@@ -10232,7 +10243,7 @@ function recordTransition(trace) {
10232
10243
  ...trace,
10233
10244
  id: `t-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
10234
10245
  };
10235
- log4.debug("transition:recorded", { trait: trace.traitName, from: trace.from, to: trace.to, event: trace.event, effectCount: trace.effects.length });
10246
+ log5.debug("transition:recorded", { trait: trace.traitName, from: trace.from, to: trace.to, event: trace.event, effectCount: trace.effects.length });
10236
10247
  getState().transitions.push(entry);
10237
10248
  if (getState().transitions.length > MAX_TRANSITIONS) {
10238
10249
  getState().transitions.shift();
@@ -10331,7 +10342,7 @@ function getTraitSnapshots() {
10331
10342
  try {
10332
10343
  snapshots.push(getter());
10333
10344
  } catch (err) {
10334
- log4.error("traitSnapshot getter failed", { trait: traitName, err: String(err) });
10345
+ log5.error("traitSnapshot getter failed", { trait: traitName, err: String(err) });
10335
10346
  }
10336
10347
  }
10337
10348
  return snapshots;
@@ -10430,10 +10441,10 @@ function updateAssetStatus(url, status) {
10430
10441
  window.__orbitalVerification.assetStatus[url] = status;
10431
10442
  }
10432
10443
  }
10433
- var log4, MAX_TRANSITIONS;
10444
+ var log5, MAX_TRANSITIONS;
10434
10445
  var init_verificationRegistry = __esm({
10435
10446
  "lib/verificationRegistry.ts"() {
10436
- log4 = createLogger("almadar:bridge");
10447
+ log5 = createLogger("almadar:bridge");
10437
10448
  MAX_TRANSITIONS = 500;
10438
10449
  exposeOnWindow();
10439
10450
  }
@@ -10784,6 +10795,8 @@ function IsometricCanvas({
10784
10795
  showMinimap = true,
10785
10796
  enableCamera = true,
10786
10797
  unitScale = 1,
10798
+ spriteHeightRatio = 1.5,
10799
+ spriteMaxWidthRatio = 0.6,
10787
10800
  // Asset resolution
10788
10801
  getTerrainSprite,
10789
10802
  getFeatureSprite,
@@ -11174,8 +11187,8 @@ function IsometricCanvas({
11174
11187
  const breatheOffset = 0.8 * scale * (1 + Math.sin(animTime * 2e-3 + (unit.position.x * 3.7 + unit.position.y * 5.3)));
11175
11188
  const unitSpriteUrl = resolveUnitSpriteUrl(unit);
11176
11189
  const img = unitSpriteUrl ? getImage(unitSpriteUrl) : null;
11177
- const unitDrawH = scaledFloorHeight * 1.5 * unitScale;
11178
- const maxUnitW = scaledTileWidth * 0.6 * unitScale;
11190
+ const unitDrawH = scaledFloorHeight * spriteHeightRatio * unitScale;
11191
+ const maxUnitW = scaledTileWidth * spriteMaxWidthRatio * unitScale;
11179
11192
  const ar = img ? img.naturalWidth / img.naturalHeight : 0.5;
11180
11193
  let drawH = unitDrawH;
11181
11194
  let drawW = unitDrawH * ar;
@@ -11559,6 +11572,8 @@ function BattleBoard({
11559
11572
  assetManifest: propAssetManifest,
11560
11573
  scale = 0.45,
11561
11574
  unitScale = 1,
11575
+ spriteHeightRatio = 1.5,
11576
+ spriteMaxWidthRatio = 0.6,
11562
11577
  header,
11563
11578
  sidebar,
11564
11579
  actions,
@@ -11849,7 +11864,9 @@ function BattleBoard({
11849
11864
  hasActiveEffects: hasActiveEffects2,
11850
11865
  effectSpriteUrls,
11851
11866
  resolveUnitFrame,
11852
- unitScale
11867
+ unitScale,
11868
+ spriteHeightRatio,
11869
+ spriteMaxWidthRatio
11853
11870
  }
11854
11871
  ),
11855
11872
  overlay && overlay(ctx)
@@ -12727,7 +12744,7 @@ var init_avl_elk_layout = __esm({
12727
12744
  elk = new ELK();
12728
12745
  }
12729
12746
  });
12730
- var log5, SWIM_GUTTER, CENTER_W, BehaviorView;
12747
+ var log6, SWIM_GUTTER, CENTER_W, BehaviorView;
12731
12748
  var init_BehaviorView = __esm({
12732
12749
  "components/avl/molecules/BehaviorView.tsx"() {
12733
12750
  "use client";
@@ -12736,7 +12753,7 @@ var init_BehaviorView = __esm({
12736
12753
  init_AvlSwimLane();
12737
12754
  init_types();
12738
12755
  init_avl_elk_layout();
12739
- log5 = createLogger("almadar:ui:avl:behavior-view");
12756
+ log6 = createLogger("almadar:ui:avl:behavior-view");
12740
12757
  SWIM_GUTTER = 120;
12741
12758
  CENTER_W = 360;
12742
12759
  BehaviorView = ({ data }) => {
@@ -12747,7 +12764,7 @@ var init_BehaviorView = __esm({
12747
12764
  const dataKey = useMemo(() => JSON.stringify(traitData), [traitData]);
12748
12765
  useEffect(() => {
12749
12766
  if (!traitData) return;
12750
- computeTraitLayout(traitData).then(setLayout).catch((err) => log5.error("compute-trait-layout-failed", { error: err instanceof Error ? err : String(err) }));
12767
+ computeTraitLayout(traitData).then(setLayout).catch((err) => log6.error("compute-trait-layout-failed", { error: err instanceof Error ? err : String(err) }));
12751
12768
  }, [dataKey]);
12752
12769
  if (!traitData) {
12753
12770
  return /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-[var(--color-border)] bg-[var(--color-card)] p-4 text-center text-[var(--color-muted-foreground)] text-sm", children: t("avl.noTraitData") });
@@ -13158,7 +13175,7 @@ function generateDiff(oldVal, newVal) {
13158
13175
  }
13159
13176
  return diff;
13160
13177
  }
13161
- var orbStyleOverrides, orbStyle, loloStyleOverrides, loloStyle, log6, CODE_LANGUAGES, CODE_LANGUAGE_SET, DIFF_STYLES, LINE_PROPS_FN, HIDDEN_LINE_NUMBERS, CodeBlock;
13178
+ var orbStyleOverrides, orbStyle, loloStyleOverrides, loloStyle, log7, CODE_LANGUAGES, CODE_LANGUAGE_SET, DIFF_STYLES, LINE_PROPS_FN, HIDDEN_LINE_NUMBERS, CodeBlock;
13162
13179
  var init_CodeBlock = __esm({
13163
13180
  "components/core/molecules/markdown/CodeBlock.tsx"() {
13164
13181
  init_cn();
@@ -13241,7 +13258,7 @@ var init_CodeBlock = __esm({
13241
13258
  "lolo-op-async": { color: ORB_COLORS.dark.async }
13242
13259
  };
13243
13260
  loloStyle = { ...dark, ...loloStyleOverrides };
13244
- log6 = createLogger("almadar:ui:markdown-code");
13261
+ log7 = createLogger("almadar:ui:markdown-code");
13245
13262
  CODE_LANGUAGES = [
13246
13263
  "text",
13247
13264
  "json",
@@ -13505,7 +13522,7 @@ var init_CodeBlock = __esm({
13505
13522
  eventBus.emit("UI:COPY_CODE", { language: activeLanguage, success: true });
13506
13523
  setTimeout(() => setCopied(false), 2e3);
13507
13524
  } catch (err) {
13508
- log6.error("Failed to copy code", { error: err instanceof Error ? err : String(err) });
13525
+ log7.error("Failed to copy code", { error: err instanceof Error ? err : String(err) });
13509
13526
  eventBus.emit("UI:COPY_CODE", { language: activeLanguage, success: false });
13510
13527
  }
13511
13528
  };
@@ -17386,14 +17403,14 @@ function useSafeEventBus2() {
17386
17403
  } };
17387
17404
  }
17388
17405
  }
17389
- var log7, lookStyles4, ButtonGroup;
17406
+ var log8, lookStyles4, ButtonGroup;
17390
17407
  var init_ButtonGroup = __esm({
17391
17408
  "components/core/molecules/ButtonGroup.tsx"() {
17392
17409
  "use client";
17393
17410
  init_cn();
17394
17411
  init_atoms2();
17395
17412
  init_useEventBus();
17396
- log7 = createLogger("almadar:ui:button-group");
17413
+ log8 = createLogger("almadar:ui:button-group");
17397
17414
  lookStyles4 = {
17398
17415
  "right-aligned-buttons": "",
17399
17416
  "floating-bar": "fixed bottom-section left-1/2 -translate-x-1/2 shadow-elevation-toast bg-card p-card-sm rounded-container",
@@ -17474,7 +17491,7 @@ var init_ButtonGroup = __esm({
17474
17491
  {
17475
17492
  variant: "ghost",
17476
17493
  onClick: () => {
17477
- log7.debug("Filter clicked", { field: filter.field });
17494
+ log8.debug("Filter clicked", { field: filter.field });
17478
17495
  },
17479
17496
  children: filter.label
17480
17497
  },
@@ -25136,9 +25153,9 @@ function debug(...args) {
25136
25153
  const [first, ...rest] = args;
25137
25154
  const message = typeof first === "string" ? first : "<debug>";
25138
25155
  if (rest.length === 0 && typeof first === "string") {
25139
- log8.debug(message);
25156
+ log9.debug(message);
25140
25157
  } else {
25141
- log8.debug(message, { args: rest.length > 0 ? formatArgs(rest) : formatArgs([first]) });
25158
+ log9.debug(message, { args: rest.length > 0 ? formatArgs(rest) : formatArgs([first]) });
25142
25159
  }
25143
25160
  }
25144
25161
  function debugGroup(label) {
@@ -25166,11 +25183,11 @@ function toLogMetaValue(v) {
25166
25183
  }
25167
25184
  return String(v);
25168
25185
  }
25169
- var NAMESPACE, log8;
25186
+ var NAMESPACE, log9;
25170
25187
  var init_debug = __esm({
25171
25188
  "lib/debug.ts"() {
25172
25189
  NAMESPACE = "almadar:ui:debug";
25173
- log8 = createLogger(NAMESPACE);
25190
+ log9 = createLogger(NAMESPACE);
25174
25191
  createLogger("almadar:ui:debug:input");
25175
25192
  createLogger("almadar:ui:debug:collision");
25176
25193
  createLogger("almadar:ui:debug:physics");
@@ -41461,11 +41478,11 @@ var init_Canvas3DErrorBoundary = __esm({
41461
41478
  "components/game/molecules/three/components/Canvas3DErrorBoundary.css"() {
41462
41479
  }
41463
41480
  });
41464
- var log9, Canvas3DErrorBoundary;
41481
+ var log10, Canvas3DErrorBoundary;
41465
41482
  var init_Canvas3DErrorBoundary2 = __esm({
41466
41483
  "components/game/molecules/three/components/Canvas3DErrorBoundary.tsx"() {
41467
41484
  init_Canvas3DErrorBoundary();
41468
- log9 = createLogger("almadar:ui:game:canvas3d:error-boundary");
41485
+ log10 = createLogger("almadar:ui:game:canvas3d:error-boundary");
41469
41486
  Canvas3DErrorBoundary = class extends Component {
41470
41487
  constructor(props) {
41471
41488
  super(props);
@@ -41493,8 +41510,8 @@ var init_Canvas3DErrorBoundary2 = __esm({
41493
41510
  componentDidCatch(error, errorInfo) {
41494
41511
  this.setState({ errorInfo });
41495
41512
  this.props.onError?.(error, errorInfo);
41496
- log9.error("Error caught", { error });
41497
- log9.error("Component stack", { componentStack: errorInfo.componentStack ?? "<none>" });
41513
+ log10.error("Error caught", { error });
41514
+ log10.error("Component stack", { componentStack: errorInfo.componentStack ?? "<none>" });
41498
41515
  }
41499
41516
  render() {
41500
41517
  if (this.state.hasError) {
@@ -41557,7 +41574,7 @@ function useGLTFModel(url, resourceBasePath) {
41557
41574
  setState({ model: null, isLoading: false, error: null });
41558
41575
  return;
41559
41576
  }
41560
- log10.debug("Loading", { url });
41577
+ log11.debug("Loading", { url });
41561
41578
  setState((prev) => ({ ...prev, isLoading: true, error: null }));
41562
41579
  const assetRoot = resourceBasePath || detectAssetRoot2(url);
41563
41580
  const loader = new GLTFLoader$1();
@@ -41565,7 +41582,7 @@ function useGLTFModel(url, resourceBasePath) {
41565
41582
  loader.load(
41566
41583
  url,
41567
41584
  (gltf) => {
41568
- log10.debug("Loaded", { url });
41585
+ log11.debug("Loaded", { url });
41569
41586
  setState({
41570
41587
  model: gltf.scene,
41571
41588
  isLoading: false,
@@ -41574,7 +41591,7 @@ function useGLTFModel(url, resourceBasePath) {
41574
41591
  },
41575
41592
  void 0,
41576
41593
  (err) => {
41577
- log10.warn("Failed", { url, error: err instanceof Error ? err : String(err) });
41594
+ log11.warn("Failed", { url, error: err instanceof Error ? err : String(err) });
41578
41595
  setState({
41579
41596
  model: null,
41580
41597
  isLoading: false,
@@ -41690,11 +41707,11 @@ function ModelLoader({
41690
41707
  }
41691
41708
  );
41692
41709
  }
41693
- var log10;
41710
+ var log11;
41694
41711
  var init_ModelLoader = __esm({
41695
41712
  "components/game/molecules/three/components/ModelLoader.tsx"() {
41696
41713
  "use client";
41697
- log10 = createLogger("almadar:ui:game:model-loader");
41714
+ log11 = createLogger("almadar:ui:game:model-loader");
41698
41715
  }
41699
41716
  });
41700
41717
 
@@ -41820,6 +41837,8 @@ var init_GameCanvas3D2 = __esm({
41820
41837
  attackTargets = [],
41821
41838
  selectedTileIds = [],
41822
41839
  selectedUnitId = null,
41840
+ unitScale = 1,
41841
+ scale = 0.45,
41823
41842
  children
41824
41843
  }, ref) => {
41825
41844
  const containerRef = useRef(null);
@@ -42036,12 +42055,18 @@ var init_GameCanvas3D2 = __esm({
42036
42055
  },
42037
42056
  [selectedTileIds, hoveredTile, validMoves, attackTargets, handleTileClick, handleTileHover]
42038
42057
  );
42058
+ const UNIT_BASE_MODEL_SCALE = 0.5;
42059
+ const UNIT_BASE_BILLBOARD_HEIGHT = 1.2;
42060
+ const UNIT_BASE_PRIMITIVE_RADIUS = 0.3;
42039
42061
  const DefaultUnitRenderer = useCallback(
42040
42062
  ({ unit, position }) => {
42041
42063
  const isSelected = selectedUnitId === unit.id;
42042
42064
  const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
42043
42065
  const hasAtlas = unitAtlasUrl(unit) !== null;
42044
42066
  const initialFrame = hasAtlas ? resolveUnitFrame(unit.id) : null;
42067
+ const modelScale = UNIT_BASE_MODEL_SCALE * unitScale;
42068
+ const billboardHeight = UNIT_BASE_BILLBOARD_HEIGHT * unitScale;
42069
+ const primitiveRadius = UNIT_BASE_PRIMITIVE_RADIUS * unitScale;
42045
42070
  return /* @__PURE__ */ jsxs(
42046
42071
  "group",
42047
42072
  {
@@ -42059,7 +42084,8 @@ var init_GameCanvas3D2 = __esm({
42059
42084
  UnitSpriteBillboard,
42060
42085
  {
42061
42086
  sheetUrl: initialFrame.sheetUrl,
42062
- resolveFrame: () => resolveUnitFrame(unit.id)
42087
+ resolveFrame: () => resolveUnitFrame(unit.id),
42088
+ height: billboardHeight
42063
42089
  }
42064
42090
  ) })
42065
42091
  ) : unit.modelUrl ? (
@@ -42068,22 +42094,22 @@ var init_GameCanvas3D2 = __esm({
42068
42094
  ModelLoader,
42069
42095
  {
42070
42096
  url: unit.modelUrl,
42071
- scale: 0.5,
42097
+ scale: modelScale,
42072
42098
  fallbackGeometry: "box",
42073
42099
  castShadow: true
42074
42100
  }
42075
42101
  )
42076
42102
  ) : /* @__PURE__ */ jsxs(Fragment, { children: [
42077
- /* @__PURE__ */ jsxs("mesh", { position: [0, 0.3, 0], children: [
42078
- /* @__PURE__ */ jsx("cylinderGeometry", { args: [0.3, 0.3, 0.1, 8] }),
42103
+ /* @__PURE__ */ jsxs("mesh", { position: [0, primitiveRadius, 0], children: [
42104
+ /* @__PURE__ */ jsx("cylinderGeometry", { args: [primitiveRadius, primitiveRadius, primitiveRadius * 0.33, 8] }),
42079
42105
  /* @__PURE__ */ jsx("meshStandardMaterial", { color })
42080
42106
  ] }),
42081
- /* @__PURE__ */ jsxs("mesh", { position: [0, 0.6, 0], children: [
42082
- /* @__PURE__ */ jsx("capsuleGeometry", { args: [0.2, 0.4, 4, 8] }),
42107
+ /* @__PURE__ */ jsxs("mesh", { position: [0, primitiveRadius * 2, 0], children: [
42108
+ /* @__PURE__ */ jsx("capsuleGeometry", { args: [primitiveRadius * 0.67, primitiveRadius * 1.33, 4, 8] }),
42083
42109
  /* @__PURE__ */ jsx("meshStandardMaterial", { color })
42084
42110
  ] }),
42085
- /* @__PURE__ */ jsxs("mesh", { position: [0, 0.9, 0], children: [
42086
- /* @__PURE__ */ jsx("sphereGeometry", { args: [0.12, 8, 8] }),
42111
+ /* @__PURE__ */ jsxs("mesh", { position: [0, primitiveRadius * 3, 0], children: [
42112
+ /* @__PURE__ */ jsx("sphereGeometry", { args: [primitiveRadius * 0.4, 8, 8] }),
42087
42113
  /* @__PURE__ */ jsx("meshStandardMaterial", { color })
42088
42114
  ] })
42089
42115
  ] }),
@@ -42116,7 +42142,7 @@ var init_GameCanvas3D2 = __esm({
42116
42142
  }
42117
42143
  );
42118
42144
  },
42119
- [selectedUnitId, handleUnitClick, resolveUnitFrame]
42145
+ [selectedUnitId, handleUnitClick, resolveUnitFrame, unitScale]
42120
42146
  );
42121
42147
  const DefaultFeatureRenderer = useCallback(
42122
42148
  ({
@@ -42259,33 +42285,35 @@ var init_GameCanvas3D2 = __esm({
42259
42285
  fadeStrength: 1
42260
42286
  }
42261
42287
  ),
42262
- tiles.map((tile, index) => {
42263
- const position = gridToWorld(
42264
- tile.x,
42265
- tile.z ?? tile.y ?? 0,
42266
- tile.elevation ?? 0
42267
- );
42268
- const Renderer = CustomTileRenderer || DefaultTileRenderer;
42269
- return /* @__PURE__ */ jsx(Renderer, { tile, position }, tile.id ?? `tile-${index}`);
42270
- }),
42271
- features.map((feature, index) => {
42272
- const position = gridToWorld(
42273
- feature.x,
42274
- feature.z ?? feature.y ?? 0,
42275
- (feature.elevation ?? 0) + 0.5
42276
- );
42277
- const Renderer = CustomFeatureRenderer || DefaultFeatureRenderer;
42278
- return /* @__PURE__ */ jsx(Renderer, { feature, position }, feature.id ?? `feature-${index}`);
42279
- }),
42280
- units.map((unit) => {
42281
- const position = gridToWorld(
42282
- unit.x ?? 0,
42283
- unit.z ?? unit.y ?? 0,
42284
- (unit.elevation ?? 0) + 0.5
42285
- );
42286
- const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
42287
- return /* @__PURE__ */ jsx(Renderer, { unit, position }, unit.id);
42288
- }),
42288
+ /* @__PURE__ */ jsxs("group", { scale, children: [
42289
+ tiles.map((tile, index) => {
42290
+ const position = gridToWorld(
42291
+ tile.x,
42292
+ tile.z ?? tile.y ?? 0,
42293
+ tile.elevation ?? 0
42294
+ );
42295
+ const Renderer = CustomTileRenderer || DefaultTileRenderer;
42296
+ return /* @__PURE__ */ jsx(Renderer, { tile, position }, tile.id ?? `tile-${index}`);
42297
+ }),
42298
+ features.map((feature, index) => {
42299
+ const position = gridToWorld(
42300
+ feature.x,
42301
+ feature.z ?? feature.y ?? 0,
42302
+ (feature.elevation ?? 0) + 0.5
42303
+ );
42304
+ const Renderer = CustomFeatureRenderer || DefaultFeatureRenderer;
42305
+ return /* @__PURE__ */ jsx(Renderer, { feature, position }, feature.id ?? `feature-${index}`);
42306
+ }),
42307
+ units.map((unit) => {
42308
+ const position = gridToWorld(
42309
+ unit.x ?? 0,
42310
+ unit.z ?? unit.y ?? 0,
42311
+ (unit.elevation ?? 0) + 0.5
42312
+ );
42313
+ const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
42314
+ return /* @__PURE__ */ jsx(Renderer, { unit, position }, unit.id);
42315
+ })
42316
+ ] }),
42289
42317
  children,
42290
42318
  /* @__PURE__ */ jsx(
42291
42319
  OrbitControls,
@@ -42328,6 +42356,8 @@ function GameBoard3D({
42328
42356
  features = [],
42329
42357
  cameraMode = "perspective",
42330
42358
  backgroundColor = "#2a1a1a",
42359
+ unitScale = 1,
42360
+ scale = 0.45,
42331
42361
  tileClickEvent,
42332
42362
  unitClickEvent,
42333
42363
  attackEvent,
@@ -42428,6 +42458,8 @@ function GameBoard3D({
42428
42458
  selectedUnitId,
42429
42459
  validMoves,
42430
42460
  attackTargets,
42461
+ unitScale,
42462
+ scale,
42431
42463
  className: "game-board-3d__canvas w-full min-h-[85vh]"
42432
42464
  }
42433
42465
  ),
@@ -44413,6 +44445,8 @@ function RoguelikeBoard({
44413
44445
  assetManifest: propAssetManifest,
44414
44446
  scale = 0.45,
44415
44447
  unitScale = 1,
44448
+ spriteHeightRatio = 1.5,
44449
+ spriteMaxWidthRatio = 0.6,
44416
44450
  moveEvent,
44417
44451
  playAgainEvent,
44418
44452
  gameEndEvent,
@@ -44560,7 +44594,9 @@ function RoguelikeBoard({
44560
44594
  scale,
44561
44595
  assetBaseUrl: propAssetManifest?.baseUrl ?? CDN4,
44562
44596
  assetManifest: propAssetManifest,
44563
- unitScale
44597
+ unitScale,
44598
+ spriteHeightRatio,
44599
+ spriteMaxWidthRatio
44564
44600
  }
44565
44601
  ) }),
44566
44602
  !isGameOver && /* @__PURE__ */ jsx(HStack, { className: "justify-center gap-2 p-3", gap: "none", children: [
@@ -45999,7 +46035,7 @@ function getAllEvents(traits2) {
45999
46035
  function EventDispatcherTab({ traits: traits2, schema }) {
46000
46036
  const eventBus = useEventBus();
46001
46037
  const { t } = useTranslate();
46002
- const [log14, setLog] = React81.useState([]);
46038
+ const [log15, setLog] = React81.useState([]);
46003
46039
  const prevStatesRef = React81.useRef(/* @__PURE__ */ new Map());
46004
46040
  React81.useEffect(() => {
46005
46041
  for (const trait of traits2) {
@@ -46063,9 +46099,9 @@ function EventDispatcherTab({ traits: traits2, schema }) {
46063
46099
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.otherEvents") }),
46064
46100
  /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: unavailableEvents.map((event) => /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", className: "opacity-50", children: event }, event)) })
46065
46101
  ] }),
46066
- log14.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
46102
+ log15.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
46067
46103
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.recentTransitions") }),
46068
- /* @__PURE__ */ jsx(Stack, { gap: "xs", children: log14.map((entry, i) => /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
46104
+ /* @__PURE__ */ jsx(Stack, { gap: "xs", children: log15.map((entry, i) => /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
46069
46105
  /* @__PURE__ */ jsx("span", { className: "text-purple-400", children: entry.traitName }),
46070
46106
  " ",
46071
46107
  /* @__PURE__ */ jsx("span", { className: "text-gray-500", children: entry.from }),
@@ -48932,6 +48968,9 @@ function TowerDefenseBoard({
48932
48968
  waveActive: propWaveActive,
48933
48969
  towerCost = 25,
48934
48970
  scale = 0.45,
48971
+ unitScale = 1,
48972
+ spriteHeightRatio = 1.5,
48973
+ spriteMaxWidthRatio = 0.6,
48935
48974
  placeTowerEvent,
48936
48975
  startWaveEvent,
48937
48976
  playAgainEvent,
@@ -49039,7 +49078,10 @@ function TowerDefenseBoard({
49039
49078
  onTileClick: handleTileClick,
49040
49079
  onTileHover: (x, y) => setHoveredTile({ x, y }),
49041
49080
  onTileLeave: () => setHoveredTile(null),
49042
- scale
49081
+ scale,
49082
+ unitScale,
49083
+ spriteHeightRatio,
49084
+ spriteMaxWidthRatio
49043
49085
  }
49044
49086
  ),
49045
49087
  hoveredTile && canPlaceTower && validMoves.some((m) => m.x === hoveredTile.x && m.y === hoveredTile.y) && /* @__PURE__ */ jsx(Box, { className: "absolute bottom-4 left-1/2 -translate-x-1/2 z-10 bg-background/80 rounded px-3 py-1 backdrop-blur-sm pointer-events-none", children: /* @__PURE__ */ jsx(Typography, { variant: "small", children: t("td.placeTower") ?? `Place tower (${towerCost} gold)` }) })
@@ -49375,6 +49417,8 @@ function WorldMapBoard({
49375
49417
  isLoading,
49376
49418
  scale = 0.4,
49377
49419
  unitScale = 2.5,
49420
+ spriteHeightRatio = 1.5,
49421
+ spriteMaxWidthRatio = 0.6,
49378
49422
  allowMoveAllHeroes = false,
49379
49423
  isInRange = defaultIsInRange,
49380
49424
  heroSelectEvent,
@@ -49612,6 +49656,8 @@ function WorldMapBoard({
49612
49656
  effectSpriteUrls,
49613
49657
  resolveUnitFrame,
49614
49658
  unitScale,
49659
+ spriteHeightRatio,
49660
+ spriteMaxWidthRatio,
49615
49661
  diamondTopY,
49616
49662
  enableCamera
49617
49663
  }
@@ -50930,7 +50976,7 @@ function isPatternConfig(value) {
50930
50976
  if (value instanceof Date) return false;
50931
50977
  if (typeof value === "function") return false;
50932
50978
  const record = value;
50933
- return "type" in record && typeof record.type === "string";
50979
+ return "type" in record && typeof record.type === "string" && isKnownPattern(record.type);
50934
50980
  }
50935
50981
  function isPlainConfigObject(value) {
50936
50982
  if (React81__default.isValidElement(value)) return false;
@@ -51677,7 +51723,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
51677
51723
  };
51678
51724
  }, [traitBindings]);
51679
51725
  const executeTransitionEffects = useCallback(async (params) => {
51680
- const { binding, previousState, newState, payload, flushEvent, syncOnly, log: log14 } = params;
51726
+ const { binding, previousState, newState, payload, flushEvent, syncOnly, log: log15 } = params;
51681
51727
  const traitName = binding.trait.name;
51682
51728
  const linkedEntity = binding.linkedEntity || "";
51683
51729
  const entityId = payload?.entityId;
@@ -51765,7 +51811,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
51765
51811
  ...handlers,
51766
51812
  set: async (targetId, field, value) => {
51767
51813
  if (baseSet) await baseSet(targetId, field, value);
51768
- log14.debug("set:write", {
51814
+ log15.debug("set:write", {
51769
51815
  traitName,
51770
51816
  field,
51771
51817
  value: JSON.stringify(value),
@@ -51805,7 +51851,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
51805
51851
  const executor = new EffectExecutor({ handlers: trackingHandlers, bindings: bindingCtx, context: effectContext });
51806
51852
  try {
51807
51853
  await executor.executeAll(effects);
51808
- log14.debug("effects:executed", () => ({
51854
+ log15.debug("effects:executed", () => ({
51809
51855
  traitName,
51810
51856
  transition: `${previousState}->${newState}`,
51811
51857
  event: flushEvent,
@@ -51815,7 +51861,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
51815
51861
  slotsTouched: Array.from(pendingSlots.keys()).join(",")
51816
51862
  }));
51817
51863
  for (const [slot, patterns] of pendingSlots) {
51818
- log14.debug("flush:slot", {
51864
+ log15.debug("flush:slot", {
51819
51865
  traitName,
51820
51866
  slot,
51821
51867
  patternCount: patterns.length,
@@ -51830,7 +51876,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
51830
51876
  });
51831
51877
  }
51832
51878
  } catch (error) {
51833
- log14.error("effects:error", {
51879
+ log15.error("effects:error", {
51834
51880
  traitName,
51835
51881
  transition: `${previousState}->${newState}`,
51836
51882
  event: flushEvent,
@@ -51853,7 +51899,10 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
51853
51899
  if (binding.config) {
51854
51900
  guardCtx.config = binding.config;
51855
51901
  }
51856
- const passed = interpolateValue(tick.guard, createContextFromBindings(guardCtx));
51902
+ const passed = evaluateGuard(
51903
+ tick.guard,
51904
+ createContextFromBindings(guardCtx)
51905
+ );
51857
51906
  if (!passed) {
51858
51907
  tickLog.debug("guard-blocked", { traitName, tick: tick.name, state: currentState });
51859
51908
  return;
@@ -52302,7 +52351,7 @@ function clearSchemaCache() {
52302
52351
 
52303
52352
  // runtime/index.ts
52304
52353
  init_EntitySchemaContext();
52305
- var log11 = createLogger("almadar:ui:trait-provider");
52354
+ var log12 = createLogger("almadar:ui:trait-provider");
52306
52355
  var TraitContext = createContext(null);
52307
52356
  function TraitProvider({
52308
52357
  traits: traitBindings,
@@ -52319,7 +52368,7 @@ function TraitProvider({
52319
52368
  currentState: stateName,
52320
52369
  availableEvents: trait.transitions.filter((t) => t.from === stateName).map((t) => t.event),
52321
52370
  dispatch: (eventKey, payload) => {
52322
- log11.debug("Dispatch", () => ({
52371
+ log12.debug("Dispatch", () => ({
52323
52372
  trait: trait.name,
52324
52373
  event: eventKey,
52325
52374
  payloadKeys: payload ? Object.keys(payload) : []
@@ -53069,7 +53118,7 @@ OrbitalThemeProvider.displayName = "OrbitalThemeProvider";
53069
53118
  init_navigation();
53070
53119
  init_verificationRegistry();
53071
53120
  var PERF_NAMESPACE = "almadar:perf:canvas";
53072
- var log13 = createLogger(PERF_NAMESPACE);
53121
+ var log14 = createLogger(PERF_NAMESPACE);
53073
53122
  var RING_SIZE = 50;
53074
53123
  var ring = [];
53075
53124
  var writeIdx = 0;
@@ -53124,7 +53173,7 @@ function perfEnd(name, startToken, detail) {
53124
53173
  }
53125
53174
  }
53126
53175
  push({ name, durationMs, ts: endTs, detail });
53127
- log13.debug(name, () => ({ durationMs, ...detail ?? {} }));
53176
+ log14.debug(name, () => ({ durationMs, ...detail ?? {} }));
53128
53177
  }
53129
53178
  function perfTime(name, fn, detail) {
53130
53179
  const t = perfStart(name);
@@ -53142,7 +53191,7 @@ var profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, com
53142
53191
  ts: commitTime,
53143
53192
  detail: { baseDuration }
53144
53193
  });
53145
- log13.debug(`profiler:${id}:${phase}`, () => ({ actualDuration, baseDuration }));
53194
+ log14.debug(`profiler:${id}:${phase}`, () => ({ actualDuration, baseDuration }));
53146
53195
  };
53147
53196
  function getPerfSnapshot() {
53148
53197
  if (ring.length < RING_SIZE) return ring.slice();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "5.54.0",
3
+ "version": "5.56.0",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "sideEffects": [