Skip to content

Commit 56d591b

Browse files
committed
Added Browser Assistant Mode
1 parent 5203266 commit 56d591b

File tree

25 files changed

+158
-26
lines changed

25 files changed

+158
-26
lines changed

src/core/webview/ClineProvider.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,6 +1419,10 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
14191419
}
14201420
await this.postStateToWebview()
14211421
break
1422+
case "browserAssistantModeEnabled":
1423+
await this.updateGlobalState("browserAssistantModeEnabled", message.bool ?? false)
1424+
await this.postStateToWebview()
1425+
break
14221426
case "testBrowserConnection":
14231427
// If no text is provided, try auto-discovery
14241428
if (!message.text) {
@@ -2559,6 +2563,7 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
25592563
remoteBrowserHost,
25602564
remoteBrowserEnabled,
25612565
cachedChromeHostUrl,
2566+
browserAssistantModeEnabled,
25622567
writeDelayMs,
25632568
terminalOutputLineLimit,
25642569
terminalShellIntegrationTimeout,
@@ -2628,6 +2633,7 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
26282633
remoteBrowserHost,
26292634
remoteBrowserEnabled: remoteBrowserEnabled ?? false,
26302635
cachedChromeHostUrl: cachedChromeHostUrl,
2636+
browserAssistantModeEnabled: browserAssistantModeEnabled ?? false,
26312637
writeDelayMs: writeDelayMs ?? 1000,
26322638
terminalOutputLineLimit: terminalOutputLineLimit ?? 500,
26332639
terminalShellIntegrationTimeout: terminalShellIntegrationTimeout ?? TERMINAL_SHELL_INTEGRATION_TIMEOUT,
@@ -2714,6 +2720,7 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
27142720
remoteBrowserHost: stateValues.remoteBrowserHost,
27152721
remoteBrowserEnabled: stateValues.remoteBrowserEnabled ?? false,
27162722
cachedChromeHostUrl: stateValues.cachedChromeHostUrl as string | undefined,
2723+
browserAssistantModeEnabled: stateValues.browserAssistantModeEnabled ?? false,
27172724
fuzzyMatchThreshold: stateValues.fuzzyMatchThreshold ?? 1.0,
27182725
writeDelayMs: stateValues.writeDelayMs ?? 1000,
27192726
terminalOutputLineLimit: stateValues.terminalOutputLineLimit ?? 500,

src/exports/roo-code.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ type GlobalSettings = {
234234
screenshotQuality?: number | undefined
235235
remoteBrowserEnabled?: boolean | undefined
236236
remoteBrowserHost?: string | undefined
237+
browserAssistantModeEnabled?: boolean | undefined
237238
cachedChromeHostUrl?: string | undefined
238239
enableCheckpoints?: boolean | undefined
239240
checkpointStorage?: ("task" | "workspace") | undefined

src/exports/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ type GlobalSettings = {
237237
screenshotQuality?: number | undefined
238238
remoteBrowserEnabled?: boolean | undefined
239239
remoteBrowserHost?: string | undefined
240+
browserAssistantModeEnabled?: boolean | undefined
240241
cachedChromeHostUrl?: string | undefined
241242
enableCheckpoints?: boolean | undefined
242243
checkpointStorage?: ("task" | "workspace") | undefined

src/schemas/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,7 @@ export const globalSettingsSchema = z.object({
513513
screenshotQuality: z.number().optional(),
514514
remoteBrowserEnabled: z.boolean().optional(),
515515
remoteBrowserHost: z.string().optional(),
516+
browserAssistantModeEnabled: z.boolean().optional(),
516517
cachedChromeHostUrl: z.string().optional(),
517518

518519
enableCheckpoints: z.boolean().optional(),
@@ -578,12 +579,12 @@ const globalSettingsRecord: GlobalSettingsRecord = {
578579
alwaysAllowSubtasks: undefined,
579580
alwaysAllowExecute: undefined,
580581
allowedCommands: undefined,
581-
582582
browserToolEnabled: undefined,
583583
browserViewportSize: undefined,
584584
screenshotQuality: undefined,
585585
remoteBrowserEnabled: undefined,
586586
remoteBrowserHost: undefined,
587+
browserAssistantModeEnabled: undefined,
587588

588589
enableCheckpoints: undefined,
589590
checkpointStorage: undefined,

src/services/browser/BrowserSession.ts

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -353,63 +353,77 @@ export class BrowserSession {
353353
// Remove trailing slash for comparison
354354
const normalizedNewUrl = url.replace(/\/$/, "")
355355

356-
// Extract the root domain from the URL
357-
const rootDomain = this.getRootDomain(normalizedNewUrl)
358-
356+
// Check if browser assistant mode is enabled
357+
const browserAssistantModeEnabled = this.context.globalState.get("browserAssistantModeEnabled") as
358+
| boolean
359+
| undefined
360+
console.log(`browserAssistantModeEnabled: ${browserAssistantModeEnabled}`)
359361
// Get all current pages
360362
const pages = await this.browser.pages()
361363

362-
// Try to find a page with the same root domain
364+
// Try to find a page with the same root domain or use the latest tab in assistant mode
363365
let existingPage: Page | undefined
364366

365-
for (const page of pages) {
366-
try {
367-
const pageUrl = page.url()
368-
if (pageUrl && this.getRootDomain(pageUrl) === rootDomain) {
369-
existingPage = page
370-
break
367+
if (browserAssistantModeEnabled) {
368+
// In assistant mode, always use the latest tab (last in the array)
369+
// Skip the first page which is usually the blank page
370+
existingPage = pages.length >= 1 ? pages[pages.length - 1] : undefined
371+
console.log(`Browser assistant mode enabled, using latest tab`)
372+
} else {
373+
// Extract the root domain from the URL
374+
const rootDomain = this.getRootDomain(normalizedNewUrl)
375+
376+
// Try to find a page with the same root domain
377+
for (const page of pages) {
378+
try {
379+
const pageUrl = page.url()
380+
if (pageUrl && this.getRootDomain(pageUrl) === rootDomain) {
381+
existingPage = page
382+
break
383+
}
384+
} catch (error) {
385+
// Skip pages that might have been closed or have errors
386+
console.log(`Error checking page URL: ${error}`)
387+
continue
371388
}
372-
} catch (error) {
373-
// Skip pages that might have been closed or have errors
374-
console.log(`Error checking page URL: ${error}`)
375-
continue
376389
}
377390
}
378391

379392
if (existingPage) {
380393
// Tab with the same root domain exists, switch to it
381-
console.log(`Tab with domain ${rootDomain} already exists, switching to it`)
394+
console.log(`Tab exists, switching to it`)
382395

383396
// Update the active page
384397
this.page = existingPage
385398
existingPage.bringToFront()
386399

387-
// Navigate to the new URL if it's different]
400+
// Navigate to the new URL if it's different
388401
const currentUrl = existingPage.url().replace(/\/$/, "") // Remove trailing / if present
389-
if (this.getRootDomain(currentUrl) === rootDomain && currentUrl !== normalizedNewUrl) {
402+
403+
if (
404+
browserAssistantModeEnabled ||
405+
(this.getRootDomain(currentUrl) === this.getRootDomain(normalizedNewUrl) &&
406+
currentUrl !== normalizedNewUrl)
407+
) {
390408
console.log(`Navigating to new URL: ${normalizedNewUrl}`)
391409
console.log(`Current URL: ${currentUrl}`)
392-
console.log(`Root domain: ${this.getRootDomain(currentUrl)}`)
393-
console.log(`New URL: ${normalizedNewUrl}`)
394410
// Navigate to the new URL
395411
return this.doAction(async (page) => {
396412
await this.navigatePageToUrl(page, normalizedNewUrl)
397413
})
398414
} else {
399-
console.log(`Tab with domain ${rootDomain} already exists, and URL is the same: ${normalizedNewUrl}`)
415+
console.log(`URL is the same: ${normalizedNewUrl}`)
400416
// URL is the same, just reload the page to ensure it's up to date
401417
console.log(`Reloading page: ${normalizedNewUrl}`)
402418
console.log(`Current URL: ${currentUrl}`)
403-
console.log(`Root domain: ${this.getRootDomain(currentUrl)}`)
404-
console.log(`New URL: ${normalizedNewUrl}`)
405419
return this.doAction(async (page) => {
406420
await page.reload({ timeout: 7_000, waitUntil: ["domcontentloaded", "networkidle2"] })
407421
await this.waitTillHTMLStable(page)
408422
})
409423
}
410424
} else {
411425
// No tab with this root domain exists, create a new one
412-
console.log(`No tab with domain ${rootDomain} exists, creating a new one`)
426+
console.log(`No suitable tab exists, creating a new one`)
413427
return this.createNewTab(normalizedNewUrl)
414428
}
415429
}

src/shared/ExtensionMessage.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ export interface ExtensionMessage {
6464
| "browserToolEnabled"
6565
| "browserConnectionResult"
6666
| "remoteBrowserEnabled"
67+
| "browserAssistantModeEnabled"
6768
| "ttsStart"
6869
| "ttsStop"
6970
| "maxReadFileLine"
@@ -140,6 +141,7 @@ export type ExtensionState = Pick<
140141
| "screenshotQuality"
141142
| "remoteBrowserEnabled"
142143
| "remoteBrowserHost"
144+
| "browserAssistantModeEnabled"
143145
// | "enableCheckpoints" // Optional in GlobalSettings, required here.
144146
// | "checkpointStorage" // Optional in GlobalSettings, required here.
145147
| "ttsEnabled"

src/shared/WebviewMessage.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { z } from "zod"
2-
import { ApiConfiguration, ApiProvider } from "./api"
3-
import { Mode, PromptComponent, ModeConfig } from "./modes"
2+
import { ApiConfiguration } from "./api"
3+
import { Mode, ModeConfig, PromptComponent } from "./modes"
44

55
export type ClineAskResponse = "yesButtonClicked" | "noButtonClicked" | "messageResponse"
66

@@ -117,6 +117,7 @@ export interface WebviewMessage {
117117
| "testBrowserConnection"
118118
| "browserConnectionResult"
119119
| "remoteBrowserEnabled"
120+
| "browserAssistantModeEnabled"
120121
| "language"
121122
| "maxReadFileLine"
122123
| "searchFiles"

webview-ui/src/components/settings/BrowserSettings.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ type BrowserSettingsProps = HTMLAttributes<HTMLDivElement> & {
1616
screenshotQuality?: number
1717
remoteBrowserHost?: string
1818
remoteBrowserEnabled?: boolean
19+
browserAssistantModeEnabled?: boolean
1920
setCachedStateField: SetCachedStateField<
2021
| "browserToolEnabled"
2122
| "browserViewportSize"
2223
| "screenshotQuality"
2324
| "remoteBrowserHost"
2425
| "remoteBrowserEnabled"
26+
| "browserAssistantModeEnabled"
2527
>
2628
}
2729

@@ -31,6 +33,7 @@ export const BrowserSettings = ({
3133
screenshotQuality,
3234
remoteBrowserHost,
3335
remoteBrowserEnabled,
36+
browserAssistantModeEnabled,
3437
setCachedStateField,
3538
...props
3639
}: BrowserSettingsProps) => {
@@ -211,6 +214,26 @@ export const BrowserSettings = ({
211214
</div>
212215
</>
213216
)}
217+
218+
<div>
219+
<VSCodeCheckbox
220+
checked={browserAssistantModeEnabled}
221+
onChange={(e: any) => {
222+
setCachedStateField("browserAssistantModeEnabled", e.target.checked)
223+
}}>
224+
<label className="block font-medium mb-1">
225+
{t("settings:browser.assistantMode.label")}
226+
</label>
227+
</VSCodeCheckbox>
228+
<div className="text-vscode-descriptionForeground text-sm mt-1">
229+
{t("settings:browser.assistantMode.description")}
230+
</div>
231+
{browserAssistantModeEnabled && (
232+
<div className="text-vscode-descriptionForeground text-sm mt-1">
233+
{t("settings:browser.assistantMode.instructions")}
234+
</div>
235+
)}
236+
</div>
214237
</div>
215238
)}
216239
</Section>

webview-ui/src/components/settings/SettingsView.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone },
133133
writeDelayMs,
134134
showRooIgnoredFiles,
135135
remoteBrowserEnabled,
136+
browserAssistantModeEnabled,
136137
maxReadFileLine,
137138
} = cachedState
138139

@@ -233,6 +234,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone },
233234
vscode.postMessage({ type: "browserViewportSize", text: browserViewportSize })
234235
vscode.postMessage({ type: "remoteBrowserHost", text: remoteBrowserHost })
235236
vscode.postMessage({ type: "remoteBrowserEnabled", bool: remoteBrowserEnabled })
237+
vscode.postMessage({ type: "browserAssistantModeEnabled", bool: browserAssistantModeEnabled })
236238
vscode.postMessage({ type: "fuzzyMatchThreshold", value: fuzzyMatchThreshold ?? 1.0 })
237239
vscode.postMessage({ type: "writeDelayMs", value: writeDelayMs })
238240
vscode.postMessage({ type: "screenshotQuality", value: screenshotQuality ?? 75 })
@@ -436,6 +438,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone },
436438
screenshotQuality={screenshotQuality}
437439
remoteBrowserHost={remoteBrowserHost}
438440
remoteBrowserEnabled={remoteBrowserEnabled}
441+
browserAssistantModeEnabled={browserAssistantModeEnabled}
439442
setCachedStateField={setCachedStateField}
440443
/>
441444
</div>

webview-ui/src/context/ExtensionStateContext.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ export interface ExtensionStateContextType extends ExtensionState {
7878
setTelemetrySetting: (value: TelemetrySetting) => void
7979
remoteBrowserEnabled?: boolean
8080
setRemoteBrowserEnabled: (value: boolean) => void
81+
browserAssistantModeEnabled?: boolean
82+
setBrowserAssistantModeEnabled: (value: boolean) => void
8183
maxReadFileLine: number
8284
setMaxReadFileLine: (value: number) => void
8385
machineId?: string
@@ -311,6 +313,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
311313
setTelemetrySetting: (value) => setState((prevState) => ({ ...prevState, telemetrySetting: value })),
312314
setShowRooIgnoredFiles: (value) => setState((prevState) => ({ ...prevState, showRooIgnoredFiles: value })),
313315
setRemoteBrowserEnabled: (value) => setState((prevState) => ({ ...prevState, remoteBrowserEnabled: value })),
316+
setBrowserAssistantModeEnabled: (value) =>
317+
setState((prevState) => ({ ...prevState, browserAssistantModeEnabled: value })),
314318
setMaxReadFileLine: (value) => setState((prevState) => ({ ...prevState, maxReadFileLine: value })),
315319
setPinnedApiConfigs: (value) => setState((prevState) => ({ ...prevState, pinnedApiConfigs: value })),
316320
togglePinnedApiConfig: (configId) =>

0 commit comments

Comments
 (0)