()
| 52 | } |
| 53 | |
| 54 | createElement() { |
| 55 | // Remove existing panel if any |
| 56 | const existing = document.getElementById('floating-panel'); |
| 57 | if (existing) { |
| 58 | existing.remove(); |
| 59 | } |
| 60 | |
| 61 | // Create floating panel container |
| 62 | const floatingPanel = document.createElement('div'); |
| 63 | floatingPanel.id = 'floating-panel'; |
| 64 | floatingPanel.className = 'fixed bg-background border border-border shadow-lg rounded-lg z-50 transition-all duration-300'; |
| 65 | |
| 66 | // Dynamically calculate position based on the status dot |
| 67 | const statusDotBtn = document.getElementById('status-dot-btn'); |
| 68 | if (statusDotBtn) { |
| 69 | const rect = statusDotBtn.getBoundingClientRect(); |
| 70 | const panelLeft = rect.right + 8; // 8px gap |
| 71 | // The breathing animation expands the dot with a 6px box-shadow. |
| 72 | // We adjust the top position to align with the top of the animation's outer edge. |
| 73 | const panelTop = rect.top - 12; // Nudge up by 11px (6px for shadow + 5px extra) |
| 74 | |
| 75 | floatingPanel.style.top = `${panelTop}px`; |
| 76 | floatingPanel.style.left = `${panelLeft}px`; |
| 77 | } else { |
| 78 | // Fallback to a default position if the dot isn't found |
| 79 | floatingPanel.style.top = '1rem'; |
| 80 | floatingPanel.style.left = '1.875rem'; |
| 81 | } |
| 82 | |
| 83 | floatingPanel.style.width = '280px'; |
| 84 | floatingPanel.style.maxWidth = 'calc(100vw - 2.5rem)'; |
| 85 | |
| 86 | // Add content container |
| 87 | const content = document.createElement('div'); |
| 88 | content.id = 'floating-panel-content'; |
| 89 | content.className = 'bg-background rounded-lg flex flex-col'; |
| 90 | content.style.height = 'auto'; |
| 91 | |
| 92 | floatingPanel.appendChild(content); |
| 93 | document.body.appendChild(floatingPanel); |
| 94 | } |
| 95 | |
| 96 | updateWithLog(log) { |
| 97 | this.currentLog = log; |
no test coverage detected