@almadar/ui 5.143.0 → 5.145.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.
@@ -1408,6 +1408,10 @@ var init_Icon = __esm({
1408
1408
  function isTilesheet(a) {
1409
1409
  return typeof a.tileWidth === "number";
1410
1410
  }
1411
+ function isSpriteSheetAtlas(a) {
1412
+ const s = a;
1413
+ return typeof s.frameWidth === "number" && typeof s.frameHeight === "number" && typeof s.animations === "object";
1414
+ }
1411
1415
  function getAtlas(url, onReady) {
1412
1416
  if (atlasCache.has(url)) return atlasCache.get(url) ?? void 0;
1413
1417
  atlasCache.set(url, void 0);
@@ -1447,6 +1451,7 @@ function subRectFor(atlas, sprite) {
1447
1451
  sh: atlas.tileHeight
1448
1452
  };
1449
1453
  }
1454
+ if (isSpriteSheetAtlas(atlas)) return null;
1450
1455
  const st = atlas.subTextures[sprite];
1451
1456
  if (!st) return null;
1452
1457
  return { sx: st.x, sy: st.y, sw: st.width, sh: st.height };
@@ -1756,7 +1761,7 @@ var init_Button = __esm({
1756
1761
  "data-testid": dataTestId ?? (action ? `action-${action}` : void 0),
1757
1762
  children: [
1758
1763
  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 }),
1759
- children || label,
1764
+ (Array.isArray(children) ? children.length > 0 : Boolean(children)) ? children : label,
1760
1765
  resolvedRightIcon && !isLoading && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", children: resolvedRightIcon })
1761
1766
  ]
1762
1767
  }
@@ -7761,6 +7766,34 @@ var init_isometric = __esm({
7761
7766
  };
7762
7767
  }
7763
7768
  });
7769
+ function isAnimationName(name) {
7770
+ return core.ANIMATION_NAMES.includes(name);
7771
+ }
7772
+ function frameRect(frame, row, columns, frameWidth, frameHeight) {
7773
+ return {
7774
+ sx: frame % columns * frameWidth,
7775
+ sy: row * frameHeight,
7776
+ sw: frameWidth,
7777
+ sh: frameHeight
7778
+ };
7779
+ }
7780
+ function getCurrentFrameFromDef(def, elapsed) {
7781
+ const frameDuration = 1e3 / def.frameRate;
7782
+ const totalDuration = def.frames * frameDuration;
7783
+ if (def.loop) {
7784
+ const frame2 = Math.floor(elapsed % totalDuration / frameDuration);
7785
+ return { frame: frame2, finished: false };
7786
+ }
7787
+ if (elapsed >= totalDuration) {
7788
+ return { frame: def.frames - 1, finished: true };
7789
+ }
7790
+ const frame = Math.floor(elapsed / frameDuration);
7791
+ return { frame, finished: false };
7792
+ }
7793
+ var init_spriteAnimation = __esm({
7794
+ "lib/spriteAnimation.ts"() {
7795
+ }
7796
+ });
7764
7797
 
7765
7798
  // lib/gameShared.ts
7766
7799
  var init_gameShared = __esm({
@@ -9514,6 +9547,9 @@ function paintFallbackSquare(painter, node, dctx, reason) {
9514
9547
  painter.strokeRect(rect.x, rect.y, rect.w, rect.h, "#5e564b", Math.max(1, tw / 32));
9515
9548
  painter.restore();
9516
9549
  }
9550
+ function isAnimatedSprite(node) {
9551
+ return node.animation !== void 0 && typeof node.asset?.atlas === "string";
9552
+ }
9517
9553
  function DrawSprite(_props) {
9518
9554
  return null;
9519
9555
  }
@@ -9522,6 +9558,7 @@ var init_DrawSprite = __esm({
9522
9558
  "components/game/atoms/DrawSprite.tsx"() {
9523
9559
  "use client";
9524
9560
  init_atlasSlice();
9561
+ init_spriteAnimation();
9525
9562
  init_imageCache();
9526
9563
  init_contract();
9527
9564
  spriteLog = logger.createLogger("almadar:ui:draw-sprite");
@@ -9535,6 +9572,23 @@ var init_DrawSprite = __esm({
9535
9572
  return;
9536
9573
  }
9537
9574
  let src = typeof node.frame === "object" ? node.frame : void 0;
9575
+ if (!src && node.animation !== void 0 && typeof node.asset.atlas === "string") {
9576
+ const atlas = getAtlas(node.asset.atlas, dctx.invalidate);
9577
+ if (!atlas) {
9578
+ if (atlasFailed(node.asset.atlas)) paintFallbackSquare(painter, node, dctx, "atlas-failed");
9579
+ return;
9580
+ }
9581
+ if (isSpriteSheetAtlas(atlas)) {
9582
+ const def = isAnimationName(node.animation) ? atlas.animations[node.animation] : void 0;
9583
+ if (!def) {
9584
+ paintFallbackSquare(painter, node, dctx, "sprite-missing");
9585
+ return;
9586
+ }
9587
+ const { frame } = getCurrentFrameFromDef(node.loop === void 0 ? def : { ...def, loop: node.loop }, dctx.time);
9588
+ const r = frameRect(frame, def.row, atlas.columns, atlas.frameWidth, atlas.frameHeight);
9589
+ src = { x: r.sx, y: r.sy, w: r.sw, h: r.sh };
9590
+ }
9591
+ }
9538
9592
  if (!src && isAtlasAsset(node.asset)) {
9539
9593
  const atlas = getAtlas(node.asset.atlas, dctx.invalidate);
9540
9594
  if (!atlas) {
@@ -10258,9 +10312,11 @@ function Canvas2D({
10258
10312
  const miniMapHeight = gridExtent.height || 10;
10259
10313
  const drawableIsAnimated = (node) => {
10260
10314
  if (node.type === "draw-shape") return isAnimatedShape(node);
10315
+ if (node.type === "draw-sprite") return isAnimatedSprite(node);
10261
10316
  if (node.type === "draw-group")
10262
10317
  return isAnimatedGroup(node) || Array.isArray(node.items) && node.items.some(drawableIsAnimated);
10263
10318
  if (node.type === "draw-shape-layer") return Array.isArray(node.items) && node.items.some(isAnimatedShape);
10319
+ if (node.type === "draw-sprite-layer") return Array.isArray(node.items) && node.items.some(isAnimatedSprite);
10264
10320
  return false;
10265
10321
  };
10266
10322
  const animRafRef = React85.useRef(0);
@@ -10601,6 +10657,7 @@ var init_Canvas2D = __esm({
10601
10657
  init_projector();
10602
10658
  init_paintDispatch();
10603
10659
  init_DrawShape();
10660
+ init_DrawSprite();
10604
10661
  init_DrawGroup();
10605
10662
  init_registry();
10606
10663
  init_hitTest();
@@ -10674,6 +10731,7 @@ function Canvas({
10674
10731
  cameraMode: to3DCameraMode(camera?.mode),
10675
10732
  ...zoom !== void 0 ? { scale: zoom } : {},
10676
10733
  ...camera?.fov !== void 0 ? { fov: camera.fov } : {},
10734
+ ...camera?.azimuth !== void 0 ? { azimuth: camera.azimuth } : {},
10677
10735
  ...camera?.target !== void 0 ? { followTarget: camera.target } : {},
10678
10736
  unitScale,
10679
10737
  backgroundColor,
@@ -22176,7 +22234,7 @@ var init_DashboardLayout = __esm({
22176
22234
  }, [isMobile, sidebarOpen]);
22177
22235
  const location = reactRouterDom.useLocation();
22178
22236
  const ctxPagePath = providers.useCurrentPagePath();
22179
- const activePath = currentPath ?? ctxPagePath ?? location.pathname;
22237
+ const activePath = (currentPath || void 0) ?? ctxPagePath ?? location.pathname;
22180
22238
  const { signOut: authSignOut } = useAuthContext();
22181
22239
  const user = userProp || (null);
22182
22240
  const { t } = hooks.useTranslate();
@@ -22639,6 +22697,14 @@ var init_DashboardLayout = __esm({
22639
22697
  NavLinkBottom.displayName = "NavLinkBottom";
22640
22698
  }
22641
22699
  });
22700
+ function downloadItemUrl(url, label) {
22701
+ const a = document.createElement("a");
22702
+ a.href = url;
22703
+ a.download = url.startsWith("data:") ? label : url.split("/").pop() ?? "download";
22704
+ document.body.appendChild(a);
22705
+ a.click();
22706
+ a.remove();
22707
+ }
22642
22708
  function computeMenuStyle(position, triggerRect) {
22643
22709
  const isTop = position.startsWith("top");
22644
22710
  const isRight = position.endsWith("right") || position.endsWith("end");
@@ -22691,6 +22757,7 @@ function SubMenu({
22691
22757
  onClick: () => {
22692
22758
  if (item.disabled) return;
22693
22759
  if (item.event) eventBus.emit(`UI:${item.event}`, { itemId, label: item.label });
22760
+ if (item.url) downloadItemUrl(item.url, item.label);
22694
22761
  item.onClick?.();
22695
22762
  },
22696
22763
  "aria-disabled": item.disabled || void 0,
@@ -22844,6 +22911,7 @@ var init_Menu = __esm({
22844
22911
  setActiveSubMenu(itemId);
22845
22912
  } else {
22846
22913
  if (item.event) eventBus.emit(`UI:${item.event}`, { itemId, label: item.label });
22914
+ if (item.url) downloadItemUrl(item.url, item.label);
22847
22915
  item.onClick?.();
22848
22916
  setIsOpen(false);
22849
22917
  }
@@ -22879,12 +22947,19 @@ var init_Menu = __esm({
22879
22947
  "bottom-end": "bottom-start"
22880
22948
  };
22881
22949
  const effectivePosition = direction === "rtl" ? rtlMirror[position] ?? position : position;
22882
- const triggerChild = React85__namespace.default.isValidElement(trigger) ? trigger : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", as: "span", children: trigger });
22883
- const triggerElement = React85__namespace.default.cloneElement(
22884
- triggerChild,
22950
+ const triggerElement = React85__namespace.default.isValidElement(trigger) ? React85__namespace.default.cloneElement(trigger, {
22951
+ ref: triggerRef,
22952
+ onClick: handleToggle
22953
+ }) : /* @__PURE__ */ jsxRuntime.jsx(
22954
+ Box,
22885
22955
  {
22886
- ref: triggerRef,
22887
- onClick: handleToggle
22956
+ as: "span",
22957
+ ref: (el) => {
22958
+ triggerRef.current = el;
22959
+ },
22960
+ onClick: handleToggle,
22961
+ className: "inline-flex",
22962
+ children: typeof trigger === "string" || typeof trigger === "number" ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", as: "span", children: trigger }) : trigger
22888
22963
  }
22889
22964
  );
22890
22965
  const renderMenuItems = (menuItems) => menuItems.map((item, index) => {
@@ -47614,6 +47689,11 @@ function OrbPreview({
47614
47689
  setRouteParams(initialPageMatch?.params ?? {});
47615
47690
  }
47616
47691
  }, [initialPagePath, initialPageName, initialPageMatch]);
47692
+ const currentPagePath = React85.useMemo(() => {
47693
+ const activePageName = currentPage ?? pages[0]?.page.name;
47694
+ const resolved = pages.find((p) => p.page.name === activePageName)?.page.path;
47695
+ return resolved ?? initialPagePath;
47696
+ }, [pages, currentPage, initialPagePath]);
47617
47697
  const handleNavigate = React85.useCallback((path) => {
47618
47698
  const hit = providers.matchPathAmong(pages, path, (entry) => entry.page.path);
47619
47699
  const match = hit?.candidate;
@@ -47687,7 +47767,7 @@ function OrbPreview({
47687
47767
  style: { height },
47688
47768
  children: [
47689
47769
  localFallback && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "px-3 py-2 bg-[var(--color-warning)] bg-opacity-10 border-b border-[var(--color-warning)] flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-[var(--color-warning-foreground)] flex-1", children: "Preview server unreachable \u2014 running locally. Server-side state and persistence are disabled." }) }),
47690
- /* @__PURE__ */ jsxRuntime.jsx(providers.OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, isolated, children: /* @__PURE__ */ jsxRuntime.jsx(context.UISlotProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(
47770
+ /* @__PURE__ */ jsxRuntime.jsx(providers.CurrentPagePathProvider, { value: currentPagePath, children: /* @__PURE__ */ jsxRuntime.jsx(providers.OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, isolated, children: /* @__PURE__ */ jsxRuntime.jsx(context.UISlotProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(
47691
47771
  SchemaRunner,
47692
47772
  {
47693
47773
  schema: parseResult.schema,
@@ -47700,7 +47780,7 @@ function OrbPreview({
47700
47780
  onLocalFallback: handleLocalFallback,
47701
47781
  persistence
47702
47782
  }
47703
- ) }) })
47783
+ ) }) }) })
47704
47784
  ]
47705
47785
  }
47706
47786
  );
@@ -1,10 +1,10 @@
1
1
  import * as React85 from 'react';
2
2
  import React85__default, { createContext, useContext, useMemo, useRef, useEffect, useCallback, Suspense, useState, useSyncExternalStore, useLayoutEffect, lazy, useId } from 'react';
3
- import { EventBusContext, useTraitScopeChain, useEntitySchemaOptional, useEntityBindingSnapshot, useTraitScope, useEntitySchema, getAllPages, matchPathAmong, OrbitalProvider, TraitScopeProvider, ServerBridgeProvider, useCurrentPagePath, useGameAudioContextOptional, VerificationProvider, EntitySchemaProvider, OrbitalThemeProvider, useServerBridge, EntityBindingContext } from '@almadar/ui/providers';
3
+ import { EventBusContext, useTraitScopeChain, useEntitySchemaOptional, useEntityBindingSnapshot, useTraitScope, useEntitySchema, getAllPages, matchPathAmong, CurrentPagePathProvider, OrbitalProvider, TraitScopeProvider, ServerBridgeProvider, useCurrentPagePath, useGameAudioContextOptional, VerificationProvider, EntitySchemaProvider, OrbitalThemeProvider, useServerBridge, EntityBindingContext } from '@almadar/ui/providers';
4
4
  export { EntitySchemaProvider, ServerBridgeProvider, TraitContext, TraitProvider, useEntitySchema, useEntitySchemaOptional, useServerBridge, useTrait, useTraitContext } from '@almadar/ui/providers';
5
5
  import { createLogger, setNamespaceLevel, isLogLevelEnabled } from '@almadar/logger';
6
6
  import { StateMachineManager, collectDeclaredConfigDefaults, resolveCallSitePayloadCaptures, createServerEffectHandlers, EffectExecutor, createContextFromBindings, createTickScheduler, isValidCronExpression, parseDurationString, InMemoryPersistence, normalizeCallSiteConfigToValues, interpolateValue } from '@almadar/runtime';
7
- import { mergeEntityFrame, isCircuitEvent, applyListenPayloadMapping, schemaToIR, getPage, clearSchemaCache as clearSchemaCache$1, walkSExpr, buildResolvedTraitConfigs, isRenderBindingMarker, isInlineTrait, containsEntityBinding, isSExpr, isEventPayloadValue, RENDER_BINDING_MARKER } from '@almadar/core';
7
+ import { mergeEntityFrame, isCircuitEvent, applyListenPayloadMapping, schemaToIR, getPage, clearSchemaCache as clearSchemaCache$1, walkSExpr, buildResolvedTraitConfigs, isRenderBindingMarker, isInlineTrait, containsEntityBinding, isSExpr, isEventPayloadValue, RENDER_BINDING_MARKER, ANIMATION_NAMES } from '@almadar/core';
8
8
  import { clsx } from 'clsx';
9
9
  import { twMerge } from 'tailwind-merge';
10
10
  import * as LucideIcons2 from 'lucide-react';
@@ -1334,6 +1334,10 @@ var init_Icon = __esm({
1334
1334
  function isTilesheet(a) {
1335
1335
  return typeof a.tileWidth === "number";
1336
1336
  }
1337
+ function isSpriteSheetAtlas(a) {
1338
+ const s = a;
1339
+ return typeof s.frameWidth === "number" && typeof s.frameHeight === "number" && typeof s.animations === "object";
1340
+ }
1337
1341
  function getAtlas(url, onReady) {
1338
1342
  if (atlasCache.has(url)) return atlasCache.get(url) ?? void 0;
1339
1343
  atlasCache.set(url, void 0);
@@ -1373,6 +1377,7 @@ function subRectFor(atlas, sprite) {
1373
1377
  sh: atlas.tileHeight
1374
1378
  };
1375
1379
  }
1380
+ if (isSpriteSheetAtlas(atlas)) return null;
1376
1381
  const st = atlas.subTextures[sprite];
1377
1382
  if (!st) return null;
1378
1383
  return { sx: st.x, sy: st.y, sw: st.width, sh: st.height };
@@ -1682,7 +1687,7 @@ var init_Button = __esm({
1682
1687
  "data-testid": dataTestId ?? (action ? `action-${action}` : void 0),
1683
1688
  children: [
1684
1689
  isLoading ? /* @__PURE__ */ jsx(Loader2, { className: "h-icon-default w-icon-default animate-spin" }) : resolvedLeftIcon && /* @__PURE__ */ jsx("span", { className: "flex-shrink-0", children: resolvedLeftIcon }),
1685
- children || label,
1690
+ (Array.isArray(children) ? children.length > 0 : Boolean(children)) ? children : label,
1686
1691
  resolvedRightIcon && !isLoading && /* @__PURE__ */ jsx("span", { className: "flex-shrink-0", children: resolvedRightIcon })
1687
1692
  ]
1688
1693
  }
@@ -7687,6 +7692,34 @@ var init_isometric = __esm({
7687
7692
  };
7688
7693
  }
7689
7694
  });
7695
+ function isAnimationName(name) {
7696
+ return ANIMATION_NAMES.includes(name);
7697
+ }
7698
+ function frameRect(frame, row, columns, frameWidth, frameHeight) {
7699
+ return {
7700
+ sx: frame % columns * frameWidth,
7701
+ sy: row * frameHeight,
7702
+ sw: frameWidth,
7703
+ sh: frameHeight
7704
+ };
7705
+ }
7706
+ function getCurrentFrameFromDef(def, elapsed) {
7707
+ const frameDuration = 1e3 / def.frameRate;
7708
+ const totalDuration = def.frames * frameDuration;
7709
+ if (def.loop) {
7710
+ const frame2 = Math.floor(elapsed % totalDuration / frameDuration);
7711
+ return { frame: frame2, finished: false };
7712
+ }
7713
+ if (elapsed >= totalDuration) {
7714
+ return { frame: def.frames - 1, finished: true };
7715
+ }
7716
+ const frame = Math.floor(elapsed / frameDuration);
7717
+ return { frame, finished: false };
7718
+ }
7719
+ var init_spriteAnimation = __esm({
7720
+ "lib/spriteAnimation.ts"() {
7721
+ }
7722
+ });
7690
7723
 
7691
7724
  // lib/gameShared.ts
7692
7725
  var init_gameShared = __esm({
@@ -9440,6 +9473,9 @@ function paintFallbackSquare(painter, node, dctx, reason) {
9440
9473
  painter.strokeRect(rect.x, rect.y, rect.w, rect.h, "#5e564b", Math.max(1, tw / 32));
9441
9474
  painter.restore();
9442
9475
  }
9476
+ function isAnimatedSprite(node) {
9477
+ return node.animation !== void 0 && typeof node.asset?.atlas === "string";
9478
+ }
9443
9479
  function DrawSprite(_props) {
9444
9480
  return null;
9445
9481
  }
@@ -9448,6 +9484,7 @@ var init_DrawSprite = __esm({
9448
9484
  "components/game/atoms/DrawSprite.tsx"() {
9449
9485
  "use client";
9450
9486
  init_atlasSlice();
9487
+ init_spriteAnimation();
9451
9488
  init_imageCache();
9452
9489
  init_contract();
9453
9490
  spriteLog = createLogger("almadar:ui:draw-sprite");
@@ -9461,6 +9498,23 @@ var init_DrawSprite = __esm({
9461
9498
  return;
9462
9499
  }
9463
9500
  let src = typeof node.frame === "object" ? node.frame : void 0;
9501
+ if (!src && node.animation !== void 0 && typeof node.asset.atlas === "string") {
9502
+ const atlas = getAtlas(node.asset.atlas, dctx.invalidate);
9503
+ if (!atlas) {
9504
+ if (atlasFailed(node.asset.atlas)) paintFallbackSquare(painter, node, dctx, "atlas-failed");
9505
+ return;
9506
+ }
9507
+ if (isSpriteSheetAtlas(atlas)) {
9508
+ const def = isAnimationName(node.animation) ? atlas.animations[node.animation] : void 0;
9509
+ if (!def) {
9510
+ paintFallbackSquare(painter, node, dctx, "sprite-missing");
9511
+ return;
9512
+ }
9513
+ const { frame } = getCurrentFrameFromDef(node.loop === void 0 ? def : { ...def, loop: node.loop }, dctx.time);
9514
+ const r = frameRect(frame, def.row, atlas.columns, atlas.frameWidth, atlas.frameHeight);
9515
+ src = { x: r.sx, y: r.sy, w: r.sw, h: r.sh };
9516
+ }
9517
+ }
9464
9518
  if (!src && isAtlasAsset(node.asset)) {
9465
9519
  const atlas = getAtlas(node.asset.atlas, dctx.invalidate);
9466
9520
  if (!atlas) {
@@ -10184,9 +10238,11 @@ function Canvas2D({
10184
10238
  const miniMapHeight = gridExtent.height || 10;
10185
10239
  const drawableIsAnimated = (node) => {
10186
10240
  if (node.type === "draw-shape") return isAnimatedShape(node);
10241
+ if (node.type === "draw-sprite") return isAnimatedSprite(node);
10187
10242
  if (node.type === "draw-group")
10188
10243
  return isAnimatedGroup(node) || Array.isArray(node.items) && node.items.some(drawableIsAnimated);
10189
10244
  if (node.type === "draw-shape-layer") return Array.isArray(node.items) && node.items.some(isAnimatedShape);
10245
+ if (node.type === "draw-sprite-layer") return Array.isArray(node.items) && node.items.some(isAnimatedSprite);
10190
10246
  return false;
10191
10247
  };
10192
10248
  const animRafRef = useRef(0);
@@ -10527,6 +10583,7 @@ var init_Canvas2D = __esm({
10527
10583
  init_projector();
10528
10584
  init_paintDispatch();
10529
10585
  init_DrawShape();
10586
+ init_DrawSprite();
10530
10587
  init_DrawGroup();
10531
10588
  init_registry();
10532
10589
  init_hitTest();
@@ -10600,6 +10657,7 @@ function Canvas({
10600
10657
  cameraMode: to3DCameraMode(camera?.mode),
10601
10658
  ...zoom !== void 0 ? { scale: zoom } : {},
10602
10659
  ...camera?.fov !== void 0 ? { fov: camera.fov } : {},
10660
+ ...camera?.azimuth !== void 0 ? { azimuth: camera.azimuth } : {},
10603
10661
  ...camera?.target !== void 0 ? { followTarget: camera.target } : {},
10604
10662
  unitScale,
10605
10663
  backgroundColor,
@@ -22102,7 +22160,7 @@ var init_DashboardLayout = __esm({
22102
22160
  }, [isMobile, sidebarOpen]);
22103
22161
  const location = useLocation();
22104
22162
  const ctxPagePath = useCurrentPagePath();
22105
- const activePath = currentPath ?? ctxPagePath ?? location.pathname;
22163
+ const activePath = (currentPath || void 0) ?? ctxPagePath ?? location.pathname;
22106
22164
  const { signOut: authSignOut } = useAuthContext();
22107
22165
  const user = userProp || (null);
22108
22166
  const { t } = useTranslate();
@@ -22565,6 +22623,14 @@ var init_DashboardLayout = __esm({
22565
22623
  NavLinkBottom.displayName = "NavLinkBottom";
22566
22624
  }
22567
22625
  });
22626
+ function downloadItemUrl(url, label) {
22627
+ const a = document.createElement("a");
22628
+ a.href = url;
22629
+ a.download = url.startsWith("data:") ? label : url.split("/").pop() ?? "download";
22630
+ document.body.appendChild(a);
22631
+ a.click();
22632
+ a.remove();
22633
+ }
22568
22634
  function computeMenuStyle(position, triggerRect) {
22569
22635
  const isTop = position.startsWith("top");
22570
22636
  const isRight = position.endsWith("right") || position.endsWith("end");
@@ -22617,6 +22683,7 @@ function SubMenu({
22617
22683
  onClick: () => {
22618
22684
  if (item.disabled) return;
22619
22685
  if (item.event) eventBus.emit(`UI:${item.event}`, { itemId, label: item.label });
22686
+ if (item.url) downloadItemUrl(item.url, item.label);
22620
22687
  item.onClick?.();
22621
22688
  },
22622
22689
  "aria-disabled": item.disabled || void 0,
@@ -22770,6 +22837,7 @@ var init_Menu = __esm({
22770
22837
  setActiveSubMenu(itemId);
22771
22838
  } else {
22772
22839
  if (item.event) eventBus.emit(`UI:${item.event}`, { itemId, label: item.label });
22840
+ if (item.url) downloadItemUrl(item.url, item.label);
22773
22841
  item.onClick?.();
22774
22842
  setIsOpen(false);
22775
22843
  }
@@ -22805,12 +22873,19 @@ var init_Menu = __esm({
22805
22873
  "bottom-end": "bottom-start"
22806
22874
  };
22807
22875
  const effectivePosition = direction === "rtl" ? rtlMirror[position] ?? position : position;
22808
- const triggerChild = React85__default.isValidElement(trigger) ? trigger : /* @__PURE__ */ jsx(Typography, { variant: "small", as: "span", children: trigger });
22809
- const triggerElement = React85__default.cloneElement(
22810
- triggerChild,
22876
+ const triggerElement = React85__default.isValidElement(trigger) ? React85__default.cloneElement(trigger, {
22877
+ ref: triggerRef,
22878
+ onClick: handleToggle
22879
+ }) : /* @__PURE__ */ jsx(
22880
+ Box,
22811
22881
  {
22812
- ref: triggerRef,
22813
- onClick: handleToggle
22882
+ as: "span",
22883
+ ref: (el) => {
22884
+ triggerRef.current = el;
22885
+ },
22886
+ onClick: handleToggle,
22887
+ className: "inline-flex",
22888
+ children: typeof trigger === "string" || typeof trigger === "number" ? /* @__PURE__ */ jsx(Typography, { variant: "small", as: "span", children: trigger }) : trigger
22814
22889
  }
22815
22890
  );
22816
22891
  const renderMenuItems = (menuItems) => menuItems.map((item, index) => {
@@ -47540,6 +47615,11 @@ function OrbPreview({
47540
47615
  setRouteParams(initialPageMatch?.params ?? {});
47541
47616
  }
47542
47617
  }, [initialPagePath, initialPageName, initialPageMatch]);
47618
+ const currentPagePath = useMemo(() => {
47619
+ const activePageName = currentPage ?? pages[0]?.page.name;
47620
+ const resolved = pages.find((p) => p.page.name === activePageName)?.page.path;
47621
+ return resolved ?? initialPagePath;
47622
+ }, [pages, currentPage, initialPagePath]);
47543
47623
  const handleNavigate = useCallback((path) => {
47544
47624
  const hit = matchPathAmong(pages, path, (entry) => entry.page.path);
47545
47625
  const match = hit?.candidate;
@@ -47613,7 +47693,7 @@ function OrbPreview({
47613
47693
  style: { height },
47614
47694
  children: [
47615
47695
  localFallback && /* @__PURE__ */ jsx(Box, { className: "px-3 py-2 bg-[var(--color-warning)] bg-opacity-10 border-b border-[var(--color-warning)] flex items-center gap-2", children: /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-[var(--color-warning-foreground)] flex-1", children: "Preview server unreachable \u2014 running locally. Server-side state and persistence are disabled." }) }),
47616
- /* @__PURE__ */ jsx(OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, isolated, children: /* @__PURE__ */ jsx(UISlotProvider, { children: /* @__PURE__ */ jsx(
47696
+ /* @__PURE__ */ jsx(CurrentPagePathProvider, { value: currentPagePath, children: /* @__PURE__ */ jsx(OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, isolated, children: /* @__PURE__ */ jsx(UISlotProvider, { children: /* @__PURE__ */ jsx(
47617
47697
  SchemaRunner,
47618
47698
  {
47619
47699
  schema: parseResult.schema,
@@ -47626,7 +47706,7 @@ function OrbPreview({
47626
47706
  onLocalFallback: handleLocalFallback,
47627
47707
  persistence
47628
47708
  }
47629
- ) }) })
47709
+ ) }) }) })
47630
47710
  ]
47631
47711
  }
47632
47712
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "5.143.0",
3
+ "version": "5.145.0",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -118,11 +118,11 @@
118
118
  "access": "public"
119
119
  },
120
120
  "dependencies": {
121
- "@almadar/core": "^10.49.0",
121
+ "@almadar/core": "^10.52.0",
122
122
  "@almadar/evaluator": "^2.39.0",
123
123
  "@almadar/logger": "^1.11.0",
124
- "@almadar/runtime": "^6.49.0",
125
- "@almadar/std": "^16.160.0",
124
+ "@almadar/runtime": "^6.51.0",
125
+ "@almadar/std": "^16.163.0",
126
126
  "@almadar/syntax": "^1.14.0",
127
127
  "@dnd-kit/core": "^6.3.1",
128
128
  "@dnd-kit/sortable": "^10.0.0",
@@ -234,6 +234,7 @@
234
234
  },
235
235
  "scripts": {
236
236
  "build": "rm -rf dist && NODE_OPTIONS=--max-old-space-size=8192 tsup",
237
+ "build:dev": "NODE_OPTIONS=--max-old-space-size=8192 UI_SKIP_DTS=1 tsup",
237
238
  "build:watch": "tsup --watch",
238
239
  "typecheck": "tsc --noEmit",
239
240
  "lint": "eslint --no-warn-ignored --max-warnings 0 .",