This commit is contained in:
th committed 2026-04-07 08:20:48 +02:00
1 parent 69a3a27348
commit 03f57cbaee
8 files changed
+136 -46

No files matched your search

+60 -27
View File
@@ -207,38 +207,71 @@ body.addEventListener('keydown', e => {
body.addEventListener('blur', () => setTimeout(hideLinkAc, 150));
// ── Clipboard image paste ──
// ── File upload (paste + drag-drop) ──
body.addEventListener('paste', async e => {
async function uploadFile(file) {
const isAudio = file.type.startsWith('audio/');
const isImage = file.type.startsWith('image/');
if (!isAudio && !isImage) return;
const ext = file.name?.split('.').pop() || file.type.split('/')[1] || 'bin';
const placeholder = isAudio ? '[uploading audio...]' : '![uploading...]()';
const pos = body.selectionStart;
body.setRangeText(placeholder, pos, body.selectionEnd, 'end');
updatePreview();
const form = new FormData();
form.append('file', file, (file.name || 'file.' + ext));
try {
const res = await fetch('api/upload', { method: 'POST', body: form, headers: { 'X-CSRF-Token': getCsrf() } });
const text = await res.text();
console.log('Upload response:', res.status, text.substring(0, 300));
const data = JSON.parse(text);
if (data.error) throw new Error(data.error);
if (data.url) {
const md = isAudio
? `<audio controls preload="metadata" src="${data.url}"></audio>`
: `![${data.name}](${data.url})`;
const idx = body.value.indexOf(placeholder);
if (idx !== -1) {
body.value = body.value.substring(0, idx) + md + body.value.substring(idx + placeholder.length);
body.selectionStart = body.selectionEnd = idx + md.length;
}
}
} catch (err) {
console.error('Upload failed:', err);
const idx = body.value.indexOf(placeholder);
if (idx !== -1) {
body.value = body.value.substring(0, idx) + body.value.substring(idx + placeholder.length);
body.selectionStart = body.selectionEnd = idx;
}
}
updatePreview();
}
body.addEventListener('paste', e => {
const items = e.clipboardData?.items;
if (!items) return;
for (const item of items) {
if (!item.type.startsWith('image/')) continue;
e.preventDefault();
const file = item.getAsFile();
if (!file) return;
const placeholder = '![uploading...]()';
body.setRangeText(placeholder, body.selectionStart, body.selectionEnd, 'end');
updatePreview();
const form = new FormData();
form.append('file', file, 'paste.' + (file.type.split('/')[1] || 'png'));
try {
const res = await fetch('api/upload', { method: 'POST', body: form, headers: { 'X-CSRF-Token': getCsrf() } });
const text = await res.text();
const data = JSON.parse(text);
if (data.error) throw new Error(data.error);
if (data.url) {
const md = `![${data.name}](${data.url})`;
const idx = body.value.indexOf(placeholder);
if (idx !== -1) { body.value = body.value.substring(0, idx) + md + body.value.substring(idx + placeholder.length); body.selectionStart = body.selectionEnd = idx + md.length; }
}
} catch (err) {
console.error('Upload failed:', err);
const idx = body.value.indexOf(placeholder);
if (idx !== -1) { body.value = body.value.substring(0, idx) + body.value.substring(idx + placeholder.length); body.selectionStart = body.selectionEnd = idx; }
if (item.type.startsWith('image/') || item.type.startsWith('audio/')) {
e.preventDefault();
const file = item.getAsFile();
if (file) uploadFile(file);
return;
}
}
});
body.addEventListener('dragover', e => { e.preventDefault(); e.dataTransfer.dropEffect = 'copy'; });
body.addEventListener('drop', e => {
const files = e.dataTransfer?.files;
if (!files?.length) return;
for (const file of files) {
if (file.type.startsWith('image/') || file.type.startsWith('audio/')) {
e.preventDefault();
uploadFile(file);
return;
}
updatePreview();
return;
}
});