Repository navigation
fix(settings): give provider model rows their own scroll viewport #6165
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
63767a3
edc83c7
f593196
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| /* | ||
| * Licensed to the Apache Software Foundation (ASF) under one | ||
| * or more contributor license agreements. See the NOTICE file | ||
| * distributed with this work for additional information | ||
| * regarding copyright ownership. The ASF licenses this file | ||
| * to you under the Apache License, Version 2.0 (the | ||
| * "License"); you may not use this file except in compliance | ||
| * with the License. You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, | ||
| * software distributed under the License is distributed on an | ||
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| * KIND, either express or implied. See the License for the | ||
| * specific language governing permissions and limitations | ||
| * under the License. | ||
| */ | ||
|
|
||
|
|
||
| /** The settings pane spends this as `scroll-padding-block-end`, so a control | ||
| * the keyboard reaches is scrolled clear of the sticky Save bar instead of | ||
| * under it (`styles/settings/provider-editor.css`). */ | ||
| const CLEARANCE_PROPERTY = '--provider-models-save-bar-space'; | ||
| const PANE_SELECTOR = '.settingsMainPane'; | ||
|
|
||
| /** Ref callback for the model Save bar: publishes the bar's height to the | ||
| * settings pane it is stuck to. | ||
| * | ||
| * The bar hangs over the bottom of the pane, and the model rows scroll in a | ||
| * viewport of their own: tabbing from one row's control to the next scrolls | ||
| * that viewport, and no amount of scrolling inside it can move a row out from | ||
| * under a bar stuck to the pane. With the clearance published the pane itself | ||
| * scrolls the focused control above the bar. The height is measured rather | ||
| * than tokenized because the bar wraps at narrow widths, the way the | ||
| * composer dock's clearance is measured in `use-focused-preview.ts`. | ||
| * | ||
| * A ref callback rather than a hook: the detail view that renders the bar is | ||
| * recorded renderer debt whose hook and capability budgets are frozen, so the | ||
| * measurement belongs to this feature instead. */ | ||
| export function attachModelsSaveBarClearance(bar: HTMLElement | null) { | ||
| const pane = bar?.closest<HTMLElement>(PANE_SELECTOR); | ||
| if (!bar || !pane) return; | ||
| const measure = () => { | ||
| pane.style.setProperty(CLEARANCE_PROPERTY, `${Math.ceil(bar.getBoundingClientRect().height)}px`); | ||
| }; | ||
| measure(); | ||
| const observer = new ResizeObserver(measure); | ||
| observer.observe(bar); | ||
| return () => { | ||
| observer.disconnect(); | ||
| pane.style.removeProperty(CLEARANCE_PROPERTY); | ||
| }; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -44,6 +44,37 @@ | |
| color: var(--muted-foreground); | ||
| } | ||
|
|
||
| /* The model rows of a provider connection, in their own scroll viewport. | ||
| * Settings → 模型 → a connection expands every chat-capable model into the | ||
| * page, so a large catalog made the page itself hundreds of rows tall: the | ||
| * filter scrolled away while browsing, and the advanced request settings | ||
| * below sat under the whole list. | ||
| * | ||
| * `max-height`, not `height`: a small catalog, a filtered-down list and the | ||
| * empty-result row are as tall as their content, with no blank box reserved | ||
| * under them. The bound tracks the window — 60vh keeps the surrounding | ||
| * section visible in a short window, and the 32rem cap stops the list from | ||
| * eating a tall one. | ||
| * | ||
| * `overscroll-behavior` is deliberately left at `auto`: once the list is | ||
| * scrolled to its end the wheel carries on down the settings page, so the | ||
| * viewport cannot swallow the gesture of a user heading for the sections | ||
| * below it. */ | ||
| .providerModelRowsViewport { | ||
| max-height: min(32rem, 60vh); | ||
| overflow-y: auto; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] Keep keyboard-focused model controls above the sticky Save bar in short windows At the supported 320px minimum window height, this nested scroll container can bring keyboard focus completely underneath Please coordinate focus scrolling with the sticky bar/visible portion of the inner viewport, or ensure the entire list viewport fits in the unobscured pane. Add a short-height keyboard-focus assertion: current tests only check that Save is visible and that the last row lies inside the list's own rect, which both pass while the focused control is completely obscured. Screenshot retained locally at /tmp/6165-600x320-focus.png. Existing 480x800 case does not expose this. |
||
| /* A scroll container clips at its padding edge, and model rows run flush | ||
| * with it: `rows.css` takes their inline padding away so the row track | ||
| * reaches the section's edges. Anything a row paints outside its own box — | ||
| * the keyboard focus ring of the trailing Switch (`--focus-ring-offset` out, | ||
| * `--focus-ring-width` wide), the hover wash a clickable row steps out by — | ||
| * would be cut by the new boundary. The viewport steps out by that same | ||
| * `--space-2` and takes it straight back as padding, so the rows keep the | ||
| * edges they had and everything painted around them stays inside the clip. */ | ||
| margin: calc(-1 * var(--space-2)); | ||
| padding: var(--space-2); | ||
| } | ||
|
|
||
| .providerEndpointField { | ||
| display: grid; | ||
| gap: 6px; | ||
|
|
@@ -89,6 +120,17 @@ | |
| accent-color: var(--primary); | ||
| } | ||
|
|
||
| /* The pane spends the Save bar's measured height as scroll padding, so a | ||
| control the keyboard reaches is scrolled clear of the bar instead of under | ||
| it. Needed since the model rows scroll in a viewport of their own: focus | ||
| moving down the list scrolls that viewport, which cannot move a row out from | ||
| beneath a bar stuck to the pane. The height is published by | ||
| `features/connection-settings/models-save-bar-clearance.ts`; where it is absent | ||
| the padding is nothing, so the other settings pages are untouched. */ | ||
| .settingsMainPane { | ||
| scroll-padding-block-end: var(--provider-models-save-bar-space, 0px); | ||
| } | ||
|
|
||
| /* The settings pane remains the only scroll owner. Sticky bottom keeps Save | ||
| reachable even when the model catalog extends far below the viewport. */ | ||
| .providerModelsSaveBar { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2:
overflow-y: autoalso makesoverflow-xclip, and the rows are edge-to-edge, so the trailing Switch's:focus-visiblering (about 3px outside the element) gets cut off on the right edge. I measured and screenshotted this in both the 1280 and the 480 story. rows.css documents exactly this as a keyboard-visibility regression to avoid. A small inset that you take back keeps the ring visible and the track aligned, e.g.padding: var(--space-1); margin-inline: calc(-1 * var(--space-1));.