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

test

parent 5847d1eb
Loading
Loading
Loading
Loading
+2.69 KiB (22.5 KiB)

File changed.

No diff preview for this file type.

+4 −0
Changes for editor/html/index.html: 4 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -296,6 +296,10 @@
            <label>Album</label>
            <select id="media-album-select"><option value="">-- Album --</option></select>
        </div>
        <div class="set-field media-upload-row">
            <input type="file" id="media-upload-input" multiple>
            <button type="button" id="btn-media-upload" data-i18n="Upload">Hochladen</button>
        </div>
        <div id="media-grid" class="media-grid"></div>
        <span id="media-status" class="set-status"></span>
        <div class="dialog-actions">
+53 −0
Changes for editor/html/js/media-browser.js: 53 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -12,6 +12,8 @@ var MediaBrowser = (function() {
    var _albumSelect = null;
    var _grid = null;
    var _status = null;
    var _uploadInput = null;
    var _uploadBtn = null;
    var _bound = false;

    function _ensureElements() {
@@ -20,6 +22,8 @@ var MediaBrowser = (function() {
        _albumSelect = document.getElementById('media-album-select');
        _grid = document.getElementById('media-grid');
        _status = document.getElementById('media-status');
        _uploadInput = document.getElementById('media-upload-input');
        _uploadBtn = document.getElementById('btn-media-upload');
    }

    function _bind() {
@@ -40,6 +44,55 @@ var MediaBrowser = (function() {
            var albumId = _albumSelect.value;
            if (connId && albumId) _loadMedia(connId, albumId);
        });

        _uploadBtn.addEventListener('click', _uploadSelectedFiles);
    }

    function _fileToBase64(file) {
        return new Promise(function(resolve, reject) {
            var reader = new FileReader();
            reader.onload = function() {
                // Strip the "data:<mime>;base64," prefix — server wants raw base64.
                var result = reader.result || '';
                var idx = result.indexOf(',');
                resolve(idx >= 0 ? result.slice(idx + 1) : result);
            };
            reader.onerror = reject;
            reader.readAsDataURL(file);
        });
    }

    function _uploadSelectedFiles() {
        var connId = _connSelect.value;
        var albumId = _albumSelect.value;
        if (!connId || !albumId) {
            _status.textContent = 'Select a connection and album first';
            _status.className = 'set-status error';
            return;
        }
        var files = _uploadInput.files;
        if (!files || files.length === 0) return;

        _status.textContent = 'Uploading...';
        _status.className = 'set-status';

        var chain = Promise.resolve();
        Array.prototype.forEach.call(files, function(file) {
            chain = chain.then(function() {
                return _fileToBase64(file).then(function(b64) {
                    return EditorApi.mediaUpload(connId, albumId, file.name, b64);
                });
            });
        });

        chain.then(function() {
            _status.textContent = '';
            _uploadInput.value = '';
            _loadMedia(connId, albumId);
        }).catch(function(err) {
            _status.textContent = 'Upload failed: ' + (err.error || 'Unknown');
            _status.className = 'set-status error';
        });
    }

    function open(onSelect) {
+83 −0
Changes for editor/html/js/properties.js: 83 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -691,6 +691,7 @@ var PropertiesPanel = (function() {
                                            var srcInput = form.querySelector('input[name="src"]');
                                            if (srcInput) srcInput.value = mediaItem.url;
                                        }
                                        _seedNaturalSize(form, mediaItem.url);
                                    }
                                    inp.dispatchEvent(new Event('change'));
                                });
@@ -801,6 +802,88 @@ var PropertiesPanel = (function() {

            panel.appendChild(group);
        }

        _applyConditionalVisibility(panel, fields);
        _bindAspectRatioFields(panel, fields);
    }

    // Fields carrying a "visibleWhen: {key, value}" schema entry (e.g. Button's
    // link_url only makes sense when link_type === "url") are hidden until the
    // watched field's live value matches, and re-evaluated on every change —
    // the backend has emitted visibleWhen for a while, this is the frontend
    // half that was never wired up.
    function _applyConditionalVisibility(panel, fields) {
        for (var i = 0; i < fields.length; i++) {
            var field = fields[i];
            if (!field.visibleWhen || !field.visibleWhen.key) continue;

            var group = panel.querySelector('[name="' + field.key + '"]');
            if (!group) continue;
            group = group.closest('.prop-group');
            if (!group) continue;

            var watched = panel.querySelector('[name="' + field.visibleWhen.key + '"]');
            if (!watched) continue;

            var expected = String(field.visibleWhen.value);
            var evaluate = function(watchedEl, groupEl, expectedVal) {
                var actual = watchedEl.type === 'checkbox' ? String(watchedEl.checked) : watchedEl.value;
                groupEl.style.display = (actual === expectedVal) ? '' : 'none';
            };
            (function(watchedEl, groupEl, expectedVal) {
                evaluate(watchedEl, groupEl, expectedVal);
                watchedEl.addEventListener('input', function() { evaluate(watchedEl, groupEl, expectedVal); });
                watchedEl.addEventListener('change', function() { evaluate(watchedEl, groupEl, expectedVal); });
            })(watched, group, expected);
        }
    }

    // Loads the picked image off-DOM to read its natural pixel size, then
    // seeds real_width/real_height so a freshly-picked image gets sane
    // dimensions immediately instead of waiting for a save round-trip.
    function _seedNaturalSize(form, url) {
        if (!url) return;
        var wInput = form.querySelector('[name="real_width"]');
        var hInput = form.querySelector('[name="real_height"]');
        if (!wInput || !hInput) return;
        var probe = new Image();
        probe.onload = function() {
            wInput.value = probe.naturalWidth;
            hInput.value = probe.naturalHeight;
            wInput.setAttribute('data-prev', probe.naturalWidth);
            hInput.setAttribute('data-prev', probe.naturalHeight);
            wInput.dispatchEvent(new Event('input', {bubbles: true}));
        };
        probe.src = url;
    }

    // If a widget's schema has both real_width and real_height (currently only
    // Image), keep them in proportion: editing one recalculates the other from
    // whatever aspect ratio the two fields' last known values describe.
    function _bindAspectRatioFields(panel, fields) {
        var wInput = panel.querySelector('[name="real_width"]');
        var hInput = panel.querySelector('[name="real_height"]');
        if (!wInput || !hInput) return;

        var syncing = false;
        function bind(source, target) {
            source.addEventListener('input', function() {
                if (syncing) return;
                var sVal = parseFloat(source.value), tVal = parseFloat(target.value);
                var sOld = parseFloat(source.getAttribute('data-prev') || sVal);
                if (!sVal || !tVal || !sOld) { source.setAttribute('data-prev', sVal || ''); return; }
                syncing = true;
                target.value = Math.round(tVal * (sVal / sOld));
                syncing = false;
                source.setAttribute('data-prev', sVal);
                wInput.setAttribute('data-prev', wInput.value);
                hInput.setAttribute('data-prev', hInput.value);
            });
        }
        wInput.setAttribute('data-prev', wInput.value);
        hInput.setAttribute('data-prev', hInput.value);
        bind(wInput, hInput);
        bind(hInput, wInput);
    }

    function saveProperties() {
+1 −14
Changes for editor/src/webedit_api.cpp: 1 added line, 14 removed lines.
Original line number Diff line number Diff line
@@ -479,9 +479,6 @@ void webedit::Api::loadDocumentTreeFromXml(DocumentState &doc, const std::string
        auto *el = createPluginByName(typeName,
                                      doc,
                                      typeIdAttr ? std::string(typeIdAttr) : std::string());
        std::cerr << "[llms-debug] load TOP <" << typeName << "> type_id="
                  << (typeIdAttr ? typeIdAttr : "(none)")
                  << " -> " << (el ? "OK" : "FAILED, skipped") << std::endl;
        if (!el)
            continue;

@@ -3903,8 +3900,6 @@ webedit::Api::PublishCoreResult webedit::Api::publishToConnectionCore(

    std::string html = renderTree(doc.root);
    std::string llmsTxt = generateLlmsTxt(doc.root);
    std::cerr << "[llms-debug] publish: html.size()=" << html.size()
              << " llmsTxt.size()=" << llmsTxt.size() << std::endl;

    // Build the publish command:
    // [{"authid":"..."}, {"command":"publish","target":"template","html":"...","params":{...}}]
@@ -4717,12 +4712,7 @@ namespace {
    // have its children walked (e.g. Accordion's panel content).
    void collectLlmsBlocks(const blogi::webedit::EditPlugin *el, std::vector<std::string> &blocks) {
        for (const blogi::webedit::EditPlugin *cur = el; cur; cur = cur->nextElement()) {
            auto block = cur->toLlmsBlock();
            std::cerr << "[llms-debug] visit " << cur->getName()
                      << " block=" << (block ? std::to_string(block->size()) + "bytes" : "nullopt")
                      << " child=" << (cur->getChildElement() ? "yes" : "no")
                      << " next=" << (cur->nextElement() ? "yes" : "no") << std::endl;
            if (block)
            if (auto block = cur->toLlmsBlock())
                appendBlock(blocks, *block);
            if (const blogi::webedit::EditPlugin *child = cur->getChildElement())
                collectLlmsBlocks(child, blocks);
@@ -4887,9 +4877,6 @@ void webedit::Api::loadChildrenFromXml(tinyxml2::XMLElement *parentXml,
        auto *childEl = createPluginByName(typeName,
                           doc,
                           typeIdAttr ? std::string(typeIdAttr) : std::string());
        std::cerr << "[llms-debug] load child <" << typeName << "> type_id="
                  << (typeIdAttr ? typeIdAttr : "(none)")
                  << " -> " << (childEl ? "OK" : "FAILED, skipped") << std::endl;
        if (!childEl) continue;

        childEl->fromXmlObject(childXml);
Loading