fix: Monaco syntax highlighting broken by CSS injection #5440

This commit is contained in:
Slinetrac
2025-11-20 15:16:25 +08:00
parent b3dc48d07e
commit be97a27ba3

View File

@@ -18,6 +18,61 @@ import { useVerge } from "@/hooks/use-verge";
import { defaultDarkTheme, defaultTheme } from "@/pages/_theme";
import { useSetThemeMode, useThemeMode } from "@/services/states";
const CSS_INJECTION_SCOPE_ROOT = "[data-css-injection-root]";
const CSS_INJECTION_SCOPE_LIMIT =
':is(.monaco-editor .view-lines, .monaco-editor .view-line, .monaco-editor .margin, .monaco-editor .margin-view-overlays, .monaco-editor .view-overlays, .monaco-editor [class^="mtk"], .monaco-editor [class*=" mtk"])';
const TOP_LEVEL_AT_RULES = [
"@charset",
"@import",
"@namespace",
"@font-face",
"@keyframes",
"@counter-style",
"@page",
"@property",
"@font-feature-values",
"@color-profile",
];
let cssScopeSupport: boolean | null = null;
const canUseCssScope = () => {
if (cssScopeSupport !== null) {
return cssScopeSupport;
}
if (typeof document === "undefined") {
return false;
}
try {
const testStyle = document.createElement("style");
testStyle.textContent = "@scope (:root) { }";
document.head.appendChild(testStyle);
cssScopeSupport = !!testStyle.sheet?.cssRules?.length;
document.head.removeChild(testStyle);
} catch {
cssScopeSupport = false;
}
return cssScopeSupport;
};
const wrapCssInjectionWithScope = (css?: string) => {
if (!css?.trim()) {
return "";
}
const lowerCss = css.toLowerCase();
const hasTopLevelOnlyRule = TOP_LEVEL_AT_RULES.some((rule) =>
lowerCss.includes(rule),
);
if (hasTopLevelOnlyRule) {
return null;
}
const scopeRoot = CSS_INJECTION_SCOPE_ROOT;
const scopeLimit = CSS_INJECTION_SCOPE_LIMIT;
const scopedBlock = `@scope (${scopeRoot}) to (${scopeLimit}) {
${css}
}`;
return scopedBlock;
};
const languagePackMap: Record<string, any> = {
zh: { ...zhXDataGrid },
fa: { ...faXDataGrid },
@@ -134,8 +189,27 @@ export const useCustomTheme = () => {
};
const legacyQuery = mediaQuery as MediaQueryListLegacy;
legacyQuery.addListener?.(handleChange);
return () => legacyQuery.removeListener?.(handleChange);
const legacyAddListener = (
legacyQuery as {
addListener?: (
listener: (this: MediaQueryList, event: MediaQueryListEvent) => void,
) => void;
}
).addListener;
legacyAddListener?.call(legacyQuery, handleChange);
return () => {
const legacyRemoveListener = (
legacyQuery as {
removeListener?: (
listener: (
this: MediaQueryList,
event: MediaQueryListEvent,
) => void,
) => void;
}
).removeListener;
legacyRemoveListener?.call(legacyQuery, handleChange);
};
}, [theme_mode, setMode]);
useEffect(() => {
@@ -264,6 +338,7 @@ export const useCustomTheme = () => {
? String(setting.background_opacity)
: "1",
);
rootEle.setAttribute("data-css-injection-root", "true");
}
let styleElement = document.querySelector("style#verge-theme");
@@ -274,6 +349,11 @@ export const useCustomTheme = () => {
}
if (styleElement) {
let scopedCss: string | null = null;
if (canUseCssScope() && setting.css_injection) {
scopedCss = wrapCssInjectionWithScope(setting.css_injection);
}
const effectiveInjectedCss = scopedCss ?? setting.css_injection ?? "";
const globalStyles = `
/* 修复滚动条样式 */
::-webkit-scrollbar {
@@ -323,7 +403,7 @@ export const useCustomTheme = () => {
}
`;
styleElement.innerHTML = (setting.css_injection || "") + globalStyles;
styleElement.innerHTML = effectiveInjectedCss + globalStyles;
}
const { palette } = muiTheme;