diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0b8501ee..256b157c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -88,24 +88,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
command once, the same as after a restart.
- **The sidebar groups tabs by repo automatically; pin what you want to keep**
- (#955). Groups now come in two halves, split by a divider. Below it, every
- tab you have not pinned is filed under its git repository, and an SSH tab
- under the host it is on rather than its remote path — `/home/ubuntu` on two
- machines used to share a header. Above it sit the groups you keep, in the
- order you drag them into, until you delete them. A pinned group can keep a
- folder: a tab whose working directory enters it joins it (the deepest folder
- wins when they nest, and a worktree of a pinned repo counts), while a tab you
- drag out stays out until it leaves the folder and comes back. Pin an auto
- group with the pin on its header or by dragging the header above the
- divider; pin a folder by dropping it from Finder, with **Pin as Group** in the
- Files panel, or with **Open Folder as Group…** in the palette. **New Group**
- in the palette or on a tab's right-click makes a label group. Deleting a
- group closes nothing — its tabs go back to auto grouping — and dragging a tab
- below the divider does the same for one tab. Groups, their order and which
- are folded are stored with the workspace, so every window onto it agrees.
- **Settings → Window & Tabs → Auto grouping** replaces the three-way *Sidebar
- grouping* choice; off, unpinned tabs sit in one flat list under the pinned
- groups. Scratch is now **Ungrouped**.
+ (#955). Groups now come in two halves. Below, every tab you have not pinned is
+ filed under its git repository, and an SSH tab under the host it is on rather
+ than its remote path — `/home/ubuntu` on two machines used to share a header.
+ Above sit the groups you keep, each marked ◆ beside its name, in the order you
+ drag them into, until you delete them. A pinned group can keep a folder: a tab
+ whose working directory enters it joins it (the deepest folder wins when they
+ nest, and a worktree of a pinned repo counts), while a tab you drag out stays
+ out until it leaves the folder and comes back. Pin an auto group with the ◆ on
+ its header or by dragging the header up among the pinned ones; pin a folder by
+ dropping it from Finder, with **Pin as Group** in the Files panel, or with
+ **Open Folder as Group…** in the palette. **New Group** in the palette or on a
+ tab's right-click makes a label group. Deleting a group closes nothing — its
+ tabs go back to auto grouping — and dragging a tab below the pinned groups
+ does the same for one tab. Groups, their order and which are folded are stored
+ with the workspace, so every window onto it agrees. **Settings → Window & Tabs
+ → Auto grouping** replaces the three-way *Sidebar grouping* choice; off,
+ unpinned tabs sit in one flat list under the pinned groups. Scratch is now
+ **Ungrouped**.
- **The New Tab menu names three shells, not every one the machine has.** A
stock macOS box reports nine, so the `+` menu opened on a column of `csh`,
diff --git a/assets/icons/pin.svg b/assets/icons/pin.svg
deleted file mode 100644
index 05568e0d..00000000
--- a/assets/icons/pin.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/docs/window/sidebar.mdx b/docs/window/sidebar.mdx
index a5c32cd5..e53b214d 100644
--- a/docs/window/sidebar.mdx
+++ b/docs/window/sidebar.mdx
@@ -18,17 +18,19 @@ is doing.
The sidebar groups tabs by repository automatically. Pin what you want to keep.
-**Auto groups** sit below a divider. Every tab you have not pinned is filed
-under the git repository its working directory is in — a linked worktree under
-the repository it belongs to, a submodule under itself — and an SSH tab under
-the host it is connected to, so `/home/ubuntu` on two machines is two groups.
+**Auto groups** sit below the pinned ones. Every tab you have not pinned is
+filed under the git repository its working directory is in — a linked worktree
+under the repository it belongs to, a submodule under itself — and an SSH tab
+under the host it is connected to, so `/home/ubuntu` on two machines is two
+groups.
Everything else collects in a trailing **Ungrouped** section. An auto group
follows the tab's working directory, not its history: `cd` into another
repository and the row moves; switching branches never does. When its last tab
leaves, the group is gone.
-**Pinned groups** sit above the divider, in the order you put them, and stay
-until you delete them — an empty one keeps its place with a **+ New Tab** row.
+**Pinned groups** sit at the top, each marked ◆ beside its name, in the order
+you put them, and stay until you delete them — an empty one keeps its place
+with a **+ New Tab** row.
A tab in a pinned group never leaves it on its own. There are two kinds:
- A **folder group** keeps a directory. A tab whose working directory *enters*
@@ -36,7 +38,7 @@ A tab in a pinned group never leaves it on its own. There are two kinds:
repository the folder is. When folders nest, the deepest one wins, so pinning
a monorepo and one package in it files the package's tabs under the package.
A tab you drag out while it is still inside stays out until it leaves the
- folder and comes back. Hover the header for the folder's path; click its pin
+ folder and comes back. Hover the header for the folder's path; click its ◆
to unpin it, and its tabs go back to auto groups.
- A **label group** is just a name, for tabs that belong together for a reason
no directory shows. Tabs go in and out of it by hand.
@@ -45,7 +47,7 @@ Ways to pin:
| To get | Do this |
|---|---|
-| A folder group from an auto group | Click the pin on its header, or drag the header above the divider |
+| A folder group from an auto group | Click the ◆ on its header, or drag the header up among the pinned groups |
| A folder group from a folder | Drop it from Finder onto the sidebar, choose **Pin as Group** on it in the Files panel, or run **Open Folder as Group…** from the command palette |
| A label group | Right-click a tab → **Move to Group → New Group…**, or run **New Group** from the command palette |
@@ -92,8 +94,8 @@ they simply stop opening the overlay.
Drag a row to reorder it within its group, or drag a group header to move the
group — pinned groups among pinned groups, auto groups among auto groups. Drag
-a row onto a pinned group to put it there, or anywhere below the divider to
-hand it back to auto grouping. A row cannot be dragged into an auto group: its
+a row onto a pinned group to put it there, or anywhere below the pinned groups
+to hand it back to auto grouping. A row cannot be dragged into an auto group: its
membership comes from the 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:
diff --git a/src/ui/assets.rs b/src/ui/assets.rs
index 73ffca40..1731232b 100644
--- a/src/ui/assets.rs
+++ b/src/ui/assets.rs
@@ -34,7 +34,6 @@ fn agent_icon(path: &str) -> Option<&'static [u8]> {
"icons/panel-left.svg" => include_bytes!("../../assets/icons/panel-left.svg"),
"icons/panel-right.svg" => include_bytes!("../../assets/icons/panel-right.svg"),
"icons/plus.svg" => include_bytes!("../../assets/icons/plus.svg"),
- "icons/pin.svg" => include_bytes!("../../assets/icons/pin.svg"),
"icons/ellipsis.svg" => include_bytes!("../../assets/icons/ellipsis.svg"),
"icons/folder-closed.svg" => include_bytes!("../../assets/icons/folder-closed.svg"),
"icons/folder-open.svg" => include_bytes!("../../assets/icons/folder-open.svg"),
diff --git a/src/ui/tab_sidebar.rs b/src/ui/tab_sidebar.rs
index e5274f79..4bf498fc 100644
--- a/src/ui/tab_sidebar.rs
+++ b/src/ui/tab_sidebar.rs
@@ -34,6 +34,17 @@ const GRAB_HANDLE_W: f32 = 48.;
const ROW_GAP: f32 = 2.;
+/// What marks a kept group, and what the header button that keeps one reads.
+///
+/// A character rather than an icon: at a header's size a drawn pin is a
+/// smudge, and the mark only has to say "kept" beside the name, the way a
+/// bullet does. With no line between kept and derived groups it is also the
+/// only thing that tells the two apart, so every kept group carries it.
+const PIN_MARK: &str = "\u{25C6}";
+
+/// The mark's size: small enough to sit under the header text's x-height.
+const PIN_MARK_SIZE: f32 = 8.;
+
/// A single-line tab row: one line of `text_sm` and a little air, the same
/// 28px the search field and the workspace chip above it stand at.
const ROW_HEIGHT: f32 = 28.;
@@ -1283,37 +1294,39 @@ impl Tty7App {
.child(Icon::new(IconName::ChevronRight).xsmall()),
)
})
- // The pin marks a folder group: kept, and keeping a folder.
- // It is also the way to stop keeping it — a click unpins,
- // and the folder's tabs fall back to the groups their cwds
- // resolve to. A label group has no such mark: it sits above
- // the divider, and there is no folder to fall back on.
- .when_some(
- pinned_id.filter(|_| pinned_folder.is_some()),
- |header, id| {
- header.child(
- div()
- .id(("sidebar-group-unpin", group_ix))
- .flex_shrink_0()
- .cursor_pointer()
- .hover(|s| s.text_color(cx.theme().foreground))
- .child(Icon::empty().path("icons/pin.svg").xsmall())
- .tooltip(|window, cx| {
- gpui_component::tooltip::Tooltip::new(t(
- L10nKey::SidebarUnpinGroup,
- ))
- .build(window, cx)
- })
- .on_mouse_down(MouseButton::Left, |_, _, cx| {
- cx.stop_propagation()
- })
- .on_click(cx.listener(move |this, _, _window, cx| {
- cx.stop_propagation();
- this.delete_group(id, cx);
- })),
- )
- },
- )
+ // Every kept group carries the mark; it is what sets them
+ // apart from the derived groups below. On a folder group it
+ // is also the way to stop keeping it — a click unpins, and
+ // the folder's tabs fall back to the groups their cwds
+ // resolve to. A label group's mark is only a mark: without a
+ // folder there is nothing to fall back on, so letting go of
+ // it is Delete Group, in its menu, not a stray click.
+ .when_some(pinned_id, |header, id| {
+ let mark = div()
+ .flex_shrink_0()
+ .text_size(px(PIN_MARK_SIZE))
+ .line_height(px(PIN_MARK_SIZE))
+ .child(PIN_MARK);
+ header.child(match pinned_folder.is_some() {
+ false => mark.into_any_element(),
+ true => mark
+ .id(("sidebar-group-unpin", group_ix))
+ .cursor_pointer()
+ .hover(|s| s.text_color(cx.theme().foreground))
+ .tooltip(|window, cx| {
+ gpui_component::tooltip::Tooltip::new(t(
+ L10nKey::SidebarUnpinGroup,
+ ))
+ .build(window, cx)
+ })
+ .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
+ .on_click(cx.listener(move |this, _, _window, cx| {
+ cx.stop_propagation();
+ this.delete_group(id, cx);
+ }))
+ .into_any_element(),
+ })
+ })
.child(match renaming_group {
Some(input) => div()
.id(("sidebar-group-rename", group_ix))
@@ -1823,8 +1836,14 @@ impl Tty7App {
.child(handle)
}
- /// The line between the kept groups and the derived ones, recording where
- /// it was drawn so a drag next frame can tell which side it is on.
+ /// The boundary between the kept groups and the derived ones, recording
+ /// where it lies so a drag next frame can tell which side it is on.
+ ///
+ /// Not drawn at rest — the mark on each kept header already says which
+ /// side a group is on, and a hairline across the list was one more rule
+ /// for the eye to cross. It keeps its place in the layout all the same, so
+ /// nothing jumps when a drag starts, and shows as a line only while it is
+ /// the target: when letting go would pin a header or hand a tab back.
///
/// With nothing pinned yet it only appears while an auto header is being
/// carried, and then as a drop zone rather than a hairline: a one-pixel
@@ -1833,9 +1852,10 @@ impl Tty7App {
/// back to auto grouping.
fn sidebar_divider(&self, lit: bool, zone: bool, cx: &Context) -> AnyElement {
let bounds = self.sidebar_divider.clone();
- let ink = match lit {
- true => cx.theme().drag_border,
- false => cx.theme().sidebar_border,
+ let ink = match (lit, zone) {
+ (true, _) => cx.theme().drag_border,
+ (false, true) => cx.theme().sidebar_border,
+ (false, false) => gpui::transparent_black(),
};
let body = match zone {
true => h_flex()
@@ -1862,7 +1882,9 @@ impl Tty7App {
.relative()
.w_full()
.px_2()
- .pt(px(10.))
+ // Less than when it was a drawn rule: the next header's own top
+ // margin already opens the gap between the two kinds of group.
+ .pt(px(4.))
.pb(px(2.))
.child(
canvas(move |b, _window, _cx| bounds.set(Some(b)), |_, _, _, _| {})
@@ -1895,7 +1917,7 @@ impl Tty7App {
fade.a = 0.;
let muted = cx.theme().muted_foreground;
let ink = cx.theme().foreground;
- let button = |id: &'static str, path: &'static str, tip: L10nKey| {
+ let button = |id: &'static str, glyph: AnyElement, tip: L10nKey| {
div()
.id((id, group_ix))
.flex_shrink_0()
@@ -1904,7 +1926,7 @@ impl Tty7App {
.cursor_pointer()
.text_color(muted)
.hover(move |s| s.text_color(ink))
- .child(Icon::empty().path(path).xsmall())
+ .child(glyph)
.tooltip(move |window, cx| {
gpui_component::tooltip::Tooltip::new(t(tip)).build(window, cx)
})
@@ -1914,7 +1936,19 @@ impl Tty7App {
buttons = buttons.child(
button(
"sidebar-group-pin",
- "icons/pin.svg",
+ // The mark the group will carry once kept, the size of
+ // the icon beside it so the two buttons line up.
+ div()
+ .size(px(12.))
+ .flex()
+ .items_center()
+ .justify_center()
+ .text_size(px(PIN_MARK_SIZE))
+ // Its own line box: the header's is several times the
+ // mark's size and would drop it below the "+" beside it.
+ .line_height(px(PIN_MARK_SIZE))
+ .child(PIN_MARK)
+ .into_any_element(),
L10nKey::SidebarPinGroup,
)
.on_mouse_down(
@@ -1930,7 +1964,10 @@ impl Tty7App {
buttons = buttons.child(
button(
"sidebar-group-add",
- "icons/plus.svg",
+ Icon::empty()
+ .path("icons/plus.svg")
+ .xsmall()
+ .into_any_element(),
L10nKey::SidebarGroupNewTab,
)
.on_mouse_down(