diff --git a/src/plugins/webContextMenus.web/index.ts b/src/plugins/webContextMenus.web/index.ts index bb98c61d..ec3070b1 100644 --- a/src/plugins/webContextMenus.web/index.ts +++ b/src/plugins/webContextMenus.web/index.ts @@ -46,6 +46,21 @@ const settings = definePluginSettings({ } }); +const MEDIA_PROXY_URL = "https://media.discordapp.net"; +const CDN_URL = "https://cdn.discordapp.com"; + +function fixImageUrl(urlString: string) { + const url = new URL(urlString); + if (url.origin === CDN_URL) return urlString; + if (url.origin === MEDIA_PROXY_URL) return CDN_URL + url.pathname; + + url.searchParams.delete("width"); + url.searchParams.delete("height"); + url.searchParams.set("quality", "lossless"); + if (url.searchParams.get("format") === "webp") url.searchParams.set("format", "png"); + return url.toString(); +} + export default definePlugin({ name: "WebContextMenus", description: "Re-adds context menus missing in the web version of Discord: Links & Images (Copy/Open Link/Image), Text Area (Copy, Cut, Paste, SpellCheck)", @@ -182,34 +197,40 @@ export default definePlugin({ ], async copyImage(url: string) { - if (IS_VESKTOP && VesktopNative.clipboard) { - const data = await fetch(url).then(r => r.arrayBuffer()); - VesktopNative.clipboard.copyImage(data, url); - return; + url = fixImageUrl(url); + + let imageData = await fetch(url).then(r => r.blob()); + if (imageData.type !== "image/png") { + const bitmap = await createImageBitmap(imageData); + + const canvas = document.createElement("canvas"); + canvas.width = bitmap.width; + canvas.height = bitmap.height; + canvas.getContext("2d")!.drawImage(bitmap, 0, 0); + + await new Promise(done => { + canvas.toBlob(data => { + imageData = data!; + done(); + }, "image/png"); + }); } - // Clipboard only supports image/png, jpeg and similar won't work. Thus, we need to convert it to png - // via canvas first - const img = new Image(); - img.onload = () => { - const canvas = document.createElement("canvas"); - canvas.width = img.naturalWidth; - canvas.height = img.naturalHeight; - canvas.getContext("2d")!.drawImage(img, 0, 0); - - canvas.toBlob(data => { - navigator.clipboard.write([ - new ClipboardItem({ - "image/png": data! - }) - ]); - }, "image/png"); - }; - img.crossOrigin = "anonymous"; - img.src = url; + if (IS_VESKTOP && VesktopNative.clipboard) { + VesktopNative.clipboard.copyImage(await imageData.arrayBuffer(), url); + return; + } else { + navigator.clipboard.write([ + new ClipboardItem({ + "image/png": imageData + }) + ]); + } }, async saveImage(url: string) { + url = fixImageUrl(url); + const data = await fetchImage(url); if (!data) return;