From ee1b8f7539b1d119a13e32c4d2a4eab3b994b019 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 24 Apr 2026 11:58:18 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20JSON=20Editor=20unterst=C3=BCtzt=20Array?= =?UTF-8?q?s=20und=20Objekte=20via=20Textarea=20-=20Komplexe=20JSON-Strukt?= =?UTF-8?q?uren=20werden=20als=20formatierter=20Textbereich=20gerendert=20?= =?UTF-8?q?-=20Benutzer=20kann=20JSON=20direkt=20mit=20korrekten=20Datenty?= =?UTF-8?q?pen=20bearbeiten=20-=20Arrays=20und=20verschachtelte=20Objekte?= =?UTF-8?q?=20bleiben=20als=20JSON=20erhalten?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Mistral Vibe --- web/index.html | 157 +++++++++++++++++-------------------------------- 1 file changed, 53 insertions(+), 104 deletions(-) diff --git a/web/index.html b/web/index.html index e34e41a..0125b6a 100644 --- a/web/index.html +++ b/web/index.html @@ -434,9 +434,7 @@

Template bearbeiten

- + const title = path.split('/').pop(); document.getElementById('edit-title').textContent = `Template bearbeiten: ${title}`; - // Inhalt laden und je nach Dateityp bearbeiten - fetch(path).then(r => r.text()).then(content => { - if (path.endsWith('.json')) { - // JSON parsen und Formular für Key-Value-Paare erstellen - try { - const jsonData = JSON.parse(content); - renderJsonEditForm(jsonData); - } catch (e) { - // Falls JSON ungültig, trotzdem als Text bearbeiten - renderTextEditForm(content); + // Inhalt laden und editierbare Formulare abhängig vom Dateityp erstellen + fetch(path) + .then(r => r.text()) + .then(content => { + const editContainer = document.getElementById('edit-content-content'); + editContainer.innerHTML = ''; + + if (path.endsWith('.json')) { + try { + const jsonData = JSON.parse(content); + createJsonEditUI(editContainer, jsonData, path); + } catch (e) { + // Falls JSON ungültig, als Text bearbeiten + createTextEditUI(editContainer, content); + } + } else { + // Markdown als einfaches Textfeld + createTextEditUI(editContainer, content); } - } else { - // Markdown als Text bearbeiten - renderTextEditForm(content); - } - document.getElementById('edit-modal').classList.add('active'); - }).catch(e => { - showToast(`✗ Fehler beim Laden: ${e.message}`); - }); + + document.getElementById('edit-modal').classList.add('active'); + }) + .catch(e => showToast(`✗ Fehler beim Laden: ${e.message}`)); } - function renderTextEditForm(content) { - document.getElementById('edit-content').innerHTML = ''; + function createTextEditUI(container, content) { + const textarea = document.createElement('textarea'); + textarea.id = 'edit-textarea'; + textarea.value = content; + textarea.style.cssText = 'width: 100%; min-height: 300px; background: var(--bg-input); color: var(--text-primary); border: 1px solid var(--border); border-radius: 4px; padding: 10px; font-family: var(--mono); font-size: 13px;'; + textarea.spellcheck = false; + container.appendChild(textarea); } - function renderJsonEditForm(jsonData) { - let html = '
'; - html += '
'; - - for (const key in jsonData) { - if (jsonData.hasOwnProperty(key)) { - html += createJsonField(key, jsonData[key]); - } - } - - html += '
'; - document.getElementById('edit-content').innerHTML = html; - } - - function createJsonField(key, value) { - if (typeof value === 'object' && value !== null) { - let inner = ''; - for (const innerKey in value) { - if (value.hasOwnProperty(innerKey)) { - inner += ''; - } - } - inner += '
' + innerKey + '
'; - return '
' + - '

' + key + '

' + inner + - '' + - '
'; - } else { - return '
' + - '

' + - '

' + - '' + - '
'; - } - } - - function addJsonField() { - const container = document.getElementById('json-edit-fields'); - container.innerHTML += createJsonField('newField', ''); - } - - function removeJsonField(button) { - button.parentElement.remove(); - } - - function escapeHtml(unsafe) { - return unsafe - .replace(/&/g, "&") - .replace(//g, ">") - .replace(/"/g, """) - .replace(/'/g, "'"); - } - - function unescapeHtml(html) { - const txt = document.createElement("textarea"); - txt.innerHTML = html; - return txt.value; + function createJsonEditUI(container, jsonData, editPathHint = '') { + const textarea = document.createElement('textarea'); + textarea.value = JSON.stringify(jsonData, null, 2); + textarea.style.cssText = 'width: 100%; min-height: 300px; background: var(--bg-input); color: var(--text-primary); border: 1px solid var(--border); border-radius: 4px; padding: 10px; font-family: var(--mono); font-size: 13px;'; + textarea.spellcheck = false; + container.appendChild(textarea); } async function saveEditedContent() { if (!currentEditTemplate) return; - + try { if (currentEditTemplate.endsWith('.json')) { - // JSON aus Formular extrahieren - const fields = document.querySelectorAll('.json-field'); - const result = {}; - - for (const field of fields) { - const title = field.querySelector('h4 input[type="text"]:first-of-type'); - const key = title ? title.value.trim() : ''; - const value = field.querySelector('h4 input[type="text"]:last-of-type') ? field.querySelector('h4 input[type="text"]:last-of-type').value : ''; - - if (key) { - result[key] = value; - } + const textarea = document.getElementById('edit-textarea'); + const finalJsonString = textarea.value.trim(); + + // Valid JSON prüfen + try { + JSON.parse(finalJsonString); + } catch (e) { + showToast('✗ Ungültiges JSON. Bitte korrigiere den Inhalt.'); + return; } - - const jsonString = JSON.stringify(result, null, 2); - + const response = await fetch(currentEditTemplate, { method: 'PUT', headers: {'Content-Type': 'text/plain'}, - body: jsonString + body: finalJsonString }); - + if (response.ok) { showToast('✓ Änderungen gespeichert'); closeEditModal(); closeModal(); - showModal(currentEditTemplate.split('/').pop(), jsonString); + viewTemplate(currentEditTemplate); } else { throw new Error(`HTTP ${response.status}`); } } else { - // Textinhalt direkt speichern const textarea = document.getElementById('edit-textarea'); - const content = textarea ? textarea.value : document.getElementById('edit-content').innerText; + const content = textarea.value; const response = await fetch(currentEditTemplate, { method: 'PUT', headers: {'Content-Type': 'text/plain'}, body: content }); - + if (response.ok) { showToast('✓ Änderungen gespeichert'); closeEditModal(); closeModal(); - showModal(currentEditTemplate.split('/').pop(), content); + viewTemplate(currentEditTemplate); } else { throw new Error(`HTTP ${response.status}`); }