Release v0.1.0 2026-07-10
This commit is contained in:
@@ -1,13 +1,51 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { useEffect, useRef, useState, type ReactNode } from "react";
|
||||
import { subscribeDisplaySurface, type DisplaySurface } from "../platform/displayRouting";
|
||||
|
||||
export function DualDisplayFrame({ top, bottom }: { top: ReactNode; bottom: ReactNode }) {
|
||||
const [activeSurface, setActiveSurface] = useState<DisplaySurface>("top");
|
||||
const activeSurfaceRef = useRef(activeSurface);
|
||||
activeSurfaceRef.current = activeSurface;
|
||||
|
||||
useEffect(() => {
|
||||
if (!document.documentElement.classList.contains("native-platform")) return;
|
||||
let frame = 0;
|
||||
let selectHeld = false;
|
||||
const toggle = () => setActiveSurface((surface) => surface === "top" ? "bottom" : "top");
|
||||
const unsubscribeSurface = subscribeDisplaySurface(setActiveSurface);
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key !== "Tab" || event.repeat) return;
|
||||
event.preventDefault();
|
||||
toggle();
|
||||
};
|
||||
const poll = () => {
|
||||
const held = navigator.getGamepads?.()[0]?.buttons[8]?.pressed ?? false;
|
||||
if (held && !selectHeld) toggle();
|
||||
selectHeld = held;
|
||||
frame = requestAnimationFrame(poll);
|
||||
};
|
||||
window.addEventListener("keydown", onKeyDown);
|
||||
frame = requestAnimationFrame(poll);
|
||||
return () => {
|
||||
window.removeEventListener("keydown", onKeyDown);
|
||||
unsubscribeSurface();
|
||||
cancelAnimationFrame(frame);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="device-frame">
|
||||
<div className={`device-frame active-${activeSurface}`}>
|
||||
<div className="screen-label"><span>Main viewport</span><small>960 × 540 CSS · 1920 × 1080 · 120Hz</small></div>
|
||||
{top}
|
||||
<div className={`surface-slot top-slot ${activeSurface === "top" ? "is-active" : ""}`}>{top}</div>
|
||||
<div className="hinge" aria-hidden="true"><i /><b>AYN THOR</b><i /></div>
|
||||
<div className="screen-label bottom-label"><span>Context display</span><small>620 × 540 CSS · 1240 × 1080 · 60Hz</small></div>
|
||||
{bottom}
|
||||
<div className={`surface-slot bottom-slot ${activeSurface === "bottom" ? "is-active" : ""}`}>{bottom}</div>
|
||||
<button
|
||||
className="native-display-switch"
|
||||
onClick={() => setActiveSurface(activeSurfaceRef.current === "top" ? "bottom" : "top")}
|
||||
aria-label={activeSurface === "top" ? "Open tactical display" : "Return to main display"}
|
||||
>
|
||||
<b>{activeSurface === "top" ? "Tactical" : "Main"}</b><small>SELECT / TAB</small>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user