useMonacoTheme.ts•4.64 kB
import { loader } from '@monaco-editor/react';
import type * as Monaco from 'monaco-editor';
import { useCallback, useEffect, useRef, useState } from 'react';
let themeInitialized = false;
const editorInstances = new Set<Monaco.editor.IStandaloneCodeEditor>();
let observerInitialized = false;
function getCurrentTheme(): 'light' | 'dark' {
if (typeof window === 'undefined') return 'dark';
return document.documentElement.classList.contains('dark') ? 'dark' : 'light';
}
function updateAllEditors(theme: 'light' | 'dark') {
const themeName = theme === 'dark' ? 'superglue-dark' : 'superglue-light';
loader.init().then((monaco) => {
monaco.editor.setTheme(themeName);
editorInstances.forEach((editor) => {
editor.updateOptions({ theme: themeName });
});
});
}
function initializeThemeObserver() {
if (observerInitialized || typeof window === 'undefined') return;
observerInitialized = true;
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
const theme = getCurrentTheme();
updateAllEditors(theme);
}
}
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['class'],
});
}
export function useMonacoTheme() {
const [currentTheme, setCurrentTheme] = useState<'light' | 'dark'>(getCurrentTheme);
const editorRef = useRef<Monaco.editor.IStandaloneCodeEditor | null>(null);
useEffect(() => {
if (themeInitialized) return;
loader.init().then((monaco) => {
monaco.editor.defineTheme('superglue-light', {
base: 'vs',
inherit: true,
rules: [
{ token: 'comment', foreground: '6A737D', fontStyle: 'italic' },
{ token: 'keyword', foreground: 'C678DD' },
{ token: 'string', foreground: '50A14F' },
{ token: 'number', foreground: '0184BC' },
{ token: 'regexp', foreground: 'E45649' },
{ token: 'operator', foreground: '383A42' },
{ token: 'delimiter', foreground: '383A42' },
{ token: 'type', foreground: '0997B3' },
{ token: 'function', foreground: '4078F2' },
{ token: 'variable', foreground: 'E45649' },
{ token: 'constant', foreground: '986801' },
],
colors: {
'editor.background': '#00000000',
'editor.foreground': '#383A42',
'editorLineNumber.foreground': '#9D9D9F',
'editorCursor.foreground': '#526FFF',
'editor.selectionBackground': '#E5E5E6',
'editor.inactiveSelectionBackground': '#EAEAEB',
}
});
monaco.editor.defineTheme('superglue-dark', {
base: 'vs-dark',
inherit: true,
rules: [
{ token: 'comment', foreground: '6A737D', fontStyle: 'italic' },
{ token: 'keyword', foreground: 'C678DD' },
{ token: 'string', foreground: '98C379' },
{ token: 'number', foreground: '61AFEF' },
{ token: 'regexp', foreground: 'E06C75' },
{ token: 'operator', foreground: 'ABB2BF' },
{ token: 'delimiter', foreground: 'ABB2BF' },
{ token: 'type', foreground: '56B6C2' },
{ token: 'function', foreground: '61AFEF' },
{ token: 'variable', foreground: 'E06C75' },
{ token: 'constant', foreground: 'D19A66' },
],
colors: {
'editor.background': '#00000000',
'editor.foreground': '#ABB2BF',
'editorLineNumber.foreground': '#5C6370',
'editorCursor.foreground': '#528BFF',
'editor.selectionBackground': '#3E4451',
'editor.inactiveSelectionBackground': '#2C313A',
}
});
themeInitialized = true;
const initialTheme = getCurrentTheme();
const themeName = initialTheme === 'dark' ? 'superglue-dark' : 'superglue-light';
monaco.editor.setTheme(themeName);
});
}, []);
useEffect(() => {
initializeThemeObserver();
}, []);
const handleEditorMount = useCallback((editor: Monaco.editor.IStandaloneCodeEditor) => {
editorRef.current = editor;
editorInstances.add(editor);
const currentTheme = getCurrentTheme();
const themeName = currentTheme === 'dark' ? 'superglue-dark' : 'superglue-light';
editor.updateOptions({ theme: themeName });
return () => {
editorInstances.delete(editor);
};
}, []);
const themeName = currentTheme === 'dark' ? 'superglue-dark' : 'superglue-light';
return {
theme: themeName,
onMount: handleEditorMount,
};
}