refactor(ui): scroll mechanism — derive paged, defer render, split edges

Four-level refactor of the chat-window scrolling state:

L1. Extract win_page_space() and unify the two-branch clamp at the end of
    win_page_down into a single last_page_start formula. Removes the
    duplicated `getmaxy(stdscr) - 4` magic across page_up / page_down /
    sub_page_up / sub_page_down and the off-by-one between the two clamp
    branches.

L2. Remove ProfLayout::paged sticky flag. Replace with win_is_paged(),
    derived from y_pos and the current pad height. Eliminates the entire
    bug class of "stuck paged=1" — there is no flag to drift, no manual
    reset to forget. The earlier non-CHAT bottom-reach reset, the
    WIN_SCROLL_REACHED_BOTTOM reset, the win_clear paged=1 line, and the
    cl_ev_send_ai_msg paged=0 workaround all collapse into "y_pos governs
    everything." cl_ev_send_ai_msg now calls win_move_to_end so the user's
    own message is visible after scroll-up.

L3. _win_printf no longer drops messages when paged. The line is appended
    to the buffer and to the curses pad unconditionally; it lands below
    the visible viewport so the user's reading position is unchanged. The
    unread badge still increments. Suppressed messages are no longer lost
    forever — page-down naturally reveals them.

L4. Replace the win_scroll_state_t enum (INNER / REACHED_TOP /
    REACHED_BOTTOM, conflating layout edge with DB exhaustion) with a
    ScrollEdges struct of two independent booleans:
      db_exhausted_above — a previous page-up DB fetch returned empty
      db_exhausted_below — same for page-down
    The flags are flipped to TRUE on DB_RESPONSE_EMPTY and cleared by a
    scroll in the opposite direction (so MAM late-delivery and /history
    switch can re-trigger fetches). Buffer-edge state is no longer
    encoded here — y_pos handles that.

Tests: stub_ui gets a no-op win_move_to_end. 545/545 unit tests pass.
This commit is contained in:
2026-05-10 16:17:01 +03:00
parent c167f48714
commit 2c74c0a058
7 changed files with 101 additions and 79 deletions

View File

@@ -301,11 +301,10 @@ cl_ev_send_ai_msg(ProfAiWin* aiwin, const char* const message, const char* const
return;
}
/* Reset paged flag before printing user message.
* If the user scrolled up to view history, paged=1 would suppress
* the message in _win_printf(). Reset it here so the message displays. */
aiwin->window.layout->paged = 0;
aiwin->window.layout->unread_msg = 0;
// Snap viewport to bottom so the user's own message is visible even if
// they had scrolled up to read history; otherwise win_is_paged() in
// _win_printf would suppress the print.
win_move_to_end(&aiwin->window);
// Display user message in AI window.
win_print_outgoing(&aiwin->window, ">>", id, NULL, message);