Skip to content

Commit 1ad94f3

Browse files
committed
release: playwriter@0.0.39, extension@0.0.66
- fix icon not updating on WS disconnect - increase aria-labels auto-hide timeout to 30s
1 parent f9a2312 commit 1ad94f3

File tree

8 files changed

+70
-35
lines changed

8 files changed

+70
-35
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
node_modules
2+
.vercel
23
dist
34
esm
45
.DS_Store

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,18 +108,18 @@ await page.screenshot({ path: '/tmp/labeled-page.png' })
108108
// Use the snapshot to find elements, then interact using aria-ref selector
109109
await page.locator('aria-ref=e5').click()
110110

111-
// Labels auto-hide after 5 seconds, or remove manually:
111+
// Labels auto-hide after 30 seconds, or remove manually:
112112
await hideAriaRefLabels({ page })
113113
```
114114

115-
**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.
115+
**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.
116116

117117
**Features:**
118118
- **Role filtering** - Only shows labels for interactive elements (buttons, links, inputs, etc.)
119119
- **Visibility detection** - Skips elements covered by modals or overlays
120120
- **Overlap prevention** - Skips labels that would overlap with already-placed ones
121121
- **Color-coded by type** - Warm color scheme helps distinguish element types
122-
- **Auto-hide** - Labels disappear after 5 seconds to prevent stale overlays
122+
- **Auto-hide** - Labels disappear after 30 seconds to prevent stale overlays
123123

124124
**Color legend:**
125125

extension/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"manifest_version": 3,
33
"name": "Playwriter MCP",
4-
"version": "0.0.65",
4+
"version": "0.0.66",
55
"description": "Automate your Browser using Cursor, Claude, VS Code. More capable and context efficient than Playwright MCP.",
66
"permissions": ["debugger", "tabGroups", "contextMenus", "tabs"],
77
"host_permissions": ["<all_urls>"],

extension/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "mcp-extension",
3-
"version": "0.0.65",
3+
"version": "0.0.66",
44
"description": "Playwright MCP Browser Extension",
55
"private": true,
66
"repository": {

playwriter/CHANGELOG.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
22

3+
## 0.0.39
4+
5+
### Patch Changes
6+
7+
- **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
8+
- **Increased aria-labels auto-hide timeout**: Labels now auto-hide after 30 seconds instead of 5 seconds
9+
310
## 0.0.38
411

512
### Patch Changes
@@ -16,7 +23,7 @@
1623
- Only shows truly interactive roles (button, link, textbox, combobox, checkbox, etc.)
1724
- Skips elements covered by opaque overlays using `elementsFromPoint()`
1825
- Greedy overlap prevention skips labels that would overlap with already-placed ones
19-
- Auto-hides after 5 seconds to prevent stale labels (timer cancelled if called again)
26+
- Auto-hides after 30 seconds to prevent stale labels (timer cancelled if called again)
2027
- Available in MCP execute context
2128

2229
### Usage
@@ -25,7 +32,7 @@
2532
const { snapshot, labelCount } = await showAriaRefLabels({ page });
2633
await page.screenshot({ path: '/tmp/labeled-page.png' });
2734
await page.locator('aria-ref=e5').click();
28-
// Labels auto-hide after 5 seconds, or call hideAriaRefLabels({ page }) manually
35+
// Labels auto-hide after 30 seconds, or call hideAriaRefLabels({ page }) manually
2936
```
3037

3138
## 0.0.35

playwriter/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "playwriter",
33
"description": "",
4-
"version": "0.0.38",
4+
"version": "0.0.39",
55
"type": "module",
66
"main": "dist/index.js",
77
"types": "dist/index.d.ts",

playwriter/src/aria-snapshot.ts

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ export async function getAriaSnapshot({ page }: { page: Page }): Promise<AriaSna
197197
* Labels are yellow badges positioned above each element showing the aria ref (e.g., "e1", "e2").
198198
* Use with screenshots so agents can see which elements are interactive.
199199
*
200-
* Labels auto-hide after 5 seconds to prevent stale labels remaining on the page.
200+
* Labels auto-hide after 30 seconds to prevent stale labels remaining on the page.
201201
* Call this function again if the page HTML changes to get fresh labels.
202202
*
203203
* 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
209209
* await page.screenshot({ path: '/tmp/screenshot.png' })
210210
* // Agent sees [e5] label on "Submit" button
211211
* await page.locator('aria-ref=e5').click()
212-
* // Labels auto-hide after 5 seconds, or call hideAriaRefLabels() manually
212+
* // Labels auto-hide after 30 seconds, or call hideAriaRefLabels() manually
213213
* ```
214214
*/
215215
export async function showAriaRefLabels({ page, interactiveOnly = true }: {
@@ -238,10 +238,9 @@ export async function showAriaRefLabels({ page, interactiveOnly = true }: {
238238

239239
// Single evaluate call: create container, styles, and all labels
240240
// ElementHandles get unwrapped to DOM elements in browser context
241-
// Using 'any' types here since this code runs in browser context
242241
const labelCount = await page.evaluate(
243242
// Using 'any' for browser types since this runs in browser context
244-
({ refs, containerId, containerStyles, labelStyles, roleColors, defaultColors }: {
243+
function ({ refs, containerId, containerStyles, labelStyles, roleColors, defaultColors }: {
245244
refs: Array<{
246245
ref: string
247246
role: string
@@ -252,7 +251,9 @@ export async function showAriaRefLabels({ page, interactiveOnly = true }: {
252251
labelStyles: string
253252
roleColors: Record<string, [string, string, string]>
254253
defaultColors: [string, string, string]
255-
}) => {
254+
}) {
255+
// Polyfill esbuild's __name helper which gets injected by vite-node but doesn't exist in browser
256+
;(globalThis as any).__name ||= (fn: any) => fn
256257
const doc = (globalThis as any).document
257258
const win = globalThis as any
258259

@@ -285,8 +286,10 @@ export async function showAriaRefLabels({ page, interactiveOnly = true }: {
285286
const LABEL_CHAR_WIDTH = 7 // approximate width per character
286287

287288
// Parse alpha from rgb/rgba color string (getComputedStyle always returns these formats)
288-
const getColorAlpha = (color: string): number => {
289-
if (color === 'transparent') return 0
289+
function getColorAlpha(color: string): number {
290+
if (color === 'transparent') {
291+
return 0
292+
}
290293
// Match rgba(r, g, b, a) or rgb(r, g, b)
291294
const match = color.match(/rgba?\(\s*[\d.]+\s*,\s*[\d.]+\s*,\s*[\d.]+\s*(?:,\s*([\d.]+)\s*)?\)/)
292295
if (match) {
@@ -296,58 +299,76 @@ export async function showAriaRefLabels({ page, interactiveOnly = true }: {
296299
}
297300

298301
// Check if an element has an opaque background that would block elements behind it
299-
const isOpaqueElement = (el: any): boolean => {
302+
function isOpaqueElement(el: any): boolean {
300303
const style = win.getComputedStyle(el)
301304

302305
// Check element opacity
303306
const opacity = parseFloat(style.opacity)
304-
if (opacity < 0.1) return false
307+
if (opacity < 0.1) {
308+
return false
309+
}
305310

306311
// Check background-color alpha
307312
const bgAlpha = getColorAlpha(style.backgroundColor)
308-
if (bgAlpha > 0.1) return true
313+
if (bgAlpha > 0.1) {
314+
return true
315+
}
309316

310317
// Check if has background-image (usually opaque)
311-
if (style.backgroundImage !== 'none') return true
318+
if (style.backgroundImage !== 'none') {
319+
return true
320+
}
312321

313322
return false
314323
}
315324

316325
// Check if element is visible (not covered by opaque overlay)
317-
const isElementVisible = (element: any, rect: any): boolean => {
326+
function isElementVisible(element: any, rect: any): boolean {
318327
const centerX = rect.left + rect.width / 2
319328
const centerY = rect.top + rect.height / 2
320329

321330
// Get all elements at this point, from top to bottom
322331
const stack = doc.elementsFromPoint(centerX, centerY) as any[]
323332

324333
// Find our target element in the stack
325-
const targetIndex = stack.findIndex((el: any) =>
326-
element.contains(el) || el.contains(element)
327-
)
334+
let targetIndex = -1
335+
for (let i = 0; i < stack.length; i++) {
336+
if (element.contains(stack[i]) || stack[i].contains(element)) {
337+
targetIndex = i
338+
break
339+
}
340+
}
328341

329342
// Element not in stack at all - not visible
330-
if (targetIndex === -1) return false
343+
if (targetIndex === -1) {
344+
return false
345+
}
331346

332347
// Check if any opaque element is above our target
333348
for (let i = 0; i < targetIndex; i++) {
334349
const el = stack[i]
335350
// Skip our own overlay container
336-
if (el.id === containerId) continue
351+
if (el.id === containerId) {
352+
continue
353+
}
337354
// Skip pointer-events: none elements (decorative overlays)
338-
if (win.getComputedStyle(el).pointerEvents === 'none') continue
355+
if (win.getComputedStyle(el).pointerEvents === 'none') {
356+
continue
357+
}
339358
// If this element is opaque, our target is blocked
340-
if (isOpaqueElement(el)) return false
359+
if (isOpaqueElement(el)) {
360+
return false
361+
}
341362
}
342363

343364
return true
344365
}
345366

346367
// Check if two rectangles overlap
347-
const rectsOverlap = (
368+
function rectsOverlap(
348369
a: { left: number; top: number; right: number; bottom: number },
349370
b: { left: number; top: number; right: number; bottom: number }
350-
) => {
371+
) {
351372
return a.left < b.right && a.right > b.left && a.top < b.bottom && a.bottom > b.top
352373
}
353374

@@ -378,7 +399,13 @@ export async function showAriaRefLabels({ page, interactiveOnly = true }: {
378399
}
379400

380401
// Skip if this label would overlap with any already-placed label
381-
const overlaps = placedLabels.some((placed) => rectsOverlap(labelRect, placed))
402+
let overlaps = false
403+
for (const placed of placedLabels) {
404+
if (rectsOverlap(labelRect, placed)) {
405+
overlaps = true
406+
break
407+
}
408+
}
382409
if (overlaps) {
383410
continue
384411
}
@@ -404,12 +431,12 @@ export async function showAriaRefLabels({ page, interactiveOnly = true }: {
404431

405432
doc.documentElement.appendChild(container)
406433

407-
// Auto-hide labels after 5 seconds to prevent stale labels
434+
// Auto-hide labels after 30 seconds to prevent stale labels
408435
// Store timer ID so it can be cancelled if showAriaRefLabels is called again
409-
win[timerKey] = win.setTimeout(() => {
436+
win[timerKey] = win.setTimeout(function() {
410437
doc.getElementById(containerId)?.remove()
411438
win[timerKey] = null
412-
}, 5000)
439+
}, 30000)
413440

414441
return count
415442
},

playwriter/src/prompt.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ const matches = await editor.grep({ regex: /console\.log/ });
206206
await editor.edit({ url: matches[0].url, oldString: 'DEBUG = false', newString: 'DEBUG = true' });
207207
```
208208

209-
**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.
209+
**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.
210210

211211
```js
212212
const { snapshot, labelCount } = await showAriaRefLabels({ page });
@@ -218,7 +218,7 @@ await page.locator('aria-ref=e5').click();
218218

219219
Labels are color-coded: yellow=links, orange=buttons, coral=inputs, pink=checkboxes, peach=sliders, salmon=menus, amber=tabs.
220220

221-
**hideAriaRefLabels** - manually remove labels before the 5-second auto-hide:
221+
**hideAriaRefLabels** - manually remove labels before the 30-second auto-hide:
222222

223223
```js
224224
await hideAriaRefLabels({ page });

0 commit comments

Comments
 (0)