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
7 changes: 6 additions & 1 deletion packages/cloud/src/WebAuthService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,10 +331,15 @@ export class WebAuthService extends EventEmitter<AuthServiceEvents> implements A

await this.storeCredentials(credentials)

// Store the provider model if provided
// Store the provider model if provided, or flag that no model was selected
if (providerModel) {
await this.context.globalState.update("roo-provider-model", providerModel)
await this.context.globalState.update("roo-auth-skip-model", undefined)
this.log(`[auth] Stored provider model: ${providerModel}`)
} else {
// No model was selected during signup - flag this for the webview
await this.context.globalState.update("roo-auth-skip-model", true)
this.log(`[auth] No provider model selected during signup`)
}

const vscode = await importVscode()
Expand Down
29 changes: 29 additions & 0 deletions packages/cloud/src/__tests__/WebAuthService.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,9 +395,38 @@ describe("WebAuthService", () => {
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(mockContext.globalState.update).toHaveBeenCalledWith("roo-auth-skip-model", undefined)
expect(mockLog).toHaveBeenCalledWith("[auth] Stored provider model: xai/grok-code-fast-1")
})

it("should set skip model flag when provider model is NOT 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)

// Call without provider model
await authService.handleCallback("auth-code", storedState, null)

expect(mockContext.globalState.update).toHaveBeenCalledWith("roo-auth-skip-model", true)
expect(mockLog).toHaveBeenCalledWith("[auth] No provider model selected during signup")
})

it("should handle Clerk API errors", async () => {
const storedState = "valid-state"
mockContext.globalState.get.mockReturnValue(storedState)
Expand Down
1 change: 1 addition & 0 deletions src/core/webview/ClineProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2025,6 +2025,7 @@ export class ClineProvider
enterBehavior: enterBehavior ?? "send",
cloudUserInfo,
cloudIsAuthenticated: cloudIsAuthenticated ?? false,
cloudAuthSkipModel: this.context.globalState.get<boolean>("roo-auth-skip-model") ?? false,
cloudOrganizations,
sharingEnabled: sharingEnabled ?? false,
publicSharingEnabled: publicSharingEnabled ?? false,
Expand Down
6 changes: 6 additions & 0 deletions src/core/webview/webviewMessageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2384,6 +2384,12 @@ export const webviewMessageHandler = async (

break
}
case "clearCloudAuthSkipModel": {
// Clear the flag that indicates auth completed without model selection
await provider.context.globalState.update("roo-auth-skip-model", undefined)
await provider.postStateToWebview()
break
}
case "switchOrganization": {
try {
const organizationId = message.organizationId ?? null
Expand Down
1 change: 1 addition & 0 deletions src/shared/ExtensionMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ export type ExtensionState = Pick<

cloudUserInfo: CloudUserInfo | null
cloudIsAuthenticated: boolean
cloudAuthSkipModel?: boolean // Flag indicating auth completed without model selection (user should pick 3rd-party provider)
cloudApiUrl?: string
cloudOrganizations?: CloudOrganizationMembership[]
sharingEnabled: boolean
Expand Down
1 change: 1 addition & 0 deletions src/shared/WebviewMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ export interface WebviewMessage {
| "searchFiles"
| "toggleApiConfigPin"
| "hasOpenedModeSelector"
| "clearCloudAuthSkipModel"
| "cloudButtonClicked"
| "rooCloudSignIn"
| "cloudLandingPageSignIn"
Expand Down
7 changes: 6 additions & 1 deletion webview-ui/src/components/settings/ApiOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,11 @@ const ApiOptions = ({
options.unshift(rooOption)
}
} else {
// Filter out roo from the welcome view
const filteredOptions = options.filter((opt) => opt.value !== "roo")
options.length = 0
options.push(...filteredOptions)

const openRouterIndex = options.findIndex((opt) => opt.value === "openrouter")
if (openRouterIndex > 0) {
const [openRouterOption] = options.splice(openRouterIndex, 1)
Expand All @@ -472,7 +477,7 @@ const ApiOptions = ({
) : (
docs && (
<VSCodeLink href={docs.url} target="_blank" className="flex gap-2">
{docs.name}
{t("settings:providers.apiProviderDocs")}
<BookOpenText className="size-4 inline ml-2" />
</VSCodeLink>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,7 @@ describe("ApiOptions", () => {
useExtensionStateMock.mockRestore()
})

it("does not pin roo provider to the top on welcome screen", () => {
it("filters out roo provider on welcome screen", () => {
// Mock useExtensionState to ensure no filtering
const useExtensionStateMock = vi.spyOn(ExtensionStateContext, "useExtensionState")
useExtensionStateMock.mockReturnValue({
Expand All @@ -663,13 +663,9 @@ describe("ApiOptions", () => {
// Filter out the placeholder option (empty value)
const providerOptions = options.filter((opt) => opt.value !== "")

// Check that roo is in the list
// Check that roo is NOT in the list when on welcome screen
const rooOption = providerOptions.find((opt) => opt.value === "roo")

if (rooOption) {
// If roo exists, verify it's NOT at the top (should be in alphabetical order)
expect(providerOptions[0].value).not.toBe("roo")
}
expect(rooOption).toBeUndefined()

useExtensionStateMock.mockRestore()
})
Expand Down
12 changes: 10 additions & 2 deletions webview-ui/src/components/welcome/RooHero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const RooHero = () => {
})

return (
<div className="pb-4 forced-color-adjust-none group">
<div className="mb-4 relative forced-color-adjust-none group flex flex-col items-center w-30 pt-4 overflow-clip">
<div
style={{
backgroundColor: "var(--vscode-foreground)",
Expand All @@ -18,9 +18,17 @@ const RooHero = () => {
maskRepeat: "no-repeat",
maskSize: "contain",
}}
className="mx-auto group-hover:animate-bounce translate-y-0 transition-transform duration-500">
className="z-5 mr-auto group-hover:animate-bounce translate-y-0 transition-transform duration-500">
<img src={imagesBaseUri + "/roo-logo.svg"} alt="Roo logo" className="h-8 opacity-0" />
</div>
<div
className="w-[200%] -mt-0.25 h-0.5 overflow-hidden opacity-0 group-hover:opacity-70 transition-opacity duration-300"
data-testid="roo-hero-ground">
<div className="w-full border-b-1 group-hover:border-b-1 border-dashed border-vscode-foreground animate-ground-slide" />
</div>
<div className="z-4 bg-gradient-to-r from-transparent to-vscode-sideBar-background absolute top-0 right-0 bottom-0 w-10 opacity-100" />
<div className="z-3 bg-gradient-to-l from-transparent to-vscode-sideBar-background absolute top-0 left-0 bottom-0 w-10 opacity-100" />
<div className="bg-vscode-foreground/10 rounded-full size-10 z-1 absolute -bottom-4 animate-sun opacity-0 group-hover:opacity-100 transition-opacity duration-300 blur-[2px]" />
</div>
)
}
Expand Down
Loading
Loading