File size: 2,698 Bytes
fed1528 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | import { useCallback, useEffect, useState } from "react";
import { AppHeader } from "./components/layout/AppHeader";
import { AppFooter } from "./components/layout/AppFooter";
import { AceCortexPane } from "./components/pipeline/AceCortexPane";
import { EveStudioPane } from "./components/eve/EveStudioPane";
import { UponLoadBadge } from "./components/metrics/UponLoadBadge";
import { usePresenceLoop } from "./lib/stageMachine/usePresenceLoop";
const SPLIT_KEY = "eve-ecc.split";
const DEFAULT_SPLIT = 58;
function clampSplit(value: number) {
return Math.min(72, Math.max(38, value));
}
export default function App() {
usePresenceLoop(true);
const [split, setSplit] = useState(() => {
const saved = Number(localStorage.getItem(SPLIT_KEY));
return Number.isFinite(saved) ? clampSplit(saved) : DEFAULT_SPLIT;
});
const [dragging, setDragging] = useState(false);
useEffect(() => {
localStorage.setItem(SPLIT_KEY, String(split));
document.documentElement.style.setProperty("--split", `${split}%`);
}, [split]);
const onPointerDown = useCallback((event: React.PointerEvent<HTMLDivElement>) => {
event.preventDefault();
setDragging(true);
event.currentTarget.setPointerCapture(event.pointerId);
}, []);
const onPointerMove = useCallback(
(event: React.PointerEvent<HTMLDivElement>) => {
if (!dragging) return;
const workspace = event.currentTarget.parentElement;
if (!workspace) return;
const rect = workspace.getBoundingClientRect();
const next = ((event.clientX - rect.left) / rect.width) * 100;
setSplit(clampSplit(next));
},
[dragging],
);
const onPointerUp = useCallback((event: React.PointerEvent<HTMLDivElement>) => {
setDragging(false);
try {
event.currentTarget.releasePointerCapture(event.pointerId);
} catch {
/* already released */
}
}, []);
return (
<div className="app-shell">
<AppHeader />
<main className="workspace" aria-label="EVE ECC dual-pane workspace">
<section className="pane pane-left" aria-label="ACE cortex">
<AceCortexPane />
</section>
<div
className="splitter"
role="separator"
aria-orientation="vertical"
aria-valuenow={Math.round(split)}
aria-label="Resize panes"
onPointerDown={onPointerDown}
onPointerMove={onPointerMove}
onPointerUp={onPointerUp}
onPointerCancel={onPointerUp}
/>
<section className="pane pane-right" aria-label="EVE live studio">
<EveStudioPane />
</section>
</main>
<AppFooter />
<UponLoadBadge />
</div>
);
}
|