@reshotdev/screenshot 0.0.1-beta.10 → 0.0.1-beta.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +17 -29
- package/package.json +3 -3
- package/src/commands/auth.js +1 -1
- package/src/commands/pull.js +4 -4
- package/src/index.js +2 -38
- package/src/lib/capture-script-runner.js +55 -1
- package/src/lib/config.js +3 -3
- package/src/lib/output-path-template.js +3 -3
- package/src/lib/storage-providers.js +1 -1
- package/src/lib/style-engine.js +5 -5
- package/web/manager/dist/assets/{index-D2qqcFNN.js → index-CvleJUur.js} +1 -1
- package/web/manager/dist/index.html +1 -1
- package/src/commands/ci-run.js +0 -178
- package/src/commands/ci-setup.js +0 -288
package/LICENSE
CHANGED
|
@@ -175,7 +175,7 @@ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
|
175
175
|
|
|
176
176
|
END OF TERMS AND CONDITIONS
|
|
177
177
|
|
|
178
|
-
Copyright 2026
|
|
178
|
+
Copyright 2026 The Plain Works Co., Ltd.
|
|
179
179
|
|
|
180
180
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
181
181
|
you may not use this file except in compliance with the License.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @reshotdev/screenshot
|
|
2
2
|
|
|
3
|
-
Product screenshots in documentation go stale within days of a
|
|
3
|
+
Product screenshots in documentation go stale within days of a UI change. Manually recapturing them across themes, viewports, and locales is tedious and error-prone. This CLI runs screenshot and video capture against a localhost build, comparing each run against the previous capture so teams can review diffs before docs change.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@reshotdev/screenshot)
|
|
6
6
|
[](https://github.com/reshotdev/screenshot/actions/workflows/ci.yml)
|
|
@@ -41,7 +41,7 @@ runtime. Use a production-like local server and see the
|
|
|
41
41
|
|
|
42
42
|
## Certified Targets
|
|
43
43
|
|
|
44
|
-
This release adds a **Certified Targets** contract for apps that need stronger guarantees than ad hoc capture. Certified targets declare their readiness selectors,
|
|
44
|
+
This release adds a **Certified Targets** contract for apps that need stronger guarantees than ad hoc capture. Certified targets declare their readiness selectors, localhost runtime, required routes, and expected published assets in `reshot.config.json`, then pass the full doctor/capture/publish/delivery pipeline before release.
|
|
45
45
|
|
|
46
46
|
## Configuration
|
|
47
47
|
|
|
@@ -120,8 +120,6 @@ Create `reshot.config.json` in your project root:
|
|
|
120
120
|
| `reshot certify` | Run the full certified-target pipeline | `--scenarios`, `--tag`, `--json` |
|
|
121
121
|
| `reshot drifts` | Manage visual drift notifications | `approve`, `reject`, `ignore`, `approve-all` |
|
|
122
122
|
| `reshot import-tests` | Import Playwright tests as scenarios | `--dry-run`, `--no-interactive` |
|
|
123
|
-
| `reshot ci setup` | Generate CI/CD workflow files | — |
|
|
124
|
-
| `reshot ci run` | Capture + publish in one step (CI) | `--tag`, `--no-publish`, `--dry-run` |
|
|
125
123
|
|
|
126
124
|
## Certification Workflow
|
|
127
125
|
|
|
@@ -343,37 +341,27 @@ Set `"format": "summary-video"` in scenario output config to record the full bro
|
|
|
343
341
|
|
|
344
342
|
Crops the screenshot to the bounding box of the selected element.
|
|
345
343
|
|
|
346
|
-
##
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
jobs:
|
|
357
|
-
capture:
|
|
358
|
-
runs-on: ubuntu-latest
|
|
359
|
-
steps:
|
|
360
|
-
- uses: actions/checkout@v4
|
|
361
|
-
- uses: actions/setup-node@v4
|
|
362
|
-
with: { node-version: 20 }
|
|
363
|
-
- run: npm ci
|
|
364
|
-
- run: npm start & # start your app
|
|
365
|
-
- run: npx @reshotdev/screenshot ci run --tag ${{ github.sha }} --message "Deploy ${{ github.sha }}"
|
|
366
|
-
env:
|
|
367
|
-
RESHOT_TOKEN: ${{ secrets.RESHOT_TOKEN }}
|
|
344
|
+
## Automation in Scripts
|
|
345
|
+
|
|
346
|
+
Use headless mode with environment variables to integrate into build scripts or local workflows:
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
# In your Makefile or build script
|
|
350
|
+
export RESHOT_API_KEY=$(cat .reshot/api-key)
|
|
351
|
+
reshot run --scenarios dashboard --no-headless false
|
|
352
|
+
reshot publish --tag v1.2.0
|
|
368
353
|
```
|
|
369
354
|
|
|
370
|
-
|
|
355
|
+
Set `RESHOT_API_KEY` and `RESHOT_PROJECT_ID` to run without interactive auth:
|
|
371
356
|
|
|
372
357
|
```bash
|
|
373
|
-
reshot
|
|
358
|
+
RESHOT_API_KEY=your-key RESHOT_PROJECT_ID=your-project reshot run
|
|
374
359
|
```
|
|
375
360
|
|
|
376
|
-
|
|
361
|
+
For headless execution, ensure:
|
|
362
|
+
- Your app is running on localhost (e.g., `npm run build && npm run start`)
|
|
363
|
+
- `headless: true` is set in `reshot.config.json`
|
|
364
|
+
- API credentials are available as environment variables
|
|
377
365
|
|
|
378
366
|
## Asset Map for Builds
|
|
379
367
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reshotdev/screenshot",
|
|
3
|
-
"version": "0.0.1-beta.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.0.1-beta.12",
|
|
4
|
+
"description": "Screenshot and video capture CLI",
|
|
5
5
|
"author": "Reshot <hello@reshot.dev>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://reshot.dev",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
},
|
|
16
16
|
"keywords": [
|
|
17
17
|
"screenshots",
|
|
18
|
-
"
|
|
18
|
+
"cli",
|
|
19
19
|
"documentation",
|
|
20
20
|
"visual-testing",
|
|
21
21
|
"automation",
|
package/src/commands/auth.js
CHANGED
|
@@ -158,7 +158,7 @@ async function verifyApiKey(apiBaseUrl, apiKey, httpClient = axios) {
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
async function authCommand(options = {}) {
|
|
161
|
-
// Support non-interactive auth via environment variables
|
|
161
|
+
// Support non-interactive auth via environment variables
|
|
162
162
|
const envApiKey = process.env.RESHOT_API_KEY;
|
|
163
163
|
const envProjectId = process.env.RESHOT_PROJECT_ID;
|
|
164
164
|
const httpClient = options.httpClient || axios;
|
package/src/commands/pull.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// pull.js - Pull asset map from Reshot platform
|
|
2
|
-
// Generates JSON, TypeScript, or CSV files for
|
|
2
|
+
// Generates JSON, TypeScript, or CSV files for local workflows
|
|
3
3
|
const chalk = require("chalk");
|
|
4
4
|
const fs = require("fs-extra");
|
|
5
5
|
const path = require("path");
|
|
@@ -493,9 +493,9 @@ async function pullCommand(options = {}) {
|
|
|
493
493
|
console.log(chalk.gray(" • Width/Height - Dimensions in pixels\n"));
|
|
494
494
|
}
|
|
495
495
|
|
|
496
|
-
//
|
|
497
|
-
console.log(chalk.blue("━━━
|
|
498
|
-
console.log(chalk.white("Add to your
|
|
496
|
+
// Workflow integration tip
|
|
497
|
+
console.log(chalk.blue("━━━ Workflow Integration ━━━\n"));
|
|
498
|
+
console.log(chalk.white("Add to your local workflow:\n"));
|
|
499
499
|
console.log(chalk.cyan(" # package.json"));
|
|
500
500
|
console.log(chalk.cyan(' "scripts": {'));
|
|
501
501
|
console.log(chalk.cyan(` "prebuild": "npx reshot pull --format=${format}${output ? ` --output=${output}` : ''}"`));
|
package/src/index.js
CHANGED
|
@@ -329,10 +329,10 @@ program
|
|
|
329
329
|
}
|
|
330
330
|
});
|
|
331
331
|
|
|
332
|
-
// Pull: Generate asset map for
|
|
332
|
+
// Pull: Generate asset map for local workflows
|
|
333
333
|
program
|
|
334
334
|
.command("pull")
|
|
335
|
-
.description("Pull asset map for your
|
|
335
|
+
.description("Pull asset map for your capture workflow")
|
|
336
336
|
.option("-f, --format <format>", "Output format: json, ts, csv", "json")
|
|
337
337
|
.option("-o, --output <path>", "Output file path")
|
|
338
338
|
.option("--full", "Include full metadata in TypeScript output")
|
|
@@ -424,42 +424,6 @@ program
|
|
|
424
424
|
}
|
|
425
425
|
});
|
|
426
426
|
|
|
427
|
-
// CI: CI/CD integration commands
|
|
428
|
-
const ciCommand = program
|
|
429
|
-
.command("ci")
|
|
430
|
-
.description("CI/CD integration commands");
|
|
431
|
-
|
|
432
|
-
ciCommand
|
|
433
|
-
.command("setup")
|
|
434
|
-
.description("Interactive CI/CD setup wizard (GitHub Actions, etc.)")
|
|
435
|
-
.action(async () => {
|
|
436
|
-
try {
|
|
437
|
-
const ciSetup = require("./commands/ci-setup");
|
|
438
|
-
await ciSetup();
|
|
439
|
-
} catch (error) {
|
|
440
|
-
console.error(chalk.red("Error:"), error.message);
|
|
441
|
-
process.exit(1);
|
|
442
|
-
}
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
ciCommand
|
|
446
|
-
.command("run")
|
|
447
|
-
.description("Run capture + publish in one step (CI-optimized)")
|
|
448
|
-
.option("-c, --config <path>", "Path to reshot.config.json")
|
|
449
|
-
.option("--tag <tag>", "Version tag for publish")
|
|
450
|
-
.option("-m, --message <message>", "Commit message for publish")
|
|
451
|
-
.option("--dry-run", "Preview without uploading")
|
|
452
|
-
.option("--no-publish", "Run capture only, skip publish")
|
|
453
|
-
.option("--skip-release-doctor", "Skip the composed release gate precheck")
|
|
454
|
-
.action(async (options) => {
|
|
455
|
-
try {
|
|
456
|
-
const ciRun = require("./commands/ci-run");
|
|
457
|
-
await ciRun(options);
|
|
458
|
-
} catch (error) {
|
|
459
|
-
console.error(chalk.red("Error:"), error.message);
|
|
460
|
-
process.exit(1);
|
|
461
|
-
}
|
|
462
|
-
});
|
|
463
427
|
|
|
464
428
|
// ============================================================================
|
|
465
429
|
// DRIFT MANAGEMENT COMMANDS
|
|
@@ -165,7 +165,61 @@ async function assertForbiddenTextAbsent(page, forbidText = []) {
|
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
const visibleText = normalizeVisibleText(
|
|
168
|
-
await page.evaluate(() =>
|
|
168
|
+
await page.evaluate(() => {
|
|
169
|
+
const isElementVisibleInViewport = (element) => {
|
|
170
|
+
if (!(element instanceof Element)) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const style = window.getComputedStyle(element);
|
|
175
|
+
if (
|
|
176
|
+
style.display === "none" ||
|
|
177
|
+
style.visibility === "hidden" ||
|
|
178
|
+
Number(style.opacity || "1") === 0
|
|
179
|
+
) {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const rect = element.getBoundingClientRect();
|
|
184
|
+
if (rect.width <= 0 || rect.height <= 0) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
rect.bottom > 0 &&
|
|
190
|
+
rect.right > 0 &&
|
|
191
|
+
rect.top < window.innerHeight &&
|
|
192
|
+
rect.left < window.innerWidth
|
|
193
|
+
);
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const walker = document.createTreeWalker(
|
|
197
|
+
document.body,
|
|
198
|
+
NodeFilter.SHOW_TEXT,
|
|
199
|
+
{
|
|
200
|
+
acceptNode(node) {
|
|
201
|
+
const text = node.textContent?.replace(/\s+/g, " ").trim();
|
|
202
|
+
if (!text) {
|
|
203
|
+
return NodeFilter.FILTER_REJECT;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const parent = node.parentElement;
|
|
207
|
+
if (!parent || !isElementVisibleInViewport(parent)) {
|
|
208
|
+
return NodeFilter.FILTER_REJECT;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
const textParts = [];
|
|
217
|
+
while (walker.nextNode()) {
|
|
218
|
+
textParts.push(walker.currentNode.textContent || "");
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return textParts.join(" ");
|
|
222
|
+
}),
|
|
169
223
|
);
|
|
170
224
|
const matched = forbidText.find((candidate) =>
|
|
171
225
|
visibleText.includes(normalizeVisibleText(candidate)),
|
package/src/lib/config.js
CHANGED
|
@@ -650,10 +650,10 @@ function getPrivacyConfig(scenarioOverrides = {}) {
|
|
|
650
650
|
* Default style configuration
|
|
651
651
|
*/
|
|
652
652
|
const DEFAULT_STYLE_CONFIG = {
|
|
653
|
-
enabled:
|
|
653
|
+
enabled: false,
|
|
654
654
|
frame: "none",
|
|
655
|
-
shadow: "
|
|
656
|
-
padding:
|
|
655
|
+
shadow: "none",
|
|
656
|
+
padding: 0,
|
|
657
657
|
background: "transparent",
|
|
658
658
|
borderRadius: 0,
|
|
659
659
|
};
|
|
@@ -54,8 +54,8 @@ const TEMPLATE_PRESETS = {
|
|
|
54
54
|
// GitHub Pages / Static site friendly
|
|
55
55
|
"static-site": "./public/screenshots/{{locale}}/{{scenario}}/{{name}}.{{ext}}",
|
|
56
56
|
|
|
57
|
-
//
|
|
58
|
-
|
|
57
|
+
// Local CLI artifacts
|
|
58
|
+
cli: "./artifacts/screenshots/{{scenario}}/{{viewport}}/{{variant}}/{{name}}.{{ext}}",
|
|
59
59
|
};
|
|
60
60
|
|
|
61
61
|
/**
|
|
@@ -291,7 +291,7 @@ function getTemplatePresets() {
|
|
|
291
291
|
{ name: "docs", template: TEMPLATE_PRESETS.docs, description: "Simple docs asset structure" },
|
|
292
292
|
{ name: "variant-matrix", template: TEMPLATE_PRESETS["variant-matrix"], description: "Hierarchical by variant dimensions" },
|
|
293
293
|
{ name: "static-site", template: TEMPLATE_PRESETS["static-site"], description: "GitHub Pages / static site friendly" },
|
|
294
|
-
{ name: "
|
|
294
|
+
{ name: "cli", template: TEMPLATE_PRESETS.cli, description: "Local CLI artifact organization" },
|
|
295
295
|
];
|
|
296
296
|
}
|
|
297
297
|
|
|
@@ -191,7 +191,7 @@ Use Reshot for full governance features (review queue, version control, etc.):
|
|
|
191
191
|
1. ${chalk.yellow('Authenticate:')}
|
|
192
192
|
${chalk.gray('reshot auth')}
|
|
193
193
|
|
|
194
|
-
2. ${chalk.yellow('Or set environment variable
|
|
194
|
+
2. ${chalk.yellow('Or set environment variable:')}
|
|
195
195
|
${chalk.gray('export RESHOT_API_KEY="your-api-key"')}
|
|
196
196
|
|
|
197
197
|
3. ${chalk.yellow('Config (optional):')}
|
package/src/lib/style-engine.js
CHANGED
|
@@ -16,10 +16,10 @@ try {
|
|
|
16
16
|
* Default style configuration
|
|
17
17
|
*/
|
|
18
18
|
const DEFAULT_STYLE_CONFIG = {
|
|
19
|
-
enabled:
|
|
19
|
+
enabled: false,
|
|
20
20
|
frame: "none",
|
|
21
|
-
shadow: "
|
|
22
|
-
padding:
|
|
21
|
+
shadow: "none",
|
|
22
|
+
padding: 0,
|
|
23
23
|
background: "transparent",
|
|
24
24
|
borderRadius: 0,
|
|
25
25
|
};
|
|
@@ -479,8 +479,8 @@ async function applyStyle(inputBuffer, styleConfig, logger, dpr = 1) {
|
|
|
479
479
|
|
|
480
480
|
const {
|
|
481
481
|
frame = "none",
|
|
482
|
-
shadow = "
|
|
483
|
-
padding =
|
|
482
|
+
shadow = "none",
|
|
483
|
+
padding = 0,
|
|
484
484
|
background = "transparent",
|
|
485
485
|
borderRadius = 0,
|
|
486
486
|
} = styleConfig;
|