import strudelTheme, { settings as strudelThemeSettings } from './themes/strudel-theme.mjs'; import bluescreen, { settings as bluescreenSettings } from './themes/bluescreen.mjs'; import blackscreen, { settings as blackscreenSettings } from './themes/blackscreen.mjs'; import whitescreen, { settings as whitescreenSettings } from './themes/whitescreen.mjs'; import teletext, { settings as teletextSettings } from './themes/teletext.mjs'; import algoboy, { settings as algoboySettings } from './themes/algoboy.mjs'; import terminal, { settings as terminalSettings } from './themes/terminal.mjs'; import abcdef, { settings as abcdefSettings } from './themes/abcdef.mjs'; import androidstudio, { settings as androidstudioSettings } from './themes/androidstudio.mjs'; import atomone, { settings as atomOneSettings } from './themes/atomone.mjs'; import aura, { settings as auraSettings } from './themes/aura.mjs'; import bespin, { settings as bespinSettings } from './themes/bespin.mjs'; import darcula, { settings as darculaSettings } from './themes/darcula.mjs'; import dracula, { settings as draculaSettings } from './themes/dracula.mjs'; import duotoneDark, { settings as duotoneDarkSettings } from './themes/duotoneDark.mjs'; import duotoneLight, { settings as duotoneLightSettings } from './themes/duotoneLight.mjs'; import eclipse, { settings as eclipseSettings } from './themes/eclipse.mjs'; import githubDark, { settings as githubDarkSettings } from './themes/githubDark.mjs'; import githubLight, { settings as githubLightSettings } from './themes/githubLight.mjs'; import gruvboxDark, { settings as gruvboxDarkSettings } from './themes/gruvboxDark.mjs'; import gruvboxLight, { settings as gruvboxLightSettings } from './themes/gruvboxLight.mjs'; import materialDark, { settings as materialDarkSettings } from './themes/materialDark.mjs'; import materialLight, { settings as materialLightSettings } from './themes/materialLight.mjs'; import nord, { settings as nordSettings } from './themes/nord.mjs'; import okaidia, { settings as okaidiaSettings } from './themes/okaidia.mjs'; import solarizedDark, { settings as solarizedDarkSettings } from './themes/solarizedDark.mjs'; import solarizedLight, { settings as solarizedLightSettings } from './themes/solarizedLight.mjs'; import sublime, { settings as sublimeSettings } from './themes/sublime.mjs'; import tokyoNight, { settings as tokyoNightSettings } from './themes/tokyoNight.mjs'; import tokyoNightStorm, { settings as tokyoNightStormSettings } from './themes/tokioNightStorm.mjs'; import vscodeDark, { settings as vscodeDarkSettings } from './themes/vscodeDark.mjs'; import vscodeLight, { settings as vscodeLightSettings } from './themes/vscodeLight.mjs'; import { setTheme } from '@strudel/draw'; export const themes = { strudelTheme, bluescreen, blackscreen, whitescreen, teletext, algoboy, terminal, abcdef, androidstudio, atomone, aura, bespin, darcula, dracula, duotoneDark, duotoneLight, eclipse, githubDark, gruvboxDark, materialDark, nord, okaidia, solarizedDark, sublime, tokyoNight, tokyoNightStorm, vscodeDark, //xcodeDark, //bbedit, githubLight, gruvboxLight, materialLight, vscodeLight, //noctisLilac, solarizedLight, //tokyoNightDay, //xcodeLight, }; // lineBackground is background with 50% opacity, to make sure the selection below is visible export const settings = { strudelTheme: strudelThemeSettings, bluescreen: bluescreenSettings, blackscreen: blackscreenSettings, whitescreen: whitescreenSettings, teletext: teletextSettings, algoboy: algoboySettings, terminal: terminalSettings, abcdef: abcdefSettings, androidstudio: androidstudioSettings, atomone: atomOneSettings, aura: auraSettings, /*bbedit: { light: true, background: '#FFFFFF', lineBackground: '#FFFFFF99', foreground: '#000000', caret: '#FBAC52', selection: '#FFD420', selectionMatch: '#FFD420', gutterBackground: '#f5f5f5', gutterForeground: '#4D4D4C', gutterBorder: 'transparent', lineHighlight: '#00000012', },*/ bespin: bespinSettings, darcula: darculaSettings, dracula: draculaSettings, duotoneLight: duotoneLightSettings, duotoneDark: duotoneDarkSettings, eclipse: eclipseSettings, githubLight: githubLightSettings, githubDark: githubDarkSettings, gruvboxDark: gruvboxDarkSettings, gruvboxLight: gruvboxLightSettings, materialDark: materialDarkSettings, materialLight: materialLightSettings, /*noctisLilac: { light: true, background: '#f2f1f8', lineBackground: '#f2f1f899', foreground: '#0c006b', caret: '#5c49e9', selection: '#d5d1f2', selectionMatch: '#d5d1f2', gutterBackground: '#f2f1f8', gutterForeground: '#0c006b70', lineHighlight: '#e1def3', },*/ nord: nordSettings, okaidia: okaidiaSettings, solarizedLight: solarizedLightSettings, solarizedDark: solarizedDarkSettings, sublime: sublimeSettings, tokyoNight: tokyoNightSettings, tokyoNightStorm: tokyoNightStormSettings, vscodeDark: vscodeDarkSettings, vscodeLight: vscodeLightSettings, /*tokyoNightDay: { light: true, background: '#e1e2e7', lineBackground: '#e1e2e799', foreground: '#3760bf', caret: '#3760bf', selection: '#99a7df', selectionMatch: '#99a7df', gutterBackground: '#e1e2e7', gutterForeground: '#3760bf', gutterBorder: 'transparent', lineHighlight: '#5f5faf11', }, xcodeLight: { light: true, background: '#fff', lineBackground: '#ffffff99', foreground: '#3D3D3D', selection: '#BBDFFF', selectionMatch: '#BBDFFF', gutterBackground: '#fff', gutterForeground: '#AFAFAF', lineHighlight: '#EDF4FF', }, xcodeDark: { background: '#292A30', lineBackground: '#292A3099', foreground: '#CECFD0', caret: '#fff', selection: '#727377', selectionMatch: '#727377', lineHighlight: '#2F3239', }, */ }; function getColors(str) { const colorRegex = /#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{3})/g; const colors = []; let match; while ((match = colorRegex.exec(str)) !== null) { const color = match[0]; if (!colors.includes(color)) { colors.push(color); } } return colors; } // TODO: remove export function themeColors(theme) { return getColors(stringifySafe(theme)); } function getCircularReplacer() { const seen = new WeakSet(); return (key, value) => { if (typeof value === 'object' && value !== null) { if (seen.has(value)) { return; } seen.add(value); } return value; }; } function stringifySafe(json) { return JSON.stringify(json, getCircularReplacer()); } export const theme = (theme) => themes[theme] || themes.strudelTheme; // css style injection helpers export function injectStyle(rule) { const newStyle = document.createElement('style'); document.head.appendChild(newStyle); const styleSheet = newStyle.sheet; const ruleIndex = styleSheet.insertRule(rule, 0); return () => styleSheet.deleteRule(ruleIndex); } let currentTheme, resetThemeStyle, themeStyle, styleID = 'strudel-theme-vars'; export function initTheme(theme) { if (!document.getElementById(styleID)) { themeStyle = document.createElement('style'); themeStyle.id = styleID; document.head.append(themeStyle); } activateTheme(theme); } export function activateTheme(name) { if (currentTheme === name) { return; } currentTheme = name; if (!settings[name]) { console.warn('theme', name, 'has no settings.. defaulting to strudelTheme settings'); } const themeSettings = settings[name] || settings.strudelTheme; // set css variables themeStyle.innerHTML = `:root { ${Object.entries(themeSettings) // important to override fallback .map(([key, value]) => `--${key}: ${value} !important;`) .join('\n')} }`; setTheme(themeSettings); // tailwind dark mode if (themeSettings.light) { document.documentElement.classList.remove('dark'); } else { document.documentElement.classList.add('dark'); } resetThemeStyle?.(); resetThemeStyle = undefined; if (themeSettings.customStyle) { resetThemeStyle = injectStyle(themeSettings.customStyle); } }