@principal-ai/subsystems-react 0.53.6 → 0.55.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 +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/pierre/PierreTrailCodeView.d.ts.map +1 -1
- package/dist/pierre/PierreTrailCodeView.js +7 -5
- package/dist/pierre/PierreTrailCodeView.js.map +1 -1
- package/dist/stories/Subsystem/ComponentGraph/fixtures.d.ts.map +1 -1
- package/dist/stories/Subsystem/ComponentGraph/fixtures.js +1 -2
- package/dist/stories/Subsystem/ComponentGraph/fixtures.js.map +1 -1
- package/dist/subsystem/C4Graph.d.ts +144 -9
- package/dist/subsystem/C4Graph.d.ts.map +1 -1
- package/dist/subsystem/C4Graph.js +924 -148
- package/dist/subsystem/C4Graph.js.map +1 -1
- package/dist/subsystem/C4NodeCard.d.ts +22 -1
- package/dist/subsystem/C4NodeCard.d.ts.map +1 -1
- package/dist/subsystem/C4NodeCard.js +10 -2
- package/dist/subsystem/C4NodeCard.js.map +1 -1
- package/dist/subsystem/ComponentDeclaration.d.ts +5 -4
- package/dist/subsystem/ComponentDeclaration.d.ts.map +1 -1
- package/dist/subsystem/ComponentDeclaration.js +12 -4
- package/dist/subsystem/ComponentDeclaration.js.map +1 -1
- package/dist/subsystem/ConstructsCatalog.d.ts.map +1 -1
- package/dist/subsystem/ConstructsCatalog.js +10 -7
- package/dist/subsystem/ConstructsCatalog.js.map +1 -1
- package/dist/subsystem/GraphSidebar.d.ts +71 -0
- package/dist/subsystem/GraphSidebar.d.ts.map +1 -0
- package/dist/subsystem/GraphSidebar.js +145 -0
- package/dist/subsystem/GraphSidebar.js.map +1 -0
- package/dist/subsystem/GraphWithSidebar.d.ts +24 -0
- package/dist/subsystem/GraphWithSidebar.d.ts.map +1 -0
- package/dist/subsystem/GraphWithSidebar.js +25 -0
- package/dist/subsystem/GraphWithSidebar.js.map +1 -0
- package/dist/subsystem/IssueList.d.ts +4 -5
- package/dist/subsystem/IssueList.d.ts.map +1 -1
- package/dist/subsystem/IssueList.js +33 -28
- package/dist/subsystem/IssueList.js.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.d.ts +33 -0
- package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.js +367 -414
- package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
- package/dist/subsystem/TrailStepsNav.d.ts +48 -0
- package/dist/subsystem/TrailStepsNav.d.ts.map +1 -0
- package/dist/subsystem/TrailStepsNav.js +189 -0
- package/dist/subsystem/TrailStepsNav.js.map +1 -0
- package/dist/subsystem/TrailsPanel.d.ts.map +1 -1
- package/dist/subsystem/TrailsPanel.js +3 -4
- package/dist/subsystem/TrailsPanel.js.map +1 -1
- package/dist/subsystem/__fixtures__/c4Drilldown.d.ts +151 -0
- package/dist/subsystem/__fixtures__/c4Drilldown.d.ts.map +1 -0
- package/dist/subsystem/__fixtures__/c4Drilldown.js +783 -0
- package/dist/subsystem/__fixtures__/c4Drilldown.js.map +1 -0
- package/dist/subsystem/c4.d.ts +39 -8
- package/dist/subsystem/c4.d.ts.map +1 -1
- package/dist/subsystem/c4.js.map +1 -1
- package/dist/subsystem/declarationRef.d.ts +26 -18
- package/dist/subsystem/declarationRef.d.ts.map +1 -1
- package/dist/subsystem/declarationRef.js +9 -4
- package/dist/subsystem/declarationRef.js.map +1 -1
- package/dist/subsystem/model.d.ts +19 -1
- package/dist/subsystem/model.d.ts.map +1 -1
- package/dist/subsystem/model.js +12 -2
- package/dist/subsystem/model.js.map +1 -1
- package/dist/subsystem/nodes.d.ts.map +1 -1
- package/dist/subsystem/nodes.js +24 -4
- package/dist/subsystem/nodes.js.map +1 -1
- package/dist/subsystem/trailBrief.d.ts.map +1 -1
- package/dist/subsystem/trailBrief.js +6 -1
- package/dist/subsystem/trailBrief.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +9 -2
- package/src/pierre/PierreTrailCodeView.tsx +9 -13
- package/src/stories/Subsystem/C4Graph/C4ToComponentDrill.stories.tsx +496 -0
- package/src/stories/Subsystem/ComponentGraph/AnimatedProgression.stories.tsx +7 -7
- package/src/stories/Subsystem/ComponentGraph/Constructs.stories.tsx +19 -25
- package/src/stories/Subsystem/ComponentGraph/DetailPanel.stories.tsx +8 -10
- package/src/stories/Subsystem/ComponentGraph/Issues.stories.tsx +40 -33
- package/src/stories/Subsystem/ComponentGraph/ProposedTrails.stories.tsx +6 -6
- package/src/stories/Subsystem/ComponentGraph/TrailAutoplay.stories.tsx +7 -7
- package/src/stories/Subsystem/ComponentGraph/Trails.stories.tsx +27 -30
- package/src/stories/Subsystem/ComponentGraph/fixtures.ts +1 -2
- package/src/stories/Subsystem/ConstructsCatalog.stories.tsx +10 -13
- package/src/stories/Subsystem/EgoGraph/EgoGraph.stories.tsx +3 -4
- package/src/stories/Subsystem/IssueIcons.stories.tsx +65 -0
- package/src/stories/Subsystem/Sidebar/GraphSidebar.stories.tsx +206 -0
- package/src/subsystem/C4Graph.groups.test.ts +96 -2
- package/src/subsystem/C4Graph.tsx +1377 -165
- package/src/subsystem/C4NodeCard.test.ts +1 -2
- package/src/subsystem/C4NodeCard.tsx +24 -2
- package/src/subsystem/ComponentDeclaration.tsx +18 -8
- package/src/subsystem/ConstructsCatalog.tsx +23 -8
- package/src/subsystem/GraphSidebar.test.tsx +152 -0
- package/src/subsystem/GraphSidebar.tsx +300 -0
- package/src/subsystem/GraphWithSidebar.test.tsx +78 -0
- package/src/subsystem/GraphWithSidebar.tsx +60 -0
- package/src/subsystem/IssueList.tsx +34 -29
- package/src/subsystem/SubsystemComponentGraph.tsx +290 -376
- package/src/subsystem/TrailStepsNav.test.tsx +179 -0
- package/src/subsystem/TrailStepsNav.tsx +372 -0
- package/src/subsystem/TrailsPanel.tsx +3 -4
- package/src/subsystem/__fixtures__/c4Drilldown.test.ts +338 -0
- package/src/subsystem/__fixtures__/c4Drilldown.ts +847 -0
- package/src/subsystem/c4.ts +41 -8
- package/src/subsystem/declarationRef.ts +28 -19
- package/src/subsystem/model.test.ts +32 -12
- package/src/subsystem/model.ts +30 -1
- package/src/subsystem/nodes.group.test.tsx +82 -0
- package/src/subsystem/nodes.tsx +30 -2
- package/src/subsystem/trailBrief.test.ts +2 -2
- package/src/subsystem/trailBrief.ts +6 -1
|
@@ -85,6 +85,8 @@ import { ComponentDeclaration } from './ComponentDeclaration';
|
|
|
85
85
|
import type { ComponentVerificationState } from './ComponentDeclaration';
|
|
86
86
|
import type { DeclarationSymbolRef, SymbolInspection } from './symbolRefs';
|
|
87
87
|
import { FileDrawer, FILE_DRAWER_HEIGHT_MS } from './FileDrawer';
|
|
88
|
+
import { GraphWithSidebar } from './GraphWithSidebar';
|
|
89
|
+
import type { GraphSidebarTab } from './GraphSidebar';
|
|
88
90
|
import { buildRepoGroups, repoAvatarUrl, type RepoGroup } from './paths';
|
|
89
91
|
import { TrailsPanel, TRAIL_PLAY_PAUSE_MS } from './TrailsPanel';
|
|
90
92
|
import {
|
|
@@ -138,7 +140,7 @@ function constructIdentifiers(comp: SubsystemComponent): string[] {
|
|
|
138
140
|
}
|
|
139
141
|
|
|
140
142
|
type DrawerTarget =
|
|
141
|
-
| { kind: 'file'; file: string; startLine?: number }
|
|
143
|
+
| { kind: 'file'; file: string; startLine?: number; endLine?: number }
|
|
142
144
|
| { kind: 'trail'; trailId: string; stepIndex: number | null };
|
|
143
145
|
|
|
144
146
|
/**
|
|
@@ -152,6 +154,9 @@ interface PersistedViewState {
|
|
|
152
154
|
expandedTrails?: string[];
|
|
153
155
|
focusedTrailId?: string | null;
|
|
154
156
|
focusedStepIndex?: number | null;
|
|
157
|
+
/** Sidebar width moved to the shared GraphWithSidebar chrome (persisted
|
|
158
|
+
* under `principal.subsystems.sidebarWidth.<persistKey>`); reading the old
|
|
159
|
+
* field still restores a width saved before that move. */
|
|
155
160
|
sidebarWidth?: number;
|
|
156
161
|
sidebarView?: 'files' | 'trails';
|
|
157
162
|
diagnosticsTab?: 'issues' | 'agents';
|
|
@@ -219,6 +224,24 @@ export interface SubsystemComponentGraphProps {
|
|
|
219
224
|
* graph.
|
|
220
225
|
*/
|
|
221
226
|
initialTrailId?: string | null;
|
|
227
|
+
/**
|
|
228
|
+
* Reports trail focus changes from inside the graph — a trail (or step)
|
|
229
|
+
* clicked in the Trails panel, or focus cleared. This is the graph-driven
|
|
230
|
+
* mirror of `initialTrailId`: hosts that render a summary alongside the
|
|
231
|
+
* graph (e.g. a C4 view) can keep both in agreement. Fired on every focus
|
|
232
|
+
* change, including re-applying a host `initialTrailId` on mount — a host
|
|
233
|
+
* that itself drives the deep-link receives an idempotent echo of the same
|
|
234
|
+
* trail.
|
|
235
|
+
*/
|
|
236
|
+
onTrailSelect?: (ref: { trailId: string; stepIndices?: number[] } | null) => void;
|
|
237
|
+
/**
|
|
238
|
+
* Reports which trail step is currently hovered in the Trails panel —
|
|
239
|
+
* the transient preview mirror of `onTrailSelect`. `null` when nothing is
|
|
240
|
+
* hovered (or a whole flow row is). Hosts rendering a summary (e.g. a C4
|
|
241
|
+
* view) can dim to a single step's participants while the pointer is over
|
|
242
|
+
* a step row, and fall back to the focused trail on leave.
|
|
243
|
+
*/
|
|
244
|
+
onTrailStepHover?: (ref: { trailId: string; stepIndex: number } | null) => void;
|
|
222
245
|
/**
|
|
223
246
|
* Called with the next trail order after a drag in the sidebar's
|
|
224
247
|
* flows panel. When set, each row grows a drag grip and a drop emits the
|
|
@@ -429,6 +452,15 @@ export interface SubsystemComponentGraphProps {
|
|
|
429
452
|
* instead of accepting the hash. Unmapped regions keep the derived color.
|
|
430
453
|
*/
|
|
431
454
|
boundaryColors?: Record<string, string>;
|
|
455
|
+
/**
|
|
456
|
+
* Process keys whose frames draw package chrome (dashed, rounded) instead
|
|
457
|
+
* of runtime chrome (solid, square): the claim groups code organisation —
|
|
458
|
+
* a library, an area, a proposal — not an accepted deployment unit. Hosts
|
|
459
|
+
* derive this from their accepted C4 element set (a process no element
|
|
460
|
+
* backs is code, not a box). Presentation only — ids, issue targeting and
|
|
461
|
+
* membership are unchanged. See `BoundaryFrameOptions.codeProcessKeys`.
|
|
462
|
+
*/
|
|
463
|
+
codeProcessKeys?: ReadonlySet<string>;
|
|
432
464
|
}
|
|
433
465
|
|
|
434
466
|
const nodeTypes: NodeTypes = {
|
|
@@ -448,11 +480,13 @@ const FileDrawerContent = memo(function FileDrawerContent({
|
|
|
448
480
|
render,
|
|
449
481
|
file,
|
|
450
482
|
startLine,
|
|
483
|
+
endLine,
|
|
451
484
|
fullFile,
|
|
452
485
|
}: {
|
|
453
486
|
render: (file: string, opts?: SubsystemOpenFileOptions) => ReactNode;
|
|
454
487
|
file: string;
|
|
455
488
|
startLine?: number;
|
|
489
|
+
endLine?: number;
|
|
456
490
|
fullFile?: boolean;
|
|
457
491
|
}) {
|
|
458
492
|
return (
|
|
@@ -460,7 +494,11 @@ const FileDrawerContent = memo(function FileDrawerContent({
|
|
|
460
494
|
{render(
|
|
461
495
|
file,
|
|
462
496
|
startLine != null
|
|
463
|
-
? {
|
|
497
|
+
? {
|
|
498
|
+
startLine,
|
|
499
|
+
...(endLine != null ? { endLine } : {}),
|
|
500
|
+
...(fullFile ? { fullFile: true } : {}),
|
|
501
|
+
}
|
|
464
502
|
: undefined,
|
|
465
503
|
)}
|
|
466
504
|
</>
|
|
@@ -536,7 +574,7 @@ interface InnerProps extends SubsystemComponentGraphProps {
|
|
|
536
574
|
measured: { w: number; h: number } | null;
|
|
537
575
|
}
|
|
538
576
|
|
|
539
|
-
function Inner({ components, trails, graphifyRelations, orderByLine, initialTrailId, onReorderTrails, onSelect, onEdgeSelect, measured: _measured, maxNodeWidth, showEdgeLabels, showSingletonFrames = true, moduleNesting, edgeView, title, hideSidebar, trailStepMode = 'focus', autoPlayTrails = false, trailAutoPlayIntervalMs = TRAIL_PLAY_PAUSE_MS, zoomOnTrailFocus = true, trailFocusDurationMs = 300, graphTitle, showTrailTitle = false, description, canvasOverlay, sidebarExtra, sidebarAfterDescription, diagnostic, issues, showIssues, focusIssueCategory, laneStatus, onSelectIssue, onApplyIssueFix, onHoverIssue, renderFileViewer, renderTrailViewer, onFileSelect, componentVerification, onInspectSymbol, boundaryColors, hideDrawer = false, persistKey, liveEvents, agentsPanel }: InnerProps) {
|
|
577
|
+
function Inner({ components, trails, graphifyRelations, orderByLine, initialTrailId, onTrailSelect, onTrailStepHover, onReorderTrails, onSelect, onEdgeSelect, measured: _measured, maxNodeWidth, showEdgeLabels, showSingletonFrames = true, moduleNesting, edgeView, title, hideSidebar, trailStepMode = 'focus', autoPlayTrails = false, trailAutoPlayIntervalMs = TRAIL_PLAY_PAUSE_MS, zoomOnTrailFocus = true, trailFocusDurationMs = 300, graphTitle, showTrailTitle = false, description, canvasOverlay, sidebarExtra, sidebarAfterDescription, diagnostic, issues, showIssues, focusIssueCategory, laneStatus, onSelectIssue, onApplyIssueFix, onHoverIssue, renderFileViewer, renderTrailViewer, onFileSelect, componentVerification, onInspectSymbol, boundaryColors, codeProcessKeys, hideDrawer = false, persistKey, liveEvents, agentsPanel }: InnerProps) {
|
|
540
578
|
const { theme } = useTheme();
|
|
541
579
|
const { fitView, fitBounds, screenToFlowPosition } = useReactFlow();
|
|
542
580
|
const viewport = useViewport();
|
|
@@ -564,6 +602,7 @@ function Inner({ components, trails, graphifyRelations, orderByLine, initialTrai
|
|
|
564
602
|
const [fileOverlay, setFileOverlay] = useState<{
|
|
565
603
|
file: string;
|
|
566
604
|
startLine?: number;
|
|
605
|
+
endLine?: number;
|
|
567
606
|
} | null>(null);
|
|
568
607
|
// Mirror so step-focus can decide whether to wait for the drawer open
|
|
569
608
|
// transition before fitView (avoids framing against full-height canvas).
|
|
@@ -699,57 +738,14 @@ function Inner({ components, trails, graphifyRelations, orderByLine, initialTrai
|
|
|
699
738
|
const [descToggleHover, setDescToggleHover] = useState(false);
|
|
700
739
|
// `true` only when a description exists AND the user opened it.
|
|
701
740
|
const showDesc = !!description && descriptionVisible;
|
|
702
|
-
// Sidebar width
|
|
703
|
-
//
|
|
704
|
-
//
|
|
705
|
-
|
|
706
|
-
const [sidebarDrag, setSidebarDrag] = useState(false);
|
|
707
|
-
const sidebarDragStartX = useRef(0);
|
|
708
|
-
const sidebarDragStartWidth = useRef(450);
|
|
709
|
-
// Mirror of `sidebarWidth` for the drag-end persist (the mouseup listener
|
|
710
|
-
// closes over the drag-start render).
|
|
711
|
-
const sidebarWidthRef = useRef(sidebarWidth);
|
|
712
|
-
sidebarWidthRef.current = sidebarWidth;
|
|
713
|
-
const sidebarMinWidth = 240;
|
|
741
|
+
// Sidebar width lives in the shared GraphWithSidebar chrome — draggable and
|
|
742
|
+
// persisted under `principal.subsystems.sidebarWidth.<persistKey>`. The graph
|
|
743
|
+
// only bounds how wide the panel may get so the canvas keeps room to breathe;
|
|
744
|
+
// a width saved under the old view-state key still seeds the panel once.
|
|
714
745
|
const sidebarMaxWidth = useMemo(
|
|
715
|
-
() => Math.max(Math.min((_measured?.w ?? 680) * 0.5, 600),
|
|
746
|
+
() => Math.max(Math.min((_measured?.w ?? 680) * 0.5, 600), 240),
|
|
716
747
|
[_measured?.w],
|
|
717
748
|
);
|
|
718
|
-
// Resize drag: capture the drag state so document-level move/up listeners
|
|
719
|
-
// stay attached for the duration of the gesture, then release on mouseup.
|
|
720
|
-
const onSidebarResizeStart = useCallback(
|
|
721
|
-
(e: ReactMouseEvent) => {
|
|
722
|
-
e.preventDefault();
|
|
723
|
-
sidebarDragStartX.current = e.clientX;
|
|
724
|
-
sidebarDragStartWidth.current = sidebarWidth;
|
|
725
|
-
setSidebarDrag(true);
|
|
726
|
-
},
|
|
727
|
-
[sidebarWidth],
|
|
728
|
-
);
|
|
729
|
-
useEffect(() => {
|
|
730
|
-
if (!sidebarDrag) return;
|
|
731
|
-
const onMove = (e: globalThis.MouseEvent) => {
|
|
732
|
-
const delta = e.clientX - sidebarDragStartX.current;
|
|
733
|
-
const next = Math.min(
|
|
734
|
-
Math.max(sidebarDragStartWidth.current + delta, sidebarMinWidth),
|
|
735
|
-
sidebarMaxWidth,
|
|
736
|
-
);
|
|
737
|
-
setSidebarWidth(next);
|
|
738
|
-
};
|
|
739
|
-
const onUp = () => {
|
|
740
|
-
setSidebarDrag(false);
|
|
741
|
-
// Persist only on drag end — not on every mousemove.
|
|
742
|
-
writeViewState(persistKey, { sidebarWidth: sidebarWidthRef.current });
|
|
743
|
-
// Re-fit the canvas so the graph re-centers in the new available space.
|
|
744
|
-
requestAnimationFrame(() => fitView());
|
|
745
|
-
};
|
|
746
|
-
document.addEventListener('mousemove', onMove);
|
|
747
|
-
document.addEventListener('mouseup', onUp);
|
|
748
|
-
return () => {
|
|
749
|
-
document.removeEventListener('mousemove', onMove);
|
|
750
|
-
document.removeEventListener('mouseup', onUp);
|
|
751
|
-
};
|
|
752
|
-
}, [sidebarDrag, sidebarMaxWidth, fitView, persistKey]);
|
|
753
749
|
// Persist the trail working set per model. Each write merges into the
|
|
754
750
|
// stored blob, so the three fields never clobber one another.
|
|
755
751
|
useEffect(() => {
|
|
@@ -944,6 +940,7 @@ function Inner({ components, trails, graphifyRelations, orderByLine, initialTrai
|
|
|
944
940
|
moduleNesting,
|
|
945
941
|
graphifyRelations: graphifyRelationsRef.current,
|
|
946
942
|
orderByLine,
|
|
943
|
+
codeProcessKeys,
|
|
947
944
|
})
|
|
948
945
|
.then(({ nodes, edges: e, absoluteRects }) => {
|
|
949
946
|
if (!alive) return;
|
|
@@ -993,7 +990,7 @@ function Inner({ components, trails, graphifyRelations, orderByLine, initialTrai
|
|
|
993
990
|
const gen = ++pass2GenRef.current;
|
|
994
991
|
void buildSubsystemGraph(
|
|
995
992
|
{ components, trails },
|
|
996
|
-
{ maxNodeWidth, showEdgeLabels, measuredWidths, measuredHeights, showSingletonFrames, moduleNesting, graphifyRelations, orderByLine },
|
|
993
|
+
{ maxNodeWidth, showEdgeLabels, measuredWidths, measuredHeights, showSingletonFrames, moduleNesting, graphifyRelations, orderByLine, codeProcessKeys },
|
|
997
994
|
)
|
|
998
995
|
.then(({ nodes, edges: e, absoluteRects }) => {
|
|
999
996
|
if (gen !== pass2GenRef.current) return;
|
|
@@ -1229,22 +1226,22 @@ function Inner({ components, trails, graphifyRelations, orderByLine, initialTrai
|
|
|
1229
1226
|
// Keyed by the React Flow edge id (from → mechanism → to). An edge can have
|
|
1230
1227
|
// multiple steps with issues; we show the worst status (stale > unconfirmed).
|
|
1231
1228
|
const stepIssueBadgeByEdgeId = useMemo(() => {
|
|
1232
|
-
const map = new Map<string, { kind: '
|
|
1229
|
+
const map = new Map<string, { kind: 'step_span_unconfirmed' | 'step_span_stale'; severity: 'info' | 'error'; count: number }>();
|
|
1233
1230
|
if (!issues?.length || !trails?.length) {
|
|
1234
1231
|
return map;
|
|
1235
1232
|
}
|
|
1236
1233
|
for (const issue of issues) {
|
|
1237
|
-
if (issue.kind !== '
|
|
1234
|
+
if (issue.kind !== 'step_span_unconfirmed' && issue.kind !== 'step_span_stale') continue;
|
|
1238
1235
|
const stepInfo = issueStep(issue);
|
|
1239
1236
|
if (!stepInfo) continue;
|
|
1240
1237
|
const step = stepInfo.trail.steps[stepInfo.stepIndex];
|
|
1241
1238
|
if (!step) continue;
|
|
1242
1239
|
const edgeId = trailStepGraphEdgeId(step);
|
|
1243
1240
|
const prev = map.get(edgeId);
|
|
1244
|
-
//
|
|
1245
|
-
const isWorse = issue.kind === '
|
|
1241
|
+
// step_span_stale is worse than step_span_unconfirmed
|
|
1242
|
+
const isWorse = issue.kind === 'step_span_stale' && prev?.kind !== 'step_span_stale';
|
|
1246
1243
|
map.set(edgeId, {
|
|
1247
|
-
kind: isWorse || !prev ? issue.kind as '
|
|
1244
|
+
kind: isWorse || !prev ? issue.kind as 'step_span_unconfirmed' | 'step_span_stale' : prev.kind,
|
|
1248
1245
|
severity: prev?.severity === 'error' || issue.severity === 'error' ? 'error' : 'info',
|
|
1249
1246
|
count: (prev?.count ?? 0) + 1,
|
|
1250
1247
|
});
|
|
@@ -1788,6 +1785,7 @@ function Inner({ components, trails, graphifyRelations, orderByLine, initialTrai
|
|
|
1788
1785
|
(file: string, opts?: SubsystemOpenFileOptions) => {
|
|
1789
1786
|
setFileOverlay(null);
|
|
1790
1787
|
const startLine = opts?.startLine;
|
|
1788
|
+
const endLine = opts?.endLine;
|
|
1791
1789
|
if (
|
|
1792
1790
|
openFileRef.current?.file === file &&
|
|
1793
1791
|
openFileRef.current.startLine === startLine
|
|
@@ -1795,16 +1793,16 @@ function Inner({ components, trails, graphifyRelations, orderByLine, initialTrai
|
|
|
1795
1793
|
setDrawerTarget(null);
|
|
1796
1794
|
return;
|
|
1797
1795
|
}
|
|
1798
|
-
setDrawerTarget({ kind: 'file', file, startLine });
|
|
1796
|
+
setDrawerTarget({ kind: 'file', file, startLine, endLine });
|
|
1799
1797
|
onFileSelect?.(file);
|
|
1800
1798
|
},
|
|
1801
1799
|
[onFileSelect],
|
|
1802
1800
|
);
|
|
1803
1801
|
|
|
1804
1802
|
// Double-click a component opens its declaration in the file drawer — the
|
|
1805
|
-
// `file` + anchored `
|
|
1806
|
-
//
|
|
1807
|
-
//
|
|
1803
|
+
// `file` + anchored `declarationSpan` range when verify resolved one, else
|
|
1804
|
+
// the file top. Uses the same open path as the declaration panel's file
|
|
1805
|
+
// link, so the drawer and file tree stay in sync.
|
|
1808
1806
|
// Boundary region currently framed by a double-click, so a second
|
|
1809
1807
|
// double-click on the same boundary zooms back out to the whole graph.
|
|
1810
1808
|
const [focusedBoundaryKey, setFocusedBoundaryKey] = useState<string | null>(null);
|
|
@@ -1841,15 +1839,21 @@ function Inner({ components, trails, graphifyRelations, orderByLine, initialTrai
|
|
|
1841
1839
|
if (node.type !== 'subsystem-component') return;
|
|
1842
1840
|
const comp = (node.data as { component?: SubsystemComponent } | undefined)?.component;
|
|
1843
1841
|
if (!comp?.file) return;
|
|
1844
|
-
const startLine = comp.
|
|
1845
|
-
|
|
1842
|
+
const startLine = comp.declarationSpan?.startLine;
|
|
1843
|
+
const endLine = comp.declarationSpan?.endLine;
|
|
1844
|
+
onOpenDeclarationFile(
|
|
1845
|
+
comp.file,
|
|
1846
|
+
startLine != null
|
|
1847
|
+
? { startLine, ...(endLine != null ? { endLine } : {}) }
|
|
1848
|
+
: undefined,
|
|
1849
|
+
);
|
|
1846
1850
|
},
|
|
1847
1851
|
[onOpenDeclarationFile, fitView, focusedBoundaryKey],
|
|
1848
1852
|
);
|
|
1849
1853
|
|
|
1850
1854
|
const onOpenFileFromTrail = useCallback(
|
|
1851
1855
|
(file: string, opts?: SubsystemOpenFileOptions) => {
|
|
1852
|
-
setFileOverlay({ file, startLine: opts?.startLine });
|
|
1856
|
+
setFileOverlay({ file, startLine: opts?.startLine, endLine: opts?.endLine });
|
|
1853
1857
|
onFileSelect?.(file);
|
|
1854
1858
|
},
|
|
1855
1859
|
[onFileSelect],
|
|
@@ -2043,14 +2047,16 @@ function Inner({ components, trails, graphifyRelations, orderByLine, initialTrai
|
|
|
2043
2047
|
setFocusedStepIndex(null);
|
|
2044
2048
|
setFocusedTrailId(null);
|
|
2045
2049
|
setDrawerTarget((prev) => (prev?.kind === 'trail' ? null : prev));
|
|
2050
|
+
onTrailSelect?.(null);
|
|
2046
2051
|
return;
|
|
2047
2052
|
}
|
|
2048
2053
|
setFocusedStepIndex(null);
|
|
2049
2054
|
setFocusedTrailId(tl.id);
|
|
2055
|
+
onTrailSelect?.({ trailId: tl.id });
|
|
2050
2056
|
fitFocusBounds(new Set(tl.steps.map((s) => trailStepGraphEdgeId(s))));
|
|
2051
2057
|
setDrawerTarget((prev) => (prev?.kind === 'trail' ? null : prev));
|
|
2052
2058
|
},
|
|
2053
|
-
[fitFocusBounds, trailStepMode],
|
|
2059
|
+
[fitFocusBounds, trailStepMode, onTrailSelect],
|
|
2054
2060
|
);
|
|
2055
2061
|
|
|
2056
2062
|
const clearTrailFocus = useCallback(() => {
|
|
@@ -2058,7 +2064,8 @@ function Inner({ components, trails, graphifyRelations, orderByLine, initialTrai
|
|
|
2058
2064
|
setFocusedStepIndex(null);
|
|
2059
2065
|
setHoveredTrailStep(null);
|
|
2060
2066
|
setDrawerTarget((prev) => (prev?.kind === 'trail' ? null : prev));
|
|
2061
|
-
|
|
2067
|
+
onTrailSelect?.(null);
|
|
2068
|
+
}, [onTrailSelect]);
|
|
2062
2069
|
|
|
2063
2070
|
// Host deep-link: opening the graph from a trail row selects that
|
|
2064
2071
|
// flow — expand its steps, focus its edges, and switch the sidebar to
|
|
@@ -2108,6 +2115,7 @@ function Inner({ components, trails, graphifyRelations, orderByLine, initialTrai
|
|
|
2108
2115
|
setFocusedTrailId(null);
|
|
2109
2116
|
setHoveredTrailStep({ trailId: tl.id, stepIndex });
|
|
2110
2117
|
setDrawerTarget((prev) => (prev?.kind === 'trail' ? null : prev));
|
|
2118
|
+
onTrailSelect?.(null);
|
|
2111
2119
|
return;
|
|
2112
2120
|
}
|
|
2113
2121
|
// Toggle: clicking the selected step unselects it (back to whole flow).
|
|
@@ -2127,6 +2135,7 @@ function Inner({ components, trails, graphifyRelations, orderByLine, initialTrai
|
|
|
2127
2135
|
setFocusedStepIndex(stepIndex);
|
|
2128
2136
|
setFocusedTrailId(tl.id);
|
|
2129
2137
|
setHoveredTrailStep(null);
|
|
2138
|
+
onTrailSelect?.({ trailId: tl.id, stepIndices: [stepIndex] });
|
|
2130
2139
|
// Open the drawer before fitting. If it was closed, wait for its height
|
|
2131
2140
|
// transition so fitView uses the reduced canvas — not full height.
|
|
2132
2141
|
const drawerWasOpen = drawerOpenRef.current;
|
|
@@ -2140,7 +2149,8 @@ function Inner({ components, trails, graphifyRelations, orderByLine, initialTrai
|
|
|
2140
2149
|
setDrawerTarget({
|
|
2141
2150
|
kind: 'file',
|
|
2142
2151
|
file: step.file,
|
|
2143
|
-
startLine: step.
|
|
2152
|
+
startLine: step.span?.startLine,
|
|
2153
|
+
endLine: step.span?.endLine,
|
|
2144
2154
|
});
|
|
2145
2155
|
}
|
|
2146
2156
|
const edgeIds = new Set([trailStepGraphEdgeId(step)]);
|
|
@@ -2163,6 +2173,7 @@ function Inner({ components, trails, graphifyRelations, orderByLine, initialTrai
|
|
|
2163
2173
|
focusedTrailId,
|
|
2164
2174
|
renderTrailViewer,
|
|
2165
2175
|
trailStepMode,
|
|
2176
|
+
onTrailSelect,
|
|
2166
2177
|
],
|
|
2167
2178
|
);
|
|
2168
2179
|
|
|
@@ -2249,7 +2260,7 @@ function Inner({ components, trails, graphifyRelations, orderByLine, initialTrai
|
|
|
2249
2260
|
if (renderTrailViewer) {
|
|
2250
2261
|
setDrawerTarget({ kind: 'trail', trailId: tl.id, stepIndex: stepIdx });
|
|
2251
2262
|
} else {
|
|
2252
|
-
setDrawerTarget({ kind: 'file', file: step.file, startLine: step.
|
|
2263
|
+
setDrawerTarget({ kind: 'file', file: step.file, startLine: step.span?.startLine, endLine: step.span?.endLine });
|
|
2253
2264
|
}
|
|
2254
2265
|
}
|
|
2255
2266
|
}
|
|
@@ -2541,10 +2552,13 @@ function Inner({ components, trails, graphifyRelations, orderByLine, initialTrai
|
|
|
2541
2552
|
(alias: string) => {
|
|
2542
2553
|
const comp = components.find((c) => c.alias === alias);
|
|
2543
2554
|
if (!comp?.file) return;
|
|
2544
|
-
const startLine = comp.
|
|
2555
|
+
const startLine = comp.declarationSpan?.startLine;
|
|
2556
|
+
const endLine = comp.declarationSpan?.endLine;
|
|
2545
2557
|
onOpenDeclarationFile(
|
|
2546
2558
|
comp.file,
|
|
2547
|
-
startLine != null
|
|
2559
|
+
startLine != null
|
|
2560
|
+
? { startLine, ...(endLine != null ? { endLine } : {}) }
|
|
2561
|
+
: undefined,
|
|
2548
2562
|
);
|
|
2549
2563
|
},
|
|
2550
2564
|
[components, onOpenDeclarationFile],
|
|
@@ -2616,323 +2630,221 @@ function Inner({ components, trails, graphifyRelations, orderByLine, initialTrai
|
|
|
2616
2630
|
[],
|
|
2617
2631
|
);
|
|
2618
2632
|
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2633
|
+
// Sidebar chrome — title/description header, Files/Trails (or Issues/Agents)
|
|
2634
|
+
// tab strip, and the lower panels. Everything renders through the shared
|
|
2635
|
+
// GraphWithSidebar; the graph keeps owning every bit of *state* (focused
|
|
2636
|
+
// trail, active tab, description visibility) because the canvas dims from
|
|
2637
|
+
// the same facts the sidebar renders.
|
|
2638
|
+
const sidebarVisible =
|
|
2639
|
+
!hideSidebar &&
|
|
2640
|
+
(title || description || diagnostic || issuesActive || sidebarExtra || sidebarAfterDescription ||
|
|
2641
|
+
treeFilePaths.length > 0 || hasTrails);
|
|
2642
|
+
const sidebarBodyVisible = treeFilePaths.length > 0 || hasTrails || issuesActive;
|
|
2643
|
+
const sidebarTabs: GraphSidebarTab[] | undefined = issuesActive
|
|
2644
|
+
? agentsPanel
|
|
2645
|
+
? [
|
|
2646
|
+
{ id: 'issues', label: 'Issues' },
|
|
2647
|
+
{ id: 'agents', label: 'Agents' },
|
|
2648
|
+
]
|
|
2649
|
+
: undefined
|
|
2650
|
+
: hasTrails
|
|
2651
|
+
? [
|
|
2652
|
+
{ id: 'trails', label: 'Trails' },
|
|
2653
|
+
{ id: 'files', label: 'Files' },
|
|
2654
|
+
]
|
|
2655
|
+
: undefined;
|
|
2656
|
+
const activeSidebarTab = issuesActive ? diagnosticsTab : sidebarView;
|
|
2657
|
+
// Header block: the extras, then the title/actions row (diagnostic chip +
|
|
2658
|
+
// description toggle), then the description that toggles out from under it.
|
|
2659
|
+
const sidebarHeader = (
|
|
2660
|
+
<>
|
|
2661
|
+
{sidebarExtra}
|
|
2662
|
+
{(title || description || diagnostic) && (
|
|
2663
|
+
<div style={{ display: 'flex', alignItems: 'flex-start', gap: 8 }}>
|
|
2664
|
+
{title && (
|
|
2665
|
+
<h2
|
|
2666
|
+
style={{
|
|
2667
|
+
margin: 0,
|
|
2668
|
+
flex: 1,
|
|
2669
|
+
minWidth: 0,
|
|
2670
|
+
fontSize: theme.fontSizes[2],
|
|
2671
|
+
fontWeight: 600,
|
|
2672
|
+
color: theme.colors.text,
|
|
2673
|
+
fontFamily: theme.fonts.monospace,
|
|
2674
|
+
}}
|
|
2675
|
+
>
|
|
2676
|
+
{title}
|
|
2677
|
+
</h2>
|
|
2678
|
+
)}
|
|
2636
2679
|
<div
|
|
2637
2680
|
style={{
|
|
2638
|
-
flex: showDesc ? 1 : '0 0 auto',
|
|
2639
|
-
minHeight: 0,
|
|
2640
|
-
overflowY: 'auto',
|
|
2641
|
-
padding: '16px',
|
|
2642
2681
|
display: 'flex',
|
|
2643
|
-
|
|
2644
|
-
gap:
|
|
2682
|
+
alignItems: 'center',
|
|
2683
|
+
gap: 4,
|
|
2684
|
+
flexShrink: 0,
|
|
2685
|
+
// Title's flex:1 already pushes this cluster right; without a
|
|
2686
|
+
// title, keep the actions flush right instead of flush-left.
|
|
2687
|
+
marginLeft: title ? undefined : 'auto',
|
|
2645
2688
|
}}
|
|
2646
2689
|
>
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
</h2>
|
|
2664
|
-
)}
|
|
2665
|
-
<div
|
|
2690
|
+
{diagnostic && (
|
|
2691
|
+
<SubsystemDiagnosticToggle
|
|
2692
|
+
{...diagnostic}
|
|
2693
|
+
active={issuesActive}
|
|
2694
|
+
onToggle={toggleIssues}
|
|
2695
|
+
/>
|
|
2696
|
+
)}
|
|
2697
|
+
{description && (
|
|
2698
|
+
<button
|
|
2699
|
+
type="button"
|
|
2700
|
+
aria-expanded={descriptionVisible}
|
|
2701
|
+
aria-label={descriptionVisible ? 'Hide description' : 'Show description'}
|
|
2702
|
+
title={descriptionVisible ? 'Hide description' : 'Show description'}
|
|
2703
|
+
onMouseEnter={() => setDescToggleHover(true)}
|
|
2704
|
+
onMouseLeave={() => setDescToggleHover(false)}
|
|
2705
|
+
onClick={() => setDescriptionVisible((v) => !v)}
|
|
2666
2706
|
style={{
|
|
2667
|
-
display: 'flex',
|
|
2707
|
+
display: 'inline-flex',
|
|
2668
2708
|
alignItems: 'center',
|
|
2669
|
-
|
|
2709
|
+
justifyContent: 'center',
|
|
2670
2710
|
flexShrink: 0,
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2711
|
+
width: 22,
|
|
2712
|
+
height: 22,
|
|
2713
|
+
padding: 0,
|
|
2714
|
+
border: 'none',
|
|
2715
|
+
borderRadius: 4,
|
|
2716
|
+
background: descriptionVisible || descToggleHover ? theme.colors.border : 'transparent',
|
|
2717
|
+
color: descriptionVisible || descToggleHover ? theme.colors.text : (theme.colors.textMuted ?? theme.colors.textSecondary),
|
|
2718
|
+
cursor: 'pointer',
|
|
2719
|
+
transition: 'background 120ms ease, color 120ms ease',
|
|
2674
2720
|
}}
|
|
2675
2721
|
>
|
|
2676
|
-
{
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
active={issuesActive}
|
|
2680
|
-
onToggle={toggleIssues}
|
|
2681
|
-
/>
|
|
2682
|
-
)}
|
|
2683
|
-
{description && (
|
|
2684
|
-
<button
|
|
2685
|
-
type="button"
|
|
2686
|
-
aria-expanded={descriptionVisible}
|
|
2687
|
-
aria-label={descriptionVisible ? 'Hide description' : 'Show description'}
|
|
2688
|
-
title={descriptionVisible ? 'Hide description' : 'Show description'}
|
|
2689
|
-
onMouseEnter={() => setDescToggleHover(true)}
|
|
2690
|
-
onMouseLeave={() => setDescToggleHover(false)}
|
|
2691
|
-
onClick={() => setDescriptionVisible((v) => !v)}
|
|
2692
|
-
style={{
|
|
2693
|
-
display: 'inline-flex',
|
|
2694
|
-
alignItems: 'center',
|
|
2695
|
-
justifyContent: 'center',
|
|
2696
|
-
flexShrink: 0,
|
|
2697
|
-
width: 22,
|
|
2698
|
-
height: 22,
|
|
2699
|
-
padding: 0,
|
|
2700
|
-
border: 'none',
|
|
2701
|
-
borderRadius: 4,
|
|
2702
|
-
background: descriptionVisible || descToggleHover ? theme.colors.border : 'transparent',
|
|
2703
|
-
color: descriptionVisible || descToggleHover ? theme.colors.text : (theme.colors.textMuted ?? theme.colors.textSecondary),
|
|
2704
|
-
cursor: 'pointer',
|
|
2705
|
-
transition: 'background 120ms ease, color 120ms ease',
|
|
2706
|
-
}}
|
|
2707
|
-
>
|
|
2708
|
-
<FileText size={14} />
|
|
2709
|
-
</button>
|
|
2710
|
-
)}
|
|
2711
|
-
</div>
|
|
2712
|
-
</div>
|
|
2713
|
-
)}
|
|
2714
|
-
{description && descriptionVisible && (
|
|
2715
|
-
<div style={{ fontSize: theme.fontSizes[0], lineHeight: 1.5 }}>
|
|
2716
|
-
<IndustryMarkdownSlide
|
|
2717
|
-
content={description}
|
|
2718
|
-
slideIdPrefix="subsystem-desc"
|
|
2719
|
-
slideIndex={0}
|
|
2720
|
-
isVisible={true}
|
|
2721
|
-
theme={theme}
|
|
2722
|
-
disableScroll={true}
|
|
2723
|
-
disableBasePadding
|
|
2724
|
-
enableKeyboardScrolling={false}
|
|
2725
|
-
autoFocusOnVisible={false}
|
|
2726
|
-
/>
|
|
2727
|
-
</div>
|
|
2728
|
-
)}
|
|
2729
|
-
{sidebarAfterDescription}
|
|
2722
|
+
<FileText size={14} />
|
|
2723
|
+
</button>
|
|
2724
|
+
)}
|
|
2730
2725
|
</div>
|
|
2731
|
-
{(treeFilePaths.length > 0 || hasTrails || issuesActive) && (
|
|
2732
|
-
<div
|
|
2733
|
-
style={{
|
|
2734
|
-
...(showDesc ? { height: '50%' as const } : { flex: 1, minHeight: 0 }),
|
|
2735
|
-
minHeight: 160,
|
|
2736
|
-
flexShrink: 0,
|
|
2737
|
-
borderTop: `1px solid ${theme.colors.border}`,
|
|
2738
|
-
display: 'flex',
|
|
2739
|
-
flexDirection: 'column',
|
|
2740
|
-
overflow: 'hidden',
|
|
2741
|
-
}}
|
|
2742
|
-
>
|
|
2743
|
-
{issuesActive ? (
|
|
2744
|
-
agentsPanel ? (
|
|
2745
|
-
<div style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column' }}>
|
|
2746
|
-
<div
|
|
2747
|
-
role="tablist"
|
|
2748
|
-
aria-label="Diagnostics view"
|
|
2749
|
-
style={{
|
|
2750
|
-
display: 'flex',
|
|
2751
|
-
width: '100%',
|
|
2752
|
-
flexShrink: 0,
|
|
2753
|
-
borderBottom: `1px solid ${theme.colors.border}`,
|
|
2754
|
-
background: theme.colors.backgroundSecondary ?? theme.colors.background,
|
|
2755
|
-
}}
|
|
2756
|
-
>
|
|
2757
|
-
{(['issues', 'agents'] as const).map((tab) => (
|
|
2758
|
-
<button
|
|
2759
|
-
key={tab}
|
|
2760
|
-
type="button"
|
|
2761
|
-
role="tab"
|
|
2762
|
-
aria-selected={diagnosticsTab === tab}
|
|
2763
|
-
onClick={() => setDiagnosticsTab(tab)}
|
|
2764
|
-
style={{
|
|
2765
|
-
flex: 1,
|
|
2766
|
-
minWidth: 0,
|
|
2767
|
-
padding: '8px 8px',
|
|
2768
|
-
border: 'none',
|
|
2769
|
-
borderRadius: 0,
|
|
2770
|
-
background: diagnosticsTab === tab ? theme.colors.background : 'transparent',
|
|
2771
|
-
color:
|
|
2772
|
-
diagnosticsTab === tab ? theme.colors.text : theme.colors.textSecondary,
|
|
2773
|
-
fontSize: theme.fontSizes[1],
|
|
2774
|
-
fontFamily: theme.fonts.monospace,
|
|
2775
|
-
textTransform: 'capitalize',
|
|
2776
|
-
cursor: 'pointer',
|
|
2777
|
-
}}
|
|
2778
|
-
>
|
|
2779
|
-
{tab === 'issues' ? 'Issues' : 'Agents'}
|
|
2780
|
-
</button>
|
|
2781
|
-
))}
|
|
2782
|
-
</div>
|
|
2783
|
-
{diagnosticsTab === 'agents' ? (
|
|
2784
|
-
<SubsystemAgentsPanel {...agentsPanel} />
|
|
2785
|
-
) : (
|
|
2786
|
-
<SubsystemIssueList
|
|
2787
|
-
issues={issues ?? []}
|
|
2788
|
-
focusCategory={focusIssueCategory}
|
|
2789
|
-
laneStatus={laneStatus}
|
|
2790
|
-
onSelectIssue={focusIssueTarget}
|
|
2791
|
-
onDeselectIssue={unfocusIssueTarget}
|
|
2792
|
-
onApplyFix={onApplyIssueFix}
|
|
2793
|
-
onHoverIssue={onHoverIssue}
|
|
2794
|
-
onExpandedCategoriesChange={handleExpandedCategoriesChange}
|
|
2795
|
-
/>
|
|
2796
|
-
)}
|
|
2797
|
-
</div>
|
|
2798
|
-
) : (
|
|
2799
|
-
<SubsystemIssueList
|
|
2800
|
-
issues={issues ?? []}
|
|
2801
|
-
focusCategory={focusIssueCategory}
|
|
2802
|
-
laneStatus={laneStatus}
|
|
2803
|
-
onSelectIssue={focusIssueTarget}
|
|
2804
|
-
onDeselectIssue={unfocusIssueTarget}
|
|
2805
|
-
onApplyFix={onApplyIssueFix}
|
|
2806
|
-
onHoverIssue={onHoverIssue}
|
|
2807
|
-
onExpandedCategoriesChange={handleExpandedCategoriesChange}
|
|
2808
|
-
/>
|
|
2809
|
-
)
|
|
2810
|
-
) : (
|
|
2811
|
-
<>
|
|
2812
|
-
{hasTrails && (
|
|
2813
|
-
<div
|
|
2814
|
-
role="tablist"
|
|
2815
|
-
aria-label="Sidebar view"
|
|
2816
|
-
style={{
|
|
2817
|
-
display: 'flex',
|
|
2818
|
-
width: '100%',
|
|
2819
|
-
flexShrink: 0,
|
|
2820
|
-
borderBottom: `1px solid ${theme.colors.border}`,
|
|
2821
|
-
background: theme.colors.backgroundSecondary ?? theme.colors.background,
|
|
2822
|
-
}}
|
|
2823
|
-
>
|
|
2824
|
-
{(['trails', 'files'] as const).map((view) => (
|
|
2825
|
-
<button
|
|
2826
|
-
key={view}
|
|
2827
|
-
type="button"
|
|
2828
|
-
role="tab"
|
|
2829
|
-
aria-selected={sidebarView === view}
|
|
2830
|
-
onClick={() => changeSidebarView(view)}
|
|
2831
|
-
style={{
|
|
2832
|
-
flex: 1,
|
|
2833
|
-
minWidth: 0,
|
|
2834
|
-
padding: '8px 8px',
|
|
2835
|
-
border: 'none',
|
|
2836
|
-
borderRadius: 0,
|
|
2837
|
-
background: sidebarView === view ? theme.colors.background : 'transparent',
|
|
2838
|
-
color:
|
|
2839
|
-
sidebarView === view
|
|
2840
|
-
? theme.colors.text
|
|
2841
|
-
: theme.colors.textSecondary,
|
|
2842
|
-
fontSize: theme.fontSizes[1],
|
|
2843
|
-
fontFamily: theme.fonts.monospace,
|
|
2844
|
-
textTransform: 'capitalize',
|
|
2845
|
-
cursor: 'pointer',
|
|
2846
|
-
}}
|
|
2847
|
-
>
|
|
2848
|
-
{view === 'trails' ? 'Trails' : 'Files'}
|
|
2849
|
-
</button>
|
|
2850
|
-
))}
|
|
2851
|
-
</div>
|
|
2852
|
-
)}
|
|
2853
|
-
{sidebarView === 'trails' && trails && trails.length > 0 ? (
|
|
2854
|
-
<TrailsPanel
|
|
2855
|
-
trails={trails}
|
|
2856
|
-
expandedTrails={expandedTrails}
|
|
2857
|
-
focusedTrailId={focusedTrailId}
|
|
2858
|
-
focusedStepIndex={focusedStepIndex}
|
|
2859
|
-
hoveredTrailStep={hoveredTrailStep}
|
|
2860
|
-
onToggleCollapsed={toggleTrailCollapsed}
|
|
2861
|
-
onFocusFlow={focusTrailEdges}
|
|
2862
|
-
onClearFocus={clearTrailFocus}
|
|
2863
|
-
onFocusStep={focusTrailStep}
|
|
2864
|
-
onHoverStep={(tl, i) =>
|
|
2865
|
-
setHoveredTrailStep({ trailId: tl.id, stepIndex: i })
|
|
2866
|
-
}
|
|
2867
|
-
onHoverFlow={(tl) =>
|
|
2868
|
-
setHoveredTrailStep({ trailId: tl.id, stepIndex: null })
|
|
2869
|
-
}
|
|
2870
|
-
onLeaveStep={() => setHoveredTrailStep(null)}
|
|
2871
|
-
onReorder={onReorderTrails}
|
|
2872
|
-
proposedAliases={proposedAliases}
|
|
2873
|
-
/>
|
|
2874
|
-
) : treeFilePaths.length > 0 ? (
|
|
2875
|
-
<>
|
|
2876
|
-
{repoGroups.groups.map((group, i) => {
|
|
2877
|
-
const groupKey = group.repoKey ?? '__no-repo__';
|
|
2878
|
-
const collapsed = collapsedRepos.has(groupKey);
|
|
2879
|
-
return (
|
|
2880
|
-
<div
|
|
2881
|
-
key={groupKey}
|
|
2882
|
-
style={{
|
|
2883
|
-
flex: collapsed ? '0 0 auto' : 1,
|
|
2884
|
-
minHeight: collapsed ? 0 : undefined,
|
|
2885
|
-
display: 'flex',
|
|
2886
|
-
flexDirection: 'column',
|
|
2887
|
-
borderTop: i > 0 ? `1px solid ${theme.colors.border}` : undefined,
|
|
2888
|
-
}}
|
|
2889
|
-
>
|
|
2890
|
-
{repoGroups.multiRepo && (
|
|
2891
|
-
<RepoGroupHeader
|
|
2892
|
-
group={group}
|
|
2893
|
-
collapsed={collapsed}
|
|
2894
|
-
onToggle={() => toggleRepoCollapsed(groupKey)}
|
|
2895
|
-
/>
|
|
2896
|
-
)}
|
|
2897
|
-
{!collapsed && (
|
|
2898
|
-
<SubsystemFileTree
|
|
2899
|
-
files={group.entries.map((e) => e.displayPath)}
|
|
2900
|
-
selectedFile={selected?.file ?? openFile}
|
|
2901
|
-
hoveredFile={hoveredFile}
|
|
2902
|
-
onSelectFile={onTreeSelectFile}
|
|
2903
|
-
hoveredFolder={hoveredBoundaryPath}
|
|
2904
|
-
headerless={repoGroups.multiRepo}
|
|
2905
|
-
/>
|
|
2906
|
-
)}
|
|
2907
|
-
</div>
|
|
2908
|
-
);
|
|
2909
|
-
})}
|
|
2910
|
-
</>
|
|
2911
|
-
) : null}
|
|
2912
|
-
</>
|
|
2913
|
-
)}
|
|
2914
|
-
</div>
|
|
2915
|
-
)}
|
|
2916
2726
|
</div>
|
|
2917
2727
|
)}
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
}}
|
|
2933
|
-
/>
|
|
2728
|
+
{description && descriptionVisible && (
|
|
2729
|
+
<div style={{ fontSize: theme.fontSizes[0], lineHeight: 1.5 }}>
|
|
2730
|
+
<IndustryMarkdownSlide
|
|
2731
|
+
content={description}
|
|
2732
|
+
slideIdPrefix="subsystem-desc"
|
|
2733
|
+
slideIndex={0}
|
|
2734
|
+
isVisible={true}
|
|
2735
|
+
theme={theme}
|
|
2736
|
+
disableScroll={true}
|
|
2737
|
+
disableBasePadding
|
|
2738
|
+
enableKeyboardScrolling={false}
|
|
2739
|
+
autoFocusOnVisible={false}
|
|
2740
|
+
/>
|
|
2741
|
+
</div>
|
|
2934
2742
|
)}
|
|
2935
|
-
|
|
2743
|
+
{sidebarAfterDescription}
|
|
2744
|
+
</>
|
|
2745
|
+
);
|
|
2746
|
+
// Lower panel: the Issues/Agents diagnostics stack, or the Files/Trails
|
|
2747
|
+
// stack with the trail flow list / repo file tree.
|
|
2748
|
+
const sidebarBody = issuesActive ? (
|
|
2749
|
+
diagnosticsTab === 'agents' && agentsPanel ? (
|
|
2750
|
+
<SubsystemAgentsPanel {...agentsPanel} />
|
|
2751
|
+
) : (
|
|
2752
|
+
<SubsystemIssueList
|
|
2753
|
+
issues={issues ?? []}
|
|
2754
|
+
focusCategory={focusIssueCategory}
|
|
2755
|
+
laneStatus={laneStatus}
|
|
2756
|
+
onSelectIssue={focusIssueTarget}
|
|
2757
|
+
onDeselectIssue={unfocusIssueTarget}
|
|
2758
|
+
onApplyFix={onApplyIssueFix}
|
|
2759
|
+
onHoverIssue={onHoverIssue}
|
|
2760
|
+
onExpandedCategoriesChange={handleExpandedCategoriesChange}
|
|
2761
|
+
/>
|
|
2762
|
+
)
|
|
2763
|
+
) : sidebarView === 'trails' && trails && trails.length > 0 ? (
|
|
2764
|
+
<TrailsPanel
|
|
2765
|
+
trails={trails}
|
|
2766
|
+
expandedTrails={expandedTrails}
|
|
2767
|
+
focusedTrailId={focusedTrailId}
|
|
2768
|
+
focusedStepIndex={focusedStepIndex}
|
|
2769
|
+
hoveredTrailStep={hoveredTrailStep}
|
|
2770
|
+
onToggleCollapsed={toggleTrailCollapsed}
|
|
2771
|
+
onFocusFlow={focusTrailEdges}
|
|
2772
|
+
onClearFocus={clearTrailFocus}
|
|
2773
|
+
onFocusStep={focusTrailStep}
|
|
2774
|
+
onHoverStep={(tl, i) => {
|
|
2775
|
+
setHoveredTrailStep({ trailId: tl.id, stepIndex: i });
|
|
2776
|
+
onTrailStepHover?.({ trailId: tl.id, stepIndex: i });
|
|
2777
|
+
}}
|
|
2778
|
+
onHoverFlow={(tl) => {
|
|
2779
|
+
setHoveredTrailStep({ trailId: tl.id, stepIndex: null });
|
|
2780
|
+
onTrailStepHover?.(null);
|
|
2781
|
+
}}
|
|
2782
|
+
onLeaveStep={() => {
|
|
2783
|
+
setHoveredTrailStep(null);
|
|
2784
|
+
onTrailStepHover?.(null);
|
|
2785
|
+
}}
|
|
2786
|
+
onReorder={onReorderTrails}
|
|
2787
|
+
proposedAliases={proposedAliases}
|
|
2788
|
+
/>
|
|
2789
|
+
) : treeFilePaths.length > 0 ? (
|
|
2790
|
+
<>
|
|
2791
|
+
{repoGroups.groups.map((group, i) => {
|
|
2792
|
+
const groupKey = group.repoKey ?? '__no-repo__';
|
|
2793
|
+
const collapsed = collapsedRepos.has(groupKey);
|
|
2794
|
+
return (
|
|
2795
|
+
<div
|
|
2796
|
+
key={groupKey}
|
|
2797
|
+
style={{
|
|
2798
|
+
flex: collapsed ? '0 0 auto' : 1,
|
|
2799
|
+
minHeight: collapsed ? 0 : undefined,
|
|
2800
|
+
display: 'flex',
|
|
2801
|
+
flexDirection: 'column',
|
|
2802
|
+
borderTop: i > 0 ? `1px solid ${theme.colors.border}` : undefined,
|
|
2803
|
+
}}
|
|
2804
|
+
>
|
|
2805
|
+
{repoGroups.multiRepo && (
|
|
2806
|
+
<RepoGroupHeader
|
|
2807
|
+
group={group}
|
|
2808
|
+
collapsed={collapsed}
|
|
2809
|
+
onToggle={() => toggleRepoCollapsed(groupKey)}
|
|
2810
|
+
/>
|
|
2811
|
+
)}
|
|
2812
|
+
{!collapsed && (
|
|
2813
|
+
<SubsystemFileTree
|
|
2814
|
+
files={group.entries.map((e) => e.displayPath)}
|
|
2815
|
+
selectedFile={selected?.file ?? openFile}
|
|
2816
|
+
hoveredFile={hoveredFile}
|
|
2817
|
+
onSelectFile={onTreeSelectFile}
|
|
2818
|
+
hoveredFolder={hoveredBoundaryPath}
|
|
2819
|
+
headerless={repoGroups.multiRepo}
|
|
2820
|
+
/>
|
|
2821
|
+
)}
|
|
2822
|
+
</div>
|
|
2823
|
+
);
|
|
2824
|
+
})}
|
|
2825
|
+
</>
|
|
2826
|
+
) : null;
|
|
2827
|
+
return (
|
|
2828
|
+
<GraphWithSidebar
|
|
2829
|
+
hidden={!sidebarVisible}
|
|
2830
|
+
bodyHidden={!sidebarBodyVisible}
|
|
2831
|
+
stretchHeader={showDesc}
|
|
2832
|
+
header={sidebarHeader}
|
|
2833
|
+
tabs={sidebarTabs}
|
|
2834
|
+
activeTab={activeSidebarTab}
|
|
2835
|
+
onTabChange={(id) =>
|
|
2836
|
+
issuesActive
|
|
2837
|
+
? setDiagnosticsTab(id as 'issues' | 'agents')
|
|
2838
|
+
: changeSidebarView(id as 'files' | 'trails')
|
|
2839
|
+
}
|
|
2840
|
+
sidebarBody={sidebarBody}
|
|
2841
|
+
tabListLabel={issuesActive ? 'Diagnostics view' : 'Sidebar view'}
|
|
2842
|
+
defaultWidth={persisted.sidebarWidth ?? 450}
|
|
2843
|
+
minWidth={240}
|
|
2844
|
+
maxWidth={sidebarMaxWidth}
|
|
2845
|
+
persistKey={persistKey}
|
|
2846
|
+
onResizeEnd={() => requestAnimationFrame(() => fitView())}
|
|
2847
|
+
>
|
|
2936
2848
|
{/* Graph area */}
|
|
2937
2849
|
<div style={{ flex: 1, position: 'relative', display: 'flex', flexDirection: 'column', minHeight: 0 }}>
|
|
2938
2850
|
{/* Canvas box — the edge-label overlay and legend anchor here, so
|
|
@@ -3082,7 +2994,7 @@ function Inner({ components, trails, graphifyRelations, orderByLine, initialTrai
|
|
|
3082
2994
|
return (
|
|
3083
2995
|
<span
|
|
3084
2996
|
aria-hidden
|
|
3085
|
-
title={lbl.stepIssue.kind === '
|
|
2997
|
+
title={lbl.stepIssue.kind === 'step_span_stale'
|
|
3086
2998
|
? 'Call site changed since verification'
|
|
3087
2999
|
: 'Call site not yet verified'}
|
|
3088
3000
|
style={{
|
|
@@ -3324,7 +3236,7 @@ function Inner({ components, trails, graphifyRelations, orderByLine, initialTrai
|
|
|
3324
3236
|
drawerTarget.startLine != null &&
|
|
3325
3237
|
!!selected.file &&
|
|
3326
3238
|
selected.file === drawerTarget.file &&
|
|
3327
|
-
selected.
|
|
3239
|
+
selected.declarationSpan?.startLine === drawerTarget.startLine
|
|
3328
3240
|
}
|
|
3329
3241
|
/>
|
|
3330
3242
|
</div>
|
|
@@ -3356,6 +3268,7 @@ function Inner({ components, trails, graphifyRelations, orderByLine, initialTrai
|
|
|
3356
3268
|
render={renderDrawerContent}
|
|
3357
3269
|
file={drawerTarget.file}
|
|
3358
3270
|
startLine={drawerTarget.startLine}
|
|
3271
|
+
endLine={drawerTarget.endLine}
|
|
3359
3272
|
/>
|
|
3360
3273
|
) : null}
|
|
3361
3274
|
</FileDrawer>
|
|
@@ -3403,6 +3316,7 @@ function Inner({ components, trails, graphifyRelations, orderByLine, initialTrai
|
|
|
3403
3316
|
render={renderDrawerContent}
|
|
3404
3317
|
file={fileOverlay.file}
|
|
3405
3318
|
startLine={fileOverlay.startLine}
|
|
3319
|
+
endLine={fileOverlay.endLine}
|
|
3406
3320
|
fullFile
|
|
3407
3321
|
/>
|
|
3408
3322
|
</div>
|
|
@@ -3413,7 +3327,7 @@ function Inner({ components, trails, graphifyRelations, orderByLine, initialTrai
|
|
|
3413
3327
|
{canvasOverlay}
|
|
3414
3328
|
{liveEvents ? <MaintainLivePanel {...liveEvents} /> : null}
|
|
3415
3329
|
</div>
|
|
3416
|
-
</
|
|
3330
|
+
</GraphWithSidebar>
|
|
3417
3331
|
);
|
|
3418
3332
|
}
|
|
3419
3333
|
|