mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
Test a provider-oriented welcome screen (#9484)
Co-authored-by: roomote[bot] <219738659+roomote[bot]@users.noreply.github.com>
This commit is contained in:
parent
1dd223d240
commit
7edc86bb52
31 changed files with 455 additions and 34 deletions
|
|
@ -178,9 +178,9 @@ export class CloudService extends EventEmitter<CloudServiceEvents> implements Di
|
|||
|
||||
// AuthService
|
||||
|
||||
public async login(landingPageSlug?: string): Promise<void> {
|
||||
public async login(landingPageSlug?: string, useProviderSignup: boolean = false): Promise<void> {
|
||||
this.ensureInitialized()
|
||||
return this.authService!.login(landingPageSlug)
|
||||
return this.authService!.login(landingPageSlug, useProviderSignup)
|
||||
}
|
||||
|
||||
public async logout(): Promise<void> {
|
||||
|
|
@ -245,9 +245,10 @@ export class CloudService extends EventEmitter<CloudServiceEvents> implements Di
|
|||
code: string | null,
|
||||
state: string | null,
|
||||
organizationId?: string | null,
|
||||
providerModel?: string | null,
|
||||
): Promise<void> {
|
||||
this.ensureInitialized()
|
||||
return this.authService!.handleCallback(code, state, organizationId)
|
||||
return this.authService!.handleCallback(code, state, organizationId, providerModel)
|
||||
}
|
||||
|
||||
public async switchOrganization(organizationId: string | null): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ export class StaticTokenAuthService extends EventEmitter<AuthServiceEvents> impl
|
|||
this.emit("user-info", { userInfo: this.userInfo })
|
||||
}
|
||||
|
||||
public async login(): Promise<void> {
|
||||
public async login(_landingPageSlug?: string, _useProviderSignup?: boolean): Promise<void> {
|
||||
throw new Error("Authentication methods are disabled in StaticTokenAuthService")
|
||||
}
|
||||
|
||||
|
|
@ -59,6 +59,7 @@ export class StaticTokenAuthService extends EventEmitter<AuthServiceEvents> impl
|
|||
_code: string | null,
|
||||
_state: string | null,
|
||||
_organizationId?: string | null,
|
||||
_providerModel?: string | null,
|
||||
): Promise<void> {
|
||||
throw new Error("Authentication methods are disabled in StaticTokenAuthService")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -252,8 +252,9 @@ export class WebAuthService extends EventEmitter<AuthServiceEvents> implements A
|
|||
* and opening the browser to the authorization URL.
|
||||
*
|
||||
* @param landingPageSlug Optional slug of a specific landing page (e.g., "supernova", "special-offer", etc.)
|
||||
* @param useProviderSignup If true, uses provider signup flow (/extension/provider-sign-up). If false, uses standard sign-in (/extension/sign-in). Defaults to false.
|
||||
*/
|
||||
public async login(landingPageSlug?: string): Promise<void> {
|
||||
public async login(landingPageSlug?: string, useProviderSignup: boolean = false): Promise<void> {
|
||||
try {
|
||||
const vscode = await importVscode()
|
||||
|
||||
|
|
@ -272,10 +273,12 @@ export class WebAuthService extends EventEmitter<AuthServiceEvents> implements A
|
|||
auth_redirect: `${vscode.env.uriScheme}://${publisher}.${name}`,
|
||||
})
|
||||
|
||||
// Use landing page URL if slug is provided, otherwise use default sign-in URL
|
||||
// Use landing page URL if slug is provided, otherwise use provider sign-up or sign-in URL based on parameter
|
||||
const url = landingPageSlug
|
||||
? `${getRooCodeApiUrl()}/l/${landingPageSlug}?${params.toString()}`
|
||||
: `${getRooCodeApiUrl()}/extension/sign-in?${params.toString()}`
|
||||
: useProviderSignup
|
||||
? `${getRooCodeApiUrl()}/extension/provider-sign-up?${params.toString()}`
|
||||
: `${getRooCodeApiUrl()}/extension/sign-in?${params.toString()}`
|
||||
|
||||
await vscode.env.openExternal(vscode.Uri.parse(url))
|
||||
} catch (error) {
|
||||
|
|
@ -294,11 +297,13 @@ export class WebAuthService extends EventEmitter<AuthServiceEvents> implements A
|
|||
* @param code The authorization code from the callback
|
||||
* @param state The state parameter from the callback
|
||||
* @param organizationId The organization ID from the callback (null for personal accounts)
|
||||
* @param providerModel The model ID selected during signup (optional)
|
||||
*/
|
||||
public async handleCallback(
|
||||
code: string | null,
|
||||
state: string | null,
|
||||
organizationId?: string | null,
|
||||
providerModel?: string | null,
|
||||
): Promise<void> {
|
||||
if (!code || !state) {
|
||||
const vscode = await importVscode()
|
||||
|
|
@ -326,6 +331,12 @@ export class WebAuthService extends EventEmitter<AuthServiceEvents> implements A
|
|||
|
||||
await this.storeCredentials(credentials)
|
||||
|
||||
// Store the provider model if provided
|
||||
if (providerModel) {
|
||||
await this.context.globalState.update("roo-provider-model", providerModel)
|
||||
this.log(`[auth] Stored provider model: ${providerModel}`)
|
||||
}
|
||||
|
||||
const vscode = await importVscode()
|
||||
|
||||
if (vscode) {
|
||||
|
|
|
|||
|
|
@ -296,12 +296,22 @@ describe("CloudService", () => {
|
|||
|
||||
it("should delegate handleAuthCallback to AuthService", async () => {
|
||||
await cloudService.handleAuthCallback("code", "state")
|
||||
expect(mockAuthService.handleCallback).toHaveBeenCalledWith("code", "state", undefined)
|
||||
expect(mockAuthService.handleCallback).toHaveBeenCalledWith("code", "state", undefined, undefined)
|
||||
})
|
||||
|
||||
it("should delegate handleAuthCallback with organizationId to AuthService", async () => {
|
||||
await cloudService.handleAuthCallback("code", "state", "org_123")
|
||||
expect(mockAuthService.handleCallback).toHaveBeenCalledWith("code", "state", "org_123")
|
||||
expect(mockAuthService.handleCallback).toHaveBeenCalledWith("code", "state", "org_123", undefined)
|
||||
})
|
||||
|
||||
it("should delegate handleAuthCallback with providerModel to AuthService", async () => {
|
||||
await cloudService.handleAuthCallback("code", "state", "org_123", "xai/grok-code-fast-1")
|
||||
expect(mockAuthService.handleCallback).toHaveBeenCalledWith(
|
||||
"code",
|
||||
"state",
|
||||
"org_123",
|
||||
"xai/grok-code-fast-1",
|
||||
)
|
||||
})
|
||||
|
||||
it("should return stored organization ID from AuthService", () => {
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ describe("WebAuthService", () => {
|
|||
)
|
||||
})
|
||||
|
||||
it("should use package.json values for redirect URI", async () => {
|
||||
it("should use package.json values for redirect URI with default sign-in endpoint", async () => {
|
||||
const mockOpenExternal = vi.fn()
|
||||
const vscode = await import("vscode")
|
||||
vi.mocked(vscode.env.openExternal).mockImplementation(mockOpenExternal)
|
||||
|
|
@ -281,6 +281,26 @@ describe("WebAuthService", () => {
|
|||
expect(calledUri.toString()).toBe(expectedUrl)
|
||||
})
|
||||
|
||||
it("should use provider signup URL when useProviderSignup is true", async () => {
|
||||
const mockOpenExternal = vi.fn()
|
||||
const vscode = await import("vscode")
|
||||
vi.mocked(vscode.env.openExternal).mockImplementation(mockOpenExternal)
|
||||
|
||||
await authService.login(undefined, true)
|
||||
|
||||
const expectedUrl =
|
||||
"https://api.test.com/extension/provider-sign-up?state=746573742d72616e646f6d2d6279746573&auth_redirect=vscode%3A%2F%2FRooVeterinaryInc.roo-cline"
|
||||
expect(mockOpenExternal).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
toString: expect.any(Function),
|
||||
}),
|
||||
)
|
||||
|
||||
// Verify the actual URL
|
||||
const calledUri = mockOpenExternal.mock.calls[0]?.[0]
|
||||
expect(calledUri.toString()).toBe(expectedUrl)
|
||||
})
|
||||
|
||||
it("should handle errors during login", async () => {
|
||||
vi.mocked(crypto.randomBytes).mockImplementation(() => {
|
||||
throw new Error("Crypto error")
|
||||
|
|
@ -351,6 +371,33 @@ describe("WebAuthService", () => {
|
|||
expect(mockShowInfo).toHaveBeenCalledWith("Successfully authenticated with Roo Code Cloud")
|
||||
})
|
||||
|
||||
it("should store provider model when provided in callback", async () => {
|
||||
const storedState = "valid-state"
|
||||
mockContext.globalState.get.mockReturnValue(storedState)
|
||||
|
||||
// Mock successful Clerk sign-in response
|
||||
const mockResponse = {
|
||||
ok: true,
|
||||
json: () =>
|
||||
Promise.resolve({
|
||||
response: { created_session_id: "session-123" },
|
||||
}),
|
||||
headers: {
|
||||
get: (header: string) => (header === "authorization" ? "Bearer token-123" : null),
|
||||
},
|
||||
}
|
||||
mockFetch.mockResolvedValue(mockResponse)
|
||||
|
||||
const vscode = await import("vscode")
|
||||
const mockShowInfo = vi.fn()
|
||||
vi.mocked(vscode.window.showInformationMessage).mockImplementation(mockShowInfo)
|
||||
|
||||
await authService.handleCallback("auth-code", storedState, null, "xai/grok-code-fast-1")
|
||||
|
||||
expect(mockContext.globalState.update).toHaveBeenCalledWith("roo-provider-model", "xai/grok-code-fast-1")
|
||||
expect(mockLog).toHaveBeenCalledWith("[auth] Stored provider model: xai/grok-code-fast-1")
|
||||
})
|
||||
|
||||
it("should handle Clerk API errors", async () => {
|
||||
const storedState = "valid-state"
|
||||
mockContext.globalState.get.mockReturnValue(storedState)
|
||||
|
|
|
|||
|
|
@ -239,9 +239,14 @@ export interface AuthService extends EventEmitter<AuthServiceEvents> {
|
|||
broadcast(): void
|
||||
|
||||
// Authentication methods
|
||||
login(landingPageSlug?: string): Promise<void>
|
||||
login(landingPageSlug?: string, useProviderSignup?: boolean): Promise<void>
|
||||
logout(): Promise<void>
|
||||
handleCallback(code: string | null, state: string | null, organizationId?: string | null): Promise<void>
|
||||
handleCallback(
|
||||
code: string | null,
|
||||
state: string | null,
|
||||
organizationId?: string | null,
|
||||
providerModel?: string | null,
|
||||
): Promise<void>
|
||||
switchOrganization(organizationId: string | null): Promise<void>
|
||||
|
||||
// State methods
|
||||
|
|
|
|||
|
|
@ -40,11 +40,13 @@ export const handleUri = async (uri: vscode.Uri) => {
|
|||
const code = query.get("code")
|
||||
const state = query.get("state")
|
||||
const organizationId = query.get("organizationId")
|
||||
const providerModel = query.get("provider_model")
|
||||
|
||||
await CloudService.instance.handleAuthCallback(
|
||||
code,
|
||||
state,
|
||||
organizationId === "null" ? null : organizationId,
|
||||
providerModel,
|
||||
)
|
||||
break
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2242,7 +2242,8 @@ export const webviewMessageHandler = async (
|
|||
case "rooCloudSignIn": {
|
||||
try {
|
||||
TelemetryService.instance.captureEvent(TelemetryEventName.AUTHENTICATION_INITIATED)
|
||||
await CloudService.instance.login()
|
||||
// Use provider signup flow if useProviderSignup is explicitly true
|
||||
await CloudService.instance.login(undefined, message.useProviderSignup ?? false)
|
||||
} catch (error) {
|
||||
provider.log(`AuthService#login failed: ${error}`)
|
||||
vscode.window.showErrorMessage("Sign in failed.")
|
||||
|
|
|
|||
|
|
@ -162,6 +162,31 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||
|
||||
if (data.state === "active-session" || data.state === "logged-out") {
|
||||
await handleRooModelsCache()
|
||||
|
||||
// Apply stored provider model to API configuration if present
|
||||
if (data.state === "active-session") {
|
||||
try {
|
||||
const storedModel = context.globalState.get<string>("roo-provider-model")
|
||||
if (storedModel) {
|
||||
cloudLogger(`[authStateChangedHandler] Applying stored provider model: ${storedModel}`)
|
||||
// Get the current API configuration name
|
||||
const currentConfigName =
|
||||
provider.contextProxy.getGlobalState("currentApiConfigName") || "default"
|
||||
// Update it with the stored model using upsertProviderProfile
|
||||
await provider.upsertProviderProfile(currentConfigName, {
|
||||
apiProvider: "roo",
|
||||
apiModelId: storedModel,
|
||||
})
|
||||
// Clear the stored model after applying
|
||||
await context.globalState.update("roo-provider-model", undefined)
|
||||
cloudLogger(`[authStateChangedHandler] Applied and cleared stored provider model`)
|
||||
}
|
||||
} catch (error) {
|
||||
cloudLogger(
|
||||
`[authStateChangedHandler] Failed to apply stored provider model: ${error instanceof Error ? error.message : String(error)}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -224,6 +224,7 @@ export interface WebviewMessage {
|
|||
upsellId?: string // For dismissUpsell
|
||||
list?: string[] // For dismissedUpsells response
|
||||
organizationId?: string | null // For organization switching
|
||||
useProviderSignup?: boolean // For rooCloudSignIn to use provider signup flow
|
||||
codeIndexSettings?: {
|
||||
// Global state settings
|
||||
codebaseIndexEnabled: boolean
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React, { useCallback, useEffect, useRef, useState, useMemo } from "react"
|
||||
import { useEvent } from "react-use"
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
|
||||
import posthog from "posthog-js"
|
||||
|
||||
import { ExtensionMessage } from "@roo/ExtensionMessage"
|
||||
import TranslationProvider from "./i18n/TranslationContext"
|
||||
|
|
@ -15,6 +16,7 @@ import ChatView, { ChatViewRef } from "./components/chat/ChatView"
|
|||
import HistoryView from "./components/history/HistoryView"
|
||||
import SettingsView, { SettingsViewRef } from "./components/settings/SettingsView"
|
||||
import WelcomeView from "./components/welcome/WelcomeView"
|
||||
import WelcomeViewProvider from "./components/welcome/WelcomeViewProvider"
|
||||
import McpView from "./components/mcp/McpView"
|
||||
import { MarketplaceView } from "./components/marketplace/MarketplaceView"
|
||||
import ModesView from "./components/modes/ModesView"
|
||||
|
|
@ -81,6 +83,16 @@ const App = () => {
|
|||
mdmCompliant,
|
||||
} = useExtensionState()
|
||||
|
||||
const [useProviderSignupView, setUseProviderSignupView] = useState(false)
|
||||
|
||||
// Check PostHog feature flag for provider signup view
|
||||
useEffect(() => {
|
||||
posthog.onFeatureFlags(function () {
|
||||
// Feature flag for new provider-focused welcome view
|
||||
setUseProviderSignupView(posthog?.getFeatureFlag("welcome-provider-signup") === "test")
|
||||
})
|
||||
}, [])
|
||||
|
||||
// Create a persistent state manager
|
||||
const marketplaceStateManager = useMemo(() => new MarketplaceViewStateManager(), [])
|
||||
|
||||
|
|
@ -247,7 +259,11 @@ const App = () => {
|
|||
// Do not conditionally load ChatView, it's expensive and there's state we
|
||||
// don't want to lose (user input, disableInput, askResponse promise, etc.)
|
||||
return showWelcome ? (
|
||||
<WelcomeView />
|
||||
useProviderSignupView ? (
|
||||
<WelcomeViewProvider />
|
||||
) : (
|
||||
<WelcomeView />
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
{tab === "modes" && <ModesView onDone={() => switchTab("chat")} />}
|
||||
|
|
|
|||
|
|
@ -63,9 +63,9 @@ const WelcomeView = () => {
|
|||
<Tab>
|
||||
<TabContent className="flex flex-col gap-4 p-6">
|
||||
<RooHero />
|
||||
<h2 className="mt-0 mb-4 text-xl text-center">{t("welcome:greeting")}</h2>
|
||||
<h2 className="mt-0 mb-4 text-xl">{t("welcome:greeting")}</h2>
|
||||
|
||||
<div className="text-base text-vscode-foreground py-2 px-2 mb-4">
|
||||
<div className="text-base text-vscode-foreground py-2 mb-4">
|
||||
<p className="mb-3 leading-relaxed">
|
||||
<Trans i18nKey="welcome:introduction" />
|
||||
</p>
|
||||
|
|
|
|||
157
webview-ui/src/components/welcome/WelcomeViewProvider.tsx
Normal file
157
webview-ui/src/components/welcome/WelcomeViewProvider.tsx
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
import { useCallback, useState } from "react"
|
||||
import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"
|
||||
|
||||
import type { ProviderSettings } from "@roo-code/types"
|
||||
|
||||
import { useExtensionState } from "@src/context/ExtensionStateContext"
|
||||
import { validateApiConfiguration } from "@src/utils/validate"
|
||||
import { vscode } from "@src/utils/vscode"
|
||||
import { useAppTranslation } from "@src/i18n/TranslationContext"
|
||||
import { Button } from "@src/components/ui"
|
||||
|
||||
import ApiOptions from "../settings/ApiOptions"
|
||||
import { Tab, TabContent } from "../common/Tab"
|
||||
|
||||
import RooHero from "./RooHero"
|
||||
import { Trans } from "react-i18next"
|
||||
|
||||
type ProviderOption = "roo" | "custom"
|
||||
|
||||
const WelcomeViewProvider = () => {
|
||||
const { apiConfiguration, currentApiConfigName, setApiConfiguration, uriScheme } = useExtensionState()
|
||||
const { t } = useAppTranslation()
|
||||
const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined)
|
||||
const [selectedProvider, setSelectedProvider] = useState<ProviderOption>("roo")
|
||||
|
||||
// Memoize the setApiConfigurationField function to pass to ApiOptions
|
||||
const setApiConfigurationFieldForApiOptions = useCallback(
|
||||
<K extends keyof ProviderSettings>(field: K, value: ProviderSettings[K]) => {
|
||||
setApiConfiguration({ [field]: value })
|
||||
},
|
||||
[setApiConfiguration], // setApiConfiguration from context is stable
|
||||
)
|
||||
|
||||
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
|
||||
vscode.postMessage({ type: "rooCloudSignIn", useProviderSignup: true })
|
||||
} else {
|
||||
// Use custom provider - validate first
|
||||
const error = apiConfiguration ? validateApiConfiguration(apiConfiguration) : undefined
|
||||
|
||||
if (error) {
|
||||
setErrorMessage(error)
|
||||
return
|
||||
}
|
||||
|
||||
setErrorMessage(undefined)
|
||||
vscode.postMessage({ type: "upsertApiConfiguration", text: currentApiConfigName, apiConfiguration })
|
||||
}
|
||||
}, [selectedProvider, apiConfiguration, currentApiConfigName])
|
||||
|
||||
return (
|
||||
<Tab>
|
||||
<TabContent className="flex flex-col gap-4 p-6">
|
||||
<RooHero />
|
||||
<h2 className="mt-0 mb-0 text-xl">{t("welcome:greeting")}</h2>
|
||||
|
||||
<div className="text-base text-vscode-foreground">
|
||||
<p className="mb-3 leading-relaxed">
|
||||
<Trans i18nKey="welcome:introduction" />
|
||||
</p>
|
||||
<p className="mb-0 leading-relaxed">
|
||||
<Trans i18nKey="welcome:chooseProvider" />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
{/* Roo Code Cloud Provider Option */}
|
||||
<div
|
||||
className="flex items-start gap-3 p-4 mb-3 border border-vscode-panel-border rounded-md cursor-pointer hover:bg-vscode-list-hoverBackground"
|
||||
onClick={() => setSelectedProvider("roo")}>
|
||||
<input
|
||||
type="radio"
|
||||
name="provider"
|
||||
checked={selectedProvider === "roo"}
|
||||
onChange={() => setSelectedProvider("roo")}
|
||||
className="mt-1"
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<span className="text-sm font-semibold mb-2 block">
|
||||
{t("welcome:providerSignup.rooCloudProvider")}
|
||||
</span>
|
||||
<p className="text-xs text-vscode-descriptionForeground mb-1">
|
||||
{t("welcome:providerSignup.rooCloudDescription")} (
|
||||
<VSCodeLink
|
||||
href="https://roocode.com/provider/pricing?utm_source=extension&utm_medium=welcome-screen&utm_campaign=provider-signup&utm_content=learn-more"
|
||||
className="text-xs">
|
||||
{t("welcome:providerSignup.learnMore")}
|
||||
</VSCodeLink>
|
||||
).
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Use Another Provider Option */}
|
||||
<div
|
||||
className="flex items-start gap-3 p-4 mb-4 border border-vscode-panel-border rounded-md cursor-pointer hover:bg-vscode-list-hoverBackground"
|
||||
onClick={() => setSelectedProvider("custom")}>
|
||||
<input
|
||||
type="radio"
|
||||
name="provider"
|
||||
checked={selectedProvider === "custom"}
|
||||
onChange={() => setSelectedProvider("custom")}
|
||||
className="mt-1"
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<span className="text-sm font-semibold mb-2 block">
|
||||
{t("welcome:providerSignup.useAnotherProvider")}
|
||||
</span>
|
||||
<p className="text-xs text-vscode-descriptionForeground mb-1">
|
||||
{t("welcome:providerSignup.useAnotherProviderDescription")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Show API options only when custom provider is selected */}
|
||||
{selectedProvider === "custom" && (
|
||||
<div className="mt-4">
|
||||
<ApiOptions
|
||||
fromWelcomeView
|
||||
apiConfiguration={apiConfiguration || {}}
|
||||
uriScheme={uriScheme}
|
||||
setApiConfigurationField={setApiConfigurationFieldForApiOptions}
|
||||
errorMessage={errorMessage}
|
||||
setErrorMessage={setErrorMessage}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</TabContent>
|
||||
<div className="sticky bottom-0 bg-vscode-sideBar-background p-4 border-t border-vscode-panel-border">
|
||||
<div className="flex flex-col gap-2">
|
||||
<Button onClick={handleGetStarted} variant="primary">
|
||||
{t("welcome:providerSignup.getStarted")} →
|
||||
</Button>
|
||||
{errorMessage && selectedProvider === "custom" && (
|
||||
<div className="text-vscode-errorForeground text-sm">{errorMessage}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Tab>
|
||||
)
|
||||
}
|
||||
|
||||
export default WelcomeViewProvider
|
||||
10
webview-ui/src/i18n/locales/ca/welcome.json
generated
10
webview-ui/src/i18n/locales/ca/welcome.json
generated
|
|
@ -16,7 +16,15 @@
|
|||
"incentive": "Prova Roo gratis"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Per fer la seva màgia, Roo necessita una clau API.",
|
||||
"chooseProvider": "Per començar, necessites un proveïdor LLM:",
|
||||
"providerSignup": {
|
||||
"rooCloudProvider": "Proveïdor Roo Code Cloud",
|
||||
"rooCloudDescription": "La manera més senzilla d'utilitzar Roo. Una combinació seleccionada de models gratuïts i de pagament a un cost baix",
|
||||
"learnMore": "més informació",
|
||||
"useAnotherProvider": "Utilitza un altre proveïdor",
|
||||
"useAnotherProviderDescription": "Introdueix una clau API i comença.",
|
||||
"getStarted": "Començar"
|
||||
},
|
||||
"startRouter": "Recomanem utilitzar un router LLM:",
|
||||
"startCustom": "O pots utilitzar la teva pròpia clau API:",
|
||||
"telemetry": {
|
||||
|
|
|
|||
10
webview-ui/src/i18n/locales/de/welcome.json
generated
10
webview-ui/src/i18n/locales/de/welcome.json
generated
|
|
@ -16,7 +16,15 @@
|
|||
"incentive": "Probier Roo kostenlos aus"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Um seine Magie zu entfalten, benötigt Roo einen API-Schlüssel.",
|
||||
"chooseProvider": "Um loszulegen, brauchst du einen LLM-Anbieter:",
|
||||
"providerSignup": {
|
||||
"rooCloudProvider": "Roo Code Cloud Provider",
|
||||
"rooCloudDescription": "Der einfachste Weg, Roo zu nutzen. Eine kuratierte Mischung aus kostenlosen und bezahlten Modellen zu niedrigen Kosten",
|
||||
"learnMore": "mehr erfahren",
|
||||
"useAnotherProvider": "Anderen Anbieter verwenden",
|
||||
"useAnotherProviderDescription": "Gib einen API-Schlüssel ein und leg los.",
|
||||
"getStarted": "Loslegen"
|
||||
},
|
||||
"startRouter": "Wir empfehlen die Verwendung eines LLM-Routers:",
|
||||
"startCustom": "Oder du kannst deinen eigenen API-Schlüssel verwenden:",
|
||||
"telemetry": {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,15 @@
|
|||
"incentive": "Try Roo out for free"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "To do its magic, Roo needs an API key.",
|
||||
"chooseProvider": "To get started, you need an LLM provider:",
|
||||
"providerSignup": {
|
||||
"rooCloudProvider": "Roo Code Cloud Provider",
|
||||
"rooCloudDescription": "The simplest way to use Roo. A curated mix of free and paid models at a low cost",
|
||||
"learnMore": "learn more",
|
||||
"useAnotherProvider": "Use another provider",
|
||||
"useAnotherProviderDescription": "Enter an API key and get going.",
|
||||
"getStarted": "Get started"
|
||||
},
|
||||
"startRouter": "We recommend using an LLM Router:",
|
||||
"startCustom": "Or you can bring your provider API key:",
|
||||
"telemetry": {
|
||||
|
|
|
|||
10
webview-ui/src/i18n/locales/es/welcome.json
generated
10
webview-ui/src/i18n/locales/es/welcome.json
generated
|
|
@ -16,7 +16,15 @@
|
|||
"incentive": "Prueba Roo gratis"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Para hacer su magia, Roo necesita una clave API.",
|
||||
"chooseProvider": "Para comenzar, necesitas un proveedor LLM:",
|
||||
"providerSignup": {
|
||||
"rooCloudProvider": "Proveedor Roo Code Cloud",
|
||||
"rooCloudDescription": "La forma más sencilla de usar Roo. Una mezcla seleccionada de modelos gratuitos y de pago a bajo costo",
|
||||
"learnMore": "más información",
|
||||
"useAnotherProvider": "Usar otro proveedor",
|
||||
"useAnotherProviderDescription": "Introduce una clave API y comienza.",
|
||||
"getStarted": "Comenzar"
|
||||
},
|
||||
"startRouter": "Recomendamos usar un router LLM:",
|
||||
"startCustom": "O puedes traer tu propia clave API:",
|
||||
"telemetry": {
|
||||
|
|
|
|||
10
webview-ui/src/i18n/locales/fr/welcome.json
generated
10
webview-ui/src/i18n/locales/fr/welcome.json
generated
|
|
@ -16,7 +16,15 @@
|
|||
"incentive": "Essayez Roo gratuitement"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Pour faire sa magie, Roo a besoin d'une clé API.",
|
||||
"chooseProvider": "Pour commencer, tu as besoin d'un fournisseur LLM :",
|
||||
"providerSignup": {
|
||||
"rooCloudProvider": "Fournisseur Roo Code Cloud",
|
||||
"rooCloudDescription": "La façon la plus simple d'utiliser Roo. Un mélange sélectionné de modèles gratuits et payants à faible coût",
|
||||
"learnMore": "en savoir plus",
|
||||
"useAnotherProvider": "Utiliser un autre fournisseur",
|
||||
"useAnotherProviderDescription": "Entre une clé API et commence.",
|
||||
"getStarted": "Commencer"
|
||||
},
|
||||
"startRouter": "Nous recommandons d'utiliser un routeur LLM :",
|
||||
"startCustom": "Ou tu peux apporter ta propre clé API :",
|
||||
"telemetry": {
|
||||
|
|
|
|||
10
webview-ui/src/i18n/locales/hi/welcome.json
generated
10
webview-ui/src/i18n/locales/hi/welcome.json
generated
|
|
@ -16,7 +16,15 @@
|
|||
"incentive": "Roo को निःशुल्क आज़माएं"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "अपना जादू दिखाने के लिए, Roo को एक API कुंजी की आवश्यकता है।",
|
||||
"chooseProvider": "शुरू करने के लिए, आपको एक LLM प्रदाता की आवश्यकता है:",
|
||||
"providerSignup": {
|
||||
"rooCloudProvider": "Roo Code Cloud प्रदाता",
|
||||
"rooCloudDescription": "Roo का उपयोग करने का सबसे आसान तरीका। कम लागत पर मुफ्त और भुगतान मॉडल का एक चयनित मिश्रण",
|
||||
"learnMore": "और जानें",
|
||||
"useAnotherProvider": "दूसरे प्रदाता का उपयोग करें",
|
||||
"useAnotherProviderDescription": "एक API कुंजी दर्ज करें और शुरू करें।",
|
||||
"getStarted": "शुरू करें"
|
||||
},
|
||||
"startRouter": "हम एक LLM राउटर का उपयोग करने की सलाह देते हैं:",
|
||||
"startCustom": "या आप अपनी खुद की API कुंजी ला सकते हैं:",
|
||||
"telemetry": {
|
||||
|
|
|
|||
10
webview-ui/src/i18n/locales/id/welcome.json
generated
10
webview-ui/src/i18n/locales/id/welcome.json
generated
|
|
@ -16,7 +16,15 @@
|
|||
"incentive": "Coba Roo gratis"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Untuk melakukan keajaibannya, Roo membutuhkan API key.",
|
||||
"chooseProvider": "Untuk memulai, kamu memerlukan penyedia LLM:",
|
||||
"providerSignup": {
|
||||
"rooCloudProvider": "Penyedia Roo Code Cloud",
|
||||
"rooCloudDescription": "Cara termudah untuk menggunakan Roo. Campuran model gratis dan berbayar yang dikurasi dengan biaya rendah",
|
||||
"learnMore": "pelajari lebih lanjut",
|
||||
"useAnotherProvider": "Gunakan penyedia lain",
|
||||
"useAnotherProviderDescription": "Masukkan kunci API dan mulai.",
|
||||
"getStarted": "Mulai"
|
||||
},
|
||||
"startRouter": "Kami merekomendasikan menggunakan Router LLM:",
|
||||
"startCustom": "Atau Anda dapat menggunakan API key Anda sendiri:",
|
||||
"telemetry": {
|
||||
|
|
|
|||
10
webview-ui/src/i18n/locales/it/welcome.json
generated
10
webview-ui/src/i18n/locales/it/welcome.json
generated
|
|
@ -16,7 +16,15 @@
|
|||
"incentive": "Prova Roo gratuitamente"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Per fare la sua magia, Roo ha bisogno di una chiave API.",
|
||||
"chooseProvider": "Per iniziare, hai bisogno di un provider LLM:",
|
||||
"providerSignup": {
|
||||
"rooCloudProvider": "Provider Roo Code Cloud",
|
||||
"rooCloudDescription": "Il modo più semplice per usare Roo. Un mix curato di modelli gratuiti e a pagamento a basso costo",
|
||||
"learnMore": "scopri di più",
|
||||
"useAnotherProvider": "Usa un altro provider",
|
||||
"useAnotherProviderDescription": "Inserisci una chiave API e inizia.",
|
||||
"getStarted": "Inizia"
|
||||
},
|
||||
"startRouter": "Consigliamo di utilizzare un router LLM:",
|
||||
"startCustom": "Oppure puoi utilizzare la tua chiave API:",
|
||||
"telemetry": {
|
||||
|
|
|
|||
10
webview-ui/src/i18n/locales/ja/welcome.json
generated
10
webview-ui/src/i18n/locales/ja/welcome.json
generated
|
|
@ -16,7 +16,15 @@
|
|||
"incentive": "Rooを無料で試す"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Rooが機能するには、APIキーが必要です。",
|
||||
"chooseProvider": "開始するには、LLMプロバイダーが必要です:",
|
||||
"providerSignup": {
|
||||
"rooCloudProvider": "Roo Code Cloudプロバイダー",
|
||||
"rooCloudDescription": "Rooを使用する最も簡単な方法。低コストで厳選された無料および有料モデルの組み合わせ",
|
||||
"learnMore": "詳細を見る",
|
||||
"useAnotherProvider": "別のプロバイダーを使用",
|
||||
"useAnotherProviderDescription": "APIキーを入力して始める。",
|
||||
"getStarted": "始める"
|
||||
},
|
||||
"startRouter": "LLMルーターの使用をお勧めします:",
|
||||
"startCustom": "または、あなた自身のAPIキーを使用できます:",
|
||||
"telemetry": {
|
||||
|
|
|
|||
10
webview-ui/src/i18n/locales/ko/welcome.json
generated
10
webview-ui/src/i18n/locales/ko/welcome.json
generated
|
|
@ -16,7 +16,15 @@
|
|||
"incentive": "Roo를 무료로 체험해보세요"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Roo가 작동하려면 API 키가 필요합니다.",
|
||||
"chooseProvider": "시작하려면 LLM 제공업체가 필요합니다:",
|
||||
"providerSignup": {
|
||||
"rooCloudProvider": "Roo Code Cloud 제공업체",
|
||||
"rooCloudDescription": "Roo를 사용하는 가장 간단한 방법. 저렴한 비용으로 무료 및 유료 모델을 선별하여 제공",
|
||||
"learnMore": "자세히 알아보기",
|
||||
"useAnotherProvider": "다른 제공업체 사용",
|
||||
"useAnotherProviderDescription": "API 키를 입력하고 시작하세요.",
|
||||
"getStarted": "시작하기"
|
||||
},
|
||||
"startRouter": "LLM 라우터 사용을 권장합니다:",
|
||||
"startCustom": "또는 직접 API 키를 가져올 수 있습니다:",
|
||||
"telemetry": {
|
||||
|
|
|
|||
10
webview-ui/src/i18n/locales/nl/welcome.json
generated
10
webview-ui/src/i18n/locales/nl/welcome.json
generated
|
|
@ -16,7 +16,15 @@
|
|||
"incentive": "Probeer Roo gratis uit"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Om zijn magie te doen, heeft Roo een API-sleutel nodig.",
|
||||
"chooseProvider": "Om te beginnen heb je een LLM-provider nodig:",
|
||||
"providerSignup": {
|
||||
"rooCloudProvider": "Roo Code Cloud Provider",
|
||||
"rooCloudDescription": "De eenvoudigste manier om Roo te gebruiken. Een samengestelde mix van gratis en betaalde modellen tegen lage kosten",
|
||||
"learnMore": "meer informatie",
|
||||
"useAnotherProvider": "Gebruik een andere provider",
|
||||
"useAnotherProviderDescription": "Voer een API-sleutel in en ga aan de slag.",
|
||||
"getStarted": "Beginnen"
|
||||
},
|
||||
"startRouter": "We raden aan om een LLM-router te gebruiken:",
|
||||
"startCustom": "Of je kunt je eigen API-sleutel gebruiken:",
|
||||
"telemetry": {
|
||||
|
|
|
|||
10
webview-ui/src/i18n/locales/pl/welcome.json
generated
10
webview-ui/src/i18n/locales/pl/welcome.json
generated
|
|
@ -16,7 +16,15 @@
|
|||
"incentive": "Wypróbuj Roo za darmo"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Aby działać, Roo potrzebuje klucza API.",
|
||||
"chooseProvider": "Aby rozpocząć, potrzebujesz dostawcy LLM:",
|
||||
"providerSignup": {
|
||||
"rooCloudProvider": "Dostawca Roo Code Cloud",
|
||||
"rooCloudDescription": "Najprostszy sposób korzystania z Roo. Wybrana mieszanka darmowych i płatnych modeli po niskich kosztach",
|
||||
"learnMore": "dowiedz się więcej",
|
||||
"useAnotherProvider": "Użyj innego dostawcy",
|
||||
"useAnotherProviderDescription": "Wprowadź klucz API i zacznij.",
|
||||
"getStarted": "Rozpocznij"
|
||||
},
|
||||
"startRouter": "Zalecamy korzystanie z routera LLM:",
|
||||
"startCustom": "Lub możesz użyć własnego klucza API:",
|
||||
"telemetry": {
|
||||
|
|
|
|||
10
webview-ui/src/i18n/locales/pt-BR/welcome.json
generated
10
webview-ui/src/i18n/locales/pt-BR/welcome.json
generated
|
|
@ -16,7 +16,15 @@
|
|||
"incentive": "Experimente o Roo gratuitamente"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Para fazer sua mágica, o Roo precisa de uma chave API.",
|
||||
"chooseProvider": "Para começar, você precisa de um provedor LLM:",
|
||||
"providerSignup": {
|
||||
"rooCloudProvider": "Provedor Roo Code Cloud",
|
||||
"rooCloudDescription": "A maneira mais simples de usar o Roo. Uma combinação selecionada de modelos gratuitos e pagos a baixo custo",
|
||||
"learnMore": "saiba mais",
|
||||
"useAnotherProvider": "Usar outro provedor",
|
||||
"useAnotherProviderDescription": "Digite uma chave API e comece.",
|
||||
"getStarted": "Começar"
|
||||
},
|
||||
"startRouter": "Recomendamos usar um roteador LLM:",
|
||||
"startCustom": "Ou você pode trazer sua própria chave API:",
|
||||
"telemetry": {
|
||||
|
|
|
|||
10
webview-ui/src/i18n/locales/ru/welcome.json
generated
10
webview-ui/src/i18n/locales/ru/welcome.json
generated
|
|
@ -16,7 +16,15 @@
|
|||
"incentive": "Попробуй Roo бесплатно"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Для своей магии Roo нуждается в API-ключе.",
|
||||
"chooseProvider": "Для начала работы тебе нужен LLM-провайдер:",
|
||||
"providerSignup": {
|
||||
"rooCloudProvider": "Провайдер Roo Code Cloud",
|
||||
"rooCloudDescription": "Самый простой способ использования Roo. Подобранная комбинация бесплатных и платных моделей по низкой цене",
|
||||
"learnMore": "подробнее",
|
||||
"useAnotherProvider": "Использовать другого провайдера",
|
||||
"useAnotherProviderDescription": "Введи API-ключ и начни.",
|
||||
"getStarted": "Начать"
|
||||
},
|
||||
"startRouter": "Мы рекомендуем использовать маршрутизатор LLM:",
|
||||
"startCustom": "Или вы можете использовать свой собственный API-ключ:",
|
||||
"telemetry": {
|
||||
|
|
|
|||
10
webview-ui/src/i18n/locales/tr/welcome.json
generated
10
webview-ui/src/i18n/locales/tr/welcome.json
generated
|
|
@ -16,7 +16,15 @@
|
|||
"incentive": "Roo'yu ücretsiz dene"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Sihirini yapabilmesi için Roo'nun bir API anahtarına ihtiyacı var.",
|
||||
"chooseProvider": "Başlamak için bir LLM sağlayıcısına ihtiyacın var:",
|
||||
"providerSignup": {
|
||||
"rooCloudProvider": "Roo Code Cloud Sağlayıcısı",
|
||||
"rooCloudDescription": "Roo'yu kullanmanın en basit yolu. Düşük maliyetle seçilmiş ücretsiz ve ücretli modellerin karışımı",
|
||||
"learnMore": "daha fazla bilgi",
|
||||
"useAnotherProvider": "Başka bir sağlayıcı kullan",
|
||||
"useAnotherProviderDescription": "Bir API anahtarı gir ve başla.",
|
||||
"getStarted": "Başla"
|
||||
},
|
||||
"startRouter": "Bir LLM yönlendiricisi kullanmanı öneririz:",
|
||||
"startCustom": "Veya kendi API anahtarını kullanabilirsin:",
|
||||
"telemetry": {
|
||||
|
|
|
|||
10
webview-ui/src/i18n/locales/vi/welcome.json
generated
10
webview-ui/src/i18n/locales/vi/welcome.json
generated
|
|
@ -16,7 +16,15 @@
|
|||
"incentive": "Dùng thử Roo miễn phí"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Để thực hiện phép màu của mình, Roo cần một khóa API.",
|
||||
"chooseProvider": "Để bắt đầu, bạn cần một nhà cung cấp LLM:",
|
||||
"providerSignup": {
|
||||
"rooCloudProvider": "Nhà cung cấp Roo Code Cloud",
|
||||
"rooCloudDescription": "Cách đơn giản nhất để sử dụng Roo. Sự kết hợp được tuyển chọn của các mô hình miễn phí và trả phí với chi phí thấp",
|
||||
"learnMore": "tìm hiểu thêm",
|
||||
"useAnotherProvider": "Sử dụng nhà cung cấp khác",
|
||||
"useAnotherProviderDescription": "Nhập khóa API và bắt đầu.",
|
||||
"getStarted": "Bắt đầu"
|
||||
},
|
||||
"startRouter": "Chúng tôi khuyên bạn nên sử dụng bộ định tuyến LLM:",
|
||||
"startCustom": "Hoặc bạn có thể sử dụng khóa API của riêng mình:",
|
||||
"telemetry": {
|
||||
|
|
|
|||
10
webview-ui/src/i18n/locales/zh-CN/welcome.json
generated
10
webview-ui/src/i18n/locales/zh-CN/welcome.json
generated
|
|
@ -16,7 +16,15 @@
|
|||
"incentive": "免费试用 Roo"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Roo 需要一个 API 密钥才能发挥魔力。",
|
||||
"chooseProvider": "开始使用需要 LLM 提供商:",
|
||||
"providerSignup": {
|
||||
"rooCloudProvider": "Roo Code Cloud 提供商",
|
||||
"rooCloudDescription": "使用 Roo 最简单的方式。低成本精选的免费和付费模型组合",
|
||||
"learnMore": "了解更多",
|
||||
"useAnotherProvider": "使用其他提供商",
|
||||
"useAnotherProviderDescription": "输入 API 密钥即可开始。",
|
||||
"getStarted": "开始使用"
|
||||
},
|
||||
"startRouter": "我们推荐使用 LLM 路由器:",
|
||||
"startCustom": "或者你可以使用自己的 API 密钥:",
|
||||
"telemetry": {
|
||||
|
|
|
|||
10
webview-ui/src/i18n/locales/zh-TW/welcome.json
generated
10
webview-ui/src/i18n/locales/zh-TW/welcome.json
generated
|
|
@ -16,7 +16,15 @@
|
|||
"incentive": "免費試用 Roo"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Roo 需要 API 金鑰才能發揮魔力。",
|
||||
"chooseProvider": "開始使用需要 LLM 提供者:",
|
||||
"providerSignup": {
|
||||
"rooCloudProvider": "Roo Code Cloud 提供者",
|
||||
"rooCloudDescription": "使用 Roo 最簡單的方式。低成本精選的免費和付費模型組合",
|
||||
"learnMore": "了解更多",
|
||||
"useAnotherProvider": "使用其他提供者",
|
||||
"useAnotherProviderDescription": "輸入 API 金鑰即可開始。",
|
||||
"getStarted": "開始使用"
|
||||
},
|
||||
"startRouter": "我們建議使用 LLM 路由器:",
|
||||
"startCustom": "或者您可以使用自己的 API 金鑰:",
|
||||
"telemetry": {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue