Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
4.4 kB · 108 lines
HTML
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109<!doctype html><html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>presentypst</title> <link rel="stylesheet" href="/assets/style.css" /> </head> <body class="display"> <div class="stage" id="stage"></div>
<!-- Browsers refuse to start audio on a page nobody has touched, so a video started later from the presenter console would come up silent. Any gesture clears this and primes playback -- including a key from a presentation remote, which is what makes it reachable on a screen being cast. --> <div class="prompt" id="prompt"> <div> <p><strong>presentypst</strong></p> <p>Press any key on the remote, or click, to start.</p> </div> </div>
<div class="disconnected" id="disconnected" hidden>disconnected</div> <div class="errors" id="errors" hidden></div>
<script type="module"> import { connect, Stage, bindKeys, renderDiagnostics } from "/assets/app.js";
const stage = new Stage(document.getElementById("stage")); const prompt = document.getElementById("prompt"); const disconnected = document.getElementById("disconnected"); const errors = document.getElementById("errors");
const send = connect({ deck(message) { stage.setDeck(message.deck); stage.enlarged = message.fullscreen ?? null; stage.show(message.page); setBlank(message.blank); renderDiagnostics(errors, message.deck.warnings); }, nav: (message) => stage.show(message.page), video: (message) => applyVideo(message), mediaStatus: (message) => stage.setMedia(message), blank: (message) => setBlank(message.blank), compileError: (message) => renderDiagnostics(errors, message.diagnostics), compileOk: () => renderDiagnostics(errors, []), onConnection: (up) => { disconnected.hidden = up; }, });
// The server decides what happens to a video; this just carries it out. function applyVideo(message) { if (message.action === "enlarge") stage.enlarge(message.id, true); else if (message.action === "restore") stage.enlarge(message.id, false); else if (message.action === "play") stage.play(message.id, message.time); else if (message.action === "pause") stage.pause(message.id, message.time); else if (message.action === "seek") stage.seek(message.id, message.time); else stage.stop(message.id); }
function setBlank(blank) { document.getElementById("stage").classList.toggle("blank", blank); if (blank) stage.stopAll(); }
// Dismiss on any gesture, so a presentation remote — which sends ordinary // key events — can clear this without anyone touching the cast screen. // // The first such key is consumed by the dismissal and does not navigate. // Otherwise clicking the remote once would both reveal the deck and step // past its opening slide, and the talk would start on slide two. // // Fullscreen is deliberately not forced here: it is a jarring thing to do // to someone's screen uninvited, and `f` asks for it when wanted. function arm() { if (document.body.classList.contains("armed")) return false; document.body.classList.add("armed"); stage.unlockAudio(); return true; }
// The display can also be driven directly, for when it is the only screen. bindKeys( send, { b: () => send({ type: "setBlank", blank: !document.getElementById("stage").classList.contains("blank") }), v: () => { const id = stage.enlarged ?? stage.deck?.slides?.[stage.page]?.videos?.[0]?.id; if (id) send({ type: "toggleVideoFullscreen", id }); }, f: () => document.fullscreenElement ? document.exitFullscreen() : document.documentElement.requestFullscreen(), }, { swallow: arm }, );
for (const event of ["pointerdown", "touchstart", "wheel"]) { document.addEventListener(event, arm, { passive: true }); } prompt.addEventListener("click", arm);
</script> </body></html>