From ef88be25fc55ae583143eac2292147f62fb8de11 Mon Sep 17 00:00:00 2001 From: Daniel <57051444+daniel-lxs@users.noreply.github.com> Date: Thu, 28 Aug 2025 00:57:48 -0500 Subject: [PATCH] feat: add image generation tool with OpenRouter integration (#7474) Co-authored-by: Matt Rubens Co-authored-by: cte --- packages/types/npm/package.metadata.json | 2 +- packages/types/src/experiment.ts | 8 +- packages/types/src/provider-settings.ts | 7 + packages/types/src/tool.ts | 1 + pnpm-lock.yaml | 18 +- src/api/providers/openrouter.ts | 128 ++++++++++ .../presentAssistantMessage.ts | 6 + src/core/prompts/tools/generate-image.ts | 20 ++ src/core/prompts/tools/index.ts | 8 + src/core/tools/generateImageTool.ts | 193 ++++++++++++++ src/package.json | 2 +- src/shared/ExtensionMessage.ts | 3 + src/shared/__tests__/experiments.spec.ts | 3 + src/shared/experiments.ts | 2 + src/shared/tools.ts | 9 +- webview-ui/src/components/chat/ChatRow.tsx | 41 +++ webview-ui/src/components/chat/ChatView.tsx | 2 + .../src/components/common/ImageBlock.tsx | 15 ++ .../src/components/common/ImageViewer.tsx | 240 ++++++++++++++++++ .../settings/ExperimentalSettings.tsx | 18 ++ .../settings/ImageGenerationSettings.tsx | 122 +++++++++ .../src/components/settings/SettingsView.tsx | 6 +- .../__tests__/ExtensionStateContext.spec.tsx | 2 + webview-ui/src/i18n/locales/ca/chat.json | 6 +- webview-ui/src/i18n/locales/ca/common.json | 5 + webview-ui/src/i18n/locales/ca/settings.json | 11 + webview-ui/src/i18n/locales/de/chat.json | 6 +- webview-ui/src/i18n/locales/de/common.json | 5 + webview-ui/src/i18n/locales/de/settings.json | 11 + webview-ui/src/i18n/locales/en/chat.json | 4 + webview-ui/src/i18n/locales/en/common.json | 5 + webview-ui/src/i18n/locales/en/settings.json | 11 + webview-ui/src/i18n/locales/es/chat.json | 6 +- webview-ui/src/i18n/locales/es/common.json | 5 + webview-ui/src/i18n/locales/es/settings.json | 11 + webview-ui/src/i18n/locales/fr/chat.json | 6 +- webview-ui/src/i18n/locales/fr/common.json | 5 + webview-ui/src/i18n/locales/fr/settings.json | 11 + webview-ui/src/i18n/locales/hi/chat.json | 6 +- webview-ui/src/i18n/locales/hi/common.json | 5 + webview-ui/src/i18n/locales/hi/settings.json | 11 + webview-ui/src/i18n/locales/id/chat.json | 4 + webview-ui/src/i18n/locales/id/common.json | 5 + webview-ui/src/i18n/locales/id/settings.json | 11 + webview-ui/src/i18n/locales/it/chat.json | 6 +- webview-ui/src/i18n/locales/it/common.json | 5 + webview-ui/src/i18n/locales/it/settings.json | 11 + webview-ui/src/i18n/locales/ja/chat.json | 6 +- webview-ui/src/i18n/locales/ja/common.json | 5 + webview-ui/src/i18n/locales/ja/settings.json | 11 + webview-ui/src/i18n/locales/ko/chat.json | 6 +- webview-ui/src/i18n/locales/ko/common.json | 5 + webview-ui/src/i18n/locales/ko/settings.json | 11 + webview-ui/src/i18n/locales/nl/chat.json | 6 +- webview-ui/src/i18n/locales/nl/common.json | 5 + webview-ui/src/i18n/locales/nl/settings.json | 11 + webview-ui/src/i18n/locales/pl/chat.json | 6 +- webview-ui/src/i18n/locales/pl/common.json | 5 + webview-ui/src/i18n/locales/pl/settings.json | 11 + webview-ui/src/i18n/locales/pt-BR/chat.json | 6 +- webview-ui/src/i18n/locales/pt-BR/common.json | 5 + .../src/i18n/locales/pt-BR/settings.json | 11 + webview-ui/src/i18n/locales/ru/chat.json | 6 +- webview-ui/src/i18n/locales/ru/common.json | 5 + webview-ui/src/i18n/locales/ru/settings.json | 11 + webview-ui/src/i18n/locales/tr/chat.json | 6 +- webview-ui/src/i18n/locales/tr/common.json | 5 + webview-ui/src/i18n/locales/tr/settings.json | 11 + webview-ui/src/i18n/locales/vi/chat.json | 6 +- webview-ui/src/i18n/locales/vi/common.json | 5 + webview-ui/src/i18n/locales/vi/settings.json | 11 + webview-ui/src/i18n/locales/zh-CN/chat.json | 6 +- webview-ui/src/i18n/locales/zh-CN/common.json | 5 + .../src/i18n/locales/zh-CN/settings.json | 11 + webview-ui/src/i18n/locales/zh-TW/chat.json | 4 + webview-ui/src/i18n/locales/zh-TW/common.json | 5 + .../src/i18n/locales/zh-TW/settings.json | 11 + 77 files changed, 1216 insertions(+), 30 deletions(-) create mode 100644 src/core/prompts/tools/generate-image.ts create mode 100644 src/core/tools/generateImageTool.ts create mode 100644 webview-ui/src/components/common/ImageBlock.tsx create mode 100644 webview-ui/src/components/common/ImageViewer.tsx create mode 100644 webview-ui/src/components/settings/ImageGenerationSettings.tsx diff --git a/packages/types/npm/package.metadata.json b/packages/types/npm/package.metadata.json index cc7d35e549..46978350d6 100644 --- a/packages/types/npm/package.metadata.json +++ b/packages/types/npm/package.metadata.json @@ -1,6 +1,6 @@ { "name": "@roo-code/types", - "version": "1.62.0", + "version": "1.63.0", "description": "TypeScript type definitions for Roo Code.", "publishConfig": { "access": "public", diff --git a/packages/types/src/experiment.ts b/packages/types/src/experiment.ts index 5424121d67..5d5610daa1 100644 --- a/packages/types/src/experiment.ts +++ b/packages/types/src/experiment.ts @@ -6,7 +6,12 @@ import type { Keys, Equals, AssertEqual } from "./type-fu.js" * ExperimentId */ -export const experimentIds = ["powerSteering", "multiFileApplyDiff", "preventFocusDisruption"] as const +export const experimentIds = [ + "powerSteering", + "multiFileApplyDiff", + "preventFocusDisruption", + "imageGeneration", +] as const export const experimentIdsSchema = z.enum(experimentIds) @@ -20,6 +25,7 @@ export const experimentsSchema = z.object({ powerSteering: z.boolean().optional(), multiFileApplyDiff: z.boolean().optional(), preventFocusDisruption: z.boolean().optional(), + imageGeneration: z.boolean().optional(), }) export type Experiments = z.infer diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index b55dc8a8c3..15833e00c4 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -142,6 +142,13 @@ const openRouterSchema = baseProviderSettingsSchema.extend({ openRouterBaseUrl: z.string().optional(), openRouterSpecificProvider: z.string().optional(), openRouterUseMiddleOutTransform: z.boolean().optional(), + // Image generation settings (experimental) + openRouterImageGenerationSettings: z + .object({ + openRouterApiKey: z.string().optional(), + selectedModel: z.string().optional(), + }) + .optional(), }) const bedrockSchema = apiModelIdProviderModelSchema.extend({ diff --git a/packages/types/src/tool.ts b/packages/types/src/tool.ts index 7a3fd21199..c31f63df76 100644 --- a/packages/types/src/tool.ts +++ b/packages/types/src/tool.ts @@ -34,6 +34,7 @@ export const toolNames = [ "fetch_instructions", "codebase_search", "update_todo_list", + "generate_image", ] as const export const toolNamesSchema = z.enum(toolNames) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8555a377ad..3e01247290 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -584,8 +584,8 @@ importers: specifier: ^1.14.0 version: 1.14.0(typescript@5.8.3) '@roo-code/cloud': - specifier: ^0.25.0 - version: 0.25.0 + specifier: ^0.29.0 + version: 0.29.0 '@roo-code/ipc': specifier: workspace:^ version: link:../packages/ipc @@ -3346,11 +3346,11 @@ packages: cpu: [x64] os: [win32] - '@roo-code/cloud@0.25.0': - resolution: {integrity: sha512-bRPQ6Zc3u5IqbDb0Vzj5+E5zFiq0tHRLICkZfwi3hyHvAiX9HxzNOboyR/HJhD+pr5xBizTDrJmccfA3RwrEBA==} + '@roo-code/cloud@0.29.0': + resolution: {integrity: sha512-fXN0mdkd5GezpVrCspe6atUkwvSk5D4wF80g+lc8E3aPVqEAozoI97kHNulRChGlBw7UIdd5xxbr1Z8Jtn+S/Q==} - '@roo-code/types@1.61.0': - resolution: {integrity: sha512-YJdFc6aYfaZ8EN08KbWaKLehRr1dcN3G3CzDjpppb08iehSEUZMycax/ryP5/G4vl34HTdtzyHNMboDen5ElUg==} + '@roo-code/types@1.63.0': + resolution: {integrity: sha512-pX8ftkDq1CySBbkUTIW9/QEG52ttFT/kl0ID286l0L3W22wpGRUct6PCedNI9kLDM4s5sxaUeZx7b3rUChikkw==} '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} @@ -12732,9 +12732,9 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.40.2': optional: true - '@roo-code/cloud@0.25.0': + '@roo-code/cloud@0.29.0': dependencies: - '@roo-code/types': 1.61.0 + '@roo-code/types': 1.63.0 ioredis: 5.6.1 jwt-decode: 4.0.0 p-wait-for: 5.0.2 @@ -12745,7 +12745,7 @@ snapshots: - supports-color - utf-8-validate - '@roo-code/types@1.61.0': + '@roo-code/types@1.63.0': dependencies: zod: 3.25.76 diff --git a/src/api/providers/openrouter.ts b/src/api/providers/openrouter.ts index 6565daa238..349e32ced3 100644 --- a/src/api/providers/openrouter.ts +++ b/src/api/providers/openrouter.ts @@ -26,6 +26,33 @@ import { DEFAULT_HEADERS } from "./constants" import { BaseProvider } from "./base-provider" import type { SingleCompletionHandler } from "../index" +// Image generation types +interface ImageGenerationResponse { + choices?: Array<{ + message?: { + content?: string + images?: Array<{ + type?: string + image_url?: { + url?: string + } + }> + } + }> + error?: { + message?: string + type?: string + code?: string + } +} + +export interface ImageGenerationResult { + success: boolean + imageData?: string + imageFormat?: string + error?: string +} + // Add custom interface for OpenRouter params. type OpenRouterChatCompletionParams = OpenAI.Chat.ChatCompletionCreateParams & { transforms?: string[] @@ -242,4 +269,105 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH const completion = response as OpenAI.Chat.ChatCompletion return completion.choices[0]?.message?.content || "" } + + /** + * Generate an image using OpenRouter's image generation API + * @param prompt The text prompt for image generation + * @param model The model to use for generation + * @param apiKey The OpenRouter API key (must be explicitly provided) + * @returns The generated image data and format, or an error + */ + async generateImage(prompt: string, model: string, apiKey: string): Promise { + if (!apiKey) { + return { + success: false, + error: "OpenRouter API key is required for image generation", + } + } + + try { + const response = await fetch("https://openrouter.ai/api/v1/chat/completions", { + method: "POST", + headers: { + Authorization: `Bearer ${apiKey}`, + "Content-Type": "application/json", + "HTTP-Referer": "https://github.com/RooVetGit/Roo-Code", + "X-Title": "Roo Code", + }, + body: JSON.stringify({ + model, + messages: [ + { + role: "user", + content: prompt, + }, + ], + modalities: ["image", "text"], + }), + }) + + if (!response.ok) { + const errorText = await response.text() + let errorMessage = `Failed to generate image: ${response.status} ${response.statusText}` + try { + const errorJson = JSON.parse(errorText) + if (errorJson.error?.message) { + errorMessage = `Failed to generate image: ${errorJson.error.message}` + } + } catch { + // Use default error message + } + return { + success: false, + error: errorMessage, + } + } + + const result: ImageGenerationResponse = await response.json() + + if (result.error) { + return { + success: false, + error: `Failed to generate image: ${result.error.message}`, + } + } + + // Extract the generated image from the response + const images = result.choices?.[0]?.message?.images + if (!images || images.length === 0) { + return { + success: false, + error: "No image was generated in the response", + } + } + + const imageData = images[0]?.image_url?.url + if (!imageData) { + return { + success: false, + error: "Invalid image data in response", + } + } + + // Extract base64 data from data URL + const base64Match = imageData.match(/^data:image\/(png|jpeg|jpg);base64,(.+)$/) + if (!base64Match) { + return { + success: false, + error: "Invalid image format received", + } + } + + return { + success: true, + imageData: imageData, + imageFormat: base64Match[1], + } + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : "Unknown error occurred", + } + } + } } diff --git a/src/core/assistant-message/presentAssistantMessage.ts b/src/core/assistant-message/presentAssistantMessage.ts index 7728645db3..bde5b15180 100644 --- a/src/core/assistant-message/presentAssistantMessage.ts +++ b/src/core/assistant-message/presentAssistantMessage.ts @@ -28,6 +28,7 @@ import { attemptCompletionTool } from "../tools/attemptCompletionTool" import { newTaskTool } from "../tools/newTaskTool" import { updateTodoListTool } from "../tools/updateTodoListTool" +import { generateImageTool } from "../tools/generateImageTool" import { formatResponse } from "../prompts/responses" import { validateToolUse } from "../tools/validateToolUse" @@ -222,6 +223,8 @@ export async function presentAssistantMessage(cline: Task) { const modeName = getModeBySlug(mode, customModes)?.name ?? mode return `[${block.name} in ${modeName} mode: '${message}']` } + case "generate_image": + return `[${block.name} for '${block.params.path}']` } } @@ -552,6 +555,9 @@ export async function presentAssistantMessage(cline: Task) { askFinishSubTaskApproval, ) break + case "generate_image": + await generateImageTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag) + break } break diff --git a/src/core/prompts/tools/generate-image.ts b/src/core/prompts/tools/generate-image.ts new file mode 100644 index 0000000000..7869765228 --- /dev/null +++ b/src/core/prompts/tools/generate-image.ts @@ -0,0 +1,20 @@ +import { ToolArgs } from "./types" + +export function getGenerateImageDescription(args: ToolArgs): string { + return `## generate_image +Description: Request to generate an image using AI models through OpenRouter API. This tool creates images from text prompts and saves them to the specified path. +Parameters: +- prompt: (required) The text prompt describing the image to generate +- path: (required) The file path where the generated image should be saved (relative to the current workspace directory ${args.cwd}). The tool will automatically add the appropriate image extension if not provided. +Usage: + +Your image description here +path/to/save/image.png + + +Example: Requesting to generate a sunset image + +A beautiful sunset over mountains with vibrant orange and purple colors +images/sunset.png +` +} diff --git a/src/core/prompts/tools/index.ts b/src/core/prompts/tools/index.ts index 3eb112d270..8b4e90733c 100644 --- a/src/core/prompts/tools/index.ts +++ b/src/core/prompts/tools/index.ts @@ -25,6 +25,7 @@ import { getSwitchModeDescription } from "./switch-mode" import { getNewTaskDescription } from "./new-task" import { getCodebaseSearchDescription } from "./codebase-search" import { getUpdateTodoListDescription } from "./update-todo-list" +import { getGenerateImageDescription } from "./generate-image" import { CodeIndexManager } from "../../../services/code-index/manager" // Map of tool names to their description functions @@ -56,6 +57,7 @@ const toolDescriptionMap: Record string | undefined> apply_diff: (args) => args.diffStrategy ? args.diffStrategy.getToolDescription({ cwd: args.cwd, toolOptions: args.toolOptions }) : "", update_todo_list: (args) => getUpdateTodoListDescription(args), + generate_image: (args) => getGenerateImageDescription(args), } export function getToolDescriptionsForMode( @@ -129,6 +131,11 @@ export function getToolDescriptionsForMode( tools.delete("update_todo_list") } + // Conditionally exclude generate_image if experiment is not enabled + if (!experiments?.imageGeneration) { + tools.delete("generate_image") + } + // Map tool descriptions for allowed tools const descriptions = Array.from(tools).map((toolName) => { const descriptionFn = toolDescriptionMap[toolName] @@ -164,4 +171,5 @@ export { getInsertContentDescription, getSearchAndReplaceDescription, getCodebaseSearchDescription, + getGenerateImageDescription, } diff --git a/src/core/tools/generateImageTool.ts b/src/core/tools/generateImageTool.ts new file mode 100644 index 0000000000..97c29e0a62 --- /dev/null +++ b/src/core/tools/generateImageTool.ts @@ -0,0 +1,193 @@ +import path from "path" +import fs from "fs/promises" +import * as vscode from "vscode" +import { Task } from "../task/Task" +import { formatResponse } from "../prompts/responses" +import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools" +import { fileExistsAtPath } from "../../utils/fs" +import { getReadablePath } from "../../utils/path" +import { isPathOutsideWorkspace } from "../../utils/pathUtils" +import { EXPERIMENT_IDS, experiments } from "../../shared/experiments" +import { safeWriteJson } from "../../utils/safeWriteJson" +import { OpenRouterHandler } from "../../api/providers/openrouter" + +// Hardcoded list of image generation models for now +const IMAGE_GENERATION_MODELS = [ + "google/gemini-2.5-flash-image-preview", + // Add more models as they become available +] + +export async function generateImageTool( + cline: Task, + block: ToolUse, + askApproval: AskApproval, + handleError: HandleError, + pushToolResult: PushToolResult, + removeClosingTag: RemoveClosingTag, +) { + const prompt: string | undefined = block.params.prompt + const relPath: string | undefined = block.params.path + + // Check if the experiment is enabled + const provider = cline.providerRef.deref() + const state = await provider?.getState() + const isImageGenerationEnabled = experiments.isEnabled(state?.experiments ?? {}, EXPERIMENT_IDS.IMAGE_GENERATION) + + if (!isImageGenerationEnabled) { + pushToolResult( + formatResponse.toolError( + "Image generation is an experimental feature that must be enabled in settings. Please enable 'Image Generation' in the Experimental Settings section.", + ), + ) + return + } + + if (block.partial && (!prompt || !relPath)) { + // Wait for complete parameters + return + } + + if (!prompt) { + cline.consecutiveMistakeCount++ + cline.recordToolError("generate_image") + pushToolResult(await cline.sayAndCreateMissingParamError("generate_image", "prompt")) + return + } + + if (!relPath) { + cline.consecutiveMistakeCount++ + cline.recordToolError("generate_image") + pushToolResult(await cline.sayAndCreateMissingParamError("generate_image", "path")) + return + } + + // Validate access permissions + const accessAllowed = cline.rooIgnoreController?.validateAccess(relPath) + if (!accessAllowed) { + await cline.say("rooignore_error", relPath) + pushToolResult(formatResponse.toolError(formatResponse.rooIgnoreError(relPath))) + return + } + + // Check if file is write-protected + const isWriteProtected = cline.rooProtectedController?.isWriteProtected(relPath) || false + + // Get OpenRouter API key from experimental settings ONLY (no fallback to profile) + const apiConfiguration = state?.apiConfiguration + const imageGenerationSettings = apiConfiguration?.openRouterImageGenerationSettings + const openRouterApiKey = imageGenerationSettings?.openRouterApiKey + + 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( + "OpenRouter API key is required for image generation. Please configure it in the Image Generation experimental settings.", + ), + ) + return + } + + // Get selected model from settings or use default + const selectedModel = imageGenerationSettings?.selectedModel || 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) + + const sharedMessageProps = { + tool: "generateImage" as const, + path: getReadablePath(cline.cwd, removeClosingTag("path", relPath)), + content: prompt, + isOutsideWorkspace, + isProtected: isWriteProtected, + } + + try { + if (!block.partial) { + cline.consecutiveMistakeCount = 0 + + // Ask for approval before generating the image + const approvalMessage = JSON.stringify({ + ...sharedMessageProps, + content: prompt, + }) + + const didApprove = await askApproval("tool", approvalMessage, undefined, isWriteProtected) + + if (!didApprove) { + return + } + + // Create a temporary OpenRouter handler with minimal options + const openRouterHandler = new OpenRouterHandler({} as any) + + // Call the generateImage method with the explicit API key + const result = await openRouterHandler.generateImage(prompt, selectedModel, openRouterApiKey) + + if (!result.success) { + await cline.say("error", result.error || "Failed to generate image") + pushToolResult(formatResponse.toolError(result.error || "Failed to generate image")) + return + } + + if (!result.imageData) { + const errorMessage = "No image data received" + await cline.say("error", errorMessage) + pushToolResult(formatResponse.toolError(errorMessage)) + return + } + + // Extract base64 data from data URL + const base64Match = result.imageData.match(/^data:image\/(png|jpeg|jpg);base64,(.+)$/) + if (!base64Match) { + const errorMessage = "Invalid image format received" + await cline.say("error", errorMessage) + pushToolResult(formatResponse.toolError(errorMessage)) + return + } + + const imageFormat = base64Match[1] + const base64Data = base64Match[2] + + // Ensure the file has the correct extension + let finalPath = relPath + if (!finalPath.match(/\.(png|jpg|jpeg)$/i)) { + finalPath = `${finalPath}.${imageFormat === "jpeg" ? "jpg" : imageFormat}` + } + + // Convert base64 to buffer + const imageBuffer = Buffer.from(base64Data, "base64") + + // Create directory if it doesn't exist + const absolutePath = path.resolve(cline.cwd, finalPath) + const directory = path.dirname(absolutePath) + await fs.mkdir(directory, { recursive: true }) + + // Write the image file + await fs.writeFile(absolutePath, imageBuffer) + + // Track file creation + if (finalPath) { + await cline.fileContextTracker.trackFileContext(finalPath, "roo_edited") + } + + cline.didEditFile = true + + // Display the generated image in the chat using a text message with the image + await cline.say("text", getReadablePath(cline.cwd, finalPath), [result.imageData]) + + // Record successful tool usage + cline.recordToolUsage("generate_image") + + pushToolResult(formatResponse.toolResult(getReadablePath(cline.cwd, finalPath))) + + return + } + } catch (error) { + await handleError("generating image", error) + return + } +} diff --git a/src/package.json b/src/package.json index dc4524b8c1..8d806d2d30 100644 --- a/src/package.json +++ b/src/package.json @@ -429,7 +429,7 @@ "@mistralai/mistralai": "^1.9.18", "@modelcontextprotocol/sdk": "^1.9.0", "@qdrant/js-client-rest": "^1.14.0", - "@roo-code/cloud": "^0.25.0", + "@roo-code/cloud": "^0.29.0", "@roo-code/ipc": "workspace:^", "@roo-code/telemetry": "workspace:^", "@roo-code/types": "workspace:^", diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 09a15d6af7..c224901088 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -357,6 +357,8 @@ export interface ClineSayTool { | "finishTask" | "searchAndReplace" | "insertContent" + | "generateImage" + | "imageGenerated" path?: string diff?: string content?: string @@ -393,6 +395,7 @@ export interface ClineSayTool { }> }> question?: string + imageData?: string // Base64 encoded image data for generated images } // Must keep in sync with system prompt. diff --git a/src/shared/__tests__/experiments.spec.ts b/src/shared/__tests__/experiments.spec.ts index 607c1e0b04..d805a19548 100644 --- a/src/shared/__tests__/experiments.spec.ts +++ b/src/shared/__tests__/experiments.spec.ts @@ -29,6 +29,7 @@ describe("experiments", () => { powerSteering: false, multiFileApplyDiff: false, preventFocusDisruption: false, + imageGeneration: false, } expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false) }) @@ -38,6 +39,7 @@ describe("experiments", () => { powerSteering: true, multiFileApplyDiff: false, preventFocusDisruption: false, + imageGeneration: false, } expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(true) }) @@ -47,6 +49,7 @@ describe("experiments", () => { powerSteering: false, multiFileApplyDiff: false, preventFocusDisruption: false, + imageGeneration: false, } expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false) }) diff --git a/src/shared/experiments.ts b/src/shared/experiments.ts index 548b55f68c..b84d871503 100644 --- a/src/shared/experiments.ts +++ b/src/shared/experiments.ts @@ -4,6 +4,7 @@ export const EXPERIMENT_IDS = { MULTI_FILE_APPLY_DIFF: "multiFileApplyDiff", POWER_STEERING: "powerSteering", PREVENT_FOCUS_DISRUPTION: "preventFocusDisruption", + IMAGE_GENERATION: "imageGeneration", } as const satisfies Record type _AssertExperimentIds = AssertEqual>> @@ -18,6 +19,7 @@ export const experimentConfigsMap: Record = { MULTI_FILE_APPLY_DIFF: { enabled: false }, POWER_STEERING: { enabled: false }, PREVENT_FOCUS_DISRUPTION: { enabled: false }, + IMAGE_GENERATION: { enabled: false }, } export const experimentDefault = Object.fromEntries( diff --git a/src/shared/tools.ts b/src/shared/tools.ts index 047c2fe351..f15e8ef4c9 100644 --- a/src/shared/tools.ts +++ b/src/shared/tools.ts @@ -65,6 +65,7 @@ export const toolParamNames = [ "query", "args", "todos", + "prompt", ] as const export type ToolParamName = (typeof toolParamNames)[number] @@ -164,6 +165,11 @@ export interface SearchAndReplaceToolUse extends ToolUse { Partial, "use_regex" | "ignore_case" | "start_line" | "end_line">> } +export interface GenerateImageToolUse extends ToolUse { + name: "generate_image" + params: Partial, "prompt" | "path">> +} + // Define tool group configuration export type ToolGroupConfig = { tools: readonly string[] @@ -190,6 +196,7 @@ export const TOOL_DISPLAY_NAMES: Record = { search_and_replace: "search and replace", codebase_search: "codebase search", update_todo_list: "update todo list", + generate_image: "generate images", } as const // Define available tool groups. @@ -205,7 +212,7 @@ export const TOOL_GROUPS: Record = { ], }, edit: { - tools: ["apply_diff", "write_to_file", "insert_content", "search_and_replace"], + tools: ["apply_diff", "write_to_file", "insert_content", "search_and_replace", "generate_image"], }, browser: { tools: ["browser_action"], diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 4413eab33c..ad33ae9187 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -25,6 +25,7 @@ import CodeBlock from "../common/CodeBlock" import MarkdownBlock from "../common/MarkdownBlock" import { ReasoningBlock } from "./ReasoningBlock" import Thumbnails from "../common/Thumbnails" +import ImageBlock from "../common/ImageBlock" import McpResourceRow from "../mcp/McpResourceRow" @@ -790,6 +791,39 @@ export const ChatRowContent = ({ ) + case "generateImage": + return ( + <> +
+ {tool.isProtected ? ( + + ) : ( + toolIcon("file-media") + )} + + {message.type === "ask" + ? tool.isProtected + ? t("chat:fileOperations.wantsToGenerateImageProtected") + : tool.isOutsideWorkspace + ? t("chat:fileOperations.wantsToGenerateImageOutsideWorkspace") + : t("chat:fileOperations.wantsToGenerateImage") + : t("chat:fileOperations.didGenerateImage")} + +
+ {message.type === "ask" && ( + + )} + + ) default: return null } @@ -1002,6 +1036,13 @@ export const ChatRowContent = ({ return (
+ {message.images && message.images.length > 0 && ( +
+ {message.images.map((image, index) => ( + + ))} +
+ )}
) case "user_feedback": diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index a74d915da8..2c3bcb1a19 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -325,6 +325,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction + + + ) +} diff --git a/webview-ui/src/components/common/ImageViewer.tsx b/webview-ui/src/components/common/ImageViewer.tsx new file mode 100644 index 0000000000..bb2f6791a4 --- /dev/null +++ b/webview-ui/src/components/common/ImageViewer.tsx @@ -0,0 +1,240 @@ +import { useState, useCallback } from "react" +import { useCopyToClipboard } from "@src/utils/clipboard" +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { vscode } from "@src/utils/vscode" +import { MermaidActionButtons } from "./MermaidActionButtons" +import { Modal } from "./Modal" +import { TabButton } from "./TabButton" +import { IconButton } from "./IconButton" +import { ZoomControls } from "./ZoomControls" +import { StandardTooltip } from "@/components/ui" + +const MIN_ZOOM = 0.5 +const MAX_ZOOM = 20 + +export interface ImageViewerProps { + imageData: string // base64 data URL or regular URL + alt?: string + path?: string + showControls?: boolean + className?: string +} + +export function ImageViewer({ + imageData, + alt = "Generated image", + path, + showControls = true, + className = "", +}: ImageViewerProps) { + const [showModal, setShowModal] = useState(false) + const [zoomLevel, setZoomLevel] = useState(1) + const [copyFeedback, setCopyFeedback] = useState(false) + const [isHovering, setIsHovering] = useState(false) + const [isDragging, setIsDragging] = useState(false) + const [dragPosition, setDragPosition] = useState({ x: 0, y: 0 }) + const { copyWithFeedback } = useCopyToClipboard() + const { t } = useAppTranslation() + + /** + * Opens a modal with the image for zooming + */ + const handleZoom = async (e: React.MouseEvent) => { + e.stopPropagation() + setShowModal(true) + setZoomLevel(1) + setDragPosition({ x: 0, y: 0 }) + } + + /** + * Copies the image path to clipboard + */ + const handleCopy = async (e: React.MouseEvent) => { + e.stopPropagation() + + try { + const textToCopy = path || imageData + await copyWithFeedback(textToCopy, e) + + // Show feedback + setCopyFeedback(true) + setTimeout(() => setCopyFeedback(false), 2000) + } catch (err) { + console.error("Error copying:", err instanceof Error ? err.message : String(err)) + } + } + + /** + * Saves the image as a file + */ + const handleSave = async (e: React.MouseEvent) => { + e.stopPropagation() + + try { + // Send message to VSCode to save the image + vscode.postMessage({ + type: "saveImage", + dataUri: imageData, + }) + } catch (error) { + console.error("Error saving image:", error) + } + } + + /** + * Opens the image in VS Code's image viewer + */ + const handleOpenInEditor = (e: React.MouseEvent) => { + e.stopPropagation() + vscode.postMessage({ + type: "openImage", + text: imageData, + }) + } + + /** + * Adjust zoom level in the modal + */ + const adjustZoom = (amount: number) => { + setZoomLevel((prev) => { + const newZoom = prev + amount + return Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, newZoom)) + }) + } + + /** + * Handle wheel event for zooming with scroll wheel + */ + const handleWheel = useCallback((e: React.WheelEvent) => { + e.preventDefault() + e.stopPropagation() + + // Determine zoom direction and amount + // Negative deltaY means scrolling up (zoom in), positive means scrolling down (zoom out) + const delta = e.deltaY > 0 ? -0.2 : 0.2 + adjustZoom(delta) + }, []) + + /** + * Handle mouse enter event for image container + */ + const handleMouseEnter = () => { + setIsHovering(true) + } + + /** + * Handle mouse leave event for image container + */ + const handleMouseLeave = () => { + setIsHovering(false) + } + + return ( + <> +
+ {alt} + {path &&
{path}
} + {showControls && isHovering && ( +
+ {}} // Not applicable for images + copyFeedback={copyFeedback} + /> +
+ )} +
+ + setShowModal(false)}> +
+
+ {}} + /> +
+ +
+ + setShowModal(false)} /> + +
+
+
+
{ + setIsDragging(true) + e.preventDefault() + }} + onMouseMove={(e) => { + if (isDragging) { + setDragPosition((prev) => ({ + x: prev.x + e.movementX / zoomLevel, + y: prev.y + e.movementY / zoomLevel, + })) + } + }} + onMouseUp={() => setIsDragging(false)} + onMouseLeave={() => setIsDragging(false)}> + {alt} +
+
+ {Math.round(zoomLevel * 100)}% +
+
+
+ + {path && ( + + + + )} + + + +
+
+ + ) +} diff --git a/webview-ui/src/components/settings/ExperimentalSettings.tsx b/webview-ui/src/components/settings/ExperimentalSettings.tsx index 3c57f978d2..7df649354d 100644 --- a/webview-ui/src/components/settings/ExperimentalSettings.tsx +++ b/webview-ui/src/components/settings/ExperimentalSettings.tsx @@ -13,6 +13,7 @@ import { SectionHeader } from "./SectionHeader" import { Section } from "./Section" import { ExperimentalFeature } from "./ExperimentalFeature" import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react" +import { ImageGenerationSettings } from "./ImageGenerationSettings" type ExperimentalSettingsProps = HTMLAttributes & { experiments: Experiments @@ -20,6 +21,8 @@ type ExperimentalSettingsProps = HTMLAttributes & { // Include Files Changed Overview toggle in Experimental section per review feedback filesChangedEnabled?: boolean setCachedStateField?: SetCachedStateField<"filesChangedEnabled"> + apiConfiguration?: any + setApiConfigurationField?: any } export const ExperimentalSettings = ({ @@ -27,6 +30,8 @@ export const ExperimentalSettings = ({ setExperimentEnabled, filesChangedEnabled, setCachedStateField, + apiConfiguration, + setApiConfigurationField, className, ...props }: ExperimentalSettingsProps) => { @@ -75,6 +80,19 @@ export const ExperimentalSettings = ({ /> ) } + if (config[0] === "IMAGE_GENERATION" && apiConfiguration && setApiConfigurationField) { + return ( + + setExperimentEnabled(EXPERIMENT_IDS.IMAGE_GENERATION, enabled) + } + apiConfiguration={apiConfiguration} + setApiConfigurationField={setApiConfigurationField} + /> + ) + } return ( void + apiConfiguration: ProviderSettings + setApiConfigurationField: ( + field: K, + value: ProviderSettings[K], + isUserAction?: boolean, + ) => void +} + +// Hardcoded list of image generation models +const IMAGE_GENERATION_MODELS = [ + { value: "google/gemini-2.5-flash-image-preview", label: "Gemini 2.5 Flash Image Preview" }, + // Add more models as they become available +] + +export const ImageGenerationSettings = ({ + enabled, + onChange, + apiConfiguration, + setApiConfigurationField, +}: ImageGenerationSettingsProps) => { + const { t } = useAppTranslation() + + // Get image generation settings from apiConfiguration + const imageGenerationSettings = apiConfiguration?.openRouterImageGenerationSettings || {} + const [openRouterApiKey, setOpenRouterApiKey] = useState(imageGenerationSettings.openRouterApiKey || "") + const [selectedModel, setSelectedModel] = useState( + imageGenerationSettings.selectedModel || IMAGE_GENERATION_MODELS[0].value, + ) + + // Update parent state when local state changes + useEffect(() => { + const newSettings = { + openRouterApiKey, + selectedModel, + } + setApiConfigurationField("openRouterImageGenerationSettings", newSettings) + }, [openRouterApiKey, selectedModel, setApiConfigurationField]) + + return ( +
+
+
+ onChange(e.target.checked)}> + {t("settings:experimental.IMAGE_GENERATION.name")} + +
+

+ {t("settings:experimental.IMAGE_GENERATION.description")} +

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

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

+
+ + {/* Model Selection */} +
+ + setSelectedModel(e.target.value)} + className="w-full"> + {IMAGE_GENERATION_MODELS.map((model) => ( + + {model.label} + + ))} + +

+ {t("settings:experimental.IMAGE_GENERATION.modelSelectionDescription")} +

+
+ + {/* Status Message */} + {enabled && !openRouterApiKey && ( +
+ {t("settings:experimental.IMAGE_GENERATION.warningMissingKey")} +
+ )} + + {enabled && openRouterApiKey && ( +
+ {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 dae3c3a8bc..667ddb310f 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -724,8 +724,10 @@ const SettingsView = forwardRef(({ onDone, t } + filesChangedEnabled={filesChangedEnabled} + setCachedStateField={setCachedStateField as SetCachedStateField<"filesChangedEnabled">} + apiConfiguration={apiConfiguration} + setApiConfigurationField={setApiConfigurationField} /> )} diff --git a/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx b/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx index 74f46176fa..7770058efa 100644 --- a/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx +++ b/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx @@ -231,6 +231,7 @@ describe("mergeExtensionState", () => { multiFileApplyDiff: true, preventFocusDisruption: false, newTaskRequireTodos: false, + imageGeneration: false, } as Record, } @@ -249,6 +250,7 @@ describe("mergeExtensionState", () => { multiFileApplyDiff: true, preventFocusDisruption: false, newTaskRequireTodos: false, + imageGeneration: false, }) }) }) diff --git a/webview-ui/src/i18n/locales/ca/chat.json b/webview-ui/src/i18n/locales/ca/chat.json index 9beddb9e27..d56a34f7f2 100644 --- a/webview-ui/src/i18n/locales/ca/chat.json +++ b/webview-ui/src/i18n/locales/ca/chat.json @@ -175,7 +175,11 @@ "wantsToInsertAtEnd": "Roo vol afegir contingut al final d'aquest fitxer:", "wantsToReadAndXMore": "En Roo vol llegir aquest fitxer i {{count}} més:", "wantsToReadMultiple": "Roo vol llegir diversos fitxers:", - "wantsToApplyBatchChanges": "Roo vol aplicar canvis a múltiples fitxers:" + "wantsToApplyBatchChanges": "Roo vol aplicar canvis a múltiples fitxers:", + "wantsToGenerateImage": "Roo vol generar una imatge:", + "wantsToGenerateImageOutsideWorkspace": "Roo vol generar una imatge fora de l'espai de treball:", + "wantsToGenerateImageProtected": "Roo vol generar una imatge en una ubicació protegida:", + "didGenerateImage": "Roo ha generat una imatge:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo vol veure els fitxers de nivell superior en aquest directori:", diff --git a/webview-ui/src/i18n/locales/ca/common.json b/webview-ui/src/i18n/locales/ca/common.json index 2351c99d01..c056a44328 100644 --- a/webview-ui/src/i18n/locales/ca/common.json +++ b/webview-ui/src/i18n/locales/ca/common.json @@ -48,6 +48,11 @@ "copyError": "Error copiant la imatge" } }, + "image": { + "tabs": { + "view": "Imatge" + } + }, "file": { "errors": { "invalidDataUri": "Format d'URI de dades no vàlid", diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index e6bf05d55a..923052b518 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -737,6 +737,17 @@ "NEW_TASK_REQUIRE_TODOS": { "name": "Requerir la llista 'todos' per a noves tasques", "description": "Quan estigui activat, l'eina new_task requerirà que es proporcioni un paràmetre 'todos'. Això garanteix que totes les noves tasques comencin amb una llista clara d'objectius. Quan estigui desactivat (per defecte), el paràmetre 'todos' continua sent opcional per a la compatibilitat amb versions anteriors." + }, + "IMAGE_GENERATION": { + "name": "Habilitar generació d'imatges amb IA", + "description": "Quan estigui habilitat, Roo pot generar imatges a partir de prompts de text utilitzant els models de generació d'imatges d'OpenRouter. Requereix que es configuri una clau d'API d'OpenRouter.", + "openRouterApiKeyLabel": "Clau API d'OpenRouter", + "openRouterApiKeyPlaceholder": "Introdueix la teva clau API d'OpenRouter", + "getApiKeyText": "Obté la teva clau API de", + "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" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/de/chat.json b/webview-ui/src/i18n/locales/de/chat.json index bc0893acd0..9f6c98ef2f 100644 --- a/webview-ui/src/i18n/locales/de/chat.json +++ b/webview-ui/src/i18n/locales/de/chat.json @@ -175,7 +175,11 @@ "wantsToInsertWithLineNumber": "Roo möchte Inhalte in diese Datei in Zeile {{lineNumber}} einfügen:", "wantsToInsertAtEnd": "Roo möchte Inhalte am Ende dieser Datei anhängen:", "wantsToReadMultiple": "Roo möchte mehrere Dateien lesen:", - "wantsToApplyBatchChanges": "Roo möchte Änderungen an mehreren Dateien vornehmen:" + "wantsToApplyBatchChanges": "Roo möchte Änderungen an mehreren Dateien vornehmen:", + "wantsToGenerateImage": "Roo möchte ein Bild generieren:", + "wantsToGenerateImageOutsideWorkspace": "Roo möchte ein Bild außerhalb des Arbeitsbereichs generieren:", + "wantsToGenerateImageProtected": "Roo möchte ein Bild an einem geschützten Ort generieren:", + "didGenerateImage": "Roo hat ein Bild generiert:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo möchte die Dateien auf oberster Ebene in diesem Verzeichnis anzeigen:", diff --git a/webview-ui/src/i18n/locales/de/common.json b/webview-ui/src/i18n/locales/de/common.json index 0fec0ba964..85137922ff 100644 --- a/webview-ui/src/i18n/locales/de/common.json +++ b/webview-ui/src/i18n/locales/de/common.json @@ -48,6 +48,11 @@ "copyError": "Fehler beim Kopieren des Bildes" } }, + "image": { + "tabs": { + "view": "Bild" + } + }, "file": { "errors": { "invalidDataUri": "Ungültiges Daten-URI-Format", diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index b1a6319450..1728ae44fb 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -737,6 +737,17 @@ "NEW_TASK_REQUIRE_TODOS": { "name": "'todos'-Liste für neue Aufgaben anfordern", "description": "Wenn aktiviert, erfordert das new_task-Tool die Angabe eines todos-Parameters. Dies stellt sicher, dass alle neuen Aufgaben mit einer klaren Zielliste beginnen. Wenn deaktiviert (Standard), bleibt der todos-Parameter aus Gründen der Abwärtskompatibilität optional." + }, + "IMAGE_GENERATION": { + "name": "KI-Bildgenerierung aktivieren", + "description": "Wenn aktiviert, kann Roo Bilder aus Textprompts mit OpenRouters Bildgenerierungsmodellen erstellen. Erfordert einen konfigurierten OpenRouter API-Schlüssel.", + "openRouterApiKeyLabel": "OpenRouter API-Schlüssel", + "openRouterApiKeyPlaceholder": "Gib deinen OpenRouter API-Schlüssel ein", + "getApiKeyText": "Hol dir deinen API-Schlüssel von", + "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" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json index 74bb3b0c79..a7e4b51d02 100644 --- a/webview-ui/src/i18n/locales/en/chat.json +++ b/webview-ui/src/i18n/locales/en/chat.json @@ -182,6 +182,10 @@ "wantsToEditOutsideWorkspace": "Roo wants to edit this file outside of the workspace:", "wantsToEditProtected": "Roo wants to edit a protected configuration file:", "wantsToApplyBatchChanges": "Roo wants to apply changes to multiple files:", + "wantsToGenerateImage": "Roo wants to generate an image:", + "wantsToGenerateImageOutsideWorkspace": "Roo wants to generate an image outside of the workspace:", + "wantsToGenerateImageProtected": "Roo wants to generate an image in a protected location:", + "didGenerateImage": "Roo generated an image:", "wantsToCreate": "Roo wants to create a new file:", "wantsToSearchReplace": "Roo wants to search and replace in this file:", "didSearchReplace": "Roo performed search and replace on this file:", diff --git a/webview-ui/src/i18n/locales/en/common.json b/webview-ui/src/i18n/locales/en/common.json index b4bc816a2b..973cb48297 100644 --- a/webview-ui/src/i18n/locales/en/common.json +++ b/webview-ui/src/i18n/locales/en/common.json @@ -48,6 +48,11 @@ "copyError": "Error copying image" } }, + "image": { + "tabs": { + "view": "Image" + } + }, "file": { "errors": { "invalidDataUri": "Invalid data URI format", diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 05a5291191..4205984a6a 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -736,6 +736,17 @@ "NEW_TASK_REQUIRE_TODOS": { "name": "Require 'todos' list for new tasks", "description": "When enabled, the new_task tool will require a todos parameter to be provided. This ensures all new tasks start with a clear list of objectives. When disabled (default), the todos parameter remains optional for backward compatibility." + }, + "IMAGE_GENERATION": { + "name": "Enable AI image generation", + "description": "When enabled, Roo can generate images from text prompts using OpenRouter's image generation models. Requires an OpenRouter API key to be configured.", + "openRouterApiKeyLabel": "OpenRouter API Key", + "openRouterApiKeyPlaceholder": "Enter your OpenRouter API key", + "getApiKeyText": "Get your API key from", + "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" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/es/chat.json b/webview-ui/src/i18n/locales/es/chat.json index 85d36ccd77..3cb9631a3a 100644 --- a/webview-ui/src/i18n/locales/es/chat.json +++ b/webview-ui/src/i18n/locales/es/chat.json @@ -175,7 +175,11 @@ "wantsToInsertAtEnd": "Roo quiere añadir contenido al final de este archivo:", "wantsToReadAndXMore": "Roo quiere leer este archivo y {{count}} más:", "wantsToReadMultiple": "Roo quiere leer varios archivos:", - "wantsToApplyBatchChanges": "Roo quiere aplicar cambios a múltiples archivos:" + "wantsToApplyBatchChanges": "Roo quiere aplicar cambios a múltiples archivos:", + "wantsToGenerateImage": "Roo quiere generar una imagen:", + "wantsToGenerateImageOutsideWorkspace": "Roo quiere generar una imagen fuera del espacio de trabajo:", + "wantsToGenerateImageProtected": "Roo quiere generar una imagen en una ubicación protegida:", + "didGenerateImage": "Roo generó una imagen:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo quiere ver los archivos de nivel superior en este directorio:", diff --git a/webview-ui/src/i18n/locales/es/common.json b/webview-ui/src/i18n/locales/es/common.json index a733673470..a293008d8a 100644 --- a/webview-ui/src/i18n/locales/es/common.json +++ b/webview-ui/src/i18n/locales/es/common.json @@ -48,6 +48,11 @@ "copyError": "Error copiando la imagen" } }, + "image": { + "tabs": { + "view": "Imagen" + } + }, "file": { "errors": { "invalidDataUri": "Formato de URI de datos inválido", diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index 620c570cb3..bd1ad45459 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -737,6 +737,17 @@ "NEW_TASK_REQUIRE_TODOS": { "name": "Requerir lista de 'todos' para nuevas tareas", "description": "Cuando está habilitado, la herramienta new_task requerirá que se proporcione un parámetro todos. Esto asegura que todas las nuevas tareas comiencen con una lista clara de objetivos. Cuando está deshabilitado (predeterminado), el parámetro todos permanece opcional por compatibilidad con versiones anteriores." + }, + "IMAGE_GENERATION": { + "name": "Habilitar generación de imágenes con IA", + "description": "Cuando esté habilitado, Roo puede generar imágenes a partir de prompts de texto usando los modelos de generación de imágenes de OpenRouter. Requiere que se configure una clave de API de OpenRouter.", + "openRouterApiKeyLabel": "Clave API de OpenRouter", + "openRouterApiKeyPlaceholder": "Introduce tu clave API de OpenRouter", + "getApiKeyText": "Obtén tu clave API de", + "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" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/fr/chat.json b/webview-ui/src/i18n/locales/fr/chat.json index ed8e542753..190c1114ad 100644 --- a/webview-ui/src/i18n/locales/fr/chat.json +++ b/webview-ui/src/i18n/locales/fr/chat.json @@ -172,7 +172,11 @@ "wantsToInsertAtEnd": "Roo veut ajouter du contenu à la fin de ce fichier :", "wantsToReadAndXMore": "Roo veut lire ce fichier et {{count}} de plus :", "wantsToReadMultiple": "Roo souhaite lire plusieurs fichiers :", - "wantsToApplyBatchChanges": "Roo veut appliquer des modifications à plusieurs fichiers :" + "wantsToApplyBatchChanges": "Roo veut appliquer des modifications à plusieurs fichiers :", + "wantsToGenerateImage": "Roo veut générer une image :", + "wantsToGenerateImageOutsideWorkspace": "Roo veut générer une image en dehors de l'espace de travail :", + "wantsToGenerateImageProtected": "Roo veut générer une image dans un emplacement protégé :", + "didGenerateImage": "Roo a généré une image :" }, "instructions": { "wantsToFetch": "Roo veut récupérer des instructions détaillées pour aider à la tâche actuelle" diff --git a/webview-ui/src/i18n/locales/fr/common.json b/webview-ui/src/i18n/locales/fr/common.json index 4c4ad83bc4..fd7f53dd97 100644 --- a/webview-ui/src/i18n/locales/fr/common.json +++ b/webview-ui/src/i18n/locales/fr/common.json @@ -48,6 +48,11 @@ "copyError": "Erreur lors de la copie de l'image" } }, + "image": { + "tabs": { + "view": "Image" + } + }, "file": { "errors": { "invalidDataUri": "Format d'URI de données invalide", diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index 73c03387c9..598b87fa7e 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -737,6 +737,17 @@ "NEW_TASK_REQUIRE_TODOS": { "name": "Exiger la liste 'todos' pour les nouvelles tâches", "description": "Lorsqu'il est activé, l'outil new_task exigera qu'un paramètre todos soit fourni. Cela garantit que toutes les nouvelles tâches commencent avec une liste claire d'objectifs. Lorsqu'il est désactivé (par défaut), le paramètre todos reste facultatif pour la compatibilité descendante." + }, + "IMAGE_GENERATION": { + "name": "Activer la génération d'images IA", + "description": "Lorsqu'activé, Roo peut générer des images à partir de prompts textuels en utilisant les modèles de génération d'images d'OpenRouter. Nécessite qu'une clé API OpenRouter soit configurée.", + "openRouterApiKeyLabel": "Clé API OpenRouter", + "openRouterApiKeyPlaceholder": "Entrez votre clé API OpenRouter", + "getApiKeyText": "Obtenez votre clé API depuis", + "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" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/hi/chat.json b/webview-ui/src/i18n/locales/hi/chat.json index 93efe3acee..2de959c1ff 100644 --- a/webview-ui/src/i18n/locales/hi/chat.json +++ b/webview-ui/src/i18n/locales/hi/chat.json @@ -175,7 +175,11 @@ "wantsToInsertAtEnd": "Roo इस फ़ाइल के अंत में सामग्री जोड़ना चाहता है:", "wantsToReadAndXMore": "रू इस फ़ाइल को और {{count}} अन्य को पढ़ना चाहता है:", "wantsToReadMultiple": "Roo कई फ़ाइलें पढ़ना चाहता है:", - "wantsToApplyBatchChanges": "Roo कई फ़ाइलों में परिवर्तन लागू करना चाहता है:" + "wantsToApplyBatchChanges": "Roo कई फ़ाइलों में परिवर्तन लागू करना चाहता है:", + "wantsToGenerateImage": "Roo एक छवि बनाना चाहता है:", + "wantsToGenerateImageOutsideWorkspace": "Roo कार्यक्षेत्र के बाहर एक छवि बनाना चाहता है:", + "wantsToGenerateImageProtected": "Roo एक संरक्षित स्थान पर छवि बनाना चाहता है:", + "didGenerateImage": "Roo ने एक छवि बनाई:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo इस निर्देशिका में शीर्ष स्तर की फ़ाइलें देखना चाहता है:", diff --git a/webview-ui/src/i18n/locales/hi/common.json b/webview-ui/src/i18n/locales/hi/common.json index 7e809bd0a7..15039dc900 100644 --- a/webview-ui/src/i18n/locales/hi/common.json +++ b/webview-ui/src/i18n/locales/hi/common.json @@ -48,6 +48,11 @@ "copyError": "इमेज कॉपी करने में त्रुटि" } }, + "image": { + "tabs": { + "view": "चित्र" + } + }, "file": { "errors": { "invalidDataUri": "अमान्य डेटा URI फॉर्मेट", diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index fe08bb3571..ced27ff7da 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -738,6 +738,17 @@ "NEW_TASK_REQUIRE_TODOS": { "name": "नए कार्यों के लिए 'todos' सूची की आवश्यकता है", "description": "जब सक्षम किया जाता है, तो new_task टूल को todos पैरामीटर प्रदान करने की आवश्यकता होगी। यह सुनिश्चित करता है कि सभी नए कार्य स्पष्ट उद्देश्यों की सूची के साथ शुरू हों। जब अक्षम किया जाता है (डिफ़ॉल्ट), तो todos पैरामीटर पिछड़े संगतता के लिए वैकल्पिक रहता है।" + }, + "IMAGE_GENERATION": { + "name": "AI छवि निर्माण सक्षम करें", + "description": "जब सक्षम किया जाता है, तो Roo OpenRouter के छवि निर्माण मॉडल का उपयोग करके टेक्स्ट प्रॉम्प्ट से छवियां उत्पन्न कर सकता है। एक कॉन्फ़िगर किए गए OpenRouter API कुंजी की आवश्यकता होती है।", + "openRouterApiKeyLabel": "OpenRouter API कुंजी", + "openRouterApiKeyPlaceholder": "अपनी OpenRouter API कुंजी दर्ज करें", + "getApiKeyText": "अपनी API कुंजी प्राप्त करें", + "modelSelectionLabel": "छवि निर्माण मॉडल", + "modelSelectionDescription": "छवि निर्माण के लिए उपयोग करने वाला मॉडल चुनें", + "warningMissingKey": "⚠️ छवि निर्माण के लिए OpenRouter API कुंजी आवश्यक है। कृपया इसे ऊपर कॉन्फ़िगर करें।", + "successConfigured": "✓ छवि निर्माण कॉन्फ़िगर है और उपयोग के लिए तैयार है" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/id/chat.json b/webview-ui/src/i18n/locales/id/chat.json index 63157aa989..1b2f1cf56f 100644 --- a/webview-ui/src/i18n/locales/id/chat.json +++ b/webview-ui/src/i18n/locales/id/chat.json @@ -185,6 +185,10 @@ "wantsToEditOutsideWorkspace": "Roo ingin mengedit file ini di luar workspace:", "wantsToEditProtected": "Roo ingin mengedit file konfigurasi yang dilindungi:", "wantsToApplyBatchChanges": "Roo ingin menerapkan perubahan ke beberapa file:", + "wantsToGenerateImage": "Roo ingin menghasilkan gambar:", + "wantsToGenerateImageOutsideWorkspace": "Roo ingin menghasilkan gambar di luar workspace:", + "wantsToGenerateImageProtected": "Roo ingin menghasilkan gambar di lokasi yang dilindungi:", + "didGenerateImage": "Roo telah menghasilkan gambar:", "wantsToCreate": "Roo ingin membuat file baru:", "wantsToSearchReplace": "Roo ingin mencari dan mengganti di file ini:", "didSearchReplace": "Roo melakukan pencarian dan penggantian pada file ini:", diff --git a/webview-ui/src/i18n/locales/id/common.json b/webview-ui/src/i18n/locales/id/common.json index 86818bb084..0dac9b2987 100644 --- a/webview-ui/src/i18n/locales/id/common.json +++ b/webview-ui/src/i18n/locales/id/common.json @@ -48,6 +48,11 @@ "copyError": "Error menyalin gambar" } }, + "image": { + "tabs": { + "view": "Gambar" + } + }, "file": { "errors": { "invalidDataUri": "Format data URI tidak valid", diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index 6fd9dcee5b..8e4434bb67 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -767,6 +767,17 @@ "NEW_TASK_REQUIRE_TODOS": { "name": "Membutuhkan daftar 'todos' untuk tugas baru", "description": "Ketika diaktifkan, alat new_task akan membutuhkan parameter todos untuk disediakan. Ini memastikan semua tugas baru dimulai dengan daftar tujuan yang jelas. Ketika dinonaktifkan (default), parameter todos tetap opsional untuk kompatibilitas mundur." + }, + "IMAGE_GENERATION": { + "name": "Aktifkan pembuatan gambar AI", + "description": "Ketika diaktifkan, Roo dapat menghasilkan gambar dari prompt teks menggunakan model pembuatan gambar OpenRouter. Memerlukan kunci API OpenRouter yang dikonfigurasi.", + "openRouterApiKeyLabel": "Kunci API OpenRouter", + "openRouterApiKeyPlaceholder": "Masukkan kunci API OpenRouter Anda", + "getApiKeyText": "Dapatkan kunci API Anda dari", + "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" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/it/chat.json b/webview-ui/src/i18n/locales/it/chat.json index 10c1c9826d..e210124df0 100644 --- a/webview-ui/src/i18n/locales/it/chat.json +++ b/webview-ui/src/i18n/locales/it/chat.json @@ -175,7 +175,11 @@ "wantsToInsertAtEnd": "Roo vuole aggiungere contenuto alla fine di questo file:", "wantsToReadAndXMore": "Roo vuole leggere questo file e altri {{count}}:", "wantsToReadMultiple": "Roo vuole leggere più file:", - "wantsToApplyBatchChanges": "Roo vuole applicare modifiche a più file:" + "wantsToApplyBatchChanges": "Roo vuole applicare modifiche a più file:", + "wantsToGenerateImage": "Roo vuole generare un'immagine:", + "wantsToGenerateImageOutsideWorkspace": "Roo vuole generare un'immagine fuori dall'area di lavoro:", + "wantsToGenerateImageProtected": "Roo vuole generare un'immagine in una posizione protetta:", + "didGenerateImage": "Roo ha generato un'immagine:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo vuole visualizzare i file di primo livello in questa directory:", diff --git a/webview-ui/src/i18n/locales/it/common.json b/webview-ui/src/i18n/locales/it/common.json index 94f637ac3d..9ac9cbadad 100644 --- a/webview-ui/src/i18n/locales/it/common.json +++ b/webview-ui/src/i18n/locales/it/common.json @@ -48,6 +48,11 @@ "copyError": "Errore nella copia dell'immagine" } }, + "image": { + "tabs": { + "view": "Immagine" + } + }, "file": { "errors": { "invalidDataUri": "Formato URI dati non valido", diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index 5548ef158a..7d396fecd2 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -738,6 +738,17 @@ "NEW_TASK_REQUIRE_TODOS": { "name": "Richiedi elenco 'todos' per nuove attività", "description": "Quando abilitato, lo strumento new_task richiederà la fornitura di un parametro todos. Ciò garantisce che tutte le nuove attività inizino con un elenco chiaro di obiettivi. Quando disabilitato (impostazione predefinita), il parametro todos rimane facoltativo per la compatibilità con le versioni precedenti." + }, + "IMAGE_GENERATION": { + "name": "Abilita generazione immagini AI", + "description": "Quando abilitato, Roo può generare immagini da prompt di testo utilizzando i modelli di generazione immagini di OpenRouter. Richiede una chiave API OpenRouter configurata.", + "openRouterApiKeyLabel": "Chiave API OpenRouter", + "openRouterApiKeyPlaceholder": "Inserisci la tua chiave API OpenRouter", + "getApiKeyText": "Ottieni la tua chiave API da", + "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" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/ja/chat.json b/webview-ui/src/i18n/locales/ja/chat.json index 30fc6a2cf9..a5b393ebea 100644 --- a/webview-ui/src/i18n/locales/ja/chat.json +++ b/webview-ui/src/i18n/locales/ja/chat.json @@ -175,7 +175,11 @@ "wantsToInsertAtEnd": "Rooはこのファイルの末尾にコンテンツを追加したい:", "wantsToReadAndXMore": "Roo はこのファイルと他に {{count}} 個のファイルを読み込もうとしています:", "wantsToReadMultiple": "Rooは複数のファイルを読み取ろうとしています:", - "wantsToApplyBatchChanges": "Rooは複数のファイルに変更を適用したい:" + "wantsToApplyBatchChanges": "Rooは複数のファイルに変更を適用したい:", + "wantsToGenerateImage": "Rooは画像を生成したい:", + "wantsToGenerateImageOutsideWorkspace": "Rooはワークスペース外で画像を生成したい:", + "wantsToGenerateImageProtected": "Rooは保護された場所で画像を生成したい:", + "didGenerateImage": "Rooは画像を生成しました:" }, "directoryOperations": { "wantsToViewTopLevel": "Rooはこのディレクトリのトップレベルファイルを表示したい:", diff --git a/webview-ui/src/i18n/locales/ja/common.json b/webview-ui/src/i18n/locales/ja/common.json index a3f6a90a22..a92a3cd79a 100644 --- a/webview-ui/src/i18n/locales/ja/common.json +++ b/webview-ui/src/i18n/locales/ja/common.json @@ -48,6 +48,11 @@ "copyError": "画像のコピーエラー" } }, + "image": { + "tabs": { + "view": "画像" + } + }, "file": { "errors": { "invalidDataUri": "無効なデータURI形式", diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index 7a373ed350..10f28d23d8 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -738,6 +738,17 @@ "NEW_TASK_REQUIRE_TODOS": { "name": "新しいタスクには'todos'リストを必須にする", "description": "有効にすると、new_taskツールはtodosパラメータの提供が必須になります。これにより、すべての新しいタスクが明確な目的のリストで開始されることが保証されます。無効(デフォルト)の場合、下位互換性のためにtodosパラメータはオプションのままです。" + }, + "IMAGE_GENERATION": { + "name": "AI画像生成を有効にする", + "description": "有効にすると、RooはOpenRouterの画像生成モデルを使用してテキストプロンプトから画像を生成できます。OpenRouter APIキーの設定が必要です。", + "openRouterApiKeyLabel": "OpenRouter APIキー", + "openRouterApiKeyPlaceholder": "OpenRouter APIキーを入力してください", + "getApiKeyText": "APIキーを取得する場所", + "modelSelectionLabel": "画像生成モデル", + "modelSelectionDescription": "画像生成に使用するモデルを選択", + "warningMissingKey": "⚠️ 画像生成にはOpenRouter APIキーが必要です。上記で設定してください。", + "successConfigured": "✓ 画像生成が設定され、使用準備完了です" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/ko/chat.json b/webview-ui/src/i18n/locales/ko/chat.json index 3fbde1f353..927cdfe5cf 100644 --- a/webview-ui/src/i18n/locales/ko/chat.json +++ b/webview-ui/src/i18n/locales/ko/chat.json @@ -175,7 +175,11 @@ "wantsToInsertAtEnd": "Roo가 이 파일의 끝에 내용을 추가하고 싶어합니다:", "wantsToReadAndXMore": "Roo가 이 파일과 {{count}}개의 파일을 더 읽으려고 합니다:", "wantsToReadMultiple": "Roo가 여러 파일을 읽으려고 합니다:", - "wantsToApplyBatchChanges": "Roo가 여러 파일에 변경 사항을 적용하고 싶어합니다:" + "wantsToApplyBatchChanges": "Roo가 여러 파일에 변경 사항을 적용하고 싶어합니다:", + "wantsToGenerateImage": "Roo가 이미지를 생성하고 싶어합니다:", + "wantsToGenerateImageOutsideWorkspace": "Roo가 워크스페이스 외부에서 이미지를 생성하고 싶어합니다:", + "wantsToGenerateImageProtected": "Roo가 보호된 위치에서 이미지를 생성하고 싶어합니다:", + "didGenerateImage": "Roo가 이미지를 생성했습니다:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo가 이 디렉토리의 최상위 파일을 보고 싶어합니다:", diff --git a/webview-ui/src/i18n/locales/ko/common.json b/webview-ui/src/i18n/locales/ko/common.json index 83d56930df..e8a9b7c64b 100644 --- a/webview-ui/src/i18n/locales/ko/common.json +++ b/webview-ui/src/i18n/locales/ko/common.json @@ -48,6 +48,11 @@ "copyError": "이미지 복사 오류" } }, + "image": { + "tabs": { + "view": "이미지" + } + }, "file": { "errors": { "invalidDataUri": "잘못된 데이터 URI 형식", diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index b9bbf84d51..65c6da29c4 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -738,6 +738,17 @@ "NEW_TASK_REQUIRE_TODOS": { "name": "새 작업에 'todos' 목록 필요", "description": "활성화하면 new_task 도구에 todos 매개변수를 제공해야 합니다. 이렇게 하면 모든 새 작업이 명확한 목표 목록으로 시작됩니다. 비활성화하면(기본값) 이전 버전과의 호환성을 위해 todos 매개변수는 선택 사항으로 유지됩니다." + }, + "IMAGE_GENERATION": { + "name": "AI 이미지 생성 활성화", + "description": "활성화하면 Roo는 OpenRouter의 이미지 생성 모델을 사용하여 텍스트 프롬프트에서 이미지를 생성할 수 있습니다. OpenRouter API 키 구성이 필요합니다.", + "openRouterApiKeyLabel": "OpenRouter API 키", + "openRouterApiKeyPlaceholder": "OpenRouter API 키를 입력하세요", + "getApiKeyText": "API 키를 받을 곳", + "modelSelectionLabel": "이미지 생성 모델", + "modelSelectionDescription": "이미지 생성에 사용할 모델을 선택하세요", + "warningMissingKey": "⚠️ 이미지 생성에는 OpenRouter API 키가 필요합니다. 위에서 설정해주세요.", + "successConfigured": "✓ 이미지 생성이 구성되었으며 사용할 준비가 되었습니다" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/nl/chat.json b/webview-ui/src/i18n/locales/nl/chat.json index a53f148bf2..48f9f345f9 100644 --- a/webview-ui/src/i18n/locales/nl/chat.json +++ b/webview-ui/src/i18n/locales/nl/chat.json @@ -170,7 +170,11 @@ "wantsToInsertAtEnd": "Roo wil inhoud toevoegen aan het einde van dit bestand:", "wantsToReadAndXMore": "Roo wil dit bestand en nog {{count}} andere lezen:", "wantsToReadMultiple": "Roo wil meerdere bestanden lezen:", - "wantsToApplyBatchChanges": "Roo wil wijzigingen toepassen op meerdere bestanden:" + "wantsToApplyBatchChanges": "Roo wil wijzigingen toepassen op meerdere bestanden:", + "wantsToGenerateImage": "Roo wil een afbeelding genereren:", + "wantsToGenerateImageOutsideWorkspace": "Roo wil een afbeelding genereren buiten de werkruimte:", + "wantsToGenerateImageProtected": "Roo wil een afbeelding genereren op een beschermde locatie:", + "didGenerateImage": "Roo heeft een afbeelding gegenereerd:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo wil de bovenliggende bestanden in deze map bekijken:", diff --git a/webview-ui/src/i18n/locales/nl/common.json b/webview-ui/src/i18n/locales/nl/common.json index d81570c705..12a6c74365 100644 --- a/webview-ui/src/i18n/locales/nl/common.json +++ b/webview-ui/src/i18n/locales/nl/common.json @@ -48,6 +48,11 @@ "copyError": "Fout bij kopiëren van afbeelding" } }, + "image": { + "tabs": { + "view": "Afbeelding" + } + }, "file": { "errors": { "invalidDataUri": "Ongeldig data-URI-formaat", diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index 169fc86b03..292796b126 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -738,6 +738,17 @@ "NEW_TASK_REQUIRE_TODOS": { "name": "'todos'-lijst vereisen voor nieuwe taken", "description": "Wanneer ingeschakeld, vereist de new_task-tool dat een todos-parameter wordt opgegeven. Dit zorgt ervoor dat alle nieuwe taken beginnen met een duidelijke lijst met doelstellingen. Wanneer uitgeschakeld (standaard), blijft de todos-parameter optioneel voor achterwaartse compatibiliteit." + }, + "IMAGE_GENERATION": { + "name": "AI-afbeeldingsgeneratie inschakelen", + "description": "Wanneer ingeschakeld, kan Roo afbeeldingen genereren van tekstprompts met behulp van OpenRouter's afbeeldingsgeneratiemodellen. Vereist een geconfigureerde OpenRouter API-sleutel.", + "openRouterApiKeyLabel": "OpenRouter API-sleutel", + "openRouterApiKeyPlaceholder": "Voer je OpenRouter API-sleutel in", + "getApiKeyText": "Haal je API-sleutel op van", + "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" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/pl/chat.json b/webview-ui/src/i18n/locales/pl/chat.json index 391aa7b319..f587406fa5 100644 --- a/webview-ui/src/i18n/locales/pl/chat.json +++ b/webview-ui/src/i18n/locales/pl/chat.json @@ -175,7 +175,11 @@ "wantsToInsertAtEnd": "Roo chce dodać zawartość na końcu tego pliku:", "wantsToReadAndXMore": "Roo chce przeczytać ten plik i {{count}} więcej:", "wantsToReadMultiple": "Roo chce odczytać wiele plików:", - "wantsToApplyBatchChanges": "Roo chce zastosować zmiany do wielu plików:" + "wantsToApplyBatchChanges": "Roo chce zastosować zmiany do wielu plików:", + "wantsToGenerateImage": "Roo chce wygenerować obraz:", + "wantsToGenerateImageOutsideWorkspace": "Roo chce wygenerować obraz poza obszarem roboczym:", + "wantsToGenerateImageProtected": "Roo chce wygenerować obraz w chronionym miejscu:", + "didGenerateImage": "Roo wygenerował obraz:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo chce zobaczyć pliki najwyższego poziomu w tym katalogu:", diff --git a/webview-ui/src/i18n/locales/pl/common.json b/webview-ui/src/i18n/locales/pl/common.json index 77679ef7c5..410c8dbb9c 100644 --- a/webview-ui/src/i18n/locales/pl/common.json +++ b/webview-ui/src/i18n/locales/pl/common.json @@ -48,6 +48,11 @@ "copyError": "Błąd kopiowania obrazu" } }, + "image": { + "tabs": { + "view": "Obraz" + } + }, "file": { "errors": { "invalidDataUri": "Nieprawidłowy format URI danych", diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index d8d21c5170..c6dbf21e43 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -738,6 +738,17 @@ "NEW_TASK_REQUIRE_TODOS": { "name": "Wymagaj listy 'todos' dla nowych zadań", "description": "Gdy włączone, narzędzie new_task będzie wymagało podania parametru todos. Zapewnia to, że wszystkie nowe zadania rozpoczynają się od jasnej listy celów. Gdy wyłączone (domyślnie), parametr todos pozostaje opcjonalny dla zachowania kompatybilności wstecznej." + }, + "IMAGE_GENERATION": { + "name": "Włącz generowanie obrazów AI", + "description": "Gdy włączone, Roo może generować obrazy z promptów tekstowych używając modeli generowania obrazów OpenRouter. Wymaga skonfigurowanego klucza API OpenRouter.", + "openRouterApiKeyLabel": "Klucz API OpenRouter", + "openRouterApiKeyPlaceholder": "Wprowadź swój klucz API OpenRouter", + "getApiKeyText": "Uzyskaj swój klucz API od", + "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" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/pt-BR/chat.json b/webview-ui/src/i18n/locales/pt-BR/chat.json index 12fa6932ae..c4ccac7458 100644 --- a/webview-ui/src/i18n/locales/pt-BR/chat.json +++ b/webview-ui/src/i18n/locales/pt-BR/chat.json @@ -175,7 +175,11 @@ "wantsToInsertAtEnd": "Roo quer adicionar conteúdo ao final deste arquivo:", "wantsToReadAndXMore": "Roo quer ler este arquivo e mais {{count}}:", "wantsToReadMultiple": "Roo deseja ler múltiplos arquivos:", - "wantsToApplyBatchChanges": "Roo quer aplicar alterações a múltiplos arquivos:" + "wantsToApplyBatchChanges": "Roo quer aplicar alterações a múltiplos arquivos:", + "wantsToGenerateImage": "Roo quer gerar uma imagem:", + "wantsToGenerateImageOutsideWorkspace": "Roo quer gerar uma imagem fora do espaço de trabalho:", + "wantsToGenerateImageProtected": "Roo quer gerar uma imagem em um local protegido:", + "didGenerateImage": "Roo gerou uma imagem:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo quer visualizar os arquivos de nível superior neste diretório:", diff --git a/webview-ui/src/i18n/locales/pt-BR/common.json b/webview-ui/src/i18n/locales/pt-BR/common.json index 3fb4273d89..30d9b6dc6c 100644 --- a/webview-ui/src/i18n/locales/pt-BR/common.json +++ b/webview-ui/src/i18n/locales/pt-BR/common.json @@ -48,6 +48,11 @@ "copyError": "Erro ao copiar imagem" } }, + "image": { + "tabs": { + "view": "Imagem" + } + }, "file": { "errors": { "invalidDataUri": "Formato de URI de dados inválido", diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index a8d8003847..f7924857dd 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -738,6 +738,17 @@ "NEW_TASK_REQUIRE_TODOS": { "name": "Exigir lista de 'todos' para novas tarefas", "description": "Quando ativado, a ferramenta new_task exigirá que um parâmetro todos seja fornecido. Isso garante que todas as novas tarefas comecem com uma lista clara de objetivos. Quando desativado (padrão), o parâmetro todos permanece opcional para compatibilidade com versões anteriores." + }, + "IMAGE_GENERATION": { + "name": "Habilitar geração de imagens com IA", + "description": "Quando habilitado, Roo pode gerar imagens a partir de prompts de texto usando os modelos de geração de imagens do OpenRouter. Requer uma chave de API do OpenRouter configurada.", + "openRouterApiKeyLabel": "Chave de API do OpenRouter", + "openRouterApiKeyPlaceholder": "Digite sua chave de API do OpenRouter", + "getApiKeyText": "Obtenha sua chave de API de", + "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" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/ru/chat.json b/webview-ui/src/i18n/locales/ru/chat.json index 0288c2bc94..9fefd4f380 100644 --- a/webview-ui/src/i18n/locales/ru/chat.json +++ b/webview-ui/src/i18n/locales/ru/chat.json @@ -170,7 +170,11 @@ "wantsToInsertAtEnd": "Roo хочет добавить содержимое в конец этого файла:", "wantsToReadAndXMore": "Roo хочет прочитать этот файл и еще {{count}}:", "wantsToReadMultiple": "Roo хочет прочитать несколько файлов:", - "wantsToApplyBatchChanges": "Roo хочет применить изменения к нескольким файлам:" + "wantsToApplyBatchChanges": "Roo хочет применить изменения к нескольким файлам:", + "wantsToGenerateImage": "Roo хочет сгенерировать изображение:", + "wantsToGenerateImageOutsideWorkspace": "Roo хочет сгенерировать изображение вне рабочего пространства:", + "wantsToGenerateImageProtected": "Roo хочет сгенерировать изображение в защищённом месте:", + "didGenerateImage": "Roo сгенерировал изображение:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo хочет просмотреть файлы верхнего уровня в этой директории:", diff --git a/webview-ui/src/i18n/locales/ru/common.json b/webview-ui/src/i18n/locales/ru/common.json index f43c2e9e8b..8cdb1431eb 100644 --- a/webview-ui/src/i18n/locales/ru/common.json +++ b/webview-ui/src/i18n/locales/ru/common.json @@ -48,6 +48,11 @@ "copyError": "Ошибка копирования изображения" } }, + "image": { + "tabs": { + "view": "Изображение" + } + }, "file": { "errors": { "invalidDataUri": "Неверный формат URI данных", diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index 187904d735..15ef86e37c 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -738,6 +738,17 @@ "NEW_TASK_REQUIRE_TODOS": { "name": "Требовать список 'todos' для новых задач", "description": "Если включено, инструмент new_task будет требовать предоставления параметра todos. Это гарантирует, что все новые задачи начинаются с четкого списка целей. Когда отключено (по умолчанию), параметр todos остается необязательным для обратной совместимости." + }, + "IMAGE_GENERATION": { + "name": "Включить генерацию изображений ИИ", + "description": "Когда включено, Roo может генерировать изображения из текстовых запросов, используя модели генерации изображений OpenRouter. Требует настроенный API-ключ OpenRouter.", + "openRouterApiKeyLabel": "API-ключ OpenRouter", + "openRouterApiKeyPlaceholder": "Введите ваш API-ключ OpenRouter", + "getApiKeyText": "Получите ваш API-ключ от", + "modelSelectionLabel": "Модель генерации изображений", + "modelSelectionDescription": "Выберите модель для генерации изображений", + "warningMissingKey": "⚠️ API-ключ OpenRouter необходим для генерации изображений. Настройте его выше.", + "successConfigured": "✓ Генерация изображений настроена и готова к использованию" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/tr/chat.json b/webview-ui/src/i18n/locales/tr/chat.json index c8dea0d545..12757edab6 100644 --- a/webview-ui/src/i18n/locales/tr/chat.json +++ b/webview-ui/src/i18n/locales/tr/chat.json @@ -175,7 +175,11 @@ "wantsToInsertAtEnd": "Roo bu dosyanın sonuna içerik eklemek istiyor:", "wantsToReadAndXMore": "Roo bu dosyayı ve {{count}} tane daha okumak istiyor:", "wantsToReadMultiple": "Roo birden fazla dosya okumak istiyor:", - "wantsToApplyBatchChanges": "Roo birden fazla dosyaya değişiklik uygulamak istiyor:" + "wantsToApplyBatchChanges": "Roo birden fazla dosyaya değişiklik uygulamak istiyor:", + "wantsToGenerateImage": "Roo bir görsel oluşturmak istiyor:", + "wantsToGenerateImageOutsideWorkspace": "Roo çalışma alanının dışında bir görsel oluşturmak istiyor:", + "wantsToGenerateImageProtected": "Roo korumalı bir konumda görsel oluşturmak istiyor:", + "didGenerateImage": "Roo bir görsel oluşturdu:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo bu dizindeki üst düzey dosyaları görüntülemek istiyor:", diff --git a/webview-ui/src/i18n/locales/tr/common.json b/webview-ui/src/i18n/locales/tr/common.json index 2f3a7c957c..15f13fcdd3 100644 --- a/webview-ui/src/i18n/locales/tr/common.json +++ b/webview-ui/src/i18n/locales/tr/common.json @@ -48,6 +48,11 @@ "copyError": "Görsel kopyalama hatası" } }, + "image": { + "tabs": { + "view": "Resim" + } + }, "file": { "errors": { "invalidDataUri": "Geçersiz veri URI formatı", diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index f0f21a40cb..a48ce0517b 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -738,6 +738,17 @@ "NEW_TASK_REQUIRE_TODOS": { "name": "Yeni görevler için 'todos' listesi gerektir", "description": "Etkinleştirildiğinde, new_task aracı bir todos parametresi sağlanmasını gerektirir. Bu, tüm yeni görevlerin net bir hedef listesiyle başlamasını sağlar. Devre dışı bırakıldığında (varsayılan), todos parametresi geriye dönük uyumluluk için isteğe bağlı kalır." + }, + "IMAGE_GENERATION": { + "name": "AI görüntü üretimini etkinleştir", + "description": "Etkinleştirildiğinde, Roo OpenRouter'ın görüntü üretim modellerini kullanarak metin istemlerinden görüntüler üretebilir. Yapılandırılmış bir OpenRouter API anahtarı gerektirir.", + "openRouterApiKeyLabel": "OpenRouter API Anahtarı", + "openRouterApiKeyPlaceholder": "OpenRouter API anahtarınızı girin", + "getApiKeyText": "API anahtarınızı alın", + "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" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/vi/chat.json b/webview-ui/src/i18n/locales/vi/chat.json index c4d56a71af..2d0135d9ac 100644 --- a/webview-ui/src/i18n/locales/vi/chat.json +++ b/webview-ui/src/i18n/locales/vi/chat.json @@ -175,7 +175,11 @@ "wantsToInsertAtEnd": "Roo muốn thêm nội dung vào cuối tệp này:", "wantsToReadAndXMore": "Roo muốn đọc tệp này và {{count}} tệp khác:", "wantsToReadMultiple": "Roo muốn đọc nhiều tệp:", - "wantsToApplyBatchChanges": "Roo muốn áp dụng thay đổi cho nhiều tệp:" + "wantsToApplyBatchChanges": "Roo muốn áp dụng thay đổi cho nhiều tệp:", + "wantsToGenerateImage": "Roo muốn tạo một hình ảnh:", + "wantsToGenerateImageOutsideWorkspace": "Roo muốn tạo hình ảnh bên ngoài không gian làm việc:", + "wantsToGenerateImageProtected": "Roo muốn tạo hình ảnh ở vị trí được bảo vệ:", + "didGenerateImage": "Roo đã tạo một hình ảnh:" }, "directoryOperations": { "wantsToViewTopLevel": "Roo muốn xem các tệp cấp cao nhất trong thư mục này:", diff --git a/webview-ui/src/i18n/locales/vi/common.json b/webview-ui/src/i18n/locales/vi/common.json index 92aa029b01..a75e1e1f4a 100644 --- a/webview-ui/src/i18n/locales/vi/common.json +++ b/webview-ui/src/i18n/locales/vi/common.json @@ -48,6 +48,11 @@ "copyError": "Lỗi sao chép hình ảnh" } }, + "image": { + "tabs": { + "view": "Hình ảnh" + } + }, "file": { "errors": { "invalidDataUri": "Định dạng URI dữ liệu không hợp lệ", diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index 2630ea4463..2d3675c1ad 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -738,6 +738,17 @@ "NEW_TASK_REQUIRE_TODOS": { "name": "Yêu cầu danh sách 'todos' cho các nhiệm vụ mới", "description": "Khi được bật, công cụ new_task sẽ yêu cầu cung cấp tham số todos. Điều này đảm bảo tất cả các nhiệm vụ mới bắt đầu với một danh sách mục tiêu rõ ràng. Khi bị tắt (mặc định), tham số todos vẫn là tùy chọn để tương thích ngược." + }, + "IMAGE_GENERATION": { + "name": "Bật tạo hình ảnh AI", + "description": "Khi được bật, Roo có thể tạo hình ảnh từ lời nhắc văn bản bằng các mô hình tạo hình ảnh của OpenRouter. Yêu cầu khóa API OpenRouter được cấu hình.", + "openRouterApiKeyLabel": "Khóa API OpenRouter", + "openRouterApiKeyPlaceholder": "Nhập khóa API OpenRouter của bạn", + "getApiKeyText": "Lấy khóa API của bạn từ", + "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" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/zh-CN/chat.json b/webview-ui/src/i18n/locales/zh-CN/chat.json index ac95f89209..a8491a63f6 100644 --- a/webview-ui/src/i18n/locales/zh-CN/chat.json +++ b/webview-ui/src/i18n/locales/zh-CN/chat.json @@ -175,7 +175,11 @@ "wantsToInsertAtEnd": "需要在文件末尾添加内容:", "wantsToReadAndXMore": "Roo 想读取此文件以及另外 {{count}} 个文件:", "wantsToReadMultiple": "Roo 想要读取多个文件:", - "wantsToApplyBatchChanges": "Roo 想要对多个文件应用更改:" + "wantsToApplyBatchChanges": "Roo 想要对多个文件应用更改:", + "wantsToGenerateImage": "需要生成图片:", + "wantsToGenerateImageOutsideWorkspace": "需要在工作区外生成图片:", + "wantsToGenerateImageProtected": "需要在受保护位置生成图片:", + "didGenerateImage": "已生成图片:" }, "directoryOperations": { "wantsToViewTopLevel": "需要查看目录文件列表:", diff --git a/webview-ui/src/i18n/locales/zh-CN/common.json b/webview-ui/src/i18n/locales/zh-CN/common.json index 6ff0132370..902bd7f7e0 100644 --- a/webview-ui/src/i18n/locales/zh-CN/common.json +++ b/webview-ui/src/i18n/locales/zh-CN/common.json @@ -48,6 +48,11 @@ "copyError": "复制图片时出错" } }, + "image": { + "tabs": { + "view": "图像" + } + }, "file": { "errors": { "invalidDataUri": "无效的数据 URI 格式", diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index 9bb7e974b2..be47c4ac60 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -738,6 +738,17 @@ "NEW_TASK_REQUIRE_TODOS": { "name": "要求新任务提供 'todos' 列表", "description": "启用后,new_task 工具将需要提供 todos 参数。这可以确保所有新任务都以明确的目标列表开始。禁用时(默认),todos 参数保持可选,以实现向后兼容。" + }, + "IMAGE_GENERATION": { + "name": "启用 AI 图像生成", + "description": "启用后,Roo 可以使用 OpenRouter 的图像生成模型从文本提示生成图像。需要配置 OpenRouter API 密钥。", + "openRouterApiKeyLabel": "OpenRouter API 密钥", + "openRouterApiKeyPlaceholder": "输入您的 OpenRouter API 密钥", + "getApiKeyText": "获取您的 API 密钥", + "modelSelectionLabel": "图像生成模型", + "modelSelectionDescription": "选择用于图像生成的模型", + "warningMissingKey": "⚠️ 图像生成需要 OpenRouter API 密钥。请在上方配置。", + "successConfigured": "✓ 图像生成已配置完成,可以使用" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/zh-TW/chat.json b/webview-ui/src/i18n/locales/zh-TW/chat.json index 8df11705d4..c01142cdfa 100644 --- a/webview-ui/src/i18n/locales/zh-TW/chat.json +++ b/webview-ui/src/i18n/locales/zh-TW/chat.json @@ -182,6 +182,10 @@ "wantsToEditOutsideWorkspace": "Roo 想要編輯此工作區外的檔案:", "wantsToEditProtected": "Roo 想要編輯受保護的設定檔案:", "wantsToApplyBatchChanges": "Roo 想要對多個檔案套用變更:", + "wantsToGenerateImage": "Roo 想要產生圖片:", + "wantsToGenerateImageOutsideWorkspace": "Roo 想要在工作區外產生圖片:", + "wantsToGenerateImageProtected": "Roo 想要在受保護位置產生圖片:", + "didGenerateImage": "Roo 已產生圖片:", "wantsToCreate": "Roo 想要建立新檔案:", "wantsToSearchReplace": "Roo 想要在此檔案中搜尋和取代:", "didSearchReplace": "Roo 已在此檔案執行搜尋和取代:", diff --git a/webview-ui/src/i18n/locales/zh-TW/common.json b/webview-ui/src/i18n/locales/zh-TW/common.json index 9750590462..9497d369a5 100644 --- a/webview-ui/src/i18n/locales/zh-TW/common.json +++ b/webview-ui/src/i18n/locales/zh-TW/common.json @@ -48,6 +48,11 @@ "copyError": "複製圖片時發生錯誤" } }, + "image": { + "tabs": { + "view": "圖像" + } + }, "file": { "errors": { "invalidDataUri": "無效的資料 URI 格式", diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index 11dfef268f..ad3339dcde 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -738,6 +738,17 @@ "NEW_TASK_REQUIRE_TODOS": { "name": "要求新工作提供 'todos' 列表", "description": "啟用後,new_task 工具將需要提供 todos 參數。這可以確保所有新工作都以明確的目標列表開始。停用時(預設),todos 參數保持可選,以實現向後相容。" + }, + "IMAGE_GENERATION": { + "name": "啟用 AI 圖像生成", + "description": "啟用後,Roo 可以使用 OpenRouter 的圖像生成模型從文字提示生成圖像。需要設定 OpenRouter API 金鑰。", + "openRouterApiKeyLabel": "OpenRouter API 金鑰", + "openRouterApiKeyPlaceholder": "輸入您的 OpenRouter API 金鑰", + "getApiKeyText": "取得您的 API 金鑰從", + "modelSelectionLabel": "圖像生成模型", + "modelSelectionDescription": "選擇用於圖像生成的模型", + "warningMissingKey": "⚠️ 圖像生成需要 OpenRouter API 金鑰。請在上方設定。", + "successConfigured": "✓ 圖像生成已設定完成並準備使用" } }, "promptCaching": {