diff --git a/extension/package.json b/extension/package.json
index 7b6fc7d..87981a0 100644
--- a/extension/package.json
+++ b/extension/package.json
@@ -16,7 +16,7 @@
},
"license": "Apache-2.0",
"scripts": {
- "build": "tsc --project . && vite build --config vite.config.mts",
+ "build": "tsc --project . && vite build --config vite.config.mts && tsx scripts/download-prism.ts",
"reload": "bun run build && osascript -e 'tell application \"Google Chrome\" to open location \"chrome://extensions/?id=pebbngnfojnignonigcnkdilknapkgid\"'",
"watch": "vite build --watch --config vite.config.mts",
"clean": "rm -rf dist"
diff --git a/extension/scripts/download-prism.ts b/extension/scripts/download-prism.ts
new file mode 100644
index 0000000..5ee18a0
--- /dev/null
+++ b/extension/scripts/download-prism.ts
@@ -0,0 +1,42 @@
+// Downloads Prism.js assets into dist/src/ for the welcome page.
+// Chrome extension CSP blocks external scripts, so we bundle them locally.
+import https from 'node:https'
+import fs from 'node:fs'
+import path from 'node:path'
+
+const BASE = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/'
+const DEST = path.join('dist', 'src')
+
+const files: [string, string][] = [
+ ['prism.min.js', 'prism.min.js'],
+ ['components/prism-bash.min.js', 'prism-bash.min.js'],
+]
+
+function download(url: string, dest: string): Promise {
+ return new Promise((resolve, reject) => {
+ https.get(url, (res) => {
+ if (res.statusCode !== 200) {
+ reject(new Error(`Failed to download ${url}: ${res.statusCode}`))
+ return
+ }
+ const chunks: Buffer[] = []
+ res.on('data', (chunk: Buffer) => { chunks.push(chunk) })
+ res.on('end', () => {
+ fs.writeFileSync(dest, Buffer.concat(chunks))
+ resolve()
+ })
+ res.on('error', reject)
+ }).on('error', reject)
+ })
+}
+
+async function main() {
+ await Promise.all(
+ files.map(([src, dest]) => {
+ return download(BASE + src, path.join(DEST, dest))
+ })
+ )
+ console.log(`Downloaded ${files.length} Prism.js files to ${DEST}`)
+}
+
+main()
diff --git a/extension/src/welcome.html b/extension/src/welcome.html
index e79bdaf..4f3a6ae 100644
--- a/extension/src/welcome.html
+++ b/extension/src/welcome.html
@@ -10,7 +10,7 @@
--color-text: #262626;
--color-heading: #171717;
--color-border: #d4d4d4;
- --color-code-bg: #f5f5f5;
+ --color-code-bg: transparent;
--color-link: #0969da;
--color-link-hover: #0550ae;
--color-success: #16a34a;
@@ -25,7 +25,7 @@
--color-text: #e5e5e5;
--color-heading: #fafafa;
--color-border: #404040;
- --color-code-bg: #171717;
+ --color-code-bg: transparent;
--color-link: #4493f8;
--color-link-hover: #539bf5;
--color-success: #22c55e;
@@ -90,24 +90,22 @@
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
"Liberation Mono", monospace;
font-size: 0.875em;
- background-color: var(--color-code-bg);
- padding: 0.2em 0.4em;
- border-radius: 6px;
}
pre {
- background-color: var(--color-code-bg);
- padding: 1rem;
- border-radius: 6px;
- overflow-x: auto;
+ background: transparent !important;
+ border: none !important;
+ padding: 0 !important;
margin-bottom: 1rem;
- border: 1px solid var(--color-border);
+ overflow: visible;
+ white-space: pre;
}
pre code {
- background-color: transparent;
- padding: 0;
+ background: transparent !important;
+ padding: 0 !important;
font-size: 0.875rem;
+ white-space: pre;
}
ol,
@@ -125,8 +123,6 @@
color: var(--color-heading);
}
-
-
.step-number {
color: var(--color-link);
font-weight: 700;
@@ -144,6 +140,7 @@
.step-content {
flex: 1;
+ min-width: 0;
}
footer {
@@ -154,6 +151,41 @@
color: var(--color-muted);
font-size: 0.875rem;
}
+
+ /* Tomorrow (light) token colors */
+ code[class*="language-"],
+ pre[class*="language-"] {
+ color: #8959a8;
+ text-shadow: none;
+ }
+ .token.comment, .token.block-comment, .token.prolog, .token.doctype, .token.cdata { color: #8e908c; }
+ .token.punctuation { color: #8959a8; }
+ .token.tag, .token.attr-name, .token.namespace, .token.deleted { color: #c82829; }
+ .token.function-name { color: #4271ae; }
+ .token.boolean, .token.number, .token.function { color: #f5871f; }
+ .token.property, .token.class-name, .token.constant, .token.symbol { color: #eab700; }
+ .token.selector, .token.important, .token.atrule, .token.keyword, .token.builtin { color: #8959a8; }
+ .token.string, .token.char, .token.attr-value, .token.regex, .token.variable { color: #718c00; }
+ .token.operator, .token.entity, .token.url { color: #3e999f; }
+ .token.inserted { color: #718c00; }
+
+ /* Tomorrow Night (dark) token colors */
+ @media (prefers-color-scheme: dark) {
+ code[class*="language-"],
+ pre[class*="language-"] {
+ color: #b294bb;
+ }
+ .token.comment, .token.block-comment, .token.prolog, .token.doctype, .token.cdata { color: #969896; }
+ .token.punctuation { color: #b294bb; }
+ .token.tag, .token.attr-name, .token.namespace, .token.deleted { color: #cc6666; }
+ .token.function-name { color: #81a2be; }
+ .token.boolean, .token.number, .token.function { color: #de935f; }
+ .token.property, .token.class-name, .token.constant, .token.symbol { color: #f0c674; }
+ .token.selector, .token.important, .token.atrule, .token.keyword, .token.builtin { color: #b294bb; }
+ .token.string, .token.char, .token.attr-value, .token.regex, .token.variable { color: #b5bd68; }
+ .token.operator, .token.entity, .token.url { color: #8abeb7; }
+ .token.inserted { color: #b5bd68; }
+ }
@@ -171,18 +203,10 @@
1
-
Install the extension
+
Pin the extension
- Add the Playwriter extension from the Chrome Web Store.
-
-
-
- Open Chrome Web Store
-
+ Click the puzzle icon in Chrome's toolbar, then pin Playwriter so
+ it's always visible.
@@ -204,7 +228,9 @@
Install the CLI and start automating the browser:
- npm i -g playwriter
+ # install the CLI globally
+npm i -g playwriter
+# navigate to a URL in the active tab
playwriter -s 1 -e "await page.goto('https://example.com')"
@@ -216,15 +242,20 @@ playwriter -s 1 -e "await page.goto('https://example.com')"
Install the Playwriter skill so your agent can call the CLI:
- npx -y skills add remorses/playwriter
+ # add playwriter skill to your coding agent
+npx -y skills add remorses/playwriter
- Quick Start (CLI)
+ CLI Examples
- playwriter session new # creates stateful sandbox, outputs session id (e.g. 1)
+ # create a stateful sandbox, outputs session id (e.g. 1)
+playwriter session new
+# navigate to a URL
playwriter -s 1 -e "await page.goto('https://example.com')"
+# get the accessibility tree of the page
playwriter -s 1 -e "console.log(await accessibilitySnapshot({ page }))"
+# click an element by its accessibility reference
playwriter -s 1 -e "await page.locator('aria-ref=e5').click()"
@@ -280,7 +311,8 @@ playwriter -s 1 -e "await page.locator('aria-ref=e5').click()"
server setup, auto-configure it with:
- npx -y @playwriter/install-mcp playwriter@latest
+ # auto-configure MCP server for your editor
+npx -y @playwriter/install-mcp playwriter@latest
For full MCP instructions, see
@@ -311,5 +343,7 @@ playwriter -s 1 -e "await page.locator('aria-ref=e5').click()"
+
+