@principal-ai/subsystems-react 0.56.3 → 0.56.5
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 +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/subsystem/DecorationPanel.d.ts +37 -0
- package/dist/subsystem/DecorationPanel.d.ts.map +1 -0
- package/dist/subsystem/DecorationPanel.js +147 -0
- package/dist/subsystem/DecorationPanel.js.map +1 -0
- package/dist/subsystem/DiagnosticToggle.d.ts +7 -1
- package/dist/subsystem/DiagnosticToggle.d.ts.map +1 -1
- package/dist/subsystem/DiagnosticToggle.js +8 -7
- package/dist/subsystem/DiagnosticToggle.js.map +1 -1
- package/dist/subsystem/GraphSidebar.d.ts +4 -2
- package/dist/subsystem/GraphSidebar.d.ts.map +1 -1
- package/dist/subsystem/GraphSidebar.js +3 -1
- package/dist/subsystem/GraphSidebar.js.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.d.ts +10 -13
- package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.js +112 -272
- package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
- package/dist/subsystem/TrailStepsNav.d.ts.map +1 -1
- package/dist/subsystem/TrailStepsNav.js +6 -4
- package/dist/subsystem/TrailStepsNav.js.map +1 -1
- package/dist/subsystem/TrailsPanel.d.ts.map +1 -1
- package/dist/subsystem/TrailsPanel.js +14 -10
- package/dist/subsystem/TrailsPanel.js.map +1 -1
- package/dist/subsystem/decoration.d.ts +112 -0
- package/dist/subsystem/decoration.d.ts.map +1 -0
- package/dist/subsystem/decoration.js +54 -0
- package/dist/subsystem/decoration.js.map +1 -0
- package/package.json +3 -3
- package/src/index.ts +37 -0
- package/src/stories/Canvases/C4/C4ToComponentDrill.stories.tsx +38 -71
- package/src/stories/Canvases/Subsystem/GraphTitle.stories.tsx +1 -2
- package/src/stories/Canvases/Subsystem/StepEmphasis.stories.tsx +528 -0
- package/src/stories/Components/DecorationPanel.stories.tsx +160 -0
- package/src/subsystem/DecorationPanel.test.tsx +128 -0
- package/src/subsystem/DecorationPanel.tsx +334 -0
- package/src/subsystem/DiagnosticToggle.tsx +20 -5
- package/src/subsystem/GraphSidebar.test.tsx +18 -0
- package/src/subsystem/GraphSidebar.tsx +6 -1
- package/src/subsystem/SubsystemComponentGraph.sidebar.test.tsx +218 -0
- package/src/subsystem/SubsystemComponentGraph.tsx +184 -414
- package/src/subsystem/TrailStepsNav.test.tsx +32 -0
- package/src/subsystem/TrailStepsNav.tsx +6 -4
- package/src/subsystem/TrailsPanel.test.tsx +20 -0
- package/src/subsystem/TrailsPanel.tsx +11 -1
- package/src/subsystem/decoration.test.ts +72 -0
- package/src/subsystem/decoration.ts +151 -0
- package/src/subsystem/stepEmphasis.test.ts +110 -0
|
@@ -18,7 +18,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
18
18
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
19
19
|
import { ReactFlow, ReactFlowProvider, useReactFlow, useViewport, applyNodeChanges, } from '@xyflow/react';
|
|
20
20
|
import { useTheme } from '@principal-ade/industry-theme';
|
|
21
|
-
import {
|
|
21
|
+
import { ArrowDown, ArrowRight, Files, Route, Unlink, X } from 'lucide-react';
|
|
22
22
|
import { IndustryMarkdownSlide } from 'themed-markdown';
|
|
23
23
|
import { buildSubsystemGraph, deriveGraphEdges, isConstructsOnlyModel, moduleGroupNodeId, processGroupNodeId, isTrailMechanism, edgeColor, MECHANISM_DESCRIPTIONS, subsystemGraphLayoutKey, trailStepGraphEdgeId, unroutedEdgeIds, } from './model';
|
|
24
24
|
import { ConstructsCatalog } from './ConstructsCatalog';
|
|
@@ -27,15 +27,14 @@ import { MaintainLivePanel, } from '../components/maintain-events/MaintainLivePa
|
|
|
27
27
|
import { SubsystemComponentNode, SubsystemGroupNode, SubsystemEdge, SUBSYSTEM_CALLBACKS, hexWithAlpha, EDGE_DIM_ALPHA, fileMatchForNode, flowElementVisibility, flowNodeVisibility } from './nodes';
|
|
28
28
|
import { SubsystemDiagnosticToggle } from './DiagnosticToggle';
|
|
29
29
|
import { SubsystemIssueList, issueCategory, issueKindOrder, issueRung, ISSUE_KIND_ICON, ISSUE_RUNG_ORDER, } from './IssueList';
|
|
30
|
-
import { SubsystemFileTree } from './SubsystemFileTree';
|
|
31
30
|
import { GraphLayoutCover } from './GraphLayoutCover';
|
|
31
|
+
import { SubsystemFileTree } from './SubsystemFileTree';
|
|
32
|
+
import { buildTreePathMapping } from './paths';
|
|
32
33
|
import { GRAPH_CANVAS_CLASS, GRAPH_NAV_PROPS, GraphChrome, GraphLayerStyle, LAYOUT_FLIP_CLASS, LAYOUT_FLIP_EDGE_MS, LAYOUT_FLIP_NODE_MS } from './graphChrome';
|
|
33
34
|
import { ComponentDeclaration } from './ComponentDeclaration';
|
|
34
35
|
import { FileDrawer, FILE_DRAWER_HEIGHT_MS } from './FileDrawer';
|
|
35
36
|
import { GraphWithSidebar } from './GraphWithSidebar';
|
|
36
|
-
import { buildRepoGroups, repoAvatarUrl } from './paths';
|
|
37
37
|
import { TrailsPanel, TRAIL_PLAY_PAUSE_MS } from './TrailsPanel';
|
|
38
|
-
import { TrailStackBreadcrumb } from './TrailStackBreadcrumb';
|
|
39
38
|
import { EDGE_LABEL_WIDTH, EDGE_LABEL_HEIGHT, EDGE_LABEL_FONT_SIZE, EDGE_LABEL_CLOUD_PATH, EDGE_LABEL_CLOUD_EXTRA_TOP, } from '../utils/edgeLabel';
|
|
40
39
|
/** Identifiers a token could match to name this component as a construct. */
|
|
41
40
|
function constructIdentifiers(comp) {
|
|
@@ -128,9 +127,9 @@ const TrailDrawerContent = memo(function TrailDrawerContent({ render, trail, ste
|
|
|
128
127
|
onVisibleStepChange,
|
|
129
128
|
}) }));
|
|
130
129
|
});
|
|
131
|
-
function Inner({ components, trails, graphifyRelations, orderByLine, layoutDirection: layoutDirectionProp, defaultLayoutDirection = 'horizontal', onLayoutDirectionChange, hideLayoutToggle = false, refocusOnLayoutFlip = true, initialTrailId, onTrailSelect, onTrailStepHover, onReorderTrails, stepEmphasis, onRemoveStepEmphasis, onSelect, onEdgeSelect, measured: _measured, maxNodeWidth, showEdgeLabels, showSingletonFrames = true, moduleNesting, edgeView,
|
|
130
|
+
function Inner({ components, trails, graphifyRelations, orderByLine, layoutDirection: layoutDirectionProp, defaultLayoutDirection = 'horizontal', onLayoutDirectionChange, hideLayoutToggle = false, refocusOnLayoutFlip = true, initialTrailId, onTrailSelect, onTrailStepHover, onReorderTrails, stepEmphasis, onRemoveStepEmphasis, onSelect, onEdgeSelect, measured: _measured, maxNodeWidth, showEdgeLabels, showSingletonFrames = true, moduleNesting, edgeView, hideSidebar, trailStepMode = 'focus', autoPlayTrails = false, trailAutoPlayIntervalMs = TRAIL_PLAY_PAUSE_MS, zoomOnTrailFocus = true, trailFocusDurationMs = 300, graphTitle, showTrailTitle = false, description, canvasOverlay, sidebarExtra, sidebarAfterDescription, issues, showIssues, filesActive, focusIssueCategory, laneStatus, onSelectIssue, onApplyIssueFix, onHoverIssue, renderFileViewer, renderTrailViewer, onFileSelect, componentVerification, onInspectSymbol, boundaryColors, codeProcessKeys, hideDrawer = false, persistKey, liveEvents, agentsPanel }) {
|
|
132
131
|
const { theme } = useTheme();
|
|
133
|
-
const { fitView, fitBounds
|
|
132
|
+
const { fitView, fitBounds } = useReactFlow();
|
|
134
133
|
const viewport = useViewport();
|
|
135
134
|
// Restored once per mount from `localStorage` (see `readViewState`). Each
|
|
136
135
|
// graph is its own tab/mount, so `persistKey` is stable for a mount.
|
|
@@ -286,73 +285,6 @@ function Inner({ components, trails, graphifyRelations, orderByLine, layoutDirec
|
|
|
286
285
|
drawerOpenRef.current = drawerTarget != null;
|
|
287
286
|
// Pending deferred fit after opening the drawer; cancelled on re-entry / unmount.
|
|
288
287
|
const pendingFocusFitRef = useRef(null);
|
|
289
|
-
// Component the pointer is over (null on leave) → transient tree highlight.
|
|
290
|
-
const [hoveredComponentAlias, setHoveredComponentId] = useState(null);
|
|
291
|
-
// Boundary frame hovered on the canvas → highlight the matching folder row in
|
|
292
|
-
// the sidebar file tree, so a frame's place in the repo tree is visible.
|
|
293
|
-
const [hoveredRegion, setHoveredRegion] = useState(null);
|
|
294
|
-
const hoveredBoundaryPath = hoveredRegion && (hoveredRegion.kind === 'module' || hoveredRegion.kind === 'directory')
|
|
295
|
-
? hoveredRegion.key
|
|
296
|
-
: null;
|
|
297
|
-
// Path frames (module / directory) as absolute flow-coord rects, hit-tested
|
|
298
|
-
// on pointer move. A pointer hit-test keeps the frames' interior hoverable
|
|
299
|
-
// WITHOUT an overlay that would block edges / child nodes.
|
|
300
|
-
const pathFrameRects = useMemo(() => {
|
|
301
|
-
const out = [];
|
|
302
|
-
for (const n of built.nodes) {
|
|
303
|
-
if (n.type !== 'subsystem-group')
|
|
304
|
-
continue;
|
|
305
|
-
const region = n.data?.region;
|
|
306
|
-
if (!region?.key || (region.kind !== 'module' && region.kind !== 'directory'))
|
|
307
|
-
continue;
|
|
308
|
-
const r = built.absoluteRects.get(n.id);
|
|
309
|
-
if (!r)
|
|
310
|
-
continue;
|
|
311
|
-
out.push({ kind: region.kind, key: region.key, x: r.x, y: r.y, w: r.width, h: r.height });
|
|
312
|
-
}
|
|
313
|
-
return out;
|
|
314
|
-
}, [built.nodes, built.absoluteRects]);
|
|
315
|
-
useEffect(() => {
|
|
316
|
-
if (pathFrameRects.length === 0)
|
|
317
|
-
return;
|
|
318
|
-
let raf = 0;
|
|
319
|
-
let last = null;
|
|
320
|
-
const overCanvas = (e) => e.composedPath().some((el) => el instanceof HTMLElement && el.classList.contains(GRAPH_CANVAS_CLASS));
|
|
321
|
-
const onMove = (e) => {
|
|
322
|
-
if (!overCanvas(e)) {
|
|
323
|
-
last = null;
|
|
324
|
-
setHoveredRegion((prev) => (prev == null ? prev : null));
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
last = { x: e.clientX, y: e.clientY };
|
|
328
|
-
if (raf)
|
|
329
|
-
return;
|
|
330
|
-
raf = window.requestAnimationFrame(() => {
|
|
331
|
-
raf = 0;
|
|
332
|
-
if (!last)
|
|
333
|
-
return;
|
|
334
|
-
const p = screenToFlowPosition(last);
|
|
335
|
-
let best = null;
|
|
336
|
-
let bestArea = Number.POSITIVE_INFINITY;
|
|
337
|
-
for (const f of pathFrameRects) {
|
|
338
|
-
if (p.x >= f.x && p.x <= f.x + f.w && p.y >= f.y && p.y <= f.y + f.h) {
|
|
339
|
-
const area = f.w * f.h;
|
|
340
|
-
if (area < bestArea) {
|
|
341
|
-
bestArea = area;
|
|
342
|
-
best = { kind: f.kind, key: f.key };
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
setHoveredRegion((prev) => prev?.kind === best?.kind && prev?.key === best?.key ? prev : best);
|
|
347
|
-
});
|
|
348
|
-
};
|
|
349
|
-
window.addEventListener('mousemove', onMove);
|
|
350
|
-
return () => {
|
|
351
|
-
window.removeEventListener('mousemove', onMove);
|
|
352
|
-
if (raf)
|
|
353
|
-
cancelAnimationFrame(raf);
|
|
354
|
-
};
|
|
355
|
-
}, [pathFrameRects, screenToFlowPosition]);
|
|
356
288
|
// Trail focus — selected flow (or step) is full strength; other
|
|
357
289
|
// opened-flow members stay visible but dimmed; everything else is hidden.
|
|
358
290
|
// Restored from the persisted view state when a `persistKey` is set.
|
|
@@ -365,44 +297,24 @@ function Inner({ components, trails, graphifyRelations, orderByLine, layoutDirec
|
|
|
365
297
|
// a number = that step. Transient — no drawer. Camera only reframes when
|
|
366
298
|
// a step is already selected.
|
|
367
299
|
const [hoveredTrailStep, setHoveredTrailStep] = useState(null);
|
|
368
|
-
// Sidebar bottom half: which panel is shown when trails exist.
|
|
369
|
-
// The persisted value is membership-checked rather than trusted: a stale
|
|
370
|
-
// localStorage entry from before the walkthrough->trail rename would match
|
|
371
|
-
// neither panel and render a blank sidebar with no selected tab.
|
|
372
|
-
const [sidebarView, setSidebarView] = useState(() => {
|
|
373
|
-
const stored = persisted.sidebarView;
|
|
374
|
-
const restored = stored === 'files' || stored === 'trails' ? stored : 'trails';
|
|
375
|
-
return trails?.length ? restored : 'files';
|
|
376
|
-
});
|
|
377
300
|
// Diagnostics area tab: issues list vs the Maintain agent pipeline.
|
|
378
301
|
const [diagnosticsTab, setDiagnosticsTab] = useState(() => persisted.diagnosticsTab ?? 'issues');
|
|
379
|
-
// One edge source at a time.
|
|
380
|
-
//
|
|
381
|
-
// Trails draws runtime step edges. Without visible tabs (no trails,
|
|
382
|
-
// or a sidebar-less embed) fall back to trail edges.
|
|
383
|
-
const sidebarTabsVisible = !hideSidebar && (trails?.length ?? 0) > 0;
|
|
302
|
+
// One edge source at a time. Prefer runtime steps when trails exist;
|
|
303
|
+
// otherwise draw static relations, unless the caller picks explicitly.
|
|
384
304
|
// A solo trail layout only draws that flow's steps, so the trail edge source
|
|
385
305
|
// is the only one that can render anything.
|
|
386
306
|
const resolvedEdgeView = committedTrailId
|
|
387
307
|
? 'trails'
|
|
388
308
|
: edgeView ??
|
|
389
|
-
(
|
|
390
|
-
?
|
|
391
|
-
: (
|
|
392
|
-
? '
|
|
393
|
-
:
|
|
394
|
-
? 'graphify'
|
|
395
|
-
: 'trails');
|
|
309
|
+
((trails?.length ?? 0) > 0
|
|
310
|
+
? 'trails'
|
|
311
|
+
: (graphifyRelations?.length ?? 0) > 0
|
|
312
|
+
? 'graphify'
|
|
313
|
+
: 'trails');
|
|
396
314
|
// Trail flows the user has expanded (via the title row). Closed by
|
|
397
315
|
// default so a graph with several flows doesn't dump every step list at once.
|
|
398
316
|
// Restored from the persisted view state when a `persistKey` is set.
|
|
399
317
|
const [expandedTrails, setExpandedTrails] = useState(() => new Set(persisted.expandedTrails ?? []));
|
|
400
|
-
// Sidebar description visibility. Hidden by default so the files/flows
|
|
401
|
-
// panel gets the vertical room; the title-row toggle reveals it.
|
|
402
|
-
const [descriptionVisible, setDescriptionVisible] = useState(false);
|
|
403
|
-
const [descToggleHover, setDescToggleHover] = useState(false);
|
|
404
|
-
// `true` only when a description exists AND the user opened it.
|
|
405
|
-
const showDesc = !!description && descriptionVisible;
|
|
406
318
|
// Sidebar width lives in the shared GraphWithSidebar chrome — draggable and
|
|
407
319
|
// persisted under `principal.subsystems.sidebarWidth.<persistKey>`. The graph
|
|
408
320
|
// caps it at half the view width so the canvas keeps room to breathe;
|
|
@@ -427,11 +339,6 @@ function Inner({ components, trails, graphifyRelations, orderByLine, layoutDirec
|
|
|
427
339
|
return;
|
|
428
340
|
writeViewState(persistKey, { focusedStepIndex });
|
|
429
341
|
}, [persistKey, focusedStepIndex]);
|
|
430
|
-
useEffect(() => {
|
|
431
|
-
if (!persistKey)
|
|
432
|
-
return;
|
|
433
|
-
writeViewState(persistKey, { sidebarView });
|
|
434
|
-
}, [persistKey, sidebarView]);
|
|
435
342
|
useEffect(() => {
|
|
436
343
|
if (!persistKey)
|
|
437
344
|
return;
|
|
@@ -757,12 +664,10 @@ function Inner({ components, trails, graphifyRelations, orderByLine, layoutDirec
|
|
|
757
664
|
};
|
|
758
665
|
SUBSYSTEM_CALLBACKS.onEdgeSelect = selectEdge;
|
|
759
666
|
SUBSYSTEM_CALLBACKS.maxNodeWidth = maxNodeWidth;
|
|
760
|
-
SUBSYSTEM_CALLBACKS.onHover = setHoveredComponentId;
|
|
761
667
|
return () => {
|
|
762
668
|
SUBSYSTEM_CALLBACKS.onSelect = undefined;
|
|
763
669
|
SUBSYSTEM_CALLBACKS.onEdgeSelect = undefined;
|
|
764
670
|
SUBSYSTEM_CALLBACKS.maxNodeWidth = undefined;
|
|
765
|
-
SUBSYSTEM_CALLBACKS.onHover = undefined;
|
|
766
671
|
};
|
|
767
672
|
}, [components, onSelect, maxNodeWidth, selectEdge]);
|
|
768
673
|
const { nodes, edges: convertedEdges } = built;
|
|
@@ -1355,18 +1260,13 @@ function Inner({ components, trails, graphifyRelations, orderByLine, layoutDirec
|
|
|
1355
1260
|
setFocusedStepIndex(null);
|
|
1356
1261
|
issueModuleFocusRef.current = null;
|
|
1357
1262
|
}, []);
|
|
1358
|
-
// Sidebar file trees — one per repo on multi-repo graphs, each under its
|
|
1359
|
-
// own owner-avatar header. Clicking a header collapses that repo's tree.
|
|
1360
|
-
const repoGroups = useMemo(() => buildRepoGroups(components), [components]);
|
|
1361
1263
|
const hasTrails = useMemo(() => (trails?.length ?? 0) > 0, [trails]);
|
|
1264
|
+
const treePaths = useMemo(() => buildTreePathMapping(components), [components]);
|
|
1362
1265
|
// Alias → component, so the Trails panel can show human hop names instead
|
|
1363
1266
|
// of model-local aliases (and the site/`via` distinction can read).
|
|
1364
1267
|
const componentByAlias = useMemo(() => new Map(components.map((c) => [c.alias, c])), [components]);
|
|
1365
|
-
//
|
|
1366
|
-
|
|
1367
|
-
const [diagnosticsOpen, setDiagnosticsOpen] = useState(showIssues ?? (issues?.length ?? 0) > 0);
|
|
1368
|
-
const controlledIssues = showIssues !== undefined;
|
|
1369
|
-
const issuesActive = controlledIssues ? showIssues : diagnosticsOpen;
|
|
1268
|
+
// The outer header owns the diagnostics toggle and passes its resolved state.
|
|
1269
|
+
const issuesActive = showIssues === true;
|
|
1370
1270
|
// Layer focus only applies while the diagnostics list is on screen — closing
|
|
1371
1271
|
// it clears any dimming the expanded layers were driving.
|
|
1372
1272
|
useEffect(() => {
|
|
@@ -1374,32 +1274,6 @@ function Inner({ components, trails, graphifyRelations, orderByLine, layoutDirec
|
|
|
1374
1274
|
setExpandedIssueCategories([]);
|
|
1375
1275
|
}
|
|
1376
1276
|
}, [issuesActive]);
|
|
1377
|
-
const toggleIssues = () => {
|
|
1378
|
-
if (!controlledIssues)
|
|
1379
|
-
setDiagnosticsOpen((v) => !v);
|
|
1380
|
-
diagnostic?.onToggle?.();
|
|
1381
|
-
};
|
|
1382
|
-
const [collapsedRepos, setCollapsedRepos] = useState(new Set());
|
|
1383
|
-
const toggleRepoCollapsed = useCallback((key) => {
|
|
1384
|
-
setCollapsedRepos((prev) => {
|
|
1385
|
-
const next = new Set(prev);
|
|
1386
|
-
if (next.has(key))
|
|
1387
|
-
next.delete(key);
|
|
1388
|
-
else
|
|
1389
|
-
next.add(key);
|
|
1390
|
-
return next;
|
|
1391
|
-
});
|
|
1392
|
-
}, []);
|
|
1393
|
-
const treeFiles = useMemo(() => repoGroups.groups.flatMap((g) => g.entries.map((e) => e.file)), [repoGroups]);
|
|
1394
|
-
const onTreeSelectFile = useCallback((file) => {
|
|
1395
|
-
setFileOverlay(null);
|
|
1396
|
-
if (openFileRef.current?.file === file && openFileRef.current.startLine == null) {
|
|
1397
|
-
setDrawerTarget(null);
|
|
1398
|
-
return;
|
|
1399
|
-
}
|
|
1400
|
-
setDrawerTarget({ kind: 'file', file });
|
|
1401
|
-
onFileSelect?.(file);
|
|
1402
|
-
}, [onFileSelect]);
|
|
1403
1277
|
const onOpenDeclarationFile = useCallback((file, opts) => {
|
|
1404
1278
|
setFileOverlay(null);
|
|
1405
1279
|
const startLine = opts?.startLine;
|
|
@@ -1648,9 +1522,22 @@ function Inner({ components, trails, graphifyRelations, orderByLine, layoutDirec
|
|
|
1648
1522
|
setDrawerTarget((prev) => (prev?.kind === 'trail' ? null : prev));
|
|
1649
1523
|
onTrailSelect?.(null);
|
|
1650
1524
|
}, [onTrailSelect]);
|
|
1525
|
+
// A hidden trail must not leave the file view filtered to its cast.
|
|
1526
|
+
const filesWereActive = useRef(false);
|
|
1527
|
+
useEffect(() => {
|
|
1528
|
+
const enteringFiles = filesActive && !filesWereActive.current;
|
|
1529
|
+
filesWereActive.current = filesActive;
|
|
1530
|
+
if (!enteringFiles)
|
|
1531
|
+
return;
|
|
1532
|
+
clearTrailFocus();
|
|
1533
|
+
setExpandedTrails(new Set());
|
|
1534
|
+
setSelectedEdgeId(null);
|
|
1535
|
+
setLayoutTrailId(null);
|
|
1536
|
+
onTrailStepHover?.(null);
|
|
1537
|
+
}, [filesActive, clearTrailFocus, onTrailStepHover]);
|
|
1651
1538
|
// Host deep-link: opening the graph from a trail row selects that
|
|
1652
|
-
// flow — expand its steps
|
|
1653
|
-
//
|
|
1539
|
+
// flow — expand its steps and focus its edges.
|
|
1540
|
+
// Guarded by a ref so a later in-tab selection isn't yanked
|
|
1654
1541
|
// back; a remount (or a new id) re-applies it.
|
|
1655
1542
|
const appliedInitialTrailRef = useRef(null);
|
|
1656
1543
|
useEffect(() => {
|
|
@@ -1664,22 +1551,9 @@ function Inner({ components, trails, graphifyRelations, orderByLine, layoutDirec
|
|
|
1664
1551
|
if (!tl)
|
|
1665
1552
|
return;
|
|
1666
1553
|
appliedInitialTrailRef.current = initialTrailId;
|
|
1667
|
-
setSidebarView('trails');
|
|
1668
1554
|
setExpandedTrails((prev) => new Set(prev).add(tl.id));
|
|
1669
1555
|
focusTrailEdges(tl);
|
|
1670
1556
|
}, [initialTrailId, layoutReady, trails, focusTrailEdges]);
|
|
1671
|
-
// Switching sidebar panels also switches the edge vocabulary. Leaving the
|
|
1672
|
-
// Trails panel drops its canvas state (focus, expanded flows, selected
|
|
1673
|
-
// edge) so the Files view's topology edges aren't gated by a flow the user
|
|
1674
|
-
// can no longer see or un-dim.
|
|
1675
|
-
const changeSidebarView = useCallback((view) => {
|
|
1676
|
-
setSidebarView(view);
|
|
1677
|
-
if (view === 'trails')
|
|
1678
|
-
return;
|
|
1679
|
-
clearTrailFocus();
|
|
1680
|
-
setExpandedTrails(new Set());
|
|
1681
|
-
setSelectedEdgeId(null);
|
|
1682
|
-
}, [clearTrailFocus]);
|
|
1683
1557
|
// Focus a single step's edge on the canvas and open/scroll the trail
|
|
1684
1558
|
// drawer to that step's snippet. Clicking the already-focused step clears
|
|
1685
1559
|
// step focus and returns to whole-flow (high-level) framing.
|
|
@@ -2142,14 +2016,6 @@ function Inner({ components, trails, graphifyRelations, orderByLine, layoutDirec
|
|
|
2142
2016
|
});
|
|
2143
2017
|
return labels;
|
|
2144
2018
|
}, [dispEdges, selectedFlowStepNos, stepIssueBadgeByEdgeId]);
|
|
2145
|
-
// Unique source files across components → sidebar file trees.
|
|
2146
|
-
const treeFilePaths = treeFiles;
|
|
2147
|
-
// The hovered node's file (null when not hovering / file-less component).
|
|
2148
|
-
const hoveredFile = useMemo(() => {
|
|
2149
|
-
if (!hoveredComponentAlias)
|
|
2150
|
-
return null;
|
|
2151
|
-
return components.find((c) => c.alias === hoveredComponentAlias)?.file ?? null;
|
|
2152
|
-
}, [components, hoveredComponentAlias]);
|
|
2153
2019
|
// Drawer content renderer. Path-keyed only — the component-keyed
|
|
2154
2020
|
// `renderFileView` variant was removed; nothing in-repo passed it.
|
|
2155
2021
|
const fileViewer = renderFileViewer;
|
|
@@ -2159,15 +2025,15 @@ function Inner({ components, trails, graphifyRelations, orderByLine, layoutDirec
|
|
|
2159
2025
|
const trailViewerRef = useRef(renderTrailViewer);
|
|
2160
2026
|
trailViewerRef.current = renderTrailViewer;
|
|
2161
2027
|
const renderTrailDrawerContent = useCallback((ctx) => trailViewerRef.current?.(ctx) ?? null, []);
|
|
2162
|
-
// Sidebar chrome —
|
|
2163
|
-
//
|
|
2028
|
+
// Sidebar chrome — description header, files/trails, and optional Issues/Agents
|
|
2029
|
+
// diagnostics. Everything renders through the shared
|
|
2164
2030
|
// GraphWithSidebar; the graph keeps owning every bit of *state* (focused
|
|
2165
|
-
// trail,
|
|
2031
|
+
// trail, diagnostics tab) because the canvas dims from
|
|
2166
2032
|
// the same facts the sidebar renders.
|
|
2167
2033
|
const sidebarVisible = !hideSidebar &&
|
|
2168
|
-
(
|
|
2169
|
-
|
|
2170
|
-
const sidebarBodyVisible =
|
|
2034
|
+
(description || filesActive || issuesActive || sidebarExtra || sidebarAfterDescription ||
|
|
2035
|
+
hasTrails);
|
|
2036
|
+
const sidebarBodyVisible = hasTrails || filesActive || issuesActive;
|
|
2171
2037
|
const sidebarTabs = issuesActive
|
|
2172
2038
|
? agentsPanel
|
|
2173
2039
|
? [
|
|
@@ -2175,49 +2041,15 @@ function Inner({ components, trails, graphifyRelations, orderByLine, layoutDirec
|
|
|
2175
2041
|
{ id: 'agents', label: 'Agents' },
|
|
2176
2042
|
]
|
|
2177
2043
|
: undefined
|
|
2178
|
-
:
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
const sidebarHeader = (_jsxs(_Fragment, { children: [sidebarExtra, (title || description || diagnostic) && (_jsxs("div", { style: { display: 'flex', alignItems: 'flex-start', gap: 8 }, children: [title && (_jsx("h2", { style: {
|
|
2188
|
-
margin: 0,
|
|
2189
|
-
flex: 1,
|
|
2190
|
-
minWidth: 0,
|
|
2191
|
-
fontSize: theme.fontSizes[2],
|
|
2192
|
-
fontWeight: 600,
|
|
2193
|
-
color: theme.colors.text,
|
|
2194
|
-
fontFamily: theme.fonts.monospace,
|
|
2195
|
-
}, children: title })), _jsxs("div", { style: {
|
|
2196
|
-
display: 'flex',
|
|
2197
|
-
alignItems: 'center',
|
|
2198
|
-
gap: 4,
|
|
2199
|
-
flexShrink: 0,
|
|
2200
|
-
// Title's flex:1 already pushes this cluster right; without a
|
|
2201
|
-
// title, keep the actions flush right instead of flush-left.
|
|
2202
|
-
marginLeft: title ? undefined : 'auto',
|
|
2203
|
-
}, children: [diagnostic && (_jsx(SubsystemDiagnosticToggle, { ...diagnostic, active: issuesActive, onToggle: toggleIssues })), description && (_jsx("button", { type: "button", "aria-expanded": descriptionVisible, "aria-label": descriptionVisible ? 'Hide description' : 'Show description', title: descriptionVisible ? 'Hide description' : 'Show description', onMouseEnter: () => setDescToggleHover(true), onMouseLeave: () => setDescToggleHover(false), onClick: () => setDescriptionVisible((v) => !v), style: {
|
|
2204
|
-
display: 'inline-flex',
|
|
2205
|
-
alignItems: 'center',
|
|
2206
|
-
justifyContent: 'center',
|
|
2207
|
-
flexShrink: 0,
|
|
2208
|
-
width: 22,
|
|
2209
|
-
height: 22,
|
|
2210
|
-
padding: 0,
|
|
2211
|
-
border: 'none',
|
|
2212
|
-
borderRadius: 4,
|
|
2213
|
-
background: descriptionVisible || descToggleHover ? theme.colors.border : 'transparent',
|
|
2214
|
-
color: descriptionVisible || descToggleHover ? theme.colors.text : (theme.colors.textMuted ?? theme.colors.textSecondary),
|
|
2215
|
-
cursor: 'pointer',
|
|
2216
|
-
transition: 'background 120ms ease, color 120ms ease',
|
|
2217
|
-
}, children: _jsx(FileText, { size: 14 }) }))] })] })), description && descriptionVisible && (_jsx("div", { style: { fontSize: theme.fontSizes[0], lineHeight: 1.5 }, children: _jsx(IndustryMarkdownSlide, { content: description, slideIdPrefix: "subsystem-desc", slideIndex: 0, isVisible: true, theme: theme, disableScroll: true, disableBasePadding: true, enableKeyboardScrolling: false, autoFocusOnVisible: false }) })), sidebarAfterDescription] }));
|
|
2218
|
-
// Lower panel: the Issues/Agents diagnostics stack, or the Files/Trails
|
|
2219
|
-
// stack with the trail flow list / repo file tree.
|
|
2220
|
-
const sidebarBody = issuesActive ? (diagnosticsTab === 'agents' && agentsPanel ? (_jsx(SubsystemAgentsPanel, { ...agentsPanel })) : (_jsx(SubsystemIssueList, { issues: issues ?? [], focusCategory: focusIssueCategory, laneStatus: laneStatus, onSelectIssue: focusIssueTarget, onDeselectIssue: unfocusIssueTarget, onApplyFix: onApplyIssueFix, onHoverIssue: onHoverIssue, onExpandedCategoriesChange: handleExpandedCategoriesChange }))) : sidebarView === 'trails' && trails && trails.length > 0 ? (_jsx(TrailsPanel, { trails: trails, expandedTrails: expandedTrails, focusedTrailId: focusedTrailId, focusedStepIndex: focusedStepIndex, hoveredTrailStep: hoveredTrailStep, onToggleCollapsed: toggleTrailCollapsed, onFocusFlow: (tl) => {
|
|
2044
|
+
: undefined;
|
|
2045
|
+
// Header block: extras, then the description.
|
|
2046
|
+
const sidebarHeader = (_jsxs(_Fragment, { children: [sidebarExtra, description && (_jsx("div", { style: { fontSize: theme.fontSizes[0], lineHeight: 1.5 }, children: _jsx(IndustryMarkdownSlide, { content: description, slideIdPrefix: "subsystem-desc", slideIndex: 0, isVisible: true, theme: theme, disableScroll: true, disableBasePadding: true, enableKeyboardScrolling: false, autoFocusOnVisible: false }) })), sidebarAfterDescription] }));
|
|
2047
|
+
// Lower panel: diagnostics, the file tree, or the trail flow list.
|
|
2048
|
+
const sidebarBody = issuesActive ? (diagnosticsTab === 'agents' && agentsPanel ? (_jsx(SubsystemAgentsPanel, { ...agentsPanel })) : (_jsx(SubsystemIssueList, { issues: issues ?? [], focusCategory: focusIssueCategory, laneStatus: laneStatus, onSelectIssue: focusIssueTarget, onDeselectIssue: unfocusIssueTarget, onApplyFix: onApplyIssueFix, onHoverIssue: onHoverIssue, onExpandedCategoriesChange: handleExpandedCategoriesChange }))) : filesActive ? (_jsx(SubsystemFileTree, { files: treePaths.entries.map((entry) => entry.displayPath), selectedFile: openFile ? treePaths.toDisplay.get(openFile) : null, onSelectFile: (path) => {
|
|
2049
|
+
const file = treePaths.toFile.get(path);
|
|
2050
|
+
if (file)
|
|
2051
|
+
onOpenDeclarationFile(file);
|
|
2052
|
+
} })) : trails && trails.length > 0 ? (_jsx(TrailsPanel, { trails: trails, expandedTrails: expandedTrails, focusedTrailId: focusedTrailId, focusedStepIndex: focusedStepIndex, hoveredTrailStep: hoveredTrailStep, onToggleCollapsed: toggleTrailCollapsed, onFocusFlow: (tl) => {
|
|
2221
2053
|
// While a solo layout is on, following the selection keeps the canvas
|
|
2222
2054
|
// and the highlighted flow in agreement instead of leaving the graph
|
|
2223
2055
|
// pinned to a flow the user just navigated away from.
|
|
@@ -2233,17 +2065,7 @@ function Inner({ components, trails, graphifyRelations, orderByLine, layoutDirec
|
|
|
2233
2065
|
}, onLeaveStep: () => {
|
|
2234
2066
|
setHoveredTrailStep(null);
|
|
2235
2067
|
onTrailStepHover?.(null);
|
|
2236
|
-
}, onReorder: onReorderTrails, componentByAlias: componentByAlias, stepEmphasis: stepEmphasis, onRemoveStepEmphasis: onRemoveStepEmphasis, layoutFocusedTrailId: layoutTrailId, onFocusLayout: toggleLayoutFocus })) :
|
|
2237
|
-
const groupKey = group.repoKey ?? '__no-repo__';
|
|
2238
|
-
const collapsed = collapsedRepos.has(groupKey);
|
|
2239
|
-
return (_jsxs("div", { style: {
|
|
2240
|
-
flex: collapsed ? '0 0 auto' : 1,
|
|
2241
|
-
minHeight: collapsed ? 0 : undefined,
|
|
2242
|
-
display: 'flex',
|
|
2243
|
-
flexDirection: 'column',
|
|
2244
|
-
borderTop: i > 0 ? `1px solid ${theme.colors.border}` : undefined,
|
|
2245
|
-
}, children: [repoGroups.multiRepo && (_jsx(RepoGroupHeader, { group: group, collapsed: collapsed, onToggle: () => toggleRepoCollapsed(groupKey) })), !collapsed && (_jsx(SubsystemFileTree, { files: group.entries.map((e) => e.displayPath), selectedFile: selected?.file ?? openFile, hoveredFile: hoveredFile, onSelectFile: onTreeSelectFile, hoveredFolder: hoveredBoundaryPath, headerless: repoGroups.multiRepo }))] }, groupKey));
|
|
2246
|
-
}) })) : null;
|
|
2068
|
+
}, onReorder: onReorderTrails, componentByAlias: componentByAlias, stepEmphasis: stepEmphasis, onRemoveStepEmphasis: onRemoveStepEmphasis, layoutFocusedTrailId: layoutTrailId, onFocusLayout: toggleLayoutFocus })) : null;
|
|
2247
2069
|
const fileOverlayView = fileOverlay && (_jsxs("div", { style: {
|
|
2248
2070
|
position: 'absolute',
|
|
2249
2071
|
inset: 0,
|
|
@@ -2269,9 +2091,7 @@ function Inner({ components, trails, graphifyRelations, orderByLine, layoutDirec
|
|
|
2269
2091
|
fontSize: theme.fontSizes[0],
|
|
2270
2092
|
color: theme.colors.textMuted ?? theme.colors.textSecondary,
|
|
2271
2093
|
}, children: fileOverlay.file }), _jsx(FileOverlayCloseButton, { onClose: closeFileOverlay })] }), _jsx("div", { style: { flex: 1, minHeight: 0, overflow: 'auto' }, children: _jsx(FileDrawerContent, { render: renderDrawerContent, file: fileOverlay.file, startLine: fileOverlay.startLine, endLine: fileOverlay.endLine, fullFile: true }) })] }));
|
|
2272
|
-
return (_jsx(GraphWithSidebar, { hidden: !sidebarVisible, bodyHidden: !sidebarBodyVisible,
|
|
2273
|
-
? setDiagnosticsTab(id)
|
|
2274
|
-
: changeSidebarView(id), sidebarBody: sidebarBody, tabListLabel: issuesActive ? 'Diagnostics view' : 'Sidebar view', defaultWidth: persisted.sidebarWidth ?? 450, minWidth: 240, maxWidth: sidebarMaxWidth, persistKey: persistKey, onResizeEnd: () => requestAnimationFrame(() => fitView()), children: _jsxs("div", { style: { flex: 1, position: 'relative', display: 'flex', flexDirection: committedDirection === 'vertical' ? 'row' : 'column', minHeight: 0, minWidth: 0 }, children: [_jsxs("div", { className: layoutFlipping ? LAYOUT_FLIP_CLASS : undefined, style: {
|
|
2094
|
+
return (_jsx(GraphWithSidebar, { hidden: !sidebarVisible, bodyHidden: !sidebarBodyVisible, headerMaxHeight: sidebarBodyVisible ? '30%' : '100%', header: sidebarHeader, tabs: sidebarTabs, activeTab: diagnosticsTab, onTabChange: (id) => setDiagnosticsTab(id), sidebarBody: sidebarBody, tabListLabel: issuesActive ? 'Diagnostics view' : 'Sidebar view', defaultWidth: persisted.sidebarWidth ?? 450, minWidth: 240, maxWidth: sidebarMaxWidth, persistKey: persistKey, onResizeEnd: () => requestAnimationFrame(() => fitView()), children: _jsxs("div", { style: { flex: 1, position: 'relative', display: 'flex', flexDirection: committedDirection === 'vertical' ? 'row' : 'column', minHeight: 0, minWidth: 0 }, children: [_jsxs("div", { className: layoutFlipping ? LAYOUT_FLIP_CLASS : undefined, style: {
|
|
2275
2095
|
position: 'relative',
|
|
2276
2096
|
flex: collapseCanvas ? 0 : 1,
|
|
2277
2097
|
minHeight: 0,
|
|
@@ -2541,55 +2361,48 @@ function Inner({ components, trails, graphifyRelations, orderByLine, layoutDirec
|
|
|
2541
2361
|
drawerTarget.startLine != null &&
|
|
2542
2362
|
!!selected.file &&
|
|
2543
2363
|
selected.file === drawerTarget.file &&
|
|
2544
|
-
selected.declarationSpan?.startLine === drawerTarget.startLine }) })), committedDirection === 'vertical' && fileOverlayView] }), _jsx(FileDrawer, { title: drawerTitle, onClose: closeDrawer, placement: committedDirection === 'vertical' ? 'left' : 'bottom', onResizeEnd: () => requestAnimationFrame(() => fitView()), fillHeight: drawerFillHeight, suppressEscape: fileOverlayOpen, hidden: hideDrawer, progress: drawerProgress,
|
|
2545
|
-
focusedTrail &&
|
|
2546
|
-
drawerTarget.stepIndex != null ? (_jsx(TrailStackBreadcrumb, { trail: focusedTrail, stepIndex: drawerTarget.stepIndex, componentByAlias: componentByAlias, onFocusStep: (index) => focusTrailStep(focusedTrail, index) })) : null, children: drawerTarget?.kind === 'trail' &&
|
|
2364
|
+
selected.declarationSpan?.startLine === drawerTarget.startLine }) })), committedDirection === 'vertical' && fileOverlayView] }), _jsx(FileDrawer, { title: drawerTitle, onClose: closeDrawer, placement: committedDirection === 'vertical' ? 'left' : 'bottom', onResizeEnd: () => requestAnimationFrame(() => fitView()), fillHeight: drawerFillHeight, suppressEscape: fileOverlayOpen, hidden: hideDrawer, progress: drawerProgress, children: drawerTarget?.kind === 'trail' &&
|
|
2547
2365
|
focusedTrail &&
|
|
2548
2366
|
renderTrailViewer ? (_jsx(TrailDrawerContent, { render: renderTrailDrawerContent, trail: focusedTrail, stepIndex: drawerTarget.stepIndex, onOpenFile: onOpenFileFromTrail, resolveSymbol: resolveTrailSymbol, onSymbolClick: openConstructDeclaration, onVisibleStepChange: setVisibleStepIndex })) : drawerTarget?.kind === 'file' ? (_jsx(FileDrawerContent, { render: renderDrawerContent, file: drawerTarget.file, startLine: drawerTarget.startLine, endLine: drawerTarget.endLine })) : null }), committedDirection !== 'vertical' && fileOverlayView, _jsx(GraphLayoutCover, { revealed: layoutReady || layoutFlipping }), canvasOverlay, liveEvents ? _jsx(MaintainLivePanel, { ...liveEvents }) : null] }) }));
|
|
2549
2367
|
}
|
|
2550
|
-
/** Sidebar header for one repo's tree: owner avatar + repo name + file count.
|
|
2551
|
-
* Clicking toggles the tree's visibility. */
|
|
2552
|
-
function RepoGroupHeader({ group, collapsed, onToggle, }) {
|
|
2553
|
-
const { theme } = useTheme();
|
|
2554
|
-
const avatar = repoAvatarUrl(group.repoKey);
|
|
2555
|
-
const label = group.repo ?? 'No repo';
|
|
2556
|
-
return (_jsxs("div", { role: "button", "aria-expanded": !collapsed, onClick: (e) => {
|
|
2557
|
-
e.stopPropagation();
|
|
2558
|
-
onToggle();
|
|
2559
|
-
}, title: collapsed ? 'Show files' : 'Hide files', style: {
|
|
2560
|
-
display: 'flex',
|
|
2561
|
-
alignItems: 'center',
|
|
2562
|
-
gap: 8,
|
|
2563
|
-
padding: '10px 16px 4px',
|
|
2564
|
-
flexShrink: 0,
|
|
2565
|
-
cursor: 'pointer',
|
|
2566
|
-
userSelect: 'none',
|
|
2567
|
-
}, children: [avatar ? (_jsx("img", { src: avatar, alt: "", width: 16, height: 16, loading: "lazy", style: { borderRadius: 4, flexShrink: 0, background: theme.colors.border } })) : group.owner ? (_jsx("span", { style: {
|
|
2568
|
-
width: 16,
|
|
2569
|
-
height: 16,
|
|
2570
|
-
borderRadius: 4,
|
|
2571
|
-
flexShrink: 0,
|
|
2572
|
-
display: 'inline-flex',
|
|
2573
|
-
alignItems: 'center',
|
|
2574
|
-
justifyContent: 'center',
|
|
2575
|
-
fontSize: 9,
|
|
2576
|
-
fontWeight: 700,
|
|
2577
|
-
fontFamily: theme.fonts.monospace,
|
|
2578
|
-
color: theme.colors.text,
|
|
2579
|
-
background: theme.colors.border,
|
|
2580
|
-
}, children: group.owner.charAt(0).toUpperCase() })) : null, _jsx("span", { style: {
|
|
2581
|
-
fontSize: theme.fontSizes[1],
|
|
2582
|
-
fontFamily: theme.fonts.monospace,
|
|
2583
|
-
color: theme.colors.text,
|
|
2584
|
-
fontWeight: 600,
|
|
2585
|
-
overflow: 'hidden',
|
|
2586
|
-
textOverflow: 'ellipsis',
|
|
2587
|
-
whiteSpace: 'nowrap',
|
|
2588
|
-
}, children: label })] }));
|
|
2589
|
-
}
|
|
2590
2368
|
export function SubsystemComponentGraph(props) {
|
|
2369
|
+
const { theme } = useTheme();
|
|
2591
2370
|
const wrapRef = useRef(null);
|
|
2592
2371
|
const [size, setSize] = useState(null);
|
|
2372
|
+
const [filesOpen, setFilesOpen] = useState(false);
|
|
2373
|
+
const fileCount = useMemo(() => buildTreePathMapping(props.components).entries.length, [props.components]);
|
|
2374
|
+
const hasFiles = fileCount > 0;
|
|
2375
|
+
const [diagnosticsOpen, setDiagnosticsOpen] = useState(props.showIssues ?? (props.issues?.length ?? 0) > 0);
|
|
2376
|
+
const issuesActive = props.showIssues ?? diagnosticsOpen;
|
|
2377
|
+
const filesActive = filesOpen && hasFiles && !issuesActive;
|
|
2378
|
+
const selectSidebarView = (view) => {
|
|
2379
|
+
const nextIssuesActive = view === 'issues';
|
|
2380
|
+
setFilesOpen(view === 'files');
|
|
2381
|
+
if (nextIssuesActive !== issuesActive) {
|
|
2382
|
+
if (props.showIssues === undefined)
|
|
2383
|
+
setDiagnosticsOpen(nextIssuesActive);
|
|
2384
|
+
props.diagnostic?.onToggle?.();
|
|
2385
|
+
}
|
|
2386
|
+
};
|
|
2387
|
+
const viewButtonStyle = (active) => ({
|
|
2388
|
+
display: 'inline-flex',
|
|
2389
|
+
alignItems: 'center',
|
|
2390
|
+
justifyContent: 'center',
|
|
2391
|
+
gap: 4,
|
|
2392
|
+
height: 28,
|
|
2393
|
+
padding: '4px 7px',
|
|
2394
|
+
fontFamily: theme.fonts.monospace,
|
|
2395
|
+
fontSize: theme.fontSizes[0],
|
|
2396
|
+
border: 'none',
|
|
2397
|
+
borderRadius: 4,
|
|
2398
|
+
background: active ? theme.colors.border : 'transparent',
|
|
2399
|
+
color: active ? theme.colors.text : (theme.colors.textMuted ?? theme.colors.textSecondary),
|
|
2400
|
+
cursor: 'pointer',
|
|
2401
|
+
});
|
|
2402
|
+
useEffect(() => {
|
|
2403
|
+
if (props.initialTrailId)
|
|
2404
|
+
setFilesOpen(false);
|
|
2405
|
+
}, [props.initialTrailId]);
|
|
2593
2406
|
// Measure the container so React Flow always has a real, non-zero pixel size
|
|
2594
2407
|
// (responsive to the parent; avoids the %→0 blank canvas).
|
|
2595
2408
|
useEffect(() => {
|
|
@@ -2611,13 +2424,40 @@ export function SubsystemComponentGraph(props) {
|
|
|
2611
2424
|
trails: props.trails,
|
|
2612
2425
|
graphifyRelations: props.graphifyRelations,
|
|
2613
2426
|
});
|
|
2614
|
-
return (
|
|
2427
|
+
return (_jsxs("div", { style: {
|
|
2615
2428
|
position: 'relative',
|
|
2616
2429
|
width: '100%',
|
|
2617
2430
|
height: '100%',
|
|
2618
2431
|
minHeight: 0,
|
|
2619
2432
|
display: 'flex',
|
|
2620
2433
|
flexDirection: 'column',
|
|
2621
|
-
}, children:
|
|
2434
|
+
}, children: [!constructsOnly && !props.hideSidebar && (_jsxs("header", { style: {
|
|
2435
|
+
flexShrink: 0,
|
|
2436
|
+
display: 'flex',
|
|
2437
|
+
alignItems: 'center',
|
|
2438
|
+
gap: 12,
|
|
2439
|
+
padding: '12px 16px',
|
|
2440
|
+
background: theme.colors.background,
|
|
2441
|
+
borderBottom: `1px solid ${theme.colors.border}`,
|
|
2442
|
+
}, children: [props.title && (_jsx("h2", { style: {
|
|
2443
|
+
margin: 0,
|
|
2444
|
+
minWidth: 0,
|
|
2445
|
+
fontSize: theme.fontSizes[2],
|
|
2446
|
+
fontWeight: 600,
|
|
2447
|
+
color: theme.colors.text,
|
|
2448
|
+
fontFamily: theme.fonts.monospace,
|
|
2449
|
+
overflowWrap: 'anywhere',
|
|
2450
|
+
}, children: props.title })), _jsxs("div", { role: "group", "aria-label": "Sidebar view", style: {
|
|
2451
|
+
marginLeft: 'auto',
|
|
2452
|
+
flexShrink: 0,
|
|
2453
|
+
display: 'grid',
|
|
2454
|
+
gridAutoFlow: 'column',
|
|
2455
|
+
gridAutoColumns: '1fr',
|
|
2456
|
+
alignItems: 'center',
|
|
2457
|
+
gap: 2,
|
|
2458
|
+
padding: 2,
|
|
2459
|
+
border: `1px solid ${theme.colors.border}`,
|
|
2460
|
+
borderRadius: 6,
|
|
2461
|
+
}, children: [_jsxs("button", { type: "button", "aria-label": "Trails", "aria-pressed": !filesActive && !issuesActive, onClick: () => selectSidebarView('trails'), style: viewButtonStyle(!filesActive && !issuesActive), children: [_jsx(Route, { size: 14, "aria-hidden": true }), _jsx("span", { children: "Trails" })] }), hasFiles && (_jsxs("button", { type: "button", "aria-label": `Files (${fileCount} files)`, title: "Show files", "aria-pressed": filesActive, onClick: () => selectSidebarView('files'), style: viewButtonStyle(filesActive), children: [_jsx(Files, { size: 14, "aria-hidden": true }), _jsx("span", { children: "Files" }), _jsx("span", { style: { fontWeight: 600 }, children: fileCount })] })), _jsx(SubsystemDiagnosticToggle, { ...(props.diagnostic ?? { status: 'unknown', issueCount: props.issues?.length }), labelText: "Issues", segmented: true, active: issuesActive, onToggle: () => selectSidebarView('issues') })] })] })), _jsx("div", { ref: wrapRef, style: { flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column' }, children: constructsOnly ? (_jsx(ConstructsCatalog, { components: props.components, onSelect: props.onSelect, title: props.title, hideSidebar: props.hideSidebar, description: props.description, descriptionOpen: props.descriptionOpen, onDescriptionOpenChange: props.onDescriptionOpenChange, diagnostic: props.diagnostic, sidebarExtra: props.sidebarExtra, sidebarAfterDescription: props.sidebarAfterDescription, renderFileViewer: props.renderFileViewer, onFileSelect: props.onFileSelect, componentVerification: props.componentVerification, onInspectSymbol: props.onInspectSymbol })) : (_jsx(ReactFlowProvider, { children: _jsx(Inner, { ...props, showIssues: issuesActive, filesActive: filesActive, measured: size }) })) })] }));
|
|
2622
2462
|
}
|
|
2623
2463
|
//# sourceMappingURL=SubsystemComponentGraph.js.map
|