shots: feature rotate, per-language frame fallback, layout-aware capture selectors

This commit is contained in:
CRTkafa 2026-10-24 13:00:48 +08:00
parent 2e1c7cdc3c
commit 61b5fa0623
15 changed files with 355 additions and 17 deletions

View file

@ -6,6 +6,7 @@
// android-emu adb: demo-mode status bar, per-app language (Android 13+), am start with deep link / extras,
// exec-out screencap. Emulator or a USB device.
// folder bring your own frames: <frames>/<lang>/<phone|tablet>/<name>.png, checked for size.
import { validateSelector } from './selectors';
import type { ShotsCapture, ShotSpec, VideoRecord } from '../lib/config';
import { validateAuth } from './auth';
@ -37,6 +38,10 @@ export function validateCapture(c: ShotsCapture | undefined): string[] {
if (src !== 'folder' && (!c.shots || !Object.keys(c.shots).length)) e.push('shots.capture.shots: at least one shot');
for (const [name, s] of Object.entries(c.shots ?? {})) {
if (!/^[A-Za-z0-9._-]+$/.test(name)) e.push(`shots.capture.shots.${name}: name must be a file name (letters, digits, . _ -)`);
if (src === 'web') {
if (s.waitSelector !== undefined) e.push(...validateSelector(`shots.capture.shots.${name}.waitSelector`, s.waitSelector));
if (s.click !== undefined) for (const [i, c] of (Array.isArray(s.click) && s.click.some(x => typeof x !== 'string') ? s.click : [s.click]).entries()) e.push(...validateSelector(`shots.capture.shots.${name}.click[${i}]`, c));
} else if (s.waitSelector !== undefined || s.click !== undefined) e.push(`shots.capture.shots.${name}: waitSelector / click only apply to source web`);
if (s.navigate && !['spa', 'reload'].includes(s.navigate)) e.push(`shots.capture.shots.${name}.navigate: spa | reload`);
if (s.device && !['phone', 'tablet', 'both'].includes(s.device)) e.push(`shots.capture.shots.${name}.device: phone | tablet | both`);
if (src === 'web' && s.path === undefined) e.push(`shots.capture.shots.${name}.path: URL path (source web)`);