From 891c14a22a4478ff7cde3ca3e26559fd8ca5d60f Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Sat, 29 Aug 2026 22:12:07 +0900 Subject: [PATCH] fix(gui): cap the log table against the dynamic viewport MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `.logs-table-wrap` capped its scroll height with `calc(100vh - 260px)`. Static `vh` is the LARGE viewport, so on mobile the cap is computed for a viewport taller than the visible one and the last rows sit under the browser chrome. Now `100dvh`, which is what the rest of the shell already uses — `.app`, `.sidebar`, `.main-inner--combos` and the mobile drawer are all `100dvh`, so this line was the outlier rather than the convention. Also makes the corner toast's width cap actually apply. `.action-toast` sets `max-width: min(480px, calc(100vw - 48px))`, but every toast also carries `.notice`, which sets `max-width: var(--prose-measure)` (70ch = 542px) and is declared later in the file. At equal specificity source order won, so the toast's own cap never took effect and a long message rendered 542px wide instead of 480px. The cap is now `.action-toast.notice` — two classes — and restates both halves of the original expression, because dropping the viewport term let the toast run to the screen edge at narrow widths (measured left=0 at 430px against the 24px inset the right side keeps). Verified in a real browser by synthesizing both toasts and the log container: - corner toast with a long message: 480px at 1440, 382px at 430, 312px at 360 — identical to the previous rules at every width, with the 24px right inset intact and no overflow - log cap resolves to 540px at an 800px viewport, inside the visual viewport One thing I could not demonstrate and therefore did not change: the `calc(100vw - Npx)` terms are theoretically wrong because `vw` includes a classic scrollbar's width, but in this environment `innerWidth` and `clientWidth` never diverged (overlay scrollbars), so rewriting them against the containing block changed no measured geometry. Left as-is rather than shipping an unverifiable change. --- gui/src/styles.css | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/gui/src/styles.css b/gui/src/styles.css index 222c0f1166a..0ef2053facc 100644 --- a/gui/src/styles.css +++ b/gui/src/styles.css @@ -763,6 +763,16 @@ a.btn, a.btn:hover { text-decoration: none; } } /* Refresh icon spins while a sync is in flight (the `.spin` keyframes already exist). */ .spin-icon { animation: spin 0.9s linear infinite; } +/* The width cap needs two classes to beat `.notice { max-width: var(--prose-measure) }`, + which every toast also carries and which is declared LATER in this file. At equal + specificity source order won, so the toast resolved to 70ch (542px) rather than its + design width — visible as a toast wider than intended at desktop sizes. */ +.action-toast.notice { + /* Both halves of the original cap have to be restated here, not just the design width: + dropping the viewport term let the toast run to the screen edge at narrow widths + (measured left=0 at 430px, losing the 24px inset the right side keeps). */ + max-width: min(480px, calc(100vw - 48px)); +} /* Toast dismiss affordance: quiet ghost button inside the notice row. */ .action-toast-dismiss { flex: none; @@ -2000,7 +2010,12 @@ table.logs-table { .logs-table-wrap { overflow-y: auto; - max-height: calc(100vh - 260px); + /* `dvh`, not `vh`: static `vh` resolves against the LARGE viewport, so on mobile the + cap is computed for a viewport taller than the one the user can see and the last rows + sit under the browser chrome. The rest of the shell already moved to `100dvh` + (see .app, .sidebar, .main-inner--combos, and the mobile drawer), so this was the + outlier rather than the convention. */ + max-height: calc(100dvh - 260px); } .logs-table thead {