@principal-ai/subsystems-react 0.51.0 → 0.51.2
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 +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/pierre/PierreTrailCodeView.d.ts +1 -1
- package/dist/pierre/PierreTrailCodeView.d.ts.map +1 -1
- package/dist/pierre/PierreTrailCodeView.js +44 -21
- package/dist/pierre/PierreTrailCodeView.js.map +1 -1
- package/dist/stories/Subsystem/C4Graph/c4ProposedElements.fixture.d.ts +493 -0
- package/dist/stories/Subsystem/C4Graph/c4ProposedElements.fixture.d.ts.map +1 -0
- package/dist/stories/Subsystem/C4Graph/c4ProposedElements.fixture.js +329 -0
- package/dist/stories/Subsystem/C4Graph/c4ProposedElements.fixture.js.map +1 -0
- package/dist/subsystem/C4Graph.d.ts +2 -2
- package/dist/subsystem/C4Graph.d.ts.map +1 -1
- package/dist/subsystem/C4Graph.js +25 -16
- package/dist/subsystem/C4Graph.js.map +1 -1
- package/dist/subsystem/C4NodeCard.d.ts +85 -35
- package/dist/subsystem/C4NodeCard.d.ts.map +1 -1
- package/dist/subsystem/C4NodeCard.js +112 -83
- package/dist/subsystem/C4NodeCard.js.map +1 -1
- package/dist/subsystem/FileDrawer.d.ts.map +1 -1
- package/dist/subsystem/FileDrawer.js +20 -21
- package/dist/subsystem/FileDrawer.js.map +1 -1
- package/dist/subsystem/IssueList.d.ts.map +1 -1
- package/dist/subsystem/IssueList.js +12 -3
- package/dist/subsystem/IssueList.js.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.js +85 -7
- package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
- package/dist/subsystem/c4.d.ts +273 -0
- package/dist/subsystem/c4.d.ts.map +1 -0
- package/dist/subsystem/c4.js +322 -0
- package/dist/subsystem/c4.js.map +1 -0
- package/dist/subsystem/c4Evidence.d.ts +143 -0
- package/dist/subsystem/c4Evidence.d.ts.map +1 -0
- package/dist/subsystem/c4Evidence.js +240 -0
- package/dist/subsystem/c4Evidence.js.map +1 -0
- package/dist/subsystem/model.d.ts +2 -6
- package/dist/subsystem/model.d.ts.map +1 -1
- package/dist/subsystem/model.js.map +1 -1
- package/package.json +5 -4
- package/src/index.ts +26 -28
- package/src/pierre/PierreTrailCodeView.tsx +49 -21
- package/src/stories/Subsystem/C4Graph/C4NodeGallery.stories.tsx +171 -80
- package/src/stories/Subsystem/C4Graph/ProposedElements.stories.tsx +293 -0
- package/src/stories/Subsystem/C4Graph/c4ProposedElements.fixture.ts +366 -0
- package/src/stories/Subsystem/ComponentGraph/IssueOverlay.stories.tsx +1 -1
- package/src/stories/Subsystem/ComponentGraph/Issues.stories.tsx +169 -3
- package/src/subsystem/C4Graph.tsx +39 -24
- package/src/subsystem/C4NodeCard.test.ts +204 -62
- package/src/subsystem/C4NodeCard.tsx +146 -101
- package/src/subsystem/FileDrawer.tsx +32 -34
- package/src/subsystem/IssueList.tsx +13 -2
- package/src/subsystem/SubsystemComponentGraph.tsx +98 -7
- package/src/subsystem/c4.test.ts +573 -0
- package/src/subsystem/c4.ts +559 -0
- package/src/subsystem/c4Evidence.test.ts +386 -0
- package/src/subsystem/c4Evidence.ts +329 -0
- package/src/subsystem/model.ts +2 -6
- package/dist/stories/Subsystem/C4Graph/c4Associations.fixture.d.ts +0 -95
- package/dist/stories/Subsystem/C4Graph/c4Associations.fixture.d.ts.map +0 -1
- package/dist/stories/Subsystem/C4Graph/c4Associations.fixture.js +0 -340
- package/dist/stories/Subsystem/C4Graph/c4Associations.fixture.js.map +0 -1
- package/dist/stories/Subsystem/C4Graph/c4Fixture.d.ts +0 -7
- package/dist/stories/Subsystem/C4Graph/c4Fixture.d.ts.map +0 -1
- package/dist/stories/Subsystem/C4Graph/c4Fixture.js +0 -7335
- package/dist/stories/Subsystem/C4Graph/c4Fixture.js.map +0 -1
- package/dist/stories/Subsystem/C4Graph/generateFixture.d.ts +0 -2
- package/dist/stories/Subsystem/C4Graph/generateFixture.d.ts.map +0 -1
- package/dist/stories/Subsystem/C4Graph/generateFixture.js +0 -145
- package/dist/stories/Subsystem/C4Graph/generateFixture.js.map +0 -1
- package/dist/subsystem/c4Associations.d.ts +0 -186
- package/dist/subsystem/c4Associations.d.ts.map +0 -1
- package/dist/subsystem/c4Associations.js +0 -284
- package/dist/subsystem/c4Associations.js.map +0 -1
- package/dist/subsystem/toC4.d.ts +0 -214
- package/dist/subsystem/toC4.d.ts.map +0 -1
- package/dist/subsystem/toC4.js +0 -282
- package/dist/subsystem/toC4.js.map +0 -1
- package/src/stories/Subsystem/C4Graph/C4Associations.stories.tsx +0 -273
- package/src/stories/Subsystem/C4Graph/C4Container.stories.tsx +0 -101
- package/src/stories/Subsystem/C4Graph/c4Associations.fixture.ts +0 -344
- package/src/stories/Subsystem/C4Graph/c4Fixture.ts +0 -7339
- package/src/stories/Subsystem/C4Graph/generateFixture.ts +0 -161
- package/src/subsystem/c4Associations.test.ts +0 -484
- package/src/subsystem/c4Associations.ts +0 -398
- package/src/subsystem/toC4.test.ts +0 -114
- package/src/subsystem/toC4.ts +0 -478
|
@@ -1,273 +0,0 @@
|
|
|
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 type { C4Association } from '../../../subsystem/toC4';
|
|
8
|
-
import { summarizeAssociations, mergeAssociations } from '../../../subsystem/c4Associations';
|
|
9
|
-
import { c4FixtureModel, c4MemberCounts } from './c4Fixture';
|
|
10
|
-
import { c4Associations, C4_MERGE_CANDIDATES, C4_CONCERNS } from './c4Associations.fixture';
|
|
11
|
-
|
|
12
|
-
const MEMBER_COUNTS = new Map(Object.entries(c4MemberCounts));
|
|
13
|
-
|
|
14
|
-
const meta = {
|
|
15
|
-
title: 'Subsystem/C4Graph/C4Associations',
|
|
16
|
-
component: C4Graph,
|
|
17
|
-
parameters: { layout: 'fullscreen' },
|
|
18
|
-
tags: ['autodocs'],
|
|
19
|
-
decorators: [
|
|
20
|
-
(Story) => (
|
|
21
|
-
<ThemeProvider theme={defaultEditorTheme}>
|
|
22
|
-
<Story />
|
|
23
|
-
</ThemeProvider>
|
|
24
|
-
),
|
|
25
|
-
],
|
|
26
|
-
} satisfies Meta<typeof C4Graph>;
|
|
27
|
-
|
|
28
|
-
export default meta;
|
|
29
|
-
type Story = StoryObj<typeof meta>;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* C4 associations — the confirmed layer over a raw derivation.
|
|
33
|
-
*
|
|
34
|
-
* `toC4()` alone rolls components up into boxes keyed by `process`. That
|
|
35
|
-
* derivation is a guess: two keys may be the same deployable, a library may be
|
|
36
|
-
* pretending to be a container, and a store may not be a C4 data store at all.
|
|
37
|
-
*
|
|
38
|
-
* This story starts from a real composite of 40 stored models and lets you act
|
|
39
|
-
* on the 13 proposed associations:
|
|
40
|
-
*
|
|
41
|
-
* accept the box goes solid — it is part of the architecture
|
|
42
|
-
* reject the box disappears, and so do the edges into it
|
|
43
|
-
* merge two keys collapse into one box
|
|
44
|
-
*
|
|
45
|
-
* Try `merge host` first: it takes `subsystems-studio/host` (113 components)
|
|
46
|
-
* and `principal-studio/host` (1 component) and folds them into one container.
|
|
47
|
-
*/
|
|
48
|
-
|
|
49
|
-
function Demo({ initialAccepted = [] as string[] }: { initialAccepted?: string[] }) {
|
|
50
|
-
const [view, setView] = useState<C4View>('container');
|
|
51
|
-
const [selected, setSelected] = useState<string | null>(null);
|
|
52
|
-
const [state, setState] = useState<Record<string, C4Association['state']>>(() =>
|
|
53
|
-
Object.fromEntries(initialAccepted.map((id) => [id, 'accepted' as const])),
|
|
54
|
-
);
|
|
55
|
-
/** Roles the user has merged. Folds the candidates into one association each. */
|
|
56
|
-
const [mergedRoles, setMergedRoles] = useState<string[]>([]);
|
|
57
|
-
|
|
58
|
-
const associations = useMemo<C4Association[]>(() => {
|
|
59
|
-
let set: C4Association[] = c4Associations;
|
|
60
|
-
for (const role of mergedRoles) {
|
|
61
|
-
const candidate = C4_MERGE_CANDIDATES.find((c) => c.role === role);
|
|
62
|
-
if (!candidate) continue;
|
|
63
|
-
// The evidence-heavy side survives automatically — see mergeAssociations.
|
|
64
|
-
set = mergeAssociations(set, candidate.keys, undefined, MEMBER_COUNTS);
|
|
65
|
-
}
|
|
66
|
-
// Apply the user's accept/reject last, over the merged set.
|
|
67
|
-
return set.map((a) => ({ ...a, state: state[a.id] ?? a.state }));
|
|
68
|
-
}, [state, mergedRoles]);
|
|
69
|
-
|
|
70
|
-
const model = useMemo(
|
|
71
|
-
() => toC4(c4FixtureModel, { view, systemLabel: 'Subsystem Modeling', associations }),
|
|
72
|
-
[view, associations],
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
const stats = useMemo(() => summarizeAssociations(model, associations), [model, associations]);
|
|
76
|
-
|
|
77
|
-
const concernsFor = (id: string) => {
|
|
78
|
-
const a = associations.find((x) => x.id === id);
|
|
79
|
-
if (!a) return [];
|
|
80
|
-
return C4_CONCERNS.filter((c) => a.sourceKeys.includes(c.target));
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const decide = (id: string, next: C4Association['state']) =>
|
|
84
|
-
setState((prev) => ({ ...prev, [id]: next }));
|
|
85
|
-
|
|
86
|
-
const reset = () => {
|
|
87
|
-
setState({});
|
|
88
|
-
setMergedRoles([]);
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const button = (label: string, value: C4View) => (
|
|
92
|
-
<button
|
|
93
|
-
type="button"
|
|
94
|
-
onClick={() => setView(value)}
|
|
95
|
-
style={{
|
|
96
|
-
fontFamily: 'monospace',
|
|
97
|
-
fontSize: 12,
|
|
98
|
-
padding: '2px 10px',
|
|
99
|
-
borderRadius: 4,
|
|
100
|
-
cursor: 'pointer',
|
|
101
|
-
border: '1px solid #555',
|
|
102
|
-
background: view === value ? '#1D9E75' : 'transparent',
|
|
103
|
-
color: view === value ? '#020B12' : '#ddd',
|
|
104
|
-
}}
|
|
105
|
-
>
|
|
106
|
-
{label}
|
|
107
|
-
</button>
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
const act = (label: string, onClick: () => void) => (
|
|
111
|
-
<button
|
|
112
|
-
type="button"
|
|
113
|
-
onClick={onClick}
|
|
114
|
-
style={{
|
|
115
|
-
fontFamily: 'monospace',
|
|
116
|
-
fontSize: 11,
|
|
117
|
-
padding: '2px 8px',
|
|
118
|
-
borderRadius: 4,
|
|
119
|
-
cursor: 'pointer',
|
|
120
|
-
border: '1px solid #555',
|
|
121
|
-
background: 'transparent',
|
|
122
|
-
color: '#ddd',
|
|
123
|
-
}}
|
|
124
|
-
>
|
|
125
|
-
{label}
|
|
126
|
-
</button>
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
return (
|
|
130
|
-
<div style={{ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column', background: '#12141a' }}>
|
|
131
|
-
<div style={{ flex: 1, minHeight: 0, position: 'relative' }}>
|
|
132
|
-
<C4Graph model={model} title="Subsystem Modeling" onSelectNode={setSelected} />
|
|
133
|
-
</div>
|
|
134
|
-
|
|
135
|
-
<div
|
|
136
|
-
style={{
|
|
137
|
-
maxHeight: 280,
|
|
138
|
-
overflowY: 'auto',
|
|
139
|
-
padding: '8px 12px',
|
|
140
|
-
display: 'flex',
|
|
141
|
-
gap: 18,
|
|
142
|
-
fontFamily: 'monospace',
|
|
143
|
-
fontSize: 12,
|
|
144
|
-
color: '#aaa',
|
|
145
|
-
borderTop: '1px solid #333',
|
|
146
|
-
}}
|
|
147
|
-
>
|
|
148
|
-
<div style={{ minWidth: 210 }}>
|
|
149
|
-
<div style={{ display: 'flex', gap: 6, marginBottom: 8 }}>
|
|
150
|
-
{button('container', 'container')}
|
|
151
|
-
{button('component', 'component')}
|
|
152
|
-
</div>
|
|
153
|
-
<div>
|
|
154
|
-
{stats.confirmed} confirmed · {stats.proposed} proposed
|
|
155
|
-
</div>
|
|
156
|
-
<div>{stats.rejected} rejected · {stats.mergedAway} keys merged away</div>
|
|
157
|
-
<div style={{ color: stats.missingTechnology > 0 ? '#e8a33a' : '#5a5' }}>
|
|
158
|
-
{stats.missingTechnology} containers missing technology
|
|
159
|
-
</div>
|
|
160
|
-
<div>{stats.needsDecision} still need a decision</div>
|
|
161
|
-
<div style={{ marginTop: 8 }}>{act('reset', reset)}</div>
|
|
162
|
-
</div>
|
|
163
|
-
|
|
164
|
-
{/* The merge question, with one click to try it. */}
|
|
165
|
-
<div style={{ minWidth: 300, maxWidth: 340 }}>
|
|
166
|
-
<div style={{ color: '#ddd', marginBottom: 4 }}>MERGE CANDIDATES</div>
|
|
167
|
-
{C4_MERGE_CANDIDATES.map((c) => {
|
|
168
|
-
const done = mergedRoles.includes(c.role);
|
|
169
|
-
// Show which side the evidence will keep, before you click.
|
|
170
|
-
const heaviest = c.keys.reduce((a, b) =>
|
|
171
|
-
(c4MemberCounts[b] ?? 0) > (c4MemberCounts[a] ?? 0) ? b : a,
|
|
172
|
-
);
|
|
173
|
-
return (
|
|
174
|
-
<div key={c.role} style={{ marginBottom: 8 }}>
|
|
175
|
-
{c.keys.map((k) => (
|
|
176
|
-
<div
|
|
177
|
-
key={k}
|
|
178
|
-
style={{
|
|
179
|
-
color: done && k === heaviest ? '#5a5' : '#888',
|
|
180
|
-
lineHeight: 1.35,
|
|
181
|
-
fontWeight: done && k === heaviest ? 600 : 400,
|
|
182
|
-
}}
|
|
183
|
-
>
|
|
184
|
-
{k} · {c4MemberCounts[k] ?? 0}
|
|
185
|
-
</div>
|
|
186
|
-
))}
|
|
187
|
-
<div style={{ display: 'flex', gap: 6, alignItems: 'center', marginTop: 3 }}>
|
|
188
|
-
{done ? (
|
|
189
|
-
<>
|
|
190
|
-
<span style={{ color: '#5a5' }}>merged — kept {heaviest.split('/').pop()}</span>
|
|
191
|
-
{act('undo', () => setMergedRoles((prev) => prev.filter((r) => r !== c.role)))}
|
|
192
|
-
</>
|
|
193
|
-
) : (
|
|
194
|
-
act(`merge ${c.role}`, () => setMergedRoles((prev) => [...prev, c.role]))
|
|
195
|
-
)}
|
|
196
|
-
</div>
|
|
197
|
-
</div>
|
|
198
|
-
);
|
|
199
|
-
})}
|
|
200
|
-
</div>
|
|
201
|
-
|
|
202
|
-
{/* Per-association review. This is the surface a real confirm UI would use. */}
|
|
203
|
-
<div style={{ flex: 1, minWidth: 380 }}>
|
|
204
|
-
<div style={{ color: '#ddd', marginBottom: 4 }}>
|
|
205
|
-
ASSOCIATIONS · {associations.length}
|
|
206
|
-
</div>
|
|
207
|
-
{associations.map((a) => {
|
|
208
|
-
const s = state[a.id] ?? a.state;
|
|
209
|
-
const concerns = concernsFor(a.id);
|
|
210
|
-
const missing: string[] = [];
|
|
211
|
-
if (!a.technology) missing.push('technology');
|
|
212
|
-
if (!a.description) missing.push('description');
|
|
213
|
-
return (
|
|
214
|
-
<div
|
|
215
|
-
key={a.id}
|
|
216
|
-
style={{
|
|
217
|
-
display: 'flex',
|
|
218
|
-
gap: 10,
|
|
219
|
-
alignItems: 'flex-start',
|
|
220
|
-
padding: '4px 0',
|
|
221
|
-
borderBottom: '1px solid #222',
|
|
222
|
-
opacity: s === 'rejected' ? 0.45 : 1,
|
|
223
|
-
}}
|
|
224
|
-
>
|
|
225
|
-
<div style={{ flex: 1, minWidth: 0 }}>
|
|
226
|
-
<div style={{ color: '#ddd' }}>
|
|
227
|
-
{a.label}
|
|
228
|
-
<span style={{ color: s === 'accepted' ? '#5a5' : s === 'proposed' ? '#e8a33a' : '#888' }}>
|
|
229
|
-
{' '}
|
|
230
|
-
· {s} · {a.type}
|
|
231
|
-
</span>
|
|
232
|
-
</div>
|
|
233
|
-
<div style={{ color: '#777', fontSize: 11 }}>
|
|
234
|
-
keys: {a.sourceKeys.join(', ')}
|
|
235
|
-
{a.technology ? ` · tech: ${a.technology}` : ''}
|
|
236
|
-
{missing.length > 0 ? ` · needs ${missing.join(' + ')}` : ''}
|
|
237
|
-
</div>
|
|
238
|
-
{concerns.length > 0 && (
|
|
239
|
-
<div style={{ color: '#c96', fontSize: 11, lineHeight: 1.35 }}>
|
|
240
|
-
{concerns.map((c) => (
|
|
241
|
-
<div key={c.kind + c.target}>⚠ {c.message}</div>
|
|
242
|
-
))}
|
|
243
|
-
</div>
|
|
244
|
-
)}
|
|
245
|
-
</div>
|
|
246
|
-
<div style={{ display: 'flex', gap: 4 }}>
|
|
247
|
-
{act('accept', () => decide(a.id, 'accepted'))}
|
|
248
|
-
{act('reject', () => decide(a.id, 'rejected'))}
|
|
249
|
-
</div>
|
|
250
|
-
</div>
|
|
251
|
-
);
|
|
252
|
-
})}
|
|
253
|
-
</div>
|
|
254
|
-
|
|
255
|
-
<div style={{ minWidth: 130, fontSize: 11, color: '#777' }}>
|
|
256
|
-
<div>selected: {selected ?? '(none)'}</div>
|
|
257
|
-
<div style={{ marginTop: 6 }}>solid = confirmed</div>
|
|
258
|
-
<div>dashed = proposed</div>
|
|
259
|
-
<div>muted = unconfirmed</div>
|
|
260
|
-
</div>
|
|
261
|
-
</div>
|
|
262
|
-
</div>
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
export const Associations: Story = {
|
|
267
|
-
render: () => <Demo />,
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
/** Start from a half-confirmed state so the visual difference is obvious. */
|
|
271
|
-
export const PartiallyConfirmed: Story = {
|
|
272
|
-
render: () => <Demo initialAccepted={['container:subsystems-studio/host', 'container:subsystems-studio/renderer']} />,
|
|
273
|
-
};
|
|
@@ -1,101 +0,0 @@
|
|
|
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
|
-
flows: model.edges.filter((e) => e.kind === 'flow').length,
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const button = (label: string, value: C4View) => (
|
|
49
|
-
<button
|
|
50
|
-
type="button"
|
|
51
|
-
onClick={() => setView(value)}
|
|
52
|
-
style={{
|
|
53
|
-
fontFamily: 'monospace',
|
|
54
|
-
fontSize: 12,
|
|
55
|
-
padding: '2px 10px',
|
|
56
|
-
borderRadius: 4,
|
|
57
|
-
cursor: 'pointer',
|
|
58
|
-
border: '1px solid #555',
|
|
59
|
-
background: view === value ? '#1D9E75' : 'transparent',
|
|
60
|
-
color: view === value ? '#020B12' : '#ddd',
|
|
61
|
-
}}
|
|
62
|
-
>
|
|
63
|
-
{label}
|
|
64
|
-
</button>
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
return (
|
|
68
|
-
<div style={{ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }}>
|
|
69
|
-
<div style={{ flex: 1, minHeight: 0, position: 'relative' }}>
|
|
70
|
-
<C4Graph model={model} title="Subsystem Modeling" onSelectNode={setSelected} />
|
|
71
|
-
</div>
|
|
72
|
-
<div
|
|
73
|
-
style={{
|
|
74
|
-
padding: '6px 12px',
|
|
75
|
-
display: 'flex',
|
|
76
|
-
alignItems: 'center',
|
|
77
|
-
gap: 16,
|
|
78
|
-
fontFamily: 'monospace',
|
|
79
|
-
fontSize: 12,
|
|
80
|
-
color: '#aaa',
|
|
81
|
-
borderTop: '1px solid #333',
|
|
82
|
-
}}
|
|
83
|
-
>
|
|
84
|
-
<span style={{ display: 'flex', gap: 6 }}>
|
|
85
|
-
view: {button('container', 'container')} {button('component', 'component')}
|
|
86
|
-
</span>
|
|
87
|
-
<span>
|
|
88
|
-
{counts.containers} containers · {counts.components} components · {counts.externals} externals ·{' '}
|
|
89
|
-
{counts.actors} actors
|
|
90
|
-
</span>
|
|
91
|
-
<span>{counts.flows} flow edges</span>
|
|
92
|
-
<span>selected: {selected ?? '(none)'}</span>
|
|
93
|
-
</div>
|
|
94
|
-
</div>
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export const C4: Story = {
|
|
99
|
-
args: { model: toC4(c4FixtureModel, { view: 'container', systemLabel: 'Subsystem Modeling' }) },
|
|
100
|
-
render: () => <C4Demo />,
|
|
101
|
-
};
|