@@ -4,6 +4,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
44import { useSessionShortcuts } from "../../hooks/useChordShortcuts" ;
55import { useRepoStore } from "../../stores/repoStore" ;
66import { useSessionStore } from "../../stores/sessionStore" ;
7+ import { useThemeStore } from "../../stores/themeStore" ;
78import { NewSessionDialog } from "./NewSessionDialog" ;
89import { SessionListItem , type SessionListItemProps } from "./SessionListItem" ;
910import { WorktreeDeleteDialog } from "./WorktreeDeleteDialog" ;
@@ -34,7 +35,10 @@ export function Sidebar() {
3435
3536 const reorderSessions = useSessionStore ( ( state ) => state . reorderSessions ) ;
3637
38+ const toggleSettings = useThemeStore ( ( state ) => state . toggleSettings ) ;
39+
3740 const [ archiveOpen , setArchiveOpen ] = useState ( false ) ;
41+ const [ shortcutsOpen , setShortcutsOpen ] = useState ( false ) ;
3842 const [ metaHeld , setMetaHeld ] = useState ( false ) ;
3943 const repoPickerRef = useRef < HTMLDivElement > ( null ) ;
4044
@@ -259,6 +263,46 @@ export function Sidebar() {
259263 </ div >
260264 ) }
261265
266+ { /* Keyboard shortcuts panel */ }
267+ { shortcutsOpen && (
268+ < div className = "border-t border-white/[0.06] px-3 py-2.5 space-y-1.5" >
269+ < p className = "text-[10px] font-medium text-text-muted uppercase tracking-wide mb-2" > Shortcuts</ p >
270+ { SHORTCUTS . map ( ( { keys, label } ) => (
271+ < div key = { label } className = "flex items-center justify-between gap-2" >
272+ < span className = "text-[11px] text-text-muted" > { label } </ span >
273+ < kbd className = "text-[10px] text-text-muted font-mono bg-white/5 rounded px-1.5 py-0.5 shrink-0" >
274+ { keys }
275+ </ kbd >
276+ </ div >
277+ ) ) }
278+ </ div >
279+ ) }
280+
281+ { /* Bottom bar: keyboard shortcuts toggle + settings */ }
282+ < div className = "border-t border-white/[0.06] flex items-center px-2 py-1.5" >
283+ < button
284+ type = "button"
285+ onClick = { ( ) => setShortcutsOpen ( ! shortcutsOpen ) }
286+ className = { `w-6 h-6 flex items-center justify-center rounded-md transition-colors ${
287+ shortcutsOpen
288+ ? "text-text-primary bg-white/10"
289+ : "text-text-muted hover:text-text-primary hover:bg-white/10"
290+ } `}
291+ title = "Keyboard shortcuts"
292+ >
293+ < KeyboardIcon />
294+ </ button >
295+ < div className = "flex-1" />
296+ < button
297+ type = "button"
298+ onClick = { toggleSettings }
299+ className = "w-6 h-6 flex items-center justify-center rounded-md text-text-muted hover:text-text-primary hover:bg-white/10 transition-colors"
300+ title = "Settings (⌘,)"
301+ >
302+ < GearIcon />
303+ </ button >
304+ </ div >
305+
262306 { /* New session dialog */ }
263307 { pendingNewSessionRepo && (
264308 < NewSessionDialog
@@ -283,6 +327,14 @@ export function Sidebar() {
283327 ) ;
284328}
285329
330+ const SHORTCUTS = [
331+ { keys : "⌘N" , label : "New session" } ,
332+ { keys : "⌘," , label : "Settings" } ,
333+ { keys : "⌘B" , label : "Toggle sidebar" } ,
334+ { keys : "⌘1-9" , label : "Jump to session" } ,
335+ { keys : "Esc×2" , label : "Stop agent" } ,
336+ ] ;
337+
286338function SortableSessionItem ( props : Omit < SessionListItemProps , "sortableProps" > ) {
287339 const sortable = useSortable ( { id : props . session . id } ) ;
288340 return < SessionListItem { ...props } sortableProps = { sortable } /> ;
@@ -323,3 +375,39 @@ function ChevronIcon({ open }: { open: boolean }) {
323375 </ svg >
324376 ) ;
325377}
378+
379+ function GearIcon ( ) {
380+ return (
381+ < svg
382+ width = "14"
383+ height = "14"
384+ viewBox = "0 0 24 24"
385+ fill = "none"
386+ stroke = "currentColor"
387+ strokeWidth = "2"
388+ strokeLinecap = "round"
389+ strokeLinejoin = "round"
390+ >
391+ < circle cx = "12" cy = "12" r = "3" />
392+ < path d = "M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" />
393+ </ svg >
394+ ) ;
395+ }
396+
397+ function KeyboardIcon ( ) {
398+ return (
399+ < svg
400+ width = "14"
401+ height = "14"
402+ viewBox = "0 0 24 24"
403+ fill = "none"
404+ stroke = "currentColor"
405+ strokeWidth = "2"
406+ strokeLinecap = "round"
407+ strokeLinejoin = "round"
408+ >
409+ < rect x = "2" y = "6" width = "20" height = "12" rx = "2" />
410+ < path d = "M6 10h.01M10 10h.01M14 10h.01M18 10h.01M8 14h8" />
411+ </ svg >
412+ ) ;
413+ }
0 commit comments