Loading editor/src/htmlimport.cpp +59 −3 Changes for editor/src/htmlimport.cpp: 59 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -1206,6 +1206,41 @@ std::string resolveSrcset(const std::string &baseUrl, const std::string &srcset) return out; } // Many real-world pages lazy-load images (lazysizes.js and similar // libraries all use this same convention): the real image lives in // data-src/data-srcset, while src/srcset hold a tiny placeholder (a blurred // low-res preview or a 1x1 transparent pixel) so the browser doesn't fetch // full-size images before they scroll into view. This importer never // scrolls or runs JS, so without this it would silently capture only that // placeholder -- prefer the data- variant whenever present. Unconditional // (unlike rewriteRelativeUrls below): this has nothing to do with baseUrl // resolution, so it applies the same whether the page was fetched from a // URL or pasted/uploaded. void preferLazyImageSrc(libhtmlpp::Element *node) { while (node) { int type = node->getType(); if (type == libhtmlpp::HtmlEl || type == libhtmlpp::SvgEL || type == libhtmlpp::TextAreaEL) { auto *hel = static_cast<libhtmlpp::HtmlElement*>(node); std::string tag = hel->getTagname(); std::transform(tag.begin(), tag.end(), tag.begin(), [](unsigned char c) { return std::tolower(c); }); if (tag == "img" || tag == "source") { std::string dataSrc = hel->getAtributte("data-src"); if (!dataSrc.empty()) hel->setAttribute("src", dataSrc); std::string dataSrcset = hel->getAtributte("data-srcset"); if (!dataSrcset.empty()) hel->setAttribute("srcset", dataSrcset); } preferLazyImageSrc(hel->firstChild()); } node = node->nextElement(); } } // Walks the whole parsed document (unlike collectTopLevelElements, this // includes <head> -- external stylesheets/scripts live there) and rewrites // every document-relative resource/link/style reference to absolute, in Loading Loading @@ -1612,6 +1647,13 @@ bool blogi::htmlimport::convertHtmlToWidgetXml(const std::string &html, if (sel.empty()) continue; bool isGlobal = false; // Only meaningful (and only set) when sel[0] == '@': the at-rule's // own "@media (...)"/"@supports (...)" wrapper and the selector // nested inside it. libhtmlpp::CSSStyleSheet flattens e.g. // "@media (max-width:768px) { :root { ... } }" into ONE CSSRule // whose getSelector() is the whole "@media (max-width:768px) :root" // string -- see the reconstruction below for why that matters. std::string atWrapper, innerSel; if (sel[0] == '@') { size_t parenDepth = 0; Loading @@ -1626,8 +1668,9 @@ bool blogi::htmlimport::convertHtmlToWidgetXml(const std::string &html, if (splitPos != std::string::npos && splitPos + 1 < sel.size()) { size_t innerStart = sel.find_first_not_of(" \t\n\r", splitPos); if (innerStart != std::string::npos) { std::string inner = sel.substr(innerStart); std::string innerLower = inner; atWrapper = sel.substr(0, splitPos); innerSel = sel.substr(innerStart); std::string innerLower = innerSel; std::transform(innerLower.begin(), innerLower.end(), innerLower.begin(), [](unsigned char c) { return std::tolower(c); }); Loading @@ -1652,7 +1695,20 @@ bool blogi::htmlimport::convertHtmlToWidgetXml(const std::string &html, } } if (isGlobal) { if (isGlobal && sel[0] == '@' && !innerSel.empty()) { // rule.serialize() just wraps getSelector() in a single "{ }" // pair -- fine for a plain selector, but here getSelector() is // the FULL flattened "@media (...) :root" string, so that would // emit "@media (...) :root { decls }", which is invalid CSS (the // at-rule needs its own wrapping braces around the nested rule). // Reconstruct the proper nested form instead, the same way // mergeStylesheetRules does for per-element @media matches. globalCss += atWrapper + " {\n " + innerSel + " {\n"; for (const auto &prop : rule.getDeclaration().getProperties()) { globalCss += " " + prop.getName() + ": " + prop.getValue() + ";\n"; } globalCss += " }\n}\n"; } else if (isGlobal) { globalCss += rule.serialize(true); globalCss += "\n"; } Loading Loading
editor/src/htmlimport.cpp +59 −3 Changes for editor/src/htmlimport.cpp: 59 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -1206,6 +1206,41 @@ std::string resolveSrcset(const std::string &baseUrl, const std::string &srcset) return out; } // Many real-world pages lazy-load images (lazysizes.js and similar // libraries all use this same convention): the real image lives in // data-src/data-srcset, while src/srcset hold a tiny placeholder (a blurred // low-res preview or a 1x1 transparent pixel) so the browser doesn't fetch // full-size images before they scroll into view. This importer never // scrolls or runs JS, so without this it would silently capture only that // placeholder -- prefer the data- variant whenever present. Unconditional // (unlike rewriteRelativeUrls below): this has nothing to do with baseUrl // resolution, so it applies the same whether the page was fetched from a // URL or pasted/uploaded. void preferLazyImageSrc(libhtmlpp::Element *node) { while (node) { int type = node->getType(); if (type == libhtmlpp::HtmlEl || type == libhtmlpp::SvgEL || type == libhtmlpp::TextAreaEL) { auto *hel = static_cast<libhtmlpp::HtmlElement*>(node); std::string tag = hel->getTagname(); std::transform(tag.begin(), tag.end(), tag.begin(), [](unsigned char c) { return std::tolower(c); }); if (tag == "img" || tag == "source") { std::string dataSrc = hel->getAtributte("data-src"); if (!dataSrc.empty()) hel->setAttribute("src", dataSrc); std::string dataSrcset = hel->getAtributte("data-srcset"); if (!dataSrcset.empty()) hel->setAttribute("srcset", dataSrcset); } preferLazyImageSrc(hel->firstChild()); } node = node->nextElement(); } } // Walks the whole parsed document (unlike collectTopLevelElements, this // includes <head> -- external stylesheets/scripts live there) and rewrites // every document-relative resource/link/style reference to absolute, in Loading Loading @@ -1612,6 +1647,13 @@ bool blogi::htmlimport::convertHtmlToWidgetXml(const std::string &html, if (sel.empty()) continue; bool isGlobal = false; // Only meaningful (and only set) when sel[0] == '@': the at-rule's // own "@media (...)"/"@supports (...)" wrapper and the selector // nested inside it. libhtmlpp::CSSStyleSheet flattens e.g. // "@media (max-width:768px) { :root { ... } }" into ONE CSSRule // whose getSelector() is the whole "@media (max-width:768px) :root" // string -- see the reconstruction below for why that matters. std::string atWrapper, innerSel; if (sel[0] == '@') { size_t parenDepth = 0; Loading @@ -1626,8 +1668,9 @@ bool blogi::htmlimport::convertHtmlToWidgetXml(const std::string &html, if (splitPos != std::string::npos && splitPos + 1 < sel.size()) { size_t innerStart = sel.find_first_not_of(" \t\n\r", splitPos); if (innerStart != std::string::npos) { std::string inner = sel.substr(innerStart); std::string innerLower = inner; atWrapper = sel.substr(0, splitPos); innerSel = sel.substr(innerStart); std::string innerLower = innerSel; std::transform(innerLower.begin(), innerLower.end(), innerLower.begin(), [](unsigned char c) { return std::tolower(c); }); Loading @@ -1652,7 +1695,20 @@ bool blogi::htmlimport::convertHtmlToWidgetXml(const std::string &html, } } if (isGlobal) { if (isGlobal && sel[0] == '@' && !innerSel.empty()) { // rule.serialize() just wraps getSelector() in a single "{ }" // pair -- fine for a plain selector, but here getSelector() is // the FULL flattened "@media (...) :root" string, so that would // emit "@media (...) :root { decls }", which is invalid CSS (the // at-rule needs its own wrapping braces around the nested rule). // Reconstruct the proper nested form instead, the same way // mergeStylesheetRules does for per-element @media matches. globalCss += atWrapper + " {\n " + innerSel + " {\n"; for (const auto &prop : rule.getDeclaration().getProperties()) { globalCss += " " + prop.getName() + ": " + prop.getValue() + ";\n"; } globalCss += " }\n}\n"; } else if (isGlobal) { globalCss += rule.serialize(true); globalCss += "\n"; } Loading