@principal-ai/subsystems-react 0.24.27 → 0.25.1
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/stories/Subsystem/ComponentGraph/fixtures.d.ts +1 -1
- package/dist/stories/Subsystem/ComponentGraph/fixtures.d.ts.map +1 -1
- package/dist/stories/Subsystem/ComponentGraph/fixtures.js +7 -7
- package/dist/stories/Subsystem/ComponentGraph/fixtures.js.map +1 -1
- package/dist/subsystem/ComponentDeclaration.d.ts +1 -1
- package/dist/subsystem/ComponentDeclaration.d.ts.map +1 -1
- package/dist/subsystem/ComponentDeclaration.js +1 -1
- package/dist/subsystem/ComponentDeclaration.js.map +1 -1
- package/dist/subsystem/ConstructsCatalog.d.ts +2 -2
- package/dist/subsystem/ConstructsCatalog.d.ts.map +1 -1
- package/dist/subsystem/ConstructsCatalog.js +62 -62
- package/dist/subsystem/ConstructsCatalog.js.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.d.ts +2 -2
- package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.js +27 -27
- package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
- package/dist/subsystem/model.d.ts +11 -5
- package/dist/subsystem/model.d.ts.map +1 -1
- package/dist/subsystem/model.js +65 -36
- package/dist/subsystem/model.js.map +1 -1
- package/dist/subsystem/nodes.d.ts +3 -3
- package/dist/subsystem/nodes.d.ts.map +1 -1
- package/dist/subsystem/nodes.js +3 -3
- package/dist/subsystem/nodes.js.map +1 -1
- package/package.json +1 -1
- package/src/stories/Subsystem/ComponentDeclarationAudit.stories.tsx +13 -13
- package/src/stories/Subsystem/ComponentGraph/Appearance.stories.tsx +17 -17
- package/src/stories/Subsystem/ComponentGraph/Basics.stories.tsx +2 -2
- package/src/stories/Subsystem/ComponentGraph/Captures.stories.tsx +3 -3
- package/src/stories/Subsystem/ComponentGraph/Constructs.stories.tsx +6 -6
- package/src/stories/Subsystem/ComponentGraph/CustomEntities.stories.tsx +8 -8
- package/src/stories/Subsystem/ComponentGraph/DetailPanel.stories.tsx +11 -11
- package/src/stories/Subsystem/ComponentGraph/Flows.stories.tsx +6 -6
- package/src/stories/Subsystem/ComponentGraph/FrameworkStereotype.stories.tsx +10 -10
- package/src/stories/Subsystem/ComponentGraph/ModuleBadges.stories.tsx +7 -7
- package/src/stories/Subsystem/ComponentGraph/Modules.stories.tsx +17 -17
- package/src/stories/Subsystem/ComponentGraph/NodeAnatomy.stories.tsx +16 -16
- package/src/stories/Subsystem/ComponentGraph/Packages.stories.tsx +12 -12
- package/src/stories/Subsystem/ComponentGraph/Processes.stories.tsx +1 -1
- package/src/stories/Subsystem/ComponentGraph/Proposed.stories.tsx +10 -10
- package/src/stories/Subsystem/ComponentGraph/Scenarios.stories.tsx +69 -69
- package/src/stories/Subsystem/ComponentGraph/Spotlights.stories.tsx +27 -27
- package/src/stories/Subsystem/ComponentGraph/fixtures.ts +8 -8
- package/src/subsystem/ComponentDeclaration.tsx +2 -2
- package/src/subsystem/ConstructsCatalog.tsx +71 -75
- package/src/subsystem/SubsystemComponentGraph.tsx +28 -28
- package/src/subsystem/model.test.ts +97 -51
- package/src/subsystem/model.ts +77 -41
- package/src/subsystem/nodes.tsx +6 -6
|
@@ -149,7 +149,7 @@ const nodeSpotlights: Array<{ label: string; component: SubsystemComponent; note
|
|
|
149
149
|
label: 'construct: type_alias — a type alias',
|
|
150
150
|
note: 'the `type` keyword form: `type X = { … }` or a union — anything `=`-assigned',
|
|
151
151
|
component: {
|
|
152
|
-
|
|
152
|
+
alias: 'alias-status',
|
|
153
153
|
name: 'AnalysisStatus',
|
|
154
154
|
construct: 'type_alias',
|
|
155
155
|
file: 'packages/subsystems-studio/src/bun/analysis.ts',
|
|
@@ -170,7 +170,7 @@ const nodeSpotlights: Array<{ label: string; component: SubsystemComponent; note
|
|
|
170
170
|
label: 'role: entry on construct: type — contract as entry',
|
|
171
171
|
note: 'same role, different inherited anatomy — entries are topology, not shape',
|
|
172
172
|
component: {
|
|
173
|
-
|
|
173
|
+
alias: 'ipc-entry',
|
|
174
174
|
name: 'StudioMessages',
|
|
175
175
|
construct: 'interface',
|
|
176
176
|
file: 'packages/subsystems-studio/src/shared/contract.ts',
|
|
@@ -183,7 +183,7 @@ const nodeSpotlights: Array<{ label: string; component: SubsystemComponent; note
|
|
|
183
183
|
label: 'construct: enum — runtime values + a type',
|
|
184
184
|
note: 'the one type-family form that exists at runtime; members render in the drill-down',
|
|
185
185
|
component: {
|
|
186
|
-
|
|
186
|
+
alias: 'enum-phase',
|
|
187
187
|
name: 'VerificationPhase',
|
|
188
188
|
construct: 'enum',
|
|
189
189
|
file: 'packages/subsystems-studio/src/bun/verify.ts',
|
|
@@ -201,7 +201,7 @@ const nodeSpotlights: Array<{ label: string; component: SubsystemComponent; note
|
|
|
201
201
|
label: 'construct: function — plain accessor',
|
|
202
202
|
note: 'indigo; the default for anchored behavior',
|
|
203
203
|
component: {
|
|
204
|
-
|
|
204
|
+
alias: 'create',
|
|
205
205
|
name: 'createSubsystemModel',
|
|
206
206
|
construct: 'function',
|
|
207
207
|
file: 'packages/subsystems-studio/src/bun/subsystem-model-store.ts',
|
|
@@ -213,7 +213,7 @@ const nodeSpotlights: Array<{ label: string; component: SubsystemComponent; note
|
|
|
213
213
|
label: 'framework: react · stereotype: component — still construct: function',
|
|
214
214
|
note: 'badge reads "react · component"; name renders bare ; color stays function',
|
|
215
215
|
component: {
|
|
216
|
-
|
|
216
|
+
alias: 'analysis-view',
|
|
217
217
|
name: 'AnalysisView',
|
|
218
218
|
construct: 'function',
|
|
219
219
|
file: 'packages/subsystems-studio/src/mainview/views/AnalysisView.tsx',
|
|
@@ -228,7 +228,7 @@ const nodeSpotlights: Array<{ label: string; component: SubsystemComponent; note
|
|
|
228
228
|
label: 'framework: react · stereotype: hook',
|
|
229
229
|
note: 'same construct, different stereotype — badge "react · hook", keeps ()',
|
|
230
230
|
component: {
|
|
231
|
-
|
|
231
|
+
alias: 'drawings-host',
|
|
232
232
|
name: 'useDrawingsHost',
|
|
233
233
|
construct: 'function',
|
|
234
234
|
file: 'packages/subsystems-studio/src/mainview/hooks/useDrawingsHost.ts',
|
|
@@ -242,7 +242,7 @@ const nodeSpotlights: Array<{ label: string; component: SubsystemComponent; note
|
|
|
242
242
|
label: 'role: entry on construct: function — boundary element',
|
|
243
243
|
note: 'orange overrides construct:color; anatomy inherited from the function',
|
|
244
244
|
component: {
|
|
245
|
-
|
|
245
|
+
alias: 'http-entry',
|
|
246
246
|
name: 'HTTP bridge :3045',
|
|
247
247
|
construct: 'function',
|
|
248
248
|
file: 'packages/subsystems-studio/src/bun/http-server.ts',
|
|
@@ -255,7 +255,7 @@ const nodeSpotlights: Array<{ label: string; component: SubsystemComponent; note
|
|
|
255
255
|
label: 'construct: class — declaration anatomy',
|
|
256
256
|
note: 'blue; drill-down renders the real class stub with its methods',
|
|
257
257
|
component: {
|
|
258
|
-
|
|
258
|
+
alias: 'cache',
|
|
259
259
|
name: 'SessionCache',
|
|
260
260
|
construct: 'class',
|
|
261
261
|
file: 'src/session/SessionCache.ts',
|
|
@@ -276,7 +276,7 @@ const nodeSpotlights: Array<{ label: string; component: SubsystemComponent; note
|
|
|
276
276
|
label: 'construct: method — a function bound to its class',
|
|
277
277
|
note: 'dotted symbol (SessionCache.put) is the identity; drill-down renders it inside a host-class stub',
|
|
278
278
|
component: {
|
|
279
|
-
|
|
279
|
+
alias: 'method-put',
|
|
280
280
|
name: 'put',
|
|
281
281
|
construct: 'method',
|
|
282
282
|
file: 'src/session/SessionCache.ts',
|
|
@@ -293,7 +293,7 @@ const nodeSpotlights: Array<{ label: string; component: SubsystemComponent; note
|
|
|
293
293
|
label: 'construct: store — state-block anatomy',
|
|
294
294
|
note: 'green = the store construct:color; drill-down renders declare const state, never a class stub',
|
|
295
295
|
component: {
|
|
296
|
-
|
|
296
|
+
alias: 'store',
|
|
297
297
|
name: 'Graph Store',
|
|
298
298
|
construct: 'store',
|
|
299
299
|
file: 'packages/subsystems-studio/src/bun/subsystem-model-store.ts',
|
|
@@ -306,7 +306,7 @@ const nodeSpotlights: Array<{ label: string; component: SubsystemComponent; note
|
|
|
306
306
|
label: 'construct: external — actor outside every boundary',
|
|
307
307
|
note: 'purple; no process → drawn outside all regions',
|
|
308
308
|
component: {
|
|
309
|
-
|
|
309
|
+
alias: 'agents',
|
|
310
310
|
name: 'agent clients',
|
|
311
311
|
construct: 'external',
|
|
312
312
|
file: '',
|
|
@@ -317,7 +317,7 @@ const nodeSpotlights: Array<{ label: string; component: SubsystemComponent; note
|
|
|
317
317
|
label: 'role: service on construct: external — far-side boundary element',
|
|
318
318
|
note: 'colored by its construct (external); identity via purl, no source at all',
|
|
319
319
|
component: {
|
|
320
|
-
|
|
320
|
+
alias: 'github',
|
|
321
321
|
name: 'api.github.com',
|
|
322
322
|
construct: 'external',
|
|
323
323
|
file: '',
|
|
@@ -332,7 +332,7 @@ function NodeSpotlightsDemo() {
|
|
|
332
332
|
const { mode } = useTheme();
|
|
333
333
|
const constructColors = constructColorsFromPierreTheme(resolvePierreSyntaxThemeName(mode));
|
|
334
334
|
const [activeIds, setActiveIds] = useState<Set<string>>(
|
|
335
|
-
() => new Set(nodeSpotlights.map((n) => n.component.
|
|
335
|
+
() => new Set(nodeSpotlights.map((n) => n.component.alias)),
|
|
336
336
|
);
|
|
337
337
|
const toggle = (id: string) =>
|
|
338
338
|
setActiveIds((prev) => {
|
|
@@ -344,7 +344,7 @@ function NodeSpotlightsDemo() {
|
|
|
344
344
|
}
|
|
345
345
|
return next;
|
|
346
346
|
});
|
|
347
|
-
const visible = nodeSpotlights.filter((n) => activeIds.has(n.component.
|
|
347
|
+
const visible = nodeSpotlights.filter((n) => activeIds.has(n.component.alias));
|
|
348
348
|
|
|
349
349
|
return (
|
|
350
350
|
<div style={{ padding: 20, overflow: 'auto', height: '100vh', boxSizing: 'border-box', background: '#0f1216' }}>
|
|
@@ -358,7 +358,7 @@ function NodeSpotlightsDemo() {
|
|
|
358
358
|
show:
|
|
359
359
|
</span>
|
|
360
360
|
{nodeSpotlights.map(({ component }) => {
|
|
361
|
-
const on = activeIds.has(component.
|
|
361
|
+
const on = activeIds.has(component.alias);
|
|
362
362
|
const color = constructColors[component.construct];
|
|
363
363
|
const chip = component.stereotype
|
|
364
364
|
? component.framework
|
|
@@ -369,9 +369,9 @@ function NodeSpotlightsDemo() {
|
|
|
369
369
|
: component.construct;
|
|
370
370
|
return (
|
|
371
371
|
<button
|
|
372
|
-
key={component.
|
|
372
|
+
key={component.alias}
|
|
373
373
|
type="button"
|
|
374
|
-
onClick={() => toggle(component.
|
|
374
|
+
onClick={() => toggle(component.alias)}
|
|
375
375
|
style={{
|
|
376
376
|
fontFamily: 'monospace',
|
|
377
377
|
fontSize: 11,
|
|
@@ -390,7 +390,7 @@ function NodeSpotlightsDemo() {
|
|
|
390
390
|
})}
|
|
391
391
|
<button
|
|
392
392
|
type="button"
|
|
393
|
-
onClick={() => setActiveIds(new Set(nodeSpotlights.map((n) => n.component.
|
|
393
|
+
onClick={() => setActiveIds(new Set(nodeSpotlights.map((n) => n.component.alias)))}
|
|
394
394
|
style={{ fontFamily: 'monospace', fontSize: 11, cursor: 'pointer', padding: '4px 10px', borderRadius: 999, border: '1px solid #2c313a', background: 'transparent', color: '#9ca3af' }}
|
|
395
395
|
>
|
|
396
396
|
all
|
|
@@ -410,7 +410,7 @@ function NodeSpotlightsDemo() {
|
|
|
410
410
|
</div>
|
|
411
411
|
)}
|
|
412
412
|
{visible.map(({ label, component, note }) => (
|
|
413
|
-
<div key={component.
|
|
413
|
+
<div key={component.alias}>
|
|
414
414
|
<div style={{ fontFamily: 'monospace', fontSize: 11, color: '#6b7280', margin: '10px 2px 6px' }}>
|
|
415
415
|
{label} — {note}
|
|
416
416
|
</div>
|
|
@@ -490,7 +490,7 @@ const anatomyComponents: Array<{ label: string; component: SubsystemComponent; n
|
|
|
490
490
|
label: 'store detail → state block',
|
|
491
491
|
note: 'declare const lines — honest to module-level state; no class stub, no methods',
|
|
492
492
|
component: {
|
|
493
|
-
|
|
493
|
+
alias: 'store',
|
|
494
494
|
name: 'Graph Store',
|
|
495
495
|
construct: 'store',
|
|
496
496
|
file: 'packages/subsystems-studio/src/bun/subsystem-model-store.ts',
|
|
@@ -510,7 +510,7 @@ const anatomyComponents: Array<{ label: string; component: SubsystemComponent; n
|
|
|
510
510
|
label: 'class detail → class stub',
|
|
511
511
|
note: 'the verifiable access mechanism — methods with typed params + returns',
|
|
512
512
|
component: {
|
|
513
|
-
|
|
513
|
+
alias: 'cache',
|
|
514
514
|
name: 'SessionCache',
|
|
515
515
|
construct: 'class',
|
|
516
516
|
file: 'src/session/SessionCache.ts',
|
|
@@ -534,7 +534,7 @@ const anatomyComponents: Array<{ label: string; component: SubsystemComponent; n
|
|
|
534
534
|
label: 'function detail → signature',
|
|
535
535
|
note: 'params + return type, callers/callees intentionally not rendered (edges carry them)',
|
|
536
536
|
component: {
|
|
537
|
-
|
|
537
|
+
alias: 'get',
|
|
538
538
|
name: 'getSubsystemModel',
|
|
539
539
|
construct: 'function',
|
|
540
540
|
file: 'packages/subsystems-studio/src/bun/subsystem-model-store.ts',
|
|
@@ -553,7 +553,7 @@ const anatomyComponents: Array<{ label: string; component: SubsystemComponent; n
|
|
|
553
553
|
label: 'type detail → interface fields',
|
|
554
554
|
note: 'the IPC contract as an entry: anatomy inherited from the type',
|
|
555
555
|
component: {
|
|
556
|
-
|
|
556
|
+
alias: 'ipc-entry',
|
|
557
557
|
name: 'StudioMessages',
|
|
558
558
|
construct: 'interface',
|
|
559
559
|
file: 'packages/subsystems-studio/src/shared/contract.ts',
|
|
@@ -575,7 +575,7 @@ const anatomyComponents: Array<{ label: string; component: SubsystemComponent; n
|
|
|
575
575
|
label: 'method detail → method in host-class stub',
|
|
576
576
|
note: 'the class is context, the method is the node — same anatomy a standalone function gets, plus the owning class',
|
|
577
577
|
component: {
|
|
578
|
-
|
|
578
|
+
alias: 'anatomy-method',
|
|
579
579
|
name: 'put',
|
|
580
580
|
construct: 'method',
|
|
581
581
|
file: 'src/session/SessionCache.ts',
|
|
@@ -593,7 +593,7 @@ const anatomyComponents: Array<{ label: string; component: SubsystemComponent; n
|
|
|
593
593
|
label: 'type_alias detail → braced alias',
|
|
594
594
|
note: 'the alias renders as `type X = { … }` — the honest keyword form',
|
|
595
595
|
component: {
|
|
596
|
-
|
|
596
|
+
alias: 'anatomy-alias',
|
|
597
597
|
name: 'AnalysisStatus',
|
|
598
598
|
construct: 'type_alias',
|
|
599
599
|
file: 'packages/subsystems-studio/src/bun/analysis.ts',
|
|
@@ -611,7 +611,7 @@ const anatomyComponents: Array<{ label: string; component: SubsystemComponent; n
|
|
|
611
611
|
label: 'enum detail → enum declaration',
|
|
612
612
|
note: 'members render inline — the runtime type',
|
|
613
613
|
component: {
|
|
614
|
-
|
|
614
|
+
alias: 'anatomy-enum',
|
|
615
615
|
name: 'VerificationPhase',
|
|
616
616
|
construct: 'enum',
|
|
617
617
|
file: 'packages/subsystems-studio/src/bun/verify.ts',
|
|
@@ -634,7 +634,7 @@ function AnatomySpotlightsDemo() {
|
|
|
634
634
|
anatomy spotlights — data (left) → the drill-down panel you get on click (right).
|
|
635
635
|
</div>
|
|
636
636
|
{anatomyComponents.map(({ label, component, note }) => (
|
|
637
|
-
<div key={component.
|
|
637
|
+
<div key={component.alias}>
|
|
638
638
|
<div style={{ fontFamily: 'monospace', fontSize: 11, color: '#6b7280', margin: '10px 2px 6px' }}>
|
|
639
639
|
{label} — {note}
|
|
640
640
|
</div>
|
|
@@ -13,10 +13,10 @@ import type { GraphifyComponentDetail } from '../../../graphify';
|
|
|
13
13
|
// Build a subsystem graph from a compact spec - helpers
|
|
14
14
|
// ---------------------------------------------------------------------------
|
|
15
15
|
export function components(
|
|
16
|
-
spec: Array<[
|
|
16
|
+
spec: Array<[alias: string, name: string, construct: SubsystemComponent['construct'], file: string, purl: string, purpose?: string, symbol?: string, declaration?: GraphifyComponentDetail]>,
|
|
17
17
|
): SubsystemComponent[] {
|
|
18
|
-
return spec.map(([
|
|
19
|
-
|
|
18
|
+
return spec.map(([alias, name, construct, file, purl, purpose, symbol, declaration]) => ({
|
|
19
|
+
alias,
|
|
20
20
|
name,
|
|
21
21
|
construct,
|
|
22
22
|
file,
|
|
@@ -135,7 +135,7 @@ export const readerDetail: GraphifyComponentDetail = {
|
|
|
135
135
|
// ---------------------------------------------------------------------------
|
|
136
136
|
export const investigateOnlyComponents: SubsystemComponent[] = [
|
|
137
137
|
{
|
|
138
|
-
|
|
138
|
+
alias: 'v1',
|
|
139
139
|
name: 'V1EventBridgeProcessor',
|
|
140
140
|
construct: 'class',
|
|
141
141
|
file: 'src/event-processing/V1EventBridge.ts',
|
|
@@ -144,7 +144,7 @@ export const investigateOnlyComponents: SubsystemComponent[] = [
|
|
|
144
144
|
symbol: 'V1EventBridgeProcessor',
|
|
145
145
|
},
|
|
146
146
|
{
|
|
147
|
-
|
|
147
|
+
alias: 'v2',
|
|
148
148
|
name: 'V2EventBridgeProcessor',
|
|
149
149
|
construct: 'class',
|
|
150
150
|
file: 'src/event-processing/V2EventBridge.ts',
|
|
@@ -153,7 +153,7 @@ export const investigateOnlyComponents: SubsystemComponent[] = [
|
|
|
153
153
|
symbol: 'V2EventBridgeProcessor',
|
|
154
154
|
},
|
|
155
155
|
{
|
|
156
|
-
|
|
156
|
+
alias: 'input',
|
|
157
157
|
name: 'RepoNormalizedUniversalAgentSessionEvent',
|
|
158
158
|
construct: 'interface',
|
|
159
159
|
file: 'types/RepoNormalizedUniversalAgentSessionEvent.ts',
|
|
@@ -162,7 +162,7 @@ export const investigateOnlyComponents: SubsystemComponent[] = [
|
|
|
162
162
|
symbol: 'RepoNormalizedUniversalAgentSessionEvent',
|
|
163
163
|
},
|
|
164
164
|
{
|
|
165
|
-
|
|
165
|
+
alias: 'acc',
|
|
166
166
|
name: 'accumulateToAgentSessionEvents',
|
|
167
167
|
construct: 'function',
|
|
168
168
|
file: 'src/accumulateToAgentSessionEvents.ts',
|
|
@@ -171,7 +171,7 @@ export const investigateOnlyComponents: SubsystemComponent[] = [
|
|
|
171
171
|
symbol: 'accumulateToAgentSessionEvents',
|
|
172
172
|
},
|
|
173
173
|
{
|
|
174
|
-
|
|
174
|
+
alias: 'out',
|
|
175
175
|
name: 'AgentSessionEvent',
|
|
176
176
|
construct: 'interface',
|
|
177
177
|
file: 'types/AgentSessionEvent.ts',
|
|
@@ -116,7 +116,7 @@ export interface ComponentDeclarationProps {
|
|
|
116
116
|
/** Max width of the declaration panel (CSS value). Defaults to none. */
|
|
117
117
|
maxWidth?: string | number;
|
|
118
118
|
/** When set, shows a Verify control that calls back with the component id. */
|
|
119
|
-
onVerify?: (
|
|
119
|
+
onVerify?: (componentAlias: string) => void;
|
|
120
120
|
/** Live verification status for the selected component. */
|
|
121
121
|
verification?: ComponentVerificationState | null;
|
|
122
122
|
/** Start with the file path row visible (catalog / source-first views). */
|
|
@@ -411,7 +411,7 @@ export function ComponentDeclaration({
|
|
|
411
411
|
disabled={verifyBusy}
|
|
412
412
|
onClick={(e) => {
|
|
413
413
|
e.stopPropagation();
|
|
414
|
-
onVerify(component.
|
|
414
|
+
onVerify(component.alias);
|
|
415
415
|
}}
|
|
416
416
|
style={{
|
|
417
417
|
display: 'flex',
|
|
@@ -11,7 +11,7 @@ import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
|
11
11
|
import type { ReactNode } from 'react';
|
|
12
12
|
import { useTheme } from '@principal-ade/industry-theme';
|
|
13
13
|
import { IndustryMarkdownSlide } from 'themed-markdown';
|
|
14
|
-
import { ChevronDown, ChevronRight,
|
|
14
|
+
import { ChevronDown, ChevronRight, FileText, Folder } from 'lucide-react';
|
|
15
15
|
import {
|
|
16
16
|
constructBadgeColor,
|
|
17
17
|
constructBadgeLabel,
|
|
@@ -29,7 +29,7 @@ import { resolvePierreSyntaxThemeName } from '../pierre/pierreSyntaxTheme';
|
|
|
29
29
|
|
|
30
30
|
export interface ConstructsCatalogProps {
|
|
31
31
|
components: SubsystemComponent[];
|
|
32
|
-
onSelect?: (
|
|
32
|
+
onSelect?: (componentAlias: string) => void;
|
|
33
33
|
title?: string;
|
|
34
34
|
hideSidebar?: boolean;
|
|
35
35
|
description?: string;
|
|
@@ -39,7 +39,7 @@ export interface ConstructsCatalogProps {
|
|
|
39
39
|
renderFileViewer?: (file: string, opts?: SubsystemOpenFileOptions) => ReactNode;
|
|
40
40
|
renderFileView?: (component: SubsystemComponent) => ReactNode;
|
|
41
41
|
onFileSelect?: (file: string) => void;
|
|
42
|
-
onVerifyComponent?: (
|
|
42
|
+
onVerifyComponent?: (componentAlias: string) => void;
|
|
43
43
|
componentVerification?: ComponentVerificationState | null;
|
|
44
44
|
}
|
|
45
45
|
|
|
@@ -111,7 +111,7 @@ function resolveRelated(
|
|
|
111
111
|
const clean = ref.replace(/\(\)$/, '');
|
|
112
112
|
return components.find(
|
|
113
113
|
(c) =>
|
|
114
|
-
c.
|
|
114
|
+
c.alias === clean ||
|
|
115
115
|
c.name === clean ||
|
|
116
116
|
c.symbol === clean ||
|
|
117
117
|
c.symbol?.replace(/\(\)$/, '') === clean,
|
|
@@ -170,11 +170,11 @@ export function ConstructsCatalog({
|
|
|
170
170
|
.map((x) => x.c);
|
|
171
171
|
}, [components]);
|
|
172
172
|
|
|
173
|
-
const [
|
|
174
|
-
orderedComponents[0] ? [orderedComponents[0].
|
|
173
|
+
const [visibleAliases, setVisibleAliases] = useState<string[]>(() =>
|
|
174
|
+
orderedComponents[0] ? [orderedComponents[0].alias] : [],
|
|
175
175
|
);
|
|
176
|
-
const [
|
|
177
|
-
() => orderedComponents[0]?.
|
|
176
|
+
const [focusedAlias, setFocusedAlias] = useState<string | null>(
|
|
177
|
+
() => orderedComponents[0]?.alias ?? null,
|
|
178
178
|
);
|
|
179
179
|
const [descriptionVisible, setDescriptionVisible] = useState(false);
|
|
180
180
|
const [descToggleHover, setDescToggleHover] = useState(false);
|
|
@@ -186,12 +186,12 @@ export function ConstructsCatalog({
|
|
|
186
186
|
const [drawer, setDrawer] = useState<{ file: string; startLine?: number } | null>(
|
|
187
187
|
null,
|
|
188
188
|
);
|
|
189
|
-
const
|
|
190
|
-
|
|
189
|
+
const focusedAliasRef = useRef<string | null>(focusedAlias);
|
|
190
|
+
focusedAliasRef.current = focusedAlias;
|
|
191
191
|
|
|
192
|
-
const visibleSet = useMemo(() => new Set(
|
|
192
|
+
const visibleSet = useMemo(() => new Set(visibleAliases), [visibleAliases]);
|
|
193
193
|
const visibleComponents = useMemo(
|
|
194
|
-
() => orderedComponents.filter((c) => visibleSet.has(c.
|
|
194
|
+
() => orderedComponents.filter((c) => visibleSet.has(c.alias)),
|
|
195
195
|
[orderedComponents, visibleSet],
|
|
196
196
|
);
|
|
197
197
|
|
|
@@ -213,7 +213,7 @@ export function ConstructsCatalog({
|
|
|
213
213
|
}> = [];
|
|
214
214
|
const index = new Map<string, number>();
|
|
215
215
|
for (const c of visibleComponents) {
|
|
216
|
-
const key = c.purl || c.
|
|
216
|
+
const key = c.purl || c.alias;
|
|
217
217
|
let at = index.get(key);
|
|
218
218
|
if (at == null) {
|
|
219
219
|
at = groups.length;
|
|
@@ -227,17 +227,17 @@ export function ConstructsCatalog({
|
|
|
227
227
|
}, [visibleComponents]);
|
|
228
228
|
|
|
229
229
|
useEffect(() => {
|
|
230
|
-
if (!
|
|
231
|
-
document.getElementById(`construct-${
|
|
232
|
-
}, [
|
|
230
|
+
if (!focusedAlias) return;
|
|
231
|
+
document.getElementById(`construct-${focusedAlias}`)?.scrollIntoView({ block: 'nearest' });
|
|
232
|
+
}, [focusedAlias]);
|
|
233
233
|
|
|
234
234
|
useEffect(() => {
|
|
235
|
-
const last =
|
|
235
|
+
const last = visibleAliases[visibleAliases.length - 1];
|
|
236
236
|
if (!last) return;
|
|
237
237
|
document
|
|
238
238
|
.getElementById(`construct-signature-${last}`)
|
|
239
239
|
?.scrollIntoView({ block: 'nearest' });
|
|
240
|
-
}, [
|
|
240
|
+
}, [visibleAliases]);
|
|
241
241
|
|
|
242
242
|
// Keep the declaration whose file is open in view, so the source and its
|
|
243
243
|
// signature stay legible together.
|
|
@@ -249,47 +249,47 @@ export function ConstructsCatalog({
|
|
|
249
249
|
) ?? orderedComponents.find((c) => c.file === drawer.file);
|
|
250
250
|
if (!match) return;
|
|
251
251
|
document
|
|
252
|
-
.getElementById(`construct-signature-${match.
|
|
252
|
+
.getElementById(`construct-signature-${match.alias}`)
|
|
253
253
|
?.scrollIntoView({ block: 'nearest' });
|
|
254
254
|
}, [orderedComponents, drawer]);
|
|
255
255
|
|
|
256
|
-
// Drop
|
|
256
|
+
// Drop aliases that left the model; if nothing remains, show the first construct.
|
|
257
257
|
useEffect(() => {
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
const next = prev.filter((
|
|
258
|
+
const aliases = new Set(orderedComponents.map((c) => c.alias));
|
|
259
|
+
setVisibleAliases((prev) => {
|
|
260
|
+
const next = prev.filter((alias) => aliases.has(alias));
|
|
261
261
|
if (next.length > 0 || orderedComponents.length === 0) return next;
|
|
262
|
-
return [orderedComponents[0]!.
|
|
262
|
+
return [orderedComponents[0]!.alias];
|
|
263
263
|
});
|
|
264
|
-
|
|
265
|
-
if (prev &&
|
|
266
|
-
return orderedComponents[0]?.
|
|
264
|
+
setFocusedAlias((prev) => {
|
|
265
|
+
if (prev && aliases.has(prev)) return prev;
|
|
266
|
+
return orderedComponents[0]?.alias ?? null;
|
|
267
267
|
});
|
|
268
268
|
}, [orderedComponents]);
|
|
269
269
|
|
|
270
270
|
const toggle = useCallback(
|
|
271
|
-
(
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
prev.includes(
|
|
271
|
+
(alias: string) => {
|
|
272
|
+
setFocusedAlias(alias);
|
|
273
|
+
setVisibleAliases((prev) =>
|
|
274
|
+
prev.includes(alias) ? prev.filter((x) => x !== alias) : [...prev, alias],
|
|
275
275
|
);
|
|
276
|
-
onSelect?.(
|
|
276
|
+
onSelect?.(alias);
|
|
277
277
|
},
|
|
278
278
|
[onSelect],
|
|
279
279
|
);
|
|
280
280
|
|
|
281
281
|
const show = useCallback(
|
|
282
|
-
(
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
onSelect?.(
|
|
282
|
+
(alias: string) => {
|
|
283
|
+
setFocusedAlias(alias);
|
|
284
|
+
setVisibleAliases((prev) => (prev.includes(alias) ? prev : [...prev, alias]));
|
|
285
|
+
onSelect?.(alias);
|
|
286
286
|
},
|
|
287
287
|
[onSelect],
|
|
288
288
|
);
|
|
289
289
|
|
|
290
290
|
const focusAt = useCallback(
|
|
291
|
-
(
|
|
292
|
-
|
|
291
|
+
(alias: string) => {
|
|
292
|
+
setFocusedAlias(alias);
|
|
293
293
|
},
|
|
294
294
|
[],
|
|
295
295
|
);
|
|
@@ -307,17 +307,17 @@ export function ConstructsCatalog({
|
|
|
307
307
|
* all when they're already shown — mirroring a construct row toggle. */
|
|
308
308
|
const toggleFile = useCallback(
|
|
309
309
|
(comps: SubsystemComponent[]) => {
|
|
310
|
-
const
|
|
311
|
-
|
|
310
|
+
const aliases = comps.map((c) => c.alias);
|
|
311
|
+
setVisibleAliases((prev) => {
|
|
312
312
|
const set = new Set(prev);
|
|
313
|
-
const allVisible =
|
|
314
|
-
|
|
315
|
-
return orderedComponents.filter((c) => set.has(c.
|
|
313
|
+
const allVisible = aliases.every((alias) => set.has(alias));
|
|
314
|
+
aliases.forEach((alias) => (allVisible ? set.delete(alias) : set.add(alias)));
|
|
315
|
+
return orderedComponents.filter((c) => set.has(c.alias)).map((c) => c.alias);
|
|
316
316
|
});
|
|
317
317
|
const first = comps[0];
|
|
318
318
|
if (first) {
|
|
319
|
-
|
|
320
|
-
onSelect?.(first.
|
|
319
|
+
setFocusedAlias(first.alias);
|
|
320
|
+
onSelect?.(first.alias);
|
|
321
321
|
}
|
|
322
322
|
},
|
|
323
323
|
[orderedComponents, onSelect],
|
|
@@ -380,8 +380,8 @@ export function ConstructsCatalog({
|
|
|
380
380
|
</li>
|
|
381
381
|
);
|
|
382
382
|
}
|
|
383
|
-
const
|
|
384
|
-
const allVisible =
|
|
383
|
+
const aliases = child.components.map((c) => c.alias);
|
|
384
|
+
const allVisible = aliases.length > 0 && aliases.every((alias) => visibleSet.has(alias));
|
|
385
385
|
const hovered = hoveredRow === child.path;
|
|
386
386
|
const color = componentColor(child.components[0]!, pierreTheme);
|
|
387
387
|
return (
|
|
@@ -438,7 +438,7 @@ export function ConstructsCatalog({
|
|
|
438
438
|
color: allVisible ? color : muted,
|
|
439
439
|
}}
|
|
440
440
|
>
|
|
441
|
-
{
|
|
441
|
+
{aliases.length}
|
|
442
442
|
</span>
|
|
443
443
|
</button>
|
|
444
444
|
</li>
|
|
@@ -473,7 +473,7 @@ export function ConstructsCatalog({
|
|
|
473
473
|
(ref: string) => {
|
|
474
474
|
const comp = resolveRelated(components, ref);
|
|
475
475
|
if (!comp) return;
|
|
476
|
-
show(comp.
|
|
476
|
+
show(comp.alias);
|
|
477
477
|
},
|
|
478
478
|
[components, show],
|
|
479
479
|
);
|
|
@@ -505,13 +505,13 @@ export function ConstructsCatalog({
|
|
|
505
505
|
if (orderedComponents.length === 0) return;
|
|
506
506
|
const idx = Math.max(
|
|
507
507
|
0,
|
|
508
|
-
orderedComponents.findIndex((c) => c.
|
|
508
|
+
orderedComponents.findIndex((c) => c.alias === focusedAliasRef.current),
|
|
509
509
|
);
|
|
510
510
|
const next =
|
|
511
511
|
orderedComponents[
|
|
512
512
|
(idx + delta + orderedComponents.length) % orderedComponents.length
|
|
513
513
|
]!;
|
|
514
|
-
focusAt(next.
|
|
514
|
+
focusAt(next.alias);
|
|
515
515
|
},
|
|
516
516
|
[orderedComponents, focusAt],
|
|
517
517
|
);
|
|
@@ -537,15 +537,15 @@ export function ConstructsCatalog({
|
|
|
537
537
|
moveFocus(-1);
|
|
538
538
|
} else if (e.key === 'Home') {
|
|
539
539
|
e.preventDefault();
|
|
540
|
-
if (orderedComponents[0]) focusAt(orderedComponents[0].
|
|
540
|
+
if (orderedComponents[0]) focusAt(orderedComponents[0].alias);
|
|
541
541
|
} else if (e.key === 'End') {
|
|
542
542
|
e.preventDefault();
|
|
543
543
|
const last = orderedComponents[orderedComponents.length - 1];
|
|
544
|
-
if (last) focusAt(last.
|
|
544
|
+
if (last) focusAt(last.alias);
|
|
545
545
|
} else if (e.key === ' ' || e.key === 'Enter') {
|
|
546
|
-
if (
|
|
546
|
+
if (focusedAliasRef.current) {
|
|
547
547
|
e.preventDefault();
|
|
548
|
-
toggle(
|
|
548
|
+
toggle(focusedAliasRef.current);
|
|
549
549
|
}
|
|
550
550
|
} else if (e.key === 'Escape' && drawer) {
|
|
551
551
|
setDrawer(null);
|
|
@@ -646,16 +646,12 @@ export function ConstructsCatalog({
|
|
|
646
646
|
padding: 0,
|
|
647
647
|
border: 'none',
|
|
648
648
|
borderRadius: 4,
|
|
649
|
-
background: descToggleHover ? theme.colors.border : 'transparent',
|
|
650
|
-
color: descToggleHover ? theme.colors.text : muted,
|
|
649
|
+
background: descriptionVisible || descToggleHover ? theme.colors.border : 'transparent',
|
|
650
|
+
color: descriptionVisible || descToggleHover ? theme.colors.text : muted,
|
|
651
651
|
cursor: 'pointer',
|
|
652
652
|
}}
|
|
653
653
|
>
|
|
654
|
-
{
|
|
655
|
-
<ChevronUp size={14} />
|
|
656
|
-
) : (
|
|
657
|
-
<ChevronDown size={14} />
|
|
658
|
-
)}
|
|
654
|
+
<FileText size={14} />
|
|
659
655
|
</button>
|
|
660
656
|
)}
|
|
661
657
|
</div>
|
|
@@ -728,7 +724,7 @@ export function ConstructsCatalog({
|
|
|
728
724
|
role="listbox"
|
|
729
725
|
aria-label="Constructs"
|
|
730
726
|
aria-multiselectable="true"
|
|
731
|
-
aria-activedescendant={
|
|
727
|
+
aria-activedescendant={focusedAlias ? `construct-${focusedAlias}` : undefined}
|
|
732
728
|
style={{
|
|
733
729
|
listStyle: 'none',
|
|
734
730
|
margin: 0,
|
|
@@ -739,8 +735,8 @@ export function ConstructsCatalog({
|
|
|
739
735
|
}}
|
|
740
736
|
>
|
|
741
737
|
{orderedComponents.map((c) => {
|
|
742
|
-
const visible = visibleSet.has(c.
|
|
743
|
-
const focused = c.
|
|
738
|
+
const visible = visibleSet.has(c.alias);
|
|
739
|
+
const focused = c.alias === focusedAlias;
|
|
744
740
|
const color = componentColor(c, pierreTheme);
|
|
745
741
|
const badgeColor = constructBadgeColor(c) ?? color;
|
|
746
742
|
const badgeLabel = constructBadgeLabel(c);
|
|
@@ -757,19 +753,19 @@ export function ConstructsCatalog({
|
|
|
757
753
|
!!c.file &&
|
|
758
754
|
c.file === drawer.file &&
|
|
759
755
|
c.declarationRef?.startLine === drawer.startLine;
|
|
760
|
-
const hovered = hoveredRow === c.
|
|
756
|
+
const hovered = hoveredRow === c.alias;
|
|
761
757
|
return (
|
|
762
|
-
<li key={c.
|
|
758
|
+
<li key={c.alias} role="presentation">
|
|
763
759
|
<button
|
|
764
760
|
type="button"
|
|
765
|
-
id={`construct-${c.
|
|
761
|
+
id={`construct-${c.alias}`}
|
|
766
762
|
role="option"
|
|
767
763
|
aria-selected={visible}
|
|
768
|
-
data-testid={`construct-row-${c.
|
|
769
|
-
onClick={() => toggle(c.
|
|
770
|
-
onMouseEnter={() => setHoveredRow(c.
|
|
764
|
+
data-testid={`construct-row-${c.alias}`}
|
|
765
|
+
onClick={() => toggle(c.alias)}
|
|
766
|
+
onMouseEnter={() => setHoveredRow(c.alias)}
|
|
771
767
|
onMouseLeave={() =>
|
|
772
|
-
setHoveredRow((h) => (h === c.
|
|
768
|
+
setHoveredRow((h) => (h === c.alias ? null : h))
|
|
773
769
|
}
|
|
774
770
|
style={{
|
|
775
771
|
display: 'flex',
|
|
@@ -938,8 +934,8 @@ export function ConstructsCatalog({
|
|
|
938
934
|
c.declarationRef?.startLine === drawer.startLine;
|
|
939
935
|
return (
|
|
940
936
|
<div
|
|
941
|
-
key={c.
|
|
942
|
-
id={`construct-signature-${c.
|
|
937
|
+
key={c.alias}
|
|
938
|
+
id={`construct-signature-${c.alias}`}
|
|
943
939
|
style={{
|
|
944
940
|
border: `1px solid ${lineOpen ? accentColor : theme.colors.border}`,
|
|
945
941
|
boxShadow: lineOpen ? `0 0 0 1px ${accentColor}` : undefined,
|
|
@@ -959,7 +955,7 @@ export function ConstructsCatalog({
|
|
|
959
955
|
showRepoIdentity={false}
|
|
960
956
|
fileBadgeChrome={!!fileViewer}
|
|
961
957
|
verification={
|
|
962
|
-
componentVerification &&
|
|
958
|
+
componentVerification && focusedAlias === c.alias
|
|
963
959
|
? componentVerification
|
|
964
960
|
: undefined
|
|
965
961
|
}
|