@flowuent-org/diagramming-core 1.2.1 → 1.2.3

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.
Files changed (21) hide show
  1. package/apps/diagramming/src/AutomationDiagramData.ts +282 -1
  2. package/package.json +1 -1
  3. package/packages/diagrams/src/lib/components/CanvasSearchBar.tsx +281 -0
  4. package/packages/diagrams/src/lib/components/automation/AutomationApiNode.tsx +796 -788
  5. package/packages/diagrams/src/lib/components/automation/AutomationEndNode.tsx +608 -600
  6. package/packages/diagrams/src/lib/components/automation/AutomationFormattingNode.tsx +833 -825
  7. package/packages/diagrams/src/lib/components/automation/AutomationGoogleServicesNode.tsx +1021 -0
  8. package/packages/diagrams/src/lib/components/automation/AutomationNavigationNode.tsx +584 -0
  9. package/packages/diagrams/src/lib/components/automation/AutomationNoteNode.tsx +422 -414
  10. package/packages/diagrams/src/lib/components/automation/AutomationSheetsNode.tsx +1120 -1112
  11. package/packages/diagrams/src/lib/components/automation/AutomationStartNode.tsx +511 -503
  12. package/packages/diagrams/src/lib/components/automation/NodeAIAssistantPopup.tsx +504 -0
  13. package/packages/diagrams/src/lib/components/automation/NodeActionButtons.tsx +146 -145
  14. package/packages/diagrams/src/lib/components/automation/index.ts +29 -12
  15. package/packages/diagrams/src/lib/contexts/SearchContext.tsx +78 -0
  16. package/packages/diagrams/src/lib/templates/DiagramContainer.tsx +79 -76
  17. package/packages/diagrams/src/lib/templates/DiagramContent.tsx +276 -61
  18. package/packages/diagrams/src/lib/types/automation-node-data-types.ts +86 -2
  19. package/packages/diagrams/src/lib/types/node-types.ts +10 -0
  20. package/packages/diagrams/src/lib/utils/highlightText.tsx +93 -0
  21. 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',
@@ -480,6 +525,163 @@ export const automationDefaultNodes = [
480
525
  height: 150,
481
526
  measured: { width: 336, height: 150 },
482
527
  },
528
+ // =====================================
529
+ // Google Services Nodes
530
+ // =====================================
531
+ {
532
+ id: 'google-docs-node',
533
+ type: 'AutomationGoogleDocsNode',
534
+ position: { x: 100, y: 450 },
535
+ data: {
536
+ label: 'Create Report',
537
+ description: 'Google Docs document එකක් create කරන්න',
538
+ serviceType: 'docs',
539
+ status: 'idle',
540
+ parameters: {
541
+ documentTitle: 'Automation Report',
542
+ operation: 'create',
543
+ content: 'Report content will be generated here...',
544
+ },
545
+ googleAuth: {
546
+ clientId: '', // Will be set from environment
547
+ scopes: [
548
+ 'https://www.googleapis.com/auth/documents',
549
+ 'https://www.googleapis.com/auth/drive',
550
+ ],
551
+ },
552
+ formData: {
553
+ nodeId: 'google-docs-node',
554
+ title: 'Create Report',
555
+ type: 'navigation',
556
+ serviceType: 'docs',
557
+ },
558
+ },
559
+ width: 300,
560
+ height: 200,
561
+ measured: { width: 300, height: 200 },
562
+ },
563
+ {
564
+ id: 'gmail-node',
565
+ type: 'AutomationGmailNode',
566
+ position: { x: 450, y: 450 },
567
+ data: {
568
+ label: 'Send Email',
569
+ description: 'Gmail හරහා email එකක් send කරන්න',
570
+ serviceType: 'gmail',
571
+ status: 'idle',
572
+ parameters: {
573
+ to: ['recipient@example.com'],
574
+ subject: 'Automation Report',
575
+ body: 'Please find the attached report...',
576
+ },
577
+ googleAuth: {
578
+ clientId: '',
579
+ scopes: [
580
+ 'https://www.googleapis.com/auth/gmail.send',
581
+ 'https://www.googleapis.com/auth/gmail.compose',
582
+ ],
583
+ },
584
+ formData: {
585
+ nodeId: 'gmail-node',
586
+ title: 'Send Email',
587
+ type: 'navigation',
588
+ serviceType: 'gmail',
589
+ },
590
+ },
591
+ width: 300,
592
+ height: 200,
593
+ measured: { width: 300, height: 200 },
594
+ },
595
+ {
596
+ id: 'google-meet-node',
597
+ type: 'AutomationGoogleMeetNode',
598
+ position: { x: 800, y: 450 },
599
+ data: {
600
+ label: 'Create Meeting',
601
+ description: 'Google Meet link එකක් create කරන්න',
602
+ serviceType: 'meet',
603
+ status: 'idle',
604
+ parameters: {
605
+ meetingTitle: 'Project Discussion',
606
+ attendees: ['team@example.com'],
607
+ },
608
+ googleAuth: {
609
+ clientId: '',
610
+ scopes: ['https://www.googleapis.com/auth/meetings.space.created'],
611
+ },
612
+ formData: {
613
+ nodeId: 'google-meet-node',
614
+ title: 'Create Meeting',
615
+ type: 'navigation',
616
+ serviceType: 'meet',
617
+ },
618
+ },
619
+ width: 300,
620
+ height: 200,
621
+ measured: { width: 300, height: 200 },
622
+ },
623
+ {
624
+ id: 'google-slides-node',
625
+ type: 'AutomationGoogleSlidesNode',
626
+ position: { x: 100, y: 700 },
627
+ data: {
628
+ label: 'Create Presentation',
629
+ description: 'Google Slides presentation එකක් create කරන්න',
630
+ serviceType: 'slides',
631
+ status: 'idle',
632
+ parameters: {
633
+ presentationTitle: 'Monthly Report',
634
+ slideContent: 'Slide content here...',
635
+ },
636
+ googleAuth: {
637
+ clientId: '',
638
+ scopes: [
639
+ 'https://www.googleapis.com/auth/presentations',
640
+ 'https://www.googleapis.com/auth/drive',
641
+ ],
642
+ },
643
+ formData: {
644
+ nodeId: 'google-slides-node',
645
+ title: 'Create Presentation',
646
+ type: 'navigation',
647
+ serviceType: 'slides',
648
+ },
649
+ },
650
+ width: 300,
651
+ height: 200,
652
+ measured: { width: 300, height: 200 },
653
+ },
654
+ {
655
+ id: 'google-calendar-node',
656
+ type: 'AutomationGoogleCalendarNode',
657
+ position: { x: 450, y: 700 },
658
+ data: {
659
+ label: 'Create Event',
660
+ description: 'Google Calendar event එකක් create කරන්න',
661
+ serviceType: 'calendar',
662
+ status: 'idle',
663
+ parameters: {
664
+ calendarId: 'primary',
665
+ eventTitle: 'Team Meeting',
666
+ eventDescription: 'Weekly sync up',
667
+ startTime: new Date().toISOString(),
668
+ endTime: new Date(Date.now() + 3600000).toISOString(),
669
+ },
670
+ googleAuth: {
671
+ clientId: '',
672
+ scopes: ['https://www.googleapis.com/auth/calendar'],
673
+ },
674
+ formData: {
675
+ nodeId: 'google-calendar-node',
676
+ title: 'Create Event',
677
+ type: 'navigation',
678
+ serviceType: 'calendar',
679
+ },
680
+ },
681
+ width: 300,
682
+ height: 200,
683
+ measured: { width: 300, height: 200 },
684
+ },
483
685
  ];
484
686
 
485
687
  // Default edges for the automation diagram
@@ -504,8 +706,27 @@ export const automationDefaultEdges = [
504
706
  },
505
707
  },
506
708
  {
507
- id: 'edge-api-to-formatting',
709
+ id: 'edge-api-to-navigation',
508
710
  source: 'api-call-node',
711
+ target: 'navigation-node',
712
+ sourceHandle: 'right',
713
+ targetHandle: 'left',
714
+ data: {
715
+ label: '',
716
+ type: 'flow',
717
+ },
718
+ style: {
719
+ stroke: '#ffffff',
720
+ strokeWidth: 2,
721
+ },
722
+ markerEnd: {
723
+ type: MarkerType.ArrowClosed,
724
+ color: '#ffffff',
725
+ },
726
+ },
727
+ {
728
+ id: 'edge-navigation-to-formatting',
729
+ source: 'navigation-node',
509
730
  target: 'data-formatting-node',
510
731
  sourceHandle: 'right',
511
732
  targetHandle: 'left',
@@ -560,4 +781,64 @@ export const automationDefaultEdges = [
560
781
  color: '#ffffff',
561
782
  },
562
783
  },
784
+ // =====================================
785
+ // Google Services Node Edges
786
+ // =====================================
787
+ {
788
+ id: 'edge-docs-to-gmail',
789
+ source: 'google-docs-node',
790
+ target: 'gmail-node',
791
+ sourceHandle: 'right',
792
+ targetHandle: 'left',
793
+ data: {
794
+ label: 'Send Report',
795
+ type: 'flow',
796
+ },
797
+ style: {
798
+ stroke: '#4285F4',
799
+ strokeWidth: 2,
800
+ },
801
+ markerEnd: {
802
+ type: MarkerType.ArrowClosed,
803
+ color: '#4285F4',
804
+ },
805
+ },
806
+ {
807
+ id: 'edge-gmail-to-meet',
808
+ source: 'gmail-node',
809
+ target: 'google-meet-node',
810
+ sourceHandle: 'right',
811
+ targetHandle: 'left',
812
+ data: {
813
+ label: 'Schedule Meet',
814
+ type: 'flow',
815
+ },
816
+ style: {
817
+ stroke: '#EA4335',
818
+ strokeWidth: 2,
819
+ },
820
+ markerEnd: {
821
+ type: MarkerType.ArrowClosed,
822
+ color: '#EA4335',
823
+ },
824
+ },
825
+ {
826
+ id: 'edge-slides-to-calendar',
827
+ source: 'google-slides-node',
828
+ target: 'google-calendar-node',
829
+ sourceHandle: 'right',
830
+ targetHandle: 'left',
831
+ data: {
832
+ label: 'Add to Calendar',
833
+ type: 'flow',
834
+ },
835
+ style: {
836
+ stroke: '#FBBC04',
837
+ strokeWidth: 2,
838
+ },
839
+ markerEnd: {
840
+ type: MarkerType.ArrowClosed,
841
+ color: '#FBBC04',
842
+ },
843
+ },
563
844
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowuent-org/diagramming-core",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -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
+