Control UI
Use local browser automation to verify UI behavior with evidence. First reuse the repo's own Playwright, browser, or Electron harness if it exists; otherwise assemble a temporary local harness around the app's dev server or Chromium debug port.
What It Is Used For
- Reproducing UI bugs that depend on real browser focus, keyboard input, scrolling, resizing, or rendering.
- Verifying visual or accessibility changes with screenshots and snapshots.
- Checking local web, IDE, or Electron behavior before shipping.
- Capturing console logs, network logs, CPU profiles, traces, or heap snapshots.
- Creating before/after evidence for
verify-this.
Setup Pattern
- Start the app locally using the repo's documented dev command.
- Discover existing local harnesses: Playwright tests, Cypress specs, Storybook, browser scripts, Electron launch scripts, or snapshot tools.
- For a web app, connect to the local URL with the existing browser tooling.
- For Electron/Chromium, enable a remote debugging port when supported.
- Select the correct page by stable app markers, not by tab order alone.
- Prefer accessibility roles, labels, and stable
data-* selectors over coordinates.
Generic Web Harness
Use the repo's installed browser tooling when possible. If the repo already has Playwright, a minimal one-off probe looks like:
import { chromium } from "playwright";
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
await page.goto("http://127.0.0.1:<port>");
await page.getByRole("button", { name: /submit/i }).click();
await page.screenshot({ path: "/tmp/ui-harness-after.png", fullPage: true });
await browser.close();
Do not add Playwright as a project dependency just for this probe unless the user asks. Prefer existing dev dependencies or external browser tools already available in the environment.
Generic CDP Harness
For Electron or a Chromium app launched with --remote-debugging-port=<port>, connect over CDP:
import { chromium } from "playwright";
const browser = await chromium.connectOverCDP("http://127.0.0.1:<debug-port>");
const pages = browser.contexts().flatMap((context) => context.pages());
let page;
for (const candidate of pages) {
if (await candidate.locator("<app-root-selector>").count()) {
page = candidate;
break;
}
}
if (!page) {
console.log(await Promise.all(pages.map(async (p) => ({
title: await p.title(),
url: p.url(),
}))));
throw new Error("No matching app page found");
}
await page.screenshot({ path: "/tmp/ui-harness-cdp.png", fullPage: true });
await browser.close();
Replace <app-root-selector> with a stable marker from the current repo, such as a root app node, landmark, or product-specific data-* attribute.
Interaction Loop
- Capture a page snapshot or screenshot before acting.
- Choose a target from the latest page structure.
- Perform exactly one structural action: click, type, keypress, drag, scroll, navigate, or resize.
- Capture a fresh snapshot/screenshot.
- Verify the expected state change.
- Save artifacts for before/after comparisons when the user asked for proof.
CDP Capabilities
Use raw CDP only when higher-level browser APIs are insufficient:
- Performance: CPU profiles, traces, paint flashing, FPS meter, layout shift inspection.
- Memory: heap snapshots and forced GC for leak investigations.
- Network: request blocking, throttling, cache disablement, request/response logs.
- Rendering: viewport changes, color scheme emulation, reduced motion, accessibility checks.
- Debugging: console streaming, exception capture, DOM snapshots.
Page Selection
When multiple app windows/tabs share a debug port:
- Prefer a positive marker for the surface under test, such as an app root selector.
- Use a negative marker to avoid the wrong surface when necessary.
- If no page matches, list available page titles and URLs instead of guessing.
Guardrails
- Do not rely on stale element references after navigation or structural changes.
- Avoid coordinate clicks unless a fresh screenshot was captured immediately before the click.
- Keep test data local and disposable.
- Do not store screenshots or heap snapshots from privacy-sensitive workspaces unless the user explicitly agrees.
- Do not hard-code selectors, ports, or script paths from another repository. Discover the current repo's local app markers.
- Clean up dev servers, debug sessions, and temp profiles when done.
1---2name: control-ui3description: Build or adapt a local browser/CDP harness to drive and inspect a web, IDE, or Electron UI. Use for local UI verification, screenshots, accessibility snapshots, perf profiles, visual diffs, or reproducing UI bugs.4---5
6# Control UI
7
8Use local browser automation to verify UI behavior with evidence. First reuse the repo's own Playwright, browser, or Electron harness if it exists; otherwise assemble a temporary local harness around the app's dev server or Chromium debug port.
9
10## What It Is Used For
11
12- Reproducing UI bugs that depend on real browser focus, keyboard input, scrolling, resizing, or rendering.
13- Verifying visual or accessibility changes with screenshots and snapshots.
14- Checking local web, IDE, or Electron behavior before shipping.
15- Capturing console logs, network logs, CPU profiles, traces, or heap snapshots.
16- Creating before/after evidence for `verify-this`.
17
18## Setup Pattern
19
201. Start the app locally using the repo's documented dev command.
212. Discover existing local harnesses: Playwright tests, Cypress specs, Storybook, browser scripts, Electron launch scripts, or snapshot tools.
223. For a web app, connect to the local URL with the existing browser tooling.
234. For Electron/Chromium, enable a remote debugging port when supported.
245. Select the correct page by stable app markers, not by tab order alone.
256. Prefer accessibility roles, labels, and stable `data-*` selectors over coordinates.
26
27## Generic Web Harness
28
29Use the repo's installed browser tooling when possible. If the repo already has Playwright, a minimal one-off probe looks like:
30
31```javascript
32import { chromium } from "playwright";
33
34const browser = await chromium.launch();
35const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
36await page.goto("http://127.0.0.1:<port>");
37await page.getByRole("button", { name: /submit/i }).click();
38await page.screenshot({ path: "/tmp/ui-harness-after.png", fullPage: true });
39await browser.close();
40```
41
42Do not add Playwright as a project dependency just for this probe unless the user asks. Prefer existing dev dependencies or external browser tools already available in the environment.
43
44## Generic CDP Harness
45
46For Electron or a Chromium app launched with `--remote-debugging-port=<port>`, connect over CDP:
47
48```javascript
49import { chromium } from "playwright";
50
51const browser = await chromium.connectOverCDP("http://127.0.0.1:<debug-port>");
52const pages = browser.contexts().flatMap((context) => context.pages());
53let page;
54for (const candidate of pages) {
55 if (await candidate.locator("<app-root-selector>").count()) {
56 page = candidate;
57 break;
58 }
59}
60
61if (!page) {
62 console.log(await Promise.all(pages.map(async (p) => ({
63 title: await p.title(),
64 url: p.url(),
65 }))));
66 throw new Error("No matching app page found");
67}
68
69await page.screenshot({ path: "/tmp/ui-harness-cdp.png", fullPage: true });
70await browser.close();
71```
72
73Replace `<app-root-selector>` with a stable marker from the current repo, such as a root app node, landmark, or product-specific `data-*` attribute.
74
75## Interaction Loop
76
771. Capture a page snapshot or screenshot before acting.
782. Choose a target from the latest page structure.
793. Perform exactly one structural action: click, type, keypress, drag, scroll, navigate, or resize.
804. Capture a fresh snapshot/screenshot.
815. Verify the expected state change.
826. Save artifacts for before/after comparisons when the user asked for proof.
83
84## CDP Capabilities
85
86Use raw CDP only when higher-level browser APIs are insufficient:
87
88- Performance: CPU profiles, traces, paint flashing, FPS meter, layout shift inspection.
89- Memory: heap snapshots and forced GC for leak investigations.
90- Network: request blocking, throttling, cache disablement, request/response logs.
91- Rendering: viewport changes, color scheme emulation, reduced motion, accessibility checks.
92- Debugging: console streaming, exception capture, DOM snapshots.
93
94## Page Selection
95
96When multiple app windows/tabs share a debug port:
97
98- Prefer a positive marker for the surface under test, such as an app root selector.
99- Use a negative marker to avoid the wrong surface when necessary.
100- If no page matches, list available page titles and URLs instead of guessing.
101
102## Guardrails
103
104- Do not rely on stale element references after navigation or structural changes.
105- Avoid coordinate clicks unless a fresh screenshot was captured immediately before the click.
106- Keep test data local and disposable.
107- Do not store screenshots or heap snapshots from privacy-sensitive workspaces unless the user explicitly agrees.
108- Do not hard-code selectors, ports, or script paths from another repository. Discover the current repo's local app markers.
109- Clean up dev servers, debug sessions, and temp profiles when done.