Improve attachment memory safety, downloads, and high-memory alert UX.
All checks were successful
Queue Release Build / prepare (push) Successful in 20s
Deploy Web Apps / deploy (push) Successful in 9m2s
Queue Release Build / build-windows (push) Successful in 28m8s
Queue Release Build / build-linux (push) Successful in 47m26s
Queue Release Build / build-android (push) Successful in 19m52s
Queue Release Build / finalize (push) Successful in 4m42s

Stream large receives to disk with chunk acks to cap renderer RAM, evict
off-screen display blobs, and route exports through a disk-aware download
service. Fix the high-memory dialog (backdrop dismiss, copy, log actions),
allow diagnostics paths in the path jail, and restore persisted image
hydration after reload.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-14 00:25:22 +02:00
parent f0d79aa627
commit bb0ac930ad
69 changed files with 2306 additions and 430 deletions

View File

@@ -11,6 +11,7 @@ const AUTO_UPDATE_STATE_CHANGED_CHANNEL = 'auto-update-state-changed';
const DEEP_LINK_RECEIVED_CHANNEL = 'deep-link-received';
const WINDOW_STATE_CHANGED_CHANNEL = 'window-state-changed';
const IDLE_STATE_CHANGED_CHANNEL = 'idle-state-changed';
const HIGH_MEMORY_ALERT_PENDING_CHANNEL = 'high-memory-alert-pending';
export interface LinuxScreenShareAudioRoutingInfo {
available: boolean;
@@ -264,8 +265,23 @@ export interface ElectronAPI {
detectedAt: number;
peakWorkingSetKb: number;
sessionId: string;
reason?: 'manual' | 'threshold';
} | null>;
acknowledgeHighMemoryAlert: () => Promise<boolean>;
exportHighMemoryDiagnostics: () => Promise<{
logFilePath: string;
detectedAt: number;
peakWorkingSetKb: number;
sessionId: string;
reason?: 'manual' | 'threshold';
}>;
onHighMemoryAlertPending: (listener: (alert: {
logFilePath: string;
detectedAt: number;
peakWorkingSetKb: number;
sessionId: string;
reason?: 'manual' | 'threshold';
}) => void) => () => void;
showLogFileInFolder: (filePath: string) => Promise<{ shown: boolean; reason?: string }>;
getAppDataPath: () => Promise<string>;
openCurrentDataFolder: () => Promise<boolean>;
@@ -335,6 +351,7 @@ export interface ElectronAPI {
grantPluginReadRoot: (rootPath: string) => Promise<boolean>;
writeFile: (filePath: string, data: string) => Promise<boolean>;
appendFile: (filePath: string, data: string) => Promise<boolean>;
appendFileBytes: (filePath: string, data: Uint8Array) => Promise<boolean>;
saveFileAs: (defaultFileName: string, data: string) => Promise<{ saved: boolean; cancelled: boolean }>;
saveExistingFileAs: (sourceFilePath: string, defaultFileName: string) => Promise<{ saved: boolean; cancelled: boolean }>;
openFilePath: (filePath: string) => Promise<{ opened: boolean; reason?: string }>;
@@ -410,6 +427,23 @@ const electronAPI: ElectronAPI = {
reportPerfDiagSample: (entry) => ipcRenderer.invoke('perf-diag-report', entry),
getPendingHighMemoryAlert: () => ipcRenderer.invoke('get-pending-high-memory-alert'),
acknowledgeHighMemoryAlert: () => ipcRenderer.invoke('acknowledge-high-memory-alert'),
exportHighMemoryDiagnostics: () => ipcRenderer.invoke('export-high-memory-diagnostics'),
onHighMemoryAlertPending: (listener) => {
const wrappedListener = (_event: Electron.IpcRendererEvent, alert: {
logFilePath: string;
detectedAt: number;
peakWorkingSetKb: number;
sessionId: string;
}) => {
listener(alert);
};
ipcRenderer.on(HIGH_MEMORY_ALERT_PENDING_CHANNEL, wrappedListener);
return () => {
ipcRenderer.removeListener(HIGH_MEMORY_ALERT_PENDING_CHANNEL, wrappedListener);
};
},
showLogFileInFolder: (filePath) => ipcRenderer.invoke('show-log-file-in-folder', filePath),
getAppDataPath: () => ipcRenderer.invoke('get-app-data-path'),
openCurrentDataFolder: () => ipcRenderer.invoke('open-current-data-folder'),
@@ -478,6 +512,7 @@ const electronAPI: ElectronAPI = {
grantPluginReadRoot: (rootPath) => ipcRenderer.invoke('grant-plugin-read-root', rootPath),
writeFile: (filePath, data) => ipcRenderer.invoke('write-file', filePath, data),
appendFile: (filePath, data) => ipcRenderer.invoke('append-file', filePath, data),
appendFileBytes: (filePath, data) => ipcRenderer.invoke('append-file-bytes', filePath, data),
saveFileAs: (defaultFileName, data) => ipcRenderer.invoke('save-file-as', defaultFileName, data),
saveExistingFileAs: (sourceFilePath, defaultFileName) => ipcRenderer.invoke('save-existing-file-as', sourceFilePath, defaultFileName),
openFilePath: (filePath) => ipcRenderer.invoke('open-file-path', filePath),