@@ -7,14 +7,15 @@ import { KnotBackground } from '@/components/knot-background'
77import { ModeSelector } from '@/components/mode-selector'
88import type { AgentMode } from '@/components/mode-selector'
99import { PermissionsToggle } from '@/components/permissions-toggle'
10- import { useRepo } from '@/context/repo-context'
10+ import { useRepo , type RepoInfo } from '@/context/repo-context'
1111import { useLocal } from '@/context/local-context'
1212import { useGateway } from '@/context/gateway-context'
1313import { useGitHubAuth } from '@/context/github-auth-context'
1414import { useEditor } from '@/context/editor-context'
1515import { emit } from '@/lib/events'
1616import { getRecentFolders } from '@/context/local-context'
1717import { getAgentConfig } from '@/lib/agent-session'
18+ import { fetchRepoByName } from '@/lib/github-api'
1819
1920const STATIC_SUGGESTIONS = [
2021 {
@@ -88,10 +89,11 @@ export const ChatHome = memo(function ChatHome({
8889 const [ agentMode , setAgentMode ] = useState < AgentMode > ( 'ask' )
8990 const [ isFocused , setIsFocused ] = useState ( false )
9091 const inputRef = useRef < HTMLTextAreaElement > ( null )
91- const { repo } = useRepo ( )
92+ const { repo, setRepo } = useRepo ( )
9293 const local = useLocal ( )
9394 const { status } = useGateway ( )
9495 const { files : openFiles } = useEditor ( )
96+ const { token : ghToken } = useGitHubAuth ( )
9597
9698 const repoShort = useMemo (
9799 ( ) => repo ?. fullName ?. split ( '/' ) . pop ( ) ?? local . rootPath ?. split ( '/' ) . pop ( ) ?? null ,
@@ -101,6 +103,45 @@ export const ChatHome = memo(function ChatHome({
101103 const branchName = local . gitInfo ?. branch ?? null
102104
103105 const [ isComposing , setIsComposing ] = useState ( false )
106+ const [ showRepoInput , setShowRepoInput ] = useState ( false )
107+ const [ repoInput , setRepoInput ] = useState ( '' )
108+ const [ repoLoading , setRepoLoading ] = useState ( false )
109+ const [ repoError , setRepoError ] = useState < string | null > ( null )
110+ const repoInputRef = useRef < HTMLInputElement > ( null )
111+
112+ const isMobile = typeof window !== 'undefined' && window . innerWidth <= 768
113+
114+ const handleRepoConnect = useCallback ( async ( ) => {
115+ const val = repoInput . trim ( ) . replace ( / ^ h t t p s ? : \/ \/ g i t h u b \. c o m \/ / , '' ) . replace ( / \. g i t $ / , '' ) . replace ( / \/ $ / , '' )
116+ if ( ! val || ! val . includes ( '/' ) ) {
117+ setRepoError ( 'Enter owner/repo (e.g. OpenKnots/code-editor)' )
118+ return
119+ }
120+ setRepoLoading ( true )
121+ setRepoError ( null )
122+ try {
123+ const ghRepo = await fetchRepoByName ( val )
124+ const info : RepoInfo = {
125+ owner : ghRepo . owner . login ,
126+ repo : ghRepo . name ,
127+ branch : ghRepo . default_branch ,
128+ fullName : ghRepo . full_name ,
129+ }
130+ setRepo ( info )
131+ setShowRepoInput ( false )
132+ setRepoInput ( '' )
133+ } catch ( err ) {
134+ setRepoError ( err instanceof Error ? err . message : 'Repository not found' )
135+ } finally {
136+ setRepoLoading ( false )
137+ }
138+ } , [ repoInput , setRepo ] )
139+
140+ useEffect ( ( ) => {
141+ if ( showRepoInput ) {
142+ setTimeout ( ( ) => repoInputRef . current ?. focus ( ) , 100 )
143+ }
144+ } , [ showRepoInput ] )
104145
105146 useEffect ( ( ) => {
106147 const t = setTimeout ( ( ) => inputRef . current ?. focus ( ) , 100 )
@@ -215,10 +256,63 @@ export const ChatHome = memo(function ChatHome({
215256 { repoShort ?? 'Select workspace' }
216257 < Icon icon = "lucide:chevron-down" width = { 14 } height = { 14 } className = "opacity-50" />
217258 </ button >
218- { /* Subtle tagline on mobile */ }
219- < p className = "mt-2 text-[13px] text-[var(--text-disabled)] sm:hidden" >
220- Your AI-powered code companion
221- </ p >
259+ { /* Mobile project selector */ }
260+ { isMobile && ! hasWorkspace && ! showRepoInput && (
261+ < button
262+ onClick = { ( ) => setShowRepoInput ( true ) }
263+ className = "mt-3 inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg border border-dashed border-[var(--border)] text-[12px] text-[var(--text-disabled)] hover:text-[var(--text-secondary)] hover:border-[var(--text-disabled)] transition-all cursor-pointer"
264+ >
265+ < Icon icon = "lucide:github" width = { 14 } height = { 14 } />
266+ Connect a repository
267+ </ button >
268+ ) }
269+ { isMobile && ! hasWorkspace && showRepoInput && (
270+ < div className = "mt-3 w-full max-w-[320px]" >
271+ < div className = "flex items-center gap-1.5" >
272+ < div className = "flex-1 relative" >
273+ < Icon icon = "lucide:github" width = { 14 } height = { 14 } className = "absolute left-2.5 top-1/2 -translate-y-1/2 text-[var(--text-disabled)]" />
274+ < input
275+ ref = { repoInputRef }
276+ type = "text"
277+ value = { repoInput }
278+ onChange = { ( e ) => { setRepoInput ( e . target . value ) ; setRepoError ( null ) } }
279+ onKeyDown = { ( e ) => { if ( e . key === 'Enter' ) handleRepoConnect ( ) ; if ( e . key === 'Escape' ) setShowRepoInput ( false ) } }
280+ placeholder = "owner/repo"
281+ autoCapitalize = "off"
282+ autoCorrect = "off"
283+ spellCheck = { false }
284+ className = "w-full pl-8 pr-3 py-2 rounded-lg border border-[var(--border)] bg-[color-mix(in_srgb,var(--bg-elevated)_80%,transparent)] text-[13px] text-[var(--text-primary)] placeholder:text-[var(--text-disabled)] outline-none focus:border-[var(--brand)] transition-colors"
285+ />
286+ </ div >
287+ < button
288+ onClick = { handleRepoConnect }
289+ disabled = { repoLoading || ! repoInput . trim ( ) }
290+ className = "shrink-0 px-3 py-2 rounded-lg text-[12px] font-medium transition-all cursor-pointer disabled:opacity-40 disabled:cursor-default bg-[var(--brand)] text-[var(--brand-contrast,#fff)]"
291+ >
292+ { repoLoading ? '…' : 'Go' }
293+ </ button >
294+ </ div >
295+ { repoError && (
296+ < p className = "mt-1.5 text-[11px] text-[var(--color-deletions)]" > { repoError } </ p >
297+ ) }
298+ </ div >
299+ ) }
300+ { isMobile && hasWorkspace && (
301+ < button
302+ onClick = { ( ) => { setRepo ( null ) ; setShowRepoInput ( true ) } }
303+ className = "mt-2 inline-flex items-center gap-1.5 text-[13px] text-[var(--text-secondary)] hover:text-[var(--text-primary)] transition-colors cursor-pointer"
304+ >
305+ < Icon icon = "lucide:github" width = { 13 } height = { 13 } className = "opacity-60" />
306+ { repoShort }
307+ < Icon icon = "lucide:chevron-down" width = { 12 } height = { 12 } className = "opacity-40" />
308+ </ button >
309+ ) }
310+ { /* Subtle tagline on mobile — only when no repo input shown */ }
311+ { isMobile && hasWorkspace && (
312+ < p className = "mt-1 text-[11px] text-[var(--text-disabled)]" >
313+ { repo ?. fullName ?? 'local project' }
314+ </ p >
315+ ) }
222316 </ div >
223317
224318 { /* "Explore more" link — desktop only */ }
0 commit comments