source: Klonkt/src/views/pages/ward.ejs@ 3866994

Last change on this file since 3866994 was 39dab6b, checked in by Robin <roboburr@โ€ฆ>, 3 months ago

Ward-PWA: de mini-speler, de themakleur en een breder kaartraster

Drie dingen in een ronde.

DE THEMAKLEUR (SkyPalette). De acht presets en de luminantie-regel komen
LETTERLIJK uit Shaer: dezelfde kleuren, en dezelfde beslissing welke
tekst erop leesbaar is (donker op zon, wit op nacht). Een handgehouden
lijstje zou uit de pas lopen; luminantie beslist. De keuze kleurt het
accent (knoppen, bubbels, de (+)), tint de achtergrond zacht, en laat de
hemel zijn placeholder-schijfjes bijkleuren. Opgeslagen per apparaat.

DE MINI-SPELER. Een feed-kaart rendert een gewone audio, maar de feed
wordt bij elke verversing opnieuw getekend en dan sterft dat element:
de muziek stopt midden in een nummer. Daarom is er EEN blijvende speler
buiten de feed; een audiotegel geeft alleen zijn bron door (gevangen in
de capture-fase) en pauzeert zichzelf. Zo overleeft het geluid een
herteken en een wissel naar de hemel, precies als in de app.

HET KAARTRASTER (Robins wens, 12-8). Op desktop net als op mobiel en
tablet MEER kolommen: auto-fill met een minimum van 280px vult zelf op,
een op een smalle telefoon, twee op een tablet, drie of meer breed.

New file:
src/assets/js/ward-theme.js

  • presets, onSky (luminantie), load/save/apply

Changed files:
src/assets/js/ward.js

  • wireTheme (swatches), wireMiniPlayer (gedeelde audio)
  • de hemel skint bij op een themawissel

src/assets/js/ward-sky.js

  • buildAtlas neemt het accent; reskin() bij een kleurwissel

src/views/pages/ward.ejs

  • themaknop, themadialoog, mini-speler-balk

src/assets/css/ward.css

  • accent/achtergrond als variabelen, mini-speler, swatches, responsief raster (auto-fill minmax 280px)

src/services/i18n.js

  • kleur + play/pause in nl, en, de

test/ward-theme.test.js

  • acht presets, en de tekstkleur volgt luminantie

remarks: 905 tests groen. Slepen en het echt doorspelen van audio zijn
in code getest, niet met oor en vinger; dat is aan Robin op dev.

-robo
Co-Authored-By: Claude Opus 4.8 <noreply@โ€ฆ>

  • Property mode set to 100644
File size: 7.4 KB
RevLineย 
[303bfc4]1<!DOCTYPE html>
2<html lang="<%= lang %>">
3<head>
4 <meta charset="utf-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
6 <title><%= state.strings.title %></title>
7 <link rel="manifest" href="/ward/manifest.webmanifest">
8 <link rel="icon" href="/ward/icon.svg" type="image/svg+xml">
9 <meta name="theme-color" content="#5a32e6">
10 <link rel="stylesheet" href="/ward/app.css">
11</head>
12<body>
[084b046]13 <!-- De hemel ligt ACHTER alles en draait altijd, ook in kaartenstand: de
14 kaarten vallen straks weg en dan is hij er gewoon nog, precies zoals in
15 Shaer. Aria-hidden want hij is decor tot je hem aanzet. -->
16 <canvas id="sky" aria-hidden="true"></canvas>
17
[303bfc4]18 <main id="app">
[084b046]19 <div class="topbar">
20 <h1><%= state.strings.title %></h1>
[39dab6b]21 <button id="theme-open" type="button" class="icon-btn" aria-label="<%= state.strings.theme %>">๐ŸŽจ</button>
[084b046]22 <button id="mode" type="button" aria-pressed="false"><%= state.strings.toSky %></button>
23 </div>
[303bfc4]24
25 <!-- De hulpknop staat BOVENAAN en niet onderaan bij de instellingen.
26 Als je hem nodig hebt, moet je niet hoeven zoeken. -->
27 <section class="help">
28 <button id="help-btn" type="button"><%= state.strings.help %></button>
29 <p class="hint" id="help-hint"></p>
30 </section>
31
[a5a6249]32 <section id="feed">
[2086ea2]33 <div class="feed-head">
34 <h2><%= state.strings.feed %></h2>
35 <button id="compose-open" type="button" class="primary"><%= state.strings.write %></button>
36 </div>
[a5a6249]37 <div class="cards" id="card-list"></div>
38 <p class="empty" id="feed-empty" hidden><%= state.strings.feedEmpty %></p>
39 </section>
40
[303bfc4]41 <section id="guardians">
42 <h2><%= state.strings.guardians %></h2>
43 <ul class="list" id="guardian-list"></ul>
44 <p class="empty" id="guardians-empty" hidden><%= state.strings.noGuardians %></p>
45 </section>
46
47 <section id="offers" hidden>
48 <h2><%= state.strings.offers %></h2>
49 <ul class="list" id="offer-list"></ul>
50 </section>
51
52 <section id="waiting" hidden>
53 <h2><%= state.strings.waiting %></h2>
54 <ul class="list" id="waiting-list"></ul>
55 </section>
56 </main>
57
[2086ea2]58 <!-- De composer: schrijven met foto, geluid of video. De posttypes
59 dicteren hoe de widget eruitziet (Robins wens), maar de kern is een
60 tekstvak plus tot vier media; de server maakt de rest af (poster,
61 waveform) zoals bij de app. -->
62 <dialog id="compose-dialog">
63 <div class="compose-head">
64 <button id="compose-cancel" type="button" aria-label="<%= state.strings.cancel %>">โœ•</button>
65 <select id="compose-vis" aria-label="<%= state.strings.visibility %>">
66 <option value="friends"><%= state.strings.visFriends %></option>
67 <option value="public"><%= state.strings.visPublic %></option>
68 </select>
69 <button id="compose-send" type="button" class="primary"><%= state.strings.post %></button>
70 </div>
71 <textarea id="compose-text" rows="4" maxlength="5000" placeholder="<%= state.strings.writeHere %>"></textarea>
72 <div id="compose-media" class="compose-media"></div>
73 <div class="compose-tools">
74 <label class="tool" title="<%= state.strings.addPhoto %>">
75 ๐Ÿ“ท<input type="file" accept="image/*" hidden data-kind="image">
76 </label>
77 <label class="tool" title="<%= state.strings.addAudio %>">
78 ๐ŸŽค<input type="file" accept="audio/*" hidden data-kind="audio">
79 </label>
80 <label class="tool" title="<%= state.strings.addVideo %>">
81 ๐ŸŽฌ<input type="file" accept="video/*" hidden data-kind="video">
82 </label>
83 <span id="compose-status" class="hint"></span>
84 </div>
85 </dialog>
86
[a5a6249]87 <!-- De reddingsboei zweeft boven de pagina en is sleepbaar, net als in
88 Shaer. Een knop, geen plaatje: dan werkt hij ook met een toetsenbord en
89 leest een schermlezer hem voor. -->
90 <button id="buoy" type="button" aria-describedby="buoy-hint">๐Ÿ›Ÿ</button>
91 <p id="buoy-hint" class="sr-only"><%= state.strings.buoyHint %></p>
92
[5218863]93 <!-- De zwaai-hand (ยง5): alleen in berichtenstand, dezelfde hoek als de
94 boei. Ze wisselen elkaar af: met de kaarten verdwijnt de boei, op de
95 hemel hangt de hand. -->
96 <button id="hand" type="button" hidden aria-describedby="hand-hint">๐Ÿ‘‹</button>
97 <p id="hand-hint" class="sr-only"><%= state.strings.handHint %></p>
98
99 <!-- Het gesprek schuift OVER de hemel; die blijft eronder gewoon draaien. -->
100 <section id="thread" hidden>
101 <header>
102 <button id="thread-close" type="button" aria-label="<%= state.strings.close %>">โ†</button>
103 <h2 id="thread-name"></h2>
104 </header>
105 <div id="thread-list"></div>
106 <form id="thread-form">
107 <input id="thread-text" type="text" autocomplete="off" maxlength="2000"
108 placeholder="<%= state.strings.writeHere %>">
109 <button type="submit" class="primary"><%= state.strings.send %></button>
110 </form>
111 </section>
112
[5412cbb]113 <!-- De vriendenkiezer achter de (+): aan wie wil je iets sturen? -->
114 <dialog id="friends-dialog">
115 <h2><%= state.strings.newTitle %></h2>
116 <ul id="friends-list" class="list"></ul>
117 <p class="empty" id="friends-empty" hidden><%= state.strings.noFriends %></p>
118 <button id="friends-cancel" type="button"><%= state.strings.cancel %></button>
119 </dialog>
120
[5218863]121 <!-- Het zwaai-paneel: de tekst die je kiest IS het bericht. -->
122 <dialog id="wave-dialog">
123 <h2 id="wave-title"></h2>
124 <div class="wave-answers">
125 <button data-wave="1">๐Ÿ‘‹ <%= state.strings.wave %></button>
126 <button data-text="<%= state.strings.waveCall %>"><%= state.strings.waveCall %></button>
127 <button data-text="<%= state.strings.waveThanks %>"><%= state.strings.waveThanks %></button>
128 </div>
129 <button id="wave-cancel" type="button"><%= state.strings.notNow %></button>
130 </dialog>
131
[a5a6249]132 <dialog id="help-dialog">
133 <form method="dialog">
134 <h2 id="help-title"></h2>
135 <label for="help-words"><%= state.strings.helpWords %></label>
136 <textarea id="help-words" rows="3" maxlength="500"></textarea>
137 <menu>
138 <button value="cancel"><%= state.strings.cancel %></button>
139 <button id="help-send" value="send" class="primary"><%= state.strings.helpSend %></button>
140 </menu>
141 </form>
142 </dialog>
143
[39dab6b]144 <!-- De mini-speler: รฉรฉn blijvende <audio> buiten de feed, zodat het geluid
145 een herteken en een wissel naar de hemel overleeft. -->
146 <div id="mini-player" hidden>
147 <audio id="mini-audio" preload="none"></audio>
148 <button id="mini-toggle" type="button" aria-label="<%= state.strings.playPause %>">โ–ถ</button>
149 <span id="mini-title"></span>
150 <button id="mini-close" type="button" aria-label="<%= state.strings.close %>">โœ•</button>
151 </div>
152
153 <!-- De themakiezer (SkyPalette): acht kleuren, de keuze kleurt de hele ward. -->
154 <dialog id="theme-dialog">
155 <h2><%= state.strings.theme %></h2>
156 <div id="theme-swatches" class="swatches"></div>
157 <button id="theme-close" type="button"><%= state.strings.cancel %></button>
158 </dialog>
159
[303bfc4]160 <!-- De begintoestand reist als JSON mee, zodat de pagina meteen compleet is
161 en niet eerst leeg staat te wachten op een fetch. Geen inline script:
162 CSP staat dat niet toe, dus het gaat als data door een script-tag met
163 een type dat de browser niet uitvoert. -->
164 <script type="application/json" id="ward-state"><%- JSON.stringify(state) %></script>
[084b046]165 <!-- Module, zodat app.js het hemelmodel kan importeren. -->
166 <script type="module" src="/ward/app.js"></script>
[303bfc4]167</body>
168</html>
Note: See TracBrowser for help on using the repository browser.