基于 Claude Code 源码二次开发,构建一个 PyCharm 风格的 AI Markdown 编辑器。
核心理念:用户下载安装后,只需配置 Anthropic API Key,就能在一个桌面应用中完成 Markdown 写作、实时预览、AI 辅助编辑的全部工作流,无需打开终端、无需额外工具。
目标用户旅程:
下载安装包 → 安装 → 首次启动
│
├─→ 检测 Bun 运行时 → 未安装则引导一键安装
│
├─→ 输入 Anthropic API Key → 加密存储到本地
│
└─→ 进入编辑器 ← 此后每次打开直接可用
│
├─ 左侧文件树浏览/管理 Markdown 文件
├─ 中央编辑 + 实时预览
└─ 底部 Claude Code 对话终端(AI 已就绪)
┌──────────────────────────────────────────────────────────┐
│ 菜单栏: 文件 | 编辑 | 视图 | 工具 | 主题 | 帮助 │
├────────┬────────────────────────────────┬────────────────┤
│ │ │ │
│ 文件树 │ Markdown 编辑区 │ 实时预览 │
│ │ (CodeMirror 6) │ (markdown-it) │
│ 📁 docs│ │ │
│ ├─ a.md │ # 标题 │ ┌────────┐ │
│ ├─ b.md │ **加粗** │ │ 渲染结果 │ │
│ 📁 src │ - 列表 │ └────────┘ │
│ │ │ │
├────────┴────────────────────────────────┴────────────────┤
│ AI 对话终端 (xterm.js + Claude Code) │
│ > 帮我优化这段 Markdown... │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Claude: 好的,我来帮你... │ │
│ └──────────────────────────────────────────────────────┘ │
├──────────────────────────────────────────────────────────┤
│ 状态栏: README.md | 行 42:列 10 | 字数 2300 | Markdown │
└──────────────────────────────────────────────────────────┘
- 左侧面板:文件目录树 + 大纲(TOC)
- 中央:Markdown 编辑区 + 右侧实时预览区(双栏可切换)
- 底部面板:Claude Code AI 对话终端(可折叠/拖拽调整高度)
- Claude Code 原有的 AI 对话能力完整保留,作为编辑器的智能助手嵌入底部终端。
已确认的技术决策:
| 决策项 | 选择 | 说明 |
|---|---|---|
| 应用形态 | Electron 桌面应用 | 对标 Typora/VS Code,支持文件关联、原生窗口、系统菜单。不选择纯 Web 方案(浏览器标签页缺乏原生感,无法双击 .md 直接打开) |
| Bun 运行时 | 外部依赖(用户自行安装) | 不嵌入 Electron。首次启动自动检测 bun 命令,未安装则引导用户一键安装(类似 VS Code 提示装 Git)。避免安装包体积膨胀 ~80MB |
技术选型表:
| 层级 | 技术 | 理由 |
|---|---|---|
| 桌面容器 | Electron 28+ | 跨平台桌面应用,可集成 Node.js 能力(文件系统、子进程),支持文件关联和原生菜单 |
| 前端框架 | React 18 + TypeScript | 与 Claude Code 现有技术栈一致,复用组件体系 |
| 编辑器内核 | CodeMirror 6 | 模块化架构,Markdown 语法高亮成熟,扩展性强 |
| Markdown 渲染 | markdown-it + 插件生态 | 支持 GFM、表格、脚注、数学公式、Mermaid 等 |
| 终端模拟 | xterm.js | 在 Electron 渲染进程中嵌入 Claude Code 终端 |
| AI 引擎 | Claude Code(原项目核心) | 保留完整 query.ts / QueryEngine.ts / REPL 逻辑,通过 Bun 子进程运行 |
| AI 运行时 | Bun(外部依赖) | Claude Code 源码使用 bun:bundle 等 Bun 专有 API,必须通过 Bun 执行。应用首次启动时自动检测并引导安装 |
| 进程通信 | Electron IPC + PTY (node-pty) | 主进程管理 PTY,渲染进程通过 IPC 读写终端 |
| API Key 管理 | 本地加密存储 | 用户配置的 Anthropic API Key 加密后存入本地用户数据目录,仅通过环境变量传递给 Claude Code 子进程 |
| 状态管理 | Zustand | 轻量、与 React 深度集成,替代 Claude Code 原有的简单 pub/sub store |
| 样式方案 | Tailwind CSS + CSS Modules | 快速构建 UI,支持主题切换 |
| 构建工具 | Vite (渲染进程) + esbuild (主进程) | 开发体验好,HMR 热更新 |
| 包管理 | pnpm(开发时)/ Bun(运行 Claude Code) | pnpm 管理编辑器自身依赖;Bun 仅用于运行 Claude Code 子进程 |
Claude Code 的 UI 基于 Ink(React → Terminal 渲染器),本质是 TUI(文本用户界面),无法渲染图形化的 Markdown 预览、代码高亮、文件树等组件。因此需要在 Ink 之上叠加一层 Electron GUI,将 Claude Code 的交互能力嵌入到 xterm.js 终端组件中,同时在 Electron 渲染进程中使用真正的浏览器 DOM 渲染编辑器 UI。
┌─────────────────────────────────────────────────────────────┐
│ Electron 主进程 (Main Process) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ 窗口管理器 │ │ 文件系统服务 │ │ PTY 进程管理器 │ │
│ │ (BrowserWin) │ │ (fs/path) │ │ (node-pty) │ │
│ └──────────────┘ └──────────────┘ └────────┬─────────┘ │
│ │ │
│ ┌────────────────▼──────────┐ │
│ │ Claude Code 后端进程 │ │
│ │ (Bun 运行时) │ │
│ │ - query.ts │ │
│ │ - QueryEngine.ts │ │
│ │ - REPL 逻辑 │ │
│ │ - 工具系统 (Bash/Read/ │ │
│ │ Edit/Grep/Agent...) │ │
│ └────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ IPC Bridge (contextBridge + ipcMain) │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
│ IPC 通道
▼
┌─────────────────────────────────────────────────────────────┐
│ Electron 渲染进程 (Renderer Process) │
│ │
│ ┌──────────┬──────────────────────────┬─────────────────┐ │
│ │ 左侧面板 │ 中央编辑区 │ 右侧预览区 │ │
│ │ │ │ │ │
│ │ 文件树 │ CodeMirror 6 │ markdown-it │ │
│ │ (Tree) │ - 语法高亮 │ 渲染结果 │ │
│ │ │ - 自动补全 │ - 标题/列表 │ │
│ │ 大纲 │ - 快捷键绑定 │ - 代码块高亮 │ │
│ │ (TOC) │ - 滚动同步 ───────────►│ - 表格 │ │
│ │ │ │ - 数学公式 │ │
│ │ │ │ - Mermaid 图表 │ │
│ └──────────┴──────────────────────────┴─────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 底部面板 (可折叠/拖拽调整高度) │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ AI 终端 (xterm.js) │ │ │
│ │ │ - Claude Code REPL 输出 │ │ │
│ │ │ - 用户输入区 │ │ │
│ │ │ - 工具调用结果展示 │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ 状态栏: 文件名 | 行:列 | 字数 | 文件类型 | 主题 │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
claude-md-editor/
├── electron/ # Electron 主进程
│ ├── main.ts # 主入口:窗口创建、菜单、IPC 注册
│ ├── preload.ts # contextBridge:暴露安全 API 到渲染进程
│ ├── ipc/
│ │ ├── fileSystem.ts # 文件操作 IPC handlers(打开/保存/目录树)
│ │ ├── terminal.ts # PTY 终端 IPC handlers
│ │ ├── export.ts # 导出 IPC handlers(HTML/PDF/DOCX/图片)
│ │ └── imageUpload.ts # 图片上传 IPC handlers(本地存储/图床)
│ ├── pty/
│ │ └── claudeTerminal.ts # node-pty 封装,启动 Claude Code 进程
│ ├── menu/
│ │ └── appMenu.ts # 原生菜单栏定义
│ └── utils/
│ └── paths.ts # 资源路径、用户数据路径工具
│
├── src/ # 渲染进程 (React 应用)
│ ├── main.tsx # React 入口
│ ├── App.tsx # 根组件:布局容器
│ │
│ ├── components/
│ │ ├── layout/
│ │ │ ├── AppShell.tsx # 顶层布局:左/中右/底 三段式
│ │ │ ├── SplitPane.tsx # 可拖拽分割面板
│ │ │ ├── Sidebar.tsx # 左侧边栏容器
│ │ │ └── BottomPanel.tsx # 底部面板容器(可折叠)
│ │ │
│ │ ├── file-tree/
│ │ │ ├── FileTree.tsx # 文件目录树组件
│ │ │ ├── FileTreeNode.tsx # 单个树节点
│ │ │ └── FileTreeContextMenu.tsx # 右键菜单(新建/删除/重命名)
│ │ │
│ │ ├── editor/
│ │ │ ├── EditorPane.tsx # 编辑器面板(CodeMirror 6 封装)
│ │ │ ├── EditorTabs.tsx # 多标签页管理
│ │ │ ├── extensions/ # CodeMirror 6 扩展
│ │ │ │ ├── markdownSyntax.ts # Markdown 语法高亮
│ │ │ │ ├── autocomplete.ts # 自动补全(括号/链接/图片)
│ │ │ │ ├── keybindings.ts # 快捷键定义
│ │ │ │ └── scrollSync.ts # 滚动同步扩展
│ │ │ └── toolbar/
│ │ │ ├── EditorToolbar.tsx # 工具栏按钮
│ │ │ └── ToolbarButton.tsx # 单个按钮
│ │ │
│ │ ├── preview/
│ │ │ ├── PreviewPane.tsx # 实时预览面板
│ │ │ ├── MarkdownRenderer.tsx # markdown-it 渲染器封装
│ │ │ └── preview-plugins/ # 预览插件
│ │ │ ├── codeHighlight.ts # 代码语法高亮 (Prism/highlight.js)
│ │ │ ├── mathRenderer.ts # KaTeX 数学公式渲染
│ │ │ ├── mermaidRenderer.ts # Mermaid 图表渲染
│ │ │ └── tocGenerator.ts # 目录生成
│ │ │
│ │ ├── terminal/
│ │ │ ├── TerminalPanel.tsx # xterm.js 终端封装
│ │ │ └── TerminalToolbar.tsx # 终端工具栏(清屏/重启/字体大小)
│ │ │
│ │ ├── statusbar/
│ │ │ └── StatusBar.tsx # 底部状态栏
│ │ │
│ │ └── dialogs/
│ │ ├── ExportDialog.tsx # 导出选项对话框
│ │ ├── TableInsertDialog.tsx # 表格插入对话框
│ │ ├── ImageUploadDialog.tsx # 图片上传对话框
│ │ └── SettingsDialog.tsx # 设置对话框
│ │
│ ├── stores/ # Zustand 状态管理
│ │ ├── useEditorStore.ts # 编辑器状态(当前文件、内容、光标、标签页)
│ │ ├── useFileStore.ts # 文件系统状态(目录树、打开的文件)
│ │ ├── usePreviewStore.ts # 预览状态(渲染模式、滚动位置)
│ │ ├── useTerminalStore.ts # 终端状态(连接状态、输出历史)
│ │ └── useThemeStore.ts # 主题状态(亮色/暗色、字体、CSS 变量)
│ │
│ ├── hooks/ # 自定义 Hooks
│ │ ├── useIpc.ts # IPC 通信 Hook(类型安全封装)
│ │ ├── useMarkdownParser.ts # Markdown 解析 + 实时渲染
│ │ ├── useScrollSync.ts # 编辑-预览滚动同步
│ │ ├── useAutoSave.ts # 自动保存草稿
│ │ ├── useShortcuts.ts # 全局快捷键注册
│ │ ├── useImagePaste.ts # 粘贴/拖拽图片处理
│ │ └── useExport.ts # 导出逻辑
│ │
│ ├── services/ # 业务逻辑服务
│ │ ├── markdownEngine.ts # markdown-it 配置 + 插件加载
│ │ ├── fileService.ts # 文件 CRUD(通过 IPC 调用主进程)
│ │ ├── exportService.ts # 导出为 HTML/PDF/DOCX
│ │ └── imageService.ts # 图片上传/本地存储
│ │
│ ├── types/ # TypeScript 类型定义
│ │ ├── editor.ts # 编辑器相关类型
│ │ ├── file.ts # 文件系统类型
│ │ ├── ipc.ts # IPC 通道类型(类型安全的通信契约)
│ │ └── theme.ts # 主题类型
│ │
│ └── styles/ # 全局样式
│ ├── globals.css # 全局 CSS + Tailwind 指令
│ ├── themes/
│ │ ├── light.css # 亮色主题变量
│ │ └── dark.css # 暗色主题变量
│ └── preview.css # Markdown 预览区样式
│
├── claude-code/ # Claude Code 源码(保持原目录结构)
│ └── src/ # 原 Claude Code 源码
│ ├── query.ts # LLM 查询引擎
│ ├── QueryEngine.ts # 查询引擎核心
│ ├── REPL.tsx → 改造为终端模式 # REPL 入口改造
│ ├── tools/ # 工具系统(复用部分)
│ ├── state/ # 状态管理(保留)
│ └── ... # 其他原有模块
│
├── resources/ # 静态资源
│ ├── icons/ # 应用图标
│ └── templates/ # 导出 HTML 模板
│
├── package.json
├── electron-builder.yml # Electron 打包配置
├── vite.config.ts # Vite 构建配置
├── tailwind.config.ts # Tailwind 配置
└── tsconfig.json
┌──────────────────┐ IPC ┌──────────────────────┐
│ Electron 主进程 │ ◄──────────────────► │ Electron 渲染进程 │
│ (Main Process) │ │ (Renderer Process) │
│ │ │ │
│ • 文件系统操作 │ file:open │ • React UI │
│ • PTY 管理 │ file:save │ • CodeMirror 6 │
│ • 窗口管理 │ file:readDir │ • markdown-it 渲染 │
│ • 原生菜单 │ terminal:input │ • xterm.js 终端 │
│ • 导出功能 │ terminal:output │ • Zustand 状态管理 │
│ │ export:html │ │
│ │ export:pdf │ │
│ │ image:upload │ │
└────────┬──────────┘ └──────────────────────┘
│
│ node-pty
▼
┌──────────────────┐
│ Claude Code │
│ (Bun 子进程) │
│ │
│ • 标准输入/输出 │
│ • AI 对话处理 │
│ • 工具调用 │
└──────────────────┘
IPC 通道契约(src/types/ipc.ts):
// 主进程暴露的 API(通过 contextBridge)
interface EditorAPI {
// 文件操作
file: {
open(filePath?: string): Promise<{ path: string; content: string }>;
save(filePath: string, content: string): Promise<void>;
saveAs(content: string): Promise<string | null>;
readDir(dirPath: string): Promise<FileTreeNode[]>;
createFile(parentPath: string, name: string): Promise<string>;
createDir(parentPath: string, name: string): Promise<string>;
delete(filePath: string): Promise<void>;
rename(oldPath: string, newPath: string): Promise<void>;
watch(dirPath: string, callback: (events: FileEvent[]) => void): () => void;
};
// 终端操作
terminal: {
create(workDir: string): Promise<number>; // 创建 PTY 会话,返回 ID
write(sessionId: number, data: string): void; // 写入终端
resize(sessionId: number, cols: number, rows: number): void;
destroy(sessionId: number): void;
onData(sessionId: number, callback: (data: string) => void): () => void;
};
// 导出功能
export: {
html(content: string, options: ExportOptions): Promise<string>;
pdf(content: string, options: ExportOptions): Promise<Buffer>;
docx(content: string, options: ExportOptions): Promise<Buffer>;
image(htmlContent: string): Promise<Buffer>;
};
// 图片处理
image: {
uploadFromPath(filePath: string): Promise<{ url: string }>;
uploadFromClipboard(): Promise<{ url: string }>;
saveLocal(base64: string, fileName: string): Promise<string>;
};
// 应用控制
app: {
getVersion(): Promise<string>;
getPlatform(): Promise<NodeJS.Platform>;
setTitle(title: string): void;
onMenuAction(callback: (action: string) => void): () => void;
};
}功能:
- 递归展示文件夹结构,支持展开/折叠
- 右键菜单:新建文件/文件夹、重命名、删除
- 拖拽移动文件
- 双击打开文件到编辑器
- 文件类型图标区分(.md / .png / .pdf 等)
- 当前编辑文件高亮标记
- 大纲视图(TOC):解析当前 Markdown 的标题层级,点击跳转
数据流:
FileTree.tsx
→ 调用 useFileStore(fileTree, setActiveFile)
→ fileTree 通过 IPC file:readDir 获取
→ 文件变更通过 IPC file:watch 实时监听
→ 用户点击文件 → setActiveFile(filePath)
→ EditorPane 监听到 activeFile 变化 → 加载文件内容
实现要点:
- 使用虚拟列表优化大量文件的渲染性能(可复用 Claude Code 原
VirtualMessageList.tsx的思路) - 目录展开状态存入
localStorage,跨会话保持 - 文件监听使用
chokidar(主进程)通过 IPC 推送变更事件到渲染进程
CodeMirror 6 扩展集成方案:
┌─────────────────────────────────────────┐
│ CodeMirror 6 EditorState │
│ │
│ Extensions: │
│ ┌─────────────────────────────────┐ │
│ │ @codemirror/lang-markdown │ │ 基础 Markdown 支持
│ │ @codemirror/view │ │ 视图层
│ │ @codemirror/state │ │ 状态管理
│ │ @codemirror/commands │ │ 命令系统
│ │ @codemirror/language │ │ 语言系统
│ │ @codemirror/search │ │ 搜索替换
│ │ @codemirror/autocomplete │ │ 自动补全
│ │ @codemirror/theme-one-dark │ │ 暗色主题
│ └─────────────────────────────────┘ │
│ │
│ 自定义扩展: │
│ ┌─────────────────────────────────┐ │
│ │ markdownAutoComplete.ts │ │ 自动补全 [] → [](), `` → ``
│ │ markdownKeybindings.ts │ │ 快捷键 (Ctrl+B/I/K…)
│ │ scrollSyncExtension.ts │ │ 滚动位置同步到预览区
│ │ livePreviewExtension.ts │ │ 粗体/斜体的轻量级内联渲染
│ │ tableEditorExtension.ts │ │ 表格 Tab 导航编辑
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
快捷键映射表:
| 快捷键 | 操作 | CodeMirror Command |
|---|---|---|
Ctrl+B |
加粗 **text** |
toggleBold |
Ctrl+I |
斜体 *text* |
toggleItalic |
Ctrl+K |
插入链接 [text](url) |
insertLink |
Ctrl+Shift+C |
行内代码 / 代码块 | toggleCode |
Ctrl+1~6 |
标题 1~6 级 | setHeading |
Ctrl+Z / Ctrl+Y |
撤销/重做 | 内置 |
Ctrl+S |
保存 | saveFile |
Ctrl+F |
搜索 | 内置 |
Ctrl+H |
替换 | 内置 |
Tab / Shift+Tab |
缩进/反缩进列表 | indentMore/indentLess |
Ctrl+Shift+F |
全局搜索 | 打开 GlobalSearchDialog |
自动补全规则:
- 输入
[→ 自动补全](<光标>) - 输入
`→ 自动补全`<光标>` - 输入
 - 输入
- [→ 自动补全](任务列表) - 输入
```→ 自动匹配闭合``` - 输入图片拖拽/粘贴 → 自动生成

渲染引擎架构:
Markdown 源码
│
▼
┌──────────────────┐
│ markdown-it │ 核心解析器
│ ─────────────── │
│ plugins: │
│ • markdown-it-gfm # GFM (表格/任务列表/删除线)
│ • markdown-it-footnote # 脚注
│ • markdown-it-sub/sup # 上标/下标
│ • markdown-it-emoji # Emoji
│ • markdown-it-mark # 高亮标记
│ • markdown-it-toc-done-right # 目录
│ • markdown-it-container # 自定义容器
│ • markdown-it-anchor # 标题锚点
└────────┬─────────┘
│ HTML 字符串
▼
┌──────────────────┐
│ 后处理器 │
│ ─────────────── │
│ • highlight.js │ 代码块语法高亮
│ • KaTeX │ 数学公式 → SVG
│ • Mermaid │ 图表 → SVG
│ • sanitize-html │ XSS 防护
└────────┬─────────┘
│ 安全的 HTML
▼
┌──────────────────┐
│ React 组件渲染 │
│ dangerouslySet │
│ InnerHTML │
└──────────────────┘
滚动同步方案:
EditorPane (CodeMirror) PreviewPane (DOM)
│ │
│ scroll 事件 │
▼ │
┌─────────────────┐ │
│ 计算当前可见行号 │────映射比例────►│ 计算对应 scrollTop
│ visibleLine / │ │ 通过 ref.scrollTo()
│ totalLines │ │
└─────────────────┘ │
│
│ 用户滚动预览区 │
│ ▼
│ ┌─────────────────┐
│◄───映射比例────────────│ 计算 scrollRatio │
│ 映射到编辑区行号 │
└─────────────────┘
使用 lodash.throttle 限制触发频率到 50ms,使用 requestAnimationFrame 确保流畅。
三种视图模式:
- 编辑 + 预览(默认):双栏布局
- 仅编辑:预览区折叠,编辑区占满
- 仅预览:编辑区折叠,预览区占满(阅读模式 + 专注模式)
终端集成架构:
┌─────────────────────────────────────────────┐
│ TerminalPanel.tsx │
│ │
│ ┌──────────────────────────────────────┐ │
│ │ xterm.js Terminal Instance │ │
│ │ ┌──────────────────────────────┐ │ │
│ │ │ Claude Code REPL │ │ │
│ │ │ - AI 对话流式输出 │ │ │
│ │ │ - 用户输入 → 主进程 PTY │ │ │
│ │ │ - 工具调用结果展示 │ │ │
│ │ └──────────────────────────────┘ │ │
│ └──────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────┐ │
│ │ 快捷操作栏: [清屏] [重启AI] [导出对话] │ │
│ └──────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
数据流:
用户键盘输入
→ xterm.onData(data)
→ IPC: terminal:write → 主进程
→ PTY.write(data) → Claude Code stdin
→ Claude Code 处理 (Bun 运行时)
→ PTY.stdout → 主进程
→ IPC: terminal:onData → 渲染进程
→ xterm.write(data) → 终端显示
Claude Code 与编辑器的交互:
除了标准的对话终端,还需支持以下编辑器特有的 AI 交互:
| 交互方式 | 触发机制 | Claude Code 动作 |
|---|---|---|
| 右键菜单 → "AI 帮忙写这段" | 将选中文本作为 Prompt 发送 | 在终端会话中继续编辑 |
| 底部输入框直接对话 | 用户键入文本,Enter 发送 | 标准 REPL 流程 |
| 工具栏按钮 → "AI 优化排版" | 将全文作为上下文发送 | 编辑当前 Markdown 内容 |
/fix 命令 |
发送语法/拼写修正请求 | 批量纠正 Markdown 语法 |
/summarize 命令 |
发送摘要请求 | 生成文档大纲/摘要 |
关键实现:主进程 PTY 管理(electron/pty/claudeTerminal.ts)
// 伪代码示意
import * as pty from 'node-pty';
import { BunWhich } from '../utils/bunResolver';
class ClaudeTerminalManager {
private sessions = new Map<number, pty.IPty>();
createSession(workDir: string): number {
const id = nextSessionId++;
const shell = process.platform === 'win32' ? 'powershell.exe' : 'bash';
const term = pty.spawn(shell, [], {
name: 'xterm-256color',
cols: 120,
rows: 30,
cwd: workDir,
env: { ...process.env, TERM: 'xterm-256color' },
});
// 启动 Claude Code CLI
term.write('claude\r');
term.onData((data: string) => {
// 转发输出到渲染进程
mainWindow.webContents.send('terminal:data', { id, data });
});
this.sessions.set(id, term);
return id;
}
write(sessionId: number, data: string): void {
this.sessions.get(sessionId)?.write(data);
}
resize(sessionId: number, cols: number, rows: number): void {
this.sessions.get(sessionId)?.resize(cols, rows);
}
destroy(sessionId: number): void {
this.sessions.get(sessionId)?.kill();
this.sessions.delete(sessionId);
}
}工具栏按钮布局:
┌──────────────────────────────────────────────────────────────┐
│ [H1] [H2] [H3] │ [B] [I] [S] │ [🔗] [🖼] │ ["] [≡] [☐] │
│ 标题 │ 粗斜删 │ 链接图片 │ 引用列表任务 │
│──────────────────────────────────────────────────────────────│
│ [📊 表格] [📋 代码块] [─ 水平线] [Σ 公式] [📈 Mermaid] │ 更多 │
└──────────────────────────────────────────────────────────────┘
每个按钮点击后在光标位置插入对应的 Markdown 语法,tooltip 中显示对应的快捷键。
Store 设计:
// useEditorStore.ts
interface EditorStore {
// 当前打开的文件标签页
tabs: EditorTab[];
activeTabId: string;
// 编辑器实例引用 (非响应式)
editorView: EditorView | null;
// 操作
openFile: (filePath: string, content: string) => void;
closeTab: (tabId: string) => void;
setActiveTab: (tabId: string) => void;
updateContent: (tabId: string, content: string) => void;
setEditorView: (view: EditorView) => void;
// 撤销/重做状态
canUndo: boolean;
canRedo: boolean;
}
// useFileStore.ts
interface FileStore {
rootPath: string; // 当前工作区根目录
fileTree: FileTreeNode[]; // 文件树数据
expandedDirs: Set<string>; // 展开的目录
activeFilePath: string | null;
setRootPath: (path: string) => void;
setFileTree: (tree: FileTreeNode[]) => void;
toggleDir: (path: string) => void;
refreshTree: () => Promise<void>;
}
// usePreviewStore.ts
interface PreviewStore {
viewMode: 'split' | 'edit-only' | 'preview-only';
scrollRatio: number; // 滚动比例 (0-1)
renderedHtml: string;
setViewMode: (mode: ViewMode) => void;
setScrollRatio: (ratio: number) => void;
updateRender: (markdown: string) => void;
}
// useThemeStore.ts
interface ThemeStore {
mode: 'light' | 'dark';
codeTheme: string; // 代码高亮主题名
fontSize: number;
lineHeight: number;
maxWidth: number; // 预览区最大宽度(打字机模式)
toggleTheme: () => void;
setCodeTheme: (theme: string) => void;
setFontSize: (size: number) => void;
setMaxWidth: (width: number) => void;
}
// useTerminalStore.ts
interface TerminalStore {
sessionId: number | null;
isConnected: boolean;
outputHistory: string[];
connect: (workDir: string) => Promise<void>;
disconnect: () => void;
sendCommand: (cmd: string) => void;
}用户输入 (CodeMirror)
│
│ onChange 事件 (debounce 150ms)
▼
useMarkdownParser Hook
│
├─→ 调用 markdownEngine.parse(mdText)
│ └─→ markdown-it 解析为 HTML
│
├─→ 后处理: 数学公式 (KaTeX)、图表 (Mermaid)
│
└─→ 更新 PreviewPane 的 innerHTML
性能优化:
- 大文档(>5000 行)使用 Web Worker 异步解析,避免阻塞 UI
- markdown-it 使用
html: false配置防止 XSS - 预览区使用 CSS
content-visibility: auto优化大文档渲染
粘贴处理流程:
用户 Ctrl+V 粘贴图片
│
▼
Clipboard API 检测是否有图片数据
│
├─→ 有图片:读取 blob → 转为 base64
│ │
│ ▼
│ 保存到项目目录 /assets/images/
│ │
│ ▼
│ 生成  插入光标位置
│
└─→ 无图片:正常粘贴文本
拖拽处理流程:
用户拖拽图片到编辑区
│
▼
Drop 事件获取 file 对象
│
▼
FileReader 读取 → 保存到本地 /assets/images/
│
▼
插入  到光标位置
插入表格对话框 → CodeMirror 表格语法生成:
用户选择行列数 (5行 x 4列)
│
▼
生成:
| 列1 | 列2 | 列3 | 列4 |
|-----|-----|-----|-----|
| | | | |
| | | | |
Tab 导航编辑: CodeMirror 自定义扩展监听 Tab 键,在表格内跳转到下一个单元格。
导出 HTML:
Markdown → markdown-it 渲染 → 完整 HTML 文档 (带 CSS 内联)
→ 通过 IPC 调用主进程 fs.writeFile 写入 .html 文件
导出 PDF:
方案 A: 渲染 HTML → Puppeteer/Playwright 无头浏览器 → 生成 PDF
方案 B: 渲染 HTML → Electron BrowserWindow (隐藏) → printToPDF()
导出 DOCX:
Markdown → 解析为 AST → 映射到 docx 格式 (使用 docx.js 库)
→ 生成 .docx 二进制 → 写入文件
导出图片:
渲染预览区 HTML → html2canvas → PNG/JPEG Buffer → 写入文件
使用 CSS 变量实现亮色/暗色切换:
/* light.css */
:root[data-theme="light"] {
--bg-primary: #ffffff;
--bg-secondary: #f5f5f5;
--text-primary: #1a1a1a;
--text-secondary: #666666;
--border-color: #e0e0e0;
--editor-bg: #ffffff;
--preview-bg: #fafafa;
--terminal-bg: #1e1e1e;
--accent-color: #0066cc;
--code-bg: #f0f0f0;
}
/* dark.css */
:root[data-theme="dark"] {
--bg-primary: #1e1e1e;
--bg-secondary: #252526;
--text-primary: #cccccc;
--text-secondary: #999999;
--border-color: #3e3e3e;
--editor-bg: #1e1e1e;
--preview-bg: #1a1a1a;
--terminal-bg: #0d0d0d;
--accent-color: #4da6ff;
--code-bg: #2d2d2d;
}专注模式触发
│
├─→ 隐藏左侧文件树 (Sidebar collapsed)
├─→ 隐藏底部终端 (BottomPanel collapsed)
├─→ 隐藏工具栏 (Toolbar hidden)
├─→ 编辑器居中,max-width: 800px
├─→ 当前段落高亮,其余段落 opacity: 0.3
└─→ 按 Escape 退出专注模式
应用首次启动
│
▼
执行 which bun (Unix) / where bun (Windows)
│
├─→ 找到 Bun → 记录路径 → 进入下一步
│
└─→ 未找到 → 弹出引导对话框
│
├─ [一键安装] → 打开终端执行对应平台的安装命令
│ macOS/Linux: curl -fsSL https://bun.sh/install | bash
│ Windows: powershell -c "irm bun.sh/install.ps1 | iex"
│
├─ [手动安装] → 打开 https://bun.sh 官网
│
└─ [跳过] → 编辑器可正常使用(编辑+预览),AI 终端功能暂不可用
Bun 检测通过后
│
▼
弹出 API Key 配置对话框
│
├─ 输入 Anthropic API Key → 验证非空
│
├─ 可选:设置 API Base URL(用于代理/自定义端点)
│
└─ 保存 → 加密写入本地用户数据目录
Windows: %APPDATA%/claude-md-editor/config.json
macOS: ~/Library/Application Support/claude-md-editor/config.json
Linux: ~/.config/claude-md-editor/config.json
后续使用:
启动 Claude Code 子进程时 → 从配置文件读取 → 设置 ANTHROPIC_API_KEY 环境变量
→ PTY 传递到 Bun 子进程 → Claude Code 使用该 Key 调用 API
{
"apiKey": "sk-ant-... (AES-256-GCM 加密)",
"apiBaseUrl": "https://api.anthropic.com",
"theme": "dark",
"fontSize": 16,
"recentFiles": ["/path/to/project/README.md"],
"bunPath": "/usr/local/bin/bun",
"firstLaunchCompleted": true
}API Key 使用 Electron 的 safeStorage API 加密(各平台原生密钥链),防止明文泄露。
| 模块 | 复用方式 | 说明 |
|---|---|---|
query.ts / QueryEngine.ts |
完整保留 | AI 对话核心,直接用于终端中的 Claude Code 进程 |
tools/(全部工具) |
完整保留 | 文件读写、搜索、Bash 等工具供 AI 调用 |
state/ |
部分复用 | AppState 类型定义可参考,但渲染进程用 Zustand 替代 |
commands/ |
保留 | 斜杠命令(/fix、/summarize)通过终端输入调用 |
context.ts |
保留 | AI 上下文收集逻辑 |
utils/messages.ts |
保留 | 消息创建与规范化 |
utils/editor.ts |
改造 | 对外部编辑器的调用改为触发内置编辑器 |
| 模块 | 说明 |
|---|---|
electron/ 整个目录 |
Electron 主进程、窗口管理、PTY、IPC |
src/components/ 中所有编辑器组件 |
file-tree、editor、preview、terminal 面板 |
src/stores/ |
Zustand 状态管理 |
src/services/ |
Markdown 引擎、文件服务、导出服务 |
src/hooks/ |
IPC 通信、滚动同步、自动保存等自定义 Hooks |
| 原模块 | 改造内容 |
|---|---|
src/main.tsx |
去除 Commander.js CLI 入口,改为被 Electron 主进程以模块方式调用的初始化函数 |
src/screens/REPL.tsx |
原 Ink 组件改造为纯逻辑模块(无 UI),通过 stdin/stdout 与 PTY 交互 |
src/components/App.tsx |
重新实现为 Electron 渲染进程的根组件 |
src/ink/ |
废弃(Electron 使用真实 DOM,不再需要终端 React 渲染器) |
原 Claude Code 的 REPL 通过 Ink 渲染到终端。在 Electron 方案中,Claude Code 作为独立 Bun 子进程运行,通过 PTY 进行输入输出。改造策略:
原方案:
CLI 启动 → main() → setup() → launchRepl()
→ Ink.render(<App><REPL /></App>)
→ 用户在终端中交互
新方案:
Electron 主进程 → PTY 启动 Bun 子进程
→ 运行改造后的 Claude Code CLI (headless 模式)
→ stdin/stdout 通过 PTY 连接到 xterm.js
→ 用户在 Electron 窗口的底部终端面板中交互
这意味着 Claude Code 本身的 REPL 逻辑可以几乎不改动,只需要提供一种"无 Ink 渲染"的运行模式——直接读写 stdin/stdout,不启动 Ink 渲染器。实际上,Claude Code 本身就支持 --headless 类似的模式(用于 pipe 输入),可以直接利用。
- Electron 项目初始化,配置 Vite + React + TypeScript
- 实现三段式布局(AppShell + SplitPane)
- 集成 CodeMirror 6,实现基础 Markdown 编辑
- 集成 markdown-it,实现实时预览
- 实现暗色/亮色主题切换
- 集成 xterm.js,连接 Claude Code PTY 终端
- 文件目录树(新建/打开/保存/另存为)
- 多标签页管理
- 工具栏按钮(加粗、斜体、链接、图片、列表、标题)
- 常用快捷键(Ctrl+B/I/K/S/Z/Y/1~6)
- 双栏/单栏切换
- 语法高亮与自动补全
- 图片拖拽/粘贴上传
- 表格可视化插入与 Tab 导航
- 滚动同步
- 导出 HTML / PDF / DOCX
- Mermaid 图表渲染
- KaTeX 数学公式渲染
- 目录自动生成(TOC)
- 专注模式
- 字体大小 / 行高 / 最大宽度调节
- 自定义预览区 CSS
- 自动保存草稿
- 全局搜索替换
- 状态栏信息展示
- 原生菜单栏集成
- electron-builder 打包配置
- Windows / macOS / Linux 三平台构建
- 自动更新配置
- 安装包签名
| 风险 | 影响 | 对策 |
|---|---|---|
| Claude Code 依赖 Bun 运行时 | 用户未安装 Bun 则 AI 终端不可用 | 首次启动自动检测,未安装则引导一键安装;编辑器核心功能(编辑+预览+文件管理)不依赖 Bun,可离线使用 |
| xterm.js 与 Claude Code 的 ANSI 转义序列兼容性 | 终端显示异常 | 充分测试 xterm.js 的 ANSI 兼容性,必要时做转义序列适配 |
| Claude Code 源码可能有内部 API 依赖 | 部分功能不可用 | 仅为终端模式保留核心对话功能(query + tools),裁剪不可用的模块 |
| markdown-it 插件与 KaTeX/Mermaid 加载性能 | 大文档渲染卡顿 | Web Worker 异步解析,虚拟滚动,懒加载重量级插件 |
| CodeMirror 6 与 markdown-it 的 AST 行号映射 | 滚动同步不精确 | 使用 markdown-it 的 source map 功能,建立行号映射表 |
{
"dependencies": {
"electron": "^28.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"zustand": "^4.5.0",
"@codemirror/state": "^6.4.0",
"@codemirror/view": "^6.22.0",
"@codemirror/lang-markdown": "^6.2.0",
"@codemirror/commands": "^6.3.0",
"@codemirror/autocomplete": "^6.12.0",
"@codemirror/search": "^6.5.0",
"@codemirror/theme-one-dark": "^6.1.0",
"@codemirror/language": "^6.10.0",
"markdown-it": "^14.0.0",
"markdown-it-gfm": "^0.1.0",
"markdown-it-footnote": "^4.0.0",
"markdown-it-emoji": "^3.0.0",
"markdown-it-sub": "^2.0.0",
"markdown-it-sup": "^2.0.0",
"markdown-it-mark": "^4.0.0",
"markdown-it-toc-done-right": "^4.2.0",
"markdown-it-container": "^4.0.0",
"markdown-it-anchor": "^8.6.0",
"highlight.js": "^11.9.0",
"katex": "^0.16.0",
"mermaid": "^10.6.0",
"xterm": "^5.3.0",
"xterm-addon-fit": "^0.8.0",
"xterm-addon-web-links": "^0.9.0",
"node-pty": "^1.0.0",
"lodash.throttle": "^4.1.0",
"sanitize-html": "^2.11.0",
"chokidar": "^3.6.0",
"docx": "^8.5.0",
"html2canvas": "^1.4.0"
},
"devDependencies": {
"typescript": "^5.3.0",
"vite": "^5.0.0",
"@vitejs/plugin-react": "^4.2.0",
"electron-builder": "^24.0.0",
"tailwindcss": "^3.4.0",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0"
}
}