Files
tty7/docs/window/sidebar.mdx
T
l0ng-aiandl0ng-ai 9fc0f331e8 feat(tabs): drag a tab in as a pane, and a pane out as a tab (#651)
* feat(tabs): drag a tab in as a pane, and a pane out as a tab

A tab dragged by its chip or its sidebar row can be dropped over the
panes to become one of them, and a pane dragged by its grip can be
dropped on the strip or the sidebar to become a tab of its own. Both
carry the panes across as they are: nothing is spawned and nothing is
killed, so a shell mid-command, an SSH session or an agent mid-turn
keeps running.

The landing is read the way a pane drag's already is, minus the middle:
an arriving tab has nothing here to trade places with, so a pane's core
means "split it the way it is longest". A tab that was itself split
arrives with its own shape intact and takes one share of the row or
column it joined. A pane on its way out is offered a caret between two
tabs, and the last pane in a tab is offered nothing, being a tab of its
own already.

Picking a tab up no longer switches to it: the strip and the sidebar
now activate on the click rather than on the press. Without that the
merge cannot be expressed at all — pressing the tab to drag it would
put it on screen, leaving no other tab to drop it into.

Two things in the machine tree had to follow:

* Panes that change tabs are told as PaneMove, one at a time, rather
  than as a tab closing and another being rebuilt around them.
* The tabs the machine already has are reconciled before new ones are
  created, so a pane leaving for a tab of its own is given up by the
  old tab before the new one asks to register it. The machine refuses a
  pane that is in two tabs at once, and the refusal desynced the window.

Closes #621

* test(tree-sync): a tab grafted above a whole layout still converges

* fix(tabs): keep a click on the close button from switching tabs

Switching on the release rather than the press means every click inside
a chip or a sidebar row now reaches the row itself, and gpui-component's
`Button` does not stop propagation on a click it handled. So one click on
a tab's close button ran `close_tab(i)` and then `activate(i)` — with `i`
by then naming whichever tab had slid into that slot, which moved the
active tab somewhere nobody asked for. A click into the rename field did
the same: it switched away from the tab whose name was being typed, and
took the focus out of the field with it.

Both now hold the click where they handled it, the way they already held
the press.

---------

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-08-15 17:05:34 +08:00

89 lines
3.9 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "The sidebar"
description: "Tabs grouped by repository, with branch, diff counts, and agent status on every row."
---
The left sidebar is tty7's tab bar, and it is the default because a vertical row
has room for things a horizontal chip does not: the repository a tab belongs to,
the branch it is on, how much has changed there, and what a coding agent in it
is doing.
<kbd>⌘ B</kbd> shows and hides it. Drag its right edge to resize.
<Frame caption="Placeholder — screenshot: the sidebar with two repo groups, agent avatars and status dots, branch and +N M counts">
<img src="/images/placeholder.svg" alt="The tty7 tab sidebar" />
</Frame>
## Grouped by repository
Rows sit under a header per git repository, with everything else collected in a
trailing **Scratch** section. The grouping follows the tab's working directory,
not its history — switching branches or `cd`-ing around inside a repository
never moves a row out from under its header.
**Settings → Window & Tabs → Sidebar grouping** switches between *By repo* (the
default), *By repo or folder*, and *Flat*. The default collects every non-repo
tab in Scratch; *By repo or folder* groups those tabs under their working
directory instead — for agents running in plain folders — leaving Scratch to
tabs whose shell has not reported a directory yet.
## What a row tells you
<CardGroup cols={2}>
<Card title="Who is running there" icon="robot">
A brand avatar when a coding agent is in the tab, plus a status dot —
blue for working, amber for needs-your-input, green for done.
</Card>
<Card title="Where it is" icon="code-branch">
The pane's git branch, refreshed on `cd` and whenever a command finishes.
</Card>
<Card title="What changed" icon="plus-minus">
The working-tree diff as `+N M`. Click the counts to open the
[diff overlay](/git/diffs).
</Card>
<Card title="Whether you have looked" icon="circle-dot">
An unread marker on tabs where a coding agent finished its turn while you
were elsewhere. Agent tabs also carry *Mark as Unread* in the right-click
menu, once there is a finished turn to mark.
</Card>
</CardGroup>
If you would rather the counts not be clickable, turn off **Settings → Window &
Tabs → Open diff preview from sidebar counts**. The branch and the numbers stay;
they simply stop opening the overlay.
## Rearranging
Drag a row to reorder it within its group, or drag a whole group header to move
the group. A row cannot be dragged into a different group: a tab's group comes
from its working directory, so `cd` is what moves it.
Drag a row out over the panes instead and it stops being a session of its own:
it lands as a pane of the tab on screen, wherever the highlight says. Dragging a
pane the other way — by its grip, onto the sidebar — gives it a row of its own,
between whichever two the caret lands between. See
[making one tab a pane of another](/window/tabs-and-splits#making-one-tab-a-pane-of-another).
## Naming
Almost no tab has a name of its own, so the sidebar falls back:
1. a name you set (right-click → **Rename Tab…**)
2. the title the shell is reporting — the running command, usually
3. **Shell 3**, numbered by position, when there is no title at all
`tty7 tab ls` answers the same question with more evidence, because a script has
no screen to look at. Its `label` falls back through the name, then the coding
agent running in the tab ("Claude Code"), then the last segment of the working
directory, then the foreground process — while `name` stays literal, so a script
can tell a real name from a stand-in.
## The switcher
<kbd>⌘ ⇧ O</kbd> opens the workspace switcher: every workspace on every machine
you are connected to on the left, that workspace's tabs on the right. Type to
filter both, <kbd>⇥</kbd> to cross into the tab column, <kbd>⏎</kbd> to open.
From here you can also rename a workspace, open one in a new window, stop one,
or connect to a machine you have a profile for.