This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Markdown 在线编辑器,支持服务端文件编辑、实时预览、Mermaid 图表和 LaTeX 公式。
可作为 CLI 工具全局安装:npm install -g colonynote → colonynote -d /path/to/docs
# 全栈开发(后端 + 前端热更新)- 前端 5787,后端 5788
# 默认使用 ./workspace 作为文档目录
pnpm dev
# 仅前端开发(Vite 开发服务器,端口 5787)
pnpm dev:frontend
# 仅后端开发(Hono 服务器,端口 5787)
pnpm dev:backend
# 生产构建(前端 Vite + 后端 TypeScript 编译)
pnpm build
# 运行生产服务器
pnpm start
# 预览生产构建
pnpm preview
# TypeScript 类型检查
pnpm typecheck
# 运行测试(watch 模式)
pnpm test
# 运行单个测试文件
pnpm vitest run src/path/to/file.test.ts
# 发布新版本(交互式选择版本号)
pnpm release- 后端: Hono.js + @hono/node-server + ws (WebSocket)
- 前端: React 18 + Vite + Tailwind CSS v4
- 编辑器: TipTap 3 + tiptap-markdown (所见即所得 + 源码模式切换)
- UI 组件: shadcn/ui (基于 Radix UI)
- 图标: lucide-react
- 图表: Mermaid
- 公式: KaTeX
- 搜索: FlexSearch (服务端 ripgrep) + IndexedDB 缓存
- 代码高亮: lowlight
- 包管理: pnpm
src/
├── client/ # 前端 React 应用
│ ├── App.tsx # 主应用组件(布局、状态管理)
│ ├── main.tsx # 入口
│ ├── components/ # UI 组件
│ │ ├── ui/ # shadcn/ui 基础组件
│ │ ├── TipTapEditor.tsx # 主编辑器组件(基于 TipTap 3)
│ │ ├── MilkdownEditor.tsx # 备用编辑器(基于 Milkdown,未启用)
│ │ ├── FileTree.tsx # 文件树
│ │ └── ... # 各种对话框/模态框
│ ├── hooks/ # React hooks
│ │ ├── useTabs.ts # 多标签页管理(打开/关闭/保存/自动重载)
│ │ ├── useWebSocket.ts # WebSocket 实时同步
│ │ └── useSearch.ts # 搜索索引
│ └── lib/ # 工具库
│ ├── types.ts # 类型定义
│ ├── tabTypes.ts # Tab 类型定义
│ ├── searchIntent.ts # 搜索意图解析(fuzzy/prefix/browse)
│ └── utils.ts # 工具函数(cn 等)
├── server/ # 后端 Hono 服务
│ ├── index.ts # 生产服务器入口(端口 5787)
│ ├── app.ts # Hono 应用创建工厂函数(createApp)
│ ├── api.ts # REST API 路由 + ConfigHolder 模式
│ ├── watcher.ts # 文件变更监听(chokidar)+ WebSocket 广播
│ └── ignore.ts # IgnoreMatcher 忽略规则引擎
├── config.ts # 配置加载(全局 + 用户配置)
├── dev.ts # 开发模式启动(前后端同时运行,后端端口 5788)
└── ...
bin/
└── colonynote.js # CLI 工具入口(npm install -g colonynote)
GET /— 获取文件树(按 dirs 分组)GET /config/PATCH /config— 读取/更新配置GET /dirs/POST /dirs/DELETE /dirs/PATCH /dirs— 目录 CRUDGET /dirs/browse/GET /dirs/search— 目录浏览/搜索(支持模糊匹配和前缀匹配)GET /children— 按需加载目录子节点GET /content— 批量读取文件内容GET /search— 全文搜索(服务端 ripgrep)GET /*— 读取单个文件或目录POST /*— 保存文件/创建文件PUT /*— 重命名/移动DELETE /*— 删除POST /copy— 复制文件
bin/colonynote.js — 全局 CLI 工具入口,支持 -d(指定目录)、-p(指定端口)、-c(指定配置文件)等参数
- REST API:
/api/files系列端点处理文件 CRUD、目录管理 - WebSocket:
/ws连接广播文件变更事件(file:change)和配置重载事件(config:reload),实现多端实时同步 - URL 路由: 使用 hash 格式
#rootPath:filePath定位文件。Tab key 格式为rootPath::filePath(多项目场景),用于区分同名文件 - Vite 代理(
vite.config.ts): 开发模式下前端端口 5787 代理/api和/ws到后端 5788;hmr: false已禁用(开发模式由后端统一管理 WebSocket)
- 全局配置:
~/.colonynote/config.json(生产环境)或~/.colonynote/config.dev.json(开发环境) - 开发环境通过
loadConfig('development')加载config.dev.json,生产环境通过loadConfig()加载config.json - 配置通过
getConfigFilePath(env)获取配置文件绝对路径 - 配置热重载: 使用
fs.watch监听配置文件变更(500ms 防抖),重新加载后通过 WebSocket 广播config:reload消息,前端收到后触发config-changed自定义事件 - 敏感文件路径由
DEFAULT_SENSITIVE_PATHS定义,禁止访问
为避免 createFileRouter 闭包捕获旧配置,使用 ConfigHolder 模式:
// src/server/api.ts
export interface ConfigHolder {
get config(): ColonynoteConfig
get matcher(): IgnoreMatcher
setConfig(config: ColonynoteConfig): void
setMatcher(matcher: IgnoreMatcher): void
}
// 创建可变 holder,路由通过 getter 实时获取最新配置
const holder = createMutableConfigHolder(config, matcher)
const fileRouter = createFileRouter(holder, env)IgnoreMatcher类(src/server/ignore.ts)管理忽略规则- 支持全局配置模式(
config.ignore.patterns)和.ignore文件(.colonynoteignore、.gitignore) .ignore文件就近加载(从目标文件向上查找),后出现的规则优先级更高- 支持否定规则(
!pattern)和目录标记(pattern/) matcher.updateGlobalPatterns()用于运行时更新全局规则
多标签页由 useTabs.ts 统一管理,替代了早期的单文件 useFile 模式:
- Tab key:
rootPath::filePath(多项目场景)或filePath(单项目),用于区分不同目录下的同名文件 - 状态管理:
tabsRef作为唯一数据源(Map<string, OpenTab>),tickstate 仅用于触发 React 重渲染 - 保存流程:
updateTabContent()→ 防抖定时器(默认 300ms)→doSave()→ POST 到/api/files→ 更新lastSavedContent - Dirty 检测:
content !== lastSavedContent,关闭 dirty tab 时弹出确认对话框 - 外部变更同步:
handleWsFileChange()收到file:change消息后,检查pendingSaveSessionsRef和SAVE_IGNORE_BUFFER_MS(5s),避免将自身保存误判为外部修改;若 tab 非 dirty 则自动重载内容 - 状态机:
idle→saving→saved/error,UI 根据状态显示保存中/已保存/失败提示
- 自动保存:
useTabs.updateTabContent()默认 300ms 防抖自动保存 - 外部修改检测:
pendingSaveSessionsRef+SAVE_IGNORE_BUFFER_MS(5s) 缓冲,避免将自己的保存误判为外部修改 - 文件树: 按
dirs分组显示,支持多根目录切换,展开状态按目录分别保存 - 服务端缓存:
api.ts中的treeCache缓存文件树结果(3s TTL + configHash 校验),避免重复遍历目录 - WebSocket 广播: 每个 server 入口维护
clients: Set<WebSocket>,通过broadcastWsMessage发送消息
- 使用函数组件 + React Hooks,不使用类组件
- 组件文件名使用 PascalCase(如
EditorToolbar.tsx) - Props 接口定义在组件文件内,命名为
ComponentNameProps - 必选属性在前,可选属性(
?)在后
- 自定义 Hook 以
use开头,camelCase 命名(如useTabs) - 使用
useCallback缓存函数引用 - 使用
useRef存储最新值,避免依赖循环 - Hook 文件放在
src/client/hooks/目录
- API 逻辑封装在自定义 Hook 中(如
useTabs) - RESTful 风格:
GET→ 读取POST→ 创建/保存PUT→ 更新/重命名/移动DELETE→ 删除
- 带 root 参数的动态路径构建:
const url = dirPath ? `/api/files${filePath}?root=${encodeURIComponent(dirPath)}` : `/api/files${filePath}`
- 全局单例模式(模块级全局变量)
- 支持自动重连(3 秒间隔)
- 消息类型:
file:change(文件变更通知)、config:reload(配置重载通知)
- 移动端优先:断点为 768px
- 移动端/桌面端判断:
window.innerWidth < 768 - 使用
isMobilestate +useEffect监听 resize - 条件渲染模式:
{isMobile && <MobileComponent />} {!isMobile && <DesktopComponent />}
- 或使用
variantprop 区分
- 使用 Tailwind CSS v4
- 使用
cn()工具函数合并类名(基于 clsx + tailwind-merge) - 图标统一使用
lucide-react,尺寸用size-4、size-5
- 类型定义在
src/client/lib/types.ts - 客户端类型需与服务器端(
src/config.ts)保持同步 - 使用 JSDoc 注释说明
格式:<类型>(<范围>): <描述>
类型:
feat: 新功能fix: 修复 bugrefactor: 重构(非功能变更)style: 样式调整chore: 杂项/构建/配置ai: AI 相关(如添加 skill)
范围(可选): 如 editor、server、search 等
示例:
feat(editor): 添加移动端快捷工具栏
fix(editor): 移动端下拉菜单改为向上展开
refactor(editor): 标题按钮改为下拉菜单,节省工具栏空间
本项目是移动端优先的 Markdown 在线编辑器。所有功能开发、UI 设计必须:
- 首先为移动端屏幕(< 768px)设计
- 再逐步增强到桌面端
- 移动端/桌面端判断使用
window.innerWidth < 768
移动端特殊处理:
- 侧边栏:桌面端固定可拖拽(200-600px),移动端 Sheet 抽屉
- 工具栏:桌面端顶部固定,移动端底部固定
- 对话框:桌面端 Dialog,移动端底部 Sheet(85vh)
- 触摸优化:按钮 active 态缩放、safe-area-inset 适配
- 保存时记录会话 ID(
pendingSaveSessionsRef),避免在网络延迟时将自身的保存误判为外部修改 - 文件树按
dirs分组显示,支持多根目录切换,展开状态按目录分别保存 - 侧边栏宽度可拖拽调整(桌面端),宽度值持久化到 localStorage
- WebSocket 使用全局单例,多个组件共享连接
- 文件保存使用防抖(默认 300ms)
- TipTap editor 内部更新使用
isInternalUpdateRef防止onUpdate循环触发 - Mermaid 渲染队列(
mermaidQueue.ts): 单例模式,LRU 缓存(最大 50 条),并发控制(最大 2 个),按theme::source键缓存渲染结果,避免频繁调用mermaid.render() - 搜索意图解析(
searchIntent.ts): 将用户输入解析为三种模式 —fuzzy(模糊搜索)、prefix(前缀匹配,如projects/col)、browse(浏览目录,如projects/) - 开发模式 Vite
hmr: false:前后端联调时 WebSocket 升级由后端统一管理,避免 Vite HMR 与后端/ws冲突