Merge pull request #2534 from openchamber/feat/bc-38abb61a-bce6-4a01-a189-c569346927fa-33b6

fix(files): stop autosave data loss on load lag and binary files
This commit is contained in:
Serhii Dziupin
2026-07-30 16:21:59 +03:00
committed by GitHub
43 changed files with 778 additions and 80 deletions
+33 -1
View File
@@ -621,6 +621,8 @@ interface UIStore {
activityRenderMode: ActivityRenderMode;
showDeletionDialog: boolean;
autoDeleteEnabled: boolean;
/** Global file-editor autosave. Default true for backward compatibility. */
autoSaveEnabled: boolean;
autoDeleteAfterDays: number;
sessionRetentionAction: SessionRetentionAction;
autoDeleteLastRunAt: number | null;
@@ -784,6 +786,7 @@ interface UIStore {
setActivityRenderMode: (value: ActivityRenderMode) => void;
setShowDeletionDialog: (value: boolean) => void;
setAutoDeleteEnabled: (value: boolean) => void;
setAutoSaveEnabled: (value: boolean) => void;
setAutoDeleteAfterDays: (days: number) => void;
setSessionRetentionAction: (value: SessionRetentionAction) => void;
setAutoDeleteLastRunAt: (timestamp: number | null) => void;
@@ -941,6 +944,7 @@ export const useUIStore = create<UIStore>()(
activityRenderMode: 'summary',
showDeletionDialog: true,
autoDeleteEnabled: false,
autoSaveEnabled: true,
autoDeleteAfterDays: 30,
sessionRetentionAction: 'archive',
autoDeleteLastRunAt: null,
@@ -1682,6 +1686,10 @@ export const useUIStore = create<UIStore>()(
set({ autoDeleteEnabled: value });
},
setAutoSaveEnabled: (value) => {
set({ autoSaveEnabled: value });
},
setAutoDeleteAfterDays: (days) => {
const clampedDays = Math.max(1, Math.min(365, days));
set({ autoDeleteAfterDays: clampedDays });
@@ -2254,13 +2262,32 @@ export const useUIStore = create<UIStore>()(
{
name: 'ui-store',
storage: createDeferredSafeJSONStorage(),
version: 12,
version: 13,
migrate: (persistedState, version) => {
if (!persistedState || typeof persistedState !== 'object') {
return persistedState;
}
const state = persistedState as Record<string, unknown>;
// v12 -> v13: promote FilesView localStorage autosave toggle into the store.
if (version < 13) {
if (typeof state.autoSaveEnabled !== 'boolean') {
let legacyEnabled = true;
try {
if (typeof localStorage !== 'undefined') {
const legacy = localStorage.getItem('openchamber:files:auto-save-enabled');
if (legacy !== null) {
legacyEnabled = legacy !== 'false';
localStorage.removeItem('openchamber:files:auto-save-enabled');
}
}
} catch {
legacyEnabled = true;
}
state.autoSaveEnabled = legacyEnabled;
}
}
// v11 -> v12: drop legacy window-controls "auto" (always meant right).
if (version < 12) {
if (state.desktopWindowControlsPosition === 'auto' || state.desktopWindowControlsPosition == null) {
@@ -2360,6 +2387,10 @@ export const useUIStore = create<UIStore>()(
state.fileEditorKeymap = normalizeFileEditorKeymap(state.fileEditorKeymap);
if (typeof state.autoSaveEnabled !== 'boolean') {
state.autoSaveEnabled = true;
}
state.contextRailOrder = Array.isArray(state.contextRailOrder)
? (state.contextRailOrder as unknown[]).filter((id): id is string => typeof id === 'string' && id.trim() !== '')
: [];
@@ -2396,6 +2427,7 @@ export const useUIStore = create<UIStore>()(
activityRenderMode: state.activityRenderMode,
showDeletionDialog: state.showDeletionDialog,
autoDeleteEnabled: state.autoDeleteEnabled,
autoSaveEnabled: state.autoSaveEnabled,
autoDeleteAfterDays: state.autoDeleteAfterDays,
sessionRetentionAction: state.sessionRetentionAction,
autoDeleteLastRunAt: state.autoDeleteLastRunAt,