Allow copying images from image preview (#180269)

Allow copying image from image preview

Fixes #171616

Lets you cmd+c / right click to copy images from the image preview

Also disables the copy/paste options in the other media previews since they don't currently support copying
This commit is contained in:
Matt Bierner 2023-04-18 19:19:09 -07:00 committed by GitHub
parent 6fefe056e2
commit 4f5c7b295c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 58 additions and 12 deletions

View file

@ -327,21 +327,40 @@
} }
switch (e.data.type) { switch (e.data.type) {
case 'setScale': case 'setScale': {
updateScale(e.data.scale); updateScale(e.data.scale);
break; break;
}
case 'setActive': case 'setActive': {
setActive(e.data.value); setActive(e.data.value);
break; break;
}
case 'zoomIn': case 'zoomIn': {
zoomIn(); zoomIn();
break; break;
}
case 'zoomOut': case 'zoomOut': {
zoomOut(); zoomOut();
break; break;
}
case 'copyImage': {
copyImage();
break;
}
} }
}); });
document.addEventListener('copy', () => {
copyImage();
});
async function copyImage() {
try {
await navigator.clipboard.write([new ClipboardItem({
'image/png': fetch(image.src).then(request => request.blob())
})]);
} catch (e) {
console.error(e);
}
}
}()); }());

View file

@ -69,6 +69,11 @@
"command": "imagePreview.zoomOut", "command": "imagePreview.zoomOut",
"title": "%command.zoomOut%", "title": "%command.zoomOut%",
"category": "Image Preview" "category": "Image Preview"
},
{
"command": "imagePreview.copyImage",
"title": "%command.copyImage%",
"category": "Image Preview"
} }
], ],
"menus": { "menus": {
@ -82,6 +87,16 @@
"command": "imagePreview.zoomOut", "command": "imagePreview.zoomOut",
"when": "activeCustomEditorId == 'imagePreview.previewEditor'", "when": "activeCustomEditorId == 'imagePreview.previewEditor'",
"group": "1_imagePreview" "group": "1_imagePreview"
},
{
"command": "imagePreview.copyImage",
"when": "false"
}
],
"webview/context": [
{
"command": "imagePreview.copyImage",
"when": "webviewId == 'imagePreview.previewEditor'"
} }
] ]
} }

View file

@ -5,5 +5,6 @@
"customEditor.imagePreview.displayName": "Image Preview", "customEditor.imagePreview.displayName": "Image Preview",
"customEditor.videoPreview.displayName": "Video Preview", "customEditor.videoPreview.displayName": "Video Preview",
"command.zoomIn": "Zoom in", "command.zoomIn": "Zoom in",
"command.zoomOut": "Zoom out" "command.zoomOut": "Zoom out",
"command.copyImage": "Copy"
} }

View file

@ -76,7 +76,7 @@ class AudioPreview extends MediaPreview {
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src data: ${cspSource}; media-src ${cspSource}; script-src 'nonce-${nonce}'; style-src ${cspSource} 'nonce-${nonce}';"> <meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src data: ${cspSource}; media-src ${cspSource}; script-src 'nonce-${nonce}'; style-src ${cspSource} 'nonce-${nonce}';">
<meta id="settings" data-settings="${escapeAttribute(JSON.stringify(settings))}"> <meta id="settings" data-settings="${escapeAttribute(JSON.stringify(settings))}">
</head> </head>
<body class="container loading"> <body class="container loading" data-vscode-context='{ "preventDefaultContextMenuItems": true }'>
<div class="loading-indicator"></div> <div class="loading-indicator"></div>
<div class="loading-error"> <div class="loading-error">
<p>${vscode.l10n.t("An error occurred while loading the audio file.")}</p> <p>${vscode.l10n.t("An error occurred while loading the audio file.")}</p>

View file

@ -135,6 +135,13 @@ class ImagePreview extends MediaPreview {
} }
} }
public copyImage() {
if (this.previewState === PreviewState.Active) {
this.webviewEditor.reveal();
this.webviewEditor.webview.postMessage({ type: 'copyImage' });
}
}
protected override updateState() { protected override updateState() {
super.updateState(); super.updateState();
@ -173,10 +180,10 @@ class ImagePreview extends MediaPreview {
<link rel="stylesheet" href="${escapeAttribute(this.extensionResource('media', 'imagePreview.css'))}" type="text/css" media="screen" nonce="${nonce}"> <link rel="stylesheet" href="${escapeAttribute(this.extensionResource('media', 'imagePreview.css'))}" type="text/css" media="screen" nonce="${nonce}">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src data: ${cspSource}; script-src 'nonce-${nonce}'; style-src ${cspSource} 'nonce-${nonce}';"> <meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src data: ${cspSource}; connect-src ${cspSource}; script-src 'nonce-${nonce}'; style-src ${cspSource} 'nonce-${nonce}';">
<meta id="image-preview-settings" data-settings="${escapeAttribute(JSON.stringify(settings))}"> <meta id="image-preview-settings" data-settings="${escapeAttribute(JSON.stringify(settings))}">
</head> </head>
<body class="container image scale-to-fit loading"> <body class="container image scale-to-fit loading" data-vscode-context='{ "preventDefaultContextMenuItems": true }'>
<div class="loading-indicator"></div> <div class="loading-indicator"></div>
<div class="image-load-error"> <div class="image-load-error">
<p>${vscode.l10n.t("An error occurred while loading the image.")}</p> <p>${vscode.l10n.t("An error occurred while loading the image.")}</p>
@ -231,5 +238,9 @@ export function registerImagePreviewSupport(context: vscode.ExtensionContext, bi
previewManager.activePreview?.zoomOut(); previewManager.activePreview?.zoomOut();
})); }));
disposables.push(vscode.commands.registerCommand('imagePreview.copyImage', () => {
previewManager.activePreview?.copyImage();
}));
return vscode.Disposable.from(...disposables); return vscode.Disposable.from(...disposables);
} }

View file

@ -77,7 +77,7 @@ class VideoPreview extends MediaPreview {
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src data: ${cspSource}; media-src ${cspSource}; script-src 'nonce-${nonce}'; style-src ${cspSource} 'nonce-${nonce}';"> <meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src data: ${cspSource}; media-src ${cspSource}; script-src 'nonce-${nonce}'; style-src ${cspSource} 'nonce-${nonce}';">
<meta id="settings" data-settings="${escapeAttribute(JSON.stringify(settings))}"> <meta id="settings" data-settings="${escapeAttribute(JSON.stringify(settings))}">
</head> </head>
<body class="loading"> <body class="loading" data-vscode-context='{ "preventDefaultContextMenuItems": true }'>
<div class="loading-indicator"></div> <div class="loading-indicator"></div>
<div class="loading-error"> <div class="loading-error">
<p>${vscode.l10n.t("An error occurred while loading the video file.")}</p> <p>${vscode.l10n.t("An error occurred while loading the video file.")}</p>