diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index a56a00fc355a..2ddebb1c089e 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -44,8 +44,11 @@ export const globalSettingsSchema = z.object({ dismissedUpsells: z.array(z.string()).optional(), // Image generation settings (experimental) - flattened for simplicity + imageGenerationProvider: z.enum(["openrouter", "gemini"]).optional(), openRouterImageApiKey: z.string().optional(), openRouterImageGenerationSelectedModel: z.string().optional(), + geminiImageApiKey: z.string().optional(), + geminiImageGenerationSelectedModel: z.string().optional(), condensingApiConfigId: z.string().optional(), customCondensingPrompt: z.string().optional(), @@ -210,7 +213,8 @@ export const SECRET_STATE_KEYS = [ // Global secrets that are part of GlobalSettings (not ProviderSettings) export const GLOBAL_SECRET_KEYS = [ - "openRouterImageApiKey", // For image generation + "openRouterImageApiKey", // For OpenRouter image generation + "geminiImageApiKey", // For Gemini image generation ] as const // Type for the actual secret storage keys diff --git a/packages/types/src/image-generation.ts b/packages/types/src/image-generation.ts new file mode 100644 index 000000000000..4d14fedc0e6f --- /dev/null +++ b/packages/types/src/image-generation.ts @@ -0,0 +1,75 @@ +import { z } from "zod" + +/** + * Image Generation Provider + */ +export const imageGenerationProviders = ["openrouter", "gemini"] as const +export const imageGenerationProviderSchema = z.enum(imageGenerationProviders) +export type ImageGenerationProvider = z.infer + +/** + * Image Generation Model Info + */ +export interface ImageGenerationModelInfo { + provider: ImageGenerationProvider + modelId: string + label: string + supportsEditMode?: boolean // Whether the model supports image editing (text + image input) + maxInputSize?: number // Maximum input image size in MB + outputFormats?: string[] // Supported output formats +} + +/** + * Image Generation Models by Provider + */ +export const IMAGE_GENERATION_MODELS: Record = { + openrouter: [ + { + provider: "openrouter", + modelId: "google/gemini-2.5-flash-image-preview", + label: "Gemini 2.5 Flash Image Preview", + supportsEditMode: true, + outputFormats: ["png", "jpeg"], + }, + { + provider: "openrouter", + modelId: "google/gemini-2.5-flash-image-preview:free", + label: "Gemini 2.5 Flash Image Preview (Free)", + supportsEditMode: true, + outputFormats: ["png", "jpeg"], + }, + ], + gemini: [ + { + provider: "gemini", + modelId: "gemini-2.5-flash-image-preview", + label: "Gemini 2.5 Flash Image Preview", + supportsEditMode: true, + outputFormats: ["png", "jpeg"], + }, + ], +} + +/** + * Helper function to get all models for a specific provider + */ +export function getImageGenerationModelsForProvider(provider: ImageGenerationProvider): ImageGenerationModelInfo[] { + return IMAGE_GENERATION_MODELS[provider] || [] +} + +/** + * Helper function to get all available image generation models + */ +export function getAllImageGenerationModels(): ImageGenerationModelInfo[] { + return Object.values(IMAGE_GENERATION_MODELS).flat() +} + +/** + * Image Generation Result + */ +export interface ImageGenerationResult { + success: boolean + imageData?: string // Base64 encoded image data URL + imageFormat?: string // Format of the generated image (png, jpeg, etc.) + error?: string +} diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 7a7d5059eb0c..198534d438ab 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -7,6 +7,7 @@ export * from "./experiment.js" export * from "./followup.js" export * from "./global-settings.js" export * from "./history.js" +export * from "./image-generation.js" export * from "./ipc.js" export * from "./marketplace.js" export * from "./mcp.js" diff --git a/src/api/providers/gemini.ts b/src/api/providers/gemini.ts index 573adda879ec..d92a7f4fd660 100644 --- a/src/api/providers/gemini.ts +++ b/src/api/providers/gemini.ts @@ -5,10 +5,17 @@ import { type GenerateContentParameters, type GenerateContentConfig, type GroundingMetadata, + type Content, } from "@google/genai" import type { JWTInput } from "google-auth-library" -import { type ModelInfo, type GeminiModelId, geminiDefaultModelId, geminiModels } from "@roo-code/types" +import { + type ModelInfo, + type GeminiModelId, + geminiDefaultModelId, + geminiModels, + type ImageGenerationResult, +} from "@roo-code/types" import type { ApiHandlerOptions } from "../../shared/api" import { safeJsonParse } from "../../shared/safeJsonParse" @@ -335,4 +342,139 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl return totalCost } + + /** + * Generate an image using Gemini's image generation API + * @param prompt The text prompt for image generation + * @param model The model to use for generation + * @param apiKey The Gemini API key (if not using vertex) + * @param inputImage Optional base64 encoded input image data URL for editing + * @returns The generated image data and format, or an error + */ + async generateImage( + prompt: string, + model: string, + apiKey?: string, + inputImage?: string, + ): Promise { + try { + // Create a temporary client with the provided API key if needed + let client: GoogleGenAI + if (apiKey && !this.options.vertexProjectId) { + // Use provided API key for standard Gemini + client = new GoogleGenAI({ apiKey }) + } else { + // Use existing client (either vertex or standard with already configured key) + client = this.client + } + + // Prepare the content for generation + const contents: Content[] = [] + + if (inputImage) { + // For image editing mode, include both text and image + const base64Match = inputImage.match(/^data:image\/(png|jpeg|jpg);base64,(.+)$/) + if (!base64Match) { + return { + success: false, + error: "Invalid input image format. Expected base64 data URL.", + } + } + + const mimeType = base64Match[1] === "jpg" ? "image/jpeg" : `image/${base64Match[1]}` + const base64Data = base64Match[2] + + contents.push({ + role: "user", + parts: [ + { text: prompt }, + { + inlineData: { + mimeType, + data: base64Data, + }, + }, + ], + }) + } else { + // For text-to-image mode + contents.push({ + role: "user", + parts: [{ text: prompt }], + }) + } + + const config: GenerateContentConfig = { + httpOptions: this.options.googleGeminiBaseUrl + ? { baseUrl: this.options.googleGeminiBaseUrl } + : undefined, + temperature: 1.0, // Higher temperature for more creative image generation + } + + const params: GenerateContentParameters = { + model, + contents, + config, + } + + const result = await client.models.generateContent(params) + + // Extract the generated image from the response + if (!result.candidates || result.candidates.length === 0) { + return { + success: false, + error: "No candidates returned in the response", + } + } + + const candidate = result.candidates[0] + if (!candidate.content || !candidate.content.parts) { + return { + success: false, + error: "No content parts in the response", + } + } + + // Find the image part in the response + let imageData: string | undefined + let imageFormat = "png" // Default format + + for (const part of candidate.content.parts) { + if (part.inlineData) { + const mimeType = part.inlineData.mimeType + const data = part.inlineData.data + + if (mimeType?.startsWith("image/")) { + // Extract format from mime type + imageFormat = mimeType.replace("image/", "").replace("jpeg", "jpg") + + // Convert to data URL format + imageData = `data:${mimeType};base64,${data}` + break + } + } + } + + if (!imageData) { + return { + success: false, + error: "No image data found in the response", + } + } + + return { + success: true, + imageData, + imageFormat, + } + } catch (error) { + const errorMessage = error instanceof Error ? error.message : "Unknown error occurred" + console.error("Gemini image generation error:", errorMessage) + + return { + success: false, + error: `Failed to generate image: ${errorMessage}`, + } + } + } } diff --git a/src/core/tools/generateImageTool.ts b/src/core/tools/generateImageTool.ts index 88a02ac82125..4a9541a76299 100644 --- a/src/core/tools/generateImageTool.ts +++ b/src/core/tools/generateImageTool.ts @@ -9,9 +9,8 @@ import { getReadablePath } from "../../utils/path" import { isPathOutsideWorkspace } from "../../utils/pathUtils" import { EXPERIMENT_IDS, experiments } from "../../shared/experiments" import { OpenRouterHandler } from "../../api/providers/openrouter" - -// Hardcoded list of image generation models for now -const IMAGE_GENERATION_MODELS = ["google/gemini-2.5-flash-image", "openai/gpt-5-image", "openai/gpt-5-image-mini"] +import { GeminiHandler } from "../../api/providers/gemini" +import { ImageGenerationProvider, getImageGenerationModelsForProvider } from "@roo-code/types" export async function generateImageTool( cline: Task, @@ -128,25 +127,60 @@ export async function generateImageTool( // Check if file is write-protected const isWriteProtected = cline.rooProtectedController?.isWriteProtected(relPath) || false - // Get OpenRouter API key from global settings (experimental image generation) - const openRouterApiKey = state?.openRouterImageApiKey + // Get the selected provider from settings (default to openrouter) + const selectedProvider = (state?.imageGenerationProvider || "openrouter") as ImageGenerationProvider - if (!openRouterApiKey) { - await cline.say( - "error", - "OpenRouter API key is required for image generation. Please configure it in the Image Generation experimental settings.", - ) - pushToolResult( - formatResponse.toolError( + // Get selected model from settings based on provider + let selectedModel: string + let apiKey: string | undefined + + if (selectedProvider === "openrouter") { + apiKey = state?.openRouterImageApiKey + if (!apiKey) { + await cline.say( + "error", "OpenRouter API key is required for image generation. Please configure it in the Image Generation experimental settings.", - ), - ) + ) + pushToolResult( + formatResponse.toolError( + "OpenRouter API key is required for image generation. Please configure it in the Image Generation experimental settings.", + ), + ) + return + } + // Get selected model or use default for OpenRouter + const models = getImageGenerationModelsForProvider("openrouter") + selectedModel = + state?.openRouterImageGenerationSelectedModel || + (models[0]?.modelId ?? "google/gemini-2.5-flash-image-preview") + } else if (selectedProvider === "gemini") { + // For Gemini, we can use the existing Gemini API key from the provider settings + // Check for a dedicated image generation API key first, then fall back to the provider's API key + apiKey = + state?.geminiImageApiKey || + (state?.apiConfiguration?.apiProvider === "gemini" ? state?.apiConfiguration?.geminiApiKey : undefined) + if (!apiKey) { + await cline.say( + "error", + "Gemini API key is required for image generation. Please configure it in the Image Generation experimental settings or in the Gemini provider settings.", + ) + pushToolResult( + formatResponse.toolError( + "Gemini API key is required for image generation. Please configure it in the Image Generation experimental settings or in the Gemini provider settings.", + ), + ) + return + } + // Get selected model or use default for Gemini + const models = getImageGenerationModelsForProvider("gemini") + selectedModel = + state?.geminiImageGenerationSelectedModel || (models[0]?.modelId ?? "gemini-2.5-flash-image-preview") + } else { + await cline.say("error", `Unsupported image generation provider: ${selectedProvider}`) + pushToolResult(formatResponse.toolError(`Unsupported image generation provider: ${selectedProvider}`)) return } - // Get selected model from settings or use default - const selectedModel = state?.openRouterImageGenerationSelectedModel || IMAGE_GENERATION_MODELS[0] - // Determine if the path is outside the workspace const fullPath = path.resolve(cline.cwd, removeClosingTag("path", relPath)) const isOutsideWorkspace = isPathOutsideWorkspace(fullPath) @@ -176,16 +210,28 @@ export async function generateImageTool( return } - // Create a temporary OpenRouter handler with minimal options - const openRouterHandler = new OpenRouterHandler({} as any) - - // Call the generateImage method with the explicit API key and optional input image - const result = await openRouterHandler.generateImage( - prompt, - selectedModel, - openRouterApiKey, - inputImageData, - ) + // Generate image based on provider + let result + + if (selectedProvider === "openrouter") { + // Create a temporary OpenRouter handler with minimal options + const openRouterHandler = new OpenRouterHandler({} as any) + + // Call the generateImage method with the explicit API key and optional input image + result = await openRouterHandler.generateImage(prompt, selectedModel, apiKey!, inputImageData) + } else if (selectedProvider === "gemini") { + // Create a temporary Gemini handler with minimal options + const geminiHandler = new GeminiHandler({ geminiApiKey: apiKey } as any) + + // Call the generateImage method with the optional input image + result = await geminiHandler.generateImage(prompt, selectedModel, apiKey, inputImageData) + } else { + // This should not happen due to earlier check, but for type safety + result = { + success: false, + error: `Unsupported provider: ${selectedProvider}`, + } + } if (!result.success) { await cline.say("error", result.error || "Failed to generate image") diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 91b868796689..c70af1a7ed64 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1811,8 +1811,11 @@ export class ClineProvider includeTaskHistoryInEnhance, taskSyncEnabled, remoteControlEnabled, + imageGenerationProvider, openRouterImageApiKey, openRouterImageGenerationSelectedModel, + geminiImageApiKey, + geminiImageGenerationSelectedModel, openRouterUseMiddleOutTransform, featureRoomoteControlEnabled, } = await this.getState() @@ -1960,8 +1963,11 @@ export class ClineProvider includeTaskHistoryInEnhance: includeTaskHistoryInEnhance ?? true, taskSyncEnabled, remoteControlEnabled, + imageGenerationProvider, openRouterImageApiKey, openRouterImageGenerationSelectedModel, + geminiImageApiKey, + geminiImageGenerationSelectedModel, openRouterUseMiddleOutTransform, featureRoomoteControlEnabled, } @@ -2181,8 +2187,11 @@ export class ClineProvider return false } })(), + imageGenerationProvider: stateValues.imageGenerationProvider, openRouterImageApiKey: stateValues.openRouterImageApiKey, openRouterImageGenerationSelectedModel: stateValues.openRouterImageGenerationSelectedModel, + geminiImageApiKey: stateValues.geminiImageApiKey, + geminiImageGenerationSelectedModel: stateValues.geminiImageGenerationSelectedModel, featureRoomoteControlEnabled: (() => { try { const userSettings = CloudService.instance.getUserSettings() diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index af5f9925c353..fdc1b790fa82 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -1578,6 +1578,21 @@ export const webviewMessageHandler = async ( await provider.contextProxy.setValue("openRouterImageGenerationSelectedModel", message.text) await provider.postStateToWebview() break + case "imageGenerationProvider": + await provider.contextProxy.setValue( + "imageGenerationProvider", + message.text as "openrouter" | "gemini" | undefined, + ) + await provider.postStateToWebview() + break + case "geminiImageApiKey": + await provider.contextProxy.setValue("geminiImageApiKey", message.text) + await provider.postStateToWebview() + break + case "geminiImageGenerationSelectedModel": + await provider.contextProxy.setValue("geminiImageGenerationSelectedModel", message.text) + await provider.postStateToWebview() + break case "showRooIgnoredFiles": await updateGlobalState("showRooIgnoredFiles", message.bool ?? false) await provider.postStateToWebview() diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 66f389f81c10..1ad4603d3007 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -343,6 +343,9 @@ export type ExtensionState = Pick< profileThresholds: Record hasOpenedModeSelector: boolean openRouterImageApiKey?: string + imageGenerationProvider?: "openrouter" | "gemini" + geminiImageApiKey?: string + geminiImageGenerationSelectedModel?: string openRouterUseMiddleOutTransform?: boolean messageQueue?: QueuedMessage[] lastShownAnnouncementId?: string diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index d43a2fce0434..8044cb84d7c1 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -224,6 +224,9 @@ export interface WebviewMessage { | "imageGenerationSettings" | "openRouterImageApiKey" | "openRouterImageGenerationSelectedModel" + | "imageGenerationProvider" + | "geminiImageApiKey" + | "geminiImageGenerationSelectedModel" | "queueMessage" | "removeQueuedMessage" | "editQueuedMessage" diff --git a/webview-ui/src/components/settings/ExperimentalSettings.tsx b/webview-ui/src/components/settings/ExperimentalSettings.tsx index 6883975d02e5..8e55aac344b4 100644 --- a/webview-ui/src/components/settings/ExperimentalSettings.tsx +++ b/webview-ui/src/components/settings/ExperimentalSettings.tsx @@ -21,8 +21,13 @@ type ExperimentalSettingsProps = HTMLAttributes & { setApiConfigurationField?: any openRouterImageApiKey?: string openRouterImageGenerationSelectedModel?: string + imageGenerationProvider?: string + geminiImageApiKey?: string + geminiImageGenerationSelectedModel?: string setOpenRouterImageApiKey?: (apiKey: string) => void setImageGenerationSelectedModel?: (model: string) => void + setImageGenerationProvider?: (provider: "gemini" | "openrouter" | undefined) => void + setGeminiImageApiKey?: (apiKey: string) => void } export const ExperimentalSettings = ({ @@ -32,8 +37,13 @@ export const ExperimentalSettings = ({ setApiConfigurationField, openRouterImageApiKey, openRouterImageGenerationSelectedModel, + imageGenerationProvider, + geminiImageApiKey, + geminiImageGenerationSelectedModel, setOpenRouterImageApiKey, setImageGenerationSelectedModel, + setImageGenerationProvider, + setGeminiImageApiKey, className, ...props }: ExperimentalSettingsProps) => { @@ -78,8 +88,13 @@ export const ExperimentalSettings = ({ } openRouterImageApiKey={openRouterImageApiKey} openRouterImageGenerationSelectedModel={openRouterImageGenerationSelectedModel} + imageGenerationProvider={imageGenerationProvider as any} + geminiApiKey={geminiImageApiKey} + geminiImageGenerationSelectedModel={geminiImageGenerationSelectedModel} setOpenRouterImageApiKey={setOpenRouterImageApiKey} setImageGenerationSelectedModel={setImageGenerationSelectedModel} + setImageGenerationProvider={setImageGenerationProvider} + setGeminiApiKey={setGeminiImageApiKey} /> ) } diff --git a/webview-ui/src/components/settings/ImageGenerationSettings.tsx b/webview-ui/src/components/settings/ImageGenerationSettings.tsx index 2f0f21f74d5c..54aeeacc6b17 100644 --- a/webview-ui/src/components/settings/ImageGenerationSettings.tsx +++ b/webview-ui/src/components/settings/ImageGenerationSettings.tsx @@ -1,57 +1,118 @@ import React, { useState, useEffect } from "react" import { VSCodeCheckbox, VSCodeTextField, VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react" import { useAppTranslation } from "@/i18n/TranslationContext" +import { getImageGenerationModelsForProvider, type ImageGenerationProvider } from "@roo-code/types" interface ImageGenerationSettingsProps { enabled: boolean onChange: (enabled: boolean) => void openRouterImageApiKey?: string + geminiApiKey?: string openRouterImageGenerationSelectedModel?: string + geminiImageGenerationSelectedModel?: string + imageGenerationProvider?: ImageGenerationProvider setOpenRouterImageApiKey: (apiKey: string) => void + setGeminiApiKey?: (apiKey: string) => void setImageGenerationSelectedModel: (model: string) => void + setImageGenerationProvider?: (provider: ImageGenerationProvider | undefined) => void } -// Hardcoded list of image generation models -const IMAGE_GENERATION_MODELS = [ - { value: "google/gemini-2.5-flash-image", label: "Gemini 2.5 Flash Image" }, - { value: "openai/gpt-5-image", label: "GPT-5 Image" }, - { value: "openai/gpt-5-image-mini", label: "GPT-5 Image Mini" }, - // Add more models as they become available -] - export const ImageGenerationSettings = ({ enabled, onChange, openRouterImageApiKey, + geminiApiKey, openRouterImageGenerationSelectedModel, + geminiImageGenerationSelectedModel, + imageGenerationProvider = "openrouter", setOpenRouterImageApiKey, + setGeminiApiKey, setImageGenerationSelectedModel, + setImageGenerationProvider, }: ImageGenerationSettingsProps) => { const { t } = useAppTranslation() - const [apiKey, setApiKey] = useState(openRouterImageApiKey || "") - const [selectedModel, setSelectedModel] = useState( - openRouterImageGenerationSelectedModel || IMAGE_GENERATION_MODELS[0].value, - ) + const [provider, setProvider] = useState(imageGenerationProvider) + const [openRouterApiKeyLocal, setOpenRouterApiKeyLocal] = useState(openRouterImageApiKey || "") + const [geminiApiKeyLocal, setGeminiApiKeyLocal] = useState(geminiApiKey || "") + + // Get models for current provider + const availableModels = getImageGenerationModelsForProvider(provider) + + // Get default model for provider + const getDefaultModel = (provider: ImageGenerationProvider) => { + const models = getImageGenerationModelsForProvider(provider) + return models.length > 0 ? models[0].modelId : "" + } + + // Get current selected model based on provider + const getCurrentSelectedModel = () => { + if (provider === "openrouter") { + return openRouterImageGenerationSelectedModel || getDefaultModel("openrouter") + } else { + return geminiImageGenerationSelectedModel || getDefaultModel("gemini") + } + } + + const [selectedModel, setSelectedModel] = useState(getCurrentSelectedModel()) // Update local state when props change (e.g., when switching profiles) useEffect(() => { - setApiKey(openRouterImageApiKey || "") - setSelectedModel(openRouterImageGenerationSelectedModel || IMAGE_GENERATION_MODELS[0].value) - }, [openRouterImageApiKey, openRouterImageGenerationSelectedModel]) + setOpenRouterApiKeyLocal(openRouterImageApiKey || "") + setGeminiApiKeyLocal(geminiApiKey || "") + const newProvider = imageGenerationProvider || "openrouter" + setProvider(newProvider) + // Calculate selected model based on the new provider value + const newSelectedModel = + newProvider === "openrouter" + ? openRouterImageGenerationSelectedModel || getDefaultModel("openrouter") + : geminiImageGenerationSelectedModel || getDefaultModel("gemini") + setSelectedModel(newSelectedModel) + }, [ + openRouterImageApiKey, + geminiApiKey, + openRouterImageGenerationSelectedModel, + geminiImageGenerationSelectedModel, + imageGenerationProvider, + ]) + + // Handle provider change + const handleProviderChange = (value: ImageGenerationProvider) => { + setProvider(value) + if (setImageGenerationProvider) { + setImageGenerationProvider(value as ImageGenerationProvider | undefined) + } + + // Set default model for new provider + const defaultModel = getDefaultModel(value) + setSelectedModel(defaultModel) + setImageGenerationSelectedModel(defaultModel) + } // Handle API key changes - const handleApiKeyChange = (value: string) => { - setApiKey(value) + const handleOpenRouterApiKeyChange = (value: string) => { + setOpenRouterApiKeyLocal(value) setOpenRouterImageApiKey(value) } + const handleGeminiApiKeyChange = (value: string) => { + setGeminiApiKeyLocal(value) + if (setGeminiApiKey) { + setGeminiApiKey(value) + } + } + // Handle model selection changes const handleModelChange = (value: string) => { setSelectedModel(value) setImageGenerationSelectedModel(value) } + // Get current API key based on provider + const getCurrentApiKey = () => { + return provider === "openrouter" ? openRouterApiKeyLocal : geminiApiKeyLocal + } + return (
@@ -67,30 +128,78 @@ export const ImageGenerationSettings = ({ {enabled && (
- {/* API Key Configuration */} + {/* Provider Selection */}
- handleApiKeyChange(e.target.value)} - placeholder={t("settings:experimental.IMAGE_GENERATION.openRouterApiKeyPlaceholder")} - className="w-full" - type="password" - /> + handleProviderChange(e.target.value as ImageGenerationProvider)} + className="w-full"> + + OpenRouter + + + Gemini + +

- {t("settings:experimental.IMAGE_GENERATION.getApiKeyText")}{" "} - - openrouter.ai/keys - + {t("settings:experimental.IMAGE_GENERATION.providerDescription")}

+ {/* API Key Configuration based on provider */} + {provider === "openrouter" && ( +
+ + handleOpenRouterApiKeyChange(e.target.value)} + placeholder={t("settings:experimental.IMAGE_GENERATION.openRouterApiKeyPlaceholder")} + className="w-full" + type="password" + /> +

+ {t("settings:experimental.IMAGE_GENERATION.getApiKeyText")}{" "} + + openrouter.ai/keys + +

+
+ )} + + {provider === "gemini" && ( +
+ + handleGeminiApiKeyChange(e.target.value)} + placeholder={t("settings:experimental.IMAGE_GENERATION.geminiApiKeyPlaceholder")} + className="w-full" + type="password" + /> +

+ {t("settings:experimental.IMAGE_GENERATION.geminiGetApiKeyText")}{" "} + + Google AI Studio + +

+
+ )} + {/* Model Selection */}
{/* Status Message */} - {enabled && !apiKey && ( + {enabled && !getCurrentApiKey() && (
- {t("settings:experimental.IMAGE_GENERATION.warningMissingKey")} + {provider === "openrouter" + ? t("settings:experimental.IMAGE_GENERATION.warningMissingKey") + : t("settings:experimental.IMAGE_GENERATION.warningMissingGeminiKey")}
)} - {enabled && apiKey && ( + {enabled && getCurrentApiKey() && (
{t("settings:experimental.IMAGE_GENERATION.successConfigured")}
diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 93b1b39e506f..6bbea103e4c6 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -195,6 +195,9 @@ const SettingsView = forwardRef(({ onDone, t openRouterImageApiKey, openRouterImageGenerationSelectedModel, reasoningBlockCollapsed, + imageGenerationProvider, + geminiImageApiKey, + geminiImageGenerationSelectedModel, } = cachedState const apiConfiguration = useMemo(() => cachedState.apiConfiguration ?? {}, [cachedState.apiConfiguration]) @@ -292,11 +295,28 @@ const SettingsView = forwardRef(({ onDone, t const setImageGenerationSelectedModel = useCallback((model: string) => { setCachedState((prevState) => { - // Only set change detected if value actually changed - if (prevState.openRouterImageGenerationSelectedModel !== model) { - setChangeDetected(true) + setChangeDetected(true) + // Update the appropriate model field based on the current provider + const provider = prevState.imageGenerationProvider || "openrouter" + if (provider === "openrouter") { + return { ...prevState, openRouterImageGenerationSelectedModel: model } + } else { + return { ...prevState, geminiImageGenerationSelectedModel: model } } - return { ...prevState, openRouterImageGenerationSelectedModel: model } + }) + }, []) + + const setImageGenerationProvider = useCallback((provider: "gemini" | "openrouter" | undefined) => { + setCachedState((prevState) => { + setChangeDetected(true) + return { ...prevState, imageGenerationProvider: provider } + }) + }, []) + + const setGeminiImageApiKey = useCallback((apiKey: string) => { + setCachedState((prevState) => { + setChangeDetected(true) + return { ...prevState, geminiImageApiKey: apiKey } }) }, []) @@ -392,6 +412,12 @@ const SettingsView = forwardRef(({ onDone, t type: "openRouterImageGenerationSelectedModel", text: openRouterImageGenerationSelectedModel, }) + vscode.postMessage({ type: "imageGenerationProvider", text: imageGenerationProvider }) + vscode.postMessage({ type: "geminiImageApiKey", text: geminiImageApiKey }) + vscode.postMessage({ + type: "geminiImageGenerationSelectedModel", + text: geminiImageGenerationSelectedModel, + }) setChangeDetected(false) } } @@ -797,8 +823,15 @@ const SettingsView = forwardRef(({ onDone, t openRouterImageGenerationSelectedModel={ openRouterImageGenerationSelectedModel as string | undefined } + imageGenerationProvider={imageGenerationProvider as string | undefined} + geminiImageApiKey={geminiImageApiKey as string | undefined} + geminiImageGenerationSelectedModel={ + geminiImageGenerationSelectedModel as string | undefined + } setOpenRouterImageApiKey={setOpenRouterImageApiKey} setImageGenerationSelectedModel={setImageGenerationSelectedModel} + setImageGenerationProvider={setImageGenerationProvider} + setGeminiImageApiKey={setGeminiImageApiKey} /> )} diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index 611159069b29..b7caa68ab70a 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -749,7 +749,13 @@ "modelSelectionLabel": "Model de generació d'imatges", "modelSelectionDescription": "Selecciona el model per a la generació d'imatges", "warningMissingKey": "⚠️ La clau API d'OpenRouter és necessària per a la generació d'imatges. Si us plau, configura-la a dalt.", - "successConfigured": "✓ La generació d'imatges està configurada i llesta per utilitzar" + "successConfigured": "✓ La generació d'imatges està configurada i llesta per utilitzar", + "providerLabel": "Proveïdor de generació d'imatges", + "providerDescription": "Tria el proveïdor per a la generació d'imatges", + "geminiApiKeyLabel": "Clau API de Gemini", + "geminiApiKeyPlaceholder": "Introdueix la teva clau API de Gemini", + "geminiGetApiKeyText": "Obté la teva clau API de", + "warningMissingGeminiKey": "⚠️ La clau API de Gemini és necessària per a la generació d'imatges. Si us plau, configura-la a dalt." }, "RUN_SLASH_COMMAND": { "name": "Habilitar comandes de barra diagonal iniciades pel model", diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index 00827751b0fd..c3a3fe5c1732 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -749,7 +749,13 @@ "modelSelectionLabel": "Bildgenerierungsmodell", "modelSelectionDescription": "Wähle das Modell für die Bildgenerierung aus", "warningMissingKey": "⚠️ OpenRouter API-Schlüssel ist für Bildgenerierung erforderlich. Bitte konfiguriere ihn oben.", - "successConfigured": "✓ Bildgenerierung ist konfiguriert und einsatzbereit" + "successConfigured": "✓ Bildgenerierung ist konfiguriert und einsatzbereit", + "providerLabel": "Bildgenerierungs-Anbieter", + "providerDescription": "Wähle den Anbieter für die Bildgenerierung", + "geminiApiKeyLabel": "Gemini API-Schlüssel", + "geminiApiKeyPlaceholder": "Gib deinen Gemini API-Schlüssel ein", + "geminiGetApiKeyText": "Hol dir deinen API-Schlüssel von", + "warningMissingGeminiKey": "⚠️ Gemini API-Schlüssel ist für Bildgenerierung erforderlich. Bitte konfiguriere ihn oben." }, "RUN_SLASH_COMMAND": { "name": "Modellinitierte Slash-Befehle aktivieren", diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index dfccc49cc4ce..a57f5054bcb5 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -754,7 +754,13 @@ "modelSelectionLabel": "Image Generation Model", "modelSelectionDescription": "Select the model to use for image generation", "warningMissingKey": "⚠️ OpenRouter API key is required for image generation. Please configure it above.", - "successConfigured": "✓ Image generation is configured and ready to use" + "successConfigured": "✓ Image generation is configured and ready to use", + "providerLabel": "Image Generation Provider", + "providerDescription": "Choose the provider for image generation", + "geminiApiKeyLabel": "Gemini API Key", + "geminiApiKeyPlaceholder": "Enter your Gemini API key", + "geminiGetApiKeyText": "Get your API key from", + "warningMissingGeminiKey": "⚠️ Gemini API key is required for image generation. Please configure it above." }, "RUN_SLASH_COMMAND": { "name": "Enable model-initiated slash commands", diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index c1271df82740..45bd3c6fc1a7 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -749,7 +749,13 @@ "modelSelectionLabel": "Modelo de generación de imágenes", "modelSelectionDescription": "Selecciona el modelo para la generación de imágenes", "warningMissingKey": "⚠️ La clave API de OpenRouter es requerida para la generación de imágenes. Por favor, configúrala arriba.", - "successConfigured": "✓ La generación de imágenes está configurada y lista para usar" + "successConfigured": "✓ La generación de imágenes está configurada y lista para usar", + "providerLabel": "Proveedor de generación de imágenes", + "providerDescription": "Elige el proveedor para la generación de imágenes", + "geminiApiKeyLabel": "Clave API de Gemini", + "geminiApiKeyPlaceholder": "Introduce tu clave API de Gemini", + "geminiGetApiKeyText": "Obtén tu clave API de", + "warningMissingGeminiKey": "⚠️ La clave API de Gemini es requerida para la generación de imágenes. Por favor, configúrala arriba." }, "RUN_SLASH_COMMAND": { "name": "Habilitar comandos slash iniciados por el modelo", diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index abcf401d6402..330c81cdd694 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -749,7 +749,13 @@ "modelSelectionLabel": "Modèle de génération d'images", "modelSelectionDescription": "Sélectionnez le modèle pour la génération d'images", "warningMissingKey": "⚠️ Une clé API OpenRouter est requise pour la génération d'images. Veuillez la configurer ci-dessus.", - "successConfigured": "✓ La génération d'images est configurée et prête à utiliser" + "successConfigured": "✓ La génération d'images est configurée et prête à utiliser", + "providerLabel": "Fournisseur de génération d'images", + "providerDescription": "Choisissez le fournisseur pour la génération d'images", + "geminiApiKeyLabel": "Clé API Gemini", + "geminiApiKeyPlaceholder": "Entrez votre clé API Gemini", + "geminiGetApiKeyText": "Obtenez votre clé API depuis", + "warningMissingGeminiKey": "⚠️ Une clé API Gemini est requise pour la génération d'images. Veuillez la configurer ci-dessus." }, "RUN_SLASH_COMMAND": { "name": "Activer les commandes slash initiées par le modèle", diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index 975e35411eea..ac4ef6e8c32b 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -750,7 +750,13 @@ "modelSelectionLabel": "छवि निर्माण मॉडल", "modelSelectionDescription": "छवि निर्माण के लिए उपयोग करने वाला मॉडल चुनें", "warningMissingKey": "⚠️ छवि निर्माण के लिए OpenRouter API कुंजी आवश्यक है। कृपया इसे ऊपर कॉन्फ़िगर करें।", - "successConfigured": "✓ छवि निर्माण कॉन्फ़िगर है और उपयोग के लिए तैयार है" + "successConfigured": "✓ छवि निर्माण कॉन्फ़िगर है और उपयोग के लिए तैयार है", + "providerLabel": "छवि निर्माण प्रदाता", + "providerDescription": "छवि निर्माण के लिए प्रदाता चुनें", + "geminiApiKeyLabel": "Gemini API कुंजी", + "geminiApiKeyPlaceholder": "अपनी Gemini API कुंजी दर्ज करें", + "geminiGetApiKeyText": "अपनी API कुंजी प्राप्त करें", + "warningMissingGeminiKey": "⚠️ छवि निर्माण के लिए Gemini API कुंजी आवश्यक है। कृपया इसे ऊपर कॉन्फ़िगर करें।" }, "RUN_SLASH_COMMAND": { "name": "मॉडल द्वारा शुरू किए गए स्लैश कमांड सक्षम करें", diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index aa2c1172119a..0b3f4059e89e 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -779,7 +779,13 @@ "modelSelectionLabel": "Model Pembuatan Gambar", "modelSelectionDescription": "Pilih model untuk pembuatan gambar", "warningMissingKey": "⚠️ Kunci API OpenRouter diperlukan untuk pembuatan gambar. Silakan konfigurasi di atas.", - "successConfigured": "✓ Pembuatan gambar dikonfigurasi dan siap digunakan" + "successConfigured": "✓ Pembuatan gambar dikonfigurasi dan siap digunakan", + "providerLabel": "Penyedia pembuatan gambar", + "providerDescription": "Pilih penyedia untuk pembuatan gambar", + "geminiApiKeyLabel": "Kunci API Gemini", + "geminiApiKeyPlaceholder": "Masukkan kunci API Gemini Anda", + "geminiGetApiKeyText": "Dapatkan kunci API Anda dari", + "warningMissingGeminiKey": "⚠️ Kunci API Gemini diperlukan untuk pembuatan gambar. Silakan konfigurasi di atas." }, "RUN_SLASH_COMMAND": { "name": "Aktifkan perintah slash yang dimulai model", diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index 6f2e06bb8fd6..0e2720ec598a 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -750,7 +750,13 @@ "modelSelectionLabel": "Modello di generazione immagini", "modelSelectionDescription": "Seleziona il modello per la generazione di immagini", "warningMissingKey": "⚠️ La chiave API OpenRouter è richiesta per la generazione di immagini. Configurala sopra.", - "successConfigured": "✓ La generazione di immagini è configurata e pronta per l'uso" + "successConfigured": "✓ La generazione di immagini è configurata e pronta per l'uso", + "providerLabel": "Provider di generazione immagini", + "providerDescription": "Scegli il provider per la generazione di immagini", + "geminiApiKeyLabel": "Chiave API Gemini", + "geminiApiKeyPlaceholder": "Inserisci la tua chiave API Gemini", + "geminiGetApiKeyText": "Ottieni la tua chiave API da", + "warningMissingGeminiKey": "⚠️ La chiave API Gemini è richiesta per la generazione di immagini. Configurala sopra." }, "RUN_SLASH_COMMAND": { "name": "Abilita comandi slash avviati dal modello", diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index cc1ea09317eb..d95c1707d05a 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -750,7 +750,13 @@ "modelSelectionLabel": "画像生成モデル", "modelSelectionDescription": "画像生成に使用するモデルを選択", "warningMissingKey": "⚠️ 画像生成にはOpenRouter APIキーが必要です。上記で設定してください。", - "successConfigured": "✓ 画像生成が設定され、使用準備完了です" + "successConfigured": "✓ 画像生成が設定され、使用準備完了です", + "providerLabel": "画像生成プロバイダー", + "providerDescription": "画像生成のプロバイダーを選択", + "geminiApiKeyLabel": "Gemini APIキー", + "geminiApiKeyPlaceholder": "Gemini APIキーを入力してください", + "geminiGetApiKeyText": "APIキーを取得する場所", + "warningMissingGeminiKey": "⚠️ 画像生成にはGemini APIキーが必要です。上記で設定してください。" }, "RUN_SLASH_COMMAND": { "name": "モデル開始スラッシュコマンドを有効にする", diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index 61539cfc4d9f..d85c3195c54d 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -750,7 +750,13 @@ "modelSelectionLabel": "이미지 생성 모델", "modelSelectionDescription": "이미지 생성에 사용할 모델을 선택하세요", "warningMissingKey": "⚠️ 이미지 생성에는 OpenRouter API 키가 필요합니다. 위에서 설정해주세요.", - "successConfigured": "✓ 이미지 생성이 구성되었으며 사용할 준비가 되었습니다" + "successConfigured": "✓ 이미지 생성이 구성되었으며 사용할 준비가 되었습니다", + "providerLabel": "이미지 생성 제공자", + "providerDescription": "이미지 생성 제공자를 선택하세요", + "geminiApiKeyLabel": "Gemini API 키", + "geminiApiKeyPlaceholder": "Gemini API 키를 입력하세요", + "geminiGetApiKeyText": "API 키를 받을 곳", + "warningMissingGeminiKey": "⚠️ 이미지 생성에는 Gemini API 키가 필요합니다. 위에서 설정해주세요." }, "RUN_SLASH_COMMAND": { "name": "모델 시작 슬래시 명령 활성화", diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index 41ee3e5910bb..ef4ea5ca9aa3 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -750,7 +750,13 @@ "modelSelectionLabel": "Afbeeldingsgeneratiemodel", "modelSelectionDescription": "Selecteer het model voor afbeeldingsgeneratie", "warningMissingKey": "⚠️ OpenRouter API-sleutel is vereist voor afbeeldingsgeneratie. Configureer deze hierboven.", - "successConfigured": "✓ Afbeeldingsgeneratie is geconfigureerd en klaar voor gebruik" + "successConfigured": "✓ Afbeeldingsgeneratie is geconfigureerd en klaar voor gebruik", + "providerLabel": "Afbeeldingsgeneratie provider", + "providerDescription": "Kies de provider voor afbeeldingsgeneratie", + "geminiApiKeyLabel": "Gemini API-sleutel", + "geminiApiKeyPlaceholder": "Voer je Gemini API-sleutel in", + "geminiGetApiKeyText": "Haal je API-sleutel op van", + "warningMissingGeminiKey": "⚠️ Gemini API-sleutel is vereist voor afbeeldingsgeneratie. Configureer deze hierboven." }, "RUN_SLASH_COMMAND": { "name": "Model-geïnitieerde slash-commando's inschakelen", diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index 6862d6f7edda..849ed5e9d487 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -750,7 +750,13 @@ "modelSelectionLabel": "Model generowania obrazów", "modelSelectionDescription": "Wybierz model do generowania obrazów", "warningMissingKey": "⚠️ Klucz API OpenRouter jest wymagany do generowania obrazów. Skonfiguruj go powyżej.", - "successConfigured": "✓ Generowanie obrazów jest skonfigurowane i gotowe do użycia" + "successConfigured": "✓ Generowanie obrazów jest skonfigurowane i gotowe do użycia", + "providerLabel": "Dostawca generowania obrazów", + "providerDescription": "Wybierz dostawcę do generowania obrazów", + "geminiApiKeyLabel": "Klucz API Gemini", + "geminiApiKeyPlaceholder": "Wprowadź swój klucz API Gemini", + "geminiGetApiKeyText": "Uzyskaj swój klucz API od", + "warningMissingGeminiKey": "⚠️ Klucz API Gemini jest wymagany do generowania obrazów. Skonfiguruj go powyżej." }, "RUN_SLASH_COMMAND": { "name": "Włącz polecenia slash inicjowane przez model", diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index b8184777acf0..b8074873d8e5 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -750,7 +750,13 @@ "modelSelectionLabel": "Modelo de Geração de Imagens", "modelSelectionDescription": "Selecione o modelo para geração de imagens", "warningMissingKey": "⚠️ A chave de API do OpenRouter é necessária para geração de imagens. Configure-a acima.", - "successConfigured": "✓ A geração de imagens está configurada e pronta para uso" + "successConfigured": "✓ A geração de imagens está configurada e pronta para uso", + "providerLabel": "Provedor de geração de imagens", + "providerDescription": "Escolha o provedor para geração de imagens", + "geminiApiKeyLabel": "Chave de API do Gemini", + "geminiApiKeyPlaceholder": "Digite sua chave de API do Gemini", + "geminiGetApiKeyText": "Obtenha sua chave de API de", + "warningMissingGeminiKey": "⚠️ A chave de API do Gemini é necessária para geração de imagens. Configure-a acima." }, "RUN_SLASH_COMMAND": { "name": "Ativar comandos slash iniciados pelo modelo", diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index bcbd72089a45..fb1225e69201 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -750,7 +750,13 @@ "modelSelectionLabel": "Модель генерации изображений", "modelSelectionDescription": "Выберите модель для генерации изображений", "warningMissingKey": "⚠️ API-ключ OpenRouter необходим для генерации изображений. Настройте его выше.", - "successConfigured": "✓ Генерация изображений настроена и готова к использованию" + "successConfigured": "✓ Генерация изображений настроена и готова к использованию", + "providerLabel": "Провайдер генерации изображений", + "providerDescription": "Выберите провайдера для генерации изображений", + "geminiApiKeyLabel": "API-ключ Gemini", + "geminiApiKeyPlaceholder": "Введите ваш API-ключ Gemini", + "geminiGetApiKeyText": "Получите ваш API-ключ от", + "warningMissingGeminiKey": "⚠️ API-ключ Gemini необходим для генерации изображений. Настройте его выше." }, "RUN_SLASH_COMMAND": { "name": "Включить слэш-команды, инициированные моделью", diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index 4ac28f47d2f2..5f39781d5347 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -750,7 +750,13 @@ "modelSelectionLabel": "Görüntü Üretim Modeli", "modelSelectionDescription": "Görüntü üretimi için kullanılacak modeli seçin", "warningMissingKey": "⚠️ Görüntü üretimi için OpenRouter API anahtarı gereklidir. Lütfen yukarıda yapılandırın.", - "successConfigured": "✓ Görüntü üretimi yapılandırılmış ve kullanıma hazır" + "successConfigured": "✓ Görüntü üretimi yapılandırılmış ve kullanıma hazır", + "providerLabel": "Görüntü üretim sağlayıcısı", + "providerDescription": "Görüntü üretimi için sağlayıcıyı seçin", + "geminiApiKeyLabel": "Gemini API Anahtarı", + "geminiApiKeyPlaceholder": "Gemini API anahtarınızı girin", + "geminiGetApiKeyText": "API anahtarınızı alın", + "warningMissingGeminiKey": "⚠️ Görüntü üretimi için Gemini API anahtarı gereklidir. Lütfen yukarıda yapılandırın." }, "RUN_SLASH_COMMAND": { "name": "Model tarafından başlatılan slash komutlarını etkinleştir", diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index 4303325d068e..9dec53ef5a5b 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -750,7 +750,13 @@ "modelSelectionLabel": "Mô hình tạo hình ảnh", "modelSelectionDescription": "Chọn mô hình để sử dụng cho việc tạo hình ảnh", "warningMissingKey": "⚠️ Khóa API OpenRouter là bắt buộc để tạo hình ảnh. Vui lòng cấu hình ở trên.", - "successConfigured": "✓ Tạo hình ảnh đã được cấu hình và sẵn sàng sử dụng" + "successConfigured": "✓ Tạo hình ảnh đã được cấu hình và sẵn sàng sử dụng", + "providerLabel": "Nhà cung cấp tạo hình ảnh", + "providerDescription": "Chọn nhà cung cấp cho việc tạo hình ảnh", + "geminiApiKeyLabel": "Khóa API Gemini", + "geminiApiKeyPlaceholder": "Nhập khóa API Gemini của bạn", + "geminiGetApiKeyText": "Lấy khóa API của bạn từ", + "warningMissingGeminiKey": "⚠️ Khóa API Gemini là bắt buộc để tạo hình ảnh. Vui lòng cấu hình ở trên." }, "RUN_SLASH_COMMAND": { "name": "Bật lệnh slash do mô hình khởi tạo", diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index f574106f4568..a0da50d7a479 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -750,7 +750,13 @@ "modelSelectionLabel": "图像生成模型", "modelSelectionDescription": "选择用于图像生成的模型", "warningMissingKey": "⚠️ 图像生成需要 OpenRouter API 密钥。请在上方配置。", - "successConfigured": "✓ 图像生成已配置完成,可以使用" + "successConfigured": "✓ 图像生成已配置完成,可以使用", + "providerLabel": "图像生成提供商", + "providerDescription": "选择图像生成的提供商", + "geminiApiKeyLabel": "Gemini API 密钥", + "geminiApiKeyPlaceholder": "输入您的 Gemini API 密钥", + "geminiGetApiKeyText": "获取您的 API 密钥", + "warningMissingGeminiKey": "⚠️ 图像生成需要 Gemini API 密钥。请在上方配置。" }, "RUN_SLASH_COMMAND": { "name": "启用模型发起的斜杠命令", diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index 67e8c43b60a9..5c09d7869562 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -750,7 +750,13 @@ "modelSelectionLabel": "圖像生成模型", "modelSelectionDescription": "選擇用於圖像生成的模型", "warningMissingKey": "⚠️ 圖像生成需要 OpenRouter API 金鑰。請在上方設定。", - "successConfigured": "✓ 圖像生成已設定完成並準備使用" + "successConfigured": "✓ 圖像生成已設定完成並準備使用", + "providerLabel": "圖像生成供應商", + "providerDescription": "選擇圖像生成的供應商", + "geminiApiKeyLabel": "Gemini API 金鑰", + "geminiApiKeyPlaceholder": "輸入您的 Gemini API 金鑰", + "geminiGetApiKeyText": "取得您的 API 金鑰從", + "warningMissingGeminiKey": "⚠️ 圖像生成需要 Gemini API 金鑰。請在上方設定。" }, "RUN_SLASH_COMMAND": { "name": "啟用模型啟動的斜線命令",