release: playwriter@0.0.39, extension@0.0.66

- fix icon not updating on WS disconnect
- increase aria-labels auto-hide timeout to 30s
This commit is contained in:
Tommy D. Rossi
2026-01-03 17:38:28 +01:00
parent f9a2312a58
commit 1ad94f3930
8 changed files with 70 additions and 35 deletions
+1
View File
@@ -1,4 +1,5 @@
node_modules
.vercel
dist
esm
.DS_Store
+3 -3
View File
@@ -108,18 +108,18 @@ await page.screenshot({ path: '/tmp/labeled-page.png' })
// Use the snapshot to find elements, then interact using aria-ref selector
await page.locator('aria-ref=e5').click()
// Labels auto-hide after 5 seconds, or remove manually:
// Labels auto-hide after 30 seconds, or remove manually:
await hideAriaRefLabels({ page })
```
**Important:** Labels auto-hide after 5 seconds to prevent stale labels. If the page HTML changes (navigation, dynamic content), call `showAriaRefLabels()` again to get fresh labels matching the current DOM.
**Important:** Labels auto-hide after 30 seconds to prevent stale labels. If the page HTML changes (navigation, dynamic content), call `showAriaRefLabels()` again to get fresh labels matching the current DOM.
**Features:**
- **Role filtering** - Only shows labels for interactive elements (buttons, links, inputs, etc.)
- **Visibility detection** - Skips elements covered by modals or overlays
- **Overlap prevention** - Skips labels that would overlap with already-placed ones
- **Color-coded by type** - Warm color scheme helps distinguish element types
- **Auto-hide** - Labels disappear after 5 seconds to prevent stale overlays
- **Auto-hide** - Labels disappear after 30 seconds to prevent stale overlays
**Color legend:**
+1 -1
View File
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "Playwriter MCP",
"version": "0.0.65",
"version": "0.0.66",
"description": "Automate your Browser using Cursor, Claude, VS Code. More capable and context efficient than Playwright MCP.",
"permissions": ["debugger", "tabGroups", "contextMenus", "tabs"],
"host_permissions": ["<all_urls>"],
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "mcp-extension",
"version": "0.0.65",
"version": "0.0.66",
"description": "Playwright MCP Browser Extension",
"private": true,
"repository": {
+9 -2
View File
@@ -1,5 +1,12 @@
# Changelog
## 0.0.39
### Patch Changes
- **Fix icon not updating on WS disconnect**: `maintainLoop` now ensures tabs transition to 'connecting' state when WebSocket is not connected, fixing edge cases where `handleClose` wasn't called
- **Increased aria-labels auto-hide timeout**: Labels now auto-hide after 30 seconds instead of 5 seconds
## 0.0.38
### Patch Changes
@@ -16,7 +23,7 @@
- Only shows truly interactive roles (button, link, textbox, combobox, checkbox, etc.)
- Skips elements covered by opaque overlays using `elementsFromPoint()`
- Greedy overlap prevention skips labels that would overlap with already-placed ones
- Auto-hides after 5 seconds to prevent stale labels (timer cancelled if called again)
- Auto-hides after 30 seconds to prevent stale labels (timer cancelled if called again)
- Available in MCP execute context
### Usage
@@ -25,7 +32,7 @@
const { snapshot, labelCount } = await showAriaRefLabels({ page });
await page.screenshot({ path: '/tmp/labeled-page.png' });
await page.locator('aria-ref=e5').click();
// Labels auto-hide after 5 seconds, or call hideAriaRefLabels({ page }) manually
// Labels auto-hide after 30 seconds, or call hideAriaRefLabels({ page }) manually
```
## 0.0.35
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "playwriter",
"description": "",
"version": "0.0.38",
"version": "0.0.39",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
+52 -25
View File
@@ -197,7 +197,7 @@ export async function getAriaSnapshot({ page }: { page: Page }): Promise<AriaSna
* Labels are yellow badges positioned above each element showing the aria ref (e.g., "e1", "e2").
* Use with screenshots so agents can see which elements are interactive.
*
* Labels auto-hide after 5 seconds to prevent stale labels remaining on the page.
* Labels auto-hide after 30 seconds to prevent stale labels remaining on the page.
* Call this function again if the page HTML changes to get fresh labels.
*
* By default, only shows labels for truly interactive roles (button, link, textbox, etc.)
@@ -209,7 +209,7 @@ export async function getAriaSnapshot({ page }: { page: Page }): Promise<AriaSna
* await page.screenshot({ path: '/tmp/screenshot.png' })
* // Agent sees [e5] label on "Submit" button
* await page.locator('aria-ref=e5').click()
* // Labels auto-hide after 5 seconds, or call hideAriaRefLabels() manually
* // Labels auto-hide after 30 seconds, or call hideAriaRefLabels() manually
* ```
*/
export async function showAriaRefLabels({ page, interactiveOnly = true }: {
@@ -238,10 +238,9 @@ export async function showAriaRefLabels({ page, interactiveOnly = true }: {
// Single evaluate call: create container, styles, and all labels
// ElementHandles get unwrapped to DOM elements in browser context
// Using 'any' types here since this code runs in browser context
const labelCount = await page.evaluate(
// Using 'any' for browser types since this runs in browser context
({ refs, containerId, containerStyles, labelStyles, roleColors, defaultColors }: {
function ({ refs, containerId, containerStyles, labelStyles, roleColors, defaultColors }: {
refs: Array<{
ref: string
role: string
@@ -252,7 +251,9 @@ export async function showAriaRefLabels({ page, interactiveOnly = true }: {
labelStyles: string
roleColors: Record<string, [string, string, string]>
defaultColors: [string, string, string]
}) => {
}) {
// Polyfill esbuild's __name helper which gets injected by vite-node but doesn't exist in browser
;(globalThis as any).__name ||= (fn: any) => fn
const doc = (globalThis as any).document
const win = globalThis as any
@@ -285,8 +286,10 @@ export async function showAriaRefLabels({ page, interactiveOnly = true }: {
const LABEL_CHAR_WIDTH = 7 // approximate width per character
// Parse alpha from rgb/rgba color string (getComputedStyle always returns these formats)
const getColorAlpha = (color: string): number => {
if (color === 'transparent') return 0
function getColorAlpha(color: string): number {
if (color === 'transparent') {
return 0
}
// Match rgba(r, g, b, a) or rgb(r, g, b)
const match = color.match(/rgba?\(\s*[\d.]+\s*,\s*[\d.]+\s*,\s*[\d.]+\s*(?:,\s*([\d.]+)\s*)?\)/)
if (match) {
@@ -296,25 +299,31 @@ export async function showAriaRefLabels({ page, interactiveOnly = true }: {
}
// Check if an element has an opaque background that would block elements behind it
const isOpaqueElement = (el: any): boolean => {
function isOpaqueElement(el: any): boolean {
const style = win.getComputedStyle(el)
// Check element opacity
const opacity = parseFloat(style.opacity)
if (opacity < 0.1) return false
if (opacity < 0.1) {
return false
}
// Check background-color alpha
const bgAlpha = getColorAlpha(style.backgroundColor)
if (bgAlpha > 0.1) return true
if (bgAlpha > 0.1) {
return true
}
// Check if has background-image (usually opaque)
if (style.backgroundImage !== 'none') return true
if (style.backgroundImage !== 'none') {
return true
}
return false
}
// Check if element is visible (not covered by opaque overlay)
const isElementVisible = (element: any, rect: any): boolean => {
function isElementVisible(element: any, rect: any): boolean {
const centerX = rect.left + rect.width / 2
const centerY = rect.top + rect.height / 2
@@ -322,32 +331,44 @@ export async function showAriaRefLabels({ page, interactiveOnly = true }: {
const stack = doc.elementsFromPoint(centerX, centerY) as any[]
// Find our target element in the stack
const targetIndex = stack.findIndex((el: any) =>
element.contains(el) || el.contains(element)
)
let targetIndex = -1
for (let i = 0; i < stack.length; i++) {
if (element.contains(stack[i]) || stack[i].contains(element)) {
targetIndex = i
break
}
}
// Element not in stack at all - not visible
if (targetIndex === -1) return false
if (targetIndex === -1) {
return false
}
// Check if any opaque element is above our target
for (let i = 0; i < targetIndex; i++) {
const el = stack[i]
// Skip our own overlay container
if (el.id === containerId) continue
if (el.id === containerId) {
continue
}
// Skip pointer-events: none elements (decorative overlays)
if (win.getComputedStyle(el).pointerEvents === 'none') continue
if (win.getComputedStyle(el).pointerEvents === 'none') {
continue
}
// If this element is opaque, our target is blocked
if (isOpaqueElement(el)) return false
if (isOpaqueElement(el)) {
return false
}
}
return true
}
// Check if two rectangles overlap
const rectsOverlap = (
function rectsOverlap(
a: { left: number; top: number; right: number; bottom: number },
b: { left: number; top: number; right: number; bottom: number }
) => {
) {
return a.left < b.right && a.right > b.left && a.top < b.bottom && a.bottom > b.top
}
@@ -378,7 +399,13 @@ export async function showAriaRefLabels({ page, interactiveOnly = true }: {
}
// Skip if this label would overlap with any already-placed label
const overlaps = placedLabels.some((placed) => rectsOverlap(labelRect, placed))
let overlaps = false
for (const placed of placedLabels) {
if (rectsOverlap(labelRect, placed)) {
overlaps = true
break
}
}
if (overlaps) {
continue
}
@@ -404,12 +431,12 @@ export async function showAriaRefLabels({ page, interactiveOnly = true }: {
doc.documentElement.appendChild(container)
// Auto-hide labels after 5 seconds to prevent stale labels
// Auto-hide labels after 30 seconds to prevent stale labels
// Store timer ID so it can be cancelled if showAriaRefLabels is called again
win[timerKey] = win.setTimeout(() => {
win[timerKey] = win.setTimeout(function() {
doc.getElementById(containerId)?.remove()
win[timerKey] = null
}, 5000)
}, 30000)
return count
},
+2 -2
View File
@@ -206,7 +206,7 @@ const matches = await editor.grep({ regex: /console\.log/ });
await editor.edit({ url: matches[0].url, oldString: 'DEBUG = false', newString: 'DEBUG = true' });
```
**showAriaRefLabels** - overlay Vimium-style visual labels on interactive elements. Useful for taking screenshots where you can see element references. Labels auto-hide after 5 seconds. Call again if page HTML changes to get fresh labels.
**showAriaRefLabels** - overlay Vimium-style visual labels on interactive elements. Useful for taking screenshots where you can see element references. Labels auto-hide after 30 seconds. Call again if page HTML changes or scrolls to get fresh labels. Use a timeout of 10 seconds at least.
```js
const { snapshot, labelCount } = await showAriaRefLabels({ page });
@@ -218,7 +218,7 @@ await page.locator('aria-ref=e5').click();
Labels are color-coded: yellow=links, orange=buttons, coral=inputs, pink=checkboxes, peach=sliders, salmon=menus, amber=tabs.
**hideAriaRefLabels** - manually remove labels before the 5-second auto-hide:
**hideAriaRefLabels** - manually remove labels before the 30-second auto-hide:
```js
await hideAriaRefLabels({ page });