feat: add clipboard paste-to-upload for images in media library

This commit is contained in:
Ender 2025-10-24 15:46:16 +02:00
parent b90ca876db
commit 9e82fad875

View File

@ -13,6 +13,8 @@ export default function MediaLibrary({ onInsert, onSetFeature, showSetFeature }:
const [error, setError] = useState('');
const [query, setQuery] = useState('');
const [sortBy, setSortBy] = useState<'date_desc' | 'date_asc' | 'name_asc' | 'size_desc'>('date_desc');
const [uploading, setUploading] = useState(false);
const [uploadingCount, setUploadingCount] = useState(0);
const load = async () => {
try {
@ -33,6 +35,42 @@ export default function MediaLibrary({ onInsert, onSetFeature, showSetFeature }:
load();
}, []);
// Paste-to-upload clipboard images
useEffect(() => {
const handler = async (e: ClipboardEvent) => {
try {
const items = e.clipboardData?.items;
if (!items || items.length === 0) return;
const files: File[] = [];
for (let i = 0; i < items.length; i++) {
const it = items[i];
if (it.kind === 'file') {
const f = it.getAsFile();
if (f && f.type.startsWith('image/')) files.push(f);
}
}
if (files.length === 0) return;
setUploading(true);
setUploadingCount(files.length);
for (const file of files) {
try {
const fd = new FormData();
fd.append('image', file, file.name || 'pasted-image.png');
const res = await fetch('/api/media/image', { method: 'POST', body: fd });
if (!res.ok) throw new Error(await res.text());
} catch (err: any) {
setError(err?.message || 'Clipboard upload failed');
}
}
setUploading(false);
setUploadingCount(0);
await load();
} catch {}
};
window.addEventListener('paste', handler);
return () => window.removeEventListener('paste', handler);
}, []);
const del = async (key: string) => {
try {
setError('');
@ -81,7 +119,7 @@ export default function MediaLibrary({ onInsert, onSetFeature, showSetFeature }:
<Paper sx={{ p: 2 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center" sx={{ mb: 2 }}>
<Typography variant="h6">Media Library</Typography>
<Stack direction="row" spacing={1}>
<Stack direction="row" spacing={1} alignItems="center">
<TextField
size="small"
placeholder="Search by name…"
@ -95,6 +133,9 @@ export default function MediaLibrary({ onInsert, onSetFeature, showSetFeature }:
<MenuItem value="size_desc">Largest</MenuItem>
</TextField>
<Button size="small" onClick={load} disabled={loading}>Refresh</Button>
<Typography variant="caption" sx={{ color: 'text.secondary' }}>
Tip: paste an image (Cmd/Ctrl+V) to upload{uploading ? ` — uploading ${uploadingCount}` : ''}
</Typography>
</Stack>
</Stack>
{error && <Typography color="error" sx={{ mb: 1 }}>{error}</Typography>}