@flowuent-org/diagramming-core 1.2.0 → 1.2.2

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.
@@ -0,0 +1,584 @@
1
+ import React, { useEffect, useState, useRef } from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import { Handle, Position, useNodeId } from '@xyflow/react';
4
+ import { Box, Typography, Chip, IconButton, Card, CardContent } from '@mui/material';
5
+ import {
6
+ AccessTime as AccessTimeIcon,
7
+ Navigation as NavigationIcon,
8
+ OpenInBrowser as OpenInBrowserIcon,
9
+ TouchApp as TouchAppIcon,
10
+ Search as SearchIcon,
11
+ } from '@mui/icons-material';
12
+ import { RiCloseLine } from 'react-icons/ri';
13
+ import ReactJson from 'react-json-view';
14
+ import { getIconByName } from '../../utils/iconMapper';
15
+ import { useTranslation } from 'react-i18next';
16
+ import { useDiagram } from '../../contexts/DiagramProvider';
17
+ import { NodeActionButtons } from './NodeActionButtons';
18
+ import { showNodeAIAssistantPopup } from './NodeAIAssistantPopup';
19
+ import { useSearch } from '../../contexts/SearchContext';
20
+
21
+ interface AutomationNavigationNodeProps {
22
+ data: {
23
+ label: string;
24
+ description: string;
25
+ status: 'Ready' | 'Running' | 'Completed' | 'Error';
26
+ navigationType: 'navigate' | 'click' | 'scroll' | 'wait' | 'extract';
27
+ url?: string;
28
+ selector?: string;
29
+ action?: string;
30
+ waitTime?: number;
31
+ scrollDirection?: 'up' | 'down' | 'to-element';
32
+ extractSelector?: string;
33
+ lastRun: string;
34
+ backgroundColor: string;
35
+ textColor: string;
36
+ borderColor: string;
37
+ iconName?: string;
38
+ formData?: {
39
+ navigationType?: 'navigate' | 'click' | 'scroll' | 'wait' | 'extract';
40
+ url?: string;
41
+ selector?: string;
42
+ action?: string;
43
+ waitTime?: number;
44
+ scrollDirection?: 'up' | 'down' | 'to-element';
45
+ extractSelector?: string;
46
+ outputVariable?: string;
47
+ timeout?: number;
48
+ retryCount?: number;
49
+ [key: string]: any;
50
+ };
51
+ };
52
+ selected?: boolean;
53
+ }
54
+
55
+ export const AutomationNavigationNode: React.FC<AutomationNavigationNodeProps> = ({ data, selected }) => {
56
+ const { t } = useTranslation();
57
+ const { highlightText } = useSearch();
58
+ const [isJsonOpen, setIsJsonOpen] = useState(false);
59
+ const rootRef = useRef<any>(null);
60
+ const portalRef = useRef<HTMLDivElement | null>(null);
61
+ const nodeRef = useRef<HTMLDivElement | null>(null);
62
+ const nodeId = useNodeId();
63
+ const setSelectedNode = useDiagram((state) => state.setSelectedNode);
64
+ const enableJson = useDiagram((state) => state.enableNodeJsonPopover ?? true);
65
+ const onNodesChange = useDiagram((state) => state.onNodesChange);
66
+ const nodes = useDiagram((state) => state.nodes);
67
+ const setNodes = useDiagram((state) => state.setNodes);
68
+
69
+ // Get the icon component based on the iconName
70
+ const IconComponent = getIconByName(data.iconName) || NavigationIcon;
71
+
72
+ const handleJsonClick = () => {
73
+ if (nodeId) setSelectedNode(nodeId);
74
+ if (!enableJson) return;
75
+ setIsJsonOpen(!isJsonOpen);
76
+ };
77
+
78
+ const handleClose = () => {
79
+ setIsJsonOpen(false);
80
+ if (rootRef.current) {
81
+ rootRef.current.unmount();
82
+ rootRef.current = null;
83
+ }
84
+ if (portalRef.current) {
85
+ document.body.removeChild(portalRef.current);
86
+ portalRef.current = null;
87
+ }
88
+ };
89
+
90
+ useEffect(() => {
91
+ const handleClickOutside = (event: MouseEvent) => {
92
+ if (isJsonOpen && !(event.target as Element).closest('#automation-json-popover')) {
93
+ handleClose();
94
+ }
95
+ };
96
+ document.addEventListener('mousedown', handleClickOutside);
97
+ return () => {
98
+ document.removeEventListener('mousedown', handleClickOutside);
99
+ };
100
+ }, [isJsonOpen]);
101
+
102
+ useEffect(() => {
103
+ if (nodeRef.current) {
104
+ const rect = nodeRef.current.getBoundingClientRect();
105
+ const computedStyle = window.getComputedStyle(nodeRef.current);
106
+ }
107
+ }, [data.label]);
108
+
109
+ useEffect(() => {
110
+ if (isJsonOpen) {
111
+ const portalRoot = document.createElement('div');
112
+ document.body.appendChild(portalRoot);
113
+ portalRef.current = portalRoot;
114
+
115
+ const root = createRoot(portalRoot);
116
+ rootRef.current = root;
117
+
118
+ root.render(
119
+ <Card
120
+ id="automation-json-popover"
121
+ sx={{
122
+ position: 'fixed',
123
+ top: 0,
124
+ right: 0,
125
+ zIndex: 9999,
126
+ width: '400px',
127
+ height: '100vh',
128
+ overflow: 'auto',
129
+ bgcolor: '#242424',
130
+ color: '#fff',
131
+ border: '1px solid #333',
132
+ '&::-webkit-scrollbar': {
133
+ width: '6px',
134
+ },
135
+ '&::-webkit-scrollbar-track': {
136
+ background: 'transparent',
137
+ },
138
+ '&::-webkit-scrollbar-thumb': {
139
+ background: '#444',
140
+ borderRadius: '3px',
141
+ '&:hover': {
142
+ background: '#666',
143
+ },
144
+ },
145
+ }}
146
+ >
147
+ <CardContent sx={{ bgcolor: '#242424', color: '#fff' }}>
148
+ <IconButton
149
+ aria-label="close"
150
+ onClick={handleClose}
151
+ sx={{
152
+ color: '#999',
153
+ '&:hover': {
154
+ color: '#fff',
155
+ bgcolor: 'rgba(255, 255, 255, 0.1)',
156
+ },
157
+ }}
158
+ >
159
+ <RiCloseLine />
160
+ </IconButton>
161
+ {data.formData?.executionResult && (
162
+ <Box sx={{ mb: 2 }}>
163
+ <Typography variant="h6" sx={{ color: '#fff', mb: 1 }}>
164
+ {t('automation.common.executionResult')}
165
+ </Typography>
166
+ <Box sx={{
167
+ bgcolor: data.formData.executionResult.success ? '#1e3a8a' : '#dc2626',
168
+ p: 1,
169
+ borderRadius: 1,
170
+ mb: 1
171
+ }}>
172
+ <Typography variant="body2" sx={{ color: '#fff' }}>
173
+ {t('automation.common.status')}: {data.formData.executionResult.success ? t('automation.common.success') : t('automation.common.failed')}
174
+ </Typography>
175
+ <Typography variant="body2" sx={{ color: '#fff' }}>
176
+ {t('automation.common.timestamp')}: {new Date(data.formData.executionResult.timestamp).toLocaleString()}
177
+ </Typography>
178
+ {data.formData.executionResult.error && (
179
+ <Typography variant="body2" sx={{ color: '#fff' }}>
180
+ {t('automation.common.error')}: {data.formData.executionResult.error}
181
+ </Typography>
182
+ )}
183
+ </Box>
184
+ </Box>
185
+ )}
186
+ <Typography variant="h6" sx={{ color: '#fff', mb: 1 }}>
187
+ {t('automation.common.fullNodeData')}
188
+ </Typography>
189
+ <ReactJson theme={'monokai'} src={data.formData || data} collapsed={false} />
190
+ </CardContent>
191
+ </Card>
192
+ );
193
+ } else {
194
+ if (rootRef.current) {
195
+ rootRef.current.unmount();
196
+ rootRef.current = null;
197
+ }
198
+ if (portalRef.current) {
199
+ document.body.removeChild(portalRef.current);
200
+ portalRef.current = null;
201
+ }
202
+ }
203
+ }, [isJsonOpen, data]);
204
+
205
+ const getNavigationIcon = () => {
206
+ const navType = data.formData?.navigationType || data.navigationType || 'navigate';
207
+ switch (navType) {
208
+ case 'navigate':
209
+ return <OpenInBrowserIcon sx={{ fontSize: '18px' }} />;
210
+ case 'click':
211
+ return <TouchAppIcon sx={{ fontSize: '18px' }} />;
212
+ case 'scroll':
213
+ return <NavigationIcon sx={{ fontSize: '18px' }} />;
214
+ case 'wait':
215
+ return <AccessTimeIcon sx={{ fontSize: '18px' }} />;
216
+ case 'extract':
217
+ return <SearchIcon sx={{ fontSize: '18px' }} />;
218
+ default:
219
+ return <NavigationIcon sx={{ fontSize: '18px' }} />;
220
+ }
221
+ };
222
+
223
+ const getNavigationTypeLabel = () => {
224
+ const navType = data.formData?.navigationType || data.navigationType || 'navigate';
225
+ switch (navType) {
226
+ case 'navigate':
227
+ return 'Navigate to URL';
228
+ case 'click':
229
+ return 'Click Element';
230
+ case 'scroll':
231
+ return 'Scroll Page';
232
+ case 'wait':
233
+ return 'Wait';
234
+ case 'extract':
235
+ return 'Extract Data';
236
+ default:
237
+ return 'Navigation';
238
+ }
239
+ };
240
+
241
+ const getNavigationDetails = () => {
242
+ const navType = data.formData?.navigationType || data.navigationType || 'navigate';
243
+ const formData = data.formData || {};
244
+
245
+ switch (navType) {
246
+ case 'navigate':
247
+ return formData.url || data.url || 'No URL specified';
248
+ case 'click':
249
+ return formData.selector || data.selector || 'No selector specified';
250
+ case 'scroll':
251
+ return formData.scrollDirection || data.scrollDirection || 'down';
252
+ case 'wait':
253
+ return `${formData.waitTime || data.waitTime || 1000}ms`;
254
+ case 'extract':
255
+ return formData.extractSelector || data.extractSelector || 'No selector specified';
256
+ default:
257
+ return 'No details';
258
+ }
259
+ };
260
+
261
+ return (
262
+ <Box
263
+ sx={{
264
+ position: 'relative',
265
+ width: '336px',
266
+ overflow: 'visible',
267
+ }}
268
+ >
269
+ <Box
270
+ ref={nodeRef}
271
+ sx={{
272
+ width: '336px',
273
+ minHeight: '150px',
274
+ backgroundColor: '#181C25',
275
+ border: selected ? '2px solid #3b82f6' : '1px solid #1e293b',
276
+ borderRadius: '12px',
277
+ color: '#ffffff',
278
+ position: 'relative',
279
+ boxShadow: selected ? '0 0 0 2px rgba(59, 130, 246, 0.5)' : '0 4px 8px rgba(0, 0, 0, 0.3)',
280
+ transition: 'all 0.2s ease',
281
+ cursor: 'pointer',
282
+ overflow: 'hidden',
283
+ ...(data.status === 'Running' && {
284
+ animation: 'pulse-glow 2s ease-in-out infinite',
285
+ '@keyframes pulse-glow': {
286
+ '0%, 100%': {
287
+ boxShadow: '0 0 20px rgba(59, 130, 246, 0.4), 0 0 40px rgba(59, 130, 246, 0.2)',
288
+ borderColor: 'rgba(59, 130, 246, 0.6)',
289
+ },
290
+ '50%': {
291
+ boxShadow: '0 0 30px rgba(59, 130, 246, 0.6), 0 0 60px rgba(59, 130, 246, 0.3)',
292
+ borderColor: 'rgba(59, 130, 246, 0.9)',
293
+ },
294
+ },
295
+ }),
296
+ }}
297
+ onClick={handleJsonClick}
298
+ >
299
+ {/* Top Header Section */}
300
+ <Box sx={{
301
+ backgroundColor: "rgba(67, 93, 132, 0.1)",
302
+ padding: '8px 16px',
303
+ borderRadius: '12px 12px 0 0'
304
+ }}>
305
+ <Typography variant="body2" sx={{
306
+ color: '#ffffff',
307
+ fontSize: '12px',
308
+ fontWeight: 500
309
+ }}>
310
+ {data.formData?.description || data.description || 'Web navigation action'}
311
+ </Typography>
312
+ </Box>
313
+
314
+ {/* Main Content */}
315
+ <Box sx={{ padding: '16px' }}>
316
+ {/* Title Section */}
317
+ <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
318
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
319
+ <Box
320
+ sx={{
321
+ width: '32px',
322
+ height: '32px',
323
+ backgroundColor: '#8b5cf6',
324
+ borderRadius: '50%',
325
+ display: 'flex',
326
+ alignItems: 'center',
327
+ justifyContent: 'center',
328
+ }}
329
+ >
330
+ {getNavigationIcon()}
331
+ </Box>
332
+ <Typography variant="h6" sx={{ fontWeight: 600, fontSize: '16px' }}>
333
+ {highlightText(data.label)}
334
+ </Typography>
335
+ </Box>
336
+ <Chip
337
+ label={data.status || 'Ready'}
338
+ size="small"
339
+ sx={{
340
+ backgroundColor: data.status === 'Completed'
341
+ ? 'rgba(37, 99, 235, 0.1)'
342
+ : data.status === 'Running'
343
+ ? 'rgba(251, 191, 36, 0.1)'
344
+ : data.status === 'Error'
345
+ ? 'rgba(239, 68, 68, 0.1)'
346
+ : 'rgba(16, 185, 129, 0.1)',
347
+ color: data.status === 'Completed'
348
+ ? '#93C5FD'
349
+ : data.status === 'Running'
350
+ ? '#FCD34D'
351
+ : data.status === 'Error'
352
+ ? '#FCA5A5'
353
+ : '#86EFAC',
354
+ fontWeight: 500,
355
+ fontSize: '12px',
356
+ height: '24px',
357
+ borderRadius: '12px',
358
+ }}
359
+ />
360
+ </Box>
361
+
362
+ {/* Navigation Type */}
363
+ <Box sx={{
364
+ backgroundColor: '#1F2937',
365
+ borderRadius: '8px',
366
+ padding: '12px',
367
+ mb: 2,
368
+ border: '1px solid #374151'
369
+ }}>
370
+ <Typography variant="body2" sx={{
371
+ color: '#9CA3AF',
372
+ fontSize: '11px',
373
+ mb: 0.5
374
+ }}>
375
+ {getNavigationTypeLabel()}
376
+ </Typography>
377
+ <Typography variant="body2" sx={{
378
+ color: '#ffffff',
379
+ fontSize: '12px',
380
+ fontWeight: 500
381
+ }}>
382
+ {getNavigationDetails()}
383
+ </Typography>
384
+ </Box>
385
+
386
+ {/* Description Box */}
387
+ <Box sx={{
388
+ backgroundColor: '#1F2937',
389
+ borderRadius: '8px',
390
+ padding: '12px',
391
+ mb: 2,
392
+ border: '1px solid #374151'
393
+ }}>
394
+ <Box sx={{
395
+ backgroundColor: 'transparent',
396
+ borderRadius: '4px',
397
+ padding: '8px',
398
+ border: '1px solid #4B5563',
399
+ minHeight: '40px',
400
+ display: 'flex',
401
+ alignItems: 'center'
402
+ }}>
403
+ <Typography variant="body2" sx={{
404
+ color: '#9CA3AF',
405
+ fontSize: '12px',
406
+ lineHeight: 1.4,
407
+ margin: 0
408
+ }}>
409
+ {highlightText(data.description)}
410
+ </Typography>
411
+ </Box>
412
+ </Box>
413
+
414
+ {/* Last Run Info */}
415
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, mt: 1 }}>
416
+ <AccessTimeIcon sx={{ fontSize: '14px', color: '#9CA3AF' }} />
417
+ <Typography variant="body2" sx={{ color: '#9CA3AF', fontSize: '11px' }}>
418
+ {t('automation.common.lastRan')}: {data.lastRun}
419
+ </Typography>
420
+ </Box>
421
+ </Box>
422
+
423
+ {/* Connection Handles */}
424
+ <Handle
425
+ type="source"
426
+ position={Position.Top}
427
+ id="top-source"
428
+ className="connection-handle"
429
+ style={{
430
+ background: selected ? '#10B981' : '#1a1a2e',
431
+ width: '14px',
432
+ height: '14px',
433
+ border: '3px solid #10B981',
434
+ top: '-8px',
435
+ opacity: selected ? 1 : 0,
436
+ transition: 'all 0.2s ease-in-out',
437
+ cursor: 'crosshair',
438
+ zIndex: 10,
439
+ }}
440
+ />
441
+ <Handle
442
+ type="target"
443
+ position={Position.Top}
444
+ id="top-target"
445
+ style={{
446
+ background: 'transparent',
447
+ width: '14px',
448
+ height: '14px',
449
+ border: 'none',
450
+ top: '-8px',
451
+ opacity: 0,
452
+ pointerEvents: selected ? 'all' : 'none',
453
+ }}
454
+ />
455
+ <Handle
456
+ type="source"
457
+ position={Position.Bottom}
458
+ id="bottom-source"
459
+ className="connection-handle"
460
+ style={{
461
+ background: selected ? '#10B981' : '#1a1a2e',
462
+ width: '14px',
463
+ height: '14px',
464
+ border: '3px solid #10B981',
465
+ bottom: '-8px',
466
+ opacity: selected ? 1 : 0,
467
+ transition: 'all 0.2s ease-in-out',
468
+ cursor: 'crosshair',
469
+ zIndex: 10,
470
+ }}
471
+ />
472
+ <Handle
473
+ type="target"
474
+ position={Position.Bottom}
475
+ id="bottom-target"
476
+ style={{
477
+ background: 'transparent',
478
+ width: '14px',
479
+ height: '14px',
480
+ border: 'none',
481
+ bottom: '-8px',
482
+ opacity: 0,
483
+ pointerEvents: selected ? 'all' : 'none',
484
+ }}
485
+ />
486
+ <Handle
487
+ type="source"
488
+ position={Position.Left}
489
+ id="left-source"
490
+ className="connection-handle"
491
+ style={{
492
+ background: selected ? '#10B981' : '#1a1a2e',
493
+ width: '14px',
494
+ height: '14px',
495
+ border: '3px solid #10B981',
496
+ left: '-8px',
497
+ opacity: selected ? 1 : 0,
498
+ transition: 'all 0.2s ease-in-out',
499
+ cursor: 'crosshair',
500
+ zIndex: 10,
501
+ }}
502
+ />
503
+ <Handle
504
+ type="target"
505
+ position={Position.Left}
506
+ id="left-target"
507
+ style={{
508
+ background: 'transparent',
509
+ width: '14px',
510
+ height: '14px',
511
+ border: 'none',
512
+ left: '-8px',
513
+ opacity: 0,
514
+ pointerEvents: selected ? 'all' : 'none',
515
+ }}
516
+ />
517
+ <Handle
518
+ type="source"
519
+ position={Position.Right}
520
+ id="right-source"
521
+ className="connection-handle"
522
+ style={{
523
+ background: selected ? '#10B981' : '#1a1a2e',
524
+ width: '14px',
525
+ height: '14px',
526
+ border: '3px solid #10B981',
527
+ right: '-8px',
528
+ opacity: selected ? 1 : 0,
529
+ transition: 'all 0.2s ease-in-out',
530
+ cursor: 'crosshair',
531
+ zIndex: 10,
532
+ }}
533
+ />
534
+ <Handle
535
+ type="target"
536
+ position={Position.Right}
537
+ id="right-target"
538
+ style={{
539
+ background: 'transparent',
540
+ width: '14px',
541
+ height: '14px',
542
+ border: 'none',
543
+ right: '-8px',
544
+ opacity: 0,
545
+ pointerEvents: selected ? 'all' : 'none',
546
+ }}
547
+ />
548
+ </Box>
549
+
550
+ {/* Node Action Buttons */}
551
+ <NodeActionButtons
552
+ selected={selected}
553
+ onOpenAIAssistant={(buttonElement) => {
554
+ if (nodeId) {
555
+ showNodeAIAssistantPopup(nodeId, 'Navigation Node', buttonElement);
556
+ }
557
+ }}
558
+ onDelete={() => {
559
+ if (nodeId && onNodesChange) {
560
+ onNodesChange([{ id: nodeId, type: 'remove' }]);
561
+ }
562
+ }}
563
+ onDuplicate={() => {
564
+ if (nodeId) {
565
+ const currentNode = nodes.find(n => n.id === nodeId);
566
+ if (currentNode) {
567
+ const newNode = {
568
+ ...currentNode,
569
+ id: `${currentNode.id}-copy-${Date.now()}`,
570
+ position: {
571
+ x: currentNode.position.x + 50,
572
+ y: currentNode.position.y + 50,
573
+ },
574
+ selected: false,
575
+ };
576
+ setNodes([...nodes, newNode]);
577
+ }
578
+ }
579
+ }}
580
+ />
581
+ </Box>
582
+ );
583
+ };
584
+
@@ -8,6 +8,7 @@ import { AISuggestionsPanel } from './AISuggestionsPanel';
8
8
  import { NodeActionButtons } from './NodeActionButtons';
9
9
  import { showNodeAIAssistantPopup } from './NodeAIAssistantPopup';
10
10
  import { useDiagram } from '../../contexts/DiagramProvider';
11
+ import { useSearch } from '../../contexts/SearchContext';
11
12
 
12
13
  interface AutomationNoteNodeProps {
13
14
  data: {
@@ -29,6 +30,7 @@ interface AutomationNoteNodeProps {
29
30
  export const AutomationNoteNode: React.FC<AutomationNoteNodeProps> = ({ data, selected }) => {
30
31
  const nodeRef = useRef<HTMLDivElement | null>(null);
31
32
  const [showSuggestions, setShowSuggestions] = useState(false);
33
+ const { highlightText } = useSearch();
32
34
  const nodeId = useNodeId();
33
35
  const onNodesChange = useDiagram((state) => state.onNodesChange);
34
36
  const nodes = useDiagram((state) => state.nodes);
@@ -122,7 +124,7 @@ export const AutomationNoteNode: React.FC<AutomationNoteNodeProps> = ({ data, se
122
124
  <IconComponent sx={{ color: 'white', fontSize: '18px' }} />
123
125
  </Box>
124
126
  <Typography variant="h6" sx={{ fontWeight: 600, fontSize: '16px' }}>
125
- {data.label}
127
+ {highlightText(data.label)}
126
128
  </Typography>
127
129
  </Box>
128
130
  </Box>
@@ -30,6 +30,7 @@ import { AISuggestion } from './AISuggestionsModal';
30
30
  import { AISuggestionsPanel } from './AISuggestionsPanel';
31
31
  import { NodeActionButtons } from './NodeActionButtons';
32
32
  import { showNodeAIAssistantPopup } from './NodeAIAssistantPopup';
33
+ import { useSearch } from '../../contexts/SearchContext';
33
34
 
34
35
  interface AutomationSheetsNodeProps {
35
36
  data: {
@@ -104,6 +105,7 @@ interface AutomationSheetsNodeProps {
104
105
 
105
106
  export const AutomationSheetsNode: React.FC<AutomationSheetsNodeProps> = ({ data, selected }) => {
106
107
  const { t } = useTranslation();
108
+ const { highlightText } = useSearch();
107
109
  const [isJsonOpen, setIsJsonOpen] = useState(false);
108
110
  const [showSuggestions, setShowSuggestions] = useState(false);
109
111
  const rootRef = useRef<any>(null);
@@ -766,7 +768,7 @@ Data: ${JSON.stringify(data, null, 2)}
766
768
  <IconComponent sx={{ color: 'white', fontSize: '18px' }} />
767
769
  </Box>
768
770
  <Typography variant="h6" sx={{ fontWeight: 600, fontSize: '16px' }}>
769
- {data.label}
771
+ {highlightText(data.label)}
770
772
  </Typography>
771
773
  </Box>
772
774
  <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
@@ -10,6 +10,7 @@ import { useTranslation } from 'react-i18next';
10
10
  import { useDiagram } from '../../contexts/DiagramProvider';
11
11
  import { NodeActionButtons } from './NodeActionButtons';
12
12
  import { showNodeAIAssistantPopup } from './NodeAIAssistantPopup';
13
+ import { useSearch } from '../../contexts/SearchContext';
13
14
 
14
15
  interface AutomationStartNodeProps {
15
16
  data: {
@@ -34,6 +35,7 @@ interface AutomationStartNodeProps {
34
35
 
35
36
  export const AutomationStartNode: React.FC<AutomationStartNodeProps> = ({ data, selected }) => {
36
37
  const { t } = useTranslation();
38
+ const { highlightText } = useSearch();
37
39
  const [isJsonOpen, setIsJsonOpen] = useState(false);
38
40
  const rootRef = useRef<any>(null);
39
41
  const portalRef = useRef<HTMLDivElement | null>(null);
@@ -247,7 +249,7 @@ export const AutomationStartNode: React.FC<AutomationStartNodeProps> = ({ data,
247
249
  fontSize: '12px',
248
250
  fontWeight: 500
249
251
  }}>
250
- {data.formData?.description || t('automation.startNode.headerDescription')}
252
+ {highlightText(data.formData?.description || t('automation.startNode.headerDescription'))}
251
253
  </Typography>
252
254
  </Box>
253
255
 
@@ -270,7 +272,7 @@ export const AutomationStartNode: React.FC<AutomationStartNodeProps> = ({ data,
270
272
  <IconComponent sx={{ color: 'white', fontSize: '18px' }} />
271
273
  </Box>
272
274
  <Typography variant="h6" sx={{ fontWeight: 600, fontSize: '16px' }}>
273
- {data.label}
275
+ {highlightText(data.label)}
274
276
  </Typography>
275
277
  </Box>
276
278
  <Chip
@@ -4,6 +4,7 @@ export { AutomationFormattingNode } from './AutomationFormattingNode';
4
4
  export { AutomationSheetsNode } from './AutomationSheetsNode';
5
5
  export { AutomationEndNode } from './AutomationEndNode';
6
6
  export { AutomationNoteNode } from './AutomationNoteNode';
7
+ export { AutomationNavigationNode } from './AutomationNavigationNode';
7
8
  export { AutomationExecutionPanel } from './AutomationExecutionPanel';
8
9
  export { AutomationAISuggestionNode } from './AutomationAISuggestionNode';
9
10
  export { AISuggestionsModal, showAISuggestionsModal } from './AISuggestionsModal';