mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-24 20:18:34 +00:00
added udels editor and header
This commit is contained in:
parent
d23038d386
commit
6c9e0aaa1a
@ -2,6 +2,7 @@ import { code2hash } from '@strudel/core';
|
|||||||
|
|
||||||
import { UdelFrame } from './UdelFrame';
|
import { UdelFrame } from './UdelFrame';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import UdelsHeader from './UdelsHeader';
|
||||||
|
|
||||||
function NumberInput({ value, onChange, label = '', min, max }) {
|
function NumberInput({ value, onChange, label = '', min, max }) {
|
||||||
const [localState, setLocalState] = useState(value);
|
const [localState, setLocalState] = useState(value);
|
||||||
@ -68,7 +69,6 @@ export function Udels() {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: 'teal',
|
|
||||||
margin: 0,
|
margin: 0,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
@ -77,7 +77,8 @@ export function Udels() {
|
|||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<UdelsHeader numWindows={numWindows} setNumWindows={numWindowsOnChange} />
|
||||||
|
{/* <div
|
||||||
style={{
|
style={{
|
||||||
height: 40,
|
height: 40,
|
||||||
width: '100',
|
width: '100',
|
||||||
@ -87,7 +88,7 @@ export function Udels() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<NumberInput min={1} max={8} value={numWindows} onChange={numWindowsOnChange} />
|
<NumberInput min={1} max={8} value={numWindows} onChange={numWindowsOnChange} />
|
||||||
</div>
|
</div> */}
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
|||||||
34
website/src/components/Udels/UdelsEditor.jsx
Normal file
34
website/src/components/Udels/UdelsEditor.jsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { ReplContext } from '@src/repl/util.mjs';
|
||||||
|
|
||||||
|
import Loader from '@src/repl/Loader';
|
||||||
|
import { Panel } from '@src/repl/panel/Panel';
|
||||||
|
import { Code } from '@src/repl/components/Code';
|
||||||
|
import BigPlayButton from '@src/repl/components/BigPlayButton';
|
||||||
|
import UserFacingErrorMessage from '@src/repl/components/UserFacingErrorMessage';
|
||||||
|
|
||||||
|
// type Props = {
|
||||||
|
// context: replcontext,
|
||||||
|
// containerRef: React.MutableRefObject<HTMLElement | null>,
|
||||||
|
// editorRef: React.MutableRefObject<HTMLElement | null>,
|
||||||
|
// error: Error
|
||||||
|
// init: () => void
|
||||||
|
// }
|
||||||
|
|
||||||
|
export default function UdelsEditor(Props) {
|
||||||
|
const { context, containerRef, editorRef, error, init } = Props;
|
||||||
|
const { pending, started, handleTogglePlay } = context;
|
||||||
|
return (
|
||||||
|
<ReplContext.Provider value={context}>
|
||||||
|
<div className={'h-full flex w-full flex-col relative'}>
|
||||||
|
<Loader active={pending} />
|
||||||
|
{/* <Header context={context} /> */}
|
||||||
|
<BigPlayButton started={started} handleTogglePlay={handleTogglePlay} />
|
||||||
|
<div className="grow flex relative overflow-hidden">
|
||||||
|
<Code containerRef={containerRef} editorRef={editorRef} init={init} />
|
||||||
|
</div>
|
||||||
|
<UserFacingErrorMessage error={error} />
|
||||||
|
<Panel context={context} />
|
||||||
|
</div>
|
||||||
|
</ReplContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
25
website/src/components/Udels/UdelsHeader.tsx
Normal file
25
website/src/components/Udels/UdelsHeader.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import NumberInput from '@src/repl/panel/NumberInput';
|
||||||
|
|
||||||
|
export default function UdelsHeader(Props) {
|
||||||
|
const { numWindows, setNumWindows } = Props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<header id="header" className="flex text-white z-[100] text-lg select-none bg-neutral-900">
|
||||||
|
<div className="px-4 items-center gap-2 flex space-x-2 md:pt-0 select-none">
|
||||||
|
<h1 onClick={() => {}} className={'text-l cursor-pointer flex gap-4'}>
|
||||||
|
<div
|
||||||
|
className={'mt-[1px] cursor-pointer'}
|
||||||
|
>
|
||||||
|
🌀
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={'animate-pulse'}>
|
||||||
|
<span className="">strudel</span> <span className="text-sm">-UDELS</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</h1>
|
||||||
|
<NumberInput value={numWindows} setValue={setNumWindows} />
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -39,6 +39,7 @@ import PlayCircleIcon from '@heroicons/react/20/solid/PlayCircleIcon';
|
|||||||
import './Repl.css';
|
import './Repl.css';
|
||||||
import { setInterval, clearInterval } from 'worker-timers';
|
import { setInterval, clearInterval } from 'worker-timers';
|
||||||
import { getMetadata } from '../metadata_parser';
|
import { getMetadata } from '../metadata_parser';
|
||||||
|
import UdelsEditor from '@components/Udels/UdelsEditor';
|
||||||
|
|
||||||
const { latestCode } = settingsMap.get();
|
const { latestCode } = settingsMap.get();
|
||||||
|
|
||||||
@ -234,7 +235,14 @@ export function Repl({ embedded = false }) {
|
|||||||
handleEvaluate,
|
handleEvaluate,
|
||||||
};
|
};
|
||||||
|
|
||||||
const showPanel = !isEmbedded || isUdels();
|
if (isUdels()) {
|
||||||
|
return (
|
||||||
|
<UdelsEditor context={context} error={error} init={init} editorRef={editorRef} containerRef={containerRef} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const showPanel = !isEmbedded;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReplContext.Provider value={context}>
|
<ReplContext.Provider value={context}>
|
||||||
<div className={cx('h-full flex flex-col relative')}>
|
<div className={cx('h-full flex flex-col relative')}>
|
||||||
|
|||||||
22
website/src/repl/components/BigPlayButton.jsx
Normal file
22
website/src/repl/components/BigPlayButton.jsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import PlayCircleIcon from '@heroicons/react/20/solid/PlayCircleIcon';
|
||||||
|
|
||||||
|
// type Props = {
|
||||||
|
// started: boolean;
|
||||||
|
// handleTogglePlay: () => void;
|
||||||
|
// };
|
||||||
|
export default function BigPlayButton(Props) {
|
||||||
|
const { started, handleTogglePlay } = Props;
|
||||||
|
if (started) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={() => handleTogglePlay()}
|
||||||
|
className="text-white text-2xl fixed left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%] z-[1000] m-auto p-4 bg-black rounded-md flex items-center space-x-2"
|
||||||
|
>
|
||||||
|
<PlayCircleIcon className="w-6 h-6" />
|
||||||
|
<span>play</span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
23
website/src/repl/components/Code.jsx
Normal file
23
website/src/repl/components/Code.jsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
|
||||||
|
// type Props = {
|
||||||
|
// containerRef: React.MutableRefObject<HTMLElement | null>,
|
||||||
|
// editorRef: React.MutableRefObject<HTMLElement | null>,
|
||||||
|
// init: () => void
|
||||||
|
// }
|
||||||
|
export function Code(Props) {
|
||||||
|
const {editorRef, containerRef, init} = Props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
className={'text-gray-100 cursor-text pb-0 overflow-auto grow'}
|
||||||
|
id="code"
|
||||||
|
ref={(el) => {
|
||||||
|
containerRef.current = el;
|
||||||
|
if (!editorRef.current) {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
></section>
|
||||||
|
);
|
||||||
|
}
|
||||||
8
website/src/repl/components/UserFacingErrorMessage.jsx
Normal file
8
website/src/repl/components/UserFacingErrorMessage.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// type Props = { error: Error | null };
|
||||||
|
export default function UserFacingErrorMessage(Props) {
|
||||||
|
const { error } = Props;
|
||||||
|
if (error == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return <div className="text-red-500 p-4 bg-lineHighlight animate-pulse">{error.message || 'Unknown Error :-/'}</div>;
|
||||||
|
}
|
||||||
62
website/src/repl/panel/NumberInput.jsx
Normal file
62
website/src/repl/panel/NumberInput.jsx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
function Button(Props) {
|
||||||
|
const { children, onClick } = Props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={onClick}
|
||||||
|
type="button"
|
||||||
|
data-input-counter-increment="counter-input"
|
||||||
|
class="flex-shrink-0 bg-gray-700 hover:bg-gray-600 border-gray-600 inline-flex items-center justify-center border rounded-md h-5 w-5 focus:ring-gray-700 focus:ring-2 focus:outline-none"
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default function NumberInput(Props) {
|
||||||
|
const { value = 0, setValue, max, min} = Props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="relative flex items-center">
|
||||||
|
<Button onClick={() => setValue(value - 1)}>
|
||||||
|
<svg
|
||||||
|
class="w-2.5 h-2.5 text-white"
|
||||||
|
aria-hidden="true"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 18 2"
|
||||||
|
>
|
||||||
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h16" />
|
||||||
|
</svg>
|
||||||
|
</Button>
|
||||||
|
<input
|
||||||
|
min={min}
|
||||||
|
max={max}
|
||||||
|
type="text"
|
||||||
|
data-input-counter
|
||||||
|
class="flex-shrink-0 text-white border-0 bg-transparent text-sm font-normal focus:outline-none focus:ring-0 max-w-[2.5rem] text-center"
|
||||||
|
placeholder=""
|
||||||
|
value={value}
|
||||||
|
onChange={e => setValue(e.target.value)}
|
||||||
|
/>
|
||||||
|
<Button onClick={() => setValue(value + 1)}>
|
||||||
|
<svg
|
||||||
|
class="w-2.5 h-2.5 text-white"
|
||||||
|
aria-hidden="true"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 18 18"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M9 1v16M1 9h16"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import { persistentMap } from '@nanostores/persistent';
|
import { persistentMap } from '@nanostores/persistent';
|
||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
import { register } from '@strudel/core';
|
import { register } from '@strudel/core';
|
||||||
import { isUdels } from './repl/util.mjs';
|
import { isUdels, } from './repl/util.mjs';
|
||||||
|
|
||||||
export const defaultAudioDeviceName = 'System Standard';
|
export const defaultAudioDeviceName = 'System Standard';
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ export const defaultSettings = {
|
|||||||
isZen: false,
|
isZen: false,
|
||||||
soundsFilter: 'all',
|
soundsFilter: 'all',
|
||||||
patternFilter: 'community',
|
patternFilter: 'community',
|
||||||
panelPosition: 'right',
|
panelPosition: 'right',
|
||||||
userPatterns: '{}',
|
userPatterns: '{}',
|
||||||
audioDeviceName: defaultAudioDeviceName,
|
audioDeviceName: defaultAudioDeviceName,
|
||||||
};
|
};
|
||||||
@ -61,7 +61,7 @@ export function useSettings() {
|
|||||||
isFlashEnabled: parseBoolean(state.isFlashEnabled),
|
isFlashEnabled: parseBoolean(state.isFlashEnabled),
|
||||||
isSyncEnabled: isUdels() ? true : parseBoolean(state.isSyncEnabled),
|
isSyncEnabled: isUdels() ? true : parseBoolean(state.isSyncEnabled),
|
||||||
fontSize: Number(state.fontSize),
|
fontSize: Number(state.fontSize),
|
||||||
panelPosition: state.activeFooter !== '' ? state.panelPosition : 'bottom', // <-- keep this 'bottom' where it is!
|
panelPosition: state.activeFooter !== '' && !isUdels() ? state.panelPosition : 'bottom', // <-- keep this 'bottom' where it is!
|
||||||
userPatterns: userPatterns,
|
userPatterns: userPatterns,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user