Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 141 additions & 16 deletions webview-ui/src/components/welcome/WelcomeViewProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useCallback, useState } from "react"
import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"
import { useCallback, useEffect, useRef, useState } from "react"
import { VSCodeLink, VSCodeProgressRing, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"

import type { ProviderSettings } from "@roo-code/types"

Expand All @@ -14,14 +14,47 @@ import { Tab, TabContent } from "../common/Tab"

import RooHero from "./RooHero"
import { Trans } from "react-i18next"
import { ArrowLeft } from "lucide-react"

type ProviderOption = "roo" | "custom"

const WelcomeViewProvider = () => {
const { apiConfiguration, currentApiConfigName, setApiConfiguration, uriScheme } = useExtensionState()
const { apiConfiguration, currentApiConfigName, setApiConfiguration, uriScheme, cloudIsAuthenticated } =
useExtensionState()
const { t } = useAppTranslation()
const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined)
const [selectedProvider, setSelectedProvider] = useState<ProviderOption>("roo")
const [authInProgress, setAuthInProgress] = useState(false)
const [showManualEntry, setShowManualEntry] = useState(false)
const [manualUrl, setManualUrl] = useState("")
const manualUrlInputRef = useRef<HTMLInputElement | null>(null)

// When auth completes during the provider signup flow, save the Roo config
// This will cause showWelcome to become false and navigate to chat
useEffect(() => {
if (cloudIsAuthenticated && authInProgress) {
// Auth completed from provider signup flow - save the config now
const rooConfig: ProviderSettings = {
apiProvider: "roo",
}
vscode.postMessage({
type: "upsertApiConfiguration",
text: currentApiConfigName,
apiConfiguration: rooConfig,
})
setAuthInProgress(false)
setShowManualEntry(false)
}
}, [cloudIsAuthenticated, authInProgress, currentApiConfigName])

// Focus the manual URL input when it becomes visible
useEffect(() => {
if (showManualEntry && manualUrlInputRef.current) {
setTimeout(() => {
manualUrlInputRef.current?.focus()
}, 50)
}
}, [showManualEntry])

// Memoize the setApiConfigurationField function to pass to ApiOptions
const setApiConfigurationFieldForApiOptions = useCallback(
Expand All @@ -33,20 +66,14 @@ const WelcomeViewProvider = () => {

const handleGetStarted = useCallback(() => {
if (selectedProvider === "roo") {
// Set the Roo provider configuration
const rooConfig: ProviderSettings = {
apiProvider: "roo",
}

// Save the Roo provider configuration
vscode.postMessage({
type: "upsertApiConfiguration",
text: currentApiConfigName,
apiConfiguration: rooConfig,
})

// Then trigger cloud sign-in with provider signup flow
// Trigger cloud sign-in with provider signup flow
// NOTE: We intentionally do NOT save the API configuration yet.
// The configuration will be saved by the extension after auth completes.
// This keeps showWelcome true so we can show the waiting state.
vscode.postMessage({ type: "rooCloudSignIn", useProviderSignup: true })

// Show the waiting state
setAuthInProgress(true)
} else {
// Use custom provider - validate first
const error = apiConfiguration ? validateApiConfiguration(apiConfiguration) : undefined
Expand All @@ -61,6 +88,104 @@ const WelcomeViewProvider = () => {
}
}, [selectedProvider, apiConfiguration, currentApiConfigName])

const handleGoBack = useCallback(() => {
setAuthInProgress(false)
setShowManualEntry(false)
setManualUrl("")
}, [])

const handleManualUrlChange = (e: any) => {
const url = e.target.value
setManualUrl(url)

// Auto-trigger authentication when a complete URL is pasted
setTimeout(() => {
if (url.trim() && url.includes("://") && url.includes("/auth/clerk/callback")) {
vscode.postMessage({ type: "rooCloudManualUrl", text: url.trim() })
}
}, 100)
}

const handleKeyDown = (e: any) => {
if (e.key === "Enter") {
const url = manualUrl.trim()
if (url && url.includes("://") && url.includes("/auth/clerk/callback")) {
vscode.postMessage({ type: "rooCloudManualUrl", text: url })
}
}
}

const handleOpenSignupUrl = () => {
vscode.postMessage({ type: "rooCloudSignIn", useProviderSignup: true })
}

// Render the waiting for cloud state
if (authInProgress) {
return (
<Tab>
<TabContent className="flex flex-col gap-4 p-6">
<div className="flex flex-col items-start gap-4 pt-8">
<VSCodeProgressRing className="size-6" />
<h2 className="mt-0 mb-0 text-lg font-semibold">{t("welcome:waitingForCloud.heading")}</h2>
<p className="text-sm text-vscode-descriptionForeground mt-0">
{t("welcome:waitingForCloud.description")}
</p>

<p className="text-sm text-vscode-descriptionForeground mt-2">
<Trans
i18nKey="welcome:waitingForCloud.noPrompt"
components={{
clickHere: (
<button
onClick={handleOpenSignupUrl}
className="text-vscode-textLink-foreground hover:text-vscode-textLink-activeForeground underline cursor-pointer bg-transparent border-none p-0 text-sm"
/>
),
}}
/>
</p>

<p className="text-sm text-vscode-descriptionForeground">
<Trans
i18nKey="welcome:waitingForCloud.havingTrouble"
components={{
clickHere: (
<button
onClick={() => setShowManualEntry(true)}
className="text-vscode-textLink-foreground hover:text-vscode-textLink-activeForeground underline cursor-pointer bg-transparent border-none p-0 text-sm"
/>
),
}}
/>
</p>

{showManualEntry && (
<div className="mt-2 w-full max-w-sm">
<p className="text-sm text-vscode-descriptionForeground mb-2">
{t("welcome:waitingForCloud.pasteUrl")}
</p>
<VSCodeTextField
ref={manualUrlInputRef as any}
value={manualUrl}
onChange={handleManualUrlChange}
onKeyDown={handleKeyDown}
placeholder="vscode://RooVeterinaryInc.roo-cline/auth/clerk/callback?state=..."
className="w-full"
/>
</div>
)}
</div>
</TabContent>
<div className="sticky bottom-0 bg-vscode-sideBar-background p-4 border-t border-vscode-panel-border">
<Button onClick={handleGoBack} variant="secondary" className="flex items-center gap-2">
<ArrowLeft className="size-4" />
{t("welcome:waitingForCloud.goBack")}
</Button>
</div>
</Tab>
)
}

return (
<Tab>
<TabContent className="flex flex-col gap-4 p-6">
Expand Down
8 changes: 8 additions & 0 deletions webview-ui/src/i18n/locales/ca/welcome.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions webview-ui/src/i18n/locales/de/welcome.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions webview-ui/src/i18n/locales/en/welcome.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@
"useAnotherProviderDescription": "Enter an API key and get going.",
"getStarted": "Get started"
},
"waitingForCloud": {
"heading": "Taking you to Roo Code Cloud...",
"description": "Complete sign-up in your browser, then you'll return here automatically.",
"noPrompt": "If you don't get prompted to open a URL, <clickHere>click here</clickHere>.",
"havingTrouble": "If you've completed the sign up but are having trouble, <clickHere>click here</clickHere>.",
"pasteUrl": "Paste the callback URL from your browser:",
"goBack": "Go back"
},
"startRouter": "We recommend using an LLM Router:",
"startCustom": "Or you can bring your provider API key:",
"telemetry": {
Expand Down
8 changes: 8 additions & 0 deletions webview-ui/src/i18n/locales/es/welcome.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions webview-ui/src/i18n/locales/fr/welcome.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions webview-ui/src/i18n/locales/hi/welcome.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions webview-ui/src/i18n/locales/id/welcome.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions webview-ui/src/i18n/locales/it/welcome.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions webview-ui/src/i18n/locales/ja/welcome.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions webview-ui/src/i18n/locales/ko/welcome.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions webview-ui/src/i18n/locales/nl/welcome.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions webview-ui/src/i18n/locales/pl/welcome.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions webview-ui/src/i18n/locales/pt-BR/welcome.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions webview-ui/src/i18n/locales/ru/welcome.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading