diff --git a/apps/desktop/e2e/settings.spec.ts b/apps/desktop/e2e/settings.spec.ts index 286d4d7d25..7e7f259a4b 100644 --- a/apps/desktop/e2e/settings.spec.ts +++ b/apps/desktop/e2e/settings.spec.ts @@ -47,3 +47,41 @@ test('settings hides expanded workbar chrome and restores it on close', async ({ await expect(workbarToolbar).toBeVisible(); await expect(taskTab).toBeVisible(); }); + +test('wide settings gutters scroll the whole main pane', async ({ window: page }) => { + await page.setViewportSize({ width: 1600, height: 520 }); + await page.getByRole('button', { name: '设置' }).click(); + await page.getByRole('button', { name: '通用', exact: true }).click(); + await expect(page.getByRole('textbox', { name: '助手语气偏好' })).toBeEnabled(); + + const pane = page.locator('.settingsMainPane'); + const content = pane.locator('.settingsPageStack').first(); + const geometry = await pane.evaluate((element) => { + const paneRect = element.getBoundingClientRect(); + const contentElement = element.querySelector('.settingsPageStack'); + const contentRect = contentElement?.getBoundingClientRect(); + const layoutContent = element.querySelector('.astryx-layout-content'); + if (!contentRect || !layoutContent) throw new Error('Settings layout is incomplete'); + return { + blankRight: paneRect.right - contentRect.right, + clientHeight: element.clientHeight, + contentOverflowY: getComputedStyle(layoutContent).overflowY, + paneOverflowY: getComputedStyle(element).overflowY, + scrollHeight: element.scrollHeight, + wheelPoint: { + x: Math.floor((contentRect.right + paneRect.right) / 2), + y: Math.floor(Math.min(contentRect.top + 120, paneRect.bottom - 40)), + }, + }; + }); + + expect(geometry.blankRight).toBeGreaterThan(40); + expect(geometry.scrollHeight).toBeGreaterThan(geometry.clientHeight); + expect(geometry.contentOverflowY).not.toBe('auto'); + expect(geometry.paneOverflowY).toBe('auto'); + + await page.mouse.move(geometry.wheelPoint.x, geometry.wheelPoint.y); + await page.mouse.wheel(0, 600); + await expect.poll(() => pane.evaluate((element) => element.scrollTop)).toBeGreaterThan(0); + await expect(content).toBeVisible(); +}); diff --git a/apps/desktop/src/renderer/settings/settings-surface.tsx b/apps/desktop/src/renderer/settings/settings-surface.tsx index a87e20fb62..812c887a2a 100644 --- a/apps/desktop/src/renderer/settings/settings-surface.tsx +++ b/apps/desktop/src/renderer/settings/settings-surface.tsx @@ -674,7 +674,10 @@ export function SettingsSurface(props: { aria-label={copy.contentLabel} > )} content={( - + {loading ? ( ) : requiresRuntimeHost && runtimeHostContentStatus === 'error' ? ( diff --git a/apps/desktop/src/renderer/styles/settings/nav-sidebar.css b/apps/desktop/src/renderer/styles/settings/nav-sidebar.css index c90934af21..4d21aa6e0c 100644 --- a/apps/desktop/src/renderer/styles/settings/nav-sidebar.css +++ b/apps/desktop/src/renderer/styles/settings/nav-sidebar.css @@ -44,7 +44,11 @@ border: 0; border-radius: var(--radius-modal); background: var(--agents-content-area-bg); - overflow: hidden; + /* The pane, not the centered 920px column, is the page scroll owner. This + lets wheel input from either wide gutter scroll and keeps the native + scrollbar against the pane's outer edge. */ + overflow-x: hidden; + overflow-y: auto; } .settingsPageHeader { diff --git a/apps/desktop/stories/settings/provider-settings.stories.tsx b/apps/desktop/stories/settings/provider-settings.stories.tsx index 25131b56fa..7a6b417778 100644 --- a/apps/desktop/stories/settings/provider-settings.stories.tsx +++ b/apps/desktop/stories/settings/provider-settings.stories.tsx @@ -330,7 +330,7 @@ function ProviderStoryFrame(props: { window's width and hides exactly the layout question a page-level form raises. */} )} content={( - +