mirror of
https://github.com/stablyai/orca.git
synced 2026-10-21 08:03:33 +00:00
fix(terminal): route a reveal metric flush through the stable fit
fitRevealedPane's new flush branch called safeFit directly, which is exactly what the function's contract forbids on reveal: resumeRendering has just re-attached WebGL, whose cell metrics transiently differ from the DOM renderer's, so a raw fit can propose a one-column-off grid and reflow — and xterm's wrap/unwrap is not a perfect inverse, leaving a diff-painting inline TUI corrupted. A landed flush leaves pixels unchanged with a diverged grid, the same shape as a snapshot resize, so it takes the same steady-grid repair. A real resize still fits synchronously, after the flush. Reachable via window wake, which calls fitAllRevealedPanes with no pre-flush loop.
This commit is contained in:
@@ -55,10 +55,12 @@ describe('fitRevealedPane routing', () => {
|
||||
mocks.flushDeferredPaneMetricOptionsIfMeasurable.mockReturnValue(false)
|
||||
})
|
||||
|
||||
it('fits when a deferred metric flush lands on a pane the size checks would skip', () => {
|
||||
// Without this the no-op branch below returns early and the parked font
|
||||
// change never reaches the grid — the stuck-metrics shape the deferral
|
||||
// is supposed to prevent.
|
||||
it('repairs on a steady grid when a deferred metric flush lands on a pane the size checks would skip', () => {
|
||||
// Without the flush branch the checks below both say "nothing to do" and the
|
||||
// parked font change never reaches the grid. It must route through the stable
|
||||
// path, not a raw fit: pixels are unchanged and the grid diverged, so a
|
||||
// synchronous fit would reflow on the WebGL/DOM metric wobble and corrupt a
|
||||
// diff-painting inline TUI.
|
||||
mocks.flushDeferredPaneMetricOptionsIfMeasurable.mockReturnValue(true)
|
||||
const pane = createPane({
|
||||
lastFitClientSize: { width: 800, height: 600 },
|
||||
@@ -69,8 +71,25 @@ describe('fitRevealedPane routing', () => {
|
||||
|
||||
fitRevealedPane(pane)
|
||||
|
||||
expect(mocks.flushDeferredPaneMetricOptionsIfMeasurable).toHaveBeenCalledWith(pane)
|
||||
expect(mocks.requestStablePaneFit).toHaveBeenCalledWith(pane)
|
||||
expect(mocks.safeFit).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('still flushes parked metric options when the pane also resized while hidden', () => {
|
||||
mocks.flushDeferredPaneMetricOptionsIfMeasurable.mockReturnValue(true)
|
||||
const pane = createPane({
|
||||
lastFitClientSize: { width: 800, height: 600 },
|
||||
currentSize: { width: 640, height: 480 },
|
||||
terminal: { cols: 80, rows: 24 },
|
||||
proposed: { cols: 64, rows: 20 }
|
||||
})
|
||||
|
||||
fitRevealedPane(pane)
|
||||
|
||||
expect(mocks.flushDeferredPaneMetricOptionsIfMeasurable).toHaveBeenCalledWith(pane)
|
||||
// A real resize still takes the synchronous path, and the flush already landed.
|
||||
expect(mocks.safeFit).toHaveBeenCalledWith(pane)
|
||||
expect(mocks.requestStablePaneFit).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('fits synchronously when the fit element resized while hidden', () => {
|
||||
|
||||
@@ -62,18 +62,17 @@ function releaseMeasurableFitContinuations(pane: ManagedPane): void {
|
||||
// mismatch refits but a transient metric wobble does not reflow;
|
||||
// - grid already correct → leave it alone.
|
||||
export function fitRevealedPane(pane: ManagedPane): void {
|
||||
// Why first: the pixel/grid checks below can both say "nothing to do" and
|
||||
// return without fitting, which would strand metric options parked while the
|
||||
// pane was unmeasurable. A landed flush changes cell size, so it must fit.
|
||||
if (flushDeferredPaneMetricOptionsIfMeasurable(pane)) {
|
||||
safeFit(pane)
|
||||
return
|
||||
}
|
||||
// Why first: the checks below can both say "nothing to do" and return without
|
||||
// fitting, stranding metric options parked while the pane was unmeasurable.
|
||||
const flushed = flushDeferredPaneMetricOptionsIfMeasurable(pane)
|
||||
if (paneFitClientSizeChanged(pane)) {
|
||||
safeFit(pane)
|
||||
return
|
||||
}
|
||||
if (!proposedGridMatchesTerminal(pane)) {
|
||||
// Why the stable path for a flush: it leaves pixels unchanged but the grid
|
||||
// diverged — the same shape as a snapshot resize, and a raw fit here would
|
||||
// reflow on the transient WebGL/DOM metric wobble this function exists to avoid.
|
||||
if (flushed || !proposedGridMatchesTerminal(pane)) {
|
||||
requestStablePaneFit(pane)
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user