|
| 1 | +# Chromium Control Canvas |
| 2 | + |
| 3 | +A GitHub Copilot canvas that drives a real **headful Chromium** window via Playwright. |
| 4 | +The host app's built-in `browser` canvas is WebKit (WKWebView); this gives you actual |
| 5 | +Chromium, controllable both from the panel UI and by the agent. |
| 6 | + |
| 7 | +The canvas panel is a control strip (URL bar, back/forward/reload, screenshot). A separate |
| 8 | +Chromium window does the real rendering, because you can't embed Chromium inside a WebKit |
| 9 | +iframe. |
| 10 | + |
| 11 | +## Files |
| 12 | + |
| 13 | +- `extension.mjs` — the extension: canvas declaration, Playwright launch, a loopback HTTP |
| 14 | + server for the panel, and the agent actions. |
| 15 | +- `index.html` — the control strip UI the panel renders. |
| 16 | +- `package.json` — declares the `playwright` dependency and `"type": "module"`. |
| 17 | +- `copilot-extension.json` — name/version metadata. |
| 18 | + |
| 19 | +## Prerequisites |
| 20 | + |
| 21 | +- **Node.js 20.19 or newer** (the Copilot SDK requires `node ^20.19.0 || >=22.12.0`). |
| 22 | + The extension runs as a Node child process. |
| 23 | +- The app's **canvas / UI-extensions experiment enabled**. Without it, the extension |
| 24 | + loads but the canvas never appears in the panel. Enable it in the app's |
| 25 | + Settings → Experiments. (This may not be available to all accounts.) |
| 26 | + |
| 27 | +## Install |
| 28 | + |
| 29 | +Drop this folder at `~/.copilot/extensions/chromium-control-canvas/` (user scope) or in a repo's |
| 30 | +`.github/extensions/chromium-control-canvas/` (project scope), then install dependencies and the |
| 31 | +Chromium binary from inside the folder you copied: |
| 32 | + |
| 33 | +```sh |
| 34 | +# User scope |
| 35 | +cd ~/.copilot/extensions/chromium-control-canvas |
| 36 | + |
| 37 | +# Or project scope, from the repository root |
| 38 | +cd .github/extensions/chromium-control-canvas |
| 39 | + |
| 40 | +npm install # playwright is declared in package.json |
| 41 | +npx playwright install chromium # downloads the browser, a few hundred MB |
| 42 | +``` |
| 43 | + |
| 44 | +Reload extensions in the app, then open the `chromium-control-canvas` canvas. |
| 45 | + |
| 46 | +Note: copying the extension files only places the source. It does **not** run the |
| 47 | +commands above or enable the experiment, so those steps are still required on first |
| 48 | +setup. |
| 49 | + |
| 50 | +## Attach to your own Chrome |
| 51 | + |
| 52 | +By default the canvas launches the bundled Chromium with a persistent profile. To drive |
| 53 | +a Chrome you already have running instead, start it with a debug port and pass `cdpUrl` |
| 54 | +when opening the canvas: |
| 55 | + |
| 56 | +```sh |
| 57 | +google-chrome --remote-debugging-port=9222 # then open the canvas with cdpUrl: http://localhost:9222 |
| 58 | +``` |
| 59 | + |
| 60 | +In this mode the extension connects over CDP and never launches or kills your browser; |
| 61 | +closing the canvas just disconnects. |
| 62 | + |
| 63 | +## Agent actions |
| 64 | + |
| 65 | +- `navigate { url }` — go to a URL or search query (blocklist-guarded). |
| 66 | +- `back` / `forward` / `reload` — history navigation. |
| 67 | +- `current_url` — current URL and page title. |
| 68 | +- `snapshot` — structured list of visible interactive elements, each with a stable ref. |
| 69 | +- `click { ref | selector }` — click an element by snapshot ref or CSS selector. |
| 70 | +- `type { ref | selector, text, submit? }` — fill an input; optionally press Enter. |
| 71 | +- `screenshot { fullPage? }` — save a PNG to `artifacts/` and return its path and size. |
| 72 | + |
| 73 | +## Notes |
| 74 | + |
| 75 | +- A persistent profile is stored under |
| 76 | + `$COPILOT_HOME/extensions/chromium-control-canvas/profile` (default |
| 77 | + `~/.copilot/extensions/chromium-control-canvas/profile`) so logins survive restarts. |
| 78 | + **Do not commit or share this folder** — it contains real session cookies. |
| 79 | +- Raw `evaluate` (arbitrary in-page JS) is intentionally omitted. |
| 80 | +- `navigate` is checked against a blocklist, and a request interceptor also blocks |
| 81 | + navigations to blocked hosts that happen via in-page redirects. The shipped |
| 82 | + `BLOCKLIST` entries are illustrative examples, not real coverage — edit the list in |
| 83 | + `extension.mjs` to fit your environment. |
| 84 | +- The loopback control server requires a per-launch token (templated into the panel), |
| 85 | + so other pages in your browser can't drive it. |
| 86 | +- Typed text (e.g. passwords) is redacted in `audit.log`, and password field values are |
| 87 | + excluded from snapshots. |
| 88 | +- Generated at runtime and not part of the source: `node_modules/` in the copied |
| 89 | + extension folder, plus `profile/`, `artifacts/`, and `audit.log` under |
| 90 | + `$COPILOT_HOME/extensions/chromium-control-canvas/`. |
0 commit comments