(
prev: Frame,
next: Frame,
altScreen = false,
decstbmSafe = true,
)
| 233 | } |
| 234 | |
| 235 | render( |
| 236 | prev: Frame, |
| 237 | next: Frame, |
| 238 | altScreen = false, |
| 239 | decstbmSafe = true, |
| 240 | ): RenderOutput { |
| 241 | const startTime = performance.now() |
| 242 | const stylePool = this.options.stylePool |
| 243 | const physicalNext = altScreen |
| 244 | ? next |
| 245 | : clipMainScreenFrameToVisibleRows(next, stylePool) |
| 246 | |
| 247 | if (!this.options.isTTY) { |
| 248 | return { |
| 249 | diff: this.renderFullFrame(next), |
| 250 | physicalFrame: physicalNext, |
| 251 | } |
| 252 | } |
| 253 | |
| 254 | const widthResizeStaysIncremental = |
| 255 | prev.viewport.width !== next.viewport.width && |
| 256 | canStayIncrementalAcrossWidthResize(prev, next) |
| 257 | |
| 258 | // Since we assume the cursor is at the bottom on the screen, we only need |
| 259 | // to clear when the viewport gets shorter (i.e. the cursor position drifts) |
| 260 | // or when it gets thinner (and text wraps). We _could_ figure out how to |
| 261 | // not reset here but that would involve predicting the current layout |
| 262 | // _after_ the viewport change which means calcuating text wrapping. |
| 263 | // Resizing is a rare enough event that it's not practically a big issue. |
| 264 | if (shouldResetForResize(prev, next)) { |
| 265 | this.state.highestIgnoredHiddenRow = undefined |
| 266 | if (!altScreen && canRepaintFromPreviousOutputTop(prev)) { |
| 267 | return { |
| 268 | diff: fullRepaintFromPreviousOutputTop(prev, next, stylePool, { |
| 269 | clearRowsBeforeWrite: true, |
| 270 | }), |
| 271 | physicalFrame: physicalNext, |
| 272 | } |
| 273 | } |
| 274 | return { |
| 275 | diff: fullViewportRepaintFromHome(next, stylePool, prev), |
| 276 | physicalFrame: physicalNext, |
| 277 | } |
| 278 | } |
| 279 | |
| 280 | // DECSTBM scroll optimization: when a ScrollBox's scrollTop changed, |
| 281 | // shift content with a hardware scroll (CSI top;bot r + CSI n S/T) |
| 282 | // instead of rewriting the whole scroll region. The shiftRows on |
| 283 | // prev.screen simulates the shift so the diff loop below naturally |
| 284 | // finds only the rows that scrolled IN as diffs. prev.screen is |
| 285 | // about to become backFrame (reused next render) so mutation is safe. |
| 286 | // CURSOR_HOME after RESET_SCROLL_REGION is defensive — DECSTBM reset |
| 287 | // homes cursor per spec but terminal implementations vary. |
| 288 | // |
| 289 | // decstbmSafe: caller passes false when the DECSTBM→diff sequence |
| 290 | // can't be made atomic (no DEC 2026 / BSU/ESU). Without atomicity the |
| 291 | // outer terminal renders the intermediate state — region scrolled, |
| 292 | // edge rows not yet painted — a visible vertical jump on every frame |
nothing calls this directly
no test coverage detected