base.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. import process from 'node:process';
  2. import {isBrowser} from 'environment';
  3. const ESC = '\u001B[';
  4. const OSC = '\u001B]';
  5. const BEL = '\u0007';
  6. const SEP = ';';
  7. const isTerminalApp = !isBrowser && process.env.TERM_PROGRAM === 'Apple_Terminal';
  8. const isWindows = !isBrowser && process.platform === 'win32';
  9. const cwdFunction = isBrowser ? () => {
  10. throw new Error('`process.cwd()` only works in Node.js, not the browser.');
  11. } : process.cwd;
  12. export const cursorTo = (x, y) => {
  13. if (typeof x !== 'number') {
  14. throw new TypeError('The `x` argument is required');
  15. }
  16. if (typeof y !== 'number') {
  17. return ESC + (x + 1) + 'G';
  18. }
  19. return ESC + (y + 1) + SEP + (x + 1) + 'H';
  20. };
  21. export const cursorMove = (x, y) => {
  22. if (typeof x !== 'number') {
  23. throw new TypeError('The `x` argument is required');
  24. }
  25. let returnValue = '';
  26. if (x < 0) {
  27. returnValue += ESC + (-x) + 'D';
  28. } else if (x > 0) {
  29. returnValue += ESC + x + 'C';
  30. }
  31. if (y < 0) {
  32. returnValue += ESC + (-y) + 'A';
  33. } else if (y > 0) {
  34. returnValue += ESC + y + 'B';
  35. }
  36. return returnValue;
  37. };
  38. export const cursorUp = (count = 1) => ESC + count + 'A';
  39. export const cursorDown = (count = 1) => ESC + count + 'B';
  40. export const cursorForward = (count = 1) => ESC + count + 'C';
  41. export const cursorBackward = (count = 1) => ESC + count + 'D';
  42. export const cursorLeft = ESC + 'G';
  43. export const cursorSavePosition = isTerminalApp ? '\u001B7' : ESC + 's';
  44. export const cursorRestorePosition = isTerminalApp ? '\u001B8' : ESC + 'u';
  45. export const cursorGetPosition = ESC + '6n';
  46. export const cursorNextLine = ESC + 'E';
  47. export const cursorPrevLine = ESC + 'F';
  48. export const cursorHide = ESC + '?25l';
  49. export const cursorShow = ESC + '?25h';
  50. export const eraseLines = count => {
  51. let clear = '';
  52. for (let i = 0; i < count; i++) {
  53. clear += eraseLine + (i < count - 1 ? cursorUp() : '');
  54. }
  55. if (count) {
  56. clear += cursorLeft;
  57. }
  58. return clear;
  59. };
  60. export const eraseEndLine = ESC + 'K';
  61. export const eraseStartLine = ESC + '1K';
  62. export const eraseLine = ESC + '2K';
  63. export const eraseDown = ESC + 'J';
  64. export const eraseUp = ESC + '1J';
  65. export const eraseScreen = ESC + '2J';
  66. export const scrollUp = ESC + 'S';
  67. export const scrollDown = ESC + 'T';
  68. export const clearScreen = '\u001Bc';
  69. export const clearTerminal = isWindows
  70. ? `${eraseScreen}${ESC}0f`
  71. // 1. Erases the screen (Only done in case `2` is not supported)
  72. // 2. Erases the whole screen including scrollback buffer
  73. // 3. Moves cursor to the top-left position
  74. // More info: https://www.real-world-systems.com/docs/ANSIcode.html
  75. : `${eraseScreen}${ESC}3J${ESC}H`;
  76. export const enterAlternativeScreen = ESC + '?1049h';
  77. export const exitAlternativeScreen = ESC + '?1049l';
  78. export const beep = BEL;
  79. export const link = (text, url) => [
  80. OSC,
  81. '8',
  82. SEP,
  83. SEP,
  84. url,
  85. BEL,
  86. text,
  87. OSC,
  88. '8',
  89. SEP,
  90. SEP,
  91. BEL,
  92. ].join('');
  93. export const image = (data, options = {}) => {
  94. let returnValue = `${OSC}1337;File=inline=1`;
  95. if (options.width) {
  96. returnValue += `;width=${options.width}`;
  97. }
  98. if (options.height) {
  99. returnValue += `;height=${options.height}`;
  100. }
  101. if (options.preserveAspectRatio === false) {
  102. returnValue += ';preserveAspectRatio=0';
  103. }
  104. return returnValue + ':' + Buffer.from(data).toString('base64') + BEL;
  105. };
  106. export const iTerm = {
  107. setCwd: (cwd = cwdFunction()) => `${OSC}50;CurrentDir=${cwd}${BEL}`,
  108. annotation(message, options = {}) {
  109. let returnValue = `${OSC}1337;`;
  110. const hasX = options.x !== undefined;
  111. const hasY = options.y !== undefined;
  112. if ((hasX || hasY) && !(hasX && hasY && options.length !== undefined)) {
  113. throw new Error('`x`, `y` and `length` must be defined when `x` or `y` is defined');
  114. }
  115. message = message.replaceAll('|', '');
  116. returnValue += options.isHidden ? 'AddHiddenAnnotation=' : 'AddAnnotation=';
  117. if (options.length > 0) {
  118. returnValue += (
  119. hasX
  120. ? [message, options.length, options.x, options.y]
  121. : [options.length, message]
  122. ).join('|');
  123. } else {
  124. returnValue += message;
  125. }
  126. return returnValue + BEL;
  127. },
  128. };