1*26ad19fcSEvan Bacon/**
2*26ad19fcSEvan Bacon * Copyright (c) 650 Industries.
3*26ad19fcSEvan Bacon * Copyright (c) Meta Platforms, Inc. and affiliates.
4*26ad19fcSEvan Bacon *
5*26ad19fcSEvan Bacon * This source code is licensed under the MIT license found in the
6*26ad19fcSEvan Bacon * LICENSE file in the root directory of this source tree.
7*26ad19fcSEvan Bacon */
8*26ad19fcSEvan Baconimport Anser from 'anser';
9*26ad19fcSEvan Baconimport React from 'react';
10*26ad19fcSEvan Baconimport { StyleProp, StyleSheet, Text, TextStyle, View } from 'react-native';
11*26ad19fcSEvan Bacon
12*26ad19fcSEvan Bacon// Afterglow theme from https://iterm2colorschemes.com/
13*26ad19fcSEvan Baconconst COLORS: Record<string, string> = {
14*26ad19fcSEvan Bacon  'ansi-black': 'rgb(27, 27, 27)',
15*26ad19fcSEvan Bacon  'ansi-red': 'rgb(187, 86, 83)',
16*26ad19fcSEvan Bacon  'ansi-green': 'rgb(144, 157, 98)',
17*26ad19fcSEvan Bacon  'ansi-yellow': 'rgb(234, 193, 121)',
18*26ad19fcSEvan Bacon  'ansi-blue': 'rgb(125, 169, 199)',
19*26ad19fcSEvan Bacon  'ansi-magenta': 'rgb(176, 101, 151)',
20*26ad19fcSEvan Bacon  'ansi-cyan': 'rgb(140, 220, 216)',
21*26ad19fcSEvan Bacon  // Instead of white, use the default color provided to the component
22*26ad19fcSEvan Bacon  // 'ansi-white': 'rgb(216, 216, 216)',
23*26ad19fcSEvan Bacon  'ansi-bright-black': 'rgb(98, 98, 98)',
24*26ad19fcSEvan Bacon  'ansi-bright-red': 'rgb(187, 86, 83)',
25*26ad19fcSEvan Bacon  'ansi-bright-green': 'rgb(144, 157, 98)',
26*26ad19fcSEvan Bacon  'ansi-bright-yellow': 'rgb(234, 193, 121)',
27*26ad19fcSEvan Bacon  'ansi-bright-blue': 'rgb(125, 169, 199)',
28*26ad19fcSEvan Bacon  'ansi-bright-magenta': 'rgb(176, 101, 151)',
29*26ad19fcSEvan Bacon  'ansi-bright-cyan': 'rgb(140, 220, 216)',
30*26ad19fcSEvan Bacon  'ansi-bright-white': 'rgb(247, 247, 247)',
31*26ad19fcSEvan Bacon};
32*26ad19fcSEvan Bacon
33*26ad19fcSEvan Baconexport function Ansi({ text, style }: { text: string; style: StyleProp<TextStyle> }) {
34*26ad19fcSEvan Bacon  let commonWhitespaceLength = Infinity;
35*26ad19fcSEvan Bacon  const parsedLines = text.split(/\n/).map((line) =>
36*26ad19fcSEvan Bacon    Anser.ansiToJson(line, {
37*26ad19fcSEvan Bacon      json: true,
38*26ad19fcSEvan Bacon      remove_empty: true,
39*26ad19fcSEvan Bacon      use_classes: true,
40*26ad19fcSEvan Bacon    })
41*26ad19fcSEvan Bacon  );
42*26ad19fcSEvan Bacon
43*26ad19fcSEvan Bacon  parsedLines.map((lines) => {
44*26ad19fcSEvan Bacon    // The third item on each line includes the whitespace of the source code.
45*26ad19fcSEvan Bacon    // We are looking for the least amount of common whitespace to trim all lines.
46*26ad19fcSEvan Bacon    // Example: Array [" ", " 96 |", "     text", ...]
47*26ad19fcSEvan Bacon    const match = lines[2] && lines[2]?.content?.match(/^ +/);
48*26ad19fcSEvan Bacon    const whitespaceLength = (match && match[0]?.length) || 0;
49*26ad19fcSEvan Bacon    if (whitespaceLength < commonWhitespaceLength) {
50*26ad19fcSEvan Bacon      commonWhitespaceLength = whitespaceLength;
51*26ad19fcSEvan Bacon    }
52*26ad19fcSEvan Bacon  });
53*26ad19fcSEvan Bacon
54*26ad19fcSEvan Bacon  const getText = (content: string, key: number) => {
55*26ad19fcSEvan Bacon    if (key === 1) {
56*26ad19fcSEvan Bacon      // Remove the vertical bar after line numbers
57*26ad19fcSEvan Bacon      return content.replace(/\| $/, ' ');
58*26ad19fcSEvan Bacon    } else if (key === 2 && commonWhitespaceLength < Infinity) {
59*26ad19fcSEvan Bacon      // Remove common whitespace at the beginning of the line
60*26ad19fcSEvan Bacon      return content.substr(commonWhitespaceLength);
61*26ad19fcSEvan Bacon    } else {
62*26ad19fcSEvan Bacon      return content;
63*26ad19fcSEvan Bacon    }
64*26ad19fcSEvan Bacon  };
65*26ad19fcSEvan Bacon
66*26ad19fcSEvan Bacon  return (
67*26ad19fcSEvan Bacon    <View>
68*26ad19fcSEvan Bacon      {parsedLines.map((items, i) => (
69*26ad19fcSEvan Bacon        <View style={styles.line} key={i}>
70*26ad19fcSEvan Bacon          {items.map((bundle, key) => {
71*26ad19fcSEvan Bacon            const textStyle =
72*26ad19fcSEvan Bacon              bundle.fg && COLORS[bundle.fg]
73*26ad19fcSEvan Bacon                ? {
74*26ad19fcSEvan Bacon                    backgroundColor: bundle.bg && COLORS[bundle.bg],
75*26ad19fcSEvan Bacon                    color: bundle.fg && COLORS[bundle.fg],
76*26ad19fcSEvan Bacon                  }
77*26ad19fcSEvan Bacon                : {
78*26ad19fcSEvan Bacon                    backgroundColor: bundle.bg && COLORS[bundle.bg],
79*26ad19fcSEvan Bacon                  };
80*26ad19fcSEvan Bacon            return (
81*26ad19fcSEvan Bacon              <Text style={[style, textStyle]} key={key}>
82*26ad19fcSEvan Bacon                {getText(bundle.content, key)}
83*26ad19fcSEvan Bacon              </Text>
84*26ad19fcSEvan Bacon            );
85*26ad19fcSEvan Bacon          })}
86*26ad19fcSEvan Bacon        </View>
87*26ad19fcSEvan Bacon      ))}
88*26ad19fcSEvan Bacon    </View>
89*26ad19fcSEvan Bacon  );
90*26ad19fcSEvan Bacon}
91*26ad19fcSEvan Bacon
92*26ad19fcSEvan Baconconst styles = StyleSheet.create({
93*26ad19fcSEvan Bacon  line: {
94*26ad19fcSEvan Bacon    flexDirection: 'row',
95*26ad19fcSEvan Bacon  },
96*26ad19fcSEvan Bacon});
97