/** * [Gitea #164] Copy an image to the system clipboard. * * The async Clipboard API only guarantees `image/png`, so anything else (JPEG, * WebP, a GIF's first frame) is re-encoded through a canvas first. The blob is * handed to `ClipboardItem` as a promise so the write starts inside the click's * user activation — Safari rejects a write that begins after an await. */ export function canCopyImage(): boolean { return ( typeof ClipboardItem !== 'undefined' && typeof navigator !== 'undefined' && typeof navigator.clipboard?.write === 'function' ); } export async function toPngBlob(blob: Blob): Promise { if (blob.type === 'image/png') return blob; const bitmap = await createImageBitmap(blob); try { const canvas = document.createElement('canvas'); canvas.width = bitmap.width; canvas.height = bitmap.height; const ctx = canvas.getContext('2d'); if (!ctx) throw new Error('no 2d context'); ctx.drawImage(bitmap, 0, 0); return await new Promise((resolve, reject) => { canvas.toBlob((b) => (b ? resolve(b) : reject(new Error('png encode failed'))), 'image/png'); }); } finally { bitmap.close(); } } export async function copyImageFromUrl(url: string): Promise { const png = fetch(url) .then((res) => { if (!res.ok) throw new Error(`fetch failed: ${res.status}`); return res.blob(); }) .then(toPngBlob); await navigator.clipboard.write([new ClipboardItem({ 'image/png': png })]); }