Skip to content

Tab strip: swap the dirty dot for close, fade only real overflow - #57

Merged
BunsDev merged 1 commit into
mainfrom
feat/tab-strip-polish
Aug 10, 2026
Merged

Tab strip: swap the dirty dot for close, fade only real overflow#57
BunsDev merged 1 commit into
mainfrom
feat/tab-strip-polish

Conversation

@BunsDev

@BunsDev BunsDev commented Aug 10, 2026

Copy link
Copy Markdown
Member

Design pass on the file tab strip.

Dirty dot and close share one slot

Every tab carried a permanently visible × at 0.6 opacity, sitting next to its dirty dot — two controls competing for the same job in a strip where width is scarce.

They now occupy one fixed 16px slot, stacked rather than adjacent: the dot shows at rest, hover swaps it for the close. Because the slot is fixed-width, revealing the close cannot reflow the strip under the cursor.

The active tab keeps its dot on hover-out — that is the file whose unsaved state matters most — and tints it with the accent instead of leaving it grey.

The edge fade is measured, not assumed

syncTabStripOverflow() compares scrollWidth against clientWidth and only then applies the mask. A half-visible tab reads as "there is more" rather than looking clipped, and a strip that fits gets no fade at all. It re-measures on refresh and on window resize, since whether the strip overflows is a function of width, not of its contents.

A test asserts the mask is not applied unconditionally, so the measurement cannot be quietly dropped later.

Smaller changes

  • The active tab is scrolled into view — it can otherwise sit off-screen after a ⌘-number jump once the strip overflows.
  • Middle click closes a tab, the way every other tab strip behaves.
  • The separator beside the active tab is dropped, so it stops competing with the accent underline; the last tab loses its trailing border too.
  • The close button gains an aria-label naming its file, instead of a bare ×.
  • Removes .tab.running and .tab.exited — styled classes no code path sets. refreshTabs is the only .tab producer, so those rules were unreachable.

Verification

  • npx vitest run1449 passed, 11 skipped, 4 failed.
  • pnpm typecheck — clean.
  • tauriDesktopTabs.test.ts extended from 8 to 11 tests.

Three of the four failures are the known environmental baseline on this machine (pnpm version pin; GNU stat shadowing BSD stat) in release-signing tests untouched here. The fourth, cleanTextInput.wrap.interactions, passes in isolation and is one of two ink/React TUI tests that wobble under parallel load — the other, useInputHandling.sidePanelToggle, flaked in an earlier run of this same suite. Neither touches the tab strip.

Note for reviewers

.tab:has(+ .tab.active) uses :has(), supported in the WKWebView this ships in.

Built in an isolated worktree off main, because the primary checkout currently carries another session's in-progress work in these same files.

Not verified

Not run in the app — doing so would have replaced the running build with one lacking that other in-progress work. The hover swap and the fade are reasoned and unit-tested, not seen.

🤖 Generated with Claude Code

Every file tab carried a permanently visible close button at 0.6 opacity
next to its dirty dot. The two now share one fixed 16px slot: the dot
shows at rest, hover swaps it for the close, and because they are stacked
rather than adjacent the strip cannot reflow under the cursor. The active
tab keeps its dot on hover-out - that is the file whose unsaved state
matters most - and tints it with the accent.

The edge fade is measured rather than assumed. syncTabStripOverflow()
compares scrollWidth to clientWidth and only then masks the edges, so a
half-visible tab reads as "there is more" instead of looking clipped,
while a strip that fits gets no fade at all. It re-measures on refresh
and on window resize, since overflow is a function of width.

Also: the active tab is scrolled into view, which matters once the strip
overflows and you jump with a command-number shortcut; middle click
closes a tab; the separator beside the active tab is dropped so it stops
competing with the accent underline, along with the last tab's trailing
border; and the close button gains an aria-label naming its file.

Removes .tab.running and .tab.exited, which styled classes no code path
sets - the file strip is the only .tab producer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 10, 2026 13:34
@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
psyche-build-docs Ready Ready Preview Aug 10, 2026 1:34pm

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Design update to the macOS Tauri file tab strip to reduce visual competition in tight space and make the edge fade reflect real overflow.

Changes:

  • Reworks the dirty-dot/close affordance to share a fixed-width end slot per tab and adds a per-file aria-label on the close button.
  • Applies the edge fade only when the strip actually overflows (measurement-driven), and re-measures on refresh and window resize; scrolls the active tab into view.
  • Adds middle-click-to-close behavior and extends unit tests to lock in the new tab-strip behaviors.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
native/macos/psyche-build-tauri/web/styles.css Adds overflow mask styling and tab-end slot layout; updates hover/active tab visuals.
native/macos/psyche-build-tauri/web/main.js Measures overflow to toggle the fade, scrolls active tab into view, and updates tab DOM/handlers (close, middle-click, aria-label).
tests/tauriDesktopTabs.test.ts Adds regression tests asserting the slot swap behavior and overflow measurement-driven fade.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 945 to 949
.tab .close {
width: 16px;
height: 16px;
position: absolute;
inset: 0;
border-radius: 4px;
display: inline-flex;
@BunsDev
BunsDev merged commit 8e1db37 into main Aug 10, 2026
4 checks passed
@BunsDev
BunsDev deleted the feat/tab-strip-polish branch August 10, 2026 13:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants