Merge pull request #781 from prakhar1605/fix/onboarding-ux-batch

fix(x): add Sign in with ChatGPT to onboarding LLM setup
This commit is contained in:
PRAKHAR PANDEY 2026-07-22 23:23:55 +05:30 committed by GitHub
commit 873187805f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 68 additions and 2 deletions

View file

@ -39,9 +39,14 @@ export function LlmSetupStep({ state }: LlmSetupStepProps) {
updateProviderConfig, handleTestAndSaveLlmConfig, handleTestAndAddAnother,
connectedFlavors, handleNext, handleBack,
upsellDismissed, setUpsellDismissed, handleSwitchToRowboat,
chatgpt,
} = state
const isMoreProvider = moreProviders.some(p => p.id === llmProvider)
// "Sign in with ChatGPT" (below the OpenAI card) is an alternative to a key:
// signing in counts as a connected provider alongside any saved BYOK one,
// which is what unlocks Continue when no API key was entered.
const hasConnectedProvider = connectedFlavors.size > 0 || chatgpt.status.signedIn
// Connect-only, mirroring Settings: entering a key (or base URL) is enough
// and the model is resolved silently at save. openai-compatible is the sole
// exception with a visible Model field — its /models endpoint often doesn't
@ -188,6 +193,56 @@ export function LlmSetupStep({ state }: LlmSetupStepProps) {
</div>
)}
{/* ChatGPT subscription OAuth sign-in shown under the OpenAI card,
mirroring Settings (settings-dialog.tsx, gated on provider ===
"openai"). Alternative to an API key: signs in via the shared
chatgpt:* path, independent of models.json (the Codex model client
consumes the token in core). */}
{llmProvider === "openai" && (
<div className="space-y-2">
<label className="text-xs font-medium text-muted-foreground">
ChatGPT Subscription
</label>
{chatgpt.status.signedIn ? (
<div className="flex items-center justify-between gap-2">
<div className="flex items-center gap-1.5 text-sm text-green-600 dark:text-green-400 min-w-0">
<CheckCircle2 className="size-4 shrink-0" />
<span className="truncate">
Connected as {chatgpt.status.email ?? "your ChatGPT account"}
</span>
</div>
<Button
variant="outline"
size="sm"
className="shrink-0 border-destructive/40 text-destructive hover:bg-destructive/10 hover:text-destructive"
onClick={chatgpt.signOut}
>
Sign Out
</Button>
</div>
) : chatgpt.isSigningIn ? (
<div className="flex items-center justify-between gap-2">
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<Loader2 className="size-4 animate-spin" />
Waiting for browser
</div>
<Button variant="outline" size="sm" className="shrink-0" onClick={chatgpt.cancelSignIn}>
Cancel
</Button>
</div>
) : (
<div className="flex items-center justify-between gap-2">
<span className="text-sm text-muted-foreground">
Use your ChatGPT Plus/Pro subscription
</span>
<Button variant="outline" size="sm" className="shrink-0" onClick={chatgpt.signIn}>
Sign In
</Button>
</div>
)}
</div>
)}
{showBaseURL && (
<div className="space-y-2">
<label className="text-xs font-medium text-muted-foreground">
@ -241,12 +296,12 @@ export function LlmSetupStep({ state }: LlmSetupStepProps) {
</Button>
<Button
onClick={canTest ? handleTestAndSaveLlmConfig : handleNext}
disabled={testState.status === "testing" || (!canTest && connectedFlavors.size === 0)}
disabled={testState.status === "testing" || (!canTest && !hasConnectedProvider)}
className="min-w-[140px]"
>
{testState.status === "testing" ? (
<><Loader2 className="size-4 animate-spin mr-2" />Testing...</>
) : (canTest || connectedFlavors.size === 0) ? (
) : (canTest || !hasConnectedProvider) ? (
"Test & Continue"
) : (
"Continue"

View file

@ -1,5 +1,6 @@
import { useState, useEffect, useCallback } from "react"
import { setGoogleCredentials } from "@/lib/google-credentials-store"
import { useChatGPT } from "@/hooks/useChatGPT"
import { toast } from "sonner"
export interface ProviderState {
@ -44,6 +45,13 @@ export function useOnboardingState(open: boolean, onComplete: (opts?: { startTou
const [connectedFlavors, setConnectedFlavors] = useState<Set<LlmProviderFlavor>>(new Set())
const [showMoreProviders, setShowMoreProviders] = useState(false)
// "Sign in with ChatGPT" (subscription OAuth) is offered below the OpenAI
// card, mirroring Settings. It isn't a LlmProviderFlavor — no API key, no
// models.json entry — it signs in via the dedicated chatgpt:* IPC (same
// path Settings uses) and the Codex model client consumes the token in
// core, leaving the BYOK config machinery untouched.
const chatgpt = useChatGPT()
// OAuth provider states
const [providers, setProviders] = useState<string[]>([])
const [providersLoading, setProvidersLoading] = useState(true)
@ -705,6 +713,9 @@ export function useOnboardingState(open: boolean, onComplete: (opts?: { startTou
handleTestAndSaveLlmConfig,
handleTestAndAddAnother,
// ChatGPT subscription sign-in (shown below the OpenAI card)
chatgpt,
// OAuth state
providers,
providersLoading,