UNPKG

askme-cli

Version:

askme-cli MCP server that collects user's next plan or confirmation through terminal window

195 lines 6.86 kB
import React from "react"; import { Text } from "ink"; /** * Lightweight Markdown parser * Supports: bold, italic, code, headers, lists */ export class SimpleMarkdownParser { /** * Parse markdown text into structured elements */ static parse(markdown) { const lines = markdown.split("\n"); const elements = []; for (const line of lines) { if (line.trim() === "") { // Skip empty lines continue; } // Parse headers const headerMatch = line.match(/^(#{1,6})\s+(.+)$/); if (headerMatch) { const level = headerMatch[1].length; const content = headerMatch[2]; elements.push({ type: "header", content: this.parseInlineMarkdown(content), level, color: this.getHeaderColor(level), bold: true, }); continue; } // Parse lists const listMatch = line.match(/^[\s]*[-\*\+]\s+(.+)$/); if (listMatch) { const content = listMatch[1]; elements.push({ type: "list", content: `• ${this.parseInlineMarkdown(content)}`, color: "cyan", }); continue; } // Normal text (including inline formatting) const parsedContent = this.parseInlineMarkdown(line); if (parsedContent.trim()) { elements.push({ type: "text", content: parsedContent, }); } } return elements; } /** * Parse inline markdown formats (bold, italic, code) */ static parseInlineMarkdown(text) { return (text // Code blocks (process first to avoid conflicts with other formats) .replace(/`([^`]+)`/g, "`$1`") // Keep code markers for later rendering // Bold .replace(/\*\*([^*]+)\*\*/g, "**$1**") // Keep format markers // Italic .replace(/\*([^*]+)\*/g, "*$1*")); // Keep format markers } /** * Get header color */ static getHeaderColor(level) { // Use cyan color for all headers to match "Ask User Confirmation" return "cyan"; } /** * Render single markdown element as Ink component */ static renderElement(element, key) { const baseProps = { key, color: element.color, backgroundColor: element.backgroundColor, bold: element.bold, italic: element.italic, }; switch (element.type) { case "header": return React.createElement(Text, { ...baseProps, bold: true, }, this.renderInlineElements(element.content)); case "list": return React.createElement(Text, { ...baseProps, }, this.renderInlineElements(element.content)); case "text": default: return React.createElement(Text, baseProps, this.renderInlineElements(element.content)); } } /** * Render inline format elements */ static renderInlineElements(content) { const parts = []; let remaining = content; let key = 0; while (remaining.length > 0) { // Find code blocks const codeMatch = remaining.match(/`([^`]+)`/); if (codeMatch && codeMatch.index !== undefined) { // Add text before code block if (codeMatch.index > 0) { const beforeText = remaining.substring(0, codeMatch.index); parts.push(...this.renderFormattedText(beforeText, key++)); } // Add code block parts.push(React.createElement(Text, { key: key++, color: "#B4591D", }, codeMatch[1])); remaining = remaining.substring(codeMatch.index + codeMatch[0].length); continue; } // No more code blocks, process remaining text parts.push(...this.renderFormattedText(remaining, key++)); break; } return parts; } /** * Render formatted text (bold, italic) */ static renderFormattedText(text, baseKey) { const parts = []; let remaining = text; let key = baseKey; while (remaining.length > 0) { // Find bold const boldMatch = remaining.match(/\*\*([^*]+)\*\*/); if (boldMatch && boldMatch.index !== undefined) { // Add text before bold if (boldMatch.index > 0) { const beforeText = remaining.substring(0, boldMatch.index); parts.push(...this.renderItalicText(beforeText, key++)); } // Add bold text parts.push(React.createElement(Text, { key: key++, bold: true, }, boldMatch[1])); remaining = remaining.substring(boldMatch.index + boldMatch[0].length); continue; } // No more bold, process remaining text's italic parts.push(...this.renderItalicText(remaining, key++)); break; } return parts; } /** * Render italic text */ static renderItalicText(text, baseKey) { const parts = []; let remaining = text; let key = baseKey; while (remaining.length > 0) { // Find italic const italicMatch = remaining.match(/\*([^*]+)\*/); if (italicMatch && italicMatch.index !== undefined) { // Add text before italic if (italicMatch.index > 0) { const beforeText = remaining.substring(0, italicMatch.index); if (beforeText) { parts.push(beforeText); } } // Add italic text parts.push(React.createElement(Text, { key: key++, italic: true, }, italicMatch[1])); remaining = remaining.substring(italicMatch.index + italicMatch[0].length); continue; } // No more italic, add plain text if (remaining) { parts.push(remaining); } break; } return parts; } } //# sourceMappingURL=markdownParser.js.map