From cf418a095b07c8127042390c748e721d1433879b Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Tue, 18 Aug 2026 13:40:15 +0200 Subject: fix(client): downloads stream to disk, and two rough edges on first run MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Downloads were going through RAM.** `_openDownloadTarget` tries a granted folder, then a service worker, then its floor: collect the whole file in the page and hand the browser a blob. Both of the first two are absent in the desktop application — `showDirectoryPicker` does not exist, and Chromium refuses a service worker on a custom scheme — so every download under 512 MB took the floor. A gigabyte of film meant a gigabyte of RAM, and the only visible symptom was a Save As dialog at the *end* rather than the start, which is what the operator noticed and asked about. The main process now streams to disk: it honours "save automatically" with a folder chosen once and no dialog, never overwrites (a colliding name gets a suffix), awaits each write so the renderer cannot outrun the disk and queue the file in memory anyway, and unlinks a cancelled download rather than leaving a truncated file that looks complete to whoever opens it next. Settings now offers the native folder picker instead of saying downloads are unsupported. Measured in the running application: the file on disk grows 256 KB → 512 KB → 768 KB → 1 MB as the chunks arrive, and an aborted download leaves nothing behind. **A permanent scrollbar on sign-in.** `.layout` and `.page-center` each reserved `100vh - 52px`, and `.page-center` sits inside `main`'s 24px vertical padding — so the page overflowed by exactly 48px at every window size. Found by measuring in the app rather than reading the stylesheet: `scrollHeight` 819 against a 771 viewport, then the bottom edge of every element. The centring page brings its own padding, so main's is dropped for it and the duplicated arithmetic goes rather than growing a third term. Now `scrollHeight == innerHeight`, no overflowing elements. **The first-run screen was unstyled.** It used a class name I invented (`auth-page`) that appears nowhere in the stylesheet, so it had no card and the button sat against the input. It now uses the same `page-center` + `login-card` markup as sign-in, which is where the 12px gap comes from. The sign-in link in the nav is hidden until a hub is chosen — it led to a page that could not work. 809 tests pass. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'CLAUDE.md') diff --git a/CLAUDE.md b/CLAUDE.md index 77d13d8..161aac4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -402,6 +402,23 @@ anything that assumes one key per person. for it would have failed these tests for no visible reason. Use `Object.defineProperty`; the suite is now green on 18 and 24 +- **A fallback chain reaches its floor silently.** `_openDownloadTarget` tries a + granted folder, then a service worker, then "collect it in memory and hand the + browser a blob". In the desktop application the first two do not exist — + `showDirectoryPicker` is absent and Chromium refuses a worker on a custom + scheme — so **every download under 512 MB went through RAM**, and the only + visible symptom was a Save As dialog at the *end* instead of the start. + Nothing errored. When a chain degrades, check what the floor costs on every + platform that will reach it + +- **Two elements each claiming `100vh - 52px`, one inside the other's padding.** + `.layout` and `.page-center` both reserved the viewport below the header, and + `main`'s `24px` top and bottom were added on top — a permanent 48px scrollbar + on sign-in at every window size. Found by measuring in the running app + (`document.documentElement.scrollHeight` against `innerHeight`, then the + bottom edge of every element), not by reading the stylesheet, which is the + standing rule here + ## Two lessons that cost four rounds of live testing - **`QE/deploy/e2e.py` cannot test `app.js`.** It is a second implementation of the -- cgit v1.2.3