mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-10-26 01:27:03 +08:00
fix(ui): reserve the scrollbar gutter on main scroll areas
v7 shows an 8px scrollbar that takes layout space, so a page or list whose content crossed the overflow threshold (filtering the MCP table, switching usage tabs) shifted every column sideways by 8px. Add a scroll-stable utility (scrollbar-gutter: stable) and apply it to page scroll areas, sheet bodies and the large list/table containers; menus, code blocks and small dialogs keep the default. Drop the unused scroll-overlay utility.
This commit is contained in:
1 parent
4aaafb8f41
commit
c57d64f4de
17 files changed
+36
-28
No files matched your search
+10
-4
@@ -1207,7 +1207,7 @@ function App() {
|
||||
<div
|
||||
ref={providerScrollContainerRef}
|
||||
id="main-content"
|
||||
className="min-h-0 flex-1 overflow-y-auto overflow-x-hidden px-6 pb-12 pt-4"
|
||||
className="min-h-0 flex-1 overflow-y-auto scroll-stable overflow-x-hidden px-6 pb-12 pt-4"
|
||||
>
|
||||
<div className="space-y-4">
|
||||
{activeApp === "claude-desktop" && (
|
||||
@@ -1284,7 +1284,7 @@ function App() {
|
||||
id="main-content"
|
||||
role="tabpanel"
|
||||
aria-labelledby={`openclaw-config-${openclawConfigTab}`}
|
||||
className="min-h-0 flex-1 overflow-y-auto"
|
||||
className="min-h-0 flex-1 overflow-y-auto scroll-stable"
|
||||
>
|
||||
{openclawConfigTab === "env" ? (
|
||||
<EnvPanel />
|
||||
@@ -1298,7 +1298,10 @@ function App() {
|
||||
);
|
||||
case "hermesMemory":
|
||||
return (
|
||||
<div id="main-content" className="min-h-0 flex-1 overflow-y-auto">
|
||||
<div
|
||||
id="main-content"
|
||||
className="min-h-0 flex-1 overflow-y-auto scroll-stable"
|
||||
>
|
||||
<HermesMemoryPanel onOpenWebUI={openHermesWebUI} />
|
||||
</div>
|
||||
);
|
||||
@@ -1350,7 +1353,10 @@ function App() {
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<div id="main-content" className="min-h-0 flex-1 overflow-y-auto">
|
||||
<div
|
||||
id="main-content"
|
||||
className="min-h-0 flex-1 overflow-y-auto scroll-stable"
|
||||
>
|
||||
<div className="px-6 pb-10 pt-4">
|
||||
<AuthCenterPanel showIntro={false} />
|
||||
</div>
|
||||
|
||||
@@ -249,7 +249,10 @@ export function AppsPage() {
|
||||
}
|
||||
/>
|
||||
|
||||
<div id="main-content" className="min-h-0 flex-1 overflow-y-auto">
|
||||
<div
|
||||
id="main-content"
|
||||
className="min-h-0 flex-1 overflow-y-auto scroll-stable"
|
||||
>
|
||||
<div className="px-6 pb-10 pt-4">
|
||||
<div className="flex h-8 items-center px-4 text-caption font-medium text-fg-2">
|
||||
<span className="flex-1">{t("appsPage.columnApp")}</span>
|
||||
|
||||
@@ -163,7 +163,7 @@ export const FullScreenPanel: React.FC<FullScreenPanelProps> = ({
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<div className="flex-1 overflow-y-auto scroll-stable">
|
||||
<div className={cn("w-full space-y-6 px-6 py-6", contentClassName)}>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -734,7 +734,7 @@ const UnifiedMcpPanel: React.FC<UnifiedMcpPanelProps> = ({
|
||||
) : (
|
||||
<div
|
||||
data-testid="mcp-matrix"
|
||||
className="min-h-0 overflow-auto rounded-panel border border-border bg-surface"
|
||||
className="min-h-0 overflow-auto scroll-stable rounded-panel border border-border bg-surface"
|
||||
>
|
||||
<MatrixColumnHighlight>
|
||||
<div className="min-w-[560px]">
|
||||
@@ -1051,7 +1051,7 @@ const UnifiedMcpPanel: React.FC<UnifiedMcpPanelProps> = ({
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex min-h-0 flex-col gap-4 overflow-y-auto overscroll-contain">
|
||||
<div className="flex min-h-0 flex-col gap-4 overflow-y-auto scroll-stable overscroll-contain">
|
||||
{importReport.error && (
|
||||
<section className="flex flex-col gap-2">
|
||||
<h3 className="m-0 text-body font-semibold">
|
||||
|
||||
@@ -134,7 +134,7 @@ function PromptDrawer({
|
||||
</SheetTitle>
|
||||
<SheetDescription className="sr-only">{description}</SheetDescription>
|
||||
</div>
|
||||
<div className="flex min-h-0 flex-1 flex-col gap-5 overflow-y-auto overscroll-contain px-6 pb-6 pt-5">
|
||||
<div className="flex min-h-0 flex-1 flex-col gap-5 overflow-y-auto scroll-stable overscroll-contain px-6 pb-6 pt-5">
|
||||
{children}
|
||||
</div>
|
||||
<div className="flex h-14 shrink-0 items-center gap-2 border-t border-border px-6">
|
||||
@@ -907,7 +907,7 @@ export const PiPromptTemplates = forwardRef<
|
||||
return (
|
||||
<ul
|
||||
aria-label={t("pi.prompts.templatesLabel")}
|
||||
className="m-0 min-h-0 shrink list-none overflow-y-auto rounded-panel border border-border bg-surface p-0"
|
||||
className="m-0 min-h-0 shrink list-none overflow-y-auto scroll-stable rounded-panel border border-border bg-surface p-0"
|
||||
>
|
||||
{filteredTemplates.map((template, index) => {
|
||||
const summary = getPiPromptTemplateSummary(template.content);
|
||||
|
||||
@@ -185,7 +185,7 @@ const PromptFormPanel: React.FC<PromptFormPanelProps> = ({
|
||||
</SheetDescription>
|
||||
</div>
|
||||
|
||||
<div className="flex min-h-0 flex-1 flex-col gap-5 overflow-y-auto overscroll-contain px-6 pb-6 pt-5">
|
||||
<div className="flex min-h-0 flex-1 flex-col gap-5 overflow-y-auto scroll-stable overscroll-contain px-6 pb-6 pt-5">
|
||||
<div className="flex shrink-0 flex-col gap-1.5">
|
||||
<label
|
||||
htmlFor={`${baseId}-name`}
|
||||
|
||||
@@ -78,7 +78,7 @@ export function PromptLibrary({
|
||||
return (
|
||||
<ul
|
||||
aria-label={listLabel}
|
||||
className="m-0 min-h-0 shrink list-none overflow-y-auto rounded-panel border border-border bg-surface p-0"
|
||||
className="m-0 min-h-0 shrink list-none overflow-y-auto scroll-stable rounded-panel border border-border bg-surface p-0"
|
||||
>
|
||||
{filtered.map(([id, prompt], index) => {
|
||||
const summary =
|
||||
|
||||
@@ -517,7 +517,7 @@ export function SwitchModePanel({
|
||||
<div
|
||||
ref={scrollRef}
|
||||
id="main-content"
|
||||
className="min-h-0 flex-1 overflow-y-auto overflow-x-hidden px-6 pb-12 pt-3"
|
||||
className="min-h-0 flex-1 overflow-y-auto scroll-stable overflow-x-hidden px-6 pb-12 pt-3"
|
||||
>
|
||||
{notices.length > 0 && (
|
||||
<div role="status" className="mb-3 flex flex-col gap-2">
|
||||
|
||||
@@ -1299,7 +1299,7 @@ export function SessionManagerPage({
|
||||
defaultValue: "会话列表",
|
||||
})}
|
||||
ref={listScrollRef}
|
||||
className="mx-6 mt-3 min-h-0 shrink overflow-y-auto overscroll-contain rounded-panel border border-border bg-surface"
|
||||
className="mx-6 mt-3 min-h-0 shrink overflow-y-auto scroll-stable overscroll-contain rounded-panel border border-border bg-surface"
|
||||
style={{ scrollPaddingTop: groupMode === "project" ? 44 : 8 }}
|
||||
>
|
||||
{renderList()}
|
||||
|
||||
@@ -103,7 +103,7 @@ export const SessionOutline = memo(function SessionOutline({
|
||||
</div>
|
||||
<ol
|
||||
ref={listRef}
|
||||
className="m-0 min-h-0 flex-1 list-none overflow-y-auto overscroll-contain px-2 pb-4"
|
||||
className="m-0 min-h-0 flex-1 list-none overflow-y-auto scroll-stable overscroll-contain px-2 pb-4"
|
||||
>
|
||||
{entries.map((entry) => {
|
||||
const active = entry.turn.index === activeTurn;
|
||||
|
||||
@@ -688,7 +688,7 @@ export function SessionReader({
|
||||
const bottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 24;
|
||||
setAtBottom((current) => (current === bottom ? current : bottom));
|
||||
}}
|
||||
className="min-h-0 flex-1 overflow-y-auto overscroll-contain px-6"
|
||||
className="min-h-0 flex-1 overflow-y-auto scroll-stable overscroll-contain px-6"
|
||||
>
|
||||
<div
|
||||
className="relative w-full"
|
||||
|
||||
@@ -381,7 +381,7 @@ export function SettingsPage({
|
||||
<div
|
||||
ref={scrollRef}
|
||||
id="main-content"
|
||||
className="min-h-0 flex-1 overflow-y-auto"
|
||||
className="min-h-0 flex-1 overflow-y-auto scroll-stable"
|
||||
>
|
||||
{isBusy ? (
|
||||
<div className="flex h-full items-center justify-center">
|
||||
|
||||
@@ -644,7 +644,7 @@ export function SkillsPage({
|
||||
<div className="flex min-h-0 flex-1 flex-col px-6 pb-5">
|
||||
<div
|
||||
ref={listScrollRef}
|
||||
className="min-h-0 overflow-auto rounded-panel border border-border bg-surface"
|
||||
className="min-h-0 overflow-auto scroll-stable rounded-panel border border-border bg-surface"
|
||||
>
|
||||
{loadingList ? (
|
||||
<div className="flex items-center justify-center gap-2 py-16 text-body text-fg-2">
|
||||
|
||||
@@ -1097,7 +1097,7 @@ const UnifiedSkillsPanel: React.FC<UnifiedSkillsPanelProps> = ({
|
||||
return (
|
||||
<div
|
||||
data-testid="skills-matrix"
|
||||
className="min-h-0 overflow-auto rounded-panel border border-border bg-surface"
|
||||
className="min-h-0 overflow-auto scroll-stable rounded-panel border border-border bg-surface"
|
||||
>
|
||||
<MatrixColumnHighlight>
|
||||
<div className="min-w-[600px]">
|
||||
|
||||
@@ -84,7 +84,7 @@ const SheetBody = ({
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"min-h-0 flex-1 overflow-y-auto overscroll-contain px-5 py-4",
|
||||
"min-h-0 flex-1 overflow-y-auto scroll-stable overscroll-contain px-5 py-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -723,12 +723,12 @@ export function UsageDashboard({
|
||||
// 空库里只有定价可配:不画全是 0 的概览和趋势,直接给页签
|
||||
<div
|
||||
id="main-content"
|
||||
className="flex min-h-0 flex-1 flex-col gap-3.5 overflow-y-auto px-6 pb-6 pt-1"
|
||||
className="flex min-h-0 flex-1 flex-col gap-3.5 overflow-y-auto scroll-stable px-6 pb-6 pt-1"
|
||||
>
|
||||
{tabsSection}
|
||||
</div>
|
||||
) : isEmpty ? (
|
||||
<div className="flex min-h-0 flex-1 flex-col items-center overflow-y-auto px-6 pb-6">
|
||||
<div className="flex min-h-0 flex-1 flex-col items-center overflow-y-auto scroll-stable px-6 pb-6">
|
||||
<div className="mt-16 flex max-w-[440px] flex-col items-center gap-3 text-center">
|
||||
<span
|
||||
aria-hidden="true"
|
||||
@@ -768,7 +768,7 @@ export function UsageDashboard({
|
||||
) : (
|
||||
<div
|
||||
id="main-content"
|
||||
className="flex min-h-0 flex-1 flex-col gap-3.5 overflow-y-auto px-6 pb-6 pt-1"
|
||||
className="flex min-h-0 flex-1 flex-col gap-3.5 overflow-y-auto scroll-stable px-6 pb-6 pt-1"
|
||||
>
|
||||
<UsageHero
|
||||
range={range}
|
||||
|
||||
+4
-5
@@ -501,11 +501,10 @@ html:not([data-keyboard]) [tabindex]:focus {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-overlay {
|
||||
scrollbar-gutter: stable both-edges;
|
||||
padding-right: 0.5rem;
|
||||
margin-right: -0.5rem;
|
||||
overflow-x: hidden;
|
||||
/* 滚动条常显且占 8px:主滚动区、大列表先把这条槽留出来,
|
||||
内容高度跨过能否滚动的临界时整列不会左右横跳 */
|
||||
.scroll-stable {
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.border-default {
|
||||
|
||||
Reference in new issue
Block a user