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>
  );
}