Skip to content

fix(emcn): stop the tab strip scrolling vertically by a pixel - #6113

Merged
waleedlatif1 merged 2 commits into
stagingfrom
fix-desktop-browser-tabs
Jul 31, 2026
Merged

fix(emcn): stop the tab strip scrolling vertically by a pixel#6113
waleedlatif1 merged 2 commits into
stagingfrom
fix-desktop-browser-tabs

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

The desktop browser's tab strip could be scrolled on the y axis — enough for a trackpad to nudge the tabs half out of view.

Root cause

The active tab extends one pixel past the strip so it covers the bottom border and reads as joined to the panel below. That pixel came from -mb-px on the tab button, which meant it overflowed the row containing it — and that row is a scroll container, because overflow-x: auto computes a visible overflow-y to auto as well. One pixel of scrollable overflow is all it takes.

Fix

Move the overlap onto the row instead of the tab. The tabs then sit flush inside the scroll container so there is nothing to scroll, while the row itself still hangs the pixel over the border. No visual change.

Verification

Measured in a real renderer using the component's own class strings:

scrollHeight clientHeight y-overflow
before 30 29 1px, scrollable
after 30 30 0

Worth noting: a first attempt to reproduce this with hand-written inline styles showed no overflow. Only the real compiled classes surfaced it, which is why the numbers above come from the actual class output rather than a simplified repro.

The guard asserts the overlap lives on the scrolling row and not on the tab, and was confirmed to fail when the original layout is restored.

TabStrip is shared by the browser and terminal panels, so both stop scrolling.

Not included

The reported "can't open a new tab" is a separate issue and is deliberately left out of this PR. Diagnosed for whoever picks it up: at MAX_BROWSER_TABS (8) the new-tab button is disabled, and the shared button applies disabled:pointer-events-none — so the trigger cannot receive hover and the tooltip that would say "Maximum of 8 tabs" never opens. Separately, ipc.ts:578 does .catch(() => {}) around handlePanelAction, so the SessionError thrown by addTabInternal at the cap is discarded. Between the two, the button looks simply broken.

The active tab extends one pixel past the strip so it covers the bottom border and
reads as joined to the panel below. That pixel came from `-mb-px` on the tab itself,
which meant it overflowed the row containing it — and that row is a scroll container,
because `overflow-x: auto` computes a visible `overflow-y` to `auto` as well. The strip
was therefore scrollable on the y axis by exactly one pixel, which is enough for a
trackpad to nudge the tabs out of view.

Moving the overlap onto the row keeps the tabs flush inside it, so the scroll container
has nothing to scroll, while the row itself still hangs the pixel over the border. The
strip is unchanged visually.

Measured in a real renderer with the component's own classes: the row was scrollHeight
30 against clientHeight 29, and is now 30 against 30. A first attempt to reproduce it
with hand-written inline styles showed no overflow at all — the real class output was
needed to see it.

Shared by the browser and terminal panels, so both stop scrolling.
@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview Jul 31, 2026 1:24am

Request Review

@cursor

cursor Bot commented Jul 31, 2026

Copy link
Copy Markdown

PR Summary

Low Risk
CSS layout tweak in shared UI with no auth, data, or API changes; visual intent is unchanged and coverage is a structural class-name guard.

Overview
Fixes a 1px vertical scroll on the shared TabStrip (browser and terminal panels) by moving -mb-px from each tab button onto the horizontally scrolling row.

The active tab still overlaps the strip’s bottom border visually, but the overlap no longer sits inside the overflow-x-auto container—where paired overflow-y becomes scrollable—so trackpad scrolling can’t nudge tabs vertically.

Adds a source-level test that enforces a single -mb-px on the scroll row (not on tab descendants), as a regression guard where full layout tests aren’t available in Node vitest.

Reviewed by Cursor Bugbot for commit b0bd2f2. Configure here.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@greptile-apps

greptile-apps Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Moves the one-pixel tab overlap from each tab button to the scrolling row and adds a source-level regression guard that ensures negative bottom margins remain outside the row’s descendants.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
packages/emcn/src/components/tab-strip/tab-strip.tsx Relocates -mb-px to the horizontal scrolling row, preserving the visual overlap without creating descendant vertical overflow.
packages/emcn/src/components/tab-strip/tab-strip.test.ts Adds a Node-compatible invariant test ensuring the sole negative bottom margin belongs to the scrolling row.

Reviews (2): Last reviewed commit: "test(emcn): pin the invariant, not the c..." | Re-trigger Greptile

Comment thread packages/emcn/src/components/tab-strip/tab-strip.test.ts Outdated

@cursor cursor Bot 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 93593c2. Configure here.

Review pointed out the guard read source strings rather than rendered geometry, so it
could pass while the overflow returned. Correct, and worth being precise about the
compromise: this package's vitest runs in `node` with no browser mode, and the repo's
only Playwright harness drives Electron against static HTML fixtures, so nothing here can
lay out a React tree against the compiled CSS. Adding that capability is real
infrastructure, not a line in this fix.

What the guard can do is pin the invariant instead of two particular class strings: no
negative bottom margin anywhere inside the scrolling row. That now catches the regression
wherever a descendant reintroduces it, rather than only on the tab button it came from —
verified against both shapes. The rendered behaviour stays measured rather than asserted,
and the numbers are recorded next to the guard.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit b0bd2f2. Configure here.

@waleedlatif1
waleedlatif1 merged commit 9e9dc54 into staging Jul 31, 2026
20 of 21 checks passed
@waleedlatif1
waleedlatif1 deleted the fix-desktop-browser-tabs branch July 31, 2026 01:23
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.

1 participant