- 任务右键菜单:实现打开输出目录(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 行
67 lines
1.9 KiB
TypeScript
67 lines
1.9 KiB
TypeScript
// ============================================================
|
|
// canvas/walk.ts — 目录遍历算法
|
|
//
|
|
// 职责:
|
|
// - 递归遍历目录,收集所有匹配扩展名的文件
|
|
// - 跳过隐藏文件和文件夹
|
|
// ============================================================
|
|
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import type { ScanEntry } from './types';
|
|
|
|
/**
|
|
* 递归遍历目录,收集所有匹配扩展名的文件。
|
|
*
|
|
* @param root - 根目录路径
|
|
* @param extensions - 允许的文件扩展名列表(不含点号)
|
|
* @param recursive - 是否递归遍历子目录
|
|
* @returns 文件列表
|
|
*/
|
|
export function walkDirectory(
|
|
root: string,
|
|
extensions: string[],
|
|
recursive: boolean,
|
|
): ScanEntry[] {
|
|
const results: ScanEntry[] = [];
|
|
const extSet = new Set(extensions.map((e) => e.toLowerCase()));
|
|
|
|
function walk(dir: string) {
|
|
let entries: fs.Dirent[];
|
|
try {
|
|
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
} catch {
|
|
return; // 权限不足等,跳过该目录
|
|
}
|
|
|
|
for (const entry of entries) {
|
|
// 跳过隐藏文件和文件夹
|
|
if (entry.name.startsWith('.')) continue;
|
|
|
|
const fullPath = path.join(dir, entry.name);
|
|
if (entry.isDirectory()) {
|
|
if (recursive) walk(fullPath);
|
|
} else if (entry.isFile()) {
|
|
const ext = path.extname(entry.name).slice(1).toLowerCase();
|
|
if (extSet.has(ext)) {
|
|
try {
|
|
const stat = fs.statSync(fullPath);
|
|
results.push({
|
|
path: fullPath,
|
|
fileName: entry.name,
|
|
ext,
|
|
fileSize: stat.size,
|
|
modifiedAt: stat.mtime.toISOString(),
|
|
});
|
|
} catch {
|
|
// 文件不可访问,跳过
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
walk(root);
|
|
return results;
|
|
}
|