Files
pamietnik/backend/internal/api/static/editor.js
Christoph K. 17186e7b64
All checks were successful
Deploy to NAS / deploy (push) Successful in 2m20s
Add TypeScript migration, image resizing, media upload UX, and multimedia support
- Migrate static JS to TypeScript (static-ts/ → compiled to internal/api/static/)
- Add image resizing on upload: JPEG/PNG/WebP scaled to max 1920px at quality 80
- Extract shared upload logic into upload.go (saveUpload, saveResizedImage, saveResizedWebP)
- Add POST /media endpoint for drag-drop/paste media uploads with markdown ref return
- Add background music player with video/audio coordination (autoplay.ts)
- Add global nav, public feed, hashtags, visibility, Markdown rendering for entries
- Add Dockerfile stage for TypeScript compilation (static-ts-builder)
- Add goldmark, disintegration/imaging, golang.org/x/image dependencies

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 23:03:04 +02:00

85 lines
3.3 KiB
JavaScript

"use strict";
(function () {
'use strict';
function initEditor(ta) {
var _a, _b;
async function upload(file) {
var _a;
const form = new FormData();
form.append('file', file);
const statusEl = (_a = ta.parentElement) === null || _a === void 0 ? void 0 : _a.querySelector('.upload-status');
if (statusEl)
statusEl.textContent = '↑ ' + file.name + ' …';
try {
const res = await fetch('/media', { method: 'POST', body: form });
if (!res.ok) {
if (statusEl)
statusEl.textContent = '✗ Fehler beim Hochladen';
return;
}
const data = await res.json();
insertAtCursor('\n' + data.ref + '\n');
if (statusEl)
statusEl.textContent = '';
}
catch (_e) {
if (statusEl)
statusEl.textContent = '✗ Fehler beim Hochladen';
}
}
function insertAtCursor(text) {
const start = ta.selectionStart;
ta.value = ta.value.slice(0, start) + text + ta.value.slice(ta.selectionEnd);
ta.selectionStart = ta.selectionEnd = start + text.length;
ta.focus();
}
// Paste: catch file pastes
ta.addEventListener('paste', function (e) {
var _a;
const items = (_a = e.clipboardData) === null || _a === void 0 ? void 0 : _a.items;
if (!items)
return;
for (let i = 0; i < items.length; i++) {
if (items[i].kind === 'file') {
e.preventDefault();
const file = items[i].getAsFile();
if (file)
void upload(file);
return;
}
}
});
// Drag & Drop onto textarea
ta.addEventListener('dragover', function (e) {
e.preventDefault();
ta.classList.add('drag-over');
});
ta.addEventListener('dragleave', function () {
ta.classList.remove('drag-over');
});
ta.addEventListener('drop', function (e) {
var _a;
e.preventDefault();
ta.classList.remove('drag-over');
const files = (_a = e.dataTransfer) === null || _a === void 0 ? void 0 : _a.files;
if (!files)
return;
for (let i = 0; i < files.length; i++)
void upload(files[i]);
});
// File picker button
const picker = (_a = ta.parentElement) === null || _a === void 0 ? void 0 : _a.querySelector('.media-picker');
const input = (_b = ta.parentElement) === null || _b === void 0 ? void 0 : _b.querySelector('.media-file-input');
if (picker && input) {
picker.addEventListener('click', function () { input.click(); });
input.addEventListener('change', function () {
if (!input.files)
return;
Array.from(input.files).forEach(f => void upload(f));
input.value = '';
});
}
}
document.querySelectorAll('textarea[name="description"]').forEach(initEditor);
})();