签忆 — 收藏管理工具

缘起 · 四小时 Vibe Coding 之旅

刚刚整理浏览器收藏夹时,面对四千多个尘封的书签,我意识到一个问题:收藏了就等于读过了吗? 那些年在各种技术文章、有趣项目、设计灵感页面上点下的「Add to bookmarks」,绝大多数再也没被打开过。浏览器的收藏管理器只是一个冰冷的列表,没有重温,没有发现,没有惊喜。

我想要一个「随机模式」------像 Spotify 的随机播放一样,把我过去的收藏串成一次时光旅行,在摸鱼之际,能够被动回忆一下。可翻了一圈市面上的书签工具,没找到满意的,索性自己写一个。

全程ai编写,代码已全部开源

市面上的收藏工具,为啥不用现成的?

在动手之前,我试了几款,总是差强人意:

浏览器自带书签

缺陷 详情
管理功能原始 只有简陋的文件夹树,没有标签系统,无法多维度组织
无预览 满屏的 favicon + 标题,要找东西只能靠记忆猜标题
无批量操作 想清理重复失效链接?一个一个手动吧
搜索体验差 只搜标题和 URL,搜不了描述和标签
跨浏览器同步玄学 不同浏览器之间迁移书签堪比考古
完全没有"发现"体验 只有主动查找,没有随机重温的功能

其他工具(GitHub 上的开源方案)

GitHub 上确实有不少书签管理项目,但试了一圈发现要么功能太简陋(纯列表展示),要么配置太复杂(要搭数据库、Redis),要么审美不在线。我想要的是一个开箱即用、颜值在线、功能刚刚好的桌面书签管理器。

核心痛点总结

  1. 没有沉浸式的收藏回顾体验

    --- 「随机模式」这个概念在书签工具里几乎是空白

  2. 数据不自由

    --- SaaS 服务把你的收藏锁在数据库里

  3. 配置太重

    --- 一个书签管理工具不应该依赖数据库和容器编排

  4. 审美不在线

    --- 大部分工具仍然停留在功能主义至上的 UI 设计

  5. 无法自由添加功能

    --- 别人的工具,无法随意添加自己的想法

我的答案

所以我写了签忆,它不是一个试图取悦所有人的产品,而是一个解决我自己真实需求的工具:

  • 数据在自己手里

    --- 全部存在浏览器 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

自定义快捷键

点击工具栏键盘图标打开快捷键设置界面:

  • 点击任意快捷键可录制新组合

  • 支持 CtrlShiftAlt 修饰键

  • 自动检测冲突

  • 支持恢复单个或全部默认值


主题切换

双主题系统

通过 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>

获取网页元数据和截图。

流程:

  1. 发送 HTTP 请求获取 HTML

  2. <title><meta> 标签提取标题和描述

  3. 启动 Puppeteer 截图(1280×800,JPEG 80%)

  4. 按 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 是否可访问。

流程:

  1. 发送 HEAD 请求(5 秒超时)

  2. 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

开发说明

添加新视图

  1. src/components/views/ 下创建新组件

  2. BookmarkView.tsx 中添加渲染分支

  3. types/index.tsViewMode 中添加新模式

  4. Toolbar.tsx 中添加切换按钮

添加新快捷键

  1. useKeyboardShortcuts.tsDEFAULT_SHORTCUTS 中添加新 action

  2. useBookmarks / App.tsx 中添加对应的 handler

  3. 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: DENYContent-Security-Policy 限制。系统会自动降级显示截图或渐变色背景。

**Q: 导入 HTML 书签后有些书签没有解析?**A: 导入后自动解析前 10 个书签。其余书签可通过选择文件夹后点击工具栏 PARSE 按钮批量解析。

**Q: 数据会丢失吗?**A: 所有数据存储在浏览器 localStorage 中,清除浏览器数据会导致数据丢失。建议定期导出备份。