GLMPilot / packages /client /src /utils /markdownParser.ts
E5K7's picture
Initial commit: Rebranded to GLMPilot and migrated to GLM-5 API
c2c8c8d
export interface MarkdownSegment {
type: 'text' | 'code';
content?: string;
filename?: string | null;
language?: string;
code?: string;
}
export function parseMarkdown(content: string): MarkdownSegment[] {
const segments: MarkdownSegment[] = [];
const lines = content.split('\n');
let currentText = '';
let inCodeBlock = false;
let codeContent = '';
let filename: string | null = null;
let language = '';
let nestingLevel = 0;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const trimmed = line.trim();
const isBackticks = trimmed.startsWith('```');
if (isBackticks) {
if (!inCodeBlock) {
inCodeBlock = true;
language = trimmed.replace(/^`+/, '').trim();
nestingLevel = 0;
// Peek at previous text for filename e.g. **`file.ts`**
const prevTextLines = currentText.trimEnd().split('\n');
const lastPrevLine = prevTextLines[prevTextLines.length - 1] || '';
const fileMatch = lastPrevLine.match(/\*\*\`?([^\`\n]+)\`?\*\*/);
if (fileMatch) {
filename = fileMatch[1];
prevTextLines.pop();
currentText = prevTextLines.join('\n');
} else {
filename = null;
}
if (currentText.trim()) {
segments.push({ type: 'text', content: currentText.trim() });
}
currentText = '';
codeContent = '';
} else {
// Distinguish between inner open (```bash) and close (```)
const isInnerOpen = trimmed.length > 3 && trimmed !== '```';
const isClose = /^`+$/.test(trimmed);
if (isInnerOpen) {
nestingLevel++;
codeContent += line + '\n';
} else if (isClose) {
if (nestingLevel > 0) {
nestingLevel--;
codeContent += line + '\n';
} else {
inCodeBlock = false;
segments.push({ type: 'code', filename, language, code: codeContent.trim() });
codeContent = '';
filename = null;
}
} else {
codeContent += line + '\n';
}
}
} else {
if (inCodeBlock) {
codeContent += line + '\n';
} else {
currentText += line + '\n';
}
}
}
if (inCodeBlock && codeContent.trim()) {
segments.push({ type: 'code', filename, language, code: codeContent.trim() });
} else if (currentText.trim()) {
segments.push({ type: 'text', content: currentText.trim() });
}
return segments;
}