diff --git a/src/renderer/quickcapture/App.tsx b/src/renderer/quickcapture/App.tsx new file mode 100644 index 0000000..8d28bd5 --- /dev/null +++ b/src/renderer/quickcapture/App.tsx @@ -0,0 +1,72 @@ +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import { captureApi } from './api.js'; + +interface PastedImage { url: string; buffer: ArrayBuffer; } + +export function App(): React.ReactElement { + const [text, setText] = useState(''); + const [images, setImages] = useState([]); + const [err, setErr] = useState(null); + const ref = useRef(null); + + useEffect(() => { ref.current?.focus(); }, []); + + const submit = useCallback(async () => { + setErr(null); + if (text.trim().length === 0 && images.length === 0) return; + try { + await captureApi.submit({ text, images: images.map((i) => i.buffer) }); + setText(''); setImages([]); captureApi.hide(); + } catch (e) { setErr('저장에 실패했습니다. 다시 시도해주세요.'); } + }, [text, images]); + + const cancel = useCallback(() => { + if (text.trim().length > 5) { + const ok = window.confirm('이 한 줄을 흘려보낼까요?'); + if (!ok) return; + } + setText(''); setImages([]); captureApi.hide(); + }, [text]); + + useEffect(() => { + const onKey = (e: KeyboardEvent) => { + if (e.key === 'Escape') { e.preventDefault(); cancel(); } + else if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) { e.preventDefault(); void submit(); } + }; + window.addEventListener('keydown', onKey); + return () => window.removeEventListener('keydown', onKey); + }, [cancel, submit]); + + const onPaste = useCallback(async (e: React.ClipboardEvent) => { + const items = Array.from(e.clipboardData.items); + const imgs = items.filter((i) => i.type.startsWith('image/')); + if (imgs.length === 0) return; + e.preventDefault(); + for (const it of imgs) { + const blob = it.getAsFile(); + if (!blob) continue; + const buffer = await blob.arrayBuffer(); + const url = URL.createObjectURL(blob); + setImages((prev) => [...prev, { url, buffer }]); + } + }, []); + + return ( +
+