refactor: Improve code quality and Unix philosophy

- Remove redundant comments for cleaner code
- Simplify error messages and status display
- Improve code consistency across modules
- Fix GitHub Actions workflow binary names
- Enhance .gitignore for common editor files
- Align help text formatting
- Remove unnecessary verbose comments
This commit is contained in:
m1ngsama 2025-12-08 15:53:17 +08:00
parent ab2d1932e4
commit ef80f9ab82
6 changed files with 36 additions and 81 deletions

View file

@ -47,13 +47,13 @@ jobs:
- name: Rename binary with platform suffix - name: Rename binary with platform suffix
run: | run: |
mv build/nbtca_tui build/nbtca_tui-${{ matrix.name }} mv build/tut build/tut-${{ matrix.name }}
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: nbtca_tui-${{ matrix.name }} name: tut-${{ matrix.name }}
path: build/nbtca_tui-${{ matrix.name }} path: build/tut-${{ matrix.name }}
release: release:
needs: build needs: build
@ -85,13 +85,13 @@ jobs:
Automated release for commit ${{ github.sha }} Automated release for commit ${{ github.sha }}
## Download ## Download
- **macOS**: `nbtca_tui-macos` - **macOS**: `tut-macos`
- **Linux**: `nbtca_tui-linux` - **Linux**: `tut-linux`
## Build from source ## Build from source
See the [README](https://github.com/${{ github.repository }}/blob/main/README.md) for build instructions. See the [README](https://github.com/${{ github.repository }}/blob/main/README.md) for build instructions.
files: | files: |
artifacts/nbtca_tui-macos/nbtca_tui-macos artifacts/tut-macos/tut-macos
artifacts/nbtca_tui-linux/nbtca_tui-linux artifacts/tut-linux/tut-linux
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

5
.gitignore vendored
View file

@ -2,3 +2,8 @@ build/
*.o *.o
tut tut
.DS_Store .DS_Store
*.swp
*.swo
*~
.vscode/
.idea/

View file

@ -52,9 +52,9 @@ public:
auto response = http_client.fetch(url); auto response = http_client.fetch(url);
if (!response.is_success()) { if (!response.is_success()) {
status_message = "Error: " + (response.error_message.empty() ? status_message = response.error_message.empty() ?
"HTTP " + std::to_string(response.status_code) : "HTTP " + std::to_string(response.status_code) :
response.error_message); response.error_message;
return false; return false;
} }
@ -65,14 +65,13 @@ public:
current_link = -1; current_link = -1;
search_results.clear(); search_results.clear();
// 更新历史
if (history_pos >= 0 && history_pos < static_cast<int>(history.size()) - 1) { if (history_pos >= 0 && history_pos < static_cast<int>(history.size()) - 1) {
history.erase(history.begin() + history_pos + 1, history.end()); history.erase(history.begin() + history_pos + 1, history.end());
} }
history.push_back(url); history.push_back(url);
history_pos = history.size() - 1; history_pos = history.size() - 1;
status_message = "Loaded: " + (current_doc.title.empty() ? url : current_doc.title); status_message = current_doc.title.empty() ? url : current_doc.title;
return true; return true;
} }
@ -80,7 +79,6 @@ public:
attron(COLOR_PAIR(COLOR_STATUS_BAR)); attron(COLOR_PAIR(COLOR_STATUS_BAR));
mvprintw(screen_height - 1, 0, "%s", std::string(screen_width, ' ').c_str()); mvprintw(screen_height - 1, 0, "%s", std::string(screen_width, ' ').c_str());
// 显示模式和缓冲
std::string mode_str; std::string mode_str;
InputMode mode = input_handler.get_mode(); InputMode mode = input_handler.get_mode();
switch (mode) { switch (mode) {
@ -88,29 +86,24 @@ public:
mode_str = "NORMAL"; mode_str = "NORMAL";
break; break;
case InputMode::COMMAND: case InputMode::COMMAND:
mode_str = input_handler.get_buffer();
break;
case InputMode::SEARCH: case InputMode::SEARCH:
mode_str = input_handler.get_buffer(); mode_str = input_handler.get_buffer();
break; break;
default: default:
mode_str = "???"; mode_str = "";
break; break;
} }
// 左侧:模式或命令
mvprintw(screen_height - 1, 0, " %s", mode_str.c_str()); mvprintw(screen_height - 1, 0, " %s", mode_str.c_str());
// 中间:状态消息
if (!status_message.empty() && mode == InputMode::NORMAL) { if (!status_message.empty() && mode == InputMode::NORMAL) {
int msg_x = (screen_width - status_message.length()) / 2; int msg_x = (screen_width - status_message.length()) / 2;
if (msg_x < mode_str.length() + 2) { if (msg_x < static_cast<int>(mode_str.length()) + 2) {
msg_x = mode_str.length() + 2; msg_x = mode_str.length() + 2;
} }
mvprintw(screen_height - 1, msg_x, "%s", status_message.c_str()); mvprintw(screen_height - 1, msg_x, "%s", status_message.c_str());
} }
// 右侧:位置信息
int total_lines = rendered_lines.size(); int total_lines = rendered_lines.size();
int visible_lines = screen_height - 2; int visible_lines = screen_height - 2;
int percentage = 0; int percentage = 0;
@ -147,7 +140,6 @@ public:
int line_idx = scroll_pos + i; int line_idx = scroll_pos + i;
const auto& line = rendered_lines[line_idx]; const auto& line = rendered_lines[line_idx];
// 高亮当前链接
if (line.is_link && line.link_index == current_link) { if (line.is_link && line.link_index == current_link) {
attron(COLOR_PAIR(COLOR_LINK_ACTIVE)); attron(COLOR_PAIR(COLOR_LINK_ACTIVE));
} else { } else {
@ -157,15 +149,12 @@ public:
} }
} }
// 搜索高亮
std::string display_text = line.text;
if (!search_term.empty() && if (!search_term.empty() &&
std::find(search_results.begin(), search_results.end(), line_idx) != search_results.end()) { std::find(search_results.begin(), search_results.end(), line_idx) != search_results.end()) {
// 简单高亮:整行反色(实际应该只高亮匹配部分)
attron(A_REVERSE); attron(A_REVERSE);
} }
mvprintw(i, 0, "%s", display_text.c_str()); mvprintw(i, 0, "%s", line.text.c_str());
if (!search_term.empty() && if (!search_term.empty() &&
std::find(search_results.begin(), search_results.end(), line_idx) != search_results.end()) { std::find(search_results.begin(), search_results.end(), line_idx) != search_results.end()) {
@ -225,7 +214,6 @@ public:
case Action::NEXT_LINK: case Action::NEXT_LINK:
if (!current_doc.links.empty()) { if (!current_doc.links.empty()) {
current_link = (current_link + 1) % current_doc.links.size(); current_link = (current_link + 1) % current_doc.links.size();
// 滚动到链接位置
scroll_to_link(current_link); scroll_to_link(current_link);
} }
break; break;

View file

@ -23,22 +23,18 @@ public:
result.has_count = false; result.has_count = false;
result.count = 1; result.count = 1;
// 处理数字前缀
if (std::isdigit(ch) && (ch != '0' || !count_buffer.empty())) { if (std::isdigit(ch) && (ch != '0' || !count_buffer.empty())) {
count_buffer += static_cast<char>(ch); count_buffer += static_cast<char>(ch);
return result; return result;
} }
// 解析count
if (!count_buffer.empty()) { if (!count_buffer.empty()) {
result.has_count = true; result.has_count = true;
result.count = std::stoi(count_buffer); result.count = std::stoi(count_buffer);
count_buffer.clear(); count_buffer.clear();
} }
// 处理vim风格的命令
switch (ch) { switch (ch) {
// 移动
case 'j': case 'j':
case KEY_DOWN: case KEY_DOWN:
result.action = Action::SCROLL_DOWN; result.action = Action::SCROLL_DOWN;
@ -55,18 +51,14 @@ public:
case KEY_RIGHT: case KEY_RIGHT:
result.action = Action::GO_FORWARD; result.action = Action::GO_FORWARD;
break; break;
case 4:
// 翻页
case 4: // Ctrl-D
case ' ': case ' ':
result.action = Action::SCROLL_PAGE_DOWN; result.action = Action::SCROLL_PAGE_DOWN;
break; break;
case 21: // Ctrl-U case 21:
case 'b': case 'b':
result.action = Action::SCROLL_PAGE_UP; result.action = Action::SCROLL_PAGE_UP;
break; break;
// 跳转
case 'g': case 'g':
buffer += 'g'; buffer += 'g';
if (buffer == "gg") { if (buffer == "gg") {
@ -82,8 +74,6 @@ public:
result.action = Action::GOTO_BOTTOM; result.action = Action::GOTO_BOTTOM;
} }
break; break;
// 搜索
case '/': case '/':
mode = InputMode::SEARCH; mode = InputMode::SEARCH;
buffer = "/"; buffer = "/";
@ -94,27 +84,21 @@ public:
case 'N': case 'N':
result.action = Action::SEARCH_PREV; result.action = Action::SEARCH_PREV;
break; break;
case '\t':
// 链接导航
case '\t': // Tab
result.action = Action::NEXT_LINK; result.action = Action::NEXT_LINK;
break; break;
case KEY_BTAB: // Shift-Tab (可能不是所有终端都支持) case KEY_BTAB:
case 'T': case 'T':
result.action = Action::PREV_LINK; result.action = Action::PREV_LINK;
break; break;
case '\n': // Enter case '\n':
case '\r': case '\r':
result.action = Action::FOLLOW_LINK; result.action = Action::FOLLOW_LINK;
break; break;
// 命令模式
case ':': case ':':
mode = InputMode::COMMAND; mode = InputMode::COMMAND;
buffer = ":"; buffer = ":";
break; break;
// 其他操作
case 'r': case 'r':
result.action = Action::REFRESH; result.action = Action::REFRESH;
break; break;
@ -124,7 +108,6 @@ public:
case '?': case '?':
result.action = Action::HELP; result.action = Action::HELP;
break; break;
default: default:
buffer.clear(); buffer.clear();
break; break;
@ -138,8 +121,7 @@ public:
result.action = Action::NONE; result.action = Action::NONE;
if (ch == '\n' || ch == '\r') { if (ch == '\n' || ch == '\r') {
// 执行命令 std::string command = buffer.substr(1);
std::string command = buffer.substr(1); // 去掉':'
if (command == "q" || command == "quit") { if (command == "q" || command == "quit") {
result.action = Action::QUIT; result.action = Action::QUIT;
@ -148,14 +130,12 @@ public:
} else if (command == "r" || command == "refresh") { } else if (command == "r" || command == "refresh") {
result.action = Action::REFRESH; result.action = Action::REFRESH;
} else if (command.rfind("o ", 0) == 0 || command.rfind("open ", 0) == 0) { } else if (command.rfind("o ", 0) == 0 || command.rfind("open ", 0) == 0) {
// :o URL 或 :open URL
size_t space_pos = command.find(' '); size_t space_pos = command.find(' ');
if (space_pos != std::string::npos) { if (space_pos != std::string::npos) {
result.action = Action::OPEN_URL; result.action = Action::OPEN_URL;
result.text = command.substr(space_pos + 1); result.text = command.substr(space_pos + 1);
} }
} else if (!command.empty() && std::isdigit(command[0])) { } else if (!command.empty() && std::isdigit(command[0])) {
// 跳转到行号
try { try {
result.action = Action::GOTO_LINE; result.action = Action::GOTO_LINE;
result.number = std::stoi(command); result.number = std::stoi(command);
@ -166,7 +146,7 @@ public:
mode = InputMode::NORMAL; mode = InputMode::NORMAL;
buffer.clear(); buffer.clear();
} else if (ch == 27) { // ESC } else if (ch == 27) {
mode = InputMode::NORMAL; mode = InputMode::NORMAL;
buffer.clear(); buffer.clear();
} else if (ch == KEY_BACKSPACE || ch == 127 || ch == 8) { } else if (ch == KEY_BACKSPACE || ch == 127 || ch == 8) {
@ -188,14 +168,13 @@ public:
result.action = Action::NONE; result.action = Action::NONE;
if (ch == '\n' || ch == '\r') { if (ch == '\n' || ch == '\r') {
// 执行搜索
if (buffer.length() > 1) { if (buffer.length() > 1) {
result.action = Action::SEARCH_FORWARD; result.action = Action::SEARCH_FORWARD;
result.text = buffer.substr(1); // 去掉'/' result.text = buffer.substr(1);
} }
mode = InputMode::NORMAL; mode = InputMode::NORMAL;
buffer.clear(); buffer.clear();
} else if (ch == 27) { // ESC } else if (ch == 27) {
mode = InputMode::NORMAL; mode = InputMode::NORMAL;
buffer.clear(); buffer.clear();
} else if (ch == KEY_BACKSPACE || ch == 127 || ch == 8) { } else if (ch == KEY_BACKSPACE || ch == 127 || ch == 8) {

View file

@ -24,7 +24,6 @@ void print_usage(const char* prog_name) {
} }
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
// 解析命令行参数
std::string initial_url; std::string initial_url;
if (argc > 1) { if (argc > 1) {

View file

@ -7,7 +7,6 @@ class TextRenderer::Impl {
public: public:
RenderConfig config; RenderConfig config;
// 自动换行处理
std::vector<std::string> wrap_text(const std::string& text, int width) { std::vector<std::string> wrap_text(const std::string& text, int width) {
std::vector<std::string> lines; std::vector<std::string> lines;
if (text.empty()) { if (text.empty()) {
@ -19,20 +18,17 @@ public:
std::string current_line; std::string current_line;
while (words_stream >> word) { while (words_stream >> word) {
// 处理单个词超长的情况
if (word.length() > static_cast<size_t>(width)) { if (word.length() > static_cast<size_t>(width)) {
if (!current_line.empty()) { if (!current_line.empty()) {
lines.push_back(current_line); lines.push_back(current_line);
current_line.clear(); current_line.clear();
} }
// 强制分割长词
for (size_t i = 0; i < word.length(); i += width) { for (size_t i = 0; i < word.length(); i += width) {
lines.push_back(word.substr(i, width)); lines.push_back(word.substr(i, width));
} }
continue; continue;
} }
// 正常换行逻辑
if (current_line.empty()) { if (current_line.empty()) {
current_line = word; current_line = word;
} else if (current_line.length() + 1 + word.length() <= static_cast<size_t>(width)) { } else if (current_line.length() + 1 + word.length() <= static_cast<size_t>(width)) {
@ -54,7 +50,6 @@ public:
return lines; return lines;
} }
// 添加缩进
std::string add_indent(const std::string& text, int indent) { std::string add_indent(const std::string& text, int indent) {
return std::string(indent, ' ') + text; return std::string(indent, ' ') + text;
} }
@ -69,20 +64,17 @@ TextRenderer::~TextRenderer() = default;
std::vector<RenderedLine> TextRenderer::render(const ParsedDocument& doc, int screen_width) { std::vector<RenderedLine> TextRenderer::render(const ParsedDocument& doc, int screen_width) {
std::vector<RenderedLine> lines; std::vector<RenderedLine> lines;
// 计算实际内容宽度
int content_width = std::min(pImpl->config.max_width, screen_width - 4); int content_width = std::min(pImpl->config.max_width, screen_width - 4);
if (content_width < 40) { if (content_width < 40) {
content_width = screen_width - 4; content_width = screen_width - 4;
} }
// 计算左边距(如果居中)
int margin = 0; int margin = 0;
if (pImpl->config.center_content && content_width < screen_width) { if (pImpl->config.center_content && content_width < screen_width) {
margin = (screen_width - content_width) / 2; margin = (screen_width - content_width) / 2;
} }
pImpl->config.margin_left = margin; pImpl->config.margin_left = margin;
// 渲染标题
if (!doc.title.empty()) { if (!doc.title.empty()) {
RenderedLine title_line; RenderedLine title_line;
title_line.text = std::string(margin, ' ') + doc.title; title_line.text = std::string(margin, ' ') + doc.title;
@ -92,7 +84,6 @@ std::vector<RenderedLine> TextRenderer::render(const ParsedDocument& doc, int sc
title_line.link_index = -1; title_line.link_index = -1;
lines.push_back(title_line); lines.push_back(title_line);
// 标题下划线
RenderedLine underline; RenderedLine underline;
underline.text = std::string(margin, ' ') + std::string(std::min((int)doc.title.length(), content_width), '='); underline.text = std::string(margin, ' ') + std::string(std::min((int)doc.title.length(), content_width), '=');
underline.color_pair = COLOR_HEADING1; underline.color_pair = COLOR_HEADING1;
@ -101,7 +92,6 @@ std::vector<RenderedLine> TextRenderer::render(const ParsedDocument& doc, int sc
underline.link_index = -1; underline.link_index = -1;
lines.push_back(underline); lines.push_back(underline);
// 空行
RenderedLine empty; RenderedLine empty;
empty.text = ""; empty.text = "";
empty.color_pair = COLOR_NORMAL; empty.color_pair = COLOR_NORMAL;
@ -111,7 +101,6 @@ std::vector<RenderedLine> TextRenderer::render(const ParsedDocument& doc, int sc
lines.push_back(empty); lines.push_back(empty);
} }
// 渲染URL
if (!doc.url.empty()) { if (!doc.url.empty()) {
RenderedLine url_line; RenderedLine url_line;
url_line.text = std::string(margin, ' ') + "URL: " + doc.url; url_line.text = std::string(margin, ' ') + "URL: " + doc.url;
@ -130,7 +119,6 @@ std::vector<RenderedLine> TextRenderer::render(const ParsedDocument& doc, int sc
lines.push_back(empty); lines.push_back(empty);
} }
// 渲染内容元素
for (const auto& elem : doc.elements) { for (const auto& elem : doc.elements) {
int color = COLOR_NORMAL; int color = COLOR_NORMAL;
bool bold = false; bool bold = false;
@ -179,7 +167,6 @@ std::vector<RenderedLine> TextRenderer::render(const ParsedDocument& doc, int sc
break; break;
} }
// 换行处理
auto wrapped_lines = pImpl->wrap_text(elem.text, content_width - prefix.length()); auto wrapped_lines = pImpl->wrap_text(elem.text, content_width - prefix.length());
for (size_t i = 0; i < wrapped_lines.size(); ++i) { for (size_t i = 0; i < wrapped_lines.size(); ++i) {
RenderedLine line; RenderedLine line;
@ -195,7 +182,6 @@ std::vector<RenderedLine> TextRenderer::render(const ParsedDocument& doc, int sc
lines.push_back(line); lines.push_back(line);
} }
// 段落间距
if (elem.type == ElementType::PARAGRAPH || if (elem.type == ElementType::PARAGRAPH ||
elem.type == ElementType::HEADING1 || elem.type == ElementType::HEADING1 ||
elem.type == ElementType::HEADING2 || elem.type == ElementType::HEADING2 ||
@ -212,7 +198,6 @@ std::vector<RenderedLine> TextRenderer::render(const ParsedDocument& doc, int sc
} }
} }
// 渲染链接列表
if (!doc.links.empty() && pImpl->config.show_link_indicators) { if (!doc.links.empty() && pImpl->config.show_link_indicators) {
RenderedLine separator; RenderedLine separator;
std::string sepline(content_width, '-'); std::string sepline(content_width, '-');
@ -254,7 +239,6 @@ std::vector<RenderedLine> TextRenderer::render(const ParsedDocument& doc, int sc
lines.push_back(link_line); lines.push_back(link_line);
} }
// URL on next line
auto url_wrapped = pImpl->wrap_text(link.url, content_width - 6); auto url_wrapped = pImpl->wrap_text(link.url, content_width - 6);
for (const auto& url_line_text : url_wrapped) { for (const auto& url_line_text : url_wrapped) {
RenderedLine url_line; RenderedLine url_line;