@@ -4,6 +4,7 @@ import { useNavigate } from "@tanstack/react-router";
44import {
55 FolderOpenIcon ,
66 FolderIcon ,
7+ GitBranchIcon ,
78 GitMergeIcon ,
89 GitPullRequestIcon ,
910 SettingsIcon ,
@@ -18,6 +19,7 @@ import { serverConfigQueryOptions } from "../lib/serverReactQuery";
1819import { newCommandId , newProjectId } from "../lib/utils" ;
1920import { readNativeApi } from "../nativeApi" ;
2021import { useStore } from "../store" ;
22+ import { CloneRepositoryDialog } from "./CloneRepositoryDialog" ;
2123import { sortProjectsForSidebar } from "./Sidebar.logic" ;
2224import { ProviderSetupCard } from "./chat/ProviderSetupCard" ;
2325import { Button } from "./ui/button" ;
@@ -34,6 +36,7 @@ export function ChatHomeEmptyState() {
3436 const threads = useStore ( ( store ) => store . threads ) ;
3537 const { handleNewThread } = useHandleNewThread ( ) ;
3638 const [ isOpeningProject , setIsOpeningProject ] = useState ( false ) ;
39+ const [ cloneDialogOpen , setCloneDialogOpen ] = useState ( false ) ;
3740
3841 const recentProjects = useMemo (
3942 ( ) =>
@@ -113,6 +116,47 @@ export function ChatHomeEmptyState() {
113116 setIsOpeningProject ( false ) ;
114117 } , [ appSettings . defaultThreadEnvMode , handleNewThread , isOpeningProject , projects ] ) ;
115118
119+ const handleCloned = useCallback (
120+ async ( result : { path : string ; branch : string ; repoName : string } ) => {
121+ const api = readNativeApi ( ) ;
122+ if ( ! api ) return ;
123+
124+ const existingProject = projects . find ( ( project ) => project . cwd === result . path ) ;
125+ if ( existingProject ) {
126+ await handleNewThread ( existingProject . id , {
127+ envMode : appSettings . defaultThreadEnvMode ,
128+ } ) . catch ( ( ) => undefined ) ;
129+ return ;
130+ }
131+
132+ try {
133+ const projectId = newProjectId ( ) ;
134+ await api . orchestration . dispatchCommand ( {
135+ type : "project.create" ,
136+ commandId : newCommandId ( ) ,
137+ projectId,
138+ title : result . repoName ,
139+ workspaceRoot : result . path ,
140+ defaultModel : DEFAULT_MODEL_BY_PROVIDER . codex ,
141+ createdAt : new Date ( ) . toISOString ( ) ,
142+ } ) ;
143+ await handleNewThread ( projectId , {
144+ envMode : appSettings . defaultThreadEnvMode ,
145+ } ) . catch ( ( ) => undefined ) ;
146+ } catch ( error ) {
147+ toastManager . add ( {
148+ type : "error" ,
149+ title : "Failed to add project" ,
150+ description :
151+ error instanceof Error
152+ ? error . message
153+ : "An unexpected error occurred while adding the project." ,
154+ } ) ;
155+ }
156+ } ,
157+ [ appSettings . defaultThreadEnvMode , handleNewThread , projects ] ,
158+ ) ;
159+
116160 const startLatestThread = useCallback ( async ( ) => {
117161 if ( ! latestProject ) {
118162 await openProjectFolder ( ) ;
@@ -178,6 +222,14 @@ export function ChatHomeEmptyState() {
178222 < FolderOpenIcon className = "size-4" />
179223 { isOpeningProject ? "Opening…" : "Open project folder" }
180224 </ Button >
225+ < Button
226+ variant = "outline"
227+ className = "justify-start gap-2"
228+ onClick = { ( ) => setCloneDialogOpen ( true ) }
229+ >
230+ < GitBranchIcon className = "size-4" />
231+ Clone from GitHub
232+ </ Button >
181233 < Button
182234 variant = "outline"
183235 className = "justify-start gap-2"
@@ -253,6 +305,12 @@ export function ChatHomeEmptyState() {
253305 ) }
254306 </ div >
255307 </ div >
308+
309+ < CloneRepositoryDialog
310+ open = { cloneDialogOpen }
311+ onOpenChange = { setCloneDialogOpen }
312+ onCloned = { handleCloned }
313+ />
256314 </ div >
257315 ) ;
258316}
0 commit comments