feat(remote): 实现文件下载真实进度显示
- 下载改用流式读取,计算真实进度百分比 - 通过 IPC 事件实时推送进度到前端 - 支持 Content-Length 计算下载进度
This commit is contained in:
@@ -351,9 +351,9 @@ ipcMain.handle("remote-upload-file", async (_event, serverHost, port, filePath,
|
|||||||
return { success: false, error: error.message };
|
return { success: false, error: error.message };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ipcMain.handle("remote-download-file", async (_event, serverHost, port, fileName, remotePath, localPath, password) => {
|
ipcMain.handle("remote-download-file", async (_event, id, serverHost, port, fileName, remotePath, localPath, password) => {
|
||||||
try {
|
try {
|
||||||
log2.info("Remote download params:", { serverHost, port, fileName, remotePath, localPath, password });
|
log2.info("Remote download params:", { id, serverHost, port, fileName, remotePath, localPath, password });
|
||||||
const win = electronState.getMainWindow();
|
const win = electronState.getMainWindow();
|
||||||
if (!win) {
|
if (!win) {
|
||||||
throw new Error("No window found");
|
throw new Error("No window found");
|
||||||
@@ -367,13 +367,36 @@ ipcMain.handle("remote-download-file", async (_event, serverHost, port, fileName
|
|||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Download failed: ${response.statusText}`);
|
throw new Error(`Download failed: ${response.statusText}`);
|
||||||
}
|
}
|
||||||
const buffer = await response.arrayBuffer();
|
const contentLength = response.headers.get("Content-Length");
|
||||||
|
if (!contentLength) {
|
||||||
|
throw new Error("Server did not return Content-Length");
|
||||||
|
}
|
||||||
|
const totalSize = parseInt(contentLength, 10);
|
||||||
const targetDir = localPath || "C:\\";
|
const targetDir = localPath || "C:\\";
|
||||||
const targetPath = path2.join(targetDir, fileName);
|
const targetPath = path2.join(targetDir, fileName);
|
||||||
if (!fs2.existsSync(targetDir)) {
|
if (!fs2.existsSync(targetDir)) {
|
||||||
fs2.mkdirSync(targetDir, { recursive: true });
|
fs2.mkdirSync(targetDir, { recursive: true });
|
||||||
}
|
}
|
||||||
fs2.writeFileSync(targetPath, Buffer.from(buffer));
|
const fileStream = fs2.createWriteStream(targetPath);
|
||||||
|
const reader = response.body?.getReader();
|
||||||
|
if (!reader) {
|
||||||
|
throw new Error("Failed to get response body reader");
|
||||||
|
}
|
||||||
|
let downloadedSize = 0;
|
||||||
|
const CHUNK_SIZE = 64 * 1024;
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
if (done) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (value) {
|
||||||
|
downloadedSize += value.length;
|
||||||
|
fileStream.write(value);
|
||||||
|
const progress = Math.round(downloadedSize / totalSize * 100);
|
||||||
|
win.webContents.send("download-progress", { id, progress });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fileStream.end();
|
||||||
return { success: true, filePath: targetPath };
|
return { success: true, filePath: targetPath };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log2.error("Remote download failed:", error);
|
log2.error("Remote download failed:", error);
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -18,11 +18,16 @@ import_electron.contextBridge.exposeInMainWorld("electronAPI", {
|
|||||||
import_electron.ipcRenderer.on("remote-clipboard-auto-sync", handler);
|
import_electron.ipcRenderer.on("remote-clipboard-auto-sync", handler);
|
||||||
return () => import_electron.ipcRenderer.removeListener("remote-clipboard-auto-sync", handler);
|
return () => import_electron.ipcRenderer.removeListener("remote-clipboard-auto-sync", handler);
|
||||||
},
|
},
|
||||||
|
onDownloadProgress: (callback) => {
|
||||||
|
const handler = (_event, data) => callback(data);
|
||||||
|
import_electron.ipcRenderer.on("download-progress", handler);
|
||||||
|
return () => import_electron.ipcRenderer.removeListener("download-progress", handler);
|
||||||
|
},
|
||||||
clipboardReadText: () => import_electron.ipcRenderer.invoke("clipboard-read-text"),
|
clipboardReadText: () => import_electron.ipcRenderer.invoke("clipboard-read-text"),
|
||||||
clipboardWriteText: (text) => import_electron.ipcRenderer.invoke("clipboard-write-text", text),
|
clipboardWriteText: (text) => import_electron.ipcRenderer.invoke("clipboard-write-text", text),
|
||||||
remoteFetchDrives: (serverHost, port, password) => import_electron.ipcRenderer.invoke("remote-fetch-drives", serverHost, port, password),
|
remoteFetchDrives: (serverHost, port, password) => import_electron.ipcRenderer.invoke("remote-fetch-drives", serverHost, port, password),
|
||||||
remoteFetchFiles: (serverHost, port, filePath, password) => import_electron.ipcRenderer.invoke("remote-fetch-files", serverHost, port, filePath, password),
|
remoteFetchFiles: (serverHost, port, filePath, password) => import_electron.ipcRenderer.invoke("remote-fetch-files", serverHost, port, filePath, password),
|
||||||
remoteUploadFile: (serverHost, port, filePath, remotePath, password) => import_electron.ipcRenderer.invoke("remote-upload-file", serverHost, port, filePath, remotePath, password),
|
remoteUploadFile: (serverHost, port, filePath, remotePath, password) => import_electron.ipcRenderer.invoke("remote-upload-file", serverHost, port, filePath, remotePath, password),
|
||||||
remoteDownloadFile: (serverHost, port, fileName, remotePath, localPath, password) => import_electron.ipcRenderer.invoke("remote-download-file", serverHost, port, fileName, remotePath, localPath, password)
|
remoteDownloadFile: (id, serverHost, port, fileName, remotePath, localPath, password) => import_electron.ipcRenderer.invoke("remote-download-file", id, serverHost, port, fileName, remotePath, localPath, password)
|
||||||
});
|
});
|
||||||
//# sourceMappingURL=preload.cjs.map
|
//# sourceMappingURL=preload.cjs.map
|
||||||
@@ -1 +1 @@
|
|||||||
{"version":3,"sources":["../electron/preload.ts"],"sourcesContent":["import { contextBridge, ipcRenderer } from 'electron'\r\n\r\nconsole.log('--- PRELOAD SCRIPT LOADED SUCCESSFULLY ---')\r\n\r\ncontextBridge.exposeInMainWorld('electronAPI', {\r\n exportPDF: (title: string, htmlContent?: string) => ipcRenderer.invoke('export-pdf', title, htmlContent),\r\n selectHtmlFile: () => ipcRenderer.invoke('select-html-file'),\r\n updateTitlebarButtons: (symbolColor: string) => ipcRenderer.invoke('update-titlebar-buttons', symbolColor),\r\n onRemoteClipboardSyncToRemote: (callback: () => void) => {\r\n ipcRenderer.on('remote-clipboard-sync-to-remote', callback);\r\n return () => ipcRenderer.removeListener('remote-clipboard-sync-to-remote', callback);\r\n },\r\n onRemoteClipboardSyncFromRemote: (callback: () => void) => {\r\n ipcRenderer.on('remote-clipboard-sync-from-remote', callback);\r\n return () => ipcRenderer.removeListener('remote-clipboard-sync-from-remote', callback);\r\n },\r\n onRemoteClipboardAutoSync: (callback: (text: string) => void) => {\r\n const handler = (_event: Electron.IpcRendererEvent, text: string) => callback(text);\r\n ipcRenderer.on('remote-clipboard-auto-sync', handler);\r\n return () => ipcRenderer.removeListener('remote-clipboard-auto-sync', handler);\r\n },\r\n clipboardReadText: () => ipcRenderer.invoke('clipboard-read-text'),\r\n clipboardWriteText: (text: string) => ipcRenderer.invoke('clipboard-write-text', text),\r\n remoteFetchDrives: (serverHost: string, port: number, password?: string) => \r\n ipcRenderer.invoke('remote-fetch-drives', serverHost, port, password),\r\n remoteFetchFiles: (serverHost: string, port: number, filePath: string, password?: string) => \r\n ipcRenderer.invoke('remote-fetch-files', serverHost, port, filePath, password),\r\n remoteUploadFile: (serverHost: string, port: number, filePath: string, remotePath: string, password?: string) => \r\n ipcRenderer.invoke('remote-upload-file', serverHost, port, filePath, remotePath, password),\r\n remoteDownloadFile: (serverHost: string, port: number, fileName: string, remotePath: string, localPath: string, password?: string) => \r\n ipcRenderer.invoke('remote-download-file', serverHost, port, fileName, remotePath, localPath, password),\r\n})\r\n"],"mappings":";AAAA,sBAA2C;AAE3C,QAAQ,IAAI,4CAA4C;AAExD,8BAAc,kBAAkB,eAAe;AAAA,EAC7C,WAAW,CAAC,OAAe,gBAAyB,4BAAY,OAAO,cAAc,OAAO,WAAW;AAAA,EACvG,gBAAgB,MAAM,4BAAY,OAAO,kBAAkB;AAAA,EAC3D,uBAAuB,CAAC,gBAAwB,4BAAY,OAAO,2BAA2B,WAAW;AAAA,EACzG,+BAA+B,CAAC,aAAyB;AACvD,gCAAY,GAAG,mCAAmC,QAAQ;AAC1D,WAAO,MAAM,4BAAY,eAAe,mCAAmC,QAAQ;AAAA,EACrF;AAAA,EACA,iCAAiC,CAAC,aAAyB;AACzD,gCAAY,GAAG,qCAAqC,QAAQ;AAC5D,WAAO,MAAM,4BAAY,eAAe,qCAAqC,QAAQ;AAAA,EACvF;AAAA,EACA,2BAA2B,CAAC,aAAqC;AAC/D,UAAM,UAAU,CAAC,QAAmC,SAAiB,SAAS,IAAI;AAClF,gCAAY,GAAG,8BAA8B,OAAO;AACpD,WAAO,MAAM,4BAAY,eAAe,8BAA8B,OAAO;AAAA,EAC/E;AAAA,EACA,mBAAmB,MAAM,4BAAY,OAAO,qBAAqB;AAAA,EACjE,oBAAoB,CAAC,SAAiB,4BAAY,OAAO,wBAAwB,IAAI;AAAA,EACrF,mBAAmB,CAAC,YAAoB,MAAc,aACpD,4BAAY,OAAO,uBAAuB,YAAY,MAAM,QAAQ;AAAA,EACtE,kBAAkB,CAAC,YAAoB,MAAc,UAAkB,aACrE,4BAAY,OAAO,sBAAsB,YAAY,MAAM,UAAU,QAAQ;AAAA,EAC/E,kBAAkB,CAAC,YAAoB,MAAc,UAAkB,YAAoB,aACzF,4BAAY,OAAO,sBAAsB,YAAY,MAAM,UAAU,YAAY,QAAQ;AAAA,EAC3F,oBAAoB,CAAC,YAAoB,MAAc,UAAkB,YAAoB,WAAmB,aAC9G,4BAAY,OAAO,wBAAwB,YAAY,MAAM,UAAU,YAAY,WAAW,QAAQ;AAC1G,CAAC;","names":[]}
|
{"version":3,"sources":["../electron/preload.ts"],"sourcesContent":["import { contextBridge, ipcRenderer } from 'electron'\r\n\r\nconsole.log('--- PRELOAD SCRIPT LOADED SUCCESSFULLY ---')\r\n\r\ncontextBridge.exposeInMainWorld('electronAPI', {\r\n exportPDF: (title: string, htmlContent?: string) => ipcRenderer.invoke('export-pdf', title, htmlContent),\r\n selectHtmlFile: () => ipcRenderer.invoke('select-html-file'),\r\n updateTitlebarButtons: (symbolColor: string) => ipcRenderer.invoke('update-titlebar-buttons', symbolColor),\r\n onRemoteClipboardSyncToRemote: (callback: () => void) => {\r\n ipcRenderer.on('remote-clipboard-sync-to-remote', callback);\r\n return () => ipcRenderer.removeListener('remote-clipboard-sync-to-remote', callback);\r\n },\r\n onRemoteClipboardSyncFromRemote: (callback: () => void) => {\r\n ipcRenderer.on('remote-clipboard-sync-from-remote', callback);\r\n return () => ipcRenderer.removeListener('remote-clipboard-sync-from-remote', callback);\r\n },\r\n onRemoteClipboardAutoSync: (callback: (text: string) => void) => {\r\n const handler = (_event: Electron.IpcRendererEvent, text: string) => callback(text);\r\n ipcRenderer.on('remote-clipboard-auto-sync', handler);\r\n return () => ipcRenderer.removeListener('remote-clipboard-auto-sync', handler);\r\n },\r\n onDownloadProgress: (callback: (data: { progress: number; id: string }) => void) => {\r\n const handler = (_event: Electron.IpcRendererEvent, data: { progress: number; id: string }) => callback(data);\r\n ipcRenderer.on('download-progress', handler);\r\n return () => ipcRenderer.removeListener('download-progress', handler);\r\n },\r\n clipboardReadText: () => ipcRenderer.invoke('clipboard-read-text'),\r\n clipboardWriteText: (text: string) => ipcRenderer.invoke('clipboard-write-text', text),\r\n remoteFetchDrives: (serverHost: string, port: number, password?: string) => \r\n ipcRenderer.invoke('remote-fetch-drives', serverHost, port, password),\r\n remoteFetchFiles: (serverHost: string, port: number, filePath: string, password?: string) => \r\n ipcRenderer.invoke('remote-fetch-files', serverHost, port, filePath, password),\r\n remoteUploadFile: (serverHost: string, port: number, filePath: string, remotePath: string, password?: string) => \r\n ipcRenderer.invoke('remote-upload-file', serverHost, port, filePath, remotePath, password),\r\n remoteDownloadFile: (id: string, serverHost: string, port: number, fileName: string, remotePath: string, localPath: string, password?: string) => \r\n ipcRenderer.invoke('remote-download-file', id, serverHost, port, fileName, remotePath, localPath, password),\r\n})\r\n"],"mappings":";AAAA,sBAA2C;AAE3C,QAAQ,IAAI,4CAA4C;AAExD,8BAAc,kBAAkB,eAAe;AAAA,EAC7C,WAAW,CAAC,OAAe,gBAAyB,4BAAY,OAAO,cAAc,OAAO,WAAW;AAAA,EACvG,gBAAgB,MAAM,4BAAY,OAAO,kBAAkB;AAAA,EAC3D,uBAAuB,CAAC,gBAAwB,4BAAY,OAAO,2BAA2B,WAAW;AAAA,EACzG,+BAA+B,CAAC,aAAyB;AACvD,gCAAY,GAAG,mCAAmC,QAAQ;AAC1D,WAAO,MAAM,4BAAY,eAAe,mCAAmC,QAAQ;AAAA,EACrF;AAAA,EACA,iCAAiC,CAAC,aAAyB;AACzD,gCAAY,GAAG,qCAAqC,QAAQ;AAC5D,WAAO,MAAM,4BAAY,eAAe,qCAAqC,QAAQ;AAAA,EACvF;AAAA,EACA,2BAA2B,CAAC,aAAqC;AAC/D,UAAM,UAAU,CAAC,QAAmC,SAAiB,SAAS,IAAI;AAClF,gCAAY,GAAG,8BAA8B,OAAO;AACpD,WAAO,MAAM,4BAAY,eAAe,8BAA8B,OAAO;AAAA,EAC/E;AAAA,EACA,oBAAoB,CAAC,aAA+D;AAClF,UAAM,UAAU,CAAC,QAAmC,SAA2C,SAAS,IAAI;AAC5G,gCAAY,GAAG,qBAAqB,OAAO;AAC3C,WAAO,MAAM,4BAAY,eAAe,qBAAqB,OAAO;AAAA,EACtE;AAAA,EACA,mBAAmB,MAAM,4BAAY,OAAO,qBAAqB;AAAA,EACjE,oBAAoB,CAAC,SAAiB,4BAAY,OAAO,wBAAwB,IAAI;AAAA,EACrF,mBAAmB,CAAC,YAAoB,MAAc,aACpD,4BAAY,OAAO,uBAAuB,YAAY,MAAM,QAAQ;AAAA,EACtE,kBAAkB,CAAC,YAAoB,MAAc,UAAkB,aACrE,4BAAY,OAAO,sBAAsB,YAAY,MAAM,UAAU,QAAQ;AAAA,EAC/E,kBAAkB,CAAC,YAAoB,MAAc,UAAkB,YAAoB,aACzF,4BAAY,OAAO,sBAAsB,YAAY,MAAM,UAAU,YAAY,QAAQ;AAAA,EAC3F,oBAAoB,CAAC,IAAY,YAAoB,MAAc,UAAkB,YAAoB,WAAmB,aAC1H,4BAAY,OAAO,wBAAwB,IAAI,YAAY,MAAM,UAAU,YAAY,WAAW,QAAQ;AAC9G,CAAC;","names":[]}
|
||||||
@@ -255,9 +255,9 @@ ipcMain.handle('remote-upload-file', async (_event, serverHost: string, port: nu
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('remote-download-file', async (_event, serverHost: string, port: number, fileName: string, remotePath: string, localPath: string, password?: string) => {
|
ipcMain.handle('remote-download-file', async (_event, id: string, serverHost: string, port: number, fileName: string, remotePath: string, localPath: string, password?: string) => {
|
||||||
try {
|
try {
|
||||||
log.info('Remote download params:', { serverHost, port, fileName, remotePath, localPath, password });
|
log.info('Remote download params:', { id, serverHost, port, fileName, remotePath, localPath, password });
|
||||||
|
|
||||||
const win = electronState.getMainWindow();
|
const win = electronState.getMainWindow();
|
||||||
if (!win) {
|
if (!win) {
|
||||||
@@ -275,8 +275,12 @@ ipcMain.handle('remote-download-file', async (_event, serverHost: string, port:
|
|||||||
throw new Error(`Download failed: ${response.statusText}`);
|
throw new Error(`Download failed: ${response.statusText}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const buffer = await response.arrayBuffer();
|
const contentLength = response.headers.get('Content-Length');
|
||||||
|
if (!contentLength) {
|
||||||
|
throw new Error('Server did not return Content-Length');
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalSize = parseInt(contentLength, 10);
|
||||||
const targetDir = localPath || 'C:\\';
|
const targetDir = localPath || 'C:\\';
|
||||||
const targetPath = path.join(targetDir, fileName);
|
const targetPath = path.join(targetDir, fileName);
|
||||||
|
|
||||||
@@ -284,8 +288,31 @@ ipcMain.handle('remote-download-file', async (_event, serverHost: string, port:
|
|||||||
fs.mkdirSync(targetDir, { recursive: true });
|
fs.mkdirSync(targetDir, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.writeFileSync(targetPath, Buffer.from(buffer));
|
const fileStream = fs.createWriteStream(targetPath);
|
||||||
|
const reader = response.body?.getReader();
|
||||||
|
if (!reader) {
|
||||||
|
throw new Error('Failed to get response body reader');
|
||||||
|
}
|
||||||
|
|
||||||
|
let downloadedSize = 0;
|
||||||
|
const CHUNK_SIZE = 64 * 1024;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
if (done) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
downloadedSize += value.length;
|
||||||
|
fileStream.write(value);
|
||||||
|
|
||||||
|
const progress = Math.round((downloadedSize / totalSize) * 100);
|
||||||
|
win.webContents.send('download-progress', { id, progress });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileStream.end();
|
||||||
return { success: true, filePath: targetPath };
|
return { success: true, filePath: targetPath };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
log.error('Remote download failed:', error);
|
log.error('Remote download failed:', error);
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||||||
ipcRenderer.on('remote-clipboard-auto-sync', handler);
|
ipcRenderer.on('remote-clipboard-auto-sync', handler);
|
||||||
return () => ipcRenderer.removeListener('remote-clipboard-auto-sync', handler);
|
return () => ipcRenderer.removeListener('remote-clipboard-auto-sync', handler);
|
||||||
},
|
},
|
||||||
|
onDownloadProgress: (callback: (data: { progress: number; id: string }) => void) => {
|
||||||
|
const handler = (_event: Electron.IpcRendererEvent, data: { progress: number; id: string }) => callback(data);
|
||||||
|
ipcRenderer.on('download-progress', handler);
|
||||||
|
return () => ipcRenderer.removeListener('download-progress', handler);
|
||||||
|
},
|
||||||
clipboardReadText: () => ipcRenderer.invoke('clipboard-read-text'),
|
clipboardReadText: () => ipcRenderer.invoke('clipboard-read-text'),
|
||||||
clipboardWriteText: (text: string) => ipcRenderer.invoke('clipboard-write-text', text),
|
clipboardWriteText: (text: string) => ipcRenderer.invoke('clipboard-write-text', text),
|
||||||
remoteFetchDrives: (serverHost: string, port: number, password?: string) =>
|
remoteFetchDrives: (serverHost: string, port: number, password?: string) =>
|
||||||
@@ -27,6 +32,6 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||||||
ipcRenderer.invoke('remote-fetch-files', serverHost, port, filePath, password),
|
ipcRenderer.invoke('remote-fetch-files', serverHost, port, filePath, password),
|
||||||
remoteUploadFile: (serverHost: string, port: number, filePath: string, remotePath: string, password?: string) =>
|
remoteUploadFile: (serverHost: string, port: number, filePath: string, remotePath: string, password?: string) =>
|
||||||
ipcRenderer.invoke('remote-upload-file', serverHost, port, filePath, remotePath, password),
|
ipcRenderer.invoke('remote-upload-file', serverHost, port, filePath, remotePath, password),
|
||||||
remoteDownloadFile: (serverHost: string, port: number, fileName: string, remotePath: string, localPath: string, password?: string) =>
|
remoteDownloadFile: (id: string, serverHost: string, port: number, fileName: string, remotePath: string, localPath: string, password?: string) =>
|
||||||
ipcRenderer.invoke('remote-download-file', serverHost, port, fileName, remotePath, localPath, password),
|
ipcRenderer.invoke('remote-download-file', id, serverHost, port, fileName, remotePath, localPath, password),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -119,13 +119,31 @@ export const downloadFileFromRemote = async (
|
|||||||
password?: string,
|
password?: string,
|
||||||
onProgress?: (progress: number) => void
|
onProgress?: (progress: number) => void
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
onProgress?.(50)
|
const transferId = Date.now().toString()
|
||||||
const result = await window.electronAPI.remoteDownloadFile(serverHost, port, fileName, remotePath, localPath, password)
|
|
||||||
onProgress?.(100)
|
const cleanup = window.electronAPI?.onDownloadProgress?.((data) => {
|
||||||
|
if (data.id === transferId) {
|
||||||
|
onProgress?.(data.progress)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await window.electronAPI.remoteDownloadFile(
|
||||||
|
transferId,
|
||||||
|
serverHost,
|
||||||
|
port,
|
||||||
|
fileName,
|
||||||
|
remotePath,
|
||||||
|
localPath,
|
||||||
|
password
|
||||||
|
)
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
if (result.canceled) {
|
if (result.canceled) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
throw new Error(result.error || 'Download failed')
|
throw new Error(result.error || 'Download failed')
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
cleanup?.()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
3
src/types/electron.d.ts
vendored
3
src/types/electron.d.ts
vendored
@@ -14,6 +14,7 @@ export interface ElectronAPI {
|
|||||||
onRemoteClipboardSyncToRemote: (callback: () => void) => () => void
|
onRemoteClipboardSyncToRemote: (callback: () => void) => () => void
|
||||||
onRemoteClipboardSyncFromRemote: (callback: () => void) => () => void
|
onRemoteClipboardSyncFromRemote: (callback: () => void) => () => void
|
||||||
onRemoteClipboardAutoSync: (callback: (text: string) => void) => () => void
|
onRemoteClipboardAutoSync: (callback: (text: string) => void) => () => void
|
||||||
|
onDownloadProgress: (callback: (data: { progress: number; id: string }) => void) => () => void
|
||||||
clipboardReadText: () => Promise<{ success: boolean; text?: string; error?: string }>
|
clipboardReadText: () => Promise<{ success: boolean; text?: string; error?: string }>
|
||||||
clipboardWriteText: (text: string) => Promise<{ success: boolean; error?: string }>
|
clipboardWriteText: (text: string) => Promise<{ success: boolean; error?: string }>
|
||||||
remoteFetchDrives: (serverHost: string, port: number, password?: string) => Promise<{
|
remoteFetchDrives: (serverHost: string, port: number, password?: string) => Promise<{
|
||||||
@@ -30,7 +31,7 @@ export interface ElectronAPI {
|
|||||||
success: boolean
|
success: boolean
|
||||||
error?: string
|
error?: string
|
||||||
}>
|
}>
|
||||||
remoteDownloadFile: (serverHost: string, port: number, fileName: string, remotePath: string, localPath: string, password?: string) => Promise<{
|
remoteDownloadFile: (id: string, serverHost: string, port: number, fileName: string, remotePath: string, localPath: string, password?: string) => Promise<{
|
||||||
success: boolean
|
success: boolean
|
||||||
filePath?: string
|
filePath?: string
|
||||||
error?: string
|
error?: string
|
||||||
|
|||||||
Reference in New Issue
Block a user