// Invariants that keep the chat from shifting on mobile, iOS in particular. // // Every assertion here corresponds to something that was actually wrong: // // - `env(safe-area-inset-*)` was already used by the composer but resolved to 0, // because the viewport meta lacked `viewport-fit=cover`. // - A first attempt pinned the shell with position:fixed and chased // visualViewport.offsetTop from JS. That is the wrong instrument: a fixed box // is laid out against the layout viewport, which iOS never shrinks for the // keyboard, so it must be chased forever — and it felt nailed down rather // than laid out. The shell is sized from the visual viewport instead. // - `--sb-vh` was rewritten on every `visualViewport` *scroll* event, so the // whole layout resized under the finger while the iOS URL bar collapsed. // - Children re-asserted a full viewport height, adding the header back. // - The message list is `flex: 1` in a column, which defaults to // `min-height: auto` — it refuses to shrink below its content and pushes the // composer off the bottom once the conversation is long enough. // // These are structural checks. The layout itself is measured against a real // connection in a real browser; see the notes in the pull request. import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; const html = readFileSync(new URL('../index.html', import.meta.url), 'utf8'); const app = readFileSync(new URL('../src/app.jsx', import.meta.url), 'utf8'); // --------------------------------------------------------------------------- // safe areas // --------------------------------------------------------------------------- const meta = /