Never let a stale settings copy erase a saved choice
The preferences store was loaded once at shell startup, and every save wrote the whole in-memory copy back. Anything that reached the file after startup -- a hand edit, a script, another shell instance flushing during a session handoff -- was erased by the next unrelated save. That is how a display scale set to 1.5 kept coming back as 2: the entry was written, then silently clobbered, and the catch-all auto rule filled the gap. Two halves, either alone insufficient. The store now watches its file and adopts outside writes instead of holding a stale copy over them; and a save merges with what is on disk rather than overwriting it -- keys this shell has set win, keys it has never seen survive. A setting changed anywhere now persists until it is changed again, which is the only behavior a settings file is entitled to have. Claude-Session: https://claude.ai/code/session_01Epx9ZC1gwm81K3jm9x9CKh
This commit is contained in:
@@ -130,6 +130,14 @@ Singleton {
|
|||||||
printErrors: false
|
printErrors: false
|
||||||
atomicWrites: true
|
atomicWrites: true
|
||||||
|
|
||||||
|
// Adopt writes made from outside the shell -- a hand edit, a script,
|
||||||
|
// a restored snapshot -- instead of holding a stale copy in memory
|
||||||
|
// and silently erasing them at the next save. A change made anywhere
|
||||||
|
// must survive everywhere; the shell is the editor, not the owner.
|
||||||
|
// The shell's own atomic writes land here too and reload as a no-op.
|
||||||
|
watchChanges: true
|
||||||
|
onFileChanged: this.reload()
|
||||||
|
|
||||||
onLoaded: root.load()
|
onLoaded: root.load()
|
||||||
// No file yet is the normal first-run case, not an error.
|
// No file yet is the normal first-run case, not an error.
|
||||||
onLoadFailed: root.load()
|
onLoadFailed: root.load()
|
||||||
@@ -145,7 +153,27 @@ Singleton {
|
|||||||
Timer {
|
Timer {
|
||||||
id: persistTimer
|
id: persistTimer
|
||||||
interval: 0
|
interval: 0
|
||||||
onTriggered: preferencesFile.setText(JSON.stringify(root.values, null, 2) + "\n")
|
// Merge with what is on disk rather than overwriting it. This model
|
||||||
|
// was loaded at startup; a key written to the file since then -- a
|
||||||
|
// hand edit, a script, another shell instance during a session
|
||||||
|
// handoff -- would otherwise be erased by the next unrelated save,
|
||||||
|
// which is how a setting "changed itself back". Keys this shell has
|
||||||
|
// set win; keys it has never seen survive.
|
||||||
|
onTriggered: {
|
||||||
|
let disk = {};
|
||||||
|
try {
|
||||||
|
const text = preferencesFile.text();
|
||||||
|
if (text && text.trim().length > 0)
|
||||||
|
disk = JSON.parse(text);
|
||||||
|
} catch (error) {
|
||||||
|
// An unreadable file loses the merge, never the write.
|
||||||
|
}
|
||||||
|
if (!disk || typeof disk !== "object")
|
||||||
|
disk = {};
|
||||||
|
const merged = Object.assign({}, disk, root.values);
|
||||||
|
root.values = merged;
|
||||||
|
preferencesFile.setText(JSON.stringify(merged, null, 2) + "\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// One-time move from the pre-Stage-1 location inside Quickshell's state
|
// One-time move from the pre-Stage-1 location inside Quickshell's state
|
||||||
|
|||||||
Reference in New Issue
Block a user