@principal-ai/subsystems-react 0.42.0 → 0.43.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/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/stories/Subsystem/C4Graph/c4Fixture.js +1 -1
- package/dist/stories/Subsystem/C4Graph/c4Fixture.js.map +1 -1
- package/dist/stories/Subsystem/ComponentGraph/fixtures.d.ts +6 -9
- package/dist/stories/Subsystem/ComponentGraph/fixtures.d.ts.map +1 -1
- package/dist/stories/Subsystem/ComponentGraph/fixtures.js +15 -50
- package/dist/stories/Subsystem/ComponentGraph/fixtures.js.map +1 -1
- package/dist/subsystem/C4Graph.d.ts +2 -3
- package/dist/subsystem/C4Graph.d.ts.map +1 -1
- package/dist/subsystem/C4Graph.js +2 -3
- package/dist/subsystem/C4Graph.js.map +1 -1
- package/dist/subsystem/IssueList.d.ts +5 -5
- package/dist/subsystem/IssueList.d.ts.map +1 -1
- package/dist/subsystem/IssueList.js +9 -18
- package/dist/subsystem/IssueList.js.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.d.ts +9 -12
- package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.js +30 -110
- package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
- package/dist/subsystem/model.d.ts +18 -51
- package/dist/subsystem/model.d.ts.map +1 -1
- package/dist/subsystem/model.js +2 -51
- package/dist/subsystem/model.js.map +1 -1
- package/dist/subsystem/toC4.d.ts +4 -4
- package/dist/subsystem/toC4.d.ts.map +1 -1
- package/dist/subsystem/toC4.js +3 -7
- package/dist/subsystem/toC4.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +0 -4
- package/src/stories/Subsystem/AgentsPanel.stories.tsx +3 -5
- package/src/stories/Subsystem/C4Graph/C4Container.stories.tsx +1 -4
- package/src/stories/Subsystem/C4Graph/c4Fixture.ts +1 -1
- package/src/stories/Subsystem/ComponentGraph/Appearance.stories.tsx +2 -4
- package/src/stories/Subsystem/ComponentGraph/Basics.stories.tsx +8 -8
- package/src/stories/Subsystem/ComponentGraph/Captures.stories.tsx +19 -19
- package/src/stories/Subsystem/ComponentGraph/Constructs.stories.tsx +0 -1
- package/src/stories/Subsystem/ComponentGraph/CustomEntities.stories.tsx +3 -3
- package/src/stories/Subsystem/ComponentGraph/DetailPanel.stories.tsx +0 -2
- package/src/stories/Subsystem/ComponentGraph/Diagnostics.stories.tsx +2 -6
- package/src/stories/Subsystem/ComponentGraph/EdgeViews.stories.tsx +21 -21
- package/src/stories/Subsystem/ComponentGraph/Flows.stories.tsx +0 -3
- package/src/stories/Subsystem/ComponentGraph/FrameworkStereotype.stories.tsx +30 -32
- package/src/stories/Subsystem/ComponentGraph/GraphTitle.stories.tsx +4 -4
- package/src/stories/Subsystem/ComponentGraph/IssueOverlay.stories.tsx +1 -26
- package/src/stories/Subsystem/ComponentGraph/Issues.stories.tsx +3 -25
- package/src/stories/Subsystem/ComponentGraph/ModuleBadges.stories.tsx +7 -8
- package/src/stories/Subsystem/ComponentGraph/Modules.stories.tsx +9 -11
- package/src/stories/Subsystem/ComponentGraph/Packages.stories.tsx +3 -6
- package/src/stories/Subsystem/ComponentGraph/Processes.stories.tsx +3 -3
- package/src/stories/Subsystem/ComponentGraph/Proposed.stories.tsx +8 -6
- package/src/stories/Subsystem/ComponentGraph/ProposedWalkthroughs.stories.tsx +0 -1
- package/src/stories/Subsystem/ComponentGraph/Reorder.stories.tsx +0 -1
- package/src/stories/Subsystem/ComponentGraph/Scenarios.stories.tsx +26 -28
- package/src/stories/Subsystem/ComponentGraph/WalkthroughAutoplay.stories.tsx +0 -1
- package/src/stories/Subsystem/ComponentGraph/WorkspacePackageFrames.stories.tsx +2 -3
- package/src/stories/Subsystem/ComponentGraph/fixtures.ts +18 -58
- package/src/stories/Subsystem/EgoGraph/EgoGraph.stories.tsx +0 -2
- package/src/subsystem/C4Graph.tsx +2 -3
- package/src/subsystem/IssueList.tsx +12 -22
- package/src/subsystem/SubsystemComponentGraph.tsx +38 -132
- package/src/subsystem/model.test.ts +37 -24
- package/src/subsystem/model.ts +18 -101
- package/src/subsystem/toC4.test.ts +5 -12
- package/src/subsystem/toC4.ts +5 -9
|
@@ -20,7 +20,7 @@ import { ReactFlow, ReactFlowProvider, useReactFlow, useViewport, applyNodeChang
|
|
|
20
20
|
import { useTheme } from '@principal-ade/industry-theme';
|
|
21
21
|
import { FileText, X } from 'lucide-react';
|
|
22
22
|
import { IndustryMarkdownSlide } from 'themed-markdown';
|
|
23
|
-
import { buildSubsystemGraph, deriveGraphEdges, isConstructsOnlyModel, moduleGroupNodeId,
|
|
23
|
+
import { buildSubsystemGraph, deriveGraphEdges, isConstructsOnlyModel, moduleGroupNodeId, isWalkthroughMechanism, edgeColor, MECHANISM_DESCRIPTIONS, subsystemGraphLayoutKey, walkthroughStepGraphEdgeId, } from './model';
|
|
24
24
|
import { ConstructsCatalog } from './ConstructsCatalog';
|
|
25
25
|
import { SubsystemAgentsPanel, } from './AgentsPanel';
|
|
26
26
|
import { MaintainLivePanel, } from '../components/maintain-events/MaintainLivePanel';
|
|
@@ -122,14 +122,14 @@ const WalkthroughDrawerContent = memo(function WalkthroughDrawerContent({ render
|
|
|
122
122
|
onSymbolClick,
|
|
123
123
|
}) }));
|
|
124
124
|
});
|
|
125
|
-
function Inner({ components,
|
|
125
|
+
function Inner({ components, walkthroughs, graphifyRelations, orderByLine, initialWalkthroughId, onReorderWalkthroughs, onSelect, onEdgeSelect, measured: _measured, maxNodeWidth, showEdgeLabels, showSingletonFrames = true, edgeView, title, hideSidebar, walkthroughStepMode = 'focus', autoPlayWalkthroughs = false, walkthroughAutoPlayIntervalMs = WALKTHROUGH_PLAY_PAUSE_MS, zoomOnWalkthroughFocus = true, walkthroughFocusDurationMs = 300, graphTitle, showWalkthroughTitle = false, description, canvasOverlay, sidebarExtra, sidebarAfterDescription, diagnostic, issues, showIssues, focusIssueCategory, onSelectIssue, onApplyIssueFix, onHoverIssue, renderFileView, renderFileViewer, renderWalkthroughViewer, onFileSelect, componentVerification, onInspectSymbol, boundaryColors, hideDrawer = false, persistKey, liveEvents, agentsPanel }) {
|
|
126
126
|
const { theme } = useTheme();
|
|
127
127
|
const { fitView, fitBounds } = useReactFlow();
|
|
128
128
|
const viewport = useViewport();
|
|
129
129
|
// Restored once per mount from `localStorage` (see `readViewState`). Each
|
|
130
130
|
// graph is its own tab/mount, so `persistKey` is stable for a mount.
|
|
131
131
|
const persisted = useMemo(() => readViewState(persistKey), [persistKey]);
|
|
132
|
-
const graphEdges = useMemo(() => deriveGraphEdges({
|
|
132
|
+
const graphEdges = useMemo(() => deriveGraphEdges({ walkthroughs }), [walkthroughs]);
|
|
133
133
|
const [built, setBuilt] = useState({
|
|
134
134
|
nodes: [],
|
|
135
135
|
edges: [],
|
|
@@ -165,24 +165,19 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
165
165
|
const [sidebarView, setSidebarView] = useState(() => persisted.sidebarView ?? (walkthroughs?.length ? 'walkthroughs' : 'files'));
|
|
166
166
|
// Diagnostics area tab: issues list vs the Maintain agent pipeline.
|
|
167
167
|
const [diagnosticsTab, setDiagnosticsTab] = useState(() => persisted.diagnosticsTab ?? 'issues');
|
|
168
|
-
//
|
|
169
|
-
//
|
|
170
|
-
// visible; it reverts when the focus is undone (see `unfocusIssueTarget`) or
|
|
171
|
-
// when the diagnostics list closes. Null = use the host-pinned / tab view.
|
|
172
|
-
const [edgeViewOverride, setEdgeViewOverride] = useState(null);
|
|
173
|
-
// One edge vocabulary at a time. When the caller doesn't pick, the sidebar's
|
|
174
|
-
// Files / Walkthroughs tab picks: Files draws topology relation edges,
|
|
168
|
+
// One edge source at a time. When the caller doesn't pick, the sidebar's
|
|
169
|
+
// Files / Walkthroughs tab picks: Files draws graphify static edges,
|
|
175
170
|
// Walkthroughs draws runtime hop edges. Without visible tabs (no walkthroughs,
|
|
176
|
-
// or a sidebar-less embed) fall back to
|
|
177
|
-
// to walkthrough edges rather than empty, everything else to relations.
|
|
171
|
+
// or a sidebar-less embed) fall back to walkthrough edges.
|
|
178
172
|
const sidebarTabsVisible = !hideSidebar && (walkthroughs?.length ?? 0) > 0;
|
|
179
|
-
const resolvedEdgeView =
|
|
180
|
-
edgeView ??
|
|
173
|
+
const resolvedEdgeView = edgeView ??
|
|
181
174
|
(sidebarTabsVisible
|
|
182
|
-
? (sidebarView === 'walkthroughs' ? 'walkthroughs' : '
|
|
183
|
-
:
|
|
175
|
+
? (sidebarView === 'walkthroughs' ? 'walkthroughs' : 'graphify')
|
|
176
|
+
: (walkthroughs?.length ?? 0) > 0
|
|
184
177
|
? 'walkthroughs'
|
|
185
|
-
:
|
|
178
|
+
: (graphifyRelations?.length ?? 0) > 0
|
|
179
|
+
? 'graphify'
|
|
180
|
+
: 'walkthroughs');
|
|
186
181
|
// Walkthrough flows the user has expanded (via the title row). Closed by
|
|
187
182
|
// default so a graph with several flows doesn't dump every step list at once.
|
|
188
183
|
// Restored from the persisted view state when a `persistKey` is set.
|
|
@@ -381,13 +376,11 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
381
376
|
// Pass 1: build with estimated widths so React Flow can measure the DOM.
|
|
382
377
|
// The pane stays hidden until Pass 2 completes. Key off layout-affecting
|
|
383
378
|
// fields only — declarationRef updates after verify must not re-run ELK.
|
|
384
|
-
const layoutKey = useMemo(() => subsystemGraphLayoutKey({ components,
|
|
379
|
+
const layoutKey = useMemo(() => subsystemGraphLayoutKey({ components, walkthroughs, graphifyRelations }), [components, walkthroughs, graphifyRelations]);
|
|
385
380
|
const componentsRef = useRef(components);
|
|
386
|
-
const relationsRef = useRef(relations);
|
|
387
381
|
const walkthroughsRef = useRef(walkthroughs);
|
|
388
382
|
const graphifyRelationsRef = useRef(graphifyRelations);
|
|
389
383
|
componentsRef.current = components;
|
|
390
|
-
relationsRef.current = relations;
|
|
391
384
|
walkthroughsRef.current = walkthroughs;
|
|
392
385
|
graphifyRelationsRef.current = graphifyRelations;
|
|
393
386
|
// Track measured dimensions from React Flow's dimension changes.
|
|
@@ -406,7 +399,6 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
406
399
|
pass2GenRef.current += 1;
|
|
407
400
|
const doc = {
|
|
408
401
|
components: componentsRef.current,
|
|
409
|
-
relations: relationsRef.current,
|
|
410
402
|
walkthroughs: walkthroughsRef.current,
|
|
411
403
|
};
|
|
412
404
|
void buildSubsystemGraph(doc, {
|
|
@@ -465,7 +457,7 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
465
457
|
const measuredWidths = new Map(leafNodes.map((n) => [n.id, dims.get(n.id).width]));
|
|
466
458
|
const measuredHeights = new Map(leafNodes.map((n) => [n.id, dims.get(n.id).height]));
|
|
467
459
|
const gen = ++pass2GenRef.current;
|
|
468
|
-
void buildSubsystemGraph({ components,
|
|
460
|
+
void buildSubsystemGraph({ components, walkthroughs }, { maxNodeWidth, showEdgeLabels, measuredWidths, measuredHeights, showSingletonFrames, graphifyRelations, orderByLine })
|
|
469
461
|
.then(({ nodes, edges: e, absoluteRects }) => {
|
|
470
462
|
if (gen !== pass2GenRef.current)
|
|
471
463
|
return;
|
|
@@ -479,7 +471,7 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
479
471
|
// Reveal Pass 1 layout rather than leaving the cover up forever.
|
|
480
472
|
setLayoutReady(true);
|
|
481
473
|
});
|
|
482
|
-
}, [built.nodes, components,
|
|
474
|
+
}, [built.nodes, components, walkthroughs, graphifyRelations, orderByLine, maxNodeWidth, showEdgeLabels]);
|
|
483
475
|
// After Pass 1 commits, try Pass 2 immediately with retained measurements.
|
|
484
476
|
// Same-id live updates often get no new React Flow `dimensions` events, so
|
|
485
477
|
// waiting only on onNodesChange leaves the cover stuck.
|
|
@@ -559,7 +551,7 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
559
551
|
const baseEdges = convertedEdges;
|
|
560
552
|
// Resolve an issue's component target to the matching node. `id` is the
|
|
561
553
|
// stable alias; `label` may be an alias, name, or symbol. Non-component
|
|
562
|
-
// targets (
|
|
554
|
+
// targets (module / flow / repo) have no single node → null.
|
|
563
555
|
const issueComponent = useCallback((issue) => {
|
|
564
556
|
const target = issue.target;
|
|
565
557
|
if (target?.kind !== 'component')
|
|
@@ -569,26 +561,6 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
569
561
|
c.name === target.label ||
|
|
570
562
|
c.symbol === target.label) ?? null);
|
|
571
563
|
}, [components]);
|
|
572
|
-
// Resolve a `relation` issue target to the display edge it flags. The target
|
|
573
|
-
// names the two endpoints (`from → to`); match an edge joining both, in
|
|
574
|
-
// either direction, preferring the target's `detail` mechanism when it names
|
|
575
|
-
// one. Null when no display edge matches.
|
|
576
|
-
const issueEdge = useCallback((issue) => {
|
|
577
|
-
const target = issue.target;
|
|
578
|
-
if (target?.kind !== 'relation')
|
|
579
|
-
return null;
|
|
580
|
-
const parts = `${target.id ?? ''} → ${target.label}`
|
|
581
|
-
.split('→')
|
|
582
|
-
.map((p) => p.replace(/\(\)$/, '').trim())
|
|
583
|
-
.filter(Boolean);
|
|
584
|
-
const joins = (e) => parts.includes(e.source) && parts.includes(e.target);
|
|
585
|
-
const matches = baseEdges.filter(joins);
|
|
586
|
-
const byMechanism = target.detail
|
|
587
|
-
? matches.filter((e) => e.data?.mechanism ===
|
|
588
|
-
target.detail)
|
|
589
|
-
: matches;
|
|
590
|
-
return byMechanism[0] ?? matches[0] ?? null;
|
|
591
|
-
}, [baseEdges]);
|
|
592
564
|
// A `module` issue target names a boundary frame, so focusing the card frames
|
|
593
565
|
// that region on the canvas. Null when the target names no frame (a singleton
|
|
594
566
|
// dropped from the layout, or a region key nothing declares).
|
|
@@ -692,7 +664,6 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
692
664
|
// the canvas keeps while everything else dims. Derived from each finding's
|
|
693
665
|
// target:
|
|
694
666
|
// component → that node
|
|
695
|
-
// relation → the edge it flags (+ its two endpoint nodes)
|
|
696
667
|
// module → every component in that module
|
|
697
668
|
// walkthrough → the flow's nodes + its hop edges
|
|
698
669
|
// repo → every component of that repo
|
|
@@ -717,23 +688,6 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
717
688
|
if (a)
|
|
718
689
|
nodeIds.add(a);
|
|
719
690
|
}
|
|
720
|
-
else if (t.kind === 'relation') {
|
|
721
|
-
// The target names the two endpoints (`from → to`); light the edge and
|
|
722
|
-
// its endpoints. Falls back to endpoints alone if no edge matches.
|
|
723
|
-
const edge = issueEdge(issue);
|
|
724
|
-
if (edge) {
|
|
725
|
-
edgeIds.add(edge.id);
|
|
726
|
-
nodeIds.add(edge.source);
|
|
727
|
-
nodeIds.add(edge.target);
|
|
728
|
-
}
|
|
729
|
-
else {
|
|
730
|
-
for (const part of `${t.id ?? ''} → ${t.label}`.split('→')) {
|
|
731
|
-
const a = resolveAlias(part);
|
|
732
|
-
if (a)
|
|
733
|
-
nodeIds.add(a);
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
691
|
else if (t.kind === 'module') {
|
|
738
692
|
const key = t.id ?? t.label;
|
|
739
693
|
const prefix = `${key.replace(/\/$/, '')}/`;
|
|
@@ -773,7 +727,7 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
773
727
|
edgeIds.add(e.id);
|
|
774
728
|
}
|
|
775
729
|
return { nodeIds, edgeIds };
|
|
776
|
-
}, [issues, expandedIssueCategories, components, walkthroughs,
|
|
730
|
+
}, [issues, expandedIssueCategories, components, walkthroughs, resolveAlias]);
|
|
777
731
|
const issueFocusNodeIds = issueFocus?.nodeIds ?? null;
|
|
778
732
|
const issueFocusEdgeIds = issueFocus?.edgeIds ?? null;
|
|
779
733
|
// When an edge is selected, dim every other edge + its label to focus it.
|
|
@@ -1016,13 +970,13 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
1016
970
|
};
|
|
1017
971
|
};
|
|
1018
972
|
// Edges outside the selected view are hidden entirely (labels included).
|
|
1019
|
-
// graphify-native edges belong to the
|
|
973
|
+
// graphify-native edges belong to the graphify view (they are static
|
|
1020
974
|
// topology, not runtime hops) regardless of their verb.
|
|
1021
975
|
const edgeInView = (e) => {
|
|
1022
976
|
const d = e.data;
|
|
1023
977
|
const mechanism = d?.mechanism ?? 'uses';
|
|
1024
|
-
if (resolvedEdgeView === '
|
|
1025
|
-
return d?.provenance === 'graphify'
|
|
978
|
+
if (resolvedEdgeView === 'graphify') {
|
|
979
|
+
return d?.provenance === 'graphify';
|
|
1026
980
|
}
|
|
1027
981
|
return isWalkthroughMechanism(mechanism);
|
|
1028
982
|
};
|
|
@@ -1103,7 +1057,6 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
1103
1057
|
setSelectedEdgeId(null);
|
|
1104
1058
|
setFocusedWalkthroughId(null);
|
|
1105
1059
|
setFocusedStepIndex(null);
|
|
1106
|
-
setEdgeViewOverride(null);
|
|
1107
1060
|
issueModuleFocusRef.current = null;
|
|
1108
1061
|
}, []);
|
|
1109
1062
|
// Sidebar file trees — one per repo on multi-repo graphs, each under its
|
|
@@ -1123,7 +1076,6 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
1123
1076
|
useEffect(() => {
|
|
1124
1077
|
if (!issuesActive) {
|
|
1125
1078
|
setExpandedIssueCategories([]);
|
|
1126
|
-
setEdgeViewOverride(null);
|
|
1127
1079
|
}
|
|
1128
1080
|
}, [issuesActive]);
|
|
1129
1081
|
const toggleIssues = () => {
|
|
@@ -1551,9 +1503,7 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
1551
1503
|
if (playable.length === 0)
|
|
1552
1504
|
return;
|
|
1553
1505
|
// Only run the cycles that can actually be shown in the current edge view.
|
|
1554
|
-
const inView = playable.filter((tl) => tl.steps.some((s) =>
|
|
1555
|
-
? isRelationMechanism(s.mechanism)
|
|
1556
|
-
: isWalkthroughMechanism(s.mechanism)));
|
|
1506
|
+
const inView = playable.filter((tl) => tl.steps.some((s) => isWalkthroughMechanism(s.mechanism)));
|
|
1557
1507
|
if (inView.length === 0)
|
|
1558
1508
|
return;
|
|
1559
1509
|
const cyc = inView;
|
|
@@ -1706,15 +1656,13 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
1706
1656
|
onSelect?.(comp.alias);
|
|
1707
1657
|
}, [components, onSelect]);
|
|
1708
1658
|
// Issue click → select + frame the target on the canvas. Component targets
|
|
1709
|
-
// frame their node;
|
|
1710
|
-
//
|
|
1711
|
-
//
|
|
1712
|
-
//
|
|
1713
|
-
// still fires afterwards for its own detail.
|
|
1659
|
+
// frame their node; other kinds (module / flow / repo) have no single element
|
|
1660
|
+
// and just forward. Selections / focus that would hide the target are cleared
|
|
1661
|
+
// first, then the camera flies to it on the next frame. The host's
|
|
1662
|
+
// `onSelectIssue` still fires afterwards for its own detail.
|
|
1714
1663
|
const focusIssueTarget = useCallback((issue) => {
|
|
1715
1664
|
const comp = issueComponent(issue);
|
|
1716
|
-
const
|
|
1717
|
-
const step = comp || edge ? null : issueStep(issue);
|
|
1665
|
+
const step = comp ? null : issueStep(issue);
|
|
1718
1666
|
if (comp) {
|
|
1719
1667
|
setSelected(comp);
|
|
1720
1668
|
setSelectedEdgeId(null);
|
|
@@ -1733,24 +1681,6 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
1733
1681
|
});
|
|
1734
1682
|
});
|
|
1735
1683
|
}
|
|
1736
|
-
else if (edge) {
|
|
1737
|
-
setSelected(null);
|
|
1738
|
-
setFocusedWalkthroughId(null);
|
|
1739
|
-
setFocusedStepIndex(null);
|
|
1740
|
-
setHoveredWalkthroughStep(null);
|
|
1741
|
-
setSelectedEdgeId(edge.id);
|
|
1742
|
-
issueModuleFocusRef.current = null;
|
|
1743
|
-
// Flip to the vocabulary this edge belongs to, so a hop flagged in the
|
|
1744
|
-
// relations view (or vice versa) is actually drawn while focused.
|
|
1745
|
-
const d = edge.data;
|
|
1746
|
-
const mechanism = d?.mechanism ?? 'uses';
|
|
1747
|
-
setEdgeViewOverride(d?.provenance === 'graphify' || isRelationMechanism(mechanism)
|
|
1748
|
-
? 'relations'
|
|
1749
|
-
: 'walkthroughs');
|
|
1750
|
-
requestAnimationFrame(() => {
|
|
1751
|
-
fitFocusBounds(new Set([edge.id]));
|
|
1752
|
-
});
|
|
1753
|
-
}
|
|
1754
1684
|
else if (step) {
|
|
1755
1685
|
// A step finding focuses the step itself, through the same entry point
|
|
1756
1686
|
// the walkthrough panel uses — so the drawer, the step numbering, the
|
|
@@ -1778,14 +1708,12 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
1778
1708
|
onSelectIssue?.(issue);
|
|
1779
1709
|
}, [
|
|
1780
1710
|
issueComponent,
|
|
1781
|
-
issueEdge,
|
|
1782
1711
|
issueStep,
|
|
1783
1712
|
issueModuleNodeId,
|
|
1784
1713
|
focusWalkthroughStep,
|
|
1785
1714
|
onSelect,
|
|
1786
1715
|
onSelectIssue,
|
|
1787
1716
|
fitView,
|
|
1788
|
-
fitFocusBounds,
|
|
1789
1717
|
fitNodeRects,
|
|
1790
1718
|
]);
|
|
1791
1719
|
// Collapsing the issue card again undoes the focus: drop the node / edge
|
|
@@ -1803,16 +1731,9 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
1803
1731
|
setSelectedEdgeId(null);
|
|
1804
1732
|
}
|
|
1805
1733
|
else {
|
|
1806
|
-
const
|
|
1807
|
-
const
|
|
1808
|
-
|
|
1809
|
-
if (edge) {
|
|
1810
|
-
if (selectedEdgeIdRef.current !== edge.id)
|
|
1811
|
-
return;
|
|
1812
|
-
setSelectedEdgeId(null);
|
|
1813
|
-
setEdgeViewOverride(null);
|
|
1814
|
-
}
|
|
1815
|
-
else if (step) {
|
|
1734
|
+
const step = issueStep(issue);
|
|
1735
|
+
const moduleNodeId = step ? null : issueModuleNodeId(issue);
|
|
1736
|
+
if (step) {
|
|
1816
1737
|
// Only unwind a step focus this card still owns. A dim-mode graph
|
|
1817
1738
|
// keeps its step focus in hover state instead (owned by the pointer,
|
|
1818
1739
|
// not the card), and correctly leaves that alone here.
|
|
@@ -1838,7 +1759,7 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
1838
1759
|
requestAnimationFrame(() => {
|
|
1839
1760
|
fitView({ padding: 0.1, duration: 300, minZoom: 0.05, maxZoom: 2 });
|
|
1840
1761
|
});
|
|
1841
|
-
}, [issueComponent,
|
|
1762
|
+
}, [issueComponent, issueStep, issueModuleNodeId, fitView]);
|
|
1842
1763
|
// Construct tokens inside a walkthrough snippet. A step's line is an edge
|
|
1843
1764
|
// between its `from`/`to` components, so a token naming either of those
|
|
1844
1765
|
// constructs should navigate to that construct's declaration. Index the
|
|
@@ -2326,7 +2247,6 @@ export function SubsystemComponentGraph(props) {
|
|
|
2326
2247
|
}, []);
|
|
2327
2248
|
const constructsOnly = isConstructsOnlyModel({
|
|
2328
2249
|
components: props.components,
|
|
2329
|
-
relations: props.relations,
|
|
2330
2250
|
walkthroughs: props.walkthroughs,
|
|
2331
2251
|
graphifyRelations: props.graphifyRelations,
|
|
2332
2252
|
});
|