@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
@@ -1,163 +0,0 @@
1
- /**
2
- * Utilities for loading and managing narrative templates
3
- */
4
- /**
5
- * Load a narrative template from a URL or file path
6
- *
7
- * Note: In most cases, you should import templates directly:
8
- * import template from './my-template.narrative.json';
9
- *
10
- * This function is useful when you need to dynamically load templates.
11
- *
12
- * @param path - Path to the narrative template JSON file
13
- * @returns Promise resolving to the narrative template, or null if not found
14
- */
15
- export async function loadNarrativeTemplate(path) {
16
- try {
17
- const response = await fetch(path);
18
- if (!response.ok) {
19
- console.warn(`Narrative template not found: ${path}`);
20
- return null;
21
- }
22
- const template = await response.json();
23
- return template;
24
- }
25
- catch (error) {
26
- console.warn(`Failed to load narrative template from ${path}:`, error);
27
- return null;
28
- }
29
- }
30
- /**
31
- * Auto-discover narrative template for a given canvas path
32
- *
33
- * Example:
34
- * Canvas: "/path/to/graph-converter.otel.canvas"
35
- * Narrative: "/path/to/graph-converter.narrative.json"
36
- *
37
- * @param canvasPath - Path to the .otel.canvas file
38
- * @returns Promise resolving to the narrative template, or null if not found
39
- */
40
- export async function discoverNarrativeTemplate(canvasPath) {
41
- const narrativePath = canvasPath.replace('.otel.canvas', '.narrative.json');
42
- return loadNarrativeTemplate(narrativePath);
43
- }
44
- /**
45
- * Validate a narrative template has required fields
46
- *
47
- * @param template - Template to validate
48
- * @returns True if valid, false otherwise
49
- */
50
- export function validateNarrativeTemplate(template) {
51
- if (!template || typeof template !== 'object') {
52
- return false;
53
- }
54
- const t = template;
55
- // Check required fields
56
- if (!t.version || !t.canvas || !t.name || !t.mode || !t.scenarioSelection || !t.scenarios) {
57
- return false;
58
- }
59
- // Check scenarios array
60
- if (!Array.isArray(t.scenarios) || t.scenarios.length === 0) {
61
- return false;
62
- }
63
- // Check each scenario has required fields
64
- for (const scenario of t.scenarios) {
65
- if (!scenario.id || !scenario.priority || !scenario.condition || !scenario.template) {
66
- return false;
67
- }
68
- }
69
- return true;
70
- }
71
- /**
72
- * Get a user-friendly error message for invalid templates
73
- *
74
- * @param template - Template to check
75
- * @returns Error message, or null if valid
76
- */
77
- export function getNarrativeTemplateError(template) {
78
- if (!template || typeof template !== 'object') {
79
- return 'Template must be a valid JSON object';
80
- }
81
- const t = template;
82
- if (!t.version)
83
- return 'Missing required field: version';
84
- if (!t.canvas)
85
- return 'Missing required field: canvas';
86
- if (!t.name)
87
- return 'Missing required field: name';
88
- if (!t.mode)
89
- return 'Missing required field: mode';
90
- if (!t.scenarioSelection)
91
- return 'Missing required field: scenarioSelection';
92
- if (!t.scenarios)
93
- return 'Missing required field: scenarios';
94
- if (!Array.isArray(t.scenarios)) {
95
- return 'Field "scenarios" must be an array';
96
- }
97
- if (t.scenarios.length === 0) {
98
- return 'Template must have at least one scenario';
99
- }
100
- for (let i = 0; i < t.scenarios.length; i++) {
101
- const scenario = t.scenarios[i];
102
- if (!scenario.id)
103
- return `Scenario ${i}: missing required field "id"`;
104
- if (scenario.priority === undefined)
105
- return `Scenario ${i}: missing required field "priority"`;
106
- if (!scenario.condition)
107
- return `Scenario ${i}: missing required field "condition"`;
108
- if (!scenario.template)
109
- return `Scenario ${i}: missing required field "template"`;
110
- }
111
- return null;
112
- }
113
- /**
114
- * React hook for loading narrative templates
115
- *
116
- * @param templatePath - Path to template, or null to skip loading
117
- * @returns {template, loading, error}
118
- */
119
- export function useNarrativeTemplate(templatePath) {
120
- const [template, setTemplate] = React.useState(null);
121
- const [loading, setLoading] = React.useState(false);
122
- const [error, setError] = React.useState(null);
123
- React.useEffect(() => {
124
- if (!templatePath) {
125
- setTemplate(null);
126
- setLoading(false);
127
- setError(null);
128
- return;
129
- }
130
- setLoading(true);
131
- setError(null);
132
- loadNarrativeTemplate(templatePath)
133
- .then((loadedTemplate) => {
134
- if (loadedTemplate) {
135
- const validationError = getNarrativeTemplateError(loadedTemplate);
136
- if (validationError) {
137
- setError(validationError);
138
- setTemplate(null);
139
- }
140
- else {
141
- setTemplate(loadedTemplate);
142
- setError(null);
143
- }
144
- }
145
- else {
146
- setError('Template not found');
147
- setTemplate(null);
148
- }
149
- })
150
- .catch((err) => {
151
- setError(err instanceof Error ? err.message : 'Unknown error');
152
- setTemplate(null);
153
- })
154
- .finally(() => {
155
- setLoading(false);
156
- });
157
- }, [templatePath]);
158
- return { template, loading, error };
159
- }
160
- // Note: React import is expected to be available in the React package
161
- // If this causes issues, we can remove the hook and export it separately
162
- import React from 'react';
163
- //# sourceMappingURL=narrative-loader.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"narrative-loader.js","sourceRoot":"","sources":["../../src/utils/narrative-loader.ts"],"names":[],"mappings":"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"}
@@ -1,404 +0,0 @@
1
- import React, { useState } from 'react';
2
- import type { Meta, StoryObj } from '@storybook/react';
3
- import { GraphRenderer } from '../components/GraphRenderer';
4
- import { TestEventPanel } from '../components/TestEventPanel';
5
- import type { ViewMode } from '../components/TestEventPanel';
6
- import type { ExtendedCanvas, GraphEvent, JsonObject, NarrativeTemplate } from '@principal-ai/principal-view-core/browser';
7
- import { ThemeProvider, defaultEditorTheme } from '@principal-ade/industry-theme';
8
- import testSpans from './data/graph-converter-test-execution.json';
9
- import narrativeTemplate from './data/graph-converter-test.narrative.json';
10
-
11
- const meta = {
12
- title: 'Features/Real Test Execution',
13
- component: GraphRenderer,
14
- parameters: {
15
- layout: 'fullscreen',
16
- docs: {
17
- description: {
18
- component:
19
- 'Visualizes REAL test execution data from instrumented Bun tests using the "wide event" pattern. Shows actual spans with file/line information collected from running GraphConverter.test.ts. Hover over graph nodes to highlight related events in the panel.',
20
- },
21
- },
22
- },
23
- tags: ['autodocs'],
24
- decorators: [
25
- (Story) => (
26
- <ThemeProvider theme={defaultEditorTheme}>
27
- <div style={{ width: '100vw', height: '100vh', background: '#0a0a0a' }}>
28
- <Story />
29
- </div>
30
- </ThemeProvider>
31
- ),
32
- ],
33
- } satisfies Meta<typeof GraphRenderer>;
34
-
35
- export default meta;
36
- type Story = StoryObj<typeof meta>;
37
-
38
- // ============================================================================
39
- // Test Execution Flow Canvas
40
- // ============================================================================
41
-
42
- const testExecutionCanvas: ExtendedCanvas = {
43
- nodes: [
44
- // Test Suite
45
- {
46
- id: 'test-suite',
47
- type: 'text',
48
- text: 'GraphConverter Test Suite',
49
- x: -100,
50
- y: -100,
51
- width: 240,
52
- height: 80,
53
- pv: {
54
- nodeType: 'test-suite',
55
- name: 'Test Suite',
56
- description: 'Collection of GraphConverter tests',
57
- shape: 'rectangle',
58
- fill: '#3b82f6',
59
- },
60
- },
61
-
62
- // Test Phase Nodes
63
- {
64
- id: 'setup-phase',
65
- type: 'text',
66
- text: 'Setup',
67
- x: -250,
68
- y: 50,
69
- width: 120,
70
- height: 80,
71
- pv: {
72
- nodeType: 'test-phase',
73
- name: 'Setup Phase',
74
- description: 'Test data preparation',
75
- shape: 'hexagon',
76
- fill: '#10b981',
77
- },
78
- },
79
- {
80
- id: 'execution-phase',
81
- type: 'text',
82
- text: 'Execution',
83
- x: -80,
84
- y: 50,
85
- width: 120,
86
- height: 80,
87
- pv: {
88
- nodeType: 'test-phase',
89
- name: 'Execution Phase',
90
- description: 'Code under test runs',
91
- shape: 'hexagon',
92
- fill: '#f59e0b',
93
- },
94
- },
95
- {
96
- id: 'assertion-phase',
97
- type: 'text',
98
- text: 'Assertion',
99
- x: 90,
100
- y: 50,
101
- width: 120,
102
- height: 80,
103
- pv: {
104
- nodeType: 'test-phase',
105
- name: 'Assertion Phase',
106
- description: 'Verify results',
107
- shape: 'hexagon',
108
- fill: '#8b5cf6',
109
- },
110
- },
111
-
112
- // Result Node
113
- {
114
- id: 'test-result',
115
- type: 'text',
116
- text: 'Test Result',
117
- x: -100,
118
- y: 200,
119
- width: 240,
120
- height: 80,
121
- pv: {
122
- nodeType: 'result',
123
- name: 'Test Result',
124
- description: 'Pass/Fail outcome',
125
- shape: 'rectangle',
126
- fill: '#10b981',
127
- },
128
- },
129
- ],
130
- edges: [
131
- {
132
- id: 'suite-to-setup',
133
- fromNode: 'test-suite',
134
- toNode: 'setup-phase',
135
- fromSide: 'bottom',
136
- toSide: 'top',
137
- label: 'start test',
138
- pv: {
139
- edgeType: 'flow',
140
- style: 'solid',
141
- },
142
- },
143
- {
144
- id: 'setup-to-execution',
145
- fromNode: 'setup-phase',
146
- toNode: 'execution-phase',
147
- fromSide: 'right',
148
- toSide: 'left',
149
- label: 'data ready',
150
- pv: {
151
- edgeType: 'flow',
152
- style: 'solid',
153
- },
154
- },
155
- {
156
- id: 'execution-to-assertion',
157
- fromNode: 'execution-phase',
158
- toNode: 'assertion-phase',
159
- fromSide: 'right',
160
- toSide: 'left',
161
- label: 'got result',
162
- pv: {
163
- edgeType: 'flow',
164
- style: 'solid',
165
- },
166
- },
167
- {
168
- id: 'assertion-to-result',
169
- fromNode: 'assertion-phase',
170
- toNode: 'test-result',
171
- fromSide: 'bottom',
172
- toSide: 'top',
173
- label: 'complete',
174
- pv: {
175
- edgeType: 'flow',
176
- style: 'solid',
177
- },
178
- },
179
- ],
180
- pv: {
181
- version: '1.0.0',
182
- name: 'Test Execution Flow',
183
- description: 'Visualizes the flow of test execution through phases',
184
- },
185
- };
186
-
187
- // ============================================================================
188
- // Interactive Story (No Animation)
189
- // ============================================================================
190
-
191
- const AnimatedTestExecution = () => {
192
- const [events] = useState<GraphEvent[]>([]);
193
- const [currentSpanIndex, setCurrentSpanIndex] = useState(0);
194
- // Show all events by default - set to a large number
195
- const [currentEventIndex] = useState(999);
196
- const [highlightedPhase, setHighlightedPhase] = useState<string | undefined>();
197
-
198
- // Extract spans and logs from test data
199
- const testData = testSpans as JsonObject;
200
- const spans = Array.isArray(testData) ? testData : testData.spans || testData;
201
- const logs = testData.logs || [];
202
-
203
- return (
204
- <div style={{ display: 'flex', width: '100vw', height: '100vh' }}>
205
- {/* Event Panel - Left Side */}
206
- <div style={{ flex: '0 0 50%', height: '100%', borderRight: `1px solid #333`, overflow: 'hidden' }}>
207
- <TestEventPanel
208
- spans={spans}
209
- logs={logs}
210
- currentSpanIndex={currentSpanIndex}
211
- currentEventIndex={currentEventIndex}
212
- highlightedPhase={highlightedPhase}
213
- onSpanIndexChange={setCurrentSpanIndex}
214
- />
215
- </div>
216
-
217
- {/* Graph Visualization - Right Side */}
218
- <div
219
- style={{ flex: '0 0 50%', height: '100%', position: 'relative' }}
220
- onMouseLeave={() => setHighlightedPhase(undefined)}
221
- >
222
- <div
223
- style={{ width: '100%', height: '100%' }}
224
- onMouseOver={(e) => {
225
- // Check if hovering over a phase node
226
- const target = e.target as HTMLElement;
227
- const textContent = target.textContent;
228
- if (textContent === 'Setup') setHighlightedPhase('setup');
229
- else if (textContent === 'Execution') setHighlightedPhase('execution');
230
- else if (textContent === 'Assertion') setHighlightedPhase('assertion');
231
- }}
232
- >
233
- <GraphRenderer
234
- canvas={testExecutionCanvas}
235
- showControls={true}
236
- events={events}
237
- />
238
- </div>
239
- </div>
240
- </div>
241
- );
242
- };
243
-
244
- /**
245
- * Interactive visualization of real test execution data using the "wide event" pattern.
246
- *
247
- * This demonstrates the key concept from loggingsucks.com:
248
- * - ONE comprehensive span per test (not multiple child spans)
249
- * - Events show the narrative of what happened during execution
250
- * - Context accumulates through event attributes with file/line information
251
- * - Easy to search by test.name to get full execution story
252
- *
253
- * **Interaction:**
254
- * - Hover over graph nodes (Setup, Execution, Assertion) to highlight related events
255
- * - Watch the code journey: blue = test file, green = code under test
256
- * - All events are shown immediately for easy review
257
- */
258
- export const Animated: Story = {
259
- render: () => <AnimatedTestExecution />,
260
- };
261
-
262
- /**
263
- * Static view of the test execution flow showing phases.
264
- */
265
- export const StaticView: Story = {
266
- args: {
267
- canvas: testExecutionCanvas,
268
- showControls: true,
269
- },
270
- };
271
-
272
- /**
273
- * Event panel component showing test execution narrative with file/line information.
274
- *
275
- * Shows how events and logs are interleaved in chronological order, with automatic
276
- * file/line capture from stack traces and severity-based color coding for logs.
277
- */
278
- export const EventPanelOnly: StoryObj = {
279
- render: () => {
280
- const testData = testSpans as JsonObject;
281
- const spans = Array.isArray(testData) ? testData : testData.spans || testData;
282
- const logs = testData.logs || [];
283
-
284
- return (
285
- <div style={{ width: '600px', height: '100vh' }}>
286
- <TestEventPanel
287
- spans={spans}
288
- logs={logs}
289
- currentSpanIndex={0}
290
- currentEventIndex={999} // Show all events
291
- highlightedPhase={undefined}
292
- />
293
- </div>
294
- );
295
- },
296
- };
297
-
298
- /**
299
- * Event panel with narrative view toggle
300
- *
301
- * Demonstrates the new narrative view mode that transforms raw OTEL events into
302
- * human-readable execution narratives. Toggle between "Raw Events" and "Narrative"
303
- * to see both views.
304
- *
305
- * Features:
306
- * - Human-readable narrative generated from template
307
- * - Scenario-based rendering (test-passed vs test-failed)
308
- * - Syntax highlighting for better readability
309
- * - Optional metadata display
310
- */
311
- export const WithNarrativeToggle: StoryObj = {
312
- render: () => {
313
- const [viewMode, setViewMode] = useState<ViewMode>('narrative');
314
- const [currentSpanIndex, setCurrentSpanIndex] = useState(0);
315
- const [showMetadata, setShowMetadata] = useState(false);
316
-
317
- const testData = testSpans as JsonObject;
318
- const spans = Array.isArray(testData) ? testData : testData.spans || testData;
319
- const logs = testData.logs || [];
320
-
321
- return (
322
- <div style={{ width: '100vw', height: '100vh', display: 'flex', flexDirection: 'column' }}>
323
- {/* Controls */}
324
- <div
325
- style={{
326
- padding: '12px 20px',
327
- background: '#1a1a1a',
328
- borderBottom: '1px solid #333',
329
- display: 'flex',
330
- gap: '12px',
331
- alignItems: 'center',
332
- }}
333
- >
334
- <label style={{ color: '#999', fontSize: '14px' }}>
335
- <input
336
- type="checkbox"
337
- checked={showMetadata}
338
- onChange={(e) => setShowMetadata(e.target.checked)}
339
- style={{ marginRight: '6px' }}
340
- />
341
- Show Metadata
342
- </label>
343
- </div>
344
-
345
- {/* Event Panel */}
346
- <div style={{ flex: 1, overflow: 'hidden' }}>
347
- <TestEventPanel
348
- spans={spans}
349
- logs={logs}
350
- currentSpanIndex={currentSpanIndex}
351
- currentEventIndex={999}
352
- onSpanIndexChange={setCurrentSpanIndex}
353
- viewMode={viewMode}
354
- narrativeTemplate={narrativeTemplate as NarrativeTemplate}
355
- onViewModeChange={setViewMode}
356
- showNarrativeMetadata={showMetadata}
357
- />
358
- </div>
359
- </div>
360
- );
361
- },
362
- };
363
-
364
- /**
365
- * Split view with narrative on left, graph on right
366
- *
367
- * Shows how the narrative view integrates with the graph visualization,
368
- * providing a complete picture of test execution.
369
- */
370
- export const NarrativeWithGraph: StoryObj = {
371
- render: () => {
372
- const [viewMode, setViewMode] = useState<ViewMode>('narrative');
373
- const [currentSpanIndex, setCurrentSpanIndex] = useState(0);
374
- const [events] = useState<GraphEvent[]>([]);
375
-
376
- const testData = testSpans as JsonObject;
377
- const spans = Array.isArray(testData) ? testData : testData.spans || testData;
378
- const logs = testData.logs || [];
379
-
380
- return (
381
- <div style={{ display: 'flex', width: '100vw', height: '100vh' }}>
382
- {/* Event Panel - Left Side */}
383
- <div style={{ flex: '0 0 50%', height: '100%', borderRight: '1px solid #333', overflow: 'hidden' }}>
384
- <TestEventPanel
385
- spans={spans}
386
- logs={logs}
387
- currentSpanIndex={currentSpanIndex}
388
- currentEventIndex={999}
389
- onSpanIndexChange={setCurrentSpanIndex}
390
- viewMode={viewMode}
391
- narrativeTemplate={narrativeTemplate as NarrativeTemplate}
392
- onViewModeChange={setViewMode}
393
- showNarrativeMetadata={true}
394
- />
395
- </div>
396
-
397
- {/* Graph Visualization - Right Side */}
398
- <div style={{ flex: '0 0 50%', height: '100%', position: 'relative' }}>
399
- <GraphRenderer canvas={testExecutionCanvas} showControls={true} events={events} />
400
- </div>
401
- </div>
402
- );
403
- },
404
- };