@principal-ai/subsystems-react 0.53.0 → 0.53.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,131 @@
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, C4Model, C4Element, C4Component, C4Container } from '../../../subsystem/c4';
7
+
8
+ const meta = {
9
+ title: 'Subsystem/C4Graph/C4Transition',
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
+ // Drill-down: two layouts, one transition.
27
+ //
28
+ // Closed, the container is a box. Its expand affordance (bottom-right) opens it,
29
+ // and the graph swaps to a *second, already-computed* layout in which the same
30
+ // element is an ELK parent — so ELK sizes it to hold its own components, and they
31
+ // arrive as nested cards. The same affordance closes it again.
32
+ //
33
+ // Nothing is a frame here: the container stays a card and grows (its own
34
+ // width/height transition), while the system frame around it morphs by transform.
35
+ // Components stay hidden until the grow settles, and the card's description
36
+ // returns only after the shrink — hence the two settled states below.
37
+ //
38
+ // The story also carries component→component edges, which the nested layout
39
+ // routes inside the parent.
40
+ // ---------------------------------------------------------------------------
41
+
42
+ const SYSTEM = { id: 'system:acme', label: 'Subsystem Modeling', repoKey: 'pkg:github/acme/subsystem-modeling' };
43
+ const HOST = 'container:studio-host';
44
+
45
+ function container(over: Partial<C4Container> & Pick<C4Container, 'id' | 'label'>): C4Element {
46
+ return {
47
+ kind: 'container',
48
+ containerKind: 'application',
49
+ technology: '',
50
+ state: 'accepted',
51
+ members: [],
52
+ constructs: [],
53
+ parentId: SYSTEM.id,
54
+ ...over,
55
+ };
56
+ }
57
+
58
+ function component(
59
+ over: Partial<C4Component> & Pick<C4Component, 'id' | 'label' | 'container'>,
60
+ ): C4Element {
61
+ return {
62
+ kind: 'component',
63
+ members: [],
64
+ constructs: [],
65
+ state: 'accepted',
66
+ parentId: over.container,
67
+ technology: 'Bun',
68
+ ...over,
69
+ };
70
+ }
71
+
72
+ const NODES: C4Element[] = [
73
+ container({ id: HOST, label: 'Subsystem Studio — host', technology: 'Bun + Electrobun', description: 'The Electron main process.' }),
74
+ component({ id: 'component:audit-verification', label: 'Audit & verification', container: HOST, description: 'Checks a model, reports problems.' }),
75
+ component({ id: 'component:maintain-proposals', label: 'Maintain & proposals', container: HOST, description: 'Runs the maintain agent, records proposals.' }),
76
+ component({ id: 'component:model-store', label: 'Model store I/O', container: HOST, technology: 'Bun fs', description: 'Reads and writes model JSON.' }),
77
+ ];
78
+
79
+ function edge(id: string, source: string, target: string, protocol: string): C4Edge {
80
+ return { id, source, target, label: protocol, protocol, mechanisms: [], count: 1 };
81
+ }
82
+
83
+ const EDGES: C4Edge[] = [
84
+ edge('e:audit-store', 'component:audit-verification', 'component:model-store', 'fs · JSON'),
85
+ edge('e:proposals-store', 'component:maintain-proposals', 'component:model-store', 'fs · JSON'),
86
+ ];
87
+
88
+ function Demo() {
89
+ const [openId, setOpenId] = useState<string | null>(null);
90
+ const [selected, setSelected] = useState<string | null>(null);
91
+
92
+ const model = useMemo<C4Model>(
93
+ () => ({ view: 'container', system: SYSTEM, nodes: NODES, edges: EDGES, openContainerId: openId }),
94
+ [openId],
95
+ );
96
+
97
+ return (
98
+ <div style={{ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }}>
99
+ <div style={{ flex: 1, minHeight: 0, position: 'relative' }}>
100
+ <C4Graph
101
+ model={model}
102
+ onSelectNode={setSelected}
103
+ onOpenContainer={setOpenId}
104
+ onCloseContainer={() => setOpenId(null)}
105
+ />
106
+ </div>
107
+ <div
108
+ style={{
109
+ padding: '6px 12px',
110
+ display: 'flex',
111
+ alignItems: 'center',
112
+ gap: 18,
113
+ fontFamily: 'monospace',
114
+ fontSize: 12,
115
+ color: '#aaa',
116
+ borderTop: '1px solid #333',
117
+ }}
118
+ >
119
+ <span>{openId ? `opened: ${openId}` : 'closed'}</span>
120
+ <span>selected: {selected ?? '(none)'}</span>
121
+ <span style={{ color: '#777' }}>
122
+ the expand icon (bottom-right of the container) opens it, and closes it again
123
+ </span>
124
+ </div>
125
+ </div>
126
+ );
127
+ }
128
+
129
+ export const DrillDown: Story = {
130
+ render: () => <Demo />,
131
+ };