122 lines
4.2 KiB
JavaScript
122 lines
4.2 KiB
JavaScript
import { useEffect, useState } from "react";
|
|
|
|
import { LayerStack } from "./components/LayerStack";
|
|
import { StackPresetToolbar } from "./components/StackPresetToolbar";
|
|
import { StatusPanels } from "./components/StatusPanels";
|
|
import { useRuntimeState } from "./hooks/useRuntimeState";
|
|
|
|
function App() {
|
|
const [appState, setAppState] = useRuntimeState();
|
|
const [pendingShaderId, setPendingShaderId] = useState("");
|
|
const [presetName, setPresetName] = useState("");
|
|
const [selectedPresetName, setSelectedPresetName] = useState("");
|
|
const [expandedLayerIds, setExpandedLayerIds] = useState([]);
|
|
const [dragLayerId, setDragLayerId] = useState(null);
|
|
const [dropTargetLayerId, setDropTargetLayerId] = useState(null);
|
|
|
|
const layers = appState?.layers ?? [];
|
|
const shaders = appState?.shaders ?? [];
|
|
const performance = appState?.performance ?? {};
|
|
const runtime = appState?.runtime ?? {};
|
|
const video = appState?.video ?? {};
|
|
const app = appState?.app ?? {};
|
|
const stackPresets = appState?.stackPresets ?? [];
|
|
|
|
useEffect(() => {
|
|
if (!pendingShaderId && shaders.length > 0) {
|
|
setPendingShaderId(shaders[0].id);
|
|
} else if (pendingShaderId && !shaders.some((shader) => shader.id === pendingShaderId)) {
|
|
setPendingShaderId(shaders[0]?.id ?? "");
|
|
}
|
|
}, [pendingShaderId, shaders]);
|
|
|
|
useEffect(() => {
|
|
if (!selectedPresetName && stackPresets.length > 0) {
|
|
setSelectedPresetName(stackPresets[0]);
|
|
} else if (selectedPresetName && !stackPresets.includes(selectedPresetName)) {
|
|
setSelectedPresetName(stackPresets[0] ?? "");
|
|
}
|
|
}, [selectedPresetName, stackPresets]);
|
|
|
|
useEffect(() => {
|
|
const layerIds = new Set(layers.map((layer) => layer.id));
|
|
setExpandedLayerIds((current) => current.filter((layerId) => layerIds.has(layerId)));
|
|
}, [layers]);
|
|
|
|
if (!appState) {
|
|
return (
|
|
<main className="layout">
|
|
<section className="panel">
|
|
<h3>Loading</h3>
|
|
<p className="muted">Waiting for control state from the native host.</p>
|
|
<div className="progress-track" aria-hidden="true">
|
|
<div className="progress-bar is-indeterminate" />
|
|
</div>
|
|
</section>
|
|
</main>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<main className="layout">
|
|
<header className="app-header">
|
|
<div>
|
|
<h2>Video Shader Toys</h2>
|
|
<p className="muted">Live shader stack, DeckLink status, and runtime controls.</p>
|
|
</div>
|
|
<div className={`status-pill${runtime.compileSucceeded ? " status-pill--ready" : " status-pill--error"}`}>
|
|
{runtime.compileSucceeded ? "Ready" : "Compile Error"}
|
|
</div>
|
|
</header>
|
|
|
|
<section className="panel app-summary" aria-label="Runtime summary">
|
|
<dl className="summary-grid">
|
|
<div className="summary-item">
|
|
<dt>Shaders</dt>
|
|
<dd>{shaders.length}</dd>
|
|
</div>
|
|
<div className="summary-item">
|
|
<dt>Layers</dt>
|
|
<dd>{layers.length}</dd>
|
|
</div>
|
|
<div className="summary-item">
|
|
<dt>Signal</dt>
|
|
<dd>{video.hasSignal ? "Present" : "Missing"}</dd>
|
|
</div>
|
|
<div className="summary-item">
|
|
<dt>Render</dt>
|
|
<dd>{Number(performance.renderMs ?? 0).toFixed(2)} ms</dd>
|
|
</div>
|
|
</dl>
|
|
</section>
|
|
|
|
<section className="dashboard-grid">
|
|
<StatusPanels app={app} performance={performance} runtime={runtime} video={video} />
|
|
<StackPresetToolbar
|
|
presetName={presetName}
|
|
selectedPresetName={selectedPresetName}
|
|
stackPresets={stackPresets}
|
|
onPresetNameChange={setPresetName}
|
|
onSelectedPresetNameChange={setSelectedPresetName}
|
|
/>
|
|
</section>
|
|
|
|
<LayerStack
|
|
dragLayerId={dragLayerId}
|
|
dropTargetLayerId={dropTargetLayerId}
|
|
expandedLayerIds={expandedLayerIds}
|
|
layers={layers}
|
|
pendingShaderId={pendingShaderId}
|
|
setAppState={setAppState}
|
|
setDragLayerId={setDragLayerId}
|
|
setDropTargetLayerId={setDropTargetLayerId}
|
|
setExpandedLayerIds={setExpandedLayerIds}
|
|
setPendingShaderId={setPendingShaderId}
|
|
shaders={shaders}
|
|
/>
|
|
</main>
|
|
);
|
|
}
|
|
|
|
export default App;
|