@@ -191,91 +191,106 @@ function PATAuthSection() {
191191
192192 try {
193193 await loginWithPAT ( patToken ) ;
194- // Success - dialog should close automatically via auth context
195194 } catch ( error ) {
196- setPatError ( error instanceof Error ? error . message : "Authentication failed" ) ;
195+ setPatError (
196+ error instanceof Error ? error . message : "Authentication failed"
197+ ) ;
197198 } finally {
198199 setIsValidatingPAT ( false ) ;
199200 }
200201 } ;
201202
202- return (
203- < div className = "border-t pt-4" >
203+ if ( ! showPATInput ) {
204+ return (
204205 < button
205- onClick = { ( ) => setShowPATInput ( ! showPATInput ) }
206- className = "text-sm text-muted-foreground hover:text-foreground transition-colors"
206+ onClick = { ( ) => setShowPATInput ( true ) }
207+ className = "w-full text-center text- sm text-muted-foreground hover:text-foreground transition-colors py-2 "
207208 >
208- { showPATInput ? "← Use OAuth instead" : " Or sign in with Personal Access Token →" }
209+ Or use a Personal Access Token
209210 </ button >
211+ ) ;
212+ }
210213
211- { showPATInput && (
212- < div className = "mt-4 space-y-3" >
213- < div >
214- < label htmlFor = "pat-token" className = "block text-sm font-medium mb-2" >
215- GitHub Personal Access Token
216- </ label >
217- < input
218- id = "pat-token"
219- type = "password"
220- value = { patToken }
221- onChange = { ( e ) => setPatToken ( e . target . value ) }
222- placeholder = "ghp_xxxxxxxxxxxx"
223- className = "w-full px-3 py-2 border rounded-md bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-blue-500"
224- disabled = { isValidatingPAT }
225- onKeyDown = { ( e ) => {
226- if ( e . key === "Enter" && patToken && ! isValidatingPAT ) {
227- handlePATLogin ( ) ;
228- }
229- } }
230- />
231- </ div >
232-
233- { patError && (
234- < div className = "text-sm text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-950/20 p-3 rounded border border-red-200 dark:border-red-900" >
235- { patError }
236- </ div >
214+ return (
215+ < div className = "space-y-3 pt-2" >
216+ < div className = "relative" >
217+ < input
218+ id = "pat-token"
219+ type = "password"
220+ value = { patToken }
221+ onChange = { ( e ) => setPatToken ( e . target . value ) }
222+ placeholder = "Paste your token (ghp_... or github_pat_...)"
223+ className = { cn (
224+ "w-full h-10 px-3 rounded-md border bg-background text-foreground text-sm" ,
225+ "placeholder:text-muted-foreground" ,
226+ "focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background" ,
227+ "disabled:opacity-50 disabled:cursor-not-allowed" ,
228+ patError && "border-destructive focus:ring-destructive"
237229 ) }
230+ disabled = { isValidatingPAT }
231+ autoFocus
232+ onKeyDown = { ( e ) => {
233+ if ( e . key === "Enter" && patToken && ! isValidatingPAT ) {
234+ handlePATLogin ( ) ;
235+ }
236+ if ( e . key === "Escape" ) {
237+ setShowPATInput ( false ) ;
238+ setPatToken ( "" ) ;
239+ setPatError ( null ) ;
240+ }
241+ } }
242+ />
243+ </ div >
238244
239- < div className = "text-xs text-muted-foreground space-y-2" >
240- < p className = "font-medium" > Required scopes:</ p >
241- < ul className = "list-disc list-inside space-y-1 ml-1" >
242- < li >
243- < code className = "bg-muted px-1 py-0.5 rounded" > repo</ code > - Access repositories
244- </ li >
245- < li >
246- < code className = "bg-muted px-1 py-0.5 rounded" > read:user</ code > - Read user profile
247- </ li >
248- </ ul >
249- < a
250- href = "https://github.com/settings/tokens/new?scopes=repo,read:user& description = Pulldash "
251- target = "_blank"
252- rel = "noopener noreferrer"
253- className = "inline-flex items-center gap-1 text-blue-600 dark:text-blue-400 hover:underline mt-2"
254- >
255- Create a token on GitHub
256- < ExternalLink className = "w-3 h-3" />
257- </ a >
258- </ div >
259-
260- < Button
261- onClick = { handlePATLogin }
262- disabled = { ! patToken || isValidatingPAT }
263- className = "w-full h-10 gap-2"
264- >
265- { isValidatingPAT ? (
266- < >
267- < Loader2 className = "w-4 h-4 animate-spin" />
268- Validating...
269- </ >
270- ) : (
271- < >
272- < Github className = "w-4 h-4" />
273- Sign in with PAT
274- </ >
275- ) }
276- </ Button >
245+ { patError && (
246+ < div className = "flex items-start gap-2 p-2.5 rounded-md bg-destructive/10 border border-destructive/20 text-destructive text-sm" >
247+ < AlertCircle className = "w-4 h-4 shrink-0 mt-0.5" />
248+ < span > { patError } </ span >
277249 </ div >
278250 ) }
251+
252+ < div className = "flex gap-2" >
253+ < Button
254+ onClick = { handlePATLogin }
255+ disabled = { ! patToken || isValidatingPAT }
256+ className = "flex-1 h-9 gap-2"
257+ >
258+ { isValidatingPAT ? (
259+ < >
260+ < Loader2 className = "w-4 h-4 animate-spin" />
261+ Validating...
262+ </ >
263+ ) : (
264+ "Sign in"
265+ ) }
266+ </ Button >
267+ < Button
268+ variant = "ghost"
269+ onClick = { ( ) => {
270+ setShowPATInput ( false ) ;
271+ setPatToken ( "" ) ;
272+ setPatError ( null ) ;
273+ } }
274+ className = "h-9 px-3 text-muted-foreground"
275+ disabled = { isValidatingPAT }
276+ >
277+ Cancel
278+ </ Button >
279+ </ div >
280+
281+ < p className = "text-xs text-muted-foreground" >
282+ Requires{ " " }
283+ < code className = "px-1 py-0.5 rounded bg-muted font-mono" > repo</ code > { " " }
284+ scope.{ " " }
285+ < a
286+ href = "https://github.com/settings/tokens/new?scopes=repo,read:user& description = Pulldash "
287+ target = "_blank"
288+ rel = "noopener noreferrer"
289+ className = "text-foreground hover:underline"
290+ >
291+ Create token →
292+ </ a >
293+ </ p >
279294 </ div >
280295 ) ;
281296}
0 commit comments