@almadar/ui 4.53.7 → 4.54.1

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.
@@ -57600,46 +57600,46 @@ function schemaToOverviewGraph(schema, mockData, behaviorMeta, layoutHint, orbit
57600
57600
  }
57601
57601
  return { nodes, edges };
57602
57602
  }
57603
- function orbitalToExpandedGraph(schema, orbitalName, mockData) {
57604
- const nodes = [];
57605
- const edges = [];
57606
- const orbital = getOrbitals(schema).find((o) => o.name === orbitalName);
57607
- if (!orbital) return { nodes, edges };
57608
- const entityInfo = getEntityInfo(orbital);
57609
- const traits2 = getTraits2(orbital);
57610
- const uiTransitions = [];
57611
- for (const trait of traits2) {
57603
+ function collectUITransitions(orbital, filter) {
57604
+ const out = [];
57605
+ for (const trait of getTraits2(orbital)) {
57606
+ if (!filter(trait)) continue;
57612
57607
  const sm = getStateMachine2(trait);
57613
57608
  if (!sm) continue;
57614
57609
  for (const t of sm.transitions) {
57615
57610
  if (!t.effects) continue;
57616
57611
  const patterns = extractRenderUI(t.effects);
57617
- if (patterns.length > 0) {
57618
- uiTransitions.push({
57619
- traitName: trait.name,
57620
- transition: t,
57621
- patterns,
57622
- eventSources: collectEventSources(patterns),
57623
- states: sm.states,
57624
- allTransitions: sm.transitions
57625
- });
57626
- }
57612
+ if (patterns.length === 0) continue;
57613
+ out.push({
57614
+ trait,
57615
+ traitName: trait.name,
57616
+ transition: t,
57617
+ patterns,
57618
+ eventSources: collectEventSources(patterns),
57619
+ states: sm.states,
57620
+ allTransitions: sm.transitions
57621
+ });
57627
57622
  }
57628
57623
  }
57629
- if (uiTransitions.length === 0) return { nodes, edges };
57624
+ return out;
57625
+ }
57626
+ function buildScreenGraph(schema, orbitalName, entityName, transitions, groupedBehaviors, mockData) {
57627
+ const nodes = [];
57628
+ const edges = [];
57630
57629
  const stateNodeMap = /* @__PURE__ */ new Map();
57631
- for (const entry of uiTransitions) {
57630
+ for (const entry of transitions) {
57632
57631
  const key = `${entry.traitName}:${entry.transition.to}`;
57633
57632
  const existing = stateNodeMap.get(key);
57634
57633
  if (!existing || entry.patterns.length > existing.patterns.length) {
57635
57634
  stateNodeMap.set(key, entry);
57636
57635
  }
57637
57636
  }
57638
- const entries = Array.from(stateNodeMap.values());
57639
- const cols = Math.min(entries.length, 3);
57637
+ const transitionEntries = Array.from(stateNodeMap.values());
57638
+ const totalCards = transitionEntries.length + groupedBehaviors.length;
57639
+ if (totalCards === 0) return { nodes, edges };
57640
+ const cols = Math.min(totalCards, 3);
57640
57641
  const nodeIdMap = /* @__PURE__ */ new Map();
57641
- for (let i = 0; i < entries.length; i++) {
57642
- const entry = entries[i];
57642
+ transitionEntries.forEach((entry, i) => {
57643
57643
  const t = entry.transition;
57644
57644
  const nodeId = `${orbitalName}-${entry.traitName}-${t.event}-${t.to}`;
57645
57645
  const stateKey = `${entry.traitName}:${t.to}`;
@@ -57662,13 +57662,48 @@ function orbitalToExpandedGraph(schema, orbitalName, mockData) {
57662
57662
  stateRole: detectStateRole(t.to, entry.states, entry.allTransitions),
57663
57663
  effectTypes: t.effects ? extractEffectTypes(t.effects) : [],
57664
57664
  guard: t.guard,
57665
- entityName: entityInfo.name,
57665
+ entityName,
57666
57666
  _fullSchema: schema,
57667
57667
  _mockData: mockData
57668
57668
  }
57669
57669
  });
57670
- }
57671
- for (const entry of uiTransitions) {
57670
+ });
57671
+ groupedBehaviors.forEach((group, i) => {
57672
+ const t = group.representative.transition;
57673
+ const nodeId = `${orbitalName}-behavior-${group.alias}`;
57674
+ const idx = transitionEntries.length + i;
57675
+ const col = idx % cols;
57676
+ const row = Math.floor(idx / cols);
57677
+ nodes.push({
57678
+ id: nodeId,
57679
+ type: "preview",
57680
+ position: { x: col * EXPANDED_SPACING_X, y: row * EXPANDED_SPACING_Y },
57681
+ data: {
57682
+ orbitalName,
57683
+ traitName: group.representative.traitName,
57684
+ stateName: t.to,
57685
+ transitionEvent: t.event,
57686
+ fromState: t.from,
57687
+ toState: t.to,
57688
+ patterns: group.representative.patterns,
57689
+ eventSources: group.representative.eventSources,
57690
+ stateRole: detectStateRole(
57691
+ t.to,
57692
+ group.representative.states,
57693
+ group.representative.allTransitions
57694
+ ),
57695
+ effectTypes: t.effects ? extractEffectTypes(t.effects) : [],
57696
+ guard: t.guard,
57697
+ entityName,
57698
+ _fullSchema: schema,
57699
+ _mockData: mockData,
57700
+ behaviorAlias: group.alias,
57701
+ behaviorName: group.behaviorName,
57702
+ transitionCount: group.transitionCount
57703
+ }
57704
+ });
57705
+ });
57706
+ for (const entry of transitions) {
57672
57707
  const t = entry.transition;
57673
57708
  const sourceKey = `${entry.traitName}:${t.from}`;
57674
57709
  const targetKey = `${entry.traitName}:${t.to}`;
@@ -57697,6 +57732,66 @@ function orbitalToExpandedGraph(schema, orbitalName, mockData) {
57697
57732
  }
57698
57733
  return { nodes, edges };
57699
57734
  }
57735
+ function orbitalToExpandedGraph(schema, orbitalName, mockData) {
57736
+ const orbital = getOrbitals(schema).find((o) => o.name === orbitalName);
57737
+ if (!orbital) return { nodes: [], edges: [] };
57738
+ const entityInfo = getEntityInfo(orbital);
57739
+ const organismTransitions = collectUITransitions(
57740
+ orbital,
57741
+ (trait) => trait.sourceBehavior === void 0
57742
+ );
57743
+ const aliasBuckets = /* @__PURE__ */ new Map();
57744
+ const importedTransitions = collectUITransitions(
57745
+ orbital,
57746
+ (trait) => trait.sourceBehavior !== void 0
57747
+ );
57748
+ for (const entry of importedTransitions) {
57749
+ const sb = entry.trait.sourceBehavior;
57750
+ if (!sb) continue;
57751
+ const bucket = aliasBuckets.get(sb.alias);
57752
+ if (bucket) {
57753
+ bucket.transitions.push(entry);
57754
+ } else {
57755
+ aliasBuckets.set(sb.alias, {
57756
+ behaviorName: sb.behavior,
57757
+ transitions: [entry]
57758
+ });
57759
+ }
57760
+ }
57761
+ const groupedBehaviors = Array.from(aliasBuckets.entries()).map(
57762
+ ([alias, bucket]) => ({
57763
+ alias,
57764
+ behaviorName: bucket.behaviorName,
57765
+ representative: bucket.transitions[0],
57766
+ transitionCount: bucket.transitions.length
57767
+ })
57768
+ );
57769
+ return buildScreenGraph(
57770
+ schema,
57771
+ orbitalName,
57772
+ entityInfo.name,
57773
+ organismTransitions,
57774
+ groupedBehaviors,
57775
+ mockData
57776
+ );
57777
+ }
57778
+ function orbitalAliasToExpandedGraph(schema, orbitalName, alias, mockData) {
57779
+ const orbital = getOrbitals(schema).find((o) => o.name === orbitalName);
57780
+ if (!orbital) return { nodes: [], edges: [] };
57781
+ const entityInfo = getEntityInfo(orbital);
57782
+ const transitions = collectUITransitions(
57783
+ orbital,
57784
+ (trait) => trait.sourceBehavior?.alias === alias
57785
+ );
57786
+ return buildScreenGraph(
57787
+ schema,
57788
+ orbitalName,
57789
+ entityInfo.name,
57790
+ transitions,
57791
+ [],
57792
+ mockData
57793
+ );
57794
+ }
57700
57795
 
57701
57796
  // components/molecules/avl/OrbPreviewNode.tsx
57702
57797
  init_Box();
@@ -59371,6 +59466,25 @@ function buildMockData(schema) {
59371
59466
  );
59372
59467
  result[entityName] = rows;
59373
59468
  }
59469
+ for (const orbital of schema.orbitals) {
59470
+ for (const traitRef of orbital.traits ?? []) {
59471
+ if (typeof traitRef === "string") continue;
59472
+ if (!("stateMachine" in traitRef)) continue;
59473
+ const trait = traitRef;
59474
+ const sourceEntity = trait.sourceEntityDefinition;
59475
+ if (!sourceEntity || core.isEntityCall(sourceEntity)) continue;
59476
+ const sourceName = sourceEntity.name;
59477
+ if (!sourceName || result[sourceName]) continue;
59478
+ if (sourceEntity.instances && sourceEntity.instances.length > 0) {
59479
+ result[sourceName] = sourceEntity.instances;
59480
+ continue;
59481
+ }
59482
+ result[sourceName] = Array.from(
59483
+ { length: 10 },
59484
+ (_, i) => generateEntityRow(sourceEntity, i + 1)
59485
+ );
59486
+ }
59487
+ }
59374
59488
  perfEnd("build-mock-data", t, { orbitalCount: schema.orbitals.length, entityCount: Object.keys(result).length });
59375
59489
  return result;
59376
59490
  }
@@ -60336,12 +60450,13 @@ var OrbPreviewNodeInner = (props) => {
60336
60450
  const eventSources = data.eventSources ?? [];
60337
60451
  const layerColor = data.layer ? LAYER_COLORS[data.layer] : void 0;
60338
60452
  const isExpanded = Boolean(data.traitName);
60453
+ const isImportedGroup = Boolean(data.behaviorAlias);
60339
60454
  const status = data.status ?? "idle";
60340
60455
  const isRunning = status === "running";
60341
60456
  const isSuccess = status === "success";
60342
60457
  const isError = status === "error";
60343
- const label = isExpanded ? `${data.transitionEvent ?? ""}` : data.orbitalName;
60344
- const sublabel = isExpanded ? `${data.fromState ?? ""} \u2192 ${data.toState ?? ""}` : data.entityName ?? "";
60458
+ const label = isImportedGroup ? data.behaviorAlias ?? "" : isExpanded ? `${data.transitionEvent ?? ""}` : data.orbitalName;
60459
+ const sublabel = isImportedGroup ? `${data.behaviorName ?? ""}${typeof data.transitionCount === "number" && data.transitionCount > 1 ? ` \xB7 ${data.transitionCount} screens` : ""}` : isExpanded ? `${data.fromState ?? ""} \u2192 ${data.toState ?? ""}` : data.entityName ?? "";
60345
60460
  const orbitalSchema = React96.useMemo(() => {
60346
60461
  const fullSchema = data._fullSchema;
60347
60462
  if (!fullSchema) return void 0;
@@ -61760,6 +61875,7 @@ function FlowCanvasInner({
61760
61875
  const [expandedOrbital, setExpandedOrbital] = React96.useState(
61761
61876
  initialOrbital
61762
61877
  );
61878
+ const [expandedBehaviorAlias, setExpandedBehaviorAlias] = React96.useState(void 0);
61763
61879
  const screenSizeUserOverrideRef = React96__namespace.default.useRef(false);
61764
61880
  const [screenSize, setScreenSize] = React96.useState(
61765
61881
  () => typeof window === "undefined" ? "laptop" : detectScreenSize(window.innerWidth)
@@ -61787,15 +61903,17 @@ function FlowCanvasInner({
61787
61903
  }
61788
61904
  }), [selectedPattern]);
61789
61905
  const [atBehaviorLevel, setAtBehaviorLevel] = React96.useState(composeLevel === "behavior");
61790
- const { composeNodes, composeEdges, overviewNodes, overviewEdges, expandedNodes, expandedEdges } = React96.useMemo(() => {
61906
+ const { composeNodes, composeEdges, overviewNodes, overviewEdges, expandedNodes, expandedEdges, behaviorExpandedNodes, behaviorExpandedEdges } = React96.useMemo(() => {
61791
61907
  const t = perfStart("compose-graph");
61792
61908
  const compose = composeLevel === "behavior" && behaviorEntries?.length ? behaviorsToComposeGraph(behaviorEntries, behaviorWires ?? [], layoutHint) : { nodes: [], edges: [] };
61793
61909
  const overview = schemaToOverviewGraph(parsedSchema, mockData, behaviorMeta, layoutHint, orbitalStatus);
61794
61910
  const expanded = expandedOrbital ? orbitalToExpandedGraph(parsedSchema, expandedOrbital, mockData) : { nodes: [], edges: [] };
61911
+ const behaviorExpanded = expandedOrbital && expandedBehaviorAlias ? orbitalAliasToExpandedGraph(parsedSchema, expandedOrbital, expandedBehaviorAlias, mockData) : { nodes: [], edges: [] };
61795
61912
  perfEnd("compose-graph", t, {
61796
61913
  composeNodes: compose.nodes.length,
61797
61914
  overviewNodes: overview.nodes.length,
61798
61915
  expandedNodes: expanded.nodes.length,
61916
+ behaviorExpandedNodes: behaviorExpanded.nodes.length,
61799
61917
  orbitalCount: parsedSchema.orbitals?.length ?? 0
61800
61918
  });
61801
61919
  return {
@@ -61804,11 +61922,13 @@ function FlowCanvasInner({
61804
61922
  overviewNodes: overview.nodes,
61805
61923
  overviewEdges: overview.edges,
61806
61924
  expandedNodes: expanded.nodes,
61807
- expandedEdges: expanded.edges
61925
+ expandedEdges: expanded.edges,
61926
+ behaviorExpandedNodes: behaviorExpanded.nodes,
61927
+ behaviorExpandedEdges: behaviorExpanded.edges
61808
61928
  };
61809
- }, [parsedSchema, expandedOrbital, behaviorMeta, layoutHint, composeLevel, behaviorEntries, behaviorWires, mockData, orbitalStatus]);
61810
- const activeNodes = atBehaviorLevel && composeNodes.length > 0 ? composeNodes : level === "overview" ? overviewNodes : expandedNodes;
61811
- const activeEdges = atBehaviorLevel && composeEdges.length > 0 ? composeEdges : level === "overview" ? overviewEdges : expandedEdges;
61929
+ }, [parsedSchema, expandedOrbital, expandedBehaviorAlias, behaviorMeta, layoutHint, composeLevel, behaviorEntries, behaviorWires, mockData, orbitalStatus]);
61930
+ const activeNodes = atBehaviorLevel && composeNodes.length > 0 ? composeNodes : level === "overview" ? overviewNodes : level === "behavior-expanded" ? behaviorExpandedNodes : expandedNodes;
61931
+ const activeEdges = atBehaviorLevel && composeEdges.length > 0 ? composeEdges : level === "overview" ? overviewEdges : level === "behavior-expanded" ? behaviorExpandedEdges : expandedEdges;
61812
61932
  const [nodes, setNodes, onNodesChange] = react.useNodesState(activeNodes);
61813
61933
  const [edges, setEdges, onEdgesChange] = react.useEdgesState(activeEdges);
61814
61934
  const reactFlow = react.useReactFlow();
@@ -61847,6 +61967,15 @@ function FlowCanvasInner({
61847
61967
  onLevelChange?.("expanded", orbitalName);
61848
61968
  return;
61849
61969
  }
61970
+ if (level === "expanded") {
61971
+ const d = node.data;
61972
+ if (d.behaviorAlias && d.orbitalName) {
61973
+ setExpandedBehaviorAlias(d.behaviorAlias);
61974
+ setLevel("behavior-expanded");
61975
+ onLevelChange?.("behavior-expanded", d.orbitalName);
61976
+ return;
61977
+ }
61978
+ }
61850
61979
  if (level === "expanded") {
61851
61980
  const d = node.data;
61852
61981
  const orbitalName = d.orbitalName ?? node.id;
@@ -61881,6 +62010,10 @@ function FlowCanvasInner({
61881
62010
  if (e.key === "Escape") {
61882
62011
  if (selectedNode) {
61883
62012
  setSelectedNode(null);
62013
+ } else if (level === "behavior-expanded") {
62014
+ setLevel("expanded");
62015
+ setExpandedBehaviorAlias(void 0);
62016
+ onLevelChange?.("expanded", expandedOrbital);
61884
62017
  } else if (level === "expanded") {
61885
62018
  setLevel("overview");
61886
62019
  setExpandedOrbital(void 0);
@@ -61897,7 +62030,7 @@ function FlowCanvasInner({
61897
62030
  setSelectedPattern(null);
61898
62031
  }
61899
62032
  }
61900
- }, [level, onLevelChange, selectedNode, selectedPattern, onPatternDelete]);
62033
+ }, [level, onLevelChange, selectedNode, selectedPattern, onPatternDelete, atBehaviorLevel, composeLevel, expandedOrbital]);
61901
62034
  React96.useEffect(() => {
61902
62035
  document.addEventListener("keydown", handleKeyDown);
61903
62036
  return () => document.removeEventListener("keydown", handleKeyDown);
@@ -61905,6 +62038,11 @@ function FlowCanvasInner({
61905
62038
  const handleGoBack = React96.useCallback(() => {
61906
62039
  if (selectedNode) {
61907
62040
  setSelectedNode(null);
62041
+ } else if (level === "behavior-expanded") {
62042
+ setLevel("expanded");
62043
+ setExpandedBehaviorAlias(void 0);
62044
+ setSelectedNode(null);
62045
+ onLevelChange?.("expanded", expandedOrbital);
61908
62046
  } else if (level === "expanded") {
61909
62047
  setLevel("overview");
61910
62048
  setExpandedOrbital(void 0);
@@ -61915,7 +62053,7 @@ function FlowCanvasInner({
61915
62053
  setExpandedOrbital(void 0);
61916
62054
  setSelectedNode(null);
61917
62055
  }
61918
- }, [level, onLevelChange, selectedNode, composeLevel, atBehaviorLevel]);
62056
+ }, [level, onLevelChange, selectedNode, composeLevel, atBehaviorLevel, expandedOrbital]);
61919
62057
  const eventBus = useEventBus();
61920
62058
  const handleConnect = React96.useCallback((connection) => {
61921
62059
  if (!connection.sourceHandle?.startsWith("event-") || !onEventWire) return;
@@ -830,8 +830,18 @@ declare const AvlBindingEdge: React__default.FC<EdgeProps>;
830
830
  * Uses @almadar/core types for schema-level constructs.
831
831
  */
832
832
 
833
- /** The two navigation levels of the FlowCanvas. */
834
- type ViewLevel = 'overview' | 'expanded';
833
+ /**
834
+ * The navigation levels of the FlowCanvas.
835
+ *
836
+ * - `overview` (L1) — one card per orbital, showing the orbital's composed
837
+ * render-ui at INIT.
838
+ * - `expanded` (L2) — one card per organism-authored transition + one
839
+ * grouped card per imported-behavior alias.
840
+ * - `behavior-expanded` (L3) — drilled into a single imported behavior at
841
+ * L2: one card per transition of THAT behavior. Used to inspect what an
842
+ * import contributes without leaving the canvas.
843
+ */
844
+ type ViewLevel = 'overview' | 'expanded' | 'behavior-expanded';
835
845
  /**
836
846
  * An interactive element inside a render-ui pattern that fires an event.
837
847
  * For example, a button with `event: "CHECKOUT"` is an event source.
@@ -923,6 +933,27 @@ interface PreviewNodeData extends Record<string, unknown> {
923
933
  * "project schema".
924
934
  */
925
935
  sourceSchemaName?: string;
936
+ /**
937
+ * Set when this L2 card represents a grouped imported-behavior bucket
938
+ * (one card per `uses[]` alias instead of one per transition). The alias
939
+ * comes from `Trait.sourceBehavior.alias` populated by the inline phase.
940
+ * Double-clicking such a card drills into L3 (`behavior-expanded`) to
941
+ * see that alias's transitions.
942
+ *
943
+ * Absent on organism-authored transition cards and L1 overview cards.
944
+ */
945
+ behaviorAlias?: string;
946
+ /**
947
+ * Human-readable behavior name for a grouped imported-behavior card
948
+ * (e.g. `'std/behaviors/std-stat-card'`). Comes from
949
+ * `Trait.sourceBehavior.behavior`. Renderer surfaces it as a label.
950
+ */
951
+ behaviorName?: string;
952
+ /**
953
+ * How many transitions are collapsed into this grouped imported-behavior
954
+ * card. Renderer can surface it as a "+N screens" badge.
955
+ */
956
+ transitionCount?: number;
926
957
  }
927
958
  /** Data for event flow edges. */
928
959
  interface EventEdgeData extends Record<string, unknown> {
@@ -975,10 +1006,15 @@ declare function schemaToOverviewGraph(schema: OrbitalSchema, mockData?: Record<
975
1006
  edges: Edge<EventEdgeData>[];
976
1007
  };
977
1008
  /**
978
- * Build a React Flow graph for the expanded level.
979
- * Each transition with a render-ui effect gets a node.
980
- * Edges connect from the specific button/pattern that fires the event
981
- * to the target screen node.
1009
+ * Build a React Flow graph for the L2 (expanded) level.
1010
+ *
1011
+ * Organism-authored traits (no `sourceBehavior` metadata) emit one
1012
+ * transition card per render-ui-bearing transition (the historical
1013
+ * behavior). Imported traits (cloned by the inline phase from `uses[]`)
1014
+ * collapse into one grouped card per `sourceBehavior.alias`, so the user
1015
+ * sees `Stats`, `Graphs`, `Layout`, etc. as single cards rather than 9
1016
+ * anonymous `INIT` peers (STUDIO-1). Drill into a grouped card to reach
1017
+ * L3 (`orbitalAliasToExpandedGraph`).
982
1018
  */
983
1019
  declare function orbitalToExpandedGraph(schema: OrbitalSchema, orbitalName: string, mockData?: Record<string, unknown[]>): {
984
1020
  nodes: Node<PreviewNodeData>[];
package/dist/avl/index.js CHANGED
@@ -57554,46 +57554,46 @@ function schemaToOverviewGraph(schema, mockData, behaviorMeta, layoutHint, orbit
57554
57554
  }
57555
57555
  return { nodes, edges };
57556
57556
  }
57557
- function orbitalToExpandedGraph(schema, orbitalName, mockData) {
57558
- const nodes = [];
57559
- const edges = [];
57560
- const orbital = getOrbitals(schema).find((o) => o.name === orbitalName);
57561
- if (!orbital) return { nodes, edges };
57562
- const entityInfo = getEntityInfo(orbital);
57563
- const traits2 = getTraits2(orbital);
57564
- const uiTransitions = [];
57565
- for (const trait of traits2) {
57557
+ function collectUITransitions(orbital, filter) {
57558
+ const out = [];
57559
+ for (const trait of getTraits2(orbital)) {
57560
+ if (!filter(trait)) continue;
57566
57561
  const sm = getStateMachine2(trait);
57567
57562
  if (!sm) continue;
57568
57563
  for (const t of sm.transitions) {
57569
57564
  if (!t.effects) continue;
57570
57565
  const patterns = extractRenderUI(t.effects);
57571
- if (patterns.length > 0) {
57572
- uiTransitions.push({
57573
- traitName: trait.name,
57574
- transition: t,
57575
- patterns,
57576
- eventSources: collectEventSources(patterns),
57577
- states: sm.states,
57578
- allTransitions: sm.transitions
57579
- });
57580
- }
57566
+ if (patterns.length === 0) continue;
57567
+ out.push({
57568
+ trait,
57569
+ traitName: trait.name,
57570
+ transition: t,
57571
+ patterns,
57572
+ eventSources: collectEventSources(patterns),
57573
+ states: sm.states,
57574
+ allTransitions: sm.transitions
57575
+ });
57581
57576
  }
57582
57577
  }
57583
- if (uiTransitions.length === 0) return { nodes, edges };
57578
+ return out;
57579
+ }
57580
+ function buildScreenGraph(schema, orbitalName, entityName, transitions, groupedBehaviors, mockData) {
57581
+ const nodes = [];
57582
+ const edges = [];
57584
57583
  const stateNodeMap = /* @__PURE__ */ new Map();
57585
- for (const entry of uiTransitions) {
57584
+ for (const entry of transitions) {
57586
57585
  const key = `${entry.traitName}:${entry.transition.to}`;
57587
57586
  const existing = stateNodeMap.get(key);
57588
57587
  if (!existing || entry.patterns.length > existing.patterns.length) {
57589
57588
  stateNodeMap.set(key, entry);
57590
57589
  }
57591
57590
  }
57592
- const entries = Array.from(stateNodeMap.values());
57593
- const cols = Math.min(entries.length, 3);
57591
+ const transitionEntries = Array.from(stateNodeMap.values());
57592
+ const totalCards = transitionEntries.length + groupedBehaviors.length;
57593
+ if (totalCards === 0) return { nodes, edges };
57594
+ const cols = Math.min(totalCards, 3);
57594
57595
  const nodeIdMap = /* @__PURE__ */ new Map();
57595
- for (let i = 0; i < entries.length; i++) {
57596
- const entry = entries[i];
57596
+ transitionEntries.forEach((entry, i) => {
57597
57597
  const t = entry.transition;
57598
57598
  const nodeId = `${orbitalName}-${entry.traitName}-${t.event}-${t.to}`;
57599
57599
  const stateKey = `${entry.traitName}:${t.to}`;
@@ -57616,13 +57616,48 @@ function orbitalToExpandedGraph(schema, orbitalName, mockData) {
57616
57616
  stateRole: detectStateRole(t.to, entry.states, entry.allTransitions),
57617
57617
  effectTypes: t.effects ? extractEffectTypes(t.effects) : [],
57618
57618
  guard: t.guard,
57619
- entityName: entityInfo.name,
57619
+ entityName,
57620
57620
  _fullSchema: schema,
57621
57621
  _mockData: mockData
57622
57622
  }
57623
57623
  });
57624
- }
57625
- for (const entry of uiTransitions) {
57624
+ });
57625
+ groupedBehaviors.forEach((group, i) => {
57626
+ const t = group.representative.transition;
57627
+ const nodeId = `${orbitalName}-behavior-${group.alias}`;
57628
+ const idx = transitionEntries.length + i;
57629
+ const col = idx % cols;
57630
+ const row = Math.floor(idx / cols);
57631
+ nodes.push({
57632
+ id: nodeId,
57633
+ type: "preview",
57634
+ position: { x: col * EXPANDED_SPACING_X, y: row * EXPANDED_SPACING_Y },
57635
+ data: {
57636
+ orbitalName,
57637
+ traitName: group.representative.traitName,
57638
+ stateName: t.to,
57639
+ transitionEvent: t.event,
57640
+ fromState: t.from,
57641
+ toState: t.to,
57642
+ patterns: group.representative.patterns,
57643
+ eventSources: group.representative.eventSources,
57644
+ stateRole: detectStateRole(
57645
+ t.to,
57646
+ group.representative.states,
57647
+ group.representative.allTransitions
57648
+ ),
57649
+ effectTypes: t.effects ? extractEffectTypes(t.effects) : [],
57650
+ guard: t.guard,
57651
+ entityName,
57652
+ _fullSchema: schema,
57653
+ _mockData: mockData,
57654
+ behaviorAlias: group.alias,
57655
+ behaviorName: group.behaviorName,
57656
+ transitionCount: group.transitionCount
57657
+ }
57658
+ });
57659
+ });
57660
+ for (const entry of transitions) {
57626
57661
  const t = entry.transition;
57627
57662
  const sourceKey = `${entry.traitName}:${t.from}`;
57628
57663
  const targetKey = `${entry.traitName}:${t.to}`;
@@ -57651,6 +57686,66 @@ function orbitalToExpandedGraph(schema, orbitalName, mockData) {
57651
57686
  }
57652
57687
  return { nodes, edges };
57653
57688
  }
57689
+ function orbitalToExpandedGraph(schema, orbitalName, mockData) {
57690
+ const orbital = getOrbitals(schema).find((o) => o.name === orbitalName);
57691
+ if (!orbital) return { nodes: [], edges: [] };
57692
+ const entityInfo = getEntityInfo(orbital);
57693
+ const organismTransitions = collectUITransitions(
57694
+ orbital,
57695
+ (trait) => trait.sourceBehavior === void 0
57696
+ );
57697
+ const aliasBuckets = /* @__PURE__ */ new Map();
57698
+ const importedTransitions = collectUITransitions(
57699
+ orbital,
57700
+ (trait) => trait.sourceBehavior !== void 0
57701
+ );
57702
+ for (const entry of importedTransitions) {
57703
+ const sb = entry.trait.sourceBehavior;
57704
+ if (!sb) continue;
57705
+ const bucket = aliasBuckets.get(sb.alias);
57706
+ if (bucket) {
57707
+ bucket.transitions.push(entry);
57708
+ } else {
57709
+ aliasBuckets.set(sb.alias, {
57710
+ behaviorName: sb.behavior,
57711
+ transitions: [entry]
57712
+ });
57713
+ }
57714
+ }
57715
+ const groupedBehaviors = Array.from(aliasBuckets.entries()).map(
57716
+ ([alias, bucket]) => ({
57717
+ alias,
57718
+ behaviorName: bucket.behaviorName,
57719
+ representative: bucket.transitions[0],
57720
+ transitionCount: bucket.transitions.length
57721
+ })
57722
+ );
57723
+ return buildScreenGraph(
57724
+ schema,
57725
+ orbitalName,
57726
+ entityInfo.name,
57727
+ organismTransitions,
57728
+ groupedBehaviors,
57729
+ mockData
57730
+ );
57731
+ }
57732
+ function orbitalAliasToExpandedGraph(schema, orbitalName, alias, mockData) {
57733
+ const orbital = getOrbitals(schema).find((o) => o.name === orbitalName);
57734
+ if (!orbital) return { nodes: [], edges: [] };
57735
+ const entityInfo = getEntityInfo(orbital);
57736
+ const transitions = collectUITransitions(
57737
+ orbital,
57738
+ (trait) => trait.sourceBehavior?.alias === alias
57739
+ );
57740
+ return buildScreenGraph(
57741
+ schema,
57742
+ orbitalName,
57743
+ entityInfo.name,
57744
+ transitions,
57745
+ [],
57746
+ mockData
57747
+ );
57748
+ }
57654
57749
 
57655
57750
  // components/molecules/avl/OrbPreviewNode.tsx
57656
57751
  init_Box();
@@ -59325,6 +59420,25 @@ function buildMockData(schema) {
59325
59420
  );
59326
59421
  result[entityName] = rows;
59327
59422
  }
59423
+ for (const orbital of schema.orbitals) {
59424
+ for (const traitRef of orbital.traits ?? []) {
59425
+ if (typeof traitRef === "string") continue;
59426
+ if (!("stateMachine" in traitRef)) continue;
59427
+ const trait = traitRef;
59428
+ const sourceEntity = trait.sourceEntityDefinition;
59429
+ if (!sourceEntity || isEntityCall(sourceEntity)) continue;
59430
+ const sourceName = sourceEntity.name;
59431
+ if (!sourceName || result[sourceName]) continue;
59432
+ if (sourceEntity.instances && sourceEntity.instances.length > 0) {
59433
+ result[sourceName] = sourceEntity.instances;
59434
+ continue;
59435
+ }
59436
+ result[sourceName] = Array.from(
59437
+ { length: 10 },
59438
+ (_, i) => generateEntityRow(sourceEntity, i + 1)
59439
+ );
59440
+ }
59441
+ }
59328
59442
  perfEnd("build-mock-data", t, { orbitalCount: schema.orbitals.length, entityCount: Object.keys(result).length });
59329
59443
  return result;
59330
59444
  }
@@ -60290,12 +60404,13 @@ var OrbPreviewNodeInner = (props) => {
60290
60404
  const eventSources = data.eventSources ?? [];
60291
60405
  const layerColor = data.layer ? LAYER_COLORS[data.layer] : void 0;
60292
60406
  const isExpanded = Boolean(data.traitName);
60407
+ const isImportedGroup = Boolean(data.behaviorAlias);
60293
60408
  const status = data.status ?? "idle";
60294
60409
  const isRunning = status === "running";
60295
60410
  const isSuccess = status === "success";
60296
60411
  const isError = status === "error";
60297
- const label = isExpanded ? `${data.transitionEvent ?? ""}` : data.orbitalName;
60298
- const sublabel = isExpanded ? `${data.fromState ?? ""} \u2192 ${data.toState ?? ""}` : data.entityName ?? "";
60412
+ const label = isImportedGroup ? data.behaviorAlias ?? "" : isExpanded ? `${data.transitionEvent ?? ""}` : data.orbitalName;
60413
+ const sublabel = isImportedGroup ? `${data.behaviorName ?? ""}${typeof data.transitionCount === "number" && data.transitionCount > 1 ? ` \xB7 ${data.transitionCount} screens` : ""}` : isExpanded ? `${data.fromState ?? ""} \u2192 ${data.toState ?? ""}` : data.entityName ?? "";
60299
60414
  const orbitalSchema = useMemo(() => {
60300
60415
  const fullSchema = data._fullSchema;
60301
60416
  if (!fullSchema) return void 0;
@@ -61714,6 +61829,7 @@ function FlowCanvasInner({
61714
61829
  const [expandedOrbital, setExpandedOrbital] = useState(
61715
61830
  initialOrbital
61716
61831
  );
61832
+ const [expandedBehaviorAlias, setExpandedBehaviorAlias] = useState(void 0);
61717
61833
  const screenSizeUserOverrideRef = React96__default.useRef(false);
61718
61834
  const [screenSize, setScreenSize] = useState(
61719
61835
  () => typeof window === "undefined" ? "laptop" : detectScreenSize(window.innerWidth)
@@ -61741,15 +61857,17 @@ function FlowCanvasInner({
61741
61857
  }
61742
61858
  }), [selectedPattern]);
61743
61859
  const [atBehaviorLevel, setAtBehaviorLevel] = useState(composeLevel === "behavior");
61744
- const { composeNodes, composeEdges, overviewNodes, overviewEdges, expandedNodes, expandedEdges } = useMemo(() => {
61860
+ const { composeNodes, composeEdges, overviewNodes, overviewEdges, expandedNodes, expandedEdges, behaviorExpandedNodes, behaviorExpandedEdges } = useMemo(() => {
61745
61861
  const t = perfStart("compose-graph");
61746
61862
  const compose = composeLevel === "behavior" && behaviorEntries?.length ? behaviorsToComposeGraph(behaviorEntries, behaviorWires ?? [], layoutHint) : { nodes: [], edges: [] };
61747
61863
  const overview = schemaToOverviewGraph(parsedSchema, mockData, behaviorMeta, layoutHint, orbitalStatus);
61748
61864
  const expanded = expandedOrbital ? orbitalToExpandedGraph(parsedSchema, expandedOrbital, mockData) : { nodes: [], edges: [] };
61865
+ const behaviorExpanded = expandedOrbital && expandedBehaviorAlias ? orbitalAliasToExpandedGraph(parsedSchema, expandedOrbital, expandedBehaviorAlias, mockData) : { nodes: [], edges: [] };
61749
61866
  perfEnd("compose-graph", t, {
61750
61867
  composeNodes: compose.nodes.length,
61751
61868
  overviewNodes: overview.nodes.length,
61752
61869
  expandedNodes: expanded.nodes.length,
61870
+ behaviorExpandedNodes: behaviorExpanded.nodes.length,
61753
61871
  orbitalCount: parsedSchema.orbitals?.length ?? 0
61754
61872
  });
61755
61873
  return {
@@ -61758,11 +61876,13 @@ function FlowCanvasInner({
61758
61876
  overviewNodes: overview.nodes,
61759
61877
  overviewEdges: overview.edges,
61760
61878
  expandedNodes: expanded.nodes,
61761
- expandedEdges: expanded.edges
61879
+ expandedEdges: expanded.edges,
61880
+ behaviorExpandedNodes: behaviorExpanded.nodes,
61881
+ behaviorExpandedEdges: behaviorExpanded.edges
61762
61882
  };
61763
- }, [parsedSchema, expandedOrbital, behaviorMeta, layoutHint, composeLevel, behaviorEntries, behaviorWires, mockData, orbitalStatus]);
61764
- const activeNodes = atBehaviorLevel && composeNodes.length > 0 ? composeNodes : level === "overview" ? overviewNodes : expandedNodes;
61765
- const activeEdges = atBehaviorLevel && composeEdges.length > 0 ? composeEdges : level === "overview" ? overviewEdges : expandedEdges;
61883
+ }, [parsedSchema, expandedOrbital, expandedBehaviorAlias, behaviorMeta, layoutHint, composeLevel, behaviorEntries, behaviorWires, mockData, orbitalStatus]);
61884
+ const activeNodes = atBehaviorLevel && composeNodes.length > 0 ? composeNodes : level === "overview" ? overviewNodes : level === "behavior-expanded" ? behaviorExpandedNodes : expandedNodes;
61885
+ const activeEdges = atBehaviorLevel && composeEdges.length > 0 ? composeEdges : level === "overview" ? overviewEdges : level === "behavior-expanded" ? behaviorExpandedEdges : expandedEdges;
61766
61886
  const [nodes, setNodes, onNodesChange] = useNodesState(activeNodes);
61767
61887
  const [edges, setEdges, onEdgesChange] = useEdgesState(activeEdges);
61768
61888
  const reactFlow = useReactFlow();
@@ -61801,6 +61921,15 @@ function FlowCanvasInner({
61801
61921
  onLevelChange?.("expanded", orbitalName);
61802
61922
  return;
61803
61923
  }
61924
+ if (level === "expanded") {
61925
+ const d = node.data;
61926
+ if (d.behaviorAlias && d.orbitalName) {
61927
+ setExpandedBehaviorAlias(d.behaviorAlias);
61928
+ setLevel("behavior-expanded");
61929
+ onLevelChange?.("behavior-expanded", d.orbitalName);
61930
+ return;
61931
+ }
61932
+ }
61804
61933
  if (level === "expanded") {
61805
61934
  const d = node.data;
61806
61935
  const orbitalName = d.orbitalName ?? node.id;
@@ -61835,6 +61964,10 @@ function FlowCanvasInner({
61835
61964
  if (e.key === "Escape") {
61836
61965
  if (selectedNode) {
61837
61966
  setSelectedNode(null);
61967
+ } else if (level === "behavior-expanded") {
61968
+ setLevel("expanded");
61969
+ setExpandedBehaviorAlias(void 0);
61970
+ onLevelChange?.("expanded", expandedOrbital);
61838
61971
  } else if (level === "expanded") {
61839
61972
  setLevel("overview");
61840
61973
  setExpandedOrbital(void 0);
@@ -61851,7 +61984,7 @@ function FlowCanvasInner({
61851
61984
  setSelectedPattern(null);
61852
61985
  }
61853
61986
  }
61854
- }, [level, onLevelChange, selectedNode, selectedPattern, onPatternDelete]);
61987
+ }, [level, onLevelChange, selectedNode, selectedPattern, onPatternDelete, atBehaviorLevel, composeLevel, expandedOrbital]);
61855
61988
  useEffect(() => {
61856
61989
  document.addEventListener("keydown", handleKeyDown);
61857
61990
  return () => document.removeEventListener("keydown", handleKeyDown);
@@ -61859,6 +61992,11 @@ function FlowCanvasInner({
61859
61992
  const handleGoBack = useCallback(() => {
61860
61993
  if (selectedNode) {
61861
61994
  setSelectedNode(null);
61995
+ } else if (level === "behavior-expanded") {
61996
+ setLevel("expanded");
61997
+ setExpandedBehaviorAlias(void 0);
61998
+ setSelectedNode(null);
61999
+ onLevelChange?.("expanded", expandedOrbital);
61862
62000
  } else if (level === "expanded") {
61863
62001
  setLevel("overview");
61864
62002
  setExpandedOrbital(void 0);
@@ -61869,7 +62007,7 @@ function FlowCanvasInner({
61869
62007
  setExpandedOrbital(void 0);
61870
62008
  setSelectedNode(null);
61871
62009
  }
61872
- }, [level, onLevelChange, selectedNode, composeLevel, atBehaviorLevel]);
62010
+ }, [level, onLevelChange, selectedNode, composeLevel, atBehaviorLevel, expandedOrbital]);
61873
62011
  const eventBus = useEventBus();
61874
62012
  const handleConnect = useCallback((connection) => {
61875
62013
  if (!connection.sourceHandle?.startsWith("event-") || !onEventWire) return;
@@ -26,12 +26,28 @@ export declare function schemaToOverviewGraph(schema: OrbitalSchema, mockData?:
26
26
  edges: Edge<EventEdgeData>[];
27
27
  };
28
28
  /**
29
- * Build a React Flow graph for the expanded level.
30
- * Each transition with a render-ui effect gets a node.
31
- * Edges connect from the specific button/pattern that fires the event
32
- * to the target screen node.
29
+ * Build a React Flow graph for the L2 (expanded) level.
30
+ *
31
+ * Organism-authored traits (no `sourceBehavior` metadata) emit one
32
+ * transition card per render-ui-bearing transition (the historical
33
+ * behavior). Imported traits (cloned by the inline phase from `uses[]`)
34
+ * collapse into one grouped card per `sourceBehavior.alias`, so the user
35
+ * sees `Stats`, `Graphs`, `Layout`, etc. as single cards rather than 9
36
+ * anonymous `INIT` peers (STUDIO-1). Drill into a grouped card to reach
37
+ * L3 (`orbitalAliasToExpandedGraph`).
33
38
  */
34
39
  export declare function orbitalToExpandedGraph(schema: OrbitalSchema, orbitalName: string, mockData?: Record<string, unknown[]>): {
35
40
  nodes: Node<PreviewNodeData>[];
36
41
  edges: Edge<EventEdgeData>[];
37
42
  };
43
+ /**
44
+ * Build a React Flow graph for the L3 (`behavior-expanded`) level: drill
45
+ * into one imported-behavior alias on an orbital and show ITS render-ui
46
+ * transitions as individual cards. Same converter logic as
47
+ * `orbitalToExpandedGraph` but scoped to traits where
48
+ * `sourceBehavior.alias === alias`. STUDIO-1.
49
+ */
50
+ export declare function orbitalAliasToExpandedGraph(schema: OrbitalSchema, orbitalName: string, alias: string, mockData?: Record<string, unknown[]>): {
51
+ nodes: Node<PreviewNodeData>[];
52
+ edges: Edge<EventEdgeData>[];
53
+ };
@@ -8,8 +8,18 @@
8
8
  * Uses @almadar/core types for schema-level constructs.
9
9
  */
10
10
  import type { Expression, UISlot } from '@almadar/core';
11
- /** The two navigation levels of the FlowCanvas. */
12
- export type ViewLevel = 'overview' | 'expanded';
11
+ /**
12
+ * The navigation levels of the FlowCanvas.
13
+ *
14
+ * - `overview` (L1) — one card per orbital, showing the orbital's composed
15
+ * render-ui at INIT.
16
+ * - `expanded` (L2) — one card per organism-authored transition + one
17
+ * grouped card per imported-behavior alias.
18
+ * - `behavior-expanded` (L3) — drilled into a single imported behavior at
19
+ * L2: one card per transition of THAT behavior. Used to inspect what an
20
+ * import contributes without leaving the canvas.
21
+ */
22
+ export type ViewLevel = 'overview' | 'expanded' | 'behavior-expanded';
13
23
  /**
14
24
  * Screen size preset for OrbPreview rendering inside nodes.
15
25
  *
@@ -131,6 +141,27 @@ export interface PreviewNodeData extends Record<string, unknown> {
131
141
  * "project schema".
132
142
  */
133
143
  sourceSchemaName?: string;
144
+ /**
145
+ * Set when this L2 card represents a grouped imported-behavior bucket
146
+ * (one card per `uses[]` alias instead of one per transition). The alias
147
+ * comes from `Trait.sourceBehavior.alias` populated by the inline phase.
148
+ * Double-clicking such a card drills into L3 (`behavior-expanded`) to
149
+ * see that alias's transitions.
150
+ *
151
+ * Absent on organism-authored transition cards and L1 overview cards.
152
+ */
153
+ behaviorAlias?: string;
154
+ /**
155
+ * Human-readable behavior name for a grouped imported-behavior card
156
+ * (e.g. `'std/behaviors/std-stat-card'`). Comes from
157
+ * `Trait.sourceBehavior.behavior`. Renderer surfaces it as a label.
158
+ */
159
+ behaviorName?: string;
160
+ /**
161
+ * How many transitions are collapsed into this grouped imported-behavior
162
+ * card. Renderer can surface it as a "+N screens" badge.
163
+ */
164
+ transitionCount?: number;
134
165
  }
135
166
  /** Data for event flow edges. */
136
167
  export interface EventEdgeData extends Record<string, unknown> {
@@ -47255,6 +47255,25 @@ function buildMockData(schema) {
47255
47255
  );
47256
47256
  result[entityName] = rows;
47257
47257
  }
47258
+ for (const orbital of schema.orbitals) {
47259
+ for (const traitRef of orbital.traits ?? []) {
47260
+ if (typeof traitRef === "string") continue;
47261
+ if (!("stateMachine" in traitRef)) continue;
47262
+ const trait = traitRef;
47263
+ const sourceEntity = trait.sourceEntityDefinition;
47264
+ if (!sourceEntity || core.isEntityCall(sourceEntity)) continue;
47265
+ const sourceName = sourceEntity.name;
47266
+ if (!sourceName || result[sourceName]) continue;
47267
+ if (sourceEntity.instances && sourceEntity.instances.length > 0) {
47268
+ result[sourceName] = sourceEntity.instances;
47269
+ continue;
47270
+ }
47271
+ result[sourceName] = Array.from(
47272
+ { length: 10 },
47273
+ (_, i) => generateEntityRow(sourceEntity, i + 1)
47274
+ );
47275
+ }
47276
+ }
47258
47277
  perfEnd("build-mock-data", t, { orbitalCount: schema.orbitals.length, entityCount: Object.keys(result).length });
47259
47278
  return result;
47260
47279
  }
@@ -47209,6 +47209,25 @@ function buildMockData(schema) {
47209
47209
  );
47210
47210
  result[entityName] = rows;
47211
47211
  }
47212
+ for (const orbital of schema.orbitals) {
47213
+ for (const traitRef of orbital.traits ?? []) {
47214
+ if (typeof traitRef === "string") continue;
47215
+ if (!("stateMachine" in traitRef)) continue;
47216
+ const trait = traitRef;
47217
+ const sourceEntity = trait.sourceEntityDefinition;
47218
+ if (!sourceEntity || isEntityCall(sourceEntity)) continue;
47219
+ const sourceName = sourceEntity.name;
47220
+ if (!sourceName || result[sourceName]) continue;
47221
+ if (sourceEntity.instances && sourceEntity.instances.length > 0) {
47222
+ result[sourceName] = sourceEntity.instances;
47223
+ continue;
47224
+ }
47225
+ result[sourceName] = Array.from(
47226
+ { length: 10 },
47227
+ (_, i) => generateEntityRow(sourceEntity, i + 1)
47228
+ );
47229
+ }
47230
+ }
47212
47231
  perfEnd("build-mock-data", t, { orbitalCount: schema.orbitals.length, entityCount: Object.keys(result).length });
47213
47232
  return result;
47214
47233
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "4.53.7",
3
+ "version": "4.54.1",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -129,7 +129,7 @@
129
129
  "typecheck": "tsc --noEmit"
130
130
  },
131
131
  "dependencies": {
132
- "@almadar/core": ">=7.13.0",
132
+ "@almadar/core": "^7.16.0",
133
133
  "@almadar/evaluator": ">=2.9.2",
134
134
  "@almadar/logger": "^1.3.0",
135
135
  "@almadar/patterns": "^2.26.0",
@@ -167,7 +167,6 @@
167
167
  },
168
168
  "devDependencies": {
169
169
  "@almadar/eslint-plugin": ">=2.3.0",
170
- "@tailwindcss/container-queries": "^0.1.1",
171
170
  "@react-three/drei": "^10.7.7",
172
171
  "@react-three/fiber": "^9.6.0",
173
172
  "@react-three/postprocessing": "3.0.4",
@@ -176,6 +175,7 @@
176
175
  "@storybook/addon-themes": "^10.2.6",
177
176
  "@storybook/react": "^10.2.6",
178
177
  "@storybook/react-vite": "^10.2.6",
178
+ "@tailwindcss/container-queries": "^0.1.1",
179
179
  "@tanstack/react-query": "^5.0.0",
180
180
  "@testing-library/jest-dom": "^6.4.0",
181
181
  "@testing-library/react": "^14.2.0",