@@ -26,6 +26,7 @@ import {
2626import { cn } from "@/lib/utils" ;
2727import { usePanel } from "@/hooks/usePanel" ;
2828import { useTranslation } from "@/hooks/useTranslation" ;
29+ import { useNativeFolderPicker } from "@/hooks/useNativeFolderPicker" ;
2930import { ConnectionStatus } from "./ConnectionStatus" ;
3031import { ImportSessionDialog } from "./ImportSessionDialog" ;
3132import { FolderPicker } from "@/components/chat/FolderPicker" ;
@@ -119,6 +120,7 @@ export function ChatListPanel({ open, width }: ChatListPanelProps) {
119120 const router = useRouter ( ) ;
120121 const { streamingSessionId, pendingApprovalSessionId, workingDirectory } = usePanel ( ) ;
121122 const { t } = useTranslation ( ) ;
123+ const { isElectron, openNativePicker } = useNativeFolderPicker ( ) ;
122124 const [ sessions , setSessions ] = useState < ChatSession [ ] > ( [ ] ) ;
123125 const [ hoveredSession , setHoveredSession ] = useState < string | null > ( null ) ;
124126 const [ deletingSession , setDeletingSession ] = useState < string | null > ( null ) ;
@@ -131,13 +133,39 @@ export function ChatListPanel({ open, width }: ChatListPanelProps) {
131133 const [ hoveredFolder , setHoveredFolder ] = useState < string | null > ( null ) ;
132134 const [ creatingChat , setCreatingChat ] = useState ( false ) ;
133135
136+ const handleFolderSelect = useCallback ( async ( path : string ) => {
137+ try {
138+ const res = await fetch ( "/api/chat/sessions" , {
139+ method : "POST" ,
140+ headers : { "Content-Type" : "application/json" } ,
141+ body : JSON . stringify ( { working_directory : path } ) ,
142+ } ) ;
143+ if ( res . ok ) {
144+ const data = await res . json ( ) ;
145+ window . dispatchEvent ( new CustomEvent ( "session-created" ) ) ;
146+ router . push ( `/chat/${ data . session . id } ` ) ;
147+ }
148+ } catch {
149+ // Silently fail
150+ }
151+ } , [ router ] ) ;
152+
153+ const openFolderPicker = useCallback ( async ( defaultPath ?: string ) => {
154+ if ( isElectron ) {
155+ const path = await openNativePicker ( { defaultPath, title : t ( 'folderPicker.title' ) } ) ;
156+ if ( path ) handleFolderSelect ( path ) ;
157+ } else {
158+ setFolderPickerOpen ( true ) ;
159+ }
160+ } , [ isElectron , openNativePicker , t , handleFolderSelect ] ) ;
161+
134162 const handleNewChat = useCallback ( async ( ) => {
135163 const lastDir = workingDirectory
136164 || ( typeof window !== 'undefined' ? localStorage . getItem ( "codepilot:last-working-directory" ) : null ) ;
137165
138166 if ( ! lastDir ) {
139167 // No saved directory — let user pick one
140- setFolderPickerOpen ( true ) ;
168+ openFolderPicker ( ) ;
141169 return ;
142170 }
143171
@@ -150,7 +178,7 @@ export function ChatListPanel({ open, width }: ChatListPanelProps) {
150178 if ( ! checkRes . ok ) {
151179 // Directory is gone — clear stale value and prompt user
152180 localStorage . removeItem ( "codepilot:last-working-directory" ) ;
153- setFolderPickerOpen ( true ) ;
181+ openFolderPicker ( ) ;
154182 return ;
155183 }
156184
@@ -162,18 +190,18 @@ export function ChatListPanel({ open, width }: ChatListPanelProps) {
162190 if ( ! res . ok ) {
163191 // Backend rejected it (e.g. INVALID_DIRECTORY) — prompt user
164192 localStorage . removeItem ( "codepilot:last-working-directory" ) ;
165- setFolderPickerOpen ( true ) ;
193+ openFolderPicker ( ) ;
166194 return ;
167195 }
168196 const data = await res . json ( ) ;
169197 router . push ( `/chat/${ data . session . id } ` ) ;
170198 window . dispatchEvent ( new CustomEvent ( "session-created" ) ) ;
171199 } catch {
172- setFolderPickerOpen ( true ) ;
200+ openFolderPicker ( ) ;
173201 } finally {
174202 setCreatingChat ( false ) ;
175203 }
176- } , [ router , workingDirectory ] ) ;
204+ } , [ router , workingDirectory , openFolderPicker ] ) ;
177205
178206 const toggleProject = useCallback ( ( wd : string ) => {
179207 setCollapsedProjects ( ( prev ) => {
@@ -263,23 +291,6 @@ export function ChatListPanel({ open, width }: ChatListPanelProps) {
263291 }
264292 } ;
265293
266- const handleFolderSelect = async ( path : string ) => {
267- try {
268- const res = await fetch ( "/api/chat/sessions" , {
269- method : "POST" ,
270- headers : { "Content-Type" : "application/json" } ,
271- body : JSON . stringify ( { working_directory : path } ) ,
272- } ) ;
273- if ( res . ok ) {
274- const data = await res . json ( ) ;
275- window . dispatchEvent ( new CustomEvent ( "session-created" ) ) ;
276- router . push ( `/chat/${ data . session . id } ` ) ;
277- }
278- } catch {
279- // Silently fail
280- }
281- } ;
282-
283294 const isSearching = searchQuery . length > 0 ;
284295
285296 const filteredSessions = searchQuery
@@ -341,7 +352,7 @@ export function ChatListPanel({ open, width }: ChatListPanelProps) {
341352 variant = "outline"
342353 size = "icon-sm"
343354 className = "h-8 w-8 shrink-0"
344- onClick = { ( ) => setFolderPickerOpen ( true ) }
355+ onClick = { ( ) => openFolderPicker ( ) }
345356 >
346357 < HugeiconsIcon icon = { FolderOpenIcon } className = "h-3.5 w-3.5" />
347358 < span className = "sr-only" > { t ( 'chatList.addProjectFolder' ) } </ span >
@@ -408,9 +419,8 @@ export function ChatListPanel({ open, width }: ChatListPanelProps) {
408419 onDoubleClick = { ( e ) => {
409420 e . stopPropagation ( ) ;
410421 if ( group . workingDirectory ) {
411- const w = window as unknown as { electronAPI ?: { shell ?: { openPath : ( p : string ) => void } } } ;
412- if ( w . electronAPI ?. shell ?. openPath ) {
413- w . electronAPI . shell . openPath ( group . workingDirectory ) ;
422+ if ( window . electronAPI ?. shell ?. openPath ) {
423+ window . electronAPI . shell . openPath ( group . workingDirectory ) ;
414424 } else {
415425 fetch ( '/api/files/open' , {
416426 method : 'POST' ,
0 commit comments