Files
MarkLite/DESIGN.md
T

335 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 和 GFMGitHub 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)。