refactor: general UI improvements (#2987)

This commit is contained in:
Dicha Zelianivan Arkana 2025-05-06 23:06:20 +07:00 committed by GitHub
parent 2e2c0a8f02
commit 883cb927a3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 403 additions and 490 deletions

View file

@ -1,14 +1,6 @@
import React, { useState, useEffect, useMemo, useCallback } from "react"
import React, { useState, useEffect, useMemo, useCallback, useRef } from "react"
import { Button } from "@/components/ui/button"
import {
VSCodeTextArea,
VSCodeDropdown,
VSCodeOption,
VSCodeTextField,
VSCodeCheckbox,
VSCodeRadioGroup,
VSCodeRadio,
} from "@vscode/webview-ui-toolkit/react"
import { VSCodeCheckbox, VSCodeRadioGroup, VSCodeRadio } from "@vscode/webview-ui-toolkit/react"
import { useExtensionState } from "@src/context/ExtensionStateContext"
import {
@ -29,6 +21,25 @@ import { Tab, TabContent, TabHeader } from "../common/Tab"
import i18next from "i18next"
import { useAppTranslation } from "@src/i18n/TranslationContext"
import { Trans } from "react-i18next"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
Textarea,
Popover,
PopoverContent,
PopoverTrigger,
Command,
CommandInput,
CommandList,
CommandEmpty,
CommandItem,
CommandGroup,
Input,
} from "../ui"
import { ChevronsUpDown, X } from "lucide-react"
// Get all available groups that should show in prompts view
const availableGroups = (Object.keys(TOOL_GROUPS) as ToolGroup[]).filter((group) => !TOOL_GROUPS[group].alwaysAvailable)
@ -78,9 +89,14 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
const [isToolsEditMode, setIsToolsEditMode] = useState(false)
const [showConfigMenu, setShowConfigMenu] = useState(false)
const [isCreateModeDialogOpen, setIsCreateModeDialogOpen] = useState(false)
const [activeSupportTab, setActiveSupportTab] = useState<SupportPromptType>("ENHANCE")
const [activeSupportOption, setActiveSupportOption] = useState<SupportPromptType>("ENHANCE")
const [isSystemPromptDisclosureOpen, setIsSystemPromptDisclosureOpen] = useState(false)
// State for mode selection popover and search
const [open, setOpen] = useState(false)
const [searchValue, setSearchValue] = useState("")
const searchInputRef = useRef<HTMLInputElement>(null)
// Direct update functions
const updateAgentPrompt = useCallback(
(mode: Mode, promptData: PromptComponent) => {
@ -144,9 +160,24 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
// Exit tools edit mode when switching modes
setIsToolsEditMode(false)
},
[visualMode, switchMode, setIsToolsEditMode],
[visualMode, switchMode],
)
// Handler for popover open state change
const onOpenChange = useCallback((open: boolean) => {
setOpen(open)
// Reset search when closing the popover
if (!open) {
setTimeout(() => setSearchValue(""), 100)
}
}, [])
// Handler for clearing search input
const onClearSearch = useCallback(() => {
setSearchValue("")
searchInputRef.current?.focus()
}, [])
// Helper function to get current mode's config
const getCurrentMode = useCallback((): ModeConfig | undefined => {
const findMode = (m: ModeConfig): boolean => m.slug === visualMode
@ -480,45 +511,95 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
{t("prompts:modes.createModeHelpText")}
</div>
<div className="flex gap-2 items-center mb-3 flex-wrap py-1">
{modes.map((modeConfig) => {
const isActive = visualMode === modeConfig.slug
return (
<button
key={modeConfig.slug}
data-testid={`${modeConfig.slug}-tab`}
data-active={isActive ? "true" : "false"}
onClick={() => handleModeSwitch(modeConfig)}
className={`px-2 py-1 border-none rounded cursor-pointer font-bold ${
isActive
? "bg-vscode-button-background text-vscode-button-foreground opacity-100"
: "bg-transparent text-vscode-foreground opacity-80"
}`}>
{modeConfig.name}
</button>
)
})}
<div className="flex items-center gap-1 mb-3">
<Popover open={open} onOpenChange={onOpenChange}>
<PopoverTrigger asChild>
<Button
variant="combobox"
role="combobox"
aria-expanded={open}
className="grow justify-between"
data-testid="mode-select-trigger">
<div>{getCurrentMode()?.name || t("prompts:modes.selectMode")}</div>
<ChevronsUpDown className="opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="p-0 w-[var(--radix-popover-trigger-width)]">
<Command>
<div className="relative">
<CommandInput
ref={searchInputRef}
value={searchValue}
onValueChange={setSearchValue}
placeholder={t("prompts:modes.selectMode")}
className="h-9 mr-4"
data-testid="mode-search-input"
/>
{searchValue.length > 0 && (
<div className="absolute right-2 top-0 bottom-0 flex items-center justify-center">
<X
className="text-vscode-input-foreground opacity-50 hover:opacity-100 size-4 p-0.5 cursor-pointer"
onClick={onClearSearch}
/>
</div>
)}
</div>
<CommandList>
<CommandEmpty>
{searchValue && (
<div className="py-2 px-1 text-sm">
{t("prompts:modes.noMatchFound")}
</div>
)}
</CommandEmpty>
<CommandGroup>
{modes
.filter((modeConfig) =>
searchValue
? modeConfig.name
.toLowerCase()
.includes(searchValue.toLowerCase())
: true,
)
.map((modeConfig) => (
<CommandItem
key={modeConfig.slug}
value={modeConfig.slug}
onSelect={() => {
handleModeSwitch(modeConfig)
setOpen(false)
}}
data-testid={`mode-option-${modeConfig.slug}`}>
<div className="flex items-center justify-between w-full">
<span>{modeConfig.name}</span>
<span className="text-foreground">{modeConfig.slug}</span>
</div>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
</div>
</div>
<div style={{ marginBottom: "20px" }}>
<div className="mb-5">
{/* Only show name and delete for custom modes */}
{visualMode && findModeBySlug(visualMode, customModes) && (
<div className="flex gap-3 mb-4">
<div className="flex-1">
<div className="font-bold mb-1">{t("prompts:createModeDialog.name.label")}</div>
<div className="flex gap-2">
<VSCodeTextField
<Input
type="text"
value={getModeProperty(findModeBySlug(visualMode, customModes), "name") ?? ""}
onChange={(e: Event | React.FormEvent<HTMLElement>) => {
const target =
(e as CustomEvent)?.detail?.target ||
((e as any).target as HTMLInputElement)
onChange={(e) => {
const customMode = findModeBySlug(visualMode, customModes)
if (customMode) {
updateCustomMode(visualMode, {
...customMode,
name: target.value,
name: e.target.value,
source: customMode.source || "global",
})
}
@ -541,7 +622,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
</div>
</div>
)}
<div style={{ marginBottom: "16px" }}>
<div className="mb-4">
<div className="flex justify-between items-center mb-1">
<div className="font-bold">{t("prompts:roleDefinition.title")}</div>
{!findModeBySlug(visualMode, customModes) && (
@ -563,7 +644,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
<div className="text-sm text-vscode-descriptionForeground mb-2">
{t("prompts:roleDefinition.description")}
</div>
<VSCodeTextArea
<Textarea
value={(() => {
const customMode = findModeBySlug(visualMode, customModes)
const prompt = customModePrompts?.[visualMode] as PromptComponent
@ -575,7 +656,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
})()}
onChange={(e) => {
const value =
(e as CustomEvent)?.detail?.target?.value ||
(e as unknown as CustomEvent)?.detail?.target?.value ||
((e as any).target as HTMLTextAreaElement).value
const customMode = findModeBySlug(visualMode, customModes)
if (customMode) {
@ -592,35 +673,35 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
})
}
}}
className="resize-y w-full"
rows={4}
resize="vertical"
style={{ width: "100%" }}
data-testid={`${getCurrentMode()?.slug || "code"}-prompt-textarea`}
/>
</div>
{/* Mode settings */}
<>
<div style={{ marginBottom: "12px" }}>
<div style={{ fontWeight: "bold", marginBottom: "4px" }}>
{t("prompts:apiConfiguration.title")}
</div>
<div style={{ marginBottom: "8px" }}>
<VSCodeDropdown
value={currentApiConfigName || ""}
onChange={(e: any) => {
const value = e.detail?.target?.value || e.target?.value
<div className="mb-3">
<div className="font-bold mb-1">{t("prompts:apiConfiguration.title")}</div>
<div className="mb-2">
<Select
value={currentApiConfigName}
onValueChange={(value) => {
vscode.postMessage({
type: "loadApiConfiguration",
text: value,
})
}}
className="w-full">
{(listApiConfigMeta || []).map((config) => (
<VSCodeOption key={config.id} value={config.name}>
{config.name}
</VSCodeOption>
))}
</VSCodeDropdown>
}}>
<SelectTrigger className="w-full">
<SelectValue placeholder={t("settings:common.select")} />
</SelectTrigger>
<SelectContent>
{(listApiConfigMeta || []).map((config) => (
<SelectItem key={config.id} value={config.name}>
{config.name}
</SelectItem>
))}
</SelectContent>
</Select>
<div className="text-xs mt-1.5 text-vscode-descriptionForeground">
{t("prompts:apiConfiguration.select")}
</div>
@ -721,15 +802,9 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
</>
{/* Role definition for both built-in and custom modes */}
<div style={{ marginBottom: "8px" }}>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "4px",
}}>
<div style={{ fontWeight: "bold" }}>{t("prompts:customInstructions.title")}</div>
<div className="mb-2">
<div className="flex justify-between items-center mb-1">
<div className="font-bold">{t("prompts:customInstructions.title")}</div>
{!findModeBySlug(visualMode, customModes) && (
<Button
variant="ghost"
@ -746,17 +821,12 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
</Button>
)}
</div>
<div
style={{
fontSize: "13px",
color: "var(--vscode-descriptionForeground)",
marginBottom: "8px",
}}>
<div className="text-[13px] text-vscode-descriptionForeground mb-2">
{t("prompts:customInstructions.description", {
modeName: getCurrentMode()?.name || "Code",
})}
</div>
<VSCodeTextArea
<Textarea
value={(() => {
const customMode = findModeBySlug(visualMode, customModes)
const prompt = customModePrompts?.[visualMode] as PromptComponent
@ -768,7 +838,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
})()}
onChange={(e) => {
const value =
(e as CustomEvent)?.detail?.target?.value ||
(e as unknown as CustomEvent)?.detail?.target?.value ||
((e as any).target as HTMLTextAreaElement).value
const customMode = findModeBySlug(visualMode, customModes)
if (customMode) {
@ -788,16 +858,10 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
}
}}
rows={4}
resize="vertical"
style={{ width: "100%" }}
className="w-full resize-y"
data-testid={`${getCurrentMode()?.slug || "code"}-custom-instructions-textarea`}
/>
<div
style={{
fontSize: "12px",
color: "var(--vscode-descriptionForeground)",
marginTop: "5px",
}}>
<div className="text-xs text-vscode-descriptionForeground mt-1.5">
<Trans
i18nKey="prompts:customInstructions.loadFromFile"
values={{
@ -807,11 +871,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
components={{
span: (
<span
style={{
color: "var(--vscode-textLink-foreground)",
cursor: "pointer",
textDecoration: "underline",
}}
className="text-vscode-textLink-foreground cursor-pointer underline"
onClick={() => {
const currentMode = getCurrentMode()
if (!currentMode) return
@ -834,13 +894,8 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
</div>
</div>
<div
style={{
paddingBottom: "40px",
marginBottom: "20px",
borderBottom: "1px solid var(--vscode-input-border)",
}}>
<div style={{ display: "flex", gap: "8px" }}>
<div className="pb-4 border-b border-vscode-input-border">
<div className="flex gap-2">
<Button
variant="default"
onClick={() => {
@ -874,7 +929,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
</div>
{/* Custom System Prompt Disclosure */}
<div className="mt-12">
<div className="mt-4">
<button
onClick={() => setIsSystemPromptDisclosureOpen(!isSystemPromptDisclosureOpen)}
className="flex items-center text-xs text-vscode-foreground hover:text-vscode-textLink-foreground focus:outline-none"
@ -918,18 +973,18 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
</div>
<div className="pb-5 border-b border-vscode-input-border">
<h3 style={{ color: "var(--vscode-foreground)", marginBottom: "12px" }}>
{t("prompts:globalCustomInstructions.title")}
</h3>
<h3 className="text-vscode-foreground mb-3">{t("prompts:globalCustomInstructions.title")}</h3>
<div className="text-sm text-vscode-descriptionForeground mb-2">
{t("prompts:globalCustomInstructions.description", { language: i18next.language })}
{t("prompts:globalCustomInstructions.description", {
language: i18next.language,
})}
</div>
<VSCodeTextArea
value={customInstructions ?? ""}
<Textarea
value={customInstructions}
onChange={(e) => {
const value =
(e as CustomEvent)?.detail?.target?.value ||
(e as unknown as CustomEvent)?.detail?.target?.value ||
((e as any).target as HTMLTextAreaElement).value
setCustomInstructions(value || undefined)
vscode.postMessage({
@ -938,21 +993,16 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
})
}}
rows={4}
resize="vertical"
className="w-full"
className="w-full resize-y"
data-testid="global-custom-instructions-textarea"
/>
<div className="text-xs text-vscode-descriptionForeground mt-1.5 mb-10">
<div className="text-xs text-vscode-descriptionForeground mt-1.5">
<Trans
i18nKey="prompts:globalCustomInstructions.loadFromFile"
components={{
span: (
<span
style={{
color: "var(--vscode-textLink-foreground)",
cursor: "pointer",
textDecoration: "underline",
}}
className="text-vscode-textLink-foreground cursor-pointer underline"
onClick={() =>
vscode.postMessage({
type: "openFile",
@ -970,156 +1020,113 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
</div>
</div>
<div
style={{
marginTop: "20px",
paddingBottom: "60px",
borderBottom: "1px solid var(--vscode-input-border)",
}}>
<h3 style={{ color: "var(--vscode-foreground)", marginBottom: "12px" }}>
{t("prompts:supportPrompts.title")}
</h3>
<div
style={{
display: "flex",
gap: "8px",
alignItems: "center",
marginBottom: "12px",
flexWrap: "wrap",
padding: "4px 0",
}}>
{Object.keys(supportPrompt.default).map((type) => (
<button
key={type}
data-testid={`${type}-tab`}
data-active={activeSupportTab === type ? "true" : "false"}
onClick={() => setActiveSupportTab(type as SupportPromptType)}
style={{
padding: "4px 8px",
border: "none",
background: activeSupportTab === type ? "var(--vscode-button-background)" : "none",
color:
activeSupportTab === type
? "var(--vscode-button-foreground)"
: "var(--vscode-foreground)",
cursor: "pointer",
opacity: activeSupportTab === type ? 1 : 0.8,
borderRadius: "3px",
fontWeight: "bold",
}}>
{t(`prompts:supportPrompts.types.${type}.label`)}
</button>
))}
<div className="mt-5 pb-15 border-b border-vscode-input-border">
<h3 className="text-vscode-foreground mb-3">{t("prompts:supportPrompts.title")}</h3>
<div className="flex gap-4 items-center flex-wrap py-1">
<Select
value={activeSupportOption}
onValueChange={(type) => setActiveSupportOption(type as SupportPromptType)}>
<SelectTrigger className="w-full" data-testid="support-prompt-select-trigger">
<SelectValue placeholder={t("settings:common.select")} />
</SelectTrigger>
<SelectContent>
{Object.keys(supportPrompt.default).map((type) => (
<SelectItem key={type} value={type} data-testid={`${type}-option`}>
{t(`prompts:supportPrompts.types.${type}.label`)}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* Support prompt description */}
<div
style={{
fontSize: "13px",
color: "var(--vscode-descriptionForeground)",
margin: "8px 0 16px",
}}>
{t(`prompts:supportPrompts.types.${activeSupportTab}.description`)}
<div className="text-[13px] text-vscode-descriptionForeground my-2 mb-4">
{t(`prompts:supportPrompts.types.${activeSupportOption}.description`)}
</div>
{/* Show active tab content */}
<div key={activeSupportTab}>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "4px",
}}>
<div style={{ fontWeight: "bold" }}>{t("prompts:supportPrompts.prompt")}</div>
<div key={activeSupportOption}>
<div className="flex justify-between items-center mb-1">
<div className="font-bold">{t("prompts:supportPrompts.prompt")}</div>
<Button
variant="ghost"
size="icon"
onClick={() => handleSupportReset(activeSupportTab)}
title={t("prompts:supportPrompts.resetPrompt", { promptType: activeSupportTab })}>
onClick={() => handleSupportReset(activeSupportOption)}
title={t("prompts:supportPrompts.resetPrompt", {
promptType: activeSupportOption,
})}>
<span className="codicon codicon-discard"></span>
</Button>
</div>
<VSCodeTextArea
value={getSupportPromptValue(activeSupportTab)}
<Textarea
value={getSupportPromptValue(activeSupportOption)}
onChange={(e) => {
const value =
(e as CustomEvent)?.detail?.target?.value ||
(e as unknown as CustomEvent)?.detail?.target?.value ||
((e as any).target as HTMLTextAreaElement).value
const trimmedValue = value.trim()
updateSupportPrompt(activeSupportTab, trimmedValue || undefined)
updateSupportPrompt(activeSupportOption, trimmedValue || undefined)
}}
rows={6}
resize="vertical"
style={{ width: "100%" }}
className="resize-y w-full"
/>
{activeSupportTab === "ENHANCE" && (
{activeSupportOption === "ENHANCE" && (
<>
<div>
<div
style={{
color: "var(--vscode-foreground)",
fontSize: "13px",
marginBottom: "20px",
marginTop: "5px",
}}></div>
<div style={{ marginBottom: "12px" }}>
<div style={{ marginBottom: "8px" }}>
<div style={{ fontWeight: "bold", marginBottom: "4px" }}>
<div className="text-vscode-foreground text-[13px] mb-5 mt-1.5"></div>
<div className="mb-3">
<div className="mb-2">
<div className="font-bold mb-1">
{t("prompts:supportPrompts.enhance.apiConfiguration")}
</div>
<div
style={{
fontSize: "13px",
color: "var(--vscode-descriptionForeground)",
}}>
<div className="text-[13px] text-vscode-descriptionForeground">
{t("prompts:supportPrompts.enhance.apiConfigDescription")}
</div>
</div>
<VSCodeDropdown
value={enhancementApiConfigId || ""}
data-testid="api-config-dropdown"
onChange={(e: any) => {
const value = e.detail?.target?.value || e.target?.value
setEnhancementApiConfigId(value)
<Select
value={enhancementApiConfigId || "-"}
onValueChange={(value) => {
// normalise to empty string for empty value
// because we can't use it directly for the select element
setEnhancementApiConfigId(value === "-" ? "" : value)
vscode.postMessage({
type: "enhancementApiConfigId",
text: value,
})
}}
style={{ width: "300px" }}>
<VSCodeOption value="">
{t("prompts:supportPrompts.enhance.useCurrentConfig")}
</VSCodeOption>
{(listApiConfigMeta || []).map((config) => (
<VSCodeOption key={config.id} value={config.id}>
{config.name}
</VSCodeOption>
))}
</VSCodeDropdown>
}}>
<SelectTrigger data-testid="api-config-select" className="w-full">
<SelectValue
placeholder={t("prompts:supportPrompts.enhance.useCurrentConfig")}
/>
</SelectTrigger>
<SelectContent>
<SelectItem value="-">
{t("prompts:supportPrompts.enhance.useCurrentConfig")}
</SelectItem>
{(listApiConfigMeta || []).map((config) => (
<SelectItem
key={config.id}
value={config.id}
data-testid={`${config.id}-option`}>
{config.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
<div style={{ marginTop: "12px" }}>
<VSCodeTextArea
<div className="mt-4">
<Textarea
value={testPrompt}
onChange={(e) => setTestPrompt((e.target as HTMLTextAreaElement).value)}
placeholder={t("prompts:supportPrompts.enhance.testPromptPlaceholder")}
rows={3}
resize="vertical"
style={{ width: "100%" }}
className="w-full resize-y"
data-testid="test-prompt-textarea"
/>
<div
style={{
marginTop: "8px",
display: "flex",
justifyContent: "flex-start",
alignItems: "center",
gap: 8,
}}>
<div className="mt-2 flex justify-start items-center gap-2">
<Button
variant="default"
onClick={handleTestEnhancement}
@ -1135,92 +1142,50 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
</TabContent>
{isCreateModeDialogOpen && (
<div
style={{
position: "fixed",
inset: 0,
display: "flex",
justifyContent: "flex-end",
backgroundColor: "rgba(0, 0, 0, 0.5)",
zIndex: 1000,
}}>
<div
style={{
width: "calc(100vw - 100px)",
height: "100%",
backgroundColor: "var(--vscode-editor-background)",
boxShadow: "-2px 0 5px rgba(0, 0, 0, 0.2)",
display: "flex",
flexDirection: "column",
position: "relative",
}}>
<div
style={{
flex: 1,
padding: "20px",
overflowY: "auto",
minHeight: 0,
}}>
<div className="fixed inset-0 flex justify-end bg-black/50 z-[1000]">
<div className="w-[calc(100vw-100px)] h-full bg-vscode-editor-background shadow-md flex flex-col relative">
<div className="flex-1 p-5 overflow-y-auto min-h-0">
<Button
variant="ghost"
size="icon"
onClick={() => setIsCreateModeDialogOpen(false)}
style={{
position: "absolute",
top: "20px",
right: "20px",
}}>
className="absolute top-5 right-5">
<span className="codicon codicon-close"></span>
</Button>
<h2 style={{ margin: "0 0 16px" }}>{t("prompts:createModeDialog.title")}</h2>
<div style={{ marginBottom: "16px" }}>
<div style={{ fontWeight: "bold", marginBottom: "4px" }}>
{t("prompts:createModeDialog.name.label")}
</div>
<VSCodeTextField
<h2 className="mb-4">{t("prompts:createModeDialog.title")}</h2>
<div className="mb-4">
<div className="font-bold mb-1">{t("prompts:createModeDialog.name.label")}</div>
<Input
type="text"
value={newModeName}
onChange={(e: Event | React.FormEvent<HTMLElement>) => {
const target =
(e as CustomEvent)?.detail?.target ||
((e as any).target as HTMLInputElement)
handleNameChange(target.value)
onChange={(e) => {
handleNameChange(e.target.value)
}}
style={{ width: "100%" }}
className="w-full"
/>
{nameError && (
<div className="text-xs text-vscode-errorForeground mt-1">{nameError}</div>
)}
</div>
<div style={{ marginBottom: "16px" }}>
<div style={{ fontWeight: "bold", marginBottom: "4px" }}>
{t("prompts:createModeDialog.slug.label")}
</div>
<VSCodeTextField
<div className="mb-4">
<div className="font-bold mb-1">{t("prompts:createModeDialog.slug.label")}</div>
<Input
type="text"
value={newModeSlug}
onChange={(e: Event | React.FormEvent<HTMLElement>) => {
const target =
(e as CustomEvent)?.detail?.target ||
((e as any).target as HTMLInputElement)
setNewModeSlug(target.value)
onChange={(e) => {
setNewModeSlug(e.target.value)
}}
style={{ width: "100%" }}
className="w-full"
/>
<div
style={{
fontSize: "12px",
color: "var(--vscode-descriptionForeground)",
marginTop: "4px",
}}>
<div className="text-xs text-vscode-descriptionForeground mt-1">
{t("prompts:createModeDialog.slug.description")}
</div>
{slugError && (
<div className="text-xs text-vscode-errorForeground mt-1">{slugError}</div>
)}
</div>
<div style={{ marginBottom: "16px" }}>
<div style={{ fontWeight: "bold", marginBottom: "4px" }}>
{t("prompts:createModeDialog.saveLocation.label")}
</div>
<div className="mb-4">
<div className="font-bold mb-1">{t("prompts:createModeDialog.saveLocation.label")}</div>
<div className="text-sm text-vscode-descriptionForeground mb-2">
{t("prompts:createModeDialog.saveLocation.description")}
</div>
@ -1233,12 +1198,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
}}>
<VSCodeRadio value="global">
{t("prompts:createModeDialog.saveLocation.global.label")}
<div
style={{
fontSize: "12px",
color: "var(--vscode-descriptionForeground)",
marginTop: "2px",
}}>
<div className="text-xs text-vscode-descriptionForeground mt-0.5">
{t("prompts:createModeDialog.saveLocation.global.description")}
</div>
</VSCodeRadio>
@ -1263,17 +1223,13 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
}}>
{t("prompts:createModeDialog.roleDefinition.description")}
</div>
<VSCodeTextArea
<Textarea
value={newModeRoleDefinition}
onChange={(e) => {
const value =
(e as CustomEvent)?.detail?.target?.value ||
((e as any).target as HTMLTextAreaElement).value
setNewModeRoleDefinition(value)
setNewModeRoleDefinition(e.target.value)
}}
rows={4}
resize="vertical"
style={{ width: "100%" }}
className="w-full resize-y"
/>
{roleDefinitionError && (
<div className="text-xs text-vscode-errorForeground mt-1">
@ -1281,24 +1237,12 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
</div>
)}
</div>
<div style={{ marginBottom: "16px" }}>
<div style={{ fontWeight: "bold", marginBottom: "4px" }}>
{t("prompts:createModeDialog.tools.label")}
</div>
<div
style={{
fontSize: "13px",
color: "var(--vscode-descriptionForeground)",
marginBottom: "8px",
}}>
<div className="mb-4">
<div className="font-bold mb-1">{t("prompts:createModeDialog.tools.label")}</div>
<div className="text-[13px] text-vscode-descriptionForeground mb-2">
{t("prompts:createModeDialog.tools.description")}
</div>
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fill, minmax(200px, 1fr))",
gap: "8px",
}}>
<div className="grid grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-2">
{availableGroups.map((group) => (
<VSCodeCheckbox
key={group}
@ -1323,41 +1267,24 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
<div className="text-xs text-vscode-errorForeground mt-1">{groupsError}</div>
)}
</div>
<div style={{ marginBottom: "16px" }}>
<div style={{ fontWeight: "bold", marginBottom: "4px" }}>
<div className="mb-4">
<div className="font-bold mb-1">
{t("prompts:createModeDialog.customInstructions.label")}
</div>
<div
style={{
fontSize: "13px",
color: "var(--vscode-descriptionForeground)",
marginBottom: "8px",
}}>
<div className="text-[13px] text-vscode-descriptionForeground mb-2">
{t("prompts:createModeDialog.customInstructions.description")}
</div>
<VSCodeTextArea
<Textarea
value={newModeCustomInstructions}
onChange={(e) => {
const value =
(e as CustomEvent)?.detail?.target?.value ||
((e as any).target as HTMLTextAreaElement).value
setNewModeCustomInstructions(value)
setNewModeCustomInstructions(e.target.value)
}}
rows={4}
resize="vertical"
style={{ width: "100%" }}
className="w-full resize-y"
/>
</div>
</div>
<div
style={{
display: "flex",
justifyContent: "flex-end",
padding: "12px 20px",
gap: "8px",
borderTop: "1px solid var(--vscode-editor-lineHighlightBorder)",
backgroundColor: "var(--vscode-editor-background)",
}}>
<div className="flex justify-end p-3 px-5 gap-2 border-t border-vscode-editor-lineHighlightBorder bg-vscode-editor-background">
<Button variant="secondary" onClick={() => setIsCreateModeDialogOpen(false)}>
{t("prompts:createModeDialog.buttons.cancel")}
</Button>
@ -1370,71 +1297,27 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
)}
{isDialogOpen && (
<div
style={{
position: "fixed",
inset: 0,
display: "flex",
justifyContent: "flex-end",
backgroundColor: "rgba(0, 0, 0, 0.5)",
zIndex: 1000,
}}>
<div
style={{
width: "calc(100vw - 100px)",
height: "100%",
backgroundColor: "var(--vscode-editor-background)",
boxShadow: "-2px 0 5px rgba(0, 0, 0, 0.2)",
display: "flex",
flexDirection: "column",
position: "relative",
}}>
<div
style={{
flex: 1,
padding: "20px",
overflowY: "auto",
minHeight: 0,
}}>
<div className="fixed inset-0 flex justify-end bg-black/50 z-[1000]">
<div className="w-[calc(100vw-100px)] h-full bg-vscode-editor-background shadow-md flex flex-col relative">
<div className="flex-1 p-5 overflow-y-auto min-h-0">
<Button
variant="ghost"
size="icon"
onClick={() => setIsDialogOpen(false)}
style={{
position: "absolute",
top: "20px",
right: "20px",
}}>
className="absolute top-5 right-5">
<span className="codicon codicon-close"></span>
</Button>
<h2 style={{ margin: "0 0 16px" }}>
<h2 className="mb-4">
{selectedPromptTitle ||
t("prompts:systemPrompt.title", { modeName: getCurrentMode()?.name || "Code" })}
t("prompts:systemPrompt.title", {
modeName: getCurrentMode()?.name || "Code",
})}
</h2>
<pre
style={{
padding: "8px",
whiteSpace: "pre-wrap",
wordBreak: "break-word",
fontFamily: "var(--vscode-editor-font-family)",
fontSize: "var(--vscode-editor-font-size)",
color: "var(--vscode-editor-foreground)",
backgroundColor: "var(--vscode-editor-background)",
border: "1px solid var(--vscode-editor-lineHighlightBorder)",
borderRadius: "4px",
overflowY: "auto",
}}>
<pre className="p-2 whitespace-pre-wrap break-words font-mono text-vscode-editor-font-size text-vscode-editor-foreground bg-vscode-editor-background border border-vscode-editor-lineHighlightBorder rounded overflow-y-auto">
{selectedPromptContent}
</pre>
</div>
<div
style={{
display: "flex",
justifyContent: "flex-end",
padding: "12px 20px",
borderTop: "1px solid var(--vscode-editor-lineHighlightBorder)",
backgroundColor: "var(--vscode-editor-background)",
}}>
<div className="flex justify-end p-3 px-5 border-t border-vscode-editor-lineHighlightBorder bg-vscode-editor-background">
<Button variant="secondary" onClick={() => setIsDialogOpen(false)}>
{t("prompts:createModeDialog.close")}
</Button>

View file

@ -10,6 +10,22 @@ jest.mock("@src/utils/vscode", () => ({
},
}))
// Mock all lucide-react icons with a proxy to handle any icon requested
jest.mock("lucide-react", () => {
return new Proxy(
{},
{
get: function (obj, prop) {
// Return a component factory for any icon that's requested
if (prop === "__esModule") {
return true
}
return () => <div data-testid={`${String(prop)}-icon`}>{String(prop)}</div>
},
},
)
})
const mockExtensionState = {
customModePrompts: {},
listApiConfigMeta: [
@ -19,6 +35,9 @@ const mockExtensionState = {
enhancementApiConfigId: "",
setEnhancementApiConfigId: jest.fn(),
mode: "code",
customModes: [],
customSupportPrompts: [],
currentApiConfigName: "",
customInstructions: "Initial instructions",
setCustomInstructions: jest.fn(),
}
@ -32,69 +51,67 @@ const renderPromptsView = (props = {}) => {
)
}
class MockResizeObserver {
observe() {}
unobserve() {}
disconnect() {}
}
global.ResizeObserver = MockResizeObserver
Element.prototype.scrollIntoView = jest.fn()
describe("PromptsView", () => {
beforeEach(() => {
jest.clearAllMocks()
})
it("renders all mode tabs", () => {
it("displays the current mode name in the select trigger", () => {
renderPromptsView({ mode: "code" })
const selectTrigger = screen.getByTestId("mode-select-trigger")
expect(selectTrigger).toHaveTextContent("Code")
})
it("opens the mode selection popover when the trigger is clicked", async () => {
renderPromptsView()
expect(screen.getByTestId("code-tab")).toBeInTheDocument()
expect(screen.getByTestId("ask-tab")).toBeInTheDocument()
expect(screen.getByTestId("architect-tab")).toBeInTheDocument()
const selectTrigger = screen.getByTestId("mode-select-trigger")
fireEvent.click(selectTrigger)
await waitFor(() => {
expect(selectTrigger).toHaveAttribute("aria-expanded", "true")
})
})
it("defaults to current mode as active tab", () => {
renderPromptsView({ mode: "ask" })
it("filters mode options based on search input", async () => {
renderPromptsView()
const selectTrigger = screen.getByTestId("mode-select-trigger")
fireEvent.click(selectTrigger)
const codeTab = screen.getByTestId("code-tab")
const askTab = screen.getByTestId("ask-tab")
const architectTab = screen.getByTestId("architect-tab")
const searchInput = screen.getByTestId("mode-search-input")
fireEvent.change(searchInput, { target: { value: "ask" } })
expect(askTab).toHaveAttribute("data-active", "true")
expect(codeTab).toHaveAttribute("data-active", "false")
expect(architectTab).toHaveAttribute("data-active", "false")
await waitFor(() => {
expect(screen.getByTestId("mode-option-ask")).toBeInTheDocument()
expect(screen.queryByTestId("mode-option-code")).not.toBeInTheDocument()
expect(screen.queryByTestId("mode-option-architect")).not.toBeInTheDocument()
})
})
it("switches between tabs correctly", async () => {
const { rerender } = render(
<ExtensionStateContext.Provider value={{ ...mockExtensionState, mode: "code" } as any}>
<PromptsView onDone={jest.fn()} />
</ExtensionStateContext.Provider>,
)
it("selects a mode from the dropdown and sends update message", async () => {
renderPromptsView()
const selectTrigger = screen.getByTestId("mode-select-trigger")
fireEvent.click(selectTrigger)
const codeTab = screen.getByTestId("code-tab")
const askTab = screen.getByTestId("ask-tab")
const architectTab = screen.getByTestId("architect-tab")
const askOption = await waitFor(() => screen.getByTestId("mode-option-ask"))
fireEvent.click(askOption)
// Initial state matches current mode (code)
expect(codeTab).toHaveAttribute("data-active", "true")
expect(askTab).toHaveAttribute("data-active", "false")
expect(architectTab).toHaveAttribute("data-active", "false")
// Click Ask tab and update context
fireEvent.click(askTab)
rerender(
<ExtensionStateContext.Provider value={{ ...mockExtensionState, mode: "ask" } as any}>
<PromptsView onDone={jest.fn()} />
</ExtensionStateContext.Provider>,
)
expect(askTab).toHaveAttribute("data-active", "true")
expect(codeTab).toHaveAttribute("data-active", "false")
expect(architectTab).toHaveAttribute("data-active", "false")
// Click Architect tab and update context
fireEvent.click(architectTab)
rerender(
<ExtensionStateContext.Provider value={{ ...mockExtensionState, mode: "architect" } as any}>
<PromptsView onDone={jest.fn()} />
</ExtensionStateContext.Provider>,
)
expect(architectTab).toHaveAttribute("data-active", "true")
expect(askTab).toHaveAttribute("data-active", "false")
expect(codeTab).toHaveAttribute("data-active", "false")
expect(mockExtensionState.setEnhancementApiConfigId).not.toHaveBeenCalled() // Ensure this is not called by mode switch
expect(vscode.postMessage).toHaveBeenCalledWith({
type: "mode",
text: "ask",
})
await waitFor(() => {
expect(selectTrigger).toHaveAttribute("aria-expanded", "false")
})
})
it("handles prompt changes correctly", async () => {
@ -159,21 +176,19 @@ describe("PromptsView", () => {
it("handles API configuration selection", async () => {
renderPromptsView()
// Click the ENHANCE tab first to show the API config dropdown
const enhanceTab = screen.getByTestId("ENHANCE-tab")
fireEvent.click(enhanceTab)
const trigger = screen.getByTestId("support-prompt-select-trigger")
fireEvent.click(trigger)
// Wait for the ENHANCE tab click to take effect
const dropdown = await waitFor(() => screen.getByTestId("api-config-dropdown"))
fireEvent.change(dropdown, {
target: { value: "config1" },
})
const enhanceOption = await waitFor(() => screen.getByTestId("ENHANCE-option"))
fireEvent.click(enhanceOption)
expect(mockExtensionState.setEnhancementApiConfigId).toHaveBeenCalledWith("config1")
expect(vscode.postMessage).toHaveBeenCalledWith({
type: "enhancementApiConfigId",
text: "config1",
})
const apiConfig = await waitFor(() => screen.getByTestId("api-config-select"))
fireEvent.click(apiConfig)
const config1 = await waitFor(() => screen.getByTestId("config1-option"))
fireEvent.click(config1)
expect(mockExtensionState.setEnhancementApiConfigId).toHaveBeenCalledWith("config1") // Ensure this is not called by mode switch
})
it("handles clearing custom instructions correctly", async () => {

View file

@ -2,9 +2,9 @@ import { HTMLAttributes, useState } from "react"
import { X } from "lucide-react"
import { useAppTranslation } from "@/i18n/TranslationContext"
import { VSCodeTextField, VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
import { vscode } from "@/utils/vscode"
import { Button, Slider } from "@/components/ui"
import { Button, Input, Slider } from "@/components/ui"
import { SetCachedStateField } from "./types"
import { SectionHeader } from "./SectionHeader"
@ -203,9 +203,9 @@ export const AutoApproveSettings = ({
</div>
<div className="flex gap-2">
<VSCodeTextField
<Input
value={commandInput}
onInput={(e: any) => setCommandInput(e.target.value)}
onChange={(e: any) => setCommandInput(e.target.value)}
onKeyDown={(e: any) => {
if (e.key === "Enter") {
e.preventDefault()
@ -216,7 +216,7 @@ export const AutoApproveSettings = ({
className="grow"
data-testid="command-input"
/>
<Button onClick={handleAddCommand} data-testid="add-command-button">
<Button className="h-8" onClick={handleAddCommand} data-testid="add-command-button">
{t("settings:autoApprove.execute.addButton")}
</Button>
</div>
@ -234,7 +234,7 @@ export const AutoApproveSettings = ({
}}>
<div className="flex flex-row items-center gap-1">
<div>{cmd}</div>
<X className="text-primary-foreground scale-75" />
<X className="text-foreground scale-75" />
</div>
</Button>
))}

View file

@ -4,7 +4,7 @@ import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
import { Database } from "lucide-react"
import { cn } from "@/lib/utils"
import { Slider } from "@/components/ui"
import { Input, Slider } from "@/components/ui"
import { SetCachedStateField } from "./types"
import { SectionHeader } from "./SectionHeader"
@ -96,7 +96,7 @@ export const ContextManagementSettings = ({
<div className="flex flex-col gap-2">
<span className="font-medium">{t("settings:contextManagement.maxReadFile.label")}</span>
<div className="flex items-center gap-4">
<input
<Input
type="number"
pattern="-?[0-9]*"
className="w-24 bg-vscode-input-background text-vscode-input-foreground border border-vscode-input-border px-2 py-1 rounded text-right [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none disabled:opacity-50"

View file

@ -90,7 +90,7 @@ function AlertDialogAction({ className, ...props }: React.ComponentProps<typeof
<AlertDialogPrimitive.Action
className={cn(
buttonVariants(),
"bg-vscode-button-background text-vscode-button-foreground hover:bg-vscode-button-hoverBackground border border-transparent h-6 px-3 py-1",
"bg-vscode-button-background text-vscode-button-foreground hover:bg-vscode-button-hoverBackground h-6 px-3 py-1 border",
className,
)}
{...props}
@ -103,7 +103,7 @@ function AlertDialogCancel({ className, ...props }: React.ComponentProps<typeof
<AlertDialogPrimitive.Cancel
className={cn(
buttonVariants({ variant: "outline" }),
"bg-vscode-button-secondaryBackground text-vscode-button-secondaryForeground hover:bg-vscode-button-secondaryHoverBackground border border-vscode-button-border h-6 px-3 py-1",
"bg-vscode-button-secondaryBackground text-vscode-button-secondaryForeground hover:bg-vscode-button-secondaryHoverBackground h-6 px-3 py-1 border",
className,
)}
{...props}

View file

@ -9,13 +9,12 @@ const buttonVariants = cva(
{
variants: {
variant: {
default:
"border border-vscode-input-border bg-primary text-primary-foreground shadow hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
default: "border border-vscode-input-border bg-primary text-primary-foreground hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-vscode-input-border bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground",
"border border-vscode-input-border bg-transparent hover:bg-accent hover:text-accent-foreground",
secondary:
"border border-vscode-input-border bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
"border border-vscode-input-border bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
combobox:

View file

@ -11,10 +11,10 @@ const Slider = React.forwardRef<
ref={ref}
className={cn("relative flex w-full touch-none select-none items-center", className)}
{...props}>
<SliderPrimitive.Track className="relative w-full h-[8px] grow overflow-hidden bg-accent border border-[#767676] dark:border-[#858585] rounded-sm">
<SliderPrimitive.Track className="relative w-full h-[8px] grow overflow-hidden bg-accent rounded-sm border">
<SliderPrimitive.Range className="absolute h-full bg-vscode-button-background" />
</SliderPrimitive.Track>
<SliderPrimitive.Thumb className="block h-3 w-3 rounded-full border border-primary/50 bg-primary shadow transition-colors cursor-pointer focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50" />
<SliderPrimitive.Thumb className="block h-3 w-3 rounded-full border border-primary/50 bg-vscode-button-background transition-colors cursor-pointer focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50" />
</SliderPrimitive.Root>
))
Slider.displayName = SliderPrimitive.Root.displayName

View file

@ -7,8 +7,8 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, React.ComponentProps<"tex
return (
<textarea
className={cn(
"flex min-h-[60px] w-full rounded-xs px-3 py-2 text-base shadow-sm placeholder:text-muted-foreground focus:outline-0 focus-visible:outline-none focus-visible:border-vscode-focusBorder disabled:cursor-not-allowed disabled:opacity-50",
"border-[var(--vscode-input-border,var(--vscode-input-background))] focus-visible:border-vscode-focusBorder",
"flex min-h-[60px] w-full rounded-xs px-3 py-2 text-base placeholder:text-muted-foreground focus:outline-0 focus-visible:outline-none focus-visible:border-vscode-focusBorder disabled:cursor-not-allowed disabled:opacity-50",
"border border-[var(--vscode-input-border,var(--vscode-input-background))] focus-visible:border-vscode-focusBorder",
"bg-vscode-input-background",
"text-vscode-input-foreground",
className,

View file

@ -7,7 +7,8 @@
"editModesConfig": "Editar configuració de modes",
"editGlobalModes": "Editar modes globals",
"editProjectModes": "Editar modes de projecte (.roomodes)",
"createModeHelpText": "Feu clic a + per crear un nou mode personalitzat, o simplement demaneu a Roo al xat que en creï un per a vostè!"
"createModeHelpText": "Feu clic a + per crear un nou mode personalitzat, o simplement demaneu a Roo al xat que en creï un per a vostè!",
"selectMode": "Cerqueu modes"
},
"apiConfiguration": {
"title": "Configuració d'API",

View file

@ -7,7 +7,8 @@
"editModesConfig": "Moduskonfiguration bearbeiten",
"editGlobalModes": "Globale Modi bearbeiten",
"editProjectModes": "Projektmodi bearbeiten (.roomodes)",
"createModeHelpText": "Klicke auf +, um einen neuen benutzerdefinierten Modus zu erstellen, oder bitte Roo einfach im Chat, einen für dich zu erstellen!"
"createModeHelpText": "Klicke auf +, um einen neuen benutzerdefinierten Modus zu erstellen, oder bitte Roo einfach im Chat, einen für dich zu erstellen!",
"selectMode": "Modi suchen"
},
"apiConfiguration": {
"title": "API-Konfiguration",

View file

@ -7,7 +7,8 @@
"editModesConfig": "Edit modes configuration",
"editGlobalModes": "Edit Global Modes",
"editProjectModes": "Edit Project Modes (.roomodes)",
"createModeHelpText": "Hit the + to create a new custom mode, or just ask Roo in chat to create one for you!"
"createModeHelpText": "Hit the + to create a new custom mode, or just ask Roo in chat to create one for you!",
"selectMode": "Search modes"
},
"apiConfiguration": {
"title": "API Configuration",

View file

@ -7,7 +7,8 @@
"editModesConfig": "Editar configuración de modos",
"editGlobalModes": "Editar modos globales",
"editProjectModes": "Editar modos del proyecto (.roomodes)",
"createModeHelpText": "¡Haz clic en + para crear un nuevo modo personalizado, o simplemente pídele a Roo en el chat que te cree uno!"
"createModeHelpText": "¡Haz clic en + para crear un nuevo modo personalizado, o simplemente pídele a Roo en el chat que te cree uno!",
"selectMode": "Buscar modos"
},
"apiConfiguration": {
"title": "Configuración de API",

View file

@ -7,7 +7,8 @@
"editModesConfig": "Modifier la configuration des modes",
"editGlobalModes": "Modifier les modes globaux",
"editProjectModes": "Modifier les modes du projet (.roomodes)",
"createModeHelpText": "Cliquez sur + pour créer un nouveau mode personnalisé, ou demandez simplement à Roo dans le chat de vous en créer un !"
"createModeHelpText": "Cliquez sur + pour créer un nouveau mode personnalisé, ou demandez simplement à Roo dans le chat de vous en créer un !",
"selectMode": "Rechercher les modes"
},
"apiConfiguration": {
"title": "Configuration API",

View file

@ -7,7 +7,8 @@
"editModesConfig": "मोड कॉन्फ़िगरेशन संपादित करें",
"editGlobalModes": "ग्लोबल मोड्स संपादित करें",
"editProjectModes": "प्रोजेक्ट मोड्स संपादित करें (.roomodes)",
"createModeHelpText": "नया कस्टम मोड बनाने के लिए + पर क्लिक करें, या बस चैट में Roo से आपके लिए एक बनाने को कहें!"
"createModeHelpText": "नया कस्टम मोड बनाने के लिए + पर क्लिक करें, या बस चैट में Roo से आपके लिए एक बनाने को कहें!",
"selectMode": "मोड खोजें"
},
"apiConfiguration": {
"title": "API कॉन्फ़िगरेशन",

View file

@ -7,7 +7,8 @@
"editModesConfig": "Modifica configurazione modalità",
"editGlobalModes": "Modifica modalità globali",
"editProjectModes": "Modifica modalità di progetto (.roomodes)",
"createModeHelpText": "Clicca sul + per creare una nuova modalità personalizzata, o chiedi semplicemente a Roo nella chat di crearne una per te!"
"createModeHelpText": "Clicca sul + per creare una nuova modalità personalizzata, o chiedi semplicemente a Roo nella chat di crearne una per te!",
"selectMode": "Cerca modalità"
},
"apiConfiguration": {
"title": "Configurazione API",

View file

@ -7,7 +7,8 @@
"editModesConfig": "モード設定を編集",
"editGlobalModes": "グローバルモードを編集",
"editProjectModes": "プロジェクトモードを編集 (.roomodes)",
"createModeHelpText": "+ をクリックして新しいカスタムモードを作成するか、チャットで Roo に作成を依頼してください!"
"createModeHelpText": "+ をクリックして新しいカスタムモードを作成するか、チャットで Roo に作成を依頼してください!",
"selectMode": "モードを検索"
},
"apiConfiguration": {
"title": "API設定",

View file

@ -7,7 +7,8 @@
"editModesConfig": "모드 구성 편집",
"editGlobalModes": "전역 모드 편집",
"editProjectModes": "프로젝트 모드 편집 (.roomodes)",
"createModeHelpText": "새 커스텀 모드를 만들려면 + 버튼을 클릭하거나, 채팅에서 Roo에게 만들어달라고 요청하세요!"
"createModeHelpText": "새 커스텀 모드를 만들려면 + 버튼을 클릭하거나, 채팅에서 Roo에게 만들어달라고 요청하세요!",
"selectMode": "모드 검색"
},
"apiConfiguration": {
"title": "API 구성",

View file

@ -7,7 +7,8 @@
"editModesConfig": "Edytuj konfigurację trybów",
"editGlobalModes": "Edytuj tryby globalne",
"editProjectModes": "Edytuj tryby projektu (.roomodes)",
"createModeHelpText": "Kliknij +, aby utworzyć nowy niestandardowy tryb, lub po prostu poproś Roo w czacie, aby utworzył go dla Ciebie!"
"createModeHelpText": "Kliknij +, aby utworzyć nowy niestandardowy tryb, lub po prostu poproś Roo w czacie, aby utworzył go dla Ciebie!",
"selectMode": "Szukaj trybów"
},
"apiConfiguration": {
"title": "Konfiguracja API",

View file

@ -7,7 +7,8 @@
"editModesConfig": "Editar configuração de modos",
"editGlobalModes": "Editar modos globais",
"editProjectModes": "Editar modos do projeto (.roomodes)",
"createModeHelpText": "Clique em + para criar um novo modo personalizado, ou simplesmente peça ao Roo no chat para criar um para você!"
"createModeHelpText": "Clique em + para criar um novo modo personalizado, ou simplesmente peça ao Roo no chat para criar um para você!",
"selectMode": "Buscar modos"
},
"apiConfiguration": {
"title": "Configuração de API",

View file

@ -7,7 +7,8 @@
"editModesConfig": "Редактировать конфигурацию режимов",
"editGlobalModes": "Редактировать глобальные режимы",
"editProjectModes": "Редактировать режимы проекта (.roomodes)",
"createModeHelpText": "Нажмите +, чтобы создать новый пользовательский режим, или просто попросите Roo в чате создать его для вас!"
"createModeHelpText": "Нажмите +, чтобы создать новый пользовательский режим, или просто попросите Roo в чате создать его для вас!",
"selectMode": "Поиск режимов"
},
"apiConfiguration": {
"title": "Конфигурация API",

View file

@ -7,7 +7,8 @@
"editModesConfig": "Mod yapılandırmasını düzenle",
"editGlobalModes": "Global modları düzenle",
"editProjectModes": "Proje modlarını düzenle (.roomodes)",
"createModeHelpText": "Yeni bir özel mod oluşturmak için + düğmesine tıklayın veya sohbette Roo'dan sizin için bir tane oluşturmasını isteyin!"
"createModeHelpText": "Yeni bir özel mod oluşturmak için + düğmesine tıklayın veya sohbette Roo'dan sizin için bir tane oluşturmasını isteyin!",
"selectMode": "Modları Ara"
},
"apiConfiguration": {
"title": "API Yapılandırması",

View file

@ -7,7 +7,8 @@
"editModesConfig": "Chỉnh sửa cấu hình chế độ",
"editGlobalModes": "Chỉnh sửa chế độ toàn cục",
"editProjectModes": "Chỉnh sửa chế độ dự án (.roomodes)",
"createModeHelpText": "Nhấn + để tạo chế độ tùy chỉnh mới, hoặc chỉ cần yêu cầu Roo trong chat tạo một chế độ cho bạn!"
"createModeHelpText": "Nhấn + để tạo chế độ tùy chỉnh mới, hoặc chỉ cần yêu cầu Roo trong chat tạo một chế độ cho bạn!",
"selectMode": "Tìm kiếm chế độ"
},
"apiConfiguration": {
"title": "Cấu hình API",

View file

@ -7,7 +7,8 @@
"editModesConfig": "模式设置",
"editGlobalModes": "修改全局模式",
"editProjectModes": "编辑项目模式 (.roomodes)",
"createModeHelpText": "点击 + 创建模式或在对话时让Roo创建一个新模式。"
"createModeHelpText": "点击 + 创建模式或在对话时让Roo创建一个新模式。",
"selectMode": "搜索模式"
},
"apiConfiguration": {
"title": "API配置",

View file

@ -7,7 +7,8 @@
"editModesConfig": "編輯模式設定",
"editGlobalModes": "編輯全域模式",
"editProjectModes": "編輯專案模式 (.roomodes)",
"createModeHelpText": "點選 + 建立新的自訂模式,或者在聊天中直接請 Roo 為您建立!"
"createModeHelpText": "點選 + 建立新的自訂模式,或者在聊天中直接請 Roo 為您建立!",
"selectMode": "搜尋模式"
},
"apiConfiguration": {
"title": "API 設定",