Skip to content

fix(tui): Old messages disappearing during long sessions#26861

Open
vpetrigo wants to merge 15 commits into
anomalyco:devfrom
vpetrigo:issue/7380
Open

fix(tui): Old messages disappearing during long sessions#26861
vpetrigo wants to merge 15 commits into
anomalyco:devfrom
vpetrigo:issue/7380

Conversation

@vpetrigo
Copy link
Copy Markdown

@vpetrigo vpetrigo commented May 11, 2026

Issue for this PR

Fix #7380

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

  1. Add lazy-scroll loading:
    • Scrolling up: When user reaches within 5px of the top, loads next 50 older messages via before cursor
    • Scrolling down: When user reaches within 5px of the bottom, loads next 50 newer messages via after cursor
  2. Timeline Dialog Complete Load: When Timeline dialog opens, exhaustively loads all session's related messages. Also, use full DD/MM/YYYY HH:MM timestamp format in footer

If you paste a large clearly AI generated description here your PR may be IGNORED or CLOSED!

How did you verify your code works?

  1. Open a session with 100+ historical messages
  2. Scroll up: loading indicator appears near top, next 50 messages prepended, visual position preserved
  3. Continue scrolling up: cycle repeats until first message visible
  4. Open Timeline dialog: all prompts appear with full date/time footers
  5. Select old message from Timeline: view scrolls to it correctly
  6. Send new messages: old messages remain (not evicted while streaming)
  7. Test short sessions (<100 messages): no regressions

Screenshots / recordings

Original behavior

original_issue

Updated behavior

fixed

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

If you do not follow this template your PR will be automatically rejected.

@vpetrigo vpetrigo force-pushed the issue/7380 branch 3 times, most recently from 15de57d to 11465c2 Compare May 12, 2026 21:37
vpetrigo added 5 commits May 13, 2026 21:12
Long sessions previously dropped older messages from the in-memory
store: the initial sync was capped at 100 and a per-event eviction
removed the oldest message whenever new ones arrived, leaving the
chat starting mid-conversation and the Timeline / Jump-to-Message
features unable to reach the missing entries.

- Remove the 100-message limit from the initial session sync so all
  historical messages are loaded.
- Remove the eviction block from the message.updated handler so older
  messages are no longer purged when new ones arrive.
- Add Locale.datetimeFull (DD/MM/YYYY HH:MM) and use it for the
  Timeline footer so each entry shows full date and time instead of
  just time.
The v1 messages endpoint previously only supported backward
pagination via the `before` cursor. Adding an `after` cursor enables
the TUI to recover messages that have been evicted from the in-memory
window when the user scrolls back toward the live tail.

- MessageV2.page accepts an optional `after` cursor (mutually
  exclusive with `before`); when set, it returns the next page in
  ascending chronological order using a `newer()` predicate.
- Handler validates that only one of `before`/`after` is supplied,
  decodes the cursor, and emits the `Link` / `X-Next-Cursor` headers
  with the correct direction parameter.
- MessagesQuery schema gains an optional `after` field.
- Tests cover forward pagination across multiple pages and the
  before+after-together rejection path.
Replaces the unbounded "load all messages on session sync" path with
a windowed loader that keeps memory bounded for long sessions while
still letting the user reach any message via scrolling or the
Timeline.

sync.tsx (TUI sync context):
- Initial sync now fetches the most-recent 100 messages and captures
  the `X-Next-Cursor` response header into `messageOlderCursor`.
- New helpers:
  - loadOlderMessages: fetches a 50-message page using the older
    cursor and prepends.
  - loadNewerMessages: fetches a 50-message page using the newer
    cursor and appends. Used after eviction to recover the live tail.
  - trimNewerMessages / trimOlderMessages: cap the in-memory window
    by dropping from the tail/head and recording a cursor for
    re-fetch. Eviction skips assistant messages still streaming
    (`time.completed` unset) so live output is never lost mid-turn.
  - loadAllMessages: paginate exhaustively in both directions
    (consumed by the Timeline dialog).
- Live event handling honours the eviction state:
  - `message.updated` for an ID past the windowed tail is dropped
    when `messageNewerCursor` is set; insertions for IDs already in
    the window still update normally.
  - `message.part.updated` no longer creates orphan entries for
    evicted messages.

SDK v2 client:
- SessionMessagesData and OpencodeClient.messages now accept the new
  `after` query parameter.
- openapi.json mirrors the schema change.
Wires the new pagination helpers into the chat surface:

- maybeLoadOlderMessages / maybeLoadNewerMessages run after every
  scroll input (key bindings: page/half-page/line up & down, first
  & last; mouse wheel via onMouseScroll). They fire only when the
  viewport is within five rows of the corresponding edge and a
  cursor is available.
- After a successful prepend, the view restores the previous logical
  scroll position by adjusting for the height delta so the user does
  not jump.
- When the in-memory window grows past 200 messages and the user is
  far from the opposite edge (>4 viewports away), the matching trim
  helper evicts the now-distant side; the streaming guard inside
  trimNewerMessages prevents dropping an in-flight assistant message.
- A loader spinner is shown at the top of the scrollbox while older
  messages are being fetched, and at the bottom while newer ones are
  being recovered after eviction.

Timeline dialog kicks off `loadAllMessages` on mount so every prompt
in the session becomes selectable, even ones that were never within
the live window.
vpetrigo added 6 commits May 13, 2026 22:03
- Changed all 3 occurrences of "X-Next-Cursor"  "x-next-cursor" for
case-insensitive header lookup reliability
- Fix scrolling in the `maybeLoadOlderMessages` and
`maybeLoadNewerMessages`
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.

old messages disappear

1 participant