fix(ui): close review findings from the switch-and-scroll integration

- Markdown DOM cache: key includes a content-length fingerprint so an
  edited or reverted part re-materializing under the same id cannot
  restore stale DOM, memoization uses scalar identities instead of the
  part object (store reducers recreate part objects on unrelated updates,
  which re-ran the async render pipeline for identical content), and a
  probe with a mismatched locale/directory no longer destroys the entry
  it failed to claim.
- Sidebar bootstrap: the layout-level sync owner only knows known
  directories, so expanded projects bootstrapped serialized at background
  priority. The visible collection now publishes a second, expansion-
  aware demand owner, restoring concurrent hydration for expanded
  projects and worktree groups.
- Settings: local changes still sitting in the debounce buffer are not
  yet tracked as mutations, so a settings GET racing the debounce window
  briefly reverted them; reconciled results now reapply the pending
  buffer.
This commit is contained in:
Bohdan Triapitsyn
2026-08-26 01:03:47 +03:00
parent ac880e8e62
commit 448ecc12b8
4 changed files with 51 additions and 7 deletions
+12 -2
View File
@@ -1884,12 +1884,22 @@ export const syncDesktopSettings = async (): Promise<void> => {
// Each step is wrapped in try/catch so a failure in one side-effect (e.g.
// a TypeError from writing to a contextBridge-protected global) doesn't
// prevent server settings from reaching the Zustand store.
// Local changes sitting in the debounce buffer are not yet tracked as
// mutations (record() only stores while a request is in flight), so a GET
// racing the debounce window would briefly revert them. Reapply the
// pending buffer over every reconciled result.
const overlayPendingChanges = (settings: DesktopSettings): DesktopSettings => {
if (!_pendingSettingsChanges || !_pendingSettingsContext) return settings;
if (!isSettingsRuntimeContextCurrent(_pendingSettingsContext)) return settings;
return { ...settings, ..._pendingSettingsChanges };
};
const applySettings = async (loadedSettings: DesktopSettings) => {
if (!isSettingsRuntimeContextCurrent(context)) return;
let settings = _settingsMutationTracker.reconcile(loadedSettings, operation);
let settings = overlayPendingChanges(_settingsMutationTracker.reconcile(loadedSettings, operation));
await waitForHydration();
if (!isSettingsRuntimeContextCurrent(context)) return;
settings = _settingsMutationTracker.reconcile(loadedSettings, operation);
settings = overlayPendingChanges(_settingsMutationTracker.reconcile(loadedSettings, operation));
const shouldPersistCraftGoalMigration = settings.draftStartersCraftGoalAdded !== true
|| settings.draftStartersScheduleTaskAdded !== true;
// `autoSaveEnabled` is new to the settings backend. Until the server has a