mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-27 04:07:05 +00:00
fix: only count CSS paints that match rendered elements
This commit is contained in:
parent
840b06695f
commit
ef93643a16
2 changed files with 57 additions and 5 deletions
|
|
@ -222,6 +222,24 @@ describe('isMonochromeSvg', () => {
|
|||
'<svg viewBox="0 0 24 24"><style>.a{color:#e00;fill:currentColor}</style><path class="a" d="M4 4h16v16H4z" /></svg>';
|
||||
expect(isMonochromeSvg(svg)).toBe(false);
|
||||
});
|
||||
|
||||
it('ignores an unused CSS paint rule that matches no element', () => {
|
||||
const svg =
|
||||
'<svg viewBox="0 0 24 24"><style>.unused{fill:#f00}.icon{fill:#333}</style><path class="icon" d="M4 4h16v16H4z" /></svg>';
|
||||
expect(isMonochromeSvg(svg)).toBe(true);
|
||||
});
|
||||
|
||||
it('ignores a CSS paint that only targets a hidden element', () => {
|
||||
const svg =
|
||||
'<svg viewBox="0 0 24 24"><style>.ghost{fill:#f00;display:none}.icon{fill:#333}</style><path class="ghost" d="M0 0h4v4H0z" /><path class="icon" d="M6 6h12v12H6z" /></svg>';
|
||||
expect(isMonochromeSvg(svg)).toBe(true);
|
||||
});
|
||||
|
||||
it('preserves a chromatic fill applied to the root via CSS', () => {
|
||||
const svg =
|
||||
'<svg viewBox="0 0 24 24"><style>svg{fill:#f00}</style><path d="M4 4h16v16H4z" /></svg>';
|
||||
expect(isMonochromeSvg(svg)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('default fills alongside <style> rules', () => {
|
||||
|
|
|
|||
|
|
@ -77,9 +77,9 @@ const RESOLVED_DECLS = new Set([
|
|||
/** A `<style>` rule reduced to the paint/opacity declarations we resolve. */
|
||||
type StyleRule = { selector: string; declarations: Map<string, string> };
|
||||
|
||||
/** Matches paint declarations inside a `<style>` block (not `color`, which only
|
||||
* resolves `currentColor` rather than painting on its own). */
|
||||
const CSS_COLOR_REGEX = /(?:fill|stroke|stop-color)\s*:\s*([^;}]+)/gi;
|
||||
/** Matches paint declarations inside a `<style>` block, capturing property and
|
||||
* value (not `color`, which only resolves `currentColor` rather than painting). */
|
||||
const CSS_COLOR_REGEX = /(fill|stroke|stop-color)\s*:\s*([^;}]+)/gi;
|
||||
|
||||
function hexToRgb(hex: string): [number, number, number] | null {
|
||||
let value = hex.slice(1);
|
||||
|
|
@ -306,10 +306,11 @@ function colorsFromStyleBlocks(root: Element, rules: StyleRule[]): string[] {
|
|||
}
|
||||
const selector = rule.slice(0, brace).trim();
|
||||
for (const match of rule.slice(brace + 1).matchAll(CSS_COLOR_REGEX)) {
|
||||
const value = match[1].trim();
|
||||
const property = match[1].toLowerCase();
|
||||
const value = match[2].trim();
|
||||
if (value.toLowerCase() === CURRENT_COLOR) {
|
||||
colors.push(...resolveCssCurrentColor(root, selector, rules));
|
||||
} else {
|
||||
} else if (selectorPaintsRendered(root, selector, rules, property)) {
|
||||
colors.push(value);
|
||||
}
|
||||
}
|
||||
|
|
@ -318,6 +319,39 @@ function colorsFromStyleBlocks(root: Element, rules: StyleRule[]): string[] {
|
|||
return colors;
|
||||
}
|
||||
|
||||
/**
|
||||
* True when a CSS selector matches at least one element that actually paints: it
|
||||
* is rendered (not `display:none`), is not inside a functional template, and the
|
||||
* paint is not made invisible by opacity. Unused or hidden rules contribute no
|
||||
* tone. The root element is checked too, since `querySelectorAll` only walks
|
||||
* descendants.
|
||||
*/
|
||||
function selectorPaintsRendered(
|
||||
root: Element,
|
||||
selector: string,
|
||||
rules: StyleRule[],
|
||||
paintProp: string,
|
||||
): boolean {
|
||||
if (selector === '') {
|
||||
return false;
|
||||
}
|
||||
let matched: Element[];
|
||||
try {
|
||||
matched = Array.from(root.querySelectorAll(selector));
|
||||
if (root.matches(selector)) {
|
||||
matched = [root, ...matched];
|
||||
}
|
||||
} catch {
|
||||
return true;
|
||||
}
|
||||
return matched.some(
|
||||
(el) =>
|
||||
!isHidden(el, root, rules) &&
|
||||
!isInside(el, root, FUNCTIONAL_CONTAINERS) &&
|
||||
!paintInvisible(el, root, rules, paintProp),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a `currentColor` paint to the fixed `color` set on the element or an
|
||||
* ancestor (inline style, attribute, or CSS), since that is what the icon actually
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue