// ============================================================ // preload/canvas.ts — 无限画布 API // // 提供画布窗口、文件扫描、缩略图、拖拽等接口 // 渲染进程通过 window.canvas 调用 // ============================================================ import { ipcRenderer, contextBridge } from 'electron'; import { BIDIRECTIONAL, RENDERER_TO_MAIN } from '../../shared/constants/ipc'; /** 无限画布 API */ export function exposeCanvas(): void { contextBridge.exposeInMainWorld('canvas', { /** 打开画布窗口(主进程创建新 BrowserWindow) */ openWindow(projectPath?: string): void { ipcRenderer.send(RENDERER_TO_MAIN.OPEN_CANVAS_WINDOW, { projectPath }); }, /** 扫描目录,返回匹配的文件列表 */ scanDirectory(params: { roots: string[]; extensions: string[]; recursive: boolean; }): Promise<{ success: boolean; data: Array<{ path: string; fileName: string; ext: string; fileSize: number; modifiedAt: string }>; error?: string; }> { return ipcRenderer.invoke(BIDIRECTIONAL.CANVAS_SCAN_DIRECTORY, params); }, /** 扫描项目(主进程内部完成 buildScanRoots + walkDirectory,推荐使用) */ scanProject(params: { projectPath: string; stage: string; assetTypeFilter: string; shotQuery: string; }): Promise<{ success: boolean; data: Array<{ path: string; fileName: string; ext: string; fileSize: number; modifiedAt: string }>; error?: string; }> { return ipcRenderer.invoke(BIDIRECTIONAL.CANVAS_SCAN_PROJECT, params); }, /** 读取旁加载 JSON 元数据 */ readSidecar(params: { filePath: string }): Promise<{ success: boolean; data: object | null }> { return ipcRenderer.invoke(BIDIRECTIONAL.CANVAS_READ_SIDECAR, params); }, /** 写入旁加载 JSON 元数据 */ writeSidecar(params: { filePath: string; metadata: object }): Promise<{ success: boolean; error?: string }> { return ipcRenderer.invoke(BIDIRECTIONAL.CANVAS_WRITE_SIDECAR, params); }, /** 生成缩略图(JPEG base64) */ generateThumbnail(params: { filePath: string; size: number }): Promise<{ success: boolean; data: { data: string; mimeType: string; width: number; height: number }; error?: string; }> { return ipcRenderer.invoke(BIDIRECTIONAL.CANVAS_GENERATE_THUMBNAIL, params); }, /** 复制文件到目标目录(导入操作) */ copyFile(params: { src: string; destDir: string; fileName: string }): Promise<{ success: boolean; data: string; error?: string; }> { return ipcRenderer.invoke(BIDIRECTIONAL.CANVAS_COPY_FILE, params); }, /** 列出目录下的子文件夹(非递归,排除隐藏) */ listSubdirs(params: { parentPath: string }): Promise<{ success: boolean; data: string[]; error?: string }> { return ipcRenderer.invoke(BIDIRECTIONAL.CANVAS_LIST_SUBDIRS, params); }, /** 扫描阶段文件(单个 episode/shot/stage 组合) */ scanStageFiles(params: { projectPath: string; episode: string; shot: string; stage: string }): Promise<{ success: boolean; data: Array<{ path: string; fileName: string; ext: string; fileSize: number; modifiedAt: string }>; error?: string; }> { return ipcRenderer.invoke(BIDIRECTIONAL.CANVAS_SCAN_STAGE_FILES, params); }, /** 扫描资产文件(深度递归扫描 assets/ 目录) */ scanAssetFiles(params: { projectPath: string; assetTypeFilter: string }): Promise<{ success: boolean; data: Array<{ path: string; fileName: string; ext: string; fileSize: number; modifiedAt: string }>; error?: string; }> { return ipcRenderer.invoke(BIDIRECTIONAL.CANVAS_SCAN_ASSET_FILES, params); }, /** 创建目录 */ createDirectory(params: { parentPath: string; dirName: string }): Promise<{ success: boolean; data: string; error?: string; }> { return ipcRenderer.invoke(BIDIRECTIONAL.CANVAS_CREATE_DIRECTORY, params); }, /** 将本地文件路径转换为可通过 local-media:// 协议加载的 URL */ getMediaUrl(filePath: string): string { const normalized = filePath.replace(/\\/g, '/'); // UNC 路径(//server/share/…)开头 // 会被 URL 解析器视为 authority, // 必须用 local-media:/// 格式而非 local-media:/// 前缀 if (normalized.startsWith('//')) { // normalized = "//192.168.110.250/share/path" return 'local-media://' + normalized.slice(2); } // 本地路径:C:/path → local-media:///C:/path return 'local-media:///' + normalized; }, /** 启动原生文件拖拽(从缩略图拖放媒体文件到外部应用) */ startDrag(params: { filePath: string; fileName: string }): Promise<{ success: boolean; error?: string }> { return ipcRenderer.invoke(BIDIRECTIONAL.CANVAS_START_DRAG, params); }, }); }