Files
cinny/src/app/utils/copyImage.ts
T

44 lines
1.5 KiB
TypeScript
Raw Normal View History

/**
* [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<Blob> {
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<Blob>((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<void> {
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 })]);
}