Spaces:
Running
Running
| class FloatingOverlay extends HTMLElement { | |
| connectedCallback() { | |
| this.attachShadow({ mode: 'open' }); | |
| this.shadowRoot.innerHTML = ` | |
| <style> | |
| :host { | |
| position: absolute; | |
| bottom: 1rem; | |
| right: 1rem; | |
| z-index: 10; | |
| } | |
| .overlay-container { | |
| display: flex; | |
| gap: 0.5rem; | |
| } | |
| .action-button { | |
| width: 3rem; | |
| height: 3rem; | |
| border-radius: 50%; | |
| background: rgba(15, 23, 42, 0.9); | |
| border: 1px solid rgba(71, 85, 105, 0.5); | |
| color: #94a3b8; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); | |
| } | |
| .action-button:hover { | |
| background: rgba(34, 197, 94, 0.1); | |
| color: #22c55e; | |
| border-color: #22c55e; | |
| } | |
| .primary-button { | |
| background: #22c55e; | |
| color: white; | |
| border-color: #22c55e; | |
| } | |
| .primary-button:hover { | |
| background: #16a34a; | |
| } | |
| .disabled { | |
| opacity: 0.5; | |
| pointer-events: none; | |
| } | |
| </style> | |
| <div class="overlay-container"> | |
| <div class="action-button" id="undo-btn" title="Undo (Ctrl+Z)"> | |
| <i data-feather="corner-up-left"></i> | |
| </div> | |
| <div class="action-button" id="redo-btn" title="Redo (Ctrl+Y)"> | |
| <i data-feather="corner-up-right"></i> | |
| </div> | |
| <div class="action-button" id="ai-btn" title="Generate with AI"> | |
| <i data-feather="sparkles"></i> | |
| </div> | |
| <div class="action-button primary-button" id="run-btn" title="Run Workflow"> | |
| <i data-feather="play"></i> | |
| </div> | |
| </div> | |
| `; | |
| this.setupEventListeners(); | |
| this.updateButtonStates(); | |
| } | |
| setupEventListeners() { | |
| this.shadowRoot.getElementById('undo-btn').addEventListener('click', () => { | |
| this.dispatchEvent(new CustomEvent('undo')); | |
| }); | |
| this.shadowRoot.getElementById('redo-btn').addEventListener('click', () => { | |
| this.dispatchEvent(new CustomEvent('redo')); | |
| }); | |
| this.shadowRoot.getElementById('ai-btn').addEventListener('click', () => { | |
| this.dispatchEvent(new CustomEvent('generate')); | |
| }); | |
| this.shadowRoot.getElementById('run-btn').addEventListener('click', () => { | |
| this.dispatchEvent(new CustomEvent('run')); | |
| }); | |
| } | |
| updateButtonStates() { | |
| const canUndo = this.hasAttribute('can-undo'); | |
| const canRedo = this.hasAttribute('can-redo'); | |
| const isExecuting = this.hasAttribute('is-executing'); | |
| const undoBtn = this.shadowRoot.getElementById('undo-btn'); | |
| const redoBtn = this.shadowRoot.getElementById('redo-btn'); | |
| const runBtn = this.shadowRoot.getElementById('run-btn'); | |
| undoBtn.classList.toggle('disabled', !canUndo); | |
| redoBtn.classList.toggle('disabled', !canRedo); | |
| runBtn.classList.toggle('disabled', isExecuting); | |
| if (isExecuting) { | |
| runBtn.innerHTML = '<i data-feather="loader" class="animate-spin"></i>'; | |
| } else { | |
| runBtn.innerHTML = '<i data-feather="play"></i>'; | |
| } | |
| } | |
| static get observedAttributes() { | |
| return ['can-undo', 'can-redo', 'is-executing']; | |
| } | |
| attributeChangedCallback(name, oldValue, newValue) { | |
| if (name === 'can-undo' || name === 'can-redo' || name === 'is-executing') { | |
| this.updateButtonStates(); | |
| } | |
| } | |
| } | |
| customElements.define('floating-overlay', FloatingOverlay); |