From 653f00c3a3d618e3a2f91d696eaa15e3307c36d7 Mon Sep 17 00:00:00 2001 From: "Tommy D. Rossi" Date: Tue, 27 Jan 2026 18:36:14 +0100 Subject: [PATCH] feat(extension): redesign welcome page with Prism.js syntax highlighting --- extension/package.json | 2 +- extension/scripts/download-prism.ts | 42 +++++++++++++ extension/src/welcome.html | 94 ++++++++++++++++++++--------- extension/vite.config.mts | 1 + playwriter/src/cli.ts | 3 +- 5 files changed, 109 insertions(+), 33 deletions(-) create mode 100644 extension/scripts/download-prism.ts 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()"

+ + diff --git a/extension/vite.config.mts b/extension/vite.config.mts index 5b9bf2e..0b6576c 100644 --- a/extension/vite.config.mts +++ b/extension/vite.config.mts @@ -24,6 +24,7 @@ export default defineConfig({ src: resolve(__dirname, 'icons/*'), dest: 'icons' }, + { src: resolve(__dirname, 'manifest.json'), dest: '.', diff --git a/playwriter/src/cli.ts b/playwriter/src/cli.ts index f259f27..477846c 100644 --- a/playwriter/src/cli.ts +++ b/playwriter/src/cli.ts @@ -141,8 +141,7 @@ cli try { const res = await fetch(`${serverUrl}/cli/session/suggest`) const { next } = await res.json() as { next: number } - console.log(`Session ${next} created.`) - console.log(`Use it with: playwriter -s ${next} -e "await page.goto('https://example.com')"`) + console.log(`Session ${next} created. Use with: playwriter -s ${next} -e "..."`) } catch (error: any) { console.error(`Error: ${error.message}`) process.exit(1)