memlnaut-nisps/manifold/src/engine/EngineProvider.tsx
monkey-w1n5t0n 19b7f7eee8 feat(manifold): add convertible React front-end + simplify console chrome
Adds the Manifold app (manifold/) — the convertible-mode React front-end on
the real NISPS ML+audio engine, served live at meml.lnfinitemonkeys.org/next/.

Console chrome trimmed per UI pass:
- drop mode label + subtitle from the top-left overlay (keep MEMLNaut wordmark)
- remove the composite split preset/ratio readout (top-centre)
- remove the OUTPUT corner tag above the bars
- remove the A/B compare toggle from the verdict cluster
- remove the follow button + input/noise readout (bottom-left)
- remove AltitudeNav focus switcher (bottom-right)
2026-06-28 03:28:45 +02:00

55 lines
1.6 KiB
TypeScript

/**
* EngineProvider — the React binding layer for the headless EngineApi.
*
* This is the ONLY place (with useEngine.ts) where `engine/` touches React.
* The lint rule "skins may not import engine internals; engine may not import
* React" is satisfied: the engine is React-free, and this provider only
* consumes the public `EngineApi` façade.
*
* The engine is created asynchronously (the WASM must load). Until it's ready,
* `useEngine()` returns null; consumers should guard on it.
*/
import {
createContext,
useEffect,
useState,
type ReactNode,
} from 'react';
import { createEngine, EngineApi, type EngineApiOptions } from './engine-api';
export const EngineContext = createContext<EngineApi | null>(null);
export interface EngineProviderProps {
children: ReactNode;
options?: EngineApiOptions;
/** Optional fallback rendered until the engine has loaded. */
fallback?: ReactNode;
}
export function EngineProvider(props: EngineProviderProps): JSX.Element {
const [engine, setEngine] = useState<EngineApi | null>(null);
useEffect(() => {
let disposed = false;
let created: EngineApi | null = null;
void createEngine(props.options ?? {}).then((eng) => {
if (disposed) {
eng.dispose();
return;
}
created = eng;
setEngine(eng);
});
return () => {
disposed = true;
created?.dispose();
};
// Recreate only if the options object identity changes.
}, [props.options]);
if (!engine) {
return <>{props.fallback ?? null}</>;
}
return <EngineContext.Provider value={engine}>{props.children}</EngineContext.Provider>;
}