mirror of
https://github.com/eliasstepanik/strudel.git
synced 2026-01-11 13:48:40 +00:00
add vim toggle to settings
+ added persistent global state store + refactored themes to use the new store
This commit is contained in:
parent
4a3540cf2b
commit
014555fe5d
@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import _CodeMirror from '@uiw/react-codemirror';
|
import _CodeMirror from '@uiw/react-codemirror';
|
||||||
import { EditorView, Decoration } from '@codemirror/view';
|
import { EditorView, Decoration } from '@codemirror/view';
|
||||||
import { StateField, StateEffect } from '@codemirror/state';
|
import { StateField, StateEffect } from '@codemirror/state';
|
||||||
@ -83,9 +83,8 @@ const highlightField = StateField.define({
|
|||||||
provide: (f) => EditorView.decorations.from(f),
|
provide: (f) => EditorView.decorations.from(f),
|
||||||
});
|
});
|
||||||
|
|
||||||
const extensions = [
|
const staticExtensions = [
|
||||||
javascript(),
|
javascript(),
|
||||||
vim(),
|
|
||||||
highlightField,
|
highlightField,
|
||||||
flashField,
|
flashField,
|
||||||
// javascriptLanguage.data.of({ autocomplete: strudelAutocomplete }),
|
// javascriptLanguage.data.of({ autocomplete: strudelAutocomplete }),
|
||||||
@ -99,6 +98,7 @@ export default function CodeMirror({
|
|||||||
onViewChanged,
|
onViewChanged,
|
||||||
onSelectionChange,
|
onSelectionChange,
|
||||||
theme,
|
theme,
|
||||||
|
vimMode,
|
||||||
options,
|
options,
|
||||||
editorDidMount,
|
editorDidMount,
|
||||||
}) {
|
}) {
|
||||||
@ -122,6 +122,7 @@ export default function CodeMirror({
|
|||||||
},
|
},
|
||||||
[onSelectionChange],
|
[onSelectionChange],
|
||||||
);
|
);
|
||||||
|
const extensions = useMemo(() => [...staticExtensions, ...(vimMode ? [vim()] : [])], [vimMode]);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<_CodeMirror
|
<_CodeMirror
|
||||||
|
|||||||
74
pnpm-lock.yaml
generated
74
pnpm-lock.yaml
generated
@ -373,6 +373,7 @@ importers:
|
|||||||
'@strudel.cycles/webaudio': workspace:*
|
'@strudel.cycles/webaudio': workspace:*
|
||||||
'@strudel.cycles/xen': workspace:*
|
'@strudel.cycles/xen': workspace:*
|
||||||
'@supabase/supabase-js': ^1.35.3
|
'@supabase/supabase-js': ^1.35.3
|
||||||
|
'@tailwindcss/forms': ^0.5.3
|
||||||
'@tailwindcss/typography': ^0.5.8
|
'@tailwindcss/typography': ^0.5.8
|
||||||
'@types/node': ^18.0.0
|
'@types/node': ^18.0.0
|
||||||
'@types/react': ^18.0.26
|
'@types/react': ^18.0.26
|
||||||
@ -417,6 +418,7 @@ importers:
|
|||||||
'@strudel.cycles/webaudio': link:../packages/webaudio
|
'@strudel.cycles/webaudio': link:../packages/webaudio
|
||||||
'@strudel.cycles/xen': link:../packages/xen
|
'@strudel.cycles/xen': link:../packages/xen
|
||||||
'@supabase/supabase-js': 1.35.7
|
'@supabase/supabase-js': 1.35.7
|
||||||
|
'@tailwindcss/forms': 0.5.3_tailwindcss@3.2.4
|
||||||
'@tailwindcss/typography': 0.5.9_tailwindcss@3.2.4
|
'@tailwindcss/typography': 0.5.9_tailwindcss@3.2.4
|
||||||
'@types/node': 18.11.18
|
'@types/node': 18.11.18
|
||||||
'@types/react': 18.0.27
|
'@types/react': 18.0.27
|
||||||
@ -3575,6 +3577,15 @@ packages:
|
|||||||
string.prototype.matchall: 4.0.8
|
string.prototype.matchall: 4.0.8
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@tailwindcss/forms/0.5.3_tailwindcss@3.2.4:
|
||||||
|
resolution: {integrity: sha512-y5mb86JUoiUgBjY/o6FJSFZSEttfb3Q5gllE4xoKjAAD+vBrnIhE4dViwUuow3va8mpH4s9jyUbUbrRGoRdc2Q==}
|
||||||
|
peerDependencies:
|
||||||
|
tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1'
|
||||||
|
dependencies:
|
||||||
|
mini-svg-data-uri: 1.4.4
|
||||||
|
tailwindcss: 3.2.4
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@tailwindcss/typography/0.5.9_tailwindcss@3.2.4:
|
/@tailwindcss/typography/0.5.9_tailwindcss@3.2.4:
|
||||||
resolution: {integrity: sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==}
|
resolution: {integrity: sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -9386,6 +9397,11 @@ packages:
|
|||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/mini-svg-data-uri/1.4.4:
|
||||||
|
resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==}
|
||||||
|
hasBin: true
|
||||||
|
dev: false
|
||||||
|
|
||||||
/minimatch/3.1.2:
|
/minimatch/3.1.2:
|
||||||
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -10428,17 +10444,6 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/postcss-import/14.1.0:
|
|
||||||
resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==}
|
|
||||||
engines: {node: '>=10.0.0'}
|
|
||||||
peerDependencies:
|
|
||||||
postcss: ^8.0.0
|
|
||||||
dependencies:
|
|
||||||
postcss-value-parser: 4.2.0
|
|
||||||
read-cache: 1.0.0
|
|
||||||
resolve: 1.22.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/postcss-import/14.1.0_postcss@8.4.21:
|
/postcss-import/14.1.0_postcss@8.4.21:
|
||||||
resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==}
|
resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==}
|
||||||
engines: {node: '>=10.0.0'}
|
engines: {node: '>=10.0.0'}
|
||||||
@ -10449,16 +10454,6 @@ packages:
|
|||||||
postcss-value-parser: 4.2.0
|
postcss-value-parser: 4.2.0
|
||||||
read-cache: 1.0.0
|
read-cache: 1.0.0
|
||||||
resolve: 1.22.1
|
resolve: 1.22.1
|
||||||
dev: true
|
|
||||||
|
|
||||||
/postcss-js/4.0.0:
|
|
||||||
resolution: {integrity: sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==}
|
|
||||||
engines: {node: ^12 || ^14 || >= 16}
|
|
||||||
peerDependencies:
|
|
||||||
postcss: ^8.3.3
|
|
||||||
dependencies:
|
|
||||||
camelcase-css: 2.0.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/postcss-js/4.0.0_postcss@8.4.21:
|
/postcss-js/4.0.0_postcss@8.4.21:
|
||||||
resolution: {integrity: sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==}
|
resolution: {integrity: sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==}
|
||||||
@ -10468,23 +10463,6 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
camelcase-css: 2.0.1
|
camelcase-css: 2.0.1
|
||||||
postcss: 8.4.21
|
postcss: 8.4.21
|
||||||
dev: true
|
|
||||||
|
|
||||||
/postcss-load-config/3.1.4:
|
|
||||||
resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==}
|
|
||||||
engines: {node: '>= 10'}
|
|
||||||
peerDependencies:
|
|
||||||
postcss: '>=8.0.9'
|
|
||||||
ts-node: '>=9.0.0'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
postcss:
|
|
||||||
optional: true
|
|
||||||
ts-node:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
lilconfig: 2.0.6
|
|
||||||
yaml: 1.10.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/postcss-load-config/3.1.4_postcss@8.4.21:
|
/postcss-load-config/3.1.4_postcss@8.4.21:
|
||||||
resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==}
|
resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==}
|
||||||
@ -10502,15 +10480,6 @@ packages:
|
|||||||
postcss: 8.4.21
|
postcss: 8.4.21
|
||||||
yaml: 1.10.2
|
yaml: 1.10.2
|
||||||
|
|
||||||
/postcss-nested/6.0.0:
|
|
||||||
resolution: {integrity: sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==}
|
|
||||||
engines: {node: '>=12.0'}
|
|
||||||
peerDependencies:
|
|
||||||
postcss: ^8.2.14
|
|
||||||
dependencies:
|
|
||||||
postcss-selector-parser: 6.0.11
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/postcss-nested/6.0.0_postcss@8.4.21:
|
/postcss-nested/6.0.0_postcss@8.4.21:
|
||||||
resolution: {integrity: sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==}
|
resolution: {integrity: sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==}
|
||||||
engines: {node: '>=12.0'}
|
engines: {node: '>=12.0'}
|
||||||
@ -10519,7 +10488,6 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
postcss: 8.4.21
|
postcss: 8.4.21
|
||||||
postcss-selector-parser: 6.0.11
|
postcss-selector-parser: 6.0.11
|
||||||
dev: true
|
|
||||||
|
|
||||||
/postcss-selector-parser/6.0.10:
|
/postcss-selector-parser/6.0.10:
|
||||||
resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
|
resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
|
||||||
@ -12142,8 +12110,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ==}
|
resolution: {integrity: sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ==}
|
||||||
engines: {node: '>=12.13.0'}
|
engines: {node: '>=12.13.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
|
||||||
postcss: ^8.0.9
|
|
||||||
dependencies:
|
dependencies:
|
||||||
arg: 5.0.2
|
arg: 5.0.2
|
||||||
chokidar: 3.5.3
|
chokidar: 3.5.3
|
||||||
@ -12160,10 +12126,10 @@ packages:
|
|||||||
object-hash: 3.0.0
|
object-hash: 3.0.0
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
postcss: 8.4.21
|
postcss: 8.4.21
|
||||||
postcss-import: 14.1.0
|
postcss-import: 14.1.0_postcss@8.4.21
|
||||||
postcss-js: 4.0.0
|
postcss-js: 4.0.0_postcss@8.4.21
|
||||||
postcss-load-config: 3.1.4
|
postcss-load-config: 3.1.4_postcss@8.4.21
|
||||||
postcss-nested: 6.0.0
|
postcss-nested: 6.0.0_postcss@8.4.21
|
||||||
postcss-selector-parser: 6.0.11
|
postcss-selector-parser: 6.0.11
|
||||||
postcss-value-parser: 4.2.0
|
postcss-value-parser: 4.2.0
|
||||||
quick-lru: 5.1.1
|
quick-lru: 5.1.1
|
||||||
|
|||||||
@ -34,6 +34,7 @@
|
|||||||
"@strudel.cycles/webaudio": "workspace:*",
|
"@strudel.cycles/webaudio": "workspace:*",
|
||||||
"@strudel.cycles/xen": "workspace:*",
|
"@strudel.cycles/xen": "workspace:*",
|
||||||
"@supabase/supabase-js": "^1.35.3",
|
"@supabase/supabase-js": "^1.35.3",
|
||||||
|
"@tailwindcss/forms": "^0.5.3",
|
||||||
"@tailwindcss/typography": "^0.5.8",
|
"@tailwindcss/typography": "^0.5.8",
|
||||||
"@types/node": "^18.0.0",
|
"@types/node": "^18.0.0",
|
||||||
"@types/react": "^18.0.26",
|
"@types/react": "^18.0.26",
|
||||||
|
|||||||
36
website/public/store.mjs
Normal file
36
website/public/store.mjs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
export const storeKey = 'strudel-settings';
|
||||||
|
|
||||||
|
export function get(prop) {
|
||||||
|
const state = JSON.parse(localStorage.getItem(storeKey));
|
||||||
|
if (!prop) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
return state[prop];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function set(next) {
|
||||||
|
localStorage.setItem(storeKey, JSON.stringify(next));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateState(func) {
|
||||||
|
const prev = get();
|
||||||
|
const next = func(prev);
|
||||||
|
set(next);
|
||||||
|
document.dispatchEvent(
|
||||||
|
new CustomEvent(storeKey, {
|
||||||
|
detail: { next, prev },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function watch(func, prop) {
|
||||||
|
document.addEventListener(storeKey, (e) => {
|
||||||
|
const { prev, next } = e.detail;
|
||||||
|
const hasPropChanged = (p) => next[p] !== prev[p];
|
||||||
|
if (!prop) {
|
||||||
|
func(next);
|
||||||
|
} else if (hasPropChanged(prop)) {
|
||||||
|
func(next[prop]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -48,7 +48,8 @@ const { strudelTheme } = settings;
|
|||||||
</style>
|
</style>
|
||||||
{pwaInfo && <Fragment set:html={pwaInfo.webManifest.linkTag} />}
|
{pwaInfo && <Fragment set:html={pwaInfo.webManifest.linkTag} />}
|
||||||
|
|
||||||
<script define:vars={{ settings, strudelTheme }} is:inline>
|
<script define:vars={{ settings, strudelTheme }} is:inline type="module">
|
||||||
|
import { watch, get } from './store.mjs';
|
||||||
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);
|
||||||
@ -76,9 +77,7 @@ const { strudelTheme } = settings;
|
|||||||
} else {
|
} else {
|
||||||
document.documentElement.classList.add('dark');
|
document.documentElement.classList.add('dark');
|
||||||
}
|
}
|
||||||
// persist theme name
|
|
||||||
localStorage.setItem('strudel-theme', name || 'strudelTheme');
|
|
||||||
}
|
}
|
||||||
setTheme(localStorage.getItem('strudel-theme'));
|
setTheme(get().theme);
|
||||||
document.addEventListener('strudel-theme', (e) => setTheme(e.detail));
|
watch(setTheme, 'theme');
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -36,7 +36,6 @@ export function MiniRepl({ tune, drawTime, punchcard, canvasHeight = 100 }) {
|
|||||||
.then(([res]) => setRepl(() => res.MiniRepl))
|
.then(([res]) => setRepl(() => res.MiniRepl))
|
||||||
.catch((err) => console.error(err));
|
.catch((err) => console.error(err));
|
||||||
}, []);
|
}, []);
|
||||||
// const { settings } = useTheme();
|
|
||||||
return Repl ? (
|
return Repl ? (
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<Repl
|
<Repl
|
||||||
|
|||||||
@ -7,11 +7,12 @@ import React, { useCallback, useLayoutEffect, useRef, useState } from 'react';
|
|||||||
import { loadedSamples } from './Repl';
|
import { loadedSamples } from './Repl';
|
||||||
import { Reference } from './Reference';
|
import { Reference } from './Reference';
|
||||||
import { themes, themeColors } from './themes.mjs';
|
import { themes, themeColors } from './themes.mjs';
|
||||||
|
import useStore from '../useStore.mjs';
|
||||||
|
|
||||||
export function Footer({ context }) {
|
export function Footer({ context }) {
|
||||||
// const [activeFooter, setActiveFooter] = useState('console');
|
// const [activeFooter, setActiveFooter] = useState('console');
|
||||||
// const { activeFooter, setActiveFooter, isZen } = useContext?.(ReplContext);
|
// const { activeFooter, setActiveFooter, isZen } = useContext?.(ReplContext);
|
||||||
const { activeFooter, setActiveFooter, isZen, theme, setTheme } = context;
|
const { activeFooter, setActiveFooter, isZen } = context;
|
||||||
const footerContent = useRef();
|
const footerContent = useRef();
|
||||||
const [log, setLog] = useState([]);
|
const [log, setLog] = useState([]);
|
||||||
|
|
||||||
@ -93,7 +94,7 @@ export function Footer({ context }) {
|
|||||||
{activeFooter === 'console' && <ConsoleTab log={log} />}
|
{activeFooter === 'console' && <ConsoleTab log={log} />}
|
||||||
{activeFooter === 'samples' && <SamplesTab />}
|
{activeFooter === 'samples' && <SamplesTab />}
|
||||||
{activeFooter === 'reference' && <Reference />}
|
{activeFooter === 'reference' && <Reference />}
|
||||||
{activeFooter === 'settings' && <SettingsTab theme={theme} setTheme={setTheme} />}
|
{activeFooter === 'settings' && <SettingsTab />}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</footer>
|
</footer>
|
||||||
@ -205,37 +206,34 @@ function SamplesTab() {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
function SettingsTab({ theme, setTheme }) {
|
function SettingsTab() {
|
||||||
/*<input type="checkbox" value={vimMode} onChange={(checked)=>{
|
const { state, update } = useStore();
|
||||||
console.log('vim mode toggle', checked)
|
const { theme, vim } = state;
|
||||||
}}/>*/
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<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="text-foreground grid space-y-4 p-2">
|
||||||
{Object.entries(themes).map(([k, t]) => (
|
<label className="space-x-2">
|
||||||
<div
|
<span>Theme</span>
|
||||||
key={k}
|
<select
|
||||||
className={cx(
|
className="p-2 bg-background rounded-md text-foreground"
|
||||||
'border-2 border-transparent cursor-pointer p-4 bg-background bg-opacity-25 rounded-md',
|
value={theme}
|
||||||
theme === k ? '!border-foreground' : '',
|
onChange={(e) => update((current) => ({ ...current, theme: e.target.value }))}
|
||||||
)}
|
|
||||||
onClick={() => {
|
|
||||||
setTheme(k);
|
|
||||||
document.dispatchEvent(
|
|
||||||
new CustomEvent('strudel-theme', {
|
|
||||||
detail: k,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div className="mb-2 w-full text-center text-foreground">{k}</div>
|
{Object.entries(themes).map(([k, t]) => (
|
||||||
<div className="flex justify-stretch overflow-hidden rounded-md">
|
<option key={k} className="bg-background">
|
||||||
{themeColors(t).map((c, i) => (
|
{k}
|
||||||
<div key={i} className="grow h-6" style={{ background: c }} />
|
</option>
|
||||||
))}
|
))}
|
||||||
</div>
|
</select>
|
||||||
</div>
|
</label>
|
||||||
))}
|
<label className="space-x-2">
|
||||||
|
<input
|
||||||
|
className="bg-background w-5 h-5 rounded-md"
|
||||||
|
type="checkbox"
|
||||||
|
checked={vim}
|
||||||
|
onChange={(e) => update((current) => ({ ...current, vim: e.target.checked }))}
|
||||||
|
/>
|
||||||
|
<span>Vim Mode</span>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import * as tunes from './tunes.mjs';
|
|||||||
import PlayCircleIcon from '@heroicons/react/20/solid/PlayCircleIcon';
|
import PlayCircleIcon from '@heroicons/react/20/solid/PlayCircleIcon';
|
||||||
import { themes } from './themes.mjs';
|
import { themes } from './themes.mjs';
|
||||||
import useTheme from '../useTheme';
|
import useTheme from '../useTheme';
|
||||||
|
import useStore from '../useStore.mjs';
|
||||||
|
|
||||||
const initialTheme = localStorage.getItem('strudel-theme') || 'strudelTheme';
|
const initialTheme = localStorage.getItem('strudel-theme') || 'strudelTheme';
|
||||||
|
|
||||||
@ -113,12 +114,16 @@ export const ReplContext = createContext(null);
|
|||||||
export function Repl({ embedded = false }) {
|
export function Repl({ embedded = false }) {
|
||||||
const isEmbedded = embedded || window.location !== window.parent.location;
|
const isEmbedded = embedded || window.location !== window.parent.location;
|
||||||
const [view, setView] = useState(); // codemirror view
|
const [view, setView] = useState(); // codemirror view
|
||||||
const [theme, setTheme] = useState(initialTheme);
|
|
||||||
const [lastShared, setLastShared] = useState();
|
const [lastShared, setLastShared] = useState();
|
||||||
const [activeFooter, setActiveFooter] = useState('');
|
const [activeFooter, setActiveFooter] = useState('');
|
||||||
const [isZen, setIsZen] = useState(false);
|
const [isZen, setIsZen] = useState(false);
|
||||||
const [pending, setPending] = useState(false);
|
const [pending, setPending] = useState(false);
|
||||||
|
|
||||||
|
const { theme, themeSettings } = useTheme();
|
||||||
|
const {
|
||||||
|
state: { vim },
|
||||||
|
} = useStore();
|
||||||
|
|
||||||
const { code, setCode, scheduler, evaluate, activateCode, isDirty, activeCode, pattern, started, stop, error } =
|
const { code, setCode, scheduler, evaluate, activateCode, isDirty, activeCode, pattern, started, stop, error } =
|
||||||
useStrudel({
|
useStrudel({
|
||||||
initialCode: '// LOADING',
|
initialCode: '// LOADING',
|
||||||
@ -172,15 +177,13 @@ export function Repl({ embedded = false }) {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const { settings } = useTheme();
|
|
||||||
|
|
||||||
// highlighting
|
// highlighting
|
||||||
useHighlighting({
|
useHighlighting({
|
||||||
view,
|
view,
|
||||||
pattern,
|
pattern,
|
||||||
active: started && !activeCode?.includes('strudel disable-highlighting'),
|
active: started && !activeCode?.includes('strudel disable-highlighting'),
|
||||||
getTime: () => scheduler.now(),
|
getTime: () => scheduler.now(),
|
||||||
color: settings?.foreground,
|
color: themeSettings?.foreground,
|
||||||
});
|
});
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -263,8 +266,6 @@ export function Repl({ embedded = false }) {
|
|||||||
handleShare,
|
handleShare,
|
||||||
isZen,
|
isZen,
|
||||||
setIsZen,
|
setIsZen,
|
||||||
theme,
|
|
||||||
setTheme,
|
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
// bg-gradient-to-t from-blue-900 to-slate-900
|
// bg-gradient-to-t from-blue-900 to-slate-900
|
||||||
@ -281,6 +282,7 @@ export function Repl({ embedded = false }) {
|
|||||||
<CodeMirror
|
<CodeMirror
|
||||||
theme={themes[theme] || themes.strudelTheme}
|
theme={themes[theme] || themes.strudelTheme}
|
||||||
value={code}
|
value={code}
|
||||||
|
vimMode={vim}
|
||||||
onChange={handleChangeCode}
|
onChange={handleChangeCode}
|
||||||
onViewChanged={setView}
|
onViewChanged={setView}
|
||||||
onSelectionChange={handleSelectionChange}
|
onSelectionChange={handleSelectionChange}
|
||||||
|
|||||||
11
website/src/useStore.mjs
Normal file
11
website/src/useStore.mjs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { useEvent } from '@strudel.cycles/react';
|
||||||
|
import * as Store from '../public/store.mjs';
|
||||||
|
|
||||||
|
function useStore() {
|
||||||
|
const [state, setState] = useState(Store.get());
|
||||||
|
useEvent(Store.storeKey, (e) => setState(e.detail.next));
|
||||||
|
return { state, update: Store.updateState };
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useStore;
|
||||||
@ -1,27 +1,14 @@
|
|||||||
import { useState } from 'react';
|
|
||||||
import { settings } from './repl/themes.mjs';
|
import { settings } from './repl/themes.mjs';
|
||||||
import { useEffect } from 'react';
|
import useStore from './useStore.mjs';
|
||||||
|
|
||||||
function useTheme() {
|
function useTheme() {
|
||||||
const [theme, setTheme] = useState(localStorage.getItem('strudel-theme'));
|
const { state } = useStore();
|
||||||
useEvent('strudel-theme', (e) => setTheme(e.detail));
|
const theme = state.theme || 'strudelTheme';
|
||||||
const themeSettings = settings[theme || 'strudelTheme'];
|
const themeSettings = settings[theme];
|
||||||
return {
|
return {
|
||||||
theme,
|
theme: state.theme,
|
||||||
setTheme,
|
themeSettings,
|
||||||
settings: themeSettings,
|
|
||||||
isDark: !themeSettings.light,
|
|
||||||
isLight: !!themeSettings.light,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// TODO: dedupe
|
|
||||||
function useEvent(name, onTrigger, useCapture = false) {
|
|
||||||
useEffect(() => {
|
|
||||||
document.addEventListener(name, onTrigger, useCapture);
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener(name, onTrigger, useCapture);
|
|
||||||
};
|
|
||||||
}, [onTrigger]);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default useTheme;
|
export default useTheme;
|
||||||
|
|||||||
@ -41,5 +41,5 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [require('@tailwindcss/typography')],
|
plugins: [require('@tailwindcss/typography'), require('@tailwindcss/forms')],
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user