Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Liquid Glass UI for n8n Workflows</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| .glass-effect { | |
| background: rgba(255, 255, 255, 0.15); | |
| backdrop-filter: blur(10px); | |
| -webkit-backdrop-filter: blur(10px); | |
| border-radius: 10px; | |
| border: 1px solid rgba(255, 255, 255, 0.18); | |
| box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37); | |
| } | |
| .liquid-animation { | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .liquid-animation::before { | |
| content: ''; | |
| position: absolute; | |
| top: -50%; | |
| left: -50%; | |
| width: 200%; | |
| height: 200%; | |
| background: linear-gradient( | |
| 45deg, | |
| transparent, | |
| rgba(255, 255, 255, 0.3), | |
| transparent | |
| ); | |
| transform: rotate(45deg); | |
| animation: liquidFlow 6s linear infinite; | |
| } | |
| @keyframes liquidFlow { | |
| 0% { | |
| transform: rotate(45deg) translate(-30%, -30%); | |
| } | |
| 100% { | |
| transform: rotate(45deg) translate(30%, 30%); | |
| } | |
| } | |
| .node-item { | |
| transition: all 0.3s ease; | |
| } | |
| .node-item:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2); | |
| } | |
| .json-viewer { | |
| max-height: 400px; | |
| overflow-y: auto; | |
| } | |
| /* Custom scrollbar */ | |
| .json-viewer::-webkit-scrollbar { | |
| width: 8px; | |
| } | |
| .json-viewer::-webkit-scrollbar-track { | |
| background: rgba(255, 255, 255, 0.1); | |
| border-radius: 10px; | |
| } | |
| .json-viewer::-webkit-scrollbar-thumb { | |
| background: rgba(255, 255, 255, 0.3); | |
| border-radius: 10px; | |
| } | |
| .json-viewer::-webkit-scrollbar-thumb:hover { | |
| background: rgba(255, 255, 255, 0.5); | |
| } | |
| </style> | |
| </head> | |
| <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"> | |
| <div class="max-w-6xl mx-auto"> | |
| <header class="flex flex-col md:flex-row justify-between items-center mb-8 gap-4"> | |
| <div class="glass-effect liquid-animation p-6 w-full md:w-auto"> | |
| <h1 class="text-3xl md:text-4xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-cyan-400 to-blue-500"> | |
| <i class="fas fa-code-branch mr-3"></i>n8n Workflow UI | |
| </h1> | |
| <p class="text-sm opacity-80 mt-2">Liquid Glass Interface for Webhook Integration</p> | |
| </div> | |
| <div class="glass-effect p-4 w-full md:w-auto flex items-center gap-4"> | |
| <div class="flex-1"> | |
| <label class="block text-sm font-medium mb-1">Webhook URL</label> | |
| <div class="flex"> | |
| <input type="text" id="webhookUrl" placeholder="https://your-n8n-instance.com/webhook/path" | |
| 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"> | |
| <button id="testWebhook" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-r-md transition"> | |
| <i class="fas fa-bolt mr-2"></i>Test | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </header> | |
| <div class="grid grid-cols-1 lg:grid-cols-3 gap-6"> | |
| <!-- Workflow Configuration --> | |
| <div class="glass-effect p-6 lg:col-span-1"> | |
| <h2 class="text-xl font-semibold mb-4 flex items-center"> | |
| <i class="fas fa-cog mr-2 text-blue-400"></i> Workflow Setup | |
| </h2> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-sm font-medium mb-1">Workflow Name</label> | |
| <input type="text" id="workflowName" placeholder="My Awesome Workflow" | |
| 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"> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium mb-1">HTTP Method</label> | |
| <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"> | |
| <option value="POST">POST</option> | |
| <option value="GET">GET</option> | |
| <option value="PUT">PUT</option> | |
| <option value="DELETE">DELETE</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium mb-1">Authentication</label> | |
| <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"> | |
| <option value="none">None</option> | |
| <option value="basic">Basic Auth</option> | |
| <option value="bearer">Bearer Token</option> | |
| <option value="apiKey">API Key</option> | |
| </select> | |
| </div> | |
| <div id="authFields" class="hidden space-y-2"> | |
| <input type="text" id="authUsername" placeholder="Username" | |
| 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"> | |
| <input type="password" id="authPassword" placeholder="Password/Token" | |
| 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"> | |
| </div> | |
| <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"> | |
| <i class="fas fa-save mr-2"></i> Save Configuration | |
| </button> | |
| </div> | |
| <div class="mt-6 pt-4 border-t border-white border-opacity-20"> | |
| <h3 class="text-sm font-medium mb-2">Recent Workflows</h3> | |
| <div id="recentWorkflows" class="space-y-2"> | |
| <!-- Dynamically populated --> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Workflow Visualization --> | |
| <div class="glass-effect p-6 lg:col-span-2"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h2 class="text-xl font-semibold flex items-center"> | |
| <i class="fas fa-project-diagram mr-2 text-purple-400"></i> Workflow Visualization | |
| </h2> | |
| <div class="flex space-x-2"> | |
| <button id="addNode" class="bg-indigo-600 hover:bg-indigo-700 px-3 py-1 rounded-md text-sm transition"> | |
| <i class="fas fa-plus mr-1"></i> Add Node | |
| </button> | |
| <button id="exportWorkflow" class="bg-purple-600 hover:bg-purple-700 px-3 py-1 rounded-md text-sm transition"> | |
| <i class="fas fa-file-export mr-1"></i> Export | |
| </button> | |
| </div> | |
| </div> | |
| <div id="workflowCanvas" class="min-h-64 bg-black bg-opacity-20 rounded-md p-4"> | |
| <div class="text-center py-8 text-white text-opacity-50"> | |
| <i class="fas fa-magic text-4xl mb-2"></i> | |
| <p>Add your first node to start building your workflow</p> | |
| </div> | |
| </div> | |
| <div class="mt-6"> | |
| <h3 class="text-sm font-medium mb-2 flex items-center"> | |
| <i class="fas fa-cubes mr-2"></i> Available Nodes | |
| </h3> | |
| <div id="nodeLibrary" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-3"> | |
| <!-- Dynamically populated --> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- JSON Viewer Modal --> | |
| <div id="jsonModal" class="fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center z-50 hidden"> | |
| <div class="glass-effect w-full max-w-3xl max-h-screen overflow-auto m-4 p-6 rounded-xl"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h3 class="text-xl font-semibold">JSON Viewer</h3> | |
| <button id="closeModal" class="text-white hover:text-gray-300"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div id="jsonViewer" class="json-viewer bg-black bg-opacity-30 p-4 rounded-md font-mono text-sm overflow-auto"></div> | |
| <div class="mt-4 flex justify-end space-x-2"> | |
| <button id="copyJson" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-md transition"> | |
| <i class="fas fa-copy mr-2"></i> Copy JSON | |
| </button> | |
| <button id="sendToWebhook" class="bg-green-600 hover:bg-green-700 px-4 py-2 rounded-md transition"> | |
| <i class="fas fa-paper-plane mr-2"></i> Send to Webhook | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <footer class="text-center text-xs opacity-70 mt-12 mb-4"> | |
| den by olu dara@stoneweb | |
| </footer> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Sample node types for the library | |
| const nodeTypes = [ | |
| { name: 'Webhook', icon: 'fa-link', color: 'bg-purple-500' }, | |
| { name: 'HTTP Request', icon: 'fa-globe', color: 'bg-blue-500' }, | |
| { name: 'Function', icon: 'fa-code', color: 'bg-yellow-500' }, | |
| { name: 'IF Condition', icon: 'fa-question', color: 'bg-green-500' }, | |
| { name: 'Delay', icon: 'fa-clock', color: 'bg-indigo-500' }, | |
| { name: 'Spreadsheet', icon: 'fa-table', color: 'bg-emerald-500' }, | |
| { name: 'Email', icon: 'fa-envelope', color: 'bg-red-500' }, | |
| { name: 'Telegram', icon: 'fa-paper-plane', color: 'bg-cyan-500' }, | |
| { name: 'Slack', icon: 'fa-slack', color: 'bg-pink-500' }, | |
| { name: 'Discord', icon: 'fa-discord', color: 'bg-indigo-500' }, | |
| { name: 'Google Sheets', icon: 'fa-google', color: 'bg-green-500' }, | |
| { name: 'MySQL', icon: 'fa-database', color: 'bg-orange-500' } | |
| ]; | |
| // Sample recent workflows | |
| const recentWorkflows = [ | |
| { name: 'Customer Onboarding', lastUsed: '2 hours ago' }, | |
| { name: 'API Monitoring', lastUsed: 'Yesterday' }, | |
| { name: 'Data Sync', lastUsed: '3 days ago' } | |
| ]; | |
| // Current workflow data | |
| let workflowData = { | |
| name: '', | |
| nodes: [], | |
| connections: [], | |
| webhookUrl: '', | |
| httpMethod: 'POST', | |
| authMethod: 'none' | |
| }; | |
| // DOM elements | |
| const webhookUrlInput = document.getElementById('webhookUrl'); | |
| const testWebhookBtn = document.getElementById('testWebhook'); | |
| const workflowNameInput = document.getElementById('workflowName'); | |
| const httpMethodSelect = document.getElementById('httpMethod'); | |
| const authMethodSelect = document.getElementById('authMethod'); | |
| const authFields = document.getElementById('authFields'); | |
| const saveConfigBtn = document.getElementById('saveConfig'); | |
| const recentWorkflowsList = document.getElementById('recentWorkflows'); | |
| const addNodeBtn = document.getElementById('addNode'); | |
| const exportWorkflowBtn = document.getElementById('exportWorkflow'); | |
| const workflowCanvas = document.getElementById('workflowCanvas'); | |
| const nodeLibrary = document.getElementById('nodeLibrary'); | |
| const jsonModal = document.getElementById('jsonModal'); | |
| const jsonViewer = document.getElementById('jsonViewer'); | |
| const closeModalBtn = document.getElementById('closeModal'); | |
| const copyJsonBtn = document.getElementById('copyJson'); | |
| const sendToWebhookBtn = document.getElementById('sendToWebhook'); | |
| // Initialize the UI | |
| function initUI() { | |
| // Populate node library | |
| nodeLibrary.innerHTML = ''; | |
| nodeTypes.forEach(nodeType => { | |
| const nodeElement = document.createElement('div'); | |
| 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`; | |
| nodeElement.innerHTML = ` | |
| <i class="fas ${nodeType.icon} text-2xl mb-1 ${nodeType.color}"></i> | |
| <span class="text-sm">${nodeType.name}</span> | |
| `; | |
| nodeElement.addEventListener('click', () => addNodeToCanvas(nodeType)); | |
| nodeLibrary.appendChild(nodeElement); | |
| }); | |
| // Populate recent workflows | |
| recentWorkflowsList.innerHTML = ''; | |
| recentWorkflows.forEach(workflow => { | |
| const workflowElement = document.createElement('div'); | |
| workflowElement.className = 'glass-effect p-2 rounded-md cursor-pointer hover:bg-opacity-30'; | |
| workflowElement.innerHTML = ` | |
| <div class="font-medium">${workflow.name}</div> | |
| <div class="text-xs opacity-70">${workflow.lastUsed}</div> | |
| `; | |
| recentWorkflowsList.appendChild(workflowElement); | |
| }); | |
| // Show/hide auth fields based on selection | |
| authMethodSelect.addEventListener('change', function() { | |
| if (this.value === 'none') { | |
| authFields.classList.add('hidden'); | |
| } else { | |
| authFields.classList.remove('hidden'); | |
| } | |
| }); | |
| // Modal controls | |
| closeModalBtn.addEventListener('click', () => { | |
| jsonModal.classList.add('hidden'); | |
| }); | |
| copyJsonBtn.addEventListener('click', () => { | |
| navigator.clipboard.writeText(jsonViewer.textContent) | |
| .then(() => { | |
| const originalText = copyJsonBtn.innerHTML; | |
| copyJsonBtn.innerHTML = '<i class="fas fa-check mr-2"></i> Copied!'; | |
| setTimeout(() => { | |
| copyJsonBtn.innerHTML = originalText; | |
| }, 2000); | |
| }); | |
| }); | |
| sendToWebhookBtn.addEventListener('click', () => { | |
| sendToWebhook(); | |
| }); | |
| // Test webhook button | |
| testWebhookBtn.addEventListener('click', () => { | |
| if (!webhookUrlInput.value) { | |
| alert('Please enter a webhook URL'); | |
| return; | |
| } | |
| testWebhookBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Testing...'; | |
| // Simulate API call | |
| setTimeout(() => { | |
| testWebhookBtn.innerHTML = '<i class="fas fa-check mr-2"></i> Success!'; | |
| setTimeout(() => { | |
| testWebhookBtn.innerHTML = '<i class="fas fa-bolt mr-2"></i> Test'; | |
| }, 2000); | |
| }, 1500); | |
| }); | |
| // Save configuration | |
| saveConfigBtn.addEventListener('click', () => { | |
| workflowData.name = workflowNameInput.value; | |
| workflowData.httpMethod = httpMethodSelect.value; | |
| workflowData.authMethod = authMethodSelect.value; | |
| workflowData.webhookUrl = webhookUrlInput.value; | |
| alert('Configuration saved successfully!'); | |
| }); | |
| // Add node button | |
| addNodeBtn.addEventListener('click', () => { | |
| // Show node library if empty canvas | |
| if (workflowCanvas.querySelector('.text-center')) { | |
| workflowCanvas.innerHTML = ''; | |
| nodeLibrary.classList.remove('hidden'); | |
| } else { | |
| nodeLibrary.classList.toggle('hidden'); | |
| } | |
| }); | |
| // Export workflow | |
| exportWorkflowBtn.addEventListener('click', () => { | |
| if (workflowData.nodes.length === 0) { | |
| alert('No nodes to export!'); | |
| return; | |
| } | |
| // Generate sample JSON (in a real app, this would be the actual workflow data) | |
| const sampleWorkflow = { | |
| name: workflowData.name || 'Unnamed Workflow', | |
| nodes: workflowData.nodes.map(node => ({ | |
| id: node.id, | |
| name: node.name, | |
| type: node.type, | |
| parameters: {} | |
| })), | |
| connections: workflowData.connections, | |
| webhook: { | |
| url: workflowData.webhookUrl, | |
| method: workflowData.httpMethod, | |
| auth: workflowData.authMethod !== 'none' ? { | |
| type: workflowData.authMethod, | |
| credentials: { | |
| username: document.getElementById('authUsername').value, | |
| password: document.getElementById('authPassword').value | |
| } | |
| } : null | |
| }, | |
| timestamp: new Date().toISOString() | |
| }; | |
| jsonViewer.textContent = JSON.stringify(sampleWorkflow, null, 2); | |
| jsonModal.classList.remove('hidden'); | |
| }); | |
| } | |
| // Add a node to the canvas | |
| function addNodeToCanvas(nodeType) { | |
| const nodeId = 'node-' + Date.now(); | |
| const nodeElement = document.createElement('div'); | |
| nodeElement.id = nodeId; | |
| nodeElement.className = `node-item glass-effect p-4 rounded-lg mb-4 cursor-move relative ${nodeType.color} bg-opacity-20 hover:bg-opacity-30`; | |
| nodeElement.innerHTML = ` | |
| <div class="flex items-center mb-2"> | |
| <i class="fas ${nodeType.icon} mr-2 ${nodeType.color}"></i> | |
| <h3 class="font-medium">${nodeType.name}</h3> | |
| <button class="ml-auto text-white text-opacity-50 hover:text-opacity-100 node-delete"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="text-xs opacity-70 mb-2">Click to configure</div> | |
| <div class="flex justify-between text-xs"> | |
| <div class="node-input bg-black bg-opacity-30 px-2 py-1 rounded">Input</div> | |
| <div class="node-output bg-black bg-opacity-30 px-2 py-1 rounded">Output</div> | |
| </div> | |
| `; | |
| // Remove the placeholder if it exists | |
| if (workflowCanvas.querySelector('.text-center')) { | |
| workflowCanvas.innerHTML = ''; | |
| } | |
| workflowCanvas.appendChild(nodeElement); | |
| // Add to workflow data | |
| workflowData.nodes.push({ | |
| id: nodeId, | |
| name: nodeType.name, | |
| type: nodeType.name.toLowerCase().replace(' ', '-') | |
| }); | |
| // Make draggable | |
| makeDraggable(nodeElement); | |
| // Add delete functionality | |
| const deleteBtn = nodeElement.querySelector('.node-delete'); | |
| deleteBtn.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| if (confirm('Delete this node?')) { | |
| nodeElement.remove(); | |
| workflowData.nodes = workflowData.nodes.filter(n => n.id !== nodeId); | |
| // If no nodes left, show placeholder | |
| if (workflowCanvas.children.length === 0) { | |
| workflowCanvas.innerHTML = ` | |
| <div class="text-center py-8 text-white text-opacity-50"> | |
| <i class="fas fa-magic text-4xl mb-2"></i> | |
| <p>Add your first node to start building your workflow</p> | |
| </div> | |
| `; | |
| } | |
| } | |
| }); | |
| // Node click for configuration | |
| nodeElement.addEventListener('click', () => { | |
| openNodeConfig(nodeId, nodeType); | |
| }); | |
| } | |
| // Make nodes draggable | |
| function makeDraggable(element) { | |
| let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; | |
| element.onmousedown = dragMouseDown; | |
| function dragMouseDown(e) { | |
| e = e || window.event; | |
| e.preventDefault(); | |
| // Get the mouse cursor position at startup | |
| pos3 = e.clientX; | |
| pos4 = e.clientY; | |
| document.onmouseup = closeDragElement; | |
| document.onmousemove = elementDrag; | |
| } | |
| function elementDrag(e) { | |
| e = e || window.event; | |
| e.preventDefault(); | |
| // Calculate the new cursor position | |
| pos1 = pos3 - e.clientX; | |
| pos2 = pos4 - e.clientY; | |
| pos3 = e.clientX; | |
| pos4 = e.clientY; | |
| // Set the element's new position | |
| element.style.top = (element.offsetTop - pos2) + "px"; | |
| element.style.left = (element.offsetLeft - pos1) + "px"; | |
| element.style.position = 'absolute'; | |
| } | |
| function closeDragElement() { | |
| // Stop moving when mouse button is released | |
| document.onmouseup = null; | |
| document.onmousemove = null; | |
| } | |
| } | |
| // Open node configuration | |
| function openNodeConfig(nodeId, nodeType) { | |
| const node = workflowData.nodes.find(n => n.id === nodeId); | |
| // Create modal content based on node type | |
| let modalContent = ` | |
| <div class="glass-effect p-6 rounded-xl max-w-md mx-auto"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h3 class="text-xl font-semibold flex items-center"> | |
| <i class="fas ${nodeType.icon} mr-2 ${nodeType.color}"></i> | |
| ${nodeType.name} Configuration | |
| </h3> | |
| <button id="closeNodeConfig" class="text-white hover:text-gray-300"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| `; | |
| // Add different fields based on node type | |
| if (nodeType.name === 'Webhook') { | |
| modalContent += ` | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-sm font-medium mb-1">Webhook Path</label> | |
| <input type="text" value="/webhook/${nodeId.split('-')[1]}" | |
| 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"> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium mb-1">HTTP Method</label> | |
| <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"> | |
| <option value="POST" selected>POST</option> | |
| <option value="GET">GET</option> | |
| </select> | |
| </div> | |
| </div> | |
| `; | |
| } else if (nodeType.name === 'HTTP Request') { | |
| modalContent += ` | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-sm font-medium mb-1">URL</label> | |
| <input type="text" placeholder="https://api.example.com/endpoint" | |
| 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"> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium mb-1">Method</label> | |
| <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"> | |
| <option value="GET">GET</option> | |
| <option value="POST" selected>POST</option> | |
| <option value="PUT">PUT</option> | |
| <option value="DELETE">DELETE</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium mb-1">Headers</label> | |
| <textarea placeholder="Content-Type: application/json\nAuthorization: Bearer token" | |
| 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> | |
| </div> | |
| </div> | |
| `; | |
| } else { | |
| modalContent += ` | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-sm font-medium mb-1">Configuration</label> | |
| <textarea placeholder="Enter configuration for ${nodeType.name} node..." | |
| 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> | |
| </div> | |
| </div> | |
| `; | |
| } | |
| modalContent += ` | |
| <div class="mt-6 flex justify-end space-x-2"> | |
| <button id="cancelNodeConfig" class="bg-gray-600 hover:bg-gray-700 px-4 py-2 rounded-md transition"> | |
| Cancel | |
| </button> | |
| <button id="saveNodeConfig" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-md transition"> | |
| <i class="fas fa-save mr-2"></i> Save | |
| </button> | |
| </div> | |
| </div> | |
| `; | |
| // Create and show modal | |
| const modal = document.createElement('div'); | |
| modal.className = 'fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center z-50'; | |
| modal.innerHTML = modalContent; | |
| document.body.appendChild(modal); | |
| // Close modal | |
| const closeBtn = modal.querySelector('#closeNodeConfig'); | |
| const cancelBtn = modal.querySelector('#cancelNodeConfig'); | |
| const closeModal = () => { | |
| document.body.removeChild(modal); | |
| }; | |
| closeBtn.addEventListener('click', closeModal); | |
| cancelBtn.addEventListener('click', closeModal); | |
| // Save configuration | |
| const saveBtn = modal.querySelector('#saveNodeConfig'); | |
| saveBtn.addEventListener('click', () => { | |
| // In a real app, save the configuration to workflowData | |
| alert('Node configuration saved!'); | |
| closeModal(); | |
| }); | |
| } | |
| // Send JSON to webhook | |
| function sendToWebhook() { | |
| if (!webhookUrlInput.value) { | |
| alert('Please configure a webhook URL first'); | |
| return; | |
| } | |
| sendToWebhookBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Sending...'; | |
| // Simulate API call | |
| setTimeout(() => { | |
| sendToWebhookBtn.innerHTML = '<i class="fas fa-check mr-2"></i> Sent!'; | |
| setTimeout(() => { | |
| sendToWebhookBtn.innerHTML = '<i class="fas fa-paper-plane mr-2"></i> Send to Webhook'; | |
| jsonModal.classList.add('hidden'); | |
| }, 1500); | |
| }, 2000); | |
| } | |
| // Initialize the app | |
| initUI(); | |
| }); | |
| </script> | |
| <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> | |
| </html> |