98 if (!config || !config->render_header) {
105 terminal_size_t new_size;
107 g_cached_term_size = new_size;
109 g_last_term_size_check_us = now_us;
116 static bool last_grep_state =
false;
117 if (last_grep_state && !grep_entering) {
118 terminal_screen_clear_cache();
123 terminal_screen_clear_cache();
126 last_grep_state = grep_entering;
128 if (!grep_entering) {
130 terminal_clear_screen();
131 terminal_cursor_home(STDOUT_FILENO);
134 terminal_cursor_home(STDOUT_FILENO);
138 config->render_header(g_cached_term_size, config->user_data);
141 if (!config->show_logs) {
147 int log_area_rows = g_cached_term_size.rows - config->fixed_header_lines - 1;
149 if (log_area_rows <= 0) {
160 int renderable_log_rows = log_area_rows;
163 session_log_entry_t *log_entries = NULL;
164 size_t log_count = 0;
168 if (result != ASCIICHAT_OK || !log_entries) {
169 log_entries = SAFE_MALLOC(SESSION_LOG_BUFFER_SIZE *
sizeof(session_log_entry_t), session_log_entry_t *);
176 session_log_entry_t *buffer_entries =
177 SAFE_MALLOC(SESSION_LOG_BUFFER_SIZE *
sizeof(session_log_entry_t), session_log_entry_t *);
178 if (buffer_entries) {
180 log_entries = buffer_entries;
181 log_count = buffer_count;
188 int total_lines_needed = 0;
189 int first_log_to_display = (log_count > 0) ? (
int)log_count - 1 : 0;
191 for (
int i = (
int)log_count - 1; i >= 0; i--) {
192 const char *msg = log_entries[i].message;
194 if (msg_display_width < 0) {
195 msg_display_width = (int)strlen(msg);
198 int lines_for_this_log = 1;
199 if (g_cached_term_size.cols > 0 && msg_display_width > g_cached_term_size.cols) {
200 lines_for_this_log = (msg_display_width + g_cached_term_size.cols - 1) / g_cached_term_size.cols;
203 if (total_lines_needed + lines_for_this_log > renderable_log_rows) {
204 first_log_to_display = i + 1;
208 total_lines_needed += lines_for_this_log;
209 first_log_to_display = i;
212 if (!grep_entering) {
214 for (
int i = first_log_to_display; i < (int)log_count; i++) {
215 fprintf(stdout,
"%s\n", log_entries[i].message);
219 int remaining = log_area_rows - total_lines_needed;
220 for (
int i = 0; i < remaining; i++) {
221 fprintf(stdout,
"\n");
230 for (
int i = first_log_to_display; i < (int)log_count; i++) {
231 const char *original_msg = log_entries[i].message;
232 const char *msg = original_msg;
235 char plain_text[SESSION_LOG_LINE_MAX] = {0};
236 strip_ansi_codes(original_msg, plain_text,
sizeof(plain_text));
239 size_t match_start = 0, match_len = 0;
242 size_t plain_len = strlen(plain_text);
243 if (match_start < plain_len && (match_start + match_len) <= plain_len) {
245 if (highlighted && highlighted[0] !=
'\0') {
252 if (msg_display_width < 0) {
253 msg_display_width = (int)strlen(msg);
255 int lines_for_this = 1;
256 if (g_cached_term_size.cols > 0 && msg_display_width > g_cached_term_size.cols) {
257 lines_for_this = (msg_display_width + g_cached_term_size.cols - 1) / g_cached_term_size.cols;
263 bool same_as_before =
false;
265 same_as_before = (log_idx < g_prev_log_count && g_prev_log_ptrs[log_idx] == original_msg);
268 if (same_as_before) {
271 if (lines_for_this == 1) {
272 fprintf(stdout,
"\x1b[0m\n");
274 fprintf(stdout,
"\x1b[0m\x1b[%dB", lines_for_this);
280 fprintf(stdout,
"%s\x1b[0m\x1b[K\n", msg);
284 g_prev_log_ptrs[log_idx] = original_msg;
287 lines_used += lines_for_this;
292 int remaining = renderable_log_rows - lines_used;
294 if (remaining >= 1 && first_log_to_display > 0) {
296 int prev_idx = first_log_to_display - 1;
297 const char *prev_msg = log_entries[prev_idx].message;
300 if (prev_width < 0) {
301 prev_width = (int)strlen(prev_msg);
304 if (prev_width > g_cached_term_size.cols) {
306 int target_width = g_cached_term_size.cols - 3;
307 if (target_width <= 0) {
308 fprintf(stdout,
"...\x1b[K\n");
310 size_t src_len = strlen(prev_msg);
313 for (
size_t truncate_at = src_len; truncate_at > 0; truncate_at--) {
314 char test_buf[SESSION_LOG_LINE_MAX];
315 SAFE_STRNCPY(test_buf, prev_msg, truncate_at);
316 test_buf[truncate_at] =
'\0';
319 if (test_width < 0) {
320 test_width = (int)strlen(test_buf);
323 if (test_width <= target_width) {
325 fprintf(stdout,
"%s...\x1b[K\n", test_buf);
332 fprintf(stdout,
"...\x1b[K\n");
337 fprintf(stdout,
"%s\x1b[K\n", prev_msg);
344 for (
int i = 0; i < remaining; i++) {
345 fprintf(stdout,
"\x1b[0m\x1b[K\n");
348 g_prev_log_count = log_idx;
349 g_prev_total_lines = lines_used;
358 char grep_ui_buffer[512];
359 int pos = snprintf(grep_ui_buffer,
sizeof(grep_ui_buffer),
"\x1b[%d;1H\x1b[0m\x1b[K",
360 g_cached_term_size.rows);
363 if (pos > 0 && pos < (
int)
sizeof(grep_ui_buffer) - 256) {
368 mutex_lock(grep_mutex);
372 if (pattern_len > 0 && pattern) {
373 int remaining = snprintf(grep_ui_buffer + pos,
sizeof(grep_ui_buffer) - (
size_t)pos,
374 "/%.*s", pattern_len, pattern);
375 if (remaining > 0 && remaining < (
int)
sizeof(grep_ui_buffer) - (
int)pos) {
380 if (pos + 1 < (
int)
sizeof(grep_ui_buffer)) {
381 grep_ui_buffer[pos++] =
'/';
384 mutex_unlock(grep_mutex);
389 if (pos > 0 && pos <= (
int)
sizeof(grep_ui_buffer)) {
394 SAFE_FREE(log_entries);