@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.
Files changed (49) hide show
  1. package/dist/stories/Subsystem/ComponentGraph/fixtures.d.ts +1 -1
  2. package/dist/stories/Subsystem/ComponentGraph/fixtures.d.ts.map +1 -1
  3. package/dist/stories/Subsystem/ComponentGraph/fixtures.js +7 -7
  4. package/dist/stories/Subsystem/ComponentGraph/fixtures.js.map +1 -1
  5. package/dist/subsystem/ComponentDeclaration.d.ts +1 -1
  6. package/dist/subsystem/ComponentDeclaration.d.ts.map +1 -1
  7. package/dist/subsystem/ComponentDeclaration.js +1 -1
  8. package/dist/subsystem/ComponentDeclaration.js.map +1 -1
  9. package/dist/subsystem/ConstructsCatalog.d.ts +2 -2
  10. package/dist/subsystem/ConstructsCatalog.d.ts.map +1 -1
  11. package/dist/subsystem/ConstructsCatalog.js +62 -62
  12. package/dist/subsystem/ConstructsCatalog.js.map +1 -1
  13. package/dist/subsystem/SubsystemComponentGraph.d.ts +2 -2
  14. package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
  15. package/dist/subsystem/SubsystemComponentGraph.js +27 -27
  16. package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
  17. package/dist/subsystem/model.d.ts +11 -5
  18. package/dist/subsystem/model.d.ts.map +1 -1
  19. package/dist/subsystem/model.js +65 -36
  20. package/dist/subsystem/model.js.map +1 -1
  21. package/dist/subsystem/nodes.d.ts +3 -3
  22. package/dist/subsystem/nodes.d.ts.map +1 -1
  23. package/dist/subsystem/nodes.js +3 -3
  24. package/dist/subsystem/nodes.js.map +1 -1
  25. package/package.json +1 -1
  26. package/src/stories/Subsystem/ComponentDeclarationAudit.stories.tsx +13 -13
  27. package/src/stories/Subsystem/ComponentGraph/Appearance.stories.tsx +17 -17
  28. package/src/stories/Subsystem/ComponentGraph/Basics.stories.tsx +2 -2
  29. package/src/stories/Subsystem/ComponentGraph/Captures.stories.tsx +3 -3
  30. package/src/stories/Subsystem/ComponentGraph/Constructs.stories.tsx +6 -6
  31. package/src/stories/Subsystem/ComponentGraph/CustomEntities.stories.tsx +8 -8
  32. package/src/stories/Subsystem/ComponentGraph/DetailPanel.stories.tsx +11 -11
  33. package/src/stories/Subsystem/ComponentGraph/Flows.stories.tsx +6 -6
  34. package/src/stories/Subsystem/ComponentGraph/FrameworkStereotype.stories.tsx +10 -10
  35. package/src/stories/Subsystem/ComponentGraph/ModuleBadges.stories.tsx +7 -7
  36. package/src/stories/Subsystem/ComponentGraph/Modules.stories.tsx +17 -17
  37. package/src/stories/Subsystem/ComponentGraph/NodeAnatomy.stories.tsx +16 -16
  38. package/src/stories/Subsystem/ComponentGraph/Packages.stories.tsx +12 -12
  39. package/src/stories/Subsystem/ComponentGraph/Processes.stories.tsx +1 -1
  40. package/src/stories/Subsystem/ComponentGraph/Proposed.stories.tsx +10 -10
  41. package/src/stories/Subsystem/ComponentGraph/Scenarios.stories.tsx +69 -69
  42. package/src/stories/Subsystem/ComponentGraph/Spotlights.stories.tsx +27 -27
  43. package/src/stories/Subsystem/ComponentGraph/fixtures.ts +8 -8
  44. package/src/subsystem/ComponentDeclaration.tsx +2 -2
  45. package/src/subsystem/ConstructsCatalog.tsx +71 -75
  46. package/src/subsystem/SubsystemComponentGraph.tsx +28 -28
  47. package/src/subsystem/model.test.ts +97 -51
  48. package/src/subsystem/model.ts +77 -41
  49. 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
- id: 'alias-status',
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
- id: 'ipc-entry',
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
- id: 'enum-phase',
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
- id: 'create',
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
- id: 'analysis-view',
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
- id: 'drawings-host',
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
- id: 'http-entry',
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
- id: 'cache',
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
- id: 'method-put',
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
- id: 'store',
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
- id: 'agents',
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
- id: 'github',
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.id)),
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.id));
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.id);
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.id}
372
+ key={component.alias}
373
373
  type="button"
374
- onClick={() => toggle(component.id)}
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.id)))}
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.id}>
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
- id: 'store',
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
- id: 'cache',
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
- id: 'get',
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
- id: 'ipc-entry',
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
- id: 'anatomy-method',
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
- id: 'anatomy-alias',
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
- id: 'anatomy-enum',
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.id}>
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<[id: string, name: string, construct: SubsystemComponent['construct'], file: string, purl: string, purpose?: string, symbol?: string, declaration?: GraphifyComponentDetail]>,
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(([id, name, construct, file, purl, purpose, symbol, declaration]) => ({
19
- id,
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
- id: 'v1',
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
- id: 'v2',
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
- id: 'input',
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
- id: 'acc',
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
- id: 'out',
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?: (componentId: string) => void;
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.id);
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, ChevronUp, FileText, Folder } from 'lucide-react';
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?: (componentId: string) => void;
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?: (componentId: string) => void;
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.id === clean ||
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 [visibleIds, setVisibleIds] = useState<string[]>(() =>
174
- orderedComponents[0] ? [orderedComponents[0].id] : [],
173
+ const [visibleAliases, setVisibleAliases] = useState<string[]>(() =>
174
+ orderedComponents[0] ? [orderedComponents[0].alias] : [],
175
175
  );
176
- const [focusedId, setFocusedId] = useState<string | null>(
177
- () => orderedComponents[0]?.id ?? null,
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 focusedRef = useRef<string | null>(focusedId);
190
- focusedRef.current = focusedId;
189
+ const focusedAliasRef = useRef<string | null>(focusedAlias);
190
+ focusedAliasRef.current = focusedAlias;
191
191
 
192
- const visibleSet = useMemo(() => new Set(visibleIds), [visibleIds]);
192
+ const visibleSet = useMemo(() => new Set(visibleAliases), [visibleAliases]);
193
193
  const visibleComponents = useMemo(
194
- () => orderedComponents.filter((c) => visibleSet.has(c.id)),
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.id;
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 (!focusedId) return;
231
- document.getElementById(`construct-${focusedId}`)?.scrollIntoView({ block: 'nearest' });
232
- }, [focusedId]);
230
+ if (!focusedAlias) return;
231
+ document.getElementById(`construct-${focusedAlias}`)?.scrollIntoView({ block: 'nearest' });
232
+ }, [focusedAlias]);
233
233
 
234
234
  useEffect(() => {
235
- const last = visibleIds[visibleIds.length - 1];
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
- }, [visibleIds]);
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.id}`)
252
+ .getElementById(`construct-signature-${match.alias}`)
253
253
  ?.scrollIntoView({ block: 'nearest' });
254
254
  }, [orderedComponents, drawer]);
255
255
 
256
- // Drop ids that left the model; if nothing remains, show the first construct.
256
+ // Drop aliases that left the model; if nothing remains, show the first construct.
257
257
  useEffect(() => {
258
- const ids = new Set(orderedComponents.map((c) => c.id));
259
- setVisibleIds((prev) => {
260
- const next = prev.filter((id) => ids.has(id));
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]!.id];
262
+ return [orderedComponents[0]!.alias];
263
263
  });
264
- setFocusedId((prev) => {
265
- if (prev && ids.has(prev)) return prev;
266
- return orderedComponents[0]?.id ?? null;
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
- (id: string) => {
272
- setFocusedId(id);
273
- setVisibleIds((prev) =>
274
- prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id],
271
+ (alias: string) => {
272
+ setFocusedAlias(alias);
273
+ setVisibleAliases((prev) =>
274
+ prev.includes(alias) ? prev.filter((x) => x !== alias) : [...prev, alias],
275
275
  );
276
- onSelect?.(id);
276
+ onSelect?.(alias);
277
277
  },
278
278
  [onSelect],
279
279
  );
280
280
 
281
281
  const show = useCallback(
282
- (id: string) => {
283
- setFocusedId(id);
284
- setVisibleIds((prev) => (prev.includes(id) ? prev : [...prev, id]));
285
- onSelect?.(id);
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
- (id: string) => {
292
- setFocusedId(id);
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 ids = comps.map((c) => c.id);
311
- setVisibleIds((prev) => {
310
+ const aliases = comps.map((c) => c.alias);
311
+ setVisibleAliases((prev) => {
312
312
  const set = new Set(prev);
313
- const allVisible = ids.every((id) => set.has(id));
314
- ids.forEach((id) => (allVisible ? set.delete(id) : set.add(id)));
315
- return orderedComponents.filter((c) => set.has(c.id)).map((c) => c.id);
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
- setFocusedId(first.id);
320
- onSelect?.(first.id);
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 ids = child.components.map((c) => c.id);
384
- const allVisible = ids.length > 0 && ids.every((id) => visibleSet.has(id));
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
- {ids.length}
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.id);
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.id === focusedRef.current),
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.id);
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].id);
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.id);
544
+ if (last) focusAt(last.alias);
545
545
  } else if (e.key === ' ' || e.key === 'Enter') {
546
- if (focusedRef.current) {
546
+ if (focusedAliasRef.current) {
547
547
  e.preventDefault();
548
- toggle(focusedRef.current);
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
- {descriptionVisible ? (
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={focusedId ? `construct-${focusedId}` : undefined}
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.id);
743
- const focused = c.id === focusedId;
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.id;
756
+ const hovered = hoveredRow === c.alias;
761
757
  return (
762
- <li key={c.id} role="presentation">
758
+ <li key={c.alias} role="presentation">
763
759
  <button
764
760
  type="button"
765
- id={`construct-${c.id}`}
761
+ id={`construct-${c.alias}`}
766
762
  role="option"
767
763
  aria-selected={visible}
768
- data-testid={`construct-row-${c.id}`}
769
- onClick={() => toggle(c.id)}
770
- onMouseEnter={() => setHoveredRow(c.id)}
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.id ? null : h))
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.id}
942
- id={`construct-signature-${c.id}`}
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 && focusedId === c.id
958
+ componentVerification && focusedAlias === c.alias
963
959
  ? componentVerification
964
960
  : undefined
965
961
  }