Skip to content

feat(session): bi-directional cursor-based pagination (#6548)#8535

Open
CasualDeveloper wants to merge 7 commits intoanomalyco:devfrom
CasualDeveloper:feat/6548-message-pagination
Open

feat(session): bi-directional cursor-based pagination (#6548)#8535
CasualDeveloper wants to merge 7 commits intoanomalyco:devfrom
CasualDeveloper:feat/6548-message-pagination

Conversation

@CasualDeveloper
Copy link
Contributor

@CasualDeveloper CasualDeveloper commented Jan 14, 2026

Problem

Session message pagination lacked robust cursor-based navigation and reliable boundary behavior in the TUI, making long sessions harder to traverse and risking unbounded in-memory growth.

Solution

  • Implement bi-directional cursor pagination (before / after / oldest) in session + API layers with RFC 8288 Link headers and regenerated SDK types.
  • Wire boundary-aware TUI loading for mouse and command-path scrolling (PgUp/PgDn/line/half-page/Home/End), including edge-aware hints.
  • Normalize pagination errors so non-Error throws do not render as [object Object] and preserve revert markers on oldest/latest jumps while keeping a 500-message memory cap.
  • Add regression coverage across session/API/link-header logic and TUI pagination helper/edge-state flows.

Notes

  • Testing: bun --cwd packages/opencode test test/session/messages-pagination.test.ts test/server/session-messages.test.ts test/cli/tui/pagination-helpers.test.ts test/util/parse-link-header.test.ts (32 pass)
  • Typecheck: bun turbo typecheck (pass)
  • AI Assistance: OpenCode 4abf804 + openai/gpt-5.3-codex (xhigh)
  • Review: Human operator reviewed

Related

@github-actions
Copy link
Contributor

The following comment was made by an LLM, it may be inaccurate:

Potential Duplicate Found

PR #6656: "session: paginate message loading"

This is not a duplicate in the traditional sense—it's a more complete implementation that builds upon and replaces the earlier approach.

@CasualDeveloper CasualDeveloper changed the title feat(session): add bi-directional cursor-based pagination for messages feat(session): bi-directional cursor-based pagination (#6548) Jan 14, 2026
@CasualDeveloper
Copy link
Contributor Author

@rekram1-node hi Aiden, would appreciate your feedback on how I implemented pagination, per your suggestion! :)

@ry2009
Copy link
Contributor

ry2009 commented Jan 14, 2026

Glad I could help kickstart this, I have been a little busy to continue my OSS journey right now. I haven't personally looked through the code in depth (just did a skim) but 1k line changes seems like a lot for one PR (might be better to split it if you can) but once again maybe the code is super clean and simple -- I just haven't sifted through deeply lol. Great work & good luck closing this!

@CasualDeveloper CasualDeveloper force-pushed the feat/6548-message-pagination branch 6 times, most recently from 4f57718 to afe9c53 Compare January 15, 2026 06:35
@CasualDeveloper CasualDeveloper force-pushed the feat/6548-message-pagination branch 3 times, most recently from 4bd8bf6 to 34eea60 Compare January 15, 2026 17:21
CasualDeveloper added a commit to CasualDeveloper/opencode that referenced this pull request Jan 15, 2026
Adds session_list_limit config option to limit sessions displayed in the session list dialog (default: 150).

Limit only applies when not searching; search uses server-side limit of 30.

Remove message_limit as it conflicts with cursor-based pagination (anomalyco#8535) and is now redundant.
ryanwyler added a commit to gignit/opencode-orig that referenced this pull request Jan 15, 2026
Phase 4-6: Complete auto-scroll implementation
- Add KV signal for runtime toggle (persists across sessions)
- Add historyConfig memo with default values
- Implement loadOlder() function:
  * Checks both KV toggle and config.enabled
  * Triggers when scroll position <= load_threshold pixels from top
  * Uses existing loadConversationHistory() with ts_before API
- Implement updateVisibleMessageViews() for batch tracking:
  * Updates lastViewed timestamp for all visible messages
  * Called on scroll events for efficient viewport detection
- Add scroll event handlers:
  * onMouseScroll: triggers auto-load and view tracking
  * onKeyDown: triggers auto-load on up/pageup/home keys
- Add command menu toggle option:
  * Dynamic title based on current state
  * Starts/stops cleanup worker on toggle
  * Uses KV system as designed
- Add lifecycle management:
  * createEffect monitors config and KV toggle
  * Starts worker when both enabled and session active
  * onCleanup ensures worker stops on unmount
- Import onCleanup from solid-js

Complete feature implementation: ~190 lines vs PR anomalyco#8535's ~400 lines
@CasualDeveloper CasualDeveloper force-pushed the feat/6548-message-pagination branch from 34eea60 to 2c70c15 Compare January 17, 2026 05:23
CasualDeveloper added a commit to CasualDeveloper/opencode that referenced this pull request Jan 17, 2026
Adds session_list_limit config option to limit sessions displayed in the session list dialog (default: 150).

Limit only applies when not searching; search uses server-side limit of 30.

Remove message_limit as it conflicts with cursor-based pagination (anomalyco#8535) and is now redundant.
@CasualDeveloper CasualDeveloper force-pushed the feat/6548-message-pagination branch from 2c70c15 to adec748 Compare January 17, 2026 10:15
CasualDeveloper added a commit to CasualDeveloper/opencode that referenced this pull request Jan 17, 2026
Adds session_list_limit config option to limit sessions displayed in the session list dialog (default: 150).

Limit only applies when not searching; search uses server-side limit of 30.

Remove message_limit as it conflicts with cursor-based pagination (anomalyco#8535) and is now redundant.
@CasualDeveloper CasualDeveloper force-pushed the feat/6548-message-pagination branch from adec748 to 01e84ef Compare January 17, 2026 10:46
@CasualDeveloper CasualDeveloper force-pushed the feat/6548-message-pagination branch from 01e84ef to ea25515 Compare January 18, 2026 18:59
CasualDeveloper added a commit to CasualDeveloper/opencode that referenced this pull request Jan 18, 2026
Adds session_list_limit config option to limit sessions displayed in the session list dialog (default: 150).

Limit only applies when not searching; search uses server-side limit of 30.

Remove message_limit as it conflicts with cursor-based pagination (anomalyco#8535) and is now redundant.
CasualDeveloper added a commit to CasualDeveloper/opencode that referenced this pull request Jan 18, 2026
Adds session_list_limit config option to limit sessions displayed in the session list dialog (default: 150).

Limit only applies when not searching; search uses server-side limit of 30.

Remove message_limit as it conflicts with cursor-based pagination (anomalyco#8535) and is now redundant.
@CasualDeveloper CasualDeveloper force-pushed the feat/6548-message-pagination branch 4 times, most recently from 5067f2a to 5f6351c Compare January 30, 2026 03:44
@thdxr thdxr force-pushed the dev branch 3 times, most recently from f1ae801 to 08fa7f7 Compare January 30, 2026 14:37
@CasualDeveloper CasualDeveloper force-pushed the feat/6548-message-pagination branch 4 times, most recently from 0649e5f to feb0c78 Compare February 7, 2026 02:59
@NamedIdentity
Copy link

NamedIdentity commented Feb 7, 2026

Tried out this PR on -dev branch. Running Windows 10. Running dev builds using XDG_HOME.

PR has partial function. Found bugs, and need for some UI changes.

When I tried to scroll to top of a long session, where it normally truncates it won't scroll past using mouse scroll or page up or page down.

when I use page up or page down at these transition border, nothing happens. When I use mouse scroll, I get an error:

Failed to load: [object Object]

If I press Home or End, it will cycle to the next 'block' of pages. I can then get to the 'top'.

Also, annoying notifications intruding.

(scroll down for more)
(scroll to retry)

Those should be removed for sure. TUI has limited viewing area and using up an entire line constantly isn't appropriate. It should be conditional; only when at a border transition area should it pop up.

image

NamedIdentity added a commit to NamedIdentity/opencode that referenced this pull request Feb 7, 2026
…oundary hints

- Fix '[object Object]' error display: extract message from non-Error thrown objects
- Fix PgUp/PgDn/line/half-page scroll not triggering page loads at boundaries
- Make '(scroll up/down for more)' hints only show near page boundaries
- Add missing Binary.lowerBound helper that PR anomalyco#8535 referenced but didn't include
@NamedIdentity
Copy link

NamedIdentity commented Feb 7, 2026

@CasualDeveloper
I pointed Claude at the bugs and found and fixed them. Or Claude did. I note now there is no 'border' just one clean scroll to top. I noticed no performance issues, so I figure, maybe this is better than the prior system. It works, and that's what matters to me. I can finally see the start of messages and figure out what was going on in sessions.

Confirmed it's working. PR submitted to your PR/fork. CasualDeveloper#1

Add a lower-bound binary search helper used by cursor pagination to locate insertion and boundary indexes efficiently.
Teach session message listing to handle before/after cursors with stable ordering so pagination windows can be traversed in both directions.
Expose before/after pagination params in the session messages route and update OpenAPI so clients can request paged message windows.
Introduce session pagination state management, bounded in-memory windows, and link-header parsing utilities to support incremental loading in the TUI.
Connect scroll commands and boundary loading in the session view, add edge-aware hint visibility, and harden pagination error and revert-marker handling for navigation jumps.
Add regression coverage for session/api pagination, Link header parsing, and TUI pagination helper flows including edge-state updates after boundary loads.
@CasualDeveloper CasualDeveloper force-pushed the feat/6548-message-pagination branch from feb0c78 to 49838f6 Compare February 7, 2026 15:35
@CasualDeveloper
Copy link
Contributor Author

CasualDeveloper commented Feb 7, 2026

Quick update: I integrated and validated the feedback from @NamedIdentity repro report, then rebased/cleaned branch history and force-pushed the final state (head 49838f685).

Integrated fixes:

  • normalize pagination error rendering so non-Error throws do not show [object Object]
  • ensure command-path scrolling (PgUp/PgDn/line/half-page/Home/End) reliably triggers boundary page loads
  • make boundary hints edge-aware and keep edge state refreshed after async/programmatic scroll updates
  • preserve revert-marker behavior when jumping to oldest
  • add regression coverage for session/API/link-header and TUI pagination helper flows

Validation on this branch:

  • bun --cwd packages/opencode test test/session/messages-pagination.test.ts test/server/session-messages.test.ts test/cli/tui/pagination-helpers.test.ts test/util/parse-link-header.test.ts (32 pass)
  • bun turbo typecheck (pass)

@NamedIdentity
Copy link

@CasualDeveloper

I pulled your latest update. Tested and it works. One small issue with keybinds found by Claude Opus 4.6 during merging and checks which it decided it needed to fix. Details below:

One small issue: messages_line_up and messages_line_down are referenced as keybind names in the "Line up"/"Line down" commands (index.tsx, lines ~728/739) but aren't defined in the keybinds schema in config.ts or the v2 SDK types. This causes typecheck errors:

error TS2820: Type '"messages_line_up"' is not assignable to type 'keyof KeybindsConfig | undefined'.
error TS2820: Type '"messages_line_down"' is not assignable to type 'keyof KeybindsConfig | undefined'.

They just need entries added to the keybinds object in config.ts (around the messages_half_page_down area) and an SDK regeneration. Something like:

messages_line_up: z.string().optional().describe("Scroll messages up by one line"),
messages_line_down: z.string().optional().describe("Scroll messages down by one line"),


@CasualDeveloper
Copy link
Contributor Author

CasualDeveloper commented Feb 7, 2026

@NamedIdentity Thanks again for checking this.

I re-verified directly on the current PR head 49838f685, and the messages_line_up / messages_line_down keybind wiring is already present in config, SDK types, and TUI mappings:

  • packages/opencode/src/config/config.ts:791
  • packages/opencode/src/config/config.ts:792
  • packages/sdk/js/src/v2/gen/types.gen.ts:1040
  • packages/sdk/js/src/v2/gen/types.gen.ts:1044
  • packages/opencode/src/cli/cmd/tui/routes/session/index.tsx:685
  • packages/opencode/src/cli/cmd/tui/routes/session/index.tsx:696
  • packages/opencode/src/server/routes/tui.ts:278
  • packages/opencode/src/server/routes/tui.ts:279

For additional context: this PR does not modify packages/opencode/src/config/config.ts. Those keybind entries were introduced earlier in bfb8c531c, and they are already present in the PR merge base (4abf8049) and current dev.

I also reran typecheck on this head (bun turbo typecheck) and it passes.

Could you confirm your local checkout is at 49838f685 and rebuilt from that commit? If you can still reproduce after that, share your exact steps (or an OpenCode session share) and I’ll dig in right away.

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.

[FEATURE] Virtualized scrolling + paginated message loading for long sessions (#8535)

3 participants