From 88e5082a9eaaf6cbceae31f3c02e37bae7d9b7b9 Mon Sep 17 00:00:00 2001 From: brennanb2025 Date: Fri, 24 Apr 2026 18:21:09 -0700 Subject: [PATCH] fix(tabs): clean up workspace chrome seams Remove the duplicated 1px border below the drag strip where the rightmost split pane's border-r stacked next to the right sidebar's borderLeft. Extend the left-sidebar seam through the drag strip when splits exist so it reaches y=0, matching the full-height right-sidebar line. --- src/renderer/src/App.tsx | 21 +---------------- .../src/components/tab-bar/BrowserTab.tsx | 2 +- .../src/components/tab-bar/EditorFileTab.tsx | 2 +- .../src/components/tab-bar/SortableTab.tsx | 2 +- .../src/components/tab-bar/TabBar.tsx | 12 ++++++---- .../src/components/tab-bar/drop-indicator.ts | 6 ++++- .../components/tab-group/TabGroupPanel.tsx | 23 ++++++++++++++++++- .../tab-group/TabGroupSplitLayout.tsx | 14 ++++++++--- 8 files changed, 49 insertions(+), 33 deletions(-) diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index 896045aa8f5..82558dc9354 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -961,26 +961,7 @@ function App(): React.JSX.Element { filesystem watchers + cached directory trees survive across open/close toggles. Unmount on the tasks view since that surface is intentionally distraction-free. */} - {showRightSidebarControls ? ( - workspaceActive ? ( - /* Why: in workspace view the right sidebar must start at the - tab row, not the window edge, so the 8px drag strip that - runs across the top of the center column continues uninterrupted - over the right sidebar. Wrapping adds a matching 8px drag - surface above and shrinks the sidebar to fill the remainder. */ -
-
-
- -
-
- ) : ( - - ) - ) : null} + {showRightSidebarControls ? : null}
{/* Why: NewWorkspaceComposerCard renders Radix s that crash diff --git a/src/renderer/src/components/tab-bar/BrowserTab.tsx b/src/renderer/src/components/tab-bar/BrowserTab.tsx index 1b2ad8ff420..8c2e62b752a 100644 --- a/src/renderer/src/components/tab-bar/BrowserTab.tsx +++ b/src/renderer/src/components/tab-bar/BrowserTab.tsx @@ -111,7 +111,7 @@ export default function BrowserTab({ ref={setNodeRef} {...attributes} {...listeners} - className={`group relative flex items-center h-full px-1.5 text-xs cursor-pointer select-none shrink-0 border-t ${hasTabsToRight ? 'border-r' : ''} border-border bg-card ${getDropIndicatorClasses(dropIndicator ?? null)} ${ + className={`group relative flex items-center h-full px-1.5 text-xs cursor-pointer select-none shrink-0 outline-none focus:outline-none focus-visible:outline-none border-t ${hasTabsToRight ? 'border-r' : ''} border-border bg-card ${getDropIndicatorClasses(dropIndicator ?? null)} ${ isActive ? 'text-foreground' : 'text-muted-foreground hover:text-foreground' }`} onPointerDown={(e) => { diff --git a/src/renderer/src/components/tab-bar/EditorFileTab.tsx b/src/renderer/src/components/tab-bar/EditorFileTab.tsx index e8b2d63281d..9095dbbdef3 100644 --- a/src/renderer/src/components/tab-bar/EditorFileTab.tsx +++ b/src/renderer/src/components/tab-bar/EditorFileTab.tsx @@ -175,7 +175,7 @@ export default function EditorFileTab({ ref={setNodeRef} {...attributes} {...listeners} - className={`group relative flex items-center h-full px-1.5 text-xs cursor-pointer select-none shrink-0 border-t ${hasTabsToRight ? 'border-r' : ''} border-border bg-card ${getDropIndicatorClasses(dropIndicator ?? null)} ${ + className={`group relative flex items-center h-full px-1.5 text-xs cursor-pointer select-none shrink-0 outline-none focus:outline-none focus-visible:outline-none border-t ${hasTabsToRight ? 'border-r' : ''} border-border bg-card ${getDropIndicatorClasses(dropIndicator ?? null)} ${ isActive ? 'text-foreground' : 'text-muted-foreground hover:text-foreground' }`} onPointerDown={(e) => { diff --git a/src/renderer/src/components/tab-bar/SortableTab.tsx b/src/renderer/src/components/tab-bar/SortableTab.tsx index 4dbaa3ea953..38193421ce6 100644 --- a/src/renderer/src/components/tab-bar/SortableTab.tsx +++ b/src/renderer/src/components/tab-bar/SortableTab.tsx @@ -179,7 +179,7 @@ export default function SortableTab({ // tab still reads as "selected + has activity". The wash is // rendered as an absolutely-positioned child below so the ::after // pseudo-element stays free for the drop indicator. - className={`group relative flex items-center h-full px-1.5 text-xs cursor-pointer select-none shrink-0 border-t ${hasTabsToRight ? 'border-r' : ''} border-border bg-card ${getDropIndicatorClasses(dropIndicator ?? null)} ${ + className={`group relative flex items-center h-full px-1.5 text-xs cursor-pointer select-none shrink-0 outline-none focus:outline-none focus-visible:outline-none border-t ${hasTabsToRight ? 'border-r' : ''} border-border bg-card ${getDropIndicatorClasses(dropIndicator ?? null)} ${ isActive ? 'text-foreground' : 'text-muted-foreground hover:text-foreground' }`} onDoubleClick={(e) => { diff --git a/src/renderer/src/components/tab-bar/TabBar.tsx b/src/renderer/src/components/tab-bar/TabBar.tsx index cd170bb998d..29bebfcfe3b 100644 --- a/src/renderer/src/components/tab-bar/TabBar.tsx +++ b/src/renderer/src/components/tab-bar/TabBar.tsx @@ -337,11 +337,13 @@ function TabBarInner({ "+" button remains window-draggable. */}
diff --git a/src/renderer/src/components/tab-bar/drop-indicator.ts b/src/renderer/src/components/tab-bar/drop-indicator.ts index 64d7725085c..a755015eea0 100644 --- a/src/renderer/src/components/tab-bar/drop-indicator.ts +++ b/src/renderer/src/components/tab-bar/drop-indicator.ts @@ -19,6 +19,10 @@ export function getDropIndicatorClasses(dropIndicator: DropIndicator): string { // positioned with z-10 so it overlays the tab chrome without shifting layout // and without conflicting with drop-indicator pseudo-elements during a drag. // `-top-px` pulls it onto the tab's own 1px top border so the blue bar -// REPLACES the faint gray line rather than stacking below it. +// REPLACES the faint gray line rather than stacking below it. Horizontal +// inset is 0 (not -1px): negative insets on the last tab bleed into the +// strip's scrollWidth, so clicking between active tabs flips the strip +// between "fits exactly" and "overflows by 1px", which jitters every tab by +// 1px because the browser preserves scrollLeft near the end. export const ACTIVE_TAB_INDICATOR_CLASSES = 'pointer-events-none absolute inset-x-0 -top-px h-px bg-[#1e3d9c] z-10' diff --git a/src/renderer/src/components/tab-group/TabGroupPanel.tsx b/src/renderer/src/components/tab-group/TabGroupPanel.tsx index 0c3fa0cc43e..d8888e1603c 100644 --- a/src/renderer/src/components/tab-group/TabGroupPanel.tsx +++ b/src/renderer/src/components/tab-group/TabGroupPanel.tsx @@ -28,6 +28,7 @@ export default function TabGroupPanel({ isWorktreeActive, isFocused, hasSplitGroups, + touchesRightEdge, reserveClosedExplorerToggleSpace, reserveCollapsedSidebarHeaderSpace, isTabDragActive = false, @@ -39,6 +40,7 @@ export default function TabGroupPanel({ isWorktreeActive: boolean isFocused: boolean hasSplitGroups: boolean + touchesRightEdge: boolean reserveClosedExplorerToggleSpace: boolean reserveCollapsedSidebarHeaderSpace: boolean isTabDragActive?: boolean @@ -176,8 +178,27 @@ export default function TabGroupPanel({ return (
@@ -232,7 +240,7 @@ export default function TabGroupSplitLayout({ worktreeId={worktreeId} focusedGroupId={focusedGroupId} isWorktreeActive={isWorktreeActive} - hasSplitGroups={layout.type === 'split'} + hasSplitGroups={hasSplits} touchesTopEdge={true} touchesRightEdge={true} touchesLeftEdge={true}