-
-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
175 additions
and
205 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
import process from 'node:process'; | ||
|
||
const ESC = '\u001B['; | ||
const OSC = '\u001B]'; | ||
const BEL = '\u0007'; | ||
const SEP = ';'; | ||
|
||
/* global window */ | ||
const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined'; | ||
|
||
const isTerminalApp = !isBrowser && process.env.TERM_PROGRAM === 'Apple_Terminal'; | ||
const isWindows = !isBrowser && process.platform === 'win32'; | ||
const cwdFunction = isBrowser ? () => { | ||
throw new Error('`process.cwd()` only works in Node.js, not the browser.'); | ||
} : process.cwd; | ||
|
||
export const cursorTo = (x, y) => { | ||
if (typeof x !== 'number') { | ||
throw new TypeError('The `x` argument is required'); | ||
} | ||
|
||
if (typeof y !== 'number') { | ||
return ESC + (x + 1) + 'G'; | ||
} | ||
|
||
return ESC + (y + 1) + SEP + (x + 1) + 'H'; | ||
}; | ||
|
||
export const cursorMove = (x, y) => { | ||
if (typeof x !== 'number') { | ||
throw new TypeError('The `x` argument is required'); | ||
} | ||
|
||
let returnValue = ''; | ||
|
||
if (x < 0) { | ||
returnValue += ESC + (-x) + 'D'; | ||
} else if (x > 0) { | ||
returnValue += ESC + x + 'C'; | ||
} | ||
|
||
if (y < 0) { | ||
returnValue += ESC + (-y) + 'A'; | ||
} else if (y > 0) { | ||
returnValue += ESC + y + 'B'; | ||
} | ||
|
||
return returnValue; | ||
}; | ||
|
||
export const cursorUp = (count = 1) => ESC + count + 'A'; | ||
export const cursorDown = (count = 1) => ESC + count + 'B'; | ||
export const cursorForward = (count = 1) => ESC + count + 'C'; | ||
export const cursorBackward = (count = 1) => ESC + count + 'D'; | ||
|
||
export const cursorLeft = ESC + 'G'; | ||
export const cursorSavePosition = isTerminalApp ? '\u001B7' : ESC + 's'; | ||
export const cursorRestorePosition = isTerminalApp ? '\u001B8' : ESC + 'u'; | ||
export const cursorGetPosition = ESC + '6n'; | ||
export const cursorNextLine = ESC + 'E'; | ||
export const cursorPrevLine = ESC + 'F'; | ||
export const cursorHide = ESC + '?25l'; | ||
export const cursorShow = ESC + '?25h'; | ||
|
||
export const eraseLines = count => { | ||
let clear = ''; | ||
|
||
for (let i = 0; i < count; i++) { | ||
clear += eraseLine + (i < count - 1 ? cursorUp() : ''); | ||
} | ||
|
||
if (count) { | ||
clear += cursorLeft; | ||
} | ||
|
||
return clear; | ||
}; | ||
|
||
export const eraseEndLine = ESC + 'K'; | ||
export const eraseStartLine = ESC + '1K'; | ||
export const eraseLine = ESC + '2K'; | ||
export const eraseDown = ESC + 'J'; | ||
export const eraseUp = ESC + '1J'; | ||
export const eraseScreen = ESC + '2J'; | ||
export const scrollUp = ESC + 'S'; | ||
export const scrollDown = ESC + 'T'; | ||
|
||
export const clearScreen = '\u001Bc'; | ||
|
||
export const clearTerminal = isWindows | ||
? `${eraseScreen}${ESC}0f` | ||
// 1. Erases the screen (Only done in case `2` is not supported) | ||
// 2. Erases the whole screen including scrollback buffer | ||
// 3. Moves cursor to the top-left position | ||
// More info: https://www.real-world-systems.com/docs/ANSIcode.html | ||
: `${eraseScreen}${ESC}3J${ESC}H`; | ||
|
||
export const enterAlternativeScreen = ESC + '?1049h'; | ||
export const exitAlternativeScreen = ESC + '?1049l'; | ||
|
||
export const beep = BEL; | ||
|
||
export const link = (text, url) => [ | ||
OSC, | ||
'8', | ||
SEP, | ||
SEP, | ||
url, | ||
BEL, | ||
text, | ||
OSC, | ||
'8', | ||
SEP, | ||
SEP, | ||
BEL, | ||
].join(''); | ||
|
||
export const image = (buffer, options = {}) => { | ||
let returnValue = `${OSC}1337;File=inline=1`; | ||
|
||
if (options.width) { | ||
returnValue += `;width=${options.width}`; | ||
} | ||
|
||
if (options.height) { | ||
returnValue += `;height=${options.height}`; | ||
} | ||
|
||
if (options.preserveAspectRatio === false) { | ||
returnValue += ';preserveAspectRatio=0'; | ||
} | ||
|
||
return returnValue + ':' + buffer.toString('base64') + BEL; | ||
}; | ||
|
||
export const iTerm = { | ||
setCwd: (cwd = cwdFunction()) => `${OSC}50;CurrentDir=${cwd}${BEL}`, | ||
|
||
annotation(message, options = {}) { | ||
let returnValue = `${OSC}1337;`; | ||
|
||
const hasX = typeof options.x !== 'undefined'; | ||
const hasY = typeof options.y !== 'undefined'; | ||
if ((hasX || hasY) && !(hasX && hasY && typeof options.length !== 'undefined')) { | ||
throw new Error('`x`, `y` and `length` must be defined when `x` or `y` is defined'); | ||
} | ||
|
||
message = message.replace(/\|/g, ''); | ||
|
||
returnValue += options.isHidden ? 'AddHiddenAnnotation=' : 'AddAnnotation='; | ||
|
||
if (options.length > 0) { | ||
returnValue += ( | ||
hasX | ||
? [message, options.length, options.x, options.y] | ||
: [options.length, message] | ||
).join('|'); | ||
} else { | ||
returnValue += message; | ||
} | ||
|
||
return returnValue + BEL; | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,198 +1,4 @@ | ||
import process from 'node:process'; | ||
import * as ansiEscapes from './base.js'; | ||
|
||
const ESC = '\u001B['; | ||
const OSC = '\u001B]'; | ||
const BEL = '\u0007'; | ||
const SEP = ';'; | ||
|
||
/* global window */ | ||
const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined'; | ||
|
||
const isTerminalApp = !isBrowser && process.env.TERM_PROGRAM === 'Apple_Terminal'; | ||
const isWindows = !isBrowser && process.platform === 'win32'; | ||
const cwdFunction = isBrowser ? () => { | ||
throw new Error('`process.cwd()` only works in Node.js, not the browser.'); | ||
} : process.cwd; | ||
|
||
export const cursorTo = (x, y) => { | ||
if (typeof x !== 'number') { | ||
throw new TypeError('The `x` argument is required'); | ||
} | ||
|
||
if (typeof y !== 'number') { | ||
return ESC + (x + 1) + 'G'; | ||
} | ||
|
||
return ESC + (y + 1) + SEP + (x + 1) + 'H'; | ||
}; | ||
|
||
export const cursorMove = (x, y) => { | ||
if (typeof x !== 'number') { | ||
throw new TypeError('The `x` argument is required'); | ||
} | ||
|
||
let returnValue = ''; | ||
|
||
if (x < 0) { | ||
returnValue += ESC + (-x) + 'D'; | ||
} else if (x > 0) { | ||
returnValue += ESC + x + 'C'; | ||
} | ||
|
||
if (y < 0) { | ||
returnValue += ESC + (-y) + 'A'; | ||
} else if (y > 0) { | ||
returnValue += ESC + y + 'B'; | ||
} | ||
|
||
return returnValue; | ||
}; | ||
|
||
export const cursorUp = (count = 1) => ESC + count + 'A'; | ||
export const cursorDown = (count = 1) => ESC + count + 'B'; | ||
export const cursorForward = (count = 1) => ESC + count + 'C'; | ||
export const cursorBackward = (count = 1) => ESC + count + 'D'; | ||
|
||
export const cursorLeft = ESC + 'G'; | ||
export const cursorSavePosition = isTerminalApp ? '\u001B7' : ESC + 's'; | ||
export const cursorRestorePosition = isTerminalApp ? '\u001B8' : ESC + 'u'; | ||
export const cursorGetPosition = ESC + '6n'; | ||
export const cursorNextLine = ESC + 'E'; | ||
export const cursorPrevLine = ESC + 'F'; | ||
export const cursorHide = ESC + '?25l'; | ||
export const cursorShow = ESC + '?25h'; | ||
|
||
export const eraseLines = count => { | ||
let clear = ''; | ||
|
||
for (let i = 0; i < count; i++) { | ||
clear += eraseLine + (i < count - 1 ? cursorUp() : ''); | ||
} | ||
|
||
if (count) { | ||
clear += cursorLeft; | ||
} | ||
|
||
return clear; | ||
}; | ||
|
||
export const eraseEndLine = ESC + 'K'; | ||
export const eraseStartLine = ESC + '1K'; | ||
export const eraseLine = ESC + '2K'; | ||
export const eraseDown = ESC + 'J'; | ||
export const eraseUp = ESC + '1J'; | ||
export const eraseScreen = ESC + '2J'; | ||
export const scrollUp = ESC + 'S'; | ||
export const scrollDown = ESC + 'T'; | ||
|
||
export const clearScreen = '\u001Bc'; | ||
|
||
export const clearTerminal = isWindows | ||
? `${eraseScreen}${ESC}0f` | ||
// 1. Erases the screen (Only done in case `2` is not supported) | ||
// 2. Erases the whole screen including scrollback buffer | ||
// 3. Moves cursor to the top-left position | ||
// More info: https://www.real-world-systems.com/docs/ANSIcode.html | ||
: `${eraseScreen}${ESC}3J${ESC}H`; | ||
|
||
export const enterAlternativeScreen = ESC + '?1049h'; | ||
export const exitAlternativeScreen = ESC + '?1049l'; | ||
|
||
export const beep = BEL; | ||
|
||
export const link = (text, url) => [ | ||
OSC, | ||
'8', | ||
SEP, | ||
SEP, | ||
url, | ||
BEL, | ||
text, | ||
OSC, | ||
'8', | ||
SEP, | ||
SEP, | ||
BEL, | ||
].join(''); | ||
|
||
export const image = (buffer, options = {}) => { | ||
let returnValue = `${OSC}1337;File=inline=1`; | ||
|
||
if (options.width) { | ||
returnValue += `;width=${options.width}`; | ||
} | ||
|
||
if (options.height) { | ||
returnValue += `;height=${options.height}`; | ||
} | ||
|
||
if (options.preserveAspectRatio === false) { | ||
returnValue += ';preserveAspectRatio=0'; | ||
} | ||
|
||
return returnValue + ':' + buffer.toString('base64') + BEL; | ||
}; | ||
|
||
export const iTerm = { | ||
setCwd: (cwd = cwdFunction()) => `${OSC}50;CurrentDir=${cwd}${BEL}`, | ||
|
||
annotation(message, options = {}) { | ||
let returnValue = `${OSC}1337;`; | ||
|
||
const hasX = typeof options.x !== 'undefined'; | ||
const hasY = typeof options.y !== 'undefined'; | ||
if ((hasX || hasY) && !(hasX && hasY && typeof options.length !== 'undefined')) { | ||
throw new Error('`x`, `y` and `length` must be defined when `x` or `y` is defined'); | ||
} | ||
|
||
message = message.replace(/\|/g, ''); | ||
|
||
returnValue += options.isHidden ? 'AddHiddenAnnotation=' : 'AddAnnotation='; | ||
|
||
if (options.length > 0) { | ||
returnValue += ( | ||
hasX | ||
? [message, options.length, options.x, options.y] | ||
: [options.length, message] | ||
).join('|'); | ||
} else { | ||
returnValue += message; | ||
} | ||
|
||
return returnValue + BEL; | ||
}, | ||
}; | ||
|
||
export default { | ||
beep, | ||
clearScreen, | ||
clearTerminal, | ||
cursorBackward, | ||
cursorDown, | ||
cursorForward, | ||
cursorGetPosition, | ||
cursorHide, | ||
cursorLeft, | ||
cursorMove, | ||
cursorNextLine, | ||
cursorPrevLine, | ||
cursorRestorePosition, | ||
cursorSavePosition, | ||
cursorShow, | ||
cursorTo, | ||
cursorUp, | ||
enterAlternativeScreen, | ||
eraseDown, | ||
eraseEndLine, | ||
eraseLine, | ||
eraseLines, | ||
eraseScreen, | ||
eraseStartLine, | ||
eraseUp, | ||
exitAlternativeScreen, | ||
iTerm, | ||
image, | ||
link, | ||
scrollDown, | ||
scrollUp, | ||
} | ||
export default ansiEscapes; | ||
export * from './base.js'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,12 @@ | ||
import test from 'ava'; | ||
import ansiEscapes from './index.js'; | ||
import ansiEscapes, { cursorTo } from './index.js'; | ||
|
||
// Note: we don't test named exports here because the default export (tested | ||
// here) is just a composition of all the named exports. So if this test | ||
// passes we can safely assume the named exports are working. | ||
|
||
test('main', t => { | ||
test('default export', t => { | ||
t.true(Object.keys(ansiEscapes).length > 0); | ||
t.is(typeof ansiEscapes.cursorTo, 'function'); | ||
t.is(ansiEscapes.cursorTo(2, 2), '\u001B[3;3H'); | ||
}); | ||
|
||
test('named export(s)', t => { | ||
t.is(cursorTo, ansiEscapes.cursorTo); | ||
}); |