@principal-ai/subsystems-react 0.55.1 → 0.55.3
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/README.md +2 -1
- package/dist/graphify/index.d.ts +2 -0
- package/dist/graphify/index.d.ts.map +1 -1
- package/dist/graphify/index.js +1 -0
- package/dist/graphify/index.js.map +1 -1
- package/dist/graphify/symbolFocus.d.ts +49 -0
- package/dist/graphify/symbolFocus.d.ts.map +1 -0
- package/dist/graphify/symbolFocus.js +139 -0
- package/dist/graphify/symbolFocus.js.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/stories/Canvases/Aggregate/aggregateViewFixture.d.ts.map +1 -0
- package/dist/stories/Canvases/Aggregate/aggregateViewFixture.js.map +1 -0
- package/dist/stories/Canvases/Subsystem/fixtures.d.ts.map +1 -0
- package/dist/stories/Canvases/Subsystem/fixtures.js.map +1 -0
- package/dist/subsystem/SubsystemComponentGraph.d.ts +35 -1
- package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.js +248 -33
- package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
- package/dist/subsystem/SymbolFocusedView.d.ts +30 -0
- package/dist/subsystem/SymbolFocusedView.d.ts.map +1 -0
- package/dist/subsystem/SymbolFocusedView.js +24 -0
- package/dist/subsystem/SymbolFocusedView.js.map +1 -0
- package/dist/subsystem/TrailsPanel.d.ts +16 -1
- package/dist/subsystem/TrailsPanel.d.ts.map +1 -1
- package/dist/subsystem/TrailsPanel.js +64 -8
- package/dist/subsystem/TrailsPanel.js.map +1 -1
- package/dist/subsystem/graphChrome.d.ts +13 -0
- package/dist/subsystem/graphChrome.d.ts.map +1 -1
- package/dist/subsystem/graphChrome.js +19 -1
- package/dist/subsystem/graphChrome.js.map +1 -1
- package/dist/subsystem/model.d.ts +40 -4
- package/dist/subsystem/model.d.ts.map +1 -1
- package/dist/subsystem/model.js +38 -7
- package/dist/subsystem/model.js.map +1 -1
- package/package.json +2 -2
- package/src/graphify/index.ts +6 -0
- package/src/graphify/symbolFocus.test.ts +163 -0
- package/src/graphify/symbolFocus.ts +190 -0
- package/src/index.ts +7 -0
- package/src/stories/{Subsystem/AggregateGraph → Canvases/Aggregate}/AggregateFrameGraph.stories.tsx +1 -1
- package/src/stories/{Subsystem/C4Graph → Canvases/C4}/C4CardGrow.stories.tsx +1 -1
- package/src/stories/{Subsystem/C4Graph → Canvases/C4}/C4Component.stories.tsx +1 -1
- package/src/stories/{Subsystem/C4Graph → Canvases/C4}/C4Container.stories.tsx +1 -1
- package/src/stories/{Subsystem/C4Graph → Canvases/C4}/C4NodeGallery.stories.tsx +1 -1
- package/src/stories/{Subsystem/C4Graph → Canvases/C4}/C4ToComponentDrill.stories.tsx +1 -1
- package/src/stories/{Subsystem/C4Graph → Canvases/C4}/C4Transition.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/AnimatedProgression.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/Appearance.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/Basics.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/Captures.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/Constructs.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/CustomEntities.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/Deprecated.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/DetailPanel.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/Diagnostics.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/EdgeViews.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/FrameworkStereotype.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/GraphTitle.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/Issues.stories.tsx +1 -1
- package/src/stories/Canvases/Subsystem/LayoutDirection.stories.tsx +122 -0
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/ModuleBadges.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/ModulePathNesting.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/Modules.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/NodeAnatomy.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/Packages.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/Processes.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/Proposed.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/ProposedTrails.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/Reorder.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/Scenarios.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/Spotlights.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/StoreValueType.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/TrailAutoplay.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/Trails.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/WorkspacePackageFrames.stories.tsx +1 -1
- package/src/stories/Canvases/SymbolFocusedView/SymbolFocusedView.stories.tsx +163 -0
- package/src/stories/{Subsystem → Components}/AgentsPanel.stories.tsx +1 -1
- package/src/stories/{Subsystem/ComponentDeclarationAudit.stories.tsx → Components/ComponentDeclaration/Audit.stories.tsx} +4 -4
- package/src/stories/{Subsystem/ComponentDeclarationSymbols.stories.tsx → Components/ComponentDeclaration/Symbols.stories.tsx} +5 -5
- package/src/stories/{Subsystem → Components}/ConstructsCatalog.stories.tsx +1 -1
- package/src/stories/{Subsystem/Sidebar → Components}/GraphSidebar.stories.tsx +4 -4
- package/src/stories/{Subsystem/IssueIcons.stories.tsx → Components/IssueList.stories.tsx} +1 -1
- package/src/stories/{Subsystem/StoreValueTypeDeclaration.stories.tsx → Components/StoreValueType.stories.tsx} +1 -1
- package/src/subsystem/SubsystemComponentGraph.tsx +331 -25
- package/src/subsystem/SymbolFocusedView.tsx +80 -0
- package/src/subsystem/TrailsPanel.tsx +107 -4
- package/src/subsystem/graphChrome.tsx +22 -1
- package/src/subsystem/layoutDirection.test.ts +26 -0
- package/src/subsystem/model.test.ts +17 -0
- package/src/subsystem/model.ts +70 -10
- package/dist/stories/Subsystem/AggregateGraph/aggregateViewFixture.d.ts.map +0 -1
- package/dist/stories/Subsystem/AggregateGraph/aggregateViewFixture.js.map +0 -1
- package/dist/stories/Subsystem/ComponentGraph/fixtures.d.ts.map +0 -1
- package/dist/stories/Subsystem/ComponentGraph/fixtures.js.map +0 -1
- package/src/stories/Subsystem/EgoGraph/EgoGraph.stories.tsx +0 -351
- /package/dist/stories/{Subsystem/AggregateGraph → Canvases/Aggregate}/aggregateViewFixture.d.ts +0 -0
- /package/dist/stories/{Subsystem/AggregateGraph → Canvases/Aggregate}/aggregateViewFixture.js +0 -0
- /package/dist/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/fixtures.d.ts +0 -0
- /package/dist/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/fixtures.js +0 -0
- /package/src/graphify/__fixtures__/{ego-samples.json → symbol-focused-samples.json} +0 -0
- /package/src/stories/{Subsystem/AggregateGraph → Canvases/Aggregate}/aggregateViewFixture.ts +0 -0
- /package/src/stories/{Subsystem/ComponentGraph → Canvases/Subsystem}/fixtures.ts +0 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SymbolFocusedView — one graphify symbol and its radius-1 neighborhood on the
|
|
3
|
+
* subsystem canvas.
|
|
4
|
+
*
|
|
5
|
+
* This is the public entry point for the "focused symbol" canvas: the host
|
|
6
|
+
* hands over the center node plus the neighbors and edges graphify already
|
|
7
|
+
* extracted, and the view projects them onto the shared component graph
|
|
8
|
+
* (`toSymbolFocusedView`). Edges keep their raw graphify verbs and draw from
|
|
9
|
+
* the graphify palette, so derived facts stay visually distinct from authored
|
|
10
|
+
* subsystem mechanisms.
|
|
11
|
+
*
|
|
12
|
+
* No trails, no drawers for a model: a symbol focus is read, not walked. Hover
|
|
13
|
+
* and select still work, and double-clicking a node opens its file like any
|
|
14
|
+
* other component node.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { useMemo } from 'react';
|
|
18
|
+
import type {
|
|
19
|
+
SymbolFocusedViewInput,
|
|
20
|
+
SymbolFocusedViewOptions,
|
|
21
|
+
} from '../graphify/symbolFocus';
|
|
22
|
+
import { toSymbolFocusedView } from '../graphify/symbolFocus';
|
|
23
|
+
import type { SubsystemComponentGraphProps } from './SubsystemComponentGraph';
|
|
24
|
+
import { SubsystemComponentGraph } from './SubsystemComponentGraph';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Canvas chrome that is meaningful for a derived, trail-less projection. The
|
|
28
|
+
* authored-model inputs (`components`, `trails`, `graphifyRelations`) and the
|
|
29
|
+
* trail deep-link/playback props are deliberately not forwarded: the view
|
|
30
|
+
* owns them.
|
|
31
|
+
*/
|
|
32
|
+
type SymbolFocusedViewChrome = Pick<
|
|
33
|
+
SubsystemComponentGraphProps,
|
|
34
|
+
| 'title'
|
|
35
|
+
| 'description'
|
|
36
|
+
| 'orderByLine'
|
|
37
|
+
| 'showEdgeLabels'
|
|
38
|
+
| 'showSingletonFrames'
|
|
39
|
+
| 'hideSidebar'
|
|
40
|
+
| 'hideDrawer'
|
|
41
|
+
| 'maxNodeWidth'
|
|
42
|
+
| 'persistKey'
|
|
43
|
+
| 'onSelect'
|
|
44
|
+
| 'onEdgeSelect'
|
|
45
|
+
| 'onFileSelect'
|
|
46
|
+
| 'componentVerification'
|
|
47
|
+
| 'onInspectSymbol'
|
|
48
|
+
| 'canvasOverlay'
|
|
49
|
+
>;
|
|
50
|
+
|
|
51
|
+
export interface SymbolFocusedViewProps
|
|
52
|
+
extends SymbolFocusedViewInput,
|
|
53
|
+
SymbolFocusedViewOptions,
|
|
54
|
+
SymbolFocusedViewChrome {}
|
|
55
|
+
|
|
56
|
+
export function SymbolFocusedView({
|
|
57
|
+
center,
|
|
58
|
+
neighbors,
|
|
59
|
+
edges,
|
|
60
|
+
repoPurl,
|
|
61
|
+
centerConstruct,
|
|
62
|
+
...chrome
|
|
63
|
+
}: SymbolFocusedViewProps) {
|
|
64
|
+
const { components, graphifyRelations } = useMemo(
|
|
65
|
+
() =>
|
|
66
|
+
toSymbolFocusedView(
|
|
67
|
+
{ center, neighbors, edges },
|
|
68
|
+
{ repoPurl, ...(centerConstruct ? { centerConstruct } : {}) },
|
|
69
|
+
),
|
|
70
|
+
[center, neighbors, edges, repoPurl, centerConstruct],
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<SubsystemComponentGraph
|
|
75
|
+
components={components}
|
|
76
|
+
graphifyRelations={graphifyRelations}
|
|
77
|
+
{...chrome}
|
|
78
|
+
/>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
14
14
|
import type { DragEvent as ReactDragEvent } from 'react';
|
|
15
15
|
import { useTheme } from '@principal-ade/industry-theme';
|
|
16
|
-
import { Check, ChevronDown, Copy, GripVertical, Pause, Play } from 'lucide-react';
|
|
16
|
+
import { Check, ChevronDown, Copy, Focus, GripVertical, Minimize2, Pause, Play } from 'lucide-react';
|
|
17
17
|
import {
|
|
18
18
|
PROPOSED_COLOR,
|
|
19
19
|
reorderTargetIndex,
|
|
@@ -27,6 +27,15 @@ import { buildStepBrief } from './trailBrief';
|
|
|
27
27
|
/** Pause (ms) between steps when a trail autoplays. */
|
|
28
28
|
export const TRAIL_PLAY_PAUSE_MS = 2500;
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Dwell time (ms) before a hover preview reaches the canvas.
|
|
32
|
+
*
|
|
33
|
+
* The row's own highlight stays immediate; the canvas preview (dimming +
|
|
34
|
+
* camera frame) waits this long, so sweeping the pointer across steps does not
|
|
35
|
+
* fire a reframe per step. Leaving hover clears immediately.
|
|
36
|
+
*/
|
|
37
|
+
export const TRAIL_HOVER_PREVIEW_MS = 150;
|
|
38
|
+
|
|
30
39
|
/** How long a step's copy button flashes its "copied" checkmark (ms). */
|
|
31
40
|
export const STEP_COPY_FEEDBACK_MS = 2000;
|
|
32
41
|
|
|
@@ -61,6 +70,8 @@ function TrailFlow({
|
|
|
61
70
|
dragActive,
|
|
62
71
|
isHoverSuppressed,
|
|
63
72
|
proposedAliases,
|
|
73
|
+
layoutFocused,
|
|
74
|
+
onToggleLayoutFocus,
|
|
64
75
|
}: {
|
|
65
76
|
trail: SubsystemTrail;
|
|
66
77
|
collapsed: boolean;
|
|
@@ -86,6 +97,10 @@ function TrailFlow({
|
|
|
86
97
|
* (darkgoldenrod, same as the proposed node treatment).
|
|
87
98
|
*/
|
|
88
99
|
proposedAliases?: ReadonlySet<string>;
|
|
100
|
+
/** This flow's components are the only ones laid out right now. */
|
|
101
|
+
layoutFocused?: boolean;
|
|
102
|
+
/** Solo this flow's components in the layout, or restore the full model. */
|
|
103
|
+
onToggleLayoutFocus?: (tl: SubsystemTrail) => void;
|
|
89
104
|
}) {
|
|
90
105
|
const { theme } = useTheme();
|
|
91
106
|
const muted = theme.colors.textMuted ?? theme.colors.textSecondary;
|
|
@@ -102,8 +117,30 @@ function TrailFlow({
|
|
|
102
117
|
const [headerHover, setHeaderHover] = useState(false);
|
|
103
118
|
const [collapseHover, setCollapseHover] = useState(false);
|
|
104
119
|
const [playHover, setPlayHover] = useState(false);
|
|
120
|
+
const [focusHover, setFocusHover] = useState(false);
|
|
105
121
|
const [gripHover, setGripHover] = useState(false);
|
|
106
122
|
const [hoveredStep, setHoveredStep] = useState<number | null>(null);
|
|
123
|
+
// Dwell timer for the canvas preview. The row highlight is immediate; only
|
|
124
|
+
// the `onHover*` callbacks (dim + camera) are delayed.
|
|
125
|
+
const hoverPreviewTimerRef = useRef<number | null>(null);
|
|
126
|
+
const cancelHoverPreview = useCallback(() => {
|
|
127
|
+
if (hoverPreviewTimerRef.current != null) {
|
|
128
|
+
window.clearTimeout(hoverPreviewTimerRef.current);
|
|
129
|
+
hoverPreviewTimerRef.current = null;
|
|
130
|
+
}
|
|
131
|
+
}, []);
|
|
132
|
+
const scheduleHoverPreview = useCallback(
|
|
133
|
+
(run: () => void) => {
|
|
134
|
+
cancelHoverPreview();
|
|
135
|
+
hoverPreviewTimerRef.current = window.setTimeout(() => {
|
|
136
|
+
hoverPreviewTimerRef.current = null;
|
|
137
|
+
run();
|
|
138
|
+
}, TRAIL_HOVER_PREVIEW_MS);
|
|
139
|
+
},
|
|
140
|
+
[cancelHoverPreview],
|
|
141
|
+
);
|
|
142
|
+
// A pending preview must not fire after the row unmounts or collapses.
|
|
143
|
+
useEffect(() => () => cancelHoverPreview(), [cancelHoverPreview]);
|
|
107
144
|
const stepButtonRefs = useRef<(HTMLButtonElement | null)[]>([]);
|
|
108
145
|
// Per-step copy affordance: revealed while a row is hovered or holds focus.
|
|
109
146
|
// `focusedStep` tracks focus-within (button or its copy control) so the icon
|
|
@@ -171,6 +208,12 @@ function TrailFlow({
|
|
|
171
208
|
setHoveredStep(null);
|
|
172
209
|
}, [dragActive]);
|
|
173
210
|
|
|
211
|
+
// Expanding / collapsing swaps which preview applies (header flow vs step
|
|
212
|
+
// list). Drop any pending preview so it cannot fire against the new shape.
|
|
213
|
+
useEffect(() => {
|
|
214
|
+
cancelHoverPreview();
|
|
215
|
+
}, [collapsed, cancelHoverPreview]);
|
|
216
|
+
|
|
174
217
|
const startPlaying = useCallback(() => {
|
|
175
218
|
if (collapsed) onToggleCollapsed(trail.id);
|
|
176
219
|
if (active === null || active.stepIndex !== null) onFocusFlow(trail);
|
|
@@ -213,11 +256,14 @@ function TrailFlow({
|
|
|
213
256
|
onMouseEnter={() => {
|
|
214
257
|
if (isHoverSuppressed?.()) return;
|
|
215
258
|
setHeaderHover(true);
|
|
216
|
-
if (collapsed) onHoverFlow(trail);
|
|
259
|
+
if (collapsed) scheduleHoverPreview(() => onHoverFlow(trail));
|
|
217
260
|
}}
|
|
218
261
|
onMouseLeave={() => {
|
|
219
262
|
setHeaderHover(false);
|
|
220
|
-
if (collapsed)
|
|
263
|
+
if (collapsed) {
|
|
264
|
+
cancelHoverPreview();
|
|
265
|
+
onLeaveStep();
|
|
266
|
+
}
|
|
221
267
|
}}
|
|
222
268
|
style={{
|
|
223
269
|
display: 'flex',
|
|
@@ -278,6 +324,49 @@ function TrailFlow({
|
|
|
278
324
|
</button>
|
|
279
325
|
{!collapsed && (
|
|
280
326
|
<>
|
|
327
|
+
{onToggleLayoutFocus && trail.steps.length > 0 && (
|
|
328
|
+
<button
|
|
329
|
+
type="button"
|
|
330
|
+
aria-label={
|
|
331
|
+
layoutFocused
|
|
332
|
+
? `Exit focus layout for ${trail.title}`
|
|
333
|
+
: `Focus layout on ${trail.title}`
|
|
334
|
+
}
|
|
335
|
+
aria-pressed={!!layoutFocused}
|
|
336
|
+
title={
|
|
337
|
+
layoutFocused
|
|
338
|
+
? 'Show the full model again'
|
|
339
|
+
: 'Lay out only this flow’s components'
|
|
340
|
+
}
|
|
341
|
+
onMouseEnter={() => setFocusHover(true)}
|
|
342
|
+
onMouseLeave={() => setFocusHover(false)}
|
|
343
|
+
onClick={(e) => {
|
|
344
|
+
e.stopPropagation();
|
|
345
|
+
onToggleLayoutFocus(trail);
|
|
346
|
+
}}
|
|
347
|
+
style={{
|
|
348
|
+
display: 'inline-flex',
|
|
349
|
+
alignItems: 'center',
|
|
350
|
+
justifyContent: 'center',
|
|
351
|
+
flexShrink: 0,
|
|
352
|
+
alignSelf: 'stretch',
|
|
353
|
+
width: TRAIL_CONTROL_SIZE,
|
|
354
|
+
padding: 0,
|
|
355
|
+
border: 'none',
|
|
356
|
+
borderRadius: 0,
|
|
357
|
+
background: layoutFocused || focusHover ? theme.colors.border : 'transparent',
|
|
358
|
+
color: layoutFocused || focusHover ? theme.colors.text : muted,
|
|
359
|
+
cursor: 'pointer',
|
|
360
|
+
transition: 'background 120ms ease, color 120ms ease',
|
|
361
|
+
}}
|
|
362
|
+
>
|
|
363
|
+
{layoutFocused ? (
|
|
364
|
+
<Minimize2 size={12} strokeWidth={2} />
|
|
365
|
+
) : (
|
|
366
|
+
<Focus size={13} strokeWidth={2} />
|
|
367
|
+
)}
|
|
368
|
+
</button>
|
|
369
|
+
)}
|
|
281
370
|
<button
|
|
282
371
|
type="button"
|
|
283
372
|
aria-label={playing ? `Pause ${trail.title} autoplay` : `Play ${trail.title}`}
|
|
@@ -370,6 +459,7 @@ function TrailFlow({
|
|
|
370
459
|
<div
|
|
371
460
|
style={{ display: 'flex', flexDirection: 'column' }}
|
|
372
461
|
onMouseLeave={() => {
|
|
462
|
+
cancelHoverPreview();
|
|
373
463
|
setHoveredStep(null);
|
|
374
464
|
onLeaveStep();
|
|
375
465
|
}}
|
|
@@ -400,7 +490,7 @@ function TrailFlow({
|
|
|
400
490
|
title={proposed ? 'Step touches a proposed component' : undefined}
|
|
401
491
|
onMouseEnter={() => {
|
|
402
492
|
setHoveredStep(i);
|
|
403
|
-
onHoverStep(trail, i);
|
|
493
|
+
scheduleHoverPreview(() => onHoverStep(trail, i));
|
|
404
494
|
}}
|
|
405
495
|
onClick={() => onFocusStep(trail, i)}
|
|
406
496
|
style={{
|
|
@@ -530,6 +620,13 @@ export interface TrailsPanelProps {
|
|
|
530
620
|
* (darkgoldenrod, same as the proposed node treatment).
|
|
531
621
|
*/
|
|
532
622
|
proposedAliases?: ReadonlySet<string>;
|
|
623
|
+
/** Trail whose components are the only ones laid out on the canvas, if any. */
|
|
624
|
+
layoutFocusedTrailId?: string | null;
|
|
625
|
+
/**
|
|
626
|
+
* Toggle the solo layout for a flow: relayout the canvas with just that
|
|
627
|
+
* flow's components and edges. Omit to hide the per-row focus control.
|
|
628
|
+
*/
|
|
629
|
+
onFocusLayout?: (trailId: string) => void;
|
|
533
630
|
}
|
|
534
631
|
|
|
535
632
|
/**
|
|
@@ -552,6 +649,8 @@ export function TrailsPanel({
|
|
|
552
649
|
onLeaveStep,
|
|
553
650
|
onReorder,
|
|
554
651
|
proposedAliases,
|
|
652
|
+
layoutFocusedTrailId,
|
|
653
|
+
onFocusLayout,
|
|
555
654
|
}: TrailsPanelProps) {
|
|
556
655
|
const { theme } = useTheme();
|
|
557
656
|
// Reordering is offered only while every row is collapsed: the compact list
|
|
@@ -732,6 +831,10 @@ export function TrailsPanel({
|
|
|
732
831
|
dragActive={dragIndex != null}
|
|
733
832
|
isHoverSuppressed={isHoverSuppressed}
|
|
734
833
|
proposedAliases={proposedAliases}
|
|
834
|
+
layoutFocused={layoutFocusedTrailId === tl.id}
|
|
835
|
+
onToggleLayoutFocus={
|
|
836
|
+
onFocusLayout ? (t) => onFocusLayout(t.id) : undefined
|
|
837
|
+
}
|
|
735
838
|
/>
|
|
736
839
|
</div>
|
|
737
840
|
);
|
|
@@ -43,7 +43,28 @@ export const GRAPH_NAV_PROPS = {
|
|
|
43
43
|
*/
|
|
44
44
|
export const GRAPH_CANVAS_CLASS = 'subsystem-graph-canvas';
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Applied to the canvas box while a layout-direction flip is in progress.
|
|
48
|
+
*
|
|
49
|
+
* The flip is sequenced in three beats: edges fade out (`LAYOUT_FLIP_EDGE_MS`),
|
|
50
|
+
* the graph re-lays out with nodes sliding to their new slots
|
|
51
|
+
* (`LAYOUT_FLIP_NODE_MS`), then edges fade back in. Hiding the edges first
|
|
52
|
+
* keeps the old routes from cutting across the new layout mid-flight.
|
|
53
|
+
*/
|
|
54
|
+
export const LAYOUT_FLIP_CLASS = 'subsystem-layout-flipping';
|
|
55
|
+
|
|
56
|
+
/** Edge fade-out/in duration around a layout flip, ms. */
|
|
57
|
+
export const LAYOUT_FLIP_EDGE_MS = 180;
|
|
58
|
+
|
|
59
|
+
/** Node slide duration to the new layout, ms. */
|
|
60
|
+
export const LAYOUT_FLIP_NODE_MS = 320;
|
|
61
|
+
|
|
62
|
+
export const GRAPH_LAYER_CSS = `
|
|
63
|
+
.${GRAPH_CANVAS_CLASS} .react-flow__nodes { z-index: 1; }
|
|
64
|
+
.${GRAPH_CANVAS_CLASS} .react-flow__edges { transition: opacity ${LAYOUT_FLIP_EDGE_MS}ms ease; }
|
|
65
|
+
.${LAYOUT_FLIP_CLASS} .react-flow__edges { opacity: 0; }
|
|
66
|
+
.${LAYOUT_FLIP_CLASS} .react-flow__node { transition: transform ${LAYOUT_FLIP_NODE_MS}ms cubic-bezier(0.4, 0, 0.2, 1); }
|
|
67
|
+
`;
|
|
47
68
|
|
|
48
69
|
/** Scoped style tag for `GRAPH_LAYER_CSS`; render once beside `<ReactFlow>`. */
|
|
49
70
|
export function GraphLayerStyle() {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Direction wiring for the layered layout.
|
|
3
|
+
*
|
|
4
|
+
* ELK itself can't run under bun's test runner (elkjs wants a Web Worker, so
|
|
5
|
+
* `computeElkLayout` falls back to manual positions), which makes the rendered
|
|
6
|
+
* geometry identical in every direction. What is testable — and what actually
|
|
7
|
+
* decides the axis — is the mapping from the canvas option onto
|
|
8
|
+
* `elk.direction`, so that is what these tests pin.
|
|
9
|
+
*/
|
|
10
|
+
import { describe, expect, test } from 'bun:test';
|
|
11
|
+
import { elkDirectionFor } from './model';
|
|
12
|
+
|
|
13
|
+
describe('elkDirectionFor', () => {
|
|
14
|
+
test('horizontal flow lays layers left-to-right', () => {
|
|
15
|
+
expect(elkDirectionFor('horizontal', true)).toBe('RIGHT');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('vertical flow lays layers top-to-bottom', () => {
|
|
19
|
+
expect(elkDirectionFor('vertical', true)).toBe('DOWN');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('edge-less containment keeps its DOWN + grid treatment either way', () => {
|
|
23
|
+
expect(elkDirectionFor('horizontal', false)).toBe('DOWN');
|
|
24
|
+
expect(elkDirectionFor('vertical', false)).toBe('DOWN');
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
buildBoundaryLayoutGroups,
|
|
27
27
|
buildBoundaryLayoutGroupsByPath,
|
|
28
28
|
buildSubsystemGraph,
|
|
29
|
+
unroutedEdgeIds,
|
|
29
30
|
componentPackageKey,
|
|
30
31
|
deriveNameFromSymbol,
|
|
31
32
|
isConstructsOnlyModel,
|
|
@@ -1227,3 +1228,19 @@ describe('graphify-native edges', () => {
|
|
|
1227
1228
|
expect((g?.data as { mechanism?: string })?.mechanism).toBe('imports');
|
|
1228
1229
|
});
|
|
1229
1230
|
});
|
|
1231
|
+
|
|
1232
|
+
describe('unroutedEdgeIds', () => {
|
|
1233
|
+
test('lists shown edges with no elkPath', () => {
|
|
1234
|
+
const ids = unroutedEdgeIds([
|
|
1235
|
+
{ id: 'routed', data: { elkPath: 'M 0 0 L 10 0' } },
|
|
1236
|
+
{ id: 'no-path', data: { mechanism: 'calls' } },
|
|
1237
|
+
{ id: 'no-data' },
|
|
1238
|
+
{ id: 'empty-path', data: { elkPath: '' } },
|
|
1239
|
+
]);
|
|
1240
|
+
expect(ids).toEqual(['no-path', 'no-data', 'empty-path']);
|
|
1241
|
+
});
|
|
1242
|
+
|
|
1243
|
+
test('skips hidden edges', () => {
|
|
1244
|
+
expect(unroutedEdgeIds([{ id: 'hidden', hidden: true, data: {} }])).toEqual([]);
|
|
1245
|
+
});
|
|
1246
|
+
});
|
package/src/subsystem/model.ts
CHANGED
|
@@ -136,6 +136,17 @@ export interface SubsystemComponent extends CoreSubsystemComponent {
|
|
|
136
136
|
*/
|
|
137
137
|
export type SubsystemEdgeView = 'graphify' | 'trails';
|
|
138
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Flow direction of the layered layout.
|
|
141
|
+
*
|
|
142
|
+
* - `horizontal`: layers run left-to-right (ELK `RIGHT`)
|
|
143
|
+
* - `vertical`: layers run top-to-bottom (ELK `DOWN`)
|
|
144
|
+
*
|
|
145
|
+
* Edge-less containment graphs (pure module/process frames) keep their
|
|
146
|
+
* existing DOWN + grid treatment in either mode — there is no flow to orient.
|
|
147
|
+
*/
|
|
148
|
+
export type SubsystemLayoutDirection = 'horizontal' | 'vertical';
|
|
149
|
+
|
|
139
150
|
/**
|
|
140
151
|
* Where a display edge came from.
|
|
141
152
|
*
|
|
@@ -167,8 +178,8 @@ export interface SubsystemGraphifyRelation {
|
|
|
167
178
|
refs?: string[];
|
|
168
179
|
/**
|
|
169
180
|
* 1-based source line of the relation site. Optional — available on
|
|
170
|
-
* `calls`/`references`-style edges; used to order
|
|
171
|
-
* call site when a host opts into line-ordered layering.
|
|
181
|
+
* `calls`/`references`-style edges; used to order a symbol-focused view's
|
|
182
|
+
* callees by call site when a host opts into line-ordered layering.
|
|
172
183
|
*/
|
|
173
184
|
line?: number;
|
|
174
185
|
/** graphify's provenance tag (`EXTRACTED` / `INFERRED` / `AMBIGUOUS`). */
|
|
@@ -1866,6 +1877,22 @@ export function subsystemGraphLayoutKey(
|
|
|
1866
1877
|
return `${components}|${edgeKey}`;
|
|
1867
1878
|
}
|
|
1868
1879
|
|
|
1880
|
+
/**
|
|
1881
|
+
* ELK `elk.direction` for a requested flow direction.
|
|
1882
|
+
*
|
|
1883
|
+
* Edge-less containment graphs have no flow to orient: a RIGHT layout stacks
|
|
1884
|
+
* disconnected top-level frames in one long vertical column, while DOWN places
|
|
1885
|
+
* those sibling frames along the horizontal axis and stacks each frame's
|
|
1886
|
+
* members vertically. That containment treatment wins in both modes.
|
|
1887
|
+
*/
|
|
1888
|
+
export function elkDirectionFor(
|
|
1889
|
+
layoutDirection: SubsystemLayoutDirection,
|
|
1890
|
+
hasEdges: boolean,
|
|
1891
|
+
): 'RIGHT' | 'DOWN' {
|
|
1892
|
+
if (!hasEdges) return 'DOWN';
|
|
1893
|
+
return layoutDirection === 'vertical' ? 'DOWN' : 'RIGHT';
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1869
1896
|
/**
|
|
1870
1897
|
* Build the full React Flow graph (nodes + edges) using **ELK auto-layout** to
|
|
1871
1898
|
* position every node (including external stub targets), so the graph is
|
|
@@ -1885,11 +1912,18 @@ export async function buildSubsystemGraph(
|
|
|
1885
1912
|
* Order same-layer nodes by `component.line` (ascending) instead of ELK's
|
|
1886
1913
|
* crossing-minimizer. Only meaningful when components carry a `line` — and
|
|
1887
1914
|
* callers are expected to stamp `line` with the RELEVANT site for their
|
|
1888
|
-
* graph (e.g.
|
|
1889
|
-
* line, not the callee's declaration line).
|
|
1915
|
+
* graph (e.g. a symbol-focused view stamps each callee with the center's
|
|
1916
|
+
* call-site line, not the callee's declaration line).
|
|
1890
1917
|
* @default false
|
|
1891
1918
|
*/
|
|
1892
1919
|
orderByLine?: boolean;
|
|
1920
|
+
/**
|
|
1921
|
+
* Flow direction of the layered layout. `horizontal` (default) lays layers
|
|
1922
|
+
* left-to-right, `vertical` top-to-bottom. Edge-less containment graphs
|
|
1923
|
+
* keep their DOWN + grid treatment either way.
|
|
1924
|
+
* @default 'horizontal'
|
|
1925
|
+
*/
|
|
1926
|
+
layoutDirection?: SubsystemLayoutDirection;
|
|
1893
1927
|
} & BoundaryFrameOptions = {},
|
|
1894
1928
|
): Promise<{
|
|
1895
1929
|
nodes: SubsystemGraphNode[];
|
|
@@ -1912,6 +1946,7 @@ export async function buildSubsystemGraph(
|
|
|
1912
1946
|
moduleNesting,
|
|
1913
1947
|
graphifyRelations,
|
|
1914
1948
|
orderByLine = false,
|
|
1949
|
+
layoutDirection = 'horizontal',
|
|
1915
1950
|
codeProcessKeys,
|
|
1916
1951
|
} = opts;
|
|
1917
1952
|
const frameOpts: BoundaryFrameOptions = {
|
|
@@ -2020,12 +2055,14 @@ export async function buildSubsystemGraph(
|
|
|
2020
2055
|
try {
|
|
2021
2056
|
const result = await computeElkLayout(nodes, edges, {
|
|
2022
2057
|
routingStyle: 'orthogonal',
|
|
2023
|
-
// Edge-driven graphs flow left-to-right
|
|
2024
|
-
//
|
|
2025
|
-
//
|
|
2026
|
-
//
|
|
2027
|
-
//
|
|
2028
|
-
|
|
2058
|
+
// Edge-driven graphs flow left-to-right (or top-to-bottom when the
|
|
2059
|
+
// host asks for a vertical layout). With no edges (pure containment,
|
|
2060
|
+
// e.g. the static-topology layer) a RIGHT layout stacks disconnected
|
|
2061
|
+
// top-level frames in one vertical column; DOWN makes the layered pass
|
|
2062
|
+
// place those sibling frames along the horizontal axis while each
|
|
2063
|
+
// frame's members stack vertically. There is no flow to orient there,
|
|
2064
|
+
// so the containment treatment wins in both modes.
|
|
2065
|
+
direction: elkDirectionFor(layoutDirection, edges.length > 0),
|
|
2029
2066
|
// Edge-less containment: fold the root frames into a column-major grid
|
|
2030
2067
|
// so the shape reads closer to square than one long strip.
|
|
2031
2068
|
reflowGrid: edges.length === 0,
|
|
@@ -2157,5 +2194,28 @@ export async function buildSubsystemGraph(
|
|
|
2157
2194
|
}
|
|
2158
2195
|
}
|
|
2159
2196
|
|
|
2197
|
+
// ELK can return an edge with no sections without throwing, which nothing
|
|
2198
|
+
// else reports.
|
|
2199
|
+
const unrouted = unroutedEdgeIds(edges);
|
|
2200
|
+
if (unrouted.length > 0) {
|
|
2201
|
+
console.warn(
|
|
2202
|
+
`[subsystem-graph] ${unrouted.length}/${edges.length} edge(s) have no ELK path and will not render:`,
|
|
2203
|
+
unrouted,
|
|
2204
|
+
);
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2160
2207
|
return { nodes: placedNodes, edges, regions, absoluteRects };
|
|
2161
2208
|
}
|
|
2209
|
+
|
|
2210
|
+
/**
|
|
2211
|
+
* Ids of shown edges that ELK left without a path. `SubsystemEdge` draws
|
|
2212
|
+
* `elkPath` with no fallback, so these edges are invisible on the canvas.
|
|
2213
|
+
* Hidden edges are skipped: they would not render either way.
|
|
2214
|
+
*/
|
|
2215
|
+
export function unroutedEdgeIds(
|
|
2216
|
+
edges: readonly { id: string; hidden?: boolean; data?: unknown }[],
|
|
2217
|
+
): string[] {
|
|
2218
|
+
return edges
|
|
2219
|
+
.filter((e) => !e.hidden && !(e.data as SubsystemGraphEdgeData | undefined)?.elkPath)
|
|
2220
|
+
.map((e) => e.id);
|
|
2221
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"aggregateViewFixture.d.ts","sourceRoot":"","sources":["../../../../src/stories/Subsystem/AggregateGraph/aggregateViewFixture.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAC;AAEvH,eAAO,MAAM,eAAe,EAAE,kBAAkB,EAA8n/C,CAAC;AAE/q/C,eAAO,MAAM,cAAc,EAAE,kBAAkB,EAAo88B,CAAC;AAEp/8B,eAAO,MAAM,aAAa,EAAE,YAAY,EAAutD,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"aggregateViewFixture.js","sourceRoot":"","sources":["../../../../src/stories/Subsystem/AggregateGraph/aggregateViewFixture.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,eAAe,GAAyB,IAAI,CAAC,KAAK,CAAC,6m/CAA6m/C,CAAC,CAAC;AAE/q/C,MAAM,CAAC,MAAM,cAAc,GAAyB,IAAI,CAAC,KAAK,CAAC,m78BAAm78B,CAAC,CAAC;AAEp/8B,MAAM,CAAC,MAAM,aAAa,GAAmB,IAAI,CAAC,KAAK,CAAC,ssDAAssD,CAAC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../../../../src/stories/Subsystem/ComponentGraph/fixtures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,sBAAsB,EACtB,cAAc,EACd,uBAAuB,EACxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAKjE,wBAAgB,UAAU,CACxB,IAAI,EAAE,KAAK,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,kBAAkB,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,uBAAuB,CAAC,CAAC,GAC3L,kBAAkB,EAAE,CAWtB;AAED,sEAAsE;AACtE,wBAAgB,cAAc,CAC5B,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,uBAAuB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,GACxH,cAAc,CAchB;AAED,6EAA6E;AAC7E,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,sBAAsB,CAAC,WAAW,CAAC,CAAC,CAAC,GACtF;IAAE,MAAM,CAAC,EAAE,cAAc,EAAE,CAAA;CAAE,CAe/B;AAED,eAAO,MAAM,YAAY,EAAE,uBAY1B,CAAC;AAMF,eAAO,MAAM,yBAAyB,EAAE,kBAAkB,EAqDzD,CAAC;AASF,eAAO,MAAM,qBAAqB,8BAAyB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fixtures.js","sourceRoot":"","sources":["../../../../src/stories/Subsystem/ComponentGraph/fixtures.ts"],"names":[],"mappings":"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"}
|