Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions apps/web/src/components/ProjectScriptsControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -334,15 +334,16 @@ export default function ProjectScriptsControl({
<TooltipTrigger
render={
<Button
size="xs"
size="icon-xs"
variant="outline"
className="@3xl/header-actions:w-auto @3xl/header-actions:px-[calc(--spacing(2)-1px)]"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expanded script buttons stay cramped

Medium Severity

Switching these controls to icon-xs keeps sm:size-6 width while only adding @3xl/header-actions:w-auto. Depending on stylesheet order, the fixed width can win, so when labels unhide the button stays 24px wide and the text overflows. The override also omits the old xs text and gap sizing used by neighboring header actions.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b931602. Configure here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expanded script buttons lose compact typography

Medium Severity

Switching these controls to icon-xs keeps them square when compact, but the @3xl expansion only adds width and padding. icon-xs still inherits base gap-2 and text-base/sm:text-sm, so expanded labels no longer match neighboring xs header actions that keep gap-1 and text-sm/sm:text-xs.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b931602. Configure here.

aria-label={`Run ${primaryScript.name}`}
onClick={() => onRunScript(primaryScript)}
/>
}
>
<ScriptIcon icon={primaryScript.icon} />
<span className="sr-only @3xl/header-actions:not-sr-only @3xl/header-actions:ml-0.5">
<span className="hidden @3xl/header-actions:ml-0.5 @3xl/header-actions:inline">
{primaryScript.name}
</span>
</TooltipTrigger>
Expand Down Expand Up @@ -428,11 +429,17 @@ export default function ProjectScriptsControl({
<Tooltip>
<TooltipTrigger
render={
<Button size="xs" variant="outline" aria-label="Add action" onClick={openAddDialog} />
<Button
size="icon-xs"
variant="outline"
className="@3xl/header-actions:w-auto @3xl/header-actions:px-[calc(--spacing(2)-1px)]"
aria-label="Add action"
onClick={openAddDialog}
/>
}
>
<PlusIcon className="size-3.5" />
<span className="sr-only @3xl/header-actions:not-sr-only @3xl/header-actions:ml-0.5">
<span className="hidden @3xl/header-actions:ml-0.5 @3xl/header-actions:inline">
Add action
</span>
</TooltipTrigger>
Expand Down
20 changes: 10 additions & 10 deletions apps/web/src/components/SidebarV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -239,13 +239,13 @@ function SidebarV2ThreadTooltip({
side="right"
align="start"
sideOffset={8}
className="dropdown-glass max-w-80 border-0! text-left whitespace-normal shadow-lg/10 before:hidden dark:shadow-none"
className="dropdown-glass max-w-80 border-0! text-left whitespace-normal shadow-lg/10 before:hidden dark:shadow-none [&_[data-slot=tooltip-viewport]]:p-0"
style={{
background:
"color-mix(in srgb, var(--popover) 18%, color-mix(in srgb, var(--popover) var(--glass-opacity), transparent))",
}}
>
<div className="flex max-w-80 flex-col gap-2 p-2">
<div className="flex max-w-80 flex-col gap-2 p-[var(--floating-content-inset)]">
<div className="whitespace-nowrap text-sm font-medium text-foreground">{thread.title}</div>
<div className="grid gap-1.5 text-xs text-muted-foreground">
{projectTitle ? (
Expand Down Expand Up @@ -860,7 +860,7 @@ const SidebarV2Row = memo(function SidebarV2Row(props: {
/>
}
>
<div className="relative z-10 h-[4.875rem] px-2.5 py-2">
<div className="relative z-10 h-[4.875rem] px-[var(--sidebar-row-content-inset)] py-[var(--sidebar-row-content-inset)]">
<div className="flex h-5 min-w-0 items-center gap-1.5">
<ProjectFavicon
environmentId={thread.environmentId}
Expand Down Expand Up @@ -2215,7 +2215,7 @@ export default function SidebarV2() {
<>
<SidebarChromeHeader isElectron={isElectron} />
<SidebarContent className="gap-0">
<SidebarGroup className="px-2 pb-2 pt-3">
<SidebarGroup className="px-[var(--sidebar-content-inset)] pb-2 pt-3">
<div className="flex items-center gap-1">
<div className="min-w-0 flex-1">
<CommandDialogTrigger
Expand All @@ -2224,7 +2224,7 @@ export default function SidebarV2() {
size="sm"
type="button"
aria-label="Search threads and commands"
className="h-8 gap-2 rounded-md border-0 bg-transparent px-2 py-1.5 text-sm font-medium text-sidebar-muted-foreground hover:bg-sidebar-row-hover hover:text-sidebar-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-sidebar"
className="h-8 gap-[var(--sidebar-control-gap)] rounded-[var(--control-radius)] border-0 bg-transparent px-[var(--sidebar-content-inset)] py-1.5 text-sm font-medium text-sidebar-muted-foreground hover:bg-sidebar-row-hover hover:text-sidebar-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-sidebar"
data-testid="command-palette-trigger"
/>
}
Expand All @@ -2245,7 +2245,7 @@ export default function SidebarV2() {
<SidebarMenuButton
size="sm"
type="button"
className="relative size-8 justify-center rounded-md border-0 bg-transparent p-0 text-sidebar-muted-foreground hover:bg-sidebar-row-hover hover:text-sidebar-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-sidebar"
className="relative size-8 justify-center rounded-[var(--control-radius)] border-0 bg-transparent p-0 text-sidebar-muted-foreground hover:bg-sidebar-row-hover hover:text-sidebar-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-sidebar"
onClick={handleNewThreadClick}
disabled={projects.length === 0}
aria-label="New thread"
Expand All @@ -2266,12 +2266,12 @@ export default function SidebarV2() {
</div>
</SidebarGroup>
{projectGroups.length > 0 ? (
<SidebarGroup className="px-2 pb-2 pt-0">
<SidebarGroup className="px-[var(--sidebar-content-inset)] pb-2 pt-0">
<div className="flex items-center gap-1">
<Menu open={projectScopeMenuOpen} onOpenChange={setProjectScopeMenuOpen}>
<MenuTrigger
aria-label="Filter threads by project"
className="flex h-8 min-w-0 flex-1 cursor-pointer items-center gap-2 rounded-md px-2 text-left text-sm font-medium text-sidebar-muted-foreground outline-none hover:bg-sidebar-row-hover hover:text-sidebar-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-sidebar"
className="flex h-8 min-w-0 flex-1 cursor-pointer items-center gap-[var(--sidebar-control-gap)] rounded-[var(--control-radius)] px-[var(--sidebar-content-inset)] text-left text-sm font-medium text-sidebar-muted-foreground outline-none hover:bg-sidebar-row-hover hover:text-sidebar-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-sidebar"
>
{scopedProjectGroup ? (
<ProjectFavicon
Expand Down Expand Up @@ -2340,7 +2340,7 @@ export default function SidebarV2() {
render={
<SidebarMenuButton
size="sm"
className="relative size-8 shrink-0 justify-center rounded-md bg-transparent p-0 text-sidebar-muted-foreground hover:bg-sidebar-row-hover hover:text-sidebar-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-sidebar"
className="relative size-8 shrink-0 justify-center rounded-[var(--control-radius)] bg-transparent p-0 text-sidebar-muted-foreground hover:bg-sidebar-row-hover hover:text-sidebar-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-sidebar"
onClick={openAddProjectCommandPalette}
type="button"
aria-label="New project"
Expand All @@ -2358,7 +2358,7 @@ export default function SidebarV2() {
</div>
</SidebarGroup>
) : null}
<SidebarGroup className="min-h-0 flex-1 overflow-y-auto px-2 py-1 [scrollbar-gutter:stable]">
<SidebarGroup className="min-h-0 flex-1 overflow-y-auto px-[var(--sidebar-content-inset)] py-1">
<TooltipProvider
key="sidebar-thread-tooltips-150"
delay={150}
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/clerk/T3ConnectSidebarSignIn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ function ConfiguredT3ConnectSidebarSignIn() {
<SidebarMenuItem>
<SidebarMenuButton
size="sm"
className="h-8 items-center gap-2 rounded-md px-2 py-1.5 text-sm font-medium text-sidebar-muted-foreground/80 hover:bg-sidebar-row-hover hover:text-sidebar-foreground"
className="h-8 items-center gap-[var(--sidebar-control-gap)] rounded-[var(--control-radius)] px-[var(--sidebar-content-inset)] py-1.5 text-sm font-medium text-sidebar-muted-foreground/80 hover:bg-sidebar-row-hover hover:text-sidebar-foreground"
onClick={openAuthPrompt}
>
<LogInIcon className="size-4 shrink-0" />
Expand Down
10 changes: 5 additions & 5 deletions apps/web/src/components/settings/SettingsSidebarNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export function SettingsSidebarNav({ pathname }: { pathname: string }) {
return (
<>
<SidebarContent className="overflow-x-hidden">
<SidebarGroup className="px-2 py-3">
<SidebarGroup className="px-[var(--sidebar-content-inset)] py-3">
<SidebarMenu>
{SETTINGS_NAV_ITEMS.map((item) => {
const Icon = item.icon;
Expand All @@ -93,7 +93,7 @@ export function SettingsSidebarNav({ pathname }: { pathname: string }) {
className={
isActive
? "size-4 shrink-0 text-sidebar-foreground"
: "size-4 shrink-0 text-sidebar-muted-foreground/60"
: "size-4 shrink-0 text-[var(--sidebar-icon-color)]"
}
/>
<span className="truncate">{item.label}</span>
Expand All @@ -104,10 +104,10 @@ export function SettingsSidebarNav({ pathname }: { pathname: string }) {
</SidebarMenu>
</SidebarGroup>
</SidebarContent>
<SidebarFooter className="p-2">
<SidebarFooter className="p-[var(--sidebar-content-inset)]">
<T3ConnectSidebarSignIn />
<div className="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-1">
<SidebarMenu className="min-w-0">
<div className="flex items-center gap-1">
<SidebarMenu className="min-w-0 flex-1">
<SidebarMenuItem>
<SidebarMenuButton
size="sm"
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/sidebar/SidebarChrome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export const SidebarChromeFooter = memo(function SidebarChromeFooter() {
}, [isMobile, navigate, setOpenMobile]);

return (
<SidebarFooter className="p-2">
<SidebarFooter className="p-[var(--sidebar-content-inset)]">
<SidebarProviderUpdatePill />
<SidebarUpdatePill />
<SidebarMenu>
Expand Down
31 changes: 31 additions & 0 deletions apps/web/src/components/ui/button.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { renderToStaticMarkup } from "react-dom/server";
import { describe, expect, it } from "vite-plus/test";
import { FlaskConicalIcon } from "lucide-react";

import { Button } from "./button";

describe("button geometry tokens", () => {
it("uses the shared control radius and an opaque semantic icon color", () => {
const html = renderToStaticMarkup(
<Button size="icon-xs" variant="outline" aria-label="Run tests">
<FlaskConicalIcon />
</Button>,
);

expect(html).toContain("rounded-[var(--control-radius)]");
expect(html).toContain("[--control-icon-color:var(--muted-foreground)]");
expect(html).toContain("text-[var(--control-icon-color)]");
expect(html).not.toContain("opacity-80");
});

it("keeps compact icon buttons square at every breakpoint", () => {
const html = renderToStaticMarkup(
<Button size="icon-xs" aria-label="Add action">
<span>+</span>
</Button>,
);

expect(html).toContain("size-7");
expect(html).toContain("sm:size-6");
});
});
10 changes: 5 additions & 5 deletions apps/web/src/components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type * as React from "react";
import { cn } from "~/lib/utils";

const buttonVariants = cva(
"[&_svg]:-mx-0.5 relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-lg border font-medium text-base outline-none transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 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 sm:text-sm [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
"[--control-icon-color:currentColor] [&_svg]:-mx-0.5 relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-[var(--control-radius)] border font-medium text-base outline-none transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--control-radius)-1px)] pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 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 sm:text-sm [&_svg:not([class*='text-'])]:text-[var(--control-icon-color)] [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
{
defaultVariants: {
size: "default",
Expand All @@ -23,11 +23,11 @@ const buttonVariants = cva(
"icon-xl":
"size-11 sm:size-10 [&_svg:not([class*='size-'])]:size-5 sm:[&_svg:not([class*='size-'])]:size-4.5",
"icon-xs":
"size-7 rounded-md before:rounded-[calc(var(--radius-md)-1px)] sm:size-6 not-in-data-[slot=input-group]:[&_svg:not([class*='size-'])]:size-4 sm:not-in-data-[slot=input-group]:[&_svg:not([class*='size-'])]:size-3.5",
"size-7 sm:size-6 not-in-data-[slot=input-group]:[&_svg:not([class*='size-'])]:size-4 sm:not-in-data-[slot=input-group]:[&_svg:not([class*='size-'])]:size-3.5",
lg: "h-10 px-[calc(--spacing(3.5)-1px)] sm:h-9",
sm: "h-8 gap-1.5 px-[calc(--spacing(2.5)-1px)] sm:h-7",
xl: "h-11 px-[calc(--spacing(4)-1px)] text-lg sm:h-10 sm:text-base [&_svg:not([class*='size-'])]:size-5 sm:[&_svg:not([class*='size-'])]:size-4.5",
xs: "h-7 gap-1 rounded-md px-[calc(--spacing(2)-1px)] text-sm before:rounded-[calc(var(--radius-md)-1px)] sm:h-6 sm:text-xs [&_svg:not([class*='size-'])]:size-4 sm:[&_svg:not([class*='size-'])]:size-3.5",
xs: "h-7 gap-1 px-[calc(--spacing(2)-1px)] text-sm sm:h-6 sm:text-xs [&_svg:not([class*='size-'])]:size-4 sm:[&_svg:not([class*='size-'])]:size-3.5",
},
variant: {
default:
Expand All @@ -37,10 +37,10 @@ const buttonVariants = cva(
"destructive-outline":
"border-input bg-popover not-dark:bg-clip-padding text-destructive-foreground shadow-xs/5 not-disabled:not-active:not-data-pressed:before:shadow-[0_1px_--theme(--color-black/4%)] dark:bg-input/32 dark:not-disabled:before:shadow-[0_-1px_--theme(--color-white/2%)] dark:not-disabled:not-active:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/6%)] [:disabled,:active,[data-pressed]]:shadow-none [:hover,[data-pressed]]:border-destructive/32 [:hover,[data-pressed]]:bg-destructive/4",
ghost:
"border-transparent text-foreground data-pressed:bg-accent [:hover,[data-pressed]]:bg-accent [&_svg:not([class*='text-'])]:text-muted-foreground",
"[--control-icon-color:var(--muted-foreground)] border-transparent text-foreground data-pressed:bg-accent [:hover,[data-pressed]]:bg-accent",
link: "border-transparent underline-offset-4 [:hover,[data-pressed]]:underline",
outline:
"border-input bg-popover not-dark:bg-clip-padding text-foreground shadow-xs/5 not-disabled:not-active:not-data-pressed:before:shadow-[0_1px_--theme(--color-black/4%)] dark:bg-input/32 dark:not-disabled:before:shadow-[0_-1px_--theme(--color-white/2%)] dark:not-disabled:not-active:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/6%)] [:disabled,:active,[data-pressed]]:shadow-none [:hover,[data-pressed]]:bg-accent/50 dark:[:hover,[data-pressed]]:bg-input/64 [&_svg:not([class*='text-'])]:text-muted-foreground",
"[--control-icon-color:var(--muted-foreground)] border-input bg-popover not-dark:bg-clip-padding text-foreground shadow-xs/5 not-disabled:not-active:not-data-pressed:before:shadow-[0_1px_--theme(--color-black/4%)] dark:bg-input/32 dark:not-disabled:before:shadow-[0_-1px_--theme(--color-white/2%)] dark:not-disabled:not-active:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/6%)] [:disabled,:active,[data-pressed]]:shadow-none [:hover,[data-pressed]]:bg-accent/50 dark:[:hover,[data-pressed]]:bg-input/64",
secondary:
"border-transparent bg-secondary text-secondary-foreground [:active,[data-pressed]]:bg-secondary/80 [:hover,[data-pressed]]:bg-secondary/90",
},
Expand Down
9 changes: 7 additions & 2 deletions apps/web/src/components/ui/command.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,12 @@ function CommandInput({
wrapperClassName?: string | undefined;
}) {
return (
<div className={cn("px-2.5 py-1.5", wrapperClassName)}>
<div
className={cn(
"px-[var(--command-shell-inset)] py-1.5 [&_[data-slot=autocomplete-start-addon]]:ps-[calc(var(--command-shell-inset)+0.0625rem)] sm:*:data-[slot=autocomplete-input]:ps-[calc(var(--command-shell-inset)+1.5rem)]",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Command input padding never applies

Medium Severity

The CommandInput's text padding is incorrect. The sm:*:data-[slot=autocomplete-input] selector fails to apply the intended ps rule because data-slot=autocomplete-input is a nested element, not a direct child. This results in the input text using hardcoded spacing instead of --command-shell-inset.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b931602. Configure here.

wrapperClassName,
)}
>
<AutocompleteInput
autoFocus
className={cn(
Expand Down Expand Up @@ -211,7 +216,7 @@ function CommandFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
className={cn(
"relative flex items-center justify-between gap-2 rounded-b-[calc(var(--radius-2xl)-1px)] bg-foreground/[0.025] px-5 py-3 font-medium text-sm text-muted-foreground [&_[data-slot=kbd-group]]:font-sans [&_[data-slot=kbd]]:bg-foreground/[0.08] [&_[data-slot=kbd]]:text-foreground [&_[data-slot=kbd]]:ring-0",
"relative flex items-center justify-between gap-2 rounded-b-[calc(var(--radius-2xl)-1px)] bg-foreground/[0.025] px-[var(--command-content-inset)] py-3 font-medium text-sm text-muted-foreground [&_[data-slot=kbd-group]]:font-sans [&_[data-slot=kbd]]:bg-foreground/[0.08] [&_[data-slot=kbd]]:text-foreground [&_[data-slot=kbd]]:ring-0",
className,
)}
data-slot="command-footer"
Expand Down
3 changes: 3 additions & 0 deletions apps/web/src/components/ui/sidebar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ describe("sidebar interactive cursors", () => {

expect(html).toContain('data-slot="sidebar-menu-button"');
expect(html).toContain("cursor-pointer");
expect(html).toContain("rounded-[var(--control-radius)]");
expect(html).toContain("gap-[var(--sidebar-control-gap)]");
expect(html).toContain("p-[var(--sidebar-content-inset)]");
});

it("lets project drag handles override the default pointer cursor", () => {
Expand Down
Loading
Loading