Tredje trin mod iOS-appen. I en browser deler fladen og API'et origin: hver
sti er relativ, og sessionen er en httpOnly-cookie. I appen ligger fladen i
appen selv på capacitor://localhost, og serveren er den, ejeren skrev ind.
Alt, der er forskelligt, bor i den nye `platform.js`, så browserens svar er
præcis, hvad det var før.
- `apiURL()` og `authorize()`: relative stier og cookie i en browser; den
valgte server og `Authorization: Bearer` i appen, `credentials: 'omit'`.
- Login sender `client: app` og gemmer `session_token`; log ud fjerner det fra
telefonen, også når serveren ikke svarede.
- Live-synk i appen beder om en billet og åbner socket'en på serveren med den.
- Første skærm i appen: "Hvilken Verdande vil du logge ind på?". Adressen
prøves med /ping, før et adgangskodefelt vises, så en tastefejl, en router
eller en server, der er nede, får sin egen sætning. Passkeys og opsætning af
første konto vises ikke i appen; begge hører til serverens eget domæne.
- Billeder og downloads under /api/v1 hentes med tokenet og vises fra en blob,
i appen og kun dér (`nativemedia.js`). Stien bevares i `data-src`, og
`richtext.js` læser den først, så en note aldrig gemmer en blob-adresse.
- Service worker og Web Push er slået fra i appen: WKWebView har ingen af dem.
Serveren: `VERDANDE_APP_ORIGINS` regner flere origins som appens. Den findes
for E2E-projektet `app-origin`, der kører fladen fra app.localhost — samme
server, anden origin — med en Capacitor-stub og uden cookie. Prøven vælger
server, logger ind, ser et projekt fra "en anden enhed" dukke op over socket'en,
viser et billede fra en blob og logger ud.
Fire plantede fejl, hver fanget af prøven ved sit eget trin: ingen bearer,
socket uden billet, billeder ikke hentet, token efterladt ved log ud.
Dokumenterer også `VERDANDE_APP_SESSION_TTL` fra trin 1.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
## Mærket
Urds oplagte mærke var ᚢ uruz — bogstavet navnet begynder med i den ældre
futhark, præcis som Verdandes er ᚹ wunjo. Det blev tegnet sådan først.
Men uruz er allerede et af husets egne produkter, og dets egen
`scripts/gen-icons.ts` siger i sin kommentar at den "draws the Uruz rune (ᚢ)
in rune-gold" med "a tall left vertical, a shoulder across the top, and a
shorter" — altså samme glyf, samme konstruktion, kun en anden farve. To af
samme persons apps side om side på en hjemmeskærm, hvilket er nøjagtig det
sted mærket skal kunne skelnes.
ᚦ thurisaz blev forkastet et niveau nede af samme grund: en stav med en
trekant til højre, og det er ᚹ også. Ved 16px er de én form.
Så bogstavet frem for runen: ð, ethet i Urðr. Centreret ved at tælle malede
pixels frem for ud fra path-tallene, fordi en centreret kasse ikke er en
centreret glyf:
verdande ᚹ blæk x 53–108 midte 80.5 · y 18–131 midte 74.5
urd ð blæk x 39–110 midte 74.5 · y 15–134 midte 74.5
Verdandes +5.5 i x er dens egen bevidste forskydning — en tung stav ved
siden af et let flag skal sidde til højre for midten for at se centreret ud.
ð er symmetrisk nok til at undvære den, og de to er enige i y.
Læsbarhed, målt: ð læses fra 32px og op. Ved 16px er det en klat, og det er
ᚹ også — en favicon er ikke det, der skiller to installerede apps i en dock.
`productRune` hedder nu `productMark`, fordi ð ikke er en rune.
## Determinisme
SvelteKits `kit.version.name` er som standard `Date.now()`. Millisekundet
landede i version.json, i index.html og i service-workeren, ændrede
indholdshashen på hver chunk der refererer dem, og dermed deres filnavne. To
bygninger af samme commit havde forskellige filer i sig.
Dockerfilen har i et år sagt at `-trimpath` og en tom buildid "keep the
output reproducible". Flagene gjorde deres arbejde; frontenden i binæren bar
et tidsstempel. Sætningen var falsk, og ingen læste den.
Fastgjort til VERSION, som udgivelsen allerede sender ind, og `dev` når
intet sætter den. Målt ved at bygge to gange og diffe træet, før og efter.
Prisen er at et lokalt `npm run build` ikke længere ligner en ny version for
service-workeren — hvilket kun betyder noget på en udrullet instans, hvor
udgivelsen sender det rigtige mærkat.
Det gør også CI's nye påstand mulig: de to images deler alle lag, så urd
bærer samme binær som verdande frem for en genbygning der tilfældigvis
matcher.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The interface for phase 1: sign-in and first-run setup, Today, Upcoming,
project views, sharing, Cmd+K search and the keyboard shortcuts.
Quick add highlights what the parser understood underneath the text as
you type — a mirrored layer behind a transparent input, sliced by byte
offset because the server counts bytes and JavaScript counts UTF-16
units, which disagree on exactly the Danish letters this app is full of.
Every local action is optimistic and reconciled afterwards: ticking a
task off strikes it through and animates it out before the request
leaves, and rolls back with a message if the server disagrees.
The Content-Security-Policy is computed at startup by hashing the inline
scripts in the built index.html. Both of them change on every frontend
build, so a hard-coded hash would go stale silently and show up only as
a blank page in production. The alternative, 'unsafe-inline', would drop
the one protection that makes an injected script in a task title inert.
Today is derived from the store by date rather than from the ids that
came back with the first request — filtering against a captured list
meant a task added through quick add never appeared until a reload.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>