import { Text } from "ink"; import hljs from "highlight.js"; import React from "react"; import { readFileSync } from "fs"; export function HighlightedCode({ code, language }: { code: string, language?: string, }) { try { let result; if (language && hljs.getLanguage(language)) { result = hljs.highlight(code, { language }); } else { result = hljs.highlightAuto(code); } const segments = parseHighlightedHTML(result.value); return ; } catch (error) { // If highlighting fails, return plain text lines return <> { code.split('\n').map((line, index) => ( {line} )) } ; } } type CodeSegment = { text: string, className?: string, }; function parseHighlightedHTML(html: string): CodeSegment[] { const segments: CodeSegment[] = []; let currentIndex = 0; // Simple state machine to parse HTML while (currentIndex < html.length) { const nextOpenTag = html.indexOf('', classEnd); if (classEnd === -1 || tagEnd === -1) break; const className = html.substring(classStart, classEnd); // Find the closing tag const closingTag = ''; const contentStart = tagEnd + 1; let closingTagStart = html.indexOf(closingTag, contentStart); // Handle nested spans by counting open/close tags let openCount = 1; let searchFrom = contentStart; while (openCount > 0 && closingTagStart !== -1) { const nextOpen = html.indexOf(' 0) { searchFrom = closingTagStart + closingTag.length; closingTagStart = html.indexOf(closingTag, searchFrom); } } } if (closingTagStart === -1) break; const content = html.substring(contentStart, closingTagStart); // Recursively parse content for nested spans if (content.includes(' { const linesInSegment = segment.text.split('\n'); linesInSegment.forEach((lineText, lineIndex) => { if (lineIndex > 0) { // This is a new line, push current line and start fresh lines.push(currentLine); currentLine = []; } if (lineText || lineIndex === linesInSegment.length - 1) { currentLine.push({ text: lineText, className: segment.className }); } }); }); // Push the last line if it has content if (currentLine.length > 0) { lines.push(currentLine); } return <> {lines.map((lineSegments, index) => ( ))} ; } function CodeLine({ segments }: { segments: CodeSegment[] }) { return {segments.map((segment, index) => { const color = segment.className ? getColorForClass(segment.className) : undefined; return ( {segment.text} ); })} ; } function decodeHtmlEntities(text: string): string { return text .replace(/"/g, '"') .replace(/'/g, "'") .replace(/'/g, "'") .replace(/'/g, "'") .replace(/</g, '<') .replace(/>/g, '>') .replace(/&/g, '&'); } function getColorForClass(className: string): string | undefined { const colorMap: Record = { 'hljs-keyword': 'blue', 'hljs-string': 'green', 'hljs-comment': 'gray', 'hljs-number': 'yellow', 'hljs-title': 'cyan', 'hljs-title function_': 'cyan', 'hljs-variable': 'magenta', 'hljs-type': 'blue', 'hljs-attr': 'yellow', 'hljs-built_in': 'red', 'hljs-literal': 'cyan', 'hljs-name': 'cyan', 'hljs-selector-tag': 'blue', 'hljs-selector-class': 'yellow', 'hljs-selector-id': 'magenta', 'hljs-property': 'cyan', 'hljs-value': 'green', }; return colorMap[className]; }