From 3e7fb5abae0b2c74e09b8365020d57e1e870e240 Mon Sep 17 00:00:00 2001 From: Radu Date: Thu, 6 Aug 2026 20:35:09 +0300 Subject: [PATCH] fix(web): restore horizontal scrolling in right panel tabs c2d44a31 moved `drag-region` off the right panel topbar and onto the tab strip's ScrollArea. `-webkit-app-region: drag` swallows pointer and wheel events, so in the desktop app the tab strip stopped scrolling horizontally once the tabs overflowed, leaving later tabs unreachable. Put the drag region back on the topbar container and mark the scroll area no-drag, matching how DiffPanelShell and the chat header already separate the drag surface from interactive children. Browser builds are unaffected: both classes only apply when Electron owns the title bar. --- .../src/components/RightPanelTabs.test.tsx | 52 +++++++++++++++++++ apps/web/src/components/RightPanelTabs.tsx | 8 ++- 2 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 apps/web/src/components/RightPanelTabs.test.tsx diff --git a/apps/web/src/components/RightPanelTabs.test.tsx b/apps/web/src/components/RightPanelTabs.test.tsx new file mode 100644 index 00000000000..0777f7b5595 --- /dev/null +++ b/apps/web/src/components/RightPanelTabs.test.tsx @@ -0,0 +1,52 @@ +import { renderToStaticMarkup } from "react-dom/server"; +import { describe, expect, it, vi } from "vite-plus/test"; + +import { RightPanelTabs } from "./RightPanelTabs"; + +vi.mock("~/env", () => ({ isElectron: true })); + +/** The opening tag carrying `attribute`, so class assertions pin the element, not attribute order. */ +function tagWith(markup: string, attribute: string): string { + const index = markup.indexOf(attribute); + if (index === -1) throw new Error(`no element carries ${attribute}`); + return markup.slice(markup.lastIndexOf("<", index), markup.indexOf(">", index) + 1); +} + +describe("RightPanelTabs", () => { + it("keeps the Electron titlebar draggable without swallowing tab strip scrolling", () => { + const markup = renderToStaticMarkup( + +
+ , + ); + + expect(tagWith(markup, "data-right-panel-tabbar")).toContain("drag-region"); + + // -webkit-app-region: drag swallows pointer and wheel events, so the scroll + // container itself must opt out or the tab strip cannot be scrolled. + const tabList = tagWith(markup, "data-right-panel-tab-list"); + expect(tabList).toContain("[-webkit-app-region:no-drag]"); + expect(tabList).not.toContain("drag-region"); + }); +}); diff --git a/apps/web/src/components/RightPanelTabs.tsx b/apps/web/src/components/RightPanelTabs.tsx index b0f18f6e126..611266c7126 100644 --- a/apps/web/src/components/RightPanelTabs.tsx +++ b/apps/web/src/components/RightPanelTabs.tsx @@ -374,7 +374,8 @@ export function RightPanelTabs(props: RightPanelTabsProps) { "workspace-topbar gap-1 pl-2", props.mode !== "inline" && "[--workspace-topbar-height:--spacing(11)]", props.mode === "inline" ? "pr-28" : "pr-3", - ownsDesktopTitleBar && "wco:pr-[calc(var(--workspace-native-controls-inset)+6rem)]", + ownsDesktopTitleBar && + "drag-region wco:pr-[calc(var(--workspace-native-controls-inset)+6rem)]", props.mode === "inline" && props.maximized && COLLAPSED_SIDEBAR_TITLEBAR_INSET_CLASS, )} data-right-panel-tabbar @@ -383,7 +384,10 @@ export function RightPanelTabs(props: RightPanelTabsProps) { ref={tabListRef} hideScrollbars scrollFade - className={cn("min-w-0 flex-1 rounded-none", ownsDesktopTitleBar && "drag-region")} + className={cn( + "min-w-0 flex-1 rounded-none", + ownsDesktopTitleBar && "[-webkit-app-region:no-drag]", + )} data-right-panel-tab-list >