chore: update cdp parsing css layers

This commit is contained in:
2026-08-31 09:31:47 +05:00
parent 0b13d4c9e8
commit 9cd86104fc
2 changed files with 65 additions and 10 deletions
+50 -9
View File
@@ -223,36 +223,62 @@ function snapshotDocument({ PROPS, NOISE_TAGS, INHERITED }) {
function collectRules() { function collectRules() {
const rules = []; const rules = [];
let ruleOrder = 0; let ruleOrder = 0;
const collect = (list, parentSel = "") => { // Declared cascade-layer order, top-level names only, in declaration
// order (from `@layer a, b;` statements and first appearance of blocks).
// Unlayered author rules are a separate, always-winning tier.
const layerNames = [];
const ensureLayer = (name) => {
if (name && !layerNames.includes(name)) layerNames.push(name);
};
// `layers` is the cascade-layer path enclosing this rule; [] = unlayered.
const collect = (list, parentSel = "", layers = []) => {
for (const r of list || []) { for (const r of list || []) {
if (r == null) continue; if (r == null) continue;
// Layer rules don't expose a reliable numeric .type in this
// Chromium (they report 0), so detect them by constructor.
const ctor = r.constructor?.name;
if (ctor === "CSSLayerStatementRule") {
// @layer a, b; — declares layer order, carries no declarations.
for (const n of r.nameList ?? []) ensureLayer(n);
continue;
}
if (ctor === "CSSLayerBlockRule") {
// @layer name { ... } — recurse with the layer appended.
ensureLayer(r.name);
const next = r.name ? [...layers, r.name] : layers;
try {
collect(r.cssRules, parentSel, next);
} catch {}
continue;
}
if (r.type === 1) { if (r.type === 1) {
let sel = r.selectorText; let sel = r.selectorText;
if (parentSel && sel?.includes("&")) if (parentSel && sel?.includes("&"))
sel = sel.replace(/&/g, parentSel); sel = sel.replace(/&/g, parentSel);
rules.push({ rule: r, effective: sel, order: ruleOrder++ }); rules.push({ rule: r, effective: sel, order: ruleOrder++, layers });
if (r.cssRules?.length) collect(r.cssRules, sel ?? parentSel); if (r.cssRules?.length) collect(r.cssRules, sel ?? parentSel, layers);
} else if (r.type === 3 && r.styleSheet) { } else if (r.type === 3 && r.styleSheet) {
try { try {
collect(r.styleSheet.cssRules, parentSel); collect(r.styleSheet.cssRules, parentSel, layers);
} catch {} } catch {}
} else if (r.type === 4) { } else if (r.type === 4) {
if (r.media?.matches) { if (r.media?.matches) {
try { try {
collect(r.cssRules, parentSel); collect(r.cssRules, parentSel, layers);
} catch {} } catch {}
} }
} else if (r.type === 12) { } else if (r.type === 12) {
try { try {
if (CSS.supports(r.conditionText)) collect(r.cssRules, parentSel); if (CSS.supports(r.conditionText))
collect(r.cssRules, parentSel, layers);
} catch { } catch {
try { try {
collect(r.cssRules, parentSel); collect(r.cssRules, parentSel, layers);
} catch {} } catch {}
} }
} else if (r.type === 15) { } else if (r.type === 15) {
try { try {
collect(r.cssRules, parentSel); collect(r.cssRules, parentSel, layers);
} catch {} } catch {}
} }
} }
@@ -262,6 +288,12 @@ function snapshotDocument({ PROPS, NOISE_TAGS, INHERITED }) {
collect(sheet.cssRules); collect(sheet.cssRules);
} catch {} } catch {}
} }
// Layer rank: unlayered rules (empty path) always outrank layered ones;
// layered rules win by the position of their OUTERMOST layer in the
// declared order (later-declared layer wins over earlier).
const rankOf = (layers) =>
layers.length ? layerNames.indexOf(layers[0]) : Number.MAX_SAFE_INTEGER;
for (const rl of rules) rl.layerRank = rankOf(rl.layers);
return rules; return rules;
} }
@@ -522,6 +554,7 @@ function snapshotDocument({ PROPS, NOISE_TAGS, INHERITED }) {
name, name,
value: st.getPropertyValue(name).trim(), value: st.getPropertyValue(name).trim(),
important: st.getPropertyPriority(name) === "important", important: st.getPropertyPriority(name) === "important",
layerRank: Number.MAX_SAFE_INTEGER,
a: 1e6, a: 1e6,
b: 0, b: 0,
c: 0, c: 0,
@@ -532,7 +565,14 @@ function snapshotDocument({ PROPS, NOISE_TAGS, INHERITED }) {
return out; return out;
} }
const keyOf = (d) => [d.important ? 1 : 0, d.a, d.b, d.c, d.order]; const keyOf = (d) => [
d.important ? 1 : 0,
d.layerRank ?? 0,
d.a,
d.b,
d.c,
d.order,
];
function betterThan(x, y) { function betterThan(x, y) {
const kx = keyOf(x), const kx = keyOf(x),
ky = keyOf(y); ky = keyOf(y);
@@ -565,6 +605,7 @@ function snapshotDocument({ PROPS, NOISE_TAGS, INHERITED }) {
name: k, name: k,
value: v.value ?? "", value: v.value ?? "",
important: false, important: false,
layerRank: -1,
a: -1, a: -1,
b: -1, b: -1,
c: -1, c: -1,
+15 -1
View File
@@ -16,9 +16,18 @@
/* Минимальный reset (новый дизайн грузится только на preview-маршрутах). */ /* Минимальный reset (новый дизайн грузится только на preview-маршрутах). */
*, *,
*::before, *::before,
*::after { *::after,
::backdrop {
box-sizing: border-box; box-sizing: border-box;
border: 0 solid;
margin: 0;
padding: 0;
} }
* {
border-color: var(--line);
}
html, html,
body { body {
margin: 0; margin: 0;
@@ -31,7 +40,9 @@
color-scheme: light dark; color-scheme: light dark;
font-family: var(--font-sans); font-family: var(--font-sans);
-webkit-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;
line-height: 1.5;
} }
body { body {
font-family: var(--font-sans); font-family: var(--font-sans);
color: var(--foreground); color: var(--foreground);
@@ -67,6 +78,9 @@
:focus-visible { :focus-visible {
/* outline: revert; */ /* outline: revert; */
} }
svg {
vertical-align: middle;
}
/* Новый дизайн: токены (§2 плана redesign). /* Новый дизайн: токены (§2 плана redesign).
Файл грузится только layout'ом новой ветки (preview/+layout), поэтому Файл грузится только layout'ом новой ветки (preview/+layout), поэтому