feat: 任务列表右键菜单回调 + 主进程下载 + sharp 缩略图 + IPC 精简重构
- 任务右键菜单:实现打开输出目录(inputs/outputs/.cache 结构)、
复制资源链接、重新编辑、再次生成、拉取结果、重新下载、收藏、删除记录
- 主进程下载:通过 Electron net 模块绕过 CORS,支持 CDN 媒体文件下载
- sharp 缩略图:替换 nativeImage,支持 4K/8K+ 大尺寸素材的流式处理
- IPC 精简:40+ 冗余通道 → 15 个分组通道,移除未使用的 preload 桥接方法
- 任务存储:新增收藏字段、schema 迁移、task-store 持久化
- 清理废弃模块:canvas-ipc / updater / request / pricing
refactor: IPC/Preload/Services 模块化拆分 — 单一职责 + 向后兼容
主要变更:
- shared/constants/ipc-channels.ts → ipc/ 模块 (base/storage/canvas/updater)
- electron/preload.ts → preload/ 模块 (base/safe-storage/file-storage/canvas)
- electron/main/ipc/canvas-ipc.ts → canvas/ 模块 (types/walk/sidecar/mime/thumbnail)
- electron/main/ipc/storage-ipc.ts → 提取 utils/ (path/download/thumbnail)
- electron/main/updater.ts → updater/ 模块 (provider/platform-updaters)
- src/services/request.ts → request/ 模块 (token/interceptors/methods)
- src/shared/utils/pricing.ts → pricing/ 模块 (types/helpers/format/calculate/fields)
- src/modules/home/center/views/ModelInputForm.tsx → 提取 utils + DependentFormItem
架构优化:
- 所有拆分均保持向后兼容(旧导入路径仍可用)
- TypeScript 编译通过 ✓
- 每个模块单一职责,平均 ~130 行
This commit is contained in:
94
electron/preload/file-storage.ts
Normal file
94
electron/preload/file-storage.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
// ============================================================
|
||||
// preload/file-storage.ts — 本地存储文件 API(主进程 fs 桥接)
|
||||
//
|
||||
// 提供文件读写、目录管理、下载等接口
|
||||
// 渲染进程通过 window.fileStorage 调用
|
||||
// ============================================================
|
||||
|
||||
import { ipcRenderer, contextBridge } from 'electron';
|
||||
import { BIDIRECTIONAL } from '../../shared/constants/ipc';
|
||||
|
||||
/** 本地存储文件 API */
|
||||
export function exposeFileStorage(): void {
|
||||
contextBridge.exposeInMainWorld('fileStorage', {
|
||||
/** 获取 heixiu-data 根目录路径 */
|
||||
async getDataDir(): Promise<string | null> {
|
||||
const r = await ipcRenderer.invoke(BIDIRECTIONAL.GET_DATA_DIR);
|
||||
return r?.success ? r.data : null;
|
||||
},
|
||||
|
||||
/** 写入文件(Base64 → 磁盘),返回写入的绝对路径 */
|
||||
async writeFile(relativePath: string, data: string, encoding?: 'base64' | 'utf8'): Promise<string | null> {
|
||||
const r = await ipcRenderer.invoke(BIDIRECTIONAL.FILE_WRITE, { relativePath, data, encoding });
|
||||
return r?.success ? r.data : null;
|
||||
},
|
||||
|
||||
/** 读取文件(磁盘 → Base64) */
|
||||
async readFile(relativePath: string): Promise<string | null> {
|
||||
const r = await ipcRenderer.invoke(BIDIRECTIONAL.FILE_READ, { relativePath });
|
||||
return r?.success ? r.data : null;
|
||||
},
|
||||
|
||||
/** 删除文件 */
|
||||
async deleteFile(relativePath: string): Promise<boolean> {
|
||||
const r = await ipcRenderer.invoke(BIDIRECTIONAL.FILE_DELETE, { relativePath });
|
||||
return r?.success ?? false;
|
||||
},
|
||||
|
||||
/** 递归创建目录 */
|
||||
async makeDir(relativePath: string): Promise<string | null> {
|
||||
const r = await ipcRenderer.invoke(BIDIRECTIONAL.MAKE_DIR, { relativePath });
|
||||
return r?.success ? r.data : null;
|
||||
},
|
||||
|
||||
/** 在文件管理器中显示文件(绝对路径) */
|
||||
async showItemInFolder(filePath: string): Promise<boolean> {
|
||||
const r = await ipcRenderer.invoke(BIDIRECTIONAL.SHOW_ITEM_IN_FOLDER, { filePath });
|
||||
return r?.success ?? false;
|
||||
},
|
||||
|
||||
/** 获取文件大小(字节) */
|
||||
async fileSize(relativePath: string): Promise<number | null> {
|
||||
const r = await ipcRenderer.invoke(BIDIRECTIONAL.FILE_SIZE, { relativePath });
|
||||
return r?.success ? r.data : null;
|
||||
},
|
||||
|
||||
/** 检查文件/目录是否存在 */
|
||||
async fileExists(relativePath: string): Promise<boolean> {
|
||||
const r = await ipcRenderer.invoke(BIDIRECTIONAL.FILE_EXISTS, { relativePath });
|
||||
return r?.success ? r.data : false;
|
||||
},
|
||||
|
||||
/** 绝对路径文件写入(绕过 heixiu-data 沙箱) */
|
||||
async writeFileAbsolute(absolutePath: string, data: string, encoding?: 'base64' | 'utf8'): Promise<string | null> {
|
||||
const r = await ipcRenderer.invoke(BIDIRECTIONAL.FILE_WRITE_ABSOLUTE, { absolutePath, data, encoding });
|
||||
return r?.success ? r.data : null;
|
||||
},
|
||||
|
||||
/** 绝对路径目录创建(绕过 heixiu-data 沙箱) */
|
||||
async makeDirAbsolute(absolutePath: string): Promise<string | null> {
|
||||
const r = await ipcRenderer.invoke(BIDIRECTIONAL.MAKE_DIR_ABSOLUTE, { absolutePath });
|
||||
return r?.success ? r.data : null;
|
||||
},
|
||||
|
||||
/** 主进程下载文件到绝对路径(绕过浏览器 CORS 限制),失败时 reject 并携带错误信息 */
|
||||
async downloadToPath(url: string, destPath: string, headers?: Record<string, string>): Promise<string> {
|
||||
const r = await ipcRenderer.invoke(BIDIRECTIONAL.DOWNLOAD_TO_PATH, { url, destPath, headers });
|
||||
if (r?.success) return r.data;
|
||||
throw new Error(r?.error || '主进程下载失败');
|
||||
},
|
||||
|
||||
/** 获取系统下载目录路径 */
|
||||
async getDownloadsPath(): Promise<string | null> {
|
||||
const r = await ipcRenderer.invoke(BIDIRECTIONAL.GET_DOWNLOADS_PATH);
|
||||
return r?.success ? r.data : null;
|
||||
},
|
||||
|
||||
/** 生成图片缩略图(读取原图 → 缩放 → JPEG 写入目标路径) */
|
||||
async generateThumbnail(srcPath: string, destPath: string, maxWidth?: number, quality?: number): Promise<string> {
|
||||
const r = await ipcRenderer.invoke(BIDIRECTIONAL.GENERATE_THUMBNAIL, { srcPath, destPath, maxWidth, quality });
|
||||
if (r?.success) return r.data;
|
||||
throw new Error(r?.error || '缩略图生成失败');
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user