@makolabs/ripple
Version:
Simple Svelte 5 powered component library ✨
154 lines (153 loc) • 4.84 kB
JavaScript
/**
* Content detection utilities for AI chat messages
*/
/**
* Detects if content contains Mermaid diagram syntax
*/
export function detectMermaidDiagrams(content) {
const mermaidBlocks = [];
// Pattern to match mermaid code blocks
// Handles various Mermaid diagram types and whitespace scenarios
const mermaidRegex = /```(?:mermaid|graph|flowchart|sequenceDiagram|classDiagram|stateDiagram|stateDiagram-v2|erDiagram|journey|gantt|pie|gitgraph|mindmap)\s*\r?\n([\s\S]*?)\r?\n```/gi;
let match;
while ((match = mermaidRegex.exec(content)) !== null) {
mermaidBlocks.push({
diagram: match[1].trim(),
startIndex: match.index,
endIndex: match.index + match[0].length
});
}
return mermaidBlocks;
}
/**
* Detects code blocks in content
*/
export function detectCodeBlocks(content) {
const codeBlocks = [];
// Pattern to match code blocks with language specification
// Handles: ```language, ```, and various whitespace scenarios
const codeRegex = /```(\w+)?\s*\r?\n([\s\S]*?)\r?\n```/gi;
let match;
while ((match = codeRegex.exec(content)) !== null) {
const language = match[1] || 'text';
const code = match[2].trim();
// Skip empty code blocks
if (!code) {
continue;
}
// Skip if it's a mermaid diagram (handled separately)
if (!isMermaidLanguage(language)) {
codeBlocks.push({
language,
code,
startIndex: match.index,
endIndex: match.index + match[0].length
});
}
}
return codeBlocks;
}
/**
* Checks if a language identifier is for Mermaid diagrams
*/
function isMermaidLanguage(language) {
const mermaidLanguages = [
'mermaid',
'graph',
'flowchart',
'sequenceDiagram',
'classDiagram',
'stateDiagram',
'stateDiagram-v2',
'erDiagram',
'journey',
'gantt',
'pie',
'gitgraph',
'mindmap'
];
return mermaidLanguages.includes(language.toLowerCase());
}
/**
* Parses content into segments (text, code, mermaid)
*/
export function parseContentSegments(content) {
console.log('parseContentSegments', content);
const segments = [];
const mermaidBlocks = detectMermaidDiagrams(content);
const codeBlocks = detectCodeBlocks(content);
// Combine all blocks and sort by position
const allBlocks = [
...mermaidBlocks.map((block) => ({ ...block, type: 'mermaid' })),
...codeBlocks.map((block) => ({ ...block, type: 'code' }))
].sort((a, b) => a.startIndex - b.startIndex);
let currentIndex = 0;
for (const block of allBlocks) {
// Add text segment before this block
if (currentIndex < block.startIndex) {
const textContent = content.slice(currentIndex, block.startIndex).trim();
if (textContent) {
segments.push({
type: 'text',
content: textContent,
startIndex: currentIndex,
endIndex: block.startIndex
});
}
}
// Add the block segment
if (block.type === 'mermaid') {
segments.push({
type: 'mermaid',
content: block.diagram,
startIndex: block.startIndex,
endIndex: block.endIndex
});
}
else {
const codeBlock = block;
segments.push({
type: 'code',
content: codeBlock.code,
language: codeBlock.language,
startIndex: block.startIndex,
endIndex: block.endIndex
});
}
currentIndex = block.endIndex;
}
// Add remaining text after last block
if (currentIndex < content.length) {
const textContent = content.slice(currentIndex).trim();
if (textContent) {
segments.push({
type: 'text',
content: textContent,
startIndex: currentIndex,
endIndex: content.length
});
}
}
// If no blocks found, return entire content as text
if (segments.length === 0) {
segments.push({
type: 'text',
content: content,
startIndex: 0,
endIndex: content.length
});
}
return segments;
}
/**
* Simple check if content contains any Mermaid diagrams
*/
export function hasMermaidDiagrams(content) {
return detectMermaidDiagrams(content).length > 0;
}
/**
* Simple check if content contains any code blocks
*/
export function hasCodeBlocks(content) {
return detectCodeBlocks(content).length > 0;
}