'use client'; import React, { useState, useEffect, useCallback } from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { ExternalLink, Eye, EyeOff, Loader2 } from 'lucide-react'; import { ConnectionBadge } from '@/components/settings/connection-badge'; import { toast } from 'sonner'; import { configManager } from '@/lib/config/storage'; import { track } from '@/lib/telemetry'; import { apiFetch } from '@/lib/api/backend-status'; interface GitHubAuthPanelProps { onAuthChange?: () => void; } export function GitHubAuthPanel({ onAuthChange }: GitHubAuthPanelProps) { const [tokenInput, setTokenInput] = useState(''); const [showToken, setShowToken] = useState(false); const [isConnecting, setIsConnecting] = useState(false); const [isLoading, setIsLoading] = useState(false); const [isConnected, setIsConnected] = useState(() => !!configManager.getGitHubAuth()); const [username, setUsername] = useState(() => configManager.getGitHubAuth()?.username); const dispatchAuthEvent = useCallback((hasKey: boolean) => { onAuthChange?.(); window.dispatchEvent(new CustomEvent('apiKeyUpdated', { detail: { provider: 'github', hasKey } })); }, [onAuthChange]); useEffect(() => { const handleAuthUpdate = (e: Event) => { const detail = (e as CustomEvent).detail; if (detail?.provider === 'github') { const auth = configManager.getGitHubAuth(); setIsConnected(detail.hasKey); if (auth?.username) setUsername(auth.username); } }; window.addEventListener('apiKeyUpdated', handleAuthUpdate); return () => window.removeEventListener('apiKeyUpdated', handleAuthUpdate); }, []); const handleConnect = async () => { const key = tokenInput.trim(); if (!key) { toast.error('Please enter a Personal Access Token'); return; } setIsConnecting(true); try { const response = await apiFetch('/api/validate-key', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ apiKey: key, provider: 'github' }) }); if (response.ok) { const { valid, username: githubUsername } = await response.json(); if (valid) { configManager.setGitHubAuth({ access_token: key, username: githubUsername }); setIsConnected(true); setUsername(githubUsername); setTokenInput(''); toast.success(`Connected to GitHub as ${githubUsername}`); track('connection_added', { provider: 'github' }); dispatchAuthEvent(true); } else { toast.error('Invalid token. Check that it has "repo" scope.'); } } else { toast.error('Failed to validate token'); } } catch { toast.error('Failed to validate token. Please try again.'); } finally { setIsConnecting(false); } }; const handleDisconnect = () => { setIsLoading(true); try { configManager.clearGitHubAuth(); setIsConnected(false); setUsername(undefined); setTokenInput(''); toast.success('Disconnected from GitHub'); track('connection_removed', { provider: 'github' }); dispatchAuthEvent(false); } catch { toast.error('Failed to disconnect'); } finally { setIsLoading(false); } }; if (isConnected) { return (
Authenticated as {username}.{' '} Manage Tokens } onDisconnect={handleDisconnect} disconnecting={isLoading} />
); } return (

Connect your GitHub account to export projects directly to repositories.

setTokenInput(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter' && tokenInput.trim()) handleConnect(); }} placeholder="ghp_..." className="pr-10" disabled={isConnecting} />

How to get a Personal Access Token:

  1. Go to{' '} GitHub Token Settings
  2. Generate a new token (classic or fine-grained)
  3. Ensure it has repo scope (full control of private repositories)
  4. Paste the token above and click Connect
); }