fix: stop settings.json from being wiped on launch
Electron main, ssh-manager, and the embedded web server all write the
same settings.json. readJsonFile/readJsonRoot silently coerced any read
failure (including mid-write parse errors) to {}, and writes were plain
fs.writeFile. A partial read during a concurrent write let the reader's
next read-modify-write overwrite the whole file with only the field it
just set — wiping projects, desktopDefaultHostId, and more. Next launch
showed the welcome chooser because defaultHostId was gone, and the
sidebar was empty because projects were gone.
- Switch all writers to atomic tmp+rename so readers never see partial
JSON.
- Add mutateSettingsRoot() in Electron main to serialize read-modify-
write pairs across its own call sites (hosts config, window state,
desktop port, ssh instances, vibrancy).
- Keep read-on-error returning {} to avoid crashing startup callers,
but log loudly now so we can catch it if it ever happens again.
- useProjectsStore: don't clobber a populated cache with empty incoming
settings. If settings ever do come back empty, the sidebar stays
intact until a real, non-empty sync lands.
This commit is contained in:
@@ -655,6 +655,25 @@ export const useProjectsStore = create<ProjectsStore>()(
|
||||
: null;
|
||||
|
||||
const current = get();
|
||||
|
||||
// Race guard: settings load can return empty projects during app
|
||||
// rebuild/reinstall or an incomplete settings read. Don't clobber
|
||||
// a populated cache with empty — the sidebar would go blank and
|
||||
// localStorage would be overwritten, losing the list entirely.
|
||||
if (incomingProjects.length === 0 && current.projects.length > 0) {
|
||||
if (incomingActive !== current.activeProjectId) {
|
||||
// Active project may still be valid within the cached list.
|
||||
const activeExists = incomingActive
|
||||
? current.projects.some((project) => project.id === incomingActive)
|
||||
: true;
|
||||
if (activeExists) {
|
||||
set({ activeProjectId: incomingActive });
|
||||
cacheProjects(current.projects, incomingActive);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const projectsChanged = JSON.stringify(current.projects) !== JSON.stringify(incomingProjects);
|
||||
const activeChanged = current.activeProjectId !== incomingActive;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user