@principal-ai/subsystems-react 0.37.5 → 0.37.7

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/index.d.ts +2 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +2 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/stories/Subsystem/C4Graph/c4Fixture.d.ts +3 -0
  6. package/dist/stories/Subsystem/C4Graph/c4Fixture.d.ts.map +1 -0
  7. package/dist/stories/Subsystem/C4Graph/c4Fixture.js +2 -0
  8. package/dist/stories/Subsystem/C4Graph/c4Fixture.js.map +1 -0
  9. package/dist/subsystem/C4Graph.d.ts +22 -0
  10. package/dist/subsystem/C4Graph.d.ts.map +1 -0
  11. package/dist/subsystem/C4Graph.js +305 -0
  12. package/dist/subsystem/C4Graph.js.map +1 -0
  13. package/dist/subsystem/SubsystemComponentGraph.d.ts +8 -0
  14. package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
  15. package/dist/subsystem/SubsystemComponentGraph.js +7 -3
  16. package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
  17. package/dist/subsystem/WalkthroughsPanel.d.ts +2 -0
  18. package/dist/subsystem/WalkthroughsPanel.d.ts.map +1 -1
  19. package/dist/subsystem/WalkthroughsPanel.js +114 -48
  20. package/dist/subsystem/WalkthroughsPanel.js.map +1 -1
  21. package/dist/subsystem/model.d.ts +2 -0
  22. package/dist/subsystem/model.d.ts.map +1 -1
  23. package/dist/subsystem/model.js +6 -6
  24. package/dist/subsystem/model.js.map +1 -1
  25. package/dist/subsystem/nodes.d.ts.map +1 -1
  26. package/dist/subsystem/nodes.js +2 -1
  27. package/dist/subsystem/nodes.js.map +1 -1
  28. package/dist/subsystem/toC4.d.ts +95 -0
  29. package/dist/subsystem/toC4.d.ts.map +1 -0
  30. package/dist/subsystem/toC4.js +196 -0
  31. package/dist/subsystem/toC4.js.map +1 -0
  32. package/dist/subsystem/walkthroughBrief.d.ts +18 -0
  33. package/dist/subsystem/walkthroughBrief.d.ts.map +1 -0
  34. package/dist/subsystem/walkthroughBrief.js +34 -0
  35. package/dist/subsystem/walkthroughBrief.js.map +1 -0
  36. package/package.json +1 -1
  37. package/src/index.ts +6 -1
  38. package/src/stories/Subsystem/C4Graph/C4Container.stories.tsx +104 -0
  39. package/src/stories/Subsystem/C4Graph/c4Fixture.ts +6 -0
  40. package/src/subsystem/C4Graph.tsx +465 -0
  41. package/src/subsystem/SubsystemComponentGraph.tsx +15 -3
  42. package/src/subsystem/WalkthroughsPanel.test.tsx +146 -0
  43. package/src/subsystem/WalkthroughsPanel.tsx +163 -60
  44. package/src/subsystem/model.ts +8 -6
  45. package/src/subsystem/nodes.tsx +2 -1
  46. package/src/subsystem/toC4.test.ts +121 -0
  47. package/src/subsystem/toC4.ts +277 -0
  48. package/src/subsystem/walkthroughBrief.test.ts +57 -0
  49. package/src/subsystem/walkthroughBrief.ts +40 -0
@@ -0,0 +1,196 @@
1
+ /**
2
+ * toC4 — project a subsystem-model (or a composition of them) onto C4 levels.
3
+ *
4
+ * The stored document is flat: `components[]` carry `process` (runtime unit),
5
+ * `purl` (repo/package), `construct`, `role`, `module`, plus `relations[]`
6
+ * (static) and `walkthroughs[]` (dynamic). C4 wants a hierarchy above the
7
+ * component — system → container → component — with externals and actors
8
+ * outside it. This module derives that hierarchy without inventing data:
9
+ *
10
+ * system ← the repo key (purlRepoKey of the first grounded component)
11
+ * container ← component.process (runtime/deployment boundary)
12
+ * component ← a grounded component
13
+ * external ← construct: 'external'
14
+ * actor ← construct: 'custom_entity' (their authored actor/entity)
15
+ *
16
+ * Edges are rolled up to the owner of each endpoint at the chosen view, so the
17
+ * container view shows container↔container collaborations and the component
18
+ * view shows component↔component ones. Intra-owner edges (from === to) drop.
19
+ *
20
+ * Pure over the document so it is unit-testable and reusable by any renderer.
21
+ */
22
+ const EXTERNAL_PURL = 'external';
23
+ const UNASSIGNED = '(unassigned)';
24
+ /** A code component (not an external, not an actor/entity). */
25
+ export function isGroundedComponent(c) {
26
+ return c.construct !== 'external' && c.construct !== 'custom_entity';
27
+ }
28
+ /** `owner/name` from a purl, trimmed of scheme and fragment. */
29
+ export function labelFromPurl(purl) {
30
+ const base = purl.replace(/^external:/, '').split('#')[0]?.trim() ?? '';
31
+ const parts = base.split('/').filter(Boolean);
32
+ return parts.slice(-2).join('/') || base || purl;
33
+ }
34
+ /** The repo key the system is derived from: first grounded component's purl. */
35
+ export function deriveRepoKey(doc) {
36
+ for (const c of doc.components) {
37
+ const p = (c.purl ?? '').split('#')[0]?.trim();
38
+ if (p && p !== EXTERNAL_PURL && !p.startsWith('external:'))
39
+ return p;
40
+ }
41
+ return undefined;
42
+ }
43
+ /** Stable identity for an external: its real purl, else the model-local alias. */
44
+ function externalKey(c) {
45
+ const purl = (c.purl ?? '').trim();
46
+ if (purl && purl !== EXTERNAL_PURL && purl !== 'external:proposed')
47
+ return purl;
48
+ return `alias:${c.alias}`;
49
+ }
50
+ function containerKey(c) {
51
+ return c.process?.trim() || UNASSIGNED;
52
+ }
53
+ /**
54
+ * Derive the C4 projection of a (possibly composed) subsystem document.
55
+ *
56
+ * @param doc - a portable document, or a composed one with canonical aliases.
57
+ * @param options - view + optional system naming + attribution.
58
+ */
59
+ export function toC4(doc, options = {}) {
60
+ const view = options.view ?? 'container';
61
+ const repoKey = options.repoKey ?? deriveRepoKey(doc) ?? 'system';
62
+ const systemId = `system:${repoKey}`;
63
+ const system = {
64
+ id: systemId,
65
+ kind: 'system',
66
+ label: options.systemLabel ?? labelFromPurl(repoKey),
67
+ key: repoKey,
68
+ members: [],
69
+ constructs: [],
70
+ isStore: false,
71
+ };
72
+ const nodes = new Map();
73
+ const ownerOf = new Map();
74
+ const containerNodeId = (key) => `container:${key}`;
75
+ const componentNodeId = (alias) => `component:${alias}`;
76
+ for (const c of doc.components) {
77
+ let id;
78
+ let kind;
79
+ let label;
80
+ let parentId;
81
+ let key;
82
+ if (c.construct === 'external') {
83
+ key = externalKey(c);
84
+ id = `external:${key}`;
85
+ kind = 'external';
86
+ label = key.startsWith('alias:') ? c.name : labelFromPurl(key);
87
+ }
88
+ else if (c.construct === 'custom_entity') {
89
+ key = c.alias;
90
+ id = `actor:${c.alias}`;
91
+ kind = 'actor';
92
+ label = c.name;
93
+ }
94
+ else if (view === 'component') {
95
+ key = containerKey(c);
96
+ id = componentNodeId(c.alias);
97
+ kind = 'component';
98
+ label = c.name;
99
+ parentId = containerNodeId(key);
100
+ }
101
+ else {
102
+ key = containerKey(c);
103
+ id = containerNodeId(key);
104
+ kind = 'container';
105
+ label = key === UNASSIGNED ? 'Unassigned' : key;
106
+ parentId = systemId;
107
+ }
108
+ ownerOf.set(c.alias, id);
109
+ let node = nodes.get(id);
110
+ if (!node) {
111
+ node = {
112
+ id,
113
+ kind,
114
+ label,
115
+ parentId,
116
+ key,
117
+ members: [],
118
+ constructs: [],
119
+ isStore: false,
120
+ ...(kind === 'component' ? { component: c } : {}),
121
+ };
122
+ nodes.set(id, node);
123
+ }
124
+ node.members.push(c.alias);
125
+ if (!node.constructs.includes(c.construct))
126
+ node.constructs.push(c.construct);
127
+ if (c.construct === 'store')
128
+ node.isStore = true;
129
+ const models = options.modelsByAlias?.[c.alias];
130
+ if (models && models.length > 0) {
131
+ node.models = [...new Set([...(node.models ?? []), ...models])];
132
+ }
133
+ }
134
+ // --- Groups (compound frames) ------------------------------------------
135
+ const groups = [];
136
+ if (view === 'container') {
137
+ const containerIds = [...nodes.values()].filter((n) => n.kind === 'container').map((n) => n.id);
138
+ if (containerIds.length > 0) {
139
+ groups.push({ id: systemId, kind: 'system', label: system.label, memberIds: containerIds });
140
+ }
141
+ }
142
+ else {
143
+ const byContainer = new Map();
144
+ for (const n of nodes.values()) {
145
+ if (n.kind !== 'component' || !n.parentId)
146
+ continue;
147
+ const list = byContainer.get(n.parentId) ?? [];
148
+ list.push(n.id);
149
+ byContainer.set(n.parentId, list);
150
+ }
151
+ const containerIds = [];
152
+ 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 });
155
+ containerIds.push(id);
156
+ }
157
+ if (containerIds.length > 0) {
158
+ groups.push({ id: systemId, kind: 'system', label: system.label, memberIds: containerIds });
159
+ }
160
+ }
161
+ // --- Edges: roll each endpoint up to its owner at this view -------------
162
+ const relEdges = new Map();
163
+ const flowEdges = new Map();
164
+ const addEdge = (bucket, source, target, kind, mechanism) => {
165
+ if (!source || !target || source === target)
166
+ return;
167
+ const key = `${source}\0${target}`;
168
+ let edge = bucket.get(key);
169
+ if (!edge) {
170
+ edge = { id: `${kind}:${key}`, source, target, kind, label: '', mechanisms: [], count: 0 };
171
+ bucket.set(key, edge);
172
+ }
173
+ edge.count += 1;
174
+ if (!edge.mechanisms.includes(mechanism))
175
+ edge.mechanisms.push(mechanism);
176
+ };
177
+ for (const r of doc.relations ?? []) {
178
+ addEdge(relEdges, ownerOf.get(r.from), ownerOf.get(r.to), 'relationship', r.relationType);
179
+ }
180
+ for (const w of doc.walkthroughs ?? []) {
181
+ for (const s of w.steps ?? []) {
182
+ addEdge(flowEdges, ownerOf.get(s.from), ownerOf.get(s.to), 'flow', s.mechanism);
183
+ }
184
+ }
185
+ const edges = [...relEdges.values(), ...flowEdges.values()];
186
+ for (const e of edges)
187
+ e.label = [...e.mechanisms].sort().join(' + ');
188
+ return {
189
+ view,
190
+ system,
191
+ nodes: [...nodes.values()].filter((n) => n.kind !== 'system'),
192
+ groups,
193
+ edges,
194
+ };
195
+ }
196
+ //# sourceMappingURL=toC4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toC4.js","sourceRoot":"","sources":["../../src/subsystem/toC4.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAsEH,MAAM,aAAa,GAAG,UAAU,CAAC;AACjC,MAAM,UAAU,GAAG,cAAc,CAAC;AAElC,+DAA+D;AAC/D,MAAM,UAAU,mBAAmB,CAAC,CAAqB;IACvD,OAAO,CAAC,CAAC,SAAS,KAAK,UAAU,IAAI,CAAC,CAAC,SAAS,KAAK,eAAe,CAAC;AACvE,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC9C,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,CAAC;AACnD,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,aAAa,CAAC,GAA2B;IACvD,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,UAAU,EAAE;QAC9B,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;QAC/C,IAAI,CAAC,IAAI,CAAC,KAAK,aAAa,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC;YAAE,OAAO,CAAC,CAAC;KACtE;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,kFAAkF;AAClF,SAAS,WAAW,CAAC,CAAqB;IACxC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACnC,IAAI,IAAI,IAAI,IAAI,KAAK,aAAa,IAAI,IAAI,KAAK,mBAAmB;QAAE,OAAO,IAAI,CAAC;IAChF,OAAO,SAAS,CAAC,CAAC,KAAK,EAAE,CAAC;AAC5B,CAAC;AAED,SAAS,YAAY,CAAC,CAAqB;IACzC,OAAO,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,UAAU,CAAC;AACzC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,IAAI,CAAC,GAA2B,EAAE,UAAuB,EAAE;IACzE,MAAM,IAAI,GAAW,OAAO,CAAC,IAAI,IAAI,WAAW,CAAC;IACjD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,aAAa,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC;IAClE,MAAM,QAAQ,GAAG,UAAU,OAAO,EAAE,CAAC;IAErC,MAAM,MAAM,GAAW;QACrB,EAAE,EAAE,QAAQ;QACZ,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,OAAO,CAAC,WAAW,IAAI,aAAa,CAAC,OAAO,CAAC;QACpD,GAAG,EAAE,OAAO;QACZ,OAAO,EAAE,EAAE;QACX,UAAU,EAAE,EAAE;QACd,OAAO,EAAE,KAAK;KACf,CAAC;IAEF,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE1C,MAAM,eAAe,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,aAAa,GAAG,EAAE,CAAC;IAC5D,MAAM,eAAe,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,aAAa,KAAK,EAAE,CAAC;IAEhE,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,UAAU,EAAE;QAC9B,IAAI,EAAU,CAAC;QACf,IAAI,IAAY,CAAC;QACjB,IAAI,KAAa,CAAC;QAClB,IAAI,QAA4B,CAAC;QACjC,IAAI,GAAuB,CAAC;QAE5B,IAAI,CAAC,CAAC,SAAS,KAAK,UAAU,EAAE;YAC9B,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;YACrB,EAAE,GAAG,YAAY,GAAG,EAAE,CAAC;YACvB,IAAI,GAAG,UAAU,CAAC;YAClB,KAAK,GAAG,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;SAChE;aAAM,IAAI,CAAC,CAAC,SAAS,KAAK,eAAe,EAAE;YAC1C,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC;YACd,EAAE,GAAG,SAAS,CAAC,CAAC,KAAK,EAAE,CAAC;YACxB,IAAI,GAAG,OAAO,CAAC;YACf,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC;SAChB;aAAM,IAAI,IAAI,KAAK,WAAW,EAAE;YAC/B,GAAG,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;YACtB,EAAE,GAAG,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YAC9B,IAAI,GAAG,WAAW,CAAC;YACnB,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC;YACf,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;SACjC;aAAM;YACL,GAAG,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;YACtB,EAAE,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;YAC1B,IAAI,GAAG,WAAW,CAAC;YACnB,KAAK,GAAG,GAAG,KAAK,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC;YAChD,QAAQ,GAAG,QAAQ,CAAC;SACrB;QAED,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAEzB,IAAI,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACzB,IAAI,CAAC,IAAI,EAAE;YACT,IAAI,GAAG;gBACL,EAAE;gBACF,IAAI;gBACJ,KAAK;gBACL,QAAQ;gBACR,GAAG;gBACH,OAAO,EAAE,EAAE;gBACX,UAAU,EAAE,EAAE;gBACd,OAAO,EAAE,KAAK;gBACd,GAAG,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAClD,CAAC;YACF,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;SACrB;QACD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC3B,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;YAAE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAC9E,IAAI,CAAC,CAAC,SAAS,KAAK,OAAO;YAAE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACjD,MAAM,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAChD,IAAI,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;YAC/B,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;SACjE;KACF;IAED,0EAA0E;IAC1E,MAAM,MAAM,GAAc,EAAE,CAAC;IAC7B,IAAI,IAAI,KAAK,WAAW,EAAE;QACxB,MAAM,YAAY,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAChG,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE;YAC3B,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,CAAC;SAC7F;KACF;SAAM;QACL,MAAM,WAAW,GAAG,IAAI,GAAG,EAAoB,CAAC;QAChD,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE;YAC9B,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,CAAC,CAAC,QAAQ;gBAAE,SAAS;YACpD,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;YAC/C,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAChB,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;SACnC;QACD,MAAM,YAAY,GAAa,EAAE,CAAC;QAClC,KAAK,MAAM,CAAC,EAAE,EAAE,SAAS,CAAC,IAAI,WAAW,EAAE;YACzC,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;YAC5C,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;YAC1H,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;SACvB;QACD,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE;YAC3B,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,CAAC;SAC7F;KACF;IAED,2EAA2E;IAC3E,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE5C,MAAM,OAAO,GAAG,CACd,MAA2B,EAC3B,MAA0B,EAC1B,MAA0B,EAC1B,IAAoB,EACpB,SAAiB,EACjB,EAAE;QACF,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,IAAI,MAAM,KAAK,MAAM;YAAE,OAAO;QACpD,MAAM,GAAG,GAAG,GAAG,MAAM,KAAK,MAAM,EAAE,CAAC;QACnC,IAAI,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC3B,IAAI,CAAC,IAAI,EAAE;YACT,IAAI,GAAG,EAAE,EAAE,EAAE,GAAG,IAAI,IAAI,GAAG,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;YAC3F,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;SACvB;QACD,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC;QAChB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAC;YAAE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC5E,CAAC,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,SAAS,IAAI,EAAE,EAAE;QACnC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC;KAC3F;IACD,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,YAAY,IAAI,EAAE,EAAE;QACtC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,EAAE;YAC7B,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC;SACjF;KACF;IAED,MAAM,KAAK,GAAG,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,EAAE,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5D,KAAK,MAAM,CAAC,IAAI,KAAK;QAAE,CAAC,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAEtE,OAAO;QACL,IAAI;QACJ,MAAM;QACN,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;QAC7D,MAAM;QACN,KAAK;KACN,CAAC;AACJ,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * walkthroughBrief — build a paste-ready markdown brief for a single
3
+ * walkthrough step so a reviewer can hand one hop to an agent without
4
+ * transcribing it by hand.
5
+ *
6
+ * The unit of handoff is one hop, not the whole flow. A bare hop is ambiguous
7
+ * on its own — the same `from`/`to`/`mechanism` can repeat across flows — so the
8
+ * brief also carries the flow it belongs to and its position within it, plus
9
+ * the exact seam site (`symbol`, `file:line`, `purl`) that resolves the
10
+ * checkout.
11
+ */
12
+ import type { SubsystemWalkthrough } from './model';
13
+ /**
14
+ * Markdown brief for `walkthrough.steps[stepIndex]`. Returns `''` when the
15
+ * index is out of range so a caller can treat it as "nothing to copy".
16
+ */
17
+ export declare function buildStepBrief(walkthrough: SubsystemWalkthrough, stepIndex: number): string;
18
+ //# sourceMappingURL=walkthroughBrief.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walkthroughBrief.d.ts","sourceRoot":"","sources":["../../src/subsystem/walkthroughBrief.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAEpD;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,oBAAoB,EACjC,SAAS,EAAE,MAAM,GAChB,MAAM,CAkBR"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * walkthroughBrief — build a paste-ready markdown brief for a single
3
+ * walkthrough step so a reviewer can hand one hop to an agent without
4
+ * transcribing it by hand.
5
+ *
6
+ * The unit of handoff is one hop, not the whole flow. A bare hop is ambiguous
7
+ * on its own — the same `from`/`to`/`mechanism` can repeat across flows — so the
8
+ * brief also carries the flow it belongs to and its position within it, plus
9
+ * the exact seam site (`symbol`, `file:line`, `purl`) that resolves the
10
+ * checkout.
11
+ */
12
+ /**
13
+ * Markdown brief for `walkthrough.steps[stepIndex]`. Returns `''` when the
14
+ * index is out of range so a caller can treat it as "nothing to copy".
15
+ */
16
+ export function buildStepBrief(walkthrough, stepIndex) {
17
+ const step = walkthrough.steps[stepIndex];
18
+ if (!step)
19
+ return '';
20
+ const lines = [];
21
+ lines.push(`Walkthrough: ${walkthrough.title} (${walkthrough.id}) — step ${stepIndex + 1}/${walkthrough.steps.length}`);
22
+ lines.push('');
23
+ lines.push(`- from: \`${step.from}\``);
24
+ lines.push(`- to: \`${step.to}\``);
25
+ lines.push(`- mechanism: \`${step.mechanism}\``);
26
+ lines.push(`- symbol: \`${step.symbol}\``);
27
+ lines.push(`- site: \`${step.file}:${step.line}\``);
28
+ lines.push(`- purl: \`${step.purl}\``);
29
+ if (step.annotation != null && step.annotation.length > 0) {
30
+ lines.push(`- note: ${step.annotation}`);
31
+ }
32
+ return lines.join('\n');
33
+ }
34
+ //# sourceMappingURL=walkthroughBrief.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walkthroughBrief.js","sourceRoot":"","sources":["../../src/subsystem/walkthroughBrief.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH;;;GAGG;AACH,MAAM,UAAU,cAAc,CAC5B,WAAiC,EACjC,SAAiB;IAEjB,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAC1C,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CACR,gBAAgB,WAAW,CAAC,KAAK,KAAK,WAAW,CAAC,EAAE,YAAY,SAAS,GAAG,CAAC,IAAI,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE,CAC5G,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC;IACvC,KAAK,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;IACnC,KAAK,CAAC,IAAI,CAAC,kBAAkB,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC;IACjD,KAAK,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;IAC3C,KAAK,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC;IACpD,KAAK,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC;IACvC,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE;QACzD,KAAK,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;KAC1C;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@principal-ai/subsystems-react",
3
- "version": "0.37.5",
3
+ "version": "0.37.7",
4
4
  "description": "React UI for Subsystem Models — graph, Pierre code views, and session-event feeds",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/index.ts CHANGED
@@ -174,8 +174,13 @@ export type {
174
174
  } from './subsystem/paths';
175
175
  export { SubsystemFileTree } from './subsystem/SubsystemFileTree';
176
176
  export type { SubsystemFileTreeProps } from './subsystem/SubsystemFileTree';
177
- export { WalkthroughsPanel, WALKTHROUGH_PLAY_PAUSE_MS } from './subsystem/WalkthroughsPanel';
177
+ export {
178
+ WalkthroughsPanel,
179
+ WALKTHROUGH_PLAY_PAUSE_MS,
180
+ STEP_COPY_FEEDBACK_MS,
181
+ } from './subsystem/WalkthroughsPanel';
178
182
  export type { WalkthroughsPanelProps } from './subsystem/WalkthroughsPanel';
183
+ export { buildStepBrief } from './subsystem/walkthroughBrief';
179
184
  export { GraphLayoutCover } from './subsystem/GraphLayoutCover';
180
185
  export type { GraphLayoutCoverProps } from './subsystem/GraphLayoutCover';
181
186
  export {
@@ -0,0 +1,104 @@
1
+ import React, { useMemo, useState } from 'react';
2
+ import '@xyflow/react/dist/style.css';
3
+ import type { Meta, StoryObj } from '@storybook/react';
4
+ import { ThemeProvider, defaultEditorTheme } from '@principal-ade/industry-theme';
5
+ import { C4Graph } from '../../../subsystem/C4Graph';
6
+ import { toC4, type C4View } from '../../../subsystem/toC4';
7
+ import { c4FixtureModel } from './c4Fixture';
8
+
9
+ const meta = {
10
+ title: 'Subsystem/C4Graph/C4Container',
11
+ component: C4Graph,
12
+ parameters: {
13
+ layout: 'fullscreen',
14
+ },
15
+ tags: ['autodocs'],
16
+ decorators: [
17
+ (Story) => (
18
+ <ThemeProvider theme={defaultEditorTheme}>
19
+ <Story />
20
+ </ThemeProvider>
21
+ ),
22
+ ],
23
+ } satisfies Meta<typeof C4Graph>;
24
+
25
+ export default meta;
26
+ type Story = StoryObj<typeof meta>;
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // C4 projection of a real composite: 60 stored subsystem models over one repo,
30
+ // merged by the host, then derived into system → container/component boxes with
31
+ // externals + actors outside. Toggle the C4 level; click a box to see the
32
+ // source components it rolled up.
33
+ // ---------------------------------------------------------------------------
34
+
35
+ function C4Demo() {
36
+ const [view, setView] = useState<C4View>('container');
37
+ const [selected, setSelected] = useState<string | null>(null);
38
+ const model = useMemo(() => toC4(c4FixtureModel, { view, systemLabel: 'Subsystem Modeling' }), [view]);
39
+
40
+ const counts = {
41
+ containers: model.nodes.filter((n) => n.kind === 'container').length,
42
+ components: model.nodes.filter((n) => n.kind === 'component').length,
43
+ externals: model.nodes.filter((n) => n.kind === 'external').length,
44
+ actors: model.nodes.filter((n) => n.kind === 'actor').length,
45
+ relationships: model.edges.filter((e) => e.kind === 'relationship').length,
46
+ flows: model.edges.filter((e) => e.kind === 'flow').length,
47
+ };
48
+
49
+ const button = (label: string, value: C4View) => (
50
+ <button
51
+ type="button"
52
+ onClick={() => setView(value)}
53
+ style={{
54
+ fontFamily: 'monospace',
55
+ fontSize: 12,
56
+ padding: '2px 10px',
57
+ borderRadius: 4,
58
+ cursor: 'pointer',
59
+ border: '1px solid #555',
60
+ background: view === value ? '#1D9E75' : 'transparent',
61
+ color: view === value ? '#020B12' : '#ddd',
62
+ }}
63
+ >
64
+ {label}
65
+ </button>
66
+ );
67
+
68
+ return (
69
+ <div style={{ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }}>
70
+ <div style={{ flex: 1, minHeight: 0, position: 'relative' }}>
71
+ <C4Graph model={model} title="Subsystem Modeling" onSelectNode={setSelected} />
72
+ </div>
73
+ <div
74
+ style={{
75
+ padding: '6px 12px',
76
+ display: 'flex',
77
+ alignItems: 'center',
78
+ gap: 16,
79
+ fontFamily: 'monospace',
80
+ fontSize: 12,
81
+ color: '#aaa',
82
+ borderTop: '1px solid #333',
83
+ }}
84
+ >
85
+ <span style={{ display: 'flex', gap: 6 }}>
86
+ view: {button('container', 'container')} {button('component', 'component')}
87
+ </span>
88
+ <span>
89
+ {counts.containers} containers · {counts.components} components · {counts.externals} externals ·{' '}
90
+ {counts.actors} actors
91
+ </span>
92
+ <span>
93
+ {counts.relationships} relationship edges · {counts.flows} flow edges
94
+ </span>
95
+ <span>selected: {selected ?? '(none)'}</span>
96
+ </div>
97
+ </div>
98
+ );
99
+ }
100
+
101
+ export const C4: Story = {
102
+ args: { model: toC4(c4FixtureModel, { view: 'container', systemLabel: 'Subsystem Modeling' }) },
103
+ render: () => <C4Demo />,
104
+ };