Loading editor/html/index.html +26 −0 Original line number Diff line number Diff line Loading @@ -205,6 +205,32 @@ </div> </dialog> <!-- Publish Dialog --> <dialog id="publish-dialog"> <h3 data-i18n="I18N_PUBLISH">Veröffentlichen</h3> <div class="set-field"> <label data-i18n="I18N_CONNECTION">Verbindung</label> <select id="publish-conn-select"></select> </div> <div id="publish-connect-status" class="set-field" style="display:none"> <span id="publish-status" class="set-status"></span> </div> <div id="publish-target-section" style="display:none"> <div class="set-field"> <label data-i18n="I18N_PUBLISH_TARGET">Ziel</label> <select id="publish-target-select"></select> </div> <div id="publish-target-fields"></div> <div class="set-field"> <button id="btn-publish-send" class="set-save-btn" data-i18n="I18N_PUBLISH">Veröffentlichen</button> </div> </div> <div class="dialog-actions"> <button id="btn-publish-settings" data-i18n="Settings" style="margin-right:auto">⚙ <span data-i18n="I18N_CONNECTIONS">Verbindungen</span></button> <button id="btn-publish-close" data-i18n="Close">Schließen</button> </div> </dialog> <dialog id="ai-dialog"> <h3 data-i18n="I18N_AI">KI-Assistent</h3> <div class="set-field"> Loading editor/html/js/editor.js +197 −1 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ var theme = this.value; document.documentElement.setAttribute('data-theme', theme); localStorage.setItem('blogi-theme', theme); Preview.refresh(); }); } Loading Loading @@ -162,7 +163,7 @@ }); document.getElementById('btn-publish').addEventListener('click', function() { openConnectionManager(); openPublishDialog(); }); document.getElementById('btn-ai').addEventListener('click', function() { Loading Loading @@ -926,6 +927,201 @@ } } // ====================== Publish Dialog ====================== var publishTargetsCache = {}; // connId -> targets array var publishDialogBound = false; function openPublishDialog() { var dialog = document.getElementById('publish-dialog'); var connSelect = document.getElementById('publish-conn-select'); var targetSection = document.getElementById('publish-target-section'); var statusEl = document.getElementById('publish-status'); var statusDiv = document.getElementById('publish-connect-status'); targetSection.style.display = 'none'; statusDiv.style.display = 'none'; statusEl.textContent = ''; connSelect.innerHTML = '<option value="">' + I18n.t('I18N_CONNECTING', 'Verbinde...') + '</option>'; bindPublishDialog(); // Load connections and populate select EditorApi.listConnections().then(function(resp) { var conns = resp.connections || []; connSelect.innerHTML = ''; if (conns.length === 0) { connSelect.innerHTML = '<option value="">' + I18n.t('I18N_NO_CONNECTIONS', 'Keine Verbindungen') + '</option>'; return; } for (var i = 0; i < conns.length; i++) { var opt = document.createElement('option'); opt.value = conns[i].id; var label = conns[i].name; if (conns[i].group) label = conns[i].group + ' / ' + label; if (connectedConnections[conns[i].id]) label += ' \u2713'; opt.textContent = label; connSelect.appendChild(opt); } // Auto-connect first autoConnectPublish(conns[0].id); }); dialog.showModal(); } function autoConnectPublish(connId) { if (!connId) return; var targetSection = document.getElementById('publish-target-section'); var statusEl = document.getElementById('publish-status'); var statusDiv = document.getElementById('publish-connect-status'); // If already connected with cached targets, show immediately if (connectedConnections[connId] && publishTargetsCache[connId]) { showPublishDialogTargets(publishTargetsCache[connId]); return; } statusDiv.style.display = 'block'; statusEl.textContent = I18n.t('I18N_CONNECTING', 'Verbinde...'); statusEl.className = 'set-status'; targetSection.style.display = 'none'; EditorApi.connectToConnection(connId).then(function(resp) { connectedConnections[connId] = true; var targets = resp.targets || []; publishTargetsCache[connId] = targets; statusDiv.style.display = 'none'; showPublishDialogTargets(targets); }).catch(function(err) { statusEl.textContent = I18n.t('I18N_PUBLISH_FAILED', 'Verbindung fehlgeschlagen') + ': ' + (err.error || ''); statusEl.className = 'set-status error'; statusDiv.style.display = 'block'; targetSection.style.display = 'none'; }); } function showPublishDialogTargets(targets) { currentPublishTargets = targets; var section = document.getElementById('publish-target-section'); var select = document.getElementById('publish-target-select'); select.innerHTML = ''; if (!targets || targets.length === 0) { section.style.display = 'none'; return; } for (var i = 0; i < targets.length; i++) { var opt = document.createElement('option'); opt.value = targets[i].name; opt.textContent = targets[i].name; select.appendChild(opt); } section.style.display = 'block'; renderPublishDialogFields(targets[0].name); } function renderPublishDialogFields(targetName) { var container = document.getElementById('publish-target-fields'); container.innerHTML = ''; var target = null; for (var i = 0; i < currentPublishTargets.length; i++) { if (currentPublishTargets[i].name === targetName) { target = currentPublishTargets[i]; break; } } if (!target || !target.fields) return; var fields = target.fields; for (var f = 0; f < fields.length; f++) { var field = fields[f]; var div = document.createElement('div'); div.className = 'set-field'; var label = document.createElement('label'); label.textContent = field.label || field.key; div.appendChild(label); if (field.type === 'select' && field.options) { var sel = document.createElement('select'); sel.setAttribute('data-publish-key', field.key); for (var o = 0; o < field.options.length; o++) { var optEl = document.createElement('option'); optEl.value = field.options[o].value; optEl.textContent = field.options[o].label; sel.appendChild(optEl); } div.appendChild(sel); } else { var inp = document.createElement('input'); inp.type = field.type || 'text'; inp.setAttribute('data-publish-key', field.key); if (field.placeholder) inp.placeholder = field.placeholder; if (field.required) inp.required = true; div.appendChild(inp); } container.appendChild(div); } } function bindPublishDialog() { if (publishDialogBound) return; publishDialogBound = true; document.getElementById('btn-publish-close').addEventListener('click', function() { document.getElementById('publish-dialog').close(); }); document.getElementById('btn-publish-settings').addEventListener('click', function() { document.getElementById('publish-dialog').close(); openConnectionManager(); }); document.getElementById('publish-conn-select').addEventListener('change', function() { autoConnectPublish(this.value); }); document.getElementById('publish-target-select').addEventListener('change', function() { renderPublishDialogFields(this.value); }); document.getElementById('btn-publish-send').addEventListener('click', function() { var connId = document.getElementById('publish-conn-select').value; if (!connId) return; var target = document.getElementById('publish-target-select').value; if (!target) return; // Collect dynamic fields var params = {}; var inputs = document.getElementById('publish-target-fields').querySelectorAll('[data-publish-key]'); for (var i = 0; i < inputs.length; i++) { var key = inputs[i].getAttribute('data-publish-key'); if (inputs[i].type === 'checkbox') { params[key] = inputs[i].checked; } else { params[key] = inputs[i].value; } } if (!confirm(I18n.t('I18N_PUBLISH_CONFIRM', 'Dokument ver\u00f6ffentlichen?'))) return; var statusEl = document.getElementById('publish-status'); var statusDiv = document.getElementById('publish-connect-status'); statusDiv.style.display = 'block'; statusEl.textContent = I18n.t('I18N_PUBLISHING', 'Ver\u00f6ffentliche...'); statusEl.className = 'set-status'; EditorApi.publishToConnection(connId, target, params).then(function() { statusEl.textContent = I18n.t('I18N_PUBLISH_SUCCESS', 'Erfolgreich ver\u00f6ffentlicht'); statusEl.className = 'set-status success'; }).catch(function(err) { statusEl.textContent = I18n.t('I18N_PUBLISH_FAILED', 'Ver\u00f6ffentlichung fehlgeschlagen') + ': ' + (err.error || ''); statusEl.className = 'set-status error'; }); }); } // --- Keyboard shortcuts --- document.addEventListener('keydown', function(e) { Loading editor/html/js/preview.js +10 −2 Original line number Diff line number Diff line Loading @@ -5,13 +5,21 @@ var Preview = (function() { 'use strict'; function getThemeStyle() { var theme = document.documentElement.getAttribute('data-theme') || 'dark'; if (theme === 'light') { return 'body{font-family:sans-serif;padding:16px;margin:0;background:#fff;color:#1e1e2e;}'; } return 'body{font-family:sans-serif;padding:16px;margin:0;background:#1e1e2e;color:#cdd6f4;}'; } function refresh() { EditorApi.getPreview().then(function(resp) { var iframe = document.getElementById('preview-frame'); var doc = iframe.contentDocument || iframe.contentWindow.document; doc.open(); doc.write('<!DOCTYPE html><html><head><meta charset="UTF-8">' + '<style>body{font-family:sans-serif;padding:16px;margin:0;}</style>' + '<style>' + getThemeStyle() + '</style>' + '</head><body>' + (resp.html || '') + '</body></html>'); doc.close(); }).catch(function(err) { Loading @@ -24,7 +32,7 @@ var Preview = (function() { var doc = iframe.contentDocument || iframe.contentWindow.document; doc.open(); doc.write('<!DOCTYPE html><html><head><meta charset="UTF-8">' + '<style>body{font-family:sans-serif;padding:40px;text-align:center;' + '<style>' + getThemeStyle() + ' body{padding:40px;text-align:center;' + 'color:#666;}</style></head><body>' + '<p>Vorschau wird hier angezeigt.</p></body></html>'); doc.close(); Loading src/translations.h +2 −0 Original line number Diff line number Diff line Loading @@ -367,6 +367,7 @@ namespace blogi { {"I18N_PASSWORD", "Passwort"}, {"I18N_LOGIN_REQUIRED", "Benutzername und Passwort eingeben"}, // Connection manager {"I18N_CONNECTION", "Verbindung"}, {"I18N_CONNECTIONS", "Verbindungen"}, {"I18N_CONN_GROUP", "Gruppe"}, {"I18N_IGNORE_SSL", "SSL ignorieren"}, Loading Loading @@ -810,6 +811,7 @@ namespace blogi { {"I18N_PASSWORD", "Password"}, {"I18N_LOGIN_REQUIRED", "Please enter username and password"}, // Connection manager {"I18N_CONNECTION", "Connection"}, {"I18N_CONNECTIONS", "Connections"}, {"I18N_CONN_GROUP", "Group"}, {"I18N_IGNORE_SSL", "Ignore SSL"}, Loading Loading
editor/html/index.html +26 −0 Original line number Diff line number Diff line Loading @@ -205,6 +205,32 @@ </div> </dialog> <!-- Publish Dialog --> <dialog id="publish-dialog"> <h3 data-i18n="I18N_PUBLISH">Veröffentlichen</h3> <div class="set-field"> <label data-i18n="I18N_CONNECTION">Verbindung</label> <select id="publish-conn-select"></select> </div> <div id="publish-connect-status" class="set-field" style="display:none"> <span id="publish-status" class="set-status"></span> </div> <div id="publish-target-section" style="display:none"> <div class="set-field"> <label data-i18n="I18N_PUBLISH_TARGET">Ziel</label> <select id="publish-target-select"></select> </div> <div id="publish-target-fields"></div> <div class="set-field"> <button id="btn-publish-send" class="set-save-btn" data-i18n="I18N_PUBLISH">Veröffentlichen</button> </div> </div> <div class="dialog-actions"> <button id="btn-publish-settings" data-i18n="Settings" style="margin-right:auto">⚙ <span data-i18n="I18N_CONNECTIONS">Verbindungen</span></button> <button id="btn-publish-close" data-i18n="Close">Schließen</button> </div> </dialog> <dialog id="ai-dialog"> <h3 data-i18n="I18N_AI">KI-Assistent</h3> <div class="set-field"> Loading
editor/html/js/editor.js +197 −1 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ var theme = this.value; document.documentElement.setAttribute('data-theme', theme); localStorage.setItem('blogi-theme', theme); Preview.refresh(); }); } Loading Loading @@ -162,7 +163,7 @@ }); document.getElementById('btn-publish').addEventListener('click', function() { openConnectionManager(); openPublishDialog(); }); document.getElementById('btn-ai').addEventListener('click', function() { Loading Loading @@ -926,6 +927,201 @@ } } // ====================== Publish Dialog ====================== var publishTargetsCache = {}; // connId -> targets array var publishDialogBound = false; function openPublishDialog() { var dialog = document.getElementById('publish-dialog'); var connSelect = document.getElementById('publish-conn-select'); var targetSection = document.getElementById('publish-target-section'); var statusEl = document.getElementById('publish-status'); var statusDiv = document.getElementById('publish-connect-status'); targetSection.style.display = 'none'; statusDiv.style.display = 'none'; statusEl.textContent = ''; connSelect.innerHTML = '<option value="">' + I18n.t('I18N_CONNECTING', 'Verbinde...') + '</option>'; bindPublishDialog(); // Load connections and populate select EditorApi.listConnections().then(function(resp) { var conns = resp.connections || []; connSelect.innerHTML = ''; if (conns.length === 0) { connSelect.innerHTML = '<option value="">' + I18n.t('I18N_NO_CONNECTIONS', 'Keine Verbindungen') + '</option>'; return; } for (var i = 0; i < conns.length; i++) { var opt = document.createElement('option'); opt.value = conns[i].id; var label = conns[i].name; if (conns[i].group) label = conns[i].group + ' / ' + label; if (connectedConnections[conns[i].id]) label += ' \u2713'; opt.textContent = label; connSelect.appendChild(opt); } // Auto-connect first autoConnectPublish(conns[0].id); }); dialog.showModal(); } function autoConnectPublish(connId) { if (!connId) return; var targetSection = document.getElementById('publish-target-section'); var statusEl = document.getElementById('publish-status'); var statusDiv = document.getElementById('publish-connect-status'); // If already connected with cached targets, show immediately if (connectedConnections[connId] && publishTargetsCache[connId]) { showPublishDialogTargets(publishTargetsCache[connId]); return; } statusDiv.style.display = 'block'; statusEl.textContent = I18n.t('I18N_CONNECTING', 'Verbinde...'); statusEl.className = 'set-status'; targetSection.style.display = 'none'; EditorApi.connectToConnection(connId).then(function(resp) { connectedConnections[connId] = true; var targets = resp.targets || []; publishTargetsCache[connId] = targets; statusDiv.style.display = 'none'; showPublishDialogTargets(targets); }).catch(function(err) { statusEl.textContent = I18n.t('I18N_PUBLISH_FAILED', 'Verbindung fehlgeschlagen') + ': ' + (err.error || ''); statusEl.className = 'set-status error'; statusDiv.style.display = 'block'; targetSection.style.display = 'none'; }); } function showPublishDialogTargets(targets) { currentPublishTargets = targets; var section = document.getElementById('publish-target-section'); var select = document.getElementById('publish-target-select'); select.innerHTML = ''; if (!targets || targets.length === 0) { section.style.display = 'none'; return; } for (var i = 0; i < targets.length; i++) { var opt = document.createElement('option'); opt.value = targets[i].name; opt.textContent = targets[i].name; select.appendChild(opt); } section.style.display = 'block'; renderPublishDialogFields(targets[0].name); } function renderPublishDialogFields(targetName) { var container = document.getElementById('publish-target-fields'); container.innerHTML = ''; var target = null; for (var i = 0; i < currentPublishTargets.length; i++) { if (currentPublishTargets[i].name === targetName) { target = currentPublishTargets[i]; break; } } if (!target || !target.fields) return; var fields = target.fields; for (var f = 0; f < fields.length; f++) { var field = fields[f]; var div = document.createElement('div'); div.className = 'set-field'; var label = document.createElement('label'); label.textContent = field.label || field.key; div.appendChild(label); if (field.type === 'select' && field.options) { var sel = document.createElement('select'); sel.setAttribute('data-publish-key', field.key); for (var o = 0; o < field.options.length; o++) { var optEl = document.createElement('option'); optEl.value = field.options[o].value; optEl.textContent = field.options[o].label; sel.appendChild(optEl); } div.appendChild(sel); } else { var inp = document.createElement('input'); inp.type = field.type || 'text'; inp.setAttribute('data-publish-key', field.key); if (field.placeholder) inp.placeholder = field.placeholder; if (field.required) inp.required = true; div.appendChild(inp); } container.appendChild(div); } } function bindPublishDialog() { if (publishDialogBound) return; publishDialogBound = true; document.getElementById('btn-publish-close').addEventListener('click', function() { document.getElementById('publish-dialog').close(); }); document.getElementById('btn-publish-settings').addEventListener('click', function() { document.getElementById('publish-dialog').close(); openConnectionManager(); }); document.getElementById('publish-conn-select').addEventListener('change', function() { autoConnectPublish(this.value); }); document.getElementById('publish-target-select').addEventListener('change', function() { renderPublishDialogFields(this.value); }); document.getElementById('btn-publish-send').addEventListener('click', function() { var connId = document.getElementById('publish-conn-select').value; if (!connId) return; var target = document.getElementById('publish-target-select').value; if (!target) return; // Collect dynamic fields var params = {}; var inputs = document.getElementById('publish-target-fields').querySelectorAll('[data-publish-key]'); for (var i = 0; i < inputs.length; i++) { var key = inputs[i].getAttribute('data-publish-key'); if (inputs[i].type === 'checkbox') { params[key] = inputs[i].checked; } else { params[key] = inputs[i].value; } } if (!confirm(I18n.t('I18N_PUBLISH_CONFIRM', 'Dokument ver\u00f6ffentlichen?'))) return; var statusEl = document.getElementById('publish-status'); var statusDiv = document.getElementById('publish-connect-status'); statusDiv.style.display = 'block'; statusEl.textContent = I18n.t('I18N_PUBLISHING', 'Ver\u00f6ffentliche...'); statusEl.className = 'set-status'; EditorApi.publishToConnection(connId, target, params).then(function() { statusEl.textContent = I18n.t('I18N_PUBLISH_SUCCESS', 'Erfolgreich ver\u00f6ffentlicht'); statusEl.className = 'set-status success'; }).catch(function(err) { statusEl.textContent = I18n.t('I18N_PUBLISH_FAILED', 'Ver\u00f6ffentlichung fehlgeschlagen') + ': ' + (err.error || ''); statusEl.className = 'set-status error'; }); }); } // --- Keyboard shortcuts --- document.addEventListener('keydown', function(e) { Loading
editor/html/js/preview.js +10 −2 Original line number Diff line number Diff line Loading @@ -5,13 +5,21 @@ var Preview = (function() { 'use strict'; function getThemeStyle() { var theme = document.documentElement.getAttribute('data-theme') || 'dark'; if (theme === 'light') { return 'body{font-family:sans-serif;padding:16px;margin:0;background:#fff;color:#1e1e2e;}'; } return 'body{font-family:sans-serif;padding:16px;margin:0;background:#1e1e2e;color:#cdd6f4;}'; } function refresh() { EditorApi.getPreview().then(function(resp) { var iframe = document.getElementById('preview-frame'); var doc = iframe.contentDocument || iframe.contentWindow.document; doc.open(); doc.write('<!DOCTYPE html><html><head><meta charset="UTF-8">' + '<style>body{font-family:sans-serif;padding:16px;margin:0;}</style>' + '<style>' + getThemeStyle() + '</style>' + '</head><body>' + (resp.html || '') + '</body></html>'); doc.close(); }).catch(function(err) { Loading @@ -24,7 +32,7 @@ var Preview = (function() { var doc = iframe.contentDocument || iframe.contentWindow.document; doc.open(); doc.write('<!DOCTYPE html><html><head><meta charset="UTF-8">' + '<style>body{font-family:sans-serif;padding:40px;text-align:center;' + '<style>' + getThemeStyle() + ' body{padding:40px;text-align:center;' + 'color:#666;}</style></head><body>' + '<p>Vorschau wird hier angezeigt.</p></body></html>'); doc.close(); Loading
src/translations.h +2 −0 Original line number Diff line number Diff line Loading @@ -367,6 +367,7 @@ namespace blogi { {"I18N_PASSWORD", "Passwort"}, {"I18N_LOGIN_REQUIRED", "Benutzername und Passwort eingeben"}, // Connection manager {"I18N_CONNECTION", "Verbindung"}, {"I18N_CONNECTIONS", "Verbindungen"}, {"I18N_CONN_GROUP", "Gruppe"}, {"I18N_IGNORE_SSL", "SSL ignorieren"}, Loading Loading @@ -810,6 +811,7 @@ namespace blogi { {"I18N_PASSWORD", "Password"}, {"I18N_LOGIN_REQUIRED", "Please enter username and password"}, // Connection manager {"I18N_CONNECTION", "Connection"}, {"I18N_CONNECTIONS", "Connections"}, {"I18N_CONN_GROUP", "Group"}, {"I18N_IGNORE_SSL", "Ignore SSL"}, Loading