@principal-ai/principal-view-react 0.7.39 → 0.7.41

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 (54) hide show
  1. package/dist/components/EdgeInfoPanel.d.ts.map +1 -1
  2. package/dist/components/EdgeInfoPanel.js +18 -12
  3. package/dist/components/EdgeInfoPanel.js.map +1 -1
  4. package/dist/components/NodeInfoPanel.d.ts.map +1 -1
  5. package/dist/components/NodeInfoPanel.js +46 -30
  6. package/dist/components/NodeInfoPanel.js.map +1 -1
  7. package/dist/components/NodeTooltip.d.ts.map +1 -1
  8. package/dist/components/NodeTooltip.js +11 -6
  9. package/dist/components/NodeTooltip.js.map +1 -1
  10. package/dist/components/SelectionSidebar.d.ts.map +1 -1
  11. package/dist/components/SelectionSidebar.js +24 -13
  12. package/dist/components/SelectionSidebar.js.map +1 -1
  13. package/dist/edges/CustomEdge.d.ts.map +1 -1
  14. package/dist/edges/CustomEdge.js +8 -4
  15. package/dist/edges/CustomEdge.js.map +1 -1
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +1 -0
  19. package/dist/index.js.map +1 -1
  20. package/dist/nodes/CustomNode.d.ts.map +1 -1
  21. package/dist/nodes/CustomNode.js +32 -19
  22. package/dist/nodes/CustomNode.js.map +1 -1
  23. package/dist/utils/orientationUtils.d.ts +19 -0
  24. package/dist/utils/orientationUtils.d.ts.map +1 -0
  25. package/dist/utils/orientationUtils.js +62 -0
  26. package/dist/utils/orientationUtils.js.map +1 -0
  27. package/package.json +1 -2
  28. package/src/components/EdgeInfoPanel.tsx +20 -14
  29. package/src/components/NodeInfoPanel.tsx +48 -32
  30. package/src/components/NodeTooltip.tsx +11 -6
  31. package/src/components/SelectionSidebar.tsx +24 -13
  32. package/src/edges/CustomEdge.tsx +8 -4
  33. package/src/index.ts +5 -0
  34. package/src/nodes/CustomNode.tsx +32 -19
  35. package/src/stories/GraphOrientation.stories.tsx +500 -0
  36. package/src/utils/orientationUtils.ts +73 -0
  37. package/dist/components/NarrativeRenderer.d.ts +0 -19
  38. package/dist/components/NarrativeRenderer.d.ts.map +0 -1
  39. package/dist/components/NarrativeRenderer.js +0 -103
  40. package/dist/components/NarrativeRenderer.js.map +0 -1
  41. package/dist/components/TestEventPanel.d.ts +0 -44
  42. package/dist/components/TestEventPanel.d.ts.map +0 -1
  43. package/dist/components/TestEventPanel.js +0 -277
  44. package/dist/components/TestEventPanel.js.map +0 -1
  45. package/dist/utils/narrative-converter.d.ts +0 -45
  46. package/dist/utils/narrative-converter.d.ts.map +0 -1
  47. package/dist/utils/narrative-converter.js +0 -121
  48. package/dist/utils/narrative-converter.js.map +0 -1
  49. package/dist/utils/narrative-loader.d.ts +0 -53
  50. package/dist/utils/narrative-loader.d.ts.map +0 -1
  51. package/dist/utils/narrative-loader.js +0 -163
  52. package/dist/utils/narrative-loader.js.map +0 -1
  53. package/src/stories/RealTestExecution.stories.tsx +0 -404
  54. package/src/stories/ValidatedExecution.stories.tsx +0 -158
@@ -0,0 +1,500 @@
1
+ import React, { useState, useMemo } from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import { GraphRenderer } from '../components/GraphRenderer';
4
+ import type { ExtendedCanvas } from '@principal-ai/principal-view-core/browser';
5
+ import { ThemeProvider, defaultEditorTheme } from '@principal-ade/industry-theme';
6
+ import { swapGraphOrientation } from '../utils/orientationUtils';
7
+ import { CanvasConverter } from '@principal-ai/principal-view-core/browser';
8
+
9
+ const meta = {
10
+ title: 'Features/Graph Orientation',
11
+ component: GraphRenderer,
12
+ parameters: {
13
+ layout: 'centered',
14
+ },
15
+ tags: ['autodocs'],
16
+ decorators: [
17
+ (Story) => (
18
+ <ThemeProvider theme={defaultEditorTheme}>
19
+ <Story />
20
+ </ThemeProvider>
21
+ ),
22
+ ],
23
+ } satisfies Meta<typeof GraphRenderer>;
24
+
25
+ export default meta;
26
+ type Story = StoryObj<typeof meta>;
27
+
28
+ /**
29
+ * Canvas with horizontal flow (left to right)
30
+ */
31
+ const horizontalCanvas: ExtendedCanvas = {
32
+ nodes: [
33
+ {
34
+ id: 'start',
35
+ type: 'text',
36
+ x: 100,
37
+ y: 200,
38
+ width: 120,
39
+ height: 80,
40
+ text: 'Start',
41
+ color: 4,
42
+ pv: {
43
+ nodeType: 'process',
44
+ shape: 'circle',
45
+ icon: 'Play',
46
+ },
47
+ },
48
+ {
49
+ id: 'process-1',
50
+ type: 'text',
51
+ x: 300,
52
+ y: 200,
53
+ width: 140,
54
+ height: 80,
55
+ text: 'Process 1',
56
+ color: 6,
57
+ pv: {
58
+ nodeType: 'process',
59
+ shape: 'rectangle',
60
+ icon: 'Box',
61
+ },
62
+ },
63
+ {
64
+ id: 'decision',
65
+ type: 'text',
66
+ x: 520,
67
+ y: 200,
68
+ width: 100,
69
+ height: 100,
70
+ text: 'Decision',
71
+ color: 2,
72
+ pv: {
73
+ nodeType: 'decision',
74
+ shape: 'diamond',
75
+ icon: 'GitBranch',
76
+ },
77
+ },
78
+ {
79
+ id: 'process-2a',
80
+ type: 'text',
81
+ x: 700,
82
+ y: 120,
83
+ width: 140,
84
+ height: 80,
85
+ text: 'Process 2A',
86
+ color: 6,
87
+ pv: {
88
+ nodeType: 'process',
89
+ shape: 'rectangle',
90
+ icon: 'Box',
91
+ },
92
+ },
93
+ {
94
+ id: 'process-2b',
95
+ type: 'text',
96
+ x: 700,
97
+ y: 280,
98
+ width: 140,
99
+ height: 80,
100
+ text: 'Process 2B',
101
+ color: 6,
102
+ pv: {
103
+ nodeType: 'process',
104
+ shape: 'rectangle',
105
+ icon: 'Box',
106
+ },
107
+ },
108
+ {
109
+ id: 'end',
110
+ type: 'text',
111
+ x: 920,
112
+ y: 200,
113
+ width: 120,
114
+ height: 80,
115
+ text: 'End',
116
+ color: 5,
117
+ pv: {
118
+ nodeType: 'process',
119
+ shape: 'circle',
120
+ icon: 'CheckCircle',
121
+ },
122
+ },
123
+ ],
124
+ edges: [
125
+ {
126
+ id: 'e1',
127
+ fromNode: 'start',
128
+ toNode: 'process-1',
129
+ fromSide: 'right',
130
+ toSide: 'left',
131
+ pv: {
132
+ edgeType: 'flow',
133
+ },
134
+ },
135
+ {
136
+ id: 'e2',
137
+ fromNode: 'process-1',
138
+ toNode: 'decision',
139
+ fromSide: 'right',
140
+ toSide: 'left',
141
+ pv: {
142
+ edgeType: 'flow',
143
+ },
144
+ },
145
+ {
146
+ id: 'e3',
147
+ fromNode: 'decision',
148
+ toNode: 'process-2a',
149
+ fromSide: 'top',
150
+ toSide: 'left',
151
+ pv: {
152
+ edgeType: 'flow',
153
+ },
154
+ },
155
+ {
156
+ id: 'e4',
157
+ fromNode: 'decision',
158
+ toNode: 'process-2b',
159
+ fromSide: 'bottom',
160
+ toSide: 'left',
161
+ pv: {
162
+ edgeType: 'flow',
163
+ },
164
+ },
165
+ {
166
+ id: 'e5',
167
+ fromNode: 'process-2a',
168
+ toNode: 'end',
169
+ fromSide: 'right',
170
+ toSide: 'top',
171
+ pv: {
172
+ edgeType: 'flow',
173
+ },
174
+ },
175
+ {
176
+ id: 'e6',
177
+ fromNode: 'process-2b',
178
+ toNode: 'end',
179
+ fromSide: 'right',
180
+ toSide: 'bottom',
181
+ pv: {
182
+ edgeType: 'flow',
183
+ },
184
+ },
185
+ ],
186
+ pv: {
187
+ version: '1.0.0',
188
+ name: 'Horizontal Flow',
189
+ description: 'Graph with horizontal (left-to-right) orientation',
190
+ edgeTypes: {
191
+ flow: {
192
+ description: 'Flow connection',
193
+ style: 'solid',
194
+ color: '#888',
195
+ },
196
+ },
197
+ },
198
+ };
199
+
200
+ /**
201
+ * Interactive story demonstrating orientation swap
202
+ */
203
+ const OrientationSwapTemplate = () => {
204
+ const [isVertical, setIsVertical] = useState(false);
205
+
206
+ const canvas = useMemo(() => {
207
+ if (!isVertical) {
208
+ return horizontalCanvas;
209
+ }
210
+
211
+ // Convert canvas to internal format
212
+ const { nodes: nodeStates, edges: edgeStates } =
213
+ CanvasConverter.canvasToGraph(horizontalCanvas);
214
+
215
+ // Swap orientation
216
+ const { nodes: swappedNodes, edges: swappedEdges } = swapGraphOrientation(
217
+ nodeStates,
218
+ edgeStates
219
+ );
220
+
221
+ // Convert back to canvas format for rendering
222
+ return {
223
+ ...horizontalCanvas,
224
+ nodes: swappedNodes.map((node, idx) => ({
225
+ ...horizontalCanvas.nodes[idx],
226
+ x: node.position?.x ?? 0,
227
+ y: node.position?.y ?? 0,
228
+ })),
229
+ edges: swappedEdges.map((edge, idx) => {
230
+ const data = edge.data as Record<string, unknown> | undefined;
231
+ return {
232
+ ...horizontalCanvas.edges[idx],
233
+ fromSide: (data?.fromSide as 'top' | 'right' | 'bottom' | 'left') ?? 'bottom',
234
+ toSide: (data?.toSide as 'top' | 'right' | 'bottom' | 'left') ?? 'top',
235
+ };
236
+ }),
237
+ };
238
+ }, [isVertical]);
239
+
240
+ return (
241
+ <div style={{ padding: 20 }}>
242
+ <div style={{ marginBottom: 20, display: 'flex', alignItems: 'center', gap: 20 }}>
243
+ <h2 style={{ margin: 0, fontFamily: 'system-ui' }}>Graph Orientation</h2>
244
+ <button
245
+ onClick={() => setIsVertical(!isVertical)}
246
+ style={{
247
+ padding: '8px 16px',
248
+ fontSize: 14,
249
+ fontFamily: 'system-ui',
250
+ backgroundColor: '#3b82f6',
251
+ color: 'white',
252
+ border: 'none',
253
+ borderRadius: 6,
254
+ cursor: 'pointer',
255
+ }}
256
+ >
257
+ Switch to {isVertical ? 'Horizontal' : 'Vertical'}
258
+ </button>
259
+ <div style={{ fontSize: 14, color: '#666', fontFamily: 'system-ui' }}>
260
+ Current: <strong>{isVertical ? 'Vertical (Top-to-Bottom)' : 'Horizontal (Left-to-Right)'}</strong>
261
+ </div>
262
+ </div>
263
+
264
+ <GraphRenderer
265
+ key={isVertical ? 'vertical' : 'horizontal'}
266
+ canvas={canvas}
267
+ width={isVertical ? 500 : 1100}
268
+ height={isVertical ? 1100 : 500}
269
+ />
270
+
271
+ <div style={{ marginTop: 30, padding: 16, backgroundColor: '#f5f5f5', borderRadius: 8 }}>
272
+ <h4 style={{ marginBottom: 10, fontFamily: 'system-ui' }}>How It Works</h4>
273
+ <ul style={{ fontSize: 13, lineHeight: 1.8, margin: 0, paddingLeft: 20 }}>
274
+ <li>
275
+ <strong>Position Swap</strong>: Swaps x and y coordinates of all nodes
276
+ </li>
277
+ <li>
278
+ <strong>Edge Side Rotation</strong>: Both sides rotate <strong>clockwise</strong>
279
+ <ul style={{ marginTop: 5 }}>
280
+ <li><code>top → right → bottom → left → top</code></li>
281
+ <li>Example: <code>right → left</code> becomes <code>bottom → top</code></li>
282
+ </ul>
283
+ </li>
284
+ <li>
285
+ <strong>Usage</strong>: <code>swapGraphOrientation(nodes, edges)</code>
286
+ </li>
287
+ </ul>
288
+ </div>
289
+ </div>
290
+ );
291
+ };
292
+
293
+ export const InteractiveOrientationSwap: Story = {
294
+ render: () => <OrientationSwapTemplate />,
295
+ parameters: {
296
+ docs: {
297
+ description: {
298
+ story: `
299
+ **Interactive Orientation Swap**
300
+
301
+ Click the button to toggle between horizontal (left-to-right) and vertical (top-to-bottom) orientations.
302
+
303
+ The \`swapGraphOrientation\` utility:
304
+ 1. Swaps x/y coordinates for all node positions
305
+ 2. Rotates edge connection sides clockwise (both fromSide and toSide)
306
+ - Rotation: top → right → bottom → left → top
307
+ - Example: \`right → left\` becomes \`bottom → top\`
308
+ 3. Preserves the graph structure while rotating it 90 degrees
309
+ `,
310
+ },
311
+ },
312
+ },
313
+ };
314
+
315
+ /**
316
+ * Horizontal flow example
317
+ */
318
+ export const HorizontalFlow: Story = {
319
+ args: {
320
+ canvas: horizontalCanvas,
321
+ width: 1100,
322
+ height: 450,
323
+ },
324
+ parameters: {
325
+ docs: {
326
+ description: {
327
+ story: `
328
+ **Horizontal Flow (Left-to-Right)**
329
+
330
+ Traditional left-to-right flowchart layout:
331
+ - Primary flow uses \`right → left\` connections
332
+ - Branches use \`top\` and \`bottom\` connections for splitting
333
+ - Typical for flowcharts and process diagrams
334
+ `,
335
+ },
336
+ },
337
+ },
338
+ };
339
+
340
+ /**
341
+ * Vertical flow example (using swapped canvas)
342
+ */
343
+ const VerticalFlowTemplate = () => {
344
+ const verticalCanvas = useMemo(() => {
345
+ const { nodes: nodeStates, edges: edgeStates } =
346
+ CanvasConverter.canvasToGraph(horizontalCanvas);
347
+
348
+ const { nodes: swappedNodes, edges: swappedEdges } = swapGraphOrientation(
349
+ nodeStates,
350
+ edgeStates
351
+ );
352
+
353
+ return {
354
+ ...horizontalCanvas,
355
+ pv: {
356
+ ...horizontalCanvas.pv,
357
+ name: 'Vertical Flow',
358
+ description: 'Graph with vertical (top-to-bottom) orientation',
359
+ },
360
+ nodes: swappedNodes.map((node, idx) => ({
361
+ ...horizontalCanvas.nodes[idx],
362
+ x: node.position?.x ?? 0,
363
+ y: node.position?.y ?? 0,
364
+ })),
365
+ edges: swappedEdges.map((edge, idx) => {
366
+ const data = edge.data as Record<string, unknown> | undefined;
367
+ return {
368
+ ...horizontalCanvas.edges[idx],
369
+ fromSide: (data?.fromSide as 'top' | 'right' | 'bottom' | 'left') ?? 'bottom',
370
+ toSide: (data?.toSide as 'top' | 'right' | 'bottom' | 'left') ?? 'top',
371
+ };
372
+ }),
373
+ };
374
+ }, []);
375
+
376
+ return <GraphRenderer canvas={verticalCanvas} width={500} height={1100} />;
377
+ };
378
+
379
+ export const VerticalFlow: Story = {
380
+ render: () => <VerticalFlowTemplate />,
381
+ parameters: {
382
+ docs: {
383
+ description: {
384
+ story: `
385
+ **Vertical Flow (Top-to-Bottom)**
386
+
387
+ Top-to-bottom layout using \`swapGraphOrientation\`:
388
+ - Primary flow uses \`bottom → top\` connections
389
+ - Branches use \`left\` and \`right\` connections for splitting
390
+ - Common for hierarchical diagrams and org charts
391
+ `,
392
+ },
393
+ },
394
+ },
395
+ };
396
+
397
+ /**
398
+ * Side-by-side comparison
399
+ */
400
+ const SideBySideTemplate = () => {
401
+ const verticalCanvas = useMemo(() => {
402
+ const { nodes: nodeStates, edges: edgeStates } =
403
+ CanvasConverter.canvasToGraph(horizontalCanvas);
404
+
405
+ const { nodes: swappedNodes, edges: swappedEdges } = swapGraphOrientation(
406
+ nodeStates,
407
+ edgeStates
408
+ );
409
+
410
+ return {
411
+ ...horizontalCanvas,
412
+ nodes: swappedNodes.map((node, idx) => ({
413
+ ...horizontalCanvas.nodes[idx],
414
+ x: node.position?.x ?? 0,
415
+ y: node.position?.y ?? 0,
416
+ })),
417
+ edges: swappedEdges.map((edge, idx) => {
418
+ const data = edge.data as Record<string, unknown> | undefined;
419
+ return {
420
+ ...horizontalCanvas.edges[idx],
421
+ fromSide: (data?.fromSide as 'top' | 'right' | 'bottom' | 'left') ?? 'bottom',
422
+ toSide: (data?.toSide as 'top' | 'right' | 'bottom' | 'left') ?? 'top',
423
+ };
424
+ }),
425
+ };
426
+ }, []);
427
+
428
+ return (
429
+ <div style={{ padding: 20 }}>
430
+ <h2 style={{ marginBottom: 20, fontFamily: 'system-ui' }}>Orientation Comparison</h2>
431
+
432
+ <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 40 }}>
433
+ <div>
434
+ <h3 style={{ fontFamily: 'system-ui', marginBottom: 10 }}>
435
+ Horizontal (Left-to-Right)
436
+ </h3>
437
+ <div style={{ border: '1px solid #ddd', borderRadius: 8, overflow: 'hidden' }}>
438
+ <GraphRenderer canvas={horizontalCanvas} width={550} height={400} />
439
+ </div>
440
+ <div style={{ marginTop: 10, fontSize: 13, color: '#666' }}>
441
+ Original orientation with right→left primary flow
442
+ </div>
443
+ </div>
444
+
445
+ <div>
446
+ <h3 style={{ fontFamily: 'system-ui', marginBottom: 10 }}>
447
+ Vertical (Top-to-Bottom)
448
+ </h3>
449
+ <div style={{ border: '1px solid #ddd', borderRadius: 8, overflow: 'hidden' }}>
450
+ <GraphRenderer canvas={verticalCanvas} width={550} height={400} />
451
+ </div>
452
+ <div style={{ marginTop: 10, fontSize: 13, color: '#666' }}>
453
+ Swapped orientation with bottom→top primary flow
454
+ </div>
455
+ </div>
456
+ </div>
457
+
458
+ <div style={{ marginTop: 30, padding: 16, backgroundColor: '#f5f5f5', borderRadius: 8 }}>
459
+ <h4 style={{ marginBottom: 10, fontFamily: 'system-ui' }}>
460
+ Edge Side Rotation (Both Clockwise)
461
+ </h4>
462
+ <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 20 }}>
463
+ <div>
464
+ <strong>Clockwise Rotation:</strong>
465
+ <ul style={{ fontSize: 13, lineHeight: 1.8, marginTop: 8, paddingLeft: 20 }}>
466
+ <li><code>top</code> → <code>right</code></li>
467
+ <li><code>right</code> → <code>bottom</code></li>
468
+ <li><code>bottom</code> → <code>left</code></li>
469
+ <li><code>left</code> → <code>top</code></li>
470
+ </ul>
471
+ </div>
472
+ <div>
473
+ <strong>Examples:</strong>
474
+ <ul style={{ fontSize: 13, lineHeight: 1.8, marginTop: 8, paddingLeft: 20 }}>
475
+ <li><code>right → left</code> becomes <code>bottom → top</code></li>
476
+ <li><code>top → left</code> becomes <code>right → top</code></li>
477
+ <li><code>bottom → left</code> becomes <code>left → top</code></li>
478
+ </ul>
479
+ </div>
480
+ </div>
481
+ </div>
482
+ </div>
483
+ );
484
+ };
485
+
486
+ export const SideBySideComparison: Story = {
487
+ render: () => <SideBySideTemplate />,
488
+ parameters: {
489
+ docs: {
490
+ description: {
491
+ story: `
492
+ **Side-by-Side Comparison**
493
+
494
+ Visual comparison of the same graph in both orientations, showing how \`swapGraphOrientation\`
495
+ transforms the layout while preserving the graph structure and flow logic.
496
+ `,
497
+ },
498
+ },
499
+ },
500
+ };
@@ -0,0 +1,73 @@
1
+ import type { NodeState, EdgeState, CanvasSide } from '@principal-ai/principal-view-core';
2
+
3
+ /**
4
+ * Swaps the orientation of a graph from horizontal to vertical or vice versa.
5
+ * This swaps x/y coordinates and adjusts edge connection sides accordingly.
6
+ */
7
+ export function swapGraphOrientation(
8
+ nodes: NodeState[],
9
+ edges: EdgeState[]
10
+ ): { nodes: NodeState[]; edges: EdgeState[] } {
11
+ return {
12
+ nodes: swapNodePositions(nodes),
13
+ edges: swapEdgeSides(edges),
14
+ };
15
+ }
16
+
17
+ /**
18
+ * Swaps x and y positions for all nodes
19
+ */
20
+ export function swapNodePositions(nodes: NodeState[]): NodeState[] {
21
+ return nodes.map(node => ({
22
+ ...node,
23
+ position: node.position ? {
24
+ x: node.position.y,
25
+ y: node.position.x,
26
+ } : undefined,
27
+ }));
28
+ }
29
+
30
+ /**
31
+ * Swaps edge connection sides to match the new orientation
32
+ * Both sides rotate clockwise: top → right → bottom → left → top
33
+ */
34
+ export function swapEdgeSides(edges: EdgeState[]): EdgeState[] {
35
+ return edges.map(edge => {
36
+ const data = edge.data as Record<string, unknown> | undefined;
37
+ const fromSide = data?.fromSide as CanvasSide | undefined;
38
+ const toSide = data?.toSide as CanvasSide | undefined;
39
+
40
+ const rotatedFromSide = rotateClockwise(fromSide);
41
+ const rotatedToSide = rotateClockwise(toSide);
42
+
43
+ // Only include fromSide/toSide if they have defined values (not undefined)
44
+ const newData = { ...edge.data } as Record<string, unknown>;
45
+ if (rotatedFromSide !== undefined) {
46
+ newData.fromSide = rotatedFromSide;
47
+ }
48
+ if (rotatedToSide !== undefined) {
49
+ newData.toSide = rotatedToSide;
50
+ }
51
+
52
+ return {
53
+ ...edge,
54
+ data: newData as EdgeState['data'],
55
+ };
56
+ });
57
+ }
58
+
59
+ /**
60
+ * Rotates a side clockwise: top → right → bottom → left → top
61
+ */
62
+ function rotateClockwise(side?: CanvasSide): CanvasSide | undefined {
63
+ if (!side) return undefined;
64
+
65
+ const clockwiseMap: Record<CanvasSide, CanvasSide> = {
66
+ top: 'right',
67
+ right: 'bottom',
68
+ bottom: 'left',
69
+ left: 'top',
70
+ };
71
+
72
+ return clockwiseMap[side];
73
+ }
@@ -1,19 +0,0 @@
1
- import React from 'react';
2
- import type { NarrativeTemplate, OtelEvent } from '@principal-ai/principal-view-core/browser';
3
- export interface NarrativeRendererProps {
4
- /** Narrative template to use for rendering */
5
- template: NarrativeTemplate;
6
- /** OTEL events to render */
7
- events: OtelEvent[];
8
- /** Optional CSS class name */
9
- className?: string;
10
- /** Optional custom style */
11
- style?: React.CSSProperties;
12
- /** Show metadata panel */
13
- showMetadata?: boolean;
14
- }
15
- /**
16
- * Renders OTEL events as a human-readable narrative using a template
17
- */
18
- export declare const NarrativeRenderer: React.FC<NarrativeRendererProps>;
19
- //# sourceMappingURL=NarrativeRenderer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"NarrativeRenderer.d.ts","sourceRoot":"","sources":["../../src/components/NarrativeRenderer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,2CAA2C,CAAC;AAG9F,MAAM,WAAW,sBAAsB;IACrC,8CAA8C;IAC9C,QAAQ,EAAE,iBAAiB,CAAC;IAE5B,4BAA4B;IAC5B,MAAM,EAAE,SAAS,EAAE,CAAC;IAEpB,8BAA8B;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,4BAA4B;IAC5B,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAE5B,0BAA0B;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CAiJ9D,CAAC"}
@@ -1,103 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useMemo } from 'react';
3
- import { renderNarrative } from '@principal-ai/principal-view-core/browser';
4
- import { useTheme } from '@principal-ade/industry-theme';
5
- /**
6
- * Renders OTEL events as a human-readable narrative using a template
7
- */
8
- export const NarrativeRenderer = ({ template, events, className, style, showMetadata = false, }) => {
9
- const { theme } = useTheme();
10
- // Render the narrative
11
- const result = useMemo(() => {
12
- try {
13
- return renderNarrative(template, events);
14
- }
15
- catch (error) {
16
- return {
17
- text: `Error rendering narrative: ${error instanceof Error ? error.message : 'Unknown error'}`,
18
- scenarioId: 'error',
19
- metadata: {
20
- eventCount: events.length,
21
- spanCount: 0,
22
- logCount: 0,
23
- },
24
- };
25
- }
26
- }, [template, events]);
27
- // Parse narrative text to add syntax highlighting
28
- const renderHighlightedText = (text) => {
29
- const lines = text.split('\n');
30
- return lines.map((line, idx) => {
31
- // Determine line style based on content
32
- let lineStyle = {};
33
- let content = line;
34
- // Status indicators (✅ ❌ ⚠️ 📋)
35
- if (/^[✅❌⚠️📋]/.test(line)) {
36
- lineStyle = {
37
- fontWeight: 'bold',
38
- fontSize: '16px',
39
- marginTop: idx > 0 ? '8px' : '0',
40
- marginBottom: '4px',
41
- };
42
- }
43
- // Separators (━━━━)
44
- else if (/^━+/.test(line)) {
45
- lineStyle = {
46
- color: theme.colors.border,
47
- opacity: 0.6,
48
- };
49
- }
50
- // Arrow items (→)
51
- else if (/^(\s*)→/.test(line)) {
52
- const indent = line.match(/^(\s*)/)?.[1] || '';
53
- lineStyle = {
54
- color: theme.colors.text,
55
- fontWeight: indent.length === 0 ? 'bold' : 'normal',
56
- marginTop: indent.length === 0 ? '12px' : '4px',
57
- };
58
- }
59
- // Bullet items (•)
60
- else if (/^\s+•/.test(line)) {
61
- lineStyle = {
62
- color: theme.colors.textMuted,
63
- paddingLeft: '8px',
64
- };
65
- }
66
- // Section headers (UPPERCASE at start)
67
- else if (/^[A-Z\s]+:/.test(line)) {
68
- lineStyle = {
69
- fontWeight: 'bold',
70
- marginTop: '8px',
71
- color: theme.colors.text,
72
- };
73
- }
74
- return (_jsx("div", { style: lineStyle, children: content }, idx));
75
- });
76
- };
77
- return (_jsxs("div", { className: className, style: {
78
- width: '100%',
79
- height: '100%',
80
- display: 'flex',
81
- flexDirection: 'column',
82
- ...style,
83
- }, children: [_jsx("div", { style: {
84
- flex: 1,
85
- overflow: 'auto',
86
- padding: '20px',
87
- fontFamily: theme.fonts.monospace,
88
- fontSize: '14px',
89
- lineHeight: '1.6',
90
- color: theme.colors.text,
91
- backgroundColor: theme.colors.background,
92
- whiteSpace: 'pre-wrap',
93
- wordWrap: 'break-word',
94
- }, children: renderHighlightedText(result.text) }), showMetadata && (_jsxs("div", { style: {
95
- borderTop: `1px solid ${theme.colors.border}`,
96
- padding: '12px 20px',
97
- backgroundColor: theme.colors.surface,
98
- fontSize: '12px',
99
- color: theme.colors.textMuted,
100
- fontFamily: theme.fonts.monospace,
101
- }, children: [_jsxs("div", { style: { marginBottom: '4px' }, children: [_jsx("strong", { style: { color: theme.colors.text }, children: "Template:" }), " ", template.name] }), _jsxs("div", { style: { marginBottom: '4px' }, children: [_jsx("strong", { style: { color: theme.colors.text }, children: "Scenario:" }), " ", result.scenarioId] }), _jsxs("div", { children: [_jsx("strong", { style: { color: theme.colors.text }, children: "Events:" }), " ", result.metadata.eventCount, " total (", result.metadata.spanCount, " spans, ", result.metadata.logCount, " logs)"] }), result.metadata.timeRange && (_jsxs("div", { style: { marginTop: '4px' }, children: [_jsx("strong", { style: { color: theme.colors.text }, children: "Duration:" }), ' ', Number(result.metadata.timeRange.end) - Number(result.metadata.timeRange.start), "ms"] }))] }))] }));
102
- };
103
- //# sourceMappingURL=NarrativeRenderer.js.map