Skip to content

Commit fc2b3b2

Browse files
committed
feat: Add visibility toggle for API key inputs
1 parent 2411c8f commit fc2b3b2

File tree

18 files changed

+115
-94
lines changed

18 files changed

+115
-94
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { useState } from "react"
2+
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
3+
4+
type ApiKeyInputProps = {
5+
value: string
6+
onInput: (event: Event | React.FormEvent<HTMLElement>) => void
7+
placeholder: string
8+
label: string
9+
className?: string
10+
name?: string
11+
children?: React.ReactNode
12+
}
13+
14+
export const ApiKeyInput = ({ value, onInput, placeholder, label, className, name, children }: ApiKeyInputProps) => {
15+
const [showApiKey, setShowApiKey] = useState(false)
16+
17+
return (
18+
<div className={`relative w-full ${className || ""}`}>
19+
<VSCodeTextField
20+
name={name}
21+
value={value}
22+
type={showApiKey ? "text" : "password"}
23+
onInput={onInput}
24+
placeholder={placeholder}
25+
className="w-full pr-10">
26+
<label className="block font-medium mb-1">{label}</label>
27+
{children}
28+
</VSCodeTextField>
29+
<div
30+
className="absolute inset-y-0 right-0 flex items-center pr-3 cursor-pointer"
31+
style={{ top: "1.5rem" }}
32+
onClick={() => setShowApiKey(!showApiKey)}>
33+
{showApiKey ? (
34+
<span className="codicon codicon-eye"></span>
35+
) : (
36+
<span className="codicon codicon-eye-closed"></span>
37+
)}
38+
</div>
39+
</div>
40+
)
41+
}

webview-ui/src/components/settings/providers/Anthropic.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { ProviderSettings } from "@roo-code/types"
66

77
import { useAppTranslation } from "@src/i18n/TranslationContext"
88
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
9+
import { ApiKeyInput } from "@src/components/common/ApiKeyInput"
910

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

@@ -32,14 +33,12 @@ export const Anthropic = ({ apiConfiguration, setApiConfigurationField }: Anthro
3233

3334
return (
3435
<>
35-
<VSCodeTextField
36+
<ApiKeyInput
3637
value={apiConfiguration?.apiKey || ""}
37-
type="password"
3838
onInput={handleInputChange("apiKey")}
3939
placeholder={t("settings:placeholders.apiKey")}
40-
className="w-full">
41-
<label className="block font-medium mb-1">{t("settings:providers.anthropicApiKey")}</label>
42-
</VSCodeTextField>
40+
label={t("settings:providers.anthropicApiKey")}
41+
/>
4342
<div className="text-sm text-vscode-descriptionForeground -mt-2">
4443
{t("settings:providers.apiKeyStorageNotice")}
4544
</div>

webview-ui/src/components/settings/providers/Bedrock.tsx

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { type ProviderSettings, type ModelInfo, BEDROCK_REGIONS } from "@roo-cod
66

77
import { useAppTranslation } from "@src/i18n/TranslationContext"
88
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, StandardTooltip } from "@src/components/ui"
9+
import { ApiKeyInput } from "@src/components/common/ApiKeyInput"
910

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

@@ -59,30 +60,24 @@ export const Bedrock = ({ apiConfiguration, setApiConfigurationField, selectedMo
5960
</VSCodeTextField>
6061
) : (
6162
<>
62-
<VSCodeTextField
63+
<ApiKeyInput
6364
value={apiConfiguration?.awsAccessKey || ""}
64-
type="password"
6565
onInput={handleInputChange("awsAccessKey")}
6666
placeholder={t("settings:placeholders.accessKey")}
67-
className="w-full">
68-
<label className="block font-medium mb-1">{t("settings:providers.awsAccessKey")}</label>
69-
</VSCodeTextField>
70-
<VSCodeTextField
67+
label={t("settings:providers.awsAccessKey")}
68+
/>
69+
<ApiKeyInput
7170
value={apiConfiguration?.awsSecretKey || ""}
72-
type="password"
7371
onInput={handleInputChange("awsSecretKey")}
7472
placeholder={t("settings:placeholders.secretKey")}
75-
className="w-full">
76-
<label className="block font-medium mb-1">{t("settings:providers.awsSecretKey")}</label>
77-
</VSCodeTextField>
78-
<VSCodeTextField
73+
label={t("settings:providers.awsSecretKey")}
74+
/>
75+
<ApiKeyInput
7976
value={apiConfiguration?.awsSessionToken || ""}
80-
type="password"
8177
onInput={handleInputChange("awsSessionToken")}
8278
placeholder={t("settings:placeholders.sessionToken")}
83-
className="w-full">
84-
<label className="block font-medium mb-1">{t("settings:providers.awsSessionToken")}</label>
85-
</VSCodeTextField>
79+
label={t("settings:providers.awsSessionToken")}
80+
/>
8681
</>
8782
)}
8883
<div>

webview-ui/src/components/settings/providers/Chutes.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { ProviderSettings } from "@roo-code/types"
55

66
import { useAppTranslation } from "@src/i18n/TranslationContext"
77
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
8+
import { ApiKeyInput } from "@src/components/common/ApiKeyInput"
89

910
import { inputEventTransform } from "../transforms"
1011

@@ -29,14 +30,12 @@ export const Chutes = ({ apiConfiguration, setApiConfigurationField }: ChutesPro
2930

3031
return (
3132
<>
32-
<VSCodeTextField
33+
<ApiKeyInput
3334
value={apiConfiguration?.chutesApiKey || ""}
34-
type="password"
3535
onInput={handleInputChange("chutesApiKey")}
3636
placeholder={t("settings:placeholders.apiKey")}
37-
className="w-full">
38-
<label className="block font-medium mb-1">{t("settings:providers.chutesApiKey")}</label>
39-
</VSCodeTextField>
37+
label={t("settings:providers.chutesApiKey")}
38+
/>
4039
<div className="text-sm text-vscode-descriptionForeground -mt-2">
4140
{t("settings:providers.apiKeyStorageNotice")}
4241
</div>

webview-ui/src/components/settings/providers/DeepSeek.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { ProviderSettings } from "@roo-code/types"
55

66
import { useAppTranslation } from "@src/i18n/TranslationContext"
77
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
8+
import { ApiKeyInput } from "@src/components/common/ApiKeyInput"
89

910
import { inputEventTransform } from "../transforms"
1011

@@ -29,14 +30,12 @@ export const DeepSeek = ({ apiConfiguration, setApiConfigurationField }: DeepSee
2930

3031
return (
3132
<>
32-
<VSCodeTextField
33+
<ApiKeyInput
3334
value={apiConfiguration?.deepSeekApiKey || ""}
34-
type="password"
3535
onInput={handleInputChange("deepSeekApiKey")}
3636
placeholder={t("settings:placeholders.apiKey")}
37-
className="w-full">
38-
<label className="block font-medium mb-1">{t("settings:providers.deepSeekApiKey")}</label>
39-
</VSCodeTextField>
37+
label={t("settings:providers.deepSeekApiKey")}
38+
/>
4039
<div className="text-sm text-vscode-descriptionForeground -mt-2">
4140
{t("settings:providers.apiKeyStorageNotice")}
4241
</div>

webview-ui/src/components/settings/providers/Gemini.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { ProviderSettings } from "@roo-code/types"
66

77
import { useAppTranslation } from "@src/i18n/TranslationContext"
88
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
9+
import { ApiKeyInput } from "@src/components/common/ApiKeyInput"
910

1011
import { inputEventTransform } from "../transforms"
1112

@@ -34,14 +35,12 @@ export const Gemini = ({ apiConfiguration, setApiConfigurationField }: GeminiPro
3435

3536
return (
3637
<>
37-
<VSCodeTextField
38+
<ApiKeyInput
3839
value={apiConfiguration?.geminiApiKey || ""}
39-
type="password"
4040
onInput={handleInputChange("geminiApiKey")}
4141
placeholder={t("settings:placeholders.apiKey")}
42-
className="w-full">
43-
<label className="block font-medium mb-1">{t("settings:providers.geminiApiKey")}</label>
44-
</VSCodeTextField>
42+
label={t("settings:providers.geminiApiKey")}
43+
/>
4544
<div className="text-sm text-vscode-descriptionForeground -mt-2">
4645
{t("settings:providers.apiKeyStorageNotice")}
4746
</div>

webview-ui/src/components/settings/providers/Glama.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type { RouterModels } from "@roo/api"
88
import { useAppTranslation } from "@src/i18n/TranslationContext"
99
import { getGlamaAuthUrl } from "@src/oauth/urls"
1010
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
11+
import { ApiKeyInput } from "@src/components/common/ApiKeyInput"
1112

1213
import { inputEventTransform } from "../transforms"
1314
import { ModelPicker } from "../ModelPicker"
@@ -44,14 +45,12 @@ export const Glama = ({
4445

4546
return (
4647
<>
47-
<VSCodeTextField
48+
<ApiKeyInput
4849
value={apiConfiguration?.glamaApiKey || ""}
49-
type="password"
5050
onInput={handleInputChange("glamaApiKey")}
5151
placeholder={t("settings:placeholders.apiKey")}
52-
className="w-full">
53-
<label className="block font-medium mb-1">{t("settings:providers.glamaApiKey")}</label>
54-
</VSCodeTextField>
52+
label={t("settings:providers.glamaApiKey")}
53+
/>
5554
<div className="text-sm text-vscode-descriptionForeground -mt-2">
5655
{t("settings:providers.apiKeyStorageNotice")}
5756
</div>

webview-ui/src/components/settings/providers/Groq.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { ProviderSettings } from "@roo-code/types"
55

66
import { useAppTranslation } from "@src/i18n/TranslationContext"
77
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
8+
import { ApiKeyInput } from "@src/components/common/ApiKeyInput"
89

910
import { inputEventTransform } from "../transforms"
1011

@@ -29,14 +30,12 @@ export const Groq = ({ apiConfiguration, setApiConfigurationField }: GroqProps)
2930

3031
return (
3132
<>
32-
<VSCodeTextField
33+
<ApiKeyInput
3334
value={apiConfiguration?.groqApiKey || ""}
34-
type="password"
3535
onInput={handleInputChange("groqApiKey")}
3636
placeholder={t("settings:placeholders.apiKey")}
37-
className="w-full">
38-
<label className="block font-medium mb-1">{t("settings:providers.groqApiKey")}</label>
39-
</VSCodeTextField>
37+
label={t("settings:providers.groqApiKey")}
38+
/>
4039
<div className="text-sm text-vscode-descriptionForeground -mt-2">
4140
{t("settings:providers.apiKeyStorageNotice")}
4241
</div>

webview-ui/src/components/settings/providers/LiteLLM.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { vscode } from "@src/utils/vscode"
1010
import { useExtensionState } from "@src/context/ExtensionStateContext"
1111
import { useAppTranslation } from "@src/i18n/TranslationContext"
1212
import { Button } from "@src/components/ui"
13+
import { ApiKeyInput } from "@src/components/common/ApiKeyInput"
1314

1415
import { inputEventTransform } from "../transforms"
1516
import { ModelPicker } from "../ModelPicker"
@@ -98,14 +99,12 @@ export const LiteLLM = ({
9899
<label className="block font-medium mb-1">{t("settings:providers.litellmBaseUrl")}</label>
99100
</VSCodeTextField>
100101

101-
<VSCodeTextField
102+
<ApiKeyInput
102103
value={apiConfiguration?.litellmApiKey || ""}
103-
type="password"
104104
onInput={handleInputChange("litellmApiKey")}
105105
placeholder={t("settings:placeholders.apiKey")}
106-
className="w-full">
107-
<label className="block font-medium mb-1">{t("settings:providers.litellmApiKey")}</label>
108-
</VSCodeTextField>
106+
label={t("settings:providers.litellmApiKey")}
107+
/>
109108

110109
<div className="text-sm text-vscode-descriptionForeground -mt-2">
111110
{t("settings:providers.apiKeyStorageNotice")}

webview-ui/src/components/settings/providers/Mistral.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type { RouterModels } from "@roo/api"
77

88
import { useAppTranslation } from "@src/i18n/TranslationContext"
99
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
10+
import { ApiKeyInput } from "@src/components/common/ApiKeyInput"
1011

1112
import { inputEventTransform } from "../transforms"
1213

@@ -32,14 +33,12 @@ export const Mistral = ({ apiConfiguration, setApiConfigurationField }: MistralP
3233

3334
return (
3435
<>
35-
<VSCodeTextField
36+
<ApiKeyInput
3637
value={apiConfiguration?.mistralApiKey || ""}
37-
type="password"
3838
onInput={handleInputChange("mistralApiKey")}
3939
placeholder={t("settings:placeholders.apiKey")}
40-
className="w-full">
41-
<span className="font-medium">{t("settings:providers.mistralApiKey")}</span>
42-
</VSCodeTextField>
40+
label={t("settings:providers.mistralApiKey")}
41+
/>
4342
<div className="text-sm text-vscode-descriptionForeground -mt-2">
4443
{t("settings:providers.apiKeyStorageNotice")}
4544
</div>

0 commit comments

Comments
 (0)