UNPKG

chromancer

Version:

A powerful command-line interface for automating Chrome browser using Playwright. Perfect for web scraping, automation, testing, and browser workflows.

183 lines (182 loc) 7.98 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const core_1 = require("@oclif/core"); const base_js_1 = require("../base.js"); class Scroll extends base_js_1.BaseCommand { static description = 'Scroll the page or to specific elements'; static examples = [ '<%= config.bin %> <%= command.id %> down', '<%= config.bin %> <%= command.id %> top', '<%= config.bin %> <%= command.id %> bottom', '<%= config.bin %> <%= command.id %> --to "#section3"', '<%= config.bin %> <%= command.id %> --by 500', '<%= config.bin %> <%= command.id %> --percent 50', ]; static args = { direction: core_1.Args.string({ description: 'Scroll direction', options: ['up', 'down', 'left', 'right', 'top', 'bottom'], }), }; static flags = { ...base_js_1.BaseCommand.baseFlags, to: core_1.Flags.string({ char: 't', description: 'CSS selector to scroll to', exclusive: ['by', 'percent'], }), by: core_1.Flags.integer({ char: 'b', description: 'Pixels to scroll by', exclusive: ['to', 'percent'], }), percent: core_1.Flags.integer({ char: 'p', description: 'Percentage of page to scroll to (0-100)', exclusive: ['to', 'by'], }), smooth: core_1.Flags.boolean({ description: 'Use smooth scrolling', default: true, }), 'wait-after': core_1.Flags.integer({ description: 'Wait time in ms after scrolling', default: 500, }), }; async run() { const { args, flags } = await this.parse(Scroll); await this.connectToChrome(flags.port, flags.host, flags.launch, flags.profile, flags.headless, flags.verbose, flags.keepOpen); if (!this.page) { this.error('Failed to connect to Chrome'); } await this.performScroll(this.page); } async performScroll(page) { const { args, flags } = await this.parse(Scroll); const behavior = flags.smooth ? 'smooth' : 'auto'; try { // Scroll to element if (flags.to) { await page.evaluate(({ selector, behavior }) => { const element = document.querySelector(selector); if (!element) throw new Error(`Element not found: ${selector}`); element.scrollIntoView({ behavior: behavior, block: 'center' }); }, { selector: flags.to, behavior }); this.log(`✅ Scrolled to element: ${flags.to}`); } // Scroll by pixels else if (flags.by) { const direction = args.direction || 'down'; let x = 0, y = 0; switch (direction) { case 'up': y = -flags.by; break; case 'down': y = flags.by; break; case 'left': x = -flags.by; break; case 'right': x = flags.by; break; default: y = flags.by; // Default to down } await page.evaluate(({ x, y, behavior }) => { window.scrollBy({ left: x, top: y, behavior: behavior }); }, { x, y, behavior }); this.log(`✅ Scrolled ${direction} by ${flags.by}px`); } // Scroll to percentage else if (flags.percent !== undefined) { if (flags.percent < 0 || flags.percent > 100) { this.error('Percentage must be between 0 and 100'); } await page.evaluate(({ percent, behavior }) => { const maxScroll = document.documentElement.scrollHeight - window.innerHeight; const targetScroll = (maxScroll * percent) / 100; window.scrollTo({ top: targetScroll, behavior: behavior }); }, { percent: flags.percent, behavior }); this.log(`✅ Scrolled to ${flags.percent}% of page`); } // Scroll by direction else if (args.direction) { switch (args.direction) { case 'top': await page.evaluate(({ behavior }) => { window.scrollTo({ top: 0, behavior: behavior }); }, { behavior }); this.log('✅ Scrolled to top'); break; case 'bottom': await page.evaluate(({ behavior }) => { window.scrollTo({ top: document.documentElement.scrollHeight, behavior: behavior }); }, { behavior }); this.log('✅ Scrolled to bottom'); break; case 'up': await page.evaluate(({ behavior }) => { window.scrollBy({ top: -window.innerHeight * 0.8, behavior: behavior }); }, { behavior }); this.log('✅ Scrolled up'); break; case 'down': await page.evaluate(({ behavior }) => { window.scrollBy({ top: window.innerHeight * 0.8, behavior: behavior }); }, { behavior }); this.log('✅ Scrolled down'); break; case 'left': await page.evaluate(({ behavior }) => { window.scrollBy({ left: -window.innerWidth * 0.8, behavior: behavior }); }, { behavior }); this.log('✅ Scrolled left'); break; case 'right': await page.evaluate(({ behavior }) => { window.scrollBy({ left: window.innerWidth * 0.8, behavior: behavior }); }, { behavior }); this.log('✅ Scrolled right'); break; } } // Default: scroll down one viewport else { await page.evaluate(({ behavior }) => { window.scrollBy({ top: window.innerHeight * 0.8, behavior: behavior }); }, { behavior }); this.log('✅ Scrolled down'); } // Wait after scrolling if (flags['wait-after'] > 0) { await page.waitForTimeout(flags['wait-after']); } // Log current position if verbose if (flags.verbose) { const position = await page.evaluate(() => ({ x: window.pageXOffset, y: window.pageYOffset, maxX: document.documentElement.scrollWidth - window.innerWidth, maxY: document.documentElement.scrollHeight - window.innerHeight, })); this.logVerbose('Scroll position', { x: `${position.x}/${position.maxX}`, y: `${position.y}/${position.maxY}`, percentX: Math.round((position.x / position.maxX) * 100) || 0, percentY: Math.round((position.y / position.maxY) * 100) || 0, }); } } catch (error) { this.error(`Failed to scroll: ${error.message}`); } } } exports.default = Scroll;