@@ -78,6 +78,95 @@ function createDiffFile(
7878 } ;
7979}
8080
81+ function lines ( ...values : string [ ] ) {
82+ return `${ values . join ( "\n" ) } \n` ;
83+ }
84+
85+ function createWindowingFiles ( count : number ) {
86+ return Array . from ( { length : count } , ( _ , index ) =>
87+ createDiffFile (
88+ `window-${ index + 1 } ` ,
89+ `window-${ index + 1 } .ts` ,
90+ lines ( `export const file${ index + 1 } = ${ index + 1 } ;` ) ,
91+ lines (
92+ `export const file${ index + 1 } = ${ index + 10 } ;` ,
93+ `export const file${ index + 1 } Extra = true;` ,
94+ ) ,
95+ ) ,
96+ ) ;
97+ }
98+
99+ function createMultiHunkDiffFile ( id : string , path : string ) {
100+ const before = lines (
101+ "export const line1 = 1;" ,
102+ "export const line2 = 2;" ,
103+ "export const line3 = 3;" ,
104+ "export const line4 = 4;" ,
105+ "export const line5 = 5;" ,
106+ "export const line6 = 6;" ,
107+ "export const line7 = 7;" ,
108+ "export const line8 = 8;" ,
109+ "export const line9 = 9;" ,
110+ "export const line10 = 10;" ,
111+ "export const line11 = 11;" ,
112+ "export const line12 = 12;" ,
113+ ) ;
114+ const after = lines (
115+ "export const line1 = 1;" ,
116+ "export const line2 = 200;" ,
117+ "export const line3 = 3;" ,
118+ "export const line4 = 4;" ,
119+ "export const line5 = 5;" ,
120+ "export const line6 = 6;" ,
121+ "export const line7 = 7;" ,
122+ "export const line8 = 8;" ,
123+ "export const line9 = 9;" ,
124+ "export const line10 = 10;" ,
125+ "export const line11 = 1100;" ,
126+ "export const line12 = 12;" ,
127+ ) ;
128+
129+ return createDiffFile ( id , path , before , after ) ;
130+ }
131+
132+ function createDiffPaneProps (
133+ files : DiffFile [ ] ,
134+ theme = resolveTheme ( "midnight" , null ) ,
135+ overrides : Partial < Parameters < typeof DiffPane > [ 0 ] > = { } ,
136+ ) {
137+ return {
138+ activeAnnotations : [ ] ,
139+ diffContentWidth : 72 ,
140+ dismissedAgentNoteIds : [ ] ,
141+ files,
142+ headerLabelWidth : 40 ,
143+ headerStatsWidth : 16 ,
144+ layout : "split" as const ,
145+ scrollRef : createRef ( ) ,
146+ selectedFileId : files [ 0 ] ?. id ,
147+ selectedHunkIndex : 0 ,
148+ separatorWidth : 68 ,
149+ showAgentNotes : false ,
150+ showLineNumbers : true ,
151+ showHunkHeaders : true ,
152+ wrapLines : false ,
153+ theme,
154+ width : 76 ,
155+ onDismissAgentNote : ( ) => { } ,
156+ onOpenAgentNotesAtHunk : ( ) => { } ,
157+ onSelectFile : ( ) => { } ,
158+ ...overrides ,
159+ } ;
160+ }
161+
162+ function settleDiffPane ( setup : Awaited < ReturnType < typeof testRender > > ) {
163+ return act ( async ( ) => {
164+ await setup . renderOnce ( ) ;
165+ await Bun . sleep ( 100 ) ;
166+ await setup . renderOnce ( ) ;
167+ } ) ;
168+ }
169+
81170function createBootstrap ( ) : AppBootstrap {
82171 return {
83172 input : {
@@ -256,6 +345,76 @@ describe("UI components", () => {
256345 expect ( frame . indexOf ( "alpha.ts" ) ) . toBeLessThan ( frame . indexOf ( "beta.ts" ) ) ;
257346 } ) ;
258347
348+ test ( "DiffPane scrolls a later selected file into view in the windowed path" , async ( ) => {
349+ const files = createWindowingFiles ( 6 ) ;
350+ const theme = resolveTheme ( "midnight" , null ) ;
351+ const props = createDiffPaneProps ( files , theme , {
352+ diffContentWidth : 88 ,
353+ selectedFileId : files [ 5 ] ?. id ,
354+ separatorWidth : 84 ,
355+ width : 92 ,
356+ } ) ;
357+ const setup = await testRender ( < DiffPane { ...props } /> , {
358+ width : 96 ,
359+ height : 12 ,
360+ } ) ;
361+
362+ try {
363+ await settleDiffPane ( setup ) ;
364+ const frame = setup . captureCharFrame ( ) ;
365+
366+ expect ( frame ) . toContain ( "window-6.ts" ) ;
367+ expect ( frame ) . toContain ( "export const file6Extra = true;" ) ;
368+ expect ( frame ) . not . toContain ( "window-1.ts" ) ;
369+ } finally {
370+ await act ( async ( ) => {
371+ setup . renderer . destroy ( ) ;
372+ } ) ;
373+ }
374+ } ) ;
375+
376+ test ( "DiffPane scrolls to the selected later hunk when hunk headers are hidden" , async ( ) => {
377+ const theme = resolveTheme ( "midnight" , null ) ;
378+ const files = [
379+ createDiffFile (
380+ "intro" ,
381+ "intro.ts" ,
382+ lines ( "export const intro = 1;" ) ,
383+ lines ( "export const intro = 2;" , "export const introExtra = true;" ) ,
384+ ) ,
385+ createMultiHunkDiffFile ( "target" , "target.ts" ) ,
386+ ] ;
387+ const props = createDiffPaneProps ( files , theme , {
388+ diffContentWidth : 96 ,
389+ headerLabelWidth : 48 ,
390+ selectedFileId : "target" ,
391+ selectedHunkIndex : 1 ,
392+ separatorWidth : 92 ,
393+ showHunkHeaders : false ,
394+ width : 100 ,
395+ } ) ;
396+ const setup = await testRender ( < DiffPane { ...props } /> , {
397+ width : 104 ,
398+ height : 12 ,
399+ } ) ;
400+
401+ try {
402+ await settleDiffPane ( setup ) ;
403+ const frame = setup . captureCharFrame ( ) ;
404+
405+ expect ( frame ) . toContain ( "11 - export const line11 = 11;" ) ;
406+ expect ( frame ) . toContain ( "11 + export const line11 = 1100;" ) ;
407+ expect ( frame ) . not . toContain ( "2 - export const line2 = 2;" ) ;
408+ expect ( frame ) . not . toContain ( "2 + export const line2 = 200;" ) ;
409+ expect ( frame ) . not . toContain ( "intro.ts" ) ;
410+ expect ( frame ) . not . toContain ( "@@ -1,3 +1,3 @@" ) ;
411+ } finally {
412+ await act ( async ( ) => {
413+ setup . renderer . destroy ( ) ;
414+ } ) ;
415+ }
416+ } ) ;
417+
259418 test ( "AgentCard removes top and bottom padding while keeping the footer inside the frame" , async ( ) => {
260419 const theme = resolveTheme ( "midnight" , null ) ;
261420 const frame = await captureFrame (
0 commit comments