mirror of
https://github.com/m1ngsama/TUT.git
synced 2026-02-08 00:54:05 +00:00
feat: Add bookmark management
- Add BookmarkManager class for bookmark CRUD operations - Store bookmarks in JSON format at ~/.config/tut/bookmarks.json - Add keyboard shortcuts: B (add), D (remove) - Add :bookmarks/:bm command to view bookmark list - Bookmarks page shows clickable links - Auto-save on add/remove, auto-load on startup
This commit is contained in:
parent
c6b1a9ac41
commit
a4c95a6527
7 changed files with 472 additions and 17 deletions
|
|
@ -88,6 +88,7 @@ add_executable(tut2
|
||||||
src/browser_v2.cpp
|
src/browser_v2.cpp
|
||||||
src/http_client.cpp
|
src/http_client.cpp
|
||||||
src/input_handler.cpp
|
src/input_handler.cpp
|
||||||
|
src/bookmark.cpp
|
||||||
src/render/terminal.cpp
|
src/render/terminal.cpp
|
||||||
src/render/renderer.cpp
|
src/render/renderer.cpp
|
||||||
src/render/layout.cpp
|
src/render/layout.cpp
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
# TUT 2.0 - 下次继续从这里开始
|
# TUT 2.0 - 下次继续从这里开始
|
||||||
|
|
||||||
## 当前位置
|
## 当前位置
|
||||||
- **阶段**: Phase 4 - 图片支持 (已完成!)
|
- **阶段**: Phase 5 - 书签管理 (已完成!)
|
||||||
- **进度**: 图片 ASCII Art 渲染已集成到浏览器
|
- **进度**: 书签添加/删除/持久化存储已完成
|
||||||
- **最后提交**: `feat: Add image ASCII art rendering support`
|
- **最后提交**: `feat: Add bookmark management`
|
||||||
|
|
||||||
## 立即可做的事
|
## 立即可做的事
|
||||||
|
|
||||||
|
|
@ -15,21 +15,25 @@ curl -L https://raw.githubusercontent.com/nothings/stb/master/stb_image.h \
|
||||||
|
|
||||||
# 重新编译
|
# 重新编译
|
||||||
cmake --build build_v2
|
cmake --build build_v2
|
||||||
|
|
||||||
# 编译后会自动支持 PNG/JPEG/GIF/BMP 图片格式
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. 测试图片渲染
|
### 2. 使用书签功能
|
||||||
```bash
|
- **B** - 添加当前页面到书签
|
||||||
# 访问有图片的网页
|
- **D** - 从书签中移除当前页面
|
||||||
./build_v2/tut2 https://httpbin.org/html
|
- **:bookmarks** 或 **:bm** - 查看书签列表
|
||||||
|
|
||||||
# 或访问包含图片的任意网页
|
书签存储在 `~/.config/tut/bookmarks.json`
|
||||||
./build_v2/tut2 https://example.com
|
|
||||||
```
|
|
||||||
|
|
||||||
## 已完成的功能清单
|
## 已完成的功能清单
|
||||||
|
|
||||||
|
### Phase 5 - 书签管理
|
||||||
|
- [x] 书签数据结构 (URL, 标题, 添加时间)
|
||||||
|
- [x] JSON 持久化存储 (~/.config/tut/bookmarks.json)
|
||||||
|
- [x] 添加书签 (B 键)
|
||||||
|
- [x] 删除书签 (D 键)
|
||||||
|
- [x] 书签列表页面 (:bookmarks 命令)
|
||||||
|
- [x] 书签链接可点击跳转
|
||||||
|
|
||||||
### Phase 4 - 图片支持
|
### Phase 4 - 图片支持
|
||||||
- [x] `<img>` 标签解析 (src, alt, width, height)
|
- [x] `<img>` 标签解析 (src, alt, width, height)
|
||||||
- [x] 图片占位符显示 `[alt text]` 或 `[Image: filename]`
|
- [x] 图片占位符显示 `[alt text]` 或 `[Image: filename]`
|
||||||
|
|
@ -127,10 +131,10 @@ cmake --build build_v2
|
||||||
|
|
||||||
## 下一步功能优先级
|
## 下一步功能优先级
|
||||||
|
|
||||||
1. **书签管理** - 添加/删除书签,书签列表页面,持久化存储
|
1. **异步 HTTP 请求** - 非阻塞加载,加载动画,可取消请求
|
||||||
2. **异步 HTTP 请求** - 非阻塞加载,加载动画,可取消请求
|
2. **更多表单交互** - 文本输入编辑,下拉选择
|
||||||
3. **更多表单交互** - 文本输入编辑,下拉选择
|
3. **图片缓存** - 避免重复下载相同图片
|
||||||
4. **图片缓存** - 避免重复下载相同图片
|
4. **历史记录管理** - 持久化历史记录,历史页面
|
||||||
|
|
||||||
## 恢复对话时说
|
## 恢复对话时说
|
||||||
|
|
||||||
|
|
|
||||||
248
src/bookmark.cpp
Normal file
248
src/bookmark.cpp
Normal file
|
|
@ -0,0 +1,248 @@
|
||||||
|
#include "bookmark.h"
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
namespace tut {
|
||||||
|
|
||||||
|
BookmarkManager::BookmarkManager() {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
|
BookmarkManager::~BookmarkManager() {
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string BookmarkManager::get_config_dir() {
|
||||||
|
const char* home = std::getenv("HOME");
|
||||||
|
if (!home) {
|
||||||
|
home = "/tmp";
|
||||||
|
}
|
||||||
|
return std::string(home) + "/.config/tut";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string BookmarkManager::get_bookmarks_path() {
|
||||||
|
return get_config_dir() + "/bookmarks.json";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BookmarkManager::ensure_config_dir() {
|
||||||
|
std::string dir = get_config_dir();
|
||||||
|
|
||||||
|
// 检查目录是否存在
|
||||||
|
struct stat st;
|
||||||
|
if (stat(dir.c_str(), &st) == 0) {
|
||||||
|
return S_ISDIR(st.st_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建 ~/.config 目录
|
||||||
|
std::string config_dir = std::string(std::getenv("HOME") ? std::getenv("HOME") : "/tmp") + "/.config";
|
||||||
|
mkdir(config_dir.c_str(), 0755);
|
||||||
|
|
||||||
|
// 创建 ~/.config/tut 目录
|
||||||
|
return mkdir(dir.c_str(), 0755) == 0 || errno == EEXIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 简单的 JSON 转义
|
||||||
|
static std::string json_escape(const std::string& s) {
|
||||||
|
std::string result;
|
||||||
|
result.reserve(s.size() + 10);
|
||||||
|
for (char c : s) {
|
||||||
|
switch (c) {
|
||||||
|
case '"': result += "\\\""; break;
|
||||||
|
case '\\': result += "\\\\"; break;
|
||||||
|
case '\n': result += "\\n"; break;
|
||||||
|
case '\r': result += "\\r"; break;
|
||||||
|
case '\t': result += "\\t"; break;
|
||||||
|
default: result += c; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 简单的 JSON 反转义
|
||||||
|
static std::string json_unescape(const std::string& s) {
|
||||||
|
std::string result;
|
||||||
|
result.reserve(s.size());
|
||||||
|
for (size_t i = 0; i < s.size(); ++i) {
|
||||||
|
if (s[i] == '\\' && i + 1 < s.size()) {
|
||||||
|
switch (s[i + 1]) {
|
||||||
|
case '"': result += '"'; ++i; break;
|
||||||
|
case '\\': result += '\\'; ++i; break;
|
||||||
|
case 'n': result += '\n'; ++i; break;
|
||||||
|
case 'r': result += '\r'; ++i; break;
|
||||||
|
case 't': result += '\t'; ++i; break;
|
||||||
|
default: result += s[i]; break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result += s[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BookmarkManager::load() {
|
||||||
|
bookmarks_.clear();
|
||||||
|
|
||||||
|
std::ifstream file(get_bookmarks_path());
|
||||||
|
if (!file) {
|
||||||
|
return false; // 文件不存在,这是正常的
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string content((std::istreambuf_iterator<char>(file)),
|
||||||
|
std::istreambuf_iterator<char>());
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
// 简单的 JSON 解析
|
||||||
|
// 格式: [{"url":"...","title":"...","time":123}, ...]
|
||||||
|
size_t pos = content.find('[');
|
||||||
|
if (pos == std::string::npos) return false;
|
||||||
|
|
||||||
|
pos++; // 跳过 '['
|
||||||
|
|
||||||
|
while (pos < content.size()) {
|
||||||
|
// 查找对象开始
|
||||||
|
pos = content.find('{', pos);
|
||||||
|
if (pos == std::string::npos) break;
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
Bookmark bm;
|
||||||
|
|
||||||
|
// 解析字段
|
||||||
|
while (pos < content.size() && content[pos] != '}') {
|
||||||
|
// 跳过空白
|
||||||
|
while (pos < content.size() && (content[pos] == ' ' || content[pos] == '\n' ||
|
||||||
|
content[pos] == '\r' || content[pos] == '\t' || content[pos] == ',')) {
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content[pos] == '}') break;
|
||||||
|
|
||||||
|
// 读取键名
|
||||||
|
if (content[pos] != '"') { pos++; continue; }
|
||||||
|
pos++; // 跳过 '"'
|
||||||
|
|
||||||
|
size_t key_end = content.find('"', pos);
|
||||||
|
if (key_end == std::string::npos) break;
|
||||||
|
std::string key = content.substr(pos, key_end - pos);
|
||||||
|
pos = key_end + 1;
|
||||||
|
|
||||||
|
// 跳过 ':'
|
||||||
|
pos = content.find(':', pos);
|
||||||
|
if (pos == std::string::npos) break;
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
// 跳过空白
|
||||||
|
while (pos < content.size() && (content[pos] == ' ' || content[pos] == '\n' ||
|
||||||
|
content[pos] == '\r' || content[pos] == '\t')) {
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content[pos] == '"') {
|
||||||
|
// 字符串值
|
||||||
|
pos++; // 跳过 '"'
|
||||||
|
size_t val_end = pos;
|
||||||
|
while (val_end < content.size()) {
|
||||||
|
if (content[val_end] == '"' && content[val_end - 1] != '\\') break;
|
||||||
|
val_end++;
|
||||||
|
}
|
||||||
|
std::string value = json_unescape(content.substr(pos, val_end - pos));
|
||||||
|
pos = val_end + 1;
|
||||||
|
|
||||||
|
if (key == "url") bm.url = value;
|
||||||
|
else if (key == "title") bm.title = value;
|
||||||
|
} else {
|
||||||
|
// 数字值
|
||||||
|
size_t val_end = pos;
|
||||||
|
while (val_end < content.size() && content[val_end] >= '0' && content[val_end] <= '9') {
|
||||||
|
val_end++;
|
||||||
|
}
|
||||||
|
std::string value = content.substr(pos, val_end - pos);
|
||||||
|
pos = val_end;
|
||||||
|
|
||||||
|
if (key == "time") {
|
||||||
|
bm.added_time = std::stoll(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bm.url.empty()) {
|
||||||
|
bookmarks_.push_back(bm);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳到下一个对象
|
||||||
|
pos = content.find('}', pos);
|
||||||
|
if (pos == std::string::npos) break;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BookmarkManager::save() const {
|
||||||
|
if (!ensure_config_dir()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ofstream file(get_bookmarks_path());
|
||||||
|
if (!file) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file << "[\n";
|
||||||
|
for (size_t i = 0; i < bookmarks_.size(); ++i) {
|
||||||
|
const auto& bm = bookmarks_[i];
|
||||||
|
file << " {\n";
|
||||||
|
file << " \"url\": \"" << json_escape(bm.url) << "\",\n";
|
||||||
|
file << " \"title\": \"" << json_escape(bm.title) << "\",\n";
|
||||||
|
file << " \"time\": " << bm.added_time << "\n";
|
||||||
|
file << " }";
|
||||||
|
if (i + 1 < bookmarks_.size()) {
|
||||||
|
file << ",";
|
||||||
|
}
|
||||||
|
file << "\n";
|
||||||
|
}
|
||||||
|
file << "]\n";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BookmarkManager::add(const std::string& url, const std::string& title) {
|
||||||
|
// 检查是否已存在
|
||||||
|
if (contains(url)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bookmarks_.emplace_back(url, title);
|
||||||
|
return save();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BookmarkManager::remove(const std::string& url) {
|
||||||
|
auto it = std::find_if(bookmarks_.begin(), bookmarks_.end(),
|
||||||
|
[&url](const Bookmark& bm) { return bm.url == url; });
|
||||||
|
|
||||||
|
if (it == bookmarks_.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bookmarks_.erase(it);
|
||||||
|
return save();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BookmarkManager::remove_at(size_t index) {
|
||||||
|
if (index >= bookmarks_.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bookmarks_.erase(bookmarks_.begin() + index);
|
||||||
|
return save();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BookmarkManager::contains(const std::string& url) const {
|
||||||
|
return std::find_if(bookmarks_.begin(), bookmarks_.end(),
|
||||||
|
[&url](const Bookmark& bm) { return bm.url == url; })
|
||||||
|
!= bookmarks_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace tut
|
||||||
96
src/bookmark.h
Normal file
96
src/bookmark.h
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
namespace tut {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 书签条目
|
||||||
|
*/
|
||||||
|
struct Bookmark {
|
||||||
|
std::string url;
|
||||||
|
std::string title;
|
||||||
|
std::time_t added_time;
|
||||||
|
|
||||||
|
Bookmark() : added_time(0) {}
|
||||||
|
Bookmark(const std::string& url, const std::string& title)
|
||||||
|
: url(url), title(title), added_time(std::time(nullptr)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 书签管理器
|
||||||
|
*
|
||||||
|
* 书签存储在 ~/.config/tut/bookmarks.json
|
||||||
|
*/
|
||||||
|
class BookmarkManager {
|
||||||
|
public:
|
||||||
|
BookmarkManager();
|
||||||
|
~BookmarkManager();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载书签(从默认路径)
|
||||||
|
*/
|
||||||
|
bool load();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存书签(到默认路径)
|
||||||
|
*/
|
||||||
|
bool save() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加书签
|
||||||
|
* @return true 如果添加成功,false 如果已存在
|
||||||
|
*/
|
||||||
|
bool add(const std::string& url, const std::string& title);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除书签
|
||||||
|
* @return true 如果删除成功
|
||||||
|
*/
|
||||||
|
bool remove(const std::string& url);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除书签(按索引)
|
||||||
|
*/
|
||||||
|
bool remove_at(size_t index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查URL是否已收藏
|
||||||
|
*/
|
||||||
|
bool contains(const std::string& url) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取书签列表
|
||||||
|
*/
|
||||||
|
const std::vector<Bookmark>& get_all() const { return bookmarks_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取书签数量
|
||||||
|
*/
|
||||||
|
size_t count() const { return bookmarks_.size(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空所有书签
|
||||||
|
*/
|
||||||
|
void clear() { bookmarks_.clear(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取配置目录路径
|
||||||
|
*/
|
||||||
|
static std::string get_config_dir();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取书签文件路径
|
||||||
|
*/
|
||||||
|
static std::string get_bookmarks_path();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Bookmark> bookmarks_;
|
||||||
|
|
||||||
|
// 确保配置目录存在
|
||||||
|
static bool ensure_config_dir();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace tut
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "browser_v2.h"
|
#include "browser_v2.h"
|
||||||
#include "dom_tree.h"
|
#include "dom_tree.h"
|
||||||
|
#include "bookmark.h"
|
||||||
#include "render/colors.h"
|
#include "render/colors.h"
|
||||||
#include "render/decorations.h"
|
#include "render/decorations.h"
|
||||||
#include "render/image.h"
|
#include "render/image.h"
|
||||||
|
|
@ -33,6 +34,7 @@ public:
|
||||||
HttpClient http_client;
|
HttpClient http_client;
|
||||||
HtmlParser html_parser;
|
HtmlParser html_parser;
|
||||||
InputHandler input_handler;
|
InputHandler input_handler;
|
||||||
|
tut::BookmarkManager bookmark_manager;
|
||||||
|
|
||||||
// 新渲染系统
|
// 新渲染系统
|
||||||
Terminal terminal;
|
Terminal terminal;
|
||||||
|
|
@ -407,6 +409,18 @@ public:
|
||||||
show_help();
|
show_help();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Action::ADD_BOOKMARK:
|
||||||
|
add_bookmark();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Action::REMOVE_BOOKMARK:
|
||||||
|
remove_bookmark();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Action::SHOW_BOOKMARKS:
|
||||||
|
show_bookmarks();
|
||||||
|
break;
|
||||||
|
|
||||||
case Action::QUIT:
|
case Action::QUIT:
|
||||||
break; // 在main loop处理
|
break; // 在main loop处理
|
||||||
|
|
||||||
|
|
@ -589,9 +603,17 @@ public:
|
||||||
<li>N - Previous match</li>
|
<li>N - Previous match</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<h2>Bookmarks</h2>
|
||||||
|
<ul>
|
||||||
|
<li>B - Add bookmark</li>
|
||||||
|
<li>D - Remove bookmark</li>
|
||||||
|
<li>:bookmarks - Show bookmarks</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h2>Commands</h2>
|
<h2>Commands</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>:o URL - Open URL</li>
|
<li>:o URL - Open URL</li>
|
||||||
|
<li>:bookmarks - Show bookmarks</li>
|
||||||
<li>:q - Quit</li>
|
<li>:q - Quit</li>
|
||||||
<li>? - Show this help</li>
|
<li>? - Show this help</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
@ -613,6 +635,79 @@ public:
|
||||||
active_link = current_tree.links.empty() ? -1 : 0;
|
active_link = current_tree.links.empty() ? -1 : 0;
|
||||||
status_message = "Help - Press any key to continue";
|
status_message = "Help - Press any key to continue";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void show_bookmarks() {
|
||||||
|
std::ostringstream html;
|
||||||
|
html << R"(
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head><title>Bookmarks</title></head>
|
||||||
|
<body>
|
||||||
|
<h1>Bookmarks</h1>
|
||||||
|
)";
|
||||||
|
|
||||||
|
const auto& bookmarks = bookmark_manager.get_all();
|
||||||
|
|
||||||
|
if (bookmarks.empty()) {
|
||||||
|
html << "<p>No bookmarks yet.</p>\n";
|
||||||
|
html << "<p>Press <b>B</b> on any page to add a bookmark.</p>\n";
|
||||||
|
} else {
|
||||||
|
html << "<ul>\n";
|
||||||
|
for (const auto& bm : bookmarks) {
|
||||||
|
html << "<li><a href=\"" << bm.url << "\">"
|
||||||
|
<< (bm.title.empty() ? bm.url : bm.title)
|
||||||
|
<< "</a></li>\n";
|
||||||
|
}
|
||||||
|
html << "</ul>\n";
|
||||||
|
html << "<hr>\n";
|
||||||
|
html << "<p>" << bookmarks.size() << " bookmark(s). Press D on any page to remove its bookmark.</p>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
html << R"(
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
)";
|
||||||
|
|
||||||
|
current_tree = html_parser.parse_tree(html.str(), "bookmarks://");
|
||||||
|
current_layout = layout_engine->layout(current_tree);
|
||||||
|
scroll_pos = 0;
|
||||||
|
active_link = current_tree.links.empty() ? -1 : 0;
|
||||||
|
status_message = "Bookmarks";
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_bookmark() {
|
||||||
|
if (current_url.empty() || current_url.find("://") == std::string::npos) {
|
||||||
|
status_message = "Cannot bookmark this page";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不要书签特殊页面
|
||||||
|
if (current_url.find("help://") == 0 || current_url.find("bookmarks://") == 0) {
|
||||||
|
status_message = "Cannot bookmark special pages";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string title = current_tree.title.empty() ? current_url : current_tree.title;
|
||||||
|
|
||||||
|
if (bookmark_manager.add(current_url, title)) {
|
||||||
|
status_message = "Bookmarked: " + title;
|
||||||
|
} else {
|
||||||
|
status_message = "Already bookmarked";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_bookmark() {
|
||||||
|
if (current_url.empty()) {
|
||||||
|
status_message = "No page to unbookmark";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bookmark_manager.remove(current_url)) {
|
||||||
|
status_message = "Bookmark removed";
|
||||||
|
} else {
|
||||||
|
status_message = "Not bookmarked";
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
BrowserV2::BrowserV2() : pImpl(std::make_unique<Impl>()) {
|
BrowserV2::BrowserV2() : pImpl(std::make_unique<Impl>()) {
|
||||||
|
|
|
||||||
|
|
@ -174,6 +174,12 @@ public:
|
||||||
case '?':
|
case '?':
|
||||||
result.action = Action::HELP;
|
result.action = Action::HELP;
|
||||||
break;
|
break;
|
||||||
|
case 'B':
|
||||||
|
result.action = Action::ADD_BOOKMARK;
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
result.action = Action::REMOVE_BOOKMARK;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
break;
|
break;
|
||||||
|
|
@ -201,6 +207,8 @@ public:
|
||||||
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 == "bookmarks" || command == "bm" || command == "b") {
|
||||||
|
result.action = Action::SHOW_BOOKMARKS;
|
||||||
} 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;
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,10 @@ enum class Action {
|
||||||
QUIT,
|
QUIT,
|
||||||
HELP,
|
HELP,
|
||||||
SET_MARK, // Set a mark (m + letter)
|
SET_MARK, // Set a mark (m + letter)
|
||||||
GOTO_MARK // Jump to mark (' + letter)
|
GOTO_MARK, // Jump to mark (' + letter)
|
||||||
|
ADD_BOOKMARK, // Add current page to bookmarks (B)
|
||||||
|
REMOVE_BOOKMARK, // Remove current page from bookmarks (D)
|
||||||
|
SHOW_BOOKMARKS // Show bookmarks page (:bookmarks)
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InputResult {
|
struct InputResult {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue