Loading editor/html/css/editor.css +142 −26 Changes for editor/html/css/editor.css: 142 added lines, 26 removed lines. Original line number Diff line number Diff line Loading @@ -94,15 +94,147 @@ body { font-size: 14px; } /* Main App Layout — the preview/canvas is the sole child now that the sidebar tree and docked properties panel have been replaced by the visual canvas (click/drag directly in the preview) and the floating properties panel. */ /* Main App Layout */ #app { display: flex; flex: 1; overflow: hidden; } /* Sidebars */ #sidebar-left, #sidebar-right { width: 280px; min-width: 200px; background: var(--bg-secondary); border-right: 1px solid var(--border); display: flex; flex-direction: column; overflow-y: auto; } #sidebar-right { border-right: none; border-left: 1px solid var(--border); } #sidebar-left h3, #sidebar-right h3 { font-size: 12px; text-transform: uppercase; letter-spacing: 1px; color: var(--text-muted); padding: 12px 12px 8px; border-bottom: 1px solid var(--border); margin: 0; } /* Widget Palette */ #widget-palette { flex-shrink: 0; } #widget-list { padding: 8px; display: flex; flex-wrap: wrap; gap: 4px; } .widget-btn { padding: 6px 10px; background: var(--bg-tertiary); color: var(--text-primary); border: 1px solid var(--border); border-radius: 4px; cursor: pointer; font-size: 12px; transition: all 0.15s; } .widget-btn:hover { background: var(--accent); color: var(--bg-primary); border-color: var(--accent); } /* Document Tree */ #document-tree-panel { flex: 1; display: flex; flex-direction: column; overflow: hidden; } #tree-actions { display: flex; gap: 4px; padding: 8px; border-bottom: 1px solid var(--border); } #tree-actions button { flex: 1; padding: 4px; background: var(--bg-tertiary); color: var(--text-primary); border: 1px solid var(--border); border-radius: 3px; cursor: pointer; font-size: 14px; } #tree-actions button:hover { background: var(--bg-hover); border-color: var(--accent); } #document-tree { flex: 1; overflow-y: auto; padding: 4px; } .tree-node { cursor: pointer; user-select: none; } .tree-node-header { display: flex; align-items: center; gap: 4px; padding: 4px 8px; border-radius: 3px; font-size: 13px; transition: background 0.1s; } .tree-node-header:hover { background: var(--bg-hover); } .tree-node-header.selected { background: var(--accent); color: var(--bg-primary); } .tree-node-toggle { width: 16px; font-size: 10px; text-align: center; color: var(--text-muted); } .tree-node-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .tree-children { padding-left: 16px; } /* Preview / visual canvas */ #preview-pane { flex: 1; Loading Loading @@ -139,9 +271,8 @@ body { overflow: auto; } /* Floating panels — the contextual properties panel and the widget-type insertion popover both anchor near whatever the user just clicked on the canvas, instead of living in a permanently-docked sidebar. */ /* Floating widget-type popover, opened by the canvas's hover "+" insertion points for precise in-place insertion directly in the preview. */ .floating-panel { position: fixed; z-index: 1000; Loading @@ -166,27 +297,12 @@ body { margin: 0; } .floating-panel-actions { display: flex; gap: 4px; padding: 8px; border-bottom: 1px solid var(--border); } .floating-panel-actions button { /* Properties Panel (docked in #sidebar-right) */ #properties-panel { flex: 1; padding: 4px; background: var(--bg-tertiary); color: var(--text-primary); border: 1px solid var(--border); border-radius: 3px; cursor: pointer; font-size: 14px; } .floating-panel-actions button:hover { background: var(--bg-hover); border-color: var(--accent); display: flex; flex-direction: column; overflow: hidden; } #properties-content { Loading editor/html/index.html +31 −13 Changes for editor/html/index.html: 31 added lines, 13 removed lines. Original line number Diff line number Diff line Loading @@ -61,7 +61,28 @@ </header> <div id="app"> <!-- Center: Preview / visual canvas (the primary editing surface) --> <!-- Left Sidebar: Widget Palette + Document Browser (tree) --> <aside id="sidebar-left"> <div id="widget-palette"> <h3 data-i18n="I18N_TOOLBOX">Widgets</h3> <div id="widget-list"></div> </div> <div id="document-tree-panel"> <h3 data-i18n="I18N_CONTENT_INDEX">Dokumentbaum</h3> <div id="tree-actions"> <button id="btn-move-up" data-i18n-title="I18N_MOVE_UP">↑</button> <button id="btn-move-down" data-i18n-title="I18N_MOVE_DOWN">↓</button> <button id="btn-indent" data-i18n-title="I18N_INDENT">→</button> <button id="btn-outdent" data-i18n-title="I18N_OUTDENT">←</button> <button id="btn-copy-el" data-i18n-title="I18N_COPY_ELEMENT">⎘</button> <button id="btn-paste-el" data-i18n-title="I18N_PASTE_ELEMENT">📋</button> <button id="btn-remove" data-i18n-title="I18N_REMOVE_ELEMENT">✖</button> </div> <div id="document-tree"></div> </div> </aside> <!-- Center: Preview / visual canvas --> <main id="preview-pane"> <div id="preview-toolbar"> <button id="btn-refresh-preview" data-i18n-title="I18N_TITLE_REFRESH_TREE">🔄 <span data-i18n="I18N_VIEW">Vorschau</span></button> Loading @@ -78,25 +99,21 @@ <div id="preview-publish-fields" style="display:none;padding:0 0.5em 0.4em;background:var(--bg-secondary);border-bottom:1px solid var(--border);font-size:0.8rem;"></div> <div id="preview-frame"></div> </main> </div> <!-- Floating contextual properties panel: shown near whatever element is selected on the canvas, replacing the old docked sidebar. --> <div id="floating-properties-panel" class="floating-panel" style="display:none"> <div id="floating-panel-actions" class="floating-panel-actions"> <button id="btn-move-up" data-i18n-title="I18N_MOVE_UP">↑</button> <button id="btn-move-down" data-i18n-title="I18N_MOVE_DOWN">↓</button> <button id="btn-indent" data-i18n-title="I18N_INDENT">→</button> <button id="btn-outdent" data-i18n-title="I18N_OUTDENT">←</button> <button id="btn-remove" data-i18n-title="I18N_REMOVE_ELEMENT">✖</button> </div> <!-- Right Sidebar: Properties of whatever is selected (in the tree or clicked directly in the preview) --> <aside id="sidebar-right"> <div id="properties-panel"> <h3 data-i18n="I18N_WIDGET_PROPERTIES">Eigenschaften</h3> <div id="properties-content"> <p class="hint" data-i18n="I18N_SELECT_WIDGET">Element auswählen um Eigenschaften zu bearbeiten.</p> </div> </div> </aside> </div> <!-- Widget-type popover: opened by the canvas's hover "+" insertion points. --> <!-- Widget-type popover: opened by the canvas's hover "+" insertion points (precise in-place insertion directly in the preview). --> <div id="widget-type-popover" class="floating-panel" style="display:none"> <div id="widget-type-list"></div> </div> Loading Loading @@ -478,6 +495,7 @@ <script src="js/i18n.js"></script> <script src="js/api.js"></script> <script src="js/tree.js"></script> <script src="js/canvas.js"></script> <script src="js/properties.js"></script> <script src="js/preview.js"></script> Loading editor/html/js/canvas.js +10 −0 Changes for editor/html/js/canvas.js: 10 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -24,6 +24,15 @@ var Canvas = (function() { selectedUuid = null; } // Sync the canvas's notion of "selected" when the selection was driven // from elsewhere (the document tree sidebar), so getSelectedUuid()/ // canSelectedHaveChildren() stay correct for copy/paste, the widget // palette, and the tree action buttons regardless of where the click // that produced the selection actually happened. function setSelectedUuid(uuid) { selectedUuid = uuid || null; } function setOnSelect(cb) { onSelectCallback = cb; } Loading Loading @@ -169,6 +178,7 @@ var Canvas = (function() { return { getSelectedUuid: getSelectedUuid, clearSelection: clearSelection, setSelectedUuid: setSelectedUuid, canSelectedHaveChildren: canSelectedHaveChildren, setOnSelect: setOnSelect, setOnChange: setOnChange, Loading editor/html/js/editor.js +67 −11 Changes for editor/html/js/editor.js: 67 added lines, 11 removed lines. Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ (function() { 'use strict'; var plugins = []; var currentDocId = ''; var currentDocGroup = ''; var saveDialogMode = 'save'; Loading @@ -19,6 +20,7 @@ initTheme(); I18n.load(function() { I18n.translatePage(); loadPlugins(); loadDocument(); try { bindToolbar(); } catch(e) { console.error('bindToolbar:', e); } try { bindTreeActions(); } catch(e) { console.error('bindTreeActions:', e); } Loading @@ -32,13 +34,66 @@ try { Canvas.bindMessageListener(); } catch(e) { console.error('Canvas.bindMessageListener:', e); } Preview.clear(); // Selection can be driven either by clicking directly on an // element in the live preview (Canvas) or by clicking a node in // the document browser sidebar (DocumentTree). Either path keeps // the other in sync and shows the matching properties. Canvas.setOnSelect(function(uuid, rect) { DocumentTree.highlightSelected(uuid); PropertiesPanel.showProperties(uuid, rect); }); DocumentTree.setOnSelect(function(uuid) { Canvas.setSelectedUuid(uuid); PropertiesPanel.showProperties(uuid); }); Canvas.setOnChange(refreshDocument); }); } // --- Load widget plugins --- function loadPlugins() { EditorApi.getPlugins().then(function(resp) { plugins = resp.plugins || []; renderWidgetPalette(); }).catch(function(err) { console.error('Failed to load plugins:', err); }); } function renderWidgetPalette() { var list = document.getElementById('widget-list'); list.innerHTML = ''; for (var i = 0; i < plugins.length; i++) { var btn = document.createElement('button'); btn.className = 'widget-btn'; btn.textContent = plugins[i].name; btn.setAttribute('data-plugin-name', plugins[i].name); btn.addEventListener('click', function() { addWidget(this.getAttribute('data-plugin-name')); }); list.appendChild(btn); } } // --- Add widget (from the sidebar palette) --- function addWidget(pluginName) { var parentUuid = Canvas.getSelectedUuid() || ''; var position = ''; if (parentUuid) { position = Canvas.canSelectedHaveChildren() ? 'child' : 'after'; } EditorApi.addElement(pluginName, parentUuid, position).then(function() { refreshDocument(); }).catch(function(err) { console.error('Failed to add widget:', err); alert(I18n.t('I18N_ACTION_FAILED') + ': ' + (err.error || I18n.t('I18N_ERR_ADD_FAILED'))); }); } // --- Theme --- function initTheme() { Loading Loading @@ -122,6 +177,7 @@ document.getElementById('doc-name').textContent = resp.name || 'Untitled'; currentDocId = resp.id || ''; Canvas.setTreeData(resp.tree || []); DocumentTree.render(resp.tree || []); Preview.refresh(); }).catch(function(err) { console.error('Failed to load document:', err); Loading @@ -137,7 +193,7 @@ function bindToolbar() { document.getElementById('btn-new').addEventListener('click', function() { EditorApi.newDocument().then(function() { Canvas.clearSelection(); Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); Preview.clear(); Loading Loading @@ -290,7 +346,7 @@ if (!uuid) return; if (!confirm(I18n.t('I18N_REMOVE_CONFIRM') + '?')) return; EditorApi.removeElement(uuid).then(function() { Canvas.clearSelection(); Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); }); Loading Loading @@ -364,7 +420,7 @@ var name = (file.name || '').toLowerCase(); if (name.endsWith('.zip')) { importZipTemplateFile(file).then(function() { Canvas.clearSelection(); Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); }).catch(function(err) { Loading @@ -379,7 +435,7 @@ var xml = e.target.result; if (!xml.trim()) return; EditorApi.importXml(xml).then(function() { Canvas.clearSelection(); Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); }).catch(function(err) { Loading @@ -395,7 +451,7 @@ if (!xml.trim()) return; EditorApi.importXml(xml).then(function() { document.getElementById('import-dialog').close(); Canvas.clearSelection(); Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); }).catch(function(err) { Loading @@ -421,7 +477,7 @@ var html = e.target.result; if (!html.trim()) return; EditorApi.importHtml(html).then(function() { Canvas.clearSelection(); Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); }).catch(function(err) { Loading @@ -437,7 +493,7 @@ if (!html.trim()) return; EditorApi.importHtml(html).then(function() { document.getElementById('import-html-dialog').close(); Canvas.clearSelection(); Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); }).catch(function(err) { Loading Loading @@ -958,7 +1014,7 @@ EditorApi.loadDocument(id).then(function(resp) { currentDocGroup = resp.group || ''; document.getElementById('open-dialog').close(); Canvas.clearSelection(); Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); }); Loading Loading @@ -1018,7 +1074,7 @@ EditorApi.loadRevision(revId).then(function() { document.getElementById('revisions-dialog').close(); document.getElementById('open-dialog').close(); Canvas.clearSelection(); Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); }); Loading @@ -1038,7 +1094,7 @@ EditorApi.restoreRevision(did, rid).then(function() { document.getElementById('revisions-dialog').close(); document.getElementById('open-dialog').close(); Canvas.clearSelection(); Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); }); Loading Loading @@ -1508,7 +1564,7 @@ EditorApi.importHtml(htmlContent, mode, parentUuid).then(function() { statusEl.remove(); addMessage('status', '\u2713 ' + I18n.t('I18N_IMPORT_HTML', 'Importiert')); Canvas.clearSelection(); Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); }).catch(function(err) { Loading editor/html/js/properties.js +4 −26 Changes for editor/html/js/properties.js: 4 added lines, 26 removed lines. Original line number Diff line number Diff line Loading @@ -10,10 +10,10 @@ var PropertiesPanel = (function() { var saveTimer = null; var isSaving = false; // rect is the selected element's bounding box in *parent-page* coordinates // (the canvas module already translates the iframe-relative rect it receives // from the injected canvas script by adding the iframe element's own offset). function showProperties(uuid, rect) { // The properties panel is docked in #sidebar-right; rect (formerly used to // position a floating panel near a canvas click) is no longer needed but // still accepted so callers (Canvas selection) don't need special-casing. function showProperties(uuid) { // Persist unsaved changes before switching to another element. if (currentUuid && uuid && currentUuid !== uuid) { saveProperties(); Loading @@ -21,19 +21,13 @@ var PropertiesPanel = (function() { currentUuid = uuid; if (typeof Preview !== 'undefined') Preview.highlight(uuid); var panel = document.getElementById('floating-properties-panel'); var container = document.getElementById('properties-content'); if (!uuid) { if (panel) panel.style.display = 'none'; container.innerHTML = '<p class="hint">Element auswählen um Eigenschaften zu bearbeiten.</p>'; return; } if (panel) { panel.style.display = 'block'; positionPanel(rect); } container.innerHTML = '<p class="hint">Lade...</p>'; // Load schema and properties in parallel Loading Loading @@ -62,20 +56,6 @@ var PropertiesPanel = (function() { }); } function positionPanel(rect) { var panel = document.getElementById('floating-properties-panel'); if (!panel || !rect) return; var margin = 12; var panelWidth = panel.offsetWidth || 320; var left = rect.left + margin; if (left + panelWidth > window.innerWidth - margin) { left = Math.max(margin, window.innerWidth - panelWidth - margin); } var top = Math.max(margin, rect.top); panel.style.left = left + 'px'; panel.style.top = top + 'px'; } function renderForm(schema, properties) { var container = document.getElementById('properties-content'); container.innerHTML = ''; Loading Loading @@ -893,8 +873,6 @@ var PropertiesPanel = (function() { currentUuid = null; currentSchema = null; if (typeof Preview !== 'undefined') Preview.highlight(null); var panel = document.getElementById('floating-properties-panel'); if (panel) panel.style.display = 'none'; var container = document.getElementById('properties-content'); container.innerHTML = '<p class="hint">Element auswählen um Eigenschaften zu bearbeiten.</p>'; } Loading Loading
editor/html/css/editor.css +142 −26 Changes for editor/html/css/editor.css: 142 added lines, 26 removed lines. Original line number Diff line number Diff line Loading @@ -94,15 +94,147 @@ body { font-size: 14px; } /* Main App Layout — the preview/canvas is the sole child now that the sidebar tree and docked properties panel have been replaced by the visual canvas (click/drag directly in the preview) and the floating properties panel. */ /* Main App Layout */ #app { display: flex; flex: 1; overflow: hidden; } /* Sidebars */ #sidebar-left, #sidebar-right { width: 280px; min-width: 200px; background: var(--bg-secondary); border-right: 1px solid var(--border); display: flex; flex-direction: column; overflow-y: auto; } #sidebar-right { border-right: none; border-left: 1px solid var(--border); } #sidebar-left h3, #sidebar-right h3 { font-size: 12px; text-transform: uppercase; letter-spacing: 1px; color: var(--text-muted); padding: 12px 12px 8px; border-bottom: 1px solid var(--border); margin: 0; } /* Widget Palette */ #widget-palette { flex-shrink: 0; } #widget-list { padding: 8px; display: flex; flex-wrap: wrap; gap: 4px; } .widget-btn { padding: 6px 10px; background: var(--bg-tertiary); color: var(--text-primary); border: 1px solid var(--border); border-radius: 4px; cursor: pointer; font-size: 12px; transition: all 0.15s; } .widget-btn:hover { background: var(--accent); color: var(--bg-primary); border-color: var(--accent); } /* Document Tree */ #document-tree-panel { flex: 1; display: flex; flex-direction: column; overflow: hidden; } #tree-actions { display: flex; gap: 4px; padding: 8px; border-bottom: 1px solid var(--border); } #tree-actions button { flex: 1; padding: 4px; background: var(--bg-tertiary); color: var(--text-primary); border: 1px solid var(--border); border-radius: 3px; cursor: pointer; font-size: 14px; } #tree-actions button:hover { background: var(--bg-hover); border-color: var(--accent); } #document-tree { flex: 1; overflow-y: auto; padding: 4px; } .tree-node { cursor: pointer; user-select: none; } .tree-node-header { display: flex; align-items: center; gap: 4px; padding: 4px 8px; border-radius: 3px; font-size: 13px; transition: background 0.1s; } .tree-node-header:hover { background: var(--bg-hover); } .tree-node-header.selected { background: var(--accent); color: var(--bg-primary); } .tree-node-toggle { width: 16px; font-size: 10px; text-align: center; color: var(--text-muted); } .tree-node-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .tree-children { padding-left: 16px; } /* Preview / visual canvas */ #preview-pane { flex: 1; Loading Loading @@ -139,9 +271,8 @@ body { overflow: auto; } /* Floating panels — the contextual properties panel and the widget-type insertion popover both anchor near whatever the user just clicked on the canvas, instead of living in a permanently-docked sidebar. */ /* Floating widget-type popover, opened by the canvas's hover "+" insertion points for precise in-place insertion directly in the preview. */ .floating-panel { position: fixed; z-index: 1000; Loading @@ -166,27 +297,12 @@ body { margin: 0; } .floating-panel-actions { display: flex; gap: 4px; padding: 8px; border-bottom: 1px solid var(--border); } .floating-panel-actions button { /* Properties Panel (docked in #sidebar-right) */ #properties-panel { flex: 1; padding: 4px; background: var(--bg-tertiary); color: var(--text-primary); border: 1px solid var(--border); border-radius: 3px; cursor: pointer; font-size: 14px; } .floating-panel-actions button:hover { background: var(--bg-hover); border-color: var(--accent); display: flex; flex-direction: column; overflow: hidden; } #properties-content { Loading
editor/html/index.html +31 −13 Changes for editor/html/index.html: 31 added lines, 13 removed lines. Original line number Diff line number Diff line Loading @@ -61,7 +61,28 @@ </header> <div id="app"> <!-- Center: Preview / visual canvas (the primary editing surface) --> <!-- Left Sidebar: Widget Palette + Document Browser (tree) --> <aside id="sidebar-left"> <div id="widget-palette"> <h3 data-i18n="I18N_TOOLBOX">Widgets</h3> <div id="widget-list"></div> </div> <div id="document-tree-panel"> <h3 data-i18n="I18N_CONTENT_INDEX">Dokumentbaum</h3> <div id="tree-actions"> <button id="btn-move-up" data-i18n-title="I18N_MOVE_UP">↑</button> <button id="btn-move-down" data-i18n-title="I18N_MOVE_DOWN">↓</button> <button id="btn-indent" data-i18n-title="I18N_INDENT">→</button> <button id="btn-outdent" data-i18n-title="I18N_OUTDENT">←</button> <button id="btn-copy-el" data-i18n-title="I18N_COPY_ELEMENT">⎘</button> <button id="btn-paste-el" data-i18n-title="I18N_PASTE_ELEMENT">📋</button> <button id="btn-remove" data-i18n-title="I18N_REMOVE_ELEMENT">✖</button> </div> <div id="document-tree"></div> </div> </aside> <!-- Center: Preview / visual canvas --> <main id="preview-pane"> <div id="preview-toolbar"> <button id="btn-refresh-preview" data-i18n-title="I18N_TITLE_REFRESH_TREE">🔄 <span data-i18n="I18N_VIEW">Vorschau</span></button> Loading @@ -78,25 +99,21 @@ <div id="preview-publish-fields" style="display:none;padding:0 0.5em 0.4em;background:var(--bg-secondary);border-bottom:1px solid var(--border);font-size:0.8rem;"></div> <div id="preview-frame"></div> </main> </div> <!-- Floating contextual properties panel: shown near whatever element is selected on the canvas, replacing the old docked sidebar. --> <div id="floating-properties-panel" class="floating-panel" style="display:none"> <div id="floating-panel-actions" class="floating-panel-actions"> <button id="btn-move-up" data-i18n-title="I18N_MOVE_UP">↑</button> <button id="btn-move-down" data-i18n-title="I18N_MOVE_DOWN">↓</button> <button id="btn-indent" data-i18n-title="I18N_INDENT">→</button> <button id="btn-outdent" data-i18n-title="I18N_OUTDENT">←</button> <button id="btn-remove" data-i18n-title="I18N_REMOVE_ELEMENT">✖</button> </div> <!-- Right Sidebar: Properties of whatever is selected (in the tree or clicked directly in the preview) --> <aside id="sidebar-right"> <div id="properties-panel"> <h3 data-i18n="I18N_WIDGET_PROPERTIES">Eigenschaften</h3> <div id="properties-content"> <p class="hint" data-i18n="I18N_SELECT_WIDGET">Element auswählen um Eigenschaften zu bearbeiten.</p> </div> </div> </aside> </div> <!-- Widget-type popover: opened by the canvas's hover "+" insertion points. --> <!-- Widget-type popover: opened by the canvas's hover "+" insertion points (precise in-place insertion directly in the preview). --> <div id="widget-type-popover" class="floating-panel" style="display:none"> <div id="widget-type-list"></div> </div> Loading Loading @@ -478,6 +495,7 @@ <script src="js/i18n.js"></script> <script src="js/api.js"></script> <script src="js/tree.js"></script> <script src="js/canvas.js"></script> <script src="js/properties.js"></script> <script src="js/preview.js"></script> Loading
editor/html/js/canvas.js +10 −0 Changes for editor/html/js/canvas.js: 10 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -24,6 +24,15 @@ var Canvas = (function() { selectedUuid = null; } // Sync the canvas's notion of "selected" when the selection was driven // from elsewhere (the document tree sidebar), so getSelectedUuid()/ // canSelectedHaveChildren() stay correct for copy/paste, the widget // palette, and the tree action buttons regardless of where the click // that produced the selection actually happened. function setSelectedUuid(uuid) { selectedUuid = uuid || null; } function setOnSelect(cb) { onSelectCallback = cb; } Loading Loading @@ -169,6 +178,7 @@ var Canvas = (function() { return { getSelectedUuid: getSelectedUuid, clearSelection: clearSelection, setSelectedUuid: setSelectedUuid, canSelectedHaveChildren: canSelectedHaveChildren, setOnSelect: setOnSelect, setOnChange: setOnChange, Loading
editor/html/js/editor.js +67 −11 Changes for editor/html/js/editor.js: 67 added lines, 11 removed lines. Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ (function() { 'use strict'; var plugins = []; var currentDocId = ''; var currentDocGroup = ''; var saveDialogMode = 'save'; Loading @@ -19,6 +20,7 @@ initTheme(); I18n.load(function() { I18n.translatePage(); loadPlugins(); loadDocument(); try { bindToolbar(); } catch(e) { console.error('bindToolbar:', e); } try { bindTreeActions(); } catch(e) { console.error('bindTreeActions:', e); } Loading @@ -32,13 +34,66 @@ try { Canvas.bindMessageListener(); } catch(e) { console.error('Canvas.bindMessageListener:', e); } Preview.clear(); // Selection can be driven either by clicking directly on an // element in the live preview (Canvas) or by clicking a node in // the document browser sidebar (DocumentTree). Either path keeps // the other in sync and shows the matching properties. Canvas.setOnSelect(function(uuid, rect) { DocumentTree.highlightSelected(uuid); PropertiesPanel.showProperties(uuid, rect); }); DocumentTree.setOnSelect(function(uuid) { Canvas.setSelectedUuid(uuid); PropertiesPanel.showProperties(uuid); }); Canvas.setOnChange(refreshDocument); }); } // --- Load widget plugins --- function loadPlugins() { EditorApi.getPlugins().then(function(resp) { plugins = resp.plugins || []; renderWidgetPalette(); }).catch(function(err) { console.error('Failed to load plugins:', err); }); } function renderWidgetPalette() { var list = document.getElementById('widget-list'); list.innerHTML = ''; for (var i = 0; i < plugins.length; i++) { var btn = document.createElement('button'); btn.className = 'widget-btn'; btn.textContent = plugins[i].name; btn.setAttribute('data-plugin-name', plugins[i].name); btn.addEventListener('click', function() { addWidget(this.getAttribute('data-plugin-name')); }); list.appendChild(btn); } } // --- Add widget (from the sidebar palette) --- function addWidget(pluginName) { var parentUuid = Canvas.getSelectedUuid() || ''; var position = ''; if (parentUuid) { position = Canvas.canSelectedHaveChildren() ? 'child' : 'after'; } EditorApi.addElement(pluginName, parentUuid, position).then(function() { refreshDocument(); }).catch(function(err) { console.error('Failed to add widget:', err); alert(I18n.t('I18N_ACTION_FAILED') + ': ' + (err.error || I18n.t('I18N_ERR_ADD_FAILED'))); }); } // --- Theme --- function initTheme() { Loading Loading @@ -122,6 +177,7 @@ document.getElementById('doc-name').textContent = resp.name || 'Untitled'; currentDocId = resp.id || ''; Canvas.setTreeData(resp.tree || []); DocumentTree.render(resp.tree || []); Preview.refresh(); }).catch(function(err) { console.error('Failed to load document:', err); Loading @@ -137,7 +193,7 @@ function bindToolbar() { document.getElementById('btn-new').addEventListener('click', function() { EditorApi.newDocument().then(function() { Canvas.clearSelection(); Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); Preview.clear(); Loading Loading @@ -290,7 +346,7 @@ if (!uuid) return; if (!confirm(I18n.t('I18N_REMOVE_CONFIRM') + '?')) return; EditorApi.removeElement(uuid).then(function() { Canvas.clearSelection(); Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); }); Loading Loading @@ -364,7 +420,7 @@ var name = (file.name || '').toLowerCase(); if (name.endsWith('.zip')) { importZipTemplateFile(file).then(function() { Canvas.clearSelection(); Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); }).catch(function(err) { Loading @@ -379,7 +435,7 @@ var xml = e.target.result; if (!xml.trim()) return; EditorApi.importXml(xml).then(function() { Canvas.clearSelection(); Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); }).catch(function(err) { Loading @@ -395,7 +451,7 @@ if (!xml.trim()) return; EditorApi.importXml(xml).then(function() { document.getElementById('import-dialog').close(); Canvas.clearSelection(); Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); }).catch(function(err) { Loading @@ -421,7 +477,7 @@ var html = e.target.result; if (!html.trim()) return; EditorApi.importHtml(html).then(function() { Canvas.clearSelection(); Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); }).catch(function(err) { Loading @@ -437,7 +493,7 @@ if (!html.trim()) return; EditorApi.importHtml(html).then(function() { document.getElementById('import-html-dialog').close(); Canvas.clearSelection(); Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); }).catch(function(err) { Loading Loading @@ -958,7 +1014,7 @@ EditorApi.loadDocument(id).then(function(resp) { currentDocGroup = resp.group || ''; document.getElementById('open-dialog').close(); Canvas.clearSelection(); Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); }); Loading Loading @@ -1018,7 +1074,7 @@ EditorApi.loadRevision(revId).then(function() { document.getElementById('revisions-dialog').close(); document.getElementById('open-dialog').close(); Canvas.clearSelection(); Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); }); Loading @@ -1038,7 +1094,7 @@ EditorApi.restoreRevision(did, rid).then(function() { document.getElementById('revisions-dialog').close(); document.getElementById('open-dialog').close(); Canvas.clearSelection(); Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); }); Loading Loading @@ -1508,7 +1564,7 @@ EditorApi.importHtml(htmlContent, mode, parentUuid).then(function() { statusEl.remove(); addMessage('status', '\u2713 ' + I18n.t('I18N_IMPORT_HTML', 'Importiert')); Canvas.clearSelection(); Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); }).catch(function(err) { Loading
editor/html/js/properties.js +4 −26 Changes for editor/html/js/properties.js: 4 added lines, 26 removed lines. Original line number Diff line number Diff line Loading @@ -10,10 +10,10 @@ var PropertiesPanel = (function() { var saveTimer = null; var isSaving = false; // rect is the selected element's bounding box in *parent-page* coordinates // (the canvas module already translates the iframe-relative rect it receives // from the injected canvas script by adding the iframe element's own offset). function showProperties(uuid, rect) { // The properties panel is docked in #sidebar-right; rect (formerly used to // position a floating panel near a canvas click) is no longer needed but // still accepted so callers (Canvas selection) don't need special-casing. function showProperties(uuid) { // Persist unsaved changes before switching to another element. if (currentUuid && uuid && currentUuid !== uuid) { saveProperties(); Loading @@ -21,19 +21,13 @@ var PropertiesPanel = (function() { currentUuid = uuid; if (typeof Preview !== 'undefined') Preview.highlight(uuid); var panel = document.getElementById('floating-properties-panel'); var container = document.getElementById('properties-content'); if (!uuid) { if (panel) panel.style.display = 'none'; container.innerHTML = '<p class="hint">Element auswählen um Eigenschaften zu bearbeiten.</p>'; return; } if (panel) { panel.style.display = 'block'; positionPanel(rect); } container.innerHTML = '<p class="hint">Lade...</p>'; // Load schema and properties in parallel Loading Loading @@ -62,20 +56,6 @@ var PropertiesPanel = (function() { }); } function positionPanel(rect) { var panel = document.getElementById('floating-properties-panel'); if (!panel || !rect) return; var margin = 12; var panelWidth = panel.offsetWidth || 320; var left = rect.left + margin; if (left + panelWidth > window.innerWidth - margin) { left = Math.max(margin, window.innerWidth - panelWidth - margin); } var top = Math.max(margin, rect.top); panel.style.left = left + 'px'; panel.style.top = top + 'px'; } function renderForm(schema, properties) { var container = document.getElementById('properties-content'); container.innerHTML = ''; Loading Loading @@ -893,8 +873,6 @@ var PropertiesPanel = (function() { currentUuid = null; currentSchema = null; if (typeof Preview !== 'undefined') Preview.highlight(null); var panel = document.getElementById('floating-properties-panel'); if (panel) panel.style.display = 'none'; var container = document.getElementById('properties-content'); container.innerHTML = '<p class="hint">Element auswählen um Eigenschaften zu bearbeiten.</p>'; } Loading