From 4a1c06320f68aa12b780a316cf5ce6349f83bee5 Mon Sep 17 00:00:00 2001 From: thzxx Date: Thu, 28 May 2026 15:53:14 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=20README.md=20?= =?UTF-8?q?=E5=92=8C=20DESIGN.md=EF=BC=8C=E5=88=A0=E9=99=A4=20REFACTOR-PLA?= =?UTF-8?q?N.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - README.md: 更新版本号 v0.1.0,添加 CodeMirror 6 说明,添加关于/Gitee按钮说明 - DESIGN.md: 重写为当前架构,移除重构计划相关内容,精简为实际代码结构 - 删除 REFACTOR-PLAN.md(重构已完成) --- DESIGN.md | 388 ++++++---------------- README.md | 165 +++------- REFACTOR-PLAN.md | 825 ----------------------------------------------- 3 files changed, 144 insertions(+), 1234 deletions(-) delete mode 100644 REFACTOR-PLAN.md diff --git a/DESIGN.md b/DESIGN.md index 33e55dc..566cf1e 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -1,44 +1,44 @@ -# MarkLite v2.0 — 架构设计文档 +# MarkLite v0.1.0 — 架构设计文档 ## 1. 项目概述 -MarkLite 是一款轻量级的 Windows 本地 Markdown 编辑器桌面应用程序。基于 Electron + React + TypeScript 构建,采用 Zustand 状态管理、IndexedDB 持久化、unified/rehype Markdown 渲染管线,提供类型安全、模块化、可测试的现代化架构。 +MarkLite 是一款轻量级的 Windows 本地 Markdown 编辑器桌面应用程序。基于 Electron + React + TypeScript 构建,采用 CodeMirror 6 编辑器、Zustand 状态管理、IndexedDB 持久化、unified/rehype Markdown 渲染管线。 ### 1.1 核心原则 1. **类型安全** — 全量 TypeScript,所有 IPC 通信、状态、接口均有类型定义 -2. **模块化** — 51 个源文件按职责分层:主进程 / 预加载 / 渲染进程(组件 / stores / hooks / lib / db / types) -3. **功能 100% 兼容** — 重构不丢失任何现有功能 -4. **可测试性** — 业务逻辑(lib/)与 UI(components/)解耦,stores 和 lib 可独立测试 +2. **模块化** — 源文件按职责分层:主进程 / 预加载 / 渲染进程(组件 / stores / hooks / lib / db / types) +3. **安全隔离** — contextIsolation + nodeIntegration:false + CSP + rehype-sanitize +4. **可测试性** — 业务逻辑(lib/)与 UI(components/)解耦 ## 2. 技术架构 ### 2.1 技术栈 -| 组件 | 技术选型 | 版本 | 说明 | -|------|----------|------|------| +| 组件 | 技术 | 版本 | 说明 | +|------|------|------|------| | 桌面框架 | Electron | v28 | 跨平台桌面应用框架 | | 前端框架 | React | v18 | 函数组件 + Hooks | | 类型系统 | TypeScript | v5.6 | 全量类型安全 | -| 状态管理 | Zustand | v5 | 轻量级状态管理,selector 优化 | +| 编辑器 | CodeMirror | v6 | 现代化代码编辑器 | +| 状态管理 | Zustand | v5 | 轻量级状态管理 | | 持久化 | Dexie.js (IndexedDB) | v4 | 标签页状态 / 用户设置 / 最近文件 | | Markdown 解析 | unified / remark / rehype | v11 | 插件化渲染管线 | -| 代码高亮 | rehype-highlight | v7 | 基于 highlight.js 的语法高亮 | -| 构建工具 | electron-vite | v3 | Electron + Vite 集成,HMR 热更新 | -| 打包工具 | electron-builder | v25 | 生成 Windows NSIS 安装包 | -| 样式 | CSS Variables + CSS Modules | — | 主题驱动,样式隔离 | +| 代码高亮 | rehype-highlight | v7 | 基于 highlight.js | +| 构建工具 | electron-vite | v3 | Electron + Vite,HMR 热更新 | +| 打包工具 | electron-builder | v25 | Windows NSIS 安装包 | +| 样式 | CSS Variables | — | 主题驱动,亮色/暗色 | ### 2.2 进程架构 ``` ┌──────────────────────────────────────────────────────────────┐ -│ Main Process (src/main/) 6 文件 │ +│ 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 窗口创建、关闭拦截、单实例锁 │ -│ ipc-channels.ts IPC 通道名常量 │ └───────────────────────┬──────────────────────────────────────┘ │ contextBridge (安全隔离) ┌───────────────────────▼──────────────────────────────────────┐ @@ -48,40 +48,25 @@ MarkLite 是一款轻量级的 Windows 本地 Markdown 编辑器桌面应用程 └───────────────────────┬──────────────────────────────────────┘ │ ┌───────────────────────▼──────────────────────────────────────┐ -│ Renderer Process (src/renderer/) 42 文件 React 18 │ +│ Renderer Process (src/renderer/) React 18 │ │ │ -│ ┌─ components/ (11) ──────────────────────────────────────┐ │ -│ │ Toolbar · TabBar · Editor · Preview · Sidebar │ │ -│ │ SearchBar · StatusBar · WelcomeScreen · Toast │ │ -│ │ ModifiedBanner · DropOverlay │ │ -│ └──────────────────────────────────────────────────────────┘ │ -│ ┌─ stores/ (4) ───────────────────────────────────────────┐ │ -│ │ tabStore · editorStore · sidebarStore · searchStore │ │ -│ └──────────────────────────────────────────────────────────┘ │ -│ ┌─ hooks/ (6) ────────────────────────────────────────────┐ │ -│ │ useTheme · useSettings · useKeyboard · useDragDrop │ │ -│ │ useFileWatch · useUnsavedWarning │ │ -│ └──────────────────────────────────────────────────────────┘ │ -│ ┌─ lib/ (5) ──────────────────────────────────────────────┐ │ -│ │ markdown · scrollSync · searchEngine · fileUtils │ │ -│ │ constants │ │ -│ └──────────────────────────────────────────────────────────┘ │ -│ ┌─ db/ (4) ───────────────────────────────────────────────┐ │ -│ │ schema · tabRepository · settingsRepository │ │ -│ │ recentFilesRepository │ │ -│ └──────────────────────────────────────────────────────────┘ │ -│ ┌─ types/ (6) ────────────────────────────────────────────┐ │ -│ │ tab · file · settings · search · ipc · index │ │ -│ └──────────────────────────────────────────────────────────┘ │ -│ ┌─ styles/ (3) ───────────────────────────────────────────┐ │ -│ │ variables.css · global.css · markdown-body.css │ │ -│ └──────────────────────────────────────────────────────────┘ │ +│ components/ (11) Toolbar · TabBar · Editor · Preview │ +│ Sidebar · StatusBar · WelcomeScreen │ +│ Toast · ModifiedBanner · DropOverlay │ +│ │ +│ stores/ (3) tabStore · editorStore · sidebarStore │ +│ hooks/ (6) useTheme · useSettings · useKeyboard │ +│ useDragDrop · useFileWatch · useUnsaved │ +│ lib/ (3) markdown · fileUtils · constants │ +│ db/ (4) schema · tab · settings · recentFiles │ +│ types/ (6) tab · file · settings · ipc · index │ +│ styles/ (3) variables · global · markdown-body │ └──────────────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────┐ │ Shared (src/shared/) 2 文件 │ -│ ipc-channels.ts IPC 通道名常量(主进程/渲染进程共用) │ -│ types.ts 共享类型定义(FileNode, 结果类型等) │ +│ ipc-channels.ts IPC 通道名常量 │ +│ types.ts 共享类型定义 │ └──────────────────────────────────────────────────────────────┘ ``` @@ -92,9 +77,10 @@ MarkLite 是一款轻量级的 Windows 本地 Markdown 编辑器桌面应用程 | Electron | `contextIsolation: true` | 渲染进程与主进程隔离 | | Electron | `nodeIntegration: false` | 渲染进程无法访问 Node.js API | | IPC | `contextBridge.exposeInMainWorld` | 仅暴露 22 个类型安全方法/事件 | -| CSP | `default-src 'self'; script-src 'self'` | 阻断内联脚本、外部资源加载 | -| HTML | `rehype-sanitize` | 渲染 Markdown 时过滤危险标签/属性/JS 协议 | +| CSP | `default-src 'self'; script-src 'self'` | 阻断内联脚本、外部资源 | +| HTML | `rehype-sanitize` | 渲染 Markdown 时过滤危险标签/属性 | | 链接 | 协议白名单 | 仅允许 `http:` / `https:` / `#` 锚点 | +| 路径 | `validatePath()` | 防止路径遍历攻击 | ## 3. 状态管理架构 @@ -108,16 +94,16 @@ MarkLite 是一款轻量级的 Windows 本地 Markdown 编辑器桌面应用程 │ │ │ │ (CM6) │ │ ├─────────┴──────────┴──────────┴──────────┴──────────────┤ │ Zustand Stores │ -│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ -│ │ tabStore │ │editorStore│ │sidebarStore│ │searchStore│ │ -│ │ - tabs │ │- viewMode│ │- tree │ │- matches │ │ -│ │- activeId│ │- darkMode│ │- expanded │ │- index │ │ -│ │ - mru │ │- viewMode│ │- rootPath │ │- options │ │ -│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └──────────┘ │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ +│ │ tabStore │ │editorStore│ │sidebarStore│ │ +│ │ - tabs │ │- viewMode│ │- tree │ │ +│ │- activeId│ │- darkMode│ │- expanded │ │ +│ │ - mru │ │ │ │- rootPath │ │ +│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │ │ │ │ │ │ ┌────▼────────────▼────────────▼────────────────────┐ │ │ │ IndexedDB (Dexie.js) │ │ -│ │ tabSnapshots │ settings │ recentFiles │ │ +│ │ tabSnapshots │ settings │ recentFiles │ activeTab │ │ │ └───────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────┘ ``` @@ -132,11 +118,13 @@ interface TabState { createTab(filePath?, content?) // 创建标签(同文件不重复打开) closeTab(tabId) // 关闭标签(自动切换到相邻标签) - switchToTab(tabId) // 切换标签(保存当前状态 → MRU 记录 → 加载目标) + switchToTab(tabId) // 切换标签 updateTabContent(tabId, content) // 更新内容(标记修改) setModified(tabId, modified) // 设置修改状态 getActiveTab() // 获取当前标签 updateTabScroll(tabId, scroll) // 更新滚动/光标位置 + saveToDB() // 保存到 IndexedDB + loadFromDB() // 从 IndexedDB 加载 } ``` @@ -145,6 +133,7 @@ interface TabState { ```typescript interface EditorState { viewMode: 'editor' | 'preview' // 视图模式 + darkMode: boolean // 暗色主题 } ``` @@ -160,34 +149,20 @@ interface SidebarState { } ``` -### 3.5 searchStore — 搜索状态 - -```typescript -interface SearchState { - isVisible: boolean // 搜索栏是否可见 - showReplace: boolean // 替换行是否展开 - searchText: string // 搜索文本 - replaceText: string // 替换文本 - matches: SearchMatch[] // 所有匹配项 - currentIndex: number // 当前匹配索引 - options: SearchOptions // { caseSensitive, useRegex } -} -``` - ## 4. 数据持久化 — IndexedDB -通过 Dexie.js 封装 IndexedDB,替代 localStorage: +通过 Dexie.js 封装 IndexedDB: ### 4.1 数据库 Schema ```typescript -// db/schema.ts const db = new Dexie('MarkLite') db.version(1).stores({ tabSnapshots: 'id, filePath, updatedAt', // 标签页快照 settings: 'id', // 用户设置 - recentFiles: '++id, filePath, lastOpened' // 最近打开文件 + recentFiles: '++id, filePath, lastOpened', // 最近打开文件 + activeTab: 'id' // 当前活动标签 }) ``` @@ -195,19 +170,10 @@ db.version(1).stores({ | Store | 字段 | 说明 | |-------|------|------| -| `tabSnapshots` | id, filePath, content, scrollTop, scrollLeft, selectionStart, selectionEnd, previewScrollTop, isModified, updatedAt | 标签页状态快照,关闭时保存,启动时恢复 | -| `settings` | id(固定'default'), darkMode, viewMode, sidebarCollapsed, sidebarWidth | 用户偏好设置 | +| `tabSnapshots` | id, filePath, content, scrollTop, selectionStart, selectionEnd, isModified, updatedAt | 标签页状态快照 | +| `settings` | id, darkMode, viewMode, sidebarCollapsed, sidebarWidth | 用户偏好设置 | | `recentFiles` | ++id, filePath, lastOpened | 最近打开文件列表 | - -### 4.3 IndexedDB 优势(vs localStorage) - -| 维度 | localStorage | IndexedDB | -|------|-------------|-----------| -| 容量 | ~5-10MB | 数百 MB | -| API | 同步 | 异步,不阻塞 UI | -| 索引 | 无 | 支持索引查询 | -| 大文件 | 无法缓存 20MB 文件 | 可缓存任意大小文件 | -| 标签恢复 | 关闭后丢失 | 可持久化恢复所有标签 | +| `activeTab` | id, activeTabId | 当前活动标签 ID | ## 5. IPC 通信设计 @@ -216,18 +182,18 @@ db.version(1).stores({ | 通道 | 参数 | 返回值 | 说明 | |------|------|--------|------| | `dialog:openFile` | 无 | `OpenFileResponse` | 打开文件对话框 | -| `file:read` | `filePath: string` | `ReadFileResult` | 读取文件内容 | +| `file:read` | `filePath` | `ReadFileResult` | 读取文件内容 | | `file:save` | `{ filePath, content }` | `SaveFileResult` | 保存文件 | | `file:saveAs` | `{ content }` | `SaveFileResult` | 另存为 | | `file:getCurrentPath` | 无 | `string \| null` | 获取当前文件路径 | -| `file:stats` | `filePath: string` | `FileStatsResult` | 获取文件元信息 | +| `file:stats` | `filePath` | `FileStatsResult` | 获取文件元信息 | | `file:reload` | 无 | `ReloadFileResult` | 重新加载当前文件 | -| `tab:switched` | `filePath: string \| null` | `void` | 通知主进程切换活动文件 | +| `tab:switched` | `filePath \| null` | `void` | 通知主进程切换活动文件 | | `window:forceClose` | 无 | `void` | 强制关闭窗口 | | `window:cancelClose` | 无 | `void` | 取消关闭 | -| `dir:readTree` | `dirPath: string` | `ReadDirTreeResult` | 递归读取目录树 | +| `dir:readTree` | `dirPath` | `ReadDirTreeResult` | 递归读取目录树 | | `dir:openDialog` | 无 | `string \| null` | 打开文件夹选择对话框 | -| `dir:watch` | `dirPath: string` | `void` | 监听目录变化 | +| `dir:watch` | `dirPath` | `void` | 监听目录变化 | | `dir:unwatch` | 无 | `void` | 停止监听目录变化 | ### 5.2 主进程 → 渲染进程(send) @@ -235,148 +201,66 @@ db.version(1).stores({ | 通道 | 数据 | 说明 | |------|------|------| | `file:openInTab` | `{ filePath, content }` | 在新标签中打开文件 | -| `file:externallyModified` | `filePath: string` | 文件被外部修改 | +| `file:externallyModified` | `filePath` | 文件被外部修改 | | `window:confirmClose` | 无 | 请求确认关闭 | -| `sidebar:dirChanged` | 无 | 目录结构变化,通知刷新树 | +| `sidebar:dirChanged` | 无 | 目录结构变化 | -### 5.3 类型安全 +## 6. 编辑器架构 — CodeMirror 6 -所有 IPC 通道通过 `src/shared/types.ts` 和 `src/renderer/types/ipc.ts` 定义类型: +### 6.1 功能特性 -```typescript -// src/renderer/types/ipc.ts -export interface ElectronAPI { - openFile: () => Promise - readFile: (filePath: string) => Promise - saveFile: (data: SaveFilePayload) => Promise - // ... 共 22 个方法/事件 -} -``` +- Markdown 语法高亮 +- 行号显示 +- 代码折叠 (foldGutter) +- 括号匹配 (bracketMatching) +- 搜索替换 (search) — 中文本地化 +- 历史记录 (history) — 支持 undo/redo +- Tab 缩进 (indentWithTab) +- 自动换行 (lineWrapping) +- 暗色主题 (oneDark) -## 6. 标签页数据模型 +### 6.2 滚动与选区持久化 -```typescript -// src/renderer/types/tab.ts -export interface Tab { - id: string // 唯一标识(自增计数器) - filePath: string | null // 文件路径(未命名标签为 null) - content: string // 编辑器内容 - isModified: boolean // 是否已修改 - scrollTop: number // 编辑器滚动位置 - scrollLeft: number - selectionStart: number // 光标选区 - selectionEnd: number - previewScrollTop: number // 预览面板滚动位置 -} -``` - -切换标签时自动保存当前状态、恢复目标状态。使用 `execCommand('insertText')` 替换内容以保留 undo/redo 历史。 +切换标签时自动保存/恢复: +- 滚动位置 (`scrollTop`) +- 光标选区 (`selectionStart`, `selectionEnd`) ## 7. Markdown 渲染管线 -### 7.1 渲染流程 - ``` Markdown 文本 │ ▼ -remark-parse 解析为 MDAST(Markdown AST) +remark-parse 解析为 MDAST │ ▼ -remark-gfm 扩展 GFM 语法(表格、任务列表、删除线) +remark-gfm 扩展 GFM 语法 │ ▼ -remark-rehype 转换为 HAST(HTML AST) +remark-rehype 转换为 HAST │ ▼ rehype-raw 解析内联 HTML │ ▼ -rehype-sanitize 安全过滤(移除 script/iframe/on* 事件/javascript: 协议) +rehype-sanitize 安全过滤 │ ▼ -rehype-highlight 代码块语法高亮(180+ 语言) +rehype-fixImages 相对路径图片转 file:// URL │ ▼ -rehype-stringify 序列化为 HTML 字符串 +rehype-highlight 代码语法高亮 │ ▼ -dangerouslySetInnerHTML 渲染到 React DOM +rehype-stringify 序列化为 HTML + │ + ▼ +dangerouslySetInnerHTML 渲染到 DOM ``` -### 7.2 支持的语法 +## 8. UI 设计 -- 标题(h1-h6)、段落、换行 -- **粗体**、*斜体*、~~删除线~~ -- 有序/无序列表、任务列表 `- [x]` -- 代码块(围栏式 + 语法高亮,180+ 语言)、行内代码 -- 链接、图片(支持相对路径转 file:// URL) -- 表格、引用块、水平线 -- HTML 内联元素 -- GFM(GitHub Flavored Markdown) - -## 8. 滚动同步算法 - -基于**块级元素 DOM 位置映射**的编辑器-预览滚动同步: - -``` -1. 识别编辑器中的块级元素起始行 - - 标题 (#)、列表 (-/*/+)、引用 (>)、表格 (|) - - 围栏代码块 (```)、水平线 (---)、段落分隔(连续空行) - -2. 获取预览面板中每个 DOM 子元素的 offsetTop - -3. 将块起始行号映射到预览 DOM 位置 - - 块起始行数 : 预览 DOM 元素数 = 线性对应 - -4. 线性插值填充所有中间行 - - 对于非块起始行,找到前后最近的已映射行 - - 按行号比例插值计算预览位置 - -5. 滚动时通过映射表直接查找目标位置 - - O(1) 查找,无需遍历 -``` - -## 9. 搜索替换引擎 - -### 9.1 匹配算法 - -``` -普通文本搜索: - - 大小写不敏感时:haystack.toLowerCase().indexOf(needle) - - 大小写敏感时:haystack.indexOf(needle) - - O(N) 线性扫描 - -正则表达式搜索: - - new RegExp(text, flags) - - 循环 exec() 收集所有匹配 - - 空匹配保护:regex.lastIndex++ -``` - -### 9.2 高亮渲染 - -``` -字符位置 → 像素坐标转换: - 1. 构建 lineStarts 缓存(每行首字符的偏移量) - 2. 二分查找 O(log N):字符位置 → 行号 - 3. 列号 = 字符位置 - lineStarts[行号] - 4. top = 行号 × 行高 + paddingTop - 5. left = 列号 × 字符宽度 + paddingLeft - -搜索高亮 overlay: - - 绝对定位 div,覆盖在 textarea 上方 - - pointer-events: none 不拦截编辑器交互 - - 滚动时同步更新 overlay top 偏移 -``` - -### 9.3 替换策略 - -- **替换当前**:使用 `execCommand('insertText')` 保留 undo 历史 -- **全部替换**:从后往前拼接,一次 `execCommand('insertText')` 原子操作,Ctrl+Z 可一次性撤销 - -## 10. UI 设计 - -### 10.1 色彩方案 +### 8.1 色彩方案 #### 亮色主题 @@ -385,10 +269,7 @@ dangerouslySetInnerHTML 渲染到 React DOM | 主色调 | `--primary` | `#1a73e8` | | 背景色 | `--bg` | `#ffffff` | | 次级背景 | `--bg-secondary` | `#f8f9fa` | -| 三级背景 | `--bg-tertiary` | `#f1f3f4` | | 文字色 | `--text` | `#333333` | -| 次级文字 | `--text-secondary` | `#5f6368` | -| 代码块背景 | `--code-bg` | `#f6f8fa` | | 边框色 | `--border` | `#e1e4e8` | #### 暗色主题 @@ -398,100 +279,45 @@ dangerouslySetInnerHTML 渲染到 React DOM | 主色调 | `--primary` | `#8ab4f8` | | 背景色 | `--bg` | `#1e1e1e` | | 次级背景 | `--bg-secondary` | `#252526` | -| 三级背景 | `--bg-tertiary` | `#2d2d2d` | | 文字色 | `--text` | `#d4d4d4` | -| 次级文字 | `--text-secondary` | `#9e9e9e` | -| 代码块背景 | `--code-bg` | `#2d2d2d` | | 边框色 | `--border` | `#3e3e3e` | -### 10.2 字体 - -- **UI 字体**: `system-ui, -apple-system, "Segoe UI", Roboto, sans-serif` -- **编辑器字体**: `"Cascadia Code", "Fira Code", "JetBrains Mono", Consolas, monospace` -- **预览字体**: 同 UI 字体 - -### 10.3 布局 +### 8.2 布局 ``` ┌──────────────────────────────────────────────────────────────────────────┐ │ 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 │ -└──────────────────────────────────────────────────────────────────────────┘ +├──────────┬─────────────────────────────────────────────────────────────┤ +│ 资源管理器 │ │ +│ ▼ project │ 1 # Title │ Title │ +│ 📁 src │ 2 │ ─────── │ +│ 📄 file1│ 3 content... │ content... │ +│ 📄 file2│ │ │ +├──────────┴─────────────────────────────────────────────────────────────┤ +│ filename.md │ UTF-8 │ Markdown │ +└────────────────────────────────────────────────────────────────────────┘ ``` -## 11. 快捷键 +## 9. 构建与发布 -| 快捷键 | 功能 | -|:-------|:-----| -| `Ctrl + T` | 新建标签页 | -| `Ctrl + W` | 关闭当前标签页 | -| `Ctrl + Tab` | 切换到下一个标签页(MRU 顺序) | -| `Ctrl + Shift + Tab` | 切换到上一个标签页 | -| `Ctrl + O` | 打开文件 | -| `Ctrl + S` | 保存文件 | -| `Ctrl + Shift + S` | 另存为 | -| `Ctrl + 1` | 编辑模式 | -| `Ctrl + 2` | 预览模式 | -| `Ctrl + F` | 搜索 | -| `Ctrl + H` | 搜索并替换 | -| `Enter` / `Shift+Enter` | 下一个 / 上一个匹配 | -| `Alt + C` | 切换区分大小写 | -| `Alt + R` | 切换正则表达式 | -| `Ctrl + Shift + G` | 替换当前匹配 | -| `Ctrl + Shift + H` | 全部替换 | -| `Esc` | 关闭搜索栏 | - -## 12. 构建与发布 - -### 12.1 开发模式 +### 9.1 开发模式 ```bash npm run dev # electron-vite dev(HMR 热更新) ``` -### 12.2 生产构建 +### 9.2 生产构建 ```bash npm run build # electron-vite build + electron-builder --win npm run build:portable # 便携版(免安装) ``` -### 12.3 构建产物 - -``` -dist/ -├── main/index.js 15 kB 主进程(TypeScript 编译) -├── preload/index.js 2 kB 预加载脚本 -└── renderer/ - ├── index.html 入口 HTML - ├── assets/index-*.css 18 kB 样式 - └── assets/index-*.js 1.4 MB React 应用 -``` - -### 12.4 代码检查 - -```bash -npm run typecheck # TypeScript 类型检查 -npm run lint # ESLint 代码检查 -``` - -### 12.5 打包配置 +### 9.3 打包配置 - 输出格式:NSIS 安装包(.exe) - 目标平台:Windows x64 @@ -499,26 +325,18 @@ npm run lint # ESLint 代码检查 - 文件关联:`.md` / `.markdown` / `.txt` - 支持自定义安装目录、桌面/开始菜单快捷方式 -详见 [DEVSETUP.md](DEVSETUP.md)。 - -## 13. 依赖清单 +## 10. 依赖清单 ### 运行时依赖 | 包名 | 版本 | 用途 | |------|------|------| -| react | ^18.3 | UI 框架 | -| react-dom | ^18.3 | React DOM 渲染 | +| react / react-dom | ^18.3 | UI 框架 | | zustand | ^5.0 | 状态管理 | | dexie | ^4.0 | IndexedDB 封装 | | nanoid | ^5.0 | 唯一 ID 生成 | -| unified | ^11.0 | Markdown 处理管线 | -| remark-parse | ^11.0 | Markdown 解析器 | -| remark-gfm | ^4.0 | GFM 扩展 | -| remark-rehype | ^11.1 | MDAST → HAST 转换 | -| rehype-raw | ^7.0 | 内联 HTML 解析 | -| rehype-sanitize | ^6.0 | HTML 安全过滤 | -| rehype-stringify | ^10.0 | HAST → HTML 序列化 | +| @codemirror/* | ^6.x | 代码编辑器 | +| unified / remark / rehype | ^11.0 | Markdown 渲染管线 | | rehype-highlight | ^7.0 | 代码语法高亮 | ### 开发依赖 @@ -528,9 +346,5 @@ npm run lint # ESLint 代码检查 | electron | ^28.0 | 桌面框架 | | electron-builder | ^25.0 | 打包工具 | | electron-vite | ^3.0 | 构建工具 | -| @vitejs/plugin-react | ^4.3 | Vite React 插件 | | typescript | ^5.6 | 类型系统 | -| @types/react | ^18.3 | React 类型 | -| @types/react-dom | ^18.3 | React DOM 类型 | | eslint | ^9.0 | 代码检查 | -| @typescript-eslint/eslint-plugin | ^8.0 | TypeScript ESLint 规则 | diff --git a/README.md b/README.md index da290e2..1d0a458 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ TypeScript React License - Version + Version

@@ -30,7 +30,7 @@ |------|------| | 📑 **多标签页** | 同时打开多个文件,Ctrl+T 新建、Ctrl+W 关闭、Ctrl+Tab MRU 切换 | | 📂 **文件打开** | 按钮打开 / 拖拽打开 / 文件关联(双击 .md) / 命令行参数 | -| ✏️ **实时编辑** | 左侧编辑器,支持 Tab 缩进、行号显示、光标位置、大文件虚拟化行号 | +| ✏️ **实时编辑** | 左侧编辑器(CodeMirror 6),支持 Tab 缩进、行号显示、折叠、括号匹配 | | 👁 **实时预览** | 右侧预览面板,基于 unified/rehype 管线渲染,编辑即更新 | | 🔤 **代码高亮** | 基于 rehype-highlight,支持 180+ 种编程语言语法高亮 | | 🎨 **两种视图** | 编辑模式 / 预览模式,自由切换 | @@ -43,32 +43,6 @@ | ⌨️ **快捷键** | 完整的键盘快捷键支持,操作高效 | | 📦 **NSIS 安装包** | 一键打包为 Windows exe 安装程序 / 便携版 | -## 📸 界面预览 - -``` -┌──────────────────────────────────────────────────────────────────────────┐ -│ MarkLite - README.md ─ □ ✕ │ -├──────────────────────────────────────────────────────────────────────────┤ -│ 📁 打开 │ 💾 保存 │ ✏️ 编辑 │ 👁 预览 │ 🌙 │ -├──────────────────────────────────────────────────────────────────────────┤ -│ [README.md] [DESIGN.md] [main.ts] [+] │ -├──────────┬─────────────────────────┬─────────────────────────────────────┤ -│ 资源管理器│ │ │ -│ ▼ MarkLite│ 🔍 查找... 3/12 │ MarkLite │ -│ 📁 src │ [替换... ] [替换][全部]│ ───────────── │ -│ 📄 README│ │ │ -│ 📄 DESIGN│ 1 # MarkLite │ 一款轻量级的 Windows 本地 │ -│ 📁 lib │ 2 │ Markdown 编辑器桌面应用... │ -│ 📁 assets│ 3 一款轻量级的... │ │ -│ │ 4 │ ■ 实时编辑 │ -│ │ 5 ## 功能特性 │ ■ 实时预览 │ -│ │ 6 │ ■ 代码高亮 │ -│ │ 7 - 📝 实时编辑 │ │ -├──────────┴─────────────────────────┴─────────────────────────────────────┤ -│ README.md │ UTF-8 │ Markdown │ 2.1 KB │ 行 9, 列 12 │ -└──────────────────────────────────────────────────────────────────────────┘ -``` - ## 🚀 快速开始 ### 环境要求 @@ -128,11 +102,9 @@ npm run lint | `Ctrl + 2` | 预览模式 | | `Ctrl + F` | 搜索 | | `Ctrl + H` | 搜索并替换 | +| `Ctrl + B` | 粗体 | +| `Ctrl + I` | 斜体 | | `Enter` / `Shift+Enter` | 下一个 / 上一个匹配 | -| `Alt + C` | 切换区分大小写 | -| `Alt + R` | 切换正则表达式 | -| `Ctrl + Shift + G` | 替换当前匹配 | -| `Ctrl + Shift + H` | 全部替换 | | `Esc` | 关闭搜索栏 | ## 🛠️ 技术栈 @@ -142,13 +114,14 @@ npm run lint | 桌面框架 | [Electron](https://www.electronjs.org/) v28 | 跨平台桌面应用框架 | | 前端框架 | [React](https://react.dev/) v18 | 函数组件 + Hooks | | 类型系统 | [TypeScript](https://www.typescriptlang.org/) v5.6 | 全量类型安全 | +| 编辑器 | [CodeMirror](https://codemirror.net/) v6 | 现代化代码编辑器 | | 状态管理 | [Zustand](https://zustand-demo.pmnd.rs/) v5 | 轻量级状态管理 | | 持久化 | [Dexie.js](https://dexie.org/) v4 (IndexedDB) | 标签页状态 & 用户设置持久化 | | Markdown 解析 | [unified](https://unifiedjs.com/) / [remark](https://remark.js.org/) / [rehype](https://rehype.js.org/) | 插件化 Markdown 渲染管线 | | 代码高亮 | [rehype-highlight](https://github.com/rehypejs/rehype-highlight) | 基于 highlight.js 的语法高亮 | | 构建工具 | [electron-vite](https://electron-vite.org/) v3 | Electron + Vite 集成,HMR 热更新 | | 打包工具 | [electron-builder](https://www.electron.build/) | 生成 exe 安装包 | -| 样式 | CSS Variables + CSS Modules | 主题驱动,样式隔离 | +| 样式 | CSS Variables | 主题驱动,亮色/暗色切换 | ## 📁 项目结构 @@ -161,125 +134,73 @@ MarkLite/ ├── .eslintrc.cjs # ESLint + TypeScript 规则 │ ├── src/ -│ ├── main/ # ===== 主进程 (Node.js) ===== +│ ├── main/ # 主进程 (Node.js) │ │ ├── index.ts # 入口:窗口创建、app 生命周期、单实例锁 │ │ ├── ipc-handlers.ts # 所有 ipcMain.handle 注册 │ │ ├── file-system.ts # 文件读写、目录树构建、BOM 剥离 │ │ ├── file-watcher.ts # fs.watch 封装(单文件 + 目录监听) -│ │ ├── window-manager.ts # 窗口创建、关闭拦截、单实例锁 -│ │ └── ipc-channels.ts # IPC 通道名常量 +│ │ └── window-manager.ts # 窗口创建、关闭拦截、单实例锁 │ │ -│ ├── preload/ # ===== 预加载脚本 ===== +│ ├── preload/ # 预加载脚本 │ │ └── index.ts # contextBridge 类型安全暴露 │ │ -│ ├── renderer/ # ===== 渲染进程 (React) ===== +│ ├── renderer/ # 渲染进程 (React 18) │ │ ├── index.html # 入口 HTML(含 CSP 策略) -│ │ ├── main.tsx # React 入口 (createRoot) +│ │ ├── main.tsx # React 入口 │ │ ├── App.tsx # 根组件:布局编排、全局事件 │ │ │ -│ │ ├── components/ # ----- UI 组件 ----- -│ │ │ ├── Toolbar/ # 工具栏(打开、保存、视图切换、暗色主题) -│ │ │ ├── TabBar/ # 标签页栏(多标签、关闭、MRU 切换) -│ │ │ ├── Editor/ # 编辑器(行号、大文件虚拟化、Tab 缩进) -│ │ │ ├── Preview/ # Markdown 预览面板(unified/rehype 渲染) -│ │ │ ├── Sidebar/ # 侧边栏文件树(递归、展开折叠、独立文件区) -│ │ │ ├── SearchBar/ # 搜索替换栏(高亮、正则、大小写) -│ │ │ ├── StatusBar/ # 状态栏(文件名、编码、语言) +│ │ ├── components/ # UI 组件 +│ │ │ ├── Toolbar/ # 工具栏 +│ │ │ ├── TabBar/ # 标签页栏 +│ │ │ ├── Editor/ # CodeMirror 6 编辑器 +│ │ │ ├── Preview/ # Markdown 预览面板 +│ │ │ ├── Sidebar/ # 侧边栏文件树 +│ │ │ ├── StatusBar/ # 状态栏 │ │ │ ├── WelcomeScreen/ # 欢迎屏幕 │ │ │ ├── Toast/ # Toast 通知 -│ │ │ ├── ModifiedBanner/ # 文件外部修改提示横幅 -│ │ │ └── DropOverlay/ # 拖拽文件覆盖层 +│ │ │ ├── ModifiedBanner/ # 文件外部修改提示 +│ │ │ ├── DropOverlay/ # 拖拽文件覆盖层 +│ │ │ └── Icons.tsx # SVG 图标库 │ │ │ -│ │ ├── stores/ # ----- Zustand 状态管理 ----- -│ │ │ ├── tabStore.ts # 标签页状态(tabs、activeTabId、mruStack) -│ │ │ ├── editorStore.ts # 编辑器状态(viewMode、darkMode) -│ │ │ ├── sidebarStore.ts # 侧边栏状态(tree、expandedDirs、rootPath) -│ │ │ └── searchStore.ts # 搜索替换状态(matches、currentIndex、options) +│ │ ├── stores/ # Zustand 状态管理 +│ │ │ ├── tabStore.ts # 标签页状态 +│ │ │ ├── editorStore.ts # 编辑器状态 +│ │ │ └── sidebarStore.ts # 侧边栏状态 │ │ │ -│ │ ├── hooks/ # ----- 自定义 Hooks ----- -│ │ │ ├── useTheme.ts # 暗色/亮色主题(IndexedDB 持久化) -│ │ │ ├── useSettings.ts # 用户设置读写 +│ │ ├── hooks/ # 自定义 Hooks +│ │ │ ├── useTheme.ts # 暗色/亮色主题 +│ │ │ ├── useSettings.ts # 用户设置 │ │ │ ├── useKeyboard.ts # 全局快捷键 -│ │ │ ├── useDragDrop.ts # 拖拽打开文件 -│ │ │ ├── useFileWatch.ts # 外部文件修改监听 -│ │ │ └── useUnsavedWarning.ts # 未保存提醒(Electron + 浏览器兼容) +│ │ │ ├── useDragDrop.ts # 拖拽打开 +│ │ │ ├── useFileWatch.ts # 外部修改监听 +│ │ │ └── useUnsavedWarning.ts # 未保存提醒 │ │ │ -│ │ ├── lib/ # ----- 工具库 ----- -│ │ │ ├── markdown.ts # Markdown 渲染管线(unified → remark → rehype) -│ │ │ ├── scrollSync.ts # 滚动同步算法(块级元素 DOM 位置映射) -│ │ │ ├── searchEngine.ts # 搜索匹配引擎(普通/正则/大小写/二分查找) -│ │ │ ├── fileUtils.ts # 文件大小格式化、扩展名检查 +│ │ ├── lib/ # 工具库 +│ │ │ ├── markdown.ts # Markdown 渲染管线 +│ │ │ ├── fileUtils.ts # 文件工具函数 │ │ │ └── constants.ts # 常量定义 │ │ │ -│ │ ├── db/ # ----- IndexedDB 持久化层 ----- -│ │ │ ├── schema.ts # Dexie 数据库定义 + 类型(tabSnapshots/settings/recentFiles) -│ │ │ ├── tabRepository.ts # 标签页状态 CRUD -│ │ │ ├── settingsRepository.ts # 用户设置 CRUD -│ │ │ └── recentFilesRepository.ts # 最近打开文件 +│ │ ├── db/ # IndexedDB 持久化层 +│ │ │ ├── schema.ts # Dexie 数据库定义 +│ │ │ ├── tabRepository.ts # 标签页 CRUD +│ │ │ ├── settingsRepository.ts # 设置 CRUD +│ │ │ └── recentFilesRepository.ts # 最近文件 │ │ │ -│ │ ├── types/ # ----- TypeScript 类型 ----- -│ │ │ ├── tab.ts # Tab 接口 -│ │ │ ├── file.ts # FileNode, DirTree 等 -│ │ │ ├── settings.ts # Settings 接口 + 默认值 -│ │ │ ├── search.ts # SearchMatch, SearchOptions -│ │ │ ├── ipc.ts # ElectronAPI 接口 + IPC 通道类型映射 -│ │ │ └── index.ts # 类型统一导出 -│ │ │ -│ │ └── styles/ # ----- 全局样式 ----- -│ │ ├── variables.css # CSS 变量(亮色/暗色主题) -│ │ ├── global.css # 全局 reset + 布局 + 组件样式 -│ │ └── markdown-body.css # Markdown 预览正文样式 +│ │ ├── types/ # TypeScript 类型 +│ │ └── styles/ # 全局样式 │ │ -│ └── shared/ # ===== 主进程/渲染进程共享 ===== +│ └── shared/ # 主进程/渲染进程共享 │ ├── ipc-channels.ts # IPC 通道名常量 │ └── types.ts # 共享类型定义 │ -├── lib/ # 第三方离线库(marked.js, highlight.js) ├── assets/ -│ └── icon.ico # 应用图标(多尺寸) +│ └── icon.ico # 应用图标 ├── DESIGN.md # 架构设计文档 ├── DEVSETUP.md # 开发环境配置指南 -├── REFACTOR-PLAN.md # 重构方案文档 ├── LICENSE # MIT 许可证 └── README.md # 本文件 ``` -## 🏗️ 架构设计 - -``` -┌─────────────────────────────────────────────────────────────┐ -│ Main Process (src/main/) │ -│ 窗口管理 · 文件系统 · 文件监听 · IPC · 目录树 · 单实例锁 │ -└──────────────────────┬──────────────────────────────────────┘ - │ contextBridge (安全隔离) -┌──────────────────────▼──────────────────────────────────────┐ -│ Preload Script (src/preload/) │ -│ 类型安全 API 桥接层 │ -└──────────────────────┬──────────────────────────────────────┘ - │ -┌──────────────────────▼──────────────────────────────────────┐ -│ Renderer Process (src/renderer/) — React 18 │ -│ │ -│ ┌─────────────────────────────────────────────────────┐ │ -│ │ Components (11 个) │ │ -│ │ Toolbar · TabBar · Editor · Preview · Sidebar │ │ -│ │ SearchBar · StatusBar · WelcomeScreen · Toast │ │ -│ │ ModifiedBanner · DropOverlay │ │ -│ └─────────────────────────────────────────────────────┘ │ -│ ┌─────────────────────────────────────────────────────┐ │ -│ │ Zustand Stores (4 个) │ │ -│ │ tabStore · editorStore · sidebarStore · searchStore │ │ -│ └─────────────────────────────────────────────────────┘ │ -│ ┌─────────────────────────────────────────────────────┐ │ -│ │ IndexedDB (Dexie.js) │ │ -│ │ tabSnapshots · settings · recentFiles │ │ -│ └─────────────────────────────────────────────────────┘ │ -└─────────────────────────────────────────────────────────────┘ - -安全策略:contextIsolation: true + nodeIntegration: false + CSP -渲染进程无法直接访问 Node.js API -``` - ## 📝 支持的 Markdown 语法 - ✅ 标题(h1 ~ h6) diff --git a/REFACTOR-PLAN.md b/REFACTOR-PLAN.md deleted file mode 100644 index a588b2a..0000000 --- a/REFACTOR-PLAN.md +++ /dev/null @@ -1,825 +0,0 @@ -# MarkLite 重构方案:TypeScript + React + IndexedDB - -## 一、重构目标与原则 - -| 维度 | 现状 | 目标 | -|------|------|------| -| 语言 | JavaScript (无类型) | **TypeScript 5.x** (全量类型) | -| UI 框架 | 原生 DOM 操作 | **React 18** (函数组件 + Hooks) | -| 状态管理 | 全局变量 + 闭包 | **Zustand** (轻量状态管理) | -| 持久化 | localStorage | **IndexedDB** (via Dexie.js) | -| 样式 | 纯 CSS | **CSS Modules** + CSS 变量 | -| 构建 | 无打包 | **Vite** + electron-vite | -| Markdown | marked.min.js (离线) | **unified/remark/rehype** 生态 | -| 代码高亮 | highlight.js (离线) | **Shiki** 或 **rehype-highlight** | -| 编辑器 | `