Attached images open full size and copy back out (#6198)
Scribe #6198 "An attached image can't be viewed large or copied back out". Web and desktop: clicking an image in the editor opens it full size, with Copy image (re-encoded to PNG, the one type every clipboard accepts), Download and Close. Copy hides where the browser has no async clipboard, such as a server reached over plain http; right-clicking the full-size image still copies there. Android: long-pressing an image in the editor copies it to the clipboard; a tap still opens it full size in the phone's viewer. Opening and copying share one hand-out that copies the file out of the blob store behind a FileProvider URI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,3 +9,38 @@
|
||||
export function rendersInline(mime: string): boolean {
|
||||
return mime.startsWith("image/") && mime !== "image/svg+xml";
|
||||
}
|
||||
|
||||
/** Whether this browser can put a picture on the clipboard at all.
|
||||
*
|
||||
* The async clipboard only exists in a secure context, so a server reached over plain
|
||||
* http has none — the viewer then leaves copying to the browser's own context menu
|
||||
* on the full-size image. */
|
||||
export function canCopyImages(): boolean {
|
||||
return typeof ClipboardItem !== "undefined" && typeof navigator.clipboard?.write === "function";
|
||||
}
|
||||
|
||||
/** Put the image at `url` on the clipboard.
|
||||
*
|
||||
* As PNG whatever it was stored as: PNG is the one image type every browser's
|
||||
* clipboard accepts, so a JPEG or WebP is redrawn through a canvas first. The item is
|
||||
* given a promise rather than a blob so the write starts inside the click — Safari
|
||||
* refuses a clipboard write that begins after an await. */
|
||||
export async function copyImage(url: string): Promise<void> {
|
||||
await navigator.clipboard.write([new ClipboardItem({ "image/png": asPng(url) })]);
|
||||
}
|
||||
|
||||
async function asPng(url: string): Promise<Blob> {
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
const blob = await res.blob();
|
||||
if (blob.type === "image/png") return blob;
|
||||
const bitmap = await createImageBitmap(blob);
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = bitmap.width;
|
||||
canvas.height = bitmap.height;
|
||||
canvas.getContext("2d")?.drawImage(bitmap, 0, 0);
|
||||
bitmap.close();
|
||||
return new Promise((resolve, reject) =>
|
||||
canvas.toBlob((png) => (png ? resolve(png) : reject(new Error("Could not encode the image."))), "image/png"),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user