oficina / slides Public

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

Plain git — no account needed to clone.

Download

Download this file

1{{/* viewer — GET /d/{sqid} and GET /s/{token}, the paged viewer.
2
3 One file for all three audiences (viewer.go's modeOwner,
4 modeMember, modeLink). The mode decides what the handler PUTS in
5 the view data — the notes, the owner's buttons, the filmstrip's
6 links — and this template only ever asks whether a field is there.
7 That is the whole reason it is not two templates: a second file is
8 a second place for "does this reader get the speaker notes?" to be
9 answered, and the two answers would drift.
10
11 render.go builds it twice, as "viewer" (with the account menu) and
12 as "viewer_link" (with a sign-in link instead), because nobody
13 reading a deck over a share link has an account to show.
14
15 Present is task 6's surface in its scriptless form: one slide, two
16 links, nothing else on the page. §8's floor, and the thing the
17 fullscreen presenter will be an enhancement OF rather than a
18 replacement for. */}}
19{{/* The slide's own stylesheet, on the screens that render a slide and
20 nowhere else: it is a document's look, not the app's chrome, and
21 every other screen would pay for it without using it. */}}
22{{define "page-head"}}<link rel="stylesheet" href="{{asset "static/slides.css"}}">
23<style>
24 /* The chrome around the slide. Everything a slide itself looks like
25 is slides.css; these five rules only place the viewer's own parts,
26 and each one is an app attribute (slides-*) so nothing here can
27 collide with the rst-* vocabulary. */
28 [slides-stage] {
29 margin-block: var(--rst-sp-4);
30 }
31 [slides-tools] {
32 display: flex;
33 flex-wrap: wrap;
34 gap: var(--rst-sp-2);
35 }
36 [slides-pager] {
37 align-items: center;
38 display: flex;
39 flex-wrap: wrap;
40 gap: var(--rst-sp-3);
41 }
42 /* A pager end — the previous link on slide one — is plain text, not
43 a disabled control: there is nowhere to go, so there is nothing to
44 put in the tab order. */
45 [slides-pager-end],
46 [slides-counter] {
47 color: var(--rst-text-muted);
48 margin: 0;
49 }
50 [slides-film] {
51 display: flex;
52 flex-wrap: wrap;
53 gap: var(--rst-sp-2);
54 list-style: none;
55 margin: 0;
56 padding: 0;
57 }
58 [slides-film] a {
59 display: inline-block;
60 /* 24 by 24 is the floor a pointer target has to meet (WCAG 2.2 AA);
61 the padding is what gets a one-word slide name there. */
62 min-height: 24px;
63 padding: var(--rst-sp-1) var(--rst-sp-2);
64 }
65 [slides-film] a[aria-current="true"] {
66 background: var(--rst-accent-soft);
67 border-radius: var(--rst-radius-sm);
68 font-weight: 600;
69 }
70</style>{{end}}
71{{define "content"}}
72{{if .Present}}
73<div slides-present data-vp="{{.VP}}">
74 {{.Slide}}
75 <nav slides-pager aria-label="{{.PagerLabel}}">
76 {{if .PrevHref}}<a rst-btn href="{{.PrevHref}}" rel="prev">{{.PrevLabel}}</a>
77 {{else}}<span slides-pager-end>{{.PrevLabel}}</span>{{end}}
78 <p slides-counter>{{.Counter}}</p>
79 {{if .NextHref}}<a rst-btn href="{{.NextHref}}" rel="next">{{.NextLabel}}</a>
80 {{else}}<span slides-pager-end>{{.NextLabel}}</span>{{end}}
81 </nav>
82</div>
83{{else}}
84<div slides-viewer data-vp="{{.VP}}">
85{{if .BackHref}}{{template "back-nav" dict "Href" .BackHref "Label" .BackLabel}}{{end}}
86{{template "page-header" dict "Title" .Title}}
87{{if or .EditHref .SettingsHref .ShareHref .NotesHref .ExportHref .PresentHref .PresentAction .FollowHref}}
88<nav slides-tools aria-label="{{.ToolsLabel}}">
89 {{if .EditHref}}<a rst-btn="primary" href="{{.EditHref}}">{{.EditLabel}}</a>{{end}}
90 {{if .SettingsHref}}<a rst-btn href="{{.SettingsHref}}">{{.SettingsLabel}}</a>{{end}}
91 {{if .ShareHref}}<a rst-btn href="{{.ShareHref}}">{{.ShareLabel}}</a>{{end}}
92 {{if .NotesHref}}<a rst-btn href="{{.NotesHref}}">{{.NotesPageLabel}}</a>{{end}}
93 {{/* Export is a LINK to a screen of its own, not a form button: it
94 asks whether the speaker notes go in the file before it starts
95 anything (exports.go). */}}
96 {{if .ExportHref}}<a rst-btn href="{{.ExportHref}}">{{.ExportLabel}}</a>{{end}}
97 {{/* Present is a FORM on the signed-in screens, because pressing it
98 writes: it opens a session and mints the remote's capability
99 (presentation.go). Over a share link it stays a link to the same
100 capability with ?present=1 (spec §5) — a reader may present the
101 deck on their own screen, and may not start a session on
102 somebody else's deck. */}}
103 {{if .PresentAction}}
104 <form method="post" action="{{.PresentAction}}">
105 <button rst-btn="primary" type="submit">{{.PresentLabel}}</button>
106 </form>
107 {{else if .PresentHref}}<a rst-btn href="{{.PresentHref}}">{{.PresentLabel}}</a>{{end}}
108 {{/* Somebody is presenting this deck right now, and this is the
109 offer to watch along (spec §3.4). Only ever on a share link. */}}
110 {{if .FollowHref}}<a rst-btn="primary" href="{{.FollowHref}}">{{.FollowLabel}}</a>{{end}}
111</nav>
112{{end}}
113
114{{if .HasSlides}}
115<div slides-stage>{{.Slide}}</div>
116
117<nav slides-pager aria-label="{{.PagerLabel}}">
118 {{if .PrevHref}}<a rst-btn href="{{.PrevHref}}" rel="prev">{{.PrevLabel}}</a>
119 {{else}}<span slides-pager-end>{{.PrevLabel}}</span>{{end}}
120 <p slides-counter>{{.Counter}}</p>
121 {{if .NextHref}}<a rst-btn href="{{.NextHref}}" rel="next">{{.NextLabel}}</a>
122 {{else}}<span slides-pager-end>{{.NextLabel}}</span>{{end}}
123</nav>
124
125{{if .HasNotes}}
126<details slides-notes>
127 <summary>{{.NotesLabel}}</summary>
128 {{.Notes}}
129</details>
130{{end}}
131
132<nav slides-film-nav aria-label="{{.FilmLabel}}">
133 <ol slides-film>
134 {{range .Film}}
135 <li><a href="{{.Href}}"{{if .Current}} aria-current="true"{{end}}>{{.Label}}</a></li>
136 {{end}}
137 </ol>
138</nav>
139{{else}}
140<div rst-box>
141 {{template "empty-state" dict "Title" .EmptyTitle "Body" .EmptyBody "ActionHref" .EditHref "ActionLabel" .EditLabel}}
142</div>
143{{end}}
144</div>
145{{end}}
146{{end}}
147