CLAUDE.md 10 KB

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

开发命令

启动服务器

npm start          # 生产模式启动
npm run dev        # 开发模式启动(使用 nodemon 自动重启)

服务器默认运行在 http://localhost:3000

安装依赖

npm install

核心架构

三层架构

前端 (public/)
    ↓ REST API
后端 (server.js)
    ↓ File I/O
数据层 (docs/)

文档配置核心: index.md 解析

关键文件: ./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 }] }

重要: 添加新文档时:

  1. docs/分类名/ 下创建 .md 文件
  2. 在项目根目录的 index.md 中添加对应条目
  3. 两者必须同时存在,文件名必须匹配

前端架构

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 点击:在 #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&currentDoc=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. 修改编辑行为时注意保持 currentMarkdownContentsavedScrollPosition 的同步
  4. 编辑模式切换时需要正确管理 DOM 显示/隐藏状态
  5. 不要允许编辑功能创建新文件,只能编辑已存在的文档

安全注意事项

路径遍历防护:

  • sanitizePath() - 移除 ..//\ 等危险字符
  • validatePath() - 确保解析后的路径在 docs/ 目录内
  • 所有 API 都必须使用这两个函数验证用户输入

示例:

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+),无框架依赖