mirror of
https://github.com/cline/cline.git
synced 2026-09-12 09:14:50 +08:00
Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1bb8512ac7 | ||
|
|
a081b3a958 | ||
|
|
e5b807f081 | ||
|
|
52ab54a93a | ||
|
|
50d1722cca | ||
|
|
2cba4cfe89 | ||
|
|
17db0a9e64 | ||
|
|
43595ee498 | ||
|
|
87210947f1 | ||
|
|
8c9631b664 | ||
|
|
22f5fac523 | ||
|
|
afacfe01ac | ||
|
|
623d6ce36a | ||
|
|
bd7257ed34 |
@@ -235,45 +235,11 @@
|
||||
"configuration": {
|
||||
"title": "Cline",
|
||||
"properties": {
|
||||
"cline.vsCodeLmModelSelector": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"vendor": {
|
||||
"type": "string",
|
||||
"description": "The vendor of the language model (e.g. copilot)"
|
||||
},
|
||||
"family": {
|
||||
"type": "string",
|
||||
"description": "The family of the language model (e.g. gpt-4)"
|
||||
}
|
||||
},
|
||||
"description": "Settings for VSCode Language Model API"
|
||||
},
|
||||
"cline.enableCheckpoints": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Enables extension to save checkpoints of workspace throughout the task. Uses git under the hood which may not work well with large workspaces."
|
||||
},
|
||||
"cline.disableBrowserTool": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Disables extension from spawning browser session."
|
||||
},
|
||||
"cline.modelSettings.o3Mini.reasoningEffort": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"low",
|
||||
"medium",
|
||||
"high"
|
||||
],
|
||||
"default": "medium",
|
||||
"description": "Controls the reasoning effort when using an OpenAI reasoning model. Higher values may result in more thorough but slower responses."
|
||||
},
|
||||
"cline.chromeExecutablePath": {
|
||||
"type": "string",
|
||||
"default": null,
|
||||
"description": "Path to Chrome executable for browser use functionality. If not set, the extension will attempt to find or download it automatically."
|
||||
},
|
||||
"cline.preferredLanguage": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
||||
@@ -40,6 +40,8 @@ message BrowserSettings {
|
||||
Viewport viewport = 1;
|
||||
optional string remote_browser_host = 2;
|
||||
optional bool remote_browser_enabled = 3;
|
||||
optional string chrome_executable_path = 4;
|
||||
optional bool disable_tool_use = 5;
|
||||
}
|
||||
|
||||
message UpdateBrowserSettingsRequest {
|
||||
@@ -47,4 +49,6 @@ message UpdateBrowserSettingsRequest {
|
||||
Viewport viewport = 2;
|
||||
optional string remote_browser_host = 3;
|
||||
optional bool remote_browser_enabled = 4;
|
||||
optional string chrome_executable_path = 5;
|
||||
optional bool disable_tool_use = 6;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { UpdateBrowserSettingsRequest } from "../../../shared/proto/browser"
|
||||
import { Boolean } from "../../../shared/proto/common"
|
||||
import { Controller } from "../index"
|
||||
import { updateGlobalState } from "../../storage/state"
|
||||
import { BrowserSettings as SharedBrowserSettings } from "../../../shared/BrowserSettings"
|
||||
import { updateGlobalState, getGlobalState } from "../../storage/state"
|
||||
import { BrowserSettings as SharedBrowserSettings, DEFAULT_BROWSER_SETTINGS } from "../../../shared/BrowserSettings"
|
||||
|
||||
/**
|
||||
* Update browser settings
|
||||
@@ -12,23 +12,39 @@ import { BrowserSettings as SharedBrowserSettings } from "../../../shared/Browse
|
||||
*/
|
||||
export async function updateBrowserSettings(controller: Controller, request: UpdateBrowserSettingsRequest): Promise<Boolean> {
|
||||
try {
|
||||
// Convert from protobuf format to shared format
|
||||
const browserSettings: SharedBrowserSettings = {
|
||||
// Get current browser settings to preserve fields not in the request
|
||||
const currentSettings = (await getGlobalState(controller.context, "browserSettings")) as SharedBrowserSettings | undefined
|
||||
const mergedWithDefaults = { ...DEFAULT_BROWSER_SETTINGS, ...currentSettings }
|
||||
|
||||
// Convert from protobuf format to shared format, merging with existing settings
|
||||
const newBrowserSettings: SharedBrowserSettings = {
|
||||
...mergedWithDefaults, // Start with existing settings (and defaults)
|
||||
viewport: {
|
||||
width: request.viewport?.width || 900,
|
||||
height: request.viewport?.height || 600,
|
||||
// Apply updates from request
|
||||
width: request.viewport?.width || mergedWithDefaults.viewport.width,
|
||||
height: request.viewport?.height || mergedWithDefaults.viewport.height,
|
||||
},
|
||||
remoteBrowserEnabled: request.remoteBrowserEnabled || false,
|
||||
remoteBrowserHost: request.remoteBrowserHost || undefined,
|
||||
// Explicitly handle optional boolean and string fields from the request
|
||||
remoteBrowserEnabled:
|
||||
request.remoteBrowserEnabled === undefined
|
||||
? mergedWithDefaults.remoteBrowserEnabled
|
||||
: request.remoteBrowserEnabled,
|
||||
remoteBrowserHost:
|
||||
request.remoteBrowserHost === undefined ? mergedWithDefaults.remoteBrowserHost : request.remoteBrowserHost,
|
||||
chromeExecutablePath:
|
||||
// If chromeExecutablePath is explicitly in the request (even as ""), use it.
|
||||
// Otherwise, fall back to mergedWithDefaults.
|
||||
"chromeExecutablePath" in request ? request.chromeExecutablePath : mergedWithDefaults.chromeExecutablePath,
|
||||
disableToolUse: request.disableToolUse === undefined ? mergedWithDefaults.disableToolUse : request.disableToolUse,
|
||||
}
|
||||
|
||||
// Update global state with new settings
|
||||
await updateGlobalState(controller.context, "browserSettings", browserSettings)
|
||||
await updateGlobalState(controller.context, "browserSettings", newBrowserSettings)
|
||||
|
||||
// Update task browser settings if task exists
|
||||
if (controller.task) {
|
||||
controller.task.browserSettings = browserSettings
|
||||
controller.task.browserSession.browserSettings = browserSettings
|
||||
controller.task.browserSettings = newBrowserSettings
|
||||
controller.task.browserSession.browserSettings = newBrowserSettings
|
||||
}
|
||||
|
||||
// Post updated state to webview
|
||||
|
||||
+16
-1
@@ -1442,13 +1442,28 @@ export class Task {
|
||||
return statusCode && !message.includes(statusCode.toString()) ? `${statusCode} - ${message}` : message
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the disableBrowserTool setting from VSCode configuration to browserSettings
|
||||
*/
|
||||
private async migrateDisableBrowserToolSetting(): Promise<void> {
|
||||
const config = vscode.workspace.getConfiguration("cline")
|
||||
const disableBrowserTool = vscode.workspace.getConfiguration("cline").get<boolean>("disableBrowserTool")
|
||||
|
||||
if (disableBrowserTool !== undefined) {
|
||||
this.browserSettings.disableToolUse = disableBrowserTool
|
||||
// Remove from VSCode configuration
|
||||
await config.update("disableBrowserTool", undefined, true)
|
||||
}
|
||||
}
|
||||
|
||||
async *attemptApiRequest(previousApiReqIndex: number): ApiStream {
|
||||
// Wait for MCP servers to be connected before generating system prompt
|
||||
await pWaitFor(() => this.mcpHub.isConnecting !== true, { timeout: 10_000 }).catch(() => {
|
||||
console.error("MCP servers failed to connect in time")
|
||||
})
|
||||
|
||||
const disableBrowserTool = vscode.workspace.getConfiguration("cline").get<boolean>("disableBrowserTool") ?? false
|
||||
await this.migrateDisableBrowserToolSetting()
|
||||
const disableBrowserTool = this.browserSettings.disableToolUse ?? false
|
||||
// cline browser tool uses image recognition for navigation (requires model image support).
|
||||
const modelSupportsBrowserUse = this.api.getModel().info.supportsImages ?? false
|
||||
|
||||
|
||||
@@ -67,11 +67,25 @@ export class BrowserSession {
|
||||
}
|
||||
}
|
||||
|
||||
async getDetectedChromePath(): Promise<{ path: string; isBundled: boolean }> {
|
||||
// First check VSCode config
|
||||
/**
|
||||
* Migrates the chromeExecutablePath setting from VSCode configuration to browserSettings
|
||||
*/
|
||||
private async migrateChromeExecutablePathSetting(): Promise<void> {
|
||||
const config = vscode.workspace.getConfiguration("cline")
|
||||
const configPath = vscode.workspace.getConfiguration("cline").get<string>("chromeExecutablePath")
|
||||
if (configPath && (await fileExistsAtPath(configPath))) {
|
||||
return { path: configPath, isBundled: false }
|
||||
|
||||
if (configPath !== undefined) {
|
||||
this.browserSettings.chromeExecutablePath = configPath
|
||||
// Remove from VSCode configuration
|
||||
await config.update("chromeExecutablePath", undefined, true)
|
||||
}
|
||||
}
|
||||
|
||||
async getDetectedChromePath(): Promise<{ path: string; isBundled: boolean }> {
|
||||
// First check browserSettings (from UI, stored in global state)
|
||||
await this.migrateChromeExecutablePathSetting()
|
||||
if (this.browserSettings.chromeExecutablePath && (await fileExistsAtPath(this.browserSettings.chromeExecutablePath))) {
|
||||
return { path: this.browserSettings.chromeExecutablePath, isBundled: false }
|
||||
}
|
||||
|
||||
// Then try to find system Chrome
|
||||
|
||||
@@ -8,6 +8,8 @@ export interface BrowserSettings {
|
||||
// chromeType: "chromium" | "system"
|
||||
remoteBrowserHost?: string
|
||||
remoteBrowserEnabled?: boolean
|
||||
chromeExecutablePath?: string
|
||||
disableToolUse?: boolean
|
||||
}
|
||||
|
||||
export const DEFAULT_BROWSER_SETTINGS: BrowserSettings = {
|
||||
@@ -17,7 +19,9 @@ export const DEFAULT_BROWSER_SETTINGS: BrowserSettings = {
|
||||
},
|
||||
remoteBrowserEnabled: false,
|
||||
remoteBrowserHost: "http://localhost:9222",
|
||||
chromeExecutablePath: "", // Changed from undefined to empty string
|
||||
// chromeType: "chromium",
|
||||
disableToolUse: false,
|
||||
}
|
||||
|
||||
export const BROWSER_VIEWPORT_PRESETS = {
|
||||
|
||||
@@ -36,6 +36,8 @@ export interface BrowserSettings {
|
||||
viewport?: Viewport | undefined
|
||||
remoteBrowserHost?: string | undefined
|
||||
remoteBrowserEnabled?: boolean | undefined
|
||||
chromeExecutablePath?: string | undefined
|
||||
disableToolUse?: boolean | undefined
|
||||
}
|
||||
|
||||
export interface UpdateBrowserSettingsRequest {
|
||||
@@ -43,6 +45,8 @@ export interface UpdateBrowserSettingsRequest {
|
||||
viewport?: Viewport | undefined
|
||||
remoteBrowserHost?: string | undefined
|
||||
remoteBrowserEnabled?: boolean | undefined
|
||||
chromeExecutablePath?: string | undefined
|
||||
disableToolUse?: boolean | undefined
|
||||
}
|
||||
|
||||
function createBaseBrowserConnectionInfo(): BrowserConnectionInfo {
|
||||
@@ -382,7 +386,13 @@ export const Viewport: MessageFns<Viewport> = {
|
||||
}
|
||||
|
||||
function createBaseBrowserSettings(): BrowserSettings {
|
||||
return { viewport: undefined, remoteBrowserHost: undefined, remoteBrowserEnabled: undefined }
|
||||
return {
|
||||
viewport: undefined,
|
||||
remoteBrowserHost: undefined,
|
||||
remoteBrowserEnabled: undefined,
|
||||
chromeExecutablePath: undefined,
|
||||
disableToolUse: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
export const BrowserSettings: MessageFns<BrowserSettings> = {
|
||||
@@ -396,6 +406,12 @@ export const BrowserSettings: MessageFns<BrowserSettings> = {
|
||||
if (message.remoteBrowserEnabled !== undefined) {
|
||||
writer.uint32(24).bool(message.remoteBrowserEnabled)
|
||||
}
|
||||
if (message.chromeExecutablePath !== undefined) {
|
||||
writer.uint32(34).string(message.chromeExecutablePath)
|
||||
}
|
||||
if (message.disableToolUse !== undefined) {
|
||||
writer.uint32(40).bool(message.disableToolUse)
|
||||
}
|
||||
return writer
|
||||
},
|
||||
|
||||
@@ -430,6 +446,22 @@ export const BrowserSettings: MessageFns<BrowserSettings> = {
|
||||
message.remoteBrowserEnabled = reader.bool()
|
||||
continue
|
||||
}
|
||||
case 4: {
|
||||
if (tag !== 34) {
|
||||
break
|
||||
}
|
||||
|
||||
message.chromeExecutablePath = reader.string()
|
||||
continue
|
||||
}
|
||||
case 5: {
|
||||
if (tag !== 40) {
|
||||
break
|
||||
}
|
||||
|
||||
message.disableToolUse = reader.bool()
|
||||
continue
|
||||
}
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break
|
||||
@@ -446,6 +478,8 @@ export const BrowserSettings: MessageFns<BrowserSettings> = {
|
||||
remoteBrowserEnabled: isSet(object.remoteBrowserEnabled)
|
||||
? globalThis.Boolean(object.remoteBrowserEnabled)
|
||||
: undefined,
|
||||
chromeExecutablePath: isSet(object.chromeExecutablePath) ? globalThis.String(object.chromeExecutablePath) : undefined,
|
||||
disableToolUse: isSet(object.disableToolUse) ? globalThis.Boolean(object.disableToolUse) : undefined,
|
||||
}
|
||||
},
|
||||
|
||||
@@ -460,6 +494,12 @@ export const BrowserSettings: MessageFns<BrowserSettings> = {
|
||||
if (message.remoteBrowserEnabled !== undefined) {
|
||||
obj.remoteBrowserEnabled = message.remoteBrowserEnabled
|
||||
}
|
||||
if (message.chromeExecutablePath !== undefined) {
|
||||
obj.chromeExecutablePath = message.chromeExecutablePath
|
||||
}
|
||||
if (message.disableToolUse !== undefined) {
|
||||
obj.disableToolUse = message.disableToolUse
|
||||
}
|
||||
return obj
|
||||
},
|
||||
|
||||
@@ -472,12 +512,21 @@ export const BrowserSettings: MessageFns<BrowserSettings> = {
|
||||
object.viewport !== undefined && object.viewport !== null ? Viewport.fromPartial(object.viewport) : undefined
|
||||
message.remoteBrowserHost = object.remoteBrowserHost ?? undefined
|
||||
message.remoteBrowserEnabled = object.remoteBrowserEnabled ?? undefined
|
||||
message.chromeExecutablePath = object.chromeExecutablePath ?? undefined
|
||||
message.disableToolUse = object.disableToolUse ?? undefined
|
||||
return message
|
||||
},
|
||||
}
|
||||
|
||||
function createBaseUpdateBrowserSettingsRequest(): UpdateBrowserSettingsRequest {
|
||||
return { metadata: undefined, viewport: undefined, remoteBrowserHost: undefined, remoteBrowserEnabled: undefined }
|
||||
return {
|
||||
metadata: undefined,
|
||||
viewport: undefined,
|
||||
remoteBrowserHost: undefined,
|
||||
remoteBrowserEnabled: undefined,
|
||||
chromeExecutablePath: undefined,
|
||||
disableToolUse: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
export const UpdateBrowserSettingsRequest: MessageFns<UpdateBrowserSettingsRequest> = {
|
||||
@@ -494,6 +543,12 @@ export const UpdateBrowserSettingsRequest: MessageFns<UpdateBrowserSettingsReque
|
||||
if (message.remoteBrowserEnabled !== undefined) {
|
||||
writer.uint32(32).bool(message.remoteBrowserEnabled)
|
||||
}
|
||||
if (message.chromeExecutablePath !== undefined) {
|
||||
writer.uint32(42).string(message.chromeExecutablePath)
|
||||
}
|
||||
if (message.disableToolUse !== undefined) {
|
||||
writer.uint32(48).bool(message.disableToolUse)
|
||||
}
|
||||
return writer
|
||||
},
|
||||
|
||||
@@ -536,6 +591,22 @@ export const UpdateBrowserSettingsRequest: MessageFns<UpdateBrowserSettingsReque
|
||||
message.remoteBrowserEnabled = reader.bool()
|
||||
continue
|
||||
}
|
||||
case 5: {
|
||||
if (tag !== 42) {
|
||||
break
|
||||
}
|
||||
|
||||
message.chromeExecutablePath = reader.string()
|
||||
continue
|
||||
}
|
||||
case 6: {
|
||||
if (tag !== 48) {
|
||||
break
|
||||
}
|
||||
|
||||
message.disableToolUse = reader.bool()
|
||||
continue
|
||||
}
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break
|
||||
@@ -553,6 +624,8 @@ export const UpdateBrowserSettingsRequest: MessageFns<UpdateBrowserSettingsReque
|
||||
remoteBrowserEnabled: isSet(object.remoteBrowserEnabled)
|
||||
? globalThis.Boolean(object.remoteBrowserEnabled)
|
||||
: undefined,
|
||||
chromeExecutablePath: isSet(object.chromeExecutablePath) ? globalThis.String(object.chromeExecutablePath) : undefined,
|
||||
disableToolUse: isSet(object.disableToolUse) ? globalThis.Boolean(object.disableToolUse) : undefined,
|
||||
}
|
||||
},
|
||||
|
||||
@@ -570,6 +643,12 @@ export const UpdateBrowserSettingsRequest: MessageFns<UpdateBrowserSettingsReque
|
||||
if (message.remoteBrowserEnabled !== undefined) {
|
||||
obj.remoteBrowserEnabled = message.remoteBrowserEnabled
|
||||
}
|
||||
if (message.chromeExecutablePath !== undefined) {
|
||||
obj.chromeExecutablePath = message.chromeExecutablePath
|
||||
}
|
||||
if (message.disableToolUse !== undefined) {
|
||||
obj.disableToolUse = message.disableToolUse
|
||||
}
|
||||
return obj
|
||||
},
|
||||
|
||||
@@ -584,6 +663,8 @@ export const UpdateBrowserSettingsRequest: MessageFns<UpdateBrowserSettingsReque
|
||||
object.viewport !== undefined && object.viewport !== null ? Viewport.fromPartial(object.viewport) : undefined
|
||||
message.remoteBrowserHost = object.remoteBrowserHost ?? undefined
|
||||
message.remoteBrowserEnabled = object.remoteBrowserEnabled ?? undefined
|
||||
message.chromeExecutablePath = object.chromeExecutablePath ?? undefined
|
||||
message.disableToolUse = object.disableToolUse ?? undefined
|
||||
return message
|
||||
},
|
||||
}
|
||||
|
||||
@@ -53,14 +53,4 @@ describe("Extension Tests", function () {
|
||||
await vscode.commands.executeCommand("cline.historyButtonClicked")
|
||||
// Success if no error thrown
|
||||
})
|
||||
|
||||
it("should handle advanced settings configuration", async () => {
|
||||
// Test browser session setting
|
||||
await vscode.workspace.getConfiguration().update("cline.disableBrowserTool", true, true)
|
||||
const updatedConfig = vscode.workspace.getConfiguration("cline")
|
||||
expect(updatedConfig.get("disableBrowserTool")).to.be.true
|
||||
|
||||
// Reset settings
|
||||
await vscode.workspace.getConfiguration().update("cline.disableBrowserTool", undefined, true)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -37,8 +37,22 @@ const ConnectionStatusIndicator = ({
|
||||
)
|
||||
}
|
||||
|
||||
const CollapsibleContent = styled.div<{ isOpen: boolean }>`
|
||||
overflow: hidden;
|
||||
transition:
|
||||
max-height 0.3s ease-in-out,
|
||||
opacity 0.3s ease-in-out,
|
||||
margin-top 0.3s ease-in-out,
|
||||
visibility 0.3s ease-in-out;
|
||||
max-height: ${({ isOpen }) => (isOpen ? "1000px" : "0")}; // Sufficiently large height
|
||||
opacity: ${({ isOpen }) => (isOpen ? 1 : 0)};
|
||||
margin-top: ${({ isOpen }) => (isOpen ? "15px" : "0")};
|
||||
visibility: ${({ isOpen }) => (isOpen ? "visible" : "hidden")};
|
||||
`
|
||||
|
||||
export const BrowserSettingsSection: React.FC = () => {
|
||||
const { browserSettings } = useExtensionState()
|
||||
const [localChromePath, setLocalChromePath] = useState(browserSettings.chromeExecutablePath || "")
|
||||
const [isCheckingConnection, setIsCheckingConnection] = useState(false)
|
||||
const [connectionStatus, setConnectionStatus] = useState<boolean | null>(null)
|
||||
const [relaunchResult, setRelaunchResult] = useState<{ success: boolean; message: string } | null>(null)
|
||||
@@ -91,6 +105,14 @@ export const BrowserSettingsSection: React.FC = () => {
|
||||
})
|
||||
}, [])
|
||||
|
||||
// Sync localChromePath with global state
|
||||
useEffect(() => {
|
||||
if (browserSettings.chromeExecutablePath !== localChromePath) {
|
||||
setLocalChromePath(browserSettings.chromeExecutablePath || "")
|
||||
}
|
||||
// Removed sync for local disableToolUse state
|
||||
}, [browserSettings.chromeExecutablePath, browserSettings.disableToolUse])
|
||||
|
||||
// Debounced connection check function
|
||||
const debouncedCheckConnection = useCallback(
|
||||
debounce(() => {
|
||||
@@ -147,6 +169,8 @@ export const BrowserSettingsSection: React.FC = () => {
|
||||
},
|
||||
remoteBrowserEnabled: browserSettings.remoteBrowserEnabled,
|
||||
remoteBrowserHost: browserSettings.remoteBrowserHost,
|
||||
chromeExecutablePath: browserSettings.chromeExecutablePath,
|
||||
disableToolUse: browserSettings.disableToolUse,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.value) {
|
||||
@@ -169,6 +193,8 @@ export const BrowserSettingsSection: React.FC = () => {
|
||||
remoteBrowserEnabled: enabled,
|
||||
// If disabling, also clear the host
|
||||
remoteBrowserHost: enabled ? browserSettings.remoteBrowserHost : undefined,
|
||||
chromeExecutablePath: browserSettings.chromeExecutablePath,
|
||||
disableToolUse: browserSettings.disableToolUse,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.value) {
|
||||
@@ -189,6 +215,55 @@ export const BrowserSettingsSection: React.FC = () => {
|
||||
},
|
||||
remoteBrowserEnabled: browserSettings.remoteBrowserEnabled,
|
||||
remoteBrowserHost: host,
|
||||
chromeExecutablePath: browserSettings.chromeExecutablePath,
|
||||
disableToolUse: browserSettings.disableToolUse,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.value) {
|
||||
console.error("Failed to update browser settings")
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error updating browser settings:", error)
|
||||
})
|
||||
}
|
||||
|
||||
const debouncedUpdateChromePath = useCallback(
|
||||
debounce((newPath: string | undefined) => {
|
||||
BrowserServiceClient.updateBrowserSettings({
|
||||
metadata: {},
|
||||
viewport: {
|
||||
width: browserSettings.viewport.width,
|
||||
height: browserSettings.viewport.height,
|
||||
},
|
||||
remoteBrowserEnabled: browserSettings.remoteBrowserEnabled,
|
||||
remoteBrowserHost: browserSettings.remoteBrowserHost,
|
||||
chromeExecutablePath: newPath,
|
||||
disableToolUse: browserSettings.disableToolUse,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.value) {
|
||||
console.error("Failed to update browser settings for chromeExecutablePath")
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error updating browser settings for chromeExecutablePath:", error)
|
||||
})
|
||||
}, 500),
|
||||
[browserSettings],
|
||||
)
|
||||
|
||||
const updateChromeExecutablePath = (path: string | undefined) => {
|
||||
BrowserServiceClient.updateBrowserSettings({
|
||||
metadata: {},
|
||||
viewport: {
|
||||
width: browserSettings.viewport.width,
|
||||
height: browserSettings.viewport.height,
|
||||
},
|
||||
remoteBrowserEnabled: browserSettings.remoteBrowserEnabled,
|
||||
remoteBrowserHost: browserSettings.remoteBrowserHost,
|
||||
chromeExecutablePath: path,
|
||||
disableToolUse: browserSettings.disableToolUse,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.value) {
|
||||
@@ -247,6 +322,28 @@ export const BrowserSettingsSection: React.FC = () => {
|
||||
return () => clearInterval(pollInterval)
|
||||
}, [browserSettings.remoteBrowserEnabled, checkConnectionOnce])
|
||||
|
||||
const updateDisableToolUse = (disabled: boolean) => {
|
||||
BrowserServiceClient.updateBrowserSettings({
|
||||
metadata: {},
|
||||
viewport: {
|
||||
width: browserSettings.viewport.width,
|
||||
height: browserSettings.viewport.height,
|
||||
},
|
||||
remoteBrowserEnabled: browserSettings.remoteBrowserEnabled,
|
||||
remoteBrowserHost: browserSettings.remoteBrowserHost,
|
||||
chromeExecutablePath: browserSettings.chromeExecutablePath,
|
||||
disableToolUse: disabled,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.value) {
|
||||
console.error("Failed to update disableToolUse setting")
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error updating disableToolUse setting:", error)
|
||||
})
|
||||
}
|
||||
|
||||
const relaunchChromeDebugMode = () => {
|
||||
setDebugMode(true)
|
||||
setRelaunchResult(null)
|
||||
@@ -260,121 +357,169 @@ export const BrowserSettingsSection: React.FC = () => {
|
||||
// Determine if we should show the relaunch button
|
||||
const isRemoteEnabled = Boolean(browserSettings.remoteBrowserEnabled)
|
||||
const shouldShowRelaunchButton = isRemoteEnabled && connectionStatus === false
|
||||
const isSubSettingsOpen = !(browserSettings.disableToolUse || false)
|
||||
|
||||
return (
|
||||
<div
|
||||
id="browser-settings-section"
|
||||
style={{ marginBottom: 20, borderTop: "1px solid var(--vscode-panel-border)", paddingTop: 15 }}>
|
||||
<h3 style={{ color: "var(--vscode-foreground)", margin: "0 0 10px 0", fontSize: "14px" }}>Browser Settings</h3>
|
||||
<div style={{ marginBottom: 15 }}>
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
<label style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>Viewport size</label>
|
||||
<VSCodeDropdown
|
||||
style={{ width: "100%" }}
|
||||
value={
|
||||
Object.entries(BROWSER_VIEWPORT_PRESETS).find(([_, size]) => {
|
||||
const typedSize = size as { width: number; height: number }
|
||||
return (
|
||||
typedSize.width === browserSettings.viewport.width &&
|
||||
typedSize.height === browserSettings.viewport.height
|
||||
)
|
||||
})?.[0]
|
||||
}
|
||||
onChange={(event) => handleViewportChange(event as Event)}>
|
||||
{Object.entries(BROWSER_VIEWPORT_PRESETS).map(([name]) => (
|
||||
<VSCodeOption key={name} value={name}>
|
||||
{name}
|
||||
</VSCodeOption>
|
||||
))}
|
||||
</VSCodeDropdown>
|
||||
</div>
|
||||
|
||||
{/* Master Toggle */}
|
||||
<div style={{ marginBottom: isSubSettingsOpen ? 0 : 10 }}>
|
||||
<VSCodeCheckbox
|
||||
checked={browserSettings.disableToolUse || false}
|
||||
onChange={(e) => updateDisableToolUse((e.target as HTMLInputElement).checked)}>
|
||||
Disable browser tool usage
|
||||
</VSCodeCheckbox>
|
||||
<p
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
margin: 0,
|
||||
margin: "4px 0 0 0px",
|
||||
}}>
|
||||
Set the size of the browser viewport for screenshots and interactions.
|
||||
Prevent Cline from using browser actions (e.g. launch, click, type).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: 0 }}>
|
||||
<div style={{ marginBottom: 4, display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
||||
<VSCodeCheckbox
|
||||
checked={browserSettings.remoteBrowserEnabled}
|
||||
onChange={(e) => updateRemoteBrowserEnabled((e.target as HTMLInputElement).checked)}>
|
||||
Use remote browser connection
|
||||
</VSCodeCheckbox>
|
||||
<ConnectionStatusIndicator
|
||||
isChecking={isCheckingConnection}
|
||||
isConnected={connectionStatus}
|
||||
remoteBrowserEnabled={browserSettings.remoteBrowserEnabled}
|
||||
/>
|
||||
<CollapsibleContent isOpen={isSubSettingsOpen}>
|
||||
<div style={{ marginBottom: 15 }}>
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
<label style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>Viewport size</label>
|
||||
<VSCodeDropdown
|
||||
style={{ width: "100%" }}
|
||||
value={
|
||||
Object.entries(BROWSER_VIEWPORT_PRESETS).find(([_, size]) => {
|
||||
const typedSize = size as { width: number; height: number }
|
||||
return (
|
||||
typedSize.width === browserSettings.viewport.width &&
|
||||
typedSize.height === browserSettings.viewport.height
|
||||
)
|
||||
})?.[0]
|
||||
}
|
||||
onChange={(event) => handleViewportChange(event as Event)}>
|
||||
{Object.entries(BROWSER_VIEWPORT_PRESETS).map(([name]) => (
|
||||
<VSCodeOption key={name} value={name}>
|
||||
{name}
|
||||
</VSCodeOption>
|
||||
))}
|
||||
</VSCodeDropdown>
|
||||
</div>
|
||||
<p
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
margin: 0,
|
||||
}}>
|
||||
Set the size of the browser viewport for screenshots and interactions.
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
margin: "0 0 6px 0px",
|
||||
}}>
|
||||
Enable Cline to use your Chrome
|
||||
{isBundled ? "(not detected on your machine)" : detectedChromePath ? ` (${detectedChromePath})` : ""}. This
|
||||
requires starting Chrome in debug mode
|
||||
{browserSettings.remoteBrowserEnabled ? (
|
||||
<>
|
||||
{" "}
|
||||
manually (<code>--remote-debugging-port=9222</code>) or using the button below. Enter the host address
|
||||
or leave it blank for automatic discovery.
|
||||
</>
|
||||
) : (
|
||||
"."
|
||||
)}
|
||||
</p>
|
||||
|
||||
{browserSettings.remoteBrowserEnabled && (
|
||||
<div style={{ marginLeft: 0 }}>
|
||||
<VSCodeTextField
|
||||
value={browserSettings.remoteBrowserHost || ""}
|
||||
placeholder="http://localhost:9222"
|
||||
style={{ width: "100%", marginBottom: 8 }}
|
||||
onChange={(e: any) => updateRemoteBrowserHost(e.target.value || undefined)}
|
||||
<div style={{ marginBottom: 0 }}>
|
||||
{" "}
|
||||
{/* This div now contains Remote Connection & Chrome Path */}
|
||||
<div style={{ marginBottom: 4, display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
||||
<VSCodeCheckbox
|
||||
checked={browserSettings.remoteBrowserEnabled}
|
||||
onChange={(e) => updateRemoteBrowserEnabled((e.target as HTMLInputElement).checked)}>
|
||||
Use remote browser connection
|
||||
</VSCodeCheckbox>
|
||||
<ConnectionStatusIndicator
|
||||
isChecking={isCheckingConnection}
|
||||
isConnected={connectionStatus}
|
||||
remoteBrowserEnabled={browserSettings.remoteBrowserEnabled}
|
||||
/>
|
||||
|
||||
{shouldShowRelaunchButton && (
|
||||
<div style={{ display: "flex", gap: "10px", marginBottom: 8, justifyContent: "center" }}>
|
||||
<VSCodeButton style={{ flex: 1 }} disabled={debugMode} onClick={relaunchChromeDebugMode}>
|
||||
{debugMode ? "Relaunching Browser..." : "Relaunch Browser with Debug Mode"}
|
||||
</VSCodeButton>
|
||||
</div>
|
||||
</div>
|
||||
<p
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
margin: "0 0 6px 0px",
|
||||
}}>
|
||||
Enable Cline to use your Chrome
|
||||
{isBundled ? "(not detected on your machine)" : detectedChromePath ? ` (${detectedChromePath})` : ""}. You
|
||||
can specify a custom path below. Using a remote browser connection requires starting Chrome in debug mode
|
||||
{browserSettings.remoteBrowserEnabled ? (
|
||||
<>
|
||||
{" "}
|
||||
manually (<code>--remote-debugging-port=9222</code>) or using the button below. Enter the host
|
||||
address or leave it blank for automatic discovery.
|
||||
</>
|
||||
) : (
|
||||
"."
|
||||
)}
|
||||
</p>
|
||||
{/* Moved remote-specific settings to appear directly after enabling remote connection */}
|
||||
{browserSettings.remoteBrowserEnabled && (
|
||||
<div style={{ marginLeft: 0, marginTop: 8 }}>
|
||||
<VSCodeTextField
|
||||
value={browserSettings.remoteBrowserHost || ""}
|
||||
placeholder="http://localhost:9222"
|
||||
style={{ width: "100%", marginBottom: 8 }}
|
||||
onChange={(e: any) => updateRemoteBrowserHost(e.target.value || undefined)}
|
||||
/>
|
||||
|
||||
{relaunchResult && (
|
||||
<div
|
||||
{shouldShowRelaunchButton && (
|
||||
<div style={{ display: "flex", gap: "10px", marginBottom: 8, justifyContent: "center" }}>
|
||||
<VSCodeButton style={{ flex: 1 }} disabled={debugMode} onClick={relaunchChromeDebugMode}>
|
||||
{debugMode ? "Relaunching Browser..." : "Relaunch Browser with Debug Mode"}
|
||||
</VSCodeButton>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{relaunchResult && (
|
||||
<div
|
||||
style={{
|
||||
padding: "8px",
|
||||
marginBottom: "8px",
|
||||
backgroundColor: relaunchResult.success ? "rgba(0, 128, 0, 0.1)" : "rgba(255, 0, 0, 0.1)",
|
||||
color: relaunchResult.success
|
||||
? "var(--vscode-terminal-ansiGreen)"
|
||||
: "var(--vscode-terminal-ansiRed)",
|
||||
borderRadius: "3px",
|
||||
fontSize: "11px",
|
||||
whiteSpace: "pre-wrap",
|
||||
wordBreak: "break-word",
|
||||
}}>
|
||||
{relaunchResult.message}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<p
|
||||
style={{
|
||||
padding: "8px",
|
||||
marginBottom: "8px",
|
||||
backgroundColor: relaunchResult.success ? "rgba(0, 128, 0, 0.1)" : "rgba(255, 0, 0, 0.1)",
|
||||
color: relaunchResult.success
|
||||
? "var(--vscode-terminal-ansiGreen)"
|
||||
: "var(--vscode-terminal-ansiRed)",
|
||||
borderRadius: "3px",
|
||||
fontSize: "11px",
|
||||
whiteSpace: "pre-wrap",
|
||||
wordBreak: "break-word",
|
||||
}}>
|
||||
{relaunchResult.message}
|
||||
</div>
|
||||
)}
|
||||
|
||||
fontSize: "12px",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
margin: 0,
|
||||
}}></p>
|
||||
</div>
|
||||
)}
|
||||
{/* Chrome Executable Path section now follows remote-specific settings */}
|
||||
<div style={{ marginBottom: 8, marginTop: 8 }}>
|
||||
<label htmlFor="chrome-executable-path" style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>
|
||||
Chrome Executable Path (Optional)
|
||||
</label>
|
||||
<VSCodeTextField
|
||||
id="chrome-executable-path"
|
||||
value={localChromePath}
|
||||
placeholder="e.g., /usr/bin/google-chrome or C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
|
||||
style={{ width: "100%" }}
|
||||
onChange={(e: any) => {
|
||||
const newValue = e.target.value || ""
|
||||
setLocalChromePath(newValue)
|
||||
debouncedUpdateChromePath(newValue) // Send "" if empty, not undefined
|
||||
}}
|
||||
/>
|
||||
<p
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
margin: 0,
|
||||
}}></p>
|
||||
margin: "4px 0 0 0",
|
||||
}}>
|
||||
Leave blank to auto-detect.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</CollapsibleContent>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user