@@ -54,7 +54,10 @@ import {
5454} from "@t3tools/client-runtime" ;
5555import { Link , useLocation , useNavigate , useParams , useRouter } from "@tanstack/react-router" ;
5656import {
57+ MAX_SIDEBAR_THREAD_PREVIEW_COUNT ,
58+ MIN_SIDEBAR_THREAD_PREVIEW_COUNT ,
5759 type SidebarProjectSortOrder ,
60+ type SidebarThreadPreviewCount ,
5861 type SidebarThreadSortOrder ,
5962} from "@t3tools/contracts/settings" ;
6063import { usePrimaryEnvironmentId } from "../environments/primary" ;
@@ -128,6 +131,13 @@ import {
128131 MenuSeparator ,
129132 MenuTrigger ,
130133} from "./ui/menu" ;
134+ import {
135+ NumberField ,
136+ NumberFieldDecrement ,
137+ NumberFieldGroup ,
138+ NumberFieldIncrement ,
139+ NumberFieldInput ,
140+ } from "./ui/number-field" ;
131141import { Select , SelectItem , SelectPopup , SelectTrigger , SelectValue } from "./ui/select" ;
132142import { Tooltip , TooltipPopup , TooltipTrigger } from "./ui/tooltip" ;
133143import {
@@ -186,7 +196,6 @@ import {
186196 type SidebarProjectSnapshot ,
187197} from "../sidebarProjectGrouping" ;
188198import { SidebarProviderUpdatePill } from "./sidebar/SidebarProviderUpdatePill" ;
189- const THREAD_PREVIEW_LIMIT = 6 ;
190199const SIDEBAR_SORT_LABELS : Record < SidebarProjectSortOrder , string > = {
191200 updated_at : "Last user message" ,
192201 created_at : "Created at" ,
@@ -207,6 +216,13 @@ const PROJECT_GROUPING_MODE_LABELS: Record<SidebarProjectGroupingMode, string> =
207216 separate : "Keep separate" ,
208217} ;
209218
219+ function clampSidebarThreadPreviewCount ( value : number ) : SidebarThreadPreviewCount {
220+ return Math . min (
221+ MAX_SIDEBAR_THREAD_PREVIEW_COUNT ,
222+ Math . max ( MIN_SIDEBAR_THREAD_PREVIEW_COUNT , value ) ,
223+ ) as SidebarThreadPreviewCount ;
224+ }
225+
210226function formatProjectMemberActionLabel (
211227 member : SidebarProjectGroupMember ,
212228 groupedProjectCount : number ,
@@ -936,6 +952,9 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
936952 sidebarProjectGroupingOverrides : settings . sidebarProjectGroupingOverrides ,
937953 } ) ) ;
938954 const { updateSettings } = useUpdateSettings ( ) ;
955+ const sidebarThreadPreviewCount = useSettings < SidebarThreadPreviewCount > (
956+ ( settings ) => settings . sidebarThreadPreviewCount ,
957+ ) ;
939958 const router = useRouter ( ) ;
940959 const { isMobile, setOpenMobile } = useSidebar ( ) ;
941960 const markThreadUnread = useUiStateStore ( ( state ) => state . markThreadUnread ) ;
@@ -1159,11 +1178,11 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
11591178 } ,
11601179 } ) ;
11611180 } ;
1162- const hasOverflowingThreads = visibleProjectThreads . length > THREAD_PREVIEW_LIMIT ;
1181+ const hasOverflowingThreads = visibleProjectThreads . length > sidebarThreadPreviewCount ;
11631182 const previewThreads =
11641183 isThreadListExpanded || ! hasOverflowingThreads
11651184 ? visibleProjectThreads
1166- : visibleProjectThreads . slice ( 0 , THREAD_PREVIEW_LIMIT ) ;
1185+ : visibleProjectThreads . slice ( 0 , sidebarThreadPreviewCount ) ;
11671186 const visibleThreadKeys = new Set (
11681187 [ ...previewThreads , ...( pinnedCollapsedThread ? [ pinnedCollapsedThread ] : [ ] ) ] . map ( ( thread ) =>
11691188 scopedThreadKey ( scopeThreadRef ( thread . environmentId , thread . id ) ) ,
@@ -1192,6 +1211,7 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
11921211 pinnedCollapsedThread ,
11931212 projectExpanded ,
11941213 projectThreads ,
1214+ sidebarThreadPreviewCount ,
11951215 threadLastVisitedAts ,
11961216 visibleProjectThreads ,
11971217 ] ) ;
@@ -2253,17 +2273,35 @@ function ProjectSortMenu({
22532273 projectSortOrder,
22542274 threadSortOrder,
22552275 projectGroupingMode,
2276+ threadPreviewCount,
22562277 onProjectSortOrderChange,
22572278 onThreadSortOrderChange,
22582279 onProjectGroupingModeChange,
2280+ onThreadPreviewCountChange,
22592281} : {
22602282 projectSortOrder : SidebarProjectSortOrder ;
22612283 threadSortOrder : SidebarThreadSortOrder ;
22622284 projectGroupingMode : SidebarProjectGroupingMode ;
2285+ threadPreviewCount : SidebarThreadPreviewCount ;
22632286 onProjectSortOrderChange : ( sortOrder : SidebarProjectSortOrder ) => void ;
22642287 onThreadSortOrderChange : ( sortOrder : SidebarThreadSortOrder ) => void ;
22652288 onProjectGroupingModeChange : ( mode : SidebarProjectGroupingMode ) => void ;
2289+ onThreadPreviewCountChange : ( count : SidebarThreadPreviewCount ) => void ;
22662290} ) {
2291+ const handleThreadPreviewCountChange = useCallback (
2292+ ( nextValue : number | null ) => {
2293+ if ( nextValue === null ) {
2294+ return ;
2295+ }
2296+
2297+ const clampedValue = clampSidebarThreadPreviewCount ( nextValue ) ;
2298+ if ( clampedValue !== threadPreviewCount ) {
2299+ onThreadPreviewCountChange ( clampedValue ) ;
2300+ }
2301+ } ,
2302+ [ onThreadPreviewCountChange , threadPreviewCount ] ,
2303+ ) ;
2304+
22672305 return (
22682306 < Menu >
22692307 < Tooltip >
@@ -2274,9 +2312,9 @@ function ProjectSortMenu({
22742312 >
22752313 < ArrowUpDownIcon className = "size-3.5" />
22762314 </ TooltipTrigger >
2277- < TooltipPopup side = "right" > Sort projects </ TooltipPopup >
2315+ < TooltipPopup side = "right" > Sidebar options </ TooltipPopup >
22782316 </ Tooltip >
2279- < MenuPopup align = "end" side = "bottom" className = "min-w-44 " >
2317+ < MenuPopup align = "end" side = "bottom" className = "min-w-52 " >
22802318 < MenuGroup >
22812319 < div className = "px-2 py-1 sm:text-xs font-medium text-muted-foreground" >
22822320 Sort projects
@@ -2315,6 +2353,42 @@ function ProjectSortMenu({
23152353 ) ) }
23162354 </ MenuRadioGroup >
23172355 </ MenuGroup >
2356+ < MenuGroup >
2357+ < div className = "px-2 pt-2 pb-1 text-muted-foreground sm:text-xs font-medium" >
2358+ Visible threads
2359+ </ div >
2360+ < div className = "px-2 py-1" >
2361+ < NumberField
2362+ aria-label = "Visible thread count"
2363+ className = "w-28 gap-0"
2364+ max = { MAX_SIDEBAR_THREAD_PREVIEW_COUNT }
2365+ min = { MIN_SIDEBAR_THREAD_PREVIEW_COUNT }
2366+ onValueChange = { handleThreadPreviewCountChange }
2367+ size = "sm"
2368+ step = { 1 }
2369+ value = { threadPreviewCount }
2370+ >
2371+ < NumberFieldGroup className = "h-7 rounded-md sm:h-6.5" >
2372+ < NumberFieldDecrement
2373+ aria-label = "Decrease visible thread count"
2374+ className = "px-2 sm:px-2 [&_svg]:size-3.5"
2375+ />
2376+ < NumberFieldInput
2377+ aria-label = "Visible thread count"
2378+ className = "h-7 w-9 grow-0 px-0 text-xs leading-7 sm:h-6.5 sm:leading-6.5"
2379+ inputMode = "numeric"
2380+ onKeyDownCapture = { ( event ) => {
2381+ event . stopPropagation ( ) ;
2382+ } }
2383+ />
2384+ < NumberFieldIncrement
2385+ aria-label = "Increase visible thread count"
2386+ className = "px-2 sm:px-2 [&_svg]:size-3.5"
2387+ />
2388+ </ NumberFieldGroup >
2389+ </ NumberField >
2390+ </ div >
2391+ </ MenuGroup >
23182392 < MenuSeparator />
23192393 < MenuGroup >
23202394 < div className = "px-2 pt-2 pb-1 font-medium text-muted-foreground sm:text-xs" >
@@ -2462,6 +2536,7 @@ interface SidebarProjectsContentProps {
24622536 projectSortOrder : SidebarProjectSortOrder ;
24632537 threadSortOrder : SidebarThreadSortOrder ;
24642538 projectGroupingMode : SidebarProjectGroupingMode ;
2539+ threadPreviewCount : SidebarThreadPreviewCount ;
24652540 updateSettings : ReturnType < typeof useUpdateSettings > [ "updateSettings" ] ;
24662541 openAddProject : ( ) => void ;
24672542 isManualProjectSorting : boolean ;
@@ -2502,6 +2577,7 @@ const SidebarProjectsContent = memo(function SidebarProjectsContent(
25022577 projectSortOrder,
25032578 threadSortOrder,
25042579 projectGroupingMode,
2580+ threadPreviewCount,
25052581 updateSettings,
25062582 openAddProject,
25072583 isManualProjectSorting,
@@ -2548,6 +2624,12 @@ const SidebarProjectsContent = memo(function SidebarProjectsContent(
25482624 } ,
25492625 [ updateSettings ] ,
25502626 ) ;
2627+ const handleThreadPreviewCountChange = useCallback (
2628+ ( count : SidebarThreadPreviewCount ) => {
2629+ updateSettings ( { sidebarThreadPreviewCount : count } ) ;
2630+ } ,
2631+ [ updateSettings ] ,
2632+ ) ;
25512633
25522634 return (
25532635 < SidebarContent className = "gap-0" >
@@ -2607,9 +2689,11 @@ const SidebarProjectsContent = memo(function SidebarProjectsContent(
26072689 projectSortOrder = { projectSortOrder }
26082690 threadSortOrder = { threadSortOrder }
26092691 projectGroupingMode = { projectGroupingMode }
2692+ threadPreviewCount = { threadPreviewCount }
26102693 onProjectSortOrderChange = { handleProjectSortOrderChange }
26112694 onThreadSortOrderChange = { handleThreadSortOrderChange }
26122695 onProjectGroupingModeChange = { handleProjectGroupingModeChange }
2696+ onThreadPreviewCountChange = { handleThreadPreviewCountChange }
26132697 />
26142698 < Tooltip >
26152699 < TooltipTrigger
@@ -2729,6 +2813,7 @@ export default function Sidebar() {
27292813 sidebarProjectGroupingMode : settings . sidebarProjectGroupingMode ,
27302814 sidebarProjectGroupingOverrides : settings . sidebarProjectGroupingOverrides ,
27312815 } ) ) ;
2816+ const sidebarThreadPreviewCount = useSettings ( ( s ) => s . sidebarThreadPreviewCount ) ;
27322817 const { updateSettings } = useUpdateSettings ( ) ;
27332818 const { handleNewThread } = useNewThreadHandler ( ) ;
27342819 const { archiveThread, deleteThread } = useThreadActions ( ) ;
@@ -3024,18 +3109,19 @@ export default function Sidebar() {
30243109 return [ ] ;
30253110 }
30263111 const isThreadListExpanded = expandedThreadListsByProject . has ( project . projectKey ) ;
3027- const hasOverflowingThreads = projectThreads . length > THREAD_PREVIEW_LIMIT ;
3112+ const hasOverflowingThreads = projectThreads . length > sidebarThreadPreviewCount ;
30283113 const previewThreads =
30293114 isThreadListExpanded || ! hasOverflowingThreads
30303115 ? projectThreads
3031- : projectThreads . slice ( 0 , THREAD_PREVIEW_LIMIT ) ;
3116+ : projectThreads . slice ( 0 , sidebarThreadPreviewCount ) ;
30323117 const renderedThreads = pinnedCollapsedThread ? [ pinnedCollapsedThread ] : previewThreads ;
30333118 return renderedThreads . map ( ( thread ) =>
30343119 scopedThreadKey ( scopeThreadRef ( thread . environmentId , thread . id ) ) ,
30353120 ) ;
30363121 } ) ,
30373122 [
30383123 sidebarThreadSortOrder ,
3124+ sidebarThreadPreviewCount ,
30393125 expandedThreadListsByProject ,
30403126 projectExpandedById ,
30413127 routeThreadKey ,
@@ -3358,6 +3444,7 @@ export default function Sidebar() {
33583444 projectSortOrder = { sidebarProjectSortOrder }
33593445 threadSortOrder = { sidebarThreadSortOrder }
33603446 projectGroupingMode = { sidebarProjectGroupingMode }
3447+ threadPreviewCount = { sidebarThreadPreviewCount }
33613448 updateSettings = { updateSettings }
33623449 openAddProject = { openAddProjectCommandPalette }
33633450 isManualProjectSorting = { isManualProjectSorting }
0 commit comments