fix(ui): size mobile layout from the live visible viewport, not dvh

Brave Android (custom bottom toolbar) and some other mobile browsers report
dvh taller than the actually visible area while position:fixed elements track
the visible viewport — so 100dvh containers extended under the browser tab
bar and the bottom of every page could never be scrolled into view.

main.ts now keeps --visual-viewport-height synced to window.innerHeight
(resize / orientationchange / visualViewport events — the same reference
frame as the fixed tab bar), and the dashboard shell, chat, and app-session
mobile heights consume it with 100dvh as fallback. The app now scales with
the browser chrome as it shows/hides. Scroll clearance margin 16px → 28px.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-07-14 09:31:52 +01:00
co-authored by Claude Fable 5
parent 85f9fc2296
commit cb4d17d1a9
3 changed files with 28 additions and 2 deletions
+18
View File
@@ -114,6 +114,24 @@ function recordError(source: string, err: unknown, info?: string) {
app.config.errorHandler = (err, _instance, info) => recordError('Vue Error', err, info)
// Keep --visual-viewport-height in sync with the REAL visible viewport.
// Mobile browsers (notably Brave Android with its custom bottom toolbar)
// misreport dvh, leaving 100dvh layouts taller than the visible area — the
// bottom of every page then hides behind the browser chrome and can never be
// scrolled into view. window.innerHeight shares the reference frame of
// position:fixed elements (the mobile tab bar), so sizing the dashboard off
// it keeps scroll containers and the fixed bars consistent on every browser.
// CSS uses var(--visual-viewport-height, 100dvh) so this is a progressive fix.
function syncViewportHeightVar() {
try {
document.documentElement.style.setProperty('--visual-viewport-height', `${window.innerHeight}px`)
} catch { /* non-browser environment */ }
}
syncViewportHeightVar()
window.addEventListener('resize', syncViewportHeightVar)
window.addEventListener('orientationchange', syncViewportHeightVar)
window.visualViewport?.addEventListener('resize', syncViewportHeightVar)
// After a frontend deploy the browser's cached index.html still points at the
// OLD hashed chunks (e.g. AppSession-Cq93o4ao.js), which 404 — Vite then throws
// "Failed to fetch dynamically imported module". The fix is to reload once so