diff --git a/.changeset/model-picker-sections.md b/.changeset/model-picker-sections.md new file mode 100644 index 00000000000..7b0ffaabad2 --- /dev/null +++ b/.changeset/model-picker-sections.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Add section headers to model selection dropdowns for "Recommended models" and "All models" diff --git a/webview-ui/src/components/kilocode/chat/ModelSelector.tsx b/webview-ui/src/components/kilocode/chat/ModelSelector.tsx index 1c08983c82d..a22fb30538b 100644 --- a/webview-ui/src/components/kilocode/chat/ModelSelector.tsx +++ b/webview-ui/src/components/kilocode/chat/ModelSelector.tsx @@ -1,5 +1,5 @@ import { useMemo } from "react" -import { SelectDropdown, DropdownOptionType } from "@/components/ui" +import { SelectDropdown, DropdownOptionType, type DropdownOption } from "@/components/ui" import { OPENROUTER_DEFAULT_PROVIDER_NAME, type ProviderSettings } from "@roo-code/types" import { vscode } from "@src/utils/vscode" import { useAppTranslation } from "@src/i18n/TranslationContext" @@ -7,7 +7,7 @@ import { cn } from "@src/lib/utils" import { prettyModelName } from "../../../utils/prettyModelName" import { useProviderModels } from "../hooks/useProviderModels" import { getModelIdKey, getSelectedModelId } from "../hooks/useSelectedModel" -import { usePreferredModels } from "@/components/ui/hooks/kilocode/usePreferredModels" +import { useGroupedModelIds } from "@/components/ui/hooks/kilocode/usePreferredModels" interface ModelSelectorProps { currentApiConfigName?: string @@ -32,15 +32,66 @@ export const ModelSelector = ({ const modelIdKey = getModelIdKey({ provider }) const isAutocomplete = apiConfiguration.profileType === "autocomplete" - const modelsIds = usePreferredModels(providerModels) + const { preferredModelIds, restModelIds } = useGroupedModelIds(providerModels) const options = useMemo(() => { - const missingModelIds = modelsIds.indexOf(selectedModelId) >= 0 ? [] : [selectedModelId] - return missingModelIds.concat(modelsIds).map((modelId) => ({ - value: modelId, - label: providerModels[modelId]?.displayName ?? prettyModelName(modelId), - type: DropdownOptionType.ITEM, - })) - }, [modelsIds, providerModels, selectedModelId]) + const result: DropdownOption[] = [] + + // Check if selected model is missing from the lists + const allModelIds = [...preferredModelIds, ...restModelIds] + const isMissingSelectedModel = selectedModelId && !allModelIds.includes(selectedModelId) + + // Add "Recommended models" section if there are preferred models + if (preferredModelIds.length > 0) { + result.push({ + value: "__label_recommended__", + label: t("settings:modelPicker.recommendedModels"), + type: DropdownOptionType.LABEL, + }) + + preferredModelIds.forEach((modelId) => { + result.push({ + value: modelId, + label: providerModels[modelId]?.displayName ?? prettyModelName(modelId), + type: DropdownOptionType.ITEM, + }) + }) + } + + // Add "All models" section + if (restModelIds.length > 0) { + result.push({ + value: "__label_all__", + label: t("settings:modelPicker.allModels"), + type: DropdownOptionType.LABEL, + }) + + // Add missing selected model at the top of "All models" if not in any list + if (isMissingSelectedModel) { + result.push({ + value: selectedModelId, + label: providerModels[selectedModelId]?.displayName ?? prettyModelName(selectedModelId), + type: DropdownOptionType.ITEM, + }) + } + + restModelIds.forEach((modelId) => { + result.push({ + value: modelId, + label: providerModels[modelId]?.displayName ?? prettyModelName(modelId), + type: DropdownOptionType.ITEM, + }) + }) + } else if (isMissingSelectedModel) { + // If there are no rest models but we have a missing selected model, add it + result.push({ + value: selectedModelId, + label: providerModels[selectedModelId]?.displayName ?? prettyModelName(selectedModelId), + type: DropdownOptionType.ITEM, + }) + } + + return result + }, [preferredModelIds, restModelIds, providerModels, selectedModelId, t]) const disabled = isLoading || isError || isAutocomplete diff --git a/webview-ui/src/components/kilocode/chat/__tests__/ModelSelector.spec.tsx b/webview-ui/src/components/kilocode/chat/__tests__/ModelSelector.spec.tsx index e011713b8da..daca799fb0d 100644 --- a/webview-ui/src/components/kilocode/chat/__tests__/ModelSelector.spec.tsx +++ b/webview-ui/src/components/kilocode/chat/__tests__/ModelSelector.spec.tsx @@ -14,8 +14,11 @@ vi.mock("@/i18n/TranslationContext", () => ({ }), })) +// Create a mock function that can be controlled per test +const mockUseGroupedModelIds = vi.fn() + vi.mock("@/components/ui/hooks/kilocode/usePreferredModels", () => ({ - usePreferredModels: () => ["model-1", "model-2"], + useGroupedModelIds: () => mockUseGroupedModelIds(), })) // Create a mock function that can be controlled per test @@ -37,9 +40,17 @@ describe("ModelSelector", () => { } beforeEach(() => { - // Reset mock before each test + // Reset mocks before each test mockUseProviderModels.mockReset() - // Default mock implementation + mockUseGroupedModelIds.mockReset() + + // Default mock implementation for useGroupedModelIds (no preferred models) + mockUseGroupedModelIds.mockReturnValue({ + preferredModelIds: [], + restModelIds: ["model-1", "model-2"], + }) + + // Default mock implementation for useProviderModels mockUseProviderModels.mockReturnValue({ provider: "openai", providerModels: { @@ -188,4 +199,98 @@ describe("ModelSelector", () => { const dropdownTrigger = screen.queryByTestId("dropdown-trigger") expect(dropdownTrigger).not.toBeInTheDocument() }) + + describe("preferred models sections", () => { + test("builds options with section headers when preferred models exist", () => { + // Setup mock to return preferred models + mockUseGroupedModelIds.mockReturnValue({ + preferredModelIds: ["preferred-1", "preferred-2"], + restModelIds: ["model-1", "model-2"], + }) + + mockUseProviderModels.mockReturnValue({ + provider: "openai", + providerModels: { + "preferred-1": { displayName: "Preferred Model 1", preferredIndex: 0 }, + "preferred-2": { displayName: "Preferred Model 2", preferredIndex: 1 }, + "model-1": { displayName: "Model 1" }, + "model-2": { displayName: "Model 2" }, + }, + providerDefaultModel: "model-1", + isLoading: false, + isError: false, + }) + + render( + , + ) + + // Should render the dropdown + const dropdownTrigger = screen.getByTestId("dropdown-trigger") + expect(dropdownTrigger).toBeInTheDocument() + }) + + test("does not add section headers when no preferred models exist", () => { + // Setup mock with no preferred models + mockUseGroupedModelIds.mockReturnValue({ + preferredModelIds: [], + restModelIds: ["model-1", "model-2"], + }) + + render( + , + ) + + // Should render the dropdown + const dropdownTrigger = screen.getByTestId("dropdown-trigger") + expect(dropdownTrigger).toBeInTheDocument() + }) + + test("handles only preferred models without rest models", () => { + // Setup mock with only preferred models (edge case) + mockUseGroupedModelIds.mockReturnValue({ + preferredModelIds: ["preferred-1"], + restModelIds: [], + }) + + mockUseProviderModels.mockReturnValue({ + provider: "openai", + providerModels: { + "preferred-1": { displayName: "Preferred Model 1", preferredIndex: 0 }, + }, + providerDefaultModel: "preferred-1", + isLoading: false, + isError: false, + }) + + render( + , + ) + + // Should render the dropdown with only preferred models section + const dropdownTrigger = screen.getByTestId("dropdown-trigger") + expect(dropdownTrigger).toBeInTheDocument() + }) + }) }) diff --git a/webview-ui/src/components/settings/ModelPicker.tsx b/webview-ui/src/components/settings/ModelPicker.tsx index 1f4fe4fc07e..9c1c933f771 100644 --- a/webview-ui/src/components/settings/ModelPicker.tsx +++ b/webview-ui/src/components/settings/ModelPicker.tsx @@ -1,4 +1,4 @@ -import { useState, useCallback, useEffect, useRef, Fragment } from "react" // kilocode_change Fragment +import { useState, useCallback, useEffect, useRef, useMemo, Fragment } from "react" // kilocode_change Fragment, useMemo import { VSCodeLink } from "@vscode/webview-ui-toolkit/react" import { Trans } from "react-i18next" import { ChevronsUpDown, Check, X, Info } from "lucide-react" @@ -7,7 +7,7 @@ import type { ProviderSettings, ModelInfo, OrganizationAllowList } from "@roo-co import { useAppTranslation } from "@src/i18n/TranslationContext" import { useSelectedModel } from "@/components/ui/hooks/useSelectedModel" -import { usePreferredModels } from "@/components/ui/hooks/kilocode/usePreferredModels" // kilocode_change +import { useGroupedModelIds } from "@/components/ui/hooks/kilocode/usePreferredModels" // kilocode_change // import { filterModels } from "./utils/organizationFilters" // kilocode_change: not doing this import { cn } from "@src/lib/utils" import { @@ -21,7 +21,6 @@ import { PopoverContent, PopoverTrigger, Button, - SelectSeparator, // kilocode_change } from "@src/components/ui" import { useEscapeKey } from "@src/hooks/useEscapeKey" @@ -88,8 +87,9 @@ export const ModelPicker = ({ const selectTimeoutRef = useRef(null) const closeTimeoutRef = useRef(null) - // kilocode_change start - const modelIds = usePreferredModels(models) + // kilocode_change start: Use grouped model IDs for section headers + const { preferredModelIds, restModelIds } = useGroupedModelIds(models) + const modelIds = useMemo(() => [...preferredModelIds, ...restModelIds], [preferredModelIds, restModelIds]) const [isPricingExpanded, setIsPricingExpanded] = useState(false) // kilocode_change end @@ -205,36 +205,51 @@ export const ModelPicker = ({ )} - - {/* kilocode_change start */} - {modelIds.map((model, i) => { - const isPreferred = Number.isInteger(models?.[model]?.preferredIndex) - const previousModelWasPreferred = Number.isInteger( - models?.[modelIds[i - 1]]?.preferredIndex, - ) - return ( - - {!isPreferred && previousModelWasPreferred ? : null} - - - {model} - - - - - ) - })} - {/* kilocode_change end */} - + {/* kilocode_change start: Section headers for recommended and all models */} + {preferredModelIds.length > 0 && ( + + {preferredModelIds.map((model) => ( + + + {model} + + + + ))} + + )} + {restModelIds.length > 0 && ( + + {restModelIds.map((model) => ( + + + {model} + + + + ))} + + )} + {/* kilocode_change end */} {searchValue && !modelIds.includes(searchValue) && (
diff --git a/webview-ui/src/components/ui/hooks/kilocode/__tests__/usePreferredModels.spec.ts b/webview-ui/src/components/ui/hooks/kilocode/__tests__/usePreferredModels.spec.ts new file mode 100644 index 00000000000..7422f1d2614 --- /dev/null +++ b/webview-ui/src/components/ui/hooks/kilocode/__tests__/usePreferredModels.spec.ts @@ -0,0 +1,124 @@ +// kilocode_change - new file +// npx vitest run src/components/ui/hooks/kilocode/__tests__/usePreferredModels.spec.ts + +import { renderHook } from "@testing-library/react" +import { useGroupedModelIds, getGroupedModelIds } from "../usePreferredModels" +import type { ModelInfo } from "@roo-code/types" + +// Helper to create minimal ModelInfo objects for testing +const createModelInfo = (overrides: Partial = {}): ModelInfo => ({ + contextWindow: 100000, + supportsPromptCache: false, + maxTokens: 4096, + ...overrides, +}) + +describe("getGroupedModelIds", () => { + it("returns empty arrays when models is null", () => { + const result = getGroupedModelIds(null) + + expect(result).toEqual({ + preferredModelIds: [], + restModelIds: [], + }) + }) + + it("returns empty arrays when models is empty", () => { + const result = getGroupedModelIds({}) + + expect(result).toEqual({ + preferredModelIds: [], + restModelIds: [], + }) + }) + + it("separates preferred models from rest models", () => { + const models: Record = { + "model-a": createModelInfo({ preferredIndex: 1 }), + "model-b": createModelInfo(), + "model-c": createModelInfo({ preferredIndex: 0 }), + "model-d": createModelInfo(), + } + + const result = getGroupedModelIds(models) + + expect(result.preferredModelIds).toEqual(["model-c", "model-a"]) + expect(result.restModelIds).toEqual(["model-b", "model-d"]) + }) + + it("sorts preferred models by preferredIndex", () => { + const models: Record = { + "model-z": createModelInfo({ preferredIndex: 2 }), + "model-a": createModelInfo({ preferredIndex: 0 }), + "model-m": createModelInfo({ preferredIndex: 1 }), + } + + const result = getGroupedModelIds(models) + + expect(result.preferredModelIds).toEqual(["model-a", "model-m", "model-z"]) + }) + + it("sorts rest models alphabetically", () => { + const models: Record = { + "zebra-model": createModelInfo(), + "alpha-model": createModelInfo(), + "beta-model": createModelInfo(), + } + + const result = getGroupedModelIds(models) + + expect(result.restModelIds).toEqual(["alpha-model", "beta-model", "zebra-model"]) + }) + + it("handles case where all models are preferred", () => { + const models: Record = { + "model-a": createModelInfo({ preferredIndex: 0 }), + "model-b": createModelInfo({ preferredIndex: 1 }), + } + + const result = getGroupedModelIds(models) + + expect(result.preferredModelIds).toEqual(["model-a", "model-b"]) + expect(result.restModelIds).toEqual([]) + }) + + it("handles case where no models are preferred", () => { + const models: Record = { + "model-a": createModelInfo(), + "model-b": createModelInfo(), + } + + const result = getGroupedModelIds(models) + + expect(result.preferredModelIds).toEqual([]) + expect(result.restModelIds).toEqual(["model-a", "model-b"]) + }) +}) + +describe("useGroupedModelIds", () => { + it("returns grouped model IDs", () => { + const models: Record = { + "pref-model": createModelInfo({ preferredIndex: 0 }), + "rest-model": createModelInfo(), + } + + const { result } = renderHook(() => useGroupedModelIds(models)) + + expect(result.current.preferredModelIds).toEqual(["pref-model"]) + expect(result.current.restModelIds).toEqual(["rest-model"]) + }) + + it("memoizes result when models don't change", () => { + const models: Record = { + "model-a": createModelInfo(), + } + + const { result, rerender } = renderHook(() => useGroupedModelIds(models)) + const firstResult = result.current + + rerender() + const secondResult = result.current + + expect(firstResult).toBe(secondResult) + }) +}) diff --git a/webview-ui/src/components/ui/hooks/kilocode/usePreferredModels.ts b/webview-ui/src/components/ui/hooks/kilocode/usePreferredModels.ts index 847625aff7e..6d9e6ac807d 100644 --- a/webview-ui/src/components/ui/hooks/kilocode/usePreferredModels.ts +++ b/webview-ui/src/components/ui/hooks/kilocode/usePreferredModels.ts @@ -1,33 +1,49 @@ import { useMemo } from "react" import type { ModelInfo } from "@roo-code/types" -export const usePreferredModels = (models: Record | null) => { - return useMemo(() => { - if (!models) return [] - - const preferredModelIds = [] - const restModelIds = [] - // first add the preferred models - for (const [key, model] of Object.entries(models)) { - if (Number.isInteger(model.preferredIndex)) { - preferredModelIds.push(key) - } +// Result containing preferred and rest model IDs +export interface GroupedModelIds { + preferredModelIds: string[] + restModelIds: string[] +} + +// Extracts and groups model IDs into preferred and rest categories +export const getGroupedModelIds = (models: Record | null): GroupedModelIds => { + if (!models) { + return { preferredModelIds: [], restModelIds: [] } + } + + const preferredModelIds: string[] = [] + const restModelIds: string[] = [] + + // First add the preferred models + for (const [key, model] of Object.entries(models)) { + if (Number.isInteger(model.preferredIndex)) { + preferredModelIds.push(key) } + } - preferredModelIds.sort((a, b) => { - const modelA = models[a] - const modelB = models[b] - return (modelA.preferredIndex ?? 0) - (modelB.preferredIndex ?? 0) - }) - - // then add the rest - for (const [key] of Object.entries(models)) { - if (!preferredModelIds.includes(key)) { - restModelIds.push(key) - } + preferredModelIds.sort((a, b) => { + const modelA = models[a] + const modelB = models[b] + return (modelA.preferredIndex ?? 0) - (modelB.preferredIndex ?? 0) + }) + + // Then add the rest + for (const [key] of Object.entries(models)) { + if (!preferredModelIds.includes(key)) { + restModelIds.push(key) } - restModelIds.sort((a, b) => a.localeCompare(b)) + } + restModelIds.sort((a, b) => a.localeCompare(b)) + + return { + preferredModelIds, + restModelIds, + } +} - return [...preferredModelIds, ...restModelIds] - }, [models]) +// Hook to get grouped model IDs with section metadata for sectioned dropdowns +export const useGroupedModelIds = (models: Record | null): GroupedModelIds => { + return useMemo(() => getGroupedModelIds(models), [models]) } diff --git a/webview-ui/src/components/ui/select-dropdown.tsx b/webview-ui/src/components/ui/select-dropdown.tsx index 18b544f2d51..78372b55682 100644 --- a/webview-ui/src/components/ui/select-dropdown.tsx +++ b/webview-ui/src/components/ui/select-dropdown.tsx @@ -15,6 +15,7 @@ export enum DropdownOptionType { SEPARATOR = "separator", SHORTCUT = "shortcut", ACTION = "action", + LABEL = "label", // kilocode_change: Section header for grouped options } export interface DropdownOption { @@ -114,7 +115,9 @@ export const SelectDropdown = React.memo( return options .filter( (option) => - option.type !== DropdownOptionType.SEPARATOR && option.type !== DropdownOptionType.SHORTCUT, + option.type !== DropdownOptionType.SEPARATOR && + option.type !== DropdownOptionType.SHORTCUT && + option.type !== DropdownOptionType.LABEL, // kilocode_change: exclude LABEL from search ) .map((option) => ({ original: option, @@ -137,9 +140,13 @@ export const SelectDropdown = React.memo( // Get fuzzy matching items - only perform search if we have a search value const matchingItems = fzfInstance.find(searchValue).map((result) => result.item.original) - // Always include separators and shortcuts + // Always include separators, shortcuts, and labels return options.filter((option) => { - if (option.type === DropdownOptionType.SEPARATOR || option.type === DropdownOptionType.SHORTCUT) { + if ( + option.type === DropdownOptionType.SEPARATOR || + option.type === DropdownOptionType.SHORTCUT || + option.type === DropdownOptionType.LABEL // kilocode_change: include LABEL in filtered results + ) { return true } @@ -148,31 +155,61 @@ export const SelectDropdown = React.memo( }) }, [options, searchValue, fzfInstance, disableSearch]) - // Group options by type and handle separators + // Group options by type and handle separators and labels + // kilocode_change start: improved handling for section labels const groupedOptions = React.useMemo(() => { const result: DropdownOption[] = [] - let lastWasSeparator = false + let lastWasSeparatorOrLabel = false filteredOptions.forEach((option) => { if (option.type === DropdownOptionType.SEPARATOR) { // Only add separator if we have items before and after it - if (result.length > 0 && !lastWasSeparator) { + if (result.length > 0 && !lastWasSeparatorOrLabel) { result.push(option) - lastWasSeparator = true + lastWasSeparatorOrLabel = true } + } else if (option.type === DropdownOptionType.LABEL) { + // Track label position - we'll only keep it if it has items after it + result.push(option) + lastWasSeparatorOrLabel = true } else { result.push(option) - lastWasSeparator = false + lastWasSeparatorOrLabel = false } }) - // Remove trailing separator if present - if (result.length > 0 && result[result.length - 1].type === DropdownOptionType.SEPARATOR) { + // Remove trailing separator or label if present + while ( + result.length > 0 && + (result[result.length - 1].type === DropdownOptionType.SEPARATOR || + result[result.length - 1].type === DropdownOptionType.LABEL) + ) { result.pop() } - return result + // Also remove any labels that ended up with no items after them + // (can happen when filtering removes all items in a section) + const finalResult: DropdownOption[] = [] + for (let i = 0; i < result.length; i++) { + const option = result[i] + if (option.type === DropdownOptionType.LABEL) { + // Check if next item is also a label or separator (meaning this label has no items) + const nextItem = result[i + 1] + if ( + nextItem && + nextItem.type !== DropdownOptionType.LABEL && + nextItem.type !== DropdownOptionType.SEPARATOR + ) { + finalResult.push(option) + } + } else { + finalResult.push(option) + } + } + + return finalResult }, [filteredOptions]) + // kilocode_change end const handleSelect = React.useCallback( (optionValue: string) => { @@ -278,13 +315,26 @@ export const SelectDropdown = React.memo( ) } + // kilocode_change start: render LABEL type as section header + if (option.type === DropdownOptionType.LABEL) { + return ( +
+ {option.label} +
+ ) + } + // kilocode_change end + if ( option.type === DropdownOptionType.SHORTCUT || (option.disabled && shortcutText && option.label.includes(shortcutText)) ) { return (
{option.label}
diff --git a/webview-ui/src/i18n/locales/ar/settings.json b/webview-ui/src/i18n/locales/ar/settings.json index 4bb57148c69..878ab64c632 100644 --- a/webview-ui/src/i18n/locales/ar/settings.json +++ b/webview-ui/src/i18n/locales/ar/settings.json @@ -1134,7 +1134,9 @@ "searchPlaceholder": "بحث", "noMatchFound": "ما فيه تطابق", "useCustomModel": "استخدام مخصص: {{modelId}}", - "simplifiedExplanation": "يمكنك ضبط إعدادات النموذج التفصيلية لاحقًا." + "simplifiedExplanation": "يمكنك ضبط إعدادات النموذج التفصيلية لاحقًا.", + "recommendedModels": "النماذج الموصى بها", + "allModels": "جميع النماذج" }, "footer": { "feedback": "عندك سؤال أو ملاحظة؟ افتح تذكرة في github.com/Kilo-Org/kilocode أو انضم لـ r/kilocode أو kilo.ai/discord.", diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index c7a1f577afe..9e3484e4e76 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -1025,7 +1025,9 @@ "searchPlaceholder": "Cerca", "noMatchFound": "No s'ha trobat cap coincidència", "useCustomModel": "Utilitzar personalitzat: {{modelId}}", - "simplifiedExplanation": "Pots ajustar la configuració detallada del model més tard." + "simplifiedExplanation": "Pots ajustar la configuració detallada del model més tard.", + "recommendedModels": "Models recomanats", + "allModels": "Tots els models" }, "footer": { "feedback": "Si teniu qualsevol pregunta o comentari, no dubteu a obrir un issue a github.com/Kilo-Org/kilocode o unir-vos a reddit.com/r/kilocode o kilo.ai/discord", diff --git a/webview-ui/src/i18n/locales/cs/settings.json b/webview-ui/src/i18n/locales/cs/settings.json index b30ef443623..681690e1a15 100644 --- a/webview-ui/src/i18n/locales/cs/settings.json +++ b/webview-ui/src/i18n/locales/cs/settings.json @@ -1110,7 +1110,9 @@ "searchPlaceholder": "Hledat", "noMatchFound": "Nebyla nalezena žádná shoda", "useCustomModel": "Použít vlastní: {{modelId}}", - "simplifiedExplanation": "Podrobná nastavení modelu můžete upravit později." + "simplifiedExplanation": "Podrobná nastavení modelu můžete upravit později.", + "recommendedModels": "Doporučené modely", + "allModels": "Všechny modely" }, "footer": { "feedback": "Pokud máte nějaké dotazy nebo zpětnou vazbu, neváhejte otevřít problém na github.com/Kilo-Org/kilocode nebo se připojte k reddit.com/r/kilocode nebo kilo.ai/discord.", diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index 9cce9de263a..43a3a59ca9f 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -1021,7 +1021,9 @@ "searchPlaceholder": "Suchen", "noMatchFound": "Keine Übereinstimmung gefunden", "useCustomModel": "Benutzerdefiniert verwenden: {{modelId}}", - "simplifiedExplanation": "Du kannst detaillierte Modelleinstellungen später anpassen." + "simplifiedExplanation": "Du kannst detaillierte Modelleinstellungen später anpassen.", + "recommendedModels": "Empfohlene Modelle", + "allModels": "Alle Modelle" }, "footer": { "feedback": "Wenn du Fragen oder Feedback hast, kannst du gerne ein Issue auf github.com/Kilo-Org/kilocode öffnen oder reddit.com/r/kilocode oder kilo.ai/discord beitreten", diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 26e31178007..86922fa1b08 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -1058,7 +1058,9 @@ "searchPlaceholder": "Search", "noMatchFound": "No match found", "useCustomModel": "Use custom: {{modelId}}", - "simplifiedExplanation": "You can adjust detailed model settings later." + "simplifiedExplanation": "You can adjust detailed model settings later.", + "recommendedModels": "Recommended models", + "allModels": "All models" }, "footer": { "feedback": "If you have any questions or feedback, feel free to open an issue at github.com/Kilo-Org/kilocode or join reddit.com/r/kilocode or kilo.ai/discord.", diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index 96a2abd3c59..18d7766d3ef 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -1025,7 +1025,9 @@ "searchPlaceholder": "Buscar", "noMatchFound": "No se encontraron coincidencias", "useCustomModel": "Usar personalizado: {{modelId}}", - "simplifiedExplanation": "Puedes ajustar la configuración detallada del modelo más tarde." + "simplifiedExplanation": "Puedes ajustar la configuración detallada del modelo más tarde.", + "recommendedModels": "Modelos recomendados", + "allModels": "Todos los modelos" }, "footer": { "feedback": "Si tiene alguna pregunta o comentario, no dude en abrir un issue en github.com/Kilo-Org/kilocode o unirse a reddit.com/r/kilocode o kilo.ai/discord", diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index c713296abb9..9bafa973f6a 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -1025,7 +1025,9 @@ "searchPlaceholder": "Rechercher", "noMatchFound": "Aucune correspondance trouvée", "useCustomModel": "Utiliser personnalisé : {{modelId}}", - "simplifiedExplanation": "Tu peux ajuster les paramètres détaillés du modèle ultérieurement." + "simplifiedExplanation": "Tu peux ajuster les paramètres détaillés du modèle ultérieurement.", + "recommendedModels": "Modèles recommandés", + "allModels": "Tous les modèles" }, "footer": { "feedback": "Si vous avez des questions ou des commentaires, n'hésitez pas à ouvrir un problème sur github.com/Kilo-Org/kilocode ou à rejoindre reddit.com/r/kilocode ou kilo.ai/discord", diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index 5f170c81e49..e89acea7084 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -1026,7 +1026,9 @@ "searchPlaceholder": "खोजें", "noMatchFound": "कोई मिलान नहीं मिला", "useCustomModel": "कस्टम उपयोग करें: {{modelId}}", - "simplifiedExplanation": "आप बाद में विस्तृत मॉडल सेटिंग्स समायोजित कर सकते हैं।" + "simplifiedExplanation": "आप बाद में विस्तृत मॉडल सेटिंग्स समायोजित कर सकते हैं।", + "recommendedModels": "अनुशंसित मॉडल", + "allModels": "सभी मॉडल" }, "footer": { "feedback": "यदि आपके कोई प्रश्न या प्रतिक्रिया है, तो github.com/Kilo-Org/kilocode पर एक मुद्दा खोलने या reddit.com/r/kilocode या kilo.ai/discord में शामिल होने में संकोच न करें", diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index 1f2969b257f..faa10d688f2 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -1047,7 +1047,9 @@ "searchPlaceholder": "Cari", "noMatchFound": "Tidak ada yang cocok ditemukan", "useCustomModel": "Gunakan kustom: {{modelId}}", - "simplifiedExplanation": "Anda dapat menyesuaikan pengaturan model terperinci nanti." + "simplifiedExplanation": "Anda dapat menyesuaikan pengaturan model terperinci nanti.", + "recommendedModels": "Model yang direkomendasikan", + "allModels": "Semua model" }, "footer": { "feedback": "Jika kamu punya pertanyaan atau feedback, jangan ragu untuk membuka issue di github.com/Kilo-Org/kilocode atau bergabung reddit.com/r/kilocode atau kilo.ai/discord", diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index 70455bee4e5..f91935e7aab 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -1027,7 +1027,9 @@ "searchPlaceholder": "Cerca", "noMatchFound": "Nessuna corrispondenza trovata", "useCustomModel": "Usa personalizzato: {{modelId}}", - "simplifiedExplanation": "Puoi modificare le impostazioni dettagliate del modello in seguito." + "simplifiedExplanation": "Puoi modificare le impostazioni dettagliate del modello in seguito.", + "recommendedModels": "Modelli consigliati", + "allModels": "Tutti i modelli" }, "footer": { "feedback": "Se hai domande o feedback, sentiti libero di aprire un issue su github.com/Kilo-Org/kilocode o unirti a reddit.com/r/kilocode o kilo.ai/discord", diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index ccf1e2b664b..360d47cedb8 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -1027,7 +1027,9 @@ "searchPlaceholder": "検索", "noMatchFound": "一致するものが見つかりません", "useCustomModel": "カスタムを使用: {{modelId}}", - "simplifiedExplanation": "詳細なモデル設定は後で調整できます。" + "simplifiedExplanation": "詳細なモデル設定は後で調整できます。", + "recommendedModels": "おすすめのモデル", + "allModels": "すべてのモデル" }, "footer": { "feedback": "質問やフィードバックがある場合は、github.com/Kilo-Org/kilocodeで問題を開くか、reddit.com/r/kilocodekilo.ai/discordに参加してください", diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index 8f6079715ef..056852c59c0 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -1026,7 +1026,9 @@ "searchPlaceholder": "검색", "noMatchFound": "일치하는 항목 없음", "useCustomModel": "사용자 정의 사용: {{modelId}}", - "simplifiedExplanation": "나중에 자세한 모델 설정을 조정할 수 있습니다." + "simplifiedExplanation": "나중에 자세한 모델 설정을 조정할 수 있습니다.", + "recommendedModels": "추천 모델", + "allModels": "모든 모델" }, "footer": { "feedback": "질문이나 피드백이 있으시면 github.com/Kilo-Org/kilocode에서 이슈를 열거나 reddit.com/r/kilocode 또는 kilo.ai/discord에 가입하세요", diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index 6e9e5d29768..0f20c587ced 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -1026,7 +1026,9 @@ "searchPlaceholder": "Zoeken", "noMatchFound": "Geen overeenkomsten gevonden", "useCustomModel": "Aangepast gebruiken: {{modelId}}", - "simplifiedExplanation": "Je kunt later gedetailleerde modelinstellingen aanpassen." + "simplifiedExplanation": "Je kunt later gedetailleerde modelinstellingen aanpassen.", + "recommendedModels": "Aanbevolen modellen", + "allModels": "Alle modellen" }, "footer": { "feedback": "Heb je vragen of feedback? Open gerust een issue op github.com/Kilo-Org/kilocode of sluit je aan bij reddit.com/r/kilocode of kilo.ai/discord", diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index df1b606903e..175526d483f 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -1026,7 +1026,9 @@ "searchPlaceholder": "Wyszukaj", "noMatchFound": "Nie znaleziono dopasowań", "useCustomModel": "Użyj niestandardowy: {{modelId}}", - "simplifiedExplanation": "Można dostosować szczegółowe ustawienia modelu później." + "simplifiedExplanation": "Można dostosować szczegółowe ustawienia modelu później.", + "recommendedModels": "Polecane modele", + "allModels": "Wszystkie modele" }, "footer": { "feedback": "Jeśli masz jakiekolwiek pytania lub opinie, śmiało otwórz zgłoszenie na github.com/Kilo-Org/kilocode lub dołącz do reddit.com/r/kilocode lub kilo.ai/discord", diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index 05254deeebe..539640148a8 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -1026,7 +1026,9 @@ "searchPlaceholder": "Pesquisar", "noMatchFound": "Nenhuma correspondência encontrada", "useCustomModel": "Usar personalizado: {{modelId}}", - "simplifiedExplanation": "Você pode ajustar as configurações detalhadas do modelo mais tarde." + "simplifiedExplanation": "Você pode ajustar as configurações detalhadas do modelo mais tarde.", + "recommendedModels": "Modelos recomendados", + "allModels": "Todos os modelos" }, "footer": { "feedback": "Se tiver alguma dúvida ou feedback, sinta-se à vontade para abrir um problema em github.com/Kilo-Org/kilocode ou juntar-se a reddit.com/r/kilocode ou kilo.ai/discord", diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index 80f8af0d31f..541dbb889aa 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -1026,7 +1026,9 @@ "searchPlaceholder": "Поиск", "noMatchFound": "Совпадений не найдено", "useCustomModel": "Использовать пользовательскую: {{modelId}}", - "simplifiedExplanation": "Ты сможешь настроить подробные параметры модели позже." + "simplifiedExplanation": "Ты сможешь настроить подробные параметры модели позже.", + "recommendedModels": "Рекомендуемые модели", + "allModels": "Все модели" }, "footer": { "feedback": "Если у вас есть вопросы или предложения, откройте issue на github.com/Kilo-Org/kilocode или присоединяйтесь к reddit.com/r/kilocode или kilo.ai/discord", diff --git a/webview-ui/src/i18n/locales/th/settings.json b/webview-ui/src/i18n/locales/th/settings.json index 6920f3b78cf..8d57cf6a49e 100644 --- a/webview-ui/src/i18n/locales/th/settings.json +++ b/webview-ui/src/i18n/locales/th/settings.json @@ -1121,7 +1121,9 @@ "searchPlaceholder": "ค้นหา", "noMatchFound": "ไม่พบรายการที่ตรงกัน", "useCustomModel": "ใช้แบบกำหนดเอง: {{modelId}}", - "simplifiedExplanation": "คุณสามารถปรับการตั้งค่าโมเดลโดยละเอียดได้ภายหลัง" + "simplifiedExplanation": "คุณสามารถปรับการตั้งค่าโมเดลโดยละเอียดได้ภายหลัง", + "recommendedModels": "โมเดลที่แนะนำ", + "allModels": "โมเดลทั้งหมด" }, "footer": { "feedback": "หากคุณมีคำถามหรือข้อเสนอแนะ โปรดเปิด issue ที่ github.com/Kilo-Org/kilocode หรือเข้าร่วม reddit.com/r/kilocode หรือ kilo.ai/discord", diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index 03253251547..5fac19b4a80 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -1027,7 +1027,9 @@ "searchPlaceholder": "Ara", "noMatchFound": "Eşleşme bulunamadı", "useCustomModel": "Özel kullan: {{modelId}}", - "simplifiedExplanation": "Ayrıntılı model ayarlarını daha sonra ayarlayabilirsiniz." + "simplifiedExplanation": "Ayrıntılı model ayarlarını daha sonra ayarlayabilirsiniz.", + "recommendedModels": "Önerilen modeller", + "allModels": "Tüm modeller" }, "footer": { "feedback": "Herhangi bir sorunuz veya geri bildiriminiz varsa, github.com/Kilo-Org/kilocode adresinde bir konu açmaktan veya reddit.com/r/kilocode ya da kilo.ai/discord'a katılmaktan çekinmeyin", diff --git a/webview-ui/src/i18n/locales/uk/settings.json b/webview-ui/src/i18n/locales/uk/settings.json index a64c5be61ca..83e9a866f24 100644 --- a/webview-ui/src/i18n/locales/uk/settings.json +++ b/webview-ui/src/i18n/locales/uk/settings.json @@ -1135,7 +1135,9 @@ "searchPlaceholder": "Пошук", "noMatchFound": "Збігів не знайдено", "useCustomModel": "Використовувати власну: {{modelId}}", - "simplifiedExplanation": "Ви можете налаштувати детальні параметри моделі пізніше." + "simplifiedExplanation": "Ви можете налаштувати детальні параметри моделі пізніше.", + "recommendedModels": "Рекомендовані моделі", + "allModels": "Усі моделі" }, "footer": { "feedback": "Якщо у тебе є питання або відгуки, не соромся відкрити issue на github.com/Kilo-Org/kilocode або приєднатися до reddit.com/r/kilocode або kilo.ai/discord.", diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index b7a24d909a8..24f762a714f 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -1026,7 +1026,9 @@ "searchPlaceholder": "Tìm kiếm", "noMatchFound": "Không tìm thấy kết quả", "useCustomModel": "Sử dụng tùy chỉnh: {{modelId}}", - "simplifiedExplanation": "Bạn có thể điều chỉnh cài đặt mô hình chi tiết sau." + "simplifiedExplanation": "Bạn có thể điều chỉnh cài đặt mô hình chi tiết sau.", + "recommendedModels": "Mô hình được đề xuất", + "allModels": "Tất cả mô hình" }, "footer": { "feedback": "Nếu bạn có bất kỳ câu hỏi hoặc phản hồi nào, vui lòng mở một vấn đề tại github.com/Kilo-Org/kilocode hoặc tham gia reddit.com/r/kilocode hoặc kilo.ai/discord", diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index d4db3a3c159..919383096e9 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -1030,7 +1030,9 @@ "searchPlaceholder": "搜索", "noMatchFound": "未找到匹配项", "useCustomModel": "使用自定义:{{modelId}}", - "simplifiedExplanation": "你可以稍后调整详细的模型设置。" + "simplifiedExplanation": "你可以稍后调整详细的模型设置。", + "recommendedModels": "推荐模型", + "allModels": "全部模型" }, "footer": { "feedback": "如果您有任何问题或反馈,请随时在 github.com/Kilo-Org/kilocode 上提出问题或加入 reddit.com/r/kilocodekilo.ai/discord", diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index 3303f9bc025..25a74df1127 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -1027,7 +1027,9 @@ "searchPlaceholder": "搜尋", "noMatchFound": "找不到符合的項目", "useCustomModel": "使用自訂模型:{{modelId}}", - "simplifiedExplanation": "你可以稍後調整詳細的模型設定。" + "simplifiedExplanation": "你可以稍後調整詳細的模型設定。", + "recommendedModels": "推薦模型", + "allModels": "所有模型" }, "footer": { "feedback": "若您有任何問題或建議,歡迎至 github.com/Kilo-Org/kilocode 提出 issue,或加入 reddit.com/r/kilocodekilo.ai/discord 討論。",