Commit afe4f362 authored by jan.koester's avatar jan.koester
Browse files

test

parent 45f67723
Loading
Loading
Loading
Loading
+142 −26
Changes for editor/html/css/editor.css: 142 added lines, 26 removed lines.
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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 {
+31 −13
Changes for editor/html/index.html: 31 added lines, 13 removed lines.
Original line number Diff line number Diff line
@@ -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">&#x2191;</button>
                    <button id="btn-move-down" data-i18n-title="I18N_MOVE_DOWN">&#x2193;</button>
                    <button id="btn-indent" data-i18n-title="I18N_INDENT">&#x2192;</button>
                    <button id="btn-outdent" data-i18n-title="I18N_OUTDENT">&#x2190;</button>
                    <button id="btn-copy-el" data-i18n-title="I18N_COPY_ELEMENT">&#x2398;</button>
                    <button id="btn-paste-el" data-i18n-title="I18N_PASTE_ELEMENT">&#x1F4CB;</button>
                    <button id="btn-remove" data-i18n-title="I18N_REMOVE_ELEMENT">&#x2716;</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">&#x1F504; <span data-i18n="I18N_VIEW">Vorschau</span></button>
@@ -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">&#x2191;</button>
            <button id="btn-move-down" data-i18n-title="I18N_MOVE_DOWN">&#x2193;</button>
            <button id="btn-indent" data-i18n-title="I18N_INDENT">&#x2192;</button>
            <button id="btn-outdent" data-i18n-title="I18N_OUTDENT">&#x2190;</button>
            <button id="btn-remove" data-i18n-title="I18N_REMOVE_ELEMENT">&#x2716;</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>
@@ -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>
+10 −0
Changes for editor/html/js/canvas.js: 10 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -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;
    }
@@ -169,6 +178,7 @@ var Canvas = (function() {
    return {
        getSelectedUuid: getSelectedUuid,
        clearSelection: clearSelection,
        setSelectedUuid: setSelectedUuid,
        canSelectedHaveChildren: canSelectedHaveChildren,
        setOnSelect: setOnSelect,
        setOnChange: setOnChange,
+67 −11
Changes for editor/html/js/editor.js: 67 added lines, 11 removed lines.
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
(function() {
    'use strict';

    var plugins = [];
    var currentDocId = '';
    var currentDocGroup = '';
    var saveDialogMode = 'save';
@@ -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); }
@@ -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() {
@@ -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);
@@ -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();
@@ -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();
            });
@@ -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) {
@@ -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) {
@@ -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) {
@@ -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) {
@@ -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) {
@@ -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();
            });
@@ -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();
                });
@@ -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();
                });
@@ -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) {
+4 −26
Changes for editor/html/js/properties.js: 4 added lines, 26 removed lines.
Original line number Diff line number Diff line
@@ -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();
@@ -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
@@ -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 = '';
@@ -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