Files
MarkLite/DESIGN.md
T
thzxx 0940c2f5c6 fix: resolve all TS errors, ESLint warnings, and npm warnings bump to v0.3.12
- Pin all @milkdown/* packages to exact 7.21.1, add phantom-dep plugins as direct deps

- Fix TextSelection.create type error in useMilkdown.ts

- Fix Backspace auto-pair dead-code bug (unreachable due to PAIRS check order)

- Extract parseHeadings/Heading to outlineUtils.ts (react-refresh fix)

- Extract StatusBar item components to StatusBarItems.tsx (react-refresh fix)

- Configure npm allowScripts for electron and esbuild postinstall

- Update QUALITY_REVIEW_REPORT.json with all fixes applied

- Bump version to 0.3.12
2026-07-06 10:51:39 +08:00

367 lines
17 KiB
Markdown
Raw Permalink 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 v0.3.12 — 架构设计文档
## 1. 项目概述
MarkLite 是一款轻量级的 Windows 本地 Markdown 编辑器桌面应用程序。基于 Electron + React + TypeScript 构建,采用 Milkdown v7 WYSIWYG 编辑器 + 源码编辑模式、Zustand 状态管理、IndexedDB 持久化、unified/rehype Markdown 渲染管线。
### 1.1 核心原则
1. **类型安全** — 全量 TypeScript,所有 IPC 通信、状态、接口均有类型定义
2. **模块化** — 源文件按职责分层:主进程 / 预加载 / 渲染进程(组件 / stores / hooks / lib / db / types
3. **安全隔离** — contextIsolation + nodeIntegration:false + CSP + rehype-sanitize
4. **可测试性** — 业务逻辑(lib/)与 UIcomponents/)解耦
## 2. 技术架构
### 2.1 技术栈
| 组件 | 技术 | 版本 | 说明 |
|------|------|------|------|
| 桌面框架 | Electron | v28 | 跨平台桌面应用框架 |
| 前端框架 | React | v18 | 函数组件 + Hooks |
| 类型系统 | TypeScript | v5.6 | 全量类型安全 |
| 编辑器 | Milkdown | v7 | WYSIWYG 编辑器 + 源码编辑模式 |
| 状态管理 | Zustand | v5 | 轻量级状态管理 |
| 持久化 | Dexie.js (IndexedDB) | v4 | 标签页状态 / 用户设置 / 最近文件 |
| Markdown 解析 | unified / remark / rehype | v11 | 插件化渲染管线 |
| 代码高亮 | rehype-highlight | v7 | 基于 highlight.js |
| 构建工具 | electron-vite | v3 | Electron + ViteHMR 热更新 |
| 打包工具 | electron-builder | v25 | Windows NSIS 安装包 |
| 样式 | CSS Variables | — | 主题驱动,亮色/暗色 |
### 2.2 进程架构
```
┌──────────────────────────────────────────────────────────────┐
│ Main Process (src/main/) 5 文件 │
│ index.ts 入口:窗口创建、app 生命周期、单实例锁 │
│ ipc-handlers.ts 所有 ipcMain.handle 注册 │
│ file-system.ts 文件读写、目录树构建、BOM 剥离 │
│ file-watcher.ts fs.watch 封装(单文件 + 目录监听) │
│ window-manager.ts 窗口创建、关闭拦截、单实例锁 │
└───────────────────────┬──────────────────────────────────────┘
│ contextBridge (安全隔离)
┌───────────────────────▼──────────────────────────────────────┐
│ Preload Script (src/preload/) 1 文件 │
│ index.ts contextBridge 类型安全暴露 │
│ electronAPI 18 个方法/事件的类型安全接口 │
└───────────────────────┬──────────────────────────────────────┘
┌───────────────────────▼──────────────────────────────────────┐
│ Renderer Process (src/renderer/) React 18 │
│ │
│ components/ (23) Toolbar · TabBar · Editor · EditorToolbar │
│ SourceEditor · Preview · Sidebar · FileTree│
│ OutlinePanel · StatusBar · WelcomeScreen │
│ Toast · ConfirmDialog · ModifiedBanner │
│ SearchReplace · DropOverlay · ErrorBoundary│
│ AboutDialog · LoadingSpinner · Icons │
│ │
│ stores/ (3) tabStore · editorStore · sidebarStore │
│ hooks/ (19) useTheme · useSettings · useSettingsInit │
│ useKeyboard · useDragDrop · useFileWatch │
│ useAutoSave · useIpcListeners ... │
│ lib/ (4) markdown · fileUtils · errorHandler │
│ constants │
│ db/ (4) schema · tabRepository · settingsRepo │
│ recentFilesRepository │
│ types/ (5) tab · file · settings · ipc · index │
│ styles/ (3) variables · global · markdown-body │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ Shared (src/shared/) 3 文件 │
│ ipc-channels.ts IPC 通道名常量 │
│ types.ts 共享类型定义 │
│ constants.ts 共享常量 (版本号、文件大小限制等) │
└──────────────────────────────────────────────────────────────┘
```
### 2.3 安全模型
| 层级 | 措施 | 说明 |
|------|------|------|
| Electron | `contextIsolation: true` | 渲染进程与主进程隔离 |
| Electron | `nodeIntegration: false` | 渲染进程无法访问 Node.js API |
| IPC | `contextBridge.exposeInMainWorld` | 仅暴露 22 个类型安全方法/事件 |
| CSP | `default-src 'self'; script-src 'self'` | 阻断内联脚本、外部资源 |
| HTML | `rehype-sanitize` | 渲染 Markdown 时过滤危险标签/属性 |
| 链接 | 协议白名单 | 仅允许 `http:` / `https:` / `#` 锚点 |
| 路径 | `validatePath()` | 防止路径遍历攻击 |
## 3. 状态管理架构
### 3.1 Zustand Stores
```
┌─────────────────────────────────────────────────────────┐
│ App.tsx (根组件) │
├─────────┬──────────┬──────────┬──────────┬──────────────┤
│Toolbar │ TabBar │ Sidebar │ Editor │ Preview │
│ │ │ │ (CM6) │ │
├─────────┴──────────┴──────────┴──────────┴──────────────┤
│ Zustand Stores │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ tabStore │ │editorStore│ │sidebarStore│ │
│ │ - tabs │ │- viewMode│ │- tree │ │
│ │- activeId│ │- darkMode│ │- expanded │ │
│ │ - mru │ │ │ │- rootPath │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ ┌────▼────────────▼────────────▼────────────────────┐ │
│ │ IndexedDB (Dexie.js) │ │
│ │ tabSnapshots │ settings │ recentFiles │ activeTab │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
```
### 3.2 tabStore — 标签页状态
```typescript
interface TabState {
tabs: Tab[] // 所有标签页
activeTabId: string | null // 当前活动标签 ID
mruStack: string[] // MRU 标签栈(Ctrl+Tab 切换)
createTab(filePath?, content?) // 创建标签(同文件不重复打开)
closeTab(tabId) // 关闭标签(自动切换到相邻标签)
switchToTab(tabId) // 切换标签
updateTabContent(tabId, content) // 更新内容(标记修改)
setModified(tabId, modified) // 设置修改状态
getActiveTab() // 获取当前标签
updateTabScroll(tabId, scroll) // 更新滚动/光标位置
saveToDB() // 保存到 IndexedDB
loadFromDB() // 从 IndexedDB 加载
}
```
### 3.3 editorStore — 编辑器状态
```typescript
interface EditorState {
viewMode: 'editor' | 'preview' // 视图模式
darkMode: boolean // 暗色主题
}
```
### 3.4 sidebarStore — 侧边栏状态
```typescript
interface SidebarState {
isVisible: boolean // 是否显示
rootPath: string | null // 当前打开的文件夹路径
tree: FileNode[] // 目录树数据
expandedDirs: Set<string> // 已展开的目录集合
sidebarWidth: number // 侧边栏宽度 (180~500)
}
```
## 4. 数据持久化 — IndexedDB
通过 Dexie.js 封装 IndexedDB
### 4.1 数据库 Schema
```typescript
const db = new Dexie('MarkLite')
db.version(1).stores({
tabSnapshots: 'id, filePath, updatedAt', // 标签页快照
settings: 'id', // 用户设置
recentFiles: '++id, filePath, lastOpened', // 最近打开文件
activeTab: 'id' // 当前活动标签
})
```
### 4.2 数据模型
| Store | 字段 | 说明 |
|-------|------|------|
| `tabSnapshots` | id, filePath, content, scrollTop, selectionStart, selectionEnd, isModified, updatedAt | 标签页状态快照 |
| `settings` | id, darkMode, viewMode, sidebarCollapsed, sidebarWidth | 用户偏好设置 |
| `recentFiles` | ++id, filePath, lastOpened | 最近打开文件列表 |
| `activeTab` | id, activeTabId | 当前活动标签 ID |
## 5. IPC 通信设计
### 5.1 渲染进程 → 主进程(invoke)
| 通道 | 参数 | 返回值 | 说明 |
|------|------|--------|------|
| `dialog:openFile` | 无 | `OpenFileResponse` | 打开文件对话框 |
| `file:read` | `filePath` | `ReadFileResult` | 读取文件内容 |
| `file:save` | `{ filePath, content }` | `SaveFileResult` | 保存文件 |
| `file:saveAs` | `{ content }` | `SaveFileResult` | 另存为 |
| `file:getCurrentPath` | 无 | `string \| null` | 获取当前文件路径 |
| `file:stats` | `filePath` | `FileStatsResult` | 获取文件元信息 |
| `file:reload` | 无 | `ReloadFileResult` | 重新加载当前文件 |
| `tab:switched` | `filePath \| null` | `void` | 通知主进程切换活动文件 |
| `window:forceClose` | 无 | `void` | 强制关闭窗口 |
| `window:cancelClose` | 无 | `void` | 取消关闭 |
| `dir:readTree` | `dirPath` | `ReadDirTreeResult` | 递归读取目录树 |
| `dir:openDialog` | 无 | `string \| null` | 打开文件夹选择对话框 |
| `dir:watch` | `dirPath` | `void` | 监听目录变化 |
| `dir:unwatch` | 无 | `void` | 停止监听目录变化 |
### 5.2 主进程 → 渲染进程(send)
| 通道 | 数据 | 说明 |
|------|------|------|
| `file:openInTab` | `{ filePath, content }` | 在新标签中打开文件 |
| `file:externallyModified` | `filePath` | 文件被外部修改 |
| `window:confirmClose` | 无 | 请求确认关闭 |
| `sidebar:dirChanged` | 无 | 目录结构变化 |
## 6. 编辑器架构 — Milkdown v7 (WYSIWYG) + SourceEditor (textarea)
### 6.1 双编辑模式
- **编辑模式 (Milkdown)**:基于 ProseMirror 的 WYSIWYG Markdown 编辑器,支持格式化工具栏(粗体/斜体/删除线/标题/列表/引用/代码块/链接/图片/分割线)、搜索替换面板(含正则支持)、自动配对括号/引号、undo/redo
- **源码模式 (SourceEditor)**:原生 textarea 控制 Markdown 原文,支持 Tab 缩进、Ctrl+B/I 快捷键
两种模式共享同一 tabStore 数据源,可随时切换。
### 6.2 插件体系
| 插件 | 说明 |
|------|------|
| commonmark | 基础 Markdown 语法 |
| gfm | GitHub Flavored Markdown(表格/任务列表/删除线等) |
| history | undo/redo |
| listener | 内容变更监听 |
| indent | Tab 缩进 |
| trailing | 尾随换行 |
| clipboard | 剪贴板增强 |
| searchPlugin (自研) | 搜索高亮装饰 |
| autoPairPlugin (自研) | 自动配对括号/引号 |
### 6.3 滚动与选区持久化
切换标签时自动保存/恢复:
- 滚动位置 (`scrollTop`)
- 光标选区 (`selectionStart`, `selectionEnd`)
## 7. Markdown 渲染管线
```
Markdown 文本
remark-parse 解析为 MDAST
remark-gfm 扩展 GFM 语法
remark-rehype 转换为 HAST
rehype-raw 解析内联 HTML
rehype-sanitize 安全过滤
rehype-fixImages 相对路径图片转 file:// URL
rehype-highlight 代码语法高亮
rehype-stringify 序列化为 HTML
dangerouslySetInnerHTML 渲染到 DOM
```
## 8. UI 设计
### 8.1 色彩方案
#### 亮色主题
| 角色 | CSS 变量 | 色值 |
|------|----------|------|
| 主色调 | `--primary` | `#1a73e8` |
| 背景色 | `--bg` | `#ffffff` |
| 次级背景 | `--bg-secondary` | `#f8f9fa` |
| 文字色 | `--text` | `#333333` |
| 边框色 | `--border` | `#e1e4e8` |
#### 暗色主题
| 角色 | CSS 变量 | 色值 |
|------|----------|------|
| 主色调 | `--primary` | `#8ab4f8` |
| 背景色 | `--bg` | `#1e1e1e` |
| 次级背景 | `--bg-secondary` | `#252526` |
| 文字色 | `--text` | `#d4d4d4` |
| 边框色 | `--border` | `#3e3e3e` |
### 8.2 布局
```
┌──────────────────────────────────────────────────────────────────────────┐
│ MarkLite - filename.md ─ □ ✕ │
├──────────────────────────────────────────────────────────────────────────┤
│ 📁 打开 │ 💾 保存 │ ✏️ 编辑 │ 👁 预览 │ 🌙 🔗 ℹ️ │
├──────────────────────────────────────────────────────────────────────────┤
│ [file1.md] [file2.md] [未命名] [+] │
├──────────┬─────────────────────────────────────────────────────────────┤
│ 资源管理器 │ │
│ ▼ project │ 1 # Title │ Title │
│ 📁 src │ 2 │ ─────── │
│ 📄 file1│ 3 content... │ content... │
│ 📄 file2│ │ │
├──────────┴─────────────────────────────────────────────────────────────┤
│ filename.md │ UTF-8 │ Markdown │
└────────────────────────────────────────────────────────────────────────┘
```
## 9. 构建与发布
### 9.1 开发模式
```bash
npm run dev # electron-vite devHMR 热更新)
```
### 9.2 生产构建
```bash
npm run build # electron-vite build + electron-builder --win
npm run build:portable # 便携版(免安装)
```
### 9.3 打包配置
- 输出格式:NSIS 安装包(.exe)
- 目标平台:Windows x64
- 应用图标:assets/icon.ico
- 文件关联:`.md` / `.markdown` / `.txt`
- 支持自定义安装目录、桌面/开始菜单快捷方式
## 10. 依赖清单
### 运行时依赖
| 包名 | 版本 | 用途 |
|------|------|------|
| react / react-dom | ^18.3 | UI 框架 |
| zustand | ^5.0 | 状态管理 |
| dexie | ^4.0 | IndexedDB 封装 |
| nanoid | ^5.0 | 唯一 ID 生成 |
| @codemirror/* | ^6.x | 代码编辑器 |
| unified / remark / rehype | ^11.0 | Markdown 渲染管线 |
| rehype-highlight | ^7.0 | 代码语法高亮 |
### 开发依赖
| 包名 | 版本 | 用途 |
|------|------|------|
| electron | ^28.0 | 桌面框架 |
| electron-builder | ^25.0 | 打包工具 |
| electron-vite | ^3.0 | 构建工具 |
| typescript | ^5.6 | 类型系统 |
| eslint | ^9.0 | 代码检查 |