@@ -48,7 +48,16 @@ import {
4848 XIcon ,
4949 XCircleIcon ,
5050} from "lucide-react" ;
51- import { type MouseEvent , memo , useCallback , useEffect , useMemo , useRef , useState } from "react" ;
51+ import {
52+ type CSSProperties ,
53+ type MouseEvent ,
54+ memo ,
55+ useCallback ,
56+ useEffect ,
57+ useMemo ,
58+ useRef ,
59+ useState ,
60+ } from "react" ;
5261import { CloneRepositoryDialog } from "~/components/CloneRepositoryDialog" ;
5362import { EditableThreadTitle } from "~/components/EditableThreadTitle" ;
5463import { useClientMode } from "~/hooks/useClientMode" ;
@@ -139,6 +148,48 @@ const SIDEBAR_THREAD_SORT_LABELS: Record<SidebarThreadSortOrder, string> = {
139148} ;
140149const EMPTY_THREADS : readonly Thread [ ] = [ ] ;
141150const EMPTY_THREAD_IDS : readonly ThreadIdType [ ] = [ ] ;
151+
152+ type SidebarDensityStyle = CSSProperties & {
153+ "--ok-sidebar-project-row-height" : string ;
154+ "--ok-sidebar-thread-row-height" : string ;
155+ "--ok-sidebar-font-size" : string ;
156+ "--ok-sidebar-spacing" : string ;
157+ } ;
158+
159+ const SIDEBAR_PROJECT_HEADER_STYLE : CSSProperties = {
160+ gap : "calc(var(--ok-sidebar-spacing) * 0.5)" ,
161+ } ;
162+
163+ const SIDEBAR_PROJECT_ROW_STYLE : CSSProperties = {
164+ minHeight : "var(--ok-sidebar-project-row-height)" ,
165+ paddingInline : "var(--ok-sidebar-spacing)" ,
166+ paddingBlock : "calc(var(--ok-sidebar-spacing) * 0.75)" ,
167+ fontSize : "var(--ok-sidebar-font-size)" ,
168+ } ;
169+
170+ const SIDEBAR_PROJECT_TITLE_STYLE : CSSProperties = {
171+ fontSize : "var(--ok-sidebar-font-size)" ,
172+ } ;
173+
174+ const SIDEBAR_THREAD_LIST_STYLE : CSSProperties = {
175+ gap : "calc(var(--ok-sidebar-spacing) * 0.25)" ,
176+ paddingInline : "calc(var(--ok-sidebar-spacing) * 0.5)" ,
177+ } ;
178+
179+ const SIDEBAR_THREAD_ROW_STYLE : CSSProperties = {
180+ minHeight : "var(--ok-sidebar-thread-row-height)" ,
181+ paddingInline : "var(--ok-sidebar-spacing)" ,
182+ paddingBlock : "calc(var(--ok-sidebar-spacing) * 0.5)" ,
183+ gap : "calc(var(--ok-sidebar-spacing) * 0.5)" ,
184+ fontSize : "var(--ok-sidebar-font-size)" ,
185+ } ;
186+
187+ const SIDEBAR_COLLAPSE_TOGGLE_STYLE : CSSProperties = {
188+ minHeight : "calc(var(--ok-sidebar-thread-row-height) - 4px)" ,
189+ paddingInline : "var(--ok-sidebar-spacing)" ,
190+ fontSize : "calc(var(--ok-sidebar-font-size) - 2px)" ,
191+ } ;
192+
142193interface PrStatusIndicator {
143194 label : "PR open" | "PR closed" | "PR merged" ;
144195 colorClass : string ;
@@ -381,13 +432,14 @@ const MemoizedThreadRow = memo(
381432 size = "sm"
382433 isActive = { isActive }
383434 className = { cn (
384- "h-auto min-h-7 translate-x-0 items-center gap-2 rounded-md px-2 py-1 text-left" ,
435+ "h-auto translate-x-0 items-center rounded-md text-left" ,
385436 isActive
386437 ? "bg-accent/60 text-foreground"
387438 : isSelected
388439 ? "bg-accent/40 text-foreground"
389440 : "text-muted-foreground hover:bg-accent/40 hover:text-foreground" ,
390441 ) }
442+ style = { SIDEBAR_THREAD_ROW_STYLE }
391443 onClick = { ( event ) => {
392444 handleThreadClick ( event , thread . id , orderedProjectThreadIds ) ;
393445 } }
@@ -422,15 +474,15 @@ const MemoizedThreadRow = memo(
422474 } }
423475 >
424476 < ThreadIcon className = { cn ( "size-3.5 shrink-0" , threadIconColor ) } />
425- < div className = "flex min-w-0 flex-1 items-center gap-1.5 text-left" >
477+ < div className = "flex min-w-0 flex-1 items-center text-left" >
426478 < EditableThreadTitle
427479 title = { thread . title }
428480 isEditing = { editingThreadId === thread . id }
429481 draftTitle = { editingThreadTitle }
430482 inputRef = { bindInputRef }
431483 containerClassName = "min-w-0 flex-1"
432- titleClassName = "min-w-0 flex-1 truncate text-xs "
433- inputClassName = "h-6 px-1 text-xs "
484+ titleClassName = "min-w-0 flex-1 truncate leading-tight "
485+ inputClassName = "h-auto min-h-0 px-1 text-[length:var(--ok-sidebar-font-size)] leading-tight "
434486 onStartEditing = { ( ) => {
435487 startEditing ( {
436488 threadId : thread . id ,
@@ -610,6 +662,21 @@ export default function Sidebar() {
610662 ( ) => sortThreadsByProjectIdForSidebar ( sidebarThreads , appSettings . sidebarThreadSortOrder ) ,
611663 [ appSettings . sidebarThreadSortOrder , sidebarThreads ] ,
612664 ) ;
665+ const sidebarDensityStyle = useMemo (
666+ ( ) =>
667+ ( {
668+ "--ok-sidebar-project-row-height" : `${ appSettings . sidebarProjectRowHeight } px` ,
669+ "--ok-sidebar-thread-row-height" : `${ appSettings . sidebarThreadRowHeight } px` ,
670+ "--ok-sidebar-font-size" : `${ appSettings . sidebarFontSize } px` ,
671+ "--ok-sidebar-spacing" : `${ appSettings . sidebarSpacing } px` ,
672+ } ) as SidebarDensityStyle ,
673+ [
674+ appSettings . sidebarFontSize ,
675+ appSettings . sidebarProjectRowHeight ,
676+ appSettings . sidebarSpacing ,
677+ appSettings . sidebarThreadRowHeight ,
678+ ] ,
679+ ) ;
613680 const orderedThreadIdsByProjectId = useMemo ( ( ) => {
614681 const orderedThreadIds = new Map < ProjectId , ThreadIdType [ ] > ( ) ;
615682 for ( const [ projectId , projectThreads ] of sortedThreadsByProjectId ) {
@@ -1366,16 +1433,20 @@ export default function Sidebar() {
13661433 return (
13671434 < Collapsible className = "group/collapsible" open = { shouldShowThreadPanel } >
13681435 < div
1369- className = "group/project-header relative flex items-center gap-1 rounded-md"
1370- style = { { backgroundColor : isDark ? pColor . bgDark : pColor . bg } }
1436+ className = "group/project-header relative flex items-center rounded-md"
1437+ style = { {
1438+ ...SIDEBAR_PROJECT_HEADER_STYLE ,
1439+ backgroundColor : isDark ? pColor . bgDark : pColor . bg ,
1440+ } }
13711441 >
13721442 < SidebarMenuButton
13731443 ref = { isManualProjectSorting ? dragHandleProps ?. setActivatorNodeRef : undefined }
13741444 size = "sm"
13751445 className = { cn (
1376- "min-w-0 flex-1 gap-0 rounded-md px-2 py-1.5 text-left hover:bg-transparent" ,
1446+ "h-auto min-w-0 flex-1 gap-0 rounded-md text-left hover:bg-transparent" ,
13771447 isManualProjectSorting ? "cursor-grab active:cursor-grabbing" : "cursor-pointer" ,
13781448 ) }
1449+ style = { SIDEBAR_PROJECT_ROW_STYLE }
13791450 { ...( isManualProjectSorting && dragHandleProps ? dragHandleProps . attributes : { } ) }
13801451 { ...( isManualProjectSorting && dragHandleProps ? dragHandleProps . listeners : { } ) }
13811452 onPointerDownCapture = { handleProjectTitlePointerDownCapture }
@@ -1394,7 +1465,8 @@ export default function Sidebar() {
13941465 ref = { bindProjectInputRef }
13951466 type = "text"
13961467 value = { draftProjectTitle }
1397- className = "min-w-0 flex-1 rounded border border-primary/40 bg-background px-1 text-xs font-medium outline-none focus:border-primary"
1468+ className = "min-w-0 flex-1 rounded border border-primary/40 bg-background px-1 font-medium outline-none focus:border-primary"
1469+ style = { SIDEBAR_PROJECT_TITLE_STYLE }
13981470 onChange = { ( e ) => setDraftProjectTitle ( e . target . value ) }
13991471 onKeyDown = { ( e ) => {
14001472 if ( e . key === "Enter" ) {
@@ -1412,15 +1484,16 @@ export default function Sidebar() {
14121484 < span className = "min-w-0 flex-1" >
14131485 < span
14141486 className = { cn (
1415- "block truncate text-xs font-semibold" ,
1487+ "block truncate font-semibold leading-tight " ,
14161488 projectNameTone === "mutedStrong" && "text-muted-foreground/72" ,
14171489 projectNameTone === "mutedSoft" && "text-muted-foreground/48" ,
14181490 ) }
1419- style = {
1420- projectNameTone === "project"
1491+ style = { {
1492+ ...SIDEBAR_PROJECT_TITLE_STYLE ,
1493+ ...( projectNameTone === "project"
14211494 ? { color : isDark ? pColor . textDark : pColor . text }
1422- : undefined
1423- }
1495+ : { } ) ,
1496+ } }
14241497 >
14251498 { project . name }
14261499 </ span >
@@ -1452,7 +1525,10 @@ export default function Sidebar() {
14521525 </ div >
14531526
14541527 < CollapsibleContent >
1455- < SidebarMenuSub className = "relative mx-0 my-0 w-auto translate-x-0 gap-0 border-none bg-transparent px-1 py-0" >
1528+ < SidebarMenuSub
1529+ className = "relative mx-0 my-0 w-auto translate-x-0 border-none bg-transparent py-0"
1530+ style = { SIDEBAR_THREAD_LIST_STYLE }
1531+ >
14561532 { renderedThreads . map ( ( thread ) => (
14571533 < MemoizedThreadRow
14581534 key = { thread . id }
@@ -1486,7 +1562,8 @@ export default function Sidebar() {
14861562 render = { < button type = "button" /> }
14871563 data-thread-selection-safe
14881564 size = "sm"
1489- className = "h-6 w-full translate-x-0 justify-start px-2 text-left text-[10px] text-muted-foreground/60 hover:bg-accent hover:text-muted-foreground/80"
1565+ className = "h-auto w-full translate-x-0 justify-start text-left text-muted-foreground/60 hover:bg-accent hover:text-muted-foreground/80"
1566+ style = { SIDEBAR_COLLAPSE_TOGGLE_STYLE }
14901567 onClick = { ( ) => {
14911568 expandThreadListForProject ( project . id ) ;
14921569 } }
@@ -1501,7 +1578,8 @@ export default function Sidebar() {
15011578 render = { < button type = "button" /> }
15021579 data-thread-selection-safe
15031580 size = "sm"
1504- className = "h-6 w-full translate-x-0 justify-start px-2 text-left text-[10px] text-muted-foreground/60 hover:bg-accent hover:text-muted-foreground/80"
1581+ className = "h-auto w-full translate-x-0 justify-start text-left text-muted-foreground/60 hover:bg-accent hover:text-muted-foreground/80"
1582+ style = { SIDEBAR_COLLAPSE_TOGGLE_STYLE }
15051583 onClick = { ( ) => {
15061584 collapseThreadListForProject ( project . id ) ;
15071585 } }
@@ -1972,7 +2050,7 @@ export default function Sidebar() {
19722050 </ SidebarMenu >
19732051 </ SidebarGroup >
19742052 ) : null }
1975- < SidebarGroup className = "px-2 py-2" >
2053+ < SidebarGroup className = "px-2 py-2" style = { sidebarDensityStyle } >
19762054 < div className = "mb-1 flex items-center justify-between px-2" >
19772055 < span className = "text-[10px] font-medium uppercase tracking-wider text-muted-foreground/60" >
19782056 Projects
@@ -2159,7 +2237,7 @@ export default function Sidebar() {
21592237 onDragEnd = { handleProjectDragEnd }
21602238 onDragCancel = { handleProjectDragCancel }
21612239 >
2162- < SidebarMenu >
2240+ < SidebarMenu style = { { gap : "calc(var(--ok-sidebar-spacing) * 0.25)" } } >
21632241 < SortableContext
21642242 items = { sortedProjects . map ( ( project ) => project . id ) }
21652243 strategy = { verticalListSortingStrategy }
@@ -2173,7 +2251,7 @@ export default function Sidebar() {
21732251 </ SidebarMenu >
21742252 </ DndContext >
21752253 ) : (
2176- < SidebarMenu className = " gap-0.5" >
2254+ < SidebarMenu style = { { gap : "calc(var(--ok-sidebar-spacing) * 0.25)" } } >
21772255 { sortedProjects . map ( ( project , index ) => (
21782256 < SidebarMenuItem key = { project . id } className = "rounded-md" >
21792257 { renderProjectItem ( project , null , index ) }
0 commit comments