diff --git a/packages/codemirror/codemirror.mjs b/packages/codemirror/codemirror.mjs index 7933600e..5ceb9619 100644 --- a/packages/codemirror/codemirror.mjs +++ b/packages/codemirror/codemirror.mjs @@ -3,7 +3,7 @@ import { closeBrackets } from '@codemirror/autocomplete'; import { history } from '@codemirror/commands'; import { javascript } from '@codemirror/lang-javascript'; import { defaultHighlightStyle, syntaxHighlighting } from '@codemirror/language'; -import { Compartment, EditorState } from '@codemirror/state'; +import { Compartment, EditorState, Prec } from '@codemirror/state'; import { EditorView, highlightActiveLineGutter, highlightActiveLine, keymap, lineNumbers } from '@codemirror/view'; import { Pattern, Drawer, repl, cleanupDraw } from '@strudel.cycles/core'; // import { isAutoCompletionEnabled } from './Autocomplete'; @@ -44,27 +44,28 @@ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, set syntaxHighlighting(defaultHighlightStyle), history(), EditorView.updateListener.of((v) => onChange(v)), - keymap.of([ - { - key: 'Ctrl-Enter', - run: () => onEvaluate?.(), - }, - { - key: 'Alt-Enter', - run: () => onEvaluate?.(), - }, - { - key: 'Ctrl-.', - run: () => onStop?.(), - }, - { - key: 'Alt-.', - run: (_, e) => { - e.preventDefault(); - onStop?.(); + Prec.highest( + keymap.of([ + { + key: 'Ctrl-Enter', + run: () => onEvaluate?.(), }, - }, - /* { + { + key: 'Alt-Enter', + run: () => onEvaluate?.(), + }, + { + key: 'Ctrl-.', + run: () => onStop?.(), + }, + { + key: 'Alt-.', + run: (_, e) => { + e.preventDefault(); + onStop?.(); + }, + }, + /* { key: 'Ctrl-Shift-.', run: () => (onPanic ? onPanic() : onStop?.()), }, @@ -72,7 +73,8 @@ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, set key: 'Ctrl-Shift-Enter', run: () => (onReEvaluate ? onReEvaluate() : onEvaluate?.()), }, */ - ]), + ]), + ), ], }); @@ -150,6 +152,11 @@ export class StrudelMirror { onEvaluate: () => this.evaluate(), onStop: () => this.stop(), }); + const cmEditor = this.root.querySelector('.cm-editor'); + if (cmEditor) { + this.root.style.backgroundColor = 'var(--background)'; + cmEditor.style.backgroundColor = 'transparent'; + } } async drawFirstFrame() { if (!this.onDraw) { @@ -190,6 +197,10 @@ export class StrudelMirror { } setFontFamily(family) { this.root.style.fontFamily = family; + const scroller = this.root.querySelector('.cm-scroller'); + if (scroller) { + scroller.style.fontFamily = family; + } } reconfigureExtension(key, value) { if (!extensions[key]) { diff --git a/packages/codemirror/index.mjs b/packages/codemirror/index.mjs index c847c32c..8f2d1630 100644 --- a/packages/codemirror/index.mjs +++ b/packages/codemirror/index.mjs @@ -2,3 +2,4 @@ export * from './codemirror.mjs'; export * from './highlight.mjs'; export * from './flash.mjs'; export * from './slider.mjs'; +export * from './themes.mjs'; diff --git a/packages/codemirror/themes.mjs b/packages/codemirror/themes.mjs index 1f520623..71fb7642 100644 --- a/packages/codemirror/themes.mjs +++ b/packages/codemirror/themes.mjs @@ -473,6 +473,9 @@ 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); @@ -481,4 +484,38 @@ export function injectStyle(rule) { return () => styleSheet.deleteRule(ruleIndex); } -export const theme = (theme) => themes[theme] || themes.strudelTheme; +let currentTheme, resetThemeStyle, themeStyle; +export function initTheme(theme) { + themeStyle = document.createElement('style'); + themeStyle.id = 'strudel-theme'; + document.head.append(themeStyle); + activateTheme(theme); +} + +export function activateTheme(name) { + if (currentTheme === name) { + return; + } + 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')} + }`; + // 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); + } +} diff --git a/website/src/components/HeadCommonNew.astro b/website/src/components/HeadCommonNew.astro new file mode 100644 index 00000000..9f323a7a --- /dev/null +++ b/website/src/components/HeadCommonNew.astro @@ -0,0 +1,58 @@ +--- +import { pwaInfo } from 'virtual:pwa-info'; +import '../styles/index.css'; + +const { BASE_URL } = import.meta.env; +const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL; +--- + + + + + + + + + + + + + + + + + + + + + + + +{pwaInfo && } + + diff --git a/website/src/pages/vanilla/index.astro b/website/src/pages/vanilla/index.astro index 5377c08f..44144bb6 100644 --- a/website/src/pages/vanilla/index.astro +++ b/website/src/pages/vanilla/index.astro @@ -1,13 +1,13 @@ --- -import HeadCommon from '../../components/HeadCommon.astro'; +import HeadCommonNew from '../../components/HeadCommonNew.astro'; --- - + Strudel Vanilla REPL - +

- +

diff --git a/website/src/repl/vanilla/vanilla.css b/website/src/repl/vanilla/vanilla.css index 7c985be7..584dd924 100644 --- a/website/src/repl/vanilla/vanilla.css +++ b/website/src/repl/vanilla/vanilla.css @@ -1,7 +1,3 @@ -:root { - --foreground: white; -} - body, input { font-family: monospace; diff --git a/website/src/repl/vanilla/vanilla.mjs b/website/src/repl/vanilla/vanilla.mjs index 6e5c54a3..488fcf8c 100644 --- a/website/src/repl/vanilla/vanilla.mjs +++ b/website/src/repl/vanilla/vanilla.mjs @@ -1,5 +1,5 @@ import { logger, getDrawContext, silence, controls, evalScope, hash2code, code2hash } from '@strudel.cycles/core'; -import { StrudelMirror } from '@strudel/codemirror'; +import { StrudelMirror, initTheme, activateTheme } from '@strudel/codemirror'; import { transpiler } from '@strudel.cycles/transpiler'; import { getAudioContext, @@ -24,6 +24,7 @@ const initialSettings = { fontFamily: 'monospace', fontSize: 18, }; +initTheme(initialSettings.theme); async function run() { const container = document.getElementById('code'); @@ -196,4 +197,6 @@ form.addEventListener('change', () => { const values = getFormValues(form, initialSettings); // console.log('values', values); editor.updateSettings(values); + // TODO: only activateTheme when it changes + activateTheme(values.theme); });