Skip to content

Latest commit

 

History

History
1079 lines (925 loc) · 47.7 KB

File metadata and controls

1079 lines (925 loc) · 47.7 KB

Markdown 编辑器 — 基于 Claude Code 二次开发架构设计

一、项目概述与目标

1.1 产品定位

基于 Claude Code 源码二次开发,构建一个 PyCharm 风格的 AI Markdown 编辑器

核心理念:用户下载安装后,只需配置 Anthropic API Key,就能在一个桌面应用中完成 Markdown 写作、实时预览、AI 辅助编辑的全部工作流,无需打开终端、无需额外工具。

目标用户旅程

下载安装包 → 安装 → 首次启动
                        │
                        ├─→ 检测 Bun 运行时 → 未安装则引导一键安装
                        │
                        ├─→ 输入 Anthropic API Key → 加密存储到本地
                        │
                        └─→ 进入编辑器 ← 此后每次打开直接可用
                              │
                              ├─ 左侧文件树浏览/管理 Markdown 文件
                              ├─ 中央编辑 + 实时预览
                              └─ 底部 Claude Code 对话终端(AI 已就绪)

1.2 核心布局

┌──────────────────────────────────────────────────────────┐
│  菜单栏: 文件 | 编辑 | 视图 | 工具 | 主题 | 帮助          │
├────────┬────────────────────────────────┬────────────────┤
│        │                                │                │
│ 文件树  │   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 对话能力完整保留,作为编辑器的智能助手嵌入底部终端。

二、技术选型

2.1 整体方案:Electron + React + Claude Code 内核

已确认的技术决策:

决策项 选择 说明
应用形态 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 子进程

2.2 为什么不直接复用 Claude Code 的 Ink 终端 UI?

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

五、核心模块设计

5.1 进程架构与通信

┌──────────────────┐         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;
  };
}

5.2 左侧面板:文件目录树(FileTree)

功能:

  • 递归展示文件夹结构,支持展开/折叠
  • 右键菜单:新建文件/文件夹、重命名、删除
  • 拖拽移动文件
  • 双击打开文件到编辑器
  • 文件类型图标区分(.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 推送变更事件到渲染进程

5.3 中央编辑区:EditorPane(CodeMirror 6)

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

自动补全规则:

  • 输入 [ → 自动补全 ](<光标>)
  • 输入 ` → 自动补全 `<光标>`
  • 输入 ![ → 自动补全 ](<光标>)
  • 输入 - [ → 自动补全 ] (任务列表)
  • 输入 ``` → 自动匹配闭合 ```
  • 输入图片拖拽/粘贴 → 自动生成 ![](path)

5.4 右侧预览区:PreviewPane

渲染引擎架构:

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 确保流畅。

三种视图模式:

  • 编辑 + 预览(默认):双栏布局
  • 仅编辑:预览区折叠,编辑区占满
  • 仅预览:编辑区折叠,预览区占满(阅读模式 + 专注模式)

5.5 底部面板:AI 终端(xterm.js + Claude Code)

终端集成架构:

┌─────────────────────────────────────────────┐
│              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);
  }
}

5.6 工具栏(EditorToolbar)

工具栏按钮布局:

┌──────────────────────────────────────────────────────────────┐
│ [H1] [H2] [H3] │ [B] [I] [S] │ [🔗] [🖼] │ ["] [≡] [☐] │
│ 标题            │ 粗斜删       │ 链接图片   │ 引用列表任务 │
│──────────────────────────────────────────────────────────────│
│ [📊 表格] [📋 代码块] [─ 水平线] [Σ 公式] [📈 Mermaid] │ 更多 │
└──────────────────────────────────────────────────────────────┘

每个按钮点击后在光标位置插入对应的 Markdown 语法,tooltip 中显示对应的快捷键。

5.7 状态管理(Zustand Stores)

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;
}

六、关键功能实现要点

6.1 实时渲染

用户输入 (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 优化大文档渲染

6.2 图片处理

粘贴处理流程:

用户 Ctrl+V 粘贴图片
    │
    ▼
Clipboard API 检测是否有图片数据
    │
    ├─→ 有图片:读取 blob → 转为 base64
    │       │
    │       ▼
    │   保存到项目目录 /assets/images/
    │       │
    │       ▼
    │   生成 ![](./assets/images/xxx.png) 插入光标位置
    │
    └─→ 无图片:正常粘贴文本

拖拽处理流程:

用户拖拽图片到编辑区
    │
    ▼
Drop 事件获取 file 对象
    │
    ▼
FileReader 读取 → 保存到本地 /assets/images/
    │
    ▼
插入 ![](./assets/images/xxx.png) 到光标位置

6.3 表格可视化编辑

插入表格对话框 → CodeMirror 表格语法生成:

用户选择行列数 (5行 x 4列)
    │
    ▼
生成:
| 列1 | 列2 | 列3 | 列4 |
|-----|-----|-----|-----|
|     |     |     |     |
|     |     |     |     |

Tab 导航编辑: CodeMirror 自定义扩展监听 Tab 键,在表格内跳转到下一个单元格。

6.4 导出功能

导出 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 → 写入文件

6.5 主题系统

使用 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;
}

6.6 专注模式

专注模式触发
    │
    ├─→ 隐藏左侧文件树 (Sidebar collapsed)
    ├─→ 隐藏底部终端 (BottomPanel collapsed)
    ├─→ 隐藏工具栏 (Toolbar hidden)
    ├─→ 编辑器居中,max-width: 800px
    ├─→ 当前段落高亮,其余段落 opacity: 0.3
    └─→ 按 Escape 退出专注模式

七、首次启动与配置流程

7.1 Bun 运行时检测

应用首次启动
    │
    ▼
执行 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 终端功能暂不可用

7.2 API Key 配置

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

7.3 配置存储结构

{
  "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 加密(各平台原生密钥链),防止明文泄露。


八、对 Claude Code 源码的改造点

8.1 保留/复用的模块

模块 复用方式 说明
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 改造 对外部编辑器的调用改为触发内置编辑器

8.2 需要新增的模块

模块 说明
electron/ 整个目录 Electron 主进程、窗口管理、PTY、IPC
src/components/ 中所有编辑器组件 file-tree、editor、preview、terminal 面板
src/stores/ Zustand 状态管理
src/services/ Markdown 引擎、文件服务、导出服务
src/hooks/ IPC 通信、滚动同步、自动保存等自定义 Hooks

8.3 需要改造的模块

原模块 改造内容
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 渲染器)

8.4 REPL 改造方案(关键改造)

原 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 输入),可以直接利用。


九、MVP 实现路线图

Phase 1:基础框架搭建(预计 2-3 周)

  • Electron 项目初始化,配置 Vite + React + TypeScript
  • 实现三段式布局(AppShell + SplitPane)
  • 集成 CodeMirror 6,实现基础 Markdown 编辑
  • 集成 markdown-it,实现实时预览
  • 实现暗色/亮色主题切换
  • 集成 xterm.js,连接 Claude Code PTY 终端

Phase 2:核心编辑功能(预计 2-3 周)

  • 文件目录树(新建/打开/保存/另存为)
  • 多标签页管理
  • 工具栏按钮(加粗、斜体、链接、图片、列表、标题)
  • 常用快捷键(Ctrl+B/I/K/S/Z/Y/1~6)
  • 双栏/单栏切换
  • 语法高亮与自动补全

Phase 3:增强功能(预计 2-3 周)

  • 图片拖拽/粘贴上传
  • 表格可视化插入与 Tab 导航
  • 滚动同步
  • 导出 HTML / PDF / DOCX
  • Mermaid 图表渲染
  • KaTeX 数学公式渲染
  • 目录自动生成(TOC)

Phase 4:体验打磨(预计 1-2 周)

  • 专注模式
  • 字体大小 / 行高 / 最大宽度调节
  • 自定义预览区 CSS
  • 自动保存草稿
  • 全局搜索替换
  • 状态栏信息展示
  • 原生菜单栏集成

Phase 5:发布与打包(预计 1 周)

  • 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"
  }
}