perf: cut the boot and poll overhead (one settings fetch, one git process per tick, one fold per poll)
Boot: loadPrefs and loadExternalDisable each fetched the settings document, serially, before the first paint — and the second had no timeout, so a stalled wire kept the sidebar unmounted forever. Both decisions now ride ONE fetch (loadBootDecision) under the existing 2s race. GitLens: the 2s silent poll re-listed worktrees on every tick (a second git process per tick for a list that almost never changes, and a fresh worktrees array re-render each time). Silent ticks now fetch only the selected checkout's status; every 15th tick (~30s) re-lists and re-runs auto-selection, so an agent-created linked checkout is still picked up mid-session. ChangesTab: extractFileOps parses every accumulated tool/call (up to 4000 events); the poll ran it once for the badge and the render ran it AGAIN per tick, and the fresh array defeated the downstream memo. The fold now runs once per poll and its result rides a ref shared by badge and render (reset on session switch).
This commit is contained in:
@@ -63,6 +63,12 @@ export function ChangesTab({ ctx, store, scope, tab, visible, onOpenFile, onOpen
|
||||
// up). The cursor is the last delivered seq, so each poll ships only
|
||||
// what the accumulator lacks. ────────────────────────────────────────
|
||||
const eventsRef = useRef<readonly SidebarSessionEvent[]>([])
|
||||
// The fold of eventsRef as of the last poll. extractFileOps parses every
|
||||
// accumulated tool/call (up to EVENTS_CAP events); running it once per
|
||||
// poll and REUSING the result across renders (the render used to re-fold
|
||||
// the whole window, twice per tick, and the fresh array defeated the
|
||||
// downstream memo on every poll) keeps the 2.5s tick at one fold.
|
||||
const opsRef = useRef<readonly FileOp[]>([])
|
||||
const seqRef = useRef(0)
|
||||
const pollGen = useRef(0)
|
||||
const [opsError, setOpsError] = useState(false)
|
||||
@@ -77,7 +83,9 @@ export function ChangesTab({ ctx, store, scope, tab, visible, onOpenFile, onOpen
|
||||
eventsRef.current = merged.length > EVENTS_CAP ? merged.slice(merged.length - EVENTS_CAP) : merged
|
||||
}
|
||||
if (lastSeq > seqRef.current) seqRef.current = lastSeq
|
||||
opCounts.set(scope.sessionId, extractFileOps(eventsRef.current).length)
|
||||
const folded = extractFileOps(eventsRef.current)
|
||||
opsRef.current = folded
|
||||
opCounts.set(scope.sessionId, folded.length)
|
||||
setOpsError(false)
|
||||
setTick(value => value + 1)
|
||||
} catch {
|
||||
@@ -89,6 +97,7 @@ export function ChangesTab({ ctx, store, scope, tab, visible, onOpenFile, onOpen
|
||||
useEffect(() => {
|
||||
pollGen.current += 1
|
||||
eventsRef.current = []
|
||||
opsRef.current = []
|
||||
seqRef.current = 0
|
||||
setOpsError(false)
|
||||
}, [scope.sessionId])
|
||||
@@ -100,7 +109,7 @@ export function ChangesTab({ ctx, store, scope, tab, visible, onOpenFile, onOpen
|
||||
}, [visible, pull])
|
||||
// tick only forces the re-render; the fold reads the ref directly.
|
||||
void tick
|
||||
const ops = extractFileOps(eventsRef.current)
|
||||
const ops = opsRef.current
|
||||
|
||||
/** Persist a meta patch onto the tab (lens choice, pane height). */
|
||||
const patchMeta = (patch: ChangesMeta): void => {
|
||||
|
||||
Reference in New Issue
Block a user