Created
August 26, 2026 17:58
-
-
Save ascorbic-acid/2da12f6ee6e568c7a4640d2256cbb823 to your computer and use it in GitHub Desktop.
Proxmox Clipboard Temp Solution
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // past in your browser console, it will show you clipboard dialog, you can navigate away and return. | |
| (function() { | |
| if (window._vncPasterActive) { | |
| console.log("VNCPaster is already active."); | |
| return; | |
| } | |
| window._vncPasterActive = true; | |
| // Helper function to query the canvas dynamically on demand | |
| function getCanvas() { | |
| const frames = [document, ...Array.from(document.querySelectorAll('iframe')).map(f => { | |
| try { return f.contentDocument; } catch(e) { return null; } | |
| })].filter(Boolean); | |
| for (const doc of frames) { | |
| const canvas = doc.querySelector('canvas[tabindex="-1"]') || doc.querySelector('canvas'); | |
| if (canvas) return canvas; | |
| } | |
| return null; | |
| } | |
| // Create persistent floating utility panel immediately | |
| const box = document.createElement('div'); | |
| box.style.cssText = "position:fixed;top:10px;right:10px;z-index:99999;background:#222;color:#fff;padding:12px;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,0.5);font-family:sans-serif;font-size:14px;transition:all 0.2s ease;"; | |
| box.innerHTML = ` | |
| <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;"> | |
| <span style="font-weight:bold;font-size:13px;">Proxmox Quick Paste</span> | |
| <button id="vncCollapseBtn" style="background:#444;color:#fff;border:none;width:20px;height:20px;border-radius:3px;cursor:pointer;font-weight:bold;line-height:16px;padding:0;">-</button> | |
| </div> | |
| <div id="vncContentArea"> | |
| <textarea id="vncPasteInput" placeholder="Click here, press Ctrl+V, then click Send..." style="width:240px;height:70px;background:#333;color:#fff;border:1px solid #555;padding:6px;border-radius:4px;resize:none;display:block;"></textarea> | |
| <div style="margin-top:6px;display:flex;justify-content:space-between;align-items:center;"> | |
| <span id="vncStatus" style="font-size:11px;color:#888;">Ready</span> | |
| <button id="vncSendBtn" style="background:#0084ff;color:#fff;border:none;padding:5px 14px;border-radius:4px;cursor:pointer;font-weight:bold;">Send to VM</button> | |
| </div> | |
| </div> | |
| `; | |
| document.body.appendChild(box); | |
| const contentArea = document.getElementById('vncContentArea'); | |
| const collapseBtn = document.getElementById('vncCollapseBtn'); | |
| const textarea = document.getElementById('vncPasteInput'); | |
| const statusMsg = document.getElementById('vncStatus'); | |
| let isCollapsed = false; | |
| // Collapse / Expand toggle logic | |
| collapseBtn.onclick = () => { | |
| isCollapsed = !isCollapsed; | |
| if (isCollapsed) { | |
| contentArea.style.display = 'none'; | |
| collapseBtn.textContent = '+'; | |
| box.style.padding = '8px 12px'; | |
| } else { | |
| contentArea.style.display = 'block'; | |
| collapseBtn.textContent = '-'; | |
| box.style.padding = '12px'; | |
| textarea.focus(); | |
| } | |
| }; | |
| // Character sender implementation using fresh dynamic canvas lookup | |
| async function sendTextToCanvas(text) { | |
| const canvas = getCanvas(); | |
| if (!canvas) { | |
| throw new Error("Active VNC canvas not found on screen!"); | |
| } | |
| text = text.replace(/\r/g, ''); | |
| for (let i = 0; i < text.length; i++) { | |
| const char = text[i]; | |
| const needsShift = /[A-Z!@#$%^&*()_+{}:"<>?~|]/.test(char); | |
| if (needsShift) { | |
| canvas.dispatchEvent(new KeyboardEvent('keydown', { key: 'Shift', code: 'ShiftLeft', keyCode: 16, bubbles: true })); | |
| } | |
| canvas.dispatchEvent(new KeyboardEvent('keydown', { key: char, bubbles: true })); | |
| canvas.dispatchEvent(new KeyboardEvent('keypress', { key: char, bubbles: true })); | |
| canvas.dispatchEvent(new KeyboardEvent('keyup', { key: char, bubbles: true })); | |
| if (needsShift) { | |
| canvas.dispatchEvent(new KeyboardEvent('keyup', { key: 'Shift', code: 'ShiftLeft', keyCode: 16, bubbles: true })); | |
| } | |
| await new Promise(r => setTimeout(r, 30)); | |
| } | |
| } | |
| // Send action fetches the canvas fresh every single time | |
| document.getElementById('vncSendBtn').onclick = async () => { | |
| const text = textarea.value; | |
| if (!text) return; | |
| statusMsg.style.color = "#00ff88"; | |
| statusMsg.textContent = "Sending..."; | |
| try { | |
| const canvas = getCanvas(); | |
| if (canvas) canvas.focus(); | |
| await sendTextToCanvas(text); | |
| statusMsg.style.color = "#00ff88"; | |
| statusMsg.textContent = "Sent successfully!"; | |
| textarea.value = ''; | |
| textarea.focus(); | |
| } catch (error) { | |
| statusMsg.style.color = "#ff4444"; | |
| statusMsg.textContent = "Error: Open VM Console"; | |
| console.error(error); | |
| } | |
| setTimeout(() => { | |
| statusMsg.style.color = "#888"; | |
| statusMsg.textContent = "Ready"; | |
| }, 3000); | |
| }; | |
| console.log("VNCPaster persistent widget loaded successfully!"); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment