refactor: v2.0 全量重构 — TypeScript + React + Zustand + IndexedDB
技术栈升级: - JavaScript → TypeScript 5.6(全量类型安全) - 原生 DOM → React 18(函数组件 + Hooks) - 全局变量 → Zustand 5(轻量状态管理) - localStorage → IndexedDB / Dexie.js 4(大容量、异步、索引) - marked.js → unified / remark / rehype(插件化渲染管线) - 无打包 → electron-vite 3(HMR 热更新) - 纯 CSS → CSS Variables + CSS Modules 新增功能: - 标签页状态 IndexedDB 持久化(关闭后可恢复) - 最近打开文件列表 - 大文件虚拟化行号(>2000 行) - 搜索高亮二分查找优化 O(log N) - rehype-sanitize HTML 安全过滤 文件结构: - 7 个源文件 → 51 个模块化文件 - src/main/ 主进程(6 文件) - src/preload/ 预加载(1 文件) - src/renderer/ 渲染进程(42 文件:组件/hooks/stores/lib/db/types/styles) - src/shared/ 共享类型(2 文件) 构建验证: - TypeScript 检查零错误 - electron-vite build 成功 - 产物:main 15kB + preload 2kB + renderer 1.5MB
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
export interface FileNode {
|
||||
name: string
|
||||
path: string
|
||||
type: 'file' | 'dir'
|
||||
children?: FileNode[]
|
||||
}
|
||||
|
||||
export interface DirTreeResult {
|
||||
success: boolean
|
||||
tree?: FileNode[]
|
||||
rootPath?: string
|
||||
error?: string
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export type { Tab } from './tab'
|
||||
export type { FileNode, DirTreeResult } from './file'
|
||||
export type { ViewMode, Settings } from './settings'
|
||||
export type { SearchMatch, SearchOptions } from './search'
|
||||
export type { ElectronAPI, IpcInvokeMap } from './ipc'
|
||||
@@ -0,0 +1,59 @@
|
||||
import type {
|
||||
OpenFileResponse,
|
||||
ReadFileResult,
|
||||
SaveFilePayload,
|
||||
SaveFileResult,
|
||||
SaveAsPayload,
|
||||
ReloadFileResult,
|
||||
FileStatsResult,
|
||||
ReadDirTreeResult
|
||||
} from '../../shared/types'
|
||||
|
||||
export interface IpcInvokeMap {
|
||||
'dialog:openFile': [void, OpenFileResponse]
|
||||
'file:read': [string, ReadFileResult]
|
||||
'file:save': [SaveFilePayload, SaveFileResult]
|
||||
'file:saveAs': [SaveAsPayload, SaveFileResult]
|
||||
'file:getCurrentPath': [void, string | null]
|
||||
'file:stats': [string, FileStatsResult]
|
||||
'file:reload': [void, ReloadFileResult]
|
||||
'tab:switched': [string | null, void]
|
||||
'window:forceClose': [void, void]
|
||||
'window:cancelClose': [void, void]
|
||||
'dir:readTree': [string, ReadDirTreeResult]
|
||||
'dir:openDialog': [void, string | null]
|
||||
'dir:watch': [string, void]
|
||||
'dir:unwatch': [void, void]
|
||||
}
|
||||
|
||||
export interface ElectronAPI {
|
||||
openFile: () => Promise<OpenFileResponse>
|
||||
readFile: (filePath: string) => Promise<ReadFileResult>
|
||||
saveFile: (data: SaveFilePayload) => Promise<SaveFileResult>
|
||||
saveFileAs: (data: SaveAsPayload) => Promise<SaveFileResult>
|
||||
getCurrentPath: () => Promise<string | null>
|
||||
getFileStats: (filePath: string) => Promise<FileStatsResult>
|
||||
reloadFile: () => Promise<ReloadFileResult>
|
||||
tabSwitched: (filePath: string | null) => Promise<void>
|
||||
forceClose: () => Promise<void>
|
||||
cancelClose: () => Promise<void>
|
||||
openExternal: (url: string) => void
|
||||
readDirTree: (dirPath: string) => Promise<ReadDirTreeResult>
|
||||
openFolderDialog: () => Promise<string | null>
|
||||
watchDir: (dirPath: string) => Promise<void>
|
||||
unwatchDir: () => Promise<void>
|
||||
onFileOpenInTab: (callback: (data: { filePath: string; content: string }) => void) => void
|
||||
onMenuSave: (callback: () => void) => void
|
||||
onMenuSaveAs: (callback: () => void) => void
|
||||
onViewModeChange: (callback: (mode: string) => void) => void
|
||||
onExternalModification: (callback: (filePath: string) => void) => void
|
||||
onDirChanged: (callback: () => void) => void
|
||||
onConfirmClose: (callback: () => void) => void
|
||||
removeAllListeners: (channel: string) => void
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
electronAPI?: ElectronAPI
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export interface SearchMatch {
|
||||
start: number
|
||||
end: number
|
||||
}
|
||||
|
||||
export interface SearchOptions {
|
||||
caseSensitive: boolean
|
||||
useRegex: boolean
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
export type ViewMode = 'split' | 'editor' | 'preview'
|
||||
|
||||
export interface Settings {
|
||||
darkMode: boolean
|
||||
viewMode: ViewMode
|
||||
splitRatio: number
|
||||
sidebarCollapsed: boolean
|
||||
sidebarWidth: number
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: Settings = {
|
||||
darkMode: false,
|
||||
viewMode: 'split',
|
||||
splitRatio: 50,
|
||||
sidebarCollapsed: false,
|
||||
sidebarWidth: 240
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export interface Tab {
|
||||
id: string
|
||||
filePath: string | null
|
||||
content: string
|
||||
isModified: boolean
|
||||
scrollTop: number
|
||||
scrollLeft: number
|
||||
selectionStart: number
|
||||
selectionEnd: number
|
||||
previewScrollTop: number
|
||||
}
|
||||
Reference in New Issue
Block a user