@@ -62,7 +62,11 @@ import {
6262 resolveMarkGrid ,
6363 versionBadgeVisible ,
6464} from "../landing.js" ;
65- import { evictedRowsNotice , trimRetainedLog } from "../long-log.js" ;
65+ import {
66+ evictedRowsNotice ,
67+ trimRetainedLog ,
68+ unloadedHistoryNotice ,
69+ } from "../long-log.js" ;
6670import { destroySubtree } from "../teardown.js" ;
6771import {
6872 badgeCount ,
@@ -1076,26 +1080,38 @@ export function appendStreamRow(shell: AppShell, row: StreamRow): void {
10761080/**
10771081 * Paint the dropped-rows notice when older history exists on disk but the
10781082 * loaded window hydrated to at most the retention cap, so trim never ran.
1083+ *
1084+ * Does not bump `streamLogBase`. That field is the splice offset of
1085+ * `streamLog[0]`; faking it to 1 leaves the first retained row unreachable
1086+ * at absolute 0 and makes the notice claim one painted row was evicted.
10791087 */
10801088export function noteUnloadedHistory ( shell : AppShell ) : void {
10811089 if ( shell . observe !== null && shell . parentStreamLog !== null ) {
1082- if (
1083- ( shell . parentStreamLogBase ?? 0 ) === 0 &&
1084- shell . parentStreamLog . length > 0
1085- ) {
1086- shell . parentStreamLogBase = 1 ;
1090+ if ( shell . parentStreamLog . length > 0 ) {
1091+ shell . parentUnloadedHistory = true ;
10871092 }
10881093 return ;
10891094 }
1090- if ( shell . streamLogBase > 0 || shell . streamLog . length === 0 ) return ;
1091- shell . streamLogBase = 1 ;
1095+ if ( shell . streamLog . length === 0 ) return ;
1096+ shell . unloadedHistory = true ;
1097+ paintDroppedHistoryMarker ( shell ) ;
1098+ }
1099+
1100+ function droppedHistoryNotice ( shell : AppShell ) : string {
1101+ return shell . streamLogBase > 0
1102+ ? evictedRowsNotice ( shell . streamLogBase )
1103+ : unloadedHistoryNotice ( ) ;
1104+ }
1105+
1106+ function paintDroppedHistoryMarker ( shell : AppShell ) : void {
1107+ const content = droppedHistoryNotice ( shell ) ;
10921108 const marker = transcriptMarker ( shell ) ;
10931109 if ( marker instanceof TextRenderable ) {
1094- marker . content = evictedRowsNotice ( shell . streamLogBase ) ;
1110+ marker . content = content ;
10951111 return ;
10961112 }
10971113 const node = new TextRenderable ( shell . renderer as CliRenderer , {
1098- content : evictedRowsNotice ( shell . streamLogBase ) ,
1114+ content,
10991115 fg : UI . textDim ,
11001116 } ) ;
11011117 evictionMarkers . add ( node ) ;
@@ -1183,17 +1199,7 @@ function paintAppendStreamRow(shell: AppShell, row: StreamRow): void {
11831199 shell . transcript . remove ( evicted ) ;
11841200 destroySubtree ( evicted ) ;
11851201 }
1186- const marker = transcriptMarker ( shell ) ;
1187- if ( marker instanceof TextRenderable ) {
1188- marker . content = evictedRowsNotice ( shell . streamLogBase ) ;
1189- } else {
1190- const node = new TextRenderable ( shell . renderer as CliRenderer , {
1191- content : evictedRowsNotice ( shell . streamLogBase ) ,
1192- fg : UI . textDim ,
1193- } ) ;
1194- evictionMarkers . add ( node ) ;
1195- shell . transcript . add ( node , 1 ) ;
1196- }
1202+ paintDroppedHistoryMarker ( shell ) ;
11971203 }
11981204
11991205 const index = shell . streamLog . length - 1 ;
@@ -1251,6 +1257,7 @@ export function clearTranscript(shell: AppShell): void {
12511257 shell . observe = null ;
12521258 shell . parentStreamLog = null ;
12531259 shell . parentStreamLogBase = null ;
1260+ shell . parentUnloadedHistory = null ;
12541261 let guard = 4 ;
12551262 while ( guard -- > 0 && focusOwner ( shell . focus ) === "observe" ) {
12561263 shell . focus = popFocus ( shell . focus ) ;
@@ -1264,9 +1271,11 @@ export function clearTranscript(shell: AppShell): void {
12641271 }
12651272 shell . streamLog . length = 0 ;
12661273 shell . streamLogBase = 0 ;
1274+ shell . unloadedHistory = false ;
12671275 shell . lineCount = 0 ;
12681276 shell . parentStreamLog = null ;
12691277 shell . parentStreamLogBase = null ;
1278+ shell . parentUnloadedHistory = null ;
12701279 repaintTranscriptWindow ( shell ) ;
12711280 paintChrome ( shell ) ;
12721281}
@@ -1350,9 +1359,11 @@ export function repaintTranscriptWindow(shell: AppShell): void {
13501359
13511360 // Rows evicted by the retention cap are gone for good, not just scrolled
13521361 // past — say so, or the boundary reads as the true start of history.
1353- if ( shell . streamLogBase > 0 ) {
1362+ // Truncated resume can also leave older history on disk without splicing
1363+ // any painted row; that path must not fake `streamLogBase`.
1364+ if ( shell . streamLogBase > 0 || shell . unloadedHistory ) {
13541365 const marker = new TextRenderable ( shell . renderer as CliRenderer , {
1355- content : evictedRowsNotice ( shell . streamLogBase ) ,
1366+ content : droppedHistoryNotice ( shell ) ,
13561367 fg : UI . textDim ,
13571368 } ) ;
13581369 evictionMarkers . add ( marker ) ;
0 commit comments