mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 04:11:10 -05:00
Allow selection of multiple browser viewport sizes and adjusting screenshot quality
This commit is contained in:
5
.changeset/five-gorillas-exist.md
Normal file
5
.changeset/five-gorillas-exist.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"roo-cline": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Allow selection of multiple browser viewport sizes and adjusting screenshot quality
|
||||||
@@ -7,7 +7,7 @@ A fork of Cline, an autonomous coding agent, with some additional experimental f
|
|||||||
- Drag and drop images into chats
|
- Drag and drop images into chats
|
||||||
- "Enhance prompt" button (OpenRouter models only for now)
|
- "Enhance prompt" button (OpenRouter models only for now)
|
||||||
- Sound effects for feedback
|
- Sound effects for feedback
|
||||||
- Option to use a larger 1280x800 browser
|
- Option to use browsers of different sizes and adjust screenshot quality
|
||||||
- Quick prompt copying from history
|
- Quick prompt copying from history
|
||||||
- OpenRouter compression support
|
- OpenRouter compression support
|
||||||
- Includes current time in the system prompt
|
- Includes current time in the system prompt
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ module.exports = {
|
|||||||
transformIgnorePatterns: [
|
transformIgnorePatterns: [
|
||||||
'node_modules/(?!(@modelcontextprotocol|delay|p-wait-for|globby|serialize-error|strip-ansi|default-shell|os-name)/)'
|
'node_modules/(?!(@modelcontextprotocol|delay|p-wait-for|globby|serialize-error|strip-ansi|default-shell|os-name)/)'
|
||||||
],
|
],
|
||||||
|
modulePathIgnorePatterns: [
|
||||||
|
'.vscode-test'
|
||||||
|
],
|
||||||
setupFiles: [],
|
setupFiles: [],
|
||||||
globals: {
|
globals: {
|
||||||
'ts-jest': {
|
'ts-jest': {
|
||||||
|
|||||||
@@ -786,8 +786,8 @@ export class Cline {
|
|||||||
throw new Error("MCP hub not available")
|
throw new Error("MCP hub not available")
|
||||||
}
|
}
|
||||||
|
|
||||||
const { browserLargeViewport, preferredLanguage } = await this.providerRef.deref()?.getState() ?? {}
|
const { browserViewportSize, preferredLanguage } = await this.providerRef.deref()?.getState() ?? {}
|
||||||
const systemPrompt = await SYSTEM_PROMPT(cwd, this.api.getModel().info.supportsComputerUse ?? false, mcpHub, this.diffStrategy, browserLargeViewport) + await addCustomInstructions(this.customInstructions ?? '', cwd, preferredLanguage)
|
const systemPrompt = await SYSTEM_PROMPT(cwd, this.api.getModel().info.supportsComputerUse ?? false, mcpHub, this.diffStrategy, browserViewportSize) + await addCustomInstructions(this.customInstructions ?? '', cwd, preferredLanguage)
|
||||||
|
|
||||||
// If the previous API request's total token usage is close to the context window, truncate the conversation history to free up space for the new request
|
// If the previous API request's total token usage is close to the context window, truncate the conversation history to free up space for the new request
|
||||||
if (previousApiReqIndex >= 0) {
|
if (previousApiReqIndex >= 0) {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const SYSTEM_PROMPT = async (
|
|||||||
supportsComputerUse: boolean,
|
supportsComputerUse: boolean,
|
||||||
mcpHub: McpHub,
|
mcpHub: McpHub,
|
||||||
diffStrategy?: DiffStrategy,
|
diffStrategy?: DiffStrategy,
|
||||||
browserLargeViewport?: boolean
|
browserViewportSize?: string
|
||||||
) => `You are Cline, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.
|
) => `You are Cline, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.
|
||||||
|
|
||||||
====
|
====
|
||||||
@@ -114,7 +114,7 @@ Usage:
|
|||||||
Description: Request to interact with a Puppeteer-controlled browser. Every action, except \`close\`, will be responded to with a screenshot of the browser's current state, along with any new console logs. You may only perform one browser action per message, and wait for the user's response including a screenshot and logs to determine the next action.
|
Description: Request to interact with a Puppeteer-controlled browser. Every action, except \`close\`, will be responded to with a screenshot of the browser's current state, along with any new console logs. You may only perform one browser action per message, and wait for the user's response including a screenshot and logs to determine the next action.
|
||||||
- The sequence of actions **must always start with** launching the browser at a URL, and **must always end with** closing the browser. If you need to visit a new URL that is not possible to navigate to from the current webpage, you must first close the browser, then launch again at the new URL.
|
- The sequence of actions **must always start with** launching the browser at a URL, and **must always end with** closing the browser. If you need to visit a new URL that is not possible to navigate to from the current webpage, you must first close the browser, then launch again at the new URL.
|
||||||
- While the browser is active, only the \`browser_action\` tool can be used. No other tools should be called during this time. You may proceed to use other tools only after closing the browser. For example if you run into an error and need to fix a file, you must close the browser, then use other tools to make the necessary changes, then re-launch the browser to verify the result.
|
- While the browser is active, only the \`browser_action\` tool can be used. No other tools should be called during this time. You may proceed to use other tools only after closing the browser. For example if you run into an error and need to fix a file, you must close the browser, then use other tools to make the necessary changes, then re-launch the browser to verify the result.
|
||||||
- The browser window has a resolution of **${browserLargeViewport ? "1280x800" : "900x600"}** pixels. When performing any click actions, ensure the coordinates are within this resolution range.
|
- The browser window has a resolution of **${browserViewportSize || "900x600"}** pixels. When performing any click actions, ensure the coordinates are within this resolution range.
|
||||||
- Before clicking on any elements such as icons, links, or buttons, you must consult the provided screenshot of the page to determine the coordinates of the element. The click should be targeted at the **center of the element**, not on its edges.
|
- Before clicking on any elements such as icons, links, or buttons, you must consult the provided screenshot of the page to determine the coordinates of the element. The click should be targeted at the **center of the element**, not on its edges.
|
||||||
Parameters:
|
Parameters:
|
||||||
- action: (required) The action to perform. The available actions are:
|
- action: (required) The action to perform. The available actions are:
|
||||||
@@ -132,7 +132,7 @@ Parameters:
|
|||||||
- Example: \`<action>close</action>\`
|
- Example: \`<action>close</action>\`
|
||||||
- url: (optional) Use this for providing the URL for the \`launch\` action.
|
- url: (optional) Use this for providing the URL for the \`launch\` action.
|
||||||
* Example: <url>https://example.com</url>
|
* Example: <url>https://example.com</url>
|
||||||
- coordinate: (optional) The X and Y coordinates for the \`click\` action. Coordinates should be within the **${browserLargeViewport ? "1280x800" : "900x600"}** resolution.
|
- coordinate: (optional) The X and Y coordinates for the \`click\` action. Coordinates should be within the **${browserViewportSize || "900x600"}** resolution.
|
||||||
* Example: <coordinate>450,300</coordinate>
|
* Example: <coordinate>450,300</coordinate>
|
||||||
- text: (optional) Use this for providing the text for the \`type\` action.
|
- text: (optional) Use this for providing the text for the \`type\` action.
|
||||||
* Example: <text>Hello, world!</text>
|
* Example: <text>Hello, world!</text>
|
||||||
|
|||||||
@@ -71,7 +71,8 @@ type GlobalStateKey =
|
|||||||
| "soundVolume"
|
| "soundVolume"
|
||||||
| "diffEnabled"
|
| "diffEnabled"
|
||||||
| "alwaysAllowMcp"
|
| "alwaysAllowMcp"
|
||||||
| "browserLargeViewport"
|
| "browserViewportSize"
|
||||||
|
| "screenshotQuality"
|
||||||
| "fuzzyMatchThreshold"
|
| "fuzzyMatchThreshold"
|
||||||
| "preferredLanguage" // Language setting for Cline's communication
|
| "preferredLanguage" // Language setting for Cline's communication
|
||||||
| "writeDelayMs"
|
| "writeDelayMs"
|
||||||
@@ -624,9 +625,9 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
await this.updateGlobalState("diffEnabled", diffEnabled)
|
await this.updateGlobalState("diffEnabled", diffEnabled)
|
||||||
await this.postStateToWebview()
|
await this.postStateToWebview()
|
||||||
break
|
break
|
||||||
case "browserLargeViewport":
|
case "browserViewportSize":
|
||||||
const browserLargeViewport = message.bool ?? false
|
const browserViewportSize = message.text ?? "900x600"
|
||||||
await this.updateGlobalState("browserLargeViewport", browserLargeViewport)
|
await this.updateGlobalState("browserViewportSize", browserViewportSize)
|
||||||
await this.postStateToWebview()
|
await this.postStateToWebview()
|
||||||
break
|
break
|
||||||
case "fuzzyMatchThreshold":
|
case "fuzzyMatchThreshold":
|
||||||
@@ -641,6 +642,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
await this.updateGlobalState("writeDelayMs", message.value)
|
await this.updateGlobalState("writeDelayMs", message.value)
|
||||||
await this.postStateToWebview()
|
await this.postStateToWebview()
|
||||||
break
|
break
|
||||||
|
case "screenshotQuality":
|
||||||
|
await this.updateGlobalState("screenshotQuality", message.value)
|
||||||
|
await this.postStateToWebview()
|
||||||
|
break
|
||||||
case "enhancePrompt":
|
case "enhancePrompt":
|
||||||
if (message.text) {
|
if (message.text) {
|
||||||
try {
|
try {
|
||||||
@@ -1015,7 +1020,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
diffEnabled,
|
diffEnabled,
|
||||||
taskHistory,
|
taskHistory,
|
||||||
soundVolume,
|
soundVolume,
|
||||||
browserLargeViewport,
|
browserViewportSize,
|
||||||
|
screenshotQuality,
|
||||||
preferredLanguage,
|
preferredLanguage,
|
||||||
writeDelayMs,
|
writeDelayMs,
|
||||||
} = await this.getState()
|
} = await this.getState()
|
||||||
@@ -1043,7 +1049,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
shouldShowAnnouncement: lastShownAnnouncementId !== this.latestAnnouncementId,
|
shouldShowAnnouncement: lastShownAnnouncementId !== this.latestAnnouncementId,
|
||||||
allowedCommands,
|
allowedCommands,
|
||||||
soundVolume: soundVolume ?? 0.5,
|
soundVolume: soundVolume ?? 0.5,
|
||||||
browserLargeViewport: browserLargeViewport ?? false,
|
browserViewportSize: browserViewportSize ?? "900x600",
|
||||||
|
screenshotQuality: screenshotQuality ?? 75,
|
||||||
preferredLanguage: preferredLanguage ?? 'English',
|
preferredLanguage: preferredLanguage ?? 'English',
|
||||||
writeDelayMs: writeDelayMs ?? 1000,
|
writeDelayMs: writeDelayMs ?? 1000,
|
||||||
}
|
}
|
||||||
@@ -1140,10 +1147,11 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
soundEnabled,
|
soundEnabled,
|
||||||
diffEnabled,
|
diffEnabled,
|
||||||
soundVolume,
|
soundVolume,
|
||||||
browserLargeViewport,
|
browserViewportSize,
|
||||||
fuzzyMatchThreshold,
|
fuzzyMatchThreshold,
|
||||||
preferredLanguage,
|
preferredLanguage,
|
||||||
writeDelayMs,
|
writeDelayMs,
|
||||||
|
screenshotQuality,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
this.getGlobalState("apiProvider") as Promise<ApiProvider | undefined>,
|
this.getGlobalState("apiProvider") as Promise<ApiProvider | undefined>,
|
||||||
this.getGlobalState("apiModelId") as Promise<string | undefined>,
|
this.getGlobalState("apiModelId") as Promise<string | undefined>,
|
||||||
@@ -1183,10 +1191,11 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
this.getGlobalState("soundEnabled") as Promise<boolean | undefined>,
|
this.getGlobalState("soundEnabled") as Promise<boolean | undefined>,
|
||||||
this.getGlobalState("diffEnabled") as Promise<boolean | undefined>,
|
this.getGlobalState("diffEnabled") as Promise<boolean | undefined>,
|
||||||
this.getGlobalState("soundVolume") as Promise<number | undefined>,
|
this.getGlobalState("soundVolume") as Promise<number | undefined>,
|
||||||
this.getGlobalState("browserLargeViewport") as Promise<boolean | undefined>,
|
this.getGlobalState("browserViewportSize") as Promise<string | undefined>,
|
||||||
this.getGlobalState("fuzzyMatchThreshold") as Promise<number | undefined>,
|
this.getGlobalState("fuzzyMatchThreshold") as Promise<number | undefined>,
|
||||||
this.getGlobalState("preferredLanguage") as Promise<string | undefined>,
|
this.getGlobalState("preferredLanguage") as Promise<string | undefined>,
|
||||||
this.getGlobalState("writeDelayMs") as Promise<number | undefined>,
|
this.getGlobalState("writeDelayMs") as Promise<number | undefined>,
|
||||||
|
this.getGlobalState("screenshotQuality") as Promise<number | undefined>,
|
||||||
])
|
])
|
||||||
|
|
||||||
let apiProvider: ApiProvider
|
let apiProvider: ApiProvider
|
||||||
@@ -1244,7 +1253,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
soundEnabled: soundEnabled ?? false,
|
soundEnabled: soundEnabled ?? false,
|
||||||
diffEnabled: diffEnabled ?? true,
|
diffEnabled: diffEnabled ?? true,
|
||||||
soundVolume,
|
soundVolume,
|
||||||
browserLargeViewport: browserLargeViewport ?? false,
|
browserViewportSize: browserViewportSize ?? "900x600",
|
||||||
|
screenshotQuality: screenshotQuality ?? 75,
|
||||||
fuzzyMatchThreshold: fuzzyMatchThreshold ?? 1.0,
|
fuzzyMatchThreshold: fuzzyMatchThreshold ?? 1.0,
|
||||||
writeDelayMs: writeDelayMs ?? 1000,
|
writeDelayMs: writeDelayMs ?? 1000,
|
||||||
preferredLanguage: preferredLanguage ?? (() => {
|
preferredLanguage: preferredLanguage ?? (() => {
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ describe('ClineProvider', () => {
|
|||||||
soundEnabled: false,
|
soundEnabled: false,
|
||||||
diffEnabled: false,
|
diffEnabled: false,
|
||||||
writeDelayMs: 1000,
|
writeDelayMs: 1000,
|
||||||
browserLargeViewport: false,
|
browserViewportSize: "900x600",
|
||||||
fuzzyMatchThreshold: 1.0,
|
fuzzyMatchThreshold: 1.0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,9 +58,11 @@ export class BrowserSession {
|
|||||||
"--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36",
|
"--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36",
|
||||||
],
|
],
|
||||||
executablePath: stats.executablePath,
|
executablePath: stats.executablePath,
|
||||||
defaultViewport: await this.context.globalState.get("browserLargeViewport")
|
defaultViewport: (() => {
|
||||||
? { width: 1280, height: 800 }
|
const size = (this.context.globalState.get("browserViewportSize") as string | undefined) || "900x600"
|
||||||
: { width: 900, height: 600 },
|
const [width, height] = size.split("x").map(Number)
|
||||||
|
return { width, height }
|
||||||
|
})(),
|
||||||
// headless: false,
|
// headless: false,
|
||||||
})
|
})
|
||||||
// (latest version of puppeteer does not add headless to user agent)
|
// (latest version of puppeteer does not add headless to user agent)
|
||||||
@@ -134,7 +136,7 @@ export class BrowserSession {
|
|||||||
let screenshotBase64 = await this.page.screenshot({
|
let screenshotBase64 = await this.page.screenshot({
|
||||||
...options,
|
...options,
|
||||||
type: "webp",
|
type: "webp",
|
||||||
quality: 100, // Set maximum quality to prevent compression artifacts
|
quality: (await this.context.globalState.get("screenshotQuality") as number | undefined) ?? 75,
|
||||||
})
|
})
|
||||||
let screenshot = `data:image/webp;base64,${screenshotBase64}`
|
let screenshot = `data:image/webp;base64,${screenshotBase64}`
|
||||||
|
|
||||||
@@ -245,27 +247,29 @@ export class BrowserSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async scrollDown(): Promise<BrowserActionResult> {
|
async scrollDown(): Promise<BrowserActionResult> {
|
||||||
const isLargeViewport = await this.context.globalState.get("browserLargeViewport")
|
const size = (await this.context.globalState.get("browserViewportSize") as string | undefined) || "900x600"
|
||||||
|
const height = parseInt(size.split("x")[1])
|
||||||
return this.doAction(async (page) => {
|
return this.doAction(async (page) => {
|
||||||
await page.evaluate((scrollHeight) => {
|
await page.evaluate((scrollHeight) => {
|
||||||
window.scrollBy({
|
window.scrollBy({
|
||||||
top: scrollHeight,
|
top: scrollHeight,
|
||||||
behavior: "auto",
|
behavior: "auto",
|
||||||
})
|
})
|
||||||
}, isLargeViewport ? 800 : 600)
|
}, height)
|
||||||
await delay(300)
|
await delay(300)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async scrollUp(): Promise<BrowserActionResult> {
|
async scrollUp(): Promise<BrowserActionResult> {
|
||||||
const isLargeViewport = await this.context.globalState.get("browserLargeViewport")
|
const size = (await this.context.globalState.get("browserViewportSize") as string | undefined) || "900x600"
|
||||||
|
const height = parseInt(size.split("x")[1])
|
||||||
return this.doAction(async (page) => {
|
return this.doAction(async (page) => {
|
||||||
await page.evaluate((scrollHeight) => {
|
await page.evaluate((scrollHeight) => {
|
||||||
window.scrollBy({
|
window.scrollBy({
|
||||||
top: -scrollHeight,
|
top: -scrollHeight,
|
||||||
behavior: "auto",
|
behavior: "auto",
|
||||||
})
|
})
|
||||||
}, isLargeViewport ? 800 : 600)
|
}, height)
|
||||||
await delay(300)
|
await delay(300)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,8 @@ export interface ExtensionState {
|
|||||||
soundEnabled?: boolean
|
soundEnabled?: boolean
|
||||||
soundVolume?: number
|
soundVolume?: number
|
||||||
diffEnabled?: boolean
|
diffEnabled?: boolean
|
||||||
browserLargeViewport?: boolean
|
browserViewportSize?: string
|
||||||
|
screenshotQuality?: number
|
||||||
fuzzyMatchThreshold?: number
|
fuzzyMatchThreshold?: number
|
||||||
preferredLanguage: string
|
preferredLanguage: string
|
||||||
writeDelayMs: number
|
writeDelayMs: number
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ export interface WebviewMessage {
|
|||||||
| "soundEnabled"
|
| "soundEnabled"
|
||||||
| "soundVolume"
|
| "soundVolume"
|
||||||
| "diffEnabled"
|
| "diffEnabled"
|
||||||
| "browserLargeViewport"
|
| "browserViewportSize"
|
||||||
|
| "screenshotQuality"
|
||||||
| "openMcpSettings"
|
| "openMcpSettings"
|
||||||
| "restartMcpServer"
|
| "restartMcpServer"
|
||||||
| "toggleToolAlwaysAllow"
|
| "toggleToolAlwaysAllow"
|
||||||
|
|||||||
@@ -61,7 +61,7 @@
|
|||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"transformIgnorePatterns": [
|
"transformIgnorePatterns": [
|
||||||
"/node_modules/(?!(rehype-highlight|react-remark|unist-util-visit|vfile|unified|bail|is-plain-obj|trough|vfile-message|unist-util-stringify-position|mdast-util-from-markdown|mdast-util-to-string|micromark|decode-named-character-reference|character-entities|markdown-table|zwitch|longest-streak|escape-string-regexp|unist-util-is|hast-util-to-text|@vscode/webview-ui-toolkit|@microsoft/fast-react-wrapper|@microsoft/fast-element|@microsoft/fast-foundation|@microsoft/fast-web-utilities|exenv-es6)/)"
|
"/node_modules/(?!(rehype-highlight|react-remark|unist-util-visit|unist-util-find-after|vfile|unified|bail|is-plain-obj|trough|vfile-message|unist-util-stringify-position|mdast-util-from-markdown|mdast-util-to-string|micromark|decode-named-character-reference|character-entities|markdown-table|zwitch|longest-streak|escape-string-regexp|unist-util-is|hast-util-to-text|@vscode/webview-ui-toolkit|@microsoft/fast-react-wrapper|@microsoft/fast-element|@microsoft/fast-foundation|@microsoft/fast-web-utilities|exenv-es6)/)"
|
||||||
],
|
],
|
||||||
"moduleNameMapper": {
|
"moduleNameMapper": {
|
||||||
"\\.(css|less|scss|sass)$": "identity-obj-proxy"
|
"\\.(css|less|scss|sass)$": "identity-obj-proxy"
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
|
|||||||
const [maxActionHeight, setMaxActionHeight] = useState(0)
|
const [maxActionHeight, setMaxActionHeight] = useState(0)
|
||||||
const [consoleLogsExpanded, setConsoleLogsExpanded] = useState(false)
|
const [consoleLogsExpanded, setConsoleLogsExpanded] = useState(false)
|
||||||
|
|
||||||
|
const { browserViewportSize = "900x600" } = useExtensionState()
|
||||||
|
const [viewportWidth, viewportHeight] = browserViewportSize.split("x").map(Number)
|
||||||
|
const aspectRatio = (viewportHeight / viewportWidth * 100).toFixed(2)
|
||||||
|
const defaultMousePosition = `${Math.round(viewportWidth/2)},${Math.round(viewportHeight/2)}`
|
||||||
|
|
||||||
const isLastApiReqInterrupted = useMemo(() => {
|
const isLastApiReqInterrupted = useMemo(() => {
|
||||||
// Check if last api_req_started is cancelled
|
// Check if last api_req_started is cancelled
|
||||||
const lastApiReqStarted = [...messages].reverse().find((m) => m.say === "api_req_started")
|
const lastApiReqStarted = [...messages].reverse().find((m) => m.say === "api_req_started")
|
||||||
@@ -165,13 +170,13 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
|
|||||||
const displayState = isLastPage
|
const displayState = isLastPage
|
||||||
? {
|
? {
|
||||||
url: currentPage?.currentState.url || latestState.url || initialUrl,
|
url: currentPage?.currentState.url || latestState.url || initialUrl,
|
||||||
mousePosition: currentPage?.currentState.mousePosition || latestState.mousePosition || "700,400",
|
mousePosition: currentPage?.currentState.mousePosition || latestState.mousePosition || defaultMousePosition,
|
||||||
consoleLogs: currentPage?.currentState.consoleLogs,
|
consoleLogs: currentPage?.currentState.consoleLogs,
|
||||||
screenshot: currentPage?.currentState.screenshot || latestState.screenshot,
|
screenshot: currentPage?.currentState.screenshot || latestState.screenshot,
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
url: currentPage?.currentState.url || initialUrl,
|
url: currentPage?.currentState.url || initialUrl,
|
||||||
mousePosition: currentPage?.currentState.mousePosition || "700,400",
|
mousePosition: currentPage?.currentState.mousePosition || defaultMousePosition,
|
||||||
consoleLogs: currentPage?.currentState.consoleLogs,
|
consoleLogs: currentPage?.currentState.consoleLogs,
|
||||||
screenshot: currentPage?.currentState.screenshot,
|
screenshot: currentPage?.currentState.screenshot,
|
||||||
}
|
}
|
||||||
@@ -220,10 +225,9 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
|
|||||||
}, [isBrowsing, currentPage?.nextAction?.messages])
|
}, [isBrowsing, currentPage?.nextAction?.messages])
|
||||||
|
|
||||||
// Use latest click position while browsing, otherwise use display state
|
// Use latest click position while browsing, otherwise use display state
|
||||||
const { browserLargeViewport } = useExtensionState()
|
const mousePosition = isBrowsing ? latestClickPosition || displayState.mousePosition : displayState.mousePosition || defaultMousePosition
|
||||||
const mousePosition = isBrowsing ? latestClickPosition || displayState.mousePosition : displayState.mousePosition
|
|
||||||
|
|
||||||
const [browserSessionRow, { height }] = useSize(
|
const [browserSessionRow, { height: rowHeight }] = useSize(
|
||||||
<div style={{ padding: "10px 6px 10px 15px", marginBottom: -10 }}>
|
<div style={{ padding: "10px 6px 10px 15px", marginBottom: -10 }}>
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "10px", marginBottom: "10px" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "10px", marginBottom: "10px" }}>
|
||||||
{isBrowsing ? (
|
{isBrowsing ? (
|
||||||
@@ -277,9 +281,10 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
|
|||||||
|
|
||||||
{/* Screenshot Area */}
|
{/* Screenshot Area */}
|
||||||
<div
|
<div
|
||||||
|
data-testid="screenshot-container"
|
||||||
style={{
|
style={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
paddingBottom: browserLargeViewport ? "62.5%" : "66.67%", // 800/1280 = 0.625, 600/900 = 0.667
|
paddingBottom: `${aspectRatio}%`, // height/width ratio
|
||||||
position: "relative",
|
position: "relative",
|
||||||
backgroundColor: "var(--vscode-input-background)",
|
backgroundColor: "var(--vscode-input-background)",
|
||||||
}}>
|
}}>
|
||||||
@@ -321,8 +326,8 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
|
|||||||
<BrowserCursor
|
<BrowserCursor
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
top: `${(parseInt(mousePosition.split(",")[1]) / (browserLargeViewport ? 800 : 600)) * 100}%`,
|
top: `${(parseInt(mousePosition.split(",")[1]) / viewportHeight) * 100}%`,
|
||||||
left: `${(parseInt(mousePosition.split(",")[0]) / (browserLargeViewport ? 1280 : 900)) * 100}%`,
|
left: `${(parseInt(mousePosition.split(",")[0]) / viewportWidth) * 100}%`,
|
||||||
transition: "top 0.3s ease-out, left 0.3s ease-out",
|
transition: "top 0.3s ease-out, left 0.3s ease-out",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -389,13 +394,13 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
|
|||||||
// Height change effect
|
// Height change effect
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const isInitialRender = prevHeightRef.current === 0
|
const isInitialRender = prevHeightRef.current === 0
|
||||||
if (isLast && height !== 0 && height !== Infinity && height !== prevHeightRef.current) {
|
if (isLast && rowHeight !== 0 && rowHeight !== Infinity && rowHeight !== prevHeightRef.current) {
|
||||||
if (!isInitialRender) {
|
if (!isInitialRender) {
|
||||||
onHeightChange(height > prevHeightRef.current)
|
onHeightChange(rowHeight > prevHeightRef.current)
|
||||||
}
|
}
|
||||||
prevHeightRef.current = height
|
prevHeightRef.current = rowHeight
|
||||||
}
|
}
|
||||||
}, [height, isLast, onHeightChange])
|
}, [rowHeight, isLast, onHeightChange])
|
||||||
|
|
||||||
return browserSessionRow
|
return browserSessionRow
|
||||||
}, deepEqual)
|
}, deepEqual)
|
||||||
@@ -552,6 +557,7 @@ const BrowserCursor: React.FC<{ style?: React.CSSProperties }> = ({ style }) =>
|
|||||||
...style,
|
...style,
|
||||||
}}
|
}}
|
||||||
alt="cursor"
|
alt="cursor"
|
||||||
|
aria-label="cursor"
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|||||||
setSoundVolume,
|
setSoundVolume,
|
||||||
diffEnabled,
|
diffEnabled,
|
||||||
setDiffEnabled,
|
setDiffEnabled,
|
||||||
browserLargeViewport,
|
browserViewportSize,
|
||||||
setBrowserLargeViewport,
|
setBrowserViewportSize,
|
||||||
openRouterModels,
|
openRouterModels,
|
||||||
setAllowedCommands,
|
setAllowedCommands,
|
||||||
allowedCommands,
|
allowedCommands,
|
||||||
@@ -44,6 +44,8 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|||||||
setPreferredLanguage,
|
setPreferredLanguage,
|
||||||
writeDelayMs,
|
writeDelayMs,
|
||||||
setWriteDelayMs,
|
setWriteDelayMs,
|
||||||
|
screenshotQuality,
|
||||||
|
setScreenshotQuality,
|
||||||
} = useExtensionState()
|
} = useExtensionState()
|
||||||
const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
|
const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
|
||||||
const [modelIdErrorMessage, setModelIdErrorMessage] = useState<string | undefined>(undefined)
|
const [modelIdErrorMessage, setModelIdErrorMessage] = useState<string | undefined>(undefined)
|
||||||
@@ -69,10 +71,11 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|||||||
vscode.postMessage({ type: "soundEnabled", bool: soundEnabled })
|
vscode.postMessage({ type: "soundEnabled", bool: soundEnabled })
|
||||||
vscode.postMessage({ type: "soundVolume", value: soundVolume })
|
vscode.postMessage({ type: "soundVolume", value: soundVolume })
|
||||||
vscode.postMessage({ type: "diffEnabled", bool: diffEnabled })
|
vscode.postMessage({ type: "diffEnabled", bool: diffEnabled })
|
||||||
vscode.postMessage({ type: "browserLargeViewport", bool: browserLargeViewport })
|
vscode.postMessage({ type: "browserViewportSize", text: browserViewportSize })
|
||||||
vscode.postMessage({ type: "fuzzyMatchThreshold", value: fuzzyMatchThreshold ?? 1.0 })
|
vscode.postMessage({ type: "fuzzyMatchThreshold", value: fuzzyMatchThreshold ?? 1.0 })
|
||||||
vscode.postMessage({ type: "preferredLanguage", text: preferredLanguage })
|
vscode.postMessage({ type: "preferredLanguage", text: preferredLanguage })
|
||||||
vscode.postMessage({ type: "writeDelayMs", value: writeDelayMs })
|
vscode.postMessage({ type: "writeDelayMs", value: writeDelayMs })
|
||||||
|
vscode.postMessage({ type: "screenshotQuality", value: screenshotQuality ?? 75 })
|
||||||
onDone()
|
onDone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -128,7 +131,8 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|||||||
marginBottom: "17px",
|
marginBottom: "17px",
|
||||||
paddingRight: 17,
|
paddingRight: 17,
|
||||||
}}>
|
}}>
|
||||||
<h3 style={{ color: "var(--vscode-foreground)", margin: 0 }}>Settings</h3>
|
|
||||||
|
<h3 style={{ color: "var(--vscode-foreground)", margin: 0 }}>Provider Settings</h3>
|
||||||
<VSCodeButton onClick={handleSubmit}>Done</VSCodeButton>
|
<VSCodeButton onClick={handleSubmit}>Done</VSCodeButton>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -143,6 +147,8 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|||||||
|
|
||||||
<div style={{ marginBottom: 5 }}>
|
<div style={{ marginBottom: 5 }}>
|
||||||
<div style={{ marginBottom: 15 }}>
|
<div style={{ marginBottom: 15 }}>
|
||||||
|
<h3 style={{ color: "var(--vscode-foreground)", margin: 0, marginBottom: 15 }}>Agent Settings</h3>
|
||||||
|
|
||||||
<label style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>Preferred Language</label>
|
<label style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>Preferred Language</label>
|
||||||
<select
|
<select
|
||||||
value={preferredLanguage}
|
value={preferredLanguage}
|
||||||
@@ -264,7 +270,7 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ marginBottom: 5, border: "2px solid var(--vscode-errorForeground)", borderRadius: "4px", padding: "10px" }}>
|
<div style={{ marginBottom: 15, border: "2px solid var(--vscode-errorForeground)", borderRadius: "4px", padding: "10px" }}>
|
||||||
<h4 style={{ fontWeight: 500, margin: "0 0 10px 0", color: "var(--vscode-errorForeground)" }}>⚠️ High-Risk Auto-Approve Settings</h4>
|
<h4 style={{ fontWeight: 500, margin: "0 0 10px 0", color: "var(--vscode-errorForeground)" }}>⚠️ High-Risk Auto-Approve Settings</h4>
|
||||||
<p style={{ fontSize: "12px", marginBottom: 15, color: "var(--vscode-descriptionForeground)" }}>
|
<p style={{ fontSize: "12px", marginBottom: 15, color: "var(--vscode-descriptionForeground)" }}>
|
||||||
The following settings allow Cline to automatically perform potentially dangerous operations without requiring approval.
|
The following settings allow Cline to automatically perform potentially dangerous operations without requiring approval.
|
||||||
@@ -422,24 +428,69 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ marginBottom: 5 }}>
|
<div style={{ marginBottom: 5 }}>
|
||||||
<h4 style={{ fontWeight: 500, marginBottom: 10 }}>Experimental Features</h4>
|
|
||||||
|
|
||||||
<div style={{ marginBottom: 10 }}>
|
<div style={{ marginBottom: 10 }}>
|
||||||
<VSCodeCheckbox checked={browserLargeViewport} onChange={(e: any) => setBrowserLargeViewport(e.target.checked)}>
|
<div style={{ marginBottom: 15 }}>
|
||||||
<span style={{ fontWeight: "500" }}>Use larger browser viewport (1280x800)</span>
|
<h3 style={{ color: "var(--vscode-foreground)", margin: 0, marginBottom: 15 }}>Browser Settings</h3>
|
||||||
</VSCodeCheckbox>
|
<label style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>Viewport Size</label>
|
||||||
<p
|
<select
|
||||||
style={{
|
value={browserViewportSize}
|
||||||
|
onChange={(e) => setBrowserViewportSize(e.target.value)}
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
padding: "4px 8px",
|
||||||
|
backgroundColor: "var(--vscode-input-background)",
|
||||||
|
color: "var(--vscode-input-foreground)",
|
||||||
|
border: "1px solid var(--vscode-input-border)",
|
||||||
|
borderRadius: "2px",
|
||||||
|
height: "28px"
|
||||||
|
}}>
|
||||||
|
<option value="1280x800">Large Desktop (1280x800)</option>
|
||||||
|
<option value="900x600">Small Desktop (900x600)</option>
|
||||||
|
<option value="768x1024">Tablet (768x1024)</option>
|
||||||
|
<option value="360x640">Mobile (360x640)</option>
|
||||||
|
</select>
|
||||||
|
<p style={{
|
||||||
fontSize: "12px",
|
fontSize: "12px",
|
||||||
marginTop: "5px",
|
marginTop: "5px",
|
||||||
color: "var(--vscode-descriptionForeground)",
|
color: "var(--vscode-descriptionForeground)",
|
||||||
}}>
|
}}>
|
||||||
When enabled, Cline will use a larger viewport size for browser interactions.
|
Select the viewport size for browser interactions. This affects how websites are displayed and interacted with.
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginBottom: 15 }}>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: '5px' }}>
|
||||||
|
<span style={{ fontWeight: "500", minWidth: '100px' }}>Screenshot Quality</span>
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min="1"
|
||||||
|
max="100"
|
||||||
|
step="1"
|
||||||
|
value={screenshotQuality ?? 75}
|
||||||
|
onChange={(e) => setScreenshotQuality(parseInt(e.target.value))}
|
||||||
|
style={{
|
||||||
|
flexGrow: 1,
|
||||||
|
accentColor: 'var(--vscode-button-background)',
|
||||||
|
height: '2px'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span style={{ minWidth: '35px', textAlign: 'left' }}>
|
||||||
|
{screenshotQuality ?? 75}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p style={{
|
||||||
|
fontSize: "12px",
|
||||||
|
marginTop: "5px",
|
||||||
|
color: "var(--vscode-descriptionForeground)",
|
||||||
|
}}>
|
||||||
|
Adjust the WebP quality of browser screenshots. Higher values provide clearer screenshots but increase token usage.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ marginBottom: 5 }}>
|
<div style={{ marginBottom: 5 }}>
|
||||||
<div style={{ marginBottom: 10 }}>
|
<div style={{ marginBottom: 10 }}>
|
||||||
|
<h3 style={{ color: "var(--vscode-foreground)", margin: 0, marginBottom: 15 }}>Notification Settings</h3>
|
||||||
<VSCodeCheckbox checked={soundEnabled} onChange={(e: any) => setSoundEnabled(e.target.checked)}>
|
<VSCodeCheckbox checked={soundEnabled} onChange={(e: any) => setSoundEnabled(e.target.checked)}>
|
||||||
<span style={{ fontWeight: "500" }}>Enable sound effects</span>
|
<span style={{ fontWeight: "500" }}>Enable sound effects</span>
|
||||||
</VSCodeCheckbox>
|
</VSCodeCheckbox>
|
||||||
@@ -468,9 +519,10 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|||||||
accentColor: 'var(--vscode-button-background)',
|
accentColor: 'var(--vscode-button-background)',
|
||||||
height: '2px'
|
height: '2px'
|
||||||
}}
|
}}
|
||||||
|
aria-label="Volume"
|
||||||
/>
|
/>
|
||||||
<span style={{ minWidth: '35px', textAlign: 'left' }}>
|
<span style={{ minWidth: '35px', textAlign: 'left' }}>
|
||||||
{Math.round((soundVolume ?? 0.5) * 100)}%
|
{((soundVolume ?? 0.5) * 100).toFixed(0)}%
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -61,6 +61,17 @@ jest.mock('@vscode/webview-ui-toolkit/react', () => ({
|
|||||||
<div onChange={onChange}>
|
<div onChange={onChange}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
),
|
||||||
|
VSCodeSlider: ({ value, onChange }: any) => (
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => onChange({ target: { value: Number(e.target.value) } })}
|
||||||
|
min={0}
|
||||||
|
max={1}
|
||||||
|
step={0.01}
|
||||||
|
style={{ flexGrow: 1, height: '2px' }}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@@ -75,6 +86,8 @@ const mockPostMessage = (state: any) => {
|
|||||||
shouldShowAnnouncement: false,
|
shouldShowAnnouncement: false,
|
||||||
allowedCommands: [],
|
allowedCommands: [],
|
||||||
alwaysAllowExecute: false,
|
alwaysAllowExecute: false,
|
||||||
|
soundEnabled: false,
|
||||||
|
soundVolume: 0.5,
|
||||||
...state
|
...state
|
||||||
}
|
}
|
||||||
}, '*')
|
}, '*')
|
||||||
@@ -106,7 +119,7 @@ describe('SettingsView - Sound Settings', () => {
|
|||||||
expect(soundCheckbox).not.toBeChecked()
|
expect(soundCheckbox).not.toBeChecked()
|
||||||
|
|
||||||
// Volume slider should not be visible when sound is disabled
|
// Volume slider should not be visible when sound is disabled
|
||||||
expect(screen.queryByRole('slider')).not.toBeInTheDocument()
|
expect(screen.queryByRole('slider', { name: /volume/i })).not.toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('toggles sound setting and sends message to VSCode', () => {
|
it('toggles sound setting and sends message to VSCode', () => {
|
||||||
@@ -142,9 +155,9 @@ describe('SettingsView - Sound Settings', () => {
|
|||||||
fireEvent.click(soundCheckbox)
|
fireEvent.click(soundCheckbox)
|
||||||
|
|
||||||
// Volume slider should be visible
|
// Volume slider should be visible
|
||||||
const volumeSlider = screen.getByRole('slider')
|
const volumeSlider = screen.getByRole('slider', { name: /volume/i })
|
||||||
expect(volumeSlider).toBeInTheDocument()
|
expect(volumeSlider).toBeInTheDocument()
|
||||||
expect(volumeSlider).toHaveValue('0.5') // Default value
|
expect(volumeSlider).toHaveValue('0.5')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('updates volume and sends message to VSCode when slider changes', () => {
|
it('updates volume and sends message to VSCode when slider changes', () => {
|
||||||
@@ -157,23 +170,18 @@ describe('SettingsView - Sound Settings', () => {
|
|||||||
fireEvent.click(soundCheckbox)
|
fireEvent.click(soundCheckbox)
|
||||||
|
|
||||||
// Change volume
|
// Change volume
|
||||||
const volumeSlider = screen.getByRole('slider')
|
const volumeSlider = screen.getByRole('slider', { name: /volume/i })
|
||||||
fireEvent.change(volumeSlider, { target: { value: '0.75' } })
|
fireEvent.change(volumeSlider, { target: { value: '0.75' } })
|
||||||
|
|
||||||
// Verify volume display updates
|
|
||||||
expect(screen.getByText('75%')).toBeInTheDocument()
|
|
||||||
|
|
||||||
// Click Done to save settings
|
// Click Done to save settings
|
||||||
const doneButton = screen.getByText('Done')
|
const doneButton = screen.getByText('Done')
|
||||||
fireEvent.click(doneButton)
|
fireEvent.click(doneButton)
|
||||||
|
|
||||||
// Verify message sent to VSCode
|
// Verify message sent to VSCode
|
||||||
expect(vscode.postMessage).toHaveBeenCalledWith(
|
expect(vscode.postMessage).toHaveBeenCalledWith({
|
||||||
expect.objectContaining({
|
type: 'soundVolume',
|
||||||
type: 'soundVolume',
|
value: 0.75
|
||||||
value: 0.75
|
})
|
||||||
})
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -32,14 +32,16 @@ export interface ExtensionStateContextType extends ExtensionState {
|
|||||||
setSoundEnabled: (value: boolean) => void
|
setSoundEnabled: (value: boolean) => void
|
||||||
setSoundVolume: (value: number) => void
|
setSoundVolume: (value: number) => void
|
||||||
setDiffEnabled: (value: boolean) => void
|
setDiffEnabled: (value: boolean) => void
|
||||||
setBrowserLargeViewport: (value: boolean) => void
|
setBrowserViewportSize: (value: string) => void
|
||||||
setFuzzyMatchThreshold: (value: number) => void
|
setFuzzyMatchThreshold: (value: number) => void
|
||||||
preferredLanguage: string
|
preferredLanguage: string
|
||||||
setPreferredLanguage: (value: string) => void
|
setPreferredLanguage: (value: string) => void
|
||||||
setWriteDelayMs: (value: number) => void
|
setWriteDelayMs: (value: number) => void
|
||||||
|
screenshotQuality?: number
|
||||||
|
setScreenshotQuality: (value: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const ExtensionStateContext = createContext<ExtensionStateContextType | undefined>(undefined)
|
export const ExtensionStateContext = createContext<ExtensionStateContextType | undefined>(undefined)
|
||||||
|
|
||||||
export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||||
const [state, setState] = useState<ExtensionState>({
|
const [state, setState] = useState<ExtensionState>({
|
||||||
@@ -54,6 +56,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
|
|||||||
fuzzyMatchThreshold: 1.0,
|
fuzzyMatchThreshold: 1.0,
|
||||||
preferredLanguage: 'English',
|
preferredLanguage: 'English',
|
||||||
writeDelayMs: 1000,
|
writeDelayMs: 1000,
|
||||||
|
browserViewportSize: "900x600",
|
||||||
|
screenshotQuality: 75,
|
||||||
})
|
})
|
||||||
const [didHydrateState, setDidHydrateState] = useState(false)
|
const [didHydrateState, setDidHydrateState] = useState(false)
|
||||||
const [showWelcome, setShowWelcome] = useState(false)
|
const [showWelcome, setShowWelcome] = useState(false)
|
||||||
@@ -151,6 +155,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
|
|||||||
soundVolume: state.soundVolume,
|
soundVolume: state.soundVolume,
|
||||||
fuzzyMatchThreshold: state.fuzzyMatchThreshold,
|
fuzzyMatchThreshold: state.fuzzyMatchThreshold,
|
||||||
writeDelayMs: state.writeDelayMs,
|
writeDelayMs: state.writeDelayMs,
|
||||||
|
screenshotQuality: state.screenshotQuality,
|
||||||
setApiConfiguration: (value) => setState((prevState) => ({
|
setApiConfiguration: (value) => setState((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
apiConfiguration: value
|
apiConfiguration: value
|
||||||
@@ -166,10 +171,11 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
|
|||||||
setSoundEnabled: (value) => setState((prevState) => ({ ...prevState, soundEnabled: value })),
|
setSoundEnabled: (value) => setState((prevState) => ({ ...prevState, soundEnabled: value })),
|
||||||
setSoundVolume: (value) => setState((prevState) => ({ ...prevState, soundVolume: value })),
|
setSoundVolume: (value) => setState((prevState) => ({ ...prevState, soundVolume: value })),
|
||||||
setDiffEnabled: (value) => setState((prevState) => ({ ...prevState, diffEnabled: value })),
|
setDiffEnabled: (value) => setState((prevState) => ({ ...prevState, diffEnabled: value })),
|
||||||
setBrowserLargeViewport: (value) => setState((prevState) => ({ ...prevState, browserLargeViewport: value })),
|
setBrowserViewportSize: (value: string) => setState((prevState) => ({ ...prevState, browserViewportSize: value })),
|
||||||
setFuzzyMatchThreshold: (value) => setState((prevState) => ({ ...prevState, fuzzyMatchThreshold: value })),
|
setFuzzyMatchThreshold: (value) => setState((prevState) => ({ ...prevState, fuzzyMatchThreshold: value })),
|
||||||
setPreferredLanguage: (value) => setState((prevState) => ({ ...prevState, preferredLanguage: value })),
|
setPreferredLanguage: (value) => setState((prevState) => ({ ...prevState, preferredLanguage: value })),
|
||||||
setWriteDelayMs: (value) => setState((prevState) => ({ ...prevState, writeDelayMs: value })),
|
setWriteDelayMs: (value) => setState((prevState) => ({ ...prevState, writeDelayMs: value })),
|
||||||
|
setScreenshotQuality: (value) => setState((prevState) => ({ ...prevState, screenshotQuality: value })),
|
||||||
}
|
}
|
||||||
|
|
||||||
return <ExtensionStateContext.Provider value={contextValue}>{children}</ExtensionStateContext.Provider>
|
return <ExtensionStateContext.Provider value={contextValue}>{children}</ExtensionStateContext.Provider>
|
||||||
|
|||||||
Reference in New Issue
Block a user