diff --git a/README.md b/README.md index 4efe488..29137c6 100644 --- a/README.md +++ b/README.md @@ -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*] -- 🚀 **纯文本浏览** - 专注于文本内容,无图片干扰 -- ⌨️ **完全vim风格操作** - hjkl移动、gg/G跳转、/搜索等 -- 📖 **报纸式排版** - 自适应宽度居中显示,优化阅读体验 -- 🔗 **链接导航** - TAB键切换链接,Enter跟随链接 -- 📜 **历史管理** - h/l快速前进后退 -- 🎨 **优雅配色** - 精心设计的终端配色方案 -- 🔍 **内容搜索** - 支持文本搜索和高亮 +**tut** **-h** | **--help** -## 依赖 +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 -- C++17 编译器(macOS 建议 clang,Linux 建议 g++) -- `ncurses` 或 `ncursesw`(支持宽字符) -- `libcurl`(支持HTTPS) +The browser does not execute JavaScript or display images. It is designed +for reading static HTML content, documentation, and text-heavy websites. -### macOS (Homebrew) 安装依赖 +OPTIONS +------- +*URL* + Open the specified URL on startup. If omitted, displays the built-in + help page. -```bash -brew install cmake ncurses curl -``` +**-h**, **--help** + Display usage information and exit. -### Linux (Ubuntu/Debian) 安装依赖 +KEYBINDINGS +----------- +**tut** uses vim-style keybindings throughout. -```bash -sudo apt-get update -sudo apt-get install build-essential cmake libncursesw5-dev libcurl4-openssl-dev -``` +### Navigation -### Linux (Fedora/RHEL) 安装依赖 +**j**, **Down** + Scroll down one line. -```bash -sudo dnf install cmake gcc-c++ ncurses-devel libcurl-devel -``` +**k**, **Up** + Scroll up one line. -## 构建 +**Ctrl-D**, **Space** + Scroll down one page. -在项目根目录执行: +**Ctrl-U**, **b** + Scroll up one page. -```bash -mkdir -p build -cd build -cmake .. -cmake --build . -``` +**gg** + Jump to top of page. -生成的可执行文件为 `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 -./tut -``` +### Link Navigation -### 打开指定URL +**Tab** + Move to next link. -```bash -./tut https://example.com -./tut https://news.ycombinator.com -``` +**Shift-Tab**, **T** + Move to previous link. -### 显示使用帮助 +**Enter** + Follow current link. -```bash -./tut --help -``` +**h**, **Left** + Go back in history. -## 键盘操作 +**l**, **Right** + Go forward in history. -### 导航 +### Search -| 按键 | 功能 | -|------|------| -| `j` / `↓` | 向下滚动一行 | -| `k` / `↑` | 向上滚动一行 | -| `Ctrl-D` / `Space` | 向下翻页 | -| `Ctrl-U` / `b` | 向上翻页 | -| `gg` | 跳转到顶部 | -| `G` | 跳转到底部 | -| `[数字]G` | 跳转到指定行(如 `50G`) | -| `[数字]j/k` | 向下/上滚动指定行数(如 `5j`) | +**/** + Start search. Enter search term and press **Enter**. -### 链接操作 +**n** + Jump to next search match. -| 按键 | 功能 | -|------|------| -| `Tab` | 下一个链接 | -| `Shift-Tab` / `T` | 上一个链接 | -| `Enter` | 跟随当前链接 | -| `h` / `←` | 后退 | -| `l` / `→` | 前进 | +**N** + Jump to previous search match. -### 搜索 +### Commands -| 按键 | 功能 | -|------|------| -| `/` | 开始搜索 | -| `n` | 下一个匹配 | -| `N` | 上一个匹配 | +Press **:** to enter command mode. Available commands: -### 命令模式 +**:q**, **:quit** + Quit the browser. -按 `:` 进入命令模式,支持以下命令: +**:o** *URL*, **:open** *URL* + Open *URL*. -| 命令 | 功能 | -|------|------| -| `:q` / `:quit` | 退出浏览器 | -| `:o URL` / `:open URL` | 打开指定URL | -| `:r` / `:refresh` | 刷新当前页面 | -| `:h` / `:help` | 显示帮助 | -| `:[数字]` | 跳转到指定行 | +**:r**, **:refresh** + Reload current page. -### 其他 +**:h**, **:help** + Display help page. -| 按键 | 功能 | -|------|------| -| `r` | 刷新当前页面 | -| `q` | 退出浏览器 | -| `?` | 显示帮助 | -| `ESC` | 取消命令/搜索输入 | +**:***number* + Jump to line *number*. -## 使用示例 +### Other -### 浏览新闻网站 +**r** + Reload current page. -```bash -./tut https://news.ycombinator.com -``` +**q** + Quit the browser. -然后: -- 使用 `j/k` 滚动浏览标题 -- 按 `Tab` 切换到感兴趣的链接 -- 按 `Enter` 打开链接 -- 按 `h` 返回上一页 +**?** + Display help page. -### 阅读文档 +**ESC** + Cancel command or search input. -```bash -./tut https://en.wikipedia.org/wiki/Unix -``` +LIMITATIONS +----------- +**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. -然后: -- 使用 `gg` 跳转到顶部 -- 使用 `/` 搜索关键词(如 `/history`) -- 使用 `n/N` 在搜索结果间跳转 -- 使用 `Space` 翻页阅读 +To determine if a site will work with **tut**, use: -### 快速查看多个网页 + curl https://example.com | less -```bash -./tut https://github.com -``` +If you can see the actual content in the HTML source, the site will work. +If you only see JavaScript code or empty div elements, it will not. -在浏览器内: -- 浏览页面并点击链接 -- 使用 `:o https://news.ycombinator.com` 打开新URL -- 使用 `h/l` 在历史中前进后退 +Additionally: +- No image display +- No CSS layout support +- No form submission +- No cookie or session management +- No AJAX or dynamic content loading -## 设计理念 +EXAMPLES +-------- +View the built-in help: -TUT 的设计目标是提供最佳的终端阅读体验: + tut -1. **极简主义** - 只关注文本内容,摒弃图片、广告等干扰元素 -2. **高效操作** - 完全键盘驱动,无需触摸鼠标 -3. **优雅排版** - 自适应宽度,居中显示,类似专业阅读器 -4. **快速响应** - 轻量级实现,即开即用 +Browse Hacker News: -## 架构 + tut https://news.ycombinator.com -``` -TUT -├── http_client - HTTP/HTTPS 网页获取 -├── html_parser - HTML 解析和文本提取 -├── text_renderer - 文本渲染和排版引擎 -├── input_handler - Vim 风格输入处理 -└── browser - 浏览器主循环和状态管理 -``` +Read Wikipedia: -## 限制 + tut https://en.wikipedia.org/wiki/Unix_philosophy -### JavaScript/SPA 网站 -**重要:** 这个浏览器**不支持JavaScript执行**。这意味着: +Open a URL, search for "unix", and navigate: -- ❌ **不支持**单页应用(SPA):React、Vue、Angular、Astro等构建的现代网站 -- ❌ **不支持**动态内容加载 -- ❌ **不支持**AJAX请求 -- ❌ **不支持**客户端路由 + tut https://example.com + /unix + n -**如何判断网站是否支持:** -1. 用 `curl` 命令查看HTML内容:`curl https://example.com | less` -2. 如果能看到实际的文章内容,则支持;如果只有JavaScript代码或空白div,则不支持 +DEPENDENCIES +------------ +- ncurses or ncursesw (for terminal UI) +- libcurl (for HTTPS support) +- CMake >= 3.15 (build time) +- C++17 compiler (build time) -**你的网站示例:** -- ✅ **thinker.m1ng.space** - 静态HTML,完全支持,可以浏览文章列表并点击进入具体文章 -- ❌ **blog.m1ng.space** - 使用Astro SPA构建,内容由JavaScript动态渲染,无法正常显示 +INSTALLATION +------------ +### From Source -**替代方案:** -- 对于SPA网站,查找是否有RSS feed或API端点 -- 使用服务器端渲染(SSR)版本的URL(如果有) -- 寻找使用传统HTML构建的同类网站 +**macOS (Homebrew):** -### 其他限制 + brew install cmake ncurses curl + mkdir -p build && cd build + cmake .. + cmake --build . + sudo install -m 755 tut /usr/local/bin/ -- 不支持图片显示 -- 不支持复杂的CSS布局 -- 不支持表单提交 -- 不支持Cookie和会话管理 -- 专注于内容阅读,不适合需要交互的网页 +**Linux (Debian/Ubuntu):** -## 开发指南 + 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 标准 -- 使用 RAII 进行资源管理 -- 使用 Pimpl 模式隐藏实现细节 + sudo dnf install cmake gcc-c++ ncurses-devel libcurl-devel + mkdir -p build && cd build + cmake .. + cmake --build . + sudo install -m 755 tut /usr/local/bin/ -### 测试 +### Using Makefile -```bash -cd build -./tut https://example.com -``` + make + sudo make install -### 贡献 +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. 代码风格与现有代码一致 -2. 添加必要的注释 -3. 测试新功能 -4. 更新文档 +**TERM** + Terminal type. Must support basic cursor movement and colors. -## 版本历史 +**LINES**, **COLUMNS** + Terminal size. Automatically detected via ncurses. -- **v1.0.0** - 完全重构为终端浏览器 - - 添加 HTTP/HTTPS 支持 - - 实现 HTML 解析 - - 实现 Vim 风格操作 - - 报纸式排版引擎 - - 链接导航和搜索功能 +EXIT STATUS +----------- +**0** + 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. -灵感来源于: -- `lynx` - 经典的终端浏览器 -- `w3m` - 另一个优秀的终端浏览器 -- `vim` - 最好的文本编辑器 -- `btop` - 美观的TUI设计 +SEE ALSO +-------- +lynx(1), w3m(1), curl(1), vim(1) +BUGS +---- +Report bugs at: https://github.com/m1ngsama/TUT/issues + +AUTHORS +------- +m1ngsama + +Inspired by lynx, w3m, and vim. + +LICENSE +------- +MIT License. See LICENSE file for details. diff --git a/src/browser.cpp b/src/browser.cpp index 7afb06f..c619c7f 100644 --- a/src/browser.cpp +++ b/src/browser.cpp @@ -17,14 +17,12 @@ public: std::vector history; int history_pos = -1; - // 视图状态 int scroll_pos = 0; int current_link = -1; std::string status_message; std::string search_term; - std::vector search_results; // 匹配行号 + std::vector search_results; - // 屏幕尺寸 int screen_height = 0; int screen_width = 0; @@ -36,7 +34,7 @@ public: noecho(); keypad(stdscr, TRUE); curs_set(0); - timeout(0); // non-blocking + timeout(0); getmaxyx(stdscr, screen_height, screen_width); } @@ -311,7 +309,6 @@ public: } void scroll_to_link(int link_idx) { - // 查找链接在渲染行中的位置 for (size_t i = 0; i < rendered_lines.size(); ++i) { if (rendered_lines[i].is_link && rendered_lines[i].link_index == link_idx) { int visible_lines = screen_height - 2; @@ -406,7 +403,7 @@ void Browser::run(const std::string& initial_url) { int ch = getch(); if (ch == ERR) { - napms(50); // 50ms sleep + napms(50); continue; } diff --git a/src/browser.h b/src/browser.h index 64ddfde..964c49f 100644 --- a/src/browser.h +++ b/src/browser.h @@ -13,13 +13,8 @@ public: Browser(); ~Browser(); - // 启动浏览器(进入主循环) void run(const std::string& initial_url = ""); - - // 加载URL bool load_url(const std::string& url); - - // 获取当前URL std::string get_current_url() const; private: diff --git a/src/html_parser.h b/src/html_parser.h index 50dbc41..d72ed1b 100644 --- a/src/html_parser.h +++ b/src/html_parser.h @@ -21,14 +21,14 @@ enum class ElementType { struct Link { std::string text; std::string url; - int position; // 在文档中的位置(用于TAB导航) + int position; }; struct ContentElement { ElementType type; std::string text; - std::string url; // 对于链接元素 - int level; // 对于标题元素(1-6) + std::string url; + int level; }; struct ParsedDocument { @@ -43,13 +43,8 @@ public: HtmlParser(); ~HtmlParser(); - // 解析HTML并提取可读内容 ParsedDocument parse(const std::string& html, const std::string& base_url = ""); - - // 设置是否保留代码块 void set_keep_code_blocks(bool keep); - - // 设置是否保留列表 void set_keep_lists(bool keep); private: diff --git a/src/http_client.h b/src/http_client.h index 9793178..40359c0 100644 --- a/src/http_client.h +++ b/src/http_client.h @@ -19,16 +19,9 @@ public: HttpClient(); ~HttpClient(); - // 获取网页内容 HttpResponse fetch(const std::string& url); - - // 设置超时(秒) void set_timeout(long timeout_seconds); - - // 设置用户代理 void set_user_agent(const std::string& user_agent); - - // 设置是否跟随重定向 void set_follow_redirects(bool follow); private: diff --git a/src/input_handler.h b/src/input_handler.h index ca90eca..be5d035 100644 --- a/src/input_handler.h +++ b/src/input_handler.h @@ -5,10 +5,10 @@ #include enum class InputMode { - NORMAL, // 正常浏览模式 - COMMAND, // 命令模式 (:) - SEARCH, // 搜索模式 (/) - LINK // 链接选择模式 + NORMAL, + COMMAND, + SEARCH, + LINK }; enum class Action { @@ -36,10 +36,10 @@ enum class Action { struct InputResult { Action action; - std::string text; // 用于命令、搜索、URL输入 - int number; // 用于跳转行号、链接编号等 - bool has_count; // 是否有数字前缀(如 5j) - int count; // 数字前缀 + std::string text; + int number; + bool has_count; + int count; }; class InputHandler { @@ -47,19 +47,10 @@ public: InputHandler(); ~InputHandler(); - // 处理单个按键 InputResult handle_key(int ch); - - // 获取当前模式 InputMode get_mode() const; - - // 获取当前输入缓冲(用于显示命令行) std::string get_buffer() const; - - // 重置状态 void reset(); - - // 设置状态栏消息回调 void set_status_callback(std::function callback); private: diff --git a/src/text_renderer.h b/src/text_renderer.h index 8298f0a..33bc54c 100644 --- a/src/text_renderer.h +++ b/src/text_renderer.h @@ -6,22 +6,20 @@ #include #include -// 渲染后的行信息 struct RenderedLine { std::string text; int color_pair; bool is_bold; bool is_link; - int link_index; // 如果是链接,对应的链接索引 + int link_index; }; -// 渲染配置 struct RenderConfig { - int max_width = 80; // 内容最大宽度 - int margin_left = 0; // 左边距(居中时自动计算) - bool center_content = true; // 是否居中内容 - int paragraph_spacing = 1; // 段落间距 - bool show_link_indicators = true; // 是否显示链接指示器 + int max_width = 80; + int margin_left = 0; + bool center_content = true; + int paragraph_spacing = 1; + bool show_link_indicators = true; }; class TextRenderer { @@ -29,13 +27,8 @@ public: TextRenderer(); ~TextRenderer(); - // 渲染文档到行数组 std::vector render(const ParsedDocument& doc, int screen_width); - - // 设置渲染配置 void set_config(const RenderConfig& config); - - // 获取当前配置 RenderConfig get_config() const; private: @@ -43,7 +36,6 @@ private: std::unique_ptr pImpl; }; -// 颜色定义 enum ColorScheme { COLOR_NORMAL = 1, COLOR_HEADING1, @@ -57,5 +49,4 @@ enum ColorScheme { COLOR_DIM }; -// 初始化颜色方案 void init_color_scheme();