{"_id":"@cydoentis/pawprint","name":"@cydoentis/pawprint","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@cydoentis/pawprint","version":"1.0.0","description":"Automated screenshot tool for web apps across themes, palettes, and viewports","type":"module","bin":{"pawprint":"dist/cli/index.js"},"scripts":{"build":"tsc","prepublishOnly":"npm run build","start":"tsx src/cli/index.ts","dev":"tsx watch src/cli/index.ts","test":"echo \"Error: no test specified\" && exit 1"},"keywords":["screenshot","playwright","testing","visual-regression","theming","dark-mode","cli","automation"],"author":{"name":"Cydo Entis"},"license":"MIT","engines":{"node":">=18"},"repository":{"type":"git","url":"git+https://github.com/CydoEntis/pawprint.git"},"devDependencies":{"@types/node":"^25.2.3","tsx":"^4.21.0","typescript":"^5.9.3"},"dependencies":{"chalk":"^5.6.2","commander":"^14.0.3","playwright":"^1.58.2"},"_id":"@cydoentis/pawprint@1.0.0","gitHead":"fb2b69e78303afedf13496dde813a0c3d877bc17","bugs":{"url":"https://github.com/CydoEntis/pawprint/issues"},"homepage":"https://github.com/CydoEntis/pawprint#readme","_nodeVersion":"22.20.0","_npmVersion":"10.9.3","dist":{"integrity":"sha512-0nSfMOut6NNIKRfOjK6ZPG22fvYriMfdnJnXKpVxI6dAQ40Bf8sx5m08hcPGcEP7LtKmjteCN+H5/Ako7P/cYQ==","shasum":"8953119c3e2d804f55d4d4206b0c55c91cc4ffb5","tarball":"https://registry.npmjs.org/@cydoentis/pawprint/-/pawprint-1.0.0.tgz","fileCount":10,"unpackedSize":48750,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCqj3OkjKPA+Jow3EqPxGovNBVBTboQXJo1+2xa8uGs3AIhANWBiK8UBjhIBsHXbCjpPhqSBI8dv0s4ImEQFFM0mKWs"}]},"_npmUser":{"name":"cydoentis","email":"cydoentis@gmail.com"},"directories":{},"maintainers":[{"name":"cydoentis","email":"cydoentis@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pawprint_1.0.0_1771725677486_0.3381539054824565"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-22T02:01:17.351Z","1.0.0":"2026-02-22T02:01:17.626Z","modified":"2026-02-22T02:01:17.860Z"},"maintainers":[{"name":"cydoentis","email":"cydoentis@gmail.com"}],"description":"Automated screenshot tool for web apps across themes, palettes, and viewports","homepage":"https://github.com/CydoEntis/pawprint#readme","keywords":["screenshot","playwright","testing","visual-regression","theming","dark-mode","cli","automation"],"repository":{"type":"git","url":"git+https://github.com/CydoEntis/pawprint.git"},"author":{"name":"Cydo Entis"},"bugs":{"url":"https://github.com/CydoEntis/pawprint/issues"},"license":"MIT","readme":"# Pawprint\r\n\r\nAutomated screenshot generation for web applications across themes, color palettes, and viewport sizes.\r\n\r\nPawprint uses Playwright to capture screenshots of your app in every combination of route, mode (light/dark), palette, and viewport you configure. It handles SPA authentication, organizes output into timestamped folders, and generates metadata for each run.\r\n\r\n## Installation\r\n\r\n```bash\r\nnpm install\r\nnpx playwright install chromium\r\nnpm run build\r\n```\r\n\r\nTo make the `pawprint` command available globally:\r\n\r\n```bash\r\nnpm link\r\n```\r\n\r\n## Quick Start\r\n\r\n```bash\r\n# 1. Generate a config file in your app directory\r\npawprint init /path/to/your-app\r\n\r\n# 2. Edit the generated pawprint.config.json with your app's details\r\n\r\n# 3. Start your app, then generate screenshots\r\npawprint generate /path/to/your-app\r\n```\r\n\r\n## CLI Commands\r\n\r\n### `pawprint generate <app-path>`\r\n\r\nTakes screenshots across all configured theme/palette/viewport combinations.\r\n\r\n```bash\r\npawprint generate /path/to/app\r\npawprint generate /path/to/app --config ./custom-config.json\r\npawprint generate /path/to/app --output ./my-screenshots\r\npawprint generate /path/to/app --debug\r\n```\r\n\r\n| Option | Description |\r\n|---|---|\r\n| `-c, --config <path>` | Custom config file path (default: `<app-path>/pawprint.config.json`) |\r\n| `-o, --output <dir>` | Override output directory |\r\n| `--debug` | Enable debug mode with extra error output |\r\n\r\n### `pawprint init [app-path]`\r\n\r\nCreates a sample `pawprint.config.json` at the given path (defaults to `.`).\r\n\r\n```bash\r\npawprint init\r\npawprint init /path/to/app\r\npawprint init . --force   # overwrite existing config\r\n```\r\n\r\n### `pawprint list <app-path>`\r\n\r\nLists previous screenshot runs organized by date.\r\n\r\n```bash\r\npawprint list /path/to/app\r\npawprint list /path/to/app --output ./my-screenshots\r\n```\r\n\r\n## Configuration\r\n\r\nCreate a `pawprint.config.json` in your app directory or pass a custom path with `--config`.\r\n\r\n### Full Example\r\n\r\n```json\r\n{\r\n  \"baseUrl\": \"http://localhost:3000\",\r\n  \"routes\": [\"/\", \"/dashboard\", \"/settings\", \"/profile\"],\r\n  \"modes\": [\"light\", \"dark\"],\r\n  \"palettes\": [\"default\", \"high-contrast\"],\r\n  \"viewports\": [\r\n    { \"name\": \"desktop\", \"width\": 1440, \"height\": 900 },\r\n    { \"name\": \"mobile\", \"width\": 390, \"height\": 844 },\r\n    { \"name\": \"tablet\", \"width\": 768, \"height\": 1024 }\r\n  ],\r\n  \"appearance\": {\r\n    \"strategy\": \"localStorage\",\r\n    \"modeKey\": \"theme\",\r\n    \"paletteKey\": \"palette\"\r\n  },\r\n  \"outputDir\": \"pawprint-output\",\r\n  \"auth\": {\r\n    \"strategy\": \"form\",\r\n    \"loginUrl\": \"/login\",\r\n    \"credentials\": {\r\n      \"username\": \"admin@example.com\",\r\n      \"password\": \"password123\"\r\n    },\r\n    \"selectors\": {\r\n      \"username\": \"input[type=email]\",\r\n      \"password\": \"input[type=password]\",\r\n      \"submit\": \"button[type=submit]\"\r\n    }\r\n  },\r\n  \"screenshotOptions\": {\r\n    \"fullPage\": false,\r\n    \"waitForTimeout\": 500,\r\n    \"waitUntil\": \"networkidle\"\r\n  }\r\n}\r\n```\r\n\r\n### Required Fields\r\n\r\n| Field | Type | Description |\r\n|---|---|---|\r\n| `baseUrl` | `string` | Base URL of the running app (e.g. `http://localhost:3000`) |\r\n| `routes` | `string[]` | Routes to screenshot (e.g. `[\"/\", \"/dashboard\"]`) |\r\n| `modes` | `string[]` | Theme modes (e.g. `[\"light\", \"dark\"]`) |\r\n| `appearance` | `object` | How themes are applied to the app |\r\n\r\n### Optional Fields\r\n\r\n| Field | Type | Default | Description |\r\n|---|---|---|---|\r\n| `palettes` | `string[]` | `[\"default\"]` | Color palette names |\r\n| `viewports` | `ViewportPreset[]` | `[{name:\"default\", width:1440, height:900}]` | Viewport sizes |\r\n| `outputDir` | `string` | `\"pawprint-output\"` | Root output directory |\r\n| `auth` | `AuthStrategy` | `undefined` | Authentication config |\r\n| `screenshotOptions` | `object` | see below | Screenshot capture settings |\r\n\r\n### Appearance Strategies\r\n\r\n**localStorage** -- For apps that store theme preferences in localStorage:\r\n\r\n```json\r\n{\r\n  \"strategy\": \"localStorage\",\r\n  \"modeKey\": \"theme\",\r\n  \"paletteKey\": \"palette\"\r\n}\r\n```\r\n\r\nPawprint sets the localStorage keys, then reloads the page so the app picks up the new values.\r\n\r\n**dom** -- For apps that use CSS classes or DOM attributes (planned):\r\n\r\n```json\r\n{\r\n  \"strategy\": \"dom\",\r\n  \"modeClass\": \"dark\",\r\n  \"paletteAttribute\": \"data-palette\"\r\n}\r\n```\r\n\r\n### Authentication\r\n\r\n#### Form-based login\r\n\r\nPawprint fills in a login form, submits it, and saves the browser state for reuse:\r\n\r\n```json\r\n{\r\n  \"strategy\": \"form\",\r\n  \"loginUrl\": \"/login\",\r\n  \"credentials\": {\r\n    \"username\": \"user@example.com\",\r\n    \"password\": \"password\"\r\n  },\r\n  \"selectors\": {\r\n    \"username\": \"input[type=email]\",\r\n    \"password\": \"input[type=password]\",\r\n    \"submit\": \"button[type=submit]\",\r\n    \"authCheck\": \".dashboard-header\"\r\n  }\r\n}\r\n```\r\n\r\nThe saved `storageState.json` is reused for up to 1 hour before re-authenticating.\r\n\r\n#### Reusing storage state\r\n\r\nIf you already have a Playwright storage state file:\r\n\r\n```json\r\n{\r\n  \"strategy\": \"storageState\",\r\n  \"storageStatePath\": \"./storageState.json\"\r\n}\r\n```\r\n\r\n## Output Structure\r\n\r\nEach run creates a timestamped folder to avoid overwriting previous results:\r\n\r\n```\r\npawprint-output/\r\n  my-app/\r\n    2026-02-16/\r\n      2026-02-16_14-30-45/\r\n        default/\r\n          light/\r\n            home-desktop.png\r\n            home-mobile.png\r\n            dashboard-desktop.png\r\n          dark/\r\n            home-desktop.png\r\n            home-mobile.png\r\n            dashboard-desktop.png\r\n        high-contrast/\r\n          light/\r\n            ...\r\n          dark/\r\n            ...\r\n        metadata.json\r\n```\r\n\r\n### Screenshot Naming\r\n\r\n| Route | File Name |\r\n|---|---|\r\n| `/` | `home-{viewport}.png` |\r\n| `/dashboard` | `dashboard-{viewport}.png` |\r\n| `/user/profile` | `user_profile-{viewport}.png` |\r\n| (auth error) | `ERROR-{route}-{viewport}.png` |\r\n| (redirect to login) | `REDIRECTED-{route}-{viewport}.png` |\r\n\r\n### Metadata\r\n\r\nEach run writes a `metadata.json` with:\r\n\r\n```json\r\n{\r\n  \"timestamp\": \"2026-02-16T14:30:45.000Z\",\r\n  \"appPath\": \"/path/to/app\",\r\n  \"config\": { \"baseUrl\": \"...\", \"routes\": [...], \"modes\": [...] },\r\n  \"outputPath\": \"pawprint-output/my-app/2026-02-16/2026-02-16_14-30-45\",\r\n  \"stats\": {\r\n    \"totalScreenshots\": 24,\r\n    \"successful\": 24,\r\n    \"failed\": 0\r\n  }\r\n}\r\n```\r\n\r\n## Programmatic Usage\r\n\r\nPawprint can be imported and used from another Node.js application instead of the CLI.\r\n\r\n### Basic Usage\r\n\r\n```typescript\r\nimport { loadConfig } from \"pawprint/dist/core/config.js\";\r\nimport { prepareOutputFolders, takeScreenshots } from \"pawprint/dist/core/engine.js\";\r\n\r\nconst appPath = \"/path/to/your-app\";\r\nconst config = loadConfig(appPath);\r\n\r\nconst folderMap = prepareOutputFolders(config, appPath);\r\nawait takeScreenshots(config, appPath, folderMap);\r\n```\r\n\r\n### With Inline Config\r\n\r\n```typescript\r\nimport type { PawprintConfig } from \"pawprint/dist/types/config.js\";\r\nimport { prepareOutputFolders, takeScreenshots } from \"pawprint/dist/core/engine.js\";\r\n\r\nconst config: PawprintConfig = {\r\n  baseUrl: \"http://localhost:3000\",\r\n  routes: [\"/\", \"/dashboard\"],\r\n  modes: [\"light\", \"dark\"],\r\n  appearance: {\r\n    strategy: \"localStorage\",\r\n    modeKey: \"theme\",\r\n  },\r\n};\r\n\r\nconst appPath = \"/path/to/your-app\";\r\nconst folderMap = prepareOutputFolders(config, appPath);\r\nawait takeScreenshots(config, appPath, folderMap);\r\n```\r\n\r\n### Available Exports\r\n\r\n| Module | Exports | Description |\r\n|---|---|---|\r\n| `core/config.js` | `loadConfig(appPath)` | Load and validate `pawprint.config.json` from a directory |\r\n| `core/engine.js` | `prepareOutputFolders(config, appPath)` | Create the timestamped output folder structure, returns a folder map |\r\n| `core/engine.js` | `takeScreenshots(config, appPath, folderMap)` | Launch Chromium and capture all screenshots |\r\n| `core/auth.js` | `setupAuth(context, page, config)` | Run authentication against a Playwright browser context |\r\n| `types/config.js` | `PawprintConfig`, `AuthStrategy`, `AppearanceStrategy`, `ViewportPreset`, `ScreenshotMetadata`, `FolderMap` | TypeScript interfaces |\r\n\r\n### Linking as a Local Dependency\r\n\r\nFrom another project:\r\n\r\n```bash\r\n# In the pawprint directory\r\nnpm link\r\n\r\n# In your other project\r\nnpm link pawprint\r\n```\r\n\r\nThen import as shown above.\r\n\r\n## Development\r\n\r\n```bash\r\n# Run CLI directly (no build needed)\r\nnpm start -- generate /path/to/app\r\n\r\n# Watch mode\r\nnpm run dev -- generate /path/to/app\r\n\r\n# Build TypeScript\r\nnpm run build\r\n```\r\n\r\n## Tech Stack\r\n\r\n- **TypeScript** -- ES2022 target, NodeNext modules\r\n- **Playwright** -- Chromium browser automation\r\n- **Commander** -- CLI argument parsing\r\n- **Chalk** -- Terminal colors\r\n\r\n## License\r\n\r\nISC\r\n","readmeFilename":"README.md","_rev":"1-4c20ddc18934fdeeb52b630deb67a70b"}