Skip to content

Browser: POC of click_coordinates tool#308716

Draft
jruales wants to merge 1 commit intomainfrom
jruales/coordinate-click-tool
Draft

Browser: POC of click_coordinates tool#308716
jruales wants to merge 1 commit intomainfrom
jruales/coordinate-click-tool

Conversation

@jruales
Copy link
Copy Markdown
Contributor

@jruales jruales commented Apr 9, 2026

No description provided.

Copilot AI review requested due to automatic review settings April 9, 2026 08:59
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new internal browser chat tool that can click at viewport-relative coordinates, and wires it into the existing browser toolset contribution so it becomes available alongside the other Playwright-backed tools.

Changes:

  • Introduces click_coordinates tool implementation using Playwright page.mouse.click(x, y, …).
  • Adds localized invocation + past-tense messages for the new tool.
  • Registers the tool in browserTools.contribution.ts (tool registry + toolset).
Show a summary per file
File Description
src/vs/workbench/contrib/browserView/electron-browser/tools/clickCoordinateBrowserTool.ts New coordinate-based click tool (schema, messaging, Playwright invocation).
src/vs/workbench/contrib/browserView/electron-browser/tools/browserTools.contribution.ts Registers the new tool with the browser tool registry and toolset.

Copilot's findings

  • Files reviewed: 2/2 changed files
  • Comments generated: 2

Comment on lines +67 to +85
async prepareToolInvocation(_context: IToolInvocationPreparationContext, _token: CancellationToken): Promise<IPreparedToolInvocation | undefined> {
const params = _context.parameters as IClickCoordinateBrowserToolParams;
const link = createBrowserPageLink(params.pageId);
const coordinateLabel = `(${params.x}, ${params.y})`;
return {
invocationMessage: params.button === 'right'
? new MarkdownString(localize('browser.clickCoordinates.invocation.right', 'Right-clicking {0} in {1}', coordinateLabel, link))
: params.button === 'middle'
? new MarkdownString(localize('browser.clickCoordinates.invocation.middle', 'Middle-clicking {0} in {1}', coordinateLabel, link))
: params.dblClick
? new MarkdownString(localize('browser.clickCoordinates.invocation.double', 'Double-clicking {0} in {1}', coordinateLabel, link))
: new MarkdownString(localize('browser.clickCoordinates.invocation', 'Clicking {0} in {1}', coordinateLabel, link)),
pastTenseMessage: params.button === 'right'
? new MarkdownString(localize('browser.clickCoordinates.past.right', 'Right-clicked {0} in {1}', coordinateLabel, link))
: params.button === 'middle'
? new MarkdownString(localize('browser.clickCoordinates.past.middle', 'Middle-clicked {0} in {1}', coordinateLabel, link))
: params.dblClick
? new MarkdownString(localize('browser.clickCoordinates.past.double', 'Double-clicked {0} in {1}', coordinateLabel, link))
: new MarkdownString(localize('browser.clickCoordinates.past', 'Clicked {0} in {1}', coordinateLabel, link)),
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The invocation/past-tense messages prioritize button over dblClick. If a caller passes both dblClick: true and button: 'right'|'middle', invoke() will perform a double-click (via clickCount: 2) but the UI message will still say “Right-clicking/Middle-clicking…”, which is misleading. Consider either (1) giving dblClick precedence in the message, (2) emitting combined wording (e.g. “Right double-clicking”), or (3) rejecting that parameter combination up front.

Suggested change
async prepareToolInvocation(_context: IToolInvocationPreparationContext, _token: CancellationToken): Promise<IPreparedToolInvocation | undefined> {
const params = _context.parameters as IClickCoordinateBrowserToolParams;
const link = createBrowserPageLink(params.pageId);
const coordinateLabel = `(${params.x}, ${params.y})`;
return {
invocationMessage: params.button === 'right'
? new MarkdownString(localize('browser.clickCoordinates.invocation.right', 'Right-clicking {0} in {1}', coordinateLabel, link))
: params.button === 'middle'
? new MarkdownString(localize('browser.clickCoordinates.invocation.middle', 'Middle-clicking {0} in {1}', coordinateLabel, link))
: params.dblClick
? new MarkdownString(localize('browser.clickCoordinates.invocation.double', 'Double-clicking {0} in {1}', coordinateLabel, link))
: new MarkdownString(localize('browser.clickCoordinates.invocation', 'Clicking {0} in {1}', coordinateLabel, link)),
pastTenseMessage: params.button === 'right'
? new MarkdownString(localize('browser.clickCoordinates.past.right', 'Right-clicked {0} in {1}', coordinateLabel, link))
: params.button === 'middle'
? new MarkdownString(localize('browser.clickCoordinates.past.middle', 'Middle-clicked {0} in {1}', coordinateLabel, link))
: params.dblClick
? new MarkdownString(localize('browser.clickCoordinates.past.double', 'Double-clicked {0} in {1}', coordinateLabel, link))
: new MarkdownString(localize('browser.clickCoordinates.past', 'Clicked {0} in {1}', coordinateLabel, link)),
private getClickCoordinateMessage(params: IClickCoordinateBrowserToolParams, coordinateLabel: string, link: string, pastTense: boolean): MarkdownString {
if (params.button === 'right' && params.dblClick) {
return new MarkdownString(localize(
pastTense ? 'browser.clickCoordinates.past.rightDouble' : 'browser.clickCoordinates.invocation.rightDouble',
pastTense ? 'Right double-clicked {0} in {1}' : 'Right double-clicking {0} in {1}',
coordinateLabel,
link
));
}
if (params.button === 'middle' && params.dblClick) {
return new MarkdownString(localize(
pastTense ? 'browser.clickCoordinates.past.middleDouble' : 'browser.clickCoordinates.invocation.middleDouble',
pastTense ? 'Middle double-clicked {0} in {1}' : 'Middle double-clicking {0} in {1}',
coordinateLabel,
link
));
}
if (params.button === 'right') {
return new MarkdownString(localize(
pastTense ? 'browser.clickCoordinates.past.right' : 'browser.clickCoordinates.invocation.right',
pastTense ? 'Right-clicked {0} in {1}' : 'Right-clicking {0} in {1}',
coordinateLabel,
link
));
}
if (params.button === 'middle') {
return new MarkdownString(localize(
pastTense ? 'browser.clickCoordinates.past.middle' : 'browser.clickCoordinates.invocation.middle',
pastTense ? 'Middle-clicked {0} in {1}' : 'Middle-clicking {0} in {1}',
coordinateLabel,
link
));
}
if (params.dblClick) {
return new MarkdownString(localize(
pastTense ? 'browser.clickCoordinates.past.double' : 'browser.clickCoordinates.invocation.double',
pastTense ? 'Double-clicked {0} in {1}' : 'Double-clicking {0} in {1}',
coordinateLabel,
link
));
}
return new MarkdownString(localize(
pastTense ? 'browser.clickCoordinates.past' : 'browser.clickCoordinates.invocation',
pastTense ? 'Clicked {0} in {1}' : 'Clicking {0} in {1}',
coordinateLabel,
link
));
}
async prepareToolInvocation(_context: IToolInvocationPreparationContext, _token: CancellationToken): Promise<IPreparedToolInvocation | undefined> {
const params = _context.parameters as IClickCoordinateBrowserToolParams;
const link = createBrowserPageLink(params.pageId);
const coordinateLabel = `(${params.x}, ${params.y})`;
return {
invocationMessage: this.getClickCoordinateMessage(params, coordinateLabel, link, false),
pastTenseMessage: this.getClickCoordinateMessage(params, coordinateLabel, link, true),

Copilot uses AI. Check for mistakes.
Comment on lines +68 to +78
const params = _context.parameters as IClickCoordinateBrowserToolParams;
const link = createBrowserPageLink(params.pageId);
const coordinateLabel = `(${params.x}, ${params.y})`;
return {
invocationMessage: params.button === 'right'
? new MarkdownString(localize('browser.clickCoordinates.invocation.right', 'Right-clicking {0} in {1}', coordinateLabel, link))
: params.button === 'middle'
? new MarkdownString(localize('browser.clickCoordinates.invocation.middle', 'Middle-clicking {0} in {1}', coordinateLabel, link))
: params.dblClick
? new MarkdownString(localize('browser.clickCoordinates.invocation.double', 'Double-clicking {0} in {1}', coordinateLabel, link))
: new MarkdownString(localize('browser.clickCoordinates.invocation', 'Clicking {0} in {1}', coordinateLabel, link)),
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prepareToolInvocation interpolates params.x/params.y into a MarkdownString without any runtime validation/escaping. Even though the schema says these are numbers, tool inputs are untrusted at runtime; a non-numeric value could lead to confusing output or markdown injection. Consider validating Number.isFinite here as well (or escaping the formatted label) and falling back to a safe placeholder when invalid.

Copilot uses AI. Check for mistakes.
@jruales jruales changed the title POC of click_coordinates tool Browser: POC of click_coordinates tool Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants