@@ -120,6 +120,9 @@ export const ChatHome = memo(function ChatHome({
120120 verificationUri : string
121121 } | null > ( null )
122122 const [ authLoading , setAuthLoading ] = useState ( false )
123+ const [ showPatInput , setShowPatInput ] = useState ( false )
124+ const [ patInput , setPatInput ] = useState ( '' )
125+ const patInputRef = useRef < HTMLInputElement > ( null )
123126 const deviceFlowAbort = useRef < AbortController | null > ( null )
124127
125128 const isMobile = typeof window !== 'undefined' && window . innerWidth <= 768
@@ -164,6 +167,18 @@ export const ChatHome = memo(function ChatHome({
164167 setAuthLoading ( false )
165168 } , [ ] )
166169
170+ const handlePatSubmit = useCallback ( ( ) => {
171+ const t = patInput . trim ( )
172+ if ( ! t ) return
173+ setManualToken ( t )
174+ setPatInput ( '' )
175+ setShowPatInput ( false )
176+ } , [ patInput , setManualToken ] )
177+
178+ useEffect ( ( ) => {
179+ if ( showPatInput ) setTimeout ( ( ) => patInputRef . current ?. focus ( ) , 100 )
180+ } , [ showPatInput ] )
181+
167182 const handleRepoConnect = useCallback ( async ( ) => {
168183 const val = repoInput
169184 . trim ( )
@@ -330,15 +345,66 @@ export const ChatHome = memo(function ChatHome({
330345 ) }
331346
332347 { /* Sign in with GitHub — when no token */ }
333- { ! ghAuthenticated && ! deviceFlow && (
334- < button
335- onClick = { startGitHubSignIn }
336- disabled = { authLoading }
337- className = "inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg border border-[var(--border)] bg-[color-mix(in_srgb,var(--bg-elevated)_80%,transparent)] text-[12px] text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:border-[var(--text-disabled)] transition-all cursor-pointer disabled:opacity-50"
338- >
339- < Icon icon = "lucide:github" width = { 14 } height = { 14 } />
340- { authLoading ? 'Signing in…' : 'Sign in with GitHub' }
341- </ button >
348+ { ! ghAuthenticated && ! deviceFlow && ! showPatInput && (
349+ < div className = "flex flex-col items-center gap-2" >
350+ < button
351+ onClick = { startGitHubSignIn }
352+ disabled = { authLoading }
353+ className = "inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg border border-[var(--border)] bg-[color-mix(in_srgb,var(--bg-elevated)_80%,transparent)] text-[12px] text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:border-[var(--text-disabled)] transition-all cursor-pointer disabled:opacity-50"
354+ >
355+ < Icon icon = "lucide:github" width = { 14 } height = { 14 } />
356+ { authLoading ? 'Signing in…' : 'Sign in with GitHub' }
357+ </ button >
358+ < button
359+ onClick = { ( ) => setShowPatInput ( true ) }
360+ className = "text-[11px] text-[var(--text-disabled)] hover:text-[var(--text-secondary)] transition-colors cursor-pointer"
361+ >
362+ or use a personal access token
363+ </ button >
364+ </ div >
365+ ) }
366+
367+ { /* PAT input */ }
368+ { ! ghAuthenticated && showPatInput && ! deviceFlow && (
369+ < div className = "w-full space-y-2" >
370+ < div className = "flex items-center gap-1.5" >
371+ < div className = "flex-1 relative" >
372+ < Icon icon = "lucide:key-round" width = { 14 } height = { 14 } className = "absolute left-2.5 top-1/2 -translate-y-1/2 text-[var(--text-disabled)]" />
373+ < input
374+ ref = { patInputRef }
375+ type = "password"
376+ value = { patInput }
377+ onChange = { ( e ) => setPatInput ( e . target . value ) }
378+ onKeyDown = { ( e ) => { if ( e . key === 'Enter' ) handlePatSubmit ( ) ; if ( e . key === 'Escape' ) setShowPatInput ( false ) } }
379+ placeholder = "ghp_xxxx..."
380+ autoCapitalize = "off"
381+ autoCorrect = "off"
382+ spellCheck = { false }
383+ 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"
384+ />
385+ </ div >
386+ < button
387+ onClick = { handlePatSubmit }
388+ disabled = { ! patInput . trim ( ) }
389+ 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)]"
390+ >
391+ Save
392+ </ button >
393+ </ div >
394+ < p className = "text-[10px] text-[var(--text-disabled)] leading-relaxed" >
395+ Create a token at{ ' ' }
396+ < a href = "https://github.com/settings/tokens" target = "_blank" rel = "noopener noreferrer" className = "text-[var(--brand)] underline" >
397+ github.com/settings/tokens
398+ </ a >
399+ { ' ' } with < span className = "font-mono" > repo</ span > scope. Stored securely on device.
400+ </ p >
401+ < button
402+ onClick = { ( ) => { setShowPatInput ( false ) ; setPatInput ( '' ) } }
403+ className = "text-[11px] text-[var(--text-disabled)] hover:text-[var(--text-secondary)] cursor-pointer"
404+ >
405+ ← back to sign in
406+ </ button >
407+ </ div >
342408 ) }
343409
344410 { /* Device flow — show code */ }
0 commit comments