Skip to content

Commit 08c5062

Browse files
committed
Alternate screen buffer option.
1 parent aad59b8 commit 08c5062

File tree

4 files changed

+37
-2
lines changed

4 files changed

+37
-2
lines changed

examples/scroll/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ import React from 'react';
22
import {render} from '../../src/index.js';
33
import ScrollableContent from './scroll.js';
44

5-
render(React.createElement(ScrollableContent));
5+
render(React.createElement(ScrollableContent), {alternateBuffer: true});

examples/scroll/scroll.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ function ScrollableContent() {
112112
getNewValue: (current: number) => number,
113113
) => {
114114
let frame = 0;
115-
const frames = 10;
115+
const frames = 3;
116+
setter(s => getNewValue(s));
116117
scrollIntervalReference.current = setInterval(() => {
117118
if (frame < frames) {
118119
setter(s => getNewValue(s));

src/ink.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export type Options = {
2828
exitOnCtrlC: boolean;
2929
patchConsole: boolean;
3030
isScreenReaderEnabled?: boolean;
31+
alternateBuffer?: boolean;
3132
waitUntilExit?: () => Promise<void>;
3233
maxFps?: number;
3334
};
@@ -37,6 +38,7 @@ export default class Ink {
3738
private readonly log: LogUpdate;
3839
private readonly throttledLog: LogUpdate;
3940
private readonly isScreenReaderEnabled: boolean;
41+
private readonly alternateBuffer: boolean;
4042

4143
// Ignore last render after unmounting a tree to prevent empty output before exit
4244
private isUnmounted: boolean;
@@ -62,6 +64,12 @@ export default class Ink {
6264
options.isScreenReaderEnabled ??
6365
process.env['INK_SCREEN_READER'] === 'true';
6466

67+
this.alternateBuffer = options.alternateBuffer ?? false;
68+
69+
if (this.alternateBuffer && !isInCi) {
70+
this.options.stdout.write(ansiEscapes.enterAlternativeScreen);
71+
}
72+
6573
const unthrottled = options.debug || this.isScreenReaderEnabled;
6674
const maxFps = options.maxFps ?? 30;
6775
const renderThrottleMs =
@@ -232,6 +240,20 @@ export default class Ink {
232240
return;
233241
}
234242

243+
if (this.alternateBuffer) {
244+
if (hasStaticOutput) {
245+
this.fullStaticOutput += staticOutput;
246+
}
247+
248+
this.options.stdout.write(
249+
ansiEscapes.clearTerminal + this.fullStaticOutput + output,
250+
);
251+
252+
this.lastOutput = output;
253+
this.lastOutputHeight = outputHeight;
254+
return;
255+
}
256+
235257
if (hasStaticOutput) {
236258
this.fullStaticOutput += staticOutput;
237259
}
@@ -355,6 +377,10 @@ export default class Ink {
355377
this.log.done();
356378
}
357379

380+
if (this.alternateBuffer && !isInCi) {
381+
this.options.stdout.write(ansiEscapes.exitAlternativeScreen);
382+
}
383+
358384
this.isUnmounted = true;
359385

360386
// @ts-expect-error the types for `react-reconciler` are not up to date with the library.

src/render.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ export type RenderOptions = {
4949
*/
5050
isScreenReaderEnabled?: boolean;
5151

52+
/**
53+
Enable alternate screen buffer.
54+
This will cause Ink to render to a new screen buffer, and restore the old one on exit.
55+
56+
@default false
57+
*/
58+
alternateBuffer?: boolean;
59+
5260
/**
5361
Maximum frames per second for render updates.
5462
This controls how frequently the UI can update to prevent excessive re-rendering.

0 commit comments

Comments
 (0)