mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
Co-authored-by: Ryan Fang <ryan.fang@gllue.com>
This commit is contained in:
@@ -9,6 +9,7 @@ import { Text, Box } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { colorizeCode } from './CodeColorizer.js';
|
||||
import { TableRenderer } from './TableRenderer.js';
|
||||
import { RenderInline } from './InlineMarkdownRenderer.js';
|
||||
|
||||
interface MarkdownDisplayProps {
|
||||
text: string;
|
||||
@@ -18,12 +19,6 @@ interface MarkdownDisplayProps {
|
||||
}
|
||||
|
||||
// Constants for Markdown parsing and rendering
|
||||
const BOLD_MARKER_LENGTH = 2; // For "**"
|
||||
const ITALIC_MARKER_LENGTH = 1; // For "*" or "_"
|
||||
const STRIKETHROUGH_MARKER_LENGTH = 2; // For "~~"
|
||||
const INLINE_CODE_MARKER_LENGTH = 1; // For "`"
|
||||
const UNDERLINE_TAG_START_LENGTH = 3; // For "<u>"
|
||||
const UNDERLINE_TAG_END_LENGTH = 4; // For "</u>"
|
||||
|
||||
const EMPTY_LINE_HEIGHT = 1;
|
||||
const CODE_BLOCK_PADDING = 1;
|
||||
@@ -277,143 +272,6 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
|
||||
|
||||
// Helper functions (adapted from static methods of MarkdownRenderer)
|
||||
|
||||
interface RenderInlineProps {
|
||||
text: string;
|
||||
}
|
||||
|
||||
const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
|
||||
const nodes: React.ReactNode[] = [];
|
||||
let lastIndex = 0;
|
||||
const inlineRegex =
|
||||
/(\*\*.*?\*\*|\*.*?\*|_.*?_|~~.*?~~|\[.*?\]\(.*?\)|`+.+?`+|<u>.*?<\/u>)/g;
|
||||
let match;
|
||||
|
||||
while ((match = inlineRegex.exec(text)) !== null) {
|
||||
if (match.index > lastIndex) {
|
||||
nodes.push(
|
||||
<Text key={`t-${lastIndex}`}>
|
||||
{text.slice(lastIndex, match.index)}
|
||||
</Text>,
|
||||
);
|
||||
}
|
||||
|
||||
const fullMatch = match[0];
|
||||
let renderedNode: React.ReactNode = null;
|
||||
const key = `m-${match.index}`;
|
||||
|
||||
try {
|
||||
if (
|
||||
fullMatch.startsWith('**') &&
|
||||
fullMatch.endsWith('**') &&
|
||||
fullMatch.length > BOLD_MARKER_LENGTH * 2
|
||||
) {
|
||||
renderedNode = (
|
||||
<Text key={key} bold>
|
||||
{fullMatch.slice(BOLD_MARKER_LENGTH, -BOLD_MARKER_LENGTH)}
|
||||
</Text>
|
||||
);
|
||||
} else if (
|
||||
fullMatch.length > ITALIC_MARKER_LENGTH * 2 &&
|
||||
((fullMatch.startsWith('*') && fullMatch.endsWith('*')) ||
|
||||
(fullMatch.startsWith('_') && fullMatch.endsWith('_'))) &&
|
||||
!/\w/.test(text.substring(match.index - 1, match.index)) &&
|
||||
!/\w/.test(
|
||||
text.substring(inlineRegex.lastIndex, inlineRegex.lastIndex + 1),
|
||||
) &&
|
||||
!/\S[./\\]/.test(text.substring(match.index - 2, match.index)) &&
|
||||
!/[./\\]\S/.test(
|
||||
text.substring(inlineRegex.lastIndex, inlineRegex.lastIndex + 2),
|
||||
)
|
||||
) {
|
||||
renderedNode = (
|
||||
<Text key={key} italic>
|
||||
{fullMatch.slice(ITALIC_MARKER_LENGTH, -ITALIC_MARKER_LENGTH)}
|
||||
</Text>
|
||||
);
|
||||
} else if (
|
||||
fullMatch.startsWith('~~') &&
|
||||
fullMatch.endsWith('~~') &&
|
||||
fullMatch.length > STRIKETHROUGH_MARKER_LENGTH * 2
|
||||
) {
|
||||
renderedNode = (
|
||||
<Text key={key} strikethrough>
|
||||
{fullMatch.slice(
|
||||
STRIKETHROUGH_MARKER_LENGTH,
|
||||
-STRIKETHROUGH_MARKER_LENGTH,
|
||||
)}
|
||||
</Text>
|
||||
);
|
||||
} else if (
|
||||
fullMatch.startsWith('`') &&
|
||||
fullMatch.endsWith('`') &&
|
||||
fullMatch.length > INLINE_CODE_MARKER_LENGTH
|
||||
) {
|
||||
const codeMatch = fullMatch.match(/^(`+)(.+?)\1$/s);
|
||||
if (codeMatch && codeMatch[2]) {
|
||||
renderedNode = (
|
||||
<Text key={key} color={Colors.AccentPurple}>
|
||||
{codeMatch[2]}
|
||||
</Text>
|
||||
);
|
||||
} else {
|
||||
renderedNode = (
|
||||
<Text key={key} color={Colors.AccentPurple}>
|
||||
{fullMatch.slice(
|
||||
INLINE_CODE_MARKER_LENGTH,
|
||||
-INLINE_CODE_MARKER_LENGTH,
|
||||
)}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
} else if (
|
||||
fullMatch.startsWith('[') &&
|
||||
fullMatch.includes('](') &&
|
||||
fullMatch.endsWith(')')
|
||||
) {
|
||||
const linkMatch = fullMatch.match(/\[(.*?)\]\((.*?)\)/);
|
||||
if (linkMatch) {
|
||||
const linkText = linkMatch[1];
|
||||
const url = linkMatch[2];
|
||||
renderedNode = (
|
||||
<Text key={key}>
|
||||
{linkText}
|
||||
<Text color={Colors.AccentBlue}> ({url})</Text>
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
} else if (
|
||||
fullMatch.startsWith('<u>') &&
|
||||
fullMatch.endsWith('</u>') &&
|
||||
fullMatch.length >
|
||||
UNDERLINE_TAG_START_LENGTH + UNDERLINE_TAG_END_LENGTH - 1 // -1 because length is compared to combined length of start and end tags
|
||||
) {
|
||||
renderedNode = (
|
||||
<Text key={key} underline>
|
||||
{fullMatch.slice(
|
||||
UNDERLINE_TAG_START_LENGTH,
|
||||
-UNDERLINE_TAG_END_LENGTH,
|
||||
)}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error parsing inline markdown part:', fullMatch, e);
|
||||
renderedNode = null;
|
||||
}
|
||||
|
||||
nodes.push(renderedNode ?? <Text key={key}>{fullMatch}</Text>);
|
||||
lastIndex = inlineRegex.lastIndex;
|
||||
}
|
||||
|
||||
if (lastIndex < text.length) {
|
||||
nodes.push(<Text key={`t-${lastIndex}`}>{text.slice(lastIndex)}</Text>);
|
||||
}
|
||||
|
||||
return <>{nodes.filter((node) => node !== null)}</>;
|
||||
};
|
||||
|
||||
const RenderInline = React.memo(RenderInlineInternal);
|
||||
|
||||
interface RenderCodeBlockProps {
|
||||
content: string[];
|
||||
lang: string | null;
|
||||
|
||||
Reference in New Issue
Block a user