mirror of
https://oauth2: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@github.com/m1ngsama/TNT.git
synced 2026-06-26 05:44:38 +08:00
67 lines
3.4 KiB
Markdown
67 lines
3.4 KiB
Markdown
# User Lifecycle
|
|
|
|
TNT solves one narrow problem: create a keyboard-first chat room that anyone
|
|
with an SSH client can join without installing a custom client.
|
|
|
|
The product path should stay short:
|
|
|
|
1. Operator installs `tnt`, chooses a state directory, and starts the server.
|
|
2. User connects with `ssh -p 2222 host`.
|
|
3. User picks a display name or presses Enter for `anonymous`.
|
|
4. User lands in INSERT mode at the live tail and can type immediately.
|
|
5. User presses Esc to browse history with Vim-style movement.
|
|
6. User uses `:help` for the concise manual or `?` for the full key reference.
|
|
7. User searches from NORMAL with `/term`, or uses commands when needed:
|
|
`:users`, `:msg`, `:reply`, `:inbox`, `:last`, `:search`, `:nick`,
|
|
`:mute-joins`, and `:q`.
|
|
8. Scripts and operators use `tntctl` or SSH exec commands for `health`,
|
|
`stats`, `users`, `tail`, `dump`, and `post`.
|
|
|
|
## TUI Experience Notes
|
|
|
|
- The first screen should make the product legible without reading external
|
|
docs: this is an SSH chat room, not a shell.
|
|
- INSERT mode is the default because most users arrive to send a message.
|
|
- NORMAL mode opens at the latest messages, not the oldest history. Users can
|
|
move upward for older context and use `G` or End to return to live chat.
|
|
- NORMAL mode accepts `/` as the fast path for history search, matching a
|
|
common terminal-reader habit while reusing the existing `:search` command.
|
|
- INSERT mode keeps a small per-session sent-message history on Up/Down and
|
|
completes trailing `@mention` prefixes with Tab.
|
|
- `:help` is a compact manual, while `?` is a full key reference. Do not add
|
|
parallel support commands for the same task.
|
|
- Command syntax stays ASCII even in localized UI text. Translations explain;
|
|
they do not change the command language.
|
|
- Private messages are visible in each participant's in-memory `:inbox`:
|
|
recipients see incoming messages, senders see local sent-message copies,
|
|
newest first. They are not written to `messages.log` and do not survive a
|
|
reconnect.
|
|
- `:inbox` is live enough for normal chat use: it can be refreshed with `r`
|
|
and refreshes automatically when a new private message arrives while the
|
|
inbox is open. Incoming unread messages are marked with `*` and counted in
|
|
the inbox title until the inbox renders them. `:inbox clear` removes private
|
|
messages and the reply target for the current session.
|
|
- `:reply` / `:r` keeps the private-message path keyboard-short: it answers
|
|
the latest private-message peer in the current session without retyping a
|
|
username.
|
|
- Long command output uses a small pager so `:last` and `:search` are readable
|
|
on small terminals.
|
|
|
|
## Regression Coverage
|
|
|
|
`make user-lifecycle-test` runs a two-user SSH TUI journey:
|
|
|
|
- second user joins and is visible through `users --json`
|
|
- first user opens `?`, checks `:users`, sends a public message, scrolls, uses
|
|
`:last` and `:search`
|
|
- first user toggles `:mute-joins`, sends two `:msg` messages, receives a
|
|
`:reply`, confirms private-message copies in `:inbox`, clears the inbox,
|
|
changes nickname, sends `/me`, and exits
|
|
- second user opens `:inbox` before the private messages arrive, sees it
|
|
auto-refresh after delivery, newest first, and replies without retyping the
|
|
sender's username
|
|
- exec `tail` sees public messages
|
|
- `messages.log` contains public history and excludes private-message content
|
|
|
|
This test is intentionally closer to a user story than a unit regression. Keep
|
|
it focused on lifecycle guarantees, not every keybinding.
|