2026-04-29 16:00:56 +02:00
|
|
|
import { Component, createMemo, createSignal, lazy, onCleanup, Show } from 'solid-js';
|
|
|
|
|
import { Dynamic } from 'solid-js/web';
|
|
|
|
|
import { modeStore } from './stores/mode-store';
|
|
|
|
|
import { MODE_REGISTRY, getModeById } from './modes';
|
|
|
|
|
import { ModeSwitcher } from './modes/ModeSwitcher';
|
2026-04-29 14:37:23 +02:00
|
|
|
import styles from './App.module.css';
|
|
|
|
|
|
2026-04-29 16:00:56 +02:00
|
|
|
type Route = 'home' | 'primitives' | 'modes' | 'unknown';
|
2026-04-29 14:37:23 +02:00
|
|
|
|
|
|
|
|
function parseRoute(path: string): Route {
|
|
|
|
|
if (path === '' || path === '/' || path === '/index.html') return 'home';
|
feat(playground): UI primitive library + /dev/primitives showcase
Sixteen primitives at src/primitives/, each with its own .module.css and a
.demo.tsx that wires the primitive to live local state. All primitives
are typed (strict), pointer-event based (touch + mouse), keyboard-accessible
where it makes sense, and free of business logic.
Primitives:
- Slider, SliderBank: value sliders with optional curve mapping;
collapsible sections.
- VirtualJoystick, XYPad: 2D pointer input, returning [0,1]^2.
- Heatmap: NxN canvas rendering with three color modes
(luminance/variance/divergence) and a configurable palette.
- OutputDisplay: bar chart of N output values.
- TrainingControls: thumbs-up/down/undo + train/randomise + status.
- Drawer: portal'd slide-in panel from left or right; ESC + scrim close.
- ControlAxis: compound-axis slider (Boldness/Memory/Precision shape)
with active-preset hint, endpoint labels, double-tap-to-relink.
- ProgressRing: SVG circular progress with optional inline label.
- PillToggle: segmented radio control.
- ParamEditor: min/max/curve/mute/pin/fixedValue editor for a single param.
- JoyMap: zoom minimap with adaptive grid + zoom window, vanishing trail
with Catmull-Rom spline + tap-to-return, dual concentric noise rings,
region pin overlays, frozen overlay.
- WeightHealth: 10-bin histogram + dead/saturating/healthy status glow.
- GradientFlow: per-layer bar chart with vanishing/exploding/converged
color coding.
- LossPlot: log-scale line chart of training loss history.
App.tsx now wires the /dev/primitives route to a lazy-loaded
PrimitivesShowcase that renders all sixteen demos in a responsive grid.
Lazy import keeps the home page bundle ~20 kB while the showcase ships
its own ~42 kB chunk.
Stream 8 of the rewrite (meml-911).
2026-04-29 14:38:55 +02:00
|
|
|
if (path === '/dev/primitives' || path === '/dev/primitives/') return 'primitives';
|
2026-04-29 16:00:56 +02:00
|
|
|
if (path === '/modes' || path === '/modes/' || path.startsWith('/modes/')) return 'modes';
|
2026-04-29 14:37:23 +02:00
|
|
|
return 'unknown';
|
|
|
|
|
}
|
|
|
|
|
|
feat(playground): UI primitive library + /dev/primitives showcase
Sixteen primitives at src/primitives/, each with its own .module.css and a
.demo.tsx that wires the primitive to live local state. All primitives
are typed (strict), pointer-event based (touch + mouse), keyboard-accessible
where it makes sense, and free of business logic.
Primitives:
- Slider, SliderBank: value sliders with optional curve mapping;
collapsible sections.
- VirtualJoystick, XYPad: 2D pointer input, returning [0,1]^2.
- Heatmap: NxN canvas rendering with three color modes
(luminance/variance/divergence) and a configurable palette.
- OutputDisplay: bar chart of N output values.
- TrainingControls: thumbs-up/down/undo + train/randomise + status.
- Drawer: portal'd slide-in panel from left or right; ESC + scrim close.
- ControlAxis: compound-axis slider (Boldness/Memory/Precision shape)
with active-preset hint, endpoint labels, double-tap-to-relink.
- ProgressRing: SVG circular progress with optional inline label.
- PillToggle: segmented radio control.
- ParamEditor: min/max/curve/mute/pin/fixedValue editor for a single param.
- JoyMap: zoom minimap with adaptive grid + zoom window, vanishing trail
with Catmull-Rom spline + tap-to-return, dual concentric noise rings,
region pin overlays, frozen overlay.
- WeightHealth: 10-bin histogram + dead/saturating/healthy status glow.
- GradientFlow: per-layer bar chart with vanishing/exploding/converged
color coding.
- LossPlot: log-scale line chart of training loss history.
App.tsx now wires the /dev/primitives route to a lazy-loaded
PrimitivesShowcase that renders all sixteen demos in a responsive grid.
Lazy import keeps the home page bundle ~20 kB while the showcase ships
its own ~42 kB chunk.
Stream 8 of the rewrite (meml-911).
2026-04-29 14:38:55 +02:00
|
|
|
// Lazy-loaded so the home route doesn't pay the cost of loading every
|
|
|
|
|
// primitive demo. Stream 9/10 can grow the showcase freely.
|
|
|
|
|
const PrimitivesShowcase = lazy(() => import('./dev/PrimitivesShowcase'));
|
|
|
|
|
|
2026-04-29 14:37:23 +02:00
|
|
|
const App: Component = () => {
|
|
|
|
|
const [route, setRoute] = createSignal<Route>(parseRoute(window.location.pathname));
|
|
|
|
|
|
|
|
|
|
const onPop = () => setRoute(parseRoute(window.location.pathname));
|
|
|
|
|
window.addEventListener('popstate', onPop);
|
|
|
|
|
onCleanup(() => window.removeEventListener('popstate', onPop));
|
|
|
|
|
|
|
|
|
|
const navigate = (path: string) => {
|
|
|
|
|
if (window.location.pathname === path) return;
|
|
|
|
|
window.history.pushState({}, '', path);
|
|
|
|
|
setRoute(parseRoute(path));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div class={styles.app}>
|
|
|
|
|
<header class={styles.header}>
|
|
|
|
|
<strong>MEMLNaut</strong>
|
|
|
|
|
<span class={styles.dim}>playground</span>
|
|
|
|
|
<nav class={styles.nav}>
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
class={route() === 'home' ? styles.active : ''}
|
|
|
|
|
onClick={() => navigate('/')}
|
|
|
|
|
>
|
|
|
|
|
home
|
|
|
|
|
</button>
|
2026-04-29 16:00:56 +02:00
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
class={route() === 'modes' ? styles.active : ''}
|
|
|
|
|
onClick={() => navigate('/modes')}
|
|
|
|
|
>
|
|
|
|
|
/modes
|
|
|
|
|
</button>
|
feat(playground): UI primitive library + /dev/primitives showcase
Sixteen primitives at src/primitives/, each with its own .module.css and a
.demo.tsx that wires the primitive to live local state. All primitives
are typed (strict), pointer-event based (touch + mouse), keyboard-accessible
where it makes sense, and free of business logic.
Primitives:
- Slider, SliderBank: value sliders with optional curve mapping;
collapsible sections.
- VirtualJoystick, XYPad: 2D pointer input, returning [0,1]^2.
- Heatmap: NxN canvas rendering with three color modes
(luminance/variance/divergence) and a configurable palette.
- OutputDisplay: bar chart of N output values.
- TrainingControls: thumbs-up/down/undo + train/randomise + status.
- Drawer: portal'd slide-in panel from left or right; ESC + scrim close.
- ControlAxis: compound-axis slider (Boldness/Memory/Precision shape)
with active-preset hint, endpoint labels, double-tap-to-relink.
- ProgressRing: SVG circular progress with optional inline label.
- PillToggle: segmented radio control.
- ParamEditor: min/max/curve/mute/pin/fixedValue editor for a single param.
- JoyMap: zoom minimap with adaptive grid + zoom window, vanishing trail
with Catmull-Rom spline + tap-to-return, dual concentric noise rings,
region pin overlays, frozen overlay.
- WeightHealth: 10-bin histogram + dead/saturating/healthy status glow.
- GradientFlow: per-layer bar chart with vanishing/exploding/converged
color coding.
- LossPlot: log-scale line chart of training loss history.
App.tsx now wires the /dev/primitives route to a lazy-loaded
PrimitivesShowcase that renders all sixteen demos in a responsive grid.
Lazy import keeps the home page bundle ~20 kB while the showcase ships
its own ~42 kB chunk.
Stream 8 of the rewrite (meml-911).
2026-04-29 14:38:55 +02:00
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
class={route() === 'primitives' ? styles.active : ''}
|
|
|
|
|
onClick={() => navigate('/dev/primitives')}
|
|
|
|
|
>
|
|
|
|
|
/dev/primitives
|
|
|
|
|
</button>
|
2026-04-29 14:37:23 +02:00
|
|
|
</nav>
|
|
|
|
|
</header>
|
|
|
|
|
<main class={styles.main}>
|
|
|
|
|
<Show when={route() === 'home'}>
|
2026-04-29 16:00:56 +02:00
|
|
|
<Home navigate={navigate} />
|
|
|
|
|
</Show>
|
|
|
|
|
<Show when={route() === 'modes'}>
|
|
|
|
|
<ModesPage />
|
2026-04-29 14:37:23 +02:00
|
|
|
</Show>
|
feat(playground): UI primitive library + /dev/primitives showcase
Sixteen primitives at src/primitives/, each with its own .module.css and a
.demo.tsx that wires the primitive to live local state. All primitives
are typed (strict), pointer-event based (touch + mouse), keyboard-accessible
where it makes sense, and free of business logic.
Primitives:
- Slider, SliderBank: value sliders with optional curve mapping;
collapsible sections.
- VirtualJoystick, XYPad: 2D pointer input, returning [0,1]^2.
- Heatmap: NxN canvas rendering with three color modes
(luminance/variance/divergence) and a configurable palette.
- OutputDisplay: bar chart of N output values.
- TrainingControls: thumbs-up/down/undo + train/randomise + status.
- Drawer: portal'd slide-in panel from left or right; ESC + scrim close.
- ControlAxis: compound-axis slider (Boldness/Memory/Precision shape)
with active-preset hint, endpoint labels, double-tap-to-relink.
- ProgressRing: SVG circular progress with optional inline label.
- PillToggle: segmented radio control.
- ParamEditor: min/max/curve/mute/pin/fixedValue editor for a single param.
- JoyMap: zoom minimap with adaptive grid + zoom window, vanishing trail
with Catmull-Rom spline + tap-to-return, dual concentric noise rings,
region pin overlays, frozen overlay.
- WeightHealth: 10-bin histogram + dead/saturating/healthy status glow.
- GradientFlow: per-layer bar chart with vanishing/exploding/converged
color coding.
- LossPlot: log-scale line chart of training loss history.
App.tsx now wires the /dev/primitives route to a lazy-loaded
PrimitivesShowcase that renders all sixteen demos in a responsive grid.
Lazy import keeps the home page bundle ~20 kB while the showcase ships
its own ~42 kB chunk.
Stream 8 of the rewrite (meml-911).
2026-04-29 14:38:55 +02:00
|
|
|
<Show when={route() === 'primitives'}>
|
|
|
|
|
<PrimitivesShowcase />
|
|
|
|
|
</Show>
|
2026-04-29 14:37:23 +02:00
|
|
|
<Show when={route() === 'unknown'}>
|
|
|
|
|
<div class={styles.notFound}>
|
|
|
|
|
<h1>404</h1>
|
|
|
|
|
<p>No route at <code>{window.location.pathname}</code>.</p>
|
|
|
|
|
<p>
|
|
|
|
|
<a href="/" onClick={(e) => { e.preventDefault(); navigate('/'); }}>back to home</a>
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
</Show>
|
|
|
|
|
</main>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-29 16:00:56 +02:00
|
|
|
const ModesPage: Component = () => {
|
|
|
|
|
const activeId = () => modeStore.state.activeModeId ?? MODE_REGISTRY[0]!.id;
|
|
|
|
|
const ActiveComponent = createMemo(() => getModeById(activeId()).Component);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div>
|
|
|
|
|
<ModeSwitcher />
|
|
|
|
|
<Dynamic component={ActiveComponent()} />
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
interface HomeProps {
|
|
|
|
|
navigate: (path: string) => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Home: Component<HomeProps> = (props) => {
|
2026-04-29 14:37:23 +02:00
|
|
|
return (
|
|
|
|
|
<div class={styles.home}>
|
|
|
|
|
<h1 class={styles.title}>MEMLNaut Playground</h1>
|
|
|
|
|
<p class={styles.tagline}>
|
2026-04-29 16:00:56 +02:00
|
|
|
Interactive ML control of audio. Stream 9: nine modes, one shell.
|
2026-04-29 14:37:23 +02:00
|
|
|
</p>
|
feat(playground): UI primitive library + /dev/primitives showcase
Sixteen primitives at src/primitives/, each with its own .module.css and a
.demo.tsx that wires the primitive to live local state. All primitives
are typed (strict), pointer-event based (touch + mouse), keyboard-accessible
where it makes sense, and free of business logic.
Primitives:
- Slider, SliderBank: value sliders with optional curve mapping;
collapsible sections.
- VirtualJoystick, XYPad: 2D pointer input, returning [0,1]^2.
- Heatmap: NxN canvas rendering with three color modes
(luminance/variance/divergence) and a configurable palette.
- OutputDisplay: bar chart of N output values.
- TrainingControls: thumbs-up/down/undo + train/randomise + status.
- Drawer: portal'd slide-in panel from left or right; ESC + scrim close.
- ControlAxis: compound-axis slider (Boldness/Memory/Precision shape)
with active-preset hint, endpoint labels, double-tap-to-relink.
- ProgressRing: SVG circular progress with optional inline label.
- PillToggle: segmented radio control.
- ParamEditor: min/max/curve/mute/pin/fixedValue editor for a single param.
- JoyMap: zoom minimap with adaptive grid + zoom window, vanishing trail
with Catmull-Rom spline + tap-to-return, dual concentric noise rings,
region pin overlays, frozen overlay.
- WeightHealth: 10-bin histogram + dead/saturating/healthy status glow.
- GradientFlow: per-layer bar chart with vanishing/exploding/converged
color coding.
- LossPlot: log-scale line chart of training loss history.
App.tsx now wires the /dev/primitives route to a lazy-loaded
PrimitivesShowcase that renders all sixteen demos in a responsive grid.
Lazy import keeps the home page bundle ~20 kB while the showcase ships
its own ~42 kB chunk.
Stream 8 of the rewrite (meml-911).
2026-04-29 14:38:55 +02:00
|
|
|
<ul class={styles.linkList}>
|
2026-04-29 16:00:56 +02:00
|
|
|
<li>
|
|
|
|
|
<a
|
|
|
|
|
href="/modes"
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
props.navigate('/modes');
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Modes
|
|
|
|
|
</a>{' '}
|
|
|
|
|
— pick a mode and start playing
|
|
|
|
|
</li>
|
|
|
|
|
<li>
|
|
|
|
|
<a
|
|
|
|
|
href="/dev/primitives"
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
props.navigate('/dev/primitives');
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Primitives showcase
|
|
|
|
|
</a>{' '}
|
|
|
|
|
— UI building blocks
|
|
|
|
|
</li>
|
feat(playground): UI primitive library + /dev/primitives showcase
Sixteen primitives at src/primitives/, each with its own .module.css and a
.demo.tsx that wires the primitive to live local state. All primitives
are typed (strict), pointer-event based (touch + mouse), keyboard-accessible
where it makes sense, and free of business logic.
Primitives:
- Slider, SliderBank: value sliders with optional curve mapping;
collapsible sections.
- VirtualJoystick, XYPad: 2D pointer input, returning [0,1]^2.
- Heatmap: NxN canvas rendering with three color modes
(luminance/variance/divergence) and a configurable palette.
- OutputDisplay: bar chart of N output values.
- TrainingControls: thumbs-up/down/undo + train/randomise + status.
- Drawer: portal'd slide-in panel from left or right; ESC + scrim close.
- ControlAxis: compound-axis slider (Boldness/Memory/Precision shape)
with active-preset hint, endpoint labels, double-tap-to-relink.
- ProgressRing: SVG circular progress with optional inline label.
- PillToggle: segmented radio control.
- ParamEditor: min/max/curve/mute/pin/fixedValue editor for a single param.
- JoyMap: zoom minimap with adaptive grid + zoom window, vanishing trail
with Catmull-Rom spline + tap-to-return, dual concentric noise rings,
region pin overlays, frozen overlay.
- WeightHealth: 10-bin histogram + dead/saturating/healthy status glow.
- GradientFlow: per-layer bar chart with vanishing/exploding/converged
color coding.
- LossPlot: log-scale line chart of training loss history.
App.tsx now wires the /dev/primitives route to a lazy-loaded
PrimitivesShowcase that renders all sixteen demos in a responsive grid.
Lazy import keeps the home page bundle ~20 kB while the showcase ships
its own ~42 kB chunk.
Stream 8 of the rewrite (meml-911).
2026-04-29 14:38:55 +02:00
|
|
|
</ul>
|
2026-04-29 14:37:23 +02:00
|
|
|
<p class={styles.note}>
|
2026-04-29 16:00:56 +02:00
|
|
|
ML inference runs on the main thread via WASM. Audio synthesis runs
|
|
|
|
|
in an AudioWorklet — start it from inside any mode using the "Start
|
|
|
|
|
audio" button. Audio cannot autoplay (browser policy).
|
2026-04-29 14:37:23 +02:00
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default App;
|