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];
}