From ee3f89f10ad2f75f02ba72228b4f9a31e664c8d2 Mon Sep 17 00:00:00 2001 From: m1ngsama Date: Sun, 19 Apr 2026 22:56:38 +0800 Subject: [PATCH] feat: improve TUI visual experience with colors and indicators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Color-code usernames using hash-based ANSI color assignment (6 colors) - Style system messages (join/leave/rename) in gray with --> prefix - Style /me action messages in italic cyan - Show "↓ N new" indicator in yellow when scrolled up in NORMAL mode - Compact timestamps from full date to HH:MM for more message space Closes #44 --- src/tui.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 98 insertions(+), 4 deletions(-) diff --git a/src/tui.c b/src/tui.c index bfeca31..5e9c808 100644 --- a/src/tui.c +++ b/src/tui.c @@ -5,6 +5,93 @@ #include #include +static const char *username_color(const char *name) { + static const char *colors[] = { + "\033[31m", "\033[32m", "\033[33m", + "\033[34m", "\033[35m", "\033[36m", + }; + unsigned int h = 5381; + for (const char *p = name; *p; p++) + h = h * 33 + (unsigned char)*p; + return colors[h % 6]; +} + +static void format_message_colored(const message_t *msg, char *buffer, + size_t buf_size, int width) { + struct tm tm_info; + localtime_r(&msg->timestamp, &tm_info); + char time_str[32]; + strftime(time_str, sizeof(time_str), "%H:%M", &tm_info); + + if (strcmp(msg->username, "系统") == 0) { + snprintf(buffer, buf_size, + "\033[90m--> %s\033[0m", msg->content); + } else if (strcmp(msg->username, "*") == 0) { + snprintf(buffer, buf_size, + "\033[90m%s\033[0m \033[3;36m* %s\033[0m", + time_str, msg->content); + } else { + snprintf(buffer, buf_size, + "\033[90m%s\033[0m %s%s\033[0m: %s", + time_str, username_color(msg->username), + msg->username, msg->content); + } + + /* Plain-text version for width calculation */ + char plain[MAX_MESSAGE_LEN + 128]; + if (strcmp(msg->username, "系统") == 0) { + snprintf(plain, sizeof(plain), "--> %s", msg->content); + } else if (strcmp(msg->username, "*") == 0) { + snprintf(plain, sizeof(plain), "%s * %s", time_str, msg->content); + } else { + snprintf(plain, sizeof(plain), "%s %s: %s", + time_str, msg->username, msg->content); + } + + if (utf8_string_width(plain) > width) { + /* Rebuild with truncated content */ + int prefix_width; + char prefix_plain[256]; + if (strcmp(msg->username, "系统") == 0) { + snprintf(prefix_plain, sizeof(prefix_plain), "--> "); + } else if (strcmp(msg->username, "*") == 0) { + snprintf(prefix_plain, sizeof(prefix_plain), "%s * ", time_str); + } else { + snprintf(prefix_plain, sizeof(prefix_plain), "%s %s: ", + time_str, msg->username); + } + prefix_width = utf8_string_width(prefix_plain); + int content_width = width - prefix_width; + if (content_width < 4) content_width = 4; + + char truncated_content[MAX_MESSAGE_LEN]; + if (strcmp(msg->username, "系统") == 0) { + strncpy(truncated_content, msg->content, sizeof(truncated_content) - 1); + truncated_content[sizeof(truncated_content) - 1] = '\0'; + } else if (strcmp(msg->username, "*") == 0) { + snprintf(truncated_content, sizeof(truncated_content), "* %s", msg->content); + } else { + strncpy(truncated_content, msg->content, sizeof(truncated_content) - 1); + truncated_content[sizeof(truncated_content) - 1] = '\0'; + } + utf8_truncate(truncated_content, content_width); + + if (strcmp(msg->username, "系统") == 0) { + snprintf(buffer, buf_size, + "\033[90m--> %s\033[0m", truncated_content); + } else if (strcmp(msg->username, "*") == 0) { + snprintf(buffer, buf_size, + "\033[90m%s\033[0m \033[3;36m%s\033[0m", + time_str, truncated_content); + } else { + snprintf(buffer, buf_size, + "\033[90m%s\033[0m %s%s\033[0m: %s", + time_str, username_color(msg->username), + msg->username, truncated_content); + } + } +} + static void buffer_append_bytes(char *buffer, size_t buf_size, size_t *pos, const char *data, size_t len) { size_t available; @@ -148,8 +235,8 @@ void tui_render_screen(client_t *client) { /* Render messages from snapshot */ if (msg_snapshot) { for (int i = 0; i < snapshot_count; i++) { - char msg_line[1024]; - message_format(&msg_snapshot[i], msg_line, sizeof(msg_line), render_width); + char msg_line[2048]; + format_message_colored(&msg_snapshot[i], msg_line, sizeof(msg_line), render_width); buffer_appendf(buffer, buf_size, &pos, "%s\033[K\r\n", msg_line); } free(msg_snapshot); @@ -173,8 +260,15 @@ void tui_render_screen(client_t *client) { int total = msg_count; int scroll_pos = client->scroll_pos + 1; if (total == 0) scroll_pos = 0; - buffer_appendf(buffer, buf_size, &pos, - "-- NORMAL -- (%d/%d)\033[K", scroll_pos, total); + int unseen = msg_count - end; + if (unseen > 0) { + buffer_appendf(buffer, buf_size, &pos, + "-- NORMAL -- (%d/%d) \033[33m↓ %d new\033[0m\033[K", + scroll_pos, total, unseen); + } else { + buffer_appendf(buffer, buf_size, &pos, + "-- NORMAL -- (%d/%d)\033[K", scroll_pos, total); + } } else if (client->mode == MODE_COMMAND) { buffer_appendf(buffer, buf_size, &pos, ":%s\033[K", client->command_input); }