refactor: split serve.py and index.html into single-responsibility modules
Backend:
- path_validator.py: PathValidator-Klasse für Pfad-Validierung
- file_ops.py: read_file, write_file, directory_exists, file_exists
- content_types.py: get_content_type mit EXTENSION_MAP
- handler.py: Handler-Klasse mit do_GET/do_PUT, nutzt above modules
- serve.py: Entry-Point (main, find_free_port), setzt Handler.validator/directory
Frontend:
- css/variables.css: CSS-Variablen (--bg-*, --text-*, --accent, etc.)
- css/styles.css: Alle CSS-Regeln (modal, card, template-grid, etc.)
- js/utils.js: esc, showToast, copyContentToClipboard
- js/modal.js: showModal, closeModal, closeEditModal, wasViewModalOpen
- js/editor.js: editModalContent, createJsonEditUI, extractJsonFromForm
- js/api.js: viewTemplate, copyContent, loadTemplates, saveEditedContent
- js/templates.js: renderTemplates, applyFilters, parseTypeFromHash
- js/main.js: Event-Listener, Hash-Filter, Initialisierung
- index.html: Inline-CSS/JS entfernt, <link>/<script src>-Tags hinzugefügt
Smoke test: SO_REUSEADDR für schnelle Port-Wiederverwendung
2026-05-03 14:40:44 +02:00
|
|
|
/**
|
|
|
|
|
* Utility-Funktionen für das Prompt Templates Frontend.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* XSS-Schutz: Escaped HTML-Special-Chars
|
|
|
|
|
* @param {string} s - Zu escapender String
|
|
|
|
|
* @returns {string} Escaped String
|
|
|
|
|
*/
|
|
|
|
|
function esc(s) {
|
|
|
|
|
const d = document.createElement('div');
|
|
|
|
|
d.textContent = s == null ? '' : String(s);
|
|
|
|
|
return d.innerHTML;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Zeige Toast-Nachricht für 3 Sekunden
|
|
|
|
|
* @param {string} message - Nachrichtentext
|
|
|
|
|
*/
|
|
|
|
|
function showToast(message) {
|
|
|
|
|
const toast = document.getElementById('toast');
|
|
|
|
|
toast.textContent = message;
|
|
|
|
|
toast.classList.add('show');
|
|
|
|
|
setTimeout(() => toast.classList.remove('show'), 3000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Inhalt in die Clipboard kopieren
|
|
|
|
|
* @param {string} content - Zu kopierender Inhalt
|
|
|
|
|
*/
|
|
|
|
|
async function copyContentToClipboard(content) {
|
|
|
|
|
try {
|
|
|
|
|
await navigator.clipboard.writeText(content);
|
|
|
|
|
showToast('✓ Inhalt kopiert');
|
|
|
|
|
} catch (e) {
|
|
|
|
|
// Fallback für ältere Browser
|
|
|
|
|
const textarea = document.createElement('textarea');
|
|
|
|
|
textarea.value = content;
|
|
|
|
|
document.body.appendChild(textarea);
|
|
|
|
|
textarea.select();
|
|
|
|
|
document.execCommand('copy');
|
|
|
|
|
document.body.removeChild(textarea);
|
|
|
|
|
showToast('✓ Inhalt kopiert');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-03 15:06:03 +02:00
|
|
|
// exported functions are global (loaded as <script> tags)
|