- ESLint flat config + Prettier + EditorConfig - Markdown处理器LRU缓存 - Zustand选择器优化减少重渲染 - CodeMirror Compartment主题热切换 - Preview防抖(150ms) + IndexedDB去抖(500ms) - 组件拆分: App.tsx 310→104行, Sidebar.tsx 244→90行 - 统一错误处理 errorHandler.ts - ConfirmDialog替代原生confirm - LoadingSpinner加载状态 - Toast多条堆叠+类型区分 - 可访问性增强(ARIA属性、键盘导航) - Vitest测试框架(78个测试用例) - Git hooks(husky + lint-staged) - 项目文档(README.md, CONTRIBUTING.md)
590 lines
26 KiB
JSON
590 lines
26 KiB
JSON
{
|
|
"project": "MarkLite",
|
|
"version": "0.1.1",
|
|
"analysisDate": "2026-06-03",
|
|
"techStack": {
|
|
"electron": "28.3.3",
|
|
"react": "18.3.1",
|
|
"typescript": "5.6.3",
|
|
"vite": "electron-vite 3.0.0",
|
|
"stateManagement": "zustand 5.0.6",
|
|
"editor": "CodeMirror 6",
|
|
"database": "Dexie (IndexedDB)"
|
|
},
|
|
"currentStatus": {
|
|
"typescriptCheck": "PASS",
|
|
"eslintConfig": "MISSING",
|
|
"testCoverage": "NONE",
|
|
"documentation": "NONE",
|
|
"ci": "NONE"
|
|
},
|
|
"codebaseStats": {
|
|
"totalSourceFiles": 44,
|
|
"mainProcessFiles": 5,
|
|
"preloadFiles": 1,
|
|
"rendererFiles": 36,
|
|
"sharedFiles": 2,
|
|
"components": 14,
|
|
"stores": 3,
|
|
"hooks": 7,
|
|
"dbRepositories": 3,
|
|
"typeDefinitions": 5,
|
|
"styleFiles": 3
|
|
},
|
|
"optimizationRequirements": {
|
|
"1_codeQuality": {
|
|
"title": "代码质量与规范",
|
|
"priority": "HIGH",
|
|
"items": [
|
|
{
|
|
"id": "CQ-01",
|
|
"title": "配置 ESLint (Flat Config)",
|
|
"priority": "P0-CRITICAL",
|
|
"description": "package.json 中已有 @typescript-eslint/eslint-plugin、eslint、eslint-plugin-react-hooks 等依赖,但缺少 eslint.config.js 配置文件。npm run lint 无法正常工作。",
|
|
"currentState": "devDependencies 已安装但无配置文件",
|
|
"expectedState": "创建 eslint.config.js (ESLint v9 flat config 格式),配置 TypeScript + React + React Hooks 规则",
|
|
"effort": "small",
|
|
"details": {
|
|
"configFormat": "ESM flat config (eslint.config.js)",
|
|
"plugins": ["@typescript-eslint/eslint-plugin", "eslint-plugin-react-hooks", "eslint-plugin-react-refresh"],
|
|
"rules": [
|
|
"typescript-eslint/recommended",
|
|
"react-hooks/recommended",
|
|
"no-unused-vars (error)",
|
|
"no-console (warn)",
|
|
"prefer-const"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"id": "CQ-02",
|
|
"title": "添加 Prettier 代码格式化",
|
|
"priority": "P1-HIGH",
|
|
"description": "项目无统一代码格式化工具,代码风格靠人工维护(缩进、引号、分号等不一致风险高)。",
|
|
"currentState": "无 Prettier 配置",
|
|
"expectedState": "添加 .prettierrc 配置 + 与 ESLint 集成",
|
|
"effort": "small",
|
|
"details": {
|
|
"config": {
|
|
"semi": false,
|
|
"singleQuote": true,
|
|
"tabWidth": 2,
|
|
"trailingComma": "none",
|
|
"printWidth": 100
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"id": "CQ-03",
|
|
"title": "添加 .editorconfig",
|
|
"priority": "P2-MEDIUM",
|
|
"description": "统一编辑器行为配置,确保不同开发者环境一致。",
|
|
"currentState": "无 .editorconfig",
|
|
"expectedState": "添加 .editorconfig 文件",
|
|
"effort": "trivial"
|
|
},
|
|
{
|
|
"id": "CQ-04",
|
|
"title": "修复 TypeScript 严格模式下的隐式 any",
|
|
"priority": "P1-HIGH",
|
|
"description": "虽然 tsc --noEmit 通过,但部分回调参数缺少类型标注(如 CustomEvent detail),依赖类型推断。",
|
|
"currentState": "编译通过但存在隐式类型",
|
|
"expectedState": "所有函数参数显式标注类型",
|
|
"effort": "medium"
|
|
},
|
|
{
|
|
"id": "CQ-05",
|
|
"title": "统一错误处理模式",
|
|
"priority": "P1-HIGH",
|
|
"description": "多处 catch 块为空或仅 console.error,用户看不到有意义的错误信息。App.tsx 中 showToast('操作失败') 过于笼统。",
|
|
"currentState": "分散的 try-catch,错误消息不具体",
|
|
"expectedState": "统一的错误处理函数,包含具体错误描述和日志",
|
|
"effort": "medium"
|
|
}
|
|
]
|
|
},
|
|
"2_performance": {
|
|
"title": "性能优化",
|
|
"priority": "HIGH",
|
|
"items": [
|
|
{
|
|
"id": "PF-01",
|
|
"title": "Markdown 渲染处理器缓存",
|
|
"priority": "P0-CRITICAL",
|
|
"description": "renderMarkdown() 每次调用都创建新的 unified() 处理器管道(含 7 个插件),大文件渲染时开销显著。unified 处理器创建后应缓存复用。",
|
|
"currentState": "每次渲染创建新 processor (7个插件)",
|
|
"expectedState": "缓存 processor 实例,仅在 filePath 变化时重建",
|
|
"effort": "small",
|
|
"impact": "HIGH - 减少每次渲染的初始化开销",
|
|
"details": {
|
|
"location": "src/renderer/lib/markdown.ts",
|
|
"solution": "使用 Map<string, Processor> 按 filePath 缓存,或使用 useMemo 在组件层面缓存"
|
|
}
|
|
},
|
|
{
|
|
"id": "PF-02",
|
|
"title": "Zustand 选择器优化 - 避免订阅整个 tabs 数组",
|
|
"priority": "P0-CRITICAL",
|
|
"description": "Editor.tsx、Preview.tsx、StatusBar.tsx、Sidebar.tsx 都订阅 tabs 数组然后 .find() 查找 activeTab。每次任何 tab 的 content 变化都会触发这些组件重渲染。",
|
|
"currentState": "const tabs = useTabStore(s => s.tabs); const activeTab = tabs.find(...)",
|
|
"expectedState": "使用 useShallow 或自定义选择器仅订阅 activeTab",
|
|
"effort": "medium",
|
|
"impact": "HIGH - 减少不必要的重渲染",
|
|
"affectedFiles": [
|
|
"src/renderer/components/Editor/Editor.tsx",
|
|
"src/renderer/components/Preview/Preview.tsx",
|
|
"src/renderer/components/StatusBar/StatusBar.tsx",
|
|
"src/renderer/components/Sidebar/Sidebar.tsx"
|
|
],
|
|
"solution": "在 tabStore 中添加 getActiveTab 选择器方法,或使用 zustand useShallow"
|
|
},
|
|
{
|
|
"id": "PF-03",
|
|
"title": "React.memo 包装纯展示组件",
|
|
"priority": "P1-HIGH",
|
|
"description": "Toolbar、StatusBar、Icons 等纯展示组件未使用 React.memo,父组件重渲染时会连带重渲染。",
|
|
"currentState": "无任何组件使用 React.memo",
|
|
"expectedState": "对 Toolbar、StatusBar、TabBar、Preview 等组件使用 memo 包装",
|
|
"effort": "small",
|
|
"impact": "MEDIUM"
|
|
},
|
|
{
|
|
"id": "PF-04",
|
|
"title": "CodeMirror 主题切换优化",
|
|
"priority": "P1-HIGH",
|
|
"description": "useCodeMirror.ts 中 darkMode 变化时销毁并重建整个 EditorView(第93行),导致编辑器完全重新初始化。",
|
|
"currentState": "darkMode 变化 → 整个 EditorView 销毁重建",
|
|
"expectedState": "使用 CodeMirror Compartment 动态切换主题扩展",
|
|
"effort": "medium",
|
|
"impact": "HIGH - 切换主题时保持编辑器状态",
|
|
"details": {
|
|
"location": "src/renderer/components/Editor/useCodeMirror.ts",
|
|
"solution": "使用 @codemirror/state 的 Compartment API 动态 reconfigure 主题扩展"
|
|
}
|
|
},
|
|
{
|
|
"id": "PF-05",
|
|
"title": "CodeMirror 内容更新优化",
|
|
"priority": "P2-MEDIUM",
|
|
"description": "setContent() 使用 from:0 到 length 全量替换,对大文件(10万行+)性能差。应使用事务 diff 或仅在内容实际变化时替换。",
|
|
"currentState": "全量替换: changes: { from: 0, to: current.length, insert: newContent }",
|
|
"expectedState": "仅在内容不同时替换,大文件考虑增量更新",
|
|
"effort": "medium"
|
|
},
|
|
{
|
|
"id": "PF-06",
|
|
"title": "Bundle 大小优化 - 代码分割",
|
|
"priority": "P2-MEDIUM",
|
|
"description": "所有渲染进程代码打包为单一 bundle。rehype-highlight (含 highlight.js)、CodeMirror 等大型库未做懒加载。",
|
|
"currentState": "单 bundle,无代码分割",
|
|
"expectedState": "动态 import 预览相关库、分割 vendor chunk",
|
|
"effort": "medium",
|
|
"details": {
|
|
"chunks": [
|
|
"vendor-react: react + react-dom",
|
|
"vendor-codemirror: @codemirror/*",
|
|
"vendor-markdown: unified + remark + rehype 生态",
|
|
"app: 业务代码"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"id": "PF-07",
|
|
"title": "Preview 渲染防抖",
|
|
"priority": "P2-MEDIUM",
|
|
"description": "Preview.tsx 在 activeTab?.content 变化时立即触发异步渲染。快速输入时会频繁调用 renderMarkdown。",
|
|
"currentState": "content 变化立即渲染",
|
|
"expectedState": "添加 150-300ms 防抖,避免快速输入时频繁渲染",
|
|
"effort": "small"
|
|
},
|
|
{
|
|
"id": "PF-08",
|
|
"title": "保存到 IndexedDB 的去抖优化",
|
|
"priority": "P2-MEDIUM",
|
|
"description": "tabStore 中多处使用 setTimeout(() => saveToDB(), 0) 异步保存,但没有去抖。频繁操作时会多次写入。",
|
|
"currentState": "setTimeout(fn, 0) 立即触发",
|
|
"expectedState": "使用 debounce (300-500ms) 合并多次保存",
|
|
"effort": "small"
|
|
}
|
|
]
|
|
},
|
|
"3_architecture": {
|
|
"title": "架构优化",
|
|
"priority": "MEDIUM",
|
|
"items": [
|
|
{
|
|
"id": "AR-01",
|
|
"title": "App.tsx 拆分 - About 对话框提取",
|
|
"priority": "P1-HIGH",
|
|
"description": "App.tsx 有 307 行,About 对话框 JSX 内联(约 40 行),文件打开/保存逻辑也堆积在组件中。",
|
|
"currentState": "App.tsx 包含 About 弹窗、文件操作、事件监听等所有逻辑",
|
|
"expectedState": "提取 AboutDialog 组件、提取 useFileOperations hook",
|
|
"effort": "medium",
|
|
"details": {
|
|
"extractions": [
|
|
{
|
|
"name": "AboutDialog",
|
|
"from": "App.tsx lines 261-300",
|
|
"to": "src/renderer/components/AboutDialog/AboutDialog.tsx"
|
|
},
|
|
{
|
|
"name": "useFileOperations",
|
|
"description": "提取 handleOpenFile, handleSave, handleSaveAs, handleOpenRecent 为自定义 hook",
|
|
"to": "src/renderer/hooks/useFileOperations.ts"
|
|
}
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"id": "AR-02",
|
|
"title": "Sidebar 组件拆分",
|
|
"priority": "P1-HIGH",
|
|
"description": "Sidebar.tsx 有 241 行,包含 FileTree 递归组件、resize 逻辑、独立文件列表等。应拆分为独立文件。",
|
|
"currentState": "FileTree 组件定义在 Sidebar.tsx 内",
|
|
"expectedState": "FileTree 提取为独立组件,resize 逻辑提取为 hook",
|
|
"effort": "medium",
|
|
"details": {
|
|
"extractions": [
|
|
{
|
|
"name": "FileTree",
|
|
"from": "Sidebar.tsx lines 180-239",
|
|
"to": "src/renderer/components/Sidebar/FileTree.tsx"
|
|
},
|
|
{
|
|
"name": "useSidebarResize",
|
|
"description": "提取 resize 相关逻辑为自定义 hook",
|
|
"to": "src/renderer/hooks/useSidebarResize.ts"
|
|
}
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"id": "AR-03",
|
|
"title": "统一设置加载 - 消除 useTheme 和 useSettings 的重复加载",
|
|
"priority": "P2-MEDIUM",
|
|
"description": "useTheme 和 useSettings 各自独立调用 settingsRepository.load(),导致启动时两次 IndexedDB 读取。",
|
|
"currentState": "useTheme.load() + useSettings.load() = 两次 DB 读取",
|
|
"expectedState": "统一为单个 useSettingsInit hook,一次加载分发到 store",
|
|
"effort": "small"
|
|
},
|
|
{
|
|
"id": "AR-04",
|
|
"title": "消除 DOM CustomEvent 中间层",
|
|
"priority": "P2-MEDIUM",
|
|
"description": "useFileWatch.ts 通过 window.dispatchEvent(CustomEvent) 传递文件修改事件,App.tsx 再 addEventListener 监听。应直接通过 Zustand store 或回调传递。",
|
|
"currentState": "IPC → CustomEvent DOM → App.tsx listener",
|
|
"expectedState": "IPC → Zustand store action → 组件响应",
|
|
"effort": "small"
|
|
},
|
|
{
|
|
"id": "AR-05",
|
|
"title": "添加 React ErrorBoundary",
|
|
"priority": "P1-HIGH",
|
|
"description": "无全局错误边界。任何渲染错误会导致白屏。Electron 应用中尤其需要,因为用户无法刷新页面恢复。",
|
|
"currentState": "无 ErrorBoundary",
|
|
"expectedState": "添加 ErrorBoundary 组件包裹 App,显示错误信息和重启按钮",
|
|
"effort": "small"
|
|
},
|
|
{
|
|
"id": "AR-06",
|
|
"title": "Icons 组件按需加载",
|
|
"priority": "P2-MEDIUM",
|
|
"description": "Icons.tsx 有 216 行,包含 16 个 SVG 图标组件。虽然是函数组件有 tree-shaking,但单文件过大影响维护。",
|
|
"currentState": "16 个图标定义在单文件",
|
|
"expectedState": "按功能模块拆分: toolbar-icons.tsx, sidebar-icons.tsx, app-icons.tsx",
|
|
"effort": "small"
|
|
},
|
|
{
|
|
"id": "AR-07",
|
|
"title": "tabStore 异步操作模式统一",
|
|
"priority": "P2-MEDIUM",
|
|
"description": "tabStore 中大量使用 setTimeout(() => saveToDB(), 0) 和 setTimeout(() => tabRepository.saveActiveTabId(), 0)。模式不统一且容易出错。",
|
|
"currentState": "分散的 setTimeout 调用",
|
|
"expectedState": "使用 Zustand middleware (subscribeWithSelector) 或统一的 persist 逻辑",
|
|
"effort": "medium"
|
|
}
|
|
]
|
|
},
|
|
"4_userExperience": {
|
|
"title": "用户体验",
|
|
"priority": "MEDIUM",
|
|
"items": [
|
|
{
|
|
"id": "UX-01",
|
|
"title": "替换原生 confirm() 为自定义确认对话框",
|
|
"priority": "P1-HIGH",
|
|
"description": "TabBar.tsx 和 useUnsavedWarning.ts 使用浏览器原生 confirm() 对话框,样式丑陋且不可自定义。Electron 应用应使用自定义模态框或 electron.dialog。",
|
|
"currentState": "5 处使用 confirm() 原生对话框",
|
|
"expectedState": "自定义 ConfirmDialog 组件或使用 electron.dialog.showMessageBox",
|
|
"effort": "medium",
|
|
"affectedLocations": [
|
|
"src/renderer/components/TabBar/TabBar.tsx (4处)",
|
|
"src/renderer/hooks/useUnsavedWarning.ts (1处)"
|
|
]
|
|
},
|
|
{
|
|
"id": "UX-02",
|
|
"title": "添加加载状态指示",
|
|
"priority": "P1-HIGH",
|
|
"description": "文件打开、目录树加载、Markdown 渲染等异步操作无加载状态。用户无法感知操作是否正在进行。",
|
|
"currentState": "无 loading 状态",
|
|
"expectedState": "添加文件打开 spinner、目录树加载骨架屏、预览渲染指示器",
|
|
"effort": "medium"
|
|
},
|
|
{
|
|
"id": "UX-03",
|
|
"title": "添加编辑器+预览分屏模式",
|
|
"priority": "P2-MEDIUM",
|
|
"description": "当前只有 'editor' 和 'preview' 两种模式,缺少 VS Code 风格的左右分屏同时编辑和预览。",
|
|
"currentState": "viewMode: 'editor' | 'preview'",
|
|
"expectedState": "新增 'split' 模式: 左侧编辑器 + 右侧实时预览",
|
|
"effort": "large",
|
|
"details": {
|
|
"implementation": [
|
|
"Settings.ViewMode 新增 'split'",
|
|
"Editor 和 Preview 同时渲染",
|
|
"编辑器滚动同步预览",
|
|
"Ctrl+3 快捷键"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"id": "UX-04",
|
|
"title": "TabBar 拖拽排序",
|
|
"priority": "P3-LOW",
|
|
"description": "标签页不支持拖拽排序,无法自定义标签顺序。",
|
|
"currentState": "标签页固定顺序",
|
|
"expectedState": "支持拖拽重新排列标签页",
|
|
"effort": "large"
|
|
},
|
|
{
|
|
"id": "UX-05",
|
|
"title": "改进 Toast 通知系统",
|
|
"priority": "P2-MEDIUM",
|
|
"description": "当前 Toast 仅支持单条消息,3秒后消失。不支持不同类型(success/error/warning)和堆叠。",
|
|
"currentState": "单条 toast,无类型区分",
|
|
"expectedState": "支持多条堆叠、不同类型(成功/错误/警告)、可关闭",
|
|
"effort": "small"
|
|
},
|
|
{
|
|
"id": "UX-06",
|
|
"title": "添加文件搜索功能 (Ctrl+P)",
|
|
"priority": "P3-LOW",
|
|
"description": "无快速文件搜索/打开功能。当打开文件夹后,应支持按文件名快速搜索打开。",
|
|
"currentState": "仅支持文件夹树浏览",
|
|
"expectedState": "Ctrl+P 快速搜索面板,模糊匹配文件名",
|
|
"effort": "large"
|
|
},
|
|
{
|
|
"id": "UX-07",
|
|
"title": "可访问性增强",
|
|
"priority": "P2-MEDIUM",
|
|
"description": "部分组件缺少 ARIA 属性。Sidebar resize handle 无键盘支持。文件树展开/折叠无键盘导航。",
|
|
"currentState": "基本的 role/aria-label 已添加",
|
|
"expectedState": "完整的键盘导航、focus 管理、屏幕阅读器支持",
|
|
"effort": "medium",
|
|
"details": {
|
|
"missing": [
|
|
"resize handle 需要 role='separator' + 键盘调整",
|
|
"文件树需要 aria-expanded、aria-owns",
|
|
"右键菜单需要 role='menu' + 键盘导航",
|
|
"About 对话框需要 focus trap"
|
|
]
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"5_developmentExperience": {
|
|
"title": "开发体验",
|
|
"priority": "MEDIUM",
|
|
"items": [
|
|
{
|
|
"id": "DX-01",
|
|
"title": "添加单元测试框架",
|
|
"priority": "P1-HIGH",
|
|
"description": "项目零测试覆盖。核心逻辑(tabStore、fileUtils、markdown 渲染)应有单元测试。",
|
|
"currentState": "无测试文件、无测试框架",
|
|
"expectedState": "Vitest + React Testing Library,核心模块测试覆盖 > 60%",
|
|
"effort": "large",
|
|
"details": {
|
|
"framework": "vitest + @testing-library/react + jsdom",
|
|
"priority_test_targets": [
|
|
"src/renderer/stores/tabStore.ts - 标签页 CRUD 逻辑",
|
|
"src/renderer/lib/fileUtils.ts - 文件名/路径工具",
|
|
"src/renderer/lib/markdown.ts - Markdown 渲染",
|
|
"src/renderer/db/schema.ts - 数据库 schema",
|
|
"src/main/file-system.ts - 文件系统操作"
|
|
],
|
|
"scripts": {
|
|
"test": "vitest run",
|
|
"test:watch": "vitest",
|
|
"test:coverage": "vitest run --coverage"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"id": "DX-02",
|
|
"title": "强化 preload 类型安全",
|
|
"priority": "P2-MEDIUM",
|
|
"description": "types/ipc.ts 定义了 ElectronAPI 接口和 IpcInvokeMap,但 preload/index.ts 未使用这些类型约束。contextBridge.exposeInMainWorld 返回值无类型检查。",
|
|
"currentState": "preload 使用字面量对象,未引用 ElectronAPI 类型",
|
|
"expectedState": "preload 实现强制匹配 ElectronAPI 接口类型",
|
|
"effort": "small",
|
|
"details": {
|
|
"solution": "const api: ElectronAPI = { ... } 在 preload 中强制类型检查"
|
|
}
|
|
},
|
|
{
|
|
"id": "DX-03",
|
|
"title": "添加类型安全的 IPC 通信层",
|
|
"priority": "P2-MEDIUM",
|
|
"description": "types/ipc.ts 定义了 IpcInvokeMap 但未被使用。应建立类型安全的 IPC 调用包装器。",
|
|
"currentState": "IpcInvokeMap 已定义但未使用",
|
|
"expectedState": "渲染进程通过类型安全的 invoke 函数调用 IPC",
|
|
"effort": "medium"
|
|
},
|
|
{
|
|
"id": "DX-04",
|
|
"title": "添加项目文档",
|
|
"priority": "P2-MEDIUM",
|
|
"description": "无 README.md、无 CONTRIBUTING.md、无开发环境搭建文档。",
|
|
"currentState": "无文档",
|
|
"expectedState": "README.md 含项目介绍、安装步骤、开发指南、构建说明",
|
|
"effort": "small",
|
|
"details": {
|
|
"files": [
|
|
"README.md - 项目介绍、截图、安装、开发、构建",
|
|
"CONTRIBUTING.md - 贡献指南、代码规范、PR 流程",
|
|
"CHANGELOG.md - 版本变更记录"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"id": "DX-05",
|
|
"title": "添加 Git hooks (husky + lint-staged)",
|
|
"priority": "P2-MEDIUM",
|
|
"description": "无 pre-commit 钩子,代码质量无法在提交时保证。",
|
|
"currentState": "无 Git hooks",
|
|
"expectedState": "husky + lint-staged: 提交前自动运行 ESLint + 类型检查",
|
|
"effort": "small"
|
|
},
|
|
{
|
|
"id": "DX-06",
|
|
"title": "添加 CI/CD 配置",
|
|
"priority": "P3-LOW",
|
|
"description": "无持续集成配置。应添加 GitHub Actions 或 Gitee CI 配置。",
|
|
"currentState": "无 CI 配置",
|
|
"expectedState": "CI 流水线: lint → typecheck → test → build",
|
|
"effort": "small"
|
|
},
|
|
{
|
|
"id": "DX-07",
|
|
"title": "添加路径别名到 Vite 配置",
|
|
"priority": "P3-LOW",
|
|
"description": "tsconfig.json 定义了 @renderer/*、@main/*、@shared/* 路径别名,但 electron.vite.config.ts 中 renderer 的 alias 只有 @renderer 和 @shared,缺少 @main。",
|
|
"currentState": "renderer alias 缺少 @main 映射",
|
|
"expectedState": "补全所有路径别名映射",
|
|
"effort": "trivial"
|
|
}
|
|
]
|
|
},
|
|
"6_security": {
|
|
"title": "安全加固",
|
|
"priority": "HIGH",
|
|
"items": [
|
|
{
|
|
"id": "SE-01",
|
|
"title": "CSP (Content Security Policy) 配置",
|
|
"priority": "P1-HIGH",
|
|
"description": "未配置 CSP。Preview 使用 dangerouslySetInnerHTML 渲染 HTML,虽然 rehype-sanitize 做了清理,但缺少 CSP 作为第二道防线。",
|
|
"currentState": "无 CSP 配置",
|
|
"expectedState": "在 index.html 和 BrowserWindow 中配置严格 CSP",
|
|
"effort": "small"
|
|
},
|
|
{
|
|
"id": "SE-02",
|
|
"title": "Preview 中 file:// 协议安全",
|
|
"priority": "P1-HIGH",
|
|
"description": "rehypeFixImages 将相对路径图片转为 file:// URL。虽然拒绝了 '..' 但未做更严格的路径校验(如 symlink 跟踪)。",
|
|
"currentState": "仅检查 '..' 路径遍历",
|
|
"expectedState": "使用 path.resolve + 检查结果路径是否在允许目录内",
|
|
"effort": "small"
|
|
},
|
|
{
|
|
"id": "SE-03",
|
|
"title": "Electron 安全最佳实践检查",
|
|
"priority": "P2-MEDIUM",
|
|
"description": "需确认 BrowserWindow 配置中 webPreferences 的安全设置(nodeIntegration、contextIsolation、sandbox 等)。",
|
|
"currentState": "未检查 window-manager.ts 中的 webPreferences",
|
|
"expectedState": "确保 nodeIntegration=false、contextIsolation=true、sandbox=true",
|
|
"effort": "small"
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"implementationPlan": {
|
|
"phase1_foundation": {
|
|
"name": "基础补全 (1-2天)",
|
|
"priority": "P0",
|
|
"items": ["CQ-01", "CQ-02", "CQ-03", "AR-05", "DX-07"],
|
|
"description": "ESLint + Prettier + EditorConfig + ErrorBoundary + 路径别名修复"
|
|
},
|
|
"phase2_criticalPerf": {
|
|
"name": "关键性能修复 (1-2天)",
|
|
"priority": "P0",
|
|
"items": ["PF-01", "PF-02", "PF-04", "PF-07", "PF-08"],
|
|
"description": "Markdown 缓存 + Zustand 选择器 + CodeMirror Compartment + 防抖"
|
|
},
|
|
"phase3_codeQuality": {
|
|
"name": "代码质量提升 (2-3天)",
|
|
"priority": "P1",
|
|
"items": ["CQ-04", "CQ-05", "AR-01", "AR-02", "AR-03", "AR-04", "PF-03"],
|
|
"description": "类型完善 + 错误处理统一 + 组件拆分 + 重复逻辑消除"
|
|
},
|
|
"phase4_userExperience": {
|
|
"name": "用户体验改进 (2-3天)",
|
|
"priority": "P1",
|
|
"items": ["UX-01", "UX-02", "UX-05", "UX-07"],
|
|
"description": "自定义确认框 + 加载状态 + Toast 改进 + 可访问性"
|
|
},
|
|
"phase5_developerExperience": {
|
|
"name": "开发体验 (2-3天)",
|
|
"priority": "P2",
|
|
"items": ["DX-01", "DX-02", "DX-04", "DX-05"],
|
|
"description": "单元测试 + 类型安全 + 文档 + Git hooks"
|
|
},
|
|
"phase6_advanced": {
|
|
"name": "高级功能 (3-5天)",
|
|
"priority": "P2-P3",
|
|
"items": ["PF-06", "UX-03", "UX-04", "UX-06", "SE-01", "SE-02", "SE-03", "DX-06"],
|
|
"description": "代码分割 + 分屏模式 + 拖拽排序 + 搜索 + 安全加固 + CI"
|
|
}
|
|
},
|
|
"riskAssessment": {
|
|
"highRisk": [
|
|
{
|
|
"id": "PF-04",
|
|
"risk": "CodeMirror Compartment 重构可能影响编辑器初始化和标签切换逻辑",
|
|
"mitigation": "保留现有逻辑作为 fallback,逐步迁移到 Compartment"
|
|
},
|
|
{
|
|
"id": "UX-03",
|
|
"risk": "分屏模式需要同时维护两个编辑器实例,内存和性能需关注",
|
|
"mitigation": "使用虚拟化、延迟加载预览面板"
|
|
}
|
|
],
|
|
"mediumRisk": [
|
|
{
|
|
"id": "PF-02",
|
|
"risk": "Zustand 选择器重构可能遗漏某些订阅点",
|
|
"mitigation": "使用 React DevTools Profiler 验证重渲染情况"
|
|
},
|
|
{
|
|
"id": "SE-01",
|
|
"risk": "CSP 配置过于严格可能阻断 CodeMirror 样式注入",
|
|
"mitigation": "使用 nonce 或 hash 方式允许 CodeMirror 内联样式"
|
|
}
|
|
]
|
|
}
|
|
}
|