diff --git a/packages/types/src/tool-params.ts b/packages/types/src/tool-params.ts index 36f69f71001..f8708b0c2b4 100644 --- a/packages/types/src/tool-params.ts +++ b/packages/types/src/tool-params.ts @@ -23,11 +23,12 @@ export interface Size { } export interface BrowserActionParams { - action: "launch" | "click" | "hover" | "type" | "scroll_down" | "scroll_up" | "resize" | "close" + action: "launch" | "click" | "hover" | "type" | "scroll_down" | "scroll_up" | "resize" | "close" | "screenshot" url?: string coordinate?: Coordinate size?: Size text?: string + path?: string } export interface GenerateImageParams { diff --git a/src/core/assistant-message/NativeToolCallParser.ts b/src/core/assistant-message/NativeToolCallParser.ts index d9b810c7ca8..6bdefb107c3 100644 --- a/src/core/assistant-message/NativeToolCallParser.ts +++ b/src/core/assistant-message/NativeToolCallParser.ts @@ -406,6 +406,7 @@ export class NativeToolCallParser { coordinate: partialArgs.coordinate, size: partialArgs.size, text: partialArgs.text, + path: partialArgs.path, } } break @@ -645,6 +646,7 @@ export class NativeToolCallParser { coordinate: args.coordinate, size: args.size, text: args.text, + path: args.path, } as NativeArgsFor } break diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-computer-use-support.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-computer-use-support.snap index 571070ac0e0..affd76ade5b 100644 --- a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-computer-use-support.snap +++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-computer-use-support.snap @@ -247,6 +247,10 @@ Parameters: - Use with the `size` parameter to specify the new size. * scroll_down: Scroll down the page by one page height. * scroll_up: Scroll up the page by one page height. + * screenshot: Take a screenshot and save it to a file. + - Use with the `path` parameter to specify the destination file path. + - Supported formats: .png, .jpeg, .webp + - Example: `screenshot` with `screenshots/result.png` * close: Close the Puppeteer-controlled browser instance. This **must always be the final browser action**. - Example: `close` - url: (optional) Use this for providing the URL for the `launch` action. @@ -264,6 +268,9 @@ Parameters: * Example: 1280,720 - text: (optional) Use this for providing the text for the `type` action. * Example: Hello, world! +- path: (optional) File path for the `screenshot` action. Path is relative to the workspace. + * Supported formats: .png, .jpeg, .webp + * Example: screenshots/my-screenshot.png Usage: Action to perform (e.g., launch, click, type, press, scroll_down, scroll_up, close) @@ -284,6 +291,12 @@ Example: Requesting to click on the element at coordinates 450,300 on a 1024x768 450,300@1024x768 +Example: Taking a screenshot and saving it to a file + +screenshot +screenshots/result.png + + ## ask_followup_question Description: Ask the user a question to gather additional information needed to complete the task. Use when you need clarification or more details to proceed effectively. diff --git a/src/core/prompts/tools/browser-action.ts b/src/core/prompts/tools/browser-action.ts index 85cc2d41562..88c7343d0a5 100644 --- a/src/core/prompts/tools/browser-action.ts +++ b/src/core/prompts/tools/browser-action.ts @@ -39,6 +39,10 @@ Parameters: - Use with the \`size\` parameter to specify the new size. * scroll_down: Scroll down the page by one page height. * scroll_up: Scroll up the page by one page height. + * screenshot: Take a screenshot and save it to a file. + - Use with the \`path\` parameter to specify the destination file path. + - Supported formats: .png, .jpeg, .webp + - Example: \`screenshot\` with \`screenshots/result.png\` * close: Close the Puppeteer-controlled browser instance. This **must always be the final browser action**. - Example: \`close\` - url: (optional) Use this for providing the URL for the \`launch\` action. @@ -56,6 +60,9 @@ Parameters: * Example: 1280,720 - text: (optional) Use this for providing the text for the \`type\` action. * Example: Hello, world! +- path: (optional) File path for the \`screenshot\` action. Path is relative to the workspace. + * Supported formats: .png, .jpeg, .webp + * Example: screenshots/my-screenshot.png Usage: Action to perform (e.g., launch, click, type, press, scroll_down, scroll_up, close) @@ -74,5 +81,11 @@ Example: Requesting to click on the element at coordinates 450,300 on a 1024x768 click 450,300@1024x768 + + +Example: Taking a screenshot and saving it to a file + +screenshot +screenshots/result.png ` } diff --git a/src/core/prompts/tools/native-tools/browser_action.ts b/src/core/prompts/tools/native-tools/browser_action.ts index 0013a2e3935..0068373313f 100644 --- a/src/core/prompts/tools/native-tools/browser_action.ts +++ b/src/core/prompts/tools/native-tools/browser_action.ts @@ -21,6 +21,8 @@ const SIZE_PARAMETER_DESCRIPTION = `Viewport dimensions for the resize action in const TEXT_PARAMETER_DESCRIPTION = `Text to type when performing the type action, or key name to press when performing the press action (e.g., 'Enter', 'Tab', 'Escape')` +const PATH_PARAMETER_DESCRIPTION = `File path where the screenshot should be saved (relative to workspace). Required for screenshot action. Supports .png, .jpeg, and .webp extensions. Example: 'screenshots/result.png'` + export default { type: "function", function: { @@ -33,7 +35,18 @@ export default { action: { type: "string", description: ACTION_PARAMETER_DESCRIPTION, - enum: ["launch", "click", "hover", "type", "press", "scroll_down", "scroll_up", "resize", "close"], + enum: [ + "launch", + "click", + "hover", + "type", + "press", + "scroll_down", + "scroll_up", + "resize", + "close", + "screenshot", + ], }, url: { type: ["string", "null"], @@ -51,6 +64,10 @@ export default { type: ["string", "null"], description: TEXT_PARAMETER_DESCRIPTION, }, + path: { + type: ["string", "null"], + description: PATH_PARAMETER_DESCRIPTION, + }, }, required: ["action"], additionalProperties: false, diff --git a/src/core/tools/BrowserActionTool.ts b/src/core/tools/BrowserActionTool.ts index 72212e926c0..d1e8c678763 100644 --- a/src/core/tools/BrowserActionTool.ts +++ b/src/core/tools/BrowserActionTool.ts @@ -23,6 +23,7 @@ export async function browserActionTool( const coordinate: string | undefined = block.params.coordinate const text: string | undefined = block.params.text const size: string | undefined = block.params.size + const filePath: string | undefined = block.params.path if (!action || !browserActions.includes(action)) { // checking for action to ensure it is complete and valid @@ -155,6 +156,17 @@ export async function browserActionTool( } } + if (action === "screenshot") { + if (!filePath) { + cline.consecutiveMistakeCount++ + cline.recordToolError("browser_action") + cline.didToolFailInCurrentTurn = true + pushToolResult(await cline.sayAndCreateMissingParamError("browser_action", "path")) + // Do not close the browser on parameter validation errors + return + } + } + cline.consecutiveMistakeCount = 0 // Prepare say payload; include executedCoordinate for pointer actions @@ -191,6 +203,9 @@ export async function browserActionTool( case "resize": browserActionResult = await cline.browserSession.resize(size!) break + case "screenshot": + browserActionResult = await cline.browserSession.saveScreenshot(filePath!, cline.cwd) + break case "close": browserActionResult = await cline.browserSession.closeBrowser() break @@ -205,12 +220,16 @@ export async function browserActionTool( case "press": case "scroll_down": case "scroll_up": - case "resize": { + case "resize": + case "screenshot": { await cline.say("browser_action_result", JSON.stringify(browserActionResult)) const images = browserActionResult?.screenshot ? [browserActionResult.screenshot] : [] - let messageText = `The browser action has been executed.` + let messageText = + action === "screenshot" + ? `Screenshot saved to: ${filePath}` + : `The browser action has been executed.` messageText += `\n\n**CRITICAL**: When providing click/hover coordinates:` messageText += `\n1. Screenshot dimensions != Browser viewport dimensions` diff --git a/src/core/tools/__tests__/BrowserActionTool.screenshot.spec.ts b/src/core/tools/__tests__/BrowserActionTool.screenshot.spec.ts new file mode 100644 index 00000000000..e4f9870c752 --- /dev/null +++ b/src/core/tools/__tests__/BrowserActionTool.screenshot.spec.ts @@ -0,0 +1,27 @@ +// Test screenshot action functionality in browser actions +import { describe, it, expect } from "vitest" +import { browserActions } from "../../../shared/ExtensionMessage" + +describe("Browser Action Screenshot", () => { + describe("browserActions array", () => { + it("should include screenshot action", () => { + expect(browserActions).toContain("screenshot") + }) + + it("should have screenshot as a valid browser action type", () => { + const allActions = [ + "launch", + "click", + "hover", + "type", + "press", + "scroll_down", + "scroll_up", + "resize", + "close", + "screenshot", + ] + expect(browserActions).toEqual(allActions) + }) + }) +}) diff --git a/src/services/browser/BrowserSession.ts b/src/services/browser/BrowserSession.ts index 98f6f85e037..7f44109bf54 100644 --- a/src/services/browser/BrowserSession.ts +++ b/src/services/browser/BrowserSession.ts @@ -756,6 +756,55 @@ export class BrowserSession { }) } + /** + * Determines image type from file extension + */ + private getImageTypeFromPath(filePath: string): "png" | "jpeg" | "webp" { + const ext = path.extname(filePath).toLowerCase() + if (ext === ".jpg" || ext === ".jpeg") return "jpeg" + if (ext === ".webp") return "webp" + return "png" + } + + /** + * Takes a screenshot and saves it to the specified file path. + * @param filePath - The destination file path (relative to workspace) + * @param cwd - Current working directory for resolving relative paths + * @returns BrowserActionResult with screenshot data and saved file path + * @throws Error if the resolved path escapes the workspace directory + */ + async saveScreenshot(filePath: string, cwd: string): Promise { + // Always resolve the path against the workspace root + const normalizedCwd = path.resolve(cwd) + const fullPath = path.resolve(cwd, filePath) + + // Validate that the resolved path stays within the workspace (before calling doAction) + if (!fullPath.startsWith(normalizedCwd + path.sep) && fullPath !== normalizedCwd) { + throw new Error( + `Screenshot path "${filePath}" resolves to "${fullPath}" which is outside the workspace "${normalizedCwd}". ` + + `Paths must be relative to the workspace and cannot escape it.`, + ) + } + + return this.doAction(async (page) => { + // Ensure directory exists + await fs.mkdir(path.dirname(fullPath), { recursive: true }) + + // Determine image type from extension + const imageType = this.getImageTypeFromPath(filePath) + + // Take screenshot directly to file (more efficient than base64 for file saving) + await page.screenshot({ + path: fullPath, + type: imageType, + quality: + imageType === "png" + ? undefined + : ((this.context.globalState.get("screenshotQuality") as number | undefined) ?? 75), + }) + }) + } + /** * Draws a cursor indicator on the page at the specified position */ diff --git a/src/services/browser/__tests__/BrowserSession.spec.ts b/src/services/browser/__tests__/BrowserSession.spec.ts index a7d9707ab39..2291fade428 100644 --- a/src/services/browser/__tests__/BrowserSession.spec.ts +++ b/src/services/browser/__tests__/BrowserSession.spec.ts @@ -1,5 +1,6 @@ // npx vitest services/browser/__tests__/BrowserSession.spec.ts +import * as path from "path" import { BrowserSession } from "../BrowserSession" import { discoverChromeHostUrl, tryChromeHostUrl } from "../browserDiscovery" @@ -395,6 +396,179 @@ describe("cursor visualization", () => { expect(result.currentMousePosition).toBeUndefined() }) + describe("saveScreenshot", () => { + // Use a cross-platform workspace path for testing + const testWorkspace = path.resolve("/workspace") + + it("should save screenshot to specified path with png format", async () => { + const mockFs = await import("fs/promises") + const page: any = { + on: vi.fn(), + off: vi.fn(), + screenshot: vi.fn().mockResolvedValue("mockScreenshotBase64"), + url: vi.fn().mockReturnValue("https://example.com"), + viewport: vi.fn().mockReturnValue({ width: 900, height: 600 }), + evaluate: vi.fn().mockResolvedValue(undefined), + } + + const mockCtx: any = { + globalState: { get: vi.fn(), update: vi.fn() }, + globalStorageUri: { fsPath: "/mock/global/storage/path" }, + extensionUri: { fsPath: "/mock/extension/path" }, + } + const session = new BrowserSession(mockCtx) + ;(session as any).page = page + + await session.saveScreenshot("screenshots/test.png", testWorkspace) + + expect(mockFs.mkdir).toHaveBeenCalledWith(path.join(testWorkspace, "screenshots"), { recursive: true }) + expect(page.screenshot).toHaveBeenCalledWith( + expect.objectContaining({ + path: path.join(testWorkspace, "screenshots", "test.png"), + type: "png", + }), + ) + }) + + it("should save screenshot with jpeg format for .jpg extension", async () => { + const page: any = { + on: vi.fn(), + off: vi.fn(), + screenshot: vi.fn().mockResolvedValue("mockScreenshotBase64"), + url: vi.fn().mockReturnValue("https://example.com"), + viewport: vi.fn().mockReturnValue({ width: 900, height: 600 }), + evaluate: vi.fn().mockResolvedValue(undefined), + } + + const mockCtx: any = { + globalState: { get: vi.fn().mockReturnValue(80), update: vi.fn() }, + globalStorageUri: { fsPath: "/mock/global/storage/path" }, + extensionUri: { fsPath: "/mock/extension/path" }, + } + const session = new BrowserSession(mockCtx) + ;(session as any).page = page + + await session.saveScreenshot("screenshots/test.jpg", testWorkspace) + + expect(page.screenshot).toHaveBeenCalledWith( + expect.objectContaining({ + path: path.join(testWorkspace, "screenshots", "test.jpg"), + type: "jpeg", + quality: 80, + }), + ) + }) + + it("should save screenshot with webp format", async () => { + const page: any = { + on: vi.fn(), + off: vi.fn(), + screenshot: vi.fn().mockResolvedValue("mockScreenshotBase64"), + url: vi.fn().mockReturnValue("https://example.com"), + viewport: vi.fn().mockReturnValue({ width: 900, height: 600 }), + evaluate: vi.fn().mockResolvedValue(undefined), + } + + const mockCtx: any = { + globalState: { get: vi.fn().mockReturnValue(75), update: vi.fn() }, + globalStorageUri: { fsPath: "/mock/global/storage/path" }, + extensionUri: { fsPath: "/mock/extension/path" }, + } + const session = new BrowserSession(mockCtx) + ;(session as any).page = page + + await session.saveScreenshot("test.webp", testWorkspace) + + expect(page.screenshot).toHaveBeenCalledWith( + expect.objectContaining({ + path: path.join(testWorkspace, "test.webp"), + type: "webp", + quality: 75, + }), + ) + }) + + it("should reject absolute file paths outside workspace", async () => { + // Create a cross-platform absolute path for testing + const absolutePath = path.resolve("/absolute/path/screenshot.png") + const page: any = { + on: vi.fn(), + off: vi.fn(), + screenshot: vi.fn().mockResolvedValue("mockScreenshotBase64"), + url: vi.fn().mockReturnValue("https://example.com"), + viewport: vi.fn().mockReturnValue({ width: 900, height: 600 }), + evaluate: vi.fn().mockResolvedValue(undefined), + } + + const mockCtx: any = { + globalState: { get: vi.fn(), update: vi.fn() }, + globalStorageUri: { fsPath: "/mock/global/storage/path" }, + extensionUri: { fsPath: "/mock/extension/path" }, + } + const session = new BrowserSession(mockCtx) + ;(session as any).page = page + + await expect(session.saveScreenshot(absolutePath, testWorkspace)).rejects.toThrow(/outside the workspace/) + + expect(page.screenshot).not.toHaveBeenCalled() + }) + + it("should reject paths with .. that escape the workspace", async () => { + const page: any = { + on: vi.fn(), + off: vi.fn(), + screenshot: vi.fn().mockResolvedValue("mockScreenshotBase64"), + url: vi.fn().mockReturnValue("https://example.com"), + viewport: vi.fn().mockReturnValue({ width: 900, height: 600 }), + evaluate: vi.fn().mockResolvedValue(undefined), + } + + const mockCtx: any = { + globalState: { get: vi.fn(), update: vi.fn() }, + globalStorageUri: { fsPath: "/mock/global/storage/path" }, + extensionUri: { fsPath: "/mock/extension/path" }, + } + const session = new BrowserSession(mockCtx) + ;(session as any).page = page + + await expect(session.saveScreenshot("../../etc/passwd", testWorkspace)).rejects.toThrow( + /outside the workspace/, + ) + + expect(page.screenshot).not.toHaveBeenCalled() + }) + + it("should allow paths with .. that stay within workspace", async () => { + const mockFs = await import("fs/promises") + const page: any = { + on: vi.fn(), + off: vi.fn(), + screenshot: vi.fn().mockResolvedValue("mockScreenshotBase64"), + url: vi.fn().mockReturnValue("https://example.com"), + viewport: vi.fn().mockReturnValue({ width: 900, height: 600 }), + evaluate: vi.fn().mockResolvedValue(undefined), + } + + const mockCtx: any = { + globalState: { get: vi.fn(), update: vi.fn() }, + globalStorageUri: { fsPath: "/mock/global/storage/path" }, + extensionUri: { fsPath: "/mock/extension/path" }, + } + const session = new BrowserSession(mockCtx) + ;(session as any).page = page + + // Path like "subdir/../screenshot.png" should resolve to "screenshot.png" within workspace + await session.saveScreenshot("subdir/../screenshot.png", testWorkspace) + + expect(page.screenshot).toHaveBeenCalledWith( + expect.objectContaining({ + path: path.join(testWorkspace, "screenshot.png"), + type: "png", + }), + ) + }) + }) + describe("getViewportSize", () => { it("falls back to configured viewport when no page or last viewport is available", () => { const localCtx: any = { diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 00b4d6c42d3..2b478c5ba02 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -432,6 +432,7 @@ export const browserActions = [ "scroll_up", "resize", "close", + "screenshot", ] as const export type BrowserAction = (typeof browserActions)[number] diff --git a/src/shared/tools.ts b/src/shared/tools.ts index d9561428f89..6b82861cb08 100644 --- a/src/shared/tools.ts +++ b/src/shared/tools.ts @@ -191,7 +191,7 @@ export interface ListCodeDefinitionNamesToolUse extends ToolUse<"list_code_defin export interface BrowserActionToolUse extends ToolUse<"browser_action"> { name: "browser_action" - params: Partial, "action" | "url" | "coordinate" | "text" | "size">> + params: Partial, "action" | "url" | "coordinate" | "text" | "size" | "path">> } export interface UseMcpToolToolUse extends ToolUse<"use_mcp_tool"> { diff --git a/webview-ui/src/components/chat/BrowserActionRow.tsx b/webview-ui/src/components/chat/BrowserActionRow.tsx index 4eecc284ae0..ae4fe3915b6 100644 --- a/webview-ui/src/components/chat/BrowserActionRow.tsx +++ b/webview-ui/src/components/chat/BrowserActionRow.tsx @@ -12,6 +12,7 @@ import { Play, Check, Maximize2, + Camera, } from "lucide-react" import { useExtensionState } from "@src/context/ExtensionStateContext" import { useTranslation } from "react-i18next" @@ -41,6 +42,8 @@ const getActionIcon = (action: string) => { return case "resize": return + case "screenshot": + return case "hover": default: return @@ -77,7 +80,7 @@ const BrowserActionRow = memo(({ message, nextMessage, actionIndex, totalActions // Format action display text const actionText = useMemo(() => { - if (!browserAction) return "Browser action" + if (!browserAction) return t("chat:browser.actions.title") // Helper to scale coordinates from screenshot dimensions to viewport dimensions // Matches the backend's scaleCoordinate function logic @@ -86,27 +89,33 @@ const BrowserActionRow = memo(({ message, nextMessage, actionIndex, totalActions switch (browserAction.action) { case "launch": - return `Launched browser` + return t("chat:browser.actions.launched") case "click": - return `Clicked at: ${browserAction.executedCoordinate || getViewportCoordinate(browserAction.coordinate)}` + return t("chat:browser.actions.clicked", { + coordinate: browserAction.executedCoordinate || getViewportCoordinate(browserAction.coordinate), + }) case "type": - return `Typed: ${browserAction.text}` + return t("chat:browser.actions.typed", { text: browserAction.text }) case "press": - return `Pressed key: ${prettyKey(browserAction.text)}` + return t("chat:browser.actions.pressed", { key: prettyKey(browserAction.text) }) case "hover": - return `Hovered at: ${browserAction.executedCoordinate || getViewportCoordinate(browserAction.coordinate)}` + return t("chat:browser.actions.hovered", { + coordinate: browserAction.executedCoordinate || getViewportCoordinate(browserAction.coordinate), + }) case "scroll_down": - return "Scrolled down" + return t("chat:browser.actions.scrolledDown") case "scroll_up": - return "Scrolled up" + return t("chat:browser.actions.scrolledUp") case "resize": - return `Resized to: ${browserAction.size?.split(/[x,]/).join(" x ")}` + return t("chat:browser.actions.resized", { size: browserAction.size?.split(/[x,]/).join(" x ") }) + case "screenshot": + return t("chat:browser.actions.screenshotSaved") case "close": - return "Closed browser" + return t("chat:browser.actions.closed") default: return browserAction.action } - }, [browserAction, viewportDimensions]) + }, [browserAction, viewportDimensions, t]) // Auto-open Browser Session panel when: // 1. This is a "launch" action (new browser session) - always opens and navigates to launch diff --git a/webview-ui/src/components/chat/BrowserSessionRow.tsx b/webview-ui/src/components/chat/BrowserSessionRow.tsx index 8fc23c6d0b2..cbaf24e8edc 100644 --- a/webview-ui/src/components/chat/BrowserSessionRow.tsx +++ b/webview-ui/src/components/chat/BrowserSessionRow.tsx @@ -1,6 +1,7 @@ import React, { memo, useEffect, useMemo, useRef, useState } from "react" import deepEqual from "fast-deep-equal" import { useTranslation } from "react-i18next" +import type { TFunction } from "i18next" import type { ClineMessage } from "@roo-code/types" import { BrowserAction, BrowserActionResult, ClineSayBrowserAction } from "@roo/ExtensionMessage" @@ -30,9 +31,11 @@ import { ChevronsRight, ExternalLink, Copy, + Camera, } from "lucide-react" const getBrowserActionText = ( + t: TFunction, action: BrowserAction, executedCoordinate?: string, coordinate?: string, @@ -48,23 +51,29 @@ const getBrowserActionText = ( switch (action) { case "launch": - return `Launched browser` + return t("chat:browser.actions.launched") case "click": - return `Clicked at: ${executedCoordinate || getViewportCoordinate(coordinate)}` + return t("chat:browser.actions.clicked", { + coordinate: executedCoordinate || getViewportCoordinate(coordinate), + }) case "type": - return `Typed: ${text}` + return t("chat:browser.actions.typed", { text }) case "press": - return `Pressed key: ${prettyKey(text)}` + return t("chat:browser.actions.pressed", { key: prettyKey(text) }) case "scroll_down": - return "Scrolled down" + return t("chat:browser.actions.scrolledDown") case "scroll_up": - return "Scrolled up" + return t("chat:browser.actions.scrolledUp") case "hover": - return `Hovered at: ${executedCoordinate || getViewportCoordinate(coordinate)}` + return t("chat:browser.actions.hovered", { + coordinate: executedCoordinate || getViewportCoordinate(coordinate), + }) case "resize": - return `Resized to: ${size?.split(/[x,]/).join(" x ")}` + return t("chat:browser.actions.resized", { size: size?.split(/[x,]/).join(" x ") }) + case "screenshot": + return t("chat:browser.actions.screenshotSaved") case "close": - return "Closed browser" + return t("chat:browser.actions.closed") default: return action } @@ -87,6 +96,8 @@ const getActionIcon = (action: BrowserAction) => { return case "resize": return + case "screenshot": + return case "hover": default: return @@ -557,6 +568,7 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => { {getActionIcon(action.action)} {getBrowserActionText( + t, action.action, action.executedCoordinate, action.coordinate, @@ -572,7 +584,7 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => { return ( <> {getActionIcon("launch" as any)} - {getBrowserActionText("launch", undefined, initialUrl, undefined)} + {getBrowserActionText(t, "launch", undefined, initialUrl, undefined)} ) } diff --git a/webview-ui/src/i18n/locales/ca/chat.json b/webview-ui/src/i18n/locales/ca/chat.json index d6b4357ef86..d8ebb821d22 100644 --- a/webview-ui/src/i18n/locales/ca/chat.json +++ b/webview-ui/src/i18n/locales/ca/chat.json @@ -341,13 +341,25 @@ "sessionStarted": "Sessió de navegador iniciada", "actions": { "title": "Acció del navegador: ", + "launched": "Navegador iniciat", "launch": "Iniciar navegador a {{url}}", + "clicked": "Fet clic ({{coordinate}})", "click": "Clic ({{coordinate}})", + "typed": "Escrit: {{text}}", "type": "Escriure \"{{text}}\"", + "pressed": "Premut: {{key}}", "press": "Prem {{key}}", + "scrolledDown": "Desplaçat avall", "scrollDown": "Desplaçar avall", + "scrolledUp": "Desplaçat amunt", "scrollUp": "Desplaçar amunt", + "hovered": "Planat sobre ({{coordinate}})", "hover": "Plana sobre ({{coordinate}})", + "resized": "Mida canviada a: {{size}}", + "resize": "Canvia la mida a {{size}}", + "screenshotSaved": "Captura de pantalla guardada", + "screenshot": "Guarda la captura de pantalla a {{path}}", + "closed": "Navegador tancat", "close": "Tancar navegador" } }, diff --git a/webview-ui/src/i18n/locales/de/chat.json b/webview-ui/src/i18n/locales/de/chat.json index 10d55471faa..2a3e2b135e7 100644 --- a/webview-ui/src/i18n/locales/de/chat.json +++ b/webview-ui/src/i18n/locales/de/chat.json @@ -341,13 +341,25 @@ "sessionStarted": "Browser-Sitzung gestartet", "actions": { "title": "Browser-Aktion: ", + "launched": "Browser gestartet", "launch": "Browser starten auf {{url}}", + "clicked": "Geklickt auf: {{coordinate}}", "click": "Klicken ({{coordinate}})", + "typed": "Eingegeben: {{text}}", "type": "Eingeben \"{{text}}\"", + "pressed": "{{key}} gedrückt", "press": "{{key}} drücken", + "scrolledDown": "Nach unten gescrollt", "scrollDown": "Nach unten scrollen", + "scrolledUp": "Nach oben gescrollt", "scrollUp": "Nach oben scrollen", + "hovered": "Gehovered auf: {{coordinate}}", "hover": "Hover ({{coordinate}})", + "resized": "Größe geändert auf: {{size}}", + "resize": "Größe ändern auf {{size}}", + "screenshotSaved": "Screenshot gespeichert", + "screenshot": "Screenshot speichern unter {{path}}", + "closed": "Browser geschlossen", "close": "Browser schließen" } }, diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json index e15a424f9f5..ae0f23cdbdf 100644 --- a/webview-ui/src/i18n/locales/en/chat.json +++ b/webview-ui/src/i18n/locales/en/chat.json @@ -356,13 +356,25 @@ "sessionStarted": "Browser Session Started", "actions": { "title": "Browser Action: ", + "launched": "Launched browser", "launch": "Launch browser at {{url}}", + "clicked": "Clicked at: {{coordinate}}", "click": "Click ({{coordinate}})", + "typed": "Typed: {{text}}", "type": "Type \"{{text}}\"", + "pressed": "Pressed key: {{key}}", "press": "Press {{key}}", + "scrolledDown": "Scrolled down", "scrollDown": "Scroll down", + "scrolledUp": "Scrolled up", "scrollUp": "Scroll up", + "hovered": "Hovered at: {{coordinate}}", "hover": "Hover ({{coordinate}})", + "resized": "Resized to: {{size}}", + "resize": "Resize to {{size}}", + "screenshotSaved": "Screenshot saved", + "screenshot": "Save screenshot to {{path}}", + "closed": "Closed browser", "close": "Close browser" } }, diff --git a/webview-ui/src/i18n/locales/es/chat.json b/webview-ui/src/i18n/locales/es/chat.json index e1e7c69173c..cedf02ee6ab 100644 --- a/webview-ui/src/i18n/locales/es/chat.json +++ b/webview-ui/src/i18n/locales/es/chat.json @@ -341,13 +341,25 @@ "sessionStarted": "Sesión de navegador iniciada", "actions": { "title": "Acción del navegador: ", + "launched": "Navegador iniciado", "launch": "Iniciar navegador en {{url}}", + "clicked": "Clic en: {{coordinate}}", "click": "Clic ({{coordinate}})", + "typed": "Escrito: {{text}}", "type": "Escribir \"{{text}}\"", + "pressed": "Pulsado: {{key}}", "press": "Pulsar {{key}}", + "scrolledDown": "Desplazado hacia abajo", "scrollDown": "Desplazar hacia abajo", + "scrolledUp": "Desplazado hacia arriba", "scrollUp": "Desplazar hacia arriba", + "hovered": "Flotado en: {{coordinate}}", "hover": "Flotar ({{coordinate}})", + "resized": "Tamaño cambiado a: {{size}}", + "resize": "Cambiar tamaño a {{size}}", + "screenshotSaved": "Captura de pantalla guardada", + "screenshot": "Guardar captura de pantalla en {{path}}", + "closed": "Navegador cerrado", "close": "Cerrar navegador" } }, diff --git a/webview-ui/src/i18n/locales/fr/chat.json b/webview-ui/src/i18n/locales/fr/chat.json index afa87b67f98..717eeb2fd65 100644 --- a/webview-ui/src/i18n/locales/fr/chat.json +++ b/webview-ui/src/i18n/locales/fr/chat.json @@ -341,13 +341,25 @@ "sessionStarted": "Session de navigateur démarrée", "actions": { "title": "Action du navigateur : ", + "launched": "Navigateur lancé", "launch": "Lancer le navigateur sur {{url}}", + "clicked": "Cliqué sur : {{coordinate}}", "click": "Cliquer ({{coordinate}})", + "typed": "Saisi : {{text}}", "type": "Saisir \"{{text}}\"", + "pressed": "Appuyé sur : {{key}}", "press": "Appuyer sur {{key}}", + "scrolledDown": "Défilé vers le bas", "scrollDown": "Défiler vers le bas", + "scrolledUp": "Défilé vers le haut", "scrollUp": "Défiler vers le haut", + "hovered": "Survolé : {{coordinate}}", "hover": "Survoler ({{coordinate}})", + "resized": "Redimensionné à : {{size}}", + "resize": "Redimensionner à {{size}}", + "screenshotSaved": "Capture d'écran enregistrée", + "screenshot": "Enregistrer une capture d'écran dans {{path}}", + "closed": "Navigateur fermé", "close": "Fermer le navigateur" } }, diff --git a/webview-ui/src/i18n/locales/hi/chat.json b/webview-ui/src/i18n/locales/hi/chat.json index 76d15e2a061..00dde75bcf3 100644 --- a/webview-ui/src/i18n/locales/hi/chat.json +++ b/webview-ui/src/i18n/locales/hi/chat.json @@ -341,13 +341,25 @@ "sessionStarted": "ब्राउज़र सत्र शुरू हुआ", "actions": { "title": "ब्राउज़र क्रिया: ", + "launched": "ब्राउज़र लॉन्च हुआ", "launch": "{{url}} पर ब्राउज़र लॉन्च करें", + "clicked": "क्लिक किया गया ({{coordinate}})", "click": "क्लिक करें ({{coordinate}})", + "typed": "टाइप किया गया: {{text}}", "type": "टाइप करें \"{{text}}\"", + "pressed": "दबाया गया: {{key}}", "press": "{{key}} दबाएँ", + "scrolledDown": "नीचे स्क्रॉल किया गया", "scrollDown": "नीचे स्क्रॉल करें", + "scrolledUp": "ऊपर स्क्रॉल किया गया", "scrollUp": "ऊपर स्क्रॉल करें", + "hovered": "होवर किया गया ({{coordinate}})", "hover": "होवर करें ({{coordinate}})", + "resized": "आकार बदला गया: {{size}}", + "resize": "आकार बदलें {{size}}", + "screenshotSaved": "स्क्रीनशॉट सहेजा गया", + "screenshot": "स्क्रीनशॉट को {{path}} में सहेजें", + "closed": "ब्राउज़र बंद किया गया", "close": "ब्राउज़र बंद करें" } }, diff --git a/webview-ui/src/i18n/locales/id/chat.json b/webview-ui/src/i18n/locales/id/chat.json index 793b68e1eeb..f798ef5e8ba 100644 --- a/webview-ui/src/i18n/locales/id/chat.json +++ b/webview-ui/src/i18n/locales/id/chat.json @@ -362,13 +362,25 @@ "sessionStarted": "Sesi Browser Dimulai", "actions": { "title": "Aksi Browser: ", + "launched": "Browser diluncurkan", "launch": "Luncurkan browser di {{url}}", + "clicked": "Diklik ({{coordinate}})", "click": "Klik ({{coordinate}})", + "typed": "Diketik: {{text}}", "type": "Ketik \"{{text}}\"", + "pressed": "Ditekan: {{key}}", "press": "Tekan {{key}}", + "scrolledDown": "Digulir ke bawah", "scrollDown": "Gulir ke bawah", + "scrolledUp": "Digulir ke atas", "scrollUp": "Gulir ke atas", + "hovered": "Diarahkan ({{coordinate}})", "hover": "Arahkan ({{coordinate}})", + "resized": "Ukuran diubah ke: {{size}}", + "resize": "Ubah ukuran ke {{size}}", + "screenshotSaved": "Screenshot disimpan", + "screenshot": "Simpan screenshot ke {{path}}", + "closed": "Browser ditutup", "close": "Tutup browser" } }, diff --git a/webview-ui/src/i18n/locales/it/chat.json b/webview-ui/src/i18n/locales/it/chat.json index dcd99e52a8a..71f21057769 100644 --- a/webview-ui/src/i18n/locales/it/chat.json +++ b/webview-ui/src/i18n/locales/it/chat.json @@ -341,13 +341,25 @@ "sessionStarted": "Sessione browser avviata", "actions": { "title": "Azione browser: ", + "launched": "Browser avviato", "launch": "Avvia browser su {{url}}", + "clicked": "Cliccato su: {{coordinate}}", "click": "Clic ({{coordinate}})", + "typed": "Digitato: {{text}}", "type": "Digita \"{{text}}\"", + "pressed": "Premuto: {{key}}", "press": "Premi {{key}}", + "scrolledDown": "Scorso verso il basso", "scrollDown": "Scorri verso il basso", + "scrolledUp": "Scorso verso l'alto", "scrollUp": "Scorri verso l'alto", + "hovered": "Passato il mouse su: {{coordinate}}", "hover": "Passa il mouse ({{coordinate}})", + "resized": "Ridimensionato a: {{size}}", + "resize": "Ridimensiona a {{size}}", + "screenshotSaved": "Screenshot salvato", + "screenshot": "Salva screenshot in {{path}}", + "closed": "Browser chiuso", "close": "Chiudi browser" } }, diff --git a/webview-ui/src/i18n/locales/ja/chat.json b/webview-ui/src/i18n/locales/ja/chat.json index 06045f57b1d..851b83f74d2 100644 --- a/webview-ui/src/i18n/locales/ja/chat.json +++ b/webview-ui/src/i18n/locales/ja/chat.json @@ -341,13 +341,25 @@ "sessionStarted": "ブラウザセッション開始", "actions": { "title": "ブラウザ操作: ", + "launched": "ブラウザを起動しました", "launch": "{{url}} でブラウザを起動", + "clicked": "クリック: {{coordinate}}", "click": "クリック ({{coordinate}})", + "typed": "入力: {{text}}", "type": "入力 \"{{text}}\"", + "pressed": "{{key}}を押しました", "press": "{{key}}を押す", + "scrolledDown": "下にスクロールしました", "scrollDown": "下にスクロール", + "scrolledUp": "上にスクロールしました", "scrollUp": "上にスクロール", + "hovered": "ホバー: {{coordinate}}", "hover": "ホバー ({{coordinate}})", + "resized": "サイズを変更しました: {{size}}", + "resize": "サイズを {{size}} に変更", + "screenshotSaved": "スクリーンショットを保存しました", + "screenshot": "スクリーンショットを {{path}} に保存", + "closed": "ブラウザを閉じました", "close": "ブラウザを閉じる" } }, diff --git a/webview-ui/src/i18n/locales/ko/chat.json b/webview-ui/src/i18n/locales/ko/chat.json index 76ff43a6abe..5dc4b8b2c8b 100644 --- a/webview-ui/src/i18n/locales/ko/chat.json +++ b/webview-ui/src/i18n/locales/ko/chat.json @@ -341,13 +341,25 @@ "sessionStarted": "브라우저 세션 시작됨", "actions": { "title": "브라우저 작업: ", + "launched": "브라우저 실행됨", "launch": "{{url}}에서 브라우저 실행", + "clicked": "클릭함: {{coordinate}}", "click": "클릭 ({{coordinate}})", + "typed": "입력함: {{text}}", "type": "입력 \"{{text}}\"", + "pressed": "{{key}} 누름", "press": "{{key}} 누르기", + "scrolledDown": "아래로 스크롤됨", "scrollDown": "아래로 스크롤", + "scrolledUp": "위로 스크롤됨", "scrollUp": "위로 스크롤", + "hovered": "가리킴: {{coordinate}}", "hover": "가리키기 ({{coordinate}})", + "resized": "크기 조정됨: {{size}}", + "resize": "크기를 {{size}}로 조정", + "screenshotSaved": "스크린샷 저장됨", + "screenshot": "스크린샷을 {{path}}에 저장", + "closed": "브라우저 닫음", "close": "브라우저 닫기" } }, diff --git a/webview-ui/src/i18n/locales/nl/chat.json b/webview-ui/src/i18n/locales/nl/chat.json index 107c6dd3fb7..66cdba7de1b 100644 --- a/webview-ui/src/i18n/locales/nl/chat.json +++ b/webview-ui/src/i18n/locales/nl/chat.json @@ -341,13 +341,25 @@ "sessionStarted": "Browsersessie gestart", "actions": { "title": "Browseractie: ", + "launched": "Browser gestart", "launch": "Browser starten op {{url}}", + "clicked": "Geklikt ({{coordinate}})", "click": "Klik ({{coordinate}})", + "typed": "Getypt: {{text}}", "type": "Typ \"{{text}}\"", + "pressed": "Ingedrukt: {{key}}", "press": "Druk op {{key}}", + "scrolledDown": "Naar beneden gescrolld", "scrollDown": "Scroll naar beneden", + "scrolledUp": "Naar boven gescrolld", "scrollUp": "Scroll naar boven", + "hovered": "Zweefde ({{coordinate}})", "hover": "Zweven ({{coordinate}})", + "resized": "Grootte gewijzigd naar: {{size}}", + "resize": "Grootte wijzigen naar {{size}}", + "screenshotSaved": "Schermopname opgeslagen", + "screenshot": "Schermopname opslaan naar {{path}}", + "closed": "Browser gesloten", "close": "Browser sluiten" } }, diff --git a/webview-ui/src/i18n/locales/pl/chat.json b/webview-ui/src/i18n/locales/pl/chat.json index e5b457285df..ba8d05db6c5 100644 --- a/webview-ui/src/i18n/locales/pl/chat.json +++ b/webview-ui/src/i18n/locales/pl/chat.json @@ -341,13 +341,25 @@ "sessionStarted": "Sesja przeglądarki rozpoczęta", "actions": { "title": "Akcja przeglądarki: ", + "launched": "Przeglądarka uruchomiona", "launch": "Uruchom przeglądarkę na {{url}}", + "clicked": "Kliknięto ({{coordinate}})", "click": "Kliknij ({{coordinate}})", + "typed": "Wpisano: {{text}}", "type": "Wpisz \"{{text}}\"", + "pressed": "Naciśnięto: {{key}}", "press": "Naciśnij {{key}}", + "scrolledDown": "Przewinięto w dół", "scrollDown": "Przewiń w dół", + "scrolledUp": "Przewinięto w górę", "scrollUp": "Przewiń w górę", + "hovered": "Najechano ({{coordinate}})", "hover": "Najedź ({{coordinate}})", + "resized": "Zmieniono rozmiar na: {{size}}", + "resize": "Zmień rozmiar na {{size}}", + "screenshotSaved": "Zrzut ekranu zapisany", + "screenshot": "Zapisz zrzut ekranu do {{path}}", + "closed": "Przeglądarka zamknięta", "close": "Zamknij przeglądarkę" } }, diff --git a/webview-ui/src/i18n/locales/pt-BR/chat.json b/webview-ui/src/i18n/locales/pt-BR/chat.json index d2014b1cd40..721af6625a2 100644 --- a/webview-ui/src/i18n/locales/pt-BR/chat.json +++ b/webview-ui/src/i18n/locales/pt-BR/chat.json @@ -341,13 +341,25 @@ "sessionStarted": "Sessão do navegador iniciada", "actions": { "title": "Ação do navegador: ", + "launched": "Navegador iniciado", "launch": "Iniciar navegador em {{url}}", + "clicked": "Clicado em: {{coordinate}}", "click": "Clique ({{coordinate}})", + "typed": "Digitado: {{text}}", "type": "Digitar \"{{text}}\"", + "pressed": "Pressionado: {{key}}", "press": "Pressione {{key}}", + "scrolledDown": "Rolado para baixo", "scrollDown": "Rolar para baixo", + "scrolledUp": "Rolado para cima", "scrollUp": "Rolar para cima", + "hovered": "Pairado em: {{coordinate}}", "hover": "Pairar ({{coordinate}})", + "resized": "Redimensionado para: {{size}}", + "resize": "Redimensionar para {{size}}", + "screenshotSaved": "Captura de tela salva", + "screenshot": "Salvar captura de tela em {{path}}", + "closed": "Navegador fechado", "close": "Fechar navegador" } }, diff --git a/webview-ui/src/i18n/locales/ru/chat.json b/webview-ui/src/i18n/locales/ru/chat.json index 60f13ba0e27..6918039b8e2 100644 --- a/webview-ui/src/i18n/locales/ru/chat.json +++ b/webview-ui/src/i18n/locales/ru/chat.json @@ -342,13 +342,25 @@ "sessionStarted": "Сессия браузера запущена", "actions": { "title": "Действие браузера: ", + "launched": "Браузер открыт", "launch": "Открыть браузер по адресу {{url}}", + "clicked": "Клик в: {{coordinate}}", "click": "Клик ({{coordinate}})", + "typed": "Введено: {{text}}", "type": "Ввести \"{{text}}\"", + "pressed": "Нажато: {{key}}", "press": "Нажать {{key}}", + "scrolledDown": "Прокручено вниз", "scrollDown": "Прокрутить вниз", + "scrolledUp": "Прокручено вверх", "scrollUp": "Прокрутить вверх", + "hovered": "Наведено в: {{coordinate}}", "hover": "Навести ({{coordinate}})", + "resized": "Размер изменен на: {{size}}", + "resize": "Изменить размер на {{size}}", + "screenshotSaved": "Снимок экрана сохранён", + "screenshot": "Сохранить снимок экрана в {{path}}", + "closed": "Браузер закрыт", "close": "Закрыть браузер" } }, diff --git a/webview-ui/src/i18n/locales/tr/chat.json b/webview-ui/src/i18n/locales/tr/chat.json index 2f14d50b90e..f803b944d7f 100644 --- a/webview-ui/src/i18n/locales/tr/chat.json +++ b/webview-ui/src/i18n/locales/tr/chat.json @@ -342,13 +342,25 @@ "sessionStarted": "Tarayıcı Oturumu Başlatıldı", "actions": { "title": "Tarayıcı Eylemi: ", + "launched": "Tarayıcı başlatıldı", "launch": "{{url}} adresinde tarayıcı başlat", + "clicked": "Tıklandı ({{coordinate}})", "click": "Tıkla ({{coordinate}})", + "typed": "Yazıldı: {{text}}", "type": "Yaz \"{{text}}\"", + "pressed": "Basıldı: {{key}}", "press": "{{key}} tuşuna bas", + "scrolledDown": "Aşağı kaydırıldı", "scrollDown": "Aşağı kaydır", + "scrolledUp": "Yukarı kaydırıldı", "scrollUp": "Yukarı kaydır", + "hovered": "Üzerine gelinildi ({{coordinate}})", "hover": "Üzerine gel ({{coordinate}})", + "resized": "Boyut değiştirildi: {{size}}", + "resize": "Boyutu değiştir {{size}}", + "screenshotSaved": "Ekran görüntüsü kaydedildi", + "screenshot": "Ekran görüntüsünü {{path}} yoluna kaydet", + "closed": "Tarayıcı kapatıldı", "close": "Tarayıcıyı kapat" } }, diff --git a/webview-ui/src/i18n/locales/vi/chat.json b/webview-ui/src/i18n/locales/vi/chat.json index d4ce8fd3662..d57dbb5ec4a 100644 --- a/webview-ui/src/i18n/locales/vi/chat.json +++ b/webview-ui/src/i18n/locales/vi/chat.json @@ -342,13 +342,25 @@ "sessionStarted": "Phiên trình duyệt đã bắt đầu", "actions": { "title": "Hành động trình duyệt: ", + "launched": "Trình duyệt đã khởi chạy", "launch": "Khởi chạy trình duyệt tại {{url}}", + "clicked": "Đã nhấp ({{coordinate}})", "click": "Nhấp ({{coordinate}})", + "typed": "Đã gõ: {{text}}", "type": "Gõ \"{{text}}\"", + "pressed": "Đã nhấn: {{key}}", "press": "Nhấn {{key}}", + "scrolledDown": "Đã cuộn xuống", "scrollDown": "Cuộn xuống", + "scrolledUp": "Đã cuộn lên", "scrollUp": "Cuộn lên", + "hovered": "Đã di chuột ({{coordinate}})", "hover": "Di chuột ({{coordinate}})", + "resized": "Đã thay đổi kích thước: {{size}}", + "resize": "Thay đổi kích thước thành {{size}}", + "screenshotSaved": "Ảnh chụp màn hình đã được lưu", + "screenshot": "Lưu ảnh chụp màn hình vào {{path}}", + "closed": "Trình duyệt đã đóng", "close": "Đóng trình duyệt" } }, diff --git a/webview-ui/src/i18n/locales/zh-CN/chat.json b/webview-ui/src/i18n/locales/zh-CN/chat.json index 5927d428bce..6dfffd1f4b1 100644 --- a/webview-ui/src/i18n/locales/zh-CN/chat.json +++ b/webview-ui/src/i18n/locales/zh-CN/chat.json @@ -342,13 +342,25 @@ "sessionStarted": "浏览器会话已启动", "actions": { "title": "浏览器操作: ", + "launched": "浏览器已启动", "launch": "访问 {{url}}", + "clicked": "点击位置: {{coordinate}}", "click": "点击 ({{coordinate}})", + "typed": "输入: {{text}}", "type": "输入 \"{{text}}\"", + "pressed": "已按 {{key}}", "press": "按 {{key}}", + "scrolledDown": "已向下滚动", "scrollDown": "向下滚动", + "scrolledUp": "已向上滚动", "scrollUp": "向上滚动", + "hovered": "悬停位置: {{coordinate}}", "hover": "悬停 ({{coordinate}})", + "resized": "大小已更改: {{size}}", + "resize": "调整大小为 {{size}}", + "screenshotSaved": "截图已保存", + "screenshot": "将截图保存到 {{path}}", + "closed": "浏览器已关闭", "close": "关闭浏览器" } }, diff --git a/webview-ui/src/i18n/locales/zh-TW/chat.json b/webview-ui/src/i18n/locales/zh-TW/chat.json index 7fc2f131c4e..81f55970201 100644 --- a/webview-ui/src/i18n/locales/zh-TW/chat.json +++ b/webview-ui/src/i18n/locales/zh-TW/chat.json @@ -360,13 +360,25 @@ "sessionStarted": "瀏覽器工作階段已啟動", "actions": { "title": "瀏覽器動作:", + "launched": "瀏覽器已啟動", "launch": "在 {{url}} 啟動瀏覽器", + "clicked": "點選位置: {{coordinate}}", "click": "點選 ({{coordinate}})", + "typed": "輸入: {{text}}", "type": "輸入「{{text}}」", + "pressed": "已按下 {{key}}", "press": "按下 {{key}}", + "scrolledDown": "已向下捲動", "scrollDown": "向下捲動", + "scrolledUp": "已向上捲動", "scrollUp": "向上捲動", + "hovered": "懸停位置: {{coordinate}}", "hover": "懸停 ({{coordinate}})", + "resized": "大小已變更: {{size}}", + "resize": "調整大小為 {{size}}", + "screenshotSaved": "螢幕擷圖已儲存", + "screenshot": "將螢幕擷圖儲存至 {{path}}", + "closed": "瀏覽器已關閉", "close": "關閉瀏覽器" } },