control-ui
cursor/plugins
Build local browser automation harnesses to verify UI behavior, capture screenshots, and reproduce bugs.
What is control-ui?
Control UI lets you automate browser or Electron interfaces to test UI behavior, capture visual evidence, and debug rendering issues. Use it when you need to verify UI changes, accessibility, performance, or reproduce bugs that depend on real browser interactions like focus, keyboard input, or scrolling.
- Automate web, IDE, or Electron UIs using Playwright or Chrome DevTools Protocol (CDP)
- Capture screenshots, accessibility snapshots, and visual diffs before and after UI changes
- Reproduce UI bugs that depend on real browser focus, keyboard input, scrolling, or rendering
- Collect performance profiles, console logs, network logs, heap snapshots, and CPU traces
- Verify UI state changes through structured interaction loops with fresh snapshots between actions
How to install control-ui
npx skills add https://github.com/cursor/plugins --skill control-ui- Local development environment with the app's dev server running or Electron launch capability
- Existing browser tooling (Playwright, Cypress, or similar) preferred; Chromium available as fallback
- For Electron/Chromium apps: remote debugging port enabled (e.g., --remote-debugging-port=<port>)
How to use control-ui
- 1.Start the app locally using the repo's documented dev command
- 2.Discover existing harnesses in the repo (Playwright tests, Cypress specs, Storybook, browser scripts, or Electron launch scripts)
- 3.Connect to the local URL or debug port using the repo's existing browser tooling or a minimal Playwright/CDP probe
- 4.Select the correct page or window using stable app markers (data-* attributes, accessibility roles, or root selectors)
- 5.Capture a screenshot or snapshot, perform one interaction (click, type, scroll, etc.), then capture again to verify the change
- 6.Save before/after artifacts as evidence when needed, then clean up dev servers and debug sessions
Use cases
- Verify visual or accessibility changes with before/after screenshots before shipping
- Reproduce focus or keyboard-dependent UI bugs in a local browser harness
- Capture performance profiles and heap snapshots to investigate rendering or memory issues
- Test Electron app behavior by connecting over Chrome DevTools Protocol to a debug port
- Create evidence artifacts for visual regression testing or bug reports
- Frontend engineers verifying UI changes and visual regressions
- QA engineers reproducing browser-dependent bugs
- Performance engineers investigating rendering and memory issues
- Full-stack developers testing Electron or web app UIs locally
control-ui FAQ
No. Prefer reusing the repo's existing browser tooling (Playwright, Cypress, etc.) or external tools already available. Only add Playwright if the user explicitly requests it.
Launch the app with --remote-debugging-port=<port>, then use Playwright's connectOverCDP() to attach to http://127.0.0.1:<port>. Identify the correct page by searching for a stable app root selector.
Prefer accessibility roles, labels, and stable data-* attributes over coordinates or tab order. Always capture a fresh screenshot immediately before coordinate clicks.
Use a positive marker (app root selector) to identify the correct page. If no page matches, list available page titles and URLs instead of guessing.
Save artifacts for before/after comparisons when the user asks for proof. Do not store screenshots from privacy-sensitive workspaces unless explicitly approved. Clean up temp profiles and debug sessions when done.
Full instructions (SKILL.md)
Source of truth, from cursor/plugins.
name: control-ui description: 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.
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.
Related skills
More from cursor/plugins and the wider catalog.

create-verification-skill
Generate a project-local verification skill that automates testing your app the way a user does.

deslop
Remove AI-generated code slop and clean up code style to match your codebase standards.

figure-it-out
Design auditable playbooks for complex tasks when no narrower skill fits.

fix-ci
Diagnose and fix failing PR checks with iterative, focused solutions.

fix-merge-conflicts
Resolve merge conflicts non-interactively with validation and testing.

get-pr-comments
Fetch and summarize review comments from your active pull request