3 months ago
dcace81Reopening the app never restored the last window size/position, switching threads lost scroll position, and switching chat -> settings -> chat dropped the sidebar + top bar entirely. selectedThreadId / view / settingsPage were already persisted/restored via the localStorage UI cache; this fills the three real gaps. Window bounds (main process): - electron/services/windowState.ts: dedicated window-state.json (kept separate from the IPC-backed state.json so resize/move never churn workspace/session state). loadMainWindowBounds clamps saved bounds to a visible display so a window saved on a now-disconnected monitor still appears on-screen. trackMainWindowBounds debounced-captures on resize/move and synchronous-flushes on close. - electron/main.ts: restore bounds + isMaximized on createMainWindow, capture on resize/move/close. Per-thread scroll: - store: add scrollPositionsByThreadId (Record<threadId, number>), persisted in the UI cache, capped to 24 recent persisted threads. - ChatView: capture the outgoing thread's scrollTop on switch and restore the incoming thread's saved offset instead of always bottom-pinning (keeps the original bottom-pin behavior on first mount and when no offset is saved, so post-mount scroll callers aren't clobbered). Settings layout: - PrimaryContent: route a 'settings' view through ChatShell's content slot so the sidebar + top bar stay mounted instead of being replaced by a bare settings shell. Removes the separate view==='settings' branch in App.tsx. Tests: - test/window-state.test.ts: clamp math, corrupt-state fallback, flush. - test/bootstrap-cache.test.ts: scrollPositionsByThreadId round-trip.
Parent6b4f950