Add on-demand CSS: registry, pre-computed component classes, header compression
Some checks failed
Build and Deploy / build-and-deploy (push) Failing after 42s
Some checks failed
Build and Deploy / build-and-deploy (push) Failing after 42s
- Parse tw.css into per-class lookup registry at startup
- Pre-scan component CSS classes at registration time (avoid per-request regex)
- Compress SX-Css header: 8-char hash replaces full class list (LRU cache)
- Add ;@css comment annotation for dynamically constructed class names
- Safelist bg-sky-{100..400} in Tailwind config for menu-row-sx dynamic shades
- Client sends/receives hash, falls back gracefully on cache miss
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1520,6 +1520,10 @@
|
||||
});
|
||||
if (loadedNames.length) headers["SX-Components"] = loadedNames.join(",");
|
||||
|
||||
// Send known CSS classes so server only sends new rules
|
||||
var cssHeader = _getSxCssHeader();
|
||||
if (cssHeader) headers["SX-Css"] = cssHeader;
|
||||
|
||||
// Extra headers from sx-headers
|
||||
var extraH = el.getAttribute("sx-headers");
|
||||
if (extraH) {
|
||||
@@ -1647,6 +1651,8 @@
|
||||
// Strip and load any <script type="text/sx" data-components> blocks
|
||||
text = text.replace(/<script[^>]*type="text\/sx"[^>]*data-components[^>]*>([\s\S]*?)<\/script>/gi,
|
||||
function (_, defs) { Sx.loadComponents(defs); return ""; });
|
||||
// Process on-demand CSS: extract <style data-sx-css> and inject into head
|
||||
text = _processCssResponse(text, resp);
|
||||
var sxSource = text.trim();
|
||||
|
||||
// Parse and render to live DOM nodes (skip renderToString + DOMParser)
|
||||
@@ -2150,9 +2156,12 @@
|
||||
var main = document.getElementById("main-panel");
|
||||
if (!main) { location.reload(); return; }
|
||||
|
||||
var histOpts = {
|
||||
headers: { "SX-Request": "true", "SX-History-Restore": "true" }
|
||||
};
|
||||
var histHeaders = { "SX-Request": "true", "SX-History-Restore": "true" };
|
||||
var cssH = _getSxCssHeader();
|
||||
if (cssH) histHeaders["SX-Css"] = cssH;
|
||||
var loadedN = Object.keys(_componentEnv).filter(function (k) { return k.charAt(0) === "~"; });
|
||||
if (loadedN.length) histHeaders["SX-Components"] = loadedN.join(",");
|
||||
var histOpts = { headers: histHeaders };
|
||||
try {
|
||||
var hHost = new URL(url, location.href).hostname;
|
||||
if (hHost !== location.hostname &&
|
||||
@@ -2162,11 +2171,15 @@
|
||||
} catch (e) {}
|
||||
|
||||
fetch(url, histOpts).then(function (resp) {
|
||||
return resp.text();
|
||||
}).then(function (text) {
|
||||
return resp.text().then(function (t) { return { text: t, resp: resp }; });
|
||||
}).then(function (r) {
|
||||
var text = r.text;
|
||||
var resp = r.resp;
|
||||
// Strip and load any <script type="text/sx" data-components> blocks
|
||||
text = text.replace(/<script[^>]*type="text\/sx"[^>]*data-components[^>]*>([\s\S]*?)<\/script>/gi,
|
||||
function (_, defs) { Sx.loadComponents(defs); return ""; });
|
||||
// Process on-demand CSS
|
||||
text = _processCssResponse(text, resp);
|
||||
text = text.trim();
|
||||
|
||||
if (text.charAt(0) === "(") {
|
||||
@@ -2279,11 +2292,64 @@
|
||||
// Auto-init in browser
|
||||
// =========================================================================
|
||||
|
||||
Sx.VERSION = "2026-03-01b-debug";
|
||||
Sx.VERSION = "2026-03-01c-cssx";
|
||||
|
||||
// CSS class tracking for on-demand CSS delivery
|
||||
var _sxCssKnown = {};
|
||||
var _sxCssHash = ""; // 8-char hex hash from server
|
||||
|
||||
function _initCssTracking() {
|
||||
var meta = document.querySelector('meta[name="sx-css-classes"]');
|
||||
if (meta) {
|
||||
var content = meta.getAttribute("content");
|
||||
if (content) {
|
||||
// If content is short (≤16 chars), it's a hash from the server
|
||||
if (content.length <= 16) {
|
||||
_sxCssHash = content;
|
||||
} else {
|
||||
content.split(",").forEach(function (c) {
|
||||
if (c) _sxCssKnown[c] = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _getSxCssHeader() {
|
||||
// Prefer sending the hash (compact) over the full class list
|
||||
if (_sxCssHash) return _sxCssHash;
|
||||
var names = Object.keys(_sxCssKnown);
|
||||
return names.length ? names.join(",") : "";
|
||||
}
|
||||
|
||||
function _processCssResponse(text, resp) {
|
||||
// Read SX-Css-Hash response header — replaces local hash
|
||||
var hashHeader = resp.headers.get("SX-Css-Hash");
|
||||
if (hashHeader) _sxCssHash = hashHeader;
|
||||
|
||||
// Merge SX-Css-Add header into known set (kept for debugging/fallback)
|
||||
var addHeader = resp.headers.get("SX-Css-Add");
|
||||
if (addHeader) {
|
||||
addHeader.split(",").forEach(function (c) {
|
||||
if (c) _sxCssKnown[c] = true;
|
||||
});
|
||||
}
|
||||
// Extract <style data-sx-css>...</style> blocks and inject into <style id="sx-css">
|
||||
var cssTarget = document.getElementById("sx-css");
|
||||
if (cssTarget) {
|
||||
text = text.replace(/<style[^>]*data-sx-css[^>]*>([\s\S]*?)<\/style>/gi,
|
||||
function (_, css) {
|
||||
cssTarget.textContent += css;
|
||||
return "";
|
||||
});
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
if (typeof document !== "undefined") {
|
||||
var init = function () {
|
||||
console.log("[sx.js] v" + Sx.VERSION + " init");
|
||||
_initCssTracking();
|
||||
Sx.processScripts();
|
||||
Sx.hydrate();
|
||||
SxEngine.process();
|
||||
|
||||
Reference in New Issue
Block a user