fix(git): bound status payload and repo discovery; add reset escape hatch (#369)
Opening the source-control panel could freeze the whole sidebar, and the frozen layout restored itself after every reload, leaving no way out (issue #369). Three unbounded layers compounded: - `git status --porcelain -z --untracked-files=all` shipped every row of a pathological untracked set into one response; the browser main thread froze on response.json() plus the unvirtualized change list, and the 2s poll replayed the freeze. status() now caps entries at 2000 and reports `truncated` (the panel shows a notice, mirroring fs.read's truncation semantics); worktree change counts inherit the bound for free. - repoRoots() probed every visible child directory of a non-repo cwd (e.g. ~) serially, once per git.* call, each with the full 30s command budget. Discovery now uses a 3s probe timeout, caps probes at 200 directories, and shares one in-flight scan plus a 60s TTL cache per cwd. - A hung layout persisted `panelOpen: true` with the git tab active, so a reload remounted straight back into the freeze with no escape hatch. Loading with `?dsh-sidebar-reset` now drops the persisted layout (and the shared width) and starts from the default. statusTruncated copy synced across all 19 shipped dictionaries.
This commit is contained in:
+43
-13
@@ -1036,25 +1036,55 @@ export function defaultWidthFor(viewport: number, percent: number): number {
|
||||
return Math.min(viewport, Math.max(PANEL_MIN, Math.round(viewport * percent / 100)))
|
||||
}
|
||||
|
||||
/**
|
||||
* URL escape hatch (#369): loading the app with `?dsh-sidebar-reset` drops
|
||||
* the persisted layout for the session instead of restoring it. When a
|
||||
* restored tab hangs the page on mount (the #369 freeze loop), reloading
|
||||
* into the same state replays the hang forever; this param starts from the
|
||||
* default layout and clears the stored copy, breaking the loop. Persisting
|
||||
* resumes as soon as the param is gone from the URL.
|
||||
*/
|
||||
const RESET_PARAM = 'dsh-sidebar-reset'
|
||||
|
||||
/** Whether the current page load asked for a persisted-state reset. */
|
||||
function resetRequested(): boolean {
|
||||
try {
|
||||
return new URLSearchParams(window.location.search).has(RESET_PARAM)
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function loadState(sessionId: string, prefs: SidebarPrefs): SidebarState {
|
||||
const reset = resetRequested()
|
||||
if (reset) {
|
||||
try {
|
||||
localStorage.removeItem(`${STORAGE_PREFIX}:${sessionId}`)
|
||||
localStorage.removeItem(GLOBAL_WIDTH_KEY)
|
||||
} catch {
|
||||
// Storage unavailable: the default layout below is still the escape.
|
||||
}
|
||||
}
|
||||
// The panel width is a cross-session preference: the last dragged width
|
||||
// wins over a session's own persisted value, so switching conversations
|
||||
// keeps the width the user chose anywhere.
|
||||
const globalWidth = readGlobalWidth()
|
||||
try {
|
||||
const raw = localStorage.getItem(`${STORAGE_PREFIX}:${sessionId}`)
|
||||
if (raw !== null) {
|
||||
const parsed = JSON.parse(raw) as unknown
|
||||
// Seed the uid counter past the persisted ids (it resets on reload);
|
||||
// sanitize re-ids any duplicates the pre-seeding counter left behind.
|
||||
nextIdCounter = maxCounterId(parsed)
|
||||
const sanitized = sanitizeState(parsed)
|
||||
if (sanitized !== undefined) {
|
||||
return globalWidth === undefined ? sanitized : { ...sanitized, width: globalWidth }
|
||||
const globalWidth = reset ? undefined : readGlobalWidth()
|
||||
if (!reset) {
|
||||
try {
|
||||
const raw = localStorage.getItem(`${STORAGE_PREFIX}:${sessionId}`)
|
||||
if (raw !== null) {
|
||||
const parsed = JSON.parse(raw) as unknown
|
||||
// Seed the uid counter past the persisted ids (it resets on reload);
|
||||
// sanitize re-ids any duplicates the pre-seeding counter left behind.
|
||||
nextIdCounter = maxCounterId(parsed)
|
||||
const sanitized = sanitizeState(parsed)
|
||||
if (sanitized !== undefined) {
|
||||
return globalWidth === undefined ? sanitized : { ...sanitized, width: globalWidth }
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Corrupt or unavailable storage: fall through to the default.
|
||||
}
|
||||
} catch {
|
||||
// Corrupt or unavailable storage: fall through to the default.
|
||||
}
|
||||
// New sessions seed from the user's side card prefs: the width is the
|
||||
// chosen percent of the window (clamped to the panel floor and the
|
||||
|
||||
Reference in New Issue
Block a user