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