diff --git a/apps/web/public/mockServiceWorker.js b/apps/web/public/mockServiceWorker.js
index 85e901012..daa58d0f1 100644
--- a/apps/web/public/mockServiceWorker.js
+++ b/apps/web/public/mockServiceWorker.js
@@ -7,7 +7,7 @@
* - Please do NOT modify this file.
*/
-const PACKAGE_VERSION = '2.12.9'
+const PACKAGE_VERSION = '2.12.10'
const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82'
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
const activeClientIds = new Set()
diff --git a/apps/web/src/components/Sidebar.logic.ts b/apps/web/src/components/Sidebar.logic.ts
index f08ed212a..d48873f7a 100644
--- a/apps/web/src/components/Sidebar.logic.ts
+++ b/apps/web/src/components/Sidebar.logic.ts
@@ -51,7 +51,7 @@ export function resolveThreadRowClassName(input: {
isSelected: boolean;
}): string {
const baseClassName =
- "h-7 w-full translate-x-0 cursor-default justify-start px-2 text-left select-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring";
+ "h-7 w-full translate-x-0 cursor-pointer justify-start px-2 text-left select-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring";
if (input.isSelected && input.isActive) {
return cn(
diff --git a/apps/web/src/components/ui/sidebar.test.tsx b/apps/web/src/components/ui/sidebar.test.tsx
new file mode 100644
index 000000000..124649236
--- /dev/null
+++ b/apps/web/src/components/ui/sidebar.test.tsx
@@ -0,0 +1,53 @@
+import { renderToStaticMarkup } from "react-dom/server";
+import { describe, expect, it } from "vitest";
+
+import {
+ SidebarMenuAction,
+ SidebarMenuButton,
+ SidebarMenuSubButton,
+ SidebarProvider,
+} from "./sidebar";
+
+function renderSidebarButton(className?: string) {
+ return renderToStaticMarkup(
+
+ Projects
+ ,
+ );
+}
+
+describe("sidebar interactive cursors", () => {
+ it("uses a pointer cursor for menu buttons by default", () => {
+ const html = renderSidebarButton();
+
+ expect(html).toContain('data-slot="sidebar-menu-button"');
+ expect(html).toContain("cursor-pointer");
+ });
+
+ it("lets project drag handles override the default pointer cursor", () => {
+ const html = renderSidebarButton("cursor-grab");
+
+ expect(html).toContain("cursor-grab");
+ expect(html).not.toContain("cursor-pointer");
+ });
+
+ it("uses a pointer cursor for menu actions", () => {
+ const html = renderToStaticMarkup(
+
+ +
+ ,
+ );
+
+ expect(html).toContain('data-slot="sidebar-menu-action"');
+ expect(html).toContain("cursor-pointer");
+ });
+
+ it("uses a pointer cursor for submenu buttons", () => {
+ const html = renderToStaticMarkup(
+ }>Show more,
+ );
+
+ expect(html).toContain('data-slot="sidebar-menu-sub-button"');
+ expect(html).toContain("cursor-pointer");
+ });
+});
diff --git a/apps/web/src/components/ui/sidebar.tsx b/apps/web/src/components/ui/sidebar.tsx
index 11ab792cc..cfa29e950 100644
--- a/apps/web/src/components/ui/sidebar.tsx
+++ b/apps/web/src/components/ui/sidebar.tsx
@@ -750,7 +750,7 @@ function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">) {
}
const sidebarMenuButtonVariants = cva(
- "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-lg p-2 text-left text-sm outline-hidden ring-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pe-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg:not([class*='size-'])]:size-4 [&>svg]:shrink-0",
+ "peer/menu-button flex w-full cursor-pointer items-center gap-2 overflow-hidden rounded-lg p-2 text-left text-sm outline-hidden ring-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pe-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg:not([class*='size-'])]:size-4 [&>svg]:shrink-0",
{
defaultVariants: {
size: "default",
@@ -834,7 +834,7 @@ function SidebarMenuAction({
}) {
const defaultProps = {
className: cn(
- "absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-lg p-0 text-sidebar-foreground outline-hidden ring-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 peer-hover/menu-button:text-sidebar-accent-foreground [&>svg:not([class*='size-'])]:size-4 [&>svg]:shrink-0",
+ "absolute top-1.5 right-1 flex aspect-square w-5 cursor-pointer items-center justify-center rounded-lg p-0 text-sidebar-foreground outline-hidden ring-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 peer-hover/menu-button:text-sidebar-accent-foreground [&>svg:not([class*='size-'])]:size-4 [&>svg]:shrink-0",
// Increases the hit area of the button on mobile.
"after:-inset-2 after:absolute md:after:hidden",
"peer-data-[size=sm]/menu-button:top-1",
@@ -946,7 +946,7 @@ function SidebarMenuSubButton({
}) {
const defaultProps = {
className: cn(
- "-translate-x-px flex h-7 min-w-0 items-center gap-2 overflow-hidden rounded-lg px-2 text-sidebar-foreground outline-hidden ring-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg:not([class*='size-'])]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground",
+ "-translate-x-px flex h-7 min-w-0 cursor-pointer items-center gap-2 overflow-hidden rounded-lg px-2 text-sidebar-foreground outline-hidden ring-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg:not([class*='size-'])]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground",
"data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground",
size === "sm" && "text-xs",
size === "md" && "text-sm",