@@ -14,6 +14,7 @@ const LIVE_CARD_TAIL_LIMIT = 4;
1414export type CardStreamItem < T > =
1515 | { kind : "spacer" ; rows : number ; key : string }
1616 | { kind : "card" ; card : T } ;
17+ type LiveRenderItem = { kind : "fold" ; count : number ; key : string } | { kind : "card" ; card : Card } ;
1718
1819/** Decide which cards render live vs collapse into a spacer, given the cached
1920 * heights and the current viewport position. Window is quantized to
@@ -67,8 +68,7 @@ export function CardStream({
6768 suppressLive && live . length > 0 && ! isFullySettled ( live [ live . length - 1 ] ! )
6869 ? live . slice ( 0 , - 1 )
6970 : live ;
70- const hiddenLive = Math . max ( 0 , visibleLive . length - LIVE_CARD_TAIL_LIMIT ) ;
71- const liveTail = hiddenLive > 0 ? visibleLive . slice ( - LIVE_CARD_TAIL_LIMIT ) : visibleLive ;
71+ const liveItems = selectLiveRenderItems ( visibleLive ) ;
7272
7373 return (
7474 < >
@@ -79,25 +79,59 @@ export function CardStream({
7979 </ Box >
8080 ) }
8181 </ Static >
82- { liveTail . length > 0 ? (
82+ { liveItems . length > 0 ? (
8383 < Box
8484 flexDirection = "column"
8585 flexShrink = { 1 }
8686 maxHeight = { maxRows !== undefined ? Math . max ( 1 , maxRows ) : undefined }
8787 overflow = "hidden"
8888 >
89- { hiddenLive > 0 ? < LiveFoldHint count = { hiddenLive } /> : null }
90- { liveTail . map ( ( card ) => (
91- < Box key = { card . id } flexDirection = "column" >
92- < CardRenderer card = { card } compact = { isFullySettled ( card ) } />
93- </ Box >
94- ) ) }
89+ { liveItems . map ( ( item ) =>
90+ item . kind === "fold" ? (
91+ < LiveFoldHint key = { item . key } count = { item . count } />
92+ ) : (
93+ < Box key = { item . card . id } flexDirection = "column" >
94+ < CardRenderer card = { item . card } compact = { isFullySettled ( item . card ) } />
95+ </ Box >
96+ ) ,
97+ ) }
9598 </ Box >
9699 ) : null }
97100 </ >
98101 ) ;
99102}
100103
104+ function selectLiveRenderItems ( cards : ReadonlyArray < Card > ) : LiveRenderItem [ ] {
105+ if ( cards . length <= LIVE_CARD_TAIL_LIMIT ) return cards . map ( ( card ) => ( { kind : "card" , card } ) ) ;
106+
107+ const tailStart = Math . max ( 0 , cards . length - LIVE_CARD_TAIL_LIMIT ) ;
108+ const lastUserIndex = findLastIndex ( cards , ( card ) => card . kind === "user" ) ;
109+
110+ if ( lastUserIndex >= 0 && lastUserIndex < tailStart ) {
111+ const items : LiveRenderItem [ ] = [ ] ;
112+ if ( lastUserIndex > 0 )
113+ items . push ( { kind : "fold" , count : lastUserIndex , key : "fold-before-user" } ) ;
114+ items . push ( { kind : "card" , card : cards [ lastUserIndex ] ! } ) ;
115+ const hiddenBetween = tailStart - lastUserIndex - 1 ;
116+ if ( hiddenBetween > 0 )
117+ items . push ( { kind : "fold" , count : hiddenBetween , key : "fold-after-user" } ) ;
118+ for ( const card of cards . slice ( tailStart ) ) items . push ( { kind : "card" , card } ) ;
119+ return items ;
120+ }
121+
122+ return [
123+ { kind : "fold" , count : tailStart , key : "fold-live-head" } ,
124+ ...cards . slice ( tailStart ) . map ( ( card ) => ( { kind : "card" as const , card } ) ) ,
125+ ] ;
126+ }
127+
128+ function findLastIndex < T > ( items : ReadonlyArray < T > , predicate : ( item : T ) => boolean ) : number {
129+ for ( let i = items . length - 1 ; i >= 0 ; i -- ) {
130+ if ( predicate ( items [ i ] ! ) ) return i ;
131+ }
132+ return - 1 ;
133+ }
134+
101135function LiveFoldHint ( { count } : { count : number } ) : React . ReactElement {
102136 return (
103137 < Box paddingLeft = { 2 } >
0 commit comments