Skip to content

fix(app): own session timeline scrolling after submit#521

Merged
Astro-Han merged 11 commits into
devfrom
codex/i516-scroll-owner
May 10, 2026
Merged

fix(app): own session timeline scrolling after submit#521
Astro-Han merged 11 commits into
devfrom
codex/i516-scroll-owner

Conversation

@Astro-Han

@Astro-Han Astro-Han commented May 10, 2026

Copy link
Copy Markdown
Owner

Summary

Adds a session timeline scroll controller so the main conversation timeline no longer treats raw scroll position as user intent. The controller owns submit/latest recovery, explicit navigation intents, message-anchor safe positions, dock/content observations, owner cleanup, and typed diagnostics.

Why

Closes #516.

The recurring jump-to-top bug came from split scroll ownership: createAutoScroll, scroll dock, history window, hash scroll, timeline gesture handling, and dock resize paths could all interpret the same movement. A submit-time layout reset could therefore be recorded as user_scrolled=true and cancel latest recovery.

Related Issue

Closes #516

Human Review Status

Pending. A human should make the final merge decision after reviewing the final diff and verification evidence.

Review Focus

  • SessionTimelineScrollController state transitions and recovery reasons.
  • The message-timeline.tsx scroll path: rejected controller observations should not continue into old auto-scroll/user-scroll handling.
  • use-session-timeline-interaction.ts recovery scheduling and owner checks.
  • ScrollView intent reporting before keyboard/thumb mechanical scrolling.
  • Renderer diagnostic sanitizer allowlist for session.timeline.scroll_controller.

Risk Notes

Visible session timeline behavior changed. Submit now explicitly returns the main timeline to latest, including when submitting from reading/hash mode. This matches the accepted #516 design, but it is the main product behavior to review.

Electron manual verification was not run because this worktree's packages/app/AGENTS.md says never to restart the app/server process. Targeted web E2E and typecheck were run instead.

How To Verify

Controller + anchor + scroll/hash unit tests: 45 passed
(cd packages/app && bun test --preload ./happydom.ts ./src/pages/session/session-timeline-scroll-controller.test.ts ./src/pages/session/session-timeline-scroll-anchors.test.ts ./src/pages/session/use-session-scroll-dock.test.ts ./src/pages/session/use-session-hash-scroll.test.ts)

ScrollView unit tests: 5 passed
(cd packages/ui && bun test src/components/scroll-view.test.ts)

Renderer diagnostics sanitizer tests: 16 passed
(cd packages/desktop-electron && bun test src/main/renderer-diagnostics.test.ts)

App typecheck: passed
bun --cwd packages/app typecheck

Focused submit/top-reset + scrollbar-thumb E2E: 2 passed
bun run --cwd packages/app test:e2e -- session/session-renderer-diagnostics.spec.ts -g "captures renderer diagnostics|honors scrollbar thumb drag after submit"

Existing scroll-position E2E: 4 passed
bun run --cwd packages/app test:e2e -- session/session-scroll-position.spec.ts

Diff check: no whitespace errors
git diff --check

Screenshots or Recordings

Not included. This is visible behavior, but the changed surface is scroll recovery and diagnostics rather than static UI. The targeted Playwright E2E covers the reproduced user path.

Checklist

  • Human review status is stated above as pending, approved, or not required
  • I linked the related issue, or stated why there is no issue
  • This PR has type, primary area, and priority labels, or I requested maintainer labeling
  • I described the review focus and any meaningful risks
  • I listed the relevant verification steps and the key result for each
  • I did not introduce unrelated refactors, dependencies, generated files, or file changes beyond the stated scope
  • I manually checked visible UI or copy changes when needed, with screenshots or recordings
  • I considered macOS and Windows impact for platform, packaging, updater, signing, paths, shell, or permissions changes
  • I called out docs, release notes, dependencies, permissions, credentials, deletion behavior, generated content, or local file changes when relevant
  • I reviewed the final diff for unrelated changes and suspicious dependency changes
  • I am targeting dev, and my PR title and commit messages use Conventional Commits in English

@Astro-Han Astro-Han added bug Something isn't working P1 High priority app Application behavior and product flows ui Design system and user interface labels May 10, 2026
@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@Astro-Han has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 52 minutes and 36 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8772c2a5-f01d-4263-b2fc-081af4508117

📥 Commits

Reviewing files that changed from the base of the PR and between 043c044 and 2dc84dc.

📒 Files selected for processing (9)
  • packages/app/e2e/selectors.ts
  • packages/app/e2e/session/session-renderer-diagnostics.spec.ts
  • packages/app/src/pages/session/message-timeline.tsx
  • packages/app/src/pages/session/session-timeline-scroll-controller.test.ts
  • packages/app/src/pages/session/session-timeline-scroll-controller.ts
  • packages/app/src/pages/session/use-session-hash-scroll.test.ts
  • packages/app/src/pages/session/use-session-hash-scroll.ts
  • packages/app/src/pages/session/use-session-timeline-interaction.ts
  • packages/ui/src/components/scroll-view.tsx
📝 Walkthrough

Walkthrough

This PR implements a comprehensive session timeline scroll controller addressing recurring jump-to-top regressions. It introduces a stateful controller distinguishing between following latest, reading history, and targeting messages, with safe-position sampling and restoration across submits, gestures, and layout resets.

Changes

Session Timeline Scroll Ownership System

Layer / File(s) Summary
Domain Model & Public Types
packages/app/src/pages/session/session-timeline-scroll-controller.ts
Defines scroll modes (following_latest, reading_history, targeting_message), safe positions (latest/reading/target), scroll intents (keyboard/wheel/touch/jump/submit/target), observations (scroll/resize/owner), recovery outcomes, and diagnostic event types.
Scroll Anchor Utilities
packages/app/src/pages/session/session-timeline-scroll-anchors.ts
Implements collectTimelineScrollMetrics for viewport state, sampleTimelineSafePosition to capture anchors by mode (latest/reading/target), and restoreTimelineSafePosition to restore viewport to saved positions with geometric validation and error handling.
Scroll Controller State Machine
packages/app/src/pages/session/session-timeline-scroll-controller.ts
Implements createSessionTimelineScrollController with intent/observe/detach operations, maintaining scroll mode, protections, recovery state, and computing acceptance/recovery outcomes based on explicit intents, gesture strength, and scroll observations.
Scroll Anchor Unit Tests
packages/app/src/pages/session/session-timeline-scroll-anchors.test.ts
Validates metric collection, safe-position sampling across all modes, position restoration with edge cases, and error handling for missing anchors using stubbed DOM geometry.
Scroll Controller Unit Tests
packages/app/src/pages/session/session-timeline-scroll-controller.test.ts
Tests controller behavior: submit-with-restore, history navigation, scrollbar-drag protection, wheel gesture strength, anchor preservation across resizes, owner detachment, and diagnostic event emission.
ScrollView Intent Emission
packages/ui/src/components/scroll-view.tsx
Adds onScrollIntent callback and emits keyboard/scrollbar-drag intents with scroll metrics; introduces scrollViewKeyboardIntent() and scrollViewMetrics() helpers.
ScrollView Intent Tests
packages/ui/src/components/scroll-view.test.ts
Validates keyboard intent classification and scrollbar-drag metric capture.
MessageTimeline Controller Wiring
packages/app/src/pages/session/message-timeline.tsx
Adds onTimelineScrollIntent and onTimelineScrollObservation props; converts ScrollView intents to timeline intents, emits observations on scroll/resize, and gates scroll-sample handling based on controller acceptance.
SessionMainView Callback Props
packages/app/src/pages/session/session-main-view.tsx
Extends SessionMainView to accept and forward timeline scroll callbacks to MessageTimeline.
Session Timeline Interaction & Recovery
packages/app/src/pages/session/use-session-timeline-interaction.ts
Centralizes scroll behavior with persistent controller instance, lifecycle management, recovery scheduling, intent/observation handlers with optional bottom-follow-lock cancellation, and submitLatest for coordinated submit+restore.
Scroll Dock Metrics & Dock Kind
packages/app/src/pages/session/use-session-scroll-dock.ts
Reports dockKind from dataset in dock-height callbacks and emits onContentResize observations with scrollTop and distanceFromBottom.
Scroll Dock Tests
packages/app/src/pages/session/use-session-scroll-dock.test.ts
Updates test type signatures to include "prompt" in dockKind union.
Session Composer Region Dock Kind
packages/app/src/pages/session/composer/session-composer-region.tsx
Computes dockKind memo and renders data-dock-kind attribute for DOM detection.
Hash Scroll & Message Navigation
packages/app/src/pages/session/use-session-hash-scroll.ts
Changes onMessageNavigation callback to accept message ID; emits target_message intents with alignment instead of directly canceling bottom-follow lock.
Hash Navigation Tests
packages/app/src/pages/session/use-session-hash-scroll.test.ts
Validates message navigation dispatches target_message intents with message IDs.
Session Page Submission Wiring
packages/app/src/pages/session.tsx
Wires submitLatest from timeline interaction into composer callbacks and connects timeline scroll handlers to SessionMainView.
Renderer Diagnostics
packages/desktop-electron/src/main/renderer-diagnostics.ts, packages/desktop-electron/src/main/renderer-diagnostics.test.ts
Allowlists session.timeline.scroll_controller event fields and adds dock_kind to session.layout.composer_dock allowlist; validates sanitization preserves expected fields.
End-to-End Tests
packages/app/e2e/session/session-renderer-diagnostics.spec.ts, packages/app/e2e/session/session-scroll-position.spec.ts
Adds E2E helpers for timeline thumb drag; validates submit-time restore-latest behavior, absence of unwanted restore after drag, and diagnostic event emission.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • #516: This PR directly implements the session timeline scroll controller redesign and state machine specified in this task, including safe-position restoration, recovery handling, and E2E coverage for submit-time top resets.
  • #496: The new controller and restore-latest-after-top-reset behavior target the same unexpected post-submit jump-to-top symptom in this issue.
  • #467: The submit-triggered jump-to-top behavior and protections added in this PR directly address the same code path issue.

Possibly related PRs

  • Astro-Han/pawwork#402: Modifies use-session-hash-scroll.ts and use-session-timeline-interaction.ts for hash navigation and timeline recovery handling.
  • Astro-Han/pawwork#354: Modifies session timeline/scrolling subsystem including timeline interaction and scroll-dock behavior.
  • Astro-Han/pawwork#498: Modifies session scroll/dock and timeline handling for submit/resume and user-gesture protection.

Poem

🐰 Hops through timelines with care,
Scroll position anchored right there,
Latest or history, the controller knows,
No jumpy resets, just smooth scroll flows! 📜✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly summarizes the main change: adding session timeline scroll ownership after submit to fix the recurring jump-to-top bug.
Linked Issues check ✅ Passed The PR comprehensively addresses issue #516's objectives: implements a session timeline scroll controller with state machine, separates user intent from programmatic movement, adds diagnostics, provides targeted E2E coverage for submit-time top resets, and preserves reading history.
Out of Scope Changes check ✅ Passed All code changes are directly related to centralizing session timeline scroll ownership and implementing the controller pattern. No unrelated refactors, dependency changes, or out-of-scope file modifications are present.
Description check ✅ Passed PR description is comprehensive and fully addresses the template requirements with clear sections for summary, motivation, issue link, review focus, risks, verification, and checklist confirmation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/i516-scroll-owner

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a comprehensive session timeline scroll controller to manage different scroll modes, including following the latest messages, reading history, and targeting specific messages. It adds logic for scroll intent classification, metric collection, and anchor-based scroll restoration to improve the user experience during layout shifts and content updates. Feedback focuses on preventing automated scroll snapping during manual scrollbar interaction, optimizing the performance of visible message detection to avoid layout thrashing, and refining diagnostic reason strings for better observability.

Comment thread packages/app/src/pages/session/session-timeline-scroll-anchors.ts
Comment thread packages/app/src/pages/session/session-timeline-scroll-controller.ts Outdated
Comment thread packages/app/src/pages/session/session-timeline-scroll-controller.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/app/src/pages/session/use-session-hash-scroll.ts (1)

125-132: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid duplicate navigation intent emission for hash-resolved messages.

At Line 127, applyHash() emits onMessageNavigation, then Line 131 calls scrollToMessage(), which emits the same callback again at Line 94. That can produce duplicate target_message intents.

Suggested patch
   const messageId = messageIdFromHash(hash)
   if (messageId) {
-    input.onMessageNavigation?.(messageId)
     input.autoScroll.pause()
     const msg = messageById().get(messageId)
     if (msg) {
       scrollToMessage(msg, behavior)
       return
     }
+    input.onMessageNavigation?.(messageId)
     return
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app/src/pages/session/use-session-hash-scroll.ts` around lines 125 -
132, The code in applyHash emits the navigation intent twice: it calls
input.onMessageNavigation?.(messageId) before calling scrollToMessage, but
scrollToMessage already emits the same callback; remove the redundant
pre-emission. Concretely, in the block that resolves messageId via
messageIdFromHash and pauses auto-scroll (the code that also calls
messageById().get(messageId)), delete the direct call to
input.onMessageNavigation?.(messageId) so only scrollToMessage(msg, behavior)
triggers the navigation intent (or alternatively remove the emission inside
scrollToMessage if you prefer that single source of truth).
🧹 Nitpick comments (3)
packages/app/e2e/session/session-renderer-diagnostics.spec.ts (1)

167-184: ⚡ Quick win

Avoid binding this E2E to ScrollView CSS internals.

timelineThumbBox() reaches into .scroll-view and .scroll-view__thumb, so a harmless class rename in packages/ui will break this spec. Please expose a data-component/data-action hook for the scrollbar thumb and query that instead.

As per coding guidelines, use data-component, data-action, or semantic roles for selectors instead of CSS class names or IDs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app/e2e/session/session-renderer-diagnostics.spec.ts` around lines
167 - 184, The test helper timelineThumbBox currently depends on internal CSS
classes (".scroll-view" and ".scroll-view__thumb"); update the UI ScrollView
component to add a stable data attribute (e.g. data-component="scroll-view" on
the scroll container and data-action="scroll-thumb" on the thumb) and then
change timelineThumbBox to query for those data attributes (use the existing
scrollViewportSelector param for finding the viewport/root and replace
".scroll-view__thumb" with the new data-action selector) so the spec relies on
stable data hooks instead of implementation CSS classes.
packages/app/src/pages/session/use-session-timeline-interaction.ts (2)

77-83: 💤 Low value

Consider also detaching the controller on owner cleanup.

onCleanup cancels a pending recovery frame but never invokes scrollController.detach({ ... }). The session-change effect at lines 62-75 properly detaches the previous controller, but on Solid owner disposal (component unmount, route change) the controller silently goes out of scope without emitting the owner_detached diagnostic that the controller is otherwise designed to produce. If the diagnostic timeline is used to verify lifecycle, missing detach events on unmount will look like the previous owner is still active.

♻️ Proposed fix
   onCleanup(cancelRecoveryFrame)
+  onCleanup(() => {
+    const owner = scrollController.state()
+    scrollController.detach({
+      sessionOwner: owner.sessionOwner,
+      viewportOwner: owner.viewportOwner,
+    })
+  })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app/src/pages/session/use-session-timeline-interaction.ts` around
lines 77 - 83, onCleanup currently only cancels the pending recoveryFrame but
does not detach the scroll controller, so the controller never emits its
owner_detached diagnostic on component unmount; update the cleanup handler (the
function referenced by onCleanup/cancelRecoveryFrame) to also check for the
scrollController instance and call its detach method (e.g.,
scrollController.detach(...)) with the appropriate owner_detached
reason/metadata used elsewhere so the controller emits the same diagnostic on
owner disposal as it does during session-change handling.

38-75: ⚡ Quick win

Use the factory for the initial controller too.

The inline controller construction at lines 39-48 duplicates the body of createScrollController at lines 51-60 verbatim. Any future change (e.g., new option, different diagnostic transform) has to be made in two places and is easy to miss. Reusing the factory for the initial seed eliminates that drift.

♻️ Proposed fix
-  let scrollController = createSessionTimelineScrollController({
-    sessionOwner: input.sessionKey(),
-    viewportOwner: `timeline:${input.sessionKey()}`,
-    routeSessionID: input.routeSessionID(),
-    visibleSessionID: input.sessionID(),
-    timelineSessionID: input.sessionID(),
-    emitDiagnostic: (event) => {
-      void emitRendererDiagnostic(event).catch(() => {})
-    },
-  })
-
-  const createScrollController = () =>
-    createSessionTimelineScrollController({
-      sessionOwner: input.sessionKey(),
-      viewportOwner: `timeline:${input.sessionKey()}`,
-      routeSessionID: input.routeSessionID(),
-      visibleSessionID: input.sessionID(),
-      timelineSessionID: input.sessionID(),
-      emitDiagnostic: (event) => {
-        void emitRendererDiagnostic(event).catch(() => {})
-      },
-    })
+  const createScrollController = () =>
+    createSessionTimelineScrollController({
+      sessionOwner: input.sessionKey(),
+      viewportOwner: `timeline:${input.sessionKey()}`,
+      routeSessionID: input.routeSessionID(),
+      visibleSessionID: input.sessionID(),
+      timelineSessionID: input.sessionID(),
+      emitDiagnostic: (event) => {
+        void emitRendererDiagnostic(event).catch(() => {})
+      },
+    })
+
+  let scrollController = createScrollController()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app/src/pages/session/use-session-timeline-interaction.ts` around
lines 38 - 75, The initial scrollController is constructed inline duplicating
createScrollController; instead call createScrollController to seed
scrollController so there's a single factory point. Replace the inline
createSessionTimelineScrollController usage that assigns scrollController (the
block creating recoveryFrame and scrollController) with: let scrollController =
createScrollController() (keeping recoveryFrame), ensuring the
createScrollController function (which wraps
createSessionTimelineScrollController and the emitRendererDiagnostic wrapper) is
the sole place constructing the controller referenced by createEffect (on
input.sessionKey()/input.sessionID()).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/app/src/pages/session/message-timeline.tsx`:
- Around line 302-303: The timeline scroll-intent path doesn't mark legacy
user-scroll state, so keyboard arrow and other explicit intents never set
hasScrollGesture and leave createAutoScroll.userScrolled() false; in the
onTimelineScrollIntent handler (and any place that forwards
TimelineScrollIntent), detect explicit user intents (e.g. keyboard
ArrowUp/ArrowDown and scrollbar-drag/drag-like intents) and call the same legacy
bookkeeping used by pointer/wheel handlers: invoke markScrollGesture() (or
directly call
createAutoScroll.userScrolled()/onUserScroll()/onAutoScrollHandleScroll() as the
pointer handlers do) so hasScrollGesture becomes true and downstream
dock-resize/auto-scroll logic respects intent.

In `@packages/app/src/pages/session/session-timeline-scroll-controller.ts`:
- Around line 556-564: In the branch inside session-timeline-scroll-controller
where state.mode === "following_latest" you should not set reason:
"explicit_bottom_navigation" for the restore_latest recovery because that label
is reserved for user-driven bottom navigations; instead, set the reason to a
more accurate value — use "submit_follow_latest" when submitOriginMode is
present/true (preserving existing submit-origin semantics) or use a new
dedicated reason like "follow_latest_preserved" (or
"content_resize_preserve_latest") otherwise; update the object returned by
result(...) (the recovery.type: "restore_latest" payload and its reason field)
and ensure the new reason is added to the TimelineScrollReason union so
diagnostics remain self-describing.

---

Outside diff comments:
In `@packages/app/src/pages/session/use-session-hash-scroll.ts`:
- Around line 125-132: The code in applyHash emits the navigation intent twice:
it calls input.onMessageNavigation?.(messageId) before calling scrollToMessage,
but scrollToMessage already emits the same callback; remove the redundant
pre-emission. Concretely, in the block that resolves messageId via
messageIdFromHash and pauses auto-scroll (the code that also calls
messageById().get(messageId)), delete the direct call to
input.onMessageNavigation?.(messageId) so only scrollToMessage(msg, behavior)
triggers the navigation intent (or alternatively remove the emission inside
scrollToMessage if you prefer that single source of truth).

---

Nitpick comments:
In `@packages/app/e2e/session/session-renderer-diagnostics.spec.ts`:
- Around line 167-184: The test helper timelineThumbBox currently depends on
internal CSS classes (".scroll-view" and ".scroll-view__thumb"); update the UI
ScrollView component to add a stable data attribute (e.g.
data-component="scroll-view" on the scroll container and
data-action="scroll-thumb" on the thumb) and then change timelineThumbBox to
query for those data attributes (use the existing scrollViewportSelector param
for finding the viewport/root and replace ".scroll-view__thumb" with the new
data-action selector) so the spec relies on stable data hooks instead of
implementation CSS classes.

In `@packages/app/src/pages/session/use-session-timeline-interaction.ts`:
- Around line 77-83: onCleanup currently only cancels the pending recoveryFrame
but does not detach the scroll controller, so the controller never emits its
owner_detached diagnostic on component unmount; update the cleanup handler (the
function referenced by onCleanup/cancelRecoveryFrame) to also check for the
scrollController instance and call its detach method (e.g.,
scrollController.detach(...)) with the appropriate owner_detached
reason/metadata used elsewhere so the controller emits the same diagnostic on
owner disposal as it does during session-change handling.
- Around line 38-75: The initial scrollController is constructed inline
duplicating createScrollController; instead call createScrollController to seed
scrollController so there's a single factory point. Replace the inline
createSessionTimelineScrollController usage that assigns scrollController (the
block creating recoveryFrame and scrollController) with: let scrollController =
createScrollController() (keeping recoveryFrame), ensuring the
createScrollController function (which wraps
createSessionTimelineScrollController and the emitRendererDiagnostic wrapper) is
the sole place constructing the controller referenced by createEffect (on
input.sessionKey()/input.sessionID()).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 37480b01-f87e-4728-8e2b-982e65d6a40d

📥 Commits

Reviewing files that changed from the base of the PR and between 310a1de and 043c044.

📒 Files selected for processing (19)
  • packages/app/e2e/session/session-renderer-diagnostics.spec.ts
  • packages/app/e2e/session/session-scroll-position.spec.ts
  • packages/app/src/pages/session.tsx
  • packages/app/src/pages/session/composer/session-composer-region.tsx
  • packages/app/src/pages/session/message-timeline.tsx
  • packages/app/src/pages/session/session-main-view.tsx
  • packages/app/src/pages/session/session-timeline-scroll-anchors.test.ts
  • packages/app/src/pages/session/session-timeline-scroll-anchors.ts
  • packages/app/src/pages/session/session-timeline-scroll-controller.test.ts
  • packages/app/src/pages/session/session-timeline-scroll-controller.ts
  • packages/app/src/pages/session/use-session-hash-scroll.test.ts
  • packages/app/src/pages/session/use-session-hash-scroll.ts
  • packages/app/src/pages/session/use-session-scroll-dock.test.ts
  • packages/app/src/pages/session/use-session-scroll-dock.ts
  • packages/app/src/pages/session/use-session-timeline-interaction.ts
  • packages/desktop-electron/src/main/renderer-diagnostics.test.ts
  • packages/desktop-electron/src/main/renderer-diagnostics.ts
  • packages/ui/src/components/scroll-view.test.ts
  • packages/ui/src/components/scroll-view.tsx

Comment thread packages/app/src/pages/session/message-timeline.tsx
@Astro-Han Astro-Han merged commit 5d4e351 into dev May 10, 2026
20 checks passed
@Astro-Han Astro-Han deleted the codex/i516-scroll-owner branch May 10, 2026 16:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app Application behavior and product flows bug Something isn't working P1 High priority ui Design system and user interface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Task] Redesign session timeline scroll ownership to stop recurring jump-to-top regressions

1 participant