diff --git a/extension/src/relayConnection.ts b/extension/src/relayConnection.ts index 968cdb5..da59084 100644 --- a/extension/src/relayConnection.ts +++ b/extension/src/relayConnection.ts @@ -275,6 +275,26 @@ export class RelayConnection { return {}; } + if (message.method === 'activateTab') { + const { targetId } = message.params; + debugLog('Activating tab with targetId:', targetId); + + for (const [tabId, tab] of this._attachedTabs) { + if (tab.targetId === targetId) { + debugLog('Found tab to activate:', tabId); + await chrome.tabs.update(tabId, { active: true }); + // const chromeTab = await chrome.tabs.get(tabId); + // if (chromeTab.windowId) { + // await chrome.windows.update(chromeTab.windowId, { focused: true }); + // } + return { success: true }; + } + } + + debugLog('Target not found:', targetId); + throw new Error(`Target not found: ${targetId}`); + } + if (message.method === 'forwardCDPCommand') { const { sessionId, method, params } = message.params; diff --git a/playwriter/src/extension/cdp-relay.ts b/playwriter/src/extension/cdp-relay.ts index bee43cb..4e596ef 100644 --- a/playwriter/src/extension/cdp-relay.ts +++ b/playwriter/src/extension/cdp-relay.ts @@ -219,6 +219,13 @@ export async function startPlayWriterCDPRelayServer({ port = 19988, logger = con params: { method, params } }) } + + case 'Playwriter.activateTab': { + return await sendToExtension({ + method: 'activateTab', + params + }) + } } return await sendToExtension({ diff --git a/playwriter/src/extension/protocol.ts b/playwriter/src/extension/protocol.ts index 858136a..69ba624 100644 --- a/playwriter/src/extension/protocol.ts +++ b/playwriter/src/extension/protocol.ts @@ -15,6 +15,13 @@ export type ExtensionCommandMessage = params?: any } } + | { + id: number + method: 'activateTab' + params: { + targetId: string + } + } export type ExtensionResponseMessage = { id: number diff --git a/playwriter/src/mcp.ts b/playwriter/src/mcp.ts index 8102137..12594e4 100644 --- a/playwriter/src/mcp.ts +++ b/playwriter/src/mcp.ts @@ -164,12 +164,21 @@ server.tool( throw new Error('accessibilitySnapshot is not available on this page') } + const activateTab = async (targetPage: Page) => { + const cdp = await context.newCDPSession(targetPage) + const { targetInfo } = await cdp.send('Target.getTargetInfo') + const targetId = targetInfo.targetId + await cdp.send('Playwriter.activateTab' as any, { targetId }) + await cdp.detach() + } + const vmContext = vm.createContext({ page, context, state, console: customConsole, accessibilitySnapshot, + activateTab, }) const wrappedCode = `(async () => { ${code} })()` diff --git a/playwriter/src/prompt.md b/playwriter/src/prompt.md index b19246b..6f3b8dc 100644 --- a/playwriter/src/prompt.md +++ b/playwriter/src/prompt.md @@ -28,6 +28,7 @@ always detach event listener you create at the end of a message using `page.remo you have access to some functions in addition to playwright methods: - `async accessibilitySnapshot(page)`: gets a human readable snapshot of clickable elements on the page. useful to see the overall structure of the page and what elements you can interact with +- `async activateTab(page)`: activates (brings to front and focuses) the browser tab for the given page example: