Compare commits

...

3 Commits

Author SHA1 Message Date
5acd062c2d fix(ui): reclaim pad by dead space instead of absolute height
All checks were successful
CI Code / Check coding style (pull_request) Successful in 32s
CI Code / Check spelling (pull_request) Successful in 18s
CI Code / Linux (debian) (pull_request) Successful in 4m39s
CI Code / Code Coverage (pull_request) Successful in 3m28s
CI Code / Linux (ubuntu) (pull_request) Successful in 4m52s
CI Code / Linux (arch) (pull_request) Successful in 6m34s
Variant A (PAD_THRESHOLD=12000) bounds the pad but still re-enters win_redraw() from the print path on every message once the *live* buffer renders >= the threshold — reachable with a single oversized message (huge paste, narrow terminal, crafted incoming), reintroducing the per-message redraw storm.

Trigger the reclaim redraw on dead space (getcury - buffer->lines) instead of absolute pad height. After a redraw the cursor equals buffer->lines, so dead space is 0 regardless of buffer size: the reclaim can never fire while scrolling, and an oversized message no longer storms (and is shown in full rather than clipped). Dead space only accrues in a long append-only session that trimmed old entries — exactly when reclaiming is wanted.
2026-06-20 09:42:28 +00:00
2d3d1ced71 ref(ui): use MAX utility method for code conciseness and clarity
All checks were successful
CI Code / Check coding style (push) Successful in 1m28s
CI Code / Check spelling (push) Successful in 2m20s
CI Code / Linux (ubuntu) (push) Successful in 5m16s
CI Code / Code Coverage (push) Successful in 3m51s
CI Code / Linux (debian) (push) Successful in 5m52s
CI Code / Check coding style (pull_request) Successful in 35s
CI Code / Check spelling (pull_request) Successful in 23s
CI Code / Linux (arch) (push) Successful in 8m34s
CI Code / Linux (debian) (pull_request) Successful in 4m25s
CI Code / Linux (ubuntu) (pull_request) Successful in 4m35s
CI Code / Code Coverage (pull_request) Successful in 3m30s
CI Code / Linux (arch) (pull_request) Successful in 8m55s
2026-06-20 09:40:11 +00:00
adb078a3e2 fix(ui): reserve pad height per message to prevent multi-line clip and scroll desync
Some checks failed
CI Code / Check spelling (pull_request) Successful in 18s
CI Code / Check coding style (pull_request) Successful in 30s
CI Code / Code Coverage (pull_request) Successful in 3m24s
CI Code / Linux (ubuntu) (pull_request) Successful in 4m54s
CI Code / Linux (debian) (pull_request) Successful in 6m58s
CI Code / Linux (arch) (pull_request) Successful in 7m7s
CI Code / Check spelling (push) Successful in 16s
CI Code / Check coding style (push) Successful in 32s
CI Code / Code Coverage (push) Failing after 3m24s
CI Code / Linux (debian) (push) Failing after 5m38s
CI Code / Linux (ubuntu) (push) Failing after 5m51s
CI Code / Linux (arch) (push) Failing after 7m38s
A tall multi-line message printed near the bottom of the ncurses pad was clipped because _win_print_internal grew the pad from the current cursor only (_win_ensure_pad_capacity(getcury)), not the height of the message about to be printed. The clipped message's captured height (e.g. 1 row for a 49-row message) was wrong while win_redraw later rendered it in full; the resulting buffer->lines / y_start_pos mismatch desynced the page-up scroll anchor, producing a ~one-page jump when scrolling past such messages from history.

Estimate the rendered height (hard newlines + soft-wrap over the usable width) and reserve that many rows before printing, so the message is never clipped and its captured height matches the redraw. All print paths go through _win_print_internal, so incoming/outgoing/history are covered.

Regression from the upstream sync (72f4f186d), which replaced the fixed-size pad with the dynamic _win_ensure_pad_capacity model.
2026-06-20 11:59:19 +03:00

View File

@@ -43,7 +43,7 @@
#include "ai/ai_client.h"
static const int PAD_MIN_HEIGHT = 100;
static const int PAD_THRESHOLD = 12000; // above buffer cap (~9000): reclaims dead pad space, never fires while scrolling
static const int PAD_DEAD_SPACE_LIMIT = 2000; // reclaim once the pad holds this much dead space (cursor past live buffer); size-independent, so it never fires while scrolling
static gboolean _in_redraw = FALSE;
static void
@@ -56,9 +56,10 @@ _win_ensure_pad_capacity(ProfWin* window, WINDOW* win, int lines_needed)
int cur_height = getmaxy(win);
int cur_width = getmaxx(win);
if (lines_needed >= cur_height - 1) {
// If we are getting too large, trigger a redraw to clean up old lines
// but only if we are not already in a redraw process.
if (window && cur_height >= PAD_THRESHOLD && !_in_redraw) {
// redraw to reclaim dead pad space (cursor far past live buffer, e.g. a long
// append-only session); dead space never accrues while scrolling, only here
int dead = window ? lines_needed - window->layout->buffer->lines : 0;
if (window && dead > PAD_DEAD_SPACE_LIMIT && !_in_redraw) {
win_redraw(window);
} else {
// resize to required lines + some buffer for next messages
@@ -67,6 +68,20 @@ _win_ensure_pad_capacity(ProfWin* window, WINDOW* win, int lines_needed)
}
}
}
// upper-bound estimate of rows a message body occupies (hard newlines + soft-wrap), to reserve pad space before printing so a tall message isn't clipped (a clipped height desyncs the scroll offset)
static int
_win_estimated_lines(WINDOW* win, const char* const message, int indent, int pad_indent)
{
int usable = MAX(1, getmaxx(win) - (indent + pad_indent));
int newlines = 0;
for (const char* p = message; *p; p++) {
if (*p == '\n') {
newlines++;
}
}
return newlines + utf8_display_len(message) / usable + 2;
}
static const char* LOADING_MESSAGE = "Loading older messages…";
static const char* CONS_WIN_TITLE = "CProof. Type /help for help information.";
static const char* XML_WIN_TITLE = "XML Console";
@@ -2001,7 +2016,7 @@ _win_print_internal(ProfWin* window, const char* show_char, int pad_indent, GDat
}
}
_win_ensure_pad_capacity(window, window->layout->win, getcury(window->layout->win));
_win_ensure_pad_capacity(window, window->layout->win, getcury(window->layout->win) + _win_estimated_lines(window->layout->win, message + offset, indent, pad_indent));
if (prefs_get_boolean(PREF_WRAP)) {
_win_print_wrapped(window->layout->win, message + offset, indent, pad_indent);