Skip to content

feat: multirow tab bar with resizable height - #616

Merged
danshapiro merged 13 commits into
mainfrom
feat/tab-bar-multirow-sizing
Aug 7, 2026
Merged

feat: multirow tab bar with resizable height#616
danshapiro merged 13 commits into
mainfrom
feat/tab-bar-multirow-sizing

Conversation

@danshapiro

Copy link
Copy Markdown
Owner

Summary

Implements tab bar UI improvements with multirow as the default, intelligent width scaling, and drag-resizable height management.

Features

  • Multirow is now the default tab bar mode
  • Smart tab width scaling: min 150px per tab, fit as many as possible per row, then stretch tabs to fill available width (up to 200px max)
  • Single-row mode: tabs remain at fixed 175px width
  • Resizable tab bar height: when multirow contains >1 row, a splitter handle appears below the bar on hover (or with focus). Drag or use keyboard to adjust visible rows (1–10). Defaults to showing 3 rows, persisted per-browser in localStorage
  • Scale-aware: measurements work correctly at any UI zoom level
  • Auto-collapse: handle hides automatically when all tabs fit on one row; bar shrinks as tabs close

Quality gates

  • Unit tests: 4614/4615 (one unrelated timing flake, passed on rerun)
  • Lint & typecheck: clean
  • Browser tests: 193 passed, 22 failures all proven pre-existing on fork point
  • Plan review: 3 rounds, PASSED (caught stale callback bug + broken browser test assumption)
  • Delta review: PASSED first round, zero blockers

Pre-existing issues discovered (not caused by this work)

  • 6 Rust-only test specs missing from RUST_ONLY_SPECS, filed as kata 0q8k
  • API returning 500 instead of 422, filed as kata kqn1
  • Mobile banner text mismatch, filed as kata 15gk

Testing notes

  • Visual baselines regenerated for multirow default
  • Full unit + real-browser e2e coverage
  • Scroll & keyboard accessibility verified

Generated with Amplifier

danshapiro and others added 13 commits August 7, 2026 00:43
…dation

Falsified assumptions fixed:
- A1: px row-pitch constants -> scale-aware rem model (--ui-scale scales all
  Tailwind rem sizing; calc((2.125n - 0.125)rem + 1px) inline max-height,
  runtime px helpers parameterized by live root font-size)
- A2: resize handle moved from straddling the bar's bottom edge to a
  bottom-only overlay (translate-y-full) so it cannot steal bottom-row
  tab clicks/drags
- A3a: sortable tab style switches to CSS.Translate to prevent mid-drag
  stretch with variable tab widths (dnd-kit issue #117)

Also: scaled-UI (uiScale 1.25) e2e case added; Self-Review updated.
Ledger: .worktrees/.the-usual-logs/tab-bar-multirow-sizing/load-bearing-ledger.md
…plan

- Task 4 Step 3c: add multirowTabs to renderSortableTab's useCallback
  dependency array (stale-closure bug that broke runtime toggling and
  made Task 4's 175px e2e gate unachievable; exhaustive-deps is
  warning-only so lint would not catch it)
- Task 1 Step 8: make sidebar.spec.ts overflow-tabs test mode-explicit
  (dispatch multirowTabs: false) since it structurally depends on the
  old single-row default via .overflow-x-auto; Step 9 now runs the
  sidebar spec; Step 11 commit now includes the edited e2e specs
- Task 9 Step 2: broaden e2e discovery with a structural grep
  (overflow-x-auto|flex-wrap|tab-strip), run sidebar in the match set,
  and add a full e2e-browser suite run as the real gate for a fully
  green tree
Fresheyes iteration 2 flagged the Task 5 Step 4 verification gate as
unachievable: 'grep -rn max-h-32 src/ test/unit/' always matches the
pre-existing, unrelated max-h-32 in src/components/ui/error-boundary.tsx,
and the 'fix any stragglers' instruction risked inducing an edit to that
unrelated production code.

Scope the sweep to src/components/TabBar.tsx, src/components/TabItem.tsx,
and test/unit/client/components/, negate the grep so the command's exit
code matches the stated expectation, and add an explicit note that the
error-boundary occurrence is out of scope and must not be modified.
Flips panes.multirowTabs default from false to true in the shared settings
contract, the TabBar selector fallback, the PanesSettings toggle fallback,
and the docs mock. Single-row-specific tests now pin multirowTabs: false
explicitly; a new unit test locks in the multirow default and another locks
in persisted opt-out. Also gives the Multi-row tabs toggle an accessible
name (aria-label) so the settings e2e can address it by role+name, and
navigates to the Panes settings section in that spec.

🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)

Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…amp 1-10)

🤖 Generated with Amplifier

Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…gle-row fixed 175px)

🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)

Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)

Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
… ariaLabel)

🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)

Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
Integrate TabBarResizeHandle into TabBar: scale-aware multi-row
detection (scrollHeight vs tabBarMultiRowThresholdPx at live root
font-size, ResizeObserver re-measure), updateSettingsLocal dispatch for
tabBarRows, handle rendered only when multirowTabs && hasMultipleRows.
TabBar's new import chain pulls the panes barrel, so the static
lucide-react mocks in the two TabBar test files become partial mocks.

🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)

Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…sistence

🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)

Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
… bar

Regenerate editor-pane-loaded-chromium-linux.png: the editor pane element
is 7px taller because the multirow tab strip's rem-based row sizing
(2rem tabs, 2.125rem pitch) is 7px shorter than the old single-row bar.
Bottom-band pixels are byte-identical under a +7px shift; the remaining
118px toolbar drift reproduces byte-for-byte with origin/main production
code against the old baseline (stale since 1a9a8c2; editor-pane commits
such as 9992762 landed after it), so it is pre-existing, not this
feature. No other baseline needed regeneration: screenshot-baselines
passes 6/6 unchanged.

🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)

Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
@danshapiro
danshapiro merged commit 6042873 into main Aug 7, 2026
3 checks passed
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