code.gitea.io/gitea@v1.21.7/web_src/js/render/ansi.js (about)

     1  import {AnsiUp} from 'ansi_up';
     2  
     3  const replacements = [
     4    [/\x1b\[\d+[A-H]/g, ''], // Move cursor, treat them as no-op
     5    [/\x1b\[\d?[JK]/g, '\r'], // Erase display/line, treat them as a Carriage Return
     6  ];
     7  
     8  // render ANSI to HTML
     9  export function renderAnsi(line) {
    10    // create a fresh ansi_up instance because otherwise previous renders can influence
    11    // the output of future renders, because ansi_up is stateful and remembers things like
    12    // unclosed opening tags for colors.
    13    const ansi_up = new AnsiUp();
    14    ansi_up.use_classes = true;
    15  
    16    if (line.endsWith('\r\n')) {
    17      line = line.substring(0, line.length - 2);
    18    } else if (line.endsWith('\n')) {
    19      line = line.substring(0, line.length - 1);
    20    }
    21  
    22    if (line.includes('\x1b')) {
    23      for (const [regex, replacement] of replacements) {
    24        line = line.replace(regex, replacement);
    25      }
    26    }
    27  
    28    if (!line.includes('\r')) {
    29      return ansi_up.ansi_to_html(line);
    30    }
    31  
    32    // handle "\rReading...1%\rReading...5%\rReading...100%",
    33    // convert it into a multiple-line string: "Reading...1%\nReading...5%\nReading...100%"
    34    const lines = [];
    35    for (const part of line.split('\r')) {
    36      if (part === '') continue;
    37      const partHtml = ansi_up.ansi_to_html(part);
    38      if (partHtml !== '') {
    39        lines.push(partHtml);
    40      }
    41    }
    42  
    43    // the log message element is with "white-space: break-spaces;", so use "\n" to break lines
    44    return lines.join('\n');
    45  }