docs: Rewrite README as Unix man page, remove Chinese comments

Following Unix philosophy and documentation standards:
- Rewrite README.md in man page format (NAME, SYNOPSIS, DESCRIPTION, etc.)
- Remove all Chinese comments from source code
- Keep code clean and self-documenting
- Add PHILOSOPHY section explaining Unix principles
- Include proper EXIT STATUS, ENVIRONMENT, and FILES sections
- Reference related tools in SEE ALSO section
This commit is contained in:
m1ngsama 2025-12-08 16:11:39 +08:00
parent 818f5ddc5e
commit 354133b500
7 changed files with 203 additions and 248 deletions

373
README.md
View file

@ -1,263 +1,256 @@
# TUT - Terminal User Interface Browser TUT(1) - Terminal User Interface Browser
========================================
一个专注于阅读体验的终端网页浏览器采用vim风格的键盘操作让你在终端中舒适地浏览网页文本内容。 NAME
----
tut - vim-style terminal web browser
## 特性 SYNOPSIS
--------
**tut** [*URL*]
- 🚀 **纯文本浏览** - 专注于文本内容,无图片干扰 **tut** **-h** | **--help**
- ⌨️ **完全vim风格操作** - hjkl移动、gg/G跳转、/搜索等
- 📖 **报纸式排版** - 自适应宽度居中显示,优化阅读体验
- 🔗 **链接导航** - TAB键切换链接Enter跟随链接
- 📜 **历史管理** - h/l快速前进后退
- 🎨 **优雅配色** - 精心设计的终端配色方案
- 🔍 **内容搜索** - 支持文本搜索和高亮
## 依赖 DESCRIPTION
-----------
**tut** is a text-mode web browser designed for comfortable reading in the
terminal. It extracts and displays the textual content of web pages with a
clean, centered layout optimized for reading, while providing vim-style
keyboard navigation.
- CMake ≥ 3.15 The browser does not execute JavaScript or display images. It is designed
- C++17 编译器macOS 建议 clangLinux 建议 g++ for reading static HTML content, documentation, and text-heavy websites.
- `ncurses``ncursesw`(支持宽字符)
- `libcurl`支持HTTPS
### macOS (Homebrew) 安装依赖 OPTIONS
-------
*URL*
Open the specified URL on startup. If omitted, displays the built-in
help page.
```bash **-h**, **--help**
brew install cmake ncurses curl Display usage information and exit.
```
### Linux (Ubuntu/Debian) 安装依赖 KEYBINDINGS
-----------
**tut** uses vim-style keybindings throughout.
```bash ### Navigation
sudo apt-get update
sudo apt-get install build-essential cmake libncursesw5-dev libcurl4-openssl-dev
```
### Linux (Fedora/RHEL) 安装依赖 **j**, **Down**
Scroll down one line.
```bash **k**, **Up**
sudo dnf install cmake gcc-c++ ncurses-devel libcurl-devel Scroll up one line.
```
## 构建 **Ctrl-D**, **Space**
Scroll down one page.
在项目根目录执行: **Ctrl-U**, **b**
Scroll up one page.
```bash **gg**
mkdir -p build Jump to top of page.
cd build
cmake ..
cmake --build .
```
生成的可执行文件为 `tut` **G**
Jump to bottom of page.
## 运行 **[***count***]G**
Jump to line *count* (e.g., **50G** jumps to line 50).
### 直接启动(显示帮助页面) **[***count***]j**, **[***count***]k**
Scroll down/up *count* lines (e.g., **5j** scrolls down 5 lines).
```bash ### Link Navigation
./tut
```
### 打开指定URL **Tab**
Move to next link.
```bash **Shift-Tab**, **T**
./tut https://example.com Move to previous link.
./tut https://news.ycombinator.com
```
### 显示使用帮助 **Enter**
Follow current link.
```bash **h**, **Left**
./tut --help Go back in history.
```
## 键盘操作 **l**, **Right**
Go forward in history.
### 导航 ### Search
| 按键 | 功能 | **/**
|------|------| Start search. Enter search term and press **Enter**.
| `j` / `↓` | 向下滚动一行 |
| `k` / `↑` | 向上滚动一行 |
| `Ctrl-D` / `Space` | 向下翻页 |
| `Ctrl-U` / `b` | 向上翻页 |
| `gg` | 跳转到顶部 |
| `G` | 跳转到底部 |
| `[数字]G` | 跳转到指定行(如 `50G` |
| `[数字]j/k` | 向下/上滚动指定行数(如 `5j` |
### 链接操作 **n**
Jump to next search match.
| 按键 | 功能 | **N**
|------|------| Jump to previous search match.
| `Tab` | 下一个链接 |
| `Shift-Tab` / `T` | 上一个链接 |
| `Enter` | 跟随当前链接 |
| `h` / `←` | 后退 |
| `l` / `→` | 前进 |
### 搜索 ### Commands
| 按键 | 功能 | Press **:** to enter command mode. Available commands:
|------|------|
| `/` | 开始搜索 |
| `n` | 下一个匹配 |
| `N` | 上一个匹配 |
### 命令模式 **:q**, **:quit**
Quit the browser.
`:` 进入命令模式,支持以下命令: **:o** *URL*, **:open** *URL*
Open *URL*.
| 命令 | 功能 | **:r**, **:refresh**
|------|------| Reload current page.
| `:q` / `:quit` | 退出浏览器 |
| `:o URL` / `:open URL` | 打开指定URL |
| `:r` / `:refresh` | 刷新当前页面 |
| `:h` / `:help` | 显示帮助 |
| `:[数字]` | 跳转到指定行 |
### 其他 **:h**, **:help**
Display help page.
| 按键 | 功能 | **:***number*
|------|------| Jump to line *number*.
| `r` | 刷新当前页面 |
| `q` | 退出浏览器 |
| `?` | 显示帮助 |
| `ESC` | 取消命令/搜索输入 |
## 使用示例 ### Other
### 浏览新闻网站 **r**
Reload current page.
```bash **q**
./tut https://news.ycombinator.com Quit the browser.
```
然后: **?**
- 使用 `j/k` 滚动浏览标题 Display help page.
- 按 `Tab` 切换到感兴趣的链接
- 按 `Enter` 打开链接
- 按 `h` 返回上一页
### 阅读文档 **ESC**
Cancel command or search input.
```bash LIMITATIONS
./tut https://en.wikipedia.org/wiki/Unix -----------
``` **tut** does not execute JavaScript. Modern single-page applications (SPAs)
built with React, Vue, Angular, or similar frameworks will not work correctly,
as they require JavaScript to render content.
然后: To determine if a site will work with **tut**, use:
- 使用 `gg` 跳转到顶部
- 使用 `/` 搜索关键词(如 `/history`
- 使用 `n/N` 在搜索结果间跳转
- 使用 `Space` 翻页阅读
### 快速查看多个网页 curl https://example.com | less
```bash If you can see the actual content in the HTML source, the site will work.
./tut https://github.com If you only see JavaScript code or empty div elements, it will not.
```
在浏览器内: Additionally:
- 浏览页面并点击链接 - No image display
- 使用 `:o https://news.ycombinator.com` 打开新URL - No CSS layout support
- 使用 `h/l` 在历史中前进后退 - No form submission
- No cookie or session management
- No AJAX or dynamic content loading
## 设计理念 EXAMPLES
--------
View the built-in help:
TUT 的设计目标是提供最佳的终端阅读体验: tut
1. **极简主义** - 只关注文本内容,摒弃图片、广告等干扰元素 Browse Hacker News:
2. **高效操作** - 完全键盘驱动,无需触摸鼠标
3. **优雅排版** - 自适应宽度,居中显示,类似专业阅读器
4. **快速响应** - 轻量级实现,即开即用
## 架构 tut https://news.ycombinator.com
``` Read Wikipedia:
TUT
├── http_client - HTTP/HTTPS 网页获取
├── html_parser - HTML 解析和文本提取
├── text_renderer - 文本渲染和排版引擎
├── input_handler - Vim 风格输入处理
└── browser - 浏览器主循环和状态管理
```
## 限制 tut https://en.wikipedia.org/wiki/Unix_philosophy
### JavaScript/SPA 网站 Open a URL, search for "unix", and navigate:
**重要:** 这个浏览器**不支持JavaScript执行**。这意味着:
- ❌ **不支持**单页应用(SPA)React、Vue、Angular、Astro等构建的现代网站 tut https://example.com
- ❌ **不支持**动态内容加载 /unix<Enter>
- ❌ **不支持**AJAX请求 n
- ❌ **不支持**客户端路由
**如何判断网站是否支持:** DEPENDENCIES
1. 用 `curl` 命令查看HTML内容`curl https://example.com | less` ------------
2. 如果能看到实际的文章内容则支持如果只有JavaScript代码或空白div则不支持 - ncurses or ncursesw (for terminal UI)
- libcurl (for HTTPS support)
- CMake >= 3.15 (build time)
- C++17 compiler (build time)
**你的网站示例:** INSTALLATION
- ✅ **thinker.m1ng.space** - 静态HTML完全支持可以浏览文章列表并点击进入具体文章 ------------
- ❌ **blog.m1ng.space** - 使用Astro SPA构建内容由JavaScript动态渲染无法正常显示 ### From Source
**替代方案:** **macOS (Homebrew):**
- 对于SPA网站查找是否有RSS feed或API端点
- 使用服务器端渲染(SSR)版本的URL如果有
- 寻找使用传统HTML构建的同类网站
### 其他限制 brew install cmake ncurses curl
mkdir -p build && cd build
cmake ..
cmake --build .
sudo install -m 755 tut /usr/local/bin/
- 不支持图片显示 **Linux (Debian/Ubuntu):**
- 不支持复杂的CSS布局
- 不支持表单提交
- 不支持Cookie和会话管理
- 专注于内容阅读,不适合需要交互的网页
## 开发指南 sudo apt-get install cmake libncursesw5-dev libcurl4-openssl-dev
mkdir -p build && cd build
cmake ..
cmake --build .
sudo install -m 755 tut /usr/local/bin/
### 代码风格 **Linux (Fedora/RHEL):**
- 遵循 C++17 标准 sudo dnf install cmake gcc-c++ ncurses-devel libcurl-devel
- 使用 RAII 进行资源管理 mkdir -p build && cd build
- 使用 Pimpl 模式隐藏实现细节 cmake ..
cmake --build .
sudo install -m 755 tut /usr/local/bin/
### 测试 ### Using Makefile
```bash make
cd build sudo make install
./tut https://example.com
```
### 贡献 FILES
-----
No configuration files are used. The browser is stateless and does not
store history, cookies, or cache.
欢迎提交 Pull Request请确保 ENVIRONMENT
-----------
**tut** respects the following environment variables:
1. 代码风格与现有代码一致 **TERM**
2. 添加必要的注释 Terminal type. Must support basic cursor movement and colors.
3. 测试新功能
4. 更新文档
## 版本历史 **LINES**, **COLUMNS**
Terminal size. Automatically detected via ncurses.
- **v1.0.0** - 完全重构为终端浏览器 EXIT STATUS
- 添加 HTTP/HTTPS 支持 -----------
- 实现 HTML 解析 **0**
- 实现 Vim 风格操作 Success.
- 报纸式排版引擎
- 链接导航和搜索功能
- **v0.0.1** - 初始版本ICS 日历查看器) **1**
Error occurred (e.g., invalid URL, network error, ncurses initialization
failure).
## 许可证 PHILOSOPHY
----------
**tut** follows the Unix philosophy:
MIT License 1. Do one thing well: display and navigate text content from the web.
2. Work with other programs: output can be piped, URLs can come from stdin.
3. Simple and minimal: no configuration files, no persistent state.
4. Text-focused: everything is text, processed and displayed cleanly.
## 致谢 The design emphasizes keyboard efficiency, clean output, and staying out
of your way.
灵感来源于: SEE ALSO
- `lynx` - 经典的终端浏览器 --------
- `w3m` - 另一个优秀的终端浏览器 lynx(1), w3m(1), curl(1), vim(1)
- `vim` - 最好的文本编辑器
- `btop` - 美观的TUI设计
BUGS
----
Report bugs at: https://github.com/m1ngsama/TUT/issues
AUTHORS
-------
m1ngsama <contact@m1ng.space>
Inspired by lynx, w3m, and vim.
LICENSE
-------
MIT License. See LICENSE file for details.

View file

@ -17,14 +17,12 @@ public:
std::vector<std::string> history; std::vector<std::string> history;
int history_pos = -1; int history_pos = -1;
// 视图状态
int scroll_pos = 0; int scroll_pos = 0;
int current_link = -1; int current_link = -1;
std::string status_message; std::string status_message;
std::string search_term; std::string search_term;
std::vector<int> search_results; // 匹配行号 std::vector<int> search_results;
// 屏幕尺寸
int screen_height = 0; int screen_height = 0;
int screen_width = 0; int screen_width = 0;
@ -36,7 +34,7 @@ public:
noecho(); noecho();
keypad(stdscr, TRUE); keypad(stdscr, TRUE);
curs_set(0); curs_set(0);
timeout(0); // non-blocking timeout(0);
getmaxyx(stdscr, screen_height, screen_width); getmaxyx(stdscr, screen_height, screen_width);
} }
@ -311,7 +309,6 @@ public:
} }
void scroll_to_link(int link_idx) { void scroll_to_link(int link_idx) {
// 查找链接在渲染行中的位置
for (size_t i = 0; i < rendered_lines.size(); ++i) { for (size_t i = 0; i < rendered_lines.size(); ++i) {
if (rendered_lines[i].is_link && rendered_lines[i].link_index == link_idx) { if (rendered_lines[i].is_link && rendered_lines[i].link_index == link_idx) {
int visible_lines = screen_height - 2; int visible_lines = screen_height - 2;
@ -406,7 +403,7 @@ void Browser::run(const std::string& initial_url) {
int ch = getch(); int ch = getch();
if (ch == ERR) { if (ch == ERR) {
napms(50); // 50ms sleep napms(50);
continue; continue;
} }

View file

@ -13,13 +13,8 @@ public:
Browser(); Browser();
~Browser(); ~Browser();
// 启动浏览器(进入主循环)
void run(const std::string& initial_url = ""); void run(const std::string& initial_url = "");
// 加载URL
bool load_url(const std::string& url); bool load_url(const std::string& url);
// 获取当前URL
std::string get_current_url() const; std::string get_current_url() const;
private: private:

View file

@ -21,14 +21,14 @@ enum class ElementType {
struct Link { struct Link {
std::string text; std::string text;
std::string url; std::string url;
int position; // 在文档中的位置用于TAB导航 int position;
}; };
struct ContentElement { struct ContentElement {
ElementType type; ElementType type;
std::string text; std::string text;
std::string url; // 对于链接元素 std::string url;
int level; // 对于标题元素1-6 int level;
}; };
struct ParsedDocument { struct ParsedDocument {
@ -43,13 +43,8 @@ public:
HtmlParser(); HtmlParser();
~HtmlParser(); ~HtmlParser();
// 解析HTML并提取可读内容
ParsedDocument parse(const std::string& html, const std::string& base_url = ""); ParsedDocument parse(const std::string& html, const std::string& base_url = "");
// 设置是否保留代码块
void set_keep_code_blocks(bool keep); void set_keep_code_blocks(bool keep);
// 设置是否保留列表
void set_keep_lists(bool keep); void set_keep_lists(bool keep);
private: private:

View file

@ -19,16 +19,9 @@ public:
HttpClient(); HttpClient();
~HttpClient(); ~HttpClient();
// 获取网页内容
HttpResponse fetch(const std::string& url); HttpResponse fetch(const std::string& url);
// 设置超时(秒)
void set_timeout(long timeout_seconds); void set_timeout(long timeout_seconds);
// 设置用户代理
void set_user_agent(const std::string& user_agent); void set_user_agent(const std::string& user_agent);
// 设置是否跟随重定向
void set_follow_redirects(bool follow); void set_follow_redirects(bool follow);
private: private:

View file

@ -5,10 +5,10 @@
#include <memory> #include <memory>
enum class InputMode { enum class InputMode {
NORMAL, // 正常浏览模式 NORMAL,
COMMAND, // 命令模式 (:) COMMAND,
SEARCH, // 搜索模式 (/) SEARCH,
LINK // 链接选择模式 LINK
}; };
enum class Action { enum class Action {
@ -36,10 +36,10 @@ enum class Action {
struct InputResult { struct InputResult {
Action action; Action action;
std::string text; // 用于命令、搜索、URL输入 std::string text;
int number; // 用于跳转行号、链接编号等 int number;
bool has_count; // 是否有数字前缀(如 5j bool has_count;
int count; // 数字前缀 int count;
}; };
class InputHandler { class InputHandler {
@ -47,19 +47,10 @@ public:
InputHandler(); InputHandler();
~InputHandler(); ~InputHandler();
// 处理单个按键
InputResult handle_key(int ch); InputResult handle_key(int ch);
// 获取当前模式
InputMode get_mode() const; InputMode get_mode() const;
// 获取当前输入缓冲(用于显示命令行)
std::string get_buffer() const; std::string get_buffer() const;
// 重置状态
void reset(); void reset();
// 设置状态栏消息回调
void set_status_callback(std::function<void(const std::string&)> callback); void set_status_callback(std::function<void(const std::string&)> callback);
private: private:

View file

@ -6,22 +6,20 @@
#include <memory> #include <memory>
#include <curses.h> #include <curses.h>
// 渲染后的行信息
struct RenderedLine { struct RenderedLine {
std::string text; std::string text;
int color_pair; int color_pair;
bool is_bold; bool is_bold;
bool is_link; bool is_link;
int link_index; // 如果是链接,对应的链接索引 int link_index;
}; };
// 渲染配置
struct RenderConfig { struct RenderConfig {
int max_width = 80; // 内容最大宽度 int max_width = 80;
int margin_left = 0; // 左边距(居中时自动计算) int margin_left = 0;
bool center_content = true; // 是否居中内容 bool center_content = true;
int paragraph_spacing = 1; // 段落间距 int paragraph_spacing = 1;
bool show_link_indicators = true; // 是否显示链接指示器 bool show_link_indicators = true;
}; };
class TextRenderer { class TextRenderer {
@ -29,13 +27,8 @@ public:
TextRenderer(); TextRenderer();
~TextRenderer(); ~TextRenderer();
// 渲染文档到行数组
std::vector<RenderedLine> render(const ParsedDocument& doc, int screen_width); std::vector<RenderedLine> render(const ParsedDocument& doc, int screen_width);
// 设置渲染配置
void set_config(const RenderConfig& config); void set_config(const RenderConfig& config);
// 获取当前配置
RenderConfig get_config() const; RenderConfig get_config() const;
private: private:
@ -43,7 +36,6 @@ private:
std::unique_ptr<Impl> pImpl; std::unique_ptr<Impl> pImpl;
}; };
// 颜色定义
enum ColorScheme { enum ColorScheme {
COLOR_NORMAL = 1, COLOR_NORMAL = 1,
COLOR_HEADING1, COLOR_HEADING1,
@ -57,5 +49,4 @@ enum ColorScheme {
COLOR_DIM COLOR_DIM
}; };
// 初始化颜色方案
void init_color_scheme(); void init_color_scheme();