perf(vishot): compress and downscale AVIF screenshots (#1598)
* perf(vishot-runner-electron): compress and downscale AVIF screenshots The capture pipeline was emitting AVIF at default quality with no resolution cap, so 4K displays produced ~800KB per screenshot (~42MB total for the tamagotchi manual). Add quality 50, speed 6, and a 1920px max-width downscale — expected output drops to ~20-40KB each with no visible quality loss for UI documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * perf(docs): regenerate manual screenshots with compression Re-capture all 27 tamagotchi manual screenshots using the new AVIF pipeline settings (quality 50, max-width 1920px, speed 6). Before: ~800KB per image, ~42MB total (4K @ default quality) After: ~15-38KB per image, ~1MB total (1920px @ quality 50) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-Authored-By-Agent: Claude Opus 4.6 <noreply@anthropic.com>
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 807 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 755 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 777 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 763 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 806 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 779 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 776 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 757 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 769 KiB After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 773 KiB After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 783 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 773 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 783 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 808 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 788 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 826 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 775 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 790 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 787 KiB After Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 807 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 811 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 772 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 812 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 813 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 807 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 799 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 798 KiB After Width: | Height: | Size: 18 KiB |
@@ -18,6 +18,13 @@ import { resolveElectronAppInfo } from '../utils/app-path'
|
||||
|
||||
type CaptureFormat = 'png' | 'avif'
|
||||
|
||||
/** Max width for AVIF output — anything wider is downscaled proportionally. */
|
||||
const AVIF_MAX_WIDTH = 1920
|
||||
/** AVIF quality (0-100, 100 = lossless). 50 is nearly indistinguishable for UI screenshots. */
|
||||
const AVIF_QUALITY = 50
|
||||
/** rav1e speed preset (1 = slow/best, 10 = fast/worst). 6 balances size vs encode time. */
|
||||
const AVIF_SPEED = 6
|
||||
|
||||
interface CaptureCliArguments {
|
||||
scenarioPath: string
|
||||
outputDir: string
|
||||
@@ -69,7 +76,20 @@ function createAvifTransformer(): ArtifactTransformer {
|
||||
return async (artifact) => {
|
||||
// eslint-disable-next-line e18e/prefer-static-regex
|
||||
const derivedFilePath = artifact.filePath.replace(/\.png$/i, '.avif')
|
||||
const avifBuffer = await new Transformer(await readFile(artifact.filePath)).avif()
|
||||
|
||||
const transformer = new Transformer(await readFile(artifact.filePath))
|
||||
const metadata = await transformer.metadata()
|
||||
|
||||
// Downscale images wider than AVIF_MAX_WIDTH, keeping aspect ratio
|
||||
if (metadata.width > AVIF_MAX_WIDTH) {
|
||||
const scale = AVIF_MAX_WIDTH / metadata.width
|
||||
transformer.resize(AVIF_MAX_WIDTH, Math.round(metadata.height * scale))
|
||||
}
|
||||
|
||||
const avifBuffer = await transformer.avif({
|
||||
quality: AVIF_QUALITY,
|
||||
speed: AVIF_SPEED,
|
||||
})
|
||||
|
||||
await writeFile(derivedFilePath, avifBuffer)
|
||||
await rm(artifact.filePath, { force: true })
|
||||
|
||||