diff --git a/website/AGENTS.md b/website/AGENTS.md index ac3c333..2a2731a 100644 --- a/website/AGENTS.md +++ b/website/AGENTS.md @@ -49,6 +49,16 @@ If you create a new CSS file that needs `@variant dark`, add it as a CSS `@impor For files with many dark mode selectors (like prism syntax colors), define CSS variables in `:root { @variant dark { ... } }` and reference them in selectors. This avoids repeating `@variant dark` in every rule. +## Images must not cause layout shift + +Every `` must have explicit `width` and `height` attributes matching the intrinsic pixel dimensions of the source file. Add `style={{ height: "auto" }}` to keep it responsive. This lets the browser reserve the correct aspect ratio space before the image loads, preventing layout shift. + +```tsx + +``` + +Use `sips -g pixelWidth -g pixelHeight ` to get dimensions on macOS. + ## No localStorage dark mode toggle There is no JS-based `.dark` class toggle. Do not add inline scripts to toggle `.dark` on ``. The site follows the OS preference only via `prefers-color-scheme`. diff --git a/website/src/routes/_index.tsx b/website/src/routes/_index.tsx index a242e49..8cdb98e 100644 --- a/website/src/routes/_index.tsx +++ b/website/src/routes/_index.tsx @@ -45,6 +45,7 @@ export const meta: MetaFunction = () => { const tocItems = [ { label: "Getting started", href: "#getting-started" }, { label: "How it works", href: "#how-it-works" }, + { label: "Collaboration", href: "#collaboration" }, { label: "Snapshots", href: "#snapshots" }, { label: "Visual labels", href: "#visual-labels" }, { label: "Sessions", href: "#sessions" }, @@ -61,9 +62,10 @@ export default function IndexPage() {

- Playwriter lets you control your Chrome browser with the full - Playwright API. A Chrome extension, a local relay, and a CLI. No new - browser windows, no Chrome flags, no context bloat.{" "} + You want your agent to control the browser. Your actual + Chrome {" \u2014 "} with logins, extensions, and cookies already + there. Not a headless instance that gets blocked by every captcha + and bot detector.{" "} Star on GitHub.

@@ -71,26 +73,36 @@ export default function IndexPage() { Playwriter controlling Chrome with accessibility labels overlay - Your existing Chrome session. Extensions, logins, cookies — all there. + Your existing Chrome session. Extensions, logins, cookies {" \u2014 "} all there.

- Every browser automation MCP I tried either spawns a new Chrome - instance or forces you into a limited set of predefined tools. Playwriter - does neither. It connects to the browser you already have open, - exposes the full Playwright API through a single{" "} - execute tool, and gets out of the way. - One tool. Any Playwright code. No wrappers. + Other browser MCPs either spawn a fresh Chrome or give agents + a fixed set of tools. New Chrome means no logins, no extensions, + instant bot detection, and double the memory. Fixed tools mean the + agent can{"'"}t profile performance, can{"'"}t set breakpoints, + can{"'"}t intercept network requests {" \u2014 "} it can only do what someone + decided to expose. +

+ +

+ Playwriter gives agents the full Playwright API through + a single execute tool. One tool, any Playwright code, + no wrappers. Low context usage because there{"'"}s no schema bloat + from dozens of tool definitions. And it runs in your existing browser, + so nothing extra gets spawned.

- Three steps. Extension, icon click, then you're automating. + Three steps and your agent is browsing.

    @@ -130,12 +142,12 @@ export default function IndexPage() {

    - The extension uses chrome.debugger to - attach to tabs where you clicked the icon. It opens a WebSocket - connection to a local relay server. The CLI (or MCP, or your own - Playwright script) connects to the same relay. CDP commands flow - through; the extension forwards them to Chrome and sends responses - back. + Click the extension icon on a tab {" \u2014 "} it attaches via{" "} + chrome.debugger and opens a WebSocket to a + local relay. Your agent (CLI, MCP, or a Playwright script) connects + to the same relay. CDP commands flow through; the extension + forwards them to Chrome and sends responses back. No Chrome restart, + no flags, no special setup.

    {dedent` @@ -156,10 +168,27 @@ export default function IndexPage() { `}

    - No Chrome restart required. No --remote-debugging-port{" "} - flags. The extension handles the CDP attachment transparently, and - the relay multiplexes sessions so multiple agents or CLI instances - can work with the same browser simultaneously. + The relay multiplexes sessions, so multiple agents + or CLI instances can work with the same browser at the same time. +

    + +
    + +
    + +

    + Because the agent works in your browser, you can + collaborate. You see everything it does in real time. When it hits + a captcha, you solve it. When a consent wall + appears, you click through it. When the agent gets stuck, you + disable the extension on that tab, fix things manually, re-enable + it, and the agent picks up where it left off. +

    + +

    + You{"'"}re not watching a remote screen or reading logs after the + fact. You{"'"}re sharing a browser {" \u2014 "} the + agent does the repetitive work, you step in when it needs a human.

    @@ -167,10 +196,11 @@ export default function IndexPage() {

    - The core feedback loop is observe → act → observe. - Accessibility snapshots are the primary way to read page state. They return - the full interactive element tree as text, with Playwright locators attached - to every element. + Your agent needs to see the page before it can act. + Accessibility snapshots return every interactive element as text, + with Playwright locators attached. 5{"\u2013"}20KB instead of + 100KB+ for a screenshot {" \u2014 "} cheaper, faster, and the + agent can parse them without vision.

    {dedent` @@ -200,24 +230,22 @@ export default function IndexPage() { `}

    - Snapshots are text. They cost a fraction of what screenshots cost in - tokens. Use them as your primary debugging tool. Only reach for - screenshots when spatial layout matters — grids, dashboards, maps. + Use snapshots as the primary way to read pages. Only + reach for screenshots when spatial layout matters {" \u2014 "} grids, + dashboards, maps.

    - - Accessibility tree as text. 5–20KB vs 100KB+ for screenshots. - -

    - For pages where spatial layout matters,{" "} + When the agent needs to understand where things are on + screen,{" "} screenshotWithAccessibilityLabels overlays{" "} - Vimium-style labels on every interactive element. Take a screenshot, - read the labels, click by reference. + Vimium-style labels on every interactive element. + The agent sees the screenshot, reads the labels, and clicks by + reference.

    {dedent` @@ -244,10 +272,10 @@ export default function IndexPage() {

    - Each session runs in an isolated sandbox with its own{" "} - state object. Variables, pages, listeners - persist between calls within a session. Different sessions get - different state. Browser tabs are shared. + Run multiple agents at once without them stepping on + each other. Each session is an isolated sandbox with its own{" "} + state object. Variables, pages, and listeners + persist between calls. Browser tabs are shared, but state is not.

    {dedent` @@ -280,9 +308,10 @@ export default function IndexPage() {

    - Full Chrome DevTools Protocol access. Set breakpoints, step through - code, inspect variables at runtime. Live-edit page scripts and CSS - without reloading. + Things no other browser MCP can do. Set breakpoints, + step through code, inspect variables at runtime. Live-edit + page scripts and CSS without reloading. Full Chrome DevTools + Protocol access, not a watered-down subset.

    {dedent` @@ -304,7 +333,7 @@ export default function IndexPage() {

    - Breakpoints, stepping, variable inspection — from the CLI. + Breakpoints, stepping, variable inspection {" \u2014 "} from the CLI.
    @@ -312,9 +341,10 @@ export default function IndexPage() {

    - Intercept requests and responses to reverse-engineer APIs, scrape - data, or debug network issues. Store captured data in{" "} - state and analyze across calls. + Let the agent watch network traffic to + reverse-engineer APIs, scrape data behind JavaScript rendering, + or debug failing requests. Captured data lives in{" "} + state and persists across calls.

    {dedent` @@ -330,10 +360,10 @@ export default function IndexPage() { `}

    - This is faster than scrolling through DOM. Capture the real API - calls, inspect their schemas, and replay them with different - parameters. Works for pagination, authenticated endpoints, and - anything behind JavaScript rendering. + Faster than scraping the DOM. The agent captures the + real API calls, inspects their schemas, and replays them with + different parameters. Works for pagination, authenticated endpoints, + and anything behind client-side rendering.

    @@ -341,10 +371,10 @@ export default function IndexPage() {

    - Record the active tab as video using{" "} - chrome.tabCapture. The recording runs in - the extension context, so it survives page navigation. Video is saved - as MP4. + Have the agent record what it{"'"}s doing as MP4 + video. The recording uses chrome.tabCapture and + runs in the extension context, so it survives page + navigation.

    {dedent` @@ -368,7 +398,7 @@ export default function IndexPage() {

    - Native tab capture. 30–60fps. Survives navigation. + Native tab capture. 30{"\u2013"}60fps. Survives navigation.
    @@ -376,7 +406,7 @@ export default function IndexPage() {

    - How Playwriter compares to other browser automation approaches. + Why use this over the alternatives.

    - Control Chrome on any machine from anywhere over the internet. - The relay runs on the host alongside Chrome. - A{" "} + Control Chrome on a remote machine {" \u2014 "} a headless + Mac mini, a cloud VM, a devcontainer. A{" "} traforo{" "} - tunnel exposes it through Cloudflare, giving you a secure public URL. - No VPN, no firewall rules, no port forwarding. + tunnel exposes the relay through Cloudflare. No VPN, no + firewall rules, no port forwarding.

    {dedent` @@ -441,9 +470,9 @@ export default function IndexPage() { `}

    - Also works on a LAN without tunnels — just set{" "} + Also works on a LAN without tunnels {" \u2014 "} just set{" "} PLAYWRITER_HOST=192.168.1.10. Works for MCP - too — set PLAYWRITER_HOST and{" "} + too {" \u2014 "} set PLAYWRITER_HOST and{" "} PLAYWRITER_TOKEN in your MCP client env config. Use cases: headless Mac mini, remote user support, multi-machine automation, dev from a VM or devcontainer. @@ -454,28 +483,27 @@ export default function IndexPage() {

    - Playwriter is local by default. The relay runs on{" "} - localhost:19988 and only accepts connections - from the extension. There's no remote server, no account, no - telemetry. + Everything runs on your machine. The relay binds + to localhost:19988 and only accepts connections + from the extension. No remote server, no account, no telemetry.

  1. - Local only — WebSocket server binds to + Local only {" \u2014 "} WebSocket server binds to localhost. Nothing leaves your machine.
  2. - Origin validation — only the Playwriter + Origin validation {" \u2014 "} only the Playwriter extension origin is accepted. Browsers cannot spoof the Origin header, so malicious websites cannot connect.
  3. - Explicit consent — only tabs where you + Explicit consent {" \u2014 "} only tabs where you clicked the extension icon are controlled. No background access.
  4. - Visible automation — Chrome shows an + Visible automation {" \u2014 "} Chrome shows an automation banner on controlled tabs.