prompt_template/web/js/api.js
Michael 9890763f0f 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

186 lines
4.9 KiB
JavaScript

/**
* 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'
});
}
} catch (e) {}
}
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'
});
}
} catch (e) {}
}
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}`);
}
}