diff --git a/components/ChatMessage.tsx b/components/ChatMessage.tsx index 5aa20c21e..67dd77859 100644 --- a/components/ChatMessage.tsx +++ b/components/ChatMessage.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Sandpack } from '@codesandbox/sandpack-react'; +import { Sandpack, SandpackFiles } from '@codesandbox/sandpack-react'; import Prism from 'prismjs'; import 'prismjs/themes/prism.css'; @@ -39,7 +39,22 @@ export interface UiMessage { /// Utilities to split the message into blocks of text and code type MessageBlock = { type: 'text'; content: string; } | CodeMessageBlock; -type CodeMessageBlock = { type: 'code'; content: string; code: string; language: string; }; +type CodeMessageBlock = { type: 'code'; content: string; language: string | null; complete: boolean; code: string; }; + +const inferLanguage = (markdownLanguage: string, code: string): string | null => { + if (markdownLanguage && !markdownLanguage.includes('.')) + return markdownLanguage; + + // based on how the code starts, return the language + if (code.startsWith(' { const codeBlockRegex = /`{3,}(\w+)?\n([\s\S]*?)(`{3,}|$)/g; @@ -49,8 +64,9 @@ const parseAndHighlightCodeBlocks = (text: string): MessageBlock[] => { let match; while ((match = codeBlockRegex.exec(text)) !== null) { - const language = match[1] || 'typescript'; - const codeBlock = match[2].trim(); + const markdownLanguage = (match[1] || '').trim(); + const code = match[2].trim(); + const blockEnd: string = match[3]; // Load the specified language if it's not loaded yet // NOTE: this is commented out because it inflates the size of the bundle by 200k @@ -62,13 +78,16 @@ const parseAndHighlightCodeBlocks = (text: string): MessageBlock[] => { // } // } + const codeLanguage = inferLanguage(markdownLanguage, code); + const highlightLanguage = codeLanguage || 'typescript'; const highlightedCode = Prism.highlight( - codeBlock, - Prism.languages[language] || Prism.languages.typescript, - language, + code, + Prism.languages[highlightLanguage] || Prism.languages.typescript, + highlightLanguage, ); + result.push({ type: 'text', content: text.slice(lastIndex, match.index) }); - result.push({ type: 'code', content: highlightedCode, code: codeBlock, language }); + result.push({ type: 'code', content: highlightedCode, language: codeLanguage, complete: blockEnd.startsWith('```'), code }); lastIndex = match.index + match[0].length; } @@ -89,22 +108,24 @@ const copyToClipboard = (text: string) => { /// Renderers for the different types of message blocks -type SandpackConfig = { template: 'vanilla-ts' | 'vanilla', files: Record }; +type SandpackConfig = { files: SandpackFiles, template: 'vanilla-ts' | 'vanilla' }; + +const runnableLanguages = ['html', 'javascript', 'typescript']; function RunnableCode({ codeBlock, theme }: { codeBlock: CodeMessageBlock, theme: Theme }): JSX.Element | null { let config: SandpackConfig; switch (codeBlock.language) { - case 'typescript': - case 'javascript': - config = { - template: 'vanilla-ts', - files: { '/index.ts': codeBlock.code }, - }; - break; case 'html': config = { template: 'vanilla', - files: { '/index.html': codeBlock.code }, + files: { '/index.html': codeBlock.code, '/index.js': '' }, + }; + break; + case 'javascript': + case 'typescript': + config = { + template: 'vanilla-ts', + files: { '/index.ts': codeBlock.code }, }; break; default: @@ -112,7 +133,7 @@ function RunnableCode({ codeBlock, theme }: { codeBlock: CodeMessageBlock, theme } return ( + options={{ showConsole: true, showConsoleButton: true, showTabs: true, showNavigator: false }} /> ); } @@ -125,6 +146,8 @@ function ChatMessageCodeBlock({ codeBlock, theme, sx }: { codeBlock: CodeMessage const handleToggleSandpack = () => setShowSandpack(!showSandpack); + const showRunIcon = codeBlock.complete && !!codeBlock.language && runnableLanguages.includes(codeBlock.language); + return - - {showSandpack ? : } - + {showRunIcon && ( + + {showSandpack ? : } + + )} - {showSandpack && } + {showRunIcon && showSandpack && } ; } diff --git a/components/Composer.tsx b/components/Composer.tsx index 226b50d5e..ef87aa740 100644 --- a/components/Composer.tsx +++ b/components/Composer.tsx @@ -283,7 +283,7 @@ export function Composer({ isDeveloper, disableSend, sendMessage }: { isDevelope {history.map((item, index) => ( pasteFromHistory(item.text)}> - {item.count > 1 && {item.count}} + {item.count > 1 && ({item.count})} {item.text.length > 60 ? item.text.slice(0, 58) + '...' : item.text} ))}