@principal-ai/principal-view-react 0.6.7 → 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 && (
@@ -62,7 +62,9 @@ export const CustomEdge: React.FC<EdgeProps<any>> = ({
62
62
  return null;
63
63
  }
64
64
 
65
- const color = hasViolations ? '#D0021B' : (typeDefinition.color || '#888');
65
+ // Color priority: violations > edge data color > type definition color > default
66
+ const edgeColor = edgeData?.color as string | undefined;
67
+ const color = hasViolations ? '#D0021B' : (edgeColor || typeDefinition.color || '#888');
66
68
  const width = typeDefinition.width || 2;
67
69
 
68
70
  // Get Bezier path
@@ -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;
@@ -48,7 +48,9 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected }) => {
48
48
  // Priority: state color > node data color > type definition color > default
49
49
  const nodeDataColor = nodeData.color as string | undefined;
50
50
  const baseColor = nodeDataColor || typeDefinition.color || '#888';
51
- const stateColor = state && typeDefinition.states?.[state]?.color;
51
+ // Check node's own states first (from pv.states), then fall back to type definition states
52
+ const nodeDataStates = nodeData.states as Record<string, { color?: string; label?: string; icon?: string }> | undefined;
53
+ const stateColor = state && (nodeDataStates?.[state]?.color || typeDefinition.states?.[state]?.color);
52
54
  const fillColor = stateColor || baseColor;
53
55
 
54
56
  // Get stroke color - priority: node data stroke > type definition stroke > fill color
@@ -58,15 +60,12 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected }) => {
58
60
  // Use fillColor as the primary "color" for backwards compatibility
59
61
  const color = fillColor;
60
62
 
61
- // Get label from data schema
62
- const labelField = Object.entries(typeDefinition.dataSchema).find(
63
- ([, schema]) => schema.displayInLabel
64
- )?.[0];
65
- 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;
66
65
 
67
- // Icon priority: node data override > state icon > type definition icon
66
+ // Icon priority: node data override > state icon (node data states first) > type definition icon
68
67
  const icon = (nodeData.icon as string)
69
- || (state && typeDefinition.states?.[state]?.icon)
68
+ || (state && (nodeDataStates?.[state]?.icon || typeDefinition.states?.[state]?.icon))
70
69
  || typeDefinition.icon;
71
70
 
72
71
  // Get animation class based on type
@@ -261,7 +260,7 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected }) => {
261
260
  <div style={hexagonInnerStyle}>
262
261
  {icon && <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>{resolveIcon(icon, 20)}</div>}
263
262
  <div style={{ textAlign: 'center', wordBreak: 'break-word' }}>
264
- {displayLabel}
263
+ {displayName}
265
264
  </div>
266
265
  {state && (
267
266
  <div style={{
@@ -272,7 +271,7 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected }) => {
272
271
  borderRadius: '4px',
273
272
  textAlign: 'center',
274
273
  }}>
275
- {typeDefinition.states?.[state]?.label || state}
274
+ {nodeDataStates?.[state]?.label || typeDefinition.states?.[state]?.label || state}
276
275
  </div>
277
276
  )}
278
277
  {hasViolations && (
@@ -292,7 +291,7 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected }) => {
292
291
  <div style={isDiamond ? { transform: 'rotate(-45deg)' } : {}}>
293
292
  {icon && <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>{resolveIcon(icon, 20)}</div>}
294
293
  <div style={{ textAlign: 'center', wordBreak: 'break-word' }}>
295
- {displayLabel}
294
+ {displayName}
296
295
  </div>
297
296
  {state && (
298
297
  <div style={{
@@ -303,7 +302,7 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected }) => {
303
302
  borderRadius: '4px',
304
303
  textAlign: 'center',
305
304
  }}>
306
- {typeDefinition.states?.[state]?.label || state}
305
+ {nodeDataStates?.[state]?.label || typeDefinition.states?.[state]?.label || state}
307
306
  </div>
308
307
  )}
309
308
  {hasViolations && (