72 lines
4.1 KiB
HTML
72 lines
4.1 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>MoQ room client</title>
|
|
<link rel="icon" href="data:,">
|
|
<style>
|
|
:root { --bg: #f6f7f9; --fg: #15171a; --muted: #5d6470; --panel: #ffffff; --line: #d9dde3; --human: #1f6f43; --machine: #8a4b00; }
|
|
@media (prefers-color-scheme: dark) { :root { --bg: #111317; --fg: #e8eaee; --muted: #9aa2ae; --panel: #1a1d23; --line: #2c313a; --human: #6fd39b; --machine: #f0a957; } }
|
|
* { box-sizing: border-box; }
|
|
[hidden] { display: none !important; }
|
|
body { margin: 0; padding: 16px; background: var(--bg); color: var(--fg); font: 14px/1.45 system-ui, sans-serif; }
|
|
header { display: flex; gap: 12px; align-items: baseline; flex-wrap: wrap; margin-bottom: 12px; }
|
|
h1 { font-size: 16px; margin: 0; }
|
|
#who { color: var(--muted); font-family: ui-monospace, monospace; }
|
|
main { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: 12px; }
|
|
@media (max-width: 760px) { main { grid-template-columns: minmax(0, 1fr); } }
|
|
section { background: var(--panel); border: 1px solid var(--line); border-radius: 6px; padding: 10px; min-width: 0; }
|
|
canvas { width: 100%; height: auto; display: block; background: #101218; border-radius: 4px; }
|
|
#caption { min-height: 2.6em; margin-top: 8px; display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; }
|
|
#caption .origin { font-size: 12px; padding: 1px 6px; border-radius: 3px; border: 1px solid currentColor; }
|
|
#caption .origin-human { color: var(--human); }
|
|
#caption .origin-machine, #caption .origin-machine-reviewed { color: var(--machine); }
|
|
#timeline { list-style: none; margin: 0; padding: 0; max-height: 280px; overflow: auto; font-family: ui-monospace, monospace; font-size: 12px; }
|
|
#timeline li { padding: 2px 0; border-bottom: 1px solid var(--line); overflow-wrap: anywhere; }
|
|
#log { white-space: pre-wrap; font-family: ui-monospace, monospace; font-size: 11px; color: var(--muted); max-height: 220px; overflow: auto; margin: 0; overflow-wrap: anywhere; }
|
|
h2 { font-size: 13px; margin: 0 0 6px; color: var(--muted); font-weight: 600; }
|
|
.controls { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-bottom: 12px; }
|
|
#status { color: var(--muted); }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<header><h1>MoQ room client</h1><span id="who">loading</span></header>
|
|
<!-- With a room service (?service=<url>): enter as a publisher or a viewer. -->
|
|
<div id="room-app" class="controls">
|
|
<button id="join" type="button">Join with camera and microphone</button>
|
|
<button id="watch" type="button">Watch</button>
|
|
<button id="leave" type="button" hidden>Leave</button>
|
|
<span id="status" role="status" aria-live="polite"></span>
|
|
<form id="caption-form" hidden><label for="caption-text">Caption</label> <input id="caption-text" maxlength="200" autocomplete="off"> <button type="submit">Send</button></form>
|
|
</div>
|
|
<main>
|
|
<section>
|
|
<h2>The room, composed</h2>
|
|
<canvas id="canvas" width="640" height="360"></canvas>
|
|
<div id="caption" aria-live="polite"></div>
|
|
<div id="dvr" class="controls" style="margin: 8px 0 0">
|
|
<label for="seek-s">Seconds back</label>
|
|
<input id="seek-s" type="number" min="1" max="900" value="10" style="width: 5em">
|
|
<button id="seek" type="button" disabled>Seek back</button>
|
|
<button id="live" type="button" hidden>Back to live</button>
|
|
<span id="dvr-status" role="status" aria-live="polite"></span>
|
|
</div>
|
|
<canvas id="dvr-canvas" width="640" height="360" hidden style="margin-top: 8px" aria-label="The participant's earlier video, from the relay's window"></canvas>
|
|
</section>
|
|
<section>
|
|
<h2>Events</h2>
|
|
<ul id="timeline"></ul>
|
|
</section>
|
|
<section style="grid-column: 1 / -1">
|
|
<h2>Log</h2>
|
|
<pre id="log"></pre>
|
|
</section>
|
|
</main>
|
|
<script type="module">
|
|
// The controls are for a room service alone; a fixture's page has no use for them.
|
|
if (!new URLSearchParams(location.search).get("service")) document.getElementById("room-app").hidden = true;
|
|
</script>
|
|
<script type="module" src="./app.js"></script>
|
|
</body>
|
|
</html>
|