@principal-ai/principal-view-react 0.14.24 → 0.14.26
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/components/GraphRenderer.d.ts.map +1 -1
- package/dist/components/GraphRenderer.js +2 -1
- package/dist/components/GraphRenderer.js.map +1 -1
- package/dist/components/SequenceDiagramRenderer.d.ts.map +1 -1
- package/dist/components/SequenceDiagramRenderer.js +83 -7
- package/dist/components/SequenceDiagramRenderer.js.map +1 -1
- package/dist/components/WorkflowSequenceDiagram.d.ts +52 -0
- package/dist/components/WorkflowSequenceDiagram.d.ts.map +1 -0
- package/dist/components/WorkflowSequenceDiagram.js +127 -0
- package/dist/components/WorkflowSequenceDiagram.js.map +1 -0
- package/dist/hooks/useSequenceLayout.d.ts +4 -0
- package/dist/hooks/useSequenceLayout.d.ts.map +1 -1
- package/dist/hooks/useSequenceLayout.js +66 -25
- package/dist/hooks/useSequenceLayout.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/graphConverter.d.ts.map +1 -1
- package/dist/utils/graphConverter.js +15 -1
- package/dist/utils/graphConverter.js.map +1 -1
- package/package.json +3 -3
- package/src/components/GraphRenderer.tsx +2 -1
- package/src/components/SequenceDiagramRenderer.tsx +162 -7
- package/src/components/WorkflowSequenceDiagram.tsx +222 -0
- package/src/hooks/useSequenceLayout.ts +73 -28
- package/src/index.ts +3 -0
- package/src/stories/FileCitySequence.stories.tsx +544 -0
- package/src/stories/data/file-city-images-transformed.otel.canvas.json +295 -0
- package/src/stories/data/file-city-workflows.json +269 -0
- package/src/utils/graphConverter.ts +14 -1
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
import { SequenceDiagramRenderer } from '../components/SequenceDiagramRenderer';
|
|
4
|
+
import { WorkflowSequenceDiagram } from '../components/WorkflowSequenceDiagram';
|
|
5
|
+
import { ThemeProvider, defaultEditorTheme } from '@principal-ade/industry-theme';
|
|
6
|
+
import workflowData from './data/file-city-workflows.json';
|
|
7
|
+
import type { WorkflowScenario } from '@principal-ai/principal-view-core';
|
|
8
|
+
|
|
9
|
+
const meta = {
|
|
10
|
+
title: 'Workflows/File City Image Generation',
|
|
11
|
+
component: SequenceDiagramRenderer,
|
|
12
|
+
parameters: {
|
|
13
|
+
layout: 'fullscreen',
|
|
14
|
+
docs: {
|
|
15
|
+
description: {
|
|
16
|
+
component: `
|
|
17
|
+
# File City Image Generation Workflows
|
|
18
|
+
|
|
19
|
+
This demonstrates the **graph-to-sequence** concepts applied to the File City image generation system.
|
|
20
|
+
|
|
21
|
+
## Participant Model
|
|
22
|
+
|
|
23
|
+
Following the sequence-first architecture:
|
|
24
|
+
|
|
25
|
+
- **Nodes** = Participants (Main Process, Renderer Process)
|
|
26
|
+
- **Edges** = Move Events (IPC calls crossing process boundaries)
|
|
27
|
+
- **Transform Events** = Internal processing within each participant
|
|
28
|
+
|
|
29
|
+
## Key Concepts
|
|
30
|
+
|
|
31
|
+
- **Sequence diagrams are primary** - They show actual execution traces
|
|
32
|
+
- **Graphs aggregate sequences** - The canvas shows all possible paths
|
|
33
|
+
- **Move vs Transform Events**:
|
|
34
|
+
- **Move**: Data crossing participant boundaries (IPC calls)
|
|
35
|
+
- **Transform**: Internal processing within a participant
|
|
36
|
+
|
|
37
|
+
See \`/docs/GRAPH_TO_SEQUENCE_CONCEPTS.md\` for the full conceptual model.
|
|
38
|
+
`,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
tags: ['autodocs'],
|
|
43
|
+
decorators: [
|
|
44
|
+
(Story) => (
|
|
45
|
+
<ThemeProvider theme={defaultEditorTheme}>
|
|
46
|
+
<div style={{ height: '100vh', boxSizing: 'border-box' }}>
|
|
47
|
+
<Story />
|
|
48
|
+
</div>
|
|
49
|
+
</ThemeProvider>
|
|
50
|
+
),
|
|
51
|
+
],
|
|
52
|
+
} satisfies Meta<typeof SequenceDiagramRenderer>;
|
|
53
|
+
|
|
54
|
+
export default meta;
|
|
55
|
+
type Story = StoryObj<typeof meta>;
|
|
56
|
+
|
|
57
|
+
// ============================================================================
|
|
58
|
+
// Helper Components
|
|
59
|
+
// ============================================================================
|
|
60
|
+
|
|
61
|
+
interface SequenceWithEventsProps {
|
|
62
|
+
events: SequenceEvent[];
|
|
63
|
+
edges: SequenceEdge[];
|
|
64
|
+
height?: number;
|
|
65
|
+
layoutOptions?: any;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const SequenceWithEventsList: React.FC<SequenceWithEventsProps> = ({
|
|
69
|
+
events,
|
|
70
|
+
edges,
|
|
71
|
+
height = 500,
|
|
72
|
+
layoutOptions,
|
|
73
|
+
}) => {
|
|
74
|
+
return (
|
|
75
|
+
<div style={{ display: 'flex', flexDirection: 'column', height: '100vh' }}>
|
|
76
|
+
{/* Top Half - Sequence Diagram */}
|
|
77
|
+
<div style={{ flex: 1, overflow: 'hidden' }}>
|
|
78
|
+
<SequenceDiagramRenderer
|
|
79
|
+
events={events}
|
|
80
|
+
edges={edges}
|
|
81
|
+
height={height}
|
|
82
|
+
layoutOptions={layoutOptions}
|
|
83
|
+
/>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
{/* Bottom Half - Event List */}
|
|
87
|
+
<div style={{
|
|
88
|
+
flex: 1,
|
|
89
|
+
overflow: 'auto',
|
|
90
|
+
background: '#1e1e1e',
|
|
91
|
+
color: '#d4d4d4',
|
|
92
|
+
fontFamily: 'monospace',
|
|
93
|
+
fontSize: 13,
|
|
94
|
+
}}>
|
|
95
|
+
<div style={{ padding: '16px' }}>
|
|
96
|
+
<h3 style={{ margin: '0 0 12px 0', color: '#4ec9b0' }}>Event Sequence</h3>
|
|
97
|
+
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
|
|
98
|
+
<thead>
|
|
99
|
+
<tr style={{ borderBottom: '1px solid #3e3e3e' }}>
|
|
100
|
+
<th style={{ textAlign: 'left', padding: '8px', color: '#9cdcfe' }}>Order</th>
|
|
101
|
+
<th style={{ textAlign: 'left', padding: '8px', color: '#9cdcfe' }}>Event Name</th>
|
|
102
|
+
<th style={{ textAlign: 'left', padding: '8px', color: '#9cdcfe' }}>Label</th>
|
|
103
|
+
<th style={{ textAlign: 'left', padding: '8px', color: '#9cdcfe' }}>Type</th>
|
|
104
|
+
</tr>
|
|
105
|
+
</thead>
|
|
106
|
+
<tbody>
|
|
107
|
+
{events.map((event, index) => {
|
|
108
|
+
const isMoveEvent = (event as any).moveEvent;
|
|
109
|
+
const participant = event.name.split('.')[0];
|
|
110
|
+
return (
|
|
111
|
+
<tr key={event.id} style={{ borderBottom: '1px solid #2a2a2a' }}>
|
|
112
|
+
<td style={{ padding: '8px', color: '#b5cea8' }}>{index + 1}</td>
|
|
113
|
+
<td style={{
|
|
114
|
+
padding: '8px',
|
|
115
|
+
color: isMoveEvent ? '#ce9178' : '#dcdcaa',
|
|
116
|
+
fontWeight: isMoveEvent ? 'bold' : 'normal'
|
|
117
|
+
}}>
|
|
118
|
+
{event.name}
|
|
119
|
+
</td>
|
|
120
|
+
<td style={{ padding: '8px', color: '#d4d4d4' }}>{event.label}</td>
|
|
121
|
+
<td style={{
|
|
122
|
+
padding: '8px',
|
|
123
|
+
color: isMoveEvent ? '#f48771' : '#569cd6'
|
|
124
|
+
}}>
|
|
125
|
+
{isMoveEvent ? '→ Move Event (IPC)' : `Transform (${participant})`}
|
|
126
|
+
</td>
|
|
127
|
+
</tr>
|
|
128
|
+
);
|
|
129
|
+
})}
|
|
130
|
+
</tbody>
|
|
131
|
+
</table>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// ============================================================================
|
|
139
|
+
// Workflow Stories
|
|
140
|
+
// ============================================================================
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* **Happy Path - Cache Hit**
|
|
144
|
+
*
|
|
145
|
+
* Optimal execution path where a cached image already exists.
|
|
146
|
+
*
|
|
147
|
+
* **Flow:**
|
|
148
|
+
* 1. Renderer requests image via IPC
|
|
149
|
+
* 2. Main process checks cache → Hit!
|
|
150
|
+
* 3. Returns cached URL immediately
|
|
151
|
+
* 4. Renderer displays image
|
|
152
|
+
*
|
|
153
|
+
* **Move Events (cross-boundary):**
|
|
154
|
+
* - `main.image_requested` (Renderer → Main)
|
|
155
|
+
* - `renderer.cache_hit` (Main → Renderer)
|
|
156
|
+
*/
|
|
157
|
+
export const CacheHitFlow: Story = {
|
|
158
|
+
render: () => (
|
|
159
|
+
<SequenceWithEventsList
|
|
160
|
+
events={workflowData.workflows[0].events}
|
|
161
|
+
edges={workflowData.workflows[0].edges}
|
|
162
|
+
height={600}
|
|
163
|
+
layoutOptions={{
|
|
164
|
+
namespaceStrategy: 'first',
|
|
165
|
+
laneWidth: 200,
|
|
166
|
+
eventSpacing: 80,
|
|
167
|
+
}}
|
|
168
|
+
/>
|
|
169
|
+
),
|
|
170
|
+
parameters: {
|
|
171
|
+
docs: {
|
|
172
|
+
description: {
|
|
173
|
+
story: 'Fast path when cached image exists - only 2 IPC calls needed.',
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* **Full Generation Flow**
|
|
181
|
+
*
|
|
182
|
+
* Complete execution path when no cached image exists.
|
|
183
|
+
*
|
|
184
|
+
* **Flow:**
|
|
185
|
+
* 1. Renderer requests image via IPC
|
|
186
|
+
* 2. Main process checks cache → Miss
|
|
187
|
+
* 3. Fetches file tree from repository cache
|
|
188
|
+
* 4. Generates new File City visualization
|
|
189
|
+
* 5. Saves to cache
|
|
190
|
+
* 6. Returns generated URL via IPC
|
|
191
|
+
* 7. Renderer displays image
|
|
192
|
+
*
|
|
193
|
+
* **Move Events (cross-boundary):**
|
|
194
|
+
* - `main.image_requested` (Renderer → Main)
|
|
195
|
+
* - `renderer.generation_complete` (Main → Renderer)
|
|
196
|
+
*
|
|
197
|
+
* **Transform Events (internal to Main):**
|
|
198
|
+
* - `cache_checked`, `filetree_fetched`, `generation_started`
|
|
199
|
+
*/
|
|
200
|
+
export const GenerateNewFlow: Story = {
|
|
201
|
+
render: () => (
|
|
202
|
+
<SequenceWithEventsList
|
|
203
|
+
events={workflowData.workflows[1].events}
|
|
204
|
+
edges={workflowData.workflows[1].edges}
|
|
205
|
+
height={700}
|
|
206
|
+
layoutOptions={{
|
|
207
|
+
namespaceStrategy: 'first',
|
|
208
|
+
laneWidth: 200,
|
|
209
|
+
eventSpacing: 80,
|
|
210
|
+
}}
|
|
211
|
+
/>
|
|
212
|
+
),
|
|
213
|
+
parameters: {
|
|
214
|
+
docs: {
|
|
215
|
+
description: {
|
|
216
|
+
story: 'Full generation path with cache miss - includes image generation.',
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* **Error Path - No FileTree**
|
|
224
|
+
*
|
|
225
|
+
* Error handling when file tree is not available.
|
|
226
|
+
*
|
|
227
|
+
* **Flow:**
|
|
228
|
+
* 1. Renderer requests image via IPC
|
|
229
|
+
* 2. Main process checks cache → Miss
|
|
230
|
+
* 3. Attempts to fetch file tree → Not available
|
|
231
|
+
* 4. Skips generation, returns null
|
|
232
|
+
* 5. Renderer displays fallback UI
|
|
233
|
+
*
|
|
234
|
+
* **Move Events:**
|
|
235
|
+
* - `main.image_requested` (Renderer → Main)
|
|
236
|
+
* - `renderer.generation_skipped` (Main → Renderer)
|
|
237
|
+
*/
|
|
238
|
+
export const NoFileTreeFlow: Story = {
|
|
239
|
+
render: () => (
|
|
240
|
+
<SequenceWithEventsList
|
|
241
|
+
events={workflowData.workflows[2].events}
|
|
242
|
+
edges={workflowData.workflows[2].edges}
|
|
243
|
+
height={600}
|
|
244
|
+
layoutOptions={{
|
|
245
|
+
namespaceStrategy: 'first',
|
|
246
|
+
laneWidth: 200,
|
|
247
|
+
eventSpacing: 80,
|
|
248
|
+
}}
|
|
249
|
+
/>
|
|
250
|
+
),
|
|
251
|
+
parameters: {
|
|
252
|
+
docs: {
|
|
253
|
+
description: {
|
|
254
|
+
story: 'Error handling when repository file tree is unavailable.',
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* **Error During Generation**
|
|
262
|
+
*
|
|
263
|
+
* Error handling when image generation fails.
|
|
264
|
+
*
|
|
265
|
+
* **Flow:**
|
|
266
|
+
* 1. Renderer requests image via IPC
|
|
267
|
+
* 2. Main process checks cache → Miss
|
|
268
|
+
* 3. Fetches file tree successfully
|
|
269
|
+
* 4. Starts generation → Error occurs
|
|
270
|
+
* 5. Returns error via IPC
|
|
271
|
+
* 6. Renderer displays fallback UI
|
|
272
|
+
*
|
|
273
|
+
* **Move Events:**
|
|
274
|
+
* - `main.image_requested` (Renderer → Main)
|
|
275
|
+
* - `renderer.generation_error` (Main → Renderer)
|
|
276
|
+
*/
|
|
277
|
+
export const ErrorFlow: Story = {
|
|
278
|
+
render: () => (
|
|
279
|
+
<SequenceWithEventsList
|
|
280
|
+
events={workflowData.workflows[3].events}
|
|
281
|
+
edges={workflowData.workflows[3].edges}
|
|
282
|
+
height={650}
|
|
283
|
+
layoutOptions={{
|
|
284
|
+
namespaceStrategy: 'first',
|
|
285
|
+
laneWidth: 200,
|
|
286
|
+
eventSpacing: 80,
|
|
287
|
+
}}
|
|
288
|
+
/>
|
|
289
|
+
),
|
|
290
|
+
parameters: {
|
|
291
|
+
docs: {
|
|
292
|
+
description: {
|
|
293
|
+
story: 'Error handling when image generation fails unexpectedly.',
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* **Comparison View**
|
|
301
|
+
*
|
|
302
|
+
* Side-by-side comparison of cache hit vs full generation.
|
|
303
|
+
* Shows how the same canvas structure supports multiple execution paths.
|
|
304
|
+
*/
|
|
305
|
+
export const ComparisonView: Story = {
|
|
306
|
+
render: () => (
|
|
307
|
+
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', height: '100vh' }}>
|
|
308
|
+
<div style={{ display: 'flex', flexDirection: 'column', borderRight: '1px solid #3e3e3e' }}>
|
|
309
|
+
<h3 style={{ margin: '12px', color: '#d4d4d4' }}>Cache Hit (Fast)</h3>
|
|
310
|
+
<div style={{ flex: 1 }}>
|
|
311
|
+
<SequenceDiagramRenderer
|
|
312
|
+
events={workflowData.workflows[0].events}
|
|
313
|
+
edges={workflowData.workflows[0].edges}
|
|
314
|
+
height={600}
|
|
315
|
+
layoutOptions={{
|
|
316
|
+
namespaceStrategy: 'first',
|
|
317
|
+
laneWidth: 180,
|
|
318
|
+
eventSpacing: 70,
|
|
319
|
+
}}
|
|
320
|
+
/>
|
|
321
|
+
</div>
|
|
322
|
+
</div>
|
|
323
|
+
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
|
324
|
+
<h3 style={{ margin: '12px', color: '#d4d4d4' }}>Generate New (Full)</h3>
|
|
325
|
+
<div style={{ flex: 1 }}>
|
|
326
|
+
<SequenceDiagramRenderer
|
|
327
|
+
events={workflowData.workflows[1].events}
|
|
328
|
+
edges={workflowData.workflows[1].edges}
|
|
329
|
+
height={600}
|
|
330
|
+
layoutOptions={{
|
|
331
|
+
namespaceStrategy: 'first',
|
|
332
|
+
laneWidth: 180,
|
|
333
|
+
eventSpacing: 70,
|
|
334
|
+
}}
|
|
335
|
+
/>
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
</div>
|
|
339
|
+
),
|
|
340
|
+
parameters: {
|
|
341
|
+
docs: {
|
|
342
|
+
description: {
|
|
343
|
+
story: 'Visual comparison showing branching execution paths from the same canvas.',
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* **All Paths Overview**
|
|
351
|
+
*
|
|
352
|
+
* Grid view of all possible execution paths through the system.
|
|
353
|
+
* Demonstrates the canvas as an "aggregation of sequences" - showing
|
|
354
|
+
* the full possibility space that the canvas represents.
|
|
355
|
+
*/
|
|
356
|
+
export const AllPathsOverview: Story = {
|
|
357
|
+
render: () => (
|
|
358
|
+
<div style={{
|
|
359
|
+
display: 'grid',
|
|
360
|
+
gridTemplateColumns: '1fr 1fr',
|
|
361
|
+
height: '100vh',
|
|
362
|
+
overflow: 'auto',
|
|
363
|
+
background: '#1e1e1e'
|
|
364
|
+
}}>
|
|
365
|
+
{workflowData.workflows.map((workflow, index) => (
|
|
366
|
+
<div
|
|
367
|
+
key={workflow.id}
|
|
368
|
+
style={{
|
|
369
|
+
display: 'flex',
|
|
370
|
+
flexDirection: 'column',
|
|
371
|
+
borderRight: index % 2 === 0 ? '1px solid #3e3e3e' : 'none',
|
|
372
|
+
borderBottom: index < 2 ? '1px solid #3e3e3e' : 'none'
|
|
373
|
+
}}
|
|
374
|
+
>
|
|
375
|
+
<div style={{ padding: '12px', borderBottom: '1px solid #3e3e3e' }}>
|
|
376
|
+
<h3 style={{ margin: '0 0 4px 0', color: '#d4d4d4' }}>{workflow.name}</h3>
|
|
377
|
+
<p style={{ fontSize: 13, color: '#858585', margin: 0 }}>
|
|
378
|
+
{workflow.description}
|
|
379
|
+
</p>
|
|
380
|
+
</div>
|
|
381
|
+
<div style={{ flex: 1 }}>
|
|
382
|
+
<SequenceDiagramRenderer
|
|
383
|
+
events={workflow.events}
|
|
384
|
+
edges={workflow.edges}
|
|
385
|
+
height={400}
|
|
386
|
+
layoutOptions={{
|
|
387
|
+
namespaceStrategy: 'first',
|
|
388
|
+
laneWidth: 160,
|
|
389
|
+
eventSpacing: 60,
|
|
390
|
+
laneGap: 40,
|
|
391
|
+
}}
|
|
392
|
+
/>
|
|
393
|
+
</div>
|
|
394
|
+
</div>
|
|
395
|
+
))}
|
|
396
|
+
</div>
|
|
397
|
+
),
|
|
398
|
+
parameters: {
|
|
399
|
+
docs: {
|
|
400
|
+
description: {
|
|
401
|
+
story: 'Complete overview of all execution paths - the "library of possibilities".',
|
|
402
|
+
},
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* **Using WorkflowSequenceDiagram Wrapper**
|
|
409
|
+
*
|
|
410
|
+
* Demonstrates the simplified API using the WorkflowSequenceDiagram component.
|
|
411
|
+
* This component automatically handles the conversion from workflow to sequence format.
|
|
412
|
+
*/
|
|
413
|
+
export const UsingWorkflowWrapper: Story = {
|
|
414
|
+
render: () => {
|
|
415
|
+
// Convert our workflow data to the expected WorkflowScenario format
|
|
416
|
+
const scenario: WorkflowScenario = {
|
|
417
|
+
id: 'cache-hit-flow',
|
|
418
|
+
name: 'Cache Hit Flow',
|
|
419
|
+
description: 'Optimal path where cached image exists',
|
|
420
|
+
events: workflowData.workflows[1].events.map(e => ({
|
|
421
|
+
name: e.name,
|
|
422
|
+
label: e.label,
|
|
423
|
+
moveEvent: e.moveEvent,
|
|
424
|
+
participant: e.participant,
|
|
425
|
+
})),
|
|
426
|
+
edges: workflowData.workflows[1].edges,
|
|
427
|
+
template: {
|
|
428
|
+
events: {},
|
|
429
|
+
},
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
return (
|
|
433
|
+
<div style={{ height: '100vh', display: 'flex', flexDirection: 'column' }}>
|
|
434
|
+
<div style={{
|
|
435
|
+
padding: '12px',
|
|
436
|
+
background: '#1e1e1e',
|
|
437
|
+
borderBottom: '1px solid #3e3e3e',
|
|
438
|
+
color: '#d4d4d4'
|
|
439
|
+
}}>
|
|
440
|
+
<h3 style={{ margin: '0 0 4px 0' }}>WorkflowSequenceDiagram Component</h3>
|
|
441
|
+
<p style={{ fontSize: 13, color: '#858585', margin: 0 }}>
|
|
442
|
+
Cleaner API - just pass the workflow scenario and optional canvas
|
|
443
|
+
</p>
|
|
444
|
+
</div>
|
|
445
|
+
<div style={{ flex: 1 }}>
|
|
446
|
+
<WorkflowSequenceDiagram
|
|
447
|
+
scenario={scenario}
|
|
448
|
+
height="100%"
|
|
449
|
+
layoutOptions={{
|
|
450
|
+
namespaceStrategy: 'first',
|
|
451
|
+
laneWidth: 200,
|
|
452
|
+
eventSpacing: 80,
|
|
453
|
+
}}
|
|
454
|
+
/>
|
|
455
|
+
</div>
|
|
456
|
+
</div>
|
|
457
|
+
);
|
|
458
|
+
},
|
|
459
|
+
parameters: {
|
|
460
|
+
docs: {
|
|
461
|
+
description: {
|
|
462
|
+
story: 'Simplified API using the WorkflowSequenceDiagram wrapper component.',
|
|
463
|
+
},
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* **Interactive Namespace Strategy**
|
|
470
|
+
*
|
|
471
|
+
* Demonstrates different namespace grouping strategies.
|
|
472
|
+
* Toggle between showing full event namespaces vs collapsed participants.
|
|
473
|
+
*/
|
|
474
|
+
export const InteractiveNamespaceStrategy: Story = {
|
|
475
|
+
render: () => {
|
|
476
|
+
const [strategy, setStrategy] = React.useState<'first' | 'all-but-last'>('first');
|
|
477
|
+
|
|
478
|
+
return (
|
|
479
|
+
<div style={{ height: '100vh', display: 'flex', flexDirection: 'column' }}>
|
|
480
|
+
<div style={{
|
|
481
|
+
padding: '12px',
|
|
482
|
+
background: '#1e1e1e',
|
|
483
|
+
borderBottom: '1px solid #3e3e3e'
|
|
484
|
+
}}>
|
|
485
|
+
<label style={{ marginRight: 16, color: '#d4d4d4' }}>
|
|
486
|
+
<strong>Namespace Strategy:</strong>
|
|
487
|
+
</label>
|
|
488
|
+
<button
|
|
489
|
+
onClick={() => setStrategy('first')}
|
|
490
|
+
style={{
|
|
491
|
+
padding: '8px 16px',
|
|
492
|
+
marginRight: 8,
|
|
493
|
+
background: strategy === 'first' ? '#3B82F6' : '#2a2a2a',
|
|
494
|
+
color: '#d4d4d4',
|
|
495
|
+
border: '1px solid #3e3e3e',
|
|
496
|
+
borderRadius: 4,
|
|
497
|
+
cursor: 'pointer',
|
|
498
|
+
}}
|
|
499
|
+
>
|
|
500
|
+
First Segment (Participants)
|
|
501
|
+
</button>
|
|
502
|
+
<button
|
|
503
|
+
onClick={() => setStrategy('all-but-last')}
|
|
504
|
+
style={{
|
|
505
|
+
padding: '8px 16px',
|
|
506
|
+
background: strategy === 'all-but-last' ? '#3B82F6' : '#2a2a2a',
|
|
507
|
+
color: '#d4d4d4',
|
|
508
|
+
border: '1px solid #3e3e3e',
|
|
509
|
+
borderRadius: 4,
|
|
510
|
+
cursor: 'pointer',
|
|
511
|
+
}}
|
|
512
|
+
>
|
|
513
|
+
Detailed Namespaces
|
|
514
|
+
</button>
|
|
515
|
+
<p style={{ fontSize: 13, color: '#858585', marginTop: 8, marginBottom: 0 }}>
|
|
516
|
+
{strategy === 'first'
|
|
517
|
+
? 'Showing high-level participants (renderer.*, main.*) - participant-focused view'
|
|
518
|
+
: 'Showing detailed event namespaces - event-focused view'
|
|
519
|
+
}
|
|
520
|
+
</p>
|
|
521
|
+
</div>
|
|
522
|
+
<div style={{ flex: 1 }}>
|
|
523
|
+
<SequenceDiagramRenderer
|
|
524
|
+
events={workflowData.workflows[1].events}
|
|
525
|
+
edges={workflowData.workflows[1].edges}
|
|
526
|
+
height={700}
|
|
527
|
+
layoutOptions={{
|
|
528
|
+
namespaceStrategy: strategy,
|
|
529
|
+
laneWidth: 200,
|
|
530
|
+
eventSpacing: 80,
|
|
531
|
+
}}
|
|
532
|
+
/>
|
|
533
|
+
</div>
|
|
534
|
+
</div>
|
|
535
|
+
);
|
|
536
|
+
},
|
|
537
|
+
parameters: {
|
|
538
|
+
docs: {
|
|
539
|
+
description: {
|
|
540
|
+
story: 'Toggle between participant-level and detailed namespace views.',
|
|
541
|
+
},
|
|
542
|
+
},
|
|
543
|
+
},
|
|
544
|
+
};
|