1- // Inspired by Chatbot-UI and modified to fit the needs of this project
2- // @see https://github.com/mckaywrigley/chatbot-ui/blob/main/components/Markdown/CodeBlock.tsx
3-
41'use client'
52
6- import { FC , memo } from 'react'
3+ import { type FC , memo } from 'react'
74import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
85import { coldarkDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'
9-
106import { Button } from '@/components/ui/button'
117import { IconCheck , IconCopy , IconDownload } from '@/components/ui/icons'
128import { useCopyToClipboard } from '@/lib/hooks/use-copy-to-clipboard'
9+ import { cn } from '@/lib/utils'
1310
1411interface Props {
1512 language : string
@@ -43,12 +40,19 @@ export const programmingLanguages: languageMap = {
4340 shell : '.sh' ,
4441 sql : '.sql' ,
4542 html : '.html' ,
46- css : '.css'
47- // add more file extensions here, make sure the key is same as language prop in CodeBlock.tsx component
43+ css : '.css' ,
44+ solidity : '.sol' ,
45+ cairo : '.cairo' ,
46+ json : '.json' ,
47+ yaml : '.yaml' ,
48+ xml : '.xml' ,
49+ markdown : '.md' ,
50+ plaintext : '.txt' ,
51+ react : '.jsx'
4852}
4953
50- export const generateRandomString = ( length : number , lowercase = false ) => {
51- const chars = 'ABCDEFGHJKLMNPQRSTUVWXY3456789' // excluding similar looking characters like Z, 2, I, 1, O, 0
54+ const generateRandomString = ( length : number , lowercase = false ) => {
55+ const chars = 'ABCDEFGHJKLMNPQRSTUVWXY3456789'
5256 let result = ''
5357 for ( let i = 0 ; i < length ; i ++ ) {
5458 result += chars . charAt ( Math . floor ( Math . random ( ) * chars . length ) )
@@ -64,14 +68,10 @@ const CodeBlock: FC<Props> = memo(({ language, value }) => {
6468 return
6569 }
6670 const fileExtension = programmingLanguages [ language ] || '.file'
67- const suggestedFileName = `file-${ generateRandomString (
68- 3 ,
69- true
70- ) } ${ fileExtension } `
71+ const suggestedFileName = `file-${ generateRandomString ( 3 , true ) } ${ fileExtension } `
7172 const fileName = window . prompt ( 'Enter file name' , suggestedFileName )
7273
7374 if ( ! fileName ) {
74- // User pressed cancel on prompt.
7575 return
7676 }
7777
@@ -93,56 +93,76 @@ const CodeBlock: FC<Props> = memo(({ language, value }) => {
9393 }
9494
9595 return (
96- < div className = "relative w-full font-sans codeblock bg-zinc-950" >
97- < div className = "flex items-center justify-between w-full px-6 py-2 pr-4 bg-zinc-800 text-zinc-100" >
98- < span className = "text-xs lowercase" > { language } </ span >
99- < div className = "flex items-center space-x-1" >
96+ < div className = "relative w-full overflow-hidden font-sans text-sm rounded-md sm:text-base" >
97+ < div
98+ className = { cn (
99+ 'flex items-center justify-between w-full bg-zinc-800 text-zinc-100' ,
100+ 'px-2 py-1.5 sm:px-6 sm:py-2'
101+ ) }
102+ >
103+ < span className = "text-[11px] sm:text-xs lowercase" > { language } </ span >
104+ < div className = "flex items-center gap-0.5 sm:gap-1" >
100105 < Button
101106 variant = "ghost"
102- className = "hover:bg-zinc-800 focus-visible:ring-1 focus-visible:ring-slate-700 focus-visible:ring-offset-0"
107+ className = "h-7 w-7 sm:h-8 sm:w-8 hover:bg-zinc-800 focus-visible:ring-1 focus-visible:ring-slate-700 focus-visible:ring-offset-0"
103108 onClick = { downloadAsFile }
104109 size = "icon"
105110 >
106- < IconDownload />
111+ < IconDownload className = "h-3.5 w-3.5 sm:h-4 sm:w-4" />
107112 < span className = "sr-only" > Download</ span >
108113 </ Button >
109114 < Button
110115 variant = "ghost"
111116 size = "icon"
112- className = "text-xs hover:bg-zinc-800 focus-visible:ring-1 focus-visible:ring-slate-700 focus-visible:ring-offset-0"
117+ className = "h-7 w-7 sm:h-8 sm:w-8 hover:bg-zinc-800 focus-visible:ring-1 focus-visible:ring-slate-700 focus-visible:ring-offset-0"
113118 onClick = { onCopy }
114119 >
115- { isCopied ? < IconCheck /> : < IconCopy /> }
120+ { isCopied ? (
121+ < IconCheck className = "h-3.5 w-3.5 sm:h-4 sm:w-4" />
122+ ) : (
123+ < IconCopy className = "h-3.5 w-3.5 sm:h-4 sm:w-4" />
124+ ) }
116125 < span className = "sr-only" > Copy code</ span >
117126 </ Button >
118127 </ div >
119128 </ div >
120- < SyntaxHighlighter
121- language = { language }
122- style = { coldarkDark }
123- PreTag = "div"
124- showLineNumbers
125- customStyle = { {
126- margin : 0 ,
127- width : '100%' ,
128- background : 'transparent' ,
129- padding : '1.5rem 1rem'
130- } }
131- lineNumberStyle = { {
132- userSelect : 'none'
133- } }
134- codeTagProps = { {
135- style : {
136- fontSize : '0.9rem' ,
137- fontFamily : 'var(--font-mono)'
138- }
139- } }
140- >
141- { value }
142- </ SyntaxHighlighter >
129+ < div className = "relative w-full overflow-auto text-xs sm:text-sm" >
130+ < SyntaxHighlighter
131+ language = { language }
132+ style = { coldarkDark }
133+ PreTag = "div"
134+ showLineNumbers
135+ customStyle = { {
136+ margin : 0 ,
137+ width : '100%' ,
138+ background : 'transparent' ,
139+ padding : '0.75rem 0.25rem'
140+ } }
141+ lineNumberStyle = { {
142+ minWidth : '2em' ,
143+ paddingRight : '0.75em' ,
144+ userSelect : 'none' ,
145+ opacity : 0.5 ,
146+ fontSize : '11px'
147+ } }
148+ codeTagProps = { {
149+ style : {
150+ fontFamily : 'var(--font-mono)' ,
151+ fontSize : 'inherit' ,
152+ lineHeight : '1.4'
153+ }
154+ } }
155+ className = "text-xs sm:text-sm"
156+ wrapLines
157+ wrapLongLines
158+ >
159+ { value }
160+ </ SyntaxHighlighter >
161+ </ div >
143162 </ div >
144163 )
145164} )
165+
146166CodeBlock . displayName = 'CodeBlock'
147167
148168export { CodeBlock }
0 commit comments