Merge pull request #865 from tidalcycles/vanilla-repl-2

Vanilla repl 3
This commit is contained in:
Felix Roos 2023-12-15 00:11:35 +01:00 committed by GitHub
commit 466574a856
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 139 additions and 35 deletions

View File

@ -3,7 +3,7 @@ import { closeBrackets } from '@codemirror/autocomplete';
import { history } from '@codemirror/commands'; import { history } from '@codemirror/commands';
import { javascript } from '@codemirror/lang-javascript'; import { javascript } from '@codemirror/lang-javascript';
import { defaultHighlightStyle, syntaxHighlighting } from '@codemirror/language'; 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 { EditorView, highlightActiveLineGutter, highlightActiveLine, keymap, lineNumbers } from '@codemirror/view';
import { Pattern, Drawer, repl, cleanupDraw } from '@strudel.cycles/core'; import { Pattern, Drawer, repl, cleanupDraw } from '@strudel.cycles/core';
// import { isAutoCompletionEnabled } from './Autocomplete'; // import { isAutoCompletionEnabled } from './Autocomplete';
@ -44,27 +44,28 @@ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, set
syntaxHighlighting(defaultHighlightStyle), syntaxHighlighting(defaultHighlightStyle),
history(), history(),
EditorView.updateListener.of((v) => onChange(v)), EditorView.updateListener.of((v) => onChange(v)),
keymap.of([ Prec.highest(
{ keymap.of([
key: 'Ctrl-Enter', {
run: () => onEvaluate?.(), key: 'Ctrl-Enter',
}, run: () => onEvaluate?.(),
{
key: 'Alt-Enter',
run: () => onEvaluate?.(),
},
{
key: 'Ctrl-.',
run: () => onStop?.(),
},
{
key: 'Alt-.',
run: (_, e) => {
e.preventDefault();
onStop?.();
}, },
}, {
/* { key: 'Alt-Enter',
run: () => onEvaluate?.(),
},
{
key: 'Ctrl-.',
run: () => onStop?.(),
},
{
key: 'Alt-.',
run: (_, e) => {
e.preventDefault();
onStop?.();
},
},
/* {
key: 'Ctrl-Shift-.', key: 'Ctrl-Shift-.',
run: () => (onPanic ? onPanic() : onStop?.()), run: () => (onPanic ? onPanic() : onStop?.()),
}, },
@ -72,7 +73,8 @@ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, set
key: 'Ctrl-Shift-Enter', key: 'Ctrl-Shift-Enter',
run: () => (onReEvaluate ? onReEvaluate() : onEvaluate?.()), run: () => (onReEvaluate ? onReEvaluate() : onEvaluate?.()),
}, */ }, */
]), ]),
),
], ],
}); });
@ -150,6 +152,11 @@ export class StrudelMirror {
onEvaluate: () => this.evaluate(), onEvaluate: () => this.evaluate(),
onStop: () => this.stop(), onStop: () => this.stop(),
}); });
const cmEditor = this.root.querySelector('.cm-editor');
if (cmEditor) {
this.root.style.backgroundColor = 'var(--background)';
cmEditor.style.backgroundColor = 'transparent';
}
} }
async drawFirstFrame() { async drawFirstFrame() {
if (!this.onDraw) { if (!this.onDraw) {
@ -190,6 +197,10 @@ export class StrudelMirror {
} }
setFontFamily(family) { setFontFamily(family) {
this.root.style.fontFamily = family; this.root.style.fontFamily = family;
const scroller = this.root.querySelector('.cm-scroller');
if (scroller) {
scroller.style.fontFamily = family;
}
} }
reconfigureExtension(key, value) { reconfigureExtension(key, value) {
if (!extensions[key]) { if (!extensions[key]) {

View File

@ -2,3 +2,4 @@ export * from './codemirror.mjs';
export * from './highlight.mjs'; export * from './highlight.mjs';
export * from './flash.mjs'; export * from './flash.mjs';
export * from './slider.mjs'; export * from './slider.mjs';
export * from './themes.mjs';

View File

@ -473,6 +473,9 @@ function stringifySafe(json) {
return JSON.stringify(json, getCircularReplacer()); return JSON.stringify(json, getCircularReplacer());
} }
export const theme = (theme) => themes[theme] || themes.strudelTheme;
// css style injection helpers
export function injectStyle(rule) { export function injectStyle(rule) {
const newStyle = document.createElement('style'); const newStyle = document.createElement('style');
document.head.appendChild(newStyle); document.head.appendChild(newStyle);
@ -481,4 +484,38 @@ export function injectStyle(rule) {
return () => styleSheet.deleteRule(ruleIndex); 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);
}
}

View File

@ -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;
---
<!-- Global Metadata -->
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<link rel="icon" type="image/svg+xml" href={`${baseNoTrailing}/favicon.ico`} />
<meta
name="description"
content="Strudel is a music live coding environment for the browser, porting the TidalCycles pattern language to JavaScript."
/>
<link rel="icon" href={`${baseNoTrailing}/favicon.ico`} />
<link rel="apple-touch-icon" href={`${baseNoTrailing}/icons/apple-icon-180.png`} sizes="180x180" />
<meta name="theme-color" content="#222222" />
<base href={BASE_URL} />
<!-- Scrollable a11y code helper -->
<script src{`${baseNoTrailing}/make-scrollable-code-focusable.js`} is:inline></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: #22222299;
--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} />}
<script>
// https://medium.com/quick-code/100vh-problem-with-ios-safari-92ab23c852a8
const appHeight = () => {
const doc = document.documentElement;
doc.style.setProperty('--app-height', `${window.innerHeight - 1}px`);
};
if (typeof window !== 'undefined') {
window.addEventListener('resize', appHeight);
appHeight();
}
</script>

View File

@ -1,13 +1,13 @@
--- ---
import HeadCommon from '../../components/HeadCommon.astro'; import HeadCommonNew from '../../components/HeadCommonNew.astro';
--- ---
<html lang="en" class="dark"> <html lang="en" class="dark">
<head> <head>
<HeadCommon /> <HeadCommonNew />
<title>Strudel Vanilla REPL</title> <title>Strudel Vanilla REPL</title>
</head> </head>
<body class="h-app-height bg-background"> <body class="h-app-height">
<div class="settings"> <div class="settings">
<form name="settings" class="flex flex-col space-y-1 bg-[#00000080]"> <form name="settings" class="flex flex-col space-y-1 bg-[#00000080]">
<label <label
@ -60,10 +60,8 @@ import HeadCommon from '../../components/HeadCommon.astro';
<option>vscode</option> <option>vscode</option>
</select> </label </select> </label
><br /> ><br />
<!-- <label>fontFamily <label>fontFamily
<select name="fontFamily"> <select name="fontFamily">
<option>monospace</option>
<option>helvetica</option>
<option value="monospace">monospace</option> <option value="monospace">monospace</option>
<option value="BigBlueTerminal">BigBlueTerminal</option> <option value="BigBlueTerminal">BigBlueTerminal</option>
<option value="x3270">x3270</option> <option value="x3270">x3270</option>
@ -76,7 +74,7 @@ import HeadCommon from '../../components/HeadCommon.astro';
<option value="mode7">mode7</option> <option value="mode7">mode7</option>
</select> </select>
</label> </label>
<br /> --> <br />
<label>fontSize <input type="number" name="fontSize" /></label> <label>fontSize <input type="number" name="fontSize" /></label>
<br /> <br />
<label><input type="checkbox" name="isLineNumbersDisplayed" />isLineNumbersDisplayed</label> <label><input type="checkbox" name="isLineNumbersDisplayed" />isLineNumbersDisplayed</label>

View File

@ -1,7 +1,3 @@
:root {
--foreground: white;
}
body, body,
input { input {
font-family: monospace; font-family: monospace;

View File

@ -1,5 +1,5 @@
import { logger, getDrawContext, silence, controls, evalScope, hash2code, code2hash } from '@strudel.cycles/core'; 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 { transpiler } from '@strudel.cycles/transpiler';
import { import {
getAudioContext, getAudioContext,
@ -24,6 +24,7 @@ const initialSettings = {
fontFamily: 'monospace', fontFamily: 'monospace',
fontSize: 18, fontSize: 18,
}; };
initTheme(initialSettings.theme);
async function run() { async function run() {
const container = document.getElementById('code'); const container = document.getElementById('code');
@ -196,4 +197,6 @@ form.addEventListener('change', () => {
const values = getFormValues(form, initialSettings); const values = getFormValues(form, initialSettings);
// console.log('values', values); // console.log('values', values);
editor.updateSettings(values); editor.updateSettings(values);
// TODO: only activateTheme when it changes
activateTheme(values.theme);
}); });