Skip to content

Repository files navigation

Sylva — 终端知乎浏览器,在终端里刷知乎,全程键盘操作

Rust ratatui MIT v0.2.0


场景

你的老板从身后走过。你的屏幕上没有五彩斑斓的网页,没有鬼鬼祟祟最小化的浏览器窗口 — 只有一个全屏终端,黑底白字,卷动着密密麻麻的日志和命令行输出。你眉头微皱,时而敲几下键盘,一副 debug 到关键处的样子。

实际上你在刷知乎热榜。

Sylva 把知乎完整地搬进终端,让每一次摸鱼都像一次严肃的工程工作。

功能

启动后是一个干净的单列 Feed 列表:顶部标题栏,中间条目,底部状态栏。热榜和推荐流各有 50 条内容,每一条显示标题和摘要;选中高亮,Enter 进入回答正文;Tab 一键切换热榜和推荐流;c 展开评论区树形阅读。全程手指不离键盘,眼睛不离开屏幕 — 从远处看,就像在 vim 里编辑配置文件。

模块 说明
热榜 知乎实时热榜,50 条全量展示,匿名可浏览
推荐流 个性化推荐,登录后自动识别,支持翻页加载
不感兴趣 推荐流中按 d 屏蔽当前条目,自动去重,下次刷新不再出现
回答阅读 全文分页展示,支持上下条切换、翻页加载更多
评论区 树形递归展开根评论和子评论,自动分页拉取
全文搜索 支持问题、回答、文章三种类型检索
Cookie 登录 登录态本地持久化(SQLite),不出机器
深色配色 终端原生深色方案,长时间阅读不刺眼

快速开始

需要 Rust 1.80 及以上版本。

git clone https://github.com/iiabc/Sylva.git
cd Sylva
cargo build --release

编译后在终端直接运行:

./target/release/sylva

或安装到系统路径:

cargo install --path .
sylva

开启调试日志(写入 ~/.local/share/sylva/debug.log):

sylva -d

登录

热榜可匿名浏览。推荐流需要登录态。

  1. 在 Chrome 中打开 zhihu.com 并登录
  2. F12 打开开发者工具,进入 Application → Cookies → zhihu.com
  3. 找到 z_c0 这个 Cookie,复制它的值
  4. 在 Sylva 中按 L,粘贴如下格式:
z_c0=2|1:0|10:10abcdef...

Cookie 只存在你本地的 SQLite 数据库里(路径:~/.local/share/sylva/sylva.db),不会离开你的机器。

快捷键

全局

按键 功能
j / 向下移动
k / 向上移动
Enter 打开选中项
q / Esc 返回上级;首页则退出
Ctrl+C 强制退出
/ 搜索知乎
r 刷新当前页面
L Cookie 登录设置
? 帮助

热榜 & 推荐流

按键 功能
Tab 热榜 / 推荐流切换
l 加载更多(推荐流翻页)
d 不感兴趣(屏蔽当前推荐条目)

回答详情

按键 功能
n / 下一条回答
p / / h 上一条回答
c 查看评论区
g 跳到顶部
G 跳到底部
l 加载更多回答

评论区

按键 功能
j / 向下滚动
k / 向上滚动

架构

从外到内四层,遵循 herdr 模式 — 命令式渲染循环 + crossterm 输入轮询 + 事件驱动状态变更:

main.rs          CLI 入口,clap 参数解析,tracing 日志初始化
    ↓
app.rs           应用核心:App 状态机、模式栈、事件循环、异步 fetch task
    ↓
┌──────────────┬────────────────┬───────────────┐
│   ui/        │   zhihu/       │   store/      │
│   TUI 渲染    │   知乎数据层   │   SQLite 持久化│
│              │               │               │
│  ratatui     │  reqwest HTTP │  rusqlite     │
│  crossterm   │  scraper 解析 │  WAL 模式     │
│  自定义组件  │  auth/cookie  │  单连接       │
└──────────────┴────────────────┴───────────────┘
  1. TUI 层 (ui/) — ratatui + crossterm。命令式渲染循环每 250ms tick 一次,poll 键盘/鼠标事件后直接修改 App 状态,再调用 terminal.draw() 全量重绘。键盘绑定支持 TOML 配置文件覆盖。
  2. 数据层 (zhihu/) — reqwest HTTP 客户端封装。处理 User-Agent 伪装、频率控制(随机间隔避免触发反爬)、429 自动退避重试、JSON 格式兼容(知乎 API 的字段类型不稳定)。
  3. 存储层 (store/) — rusqlite 单文件数据库,WAL 模式,单连接。管理 Cookie 持久化、API 响应缓存、用户设置、屏蔽条目列表。
  4. 事件通道 (event.rs) — tokio mpsc channel 连接异步 fetch task 和主渲染循环。Fetch task 在独立协程中跑 HTTP 请求,完成后通过 channel 发送 AppEvent 给主循环消费。

项目结构

src/
  main.rs            CLI 入口,tokio runtime 引导
  app.rs             应用核心:模式栈、状态机、事件处理、fetch task
  config.rs          TOML 配置管理(user_agent、超时、键位映射)
  event.rs           事件通道定义(AppEvent、FetchCommand、mpsc channel)
  session.rs         会话管理
  renderer/          内容渲染(HTML → 终端文本)
  store/             SQLite 持久化
    db.rs             连接管理
    cookies.rs        Cookie 存取
    cache.rs          响应缓存
    dismiss.rs        屏蔽条目
    settings.rs       用户设置
  ui/                TUI 层
    render.rs         渲染主入口
    layout.rs         布局计算
    theme.rs          配色方案
    keybindings.rs    键位绑定(支持配置文件覆盖)
    screens/          视图定义(Feed、Answer、Comment、Search)
    widgets/          可复用组件(feed_list、status_bar、title_bar)
  zhihu/             知乎数据层
    client.rs         HTTP 客户端(User-Agent、频率控制、429 重试)
    models.rs         数据模型
    parser.rs         HTML → 文本 / Markdown 转换
    feed.rs           热榜 & 推荐流 API
    answer.rs         回答 & 问题 API
    comment.rs        评论 & 子评论 API
    search.rs         搜索 API
    auth.rs           登录态验证

开发

cargo build                  # 编译 debug 版
cargo build --release        # 编译 release 版(优化体积,LTO + strip)
cargo run                    # 编译并运行
cargo test                   # 运行测试
cargo clippy                 # 静态检查
cargo fmt --all -- --check   # 格式检查

添加新视图

  1. src/ui/screens/ 新建屏幕文件,实现渲染函数
  2. src/app.rsAppMode 枚举中注册新模式
  3. handle_event() 的 match 分支中添加路由
  4. src/ui/render.rs 中添加渲染分发

添加新 API

  1. src/zhihu/ 中添加对应的 Client 方法
  2. 如需新响应格式,在 models.rs 中定义结构体(标注 #[derive(Deserialize)]
  3. 添加 To*() 转换方法,将 API 响应转为内部模型

贡献

欢迎提交 PR。动手之前建议先开 Issue 讨论,避免白费力气。

代码规范:

  • cargo fmt 标准格式化
  • cargo clippy 零警告
  • 公开 API 必须有文档注释(///
  • 错误使用 thiserror 派生或 anyhow::Context 附加上下文
  • 数据库查询用 Option / Result 区分"未找到"和真正的错误
  • 资源(handle、guard)在 Drop 实现中释放
  • 正则表达式提取为 static / Lazy,不在循环内重复编译

提交信息格式:

<type>: <简短描述>

<详细说明(可选)>

类型:feat / fix / refactor / docs / style / test / chore


免责声明

Sylva 是一个个人学习项目,与知乎公司无任何关联。请合理使用,遵守知乎用户协议,不要用于批量抓取或任何破坏性行为。

另外,摸鱼有风险,演技需精湛。

许可

MIT

About

在终端逛逛知乎

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages