@@ -23,6 +23,88 @@ const INDEX_HTML_PATH = path.join(
2323// Placeholder CSS - will be replaced by dynamic theme injection at runtime
2424const CUSTOM_CSS = ` <!-- Dynamic Theme Injection Placeholder -->
2525 <script>
26+ // === Clipboard paste bridge ===
27+ // Cross-origin iframes inside VS Code webviews cannot use the Clipboard API
28+ // (microsoft/vscode#129178, #182642). We intercept Cmd/Ctrl+V over paste-capable
29+ // fields and ask the parent webview for the clipboard text, then insert it ourselves.
30+ (function () {
31+ var pasteRequestId = 0;
32+ var pendingPasteRequests = new Map();
33+
34+ function isPasteableInput(el) {
35+ if (!el) return false;
36+ if (el.isContentEditable) return true;
37+ if (el.tagName === 'TEXTAREA') return !el.disabled && !el.readOnly;
38+ if (el.tagName === 'INPUT') {
39+ var t = (el.type || 'text').toLowerCase();
40+ var pasteableTypes = ['text', 'search', 'url', 'email', 'password', 'tel', 'number'];
41+ return pasteableTypes.indexOf(t) !== -1 && !el.disabled && !el.readOnly;
42+ }
43+ return false;
44+ }
45+
46+ function insertTextAt(el, text) {
47+ if (!el || !text) return;
48+ if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') {
49+ var start = el.selectionStart != null ? el.selectionStart : el.value.length;
50+ var end = el.selectionEnd != null ? el.selectionEnd : el.value.length;
51+ var nativeSetter = Object.getOwnPropertyDescriptor(
52+ el.tagName === 'TEXTAREA' ? window.HTMLTextAreaElement.prototype : window.HTMLInputElement.prototype,
53+ 'value'
54+ ).set;
55+ // Use the native setter so React's onChange fires (React tracks the prior value)
56+ nativeSetter.call(el, el.value.slice(0, start) + text + el.value.slice(end));
57+ var caret = start + text.length;
58+ el.setSelectionRange(caret, caret);
59+ el.dispatchEvent(new Event('input', { bubbles: true }));
60+ } else if (el.isContentEditable) {
61+ document.execCommand('insertText', false, text);
62+ }
63+ }
64+
65+ function selectAll(el) {
66+ if (!el) return;
67+ if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') {
68+ try { el.select(); } catch (_) { /* ignore */ }
69+ } else if (el.isContentEditable) {
70+ var range = document.createRange();
71+ range.selectNodeContents(el);
72+ var sel = window.getSelection();
73+ sel.removeAllRanges();
74+ sel.addRange(range);
75+ }
76+ }
77+
78+ document.addEventListener('keydown', function (e) {
79+ if (!(e.metaKey || e.ctrlKey)) return;
80+ if (e.shiftKey || e.altKey) return;
81+ var ae = document.activeElement;
82+ if (!isPasteableInput(ae)) return;
83+ var k = e.key && e.key.toLowerCase();
84+ if (k === 'v') {
85+ // Prevent the (broken) native paste path; do our own via the parent webview.
86+ e.preventDefault();
87+ e.stopPropagation();
88+ var id = ++pasteRequestId;
89+ pendingPasteRequests.set(id, ae);
90+ window.parent.postMessage({ type: 'mcp-inspector-request-paste', requestId: id }, '*');
91+ } else if (k === 'a') {
92+ // VS Code's webview swallows the native Select All; do it manually.
93+ e.preventDefault();
94+ e.stopPropagation();
95+ selectAll(ae);
96+ }
97+ }, true);
98+
99+ window.addEventListener('message', function (e) {
100+ var msg = e.data;
101+ if (!msg || msg.type !== 'mcp-inspector-paste-response') return;
102+ var target = pendingPasteRequests.get(msg.requestId);
103+ pendingPasteRequests.delete(msg.requestId);
104+ if (target && msg.text) insertTextAt(target, msg.text);
105+ });
106+ })();
107+
26108 // Function to convert color (hex or rgb/rgba) to HSL format
27109 function colorToHsl(color) {
28110 let r, g, b;
0 commit comments