fix(files): prevent autosave data loss on load lag and binary files

Guard FilesView autosave until the selected file has finished loading,
refuse binary/PDF/office/archive text saves, and add a persisted global
autoSaveEnabled setting (default true) under Settings → General.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-07-30 06:37:47 +00:00
co-authored by Serhii Dziupin
parent a4c7bac303
commit 5b727ed53c
34 changed files with 504 additions and 55 deletions
+54
View File
@@ -0,0 +1,54 @@
export type FileEditorAutosaveGate = {
autoSaveEnabled: boolean;
isDirty: boolean;
canWrite: boolean;
isSaving: boolean;
fileLoading: boolean;
selectedFilePath: string | null | undefined;
loadedFilePath: string | null;
/** True when the selected file must never be written as text (binary / non-editable). */
isNonEditableBinary: boolean;
};
/**
* Whether the FilesView autosave effect should schedule a debounced save.
* Incomplete loads and binary files must never trigger a write.
*/
export function shouldScheduleFileAutosave(gate: FileEditorAutosaveGate): boolean {
if (!gate.autoSaveEnabled || !gate.isDirty || !gate.canWrite || gate.isSaving) {
return false;
}
if (gate.fileLoading || gate.isNonEditableBinary) {
return false;
}
if (!gate.selectedFilePath || gate.loadedFilePath !== gate.selectedFilePath) {
return false;
}
return true;
}
export type FileEditorSaveDraftGate = {
selectedFilePath: string | null | undefined;
loadedFilePath: string | null;
fileLoading: boolean;
isDirty: boolean;
draftContent: string;
fileContent: string;
isNonEditableBinary: boolean;
};
/**
* Whether saveDraft may write. Refuses empty drafts against stale content and any binary target.
*/
export function shouldAllowFileDraftSave(gate: FileEditorSaveDraftGate): boolean {
if (!gate.selectedFilePath || !gate.isDirty) {
return false;
}
if (gate.fileLoading || gate.loadedFilePath !== gate.selectedFilePath || gate.isNonEditableBinary) {
return false;
}
if (gate.draftContent === '' && gate.fileContent !== '' && gate.loadedFilePath !== gate.selectedFilePath) {
return false;
}
return true;
}