Fix/tabs overflow scrolling#253
Open
shiva-manu wants to merge 2 commits into
Open
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR enhances the UI tab bar scrolling experience and adds support for pasting clipboard images into the terminal by saving them to a temporary file via a new Tauri backend command.
Changes:
- Add a custom (hidden native) tab scroller style and a draggable scrollbar thumb for the
TabBar. - Add terminal “paste image” handling that writes clipboard images to disk and inserts the path into the terminal.
- Introduce a new Tauri command (
fs_write_clipboard_image) to persist clipboard images in a temp directory.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| src/styles/globals.css | Adds .terax-tab-scroll class to hide native scrollbars and tweak scrolling behavior. |
| src/modules/tabs/TabBar.tsx | Implements custom scroll thumb + updated wheel/scrollIntoView behavior for tab strip. |
| src/modules/header/Header.tsx | Removes data-tauri-drag-region on header containers (affects window dragging). |
| src/modules/terminal/lib/useTerminalSession.ts | Binds a paste listener to detect/save clipboard images and write their path into the pty. |
| src/modules/terminal/lib/clipboard-image.ts | Adds helpers to extract clipboard images, save via Tauri invoke, and format terminal path mentions. |
| src-tauri/src/modules/fs/file.rs | Adds fs_write_clipboard_image command to write bytes to temp directory + mime-to-extension mapping. |
| src-tauri/src/lib.rs | Registers the new Tauri command. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| observer.disconnect(); | ||
| el.removeEventListener("scroll", updateScrollThumb); | ||
| }; | ||
| }, [tabs.length, updateScrollThumb]); |
Comment on lines
91
to
97
| const onWheel = (e: WheelEvent) => { | ||
| if (Math.abs(e.deltaY) <= Math.abs(e.deltaX)) return; | ||
| if (el.scrollWidth <= el.clientWidth) return; | ||
| const delta = | ||
| Math.abs(e.deltaX) > Math.abs(e.deltaY) ? e.deltaX : e.deltaY; | ||
| e.preventDefault(); | ||
| el.scrollLeft += e.deltaY; | ||
| el.scrollLeft += delta; | ||
| }; |
Comment on lines
+292
to
+310
| <div | ||
| ref={trackRef} | ||
| aria-hidden="true" | ||
| className={cn( | ||
| "pointer-events-none absolute inset-x-0 bottom-0 h-1 opacity-0 transition-opacity", | ||
| scrollThumb.visible && "opacity-100", | ||
| )} | ||
| > | ||
| <div | ||
| className="pointer-events-auto flex h-2 cursor-ew-resize items-end pb-0.5" | ||
| style={{ | ||
| width: scrollThumb.width, | ||
| transform: `translateX(${scrollThumb.left}px)`, | ||
| }} | ||
| onPointerDown={onScrollbarPointerDown} | ||
| onPointerMove={onScrollbarPointerMove} | ||
| onPointerUp={onScrollbarPointerUp} | ||
| onPointerCancel={onScrollbarPointerUp} | ||
| > |
Comment on lines
132
to
136
| <div | ||
| ref={rootRef} | ||
| data-tauri-drag-region | ||
| className={`flex h-10 shrink-0 items-center gap-2 border-b border-border/60 bg-card select-none ${ | ||
| IS_MAC ? "pr-2 pl-20" : "pr-0 pl-2" | ||
| }`} |
Comment on lines
+21
to
+29
| export async function saveClipboardImage(file: File): Promise<string> { | ||
| const bytes = Array.from(new Uint8Array(await file.arrayBuffer())); | ||
| const result = await invoke<TempImage>("fs_write_clipboard_image", { | ||
| bytes, | ||
| mime: file.type || "image/png", | ||
| workspace: currentWorkspaceEnv(), | ||
| }); | ||
| return result.path; | ||
| } |
Comment on lines
+142
to
+146
| let millis = SystemTime::now() | ||
| .duration_since(UNIX_EPOCH) | ||
| .map_err(|e| e.to_string())? | ||
| .as_millis(); | ||
| let name = format!("clipboard-{millis}.{ext}"); |
Comment on lines
+131
to
+138
| pub fn fs_write_clipboard_image( | ||
| bytes: Vec<u8>, | ||
| mime: String, | ||
| workspace: Option<WorkspaceEnv>, | ||
| ) -> Result<TempImage, String> { | ||
| if bytes.is_empty() { | ||
| return Err("clipboard image is empty".to_string()); | ||
| } |
ba0067f to
8e6132b
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
Comments suppressed due to low confidence (1)
src/modules/tabs/TabBar.tsx:1
updateScrollThumbsets React state on everyscrollevent, which can cause frequent re-renders and jank during smooth/trackpad scrolling. Consider throttling the state update torequestAnimationFrame, or updating thumb position via a ref/CSS variable (and only togglingvisible/widthin state) to reduce render frequency under continuous scroll.
import { Button } from "@/components/ui/button";
Comment on lines
132
to
136
| <div | ||
| ref={rootRef} | ||
| data-tauri-drag-region | ||
| className={`flex h-10 shrink-0 items-center gap-2 border-b border-border/60 bg-card select-none ${ | ||
| IS_MAC ? "pr-2 pl-20" : "pr-0 pl-2" | ||
| }`} |
| className="flex min-w-0 flex-1 items-center gap-2" | ||
| data-tauri-drag-region | ||
| > | ||
| <div className="flex min-w-0 flex-1 items-center gap-2"> |
| const track = trackRef.current; | ||
| if (!el || !track || el.scrollWidth <= el.clientWidth) { | ||
| setScrollThumb((current) => | ||
| current.visible ? { ...current, left: 0, visible: false } : current, |
Comment on lines
+348
to
+353
| aria-label="Scroll tabs" | ||
| aria-controls="terax-tab-scroll" | ||
| aria-orientation="horizontal" | ||
| aria-valuemin={0} | ||
| aria-valuemax={Math.round(scrollThumb.maxScrollLeft)} | ||
| aria-valuenow={Math.round(scrollThumb.scrollLeft)} |
| observer.disconnect(); | ||
| el.removeEventListener("scroll", updateScrollThumb); | ||
| }; | ||
| }, [tabs, updateScrollThumb]); |
Contributor
Author
|
@crynta look into this pr. I added the scroll bar for the tabs |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds horizontal overflow support for the tab strip when many terminal/editor tabs are open.
Replaces the long native scrollbar with a small draggable scroll thumb and smooth tab
scrolling.
Why
When multiple tabs were opened, the first tabs could overflow out of view and become hard to
access. The previous scrollbar behavior was either hidden or too visually large for the
compact header.
How
The tab bar now uses a hidden native scroll container with wheel/trackpad support and a
custom capped scroll thumb. The tab area no longer uses the Tauri drag region, so dragging
the scroll thumb scrolls tabs instead of moving the window.
Testing
pnpm exec tsc --noEmitcleansrc-tauri/)cargo checkcleanpnpm tauri devManual smoke-test:
Opened many tabs until the tab strip overflowed, verified horizontal scrolling works, the
active tab scrolls into view smoothly, and the custom thumb remains small and draggable.
Screenshots / GIFs
Before: tab overflow was difficult to access and the visible scrollbar appeared as a long
underline across many tabs.
Screencast.from.2026-05-14.22-26-58.mp4
After: tab overflow is scrollable with a compact draggable thumb around one tab wide.
Screencast.from.2026-05-14.23-35-29.mp4
Notes for reviewer
This is a focused UI change in the React tab/header area. No
src-tauri/files weretouched.
#251