mirror of
https://github.com/stablyai/orca.git
synced 2026-09-30 16:02:56 +00:00
fix(diffs): bound the selection-restore retry window
Adversarial review (Opus) showed the wall-clock deadline re-armed on Pierre's 'update' phase, which fires on every render, so a row receiving periodic renders could push it forward indefinitely -- the 2s bound was not a bound. Keep the extension (convergence genuinely needs a later Pierre render; gating to 'mount' alone regresses restore) but add a hard ceiling from mount, past which no further extension is granted. A row that renders forever without converging can no longer spin forever. Also moves the measured-height discard below the stale-payload skip: a rejected payload was resizing the row the user is typing in.
This commit is contained in:
+5
-4
@@ -151,10 +151,6 @@ export function useCombinedDiffSectionLoader({
|
||||
) {
|
||||
return
|
||||
}
|
||||
if (wasShowingContent) {
|
||||
// Why: content really changed, so the old Monaco height no longer describes this row.
|
||||
setSectionHeights((prev) => removeDiffSectionMeasuredHeight(prev, index))
|
||||
}
|
||||
// `dirty` flips back to false once a save is acknowledged, so it cannot tell a stale payload
|
||||
// from a fresh one. If the draft moved while this fetch was in flight, only commit when the
|
||||
// payload actually agrees with that draft — otherwise this reverts the user's saved text on
|
||||
@@ -163,6 +159,11 @@ export function useCombinedDiffSectionLoader({
|
||||
if (liveDraft !== draftAtFetchStart && storedContent.modifiedContent !== liveDraft) {
|
||||
return
|
||||
}
|
||||
if (wasShowingContent) {
|
||||
// Why: content really changed, so the old measured height no longer describes this row.
|
||||
// Must follow the skip above, or a rejected payload resizes the row being typed in.
|
||||
setSectionHeights((prev) => removeDiffSectionMeasuredHeight(prev, index))
|
||||
}
|
||||
setSections((prev) => {
|
||||
return prev.map((s, i) =>
|
||||
i === index && !s.dirty
|
||||
|
||||
@@ -14,7 +14,12 @@ import {
|
||||
// The editor reasserts its own selection on attach, which can land well after ours. Budget by
|
||||
// wall clock, not frames: under load a frame count expires long before the editor settles.
|
||||
// User interaction still cancels the pending restore, so this only bounds the quiet case.
|
||||
// The editor reasserts its own selection well after ours, and convergence needs a later Pierre
|
||||
// render, so the window extends on each render while a restore is still pending. RESTORE_CEILING
|
||||
// is the hard bound: past it no further extension is granted, so a row that renders forever
|
||||
// without converging cannot spin forever. User interaction cancels the restore sooner still.
|
||||
const RESTORE_DEADLINE_MS = 2_000
|
||||
const RESTORE_CEILING_MS = 15_000
|
||||
|
||||
export function usePierreDiffNativeView(
|
||||
key: string | undefined,
|
||||
@@ -34,6 +39,7 @@ export function usePierreDiffNativeView(
|
||||
const frame = useRef<number | null>(null)
|
||||
// Armed on attach; 0 until then so a stale ref can never keep a restore alive.
|
||||
const deadline = useRef(0)
|
||||
const ceiling = useRef(0)
|
||||
const lastSnapshot = useRef<PierreNativeViewState | undefined>(undefined)
|
||||
const schedule = useCallback(() => {
|
||||
if (frame.current !== null || !pending.current || Date.now() > deadline.current) {
|
||||
@@ -103,7 +109,9 @@ export function usePierreDiffNativeView(
|
||||
})
|
||||
}, [editorRef])
|
||||
useLayoutEffect(() => {
|
||||
deadline.current = Date.now() + RESTORE_DEADLINE_MS
|
||||
const now = Date.now()
|
||||
ceiling.current = now + RESTORE_CEILING_MS
|
||||
deadline.current = now + RESTORE_DEADLINE_MS
|
||||
schedule()
|
||||
}, [activeGroupId, schedule])
|
||||
useLayoutEffect(() => {
|
||||
@@ -174,8 +182,14 @@ export function usePierreDiffNativeView(
|
||||
(host: HTMLElement, phase: PostRenderPhase, instance: PierreDiffInstance) => {
|
||||
if (phase !== 'unmount') {
|
||||
view.current = { host, instance }
|
||||
// Re-arm on attach: the remount is the point a restore becomes possible.
|
||||
deadline.current = Date.now() + RESTORE_DEADLINE_MS
|
||||
const now = Date.now()
|
||||
if (phase === 'mount') {
|
||||
ceiling.current = now + RESTORE_CEILING_MS
|
||||
}
|
||||
// Extend while a restore is still pending, but never past the ceiling.
|
||||
if (pending.current && now < ceiling.current) {
|
||||
deadline.current = Math.min(now + RESTORE_DEADLINE_MS, ceiling.current)
|
||||
}
|
||||
schedule()
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user