@principal-ai/subsystems-react 0.51.4 → 0.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/subsystem/C4Graph.d.ts +3 -7
- package/dist/subsystem/C4Graph.d.ts.map +1 -1
- package/dist/subsystem/C4Graph.js +159 -45
- package/dist/subsystem/C4Graph.js.map +1 -1
- package/dist/subsystem/C4NodeCard.d.ts +26 -26
- package/dist/subsystem/C4NodeCard.d.ts.map +1 -1
- package/dist/subsystem/C4NodeCard.js +56 -30
- package/dist/subsystem/C4NodeCard.js.map +1 -1
- package/dist/subsystem/IssueList.d.ts +1 -1
- package/dist/subsystem/IssueList.d.ts.map +1 -1
- package/dist/subsystem/IssueList.js +20 -1
- package/dist/subsystem/IssueList.js.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.js +42 -19
- package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
- package/dist/subsystem/c4.d.ts +48 -49
- package/dist/subsystem/c4.d.ts.map +1 -1
- package/dist/subsystem/c4.js +50 -0
- package/dist/subsystem/c4.js.map +1 -1
- package/dist/subsystem/c4Evidence.d.ts +88 -84
- package/dist/subsystem/c4Evidence.d.ts.map +1 -1
- package/dist/subsystem/c4Evidence.js +113 -166
- package/dist/subsystem/c4Evidence.js.map +1 -1
- package/dist/subsystem/nodes.js +5 -5
- package/dist/subsystem/nodes.js.map +1 -1
- package/dist/subsystem/techIcons/index.d.ts +5 -0
- package/dist/subsystem/techIcons/index.d.ts.map +1 -1
- package/dist/subsystem/techIcons/index.js +3 -0
- package/dist/subsystem/techIcons/index.js.map +1 -1
- package/dist/utils/edgeLabel.d.ts +15 -1
- package/dist/utils/edgeLabel.d.ts.map +1 -1
- package/dist/utils/edgeLabel.js +25 -1
- package/dist/utils/edgeLabel.js.map +1 -1
- package/dist/utils/elkLayout.d.ts +47 -0
- package/dist/utils/elkLayout.d.ts.map +1 -1
- package/dist/utils/elkLayout.js +71 -7
- package/dist/utils/elkLayout.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +8 -6
- package/src/stories/Subsystem/C4Graph/C4Component.stories.tsx +198 -0
- package/src/stories/Subsystem/C4Graph/C4Container.stories.tsx +42 -6
- package/src/stories/Subsystem/C4Graph/C4NodeGallery.stories.tsx +9 -7
- package/src/stories/Subsystem/ComponentGraph/Issues.stories.tsx +686 -2
- package/src/subsystem/C4Graph.tsx +242 -82
- package/src/subsystem/C4NodeCard.test.ts +32 -22
- package/src/subsystem/C4NodeCard.tsx +67 -39
- package/src/subsystem/IssueList.tsx +23 -0
- package/src/subsystem/SubsystemComponentGraph.tsx +38 -16
- package/src/subsystem/c4.protocolColor.test.ts +26 -0
- package/src/subsystem/c4.ts +76 -52
- package/src/subsystem/c4Evidence.test.ts +177 -237
- package/src/subsystem/c4Evidence.ts +160 -235
- package/src/subsystem/nodes.group.test.tsx +15 -0
- package/src/subsystem/nodes.tsx +7 -7
- package/src/subsystem/techIcons/index.tsx +8 -0
- package/src/utils/edgeLabel.test.ts +21 -2
- package/src/utils/edgeLabel.ts +29 -1
- package/src/utils/elkLayout.test.ts +16 -0
- package/src/utils/elkLayout.ts +119 -7
- package/src/stories/Subsystem/ComponentGraph/IssueOverlay.stories.tsx +0 -602
|
@@ -0,0 +1,198 @@
|
|
|
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 type { C4Edge, C4Group, C4Model, C4Element, C4Component } from '../../../subsystem/c4';
|
|
7
|
+
|
|
8
|
+
const meta = {
|
|
9
|
+
title: 'Subsystem/C4Graph/C4Component',
|
|
10
|
+
component: C4Graph,
|
|
11
|
+
parameters: { layout: 'fullscreen' },
|
|
12
|
+
tags: ['autodocs'],
|
|
13
|
+
decorators: [
|
|
14
|
+
(Story) => (
|
|
15
|
+
<ThemeProvider theme={defaultEditorTheme}>
|
|
16
|
+
<Story />
|
|
17
|
+
</ThemeProvider>
|
|
18
|
+
),
|
|
19
|
+
],
|
|
20
|
+
} satisfies Meta<typeof C4Graph>;
|
|
21
|
+
|
|
22
|
+
export default meta;
|
|
23
|
+
type Story = StoryObj<typeof meta>;
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// A C4 *component* diagram, hand-built.
|
|
27
|
+
//
|
|
28
|
+
// No single subsystem model owns these groupings — each one is stitched from
|
|
29
|
+
// constructs that recur across many models (e.g. `auditSubsystemModel` appears
|
|
30
|
+
// in 8). That is exactly why a component is an authored grouping: the evidence
|
|
31
|
+
// lives across models, and a principal agent proposes the union while a person
|
|
32
|
+
// confirms it.
|
|
33
|
+
//
|
|
34
|
+
// Two containers are drawn as frames; their components sit inside.
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
const SYSTEM = { id: 'system:acme', label: 'Subsystem Modeling', repoKey: 'pkg:github/acme/subsystem-modeling' };
|
|
38
|
+
const HOST = 'container:studio-host';
|
|
39
|
+
const RENDERER = 'container:studio-renderer';
|
|
40
|
+
|
|
41
|
+
/** A component at level 3, inside a container. */
|
|
42
|
+
function component(
|
|
43
|
+
over: Partial<C4Component> & Pick<C4Component, 'id' | 'label' | 'container'>,
|
|
44
|
+
): C4Element {
|
|
45
|
+
return {
|
|
46
|
+
kind: 'component',
|
|
47
|
+
members: [],
|
|
48
|
+
constructs: [],
|
|
49
|
+
state: 'accepted',
|
|
50
|
+
parentId: over.container,
|
|
51
|
+
technology: 'Bun + Electrobun',
|
|
52
|
+
...over,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const NODES: C4Element[] = [
|
|
57
|
+
// --- inside the host ------------------------------------------------------
|
|
58
|
+
component({
|
|
59
|
+
id: 'component:audit-verification',
|
|
60
|
+
label: 'Audit & verification',
|
|
61
|
+
container: HOST,
|
|
62
|
+
technology: 'Bun',
|
|
63
|
+
description: 'Checks a subsystem model and reports what is wrong.',
|
|
64
|
+
// The recurring constructs, stitched across ~8 models. See the story notes.
|
|
65
|
+
members: [
|
|
66
|
+
'auditSubsystemModel', 'verifySubsystemComponent', 'verifyModelFiles',
|
|
67
|
+
'createRegularAuditScheduler', 'applySubsystemModelAuditFix',
|
|
68
|
+
'auditTopologyRelations', 'auditBoundaryFields',
|
|
69
|
+
],
|
|
70
|
+
}),
|
|
71
|
+
component({
|
|
72
|
+
id: 'component:maintain-proposals',
|
|
73
|
+
label: 'Maintain & proposals',
|
|
74
|
+
container: HOST,
|
|
75
|
+
technology: 'Bun',
|
|
76
|
+
description: 'Runs a maintain agent and records the corrections it proposes.',
|
|
77
|
+
members: [
|
|
78
|
+
'maintainSubsystemModel', 'runMaintainAgent', 'buildMaintainBrief',
|
|
79
|
+
'createSubsystemModelProposal', 'acceptSubsystemModelProposal',
|
|
80
|
+
'autoAcceptProposalIfConfident',
|
|
81
|
+
],
|
|
82
|
+
}),
|
|
83
|
+
component({
|
|
84
|
+
id: 'component:model-store',
|
|
85
|
+
label: 'Model store I/O',
|
|
86
|
+
container: HOST,
|
|
87
|
+
technology: 'Bun fs',
|
|
88
|
+
description: 'Reads and writes model JSON under ~/.principal.',
|
|
89
|
+
members: [
|
|
90
|
+
'getSubsystemModel', 'listSubsystemModels', 'updateSubsystemModel',
|
|
91
|
+
'createSubsystemModel', 'readFile', 'writeFile', 'touchSubsystemModelOpened',
|
|
92
|
+
],
|
|
93
|
+
}),
|
|
94
|
+
component({
|
|
95
|
+
id: 'component:session-pipeline',
|
|
96
|
+
label: 'Session pipeline',
|
|
97
|
+
container: HOST,
|
|
98
|
+
technology: 'Bun',
|
|
99
|
+
description: 'Turns raw OpenCode session rows into universal events.',
|
|
100
|
+
members: ['processSessionEvents', 'opencodeRowsToUniversalEvents', 'buildSessionIndex'],
|
|
101
|
+
}),
|
|
102
|
+
|
|
103
|
+
// --- inside the renderer --------------------------------------------------
|
|
104
|
+
component({
|
|
105
|
+
id: 'component:graph-view',
|
|
106
|
+
label: 'Graph view',
|
|
107
|
+
container: RENDERER,
|
|
108
|
+
technology: 'React + ELK',
|
|
109
|
+
description: 'Renders the component graph and its layout.',
|
|
110
|
+
}),
|
|
111
|
+
component({
|
|
112
|
+
id: 'component:model-views',
|
|
113
|
+
label: 'Model views',
|
|
114
|
+
container: RENDERER,
|
|
115
|
+
technology: 'React',
|
|
116
|
+
description: 'Lists and opens stored subsystem models.',
|
|
117
|
+
}),
|
|
118
|
+
component({
|
|
119
|
+
id: 'component:agent-sessions',
|
|
120
|
+
label: 'Agent sessions view',
|
|
121
|
+
container: RENDERER,
|
|
122
|
+
technology: 'React',
|
|
123
|
+
description: 'Shows maintain-agent sessions and their timelines.',
|
|
124
|
+
}),
|
|
125
|
+
component({
|
|
126
|
+
id: 'component:rpc-bridge',
|
|
127
|
+
label: 'RPC bridge',
|
|
128
|
+
container: RENDERER,
|
|
129
|
+
technology: 'Electrobun RPC',
|
|
130
|
+
description: 'Talks to the host process over the Electrobun bridge.',
|
|
131
|
+
}),
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
/** The two containers at this level, drawn as frames. */
|
|
135
|
+
const GROUPS: C4Group[] = [
|
|
136
|
+
{ id: HOST, kind: 'container', label: 'Subsystem Studio — host', parentId: SYSTEM.id, memberIds: NODES.filter((n) => n.parentId === HOST).map((n) => n.id) },
|
|
137
|
+
{ id: RENDERER, kind: 'container', label: 'Subsystem Studio — renderer', parentId: SYSTEM.id, memberIds: NODES.filter((n) => n.parentId === RENDERER).map((n) => n.id) },
|
|
138
|
+
{ id: SYSTEM.id, kind: 'system', label: SYSTEM.label, memberIds: [HOST, RENDERER] },
|
|
139
|
+
];
|
|
140
|
+
|
|
141
|
+
/** A line between components — protocol-labelled, like the container view. */
|
|
142
|
+
function edge(id: string, source: string, target: string, protocol: string): C4Edge {
|
|
143
|
+
return { id, source, target, label: protocol, protocol, mechanisms: [], count: 1 };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const EDGES: C4Edge[] = [
|
|
147
|
+
edge('e:views-store', 'component:model-views', 'component:model-store', 'RPC'),
|
|
148
|
+
edge('e:views-audit', 'component:model-views', 'component:audit-verification', 'RPC'),
|
|
149
|
+
edge('e:audit-store', 'component:audit-verification', 'component:model-store', 'fs · JSON'),
|
|
150
|
+
edge('e:proposals-store', 'component:maintain-proposals', 'component:model-store', 'fs · JSON'),
|
|
151
|
+
edge('e:proposals-sessions', 'component:maintain-proposals', 'component:session-pipeline', 'feeds'),
|
|
152
|
+
edge('e:sessions-agent', 'component:agent-sessions', 'component:session-pipeline', 'RPC'),
|
|
153
|
+
edge('e:renderer-host', 'component:rpc-bridge', 'component:model-store', 'RPC'),
|
|
154
|
+
edge('e:views-graph', 'component:model-views', 'component:graph-view', 'calls'),
|
|
155
|
+
];
|
|
156
|
+
|
|
157
|
+
const componentModel: C4Model = {
|
|
158
|
+
view: 'component',
|
|
159
|
+
system: SYSTEM,
|
|
160
|
+
nodes: NODES,
|
|
161
|
+
groups: GROUPS,
|
|
162
|
+
edges: EDGES,
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
function Demo() {
|
|
166
|
+
const [selected, setSelected] = useState<string | null>(null);
|
|
167
|
+
const model = useMemo(() => componentModel, []);
|
|
168
|
+
const components = model.nodes.filter((n) => n.kind === 'component').length;
|
|
169
|
+
|
|
170
|
+
return (
|
|
171
|
+
<div style={{ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }}>
|
|
172
|
+
<div style={{ flex: 1, minHeight: 0, position: 'relative' }}>
|
|
173
|
+
<C4Graph model={model} title="Subsystem Modeling" onSelectNode={setSelected} />
|
|
174
|
+
</div>
|
|
175
|
+
<div
|
|
176
|
+
style={{
|
|
177
|
+
padding: '6px 12px',
|
|
178
|
+
display: 'flex',
|
|
179
|
+
alignItems: 'center',
|
|
180
|
+
gap: 18,
|
|
181
|
+
fontFamily: 'monospace',
|
|
182
|
+
fontSize: 12,
|
|
183
|
+
color: '#aaa',
|
|
184
|
+
borderTop: '1px solid #333',
|
|
185
|
+
}}
|
|
186
|
+
>
|
|
187
|
+
<span>{components} components in {model.groups.filter((g) => g.kind === 'container').length} containers</span>
|
|
188
|
+
<span>{model.edges.length} lines</span>
|
|
189
|
+
<span>selected: {selected ?? '(none)'}</span>
|
|
190
|
+
<span style={{ color: '#777' }}>each component is a grouping stitched across many models</span>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export const Components: Story = {
|
|
197
|
+
render: () => <Demo />,
|
|
198
|
+
};
|
|
@@ -3,7 +3,7 @@ import '@xyflow/react/dist/style.css';
|
|
|
3
3
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
4
4
|
import { ThemeProvider, defaultEditorTheme } from '@principal-ade/industry-theme';
|
|
5
5
|
import { C4Graph } from '../../../subsystem/C4Graph';
|
|
6
|
-
import type { C4Edge, C4Model,
|
|
6
|
+
import type { C4Edge, C4Model, C4Element, C4Container } from '../../../subsystem/c4';
|
|
7
7
|
|
|
8
8
|
const meta = {
|
|
9
9
|
title: 'Subsystem/C4Graph/C4Container',
|
|
@@ -25,7 +25,7 @@ type Story = StoryObj<typeof meta>;
|
|
|
25
25
|
// ---------------------------------------------------------------------------
|
|
26
26
|
// A C4 container diagram, hand-built.
|
|
27
27
|
//
|
|
28
|
-
// Nothing here is derived: the boxes are `
|
|
28
|
+
// Nothing here is derived: the boxes are authored `C4Element`s, and
|
|
29
29
|
// the lines are explicit `C4Edge`s — no trails, no rollup. This is the shape a
|
|
30
30
|
// caller hands to `C4Graph` once a model has been authored.
|
|
31
31
|
//
|
|
@@ -35,18 +35,19 @@ type Story = StoryObj<typeof meta>;
|
|
|
35
35
|
|
|
36
36
|
const SYSTEM = { id: 'system:acme', label: 'Subsystem Modeling', repoKey: 'pkg:github/acme/subsystem-modeling' };
|
|
37
37
|
|
|
38
|
-
function container(over: Partial<
|
|
38
|
+
function container(over: Partial<C4Container> & Pick<C4Container, 'id' | 'label'>): C4Element {
|
|
39
39
|
return {
|
|
40
40
|
kind: 'container',
|
|
41
41
|
members: [],
|
|
42
42
|
constructs: [],
|
|
43
43
|
state: 'accepted',
|
|
44
44
|
containerKind: 'application',
|
|
45
|
+
technology: '',
|
|
45
46
|
...over,
|
|
46
47
|
};
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
const NODES:
|
|
50
|
+
const NODES: C4Element[] = [
|
|
50
51
|
container({
|
|
51
52
|
id: 'container:studio-host',
|
|
52
53
|
label: 'Subsystem Studio — host',
|
|
@@ -79,7 +80,7 @@ const NODES: C4Node[] = [
|
|
|
79
80
|
description: 'Every stored model and the correction queue.',
|
|
80
81
|
parentId: SYSTEM.id,
|
|
81
82
|
}),
|
|
82
|
-
// An external system drawn
|
|
83
|
+
// An external system: someone else's system, drawn OUTSIDE the boundary.
|
|
83
84
|
{
|
|
84
85
|
kind: 'external-system',
|
|
85
86
|
id: 'external:opencode',
|
|
@@ -89,7 +90,6 @@ const NODES: C4Node[] = [
|
|
|
89
90
|
state: 'accepted',
|
|
90
91
|
members: [],
|
|
91
92
|
constructs: [],
|
|
92
|
-
parentId: SYSTEM.id,
|
|
93
93
|
},
|
|
94
94
|
// A person sits outside every system boundary.
|
|
95
95
|
{
|
|
@@ -101,6 +101,42 @@ const NODES: C4Node[] = [
|
|
|
101
101
|
members: [],
|
|
102
102
|
constructs: [],
|
|
103
103
|
},
|
|
104
|
+
// Components that live inside the host. Not drawn in the container view
|
|
105
|
+
// (`groups` frames only the containers), but carried on the model so the
|
|
106
|
+
// side panel can list them when the host is clicked.
|
|
107
|
+
{
|
|
108
|
+
kind: 'component',
|
|
109
|
+
id: 'component:audit-verification',
|
|
110
|
+
label: 'Audit & verification',
|
|
111
|
+
container: 'container:studio-host',
|
|
112
|
+
parentId: 'container:studio-host',
|
|
113
|
+
technology: 'Bun',
|
|
114
|
+
description: 'Checks a subsystem model and reports what is wrong.',
|
|
115
|
+
state: 'accepted',
|
|
116
|
+
members: ['auditSubsystemModel', 'verifySubsystemComponent', 'verifyModelFiles'],
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
kind: 'component',
|
|
120
|
+
id: 'component:maintain-proposals',
|
|
121
|
+
label: 'Maintain & proposals',
|
|
122
|
+
container: 'container:studio-host',
|
|
123
|
+
parentId: 'container:studio-host',
|
|
124
|
+
technology: 'Bun',
|
|
125
|
+
description: 'Runs a maintain agent and records the corrections it proposes.',
|
|
126
|
+
state: 'accepted',
|
|
127
|
+
members: ['maintainSubsystemModel', 'runMaintainAgent', 'createSubsystemModelProposal'],
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
kind: 'component',
|
|
131
|
+
id: 'component:model-store',
|
|
132
|
+
label: 'Model store I/O',
|
|
133
|
+
container: 'container:studio-host',
|
|
134
|
+
parentId: 'container:studio-host',
|
|
135
|
+
technology: 'Bun fs',
|
|
136
|
+
description: 'Reads and writes model JSON under ~/.principal.',
|
|
137
|
+
state: 'accepted',
|
|
138
|
+
members: ['getSubsystemModel', 'updateSubsystemModel', 'readFile'],
|
|
139
|
+
},
|
|
104
140
|
];
|
|
105
141
|
|
|
106
142
|
const EDGES: C4Edge[] = [
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
nodeMissing,
|
|
13
13
|
NODE_W,
|
|
14
14
|
} from '../../../subsystem/C4NodeCard';
|
|
15
|
-
import type {
|
|
15
|
+
import type { C4Element } from '../../../subsystem/c4';
|
|
16
16
|
|
|
17
17
|
// ---------------------------------------------------------------------------
|
|
18
18
|
// A card gallery: every C4 box variant, outside the graph.
|
|
@@ -25,7 +25,7 @@ import type { C4Node } from '../../../subsystem/c4';
|
|
|
25
25
|
// is a plain presentational component, so it can be reviewed and diffed
|
|
26
26
|
// without a layout engine in the way.
|
|
27
27
|
//
|
|
28
|
-
// Every card here is
|
|
28
|
+
// Every card here is an authored `C4Element` — the shape a C4 box draws with,
|
|
29
29
|
// produces from an authored element, with the per-kind fields already resolved
|
|
30
30
|
// to the one shape the card draws with.
|
|
31
31
|
// ---------------------------------------------------------------------------
|
|
@@ -48,17 +48,19 @@ type Story = StoryObj<typeof meta>;
|
|
|
48
48
|
|
|
49
49
|
// --- sample nodes ---------------------------------------------------------
|
|
50
50
|
|
|
51
|
-
function node(over:
|
|
51
|
+
function node(over: Record<string, unknown> & { id: string; label: string }): C4Element {
|
|
52
52
|
return {
|
|
53
53
|
kind: 'container',
|
|
54
|
+
containerKind: 'application',
|
|
55
|
+
technology: '',
|
|
56
|
+
state: 'proposed',
|
|
54
57
|
members: [],
|
|
55
58
|
constructs: [],
|
|
56
|
-
state: 'proposed',
|
|
57
59
|
...over,
|
|
58
|
-
};
|
|
60
|
+
} as C4Element;
|
|
59
61
|
}
|
|
60
62
|
|
|
61
|
-
const VARIANTS: Array<{ group: string; note: string; node:
|
|
63
|
+
const VARIANTS: Array<{ group: string; note: string; node: C4Element }> = [
|
|
62
64
|
// `id` on each variant is its caption key, not a component field.
|
|
63
65
|
|
|
64
66
|
// --- confirmation state: the axis that matters most --------------------
|
|
@@ -356,7 +358,7 @@ function Gallery() {
|
|
|
356
358
|
width: 28,
|
|
357
359
|
height: Math.round((28 * size.height) / size.width),
|
|
358
360
|
borderRadius: Math.min(s.radius, 9),
|
|
359
|
-
border: `${weight} ${
|
|
361
|
+
border: `${weight} ${nodeStyle(node({ id: 'x', label: '', kind }), theme).dash} ${color}`,
|
|
360
362
|
display: 'inline-block',
|
|
361
363
|
}}
|
|
362
364
|
/>
|