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 Hannes Rudolph
parent 3eb9d12534
commit ea51c04483
58 changed files with 197 additions and 1274 deletions

View file

@ -1,6 +1,6 @@
{
"name": "@roo-code/types",
"version": "1.74.0",
"version": "1.63.0",
"description": "TypeScript type definitions for Roo Code.",
"publishConfig": {
"access": "public",

View file

@ -11,7 +11,6 @@ export const experimentIds = [
"multiFileApplyDiff",
"preventFocusDisruption",
"imageGeneration",
"runSlashCommand",
] as const
export const experimentIdsSchema = z.enum(experimentIds)
@ -27,7 +26,6 @@ export const experimentsSchema = z.object({
multiFileApplyDiff: z.boolean().optional(),
preventFocusDisruption: z.boolean().optional(),
imageGeneration: z.boolean().optional(),
runSlashCommand: z.boolean().optional(),
})
export type Experiments = z.infer<typeof experimentsSchema>

View file

@ -143,6 +143,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,7 +34,6 @@ export const toolNames = [
"fetch_instructions",
"codebase_search",
"update_todo_list",
"run_slash_command",
"generate_image",
] as const

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

@ -25,7 +25,6 @@ import { getModelEndpoints } from "./fetchers/modelEndpointCache"
import { DEFAULT_HEADERS } from "./constants"
import { BaseProvider } from "./base-provider"
import type { SingleCompletionHandler } from "../index"
import { handleOpenAIError } from "./utils/openai-error-handler"
// Image generation types
interface ImageGenerationResponse {
@ -86,7 +85,6 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH
private client: OpenAI
protected models: ModelRecord = {}
protected endpoints: ModelRecord = {}
private readonly providerName = "OpenRouter"
constructor(options: ApiHandlerOptions) {
super()
@ -163,12 +161,7 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH
...(reasoning && { reasoning }),
}
let stream
try {
stream = await this.client.chat.completions.create(completionParams)
} catch (error) {
throw handleOpenAIError(error, this.providerName)
}
const stream = await this.client.chat.completions.create(completionParams)
let lastUsage: CompletionUsage | undefined = undefined
@ -266,12 +259,7 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH
...(reasoning && { reasoning }),
}
let response
try {
response = await this.client.chat.completions.create(completionParams)
} catch (error) {
throw handleOpenAIError(error, this.providerName)
}
const response = await this.client.chat.completions.create(completionParams)
if ("error" in response) {
const error = response.error as { message?: string; code?: number }
@ -287,15 +275,9 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH
* @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)
* @param inputImage Optional base64 encoded input image data URL
* @returns The generated image data and format, or an error
*/
async generateImage(
prompt: string,
model: string,
apiKey: string,
inputImage?: string,
): Promise<ImageGenerationResult> {
async generateImage(prompt: string, model: string, apiKey: string): Promise<ImageGenerationResult> {
if (!apiKey) {
return {
success: false,
@ -317,20 +299,7 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH
messages: [
{
role: "user",
content: inputImage
? [
{
type: "text",
text: prompt,
},
{
type: "image_url",
image_url: {
url: inputImage,
},
},
]
: prompt,
content: prompt,
},
],
modalities: ["image", "text"],

View file

@ -28,7 +28,6 @@ import { attemptCompletionTool } from "../tools/attemptCompletionTool"
import { newTaskTool } from "../tools/newTaskTool"
import { updateTodoListTool } from "../tools/updateTodoListTool"
import { runSlashCommandTool } from "../tools/runSlashCommandTool"
import { generateImageTool } from "../tools/generateImageTool"
import { formatResponse } from "../prompts/responses"
@ -224,8 +223,6 @@ export async function presentAssistantMessage(cline: Task) {
const modeName = getModeBySlug(mode, customModes)?.name ?? mode
return `[${block.name} in ${modeName} mode: '${message}']`
}
case "run_slash_command":
return `[${block.name} for '${block.params.command}'${block.params.args ? ` with args: ${block.params.args}` : ""}]`
case "generate_image":
return `[${block.name} for '${block.params.path}']`
}
@ -558,9 +555,6 @@ export async function presentAssistantMessage(cline: Task) {
askFinishSubTaskApproval,
)
break
case "run_slash_command":
await runSlashCommandTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag)
break
case "generate_image":
await generateImageTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag)
break

View file

@ -2,35 +2,19 @@ import { ToolArgs } from "./types"
export function getGenerateImageDescription(args: ToolArgs): string {
return `## generate_image
Description: Request to generate or edit an image using AI models through OpenRouter API. This tool can create new images from text prompts or modify existing images based on your instructions. When an input image is provided, the AI will apply the requested edits, transformations, or enhancements to that 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 what to generate or how to edit the image
- path: (required) The file path where the generated/edited image should be saved (relative to the current workspace directory ${args.cwd}). The tool will automatically add the appropriate image extension if not provided.
- image: (optional) The file path to an input image to edit or transform (relative to the current workspace directory ${args.cwd}). Supported formats: PNG, JPG, JPEG, GIF, WEBP.
- 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>
<image>path/to/input/image.jpg</image>
</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>
Example: Editing an existing image
<generate_image>
<prompt>Transform this image into a watercolor painting style</prompt>
<path>images/watercolor-output.png</path>
<image>images/original-photo.jpg</image>
</generate_image>
Example: Upscaling and enhancing an image
<generate_image>
<prompt>Upscale this image to higher resolution, enhance details, improve clarity and sharpness while maintaining the original content and composition</prompt>
<path>images/enhanced-photo.png</path>
<image>images/low-res-photo.jpg</image>
</generate_image>`
}

View file

@ -25,7 +25,6 @@ import { getSwitchModeDescription } from "./switch-mode"
import { getNewTaskDescription } from "./new-task"
import { getCodebaseSearchDescription } from "./codebase-search"
import { getUpdateTodoListDescription } from "./update-todo-list"
import { getRunSlashCommandDescription } from "./run-slash-command"
import { getGenerateImageDescription } from "./generate-image"
import { CodeIndexManager } from "../../../services/code-index/manager"
@ -58,7 +57,6 @@ 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),
run_slash_command: () => getRunSlashCommandDescription(),
generate_image: (args) => getGenerateImageDescription(args),
}
@ -138,11 +136,6 @@ export function getToolDescriptionsForMode(
tools.delete("generate_image")
}
// Conditionally exclude run_slash_command if experiment is not enabled
if (!experiments?.runSlashCommand) {
tools.delete("run_slash_command")
}
// Map tool descriptions for allowed tools
const descriptions = Array.from(tools).map((toolName) => {
const descriptionFn = toolDescriptionMap[toolName]
@ -178,6 +171,5 @@ export {
getInsertContentDescription,
getSearchAndReplaceDescription,
getCodebaseSearchDescription,
getRunSlashCommandDescription,
getGenerateImageDescription,
}

View file

@ -8,10 +8,14 @@ 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", "google/gemini-2.5-flash-image-preview:free"]
const IMAGE_GENERATION_MODELS = [
"google/gemini-2.5-flash-image-preview",
// Add more models as they become available
]
export async function generateImageTool(
cline: Task,
@ -23,7 +27,6 @@ export async function generateImageTool(
) {
const prompt: string | undefined = block.params.prompt
const relPath: string | undefined = block.params.path
const inputImagePath: string | undefined = block.params.image
// Check if the experiment is enabled
const provider = cline.providerRef.deref()
@ -39,7 +42,8 @@ export async function generateImageTool(
return
}
if (block.partial) {
if (block.partial && (!prompt || !relPath)) {
// Wait for complete parameters
return
}
@ -65,71 +69,13 @@ export async function generateImageTool(
return
}
// If input image is provided, validate it exists and can be read
let inputImageData: string | undefined
if (inputImagePath) {
const inputImageFullPath = path.resolve(cline.cwd, inputImagePath)
// Check if input image exists
const inputImageExists = await fileExistsAtPath(inputImageFullPath)
if (!inputImageExists) {
await cline.say("error", `Input image not found: ${getReadablePath(cline.cwd, inputImagePath)}`)
pushToolResult(
formatResponse.toolError(`Input image not found: ${getReadablePath(cline.cwd, inputImagePath)}`),
)
return
}
// Validate input image access permissions
const inputImageAccessAllowed = cline.rooIgnoreController?.validateAccess(inputImagePath)
if (!inputImageAccessAllowed) {
await cline.say("rooignore_error", inputImagePath)
pushToolResult(formatResponse.toolError(formatResponse.rooIgnoreError(inputImagePath)))
return
}
// Read the input image file
try {
const imageBuffer = await fs.readFile(inputImageFullPath)
const imageExtension = path.extname(inputImageFullPath).toLowerCase().replace(".", "")
// Validate image format
const supportedFormats = ["png", "jpg", "jpeg", "gif", "webp"]
if (!supportedFormats.includes(imageExtension)) {
await cline.say(
"error",
`Unsupported image format: ${imageExtension}. Supported formats: ${supportedFormats.join(", ")}`,
)
pushToolResult(
formatResponse.toolError(
`Unsupported image format: ${imageExtension}. Supported formats: ${supportedFormats.join(", ")}`,
),
)
return
}
// Convert to base64 data URL
const mimeType = imageExtension === "jpg" ? "jpeg" : imageExtension
inputImageData = `data:image/${mimeType};base64,${imageBuffer.toString("base64")}`
} catch (error) {
await cline.say(
"error",
`Failed to read input image: ${error instanceof Error ? error.message : "Unknown error"}`,
)
pushToolResult(
formatResponse.toolError(
`Failed to read input image: ${error instanceof Error ? error.message : "Unknown error"}`,
),
)
return
}
}
// Check if file is write-protected
const isWriteProtected = cline.rooProtectedController?.isWriteProtected(relPath) || false
// Get OpenRouter API key from global settings (experimental image generation)
const openRouterApiKey = state?.openRouterImageApiKey
// Get 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(
@ -145,7 +91,7 @@ export async function generateImageTool(
}
// Get selected model from settings or use default
const selectedModel = state?.openRouterImageGenerationSelectedModel || IMAGE_GENERATION_MODELS[0]
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))
@ -167,7 +113,6 @@ export async function generateImageTool(
const approvalMessage = JSON.stringify({
...sharedMessageProps,
content: prompt,
...(inputImagePath && { inputImage: getReadablePath(cline.cwd, inputImagePath) }),
})
const didApprove = await askApproval("tool", approvalMessage, undefined, isWriteProtected)
@ -179,13 +124,8 @@ export async function generateImageTool(
// Create a temporary OpenRouter handler with minimal options
const openRouterHandler = new OpenRouterHandler({} as any)
// Call the generateImage method with the explicit API key and optional input image
const result = await openRouterHandler.generateImage(
prompt,
selectedModel,
openRouterApiKey,
inputImageData,
)
// 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")
@ -236,18 +176,12 @@ export async function generateImageTool(
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")
// Get the webview URI for the image
const provider = cline.providerRef.deref()
const fullImagePath = path.join(cline.cwd, finalPath)
// Convert to webview URI if provider is available
const imageUri = provider?.convertToWebviewUri?.(fullImagePath) ?? vscode.Uri.file(fullImagePath).toString()
// Send the image with the webview URI
await cline.say("image", JSON.stringify({ imageUri, imagePath: fullImagePath }))
pushToolResult(formatResponse.toolResult(getReadablePath(cline.cwd, finalPath)))
return

View file

@ -436,7 +436,7 @@
"@mistralai/mistralai": "^1.9.18",
"@modelcontextprotocol/sdk": "1.12.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

@ -9,13 +9,10 @@ import type {
ClineMessage,
MarketplaceItem,
TodoItem,
CloudUserInfo,
OrganizationAllowList,
ShareVisibility,
QueuedMessage,
ClineSay,
FileChangeset,
} from "@roo-code/types"
import type { CloudUserInfo, OrganizationAllowList, ShareVisibility } from "@roo-code/cloud"
import { GitCommit } from "../utils/git"
@ -141,7 +138,7 @@ export interface ExtensionMessage {
| "historyButtonClicked"
| "promptsButtonClicked"
| "marketplaceButtonClicked"
| "cloudButtonClicked"
| "accountButtonClicked"
| "didBecomeVisible"
| "focusInput"
| "switchTab"
@ -204,10 +201,8 @@ export interface ExtensionMessage {
rulesFolderPath?: string
settings?: any
messageTs?: number
hasCheckpoint?: boolean
context?: string
commands?: Command[]
queuedMessages?: QueuedMessage[]
filesChanged?: FileChangeset // Added filesChanged property
checkpoint?: string // For checkpointCreated and checkpointRestored messages
previousCheckpoint?: string // For checkpoint_created message
@ -235,10 +230,8 @@ export type ExtensionState = Pick<
| "alwaysAllowMcp"
| "alwaysAllowModeSwitch"
| "alwaysAllowSubtasks"
| "alwaysAllowFollowupQuestions"
| "alwaysAllowExecute"
| "alwaysAllowUpdateTodoList"
| "followupAutoApproveTimeoutMs"
| "allowedCommands"
| "deniedCommands"
| "allowedMaxRequests"
@ -247,7 +240,6 @@ export type ExtensionState = Pick<
| "browserViewportSize"
| "screenshotQuality"
| "remoteBrowserEnabled"
| "cachedChromeHostUrl"
| "remoteBrowserHost"
// | "enableCheckpoints" // Optional in GlobalSettings, required here.
| "ttsEnabled"
@ -292,14 +284,12 @@ export type ExtensionState = Pick<
| "includeDiagnosticMessages"
| "maxDiagnosticMessages"
| "remoteControlEnabled"
| "openRouterImageGenerationSelectedModel"
| "includeTaskHistoryInEnhance"
> & {
version: string
clineMessages: ClineMessage[]
currentTaskItem?: HistoryItem
currentTaskTodos?: TodoItem[] // Initial todos for the current task
apiConfiguration: ProviderSettings
apiConfiguration?: ProviderSettings
uriScheme?: string
shouldShowAnnouncement: boolean
@ -347,14 +337,6 @@ export type ExtensionState = Pick<
marketplaceInstalledMetadata?: { project: Record<string, any>; global: Record<string, any> }
profileThresholds: Record<string, number>
hasOpenedModeSelector: boolean
openRouterImageApiKey?: string
openRouterUseMiddleOutTransform?: boolean
messageQueue?: QueuedMessage[]
lastShownAnnouncementId?: string
apiModelId?: string
mcpServers?: McpServer[]
hasSystemPromptOverride?: boolean
mdmCompliant?: boolean
filesChangedEnabled: boolean
}
@ -377,7 +359,6 @@ export interface ClineSayTool {
| "insertContent"
| "generateImage"
| "imageGenerated"
| "runSlashCommand"
path?: string
diff?: string
content?: string
@ -415,11 +396,6 @@ export interface ClineSayTool {
}>
question?: string
imageData?: string // Base64 encoded image data for generated images
// Properties for runSlashCommand tool
command?: string
args?: string
source?: string
description?: string
}
// Must keep in sync with system prompt.

View file

@ -30,7 +30,6 @@ describe("experiments", () => {
multiFileApplyDiff: false,
preventFocusDisruption: false,
imageGeneration: false,
runSlashCommand: false,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
})
@ -41,7 +40,6 @@ describe("experiments", () => {
multiFileApplyDiff: false,
preventFocusDisruption: false,
imageGeneration: false,
runSlashCommand: false,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(true)
})
@ -52,7 +50,6 @@ describe("experiments", () => {
multiFileApplyDiff: false,
preventFocusDisruption: false,
imageGeneration: false,
runSlashCommand: false,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
})

View file

@ -5,7 +5,6 @@ export const EXPERIMENT_IDS = {
POWER_STEERING: "powerSteering",
PREVENT_FOCUS_DISRUPTION: "preventFocusDisruption",
IMAGE_GENERATION: "imageGeneration",
RUN_SLASH_COMMAND: "runSlashCommand",
} as const satisfies Record<string, ExperimentId>
type _AssertExperimentIds = AssertEqual<Equals<ExperimentId, Values<typeof EXPERIMENT_IDS>>>
@ -21,7 +20,6 @@ export const experimentConfigsMap: Record<ExperimentKey, ExperimentConfig> = {
POWER_STEERING: { enabled: false },
PREVENT_FOCUS_DISRUPTION: { enabled: false },
IMAGE_GENERATION: { enabled: false },
RUN_SLASH_COMMAND: { enabled: false },
}
export const experimentDefault = Object.fromEntries(

View file

@ -66,7 +66,6 @@ export const toolParamNames = [
"args",
"todos",
"prompt",
"image",
] as const
export type ToolParamName = (typeof toolParamNames)[number]
@ -160,11 +159,6 @@ export interface NewTaskToolUse extends ToolUse {
params: Partial<Pick<Record<ToolParamName, string>, "mode" | "message" | "todos">>
}
export interface RunSlashCommandToolUse extends ToolUse {
name: "run_slash_command"
params: Partial<Pick<Record<ToolParamName, string>, "command" | "args">>
}
export interface SearchAndReplaceToolUse extends ToolUse {
name: "search_and_replace"
params: Required<Pick<Record<ToolParamName, string>, "path" | "search" | "replace">> &
@ -173,7 +167,7 @@ export interface SearchAndReplaceToolUse extends ToolUse {
export interface GenerateImageToolUse extends ToolUse {
name: "generate_image"
params: Partial<Pick<Record<ToolParamName, string>, "prompt" | "path" | "image">>
params: Partial<Pick<Record<ToolParamName, string>, "prompt" | "path">>
}
// Define tool group configuration
@ -202,7 +196,6 @@ export const TOOL_DISPLAY_NAMES: Record<ToolName, string> = {
search_and_replace: "search and replace",
codebase_search: "codebase search",
update_todo_list: "update todo list",
run_slash_command: "run slash command",
generate_image: "generate images",
} as const
@ -243,7 +236,6 @@ export const ALWAYS_AVAILABLE_TOOLS: ToolName[] = [
"switch_mode",
"new_task",
"update_todo_list",
"run_slash_command",
] as const
export type DiffResult =

View file

@ -5,7 +5,6 @@ import deepEqual from "fast-deep-equal"
import { VSCodeBadge, VSCodeButton } from "@vscode/webview-ui-toolkit/react"
import type { ClineMessage, FollowUpData, SuggestionItem } from "@roo-code/types"
import { Mode } from "@roo/modes"
import { ClineApiReqInfo, ClineAskUseMcpServer, ClineSayTool } from "@roo/ExtensionMessage"
import { COMMAND_OUTPUT_STRING } from "@roo/combineCommandSequences"
@ -42,11 +41,7 @@ import { CommandExecutionError } from "./CommandExecutionError"
import { AutoApprovedRequestLimitWarning } from "./AutoApprovedRequestLimitWarning"
import { CondenseContextErrorRow, CondensingContextRow, ContextCondenseRow } from "./ContextCondenseRow"
import CodebaseSearchResultsDisplay from "./CodebaseSearchResultsDisplay"
import { appendImages } from "@src/utils/imageUtils"
import { McpExecution } from "./McpExecution"
import { ChatTextArea } from "./ChatTextArea"
import { MAX_IMAGES_PER_MESSAGE } from "./ChatView"
import { useSelectedModel } from "../ui/hooks/useSelectedModel"
interface ChatRowProps {
message: ClineMessage
@ -116,70 +111,19 @@ export const ChatRowContent = ({
}: ChatRowContentProps) => {
const { t } = useTranslation()
const { mcpServers, alwaysAllowMcp, currentCheckpoint, mode, apiConfiguration } = useExtensionState()
const { info: model } = useSelectedModel(apiConfiguration)
const { mcpServers, alwaysAllowMcp, currentCheckpoint } = useExtensionState()
const [reasoningCollapsed, setReasoningCollapsed] = useState(true)
const [isDiffErrorExpanded, setIsDiffErrorExpanded] = useState(false)
const [showCopySuccess, setShowCopySuccess] = useState(false)
const [isEditing, setIsEditing] = useState(false)
const [editedContent, setEditedContent] = useState("")
const [editMode, setEditMode] = useState<Mode>(mode || "code")
const [editImages, setEditImages] = useState<string[]>([])
const { copyWithFeedback } = useCopyToClipboard()
// Handle message events for image selection during edit mode
useEffect(() => {
const handleMessage = (event: MessageEvent) => {
const msg = event.data
if (msg.type === "selectedImages" && msg.context === "edit" && msg.messageTs === message.ts && isEditing) {
setEditImages((prevImages) => appendImages(prevImages, msg.images, MAX_IMAGES_PER_MESSAGE))
}
}
window.addEventListener("message", handleMessage)
return () => window.removeEventListener("message", handleMessage)
}, [isEditing, message.ts])
// Memoized callback to prevent re-renders caused by inline arrow functions.
const handleToggleExpand = useCallback(() => {
onToggleExpand(message.ts)
}, [onToggleExpand, message.ts])
// Handle edit button click
const handleEditClick = useCallback(() => {
setIsEditing(true)
setEditedContent(message.text || "")
setEditImages(message.images || [])
setEditMode(mode || "code")
// Edit mode is now handled entirely in the frontend
// No need to notify the backend
}, [message.text, message.images, mode])
// Handle cancel edit
const handleCancelEdit = useCallback(() => {
setIsEditing(false)
setEditedContent(message.text || "")
setEditImages(message.images || [])
setEditMode(mode || "code")
}, [message.text, message.images, mode])
// Handle save edit
const handleSaveEdit = useCallback(() => {
setIsEditing(false)
// Send edited message to backend
vscode.postMessage({
type: "submitEditedMessage",
value: message.ts,
editedMessageContent: editedContent,
images: editImages,
})
}, [message.ts, editedContent, editImages])
// Handle image selection for editing
const handleSelectImages = useCallback(() => {
vscode.postMessage({ type: "selectImages", context: "edit", messageTs: message.ts })
}, [message.ts])
const [cost, apiReqCancelReason, apiReqStreamingFailedMessage] = useMemo(() => {
if (message.text !== null && message.text !== undefined && message.say === "api_req_started") {
const info = safeJsonParse<ClineApiReqInfo>(message.text)
@ -847,75 +791,6 @@ export const ChatRowContent = ({
</div>
</>
)
case "runSlashCommand": {
const slashCommandInfo = tool
return (
<>
<div style={headerStyle}>
{toolIcon("play")}
<span style={{ fontWeight: "bold" }}>
{message.type === "ask"
? t("chat:slashCommand.wantsToRun")
: t("chat:slashCommand.didRun")}
</span>
</div>
<div
style={{
marginTop: "4px",
backgroundColor: "var(--vscode-editor-background)",
border: "1px solid var(--vscode-editorGroup-border)",
borderRadius: "4px",
overflow: "hidden",
cursor: "pointer",
}}
onClick={handleToggleExpand}>
<ToolUseBlockHeader
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
padding: "10px 12px",
}}>
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
<span style={{ fontWeight: "500", fontSize: "var(--vscode-font-size)" }}>
/{slashCommandInfo.command}
</span>
{slashCommandInfo.source && (
<VSCodeBadge style={{ fontSize: "calc(var(--vscode-font-size) - 2px)" }}>
{slashCommandInfo.source}
</VSCodeBadge>
)}
</div>
<span className={`codicon codicon-chevron-${isExpanded ? "up" : "down"}`}></span>
</ToolUseBlockHeader>
{isExpanded && (slashCommandInfo.args || slashCommandInfo.description) && (
<div
style={{
padding: "12px 16px",
borderTop: "1px solid var(--vscode-editorGroup-border)",
display: "flex",
flexDirection: "column",
gap: "8px",
}}>
{slashCommandInfo.args && (
<div>
<span style={{ fontWeight: "500" }}>Arguments: </span>
<span style={{ color: "var(--vscode-descriptionForeground)" }}>
{slashCommandInfo.args}
</span>
</div>
)}
{slashCommandInfo.description && (
<div style={{ color: "var(--vscode-descriptionForeground)" }}>
{slashCommandInfo.description}
</div>
)}
</div>
)}
</div>
</>
)
}
case "generateImage":
return (
<>
@ -1173,58 +1048,26 @@ export const ChatRowContent = ({
case "user_feedback":
return (
<div className="bg-vscode-editor-background border rounded-xs p-1 overflow-hidden whitespace-pre-wrap">
{isEditing ? (
<div className="flex flex-col gap-2 p-2">
<ChatTextArea
inputValue={editedContent}
setInputValue={setEditedContent}
sendingDisabled={false}
selectApiConfigDisabled={true}
placeholderText={t("chat:editMessage.placeholder")}
selectedImages={editImages}
setSelectedImages={setEditImages}
onSend={handleSaveEdit}
onSelectImages={handleSelectImages}
shouldDisableImages={!model?.supportsImages}
mode={editMode}
setMode={setEditMode}
modeShortcutText=""
isEditMode={true}
onCancel={handleCancelEdit}
/>
<div className="flex justify-between">
<div className="flex-grow px-2 py-1 wrap-anywhere">
<Mention text={message.text} withShadow />
</div>
) : (
<div className="flex justify-between">
<div className="flex-grow px-2 py-1 wrap-anywhere">
<Mention text={message.text} withShadow />
</div>
<div className="flex">
<Button
variant="ghost"
size="icon"
className="shrink-0"
disabled={isStreaming}
onClick={(e) => {
e.stopPropagation()
handleEditClick()
}}>
<span className="codicon codicon-edit" />
</Button>
<Button
variant="ghost"
size="icon"
className="shrink-0"
disabled={isStreaming}
onClick={(e) => {
e.stopPropagation()
vscode.postMessage({ type: "deleteMessage", value: message.ts })
}}>
<span className="codicon codicon-trash" />
</Button>
</div>
<div className="flex">
<Button
variant="ghost"
size="icon"
className="shrink-0"
disabled={isStreaming}
onClick={(e) => {
e.stopPropagation()
vscode.postMessage({ type: "deleteMessage", value: message.ts })
}}>
<span className="codicon codicon-trash" />
</Button>
</div>
)}
{!isEditing && message.images && message.images.length > 0 && (
</div>
{message.images && message.images.length > 0 && (
<Thumbnails images={message.images} style={{ marginTop: "8px" }} />
)}
</div>
@ -1316,91 +1159,6 @@ export const ChatRowContent = ({
return <CodebaseSearchResultsDisplay results={results} />
case "user_edit_todos":
return <UpdateTodoListToolBlock userEdited onChange={() => {}} />
case "tool" as any:
// Handle say tool messages
const sayTool = safeJsonParse<ClineSayTool>(message.text)
if (!sayTool) return null
switch (sayTool.tool) {
case "runSlashCommand": {
const slashCommandInfo = sayTool
return (
<>
<div style={headerStyle}>
<span
className="codicon codicon-terminal-cmd"
style={{
color: "var(--vscode-foreground)",
marginBottom: "-1.5px",
}}></span>
<span style={{ fontWeight: "bold" }}>{t("chat:slashCommand.didRun")}</span>
</div>
<ToolUseBlock>
<ToolUseBlockHeader
style={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
gap: "4px",
padding: "10px 12px",
}}>
<div
style={{
display: "flex",
alignItems: "center",
gap: "8px",
width: "100%",
}}>
<span
style={{ fontWeight: "500", fontSize: "var(--vscode-font-size)" }}>
/{slashCommandInfo.command}
</span>
{slashCommandInfo.args && (
<span
style={{
color: "var(--vscode-descriptionForeground)",
fontSize: "var(--vscode-font-size)",
}}>
{slashCommandInfo.args}
</span>
)}
</div>
{slashCommandInfo.description && (
<div
style={{
color: "var(--vscode-descriptionForeground)",
fontSize: "calc(var(--vscode-font-size) - 1px)",
}}>
{slashCommandInfo.description}
</div>
)}
{slashCommandInfo.source && (
<div style={{ display: "flex", alignItems: "center", gap: "4px" }}>
<VSCodeBadge
style={{ fontSize: "calc(var(--vscode-font-size) - 2px)" }}>
{slashCommandInfo.source}
</VSCodeBadge>
</div>
)}
</ToolUseBlockHeader>
</ToolUseBlock>
</>
)
}
default:
return null
}
case "image":
// Parse the JSON to get imageUri and imagePath
const imageInfo = safeJsonParse<{ imageUri: string; imagePath: string }>(message.text || "{}")
if (!imageInfo) {
return null
}
return (
<div style={{ marginTop: "10px" }}>
<ImageBlock imageUri={imageInfo.imageUri} imagePath={imageInfo.imagePath} />
</div>
)
default:
return (
<>

View file

@ -1,66 +1,15 @@
import React from "react"
import { ImageViewer } from "./ImageViewer"
/**
* Props for the ImageBlock component
*/
interface ImageBlockProps {
/**
* The webview-accessible URI for rendering the image.
* This is the preferred format for new image generation tools.
* Should be a URI that can be directly loaded in the webview context.
*/
imageUri?: string
/**
* The actual file path for display purposes and file operations.
* Used to show the path to the user and for opening the file in the editor.
* This is typically an absolute or relative path to the image file.
*/
imagePath?: string
/**
* Base64 data or regular URL for backward compatibility.
* @deprecated Use imageUri instead for new implementations.
* This is maintained for compatibility with Mermaid diagrams and legacy code.
*/
imageData?: string
/**
* Optional path for Mermaid diagrams.
* @deprecated Use imagePath instead for new implementations.
* This is maintained for backward compatibility with existing Mermaid diagram rendering.
*/
imageData: string
path?: string
}
export default function ImageBlock({ imageUri, imagePath, imageData, path }: ImageBlockProps) {
// Determine which props to use based on what's provided
let finalImageUri: string
let finalImagePath: string | undefined
if (imageUri) {
// New format: explicit imageUri and imagePath
finalImageUri = imageUri
finalImagePath = imagePath
} else if (imageData) {
// Legacy format: use imageData as direct URI (for Mermaid diagrams)
finalImageUri = imageData
finalImagePath = path
} else {
// No valid image data provided
console.error("ImageBlock: No valid image data provided")
return null
}
export default function ImageBlock({ imageData, path }: ImageBlockProps) {
return (
<div className="my-2">
<ImageViewer
imageUri={finalImageUri}
imagePath={finalImagePath}
alt="AI Generated Image"
showControls={true}
/>
<ImageViewer imageData={imageData} path={path} alt="AI Generated Image" showControls={true} />
</div>
)
}

View file

@ -13,17 +13,17 @@ const MIN_ZOOM = 0.5
const MAX_ZOOM = 20
export interface ImageViewerProps {
imageUri: string // The URI to use for rendering (webview URI, base64, or regular URL)
imagePath?: string // The actual file path for display and opening
imageData: string // base64 data URL or regular URL
alt?: string
path?: string
showControls?: boolean
className?: string
}
export function ImageViewer({
imageUri,
imagePath,
imageData,
alt = "Generated image",
path,
showControls = true,
className = "",
}: ImageViewerProps) {
@ -33,7 +33,6 @@ export function ImageViewer({
const [isHovering, setIsHovering] = useState(false)
const [isDragging, setIsDragging] = useState(false)
const [dragPosition, setDragPosition] = useState({ x: 0, y: 0 })
const [imageError, setImageError] = useState<string | null>(null)
const { copyWithFeedback } = useCopyToClipboard()
const { t } = useAppTranslation()
@ -54,13 +53,12 @@ export function ImageViewer({
e.stopPropagation()
try {
// Copy the file path if available
if (imagePath) {
await copyWithFeedback(imagePath, e)
// Show feedback
setCopyFeedback(true)
setTimeout(() => setCopyFeedback(false), 2000)
}
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))
}
@ -73,10 +71,10 @@ export function ImageViewer({
e.stopPropagation()
try {
// Request VSCode to save the image
// Send message to VSCode to save the image
vscode.postMessage({
type: "saveImage",
dataUri: imageUri,
dataUri: imageData,
})
} catch (error) {
console.error("Error saving image:", error)
@ -88,21 +86,10 @@ export function ImageViewer({
*/
const handleOpenInEditor = (e: React.MouseEvent) => {
e.stopPropagation()
// Use openImage for both file paths and data URIs
// The backend will handle both cases appropriately
if (imagePath) {
// Use the actual file path for opening
vscode.postMessage({
type: "openImage",
text: imagePath,
})
} else if (imageUri) {
// Fallback to opening image URI if no path is available (for Mermaid diagrams)
vscode.postMessage({
type: "openImage",
text: imageUri,
})
}
vscode.postMessage({
type: "openImage",
text: imageData,
})
}
/**
@ -142,86 +129,24 @@ export function ImageViewer({
setIsHovering(false)
}
const handleImageError = useCallback(() => {
setImageError("Failed to load image")
}, [])
const handleImageLoad = useCallback(() => {
setImageError(null)
}, [])
/**
* Format the display path for the image
*/
const formatDisplayPath = (path: string): string => {
// If it's already a relative path starting with ./, keep it
if (path.startsWith("./")) return path
// If it's an absolute path, extract the relative portion
// Look for workspace patterns - match the last segment after any directory separator
const workspaceMatch = path.match(/\/([^/]+)\/(.+)$/)
if (workspaceMatch && workspaceMatch[2]) {
// Return relative path from what appears to be the workspace root
return `./${workspaceMatch[2]}`
}
// Otherwise, just get the filename
const filename = path.split("/").pop()
return filename || path
}
// Handle missing image URI
if (!imageUri) {
return (
<div
className={`relative w-full ${className}`}
style={{
minHeight: "100px",
backgroundColor: "var(--vscode-editor-background)",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}>
<span style={{ color: "var(--vscode-descriptionForeground)" }}>{t("common:image.noData")}</span>
</div>
)
}
return (
<>
<div
className={`relative w-full ${className}`}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}>
{imageError ? (
<div
style={{
minHeight: "100px",
display: "flex",
alignItems: "center",
justifyContent: "center",
backgroundColor: "var(--vscode-editor-background)",
borderRadius: "4px",
padding: "20px",
}}>
<span style={{ color: "var(--vscode-errorForeground)" }}> {imageError}</span>
</div>
) : (
<img
src={imageUri}
alt={alt}
className="w-full h-auto rounded cursor-pointer"
onClick={handleOpenInEditor}
onError={handleImageError}
onLoad={handleImageLoad}
style={{
maxHeight: "400px",
objectFit: "contain",
backgroundColor: "var(--vscode-editor-background)",
}}
/>
)}
{imagePath && (
<div className="mt-1 text-xs text-vscode-descriptionForeground">{formatDisplayPath(imagePath)}</div>
)}
<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
@ -277,7 +202,7 @@ export function ImageViewer({
onMouseUp={() => setIsDragging(false)}
onMouseLeave={() => setIsDragging(false)}>
<img
src={imageUri}
src={imageData}
alt={alt}
style={{
maxWidth: "90vw",
@ -300,7 +225,7 @@ export function ImageViewer({
zoomInStep={0.2}
zoomOutStep={-0.2}
/>
{imagePath && (
{path && (
<StandardTooltip content={t("common:mermaid.buttons.copy")}>
<IconButton icon={copyFeedback ? "check" : "copy"} onClick={handleCopy} />
</StandardTooltip>

View file

@ -12,34 +12,26 @@ import { SetExperimentEnabled, SetCachedStateField } from "./types"
import { SectionHeader } from "./SectionHeader"
import { Section } from "./Section"
import { ExperimentalFeature } from "./ExperimentalFeature"
import { ImageGenerationSettings } from "./ImageGenerationSettings"
import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
import { ImageGenerationSettings } from "./ImageGenerationSettings"
type ExperimentalSettingsProps = HTMLAttributes<HTMLDivElement> & {
experiments: Experiments
setExperimentEnabled: SetExperimentEnabled
apiConfiguration?: any
setApiConfigurationField?: any
openRouterImageApiKey?: string
openRouterImageGenerationSelectedModel?: string
setOpenRouterImageApiKey?: (apiKey: string) => void
setImageGenerationSelectedModel?: (model: string) => void
// Include Files Changed Overview toggle in Experimental section per review feedback
filesChangedEnabled?: boolean
setCachedStateField?: SetCachedStateField<"filesChangedEnabled">
apiConfiguration?: any
setApiConfigurationField?: any
}
export const ExperimentalSettings = ({
experiments,
setExperimentEnabled,
apiConfiguration,
setApiConfigurationField,
openRouterImageApiKey,
openRouterImageGenerationSelectedModel,
setOpenRouterImageApiKey,
setImageGenerationSelectedModel,
filesChangedEnabled,
setCachedStateField,
apiConfiguration,
setApiConfigurationField,
className,
...props
}: ExperimentalSettingsProps) => {
@ -88,11 +80,7 @@ export const ExperimentalSettings = ({
/>
)
}
if (
config[0] === "IMAGE_GENERATION" &&
setOpenRouterImageApiKey &&
setImageGenerationSelectedModel
) {
if (config[0] === "IMAGE_GENERATION" && apiConfiguration && setApiConfigurationField) {
return (
<ImageGenerationSettings
key={config[0]}
@ -100,10 +88,8 @@ export const ExperimentalSettings = ({
onChange={(enabled) =>
setExperimentEnabled(EXPERIMENT_IDS.IMAGE_GENERATION, enabled)
}
openRouterImageApiKey={openRouterImageApiKey}
openRouterImageGenerationSelectedModel={openRouterImageGenerationSelectedModel}
setOpenRouterImageApiKey={setOpenRouterImageApiKey}
setImageGenerationSelectedModel={setImageGenerationSelectedModel}
apiConfiguration={apiConfiguration}
setApiConfigurationField={setApiConfigurationField}
/>
)
}

View file

@ -1,55 +1,48 @@
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
openRouterImageApiKey?: string
openRouterImageGenerationSelectedModel?: string
setOpenRouterImageApiKey: (apiKey: string) => void
setImageGenerationSelectedModel: (model: string) => 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" },
{ value: "google/gemini-2.5-flash-image-preview:free", label: "Gemini 2.5 Flash Image Preview (Free)" },
// Add more models as they become available
]
export const ImageGenerationSettings = ({
enabled,
onChange,
openRouterImageApiKey,
openRouterImageGenerationSelectedModel,
setOpenRouterImageApiKey,
setImageGenerationSelectedModel,
apiConfiguration,
setApiConfigurationField,
}: ImageGenerationSettingsProps) => {
const { t } = useAppTranslation()
const [apiKey, setApiKey] = useState(openRouterImageApiKey || "")
// Get image generation settings from apiConfiguration
const imageGenerationSettings = apiConfiguration?.openRouterImageGenerationSettings || {}
const [openRouterApiKey, setOpenRouterApiKey] = useState(imageGenerationSettings.openRouterApiKey || "")
const [selectedModel, setSelectedModel] = useState(
openRouterImageGenerationSelectedModel || IMAGE_GENERATION_MODELS[0].value,
imageGenerationSettings.selectedModel || IMAGE_GENERATION_MODELS[0].value,
)
// Update local state when props change (e.g., when switching profiles)
// Update parent state when local state changes
useEffect(() => {
setApiKey(openRouterImageApiKey || "")
setSelectedModel(openRouterImageGenerationSelectedModel || IMAGE_GENERATION_MODELS[0].value)
}, [openRouterImageApiKey, openRouterImageGenerationSelectedModel])
// Handle API key changes
const handleApiKeyChange = (value: string) => {
setApiKey(value)
setOpenRouterImageApiKey(value)
}
// Handle model selection changes
const handleModelChange = (value: string) => {
setSelectedModel(value)
setImageGenerationSelectedModel(value)
}
const newSettings = {
openRouterApiKey,
selectedModel,
}
setApiConfigurationField("openRouterImageGenerationSettings", newSettings)
}, [openRouterApiKey, selectedModel, setApiConfigurationField])
return (
<div className="space-y-4">
@ -72,8 +65,8 @@ export const ImageGenerationSettings = ({
{t("settings:experimental.IMAGE_GENERATION.openRouterApiKeyLabel")}
</label>
<VSCodeTextField
value={apiKey}
onInput={(e: any) => handleApiKeyChange(e.target.value)}
value={openRouterApiKey}
onInput={(e: any) => setOpenRouterApiKey(e.target.value)}
placeholder={t("settings:experimental.IMAGE_GENERATION.openRouterApiKeyPlaceholder")}
className="w-full"
type="password"
@ -97,10 +90,10 @@ export const ImageGenerationSettings = ({
</label>
<VSCodeDropdown
value={selectedModel}
onChange={(e: any) => handleModelChange(e.target.value)}
onChange={(e: any) => setSelectedModel(e.target.value)}
className="w-full">
{IMAGE_GENERATION_MODELS.map((model) => (
<VSCodeOption key={model.value} value={model.value} className="py-2 px-3">
<VSCodeOption key={model.value} value={model.value}>
{model.label}
</VSCodeOption>
))}
@ -111,13 +104,13 @@ export const ImageGenerationSettings = ({
</div>
{/* Status Message */}
{enabled && !apiKey && (
{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 && apiKey && (
{enabled && openRouterApiKey && (
<div className="p-2 bg-vscode-editorInfo-background text-vscode-editorInfo-foreground rounded text-sm">
{t("settings:experimental.IMAGE_GENERATION.successConfigured")}
</div>

View file

@ -113,11 +113,6 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
: "providers",
)
const scrollPositions = useRef<Record<SectionName, number>>(
Object.fromEntries(sectionNames.map((s) => [s, 0])) as Record<SectionName, number>,
)
const contentRef = useRef<HTMLDivElement | null>(null)
const prevApiConfigName = useRef(currentApiConfigName)
const confirmDialogHandler = useRef<() => void>()
@ -187,8 +182,6 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
includeDiagnosticMessages,
maxDiagnosticMessages,
includeTaskHistoryInEnhance,
openRouterImageApiKey,
openRouterImageGenerationSelectedModel,
filesChangedEnabled,
} = cachedState
@ -269,20 +262,6 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
})
}, [])
const setOpenRouterImageApiKey = useCallback((apiKey: string) => {
setCachedState((prevState) => {
setChangeDetected(true)
return { ...prevState, openRouterImageApiKey: apiKey }
})
}, [])
const setImageGenerationSelectedModel = useCallback((model: string) => {
setCachedState((prevState) => {
setChangeDetected(true)
return { ...prevState, openRouterImageGenerationSelectedModel: model }
})
}, [])
const setCustomSupportPromptsField = useCallback((prompts: Record<string, string | undefined>) => {
setCachedState((prevState) => {
if (JSON.stringify(prevState.customSupportPrompts) === JSON.stringify(prompts)) {
@ -367,11 +346,6 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
vscode.postMessage({ type: "upsertApiConfiguration", text: currentApiConfigName, apiConfiguration })
vscode.postMessage({ type: "telemetrySetting", text: telemetrySetting })
vscode.postMessage({ type: "profileThresholds", values: profileThresholds })
vscode.postMessage({ type: "openRouterImageApiKey", text: openRouterImageApiKey })
vscode.postMessage({
type: "openRouterImageGenerationSelectedModel",
text: openRouterImageGenerationSelectedModel,
})
setChangeDetected(false)
}
}
@ -406,20 +380,12 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
// Handle tab changes with unsaved changes check
const handleTabChange = useCallback(
(newTab: SectionName) => {
if (contentRef.current) {
scrollPositions.current[activeTab] = contentRef.current.scrollTop
}
// Directly switch tab without checking for unsaved changes
setActiveTab(newTab)
},
[activeTab],
[], // No dependency on isChangeDetected needed anymore
)
useLayoutEffect(() => {
if (contentRef.current) {
contentRef.current.scrollTop = scrollPositions.current[activeTab] ?? 0
}
}, [activeTab])
// Store direct DOM element refs for each tab
const tabRefs = useRef<Record<SectionName, HTMLButtonElement | null>>(
Object.fromEntries(sectionNames.map((name) => [name, null])) as Record<SectionName, HTMLButtonElement | null>,
@ -595,7 +561,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
</TabList>
{/* Content area */}
<TabContent ref={contentRef} className="p-0 flex-1 overflow-auto">
<TabContent className="p-0 flex-1 overflow-auto">
{/* Providers Section */}
{activeTab === "providers" && (
<div>
@ -758,16 +724,10 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
<ExperimentalSettings
setExperimentEnabled={setExperimentEnabled}
experiments={experiments}
apiConfiguration={apiConfiguration}
setApiConfigurationField={setApiConfigurationField}
openRouterImageApiKey={openRouterImageApiKey as string | undefined}
openRouterImageGenerationSelectedModel={
openRouterImageGenerationSelectedModel as string | undefined
}
setOpenRouterImageApiKey={setOpenRouterImageApiKey}
setImageGenerationSelectedModel={setImageGenerationSelectedModel}
filesChangedEnabled={filesChangedEnabled}
setCachedStateField={setCachedStateField as SetCachedStateField<"filesChangedEnabled">}
filesChangedEnabled={filesChangedEnabled}
setCachedStateField={setCachedStateField as SetCachedStateField<"filesChangedEnabled">}
apiConfiguration={apiConfiguration}
setApiConfigurationField={setApiConfigurationField}
/>
)}

View file

@ -232,7 +232,6 @@ describe("mergeExtensionState", () => {
preventFocusDisruption: false,
newTaskRequireTodos: false,
imageGeneration: false,
runSlashCommand: false,
} as Record<ExperimentId, boolean>,
}
@ -252,7 +251,6 @@ describe("mergeExtensionState", () => {
preventFocusDisruption: false,
newTaskRequireTodos: false,
imageGeneration: false,
runSlashCommand: false,
})
})
})

View file

@ -51,8 +51,7 @@
"image": {
"tabs": {
"view": "Imatge"
},
"noData": "Sense dades d'imatge"
}
},
"file": {
"errors": {
@ -72,13 +71,7 @@
"deleteWarning": "Eliminar aquest missatge eliminarà tots els missatges posteriors de la conversa. Vols continuar?",
"editMessage": "Editar missatge",
"editWarning": "Editar aquest missatge eliminarà tots els missatges posteriors de la conversa. Vols continuar?",
"editQuestionWithCheckpoint": "Editar aquest missatge eliminarà tots els missatges posteriors de la conversa. També vols desfer tots els canvis fins a aquest punt de control?",
"deleteQuestionWithCheckpoint": "Eliminar aquest missatge eliminarà tots els missatges posteriors de la conversa. També vols desfer tots els canvis fins a aquest punt de control?",
"editOnly": "No, només editar el missatge",
"deleteOnly": "No, només eliminar el missatge",
"restoreToCheckpoint": "Sí, restaurar el punt de control",
"proceed": "Continuar",
"dontShowAgain": "No tornis a mostrar això"
"proceed": "Continuar"
},
"time_ago": {
"just_now": "ara mateix",

View file

@ -375,8 +375,6 @@
"ollama": {
"baseUrl": "URL base (opcional)",
"modelId": "ID del model",
"apiKey": "Clau API d'Ollama",
"apiKeyHelp": "Clau API opcional per a instàncies d'Ollama autenticades o serveis al núvol. Deixa-ho buit per a instal·lacions locals.",
"description": "Ollama permet executar models localment al vostre ordinador. Per a instruccions sobre com començar, consulteu la Guia d'inici ràpid.",
"warning": "Nota: Roo Code utilitza prompts complexos i funciona millor amb models Claude. Els models menys capaços poden no funcionar com s'espera."
},
@ -750,10 +748,6 @@
"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"
},
"RUN_SLASH_COMMAND": {
"name": "Habilitar comandes de barra diagonal iniciades pel model",
"description": "Quan està habilitat, Roo pot executar les vostres comandes de barra diagonal per executar fluxos de treball."
}
},
"promptCaching": {
@ -866,19 +860,5 @@
"includeMaxOutputTokensDescription": "Enviar el paràmetre de tokens màxims de sortida a les sol·licituds API. Alguns proveïdors poden no admetre això.",
"limitMaxTokensDescription": "Limitar el nombre màxim de tokens en la resposta",
"maxOutputTokensLabel": "Tokens màxims de sortida",
"maxTokensGenerateDescription": "Tokens màxims a generar en la resposta",
"serviceTier": {
"label": "Nivell de servei",
"tooltip": "Per a un processament més ràpid de les sol·licituds de l'API, proveu el nivell de servei de processament prioritari. Per a preus més baixos amb una latència més alta, proveu el nivell de processament flexible.",
"standard": "Estàndard",
"flex": "Flex",
"priority": "Prioritat",
"pricingTableTitle": "Preus per nivell de servei (preu per 1M de fitxes)",
"columns": {
"tier": "Nivell",
"input": "Entrada",
"output": "Sortida",
"cacheReads": "Lectures de memòria cau"
}
}
"maxTokensGenerateDescription": "Tokens màxims a generar en la resposta"
}

View file

@ -51,8 +51,7 @@
"image": {
"tabs": {
"view": "Bild"
},
"noData": "Keine Bilddaten"
}
},
"file": {
"errors": {
@ -72,13 +71,7 @@
"deleteWarning": "Das Löschen dieser Nachricht wird alle nachfolgenden Nachrichten in der Unterhaltung löschen. Möchtest du fortfahren?",
"editMessage": "Nachricht bearbeiten",
"editWarning": "Das Bearbeiten dieser Nachricht wird alle nachfolgenden Nachrichten in der Unterhaltung löschen. Möchtest du fortfahren?",
"editQuestionWithCheckpoint": "Das Bearbeiten dieser Nachricht wird alle späteren Nachrichten in der Unterhaltung löschen. Möchtest du auch alle Änderungen bis zu diesem Checkpoint rückgängig machen?",
"deleteQuestionWithCheckpoint": "Das Löschen dieser Nachricht wird alle späteren Nachrichten in der Unterhaltung löschen. Möchtest du auch alle Änderungen bis zu diesem Checkpoint rückgängig machen?",
"editOnly": "Nein, nur Nachricht bearbeiten",
"deleteOnly": "Nein, nur Nachricht löschen",
"restoreToCheckpoint": "Ja, Checkpoint wiederherstellen",
"proceed": "Fortfahren",
"dontShowAgain": "Nicht mehr anzeigen"
"proceed": "Fortfahren"
},
"time_ago": {
"just_now": "gerade eben",

View file

@ -375,8 +375,6 @@
"ollama": {
"baseUrl": "Basis-URL (optional)",
"modelId": "Modell-ID",
"apiKey": "Ollama API-Schlüssel",
"apiKeyHelp": "Optionaler API-Schlüssel für authentifizierte Ollama-Instanzen oder Cloud-Services. Leer lassen für lokale Installationen.",
"description": "Ollama ermöglicht es dir, Modelle lokal auf deinem Computer auszuführen. Eine Anleitung zum Einstieg findest du im Schnellstart-Guide.",
"warning": "Hinweis: Roo Code verwendet komplexe Prompts und funktioniert am besten mit Claude-Modellen. Weniger leistungsfähige Modelle funktionieren möglicherweise nicht wie erwartet."
},
@ -750,10 +748,6 @@
"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"
},
"RUN_SLASH_COMMAND": {
"name": "Modellinitierte Slash-Befehle aktivieren",
"description": "Wenn aktiviert, kann Roo deine Slash-Befehle ausführen, um Workflows zu starten."
}
},
"promptCaching": {
@ -866,19 +860,5 @@
"includeMaxOutputTokensDescription": "Senden Sie den Parameter für maximale Ausgabe-Tokens in API-Anfragen. Einige Anbieter unterstützen dies möglicherweise nicht.",
"limitMaxTokensDescription": "Begrenze die maximale Anzahl von Tokens in der Antwort",
"maxOutputTokensLabel": "Maximale Ausgabe-Tokens",
"maxTokensGenerateDescription": "Maximale Tokens, die in der Antwort generiert werden",
"serviceTier": {
"label": "Service-Stufe",
"tooltip": "Für eine schnellere Verarbeitung von API-Anfragen, probiere die Prioritäts-Verarbeitungsstufe. Für niedrigere Preise bei höherer Latenz, probiere die Flex-Verarbeitungsstufe.",
"standard": "Standard",
"flex": "Flex",
"priority": "Priorität",
"pricingTableTitle": "Preise nach Service-Stufe (Preis pro 1 Mio. Token)",
"columns": {
"tier": "Stufe",
"input": "Eingabe",
"output": "Ausgabe",
"cacheReads": "Cache-Lesevorgänge"
}
}
"maxTokensGenerateDescription": "Maximale Tokens, die in der Antwort generiert werden"
}

View file

@ -51,8 +51,7 @@
"image": {
"tabs": {
"view": "Image"
},
"noData": "No image data"
}
},
"file": {
"errors": {
@ -72,13 +71,7 @@
"deleteWarning": "Deleting this message will delete all subsequent messages in the conversation. Do you want to proceed?",
"editMessage": "Edit Message",
"editWarning": "Editing this message will delete all subsequent messages in the conversation. Do you want to proceed?",
"editQuestionWithCheckpoint": "Editing this message will delete all later messages in the conversation. Do you also want to undo all changes back to this checkpoint?",
"deleteQuestionWithCheckpoint": "Deleting this message will delete all later messages in the conversation. Do you also want to undo all changes back to this checkpoint?",
"editOnly": "No, edit message only",
"deleteOnly": "No, delete message only",
"restoreToCheckpoint": "Yes, restore the checkpoint",
"proceed": "Proceed",
"dontShowAgain": "Don't show this again"
"proceed": "Proceed"
},
"time_ago": {
"just_now": "just now",

View file

@ -374,8 +374,6 @@
"ollama": {
"baseUrl": "Base URL (optional)",
"modelId": "Model ID",
"apiKey": "Ollama API Key",
"apiKeyHelp": "Optional API key for authenticated Ollama instances or cloud services. Leave empty for local installations.",
"description": "Ollama allows you to run models locally on your computer. For instructions on how to get started, see their quickstart guide.",
"warning": "Note: Roo Code uses complex prompts and works best with Claude models. Less capable models may not work as expected."
},
@ -749,10 +747,6 @@
"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"
},
"RUN_SLASH_COMMAND": {
"name": "Enable model-initiated slash commands",
"description": "When enabled, Roo can run your slash commands to execute workflows."
}
},
"promptCaching": {
@ -865,19 +859,5 @@
"includeMaxOutputTokensDescription": "Send max output tokens parameter in API requests. Some providers may not support this.",
"limitMaxTokensDescription": "Limit the maximum number of tokens in the response",
"maxOutputTokensLabel": "Max output tokens",
"maxTokensGenerateDescription": "Maximum tokens to generate in response",
"serviceTier": {
"label": "Service tier",
"tooltip": "For faster processing of API requests, try the priority processing service tier. For lower prices with higher latency, try the flex processing tier.",
"standard": "Standard",
"flex": "Flex",
"priority": "Priority",
"pricingTableTitle": "Pricing by service tier (price per 1M tokens)",
"columns": {
"tier": "Tier",
"input": "Input",
"output": "Output",
"cacheReads": "Cache reads"
}
}
"maxTokensGenerateDescription": "Maximum tokens to generate in response"
}

View file

@ -51,8 +51,7 @@
"image": {
"tabs": {
"view": "Imagen"
},
"noData": "Sin datos de imagen"
}
},
"file": {
"errors": {
@ -72,13 +71,7 @@
"deleteWarning": "Eliminar este mensaje eliminará todos los mensajes posteriores en la conversación. ¿Deseas continuar?",
"editMessage": "Editar mensaje",
"editWarning": "Editar este mensaje eliminará todos los mensajes posteriores en la conversación. ¿Deseas continuar?",
"editQuestionWithCheckpoint": "Editar este mensaje eliminará todos los mensajes posteriores en la conversación. ¿También deseas deshacer todos los cambios hasta este punto de control?",
"deleteQuestionWithCheckpoint": "Eliminar este mensaje eliminará todos los mensajes posteriores en la conversación. ¿También deseas deshacer todos los cambios hasta este punto de control?",
"editOnly": "No, solo editar el mensaje",
"deleteOnly": "No, solo eliminar el mensaje",
"restoreToCheckpoint": "Sí, restaurar el punto de control",
"proceed": "Continuar",
"dontShowAgain": "No mostrar esto de nuevo"
"proceed": "Continuar"
},
"time_ago": {
"just_now": "ahora mismo",

View file

@ -375,8 +375,6 @@
"ollama": {
"baseUrl": "URL base (opcional)",
"modelId": "ID del modelo",
"apiKey": "Clave API de Ollama",
"apiKeyHelp": "Clave API opcional para instancias de Ollama autenticadas o servicios en la nube. Deja vacío para instalaciones locales.",
"description": "Ollama le permite ejecutar modelos localmente en su computadora. Para obtener instrucciones sobre cómo comenzar, consulte la guía de inicio rápido.",
"warning": "Nota: Roo Code utiliza prompts complejos y funciona mejor con modelos Claude. Los modelos menos capaces pueden no funcionar como se espera."
},
@ -750,10 +748,6 @@
"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"
},
"RUN_SLASH_COMMAND": {
"name": "Habilitar comandos slash iniciados por el modelo",
"description": "Cuando está habilitado, Roo puede ejecutar tus comandos slash para ejecutar flujos de trabajo."
}
},
"promptCaching": {
@ -866,19 +860,5 @@
"includeMaxOutputTokensDescription": "Enviar parámetro de tokens máximos de salida en solicitudes API. Algunos proveedores pueden no soportar esto.",
"limitMaxTokensDescription": "Limitar el número máximo de tokens en la respuesta",
"maxOutputTokensLabel": "Tokens máximos de salida",
"maxTokensGenerateDescription": "Tokens máximos a generar en la respuesta",
"serviceTier": {
"label": "Nivel de servicio",
"tooltip": "Para un procesamiento más rápido de las solicitudes de API, prueba el nivel de servicio de procesamiento prioritario. Para precios más bajos con mayor latencia, prueba el nivel de procesamiento flexible.",
"standard": "Estándar",
"flex": "Flexible",
"priority": "Prioridad",
"pricingTableTitle": "Precios por nivel de servicio (precio por 1M de tokens)",
"columns": {
"tier": "Nivel",
"input": "Entrada",
"output": "Salida",
"cacheReads": "Lecturas de caché"
}
}
"maxTokensGenerateDescription": "Tokens máximos a generar en la respuesta"
}

View file

@ -51,8 +51,7 @@
"image": {
"tabs": {
"view": "Image"
},
"noData": "Aucune donnée d'image"
}
},
"file": {
"errors": {
@ -72,13 +71,7 @@
"deleteWarning": "Supprimer ce message supprimera tous les messages suivants dans la conversation. Voulez-vous continuer ?",
"editMessage": "Modifier le message",
"editWarning": "Modifier ce message supprimera tous les messages suivants dans la conversation. Voulez-vous continuer ?",
"editQuestionWithCheckpoint": "Modifier ce message supprimera tous les messages ultérieurs dans la conversation. Voulez-vous aussi annuler tous les changements jusqu'à ce point de contrôle ?",
"deleteQuestionWithCheckpoint": "Supprimer ce message supprimera tous les messages ultérieurs dans la conversation. Voulez-vous aussi annuler tous les changements jusqu'à ce point de contrôle ?",
"editOnly": "Non, modifier le message seulement",
"deleteOnly": "Non, supprimer le message seulement",
"restoreToCheckpoint": "Oui, restaurer le point de contrôle",
"proceed": "Continuer",
"dontShowAgain": "Ne plus afficher ceci"
"proceed": "Continuer"
},
"time_ago": {
"just_now": "à l'instant",

View file

@ -375,8 +375,6 @@
"ollama": {
"baseUrl": "URL de base (optionnel)",
"modelId": "ID du modèle",
"apiKey": "Clé API Ollama",
"apiKeyHelp": "Clé API optionnelle pour les instances Ollama authentifiées ou les services cloud. Laissez vide pour les installations locales.",
"description": "Ollama vous permet d'exécuter des modèles localement sur votre ordinateur. Pour obtenir des instructions sur la mise en route, consultez le guide de démarrage rapide.",
"warning": "Remarque : Roo Code utilise des prompts complexes et fonctionne mieux avec les modèles Claude. Les modèles moins performants peuvent ne pas fonctionner comme prévu."
},
@ -750,10 +748,6 @@
"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"
},
"RUN_SLASH_COMMAND": {
"name": "Activer les commandes slash initiées par le modèle",
"description": "Lorsque activé, Roo peut exécuter tes commandes slash pour lancer des workflows."
}
},
"promptCaching": {
@ -866,19 +860,5 @@
"includeMaxOutputTokensDescription": "Envoyer le paramètre de tokens de sortie maximum dans les requêtes API. Certains fournisseurs peuvent ne pas supporter cela.",
"limitMaxTokensDescription": "Limiter le nombre maximum de tokens dans la réponse",
"maxOutputTokensLabel": "Tokens de sortie maximum",
"maxTokensGenerateDescription": "Tokens maximum à générer dans la réponse",
"serviceTier": {
"label": "Niveau de service",
"tooltip": "Pour un traitement plus rapide des demandes d'API, essayez le niveau de service de traitement prioritaire. Pour des prix plus bas avec une latence plus élevée, essayez le niveau de traitement flexible.",
"standard": "Standard",
"flex": "Flexible",
"priority": "Priorité",
"pricingTableTitle": "Tarification par niveau de service (prix par 1M de tokens)",
"columns": {
"tier": "Niveau",
"input": "Entrée",
"output": "Sortie",
"cacheReads": "Lectures du cache"
}
}
"maxTokensGenerateDescription": "Tokens maximum à générer dans la réponse"
}

View file

@ -51,8 +51,7 @@
"image": {
"tabs": {
"view": "चित्र"
},
"noData": "कोई छवि डेटा नहीं"
}
},
"file": {
"errors": {
@ -72,13 +71,7 @@
"deleteWarning": "इस संदेश को हटाने से बातचीत के सभी बाद के संदेश हट जाएंगे। क्या आप जारी रखना चाहते हैं?",
"editMessage": "संदेश संपादित करें",
"editWarning": "इस संदेश को संपादित करने से बातचीत के सभी बाद के संदेश हट जाएंगे। क्या आप जारी रखना चाहते हैं?",
"editQuestionWithCheckpoint": "इस संदेश को संपादित करने से बातचीत के सभी बाद के संदेश हट जाएंगे। क्या आप इस चेकपॉइंट तक सभी परिवर्तनों को भी पूर्ववत करना चाहते हैं?",
"deleteQuestionWithCheckpoint": "इस संदेश को हटाने से बातचीत के सभी बाद के संदेश हट जाएंगे। क्या आप इस चेकपॉइंट तक सभी परिवर्तनों को भी पूर्ववत करना चाहते हैं?",
"editOnly": "नहीं, केवल संदेश संपादित करें",
"deleteOnly": "नहीं, केवल संदेश हटाएं",
"restoreToCheckpoint": "हां, चेकपॉइंट पुनर्स्थापित करें",
"proceed": "जारी रखें",
"dontShowAgain": "यह फिर से न दिखाएं"
"proceed": "जारी रखें"
},
"time_ago": {
"just_now": "अभी",

View file

@ -375,8 +375,6 @@
"ollama": {
"baseUrl": "बेस URL (वैकल्पिक)",
"modelId": "मॉडल ID",
"apiKey": "Ollama API Key",
"apiKeyHelp": "प्रमाणित Ollama इंस्टेंसेस या क्लाउड सेवाओं के लिए वैकल्पिक API key। स्थानीय इंस्टॉलेशन के लिए खाली छोड़ें।",
"description": "Ollama आपको अपने कंप्यूटर पर स्थानीय रूप से मॉडल चलाने की अनुमति देता है। आरंभ करने के निर्देशों के लिए, उनकी क्विकस्टार्ट गाइड देखें।",
"warning": "नोट: Roo Code जटिल प्रॉम्प्ट्स का उपयोग करता है और Claude मॉडल के साथ सबसे अच्छा काम करता है। कम क्षमता वाले मॉडल अपेक्षित रूप से काम नहीं कर सकते हैं।"
},
@ -751,10 +749,6 @@
"modelSelectionDescription": "छवि निर्माण के लिए उपयोग करने वाला मॉडल चुनें",
"warningMissingKey": "⚠️ छवि निर्माण के लिए OpenRouter API कुंजी आवश्यक है। कृपया इसे ऊपर कॉन्फ़िगर करें।",
"successConfigured": "✓ छवि निर्माण कॉन्फ़िगर है और उपयोग के लिए तैयार है"
},
"RUN_SLASH_COMMAND": {
"name": "मॉडल द्वारा शुरू किए गए स्लैश कमांड सक्षम करें",
"description": "जब सक्षम होता है, Roo वर्कफ़्लो चलाने के लिए आपके स्लैश कमांड चला सकता है।"
}
},
"promptCaching": {
@ -867,19 +861,5 @@
"includeMaxOutputTokensDescription": "API अनुरोधों में अधिकतम आउटपुट टोकन पैरामीटर भेजें। कुछ प्रदाता इसका समर्थन नहीं कर सकते हैं।",
"limitMaxTokensDescription": "प्रतिक्रिया में टोकन की अधिकतम संख्या सीमित करें",
"maxOutputTokensLabel": "अधिकतम आउटपुट टोकन",
"maxTokensGenerateDescription": "प्रतिक्रिया में उत्पन्न करने के लिए अधिकतम टोकन",
"serviceTier": {
"label": "सेवा स्तर",
"tooltip": "API अनुरोधों के तेज़ प्रसंस्करण के लिए, प्राथमिकता प्रसंस्करण सेवा स्तर का प्रयास करें। उच्च विलंबता के साथ कम कीमतों के लिए, फ्लेक्स प्रसंस्करण स्तर का प्रयास करें।",
"standard": "मानक",
"flex": "फ्लेक्स",
"priority": "प्राथमिकता",
"pricingTableTitle": "सेवा स्तर के अनुसार मूल्य निर्धारण (प्रति 1M टोकन मूल्य)",
"columns": {
"tier": "स्तर",
"input": "इनपुट",
"output": "आउटपुट",
"cacheReads": "कैश रीड"
}
}
"maxTokensGenerateDescription": "प्रतिक्रिया में उत्पन्न करने के लिए अधिकतम टोकन"
}

View file

@ -51,8 +51,7 @@
"image": {
"tabs": {
"view": "Gambar"
},
"noData": "Tidak ada data gambar"
}
},
"file": {
"errors": {
@ -72,13 +71,7 @@
"deleteWarning": "Menghapus pesan ini akan menghapus semua pesan selanjutnya dalam percakapan. Apakah kamu ingin melanjutkan?",
"editMessage": "Edit Pesan",
"editWarning": "Mengedit pesan ini akan menghapus semua pesan selanjutnya dalam percakapan. Apakah kamu ingin melanjutkan?",
"editQuestionWithCheckpoint": "Mengedit pesan ini akan menghapus semua pesan selanjutnya dalam percakapan. Apakah kamu juga ingin membatalkan semua perubahan kembali ke checkpoint ini?",
"deleteQuestionWithCheckpoint": "Menghapus pesan ini akan menghapus semua pesan selanjutnya dalam percakapan. Apakah kamu juga ingin membatalkan semua perubahan kembali ke checkpoint ini?",
"editOnly": "Tidak, edit pesan saja",
"deleteOnly": "Tidak, hapus pesan saja",
"restoreToCheckpoint": "Ya, pulihkan checkpoint",
"proceed": "Lanjutkan",
"dontShowAgain": "Jangan tampilkan lagi"
"proceed": "Lanjutkan"
},
"time_ago": {
"just_now": "baru saja",

View file

@ -379,8 +379,6 @@
"ollama": {
"baseUrl": "Base URL (opsional)",
"modelId": "Model ID",
"apiKey": "Ollama API Key",
"apiKeyHelp": "API key opsional untuk instance Ollama yang terautentikasi atau layanan cloud. Biarkan kosong untuk instalasi lokal.",
"description": "Ollama memungkinkan kamu menjalankan model secara lokal di komputer. Untuk instruksi cara memulai, lihat panduan quickstart mereka.",
"warning": "Catatan: Roo Code menggunakan prompt kompleks dan bekerja terbaik dengan model Claude. Model yang kurang mampu mungkin tidak bekerja seperti yang diharapkan."
},
@ -780,10 +778,6 @@
"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"
},
"RUN_SLASH_COMMAND": {
"name": "Aktifkan perintah slash yang dimulai model",
"description": "Ketika diaktifkan, Roo dapat menjalankan perintah slash Anda untuk mengeksekusi alur kerja."
}
},
"promptCaching": {
@ -896,19 +890,5 @@
"includeMaxOutputTokensDescription": "Kirim parameter token output maksimum dalam permintaan API. Beberapa provider mungkin tidak mendukung ini.",
"limitMaxTokensDescription": "Batasi jumlah maksimum token dalam respons",
"maxOutputTokensLabel": "Token output maksimum",
"maxTokensGenerateDescription": "Token maksimum untuk dihasilkan dalam respons",
"serviceTier": {
"label": "Tingkat layanan",
"tooltip": "Untuk pemrosesan permintaan API yang lebih cepat, coba tingkat layanan pemrosesan prioritas. Untuk harga lebih rendah dengan latensi lebih tinggi, coba tingkat pemrosesan fleksibel.",
"standard": "Standar",
"flex": "Fleksibel",
"priority": "Prioritas",
"pricingTableTitle": "Harga berdasarkan tingkat layanan (harga per 1 juta token)",
"columns": {
"tier": "Tingkat",
"input": "Input",
"output": "Output",
"cacheReads": "Pembacaan cache"
}
}
"maxTokensGenerateDescription": "Token maksimum untuk dihasilkan dalam respons"
}

View file

@ -51,8 +51,7 @@
"image": {
"tabs": {
"view": "Immagine"
},
"noData": "Nessun dato immagine"
}
},
"file": {
"errors": {
@ -72,13 +71,7 @@
"deleteWarning": "Eliminando questo messaggio verranno eliminati tutti i messaggi successivi nella conversazione. Vuoi procedere?",
"editMessage": "Modifica Messaggio",
"editWarning": "Modificando questo messaggio verranno eliminati tutti i messaggi successivi nella conversazione. Vuoi procedere?",
"editQuestionWithCheckpoint": "Modificando questo messaggio verranno eliminati tutti i messaggi successivi nella conversazione. Vuoi anche annullare tutte le modifiche fino a questo checkpoint?",
"deleteQuestionWithCheckpoint": "Eliminando questo messaggio verranno eliminati tutti i messaggi successivi nella conversazione. Vuoi anche annullare tutte le modifiche fino a questo checkpoint?",
"editOnly": "No, modifica solo il messaggio",
"deleteOnly": "No, elimina solo il messaggio",
"restoreToCheckpoint": "Sì, ripristina il checkpoint",
"proceed": "Procedi",
"dontShowAgain": "Non mostrare più"
"proceed": "Procedi"
},
"time_ago": {
"just_now": "proprio ora",

View file

@ -375,8 +375,6 @@
"ollama": {
"baseUrl": "URL base (opzionale)",
"modelId": "ID modello",
"apiKey": "Chiave API Ollama",
"apiKeyHelp": "Chiave API opzionale per istanze Ollama autenticate o servizi cloud. Lascia vuoto per installazioni locali.",
"description": "Ollama ti permette di eseguire modelli localmente sul tuo computer. Per iniziare, consulta la guida rapida.",
"warning": "Nota: Roo Code utilizza prompt complessi e funziona meglio con i modelli Claude. I modelli con capacità inferiori potrebbero non funzionare come previsto."
},
@ -751,10 +749,6 @@
"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"
},
"RUN_SLASH_COMMAND": {
"name": "Abilita comandi slash avviati dal modello",
"description": "Quando abilitato, Roo può eseguire i tuoi comandi slash per eseguire flussi di lavoro."
}
},
"promptCaching": {
@ -867,19 +861,5 @@
"includeMaxOutputTokensDescription": "Invia il parametro dei token di output massimi nelle richieste API. Alcuni provider potrebbero non supportarlo.",
"limitMaxTokensDescription": "Limita il numero massimo di token nella risposta",
"maxOutputTokensLabel": "Token di output massimi",
"maxTokensGenerateDescription": "Token massimi da generare nella risposta",
"serviceTier": {
"label": "Livello di servizio",
"tooltip": "Per un'elaborazione più rapida delle richieste API, prova il livello di servizio di elaborazione prioritaria. Per prezzi più bassi con una latenza maggiore, prova il livello di elaborazione flessibile.",
"standard": "Standard",
"flex": "Flessibile",
"priority": "Priorità",
"pricingTableTitle": "Prezzi per livello di servizio (prezzo per 1 milione di token)",
"columns": {
"tier": "Livello",
"input": "Input",
"output": "Output",
"cacheReads": "Letture cache"
}
}
"maxTokensGenerateDescription": "Token massimi da generare nella risposta"
}

View file

@ -51,8 +51,7 @@
"image": {
"tabs": {
"view": "画像"
},
"noData": "画像データなし"
}
},
"file": {
"errors": {
@ -72,13 +71,7 @@
"deleteWarning": "このメッセージを削除すると、会話内の後続のメッセージもすべて削除されます。続行しますか?",
"editMessage": "メッセージを編集",
"editWarning": "このメッセージを編集すると、会話内の後続のメッセージもすべて削除されます。続行しますか?",
"editQuestionWithCheckpoint": "このメッセージを編集すると、会話内の後続のメッセージもすべて削除されます。このチェックポイントまでのすべての変更も元に戻しますか?",
"deleteQuestionWithCheckpoint": "このメッセージを削除すると、会話内の後続のメッセージもすべて削除されます。このチェックポイントまでのすべての変更も元に戻しますか?",
"editOnly": "いいえ、メッセージのみ編集",
"deleteOnly": "いいえ、メッセージのみ削除",
"restoreToCheckpoint": "はい、チェックポイントを復元",
"proceed": "続行",
"dontShowAgain": "今後表示しない"
"proceed": "続行"
},
"time_ago": {
"just_now": "たった今",

View file

@ -375,8 +375,6 @@
"ollama": {
"baseUrl": "ベースURLオプション",
"modelId": "モデルID",
"apiKey": "Ollama APIキー",
"apiKeyHelp": "認証されたOllamaインスタンスやクラウドサービス用のオプションAPIキー。ローカルインストールの場合は空のままにしてください。",
"description": "Ollamaを使用すると、ローカルコンピューターでモデルを実行できます。始め方については、クイックスタートガイドをご覧ください。",
"warning": "注意Roo Codeは複雑なプロンプトを使用し、Claudeモデルで最適に動作します。能力の低いモデルは期待通りに動作しない場合があります。"
},
@ -751,10 +749,6 @@
"modelSelectionDescription": "画像生成に使用するモデルを選択",
"warningMissingKey": "⚠️ 画像生成にはOpenRouter APIキーが必要です。上記で設定してください。",
"successConfigured": "✓ 画像生成が設定され、使用準備完了です"
},
"RUN_SLASH_COMMAND": {
"name": "モデル開始スラッシュコマンドを有効にする",
"description": "有効にすると、Rooがワークフローを実行するためにあなたのスラッシュコマンドを実行できます。"
}
},
"promptCaching": {
@ -867,19 +861,5 @@
"includeMaxOutputTokensDescription": "APIリクエストで最大出力トークンパラメータを送信します。一部のプロバイダーはこれをサポートしていない場合があります。",
"limitMaxTokensDescription": "レスポンスの最大トークン数を制限する",
"maxOutputTokensLabel": "最大出力トークン",
"maxTokensGenerateDescription": "レスポンスで生成する最大トークン数",
"serviceTier": {
"label": "サービスティア",
"tooltip": "APIリクエストをより速く処理するには、優先処理サービスティアをお試しください。低価格でレイテンシが高い場合は、フレックス処理ティアをお試しください。",
"standard": "標準",
"flex": "フレックス",
"priority": "優先",
"pricingTableTitle": "サービスティア別料金100万トークンあたりの価格",
"columns": {
"tier": "ティア",
"input": "入力",
"output": "出力",
"cacheReads": "キャッシュ読み取り"
}
}
"maxTokensGenerateDescription": "レスポンスで生成する最大トークン数"
}

View file

@ -51,8 +51,7 @@
"image": {
"tabs": {
"view": "이미지"
},
"noData": "이미지 데이터 없음"
}
},
"file": {
"errors": {
@ -72,13 +71,7 @@
"deleteWarning": "이 메시지를 삭제하면 대화의 모든 후속 메시지가 삭제됩니다. 계속하시겠습니까?",
"editMessage": "메시지 편집",
"editWarning": "이 메시지를 편집하면 대화의 모든 후속 메시지가 삭제됩니다. 계속하시겠습니까?",
"editQuestionWithCheckpoint": "이 메시지를 편집하면 대화의 모든 후속 메시지가 삭제됩니다. 이 체크포인트까지의 모든 변경사항도 되돌리시겠습니까?",
"deleteQuestionWithCheckpoint": "이 메시지를 삭제하면 대화의 모든 후속 메시지가 삭제됩니다. 이 체크포인트까지의 모든 변경사항도 되돌리시겠습니까?",
"editOnly": "아니요, 메시지만 편집",
"deleteOnly": "아니요, 메시지만 삭제",
"restoreToCheckpoint": "예, 체크포인트 복원",
"proceed": "계속",
"dontShowAgain": "다시 표시하지 않음"
"proceed": "계속"
},
"time_ago": {
"just_now": "방금",

View file

@ -375,8 +375,6 @@
"ollama": {
"baseUrl": "기본 URL (선택사항)",
"modelId": "모델 ID",
"apiKey": "Ollama API 키",
"apiKeyHelp": "인증된 Ollama 인스턴스나 클라우드 서비스용 선택적 API 키. 로컬 설치의 경우 비워두세요.",
"description": "Ollama를 사용하면 컴퓨터에서 로컬로 모델을 실행할 수 있습니다. 시작하는 방법은 빠른 시작 가이드를 참조하세요.",
"warning": "참고: Roo Code는 복잡한 프롬프트를 사용하며 Claude 모델에서 가장 잘 작동합니다. 덜 강력한 모델은 예상대로 작동하지 않을 수 있습니다."
},
@ -751,10 +749,6 @@
"modelSelectionDescription": "이미지 생성에 사용할 모델을 선택하세요",
"warningMissingKey": "⚠️ 이미지 생성에는 OpenRouter API 키가 필요합니다. 위에서 설정해주세요.",
"successConfigured": "✓ 이미지 생성이 구성되었으며 사용할 준비가 되었습니다"
},
"RUN_SLASH_COMMAND": {
"name": "모델 시작 슬래시 명령 활성화",
"description": "활성화되면 Roo가 워크플로를 실행하기 위해 슬래시 명령을 실행할 수 있습니다."
}
},
"promptCaching": {
@ -867,19 +861,5 @@
"includeMaxOutputTokensDescription": "API 요청에서 최대 출력 토큰 매개변수를 전송합니다. 일부 제공업체는 이를 지원하지 않을 수 있습니다.",
"limitMaxTokensDescription": "응답에서 최대 토큰 수 제한",
"maxOutputTokensLabel": "최대 출력 토큰",
"maxTokensGenerateDescription": "응답에서 생성할 최대 토큰 수",
"serviceTier": {
"label": "서비스 등급",
"tooltip": "API 요청을 더 빠르게 처리하려면 우선 처리 서비스 등급을 사용해 보세요. 더 낮은 가격에 더 높은 지연 시간을 원하시면 플렉스 처리 등급을 사용해 보세요.",
"standard": "표준",
"flex": "플렉스",
"priority": "우선",
"pricingTableTitle": "서비스 등급별 가격 (100만 토큰당 가격)",
"columns": {
"tier": "등급",
"input": "입력",
"output": "출력",
"cacheReads": "캐시 읽기"
}
}
"maxTokensGenerateDescription": "응답에서 생성할 최대 토큰 수"
}

View file

@ -51,8 +51,7 @@
"image": {
"tabs": {
"view": "Afbeelding"
},
"noData": "Geen afbeeldingsgegevens"
}
},
"file": {
"errors": {
@ -72,13 +71,7 @@
"deleteWarning": "Het verwijderen van dit bericht zal alle volgende berichten in het gesprek verwijderen. Wil je doorgaan?",
"editMessage": "Bericht Bewerken",
"editWarning": "Het bewerken van dit bericht zal alle volgende berichten in het gesprek verwijderen. Wil je doorgaan?",
"editQuestionWithCheckpoint": "Het bewerken van dit bericht zal alle latere berichten in het gesprek verwijderen. Wil je ook alle wijzigingen ongedaan maken tot dit checkpoint?",
"deleteQuestionWithCheckpoint": "Het verwijderen van dit bericht zal alle latere berichten in het gesprek verwijderen. Wil je ook alle wijzigingen ongedaan maken tot dit checkpoint?",
"editOnly": "Nee, alleen bericht bewerken",
"deleteOnly": "Nee, alleen bericht verwijderen",
"restoreToCheckpoint": "Ja, checkpoint herstellen",
"proceed": "Doorgaan",
"dontShowAgain": "Niet meer tonen"
"proceed": "Doorgaan"
},
"time_ago": {
"just_now": "zojuist",

View file

@ -375,8 +375,6 @@
"ollama": {
"baseUrl": "Basis-URL (optioneel)",
"modelId": "Model-ID",
"apiKey": "Ollama API-sleutel",
"apiKeyHelp": "Optionele API-sleutel voor geauthenticeerde Ollama-instanties of cloudservices. Laat leeg voor lokale installaties.",
"description": "Ollama laat je modellen lokaal op je computer draaien. Zie hun quickstart-gids voor instructies.",
"warning": "Let op: Roo Code gebruikt complexe prompts en werkt het beste met Claude-modellen. Minder krachtige modellen werken mogelijk niet zoals verwacht."
},
@ -751,10 +749,6 @@
"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"
},
"RUN_SLASH_COMMAND": {
"name": "Model-geïnitieerde slash-commando's inschakelen",
"description": "Wanneer ingeschakeld, kan Roo je slash-commando's uitvoeren om workflows uit te voeren."
}
},
"promptCaching": {
@ -867,19 +861,5 @@
"includeMaxOutputTokensDescription": "Stuur maximale output tokens parameter in API-verzoeken. Sommige providers ondersteunen dit mogelijk niet.",
"limitMaxTokensDescription": "Beperk het maximale aantal tokens in het antwoord",
"maxOutputTokensLabel": "Maximale output tokens",
"maxTokensGenerateDescription": "Maximale tokens om te genereren in het antwoord",
"serviceTier": {
"label": "Serviceniveau",
"tooltip": "Voor snellere verwerking van API-verzoeken, probeer het prioriteitsverwerkingsniveau. Voor lagere prijzen met hogere latentie, probeer het flexverwerkingsniveau.",
"standard": "Standaard",
"flex": "Flex",
"priority": "Prioriteit",
"pricingTableTitle": "Prijzen per serviceniveau (prijs per 1M tokens)",
"columns": {
"tier": "Niveau",
"input": "Invoer",
"output": "Uitvoer",
"cacheReads": "Cache leest"
}
}
"maxTokensGenerateDescription": "Maximale tokens om te genereren in het antwoord"
}

View file

@ -51,8 +51,7 @@
"image": {
"tabs": {
"view": "Obraz"
},
"noData": "Brak danych obrazu"
}
},
"file": {
"errors": {
@ -72,13 +71,7 @@
"deleteWarning": "Usunięcie tej wiadomości spowoduje usunięcie wszystkich kolejnych wiadomości w rozmowie. Czy chcesz kontynuować?",
"editMessage": "Edytuj Wiadomość",
"editWarning": "Edycja tej wiadomości spowoduje usunięcie wszystkich kolejnych wiadomości w rozmowie. Czy chcesz kontynuować?",
"editQuestionWithCheckpoint": "Edycja tej wiadomości spowoduje usunięcie wszystkich późniejszych wiadomości w rozmowie. Czy chcesz również cofnąć wszystkie zmiany do tego punktu kontrolnego?",
"deleteQuestionWithCheckpoint": "Usunięcie tej wiadomości spowoduje usunięcie wszystkich późniejszych wiadomości w rozmowie. Czy chcesz również cofnąć wszystkie zmiany do tego punktu kontrolnego?",
"editOnly": "Nie, tylko edytuj wiadomość",
"deleteOnly": "Nie, tylko usuń wiadomość",
"restoreToCheckpoint": "Tak, przywróć punkt kontrolny",
"proceed": "Kontynuuj",
"dontShowAgain": "Nie pokazuj ponownie"
"proceed": "Kontynuuj"
},
"time_ago": {
"just_now": "przed chwilą",

View file

@ -375,8 +375,6 @@
"ollama": {
"baseUrl": "URL bazowy (opcjonalnie)",
"modelId": "ID modelu",
"apiKey": "Klucz API Ollama",
"apiKeyHelp": "Opcjonalny klucz API dla uwierzytelnionych instancji Ollama lub usług chmurowych. Pozostaw puste dla instalacji lokalnych.",
"description": "Ollama pozwala na lokalne uruchamianie modeli na twoim komputerze. Aby rozpocząć, zapoznaj się z przewodnikiem szybkiego startu.",
"warning": "Uwaga: Roo Code używa złożonych podpowiedzi i działa najlepiej z modelami Claude. Modele o niższych możliwościach mogą nie działać zgodnie z oczekiwaniami."
},
@ -751,10 +749,6 @@
"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"
},
"RUN_SLASH_COMMAND": {
"name": "Włącz polecenia slash inicjowane przez model",
"description": "Gdy włączone, Roo może uruchamiać twoje polecenia slash w celu wykonywania przepływów pracy."
}
},
"promptCaching": {
@ -867,19 +861,5 @@
"includeMaxOutputTokensDescription": "Wyślij parametr maksymalnych tokenów wyjściowych w żądaniach API. Niektórzy dostawcy mogą tego nie obsługiwać.",
"limitMaxTokensDescription": "Ogranicz maksymalną liczbę tokenów w odpowiedzi",
"maxOutputTokensLabel": "Maksymalne tokeny wyjściowe",
"maxTokensGenerateDescription": "Maksymalne tokeny do wygenerowania w odpowiedzi",
"serviceTier": {
"label": "Poziom usług",
"tooltip": "Aby szybciej przetwarzać żądania API, wypróbuj priorytetowy poziom usług. Aby uzyskać niższe ceny przy wyższej latencji, wypróbuj elastyczny poziom usług.",
"standard": "Standardowy",
"flex": "Elastyczny",
"priority": "Priorytetowy",
"pricingTableTitle": "Cennik według poziomu usług (cena za 1 mln tokenów)",
"columns": {
"tier": "Poziom",
"input": "Wejście",
"output": "Wyjście",
"cacheReads": "Odczyty z pamięci podręcznej"
}
}
"maxTokensGenerateDescription": "Maksymalne tokeny do wygenerowania w odpowiedzi"
}

View file

@ -51,8 +51,7 @@
"image": {
"tabs": {
"view": "Imagem"
},
"noData": "Nenhum dado de imagem"
}
},
"file": {
"errors": {
@ -72,13 +71,7 @@
"deleteWarning": "Excluir esta mensagem irá excluir todas as mensagens subsequentes na conversa. Deseja prosseguir?",
"editMessage": "Editar Mensagem",
"editWarning": "Editar esta mensagem irá excluir todas as mensagens subsequentes na conversa. Deseja prosseguir?",
"editQuestionWithCheckpoint": "Editar esta mensagem irá excluir todas as mensagens posteriores na conversa. Você também deseja desfazer todas as alterações até este checkpoint?",
"deleteQuestionWithCheckpoint": "Excluir esta mensagem irá excluir todas as mensagens posteriores na conversa. Você também deseja desfazer todas as alterações até este checkpoint?",
"editOnly": "Não, apenas editar a mensagem",
"deleteOnly": "Não, apenas excluir a mensagem",
"restoreToCheckpoint": "Sim, restaurar o checkpoint",
"proceed": "Prosseguir",
"dontShowAgain": "Não mostrar novamente"
"proceed": "Prosseguir"
},
"time_ago": {
"just_now": "agora mesmo",

View file

@ -375,8 +375,6 @@
"ollama": {
"baseUrl": "URL Base (opcional)",
"modelId": "ID do Modelo",
"apiKey": "Chave API Ollama",
"apiKeyHelp": "Chave API opcional para instâncias Ollama autenticadas ou serviços em nuvem. Deixe vazio para instalações locais.",
"description": "O Ollama permite que você execute modelos localmente em seu computador. Para instruções sobre como começar, veja o guia de início rápido deles.",
"warning": "Nota: O Roo Code usa prompts complexos e funciona melhor com modelos Claude. Modelos menos capazes podem não funcionar como esperado."
},
@ -751,10 +749,6 @@
"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"
},
"RUN_SLASH_COMMAND": {
"name": "Ativar comandos slash iniciados pelo modelo",
"description": "Quando ativado, Roo pode executar seus comandos slash para executar fluxos de trabalho."
}
},
"promptCaching": {
@ -867,19 +861,5 @@
"includeMaxOutputTokensDescription": "Enviar parâmetro de tokens máximos de saída nas solicitações de API. Alguns provedores podem não suportar isso.",
"limitMaxTokensDescription": "Limitar o número máximo de tokens na resposta",
"maxOutputTokensLabel": "Tokens máximos de saída",
"maxTokensGenerateDescription": "Tokens máximos para gerar na resposta",
"serviceTier": {
"label": "Nível de serviço",
"tooltip": "Para um processamento mais rápido das solicitações de API, experimente o nível de serviço de processamento prioritário. Para preços mais baixos com maior latência, experimente o nível de processamento flexível.",
"standard": "Padrão",
"flex": "Flexível",
"priority": "Prioritário",
"pricingTableTitle": "Preços por nível de serviço (preço por 1 milhão de tokens)",
"columns": {
"tier": "Nível",
"input": "Entrada",
"output": "Saída",
"cacheReads": "Leituras de cache"
}
}
"maxTokensGenerateDescription": "Tokens máximos para gerar na resposta"
}

View file

@ -51,8 +51,7 @@
"image": {
"tabs": {
"view": "Изображение"
},
"noData": "Нет данных изображения"
}
},
"file": {
"errors": {
@ -72,13 +71,7 @@
"deleteWarning": "Удаление этого сообщения приведет к удалению всех последующих сообщений в разговоре. Хотите продолжить?",
"editMessage": "Редактировать Сообщение",
"editWarning": "Редактирование этого сообщения приведет к удалению всех последующих сообщений в разговоре. Хотите продолжить?",
"editQuestionWithCheckpoint": "Редактирование этого сообщения приведет к удалению всех последующих сообщений в разговоре. Хотите также отменить все изменения до этой контрольной точки?",
"deleteQuestionWithCheckpoint": "Удаление этого сообщения приведет к удалению всех последующих сообщений в разговоре. Хотите также отменить все изменения до этой контрольной точки?",
"editOnly": "Нет, только редактировать сообщение",
"deleteOnly": "Нет, только удалить сообщение",
"restoreToCheckpoint": "Да, восстановить контрольную точку",
"proceed": "Продолжить",
"dontShowAgain": "Больше не показывать"
"proceed": "Продолжить"
},
"time_ago": {
"just_now": "только что",

View file

@ -375,8 +375,6 @@
"ollama": {
"baseUrl": "Базовый URL (опционально)",
"modelId": "ID модели",
"apiKey": "API-ключ Ollama",
"apiKeyHelp": "Опциональный API-ключ для аутентифицированных экземпляров Ollama или облачных сервисов. Оставьте пустым для локальных установок.",
"description": "Ollama позволяет запускать модели локально на вашем компьютере. Для начала ознакомьтесь с кратким руководством.",
"warning": "Примечание: Roo Code использует сложные подсказки и лучше всего работает с моделями Claude. Менее мощные модели могут работать некорректно."
},
@ -751,10 +749,6 @@
"modelSelectionDescription": "Выберите модель для генерации изображений",
"warningMissingKey": "⚠️ API-ключ OpenRouter необходим для генерации изображений. Настройте его выше.",
"successConfigured": "✓ Генерация изображений настроена и готова к использованию"
},
"RUN_SLASH_COMMAND": {
"name": "Включить слэш-команды, инициированные моделью",
"description": "Когда включено, Roo может выполнять ваши слэш-команды для выполнения рабочих процессов."
}
},
"promptCaching": {
@ -867,19 +861,5 @@
"includeMaxOutputTokensDescription": "Отправлять параметр максимальных выходных токенов в API-запросах. Некоторые провайдеры могут не поддерживать это.",
"limitMaxTokensDescription": "Ограничить максимальное количество токенов в ответе",
"maxOutputTokensLabel": "Максимальные выходные токены",
"maxTokensGenerateDescription": "Максимальные токены для генерации в ответе",
"serviceTier": {
"label": "Уровень обслуживания",
"tooltip": "Для более быстрой обработки запросов API попробуйте уровень обслуживания с приоритетной обработкой. Для более низких цен с более высокой задержкой попробуйте уровень гибкой обработки.",
"standard": "Стандартный",
"flex": "Гибкий",
"priority": "Приоритетный",
"pricingTableTitle": "Цены по уровням обслуживания (цена за 1 млн токенов)",
"columns": {
"tier": "Уровень",
"input": "Вход",
"output": "Выход",
"cacheReads": "Чтения из кэша"
}
}
"maxTokensGenerateDescription": "Максимальные токены для генерации в ответе"
}

View file

@ -51,8 +51,7 @@
"image": {
"tabs": {
"view": "Resim"
},
"noData": "Resim verisi yok"
}
},
"file": {
"errors": {
@ -72,13 +71,7 @@
"deleteWarning": "Bu mesajı silmek, konuşmadaki sonraki tüm mesajları da silecektir. Devam etmek istiyor musun?",
"editMessage": "Mesajı Düzenle",
"editWarning": "Bu mesajı düzenlemek, konuşmadaki sonraki tüm mesajları da silecektir. Devam etmek istiyor musun?",
"editQuestionWithCheckpoint": "Bu mesajı düzenlemek, konuşmadaki sonraki tüm mesajları da silecektir. Bu kontrol noktasına kadar olan tüm değişiklikleri de geri almak istiyor musun?",
"deleteQuestionWithCheckpoint": "Bu mesajı silmek, konuşmadaki sonraki tüm mesajları da silecektir. Bu kontrol noktasına kadar olan tüm değişiklikleri de geri almak istiyor musun?",
"editOnly": "Hayır, sadece mesajı düzenle",
"deleteOnly": "Hayır, sadece mesajı sil",
"restoreToCheckpoint": "Evet, kontrol noktasını geri yükle",
"proceed": "Devam Et",
"dontShowAgain": "Tekrar gösterme"
"proceed": "Devam Et"
},
"time_ago": {
"just_now": "şimdi",

View file

@ -375,8 +375,6 @@
"ollama": {
"baseUrl": "Temel URL (İsteğe bağlı)",
"modelId": "Model Kimliği",
"apiKey": "Ollama API Anahtarı",
"apiKeyHelp": "Kimlik doğrulamalı Ollama örnekleri veya bulut hizmetleri için isteğe bağlı API anahtarı. Yerel kurulumlar için boş bırakın.",
"description": "Ollama, modelleri bilgisayarınızda yerel olarak çalıştırmanıza olanak tanır. Başlamak için hızlı başlangıç kılavuzlarına bakın.",
"warning": "Not: Roo Code karmaşık istemler kullanır ve Claude modelleriyle en iyi şekilde çalışır. Daha az yetenekli modeller beklendiği gibi çalışmayabilir."
},
@ -751,10 +749,6 @@
"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"
},
"RUN_SLASH_COMMAND": {
"name": "Model tarafından başlatılan slash komutlarını etkinleştir",
"description": "Etkinleştirildiğinde, Roo iş akışlarını yürütmek için slash komutlarınızı çalıştırabilir."
}
},
"promptCaching": {
@ -867,19 +861,5 @@
"includeMaxOutputTokensDescription": "API isteklerinde maksimum çıktı token parametresini gönder. Bazı sağlayıcılar bunu desteklemeyebilir.",
"limitMaxTokensDescription": "Yanıttaki maksimum token sayısını sınırla",
"maxOutputTokensLabel": "Maksimum çıktı tokenları",
"maxTokensGenerateDescription": "Yanıtta oluşturulacak maksimum token sayısı",
"serviceTier": {
"label": "Hizmet seviyesi",
"tooltip": "Daha hızlı API isteği işleme için öncelikli işleme hizmeti seviyesini deneyin. Daha düşük gecikme süresiyle daha düşük fiyatlar için esnek işleme seviyesini deneyin.",
"standard": "Standart",
"flex": "Esnek",
"priority": "Öncelik",
"pricingTableTitle": "Hizmet seviyesine göre fiyatlandırma (1 milyon token başına fiyat)",
"columns": {
"tier": "Seviye",
"input": "Giriş",
"output": ıkış",
"cacheReads": "Önbellek okumaları"
}
}
"maxTokensGenerateDescription": "Yanıtta oluşturulacak maksimum token sayısı"
}

View file

@ -51,8 +51,7 @@
"image": {
"tabs": {
"view": "Hình ảnh"
},
"noData": "Không có dữ liệu hình ảnh"
}
},
"file": {
"errors": {
@ -72,13 +71,7 @@
"deleteWarning": "Xóa tin nhắn này sẽ xóa tất cả các tin nhắn tiếp theo trong cuộc trò chuyện. Bạn có muốn tiếp tục không?",
"editMessage": "Chỉnh Sửa Tin Nhắn",
"editWarning": "Chỉnh sửa tin nhắn này sẽ xóa tất cả các tin nhắn tiếp theo trong cuộc trò chuyện. Bạn có muốn tiếp tục không?",
"editQuestionWithCheckpoint": "Chỉnh sửa tin nhắn này sẽ xóa tất cả các tin nhắn sau đó trong cuộc trò chuyện. Bạn có muốn hoàn tác tất cả các thay đổi về checkpoint này không?",
"deleteQuestionWithCheckpoint": "Xóa tin nhắn này sẽ xóa tất cả các tin nhắn sau đó trong cuộc trò chuyện. Bạn có muốn hoàn tác tất cả các thay đổi về checkpoint này không?",
"editOnly": "Không, chỉ chỉnh sửa tin nhắn",
"deleteOnly": "Không, chỉ xóa tin nhắn",
"restoreToCheckpoint": "Có, khôi phục checkpoint",
"proceed": "Tiếp Tục",
"dontShowAgain": "Không hiển thị lại"
"proceed": "Tiếp Tục"
},
"time_ago": {
"just_now": "vừa xong",

View file

@ -375,8 +375,6 @@
"ollama": {
"baseUrl": "URL cơ sở (tùy chọn)",
"modelId": "ID mô hình",
"apiKey": "Khóa API Ollama",
"apiKeyHelp": "Khóa API tùy chọn cho các phiên bản Ollama đã xác thực hoặc dịch vụ đám mây. Để trống cho cài đặt cục bộ.",
"description": "Ollama cho phép bạn chạy các mô hình cục bộ trên máy tính của bạn. Để biết hướng dẫn về cách bắt đầu, xem hướng dẫn nhanh của họ.",
"warning": "Lưu ý: Roo Code sử dụng các lời nhắc phức tạp và hoạt động tốt nhất với các mô hình Claude. Các mô hình kém mạnh hơn có thể không hoạt động như mong đợi."
},
@ -751,10 +749,6 @@
"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"
},
"RUN_SLASH_COMMAND": {
"name": "Bật lệnh slash do mô hình khởi tạo",
"description": "Khi được bật, Roo có thể chạy các lệnh slash của bạn để thực hiện các quy trình làm việc."
}
},
"promptCaching": {
@ -867,19 +861,5 @@
"includeMaxOutputTokensDescription": "Gửi tham số token đầu ra tối đa trong các yêu cầu API. Một số nhà cung cấp có thể không hỗ trợ điều này.",
"limitMaxTokensDescription": "Giới hạn số lượng token tối đa trong phản hồi",
"maxOutputTokensLabel": "Token đầu ra tối đa",
"maxTokensGenerateDescription": "Token tối đa để tạo trong phản hồi",
"serviceTier": {
"label": "Cấp độ dịch vụ",
"tooltip": "Để xử lý các yêu cầu API nhanh hơn, hãy thử cấp độ dịch vụ xử lý ưu tiên. Để có giá thấp hơn với độ trễ cao hơn, hãy thử cấp độ xử lý linh hoạt.",
"standard": "Tiêu chuẩn",
"flex": "Linh hoạt",
"priority": "Ưu tiên",
"pricingTableTitle": "Giá theo cấp độ dịch vụ (giá mỗi 1 triệu token)",
"columns": {
"tier": "Cấp độ",
"input": "Đầu vào",
"output": "Đầu ra",
"cacheReads": "Lượt đọc bộ nhớ đệm"
}
}
"maxTokensGenerateDescription": "Token tối đa để tạo trong phản hồi"
}

View file

@ -51,8 +51,7 @@
"image": {
"tabs": {
"view": "图像"
},
"noData": "无图片数据"
}
},
"file": {
"errors": {
@ -72,13 +71,7 @@
"deleteWarning": "删除此消息将删除对话中的所有后续消息。是否继续?",
"editMessage": "编辑消息",
"editWarning": "编辑此消息将删除对话中的所有后续消息。是否继续?",
"editQuestionWithCheckpoint": "编辑此消息将删除对话中的所有后续消息。是否同时将所有变更撤销到此存档点?",
"deleteQuestionWithCheckpoint": "删除此消息将删除对话中的所有后续消息。是否同时将所有变更撤销到此存档点?",
"editOnly": "否,仅编辑消息",
"deleteOnly": "否,仅删除消息",
"restoreToCheckpoint": "是,恢复存档点",
"proceed": "继续",
"dontShowAgain": "不再显示"
"proceed": "继续"
},
"time_ago": {
"just_now": "刚刚",

View file

@ -375,8 +375,6 @@
"ollama": {
"baseUrl": "基础 URL可选",
"modelId": "模型 ID",
"apiKey": "Ollama API 密钥",
"apiKeyHelp": "用于已认证 Ollama 实例或云服务的可选 API 密钥。本地安装请留空。",
"description": "Ollama 允许您在本地计算机上运行模型。有关如何开始使用的说明,请参阅其快速入门指南。",
"warning": "注意Roo Code 使用复杂的提示,与 Claude 模型配合最佳。功能较弱的模型可能无法按预期工作。"
},
@ -751,10 +749,6 @@
"modelSelectionDescription": "选择用于图像生成的模型",
"warningMissingKey": "⚠️ 图像生成需要 OpenRouter API 密钥。请在上方配置。",
"successConfigured": "✓ 图像生成已配置完成,可以使用"
},
"RUN_SLASH_COMMAND": {
"name": "启用模型发起的斜杠命令",
"description": "启用后 Roo 可运行斜杠命令执行工作流程。"
}
},
"promptCaching": {
@ -867,19 +861,5 @@
"includeMaxOutputTokensDescription": "在 API 请求中发送最大输出 Token 参数。某些提供商可能不支持此功能。",
"limitMaxTokensDescription": "限制响应中的最大 Token 数量",
"maxOutputTokensLabel": "最大输出 Token 数",
"maxTokensGenerateDescription": "响应中生成的最大 Token 数",
"serviceTier": {
"label": "服务等级",
"tooltip": "为加快API请求处理速度请尝试优先处理服务等级。为获得更低价格但延迟较高请尝试灵活处理等级。",
"standard": "标准",
"flex": "灵活",
"priority": "优先",
"pricingTableTitle": "按服务等级定价 (每百万Token价格)",
"columns": {
"tier": "等级",
"input": "输入",
"output": "输出",
"cacheReads": "缓存读取"
}
}
"maxTokensGenerateDescription": "响应中生成的最大 Token 数"
}

View file

@ -51,8 +51,7 @@
"image": {
"tabs": {
"view": "圖像"
},
"noData": "無圖片資料"
}
},
"file": {
"errors": {
@ -71,14 +70,8 @@
"deleteMessage": "刪除訊息",
"deleteWarning": "刪除此訊息將會刪除對話中所有後續的訊息。您要繼續嗎?",
"editMessage": "編輯訊息",
"editWarning": "編輯此訊息將刪除對話中的所有後續訊息。是否繼續?",
"editQuestionWithCheckpoint": "編輯此訊息將刪除對話中的所有後續訊息。是否同時將所有變更撤銷到此存檔點?",
"deleteQuestionWithCheckpoint": "刪除此訊息將刪除對話中的所有後續訊息。是否同時將所有變更撤銷到此存檔點?",
"editOnly": "否,僅編輯訊息",
"deleteOnly": "否,僅刪除訊息",
"restoreToCheckpoint": "是,恢復存檔點",
"proceed": "繼續",
"dontShowAgain": "不再顯示"
"editWarning": "編輯此訊息將會刪除對話中所有後續的訊息。您要繼續嗎?",
"proceed": "繼續"
},
"time_ago": {
"just_now": "剛剛",

View file

@ -375,8 +375,6 @@
"ollama": {
"baseUrl": "基礎 URL選用",
"modelId": "模型 ID",
"apiKey": "Ollama API 金鑰",
"apiKeyHelp": "用於已認證 Ollama 執行個體或雲端服務的選用 API 金鑰。本機安裝請留空。",
"description": "Ollama 允許您在本機電腦執行模型。請參閱快速入門指南。",
"warning": "注意Roo Code 使用複雜提示,與 Claude 模型搭配最佳。功能較弱的模型可能無法正常運作。"
},
@ -731,7 +729,7 @@
},
"PREVENT_FOCUS_DISRUPTION": {
"name": "背景編輯",
"description": "啟用後可防止編輯器焦點中斷。檔案編輯會在背景進行,不會開啟 diff 檢視或搶奪焦點。您可以在 Roo 進行變更時繼續不受擾地工作。檔案可能會在不獲得焦點的情況下開啟以捕獲診斷,或保持完全關閉。"
"description": "啟用後可防止編輯器焦點中斷。檔案編輯會在背景進行,不會開啟 diff 檢視或搶奪焦點。您可以在 Roo 進行變更時繼續不受擾地工作。檔案可能會在不獲得焦點的情況下開啟以捕獲診斷,或保持完全關閉。"
},
"ASSISTANT_MESSAGE_PARSER": {
"name": "使用全新訊息解析器",
@ -751,10 +749,6 @@
"modelSelectionDescription": "選擇用於圖像生成的模型",
"warningMissingKey": "⚠️ 圖像生成需要 OpenRouter API 金鑰。請在上方設定。",
"successConfigured": "✓ 圖像生成已設定完成並準備使用"
},
"RUN_SLASH_COMMAND": {
"name": "啟用模型啟動的斜線命令",
"description": "啟用時Roo 可以執行您的斜線命令來執行工作流程。"
}
},
"promptCaching": {
@ -867,19 +861,5 @@
"includeMaxOutputTokensDescription": "在 API 請求中傳送最大輸出 Token 參數。某些提供商可能不支援此功能。",
"limitMaxTokensDescription": "限制回應中的最大 Token 數量",
"maxOutputTokensLabel": "最大輸出 Token 數",
"maxTokensGenerateDescription": "回應中產生的最大 Token 數",
"serviceTier": {
"label": "服務層級",
"tooltip": "若需更快的 API 請求處理,請嘗試優先處理服務層級。若需較低價格但延遲較高,請嘗試彈性處理層級。",
"standard": "標準",
"flex": "彈性",
"priority": "優先",
"pricingTableTitle": "按服務層級定價(每百萬 Token 價格)",
"columns": {
"tier": "層級",
"input": "輸入",
"output": "輸出",
"cacheReads": "快取讀取"
}
}
"maxTokensGenerateDescription": "回應中產生的最大 Token 數"
}