Commit Graph
4 Commits
Author SHA1 Message Date
Kristian WindandClaude Opus 5 4be934ccc1 feat(pwa): the PNG icons the manifest and iOS have always wanted
The manifest pointed at icon.svg and nothing else. Chrome and Firefox
install happily on that; Safari does not read the manifest's icons at all
and looks for /apple-touch-icon.png by name, so an installed PWA on iOS got
a screenshot of the page as its home-screen icon.

This was written down as blocked: there is no SVG renderer on this machine,
which is why the references were removed rather than left pointing at files
that did not exist. Playwright is an SVG renderer, and it has been a
dependency the whole time.

scripts/generate-icons.js renders the same SVG at 192, 512 and 180. A script
rather than a test, so CI never runs it and never rewrites a tracked binary
— run it when the icon changes. The smoke test already fetches every icon
the manifest names, and now asks for the Apple one by name too, so a
reference with no file behind it fails there rather than on somebody's
phone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 22:37:52 +02:00
Kristian WindandClaude Opus 5 1bb28ce862 feat(web): the service worker, and subscribing to Web Push
The server side has been finished for a while — VAPID keys, RFC 8291
encryption, delivery on reminders and assignments — and nothing in the
browser ever asked for a subscription. web/static/ had a manifest and an
icon and no sw.js, so a subscription could not be created at all.

The worker handles push and notificationclick and nothing else. There is
deliberately no offline cache: the app is a live view of a list other
people are editing, and a cached shell serving yesterday's data is worse
than the browser saying it cannot connect. Adding one later means
versioning and invalidating it, which is a feature rather than a few
lines here.

Unsubscribing tells the server first. If the browser unsubscribed and
then the request failed, the server would keep pushing to an endpoint
nothing is listening on, and only a 410 from the push service would ever
clean it up.

Still not verified against a live push service — this is the client half
of a chain whose other half has only ever been tested against the RFC.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 10:56:33 +02:00
Kristian WindandClaude Opus 5 2a3367bfe0 fix(web): the label route was written outside the routes tree, and the PWA promised icons that do not exist
Release / Build and publish image (push) Failing after 13m28s
Every label link in the sidebar was dead. The route file landed in
web/web/src/routes/ because the shell that created it was already inside
web/ — SvelteKit never saw it, so /etiket/<name> fell through to the
app shell and rendered nothing.

The manifest also listed icon-192.png and icon-512.png, and app.html an
apple-touch-icon, none of which were ever generated. A manifest pointing
at missing icons is worse than one listing only what exists, so the dead
references are gone and the SVG — which Chrome and Firefox install from
happily — is what remains.

docs: hand over the project

HANDOFF.md covers what cannot be read out of the code: why the name is
not skuld, the decisions that look strange until you know the reason,
what is tested against a real service and what is only tested against
its specification, the gaps in the order I would take them, and the
traps that cost the most time to find.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 08:47:29 +02:00
Kristian WindandClaude Opus 5 12a9597919 feat(web): SvelteKit PWA with the Nordic design system
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>
2026-08-17 00:03:20 +02:00