feat: implement secure IPC-based file export for Electron

Replace sandboxed Capacitor filesystem with native IPC for reliable file exports:
- Add IPC handler in main process for direct Downloads folder access
- Expose secure electronAPI via contextBridge in preload script
- Update ElectronPlatformService to use native IPC with web fallback
- Add TypeScript definitions for electron APIs
- Fix file export issues where files were trapped in virtual filesystem
- Enable proper date-stamped backup filenames in Downloads folder
- Follow Electron security best practices with process isolation

Files now export directly to ~/Downloads with exact path feedback.
This commit is contained in:
Matthew Raymer
2025-07-06 03:46:28 +00:00
parent 10562b7c47
commit e883029531
7 changed files with 328 additions and 159 deletions

View File

@@ -559,13 +559,39 @@ export class WebPlatformService implements PlatformService {
}
/**
* Not supported in web platform.
* @param _fileName - Unused fileName parameter
* @param _content - Unused content parameter
* @throws Error indicating file system access is not available
* Downloads a file in the web platform using blob URLs and download links.
* Creates a temporary download link and triggers the browser's download mechanism.
* @param fileName - The name of the file to download
* @param content - The content to write to the file
* @returns Promise that resolves when the download is initiated
*/
async writeAndShareFile(_fileName: string, _content: string): Promise<void> {
throw new Error("File system access not available in web platform");
async writeAndShareFile(fileName: string, content: string): Promise<void> {
try {
// Create a blob with the content
const blob = new Blob([content], { type: "application/json" });
// Create a temporary download link
const url = URL.createObjectURL(blob);
const downloadLink = document.createElement("a");
downloadLink.href = url;
downloadLink.download = fileName;
downloadLink.style.display = "none";
// Add to DOM, click, and remove
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
// Clean up the object URL after a short delay
setTimeout(() => URL.revokeObjectURL(url), 1000);
logger.log("[WebPlatformService] File download initiated:", fileName);
} catch (error) {
logger.error("[WebPlatformService] Error downloading file:", error);
throw new Error(
`Failed to download file: ${error instanceof Error ? error.message : "Unknown error"}`,
);
}
}
/**