Verdande og Urd er to af nornerne i Völuspá, og digtet har dem skære
mærker i træ: *skáru á skíði*. Verdande er det der bliver; Urd er det der
blev, og blev gemt. En instans startet uden opgaveruterne er et andet
produkt for den der bruger den, så den siger et andet navn.
Navnet læses fra udgaven i ÉT sted — Config.ProductName/ProductSlug —
frem for at stå skrevet i teksterne. Frontend-bygget er byte-identisk i
begge udgaver; serveren omdøber de to filer, browseren læser før noget
JavaScript kører.
Der er to titler, og det blev først klart, da e2e-prøven sagde det:
skallens <title>, som serveren omdøber, og rutens egen i <svelte:head>,
som appen sætter bagefter og som overskriver den første. Go-prøven var
grøn over de bytes, serveren sendte, mens fanen sagde "Noter · verdande".
Begge prøver spørger nu om begge.
Omdøbningen er ankret på hele strenge frem for på ordet "verdande", fordi
app.html også indeholder localStorage.getItem('verdande:theme'). Omdøbes
præfikset der og ikke i resten af appen, bliver temaet sat fra én nøgle og
læst fra en anden, og det eneste symptom er det hvide blink, det
indlejrede script findes for at undgå. Tre plantede fejl bekræfter, at det
er de rigtige prøver, der fanger det: et flyttet anker i app.html, en bred
søg-erstat, og en applyRebrands uden fejlretur.
Mailen var det vigtigste sted. appName var et felt med en værdi og ingen
læser, og kommentaren ved siden af sagde, at det stod i hver besked — de
tre steder havde ordet skrevet ud i stedet. En invitation fra en
urd-instans går til et andet menneskes indbakke.
Også omdøbt: web-manifestet (og sendt som application/manifest+json — Go
kender ikke .webmanifest og sniffer den til text/plain, hvorefter
browseren ignorerer manifestet uden at sige noget, og installationsknappen
bare ikke er der), mærket i menuen (ᚢ uruz frem for ᚹ wunjo),
opstartsloggen, passkey-promptens visningsnavn, ICS-kalendernavnet og
udseendet, der er opkaldt efter programmet.
Ikonet er IKKE med: icon.svg er stadig ᚹ, og de tre PNG'er genereres fra
den. Det rører binære, genererede filer og hører i sin egen ændring.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
96 lines
2.1 KiB
Svelte
96 lines
2.1 KiB
Svelte
<script>
|
|
/**
|
|
* What the app shows when it could not reach the server at all.
|
|
*
|
|
* Before this, an outage rendered the sign-in screen: the boot asks who you
|
|
* are, the request never gets an answer, `user` stays null, and null means
|
|
* "logged out" everywhere else. So the app blamed the session for the network
|
|
* being down, and offered a password box that could not possibly work. That is
|
|
* worse than an error message, because it looks like it might work — people
|
|
* type the password, get nothing, and conclude their account is broken.
|
|
*
|
|
* It happened for real on 17 September 2026: the instance's DNS record was
|
|
* deleted, and what the app showed was a slow-loading login page.
|
|
*
|
|
* The text says the data is safe, because that is the question somebody
|
|
* actually has when their notes vanish behind a login box.
|
|
*/
|
|
import { t } from '$lib/i18n.svelte.js';
|
|
import { app } from '$lib/stores.svelte.js';
|
|
|
|
let trying = $state(false);
|
|
|
|
async function again() {
|
|
trying = true;
|
|
try {
|
|
await app.load();
|
|
} finally {
|
|
trying = false;
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<div class="screen">
|
|
<div class="card">
|
|
<div class="brand">
|
|
<span class="rune" aria-hidden="true">ᚹ</span>
|
|
<h1>verdande</h1>
|
|
</div>
|
|
|
|
<h2>{t('net.downTitle')}</h2>
|
|
<p class="lede">{t('net.downBody', { product: app.productName })}</p>
|
|
|
|
<button onclick={again} disabled={trying}>
|
|
{trying ? t('net.reconnecting') : t('net.downRetry')}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.screen {
|
|
height: 100dvh;
|
|
display: grid;
|
|
place-items: center;
|
|
padding: var(--s4);
|
|
background: var(--ground);
|
|
}
|
|
|
|
.card {
|
|
width: 100%;
|
|
max-width: 340px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--s4);
|
|
text-align: center;
|
|
}
|
|
|
|
.brand {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--s3);
|
|
justify-content: center;
|
|
}
|
|
|
|
.rune {
|
|
font-size: var(--text-2xl);
|
|
color: var(--accent);
|
|
line-height: 1;
|
|
}
|
|
|
|
h1 {
|
|
font-size: var(--text-xl);
|
|
margin: 0;
|
|
}
|
|
|
|
h2 {
|
|
font-size: var(--text-lg);
|
|
margin: 0;
|
|
}
|
|
|
|
.lede {
|
|
margin: 0;
|
|
color: var(--ink-soft);
|
|
line-height: 1.5;
|
|
}
|
|
</style>
|