From 04b757a7c87aa8687fafdd7637ce62e46ca2374b Mon Sep 17 00:00:00 2001 From: "Tommy D. Rossi" Date: Fri, 19 Dec 2025 18:34:39 +0100 Subject: [PATCH] add support for tab groups. green --- extension/manifest.json | 2 +- extension/src/background.ts | 46 +++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/extension/manifest.json b/extension/manifest.json index a568662..553853b 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -3,7 +3,7 @@ "name": "Playwriter MCP", "version": "0.0.52", "description": "Automate your Browser using Cursor, Claude, VS Code. More capable and context efficient than Playwright MCP.", - "permissions": ["debugger"], + "permissions": ["debugger", "tabGroups"], "host_permissions": [""], "background": { "service_worker": "lib/background.mjs", diff --git a/extension/src/background.ts b/extension/src/background.ts index f34f8be..711cd47 100644 --- a/extension/src/background.ts +++ b/extension/src/background.ts @@ -8,6 +8,7 @@ const RELAY_URL = 'ws://localhost:19988/extension' let ws: WebSocket | null = null let childSessions: Map = new Map() let nextSessionId = 1 +let playwriterGroupId: number | null = null const store = createStore(() => ({ tabs: new Map(), @@ -94,6 +95,46 @@ function sendMessage(message: any): void { } } +async function addTabToGroup(tabId: number): Promise { + try { + if (playwriterGroupId !== null) { + const existingGroups = await chrome.tabGroups.query({ title: 'playwriter' }) + if (!existingGroups.some((g) => g.id === playwriterGroupId)) { + playwriterGroupId = null + } + } + + if (playwriterGroupId === null) { + playwriterGroupId = await chrome.tabs.group({ tabIds: [tabId] }) + await chrome.tabGroups.update(playwriterGroupId, { title: 'playwriter', color: 'green' }) + logger.debug('Created tab group:', playwriterGroupId) + } else { + await chrome.tabs.group({ tabIds: [tabId], groupId: playwriterGroupId }) + logger.debug('Added tab to existing group:', tabId) + } + } catch (error: any) { + logger.debug('Failed to add tab to group:', error.message) + } +} + +async function removeTabFromGroup(tabId: number): Promise { + try { + await chrome.tabs.ungroup(tabId) + logger.debug('Removed tab from group:', tabId) + + if (playwriterGroupId !== null) { + const groups = await chrome.tabGroups.query({ title: 'playwriter' }) + const group = groups.find((g) => g.id === playwriterGroupId) + if (!group) { + playwriterGroupId = null + logger.debug('Tab group was deleted') + } + } + } catch (error: any) { + logger.debug('Failed to remove tab from group:', error.message) + } +} + function getTabBySessionId(sessionId: string): { tabId: number; tab: TabInfo } | undefined { for (const [tabId, tab] of store.getState().tabs) { if (tab.sessionId === sessionId) { @@ -299,6 +340,7 @@ async function attachTab(tabId: number): Promise { }) logger.debug('Tab attached successfully:', tabId, 'sessionId:', sessionId, 'targetId:', targetInfo.targetId) + await addTabToGroup(tabId) return targetInfo } @@ -311,6 +353,8 @@ function detachTab(tabId: number, shouldDetachDebugger: boolean): void { logger.debug('Detaching tab:', tabId, 'sessionId:', tab.sessionId, 'shouldDetach:', shouldDetachDebugger) + void removeTabFromGroup(tabId) + sendMessage({ method: 'forwardCDPEvent', params: { @@ -353,6 +397,7 @@ function closeConnection(reason: string): void { store.setState({ tabs: new Map(), connectionState: 'disconnected', errorText: undefined }) childSessions.clear() + playwriterGroupId = null if (ws) { ws.close(1000, reason) @@ -375,6 +420,7 @@ function handleConnectionClose(reason: string, code: number): void { } childSessions.clear() + playwriterGroupId = null ws = null if (reason === 'Extension Replaced' || code === 4001) {