# MarkLite - 设计文档 ## 1. 项目概述 MarkLite 是一款轻量级的 Windows 本地 Markdown 编辑器桌面应用程序。基于 Electron 框架构建,提供简洁现代的用户界面,支持多标签页、Markdown 文件的打开、编辑和实时预览,支持亮色/暗色主题切换,支持搜索替换和文件树侧边栏。 ## 2. 技术架构 ### 2.1 技术栈 | 组件 | 技术选型 | 说明 | |------|----------|------| | 桌面框架 | Electron v28 | 跨平台桌面应用框架 | | 前端 | HTML + CSS + JavaScript | 原生前端技术,无框架依赖 | | Markdown 解析 | marked.js v12 | 高性能 Markdown 解析库(本地离线) | | 代码高亮 | highlight.js v11 | 代码块语法高亮(本地离线) | | 打包 | electron-builder | 生成 Windows NSIS 安装包 | ### 2.2 进程架构 ``` ┌─────────────────────────────────────────┐ │ Main Process (main.js) │ │ - 窗口管理 │ │ - 文件系统操作 (fs) │ │ - 文件监听 (fs.watch) │ │ - 目录树读取与监听 │ │ - 未保存提醒拦截 │ │ - IPC 通信主端 │ └──────────────┬──────────────────────────┘ │ IPC (contextBridge) ┌──────────────▼──────────────────────────┐ │ Preload Script (preload.js) │ │ - 安全的 API 桥接 │ │ - exposeInMainWorld │ └──────────────┬──────────────────────────┘ │ ┌──────────────▼──────────────────────────┐ │ Renderer Process (renderer/) │ │ - 多标签页管理 │ │ - UI 渲染(亮色/暗色主题) │ │ - Markdown 编辑与预览 │ │ - 搜索替换(高亮、正则、大小写) │ │ - 文件树侧边栏 │ │ - 拖拽文件处理 │ │ - 快捷键处理 │ │ - 用户设置持久化 (localStorage) │ └─────────────────────────────────────────┘ ``` ### 2.3 安全模型 - `contextIsolation: true` + `nodeIntegration: false` - 通过 `contextBridge.exposeInMainWorld` 安全暴露 API - CSP 策略:`default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self'; img-src 'self' data:` - 渲染进程无法直接访问 Node.js API ## 3. 功能设计 ### 3.1 核心功能 1. **多标签页** - Ctrl+T 新建空白标签 - Ctrl+W 关闭当前标签(未保存时确认) - Ctrl+Tab / Ctrl+Shift+Tab 切换标签 - 点击标签切换,hover 显示关闭按钮 - 同一文件不会重复打开(自动切换到已有标签) - 每个标签独立保存:内容、滚动位置、光标位置、修改状态 2. **文件打开** - 工具栏 → 打开按钮(支持 .md / .txt / .markdown) - 拖拽文件到窗口打开(支持多文件同时拖入) - 支持 Windows 文件关联(双击 .md 文件打开) - 支持命令行参数传入文件路径 3. **文件保存** - Ctrl+S 快捷键保存 - 工具栏 → 保存按钮 - Ctrl+Shift+S 另存为 4. **实时预览** - 左侧编辑器 + 右侧预览(默认分屏模式) - 编辑 150ms 防抖后更新预览 - 基于 DOM 位置映射的滚动同步 5. **视图模式** - 编辑+预览(Split View)— Ctrl+1 - 纯编辑模式 — Ctrl+2 - 纯预览模式 — Ctrl+3 - 记忆上次使用的视图模式(localStorage) 6. **文件修改检测** - 主进程通过 `fs.watch` 监听当前活动标签的文件 - 外部修改时显示黄色提示横幅 - 支持「重新加载」或「忽略」 - 切换标签时自动切换监听目标 7. **未保存提醒** - 关闭窗口时检测所有标签的未保存修改 - 弹出确认对话框,防止误操作 - 主进程 5 秒超时兜底,防止渲染进程无响应时窗口卡死 8. **暗色主题** - 工具栏右侧月亮/太阳图标切换 - CSS 变量驱动,一键切换整套配色 - 主题偏好持久化(localStorage) 9. **搜索替换** - Ctrl+F 打开搜索栏,Ctrl+H 打开搜索+替换栏 - 实时高亮所有匹配项,当前匹配用不同颜色标识 - Enter / Shift+Enter 导航下一个/上一个匹配 - 区分大小写(Alt+C)、正则表达式(Alt+R)切换 - 替换当前(Ctrl+Shift+G)、全部替换(Ctrl+Shift+H) - 自动将选中文本填充到搜索框 - Esc 关闭搜索栏 10. **文件树侧边栏** - 工具栏按钮打开文件夹选择对话框 - 递归读取目录结构,只显示 .md/.markdown/.txt 文件 - 自动跳过 node_modules、.git、dist 等无关目录 - 点击文件夹展开/折叠,点击文件在新标签页打开 - 已打开的文件自动切换到对应标签 - fs.watch 监听目录变化,自动刷新树 - 侧边栏折叠状态持久化(localStorage) ### 3.2 UI 设计 #### 色彩方案(亮色) | 角色 | 色值 | |------|------| | 主色调 | `#1a73e8` | | 背景色 | `#ffffff` | | 次级背景 | `#f8f9fa` | | 三级背景 | `#f1f3f4` | | 文字色 | `#333333` | | 次级文字 | `#5f6368` | | 代码块背景 | `#f6f8fa` | | 边框色 | `#e1e4e8` | #### 色彩方案(暗色) | 角色 | 色值 | |------|------| | 主色调 | `#8ab4f8` | | 背景色 | `#1e1e1e` | | 次级背景 | `#252526` | | 三级背景 | `#2d2d2d` | | 文字色 | `#d4d4d4` | | 次级文字 | `#9e9e9e` | | 代码块背景 | `#2d2d2d` | | 边框色 | `#3e3e3e` | #### 字体 - **UI 字体**: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif - **编辑器字体**: "Cascadia Code", "Fira Code", "JetBrains Mono", Consolas, monospace - **预览字体**: 同 UI 字体 #### 布局 ``` ┌──────────────────────────────────────────────────────────────────────────┐ │ MarkLite - filename.md ─ □ ✕ │ ├──────────────────────────────────────────────────────────────────────────┤ │ 📁 打开 │ 💾 保存 │ ⬜ 分屏 │ ✏️ 编辑 │ 👁 预览 │ 🌙 │ ├──────────────────────────────────────────────────────────────────────────┤ │ [file1.md] [file2.md] [未命名] [+] │ ├──────────────────────────────────────────────────────────────────────────┤ │ ⚠️ 文件已被外部程序修改 [重新加载] [忽略] │ ├──────────┬─────────────────────────┬─────────────────────────────────────┤ │ 资源管理器│ │ │ │ ▼ project │ 🔍 查找... 2/5 │ Title │ │ 📁 src │ [替换... ] [替换][全部]│ ─────── │ │ 📄 file1│ │ │ │ 📄 file2│ 1 # Title │ Title │ │ 📁 lib │ 2 │ ─────── │ │ │ 3 content... │ content... │ │ │ 4 │ │ ├──────────┴─────────────────────────┴─────────────────────────────────────┤ │ filename.md │ UTF-8 │ Markdown │ 1.2 KB │ 行 3, 列 1 │ └──────────────────────────────────────────────────────────────────────────┘ ``` ## 4. 文件结构 ``` MarkLite/ ├── package.json # 项目配置与依赖 ├── main.js # Electron 主进程 ├── preload.js # 预加载脚本(IPC 桥接) ├── renderer/ │ ├── index.html # 主页面结构(含侧边栏、搜索栏) │ ├── style.css # UI 样式(亮色/暗色主题、侧边栏、搜索栏) │ └── renderer.js # 渲染进程逻辑(标签页、编辑、预览、搜索、文件树) ├── lib/ │ ├── marked.min.js # Markdown 解析库(离线) │ ├── highlight.min.js # 代码高亮库(离线) │ └── highlight-github.css # 代码高亮主题 ├── assets/ │ └── icon.ico # 应用图标(多尺寸) ├── DESIGN.md # 设计文档(本文件) ├── DEVSETUP.md # 开发环境配置指南 ├── README.md # 项目说明 ├── LICENSE # MIT 许可证 └── .gitignore # Git 忽略文件 ``` ## 5. IPC 通信设计 ### 5.1 渲染进程 → 主进程(invoke) | 通道 | 参数 | 返回值 | 说明 | |------|------|--------|------| | `dialog:openFile` | 无 | `{ filePath, content }` 或 `null` | 打开文件对话框 | | `file:read` | `filePath` | `{ success, content }` | 读取文件内容 | | `file:save` | `{ filePath, content }` | `{ success, filePath }` | 保存文件 | | `file:saveAs` | `{ content }` | `{ success, filePath }` | 另存为 | | `file:getCurrentPath` | 无 | `string \| null` | 获取当前文件路径 | | `file:stats` | `filePath` | `{ success, size, mtime }` | 获取文件元信息 | | `file:reload` | 无 | `{ success, content, filePath }` | 重新加载当前文件 | | `tab:switched` | `filePath` | 无 | 通知主进程切换活动文件 | | `window:forceClose` | 无 | 无 | 强制关闭窗口(跳过未保存检查) | | `dir:readTree` | `dirPath` | `{ success, tree, rootPath }` | 递归读取目录树 | | `dir:openDialog` | 无 | `string \| null` | 打开文件夹选择对话框 | | `dir:watch` | `dirPath` | 无 | 监听目录变化 | | `dir:unwatch` | 无 | 无 | 停止监听目录变化 | ### 5.2 主进程 → 渲染进程(send) | 通道 | 数据 | 说明 | |------|------|------| | `file:openInTab` | `{ filePath, content }` | 在新标签中打开文件 | | `file:opened` | `{ filePath, content }` | 文件已打开(兼容旧路径) | | `file:externallyModified` | `filePath` | 文件被外部修改 | | `menu:save` | 无 | 菜单触发保存 | | `menu:saveAs` | 无 | 菜单触发另存为 | | `menu:viewMode` | `mode` | 菜单切换视图模式 | | `window:confirmClose` | 无 | 请求确认关闭 | | `window:closing` | 无 | 窗口即将关闭 | | `sidebar:dirChanged` | 无 | 目录结构变化,通知渲染进程刷新树 | ## 6. 标签页数据模型 每个标签页在渲染进程中维护独立状态: ```javascript { id: Number, // 唯一标识 filePath: String | null, // 文件路径(未命名标签为 null) content: String, // 编辑器内容 isModified: Boolean, // 是否已修改 scrollTop: Number, // 编辑器滚动位置 scrollLeft: Number, selectionStart: Number, // 光标选区 selectionEnd: Number, previewScrollTop: Number // 预览面板滚动位置 } ``` 切换标签时自动保存当前状态、恢复目标状态。 ## 7. 数据持久化 通过 `localStorage` 存储用户偏好,key 为 `marklite-settings`: ```json { "darkMode": false, "viewMode": "split", "splitRatio": 50, "sidebarCollapsed": false } ``` | 字段 | 类型 | 说明 | |------|------|------| | `darkMode` | boolean | 暗色主题开关 | | `viewMode` | string | 视图模式:`split` / `editor` / `preview` | | `splitRatio` | number | 分屏比例(20~80) | | `sidebarCollapsed` | boolean | 侧边栏是否折叠 | ## 8. Markdown 渲染支持 支持标准 Markdown 和 GFM(GitHub Flavored Markdown): - 标题(h1-h6) - 段落、换行 - **粗体**、*斜体*、~~删除线~~ - 有序/无序列表 - 任务列表(`- [x]`) - 代码块(围栏式 + 语法高亮,180+ 语言) - 行内代码 - 链接、图片 - 表格 - 引用块 - 水平线 - HTML 内联 ## 9. 快捷键 | 快捷键 | 功能 | |:-------|:-----| | `Ctrl + T` | 新建标签页 | | `Ctrl + W` | 关闭当前标签页 | | `Ctrl + Tab` | 切换到下一个标签页 | | `Ctrl + Shift + Tab` | 切换到上一个标签页 | | `Ctrl + O` | 打开文件 | | `Ctrl + S` | 保存文件 | | `Ctrl + Shift + S` | 另存为 | | `Ctrl + 1` | 编辑 + 预览(分屏) | | `Ctrl + 2` | 纯编辑模式 | | `Ctrl + 3` | 纯预览模式 | | `Ctrl + F` | 搜索 | | `Ctrl + H` | 搜索并替换 | | `Enter` / `Shift+Enter` | 下一个 / 上一个匹配 | | `Alt + C` | 切换区分大小写 | | `Alt + R` | 切换正则表达式 | | `Ctrl + Shift + G` | 替换当前匹配 | | `Ctrl + Shift + H` | 全部替换 | | `Esc` | 关闭搜索栏 | ## 10. 构建与发布 使用 `electron-builder` 打包: - 输出格式:NSIS 安装包(.exe) - 目标平台:Windows x64 - 应用图标:assets/icon.ico - 文件关联:`.md` / `.markdown` / `.txt` - 支持自定义安装目录、桌面/开始菜单快捷方式 - 便携版:`npm run build:portable` 详见 [DEVSETUP.md](DEVSETUP.md)。