Documentation Index
Fetch the complete documentation index at: https://mintlify.com/remorses/playwriter/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The Editor class provides a code editor-like interface for viewing and modifying scripts and stylesheets in running web pages via Chrome DevTools Protocol.
Key features:
- List, read, search, and edit JavaScript and CSS files
- Changes are applied in-memory (persist until page reload)
- No server access needed - edits modify the running V8 instance
- Claude Code-like interface:
list(), read(), edit(), grep()
Use cases:
- Test quick fixes without rebuilding
- Toggle debug flags in production code
- Search page scripts for patterns
- Modify CSS for visual testing
Quick Start
const cdp = await getCDPSession({ page: state.page })
const editor = createEditor({ cdp })
await editor.enable()
// List available scripts
const scripts = await editor.list({ pattern: /app/ })
// Read a script
const { content } = await editor.read({ url: 'https://example.com/app.js' })
// Edit a script
await editor.edit({
url: 'https://example.com/app.js',
oldString: 'const DEBUG = false',
newString: 'const DEBUG = true'
})
Creating an Editor
const cdp = await getCDPSession({ page: state.page })
const editor = createEditor({ cdp })
await editor.enable() // Must be called before other methods
Important: Reload the page after enabling to capture all scripts. The editor collects scripts from Debugger.scriptParsed events.
Listing Scripts and Stylesheets
List All Resources
// List all scripts and stylesheets
const urls = await editor.list()
Filter by Pattern
// List only JS files
const jsFiles = await editor.list({ pattern: /\.js/ })
// List only CSS files
const cssFiles = await editor.list({ pattern: /\.css/ })
// Search for specific scripts
const appScripts = await editor.list({ pattern: /app/ })
Returns: Array of URLs (both external files and inline scripts)
Inline scripts without a URL get special inline://{id} URLs.
Reading Source Code
Read Full File
const { content, totalLines } = await editor.read({
url: 'https://example.com/app.js'
})
console.log('Total lines:', totalLines)
console.log(content)
Read Specific Range
// Read lines 100-200
const { content } = await editor.read({
url: 'https://example.com/app.js',
offset: 100,
limit: 100
})
Parameters:
url - Script or stylesheet URL (inline scripts have inline://{id} URLs)
offset - Line number to start from (0-based, default 0)
limit - Number of lines to return (default 2000)
Returns:
{
content: string, // Line-numbered content
totalLines: number, // Total lines in file
startLine: number, // First line in output (1-based)
endLine: number // Last line in output
}
Content format:
1| function example() {
2| console.log('hello')
3| }
Read CSS Files
const { content } = await editor.read({
url: 'https://example.com/styles.css'
})
CSS stylesheets work the same as JavaScript files.
Editing Code
Basic Edit
// Replace exact string
await editor.edit({
url: 'https://example.com/app.js',
oldString: 'const DEBUG = false',
newString: 'const DEBUG = true'
})
How it works:
- Performs exact string replacement (like Claude Code’s Edit tool)
oldString must match exactly once in the file
- If not found: throws error
- If found multiple times: throws error asking for more context
Edit CSS
await editor.edit({
url: 'https://example.com/styles.css',
oldString: 'color: red',
newString: 'color: blue'
})
CSS edits apply immediately to the page.
Dry Run
// Validate without applying
const result = await editor.edit({
url: 'https://example.com/app.js',
oldString: 'old code',
newString: 'new code',
dryRun: true
})
console.log('Valid:', result.success)
Edit Inline Scripts
Inline scripts (scripts without a URL) get inline://{id} URLs:
const matches = await editor.grep({ regex: /myFunction/ })
if (matches.length > 0) {
const { url } = matches[0]
console.log('Found in:', url) // 'inline://123'
await editor.edit({
url,
oldString: 'return false',
newString: 'return true'
})
}
Searching Code
Search All Files
const matches = await editor.grep({ regex: /console\.log/ })
// [
// { url: 'https://example.com/app.js', lineNumber: 42, lineContent: ' console.log("debug")' },
// ...
// ]
Search Specific File Types
// Search only CSS files
const matches = await editor.grep({
regex: /background-color/,
pattern: /\.css/
})
// Search only JS files
const matches = await editor.grep({
regex: /console\.(log|error|warn)/,
pattern: /\.js/
})
Search with Pattern Filters
// Search TODOs in app scripts only
const todoMatches = await editor.grep({
regex: /TODO|FIXME/i,
pattern: /app/
})
Returns:
[
{
url: string,
lineNumber: number, // 1-based
lineContent: string // Trimmed, max 200 chars
}
]
Writing Complete Files
// Read current content
const { content } = await editor.read({ url: 'https://example.com/app.js' })
// Transform content
const newContent = content.replace(/console\.log/g, 'console.debug')
// Write back
await editor.write({
url: 'https://example.com/app.js',
content: newContent
})
Use with caution - write() replaces entire file content. Prefer edit() for targeted changes.
Parameters:
url - Script or stylesheet URL
content - New complete content
dryRun - Validate without applying (default false, JS only)
Complete Examples
Toggle Debug Mode
const cdp = await getCDPSession({ page: state.page })
const editor = createEditor({ cdp })
await editor.enable()
// Find config file
const scripts = await editor.list({ pattern: /config/ })
// Read and edit
const { content } = await editor.read({ url: scripts[0] })
await editor.edit({
url: scripts[0],
oldString: 'DEBUG: false',
newString: 'DEBUG: true'
})
Search and Replace Console Logs
const cdp = await getCDPSession({ page: state.page })
const editor = createEditor({ cdp })
await editor.enable()
// Find all console.log calls
const matches = await editor.grep({ regex: /console\.log/ })
console.log(`Found ${matches.length} console.log calls`)
// For each file with matches
const files = [...new Set(matches.map(m => m.url))]
for (const url of files) {
const { content } = await editor.read({ url })
const newContent = content.replace(/console\.log/g, 'console.debug')
await editor.write({ url, content: newContent })
}
Edit Inline Script
const cdp = await getCDPSession({ page: state.page })
const editor = createEditor({ cdp })
await editor.enable()
// Search for inline scripts
const matches = await editor.grep({
regex: /myFunction/,
pattern: /^inline:/
})
if (matches.length > 0) {
const { url } = matches[0]
await editor.edit({
url,
oldString: 'const enabled = false',
newString: 'const enabled = true'
})
}
CSS Color Scheme Switch
const cdp = await getCDPSession({ page: state.page })
const editor = createEditor({ cdp })
await editor.enable()
// Find main stylesheet
const stylesheets = await editor.list({ pattern: /main\.css/ })
// Read current styles
const { content } = await editor.read({ url: stylesheets[0] })
// Replace color scheme
await editor.edit({
url: stylesheets[0],
oldString: '--primary: #007bff',
newString: '--primary: #dc3545'
})
await editor.edit({
url: stylesheets[0],
oldString: '--background: #ffffff',
newString: '--background: #1a1a1a'
})
Implementation Details
How Edits Work
For JavaScript:
- Uses
Debugger.setScriptSource CDP command (Chrome versions below 142)
- For Chrome 142+: Falls back to
Runtime.evaluate to re-execute modified script
- Edits modify the running V8 instance
- Changes persist until page reload
- Works best for scripts that define functions at global scope
For CSS:
- Uses
CSS.setStyleSheetText CDP command
- Changes apply immediately to rendered page
- No page flash or reload
Limitations
- In-memory only - edits are not saved to disk or server
- Lost on reload - changes disappear when page reloads
- Scope dependent - JavaScript edits work best for global function definitions
- No source maps - edits apply to compiled/minified code, not original source
Chrome 142+ Compatibility
Chrome deprecated Debugger.setScriptSource in version 142 (Feb 2026). The Editor automatically falls back to re-executing scripts via Runtime.evaluate, which works for most use cases.
Best Practices
Prefer edit() over write():
// Good - surgical change
await editor.edit({ url, oldString: 'x', newString: 'y' })
// Risky - replaces entire file
await editor.write({ url, content: newContent })
Use grep() to find targets:
// Don't guess URLs
const matches = await editor.grep({ regex: /DEBUG/ })
await editor.edit({ url: matches[0].url, ... })
Dry run for validation:
// Test before applying
await editor.edit({ url, oldString, newString, dryRun: true })
Reload to reset:
// Start fresh
await state.page.reload()
await editor.enable() // Re-enable after reload