@principal-ai/principal-view-react 0.6.8 → 0.6.9

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,4 @@
1
- import React, { useState, useEffect } from 'react';
1
+ import React, { useState } from 'react';
2
2
  import type { NodeState, NodeTypeDefinition } from '@principal-ai/principal-view-core';
3
3
  import { resolveIcon } from '../utils/iconResolver';
4
4
 
@@ -40,10 +40,9 @@ export const NodeInfoPanel: React.FC<NodeInfoPanelProps> = ({
40
40
  const color = typeDefinition?.color || '#888';
41
41
  const canEdit = Boolean(onUpdate);
42
42
 
43
- // Local state for editing
44
- const [editingName, setEditingName] = useState(false);
45
- const [nameValue, setNameValue] = useState('');
43
+ // Local state for UI
46
44
  const [showIconPicker, setShowIconPicker] = useState(false);
45
+ const [showDetails, setShowDetails] = useState(false);
47
46
 
48
47
  // Current icon - either from node data override or type definition
49
48
  const currentIcon = (node.data?.icon as string) || typeDefinition?.icon;
@@ -53,13 +52,6 @@ export const NodeInfoPanel: React.FC<NodeInfoPanelProps> = ({
53
52
  ? Object.entries(typeDefinition.dataSchema).find(([, schema]) => schema.displayInLabel)?.[0]
54
53
  : null;
55
54
 
56
- // Initialize name value when node changes
57
- useEffect(() => {
58
- if (nameField && node.data?.[nameField]) {
59
- setNameValue(String(node.data[nameField]));
60
- }
61
- }, [node.id, nameField, node.data]);
62
-
63
55
  // Get fields to display based on dataSchema
64
56
  const displayFields = typeDefinition?.dataSchema
65
57
  ? Object.entries(typeDefinition.dataSchema)
@@ -73,16 +65,10 @@ export const NodeInfoPanel: React.FC<NodeInfoPanelProps> = ({
73
65
 
74
66
  // Always show basic node data if no schema is defined
75
67
  const hasSchemaFields = displayFields.length > 0;
76
- const nodeDataEntries = node.data ? Object.entries(node.data).filter(([key]) => key !== 'icon') : [];
68
+ const nodeDataEntries = node.data ? Object.entries(node.data).filter(([key]) => !['icon', 'name', 'description', 'sources'].includes(key)) : [];
77
69
 
78
- const handleNameSave = () => {
79
- if (onUpdate && nameField && nameValue !== node.data?.[nameField]) {
80
- onUpdate(node.id, {
81
- data: { ...node.data, [nameField]: nameValue },
82
- });
83
- }
84
- setEditingName(false);
85
- };
70
+ // Get sources from node data
71
+ const sources = (node.data?.sources as string[]) || [];
86
72
 
87
73
  const handleTypeChange = (newType: string) => {
88
74
  if (onUpdate && newType !== node.type) {
@@ -114,7 +100,7 @@ export const NodeInfoPanel: React.FC<NodeInfoPanelProps> = ({
114
100
  zIndex: 1000,
115
101
  }}
116
102
  >
117
- {/* Header */}
103
+ {/* Header - shows node name */}
118
104
  <div style={{
119
105
  display: 'flex',
120
106
  justifyContent: 'space-between',
@@ -124,7 +110,7 @@ export const NodeInfoPanel: React.FC<NodeInfoPanelProps> = ({
124
110
  borderBottom: `2px solid ${color}`,
125
111
  }}>
126
112
  <div style={{ fontWeight: 'bold', fontSize: '14px' }}>
127
- Node Information
113
+ {node.data?.name || node.id}
128
114
  </div>
129
115
  <button
130
116
  onClick={onClose}
@@ -146,256 +132,265 @@ export const NodeInfoPanel: React.FC<NodeInfoPanelProps> = ({
146
132
  </button>
147
133
  </div>
148
134
 
149
- {/* Icon Selector */}
150
- <div style={{ marginBottom: '12px' }}>
151
- <div style={{ fontSize: '10px', color: '#666', marginBottom: '4px' }}>
152
- Icon
153
- </div>
154
- <div style={{ position: 'relative' }}>
155
- <button
156
- onClick={() => canEdit && setShowIconPicker(!showIconPicker)}
157
- disabled={!canEdit}
158
- style={{
159
- display: 'flex',
160
- alignItems: 'center',
161
- gap: '8px',
162
- padding: '6px 10px',
163
- backgroundColor: '#f5f5f5',
164
- border: canEdit ? '1px dashed #ccc' : '1px solid #eee',
165
- borderRadius: '4px',
166
- cursor: canEdit ? 'pointer' : 'default',
167
- fontSize: '12px',
168
- width: '100%',
169
- justifyContent: 'flex-start',
170
- }}
171
- >
172
- <span style={{ display: 'flex', alignItems: 'center' }}>
173
- {resolveIcon(currentIcon, 18)}
174
- </span>
175
- <span>{currentIcon || 'No icon'}</span>
176
- {canEdit && <span style={{ marginLeft: 'auto', color: '#999', fontSize: '10px' }}>✎</span>}
177
- </button>
178
-
179
- {/* Icon Picker Dropdown */}
180
- {showIconPicker && (
181
- <div
182
- style={{
183
- position: 'absolute',
184
- top: '100%',
185
- left: 0,
186
- right: 0,
187
- marginTop: '4px',
188
- backgroundColor: 'white',
189
- border: '1px solid #ddd',
190
- borderRadius: '4px',
191
- boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
192
- padding: '8px',
193
- maxHeight: '200px',
194
- overflowY: 'auto',
195
- zIndex: 1001,
196
- }}
197
- >
198
- <div
199
- style={{
200
- display: 'grid',
201
- gridTemplateColumns: 'repeat(6, 1fr)',
202
- gap: '4px',
203
- }}
204
- >
205
- {COMMON_ICONS.map(iconName => (
206
- <button
207
- key={iconName}
208
- onClick={() => handleIconSelect(iconName)}
209
- title={iconName}
210
- style={{
211
- padding: '6px',
212
- border: currentIcon === iconName ? `2px solid ${color}` : '1px solid #eee',
213
- borderRadius: '4px',
214
- backgroundColor: currentIcon === iconName ? '#f0f7ff' : 'white',
215
- cursor: 'pointer',
216
- display: 'flex',
217
- alignItems: 'center',
218
- justifyContent: 'center',
219
- }}
220
- >
221
- {resolveIcon(iconName, 16)}
222
- </button>
223
- ))}
224
- </div>
225
- </div>
226
- )}
227
- </div>
228
- </div>
229
-
230
- {/* Node Type - Editable if availableNodeTypes provided */}
231
- <div style={{ marginBottom: '12px' }}>
232
- <div style={{ fontSize: '10px', color: '#666', marginBottom: '4px' }}>
233
- Type
234
- </div>
235
- {canEdit && availableNodeTypes && Object.keys(availableNodeTypes).length > 1 ? (
236
- <select
237
- value={node.type}
238
- onChange={(e) => handleTypeChange(e.target.value)}
239
- style={{
240
- fontSize: '12px',
241
- padding: '4px 8px',
242
- borderRadius: '4px',
243
- border: '1px solid #ccc',
244
- backgroundColor: 'white',
245
- cursor: 'pointer',
246
- width: '100%',
247
- }}
248
- >
249
- {Object.entries(availableNodeTypes).map(([typeName, typeDef]) => (
250
- <option key={typeName} value={typeName}>
251
- {typeName} ({typeDef.shape})
252
- </option>
253
- ))}
254
- </select>
255
- ) : (
256
- <div style={{
257
- fontSize: '12px',
258
- padding: '4px 8px',
259
- backgroundColor: color,
260
- color: 'white',
261
- borderRadius: '4px',
262
- display: 'inline-block',
263
- }}>
264
- {node.type}
265
- </div>
266
- )}
267
- </div>
268
-
269
- {/* Node State */}
270
- {node.state && (
135
+ {/* Description - first field under header */}
136
+ {node.data?.description && (
271
137
  <div style={{ marginBottom: '12px' }}>
272
138
  <div style={{ fontSize: '10px', color: '#666', marginBottom: '4px' }}>
273
- State
139
+ Description
274
140
  </div>
275
141
  <div style={{
276
142
  fontSize: '12px',
277
- padding: '4px 8px',
278
- backgroundColor: typeDefinition?.states?.[node.state]?.color || '#888',
279
- color: 'white',
280
- borderRadius: '4px',
281
- display: 'inline-block',
143
+ color: '#333',
282
144
  }}>
283
- {typeDefinition?.states?.[node.state]?.label || node.state}
145
+ {String(node.data.description)}
284
146
  </div>
285
147
  </div>
286
148
  )}
287
149
 
288
- {/* Editable Name Field */}
289
- {nameField && (
150
+ {/* Sources - shown after description */}
151
+ {sources.length > 0 && (
290
152
  <div style={{ marginBottom: '12px' }}>
291
153
  <div style={{ fontSize: '10px', color: '#666', marginBottom: '4px' }}>
292
- Name
154
+ Sources
293
155
  </div>
294
- {canEdit && editingName ? (
295
- <div style={{ display: 'flex', gap: '4px' }}>
296
- <input
297
- type="text"
298
- value={nameValue}
299
- onChange={(e) => setNameValue(e.target.value)}
300
- onKeyDown={(e) => {
301
- if (e.key === 'Enter') handleNameSave();
302
- if (e.key === 'Escape') setEditingName(false);
303
- }}
304
- autoFocus
156
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '4px' }}>
157
+ {sources.map((source, index) => (
158
+ <span
159
+ key={index}
305
160
  style={{
306
- flex: 1,
307
- fontSize: '12px',
308
- padding: '4px 8px',
161
+ fontSize: '11px',
162
+ padding: '2px 8px',
163
+ backgroundColor: '#f0f0f0',
309
164
  borderRadius: '4px',
310
- border: '1px solid #4A90E2',
311
- outline: 'none',
165
+ color: '#555',
312
166
  }}
313
- />
167
+ >
168
+ {source}
169
+ </span>
170
+ ))}
171
+ </div>
172
+ </div>
173
+ )}
174
+
175
+ {/* Expand/Collapse button for additional details */}
176
+ <button
177
+ onClick={() => setShowDetails(!showDetails)}
178
+ style={{
179
+ width: '100%',
180
+ padding: '8px',
181
+ backgroundColor: '#f5f5f5',
182
+ border: '1px solid #ddd',
183
+ borderRadius: '4px',
184
+ cursor: 'pointer',
185
+ fontSize: '12px',
186
+ color: '#666',
187
+ display: 'flex',
188
+ alignItems: 'center',
189
+ justifyContent: 'center',
190
+ gap: '6px',
191
+ marginBottom: showDetails ? '12px' : '0',
192
+ }}
193
+ >
194
+ <span style={{ transform: showDetails ? 'rotate(180deg)' : 'rotate(0deg)', transition: 'transform 0.2s' }}>
195
+
196
+ </span>
197
+ {showDetails ? 'Hide Details' : 'Show Details'}
198
+ </button>
199
+
200
+ {/* Expandable details section */}
201
+ {showDetails && (
202
+ <>
203
+ {/* Icon Selector */}
204
+ <div style={{ marginBottom: '12px' }}>
205
+ <div style={{ fontSize: '10px', color: '#666', marginBottom: '4px' }}>
206
+ Icon
207
+ </div>
208
+ <div style={{ position: 'relative' }}>
314
209
  <button
315
- onClick={handleNameSave}
210
+ onClick={() => canEdit && setShowIconPicker(!showIconPicker)}
211
+ disabled={!canEdit}
316
212
  style={{
317
- padding: '4px 8px',
318
- backgroundColor: '#4A90E2',
319
- color: 'white',
320
- border: 'none',
213
+ display: 'flex',
214
+ alignItems: 'center',
215
+ gap: '8px',
216
+ padding: '6px 10px',
217
+ backgroundColor: '#f5f5f5',
218
+ border: canEdit ? '1px dashed #ccc' : '1px solid #eee',
321
219
  borderRadius: '4px',
322
- cursor: 'pointer',
323
- fontSize: '11px',
220
+ cursor: canEdit ? 'pointer' : 'default',
221
+ fontSize: '12px',
222
+ width: '100%',
223
+ justifyContent: 'flex-start',
324
224
  }}
325
225
  >
326
- Save
226
+ <span style={{ display: 'flex', alignItems: 'center' }}>
227
+ {resolveIcon(currentIcon, 18)}
228
+ </span>
229
+ <span>{currentIcon || 'No icon'}</span>
230
+ {canEdit && <span style={{ marginLeft: 'auto', color: '#999', fontSize: '10px' }}>✎</span>}
327
231
  </button>
232
+
233
+ {/* Icon Picker Dropdown */}
234
+ {showIconPicker && (
235
+ <div
236
+ style={{
237
+ position: 'absolute',
238
+ top: '100%',
239
+ left: 0,
240
+ right: 0,
241
+ marginTop: '4px',
242
+ backgroundColor: 'white',
243
+ border: '1px solid #ddd',
244
+ borderRadius: '4px',
245
+ boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
246
+ padding: '8px',
247
+ maxHeight: '200px',
248
+ overflowY: 'auto',
249
+ zIndex: 1001,
250
+ }}
251
+ >
252
+ <div
253
+ style={{
254
+ display: 'grid',
255
+ gridTemplateColumns: 'repeat(6, 1fr)',
256
+ gap: '4px',
257
+ }}
258
+ >
259
+ {COMMON_ICONS.map(iconName => (
260
+ <button
261
+ key={iconName}
262
+ onClick={() => handleIconSelect(iconName)}
263
+ title={iconName}
264
+ style={{
265
+ padding: '6px',
266
+ border: currentIcon === iconName ? `2px solid ${color}` : '1px solid #eee',
267
+ borderRadius: '4px',
268
+ backgroundColor: currentIcon === iconName ? '#f0f7ff' : 'white',
269
+ cursor: 'pointer',
270
+ display: 'flex',
271
+ alignItems: 'center',
272
+ justifyContent: 'center',
273
+ }}
274
+ >
275
+ {resolveIcon(iconName, 16)}
276
+ </button>
277
+ ))}
278
+ </div>
279
+ </div>
280
+ )}
281
+ </div>
282
+ </div>
283
+
284
+ {/* Node Type - Editable if availableNodeTypes provided */}
285
+ <div style={{ marginBottom: '12px' }}>
286
+ <div style={{ fontSize: '10px', color: '#666', marginBottom: '4px' }}>
287
+ Type
328
288
  </div>
329
- ) : (
330
- <div
331
- onClick={() => canEdit && setEditingName(true)}
332
- style={{
289
+ {canEdit && availableNodeTypes && Object.keys(availableNodeTypes).length > 1 ? (
290
+ <select
291
+ value={node.type}
292
+ onChange={(e) => handleTypeChange(e.target.value)}
293
+ style={{
294
+ fontSize: '12px',
295
+ padding: '4px 8px',
296
+ borderRadius: '4px',
297
+ border: '1px solid #ccc',
298
+ backgroundColor: 'white',
299
+ cursor: 'pointer',
300
+ width: '100%',
301
+ }}
302
+ >
303
+ {Object.entries(availableNodeTypes).map(([typeName, typeDef]) => (
304
+ <option key={typeName} value={typeName}>
305
+ {typeName} ({typeDef.shape})
306
+ </option>
307
+ ))}
308
+ </select>
309
+ ) : (
310
+ <div style={{
333
311
  fontSize: '12px',
334
312
  padding: '4px 8px',
335
- backgroundColor: '#f5f5f5',
313
+ backgroundColor: color,
314
+ color: 'white',
336
315
  borderRadius: '4px',
337
- cursor: canEdit ? 'pointer' : 'default',
338
- border: canEdit ? '1px dashed #ccc' : 'none',
339
- }}
340
- title={canEdit ? 'Click to edit' : undefined}
341
- >
342
- {node.data?.[nameField] ?? '-'}
343
- {canEdit && <span style={{ marginLeft: '8px', color: '#999', fontSize: '10px' }}>✎</span>}
344
- </div>
345
- )}
346
- </div>
347
- )}
348
-
349
- {/* Display other schema-defined fields (non-editable for now) */}
350
- {hasSchemaFields && displayFields.filter(f => f.field !== nameField).length > 0 && (
351
- <div style={{ marginBottom: '12px' }}>
352
- <div style={{ fontSize: '10px', color: '#666', marginBottom: '8px', fontWeight: 'bold' }}>
353
- Properties
316
+ display: 'inline-block',
317
+ }}>
318
+ {node.type}
319
+ </div>
320
+ )}
354
321
  </div>
355
- {displayFields.filter(f => f.field !== nameField).map(({ field, label, value }) => (
356
- <div key={field} style={{ marginBottom: '8px' }}>
357
- <div style={{ fontSize: '10px', color: '#666', marginBottom: '2px' }}>
358
- {label}
322
+
323
+ {/* Node State */}
324
+ {node.state && (
325
+ <div style={{ marginBottom: '12px' }}>
326
+ <div style={{ fontSize: '10px', color: '#666', marginBottom: '4px' }}>
327
+ State
359
328
  </div>
360
- <div style={{ fontSize: '12px', color: '#333' }}>
361
- {value !== undefined && value !== null
362
- ? typeof value === 'object'
363
- ? JSON.stringify(value, null, 2)
364
- : String(value)
365
- : '-'}
329
+ <div style={{
330
+ fontSize: '12px',
331
+ padding: '4px 8px',
332
+ backgroundColor: typeDefinition?.states?.[node.state]?.color || '#888',
333
+ color: 'white',
334
+ borderRadius: '4px',
335
+ display: 'inline-block',
336
+ }}>
337
+ {typeDefinition?.states?.[node.state]?.label || node.state}
366
338
  </div>
367
339
  </div>
368
- ))}
369
- </div>
370
- )}
340
+ )}
371
341
 
372
- {/* Show all node data if no schema is defined */}
373
- {!hasSchemaFields && nodeDataEntries.length > 0 && (
374
- <div style={{ marginBottom: '12px' }}>
375
- <div style={{ fontSize: '10px', color: '#666', marginBottom: '8px', fontWeight: 'bold' }}>
376
- Data
377
- </div>
378
- {nodeDataEntries.map(([key, value]) => (
379
- <div key={key} style={{ marginBottom: '8px' }}>
380
- <div style={{ fontSize: '10px', color: '#666', marginBottom: '2px' }}>
381
- {key}
342
+ {/* Display other schema-defined fields (non-editable for now) */}
343
+ {hasSchemaFields && displayFields.filter(f => f.field !== nameField).length > 0 && (
344
+ <div style={{ marginBottom: '12px' }}>
345
+ <div style={{ fontSize: '10px', color: '#666', marginBottom: '8px', fontWeight: 'bold' }}>
346
+ Properties
382
347
  </div>
383
- <div style={{ fontSize: '12px', color: '#333', wordBreak: 'break-word' }}>
384
- {value !== undefined && value !== null
385
- ? typeof value === 'object'
386
- ? JSON.stringify(value, null, 2)
387
- : String(value)
388
- : '-'}
348
+ {displayFields.filter(f => f.field !== nameField).map(({ field, label, value }) => (
349
+ <div key={field} style={{ marginBottom: '8px' }}>
350
+ <div style={{ fontSize: '10px', color: '#666', marginBottom: '2px' }}>
351
+ {label}
352
+ </div>
353
+ <div style={{ fontSize: '12px', color: '#333' }}>
354
+ {value !== undefined && value !== null
355
+ ? typeof value === 'object'
356
+ ? JSON.stringify(value, null, 2)
357
+ : String(value)
358
+ : '-'}
359
+ </div>
360
+ </div>
361
+ ))}
362
+ </div>
363
+ )}
364
+
365
+ {/* Show all node data if no schema is defined */}
366
+ {!hasSchemaFields && nodeDataEntries.length > 0 && (
367
+ <div style={{ marginBottom: '12px' }}>
368
+ <div style={{ fontSize: '10px', color: '#666', marginBottom: '8px', fontWeight: 'bold' }}>
369
+ Data
389
370
  </div>
371
+ {nodeDataEntries.map(([key, value]) => (
372
+ <div key={key} style={{ marginBottom: '8px' }}>
373
+ <div style={{ fontSize: '10px', color: '#666', marginBottom: '2px' }}>
374
+ {key}
375
+ </div>
376
+ <div style={{ fontSize: '12px', color: '#333', wordBreak: 'break-word' }}>
377
+ {value !== undefined && value !== null
378
+ ? typeof value === 'object'
379
+ ? JSON.stringify(value, null, 2)
380
+ : String(value)
381
+ : '-'}
382
+ </div>
383
+ </div>
384
+ ))}
390
385
  </div>
391
- ))}
392
- </div>
393
- )}
386
+ )}
394
387
 
395
- {/* Metadata */}
396
- <div style={{ fontSize: '10px', color: '#999', marginTop: '12px', paddingTop: '8px', borderTop: '1px solid #eee' }}>
397
- ID: {node.id}
398
- </div>
388
+ {/* Metadata */}
389
+ <div style={{ fontSize: '10px', color: '#999', marginTop: '12px', paddingTop: '8px', borderTop: '1px solid #eee' }}>
390
+ ID: {node.id}
391
+ </div>
392
+ </>
393
+ )}
399
394
 
400
395
  {/* Delete Button */}
401
396
  {onDelete && (
@@ -5,7 +5,7 @@ import type { NodeTypeDefinition } from '@principal-ai/principal-view-core';
5
5
  import { resolveIcon } from '../utils/iconResolver';
6
6
 
7
7
  export interface CustomNodeData extends Record<string, unknown> {
8
- label: string;
8
+ name: string;
9
9
  typeDefinition: NodeTypeDefinition;
10
10
  state?: string;
11
11
  hasViolations?: boolean;
@@ -60,11 +60,8 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected }) => {
60
60
  // Use fillColor as the primary "color" for backwards compatibility
61
61
  const color = fillColor;
62
62
 
63
- // Get label from data schema
64
- const labelField = Object.entries(typeDefinition.dataSchema).find(
65
- ([, schema]) => schema.displayInLabel
66
- )?.[0];
67
- const displayLabel = labelField && nodeData[labelField] ? String(nodeData[labelField]) : nodeProps.label;
63
+ // Get display name - use name from props (falls back to node.id in converter)
64
+ const displayName = nodeProps.name;
68
65
 
69
66
  // Icon priority: node data override > state icon (node data states first) > type definition icon
70
67
  const icon = (nodeData.icon as string)
@@ -263,7 +260,7 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected }) => {
263
260
  <div style={hexagonInnerStyle}>
264
261
  {icon && <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>{resolveIcon(icon, 20)}</div>}
265
262
  <div style={{ textAlign: 'center', wordBreak: 'break-word' }}>
266
- {displayLabel}
263
+ {displayName}
267
264
  </div>
268
265
  {state && (
269
266
  <div style={{
@@ -294,7 +291,7 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected }) => {
294
291
  <div style={isDiamond ? { transform: 'rotate(-45deg)' } : {}}>
295
292
  {icon && <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>{resolveIcon(icon, 20)}</div>}
296
293
  <div style={{ textAlign: 'center', wordBreak: 'break-word' }}>
297
- {displayLabel}
294
+ {displayName}
298
295
  </div>
299
296
  {state && (
300
297
  <div style={{
@@ -28,7 +28,7 @@ const sampleCanvas: ExtendedCanvas = {
28
28
  height: 70,
29
29
  text: 'Source',
30
30
  color: '#4A90E2',
31
- vv: {
31
+ pv: {
32
32
  nodeType: 'process',
33
33
  shape: 'rectangle',
34
34
  icon: 'Settings',
@@ -49,7 +49,7 @@ const sampleCanvas: ExtendedCanvas = {
49
49
  height: 70,
50
50
  text: 'Processor',
51
51
  color: '#4A90E2',
52
- vv: {
52
+ pv: {
53
53
  nodeType: 'process',
54
54
  shape: 'rectangle',
55
55
  icon: 'Settings',
@@ -70,7 +70,7 @@ const sampleCanvas: ExtendedCanvas = {
70
70
  height: 100,
71
71
  text: 'Storage',
72
72
  color: '#7B68EE',
73
- vv: {
73
+ pv: {
74
74
  nodeType: 'data',
75
75
  shape: 'circle',
76
76
  icon: 'Database',
@@ -82,16 +82,16 @@ const sampleCanvas: ExtendedCanvas = {
82
82
  id: 'edge-1',
83
83
  fromNode: 'node-1',
84
84
  toNode: 'node-2',
85
- vv: { edgeType: 'dataflow' },
85
+ pv: { edgeType: 'dataflow' },
86
86
  },
87
87
  {
88
88
  id: 'edge-2',
89
89
  fromNode: 'node-2',
90
90
  toNode: 'node-3',
91
- vv: { edgeType: 'dataflow' },
91
+ pv: { edgeType: 'dataflow' },
92
92
  },
93
93
  ],
94
- vv: {
94
+ pv: {
95
95
  version: '1.0.0',
96
96
  name: 'Event-Driven Animation Demo',
97
97
  description: 'Demonstrates event-driven animations',