@@ -121,6 +121,24 @@ function createWrapBootstrap(): AppBootstrap {
121121 } ;
122122}
123123
124+ function createEmptyDiffFile ( type : "rename-pure" | "new" | "deleted" ) : DiffFile {
125+ return {
126+ id : `empty:${ type } ` ,
127+ path : `${ type } .ts` ,
128+ patch : "" ,
129+ language : "typescript" ,
130+ stats : {
131+ additions : 0 ,
132+ deletions : 0 ,
133+ } ,
134+ metadata : {
135+ hunks : [ ] ,
136+ type,
137+ } as never ,
138+ agent : null ,
139+ } ;
140+ }
141+
124142async function captureFrame ( node : ReactNode , width = 120 , height = 24 ) {
125143 const setup = await testRender ( node , { width, height } ) ;
126144
@@ -486,6 +504,106 @@ describe("UI components", () => {
486504 expect ( frame ) . toContain ( "1 + export const alpha = 2;" ) ;
487505 } ) ;
488506
507+ test ( "PierreDiffView renders stack-mode wrapped continuation rows" , async ( ) => {
508+ const file = createWrapBootstrap ( ) . changeset . files [ 0 ] ! ;
509+ const theme = resolveTheme ( "midnight" , null ) ;
510+ const frame = await captureFrame (
511+ < PierreDiffView
512+ file = { file }
513+ layout = "stack"
514+ theme = { theme }
515+ width = { 48 }
516+ selectedHunkIndex = { 0 }
517+ wrapLines = { true }
518+ scrollable = { false }
519+ /> ,
520+ 52 ,
521+ 18 ,
522+ ) ;
523+
524+ expect ( frame ) . toContain ( "1 - export const message = 'short';" ) ;
525+ expect ( frame ) . toContain ( "1 + export const message = 'this is a very l" ) ;
526+ expect ( frame ) . toContain ( "ong wrapped line for diff rendering cove" ) ;
527+ expect ( frame ) . toContain ( "rage';" ) ;
528+ } ) ;
529+
530+ test ( "PierreDiffView anchors range-less notes to the first visible row when hunk headers are hidden" , async ( ) => {
531+ const file = createDiffFile (
532+ "note-fallback" ,
533+ "note-fallback.ts" ,
534+ "export const value = 1;\n" ,
535+ "export const value = 2;\nexport const added = true;\n" ,
536+ ) ;
537+ const theme = resolveTheme ( "midnight" , null ) ;
538+ const frame = await captureFrame (
539+ < PierreDiffView
540+ file = { file }
541+ layout = "split"
542+ theme = { theme }
543+ width = { 88 }
544+ selectedHunkIndex = { 0 }
545+ visibleAgentNotes = { [
546+ {
547+ id : "note:ungrounded" ,
548+ annotation : {
549+ summary : "Ungrounded note" ,
550+ rationale : "Falls back to the first visible row." ,
551+ } ,
552+ } ,
553+ ] }
554+ showHunkHeaders = { false }
555+ scrollable = { false }
556+ /> ,
557+ 92 ,
558+ 18 ,
559+ ) ;
560+
561+ expect ( frame ) . not . toContain ( "@@ -1,1 +1,2 @@" ) ;
562+ expect ( frame ) . toContain ( "Ungrounded note" ) ;
563+ expect ( frame ) . toContain ( "Falls back to the first visible row." ) ;
564+ expect ( frame ) . toContain ( "note-fallback.ts" ) ;
565+ expect ( frame ) . toContain ( "1 - export const value = 1;" ) ;
566+ } ) ;
567+
568+ test ( "PierreDiffView shows contextual messages when there is no selected file or no textual hunks" , async ( ) => {
569+ const theme = resolveTheme ( "midnight" , null ) ;
570+
571+ const noFileFrame = await captureFrame (
572+ < PierreDiffView file = { undefined } layout = "split" theme = { theme } width = { 72 } selectedHunkIndex = { 0 } scrollable = { false } /> ,
573+ 76 ,
574+ 6 ,
575+ ) ;
576+ expect ( noFileFrame ) . toContain ( "No file selected." ) ;
577+
578+ const renameOnlyFrame = await captureFrame (
579+ < PierreDiffView
580+ file = { createEmptyDiffFile ( "rename-pure" ) }
581+ layout = "split"
582+ theme = { theme }
583+ width = { 72 }
584+ selectedHunkIndex = { 0 }
585+ scrollable = { false }
586+ /> ,
587+ 76 ,
588+ 6 ,
589+ ) ;
590+ expect ( renameOnlyFrame ) . toContain ( "This change only renames the file." ) ;
591+
592+ const newFileFrame = await captureFrame (
593+ < PierreDiffView file = { createEmptyDiffFile ( "new" ) } layout = "split" theme = { theme } width = { 72 } selectedHunkIndex = { 0 } scrollable = { false } /> ,
594+ 76 ,
595+ 6 ,
596+ ) ;
597+ expect ( newFileFrame ) . toContain ( "The file is marked as new." ) ;
598+
599+ const deletedFileFrame = await captureFrame (
600+ < PierreDiffView file = { createEmptyDiffFile ( "deleted" ) } layout = "split" theme = { theme } width = { 72 } selectedHunkIndex = { 0 } scrollable = { false } /> ,
601+ 76 ,
602+ 6 ,
603+ ) ;
604+ expect ( deletedFileFrame ) . toContain ( "The file is marked as deleted." ) ;
605+ } ) ;
606+
489607 test ( "PierreDiffView reuses highlighted rows after unmounting and remounting a file section" , async ( ) => {
490608 const file = createDiffFile (
491609 "cache" ,
0 commit comments