@principal-ai/subsystems-react 0.20.6 → 0.21.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/README.md +1 -1
- package/dist/graphify/anchor.d.ts +17 -0
- package/dist/graphify/anchor.d.ts.map +1 -1
- package/dist/graphify/anchor.js +41 -0
- package/dist/graphify/anchor.js.map +1 -1
- package/dist/graphify/consolidated.d.ts +40 -1
- package/dist/graphify/consolidated.d.ts.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/pierre/PierreWalkthroughCodeView.d.ts +22 -0
- package/dist/pierre/PierreWalkthroughCodeView.d.ts.map +1 -0
- package/dist/pierre/PierreWalkthroughCodeView.js +198 -0
- package/dist/pierre/PierreWalkthroughCodeView.js.map +1 -0
- package/dist/pierre/index.d.ts +2 -2
- package/dist/pierre/index.js +1 -1
- package/dist/pierre/pierreFileLang.d.ts +1 -1
- package/dist/pierre/pierreFileLang.js +1 -1
- package/dist/stories/Subsystem/ComponentGraph/fixtures.d.ts +14 -2
- package/dist/stories/Subsystem/ComponentGraph/fixtures.d.ts.map +1 -1
- package/dist/stories/Subsystem/ComponentGraph/fixtures.js +86 -20
- package/dist/stories/Subsystem/ComponentGraph/fixtures.js.map +1 -1
- package/dist/subsystem/FileDrawer.d.ts +2 -2
- package/dist/subsystem/FileDrawer.js +2 -2
- package/dist/subsystem/SubsystemComponentGraph.d.ts +25 -24
- package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.js +204 -199
- package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
- package/dist/subsystem/formatDeclaration.d.ts +2 -1
- package/dist/subsystem/formatDeclaration.d.ts.map +1 -1
- package/dist/subsystem/formatDeclaration.js +57 -33
- package/dist/subsystem/formatDeclaration.js.map +1 -1
- package/dist/subsystem/model.d.ts +85 -35
- package/dist/subsystem/model.d.ts.map +1 -1
- package/dist/subsystem/model.js +85 -6
- package/dist/subsystem/model.js.map +1 -1
- package/dist/subsystem/nodes.d.ts.map +1 -1
- package/dist/subsystem/nodes.js +30 -13
- package/dist/subsystem/nodes.js.map +1 -1
- package/dist/subsystem/tokenizeComponent.js +2 -2
- package/dist/subsystem/tokenizeComponent.js.map +1 -1
- package/package.json +3 -3
- package/src/graphify/anchor.test.ts +31 -2
- package/src/graphify/anchor.ts +46 -0
- package/src/graphify/consolidated.ts +43 -1
- package/src/index.ts +13 -5
- package/src/pierre/{PierreThroughlineCodeView.tsx → PierreWalkthroughCodeView.tsx} +39 -47
- package/src/pierre/index.ts +2 -2
- package/src/pierre/pierreFileLang.ts +1 -1
- package/src/stories/Pierre/CodeView.stories.tsx +3 -3
- package/src/stories/Subsystem/ComponentDeclarationAudit.stories.tsx +14 -14
- package/src/stories/Subsystem/ComponentGraph/Appearance.stories.tsx +13 -15
- package/src/stories/Subsystem/ComponentGraph/Basics.stories.tsx +8 -8
- package/src/stories/Subsystem/ComponentGraph/Captures.stories.tsx +14 -14
- package/src/stories/Subsystem/ComponentGraph/CustomEntities.stories.tsx +11 -10
- package/src/stories/Subsystem/ComponentGraph/DetailPanel.stories.tsx +13 -14
- package/src/stories/Subsystem/ComponentGraph/Flows.stories.tsx +31 -66
- package/src/stories/Subsystem/ComponentGraph/FrameworkStereotype.stories.tsx +2 -2
- package/src/stories/Subsystem/ComponentGraph/GraphTitle.stories.tsx +4 -4
- package/src/stories/Subsystem/ComponentGraph/Processes.stories.tsx +3 -3
- package/src/stories/Subsystem/ComponentGraph/Proposed.stories.tsx +1 -1
- package/src/stories/Subsystem/ComponentGraph/Scenarios.stories.tsx +518 -33
- package/src/stories/Subsystem/ComponentGraph/Spotlights.stories.tsx +13 -13
- package/src/stories/Subsystem/ComponentGraph/fixtures.ts +105 -21
- package/src/subsystem/FileDrawer.tsx +2 -2
- package/src/subsystem/SubsystemComponentGraph.tsx +268 -259
- package/src/subsystem/formatDeclaration.test.ts +76 -1
- package/src/subsystem/formatDeclaration.ts +57 -33
- package/src/subsystem/model.test.ts +27 -15
- package/src/subsystem/model.ts +175 -45
- package/src/subsystem/nodes.tsx +47 -13
- package/src/subsystem/tokenizeComponent.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@principal-ai/subsystems-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "React UI for Subsystem Models — graph, Pierre code views, and session-event feeds",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@pierre/diffs": ">=1.1.20",
|
|
39
39
|
"@principal-ade/industry-theme": "^0.1.7",
|
|
40
|
-
"@principal-ai/subsystems-core": ">=0.
|
|
40
|
+
"@principal-ai/subsystems-core": ">=0.32.0",
|
|
41
41
|
"react": "^18.0.0 || ^19.0.0",
|
|
42
42
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
43
43
|
},
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"@pierre/diffs": "1.3.6",
|
|
46
46
|
"@principal-ade/industry-theme": "0.1.7",
|
|
47
47
|
"@principal-ai/agent-monitoring": "^0.3.38",
|
|
48
|
-
"@principal-ai/subsystems-core": "0.
|
|
48
|
+
"@principal-ai/subsystems-core": "0.32.0",
|
|
49
49
|
"@storybook/addon-docs": "10.1.2",
|
|
50
50
|
"@storybook/addon-links": "10.1.2",
|
|
51
51
|
"@storybook/addon-onboarding": "10.1.2",
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
makeGraphifyId,
|
|
6
6
|
normalizeGraphifyId,
|
|
7
7
|
} from './ids';
|
|
8
|
-
import { resolveComponentAnchor, symbolLabelVariants } from './anchor';
|
|
8
|
+
import { resolveComponentAnchor, symbolLabelVariants, findUniqueDefinitionBySymbol } from './anchor';
|
|
9
9
|
|
|
10
10
|
function node(
|
|
11
11
|
id: string,
|
|
@@ -31,7 +31,7 @@ describe('makeGraphifyId', () => {
|
|
|
31
31
|
'packages/subsystems-studio/src/mainview/views/SubsystemModelView',
|
|
32
32
|
);
|
|
33
33
|
expect(makeGraphifyId(stem, 'SubsystemModelView')).toBe(
|
|
34
|
-
'
|
|
34
|
+
'packages_subsystems_studio_src_mainview_views_subsystemmodelview_subsystemmodelview',
|
|
35
35
|
);
|
|
36
36
|
});
|
|
37
37
|
|
|
@@ -122,3 +122,32 @@ describe('resolveComponentAnchor', () => {
|
|
|
122
122
|
expect(r.resolution).toBe('missing');
|
|
123
123
|
});
|
|
124
124
|
});
|
|
125
|
+
|
|
126
|
+
describe('findUniqueDefinitionBySymbol', () => {
|
|
127
|
+
test('unique when one defining file', () => {
|
|
128
|
+
const nodes = [
|
|
129
|
+
node('a', 'HostInfo', 'src/a.ts', 'L1'),
|
|
130
|
+
node('b', 'Other', 'src/b.ts', 'L1'),
|
|
131
|
+
];
|
|
132
|
+
const r = findUniqueDefinitionBySymbol(nodes, 'HostInfo');
|
|
133
|
+
expect(r.status).toBe('unique');
|
|
134
|
+
if (r.status === 'unique') expect(r.node.source_file).toBe('src/a.ts');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test('ambiguous when same symbol in two files', () => {
|
|
138
|
+
const nodes = [
|
|
139
|
+
node('a', 'Foo()', 'src/a.ts', 'L1'),
|
|
140
|
+
node('b', 'Foo()', 'src/b.ts', 'L1'),
|
|
141
|
+
];
|
|
142
|
+
const r = findUniqueDefinitionBySymbol(nodes, 'Foo');
|
|
143
|
+
expect(r.status).toBe('ambiguous');
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test('none when symbol absent', () => {
|
|
147
|
+
const r = findUniqueDefinitionBySymbol(
|
|
148
|
+
[node('a', 'Bar', 'src/a.ts')],
|
|
149
|
+
'Foo',
|
|
150
|
+
);
|
|
151
|
+
expect(r.status).toBe('none');
|
|
152
|
+
});
|
|
153
|
+
});
|
package/src/graphify/anchor.ts
CHANGED
|
@@ -158,3 +158,49 @@ export function resolveComponentAnchor(
|
|
|
158
158
|
triedIds,
|
|
159
159
|
};
|
|
160
160
|
}
|
|
161
|
+
|
|
162
|
+
export type UniqueDefinitionBySymbolResult =
|
|
163
|
+
| { status: 'unique'; node: GraphifyNode }
|
|
164
|
+
| { status: 'ambiguous'; candidates: GraphifyNode[] }
|
|
165
|
+
| { status: 'none' };
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Corpus-wide definition lookup by symbol label only (no claimed file).
|
|
169
|
+
* Used when the model’s file path is missing and we want a deterministic
|
|
170
|
+
* relocate if exactly one defining file still declares the symbol.
|
|
171
|
+
*
|
|
172
|
+
* Unique means a single `source_file` among matching definition nodes.
|
|
173
|
+
*/
|
|
174
|
+
export function findUniqueDefinitionBySymbol(
|
|
175
|
+
nodes: readonly GraphifyNode[],
|
|
176
|
+
symbol: string,
|
|
177
|
+
): UniqueDefinitionBySymbolResult {
|
|
178
|
+
const trimmed = symbol.trim();
|
|
179
|
+
if (!trimmed) return { status: 'none' };
|
|
180
|
+
|
|
181
|
+
const hits: GraphifyNode[] = [];
|
|
182
|
+
for (const node of nodes) {
|
|
183
|
+
if (!isDefinition(node)) continue;
|
|
184
|
+
if (!labelMatchesSymbol(String(node.label ?? ''), trimmed)) continue;
|
|
185
|
+
hits.push(node);
|
|
186
|
+
}
|
|
187
|
+
if (hits.length === 0) return { status: 'none' };
|
|
188
|
+
|
|
189
|
+
const byFile = new Map<string, GraphifyNode[]>();
|
|
190
|
+
for (const n of hits) {
|
|
191
|
+
const sf = normalizeSourcePath(String(n.source_file ?? ''));
|
|
192
|
+
if (!sf) continue;
|
|
193
|
+
const list = byFile.get(sf) ?? [];
|
|
194
|
+
list.push(n);
|
|
195
|
+
byFile.set(sf, list);
|
|
196
|
+
}
|
|
197
|
+
if (byFile.size === 0) return { status: 'none' };
|
|
198
|
+
if (byFile.size > 1) {
|
|
199
|
+
return {
|
|
200
|
+
status: 'ambiguous',
|
|
201
|
+
candidates: hits.slice(0, 12),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
const only = [...byFile.values()][0]!;
|
|
205
|
+
return { status: 'unique', node: only[0]! };
|
|
206
|
+
}
|
|
@@ -161,6 +161,25 @@ export interface GraphifyFunctionDetail {
|
|
|
161
161
|
callees: GraphifyCallInfo[];
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
/** One generic type parameter, e.g. `T` or `K extends keyof StudioMessages`. */
|
|
165
|
+
export interface GraphifyTypeParamInfo {
|
|
166
|
+
name: string;
|
|
167
|
+
/** Constraint written after `extends` (or language equivalent). */
|
|
168
|
+
constraint?: string;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** A callable/function type: `(params) => returnType`. */
|
|
172
|
+
export interface GraphifyCallableTypeInfo {
|
|
173
|
+
parameters?: GraphifyParamInfo[];
|
|
174
|
+
returnType?: string;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** One enum member, with its optional literal value. */
|
|
178
|
+
export interface GraphifyEnumMemberInfo {
|
|
179
|
+
name: string;
|
|
180
|
+
value?: string;
|
|
181
|
+
}
|
|
182
|
+
|
|
164
183
|
/** A type-like node (interface/struct): revealed by incoming `implements` edges. */
|
|
165
184
|
export interface GraphifyTypeDetail {
|
|
166
185
|
kind: 'type';
|
|
@@ -170,6 +189,22 @@ export interface GraphifyTypeDetail {
|
|
|
170
189
|
usedBy: GraphifyReferenceInfo[];
|
|
171
190
|
/** Nodes that `inherits`/`implements` this type. */
|
|
172
191
|
implementors: string[];
|
|
192
|
+
/** Generic type parameters, e.g. `<K extends keyof StudioMessages>`. */
|
|
193
|
+
generics?: GraphifyTypeParamInfo[];
|
|
194
|
+
/** Callable type — `(params) => returnType` (TS/Go/Rust fn, Java lambda). */
|
|
195
|
+
signature?: GraphifyCallableTypeInfo;
|
|
196
|
+
/** Enum members (name, optional literal value). */
|
|
197
|
+
enumMembers?: GraphifyEnumMemberInfo[];
|
|
198
|
+
/** Alias whose RHS is a plain reference, e.g. `StudioMessages[K]`. */
|
|
199
|
+
aliasOf?: string;
|
|
200
|
+
/** Simple union of alternatives, e.g. `'started' | 'stopped'`. */
|
|
201
|
+
unionOf?: string[];
|
|
202
|
+
/**
|
|
203
|
+
* Verbatim RHS — the escape hatch for shapes the structured fields can't
|
|
204
|
+
* express (mapped/conditional/template-literal/infer types, `typeof`/utility
|
|
205
|
+
* compositions, C pointers, Rust trait objects). Wins over every shape above.
|
|
206
|
+
*/
|
|
207
|
+
rhs?: string;
|
|
173
208
|
}
|
|
174
209
|
|
|
175
210
|
/** A module/file-like node: a `contains` target with a filename label. */
|
|
@@ -221,6 +256,13 @@ export interface GraphifyStoreDetail {
|
|
|
221
256
|
kind: 'store';
|
|
222
257
|
/** The retained state members (module-level consts or class fields). */
|
|
223
258
|
properties: GraphifyPropertyInfo[];
|
|
259
|
+
/**
|
|
260
|
+
* Where the retained state physically lives / who mediates access. Authored
|
|
261
|
+
* — graphify cannot infer it: `memory` = process-lifetime RAM, `disk` =
|
|
262
|
+
* this process reads/writes the filesystem, `external` = mediated by another
|
|
263
|
+
* system (db/service; carries no `process`).
|
|
264
|
+
*/
|
|
265
|
+
storage?: 'memory' | 'disk' | 'external';
|
|
224
266
|
}
|
|
225
267
|
|
|
226
268
|
/** A standalone method selected from its owning class. */
|
|
@@ -235,7 +277,7 @@ export interface GraphifyMethodDetail {
|
|
|
235
277
|
}
|
|
236
278
|
|
|
237
279
|
/**
|
|
238
|
-
* The
|
|
280
|
+
* The structured declaration payload for a subsystem component (click panel).
|
|
239
281
|
*
|
|
240
282
|
* The discriminant is derived from graph structure, not read off the node:
|
|
241
283
|
* method edges → class; `()` label + no method edges → function; incoming
|
package/src/index.ts
CHANGED
|
@@ -86,7 +86,7 @@ export type { UseElkLayoutOptions, UseElkLayoutResult } from './hooks/useElkLayo
|
|
|
86
86
|
|
|
87
87
|
// Subsystem component graph
|
|
88
88
|
export { SubsystemComponentGraph } from './subsystem/SubsystemComponentGraph';
|
|
89
|
-
export type { SubsystemComponentGraphProps,
|
|
89
|
+
export type { SubsystemComponentGraphProps, WalkthroughViewerContext } from './subsystem/SubsystemComponentGraph';
|
|
90
90
|
export type {
|
|
91
91
|
ComponentVerificationState,
|
|
92
92
|
ComponentVerificationPhase,
|
|
@@ -114,14 +114,19 @@ export type { GraphLayoutCoverProps } from './subsystem/GraphLayoutCover';
|
|
|
114
114
|
export type {
|
|
115
115
|
SubsystemComponent,
|
|
116
116
|
SubsystemComponentEdge,
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
SubsystemRelation,
|
|
118
|
+
SubsystemRelationType,
|
|
119
|
+
SubsystemWalkthrough,
|
|
120
|
+
SubsystemWalkthroughStep,
|
|
121
|
+
SubsystemWalkthroughMechanism,
|
|
119
122
|
SubsystemModelDocument,
|
|
120
123
|
SubsystemComponentConstruct,
|
|
121
124
|
SubsystemComponentRole,
|
|
122
125
|
SubsystemFramework,
|
|
123
126
|
SubsystemStereotype,
|
|
124
127
|
SubsystemEdgeMechanism,
|
|
128
|
+
SubsystemConstructDeclaration,
|
|
129
|
+
SubsystemDeclarationProvenance,
|
|
125
130
|
} from './subsystem/model';
|
|
126
131
|
export {
|
|
127
132
|
constructBadgeLabel,
|
|
@@ -129,6 +134,9 @@ export {
|
|
|
129
134
|
rightBadgeColor,
|
|
130
135
|
deriveNameFromSymbol,
|
|
131
136
|
nodeMinWidthForBadges,
|
|
137
|
+
deriveGraphEdges,
|
|
138
|
+
derivedGraphEdgeId,
|
|
139
|
+
walkthroughStepGraphEdgeId,
|
|
132
140
|
ROLE_COLOR,
|
|
133
141
|
ROLE_LABEL,
|
|
134
142
|
PROPOSED_COLOR,
|
|
@@ -146,11 +154,11 @@ export {
|
|
|
146
154
|
} from './subsystem/declarationRef';
|
|
147
155
|
|
|
148
156
|
// Pierre code views (@pierre/diffs wrappers)
|
|
149
|
-
export { PierreFileView, PierreSnippetView,
|
|
157
|
+
export { PierreFileView, PierreSnippetView, PierreWalkthroughCodeView, sliceSnippetWindow, resolvePierreSyntaxThemeName } from './pierre';
|
|
150
158
|
export type {
|
|
151
159
|
PierreFileViewProps,
|
|
152
160
|
PierreSnippetViewProps,
|
|
153
|
-
|
|
161
|
+
PierreWalkthroughCodeViewProps,
|
|
154
162
|
SnippetSlice,
|
|
155
163
|
PierreSyntaxThemeName,
|
|
156
164
|
} from './pierre';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* PierreWalkthroughCodeView — multi-file step snippets via `@pierre/diffs` CodeView.
|
|
3
3
|
*
|
|
4
|
-
* Renders one sliced window per
|
|
4
|
+
* Renders one sliced window per walkthrough step in a single virtualized
|
|
5
5
|
* scroll; when `stepIndex` changes, scrolls that step's site line into view
|
|
6
6
|
* and highlights it via CodeView `selectedLines` (same mechanism as
|
|
7
7
|
* `PierreSnippetView`).
|
|
@@ -22,10 +22,10 @@ type CodeViewLineSelection = {
|
|
|
22
22
|
id: string;
|
|
23
23
|
range: { start: number; end: number };
|
|
24
24
|
};
|
|
25
|
-
/** Metadata carried on a
|
|
26
|
-
type
|
|
25
|
+
/** Metadata carried on a walkthrough step's line annotation. */
|
|
26
|
+
type WalkthroughStepAnnotation = { text: string };
|
|
27
27
|
import { useTheme } from '@principal-ade/industry-theme';
|
|
28
|
-
import type {
|
|
28
|
+
import type { SubsystemWalkthrough } from '../subsystem/model';
|
|
29
29
|
import { buildPierreOptions, PIERRE_FILE_STYLE } from './pierreBackground';
|
|
30
30
|
import {
|
|
31
31
|
pierreCodeViewFileName,
|
|
@@ -34,8 +34,8 @@ import {
|
|
|
34
34
|
import { resolvePierreSyntaxThemeName } from './pierreSyntaxTheme';
|
|
35
35
|
import { sliceSnippetWindow, type SnippetSlice } from './sliceSnippet';
|
|
36
36
|
|
|
37
|
-
export interface
|
|
38
|
-
|
|
37
|
+
export interface PierreWalkthroughCodeViewProps {
|
|
38
|
+
walkthrough: SubsystemWalkthrough;
|
|
39
39
|
/** Focused step; `null` shows all snippets without scrolling to a step. */
|
|
40
40
|
stepIndex: number | null;
|
|
41
41
|
readFile: (path: string) => Promise<string>;
|
|
@@ -50,31 +50,31 @@ type FileLoadState =
|
|
|
50
50
|
| { status: 'error'; message: string }
|
|
51
51
|
| { status: 'ready'; byPath: Map<string, string> };
|
|
52
52
|
|
|
53
|
-
function stepItemId(
|
|
54
|
-
return `${
|
|
53
|
+
function stepItemId(walkthroughId: string, index: number): string {
|
|
54
|
+
return `${walkthroughId}:${index}`;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
export function
|
|
58
|
-
|
|
57
|
+
export function PierreWalkthroughCodeView({
|
|
58
|
+
walkthrough,
|
|
59
59
|
stepIndex,
|
|
60
60
|
readFile,
|
|
61
61
|
contextLines = 8,
|
|
62
62
|
background,
|
|
63
|
-
}:
|
|
63
|
+
}: PierreWalkthroughCodeViewProps) {
|
|
64
64
|
const { theme, mode } = useTheme();
|
|
65
65
|
const viewRef = useRef<CodeViewHandle<undefined>>(null);
|
|
66
66
|
const [load, setLoad] = useState<FileLoadState>({ status: 'loading' });
|
|
67
67
|
|
|
68
68
|
const pathsKey = useMemo(() => {
|
|
69
|
-
const paths = [...new Set(
|
|
69
|
+
const paths = [...new Set(walkthrough.steps.map((s) => s.file))];
|
|
70
70
|
paths.sort();
|
|
71
71
|
return paths.join('\0');
|
|
72
|
-
}, [
|
|
72
|
+
}, [walkthrough.steps]);
|
|
73
73
|
|
|
74
74
|
useEffect(() => {
|
|
75
75
|
let cancelled = false;
|
|
76
76
|
setLoad({ status: 'loading' });
|
|
77
|
-
const paths = [...new Set(
|
|
77
|
+
const paths = [...new Set(walkthrough.steps.map((s) => s.file))];
|
|
78
78
|
void Promise.all(
|
|
79
79
|
paths.map(async (path) => {
|
|
80
80
|
const contents = await readFile(path);
|
|
@@ -95,11 +95,11 @@ export function PierreThroughlineCodeView({
|
|
|
95
95
|
return () => {
|
|
96
96
|
cancelled = true;
|
|
97
97
|
};
|
|
98
|
-
}, [
|
|
98
|
+
}, [walkthrough.id, pathsKey, readFile]);
|
|
99
99
|
|
|
100
100
|
const slices = useMemo((): SnippetSlice[] => {
|
|
101
101
|
if (load.status !== 'ready') return [];
|
|
102
|
-
return
|
|
102
|
+
return walkthrough.steps.map((step) => {
|
|
103
103
|
const contents = load.byPath.get(step.file) ?? '';
|
|
104
104
|
return sliceSnippetWindow(
|
|
105
105
|
contents,
|
|
@@ -109,15 +109,15 @@ export function PierreThroughlineCodeView({
|
|
|
109
109
|
step.line,
|
|
110
110
|
);
|
|
111
111
|
});
|
|
112
|
-
}, [load,
|
|
112
|
+
}, [load, walkthrough.steps, contextLines]);
|
|
113
113
|
|
|
114
|
-
const items = useMemo((): CodeViewItem<
|
|
114
|
+
const items = useMemo((): CodeViewItem<WalkthroughStepAnnotation>[] => {
|
|
115
115
|
if (load.status !== 'ready' || slices.length === 0) return [];
|
|
116
|
-
return
|
|
116
|
+
return walkthrough.steps.map((step, index) => {
|
|
117
117
|
const slice = slices[index]!;
|
|
118
118
|
const focus = slice.focusOffset;
|
|
119
119
|
const annotations:
|
|
120
|
-
| LineAnnotation<
|
|
120
|
+
| LineAnnotation<WalkthroughStepAnnotation>[]
|
|
121
121
|
| undefined =
|
|
122
122
|
step.annotation != null && step.annotation.length > 0 && focus != null
|
|
123
123
|
? [
|
|
@@ -128,26 +128,20 @@ const items = useMemo((): CodeViewItem<ThroughlineStepAnnotation>[] => {
|
|
|
128
128
|
]
|
|
129
129
|
: undefined;
|
|
130
130
|
return {
|
|
131
|
-
id: stepItemId(
|
|
131
|
+
id: stepItemId(walkthrough.id, index),
|
|
132
132
|
type: 'file' as const,
|
|
133
133
|
version: 1,
|
|
134
134
|
annotations,
|
|
135
135
|
file: {
|
|
136
|
-
// Unique per-step name: many throughline steps share one path
|
|
137
|
-
// (e.g. seven sites in main.cpp), and identical `name`s have
|
|
138
|
-
// correlated with WebKit renderer traps in CodeView.
|
|
139
136
|
name: pierreCodeViewFileName(step.file, index),
|
|
140
137
|
contents: slice.contents,
|
|
141
|
-
// C-family → plain text: cpp Shiki still traps WebKit even with
|
|
142
|
-
// unique names (reproduced on 0.16.67).
|
|
143
138
|
lang: pierreLangForPath(step.file),
|
|
144
|
-
cacheKey: `${
|
|
139
|
+
cacheKey: `${walkthrough.id}:${index}:${step.file}:${step.line}:${slice.sliceStart}-${slice.sliceEnd}`,
|
|
145
140
|
},
|
|
146
141
|
};
|
|
147
142
|
});
|
|
148
|
-
}, [load, slices,
|
|
143
|
+
}, [load, slices, walkthrough.id, walkthrough.steps]);
|
|
149
144
|
|
|
150
|
-
/** Highlight the focused step's site line (1-based within its sliced window). */
|
|
151
145
|
const selectedLines = useMemo((): CodeViewLineSelection | null => {
|
|
152
146
|
if (stepIndex == null || stepIndex < 0 || stepIndex >= slices.length) {
|
|
153
147
|
return null;
|
|
@@ -155,15 +149,15 @@ const items = useMemo((): CodeViewItem<ThroughlineStepAnnotation>[] => {
|
|
|
155
149
|
const focus = slices[stepIndex]?.focusOffset;
|
|
156
150
|
if (focus == null) return null;
|
|
157
151
|
return {
|
|
158
|
-
id: stepItemId(
|
|
152
|
+
id: stepItemId(walkthrough.id, stepIndex),
|
|
159
153
|
range: { start: focus, end: focus },
|
|
160
154
|
};
|
|
161
|
-
}, [stepIndex, slices,
|
|
155
|
+
}, [stepIndex, slices, walkthrough.id]);
|
|
162
156
|
|
|
163
157
|
const renderHeaderPrefix = useMemo(() => {
|
|
164
158
|
return (item: CodeViewItem) => {
|
|
165
159
|
const index = Number.parseInt(item.id.split(':').pop() ?? '', 10);
|
|
166
|
-
const step =
|
|
160
|
+
const step = walkthrough.steps[index];
|
|
167
161
|
if (!step) return null;
|
|
168
162
|
const label =
|
|
169
163
|
step.symbol != null && step.symbol.length > 0
|
|
@@ -182,12 +176,12 @@ const items = useMemo((): CodeViewItem<ThroughlineStepAnnotation>[] => {
|
|
|
182
176
|
</span>
|
|
183
177
|
);
|
|
184
178
|
};
|
|
185
|
-
}, [
|
|
179
|
+
}, [walkthrough.steps, theme]);
|
|
186
180
|
|
|
187
181
|
const renderHeaderMetadata = useMemo(() => {
|
|
188
182
|
return (item: CodeViewItem) => {
|
|
189
183
|
const index = Number.parseInt(item.id.split(':').pop() ?? '', 10);
|
|
190
|
-
const step =
|
|
184
|
+
const step = walkthrough.steps[index];
|
|
191
185
|
if (!step) return null;
|
|
192
186
|
return (
|
|
193
187
|
<span
|
|
@@ -202,17 +196,17 @@ const items = useMemo((): CodeViewItem<ThroughlineStepAnnotation>[] => {
|
|
|
202
196
|
</span>
|
|
203
197
|
);
|
|
204
198
|
};
|
|
205
|
-
}, [
|
|
199
|
+
}, [walkthrough.steps, theme]);
|
|
206
200
|
|
|
207
201
|
const renderAnnotation = useMemo(() => {
|
|
208
202
|
return (
|
|
209
203
|
annotation:
|
|
210
|
-
| LineAnnotation<
|
|
211
|
-
| DiffLineAnnotation<
|
|
212
|
-
item: CodeViewItem<
|
|
204
|
+
| LineAnnotation<WalkthroughStepAnnotation>
|
|
205
|
+
| DiffLineAnnotation<WalkthroughStepAnnotation>,
|
|
206
|
+
item: CodeViewItem<WalkthroughStepAnnotation>,
|
|
213
207
|
) => {
|
|
214
208
|
const index = Number.parseInt(item.id.split(':').pop() ?? '', 10);
|
|
215
|
-
const step =
|
|
209
|
+
const step = walkthrough.steps[index];
|
|
216
210
|
const text = annotation.metadata?.text;
|
|
217
211
|
if (!text || !step) return null;
|
|
218
212
|
return (
|
|
@@ -230,7 +224,7 @@ const items = useMemo((): CodeViewItem<ThroughlineStepAnnotation>[] => {
|
|
|
230
224
|
</span>
|
|
231
225
|
);
|
|
232
226
|
};
|
|
233
|
-
}, [
|
|
227
|
+
}, [walkthrough.steps, theme]);
|
|
234
228
|
|
|
235
229
|
const options = useMemo((): CodeViewReactOptions => {
|
|
236
230
|
return {
|
|
@@ -248,11 +242,10 @@ const items = useMemo((): CodeViewItem<ThroughlineStepAnnotation>[] => {
|
|
|
248
242
|
|
|
249
243
|
useEffect(() => {
|
|
250
244
|
if (load.status !== 'ready' || stepIndex == null) return;
|
|
251
|
-
if (stepIndex < 0 || stepIndex >=
|
|
245
|
+
if (stepIndex < 0 || stepIndex >= walkthrough.steps.length) return;
|
|
252
246
|
const focus = slices[stepIndex]?.focusOffset;
|
|
253
247
|
if (focus == null) return;
|
|
254
|
-
|
|
255
|
-
const id = stepItemId(throughline.id, stepIndex);
|
|
248
|
+
const id = stepItemId(walkthrough.id, stepIndex);
|
|
256
249
|
const t = window.setTimeout(() => {
|
|
257
250
|
viewRef.current?.scrollTo({
|
|
258
251
|
type: 'line',
|
|
@@ -265,8 +258,8 @@ const items = useMemo((): CodeViewItem<ThroughlineStepAnnotation>[] => {
|
|
|
265
258
|
}, [
|
|
266
259
|
load.status,
|
|
267
260
|
stepIndex,
|
|
268
|
-
|
|
269
|
-
|
|
261
|
+
walkthrough.id,
|
|
262
|
+
walkthrough.steps.length,
|
|
270
263
|
slices,
|
|
271
264
|
items.length,
|
|
272
265
|
]);
|
|
@@ -286,7 +279,6 @@ const items = useMemo((): CodeViewItem<ThroughlineStepAnnotation>[] => {
|
|
|
286
279
|
);
|
|
287
280
|
}
|
|
288
281
|
|
|
289
|
-
// Pierre's CodeView prop / item unions blow past TS's complexity limit.
|
|
290
282
|
const CodeViewLoose = CodeView as unknown as (props: Record<string, unknown>) => ReactElement;
|
|
291
283
|
|
|
292
284
|
return (
|
package/src/pierre/index.ts
CHANGED
|
@@ -2,8 +2,8 @@ export { PierreFileView } from './PierreFileView';
|
|
|
2
2
|
export type { PierreFileViewProps } from './PierreFileView';
|
|
3
3
|
export { PierreSnippetView } from './PierreSnippetView';
|
|
4
4
|
export type { PierreSnippetViewProps } from './PierreSnippetView';
|
|
5
|
-
export {
|
|
6
|
-
export type {
|
|
5
|
+
export { PierreWalkthroughCodeView } from './PierreWalkthroughCodeView';
|
|
6
|
+
export type { PierreWalkthroughCodeViewProps } from './PierreWalkthroughCodeView';
|
|
7
7
|
export { sliceSnippetWindow } from './sliceSnippet';
|
|
8
8
|
export type { SnippetSlice } from './sliceSnippet';
|
|
9
9
|
export {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* `file.name`s — so we force plain text for those paths until Pierre/WebKit
|
|
8
8
|
* is safe with them.
|
|
9
9
|
*
|
|
10
|
-
* Distinct
|
|
10
|
+
* Distinct walkthrough slices of the same path still need unique `file.name`s
|
|
11
11
|
* (see `pierreCodeViewFileName`).
|
|
12
12
|
*/
|
|
13
13
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Demo: Pierre CodeView with snippets from different files.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Walkthroughs want many short windows across files in one scroll —
|
|
5
5
|
* CodeView is the Pierre primitive for that (virtualized list of file items).
|
|
6
6
|
*/
|
|
7
7
|
import React, { useMemo, useRef } from 'react';
|
|
@@ -54,7 +54,7 @@ const SNIPPETS: SnippetSpec[] = [
|
|
|
54
54
|
startLine: 216,
|
|
55
55
|
endLine: 250,
|
|
56
56
|
focusLine: 216,
|
|
57
|
-
label: '3 ·
|
|
57
|
+
label: '3 · Walkthrough types',
|
|
58
58
|
},
|
|
59
59
|
];
|
|
60
60
|
|
|
@@ -159,7 +159,7 @@ function MultiFileSnippetsDemo() {
|
|
|
159
159
|
}}
|
|
160
160
|
>
|
|
161
161
|
<div style={{ fontSize: 13, fontWeight: 600, marginBottom: 4 }}>
|
|
162
|
-
|
|
162
|
+
Walkthrough steps
|
|
163
163
|
</div>
|
|
164
164
|
<div style={{ fontSize: 12, color: theme.colors.textSecondary, marginBottom: 8 }}>
|
|
165
165
|
Click a step to scroll + highlight its site line.
|
|
@@ -43,7 +43,7 @@ const cases: { label: string; component: SubsystemComponent }[] = [
|
|
|
43
43
|
file: 'src/event-processing/sink.ts',
|
|
44
44
|
purl: 'pkg:github/principal-ai/agent-monitoring',
|
|
45
45
|
symbol: 'flush',
|
|
46
|
-
|
|
46
|
+
declaration: {
|
|
47
47
|
kind: 'function',
|
|
48
48
|
parameters: [],
|
|
49
49
|
} satisfies GraphifyComponentDetail,
|
|
@@ -58,7 +58,7 @@ const cases: { label: string; component: SubsystemComponent }[] = [
|
|
|
58
58
|
file: 'src/session/SessionReader.ts',
|
|
59
59
|
purl: 'pkg:github/principal-ai/agent-monitoring',
|
|
60
60
|
symbol: 'SessionReader.normalize',
|
|
61
|
-
|
|
61
|
+
declaration: {
|
|
62
62
|
kind: 'function',
|
|
63
63
|
parameters: [{ name: 'session', type: 'SessionRecord' }],
|
|
64
64
|
returnType: 'SessionEvent[]',
|
|
@@ -74,7 +74,7 @@ const cases: { label: string; component: SubsystemComponent }[] = [
|
|
|
74
74
|
file: 'src/event-processing/normalize.ts',
|
|
75
75
|
purl: 'pkg:github/principal-ai/agent-monitoring',
|
|
76
76
|
symbol: 'normalizeSession',
|
|
77
|
-
|
|
77
|
+
declaration: {
|
|
78
78
|
kind: 'function',
|
|
79
79
|
parameters: [
|
|
80
80
|
{ name: 'session', type: 'SessionRecord' },
|
|
@@ -93,7 +93,7 @@ const cases: { label: string; component: SubsystemComponent }[] = [
|
|
|
93
93
|
file: 'src/session/merge.ts',
|
|
94
94
|
purl: 'pkg:github/principal-ai/agent-monitoring',
|
|
95
95
|
symbol: 'mergeSessions',
|
|
96
|
-
|
|
96
|
+
declaration: {
|
|
97
97
|
kind: 'function',
|
|
98
98
|
parameters: [
|
|
99
99
|
{ name: 'sessions', type: 'SessionRecord[]' },
|
|
@@ -114,7 +114,7 @@ const cases: { label: string; component: SubsystemComponent }[] = [
|
|
|
114
114
|
file: 'src/empty.ts',
|
|
115
115
|
purl: 'pkg:github/principal-ai/agent-monitoring',
|
|
116
116
|
symbol: 'EmptyClass',
|
|
117
|
-
|
|
117
|
+
declaration: {
|
|
118
118
|
kind: 'class',
|
|
119
119
|
} satisfies GraphifyComponentDetail,
|
|
120
120
|
},
|
|
@@ -128,7 +128,7 @@ const cases: { label: string; component: SubsystemComponent }[] = [
|
|
|
128
128
|
file: 'src/config.ts',
|
|
129
129
|
purl: 'pkg:github/principal-ai/agent-monitoring',
|
|
130
130
|
symbol: 'Config',
|
|
131
|
-
|
|
131
|
+
declaration: {
|
|
132
132
|
kind: 'class',
|
|
133
133
|
properties: [
|
|
134
134
|
{ name: 'host', type: 'string' },
|
|
@@ -147,7 +147,7 @@ const cases: { label: string; component: SubsystemComponent }[] = [
|
|
|
147
147
|
file: 'src/event-processing/EventProcessor.ts',
|
|
148
148
|
purl: 'pkg:github/principal-ai/agent-monitoring',
|
|
149
149
|
symbol: 'EventProcessor',
|
|
150
|
-
|
|
150
|
+
declaration: {
|
|
151
151
|
kind: 'class',
|
|
152
152
|
methods: [
|
|
153
153
|
{ name: 'process', parameters: [{ type: 'RawEvent' }, { type: 'ProcessingOptions' }], returnType: 'ProcessedEvent' },
|
|
@@ -174,7 +174,7 @@ const cases: { label: string; component: SubsystemComponent }[] = [
|
|
|
174
174
|
file: 'src/session/transcript.ts',
|
|
175
175
|
purl: 'pkg:github/principal-ai/agent-monitoring',
|
|
176
176
|
symbol: 'SessionRecord',
|
|
177
|
-
|
|
177
|
+
declaration: {
|
|
178
178
|
kind: 'type',
|
|
179
179
|
properties: [
|
|
180
180
|
{ name: 'id', type: 'string' },
|
|
@@ -192,7 +192,7 @@ const cases: { label: string; component: SubsystemComponent }[] = [
|
|
|
192
192
|
file: 'src/types.ts',
|
|
193
193
|
purl: 'pkg:github/principal-ai/agent-monitoring',
|
|
194
194
|
symbol: 'BrandedId',
|
|
195
|
-
|
|
195
|
+
declaration: {
|
|
196
196
|
kind: 'type',
|
|
197
197
|
} satisfies GraphifyComponentDetail,
|
|
198
198
|
},
|
|
@@ -206,7 +206,7 @@ const cases: { label: string; component: SubsystemComponent }[] = [
|
|
|
206
206
|
file: 'src/index.ts',
|
|
207
207
|
purl: 'pkg:github/principal-ai/agent-monitoring',
|
|
208
208
|
symbol: 'index',
|
|
209
|
-
|
|
209
|
+
declaration: {
|
|
210
210
|
kind: 'module',
|
|
211
211
|
imports: [
|
|
212
212
|
{ name: './session/SessionReader' },
|
|
@@ -237,7 +237,7 @@ const cases: { label: string; component: SubsystemComponent }[] = [
|
|
|
237
237
|
file: '',
|
|
238
238
|
purl: 'pkg:npm/@principal-ai/subsystems-studio',
|
|
239
239
|
symbol: '',
|
|
240
|
-
|
|
240
|
+
declaration: {
|
|
241
241
|
kind: 'external',
|
|
242
242
|
label: 'pkg:npm/@principal-ai/subsystems-studio',
|
|
243
243
|
} satisfies GraphifyComponentDetail,
|
|
@@ -251,7 +251,7 @@ const cases: { label: string; component: SubsystemComponent }[] = [
|
|
|
251
251
|
construct: 'function',
|
|
252
252
|
file: 'src/helpers.ts',
|
|
253
253
|
purl: 'pkg:github/principal-ai/agent-monitoring',
|
|
254
|
-
|
|
254
|
+
declaration: {
|
|
255
255
|
kind: 'function',
|
|
256
256
|
parameters: [{ name: 'input', type: 'string' }],
|
|
257
257
|
returnType: 'void',
|
|
@@ -267,7 +267,7 @@ const cases: { label: string; component: SubsystemComponent }[] = [
|
|
|
267
267
|
file: 'src/session/SessionReader.ts',
|
|
268
268
|
purl: 'pkg:github/principal-ai/agent-monitoring',
|
|
269
269
|
symbol: 'SessionReader.normalize',
|
|
270
|
-
|
|
270
|
+
declaration: {
|
|
271
271
|
kind: 'method',
|
|
272
272
|
hostClass: 'SessionReader',
|
|
273
273
|
parameters: [{ name: 'session', type: 'SessionRecord' }],
|
|
@@ -284,7 +284,7 @@ const cases: { label: string; component: SubsystemComponent }[] = [
|
|
|
284
284
|
file: 'src/event-processing/EventProcessor.ts',
|
|
285
285
|
purl: 'pkg:github/principal-ai/agent-monitoring',
|
|
286
286
|
symbol: 'EventProcessor.dispose',
|
|
287
|
-
|
|
287
|
+
declaration: {
|
|
288
288
|
kind: 'method',
|
|
289
289
|
hostClass: 'EventProcessor',
|
|
290
290
|
} satisfies GraphifyComponentDetail,
|