DUNGEONS-34: keep host screen awake while streaming

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
2026-09-06 18:47:42 +05:00
parent ae3a3c5def
commit 1b55a86ba5
12 changed files with 307 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
export type WakeStatus = "off" | "waiting" | "requesting" | "active" | "unavailable" | "unsupported";
type Sentinel = Pick<WakeLockSentinel, "released" | "release" | "addEventListener">;
// Browser access is injected so asynchronous acquisition and release can be tested.
export function createScreenWakeLock(options: {
request?: () => Promise<Sentinel>;
visible: () => boolean;
status: (value: WakeStatus) => void;
}) {
let desired = false;
let disposed = false;
let pending = false;
let generation = 0;
let lock: Sentinel | null = null;
const report = (value: WakeStatus) => { if (!disposed) options.status(value); };
const wanted = () => desired && !disposed && options.visible();
function release() {
generation++;
const old = lock;
lock = null;
if (old) void old.release().catch(() => {});
}
async function sync() {
if (!wanted()) {
release();
report(desired ? "waiting" : "off");
return;
}
if (!options.request) { report("unsupported"); return; }
if (lock && !lock.released) { report("active"); return; }
if (pending) return;
pending = true;
const version = generation;
report("requesting");
try {
const acquired = await options.request();
if (version !== generation || !wanted()) {
await acquired.release();
return;
}
lock = acquired;
acquired.addEventListener("release", () => {
if (lock !== acquired) return;
lock = null;
// Do not repeatedly fight a system/battery decision to revoke the lock.
report(wanted() ? "unavailable" : desired ? "waiting" : "off");
}, { once: true });
report(acquired.released ? "unavailable" : "active");
} catch {
if (version === generation && wanted()) report("unavailable");
} finally {
pending = false;
// An off/on or hidden/visible transition may have invalidated a pending request.
if (version !== generation && wanted()) void sync();
}
}
return {
update(enabled: boolean) { desired = enabled; void sync(); },
refresh() { void sync(); },
dispose() { disposed = true; desired = false; release(); },
};
}