@principal-ai/principal-view-react 0.7.10 → 0.7.12

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.
@@ -1,4 +1,6 @@
1
- import React from 'react';
1
+ import React, { useState, useMemo } from 'react';
2
+ import { useTheme } from '@principal-ade/industry-theme';
3
+ import { HelpCircle } from 'lucide-react';
2
4
 
3
5
  interface SpanEvent {
4
6
  time: number;
@@ -18,106 +20,298 @@ interface TestSpan {
18
20
  errorMessage?: string;
19
21
  }
20
22
 
23
+ // OTEL Log types
24
+ export type OtelSeverity = 'TRACE' | 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'FATAL';
25
+
26
+ export interface OtelLog {
27
+ timestamp: number;
28
+ severity: OtelSeverity;
29
+ body: string | Record<string, unknown>;
30
+ resource: Record<string, string | number>;
31
+ attributes?: Record<string, any>;
32
+ traceId?: string;
33
+ spanId?: string;
34
+ }
35
+
36
+ // Timeline item (event or log)
37
+ interface TimelineItem {
38
+ type: 'event' | 'log';
39
+ time: number;
40
+ // For events
41
+ name?: string;
42
+ attributes?: Record<string, any>;
43
+ // For logs
44
+ severity?: OtelSeverity;
45
+ body?: string | Record<string, unknown>;
46
+ resource?: Record<string, string | number>;
47
+ }
48
+
21
49
  export interface TestEventPanelProps {
22
50
  spans: TestSpan[];
51
+ logs?: OtelLog[]; // Optional for backward compatibility
23
52
  currentSpanIndex: number;
24
53
  currentEventIndex: number;
25
54
  highlightedPhase?: string; // 'setup' | 'execution' | 'assertion'
26
55
  }
27
56
 
57
+ // Helper functions for log severity
58
+ function getSeverityColor(severity: OtelSeverity): string {
59
+ const colors = {
60
+ TRACE: '#6b7280',
61
+ DEBUG: '#60a5fa',
62
+ INFO: '#4ade80',
63
+ WARN: '#fbbf24',
64
+ ERROR: '#f87171',
65
+ FATAL: '#dc2626',
66
+ };
67
+ return colors[severity] || '#9ca3af';
68
+ }
69
+
70
+ function getSeverityIcon(severity: OtelSeverity): string {
71
+ const icons = {
72
+ TRACE: '○',
73
+ DEBUG: '◐',
74
+ INFO: '●',
75
+ WARN: '⚠',
76
+ ERROR: '✕',
77
+ FATAL: '☠',
78
+ };
79
+ return icons[severity] || '•';
80
+ }
81
+
28
82
  export const TestEventPanel: React.FC<TestEventPanelProps> = ({
29
83
  spans,
84
+ logs = [],
30
85
  currentSpanIndex,
31
86
  currentEventIndex,
32
87
  highlightedPhase,
33
88
  }) => {
89
+ const { theme } = useTheme();
90
+ const [showHelp, setShowHelp] = useState(false);
91
+ const [viewMode, setViewMode] = useState<'all' | 'events' | 'logs'>('all');
92
+
34
93
  const currentSpan = spans[currentSpanIndex];
35
- const eventsUpToNow = currentSpan?.events.slice(0, currentEventIndex + 1) || [];
94
+
95
+ // Build interleaved timeline
96
+ const timeline = useMemo(() => {
97
+ if (!currentSpan) return [];
98
+
99
+ const items: TimelineItem[] = [
100
+ // Span events
101
+ ...currentSpan.events.slice(0, currentEventIndex + 1).map((event) => ({
102
+ type: 'event' as const,
103
+ time: event.time,
104
+ name: event.name,
105
+ attributes: event.attributes,
106
+ })),
107
+
108
+ // Correlated logs (matching current span's traceId)
109
+ ...logs
110
+ .filter((log) => log.traceId === currentSpan.id)
111
+ .map((log) => ({
112
+ type: 'log' as const,
113
+ time: typeof log.timestamp === 'number' ? log.timestamp : new Date(log.timestamp).getTime(),
114
+ severity: log.severity,
115
+ body: log.body,
116
+ resource: log.resource,
117
+ attributes: log.attributes,
118
+ })),
119
+ ].sort((a, b) => a.time - b.time);
120
+
121
+ return items;
122
+ }, [currentSpan, currentEventIndex, logs]);
123
+
124
+ // Filter timeline based on view mode
125
+ const filteredTimeline = useMemo(() => {
126
+ if (viewMode === 'all') return timeline;
127
+ return timeline.filter((item) => item.type === viewMode.slice(0, -1)); // 'events' -> 'event', 'logs' -> 'log'
128
+ }, [timeline, viewMode]);
129
+
130
+ const eventCount = timeline.filter((i) => i.type === 'event').length;
131
+ const logCount = timeline.filter((i) => i.type === 'log').length;
36
132
 
37
133
  return (
38
134
  <div
39
135
  style={{
40
136
  width: '100%',
41
137
  height: '100%',
42
- backgroundColor: '#1a1a1a',
43
- color: '#ffffff',
138
+ backgroundColor: theme.colors.background,
139
+ color: theme.colors.text,
44
140
  padding: '20px',
45
- fontFamily: 'monospace',
46
- fontSize: '12px',
141
+ fontFamily: theme.fonts.monospace,
142
+ fontSize: '14px',
47
143
  overflow: 'auto',
144
+ boxSizing: 'border-box',
48
145
  }}
49
146
  >
50
- <div style={{ fontWeight: 'bold', marginBottom: '15px', fontSize: '14px' }}>
51
- 📊 Wide Event Pattern - Code Journey
147
+ <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '15px' }}>
148
+ <div style={{ fontWeight: 'bold', fontSize: '18px' }}>
149
+ Execution Timeline
150
+ </div>
151
+ <button
152
+ onClick={() => setShowHelp(true)}
153
+ style={{
154
+ background: 'transparent',
155
+ border: 'none',
156
+ cursor: 'pointer',
157
+ padding: '4px',
158
+ display: 'flex',
159
+ alignItems: 'center',
160
+ color: theme.colors.textMuted,
161
+ }}
162
+ onMouseEnter={(e) => {
163
+ e.currentTarget.style.color = theme.colors.text;
164
+ }}
165
+ onMouseLeave={(e) => {
166
+ e.currentTarget.style.color = theme.colors.textMuted;
167
+ }}
168
+ >
169
+ <HelpCircle size={20} />
170
+ </button>
52
171
  </div>
53
- <div style={{ fontSize: '10px', color: '#888', marginBottom: '15px' }}>
54
- Test: {currentSpan?.name || 'Loading...'}
55
- <br />
56
- Events: {eventsUpToNow.length} / {currentSpan?.events.length || 0}
172
+
173
+ {/* Filter Tabs */}
174
+ <div style={{ display: 'flex', gap: '8px', marginBottom: '12px' }}>
175
+ <button
176
+ onClick={() => setViewMode('all')}
177
+ style={{
178
+ padding: '6px 12px',
179
+ background: viewMode === 'all' ? theme.colors.primary : 'transparent',
180
+ border: `1px solid ${theme.colors.border}`,
181
+ borderRadius: '4px',
182
+ color: viewMode === 'all' ? '#ffffff' : theme.colors.text,
183
+ cursor: 'pointer',
184
+ fontSize: '12px',
185
+ fontWeight: 500,
186
+ }}
187
+ >
188
+ All ({timeline.length})
189
+ </button>
190
+ <button
191
+ onClick={() => setViewMode('events')}
192
+ style={{
193
+ padding: '6px 12px',
194
+ background: viewMode === 'events' ? theme.colors.primary : 'transparent',
195
+ border: `1px solid ${theme.colors.border}`,
196
+ borderRadius: '4px',
197
+ color: viewMode === 'events' ? '#ffffff' : theme.colors.text,
198
+ cursor: 'pointer',
199
+ fontSize: '12px',
200
+ fontWeight: 500,
201
+ }}
202
+ >
203
+ Events ({eventCount})
204
+ </button>
205
+ <button
206
+ onClick={() => setViewMode('logs')}
207
+ style={{
208
+ padding: '6px 12px',
209
+ background: viewMode === 'logs' ? theme.colors.primary : 'transparent',
210
+ border: `1px solid ${theme.colors.border}`,
211
+ borderRadius: '4px',
212
+ color: viewMode === 'logs' ? '#ffffff' : theme.colors.text,
213
+ cursor: 'pointer',
214
+ fontSize: '12px',
215
+ fontWeight: 500,
216
+ }}
217
+ >
218
+ Logs ({logCount})
219
+ </button>
57
220
  </div>
58
- <div style={{ fontSize: '10px', color: '#a3a3a3', marginBottom: '20px', lineHeight: '1.5' }}>
59
- Watch how execution flows through files:
60
- <br />
61
- <span style={{ color: '#60a5fa' }}>Blue = Test file</span>
62
- {' • '}
63
- <span style={{ color: '#4ade80' }}>Green → Code under test</span>
221
+
222
+ <div style={{ fontSize: '13px', color: theme.colors.textMuted, marginBottom: '15px' }}>
223
+ Test: {currentSpan?.name || 'Loading...'}
64
224
  </div>
65
225
 
66
- {currentSpan && (
67
- <>
68
- {/* Span Attributes (static context) */}
69
- <div style={{ marginBottom: '20px' }}>
70
- <div
71
- style={{
72
- color: '#60a5fa',
73
- fontWeight: 'bold',
74
- marginBottom: '8px',
75
- fontSize: '11px',
76
- }}
77
- >
78
- Span Context (Static)
226
+ {/* Help Modal */}
227
+ {showHelp && (
228
+ <div
229
+ style={{
230
+ position: 'fixed',
231
+ top: 0,
232
+ left: 0,
233
+ right: 0,
234
+ bottom: 0,
235
+ backgroundColor: 'rgba(0, 0, 0, 0.7)',
236
+ display: 'flex',
237
+ alignItems: 'center',
238
+ justifyContent: 'center',
239
+ zIndex: 9999,
240
+ }}
241
+ onClick={() => setShowHelp(false)}
242
+ >
243
+ <div
244
+ style={{
245
+ backgroundColor: theme.colors.background,
246
+ color: theme.colors.text,
247
+ padding: '24px',
248
+ borderRadius: '8px',
249
+ maxWidth: '600px',
250
+ border: `1px solid ${theme.colors.border}`,
251
+ }}
252
+ onClick={(e) => e.stopPropagation()}
253
+ >
254
+ <div style={{ fontWeight: 'bold', fontSize: '18px', marginBottom: '16px' }}>
255
+ How to Read This Panel
256
+ </div>
257
+ <div style={{ fontSize: '14px', marginBottom: '16px', lineHeight: '1.6' }}>
258
+ <p style={{ marginBottom: '12px' }}>
259
+ <strong>Timeline shows both events and logs:</strong>
260
+ </p>
261
+ <ul style={{ marginLeft: '20px', marginBottom: '16px' }}>
262
+ <li style={{ marginBottom: '8px' }}>
263
+ <span style={{ color: '#f59e0b' }}>🟧 Events</span> - Structured lifecycle points
264
+ </li>
265
+ <li style={{ marginBottom: '8px' }}>
266
+ <span style={{ color: '#4ade80' }}>● Logs</span> - Standalone log records (color = severity)
267
+ </li>
268
+ <li style={{ marginBottom: '8px' }}>
269
+ <span style={{ color: '#60a5fa' }}>Blue = Test file</span>
270
+ </li>
271
+ <li>
272
+ <span style={{ color: '#4ade80' }}>Green → Code under test</span>
273
+ </li>
274
+ </ul>
275
+ <p style={{ marginBottom: '12px' }}>
276
+ <strong>Use filter tabs to focus:</strong>
277
+ </p>
278
+ <ul style={{ marginLeft: '20px' }}>
279
+ <li>All - Interleaved timeline</li>
280
+ <li>Events - Span events only</li>
281
+ <li>Logs - OTEL logs only</li>
282
+ </ul>
79
283
  </div>
80
- <pre
284
+ <button
285
+ onClick={() => setShowHelp(false)}
81
286
  style={{
82
- background: '#0d0d0d',
83
- padding: '10px',
287
+ padding: '8px 16px',
288
+ backgroundColor: theme.colors.primary,
289
+ color: '#ffffff',
290
+ border: 'none',
84
291
  borderRadius: '4px',
85
- margin: 0,
86
- fontSize: '10px',
87
- lineHeight: '1.5',
292
+ cursor: 'pointer',
293
+ fontSize: '14px',
294
+ fontWeight: 500,
88
295
  }}
89
296
  >
90
- {JSON.stringify(
91
- {
92
- 'test.file': currentSpan.attributes['test.file'],
93
- 'test.suite': currentSpan.attributes['test.suite'],
94
- 'test.result': currentSpan.attributes['test.result'],
95
- },
96
- null,
97
- 2
98
- )}
99
- </pre>
297
+ Got it
298
+ </button>
100
299
  </div>
300
+ </div>
301
+ )}
101
302
 
102
- {/* Event Timeline (context mutations) */}
103
- <div>
104
- <div
105
- style={{
106
- color: '#4ade80',
107
- fontWeight: 'bold',
108
- marginBottom: '8px',
109
- fontSize: '11px',
110
- }}
111
- >
112
- Event Timeline (Context Mutations)
113
- </div>
114
- {eventsUpToNow.map((event, idx) => {
115
- const filepath = event.attributes['code.filepath'] as string;
116
- const lineno = event.attributes['code.lineno'] as number;
303
+ {/* Timeline Items */}
304
+ {currentSpan && (
305
+ <div>
306
+ {filteredTimeline.map((item, idx) => {
307
+ if (item.type === 'event') {
308
+ // SPAN EVENT RENDERING
309
+ const filepath = item.attributes?.['code.filepath'] as string;
310
+ const lineno = item.attributes?.['code.lineno'] as number;
117
311
  const isCodeUnderTest = filepath && filepath !== 'GraphConverter.test.ts';
118
312
 
119
313
  // Determine which phase this event belongs to
120
- const eventPhase = event.name.split('.')[0]; // 'setup', 'execution', 'assertion'
314
+ const eventPhase = item.name?.split('.')[0]; // 'setup', 'execution', 'assertion'
121
315
  const isHighlighted = highlightedPhase === eventPhase;
122
316
 
123
317
  return (
@@ -126,12 +320,14 @@ export const TestEventPanel: React.FC<TestEventPanelProps> = ({
126
320
  style={{
127
321
  marginBottom: '12px',
128
322
  paddingBottom: '12px',
129
- borderBottom: idx < eventsUpToNow.length - 1 ? '1px solid #333' : 'none',
323
+ paddingLeft: '12px',
324
+ borderBottom: idx < filteredTimeline.length - 1 ? `1px solid ${theme.colors.border}` : 'none',
325
+ borderLeft: '3px solid #f59e0b',
130
326
  opacity: highlightedPhase && !isHighlighted ? 0.4 : 1,
131
327
  transition: 'opacity 0.2s ease',
132
328
  transform: isHighlighted ? 'scale(1.02)' : 'scale(1)',
133
- backgroundColor: isHighlighted ? '#1e293b' : 'transparent',
134
- padding: isHighlighted ? '8px' : '0',
329
+ backgroundColor: isHighlighted ? theme.colors.surface : 'transparent',
330
+ padding: isHighlighted ? '8px 8px 8px 12px' : '0 0 12px 12px',
135
331
  borderRadius: '4px',
136
332
  }}
137
333
  >
@@ -141,20 +337,26 @@ export const TestEventPanel: React.FC<TestEventPanelProps> = ({
141
337
  justifyContent: 'space-between',
142
338
  alignItems: 'center',
143
339
  marginBottom: '4px',
340
+ gap: '8px',
144
341
  }}
145
342
  >
146
- <div style={{ color: '#f59e0b', fontSize: '10px' }}>
147
- {idx + 1}. {event.name}
343
+ <div style={{ color: '#f59e0b', fontSize: '13px', fontWeight: 'bold', flexShrink: 0 }}>
344
+ EVENT: {item.name}
148
345
  </div>
149
346
  {filepath && (
150
347
  <div
151
348
  style={{
152
- fontSize: '9px',
349
+ fontSize: '12px',
153
350
  color: isCodeUnderTest ? '#4ade80' : '#60a5fa',
154
351
  fontFamily: 'monospace',
155
352
  background: isCodeUnderTest ? '#064e3b' : '#1e3a8a',
156
353
  padding: '2px 6px',
157
354
  borderRadius: '3px',
355
+ flexShrink: 1,
356
+ minWidth: 0,
357
+ overflow: 'hidden',
358
+ textOverflow: 'ellipsis',
359
+ whiteSpace: 'nowrap',
158
360
  }}
159
361
  >
160
362
  {isCodeUnderTest && '→ '}
@@ -164,17 +366,19 @@ export const TestEventPanel: React.FC<TestEventPanelProps> = ({
164
366
  </div>
165
367
  <pre
166
368
  style={{
167
- background: '#0d0d0d',
369
+ background: theme.colors.surface,
168
370
  padding: '8px',
169
371
  borderRadius: '4px',
170
372
  margin: 0,
171
- fontSize: '9px',
373
+ fontSize: '12px',
172
374
  lineHeight: '1.4',
375
+ overflow: 'auto',
376
+ maxWidth: '100%',
173
377
  }}
174
378
  >
175
379
  {JSON.stringify(
176
380
  Object.fromEntries(
177
- Object.entries(event.attributes).filter(
381
+ Object.entries(item.attributes || {}).filter(
178
382
  ([key]) => key !== 'code.filepath' && key !== 'code.lineno'
179
383
  )
180
384
  ),
@@ -184,25 +388,112 @@ export const TestEventPanel: React.FC<TestEventPanelProps> = ({
184
388
  </pre>
185
389
  </div>
186
390
  );
187
- })}
188
- </div>
189
- </>
391
+ } else {
392
+ // OTEL LOG RENDERING
393
+ const serviceName = item.resource?.['service.name'];
394
+ const severityColor = getSeverityColor(item.severity!);
395
+
396
+ return (
397
+ <div
398
+ key={idx}
399
+ style={{
400
+ marginBottom: '12px',
401
+ paddingBottom: '12px',
402
+ paddingLeft: '12px',
403
+ borderBottom: idx < filteredTimeline.length - 1 ? `1px solid ${theme.colors.border}` : 'none',
404
+ borderLeft: `3px solid ${severityColor}`,
405
+ }}
406
+ >
407
+ <div
408
+ style={{
409
+ display: 'flex',
410
+ justifyContent: 'space-between',
411
+ alignItems: 'center',
412
+ marginBottom: '4px',
413
+ }}
414
+ >
415
+ <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
416
+ <span style={{ fontSize: '16px' }}>{getSeverityIcon(item.severity!)}</span>
417
+ <span
418
+ style={{
419
+ color: severityColor,
420
+ fontSize: '13px',
421
+ fontWeight: 'bold',
422
+ }}
423
+ >
424
+ LOG: {item.severity}
425
+ </span>
426
+ </div>
427
+ {serviceName && (
428
+ <div
429
+ style={{
430
+ fontSize: '12px',
431
+ color: '#9ca3af',
432
+ background: '#1e293b',
433
+ padding: '2px 6px',
434
+ borderRadius: '3px',
435
+ }}
436
+ >
437
+ {serviceName}
438
+ </div>
439
+ )}
440
+ </div>
441
+
442
+ {/* Log body */}
443
+ <div
444
+ style={{
445
+ background: theme.colors.surface,
446
+ padding: '8px',
447
+ borderRadius: '4px',
448
+ marginBottom: item.attributes && Object.keys(item.attributes).length > 0 ? '8px' : '0',
449
+ fontSize: '13px',
450
+ }}
451
+ >
452
+ {typeof item.body === 'string' ? (
453
+ item.body
454
+ ) : (
455
+ <pre style={{ margin: 0, fontSize: '12px' }}>
456
+ {JSON.stringify(item.body, null, 2)}
457
+ </pre>
458
+ )}
459
+ </div>
460
+
461
+ {/* Log attributes */}
462
+ {item.attributes && Object.keys(item.attributes).length > 0 && (
463
+ <pre
464
+ style={{
465
+ background: theme.colors.surface,
466
+ padding: '8px',
467
+ borderRadius: '4px',
468
+ margin: 0,
469
+ fontSize: '11px',
470
+ opacity: 0.8,
471
+ }}
472
+ >
473
+ {JSON.stringify(item.attributes, null, 2)}
474
+ </pre>
475
+ )}
476
+ </div>
477
+ );
478
+ }
479
+ })}
480
+ </div>
190
481
  )}
191
482
 
192
483
  <div
193
484
  style={{
194
485
  marginTop: '20px',
195
486
  paddingTop: '15px',
196
- borderTop: '1px solid #333',
197
- fontSize: '10px',
198
- color: '#888',
487
+ borderTop: `1px solid ${theme.colors.border}`,
488
+ fontSize: '13px',
489
+ color: theme.colors.textMuted,
199
490
  }}
200
491
  >
201
492
  <div style={{ marginBottom: '8px' }}>
202
493
  <strong>Total tests:</strong> {spans.length}
203
494
  </div>
204
495
  <div style={{ marginBottom: '8px' }}>
205
- <strong>Pattern:</strong> One span per test + event timeline
496
+ <strong>Timeline:</strong> {eventCount} events, {logCount} logs
206
497
  </div>
207
498
  <div>
208
499
  <strong>Status:</strong>{' '}
@@ -7,12 +7,12 @@ import { NodeTooltip } from '../components/NodeTooltip';
7
7
  import type { OtelInfo } from '../components/NodeTooltip';
8
8
 
9
9
  /**
10
- * Converts a hex color to a soft/lighter version with transparency
10
+ * Converts a hex color to a lighter/tinted version (opaque, not transparent)
11
11
  * @param hexColor - Hex color string (e.g., "#FF5733" or "#888")
12
- * @param alpha - Transparency level (0-1), defaults to 0.12 for subtle backgrounds
13
- * @returns rgba color string
12
+ * @param lightness - How much to lighten (0-1), defaults to 0.88 (88% white mixed in)
13
+ * @returns hex color string
14
14
  */
15
- function hexToSoftColor(hexColor: string, alpha = 0.12): string {
15
+ function hexToLightColor(hexColor: string, lightness = 0.88): string {
16
16
  // Remove # if present
17
17
  const hex = hexColor.replace('#', '');
18
18
 
@@ -21,7 +21,15 @@ function hexToSoftColor(hexColor: string, alpha = 0.12): string {
21
21
  const g = parseInt(hex.substring(2, 4), 16);
22
22
  const b = parseInt(hex.substring(4, 6), 16);
23
23
 
24
- return `rgba(${r}, ${g}, ${b}, ${alpha})`;
24
+ // Mix with white based on lightness factor
25
+ // lightness of 0.88 means 88% white + 12% original color
26
+ const newR = Math.round(r + (255 - r) * lightness);
27
+ const newG = Math.round(g + (255 - g) * lightness);
28
+ const newB = Math.round(b + (255 - b) * lightness);
29
+
30
+ // Convert back to hex
31
+ const toHex = (n: number) => n.toString(16).padStart(2, '0');
32
+ return `#${toHex(newR)}${toHex(newG)}${toHex(newB)}`;
25
33
  }
26
34
 
27
35
  export interface CustomNodeData extends Record<string, unknown> {
@@ -176,7 +184,7 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected, dragging
176
184
  const getShapeStyles = () => {
177
185
  const baseStyles = {
178
186
  padding: '12px 16px',
179
- backgroundColor: isGroup ? 'rgba(255, 255, 255, 0.7)' : hexToSoftColor(fillColor),
187
+ backgroundColor: isGroup ? 'rgba(255, 255, 255, 0.7)' : hexToLightColor(fillColor),
180
188
  color: '#000',
181
189
  border: `2px solid ${hasViolations ? '#D0021B' : strokeColor}`,
182
190
  fontSize: '12px',
@@ -278,7 +286,7 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected, dragging
278
286
  }
279
287
  : {};
280
288
 
281
- // Hexagon inner fill styles (soft color background inset from border)
289
+ // Hexagon inner fill styles (light color background inset from border)
282
290
  const hexagonInnerStyle: React.CSSProperties = isHexagon
283
291
  ? {
284
292
  position: 'absolute',
@@ -287,7 +295,7 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected, dragging
287
295
  right: hexagonBorderWidth,
288
296
  bottom: hexagonBorderWidth,
289
297
  clipPath: hexagonClipPath,
290
- backgroundColor: hexToSoftColor(fillColor),
298
+ backgroundColor: hexToLightColor(fillColor),
291
299
  color: '#000',
292
300
  display: 'flex',
293
301
  flexDirection: 'column',
@@ -320,7 +328,7 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected, dragging
320
328
  }
321
329
  : {};
322
330
 
323
- // Diamond inner fill styles (soft color background inset from border)
331
+ // Diamond inner fill styles (light color background inset from border)
324
332
  const diamondInnerStyle: React.CSSProperties = isDiamond
325
333
  ? {
326
334
  position: 'absolute',
@@ -329,7 +337,7 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected, dragging
329
337
  right: diamondBorderWidth,
330
338
  bottom: diamondBorderWidth,
331
339
  clipPath: diamondClipPath,
332
- backgroundColor: hexToSoftColor(fillColor),
340
+ backgroundColor: hexToLightColor(fillColor),
333
341
  color: '#000',
334
342
  display: 'flex',
335
343
  flexDirection: 'column',
@@ -411,7 +411,7 @@ function MultiConfigDemo() {
411
411
 
412
412
  {/* Graph visualization */}
413
413
  <div style={{ flex: 1 }}>
414
- <GraphRenderer canvas={selectedConfig.canvas} showMinimap showControls showBackground />
414
+ <GraphRenderer canvas={selectedConfig.canvas} showControls showBackground />
415
415
  </div>
416
416
  </div>
417
417
  );
@@ -310,7 +310,6 @@ export const Default: Story = {
310
310
  canvas: multiDirectionalCanvas,
311
311
  width: '100%',
312
312
  height: '800px',
313
- showMinimap: true,
314
313
  showControls: true,
315
314
  showBackground: true,
316
315
  },