@principal-ai/subsystems-react 0.41.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 +42 -112
- 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 +55 -136
- 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: [],
|
|
@@ -162,27 +162,22 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
162
162
|
// a step is already selected.
|
|
163
163
|
const [hoveredWalkthroughStep, setHoveredWalkthroughStep] = useState(null);
|
|
164
164
|
// Sidebar bottom half: which panel is shown when walkthroughs exist.
|
|
165
|
-
const [sidebarView, setSidebarView] = useState(() => walkthroughs?.length ? 'walkthroughs' : 'files');
|
|
165
|
+
const [sidebarView, setSidebarView] = useState(() => persisted.sidebarView ?? (walkthroughs?.length ? 'walkthroughs' : 'files'));
|
|
166
166
|
// Diagnostics area tab: issues list vs the Maintain agent pipeline.
|
|
167
|
-
const [diagnosticsTab, setDiagnosticsTab] = useState('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,
|
|
167
|
+
const [diagnosticsTab, setDiagnosticsTab] = useState(() => persisted.diagnosticsTab ?? 'issues');
|
|
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.
|
|
@@ -255,6 +250,16 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
255
250
|
return;
|
|
256
251
|
writeViewState(persistKey, { focusedStepIndex });
|
|
257
252
|
}, [persistKey, focusedStepIndex]);
|
|
253
|
+
useEffect(() => {
|
|
254
|
+
if (!persistKey)
|
|
255
|
+
return;
|
|
256
|
+
writeViewState(persistKey, { sidebarView });
|
|
257
|
+
}, [persistKey, sidebarView]);
|
|
258
|
+
useEffect(() => {
|
|
259
|
+
if (!persistKey)
|
|
260
|
+
return;
|
|
261
|
+
writeViewState(persistKey, { diagnosticsTab });
|
|
262
|
+
}, [persistKey, diagnosticsTab]);
|
|
258
263
|
// Ref mirror of `selected` so the SUBSYSTEM_CALLBACKS click handler (a
|
|
259
264
|
// closure over the effect deps) can toggle without a stale value.
|
|
260
265
|
const selectedRef = useRef(null);
|
|
@@ -371,13 +376,11 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
371
376
|
// Pass 1: build with estimated widths so React Flow can measure the DOM.
|
|
372
377
|
// The pane stays hidden until Pass 2 completes. Key off layout-affecting
|
|
373
378
|
// fields only — declarationRef updates after verify must not re-run ELK.
|
|
374
|
-
const layoutKey = useMemo(() => subsystemGraphLayoutKey({ components,
|
|
379
|
+
const layoutKey = useMemo(() => subsystemGraphLayoutKey({ components, walkthroughs, graphifyRelations }), [components, walkthroughs, graphifyRelations]);
|
|
375
380
|
const componentsRef = useRef(components);
|
|
376
|
-
const relationsRef = useRef(relations);
|
|
377
381
|
const walkthroughsRef = useRef(walkthroughs);
|
|
378
382
|
const graphifyRelationsRef = useRef(graphifyRelations);
|
|
379
383
|
componentsRef.current = components;
|
|
380
|
-
relationsRef.current = relations;
|
|
381
384
|
walkthroughsRef.current = walkthroughs;
|
|
382
385
|
graphifyRelationsRef.current = graphifyRelations;
|
|
383
386
|
// Track measured dimensions from React Flow's dimension changes.
|
|
@@ -396,7 +399,6 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
396
399
|
pass2GenRef.current += 1;
|
|
397
400
|
const doc = {
|
|
398
401
|
components: componentsRef.current,
|
|
399
|
-
relations: relationsRef.current,
|
|
400
402
|
walkthroughs: walkthroughsRef.current,
|
|
401
403
|
};
|
|
402
404
|
void buildSubsystemGraph(doc, {
|
|
@@ -455,7 +457,7 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
455
457
|
const measuredWidths = new Map(leafNodes.map((n) => [n.id, dims.get(n.id).width]));
|
|
456
458
|
const measuredHeights = new Map(leafNodes.map((n) => [n.id, dims.get(n.id).height]));
|
|
457
459
|
const gen = ++pass2GenRef.current;
|
|
458
|
-
void buildSubsystemGraph({ components,
|
|
460
|
+
void buildSubsystemGraph({ components, walkthroughs }, { maxNodeWidth, showEdgeLabels, measuredWidths, measuredHeights, showSingletonFrames, graphifyRelations, orderByLine })
|
|
459
461
|
.then(({ nodes, edges: e, absoluteRects }) => {
|
|
460
462
|
if (gen !== pass2GenRef.current)
|
|
461
463
|
return;
|
|
@@ -469,7 +471,7 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
469
471
|
// Reveal Pass 1 layout rather than leaving the cover up forever.
|
|
470
472
|
setLayoutReady(true);
|
|
471
473
|
});
|
|
472
|
-
}, [built.nodes, components,
|
|
474
|
+
}, [built.nodes, components, walkthroughs, graphifyRelations, orderByLine, maxNodeWidth, showEdgeLabels]);
|
|
473
475
|
// After Pass 1 commits, try Pass 2 immediately with retained measurements.
|
|
474
476
|
// Same-id live updates often get no new React Flow `dimensions` events, so
|
|
475
477
|
// waiting only on onNodesChange leaves the cover stuck.
|
|
@@ -549,7 +551,7 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
549
551
|
const baseEdges = convertedEdges;
|
|
550
552
|
// Resolve an issue's component target to the matching node. `id` is the
|
|
551
553
|
// stable alias; `label` may be an alias, name, or symbol. Non-component
|
|
552
|
-
// targets (
|
|
554
|
+
// targets (module / flow / repo) have no single node → null.
|
|
553
555
|
const issueComponent = useCallback((issue) => {
|
|
554
556
|
const target = issue.target;
|
|
555
557
|
if (target?.kind !== 'component')
|
|
@@ -559,26 +561,6 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
559
561
|
c.name === target.label ||
|
|
560
562
|
c.symbol === target.label) ?? null);
|
|
561
563
|
}, [components]);
|
|
562
|
-
// Resolve a `relation` issue target to the display edge it flags. The target
|
|
563
|
-
// names the two endpoints (`from → to`); match an edge joining both, in
|
|
564
|
-
// either direction, preferring the target's `detail` mechanism when it names
|
|
565
|
-
// one. Null when no display edge matches.
|
|
566
|
-
const issueEdge = useCallback((issue) => {
|
|
567
|
-
const target = issue.target;
|
|
568
|
-
if (target?.kind !== 'relation')
|
|
569
|
-
return null;
|
|
570
|
-
const parts = `${target.id ?? ''} → ${target.label}`
|
|
571
|
-
.split('→')
|
|
572
|
-
.map((p) => p.replace(/\(\)$/, '').trim())
|
|
573
|
-
.filter(Boolean);
|
|
574
|
-
const joins = (e) => parts.includes(e.source) && parts.includes(e.target);
|
|
575
|
-
const matches = baseEdges.filter(joins);
|
|
576
|
-
const byMechanism = target.detail
|
|
577
|
-
? matches.filter((e) => e.data?.mechanism ===
|
|
578
|
-
target.detail)
|
|
579
|
-
: matches;
|
|
580
|
-
return byMechanism[0] ?? matches[0] ?? null;
|
|
581
|
-
}, [baseEdges]);
|
|
582
564
|
// A `module` issue target names a boundary frame, so focusing the card frames
|
|
583
565
|
// that region on the canvas. Null when the target names no frame (a singleton
|
|
584
566
|
// dropped from the layout, or a region key nothing declares).
|
|
@@ -682,7 +664,6 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
682
664
|
// the canvas keeps while everything else dims. Derived from each finding's
|
|
683
665
|
// target:
|
|
684
666
|
// component → that node
|
|
685
|
-
// relation → the edge it flags (+ its two endpoint nodes)
|
|
686
667
|
// module → every component in that module
|
|
687
668
|
// walkthrough → the flow's nodes + its hop edges
|
|
688
669
|
// repo → every component of that repo
|
|
@@ -707,23 +688,6 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
707
688
|
if (a)
|
|
708
689
|
nodeIds.add(a);
|
|
709
690
|
}
|
|
710
|
-
else if (t.kind === 'relation') {
|
|
711
|
-
// The target names the two endpoints (`from → to`); light the edge and
|
|
712
|
-
// its endpoints. Falls back to endpoints alone if no edge matches.
|
|
713
|
-
const edge = issueEdge(issue);
|
|
714
|
-
if (edge) {
|
|
715
|
-
edgeIds.add(edge.id);
|
|
716
|
-
nodeIds.add(edge.source);
|
|
717
|
-
nodeIds.add(edge.target);
|
|
718
|
-
}
|
|
719
|
-
else {
|
|
720
|
-
for (const part of `${t.id ?? ''} → ${t.label}`.split('→')) {
|
|
721
|
-
const a = resolveAlias(part);
|
|
722
|
-
if (a)
|
|
723
|
-
nodeIds.add(a);
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
691
|
else if (t.kind === 'module') {
|
|
728
692
|
const key = t.id ?? t.label;
|
|
729
693
|
const prefix = `${key.replace(/\/$/, '')}/`;
|
|
@@ -763,7 +727,7 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
763
727
|
edgeIds.add(e.id);
|
|
764
728
|
}
|
|
765
729
|
return { nodeIds, edgeIds };
|
|
766
|
-
}, [issues, expandedIssueCategories, components, walkthroughs,
|
|
730
|
+
}, [issues, expandedIssueCategories, components, walkthroughs, resolveAlias]);
|
|
767
731
|
const issueFocusNodeIds = issueFocus?.nodeIds ?? null;
|
|
768
732
|
const issueFocusEdgeIds = issueFocus?.edgeIds ?? null;
|
|
769
733
|
// When an edge is selected, dim every other edge + its label to focus it.
|
|
@@ -1006,13 +970,13 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
1006
970
|
};
|
|
1007
971
|
};
|
|
1008
972
|
// Edges outside the selected view are hidden entirely (labels included).
|
|
1009
|
-
// graphify-native edges belong to the
|
|
973
|
+
// graphify-native edges belong to the graphify view (they are static
|
|
1010
974
|
// topology, not runtime hops) regardless of their verb.
|
|
1011
975
|
const edgeInView = (e) => {
|
|
1012
976
|
const d = e.data;
|
|
1013
977
|
const mechanism = d?.mechanism ?? 'uses';
|
|
1014
|
-
if (resolvedEdgeView === '
|
|
1015
|
-
return d?.provenance === 'graphify'
|
|
978
|
+
if (resolvedEdgeView === 'graphify') {
|
|
979
|
+
return d?.provenance === 'graphify';
|
|
1016
980
|
}
|
|
1017
981
|
return isWalkthroughMechanism(mechanism);
|
|
1018
982
|
};
|
|
@@ -1093,7 +1057,6 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
1093
1057
|
setSelectedEdgeId(null);
|
|
1094
1058
|
setFocusedWalkthroughId(null);
|
|
1095
1059
|
setFocusedStepIndex(null);
|
|
1096
|
-
setEdgeViewOverride(null);
|
|
1097
1060
|
issueModuleFocusRef.current = null;
|
|
1098
1061
|
}, []);
|
|
1099
1062
|
// Sidebar file trees — one per repo on multi-repo graphs, each under its
|
|
@@ -1113,7 +1076,6 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
1113
1076
|
useEffect(() => {
|
|
1114
1077
|
if (!issuesActive) {
|
|
1115
1078
|
setExpandedIssueCategories([]);
|
|
1116
|
-
setEdgeViewOverride(null);
|
|
1117
1079
|
}
|
|
1118
1080
|
}, [issuesActive]);
|
|
1119
1081
|
const toggleIssues = () => {
|
|
@@ -1541,9 +1503,7 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
1541
1503
|
if (playable.length === 0)
|
|
1542
1504
|
return;
|
|
1543
1505
|
// Only run the cycles that can actually be shown in the current edge view.
|
|
1544
|
-
const inView = playable.filter((tl) => tl.steps.some((s) =>
|
|
1545
|
-
? isRelationMechanism(s.mechanism)
|
|
1546
|
-
: isWalkthroughMechanism(s.mechanism)));
|
|
1506
|
+
const inView = playable.filter((tl) => tl.steps.some((s) => isWalkthroughMechanism(s.mechanism)));
|
|
1547
1507
|
if (inView.length === 0)
|
|
1548
1508
|
return;
|
|
1549
1509
|
const cyc = inView;
|
|
@@ -1696,15 +1656,13 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
1696
1656
|
onSelect?.(comp.alias);
|
|
1697
1657
|
}, [components, onSelect]);
|
|
1698
1658
|
// Issue click → select + frame the target on the canvas. Component targets
|
|
1699
|
-
// frame their node;
|
|
1700
|
-
//
|
|
1701
|
-
//
|
|
1702
|
-
//
|
|
1703
|
-
// 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.
|
|
1704
1663
|
const focusIssueTarget = useCallback((issue) => {
|
|
1705
1664
|
const comp = issueComponent(issue);
|
|
1706
|
-
const
|
|
1707
|
-
const step = comp || edge ? null : issueStep(issue);
|
|
1665
|
+
const step = comp ? null : issueStep(issue);
|
|
1708
1666
|
if (comp) {
|
|
1709
1667
|
setSelected(comp);
|
|
1710
1668
|
setSelectedEdgeId(null);
|
|
@@ -1723,24 +1681,6 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
1723
1681
|
});
|
|
1724
1682
|
});
|
|
1725
1683
|
}
|
|
1726
|
-
else if (edge) {
|
|
1727
|
-
setSelected(null);
|
|
1728
|
-
setFocusedWalkthroughId(null);
|
|
1729
|
-
setFocusedStepIndex(null);
|
|
1730
|
-
setHoveredWalkthroughStep(null);
|
|
1731
|
-
setSelectedEdgeId(edge.id);
|
|
1732
|
-
issueModuleFocusRef.current = null;
|
|
1733
|
-
// Flip to the vocabulary this edge belongs to, so a hop flagged in the
|
|
1734
|
-
// relations view (or vice versa) is actually drawn while focused.
|
|
1735
|
-
const d = edge.data;
|
|
1736
|
-
const mechanism = d?.mechanism ?? 'uses';
|
|
1737
|
-
setEdgeViewOverride(d?.provenance === 'graphify' || isRelationMechanism(mechanism)
|
|
1738
|
-
? 'relations'
|
|
1739
|
-
: 'walkthroughs');
|
|
1740
|
-
requestAnimationFrame(() => {
|
|
1741
|
-
fitFocusBounds(new Set([edge.id]));
|
|
1742
|
-
});
|
|
1743
|
-
}
|
|
1744
1684
|
else if (step) {
|
|
1745
1685
|
// A step finding focuses the step itself, through the same entry point
|
|
1746
1686
|
// the walkthrough panel uses — so the drawer, the step numbering, the
|
|
@@ -1768,14 +1708,12 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
1768
1708
|
onSelectIssue?.(issue);
|
|
1769
1709
|
}, [
|
|
1770
1710
|
issueComponent,
|
|
1771
|
-
issueEdge,
|
|
1772
1711
|
issueStep,
|
|
1773
1712
|
issueModuleNodeId,
|
|
1774
1713
|
focusWalkthroughStep,
|
|
1775
1714
|
onSelect,
|
|
1776
1715
|
onSelectIssue,
|
|
1777
1716
|
fitView,
|
|
1778
|
-
fitFocusBounds,
|
|
1779
1717
|
fitNodeRects,
|
|
1780
1718
|
]);
|
|
1781
1719
|
// Collapsing the issue card again undoes the focus: drop the node / edge
|
|
@@ -1793,16 +1731,9 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
1793
1731
|
setSelectedEdgeId(null);
|
|
1794
1732
|
}
|
|
1795
1733
|
else {
|
|
1796
|
-
const
|
|
1797
|
-
const
|
|
1798
|
-
|
|
1799
|
-
if (edge) {
|
|
1800
|
-
if (selectedEdgeIdRef.current !== edge.id)
|
|
1801
|
-
return;
|
|
1802
|
-
setSelectedEdgeId(null);
|
|
1803
|
-
setEdgeViewOverride(null);
|
|
1804
|
-
}
|
|
1805
|
-
else if (step) {
|
|
1734
|
+
const step = issueStep(issue);
|
|
1735
|
+
const moduleNodeId = step ? null : issueModuleNodeId(issue);
|
|
1736
|
+
if (step) {
|
|
1806
1737
|
// Only unwind a step focus this card still owns. A dim-mode graph
|
|
1807
1738
|
// keeps its step focus in hover state instead (owned by the pointer,
|
|
1808
1739
|
// not the card), and correctly leaves that alone here.
|
|
@@ -1828,7 +1759,7 @@ function Inner({ components, relations, walkthroughs, graphifyRelations, orderBy
|
|
|
1828
1759
|
requestAnimationFrame(() => {
|
|
1829
1760
|
fitView({ padding: 0.1, duration: 300, minZoom: 0.05, maxZoom: 2 });
|
|
1830
1761
|
});
|
|
1831
|
-
}, [issueComponent,
|
|
1762
|
+
}, [issueComponent, issueStep, issueModuleNodeId, fitView]);
|
|
1832
1763
|
// Construct tokens inside a walkthrough snippet. A step's line is an edge
|
|
1833
1764
|
// between its `from`/`to` components, so a token naming either of those
|
|
1834
1765
|
// constructs should navigate to that construct's declaration. Index the
|
|
@@ -2316,7 +2247,6 @@ export function SubsystemComponentGraph(props) {
|
|
|
2316
2247
|
}, []);
|
|
2317
2248
|
const constructsOnly = isConstructsOnlyModel({
|
|
2318
2249
|
components: props.components,
|
|
2319
|
-
relations: props.relations,
|
|
2320
2250
|
walkthroughs: props.walkthroughs,
|
|
2321
2251
|
graphifyRelations: props.graphifyRelations,
|
|
2322
2252
|
});
|