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
11 changed files with 120 additions and 120 deletions
|
|
@ -48,7 +48,8 @@ const { strudelTheme } = settings;
|
|||
</style>
|
||||
{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');
|
||||
themeStyle.id = 'strudel-theme';
|
||||
document.head.append(themeStyle);
|
||||
|
|
@ -76,9 +77,7 @@ const { strudelTheme } = settings;
|
|||
} else {
|
||||
document.documentElement.classList.add('dark');
|
||||
}
|
||||
// persist theme name
|
||||
localStorage.setItem('strudel-theme', name || 'strudelTheme');
|
||||
}
|
||||
setTheme(localStorage.getItem('strudel-theme'));
|
||||
document.addEventListener('strudel-theme', (e) => setTheme(e.detail));
|
||||
setTheme(get().theme);
|
||||
watch(setTheme, 'theme');
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ export function MiniRepl({ tune, drawTime, punchcard, canvasHeight = 100 }) {
|
|||
.then(([res]) => setRepl(() => res.MiniRepl))
|
||||
.catch((err) => console.error(err));
|
||||
}, []);
|
||||
// const { settings } = useTheme();
|
||||
return Repl ? (
|
||||
<div className="mb-4">
|
||||
<Repl
|
||||
|
|
|
|||
|
|
@ -7,11 +7,12 @@ import React, { useCallback, useLayoutEffect, useRef, useState } from 'react';
|
|||
import { loadedSamples } from './Repl';
|
||||
import { Reference } from './Reference';
|
||||
import { themes, themeColors } from './themes.mjs';
|
||||
import useStore from '../useStore.mjs';
|
||||
|
||||
export function Footer({ context }) {
|
||||
// const [activeFooter, setActiveFooter] = useState('console');
|
||||
// const { activeFooter, setActiveFooter, isZen } = useContext?.(ReplContext);
|
||||
const { activeFooter, setActiveFooter, isZen, theme, setTheme } = context;
|
||||
const { activeFooter, setActiveFooter, isZen } = context;
|
||||
const footerContent = useRef();
|
||||
const [log, setLog] = useState([]);
|
||||
|
||||
|
|
@ -93,7 +94,7 @@ export function Footer({ context }) {
|
|||
{activeFooter === 'console' && <ConsoleTab log={log} />}
|
||||
{activeFooter === 'samples' && <SamplesTab />}
|
||||
{activeFooter === 'reference' && <Reference />}
|
||||
{activeFooter === 'settings' && <SettingsTab theme={theme} setTheme={setTheme} />}
|
||||
{activeFooter === 'settings' && <SettingsTab />}
|
||||
</div>
|
||||
)}
|
||||
</footer>
|
||||
|
|
@ -205,37 +206,34 @@ function SamplesTab() {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
function SettingsTab({ theme, setTheme }) {
|
||||
/*<input type="checkbox" value={vimMode} onChange={(checked)=>{
|
||||
console.log('vim mode toggle', checked)
|
||||
}}/>*/
|
||||
|
||||
function SettingsTab() {
|
||||
const { state, update } = useStore();
|
||||
const { theme, vim } = state;
|
||||
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">
|
||||
{Object.entries(themes).map(([k, t]) => (
|
||||
<div
|
||||
key={k}
|
||||
className={cx(
|
||||
'border-2 border-transparent cursor-pointer p-4 bg-background bg-opacity-25 rounded-md',
|
||||
theme === k ? '!border-foreground' : '',
|
||||
)}
|
||||
onClick={() => {
|
||||
setTheme(k);
|
||||
document.dispatchEvent(
|
||||
new CustomEvent('strudel-theme', {
|
||||
detail: k,
|
||||
}),
|
||||
);
|
||||
}}
|
||||
<div className="text-foreground grid space-y-4 p-2">
|
||||
<label className="space-x-2">
|
||||
<span>Theme</span>
|
||||
<select
|
||||
className="p-2 bg-background rounded-md text-foreground"
|
||||
value={theme}
|
||||
onChange={(e) => update((current) => ({ ...current, theme: e.target.value }))}
|
||||
>
|
||||
<div className="mb-2 w-full text-center text-foreground">{k}</div>
|
||||
<div className="flex justify-stretch overflow-hidden rounded-md">
|
||||
{themeColors(t).map((c, i) => (
|
||||
<div key={i} className="grow h-6" style={{ background: c }} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{Object.entries(themes).map(([k, t]) => (
|
||||
<option key={k} className="bg-background">
|
||||
{k}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import * as tunes from './tunes.mjs';
|
|||
import PlayCircleIcon from '@heroicons/react/20/solid/PlayCircleIcon';
|
||||
import { themes } from './themes.mjs';
|
||||
import useTheme from '../useTheme';
|
||||
import useStore from '../useStore.mjs';
|
||||
|
||||
const initialTheme = localStorage.getItem('strudel-theme') || 'strudelTheme';
|
||||
|
||||
|
|
@ -113,12 +114,16 @@ export const ReplContext = createContext(null);
|
|||
export function Repl({ embedded = false }) {
|
||||
const isEmbedded = embedded || window.location !== window.parent.location;
|
||||
const [view, setView] = useState(); // codemirror view
|
||||
const [theme, setTheme] = useState(initialTheme);
|
||||
const [lastShared, setLastShared] = useState();
|
||||
const [activeFooter, setActiveFooter] = useState('');
|
||||
const [isZen, setIsZen] = 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 } =
|
||||
useStrudel({
|
||||
initialCode: '// LOADING',
|
||||
|
|
@ -172,15 +177,13 @@ export function Repl({ embedded = false }) {
|
|||
),
|
||||
);
|
||||
|
||||
const { settings } = useTheme();
|
||||
|
||||
// highlighting
|
||||
useHighlighting({
|
||||
view,
|
||||
pattern,
|
||||
active: started && !activeCode?.includes('strudel disable-highlighting'),
|
||||
getTime: () => scheduler.now(),
|
||||
color: settings?.foreground,
|
||||
color: themeSettings?.foreground,
|
||||
});
|
||||
|
||||
//
|
||||
|
|
@ -263,8 +266,6 @@ export function Repl({ embedded = false }) {
|
|||
handleShare,
|
||||
isZen,
|
||||
setIsZen,
|
||||
theme,
|
||||
setTheme,
|
||||
};
|
||||
return (
|
||||
// bg-gradient-to-t from-blue-900 to-slate-900
|
||||
|
|
@ -281,6 +282,7 @@ export function Repl({ embedded = false }) {
|
|||
<CodeMirror
|
||||
theme={themes[theme] || themes.strudelTheme}
|
||||
value={code}
|
||||
vimMode={vim}
|
||||
onChange={handleChangeCode}
|
||||
onViewChanged={setView}
|
||||
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 { useEffect } from 'react';
|
||||
import useStore from './useStore.mjs';
|
||||
|
||||
function useTheme() {
|
||||
const [theme, setTheme] = useState(localStorage.getItem('strudel-theme'));
|
||||
useEvent('strudel-theme', (e) => setTheme(e.detail));
|
||||
const themeSettings = settings[theme || 'strudelTheme'];
|
||||
const { state } = useStore();
|
||||
const theme = state.theme || 'strudelTheme';
|
||||
const themeSettings = settings[theme];
|
||||
return {
|
||||
theme,
|
||||
setTheme,
|
||||
settings: themeSettings,
|
||||
isDark: !themeSettings.light,
|
||||
isLight: !!themeSettings.light,
|
||||
theme: state.theme,
|
||||
themeSettings,
|
||||
};
|
||||
}
|
||||
// TODO: dedupe
|
||||
function useEvent(name, onTrigger, useCapture = false) {
|
||||
useEffect(() => {
|
||||
document.addEventListener(name, onTrigger, useCapture);
|
||||
return () => {
|
||||
document.removeEventListener(name, onTrigger, useCapture);
|
||||
};
|
||||
}, [onTrigger]);
|
||||
}
|
||||
|
||||
export default useTheme;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue