diff --git a/apps/web/src/browser/BrowserDeviceToolbar.tsx b/apps/web/src/browser/BrowserDeviceToolbar.tsx index e8ad80b597ba..1a9a0c45b69b 100644 --- a/apps/web/src/browser/BrowserDeviceToolbar.tsx +++ b/apps/web/src/browser/BrowserDeviceToolbar.tsx @@ -175,7 +175,7 @@ export function BrowserDeviceToolbar({ }} > {width >= 560 ? ( - + Dimensions ) : null} diff --git a/apps/web/src/browser/BrowserViewportResizeHandles.tsx b/apps/web/src/browser/BrowserViewportResizeHandles.tsx index 08e69c359a15..7b0015e1cb63 100644 --- a/apps/web/src/browser/BrowserViewportResizeHandles.tsx +++ b/apps/web/src/browser/BrowserViewportResizeHandles.tsx @@ -30,7 +30,7 @@ interface Props { type HandleKind = "horizontal" | "vertical" | "corner"; const EDGE_BUTTON_CLASS = - "group absolute z-20 touch-none border-0 bg-transparent p-0 outline-none before:absolute before:-inset-1 before:content-[''] focus-visible:bg-foreground/[0.04]"; + "group absolute z-20 touch-none border-0 bg-transparent p-0 outline-none before:absolute before:-inset-1 before:content-[''] focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"; const EDGE_GRIP_CLASS = "pointer-events-none absolute left-1/2 top-1/2 flex -translate-x-1/2 -translate-y-1/2 items-center justify-center text-muted-foreground/55 transition-colors duration-150 group-hover:text-foreground/85 group-focus-visible:text-foreground group-active:text-foreground"; diff --git a/apps/web/src/components/AgentsPanel.tsx b/apps/web/src/components/AgentsPanel.tsx index 62044a8659d7..1f3abf3f6de7 100644 --- a/apps/web/src/components/AgentsPanel.tsx +++ b/apps/web/src/components/AgentsPanel.tsx @@ -338,7 +338,7 @@ function PhaseSection({ onClick={() => setOpen((value) => !value)} aria-expanded={open} className={cn( - "mt-2 flex w-full items-center gap-1.5 rounded-sm px-1.5 text-left text-[.65rem] font-medium uppercase tracking-wider hover:bg-accent/40", + "mt-2 flex w-full cursor-pointer items-center gap-1.5 rounded-sm px-1.5 text-left text-[.65rem] font-medium uppercase tracking-wider hover:bg-accent/40", phase.state === "done" ? "text-success-foreground" : phase.state === "running" @@ -408,7 +408,7 @@ function ExpandedWorkflowSection({ type="button" onClick={() => setScriptOpen((value) => !value)} className={cn( - "rounded-sm border border-border/60 px-1 font-mono normal-case hover:text-foreground", + "cursor-pointer rounded-sm border border-border/60 px-1 font-mono normal-case hover:text-foreground", scriptOpen && "text-foreground", )} aria-expanded={scriptOpen} @@ -478,7 +478,7 @@ function CollapsedWorkflowSection({ } @@ -4580,7 +4583,7 @@ function ChatViewContent(props: ChatViewProps) { ), - className: "dark:shadow-none", + actions: ( @@ -1093,7 +1091,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { // a useful hierarchy nor a reliable hover cue. Status now lives in the row // content; surface is reserved for interaction (hover, multi-select, route). const rowSurfaceClassName = cn( - "group/sidebar-row relative w-full cursor-pointer overflow-hidden rounded-md text-left outline-none select-none", + "group/sidebar-row relative w-full cursor-pointer overflow-hidden rounded-[var(--control-radius)] text-left outline-none select-none focus-visible:ring-2 focus-visible:ring-ring", props.isActive ? "bg-sidebar-row-active text-sidebar-foreground" : isSelected @@ -1164,7 +1162,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { className={cn( // Sidebar chrome follows the interface font; tabular digits keep the // number from reflowing as PR states stream in. - "shrink-0 text-xs tabular-nums hover:underline", + "shrink-0 cursor-pointer rounded-sm text-xs tabular-nums outline-none hover:underline focus-visible:ring-2 focus-visible:ring-ring", variant === "slim" && variantAction === "unsettle" ? props.isActive ? "text-secondary-label" @@ -1201,7 +1199,10 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { tabIndex={0} data-testid="sidebar-row-slim" aria-busy={isRegeneratingTitle || undefined} - className={cn(rowSurfaceClassName, "flex h-9 items-center gap-2.5 px-2.5")} + className={cn( + rowSurfaceClassName, + "flex h-9 items-center gap-2.5 px-[var(--sidebar-row-content-inset)]", + )} onClick={handleClick} onDoubleClick={handleDoubleClick} onKeyDown={handleKeyDown} @@ -1284,7 +1285,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { aria-label="Wake thread now" onClick={handleUnsnoozeClick} className={cn( - "pointer-events-none absolute inset-y-0 right-0 -mr-1 inline-flex cursor-pointer items-center gap-1 rounded-md bg-transparent px-1.5 text-xs text-muted-foreground opacity-0 transition-opacity hover:text-foreground focus-visible:pointer-events-auto focus-visible:opacity-100 group-hover/sidebar-row:pointer-events-auto group-hover/sidebar-row:opacity-100", + "pointer-events-none absolute inset-y-0 right-0 -mr-1 inline-flex cursor-pointer items-center gap-1 rounded-md bg-transparent px-1.5 text-xs text-muted-foreground opacity-0 transition-[color,opacity] hover:text-foreground focus-visible:pointer-events-auto focus-visible:opacity-100 group-hover/sidebar-row:pointer-events-auto group-hover/sidebar-row:opacity-100", isWoke && "group-hover/sidebar-row:static", )} > @@ -1297,7 +1298,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { aria-label="Un-settle thread" onClick={handleUnsettleClick} className={cn( - "pointer-events-none absolute inset-y-0 right-0 -mr-1 inline-flex cursor-pointer items-center gap-1 rounded-md bg-transparent px-1.5 text-xs text-muted-foreground opacity-0 transition-opacity hover:text-foreground focus-visible:pointer-events-auto focus-visible:opacity-100 group-hover/sidebar-row:pointer-events-auto group-hover/sidebar-row:opacity-100", + "pointer-events-none absolute inset-y-0 right-0 -mr-1 inline-flex cursor-pointer items-center gap-1 rounded-md bg-transparent px-1.5 text-xs text-muted-foreground opacity-0 transition-[color,opacity] hover:text-foreground focus-visible:pointer-events-auto focus-visible:opacity-100 group-hover/sidebar-row:pointer-events-auto group-hover/sidebar-row:opacity-100", isWoke && "group-hover/sidebar-row:static", )} > @@ -1309,7 +1310,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { aria-label="Settle thread" onClick={handleSettleClick} className={cn( - "pointer-events-none absolute inset-y-0 right-0 inline-flex cursor-pointer items-center gap-1 rounded-md bg-transparent px-2 text-xs text-muted-foreground opacity-0 transition-opacity hover:text-foreground focus-visible:pointer-events-auto focus-visible:opacity-100 group-hover/sidebar-row:pointer-events-auto group-hover/sidebar-row:opacity-100", + "pointer-events-none absolute inset-y-0 right-0 inline-flex cursor-pointer items-center gap-1 rounded-md bg-transparent px-2 text-xs text-muted-foreground opacity-0 transition-[color,opacity] hover:text-foreground focus-visible:pointer-events-auto focus-visible:opacity-100 group-hover/sidebar-row:pointer-events-auto group-hover/sidebar-row:opacity-100", isWoke && "group-hover/sidebar-row:static", )} > @@ -1655,7 +1656,7 @@ const SidebarSearchResultRow = memo(function SidebarSearchResultRow(props: { onMouseMove={props.onHighlight} onClick={props.onSelect} className={cn( - "flex h-9 w-full cursor-pointer items-center gap-2.5 rounded-md px-2.5 text-left text-sm outline-none", + "flex h-9 w-full cursor-pointer items-center gap-2.5 rounded-[var(--control-radius)] px-[var(--sidebar-row-content-inset)] text-left text-sm outline-none", props.isHighlighted || props.isRouteActive ? "bg-sidebar-row-active text-sidebar-foreground" : "text-sidebar-muted-foreground/75 hover:bg-sidebar-row-hover hover:text-sidebar-foreground", @@ -2176,9 +2177,23 @@ export default function Sidebar() { true, Schema.Boolean, ); + // Shelf toggles insert or remove a whole block of rows at once. Letting the + // list's auto-animate FLIP every row below the shelf reads as jank for the + // snoozed shelf (mid-list) while the settled shelf (list tail) stays smooth, + // so both shelves toggle with the list animation suspended and behave + // identically; single-row transitions (settle, wake, reorder) keep animating. + const threadListAnimationRef = useRef | null>(null); + const toggleShelfWithoutListAnimation = useCallback((toggle: () => void) => { + const controller = threadListAnimationRef.current; + controller?.disable(); + toggle(); + window.requestAnimationFrame(() => { + window.requestAnimationFrame(() => controller?.enable()); + }); + }, []); const toggleSettledShelf = useCallback( - () => setSettledShelfExpanded((value) => !value), - [setSettledShelfExpanded], + () => toggleShelfWithoutListAnimation(() => setSettledShelfExpanded((value) => !value)), + [setSettledShelfExpanded, toggleShelfWithoutListAnimation], ); const renderedSettledThreads = useMemo(() => { if (settledShelfExpanded) return visibleSettledThreads; @@ -2199,8 +2214,8 @@ export default function Sidebar() { Schema.Boolean, ); const toggleSnoozedShelf = useCallback( - () => setSnoozedShelfExpanded((value) => !value), - [setSnoozedShelfExpanded], + () => toggleShelfWithoutListAnimation(() => setSnoozedShelfExpanded((value) => !value)), + [setSnoozedShelfExpanded, toggleShelfWithoutListAnimation], ); const visibleSnoozedThreads = useMemo(() => { if (snoozedShelfExpanded) return snoozedThreads; @@ -3316,7 +3331,7 @@ export default function Sidebar() { const attachListAutoAnimateRef = useCallback((node: HTMLUListElement | null) => { if (!node) return; - autoAnimate(node, { duration: 150, easing: "ease-out" }); + threadListAnimationRef.current = autoAnimate(node, { duration: 150, easing: "ease-out" }); }, []); // New thread defaults to the project you're in (active thread's project, @@ -3803,7 +3818,7 @@ export default function Sidebar() { onClick={toggleSnoozedShelf} aria-expanded={snoozedShelfExpanded} data-testid="sidebar-snoozed-shelf-toggle" - className="mb-1 mt-3 flex w-full cursor-pointer items-center gap-2 px-2.5 text-left" + className="mb-1 mt-3 flex w-full cursor-pointer items-center gap-2 rounded-[var(--control-radius)] px-[var(--sidebar-row-content-inset)] text-left outline-none hover:bg-sidebar-row-hover focus-visible:ring-2 focus-visible:ring-ring" > {snoozedShelfExpanded @@ -3837,7 +3852,7 @@ export default function Sidebar() { onClick={toggleSettledShelf} aria-expanded={settledShelfExpanded} data-testid="sidebar-settled-shelf-toggle" - className="mb-1 mt-3 flex w-full cursor-pointer items-center gap-2 px-2.5 text-left" + className="mb-1 mt-3 flex w-full cursor-pointer items-center gap-2 rounded-[var(--control-radius)] px-[var(--sidebar-row-content-inset)] text-left outline-none hover:bg-sidebar-row-hover focus-visible:ring-2 focus-visible:ring-ring" > {settledShelfExpanded @@ -3866,7 +3881,7 @@ export default function Sidebar() {
- {expanded ? ( + {expanded && directoryPaths.length > 0 ? ( } > @@ -155,8 +210,10 @@ export const ChangedFilesCard = memo(function ChangedFilesCard(props: { key={`changed-files-tree:${turnId}`} turnId={turnId} files={files} - allDirectoriesExpanded={allDirectoriesExpanded} + allDirectoriesExpanded={directoryBaseline} + expandedDirectories={expandedDirectories} resolvedTheme={resolvedTheme} + onToggleDirectory={toggleDirectory} onOpenTurnDiff={onOpenTurnDiff} /> ) : compactPreviewVisible ? ( @@ -179,7 +236,7 @@ export const ChangedFilesCard = memo(function ChangedFilesCard(props: { render={ ) : ( - + )} diff --git a/apps/web/src/components/chat/ComposerStashMenu.tsx b/apps/web/src/components/chat/ComposerStashMenu.tsx index fc8be327da18..7e6a7871fc13 100644 --- a/apps/web/src/components/chat/ComposerStashMenu.tsx +++ b/apps/web/src/components/chat/ComposerStashMenu.tsx @@ -6,6 +6,7 @@ import { cn } from "~/lib/utils"; import { type PromptStashEntry } from "../../promptStashStore"; import { Command, CommandGroup, CommandGroupLabel, CommandItem, CommandList } from "../ui/command"; import { Button } from "../ui/button"; +import { DROPDOWN_ELEVATION_CLASS } from "~/components/ui/popup-styles"; const SNIPPET_MAX_CHARS = 90; @@ -91,7 +92,12 @@ export const ComposerStashMenu = memo(function ComposerStashMenu(props: { return ( -
+
@@ -156,7 +162,7 @@ export const ComposerStashMenu = memo(function ComposerStashMenu(props: { @@ -886,7 +886,7 @@ function TimelineMinimap({ transform: `translateY(${activeTooltipTranslate})`, }} > - + {activeItem.userText ?? "User message"} @@ -988,7 +988,7 @@ function UserTimelineRow({ row }: { row: Extract { const preview = buildExpandedImagePreview(regularImages, image.id); @@ -1206,11 +1206,13 @@ const TurnPlanTimelineRow = memo(function TurnPlanTimelineRow({
); diff --git a/apps/web/src/components/clerk/T3ConnectSidebarSignIn.tsx b/apps/web/src/components/clerk/T3ConnectSidebarSignIn.tsx index 9dfd8dce13b1..7099b8b2cb73 100644 --- a/apps/web/src/components/clerk/T3ConnectSidebarSignIn.tsx +++ b/apps/web/src/components/clerk/T3ConnectSidebarSignIn.tsx @@ -29,7 +29,8 @@ function ConfiguredT3ConnectSidebarAvatar() { appearance={{ elements: { avatarBox: "size-7", - userButtonTrigger: "rounded-lg p-1 hover:bg-sidebar-row-hover", + userButtonTrigger: + "cursor-pointer rounded-lg p-1 outline-hidden ring-ring transition-colors hover:bg-sidebar-row-hover focus-visible:ring-2", }, }} > diff --git a/apps/web/src/components/cloud/ConnectOnboardingDialog.tsx b/apps/web/src/components/cloud/ConnectOnboardingDialog.tsx index 6248fb00bcfe..cd346b89be67 100644 --- a/apps/web/src/components/cloud/ConnectOnboardingDialog.tsx +++ b/apps/web/src/components/cloud/ConnectOnboardingDialog.tsx @@ -307,7 +307,7 @@ function OnboardingStepper({ type="button" disabled={disabled} className={cn( - "grid min-w-0 grid-cols-[1rem_minmax(0,1fr)] gap-x-2 rounded-lg border px-3 py-2 text-left", + "grid min-w-0 cursor-pointer grid-cols-[1rem_minmax(0,1fr)] gap-x-2 rounded-lg border px-3 py-2 text-left outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-64", index === currentIndex ? "border-primary bg-primary/10 ring-1 ring-primary/25" : index < currentIndex diff --git a/apps/web/src/components/diffs/DiffCommentAnnotation.tsx b/apps/web/src/components/diffs/DiffCommentAnnotation.tsx index d210732b6b60..4009fb0276ad 100644 --- a/apps/web/src/components/diffs/DiffCommentAnnotation.tsx +++ b/apps/web/src/components/diffs/DiffCommentAnnotation.tsx @@ -63,7 +63,7 @@ export function DiffCommentAnnotation({ aria-label="Delete comment" onClick={onDelete} > - + ) : null}
diff --git a/apps/web/src/components/preview/PreviewLocalServerCard.tsx b/apps/web/src/components/preview/PreviewLocalServerCard.tsx index 263cdb294f48..d19a6197a45f 100644 --- a/apps/web/src/components/preview/PreviewLocalServerCard.tsx +++ b/apps/web/src/components/preview/PreviewLocalServerCard.tsx @@ -15,7 +15,7 @@ export function PreviewLocalServerCard({ threadRef, server, onOpen }: Props) { diff --git a/apps/web/src/components/preview/PreviewView.tsx b/apps/web/src/components/preview/PreviewView.tsx index 5a828b863ced..084e4442475d 100644 --- a/apps/web/src/components/preview/PreviewView.tsx +++ b/apps/web/src/components/preview/PreviewView.tsx @@ -732,7 +732,7 @@ export function PreviewView({ /> ) : null} {controller !== "none" ? ( -
+
{controller === "agent" ? "Agent controlling browser" : "Human control"}
) : null} diff --git a/apps/web/src/components/preview/ThreadPreviewMiniPlayer.tsx b/apps/web/src/components/preview/ThreadPreviewMiniPlayer.tsx index 623928d102ef..f79065df32e9 100644 --- a/apps/web/src/components/preview/ThreadPreviewMiniPlayer.tsx +++ b/apps/web/src/components/preview/ThreadPreviewMiniPlayer.tsx @@ -338,7 +338,7 @@ export function ThreadPreviewMiniPlayer({ threadRef, tabId, bottomInset }: Props diff --git a/apps/web/src/components/settings/ProjectSettingsPanel.tsx b/apps/web/src/components/settings/ProjectSettingsPanel.tsx index 416df60c1290..fba59d2b92c3 100644 --- a/apps/web/src/components/settings/ProjectSettingsPanel.tsx +++ b/apps/web/src/components/settings/ProjectSettingsPanel.tsx @@ -110,6 +110,7 @@ import { SettingsSection, } from "./settingsLayout"; import { ProjectFaviconPickerDialog } from "./ProjectFaviconPickerDialog"; +import { ITEM_ROW_DESCRIPTION_CLASSNAME, ITEM_ROW_TITLE_CLASSNAME } from "./itemRows"; export const PROJECT_GROUPING_MODE_LABELS: Record = { repository: "Group by repository", @@ -936,14 +937,14 @@ function ProjectDetail({ group }: { group: SidebarProjectSnapshot }) { } > -
+
copyPathToClipboard(selectedCheckout.workspaceRoot, { @@ -1026,8 +1027,8 @@ function ProjectDetail({ group }: { group: SidebarProjectSnapshot }) { ) : null}
-

Actions

-

+

Actions

+

Saved and run only in {selectedCheckoutLabel}.

@@ -1081,7 +1082,7 @@ function ProjectDetail({ group }: { group: SidebarProjectSnapshot }) {
{scripts.length === 0 ? ( -

+

No actions configured for this checkout.

) : ( @@ -1093,7 +1094,7 @@ function ProjectDetail({ group }: { group: SidebarProjectSnapshot }) { return ( +
{titleIconNode} -

- {displayName} -

+

{displayName}

{String(instanceId) !== String(instance.driver) ? ( {instanceId} @@ -578,7 +577,9 @@ export function ProviderInstanceCard({ ); const authRowNode = ( -

+

{hasAuthenticatedEmail ? ( <> Authenticated as diff --git a/apps/web/src/components/settings/ProviderSettingsPanel.tsx b/apps/web/src/components/settings/ProviderSettingsPanel.tsx index 773463a3835c..6d27fdee7c21 100644 --- a/apps/web/src/components/settings/ProviderSettingsPanel.tsx +++ b/apps/web/src/components/settings/ProviderSettingsPanel.tsx @@ -238,7 +238,7 @@ export function ProviderSettingsPanel() { type="button" aria-pressed={selected} className={cn( - "flex min-w-0 items-center gap-3 rounded-xl px-3 py-2.5 text-left transition-colors sm:px-4", + "flex min-w-0 cursor-pointer items-center gap-3 rounded-xl px-3 py-3 text-left transition-colors sm:px-4", selected ? "bg-primary/8 ring-1 ring-primary/25 dark:bg-primary/12" : "hover:bg-muted/40", @@ -718,7 +718,7 @@ export function EnvironmentProviderSettings({ // threading a disabled flag through every control. inert={readOnly} aria-disabled={readOnly || undefined} - className={readOnly ? "space-y-1 opacity-50 select-none" : "space-y-1"} + className={readOnly ? "space-y-1 opacity-64 select-none" : "space-y-1"} > onSignal(process, "SIGINT")} > INT @@ -547,7 +547,7 @@ function ProcessActions({