@@ -3,11 +3,13 @@ import { Clipboard, ClipboardCheck } from "lucide-react";
3
3
import type { Language , PrismTheme } from "prism-react-renderer" ;
4
4
import { Highlight , Prism } from "prism-react-renderer" ;
5
5
import { forwardRef , ReactNode , useCallback , useEffect , useState } from "react" ;
6
+ import { TextWrapIcon } from "~/assets/icons/TextWrapIcon" ;
6
7
import { cn } from "~/utils/cn" ;
7
8
import { Button } from "../primitives/Buttons" ;
8
9
import { Dialog , DialogContent , DialogHeader , DialogTitle } from "../primitives/Dialog" ;
9
10
import { Paragraph } from "../primitives/Paragraph" ;
10
11
import { Tooltip , TooltipContent , TooltipProvider , TooltipTrigger } from "../primitives/Tooltip" ;
12
+ import { TextInlineIcon } from "~/assets/icons/TextInlineIcon" ;
11
13
12
14
//This is a fork of https://github.com/mantinedev/mantine/blob/master/src/mantine-prism/src/Prism/Prism.tsx
13
15
//it didn't support highlighting lines by dimming the rest of the code, or animations on the highlighting
@@ -31,6 +33,9 @@ type CodeBlockProps = {
31
33
/** Show copy to clipboard button */
32
34
showCopyButton ?: boolean ;
33
35
36
+ /** Show text wrapping button */
37
+ showTextWrapping ?: boolean ;
38
+
34
39
/** Display line numbers */
35
40
showLineNumbers ?: boolean ;
36
41
@@ -183,6 +188,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
183
188
(
184
189
{
185
190
showCopyButton = true ,
191
+ showTextWrapping = false ,
186
192
showLineNumbers = true ,
187
193
showOpenInModal = true ,
188
194
highlightedRanges,
@@ -202,6 +208,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
202
208
const [ copied , setCopied ] = useState ( false ) ;
203
209
const [ modalCopied , setModalCopied ] = useState ( false ) ;
204
210
const [ isModalOpen , setIsModalOpen ] = useState ( false ) ;
211
+ const [ isWrapped , setIsWrapped ] = useState ( false ) ;
205
212
206
213
const onCopied = useCallback (
207
214
( event : React . MouseEvent < HTMLButtonElement > ) => {
@@ -263,6 +270,25 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
263
270
showChrome ? "right-1.5 top-1.5" : "top-2.5"
264
271
) }
265
272
>
273
+ { showTextWrapping && (
274
+ < TooltipProvider >
275
+ < Tooltip disableHoverableContent >
276
+ < TooltipTrigger
277
+ onClick = { ( ) => setIsWrapped ( ! isWrapped ) }
278
+ className = "transition-colors focus-custom hover:cursor-pointer hover:text-text-bright"
279
+ >
280
+ { isWrapped ? (
281
+ < TextInlineIcon className = "size-4" />
282
+ ) : (
283
+ < TextWrapIcon className = "size-4" />
284
+ ) }
285
+ </ TooltipTrigger >
286
+ < TooltipContent side = "left" className = "text-xs" >
287
+ { isWrapped ? "Unwrap" : "Wrap" }
288
+ </ TooltipContent >
289
+ </ Tooltip >
290
+ </ TooltipProvider >
291
+ ) }
266
292
{ showCopyButton && (
267
293
< TooltipProvider >
268
294
< Tooltip open = { copied || mouseOver } disableHoverableContent >
@@ -311,16 +337,27 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
311
337
maxLineWidth = { maxLineWidth }
312
338
className = "px-2 py-3"
313
339
preClassName = "text-xs"
340
+ isWrapped = { isWrapped }
314
341
/>
315
342
) : (
316
343
< div
317
344
dir = "ltr"
318
- className = "overflow-auto px-2 py-3 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
345
+ className = { cn (
346
+ "px-2 py-3 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600" ,
347
+ ! isWrapped && "overflow-x-auto" ,
348
+ isWrapped && "overflow-y-auto"
349
+ ) }
319
350
style = { {
320
351
maxHeight,
321
352
} }
322
353
>
323
- < pre className = "relative mr-2 p-2 font-mono text-xs leading-relaxed" dir = "ltr" >
354
+ < pre
355
+ className = { cn (
356
+ "relative mr-2 p-2 font-mono text-xs leading-relaxed" ,
357
+ isWrapped && "[&_span]:whitespace-pre-wrap [&_span]:break-words"
358
+ ) }
359
+ dir = "ltr"
360
+ >
324
361
{ code }
325
362
</ pre >
326
363
</ div >
@@ -355,6 +392,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
355
392
maxLineWidth = { maxLineWidth }
356
393
className = "min-h-full"
357
394
preClassName = "text-sm"
395
+ isWrapped = { isWrapped }
358
396
/>
359
397
) : (
360
398
< div
@@ -410,6 +448,7 @@ type HighlightCodeProps = {
410
448
maxLineWidth ?: number ;
411
449
className ?: string ;
412
450
preClassName ?: string ;
451
+ isWrapped : boolean ;
413
452
} ;
414
453
415
454
function HighlightCode ( {
@@ -421,11 +460,11 @@ function HighlightCode({
421
460
maxLineWidth,
422
461
className,
423
462
preClassName,
463
+ isWrapped,
424
464
} : HighlightCodeProps ) {
425
465
const [ isLoaded , setIsLoaded ] = useState ( false ) ;
426
466
427
467
useEffect ( ( ) => {
428
- // This ensures the language definitions are loaded
429
468
Promise . all ( [
430
469
//@ts -ignore
431
470
import ( "prismjs/components/prism-json" ) ,
@@ -434,16 +473,23 @@ function HighlightCode({
434
473
] ) . then ( ( ) => setIsLoaded ( true ) ) ;
435
474
} , [ ] ) ;
436
475
476
+ const containerClasses = cn (
477
+ "px-3 py-3 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600" ,
478
+ ! isWrapped && "overflow-x-auto" ,
479
+ isWrapped && "overflow-y-auto" ,
480
+ className
481
+ ) ;
482
+
483
+ const preClasses = cn (
484
+ "relative mr-2 font-mono leading-relaxed" ,
485
+ preClassName ,
486
+ isWrapped && "[&_span]:whitespace-pre-wrap [&_span]:break-words"
487
+ ) ;
488
+
437
489
if ( ! isLoaded ) {
438
490
return (
439
- < div
440
- dir = "ltr"
441
- className = { cn (
442
- "overflow-auto px-3 py-3 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600" ,
443
- className
444
- ) }
445
- >
446
- < pre className = { cn ( "relative mr-2 font-mono leading-relaxed" , preClassName ) } > { code } </ pre >
491
+ < div dir = "ltr" className = { containerClasses } >
492
+ < pre className = { preClasses } > { code } </ pre >
447
493
</ div >
448
494
) ;
449
495
}
@@ -457,22 +503,8 @@ function HighlightCode({
457
503
getLineProps,
458
504
getTokenProps,
459
505
} ) => (
460
- < div
461
- dir = "ltr"
462
- className = { cn (
463
- "overflow-auto px-3 py-3 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600" ,
464
- className
465
- ) }
466
- >
467
- < pre
468
- className = { cn (
469
- "relative mr-2 font-mono leading-relaxed" ,
470
- inheritedClassName ,
471
- preClassName
472
- ) }
473
- style = { inheritedStyle }
474
- dir = "ltr"
475
- >
506
+ < div dir = "ltr" className = { containerClasses } >
507
+ < pre className = { cn ( preClasses , inheritedClassName ) } style = { inheritedStyle } dir = "ltr" >
476
508
{ tokens
477
509
. map ( ( line , index ) => {
478
510
if ( index === tokens . length - 1 && line . length === 1 && line [ 0 ] . content === "\n" ) {
@@ -495,7 +527,8 @@ function HighlightCode({
495
527
{ ...lineProps }
496
528
className = { cn (
497
529
"flex w-full justify-start transition-opacity duration-500" ,
498
- lineProps . className
530
+ lineProps . className ,
531
+ isWrapped && "flex-wrap"
499
532
) }
500
533
style = { {
501
534
opacity : shouldDim ? dimAmount : undefined ,
@@ -504,9 +537,10 @@ function HighlightCode({
504
537
>
505
538
{ showLineNumbers && (
506
539
< div
507
- className = {
508
- "mr-2 flex-none select-none text-right text-charcoal-500 transition-opacity duration-500"
509
- }
540
+ className = { cn (
541
+ "mr-2 flex-none select-none text-right text-charcoal-500 transition-opacity duration-500" ,
542
+ isWrapped && "sticky left-0"
543
+ ) }
510
544
style = { {
511
545
width : `calc(8 * ${ ( maxLineWidth as number ) / 16 } rem)` ,
512
546
} }
0 commit comments