From a693d281f859e80800237e6f3dcc2b52da1b70e9 Mon Sep 17 00:00:00 2001 From: m1ngsama Date: Sun, 24 May 2026 10:17:25 +0800 Subject: [PATCH] ux: collapse help surface around manual --- README.md | 12 ++-- docs/CHANGELOG.md | 11 ++++ docs/CICD.md | 2 +- docs/DEPLOYMENT.md | 2 +- docs/Development-Guide.md | 12 ++-- docs/QUICKREF.md | 10 ++-- include/help_text.h | 2 - include/manual.h | 9 +++ include/manual_text.h | 8 +++ include/support.h | 11 ---- include/support_text.h | 9 --- src/commands.c | 17 ++---- src/exec.c | 12 ---- src/help_text.c | 68 +++-------------------- src/i18n.c | 26 ++++----- src/manual.c | 10 ++++ src/manual_text.c | 63 +++++++++++++++++++++ src/support.c | 17 ------ src/support_text.c | 97 --------------------------------- tests/test_exec_mode.sh | 12 ---- tests/test_interactive_input.sh | 34 ++++++------ tests/unit/Makefile | 10 ++-- tests/unit/test_help_text.c | 30 +++------- tests/unit/test_i18n.c | 8 ++- tests/unit/test_manual_text.c | 44 +++++++++++++++ tests/unit/test_support_text.c | 52 ------------------ tnt.1 | 16 +++--- 27 files changed, 236 insertions(+), 368 deletions(-) create mode 100644 include/manual.h create mode 100644 include/manual_text.h delete mode 100644 include/support.h delete mode 100644 include/support_text.h create mode 100644 src/manual.c create mode 100644 src/manual_text.c delete mode 100644 src/support.c delete mode 100644 src/support_text.c create mode 100644 tests/unit/test_manual_text.c delete mode 100644 tests/unit/test_support_text.c diff --git a/README.md b/README.md index 2db2d04..6831266 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ Ctrl+F/B - Scroll full page down/up PgDn/PgUp - Scroll full page down/up End/Home - Jump to bottom/top g/G - Jump to top/bottom -? - Show help +? - Show full key reference Ctrl+C - Exit chat ``` @@ -92,12 +92,12 @@ Ctrl+C - Exit chat :nick - Change nickname :msg - Whisper to user :w - Short alias for :msg +:inbox - Show whispers :last [N] - Show last N messages from history (max 50, default 10) :search - Search full message history (case-insensitive) :mute-joins - Toggle join/leave system notifications -:support - Show quick support guide :lang - Switch UI language for this session -:help - Show available commands +:help - Show concise manual :clear - Clear command output :q, :quit, :exit - Disconnect Up/Down - Browse command history @@ -176,7 +176,6 @@ TNT also exposes a small non-interactive SSH surface for scripts: ssh -p 2222 chat.m1ng.space health ssh -p 2222 chat.m1ng.space stats --json ssh -p 2222 chat.m1ng.space users -ssh -p 2222 chat.m1ng.space support ssh -p 2222 chat.m1ng.space "tail -n 20" ssh -p 2222 operator@chat.m1ng.space post "service notice" ssh -p 2222 chat.m1ng.space post "/me deploys v2.0" @@ -256,8 +255,9 @@ TNT/ │ ├── chat_room.c # chat room logic │ ├── message.c # message persistence │ ├── history_view.c # message viewport and scroll state -│ ├── help_text.c # full-screen and command help content -│ ├── support_text.c # quick support guide content +│ ├── help_text.c # full-screen key reference content +│ ├── manual.c # concise manual panel rendering +│ ├── manual_text.c # concise manual content │ ├── i18n.c # language selection and shared UI text │ ├── ratelimit.c # connection limits and rate limiting │ ├── tui.c # terminal UI rendering diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 61b6168..6bc160e 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## Unreleased + +### Changed +- Collapsed the interactive help surface around a concise Unix-style `:help` + manual and the `?` full key reference; `:support` is no longer a user-facing + command. +- First-use hints and unknown-command guidance now point users to `:help` + instead of the removed support entry. +- The concise manual module is now named `manual_text`, and the redundant + interactive `:commands` entrypoint was removed. + ## 1.0.1 - 2026-05-24 - Release candidate hardening ### Added diff --git a/docs/CICD.md b/docs/CICD.md index 3624a91..427cf1e 100644 --- a/docs/CICD.md +++ b/docs/CICD.md @@ -58,7 +58,7 @@ Deployments are operator-driven: 3. Install the new binary. 4. Restart the service. 5. Run black-box checks (`health`, `stats --json`, `users --json`, - `support`, and a post/tail smoke test). + and a post/tail smoke test). The installer can still be used manually on a server: curl -sSL https://raw.githubusercontent.com/m1ngsama/TNT/main/install.sh | sh diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index faadcf0..e9dc7b9 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -97,7 +97,7 @@ Place a `motd.txt` file in the state directory. TNT displays it to each user on # Systemd deployment (state dir is /var/lib/tnt) sudo tee /var/lib/tnt/motd.txt <<'EOF' Welcome! Be respectful. No spam. -Type :help for available commands. +Type :help for a concise manual, or ? for the full key reference. EOF sudo chown tnt:tnt /var/lib/tnt/motd.txt diff --git a/docs/Development-Guide.md b/docs/Development-Guide.md index eac88f1..779cfd8 100644 --- a/docs/Development-Guide.md +++ b/docs/Development-Guide.md @@ -79,8 +79,9 @@ src/ ├── tui.c - Terminal UI rendering (ANSI escape codes) ├── tui_status.c - Mode/status/input-line rendering ├── i18n.c - Language selection and shared UI text -├── help_text.c - Full-screen and command help text -├── support_text.c - Quick support guide text +├── help_text.c - Full-screen key reference text +├── manual.c - Concise manual panel rendering +├── manual_text.c - Concise manual text ├── system_message.c - Localized join/leave/nick system messages ├── ratelimit.c - Per-IP and global connection limits └── utf8.c - UTF-8 character handling @@ -98,8 +99,9 @@ include/ ├── history_view.h - Scroll-state helpers ├── tui.h - TUI rendering functions ├── i18n.h - Language and shared text IDs -├── help_text.h - Help text interface -├── support_text.h - Support guide text interface +├── help_text.h - Key reference text interface +├── manual.h - Concise manual panel interface +├── manual_text.h - Concise manual text interface ├── ratelimit.h - Connection limit interface └── utf8.h - UTF-8 utilities ``` @@ -363,7 +365,7 @@ if (strcmp(cmd, "newcmd") == 0) { 3. **Move user-facing strings through `src/i18n.c` when they need localization or are reused.** -4. **Update help text in `src/help_text.c`:** +4. **Update user help text in `src/manual_text.c` and `src/help_text.c`:** ```c "AVAILABLE COMMANDS:\n" " newcmd - Description of new command\n" diff --git a/docs/QUICKREF.md b/docs/QUICKREF.md index 4a821e1..af90183 100644 --- a/docs/QUICKREF.md +++ b/docs/QUICKREF.md @@ -27,11 +27,12 @@ COMMANDS (COMMAND mode, prefix with :) nick change nickname msg whisper to user w alias for msg + inbox show whispers last [N] last N messages from log (default 10, max 50) search search full history (case-insensitive, 15 results) mute-joins toggle join/leave notifications - support quick support guide - help show all commands + help concise manual + lang [en|zh] show or switch UI language clear clear output q / quit / exit disconnect @@ -52,8 +53,9 @@ STRUCTURE src/exec.c SSH exec command dispatch src/message.c persistence, search src/history_view.c message viewport / scroll state - src/help_text.c full-screen and command help text - src/support_text.c quick support guide content + src/help_text.c full-screen key reference text + src/manual.c concise manual panel rendering + src/manual_text.c concise manual content src/i18n.c language selection and shared text src/ratelimit.c connection limits and rate limiting src/tui.c rendering diff --git a/include/help_text.h b/include/help_text.h index 232b854..ae44b31 100644 --- a/include/help_text.h +++ b/include/help_text.h @@ -4,7 +4,5 @@ #include "common.h" const char *help_text_full(help_lang_t lang); -void help_text_append_commands(char *output, size_t buf_size, size_t *pos, - help_lang_t lang); #endif /* HELP_TEXT_H */ diff --git a/include/manual.h b/include/manual.h new file mode 100644 index 0000000..ea52944 --- /dev/null +++ b/include/manual.h @@ -0,0 +1,9 @@ +#ifndef MANUAL_H +#define MANUAL_H + +#include "common.h" + +void manual_append_interactive_panel(char *buffer, size_t buf_size, + size_t *pos, help_lang_t lang); + +#endif /* MANUAL_H */ diff --git a/include/manual_text.h b/include/manual_text.h new file mode 100644 index 0000000..9a798cb --- /dev/null +++ b/include/manual_text.h @@ -0,0 +1,8 @@ +#ifndef MANUAL_TEXT_H +#define MANUAL_TEXT_H + +#include "common.h" + +const char *manual_text_interactive(help_lang_t lang); + +#endif /* MANUAL_TEXT_H */ diff --git a/include/support.h b/include/support.h deleted file mode 100644 index cabc9b4..0000000 --- a/include/support.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef SUPPORT_H -#define SUPPORT_H - -#include "common.h" - -void support_append_interactive_panel(char *buffer, size_t buf_size, - size_t *pos, help_lang_t lang); -void support_append_exec_panel(char *buffer, size_t buf_size, size_t *pos, - help_lang_t lang); - -#endif /* SUPPORT_H */ diff --git a/include/support_text.h b/include/support_text.h deleted file mode 100644 index f837358..0000000 --- a/include/support_text.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef SUPPORT_TEXT_H -#define SUPPORT_TEXT_H - -#include "common.h" - -const char *support_text_interactive(help_lang_t lang); -const char *support_text_exec(help_lang_t lang); - -#endif /* SUPPORT_TEXT_H */ diff --git a/src/commands.c b/src/commands.c index 2fe2e3c..d951130 100644 --- a/src/commands.c +++ b/src/commands.c @@ -8,10 +8,9 @@ #include "chat_room.h" #include "client.h" #include "common.h" -#include "help_text.h" #include "i18n.h" +#include "manual.h" #include "message.h" -#include "support.h" #include "system_message.h" #include "tui.h" #include "utf8.h" @@ -84,8 +83,8 @@ static int command_edit_distance(const char *a, const char *b) { static const char *suggest_command(const char *cmd) { static const char *commands[] = { "list", "users", "who", "nick", "name", "msg", "w", "inbox", - "last", "search", "mute-joins", "mute", "support", "guide", - "lang", "language", "help", "commands", "clear", "cls", + "last", "search", "mute-joins", "mute", "lang", "language", + "help", "clear", "cls", "q", "quit", "exit" }; const char *best = NULL; @@ -168,13 +167,9 @@ void commands_dispatch(client_t *client) { } pthread_rwlock_unlock(&g_room->lock); - } else if (strcmp(cmd, "help") == 0 || strcmp(cmd, "commands") == 0) { - help_text_append_commands(output, sizeof(output), &pos, - client->help_lang); - - } else if (strcmp(cmd, "support") == 0 || strcmp(cmd, "guide") == 0) { - support_append_interactive_panel(output, sizeof(output), &pos, - client->help_lang); + } else if (strcmp(cmd, "help") == 0) { + manual_append_interactive_panel(output, sizeof(output), &pos, + client->help_lang); } else if (strcmp(cmd, "lang") == 0 || strcmp(cmd, "language") == 0 || strncmp(cmd, "lang ", 5) == 0 || diff --git a/src/exec.c b/src/exec.c index 319d661..8add1c4 100644 --- a/src/exec.c +++ b/src/exec.c @@ -6,7 +6,6 @@ #include "input.h" #include "message.h" #include "ratelimit.h" -#include "support.h" #include "utf8.h" #include #include @@ -122,14 +121,6 @@ static int exec_command_help(client_t *client) { return client_send(client, help_text, strlen(help_text)) == 0 ? 0 : 1; } -static int exec_command_support(client_t *client) { - char output[2048] = {0}; - size_t pos = 0; - - support_append_exec_panel(output, sizeof(output), &pos, client->help_lang); - return client_send(client, output, pos) == 0 ? 0 : 1; -} - static int exec_command_health(client_t *client) { static const char ok[] = "ok\n"; return client_send(client, ok, sizeof(ok) - 1) == 0 ? 0 : 1; @@ -429,9 +420,6 @@ int exec_dispatch(client_t *client) { if (strcmp(cmd, "help") == 0 || strcmp(cmd, "--help") == 0) { return exec_command_help(client); } - if (strcmp(cmd, "support") == 0 || strcmp(cmd, "guide") == 0) { - return exec_command_support(client); - } if (strcmp(cmd, "health") == 0) { return exec_command_health(client); } diff --git a/src/help_text.c b/src/help_text.c index 710ad41..477257c 100644 --- a/src/help_text.c +++ b/src/help_text.c @@ -1,60 +1,8 @@ #include "help_text.h" -void help_text_append_commands(char *output, size_t buf_size, size_t *pos, - help_lang_t lang) { - if (lang == LANG_ZH) { - buffer_appendf(output, buf_size, pos, - "========================================\n" - " 可用命令\n" - "========================================\n" - "list, users, who - 显示在线用户\n" - "nick/name - 修改昵称\n" - "msg/w - 私聊用户\n" - "inbox - 查看私聊历史\n" - "last [N] - 查看最近 N 条消息\n" - "search - 搜索消息历史\n" - "mute-joins - 切换加入/离开提示\n" - "support - 显示快速支持指南\n" - "lang [en|zh] - 查看或切换界面语言\n" - "help, commands - 显示此帮助\n" - "clear, cls - 清空命令输出\n" - "q, quit, exit - 断开连接\n" - "上/下方向键 - 命令历史\n" - "========================================\n" - "INSERT 模式:\n" - " /me - 发送动作消息\n" - " @username - 提及用户并响铃提示\n" - "========================================\n"); - return; - } - - buffer_appendf(output, buf_size, pos, - "========================================\n" - " Available Commands\n" - "========================================\n" - "list, users, who - Show online users\n" - "nick/name - Change nickname\n" - "msg/w - Whisper to user (private)\n" - "inbox - Show whisper history\n" - "last [N] - Show last N messages\n" - "search - Search message history\n" - "mute-joins - Toggle join/leave notices\n" - "support - Show quick support guide\n" - "lang [en|zh] - Show or switch UI language\n" - "help, commands - Show this help\n" - "clear, cls - Clear command output\n" - "q, quit, exit - Disconnect\n" - "Up/Down arrows - Command history\n" - "========================================\n" - "In INSERT mode:\n" - " /me - Send action message\n" - " @username - Mention (bell notify)\n" - "========================================\n"); -} - const char *help_text_full(help_lang_t lang) { if (lang == LANG_EN) { - return "TERMINAL CHAT ROOM - HELP\n" + return "TNT KEY REFERENCE\n" "\n" "OPERATING MODES:\n" " INSERT - Type and send messages (default)\n" @@ -80,7 +28,7 @@ const char *help_text_full(help_lang_t lang) { " PgDn/PgUp - Scroll full page down/up\n" " End/Home - Jump to bottom/top\n" " g/G - Jump to top/bottom\n" - " ? - Show this help\n" + " ? - Show full key reference\n" " Ctrl+C - Exit chat\n" "\n" "AVAILABLE COMMANDS:\n" @@ -88,12 +36,12 @@ const char *help_text_full(help_lang_t lang) { " :nick - Change nickname\n" " :msg - Whisper to user\n" " :w - Short alias for :msg\n" + " :inbox - Show whispers\n" " :last [N] - Show last N messages (max 50)\n" " :search - Search message history\n" " :mute-joins - Toggle join/leave notices\n" - " :support - Show quick support guide\n" + " :help - Show concise manual\n" " :lang - Switch UI language\n" - " :help - Show available commands\n" " :clear - Clear command output\n" " :q, :quit, :exit - Disconnect\n" "\n" @@ -110,7 +58,7 @@ const char *help_text_full(help_lang_t lang) { " e/z - Switch English/Chinese\n"; } - return "终端聊天室 - 帮助\n" + return "TNT 按键参考\n" "\n" "操作模式:\n" " INSERT - 输入和发送消息(默认)\n" @@ -136,7 +84,7 @@ const char *help_text_full(help_lang_t lang) { " PgDn/PgUp - 向下/上滚动整页\n" " End/Home - 跳到底部/顶部\n" " g/G - 跳到顶部/底部\n" - " ? - 显示此帮助\n" + " ? - 显示完整按键参考\n" " Ctrl+C - 退出聊天\n" "\n" "可用命令:\n" @@ -144,12 +92,12 @@ const char *help_text_full(help_lang_t lang) { " :nick <名字> - 更改昵称\n" " :msg <用户> <文本> - 私聊\n" " :w <用户> <文本> - :msg 的简写\n" + " :inbox - 查看私聊\n" " :last [N] - 显示最后 N 条消息(最多50)\n" " :search <关键词> - 搜索消息历史\n" " :mute-joins - 切换加入/离开提示\n" - " :support - 显示快速支持指南\n" + " :help - 显示简明手册\n" " :lang - 切换界面语言\n" - " :help - 显示可用命令\n" " :clear - 清空命令输出\n" " :q, :quit, :exit - 断开连接\n" "\n" diff --git a/src/i18n.c b/src/i18n.c index 844053c..70977d0 100644 --- a/src/i18n.c +++ b/src/i18n.c @@ -104,17 +104,17 @@ const char *i18n_text(help_lang_t lang, i18n_text_id_t id) { case I18N_WELCOME_FALLBACK_FORMAT: return "TNT %s - SSH 匿名聊天室\r\n\r\n"; case I18N_INSERT_HINT_WIDE: - return "Enter 发送 · Esc 浏览 · :support"; + return "Enter 发送 · Esc 浏览 · :help"; case I18N_INSERT_HINT_NARROW: - return "Enter · Esc · :support"; + return "Enter · Esc · :help"; case I18N_NORMAL_LATEST: return "G 最新"; case I18N_NORMAL_NEW_MESSAGES: return "新消息"; case I18N_HELP_TITLE: - return " 帮助 "; + return " 按键 "; case I18N_HELP_STATUS_FORMAT: - return "-- 帮助 -- (%d/%d) j/k:滚动 g/G:首尾 e/z:语言 q:关闭"; + return "-- 按键参考 -- (%d/%d) j/k:滚动 g/G:首尾 e/z:语言 q:关闭"; case I18N_COMMAND_OUTPUT_TITLE: return " 命令输出 "; case I18N_MOTD_TITLE: @@ -126,7 +126,7 @@ const char *i18n_text(help_lang_t lang, i18n_text_id_t id) { case I18N_TITLE_MUTED: return "静音"; case I18N_TITLE_HELP_HINT: - return "? 帮助"; + return "? 按键"; case I18N_IDLE_TIMEOUT_FORMAT: return "\r\n\033[33m已断开: 空闲超时 (%d 分钟)\033[0m\r\n"; case I18N_SYSTEM_USERNAME: @@ -189,7 +189,7 @@ const char *i18n_text(help_lang_t lang, i18n_text_id_t id) { case I18N_DID_YOU_MEAN_FORMAT: return "你是想输入 :%s 吗?\n"; case I18N_UNKNOWN_GUIDANCE: - return "输入 :support 查看引导,或 :help 查看命令\n"; + return "输入 :help 查看帮助\n"; case I18N_EXEC_HELP: return "TNT exec 接口\n" "命令:\n" @@ -201,7 +201,6 @@ const char *i18n_text(help_lang_t lang, i18n_text_id_t id) { " tail -n N 输出最近消息\n" " post MESSAGE 非交互发送消息\n" " post \"/me act\" 发送动作消息\n" - " support 显示快速支持指南\n" " exit 成功退出\n"; case I18N_EXEC_USERS_USAGE: return "users: 用法: users [--json]\n"; @@ -236,17 +235,17 @@ const char *i18n_text(help_lang_t lang, i18n_text_id_t id) { case I18N_WELCOME_FALLBACK_FORMAT: return "TNT %s - anonymous chat over SSH\r\n\r\n"; case I18N_INSERT_HINT_WIDE: - return "Enter send · Esc browse · :support"; + return "Enter send · Esc browse · :help"; case I18N_INSERT_HINT_NARROW: - return "Enter · Esc · :support"; + return "Enter · Esc · :help"; case I18N_NORMAL_LATEST: return "G latest"; case I18N_NORMAL_NEW_MESSAGES: return "new"; case I18N_HELP_TITLE: - return " HELP "; + return " KEYS "; case I18N_HELP_STATUS_FORMAT: - return "-- HELP -- (%d/%d) j/k:scroll g/G:top/bottom e/z:lang q:close"; + return "-- KEY REFERENCE -- (%d/%d) j/k:scroll g/G:top/bottom e/z:lang q:close"; case I18N_COMMAND_OUTPUT_TITLE: return " COMMAND OUTPUT "; case I18N_MOTD_TITLE: @@ -258,7 +257,7 @@ const char *i18n_text(help_lang_t lang, i18n_text_id_t id) { case I18N_TITLE_MUTED: return "muted"; case I18N_TITLE_HELP_HINT: - return "? help"; + return "? keys"; case I18N_IDLE_TIMEOUT_FORMAT: return "\r\n\033[33mDisconnected: idle timeout (%d min)\033[0m\r\n"; case I18N_SYSTEM_USERNAME: @@ -321,7 +320,7 @@ const char *i18n_text(help_lang_t lang, i18n_text_id_t id) { case I18N_DID_YOU_MEAN_FORMAT: return "Did you mean :%s?\n"; case I18N_UNKNOWN_GUIDANCE: - return "Type :support for guidance or :help for commands\n"; + return "Type :help for help\n"; case I18N_EXEC_HELP: return "TNT exec interface\n" "Commands:\n" @@ -333,7 +332,6 @@ const char *i18n_text(help_lang_t lang, i18n_text_id_t id) { " tail -n N Print recent messages\n" " post MESSAGE Post a message non-interactively\n" " post \"/me act\" Post an action message\n" - " support Show quick support guide\n" " exit Exit successfully\n"; case I18N_EXEC_USERS_USAGE: return "users: usage: users [--json]\n"; diff --git a/src/manual.c b/src/manual.c new file mode 100644 index 0000000..c135b13 --- /dev/null +++ b/src/manual.c @@ -0,0 +1,10 @@ +#include "manual.h" +#include "manual_text.h" + +void manual_append_interactive_panel(char *buffer, size_t buf_size, + size_t *pos, help_lang_t lang) { + if (!buffer || !pos) return; + + buffer_appendf(buffer, buf_size, pos, "%s", + manual_text_interactive(lang)); +} diff --git a/src/manual_text.c b/src/manual_text.c new file mode 100644 index 0000000..cb4f0ab --- /dev/null +++ b/src/manual_text.c @@ -0,0 +1,63 @@ +#include "manual_text.h" + +const char *manual_text_interactive(help_lang_t lang) { + if (lang == LANG_ZH) { + return "\033[1;36mTNT(1) 帮助\033[0m\n" + "\n" + "\033[1;37m名称\033[0m\n" + " TNT - SSH 终端聊天室\n" + "\n" + "\033[1;37m快速开始\033[0m\n" + " 直接输入消息,Enter 发送\n" + " Esc 进入 NORMAL 浏览历史;G 回到最新;i 继续输入\n" + " : 输入命令;q 或 Esc 关闭输出面板\n" + "\n" + "\033[1;37m命令\033[0m\n" + " :users 在线用户\n" + " :last [N] 最近消息,默认 10,最多 50\n" + " :search <关键词> 搜索历史\n" + " :msg <用户> <文本> 私聊\n" + " :inbox 私聊收件箱\n" + " :nick <名字> 修改昵称\n" + " :mute-joins 静音/开启进出提示\n" + " :clear 清空命令输出\n" + " :q 断开连接\n" + "\n" + "\033[1;37m语言\033[0m\n" + " :lang 显示当前语言\n" + " :lang zh 切换中文\n" + " :lang en 切换英文\n" + "\n" + "\033[1;37m参见\033[0m\n" + " ? 完整按键参考\n"; + } + + return "\033[1;36mTNT(1) help\033[0m\n" + "\n" + "\033[1;37mName\033[0m\n" + " TNT - SSH terminal chat room\n" + "\n" + "\033[1;37mQuick start\033[0m\n" + " Type a message and press Enter to send\n" + " Esc enters NORMAL history browsing; G jumps latest; i types again\n" + " : enters COMMAND mode; q or Esc closes output panels\n" + "\n" + "\033[1;37mCommands\033[0m\n" + " :users show online users\n" + " :last [N] show recent messages, default 10, max 50\n" + " :search search history\n" + " :msg whisper privately\n" + " :inbox show whispers\n" + " :nick change nickname\n" + " :mute-joins toggle join/leave notices\n" + " :clear clear command output\n" + " :q disconnect\n" + "\n" + "\033[1;37mLanguage\033[0m\n" + " :lang show current language\n" + " :lang en switch to English\n" + " :lang zh switch to Chinese\n" + "\n" + "\033[1;37mSee also\033[0m\n" + " ? full key reference\n"; +} diff --git a/src/support.c b/src/support.c deleted file mode 100644 index 27ea42f..0000000 --- a/src/support.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "support.h" -#include "support_text.h" - -void support_append_interactive_panel(char *buffer, size_t buf_size, - size_t *pos, help_lang_t lang) { - if (!buffer || !pos) return; - - buffer_appendf(buffer, buf_size, pos, "%s", - support_text_interactive(lang)); -} - -void support_append_exec_panel(char *buffer, size_t buf_size, size_t *pos, - help_lang_t lang) { - if (!buffer || !pos) return; - - buffer_appendf(buffer, buf_size, pos, "%s", support_text_exec(lang)); -} diff --git a/src/support_text.c b/src/support_text.c deleted file mode 100644 index 5bea700..0000000 --- a/src/support_text.c +++ /dev/null @@ -1,97 +0,0 @@ -#include "support_text.h" - -const char *support_text_interactive(help_lang_t lang) { - if (lang == LANG_ZH) { - return "\033[1;36m支持 · support\033[0m\n" - "\n" - "\033[1;37m第一次进来\033[0m\n" - " INSERT 输入消息,Enter 发送,ESC 进入 NORMAL\n" - " NORMAL 浏览消息,G 回到最新,i 继续输入\n" - " COMMAND 按 : 输入命令,q/ESC 关闭当前面板\n" - "\n" - "\033[1;37m我想...\033[0m\n" - " 看谁在线 :users\n" - " 看最近历史 :last 20\n" - " 搜索聊天记录 :search \n" - " 回到最新消息 G 或 End\n" - " 私聊某个人 :msg \n" - " 查看私聊收件箱 :inbox\n" - " 静音进出提示 :mute-joins\n" - "\n" - "\033[1;37m遇到问题\033[0m\n" - " 看不到新消息: 在 NORMAL 按 G 或 End 回到最新\n" - " 粘贴多行文本: 直接粘贴,TNT 会等 Enter 后一次发送\n" - " 输入太长: 状态行接近限制时会提示,超出会响铃\n" - " 命令不记得: 输入 :help 看列表,输入 :support 回到这里\n" - " 连接断开: 可能是空闲超时、连接数限制或网络重连\n" - "\n" - "\033[2;37m更多: ? 打开完整按键帮助,:help 查看命令列表\033[0m\n"; - } - - return "\033[1;36mSupport\033[0m\n" - "\n" - "\033[1;37mFirst minute\033[0m\n" - " INSERT Type messages, Enter sends, ESC enters NORMAL\n" - " NORMAL Browse history, G jumps latest, i continues typing\n" - " COMMAND Press : for commands, q/ESC closes this panel\n" - "\n" - "\033[1;37mI want to...\033[0m\n" - " See who is online :users\n" - " See recent history :last 20\n" - " Search history :search \n" - " Return to latest G or End\n" - " Whisper someone :msg \n" - " Read whispers :inbox\n" - " Mute join notices :mute-joins\n" - "\n" - "\033[1;37mTroubleshooting\033[0m\n" - " Missing new messages: press G or End in NORMAL\n" - " Pasting many lines: paste normally, then Enter sends once\n" - " Message too long: the status line warns near the limit\n" - " Forgot a command: type :help or return here with :support\n" - " Disconnected: check idle timeout, limits, or reconnect\n" - "\n" - "\033[2;37mMore: ? opens full key help, :help lists commands\033[0m\n"; -} - -const char *support_text_exec(help_lang_t lang) { - if (lang == LANG_ZH) { - return "TNT 支持\n" - "\n" - "交互使用:\n" - " ssh -p 2222 HOST\n" - " INSERT: 输入消息并按 Enter 发送\n" - " NORMAL: G 回到最新,k/PageUp 查看更早消息\n" - " COMMAND: 按 : 后可运行 users, last, search, msg, inbox\n" - "\n" - "非交互检查:\n" - " ssh -p 2222 HOST health\n" - " ssh -p 2222 HOST stats --json\n" - " ssh -p 2222 HOST users --json\n" - " ssh -p 2222 HOST 'tail -n 20'\n" - " ssh -p 2222 USER@HOST post 'message'\n" - "\n" - "排查:\n" - " 连接过早关闭: 检查限流、空闲超时、连接容量、\n" - " 单 IP 限制和防火墙规则。\n"; - } - - return "TNT support\n" - "\n" - "Interactive use:\n" - " ssh -p 2222 HOST\n" - " INSERT: type and press Enter to send\n" - " NORMAL: press G for latest, k/PageUp for older messages\n" - " COMMAND: press : then run users, last, search, msg, inbox\n" - "\n" - "Non-interactive checks:\n" - " ssh -p 2222 HOST health\n" - " ssh -p 2222 HOST stats --json\n" - " ssh -p 2222 HOST users --json\n" - " ssh -p 2222 HOST 'tail -n 20'\n" - " ssh -p 2222 USER@HOST post 'message'\n" - "\n" - "Troubleshooting:\n" - " Connection closes early: check rate limits, idle timeout,\n" - " global connection capacity, per-IP limits, and firewall rules.\n"; -} diff --git a/tests/test_exec_mode.sh b/tests/test_exec_mode.sh index 880f22d..5578b8e 100755 --- a/tests/test_exec_mode.sh +++ b/tests/test_exec_mode.sh @@ -75,18 +75,6 @@ else FAIL=$((FAIL + 1)) fi -SUPPORT_OUTPUT=$(ssh $SSH_OPTS localhost support 2>/dev/null || true) -printf '%s\n' "$SUPPORT_OUTPUT" | grep -Eq '^TNT (support|支持)$' && -printf '%s\n' "$SUPPORT_OUTPUT" | grep -Eq '^(Troubleshooting|排查):' -if [ $? -eq 0 ]; then - echo "✓ support returns quick guide" - PASS=$((PASS + 1)) -else - echo "✗ support output unexpected" - printf '%s\n' "$SUPPORT_OUTPUT" - FAIL=$((FAIL + 1)) -fi - HELP_OUTPUT=$(ssh $SSH_OPTS localhost help 2>/dev/null || true) printf '%s\n' "$HELP_OUTPUT" | grep -q '^TNT exec 接口$' && printf '%s\n' "$HELP_OUTPUT" | grep -q '^命令:$' diff --git a/tests/test_interactive_input.sh b/tests/test_interactive_input.sh index 20e412d..bfbd577 100755 --- a/tests/test_interactive_input.sh +++ b/tests/test_interactive_input.sh @@ -64,7 +64,7 @@ set timeout 10 spawn ssh $SSH_OPTS anonymous@127.0.0.1 sleep 1 send -- "tester\r" -expect ":support" +expect ":help" send -- "\033\[200~" send -- "line1\nline2\nline3" send -- "\033\[201~" @@ -133,19 +133,19 @@ else FAIL=$((FAIL + 1)) fi -SUPPORT_SCRIPT="$STATE_DIR/support.expect" -cat >"$SUPPORT_SCRIPT" <"$HELP_SCRIPT" <"$STATE_DIR/support.log" 2>&1; then - echo "✓ :support renders quick guide" +if expect "$HELP_SCRIPT" >"$STATE_DIR/help.log" 2>&1; then + echo "✓ :help renders concise manual" PASS=$((PASS + 1)) else - echo "x :support command failed" - sed -n '1,160p' "$STATE_DIR/support.log" + echo "x :help command failed" + sed -n '1,160p' "$STATE_DIR/help.log" sed -n '1,120p' "$STATE_DIR/server.log" FAIL=$((FAIL + 1)) fi @@ -178,13 +178,13 @@ set timeout 10 spawn ssh $SSH_OPTS anonymous@127.0.0.1 sleep 1 send -- "mistype\r" -expect ":support" +expect ":help" send -- "\033" expect "NORMAL" send -- ":" expect ":" -send -- "suport\r" -expect "你是想输入 :support 吗?" +send -- "hlep\r" +expect "你是想输入 :help 吗?" expect "按任意键" send -- "q" sleep 0.2 @@ -210,7 +210,7 @@ set timeout 10 spawn ssh $SSH_OPTS anonymous@127.0.0.1 sleep 1 send -- "localized\r" -expect ":support" +expect ":help" send -- "\033" expect "NORMAL" send -- ":" @@ -260,7 +260,7 @@ set timeout 10 spawn ssh $SSH_OPTS anonymous@127.0.0.1 sleep 1 send -- "usageuser\r" -expect ":support" +expect ":help" send -- "\033" expect "NORMAL" send -- ":" @@ -328,7 +328,7 @@ set timeout 10 spawn ssh $SSH_OPTS anonymous@127.0.0.1 sleep 1 send -- "systemuser\r" -expect ":support" +expect ":help" send -- "\033" expect "NORMAL" send -- ":" diff --git a/tests/unit/Makefile b/tests/unit/Makefile index edc09d9..59e4a13 100644 --- a/tests/unit/Makefile +++ b/tests/unit/Makefile @@ -19,10 +19,10 @@ HISTORY_VIEW_SRC = ../../src/history_view.c I18N_SRC = ../../src/i18n.c SYSTEM_MESSAGE_SRC = ../../src/system_message.c HELP_TEXT_SRC = ../../src/help_text.c -SUPPORT_TEXT_SRC = ../../src/support_text.c +MANUAL_TEXT_SRC = ../../src/manual_text.c RATELIMIT_SRC = ../../src/ratelimit.c -TESTS = test_utf8 test_message test_chat_room test_history_view test_i18n test_system_message test_help_text test_support_text test_cli_text test_ratelimit +TESTS = test_utf8 test_message test_chat_room test_history_view test_i18n test_system_message test_help_text test_manual_text test_cli_text test_ratelimit .PHONY: all clean run @@ -49,7 +49,7 @@ test_system_message: test_system_message.c $(SYSTEM_MESSAGE_SRC) $(I18N_SRC) test_help_text: test_help_text.c $(HELP_TEXT_SRC) $(COMMON_SRC) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) -test_support_text: test_support_text.c $(SUPPORT_TEXT_SRC) +test_manual_text: test_manual_text.c $(MANUAL_TEXT_SRC) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) test_cli_text: test_cli_text.c $(CLI_TEXT_SRC) $(COMMON_SRC) @@ -80,8 +80,8 @@ run: all @echo "=== Running Help Text Tests ===" ./test_help_text @echo "" - @echo "=== Running Support Text Tests ===" - ./test_support_text + @echo "=== Running Manual Text Tests ===" + ./test_manual_text @echo "" @echo "=== Running CLI Text Tests ===" ./test_cli_text diff --git a/tests/unit/test_help_text.c b/tests/unit/test_help_text.c index 12bfbdc..90cd67a 100644 --- a/tests/unit/test_help_text.c +++ b/tests/unit/test_help_text.c @@ -19,39 +19,25 @@ TEST(full_help_matches_language) { const char *en = help_text_full(LANG_EN); const char *zh = help_text_full(LANG_ZH); - assert(strstr(en, "TERMINAL CHAT ROOM - HELP") != NULL); + assert(strstr(en, "TNT KEY REFERENCE") != NULL); assert(strstr(en, "AVAILABLE COMMANDS") != NULL); + assert(strstr(en, ":inbox") != NULL); + assert(strstr(en, ":support") == NULL); + assert(strstr(en, ":commands") == NULL); assert(strstr(en, "Switch English/Chinese") != NULL); - assert(strstr(zh, "终端聊天室 - 帮助") != NULL); + assert(strstr(zh, "TNT 按键参考") != NULL); assert(strstr(zh, "可用命令") != NULL); + assert(strstr(zh, ":inbox") != NULL); + assert(strstr(zh, ":support") == NULL); + assert(strstr(zh, ":commands") == NULL); assert(strstr(zh, "切换英文/中文") != NULL); } -TEST(command_help_matches_language) { - char out[2048]; - size_t pos; - - out[0] = '\0'; - pos = 0; - help_text_append_commands(out, sizeof(out), &pos, LANG_EN); - assert(strstr(out, "Available Commands") != NULL); - assert(strstr(out, "Show online users") != NULL); - assert(pos == strlen(out)); - - out[0] = '\0'; - pos = 0; - help_text_append_commands(out, sizeof(out), &pos, LANG_ZH); - assert(strstr(out, "可用命令") != NULL); - assert(strstr(out, "显示在线用户") != NULL); - assert(pos == strlen(out)); -} - int main(void) { printf("Running help text unit tests...\n\n"); RUN_TEST(full_help_matches_language); - RUN_TEST(command_help_matches_language); printf("\n✓ All %d tests passed!\n", tests_passed); return 0; diff --git a/tests/unit/test_i18n.c b/tests/unit/test_i18n.c index d1ab38f..a7c061e 100644 --- a/tests/unit/test_i18n.c +++ b/tests/unit/test_i18n.c @@ -81,9 +81,9 @@ TEST(text_lookup_matches_language) { assert(strstr(i18n_text(LANG_ZH, I18N_WELCOME_SUBTITLE), "匿名聊天室") != NULL); assert(strstr(i18n_text(LANG_EN, I18N_HELP_STATUS_FORMAT), - "HELP") != NULL); + "KEY REFERENCE") != NULL); assert(strstr(i18n_text(LANG_ZH, I18N_HELP_STATUS_FORMAT), - "帮助") != NULL); + "按键参考") != NULL); assert(strstr(i18n_text(LANG_EN, I18N_COMMAND_OUTPUT_TITLE), "COMMAND") != NULL); assert(strstr(i18n_text(LANG_ZH, I18N_COMMAND_OUTPUT_TITLE), @@ -118,8 +118,12 @@ TEST(text_lookup_matches_language) { "未知命令") != NULL); assert(strstr(i18n_text(LANG_EN, I18N_EXEC_HELP), "TNT exec interface") != NULL); + assert(strstr(i18n_text(LANG_EN, I18N_EXEC_HELP), + "support") == NULL); assert(strstr(i18n_text(LANG_ZH, I18N_EXEC_HELP), "TNT exec 接口") != NULL); + assert(strstr(i18n_text(LANG_ZH, I18N_EXEC_HELP), + "support") == NULL); assert(strstr(i18n_text(LANG_EN, I18N_EXEC_POST_EMPTY), "message cannot be empty") != NULL); assert(strstr(i18n_text(LANG_ZH, I18N_EXEC_POST_EMPTY), diff --git a/tests/unit/test_manual_text.c b/tests/unit/test_manual_text.c new file mode 100644 index 0000000..9309a51 --- /dev/null +++ b/tests/unit/test_manual_text.c @@ -0,0 +1,44 @@ +/* Unit tests for concise manual text language selection */ + +#include "../../include/manual_text.h" +#include +#include +#include + +#define TEST(name) static void test_##name() +#define RUN_TEST(name) do { \ + printf("Running %s... ", #name); \ + test_##name(); \ + printf("✓\n"); \ + tests_passed++; \ +} while(0) + +static int tests_passed = 0; + +TEST(interactive_manual_matches_language) { + const char *en = manual_text_interactive(LANG_EN); + const char *zh = manual_text_interactive(LANG_ZH); + + assert(strstr(en, "TNT(1) help") != NULL); + assert(strstr(en, "Quick start") != NULL); + assert(strstr(en, "Commands") != NULL); + assert(strstr(en, ":mute-joins") != NULL); + assert(strstr(en, ":support") == NULL); + assert(strstr(en, ":commands") == NULL); + + assert(strstr(zh, "TNT(1) 帮助") != NULL); + assert(strstr(zh, "快速开始") != NULL); + assert(strstr(zh, "命令") != NULL); + assert(strstr(zh, ":mute-joins") != NULL); + assert(strstr(zh, ":support") == NULL); + assert(strstr(zh, ":commands") == NULL); +} + +int main(void) { + printf("Running manual text unit tests...\n\n"); + + RUN_TEST(interactive_manual_matches_language); + + printf("\n✓ All %d tests passed!\n", tests_passed); + return 0; +} diff --git a/tests/unit/test_support_text.c b/tests/unit/test_support_text.c deleted file mode 100644 index 70f285e..0000000 --- a/tests/unit/test_support_text.c +++ /dev/null @@ -1,52 +0,0 @@ -/* Unit tests for support text language selection */ - -#include "../../include/support_text.h" -#include -#include -#include - -#define TEST(name) static void test_##name() -#define RUN_TEST(name) do { \ - printf("Running %s... ", #name); \ - test_##name(); \ - printf("✓\n"); \ - tests_passed++; \ -} while(0) - -static int tests_passed = 0; - -TEST(interactive_support_matches_language) { - const char *en = support_text_interactive(LANG_EN); - const char *zh = support_text_interactive(LANG_ZH); - - assert(strstr(en, "Support") != NULL); - assert(strstr(en, "First minute") != NULL); - assert(strstr(en, ":mute-joins") != NULL); - - assert(strstr(zh, "支持") != NULL); - assert(strstr(zh, "第一次进来") != NULL); - assert(strstr(zh, ":mute-joins") != NULL); -} - -TEST(exec_support_matches_language) { - const char *en = support_text_exec(LANG_EN); - const char *zh = support_text_exec(LANG_ZH); - - assert(strstr(en, "TNT support") != NULL); - assert(strstr(en, "Non-interactive checks") != NULL); - assert(strstr(en, "stats --json") != NULL); - - assert(strstr(zh, "TNT 支持") != NULL); - assert(strstr(zh, "非交互检查") != NULL); - assert(strstr(zh, "stats --json") != NULL); -} - -int main(void) { - printf("Running support text unit tests...\n\n"); - - RUN_TEST(interactive_support_matches_language); - RUN_TEST(exec_support_matches_language); - - printf("\n✓ All %d tests passed!\n", tests_passed); - return 0; -} diff --git a/tnt.1 b/tnt.1 index 91d372e..37beed0 100644 --- a/tnt.1 +++ b/tnt.1 @@ -1,5 +1,5 @@ .\" tnt(1) - Terminal Network Talk -.TH TNT 1 "May 2026" "TNT 1.0.1" "User Commands" +.TH TNT 1 "2026-05-24" "TNT 1.0.1" "User Commands" .SH NAME tnt \- anonymous SSH chat server with Vim\-style TUI .SH SYNOPSIS @@ -15,7 +15,8 @@ tnt \- anonymous SSH chat server with Vim\-style TUI is a multi\-user anonymous chat server accessed over SSH. It provides a Vim\-style terminal user interface with INSERT, NORMAL, and COMMAND modes. -Users connect with any standard SSH client; no account or registration is needed. +Users connect with any standard SSH client; no account or registration is +needed. .PP Messages are persisted to a log file and restored on server restart. The server supports CJK and emoji input, rate limiting, access tokens, and @@ -44,7 +45,6 @@ Print version and exit. .BR \-h ", " \-\-help Print a short usage summary and exit. .SH CONNECTING -.PP .nf ssh any\-username@hostname \-p 2222 .fi @@ -70,7 +70,7 @@ to return to INSERT, .B : to enter COMMAND mode, .B ? -to open the help screen. +to open the full key reference. .TP .B COMMAND Execute commands prefixed with @@ -102,7 +102,7 @@ End/Home Jump to bottom/top g/G Jump to top/bottom i Switch to INSERT : Enter COMMAND mode -? Open help screen +? Open full key reference Ctrl+C Disconnect .TE .PP @@ -117,12 +117,12 @@ l l. :name \fIname\fR Alias for :nick :msg \fIuser text\fR Send private whisper :w \fIuser text\fR Short alias for :msg +:inbox Show private whispers :last [\fIN\fR] Show last N messages from history (1\-50, default 10) :search \fIkeyword\fR Case\-insensitive search across full message history :mute\-joins Toggle join/leave system notifications on/off -:support Show quick support guide :lang \fIen|zh\fR Switch UI language for this session -:help Show available commands +:help Show concise manual :clear Clear command output :q, :quit, :exit Disconnect Up/Down Browse command history @@ -133,7 +133,6 @@ Commands can be run non\-interactively for scripting: .PP .nf ssh host \-p 2222 help -ssh host \-p 2222 support ssh host \-p 2222 users \-\-json ssh host \-p 2222 stats \-\-json ssh host \-p 2222 tail 20 @@ -246,6 +245,7 @@ m1ngsama .SH BUGS Report bugs at .UR https://github.com/m1ngsama/TNT/issues +the project issue tracker .UE . .SH SEE ALSO .BR ssh (1),