@almadar/ui 2.45.0 → 2.46.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.
package/dist/avl/index.js CHANGED
@@ -3178,6 +3178,7 @@ var init_Box = __esm({
3178
3178
  action,
3179
3179
  actionPayload,
3180
3180
  hoverEvent,
3181
+ maxWidth,
3181
3182
  onClick,
3182
3183
  onMouseEnter,
3183
3184
  onMouseLeave,
@@ -3242,6 +3243,7 @@ var init_Box = __esm({
3242
3243
  onClick: isClickable ? handleClick : void 0,
3243
3244
  onMouseEnter: hoverEvent || onMouseEnter ? handleMouseEnter : void 0,
3244
3245
  onMouseLeave: hoverEvent || onMouseLeave ? handleMouseLeave : void 0,
3246
+ style: maxWidth ? { maxWidth, ...rest.style } : rest.style,
3245
3247
  ...rest,
3246
3248
  children
3247
3249
  }
@@ -12830,12 +12832,12 @@ function findCrossLinks(orbitals) {
12830
12832
  }
12831
12833
  return links;
12832
12834
  }
12833
- function schemaToOverviewGraph(schema, mockData) {
12835
+ function schemaToOverviewGraph(schema, mockData, behaviorMeta, layoutHint) {
12834
12836
  const orbitals = getOrbitals(schema);
12835
12837
  const nodes = [];
12836
12838
  const edges = [];
12837
12839
  const count = orbitals.length;
12838
- const cols = Math.ceil(Math.sqrt(count));
12840
+ const cols = layoutHint === "pipeline" ? count : Math.ceil(Math.sqrt(count));
12839
12841
  for (let i = 0; i < orbitals.length; i++) {
12840
12842
  const orb = orbitals[i];
12841
12843
  const entityInfo = getEntityInfo(orb);
@@ -12874,6 +12876,22 @@ function schemaToOverviewGraph(schema, mockData) {
12874
12876
  }
12875
12877
  }
12876
12878
  const eventSources = collectEventSources(initPatterns);
12879
+ for (const source of eventSources) {
12880
+ for (const trait of traits2) {
12881
+ const sm = getStateMachine2(trait);
12882
+ if (!sm) continue;
12883
+ const smEvents = trait.stateMachine?.events ?? [];
12884
+ const matchingEvent = smEvents.find((ev) => ev.key === source.event);
12885
+ if (matchingEvent?.payload && Array.isArray(matchingEvent.payload)) {
12886
+ source.payloadFields = matchingEvent.payload.map((p2) => ({
12887
+ name: String(p2.name ?? ""),
12888
+ type: String(p2.type ?? "string"),
12889
+ ...p2.required ? { required: true } : {}
12890
+ }));
12891
+ break;
12892
+ }
12893
+ }
12894
+ }
12877
12895
  const col = i % cols;
12878
12896
  const row = Math.floor(i / cols);
12879
12897
  nodes.push({
@@ -12885,6 +12903,7 @@ function schemaToOverviewGraph(schema, mockData) {
12885
12903
  patterns: initPatterns,
12886
12904
  eventSources,
12887
12905
  effectTypes: initEffectTypes,
12906
+ layer: behaviorMeta?.[orb.name]?.layer,
12888
12907
  stateRole: "initial",
12889
12908
  entityName: entityInfo.name,
12890
12909
  persistence: entityInfo.persistence,
@@ -47147,6 +47166,43 @@ OrbPreview.displayName = "OrbPreview";
47147
47166
 
47148
47167
  // components/molecules/avl/OrbPreviewNode.tsx
47149
47168
  init_useEventBus();
47169
+
47170
+ // components/molecules/avl/wire-validation.ts
47171
+ function validateWire(sourcePayload, targetPayload) {
47172
+ const warnings = [];
47173
+ if (!sourcePayload?.length && !targetPayload?.length) {
47174
+ return { valid: true, warnings };
47175
+ }
47176
+ if (sourcePayload?.length && !targetPayload?.length) {
47177
+ return { valid: true, warnings };
47178
+ }
47179
+ if (!sourcePayload?.length && targetPayload?.length) {
47180
+ const requiredFields = targetPayload.filter((f3) => f3.required);
47181
+ if (requiredFields.length > 0) {
47182
+ warnings.push(`Missing required fields: ${requiredFields.map((f3) => f3.name).join(", ")}`);
47183
+ }
47184
+ return { valid: warnings.length === 0, warnings };
47185
+ }
47186
+ const sourceFields = new Map(sourcePayload.map((f3) => [f3.name, f3]));
47187
+ for (const targetField of targetPayload) {
47188
+ const sourceField = sourceFields.get(targetField.name);
47189
+ if (!sourceField) {
47190
+ if (targetField.required) {
47191
+ warnings.push(`Missing required field: ${targetField.name} (${targetField.type})`);
47192
+ }
47193
+ } else if (sourceField.type !== targetField.type) {
47194
+ warnings.push(`Type mismatch: ${targetField.name} (source: ${sourceField.type}, target: ${targetField.type})`);
47195
+ }
47196
+ }
47197
+ return { valid: warnings.length === 0, warnings };
47198
+ }
47199
+ function formatPayloadTooltip(fields) {
47200
+ if (fields.length === 0) return "";
47201
+ const parts = fields.map(
47202
+ (f3) => `${f3.name}: ${f3.type}${f3.required ? " (req)" : ""}`
47203
+ );
47204
+ return `{ ${parts.join(", ")} }`;
47205
+ }
47150
47206
  var ScreenSizeContext = createContext("tablet");
47151
47207
  var PatternSelectionContext = createContext({ selected: null, select: () => {
47152
47208
  } });
@@ -47157,6 +47213,15 @@ var ROLE_COLORS = {
47157
47213
  error: { border: "#D97706", dot: "#F59E0B" },
47158
47214
  default: { border: "var(--color-border)", dot: "#6B7280" }
47159
47215
  };
47216
+ var LAYER_COLORS = {
47217
+ Infrastructure: "#3B82F6",
47218
+ Services: "#F59E0B",
47219
+ "UI Patterns": "#8B5CF6",
47220
+ Game: "#22C55E",
47221
+ ML: "#EC4899",
47222
+ Domain: "#6366F1",
47223
+ Community: "#6B7280"
47224
+ };
47160
47225
  var TARGET_HANDLE_STYLE = {
47161
47226
  background: "var(--color-primary)",
47162
47227
  width: 8,
@@ -47259,6 +47324,7 @@ var OrbPreviewNodeInner = (props) => {
47259
47324
  const role = data.stateRole ?? "default";
47260
47325
  const colors = ROLE_COLORS[role] ?? ROLE_COLORS.default;
47261
47326
  const eventSources = data.eventSources ?? [];
47327
+ const layerColor = data.layer ? LAYER_COLORS[data.layer] : void 0;
47262
47328
  const isExpanded = Boolean(data.traitName);
47263
47329
  const label = isExpanded ? `${data.transitionEvent ?? ""}` : data.orbitalName;
47264
47330
  const sublabel = isExpanded ? `${data.fromState ?? ""} \u2192 ${data.toState ?? ""}` : data.entityName ?? "";
@@ -47400,6 +47466,13 @@ var OrbPreviewNodeInner = (props) => {
47400
47466
  onMouseLeave: handleMouseLeave,
47401
47467
  children: [
47402
47468
  /* @__PURE__ */ jsx("style", { children: SELECTION_STYLES }),
47469
+ layerColor && /* @__PURE__ */ jsx(
47470
+ Box,
47471
+ {
47472
+ style: { height: 3, backgroundColor: layerColor },
47473
+ title: data.layer
47474
+ }
47475
+ ),
47403
47476
  /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-2 px-3 py-1.5 border-b border-border/40 drag-handle cursor-grab", children: [
47404
47477
  /* @__PURE__ */ jsx(
47405
47478
  Box,
@@ -47421,7 +47494,7 @@ var OrbPreviewNodeInner = (props) => {
47421
47494
  color: "#F97316",
47422
47495
  border: "1px solid #F9731630"
47423
47496
  },
47424
- title: `${src.label ?? src.patternType} \u2192 ${src.event}`,
47497
+ title: `${src.label ?? src.patternType} \u2192 ${src.event}${src.payloadFields?.length ? ` ${formatPayloadTooltip(src.payloadFields)}` : ""}`,
47425
47498
  children: src.label ?? src.event
47426
47499
  },
47427
47500
  src.event
@@ -47455,7 +47528,7 @@ var OrbPreviewNodeInner = (props) => {
47455
47528
  type: "source",
47456
47529
  position: Position.Right,
47457
47530
  style: eventHandleStyle(src),
47458
- title: `${src.label ?? src.patternType}: ${src.event}`
47531
+ title: `${src.label ?? src.patternType}: ${src.event}${src.payloadFields?.length ? ` ${formatPayloadTooltip(src.payloadFields)}` : ""}`
47459
47532
  },
47460
47533
  `event-${src.event}`
47461
47534
  ))
@@ -47543,6 +47616,211 @@ var EventFlowEdgeInner = (props) => {
47543
47616
  var EventFlowEdge = React125__default.memo(EventFlowEdgeInner);
47544
47617
  EventFlowEdge.displayName = "EventFlowEdge";
47545
47618
 
47619
+ // components/molecules/avl/BehaviorComposeNode.tsx
47620
+ init_Box();
47621
+ init_Typography();
47622
+ var LAYER_COLORS2 = {
47623
+ Infrastructure: "#3B82F6",
47624
+ Services: "#F59E0B",
47625
+ "UI Patterns": "#8B5CF6",
47626
+ Game: "#22C55E",
47627
+ ML: "#EC4899",
47628
+ Domain: "#6366F1",
47629
+ Community: "#6B7280"
47630
+ };
47631
+ var NODE_WIDTH = 220;
47632
+ var TARGET_HANDLE_STYLE2 = {
47633
+ background: "var(--color-primary)",
47634
+ width: 8,
47635
+ height: 8,
47636
+ border: "2px solid var(--color-card)"
47637
+ };
47638
+ function eventHandleStyle2(source) {
47639
+ return {
47640
+ background: "#F97316",
47641
+ width: 10,
47642
+ height: 10,
47643
+ border: "2px solid var(--color-card)",
47644
+ top: `${source.positionHint * 100}%`,
47645
+ right: -5
47646
+ };
47647
+ }
47648
+ var BehaviorComposeNodeInner = (props) => {
47649
+ const data = props.data;
47650
+ const [hovered, setHovered] = useState(false);
47651
+ const handleMouseEnter = useCallback(() => setHovered(true), []);
47652
+ const handleMouseLeave = useCallback(() => setHovered(false), []);
47653
+ const layerColor = data.layer ? LAYER_COLORS2[data.layer] : void 0;
47654
+ const connectableEvents = data.connectableEvents ?? [];
47655
+ return /* @__PURE__ */ jsxs(
47656
+ Box,
47657
+ {
47658
+ className: "rounded-lg border shadow-sm bg-card transition-all duration-200 overflow-hidden",
47659
+ style: {
47660
+ borderColor: hovered ? "var(--color-primary)" : "var(--color-border)",
47661
+ borderWidth: "1.5px",
47662
+ width: NODE_WIDTH
47663
+ },
47664
+ onMouseEnter: handleMouseEnter,
47665
+ onMouseLeave: handleMouseLeave,
47666
+ children: [
47667
+ layerColor && /* @__PURE__ */ jsx(
47668
+ Box,
47669
+ {
47670
+ style: { height: 3, backgroundColor: layerColor },
47671
+ title: data.layer
47672
+ }
47673
+ ),
47674
+ /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-2 px-3 py-1.5 border-b border-border/40 drag-handle cursor-grab", children: [
47675
+ /* @__PURE__ */ jsxs(Box, { className: "flex flex-col min-w-0 flex-1", children: [
47676
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "font-semibold truncate leading-tight text-[12px]", children: data.behaviorName }),
47677
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground truncate text-[10px] leading-tight", children: data.entityName })
47678
+ ] }),
47679
+ /* @__PURE__ */ jsx(Badge, { variant: "neutral", className: "shrink-0", children: /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-[9px] text-inherit", children: data.level }) })
47680
+ ] }),
47681
+ /* @__PURE__ */ jsx(Box, { className: "flex items-center justify-center py-3", children: /* @__PURE__ */ jsx(
47682
+ AvlBehaviorGlyph,
47683
+ {
47684
+ name: data.behaviorName,
47685
+ level: data.level,
47686
+ domain: data.domain,
47687
+ stateCount: data.stateCount,
47688
+ fieldCount: data.fieldCount,
47689
+ persistence: data.persistence,
47690
+ effectTypes: data.effectTypes,
47691
+ children: data.children,
47692
+ connections: data.connections,
47693
+ size: "sm",
47694
+ showLabels: false
47695
+ }
47696
+ ) }),
47697
+ connectableEvents.length > 0 && /* @__PURE__ */ jsxs(Box, { className: "flex flex-wrap gap-0.5 px-2 pb-2", children: [
47698
+ connectableEvents.slice(0, 4).map((ev) => /* @__PURE__ */ jsx(
47699
+ Box,
47700
+ {
47701
+ className: "rounded-full px-1.5 py-0 text-[8px] font-medium leading-tight",
47702
+ style: {
47703
+ backgroundColor: "#F9731615",
47704
+ color: "#F97316",
47705
+ border: "1px solid #F9731630"
47706
+ },
47707
+ title: `${ev.event}${ev.payloadFields?.length ? ` ${formatPayloadTooltip(ev.payloadFields)}` : ""}`,
47708
+ children: ev.event
47709
+ },
47710
+ ev.event
47711
+ )),
47712
+ connectableEvents.length > 4 && /* @__PURE__ */ jsxs(
47713
+ Box,
47714
+ {
47715
+ className: "rounded-full px-1.5 py-0 text-[8px] font-medium leading-tight",
47716
+ style: { color: "var(--color-muted-foreground)" },
47717
+ children: [
47718
+ "+",
47719
+ connectableEvents.length - 4
47720
+ ]
47721
+ }
47722
+ )
47723
+ ] }),
47724
+ /* @__PURE__ */ jsxs(Box, { className: "flex items-center justify-between px-2 pb-1.5", children: [
47725
+ /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "text-muted-foreground text-[9px]", children: [
47726
+ data.stateCount,
47727
+ " states"
47728
+ ] }),
47729
+ data.layer && /* @__PURE__ */ jsx(Badge, { variant: "neutral", className: "text-[8px]", children: /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-[8px] text-inherit", children: data.layer }) })
47730
+ ] }),
47731
+ /* @__PURE__ */ jsx(
47732
+ Handle,
47733
+ {
47734
+ type: "target",
47735
+ position: Position.Left,
47736
+ style: TARGET_HANDLE_STYLE2
47737
+ }
47738
+ ),
47739
+ connectableEvents.map((ev) => /* @__PURE__ */ jsx(
47740
+ Handle,
47741
+ {
47742
+ id: `event-${ev.event}`,
47743
+ type: "source",
47744
+ position: Position.Right,
47745
+ style: eventHandleStyle2(ev),
47746
+ title: `${ev.event}${ev.payloadFields?.length ? ` ${formatPayloadTooltip(ev.payloadFields)}` : ""}`
47747
+ },
47748
+ `event-${ev.event}`
47749
+ ))
47750
+ ]
47751
+ }
47752
+ );
47753
+ };
47754
+ var BehaviorComposeNode = React125__default.memo(BehaviorComposeNodeInner);
47755
+ BehaviorComposeNode.displayName = "BehaviorComposeNode";
47756
+
47757
+ // components/molecules/avl/avl-behavior-compose-converter.ts
47758
+ var COMPOSE_SPACING = 320;
47759
+ function behaviorsToComposeGraph(entries, wires, layoutHint) {
47760
+ const nodes = [];
47761
+ const edges = [];
47762
+ const count = entries.length;
47763
+ const cols = layoutHint === "pipeline" ? count : Math.ceil(Math.sqrt(count));
47764
+ for (let i = 0; i < entries.length; i++) {
47765
+ const entry = entries[i];
47766
+ const col = i % cols;
47767
+ const row = Math.floor(i / cols);
47768
+ nodes.push({
47769
+ id: entry.behaviorName,
47770
+ type: "behaviorCompose",
47771
+ position: { x: col * COMPOSE_SPACING, y: row * COMPOSE_SPACING },
47772
+ data: {
47773
+ behaviorName: entry.behaviorName,
47774
+ level: entry.level,
47775
+ domain: entry.domain,
47776
+ layer: entry.layer,
47777
+ entityName: entry.entityName,
47778
+ stateCount: entry.stateCount,
47779
+ fieldCount: entry.fieldCount,
47780
+ persistence: entry.persistence,
47781
+ effectTypes: entry.effectTypes,
47782
+ children: entry.children,
47783
+ connections: entry.connections,
47784
+ connectableEvents: entry.connectableEvents,
47785
+ composableWith: entry.composableWith,
47786
+ orbitalNames: entry.orbitalNames
47787
+ }
47788
+ });
47789
+ }
47790
+ for (const wire of wires) {
47791
+ edges.push({
47792
+ id: `bw-${wire.sourceBehavior}-${wire.targetBehavior}-${wire.event}`,
47793
+ source: wire.sourceBehavior,
47794
+ target: wire.targetBehavior,
47795
+ sourceHandle: `event-${wire.event}`,
47796
+ type: "eventFlow",
47797
+ data: wire
47798
+ });
47799
+ }
47800
+ return { nodes, edges };
47801
+ }
47802
+ function registryEntryToCanvasEntry(entry, orbitalNames) {
47803
+ const events2 = entry.connectableEvents;
47804
+ const connectableEvents = events2.map((eventName, i) => ({
47805
+ event: eventName,
47806
+ payloadFields: entry.eventPayloads[eventName],
47807
+ positionHint: events2.length > 1 ? 0.1 + i * 0.8 / (events2.length - 1) : 0.5
47808
+ }));
47809
+ return {
47810
+ behaviorName: entry.name,
47811
+ level: entry.level,
47812
+ domain: entry.family,
47813
+ layer: entry.layer,
47814
+ entityName: entry.defaultEntity.name,
47815
+ stateCount: entry.complexity.states,
47816
+ fieldCount: entry.defaultEntity.fields.length,
47817
+ persistence: entry.defaultEntity.persistence,
47818
+ connectableEvents,
47819
+ composableWith: entry.composableWith,
47820
+ orbitalNames
47821
+ };
47822
+ }
47823
+
47546
47824
  // components/molecules/avl/index.ts
47547
47825
  init_Avl3DOrbitalNode();
47548
47826
  init_Avl3DCrossWire();
@@ -47655,6 +47933,7 @@ function OrbInspector({ node, schema, editable = false, onSchemaChange, onClose
47655
47933
  const effectTypes = node.effectTypes ?? [];
47656
47934
  const guard = node.guard;
47657
47935
  const isExpanded = Boolean(traitName);
47936
+ const hasRenderUi = effectTypes.includes("render-ui");
47658
47937
  const patternType = selectedPattern?.patternType;
47659
47938
  const patternDef = useMemo(() => patternType ? getPatternDefinition(patternType) : null, [patternType]);
47660
47939
  const isEntityPattern = patternType ? isEntityAwarePattern(patternType) : false;
@@ -47854,6 +48133,42 @@ function OrbInspector({ node, schema, editable = false, onSchemaChange, onClose
47854
48133
  }
47855
48134
  )
47856
48135
  ] }),
48136
+ editable && !selectedPattern && !isExpanded && node.layer === "Services" && /* @__PURE__ */ jsxs(Box, { className: "px-4 py-3 border-b border-border/40", children: [
48137
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground text-[10px] uppercase tracking-wider mb-2", children: t("Service Mode") }),
48138
+ /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center", children: [
48139
+ /* @__PURE__ */ jsxs(
48140
+ Button,
48141
+ {
48142
+ variant: hasRenderUi ? "primary" : "ghost",
48143
+ size: "sm",
48144
+ className: "flex-1 text-[11px]",
48145
+ onClick: () => {
48146
+ if (!hasRenderUi) eventBus.emit("UI:SERVICE_MODE_TOGGLE", { orbitalName: node.orbitalName, standalone: true });
48147
+ },
48148
+ children: [
48149
+ /* @__PURE__ */ jsx(Icon, { name: "monitor", size: "xs", className: "mr-1" }),
48150
+ t("Standalone")
48151
+ ]
48152
+ }
48153
+ ),
48154
+ /* @__PURE__ */ jsxs(
48155
+ Button,
48156
+ {
48157
+ variant: hasRenderUi ? "ghost" : "primary",
48158
+ size: "sm",
48159
+ className: "flex-1 text-[11px]",
48160
+ onClick: () => {
48161
+ if (hasRenderUi) eventBus.emit("UI:SERVICE_MODE_TOGGLE", { orbitalName: node.orbitalName, standalone: false });
48162
+ },
48163
+ children: [
48164
+ /* @__PURE__ */ jsx(Icon, { name: "cpu", size: "xs", className: "mr-1" }),
48165
+ t("Embedded")
48166
+ ]
48167
+ }
48168
+ )
48169
+ ] }),
48170
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground text-[10px] mt-1", children: hasRenderUi ? t("Renders its own UI") : t("Headless, wired to other behaviors") })
48171
+ ] }),
47857
48172
  !selectedPattern && !isExpanded && traits2.length > 0 && /* @__PURE__ */ jsxs(Box, { className: "px-4 py-3 border-b border-border/40", children: [
47858
48173
  /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground text-[10px] uppercase tracking-wider mb-2", children: "Traits" }),
47859
48174
  /* @__PURE__ */ jsx(Box, { className: "flex flex-col gap-1", children: traits2.map((t2) => /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-2", children: [
@@ -48002,8 +48317,10 @@ OrbInspector.displayName = "OrbInspector";
48002
48317
  // components/organisms/avl/FlowCanvas.tsx
48003
48318
  init_Box();
48004
48319
  init_Typography();
48320
+ init_useEventBus();
48005
48321
  var NODE_TYPES = {
48006
- preview: OrbPreviewNode
48322
+ preview: OrbPreviewNode,
48323
+ behaviorCompose: BehaviorComposeNode
48007
48324
  };
48008
48325
  var EDGE_TYPES = {
48009
48326
  eventFlow: EventFlowEdge
@@ -48025,7 +48342,13 @@ function FlowCanvasInner({
48025
48342
  editable,
48026
48343
  onSchemaChange,
48027
48344
  onPatternDelete,
48028
- onEventWire
48345
+ onEventWire,
48346
+ behaviorMeta,
48347
+ layoutHint,
48348
+ onNodeSelect,
48349
+ composeLevel,
48350
+ behaviorEntries,
48351
+ behaviorWires
48029
48352
  }) {
48030
48353
  const parsedSchema = useMemo(() => {
48031
48354
  if (typeof schemaProp === "string") return JSON.parse(schemaProp);
@@ -48047,18 +48370,22 @@ function FlowCanvasInner({
48047
48370
  if (p2) setSelectedNode(p2.nodeData);
48048
48371
  }
48049
48372
  }), [selectedPattern]);
48050
- const { overviewNodes, overviewEdges, expandedNodes, expandedEdges } = useMemo(() => {
48051
- const overview = schemaToOverviewGraph(parsedSchema, mockData);
48373
+ const [atBehaviorLevel, setAtBehaviorLevel] = useState(composeLevel === "behavior");
48374
+ const { composeNodes, composeEdges, overviewNodes, overviewEdges, expandedNodes, expandedEdges } = useMemo(() => {
48375
+ const compose = composeLevel === "behavior" && behaviorEntries?.length ? behaviorsToComposeGraph(behaviorEntries, behaviorWires ?? [], layoutHint) : { nodes: [], edges: [] };
48376
+ const overview = schemaToOverviewGraph(parsedSchema, mockData, behaviorMeta, layoutHint);
48052
48377
  const expanded = expandedOrbital ? orbitalToExpandedGraph(parsedSchema, expandedOrbital, mockData) : { nodes: [], edges: [] };
48053
48378
  return {
48379
+ composeNodes: compose.nodes,
48380
+ composeEdges: compose.edges,
48054
48381
  overviewNodes: overview.nodes,
48055
48382
  overviewEdges: overview.edges,
48056
48383
  expandedNodes: expanded.nodes,
48057
48384
  expandedEdges: expanded.edges
48058
48385
  };
48059
- }, [parsedSchema, expandedOrbital]);
48060
- const activeNodes = level === "overview" ? overviewNodes : expandedNodes;
48061
- const activeEdges = level === "overview" ? overviewEdges : expandedEdges;
48386
+ }, [parsedSchema, expandedOrbital, behaviorMeta, layoutHint, composeLevel, behaviorEntries, behaviorWires]);
48387
+ const activeNodes = atBehaviorLevel && composeNodes.length > 0 ? composeNodes : level === "overview" ? overviewNodes : expandedNodes;
48388
+ const activeEdges = atBehaviorLevel && composeEdges.length > 0 ? composeEdges : level === "overview" ? overviewEdges : expandedEdges;
48062
48389
  const [nodes, setNodes, onNodesChange] = useNodesState(activeNodes);
48063
48390
  const [edges, setEdges, onEdgesChange] = useEdgesState(activeEdges);
48064
48391
  const reactFlow = useReactFlow();
@@ -48070,13 +48397,23 @@ function FlowCanvasInner({
48070
48397
  });
48071
48398
  }, [activeNodes, activeEdges, setNodes, setEdges, reactFlow]);
48072
48399
  const handleNodeDoubleClick = useCallback((_, node) => {
48400
+ if (atBehaviorLevel && composeLevel === "behavior") {
48401
+ const d = node.data;
48402
+ if (d.orbitalNames?.length) {
48403
+ setExpandedOrbital(d.orbitalNames[0]);
48404
+ }
48405
+ setAtBehaviorLevel(false);
48406
+ setLevel("overview");
48407
+ onLevelChange?.("overview", d.behaviorName);
48408
+ return;
48409
+ }
48073
48410
  if (level === "overview") {
48074
48411
  const d = node.data;
48075
48412
  setExpandedOrbital(d.orbitalName ?? node.id);
48076
48413
  setLevel("expanded");
48077
48414
  onLevelChange?.("expanded", d.orbitalName ?? node.id);
48078
48415
  }
48079
- }, [level, onLevelChange]);
48416
+ }, [level, onLevelChange, atBehaviorLevel, composeLevel]);
48080
48417
  const handleNodeClick = useCallback((_, node) => {
48081
48418
  const nodeData = node.data;
48082
48419
  if (level === "expanded") {
@@ -48092,6 +48429,7 @@ function FlowCanvasInner({
48092
48429
  level: "overview",
48093
48430
  orbital: nodeData.orbitalName ?? node.id
48094
48431
  });
48432
+ onNodeSelect?.(nodeData.orbitalName ?? node.id);
48095
48433
  }
48096
48434
  }, [level, expandedOrbital, onNodeClick]);
48097
48435
  const handleClosePanel = useCallback(() => {
@@ -48105,6 +48443,9 @@ function FlowCanvasInner({
48105
48443
  setLevel("overview");
48106
48444
  setExpandedOrbital(void 0);
48107
48445
  onLevelChange?.("overview");
48446
+ } else if (level === "overview" && composeLevel === "behavior" && !atBehaviorLevel) {
48447
+ setAtBehaviorLevel(true);
48448
+ setExpandedOrbital(void 0);
48108
48449
  }
48109
48450
  } else if (e.key === "Delete" || e.key === "Backspace") {
48110
48451
  const target = e.target;
@@ -48127,8 +48468,13 @@ function FlowCanvasInner({
48127
48468
  setExpandedOrbital(void 0);
48128
48469
  setSelectedNode(null);
48129
48470
  onLevelChange?.("overview");
48471
+ } else if (level === "overview" && composeLevel === "behavior" && !atBehaviorLevel) {
48472
+ setAtBehaviorLevel(true);
48473
+ setExpandedOrbital(void 0);
48474
+ setSelectedNode(null);
48130
48475
  }
48131
- }, [level, onLevelChange, selectedNode]);
48476
+ }, [level, onLevelChange, selectedNode, composeLevel, atBehaviorLevel]);
48477
+ const eventBus = useEventBus();
48132
48478
  const handleConnect = useCallback((connection) => {
48133
48479
  if (!connection.sourceHandle?.startsWith("event-") || !onEventWire) return;
48134
48480
  const eventName = connection.sourceHandle.replace("event-", "");
@@ -48137,6 +48483,19 @@ function FlowCanvasInner({
48137
48483
  if (!sourceNode || !targetNode) return;
48138
48484
  const srcData = sourceNode.data;
48139
48485
  const tgtData = targetNode.data;
48486
+ const sourceEventSource = srcData.eventSources?.find((es) => es.event === eventName);
48487
+ const sourcePayload = sourceEventSource?.payloadFields;
48488
+ const targetEventSource = tgtData.eventSources?.find((es) => es.event === eventName);
48489
+ const targetPayload = targetEventSource?.payloadFields;
48490
+ const validation = validateWire(sourcePayload, targetPayload);
48491
+ if (validation.warnings.length > 0) {
48492
+ eventBus.emit("UI:WIRE_VALIDATION_WARNING", {
48493
+ eventName,
48494
+ sourceOrbital: srcData.orbitalName,
48495
+ targetOrbital: tgtData.orbitalName,
48496
+ warnings: validation.warnings
48497
+ });
48498
+ }
48140
48499
  onEventWire({
48141
48500
  eventName,
48142
48501
  sourceOrbital: srcData.orbitalName ?? "",
@@ -48144,7 +48503,7 @@ function FlowCanvasInner({
48144
48503
  sourceTraitName: srcData.traitName,
48145
48504
  targetTraitName: tgtData.traitName
48146
48505
  });
48147
- }, [nodes, onEventWire]);
48506
+ }, [nodes, onEventWire, eventBus]);
48148
48507
  const screenSizeKeys = ["mobile", "tablet", "desktop"];
48149
48508
  return /* @__PURE__ */ jsx(ScreenSizeContext.Provider, { value: screenSize, children: /* @__PURE__ */ jsx(PatternSelectionContext.Provider, { value: patternSelectionValue, children: /* @__PURE__ */ jsxs(
48150
48509
  Box,
@@ -48794,4 +49153,4 @@ AvlClickTarget.displayName = "AvlClickTarget";
48794
49153
  init_avl_schema_parser();
48795
49154
  init_avl_zoom_state();
48796
49155
 
48797
- export { AVL_FIELD_TYPE_SHAPES, AVL_OPERATOR_COLORS, AvlApplication, AvlBackwardEdge, AvlBehaviorGlyph, AvlBinding, AvlBindingEdge, AvlBindingRef, AvlClickTarget, AvlClosedCircuit, AvlCosmicZoom, AvlEffect, AvlEmitListen, AvlEntity, AvlEvent, AvlEventWireEdge, AvlExprTree, AvlField, AvlFieldType, AvlGuard, AvlLiteral, AvlOperator, AvlOrbital, AvlOrbitalNode, AvlOrbitalUnit, AvlPage, AvlPageEdge, AvlPersistence, AvlSExpr, AvlSlotMap, AvlState, AvlStateMachine, AvlSwimLane, AvlTrait, AvlTraitScene, AvlTransition, AvlTransitionEdge, AvlTransitionLane, AvlTransitionScene, BehaviorView, CONNECTION_COLORS, DOMAIN_COLORS, DetailView, EFFECT_CATEGORY_COLORS, EFFECT_TYPE_TO_CATEGORY, EventFlowEdge, FlowCanvas, MiniStateMachine, ModuleCard, OrbInspector, OrbPreviewNode, STATE_COLORS, SystemNode, ZOOM_BAND_THRESHOLDS, ZoomBandContext, ZoomBreadcrumb, ZoomLegend, arcPath, computeTraitLayout, computeZoomBand, curveControlPoint, edgePath, getStateRole, gridPositions, orbitalToExpandedGraph, parseApplicationLevel, parseOrbitalLevel, parseTraitLevel, parseTransitionLevel, radialPositions, ringPositions, schemaToFlowGraph, schemaToOverviewGraph, useZoomBand, zoomProgress };
49156
+ export { AVL_FIELD_TYPE_SHAPES, AVL_OPERATOR_COLORS, AvlApplication, AvlBackwardEdge, AvlBehaviorGlyph, AvlBinding, AvlBindingEdge, AvlBindingRef, AvlClickTarget, AvlClosedCircuit, AvlCosmicZoom, AvlEffect, AvlEmitListen, AvlEntity, AvlEvent, AvlEventWireEdge, AvlExprTree, AvlField, AvlFieldType, AvlGuard, AvlLiteral, AvlOperator, AvlOrbital, AvlOrbitalNode, AvlOrbitalUnit, AvlPage, AvlPageEdge, AvlPersistence, AvlSExpr, AvlSlotMap, AvlState, AvlStateMachine, AvlSwimLane, AvlTrait, AvlTraitScene, AvlTransition, AvlTransitionEdge, AvlTransitionLane, AvlTransitionScene, BehaviorComposeNode, BehaviorView, CONNECTION_COLORS, DOMAIN_COLORS, DetailView, EFFECT_CATEGORY_COLORS, EFFECT_TYPE_TO_CATEGORY, EventFlowEdge, FlowCanvas, MiniStateMachine, ModuleCard, OrbInspector, OrbPreviewNode, STATE_COLORS, SystemNode, ZOOM_BAND_THRESHOLDS, ZoomBandContext, ZoomBreadcrumb, ZoomLegend, arcPath, behaviorsToComposeGraph, computeTraitLayout, computeZoomBand, curveControlPoint, edgePath, getStateRole, gridPositions, orbitalToExpandedGraph, parseApplicationLevel, parseOrbitalLevel, parseTraitLevel, parseTransitionLevel, radialPositions, registryEntryToCanvasEntry, ringPositions, schemaToFlowGraph, schemaToOverviewGraph, useZoomBand, zoomProgress };
@@ -49,6 +49,10 @@ export interface BoxProps extends React.HTMLAttributes<HTMLDivElement> {
49
49
  actionPayload?: Record<string, unknown>;
50
50
  /** Declarative hover event — emits UI:{hoverEvent} with { hovered: true/false } on mouseEnter/mouseLeave */
51
51
  hoverEvent?: string;
52
+ /** Maximum width (CSS value, e.g., "550px", "80rem") */
53
+ maxWidth?: string;
54
+ /** Children elements */
55
+ children?: React.ReactNode;
52
56
  }
53
57
  /**
54
58
  * Box - Versatile container component with design tokens
@@ -1089,6 +1089,7 @@ var Box = React90__namespace.default.forwardRef(
1089
1089
  action,
1090
1090
  actionPayload,
1091
1091
  hoverEvent,
1092
+ maxWidth,
1092
1093
  onClick,
1093
1094
  onMouseEnter,
1094
1095
  onMouseLeave,
@@ -1153,6 +1154,7 @@ var Box = React90__namespace.default.forwardRef(
1153
1154
  onClick: isClickable ? handleClick : void 0,
1154
1155
  onMouseEnter: hoverEvent || onMouseEnter ? handleMouseEnter : void 0,
1155
1156
  onMouseLeave: hoverEvent || onMouseLeave ? handleMouseLeave : void 0,
1157
+ style: maxWidth ? { maxWidth, ...rest.style } : rest.style,
1156
1158
  ...rest,
1157
1159
  children
1158
1160
  }
@@ -1043,6 +1043,7 @@ var Box = React90__default.forwardRef(
1043
1043
  action,
1044
1044
  actionPayload,
1045
1045
  hoverEvent,
1046
+ maxWidth,
1046
1047
  onClick,
1047
1048
  onMouseEnter,
1048
1049
  onMouseLeave,
@@ -1107,6 +1108,7 @@ var Box = React90__default.forwardRef(
1107
1108
  onClick: isClickable ? handleClick : void 0,
1108
1109
  onMouseEnter: hoverEvent || onMouseEnter ? handleMouseEnter : void 0,
1109
1110
  onMouseLeave: hoverEvent || onMouseLeave ? handleMouseLeave : void 0,
1111
+ style: maxWidth ? { maxWidth, ...rest.style } : rest.style,
1110
1112
  ...rest,
1111
1113
  children
1112
1114
  }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * BehaviorComposeNode
3
+ *
4
+ * React Flow custom node for behavior-level composition.
5
+ * Shows AvlBehaviorGlyph instead of live UI (OrbPreview).
6
+ * Each behavior is one compact node with event handles for wiring.
7
+ *
8
+ * This is the compose-level node. Double-clicking drills into
9
+ * the orbital-level view (OrbPreviewNode).
10
+ */
11
+ import React from 'react';
12
+ import { type NodeProps } from '@xyflow/react';
13
+ export declare const BehaviorComposeNode: React.NamedExoticComponent<NodeProps>;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * AVL Behavior Compose Converter
3
+ *
4
+ * Converts BehaviorCanvasEntry[] to React Flow nodes and edges
5
+ * for the behavior-level composition canvas.
6
+ *
7
+ * Parallel to avl-preview-converter.ts which works at the orbital level.
8
+ * This works one level higher: each node is a behavior (not an orbital).
9
+ */
10
+ import type { Node, Edge } from '@xyflow/react';
11
+ import type { BehaviorCanvasEntry, BehaviorComposeNodeData, BehaviorWireEdgeData } from './avl-behavior-compose-types';
12
+ /**
13
+ * Build a React Flow graph for behavior-level composition.
14
+ * Each behavior entry becomes one BehaviorComposeNode.
15
+ */
16
+ export declare function behaviorsToComposeGraph(entries: BehaviorCanvasEntry[], wires: BehaviorWireEdgeData[], layoutHint?: 'pipeline' | 'grid'): {
17
+ nodes: Node<BehaviorComposeNodeData>[];
18
+ edges: Edge<BehaviorWireEdgeData>[];
19
+ };
20
+ /** Registry record shape (matches behaviors-registry.json entries). */
21
+ export interface BehaviorRegistryRecord {
22
+ name: string;
23
+ level: 'atom' | 'molecule' | 'organism';
24
+ family: string;
25
+ layer: string;
26
+ description: string;
27
+ complexity: {
28
+ states: number;
29
+ events: number;
30
+ transitions: number;
31
+ };
32
+ defaultEntity: {
33
+ name: string;
34
+ persistence?: string;
35
+ fields: Array<{
36
+ name: string;
37
+ type: string;
38
+ required?: boolean;
39
+ }>;
40
+ };
41
+ connectableEvents: string[];
42
+ eventPayloads: Record<string, Array<{
43
+ name: string;
44
+ type: string;
45
+ required?: boolean;
46
+ }>>;
47
+ composableWith: string[];
48
+ }
49
+ /**
50
+ * Convert a registry entry to a BehaviorCanvasEntry.
51
+ * Maps connectableEvents + eventPayloads into typed ConnectableEvent[].
52
+ */
53
+ export declare function registryEntryToCanvasEntry(entry: BehaviorRegistryRecord, orbitalNames: string[]): BehaviorCanvasEntry;