-
-
Save amunchet/4cfaf0274f3d238946f9f8f94fa9ee02 to your computer and use it in GitHub Desktop.
// ==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); | |
}) | |
})() |
I think you can install Tampermonkey on iOS - the question is how well Proxmox's web console works on mobile. I think you would just two finger press on the canvas area of the VM and it would paste.
You could also modify the script to create a button and then press it to type the text in (as someone did in one of the above comments). That might work better if the iPad is fussy about "right click" (or two finger press).
@amunchet thx a million for this script!
I need to remove and install it on different system.
I can follow the steps from above to install it.
what’s the proper way to remove it?
You can either uninstall Tampermonkey as a browser extension or you can just click on the Tampermonkey extension (you might have to allow it to be shown on the browser extension bar), then click Delete from there.
🙌
I'm still seeing this code when viewing Raw form:
if(e.button == 2){ // Right Click
navigator.clipboard.readText().then(text =>{
window.sendString(text);
})
}
@felix822 Not 100% sure what you're trying to do. Do you want to do the popup instead of using the right click?
If so, use this reply and modify the script as needed: https://gist.github.com/amunchet/4cfaf0274f3d238946f9f8f94fa9ee02?permalink_comment_id=4464804#gistcomment-4464804
Thanks for the quick reply. No, I would like to use the right click function. I followed the steps to install it, found here - https://gist.github.com/amunchet/4cfaf0274f3d238946f9f8f94fa9ee02?permalink_comment_id=4475025#gistcomment-4475025
But when I right click in the guest console, I get an error message:
"noVNC encountered an error:
TypeError: navigator.clipboard.readText is not a function
eval:52:41"
Sorry, I should have mentioned that this was in Firefox. I just tried the same thing in Chrome and it works. Any work-around for FF?
Thanks!
I've got Germany keyboard layout and I wonder if the key mapping might need a lot more code to be accomplished.
I did update the needs_shift var, but there is lot more to be done as there are additional keys for the Umlauts (äüö) as well as swapping the y with the z. Is this easily possible, has anybody done this yet?
I don't have a German keyboard, so I'm not 100% sure, but I would imagine that the umlaut key is a modifier like shift.
You would just need another section of code like this:
var needs_shift = x.match(/[A-Z!@#$%^&*()_+{}:\"<>?~|]/)
// New code
var needs_umlaut = x.match(/äüö/)
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 if(needs_umlaut){
var umlaut_keycode = 999999999999999999999 // Replace with the keyCode for the umlaut modifier is.
evt = new KeyboardEvent("keydown", {keyCode: umlaut_keycode})
el.dispatchEvent(evt)
evt = new KeyboardEvent("keydown", {key: x}) // You might need to modify this, I'm not sure. You can try it without, but it might need some reading of documentation on KeyboardEvents for German keyboards. Would be something like the above code with the shiftKey
el.dispatchEvent(evt)
evt = new KeyboardEvent("keyup", {keyCode: umlaut_keycode})
el.dispatchEvent(evt)
}else{
evt = new KeyboardEvent("keydown", {key: x})
}
I hope that gets you pointed in at least the right direction!
Unfortunately, Umlauts are not like the shift key, they are additional keys that exist only in specific layouts like German.
Here's the German layout to demonstrate the differences to the English one:
- I have marked the Umlauts extra keys in red. They are not like shift, but shift works on these (normal letters) to capitalize them
- z and y are swapped, marked in green
- to make things worse, there is yet another shift-like modifier key called Alt Gr (marked blue, to the right of the Space key). This modifier works on the other keys in that, if pressed, you get the dark blue result displayed on them
That must look pretty weird to English speakers (I guess it is ;)
(I actually took German in Jr. High, so it's not that odd to me :)....not as weird as like the Japanese layout).
Couple of thoughts:
- The Alt Gr needs to have the shift style applied (like in the above example)
- You'll probably need to just swap Z and Y in the javascript. This can be done like this (probably can be done better):
x = x.split('').map(function(c) {
if (c === 'z') {
return 'y';
} else if (c === 'y') {
return 'z';
} else {
return c;
}
}).join('');
- What keys do the Umlauts (U, O, and A) appear as? You will probably need to do some kind of swap for them as well.
This script's basic operating principle is to take a string and then pretend to type it into Proxmox via Javascript from the browser. I don't have a deep understanding into how the layouts for each browser work - there may be a way to adjust the internal key mappings that I'm
not aware of. KeyboardEvent
is the crux of the idea - maybe there is some documentation on how that works for international layouts.
There may also be a way to temporarily switch your keyboard layout to US-en
for the duration of the paste, then switch it immediately back. I think the internationalization issues all stem from trying to type letters into an international layout and emulating those keycodes.
I have a QWERTY keyboard and for some reason, the script is replacing lowercase with uppercase and uppercase with lowercase. There also seems to be a character limit?
Is this just me?
So what's happening in this script is the keyboard itself is being emulated. If you have uppercase and lowercase switching, then it's likely something strange is happening on your keyboard (dumb question, but are you certain caps lock isn't on?)
If that's just the way your keyboard is, then change this line:
from
var needs_shift = x.match(/[A-Z!@#$%^&*()_+{}:\"<>?~|]/)
to
var needs_shift = x.match(/[!@#$%^&*()_+{}:\"<>?~|]/)
That removes the script pushing shift to get to those particular letters.
I don't think there should be a character limit in the script, but depending on your browser and operating system, there may be a clipboard character limit that's getting hit. I do know that some ad-block software might cause weird issues like that as well.
Awesome! It saves my life!
I have a QWERTY keyboard and for some reason, the script is replacing lowercase with uppercase and uppercase with lowercase. There also seems to be a character limit?
Is this just me?
I have the character limit issue too. Looking at the code, I have no clue what could be causing it
@jorgepsmatos First thing I can think of is your browser or OS might be limiting the number of characters in your clipboard. Try to see if you can copy and paste that number of characters into your specific browser in other situations (like into the address bar or something). The other possibility may be a timeout with Tampermonkey - there might be a setting somewhere that kills the script after a certain run period (if you're trying to paste a large amount of text). Hope that helps!
Absolute legend. Thanks.
For anyone interested, I removed the clipboard dependency by changing:
if(e.button == 2){ // Right Click navigator.clipboard.readText().then(text =>{ window.sendString(text); }) }Into:
if(e.button == 2){ // Right Click let text = prompt("Enter text to paste:"); if (text != null) window.sendString(text); }That way, you can paste the text into the native JS prompt.
I got errors with the original script. it needs to check if clipboard access is present and than use prompt or other method as work around. otherwise now it's perfect. almost. I also encountered the same issue with text getting cut off at the end with really long text. I'm using mint / firefox / violentmonkey
Wow! Thank you so much! This script is really nice!
(I actually took German in Jr. High, so it's not that odd to me :)....not as weird as like the Japanese layout).
Couple of thoughts:
- The Alt Gr needs to have the shift style applied (like in the above example)
- You'll probably need to just swap Z and Y in the javascript. This can be done like this (probably can be done better):
x = x.split('').map(function(c) { if (c === 'z') { return 'y'; } else if (c === 'y') { return 'z'; } else { return c; } }).join('');
- What keys do the Umlauts (U, O, and A) appear as? You will probably need to do some kind of swap for them as well.
This script's basic operating principle is to take a string and then pretend to type it into Proxmox via Javascript from the browser. I don't have a deep understanding into how the layouts for each browser work - there may be a way to adjust the internal key mappings that I'm not aware of.
KeyboardEvent
is the crux of the idea - maybe there is some documentation on how that works for international layouts.There may also be a way to temporarily switch your keyboard layout to
US-en
for the duration of the paste, then switch it immediately back. I think the internationalization issues all stem from trying to type letters into an international layout and emulating those keycodes.
Hi
Any updates on this?
I'd really love such a feature, as with the current code I can barely even use it.
Hello all,
Thank you for this excellent script !
I forked your script to adapt french mac keyboard like this one :
I have to work more to be able to add all the keys but it's a good start.
Working key in french mac keyboard :
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890*=-_+&"(;/
Not working for now : :)[]{}!\'
.....
const keyMap = {
'a': { key: 'q', keyCode: 65 }, 'b': { key: 'b', keyCode: 66 }, 'c': { key: 'c', keyCode: 67 },
'd': { key: 'd', keyCode: 68 }, 'e': { key: 'e', keyCode: 69 }, 'f': { key: 'f', keyCode: 70 },
'g': { key: 'g', keyCode: 71 }, 'h': { key: 'h', keyCode: 72 }, 'i': { key: 'i', keyCode: 73 },
'j': { key: 'j', keyCode: 74 }, 'k': { key: 'k', keyCode: 75 }, 'l': { key: 'l', keyCode: 76 },
'm': { key: ':', keyCode: 191 }, 'n': { key: 'n', keyCode: 78 }, 'o': { key: 'o', keyCode: 79 },
'p': { key: 'p', keyCode: 80 }, 'q': { key: 'a', keyCode: 81 }, 'r': { key: 'r', keyCode: 82 },
's': { key: 's', keyCode: 83 }, 't': { key: 't', keyCode: 84 }, 'u': { key: 'u', keyCode: 85 },
'v': { key: 'v', keyCode: 86 }, 'w': { key: 'z', keyCode: 87 }, 'x': { key: 'x', keyCode: 88 },
'y': { key: 'y', keyCode: 89 }, 'z': { key: 'w', keyCode: 90 },
'A': { key: 'Q', keyCode: 65, shiftKey: true }, 'B': { key: 'B', keyCode: 66, shiftKey: true },
'C': { key: 'C', keyCode: 67, shiftKey: true }, 'D': { key: 'D', keyCode: 68, shiftKey: true },
'E': { key: 'E', keyCode: 69, shiftKey: true }, 'F': { key: 'F', keyCode: 70, shiftKey: true },
'G': { key: 'G', keyCode: 71, shiftKey: true }, 'H': { key: 'H', keyCode: 72, shiftKey: true },
'I': { key: 'I', keyCode: 73, shiftKey: true }, 'J': { key: 'J', keyCode: 74, shiftKey: true },
'K': { key: 'K', keyCode: 75, shiftKey: true }, 'L': { key: 'L', keyCode: 76, shiftKey: true },
'M': { key: ':', keyCode: 220, shiftKey: true }, 'N': { key: 'N', keyCode: 78, shiftKey: true },
'O': { key: 'O', keyCode: 79, shiftKey: true }, 'P': { key: 'P', keyCode: 80, shiftKey: true },
'Q': { key: 'A', keyCode: 81, shiftKey: true }, 'R': { key: 'R', keyCode: 82, shiftKey: true },
'S': { key: 'S', keyCode: 83, shiftKey: true }, 'T': { key: 'T', keyCode: 84, shiftKey: true },
'U': { key: 'U', keyCode: 85, shiftKey: true }, 'V': { key: 'V', keyCode: 86, shiftKey: true },
'W': { key: 'Z', keyCode: 87, shiftKey: true }, 'X': { key: 'X', keyCode: 88, shiftKey: true },
'Y': { key: 'Y', keyCode: 89, shiftKey: true }, 'Z': { key: 'W', keyCode: 90, shiftKey: true },
'1': { key: '&', keyCode: 49, shiftKey: true }, '2': { key: 'é', keyCode: 50, shiftKey: true },
'3': { key: '"', keyCode: 51, shiftKey: true }, '4': { key: '\'', keyCode: 52, shiftKey: true },
'5': { key: '(', keyCode: 53, shiftKey: true }, '6': { key: '§', keyCode: 54, shiftKey: true },
'7': { key: 'è', keyCode: 55, shiftKey: true }, '8': { key: '!', keyCode: 56, shiftKey: true },
'9': { key: 'ç', keyCode: 57, shiftKey: true }, '0': { key: 'à', keyCode: 48, shiftKey: true },
'=': { key: '=', keyCode: 187 }, // OK
'(': { key: '(', keyCode: 53, }, // OK
'&': { key: '&', keyCode: 49,}, // OK
';': { key: ',', keyCode: 188 }, // OK
'"': { key: '"', keyCode: 51, }, // OK
'+': { key: '+', keyCode: 187, shiftKey: true }, // OK
'_': { key: '_', keyCode: 56 }, // OK
'*': { key: '\\', keyCode: 220}, // OK
'-': { key: '^', keyCode: 219 }, // OK
'/': { key: '.', keyCode: 188, shiftKey: true }, // OK
/* '/': { key: '/', keyCode: 191 },
':': { key: '/', keyCode: 186 },
')': { key: '°', keyCode: 48 },
'[': { key: '^', keyCode: 219 },
']': { key: '$', keyCode: 221 },
'{': { key: '^', keyCode: 219 },
'}': { key: '4', keyCode: 221 },
'!': { key: '', keyCode: 56 },
'\'': { key: '4', keyCode: 52 }, */
};
For anyone interested, I removed the clipboard dependency by changing:
Thank you too @flightless22, I used your code too.
merci beaucoup <3 you're the best @pesposito
I have forked this script and edited it to work with French AZERTY Keyboards (Windows default French Keyboard)
Hope this help my fellow French users
For some reason the "|" symbol wasn't pasting properly for me, so I did this:
// ==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) {
switch (x) {
case '|':
evt = new KeyboardEvent("keydown", {which: 16, keyCode: 16, shiftKey: true, key: "Shift"})
el.dispatchEvent(evt)
evt = new KeyboardEvent("keydown", {which: 220, keyCode: 220, shiftKey: true, key: "|"})
el.dispatchEvent(evt)
evt = new KeyboardEvent("keyup", {which: 16, keyCode: 16, shiftKey: false, key: "Shift"})
el.dispatchEvent(evt)
break;
default:
evt = new KeyboardEvent("keydown", {which: 16, keyCode: 16, shiftKey: true, key: "Shift"})
el.dispatchEvent(evt)
evt = new KeyboardEvent("keydown", {shiftKey: true, key: x})
el.dispatchEvent(evt)
evt = new KeyboardEvent("keyup", {which: 16, keyCode: 16, shiftKey: false, key: "Shift"})
el.dispatchEvent(evt)
break;
}
/**/
}else{
evt = new KeyboardEvent("keydown", {key: x})
el.dispatchEvent(evt)
evt = new KeyboardEvent("keyup", {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);
})
})()
I hope this would be helpful for somebody
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);
})();
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
it worked, thank you <3
I wonder - will it work between iPad and the proxmox console?