From 091e37fa337030ba16a21aa0ce284a86664146f7 Mon Sep 17 00:00:00 2001 From: armada-lookout Date: Tue, 21 Jul 2026 08:19:17 +0000 Subject: [PATCH 1/2] Fix #785: make the Settings nav scrollbar visible across OSes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Settings side-nav already scrolled mechanically (fixed in #803 via overflow-y-auto + min-h-0), but it relied on the OS/browser's native scrollbar. That scrollbar can render with zero visible width (observed here on Linux/GTK themes) or stay hidden until actively scrolled (macOS default), leaving no visual cue that more tabs exist below the fold — even though wheel/trackpad scrolling already worked. Wrap the tablist in the same ScrollArea (Radix) component the settings panel already uses, with type="auto" so its styled scrollbar thumb renders immediately whenever the list overflows (no hover/scroll needed to discover it), and is absent when everything fits. Co-Authored-By: Claude Sonnet 5 --- .../components/settings/SettingsNav.test.tsx | 21 +++++ .../src/components/settings/SettingsNav.tsx | 85 +++++++++++-------- 2 files changed, 70 insertions(+), 36 deletions(-) diff --git a/src/ui/src/components/settings/SettingsNav.test.tsx b/src/ui/src/components/settings/SettingsNav.test.tsx index ba8e9148..ca16f33d 100644 --- a/src/ui/src/components/settings/SettingsNav.test.tsx +++ b/src/ui/src/components/settings/SettingsNav.test.tsx @@ -118,3 +118,24 @@ describe("SettingsNav (#803) keyboard navigation", () => { expect(buttons[0]).toHaveAttribute("tabindex", "-1"); }); }); + +describe("SettingsNav (#785) scrollable when window height is reduced", () => { + it("renders the tablist inside a scroll-area viewport, not a plain overflow div", () => { + // #785: the nav previously scrolled via a bare `overflow-y-auto` div, whose + // scrollbar is the OS/browser's native one — which can render with zero + // visible width (observed on Linux/GTK themes) or stay hidden until + // scrolled (macOS default), leaving no visible affordance that more tabs + // exist below the fold. The tablist must live inside the shared + // `ScrollArea` (the same styled-scrollbar component the settings panel + // uses) so it gets a visible, cross-OS-consistent scrollbar whenever its + // content overflows. + render(); + + const tablist = screen.getByRole("tablist", { name: "Settings sections" }); + const viewport = tablist.closest('[data-slot="scroll-area-viewport"]'); + expect(viewport).not.toBeNull(); + + const scrollAreaRoot = viewport?.closest('[data-slot="scroll-area"]'); + expect(scrollAreaRoot).not.toBeNull(); + }); +}); diff --git a/src/ui/src/components/settings/SettingsNav.tsx b/src/ui/src/components/settings/SettingsNav.tsx index 6f0e90eb..62e63942 100644 --- a/src/ui/src/components/settings/SettingsNav.tsx +++ b/src/ui/src/components/settings/SettingsNav.tsx @@ -1,4 +1,5 @@ import { type KeyboardEvent, useEffect, useRef, useState } from "react"; +import { ScrollArea } from "../ui/scroll-area"; import { SettingsNavHealthIndicator } from "./SettingsNavHealthIndicator"; import type { SettingsHealthMap } from "./settings-health"; import { nextRovingIndex } from "./settings-nav-keys"; @@ -23,7 +24,7 @@ interface SettingsNavProps { * Keyboard-accessible Settings options list. * * Fixes #803: - * - `min-h-0` lets the nav shrink inside its flex row so `overflow-y-auto` + * - `min-h-0` lets the nav shrink inside its flex row so the scroll container * actually engages and every tab (e.g. "Account") stays reachable instead of * overflowing and being clipped. * - Roving-tabindex + Up/Down/Home/End arrow navigation (the list previously @@ -35,6 +36,16 @@ interface SettingsNavProps { * was vetoed by the unsaved-changes guard (where `activeIndex` doesn't change * and the resync effect below wouldn't fire). A later Arrow press then steps * from where the user visibly is, not a stale keyboard position. + * + * Fixes #785: + * - The list previously scrolled via plain `overflow-y-auto`, which relies on + * the OS/browser's native scrollbar. That scrollbar can render with zero + * visible width (seen on Linux/GTK themes, and macOS's default + * hidden-until-scroll setting), leaving no visual affordance that the list + * has more content below the fold even though wheel/trackpad scrolling + * mechanically works. Wrapping the tablist in the same `ScrollArea` used by + * the settings panel gives it the app's styled, always-visible scrollbar + * thumb consistently across OSes, matching AC3/AC4/AC5. */ export function SettingsNav({ tabs, activeTabId, panelId, onSelect, tabHealth }: SettingsNavProps) { const activeIndex = Math.max( @@ -61,40 +72,42 @@ export function SettingsNav({ tabs, activeTabId, panelId, onSelect, tabHealth }: }; return ( -
- {tabs.map((tab, index) => { - const isActive = tab.id === activeTabId; - const health = tabHealth?.[tab.id]; - return ( - - ); - })} -
+ +
+ {tabs.map((tab, index) => { + const isActive = tab.id === activeTabId; + const health = tabHealth?.[tab.id]; + return ( + + ); + })} +
+
); } From c549d9a32836e53ab3c91bd93d2cf274887a1f32 Mon Sep 17 00:00:00 2001 From: ZenoWang1999 Date: Fri, 24 Jul 2026 13:02:34 +0800 Subject: [PATCH 2/2] Fix Settings content scrollbar overlapping full-width inputs The settings content ScrollArea's 10px (w-2.5) scrollbar sat over the right edge of full-width inputs like the API Key field, because the content wrapper only had pr-1 (4px) of clearance. Bump to pr-3 so content clears the scrollbar track. Co-Authored-By: Claude Opus 4.8 --- src/ui/src/components/settings/SettingsDialog.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/src/components/settings/SettingsDialog.tsx b/src/ui/src/components/settings/SettingsDialog.tsx index 0845dbdb..d0ab49c9 100644 --- a/src/ui/src/components/settings/SettingsDialog.tsx +++ b/src/ui/src/components/settings/SettingsDialog.tsx @@ -214,7 +214,7 @@ export function SettingsDialog() { className="flex-1 min-w-0 h-full overflow-hidden" > -
+
{activeTab?.id === "general" && ( )}