@@ -3,7 +3,13 @@ import { FileDiff, type FileDiffMetadata, Virtualizer } from "@pierre/diffs/reac
33import { useQuery } from "@tanstack/react-query" ;
44import { useNavigate , useParams , useSearch } from "@tanstack/react-router" ;
55import { ThreadId , type TurnId } from "@t3tools/contracts" ;
6- import { ChevronLeftIcon , ChevronRightIcon , Columns2Icon , Rows3Icon } from "lucide-react" ;
6+ import {
7+ ChevronLeftIcon ,
8+ ChevronRightIcon ,
9+ Columns2Icon ,
10+ Rows3Icon ,
11+ TextWrapIcon ,
12+ } from "lucide-react" ;
713import {
814 type WheelEvent as ReactWheelEvent ,
915 useCallback ,
@@ -162,15 +168,18 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) {
162168 const { resolvedTheme } = useTheme ( ) ;
163169 const { settings } = useAppSettings ( ) ;
164170 const [ diffRenderMode , setDiffRenderMode ] = useState < DiffRenderMode > ( "stacked" ) ;
171+ const [ diffWordWrap , setDiffWordWrap ] = useState ( settings . diffWordWrap ) ;
165172 const patchViewportRef = useRef < HTMLDivElement > ( null ) ;
166173 const turnStripRef = useRef < HTMLDivElement > ( null ) ;
174+ const previousDiffOpenRef = useRef ( false ) ;
167175 const [ canScrollTurnStripLeft , setCanScrollTurnStripLeft ] = useState ( false ) ;
168176 const [ canScrollTurnStripRight , setCanScrollTurnStripRight ] = useState ( false ) ;
169177 const routeThreadId = useParams ( {
170178 strict : false ,
171179 select : ( params ) => ( params . threadId ? ThreadId . makeUnsafe ( params . threadId ) : null ) ,
172180 } ) ;
173181 const diffSearch = useSearch ( { strict : false , select : ( search ) => parseDiffRouteSearch ( search ) } ) ;
182+ const diffOpen = diffSearch . diff === "1" ;
174183 const activeThreadId = routeThreadId ;
175184 const activeThread = useStore ( ( store ) =>
176185 activeThreadId ? store . threads . find ( ( thread ) => thread . id === activeThreadId ) : undefined ,
@@ -293,6 +302,13 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) {
293302 ) ;
294303 } , [ renderablePatch ] ) ;
295304
305+ useEffect ( ( ) => {
306+ if ( diffOpen && ! previousDiffOpenRef . current ) {
307+ setDiffWordWrap ( settings . diffWordWrap ) ;
308+ }
309+ previousDiffOpenRef . current = diffOpen ;
310+ } , [ diffOpen , settings . diffWordWrap ] ) ;
311+
296312 useEffect ( ( ) => {
297313 if ( ! selectedFilePath || ! patchViewportRef . current ) {
298314 return ;
@@ -490,25 +506,39 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) {
490506 ) ) }
491507 </ div >
492508 </ div >
493- < ToggleGroup
494- className = "shrink-0 [-webkit-app-region:no-drag]"
495- variant = "outline"
496- size = "xs"
497- value = { [ diffRenderMode ] }
498- onValueChange = { ( value ) => {
499- const next = value [ 0 ] ;
500- if ( next === "stacked" || next === "split" ) {
501- setDiffRenderMode ( next ) ;
502- }
503- } }
504- >
505- < Toggle aria-label = "Stacked diff view" value = "stacked" >
506- < Rows3Icon className = "size-3" />
507- </ Toggle >
508- < Toggle aria-label = "Split diff view" value = "split" >
509- < Columns2Icon className = "size-3" />
509+ < div className = "flex shrink-0 items-center gap-1 [-webkit-app-region:no-drag]" >
510+ < ToggleGroup
511+ className = "shrink-0"
512+ variant = "outline"
513+ size = "xs"
514+ value = { [ diffRenderMode ] }
515+ onValueChange = { ( value ) => {
516+ const next = value [ 0 ] ;
517+ if ( next === "stacked" || next === "split" ) {
518+ setDiffRenderMode ( next ) ;
519+ }
520+ } }
521+ >
522+ < Toggle aria-label = "Stacked diff view" value = "stacked" >
523+ < Rows3Icon className = "size-3" />
524+ </ Toggle >
525+ < Toggle aria-label = "Split diff view" value = "split" >
526+ < Columns2Icon className = "size-3" />
527+ </ Toggle >
528+ </ ToggleGroup >
529+ < Toggle
530+ aria-label = { diffWordWrap ? "Disable diff line wrapping" : "Enable diff line wrapping" }
531+ title = { diffWordWrap ? "Disable line wrapping" : "Enable line wrapping" }
532+ variant = "outline"
533+ size = "xs"
534+ pressed = { diffWordWrap }
535+ onPressedChange = { ( pressed ) => {
536+ setDiffWordWrap ( Boolean ( pressed ) ) ;
537+ } }
538+ >
539+ < TextWrapIcon className = "size-3" />
510540 </ Toggle >
511- </ ToggleGroup >
541+ </ div >
512542 </ >
513543 ) ;
514544
@@ -582,6 +612,7 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) {
582612 options = { {
583613 diffStyle : diffRenderMode === "split" ? "split" : "unified" ,
584614 lineDiffType : "none" ,
615+ overflow : diffWordWrap ? "wrap" : "scroll" ,
585616 theme : resolveDiffThemeName ( resolvedTheme ) ,
586617 themeType : resolvedTheme as DiffThemeType ,
587618 unsafeCSS : DIFF_PANEL_UNSAFE_CSS ,
@@ -595,7 +626,14 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) {
595626 < div className = "h-full overflow-auto p-2" >
596627 < div className = "space-y-2" >
597628 < p className = "text-[11px] text-muted-foreground/75" > { renderablePatch . reason } </ p >
598- < pre className = "max-h-[72vh] overflow-auto rounded-md border border-border/70 bg-background/70 p-3 font-mono text-[11px] leading-relaxed text-muted-foreground/90" >
629+ < pre
630+ className = { cn (
631+ "max-h-[72vh] rounded-md border border-border/70 bg-background/70 p-3 font-mono text-[11px] leading-relaxed text-muted-foreground/90" ,
632+ diffWordWrap
633+ ? "overflow-auto whitespace-pre-wrap wrap-break-word"
634+ : "overflow-auto" ,
635+ ) }
636+ >
599637 { renderablePatch . text }
600638 </ pre >
601639 </ div >
0 commit comments