|
import stripAnsi from 'strip-ansi'; |
|
import ansiEscapes from 'ansi-escapes'; |
|
import { breakLines, readlineWidth } from './utils.mjs'; |
|
const height = (content) => content.split('\n').length; |
|
const lastLine = (content) => content.split('\n').pop() ?? ''; |
|
export default class ScreenManager { |
|
rl; |
|
|
|
height = 0; |
|
extraLinesUnderPrompt = 0; |
|
cursorPos; |
|
constructor(rl) { |
|
this.rl = rl; |
|
this.rl = rl; |
|
this.cursorPos = rl.getCursorPos(); |
|
} |
|
render(content, bottomContent = '') { |
|
|
|
|
|
|
|
const promptLine = lastLine(content); |
|
const rawPromptLine = stripAnsi(promptLine); |
|
|
|
|
|
|
|
let prompt = rawPromptLine; |
|
if (this.rl.line.length > 0) { |
|
prompt = prompt.slice(0, -this.rl.line.length); |
|
} |
|
this.rl.setPrompt(prompt); |
|
|
|
this.cursorPos = this.rl.getCursorPos(); |
|
const width = readlineWidth(); |
|
content = breakLines(content, width); |
|
bottomContent = breakLines(bottomContent, width); |
|
|
|
|
|
|
|
if (rawPromptLine.length % width === 0) { |
|
content += '\n'; |
|
} |
|
let output = content + (bottomContent ? '\n' + bottomContent : ''); |
|
|
|
|
|
|
|
|
|
|
|
const promptLineUpDiff = Math.floor(rawPromptLine.length / width) - this.cursorPos.rows; |
|
const bottomContentHeight = promptLineUpDiff + (bottomContent ? height(bottomContent) : 0); |
|
|
|
if (bottomContentHeight > 0) |
|
output += ansiEscapes.cursorUp(bottomContentHeight); |
|
|
|
output += ansiEscapes.cursorTo(this.cursorPos.cols); |
|
this.clean(); |
|
this.rl.output.unmute(); |
|
|
|
|
|
|
|
this.extraLinesUnderPrompt = bottomContentHeight; |
|
this.height = height(output); |
|
this.rl.output.write(output); |
|
this.rl.output.mute(); |
|
} |
|
checkCursorPos() { |
|
const cursorPos = this.rl.getCursorPos(); |
|
if (cursorPos.cols !== this.cursorPos.cols) { |
|
this.rl.output.unmute(); |
|
this.rl.output.write(ansiEscapes.cursorTo(cursorPos.cols)); |
|
this.rl.output.mute(); |
|
this.cursorPos = cursorPos; |
|
} |
|
} |
|
clean() { |
|
this.rl.output.unmute(); |
|
this.rl.output.write([ |
|
this.extraLinesUnderPrompt > 0 |
|
? ansiEscapes.cursorDown(this.extraLinesUnderPrompt) |
|
: '', |
|
ansiEscapes.eraseLines(this.height), |
|
].join('')); |
|
this.extraLinesUnderPrompt = 0; |
|
this.rl.output.mute(); |
|
} |
|
clearContent() { |
|
this.rl.output.unmute(); |
|
|
|
this.rl.output.write([ |
|
this.extraLinesUnderPrompt > 0 |
|
? ansiEscapes.cursorDown(this.extraLinesUnderPrompt) |
|
: '', |
|
'\n', |
|
].join('')); |
|
this.rl.output.mute(); |
|
} |
|
done() { |
|
this.rl.setPrompt(''); |
|
this.rl.output.unmute(); |
|
this.rl.output.write(ansiEscapes.cursorShow); |
|
this.rl.output.end(); |
|
this.rl.close(); |
|
} |
|
} |
|
|