@principal-ai/subsystems-react 0.28.6 → 0.29.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/pierre/PierreWalkthroughCodeView.js +1 -1
- package/dist/pierre/PierreWalkthroughCodeView.js.map +1 -1
- package/dist/stories/Subsystem/ComponentGraph/fixtures.d.ts.map +1 -1
- package/dist/stories/Subsystem/ComponentGraph/fixtures.js +0 -1
- package/dist/stories/Subsystem/ComponentGraph/fixtures.js.map +1 -1
- package/dist/subsystem/ConstructsCatalog.d.ts.map +1 -1
- package/dist/subsystem/ConstructsCatalog.js +1 -0
- package/dist/subsystem/ConstructsCatalog.js.map +1 -1
- package/dist/subsystem/FileDrawer.d.ts +5 -4
- package/dist/subsystem/FileDrawer.d.ts.map +1 -1
- package/dist/subsystem/FileDrawer.js +89 -54
- package/dist/subsystem/FileDrawer.js.map +1 -1
- package/dist/subsystem/IssueList.d.ts +9 -1
- package/dist/subsystem/IssueList.d.ts.map +1 -1
- package/dist/subsystem/IssueList.js +15 -4
- package/dist/subsystem/IssueList.js.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.d.ts +8 -2
- package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.js +5 -5
- package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
- package/dist/subsystem/model.d.ts +3 -3
- package/dist/subsystem/model.d.ts.map +1 -1
- package/dist/subsystem/model.js +0 -4
- package/dist/subsystem/model.js.map +1 -1
- package/dist/subsystem/nodes.js +1 -1
- package/dist/subsystem/nodes.js.map +1 -1
- package/package.json +1 -1
- package/src/pierre/PierreWalkthroughCodeView.tsx +1 -1
- package/src/stories/Subsystem/ComponentGraph/Basics.stories.tsx +2 -2
- package/src/stories/Subsystem/ComponentGraph/Captures.stories.tsx +6 -6
- package/src/stories/Subsystem/ComponentGraph/EdgeViews.stories.tsx +7 -7
- package/src/stories/Subsystem/ComponentGraph/Issues.stories.tsx +1 -1
- package/src/stories/Subsystem/ComponentGraph/ModuleBadges.stories.tsx +5 -5
- package/src/stories/Subsystem/ComponentGraph/Modules.stories.tsx +2 -2
- package/src/stories/Subsystem/ComponentGraph/Scenarios.stories.tsx +2 -2
- package/src/stories/Subsystem/ComponentGraph/fixtures.ts +0 -1
- package/src/subsystem/ConstructsCatalog.tsx +1 -0
- package/src/subsystem/FileDrawer.tsx +130 -70
- package/src/subsystem/IssueList.tsx +28 -3
- package/src/subsystem/SubsystemComponentGraph.tsx +16 -5
- package/src/subsystem/model.test.ts +2 -2
- package/src/subsystem/model.ts +1 -6
- package/src/subsystem/nodes.tsx +1 -1
|
@@ -5,19 +5,20 @@
|
|
|
5
5
|
* children by the graph component.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { useEffect, useState } from
|
|
9
|
-
import type { ReactNode } from
|
|
10
|
-
import { useTheme } from
|
|
11
|
-
import { X } from
|
|
8
|
+
import { useEffect, useState } from "react";
|
|
9
|
+
import type { ReactNode } from "react";
|
|
10
|
+
import { useTheme } from "@principal-ade/industry-theme";
|
|
11
|
+
import { Minimize2, Maximize2, X } from "lucide-react";
|
|
12
12
|
|
|
13
13
|
/** Matches `transition: height …` below — callers that fitView after open
|
|
14
14
|
* should wait at least this long so the canvas has its reduced height. */
|
|
15
15
|
export const FILE_DRAWER_HEIGHT_MS = 200;
|
|
16
16
|
|
|
17
17
|
/** Bottom panel that slides up from the bottom of the graph area.
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* Bottom-anchored with an animated height; an in-flow spacer reserves its
|
|
19
|
+
* footprint so the canvas shrinks while open (nothing covered). Stays mounted
|
|
20
|
+
* so open/close/maximize animate. The header's maximize button (or a
|
|
21
|
+
* double-click) grows it over the whole graph column for whole-file reading. */
|
|
21
22
|
export function FileDrawer({
|
|
22
23
|
title,
|
|
23
24
|
onClose,
|
|
@@ -38,85 +39,144 @@ export function FileDrawer({
|
|
|
38
39
|
const muted = theme.colors.textMuted ?? theme.colors.textSecondary;
|
|
39
40
|
const open = title !== null;
|
|
40
41
|
const [closeHover, setCloseHover] = useState(false);
|
|
42
|
+
const [maxHover, setMaxHover] = useState(false);
|
|
43
|
+
// Full-height mode: the maximize button or double-clicking the header.
|
|
44
|
+
// The panel is always bottom-anchored with an animated height, and a spacer
|
|
45
|
+
// in normal flow reserves its footprint, so maximizing grows it upward over
|
|
46
|
+
// the still-mounted graph instead of snapping to full.
|
|
47
|
+
const [maximized, setMaximized] = useState(false);
|
|
48
|
+
// A closed drawer reopens at the default height, not maximized.
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (!open) setMaximized(false);
|
|
51
|
+
}, [open]);
|
|
52
|
+
const fullHeight = open && (maximized || fillHeight);
|
|
53
|
+
// Space the panel takes out of the canvas's flow (0 when it overlays fully).
|
|
54
|
+
const reservedHeight = fullHeight ? 0 : open ? "45%" : 0;
|
|
41
55
|
|
|
42
56
|
useEffect(() => {
|
|
43
57
|
if (!open || suppressEscape) return;
|
|
44
58
|
const onKey = (e: KeyboardEvent) => {
|
|
45
|
-
if (e.key ===
|
|
59
|
+
if (e.key === "Escape") onClose();
|
|
46
60
|
};
|
|
47
|
-
window.addEventListener(
|
|
48
|
-
return () => window.removeEventListener(
|
|
61
|
+
window.addEventListener("keydown", onKey);
|
|
62
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
49
63
|
}, [open, onClose, suppressEscape]);
|
|
50
64
|
|
|
51
65
|
return (
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
// whole graph-area container including this panel's slice.
|
|
57
|
-
zIndex: 6,
|
|
58
|
-
flexShrink: 0,
|
|
59
|
-
flex: open && fillHeight ? 1 : undefined,
|
|
60
|
-
height: open ? (fillHeight ? undefined : '45%') : 0,
|
|
61
|
-
minHeight: 0,
|
|
62
|
-
overflow: 'hidden',
|
|
63
|
-
display: 'flex',
|
|
64
|
-
flexDirection: 'column',
|
|
65
|
-
background: theme.colors.background,
|
|
66
|
-
borderTop: open ? `1px solid ${theme.colors.border}` : 'none',
|
|
67
|
-
transition: `height ${FILE_DRAWER_HEIGHT_MS}ms ease`,
|
|
68
|
-
}}
|
|
69
|
-
>
|
|
66
|
+
<>
|
|
67
|
+
{/* Reserves the drawer's footprint in the column so the canvas shrinks
|
|
68
|
+
while the drawer is open. Animates with the panel, so the boundary
|
|
69
|
+
between graph and code slides smoothly. */}
|
|
70
70
|
<div
|
|
71
|
+
aria-hidden="true"
|
|
71
72
|
style={{
|
|
72
|
-
display: 'flex',
|
|
73
|
-
alignItems: 'center',
|
|
74
|
-
gap: 8,
|
|
75
|
-
padding: '6px 10px',
|
|
76
|
-
borderBottom: `1px solid ${theme.colors.border}`,
|
|
77
73
|
flexShrink: 0,
|
|
74
|
+
height: reservedHeight,
|
|
75
|
+
transition: `height ${FILE_DRAWER_HEIGHT_MS}ms ease`,
|
|
76
|
+
}}
|
|
77
|
+
/>
|
|
78
|
+
{/* The panel — bottom-anchored and grown via `height`, so maximize/fill
|
|
79
|
+
reads as dragging up over the diagram. */}
|
|
80
|
+
<div
|
|
81
|
+
style={{
|
|
82
|
+
position: "absolute",
|
|
83
|
+
left: 0,
|
|
84
|
+
right: 0,
|
|
85
|
+
bottom: 0,
|
|
86
|
+
// Above the edge-label overlay (zIndex 5), React Flow, and overlays
|
|
87
|
+
// (zIndex 6–8); 30 lifts it over the whole graph area when maximized.
|
|
88
|
+
zIndex: maximized ? 30 : 6,
|
|
89
|
+
height: open ? (fullHeight ? "100%" : "45%") : 0,
|
|
90
|
+
minHeight: 0,
|
|
91
|
+
overflow: "hidden",
|
|
92
|
+
display: "flex",
|
|
93
|
+
flexDirection: "column",
|
|
94
|
+
background: theme.colors.background,
|
|
95
|
+
borderTop: open ? `1px solid ${theme.colors.border}` : "none",
|
|
96
|
+
transition: `height ${FILE_DRAWER_HEIGHT_MS}ms ease`,
|
|
78
97
|
}}
|
|
79
98
|
>
|
|
80
|
-
<
|
|
81
|
-
|
|
82
|
-
style={{
|
|
83
|
-
flex: 1,
|
|
84
|
-
minWidth: 0,
|
|
85
|
-
overflow: 'hidden',
|
|
86
|
-
textOverflow: 'ellipsis',
|
|
87
|
-
whiteSpace: 'nowrap',
|
|
88
|
-
fontFamily: theme.fonts.monospace,
|
|
89
|
-
fontSize: theme.fontSizes[0],
|
|
90
|
-
color: muted,
|
|
91
|
-
}}
|
|
92
|
-
>
|
|
93
|
-
{title}
|
|
94
|
-
</span>
|
|
95
|
-
<button
|
|
96
|
-
type="button"
|
|
97
|
-
onClick={onClose}
|
|
98
|
-
onMouseEnter={() => setCloseHover(true)}
|
|
99
|
-
onMouseLeave={() => setCloseHover(false)}
|
|
100
|
-
aria-label="Close file"
|
|
99
|
+
<div
|
|
100
|
+
onDoubleClick={() => setMaximized((v) => !v)}
|
|
101
101
|
style={{
|
|
102
|
-
display:
|
|
103
|
-
alignItems:
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
background: closeHover ? theme.colors.border : 'transparent',
|
|
111
|
-
color: closeHover ? theme.colors.text : muted,
|
|
112
|
-
cursor: 'pointer',
|
|
113
|
-
transition: 'background 120ms ease, color 120ms ease',
|
|
102
|
+
display: "flex",
|
|
103
|
+
alignItems: "center",
|
|
104
|
+
gap: 8,
|
|
105
|
+
padding: "6px 10px",
|
|
106
|
+
borderBottom: `1px solid ${theme.colors.border}`,
|
|
107
|
+
flexShrink: 0,
|
|
108
|
+
cursor: "default",
|
|
109
|
+
userSelect: "none",
|
|
114
110
|
}}
|
|
115
111
|
>
|
|
116
|
-
<
|
|
117
|
-
|
|
112
|
+
<span
|
|
113
|
+
title={title ?? undefined}
|
|
114
|
+
style={{
|
|
115
|
+
flex: 1,
|
|
116
|
+
minWidth: 0,
|
|
117
|
+
overflow: "hidden",
|
|
118
|
+
textOverflow: "ellipsis",
|
|
119
|
+
whiteSpace: "nowrap",
|
|
120
|
+
fontFamily: theme.fonts.monospace,
|
|
121
|
+
fontSize: theme.fontSizes[0],
|
|
122
|
+
color: muted,
|
|
123
|
+
}}
|
|
124
|
+
>
|
|
125
|
+
{title}
|
|
126
|
+
</span>
|
|
127
|
+
<button
|
|
128
|
+
type="button"
|
|
129
|
+
onClick={() => setMaximized((v) => !v)}
|
|
130
|
+
onMouseEnter={() => setMaxHover(true)}
|
|
131
|
+
onMouseLeave={() => setMaxHover(false)}
|
|
132
|
+
aria-label={maximized ? "Minimize code view" : "Maximize code view"}
|
|
133
|
+
aria-pressed={maximized}
|
|
134
|
+
title={maximized ? "Minimize code view" : "Maximize code view"}
|
|
135
|
+
style={{
|
|
136
|
+
display: "inline-flex",
|
|
137
|
+
alignItems: "center",
|
|
138
|
+
justifyContent: "center",
|
|
139
|
+
width: 22,
|
|
140
|
+
height: 22,
|
|
141
|
+
padding: 0,
|
|
142
|
+
border: "none",
|
|
143
|
+
borderRadius: 4,
|
|
144
|
+
background: maxHover ? theme.colors.border : "transparent",
|
|
145
|
+
color: maxHover ? theme.colors.text : muted,
|
|
146
|
+
cursor: "pointer",
|
|
147
|
+
transition: "background 120ms ease, color 120ms ease",
|
|
148
|
+
}}
|
|
149
|
+
>
|
|
150
|
+
{maximized ? <Minimize2 size={14} /> : <Maximize2 size={14} />}
|
|
151
|
+
</button>
|
|
152
|
+
<button
|
|
153
|
+
type="button"
|
|
154
|
+
onClick={onClose}
|
|
155
|
+
onMouseEnter={() => setCloseHover(true)}
|
|
156
|
+
onMouseLeave={() => setCloseHover(false)}
|
|
157
|
+
aria-label="Close file"
|
|
158
|
+
style={{
|
|
159
|
+
display: "inline-flex",
|
|
160
|
+
alignItems: "center",
|
|
161
|
+
justifyContent: "center",
|
|
162
|
+
width: 22,
|
|
163
|
+
height: 22,
|
|
164
|
+
padding: 0,
|
|
165
|
+
border: "none",
|
|
166
|
+
borderRadius: 4,
|
|
167
|
+
background: closeHover ? theme.colors.border : "transparent",
|
|
168
|
+
color: closeHover ? theme.colors.text : muted,
|
|
169
|
+
cursor: "pointer",
|
|
170
|
+
transition: "background 120ms ease, color 120ms ease",
|
|
171
|
+
}}
|
|
172
|
+
>
|
|
173
|
+
<X size={14} />
|
|
174
|
+
</button>
|
|
175
|
+
</div>
|
|
176
|
+
<div style={{ flex: 1, minHeight: 0, overflow: "auto" }}>
|
|
177
|
+
{children}
|
|
178
|
+
</div>
|
|
118
179
|
</div>
|
|
119
|
-
|
|
120
|
-
</div>
|
|
180
|
+
</>
|
|
121
181
|
);
|
|
122
182
|
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* (e.g. focus the node on the graph, or apply the fix in the report).
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { useState } from 'react';
|
|
14
|
+
import { useEffect, useState } from 'react';
|
|
15
15
|
import type { LucideIcon } from 'lucide-react';
|
|
16
16
|
import {
|
|
17
17
|
ChevronDown,
|
|
@@ -499,6 +499,14 @@ export interface SubsystemIssueListProps {
|
|
|
499
499
|
onSelectIssue?: (issue: SubsystemIssue) => void;
|
|
500
500
|
onApplyFix?: (issue: SubsystemIssue) => void;
|
|
501
501
|
onHoverIssue?: (issue: SubsystemIssue | null) => void;
|
|
502
|
+
/**
|
|
503
|
+
* Land the list focused on one verification layer: that category starts
|
|
504
|
+
* expanded and every other category starts collapsed. Used when the list is
|
|
505
|
+
* opened from a lane-specific entry point (e.g. the Maintainer tab's lane
|
|
506
|
+
* badges) so the caller's intent — "show me this layer's findings" — is
|
|
507
|
+
* honoured without an extra click.
|
|
508
|
+
*/
|
|
509
|
+
focusCategory?: SubsystemIssueCategory;
|
|
502
510
|
}
|
|
503
511
|
|
|
504
512
|
export function SubsystemIssueList({
|
|
@@ -506,15 +514,32 @@ export function SubsystemIssueList({
|
|
|
506
514
|
onSelectIssue,
|
|
507
515
|
onApplyFix,
|
|
508
516
|
onHoverIssue,
|
|
517
|
+
focusCategory,
|
|
509
518
|
}: SubsystemIssueListProps) {
|
|
510
519
|
const { theme } = useTheme();
|
|
511
520
|
const muted = theme.colors.textMuted ?? theme.colors.textSecondary;
|
|
512
521
|
const categories = groupIssuesByCategory(issues);
|
|
513
|
-
// Collapsed categories (by id). Everything starts expanded
|
|
522
|
+
// Collapsed categories (by id). Everything starts expanded, unless the list
|
|
523
|
+
// was opened focused on a single layer — then every other layer starts
|
|
524
|
+
// collapsed so the focused category's findings are what you land on.
|
|
514
525
|
const [collapsed, setCollapsed] = useState<Set<SubsystemIssueCategory>>(
|
|
515
|
-
() =>
|
|
526
|
+
() =>
|
|
527
|
+
focusCategory
|
|
528
|
+
? new Set(
|
|
529
|
+
SUBSYSTEM_ISSUE_CATEGORIES.filter((c) => c !== focusCategory),
|
|
530
|
+
)
|
|
531
|
+
: new Set(),
|
|
516
532
|
);
|
|
517
533
|
const [hovered, setHovered] = useState<SubsystemIssueCategory | null>(null);
|
|
534
|
+
// Re-apply the focus when it changes on an already-mounted list (e.g. the
|
|
535
|
+
// host switches which lane the open model is focused on).
|
|
536
|
+
useEffect(() => {
|
|
537
|
+
setCollapsed(
|
|
538
|
+
focusCategory
|
|
539
|
+
? new Set(SUBSYSTEM_ISSUE_CATEGORIES.filter((c) => c !== focusCategory))
|
|
540
|
+
: new Set(),
|
|
541
|
+
);
|
|
542
|
+
}, [focusCategory]);
|
|
518
543
|
const toggleCollapsed = (category: SubsystemIssueCategory) => {
|
|
519
544
|
setCollapsed((prev) => {
|
|
520
545
|
const next = new Set(prev);
|
|
@@ -55,7 +55,11 @@ import { ConstructsCatalog } from './ConstructsCatalog';
|
|
|
55
55
|
import type { SubsystemOpenFileOptions } from './declarationRef';
|
|
56
56
|
import { SubsystemComponentNode, SubsystemGroupNode, SubsystemEdge, SUBSYSTEM_CALLBACKS, hexWithAlpha, EDGE_DIM_ALPHA, fileMatchForNode, flowElementVisibility } from './nodes';
|
|
57
57
|
import { SubsystemDiagnosticToggle, type SubsystemDiagnostic } from './DiagnosticToggle';
|
|
58
|
-
import {
|
|
58
|
+
import {
|
|
59
|
+
SubsystemIssueList,
|
|
60
|
+
type SubsystemIssue,
|
|
61
|
+
type SubsystemIssueCategory,
|
|
62
|
+
} from './IssueList';
|
|
59
63
|
import { SubsystemFileTree } from './SubsystemFileTree';
|
|
60
64
|
import { GraphLayoutCover } from './GraphLayoutCover';
|
|
61
65
|
import { GRAPH_NAV_PROPS, GraphChrome } from './graphChrome';
|
|
@@ -172,7 +176,7 @@ export interface SubsystemComponentGraphProps {
|
|
|
172
176
|
* Which edge vocabulary the canvas draws. The relation and walkthrough
|
|
173
177
|
* vocabularies are disjoint, so a graph carrying both shows one or the
|
|
174
178
|
* other — never both. Edges outside the view are hidden (labels go too).
|
|
175
|
-
* - `relations`: topology relation edges (`
|
|
179
|
+
* - `relations`: topology relation edges (`extends`, `implements`, …)
|
|
176
180
|
* - `walkthroughs`: walkthrough hop edges (`calls`, `feeds`, …), including
|
|
177
181
|
* step numbers when a flow is focused/hovered
|
|
178
182
|
* Leave unset to let the sidebar's Files / Walkthroughs tab drive it: Files
|
|
@@ -246,6 +250,12 @@ export interface SubsystemComponentGraphProps {
|
|
|
246
250
|
* diagnostics on iff `issues` is non-empty, and the title-row chip toggles it.
|
|
247
251
|
*/
|
|
248
252
|
showIssues?: boolean;
|
|
253
|
+
/**
|
|
254
|
+
* When the issues list is active, land it focused on this verification
|
|
255
|
+
* layer: that category expands, the rest start collapsed. Optional — the
|
|
256
|
+
* plain issues view expands everything.
|
|
257
|
+
*/
|
|
258
|
+
focusIssueCategory?: SubsystemIssueCategory;
|
|
249
259
|
/** Click an issue — focus its target on the graph / open detail. */
|
|
250
260
|
onSelectIssue?: (issue: SubsystemIssue) => void;
|
|
251
261
|
/** Apply an issue's deterministic fix. */
|
|
@@ -375,7 +385,7 @@ interface InnerProps extends SubsystemComponentGraphProps {
|
|
|
375
385
|
measured: { w: number; h: number } | null;
|
|
376
386
|
}
|
|
377
387
|
|
|
378
|
-
function Inner({ components, relations, walkthroughs, initialWalkthroughId, onReorderWalkthroughs, onSelect, onEdgeSelect, measured: _measured, maxNodeWidth, showEdgeLabels, edgeView, title, hideSidebar, walkthroughStepMode = 'focus', autoPlayWalkthroughs = false, walkthroughAutoPlayIntervalMs = WALKTHROUGH_PLAY_PAUSE_MS, zoomOnWalkthroughFocus = true, graphTitle, showWalkthroughTitle = false, description, canvasOverlay, sidebarExtra, sidebarAfterDescription, diagnostic, issues, showIssues, onSelectIssue, onApplyIssueFix, onHoverIssue, renderFileView, renderFileViewer, renderWalkthroughViewer, onFileSelect, onVerifyComponent, componentVerification, persistKey }: InnerProps) {
|
|
388
|
+
function Inner({ components, relations, walkthroughs, initialWalkthroughId, onReorderWalkthroughs, onSelect, onEdgeSelect, measured: _measured, maxNodeWidth, showEdgeLabels, edgeView, title, hideSidebar, walkthroughStepMode = 'focus', autoPlayWalkthroughs = false, walkthroughAutoPlayIntervalMs = WALKTHROUGH_PLAY_PAUSE_MS, zoomOnWalkthroughFocus = true, graphTitle, showWalkthroughTitle = false, description, canvasOverlay, sidebarExtra, sidebarAfterDescription, diagnostic, issues, showIssues, focusIssueCategory, onSelectIssue, onApplyIssueFix, onHoverIssue, renderFileView, renderFileViewer, renderWalkthroughViewer, onFileSelect, onVerifyComponent, componentVerification, persistKey }: InnerProps) {
|
|
379
389
|
const { theme } = useTheme();
|
|
380
390
|
const { fitView } = useReactFlow();
|
|
381
391
|
const viewport = useViewport();
|
|
@@ -972,7 +982,7 @@ function Inner({ components, relations, walkthroughs, initialWalkthroughId, onRe
|
|
|
972
982
|
};
|
|
973
983
|
// Edges outside the selected view are hidden entirely (labels included).
|
|
974
984
|
const edgeInView = (e: Edge): boolean => {
|
|
975
|
-
const mechanism = (e.data as { mechanism?: string } | undefined)?.mechanism ?? '
|
|
985
|
+
const mechanism = (e.data as { mechanism?: string } | undefined)?.mechanism ?? 'uses';
|
|
976
986
|
return resolvedEdgeView === 'relations'
|
|
977
987
|
? isRelationMechanism(mechanism)
|
|
978
988
|
: isWalkthroughMechanism(mechanism);
|
|
@@ -1560,7 +1570,7 @@ function Inner({ components, relations, walkthroughs, initialWalkthroughId, onRe
|
|
|
1560
1570
|
} | undefined;
|
|
1561
1571
|
return {
|
|
1562
1572
|
id: e.id,
|
|
1563
|
-
mechanism: d?.mechanism ?? '
|
|
1573
|
+
mechanism: d?.mechanism ?? 'uses',
|
|
1564
1574
|
dimmed: d?.dimmed === true,
|
|
1565
1575
|
midX: d?.labelX ?? 0,
|
|
1566
1576
|
midY: d?.labelY ?? 0,
|
|
@@ -1738,6 +1748,7 @@ function Inner({ components, relations, walkthroughs, initialWalkthroughId, onRe
|
|
|
1738
1748
|
{issuesActive ? (
|
|
1739
1749
|
<SubsystemIssueList
|
|
1740
1750
|
issues={issues ?? []}
|
|
1751
|
+
focusCategory={focusIssueCategory}
|
|
1741
1752
|
onSelectIssue={onSelectIssue}
|
|
1742
1753
|
onApplyFix={onApplyIssueFix}
|
|
1743
1754
|
onHoverIssue={onHoverIssue}
|
|
@@ -40,9 +40,9 @@ const comps: SubsystemComponent[] = [
|
|
|
40
40
|
];
|
|
41
41
|
|
|
42
42
|
const relations = [
|
|
43
|
-
{ id: 'e1', from: 'transcript', to: 'reader', relationType: '
|
|
43
|
+
{ id: 'e1', from: 'transcript', to: 'reader', relationType: 'references' as const },
|
|
44
44
|
// 'host' is NOT a component — this is the cross-package external case.
|
|
45
|
-
{ id: 'e2', from: 'reader', to: 'host', relationType: '
|
|
45
|
+
{ id: 'e2', from: 'reader', to: 'host', relationType: 'references' as const, refs: ['bun/index.ts'] },
|
|
46
46
|
];
|
|
47
47
|
|
|
48
48
|
const doc = { components: comps, relations };
|
package/src/subsystem/model.ts
CHANGED
|
@@ -91,7 +91,6 @@ export interface SubsystemDeclToken {
|
|
|
91
91
|
* Belongs on `relations[]`, not on walkthrough hops.
|
|
92
92
|
*/
|
|
93
93
|
export type SubsystemRelationType =
|
|
94
|
-
| 'imports'
|
|
95
94
|
| 'extends'
|
|
96
95
|
| 'inherits'
|
|
97
96
|
| 'implements'
|
|
@@ -120,7 +119,7 @@ export type SubsystemEdgeMechanism =
|
|
|
120
119
|
/**
|
|
121
120
|
* Which edge vocabulary the canvas draws. The two vocabularies are disjoint;
|
|
122
121
|
* a view shows only edges (and their labels) from the selected source.
|
|
123
|
-
* - `relations`: only topology relation edges (`
|
|
122
|
+
* - `relations`: only topology relation edges (`extends`, `implements`, …)
|
|
124
123
|
* - `walkthroughs`: only walkthrough hop edges (`calls`, `feeds`, …)
|
|
125
124
|
*/
|
|
126
125
|
export type SubsystemEdgeView = 'relations' | 'walkthroughs';
|
|
@@ -945,7 +944,6 @@ export type SubsystemGraphEdge = Edge<SubsystemGraphEdgeData>;
|
|
|
945
944
|
* source (the two unions are disjoint).
|
|
946
945
|
*/
|
|
947
946
|
export const SUBSYSTEM_RELATION_TYPES = [
|
|
948
|
-
'imports',
|
|
949
947
|
'extends',
|
|
950
948
|
'inherits',
|
|
951
949
|
'implements',
|
|
@@ -986,7 +984,6 @@ export function isWalkthroughMechanism(
|
|
|
986
984
|
}
|
|
987
985
|
|
|
988
986
|
export const MECHANISM_COLOR: Record<SubsystemEdgeMechanism, string> = {
|
|
989
|
-
imports: '#0893d2', // blue
|
|
990
987
|
calls: '#4ec9b0', // teal
|
|
991
988
|
extends: '#b48ead', // purple
|
|
992
989
|
inherits: '#9b6fd0', // purple
|
|
@@ -1004,7 +1001,6 @@ export const MECHANISM_COLOR: Record<SubsystemEdgeMechanism, string> = {
|
|
|
1004
1001
|
};
|
|
1005
1002
|
|
|
1006
1003
|
export const MECHANISM_STYLE: Record<SubsystemEdgeMechanism, 'solid' | 'dashed' | 'dotted'> = {
|
|
1007
|
-
imports: 'solid',
|
|
1008
1004
|
calls: 'solid',
|
|
1009
1005
|
extends: 'dashed',
|
|
1010
1006
|
inherits: 'dashed',
|
|
@@ -1024,7 +1020,6 @@ export const MECHANISM_STYLE: Record<SubsystemEdgeMechanism, 'solid' | 'dashed'
|
|
|
1024
1020
|
/** Mechanism → [description, verifiable-with-graphify]. Drives the "not
|
|
1025
1021
|
* directly verifiable" styling of edge labels. */
|
|
1026
1022
|
export const MECHANISM_DESCRIPTIONS: [SubsystemEdgeMechanism, string, boolean][] = [
|
|
1027
|
-
['imports', 'import statement (code-level dependency)', true],
|
|
1028
1023
|
['calls', 'function/method call (call graph edge)', true],
|
|
1029
1024
|
['extends', 'class inheritance', true],
|
|
1030
1025
|
['inherits', 'class inheritance', true],
|
package/src/subsystem/nodes.tsx
CHANGED
|
@@ -539,7 +539,7 @@ export function SubsystemEdge({
|
|
|
539
539
|
markerEnd,
|
|
540
540
|
}: EdgeProps<SubsystemGraphEdge>) {
|
|
541
541
|
const path = data?.elkPath ?? '';
|
|
542
|
-
const mechanism = data?.mechanism ?? '
|
|
542
|
+
const mechanism = data?.mechanism ?? 'uses';
|
|
543
543
|
const color = MECHANISM_COLOR[mechanism] ?? '#888';
|
|
544
544
|
// Dash style comes from the mechanism table (dashed = inverted-control or
|
|
545
545
|
// observational relationships: hierarchy, registration, watches).
|