Skip to content

Instantly share code, notes, and snippets.

@ascorbic-acid
Created August 26, 2026 17:58
Show Gist options
  • Select an option

  • Save ascorbic-acid/2da12f6ee6e568c7a4640d2256cbb823 to your computer and use it in GitHub Desktop.

Select an option

Save ascorbic-acid/2da12f6ee6e568c7a4640d2256cbb823 to your computer and use it in GitHub Desktop.
Proxmox Clipboard Temp Solution
// 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