From 5745652ebd6cf0672b2131cda21ff559aa88ff95 Mon Sep 17 00:00:00 2001 From: kennylauren Date: Fri, 14 Aug 2026 11:41:34 -0700 Subject: [PATCH] fix(chat): unify context rail hover states, dropdown surfaces, and radii Co-Authored-By: Claude Fable 5 --- .../chat/ui/widgets/ChangesWidget.tsx | 92 ++++++++++--------- .../chat/ui/widgets/WorkingContextPicker.tsx | 25 +++-- .../chat/ui/widgets/WorkspaceActionsMenu.tsx | 4 +- .../ui/widgets/WorkspaceContextPicker.tsx | 27 ++++-- .../ui/widgets/WorkspaceRowActionsMenu.tsx | 7 +- .../chat/ui/widgets/WorkspaceWidget.tsx | 10 +- 6 files changed, 98 insertions(+), 67 deletions(-) diff --git a/src/features/chat/ui/widgets/ChangesWidget.tsx b/src/features/chat/ui/widgets/ChangesWidget.tsx index d25dc8a14..aed3f8a23 100644 --- a/src/features/chat/ui/widgets/ChangesWidget.tsx +++ b/src/features/chat/ui/widgets/ChangesWidget.tsx @@ -1,4 +1,4 @@ -import { useMemo } from "react"; +import { useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { ExternalLink } from "lucide-react"; import { cn } from "@/shared/lib/cn"; @@ -7,11 +7,6 @@ import { Skeleton } from "@/shared/ui/skeleton"; import type { ChangedFile } from "@/shared/types/git"; import type { WorkspaceChangedFilesRuntime } from "../hooks/useWorkspaceGitRuntimes"; -const CHANGES_SCROLL_CONTAINER_CLASS = - "scrollbar-none max-h-[300px] overflow-y-auto rounded-sm bg-muted/60 py-1"; -const CHANGES_SCROLL_FADE_CLASS = - "pointer-events-none absolute inset-x-0 bottom-0 h-10 rounded-b-sm bg-gradient-to-t from-muted/90 to-transparent"; - /** Placeholder rows shared by the widgets and the tab-level loading branch. * Padding is the caller's job: the widgets already sit in a padded * `
`, while the tab renders these directly. */ @@ -104,11 +99,10 @@ function ChangedFileRow({ type="button" disabled={isDeleted} className={cn( - "group relative flex min-h-12 w-full select-none items-center gap-2 py-3 pl-3 pr-2 text-left", - "transition-colors duration-100 before:absolute before:inset-x-1 before:inset-y-0 before:rounded-[8px] before:transition-colors after:absolute after:bottom-0 after:left-3 after:right-3 after:h-px after:bg-border/60 last:after:hidden [&:has(+_:focus-visible)]:after:bg-transparent [&:has(+_:hover)]:after:bg-transparent [&>*]:relative [&>*]:z-[1]", + "group flex min-h-9 w-full select-none items-center gap-2 rounded-sm px-2.5 py-2 text-left transition-colors duration-100", isDeleted ? "cursor-not-allowed opacity-60" - : "cursor-pointer hover:before:bg-background/45 hover:after:bg-transparent focus-visible:before:bg-background/45 focus-visible:after:bg-transparent focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", + : "cursor-pointer hover:bg-muted focus-visible:bg-muted focus-visible:outline-none", )} onClick={isDeleted ? undefined : () => onOpen(file.path)} > @@ -134,6 +128,41 @@ function ChangedFileRow({ return {row}; } +function ChangedFilesList({ + files, + getFullPath, + onOpen, +}: { + files: ChangedFile[]; + getFullPath: (file: ChangedFile) => string; + onOpen: (path: string) => void; +}) { + const [scrolled, setScrolled] = useState(false); + + return ( +
setScrolled(event.currentTarget.scrollTop > 0)} + > + {files.map((file) => ( + + ))} +
+ ); +} + interface ChangesWidgetProps { files: ChangedFile[] | undefined; isLoading: boolean; @@ -215,19 +244,11 @@ export function ChangesWidget({ )} {files?.length ? ( -
-
- {files.map((file) => ( - - ))} -
- + `${repoPath}/${file.path}`} + onOpen={onOpenFile} + /> ) : null}
) : ( @@ -291,12 +312,12 @@ export function WorkspaceChangesWidget({ ) : changedGroups.length > 0 ? (
{probeErrorMessage ? ( -

{probeErrorMessage}

+

{probeErrorMessage}

) : null} {changedGroups.map((group) => (
{hasMultipleGroups ? ( -

+

{group.workspaceTitle}

) : null} @@ -338,24 +359,11 @@ export function WorkspaceChangesWidget({ )}
{group.files.length > 0 ? ( -
-
- {group.files.map((file) => ( - - onOpenFile(`${group.repoPath}/${file.path}`) - } - /> - ))} -
- + `${group.repoPath}/${file.path}`} + onOpen={(path) => onOpenFile(`${group.repoPath}/${path}`)} + /> ) : null}
))} diff --git a/src/features/chat/ui/widgets/WorkingContextPicker.tsx b/src/features/chat/ui/widgets/WorkingContextPicker.tsx index c0430532d..cf9accd86 100644 --- a/src/features/chat/ui/widgets/WorkingContextPicker.tsx +++ b/src/features/chat/ui/widgets/WorkingContextPicker.tsx @@ -312,15 +312,20 @@ export function WorkingContextPicker({ const hasWorktrees = worktrees.length > 0; const hasVisibleWorktrees = visibleWorktrees.length > 0; const hasVisibleBranches = visibleBranches.length > 0 && Boolean(currentPath); + // Hover/selected fills use `muted`: in dark it equals the old + // `sidebar-accent` value (gray-700), while in light it stays visible on + // white surfaces where `sidebar-accent` (gray-50) disappears. rounded-sm + // rows sit concentric inside the rounded-md popover's p-1.5 padding + // (12px + 6px = 18px); the active row carries the selected fill. const pickerRowClassName = cn( - "group flex w-full items-start gap-3 rounded-xs px-2 py-2.5 text-left", + "group flex w-full items-start gap-3 rounded-sm px-2 py-2.5 text-left", SIDEBAR_MENU_HOVER_TRANSITION_CLASS, - "hover:bg-sidebar-accent hover:text-sidebar-foreground focus-visible:bg-sidebar-accent focus-visible:text-sidebar-foreground focus-visible:outline-none aria-[current=true]:bg-sidebar-accent aria-[current=true]:text-sidebar-foreground", + "enabled:hover:bg-muted focus-visible:bg-muted focus-visible:outline-none aria-[current=true]:bg-muted", ); const branchRowClassName = cn( - "group flex w-full items-center gap-3 rounded-xs px-2 py-2.5 text-left", + "group flex w-full items-center gap-3 rounded-sm px-2 py-2.5 text-left", SIDEBAR_MENU_HOVER_TRANSITION_CLASS, - "hover:bg-sidebar-accent hover:text-sidebar-foreground focus-visible:bg-sidebar-accent focus-visible:text-sidebar-foreground focus-visible:outline-none aria-[current=true]:bg-sidebar-accent aria-[current=true]:text-sidebar-foreground", + "enabled:hover:bg-muted focus-visible:bg-muted focus-visible:outline-none aria-[current=true]:bg-muted", ); return ( @@ -334,7 +339,7 @@ export function WorkingContextPicker({ "flex w-full items-start gap-3 rounded-sm bg-muted/60 px-3.5 py-2.5", "text-sm text-foreground", SIDEBAR_MENU_HOVER_TRANSITION_CLASS, - "hover:bg-sidebar-accent hover:text-sidebar-foreground focus-visible:bg-sidebar-accent focus-visible:text-sidebar-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", + "hover:bg-muted focus-visible:bg-muted focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", )} aria-label={t("contextPanel.picker.selectWorktree")} > @@ -359,9 +364,9 @@ export function WorkingContextPicker({ -
+
@@ -473,9 +478,9 @@ export function WorkingContextPicker({ -
+
{onToggleTerminal ? ( -
+
-
+
{onOpenTerminalAtPath ? ( void onAddWorkspace?.()} > @@ -537,7 +541,7 @@ export function WorkspaceWidget({ className={cn( "flex w-full items-center gap-2 rounded-sm px-2 py-1", "text-sm text-foreground transition-colors", - "hover:bg-sidebar-accent focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", + "hover:bg-muted focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", "disabled:cursor-not-allowed disabled:opacity-70 disabled:hover:bg-transparent", )} aria-label={t("contextPanel.folder.change")}