diff --git a/src/lib/clipboard-image.ts b/src/lib/clipboard-image.ts new file mode 100644 index 0000000..e917416 --- /dev/null +++ b/src/lib/clipboard-image.ts @@ -0,0 +1,45 @@ +/** + * Extracts the first image File from a DataTransfer object. + * + * Resolution order (most → least reliable across browsers/OS): + * 1. clipboardData.files – browser-normalised FileList; Chrome/Linux/Mac/Safari + * all populate this for real paste events. Most reliable. + * 2. clipboardData.items – DataTransferItemList fallback for edge cases where + * files is empty but items contains a "file" kind entry. + * + * MIME matching: type.startsWith("image/") intentionally accepts any image + * subtype, including OS-specific variants like "image/x-png" on Linux that + * a strict allowlist (["image/png", ...]) would silently reject. + * + * The caller (onImageSelect / handleImageSelect) still runs validateImageFile + * which enforces the app's supported format allowlist — so we stay permissive + * here and strict at the validation boundary. + */ +export function extractImageFromClipboard( + clipboardData: DataTransfer | null | undefined, +): File | null { + if (!clipboardData) return null; + + // ── 1. files array (primary) ────────────────────────────────────────────── + const { files } = clipboardData; + if (files?.length) { + for (let i = 0; i < files.length; i++) { + const file = files[i]; + if (file.type.startsWith("image/")) return file; + } + } + + // ── 2. items fallback ───────────────────────────────────────────────────── + const { items } = clipboardData; + if (items?.length) { + for (let i = 0; i < items.length; i++) { + const item = items[i]; + if (item.kind === "file" && item.type.startsWith("image/")) { + const file = item.getAsFile(); + if (file) return file; + } + } + } + + return null; +}