@principal-ai/subsystems-react 0.21.1 → 0.23.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.
Files changed (92) hide show
  1. package/dist/graphify/consolidated.d.ts +5 -24
  2. package/dist/graphify/consolidated.d.ts.map +1 -1
  3. package/dist/graphify/index.d.ts +1 -1
  4. package/dist/graphify/index.d.ts.map +1 -1
  5. package/dist/graphify/index.js.map +1 -1
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/pierre/PierreFileView.d.ts +3 -1
  10. package/dist/pierre/PierreFileView.d.ts.map +1 -1
  11. package/dist/pierre/PierreFileView.js +13 -3
  12. package/dist/pierre/PierreFileView.js.map +1 -1
  13. package/dist/pierre/PierreSnippetView.d.ts.map +1 -1
  14. package/dist/pierre/PierreSnippetView.js +5 -26
  15. package/dist/pierre/PierreSnippetView.js.map +1 -1
  16. package/dist/pierre/PierreWalkthroughCodeView.d.ts +3 -1
  17. package/dist/pierre/PierreWalkthroughCodeView.d.ts.map +1 -1
  18. package/dist/pierre/PierreWalkthroughCodeView.js +65 -8
  19. package/dist/pierre/PierreWalkthroughCodeView.js.map +1 -1
  20. package/dist/pierre/constructColors.d.ts.map +1 -1
  21. package/dist/pierre/constructColors.js +0 -5
  22. package/dist/pierre/constructColors.js.map +1 -1
  23. package/dist/pierre/index.d.ts +1 -1
  24. package/dist/pierre/index.d.ts.map +1 -1
  25. package/dist/pierre/index.js +1 -1
  26. package/dist/pierre/index.js.map +1 -1
  27. package/dist/pierre/scrollAnchor.d.ts +2 -0
  28. package/dist/pierre/scrollAnchor.d.ts.map +1 -1
  29. package/dist/pierre/scrollAnchor.js +7 -0
  30. package/dist/pierre/scrollAnchor.js.map +1 -1
  31. package/dist/pierre/sliceSnippet.d.ts +6 -0
  32. package/dist/pierre/sliceSnippet.d.ts.map +1 -1
  33. package/dist/pierre/sliceSnippet.js +25 -0
  34. package/dist/pierre/sliceSnippet.js.map +1 -1
  35. package/dist/stories/Subsystem/ComponentGraph/fixtures.d.ts.map +1 -1
  36. package/dist/stories/Subsystem/ComponentGraph/fixtures.js +0 -4
  37. package/dist/stories/Subsystem/ComponentGraph/fixtures.js.map +1 -1
  38. package/dist/subsystem/FileDrawer.d.ts +7 -2
  39. package/dist/subsystem/FileDrawer.d.ts.map +1 -1
  40. package/dist/subsystem/FileDrawer.js +13 -9
  41. package/dist/subsystem/FileDrawer.js.map +1 -1
  42. package/dist/subsystem/SubsystemComponentGraph.d.ts +2 -0
  43. package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
  44. package/dist/subsystem/SubsystemComponentGraph.js +97 -8
  45. package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
  46. package/dist/subsystem/declarationRef.d.ts +6 -1
  47. package/dist/subsystem/declarationRef.d.ts.map +1 -1
  48. package/dist/subsystem/formatDeclaration.d.ts.map +1 -1
  49. package/dist/subsystem/formatDeclaration.js +0 -15
  50. package/dist/subsystem/formatDeclaration.js.map +1 -1
  51. package/dist/subsystem/model.d.ts +61 -22
  52. package/dist/subsystem/model.d.ts.map +1 -1
  53. package/dist/subsystem/model.js +185 -65
  54. package/dist/subsystem/model.js.map +1 -1
  55. package/dist/subsystem/nodes.d.ts +3 -4
  56. package/dist/subsystem/nodes.d.ts.map +1 -1
  57. package/dist/subsystem/nodes.js +5 -6
  58. package/dist/subsystem/nodes.js.map +1 -1
  59. package/dist/utils/elkLayout.d.ts +6 -4
  60. package/dist/utils/elkLayout.d.ts.map +1 -1
  61. package/dist/utils/elkLayout.js +144 -54
  62. package/dist/utils/elkLayout.js.map +1 -1
  63. package/package.json +3 -3
  64. package/src/graphify/consolidated.ts +4 -26
  65. package/src/graphify/index.ts +0 -2
  66. package/src/index.ts +0 -2
  67. package/src/pierre/PierreFileView.tsx +25 -2
  68. package/src/pierre/PierreSnippetView.tsx +5 -31
  69. package/src/pierre/PierreWalkthroughCodeView.tsx +110 -5
  70. package/src/pierre/constructColors.test.ts +1 -2
  71. package/src/pierre/constructColors.ts +0 -5
  72. package/src/pierre/index.ts +1 -1
  73. package/src/pierre/scrollAnchor.ts +13 -0
  74. package/src/pierre/sliceSnippet.test.ts +44 -1
  75. package/src/pierre/sliceSnippet.ts +28 -0
  76. package/src/stories/Subsystem/ComponentDeclarationAudit.stories.tsx +0 -31
  77. package/src/stories/Subsystem/ComponentGraph/Appearance.stories.tsx +2 -46
  78. package/src/stories/Subsystem/ComponentGraph/Captures.stories.tsx +9 -24
  79. package/src/stories/Subsystem/ComponentGraph/DetailPanel.stories.tsx +2 -2
  80. package/src/stories/Subsystem/ComponentGraph/Flows.stories.tsx +2 -1
  81. package/src/stories/Subsystem/ComponentGraph/Modules.stories.tsx +287 -0
  82. package/src/stories/Subsystem/ComponentGraph/Processes.stories.tsx +1 -1
  83. package/src/stories/Subsystem/ComponentGraph/Scenarios.stories.tsx +6 -6
  84. package/src/stories/Subsystem/ComponentGraph/fixtures.ts +0 -4
  85. package/src/subsystem/FileDrawer.tsx +19 -7
  86. package/src/subsystem/SubsystemComponentGraph.tsx +153 -7
  87. package/src/subsystem/declarationRef.ts +6 -1
  88. package/src/subsystem/formatDeclaration.ts +0 -19
  89. package/src/subsystem/model.test.ts +190 -9
  90. package/src/subsystem/model.ts +219 -77
  91. package/src/subsystem/nodes.tsx +6 -6
  92. package/src/utils/elkLayout.ts +148 -58
@@ -73,12 +73,13 @@ export interface ElkLayoutOptions {
73
73
  direction?: 'RIGHT' | 'LEFT' | 'DOWN' | 'UP';
74
74
 
75
75
  /**
76
- * Compound groups — each becomes a nested ELK parent whose `memberIds`
77
- * are laid out inside it. Members reference the group via React Flow
78
- * `parentId`; ELK returns parent-relative child positions which this
79
- * module flattens back to absolute flow coordinates.
76
+ * Compound groups — each becomes an ELK parent whose `memberIds` are laid
77
+ * out inside it. `memberIds` may be leaf node ids or other group ids
78
+ * (for nesting, e.g. process → module → leaves). Optional `parentId`
79
+ * nests this group under another group; omit for a root-level frame.
80
+ * Members reference their immediate parent via React Flow `parentId`.
80
81
  */
81
- groups?: Array<{ id: string; memberIds: string[] }>;
82
+ groups?: Array<{ id: string; memberIds: string[]; parentId?: string }>;
82
83
  }
83
84
 
84
85
  /** Result of ELK layout computation */
@@ -502,51 +503,134 @@ export async function computeElkLayout(
502
503
 
503
504
  // Partition leaf nodes into compound parents when groups are given.
504
505
  // Group shells themselves are NOT part of `nodes` — they are reconstructed
505
- // by the caller from `groupBounds`. Only leaf ids in `memberIds` nest.
506
+ // by the caller from `groupBounds`. `memberIds` may reference leaves or
507
+ // other group ids (process → module → leaves).
506
508
  const groupDefs = (options.groups ?? []).filter((g) => g.memberIds.length > 0);
507
- const memberToGroup = new Map<string, string>();
509
+ const groupById = new Map(groupDefs.map((g) => [g.id, g]));
510
+ const elkById = new Map(elkNodes.map((n) => [n.id, n]));
511
+
512
+ const compoundLayoutOptions: LayoutOptions = {
513
+ 'elk.algorithm': 'layered',
514
+ 'elk.direction': direction,
515
+ 'elk.padding': '[top=48,left=24,bottom=24,right=24]',
516
+ 'elk.spacing.nodeNode': '40',
517
+ };
518
+
519
+ /** Descendant leaf count for singleton checks (nested groups count through). */
520
+ const leafDescendantCount = (memberIds: string[]): number => {
521
+ let n = 0;
522
+ for (const mid of memberIds) {
523
+ if (elkById.has(mid)) n += 1;
524
+ else {
525
+ const g = groupById.get(mid);
526
+ if (g) n += leafDescendantCount(g.memberIds);
527
+ }
528
+ }
529
+ return n;
530
+ };
531
+
532
+ const builtGroups = new Map<string, ElkNode>();
533
+ const skippedGroups = new Set<string>();
534
+ const pending = [...groupDefs];
535
+ // Build bottom-up: a group is ready when every member is a leaf or an
536
+ // already-built / skipped group. Skipped groups promote their members.
537
+ while (pending.length > 0) {
538
+ let progress = false;
539
+ for (let i = pending.length - 1; i >= 0; i--) {
540
+ const g = pending[i]!;
541
+ const childNodes: ElkNode[] = [];
542
+ let ready = true;
543
+ for (const mid of g.memberIds) {
544
+ if (elkById.has(mid)) {
545
+ childNodes.push(elkById.get(mid)!);
546
+ continue;
547
+ }
548
+ if (skippedGroups.has(mid)) {
549
+ // Promote skipped group's members into this parent.
550
+ const skipped = groupById.get(mid);
551
+ if (!skipped) {
552
+ ready = false;
553
+ break;
554
+ }
555
+ for (const sm of skipped.memberIds) {
556
+ if (elkById.has(sm)) childNodes.push(elkById.get(sm)!);
557
+ else if (builtGroups.has(sm)) childNodes.push(builtGroups.get(sm)!);
558
+ else if (!skippedGroups.has(sm)) {
559
+ ready = false;
560
+ break;
561
+ }
562
+ }
563
+ if (!ready) break;
564
+ continue;
565
+ }
566
+ if (builtGroups.has(mid)) {
567
+ childNodes.push(builtGroups.get(mid)!);
568
+ continue;
569
+ }
570
+ if (groupById.has(mid)) {
571
+ ready = false;
572
+ break;
573
+ }
574
+ // Unknown id — ignore (external stubs etc. may be absent).
575
+ }
576
+ if (!ready) continue;
577
+
578
+ pending.splice(i, 1);
579
+ progress = true;
580
+
581
+ // Skip frames with fewer than 2 leaf descendants — promote children up.
582
+ if (leafDescendantCount(g.memberIds) < 2 || childNodes.length < 1) {
583
+ skippedGroups.add(g.id);
584
+ continue;
585
+ }
586
+
587
+ builtGroups.set(g.id, {
588
+ id: g.id,
589
+ children: childNodes,
590
+ layoutOptions: compoundLayoutOptions,
591
+ });
592
+ }
593
+ if (!progress) {
594
+ // Cycle or unresolved refs — leave remaining groups unbuilt.
595
+ for (const g of pending) skippedGroups.add(g.id);
596
+ break;
597
+ }
598
+ }
599
+
508
600
  const groupedLeafIds = new Set<string>();
509
- for (const g of groupDefs) {
510
- for (const mid of g.memberIds) {
511
- if (!memberToGroup.has(mid)) {
512
- memberToGroup.set(mid, g.id);
513
- groupedLeafIds.add(mid);
601
+ const markLeaves = (memberIds: string[]) => {
602
+ for (const mid of memberIds) {
603
+ if (elkById.has(mid)) groupedLeafIds.add(mid);
604
+ else {
605
+ const g = groupById.get(mid);
606
+ if (g && builtGroups.has(g.id)) markLeaves(g.memberIds);
607
+ else if (g && skippedGroups.has(g.id)) markLeaves(g.memberIds);
514
608
  }
515
609
  }
610
+ };
611
+ for (const [id] of builtGroups) {
612
+ const g = groupById.get(id);
613
+ if (g) markLeaves(g.memberIds);
516
614
  }
517
- const elkById = new Map(elkNodes.map((n) => [n.id, n]));
615
+
518
616
  const ungroupedElkNodes: ElkNode[] = [];
519
617
  for (const n of elkNodes) {
520
618
  if (!groupedLeafIds.has(n.id)) ungroupedElkNodes.push(n);
521
619
  }
620
+
621
+ // Root-level compound parents: built groups with no parent, or whose parent
622
+ // was skipped / never built.
522
623
  const elkParents: ElkNode[] = [];
523
- for (const g of groupDefs) {
524
- const children = g.memberIds
525
- .map((mid) => elkById.get(mid))
526
- .filter((n): n is ElkNode => !!n);
527
- // Skip groups with <2 real members — a single-child frame adds noise;
528
- // the caller drops the shell and leaves the node top-level.
529
- if (children.length < 2) {
530
- for (const c of children) ungroupedElkNodes.push(c);
531
- memberToGroup.delete(g.memberIds[0]);
532
- groupedLeafIds.delete(g.memberIds[0]);
533
- continue;
534
- }
535
- elkParents.push({
536
- id: g.id,
537
- children,
538
- layoutOptions: {
539
- 'elk.algorithm': 'layered',
540
- 'elk.direction': direction,
541
- 'elk.padding': '[top=48,left=24,bottom=24,right=24]',
542
- 'elk.spacing.nodeNode': '40',
543
- },
544
- });
624
+ for (const [id, node] of builtGroups) {
625
+ const g = groupById.get(id);
626
+ const parentId = g?.parentId;
627
+ if (parentId && builtGroups.has(parentId)) continue; // nested inside parent
628
+ elkParents.push(node);
545
629
  }
546
630
 
547
631
  // Create ELK graph
548
632
  const rootOptions = getElkOptions(options);
549
- if (elkParents.length > 0) {
633
+ if (elkParents.length > 0 || builtGroups.size > 0) {
550
634
  rootOptions['elk.hierarchyHandling'] = 'INCLUDE_CHILDREN';
551
635
  }
552
636
  const elkGraph: ElkNode = {
@@ -562,9 +646,6 @@ export async function computeElkLayout(
562
646
  // Build maps of ELK-computed positions. Nested children report
563
647
  // parent-relative coords — flatten to absolute for edges, and keep the
564
648
  // relative form for React Flow children (whose position is parent-relative).
565
- // Absolute offset of every ELK node (parents included), accumulated down
566
- // the ancestor chain — edge sections/labels are relative to their
567
- // `container`, so each edge needs its container's absolute offset.
568
649
  const elkAbsOffsets = new Map<string, { x: number; y: number }>();
569
650
  const elkPositions = new Map<string, { x: number; y: number }>();
570
651
  const elkRelativePositions = new Map<string, { x: number; y: number }>();
@@ -576,27 +657,36 @@ export async function computeElkLayout(
576
657
  for (const c of n.children ?? []) walkElk(c, ax, ay);
577
658
  };
578
659
  walkElk(layoutedGraph, 0, 0);
660
+
661
+ const collectGroupResults = (n: ElkNode, isRootChild: boolean) => {
662
+ const isGroup = builtGroups.has(n.id);
663
+ if (isGroup) {
664
+ // Nested group positions are parent-relative (RF child semantics);
665
+ // root-level groups use absolute coords.
666
+ groupBounds.set(n.id, {
667
+ x: n.x ?? 0,
668
+ y: n.y ?? 0,
669
+ width: n.width ?? 0,
670
+ height: n.height ?? 0,
671
+ });
672
+ for (const c of n.children ?? []) {
673
+ const rx = c.x ?? 0;
674
+ const ry = c.y ?? 0;
675
+ elkRelativePositions.set(c.id, { x: rx, y: ry });
676
+ const abs = elkAbsOffsets.get(c.id) ?? { x: rx, y: ry };
677
+ elkPositions.set(c.id, abs);
678
+ if (builtGroups.has(c.id)) collectGroupResults(c, false);
679
+ }
680
+ return;
681
+ }
682
+ if (isRootChild) {
683
+ elkPositions.set(n.id, { x: n.x ?? 0, y: n.y ?? 0 });
684
+ elkRelativePositions.set(n.id, { x: n.x ?? 0, y: n.y ?? 0 });
685
+ }
686
+ };
579
687
  if (layoutedGraph.children) {
580
688
  for (const child of layoutedGraph.children) {
581
- if (child.children && child.children.length > 0 && groupDefs.some((g) => g.id === child.id)) {
582
- const gx = child.x ?? 0;
583
- const gy = child.y ?? 0;
584
- groupBounds.set(child.id, {
585
- x: gx,
586
- y: gy,
587
- width: child.width ?? 0,
588
- height: child.height ?? 0,
589
- });
590
- for (const grand of child.children) {
591
- const rx = grand.x ?? 0;
592
- const ry = grand.y ?? 0;
593
- elkRelativePositions.set(grand.id, { x: rx, y: ry });
594
- elkPositions.set(grand.id, { x: gx + rx, y: gy + ry });
595
- }
596
- } else {
597
- elkPositions.set(child.id, { x: child.x ?? 0, y: child.y ?? 0 });
598
- elkRelativePositions.set(child.id, { x: child.x ?? 0, y: child.y ?? 0 });
599
- }
689
+ collectGroupResults(child, true);
600
690
  }
601
691
  }
602
692