Spaces:
Running
Running
Commit Β·
395b01f
1
Parent(s): 8be02a3
commit 02115
Browse files- src/components/ChatBox.jsx +85 -2
src/components/ChatBox.jsx
CHANGED
|
@@ -27,13 +27,96 @@ export default function ChatBox({ messages, loading }) {
|
|
| 27 |
</div>
|
| 28 |
);
|
| 29 |
}
|
| 30 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
function Message({ content }) {
|
| 32 |
const parts = content.split(/```(?:[a-z]*)\n([\s\S]*?)```/g);
|
| 33 |
return (
|
| 34 |
<>
|
| 35 |
{parts.map((part, i) =>
|
| 36 |
-
i % 2 === 1 ? <CodeBlock key={i} code={part.trim()} /> : <p key={i}>{part}</p>
|
| 37 |
)}
|
| 38 |
</>
|
| 39 |
);
|
|
|
|
| 27 |
</div>
|
| 28 |
);
|
| 29 |
}
|
| 30 |
+
const formatText = (text) => {
|
| 31 |
+
// β
Handle base64 images
|
| 32 |
+
const imageRegex = /\[IMAGE_START\](.*?)\[IMAGE_END\]/gs;
|
| 33 |
+
text = text.replace(imageRegex, (match, base64) => {
|
| 34 |
+
const src = `data:image/png;base64,${base64.trim()}`;
|
| 35 |
+
return `<img src="${src}" alt="Generated Image" class="chat-image"/>`;
|
| 36 |
+
});
|
| 37 |
+
// β
Normalize line endings and remove excessive blank lines
|
| 38 |
+
text = text.replace(/\r\n|\r/g, '\n');
|
| 39 |
+
text = text.replace(/\n{3,}/g, '\n\n');
|
| 40 |
+
// β
Parse fenced code blocks (```code```)
|
| 41 |
+
text = text.replace(/```(\w+)?\n([\s\S]*?)```/g, (match, lang, code) => {
|
| 42 |
+
const language = lang ? ` class="language-${lang}"` : '';
|
| 43 |
+
return `<pre><code${language}>${code.trim().replace(/</g, '<').replace(/>/g, '>')}</code></pre>`;
|
| 44 |
+
});
|
| 45 |
+
// β
Parse blockquotes
|
| 46 |
+
text = text.replace(/^> (.*)$/gm, '<blockquote>$1</blockquote>');
|
| 47 |
+
// β
Headings
|
| 48 |
+
text = text.replace(/^### (.*)$/gm, '<h3>$1</h3>');
|
| 49 |
+
// β
Horizontal rules
|
| 50 |
+
text = text.replace(/^---$/gm, '<hr>');
|
| 51 |
+
// β
Bold (**text**) and italic (*text*)
|
| 52 |
+
text = text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
|
| 53 |
+
text = text.replace(/\*(.*?)\*/g, '<em>$1</em>');
|
| 54 |
+
// β
Emoji rendering using colon syntax (:smile:)
|
| 55 |
+
const emojiMap = {
|
| 56 |
+
smile: "π",
|
| 57 |
+
sad: "π’",
|
| 58 |
+
heart: "β€οΈ",
|
| 59 |
+
thumbs_up: "π",
|
| 60 |
+
fire: "π₯",
|
| 61 |
+
check: "β
",
|
| 62 |
+
x: "β",
|
| 63 |
+
star: "β",
|
| 64 |
+
rocket: "π",
|
| 65 |
+
warning: "β οΈ",
|
| 66 |
+
};
|
| 67 |
+
text = text.replace(/:([a-z0-9_+-]+):/g, (match, name) => emojiMap[name] || match);
|
| 68 |
+
// β
Unordered list (bullets)
|
| 69 |
+
const listify = (lines, tag) =>
|
| 70 |
+
`<${tag}>` +
|
| 71 |
+
lines.map(item => `<li>${item.replace(/^(\-|\d+\.)\s*/, '').trim()}</li>`).join('') +
|
| 72 |
+
`</${tag}>`;
|
| 73 |
+
text = text.replace(
|
| 74 |
+
/((?:^[-*] .+(?:\n|$))+)/gm,
|
| 75 |
+
(match) => listify(match.trim().split('\n'), 'ul')
|
| 76 |
+
);
|
| 77 |
+
// β
Ordered list (fix separate `1.` items issue)
|
| 78 |
+
text = text.replace(/^(\d+\. .+)$/gm, '__ORDERED__START__$1__ORDERED__END__');
|
| 79 |
+
text = text.replace(
|
| 80 |
+
/__ORDERED__START__(\d+\. .+?)__ORDERED__END__/gs,
|
| 81 |
+
(_, line) => `<ol><li>${line.replace(/^\d+\.\s*/, '')}</li></ol>`
|
| 82 |
+
);
|
| 83 |
+
text = text.replace(/<\/ol>\s*<ol>/g, '');
|
| 84 |
+
// β
Markdown-style tables
|
| 85 |
+
text = text.replace(
|
| 86 |
+
/^\|(.+?)\|\n\|([-:| ]+)\|\n((?:\|.*\|\n?)*)/gm,
|
| 87 |
+
(_, headerRow, dividerRow, bodyRows) => {
|
| 88 |
+
const headers = headerRow.split('|').map(h => `<th>${h.trim()}</th>`).join('');
|
| 89 |
+
const rows = bodyRows.trim().split('\n').map(r =>
|
| 90 |
+
'<tr>' + r.split('|').map(cell => `<td>${cell.trim()}</td>`).join('') + '</tr>'
|
| 91 |
+
).join('');
|
| 92 |
+
return `<table><thead><tr>${headers}</tr></thead><tbody>${rows}</tbody></table>`;
|
| 93 |
+
}
|
| 94 |
+
);
|
| 95 |
+
// β
Paragraphs and line breaks inside paragraphs
|
| 96 |
+
const blocks = text.split(/\n{2,}/).map(block => {
|
| 97 |
+
if (
|
| 98 |
+
block.startsWith('<h3>') ||
|
| 99 |
+
block.startsWith('<hr>') ||
|
| 100 |
+
block.startsWith('<ul>') ||
|
| 101 |
+
block.startsWith('<ol>') ||
|
| 102 |
+
block.startsWith('<table>') ||
|
| 103 |
+
block.startsWith('<pre>') ||
|
| 104 |
+
block.startsWith('<blockquote>') ||
|
| 105 |
+
block.startsWith('<img')
|
| 106 |
+
) {
|
| 107 |
+
return block;
|
| 108 |
+
} else {
|
| 109 |
+
return `<p>${block.trim().replace(/\n/g, '<br>')}</p>`;
|
| 110 |
+
}
|
| 111 |
+
});
|
| 112 |
+
return blocks.join('\n');
|
| 113 |
+
};
|
| 114 |
function Message({ content }) {
|
| 115 |
const parts = content.split(/```(?:[a-z]*)\n([\s\S]*?)```/g);
|
| 116 |
return (
|
| 117 |
<>
|
| 118 |
{parts.map((part, i) =>
|
| 119 |
+
i % 2 === 1 ? <CodeBlock key={i} code={part.trim()} /> : <p key={i}>{formatText(part)}</p>
|
| 120 |
)}
|
| 121 |
</>
|
| 122 |
);
|