This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
CJYDocs 是一个基于 Node.js + Express 的轻量级 Markdown 文档管理和渲染系统,采用前后端分离架构。核心特点包括:
index.md 配置文件实现文档的层级化管理npm start # 生产模式启动
npm run dev # 开发模式启动(使用 nodemon 自动重启)
服务器默认运行在 http://localhost:3000
npm install
前端 (public/)
↓ REST API
后端 (server.js)
↓ File I/O
数据层 (docs/)
关键文件: ./index.md (项目根目录,NOT docs/index.md)
系统的文档结构完全由 index.md 定义,格式如下:
[分类名称]
1: 文档名.md
2: 文档名.md
2.1: 子文档名.md
2.2: 子文档名.md
解析逻辑 (server.js:247-282):
[分类名] → 对应 docs/ 下的目录名编号: 文档名.md → 文档项,支持多级编号(如 1.1.1)level 通过点号数量计算: 1 = level 0, 1.1 = level 1, 1.1.1 = level 2{ name, docs: [{ number, name, level, fullName }] }重要: 添加新文档时:
docs/分类名/ 下创建 .md 文件index.md 中添加对应条目DOM 缓存策略 (reader.js:8-22):
const DOM = {
loading: null,
content: null,
docNav: null,
toc: null,
leftSidebar: null,
rightSidebar: null,
editBtn: null,
editorContainer: null,
markdownEditor: null,
// ... 更多元素
};
所有频繁访问的 DOM 元素都通过此对象缓存,避免重复查询。缓存采用懒加载策略:首次使用时查询并缓存,后续直接使用。
事件委托模式:
#doc-nav 父元素上监听,而非每个导航项#toc 父元素上监听所有 API 都在 server.js 中定义:
GET /api/structure - 获取完整文档结构(解析 index.md,带5秒缓存)GET /api/category/:category - 获取指定分类信息GET /api/doc/:category/:docName - 获取文档内容PUT /api/doc/:category/:docName - 保存文档内容(在线编辑)GET /api/search/:category?q=xxx¤tDoc=yyy - 搜索文档安全机制:
所有用户输入都经过 sanitizePath() 清理和 validatePath() 验证,防止路径遍历攻击。
缓存策略:
index.md 解析结果缓存5秒(server.js:9-12),减少文件系统读取频率。
使用 marked.js + highlight.js,配置在 reader.js:25-38
reader.js:190-270 - 遍历渲染后的 HTML,提取所有 h1-h6 标题,使用 IntersectionObserver 实现滚动监听
server.js:159-244 - 逐行扫描所有 .md 文件,返回匹配的行号和上下文片段(最多5个)reader.js:443-785 - 使用 TreeWalker API 精确定位到匹配的文本节点并滚动localStorage,按分类隔离reader.js:359-440 - 移动端侧边栏管理逻辑:
e.stopPropagation() 防止事件冒泡,使用 element.contains() 精确判断点击位置IntersectionObserver:替代 scroll 事件,性能提升 20-30%reader.js:820-1095 + server.js:113-156 - 完整的在线文档编辑系统:
编辑器特性:
reader.js:869-988):根据当前阅读位置(标题、段落)在编辑器中自动定位到对应的 markdown 源码行PUT /api/doc/:category/:docName 保存到服务器,保存后自动重新渲染并更新 TOC编辑流程:
点击编辑按钮 → 进入编辑模式
↓
findVisibleElement() - 找到视口中可见的元素
↓
positionCursorByElement() - 在编辑器中定位到对应行
↓
用户编辑内容
↓
saveDocument() → PUT /api/doc/:category/:docName
↓
服务器验证路径 + 保存文件
↓
前端重新渲染 + 退出编辑模式
安全验证:
sanitizePath() 和 validatePath() 验证用户体验细节:
cjydocs/
├── server.js # Express 后端(288行)
│ # - 所有 API 端点(GET/PUT)
│ # - index.md 解析逻辑(带5秒缓存)
│ # - 安全验证函数(sanitizePath, validatePath)
│ # - 搜索引擎实现
├── index.md # 文档结构配置文件(项目根目录)
├── docs/
│ └── 分类名/
│ └── 文档.md # 实际文档内容(可通过在线编辑修改)
└── public/
├── index.html # 首页(显示分类列表)35行
├── reader.html # 阅读器页面(三栏布局+编辑器)129行
├── css/style.css # 统一样式
└── js/
├── index.js # 首页逻辑(70行)
└── reader.js # 阅读器核心逻辑(1113行)
# - DOM缓存、事件委托
# - 搜索、TOC、侧边栏交互
# - 在线编辑器(智能光标定位)
server.js 中定义新路由sanitizePath() 清理所有用户输入validatePath() 验证文件路径在 docs/ 目录内DOM 缓存对象获取元素classList.toggle() 简化条件判断window.innerWidth)以区分设备类型e.stopPropagation() 防止事件冒泡,避免触发外部点击事件index.md.md 文件findVisibleElement() 和 positionCursorByElement() 函数currentMarkdownContent 和 savedScrollPosition 的同步路径遍历防护:
sanitizePath() - 移除 ../、/、\ 等危险字符validatePath() - 确保解析后的路径在 docs/ 目录内示例:
const category = sanitizePath(req.params.category);
const docPath = path.join(docsDir, category, `${docName}.md`);
if (!validatePath(docPath, docsDir)) {
return res.status(403).json({ error: '拒绝访问' });
}
fixed 定位覆盖在内容区上方transform: translateX() 实现滑入滑出动画bottom: 100px,避免与侧边栏按钮冲突fixed 定位,只在需要时显示检查:
index.md 中是否有对应条目index.md 中的名称完全一致(区分大小写)检查:
检查:
e.stopPropagation() 是否正确阻止了按钮点击事件冒泡window.innerWidth <= 1024)检查:
DOM 缓存对象)检查: