import { useCallback, useEffect, useRef } from "react"; import { commandIds } from "@commands/ids"; import type { AppStore } from "@editor/store"; export function useImageImport(store: AppStore) { const inputRef = useRef(null); const importFile = useCallback( async (file: File) => { if (!file.type.startsWith("image/")) return; const source = URL.createObjectURL(file); const intrinsicSize = await loadImageSize(source); const state = store.getState(); const artboard = state.editor.selection.artboardId ? state.document.artboards.find((candidate) => candidate.id === state.editor.selection.artboardId) : state.document.artboards[0]; if (!artboard) { URL.revokeObjectURL(source); return; } const assetId = crypto.randomUUID(); const layerId = crypto.randomUUID(); const center = state.editor.viewport.center; store.dispatch(commandIds.documentAddAsset, { asset: { id: assetId, name: file.name, mimeType: file.type, source, intrinsicSize, }, }); store.dispatch(commandIds.documentAddImageLayer, { artboardId: artboard.id, layer: { id: layerId, type: "image", name: file.name, visible: true, locked: false, opacity: 1, assetId, transform: { position: { x: center.x - intrinsicSize.w / 2, y: center.y - intrinsicSize.h / 2 }, scale: { x: 1, y: 1 }, rotation: 0, }, }, }); store.dispatch(commandIds.selectionSet, { artboardId: artboard.id, layerIds: [layerId] }); }, [store], ); const openFilePicker = useCallback(() => inputRef.current?.click(), []); useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { if (event.defaultPrevented || event.key.toLowerCase() !== "o" || (!event.metaKey && !event.ctrlKey)) return; event.preventDefault(); openFilePicker(); }; const handlePaste = (event: ClipboardEvent) => { const file = [...(event.clipboardData?.files ?? [])].find((candidate) => candidate.type.startsWith("image/")); if (!file) return; event.preventDefault(); void importFile(file); }; window.addEventListener("keydown", handleKeyDown); window.addEventListener("paste", handlePaste); return () => { window.removeEventListener("keydown", handleKeyDown); window.removeEventListener("paste", handlePaste); }; }, [importFile, openFilePicker]); const input = ( { const file = event.currentTarget.files?.[0]; event.currentTarget.value = ""; if (file) void importFile(file); }} /> ); return { input, openFilePicker, importFile }; } function loadImageSize(source: string): Promise<{ w: number; h: number }> { return new Promise((resolve, reject) => { const image = new Image(); image.onload = () => resolve({ w: image.naturalWidth, h: image.naturalHeight }); image.onerror = () => reject(new Error("Failed to load image")); image.src = source; }); }