themed minirepl

+ add strudelTheme
+ add lineBackground with opacity
+ add some missing light flags
+ add fallback css variables for non js context
This commit is contained in:
Felix Roos 2023-02-10 21:42:27 +01:00
parent 6170eef441
commit 14cb954213
13 changed files with 116 additions and 34 deletions

View file

@ -13,7 +13,15 @@ import { logger } from '@strudel.cycles/core';
const getTime = () => getAudioContext().currentTime; const getTime = () => getAudioContext().currentTime;
export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, drawTime, punchcard, canvasHeight = 200 }) { export function MiniRepl({
tune,
hideOutsideView = false,
enableKeyboard,
drawTime,
punchcard,
canvasHeight = 200,
theme,
}) {
drawTime = drawTime || (punchcard ? [0, 4] : undefined); drawTime = drawTime || (punchcard ? [0, 4] : undefined);
const evalOnMount = !!drawTime; const evalOnMount = !!drawTime;
const drawContext = useCallback( const drawContext = useCallback(
@ -111,7 +119,7 @@ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, drawTi
{error && <div className={styles.error}>{error.message}</div>} {error && <div className={styles.error}>{error.message}</div>}
</div> </div>
<div className={styles.body}> <div className={styles.body}>
{show && <CodeMirror6 value={code} onChange={setCode} onViewChanged={setView} />} {show && <CodeMirror6 value={code} onChange={setCode} onViewChanged={setView} theme={theme} />}
</div> </div>
{drawTime && ( {drawTime && (
<canvas <canvas

View file

@ -1,9 +1,9 @@
.container { .container {
@apply rounded-md overflow-hidden bg-[#222222]; @apply overflow-hidden;
} }
.header { .header {
@apply flex justify-between bg-slate-700 border-t border-slate-500; @apply flex justify-between bg-lineHighlight border-t border-l border-r border-lineHighlight rounded-t-md overflow-hidden;
} }
.buttons { .buttons {
@ -11,11 +11,11 @@
} }
.button { .button {
@apply cursor-pointer w-16 flex items-center justify-center p-1 bg-slate-700 border-r border-slate-500 text-white hover:bg-slate-600; @apply cursor-pointer w-16 flex items-center justify-center p-1 border-r border-lineHighlight text-foreground hover:bg-background;
} }
.buttonDisabled { .buttonDisabled {
@apply w-16 flex items-center justify-center p-1 bg-slate-600 text-slate-400 cursor-not-allowed; @apply w-16 flex items-center justify-center p-1 opacity-50 cursor-not-allowed border-r border-lineHighlight;
} }
.error { .error {

View file

@ -12,7 +12,3 @@
.cm-theme-light { .cm-theme-light {
width: 100%; width: 100%;
} }
.cm-line > * {
background: var(--background);
}

View file

@ -6,4 +6,5 @@ export { default as useHighlighting } from './hooks/useHighlighting';
export { default as usePostMessage } from './hooks/usePostMessage'; export { default as usePostMessage } from './hooks/usePostMessage';
export { default as useStrudel } from './hooks/useStrudel'; export { default as useStrudel } from './hooks/useStrudel';
export { default as useKeydown } from './hooks/useKeydown'; export { default as useKeydown } from './hooks/useKeydown';
export { default as strudelTheme } from './themes/strudel-theme';
export { default as cx } from './cx'; export { default as cx } from './cx';

View file

@ -5,6 +5,8 @@ import { settings } from '../repl/themes.mjs';
const { BASE_URL } = import.meta.env; const { BASE_URL } = import.meta.env;
const base = BASE_URL; const base = BASE_URL;
const { strudelTheme } = settings;
--- ---
<!-- Global Metadata --> <!-- Global Metadata -->
@ -28,14 +30,28 @@ const base = BASE_URL;
<script src="./make-scrollable-code-focusable.js" is:inline></script> <script src="./make-scrollable-code-focusable.js" is:inline></script>
<script src="/src/pwa.ts"></script> <script src="/src/pwa.ts"></script>
<!-- this does not work for some reason: -->
<!-- <style is:global define:vars={strudelTheme}></style> -->
<!-- the following variables are just a fallback to make sure everything is readable without JS -->
<style is:global>
:root {
--background: #222;
--lineBackground: #22222250;
--foreground: #fff;
--caret: #ffcc00;
--selection: rgba(128, 203, 196, 0.5);
--selectionMatch: #036dd626;
--lineHighlight: #00000050;
--gutterBackground: transparent;
--gutterForeground: #8a919966;
}
</style>
{pwaInfo && <Fragment set:html={pwaInfo.webManifest.linkTag} />} {pwaInfo && <Fragment set:html={pwaInfo.webManifest.linkTag} />}
<script define:vars={{ settings }}> <script define:vars={{ settings, strudelTheme }} is:inline>
// const themeStyle = document.getElementById('theme');
const themeStyle = document.createElement('style'); const themeStyle = document.createElement('style');
themeStyle.id = 'strudel-theme'; themeStyle.id = 'strudel-theme';
document.head.append(themeStyle); document.head.append(themeStyle);
function getTheme(name) { function getTheme(name) {
if (!settings[name]) { if (!settings[name]) {
console.warn('theme', name, 'has no settings'); console.warn('theme', name, 'has no settings');
@ -50,7 +66,8 @@ const base = BASE_URL;
// set css variables // set css variables
themeStyle.innerHTML = `:root { themeStyle.innerHTML = `:root {
${Object.entries(settings) ${Object.entries(settings)
.map(([key, value]) => `--${key}: ${value};`) // important to override fallback
.map(([key, value]) => `--${key}: ${value} !important;`)
.join('\n')} .join('\n')}
}`; }`;
// tailwind dark mode // tailwind dark mode
@ -60,7 +77,7 @@ const base = BASE_URL;
document.documentElement.classList.add('dark'); document.documentElement.classList.add('dark');
} }
// persist theme name // persist theme name
localStorage.setItem('strudel-theme', name); localStorage.setItem('strudel-theme', name || 'strudelTheme');
} }
setTheme(localStorage.getItem('strudel-theme')); setTheme(localStorage.getItem('strudel-theme'));
document.addEventListener('strudel-theme', (e) => setTheme(e.detail)); document.addEventListener('strudel-theme', (e) => setTheme(e.detail));

View file

@ -0,0 +1,9 @@
.cm-activeLine {
background-color: transparent !important;
}
.cm-theme {
background-color: var(--background);
border: 1px solid var(--lineHighlight);
padding: 2px;
}

View file

@ -2,6 +2,10 @@ import { evalScope, controls } from '@strudel.cycles/core';
import { initAudioOnFirstClick } from '@strudel.cycles/webaudio'; import { initAudioOnFirstClick } from '@strudel.cycles/webaudio';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { prebake } from '../repl/prebake'; import { prebake } from '../repl/prebake';
import { themes } from '../repl/themes.mjs';
import './MiniRepl.css';
const theme = localStorage.getItem('strudel-theme') || 'strudelTheme';
let modules; let modules;
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
@ -34,7 +38,14 @@ export function MiniRepl({ tune, drawTime, punchcard, canvasHeight = 100 }) {
}, []); }, []);
return Repl ? ( return Repl ? (
<div className="mb-4"> <div className="mb-4">
<Repl tune={tune} hideOutsideView={true} drawTime={drawTime} punchcard={punchcard} canvasHeight={canvasHeight} /> <Repl
tune={tune}
hideOutsideView={true}
drawTime={drawTime}
punchcard={punchcard}
canvasHeight={canvasHeight}
theme={themes[theme]}
/>
</div> </div>
) : ( ) : (
<pre>{tune}</pre> <pre>{tune}</pre>

View file

@ -22,7 +22,7 @@ const currentFile = `src/pages${currentPage.replace(/\/$/, '')}.mdx`;
const githubEditUrl = `${CONFIG.GITHUB_EDIT_URL}/${currentFile}`; const githubEditUrl = `${CONFIG.GITHUB_EDIT_URL}/${currentFile}`;
--- ---
<html dir={frontmatter.dir ?? 'ltr'} lang={frontmatter.lang ?? 'en'} class="initial"> <html dir={frontmatter.dir ?? 'ltr'} lang={frontmatter.lang ?? 'en'} class="initial dark">
<head> <head>
<HeadCommon /> <HeadCommon />
<HeadSEO frontmatter={frontmatter} canonicalUrl={canonicalURL} /> <HeadSEO frontmatter={frontmatter} canonicalUrl={canonicalURL} />

View file

@ -3,7 +3,7 @@ import HeadCommon from '../components/HeadCommon.astro';
import { Repl } from '../repl/Repl.jsx'; import { Repl } from '../repl/Repl.jsx';
--- ---
<html lang="en"> <html lang="en" class="dark">
<head> <head>
<HeadCommon /> <HeadCommon />
<title>Strudel REPL</title> <title>Strudel REPL</title>

View file

@ -75,7 +75,7 @@ export function Footer({ context }) {
<FooterTab name="samples" /> <FooterTab name="samples" />
<FooterTab name="console" /> <FooterTab name="console" />
<FooterTab name="reference" /> <FooterTab name="reference" />
<FooterTab name="settings" /> <FooterTab name="theme" />
</div> </div>
{activeFooter !== '' && ( {activeFooter !== '' && (
<button onClick={() => setActiveFooter('')} className="text-foreground" aria-label="Close Panel"> <button onClick={() => setActiveFooter('')} className="text-foreground" aria-label="Close Panel">
@ -167,13 +167,13 @@ export function Footer({ context }) {
</div> </div>
)} )}
{activeFooter === 'reference' && <Reference />} {activeFooter === 'reference' && <Reference />}
{activeFooter === 'settings' && ( {activeFooter === 'theme' && (
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-2 p-2"> <div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-2 p-2">
{Object.entries(themes).map(([k, t]) => ( {Object.entries(themes).map(([k, t]) => (
<div <div
key={k} key={k}
className={classNames( className={classNames(
'border-2 border-transparent cursor-pointer p-4 bg-background rounded-md', 'border-2 border-transparent cursor-pointer p-4 bg-background bg-opacity-25 rounded-md',
theme === k ? '!border-foreground' : '', theme === k ? '!border-foreground' : '',
)} )}
onClick={() => { onClick={() => {

View file

@ -14,3 +14,7 @@
padding-top: 12px !important; padding-top: 12px !important;
padding-left: 8px !important; padding-left: 8px !important;
} }
.cm-line > * {
background: var(--lineBackground);
}

View file

@ -30,8 +30,10 @@ import {
xcodeLight, xcodeLight,
} from '@uiw/codemirror-themes-all'; } from '@uiw/codemirror-themes-all';
import strudelTheme from '@strudel.cycles/react/src/themes/strudel-theme';
export const themes = { export const themes = {
// none: 'none', strudelTheme,
abcdef, abcdef,
androidstudio, androidstudio,
atomone, atomone,
@ -63,24 +65,26 @@ export const themes = {
xcodeLight, xcodeLight,
}; };
// lineBackground is background with 50% opacity, to make sure the selection below is visible
export const settings = { export const settings = {
none: {
background: '#222',
foreground: '#FFFFFF',
lineHighlight: '#00000059',
},
strudelTheme: { strudelTheme: {
background: '#222', background: '#222',
foreground: '#FFFFFF', lineBackground: '#22222250',
caret: '#FFFFFF', foreground: '#fff',
selection: '#49483E', // foreground: '#75baff',
selectionMatch: '#49483E', caret: '#ffcc00',
gutterBackground: '#272822', selection: 'rgba(128, 203, 196, 0.5)',
gutterForeground: '#FFFFFF70', selectionMatch: '#036dd626',
lineHighlight: '#00000059', // lineHighlight: '#8a91991a', // original
lineHighlight: '#00000050',
gutterBackground: 'transparent',
// gutterForeground: '#8a919966',
gutterForeground: '#8a919966',
}, },
abcdef: { abcdef: {
background: '#0f0f0f', background: '#0f0f0f',
lineBackground: '#0f0f0f50',
foreground: '#defdef', foreground: '#defdef',
caret: '#00FF00', caret: '#00FF00',
selection: '#515151', selection: '#515151',
@ -91,6 +95,7 @@ export const settings = {
}, },
androidstudio: { androidstudio: {
background: '#282b2e', background: '#282b2e',
lineBackground: '#282b2e50',
foreground: '#a9b7c6', foreground: '#a9b7c6',
caret: '#00FF00', caret: '#00FF00',
selection: '#343739', selection: '#343739',
@ -99,6 +104,7 @@ export const settings = {
}, },
atomone: { atomone: {
background: '#272C35', background: '#272C35',
lineBackground: '#272C3550',
foreground: '#9d9b97', foreground: '#9d9b97',
caret: '#797977', caret: '#797977',
selection: '#ffffff30', selection: '#ffffff30',
@ -110,6 +116,7 @@ export const settings = {
}, },
aura: { aura: {
background: '#21202e', background: '#21202e',
lineBackground: '#21202e50',
foreground: '#edecee', foreground: '#edecee',
caret: '#a277ff', caret: '#a277ff',
selection: '#3d375e7f', selection: '#3d375e7f',
@ -120,7 +127,9 @@ export const settings = {
lineHighlight: '#a394f033', lineHighlight: '#a394f033',
}, },
bbedit: { bbedit: {
light: true,
background: '#FFFFFF', background: '#FFFFFF',
lineBackground: '#FFFFFF50',
foreground: '#000000', foreground: '#000000',
caret: '#FBAC52', caret: '#FBAC52',
selection: '#FFD420', selection: '#FFD420',
@ -132,6 +141,7 @@ export const settings = {
}, },
bespin: { bespin: {
background: '#28211c', background: '#28211c',
lineBackground: '#28211c50',
foreground: '#9d9b97', foreground: '#9d9b97',
caret: '#797977', caret: '#797977',
selection: '#36312e', selection: '#36312e',
@ -142,6 +152,7 @@ export const settings = {
}, },
darcula: { darcula: {
background: '#2B2B2B', background: '#2B2B2B',
lineBackground: '#2B2B2B50',
foreground: '#f8f8f2', foreground: '#f8f8f2',
caret: '#FFFFFF', caret: '#FFFFFF',
selection: 'rgba(255, 255, 255, 0.1)', selection: 'rgba(255, 255, 255, 0.1)',
@ -153,6 +164,7 @@ export const settings = {
}, },
dracula: { dracula: {
background: '#282a36', background: '#282a36',
lineBackground: '#282a3650',
foreground: '#f8f8f2', foreground: '#f8f8f2',
caret: '#f8f8f0', caret: '#f8f8f0',
selection: 'rgba(255, 255, 255, 0.1)', selection: 'rgba(255, 255, 255, 0.1)',
@ -163,7 +175,9 @@ export const settings = {
lineHighlight: 'rgba(255, 255, 255, 0.1)', lineHighlight: 'rgba(255, 255, 255, 0.1)',
}, },
duotoneLight: { duotoneLight: {
light: true,
background: '#faf8f5', background: '#faf8f5',
lineBackground: '#faf8f550',
foreground: '#b29762', foreground: '#b29762',
caret: '#93abdc', caret: '#93abdc',
selection: '#e3dcce', selection: '#e3dcce',
@ -175,6 +189,7 @@ export const settings = {
}, },
duotoneDark: { duotoneDark: {
background: '#2a2734', background: '#2a2734',
lineBackground: '#2a273450',
foreground: '#6c6783', foreground: '#6c6783',
caret: '#ffad5c', caret: '#ffad5c',
selection: 'rgba(255, 255, 255, 0.1)', selection: 'rgba(255, 255, 255, 0.1)',
@ -185,6 +200,7 @@ export const settings = {
eclipse: { eclipse: {
light: true, light: true,
background: '#fff', background: '#fff',
lineBackground: '#ffffff50',
foreground: '#000', foreground: '#000',
caret: '#FFFFFF', caret: '#FFFFFF',
selection: '#d7d4f0', selection: '#d7d4f0',
@ -197,6 +213,7 @@ export const settings = {
githubLight: { githubLight: {
light: true, light: true,
background: '#fff', background: '#fff',
lineBackground: '#ffffff50',
foreground: '#24292e', foreground: '#24292e',
selection: '#BBDFFF', selection: '#BBDFFF',
selectionMatch: '#BBDFFF', selectionMatch: '#BBDFFF',
@ -205,6 +222,7 @@ export const settings = {
}, },
githubDark: { githubDark: {
background: '#0d1117', background: '#0d1117',
lineBackground: '#0d111750',
foreground: '#c9d1d9', foreground: '#c9d1d9',
caret: '#c9d1d9', caret: '#c9d1d9',
selection: '#003d73', selection: '#003d73',
@ -213,6 +231,7 @@ export const settings = {
}, },
gruvboxDark: { gruvboxDark: {
background: '#282828', background: '#282828',
lineBackground: '#28282850',
foreground: '#ebdbb2', foreground: '#ebdbb2',
caret: '#ebdbb2', caret: '#ebdbb2',
selection: '#bdae93', selection: '#bdae93',
@ -224,6 +243,7 @@ export const settings = {
gruvboxLight: { gruvboxLight: {
light: true, light: true,
background: '#fbf1c7', background: '#fbf1c7',
lineBackground: '#fbf1c750',
foreground: '#3c3836', foreground: '#3c3836',
caret: '#af3a03', caret: '#af3a03',
selection: '#ebdbb2', selection: '#ebdbb2',
@ -235,6 +255,7 @@ export const settings = {
}, },
materialDark: { materialDark: {
background: '#2e3235', background: '#2e3235',
lineBackground: '#2e323550',
foreground: '#bdbdbd', foreground: '#bdbdbd',
caret: '#a0a4ae', caret: '#a0a4ae',
selection: '#d7d4f0', selection: '#d7d4f0',
@ -247,6 +268,7 @@ export const settings = {
materialLight: { materialLight: {
light: true, light: true,
background: '#FAFAFA', background: '#FAFAFA',
lineBackground: '#FAFAFA50',
foreground: '#90A4AE', foreground: '#90A4AE',
caret: '#272727', caret: '#272727',
selection: '#80CBC440', selection: '#80CBC440',
@ -259,6 +281,7 @@ export const settings = {
noctisLilac: { noctisLilac: {
light: true, light: true,
background: '#f2f1f8', background: '#f2f1f8',
lineBackground: '#f2f1f850',
foreground: '#0c006b', foreground: '#0c006b',
caret: '#5c49e9', caret: '#5c49e9',
selection: '#d5d1f2', selection: '#d5d1f2',
@ -269,6 +292,7 @@ export const settings = {
}, },
nord: { nord: {
background: '#2e3440', background: '#2e3440',
lineBackground: '#2e344050',
foreground: '#FFFFFF', foreground: '#FFFFFF',
caret: '#FFFFFF', caret: '#FFFFFF',
selection: '#3b4252', selection: '#3b4252',
@ -280,6 +304,7 @@ export const settings = {
}, },
okaidia: { okaidia: {
background: '#272822', background: '#272822',
lineBackground: '#27282250',
foreground: '#FFFFFF', foreground: '#FFFFFF',
caret: '#FFFFFF', caret: '#FFFFFF',
selection: '#49483E', selection: '#49483E',
@ -291,6 +316,7 @@ export const settings = {
solarizedLight: { solarizedLight: {
light: true, light: true,
background: '#fdf6e3', background: '#fdf6e3',
lineBackground: '#fdf6e350',
foreground: '#657b83', foreground: '#657b83',
caret: '#586e75', caret: '#586e75',
selection: '#dfd9c8', selection: '#dfd9c8',
@ -301,6 +327,7 @@ export const settings = {
}, },
solarizedDark: { solarizedDark: {
background: '#002b36', background: '#002b36',
lineBackground: '#002b3650',
foreground: '#93a1a1', foreground: '#93a1a1',
caret: '#839496', caret: '#839496',
selection: '#173541', selection: '#173541',
@ -311,6 +338,7 @@ export const settings = {
}, },
sublime: { sublime: {
background: '#303841', background: '#303841',
lineBackground: '#30384150',
foreground: '#FFFFFF', foreground: '#FFFFFF',
caret: '#FBAC52', caret: '#FBAC52',
selection: '#4C5964', selection: '#4C5964',
@ -322,6 +350,7 @@ export const settings = {
tokyoNightDay: { tokyoNightDay: {
light: true, light: true,
background: '#e1e2e7', background: '#e1e2e7',
lineBackground: '#e1e2e750',
foreground: '#3760bf', foreground: '#3760bf',
caret: '#3760bf', caret: '#3760bf',
selection: '#99a7df', selection: '#99a7df',
@ -333,6 +362,7 @@ export const settings = {
}, },
tokyoNightStorm: { tokyoNightStorm: {
background: '#24283b', background: '#24283b',
lineBackground: '#24283b50',
foreground: '#7982a9', foreground: '#7982a9',
caret: '#c0caf5', caret: '#c0caf5',
selection: '#6f7bb630', selection: '#6f7bb630',
@ -344,6 +374,7 @@ export const settings = {
}, },
tokyoNight: { tokyoNight: {
background: '#1a1b26', background: '#1a1b26',
lineBackground: '#1a1b2650',
foreground: '#787c99', foreground: '#787c99',
caret: '#c0caf5', caret: '#c0caf5',
selection: '#515c7e40', selection: '#515c7e40',
@ -355,6 +386,7 @@ export const settings = {
}, },
vscodeDark: { vscodeDark: {
background: '#1e1e1e', background: '#1e1e1e',
lineBackground: '#1e1e1e50',
foreground: '#9cdcfe', foreground: '#9cdcfe',
caret: '#c6c6c6', caret: '#c6c6c6',
selection: '#6199ff2f', selection: '#6199ff2f',
@ -368,6 +400,7 @@ export const settings = {
xcodeLight: { xcodeLight: {
light: true, light: true,
background: '#fff', background: '#fff',
lineBackground: '#ffffff50',
foreground: '#3D3D3D', foreground: '#3D3D3D',
selection: '#BBDFFF', selection: '#BBDFFF',
selectionMatch: '#BBDFFF', selectionMatch: '#BBDFFF',
@ -377,6 +410,7 @@ export const settings = {
}, },
xcodeDark: { xcodeDark: {
background: '#292A30', background: '#292A30',
lineBackground: '#292A3050',
foreground: '#CECFD0', foreground: '#CECFD0',
caret: '#fff', caret: '#fff',
selection: '#727377', selection: '#727377',
@ -400,6 +434,7 @@ function getColors(str) {
return colors; return colors;
} }
// TODO: remove
export function themeColors(theme) { export function themeColors(theme) {
return getColors(stringifySafe(theme)); return getColors(stringifySafe(theme));
} }

View file

@ -15,6 +15,7 @@ module.exports = {
highlight: '#bb8800', highlight: '#bb8800',
// codemirror-theme settings // codemirror-theme settings
background: 'var(--background)', background: 'var(--background)',
lineBackground: 'var(--lineBackground)',
foreground: 'var(--foreground)', foreground: 'var(--foreground)',
caret: 'var(--caret)', caret: 'var(--caret)',
selection: 'var(--selection)', selection: 'var(--selection)',