From 159073ec4980963c879d7a2480eca991ea40d909 Mon Sep 17 00:00:00 2001 From: m1ngsama Date: Sun, 17 May 2026 10:48:27 +0800 Subject: [PATCH] tui: framed welcome banner before username prompt (M7-1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the four lines of `==` rules + bilingual title with a centred rounded box so the eye lands on a single visual element instead of two. The new tui_render_welcome(): - centres horizontally and vertically (about a third of the way down) - shows TNT + version, then 中文 subtitle, then English caption with decreasing visual weight (bold cyan / default / dim grey) - falls back to a plain "TNT $version — anonymous chat over SSH" line when the terminal is too narrow for the frame - declared in tui.h, called from input.c read_username() read_username()'s prompt becomes a single short bilingual hint ("请输入用户名 (留空 anonymous): ") that lives below the box. --- include/tui.h | 5 +++ src/input.c | 8 ++--- src/tui.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 6 deletions(-) diff --git a/include/tui.h b/include/tui.h index 9dcaf5d..592c88f 100644 --- a/include/tui.h +++ b/include/tui.h @@ -22,6 +22,11 @@ void tui_render_input(struct client *client, const char *input); /* Clear the screen */ void tui_clear_screen(struct client *client); +/* Render the pre-login welcome banner. Centered, framed, shown once before + * the username prompt. Caller is responsible for printing the prompt + * itself afterwards. */ +void tui_render_welcome(struct client *client); + /* Get help text based on language */ const char* tui_get_help_text(help_lang_t lang); diff --git a/src/input.c b/src/input.c index c9742f0..11720e4 100644 --- a/src/input.c +++ b/src/input.c @@ -27,12 +27,8 @@ static int read_username(client_t *client) { int pos = 0; char buf[4]; - tui_clear_screen(client); - client_printf(client, "================================\r\n"); - client_printf(client, " 欢迎来到 TNT 匿名聊天室\r\n"); - client_printf(client, " Welcome to TNT Anonymous Chat\r\n"); - client_printf(client, "================================\r\n\r\n"); - client_printf(client, "请输入用户名 (留空默认为 anonymous): "); + tui_render_welcome(client); + client_printf(client, " 请输入用户名 (留空 anonymous): "); while (1) { int n = ssh_channel_read_timeout(client->channel, buf, 1, 0, 60000); /* 60 sec timeout */ diff --git a/src/tui.c b/src/tui.c index cb3670e..e735f04 100644 --- a/src/tui.c +++ b/src/tui.c @@ -118,6 +118,102 @@ void tui_clear_screen(client_t *client) { client_send(client, clear, strlen(clear)); } +/* Render the pre-login welcome banner. + * + * Centred horizontally; vertically positioned about a third of the way down + * the available height so the user's eye lands naturally on it before the + * prompt below. Uses light box-drawing characters (U+256D / U+2570) so the + * frame matches the rest of the TUI's aesthetic instead of the older ASCII + * `==` rules. */ +void tui_render_welcome(client_t *client) { + if (!client || !client->connected) return; + + int rw = client->width; + int rh = client->height; + if (rw < 10) rw = 10; + if (rh < 4) rh = 4; + + /* Lines, in display order. Width is computed in display columns. */ + const char *line1 = "TNT · " TNT_VERSION; + const char *line2 = "匿名聊天室 · SSH"; + const char *line3 = "Anonymous chat over SSH"; + + int inner_w = utf8_string_width(line1); + int w2 = utf8_string_width(line2); + int w3 = utf8_string_width(line3); + if (w2 > inner_w) inner_w = w2; + if (w3 > inner_w) inner_w = w3; + inner_w += 4; /* 2 columns padding on each side */ + + /* Fall back to plain prompt if the terminal is too narrow for the frame. */ + if (inner_w + 2 > rw) { + char fallback[128]; + int n = snprintf(fallback, sizeof(fallback), + ANSI_CLEAR ANSI_HOME + "TNT %s — anonymous chat over SSH\r\n\r\n", + TNT_VERSION); + if (n > 0) client_send(client, fallback, (size_t)n); + return; + } + + int top_pad = rh / 3; + if (top_pad < 1) top_pad = 1; + int left_pad = (rw - (inner_w + 2)) / 2; + if (left_pad < 0) left_pad = 0; + + /* ~5 KiB is plenty for the framed banner even on the largest terminals. */ + char buf[4096]; + size_t pos = 0; + + buffer_appendf(buf, sizeof(buf), &pos, ANSI_CLEAR ANSI_HOME); + for (int i = 0; i < top_pad; i++) { + buffer_appendf(buf, sizeof(buf), &pos, "\r\n"); + } + + /* Top border: ╭───…───╮ */ + for (int i = 0; i < left_pad; i++) buffer_append_bytes(buf, sizeof(buf), &pos, " ", 1); + buffer_append_bytes(buf, sizeof(buf), &pos, "\033[36m", 5); + buffer_append_bytes(buf, sizeof(buf), &pos, "╭", strlen("╭")); + for (int i = 0; i < inner_w; i++) buffer_append_bytes(buf, sizeof(buf), &pos, "─", strlen("─")); + buffer_append_bytes(buf, sizeof(buf), &pos, "╮", strlen("╮")); + buffer_append_bytes(buf, sizeof(buf), &pos, "\033[0m", 4); + buffer_appendf(buf, sizeof(buf), &pos, "\r\n"); + + /* Three content lines with surrounding │ borders, centred inside the frame. */ + const char *lines[3] = {line1, line2, line3}; + int widths[3] = {utf8_string_width(line1), w2, w3}; + const char *line_color[3] = {"\033[1;36m", "\033[0m", "\033[2;37m"}; + for (int li = 0; li < 3; li++) { + for (int i = 0; i < left_pad; i++) buffer_append_bytes(buf, sizeof(buf), &pos, " ", 1); + buffer_append_bytes(buf, sizeof(buf), &pos, "\033[36m", 5); + buffer_append_bytes(buf, sizeof(buf), &pos, "│", strlen("│")); + buffer_append_bytes(buf, sizeof(buf), &pos, "\033[0m", 4); + + int pad_total = inner_w - widths[li]; + int pad_left = pad_total / 2; + int pad_right = pad_total - pad_left; + for (int i = 0; i < pad_left; i++) buffer_append_bytes(buf, sizeof(buf), &pos, " ", 1); + buffer_appendf(buf, sizeof(buf), &pos, "%s%s\033[0m", line_color[li], lines[li]); + for (int i = 0; i < pad_right; i++) buffer_append_bytes(buf, sizeof(buf), &pos, " ", 1); + + buffer_append_bytes(buf, sizeof(buf), &pos, "\033[36m", 5); + buffer_append_bytes(buf, sizeof(buf), &pos, "│", strlen("│")); + buffer_append_bytes(buf, sizeof(buf), &pos, "\033[0m", 4); + buffer_appendf(buf, sizeof(buf), &pos, "\r\n"); + } + + /* Bottom border: ╰───…───╯ */ + for (int i = 0; i < left_pad; i++) buffer_append_bytes(buf, sizeof(buf), &pos, " ", 1); + buffer_append_bytes(buf, sizeof(buf), &pos, "\033[36m", 5); + buffer_append_bytes(buf, sizeof(buf), &pos, "╰", strlen("╰")); + for (int i = 0; i < inner_w; i++) buffer_append_bytes(buf, sizeof(buf), &pos, "─", strlen("─")); + buffer_append_bytes(buf, sizeof(buf), &pos, "╯", strlen("╯")); + buffer_append_bytes(buf, sizeof(buf), &pos, "\033[0m", 4); + buffer_appendf(buf, sizeof(buf), &pos, "\r\n\r\n"); + + client_send(client, buf, pos); +} + /* Render the main screen */ void tui_render_screen(client_t *client) { if (!client || !client->connected) return;