极简主义者的个人知识管理系统 - 不仅是笔记,更是你的第二大脑
SumuNote 是一个现代化的 Web 笔记应用,专注于极简设计、流畅体验和强大的知识管理能力。支持双向链接、Markdown 编辑、文件夹组织、数据统计等功能,让笔记管理变得简单而高效。
- Markdown 编辑器:实时预览、语法高亮、自动保存、分段编辑(文本 + 表格)
- 文件夹组织:多层级文件夹管理,清晰分类
- 笔记操作:创建、编辑、删除、复制、重命名、置顶
- 撤销/重做:支持 Ctrl/Cmd + Z 撤销、Ctrl+Y/Cmd+Shift+Z 重做(多步)
- 图片上传:支持图片上传到 Supabase Storage
- 发布分享:一键发布笔记到 Web,生成公开链接
- 标签系统:为笔记添加多个标签(如
#项目、#想法),支持标签筛选与搜索
- 大纲结构:无限层级的节点树,支持任意深度的父子关系
- 富文本格式化:支持加粗(
**文本**)、高亮(==文本==) - 快捷键操作:
- 桌面端:Tab(创建子节点)、Enter(创建同级节点)、Shift+Tab(提升层级)、Alt+.(展开/折叠全部)
- 移动端:长按多选、拖拽改变层级、工具栏操作
- 拖拽重排:流畅的拖拽体验,支持改变节点层级和顺序
- 文档内嵌:在节点中插入其他思维笔记(
[[mind_note_id|显示名称]]) - 文件夹支持:思维笔记支持文件夹组织,与文本笔记共享文件夹系统
- 长按多选:长按文件夹或思维笔记进入多选模式,支持批量操作(重命名、移动、删除)
- Dock 工具栏:多选模式下显示操作工具栏,支持拖拽到 Dock 执行操作
- Markdown 链接语法:支持
[[笔记标题]]和[[noteId|显示名称]]语法 - 自动链接渲染:
[[...]]自动转换为可点击的内部链接 - 反向链接(Backlinks):自动追踪哪些笔记引用了当前笔记,并显示上下文预览
- 自动补全:输入
[[时触发智能补全,快速插入笔记链接
- 写作热力图:可视化展示一年内的写作活跃度
- 统计仪表盘:总笔记数、本周新增、总字数、活跃天数等
- 文件夹分布:饼图展示不同文件夹的笔记分布情况
- 最近编辑:快速访问最近更新的笔记
- 拖拽交互:支持拖拽笔记进行批量操作(删除、复制、置顶等)
- 多选模式:长按或点击进入多选模式,批量管理笔记
- 最近打开:Dashboard 展示最近打开的笔记,快速回到上次编辑内容
- 搜索功能:
- 列表内搜索:按标题、内容和标签搜索当前文件夹内的笔记
- 全局搜索:在 Dashboard 顶部对所有笔记做全文搜索(标题 / 内容 / 标签)
- 搜索结果预览:点击搜索结果进入只读预览页面,支持 Markdown 渲染、内容复制
- 搜索高亮:自动高亮所有匹配的搜索词,支持快捷键导航(
Ctrl+G/Ctrl+Shift+G)
- 回收站:删除的笔记可恢复,支持彻底删除
- 暗色模式:支持亮色/暗色主题切换
- ZEN 专注模式:编辑页面一键进入/退出专注模式,放大编辑区域、隐藏部分干扰元素
- 长文目录:包含 H1/H2/H3 的笔记可在「更多」菜单中打开目录,快速跳转到对应标题
- 表格增强:可视化表格编辑、列宽拖拽、首列冻结(预览与编辑一致)
- 长文优化:超长笔记支持虚拟滚动/懒加载,滚动与编辑更流畅
- 离线首屏:优先展示本地缓存内容,弱网/离线也能快速打开
- 邮箱密码登录:传统邮箱+密码登录/注册
- OAuth 登录:支持 Google 和 Apple 一键登录
- 自动登录:近期登录用户自动恢复会话,无需重复输入密码
- 会话持久化:Supabase 自动管理 session,保持登录状态
- 数据导出:支持导出所有笔记为 ZIP 文件备份
- 云端同步:基于 Supabase 的实时数据同步
- 实时同步:使用 Supabase Realtime 实现多端实时同步,检测到云端更新时自动保存本地更改到版本历史,然后加载云端最新版本
- 版本历史:每次保存笔记时自动创建版本快照,保留最近 50 个版本,支持版本恢复
- 离线支持:支持离线编辑,自动保存到本地 IndexedDB,网络恢复后自动同步到云端
- 数据安全:企业级数据存储,用户数据私有化
- Next.js 16 - React 框架,App Router
- React 19 - UI 库
- TypeScript - 类型安全
- Tailwind CSS 4 - 实用优先的 CSS 框架
- Radix UI - 无样式、可访问的 UI 组件
- Lucide React - 图标库
- next-themes - 主题切换
- Supabase - 后端即服务(BaaS)
- PostgreSQL 数据库
- 身份认证(Auth)
- 对象存储(Storage)
- react-markdown - Markdown 渲染
- recharts - 数据可视化图表
- @dnd-kit/core - 拖拽交互
- date-fns - 日期处理
- jszip + file-saver - 数据导出
- localforage - 离线存储(IndexedDB)
- Vitest - 测试框架
- ESLint - 代码检查
- Testing Library - React 组件测试
sumu-note/
├── src/
│ ├── app/ # Next.js App Router 页面
│ │ ├── page.tsx # 首页(登录页)
│ │ ├── dashboard/ # 仪表盘
│ │ │ ├── mind-notes/ # 思维笔记列表页
│ │ │ └── mind-notes/[id]/ # 思维笔记编辑页
│ │ ├── notes/[id]/ # 笔记详情页
│ │ └── auth/callback/ # OAuth 回调
│ ├── components/ # React 组件
│ │ ├── AuthModal.tsx # 登录/注册弹窗
│ │ ├── FolderManager.tsx # 文件夹管理
│ │ ├── NoteManager.tsx # 笔记管理
│ │ ├── MarkdownRenderer.tsx # Markdown 渲染(支持双向链接、标题锚点)
│ │ ├── NoteEditor.tsx # 笔记编辑页(标题、标签、SegmentedEditor、目录等)
│ │ ├── SegmentedEditor.tsx # 分段编辑器(文本段 + 表格段)
│ │ ├── MindNoteManager.tsx # 思维笔记管理(列表页)
│ │ ├── MindNoteEditor.tsx # 思维笔记编辑器
│ │ ├── MindNode.tsx # 思维笔记节点组件
│ │ ├── DraggableMindNode.tsx # 可拖拽节点组件
│ │ ├── MindNodeToolbar.tsx # 移动端工具栏
│ │ └── MindNodeContent.tsx # 节点内容渲染
│ └── lib/ # 工具函数
│ ├── supabase.ts # Supabase 客户端
│ ├── stats.ts # 统计数据逻辑
│ ├── export-utils.ts # 导出功能
│ ├── version-history.ts # 版本历史管理
│ ├── offline-storage.ts # 离线存储管理
│ ├── mind-note-storage.ts # 思维笔记数据存储
│ ├── mind-note-utils.ts # 思维笔记工具函数
│ └── outline-utils.ts # 目录解析(H1/H2/H3 提取,用于长文跳转)
├── docs/ # 文档
│ ├── sql/ # SQL 脚本
│ │ ├── create_mind_notes_tables.sql
│ │ └── add_folder_support_to_mind_notes.sql
│ └── MIND_NOTE_FEATURE.md # 思维笔记功能文档
├── tests/ # 测试文件
└── public/ # 静态资源
- Node.js 18+
- npm / yarn / pnpm / bun
npm install
# 或
yarn install
# 或
pnpm install在项目根目录创建 .env.local 文件:
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_keynpm run dev
# 或
yarn dev
# 或
pnpm dev
# 或
bun dev打开 http://localhost:3000 查看应用。
npm run build
npm startnpm test # 运行测试
npm test:ui # 测试 UI
npm test:coverage # 测试覆盖率- 将代码推送到 GitHub
- 在 Vercel 导入项目
- 配置环境变量(
NEXT_PUBLIC_SUPABASE_URL和NEXT_PUBLIC_SUPABASE_ANON_KEY) - 重要:确保 Git 提交作者在 Vercel 项目中有访问权限(团队成员)
- 部署完成
遇到部署权限问题? 查看 Vercel 部署问题解决方案
项目基于 Next.js,可以部署到任何支持 Node.js 的平台:
- Netlify
- Railway
- Render
- 自建服务器
- 标题下元信息行:在标题下方弱化展示更新时间(如「更新于 3月5日」)和标签(
#tag1 #tag2),小字号、不抢正文注意力 - 长文目录/大纲:当笔记包含 H1/H2/H3 标题时,在「更多」菜单中提供「目录」入口,点击可展开目录列表并平滑滚动到对应标题
- 聚焦态与弱化边框:去掉标题与正文的默认粗边框,通过
focus-within微弱底色/左边框表达可编辑,光标可见且稳定 - Placeholder 与首次轻提示:标题 placeholder「写个标题」、正文「向下输入正文,输入 / 可插入内容…」;移动端首次进入编辑态时显示一次轻提示(localStorage 标记,仅一次)
- Markdown 渲染:只读预览页面使用 Markdown 渲染,支持完整的 Markdown 语法
- 内容可复制:支持选择和复制渲染后的内容,提升可用性
- 搜索高亮:从搜索结果进入时,自动高亮所有匹配的搜索词
- 自动定位:自动滚动到第一个匹配项,确保用户能看到相关内容
- 匹配项导航:支持
Ctrl+G/Cmd+G(下一个)、Ctrl+Shift+G/Cmd+Shift+G(上一个)快捷键导航 - 可视化导航:提供导航按钮,显示当前匹配项位置(如 "2/5")
- 编辑功能:提供"编辑"按钮,可快速切换到编辑模式
- 大纲编辑器:类似 Workflowy/Roam Research 的无限层级节点结构
- 富文本格式化:支持加粗(
**文本**)和高亮(==文本==) - 快捷键系统:
- 桌面端:Tab(创建子节点)、Enter(创建同级节点)、Shift+Tab(提升层级)、Alt+.(展开/折叠全部)
- 移动端:长按多选、拖拽改变层级、工具栏操作
- 拖拽重排:流畅的拖拽体验,支持改变节点层级和顺序
- 文档内嵌:在节点中插入其他思维笔记(
[[mind_note_id|显示名称]]) - 文件夹支持:思维笔记支持文件夹组织,与文本笔记共享文件夹系统
- 长按多选:文件夹和思维笔记都支持长按(500ms)进入多选模式
- Dock 工具栏:多选模式下显示操作工具栏(重命名、移动、删除),支持拖拽到 Dock 执行操作
- Google 登录:一键使用 Google 账号登录/注册
- Apple 登录:支持 Apple ID 登录(需配置)
- 登录体验优化:保留密码登录的同时,提供更便捷的第三方登录选项
- Wiki-style 链接语法:支持
[[笔记标题]]和[[noteId|显示名称]] - 反向链接(Backlinks):自动展示引用当前笔记的其他笔记列表
- 智能自动补全:输入
[[时触发笔记标题补全,支持模糊搜索
- 会话持久化:近期登录用户访问时自动恢复登录状态
- 无需重复输入:Supabase 自动管理 session,提升用户体验
- 修复 Next.js 14 动态路由参数使用方式
- 修复 Markdown 渲染中的 HTML 结构问题(hydration 错误)
- 修复 Vercel 构建时的类型错误和 Suspense 边界问题
-
标签系统(Tags)
- 支持在笔记编辑页为笔记添加多个标签(输入后回车即可添加,点击标签上的叉号可移除)
- 标签保存到
notes.tags字段,并在笔记卡片上以#tag形式展示 - 列表内搜索与全局搜索均会匹配标签内容
-
ZEN 专注模式
- 编辑页工具栏新增 ZEN 按钮:进入专注模式时放大编辑区域(
max-w-5xl)、隐藏部分工具按钮,仅保留撤回、专注、预览 - 适合长文写作或需要专注时使用,再次点击即可退出
- 编辑页工具栏新增 ZEN 按钮:进入专注模式时放大编辑区域(
-
全局全文搜索
- Dashboard 顶部新增全局搜索框(桌面端可见)
- 支持在所有笔记中按标题、内容、标签进行模糊搜索,并以列表形式展示结果
- 点击搜索结果进入只读预览页面,支持 Markdown 渲染、内容复制
- 自动高亮所有匹配的搜索词,并自动定位到第一个匹配项
- 支持快捷键导航:
Ctrl+G/Cmd+G(下一个)、Ctrl+Shift+G/Cmd+Shift+G(上一个) - 提供可视化导航按钮,显示当前匹配项位置(如 "2/5")
- 自动版本快照:每次保存笔记时自动创建版本历史,无需手动操作
- 版本管理:自动保留最近 50 个版本,旧版本自动清理以节省存储空间
- 版本恢复:支持查看和恢复任意历史版本(UI 功能待完善)
- 数据安全:版本历史存储在独立的
note_versions表中,支持 RLS 权限控制
-
实时同步(Realtime Sync)
- 使用 Supabase Realtime 监听笔记变化,多端同时编辑时自动检测云端更新
- 智能冲突处理:检测到云端更新时提示用户选择
- 保留我的更改:使用本地内容覆盖云端
- 查看最新内容:加载云端最新版本
- 有未保存更改时,提供二次确认对话框
- 支持"保存后刷新":先将本地更改保存到版本历史,再加载云端版本
- 支持"不保存,直接刷新":丢弃本地更改,直接加载云端版本
- 无需手动刷新页面即可看到其他设备的更新
-
离线支持(Offline Support)
- 智能网络检测:自动检测网络状态,即使
navigator.onLine不准确也能通过错误捕获正确判断 - 离线编辑:网络断开时自动保存到本地 IndexedDB,编辑体验不受影响
- 自动同步:网络恢复时自动同步离线更改到云端
- 状态指示:编辑器工具栏显示离线状态图标,清晰提示当前网络状态
- 数据持久化:使用 localforage 封装 IndexedDB,确保离线数据不丢失
- 智能网络检测:自动检测网络状态,即使
- 大纲编辑器:类似 Workflowy/Roam Research 的无限层级节点结构
- 富文本格式化:支持加粗(
**文本**)和高亮(==文本==) - 快捷键系统:
- 桌面端:Tab(创建子节点)、Enter(创建同级节点)、Shift+Tab(提升层级)、Alt+.(展开/折叠全部)
- 移动端:长按多选、拖拽改变层级、工具栏操作
- 拖拽重排:使用
@dnd-kit实现流畅的拖拽体验,支持改变节点层级和顺序 - 文档内嵌:在节点中插入其他思维笔记(
[[mind_note_id|显示名称]]),支持跳转和预览 - 文件夹组织:思维笔记支持文件夹管理,与文本笔记共享文件夹系统
- 长按多选:文件夹和思维笔记都支持长按(500ms)进入多选模式
- Dock 工具栏:多选模式下显示操作工具栏(重命名、移动、删除),支持拖拽到 Dock 执行操作
- 在 Google Cloud Console 创建 OAuth 2.0 客户端 ID
- 配置重定向 URI:
https://你的项目ID.supabase.co/auth/v1/callback - 在 Supabase Dashboard → Authentication → Providers → Google 中启用并填入 Client ID 和 Secret
详细配置步骤请查看项目文档或 Supabase 官方文档。
- 需要 Apple Developer 账号($99/年)
- 在 Apple Developer 创建 App ID 和 Service ID
- 配置回调 URL
- 在 Supabase Dashboard 中启用 Apple Provider 并填入凭据
实时同步功能使用 Supabase Realtime 实现多端同步。要启用此功能:
- 登录 Supabase Dashboard
- 选择你的项目
- 进入 Database → Replication 页面
- 找到
notes表,点击右侧的开关启用 Realtime
或者使用 SQL:
-- 启用 notes 表的 Realtime
ALTER PUBLICATION supabase_realtime ADD TABLE notes;运行以下 SQL 查询验证 Realtime 已启用:
SELECT * FROM pg_publication_tables WHERE pubname = 'supabase_realtime' AND tablename = 'notes';如果返回一行数据,说明设置成功。
版本历史功能需要在 Supabase 数据库中创建 note_versions 表:
- 登录 Supabase Dashboard
- 选择你的项目
- 进入 SQL Editor
- 执行以下 SQL 脚本(见
docs/sql/create_note_versions.sql)
-- 创建 note_versions 表
CREATE TABLE note_versions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
note_id UUID NOT NULL REFERENCES notes(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
title TEXT,
content TEXT,
tags TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- 创建索引
CREATE INDEX idx_note_versions_note_id ON note_versions(note_id);
CREATE INDEX idx_note_versions_created_at ON note_versions(created_at DESC);
-- 启用 RLS
ALTER TABLE note_versions ENABLE ROW LEVEL SECURITY;
-- 创建 RLS 策略
CREATE POLICY "Users can view their own note versions"
ON note_versions FOR SELECT
USING (auth.uid() = user_id);
CREATE POLICY "Users can insert their own note versions"
ON note_versions FOR INSERT
WITH CHECK (auth.uid() = user_id);
CREATE POLICY "Users can delete their own note versions"
ON note_versions FOR DELETE
USING (auth.uid() = user_id);版本历史表会自动保留最近 50 个版本,旧版本会被自动清理。如需调整清理策略,可修改 SQL 脚本中的触发器逻辑。
思维笔记功能需要在 Supabase 数据库中创建 mind_notes 和 mind_note_nodes 表:
- 登录 Supabase Dashboard
- 选择你的项目
- 进入 SQL Editor
- 执行以下 SQL 脚本(见
docs/sql/create_mind_notes_tables.sql)
-- 创建 mind_notes 表(思维笔记主表)
CREATE TABLE IF NOT EXISTS mind_notes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
title TEXT NOT NULL DEFAULT '未命名思维笔记',
root_node_id UUID,
folder_id UUID REFERENCES folders(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
is_deleted BOOLEAN DEFAULT FALSE
);
-- 创建 mind_note_nodes 表(思维笔记节点表)
CREATE TABLE IF NOT EXISTS mind_note_nodes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
mind_note_id UUID NOT NULL REFERENCES mind_notes(id) ON DELETE CASCADE,
parent_id UUID REFERENCES mind_note_nodes(id) ON DELETE CASCADE,
content TEXT NOT NULL DEFAULT '',
order_index INTEGER NOT NULL DEFAULT 0,
is_expanded BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- 创建索引
CREATE INDEX IF NOT EXISTS idx_mind_notes_user_id ON mind_notes(user_id);
CREATE INDEX IF NOT EXISTS idx_mind_notes_updated_at ON mind_notes(updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_mind_notes_is_deleted ON mind_notes(is_deleted) WHERE is_deleted = FALSE;
CREATE INDEX IF NOT EXISTS idx_mind_notes_folder_id ON mind_notes(folder_id);
CREATE INDEX IF NOT EXISTS idx_mind_note_nodes_mind_note_id ON mind_note_nodes(mind_note_id);
CREATE INDEX IF NOT EXISTS idx_mind_note_nodes_parent_id ON mind_note_nodes(parent_id);
CREATE INDEX IF NOT EXISTS idx_mind_note_nodes_order ON mind_note_nodes(mind_note_id, parent_id, order_index);
-- 启用 RLS
ALTER TABLE mind_notes ENABLE ROW LEVEL SECURITY;
ALTER TABLE mind_note_nodes ENABLE ROW LEVEL SECURITY;
-- 创建 RLS 策略(详见 docs/sql/create_mind_notes_tables.sql)如果需要在思维笔记中使用文件夹功能,执行以下 SQL 脚本(见 docs/sql/add_folder_support_to_mind_notes.sql):
-- 为 mind_notes 表添加 folder_id 字段(如果尚未添加)
ALTER TABLE mind_notes
ADD COLUMN IF NOT EXISTS folder_id UUID REFERENCES folders(id) ON DELETE SET NULL;
CREATE INDEX IF NOT EXISTS idx_mind_notes_folder_id ON mind_notes(folder_id);详细配置步骤请查看 docs/sql/create_mind_notes_tables.sql 和 docs/MIND_NOTE_FEATURE.md。
本项目为私有项目。
欢迎提交 Issue 和 Pull Request!
Built with ❤️ using Next.js and Supabase