diff --git a/playwriter/src/debugger-examples-types.ts b/playwriter/src/debugger-examples-types.ts index 9ddc8fa..a1cd6fb 100644 --- a/playwriter/src/debugger-examples-types.ts +++ b/playwriter/src/debugger-examples-types.ts @@ -1,10 +1,13 @@ -import type { Page } from 'playwright-core' +import type { Page, Locator } from 'playwright-core' import type { CDPSession } from './cdp-session.js' import type { Debugger } from './debugger.js' import type { Editor } from './editor.js' +import type { StylesResult } from './styles.js' export declare const page: Page export declare const getCDPSession: (options: { page: Page }) => Promise export declare const createDebugger: (options: { cdp: CDPSession }) => Debugger export declare const createEditor: (options: { cdp: CDPSession }) => Editor +export declare const getStylesForLocator: (options: { locator: Locator; includeUserAgentStyles?: boolean }) => Promise +export declare const formatStylesAsText: (styles: StylesResult) => string export declare const console: { log: (...args: unknown[]) => void } diff --git a/playwriter/src/mcp.ts b/playwriter/src/mcp.ts index b9a11d5..fa1f856 100644 --- a/playwriter/src/mcp.ts +++ b/playwriter/src/mcp.ts @@ -503,6 +503,43 @@ server.resource('editor-api', 'playwriter://editor-api', { mimeType: 'text/plain } }) +server.resource('styles-api', 'playwriter://styles-api', { mimeType: 'text/plain' }, async () => { + const packageJsonPath = require.resolve('playwriter/package.json') + const distDir = path.join(path.dirname(packageJsonPath), 'dist') + + const stylesTypes = fs + .readFileSync(path.join(distDir, 'styles.d.ts'), 'utf-8') + .replace(/\/\/# sourceMappingURL=.*$/gm, '') + .trim() + const stylesExamples = fs.readFileSync(path.join(distDir, 'styles-examples.ts'), 'utf-8') + + return { + contents: [ + { + uri: 'playwriter://styles-api', + text: dedent` + # Styles API Reference + + The getStylesForLocator function inspects CSS styles applied to an element, similar to browser DevTools "Styles" panel. + + ## Types + + \`\`\`ts + ${stylesTypes} + \`\`\` + + ## Examples + + \`\`\`ts + ${stylesExamples} + \`\`\` + `, + mimeType: 'text/plain', + }, + ], + } +}) + server.tool( 'execute', promptContent, diff --git a/playwriter/src/styles-examples.ts b/playwriter/src/styles-examples.ts new file mode 100644 index 0000000..321833b --- /dev/null +++ b/playwriter/src/styles-examples.ts @@ -0,0 +1,77 @@ +import { page, getStylesForLocator, formatStylesAsText, console } from './debugger-examples-types.js' + +// Example: Get styles for an element and display them +async function getElementStyles() { + const loc = page.locator('.my-button') + const styles = await getStylesForLocator({ locator: loc }) + console.log(formatStylesAsText(styles)) +} + +// Example: Inspect computed styles for a specific element +async function inspectButtonStyles() { + const button = page.getByRole('button', { name: 'Submit' }) + const styles = await getStylesForLocator({ locator: button }) + + console.log('Element:', styles.element) + + if (styles.inlineStyle) { + console.log('Inline styles:', styles.inlineStyle) + } + + for (const rule of styles.rules) { + console.log(`${rule.selector}: ${JSON.stringify(rule.declarations)}`) + if (rule.source) { + console.log(` Source: ${rule.source.url}:${rule.source.line}`) + } + } +} + +// Example: Include browser default (user-agent) styles +async function getStylesWithUserAgent() { + const loc = page.locator('input[type="text"]') + const styles = await getStylesForLocator({ + locator: loc, + includeUserAgentStyles: true, + }) + console.log(formatStylesAsText(styles)) +} + +// Example: Find where a CSS property is defined +async function findPropertySource() { + const loc = page.locator('.card') + const styles = await getStylesForLocator({ locator: loc }) + + const backgroundRule = styles.rules.find((r) => 'background-color' in r.declarations) + if (backgroundRule) { + console.log('background-color defined by:', backgroundRule.selector) + if (backgroundRule.source) { + console.log(` at ${backgroundRule.source.url}:${backgroundRule.source.line}`) + } + } +} + +// Example: Check inherited styles +async function checkInheritedStyles() { + const loc = page.locator('.nested-text') + const styles = await getStylesForLocator({ locator: loc }) + + const inheritedRules = styles.rules.filter((r) => r.inheritedFrom) + for (const rule of inheritedRules) { + console.log(`Inherited from ${rule.inheritedFrom}: ${rule.selector}`) + console.log(' Properties:', rule.declarations) + } +} + +// Example: Compare styles between two elements +async function compareStyles() { + const primary = await getStylesForLocator({ locator: page.locator('.btn-primary') }) + const secondary = await getStylesForLocator({ locator: page.locator('.btn-secondary') }) + + console.log('Primary button:') + console.log(formatStylesAsText(primary)) + + console.log('Secondary button:') + console.log(formatStylesAsText(secondary)) +} + +export { getElementStyles, inspectButtonStyles, getStylesWithUserAgent, findPropertySource, checkInheritedStyles, compareStyles }