MCPcopy
hub / github.com/unicity-sphere/sphere / parseInline

Function parseInline

src/utils/markdown.tsx:243–462  ·  view source on GitHub ↗
(text: string, keyPrefix: string, mentionClassName: string = 'text-white')

Source from the content-addressed store, hash-verified

241
242// Parse inline markdown and HTML (bold, italic, code, br, links, images, plain URLs, @mentions)
243function parseInline(text: string, keyPrefix: string, mentionClassName: string = 'text-white'): React.ReactNode[] {
244 // FIRST PASS: Handle escape sequences (e.g., \* should become just *)
245 const unescapedText = text.replace(/\\([*_`[\]()#+-.|!\\])/g, '$1');
246
247 // SECOND PASS: Extract inline math and replace with safe tokens that won't be matched by markdown regex
248 const mathBlocks: string[] = [];
249 const mathPlaceholder = '\u0000MATH'; // Unique placeholder that markdown won't match
250
251 const processedText = unescapedText.replace(
252 /(?<!\\)((?:\\\\)*)\\\((.+?)\\\)/g,
253 (match, backslashes, latex) => {
254 if (backslashes && backslashes.length % 2 === 1) {
255 // Escaped, remove one backslash
256 return match.slice(1);
257 }
258 // Store math and return placeholder
259 const index = mathBlocks.push(latex) - 1;
260 return `${mathPlaceholder}${index}\u0000`;
261 }
262 );
263
264 const parts: React.ReactNode[] = [];
265 let key = 0;
266
267 // THIRD PASS: Process markdown - math placeholders won't be captured by markdown patterns
268 // Added @mention pattern at the end: @username (alphanumeric, underscore, hyphen)
269 // Note: (?<!\S) ensures @ is at start of word (not in email like user@example.com)
270 const regex = /(\*\*(.+?)\*\*|\*([^\s*](?:[^*]*[^\s*])?)\*|_([^_]+?)_|`([^`]+?)`|<br\s*\/?>|<b>(.+?)<\/b>|<strong>(.+?)<\/strong>|<i>(.+?)<\/i>|<em>(.+?)<\/em>|<code>(.+?)<\/code>|<a\s+href=["']([^"']+)["']>(.+?)<\/a>|\[([^\]]+)\]\(((?:[^\s()]|\([^\s)]*\))+)(?:\s+"([^"]+)")?\)|!\[([^\]]*)\]\(((?:[^()]|\([^)]*\))+)\)|(unicity-connect:\/\/[^\s<>[\]()]+[^\s<>[\]().,;:!?'"])|(https?:\/\/[^\s<>[\]()]+[^\s<>[\]().,;:!?'"])|((?<!\S)@[\w-]+))/gi;
271 let lastIndex = 0;
272 let match;
273
274 while ((match = regex.exec(processedText)) !== null) {
275 // Process text before match (may contain math placeholders)
276 if (match.index > lastIndex) {
277 const textBefore = processedText.slice(lastIndex, match.index);
278 parts.push(...replaceMathPlaceholders(textBefore, mathBlocks, keyPrefix, key));
279 key += mathBlocks.length;
280 }
281
282 if (match[2]) {
283 // **bold** - recursively parse content for @mentions, links, etc.
284 const content = parseInline(match[2], `${keyPrefix}-bold-${key}`, mentionClassName);
285 parts.push(<strong key={`${keyPrefix}-strong-${key++}`}>{content}</strong>);
286 } else if (match[3]) {
287 // *italic* - recursively parse content
288 const content = parseInline(match[3], `${keyPrefix}-italic-${key}`, mentionClassName);
289 parts.push(<em key={`${keyPrefix}-em-${key++}`}>{content}</em>);
290 } else if (match[4]) {
291 // _italic_ - recursively parse content
292 const content = parseInline(match[4], `${keyPrefix}-italic2-${key}`, mentionClassName);
293 parts.push(<em key={`${keyPrefix}-em2-${key++}`}>{content}</em>);
294 } else if (match[5]) {
295 // `code` - don't process math inside code blocks
296 parts.push(
297 <code key={`${keyPrefix}-code-${key++}`} className="bg-neutral-200 dark:bg-neutral-700/50 text-neutral-900 dark:text-neutral-200 px-1.5 py-0.5 rounded text-sm font-mono">
298 {match[5]}
299 </code>
300 );

Callers 3

parseTableFunction · 0.85
parseHeaderFunction · 0.85
MarkdownContentFunction · 0.85

Calls 4

deepLinkToHttpsFunction · 0.90
getMentionClickHandlerFunction · 0.90
replaceMathPlaceholdersFunction · 0.85
handlerFunction · 0.50

Tested by

no test coverage detected