Loading editor/html/css/editor.css +123 −0 Original line number Diff line number Diff line Loading @@ -395,6 +395,129 @@ body { padding-top: 4px; } /* Rich Text Editor */ .rte-wrap { border: 1px solid var(--border); border-radius: 3px; overflow: hidden; } .rte-toolbar { display: flex; flex-wrap: wrap; gap: 1px; padding: 3px; background: var(--bg-tertiary); border-bottom: 1px solid var(--border); align-items: center; } .rte-btn { background: none; border: 1px solid transparent; color: var(--text-primary); cursor: pointer; padding: 2px 5px; font-size: 12px; border-radius: 2px; min-width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center; font-family: inherit; } .rte-btn:hover { background: var(--bg-primary); border-color: var(--border); } .rte-btn.active { background: var(--accent); color: var(--bg-primary); } .rte-sep { width: 1px; height: 18px; background: var(--border); margin: 0 2px; display: inline-block; } .rte-editor { min-height: 120px; max-height: 400px; overflow-y: auto; padding: 8px; background: var(--bg-primary); color: var(--text-primary); font-size: 13px; line-height: 1.5; outline: none; } .rte-editor:focus { box-shadow: inset 0 0 0 1px var(--accent); } .rte-editor table { border-collapse: collapse; width: 100%; margin: 4px 0; } .rte-editor th, .rte-editor td { border: 1px solid var(--border); padding: 4px 6px; min-width: 30px; } .rte-editor th { background: var(--bg-tertiary); font-weight: 600; } .rte-editor h1, .rte-editor h2, .rte-editor h3, .rte-editor h4 { margin: 0.3em 0; } .rte-editor ul, .rte-editor ol { padding-left: 1.5em; margin: 0.3em 0; } .rte-editor a { color: var(--accent); } .rte-source { width: 100%; min-height: 120px; padding: 8px; background: var(--bg-primary); color: var(--text-primary); border: none; font-family: monospace; font-size: 12px; resize: vertical; box-sizing: border-box; } .rte-color-input { position: absolute; left: 0; top: 100%; width: 0; height: 0; padding: 0; border: 0; opacity: 0; pointer-events: none; } /* Dialogs */ dialog { background: var(--bg-secondary); Loading editor/html/js/properties.js +185 −1 Original line number Diff line number Diff line Loading @@ -193,10 +193,194 @@ var PropertiesPanel = (function() { input.value = value; input.placeholder = field.placeholder || '#000000 oder var(--name)'; } else if (field.type === 'html' || field.type === 'richtext') { var rteWrap = document.createElement('div'); rteWrap.className = 'rte-wrap'; // Toolbar var toolbar = document.createElement('div'); toolbar.className = 'rte-toolbar'; var toolbarDefs = [ { cmd: 'bold', icon: 'B', title: 'Fett', style: 'font-weight:bold' }, { cmd: 'italic', icon: 'I', title: 'Kursiv', style: 'font-style:italic' }, { cmd: 'underline', icon: 'U', title: 'Unterstrichen', style: 'text-decoration:underline' }, { cmd: 'strikeThrough', icon: 'S', title: 'Durchgestrichen', style: 'text-decoration:line-through' }, { sep: true }, { cmd: 'formatBlock', val: 'h1', icon: 'H1', title: 'Überschrift 1' }, { cmd: 'formatBlock', val: 'h2', icon: 'H2', title: 'Überschrift 2' }, { cmd: 'formatBlock', val: 'h3', icon: 'H3', title: 'Überschrift 3' }, { cmd: 'formatBlock', val: 'h4', icon: 'H4', title: 'Überschrift 4' }, { cmd: 'formatBlock', val: 'p', icon: 'P', title: 'Absatz' }, { sep: true }, { cmd: 'insertUnorderedList', icon: '\u2022', title: 'Aufzählung' }, { cmd: 'insertOrderedList', icon: '1.', title: 'Nummerierung' }, { sep: true }, { cmd: 'justifyLeft', icon: '\u2261', title: 'Links' }, { cmd: 'justifyCenter', icon: '\u2263', title: 'Zentriert' }, { cmd: 'justifyRight', icon: '\u2262', title: 'Rechts' }, { sep: true }, { cmd: 'createLink', icon: '\uD83D\uDD17', title: 'Link einfügen', prompt: true }, { cmd: 'unlink', icon: '\u2718', title: 'Link entfernen' }, { sep: true }, { custom: 'foreColor', icon: 'A\u25BC', title: 'Textfarbe' }, { custom: 'table', icon: '\u25A6', title: 'Tabelle einfügen' }, { sep: true }, { cmd: 'removeFormat', icon: '\u2205', title: 'Formatierung entfernen' }, { custom: 'source', icon: '</>', title: 'Quelltext' } ]; var editorDiv = document.createElement('div'); editorDiv.className = 'rte-editor'; editorDiv.contentEditable = 'true'; editorDiv.innerHTML = value; var hiddenInput = document.createElement('input'); hiddenInput.type = 'hidden'; hiddenInput.name = field.key; hiddenInput.value = value; var sourceMode = false; var sourceArea = document.createElement('textarea'); sourceArea.className = 'rte-source'; sourceArea.style.display = 'none'; sourceArea.rows = 8; // Sync editor -> hidden input editorDiv.addEventListener('input', function() { hiddenInput.value = editorDiv.innerHTML; }); sourceArea.addEventListener('input', function() { hiddenInput.value = sourceArea.value; }); for (var ti = 0; ti < toolbarDefs.length; ti++) { var def = toolbarDefs[ti]; if (def.sep) { var sep = document.createElement('span'); sep.className = 'rte-sep'; toolbar.appendChild(sep); continue; } var btn = document.createElement('button'); btn.type = 'button'; btn.className = 'rte-btn'; btn.innerHTML = def.icon; btn.title = def.title; if (def.style) btn.setAttribute('style', def.style); if (def.custom === 'foreColor') { (function(b, ed, hi) { // Color picker with RGBA support var colorWrap = document.createElement('span'); colorWrap.style.position = 'relative'; colorWrap.style.display = 'inline-block'; var colorInput = document.createElement('input'); colorInput.type = 'color'; colorInput.className = 'rte-color-input'; colorInput.value = '#000000'; colorInput.addEventListener('input', function() { ed.focus(); document.execCommand('foreColor', false, colorInput.value); hi.value = ed.innerHTML; }); b.addEventListener('click', function(e) { e.preventDefault(); colorInput.click(); }); colorWrap.appendChild(b); colorWrap.appendChild(colorInput); toolbar.appendChild(colorWrap); })(btn, editorDiv, hiddenInput); continue; } else if (def.custom === 'table') { (function(b, ed, hi) { b.addEventListener('click', function(e) { e.preventDefault(); var rows = prompt('Zeilen:', '3'); if (!rows) return; var cols = prompt('Spalten:', '3'); if (!cols) return; rows = parseInt(rows, 10) || 3; cols = parseInt(cols, 10) || 3; var html = '<table border="1" style="border-collapse:collapse;width:100%">'; for (var r = 0; r < rows; r++) { html += '<tr>'; for (var c = 0; c < cols; c++) { var tag = r === 0 ? 'th' : 'td'; html += '<' + tag + ' style="padding:4px;border:1px solid #999"> </' + tag + '>'; } html += '</tr>'; } html += '</table>'; ed.focus(); document.execCommand('insertHTML', false, html); hi.value = ed.innerHTML; }); })(btn, editorDiv, hiddenInput); } else if (def.custom === 'source') { (function(b, ed, sa, hi) { b.addEventListener('click', function(e) { e.preventDefault(); sourceMode = !sourceMode; if (sourceMode) { sa.value = ed.innerHTML; ed.style.display = 'none'; sa.style.display = ''; b.classList.add('active'); } else { ed.innerHTML = sa.value; hi.value = sa.value; sa.style.display = 'none'; ed.style.display = ''; b.classList.remove('active'); } }); })(btn, editorDiv, sourceArea, hiddenInput); } else if (def.prompt) { (function(b, d, ed, hi) { b.addEventListener('click', function(e) { e.preventDefault(); var url = prompt('URL:', 'https://'); if (url) { ed.focus(); document.execCommand(d.cmd, false, url); hi.value = ed.innerHTML; } }); })(btn, def, editorDiv, hiddenInput); } else { (function(b, d, ed, hi) { b.addEventListener('click', function(e) { e.preventDefault(); ed.focus(); if (d.val) { document.execCommand(d.cmd, false, d.val); } else { document.execCommand(d.cmd, false, null); } hi.value = ed.innerHTML; }); })(btn, def, editorDiv, hiddenInput); } if (!def.custom || def.custom !== 'foreColor') { toolbar.appendChild(btn); } } rteWrap.appendChild(toolbar); rteWrap.appendChild(editorDiv); rteWrap.appendChild(sourceArea); rteWrap.appendChild(hiddenInput); group.appendChild(rteWrap); panel.appendChild(group); continue; } else if (field.type === 'textarea') { input = document.createElement('textarea'); input.name = field.key; input.value = value; input.rows = 6; input.rows = 4; input.style.fontFamily = 'monospace'; if (field.placeholder) input.placeholder = field.placeholder; } else if (field.type === 'media_browse') { var wrapper = document.createElement('div'); Loading editor/widgets/textbox/textbox.cpp +11 −0 Original line number Diff line number Diff line Loading @@ -240,6 +240,17 @@ extern "C" { json_object *hf = json_object_array_get_idx(arr, json_object_array_length(arr) - 1); json_object_object_add(hf, "default", json_object_new_boolean(false)); } // Mobile overrides addField("m_width", "Width", "text", "e.g. 100%", "mobile"); addField("m_height", "Height", "text", "e.g. auto", "mobile"); addField("m_font_size", "Font Size", "text", "e.g. 14px", "mobile"); addField("m_font_color", "Font Color", "color_var", nullptr, "mobile"); addField("m_text_align", "Text Align", "select", nullptr, "mobile", mkOpts({"left","center","right","justify"})); addField("m_hidden", "Hidden", "checkbox", nullptr, "mobile"); { json_object *hf = json_object_array_get_idx(arr, json_object_array_length(arr) - 1); json_object_object_add(hf, "default", json_object_new_boolean(false)); } addField("custom_css", "Custom CSS", "textarea", ".we-xxx { color: red; }"); return arr; } Loading Loading
editor/html/css/editor.css +123 −0 Original line number Diff line number Diff line Loading @@ -395,6 +395,129 @@ body { padding-top: 4px; } /* Rich Text Editor */ .rte-wrap { border: 1px solid var(--border); border-radius: 3px; overflow: hidden; } .rte-toolbar { display: flex; flex-wrap: wrap; gap: 1px; padding: 3px; background: var(--bg-tertiary); border-bottom: 1px solid var(--border); align-items: center; } .rte-btn { background: none; border: 1px solid transparent; color: var(--text-primary); cursor: pointer; padding: 2px 5px; font-size: 12px; border-radius: 2px; min-width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center; font-family: inherit; } .rte-btn:hover { background: var(--bg-primary); border-color: var(--border); } .rte-btn.active { background: var(--accent); color: var(--bg-primary); } .rte-sep { width: 1px; height: 18px; background: var(--border); margin: 0 2px; display: inline-block; } .rte-editor { min-height: 120px; max-height: 400px; overflow-y: auto; padding: 8px; background: var(--bg-primary); color: var(--text-primary); font-size: 13px; line-height: 1.5; outline: none; } .rte-editor:focus { box-shadow: inset 0 0 0 1px var(--accent); } .rte-editor table { border-collapse: collapse; width: 100%; margin: 4px 0; } .rte-editor th, .rte-editor td { border: 1px solid var(--border); padding: 4px 6px; min-width: 30px; } .rte-editor th { background: var(--bg-tertiary); font-weight: 600; } .rte-editor h1, .rte-editor h2, .rte-editor h3, .rte-editor h4 { margin: 0.3em 0; } .rte-editor ul, .rte-editor ol { padding-left: 1.5em; margin: 0.3em 0; } .rte-editor a { color: var(--accent); } .rte-source { width: 100%; min-height: 120px; padding: 8px; background: var(--bg-primary); color: var(--text-primary); border: none; font-family: monospace; font-size: 12px; resize: vertical; box-sizing: border-box; } .rte-color-input { position: absolute; left: 0; top: 100%; width: 0; height: 0; padding: 0; border: 0; opacity: 0; pointer-events: none; } /* Dialogs */ dialog { background: var(--bg-secondary); Loading
editor/html/js/properties.js +185 −1 Original line number Diff line number Diff line Loading @@ -193,10 +193,194 @@ var PropertiesPanel = (function() { input.value = value; input.placeholder = field.placeholder || '#000000 oder var(--name)'; } else if (field.type === 'html' || field.type === 'richtext') { var rteWrap = document.createElement('div'); rteWrap.className = 'rte-wrap'; // Toolbar var toolbar = document.createElement('div'); toolbar.className = 'rte-toolbar'; var toolbarDefs = [ { cmd: 'bold', icon: 'B', title: 'Fett', style: 'font-weight:bold' }, { cmd: 'italic', icon: 'I', title: 'Kursiv', style: 'font-style:italic' }, { cmd: 'underline', icon: 'U', title: 'Unterstrichen', style: 'text-decoration:underline' }, { cmd: 'strikeThrough', icon: 'S', title: 'Durchgestrichen', style: 'text-decoration:line-through' }, { sep: true }, { cmd: 'formatBlock', val: 'h1', icon: 'H1', title: 'Überschrift 1' }, { cmd: 'formatBlock', val: 'h2', icon: 'H2', title: 'Überschrift 2' }, { cmd: 'formatBlock', val: 'h3', icon: 'H3', title: 'Überschrift 3' }, { cmd: 'formatBlock', val: 'h4', icon: 'H4', title: 'Überschrift 4' }, { cmd: 'formatBlock', val: 'p', icon: 'P', title: 'Absatz' }, { sep: true }, { cmd: 'insertUnorderedList', icon: '\u2022', title: 'Aufzählung' }, { cmd: 'insertOrderedList', icon: '1.', title: 'Nummerierung' }, { sep: true }, { cmd: 'justifyLeft', icon: '\u2261', title: 'Links' }, { cmd: 'justifyCenter', icon: '\u2263', title: 'Zentriert' }, { cmd: 'justifyRight', icon: '\u2262', title: 'Rechts' }, { sep: true }, { cmd: 'createLink', icon: '\uD83D\uDD17', title: 'Link einfügen', prompt: true }, { cmd: 'unlink', icon: '\u2718', title: 'Link entfernen' }, { sep: true }, { custom: 'foreColor', icon: 'A\u25BC', title: 'Textfarbe' }, { custom: 'table', icon: '\u25A6', title: 'Tabelle einfügen' }, { sep: true }, { cmd: 'removeFormat', icon: '\u2205', title: 'Formatierung entfernen' }, { custom: 'source', icon: '</>', title: 'Quelltext' } ]; var editorDiv = document.createElement('div'); editorDiv.className = 'rte-editor'; editorDiv.contentEditable = 'true'; editorDiv.innerHTML = value; var hiddenInput = document.createElement('input'); hiddenInput.type = 'hidden'; hiddenInput.name = field.key; hiddenInput.value = value; var sourceMode = false; var sourceArea = document.createElement('textarea'); sourceArea.className = 'rte-source'; sourceArea.style.display = 'none'; sourceArea.rows = 8; // Sync editor -> hidden input editorDiv.addEventListener('input', function() { hiddenInput.value = editorDiv.innerHTML; }); sourceArea.addEventListener('input', function() { hiddenInput.value = sourceArea.value; }); for (var ti = 0; ti < toolbarDefs.length; ti++) { var def = toolbarDefs[ti]; if (def.sep) { var sep = document.createElement('span'); sep.className = 'rte-sep'; toolbar.appendChild(sep); continue; } var btn = document.createElement('button'); btn.type = 'button'; btn.className = 'rte-btn'; btn.innerHTML = def.icon; btn.title = def.title; if (def.style) btn.setAttribute('style', def.style); if (def.custom === 'foreColor') { (function(b, ed, hi) { // Color picker with RGBA support var colorWrap = document.createElement('span'); colorWrap.style.position = 'relative'; colorWrap.style.display = 'inline-block'; var colorInput = document.createElement('input'); colorInput.type = 'color'; colorInput.className = 'rte-color-input'; colorInput.value = '#000000'; colorInput.addEventListener('input', function() { ed.focus(); document.execCommand('foreColor', false, colorInput.value); hi.value = ed.innerHTML; }); b.addEventListener('click', function(e) { e.preventDefault(); colorInput.click(); }); colorWrap.appendChild(b); colorWrap.appendChild(colorInput); toolbar.appendChild(colorWrap); })(btn, editorDiv, hiddenInput); continue; } else if (def.custom === 'table') { (function(b, ed, hi) { b.addEventListener('click', function(e) { e.preventDefault(); var rows = prompt('Zeilen:', '3'); if (!rows) return; var cols = prompt('Spalten:', '3'); if (!cols) return; rows = parseInt(rows, 10) || 3; cols = parseInt(cols, 10) || 3; var html = '<table border="1" style="border-collapse:collapse;width:100%">'; for (var r = 0; r < rows; r++) { html += '<tr>'; for (var c = 0; c < cols; c++) { var tag = r === 0 ? 'th' : 'td'; html += '<' + tag + ' style="padding:4px;border:1px solid #999"> </' + tag + '>'; } html += '</tr>'; } html += '</table>'; ed.focus(); document.execCommand('insertHTML', false, html); hi.value = ed.innerHTML; }); })(btn, editorDiv, hiddenInput); } else if (def.custom === 'source') { (function(b, ed, sa, hi) { b.addEventListener('click', function(e) { e.preventDefault(); sourceMode = !sourceMode; if (sourceMode) { sa.value = ed.innerHTML; ed.style.display = 'none'; sa.style.display = ''; b.classList.add('active'); } else { ed.innerHTML = sa.value; hi.value = sa.value; sa.style.display = 'none'; ed.style.display = ''; b.classList.remove('active'); } }); })(btn, editorDiv, sourceArea, hiddenInput); } else if (def.prompt) { (function(b, d, ed, hi) { b.addEventListener('click', function(e) { e.preventDefault(); var url = prompt('URL:', 'https://'); if (url) { ed.focus(); document.execCommand(d.cmd, false, url); hi.value = ed.innerHTML; } }); })(btn, def, editorDiv, hiddenInput); } else { (function(b, d, ed, hi) { b.addEventListener('click', function(e) { e.preventDefault(); ed.focus(); if (d.val) { document.execCommand(d.cmd, false, d.val); } else { document.execCommand(d.cmd, false, null); } hi.value = ed.innerHTML; }); })(btn, def, editorDiv, hiddenInput); } if (!def.custom || def.custom !== 'foreColor') { toolbar.appendChild(btn); } } rteWrap.appendChild(toolbar); rteWrap.appendChild(editorDiv); rteWrap.appendChild(sourceArea); rteWrap.appendChild(hiddenInput); group.appendChild(rteWrap); panel.appendChild(group); continue; } else if (field.type === 'textarea') { input = document.createElement('textarea'); input.name = field.key; input.value = value; input.rows = 6; input.rows = 4; input.style.fontFamily = 'monospace'; if (field.placeholder) input.placeholder = field.placeholder; } else if (field.type === 'media_browse') { var wrapper = document.createElement('div'); Loading
editor/widgets/textbox/textbox.cpp +11 −0 Original line number Diff line number Diff line Loading @@ -240,6 +240,17 @@ extern "C" { json_object *hf = json_object_array_get_idx(arr, json_object_array_length(arr) - 1); json_object_object_add(hf, "default", json_object_new_boolean(false)); } // Mobile overrides addField("m_width", "Width", "text", "e.g. 100%", "mobile"); addField("m_height", "Height", "text", "e.g. auto", "mobile"); addField("m_font_size", "Font Size", "text", "e.g. 14px", "mobile"); addField("m_font_color", "Font Color", "color_var", nullptr, "mobile"); addField("m_text_align", "Text Align", "select", nullptr, "mobile", mkOpts({"left","center","right","justify"})); addField("m_hidden", "Hidden", "checkbox", nullptr, "mobile"); { json_object *hf = json_object_array_get_idx(arr, json_object_array_length(arr) - 1); json_object_object_add(hf, "default", json_object_new_boolean(false)); } addField("custom_css", "Custom CSS", "textarea", ".we-xxx { color: red; }"); return arr; } Loading