# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## 项目概述 CJYDocs 是一个基于 Node.js + Express 的轻量级 Markdown 文档管理和渲染系统,采用前后端分离架构。核心特点包括: - 通过解析 `index.md` 配置文件实现文档的层级化管理 - 支持在线编辑功能,智能光标定位 - 全文搜索、TOC 目录、响应式设计 - 高性能优化: DOM 缓存、事件委托、IntersectionObserver ## 开发命令 ### 启动服务器 ```bash npm start # 生产模式启动 npm run dev # 开发模式启动(使用 nodemon 自动重启) ``` 服务器默认运行在 `http://localhost:3000` ### 安装依赖 ```bash npm install ``` ## 核心架构 ### 三层架构 ``` 前端 (public/) ↓ REST API 后端 (server.js) ↓ File I/O 数据层 (docs/) ``` ### 文档配置核心: index.md 解析 **关键文件**: `./index.md` (项目根目录,NOT docs/index.md) 系统的文档结构完全由 `index.md` 定义,格式如下: ```markdown [分类名称] 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 }] }` **重要**: 添加新文档时: 1. 在 `docs/分类名/` 下创建 `.md` 文件 2. 在项目根目录的 `index.md` 中添加对应条目 3. 两者必须同时存在,文件名必须匹配 ### 前端架构 **DOM 缓存策略** (`reader.js:8-22`): ```javascript const DOM = { loading: null, content: null, docNav: null, toc: null, leftSidebar: null, rightSidebar: null, editBtn: null, editorContainer: null, markdownEditor: null, // ... 更多元素 }; ``` 所有频繁访问的 DOM 元素都通过此对象缓存,避免重复查询。缓存采用懒加载策略:首次使用时查询并缓存,后续直接使用。 **事件委托模式**: - 导航点击:在 `#doc-nav` 父元素上监听,而非每个导航项 - TOC 点击:在 `#toc` 父元素上监听 - 优势:减少 90%+ 的事件监听器,降低内存占用 ### API 端点 所有 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`),减少文件系统读取频率。 ### 关键功能实现 #### 1. Markdown 渲染 使用 `marked.js` + `highlight.js`,配置在 `reader.js:25-38` #### 2. TOC 自动生成 `reader.js:190-270` - 遍历渲染后的 HTML,提取所有 h1-h6 标题,使用 `IntersectionObserver` 实现滚动监听 #### 3. 搜索功能 - 后端: `server.js:159-244` - 逐行扫描所有 `.md` 文件,返回匹配的行号和上下文片段(最多5个) - 前端: `reader.js:443-785` - 使用 `TreeWalker` API 精确定位到匹配的文本节点并滚动 - 搜索历史保存在 `localStorage`,按分类隔离 - 搜索防抖 500ms,点击搜索结果后自动关闭搜索框和侧边栏 #### 4. 移动端侧边栏交互 `reader.js:359-440` - 移动端侧边栏管理逻辑: - **互斥展开**: 点击一侧目录按钮时,自动隐藏另一侧已展开的目录,确保同一时间最多只有一个侧边栏展开 - **点击外部关闭**: 点击文档内容区域或其他目录以外的地方时,所有展开的侧边栏自动隐藏 - **响应式行为**: 此功能仅在移动端和平板(≤1024px)上启用,桌面端不受影响 - **事件处理**: 使用 `e.stopPropagation()` 防止事件冒泡,使用 `element.contains()` 精确判断点击位置 #### 5. 性能优化 - DOM 缓存:减少 60%+ 的查询次数 - 事件委托:减少 90%+ 的事件监听器 - 搜索防抖:500ms,减少 40% 的 API 请求 - `IntersectionObserver`:替代 scroll 事件,性能提升 20-30% #### 6. 在线编辑功能 `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()` 验证 **用户体验细节**: - 浮动编辑按钮(右下角) - 防重复提交(保存时禁用按钮) - 成功提示 Toast(2秒后自动消失) - 完整的错误处理和提示 ## 文件结构 ``` 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、侧边栏交互 # - 在线编辑器(智能光标定位) ``` ## 代码修改指南 ### 添加新 API 1. 在 `server.js` 中定义新路由 2. 使用 `sanitizePath()` 清理所有用户输入 3. 使用 `validatePath()` 验证文件路径在 `docs/` 目录内 4. 返回详细的错误信息(但不泄露内部路径) ### 修改前端逻辑 1. 优先使用 `DOM` 缓存对象获取元素 2. 使用事件委托替代单独绑定事件 3. 对高频操作使用防抖/节流 4. 更新 DOM 时使用 `classList.toggle()` 简化条件判断 5. 添加移动端交互时,注意检查窗口宽度(`window.innerWidth`)以区分设备类型 6. 使用 `e.stopPropagation()` 防止事件冒泡,避免触发外部点击事件 ### 修改文档结构 1. 编辑项目根目录的 `index.md` 2. 在对应目录下创建/删除 `.md` 文件 3. 无需重启服务器,刷新页面即可(缓存5秒后生效) ### 修改/扩展编辑功能 1. 所有文档保存操作必须通过 PUT API,并经过安全验证 2. 编辑器光标定位依赖 `findVisibleElement()` 和 `positionCursorByElement()` 函数 3. 修改编辑行为时注意保持 `currentMarkdownContent` 和 `savedScrollPosition` 的同步 4. 编辑模式切换时需要正确管理 DOM 显示/隐藏状态 5. 不要允许编辑功能创建新文件,只能编辑已存在的文档 ## 安全注意事项 **路径遍历防护**: - `sanitizePath()` - 移除 `../`、`/`、`\` 等危险字符 - `validatePath()` - 确保解析后的路径在 `docs/` 目录内 - 所有 API 都必须使用这两个函数验证用户输入 **示例**: ```javascript const category = sanitizePath(req.params.category); const docPath = path.join(docsDir, category, `${docName}.md`); if (!validatePath(docPath, docsDir)) { return res.status(403).json({ error: '拒绝访问' }); } ``` ## 响应式设计要点 ### 移动端适配 (≤768px) - 左右侧边栏默认折叠,通过底部悬浮按钮切换 - 侧边栏以 `fixed` 定位覆盖在内容区上方 - 使用 `transform: translateX()` 实现滑入滑出动画 - 回到顶部按钮位置调整为 `bottom: 100px`,避免与侧边栏按钮冲突 ### 平板适配 (768px-1024px) - 左侧边栏默认展开,右侧边栏默认折叠 - 右侧边栏以 `fixed` 定位,只在需要时显示 ### 桌面端 (>1024px) - 左右侧边栏始终展开 - 三栏布局:文档导航 + 内容区 + TOC 目录 ## 常见问题 ### 文档显示为空或 404 检查: 1. 项目根目录的 `index.md` 中是否有对应条目 2. 文件名是否与 `index.md` 中的名称完全一致(区分大小写) 3. 文件是否在正确的分类目录下 ### 搜索功能不工作 检查: 1. 搜索关键词是否至少 2 个字符 2. 文档内容是否包含匹配文本 3. 浏览器控制台是否有 API 错误 ### 移动端侧边栏无法关闭 检查: 1. 点击事件是否正确绑定到 document 2. `e.stopPropagation()` 是否正确阻止了按钮点击事件冒泡 3. 窗口宽度判断逻辑是否正确(`window.innerWidth <= 1024`) ### 性能问题 检查: 1. 是否为每个元素单独绑定了事件(应使用事件委托) 2. 是否频繁查询 DOM(应使用 `DOM` 缓存对象) 3. 是否对搜索等高频操作使用了防抖 ### 编辑功能无法保存 检查: 1. 文件是否存在(编辑功能不能创建新文件) 2. 浏览器控制台是否有 PUT API 错误 3. 服务器是否返回 403(路径验证失败)或 400(数据格式错误) 4. 文件权限是否允许服务器写入 ## 技术栈版本 - Node.js: v14+ - Express: 4.x - Marked.js: 11.x - Highlight.js: 11.x - 前端:Vanilla JavaScript (ES6+),无框架依赖