@flowuent-org/diagramming-core 1.2.1 → 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.
- package/apps/diagramming/src/AutomationDiagramData.ts +65 -1
- package/package.json +1 -1
- package/packages/diagrams/src/lib/components/CanvasSearchBar.tsx +281 -0
- package/packages/diagrams/src/lib/components/automation/AutomationApiNode.tsx +796 -788
- package/packages/diagrams/src/lib/components/automation/AutomationEndNode.tsx +608 -600
- package/packages/diagrams/src/lib/components/automation/AutomationFormattingNode.tsx +833 -825
- package/packages/diagrams/src/lib/components/automation/AutomationNavigationNode.tsx +584 -0
- package/packages/diagrams/src/lib/components/automation/AutomationNoteNode.tsx +422 -414
- package/packages/diagrams/src/lib/components/automation/AutomationSheetsNode.tsx +1120 -1112
- package/packages/diagrams/src/lib/components/automation/AutomationStartNode.tsx +511 -503
- package/packages/diagrams/src/lib/components/automation/NodeAIAssistantPopup.tsx +504 -0
- package/packages/diagrams/src/lib/components/automation/NodeActionButtons.tsx +146 -145
- package/packages/diagrams/src/lib/components/automation/index.ts +21 -12
- package/packages/diagrams/src/lib/contexts/SearchContext.tsx +78 -0
- package/packages/diagrams/src/lib/templates/DiagramContainer.tsx +79 -76
- package/packages/diagrams/src/lib/templates/DiagramContent.tsx +276 -61
- package/packages/diagrams/src/lib/types/automation-node-data-types.ts +22 -2
- package/packages/diagrams/src/lib/types/node-types.ts +2 -0
- package/packages/diagrams/src/lib/utils/highlightText.tsx +93 -0
- package/packages/diagrams/src/lib/utils/nodeAIAssistantConfig.ts +54 -0
|
@@ -149,6 +149,51 @@ export const automationDefaultNodes = [
|
|
|
149
149
|
height: 150,
|
|
150
150
|
measured: { width: 336, height: 150 },
|
|
151
151
|
},
|
|
152
|
+
{
|
|
153
|
+
id: 'navigation-node',
|
|
154
|
+
type: 'AutomationNavigationNode',
|
|
155
|
+
position: { x: 600, y: 200 },
|
|
156
|
+
data: {
|
|
157
|
+
// Display data for the node UI
|
|
158
|
+
label: 'Navigate to Website',
|
|
159
|
+
description: 'Navigate to the target website and wait for page load',
|
|
160
|
+
status: 'Ready',
|
|
161
|
+
navigationType: 'navigate',
|
|
162
|
+
url: 'https://example.com',
|
|
163
|
+
lastRun: 'Never',
|
|
164
|
+
backgroundColor: '#181C25',
|
|
165
|
+
textColor: '#ffffff',
|
|
166
|
+
borderColor: '#1e293b',
|
|
167
|
+
iconName: 'Navigation',
|
|
168
|
+
// Form data for configuration
|
|
169
|
+
formData: {
|
|
170
|
+
nodeId: 'navigation-node',
|
|
171
|
+
title: 'Navigate to Website',
|
|
172
|
+
type: 'navigation',
|
|
173
|
+
navigationType: 'navigate',
|
|
174
|
+
url: 'https://example.com',
|
|
175
|
+
timeout: 30000,
|
|
176
|
+
retryCount: 3,
|
|
177
|
+
outputVariable: 'navigationResult',
|
|
178
|
+
errorHandling: {
|
|
179
|
+
onError: 'retry',
|
|
180
|
+
maxRetries: 3,
|
|
181
|
+
fallbackAction: 'skip',
|
|
182
|
+
},
|
|
183
|
+
isPinned: false,
|
|
184
|
+
isBlock: false,
|
|
185
|
+
blocks: [],
|
|
186
|
+
parallelChildrenCount: 0,
|
|
187
|
+
conditions: {
|
|
188
|
+
combinator: 'and',
|
|
189
|
+
rules: [],
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
width: 336,
|
|
194
|
+
height: 150,
|
|
195
|
+
measured: { width: 336, height: 150 },
|
|
196
|
+
},
|
|
152
197
|
{
|
|
153
198
|
id: 'ai-suggestion-node',
|
|
154
199
|
type: 'AutomationAISuggestionNode',
|
|
@@ -504,8 +549,27 @@ export const automationDefaultEdges = [
|
|
|
504
549
|
},
|
|
505
550
|
},
|
|
506
551
|
{
|
|
507
|
-
id: 'edge-api-to-
|
|
552
|
+
id: 'edge-api-to-navigation',
|
|
508
553
|
source: 'api-call-node',
|
|
554
|
+
target: 'navigation-node',
|
|
555
|
+
sourceHandle: 'right',
|
|
556
|
+
targetHandle: 'left',
|
|
557
|
+
data: {
|
|
558
|
+
label: '',
|
|
559
|
+
type: 'flow',
|
|
560
|
+
},
|
|
561
|
+
style: {
|
|
562
|
+
stroke: '#ffffff',
|
|
563
|
+
strokeWidth: 2,
|
|
564
|
+
},
|
|
565
|
+
markerEnd: {
|
|
566
|
+
type: MarkerType.ArrowClosed,
|
|
567
|
+
color: '#ffffff',
|
|
568
|
+
},
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
id: 'edge-navigation-to-formatting',
|
|
572
|
+
source: 'navigation-node',
|
|
509
573
|
target: 'data-formatting-node',
|
|
510
574
|
sourceHandle: 'right',
|
|
511
575
|
targetHandle: 'left',
|
package/package.json
CHANGED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
|
2
|
+
import { Box, TextField, IconButton, Typography, Paper } from '@mui/material';
|
|
3
|
+
import { RiCloseLine, RiArrowUpLine, RiArrowDownLine } from 'react-icons/ri';
|
|
4
|
+
import { Node } from '@xyflow/react';
|
|
5
|
+
import { useSearch } from '../contexts/SearchContext';
|
|
6
|
+
|
|
7
|
+
interface CanvasSearchBarProps {
|
|
8
|
+
nodes: Node[];
|
|
9
|
+
onClose: () => void;
|
|
10
|
+
onNodeSelect: (nodeId: string) => void;
|
|
11
|
+
onViewportChange: (nodeId: string) => void;
|
|
12
|
+
getViewport: () => { x: number; y: number; zoom: number };
|
|
13
|
+
setViewport: (viewport: { x: number; y: number; zoom: number }) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const CanvasSearchBar: React.FC<CanvasSearchBarProps> = ({
|
|
17
|
+
nodes,
|
|
18
|
+
onClose,
|
|
19
|
+
onNodeSelect,
|
|
20
|
+
onViewportChange,
|
|
21
|
+
getViewport,
|
|
22
|
+
setViewport,
|
|
23
|
+
}) => {
|
|
24
|
+
const { searchQuery, setSearchQuery } = useSearch();
|
|
25
|
+
const [currentMatchIndex, setCurrentMatchIndex] = useState(0);
|
|
26
|
+
const [matchingNodes, setMatchingNodes] = useState<Node[]>([]);
|
|
27
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
28
|
+
|
|
29
|
+
// Focus input when component mounts
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
inputRef.current?.focus();
|
|
32
|
+
}, []);
|
|
33
|
+
|
|
34
|
+
// Search nodes when query changes
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (!searchQuery.trim()) {
|
|
37
|
+
setMatchingNodes([]);
|
|
38
|
+
setCurrentMatchIndex(0);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const searchLower = searchQuery.toLowerCase().trim();
|
|
43
|
+
const matches = nodes.filter((node) => {
|
|
44
|
+
const label = (node.data as any)?.label?.toLowerCase() || '';
|
|
45
|
+
const id = node.id.toLowerCase();
|
|
46
|
+
const description = (node.data as any)?.description?.toLowerCase() || '';
|
|
47
|
+
return (
|
|
48
|
+
label.includes(searchLower) ||
|
|
49
|
+
id.includes(searchLower) ||
|
|
50
|
+
description.includes(searchLower)
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
setMatchingNodes(matches);
|
|
55
|
+
setCurrentMatchIndex(0);
|
|
56
|
+
|
|
57
|
+
// Navigate to first match if any
|
|
58
|
+
if (matches.length > 0) {
|
|
59
|
+
navigateToNode(matches[0].id);
|
|
60
|
+
}
|
|
61
|
+
}, [searchQuery, nodes]);
|
|
62
|
+
|
|
63
|
+
// Navigate to a specific node
|
|
64
|
+
const navigateToNode = useCallback(
|
|
65
|
+
(nodeId: string) => {
|
|
66
|
+
const node = nodes.find((n) => n.id === nodeId);
|
|
67
|
+
if (!node) return;
|
|
68
|
+
|
|
69
|
+
// Center view on the node
|
|
70
|
+
const viewport = getViewport();
|
|
71
|
+
setViewport({
|
|
72
|
+
x: -node.position.x + window.innerWidth / 2 - (node.width || 0) / 2,
|
|
73
|
+
y: -node.position.y + window.innerHeight / 2 - (node.height || 0) / 2,
|
|
74
|
+
zoom: viewport.zoom,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Select the node
|
|
78
|
+
onNodeSelect(nodeId);
|
|
79
|
+
onViewportChange(nodeId);
|
|
80
|
+
},
|
|
81
|
+
[nodes, getViewport, setViewport, onNodeSelect, onViewportChange]
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
// Navigate to next match
|
|
85
|
+
const handleNextMatch = useCallback(() => {
|
|
86
|
+
if (matchingNodes.length === 0) return;
|
|
87
|
+
const nextIndex = (currentMatchIndex + 1) % matchingNodes.length;
|
|
88
|
+
setCurrentMatchIndex(nextIndex);
|
|
89
|
+
navigateToNode(matchingNodes[nextIndex].id);
|
|
90
|
+
}, [matchingNodes, currentMatchIndex, navigateToNode]);
|
|
91
|
+
|
|
92
|
+
// Navigate to previous match
|
|
93
|
+
const handlePreviousMatch = useCallback(() => {
|
|
94
|
+
if (matchingNodes.length === 0) return;
|
|
95
|
+
const prevIndex =
|
|
96
|
+
currentMatchIndex === 0 ? matchingNodes.length - 1 : currentMatchIndex - 1;
|
|
97
|
+
setCurrentMatchIndex(prevIndex);
|
|
98
|
+
navigateToNode(matchingNodes[prevIndex].id);
|
|
99
|
+
}, [matchingNodes, currentMatchIndex, navigateToNode]);
|
|
100
|
+
|
|
101
|
+
// Clear search query when closing
|
|
102
|
+
const handleClose = useCallback(() => {
|
|
103
|
+
setSearchQuery('');
|
|
104
|
+
onClose();
|
|
105
|
+
}, [setSearchQuery, onClose]);
|
|
106
|
+
|
|
107
|
+
// Handle keyboard shortcuts
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
const handleKeyDown = (event: KeyboardEvent) => {
|
|
110
|
+
if (event.key === 'Escape') {
|
|
111
|
+
handleClose();
|
|
112
|
+
} else if (event.key === 'Enter') {
|
|
113
|
+
if (event.shiftKey) {
|
|
114
|
+
handlePreviousMatch();
|
|
115
|
+
} else {
|
|
116
|
+
handleNextMatch();
|
|
117
|
+
}
|
|
118
|
+
} else if (event.key === 'ArrowDown' && event.ctrlKey) {
|
|
119
|
+
event.preventDefault();
|
|
120
|
+
handleNextMatch();
|
|
121
|
+
} else if (event.key === 'ArrowUp' && event.ctrlKey) {
|
|
122
|
+
event.preventDefault();
|
|
123
|
+
handlePreviousMatch();
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
128
|
+
return () => {
|
|
129
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
130
|
+
};
|
|
131
|
+
}, [handleClose, handleNextMatch, handlePreviousMatch]);
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<Paper
|
|
135
|
+
elevation={8}
|
|
136
|
+
sx={{
|
|
137
|
+
position: 'fixed',
|
|
138
|
+
top: 16,
|
|
139
|
+
left: '50%',
|
|
140
|
+
transform: 'translateX(-50%)',
|
|
141
|
+
zIndex: 10000,
|
|
142
|
+
display: 'flex',
|
|
143
|
+
alignItems: 'center',
|
|
144
|
+
gap: 1,
|
|
145
|
+
p: 1,
|
|
146
|
+
bgcolor: '#1F2937',
|
|
147
|
+
border: '1px solid #374151',
|
|
148
|
+
borderRadius: '8px',
|
|
149
|
+
minWidth: '400px',
|
|
150
|
+
boxShadow: '0 4px 20px rgba(0, 0, 0, 0.5)',
|
|
151
|
+
}}
|
|
152
|
+
>
|
|
153
|
+
<TextField
|
|
154
|
+
inputRef={inputRef}
|
|
155
|
+
placeholder="Search nodes..."
|
|
156
|
+
value={searchQuery}
|
|
157
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
158
|
+
size="small"
|
|
159
|
+
autoFocus
|
|
160
|
+
sx={{
|
|
161
|
+
flex: 1,
|
|
162
|
+
'& .MuiOutlinedInput-root': {
|
|
163
|
+
bgcolor: '#111827',
|
|
164
|
+
color: '#fff',
|
|
165
|
+
borderRadius: '6px',
|
|
166
|
+
'& fieldset': {
|
|
167
|
+
borderColor: '#374151',
|
|
168
|
+
},
|
|
169
|
+
'&:hover fieldset': {
|
|
170
|
+
borderColor: '#4B5563',
|
|
171
|
+
},
|
|
172
|
+
'&.Mui-focused fieldset': {
|
|
173
|
+
borderColor: '#3b82f6',
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
'& .MuiInputBase-input': {
|
|
177
|
+
color: '#fff',
|
|
178
|
+
fontSize: '14px',
|
|
179
|
+
'&::placeholder': {
|
|
180
|
+
color: '#9CA3AF',
|
|
181
|
+
opacity: 1,
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
}}
|
|
185
|
+
/>
|
|
186
|
+
|
|
187
|
+
{/* Match count and navigation */}
|
|
188
|
+
{searchQuery.trim() && matchingNodes.length > 0 && (
|
|
189
|
+
<Box
|
|
190
|
+
sx={{
|
|
191
|
+
display: 'flex',
|
|
192
|
+
alignItems: 'center',
|
|
193
|
+
gap: 0.5,
|
|
194
|
+
px: 1,
|
|
195
|
+
borderRight: '1px solid #374151',
|
|
196
|
+
borderLeft: '1px solid #374151',
|
|
197
|
+
}}
|
|
198
|
+
>
|
|
199
|
+
<Typography
|
|
200
|
+
variant="caption"
|
|
201
|
+
sx={{
|
|
202
|
+
color: '#9CA3AF',
|
|
203
|
+
fontSize: '12px',
|
|
204
|
+
minWidth: '50px',
|
|
205
|
+
textAlign: 'center',
|
|
206
|
+
}}
|
|
207
|
+
>
|
|
208
|
+
{currentMatchIndex + 1}/{matchingNodes.length}
|
|
209
|
+
</Typography>
|
|
210
|
+
<IconButton
|
|
211
|
+
size="small"
|
|
212
|
+
onClick={handlePreviousMatch}
|
|
213
|
+
disabled={matchingNodes.length === 0}
|
|
214
|
+
sx={{
|
|
215
|
+
color: '#9CA3AF',
|
|
216
|
+
'&:hover': {
|
|
217
|
+
bgcolor: 'rgba(255, 255, 255, 0.1)',
|
|
218
|
+
color: '#fff',
|
|
219
|
+
},
|
|
220
|
+
'&:disabled': {
|
|
221
|
+
color: '#4B5563',
|
|
222
|
+
},
|
|
223
|
+
}}
|
|
224
|
+
title="Previous match (Shift+Enter or Ctrl+↑)"
|
|
225
|
+
>
|
|
226
|
+
<RiArrowUpLine size={16} />
|
|
227
|
+
</IconButton>
|
|
228
|
+
<IconButton
|
|
229
|
+
size="small"
|
|
230
|
+
onClick={handleNextMatch}
|
|
231
|
+
disabled={matchingNodes.length === 0}
|
|
232
|
+
sx={{
|
|
233
|
+
color: '#9CA3AF',
|
|
234
|
+
'&:hover': {
|
|
235
|
+
bgcolor: 'rgba(255, 255, 255, 0.1)',
|
|
236
|
+
color: '#fff',
|
|
237
|
+
},
|
|
238
|
+
'&:disabled': {
|
|
239
|
+
color: '#4B5563',
|
|
240
|
+
},
|
|
241
|
+
}}
|
|
242
|
+
title="Next match (Enter or Ctrl+↓)"
|
|
243
|
+
>
|
|
244
|
+
<RiArrowDownLine size={16} />
|
|
245
|
+
</IconButton>
|
|
246
|
+
</Box>
|
|
247
|
+
)}
|
|
248
|
+
|
|
249
|
+
{/* No results indicator */}
|
|
250
|
+
{searchQuery.trim() && matchingNodes.length === 0 && (
|
|
251
|
+
<Typography
|
|
252
|
+
variant="caption"
|
|
253
|
+
sx={{
|
|
254
|
+
color: '#EF4444',
|
|
255
|
+
fontSize: '12px',
|
|
256
|
+
px: 1,
|
|
257
|
+
}}
|
|
258
|
+
>
|
|
259
|
+
No matches
|
|
260
|
+
</Typography>
|
|
261
|
+
)}
|
|
262
|
+
|
|
263
|
+
{/* Close button */}
|
|
264
|
+
<IconButton
|
|
265
|
+
size="small"
|
|
266
|
+
onClick={handleClose}
|
|
267
|
+
sx={{
|
|
268
|
+
color: '#9CA3AF',
|
|
269
|
+
'&:hover': {
|
|
270
|
+
bgcolor: 'rgba(255, 255, 255, 0.1)',
|
|
271
|
+
color: '#fff',
|
|
272
|
+
},
|
|
273
|
+
}}
|
|
274
|
+
title="Close (Esc)"
|
|
275
|
+
>
|
|
276
|
+
<RiCloseLine size={18} />
|
|
277
|
+
</IconButton>
|
|
278
|
+
</Paper>
|
|
279
|
+
);
|
|
280
|
+
};
|
|
281
|
+
|