fix: JSON Editor unterstützt Arrays und Objekte via Textarea
- Komplexe JSON-Strukturen werden als formatierter Textbereich gerendert - Benutzer kann JSON direkt mit korrekten Datentypen bearbeiten - Arrays und verschachtelte Objekte bleiben als JSON erhalten Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
parent
581b728c1b
commit
ee1b8f7539
1 changed files with 53 additions and 104 deletions
147
web/index.html
147
web/index.html
|
|
@ -434,9 +434,7 @@
|
|||
<h2 id="edit-title">Template bearbeiten</h2>
|
||||
<button class="modal-close" onclick="closeEditModal()">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<textarea id="edit-content" style="width: 100%; min-height: 300px; background: var(--bg-input); color: var(--text-primary); border: 1px solid var(--border); border-radius: 4px; font-family: var(--mono); font-size: 13px; padding: 10px;" spellcheck="false"></textarea>
|
||||
</div>
|
||||
<div class="modal-body" id="edit-content-content" style="min-height: 300px; padding: 16px; overflow-y: auto;"></div>
|
||||
<div class="modal-actions">
|
||||
<button class="btn btn-primary" onclick="saveEditedContent()">Speichern</button>
|
||||
<button class="btn" onclick="closeEditModal()">Abbrechen</button>
|
||||
|
|
@ -514,89 +512,46 @@ $ python web/serve.py</div>
|
|||
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 = '<textarea id="edit-textarea" style="width: 100%; min-height: 300px; background: var(--bg-input); color: var(--text-primary); border: 1px solid var(--border); border-radius: 4px; font-family: var(--mono); font-size: 13px; padding: 10px;" spellcheck="false">' + escapeHtml(content) + '</textarea>';
|
||||
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 = '<div style="padding-bottom: 15px;"><button onclick="addJsonField()" class="btn" style="margin-bottom: 10px;">Neues Feld hinzufügen</button></div>';
|
||||
html += '<div id="json-edit-fields">';
|
||||
|
||||
for (const key in jsonData) {
|
||||
if (jsonData.hasOwnProperty(key)) {
|
||||
html += createJsonField(key, jsonData[key]);
|
||||
}
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
document.getElementById('edit-content').innerHTML = html;
|
||||
}
|
||||
|
||||
function createJsonField(key, value) {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
let inner = '<table style="width: 100%; margin-bottom: 10px;">';
|
||||
for (const innerKey in value) {
|
||||
if (value.hasOwnProperty(innerKey)) {
|
||||
inner += '<tr><td style="padding: 5px 10px; border-bottom: 1px solid var(--border-light);">' + innerKey + '</td><td style="padding: 5px 10px; border-bottom: 1px solid var(--border-light);"><input type="text" value="' + escapeHtml(String(value[innerKey])) + '" style="width: 100%; background: var(--bg-input); border: 1px solid var(--border); padding: 4px; color: var(--text-primary);"></td></tr>';
|
||||
}
|
||||
}
|
||||
inner += '</table>';
|
||||
return '<div style="background: var(--bg-input); border: 1px solid var(--border-light); border-radius: 4px; margin-bottom: 10px; padding: 10px;" class="json-field">' +
|
||||
'<h4 style="margin-bottom: 8px;">' + key + '</h4>' + inner +
|
||||
'<button onclick="removeJsonField(this)" style="background: var(--red); color: white; border: none; padding: 4px 8px; border-radius: 3px; cursor: pointer;">🗑️ Löschen</button>' +
|
||||
'</div>';
|
||||
} else {
|
||||
return '<div style="background: var(--bg-input); border: 1px solid var(--border-light); border-radius: 4px; padding: 10px; margin-bottom: 10px;" class="json-field">' +
|
||||
'<h4 style="margin-bottom: 5px;"><input type="text" value="' + key + '" style="width: 45%; background: var(--bg-input); border: 1px solid var(--border); padding: 4px; color: var(--text-primary); font-family: var(--mono);" placeholder="Key">' +
|
||||
' <input type="text" value="' + escapeHtml(String(value)) + '" style="width: 45%; background: var(--bg-input); border: 1px solid var(--border); padding: 4px; color: var(--text-primary); font-family: var(--mono);" placeholder="Value"></h4>' +
|
||||
'<button onclick="removeJsonField(this)" style="background: var(--red); color: white; border: none; padding: 4px 8px; border-radius: 3px; cursor: pointer;">🗑️ Löschen</button>' +
|
||||
'</div>';
|
||||
}
|
||||
}
|
||||
|
||||
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, """)
|
||||
.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() {
|
||||
|
|
@ -604,40 +559,34 @@ $ python web/serve.py</div>
|
|||
|
||||
try {
|
||||
if (currentEditTemplate.endsWith('.json')) {
|
||||
// JSON aus Formular extrahieren
|
||||
const fields = document.querySelectorAll('.json-field');
|
||||
const result = {};
|
||||
const textarea = document.getElementById('edit-textarea');
|
||||
const finalJsonString = textarea.value.trim();
|
||||
|
||||
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;
|
||||
}
|
||||
// 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',
|
||||
|
|
@ -649,7 +598,7 @@ $ python web/serve.py</div>
|
|||
showToast('✓ Änderungen gespeichert');
|
||||
closeEditModal();
|
||||
closeModal();
|
||||
showModal(currentEditTemplate.split('/').pop(), content);
|
||||
viewTemplate(currentEditTemplate);
|
||||
} else {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue