@almadar/ui 5.70.0 → 5.72.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 (63) hide show
  1. package/dist/avl/index.cjs +2198 -2218
  2. package/dist/avl/index.d.cts +93 -56
  3. package/dist/avl/index.js +931 -951
  4. package/dist/components/avl/molecules/AvlEventWireEdge.d.ts +2 -1
  5. package/dist/components/avl/molecules/AvlTransitionEdge.d.ts +2 -1
  6. package/dist/components/avl/molecules/avl-behavior-compose-converter.d.ts +7 -5
  7. package/dist/components/avl/molecules/avl-behavior-compose-types.d.ts +41 -21
  8. package/dist/components/avl/molecules/avl-canvas-types.d.ts +10 -7
  9. package/dist/components/avl/molecules/avl-preview-converter.d.ts +5 -5
  10. package/dist/components/avl/molecules/avl-preview-types.d.ts +15 -4
  11. package/dist/components/avl/organisms/FlowCanvas.d.ts +2 -2
  12. package/dist/components/avl/organisms/avl-schema-parser.d.ts +18 -16
  13. package/dist/components/core/atoms/Button.d.ts +2 -0
  14. package/dist/components/core/atoms/ConditionalWrapper.d.ts +5 -4
  15. package/dist/components/core/molecules/Carousel.d.ts +2 -2
  16. package/dist/components/core/molecules/OrbitalVisualization.d.ts +4 -3
  17. package/dist/components/core/molecules/PageHeader.d.ts +3 -3
  18. package/dist/components/core/molecules/RepeatableFormSection.d.ts +4 -3
  19. package/dist/components/core/molecules/ViolationAlert.d.ts +2 -0
  20. package/dist/components/core/molecules/WizardContainer.d.ts +23 -22
  21. package/dist/components/core/organisms/CardGrid.d.ts +2 -2
  22. package/dist/components/core/organisms/CustomPattern.d.ts +3 -4
  23. package/dist/components/core/organisms/DataTable.d.ts +2 -2
  24. package/dist/components/core/organisms/DetailPanel.d.ts +1 -1
  25. package/dist/components/core/organisms/Form.d.ts +11 -11
  26. package/dist/components/core/organisms/List.d.ts +3 -5
  27. package/dist/components/core/organisms/StatCard.d.ts +2 -2
  28. package/dist/components/core/organisms/book/types.d.ts +1 -1
  29. package/dist/components/core/organisms/debug/RuntimeDebugger.d.ts +2 -1
  30. package/dist/components/core/organisms/debug/tabs/EventDispatcherTab.d.ts +2 -1
  31. package/dist/components/core/organisms/layout/DashboardGrid.d.ts +0 -2
  32. package/dist/components/core/organisms/types.d.ts +3 -2
  33. package/dist/components/game/atoms/ScoreDisplay.d.ts +3 -3
  34. package/dist/components/game/atoms/Sprite.d.ts +6 -1
  35. package/dist/components/game/atoms/XPBar.d.ts +1 -1
  36. package/dist/components/game/molecules/GameCanvas3D.d.ts +0 -2
  37. package/dist/components/game/molecules/IsometricCanvas.d.ts +4 -2
  38. package/dist/components/game/molecules/PlatformerCanvas.d.ts +4 -1
  39. package/dist/components/game/molecules/StatBadge.d.ts +3 -1
  40. package/dist/components/game/molecules/three/hooks/useSceneGraph.d.ts +2 -1
  41. package/dist/components/game/molecules/three/index.cjs +41 -105
  42. package/dist/components/game/molecules/three/index.js +41 -105
  43. package/dist/components/game/molecules/useUnitSpriteAtlas.d.ts +3 -2
  44. package/dist/components/game/organisms/PinballBoard.d.ts +4 -4
  45. package/dist/components/game/organisms/boardEntity.d.ts +6 -6
  46. package/dist/components/game/organisms/puzzles/debugger/DebuggerBoard.d.ts +16 -2
  47. package/dist/components/game/organisms/puzzles/event-handler/puzzleObject.d.ts +21 -10
  48. package/dist/components/game/organisms/puzzles/state-architect/StateJsonView.d.ts +2 -1
  49. package/dist/components/game/organisms/types/isometric.d.ts +16 -0
  50. package/dist/components/index.cjs +2228 -2251
  51. package/dist/components/index.js +1116 -1139
  52. package/dist/docs/index.cjs +107 -104
  53. package/dist/docs/index.d.cts +2 -0
  54. package/dist/docs/index.js +38 -35
  55. package/dist/lib/getNestedValue.d.ts +2 -13
  56. package/dist/marketing/index.cjs +97 -98
  57. package/dist/marketing/index.d.cts +2 -0
  58. package/dist/marketing/index.js +54 -55
  59. package/dist/providers/index.cjs +1908 -1951
  60. package/dist/providers/index.js +808 -851
  61. package/dist/runtime/index.cjs +1966 -2009
  62. package/dist/runtime/index.js +814 -857
  63. package/package.json +1 -1
@@ -2293,13 +2293,6 @@ function preloadFeatures(urls) {
2293
2293
  }
2294
2294
 
2295
2295
  // components/game/organisms/utils/spriteAnimation.ts
2296
- function inferDirection(dx, dy) {
2297
- if (dx === 0 && dy === 0) return "se";
2298
- if (dx >= 0 && dy >= 0) return "se";
2299
- if (dx <= 0 && dy >= 0) return "sw";
2300
- if (dx >= 0 && dy <= 0) return "ne";
2301
- return "nw";
2302
- }
2303
2296
  function resolveSheetDirection(facing) {
2304
2297
  switch (facing) {
2305
2298
  case "se":
@@ -2320,22 +2313,8 @@ function frameRect(frame, row, columns, frameWidth, frameHeight) {
2320
2313
  sh: frameHeight
2321
2314
  };
2322
2315
  }
2323
- function getCurrentFrameFromDef(def, elapsed) {
2324
- const frameDuration = 1e3 / def.frameRate;
2325
- const totalDuration = def.frames * frameDuration;
2326
- if (def.loop) {
2327
- const frame2 = Math.floor(elapsed % totalDuration / frameDuration);
2328
- return { frame: frame2, finished: false };
2329
- }
2330
- if (elapsed >= totalDuration) {
2331
- return { frame: def.frames - 1, finished: true };
2332
- }
2333
- const frame = Math.floor(elapsed / frameDuration);
2334
- return { frame, finished: false };
2335
- }
2336
2316
 
2337
2317
  // components/game/molecules/useUnitSpriteAtlas.ts
2338
- var WALK_HOLD_MS = 600;
2339
2318
  function unitAtlasUrl(unit) {
2340
2319
  if (unit.spriteSheet) return unit.spriteSheet;
2341
2320
  const sprite = unit.sprite;
@@ -2357,9 +2336,6 @@ function useUnitSpriteAtlas(units) {
2357
2336
  const loadingRef = React11.useRef(/* @__PURE__ */ new Set());
2358
2337
  const [pendingCount, setPendingCount] = React11.useState(0);
2359
2338
  const [, forceTick] = React11.useState(0);
2360
- const animStatesRef = React11.useRef(/* @__PURE__ */ new Map());
2361
- const lastTickRef = React11.useRef(0);
2362
- const rafRef = React11.useRef(0);
2363
2339
  const atlasUrls = React11.useMemo(() => {
2364
2340
  const set = /* @__PURE__ */ new Set();
2365
2341
  for (const unit of units) {
@@ -2405,54 +2381,6 @@ function useUnitSpriteAtlas(units) {
2405
2381
  }
2406
2382
  return [...urls];
2407
2383
  }, [units, pendingCount]);
2408
- React11.useEffect(() => {
2409
- const hasAtlasUnits = units.some((u) => unitAtlasUrl(u) !== null);
2410
- if (!hasAtlasUnits) return;
2411
- let running = true;
2412
- const tick = (ts) => {
2413
- if (!running) return;
2414
- const last = lastTickRef.current || ts;
2415
- const delta = ts - last;
2416
- lastTickRef.current = ts;
2417
- const states = animStatesRef.current;
2418
- const currentIds = /* @__PURE__ */ new Set();
2419
- for (const unit of units) {
2420
- if (unitAtlasUrl(unit) === null) continue;
2421
- currentIds.add(unit.id);
2422
- let state = states.get(unit.id);
2423
- if (!state) {
2424
- state = { animation: "idle", direction: "se", elapsed: 0, walkHold: 0, prev: null };
2425
- states.set(unit.id, state);
2426
- }
2427
- const posX = unit.position?.x ?? unit.x ?? 0;
2428
- const posY = unit.position?.y ?? unit.y ?? 0;
2429
- if (state.prev) {
2430
- const dx = posX - state.prev.x;
2431
- const dy = posY - state.prev.y;
2432
- if (dx !== 0 || dy !== 0) {
2433
- state.animation = "walk";
2434
- state.direction = inferDirection(dx, dy);
2435
- state.walkHold = WALK_HOLD_MS;
2436
- } else if (state.animation === "walk") {
2437
- state.walkHold -= delta;
2438
- if (state.walkHold <= 0) state.animation = "idle";
2439
- }
2440
- }
2441
- state.prev = { x: posX, y: posY };
2442
- state.elapsed += delta;
2443
- }
2444
- for (const id of states.keys()) {
2445
- if (!currentIds.has(id)) states.delete(id);
2446
- }
2447
- rafRef.current = requestAnimationFrame(tick);
2448
- };
2449
- rafRef.current = requestAnimationFrame(tick);
2450
- return () => {
2451
- running = false;
2452
- cancelAnimationFrame(rafRef.current);
2453
- lastTickRef.current = 0;
2454
- };
2455
- }, [units]);
2456
2384
  const resolveUnitFrame = React11.useCallback((unitId) => {
2457
2385
  const unit = units.find((u) => u.id === unitId);
2458
2386
  if (!unit) return null;
@@ -2460,18 +2388,14 @@ function useUnitSpriteAtlas(units) {
2460
2388
  if (!atlasUrl) return null;
2461
2389
  const atlas = atlasCacheRef.current.get(atlasUrl);
2462
2390
  if (!atlas) return null;
2463
- const state = animStatesRef.current.get(unitId);
2464
- const animation = state?.animation ?? "idle";
2465
- const direction = state?.direction ?? "se";
2466
- const elapsed = state?.elapsed ?? 0;
2391
+ const animation = unit.animation ?? "idle";
2392
+ const frame = unit.frame ?? 0;
2467
2393
  const def = atlas.animations[animation] ?? atlas.animations.idle;
2468
2394
  if (!def) return null;
2469
- const { sheetDir, flipX } = resolveSheetDirection(direction);
2395
+ const { sheetDir, flipX } = resolveSheetDirection("se");
2470
2396
  const rel = atlas.sheets[sheetDir] ?? atlas.sheets.se ?? atlas.sheets.sw;
2471
2397
  if (!rel) return null;
2472
2398
  const sheetUrl = resolveSheetUrl(atlasUrl, rel);
2473
- const isIdle = animation === "idle";
2474
- const frame = isIdle ? 0 : getCurrentFrameFromDef(def, elapsed).frame;
2475
2399
  const rect = frameRect(frame, def.row, atlas.columns, atlas.frameWidth, atlas.frameHeight);
2476
2400
  return {
2477
2401
  sheetUrl,
@@ -2480,7 +2404,7 @@ function useUnitSpriteAtlas(units) {
2480
2404
  sw: rect.sw,
2481
2405
  sh: rect.sh,
2482
2406
  flipX,
2483
- applyBreathing: isIdle
2407
+ applyBreathing: animation === "idle"
2484
2408
  };
2485
2409
  }, [units]);
2486
2410
  return { sheetUrls, resolveUnitFrame, pendingCount };
@@ -2576,7 +2500,6 @@ var GameCanvas3D = React11.forwardRef(
2576
2500
  className,
2577
2501
  isLoading: externalLoading,
2578
2502
  error: externalError,
2579
- entity,
2580
2503
  preloadAssets = [],
2581
2504
  tileClickEvent,
2582
2505
  unitClickEvent,
@@ -3989,6 +3912,9 @@ var iconSizeStyles = {
3989
3912
  md: "h-icon-default w-icon-default",
3990
3913
  lg: "h-icon-default w-icon-default"
3991
3914
  };
3915
+ function isIconLike(v) {
3916
+ return typeof v.render === "function";
3917
+ }
3992
3918
  function resolveIconProp(value, sizeClass) {
3993
3919
  if (!value) return null;
3994
3920
  if (typeof value === "string") {
@@ -4001,7 +3927,7 @@ function resolveIconProp(value, sizeClass) {
4001
3927
  if (React11__default.default.isValidElement(value)) {
4002
3928
  return value;
4003
3929
  }
4004
- if (typeof value === "object" && value !== null && "render" in value) {
3930
+ if (typeof value === "object" && value !== null && isIconLike(value)) {
4005
3931
  const IconComp = value;
4006
3932
  return /* @__PURE__ */ jsxRuntime.jsx(IconComp, { className: sizeClass });
4007
3933
  }
@@ -4023,6 +3949,7 @@ var Button = React11__default.default.forwardRef(
4023
3949
  label,
4024
3950
  children,
4025
3951
  onClick,
3952
+ "data-testid": dataTestId,
4026
3953
  ...props
4027
3954
  }, ref) => {
4028
3955
  const eventBus = useEventBus();
@@ -4055,7 +3982,7 @@ var Button = React11__default.default.forwardRef(
4055
3982
  ),
4056
3983
  onClick: handleClick,
4057
3984
  ...props,
4058
- "data-testid": props["data-testid"] ?? (action ? `action-${action}` : void 0),
3985
+ "data-testid": dataTestId ?? (action ? `action-${action}` : void 0),
4059
3986
  children: [
4060
3987
  isLoading ? /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Loader2, { className: "h-icon-default w-icon-default animate-spin" }) : resolvedLeftIcon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", children: resolvedLeftIcon }),
4061
3988
  children || label,
@@ -4152,9 +4079,8 @@ var Typography = ({
4152
4079
  }) => {
4153
4080
  const variant = variantProp ?? (level ? `h${level}` : "body1");
4154
4081
  const Component2 = as || defaultElements[variant];
4155
- const Comp = Component2;
4156
- return /* @__PURE__ */ jsxRuntime.jsx(
4157
- Comp,
4082
+ return React11__default.default.createElement(
4083
+ Component2,
4158
4084
  {
4159
4085
  id,
4160
4086
  className: cn(
@@ -4167,9 +4093,9 @@ var Typography = ({
4167
4093
  overflow && overflowStyles[overflow],
4168
4094
  className
4169
4095
  ),
4170
- style,
4171
- children: children ?? content
4172
- }
4096
+ style
4097
+ },
4098
+ children ?? content
4173
4099
  );
4174
4100
  };
4175
4101
  Typography.displayName = "Typography";
@@ -4226,9 +4152,8 @@ var Stack = ({
4226
4152
  };
4227
4153
  const isHorizontal = direction === "horizontal";
4228
4154
  const directionClass = responsive && isHorizontal ? reverse ? "flex-col-reverse md:flex-row-reverse" : "flex-col md:flex-row" : isHorizontal ? reverse ? "flex-row-reverse" : "flex-row" : reverse ? "flex-col-reverse" : "flex-col";
4229
- const Comp = Component2;
4230
- return /* @__PURE__ */ jsxRuntime.jsx(
4231
- Comp,
4155
+ return React11__default.default.createElement(
4156
+ Component2,
4232
4157
  {
4233
4158
  className: cn(
4234
4159
  "flex",
@@ -4244,9 +4169,9 @@ var Stack = ({
4244
4169
  onClick: action || onClick ? handleClick : void 0,
4245
4170
  onKeyDown,
4246
4171
  role,
4247
- tabIndex,
4248
- children
4249
- }
4172
+ tabIndex
4173
+ },
4174
+ children
4250
4175
  );
4251
4176
  };
4252
4177
  var VStack = (props) => /* @__PURE__ */ jsxRuntime.jsx(Stack, { direction: "vertical", ...props });
@@ -5163,9 +5088,17 @@ function getEntity(orbital) {
5163
5088
  if (typeof entity === "string") {
5164
5089
  return { name: entity, fields: [], persistence: "runtime" };
5165
5090
  }
5091
+ if ("extends" in entity) {
5092
+ const ec = entity;
5093
+ return {
5094
+ name: ec.name ?? orbital.name,
5095
+ fields: ec.fields ?? [],
5096
+ persistence: ec.persistence ?? "runtime"
5097
+ };
5098
+ }
5166
5099
  const e = entity;
5167
5100
  return {
5168
- name: e.name ?? orbital.name,
5101
+ name: e.name,
5169
5102
  fields: e.fields ?? [],
5170
5103
  persistence: e.persistence ?? "runtime"
5171
5104
  };
@@ -5174,6 +5107,7 @@ function getTraits(orbital) {
5174
5107
  if (!orbital.traits) return [];
5175
5108
  return orbital.traits.map((t) => {
5176
5109
  if (typeof t === "string") return { name: t };
5110
+ if ("ref" in t && !("stateMachine" in t)) return { name: t.name ?? t.ref };
5177
5111
  return t;
5178
5112
  });
5179
5113
  }
@@ -5181,6 +5115,7 @@ function getPages(orbital) {
5181
5115
  if (!orbital.pages) return [];
5182
5116
  return orbital.pages.map((p) => {
5183
5117
  if (typeof p === "string") return { name: p, path: `/${p.toLowerCase()}` };
5118
+ if ("ref" in p && !("path" in p)) return { name: p.ref, path: `/${p.ref.toLowerCase()}` };
5184
5119
  return p;
5185
5120
  });
5186
5121
  }
@@ -5199,12 +5134,12 @@ function getEvents(sm) {
5199
5134
  function getEmits(trait) {
5200
5135
  const emits = trait.emits;
5201
5136
  if (!emits) return [];
5202
- return emits.map((e) => typeof e === "string" ? e : e.event ?? e.name ?? "");
5137
+ return emits.map((e) => e.event ?? "");
5203
5138
  }
5204
5139
  function getListens(trait) {
5205
5140
  const listens = trait.listens;
5206
5141
  if (!listens) return [];
5207
- return listens.map((l) => typeof l === "string" ? l : l.event ?? "");
5142
+ return listens.map((l) => l.event ?? "");
5208
5143
  }
5209
5144
  function parseEffectType(effect) {
5210
5145
  if (Array.isArray(effect)) {
@@ -5375,14 +5310,14 @@ function parseTraitLevel(schema, orbitalName, traitName) {
5375
5310
  if (!sm) return null;
5376
5311
  const states = getStates(sm).map((s) => ({
5377
5312
  name: s.name ?? "",
5378
- isInitial: s.isInitial ?? false,
5379
- isTerminal: s.isTerminal ?? false
5313
+ isInitial: s.isInitial ?? null,
5314
+ isTerminal: s.isTerminal ?? null
5380
5315
  }));
5381
5316
  const transitions = getTransitions(sm).map((t, i) => ({
5382
5317
  from: t.from ?? "",
5383
5318
  to: t.to ?? "",
5384
5319
  event: t.event ?? "",
5385
- guard: t.guard,
5320
+ guard: t.guard ?? null,
5386
5321
  effects: (t.effects ?? []).map(parseEffectType),
5387
5322
  index: i
5388
5323
  }));
@@ -5401,13 +5336,14 @@ function parseTransitionLevel(schema, orbitalName, traitName, transitionIndex) {
5401
5336
  if (!traitData) return null;
5402
5337
  const transition = traitData.transitions[transitionIndex];
5403
5338
  if (!transition) return null;
5404
- const guard = transition.guard ? exprToTree(transition.guard) : null;
5339
+ const guardVal = transition.guard;
5340
+ const guard = guardVal != null ? exprToTree(guardVal) : null;
5405
5341
  const effects = transition.effects.map(
5406
5342
  (e) => exprToTree([e.type, ...e.args])
5407
5343
  );
5408
5344
  const slotTargets = transition.effects.filter((e) => e.type === "render-ui").map((e) => ({
5409
5345
  name: String(e.args[0] ?? "main"),
5410
- pattern: typeof e.args[1] === "object" && e.args[1] !== null ? e.args[1].type ?? "unknown" : String(e.args[1] ?? "unknown")
5346
+ pattern: typeof e.args[1] === "object" && e.args[1] !== null && !Array.isArray(e.args[1]) ? e.args[1].type ?? "unknown" : String(e.args[1] ?? "unknown")
5411
5347
  }));
5412
5348
  return {
5413
5349
  from: transition.from,
@@ -6870,8 +6806,8 @@ var Avl3DTraitScene = ({
6870
6806
  Avl3DStateNode,
6871
6807
  {
6872
6808
  name: state.name,
6873
- isInitial: state.isInitial,
6874
- isTerminal: state.isTerminal,
6809
+ isInitial: state.isInitial ?? void 0,
6810
+ isTerminal: state.isTerminal ?? void 0,
6875
6811
  position: pos,
6876
6812
  incomingCount: transitionCounts.incoming.get(state.name) ?? 0,
6877
6813
  outgoingCount: transitionCounts.outgoing.get(state.name) ?? 0
@@ -2269,13 +2269,6 @@ function preloadFeatures(urls) {
2269
2269
  }
2270
2270
 
2271
2271
  // components/game/organisms/utils/spriteAnimation.ts
2272
- function inferDirection(dx, dy) {
2273
- if (dx === 0 && dy === 0) return "se";
2274
- if (dx >= 0 && dy >= 0) return "se";
2275
- if (dx <= 0 && dy >= 0) return "sw";
2276
- if (dx >= 0 && dy <= 0) return "ne";
2277
- return "nw";
2278
- }
2279
2272
  function resolveSheetDirection(facing) {
2280
2273
  switch (facing) {
2281
2274
  case "se":
@@ -2296,22 +2289,8 @@ function frameRect(frame, row, columns, frameWidth, frameHeight) {
2296
2289
  sh: frameHeight
2297
2290
  };
2298
2291
  }
2299
- function getCurrentFrameFromDef(def, elapsed) {
2300
- const frameDuration = 1e3 / def.frameRate;
2301
- const totalDuration = def.frames * frameDuration;
2302
- if (def.loop) {
2303
- const frame2 = Math.floor(elapsed % totalDuration / frameDuration);
2304
- return { frame: frame2, finished: false };
2305
- }
2306
- if (elapsed >= totalDuration) {
2307
- return { frame: def.frames - 1, finished: true };
2308
- }
2309
- const frame = Math.floor(elapsed / frameDuration);
2310
- return { frame, finished: false };
2311
- }
2312
2292
 
2313
2293
  // components/game/molecules/useUnitSpriteAtlas.ts
2314
- var WALK_HOLD_MS = 600;
2315
2294
  function unitAtlasUrl(unit) {
2316
2295
  if (unit.spriteSheet) return unit.spriteSheet;
2317
2296
  const sprite = unit.sprite;
@@ -2333,9 +2312,6 @@ function useUnitSpriteAtlas(units) {
2333
2312
  const loadingRef = useRef(/* @__PURE__ */ new Set());
2334
2313
  const [pendingCount, setPendingCount] = useState(0);
2335
2314
  const [, forceTick] = useState(0);
2336
- const animStatesRef = useRef(/* @__PURE__ */ new Map());
2337
- const lastTickRef = useRef(0);
2338
- const rafRef = useRef(0);
2339
2315
  const atlasUrls = useMemo(() => {
2340
2316
  const set = /* @__PURE__ */ new Set();
2341
2317
  for (const unit of units) {
@@ -2381,54 +2357,6 @@ function useUnitSpriteAtlas(units) {
2381
2357
  }
2382
2358
  return [...urls];
2383
2359
  }, [units, pendingCount]);
2384
- useEffect(() => {
2385
- const hasAtlasUnits = units.some((u) => unitAtlasUrl(u) !== null);
2386
- if (!hasAtlasUnits) return;
2387
- let running = true;
2388
- const tick = (ts) => {
2389
- if (!running) return;
2390
- const last = lastTickRef.current || ts;
2391
- const delta = ts - last;
2392
- lastTickRef.current = ts;
2393
- const states = animStatesRef.current;
2394
- const currentIds = /* @__PURE__ */ new Set();
2395
- for (const unit of units) {
2396
- if (unitAtlasUrl(unit) === null) continue;
2397
- currentIds.add(unit.id);
2398
- let state = states.get(unit.id);
2399
- if (!state) {
2400
- state = { animation: "idle", direction: "se", elapsed: 0, walkHold: 0, prev: null };
2401
- states.set(unit.id, state);
2402
- }
2403
- const posX = unit.position?.x ?? unit.x ?? 0;
2404
- const posY = unit.position?.y ?? unit.y ?? 0;
2405
- if (state.prev) {
2406
- const dx = posX - state.prev.x;
2407
- const dy = posY - state.prev.y;
2408
- if (dx !== 0 || dy !== 0) {
2409
- state.animation = "walk";
2410
- state.direction = inferDirection(dx, dy);
2411
- state.walkHold = WALK_HOLD_MS;
2412
- } else if (state.animation === "walk") {
2413
- state.walkHold -= delta;
2414
- if (state.walkHold <= 0) state.animation = "idle";
2415
- }
2416
- }
2417
- state.prev = { x: posX, y: posY };
2418
- state.elapsed += delta;
2419
- }
2420
- for (const id of states.keys()) {
2421
- if (!currentIds.has(id)) states.delete(id);
2422
- }
2423
- rafRef.current = requestAnimationFrame(tick);
2424
- };
2425
- rafRef.current = requestAnimationFrame(tick);
2426
- return () => {
2427
- running = false;
2428
- cancelAnimationFrame(rafRef.current);
2429
- lastTickRef.current = 0;
2430
- };
2431
- }, [units]);
2432
2360
  const resolveUnitFrame = useCallback((unitId) => {
2433
2361
  const unit = units.find((u) => u.id === unitId);
2434
2362
  if (!unit) return null;
@@ -2436,18 +2364,14 @@ function useUnitSpriteAtlas(units) {
2436
2364
  if (!atlasUrl) return null;
2437
2365
  const atlas = atlasCacheRef.current.get(atlasUrl);
2438
2366
  if (!atlas) return null;
2439
- const state = animStatesRef.current.get(unitId);
2440
- const animation = state?.animation ?? "idle";
2441
- const direction = state?.direction ?? "se";
2442
- const elapsed = state?.elapsed ?? 0;
2367
+ const animation = unit.animation ?? "idle";
2368
+ const frame = unit.frame ?? 0;
2443
2369
  const def = atlas.animations[animation] ?? atlas.animations.idle;
2444
2370
  if (!def) return null;
2445
- const { sheetDir, flipX } = resolveSheetDirection(direction);
2371
+ const { sheetDir, flipX } = resolveSheetDirection("se");
2446
2372
  const rel = atlas.sheets[sheetDir] ?? atlas.sheets.se ?? atlas.sheets.sw;
2447
2373
  if (!rel) return null;
2448
2374
  const sheetUrl = resolveSheetUrl(atlasUrl, rel);
2449
- const isIdle = animation === "idle";
2450
- const frame = isIdle ? 0 : getCurrentFrameFromDef(def, elapsed).frame;
2451
2375
  const rect = frameRect(frame, def.row, atlas.columns, atlas.frameWidth, atlas.frameHeight);
2452
2376
  return {
2453
2377
  sheetUrl,
@@ -2456,7 +2380,7 @@ function useUnitSpriteAtlas(units) {
2456
2380
  sw: rect.sw,
2457
2381
  sh: rect.sh,
2458
2382
  flipX,
2459
- applyBreathing: isIdle
2383
+ applyBreathing: animation === "idle"
2460
2384
  };
2461
2385
  }, [units]);
2462
2386
  return { sheetUrls, resolveUnitFrame, pendingCount };
@@ -2552,7 +2476,6 @@ var GameCanvas3D = forwardRef(
2552
2476
  className,
2553
2477
  isLoading: externalLoading,
2554
2478
  error: externalError,
2555
- entity,
2556
2479
  preloadAssets = [],
2557
2480
  tileClickEvent,
2558
2481
  unitClickEvent,
@@ -3965,6 +3888,9 @@ var iconSizeStyles = {
3965
3888
  md: "h-icon-default w-icon-default",
3966
3889
  lg: "h-icon-default w-icon-default"
3967
3890
  };
3891
+ function isIconLike(v) {
3892
+ return typeof v.render === "function";
3893
+ }
3968
3894
  function resolveIconProp(value, sizeClass) {
3969
3895
  if (!value) return null;
3970
3896
  if (typeof value === "string") {
@@ -3977,7 +3903,7 @@ function resolveIconProp(value, sizeClass) {
3977
3903
  if (React11.isValidElement(value)) {
3978
3904
  return value;
3979
3905
  }
3980
- if (typeof value === "object" && value !== null && "render" in value) {
3906
+ if (typeof value === "object" && value !== null && isIconLike(value)) {
3981
3907
  const IconComp = value;
3982
3908
  return /* @__PURE__ */ jsx(IconComp, { className: sizeClass });
3983
3909
  }
@@ -3999,6 +3925,7 @@ var Button = React11.forwardRef(
3999
3925
  label,
4000
3926
  children,
4001
3927
  onClick,
3928
+ "data-testid": dataTestId,
4002
3929
  ...props
4003
3930
  }, ref) => {
4004
3931
  const eventBus = useEventBus();
@@ -4031,7 +3958,7 @@ var Button = React11.forwardRef(
4031
3958
  ),
4032
3959
  onClick: handleClick,
4033
3960
  ...props,
4034
- "data-testid": props["data-testid"] ?? (action ? `action-${action}` : void 0),
3961
+ "data-testid": dataTestId ?? (action ? `action-${action}` : void 0),
4035
3962
  children: [
4036
3963
  isLoading ? /* @__PURE__ */ jsx(Loader2, { className: "h-icon-default w-icon-default animate-spin" }) : resolvedLeftIcon && /* @__PURE__ */ jsx("span", { className: "flex-shrink-0", children: resolvedLeftIcon }),
4037
3964
  children || label,
@@ -4128,9 +4055,8 @@ var Typography = ({
4128
4055
  }) => {
4129
4056
  const variant = variantProp ?? (level ? `h${level}` : "body1");
4130
4057
  const Component2 = as || defaultElements[variant];
4131
- const Comp = Component2;
4132
- return /* @__PURE__ */ jsx(
4133
- Comp,
4058
+ return React11.createElement(
4059
+ Component2,
4134
4060
  {
4135
4061
  id,
4136
4062
  className: cn(
@@ -4143,9 +4069,9 @@ var Typography = ({
4143
4069
  overflow && overflowStyles[overflow],
4144
4070
  className
4145
4071
  ),
4146
- style,
4147
- children: children ?? content
4148
- }
4072
+ style
4073
+ },
4074
+ children ?? content
4149
4075
  );
4150
4076
  };
4151
4077
  Typography.displayName = "Typography";
@@ -4202,9 +4128,8 @@ var Stack = ({
4202
4128
  };
4203
4129
  const isHorizontal = direction === "horizontal";
4204
4130
  const directionClass = responsive && isHorizontal ? reverse ? "flex-col-reverse md:flex-row-reverse" : "flex-col md:flex-row" : isHorizontal ? reverse ? "flex-row-reverse" : "flex-row" : reverse ? "flex-col-reverse" : "flex-col";
4205
- const Comp = Component2;
4206
- return /* @__PURE__ */ jsx(
4207
- Comp,
4131
+ return React11.createElement(
4132
+ Component2,
4208
4133
  {
4209
4134
  className: cn(
4210
4135
  "flex",
@@ -4220,9 +4145,9 @@ var Stack = ({
4220
4145
  onClick: action || onClick ? handleClick : void 0,
4221
4146
  onKeyDown,
4222
4147
  role,
4223
- tabIndex,
4224
- children
4225
- }
4148
+ tabIndex
4149
+ },
4150
+ children
4226
4151
  );
4227
4152
  };
4228
4153
  var VStack = (props) => /* @__PURE__ */ jsx(Stack, { direction: "vertical", ...props });
@@ -5139,9 +5064,17 @@ function getEntity(orbital) {
5139
5064
  if (typeof entity === "string") {
5140
5065
  return { name: entity, fields: [], persistence: "runtime" };
5141
5066
  }
5067
+ if ("extends" in entity) {
5068
+ const ec = entity;
5069
+ return {
5070
+ name: ec.name ?? orbital.name,
5071
+ fields: ec.fields ?? [],
5072
+ persistence: ec.persistence ?? "runtime"
5073
+ };
5074
+ }
5142
5075
  const e = entity;
5143
5076
  return {
5144
- name: e.name ?? orbital.name,
5077
+ name: e.name,
5145
5078
  fields: e.fields ?? [],
5146
5079
  persistence: e.persistence ?? "runtime"
5147
5080
  };
@@ -5150,6 +5083,7 @@ function getTraits(orbital) {
5150
5083
  if (!orbital.traits) return [];
5151
5084
  return orbital.traits.map((t) => {
5152
5085
  if (typeof t === "string") return { name: t };
5086
+ if ("ref" in t && !("stateMachine" in t)) return { name: t.name ?? t.ref };
5153
5087
  return t;
5154
5088
  });
5155
5089
  }
@@ -5157,6 +5091,7 @@ function getPages(orbital) {
5157
5091
  if (!orbital.pages) return [];
5158
5092
  return orbital.pages.map((p) => {
5159
5093
  if (typeof p === "string") return { name: p, path: `/${p.toLowerCase()}` };
5094
+ if ("ref" in p && !("path" in p)) return { name: p.ref, path: `/${p.ref.toLowerCase()}` };
5160
5095
  return p;
5161
5096
  });
5162
5097
  }
@@ -5175,12 +5110,12 @@ function getEvents(sm) {
5175
5110
  function getEmits(trait) {
5176
5111
  const emits = trait.emits;
5177
5112
  if (!emits) return [];
5178
- return emits.map((e) => typeof e === "string" ? e : e.event ?? e.name ?? "");
5113
+ return emits.map((e) => e.event ?? "");
5179
5114
  }
5180
5115
  function getListens(trait) {
5181
5116
  const listens = trait.listens;
5182
5117
  if (!listens) return [];
5183
- return listens.map((l) => typeof l === "string" ? l : l.event ?? "");
5118
+ return listens.map((l) => l.event ?? "");
5184
5119
  }
5185
5120
  function parseEffectType(effect) {
5186
5121
  if (Array.isArray(effect)) {
@@ -5351,14 +5286,14 @@ function parseTraitLevel(schema, orbitalName, traitName) {
5351
5286
  if (!sm) return null;
5352
5287
  const states = getStates(sm).map((s) => ({
5353
5288
  name: s.name ?? "",
5354
- isInitial: s.isInitial ?? false,
5355
- isTerminal: s.isTerminal ?? false
5289
+ isInitial: s.isInitial ?? null,
5290
+ isTerminal: s.isTerminal ?? null
5356
5291
  }));
5357
5292
  const transitions = getTransitions(sm).map((t, i) => ({
5358
5293
  from: t.from ?? "",
5359
5294
  to: t.to ?? "",
5360
5295
  event: t.event ?? "",
5361
- guard: t.guard,
5296
+ guard: t.guard ?? null,
5362
5297
  effects: (t.effects ?? []).map(parseEffectType),
5363
5298
  index: i
5364
5299
  }));
@@ -5377,13 +5312,14 @@ function parseTransitionLevel(schema, orbitalName, traitName, transitionIndex) {
5377
5312
  if (!traitData) return null;
5378
5313
  const transition = traitData.transitions[transitionIndex];
5379
5314
  if (!transition) return null;
5380
- const guard = transition.guard ? exprToTree(transition.guard) : null;
5315
+ const guardVal = transition.guard;
5316
+ const guard = guardVal != null ? exprToTree(guardVal) : null;
5381
5317
  const effects = transition.effects.map(
5382
5318
  (e) => exprToTree([e.type, ...e.args])
5383
5319
  );
5384
5320
  const slotTargets = transition.effects.filter((e) => e.type === "render-ui").map((e) => ({
5385
5321
  name: String(e.args[0] ?? "main"),
5386
- pattern: typeof e.args[1] === "object" && e.args[1] !== null ? e.args[1].type ?? "unknown" : String(e.args[1] ?? "unknown")
5322
+ pattern: typeof e.args[1] === "object" && e.args[1] !== null && !Array.isArray(e.args[1]) ? e.args[1].type ?? "unknown" : String(e.args[1] ?? "unknown")
5387
5323
  }));
5388
5324
  return {
5389
5325
  from: transition.from,
@@ -6846,8 +6782,8 @@ var Avl3DTraitScene = ({
6846
6782
  Avl3DStateNode,
6847
6783
  {
6848
6784
  name: state.name,
6849
- isInitial: state.isInitial,
6850
- isTerminal: state.isTerminal,
6785
+ isInitial: state.isInitial ?? void 0,
6786
+ isTerminal: state.isTerminal ?? void 0,
6851
6787
  position: pos,
6852
6788
  incomingCount: transitionCounts.incoming.get(state.name) ?? 0,
6853
6789
  outgoingCount: transitionCounts.outgoing.get(state.name) ?? 0
@@ -23,8 +23,9 @@ export interface UseUnitSpriteAtlasResult {
23
23
  pendingCount: number;
24
24
  }
25
25
  /**
26
- * Load per-unit sprite atlases and drive single-frame animation.
26
+ * Load per-unit sprite atlases and expose a pure frame resolver.
27
+ * Frame is computed from each unit's own `animation` + `frame` props — no internal clock.
27
28
  *
28
- * @param units - Units to animate. Only units with a `spriteSheet` URL participate.
29
+ * @param units - Units whose atlases should be loaded.
29
30
  */
30
31
  export declare function useUnitSpriteAtlas(units: IsometricUnit[]): UseUnitSpriteAtlasResult;
@@ -1,13 +1,13 @@
1
1
  import React from 'react';
2
- import type { AssetUrl, EventEmit } from '@almadar/core';
2
+ import type { AssetUrl, EventEmit, EntityRow } from '@almadar/core';
3
3
  import type { DisplayStateProps } from '../../core/organisms/types';
4
4
  export interface PinballBoardProps extends DisplayStateProps {
5
5
  /** Playfield tiles */
6
- tiles?: unknown[];
6
+ tiles?: readonly EntityRow[];
7
7
  /** Ball units on the board */
8
- units?: unknown[];
8
+ units?: readonly EntityRow[];
9
9
  /** Features (bumpers, paddles, etc.) on the board */
10
- features?: unknown[];
10
+ features?: readonly EntityRow[];
11
11
  /** Asset sprite manifest */
12
12
  assetManifest?: string;
13
13
  /** Base URL prepended to manifest sprite paths */