@principal-ai/subsystems-react 0.49.0 → 0.51.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.
Files changed (97) hide show
  1. package/dist/index.d.ts +8 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +5 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/pierre/PierreFileView.d.ts +1 -1
  6. package/dist/pierre/PierreFileView.d.ts.map +1 -1
  7. package/dist/pierre/PierreFileView.js +2 -1
  8. package/dist/pierre/PierreFileView.js.map +1 -1
  9. package/dist/pierre/PierreSnippetView.d.ts +1 -1
  10. package/dist/pierre/PierreSnippetView.d.ts.map +1 -1
  11. package/dist/pierre/PierreSnippetView.js +2 -1
  12. package/dist/pierre/PierreSnippetView.js.map +1 -1
  13. package/dist/pierre/PierreTrailCodeView.d.ts +7 -1
  14. package/dist/pierre/PierreTrailCodeView.d.ts.map +1 -1
  15. package/dist/pierre/PierreTrailCodeView.js +71 -18
  16. package/dist/pierre/PierreTrailCodeView.js.map +1 -1
  17. package/dist/pierre/pierreBackground.d.ts +36 -2
  18. package/dist/pierre/pierreBackground.d.ts.map +1 -1
  19. package/dist/pierre/pierreBackground.js +40 -13
  20. package/dist/pierre/pierreBackground.js.map +1 -1
  21. package/dist/pierre/visibleStep.d.ts +48 -0
  22. package/dist/pierre/visibleStep.d.ts.map +1 -0
  23. package/dist/pierre/visibleStep.js +53 -0
  24. package/dist/pierre/visibleStep.js.map +1 -0
  25. package/dist/stories/Subsystem/C4Graph/c4Associations.fixture.d.ts +95 -0
  26. package/dist/stories/Subsystem/C4Graph/c4Associations.fixture.d.ts.map +1 -0
  27. package/dist/stories/Subsystem/C4Graph/c4Associations.fixture.js +340 -0
  28. package/dist/stories/Subsystem/C4Graph/c4Associations.fixture.js.map +1 -0
  29. package/dist/stories/Subsystem/C4Graph/c4Fixture.d.ts +4 -0
  30. package/dist/stories/Subsystem/C4Graph/c4Fixture.d.ts.map +1 -1
  31. package/dist/stories/Subsystem/C4Graph/c4Fixture.js +7334 -1
  32. package/dist/stories/Subsystem/C4Graph/c4Fixture.js.map +1 -1
  33. package/dist/stories/Subsystem/C4Graph/generateFixture.d.ts +2 -0
  34. package/dist/stories/Subsystem/C4Graph/generateFixture.d.ts.map +1 -0
  35. package/dist/stories/Subsystem/C4Graph/generateFixture.js +145 -0
  36. package/dist/stories/Subsystem/C4Graph/generateFixture.js.map +1 -0
  37. package/dist/subsystem/C4Graph.d.ts.map +1 -1
  38. package/dist/subsystem/C4Graph.js +16 -60
  39. package/dist/subsystem/C4Graph.js.map +1 -1
  40. package/dist/subsystem/C4NodeCard.d.ts +84 -0
  41. package/dist/subsystem/C4NodeCard.d.ts.map +1 -0
  42. package/dist/subsystem/C4NodeCard.js +229 -0
  43. package/dist/subsystem/C4NodeCard.js.map +1 -0
  44. package/dist/subsystem/DiagnosticToggle.d.ts +8 -1
  45. package/dist/subsystem/DiagnosticToggle.d.ts.map +1 -1
  46. package/dist/subsystem/DiagnosticToggle.js +40 -6
  47. package/dist/subsystem/DiagnosticToggle.js.map +1 -1
  48. package/dist/subsystem/FileDrawer.d.ts +10 -1
  49. package/dist/subsystem/FileDrawer.d.ts.map +1 -1
  50. package/dist/subsystem/FileDrawer.js +61 -40
  51. package/dist/subsystem/FileDrawer.js.map +1 -1
  52. package/dist/subsystem/SubsystemComponentGraph.d.ts +6 -0
  53. package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
  54. package/dist/subsystem/SubsystemComponentGraph.js +21 -10
  55. package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
  56. package/dist/subsystem/c4Associations.d.ts +186 -0
  57. package/dist/subsystem/c4Associations.d.ts.map +1 -0
  58. package/dist/subsystem/c4Associations.js +284 -0
  59. package/dist/subsystem/c4Associations.js.map +1 -0
  60. package/dist/subsystem/model.d.ts +25 -1
  61. package/dist/subsystem/model.d.ts.map +1 -1
  62. package/dist/subsystem/model.js +15 -0
  63. package/dist/subsystem/model.js.map +1 -1
  64. package/dist/subsystem/nodes.d.ts.map +1 -1
  65. package/dist/subsystem/nodes.js +78 -7
  66. package/dist/subsystem/nodes.js.map +1 -1
  67. package/dist/subsystem/toC4.d.ts +119 -0
  68. package/dist/subsystem/toC4.d.ts.map +1 -1
  69. package/dist/subsystem/toC4.js +95 -5
  70. package/dist/subsystem/toC4.js.map +1 -1
  71. package/package.json +2 -2
  72. package/src/index.ts +39 -0
  73. package/src/pierre/PierreFileView.tsx +2 -1
  74. package/src/pierre/PierreSnippetView.tsx +2 -1
  75. package/src/pierre/PierreTrailCodeView.tsx +81 -22
  76. package/src/pierre/pierreBackground.ts +62 -12
  77. package/src/pierre/visibleStep.test.ts +108 -0
  78. package/src/pierre/visibleStep.ts +76 -0
  79. package/src/stories/Subsystem/C4Graph/C4Associations.stories.tsx +273 -0
  80. package/src/stories/Subsystem/C4Graph/C4NodeGallery.stories.tsx +325 -0
  81. package/src/stories/Subsystem/C4Graph/c4Associations.fixture.ts +344 -0
  82. package/src/stories/Subsystem/C4Graph/c4Fixture.ts +7337 -4
  83. package/src/stories/Subsystem/C4Graph/generateFixture.ts +161 -0
  84. package/src/stories/Subsystem/ComponentGraph/Deprecated.stories.tsx +358 -0
  85. package/src/stories/Subsystem/ComponentGraph/Diagnostics.stories.tsx +27 -2
  86. package/src/stories/Subsystem/ComponentGraph/{Flows.stories.tsx → Trails.stories.tsx} +10 -4
  87. package/src/subsystem/C4Graph.tsx +51 -92
  88. package/src/subsystem/C4NodeCard.test.ts +196 -0
  89. package/src/subsystem/C4NodeCard.tsx +307 -0
  90. package/src/subsystem/DiagnosticToggle.tsx +58 -4
  91. package/src/subsystem/FileDrawer.tsx +112 -63
  92. package/src/subsystem/SubsystemComponentGraph.tsx +29 -5
  93. package/src/subsystem/c4Associations.test.ts +484 -0
  94. package/src/subsystem/c4Associations.ts +398 -0
  95. package/src/subsystem/model.ts +34 -1
  96. package/src/subsystem/nodes.tsx +124 -5
  97. package/src/subsystem/toC4.ts +210 -5
@@ -1 +1 @@
1
- {"version":3,"file":"toC4.d.ts","sourceRoot":"","sources":["../../src/subsystem/toC4.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAE1E,4DAA4D;AAC5D,MAAM,MAAM,MAAM,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,OAAO,CAAC;AAEjF,+EAA+E;AAC/E,MAAM,MAAM,MAAM,GAAG,WAAW,GAAG,WAAW,CAAC;AAE/C,mDAAmD;AACnD,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,qEAAqE;IACrE,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,wEAAwE;IACxE,OAAO,EAAE,OAAO,CAAC;IACjB,kEAAkE;IAClE,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,wEAAwE;AACxE,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,QAAQ,GAAG,WAAW,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,+DAA+D;AAC/D,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,OAAO,EAAE,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4EAA4E;IAC5E,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAC1C;AAKD,+DAA+D;AAC/D,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAElE;AAED,gEAAgE;AAChE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIlD;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,GAAG,EAAE,sBAAsB,GAAG,MAAM,GAAG,SAAS,CAM7E;AAaD;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,GAAG,EAAE,sBAAsB,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CA6IpF"}
1
+ {"version":3,"file":"toC4.d.ts","sourceRoot":"","sources":["../../src/subsystem/toC4.ts"],"names":[],"mappings":"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"}
@@ -25,6 +25,40 @@ const UNASSIGNED = '(unassigned)';
25
25
  export function isGroundedComponent(c) {
26
26
  return c.construct !== 'external' && c.construct !== 'custom_entity';
27
27
  }
28
+ /**
29
+ * Index confirmed associations by the derived keys they claim.
30
+ *
31
+ * First writer wins on a contested key so the result does not depend on array
32
+ * order, and the loser is reported so the conflict is visible rather than
33
+ * silently dropped.
34
+ */
35
+ export function indexAssociations(associations) {
36
+ const byKey = new Map();
37
+ const contested = [];
38
+ if (!associations)
39
+ return { byKey, contested };
40
+ // Stable order: accepted before proposed, so a confirmed decision outranks a
41
+ // speculative one for the same key.
42
+ const ordered = [...associations].sort((a, b) => {
43
+ const rank = (s) => (s === 'accepted' ? 0 : s === 'proposed' ? 1 : 2);
44
+ return rank(a.state) - rank(b.state) || a.id.localeCompare(b.id);
45
+ });
46
+ for (const a of ordered) {
47
+ for (const key of a.sourceKeys) {
48
+ const existing = byKey.get(key);
49
+ if (existing && existing.id !== a.id) {
50
+ contested.push({ key, kept: existing.id, dropped: a.id });
51
+ continue;
52
+ }
53
+ byKey.set(key, a);
54
+ }
55
+ }
56
+ return { byKey, contested };
57
+ }
58
+ /** True when a resolved association means this box should not be drawn. */
59
+ function isSuppressed(a) {
60
+ return a?.state === 'rejected';
61
+ }
28
62
  /** `owner/name` from a purl, trimmed of scheme and fragment. */
29
63
  export function labelFromPurl(purl) {
30
64
  const base = purl.replace(/^external:/, '').split('#')[0]?.trim() ?? '';
@@ -60,6 +94,12 @@ export function toC4(doc, options = {}) {
60
94
  const view = options.view ?? 'container';
61
95
  const repoKey = options.repoKey ?? deriveRepoKey(doc) ?? 'system';
62
96
  const systemId = `system:${repoKey}`;
97
+ const { byKey: assocByKey } = indexAssociations(options.associations);
98
+ // Id -> association, so a container frame can be labelled from its
99
+ // association even when no node exists for it (component view).
100
+ const assocById = new Map();
101
+ for (const a of options.associations ?? [])
102
+ assocById.set(a.id, a);
63
103
  const system = {
64
104
  id: systemId,
65
105
  kind: 'system',
@@ -73,12 +113,32 @@ export function toC4(doc, options = {}) {
73
113
  const ownerOf = new Map();
74
114
  const containerNodeId = (key) => `container:${key}`;
75
115
  const componentNodeId = (alias) => `component:${alias}`;
116
+ /**
117
+ * Resolve one derived container key into the box that should carry it.
118
+ *
119
+ * An accepted association wins: its id becomes the node id, so every key it
120
+ * claims rolls into one node (that is the dedup), and its label / type /
121
+ * technology / description become the node's presentation. A rejected one
122
+ * means the box is not drawn at all. Unclaimed keys keep the raw derived
123
+ * behaviour, so the projection is still useful with zero associations.
124
+ */
125
+ const resolveContainer = (key) => {
126
+ const assoc = assocByKey.get(key);
127
+ if (assoc && assoc.level === 'container') {
128
+ return { id: assoc.id, label: assoc.label, assoc };
129
+ }
130
+ if (view === 'component') {
131
+ return { id: containerNodeId(key), label: key === UNASSIGNED ? 'Unassigned' : key, assoc: undefined };
132
+ }
133
+ return { id: containerNodeId(key), label: key === UNASSIGNED ? 'Unassigned' : key, assoc: undefined };
134
+ };
76
135
  for (const c of doc.components) {
77
136
  let id;
78
137
  let kind;
79
138
  let label;
80
139
  let parentId;
81
140
  let key;
141
+ let assoc;
82
142
  if (c.construct === 'external') {
83
143
  key = externalKey(c);
84
144
  id = `external:${key}`;
@@ -93,17 +153,25 @@ export function toC4(doc, options = {}) {
93
153
  }
94
154
  else if (view === 'component') {
95
155
  key = containerKey(c);
156
+ const resolved = resolveContainer(key);
157
+ if (isSuppressed(resolved.assoc))
158
+ continue;
96
159
  id = componentNodeId(c.alias);
97
160
  kind = 'component';
98
161
  label = c.name;
99
- parentId = containerNodeId(key);
162
+ parentId = resolved.id;
163
+ assoc = undefined; // the component itself has no association
100
164
  }
101
165
  else {
102
166
  key = containerKey(c);
103
- id = containerNodeId(key);
167
+ const resolved = resolveContainer(key);
168
+ if (isSuppressed(resolved.assoc))
169
+ continue;
170
+ id = resolved.id;
104
171
  kind = 'container';
105
- label = key === UNASSIGNED ? 'Unassigned' : key;
172
+ label = resolved.label;
106
173
  parentId = systemId;
174
+ assoc = resolved.assoc;
107
175
  }
108
176
  ownerOf.set(c.alias, id);
109
177
  let node = nodes.get(id);
@@ -117,7 +185,12 @@ export function toC4(doc, options = {}) {
117
185
  members: [],
118
186
  constructs: [],
119
187
  isStore: false,
188
+ // A merged box is built from several derived keys. Track them as a
189
+ // set so the confirm UI can show what was folded together — without
190
+ // this, the accumulation would run once per member, not per key.
191
+ ...(assoc && assoc.sourceKeys.length > 1 ? { sourceKeys: [...assoc.sourceKeys] } : {}),
120
192
  ...(kind === 'component' ? { component: c } : {}),
193
+ ...(assoc ? { decoration: toDecoration(assoc) } : {}),
121
194
  };
122
195
  nodes.set(id, node);
123
196
  }
@@ -133,6 +206,9 @@ export function toC4(doc, options = {}) {
133
206
  }
134
207
  // --- Groups (compound frames) ------------------------------------------
135
208
  const groups = [];
209
+ const labelForContainerId = (id) => nodes.get(id)?.label ??
210
+ assocById.get(id)?.label ??
211
+ (id.startsWith('container:') ? id.slice('container:'.length) : id);
136
212
  if (view === 'container') {
137
213
  const containerIds = [...nodes.values()].filter((n) => n.kind === 'container').map((n) => n.id);
138
214
  if (containerIds.length > 0) {
@@ -150,8 +226,7 @@ export function toC4(doc, options = {}) {
150
226
  }
151
227
  const containerIds = [];
152
228
  for (const [id, memberIds] of byContainer) {
153
- const label = id.slice('container:'.length);
154
- groups.push({ id, kind: 'container', label: label === UNASSIGNED ? 'Unassigned' : label, parentId: systemId, memberIds });
229
+ groups.push({ id, kind: 'container', label: labelForContainerId(id), parentId: systemId, memberIds });
155
230
  containerIds.push(id);
156
231
  }
157
232
  if (containerIds.length > 0) {
@@ -163,6 +238,10 @@ export function toC4(doc, options = {}) {
163
238
  const addEdge = (bucket, source, target, kind, mechanism) => {
164
239
  if (!source || !target || source === target)
165
240
  return;
241
+ // An endpoint whose box was rejected has no owner; the edge cannot be
242
+ // drawn, so drop it rather than inventing a dangling arrow.
243
+ if (!nodes.has(source) || !nodes.has(target))
244
+ return;
166
245
  const key = `${source}\0${target}`;
167
246
  let edge = bucket.get(key);
168
247
  if (!edge) {
@@ -189,4 +268,15 @@ export function toC4(doc, options = {}) {
189
268
  edges,
190
269
  };
191
270
  }
271
+ /** Project an association down to the attributes a node carries. */
272
+ function toDecoration(a) {
273
+ return {
274
+ id: a.id,
275
+ label: a.label,
276
+ type: a.type,
277
+ technology: a.technology,
278
+ description: a.description,
279
+ state: a.state,
280
+ };
281
+ }
192
282
  //# sourceMappingURL=toC4.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"toC4.js","sourceRoot":"","sources":["../../src/subsystem/toC4.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"toC4.js","sourceRoot":"","sources":["../../src/subsystem/toC4.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@principal-ai/subsystems-react",
3
- "version": "0.49.0",
4
- "description": "React UI for Subsystem Models — graph, Pierre code views, and session-event feeds",
3
+ "version": "0.51.0",
4
+ "description": "React UI for Subsystem Models \u2014 graph, Pierre code views, and session-event feeds",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
package/src/index.ts CHANGED
@@ -239,6 +239,45 @@ export type {
239
239
  SubsystemSignatureClaim,
240
240
  SubsystemSignatureParameter,
241
241
  } from './subsystem/model';
242
+ // --- C4 projection + the confirmed association layer ------------------
243
+ export { C4Graph } from './subsystem/C4Graph';
244
+ export type { C4GraphProps } from './subsystem/C4Graph';
245
+ export { C4NodeCard, nodeStyle, nodeSubtitle, nodeTag, nodeMissing, NODE_W, NODE_H } from './subsystem/C4NodeCard';
246
+ export type { C4NodeCardProps, NodeStyle } from './subsystem/C4NodeCard';
247
+ export {
248
+ toC4,
249
+ isGroundedComponent,
250
+ labelFromPurl,
251
+ deriveRepoKey,
252
+ indexAssociations,
253
+ } from './subsystem/toC4';
254
+ export type {
255
+ C4Kind,
256
+ C4View,
257
+ C4Node,
258
+ C4Group,
259
+ C4Edge,
260
+ C4Model,
261
+ C4Decoration,
262
+ C4ElementType,
263
+ C4Association,
264
+ C4AssociationState,
265
+ ToC4Options,
266
+ } from './subsystem/toC4';
267
+ export {
268
+ deriveConcerns,
269
+ suggestMerges,
270
+ buildAssociations,
271
+ mergeAssociations,
272
+ associationWeight,
273
+ summarizeAssociations,
274
+ } from './subsystem/c4Associations';
275
+ export type {
276
+ C4AssociationSet,
277
+ C4AssociationReason,
278
+ C4ConcernKind,
279
+ C4MemberView,
280
+ } from './subsystem/c4Associations';
242
281
  export {
243
282
  constructBadgeLabel,
244
283
  constructBadgeColor,
@@ -33,10 +33,11 @@ export function PierreFileView({
33
33
  filePath,
34
34
  fileName,
35
35
  readFile,
36
- background,
36
+ background: backgroundProp,
37
37
  focusLine,
38
38
  }: PierreFileViewProps) {
39
39
  const { theme } = useTheme();
40
+ const background = backgroundProp ?? theme.colors.background;
40
41
  const [contents, setContents] = useState<string | null>(null);
41
42
  const [error, setError] = useState<string | null>(null);
42
43
  // Pierre highlights on the main thread with no worker pool: a cold first
@@ -47,9 +47,10 @@ export function PierreSnippetView({
47
47
  focusLine,
48
48
  contextLines = 2,
49
49
  readFile,
50
- background,
50
+ background: backgroundProp,
51
51
  }: PierreSnippetViewProps) {
52
52
  const { theme } = useTheme();
53
+ const background = backgroundProp ?? theme.colors.background;
53
54
  const [contents, setContents] = useState<string | null>(null);
54
55
  const [error, setError] = useState<string | null>(null);
55
56
  // Warm the shared highlighter before mounting <File>; a cold first render
@@ -42,6 +42,7 @@ import type { SubsystemOpenFileOptions } from '../subsystem/declarationRef';
42
42
  import { buildPierreOptions, PIERRE_FILE_STYLE } from './pierreBackground';
43
43
  import { pierreLangForPath } from './pierreFileLang';
44
44
  import { resolvePierreSyntaxThemeName } from './pierreSyntaxTheme';
45
+ import { resolveVisibleStepIndex, type VisibleStepRect } from './visibleStep';
45
46
  import { fileUnavailableNotice, isFileUnavailableError } from './fileAvailability';
46
47
  import {
47
48
  remapSnippetLineNumbers,
@@ -67,6 +68,16 @@ function stepIndexFromItemContext(context: unknown): number | null {
67
68
  return Number.isFinite(index) ? index : null;
68
69
  }
69
70
 
71
+ /**
72
+ * Assumed height of the compact file header, in px.
73
+ *
74
+ * Must track the header chrome passed to `buildPierreOptions` (fontSizes[0] at
75
+ * line-height 1.15 plus 2px vertical padding) — CodeView sizes its
76
+ * virtualization window and sticky offset from this, and the library's own
77
+ * default of 44 is more than double the compact header.
78
+ */
79
+ const TRAIL_HEADER_HEIGHT = 18;
80
+
70
81
  /** Pointer/dotted-underline affordance for a clickable construct token. */
71
82
  function paintSymbolToken(el: HTMLElement | undefined, active: boolean): void {
72
83
  if (el == null) return;
@@ -88,6 +99,12 @@ export interface PierreTrailCodeViewProps {
88
99
  background?: string;
89
100
  /** Open the step's full source file (header button or double-click snippet body). */
90
101
  onOpenFile?: (path: string, opts?: SubsystemOpenFileOptions) => void;
102
+ /**
103
+ * Fires when scrolling brings a different step to the top of the view, with
104
+ * that step's index. Drives a progress readout that tracks the viewport
105
+ * rather than the selected step. Only fires on change, not per scroll event.
106
+ */
107
+ onVisibleStepChange?: (index: number) => void;
91
108
  /**
92
109
  * Aliases of components marked `proposed`. A step whose file can't be read
93
110
  * but whose endpoint is proposed reads as "planned" rather than "missing".
@@ -193,12 +210,15 @@ export function PierreTrailCodeView({
193
210
  contextLines = 8,
194
211
  background,
195
212
  onOpenFile,
213
+ onVisibleStepChange,
196
214
  proposedAliases,
197
215
  resolveSymbol,
198
216
  onSymbolClick,
199
217
  }: PierreTrailCodeViewProps) {
200
218
  const { theme, mode } = useTheme();
201
219
  const viewRef = useRef<CodeViewHandle<undefined>>(null);
220
+ // CodeView's scroll container — also the box we measure items against.
221
+ const containerRef = useRef<HTMLDivElement | null>(null);
202
222
  const [load, setLoad] = useState<FileLoadState>({ status: 'loading' });
203
223
 
204
224
  // Keep the newest symbol callbacks in refs so the Pierre options object stays
@@ -207,6 +227,43 @@ export function PierreTrailCodeView({
207
227
  resolveSymbolRef.current = resolveSymbol;
208
228
  const onSymbolClickRef = useRef(onSymbolClick);
209
229
  onSymbolClickRef.current = onSymbolClick;
230
+ const onVisibleStepChangeRef = useRef(onVisibleStepChange);
231
+ onVisibleStepChangeRef.current = onVisibleStepChange;
232
+ const visibleStepRef = useRef<number | null>(null);
233
+
234
+ // Report which step the viewport is on. The geometry lives in `visibleStep` so
235
+ // it can be tested — the answer depends on how tall the final snippet is
236
+ // relative to the viewport, which is not something to eyeball.
237
+ const reportVisibleStep = useCallback(() => {
238
+ const container = containerRef.current;
239
+ const viewer = viewRef.current?.getInstance();
240
+ if (container == null || viewer == null) return;
241
+ const rect = container.getBoundingClientRect();
242
+ const rects: VisibleStepRect[] = [];
243
+ for (const rendered of viewer.getRenderedItems()) {
244
+ const index = Number.parseInt(rendered.id.split(':').pop() ?? '', 10);
245
+ if (!Number.isFinite(index)) continue;
246
+ const itemRect = rendered.element.getBoundingClientRect();
247
+ rects.push({ index, top: itemRect.top, bottom: itemRect.bottom });
248
+ }
249
+ const next = resolveVisibleStepIndex(
250
+ {
251
+ top: rect.top,
252
+ bottom: rect.bottom,
253
+ scrollTop: container.scrollTop,
254
+ clientHeight: container.clientHeight,
255
+ scrollHeight: container.scrollHeight,
256
+ },
257
+ rects,
258
+ );
259
+ if (next == null || next === visibleStepRef.current) return;
260
+ visibleStepRef.current = next;
261
+ onVisibleStepChangeRef.current?.(next);
262
+ }, []);
263
+
264
+ const onCodeViewScroll = useCallback(() => {
265
+ reportVisibleStep();
266
+ }, [reportVisibleStep]);
210
267
 
211
268
  // A step onto a proposed component is planned work; label its missing file
212
269
  // accordingly instead of showing a bare "not found".
@@ -346,32 +403,19 @@ export function PierreTrailCodeView({
346
403
  context?: { item?: { id?: string } },
347
404
  ) => {
348
405
  const id = context?.item?.id;
406
+ // Item-level renders don't move the viewport, but the first paint does
407
+ // decide which step is on screen.
408
+ reportVisibleStep();
349
409
  if (id == null) return;
350
410
  const sliceStart = sliceStartByItemId.get(id);
351
411
  if (sliceStart == null) return;
352
412
  remapSnippetLineNumbers(node, sliceStart);
353
413
  },
354
- [sliceStartByItemId],
414
+ [sliceStartByItemId, reportVisibleStep],
355
415
  );
356
416
 
357
- const renderHeaderPrefix = useMemo(() => {
358
- return (item: CodeViewItem) => {
359
- const index = Number.parseInt(item.id.split(':').pop() ?? '', 10);
360
- if (!trail.steps[index]) return null;
361
- return (
362
- <span
363
- style={{
364
- fontFamily: theme.fonts.monospace,
365
- fontSize: theme.fontSizes[0],
366
- color: theme.colors.textSecondary,
367
- marginRight: 8,
368
- }}
369
- >
370
- {index + 1}.
371
- </span>
372
- );
373
- };
374
- }, [trail.steps, theme]);
417
+ // No step numbers in the file header: the sidebar already numbers the steps,
418
+ // and the drawer header carries a segmented progress readout.
375
419
 
376
420
  const renderHeaderMetadata = useMemo(() => {
377
421
  return (item: CodeViewItem) => {
@@ -495,15 +539,29 @@ export function PierreTrailCodeView({
495
539
  light: resolvePierreSyntaxThemeName('light'),
496
540
  },
497
541
  stickyHeaders: true,
542
+ // CodeView never injects `unsafeCSS` into its own shadow root, but it
543
+ // forwards the option to each file item (CODE_VIEW_FILE_OPTION_KEYS),
544
+ // which does — so this retints the file surfaces and headers. It cannot
545
+ // reach CodeView's host, hence the spread only carries
546
+ // PIERRE_BASE_OPTIONS and `disableFileHeader` must follow it.
547
+ ...buildPierreOptions(background, {
548
+ background: theme.colors.backgroundSecondary,
549
+ fontSize: `${theme.fontSizes[0]}px`,
550
+ lineHeight: '1.15',
551
+ padding: '2px 8px',
552
+ }),
498
553
  disableFileHeader: false,
554
+ // The library assumes a 44px header (DEFAULT_VIRTUAL_FILE_METRICS); the
555
+ // compact chrome above is ~18px. CodeView uses this for its
556
+ // virtualization window and sticky offset, so leaving it at 44 drifts.
557
+ itemMetrics: { diffHeaderHeight: TRAIL_HEADER_HEIGHT },
499
558
  layout: { paddingTop: 0, paddingBottom: 0, gap: 4 },
500
559
  onPostRender,
501
560
  ...(onOpenFile ? { onLineClick } : {}),
502
561
  ...symbolHandlers,
503
- ...(background ? buildPierreOptions(background) : {}),
504
562
  ...(mode === 'light' || mode === 'dark' ? { themeType: mode } : {}),
505
563
  };
506
- }, [background, mode, onPostRender, onOpenFile, onLineClick, symbolHandlers]);
564
+ }, [background, theme.colors.backgroundSecondary, theme.fontSizes, mode, onPostRender, onOpenFile, onLineClick, symbolHandlers]);
507
565
 
508
566
  useEffect(() => {
509
567
  if (load.status !== 'ready' || stepIndex == null) return;
@@ -549,10 +607,11 @@ export function PierreTrailCodeView({
549
607
  return (
550
608
  <CodeViewLoose
551
609
  ref={viewRef}
610
+ containerRef={containerRef}
552
611
  items={items}
553
612
  options={options}
613
+ onScroll={onCodeViewScroll}
554
614
  selectedLines={selectedLines}
555
- renderHeaderPrefix={renderHeaderPrefix}
556
615
  renderHeaderMetadata={renderHeaderMetadata}
557
616
  renderAnnotation={renderAnnotation}
558
617
  style={{ ...PIERRE_FILE_STYLE, height: '100%', overflow: 'auto' }}
@@ -6,28 +6,78 @@ export const PIERRE_BASE_OPTIONS = {
6
6
  disableFileHeader: true,
7
7
  } as const;
8
8
 
9
- export function buildBackgroundCSS(background: string): string {
9
+ /** Chrome overrides for the per-file header (`[data-diffs-header]`). */
10
+ export interface PierreHeaderStyle {
11
+ /** Fill behind the header row. */
12
+ background?: string;
13
+ /** Rule under the header row. */
14
+ borderBottom?: string;
15
+ /** Explicit size — the library leaves the header to inherit its line box. */
16
+ fontSize?: string;
17
+ lineHeight?: string;
18
+ /** Vertical padding. The library ships none, so this is the only breathing room. */
19
+ padding?: string;
20
+ }
21
+
22
+ /**
23
+ * Retint Pierre's code surface to `background`, optionally restyling the file
24
+ * header.
25
+ *
26
+ * Every background in `@pierre/diffs` resolves to `--diffs-bg`, which is
27
+ * `light-dark(var(--diffs-light-bg, #fff), var(--diffs-dark-bg, #000))` — so
28
+ * overriding both inputs re-tints the whole surface from one variable and lets
29
+ * the diff / selection / hover layers keep recomputing their `color-mix` tints
30
+ * against it.
31
+ *
32
+ * `:host` still needs a direct override: the theme layer writes a literal
33
+ * `background-color` there from the Shiki theme, which does not go through
34
+ * `--diffs-bg`.
35
+ *
36
+ * Header rules land in each file item's shadow root, which is where
37
+ * `FileRenderer` puts the header — `CodeView` forwards `unsafeCSS` to its file
38
+ * items even though it never injects it into its own shadow root.
39
+ *
40
+ * Changing header height also invalidates `itemMetrics.diffHeaderHeight`, which
41
+ * `CodeView` uses for its virtualization window and sticky offset. Keep the two
42
+ * in step when overriding `fontSize` / `lineHeight` / `padding`.
43
+ */
44
+ export function buildBackgroundCSS(
45
+ background: string,
46
+ header?: PierreHeaderStyle,
47
+ ): string {
48
+ const headerDeclarations = header
49
+ ? Object.entries(header)
50
+ .map(([property, value]) => ` ${kebab(property)}: ${value};`)
51
+ .join("\n")
52
+ : "";
53
+
10
54
  return `
11
55
  :host {
56
+ --diffs-light-bg: ${background};
57
+ --diffs-dark-bg: ${background};
12
58
  background: ${background} !important;
13
59
  }
14
- pre, code,
15
- [data-gutter], [data-content],
16
- [data-line], [data-column-number],
17
- [data-gutter-buffer], [data-line-annotation], [data-no-newline],
18
- [data-separator], [data-separator-wrapper] {
19
- background: ${background} !important;
20
- }
21
- [data-line] span {
22
- background: ${background} !important;
60
+ ${
61
+ headerDeclarations
62
+ ? `[data-diffs-header] {
63
+ ${headerDeclarations}
64
+ }`
65
+ : ""
23
66
  }
24
67
  `;
25
68
  }
26
69
 
27
- export function buildPierreOptions(background?: string) {
70
+ function kebab(property: string): string {
71
+ return property.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
72
+ }
73
+
74
+ export function buildPierreOptions(
75
+ background?: string,
76
+ header?: PierreHeaderStyle,
77
+ ) {
28
78
  if (!background) return PIERRE_BASE_OPTIONS;
29
79
  return {
30
80
  ...PIERRE_BASE_OPTIONS,
31
- unsafeCSS: buildBackgroundCSS(background),
81
+ unsafeCSS: buildBackgroundCSS(background, header),
32
82
  };
33
83
  }