// Inspired by Chatbot-UI and modified to fit the needs of this project // @see https://github.com/mckaywrigley/chatbot-ui/blob/main/components/Markdown/CodeBlock.tsx 'use client'; import { FC, memo } from 'react'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { coldarkDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'; import { useCopyToClipboard } from '@/lib/hooks/useCopyToClipboard'; import { IconCheck, IconCopy, IconDownload } from '@/components/ui/Icons'; import { Button } from '@/components/ui/Button'; interface Props { language: string; value: string; } interface languageMap { [key: string]: string | undefined; } export const programmingLanguages: languageMap = { javascript: '.js', python: '.py', java: '.java', vim: '.txt', c: '.c', cpp: '.cpp', 'c++': '.cpp', 'c#': '.cs', ruby: '.rb', php: '.php', swift: '.swift', 'objective-c': '.m', kotlin: '.kt', typescript: '.ts', go: '.go', perl: '.pl', rust: '.rs', scala: '.scala', haskell: '.hs', lua: '.lua', shell: '.sh', sql: '.sql', html: '.html', css: '.css', // custom titles print: '.txt', error: '.txt', // add more file extensions here, make sure the key is same as language prop in CodeBlock.tsx component }; const customSyntax: languageMap = { print: 'vim', output: 'vim', error: 'vim', }; export const generateRandomString = (length: number, lowercase = false) => { const chars = 'ABCDEFGHJKLMNPQRSTUVWXY3456789'; // excluding similar looking characters like Z, 2, I, 1, O, 0 let result = ''; for (let i = 0; i < length; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return lowercase ? result.toLowerCase() : result; }; const CodeBlock: FC = memo(({ language, value }) => { const { isCopied, copyToClipboard } = useCopyToClipboard({ timeout: 2000 }); const downloadAsFile = () => { if (typeof window === 'undefined') { return; } const fileExtension = programmingLanguages[language] || '.file'; const suggestedFileName = `file-${generateRandomString( 3, true, )}${fileExtension}`; const fileName = window.prompt('Enter file name' || '', suggestedFileName); if (!fileName) { // User pressed cancel on prompt. return; } const blob = new Blob([value], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.download = fileName; link.href = url; link.style.display = 'none'; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); }; const onCopy = () => { if (isCopied) return; copyToClipboard(value); }; return (
{language}
{value}
); }); CodeBlock.displayName = 'CodeBlock'; export { CodeBlock };