缘起 · 四小时 Vibe Coding 之旅
刚刚整理浏览器收藏夹时,面对四千多个尘封的书签,我意识到一个问题:收藏了就等于读过了吗? 那些年在各种技术文章、有趣项目、设计灵感页面上点下的「Add to bookmarks」,绝大多数再也没被打开过。浏览器的收藏管理器只是一个冰冷的列表,没有重温,没有发现,没有惊喜。
我想要一个「随机模式」------像 Spotify 的随机播放一样,把我过去的收藏串成一次时光旅行,在摸鱼之际,能够被动回忆一下。可翻了一圈市面上的书签工具,没找到满意的,索性自己写一个。
全程ai编写,代码已全部开源
市面上的收藏工具,为啥不用现成的?
在动手之前,我试了几款,总是差强人意:
浏览器自带书签
| 缺陷 | 详情 |
|---|---|
| 管理功能原始 | 只有简陋的文件夹树,没有标签系统,无法多维度组织 |
| 无预览 | 满屏的 favicon + 标题,要找东西只能靠记忆猜标题 |
| 无批量操作 | 想清理重复失效链接?一个一个手动吧 |
| 搜索体验差 | 只搜标题和 URL,搜不了描述和标签 |
| 跨浏览器同步玄学 | 不同浏览器之间迁移书签堪比考古 |
| 完全没有"发现"体验 | 只有主动查找,没有随机重温的功能 |
其他工具(GitHub 上的开源方案)
GitHub 上确实有不少书签管理项目,但试了一圈发现要么功能太简陋(纯列表展示),要么配置太复杂(要搭数据库、Redis),要么审美不在线。我想要的是一个开箱即用、颜值在线、功能刚刚好的桌面书签管理器。
核心痛点总结
-
没有沉浸式的收藏回顾体验
--- 「随机模式」这个概念在书签工具里几乎是空白
-
数据不自由
--- SaaS 服务把你的收藏锁在数据库里
-
配置太重
--- 一个书签管理工具不应该依赖数据库和容器编排
-
审美不在线
--- 大部分工具仍然停留在功能主义至上的 UI 设计
-
无法自由添加功能
--- 别人的工具,无法随意添加自己的想法
我的答案
所以我写了签忆,它不是一个试图取悦所有人的产品,而是一个解决我自己真实需求的工具:
-
数据在自己手里
--- 全部存在浏览器 localStorage,一份 JSON 就能备份带走
-
本地优先
--- Node.js + Puppeteer 本地跑解析,没有云服务依赖
-
沉浸式浏览
--- 随机模式 + 3D 卡片 + iframe 网页预览,让收藏夹变成博物馆
-
颜值在线
--- 深色/浅色双主题、玻璃拟态设计、交互动效,每一帧都用心打磨
-
轻量启动
---
npm install && npm run dev,一分钟跑起来 -
自由发挥 ---
有想法AI立马实现
签忆功能介绍
一款功能丰富的浏览器书签管理桌面应用,支持书签解析、分类管理、标签系统、多视图浏览和随机模式。基于 React + TypeScript + Vite + TailwindCSS 构建,后端使用 Puppeteer 进行网页截图与元数据提取。

目录
-
功能概览
-
技术架构
-
快速开始
-
视图模式
-
文件夹管理
-
标签系统
-
书签操作
-
随机模式
-
键盘快捷键
-
主题切换
-
后端 API
-
数据持久化
-
项目结构
功能概览
| 功能 | 说明 |
|---|---|
| 书签解析 | 自动获取网页标题、描述、截图和 favicon |
| 文件夹管理 | 树形结构嵌套、拖拽排序、emoji + 颜色自定义 |
| 标签系统 | 标签分组、颜色标记、筛选过滤 |
| 多视图浏览 | 图标视图、列表视图、详情视图、随机模式 |
| 键盘快捷键 | 16 个预设快捷键,所有组合可自定义 |
| 主题切换 | 深色/浅色双主题,CSS 变量驱动 |
| 失效链接扫描 | 批量检测书签 URL 是否可访问 |
| 导入导出 | 支持 HTML (Netscape) 和 JSON 格式导入 |
技术架构
前端
| 技术 | 用途 |
|---|---|
| React 18 | UI 框架 |
| TypeScript | 类型安全 |
| Vite 5 | 构建工具 |
| TailwindCSS 3 | 样式系统 |
| Lucide React | 图标库 |
| CSS Custom Properties | 双主题系统 |
后端
| 技术 | 用途 |
|---|---|
| Node.js + Express | API 服务器 |
| Puppeteer | 网页截图与元数据提取 |
| MD5 哈希 | 截图缓存 |
数据流
bash
用户操作 → React Hook (useBookmarks) → localStorage 持久化
→ Fetch API → Node 服务器 → Puppeteer 截图
→ HTML 元数据提取
快速开始
bash
# 安装依赖
npm install
# 启动截图 API 服务器(端口 3001)
npm run server
# 启动开发服务器(端口 5173,新终端)
npm run dev
# 构建生产版本
npm run build
访问 http://localhost:5173 即可使用。
Vite 配置了代理规则,
/api/*和/screenshots/*请求会自动转发到端口 3001。
视图模式
四种视图模式可通过工具栏切换,适应不同的浏览习惯。
图标视图(默认)

网格布局展示书签截图卡片,支持四种尺寸:
| 尺寸 | 说明 |
|---|---|
| 小 (small) | 紧凑网格 |
| 中 (medium) | 默认大小 |
| 大 (large) | 大卡片展示 |
| 超大 (extra-large) | 单行较少卡片 |
列表视图

表格视图,支持以下列:
-
标题
--- 书签名称 + favicon
-
网址
--- 完整 URL
-
标签
--- 颜色标签胶囊
-
添加时间
--- 时间戳
-
状态
--- 解析状态指示器
表头固定,支持长列表滚动查看。
详情视图

左右分栏布局:
-
左侧
--- 可滚动的书签列表
-
右侧
--- 选中书签的完整信息面板
详情面板包含:
-
大幅网页截图
-
标题、URL、描述
-
标签列表(可编辑)
-
元数据(创建时间、解析时间)
-
操作按钮(打开链接、刷新解析)
文件夹管理
树形结构
文件夹采用树形嵌套结构,左侧边栏清晰展示层级关系:

操作
| 操作 | 方式 |
|---|---|
| 新建根级分类 | 点击侧栏顶部 + 按钮 |
| 新建子分类 | 悬停文件夹,点击 + 按钮 |
| 重命名 | 悬停文件夹,点击编辑图标(铅笔) |
| 删除 | 悬停文件夹,点击删除图标(垃圾桶) |
| 拖拽排序 | 直接拖拽文件夹到目标位置 |
| 自定义样式 | 点击设置图标,打开文件夹设置 |
文件夹设置

每个文件夹可自定义:
-
Emoji 图标
--- 从 8 个分类 80 个 emoji 中选择,或自定义输入
-
颜色
--- 14 种预设颜色或自定义十六进制颜色
设置后侧栏文件夹图标会显示对应的 emoji 和颜色。
标签系统
功能
-
标签面板
--- 点击工具栏
TAGS按钮打开左侧标签面板 -
标签分组
--- 支持按组组织标签(如"技术"、"工作"、"个人")
-
颜色标记
--- 每个标签可设置独立颜色,便于视觉区分
-
筛选过滤
--- 选择标签后仅显示带该标签的书签
-
自动发现
--- 书签中的新标签会自动出现在标签面板中
标签操作

| 操作 | 说明 |
|---|---|
| 添加标签 | 输入标签名称,自动分配颜色 |
| 编辑标签 | 修改名称、颜色或分组 |
| 删除标签 | 移除标签定义(不影响书签) |
| 标签分组 | 创建/编辑/删除分组管理标签 |
| 筛选 | 点击标签切换筛选状态 |
书签操作
添加书签

-
点击工具栏
ADD按钮 -
输入 URL(自动补全
https://) -
选择目标文件夹
-
自动触发解析(获取标题、截图、描述)
编辑书签
右键书签 → 编辑,或选中后按 F2:
可修改字段:
-
标题
-
URL(修改后自动重新解析)
-
描述
-
标签(支持自动补全)
-
文件夹(支持树形选择)
右键菜单

书签右键菜单:
-
打开链接
-
重新解析(刷新截图和元数据)
-
编辑
-
移动到...
-
复制链接
-
删除
背景右键菜单:
-
添加网址
-
新建分类
-
全选 / 清除选择
-
解析全部
-
扫描失效链接
导入书签

-
点击工具栏
IMPORT按钮打开导入界面 -
支持 HTML(Netscape 书签格式)和 JSON 格式
-
可直接拖拽文件到导入区域
-
导入前预览书签树结构,按文件夹名称智能合并
-
导入后自动解析前 10 个书签的元数据
-
其余书签可通过
PARSE按钮批量解析
批量操作
-
全选
---
Ctrl+A -
解析全部
--- 工具栏
PARSE按钮(仅处理待解析的书签) -
扫描失效
--- 工具栏
SCAN按钮,检测所有书签 URL 有效性 -
批量删除
--- 选中后点击工具栏
DELETE
随机模式
随机模式是一种沉浸式的书签浏览体验。

特性
| 特性 | 说明 |
|---|---|
| 3D 倾斜 | 鼠标移动时卡片产生 ±10° 的 3D 旋转效果 |
| 浮动动画 | 卡片持续上下浮动(animate-float) |
| Iframe 预加载 | 直接嵌入网页进行实时预览 |
| 自动轮播 | 可按 2s / 5s / 10s / 30s 间隔自动切换 |
| 键盘控制 | Space 切换下一个,Esc 退出 |
操作
| 操作 | 方式 |
|---|---|
| 进入随机模式 | 点击工具栏 RANDOM 按钮 |
| 切换书签 | 点击 NEXT 按钮或按 Space |
| 退出随机模式 | 点击 EXIT 按钮或按 Esc |
| 调整速度 | 点击速度指示器(如 5s),选择预设间隔 |
| 打开链接 | 点击卡片下方的 URL 链接 |
显示内容
-
网页预览
--- 通过 sandboxed iframe 加载网页,支持脚本运行和表单交互
-
标题
--- 书签标题(带文字阴影)
-
URL
--- 可点击打开
-
标签
--- 最多显示 5 个,超出显示
+N -
创建时间
--- 中文格式显示
-
状态徽章
--- 解析中 / 错误提示
部分网站因
X-Frame-Options限制无法在 iframe 中加载,此时自动降级显示截图或渐变色背景。
键盘快捷键
支持 16 个预设快捷键,所有组合可自定义。

默认快捷键
| 分类 | 操作 | 快捷键 |
|---|---|---|
| 选择 | 全选 | Ctrl+A |
| 选择 | 删除选中 | Delete |
| 选择 | 清除选择 | Escape |
| 导航 | 上一个 | ArrowUp |
| 导航 | 下一个 | ArrowDown |
| 导航 | 打开链接 | Enter |
| 视图 | 图标视图 | Ctrl+1 |
| 视图 | 列表视图 | Ctrl+2 |
| 视图 | 详情视图 | Ctrl+3 |
| 视图 | 切换标签面板 | Ctrl+T |
| 操作 | 复制 URL | Ctrl+C |
| 操作 | 复制标题 + URL | Ctrl+Shift+C |
| 操作 | 粘贴为书签 | Ctrl+V |
| 操作 | 添加书签 | Ctrl+D |
| 操作 | 搜索 | Ctrl+F |
| 操作 | 重命名 | F2 |
自定义快捷键
点击工具栏键盘图标打开快捷键设置界面:
-
点击任意快捷键可录制新组合
-
支持
Ctrl、Shift、Alt修饰键 -
自动检测冲突
-
支持恢复单个或全部默认值
主题切换
双主题系统

通过 CSS 自定义属性实现深色/浅色双主题:
bash
[data-theme="dark"]
{
--color-surface
:
#0f0f0f
;
--color-dark-900
:
#0a0a0a
;
/* ... */
}
[data-theme="light"]
{
--color-surface
:
#ffffff
;
--color-dark-900
:
#f8f8f8
;
/* ... */
}
-
切换方式:点击工具栏太阳/月亮图标
-
持久化:主题偏好保存在
localStorage -
过渡动画:所有颜色变化有 0.4s 的平滑过渡
Glass Morphism
界面大量使用毛玻璃效果:
bash
glass-card → 背景模糊 + 边框
glass-input → 输入框聚焦发光
glass-dropdown → 下拉菜单弹出动画
glass-toolbar → 顶部工具栏
glass-statusbar → 底部状态栏
glass-panel → 侧栏面板
后端 API
API 服务器运行在 http://localhost:3001。
GET /api/parse?url=<encoded_url>
获取网页元数据和截图。
流程:
-
发送 HTTP 请求获取 HTML
-
从
<title>和<meta>标签提取标题和描述 -
启动 Puppeteer 截图(1280×800,JPEG 80%)
-
按 URL 的 MD5 哈希缓存截图
响应:
bash
{
"title"
:
"页面标题"
,
"description"
:
"页面描述"
,
"screenshot"
:
"/screenshots/a1b2c3d4.jpg"
,
"favicon"
:
"https://www.google.com/s2/favicons?domain=example.com&sz=32"
}
GET /api/check?url=<encoded_url>
检测 URL 是否可访问。
流程:
-
发送 HEAD 请求(5 秒超时)
-
HEAD 失败则回退到 GET 请求
响应:
bash
{
"valid"
:
true
,
"status"
:
200
}
GET /screenshots/:filename
提供缓存的截图文件。
- 缓存头:
Cache-Control: public, max-age=86400
数据持久化
所有数据存储在浏览器 localStorage 中:
| Key | 内容 |
|---|---|
bookmark-parser-data |
文件夹树 + 书签数据 |
bookmark-parser-view-settings |
视图偏好(模式、排序、列配置) |
bookmark-parser-tags |
标签定义 |
bookmark-parser-tag-groups |
标签分组 |
bookmark-parser-shortcuts |
自定义快捷键 |
bookmark-parser-theme |
主题偏好 |
sidebarWidth |
侧栏宽度 |
数据格式为 JSON 序列化,加载时通过 sanitizeTree() 进行数据清洗和兼容性处理。
项目结构
bash
bookmark-parser/
├── src/
│ ├── App.tsx # 主应用组件
│ ├── main.tsx # 入口
│ ├── index.css # 全局样式 + 双主题
│ ├── types/
│ │ └── index.ts # 所有 TypeScript 类型
│ ├── hooks/
│ │ ├── useBookmarks.ts # 书签/文件夹状态管理
│ │ ├── useTags.ts # 标签系统
│ │ └── useKeyboardShortcuts.ts # 快捷键系统
│ ├── components/
│ │ ├── Sidebar.tsx # 文件夹树 + 拖拽
│ │ ├── BookmarkCard.tsx # 书签卡片
│ │ ├── BookmarkList.tsx # 书签列表容器
│ │ ├── BookmarkDetail.tsx # 书签详情
│ │ ├── AddBookmarkModal.tsx # 添加书签
│ │ ├── EditBookmarkModal.tsx # 编辑书签
│ │ └── ImportBookmarkModal.tsx # 导入书签
│ │ ├── layout/
│ │ │ ├── Toolbar.tsx # 顶部工具栏
│ │ │ ├── StatusBar.tsx # 底部状态栏
│ │ │ └── ContextMenu.tsx # 右键菜单
│ │ ├── views/
│ │ │ ├── BookmarkView.tsx # 视图路由
│ │ │ ├── IconView.tsx # 图标视图
│ │ │ ├── ListView.tsx # 列表视图
│ │ │ ├── DetailView.tsx # 详情视图
│ │ │ └── RandomView.tsx # 随机模式
│ │ ├── tags/
│ │ │ ├── TagPanel.tsx # 标签面板
│ │ │ ├── TagPicker.tsx # 标签选择器
│ │ │ └── TagColorPicker.tsx # 标签颜色选择
│ │ ├── folder-settings/
│ │ │ ├── FolderSettingsModal.tsx # 文件夹设置
│ │ │ ├── ColorPicker.tsx # 颜色选择器
│ │ │ └── EmojiPicker.tsx # Emoji 选择器
│ │ └── shortcuts/
│ │ ├── ShortcutCheatsheet.tsx # 快捷键速查
│ │ └── ShortcutSettingsModal.tsx # 快捷键设置
├── server/
│ └── index.js # Express API 服务器
├── scripts/
│ └── take-screenshots.mjs # 截图脚本(文档用)
├── docs/
│ └── screenshots/ # 文档截图
├── vite.config.js
├── tailwind.config.js
├── tsconfig.json
└── package.json
开发说明
添加新视图
-
在
src/components/views/下创建新组件 -
在
BookmarkView.tsx中添加渲染分支 -
在
types/index.ts的ViewMode中添加新模式 -
在
Toolbar.tsx中添加切换按钮
添加新快捷键
-
在
useKeyboardShortcuts.ts的DEFAULT_SHORTCUTS中添加新 action -
在
useBookmarks/App.tsx中添加对应的 handler -
在
ShortcutCheatsheet.tsx中确认显示
主题自定义
修改 src/index.css 中的 CSS 变量:
bash
[data-theme="dark"]
{
--color-accent-amber
:
#f59e0b
;
--color-surface
:
#0f0f0f
;
/* ... */
}
或修改 tailwind.config.js 中的颜色定义。
常见问题
**Q: 截图不显示?**A: 确保 npm run server 已启动,Puppeteer 可能需要首次下载 Chromium。
**Q: 某些网站无法在随机模式下预览?**A: 目标网站可能设置了 X-Frame-Options: DENY 或 Content-Security-Policy 限制。系统会自动降级显示截图或渐变色背景。
**Q: 导入 HTML 书签后有些书签没有解析?**A: 导入后自动解析前 10 个书签。其余书签可通过选择文件夹后点击工具栏 PARSE 按钮批量解析。
**Q: 数据会丢失吗?**A: 所有数据存储在浏览器 localStorage 中,清除浏览器数据会导致数据丢失。建议定期导出备份。