Skip to content

Instantly share code, notes, and snippets.

@amunchet
Last active November 14, 2024 19:40
Show Gist options
  • Save amunchet/4cfaf0274f3d238946f9f8f94fa9ee02 to your computer and use it in GitHub Desktop.
Save amunchet/4cfaf0274f3d238946f9f8f94fa9ee02 to your computer and use it in GitHub Desktop.
Copy/Paste for noVNC Proxmox
// ==UserScript==
// @name noVNC Paste for Proxmox
// @namespace http://tampermonkey.net/
// @version 0.2a
// @description Pastes text into a noVNC window (for use with Proxmox specifically)
// @author Chester Enright
// @match https://*
// @include /^.*novnc.*/
// @require http://code.jquery.com/jquery-3.3.1.min.js
// @grant none
// ==/UserScript==
const delay = 1
;(function () {
'use strict'
window.sendString = function(text) {
var el = document.getElementById("canvas-id")
text.split("").forEach(x=>{
setTimeout(()=>{
var needs_shift = x.match(/[A-Z!@#$%^&*()_+{}:\"<>?~|]/)
let evt
if (needs_shift) {
evt = new KeyboardEvent("keydown", {keyCode: 16})
el.dispatchEvent(evt)
evt = new KeyboardEvent("keydown", {key: x, shiftKey: true})
el.dispatchEvent(evt)
evt = new KeyboardEvent("keyup", {keyCode: 16})
el.dispatchEvent(evt)
}else{
evt = new KeyboardEvent("keydown", {key: x})
}
el.dispatchEvent(evt)
}, delay)
})
}
$(document).ready(function() {
setTimeout(()=>{
console.log("Starting up noVNC Copy/Paste (for Proxmox)")
$("canvas").attr("id", "canvas-id")
$("canvas").on("mousedown", (e)=>{
if(e.button == 2){ // Right Click
navigator.clipboard.readText().then(text =>{
window.sendString(text)
})
}
})
}, 1000);
})
})()
@zakhar-kogan
Copy link

zakhar-kogan commented Oct 20, 2024

Did some modifications (mostly with LLMs, yet tested massively and haven't found any bugs) to correctly work with new lines, symbols etc:

(function () {
    'use strict';

    let capsLockOn = false;
    const KEY_DELAY = 50;
    const SHIFT_NEEDED = /[A-Z!@#$%^&*()_+{}:"<>?~|]/;

    function simulateKeyEvent(el, eventType, key, options = {}) {
        const evt = new KeyboardEvent(eventType, { key, ...options });
        el.dispatchEvent(evt);
    }

    window.sendString = function(text) {
        const el = document.getElementById("novnc-canvas");
        if (!el) {
            console.error("Canvas element not found");
            return;
        }

        text.split('').forEach((char, index) => {
            setTimeout(() => {
                if (char === '\n') {
                    // Simulate "Enter" key press for line breaks
                    simulateKeyEvent(el, "keydown", "Enter");
                    simulateKeyEvent(el, "keyup", "Enter");
                } else {
                    const needsShift = SHIFT_NEEDED.test(char);
                    const isUpperCase = char >= 'A' && char <= 'Z';

                    if (needsShift) {
                        simulateKeyEvent(el, "keydown", "Shift", { keyCode: 16 });
                    }

                    if (isUpperCase && capsLockOn) {
                        simulateKeyEvent(el, "keydown", char.toLowerCase());
                        simulateKeyEvent(el, "keyup", char.toLowerCase());
                    } else {
                        simulateKeyEvent(el, "keydown", char);
                        simulateKeyEvent(el, "keyup", char);
                    }

                    if (needsShift) {
                        simulateKeyEvent(el, "keyup", "Shift", { keyCode: 16 });
                    }

                    if (char === "CapsLock") {
                        capsLockOn = !capsLockOn;
                        console.log("Caps Lock state changed:", capsLockOn);
                    }
                }
            }, index * KEY_DELAY);
        });
    };

    function waitForCanvas() {
        return new Promise((resolve) => {
            const checkCanvas = () => {
                const canvas = $("canvas");
                if (canvas.length > 0) {
                    canvas.attr("id", "novnc-canvas");
                    resolve(canvas);
                } else {
                    setTimeout(checkCanvas, 500);
                }
            };
            checkCanvas();
        });
    }

    async function setupNoVNCPaste() {
        try {
            console.log("Starting up noVNC Copy/Paste (for Proxmox) - Improved Version with Line Breaks");

            const canvas = await waitForCanvas();

            canvas.on("mousedown", (e) => {
                if (e.button == 2) { // Right Click
                    navigator.clipboard.readText()
                        .then(text => {
                            window.sendString(text);
                        })
                        .catch(err => {
                            console.error("Failed to read clipboard:", err);
                        });
                }
            });

            console.log("noVNC Copy/Paste setup completed");
        } catch (error) {
            console.error("Error setting up noVNC Copy/Paste:", error);
        }
    }

    $(document).ready(setupNoVNCPaste);
})();

@JonasKrausch
Copy link

Forked and updated it for German Mac Keyboard.
Added Async writing to be able to paste walls of text.

Tried many mapping variants, but mapping the whole keyboard worked best.

https://gist.github.com/JonasKrausch/3c64e4ca9f4a9bdde66a91759f24e574

@eerison
Copy link

eerison commented Nov 14, 2024

it worked, thank you <3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment