你的LeetCode数据属于你自己:一个完全离线的DSA进度追踪器,连一次云都不上
大家好,我是提米哥,提米大门的首席选品官。今天要聊的这个开源项目,让我这个常年跟工具打交道的“老油条”都眼前一亮——它解决了一个大家心知肚明但很少有人捅破的问题:你的刷题数据,到底归谁?
你有没有半夜两点刷LeetCode题,刚Accepted就暗自庆幸?你有没有用过那些号称“免费”的刷题追踪工具?醒醒吧,你的解题历史、薄弱知识点、连续天数、面试前的复习节奏——这些数据全都在别人服务器上,人家拿去“优化产品”(翻译:训练模型、卖情报、投广告)。
我自己就踩过坑:同时用三个工具,一个要Google登录,一个自动同步到“云”且不给导出,第三个直接跑路了——顺带带走了我半年的刷题记录。那一刻我决定:不玩了,我要自己造一个只属于我自己的追踪器。
这个项目叫 My DSA Buddy,一个完全跑在 localhost 上的 LeetCode 仪表盘。没有账号、没有云、没有埋点。你的数据躺在你本机的 SQLite 文件里,唯一一次网络请求是把你的解法推到你自己的 GitHub 仓库。仅此而已。
外部网络请求次数:1(推送到你自己的 GitHub)
远程存储的数据:0 字节
分析/追踪:无
第三方数据依赖:零
就这么硬核。
它怎么工作?
两个本地模块对话:
- 本地网页仪表盘(Astro 5 + React 19),跑在
localhost:4321 - Chrome 扩展,静默检测 LeetCode 上的“Accepted”提交
你做完一道题,扩展自动抓取解法、推到你 GitHub,再往本地仪表盘发个信号更新数据。整个仪表盘根本不上网。
给技术控的架构图
┌───────────┐ ┌──────────┐ ┌────────────┐ ┌───────────┐
│ LeetCode │ │ 内容脚本 │ │ 后台任务 │ │ 本地仪表盘 │
│ 页面DOM │ │ (content) │ │ (worker) │ │ (dashboard)│
└─────┬─────┘ └─────┬────┘ └─────┬──────┘ └─────┬─────┘
│ │ │ │
│ "Accepted" │ │ │
│─────────────>│ │ │
│ │ sendMessage │ │
│ │────────────>│ │
│ │ │ │
│ │ │─ GitHub API ─>│(唯一外部调用)
│ │ │ PUT 解法 │
│ │ │<─ 201 ────────│
│ │ │ │
│ │ │ POST /api/sync│
│ │ │──────────────>│
│ │ │ │ 更新 SQLite
│ │ │ │ 重新统计
│ │ │<──────────────│ 200 OK
技术栈一览(无表格版)
- 框架:Astro 5(SSR,Node 适配器)—— 只有交互部分才水合,性能优秀
- UI:React 19 孤岛组件 —— 每个仪表盘小部件独立驱动
- 样式:Tailwind CSS v4.0 —— 工具类优先,零运行时
- 数据库:better-sqlite3 —— 单文件,零配置,读取超快
- 数据导入:csv-parse —— 从 CSV 加载按公司分类的题目列表
- 扩展:Vite + CRXJS(Manifest V3)—— 现代化的 Chrome 扩展构建
- 类型安全:TypeScript 5.9 全链路 —— 仪表盘和扩展共享类型
- 包管理:pnpm + Turborepo —— 共享代码,并行构建,缓存
- 动画:Motion(原 Framer Motion)—— 流畅的组件过渡
数据结构:一个 SQLite 文件搞定
-- 建立解题记录表
CREATE TABLE solved_problems (
id INTEGER PRIMARY KEY AUTOINCREMENT, -- 自增主键
problem_id INTEGER NOT NULL UNIQUE, -- LeetCode 题目编号(唯一)
title TEXT NOT NULL, -- 题目标题
difficulty TEXT NOT NULL CHECK(difficulty IN ('Easy', 'Medium', 'Hard')), -- 难度
topics TEXT, -- 所属标签,JSON 数组
companies TEXT, -- 所属公司,JSON 数组
solved_at TEXT NOT NULL, -- 解题时间
language TEXT, -- 编程语言
solution TEXT, -- 代码解法
github_path TEXT -- 在 GitHub 上的路径
);
-- 每日进度表
CREATE TABLE daily_progress (
date TEXT PRIMARY KEY, -- 日期,主键
problems_solved INTEGER DEFAULT 0, -- 当日解题数
time_spent INTEGER DEFAULT 0, -- 耗时(分钟)
problem_ids TEXT -- 题目 ID 列表,JSON 数组
);
没有 ORM,没有迁移脚本,就是一个 .db 文件——你可以复制、备份、甚至直接删掉。你的数据,你说了算。
设计:像翻开一本程序员手账
我不想要又一个灰色调的 SaaS 仪表盘。所以整个设计致敬 Claude.com 的美学:暖白画布、珊瑚色点缀、衬线字体。但又贴了开发者的标签:
- 奶油色画布 (#faf9f5) —— 温润有质感,不是那种冷冰冰的灰色
- 珊瑚色点缀 (#cc785c) —— 进度条、连续天数、已解决徽章
- 深海军蓝表面 (#181715) —— 代码块、题目卡片、统计面板
- Cormorant Garamond 衬线字体用于标题 —— “手账”氛围
- JetBrains Mono 突出显示统计数字、ID、代码 —— “开发者”信号
- 全暗黑模式,切换时带圆形过渡动画
最终效果:不像一个杂乱的统计工具,更像一本精心整理的刷题笔记。
/* 设计系统的 CSS 变量,加中文注释 */
@theme {
--color-canvas: #faf9f5; /* 画布底色:暖白 */
--color-primary: #cc785c; /* 主色:珊瑚 */
--color-surface-dark: #181715; /* 深色表面:深海军蓝 */
--color-difficulty-easy: #5db872; /* 简单题:绿色 */
--color-difficulty-medium: #d4a017; /* 中等题:金色 */
--color-difficulty-hard: #c64545; /* 困难题:红色 */
}
目前已完成的
- 完整设计系统(CSS 变量、暗黑模式、组件类、动画)
- 完整 TypeScript 类型定义(Problem、Roadmap、Progress、Settings、SyncPayload)
- 单仓架构:仪表盘和扩展共享代码
- Chrome 扩展骨架(Manifest V3,内容脚本、后台任务、弹出窗口)
- 基础布局和首页
- 扩展与仪表盘的通信协议
正在进行的
- 仪表盘小部件(ProgressOverview、StreakCalendar、StatsPanel)
- SQLite 数据库层和 API 路由
- 带过滤的题目列表(难度、标签、公司、状态)
- 按公司分类的路线图卡片
后续计划
- 完整的扩展 DOM 提取(LeetCode 爬取)
- GitHub 推送集成
- 设置页面(PAT 配置、主题、同步偏好)
- 数据导出/导入
- 连续天数追踪和里程碑徽章
这就是你需要登场的地方。
为什么这么早就开源?
因为最好的开源项目不是一个人闭门造出来的,而是从第一天就有社区一起塑造。我现在把架构和设计系统放出来,就是想找到能一起打磨的人:
- 在 UX 锁定前给出反馈
- 带来领域专长(LeetCode DOM 结构、Chrome 扩展最佳实践、SQLite 优化)
- 挑战我的决策(比如:扩展本地缓存用 IndexedDB 还是 SQLite?Astro 是不是过重了?连续算法该不该改?)
适合新手的任务(无表格版)
- 构建 ProgressRing SVG 组件:难度易,技能要求 React + SVG
- 实现连续天数日历(365天热力图):难度中,技能要求 React + CSS Grid/SVG
- 编写 SQLite 初始化脚本:难度易,技能要求 SQL + Node.js
- 构建
/api/sync端点:难度中,技能要求 Astro API 路由 + better-sqlite3 - 实现 LeetCode DOM 提取(内容脚本):难度难,技能要求 DOM API + Chrome 扩展
- 添加 CSV 题目列表(Google, Amazon, Meta 等):难度易,技能要求数据收集
- 构建带滚动动画的浮动导航栏:难度中,技能要求 React + CSS 过渡
- 用 View Transition API 实现暗黑模式切换:难度中,技能要求 React + CSS + Web API
我想问社区的几个问题
-
SQLite 还是 IndexedDB 作为扩展的本地缓存?
当前仪表盘用 better-sqlite3,但扩展自己也可能需要缓存。用 Dexie.js 的 IndexedDB 多一份复杂度,但值不值得?还是说扩展干脆直接 ping localhost? -
“唯一一次网络请求”这条约束是不是太死?
目前只允许推送 GitHub。但如果想要可选功能,比如从 LeetCode API 拉取题目元数据,或者可选云备份,这些是否应该作为“可选”加入?加入后会不会稀释隐私承诺? -
Astro SSR 跑在 localhost 上是不是杀鸡用牛刀?
我选 Astro 是因为孤岛架构和服务端数据加载。但对于纯本地应用,一个 Vite + React SPA 再加一个 Express API 会不会更简单?你的看法呢? -
什么功能能让你从现在的追踪工具切换过来?
认真问——什么东西能让你每天都愿意打开这个工具?
快速上手
# 克隆并本地运行
git clone https://github.com/Ritanjit/My_DSA_Buddy.git
cd my-dsa-buddy
pnpm install
pnpm dev
# 仪表盘跑在 localhost:4321
# 扩展构建目录在 apps/chrome-extension/dist/
链接
直达网址:https://github.com/Ritanjit/My_DSA_Buddy.git
项目 MIT 协议,Issues 区欢迎吐槽。如果你也曾为数据归属发过愁,给个 star 或者挑个 issue 提个 PR。架构就位,设计就位,只差你了。
“隐私不是一个功能,而是一种权利。你的 DSA 刷题之路不应该以放弃它为代价。”
不管你是提交代码、在评论区丢个建议还是只点个 star,都是帮助。咱们一起造一个默认就尊重开发者数据的工具,而不是事后才想起来补救。
用 Astro 5、React 19、TypeScript、Tailwind CSS v4.0、better-sqlite3 以及对“免费”云服务的一丝不信任构建。
