Repository navigation
fix(sidebar): drag-and-drop lands where you drop, and shows where first - #173
Merged
Merged
Conversation
Reported as "the session doesn't land where I want it, and it's hard to
know where it actually lands", most noticeably with groups and a PS session
next to a Claude one. A faithful simulation of the old code reproduced it
in 7 of 7 scenarios. There was not one cause but five, and one more
mechanism that undid even correct drops.
The drop was resolved to an INDEX, computed in one list and applied to two
others that hold different sessions:
1. The index counted the visible rows, so a "⎇ repo (N)" worktree cluster
header shifted it by one.
2. In FilterStrip mode it was an index into the FILTERED rows, applied to
SessionManager's full list (every group): dropping into the middle of a
group put the session at the top.
3. In inline-headers mode it was a _vm.Sessions index (live only), applied
to SessionManager (live + dormant): a sleeping session above the target
moved the drop.
4. The same index moved both lists, so the pane grid and the sidebar went
out of step.
5. Remove-then-insert at the pre-removal index made every downward move
overshoot by one.
A drop now resolves to an anchor ("before session X" / "after session Y"),
which is the same slot in every list. The logic is in a new WPF-free
SidebarDropResolver, fed the rows as actually rendered.
SessionManager.MoveSessionRelative finds the anchor after removing the moved
session, and the index-based MoveSession is gone from both SessionManager and
MainViewModel so the pattern can't come back. The grid is re-aligned to the
sidebar order (SessionOrder.AlignTo) instead of moved by the same index, and
RegisterSession inserts a woken or restarted session by relative position
for the same reason.
Then the part that made "PS and Claude" worse: "Open PowerShell here"
creates a session in the same repo, so it counts as a worktree sibling, and
ApplyClusteredOrder re-sorted EVERY sibling on any Add/Remove and whenever a
repo root resolved, i.e. on every restore. A PS session dragged away from
its Claude session snapped back the next time anything opened or closed,
and the saved order was rewritten on every launch. It also moved
SessionManager by a _vm.Sessions index (cause 3 again). It is replaced by
PlaceNewSessionsInClusters: only sessions launched with restoring: false are
placed, once, when their root resolves, and a drag removes a session from
that set. New PS sessions still appear beside their Claude session; after
that, the user's order wins. Behaviour change: siblings the user has
separated are no longer pulled back together.
"Hard to know where it lands": while dragging, a blue insertion line
(Views/InsertionLineAdorner) shows the slot. It is computed from the same
resolved target the drop applies, so it cannot disagree with the drop. It
hides over group headers, which highlight themselves instead. Dropping on an
inline group header now lands the session at the top of that group, rather
than wherever its old global position fell among the new group's members.
Tests: SidebarDropResolverTests and SessionOrderTests, plus
SidebarReorderScenarioTests, which runs scenarios 1-6 end-to-end through
the real SessionManager, the resolver and the grid alignment. Re-introducing
the overshoot, or a grid that doesn't follow the sidebar, makes 4 tests fail
each. Scenario 7 (restore) is enforced by the restoring: false gate in
LaunchSessionAsync, which unit tests can't reach. Not yet tested manually.
CLAUDE.md gains a "Sidebar order and drag-and-drop" section recording why no
index may cross between the lists.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NsYm9iLc2aRnRDfV2uZRQX
PR #159 fixed the same RegisterSession index-space bug with its own SessionOrder.LiveInsertIndex; this branch fixes it with InsertionIndex. Its six placement cases (dormant above, first/last, several dormant, restore in progress, nothing dormant) pass unchanged against InsertionIndex, so #159 can close without losing its coverage. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NsYm9iLc2aRnRDfV2uZRQX
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Sidebar drag-and-drop now lands the session exactly where you drop it, in both the sidebar and the pane grid, and a blue insertion line shows the slot while you drag. Also fixes a restarted or woken pane jumping out of its sidebar position — the bug #159 targets — so this supersedes #159, and carries its test cases over.
Why
Reported as "the session doesn't land where I want it, and it's hard to know where it actually lands", most noticeably with groups and a PowerShell session next to a Claude one. A faithful simulation of the old code reproduced it in 7 of 7 scenarios. There were five separate causes, all of the same shape: a drop was resolved to an index computed in one list and applied to others that hold different sessions:
⎇ repo (N)worktree cluster header shifted it by one.And one mechanism undid even correct drops:
ApplyClusteredOrderre-sorted every worktree sibling on any add/remove and on every restore, so a PowerShell session dragged away from its Claude session snapped back.Implementation notes
SidebarDropResolver, fed the rows as actually rendered.SessionManager.MoveSessionRelativefinds the anchor after removal. Index-basedMoveSessionis removed fromSessionManagerandMainViewModelso the pattern can't return.SessionOrder.AlignTo);RegisterSessioninserts a woken/restarted session by relative position (SessionOrder.InsertionIndex).ApplyClusteredOrder→PlaceNewSessionsInClusters: only sessions created in this run are placed beside their siblings, once. Behaviour change: siblings the user has separated are no longer pulled back together.Test plan
SidebarDropResolverTests,SessionOrderTests,SidebarReorderScenarioTests(scenarios 1–6 end to end through the realSessionManager, resolver and grid alignment). Re-introducing the overshoot, or a grid that doesn't follow the sidebar, fails 4 tests each.InsertionIndex.🤖 Generated with Claude Code
https://claude.ai/code/session_01NsYm9iLc2aRnRDfV2uZRQX