feat: add image generation tool with OpenRouter integration (#7474)

Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com>
Co-authored-by: cte <cestreich@gmail.com>
This commit is contained in:
Daniel 2025-08-28 00:57:48 -05:00 committed by Shawn
parent 5150dd96b8
commit ef88be25fc
77 changed files with 1216 additions and 30 deletions

View file

@ -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",

View file

@ -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<typeof experimentsSchema>

View file

@ -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({

View file

@ -34,6 +34,7 @@ export const toolNames = [
"fetch_instructions",
"codebase_search",
"update_todo_list",
"generate_image",
] as const
export const toolNamesSchema = z.enum(toolNames)

18
pnpm-lock.yaml generated
View file

@ -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

View file

@ -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<ImageGenerationResult> {
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",
}
}
}
}

View file

@ -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

View file

@ -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:
<generate_image>
<prompt>Your image description here</prompt>
<path>path/to/save/image.png</path>
</generate_image>
Example: Requesting to generate a sunset image
<generate_image>
<prompt>A beautiful sunset over mountains with vibrant orange and purple colors</prompt>
<path>images/sunset.png</path>
</generate_image>`
}

View file

@ -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, (args: ToolArgs) => 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,
}

View file

@ -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
}
}

View file

@ -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:^",

View file

@ -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.

View file

@ -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)
})

View file

@ -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<string, ExperimentId>
type _AssertExperimentIds = AssertEqual<Equals<ExperimentId, Values<typeof EXPERIMENT_IDS>>>
@ -18,6 +19,7 @@ export const experimentConfigsMap: Record<ExperimentKey, ExperimentConfig> = {
MULTI_FILE_APPLY_DIFF: { enabled: false },
POWER_STEERING: { enabled: false },
PREVENT_FOCUS_DISRUPTION: { enabled: false },
IMAGE_GENERATION: { enabled: false },
}
export const experimentDefault = Object.fromEntries(

View file

@ -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<Pick<Record<ToolParamName, string>, "use_regex" | "ignore_case" | "start_line" | "end_line">>
}
export interface GenerateImageToolUse extends ToolUse {
name: "generate_image"
params: Partial<Pick<Record<ToolParamName, string>, "prompt" | "path">>
}
// Define tool group configuration
export type ToolGroupConfig = {
tools: readonly string[]
@ -190,6 +196,7 @@ export const TOOL_DISPLAY_NAMES: Record<ToolName, string> = {
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<ToolGroup, ToolGroupConfig> = {
],
},
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"],

View file

@ -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 = ({
</div>
</>
)
case "generateImage":
return (
<>
<div style={headerStyle}>
{tool.isProtected ? (
<span
className="codicon codicon-lock"
style={{ color: "var(--vscode-editorWarning-foreground)", marginBottom: "-1.5px" }}
/>
) : (
toolIcon("file-media")
)}
<span style={{ fontWeight: "bold" }}>
{message.type === "ask"
? tool.isProtected
? t("chat:fileOperations.wantsToGenerateImageProtected")
: tool.isOutsideWorkspace
? t("chat:fileOperations.wantsToGenerateImageOutsideWorkspace")
: t("chat:fileOperations.wantsToGenerateImage")
: t("chat:fileOperations.didGenerateImage")}
</span>
</div>
{message.type === "ask" && (
<CodeAccordian
path={tool.path}
code={tool.content}
language="text"
isExpanded={isExpanded}
onToggleExpand={handleToggleExpand}
/>
)}
</>
)
default:
return null
}
@ -1002,6 +1036,13 @@ export const ChatRowContent = ({
return (
<div>
<Markdown markdown={message.text} partial={message.partial} />
{message.images && message.images.length > 0 && (
<div style={{ marginTop: "10px" }}>
{message.images.map((image, index) => (
<ImageBlock key={index} imageData={image} />
))}
</div>
)}
</div>
)
case "user_feedback":

View file

@ -325,6 +325,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
case "appliedDiff":
case "newFileCreated":
case "insertContent":
case "generateImage":
setPrimaryButtonText(t("chat:save.title"))
setSecondaryButtonText(t("chat:reject.title"))
break
@ -1050,6 +1051,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
"newFileCreated",
"searchAndReplace",
"insertContent",
"generateImage",
].includes(tool.tool)
}

View file

@ -0,0 +1,15 @@
import React from "react"
import { ImageViewer } from "./ImageViewer"
interface ImageBlockProps {
imageData: string
path?: string
}
export default function ImageBlock({ imageData, path }: ImageBlockProps) {
return (
<div className="my-2">
<ImageViewer imageData={imageData} path={path} alt="AI Generated Image" showControls={true} />
</div>
)
}

View file

@ -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 (
<>
<div
className={`relative w-full ${className}`}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}>
<img
src={imageData}
alt={alt}
className="w-full h-auto rounded cursor-pointer"
onClick={handleOpenInEditor}
style={{
maxHeight: "400px",
objectFit: "contain",
backgroundColor: "var(--vscode-editor-background)",
}}
/>
{path && <div className="mt-1 text-xs text-vscode-descriptionForeground">{path}</div>}
{showControls && isHovering && (
<div className="absolute bottom-2 right-2 flex gap-1 bg-vscode-editor-background/90 rounded p-0.5 z-10 opacity-100 transition-opacity duration-200 ease-in-out">
<MermaidActionButtons
onZoom={handleZoom}
onCopy={handleCopy}
onSave={handleSave}
onViewCode={() => {}} // Not applicable for images
copyFeedback={copyFeedback}
/>
</div>
)}
</div>
<Modal isOpen={showModal} onClose={() => setShowModal(false)}>
<div className="flex justify-between items-center border-b border-vscode-editorGroup-border">
<div className="flex gap-0">
<TabButton
icon="file-media"
label={t("common:image.tabs.view")}
isActive={true}
onClick={() => {}}
/>
</div>
<div className="pr-3">
<StandardTooltip content={t("common:mermaid.buttons.close")}>
<IconButton icon="close" onClick={() => setShowModal(false)} />
</StandardTooltip>
</div>
</div>
<div
className="flex-1 p-4 pb-[60px] overflow-auto flex items-center justify-center"
onWheel={handleWheel}>
<div
style={{
transform: `scale(${zoomLevel}) translate(${dragPosition.x}px, ${dragPosition.y}px)`,
transformOrigin: "center center",
transition: isDragging ? "none" : "transform 0.1s ease",
cursor: isDragging ? "grabbing" : "grab",
}}
onMouseDown={(e) => {
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)}>
<img
src={imageData}
alt={alt}
style={{
maxWidth: "90vw",
maxHeight: "80vh",
objectFit: "contain",
}}
/>
</div>
<div className="absolute bottom-4 left-4 bg-vscode-editor-background border border-vscode-editorGroup-border rounded px-2 py-1 text-xs text-vscode-descriptionForeground pointer-events-none opacity-80">
{Math.round(zoomLevel * 100)}%
</div>
</div>
<div className="absolute bottom-0 right-0 left-0 p-3 flex items-center justify-end gap-2 bg-vscode-editor-background border-t border-vscode-editorGroup-border rounded-b">
<ZoomControls
zoomLevel={zoomLevel}
zoomInTitle={t("common:mermaid.buttons.zoomIn")}
zoomOutTitle={t("common:mermaid.buttons.zoomOut")}
useContinuousZoom={true}
adjustZoom={adjustZoom}
zoomInStep={0.2}
zoomOutStep={-0.2}
/>
{path && (
<StandardTooltip content={t("common:mermaid.buttons.copy")}>
<IconButton icon={copyFeedback ? "check" : "copy"} onClick={handleCopy} />
</StandardTooltip>
)}
<StandardTooltip content={t("common:mermaid.buttons.save")}>
<IconButton icon="save" onClick={handleSave} />
</StandardTooltip>
</div>
</Modal>
</>
)
}

View file

@ -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<HTMLDivElement> & {
experiments: Experiments
@ -20,6 +21,8 @@ type ExperimentalSettingsProps = HTMLAttributes<HTMLDivElement> & {
// 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 (
<ImageGenerationSettings
key={config[0]}
enabled={experiments[EXPERIMENT_IDS.IMAGE_GENERATION] ?? false}
onChange={(enabled) =>
setExperimentEnabled(EXPERIMENT_IDS.IMAGE_GENERATION, enabled)
}
apiConfiguration={apiConfiguration}
setApiConfigurationField={setApiConfigurationField}
/>
)
}
return (
<ExperimentalFeature
key={config[0]}

View file

@ -0,0 +1,122 @@
import React, { useState, useEffect } from "react"
import { VSCodeCheckbox, VSCodeTextField, VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"
import { useAppTranslation } from "@/i18n/TranslationContext"
import type { ProviderSettings } from "@roo-code/types"
interface ImageGenerationSettingsProps {
enabled: boolean
onChange: (enabled: boolean) => void
apiConfiguration: ProviderSettings
setApiConfigurationField: <K extends keyof ProviderSettings>(
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 (
<div className="space-y-4">
<div>
<div className="flex items-center gap-2">
<VSCodeCheckbox checked={enabled} onChange={(e: any) => onChange(e.target.checked)}>
<span className="font-medium">{t("settings:experimental.IMAGE_GENERATION.name")}</span>
</VSCodeCheckbox>
</div>
<p className="text-vscode-descriptionForeground text-sm mt-0">
{t("settings:experimental.IMAGE_GENERATION.description")}
</p>
</div>
{enabled && (
<div className="ml-2 space-y-3">
{/* API Key Configuration */}
<div>
<label className="block font-medium mb-1">
{t("settings:experimental.IMAGE_GENERATION.openRouterApiKeyLabel")}
</label>
<VSCodeTextField
value={openRouterApiKey}
onInput={(e: any) => setOpenRouterApiKey(e.target.value)}
placeholder={t("settings:experimental.IMAGE_GENERATION.openRouterApiKeyPlaceholder")}
className="w-full"
type="password"
/>
<p className="text-vscode-descriptionForeground text-xs mt-1">
{t("settings:experimental.IMAGE_GENERATION.getApiKeyText")}{" "}
<a
href="https://openrouter.ai/keys"
target="_blank"
rel="noopener noreferrer"
className="text-vscode-textLink-foreground hover:text-vscode-textLink-activeForeground">
openrouter.ai/keys
</a>
</p>
</div>
{/* Model Selection */}
<div>
<label className="block font-medium mb-1">
{t("settings:experimental.IMAGE_GENERATION.modelSelectionLabel")}
</label>
<VSCodeDropdown
value={selectedModel}
onChange={(e: any) => setSelectedModel(e.target.value)}
className="w-full">
{IMAGE_GENERATION_MODELS.map((model) => (
<VSCodeOption key={model.value} value={model.value}>
{model.label}
</VSCodeOption>
))}
</VSCodeDropdown>
<p className="text-vscode-descriptionForeground text-xs mt-1">
{t("settings:experimental.IMAGE_GENERATION.modelSelectionDescription")}
</p>
</div>
{/* Status Message */}
{enabled && !openRouterApiKey && (
<div className="p-2 bg-vscode-editorWarning-background text-vscode-editorWarning-foreground rounded text-sm">
{t("settings:experimental.IMAGE_GENERATION.warningMissingKey")}
</div>
)}
{enabled && openRouterApiKey && (
<div className="p-2 bg-vscode-editorInfo-background text-vscode-editorInfo-foreground rounded text-sm">
{t("settings:experimental.IMAGE_GENERATION.successConfigured")}
</div>
)}
</div>
)}
</div>
)
}

View file

@ -724,8 +724,10 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
<ExperimentalSettings
setExperimentEnabled={setExperimentEnabled}
experiments={experiments}
filesChangedEnabled={filesChangedEnabled}
setCachedStateField={setCachedStateField as SetCachedStateField<"filesChangedEnabled">}
filesChangedEnabled={filesChangedEnabled}
setCachedStateField={setCachedStateField as SetCachedStateField<"filesChangedEnabled">}
apiConfiguration={apiConfiguration}
setApiConfigurationField={setApiConfigurationField}
/>
)}

View file

@ -231,6 +231,7 @@ describe("mergeExtensionState", () => {
multiFileApplyDiff: true,
preventFocusDisruption: false,
newTaskRequireTodos: false,
imageGeneration: false,
} as Record<ExperimentId, boolean>,
}
@ -249,6 +250,7 @@ describe("mergeExtensionState", () => {
multiFileApplyDiff: true,
preventFocusDisruption: false,
newTaskRequireTodos: false,
imageGeneration: false,
})
})
})

View file

@ -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:",

View file

@ -48,6 +48,11 @@
"copyError": "Error copiant la imatge"
}
},
"image": {
"tabs": {
"view": "Imatge"
}
},
"file": {
"errors": {
"invalidDataUri": "Format d'URI de dades no vàlid",

View file

@ -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": {

View file

@ -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:",

View file

@ -48,6 +48,11 @@
"copyError": "Fehler beim Kopieren des Bildes"
}
},
"image": {
"tabs": {
"view": "Bild"
}
},
"file": {
"errors": {
"invalidDataUri": "Ungültiges Daten-URI-Format",

View file

@ -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": {

View file

@ -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:",

View file

@ -48,6 +48,11 @@
"copyError": "Error copying image"
}
},
"image": {
"tabs": {
"view": "Image"
}
},
"file": {
"errors": {
"invalidDataUri": "Invalid data URI format",

View file

@ -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": {

View file

@ -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:",

View file

@ -48,6 +48,11 @@
"copyError": "Error copiando la imagen"
}
},
"image": {
"tabs": {
"view": "Imagen"
}
},
"file": {
"errors": {
"invalidDataUri": "Formato de URI de datos inválido",

View file

@ -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": {

View file

@ -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"

View file

@ -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",

View file

@ -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": {

View file

@ -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 इस निर्देशिका में शीर्ष स्तर की फ़ाइलें देखना चाहता है:",

View file

@ -48,6 +48,11 @@
"copyError": "इमेज कॉपी करने में त्रुटि"
}
},
"image": {
"tabs": {
"view": "चित्र"
}
},
"file": {
"errors": {
"invalidDataUri": "अमान्य डेटा URI फॉर्मेट",

View file

@ -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": {

View file

@ -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:",

View file

@ -48,6 +48,11 @@
"copyError": "Error menyalin gambar"
}
},
"image": {
"tabs": {
"view": "Gambar"
}
},
"file": {
"errors": {
"invalidDataUri": "Format data URI tidak valid",

View file

@ -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": {

View file

@ -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:",

View file

@ -48,6 +48,11 @@
"copyError": "Errore nella copia dell'immagine"
}
},
"image": {
"tabs": {
"view": "Immagine"
}
},
"file": {
"errors": {
"invalidDataUri": "Formato URI dati non valido",

View file

@ -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": {

View file

@ -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はこのディレクトリのトップレベルファイルを表示したい:",

View file

@ -48,6 +48,11 @@
"copyError": "画像のコピーエラー"
}
},
"image": {
"tabs": {
"view": "画像"
}
},
"file": {
"errors": {
"invalidDataUri": "無効なデータURI形式",

View file

@ -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": {

View file

@ -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가 이 디렉토리의 최상위 파일을 보고 싶어합니다:",

View file

@ -48,6 +48,11 @@
"copyError": "이미지 복사 오류"
}
},
"image": {
"tabs": {
"view": "이미지"
}
},
"file": {
"errors": {
"invalidDataUri": "잘못된 데이터 URI 형식",

View file

@ -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": {

View file

@ -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:",

View file

@ -48,6 +48,11 @@
"copyError": "Fout bij kopiëren van afbeelding"
}
},
"image": {
"tabs": {
"view": "Afbeelding"
}
},
"file": {
"errors": {
"invalidDataUri": "Ongeldig data-URI-formaat",

View file

@ -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": {

View file

@ -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:",

View file

@ -48,6 +48,11 @@
"copyError": "Błąd kopiowania obrazu"
}
},
"image": {
"tabs": {
"view": "Obraz"
}
},
"file": {
"errors": {
"invalidDataUri": "Nieprawidłowy format URI danych",

View file

@ -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": {

View file

@ -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:",

View file

@ -48,6 +48,11 @@
"copyError": "Erro ao copiar imagem"
}
},
"image": {
"tabs": {
"view": "Imagem"
}
},
"file": {
"errors": {
"invalidDataUri": "Formato de URI de dados inválido",

View file

@ -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": {

View file

@ -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 хочет просмотреть файлы верхнего уровня в этой директории:",

View file

@ -48,6 +48,11 @@
"copyError": "Ошибка копирования изображения"
}
},
"image": {
"tabs": {
"view": "Изображение"
}
},
"file": {
"errors": {
"invalidDataUri": "Неверный формат URI данных",

View file

@ -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": {

View file

@ -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:",

View file

@ -48,6 +48,11 @@
"copyError": "Görsel kopyalama hatası"
}
},
"image": {
"tabs": {
"view": "Resim"
}
},
"file": {
"errors": {
"invalidDataUri": "Geçersiz veri URI formatı",

View file

@ -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": {

View file

@ -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:",

View file

@ -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ệ",

View file

@ -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": {

View file

@ -175,7 +175,11 @@
"wantsToInsertAtEnd": "需要在文件末尾添加内容:",
"wantsToReadAndXMore": "Roo 想读取此文件以及另外 {{count}} 个文件:",
"wantsToReadMultiple": "Roo 想要读取多个文件:",
"wantsToApplyBatchChanges": "Roo 想要对多个文件应用更改:"
"wantsToApplyBatchChanges": "Roo 想要对多个文件应用更改:",
"wantsToGenerateImage": "需要生成图片:",
"wantsToGenerateImageOutsideWorkspace": "需要在工作区外生成图片:",
"wantsToGenerateImageProtected": "需要在受保护位置生成图片:",
"didGenerateImage": "已生成图片:"
},
"directoryOperations": {
"wantsToViewTopLevel": "需要查看目录文件列表:",

View file

@ -48,6 +48,11 @@
"copyError": "复制图片时出错"
}
},
"image": {
"tabs": {
"view": "图像"
}
},
"file": {
"errors": {
"invalidDataUri": "无效的数据 URI 格式",

View file

@ -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": {

View file

@ -182,6 +182,10 @@
"wantsToEditOutsideWorkspace": "Roo 想要編輯此工作區外的檔案:",
"wantsToEditProtected": "Roo 想要編輯受保護的設定檔案:",
"wantsToApplyBatchChanges": "Roo 想要對多個檔案套用變更:",
"wantsToGenerateImage": "Roo 想要產生圖片:",
"wantsToGenerateImageOutsideWorkspace": "Roo 想要在工作區外產生圖片:",
"wantsToGenerateImageProtected": "Roo 想要在受保護位置產生圖片:",
"didGenerateImage": "Roo 已產生圖片:",
"wantsToCreate": "Roo 想要建立新檔案:",
"wantsToSearchReplace": "Roo 想要在此檔案中搜尋和取代:",
"didSearchReplace": "Roo 已在此檔案執行搜尋和取代:",

View file

@ -48,6 +48,11 @@
"copyError": "複製圖片時發生錯誤"
}
},
"image": {
"tabs": {
"view": "圖像"
}
},
"file": {
"errors": {
"invalidDataUri": "無效的資料 URI 格式",

View file

@ -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": {