Take the long dashes out of the copy

Forty five of them across the account page and the status dashboard, in prose
the owner and his family read. They are a tell: the writing reads as machine
written, which is the opposite of what a family app should feel like.

Most become a full stop or a comma. Headings that were a name plus an explainer
take a colon instead. Two subtitles that were dash-led asides become
parentheses, and the language selects lose the construction entirely: "Zvuk,
hlavní" was never how anyone says it, so they are now "Hlavní zvuk" and
"Záložní zvuk", "Hlavní titulky" and "Záložní titulky", "Zvuk u anime" and
"Titulky u anime".

Six are deliberately left: the em dash that stands in for an empty value in a
table cell or an unset field. That is a typographic convention rather than
prose, and replacing it with a word puts noise in every empty cell. Code
comments are untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Claude 2026-09-05 19:53:30 +02:00
parent 9ee36181cb
commit 0fdbd86bad
2 changed files with 46 additions and 46 deletions

View file

@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="robots" content="noindex">
<title>Amber můj účet</title>
<title>Amber: můj účet</title>
<style>
:root { color-scheme: dark; --bg:#0e0f13; --card:#191b21; --fg:#f2e9d8;
--muted:#9aa0aa; --amber:#f0a63c; --amber2:#c9791b; --err:#ff6b6b; --ok:#5fd08a;
@ -121,7 +121,7 @@
<div class="card">
<h1>Nové heslo</h1>
<p class="sub">Zadej si nové heslo k účtu. Odkaz z e-mailu platí omezenou
dobu — když nezabere, požádej o nový.</p>
dobu. Když nezabere, požádej o nový.</p>
<label for="rNew">Nové heslo</label>
<input id="rNew" type="password" autocomplete="new-password">
<label for="rNew2">Nové heslo znovu</label>
@ -175,7 +175,7 @@
<div id="forgotMsg" class="msg"></div>
</div>
<p class="hint">Účty zakládá Richard registrace tu není. Když se nemůžeš
<p class="hint">Účty zakládá Richard, registrace tu není. Když se nemůžeš
dostat dovnitř, napiš mu.</p>
</div>
@ -184,7 +184,7 @@
<ol class="steps">
<li>Na televizi otevři <b>prohlížeč</b> a zadej
<code class="url" id="tvUrl">amber.petruzalekr.cz</code>.</li>
<li>Zmáčkni tlačítko níž — stáhne se instalace, pak ji spusť.</li>
<li>Zmáčkni tlačítko níž, stáhne se instalace a pak ji spusť.</li>
<li>V Amberu zvol <b>Přihlásit z jiného zařízení</b> a načti QR kód
telefonem. Na televizi tak nemusíš nic psát.</li>
</ol>
@ -262,15 +262,15 @@
<select id="prProfile"></select>
<hr class="sep">
<div class="row">
<div><label for="prAudio">Zvuk — hlavní</label>
<div><label for="prAudio">Hlavní zvuk</label>
<select id="prAudio" class="lang"></select></div>
<div><label for="prAudio2">Zvuk — záložní</label>
<div><label for="prAudio2">Záložní zvuk</label>
<select id="prAudio2" class="lang"></select></div>
</div>
<div class="row">
<div><label for="prSub">Titulky — hlavní</label>
<div><label for="prSub">Hlavní titulky</label>
<select id="prSub" class="lang sub"></select></div>
<div><label for="prSub2">Titulky — záložní</label>
<div><label for="prSub2">Záložní titulky</label>
<select id="prSub2" class="lang sub"></select></div>
</div>
<p class="hint">Čeština jako hlavní zvuk znamená, že Amber vždycky hledá
@ -278,9 +278,9 @@
český zdroj neexistuje.</p>
<hr class="sep">
<div class="row">
<div><label for="prAnimeAudio">Anime — zvuk</label>
<div><label for="prAnimeAudio">Zvuk u anime</label>
<select id="prAnimeAudio" class="lang"></select></div>
<div><label for="prAnimeSub">Anime — titulky</label>
<div><label for="prAnimeSub">Titulky u anime</label>
<select id="prAnimeSub" class="lang sub"></select></div>
</div>
<hr class="sep">
@ -312,8 +312,8 @@
<!-- addons -->
<section id="tab-addons" class="card tab hidden">
<h2>Zdroje</h2>
<p class="sub">Odkud Amber bere filmy a seriály. Uloženo zašifrovaně
klíč se počítá z tvého hesla a nikdy neopustí prohlížeč.</p>
<p class="sub">Odkud Amber bere filmy a seriály. Uloženo zašifrovaně.
Klíč se počítá z tvého hesla a nikdy neopustí prohlížeč.</p>
<div id="adNeedPass">
<label for="adPass">Heslo k účtu</label>
@ -345,7 +345,7 @@
<p class="sub">Amber si z tvých přihlašovacích údajů poskládá adresy
zdrojů sám. Údaje jdou přímo do doplňku, přes náš server neprojdou.</p>
<h2 style="margin-top:18px">prehraj.to <span style="color:var(--muted);font-weight:400">— nebo webshare níž</span></h2>
<h2 style="margin-top:18px">prehraj.to <span style="color:var(--muted);font-weight:400">(nebo webshare níž)</span></h2>
<div class="row">
<div><label for="byPtUser">Jméno</label>
<input id="byPtUser" type="text" autocomplete="off" spellcheck="false"></div>
@ -365,14 +365,14 @@
<input id="byWsPass" type="password" autocomplete="off"></div>
</div>
<p class="hint">Druhý český host, funguje i sám o sobě. U každého
hosta ale buď obojí, nebo nic — půlka údajů selže při každém hledání.</p>
hosta ale buď obojí, nebo nic. Půlka údajů selže při každém hledání.</p>
<h2 style="margin-top:18px">TorBox <span style="color:var(--muted);font-weight:400">— volitelné, ale doporučené</span></h2>
<h2 style="margin-top:18px">TorBox <span style="color:var(--muted);font-weight:400">(volitelné, ale doporučené)</span></h2>
<label for="byTbKey">API klíč</label>
<input id="byTbKey" type="text" autocomplete="off" spellcheck="false"
placeholder="z torbox.app → Settings → API">
<p class="hint"><b>Tohle je zdroj s největším výběrem.</b> Pro cokoli,
co není česky dabované — původní znění, novinky, seriály, 4K — je
co není česky dabované (původní znění, novinky, seriály, 4K), je
TorBox zdaleka nejlepší a najde toho nejvíc. Bez něj zůstaneš jen
u českých zdrojů.</p>
@ -414,7 +414,7 @@
<ul class="list" id="deviceList"></ul>
<div id="dvMsg" class="msg"></div>
<p class="hint">Už přihlášené zařízení může dobíhat, dokud mu nevyprší
token — odebrání zastaví nová přihlášení, ne běžící relaci.</p>
token. Odebrání zastaví nová přihlášení, ne běžící relaci.</p>
</section>
<!-- downloads -->
@ -455,7 +455,7 @@
<hr class="sep">
<h2 style="margin-top:18px">Změna hesla</h2>
<div class="warn">Heslo šifruje tvoje zdroje. Změna je proto přešifruje
<div class="warn">Heslo šifruje tvoje zdroje. Změna je proto přešifruje, takže
musíš mít v této relaci odemčené <b>Zdroje</b>, jinak o ně přijdeš.</div>
<label for="acOld">Současné heslo</label>
<input id="acOld" type="password" autocomplete="current-password">
@ -598,7 +598,7 @@ $("forgotBtn").onclick=async function(){
// read from the outside.
if(!r.ok){ setMsg($("forgotMsg"),"Nepovedlo se odeslat. Zkus to za chvíli.","err"); return; }
setMsg($("forgotMsg"),"Pokud k té adrese účet existuje, přišel na ni odkaz. "+
"Mrkni i do spamu. Když nic nedorazí, napiš Richardovi heslo ti nastaví přímo.","ok");
"Mrkni i do spamu. Když nic nedorazí, napiš Richardovi, heslo ti nastaví přímo.","ok");
};
$("logoutBtn").onclick=signOut;
@ -1340,7 +1340,7 @@ $("acSaveRating").onclick=async function(){
$("acChange").onclick=async function(){
var oldP=$("acOld").value, n1=$("acNew").value, n2=$("acNew2").value;
if (!PASSWORD){ setMsg($("acMsg"),"Nejdřív odemkni Zdroje jinak přijdeš o nastavení zdrojů.","err"); return; }
if (!PASSWORD){ setMsg($("acMsg"),"Nejdřív odemkni Zdroje, jinak přijdeš o nastavení zdrojů.","err"); return; }
if (n1.length<8){ setMsg($("acMsg"),"Nové heslo musí mít aspoň 8 znaků.","err"); return; }
if (n1!==n2){ setMsg($("acMsg"),"Nová hesla se neshodují.","err"); return; }
busy($("acChange"),true,"Měním…"); setMsg($("acMsg"),"");
@ -1394,7 +1394,7 @@ $("acChange").onclick=async function(){
busy($("acChange"),false);
$("acOld").value=$("acNew").value=$("acNew2").value="";
setMsg($("acMsg"), failed
? "Heslo změněno, ale "+failed+" nastavení zdrojů se nepodařilo přešifrovat — otevři Zdroje a ulož je znovu."
? "Heslo změněno, ale "+failed+" nastavení zdrojů se nepodařilo přešifrovat. Otevři Zdroje a ulož je znovu."
: "Heslo změněno a zdroje přešifrovány.", failed?"err":"ok");
await loadAddons();
};
@ -1426,7 +1426,7 @@ $("rSave").onclick=async function(){
// The overwhelmingly likely cause, and the one worth naming: these links
// expire, and a person who tried yesterday's email has no way to know that
// from "something went wrong".
setMsg($("rMsg"),"Nepovedlo se. Odkaz nejspíš vypršel požádej o nový.","err");
setMsg($("rMsg"),"Nepovedlo se. Odkaz nejspíš vypršel, požádej o nový.","err");
return;
}
$("rNew").value=$("rNew2").value="";

View file

@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="robots" content="noindex,nofollow">
<title>Amber stav služeb</title>
<title>Amber: stav služeb</title>
<style>
/* Amber's palette, matching the other pb_public pages. */
:root{
@ -141,7 +141,7 @@
<div id="login" class="login" hidden>
<section>
<h2>Amber stav služeb</h2>
<h2>Amber: stav služeb</h2>
<p class="note" style="margin-bottom:12px">
Přihlaš se superuživatelem PocketBase. Tenhle přehled je jen pro správu.
</p>
@ -319,10 +319,10 @@ function panelReleases(rs){
"</td><td class='mono'>" + esc(r.version) + "+" + r.build +
"</td><td class='mono'>" + esc(r.sha8) + "</td><td class='num'>" + bytes(r.size) + "</td></tr>";
}).join("");
return '<section class="wide"><h2>Vydání co se rozdává</h2><table>' +
return '<section class="wide"><h2>Vydání: co se rozdává</h2><table>' +
"<tr><th>platforma</th><th>verze</th><th>sha256</th><th class='num'>velikost</th></tr>" +
body + "</table>" +
'<p class="note">Buildy se nemusí shodovat mezi platformami — když se vydává jen ' +
'<p class="note">Buildy se nemusí shodovat mezi platformami. Když se vydává jen ' +
"pro počítače, televize zůstane na starším čísle a nedostane výzvu k aktualizaci." +
"</p></section>";
}
@ -333,7 +333,7 @@ function panelClients(c){
var ev = Object.keys(c.byEvent || {}).sort(function(a, b){ return c.byEvent[b] - c.byEvent[a]; })
.slice(0, 6).map(function(k){
return "<tr><td>" + esc(k) + "</td><td class='num'>" + c.byEvent[k] + "</td></tr>"; }).join("");
return '<section><h2>Klienti 24 h</h2><div class="kv">' +
return '<section><h2>Klienti za 24 h</h2><div class="kv">' +
'<div><div class="lbl">záznamů</div><div class="big">' + c.total + "</div></div>" +
'<div><div class="lbl">chyb</div><div class="big" style="color:' +
(c.errors ? "var(--bad)" : "var(--good)") + '">' + c.errors + "</div></div>" +
@ -341,7 +341,7 @@ function panelClients(c){
(c.stutterReports ? "var(--warn)" : "var(--muted)") + '">' + c.stutterReports + "</div></div>" +
"</div>" + (ev ? "<table style='margin-top:10px'>" + ev + "</table>" : "") +
'<p class="note">Hodnocení „sekalo se" při nulových stallech je slepé místo, ' +
"které počítadla nevidí proto je zvlášť. Podrobnosti jsou v <b>Hlášení</b>." +
"které počítadla nevidí, proto je zvlášť. Podrobnosti jsou v <b>Hlášení</b>." +
"</p></section>";
}
@ -420,7 +420,7 @@ function panelErrors(d){
(showRate ? "<th class='num'>podíl</th>" : "") + "</tr>" + body + "</table></div>";
}
return '<section class="wide"><h2>Chyby v čase ' + d.window + ' dní</h2>' +
return '<section class="wide"><h2>Chyby v čase za ' + d.window + ' dní</h2>' +
'<svg class="chart" viewBox="0 0 ' + W + " " + H + '" preserveAspectRatio="none" ' +
'role="img" aria-label="chyby za posledních ' + d.window + ' dní">' +
'<line class="grid" x1="' + PAD + '" y1="' + (H - PAD) + '" x2="' + (W - PAD) +
@ -429,7 +429,7 @@ function panelErrors(d){
'<span><i style="background:var(--bad)"></i>chyby</span>' +
'<span style="margin-left:auto">vzorek ' + d.sampled +
(d.truncated ? " (zkráceno)" : "") + "</span></div>" +
tbl("Podle vydání podíl chyb, ne počet", d.byVersion, true) +
tbl("Podle vydání: podíl chyb, ne počet", d.byVersion, true) +
tbl("Podle systému", d.byPlatform, true) +
tbl("Nejčastější chyby", (d.topErrorEvents || []).map(function(x){
return { key: x.key, total: x.errors, errors: x.errors }; }), false) +
@ -577,8 +577,8 @@ function viewReports(rows){
? shown.map(reportCard).join("")
: '<div class="okbox">Za tohle období si nikdo nestěžoval.</div>';
return '<section class="wide"><h2>Hlášení co hlásila rodina</h2>' + ctl + body +
'<p class="note">„Neměří se“ znamená, že to tenhle přehrávač neposílá ' +
return '<section class="wide"><h2>Hlášení: co hlásila rodina</h2>' + ctl + body +
'<p class="note">„Neměří se“ znamená, že to tenhle přehrávač neposílá. ' +
"televize (ExoPlayer) posílá všechno, počítač (mpv) jen část. Je to jiné " +
"tvrzení než nula.</p></section>";
}
@ -607,10 +607,10 @@ function viewDevices(rows){
"<td class='num'>" + g.n + "</td>" +
"<td class='mono'>" + esc(g.last.substring(0, 16)) + "</td></tr>";
}).join("");
return '<section class="wide"><h2>Zařízení kde se kouká</h2><table>' +
return '<section class="wide"><h2>Zařízení: kde se kouká</h2><table>' +
"<tr><th>krabice</th><th>kdo</th><th>build</th><th class='num'>sezení</th>" +
"<th>naposled</th></tr>" + body + "</table>" +
'<p class="note">Identifikátor za tečkou je náhodný a jen místní říká ' +
'<p class="note">Identifikátor za tečkou je náhodný a jen místní, říká ' +
"„je to pořád ta samá krabice“, nic víc. Po reinstalaci se změní.</p></section>";
}
@ -645,7 +645,7 @@ function viewNotices(ns){
return '<section class="wide"><h2>Oznámení v aplikaci</h2>' +
'<p class="note">Uvidí ho každý v aplikaci, i nepřihlášený. ' +
'Nepiš sem nic citlivého jména služeb, adresy ani klíče.</p>' +
'Nepiš sem nic citlivého: jména služeb, adresy ani klíče.</p>' +
'<div class="kv"><label>Text<input id="nMsg" maxlength="400" ' +
'placeholder="Anime dnes nefunguje, je to výpadek u poskytovatele."></label>' +
'<label>Skrýt po (nepovinné)<input id="nEnds" type="datetime-local"></label></div>' +
@ -712,7 +712,7 @@ async function setPassword(id, btn){
// and a password left sitting in a field is one that ends up in a
// screenshot.
input.value = "";
out.textContent = "hotovo předej mu ho";
out.textContent = "hotovo, předej mu ho";
} catch (e) {
out.style.color = "var(--bad)";
out.textContent = "nepovedlo se: " + e.message;
@ -742,7 +742,7 @@ function viewAdmin(users, tpls){
'placeholder="nejméně 8 znaků"></div>' +
'<div class="span"><div class="lbl">sdílené nastavení</div>' +
'<select id="nGroup" style="width:100%">' +
'<option value="">žádné zdroje si zadá sám</option>' +
'<option value="">žádné, zdroje si zadá sám</option>' +
(tpls || []).map(function(t){
return '<option value="' + esc(t.id) + '"' + (t.isDefault ? " selected" : "") +
">" + esc(t.name || "(bez názvu)") + "</option>"; }).join("") +
@ -756,7 +756,7 @@ function viewAdmin(users, tpls){
'<div class="span"><button class="primary" id="createBtn">Založit účet</button></div>' +
"</div>" +
'<div id="createOut"></div>' +
'<p class="note">Heslo je vidět schválně vytváříš ho pro někoho jiného a ' +
'<p class="note">Heslo je vidět schválně, vytváříš ho pro někoho jiného a ' +
"musíš mu ho předat. Nikam se neukládá, po založení zmizí.</p>" +
'<p class="note">Registrace je zavřená: veřejné zakládání účtů neexistuje a ' +
"tohle jde přes superuživatele, kterým jsi právě přihlášený.</p></section>" +
@ -764,12 +764,12 @@ function viewAdmin(users, tpls){
'<section class="wide"><h2>Účty</h2><table class="accts">' +
"<tr><th>e-mail</th><th>strop</th><th>vznik</th><th>heslo</th></tr>" +
(rows || "<tr><td colspan=4 class='note'>nic</td></tr>") + "</table>" +
'<p class="note">Heslo nastavíš rovnou, bez e-mailu — reset přes e-mail ' +
'<p class="note">Heslo nastavíš rovnou, bez e-mailu. Reset přes e-mail ' +
"selhává tiše, když si adresu někdo napíše s velkým písmenem, protože " +
"PocketBase hledá účty přesně na znak a stejně vždycky odpoví „odesláno“, " +
"aby přes něj nešlo zjišťovat, které adresy existují.</p>" +
'<p class="note">Varianta rozhoduje, jaký build dostane přes automatickou ' +
"aktualizaci — server ji vybírá podle tohohle příznaku, klient si ji nemůže " +
"aktualizaci. Server ji vybírá podle tohohle příznaku, klient si ji nemůže " +
"vyžádat.</p></section>";
}
@ -808,7 +808,7 @@ function viewTemplate(list){
esc(r.name || "(bez názvu)") + (r.isDefault ? " (výchozí)" : "") + "</option>";
}).join("") + '<option value="new"' + (tplSel === "new" ? " selected" : "") +
">+ nová skupina</option>";
return '<section class="wide"><h2>Sdílené nastavení co dostane nový člen</h2>' +
return '<section class="wide"><h2>Sdílené nastavení: co dostane nový člen</h2>' +
'<div class="form">' +
'<div class="span"><div class="lbl">skupina</div>' +
'<select id="tGroup" style="width:100%">' + opts + "</select></div>" +
@ -830,7 +830,7 @@ function viewTemplate(list){
'<label class="row span" style="color:var(--muted);font-size:13px">' +
'<input type="checkbox" id="tDefault"' + (v.isDefault ? " checked" : "") +
' style="width:auto;accent-color:var(--accent)"> ' +
"výchozí skupina tuhle otevře starý kód z AMBER_INVITE_CODE</label>" +
"výchozí skupina, tuhle otevře starý kód z AMBER_INVITE_CODE</label>" +
'<label class="row span" style="color:var(--muted);font-size:13px">' +
'<input type="checkbox" id="tShow" style="width:auto;accent-color:var(--accent)"> ' +
"zobrazit hodnoty</label>" +
@ -839,12 +839,12 @@ function viewTemplate(list){
esc(String(v.updated).substring(0, 16)) + "</span>" : "") + "</div>" +
"</div><div id=\"tOut\"></div>" +
'<p class="note">V těchhle adresách jsou přihlašovací údaje k prehraj.to a ' +
"klíč k TorBoxu proto jsou schované a proto je tahle stránka jen pro tebe. " +
"klíč k TorBoxu, proto jsou schované a proto je tahle stránka jen pro tebe. " +
"Kdo dostane kód téhle skupiny, stáhne si přesně tyhle zdroje.</p>" +
'<p class="note">Každá skupina má vlastní údaje a vlastní kód, takže přátelé ' +
"nesdílí účty s rodinou. Kód musí být unikátní; dvě skupiny se stejným kódem " +
"databáze odmítne.</p>" +
(rows.length ? "" : '<div class="warnbox">Žádná skupina ještě není uložená ' +
(rows.length ? "" : '<div class="warnbox">Žádná skupina ještě není uložená, ' +
"první uložení ji vytvoří.</div>") + "</section>";
}
@ -852,7 +852,7 @@ async function saveTemplate(){
var out = el("tOut");
var name = el("tName").value.trim();
if (!name) {
out.innerHTML = '<div class="warnbox">Skupina musí mít název jinak ji ' +
out.innerHTML = '<div class="warnbox">Skupina musí mít název, jinak ji ' +
"v seznamu nerozeznáš od ostatních.</div>";
return;
}
@ -1051,7 +1051,7 @@ async function createAccount(){
// account you were not told about is the worst of the three.
try {
await preloadSharedConfig(d.id, name, pass, gid);
extra = " Sdílené zdroje jsou nastavené po přihlášení je uvidí sám.";
extra = " Sdílené zdroje jsou nastavené, po přihlášení je uvidí sám.";
} catch (e2) {
extra = ' <b style="color:var(--warn)">Účet je založený, ale sdílené ' +
"nastavení se nepodařilo přiřadit (" + esc(e2.message) +