Skip to content

fix(chat): pin streaming responses to the composer; open remote-only branches as worktrees - #66

Merged
enisze merged 2 commits into
mainfrom
feature/fix-chat-scroll-empty-space
Aug 25, 2026
Merged

fix(chat): pin streaming responses to the composer; open remote-only branches as worktrees#66
enisze merged 2 commits into
mainfrom
feature/fix-chat-scroll-empty-space

Conversation

@enisze

@enisze enisze commented Aug 25, 2026

Copy link
Copy Markdown
Owner

Two independent fixes reported from the same session.

Streaming responses sat far above the composer

Sending made the new user message a LegendList anchoredEndSpace target, which reserved a viewport-tall blank tail below it and switched maintainScrollAtEnd off. Until a turn outgrew that reserve, the response stayed pinned near the top of the timeline with a large gap between the text and the composer.

The anchored tail is gone: the timeline always maintains scroll at the end, so a streaming turn hugs the composer. That removes the anchor plumbing whose only job was positioning and re-settling that reserve (~200 lines in ChatView), and replaces timelineScrollAnchoring with timelineScroll — keeping the overflow probe the follow effect already needed. Scroll-away behaviour is unchanged: a wheel/touch/pointer gesture still parks the view and shows the "Scroll to end" pill.

Selecting a remote-only branch created no worktree

listRefs reports a remote ref as { name: "origin/claude/x", remoteName: "origin" } — the prefix is already in name. The workspace picker's branch suggestions concatenated both fields, and the pull-request dialog matched candidate refs against that same doubled shape. Picking a branch that exists only on origin sent origin/origin/claude/x, matched nothing, and silently produced no worktree. That is the normal case for an agent-pushed claude/* branch; a branch already present locally worked, which made it look intermittent.

Suggestions now pass and display the ref name as listed, and a reference resolves through findBranchRefForReference, which accepts either the qualified origin/claude/x or the plain claude/x and prefers a local ref over a remote one. The new shared stripRemoteRefPrefix strips only a ref's own remote prefix, so a local claude/x keeps every segment; baseRefChoices drops its private copy of that logic.

Picking such a branch now runs git worktree add -b claude/x <path> origin/claude/x, roots the chat in that worktree, and the diff resolves its base via upstream → provider default, so it shows the branch's changes. The base-branch selector on the right is untouched.

Verification

  • Workspace typecheck: clean apart from pre-existing failures (DesktopClientSettings.test.ts fixture from 3396b19, effect-lint noise in WorkspaceSearchIndex.test.ts).
  • oxlint clean on the touched files.
  • Full web unit project green (193 files / 1786 tests); one unrelated flake, stashImageCompression.test.ts timing out under parallel load, passes on its own.
  • New tests: timelineScroll.test.ts, stripRemoteRefPrefix, findBranchRefForReference, plus a rewritten MessagesTimeline case asserting no reserved tail.

Not covered: a manually typed origin/claude/x while a local claude/x exists still dead-ends, because the server-side ref search filters by substring on the full string. Clicking a suggestion is unaffected.

🤖 Generated with Claude Code

enisze and others added 2 commits August 25, 2026 17:50
Sending made the new user message a LegendList anchoredEndSpace target,
which reserved a viewport-tall blank tail below it and disabled
maintainScrollAtEnd. Until a turn outgrew that reserve, the response sat
at the top of the timeline with a large gap above the composer.

Drop the anchored tail: the list now always maintains scroll at the end,
so a streaming turn hugs the composer. This removes the anchor plumbing
that existed only to position and re-settle that reserve, and replaces
timelineScrollAnchoring with timelineScroll, whose overflow probe the
follow effect already needed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
listRefs already carries the remote in a ref's name (`origin/claude/x`)
and repeats it in `remoteName`, but the workspace picker's branch
suggestions concatenated both and the pull-request dialog matched refs
against that same doubled shape. Selecting a branch that exists only on
origin therefore sent `origin/origin/claude/x`, matched no ref, and
silently created no worktree — the common case for an agent-pushed
branch.

Pass and display the ref name as listed, and resolve a reference through
findBranchRefForReference, which accepts either the qualified or the
plain branch name and prefers a local ref over a remote one. Add a shared
stripRemoteRefPrefix that strips only a ref's own remote prefix, so a
local `claude/x` keeps every segment.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@enisze
enisze merged commit a0c94ec into main Aug 25, 2026
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