@@ -75,12 +75,57 @@ const CUSTOM_CSS = ` <!-- Dynamic Theme Injection Placeholder -->
7575 }
7676 }
7777
78+ function getSelectionContext() {
79+ var ae = document.activeElement;
80+ if (ae && (ae.tagName === 'INPUT' || ae.tagName === 'TEXTAREA')) {
81+ var s = ae.selectionStart;
82+ var en = ae.selectionEnd;
83+ if (s != null && en != null && s !== en) {
84+ return { text: ae.value.slice(s, en), inInput: true, el: ae, start: s, end: en };
85+ }
86+ }
87+ var sel = window.getSelection();
88+ var selText = sel ? sel.toString() : '';
89+ if (selText) return { text: selText, inInput: false };
90+ return null;
91+ }
92+
93+ function deleteSelection(ctx) {
94+ if (!ctx) return;
95+ if (ctx.inInput && ctx.el && !ctx.el.disabled && !ctx.el.readOnly) {
96+ var nativeSetter = Object.getOwnPropertyDescriptor(
97+ ctx.el.tagName === 'TEXTAREA' ? window.HTMLTextAreaElement.prototype : window.HTMLInputElement.prototype,
98+ 'value'
99+ ).set;
100+ nativeSetter.call(ctx.el, ctx.el.value.slice(0, ctx.start) + ctx.el.value.slice(ctx.end));
101+ ctx.el.setSelectionRange(ctx.start, ctx.start);
102+ ctx.el.dispatchEvent(new Event('input', { bubbles: true }));
103+ } else {
104+ try { document.execCommand('delete'); } catch (_) { /* ignore */ }
105+ }
106+ }
107+
78108 document.addEventListener('keydown', function (e) {
79109 if (!(e.metaKey || e.ctrlKey)) return;
80110 if (e.shiftKey || e.altKey) return;
111+ var k = e.key && e.key.toLowerCase();
112+
113+ // Copy / Cut: work on any selection in the document (form field or UI text).
114+ if (k === 'c' || k === 'x') {
115+ var ctx = getSelectionContext();
116+ if (!ctx || !ctx.text) return;
117+ e.preventDefault();
118+ e.stopPropagation();
119+ if (typeof window.__mcpInspectorWriteClipboard === 'function') {
120+ window.__mcpInspectorWriteClipboard(ctx.text).catch(function () { /* swallow */ });
121+ }
122+ if (k === 'x') deleteSelection(ctx);
123+ return;
124+ }
125+
126+ // Paste / Select-all: only meaningful when a paste-capable field is focused.
81127 var ae = document.activeElement;
82128 if (!isPasteableInput(ae)) return;
83- var k = e.key && e.key.toLowerCase();
84129 if (k === 'v') {
85130 // Prevent the (broken) native paste path; do our own via the parent webview.
86131 e.preventDefault();
@@ -96,12 +141,43 @@ const CUSTOM_CSS = ` <!-- Dynamic Theme Injection Placeholder -->
96141 }
97142 }, true);
98143
144+ // Bridge for writing to the system clipboard (copy direction).
145+ // Cross-origin iframes can't use navigator.clipboard.writeText() either,
146+ // so we ask the extension host to do it via vscode.env.clipboard.writeText().
147+ var copyRequestId = 0;
148+ var pendingCopyRequests = new Map();
149+ window.__mcpInspectorWriteClipboard = function (text) {
150+ return new Promise(function (resolve, reject) {
151+ var id = ++copyRequestId;
152+ pendingCopyRequests.set(id, { resolve: resolve, reject: reject });
153+ window.parent.postMessage({
154+ type: 'mcp-inspector-request-clipboard-write',
155+ requestId: id,
156+ text: text == null ? '' : String(text)
157+ }, '*');
158+ setTimeout(function () {
159+ if (pendingCopyRequests.has(id)) {
160+ pendingCopyRequests.delete(id);
161+ reject(new Error('Clipboard write timed out'));
162+ }
163+ }, 5000);
164+ });
165+ };
166+
99167 window.addEventListener('message', function (e) {
100168 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);
169+ if (!msg) return;
170+ if (msg.type === 'mcp-inspector-paste-response') {
171+ var target = pendingPasteRequests.get(msg.requestId);
172+ pendingPasteRequests.delete(msg.requestId);
173+ if (target && msg.text) insertTextAt(target, msg.text);
174+ } else if (msg.type === 'mcp-inspector-clipboard-write-result') {
175+ var pending = pendingCopyRequests.get(msg.requestId);
176+ if (!pending) return;
177+ pendingCopyRequests.delete(msg.requestId);
178+ if (msg.ok) pending.resolve();
179+ else pending.reject(new Error(msg.error || 'Clipboard write failed'));
180+ }
105181 });
106182 })();
107183
@@ -151,7 +227,44 @@ const CUSTOM_CSS = ` <!-- Dynamic Theme Injection Placeholder -->
151227 return Math.round(h * 360) + ' ' + Math.round(s * 100) + '% ' + Math.round(l * 100) + '%';
152228 }
153229
154- // Remove existing theme styles when updating
230+ // Fallback to execCommand when webview blocks navigator.clipboard.writeText.
231+ (function patchClipboard() {
232+ const execCopy = (text) => {
233+ const ta = document.createElement('textarea');
234+ ta.value = text == null ? '' : String(text);
235+ ta.setAttribute('readonly', '');
236+ ta.style.cssText = 'position:fixed;top:0;left:0;opacity:0;pointer-events:none';
237+ document.body.appendChild(ta);
238+ const sel = document.getSelection();
239+ const prevRange = sel && sel.rangeCount > 0 ? sel.getRangeAt(0) : null;
240+ ta.select();
241+ ta.setSelectionRange(0, ta.value.length);
242+ let ok = false;
243+ try { ok = document.execCommand('copy'); } catch (_) { ok = false; }
244+ document.body.removeChild(ta);
245+ if (prevRange && sel) { sel.removeAllRanges(); sel.addRange(prevRange); }
246+ return ok;
247+ };
248+ const native = navigator.clipboard && navigator.clipboard.writeText
249+ ? navigator.clipboard.writeText.bind(navigator.clipboard)
250+ : null;
251+ const writeText = async (text) => {
252+ if (native) {
253+ try { return await native(text); } catch (_) {}
254+ }
255+ // VS Code webview path: ask the extension host to write the clipboard.
256+ if (typeof window.__mcpInspectorWriteClipboard === 'function') {
257+ try { await window.__mcpInspectorWriteClipboard(text); return; } catch (_) {}
258+ }
259+ if (execCopy(text)) return;
260+ throw new Error('Clipboard copy failed');
261+ };
262+ if (!navigator.clipboard) {
263+ Object.defineProperty(navigator, 'clipboard', { value: {}, configurable: true });
264+ }
265+ try { navigator.clipboard.writeText = writeText; } catch (_) {}
266+ })();
267+
155268 let themeStyleElement = null;
156269
157270 // Listen for theme color messages from parent
0 commit comments