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
@@ -77,10 +77,14 @@ export class DetachedMarkdownDomCache {
const entry = session.get(entryKey);
if (entry === undefined) return null;
// A mismatched probe (different locale or directory for the same part)
// must not destroy the entry — the matching renderer may still come for
// it. Only a real hit transfers ownership out of the cache.
if (entry.locale !== key.locale || entry.directory !== key.directory) return null;
// A fragment is a move-only resource; taking it removes cache ownership.
session.delete(entryKey);
if (session.size === 0) this.sessions.delete(sessionKey);
if (entry.locale !== key.locale || entry.directory !== key.directory) return null;
return entry.fragment;
}