Tab strip: swap the dirty dot for close, fade only real overflow - #57
Merged
Conversation
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>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
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-labelon 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; |
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.
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()comparesscrollWidthagainstclientWidthand 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
aria-labelnaming its file, instead of a bare×..tab.runningand.tab.exited— styled classes no code path sets.refreshTabsis the only.tabproducer, so those rules were unreachable.Verification
npx vitest run— 1449 passed, 11 skipped, 4 failed.pnpm typecheck— clean.tauriDesktopTabs.test.tsextended from 8 to 11 tests.Three of the four failures are the known environmental baseline on this machine (pnpm version pin; GNU
statshadowing BSDstat) 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