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
122 changes: 97 additions & 25 deletions webview-ui/src/components/settings/ApiOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { memo, useCallback, useEffect, useMemo, useState } from "react"
import { convertHeadersToObject } from "./utils/headers"
import { useDebounce } from "react-use"
import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"
import { ExternalLinkIcon } from "@radix-ui/react-icons"

import {
type ProviderName,
Expand Down Expand Up @@ -31,8 +32,22 @@ import { useAppTranslation } from "@src/i18n/TranslationContext"
import { useRouterModels } from "@src/components/ui/hooks/useRouterModels"
import { useSelectedModel } from "@src/components/ui/hooks/useSelectedModel"
import { useExtensionState } from "@src/context/ExtensionStateContext"
import {
useOpenRouterModelProviders,
OPENROUTER_DEFAULT_PROVIDER_NAME,
} from "@src/components/ui/hooks/useOpenRouterModelProviders"
import { filterProviders, filterModels } from "./utils/organizationFilters"
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, SearchableSelect } from "@src/components/ui"
import {
Select,
SelectTrigger,
SelectValue,
SelectContent,
SelectItem,
SearchableSelect,
Collapsible,
CollapsibleTrigger,
CollapsibleContent,
} from "@src/components/ui"

import {
Anthropic,
Expand Down Expand Up @@ -121,6 +136,7 @@ const ApiOptions = ({
)

const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false)
const [isAdvancedSettingsOpen, setIsAdvancedSettingsOpen] = useState(false)

const handleInputChange = useCallback(
<K extends keyof ProviderSettings, E>(
Expand All @@ -141,6 +157,14 @@ const ApiOptions = ({

const { data: routerModels, refetch: refetchRouterModels } = useRouterModels()

const { data: openRouterModelProviders } = useOpenRouterModelProviders(apiConfiguration?.openRouterModelId, {
enabled:
!!apiConfiguration?.openRouterModelId &&
routerModels?.openrouter &&
Object.keys(routerModels.openrouter).length > 1 &&
apiConfiguration.openRouterModelId in routerModels.openrouter,
})

// Update `apiModelId` whenever `selectedModelId` changes.
useEffect(() => {
if (selectedModelId) {
Expand Down Expand Up @@ -534,30 +558,78 @@ const ApiOptions = ({
/>

{!fromWelcomeView && (
<>
<DiffSettingsControl
diffEnabled={apiConfiguration.diffEnabled}
fuzzyMatchThreshold={apiConfiguration.fuzzyMatchThreshold}
onChange={(field, value) => setApiConfigurationField(field, value)}
/>
<TemperatureControl
value={apiConfiguration.modelTemperature}
onChange={handleInputChange("modelTemperature", noTransform)}
maxValue={2}
/>
<RateLimitSecondsControl
value={apiConfiguration.rateLimitSeconds || 0}
onChange={(value) => setApiConfigurationField("rateLimitSeconds", value)}
/>
<ConsecutiveMistakeLimitControl
value={
apiConfiguration.consecutiveMistakeLimit !== undefined
? apiConfiguration.consecutiveMistakeLimit
: DEFAULT_CONSECUTIVE_MISTAKE_LIMIT
}
onChange={(value) => setApiConfigurationField("consecutiveMistakeLimit", value)}
/>
</>
<Collapsible open={isAdvancedSettingsOpen} onOpenChange={setIsAdvancedSettingsOpen}>
<CollapsibleTrigger className="flex items-center gap-1 w-full cursor-pointer hover:opacity-80 mb-2">
<span className={`codicon codicon-chevron-${isAdvancedSettingsOpen ? "down" : "right"}`}></span>
<span className="font-medium">{t("settings:advancedSettings.title")}</span>
</CollapsibleTrigger>
<CollapsibleContent className="space-y-3">
<DiffSettingsControl
diffEnabled={apiConfiguration.diffEnabled}
fuzzyMatchThreshold={apiConfiguration.fuzzyMatchThreshold}
onChange={(field, value) => setApiConfigurationField(field, value)}
/>
<TemperatureControl
value={apiConfiguration.modelTemperature}
onChange={handleInputChange("modelTemperature", noTransform)}
maxValue={2}
/>
<RateLimitSecondsControl
value={apiConfiguration.rateLimitSeconds || 0}
onChange={(value) => setApiConfigurationField("rateLimitSeconds", value)}
/>
<ConsecutiveMistakeLimitControl
value={
apiConfiguration.consecutiveMistakeLimit !== undefined
? apiConfiguration.consecutiveMistakeLimit
: DEFAULT_CONSECUTIVE_MISTAKE_LIMIT
}
onChange={(value) => setApiConfigurationField("consecutiveMistakeLimit", value)}
/>
{selectedProvider === "openrouter" &&
openRouterModelProviders &&
Object.keys(openRouterModelProviders).length > 0 && (
<div>
<div className="flex items-center gap-1">
<label className="block font-medium mb-1">
{t("settings:providers.openRouter.providerRouting.title")}
</label>
<a href={`https://openrouter.ai/${selectedModelId}/providers`}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding target="_blank" and rel="noopener noreferrer" to external link anchors (e.g. the one at line 597 and line 625) to ensure safe navigation when opening external URLs.

Suggested change
<a href={`https://openrouter.ai/${selectedModelId}/providers`}>
<a href={`https://openrouter.ai/${selectedModelId}/providers`} target="_blank" rel="noopener noreferrer">

<ExternalLinkIcon className="w-4 h-4" />
</a>
</div>
<Select
value={
apiConfiguration?.openRouterSpecificProvider ||
OPENROUTER_DEFAULT_PROVIDER_NAME
}
onValueChange={(value) =>
setApiConfigurationField("openRouterSpecificProvider", value)
}>
<SelectTrigger className="w-full">
<SelectValue placeholder={t("settings:common.select")} />
</SelectTrigger>
<SelectContent>
<SelectItem value={OPENROUTER_DEFAULT_PROVIDER_NAME}>
{OPENROUTER_DEFAULT_PROVIDER_NAME}
</SelectItem>
{Object.entries(openRouterModelProviders).map(([value, { label }]) => (
<SelectItem key={value} value={value}>
{label}
</SelectItem>
))}
</SelectContent>
</Select>
<div className="text-sm text-vscode-descriptionForeground mt-1">
{t("settings:providers.openRouter.providerRouting.description")}{" "}
<a href="https://openrouter.ai/docs/features/provider-routing">
{t("settings:providers.openRouter.providerRouting.learnMore")}.
</a>
</div>
</div>
)}
</CollapsibleContent>
</Collapsible>
)}
</div>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,20 @@ vi.mock("@/components/ui", () => ({
</select>
</div>
),
// Add Collapsible components
Collapsible: ({ children, open }: any) => (
<div className="collapsible-mock" data-open={open}>
{children}
</div>
),
CollapsibleTrigger: ({ children, className, onClick }: any) => (
<div className={`collapsible-trigger-mock ${className || ""}`} onClick={onClick}>
{children}
</div>
),
CollapsibleContent: ({ children, className }: any) => (
<div className={`collapsible-content-mock ${className || ""}`}>{children}</div>
),
}))

vi.mock("../TemperatureControl", () => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,20 @@ vi.mock("@/components/ui", () => ({
{children}
</button>
),
// Add Collapsible components
Collapsible: ({ children, open }: any) => (
<div className="collapsible-mock" data-open={open}>
{children}
</div>
),
CollapsibleTrigger: ({ children, className, onClick }: any) => (
<div className={`collapsible-trigger-mock ${className || ""}`} onClick={onClick}>
{children}
</div>
),
CollapsibleContent: ({ children, className }: any) => (
<div className={`collapsible-content-mock ${className || ""}`}>{children}</div>
),
}))

// Mock window.postMessage to trigger state hydration
Expand Down
50 changes: 0 additions & 50 deletions webview-ui/src/components/settings/providers/OpenRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,14 @@ import { useCallback, useState } from "react"
import { Trans } from "react-i18next"
import { Checkbox } from "vscrui"
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
import { ExternalLinkIcon } from "@radix-ui/react-icons"

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

import type { RouterModels } from "@roo/api"

import { useAppTranslation } from "@src/i18n/TranslationContext"
import { getOpenRouterAuthUrl } from "@src/oauth/urls"
import {
useOpenRouterModelProviders,
OPENROUTER_DEFAULT_PROVIDER_NAME,
} from "@src/components/ui/hooks/useOpenRouterModelProviders"
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@src/components/ui"

import { inputEventTransform, noTransform } from "../transforms"

Expand All @@ -37,7 +31,6 @@ export const OpenRouter = ({
apiConfiguration,
setApiConfigurationField,
routerModels,
selectedModelId,
uriScheme,
fromWelcomeView,
organizationAllowList,
Expand All @@ -58,14 +51,6 @@ export const OpenRouter = ({
[setApiConfigurationField],
)

const { data: openRouterModelProviders } = useOpenRouterModelProviders(apiConfiguration?.openRouterModelId, {
enabled:
!!apiConfiguration?.openRouterModelId &&
routerModels?.openrouter &&
Object.keys(routerModels.openrouter).length > 1 &&
apiConfiguration.openRouterModelId in routerModels.openrouter,
})

return (
<>
<VSCodeTextField
Expand Down Expand Up @@ -139,41 +124,6 @@ export const OpenRouter = ({
organizationAllowList={organizationAllowList}
errorMessage={modelValidationError}
/>
{openRouterModelProviders && Object.keys(openRouterModelProviders).length > 0 && (
<div>
<div className="flex items-center gap-1">
<label className="block font-medium mb-1">
{t("settings:providers.openRouter.providerRouting.title")}
</label>
<a href={`https://openrouter.ai/${selectedModelId}/providers`}>
<ExternalLinkIcon className="w-4 h-4" />
</a>
</div>
<Select
value={apiConfiguration?.openRouterSpecificProvider || OPENROUTER_DEFAULT_PROVIDER_NAME}
onValueChange={(value) => setApiConfigurationField("openRouterSpecificProvider", value)}>
<SelectTrigger className="w-full">
<SelectValue placeholder={t("settings:common.select")} />
</SelectTrigger>
<SelectContent>
<SelectItem value={OPENROUTER_DEFAULT_PROVIDER_NAME}>
{OPENROUTER_DEFAULT_PROVIDER_NAME}
</SelectItem>
{Object.entries(openRouterModelProviders).map(([value, { label }]) => (
<SelectItem key={value} value={value}>
{label}
</SelectItem>
))}
</SelectContent>
</Select>
<div className="text-sm text-vscode-descriptionForeground mt-1">
{t("settings:providers.openRouter.providerRouting.description")}{" "}
<a href="https://openrouter.ai/docs/features/provider-routing">
{t("settings:providers.openRouter.providerRouting.learnMore")}.
</a>
</div>
</div>
)}
</>
)
}
3 changes: 3 additions & 0 deletions webview-ui/src/i18n/locales/ca/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,9 @@
"description": "Quan està habilitat, el terminal hereta les variables d'entorn del procés pare de VSCode, com ara la configuració d'integració del shell definida al perfil d'usuari. Això commuta directament la configuració global de VSCode `terminal.integrated.inheritEnv`. <0>Més informació</0>"
}
},
"advancedSettings": {
"title": "Configuració avançada"
},
"advanced": {
"diff": {
"label": "Habilitar edició mitjançant diffs",
Expand Down
3 changes: 3 additions & 0 deletions webview-ui/src/i18n/locales/de/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,9 @@
"description": "Wenn aktiviert, erbt das Terminal Umgebungsvariablen aus dem übergeordneten Prozess von VSCode, wie z.B. benutzerdefinierte Shell-Integrationseinstellungen. Dies schaltet direkt die globale VSCode-Einstellung `terminal.integrated.inheritEnv` um. <0>Mehr erfahren</0>"
}
},
"advancedSettings": {
"title": "Erweiterte Einstellungen"
},
"advanced": {
"diff": {
"label": "Bearbeitung durch Diffs aktivieren",
Expand Down
3 changes: 3 additions & 0 deletions webview-ui/src/i18n/locales/en/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,9 @@
"description": "When enabled, the terminal will inherit environment variables from VSCode's parent process, such as user-profile-defined shell integration settings. This directly toggles VSCode global setting `terminal.integrated.inheritEnv`. <0>Learn more</0>"
}
},
"advancedSettings": {
"title": "Advanced settings"
},
"advanced": {
"diff": {
"label": "Enable editing through diffs",
Expand Down
3 changes: 3 additions & 0 deletions webview-ui/src/i18n/locales/es/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,9 @@
"description": "Cuando está habilitado, el terminal hereda las variables de entorno del proceso padre de VSCode, como la configuración de integración del shell definida en el perfil del usuario. Esto alterna directamente la configuración global de VSCode `terminal.integrated.inheritEnv`. <0>Más información</0>"
}
},
"advancedSettings": {
"title": "Configuración avanzada"
},
"advanced": {
"diff": {
"label": "Habilitar edición a través de diffs",
Expand Down
3 changes: 3 additions & 0 deletions webview-ui/src/i18n/locales/fr/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,9 @@
"description": "Lorsqu'activé, le terminal hérite des variables d'environnement du processus parent VSCode, comme les paramètres d'intégration du shell définis dans le profil utilisateur. Cela bascule directement le paramètre global VSCode `terminal.integrated.inheritEnv`. <0>En savoir plus</0>"
}
},
"advancedSettings": {
"title": "Paramètres avancés"
},
"advanced": {
"diff": {
"label": "Activer l'édition via des diffs",
Expand Down
3 changes: 3 additions & 0 deletions webview-ui/src/i18n/locales/hi/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,9 @@
"description": "सक्षम होने पर, टर्मिनल VSCode के मूल प्रक्रिया से पर्यावरण चर विरासत में लेता है, जैसे उपयोगकर्ता प्रोफ़ाइल में परिभाषित शेल एकीकरण सेटिंग्स। यह VSCode की वैश्विक सेटिंग `terminal.integrated.inheritEnv` को सीधे टॉगल करता है। <0>अधिक जानें</0>"
}
},
"advancedSettings": {
"title": "उन्नत सेटिंग्स"
},
"advanced": {
"diff": {
"label": "diffs के माध्यम से संपादन सक्षम करें",
Expand Down
3 changes: 3 additions & 0 deletions webview-ui/src/i18n/locales/id/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,9 @@
"description": "Ketika diaktifkan, terminal akan mewarisi variabel environment dari proses parent VSCode, seperti pengaturan integrasi shell yang didefinisikan user-profile. Ini secara langsung mengalihkan pengaturan global VSCode `terminal.integrated.inheritEnv`. <0>Pelajari lebih lanjut</0>"
}
},
"advancedSettings": {
"title": "Pengaturan lanjutan"
},
"advanced": {
"diff": {
"label": "Aktifkan editing melalui diff",
Expand Down
3 changes: 3 additions & 0 deletions webview-ui/src/i18n/locales/it/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,9 @@
"description": "Quando abilitato, il terminale eredita le variabili d'ambiente dal processo padre di VSCode, come le impostazioni di integrazione della shell definite nel profilo utente. Questo attiva direttamente l'impostazione globale di VSCode `terminal.integrated.inheritEnv`. <0>Scopri di più</0>"
}
},
"advancedSettings": {
"title": "Impostazioni avanzate"
},
"advanced": {
"diff": {
"label": "Abilita modifica tramite diff",
Expand Down
3 changes: 3 additions & 0 deletions webview-ui/src/i18n/locales/ja/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,9 @@
"description": "有効にすると、ターミナルは VSCode の親プロセスから環境変数を継承します。ユーザープロファイルで定義されたシェル統合設定などが含まれます。これは VSCode のグローバル設定 `terminal.integrated.inheritEnv` を直接切り替えます。 <0>詳細情報</0>"
}
},
"advancedSettings": {
"title": "詳細設定"
},
"advanced": {
"diff": {
"label": "diff経由の編集を有効化",
Expand Down
3 changes: 3 additions & 0 deletions webview-ui/src/i18n/locales/ko/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,9 @@
"description": "활성화하면 터미널이 VSCode 부모 프로세스로부터 환경 변수를 상속받습니다. 사용자 프로필에 정의된 셸 통합 설정 등이 포함됩니다. 이는 VSCode 전역 설정 `terminal.integrated.inheritEnv`를 직접 전환합니다. <0>더 알아보기</0>"
}
},
"advancedSettings": {
"title": "고급 설정"
},
"advanced": {
"diff": {
"label": "diff를 통한 편집 활성화",
Expand Down
3 changes: 3 additions & 0 deletions webview-ui/src/i18n/locales/nl/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,9 @@
"description": "Indien ingeschakeld, neemt de terminal omgevingsvariabelen over van het bovenliggende VSCode-proces, zoals shell-integratie-instellingen uit het gebruikersprofiel. Dit schakelt direct de VSCode-instelling `terminal.integrated.inheritEnv` om. <0>Meer informatie</0>"
}
},
"advancedSettings": {
"title": "Geavanceerde instellingen"
},
"advanced": {
"diff": {
"label": "Bewerken via diffs inschakelen",
Expand Down
Loading