Store assets

Store assets

pekoshots generates App Store screenshots and preview recordings by driving your real app through a scripted sequence and capturing its own webview. There is no screen recording and no window chrome in the output.

It is a gated package, and it is scoped to demo builds so it cannot reach a shipped binary:

[dependencies]
pekoshots = { version = "0.1.0", demo = true }

Attaching

import pekoshots as shots;

fn on_start() {
    let application = ui::app::from_bundle();

    demo {
        shots::driver::attach(application, closure(d: shots::demo::Demo) => void {
            register_demo(d);
        });
    }

    application.run();
}

attach is a no-op unless the demo environment is active, so the same entry ships to production. Wrapping it in a demo { } block goes further and removes the code at compile time, so the dependency never reaches the binary at all.

Authoring a shot

d.device(shots::catalog::iphone_69());

d.shots(closure(s: shots::builder::ShotSet) => void {
    s.screenshot("home", closure(shot: shots::builder::Shot) => void {
        shot.navigate("/")
            .wait_for(sel("#feed"))
            .caption("Everything in one place")
            .capture()
    });
});

The catalog module provides the exact store dimensions: iphone_69, ipad_13, mac_1440x900, play_phone, windows_1080p, and more.

Steps cover navigation (navigate, back, forward), waiting (wait_for, wait_for_gone, wait_for_idle), interaction (click, double_click, hover, type_text, fill, submit, press, scroll_to, drag), cursor control, and labels. Targets are sel("#id") for an element's centre or at("#id", x, y) for a point within it.

The one rule

Wait on conditions, never on time. Machines differ, and a sleep that works on your laptop fails in CI.

shot.click(sel("#save")).wait_for(sel(".saved"))   // right
shot.click(sel("#save")).dwell(500)                // wrong as a gate

dwell(ms) exists, but it is a presentation pause inside a recording, not a control gate. Everything that advances the script waits for a predicate: an element appearing, an element disappearing, or the page going idle. Idle means no in-flight requests and no DOM mutation for a short quiet period.

Verifying without capturing

peko demo
peko demo onboarding --from 2

This builds in demo mode, launches against the dev server, and runs every step, printing each as it passes or fails. It produces no screenshots; it verifies that the scripts navigate, find their elements, and interact as written. That is the loop to iterate in, because a real capture run is slow.

--delay pauses between shots so you can watch. --shot runs one.

Fixtures

A demo module can declare a fixed user, seed data, a frozen clock, and a quiet mode. These are exposed to the app rather than applied by the compiler: the app fetches them at boot and decides what to do with them. That is what makes a screenshot run reproducible instead of showing whatever happened to be in your development database.