@almadar/ui 5.153.0 → 5.155.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.
package/dist/avl/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import * as React94 from 'react';
3
3
  import React94__default, { createContext, useState, useMemo, useRef, useEffect, useContext, useCallback, Suspense, useLayoutEffect, Profiler, useReducer, useSyncExternalStore, lazy, useId } from 'react';
4
- import { getAllPages, matchPathAmong, CurrentPagePathProvider, OrbitalProvider, EventBusContext, useTraitScopeChain, ServerBridgeProvider, VerificationProvider, EntitySchemaProvider, OrbitalThemeProvider, useServerBridge, EntityBindingContext, useEntitySchema, useEntitySchemaOptional, useEntityBindingSnapshot, TraitScopeProvider, useTraitScope, useCurrentPagePath, useGameAudioContextOptional } from '@almadar/ui/providers';
4
+ import { getAllPages, matchPathAmong, CurrentPagePathProvider, NavStackProvider, OrbitalProvider, EventBusContext, useTraitScopeChain, useNavStack, ServerBridgeProvider, VerificationProvider, EntitySchemaProvider, OrbitalThemeProvider, useServerBridge, EntityBindingContext, useEntitySchema, useEntitySchemaOptional, useEntityBindingSnapshot, TraitScopeProvider, useTraitScope, useCurrentPagePath, useGameAudioContextOptional } from '@almadar/ui/providers';
5
5
  import { createLogger, setNamespaceLevel, isLogLevelEnabled } from '@almadar/logger';
6
6
  import ELK from 'elkjs/lib/elk.bundled.js';
7
7
  import { MarkerType, useReactFlow, Handle, Position, getBezierPath, EdgeLabelRenderer, useNodeId, ReactFlowProvider, BaseEdge, useNodesState, useEdgesState, ReactFlow, Controls, Background, BackgroundVariant } from '@xyflow/react';
@@ -23589,17 +23589,27 @@ var init_Breadcrumb = __esm({
23589
23589
  init_useEventBus();
23590
23590
  Breadcrumb = ({
23591
23591
  items,
23592
+ fromNavStack = false,
23593
+ itemEvent,
23592
23594
  separator = "chevron-right",
23593
23595
  maxItems,
23594
23596
  className
23595
23597
  }) => {
23596
23598
  const eventBus = useEventBus();
23597
23599
  const { t } = useTranslate();
23598
- const displayItems = maxItems && items.length > maxItems ? [
23599
- ...items.slice(0, 1),
23600
+ const navStack = useNavStack();
23601
+ const sourceItems = fromNavStack ? navStack.entries.map((entry, i) => ({
23602
+ label: entry.label,
23603
+ path: entry.href,
23604
+ isCurrent: i === navStack.entries.length - 1,
23605
+ event: itemEvent
23606
+ })) : items ?? [];
23607
+ if (fromNavStack && sourceItems.length === 0) return null;
23608
+ const displayItems = maxItems && sourceItems.length > maxItems ? [
23609
+ ...sourceItems.slice(0, 1),
23600
23610
  { label: "...", isCurrent: false },
23601
- ...items.slice(-maxItems + 1)
23602
- ] : items;
23611
+ ...sourceItems.slice(-maxItems + 1)
23612
+ ] : sourceItems;
23603
23613
  return /* @__PURE__ */ jsx(
23604
23614
  "nav",
23605
23615
  {
@@ -23609,7 +23619,7 @@ var init_Breadcrumb = __esm({
23609
23619
  const isLast = index === displayItems.length - 1;
23610
23620
  const isEllipsis = item.label === "...";
23611
23621
  return /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-2", children: [
23612
- isEllipsis ? /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: item.label }) : item.href || item.path ? /* @__PURE__ */ jsxs(
23622
+ isEllipsis ? /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: item.label }) : (item.href || item.path) && !item.event && !fromNavStack ? /* @__PURE__ */ jsxs(
23613
23623
  "a",
23614
23624
  {
23615
23625
  href: item.href || item.path,
@@ -23635,7 +23645,12 @@ var init_Breadcrumb = __esm({
23635
23645
  {
23636
23646
  type: "button",
23637
23647
  onClick: () => {
23638
- if (item.event) eventBus.emit(`UI:${item.event}`, { label: item.label });
23648
+ const href = item.path ?? item.href;
23649
+ if (item.event) {
23650
+ eventBus.emit(`UI:${item.event}`, { label: item.label, href, index });
23651
+ } else if (fromNavStack && href) {
23652
+ navStack.goTo(href);
23653
+ }
23639
23654
  item.onClick?.();
23640
23655
  },
23641
23656
  className: cn(
@@ -44108,6 +44123,7 @@ var init_DetailPanel = __esm({
44108
44123
  init_Box();
44109
44124
  init_Stack();
44110
44125
  init_SimpleGrid();
44126
+ init_Menu();
44111
44127
  init_LoadingState();
44112
44128
  init_ErrorState();
44113
44129
  init_EmptyState();
@@ -44125,6 +44141,7 @@ var init_DetailPanel = __esm({
44125
44141
  avatar,
44126
44142
  sections: propSections,
44127
44143
  actions,
44144
+ maxInlineActions,
44128
44145
  backAction,
44129
44146
  footer,
44130
44147
  slideOver = false,
@@ -44359,7 +44376,7 @@ var init_DetailPanel = __esm({
44359
44376
  }
44360
44377
  ) }),
44361
44378
  /* @__PURE__ */ jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
44362
- otherActions.map((action, idx) => /* @__PURE__ */ jsx(
44379
+ (maxInlineActions != null ? otherActions.slice(0, maxInlineActions) : otherActions).map((action, idx) => /* @__PURE__ */ jsx(
44363
44380
  Button,
44364
44381
  {
44365
44382
  variant: action.variant || "secondary",
@@ -44374,6 +44391,22 @@ var init_DetailPanel = __esm({
44374
44391
  },
44375
44392
  idx
44376
44393
  )),
44394
+ maxInlineActions != null && otherActions.length > maxInlineActions && /* @__PURE__ */ jsx(
44395
+ Menu,
44396
+ {
44397
+ position: "bottom-end",
44398
+ trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
44399
+ items: otherActions.slice(maxInlineActions).map((action) => ({
44400
+ // ONE firing path: onClick → handleActionClick (emits with
44401
+ // the {id, row} payload). Passing `event` too would make
44402
+ // Menu emit a second, payload-less copy of the same event.
44403
+ label: action.label,
44404
+ icon: action.icon,
44405
+ variant: action.variant === "danger" ? "danger" : "default",
44406
+ onClick: () => handleActionClick(action, normalizedData)
44407
+ }))
44408
+ }
44409
+ ),
44377
44410
  /* @__PURE__ */ jsx(
44378
44411
  Button,
44379
44412
  {
@@ -55364,7 +55397,7 @@ function runTickFrame(entityId, orderedWriters, store) {
55364
55397
  }
55365
55398
  var log8 = createLogger("almadar:ui:effects:client-handlers");
55366
55399
  function createClientEffectHandlers(options) {
55367
- const { eventBus, slotSetter, navigate, notify, callService, liveEntity } = options;
55400
+ const { eventBus, slotSetter, navigate, navigateBack, notify, callService, liveEntity } = options;
55368
55401
  return {
55369
55402
  emit: (event, payload, source) => {
55370
55403
  const prefixedEvent = event.startsWith("UI:") ? event : `UI:${event}`;
@@ -55414,6 +55447,9 @@ function createClientEffectHandlers(options) {
55414
55447
  }
55415
55448
  log8.warn("No navigate handler, ignoring", { path });
55416
55449
  }),
55450
+ navigateBack: navigateBack ?? (() => {
55451
+ log8.warn("No navigate-back handler, ignoring");
55452
+ }),
55417
55453
  notify: notify ?? ((msg, type) => {
55418
55454
  log8.debug("notify", { type, message: msg });
55419
55455
  })
@@ -55883,6 +55919,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
55883
55919
  }
55884
55920
  },
55885
55921
  navigate: optionsRef.current?.navigate,
55922
+ navigateBack: optionsRef.current?.navigateBack,
55886
55923
  notify: optionsRef.current?.notify,
55887
55924
  callService: optionsRef.current?.callService,
55888
55925
  // The canonical client `set` writes `(set @entity.X)` straight into
@@ -55944,6 +55981,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
55944
55981
  emit: clientHandlers.emit,
55945
55982
  renderUI: clientHandlers.renderUI,
55946
55983
  navigate: clientHandlers.navigate,
55984
+ navigateBack: clientHandlers.navigateBack,
55947
55985
  notify: clientHandlers.notify
55948
55986
  };
55949
55987
  }
@@ -56563,7 +56601,7 @@ function normalizeChild(child) {
56563
56601
  props: { ...rest, ...normalizedChildren !== void 0 ? { children: normalizedChildren } : {} }
56564
56602
  };
56565
56603
  }
56566
- function applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraits) {
56604
+ function applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraits, onNavigateBack) {
56567
56605
  for (const eff of effects) {
56568
56606
  if (eff.type === "render-ui" && eff.slot && eff.pattern) {
56569
56607
  if (eff.traitName && activeTraits && !activeTraits.has(eff.traitName)) {
@@ -56609,7 +56647,9 @@ function applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, active
56609
56647
  });
56610
56648
  }
56611
56649
  } else if (eff.type === "navigate" && eff.route && onNavigate) {
56612
- onNavigate(eff.route, eff.params);
56650
+ onNavigate(eff.route, eff.params, eff.crumb);
56651
+ } else if (eff.type === "navigate-back" && onNavigateBack) {
56652
+ onNavigateBack();
56613
56653
  }
56614
56654
  }
56615
56655
  }
@@ -56632,7 +56672,17 @@ function collectServerActiveTraits(ir, allTraits, mountedTraitNames) {
56632
56672
  }
56633
56673
  return active;
56634
56674
  }
56635
- function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNavigate, onLocalFallback, persistence, traitConfigsByName, orbitalsByTrait, embeddedTraits, serverActiveTraits, children }) {
56675
+ function NavStackRefBridge({ apiRef }) {
56676
+ const api = useNavStack();
56677
+ useEffect(() => {
56678
+ apiRef.current = api;
56679
+ return () => {
56680
+ apiRef.current = null;
56681
+ };
56682
+ }, [api, apiRef]);
56683
+ return null;
56684
+ }
56685
+ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNavigate, onNavigateBack, onLocalFallback, persistence, traitConfigsByName, orbitalsByTrait, embeddedTraits, serverActiveTraits, children }) {
56636
56686
  const bridge = useServerBridge();
56637
56687
  const activeTraitNames = useMemo(
56638
56688
  () => new Set(traits2.map((b) => b.trait.name).filter((n) => !!n)),
@@ -56658,10 +56708,10 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
56658
56708
  for (const name of targets) {
56659
56709
  const { effects, meta } = await bridge.sendEvent(name, event, withActiveTraits(payload));
56660
56710
  recordServerResponse(name, event, meta);
56661
- applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames);
56711
+ applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames, onNavigateBack);
56662
56712
  }
56663
- }, [bridge.connected, bridge.sendEvent, orbitalNames, uiSlots, onNavigate, embeddedTraits, activeTraitNames, withActiveTraits]);
56664
- const opts = orbitalNames ? { onEventProcessed, navigate: onNavigate, traitConfigsByName, orbitalsByTrait, embeddedTraits, initPayload: routeParams } : { navigate: onNavigate, persistence, traitConfigsByName, orbitalsByTrait, embeddedTraits, initPayload: routeParams };
56713
+ }, [bridge.connected, bridge.sendEvent, orbitalNames, uiSlots, onNavigate, onNavigateBack, embeddedTraits, activeTraitNames, withActiveTraits]);
56714
+ const opts = orbitalNames ? { onEventProcessed, navigate: onNavigate, navigateBack: onNavigateBack, traitConfigsByName, orbitalsByTrait, embeddedTraits, initPayload: routeParams } : { navigate: onNavigate, navigateBack: onNavigateBack, persistence, traitConfigsByName, orbitalsByTrait, embeddedTraits, initPayload: routeParams };
56665
56715
  const { sendEvent, entityBindingSource } = useTraitStateMachine(traits2, uiSlots, opts);
56666
56716
  const initSentRef = useRef(false);
56667
56717
  const prevTraitsRef = useRef(void 0);
@@ -56720,13 +56770,13 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
56720
56770
  effects: effectTraces,
56721
56771
  timestamp: Date.now()
56722
56772
  });
56723
- applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames);
56773
+ applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames, onNavigateBack);
56724
56774
  }
56725
56775
  })();
56726
- }, [bridge.connected, orbitalNames, bridge.sendEvent, uiSlots, onNavigate, embeddedTraits, activeTraitNames, withActiveTraits, routeParams]);
56776
+ }, [bridge.connected, orbitalNames, bridge.sendEvent, uiSlots, onNavigate, onNavigateBack, embeddedTraits, activeTraitNames, withActiveTraits, routeParams]);
56727
56777
  return /* @__PURE__ */ jsx(EntityBindingContext.Provider, { value: entityBindingSource, children });
56728
56778
  }
56729
- function SchemaRunner({ schema, serverUrl, transport, mockData, pageName, routeParams, onNavigate, onLocalFallback, persistence }) {
56779
+ function SchemaRunner({ schema, serverUrl, transport, mockData, pageName, routeParams, onNavigate, onNavigateBack, onLocalFallback, persistence }) {
56730
56780
  const { traits: traits2, allEntities, allTraits, ir } = useResolvedSchema(schema, pageName);
56731
56781
  const allPageTraits = useMemo(() => {
56732
56782
  let base;
@@ -56874,6 +56924,7 @@ function SchemaRunner({ schema, serverUrl, transport, mockData, pageName, routeP
56874
56924
  embeddedTraits,
56875
56925
  serverActiveTraits,
56876
56926
  onNavigate,
56927
+ onNavigateBack,
56877
56928
  onLocalFallback,
56878
56929
  persistence,
56879
56930
  children: /* @__PURE__ */ jsx(OrbitalThemeProvider, { theme: activeOrbitalTheme, children: /* @__PURE__ */ jsx(Box, { className: "h-full min-h-full overflow-auto p-4", children: /* @__PURE__ */ jsx(UISlotRenderer, { includeHud: true, hudMode: "inline", includeFloating: true }) }) })
@@ -56966,6 +57017,15 @@ function OrbPreview({
56966
57017
  const resolved = pages.find((p) => p.page.name === activePageName)?.page.path;
56967
57018
  return resolved ?? initialPagePath;
56968
57019
  }, [pages, currentPage, initialPagePath]);
57020
+ const navPages = useMemo(
57021
+ () => pages.filter((p) => typeof p.page.path === "string" && p.page.path.length > 0).map((p) => ({ path: p.page.path, name: p.page.name, orbital: p.orbitalName })),
57022
+ [pages]
57023
+ );
57024
+ const concreteCurrentPath = useMemo(() => {
57025
+ const pattern = currentPagePath ?? "/";
57026
+ return pattern.replace(/:([A-Za-z0-9_]+)/g, (whole, key) => routeParams[key] ?? whole);
57027
+ }, [currentPagePath, routeParams]);
57028
+ const navStackRef = useRef(null);
56969
57029
  const handleNavigate = useCallback((path) => {
56970
57030
  const hit = matchPathAmong(pages, path, (entry) => entry.page.path);
56971
57031
  const match = hit?.candidate;
@@ -56987,6 +57047,16 @@ function OrbPreview({
56987
57047
  }
56988
57048
  }
56989
57049
  }, [pages]);
57050
+ const handleNavigateEffect = useCallback(
57051
+ (path, _params, crumb) => {
57052
+ navStackRef.current?.beginNavigate(path, crumb);
57053
+ handleNavigate(path);
57054
+ },
57055
+ [handleNavigate]
57056
+ );
57057
+ const handleNavigateBack = useCallback(() => {
57058
+ navStackRef.current?.back();
57059
+ }, []);
56990
57060
  useEffect(() => {
56991
57061
  const unsubscribe = eventBus.on("UI:NAVIGATE", (event) => {
56992
57062
  const url = event.payload?.url;
@@ -57039,20 +57109,33 @@ function OrbPreview({
57039
57109
  style: { height },
57040
57110
  children: [
57041
57111
  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." }) }),
57042
- /* @__PURE__ */ jsx(CurrentPagePathProvider, { value: currentPagePath, children: /* @__PURE__ */ jsx(OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, isolated, children: /* @__PURE__ */ jsx(UISlotProvider, { children: /* @__PURE__ */ jsx(
57043
- SchemaRunner,
57044
- {
57045
- schema: parseResult.schema,
57046
- serverUrl,
57047
- transport,
57048
- mockData: effectiveMockData,
57049
- pageName: currentPage,
57050
- routeParams,
57051
- onNavigate: handleNavigate,
57052
- onLocalFallback: handleLocalFallback,
57053
- persistence
57054
- }
57055
- ) }) }) })
57112
+ /* @__PURE__ */ jsx(CurrentPagePathProvider, { value: currentPagePath, children: /* @__PURE__ */ jsxs(
57113
+ NavStackProvider,
57114
+ {
57115
+ pages: navPages,
57116
+ currentPath: concreteCurrentPath,
57117
+ navigate: handleNavigate,
57118
+ storageKey: `almadar:navstack:${parseResult.schema.name ?? "preview"}`,
57119
+ children: [
57120
+ /* @__PURE__ */ jsx(NavStackRefBridge, { apiRef: navStackRef }),
57121
+ /* @__PURE__ */ jsx(OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, isolated, children: /* @__PURE__ */ jsx(UISlotProvider, { children: /* @__PURE__ */ jsx(
57122
+ SchemaRunner,
57123
+ {
57124
+ schema: parseResult.schema,
57125
+ serverUrl,
57126
+ transport,
57127
+ mockData: effectiveMockData,
57128
+ pageName: currentPage,
57129
+ routeParams,
57130
+ onNavigate: handleNavigateEffect,
57131
+ onNavigateBack: handleNavigateBack,
57132
+ onLocalFallback: handleLocalFallback,
57133
+ persistence
57134
+ }
57135
+ ) }) })
57136
+ ]
57137
+ }
57138
+ ) })
57056
57139
  ]
57057
57140
  }
57058
57141
  );
@@ -16201,17 +16201,27 @@ var init_Breadcrumb = __esm({
16201
16201
  init_useEventBus();
16202
16202
  exports.Breadcrumb = ({
16203
16203
  items,
16204
+ fromNavStack = false,
16205
+ itemEvent,
16204
16206
  separator = "chevron-right",
16205
16207
  maxItems,
16206
16208
  className
16207
16209
  }) => {
16208
16210
  const eventBus = useEventBus();
16209
16211
  const { t } = hooks.useTranslate();
16210
- const displayItems = maxItems && items.length > maxItems ? [
16211
- ...items.slice(0, 1),
16212
+ const navStack = providers.useNavStack();
16213
+ const sourceItems = fromNavStack ? navStack.entries.map((entry, i) => ({
16214
+ label: entry.label,
16215
+ path: entry.href,
16216
+ isCurrent: i === navStack.entries.length - 1,
16217
+ event: itemEvent
16218
+ })) : items ?? [];
16219
+ if (fromNavStack && sourceItems.length === 0) return null;
16220
+ const displayItems = maxItems && sourceItems.length > maxItems ? [
16221
+ ...sourceItems.slice(0, 1),
16212
16222
  { label: "...", isCurrent: false },
16213
- ...items.slice(-maxItems + 1)
16214
- ] : items;
16223
+ ...sourceItems.slice(-maxItems + 1)
16224
+ ] : sourceItems;
16215
16225
  return /* @__PURE__ */ jsxRuntime.jsx(
16216
16226
  "nav",
16217
16227
  {
@@ -16221,7 +16231,7 @@ var init_Breadcrumb = __esm({
16221
16231
  const isLast = index === displayItems.length - 1;
16222
16232
  const isEllipsis = item.label === "...";
16223
16233
  return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-center gap-2", children: [
16224
- isEllipsis ? /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", color: "muted", children: item.label }) : item.href || item.path ? /* @__PURE__ */ jsxRuntime.jsxs(
16234
+ isEllipsis ? /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", color: "muted", children: item.label }) : (item.href || item.path) && !item.event && !fromNavStack ? /* @__PURE__ */ jsxRuntime.jsxs(
16225
16235
  "a",
16226
16236
  {
16227
16237
  href: item.href || item.path,
@@ -16247,7 +16257,12 @@ var init_Breadcrumb = __esm({
16247
16257
  {
16248
16258
  type: "button",
16249
16259
  onClick: () => {
16250
- if (item.event) eventBus.emit(`UI:${item.event}`, { label: item.label });
16260
+ const href = item.path ?? item.href;
16261
+ if (item.event) {
16262
+ eventBus.emit(`UI:${item.event}`, { label: item.label, href, index });
16263
+ } else if (fromNavStack && href) {
16264
+ navStack.goTo(href);
16265
+ }
16251
16266
  item.onClick?.();
16252
16267
  },
16253
16268
  className: cn(
@@ -43588,6 +43603,7 @@ var init_DetailPanel = __esm({
43588
43603
  init_Box();
43589
43604
  init_Stack();
43590
43605
  init_SimpleGrid();
43606
+ init_Menu();
43591
43607
  init_LoadingState();
43592
43608
  init_ErrorState();
43593
43609
  init_EmptyState();
@@ -43605,6 +43621,7 @@ var init_DetailPanel = __esm({
43605
43621
  avatar,
43606
43622
  sections: propSections,
43607
43623
  actions,
43624
+ maxInlineActions,
43608
43625
  backAction,
43609
43626
  footer,
43610
43627
  slideOver = false,
@@ -43839,7 +43856,7 @@ var init_DetailPanel = __esm({
43839
43856
  }
43840
43857
  ) }),
43841
43858
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { justify: "end", align: "center", gap: "xs", children: [
43842
- otherActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
43859
+ (maxInlineActions != null ? otherActions.slice(0, maxInlineActions) : otherActions).map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
43843
43860
  exports.Button,
43844
43861
  {
43845
43862
  variant: action.variant || "secondary",
@@ -43854,6 +43871,22 @@ var init_DetailPanel = __esm({
43854
43871
  },
43855
43872
  idx
43856
43873
  )),
43874
+ maxInlineActions != null && otherActions.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
43875
+ exports.Menu,
43876
+ {
43877
+ position: "bottom-end",
43878
+ trigger: /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: "more-horizontal", size: "xs" }) }),
43879
+ items: otherActions.slice(maxInlineActions).map((action) => ({
43880
+ // ONE firing path: onClick → handleActionClick (emits with
43881
+ // the {id, row} payload). Passing `event` too would make
43882
+ // Menu emit a second, payload-less copy of the same event.
43883
+ label: action.label,
43884
+ icon: action.icon,
43885
+ variant: action.variant === "danger" ? "danger" : "default",
43886
+ onClick: () => handleActionClick(action, normalizedData)
43887
+ }))
43888
+ }
43889
+ ),
43857
43890
  /* @__PURE__ */ jsxRuntime.jsx(
43858
43891
  exports.Button,
43859
43892
  {
@@ -53701,6 +53734,10 @@ Object.defineProperty(exports, "useGameAudioContext", {
53701
53734
  enumerable: true,
53702
53735
  get: function () { return providers.useGameAudioContext; }
53703
53736
  });
53737
+ Object.defineProperty(exports, "useNavStack", {
53738
+ enumerable: true,
53739
+ get: function () { return providers.useNavStack; }
53740
+ });
53704
53741
  exports.ALMADAR_DND_MIME = ALMADAR_DND_MIME;
53705
53742
  exports.ActionCard = Card2;
53706
53743
  exports.ActionPalette = ActionPalette;
@@ -18,6 +18,7 @@ export { D as DEFAULT_SLOTS, S as SlotAnimation, a as SlotChangeCallback, c as S
18
18
  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';
19
19
  export { u as useEmitEvent, a as useEventBus, b as useEventListener } from '../useEventBus-Ckr4wqW3.cjs';
20
20
  export { E as EventBusContextType } from '../event-bus-types-Bl78kokd.cjs';
21
+ export { a as NavStackApi, u as useNavStack } from '../NavStackContext-BoVV9nWb.cjs';
21
22
  import 'three';
22
23
  import 'clsx';
23
24
  import '@tanstack/react-query';
@@ -482,7 +483,8 @@ interface BadgeProps extends React__default.HTMLAttributes<HTMLSpanElement> {
482
483
  /** When set, renders a small X button on the right of the badge that
483
484
  * invokes this handler — turns the badge into a removable chip.
484
485
  * Used by the TagInput molecule and other "list of removable values"
485
- * surfaces. */
486
+ * surfaces.
487
+ * @offByDefault */
486
488
  onRemove?: () => void;
487
489
  /** Accessible label for the remove button. Defaults to "Remove". */
488
490
  removeLabel?: string;
@@ -596,7 +598,8 @@ interface FilterPillProps extends Omit<React__default.HTMLAttributes<HTMLSpanEle
596
598
  label?: string | number;
597
599
  /** Lucide icon component or canonical kebab-case icon name string */
598
600
  icon?: IconInput;
599
- /** Called when the user clicks the remove (×) button */
601
+ /** Called when the user clicks the remove (×) button
602
+ * @offByDefault */
600
603
  onRemove?: () => void;
601
604
  /** Disable the remove button (renders without × control) */
602
605
  removable?: boolean;
@@ -606,7 +609,8 @@ interface FilterPillProps extends Omit<React__default.HTMLAttributes<HTMLSpanEle
606
609
  clickEvent?: EventEmit<{
607
610
  label: string | number | undefined;
608
611
  }>;
609
- /** Event name dispatched via event bus when the remove (×) button is clicked. Payload: { label } */
612
+ /** Event name dispatched via event bus when the remove (×) button is clicked. Payload: { label }
613
+ * @offByDefault */
610
614
  removeEvent?: EventEmit<{
611
615
  label: string | number | undefined;
612
616
  }>;
@@ -2923,13 +2927,15 @@ interface TraitSlotProps {
2923
2927
  feedback?: 'correct' | 'wrong' | null;
2924
2928
  /** Click handler */
2925
2929
  onClick?: () => void;
2926
- /** Remove handler */
2930
+ /** Remove handler
2931
+ * @offByDefault */
2927
2932
  onRemove?: () => void;
2928
2933
  /** Emits UI:{clickEvent} with { slotNumber } */
2929
2934
  clickEvent?: EventEmit<{
2930
2935
  slotNumber: number;
2931
2936
  }>;
2932
- /** Emits UI:{removeEvent} with { slotNumber } */
2937
+ /** Emits UI:{removeEvent} with { slotNumber }
2938
+ * @offByDefault */
2933
2939
  removeEvent?: EventEmit<{
2934
2940
  slotNumber: number;
2935
2941
  }>;
@@ -3966,13 +3972,27 @@ interface BreadcrumbItem {
3966
3972
  */
3967
3973
  isCurrent?: boolean;
3968
3974
  /** Event name to emit when clicked (for trait state machine integration) */
3969
- event?: string;
3975
+ event?: EventKey;
3970
3976
  }
3971
3977
  interface BreadcrumbProps {
3972
3978
  /**
3973
- * Breadcrumb items
3979
+ * Breadcrumb items. Omit together with `fromNavStack` to render the
3980
+ * orbital-scoped navigation stack instead of an authored trail.
3981
+ */
3982
+ items?: BreadcrumbItem[];
3983
+ /**
3984
+ * Render the current orbital's navigation stack (NavStackProvider) as the
3985
+ * trail: one crumb per visited/declared level, last entry current. The
3986
+ * stack is client-session state both execution paths maintain; outside a
3987
+ * provider this renders nothing.
3988
+ */
3989
+ fromNavStack?: boolean;
3990
+ /**
3991
+ * Event emitted when a non-current crumb is clicked, with payload
3992
+ * `{ label, href, index }` — the trait handles it with
3993
+ * `(navigate ?href)`. Without it, stack crumbs navigate directly.
3974
3994
  */
3975
- items: BreadcrumbItem[];
3995
+ itemEvent?: EventKey;
3976
3996
  /**
3977
3997
  * Separator icon (canonical kebab-case name or LucideIcon component)
3978
3998
  */
@@ -10750,6 +10770,8 @@ interface DetailPanelProps extends DisplayStateProps {
10750
10770
  sections?: readonly DetailSection[];
10751
10771
  /** Unified actions array - first action with variant='primary' is the main action */
10752
10772
  actions?: readonly DetailPanelAction[];
10773
+ /** Max inline action buttons before the rest collapse into a "⋯" overflow menu (mirrors DataGrid's maxInlineActions). Omit = all inline. */
10774
+ maxInlineActions?: number;
10753
10775
  /**
10754
10776
  * Navigation-back affordance. Renders top-LEFT before the title (OS/back
10755
10777
  * convention — Almadar_UX §8.4), spatially separated from the right-aligned
@@ -18,6 +18,7 @@ export { D as DEFAULT_SLOTS, S as SlotAnimation, a as SlotChangeCallback, c as S
18
18
  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.js';
19
19
  export { u as useEmitEvent, a as useEventBus, b as useEventListener } from '../useEventBus-CQWyAWpK.js';
20
20
  export { E as EventBusContextType } from '../event-bus-types-Bl78kokd.js';
21
+ export { a as NavStackApi, u as useNavStack } from '../NavStackContext-BoVV9nWb.js';
21
22
  import 'three';
22
23
  import 'clsx';
23
24
  import '@tanstack/react-query';
@@ -482,7 +483,8 @@ interface BadgeProps extends React__default.HTMLAttributes<HTMLSpanElement> {
482
483
  /** When set, renders a small X button on the right of the badge that
483
484
  * invokes this handler — turns the badge into a removable chip.
484
485
  * Used by the TagInput molecule and other "list of removable values"
485
- * surfaces. */
486
+ * surfaces.
487
+ * @offByDefault */
486
488
  onRemove?: () => void;
487
489
  /** Accessible label for the remove button. Defaults to "Remove". */
488
490
  removeLabel?: string;
@@ -596,7 +598,8 @@ interface FilterPillProps extends Omit<React__default.HTMLAttributes<HTMLSpanEle
596
598
  label?: string | number;
597
599
  /** Lucide icon component or canonical kebab-case icon name string */
598
600
  icon?: IconInput;
599
- /** Called when the user clicks the remove (×) button */
601
+ /** Called when the user clicks the remove (×) button
602
+ * @offByDefault */
600
603
  onRemove?: () => void;
601
604
  /** Disable the remove button (renders without × control) */
602
605
  removable?: boolean;
@@ -606,7 +609,8 @@ interface FilterPillProps extends Omit<React__default.HTMLAttributes<HTMLSpanEle
606
609
  clickEvent?: EventEmit<{
607
610
  label: string | number | undefined;
608
611
  }>;
609
- /** Event name dispatched via event bus when the remove (×) button is clicked. Payload: { label } */
612
+ /** Event name dispatched via event bus when the remove (×) button is clicked. Payload: { label }
613
+ * @offByDefault */
610
614
  removeEvent?: EventEmit<{
611
615
  label: string | number | undefined;
612
616
  }>;
@@ -2923,13 +2927,15 @@ interface TraitSlotProps {
2923
2927
  feedback?: 'correct' | 'wrong' | null;
2924
2928
  /** Click handler */
2925
2929
  onClick?: () => void;
2926
- /** Remove handler */
2930
+ /** Remove handler
2931
+ * @offByDefault */
2927
2932
  onRemove?: () => void;
2928
2933
  /** Emits UI:{clickEvent} with { slotNumber } */
2929
2934
  clickEvent?: EventEmit<{
2930
2935
  slotNumber: number;
2931
2936
  }>;
2932
- /** Emits UI:{removeEvent} with { slotNumber } */
2937
+ /** Emits UI:{removeEvent} with { slotNumber }
2938
+ * @offByDefault */
2933
2939
  removeEvent?: EventEmit<{
2934
2940
  slotNumber: number;
2935
2941
  }>;
@@ -3966,13 +3972,27 @@ interface BreadcrumbItem {
3966
3972
  */
3967
3973
  isCurrent?: boolean;
3968
3974
  /** Event name to emit when clicked (for trait state machine integration) */
3969
- event?: string;
3975
+ event?: EventKey;
3970
3976
  }
3971
3977
  interface BreadcrumbProps {
3972
3978
  /**
3973
- * Breadcrumb items
3979
+ * Breadcrumb items. Omit together with `fromNavStack` to render the
3980
+ * orbital-scoped navigation stack instead of an authored trail.
3981
+ */
3982
+ items?: BreadcrumbItem[];
3983
+ /**
3984
+ * Render the current orbital's navigation stack (NavStackProvider) as the
3985
+ * trail: one crumb per visited/declared level, last entry current. The
3986
+ * stack is client-session state both execution paths maintain; outside a
3987
+ * provider this renders nothing.
3988
+ */
3989
+ fromNavStack?: boolean;
3990
+ /**
3991
+ * Event emitted when a non-current crumb is clicked, with payload
3992
+ * `{ label, href, index }` — the trait handles it with
3993
+ * `(navigate ?href)`. Without it, stack crumbs navigate directly.
3974
3994
  */
3975
- items: BreadcrumbItem[];
3995
+ itemEvent?: EventKey;
3976
3996
  /**
3977
3997
  * Separator icon (canonical kebab-case name or LucideIcon component)
3978
3998
  */
@@ -10750,6 +10770,8 @@ interface DetailPanelProps extends DisplayStateProps {
10750
10770
  sections?: readonly DetailSection[];
10751
10771
  /** Unified actions array - first action with variant='primary' is the main action */
10752
10772
  actions?: readonly DetailPanelAction[];
10773
+ /** Max inline action buttons before the rest collapse into a "⋯" overflow menu (mirrors DataGrid's maxInlineActions). Omit = all inline. */
10774
+ maxInlineActions?: number;
10753
10775
  /**
10754
10776
  * Navigation-back affordance. Renders top-LEFT before the title (OS/back
10755
10777
  * convention — Almadar_UX §8.4), spatially separated from the right-aligned