feat: add clipboard paste-to-upload for images in media library
This commit is contained in:
parent
b90ca876db
commit
9e82fad875
@ -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>}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user