From 73e4e37f1909af89a16bfaa32b58b74a6901b7c6 Mon Sep 17 00:00:00 2001 From: r-zakarya <82443831+r-zakarya@users.noreply.github.com> Date: Sat, 4 Jul 2026 17:49:53 +0100 Subject: [PATCH] docs: document chunked file transport, busy overlay, VACUUM, shutdown, UI features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CLAUDE.md was missing most of this session's work. Added the gotchas a fresh session most needs: - Chunked JS→Delphi transport (_streamChunks) + the WebView2 URL-limit black-screen trap + the resolved-chunk stale-timeout hang (don't regress the clearTimeout in onFileChunkAck). - Busy overlay helpers + the undefined-CSS-var trap (--bg-elev-3). - Auto-VACUUM (SQLite never shrinks on DELETE). - Clean shutdown / WAL (WM_QUERYENDSESSION). - WebView2 nav race (cold-start black screen). - node --check build gate. - UI/data: profile avatar (users.avatar_b64 + /avatar), quick-search fill modes + username-only autofill + keepclip, editable custom-field combobox, settings search, password reveal on prompts. Co-Authored-By: Claude Opus 4.7 --- CLAUDE.md | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 84458ab..60560ee 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -22,6 +22,12 @@ Toute modif `index.html` / `js/` / `css/` nécessite : Sans étape 1, l'exe embarque l'ancienne version des assets — le bug le plus courant après modif frontend. +`BuildAssets.ps1` lance `node --check` sur chaque `.js` embarqué **avant** +de générer `assets.res` : une erreur de syntaxe JS avorte le build (au +lieu d'embarquer un bundle mort qui ne se révèle qu'après un rebuild +Delphi complet). Node optionnel — absent = warning, on continue. Toujours +`node --check js/app.js` après un gros edit JS. + ## Carte des fichiers | Rôle | Path | @@ -689,6 +695,96 @@ champ — ça détruisait le focus sur les forms qui n'étaient pas centrés brandés. URL hostname-only fonctionne pour les sites dont le titre contient le hostname (rare). +## Transport gros fichiers JS→Delphi (chunké — PIÈGE MAJEUR) + +Les bridge cmds passent par `window.location.href = 'cmd://...'`. WebView2 +**cape l'URL à ~2 Mo** : un gros payload base64 dans l'URL (attachment, +export, backup, snapshot de sync) fait dépasser la limite → la navigation +**vide le document → écran noir**. Symptôme classique quand le vault +grossit. + +Solution : `_streamChunks(reqId, b64, onProgress)` (js/app.js) découpe le +base64 en morceaux de 1 Mo envoyés **séquentiellement** (chaque chunk +attend son ack `Bridge.onFileChunkAck` avant le suivant — sinon les +`location.href` se coalescent et seul le dernier passe). Côté Delphi, +`file/chunk` accumule dans `FFileSaveChunks[reqId]` (un `TStringBuilder`), +puis un `*-commit` reconstruit et agit : + +- `file/save-commit` → `SaveDecodedFile` (dialogue Save As) +- `file/write-commit` → `WriteDecodedFile` (write silencieux vers un path) +- `webdav/put-commit` → PUT WebDAV (avec If-Match) + +`Bridge.saveFile` / `writeFile` / `_webdavCall(put)` basculent +automatiquement sur ce chemin si `b64.length > 1 Mo`, sinon single-shot. + +**Bug historique corrigé** : chaque chunk réutilise le même `reqId` ; un +`setTimeout(30s)` d'un chunk déjà résolu finissait par supprimer le +resolver du chunk courant → hang permanent. `onFileChunkAck` **clear +maintenant le timer** (`clearTimeout(r.timer)`) — ne PAS régresser ça. + +Tout nouveau transfert de gros payload JS→Delphi DOIT passer par +`_streamChunks`, jamais mettre le data brut dans l'URL. + +## Busy overlay (ops longues) + +`showBusy(text)` / `updateBusy(text)` / `hideBusy()` (js/app.js) → overlay +plein écran avec spinner + texte, `z-index 200`. Utilisé pour export, +download attachment, backup manuel, rotation master pw. Pattern : `showBusy` ++ `await new Promise(r => setTimeout(r,0))` (laisse peindre l'overlay avant +de bloquer le thread) puis `finally hideBusy()`. Le spinner utilise +`--border` (pas `--bg-elev-3` qui n'était pas défini avant — piège CSS : +un `var()` non défini SANS fallback = déclaration invalide, élément +invisible). + +## Auto-VACUUM SQLite + +SQLite ne réduit **jamais** le fichier sur `DELETE` (pages marquées libres, +fichier gardé). Supprimer de gros attachments laisse `vault.db` gonflé. +`DB.CompactIfBloated` (PM.Database) fait `VACUUM` **seulement si** > 20 % +des pages sont libres ET > ~2 Mo récupérables → appelé au démarrage + +après chaque `DELETE /attachments/{id}`. Un petit vault sain ne paie rien. + +## Divers UI / data (session récente) + +- **Profil / avatar** : colonne `users.avatar_b64 TEXT` (data URI, non + chiffré, cosmétique). Endpoints `GET/POST /avatar`. `state.avatarDataUri` + chargé à `enterApp`. Pas de photo → initiale sur couleur déterministe + (`avatarColorFor(username)`). Upload downscale 128px JPEG via `FileReader` + → `data:` URI (PAS `blob:` — la CSP `img-src 'self' data:` bloque blob). + Inclus dans l'export JSON (`avatar_b64`), restauré à l'import si absent. +- **Quick search — modes fill** (Ctrl+Shift+Q) : Enter/clic-gauche = full + (user + Tab + pwd), Shift+Enter/clic-droit = username seul, Ctrl+Enter/ + Ctrl+clic = password seul. `Bridge.executeAutofill(u, p, hide, 'user')` + → `field=user` → `ExecuteAutofill(..., AUsernameOnly=True)`. En copy-mode + (tray/palette, pas de HWND cible) : clic = copy pwd, clic-droit = copy user. + Fix clipboard : `MinimizeToTray(AClearClipboard=False)` via `keepclip=1` + pour ne pas effacer le pwd juste copié quand on re-minimise. +- **Custom fields combobox éditable** : les champs avec `options` (card + brand, expiry…) rendent un `` + menu custom `.so-combo` (flèche = + toutes les options, input = frappe libre). PAS un `` natif + (il filtre au texte tapé → n'affiche que ce qui matche, déroutant). +- **Settings search** : `#settingsSearch` filtre les `.setting-row` par + texte ; match sur le label de section garde toute la section. Esc dans + la search avec query = clear ; Esc vide = ferme Settings. +- **Password reveal** : `promptDialog` a un œil en mode `password` → tous + les prompts (export, import, backup pwd, recovery, sync pwd) le montrent. + +## Clean shutdown (WAL) + +`WM_QUERYENDSESSION` / `WM_ENDSESSION` captés dans la fenêtre message-only +(`PM.Bridge`) → `FShutdownPending`. `FormCloseQuery` bypasse le +minimize-to-tray sur arrêt/redémarrage/logoff Windows → `FServer.Free` +checkpoint le WAL proprement (sinon Windows force-kill après timeout → +résidus `-shm`/`-wal`). Kill brutal (Task Manager) laisse les résidus, mais +SQLite récupère au prochain open. + +## WebView2 nav race (écran noir cold-start) + +Le timer de navigation (1.5 s) ne consomme `FPendingURL` que si +`FBrowserInitialized` (set dans `OnInitialized`) — sinon il ré-arme (borné +10 essais). Sans ça, un tick timer avant l'init du moteur Navigate() dans +le vide ET vide `FPendingURL` → écran noir permanent sur cold-start lent. + ## Contraintes / pièges connus - **UIPI** : process non-élevé ne peut pas SendInput vers process élevé