taleslemonade commited on
Commit
03ae5fe
·
verified ·
1 Parent(s): 4a8ee11

undefined - Initial Deployment

Browse files
Files changed (2) hide show
  1. README.md +6 -4
  2. index.html +647 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Drstoneworkui
3
- emoji: 🏃
4
- colorFrom: purple
5
  colorTo: purple
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: drstoneworkui
3
+ emoji: 🐳
4
+ colorFrom: red
5
  colorTo: purple
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,647 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Liquid Glass UI for n8n Workflows</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ .glass-effect {
11
+ background: rgba(255, 255, 255, 0.15);
12
+ backdrop-filter: blur(10px);
13
+ -webkit-backdrop-filter: blur(10px);
14
+ border-radius: 10px;
15
+ border: 1px solid rgba(255, 255, 255, 0.18);
16
+ box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
17
+ }
18
+
19
+ .liquid-animation {
20
+ position: relative;
21
+ overflow: hidden;
22
+ }
23
+
24
+ .liquid-animation::before {
25
+ content: '';
26
+ position: absolute;
27
+ top: -50%;
28
+ left: -50%;
29
+ width: 200%;
30
+ height: 200%;
31
+ background: linear-gradient(
32
+ 45deg,
33
+ transparent,
34
+ rgba(255, 255, 255, 0.3),
35
+ transparent
36
+ );
37
+ transform: rotate(45deg);
38
+ animation: liquidFlow 6s linear infinite;
39
+ }
40
+
41
+ @keyframes liquidFlow {
42
+ 0% {
43
+ transform: rotate(45deg) translate(-30%, -30%);
44
+ }
45
+ 100% {
46
+ transform: rotate(45deg) translate(30%, 30%);
47
+ }
48
+ }
49
+
50
+ .node-item {
51
+ transition: all 0.3s ease;
52
+ }
53
+
54
+ .node-item:hover {
55
+ transform: translateY(-5px);
56
+ box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
57
+ }
58
+
59
+ .json-viewer {
60
+ max-height: 400px;
61
+ overflow-y: auto;
62
+ }
63
+
64
+ /* Custom scrollbar */
65
+ .json-viewer::-webkit-scrollbar {
66
+ width: 8px;
67
+ }
68
+
69
+ .json-viewer::-webkit-scrollbar-track {
70
+ background: rgba(255, 255, 255, 0.1);
71
+ border-radius: 10px;
72
+ }
73
+
74
+ .json-viewer::-webkit-scrollbar-thumb {
75
+ background: rgba(255, 255, 255, 0.3);
76
+ border-radius: 10px;
77
+ }
78
+
79
+ .json-viewer::-webkit-scrollbar-thumb:hover {
80
+ background: rgba(255, 255, 255, 0.5);
81
+ }
82
+ </style>
83
+ </head>
84
+ <body class="min-h-screen bg-gradient-to-br from-indigo-900 via-purple-900 to-pink-800 text-white p-4 md:p-8">
85
+ <div class="max-w-6xl mx-auto">
86
+ <header class="flex flex-col md:flex-row justify-between items-center mb-8 gap-4">
87
+ <div class="glass-effect liquid-animation p-6 w-full md:w-auto">
88
+ <h1 class="text-3xl md:text-4xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-cyan-400 to-blue-500">
89
+ <i class="fas fa-code-branch mr-3"></i>n8n Workflow UI
90
+ </h1>
91
+ <p class="text-sm opacity-80 mt-2">Liquid Glass Interface for Webhook Integration</p>
92
+ </div>
93
+
94
+ <div class="glass-effect p-4 w-full md:w-auto flex items-center gap-4">
95
+ <div class="flex-1">
96
+ <label class="block text-sm font-medium mb-1">Webhook URL</label>
97
+ <div class="flex">
98
+ <input type="text" id="webhookUrl" placeholder="https://your-n8n-instance.com/webhook/path"
99
+ class="bg-black bg-opacity-30 text-white px-4 py-2 rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500 flex-1">
100
+ <button id="testWebhook" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-r-md transition">
101
+ <i class="fas fa-bolt mr-2"></i>Test
102
+ </button>
103
+ </div>
104
+ </div>
105
+ </div>
106
+ </header>
107
+
108
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
109
+ <!-- Workflow Configuration -->
110
+ <div class="glass-effect p-6 lg:col-span-1">
111
+ <h2 class="text-xl font-semibold mb-4 flex items-center">
112
+ <i class="fas fa-cog mr-2 text-blue-400"></i> Workflow Setup
113
+ </h2>
114
+
115
+ <div class="space-y-4">
116
+ <div>
117
+ <label class="block text-sm font-medium mb-1">Workflow Name</label>
118
+ <input type="text" id="workflowName" placeholder="My Awesome Workflow"
119
+ class="w-full bg-black bg-opacity-30 text-white px-4 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
120
+ </div>
121
+
122
+ <div>
123
+ <label class="block text-sm font-medium mb-1">HTTP Method</label>
124
+ <select id="httpMethod" class="w-full bg-black bg-opacity-30 text-white px-4 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
125
+ <option value="POST">POST</option>
126
+ <option value="GET">GET</option>
127
+ <option value="PUT">PUT</option>
128
+ <option value="DELETE">DELETE</option>
129
+ </select>
130
+ </div>
131
+
132
+ <div>
133
+ <label class="block text-sm font-medium mb-1">Authentication</label>
134
+ <select id="authMethod" class="w-full bg-black bg-opacity-30 text-white px-4 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
135
+ <option value="none">None</option>
136
+ <option value="basic">Basic Auth</option>
137
+ <option value="bearer">Bearer Token</option>
138
+ <option value="apiKey">API Key</option>
139
+ </select>
140
+ </div>
141
+
142
+ <div id="authFields" class="hidden space-y-2">
143
+ <input type="text" id="authUsername" placeholder="Username"
144
+ class="w-full bg-black bg-opacity-30 text-white px-4 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
145
+ <input type="password" id="authPassword" placeholder="Password/Token"
146
+ class="w-full bg-black bg-opacity-30 text-white px-4 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
147
+ </div>
148
+
149
+ <button id="saveConfig" class="w-full bg-green-600 hover:bg-green-700 px-4 py-2 rounded-md transition flex items-center justify-center">
150
+ <i class="fas fa-save mr-2"></i> Save Configuration
151
+ </button>
152
+ </div>
153
+
154
+ <div class="mt-6 pt-4 border-t border-white border-opacity-20">
155
+ <h3 class="text-sm font-medium mb-2">Recent Workflows</h3>
156
+ <div id="recentWorkflows" class="space-y-2">
157
+ <!-- Dynamically populated -->
158
+ </div>
159
+ </div>
160
+ </div>
161
+
162
+ <!-- Workflow Visualization -->
163
+ <div class="glass-effect p-6 lg:col-span-2">
164
+ <div class="flex justify-between items-center mb-4">
165
+ <h2 class="text-xl font-semibold flex items-center">
166
+ <i class="fas fa-project-diagram mr-2 text-purple-400"></i> Workflow Visualization
167
+ </h2>
168
+ <div class="flex space-x-2">
169
+ <button id="addNode" class="bg-indigo-600 hover:bg-indigo-700 px-3 py-1 rounded-md text-sm transition">
170
+ <i class="fas fa-plus mr-1"></i> Add Node
171
+ </button>
172
+ <button id="exportWorkflow" class="bg-purple-600 hover:bg-purple-700 px-3 py-1 rounded-md text-sm transition">
173
+ <i class="fas fa-file-export mr-1"></i> Export
174
+ </button>
175
+ </div>
176
+ </div>
177
+
178
+ <div id="workflowCanvas" class="min-h-64 bg-black bg-opacity-20 rounded-md p-4">
179
+ <div class="text-center py-8 text-white text-opacity-50">
180
+ <i class="fas fa-magic text-4xl mb-2"></i>
181
+ <p>Add your first node to start building your workflow</p>
182
+ </div>
183
+ </div>
184
+
185
+ <div class="mt-6">
186
+ <h3 class="text-sm font-medium mb-2 flex items-center">
187
+ <i class="fas fa-cubes mr-2"></i> Available Nodes
188
+ </h3>
189
+ <div id="nodeLibrary" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-3">
190
+ <!-- Dynamically populated -->
191
+ </div>
192
+ </div>
193
+ </div>
194
+ </div>
195
+
196
+ <!-- JSON Viewer Modal -->
197
+ <div id="jsonModal" class="fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center z-50 hidden">
198
+ <div class="glass-effect w-full max-w-3xl max-h-screen overflow-auto m-4 p-6 rounded-xl">
199
+ <div class="flex justify-between items-center mb-4">
200
+ <h3 class="text-xl font-semibold">JSON Viewer</h3>
201
+ <button id="closeModal" class="text-white hover:text-gray-300">
202
+ <i class="fas fa-times"></i>
203
+ </button>
204
+ </div>
205
+ <div id="jsonViewer" class="json-viewer bg-black bg-opacity-30 p-4 rounded-md font-mono text-sm overflow-auto"></div>
206
+ <div class="mt-4 flex justify-end space-x-2">
207
+ <button id="copyJson" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-md transition">
208
+ <i class="fas fa-copy mr-2"></i> Copy JSON
209
+ </button>
210
+ <button id="sendToWebhook" class="bg-green-600 hover:bg-green-700 px-4 py-2 rounded-md transition">
211
+ <i class="fas fa-paper-plane mr-2"></i> Send to Webhook
212
+ </button>
213
+ </div>
214
+ </div>
215
+ </div>
216
+ </div>
217
+
218
+ <footer class="text-center text-xs opacity-70 mt-12 mb-4">
219
+ den by olu dara@stoneweb
220
+ </footer>
221
+
222
+ <script>
223
+ document.addEventListener('DOMContentLoaded', function() {
224
+ // Sample node types for the library
225
+ const nodeTypes = [
226
+ { name: 'Webhook', icon: 'fa-link', color: 'bg-purple-500' },
227
+ { name: 'HTTP Request', icon: 'fa-globe', color: 'bg-blue-500' },
228
+ { name: 'Function', icon: 'fa-code', color: 'bg-yellow-500' },
229
+ { name: 'IF Condition', icon: 'fa-question', color: 'bg-green-500' },
230
+ { name: 'Delay', icon: 'fa-clock', color: 'bg-indigo-500' },
231
+ { name: 'Spreadsheet', icon: 'fa-table', color: 'bg-emerald-500' },
232
+ { name: 'Email', icon: 'fa-envelope', color: 'bg-red-500' },
233
+ { name: 'Telegram', icon: 'fa-paper-plane', color: 'bg-cyan-500' },
234
+ { name: 'Slack', icon: 'fa-slack', color: 'bg-pink-500' },
235
+ { name: 'Discord', icon: 'fa-discord', color: 'bg-indigo-500' },
236
+ { name: 'Google Sheets', icon: 'fa-google', color: 'bg-green-500' },
237
+ { name: 'MySQL', icon: 'fa-database', color: 'bg-orange-500' }
238
+ ];
239
+
240
+ // Sample recent workflows
241
+ const recentWorkflows = [
242
+ { name: 'Customer Onboarding', lastUsed: '2 hours ago' },
243
+ { name: 'API Monitoring', lastUsed: 'Yesterday' },
244
+ { name: 'Data Sync', lastUsed: '3 days ago' }
245
+ ];
246
+
247
+ // Current workflow data
248
+ let workflowData = {
249
+ name: '',
250
+ nodes: [],
251
+ connections: [],
252
+ webhookUrl: '',
253
+ httpMethod: 'POST',
254
+ authMethod: 'none'
255
+ };
256
+
257
+ // DOM elements
258
+ const webhookUrlInput = document.getElementById('webhookUrl');
259
+ const testWebhookBtn = document.getElementById('testWebhook');
260
+ const workflowNameInput = document.getElementById('workflowName');
261
+ const httpMethodSelect = document.getElementById('httpMethod');
262
+ const authMethodSelect = document.getElementById('authMethod');
263
+ const authFields = document.getElementById('authFields');
264
+ const saveConfigBtn = document.getElementById('saveConfig');
265
+ const recentWorkflowsList = document.getElementById('recentWorkflows');
266
+ const addNodeBtn = document.getElementById('addNode');
267
+ const exportWorkflowBtn = document.getElementById('exportWorkflow');
268
+ const workflowCanvas = document.getElementById('workflowCanvas');
269
+ const nodeLibrary = document.getElementById('nodeLibrary');
270
+ const jsonModal = document.getElementById('jsonModal');
271
+ const jsonViewer = document.getElementById('jsonViewer');
272
+ const closeModalBtn = document.getElementById('closeModal');
273
+ const copyJsonBtn = document.getElementById('copyJson');
274
+ const sendToWebhookBtn = document.getElementById('sendToWebhook');
275
+
276
+ // Initialize the UI
277
+ function initUI() {
278
+ // Populate node library
279
+ nodeLibrary.innerHTML = '';
280
+ nodeTypes.forEach(nodeType => {
281
+ const nodeElement = document.createElement('div');
282
+ nodeElement.className = `node-item glass-effect p-3 rounded-md cursor-pointer flex flex-col items-center ${nodeType.color} bg-opacity-20 hover:bg-opacity-30`;
283
+ nodeElement.innerHTML = `
284
+ <i class="fas ${nodeType.icon} text-2xl mb-1 ${nodeType.color}"></i>
285
+ <span class="text-sm">${nodeType.name}</span>
286
+ `;
287
+ nodeElement.addEventListener('click', () => addNodeToCanvas(nodeType));
288
+ nodeLibrary.appendChild(nodeElement);
289
+ });
290
+
291
+ // Populate recent workflows
292
+ recentWorkflowsList.innerHTML = '';
293
+ recentWorkflows.forEach(workflow => {
294
+ const workflowElement = document.createElement('div');
295
+ workflowElement.className = 'glass-effect p-2 rounded-md cursor-pointer hover:bg-opacity-30';
296
+ workflowElement.innerHTML = `
297
+ <div class="font-medium">${workflow.name}</div>
298
+ <div class="text-xs opacity-70">${workflow.lastUsed}</div>
299
+ `;
300
+ recentWorkflowsList.appendChild(workflowElement);
301
+ });
302
+
303
+ // Show/hide auth fields based on selection
304
+ authMethodSelect.addEventListener('change', function() {
305
+ if (this.value === 'none') {
306
+ authFields.classList.add('hidden');
307
+ } else {
308
+ authFields.classList.remove('hidden');
309
+ }
310
+ });
311
+
312
+ // Modal controls
313
+ closeModalBtn.addEventListener('click', () => {
314
+ jsonModal.classList.add('hidden');
315
+ });
316
+
317
+ copyJsonBtn.addEventListener('click', () => {
318
+ navigator.clipboard.writeText(jsonViewer.textContent)
319
+ .then(() => {
320
+ const originalText = copyJsonBtn.innerHTML;
321
+ copyJsonBtn.innerHTML = '<i class="fas fa-check mr-2"></i> Copied!';
322
+ setTimeout(() => {
323
+ copyJsonBtn.innerHTML = originalText;
324
+ }, 2000);
325
+ });
326
+ });
327
+
328
+ sendToWebhookBtn.addEventListener('click', () => {
329
+ sendToWebhook();
330
+ });
331
+
332
+ // Test webhook button
333
+ testWebhookBtn.addEventListener('click', () => {
334
+ if (!webhookUrlInput.value) {
335
+ alert('Please enter a webhook URL');
336
+ return;
337
+ }
338
+
339
+ testWebhookBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Testing...';
340
+
341
+ // Simulate API call
342
+ setTimeout(() => {
343
+ testWebhookBtn.innerHTML = '<i class="fas fa-check mr-2"></i> Success!';
344
+ setTimeout(() => {
345
+ testWebhookBtn.innerHTML = '<i class="fas fa-bolt mr-2"></i> Test';
346
+ }, 2000);
347
+ }, 1500);
348
+ });
349
+
350
+ // Save configuration
351
+ saveConfigBtn.addEventListener('click', () => {
352
+ workflowData.name = workflowNameInput.value;
353
+ workflowData.httpMethod = httpMethodSelect.value;
354
+ workflowData.authMethod = authMethodSelect.value;
355
+ workflowData.webhookUrl = webhookUrlInput.value;
356
+
357
+ alert('Configuration saved successfully!');
358
+ });
359
+
360
+ // Add node button
361
+ addNodeBtn.addEventListener('click', () => {
362
+ // Show node library if empty canvas
363
+ if (workflowCanvas.querySelector('.text-center')) {
364
+ workflowCanvas.innerHTML = '';
365
+ nodeLibrary.classList.remove('hidden');
366
+ } else {
367
+ nodeLibrary.classList.toggle('hidden');
368
+ }
369
+ });
370
+
371
+ // Export workflow
372
+ exportWorkflowBtn.addEventListener('click', () => {
373
+ if (workflowData.nodes.length === 0) {
374
+ alert('No nodes to export!');
375
+ return;
376
+ }
377
+
378
+ // Generate sample JSON (in a real app, this would be the actual workflow data)
379
+ const sampleWorkflow = {
380
+ name: workflowData.name || 'Unnamed Workflow',
381
+ nodes: workflowData.nodes.map(node => ({
382
+ id: node.id,
383
+ name: node.name,
384
+ type: node.type,
385
+ parameters: {}
386
+ })),
387
+ connections: workflowData.connections,
388
+ webhook: {
389
+ url: workflowData.webhookUrl,
390
+ method: workflowData.httpMethod,
391
+ auth: workflowData.authMethod !== 'none' ? {
392
+ type: workflowData.authMethod,
393
+ credentials: {
394
+ username: document.getElementById('authUsername').value,
395
+ password: document.getElementById('authPassword').value
396
+ }
397
+ } : null
398
+ },
399
+ timestamp: new Date().toISOString()
400
+ };
401
+
402
+ jsonViewer.textContent = JSON.stringify(sampleWorkflow, null, 2);
403
+ jsonModal.classList.remove('hidden');
404
+ });
405
+ }
406
+
407
+ // Add a node to the canvas
408
+ function addNodeToCanvas(nodeType) {
409
+ const nodeId = 'node-' + Date.now();
410
+
411
+ const nodeElement = document.createElement('div');
412
+ nodeElement.id = nodeId;
413
+ nodeElement.className = `node-item glass-effect p-4 rounded-lg mb-4 cursor-move relative ${nodeType.color} bg-opacity-20 hover:bg-opacity-30`;
414
+ nodeElement.innerHTML = `
415
+ <div class="flex items-center mb-2">
416
+ <i class="fas ${nodeType.icon} mr-2 ${nodeType.color}"></i>
417
+ <h3 class="font-medium">${nodeType.name}</h3>
418
+ <button class="ml-auto text-white text-opacity-50 hover:text-opacity-100 node-delete">
419
+ <i class="fas fa-times"></i>
420
+ </button>
421
+ </div>
422
+ <div class="text-xs opacity-70 mb-2">Click to configure</div>
423
+ <div class="flex justify-between text-xs">
424
+ <div class="node-input bg-black bg-opacity-30 px-2 py-1 rounded">Input</div>
425
+ <div class="node-output bg-black bg-opacity-30 px-2 py-1 rounded">Output</div>
426
+ </div>
427
+ `;
428
+
429
+ // Remove the placeholder if it exists
430
+ if (workflowCanvas.querySelector('.text-center')) {
431
+ workflowCanvas.innerHTML = '';
432
+ }
433
+
434
+ workflowCanvas.appendChild(nodeElement);
435
+
436
+ // Add to workflow data
437
+ workflowData.nodes.push({
438
+ id: nodeId,
439
+ name: nodeType.name,
440
+ type: nodeType.name.toLowerCase().replace(' ', '-')
441
+ });
442
+
443
+ // Make draggable
444
+ makeDraggable(nodeElement);
445
+
446
+ // Add delete functionality
447
+ const deleteBtn = nodeElement.querySelector('.node-delete');
448
+ deleteBtn.addEventListener('click', (e) => {
449
+ e.stopPropagation();
450
+ if (confirm('Delete this node?')) {
451
+ nodeElement.remove();
452
+ workflowData.nodes = workflowData.nodes.filter(n => n.id !== nodeId);
453
+
454
+ // If no nodes left, show placeholder
455
+ if (workflowCanvas.children.length === 0) {
456
+ workflowCanvas.innerHTML = `
457
+ <div class="text-center py-8 text-white text-opacity-50">
458
+ <i class="fas fa-magic text-4xl mb-2"></i>
459
+ <p>Add your first node to start building your workflow</p>
460
+ </div>
461
+ `;
462
+ }
463
+ }
464
+ });
465
+
466
+ // Node click for configuration
467
+ nodeElement.addEventListener('click', () => {
468
+ openNodeConfig(nodeId, nodeType);
469
+ });
470
+ }
471
+
472
+ // Make nodes draggable
473
+ function makeDraggable(element) {
474
+ let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
475
+
476
+ element.onmousedown = dragMouseDown;
477
+
478
+ function dragMouseDown(e) {
479
+ e = e || window.event;
480
+ e.preventDefault();
481
+
482
+ // Get the mouse cursor position at startup
483
+ pos3 = e.clientX;
484
+ pos4 = e.clientY;
485
+
486
+ document.onmouseup = closeDragElement;
487
+ document.onmousemove = elementDrag;
488
+ }
489
+
490
+ function elementDrag(e) {
491
+ e = e || window.event;
492
+ e.preventDefault();
493
+
494
+ // Calculate the new cursor position
495
+ pos1 = pos3 - e.clientX;
496
+ pos2 = pos4 - e.clientY;
497
+ pos3 = e.clientX;
498
+ pos4 = e.clientY;
499
+
500
+ // Set the element's new position
501
+ element.style.top = (element.offsetTop - pos2) + "px";
502
+ element.style.left = (element.offsetLeft - pos1) + "px";
503
+ element.style.position = 'absolute';
504
+ }
505
+
506
+ function closeDragElement() {
507
+ // Stop moving when mouse button is released
508
+ document.onmouseup = null;
509
+ document.onmousemove = null;
510
+ }
511
+ }
512
+
513
+ // Open node configuration
514
+ function openNodeConfig(nodeId, nodeType) {
515
+ const node = workflowData.nodes.find(n => n.id === nodeId);
516
+
517
+ // Create modal content based on node type
518
+ let modalContent = `
519
+ <div class="glass-effect p-6 rounded-xl max-w-md mx-auto">
520
+ <div class="flex justify-between items-center mb-4">
521
+ <h3 class="text-xl font-semibold flex items-center">
522
+ <i class="fas ${nodeType.icon} mr-2 ${nodeType.color}"></i>
523
+ ${nodeType.name} Configuration
524
+ </h3>
525
+ <button id="closeNodeConfig" class="text-white hover:text-gray-300">
526
+ <i class="fas fa-times"></i>
527
+ </button>
528
+ </div>
529
+ `;
530
+
531
+ // Add different fields based on node type
532
+ if (nodeType.name === 'Webhook') {
533
+ modalContent += `
534
+ <div class="space-y-4">
535
+ <div>
536
+ <label class="block text-sm font-medium mb-1">Webhook Path</label>
537
+ <input type="text" value="/webhook/${nodeId.split('-')[1]}"
538
+ class="w-full bg-black bg-opacity-30 text-white px-4 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
539
+ </div>
540
+ <div>
541
+ <label class="block text-sm font-medium mb-1">HTTP Method</label>
542
+ <select class="w-full bg-black bg-opacity-30 text-white px-4 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
543
+ <option value="POST" selected>POST</option>
544
+ <option value="GET">GET</option>
545
+ </select>
546
+ </div>
547
+ </div>
548
+ `;
549
+ } else if (nodeType.name === 'HTTP Request') {
550
+ modalContent += `
551
+ <div class="space-y-4">
552
+ <div>
553
+ <label class="block text-sm font-medium mb-1">URL</label>
554
+ <input type="text" placeholder="https://api.example.com/endpoint"
555
+ class="w-full bg-black bg-opacity-30 text-white px-4 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
556
+ </div>
557
+ <div>
558
+ <label class="block text-sm font-medium mb-1">Method</label>
559
+ <select class="w-full bg-black bg-opacity-30 text-white px-4 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
560
+ <option value="GET">GET</option>
561
+ <option value="POST" selected>POST</option>
562
+ <option value="PUT">PUT</option>
563
+ <option value="DELETE">DELETE</option>
564
+ </select>
565
+ </div>
566
+ <div>
567
+ <label class="block text-sm font-medium mb-1">Headers</label>
568
+ <textarea placeholder="Content-Type: application/json\nAuthorization: Bearer token"
569
+ class="w-full bg-black bg-opacity-30 text-white px-4 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 h-24"></textarea>
570
+ </div>
571
+ </div>
572
+ `;
573
+ } else {
574
+ modalContent += `
575
+ <div class="space-y-4">
576
+ <div>
577
+ <label class="block text-sm font-medium mb-1">Configuration</label>
578
+ <textarea placeholder="Enter configuration for ${nodeType.name} node..."
579
+ class="w-full bg-black bg-opacity-30 text-white px-4 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 h-32"></textarea>
580
+ </div>
581
+ </div>
582
+ `;
583
+ }
584
+
585
+ modalContent += `
586
+ <div class="mt-6 flex justify-end space-x-2">
587
+ <button id="cancelNodeConfig" class="bg-gray-600 hover:bg-gray-700 px-4 py-2 rounded-md transition">
588
+ Cancel
589
+ </button>
590
+ <button id="saveNodeConfig" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-md transition">
591
+ <i class="fas fa-save mr-2"></i> Save
592
+ </button>
593
+ </div>
594
+ </div>
595
+ `;
596
+
597
+ // Create and show modal
598
+ const modal = document.createElement('div');
599
+ modal.className = 'fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center z-50';
600
+ modal.innerHTML = modalContent;
601
+ document.body.appendChild(modal);
602
+
603
+ // Close modal
604
+ const closeBtn = modal.querySelector('#closeNodeConfig');
605
+ const cancelBtn = modal.querySelector('#cancelNodeConfig');
606
+
607
+ const closeModal = () => {
608
+ document.body.removeChild(modal);
609
+ };
610
+
611
+ closeBtn.addEventListener('click', closeModal);
612
+ cancelBtn.addEventListener('click', closeModal);
613
+
614
+ // Save configuration
615
+ const saveBtn = modal.querySelector('#saveNodeConfig');
616
+ saveBtn.addEventListener('click', () => {
617
+ // In a real app, save the configuration to workflowData
618
+ alert('Node configuration saved!');
619
+ closeModal();
620
+ });
621
+ }
622
+
623
+ // Send JSON to webhook
624
+ function sendToWebhook() {
625
+ if (!webhookUrlInput.value) {
626
+ alert('Please configure a webhook URL first');
627
+ return;
628
+ }
629
+
630
+ sendToWebhookBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Sending...';
631
+
632
+ // Simulate API call
633
+ setTimeout(() => {
634
+ sendToWebhookBtn.innerHTML = '<i class="fas fa-check mr-2"></i> Sent!';
635
+ setTimeout(() => {
636
+ sendToWebhookBtn.innerHTML = '<i class="fas fa-paper-plane mr-2"></i> Send to Webhook';
637
+ jsonModal.classList.add('hidden');
638
+ }, 1500);
639
+ }, 2000);
640
+ }
641
+
642
+ // Initialize the app
643
+ initUI();
644
+ });
645
+ </script>
646
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=taleslemonade/drstoneworkui" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
647
+ </html>