@almadar/ui 5.142.0 → 5.144.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.
@@ -1526,6 +1526,10 @@ var init_Icon = __esm({
1526
1526
  function isTilesheet(a) {
1527
1527
  return typeof a.tileWidth === "number";
1528
1528
  }
1529
+ function isSpriteSheetAtlas(a) {
1530
+ const s = a;
1531
+ return typeof s.frameWidth === "number" && typeof s.frameHeight === "number" && typeof s.animations === "object";
1532
+ }
1529
1533
  function getAtlas(url, onReady) {
1530
1534
  if (atlasCache.has(url)) return atlasCache.get(url) ?? void 0;
1531
1535
  atlasCache.set(url, void 0);
@@ -1565,6 +1569,7 @@ function subRectFor(atlas, sprite) {
1565
1569
  sh: atlas.tileHeight
1566
1570
  };
1567
1571
  }
1572
+ if (isSpriteSheetAtlas(atlas)) return null;
1568
1573
  const st = atlas.subTextures[sprite];
1569
1574
  if (!st) return null;
1570
1575
  return { sx: st.x, sy: st.y, sw: st.width, sh: st.height };
@@ -1874,7 +1879,7 @@ var init_Button = __esm({
1874
1879
  "data-testid": dataTestId ?? (action ? `action-${action}` : void 0),
1875
1880
  children: [
1876
1881
  isLoading ? /* @__PURE__ */ jsxRuntime.jsx(LucideIcons2.Loader2, { className: "h-icon-default w-icon-default animate-spin" }) : resolvedLeftIcon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", children: resolvedLeftIcon }),
1877
- children || label,
1882
+ (Array.isArray(children) ? children.length > 0 : Boolean(children)) ? children : label,
1878
1883
  resolvedRightIcon && !isLoading && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", children: resolvedRightIcon })
1879
1884
  ]
1880
1885
  }
@@ -16021,6 +16026,138 @@ var init_contract = __esm({
16021
16026
  "lib/drawable/contract.ts"() {
16022
16027
  }
16023
16028
  });
16029
+
16030
+ // lib/spriteSheetConstants.ts
16031
+ exports.SHEET_COLUMNS = void 0; exports.SPRITE_SHEET_LAYOUT = void 0;
16032
+ var init_spriteSheetConstants = __esm({
16033
+ "lib/spriteSheetConstants.ts"() {
16034
+ exports.SHEET_COLUMNS = 8;
16035
+ exports.SPRITE_SHEET_LAYOUT = {
16036
+ idle: { row: 0, frames: 4, frameRate: 6, loop: true },
16037
+ walk: { row: 1, frames: 8, frameRate: 10, loop: true },
16038
+ attack: { row: 2, frames: 6, frameRate: 12, loop: false },
16039
+ hit: { row: 3, frames: 3, frameRate: 8, loop: false },
16040
+ death: { row: 4, frames: 6, frameRate: 8, loop: false }
16041
+ };
16042
+ }
16043
+ });
16044
+ function isAnimationName(name) {
16045
+ return core.ANIMATION_NAMES.includes(name);
16046
+ }
16047
+ function inferDirection(dx, dy) {
16048
+ if (dx === 0 && dy === 0) return "se";
16049
+ if (dx >= 0 && dy >= 0) return "se";
16050
+ if (dx <= 0 && dy >= 0) return "sw";
16051
+ if (dx >= 0 && dy <= 0) return "ne";
16052
+ return "nw";
16053
+ }
16054
+ function resolveSheetDirection(facing) {
16055
+ switch (facing) {
16056
+ case "se":
16057
+ return { sheetDir: "se", flipX: false };
16058
+ case "sw":
16059
+ return { sheetDir: "sw", flipX: false };
16060
+ case "ne":
16061
+ return { sheetDir: "sw", flipX: true };
16062
+ case "nw":
16063
+ return { sheetDir: "se", flipX: true };
16064
+ }
16065
+ }
16066
+ function frameRect(frame, row, columns, frameWidth, frameHeight) {
16067
+ return {
16068
+ sx: frame % columns * frameWidth,
16069
+ sy: row * frameHeight,
16070
+ sw: frameWidth,
16071
+ sh: frameHeight
16072
+ };
16073
+ }
16074
+ function getCurrentFrameFromDef(def, elapsed) {
16075
+ const frameDuration = 1e3 / def.frameRate;
16076
+ const totalDuration = def.frames * frameDuration;
16077
+ if (def.loop) {
16078
+ const frame2 = Math.floor(elapsed % totalDuration / frameDuration);
16079
+ return { frame: frame2, finished: false };
16080
+ }
16081
+ if (elapsed >= totalDuration) {
16082
+ return { frame: def.frames - 1, finished: true };
16083
+ }
16084
+ const frame = Math.floor(elapsed / frameDuration);
16085
+ return { frame, finished: false };
16086
+ }
16087
+ function getCurrentFrame(animName, elapsed) {
16088
+ return getCurrentFrameFromDef(exports.SPRITE_SHEET_LAYOUT[animName], elapsed);
16089
+ }
16090
+ function resolveFrame(sheetUrls, frameDims, animState) {
16091
+ if (!sheetUrls) return null;
16092
+ const real = sheetUrls[animState.direction];
16093
+ const { sheetUrl, flipX } = real ? { sheetUrl: real, flipX: false } : (() => {
16094
+ const legacy = resolveSheetDirection(animState.direction);
16095
+ return { sheetUrl: sheetUrls[legacy.sheetDir], flipX: legacy.flipX };
16096
+ })();
16097
+ if (!sheetUrl) return null;
16098
+ const def = exports.SPRITE_SHEET_LAYOUT[animState.animation];
16099
+ const { frame } = getCurrentFrame(animState.animation, animState.elapsed);
16100
+ const rect = frameRect(frame, def.row, def.frames, frameDims.width, frameDims.height);
16101
+ return {
16102
+ sheetUrl,
16103
+ sx: rect.sx,
16104
+ sy: rect.sy,
16105
+ sw: rect.sw,
16106
+ sh: rect.sh,
16107
+ flipX
16108
+ };
16109
+ }
16110
+ function createUnitAnimationState(unitId) {
16111
+ return {
16112
+ unitId,
16113
+ animation: "idle",
16114
+ direction: "se",
16115
+ frame: 0,
16116
+ elapsed: 0,
16117
+ queuedAnimation: null,
16118
+ finished: false
16119
+ };
16120
+ }
16121
+ function transitionAnimation(state, newAnim, direction) {
16122
+ if (state.animation === "death" && state.finished) return state;
16123
+ if (state.animation === newAnim && exports.SPRITE_SHEET_LAYOUT[newAnim].loop) {
16124
+ return direction ? { ...state, direction } : state;
16125
+ }
16126
+ return {
16127
+ ...state,
16128
+ animation: newAnim,
16129
+ direction: direction ?? state.direction,
16130
+ frame: 0,
16131
+ elapsed: 0,
16132
+ queuedAnimation: null,
16133
+ finished: false
16134
+ };
16135
+ }
16136
+ function tickAnimationState(state, deltaMs) {
16137
+ const newElapsed = state.elapsed + deltaMs;
16138
+ const { frame, finished } = getCurrentFrame(state.animation, newElapsed);
16139
+ const def = exports.SPRITE_SHEET_LAYOUT[state.animation];
16140
+ if (finished && !def.loop && !state.finished) {
16141
+ if (state.animation === "death") {
16142
+ return { ...state, elapsed: newElapsed, frame, finished: true };
16143
+ }
16144
+ const nextAnim = state.queuedAnimation ?? "idle";
16145
+ return {
16146
+ ...state,
16147
+ animation: nextAnim,
16148
+ elapsed: 0,
16149
+ frame: 0,
16150
+ queuedAnimation: null,
16151
+ finished: false
16152
+ };
16153
+ }
16154
+ return { ...state, elapsed: newElapsed, frame, finished };
16155
+ }
16156
+ var init_spriteAnimation = __esm({
16157
+ "lib/spriteAnimation.ts"() {
16158
+ init_spriteSheetConstants();
16159
+ }
16160
+ });
16024
16161
  function warnMissingOnce(reason, node) {
16025
16162
  const key = `${reason}:${node.asset.url}:${String(node.asset.atlas)}:${String(node.asset.sprite)}`;
16026
16163
  if (loggedMissing.has(key)) return;
@@ -16038,6 +16175,9 @@ function paintFallbackSquare(painter, node, dctx, reason) {
16038
16175
  painter.strokeRect(rect.x, rect.y, rect.w, rect.h, "#5e564b", Math.max(1, tw / 32));
16039
16176
  painter.restore();
16040
16177
  }
16178
+ function isAnimatedSprite(node) {
16179
+ return node.animation !== void 0 && typeof node.asset?.atlas === "string";
16180
+ }
16041
16181
  function DrawSprite(_props) {
16042
16182
  return null;
16043
16183
  }
@@ -16046,6 +16186,7 @@ var init_DrawSprite = __esm({
16046
16186
  "components/game/atoms/DrawSprite.tsx"() {
16047
16187
  "use client";
16048
16188
  init_atlasSlice();
16189
+ init_spriteAnimation();
16049
16190
  init_imageCache();
16050
16191
  init_contract();
16051
16192
  spriteLog = logger.createLogger("almadar:ui:draw-sprite");
@@ -16059,6 +16200,23 @@ var init_DrawSprite = __esm({
16059
16200
  return;
16060
16201
  }
16061
16202
  let src = typeof node.frame === "object" ? node.frame : void 0;
16203
+ if (!src && node.animation !== void 0 && typeof node.asset.atlas === "string") {
16204
+ const atlas = getAtlas(node.asset.atlas, dctx.invalidate);
16205
+ if (!atlas) {
16206
+ if (atlasFailed(node.asset.atlas)) paintFallbackSquare(painter, node, dctx, "atlas-failed");
16207
+ return;
16208
+ }
16209
+ if (isSpriteSheetAtlas(atlas)) {
16210
+ const def = isAnimationName(node.animation) ? atlas.animations[node.animation] : void 0;
16211
+ if (!def) {
16212
+ paintFallbackSquare(painter, node, dctx, "sprite-missing");
16213
+ return;
16214
+ }
16215
+ const { frame } = getCurrentFrameFromDef(node.loop === void 0 ? def : { ...def, loop: node.loop }, dctx.time);
16216
+ const r = frameRect(frame, def.row, atlas.columns, atlas.frameWidth, atlas.frameHeight);
16217
+ src = { x: r.sx, y: r.sy, w: r.sw, h: r.sh };
16218
+ }
16219
+ }
16062
16220
  if (!src && isAtlasAsset(node.asset)) {
16063
16221
  const atlas = getAtlas(node.asset.atlas, dctx.invalidate);
16064
16222
  if (!atlas) {
@@ -16344,6 +16502,83 @@ var init_DrawText = __esm({
16344
16502
  };
16345
16503
  }
16346
16504
  });
16505
+ function isAnimatedGroup(node) {
16506
+ return Boolean(node.animation && node.animation.durationMs > 0 && node.animation.keyframes.length > 0);
16507
+ }
16508
+ function DrawGroup(props) {
16509
+ const register = React77.useContext(DrawableRegistryContext);
16510
+ if (register) register({ ...props, type: "draw-group" });
16511
+ return null;
16512
+ }
16513
+ var init_DrawGroup = __esm({
16514
+ "components/game/atoms/DrawGroup.tsx"() {
16515
+ "use client";
16516
+ init_registry();
16517
+ }
16518
+ });
16519
+ function applyMeshAnimation(node, timeMs) {
16520
+ const anim = node.animation;
16521
+ if (!anim || !(anim.durationMs > 0) || anim.keyframes.length === 0) return null;
16522
+ const frames = [...anim.keyframes].sort((a, b) => a.at - b.at);
16523
+ const cycle = timeMs / anim.durationMs;
16524
+ const t = anim.loop === false ? Math.min(cycle, 1) : cycle - Math.floor(cycle);
16525
+ const trackValue = (key) => {
16526
+ const defined = frames.filter((f3) => f3[key] !== void 0);
16527
+ if (defined.length === 0) return void 0;
16528
+ let prev;
16529
+ let next;
16530
+ for (const f3 of defined) {
16531
+ if (f3.at <= t) prev = f3;
16532
+ else if (!next) next = f3;
16533
+ }
16534
+ if (!prev) return defined[0][key];
16535
+ if (!next) return prev[key];
16536
+ const span = next.at - prev.at;
16537
+ const k = span > 0 ? (t - prev.at) / span : 1;
16538
+ const a = prev[key];
16539
+ const b = next[key];
16540
+ if (typeof a === "number" && typeof b === "number") return lerp2(a, b, k);
16541
+ return a;
16542
+ };
16543
+ const num2 = {};
16544
+ for (const key of NUMERIC_TRACKS2) {
16545
+ const v = trackValue(key);
16546
+ if (v !== void 0) num2[key] = v;
16547
+ }
16548
+ return {
16549
+ offset: [num2.offsetX ?? 0, num2.offsetY ?? 0, num2.offsetZ ?? 0],
16550
+ rotate: [num2.rotateX ?? 0, num2.rotateY ?? 0, num2.rotateZ ?? 0],
16551
+ scale: num2.scale ?? 1,
16552
+ opacity: num2.opacity,
16553
+ emissiveIntensity: num2.emissiveIntensity,
16554
+ color: trackValue("color"),
16555
+ emissive: trackValue("emissive")
16556
+ };
16557
+ }
16558
+ function DrawMesh(props) {
16559
+ const register = React77.useContext(DrawableRegistryContext);
16560
+ if (register) register({ ...props, type: "draw-mesh" });
16561
+ return null;
16562
+ }
16563
+ var lerp2, NUMERIC_TRACKS2;
16564
+ var init_DrawMesh = __esm({
16565
+ "components/game/atoms/DrawMesh.tsx"() {
16566
+ "use client";
16567
+ init_registry();
16568
+ lerp2 = (a, b, k) => a + (b - a) * k;
16569
+ NUMERIC_TRACKS2 = [
16570
+ "offsetX",
16571
+ "offsetY",
16572
+ "offsetZ",
16573
+ "rotateX",
16574
+ "rotateY",
16575
+ "rotateZ",
16576
+ "scale",
16577
+ "opacity",
16578
+ "emissiveIntensity"
16579
+ ];
16580
+ }
16581
+ });
16347
16582
 
16348
16583
  // components/game/molecules/DrawSpriteLayer.tsx
16349
16584
  function DrawSpriteLayer(_props) {
@@ -16410,18 +16645,22 @@ function paintDrawable(painter, node, dctx) {
16410
16645
  if (!isValidScenePos(node.position)) break;
16411
16646
  if (!Array.isArray(node.items)) break;
16412
16647
  const p = dctx.projector.project(node.position);
16648
+ const anim = dctx.time > 0 && isAnimatedGroup(node) ? applyMeshAnimation(node, dctx.time) : null;
16649
+ const tw = dctx.projector.tileWidth;
16413
16650
  painter.save();
16414
- painter.translate(p.x, p.y);
16415
- if (node.scale !== void 0) painter.scale(node.scale, node.scale);
16416
- if (node.rotate !== void 0) painter.rotate(node.rotate);
16417
- if (node.opacity !== void 0 && node.opacity !== 1) painter.setAlpha(node.opacity);
16651
+ painter.translate(p.x + (anim ? anim.offset[0] * tw : 0), p.y + (anim ? anim.offset[1] * tw : 0));
16652
+ const scale = (node.scale ?? 1) * (anim?.scale ?? 1);
16653
+ if (scale !== 1) painter.scale(scale, scale);
16654
+ const rotate = (node.rotate ?? 0) + (node.rotation?.[2] ?? 0) + (anim?.rotate[2] ?? 0);
16655
+ if (rotate !== 0) painter.rotate(rotate);
16656
+ const opacity = (node.opacity ?? 1) * (anim?.opacity ?? 1);
16657
+ if (opacity !== 1) painter.setAlpha(opacity);
16418
16658
  if (node.clip) {
16419
- const tw = dctx.projector.tileWidth;
16420
16659
  painter.scale(tw, tw);
16421
16660
  painter.clipPath(node.clip);
16422
16661
  painter.scale(1 / tw, 1 / tw);
16423
16662
  }
16424
- const childCtx = node.scale !== void 0 && node.scale !== 1 ? { ...dctx, groupScale: (dctx.groupScale ?? 1) * node.scale } : dctx;
16663
+ const childCtx = scale !== 1 ? { ...dctx, groupScale: (dctx.groupScale ?? 1) * scale } : dctx;
16425
16664
  for (const item of node.items) paintDrawable(painter, item, childCtx);
16426
16665
  painter.restore();
16427
16666
  break;
@@ -16447,6 +16686,8 @@ var init_paintDispatch = __esm({
16447
16686
  init_DrawSprite();
16448
16687
  init_DrawShape();
16449
16688
  init_DrawText();
16689
+ init_DrawGroup();
16690
+ init_DrawMesh();
16450
16691
  init_DrawSpriteLayer();
16451
16692
  init_DrawShapeLayer();
16452
16693
  init_DrawTextLayer();
@@ -16693,8 +16934,11 @@ function Canvas2D({
16693
16934
  const miniMapHeight = gridExtent.height || 10;
16694
16935
  const drawableIsAnimated = (node) => {
16695
16936
  if (node.type === "draw-shape") return isAnimatedShape(node);
16696
- if (node.type === "draw-group") return Array.isArray(node.items) && node.items.some(drawableIsAnimated);
16937
+ if (node.type === "draw-sprite") return isAnimatedSprite(node);
16938
+ if (node.type === "draw-group")
16939
+ return isAnimatedGroup(node) || Array.isArray(node.items) && node.items.some(drawableIsAnimated);
16697
16940
  if (node.type === "draw-shape-layer") return Array.isArray(node.items) && node.items.some(isAnimatedShape);
16941
+ if (node.type === "draw-sprite-layer") return Array.isArray(node.items) && node.items.some(isAnimatedSprite);
16698
16942
  return false;
16699
16943
  };
16700
16944
  const animRafRef = React77.useRef(0);
@@ -17035,6 +17279,8 @@ var init_Canvas2D = __esm({
17035
17279
  init_projector();
17036
17280
  init_paintDispatch();
17037
17281
  init_DrawShape();
17282
+ init_DrawSprite();
17283
+ init_DrawGroup();
17038
17284
  init_registry();
17039
17285
  init_hitTest();
17040
17286
  init_isometric();
@@ -17107,6 +17353,7 @@ function Canvas({
17107
17353
  cameraMode: to3DCameraMode(camera?.mode),
17108
17354
  ...zoom !== void 0 ? { scale: zoom } : {},
17109
17355
  ...camera?.fov !== void 0 ? { fov: camera.fov } : {},
17356
+ ...camera?.azimuth !== void 0 ? { azimuth: camera.azimuth } : {},
17110
17357
  ...camera?.target !== void 0 ? { followTarget: camera.target } : {},
17111
17358
  unitScale,
17112
17359
  backgroundColor,
@@ -20562,6 +20809,14 @@ var init_DashboardLayout = __esm({
20562
20809
  NavLinkBottom.displayName = "NavLinkBottom";
20563
20810
  }
20564
20811
  });
20812
+ function downloadItemUrl(url, label) {
20813
+ const a = document.createElement("a");
20814
+ a.href = url;
20815
+ a.download = url.startsWith("data:") ? label : url.split("/").pop() ?? "download";
20816
+ document.body.appendChild(a);
20817
+ a.click();
20818
+ a.remove();
20819
+ }
20565
20820
  function computeMenuStyle(position, triggerRect) {
20566
20821
  const isTop = position.startsWith("top");
20567
20822
  const isRight = position.endsWith("right") || position.endsWith("end");
@@ -20614,6 +20869,7 @@ function SubMenu({
20614
20869
  onClick: () => {
20615
20870
  if (item.disabled) return;
20616
20871
  if (item.event) eventBus.emit(`UI:${item.event}`, { itemId, label: item.label });
20872
+ if (item.url) downloadItemUrl(item.url, item.label);
20617
20873
  item.onClick?.();
20618
20874
  },
20619
20875
  "aria-disabled": item.disabled || void 0,
@@ -20767,6 +21023,7 @@ var init_Menu = __esm({
20767
21023
  setActiveSubMenu(itemId);
20768
21024
  } else {
20769
21025
  if (item.event) eventBus.emit(`UI:${item.event}`, { itemId, label: item.label });
21026
+ if (item.url) downloadItemUrl(item.url, item.label);
20770
21027
  item.onClick?.();
20771
21028
  setIsOpen(false);
20772
21029
  }
@@ -20802,12 +21059,19 @@ var init_Menu = __esm({
20802
21059
  "bottom-end": "bottom-start"
20803
21060
  };
20804
21061
  const effectivePosition = direction === "rtl" ? rtlMirror[position] ?? position : position;
20805
- const triggerChild = React77__namespace.default.isValidElement(trigger) ? trigger : /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", as: "span", children: trigger });
20806
- const triggerElement = React77__namespace.default.cloneElement(
20807
- triggerChild,
21062
+ const triggerElement = React77__namespace.default.isValidElement(trigger) ? React77__namespace.default.cloneElement(trigger, {
21063
+ ref: triggerRef,
21064
+ onClick: handleToggle
21065
+ }) : /* @__PURE__ */ jsxRuntime.jsx(
21066
+ exports.Box,
20808
21067
  {
20809
- ref: triggerRef,
20810
- onClick: handleToggle
21068
+ as: "span",
21069
+ ref: (el) => {
21070
+ triggerRef.current = el;
21071
+ },
21072
+ onClick: handleToggle,
21073
+ className: "inline-flex",
21074
+ children: typeof trigger === "string" || typeof trigger === "number" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", as: "span", children: trigger }) : trigger
20811
21075
  }
20812
21076
  );
20813
21077
  const renderMenuItems = (menuItems) => menuItems.map((item, index) => {
@@ -26492,134 +26756,6 @@ var init_makeAsset = __esm({
26492
26756
  }
26493
26757
  });
26494
26758
 
26495
- // lib/spriteSheetConstants.ts
26496
- exports.SHEET_COLUMNS = void 0; exports.SPRITE_SHEET_LAYOUT = void 0;
26497
- var init_spriteSheetConstants = __esm({
26498
- "lib/spriteSheetConstants.ts"() {
26499
- exports.SHEET_COLUMNS = 8;
26500
- exports.SPRITE_SHEET_LAYOUT = {
26501
- idle: { row: 0, frames: 4, frameRate: 6, loop: true },
26502
- walk: { row: 1, frames: 8, frameRate: 10, loop: true },
26503
- attack: { row: 2, frames: 6, frameRate: 12, loop: false },
26504
- hit: { row: 3, frames: 3, frameRate: 8, loop: false },
26505
- death: { row: 4, frames: 6, frameRate: 8, loop: false }
26506
- };
26507
- }
26508
- });
26509
-
26510
- // lib/spriteAnimation.ts
26511
- function inferDirection(dx, dy) {
26512
- if (dx === 0 && dy === 0) return "se";
26513
- if (dx >= 0 && dy >= 0) return "se";
26514
- if (dx <= 0 && dy >= 0) return "sw";
26515
- if (dx >= 0 && dy <= 0) return "ne";
26516
- return "nw";
26517
- }
26518
- function resolveSheetDirection(facing) {
26519
- switch (facing) {
26520
- case "se":
26521
- return { sheetDir: "se", flipX: false };
26522
- case "sw":
26523
- return { sheetDir: "sw", flipX: false };
26524
- case "ne":
26525
- return { sheetDir: "sw", flipX: true };
26526
- case "nw":
26527
- return { sheetDir: "se", flipX: true };
26528
- }
26529
- }
26530
- function frameRect(frame, row, columns, frameWidth, frameHeight) {
26531
- return {
26532
- sx: frame % columns * frameWidth,
26533
- sy: row * frameHeight,
26534
- sw: frameWidth,
26535
- sh: frameHeight
26536
- };
26537
- }
26538
- function getCurrentFrameFromDef(def, elapsed) {
26539
- const frameDuration = 1e3 / def.frameRate;
26540
- const totalDuration = def.frames * frameDuration;
26541
- if (def.loop) {
26542
- const frame2 = Math.floor(elapsed % totalDuration / frameDuration);
26543
- return { frame: frame2, finished: false };
26544
- }
26545
- if (elapsed >= totalDuration) {
26546
- return { frame: def.frames - 1, finished: true };
26547
- }
26548
- const frame = Math.floor(elapsed / frameDuration);
26549
- return { frame, finished: false };
26550
- }
26551
- function getCurrentFrame(animName, elapsed) {
26552
- return getCurrentFrameFromDef(exports.SPRITE_SHEET_LAYOUT[animName], elapsed);
26553
- }
26554
- function resolveFrame(sheetUrls, frameDims, animState) {
26555
- if (!sheetUrls) return null;
26556
- const { sheetDir, flipX } = resolveSheetDirection(animState.direction);
26557
- const sheetUrl = sheetUrls[sheetDir];
26558
- if (!sheetUrl) return null;
26559
- const def = exports.SPRITE_SHEET_LAYOUT[animState.animation];
26560
- const { frame } = getCurrentFrame(animState.animation, animState.elapsed);
26561
- const rect = frameRect(frame, def.row, def.frames, frameDims.width, frameDims.height);
26562
- return {
26563
- sheetUrl,
26564
- sx: rect.sx,
26565
- sy: rect.sy,
26566
- sw: rect.sw,
26567
- sh: rect.sh,
26568
- flipX
26569
- };
26570
- }
26571
- function createUnitAnimationState(unitId) {
26572
- return {
26573
- unitId,
26574
- animation: "idle",
26575
- direction: "se",
26576
- frame: 0,
26577
- elapsed: 0,
26578
- queuedAnimation: null,
26579
- finished: false
26580
- };
26581
- }
26582
- function transitionAnimation(state, newAnim, direction) {
26583
- if (state.animation === "death" && state.finished) return state;
26584
- if (state.animation === newAnim && exports.SPRITE_SHEET_LAYOUT[newAnim].loop) {
26585
- return direction ? { ...state, direction } : state;
26586
- }
26587
- return {
26588
- ...state,
26589
- animation: newAnim,
26590
- direction: direction ?? state.direction,
26591
- frame: 0,
26592
- elapsed: 0,
26593
- queuedAnimation: null,
26594
- finished: false
26595
- };
26596
- }
26597
- function tickAnimationState(state, deltaMs) {
26598
- const newElapsed = state.elapsed + deltaMs;
26599
- const { frame, finished } = getCurrentFrame(state.animation, newElapsed);
26600
- const def = exports.SPRITE_SHEET_LAYOUT[state.animation];
26601
- if (finished && !def.loop && !state.finished) {
26602
- if (state.animation === "death") {
26603
- return { ...state, elapsed: newElapsed, frame, finished: true };
26604
- }
26605
- const nextAnim = state.queuedAnimation ?? "idle";
26606
- return {
26607
- ...state,
26608
- animation: nextAnim,
26609
- elapsed: 0,
26610
- frame: 0,
26611
- queuedAnimation: null,
26612
- finished: false
26613
- };
26614
- }
26615
- return { ...state, elapsed: newElapsed, frame, finished };
26616
- }
26617
- var init_spriteAnimation = __esm({
26618
- "lib/spriteAnimation.ts"() {
26619
- init_spriteSheetConstants();
26620
- }
26621
- });
26622
-
26623
26759
  // lib/gameShared.ts
26624
26760
  var init_gameShared = __esm({
26625
26761
  "lib/gameShared.ts"() {
@@ -40349,28 +40485,6 @@ var init_DetailPanel = __esm({
40349
40485
  exports.DetailPanel.displayName = "DetailPanel";
40350
40486
  }
40351
40487
  });
40352
- function DrawGroup(props) {
40353
- const register = React77.useContext(DrawableRegistryContext);
40354
- if (register) register({ ...props, type: "draw-group" });
40355
- return null;
40356
- }
40357
- var init_DrawGroup = __esm({
40358
- "components/game/atoms/DrawGroup.tsx"() {
40359
- "use client";
40360
- init_registry();
40361
- }
40362
- });
40363
- function DrawMesh(props) {
40364
- const register = React77.useContext(DrawableRegistryContext);
40365
- if (register) register({ ...props, type: "draw-mesh" });
40366
- return null;
40367
- }
40368
- var init_DrawMesh = __esm({
40369
- "components/game/atoms/DrawMesh.tsx"() {
40370
- "use client";
40371
- init_registry();
40372
- }
40373
- });
40374
40488
  function extractTitle(children) {
40375
40489
  if (!React77__namespace.default.isValidElement(children)) return void 0;
40376
40490
  const props = children.props;
@@ -6,11 +6,11 @@ import { LucideIcon } from 'lucide-react';
6
6
  import { C as ColorToken, U as UiError, P as Point, L as LinkAction, I as ImageSource } from '../GameAudioProvider-CPGwD49P.cjs';
7
7
  export { A as AudioManifest, G as GameAudioContext, a as GameAudioContextValue, e as GameAudioControls, b as GameAudioProvider, c as GameAudioProviderProps, R as Rect, S as SoundEntry, f as UseGameAudioOptions, g as useGameAudio, u as useGameAudioContext } from '../GameAudioProvider-CPGwD49P.cjs';
8
8
  import { SExpr } from '@almadar/evaluator';
9
- import { U as UnitAnimationState, F as FacingDirection, S as SpriteSheetUrls, g as SpriteFrameDims, R as ResolvedFrame, h as CanvasLighting, i as CanvasPost, a as IsometricUnit, j as CameraState, k as FieldInfo, l as OrbitalTraitInfo, m as OrbitalPageInfo, T as TraitLevelData, E as ExternalLink } from '../avl-schema-parser-wOkfbwAh.cjs';
10
- export { B as BoardTile, G as GameAction, n as GamePhase, o as GameState, p as GameUnit, b as IsometricFeature, I as IsometricTile, P as Position, q as UnitTrait, r as calculateAttackTargets, s as calculateValidMoves, t as createInitialGameState } from '../avl-schema-parser-wOkfbwAh.cjs';
11
- import { D as DrawableNode } from '../paintDispatch-BQn5Lyx1.cjs';
12
- import { C as ContentSegment, b as DomLayoutData, d as DomStateNode, V as VisualizerConfig, e as DomTransitionLabel } from '../cn-rwc3svaX.cjs';
13
- export { n as cn } from '../cn-rwc3svaX.cjs';
9
+ import { U as UnitAnimationState, F as FacingDirection, S as SpriteSheetUrls, g as SpriteFrameDims, R as ResolvedFrame, h as CanvasLighting, i as CanvasPost, a as IsometricUnit, j as CameraState, k as FieldInfo, l as OrbitalTraitInfo, m as OrbitalPageInfo, T as TraitLevelData, E as ExternalLink } from '../avl-schema-parser-DVmrgdwc.cjs';
10
+ export { B as BoardTile, G as GameAction, n as GamePhase, o as GameState, p as GameUnit, b as IsometricFeature, I as IsometricTile, P as Position, q as UnitTrait, r as calculateAttackTargets, s as calculateValidMoves, t as createInitialGameState } from '../avl-schema-parser-DVmrgdwc.cjs';
11
+ import { D as DrawableNode } from '../paintDispatch-Cb_hQj4Y.cjs';
12
+ import { C as ContentSegment, b as DomLayoutData, d as DomStateNode, V as VisualizerConfig, e as DomTransitionLabel } from '../cn-BdGFhYe3.cjs';
13
+ export { n as cn } from '../cn-BdGFhYe3.cjs';
14
14
  import { b as SlotContent } from '../useUISlots-BesZYMks.cjs';
15
15
  export { D as DEFAULT_SLOTS, S as SlotAnimation, a as SlotChangeCallback, c as SlotRenderConfig, U as UISlotManager, u as useUISlotManager } from '../useUISlots-BesZYMks.cjs';
16
16
  export { ALMADAR_DND_MIME, AuthContextValue, AuthUser, CanvasGestureCallbacks, CanvasGestureHandlers, CompileResult, CompileStage, DragReorderResult, DraggablePayload, Extension, ExtensionManifest, FileSystemFile, FileSystemStatus, GitHubRepo, GitHubStatus, HistoryChangeSummary, HistoryTimelineItem, I18nContextValue, I18nProvider, InfiniteScrollOptions, InfiniteScrollResult, LongPressHandlers, LongPressOptions, OpenFile, Positioned, PullToRefreshOptions, PullToRefreshResult, QuerySingletonEntity, QuerySingletonResult, QuerySingletonState, QueryState, RenderInterpolationHandle, RenderInterpolationOptions, RevertResult, SelectedFile, SharedEntityStore, SharedEntityStoreContext, SharedEntitySubscriber, SharedEntityWriter, SwipeGestureOptions, SwipeGestureResult, SwipeHandlers, TapRevealOptions, TapRevealResult, TraitListenSpec, TranslateFunction, UseCanvasGesturesOptions, UseCompileResult, UseDraggableOptions, UseDraggableResult, UseDropZoneOptions, UseDropZoneResult, UseExtensionsOptions, UseExtensionsResult, UseFileEditorOptions, UseFileEditorResult, UseFileSystemResult, UseOrbitalHistoryOptions, UseOrbitalHistoryResult, createSharedEntityStore, createTranslate, parseQueryBinding, runTickFrame, useAgentChat, useAuthContext, useCanvasGestures, useCompile, useConnectGitHub, useDeepAgentGeneration, useDisconnectGitHub, useDragReorder, useDraggable, useDropZone, useExtensions, useFileEditor, useFileSystem, useGitHubBranches, useGitHubRepo, useGitHubRepos, useGitHubStatus, useInfiniteScroll, useLongPress, useMediaQuery, useOrbitalHistory, usePreview, usePullToRefresh, useQuerySingleton, useRenderInterpolation, useSharedEntitySnapshot, useSharedEntityStore, useSharedEntityStoreContext, useSwipeGesture, useTapReveal, useTraitListens, useTranslate, useUIEvents, useValidation } from '../hooks/index.cjs';
@@ -4367,6 +4367,8 @@ interface MenuItem {
4367
4367
  onClick?: () => void;
4368
4368
  /** Event name for pattern compatibility */
4369
4369
  event?: string;
4370
+ /** File URL this item downloads on pick (gesture-driven, `DocumentViewer` precedent). The item's `event` still emits on the bus. */
4371
+ url?: string;
4370
4372
  /** Variant for styling (pattern compatibility) */
4371
4373
  variant?: "default" | "danger";
4372
4374
  /** Sub-menu items */
@@ -6519,7 +6521,7 @@ interface DataGridProps extends DataDndProps {
6519
6521
  */
6520
6522
  look?: "dense" | "spacious" | "striped" | "borderless" | "card-rows";
6521
6523
  }
6522
- declare function DataGrid({ entity, fields, columns, itemActions, maxInlineActions, scrollX, cols, gap, minCardWidth, className, isLoading, error, imageField, selectable, selectionEvent, infiniteScroll, loadMoreEvent, hasMore, children, pageSize, renderItem: schemaRenderItem, dragGroup, accepts, sortable, dropEvent, reorderEvent, positionEvent, dndItemIdField, dndRoot, look, }: DataGridProps): string | number | bigint | boolean | React__default.JSX.Element | Iterable<React__default.ReactNode> | Promise<string | number | bigint | boolean | React__default.ReactPortal | React__default.ReactElement<unknown, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | null | undefined> | null | undefined;
6524
+ declare function DataGrid({ entity, fields, columns, itemActions, maxInlineActions, scrollX, cols, gap, minCardWidth, className, isLoading, error, imageField, selectable, selectionEvent, infiniteScroll, loadMoreEvent, hasMore, children, pageSize, renderItem: schemaRenderItem, dragGroup, accepts, sortable, dropEvent, reorderEvent, positionEvent, dndItemIdField, dndRoot, look, }: DataGridProps): string | number | bigint | boolean | Iterable<React__default.ReactNode> | Promise<string | number | bigint | boolean | React__default.ReactPortal | React__default.ReactElement<unknown, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | null | undefined> | React__default.JSX.Element | null | undefined;
6523
6525
  declare namespace DataGrid {
6524
6526
  var displayName: string;
6525
6527
  }
@@ -6672,7 +6674,7 @@ interface DataListProps extends DataDndProps {
6672
6674
  */
6673
6675
  look?: "dense" | "spacious" | "striped" | "borderless" | "card-rows";
6674
6676
  }
6675
- declare function DataList({ entity, fields, columns, itemActions, maxInlineActions, itemClickEvent, gap, variant, groupBy, senderField, senderLabelField, currentUser, emptyMessage, className, isLoading, error, reorderable: _reorderable, reorderEvent: _reorderEvent, swipeLeftEvent: _swipeLeftEvent, swipeLeftActions: _swipeLeftActions, swipeRightEvent: _swipeRightEvent, swipeRightActions: _swipeRightActions, longPressEvent: _longPressEvent, infiniteScroll, loadMoreEvent, hasMore, children, pageSize, sortBy, sortDirection, renderItem: schemaRenderItem, dragGroup, accepts, sortable: sortableProp, dropEvent, reorderEvent: dndReorderEvent, positionEvent, dndItemIdField, dndRoot, look, }: DataListProps): string | number | bigint | boolean | React__default.JSX.Element | Iterable<React__default.ReactNode> | Promise<string | number | bigint | boolean | React__default.ReactPortal | React__default.ReactElement<unknown, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | null | undefined> | null | undefined;
6677
+ declare function DataList({ entity, fields, columns, itemActions, maxInlineActions, itemClickEvent, gap, variant, groupBy, senderField, senderLabelField, currentUser, emptyMessage, className, isLoading, error, reorderable: _reorderable, reorderEvent: _reorderEvent, swipeLeftEvent: _swipeLeftEvent, swipeLeftActions: _swipeLeftActions, swipeRightEvent: _swipeRightEvent, swipeRightActions: _swipeRightActions, longPressEvent: _longPressEvent, infiniteScroll, loadMoreEvent, hasMore, children, pageSize, sortBy, sortDirection, renderItem: schemaRenderItem, dragGroup, accepts, sortable: sortableProp, dropEvent, reorderEvent: dndReorderEvent, positionEvent, dndItemIdField, dndRoot, look, }: DataListProps): string | number | bigint | boolean | Iterable<React__default.ReactNode> | Promise<string | number | bigint | boolean | React__default.ReactPortal | React__default.ReactElement<unknown, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | null | undefined> | React__default.JSX.Element | null | undefined;
6676
6678
  declare namespace DataList {
6677
6679
  var displayName: string;
6678
6680
  }