perf(gui): hydrate native message media only for visible owners

Merged
JL
5d ago
+2118-11

Native history and page frames read distinct attachments even when no conversation is selected. Retain their cache references and hydrate only the actual timeline viewport, displayed Status update or current viewer image. Cache reads run on the background executor. Retained native history can still obtain the directory cache after IPC closes, without opening a WhatsApp session.

Depends on #280, which is now merged. This branch integrates main 01987cc, including #281's dependency refresh. The diff against main contains only 13 hydration/test/measurement files; it does not repeat deduplication, storage, CLI, daemon, dependency, manifest or workflow changes. Hooks preserve the updated shortcut initialization and scrolling APIs.

The controller admits at most 64 demanded descriptors and one read at a time, including cancelled work until its task finishes. A read checks file size before allocating a fixed buffer, caps payloads at 48 MiB and rejects growth during the read. Revalidate account/reset generation, visible owner, key and message media/content/edit/revoke state before adoption. Destroying the controller cancels queued work; a regression releases the app entity before the worker runs and verifies zero cache reads. Repeated visible keys share one buffer. Missing reads do not retry each repaint; leaving/returning can retry. The read ceiling is not a total application-heap bound; existing retained/decoded-media policies remain in place.

Preserve inline previews without downloadable descriptors offline, full/preview MIME adoption and the winner of a racing explicit download. Native hydration works without a live IPC session. Remote/web caches, avatar readiness and explicit download responses retain their existing paths. Automatic channel-preview improvements and Status full-media behavior wait for the local cache attempt. Opening Status still carries its existing full-media request; cache hydration does not substitute an upstream fetch for a missing local preview.

Viewer albums include deferred full references, hydrate only the shown image and transition from loading to an error when a cache read fails. Inline preview bytes survive a separate full-reference failure for the bubble; the fullscreen viewer waits for full bytes. Preview-only, video and revoked rows stay outside the image album. Per-row prepaint listeners are attached only while references need hydration; plain-text and materialized rows do not allocate that callback. No persistent negative cache, account-wide payload map or new worker pool.

Paired synthetic release I/O probe, 12 distinct 1 MiB files: no demand changes payload reads/retained payload from 12 MiB to zero, with zero additional requested payload heap in the extracted fill path. With two requested rows, reads/retained payload fall to 2 MiB and additional peak requested heap from 12,583,508 to 2,097,203 B; primitive-loop medians are 5.742 to 0.711 ms, repeated at 6.374 to 0.853 ms. With all 12 requested, distributions overlap, so no speedup is claimed. Eleven alternating process pairs per scenario/mode, excluded warmups, repeated independently, with 528 observations in docs/measurements/demand-media*.csv.

The probe extracts production fill helpers and the bounded-file primitive with identical minimal domain/trait shims, using rustc 1.99.1 -O on one Linux runner. It excludes the controller, task dispatch, real domain adoption, layout, decoding, IPC, daemon and startup. No-demand head code is optimized away in the shim; its timer-floor duration is not a GUI time or speedup. Heap accounting runs separately; the disabled allocator wrapper remains installed during timing. C wait4 process CPU/RSS include startup/assertions/teardown and are documented separately. The measured helper bodies are unchanged by the main integration; the probe does not measure dependency-upgrade effects. No real-account peak elimination or whole-GUI CPU/RSS gain is claimed. No account data, raw laptop profiles, credentials, root, CodSpeed or eBPF were used.

Validation after integrating main: cargo test --locked --workspace --all-features passed 2,387 top-level unit/integration/doctests, with 31 ignored, plus one successful isolated avatar subprocess. The GUI group passed 613 tests with 8 ignored measurements. Workspace all-target/all-feature Clippy with -D warnings, formatting and the complete diff check passed. The all-feature GUI web target type-checked with nightly-2026-10-29 and rebuilt std/panic_abort using the production wasm flags, with warnings in unmodified paths. This is not a browser runtime test.

Coverage includes actual bottom-follow layout and scrolling excluding overdraw, no-selection zero reads, native cache availability/key validation after IPC ends, offline previews, full MIME, repeated keys, read-slot/demand limits, failure suppression, stale edits/revokes/accounts, racing explicit downloads, absent/removed viewer files, rapid image changes, loading-to-error rendering and owner destruction. CSVs emit LF; all parsed measurement records remain unchanged. Method and limits are in docs/native-demand-media.md. Remote platform/browser checks and independent review of the latest head remain pending.