@@ -302,38 +302,30 @@ namespace winrt::TerminalApp::implementation
302302 // - <none>
303303 void AppLogic::_RegisterSettingsChange ()
304304 {
305- const std::filesystem::path settingsPath{ std::wstring_view{ CascadiaSettings::SettingsPath () } };
306- _reader.create (
307- settingsPath.parent_path ().c_str (),
308- false ,
309- // We want file modifications, AND when files are renamed to be
310- // settings.json. This second case will often happen with text
311- // editors, who will write a temp file, then rename it to be the
312- // actual file you wrote. So listen for that too.
313- wil::FolderChangeEvents::FileName | wil::FolderChangeEvents::LastWriteTime,
314- [this , settingsBasename = settingsPath.filename ()](wil::FolderChangeEvent, PCWSTR fileModified) {
315- // DO NOT create a static reference to ApplicationState::SharedInstance here.
316- //
317- // ApplicationState::SharedInstance already caches its own
318- // static ref. If _we_ keep a static ref to the member in
319- // AppState, then our reference will keep ApplicationState alive
320- // after the `ActionToStringMap` gets cleaned up. Then, when we
321- // try to persist the actions in the window state, we won't be
322- // able to. We'll try to look up the action and the map just
323- // won't exist. We'll explode, even though the Terminal is
324- // tearing down anyways. So we'll just die, but still invoke
325- // WinDBG's post-mortem debugger, who won't be able to attach to
326- // the process that's already exiting.
327- //
328- // So DON'T ~give a mouse a cookie~ take a static ref here.
329-
330- const auto modifiedBasename = std::filesystem::path{ fileModified }.filename ();
331-
332- if (modifiedBasename == settingsBasename)
333- {
334- ReloadSettingsThrottled ();
335- }
336- });
305+ // The JsonManager owns the settings.json watcher and the auto-save write
306+ // path. It raises SettingsChangedExternally (on a background
307+ // thread) when it detects an edit it didn't make; we marshal a throttled
308+ // reload onto the UI thread in response.
309+ _jsonManager = Settings::Model::JsonManager{};
310+ _jsonManager.SettingsChangedExternally ([weakSelf = get_weak ()](auto &&, auto &&) {
311+ if (auto self{ weakSelf.get () })
312+ {
313+ self->ReloadSettingsThrottled ();
314+ }
315+ });
316+ _jsonManager.Start ();
317+
318+ // Bind the live tree for auto-save -- but only if the initial load
319+ // actually succeeded. We must never auto-save the defaults fallback over
320+ // a user's broken settings.json.
321+ if (SUCCEEDED (_settingsLoadedResult))
322+ {
323+ _jsonManager.SetLiveSettings (_settings);
324+ }
325+ else
326+ {
327+ _jsonManager.ClearLiveSettings ();
328+ }
337329 }
338330
339331 void AppLogic::_ApplyLanguageSettingChange () noexcept
@@ -389,6 +381,11 @@ namespace winrt::TerminalApp::implementation
389381 if (initialLoad)
390382 {
391383 _settings = CascadiaSettings::LoadDefaults ();
384+ // Don't auto-save the defaults over the user's broken file.
385+ if (_jsonManager)
386+ {
387+ _jsonManager.ClearLiveSettings ();
388+ }
392389 }
393390 else
394391 {
@@ -409,6 +406,11 @@ namespace winrt::TerminalApp::implementation
409406 else
410407 {
411408 _settings.LogSettingChanges (true );
409+ // Rebind the freshly-loaded tree for auto-save.
410+ if (_jsonManager)
411+ {
412+ _jsonManager.SetLiveSettings (_settings);
413+ }
412414 }
413415
414416 _ApplyLanguageSettingChange ();
0 commit comments