|
@@ -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"
|
|
import type { ProviderSettings } from "@roo-code/types"
|
|
|
|
|
|
|
@@ -14,14 +14,47 @@ import { Tab, TabContent } from "../common/Tab"
|
|
|
|
|
|
|
|
import RooHero from "./RooHero"
|
|
import RooHero from "./RooHero"
|
|
|
import { Trans } from "react-i18next"
|
|
import { Trans } from "react-i18next"
|
|
|
|
|
+import { ArrowLeft } from "lucide-react"
|
|
|
|
|
|
|
|
type ProviderOption = "roo" | "custom"
|
|
type ProviderOption = "roo" | "custom"
|
|
|
|
|
|
|
|
const WelcomeViewProvider = () => {
|
|
const WelcomeViewProvider = () => {
|
|
|
- const { apiConfiguration, currentApiConfigName, setApiConfiguration, uriScheme } = useExtensionState()
|
|
|
|
|
|
|
+ const { apiConfiguration, currentApiConfigName, setApiConfiguration, uriScheme, cloudIsAuthenticated } =
|
|
|
|
|
+ useExtensionState()
|
|
|
const { t } = useAppTranslation()
|
|
const { t } = useAppTranslation()
|
|
|
const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined)
|
|
const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined)
|
|
|
const [selectedProvider, setSelectedProvider] = useState<ProviderOption>("roo")
|
|
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
|
|
// Memoize the setApiConfigurationField function to pass to ApiOptions
|
|
|
const setApiConfigurationFieldForApiOptions = useCallback(
|
|
const setApiConfigurationFieldForApiOptions = useCallback(
|
|
@@ -33,20 +66,14 @@ const WelcomeViewProvider = () => {
|
|
|
|
|
|
|
|
const handleGetStarted = useCallback(() => {
|
|
const handleGetStarted = useCallback(() => {
|
|
|
if (selectedProvider === "roo") {
|
|
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 })
|
|
vscode.postMessage({ type: "rooCloudSignIn", useProviderSignup: true })
|
|
|
|
|
+
|
|
|
|
|
+ // Show the waiting state
|
|
|
|
|
+ setAuthInProgress(true)
|
|
|
} else {
|
|
} else {
|
|
|
// Use custom provider - validate first
|
|
// Use custom provider - validate first
|
|
|
const error = apiConfiguration ? validateApiConfiguration(apiConfiguration) : undefined
|
|
const error = apiConfiguration ? validateApiConfiguration(apiConfiguration) : undefined
|
|
@@ -61,6 +88,104 @@ const WelcomeViewProvider = () => {
|
|
|
}
|
|
}
|
|
|
}, [selectedProvider, apiConfiguration, currentApiConfigName])
|
|
}, [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 (
|
|
return (
|
|
|
<Tab>
|
|
<Tab>
|
|
|
<TabContent className="flex flex-col gap-4 p-6">
|
|
<TabContent className="flex flex-col gap-4 p-6">
|