Skip to content

fix(connect): land the transcript ↑/↓ snap on the edge you are travelling towards - #94

Merged
hbrooks merged 1 commit into
mainfrom
fix/transcript-nav-directional-snap
Jul 29, 2026
Merged

fix(connect): land the transcript ↑/↓ snap on the edge you are travelling towards#94
hbrooks merged 1 commit into
mainfrom
fix/transcript-nav-directional-snap

Conversation

@ellipsis-dev

@ellipsis-dev ellipsis-dev Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Walking the ↑/↓ highlight in agent session connect onto a message taller than the transcript window landed it on the wrong edge — you entered a long message from the end you were leaving rather than the end you were heading towards.

Scenario Expected Before this PR
(short) (long), on (short), press ↓ land on the top of (long) landed on its bottom
(short) (long) (short), on the trailing (short), press ↑ land on the bottom of (long) landed on its top

Either way the highlight jumped past the whole message to the far edge, so pressing the same arrow again scrolled back over what you had just skipped.

Two compounding causes

  1. snapToEntry inferred direction from the entry's position, not the direction of travel — any entry too tall to fit was top-aligned unconditionally. Backing up into one from below jumped to its far-away first line instead of the last line just above you.

  2. ensureVisible discarded the snap target whenever the entry was the newest one, pinning to the bottom so streamed content keeps arriving in view. For a trailing entry taller than the window, walking down onto it showed its end, not its beginning.

Fixing one leaves the other symptom, so both scenarios are covered separately.

The fix

  • snapToEntry takes a dir argument (1 for ↓, -1 for ↑). A too-tall entry lands on the edge you are heading towards: ↓ on its first row, ↑ on its last. From there ↑/↓ walk the rest a row at a time (revealMore), so the two together read a long entry continuously in whichever direction you started. Entries that fit, and entries already fully in frame, behave exactly as before.
  • The bottom-follow decision moves out of the component into a new pure snapAnchorForEntry, keyed on where the snap lands (the last screenful) rather than on the entry being the newest. That is what it always meant, and it makes both halves unit-testable — the component half was previously unreachable from the pure-function tests.
  • ConnectApp's ↑/↓ handlers thread the direction through.

Nothing else calls snapToEntry, and this navigation behaviour isn't described in docs/ or skills/, so no doc changes are needed.

Verification

Full CI equivalent run locally on this branch with bun 1.3.14 — typecheck, test, build, and the --compile smoke all clean:

Test Files  22 passed (22)
     Tests  395 passed (395)

test/connect-app.test.ts carries 98 tests, including the 6 new ones: two on snapToEntry for the directional too-tall snap, and a new snapAnchorForEntry suite of four covering both reported scenarios, the genuine bottom-follow case, and the no-move case.

Reverting only src/ui/transcriptRows.ts to main fails 5 of them — notably backs ↑ into a too-tall entry at its LAST line with expected 1 to be 7, i.e. the old code really did return the far top edge. So these are regression tests, not just coverage.

Provenance

The change was written and verified in an Ellipsis sandbox whose token was scoped to the ellipsis-dev/ellipsis monorepo, so it was parked there as a patch doc in ellipsis-dev/ellipsis#6047. This PR is that patch applied to main @ 9d479f1 via git am (clean, no fuzz) and re-verified from scratch here; #6047 is being closed in favour of this one.

🤖 Generated with Claude Code

Walking into an entry taller than the window put the highlight in the wrong place, so a long message was entered from the end you were leaving rather than the end you were heading for.

Two bugs compounded:

1. snapToEntry inferred direction from the entry's POSITION rather than the direction of travel, so it top-aligned any too-tall entry. Backing UP into one from below jumped to its far-away first line instead of the last line just above you. It now takes a dir argument: down lands on the entry's first row, up on its last, and revealMore walks through the rest — the two read the entry continuously either way.

2. ensureVisible discarded the snap target entirely when the entry was the NEWEST one, pinning to the bottom to keep following streamed content. For a trailing entry taller than the window that meant walking down onto it showed its end. The bottom-follow is now keyed on the snap landing on the last screenful, which is what it always meant.

So with two messages (short)(long), ↓ off (short) lands on the top of (long); with (short)(long)(short), ↑ off the trailing (short) lands on the bottom of (long).

The bottom-follow decision moves into a pure snapAnchorForEntry so both halves are covered by tests.

Co-Authored-By: Claude <noreply@anthropic.com>
@hbrooks
hbrooks merged commit cb0507a into main Jul 29, 2026
1 check passed
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