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
|
|
|
/**
|
|
|
|
|
* API-Client für HTTP-Kommunikation mit dem Backend.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Template-Inhalt laden und anzeigen
|
|
|
|
|
* @param {string} path - Pfad zur Template-Datei
|
|
|
|
|
*/
|
|
|
|
|
async function viewTemplate(path) {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch(path);
|
|
|
|
|
if (!response.ok) throw new Error('Datei nicht gefunden');
|
|
|
|
|
|
|
|
|
|
let content = await response.text();
|
|
|
|
|
|
|
|
|
|
// Für JSON: formatiert anzeigen
|
|
|
|
|
if (path.endsWith('.json')) {
|
|
|
|
|
try {
|
|
|
|
|
const data = JSON.parse(content);
|
|
|
|
|
content = JSON.stringify(data, null, 2);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
// Nicht valides JSON, als Raw Text anzeigen
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
showModal(path.split('/').pop(), content);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
showModal(path, `Fehler beim Laden: ${e.message}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Template-Inhalt kopieren
|
|
|
|
|
* @param {string} path - Pfad zur Template-Datei
|
|
|
|
|
*/
|
|
|
|
|
async function copyContent(path) {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch(path);
|
|
|
|
|
if (!response.ok) throw new Error('Datei nicht gefunden');
|
|
|
|
|
|
|
|
|
|
let content = await response.text();
|
|
|
|
|
|
|
|
|
|
// Für JSON: originalen Inhalt (nicht formatiert) kopieren
|
|
|
|
|
if (path.endsWith('.json')) {
|
|
|
|
|
try {
|
|
|
|
|
JSON.parse(content);
|
|
|
|
|
// Valides JSON - originalen Inhalt behalten
|
|
|
|
|
} catch (e) {
|
|
|
|
|
// Nicht valides JSON
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await copyContentToClipboard(content);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
showToast(`✗ Fehler: ${e.message}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Template-Daten vom Server laden
|
|
|
|
|
* @returns {Promise<Array>} Template-Liste
|
|
|
|
|
*/
|
|
|
|
|
async function loadTemplates() {
|
|
|
|
|
const response = await fetch('/templates.json');
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
return await scanTemplates();
|
|
|
|
|
}
|
|
|
|
|
return response.json();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Templates manuell scannen (Fallback wenn templates.json fehlt)
|
|
|
|
|
* @returns {Promise<Array>} Template-Liste
|
|
|
|
|
*/
|
|
|
|
|
async function scanTemplates() {
|
|
|
|
|
const templates = [];
|
|
|
|
|
const systemFiles = ['commit_analysis', 'code_reviewer', 'summarizer'];
|
|
|
|
|
for (const file of systemFiles) {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch(`/templates/system/${file}.json`);
|
|
|
|
|
if (response.ok) {
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
templates.push({
|
|
|
|
|
path: `/templates/system/${file}.json`,
|
|
|
|
|
type: 'system',
|
|
|
|
|
name: data.name || file,
|
|
|
|
|
description: data.description || '',
|
|
|
|
|
version: data.version || '1.0',
|
|
|
|
|
tags: data.tags || [],
|
|
|
|
|
format: 'json'
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-05-03 20:09:36 +02:00
|
|
|
} catch (e) { console.error('scanTemplates error:', e); }
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const userFiles = ['email_draft', 'brainstorming'];
|
|
|
|
|
for (const file of userFiles) {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch(`/templates/user/${file}.md`);
|
|
|
|
|
if (response.ok) {
|
|
|
|
|
const text = await response.text();
|
|
|
|
|
const name = text.split('\n')[0].replace('# ', '');
|
|
|
|
|
templates.push({
|
|
|
|
|
path: `/templates/user/${file}.md`,
|
|
|
|
|
type: 'user',
|
|
|
|
|
name: name,
|
|
|
|
|
description: '',
|
|
|
|
|
version: '1.0',
|
|
|
|
|
tags: [],
|
|
|
|
|
format: 'md'
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-05-03 20:09:36 +02:00
|
|
|
} catch (e) { console.error('scanTemplates error:', e); }
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return templates;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Bearbeiteten Inhalt speichern
|
|
|
|
|
*/
|
|
|
|
|
async function saveEditedContent() {
|
|
|
|
|
if (!window.currentEditTemplate) return;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
if (window.currentEditTemplate.endsWith('.json')) {
|
|
|
|
|
const editContainer = document.getElementById('edit-content-content');
|
|
|
|
|
const firstChild = editContainer.children[0];
|
|
|
|
|
const formDiv = (firstChild && firstChild.nodeType === Node.ELEMENT_NODE && firstChild.tagName === 'DIV') ? firstChild : null;
|
|
|
|
|
if (!formDiv) {
|
|
|
|
|
showToast('✗ Keine Eingabefelder gefunden');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const updatedData = extractJsonFromForm(formDiv);
|
|
|
|
|
const finalJsonString = JSON.stringify(updatedData, null, window.currentIndent);
|
|
|
|
|
|
|
|
|
|
// Valid JSON prüfen
|
|
|
|
|
try {
|
|
|
|
|
JSON.parse(finalJsonString);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
showToast('✗ Ungültiges JSON. Bitte korrigiere den Inhalt.');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const response = await fetch(window.currentEditTemplate, {
|
|
|
|
|
method: 'PUT',
|
|
|
|
|
headers: {'Content-Type': 'text/plain'},
|
|
|
|
|
body: finalJsonString
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response.ok) {
|
|
|
|
|
showToast('✓ Änderungen gespeichert');
|
|
|
|
|
closeEditModal();
|
|
|
|
|
if (wasViewModalOpen()) {
|
|
|
|
|
closeModal();
|
|
|
|
|
}
|
|
|
|
|
await viewTemplate(window.currentEditTemplate);
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error(`HTTP ${response.status}`);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
const textarea = document.getElementById('edit-textarea');
|
|
|
|
|
const content = textarea.value;
|
|
|
|
|
|
|
|
|
|
const response = await fetch(window.currentEditTemplate, {
|
|
|
|
|
method: 'PUT',
|
|
|
|
|
headers: {'Content-Type': 'text/plain'},
|
|
|
|
|
body: content
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response.ok) {
|
|
|
|
|
showToast('✓ Änderungen gespeichert');
|
|
|
|
|
closeEditModal();
|
|
|
|
|
if (wasViewModalOpen()) {
|
|
|
|
|
closeModal();
|
|
|
|
|
}
|
|
|
|
|
await viewTemplate(window.currentEditTemplate);
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error(`HTTP ${response.status}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
showToast(`✗ Fehler beim Speichern: ${e.message}`);
|
|
|
|
|
}
|
|
|
|
|
}
|