@@ -54,6 +54,8 @@ import { useUiStateStore } from "../../uiStateStore";
5454import { COLLAPSED_SIDEBAR_TITLEBAR_INSET_CLASS } from "../../workspaceTitlebar" ;
5555import { ProjectFavicon , ProjectFaviconFallback } from "../ProjectFavicon" ;
5656import {
57+ SETTLED_TAIL_INITIAL_COUNT ,
58+ SETTLED_TAIL_PAGE_COUNT ,
5759 archiveSelectedThreadEntries ,
5860 buildSidebarV2ThreadContextMenuItems ,
5961 isThreadSettledForDisplay ,
@@ -81,10 +83,11 @@ import {
8183 boardWorktreeKey ,
8284 buildBoardColumns ,
8385 buildBoardProjectFilterPredicate ,
86+ countBoardColumnThreads ,
8487 deriveBoardColumn ,
8588 parseBoardWorktreeGroupDragId ,
8689 resolveBoardDropIntent ,
87- type BoardColumnItem ,
90+ sliceBoardSettledItems ,
8891 type BoardDropIntent ,
8992} from "./Board.logic" ;
9093import { BoardCard , BoardCardDragOverlay } from "./BoardCard" ;
@@ -105,13 +108,6 @@ interface BoardThreadGitContext {
105108 readonly gitStatusPending : boolean ;
106109}
107110
108- function countBoardColumnThreads < T > ( items : readonly BoardColumnItem < T > [ ] ) : number {
109- return items . reduce (
110- ( count , item ) => count + ( item . kind === "thread" ? 1 : item . threads . length ) ,
111- 0 ,
112- ) ;
113- }
114-
115111/** Error toast for a failed thread action; interruptions and successes are silent. */
116112function reportThreadActionFailure ( result : AtomCommandResult < unknown , unknown > , title : string ) {
117113 if ( result . _tag !== "Failure" || isAtomCommandInterrupted ( result ) ) {
@@ -429,6 +425,24 @@ function BoardContent() {
429425 ] ,
430426 ) ;
431427
428+ // Settled tail renders in pages, mirroring SidebarV2: expansion resets when
429+ // the project filter changes so a scope flip never inherits a deep page.
430+ const [ settledVisibleCount , setSettledVisibleCount ] = useState ( SETTLED_TAIL_INITIAL_COUNT ) ;
431+ const settledResetKey = storedProjectFilter ?? "all" ;
432+ const lastSettledResetKeyRef = useRef ( settledResetKey ) ;
433+ if ( lastSettledResetKeyRef . current !== settledResetKey ) {
434+ lastSettledResetKeyRef . current = settledResetKey ;
435+ setSettledVisibleCount ( SETTLED_TAIL_INITIAL_COUNT ) ;
436+ }
437+ const settledTail = useMemo (
438+ ( ) => sliceBoardSettledItems ( columns . settled , settledVisibleCount ) ,
439+ [ columns , settledVisibleCount ] ,
440+ ) ;
441+ const showMoreSettled = useCallback (
442+ ( ) => setSettledVisibleCount ( ( count ) => count + SETTLED_TAIL_PAGE_COUNT ) ,
443+ [ ] ,
444+ ) ;
445+
432446 const dragClickGuard = useMemo ( ( ) => createBoardDragClickGuard ( ) , [ ] ) ;
433447 useEffect ( ( ) => ( ) => dragClickGuard . dispose ( ) , [ dragClickGuard ] ) ;
434448 const [ activeDragId , setActiveDragId ] = useState < string | null > ( null ) ;
@@ -792,55 +806,71 @@ function BoardContent() {
792806 data-testid = "board-column-row"
793807 className = "flex h-full w-full min-w-max justify-center gap-3 p-3 sm:p-4"
794808 >
795- { BOARD_COLUMN_IDS . map ( ( columnId ) => (
796- < BoardColumn
797- key = { columnId }
798- columnId = { columnId }
799- count = { countBoardColumnThreads ( columns [ columnId ] ) }
800- >
801- { columns [ columnId ] . map ( ( item ) => {
802- const renderCard = ( thread : SidebarThreadSummary ) => {
803- const gitContext = getThreadGitContext ( thread ) ;
804- const threadKey = scopedThreadKey (
805- scopeThreadRef ( thread . environmentId , thread . id ) ,
806- ) ;
809+ { BOARD_COLUMN_IDS . map ( ( columnId ) => {
810+ const items = columnId === "settled" ? settledTail . visibleItems : columns [ columnId ] ;
811+ return (
812+ < BoardColumn
813+ key = { columnId }
814+ columnId = { columnId }
815+ count = { countBoardColumnThreads ( columns [ columnId ] ) }
816+ >
817+ { items . map ( ( item ) => {
818+ const renderCard = ( thread : SidebarThreadSummary ) => {
819+ const gitContext = getThreadGitContext ( thread ) ;
820+ const threadKey = scopedThreadKey (
821+ scopeThreadRef ( thread . environmentId , thread . id ) ,
822+ ) ;
823+ return (
824+ < BoardCard
825+ key = { threadKey }
826+ thread = { thread }
827+ project = { gitContext . project }
828+ gitStatus = { gitContext . gitStatus }
829+ gitStatusPending = { gitContext . gitStatusPending }
830+ isSettled = { settledThreadKeys . has ( threadKey ) }
831+ onOpenThread = { handleOpenThread }
832+ onShowContextMenu = { showThreadContextMenu }
833+ dragClickGuard = { dragClickGuard }
834+ />
835+ ) ;
836+ } ;
837+ if ( item . kind === "thread" ) {
838+ return renderCard ( item . thread ) ;
839+ }
840+ // buildBoardColumns only emits groups with >= 2 members.
841+ const mostRecentThread = item . threads [ 0 ] ! ;
807842 return (
808- < BoardCard
809- key = { threadKey }
810- thread = { thread }
811- project = { gitContext . project }
812- gitStatus = { gitContext . gitStatus }
813- gitStatusPending = { gitContext . gitStatusPending }
814- isSettled = { settledThreadKeys . has ( threadKey ) }
815- onOpenThread = { handleOpenThread }
816- onShowContextMenu = { showThreadContextMenu }
843+ < BoardWorktreeGroup
844+ key = { item . worktreeKey }
845+ worktreeKey = { item . worktreeKey }
846+ threadRefs = { item . threads . map ( ( thread ) =>
847+ scopeThreadRef ( thread . environmentId , thread . id ) ,
848+ ) }
849+ worktreePath = { mostRecentThread . worktreePath ?? "" }
850+ branch = { mostRecentThread . branch }
851+ mostRecentCard = { renderCard ( mostRecentThread ) }
817852 dragClickGuard = { dragClickGuard }
818- />
853+ >
854+ { item . threads . slice ( 1 ) . map ( renderCard ) }
855+ </ BoardWorktreeGroup >
819856 ) ;
820- } ;
821- if ( item . kind === "thread" ) {
822- return renderCard ( item . thread ) ;
823- }
824- // buildBoardColumns only emits groups with >= 2 members.
825- const mostRecentThread = item . threads [ 0 ] ! ;
826- return (
827- < BoardWorktreeGroup
828- key = { item . worktreeKey }
829- worktreeKey = { item . worktreeKey }
830- threadRefs = { item . threads . map ( ( thread ) =>
831- scopeThreadRef ( thread . environmentId , thread . id ) ,
832- ) }
833- worktreePath = { mostRecentThread . worktreePath ?? "" }
834- branch = { mostRecentThread . branch }
835- mostRecentCard = { renderCard ( mostRecentThread ) }
836- dragClickGuard = { dragClickGuard }
857+ } ) }
858+ { columnId === "settled" && settledTail . hiddenThreadCount > 0 ? (
859+ < button
860+ type = "button"
861+ onClick = { showMoreSettled }
862+ data-testid = "board-settled-show-more"
863+ className = "mt-1 flex h-[30px] w-full items-center justify-center gap-1.5 rounded-md border border-dashed border-border font-mono text-[11px] text-muted-foreground transition-colors hover:border-solid hover:border-input hover:bg-background/45 hover:text-foreground dark:border-white/15 dark:hover:border-white/30 dark:hover:bg-transparent"
837864 >
838- { item . threads . slice ( 1 ) . map ( renderCard ) }
839- </ BoardWorktreeGroup >
840- ) ;
841- } ) }
842- </ BoardColumn >
843- ) ) }
865+ Show { Math . min ( settledTail . hiddenThreadCount , SETTLED_TAIL_PAGE_COUNT ) } more
866+ < span className = "text-muted-foreground/50" >
867+ ({ settledTail . hiddenThreadCount } settled hidden)
868+ </ span >
869+ </ button >
870+ ) : null }
871+ </ BoardColumn >
872+ ) ;
873+ } ) }
844874 </ div >
845875 </ div >
846876 { activeDragId !== null ? (
0 commit comments