import { loader } from "@monaco-editor/react";
import type * as Monaco from "monaco-editor";
import { useCallback, useEffect, useRef, useState } from "react";
let themeInitialized = false;
let observerInitialized = false;
const editorInstances = new Set<Monaco.editor.IStandaloneCodeEditor>();
function getCurrentTheme(): "light" | "dark" {
if (typeof document === "undefined") return "dark";
return document.documentElement.classList.contains("dark") ? "dark" : "light";
}
function defineThemes(monaco: typeof Monaco | any) {
if (themeInitialized) return;
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;
}
function updateAllEditorsTheme(theme: "light" | "dark") {
const themeName = theme === "dark" ? "superglue-dark" : "superglue-light";
loader.init().then((monaco) => {
defineThemes(monaco);
monaco.editor.setTheme(themeName);
editorInstances.forEach((editor) => {
editor.updateOptions({ theme: themeName });
});
});
}
function initThemeObserver() {
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") {
updateAllEditorsTheme(getCurrentTheme());
break;
}
}
});
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(() => {
initThemeObserver();
let mounted = true;
loader.init().then((monaco) => {
defineThemes(monaco);
const theme = getCurrentTheme();
if (mounted) setCurrentTheme(theme);
monaco.editor.setTheme(theme === "dark" ? "superglue-dark" : "superglue-light");
});
return () => {
mounted = false;
};
}, []);
const handleEditorMount = useCallback((editor: Monaco.editor.IStandaloneCodeEditor) => {
editorRef.current = editor;
editorInstances.add(editor);
loader.init().then((monaco) => {
defineThemes(monaco);
const theme = getCurrentTheme();
const themeName = theme === "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,
};
}