Skip to content

feat(web): let the sidebar's sections be arranged by hand - #5

Merged
BarretoDiego merged 1 commit into
mainfrom
feat/sidebar-custom-order
Aug 24, 2026
Merged

feat(web): let the sidebar's sections be arranged by hand#5
BarretoDiego merged 1 commit into
mainfrom
feat/sidebar-custom-order

Conversation

@BarretoDiego

Copy link
Copy Markdown
Owner

Problem

Section order in the sidebar was whatever the grouping produced: sections with live work first, then history-only ones, then quiet seeded projects, with first-appearance breaking ties. A good default, a poor permanent arrangement — the project you check every morning drifts down the list the moment nothing is running in it, and environments had no ordering control at all.

What changed

Ordering is now a second pass over the built section tree, kept separate from grouping. Three modes, in the menu that already sorts threads:

  • Busiest first — the previous behavior, still the default.
  • Name (A–Z).
  • Custom (drag to arrange).

Custom is reachable two ways: drag a section header, or right-click it and pick Move up / Move down. Either one switches to Custom and records the whole level, so the result stops depending on activity. Reset section order goes back to Busiest first.

Sections move only within their own level. A drop outside the dragged section's siblings is refused, and while a drag is in flight every other level disables its droppables, so a project level never animates a swap it would discard. Sections cannot be reparented, only resequenced.

Notes on the design

  • Header rows now carry their parent, siblings and index, so a reorder never has to recover the level by splitting keys on /.
  • The manual list is one flat array across levels — child keys are already parent-prefixed. Keys of sections that are not currently rendered are kept rather than pruned, because grouping axes and the provider filter hide sections temporarily.
  • Switching to Custom from the menu seeds every unplaced section where it currently sits, freezing the layout on screen. A project added later joins the end of its level.

Verification

  • vp test run apps/web/src/components/sidebarThreadGrouping.test.ts — 42 passed, including new coverage for orderSidebarThreadGroups, planSidebarSectionOrder, collectSidebarSectionKeys and the level facts on header rows.
  • vp test run on Sidebar.logic, Sidebar.snooze, environmentGrouping, CommandPalette.logic, ui/sidebar, SidebarStageBackdrop, threadSidebarWidth, DesktopClientSettings — all passing.
  • tsgo --noEmit clean for apps/web, apps/desktop, packages/contracts; vp lint clean for the touched files.
  • No browser pass — say the word and I will drive the real app for before/after shots of the drag.

Written by Claude Opus 5 in Claude Code.

🤖 Generated with Claude Code

Section order was whatever the grouping produced: sections with live work
first, then history-only ones, then the quiet seeded projects, first
appearance breaking ties inside each rank. That is a reasonable default and a
poor permanent arrangement — the one project you check every morning drifts
down the list the moment nothing is running in it, and environments had no
ordering control at all.

Order becomes a second pass over the built tree rather than a variant of the
grouping. Which sections exist is one question; where they sit is another, and
splitting them is what lets an arrangement survive threads arriving, settling,
and being filtered out. Three modes, in the same menu that already sorts
threads: Busiest first (the old behavior), Name (A–Z), and Custom.

Custom is reachable two ways, because a sidebar is a list you point at and
also a list you drive from the keyboard. Drag a header to move it, or
right-click and choose Move up / Move down; either switches to Custom and
records the whole level, so the result stops depending on activity. Requiring
the mode to be picked first would have made the first drag a silent no-op.

Sections move only within their own level. A drop is refused outright when it
lands outside the dragged section's siblings, and while a drag is in flight
every other level disables its droppables, so a project level never animates a
swap it would then discard. Reparenting is not a thing sections can express.

Header rows now carry their parent, their siblings and their index. The
alternative was recovering the level by splitting keys on "/", which happens to
work today and would break the first time an id contains one.

The manual list is one flat array across levels — child keys are already
parent-prefixed, so ranks are unambiguous — and keys of sections that are not
currently rendered are kept rather than pruned: grouping axes and the provider
filter hide sections temporarily, and dropping their order would quietly throw
away an arrangement. Switching to Custom from the menu seeds every unplaced
section where it currently sits, so it freezes what you are looking at instead
of leaving untouched levels free to reshuffle. A project added later joins the
end of its level; Reset section order goes back to Busiest first.

Written by Claude Opus 5 in Claude Code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@BarretoDiego
BarretoDiego merged commit 067399c into main Aug 24, 2026
@BarretoDiego
BarretoDiego deleted the feat/sidebar-custom-order branch August 25, 2026 01:28
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