ux: collapse help surface around manual

This commit is contained in:
m1ngsama 2026-05-24 10:17:25 +08:00
parent 15aac7134f
commit a693d281f8
27 changed files with 236 additions and 368 deletions

View file

@ -82,7 +82,7 @@ Ctrl+F/B - Scroll full page down/up
PgDn/PgUp - Scroll full page down/up PgDn/PgUp - Scroll full page down/up
End/Home - Jump to bottom/top End/Home - Jump to bottom/top
g/G - Jump to top/bottom g/G - Jump to top/bottom
? - Show help ? - Show full key reference
Ctrl+C - Exit chat Ctrl+C - Exit chat
``` ```
@ -92,12 +92,12 @@ Ctrl+C - Exit chat
:nick <name> - Change nickname :nick <name> - Change nickname
:msg <user> <text> - Whisper to user :msg <user> <text> - Whisper to user
:w <user> <text> - Short alias for :msg :w <user> <text> - Short alias for :msg
:inbox - Show whispers
:last [N] - Show last N messages from history (max 50, default 10) :last [N] - Show last N messages from history (max 50, default 10)
:search <keyword> - Search full message history (case-insensitive) :search <keyword> - Search full message history (case-insensitive)
:mute-joins - Toggle join/leave system notifications :mute-joins - Toggle join/leave system notifications
:support - Show quick support guide
:lang <en|zh> - Switch UI language for this session :lang <en|zh> - Switch UI language for this session
:help - Show available commands :help - Show concise manual
:clear - Clear command output :clear - Clear command output
:q, :quit, :exit - Disconnect :q, :quit, :exit - Disconnect
Up/Down - Browse command history 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 health
ssh -p 2222 chat.m1ng.space stats --json ssh -p 2222 chat.m1ng.space stats --json
ssh -p 2222 chat.m1ng.space users 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 chat.m1ng.space "tail -n 20"
ssh -p 2222 operator@chat.m1ng.space post "service notice" ssh -p 2222 operator@chat.m1ng.space post "service notice"
ssh -p 2222 chat.m1ng.space post "/me deploys v2.0" ssh -p 2222 chat.m1ng.space post "/me deploys v2.0"
@ -256,8 +255,9 @@ TNT/
│ ├── chat_room.c # chat room logic │ ├── chat_room.c # chat room logic
│ ├── message.c # message persistence │ ├── message.c # message persistence
│ ├── history_view.c # message viewport and scroll state │ ├── history_view.c # message viewport and scroll state
│ ├── help_text.c # full-screen and command help content │ ├── help_text.c # full-screen key reference content
│ ├── support_text.c # quick support guide content │ ├── manual.c # concise manual panel rendering
│ ├── manual_text.c # concise manual content
│ ├── i18n.c # language selection and shared UI text │ ├── i18n.c # language selection and shared UI text
│ ├── ratelimit.c # connection limits and rate limiting │ ├── ratelimit.c # connection limits and rate limiting
│ ├── tui.c # terminal UI rendering │ ├── tui.c # terminal UI rendering

View file

@ -1,5 +1,16 @@
# Changelog # 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 ## 1.0.1 - 2026-05-24 - Release candidate hardening
### Added ### Added

View file

@ -58,7 +58,7 @@ Deployments are operator-driven:
3. Install the new binary. 3. Install the new binary.
4. Restart the service. 4. Restart the service.
5. Run black-box checks (`health`, `stats --json`, `users --json`, 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: The installer can still be used manually on a server:
curl -sSL https://raw.githubusercontent.com/m1ngsama/TNT/main/install.sh | sh curl -sSL https://raw.githubusercontent.com/m1ngsama/TNT/main/install.sh | sh

View file

@ -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) # Systemd deployment (state dir is /var/lib/tnt)
sudo tee /var/lib/tnt/motd.txt <<'EOF' sudo tee /var/lib/tnt/motd.txt <<'EOF'
Welcome! Be respectful. No spam. Welcome! Be respectful. No spam.
Type :help for available commands. Type :help for a concise manual, or ? for the full key reference.
EOF EOF
sudo chown tnt:tnt /var/lib/tnt/motd.txt sudo chown tnt:tnt /var/lib/tnt/motd.txt

View file

@ -79,8 +79,9 @@ src/
├── tui.c - Terminal UI rendering (ANSI escape codes) ├── tui.c - Terminal UI rendering (ANSI escape codes)
├── tui_status.c - Mode/status/input-line rendering ├── tui_status.c - Mode/status/input-line rendering
├── i18n.c - Language selection and shared UI text ├── i18n.c - Language selection and shared UI text
├── help_text.c - Full-screen and command help text ├── help_text.c - Full-screen key reference text
├── support_text.c - Quick support guide text ├── manual.c - Concise manual panel rendering
├── manual_text.c - Concise manual text
├── system_message.c - Localized join/leave/nick system messages ├── system_message.c - Localized join/leave/nick system messages
├── ratelimit.c - Per-IP and global connection limits ├── ratelimit.c - Per-IP and global connection limits
└── utf8.c - UTF-8 character handling └── utf8.c - UTF-8 character handling
@ -98,8 +99,9 @@ include/
├── history_view.h - Scroll-state helpers ├── history_view.h - Scroll-state helpers
├── tui.h - TUI rendering functions ├── tui.h - TUI rendering functions
├── i18n.h - Language and shared text IDs ├── i18n.h - Language and shared text IDs
├── help_text.h - Help text interface ├── help_text.h - Key reference text interface
├── support_text.h - Support guide text interface ├── manual.h - Concise manual panel interface
├── manual_text.h - Concise manual text interface
├── ratelimit.h - Connection limit interface ├── ratelimit.h - Connection limit interface
└── utf8.h - UTF-8 utilities └── 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.** 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 ```c
"AVAILABLE COMMANDS:\n" "AVAILABLE COMMANDS:\n"
" newcmd - Description of new command\n" " newcmd - Description of new command\n"

View file

@ -27,11 +27,12 @@ COMMANDS (COMMAND mode, prefix with :)
nick <name> change nickname nick <name> change nickname
msg <user> <text> whisper to user msg <user> <text> whisper to user
w <user> <text> alias for msg w <user> <text> alias for msg
inbox show whispers
last [N] last N messages from log (default 10, max 50) last [N] last N messages from log (default 10, max 50)
search <keyword> search full history (case-insensitive, 15 results) search <keyword> search full history (case-insensitive, 15 results)
mute-joins toggle join/leave notifications mute-joins toggle join/leave notifications
support quick support guide help concise manual
help show all commands lang [en|zh] show or switch UI language
clear clear output clear clear output
q / quit / exit disconnect q / quit / exit disconnect
@ -52,8 +53,9 @@ STRUCTURE
src/exec.c SSH exec command dispatch src/exec.c SSH exec command dispatch
src/message.c persistence, search src/message.c persistence, search
src/history_view.c message viewport / scroll state src/history_view.c message viewport / scroll state
src/help_text.c full-screen and command help text src/help_text.c full-screen key reference text
src/support_text.c quick support guide content src/manual.c concise manual panel rendering
src/manual_text.c concise manual content
src/i18n.c language selection and shared text src/i18n.c language selection and shared text
src/ratelimit.c connection limits and rate limiting src/ratelimit.c connection limits and rate limiting
src/tui.c rendering src/tui.c rendering

View file

@ -4,7 +4,5 @@
#include "common.h" #include "common.h"
const char *help_text_full(help_lang_t lang); 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 */ #endif /* HELP_TEXT_H */

9
include/manual.h Normal file
View file

@ -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 */

8
include/manual_text.h Normal file
View file

@ -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 */

View file

@ -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 */

View file

@ -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 */

View file

@ -8,10 +8,9 @@
#include "chat_room.h" #include "chat_room.h"
#include "client.h" #include "client.h"
#include "common.h" #include "common.h"
#include "help_text.h"
#include "i18n.h" #include "i18n.h"
#include "manual.h"
#include "message.h" #include "message.h"
#include "support.h"
#include "system_message.h" #include "system_message.h"
#include "tui.h" #include "tui.h"
#include "utf8.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 *suggest_command(const char *cmd) {
static const char *commands[] = { static const char *commands[] = {
"list", "users", "who", "nick", "name", "msg", "w", "inbox", "list", "users", "who", "nick", "name", "msg", "w", "inbox",
"last", "search", "mute-joins", "mute", "support", "guide", "last", "search", "mute-joins", "mute", "lang", "language",
"lang", "language", "help", "commands", "clear", "cls", "help", "clear", "cls",
"q", "quit", "exit" "q", "quit", "exit"
}; };
const char *best = NULL; const char *best = NULL;
@ -168,12 +167,8 @@ void commands_dispatch(client_t *client) {
} }
pthread_rwlock_unlock(&g_room->lock); pthread_rwlock_unlock(&g_room->lock);
} else if (strcmp(cmd, "help") == 0 || strcmp(cmd, "commands") == 0) { } else if (strcmp(cmd, "help") == 0) {
help_text_append_commands(output, sizeof(output), &pos, manual_append_interactive_panel(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); client->help_lang);
} else if (strcmp(cmd, "lang") == 0 || strcmp(cmd, "language") == 0 || } else if (strcmp(cmd, "lang") == 0 || strcmp(cmd, "language") == 0 ||

View file

@ -6,7 +6,6 @@
#include "input.h" #include "input.h"
#include "message.h" #include "message.h"
#include "ratelimit.h" #include "ratelimit.h"
#include "support.h"
#include "utf8.h" #include "utf8.h"
#include <ctype.h> #include <ctype.h>
#include <stdio.h> #include <stdio.h>
@ -122,14 +121,6 @@ static int exec_command_help(client_t *client) {
return client_send(client, help_text, strlen(help_text)) == 0 ? 0 : 1; 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 int exec_command_health(client_t *client) {
static const char ok[] = "ok\n"; static const char ok[] = "ok\n";
return client_send(client, ok, sizeof(ok) - 1) == 0 ? 0 : 1; 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) { if (strcmp(cmd, "help") == 0 || strcmp(cmd, "--help") == 0) {
return exec_command_help(client); return exec_command_help(client);
} }
if (strcmp(cmd, "support") == 0 || strcmp(cmd, "guide") == 0) {
return exec_command_support(client);
}
if (strcmp(cmd, "health") == 0) { if (strcmp(cmd, "health") == 0) {
return exec_command_health(client); return exec_command_health(client);
} }

View file

@ -1,60 +1,8 @@
#include "help_text.h" #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 <name> - 修改昵称\n"
"msg/w <user> <text> - 私聊用户\n"
"inbox - 查看私聊历史\n"
"last [N] - 查看最近 N 条消息\n"
"search <keyword> - 搜索消息历史\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 <action> - 发送动作消息\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 <name> - Change nickname\n"
"msg/w <user> <text> - Whisper to user (private)\n"
"inbox - Show whisper history\n"
"last [N] - Show last N messages\n"
"search <keyword> - 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 <action> - Send action message\n"
" @username - Mention (bell notify)\n"
"========================================\n");
}
const char *help_text_full(help_lang_t lang) { const char *help_text_full(help_lang_t lang) {
if (lang == LANG_EN) { if (lang == LANG_EN) {
return "TERMINAL CHAT ROOM - HELP\n" return "TNT KEY REFERENCE\n"
"\n" "\n"
"OPERATING MODES:\n" "OPERATING MODES:\n"
" INSERT - Type and send messages (default)\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" " PgDn/PgUp - Scroll full page down/up\n"
" End/Home - Jump to bottom/top\n" " End/Home - Jump to bottom/top\n"
" g/G - Jump to top/bottom\n" " g/G - Jump to top/bottom\n"
" ? - Show this help\n" " ? - Show full key reference\n"
" Ctrl+C - Exit chat\n" " Ctrl+C - Exit chat\n"
"\n" "\n"
"AVAILABLE COMMANDS:\n" "AVAILABLE COMMANDS:\n"
@ -88,12 +36,12 @@ const char *help_text_full(help_lang_t lang) {
" :nick <name> - Change nickname\n" " :nick <name> - Change nickname\n"
" :msg <user> <text> - Whisper to user\n" " :msg <user> <text> - Whisper to user\n"
" :w <user> <text> - Short alias for :msg\n" " :w <user> <text> - Short alias for :msg\n"
" :inbox - Show whispers\n"
" :last [N] - Show last N messages (max 50)\n" " :last [N] - Show last N messages (max 50)\n"
" :search <keyword> - Search message history\n" " :search <keyword> - Search message history\n"
" :mute-joins - Toggle join/leave notices\n" " :mute-joins - Toggle join/leave notices\n"
" :support - Show quick support guide\n" " :help - Show concise manual\n"
" :lang <en|zh> - Switch UI language\n" " :lang <en|zh> - Switch UI language\n"
" :help - Show available commands\n"
" :clear - Clear command output\n" " :clear - Clear command output\n"
" :q, :quit, :exit - Disconnect\n" " :q, :quit, :exit - Disconnect\n"
"\n" "\n"
@ -110,7 +58,7 @@ const char *help_text_full(help_lang_t lang) {
" e/z - Switch English/Chinese\n"; " e/z - Switch English/Chinese\n";
} }
return "终端聊天室 - 帮助\n" return "TNT 按键参考\n"
"\n" "\n"
"操作模式:\n" "操作模式:\n"
" INSERT - 输入和发送消息(默认)\n" " INSERT - 输入和发送消息(默认)\n"
@ -136,7 +84,7 @@ const char *help_text_full(help_lang_t lang) {
" PgDn/PgUp - 向下/上滚动整页\n" " PgDn/PgUp - 向下/上滚动整页\n"
" End/Home - 跳到底部/顶部\n" " End/Home - 跳到底部/顶部\n"
" g/G - 跳到顶部/底部\n" " g/G - 跳到顶部/底部\n"
" ? - 显示此帮助\n" " ? - 显示完整按键参考\n"
" Ctrl+C - 退出聊天\n" " Ctrl+C - 退出聊天\n"
"\n" "\n"
"可用命令:\n" "可用命令:\n"
@ -144,12 +92,12 @@ const char *help_text_full(help_lang_t lang) {
" :nick <名字> - 更改昵称\n" " :nick <名字> - 更改昵称\n"
" :msg <用户> <文本> - 私聊\n" " :msg <用户> <文本> - 私聊\n"
" :w <用户> <文本> - :msg 的简写\n" " :w <用户> <文本> - :msg 的简写\n"
" :inbox - 查看私聊\n"
" :last [N] - 显示最后 N 条消息(最多50)\n" " :last [N] - 显示最后 N 条消息(最多50)\n"
" :search <关键词> - 搜索消息历史\n" " :search <关键词> - 搜索消息历史\n"
" :mute-joins - 切换加入/离开提示\n" " :mute-joins - 切换加入/离开提示\n"
" :support - 显示快速支持指南\n" " :help - 显示简明手册\n"
" :lang <en|zh> - 切换界面语言\n" " :lang <en|zh> - 切换界面语言\n"
" :help - 显示可用命令\n"
" :clear - 清空命令输出\n" " :clear - 清空命令输出\n"
" :q, :quit, :exit - 断开连接\n" " :q, :quit, :exit - 断开连接\n"
"\n" "\n"

View file

@ -104,17 +104,17 @@ const char *i18n_text(help_lang_t lang, i18n_text_id_t id) {
case I18N_WELCOME_FALLBACK_FORMAT: case I18N_WELCOME_FALLBACK_FORMAT:
return "TNT %s - SSH 匿名聊天室\r\n\r\n"; return "TNT %s - SSH 匿名聊天室\r\n\r\n";
case I18N_INSERT_HINT_WIDE: case I18N_INSERT_HINT_WIDE:
return "Enter 发送 · Esc 浏览 · :support"; return "Enter 发送 · Esc 浏览 · :help";
case I18N_INSERT_HINT_NARROW: case I18N_INSERT_HINT_NARROW:
return "Enter · Esc · :support"; return "Enter · Esc · :help";
case I18N_NORMAL_LATEST: case I18N_NORMAL_LATEST:
return "G 最新"; return "G 最新";
case I18N_NORMAL_NEW_MESSAGES: case I18N_NORMAL_NEW_MESSAGES:
return "新消息"; return "新消息";
case I18N_HELP_TITLE: case I18N_HELP_TITLE:
return " 帮助 "; return " 按键 ";
case I18N_HELP_STATUS_FORMAT: 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: case I18N_COMMAND_OUTPUT_TITLE:
return " 命令输出 "; return " 命令输出 ";
case I18N_MOTD_TITLE: 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: case I18N_TITLE_MUTED:
return "静音"; return "静音";
case I18N_TITLE_HELP_HINT: case I18N_TITLE_HELP_HINT:
return "? 帮助"; return "? 按键";
case I18N_IDLE_TIMEOUT_FORMAT: case I18N_IDLE_TIMEOUT_FORMAT:
return "\r\n\033[33m已断开: 空闲超时 (%d 分钟)\033[0m\r\n"; return "\r\n\033[33m已断开: 空闲超时 (%d 分钟)\033[0m\r\n";
case I18N_SYSTEM_USERNAME: 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: case I18N_DID_YOU_MEAN_FORMAT:
return "你是想输入 :%s 吗?\n"; return "你是想输入 :%s 吗?\n";
case I18N_UNKNOWN_GUIDANCE: case I18N_UNKNOWN_GUIDANCE:
return "输入 :support 查看引导,或 :help 查看命令\n"; return "输入 :help 查看帮助\n";
case I18N_EXEC_HELP: case I18N_EXEC_HELP:
return "TNT exec 接口\n" return "TNT exec 接口\n"
"命令:\n" "命令:\n"
@ -201,7 +201,6 @@ const char *i18n_text(help_lang_t lang, i18n_text_id_t id) {
" tail -n N 输出最近消息\n" " tail -n N 输出最近消息\n"
" post MESSAGE 非交互发送消息\n" " post MESSAGE 非交互发送消息\n"
" post \"/me act\" 发送动作消息\n" " post \"/me act\" 发送动作消息\n"
" support 显示快速支持指南\n"
" exit 成功退出\n"; " exit 成功退出\n";
case I18N_EXEC_USERS_USAGE: case I18N_EXEC_USERS_USAGE:
return "users: 用法: users [--json]\n"; 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: case I18N_WELCOME_FALLBACK_FORMAT:
return "TNT %s - anonymous chat over SSH\r\n\r\n"; return "TNT %s - anonymous chat over SSH\r\n\r\n";
case I18N_INSERT_HINT_WIDE: case I18N_INSERT_HINT_WIDE:
return "Enter send · Esc browse · :support"; return "Enter send · Esc browse · :help";
case I18N_INSERT_HINT_NARROW: case I18N_INSERT_HINT_NARROW:
return "Enter · Esc · :support"; return "Enter · Esc · :help";
case I18N_NORMAL_LATEST: case I18N_NORMAL_LATEST:
return "G latest"; return "G latest";
case I18N_NORMAL_NEW_MESSAGES: case I18N_NORMAL_NEW_MESSAGES:
return "new"; return "new";
case I18N_HELP_TITLE: case I18N_HELP_TITLE:
return " HELP "; return " KEYS ";
case I18N_HELP_STATUS_FORMAT: 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: case I18N_COMMAND_OUTPUT_TITLE:
return " COMMAND OUTPUT "; return " COMMAND OUTPUT ";
case I18N_MOTD_TITLE: 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: case I18N_TITLE_MUTED:
return "muted"; return "muted";
case I18N_TITLE_HELP_HINT: case I18N_TITLE_HELP_HINT:
return "? help"; return "? keys";
case I18N_IDLE_TIMEOUT_FORMAT: case I18N_IDLE_TIMEOUT_FORMAT:
return "\r\n\033[33mDisconnected: idle timeout (%d min)\033[0m\r\n"; return "\r\n\033[33mDisconnected: idle timeout (%d min)\033[0m\r\n";
case I18N_SYSTEM_USERNAME: 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: case I18N_DID_YOU_MEAN_FORMAT:
return "Did you mean :%s?\n"; return "Did you mean :%s?\n";
case I18N_UNKNOWN_GUIDANCE: case I18N_UNKNOWN_GUIDANCE:
return "Type :support for guidance or :help for commands\n"; return "Type :help for help\n";
case I18N_EXEC_HELP: case I18N_EXEC_HELP:
return "TNT exec interface\n" return "TNT exec interface\n"
"Commands:\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" " tail -n N Print recent messages\n"
" post MESSAGE Post a message non-interactively\n" " post MESSAGE Post a message non-interactively\n"
" post \"/me act\" Post an action message\n" " post \"/me act\" Post an action message\n"
" support Show quick support guide\n"
" exit Exit successfully\n"; " exit Exit successfully\n";
case I18N_EXEC_USERS_USAGE: case I18N_EXEC_USERS_USAGE:
return "users: usage: users [--json]\n"; return "users: usage: users [--json]\n";

10
src/manual.c Normal file
View file

@ -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));
}

63
src/manual_text.c Normal file
View file

@ -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 <keyword> search history\n"
" :msg <user> <text> whisper privately\n"
" :inbox show whispers\n"
" :nick <name> 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";
}

View file

@ -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));
}

View file

@ -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 <keyword>\n"
" 回到最新消息 G 或 End\n"
" 私聊某个人 :msg <user> <text>\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 <keyword>\n"
" Return to latest G or End\n"
" Whisper someone :msg <user> <text>\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";
}

View file

@ -75,18 +75,6 @@ else
FAIL=$((FAIL + 1)) FAIL=$((FAIL + 1))
fi 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) 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 '^TNT exec 接口$' &&
printf '%s\n' "$HELP_OUTPUT" | grep -q '^命令:$' printf '%s\n' "$HELP_OUTPUT" | grep -q '^命令:$'

View file

@ -64,7 +64,7 @@ set timeout 10
spawn ssh $SSH_OPTS anonymous@127.0.0.1 spawn ssh $SSH_OPTS anonymous@127.0.0.1
sleep 1 sleep 1
send -- "tester\r" send -- "tester\r"
expect ":support" expect ":help"
send -- "\033\[200~" send -- "\033\[200~"
send -- "line1\nline2\nline3" send -- "line1\nline2\nline3"
send -- "\033\[201~" send -- "\033\[201~"
@ -133,19 +133,19 @@ else
FAIL=$((FAIL + 1)) FAIL=$((FAIL + 1))
fi fi
SUPPORT_SCRIPT="$STATE_DIR/support.expect" HELP_SCRIPT="$STATE_DIR/help.expect"
cat >"$SUPPORT_SCRIPT" <<EOF cat >"$HELP_SCRIPT" <<EOF
set timeout 10 set timeout 10
spawn ssh $SSH_OPTS anonymous@127.0.0.1 spawn ssh $SSH_OPTS anonymous@127.0.0.1
sleep 1 sleep 1
send -- "supporter\r" send -- "helper\r"
expect ":support" expect ":help"
send -- "\033" send -- "\033"
expect "NORMAL" expect "NORMAL"
send -- ":" send -- ":"
expect ":" expect ":"
send -- "support\r" send -- "help\r"
expect "支持" expect "TNT\\(1\\) 帮助"
expect "按任意键" expect "按任意键"
send -- "q" send -- "q"
expect "NORMAL" expect "NORMAL"
@ -162,12 +162,12 @@ send -- "\003"
expect eof expect eof
EOF EOF
if expect "$SUPPORT_SCRIPT" >"$STATE_DIR/support.log" 2>&1; then if expect "$HELP_SCRIPT" >"$STATE_DIR/help.log" 2>&1; then
echo "✓ :support renders quick guide" echo "✓ :help renders concise manual"
PASS=$((PASS + 1)) PASS=$((PASS + 1))
else else
echo "x :support command failed" echo "x :help command failed"
sed -n '1,160p' "$STATE_DIR/support.log" sed -n '1,160p' "$STATE_DIR/help.log"
sed -n '1,120p' "$STATE_DIR/server.log" sed -n '1,120p' "$STATE_DIR/server.log"
FAIL=$((FAIL + 1)) FAIL=$((FAIL + 1))
fi fi
@ -178,13 +178,13 @@ set timeout 10
spawn ssh $SSH_OPTS anonymous@127.0.0.1 spawn ssh $SSH_OPTS anonymous@127.0.0.1
sleep 1 sleep 1
send -- "mistype\r" send -- "mistype\r"
expect ":support" expect ":help"
send -- "\033" send -- "\033"
expect "NORMAL" expect "NORMAL"
send -- ":" send -- ":"
expect ":" expect ":"
send -- "suport\r" send -- "hlep\r"
expect "你是想输入 :support 吗?" expect "你是想输入 :help 吗?"
expect "按任意键" expect "按任意键"
send -- "q" send -- "q"
sleep 0.2 sleep 0.2
@ -210,7 +210,7 @@ set timeout 10
spawn ssh $SSH_OPTS anonymous@127.0.0.1 spawn ssh $SSH_OPTS anonymous@127.0.0.1
sleep 1 sleep 1
send -- "localized\r" send -- "localized\r"
expect ":support" expect ":help"
send -- "\033" send -- "\033"
expect "NORMAL" expect "NORMAL"
send -- ":" send -- ":"
@ -260,7 +260,7 @@ set timeout 10
spawn ssh $SSH_OPTS anonymous@127.0.0.1 spawn ssh $SSH_OPTS anonymous@127.0.0.1
sleep 1 sleep 1
send -- "usageuser\r" send -- "usageuser\r"
expect ":support" expect ":help"
send -- "\033" send -- "\033"
expect "NORMAL" expect "NORMAL"
send -- ":" send -- ":"
@ -328,7 +328,7 @@ set timeout 10
spawn ssh $SSH_OPTS anonymous@127.0.0.1 spawn ssh $SSH_OPTS anonymous@127.0.0.1
sleep 1 sleep 1
send -- "systemuser\r" send -- "systemuser\r"
expect ":support" expect ":help"
send -- "\033" send -- "\033"
expect "NORMAL" expect "NORMAL"
send -- ":" send -- ":"

View file

@ -19,10 +19,10 @@ HISTORY_VIEW_SRC = ../../src/history_view.c
I18N_SRC = ../../src/i18n.c I18N_SRC = ../../src/i18n.c
SYSTEM_MESSAGE_SRC = ../../src/system_message.c SYSTEM_MESSAGE_SRC = ../../src/system_message.c
HELP_TEXT_SRC = ../../src/help_text.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 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 .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) test_help_text: test_help_text.c $(HELP_TEXT_SRC) $(COMMON_SRC)
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(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) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
test_cli_text: test_cli_text.c $(CLI_TEXT_SRC) $(COMMON_SRC) test_cli_text: test_cli_text.c $(CLI_TEXT_SRC) $(COMMON_SRC)
@ -80,8 +80,8 @@ run: all
@echo "=== Running Help Text Tests ===" @echo "=== Running Help Text Tests ==="
./test_help_text ./test_help_text
@echo "" @echo ""
@echo "=== Running Support Text Tests ===" @echo "=== Running Manual Text Tests ==="
./test_support_text ./test_manual_text
@echo "" @echo ""
@echo "=== Running CLI Text Tests ===" @echo "=== Running CLI Text Tests ==="
./test_cli_text ./test_cli_text

View file

@ -19,39 +19,25 @@ TEST(full_help_matches_language) {
const char *en = help_text_full(LANG_EN); const char *en = help_text_full(LANG_EN);
const char *zh = help_text_full(LANG_ZH); 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, "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(en, "Switch English/Chinese") != NULL);
assert(strstr(zh, "终端聊天室 - 帮助") != NULL); assert(strstr(zh, "TNT 按键参考") != NULL);
assert(strstr(zh, "可用命令") != NULL); assert(strstr(zh, "可用命令") != NULL);
assert(strstr(zh, ":inbox") != NULL);
assert(strstr(zh, ":support") == NULL);
assert(strstr(zh, ":commands") == NULL);
assert(strstr(zh, "切换英文/中文") != 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) { int main(void) {
printf("Running help text unit tests...\n\n"); printf("Running help text unit tests...\n\n");
RUN_TEST(full_help_matches_language); RUN_TEST(full_help_matches_language);
RUN_TEST(command_help_matches_language);
printf("\n✓ All %d tests passed!\n", tests_passed); printf("\n✓ All %d tests passed!\n", tests_passed);
return 0; return 0;

View file

@ -81,9 +81,9 @@ TEST(text_lookup_matches_language) {
assert(strstr(i18n_text(LANG_ZH, I18N_WELCOME_SUBTITLE), assert(strstr(i18n_text(LANG_ZH, I18N_WELCOME_SUBTITLE),
"匿名聊天室") != NULL); "匿名聊天室") != NULL);
assert(strstr(i18n_text(LANG_EN, I18N_HELP_STATUS_FORMAT), 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), assert(strstr(i18n_text(LANG_ZH, I18N_HELP_STATUS_FORMAT),
"帮助") != NULL); "按键参考") != NULL);
assert(strstr(i18n_text(LANG_EN, I18N_COMMAND_OUTPUT_TITLE), assert(strstr(i18n_text(LANG_EN, I18N_COMMAND_OUTPUT_TITLE),
"COMMAND") != NULL); "COMMAND") != NULL);
assert(strstr(i18n_text(LANG_ZH, I18N_COMMAND_OUTPUT_TITLE), assert(strstr(i18n_text(LANG_ZH, I18N_COMMAND_OUTPUT_TITLE),
@ -118,8 +118,12 @@ TEST(text_lookup_matches_language) {
"未知命令") != NULL); "未知命令") != NULL);
assert(strstr(i18n_text(LANG_EN, I18N_EXEC_HELP), assert(strstr(i18n_text(LANG_EN, I18N_EXEC_HELP),
"TNT exec interface") != NULL); "TNT exec interface") != NULL);
assert(strstr(i18n_text(LANG_EN, I18N_EXEC_HELP),
"support") == NULL);
assert(strstr(i18n_text(LANG_ZH, I18N_EXEC_HELP), assert(strstr(i18n_text(LANG_ZH, I18N_EXEC_HELP),
"TNT exec 接口") != NULL); "TNT exec 接口") != NULL);
assert(strstr(i18n_text(LANG_ZH, I18N_EXEC_HELP),
"support") == NULL);
assert(strstr(i18n_text(LANG_EN, I18N_EXEC_POST_EMPTY), assert(strstr(i18n_text(LANG_EN, I18N_EXEC_POST_EMPTY),
"message cannot be empty") != NULL); "message cannot be empty") != NULL);
assert(strstr(i18n_text(LANG_ZH, I18N_EXEC_POST_EMPTY), assert(strstr(i18n_text(LANG_ZH, I18N_EXEC_POST_EMPTY),

View file

@ -0,0 +1,44 @@
/* Unit tests for concise manual text language selection */
#include "../../include/manual_text.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#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;
}

View file

@ -1,52 +0,0 @@
/* Unit tests for support text language selection */
#include "../../include/support_text.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#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;
}

16
tnt.1
View file

@ -1,5 +1,5 @@
.\" tnt(1) - Terminal Network Talk .\" 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 .SH NAME
tnt \- anonymous SSH chat server with Vim\-style TUI tnt \- anonymous SSH chat server with Vim\-style TUI
.SH SYNOPSIS .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. is a multi\-user anonymous chat server accessed over SSH.
It provides a Vim\-style terminal user interface with INSERT, NORMAL, and It provides a Vim\-style terminal user interface with INSERT, NORMAL, and
COMMAND modes. 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 .PP
Messages are persisted to a log file and restored on server restart. Messages are persisted to a log file and restored on server restart.
The server supports CJK and emoji input, rate limiting, access tokens, and The server supports CJK and emoji input, rate limiting, access tokens, and
@ -44,7 +45,6 @@ Print version and exit.
.BR \-h ", " \-\-help .BR \-h ", " \-\-help
Print a short usage summary and exit. Print a short usage summary and exit.
.SH CONNECTING .SH CONNECTING
.PP
.nf .nf
ssh any\-username@hostname \-p 2222 ssh any\-username@hostname \-p 2222
.fi .fi
@ -70,7 +70,7 @@ to return to INSERT,
.B : .B :
to enter COMMAND mode, to enter COMMAND mode,
.B ? .B ?
to open the help screen. to open the full key reference.
.TP .TP
.B COMMAND .B COMMAND
Execute commands prefixed with Execute commands prefixed with
@ -102,7 +102,7 @@ End/Home Jump to bottom/top
g/G Jump to top/bottom g/G Jump to top/bottom
i Switch to INSERT i Switch to INSERT
: Enter COMMAND mode : Enter COMMAND mode
? Open help screen ? Open full key reference
Ctrl+C Disconnect Ctrl+C Disconnect
.TE .TE
.PP .PP
@ -117,12 +117,12 @@ l l.
:name \fIname\fR Alias for :nick :name \fIname\fR Alias for :nick
:msg \fIuser text\fR Send private whisper :msg \fIuser text\fR Send private whisper
:w \fIuser text\fR Short alias for :msg :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) :last [\fIN\fR] Show last N messages from history (1\-50, default 10)
:search \fIkeyword\fR Case\-insensitive search across full message history :search \fIkeyword\fR Case\-insensitive search across full message history
:mute\-joins Toggle join/leave system notifications on/off :mute\-joins Toggle join/leave system notifications on/off
:support Show quick support guide
:lang \fIen|zh\fR Switch UI language for this session :lang \fIen|zh\fR Switch UI language for this session
:help Show available commands :help Show concise manual
:clear Clear command output :clear Clear command output
:q, :quit, :exit Disconnect :q, :quit, :exit Disconnect
Up/Down Browse command history Up/Down Browse command history
@ -133,7 +133,6 @@ Commands can be run non\-interactively for scripting:
.PP .PP
.nf .nf
ssh host \-p 2222 help ssh host \-p 2222 help
ssh host \-p 2222 support
ssh host \-p 2222 users \-\-json ssh host \-p 2222 users \-\-json
ssh host \-p 2222 stats \-\-json ssh host \-p 2222 stats \-\-json
ssh host \-p 2222 tail 20 ssh host \-p 2222 tail 20
@ -246,6 +245,7 @@ m1ngsama <contact@m1ng.space>
.SH BUGS .SH BUGS
Report bugs at Report bugs at
.UR https://github.com/m1ngsama/TNT/issues .UR https://github.com/m1ngsama/TNT/issues
the project issue tracker
.UE . .UE .
.SH SEE ALSO .SH SEE ALSO
.BR ssh (1), .BR ssh (1),