oficina / meet Public

Clone
git clone https://amadan.net/oficina/meet

Plain git — no account needed to clone.

Download

Download this file

1{{define "head"}}<link rel="stylesheet" href="{{asset "static/calls.css"}}">
2{{/* #devices is in the layout from the first paint, so its arrival does
3 not push the page down when devices.js runs; without script this
4 sheet hides it, as the hidden attribute used to. */}}
5<noscript><link rel="stylesheet" href="{{asset "static/noscript.css"}}"></noscript>
6<script defer src="{{asset "static/devices.js"}}"></script>
7<script defer src="{{asset "static/report.js"}}"></script>
8{{end}}
9{{/* The holding screen. Its layout is fixed at the first paint (spec
10 §2's "reserved up front"): the controls and the status line come
11 first in the device panel, and the rows devices.js may reveal later
12 come after them, the speaker last of all, so nothing that is
13 already on screen moves when they arrive. */}}
14{{define "content"}}
15{{if .IsMember}}{{template "back-nav" dict "Href" "/" "Label" "Meetings"}}{{end}}
16<div class="calls-lobby-heading">{{template "page-header" dict "Title" .Title}}</div>
17<p class="calls-standing">{{if .Starts}}Starts {{.Starts}}. {{end}}You’ll join as {{.Name}}{{if eq .Standing "guest"}}, a guest{{end}}.</p>
18{{if .Floor}}{{template "callout" dict "Body" "Calls outside your network may not connect yet." "Tone" "warning"}}{{end}}
19{{if .OpenElsewhere}}<div rst-callout rst-tone="warning"><span rst-callout-ic aria-hidden="true">{{icon "alert-triangle"}}</span><div rst-callout-body><p>This meeting is already open in another tab. <a href="/m/{{.Sqid}}/takeover">Use this tab instead</a>.</p></div></div>{{end}}
20<noscript><p>The call needs JavaScript to reach your camera and the other participants; everything else on this page works without it.</p></noscript>
21<div id="devices" data-sqid="{{.Sqid}}" data-join-href="{{if .OpenElsewhere}}/m/{{.Sqid}}/takeover{{else}}/m/{{.Sqid}}{{end}}">
22 <div class="calls-device-layout">
23<div class="calls-preview-wrap"><video id="preview" muted autoplay playsinline aria-label="Your camera preview" class="calls-preview"></video></div>
24<div estilo-call-panel>
25 <div class="calls-controls" estilo-call-actions>
26 <button type="button" id="toggle-mute" rst-btn aria-label="Microphone" aria-pressed="false">{{icon "mic"}}Microphone<span id="toggle-mute-state" class="calls-toggle-state" aria-hidden="true">On</span></button>
27 <button type="button" id="toggle-camera" rst-btn aria-label="Camera" aria-pressed="false">{{icon "video"}}Camera<span id="toggle-camera-state" class="calls-toggle-state" aria-hidden="true">On</span></button>
28 <button type="button" id="join" rst-btn="primary lg" disabled>Join in browser</button>
29 </div>
30 <p id="status" class="calls-status" role="status"></p>
31 <div id="pickers" class="calls-pickers">
32 <div id="camera-row">{{template "field-select" dict "ID" "pick-camera" "Name" "camera" "Label" "Camera"}}</div>
33 <div id="mic-row">{{template "field-select" dict "ID" "pick-mic" "Name" "mic" "Label" "Microphone"}}</div>
34 {{/* Studio audio: the unprocessed capture option. field-check is the
35 vendored toggle (AGENTS.md's field rule), and the sentence under
36 it is wired to the input with aria-describedby by devices.js,
37 because field-check gives its checkbox no id to write here. It
38 is shown from the first paint, so the controls do not move when
39 devices.js decides; devices.js hides it only where there is no
40 microphone to apply it to. */}}
41 <div id="raw-row" class="calls-raw-row">{{template "field-check" dict "Name" "raw-audio" "Label" "Studio audio"}}
42 <p id="raw-help" class="calls-raw-help">Sends your microphone unprocessed and at a higher bitrate, for an audio interface in a room you have treated. It turns off echo cancellation, noise suppression and automatic gain, so leave it off on a laptop.</p></div>
43 {{/* Last, so that revealing it (only where the browser can choose an
44 output, and once it knows there is one) moves nothing below it. */}}
45 <div id="speaker-row" hidden>{{template "field-select" dict "ID" "pick-speaker" "Name" "speaker" "Label" "Speaker"}}</div>
46 </div>
47 {{/* Spec §15.20: the audio test, beside the choices it tests. A
48 native disclosure, so it opens and closes with no script of its
49 own; devices.js removes it where there is no Web Audio to run it
50 with, and like the rest of #devices it is never seen without
51 script. Each result is said in words in its own status line, and
52 the meters are native <meter>s with names of their own. Nothing
53 here is sent anywhere: the recording is played back from memory
54 and dropped. */}}
55 <details id="audio-test" class="calls-audio-test">
56 <summary>Test audio</summary>
57 <p class="calls-raw-help">Everything here stays in this browser. Nothing you say is sent anywhere.</p>
58 <div class="calls-audio-row">
59 <button type="button" id="test-mic" rst-btn>Test microphone</button>
60 <meter id="mic-level" min="0" max="1" value="0" aria-label="Microphone level"></meter>
61 <p id="mic-result" class="calls-audio-result" role="status"></p>
62 </div>
63 <div class="calls-audio-row">
64 <button type="button" id="test-speaker" rst-btn>Play a test sound</button>
65 <meter id="speaker-level" min="0" max="1" value="0" aria-label="Test sound level"></meter>
66 <p id="speaker-result" class="calls-audio-result" role="status"></p>
67 </div>
68 <div class="calls-audio-row">
69 <button type="button" id="test-loopback" rst-btn aria-describedby="loopback-help">Hear yourself as others will</button>
70 <meter id="loopback-level" min="0" max="1" value="0" aria-label="Recording and playback level"></meter>
71 <p id="loopback-help" class="calls-raw-help">Records five seconds from your microphone, with the same settings the call will use, and plays it back to you here. The recording never leaves this browser and is thrown away once it has played.</p>
72 <p id="loopback-result" class="calls-audio-result" role="status"></p>
73 </div>
74 <audio id="test-audio" hidden></audio>
75 </details>
76</div>
77</div>
78</div>
79<div class="calls-holding-more">
80 {{/* Spec §15.26: for ten minutes after leaving a call, the call can
81 still be reported from here. report.js reveals it only when this
82 tab left this meeting's call that recently, and never without
83 script, which the sheet needs. */}}
84 <div id="report-last" class="calls-report-last" hidden><button type="button" id="report-last-open" rst-btn>Report a problem with the last call</button></div>
85 <p><a href="{{.AppHref}}" rst-btn>Join in the app</a> <span class="calls-option-help">The Calls app must be installed to open it.</span></p>
86 {{if .IsMember}}<form method="post" action="/m/{{.Sqid}}/share" target="_blank" rel="noopener" data-busy="false"><button type="submit" rst-btn aria-label="Share meeting link">{{icon "share"}}Share link</button></form>
87 <p class="calls-holding-links"><a href="/meetings/{{.Sqid}}/settings">Meeting settings</a> · <a href="/meetings/{{.Sqid}}/end">End meeting for everyone</a></p>{{end}}
88 {{/* §15.26's disclosure, where the small print is. The switch is in
89 the call's Devices panel. */}}
90 <p class="calls-option-help calls-quality-note">Calls sends anonymous connection quality reports to help fix call problems. You can turn them off under Devices during a call.</p>
91</div>
92{{template "report-sheet" .}}
93{{end}}
94