diff --git a/browse/src/meta-commands.ts b/browse/src/meta-commands.ts index 16ed7f84..520b5485 100644 --- a/browse/src/meta-commands.ts +++ b/browse/src/meta-commands.ts @@ -114,7 +114,11 @@ export async function handleMetaCommand( // Separate target (selector/@ref) from output path for (const arg of remaining) { - if (arg.startsWith('@e') || arg.startsWith('@c') || arg.startsWith('.') || arg.startsWith('#') || arg.includes('[')) { + // File paths containing / and ending with an image/pdf extension are never CSS selectors + const isFilePath = arg.includes('/') && /\.(png|jpe?g|webp|pdf)$/i.test(arg); + if (isFilePath) { + outputPath = arg; + } else if (arg.startsWith('@e') || arg.startsWith('@c') || arg.startsWith('.') || arg.startsWith('#') || arg.includes('[')) { targetSelector = arg; } else { outputPath = arg; diff --git a/browse/test/commands.test.ts b/browse/test/commands.test.ts index 8e632567..256e8979 100644 --- a/browse/test/commands.test.ts +++ b/browse/test/commands.test.ts @@ -543,6 +543,17 @@ describe('Visual', () => { } }); + test('screenshot treats relative dot-slash path as file path, not CSS selector', async () => { + await handleWriteCommand('goto', [baseUrl + '/basic.html'], bm); + // ./path/to/file.png must be treated as output path, not a CSS class selector (#495) + const relPath = './browse-test-dotpath.png'; + const absPath = path.resolve(relPath); + const result = await handleMetaCommand('screenshot', [relPath], bm, async () => {}); + expect(result).toContain('Screenshot saved'); + expect(fs.existsSync(absPath)).toBe(true); + fs.unlinkSync(absPath); + }); + test('screenshot with nonexistent selector throws timeout', async () => { await handleWriteCommand('goto', [baseUrl + '/basic.html'], bm); try {