noVNC Paste - MidMouseButton Paste

Pastes simple text into a noVNC window(with middle mouseButton)

  1. // ==UserScript==
  2. // @name noVNC Paste - MidMouseButton Paste
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.4
  5. // @description Pastes simple text into a noVNC window(with middle mouseButton)
  6. // @author A Cat
  7. // @match https://*
  8. // @include /^.*novnc.*/
  9. // @include /^.*vnc.php.*/
  10. // @grant none
  11. // @run-at document-end
  12. // @license MIT
  13. // ==/UserScript==
  14. const delay = 50;
  15. let isPVETerminal = [...document.head.querySelectorAll('link')].map(one=>one.href).join(';').includes('pve');
  16. console.log('当前机器', isPVETerminal ? '是 PVE环境' : '其他');
  17.  
  18. ~(function() {
  19. 'use strict';
  20. const charToKeyName = {
  21. // 基本符号(对应物理按键)
  22. ' ': 'Space',
  23. '!': 'Digit1', // Shift+1
  24. '"': 'Quote', // 双引号(美式键盘)
  25. '#': 'Digit3', // Shift+3(英式键盘)
  26. '$': 'Digit4', // Shift+4
  27. '%': 'Digit5', // Shift+5
  28. '&': 'Digit7', // Shift+7
  29. '\'': 'Quote', // 单引号
  30. '(': 'Digit9', // Shift+9
  31. ')': 'Digit0', // Shift+0
  32. '*': 'Digit8', // Shift+8
  33. '+': 'Equal', // Shift+=
  34. ',': 'Comma', // 逗号
  35. '-': 'Minus', // 减号
  36. '.': 'Period', // 句号
  37. '/': 'Slash', // 斜杠
  38.  
  39. // 数字(直接对应)
  40. '0': 'Digit0',
  41. '1': 'Digit1',
  42. '2': 'Digit2',
  43. '3': 'Digit3',
  44. '4': 'Digit4',
  45. '5': 'Digit5',
  46. '6': 'Digit6',
  47. '7': 'Digit7',
  48. '8': 'Digit8',
  49. '9': 'Digit9',
  50.  
  51. // 符号(基于物理按键)
  52. ':': 'Semicolon', // Shift+;
  53. ';': 'Semicolon',
  54. '<': 'Comma', // Shift+, (这才是物理按键的真实名称)
  55. '=': 'Equal',
  56. '>': 'Period', // Shift+.
  57. '?': 'Slash', // Shift+/
  58. '@': 'Digit2', // Shift+2(美式键盘)
  59.  
  60. // 其他符号
  61. '[': 'BracketLeft',
  62. '\\': 'Backslash',
  63. ']': 'BracketRight',
  64. '^': 'Digit6', // Shift+6
  65. '_': 'Minus', // Shift+-
  66. '`': 'Backquote',
  67. '{': 'BracketLeft', // Shift+[
  68. '|': 'Backslash', // Shift+\
  69. '}': 'BracketRight', // Shift+]
  70. '~': 'Backquote', // Shift+`
  71.  
  72. // 中文符号映射到对应的物理按键
  73. ',': 'Comma',
  74. '。': 'Period',
  75. ';': 'Semicolon',
  76. ':': 'Semicolon', // Shift+;
  77. '?': 'Slash', // Shift+/
  78. '!': 'Digit1', // Shift+1
  79. '、': 'Slash',
  80. '(': 'Digit9', // Shift+9
  81. ')': 'Digit0', // Shift+0
  82. '【': 'BracketLeft',
  83. '】': 'BracketRight',
  84. '「': 'Quote',
  85. '」': 'Quote',
  86. '『': 'Quote', // Shift+'
  87. '』': 'Quote', // Shift+'
  88. '¥': 'Backslash', // 中文键盘反斜杠位置
  89. '……': 'Digit6', // Shift+6(常见输入法)
  90. '——': 'Minus', // Shift+-
  91. '~': 'Backquote' // Shift+`
  92. };
  93. window.sendString = function(text) {
  94. const el = document.querySelector("canvas");
  95. if (!el) {
  96. console.error("Canvas element not found!");
  97. return;
  98. }
  99.  
  100. el.focus();
  101.  
  102. let i = 0;
  103.  
  104. function sendNextChar() {
  105. if (i >= text.length) return;
  106.  
  107. const char = text[i++];
  108. const needsShift = /[A-Z!@#$%^&*()_\-+{}:"<>?~|;“”?《》!:()「」『』¥…—~]/.test(char);
  109.  
  110. const sendEvent = (type, key, modifiers = {}) => {
  111. const code = (() => {
  112. if (key in charToKeyName) return charToKeyName[key];
  113. return /^[a-zA-Z]$/.test(key) ? `Key${key.toUpperCase()}` : undefined;
  114. })();
  115. const event = new KeyboardEvent(type, {
  116. key: key,
  117. code: code,
  118. keyCode: key.charCodeAt(0),
  119. which: key.charCodeAt(0),
  120. bubbles: true,
  121. cancelable: true,
  122. composed: true,
  123. ...modifiers
  124. });
  125.  
  126. el.dispatchEvent(event);
  127. };
  128.  
  129. if (needsShift) {
  130. sendEvent('keydown', 'Shift', { shiftKey: true });
  131. isPVETerminal && sendEvent('keydown', char, { shiftKey: true });
  132. sendEvent('keypress', char, { shiftKey: true });
  133.  
  134. isPVETerminal && sendEvent('keyup', char, { shiftKey: true });
  135. sendEvent('keyup', 'Shift', { shiftKey: false });
  136. } else {
  137. isPVETerminal && sendEvent('keydown', char);
  138. sendEvent('keypress', char);
  139. isPVETerminal && sendEvent('keyup', char);
  140. }
  141.  
  142. console.log(`已发送: ${char} (Shift: ${needsShift})`);
  143. setTimeout(sendNextChar, delay);
  144. }
  145.  
  146. sendNextChar();
  147. };
  148.  
  149. setTimeout(() => {
  150. console.log("Starting up noVNC Copy/Paste (for Proxmox)");
  151.  
  152. const canvas = document.querySelector("canvas")
  153. if(canvas) {
  154. canvas.addEventListener("mousedown", (e) => {
  155. if (e.button === 1) {
  156. navigator.clipboard.readText().then(text => {
  157. console.log("从剪贴板获取内容:", text);
  158. window.sendString(text);
  159. }).catch(err => {
  160. console.error("无法读取剪贴板:", err);
  161. });
  162. }
  163. });
  164. }
  165. }, 2000);
  166. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址