capture-flow 0.1.0 → 0.1.1
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/README.md
CHANGED
|
@@ -25,6 +25,24 @@ npm install --save-dev playwright
|
|
|
25
25
|
npx playwright install chromium
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
## Install as an agent skill
|
|
29
|
+
|
|
30
|
+
The repository ships a `capture-flow` skill so coding agents know the manifest
|
|
31
|
+
contract, capture rules, and failure fixes without reading this README first.
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx skills add adelpro/capture-flow
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Add `-g` for a global install, or `-a claude-code -a codex -a opencode` to target
|
|
38
|
+
specific agents. The skill also ships inside the npm package, so a local install works:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx skills add ./node_modules/capture-flow
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Skill source: [`skills/capture-flow/SKILL.md`](skills/capture-flow/SKILL.md).
|
|
45
|
+
|
|
28
46
|
## Quick start
|
|
29
47
|
|
|
30
48
|
A **flow** describes how to capture. A **manifest** describes how to frame it.
|
|
@@ -130,7 +148,7 @@ Because steps are plain commands, a native flow is just as natural as a web one:
|
|
|
130
148
|
| `slides[].templateSlide` | no | Which template slide layout to use (defaults to the slide's index). |
|
|
131
149
|
| `slides[].captures` | yes | Device family → image path. |
|
|
132
150
|
|
|
133
|
-
Device families: `iphone`, `iphone-landscape`, `ipad`, `ipad-landscape`, `android-phone`, `android-landscape`, `android-tablet`, `mac`, `watch`.
|
|
151
|
+
Device families: `iphone`, `iphone-landscape`, `ipad`, `ipad-landscape`, `android-phone`, `android-landscape`, `android-tablet`, `mac`, `watch`, `apple-tv`, `vision`, `play-feature`.
|
|
134
152
|
|
|
135
153
|
Store sizes: `ios-6.9`, `ios-6.9-b`, `ios-6.5`, `ios-6.5-b`, `ios-6.3`, `ios-6.1`, `ipad-13`, `ipad-12.9`, `ipad-11`, `play-phone`, `play-phone-hd`, `play-tablet-7`, `play-tablet-10`, plus the corresponding `-land` ids. Run `build` with an unknown id to get the full list in the error.
|
|
136
154
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "capture-flow",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Turn raw simulator and browser captures into App Store and Google Play screenshots.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
"files": [
|
|
32
32
|
"bin",
|
|
33
33
|
"src",
|
|
34
|
-
"vendor"
|
|
34
|
+
"vendor",
|
|
35
|
+
"skills"
|
|
35
36
|
],
|
|
36
37
|
"scripts": {
|
|
37
38
|
"build": "tsx src/cli.ts build",
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: capture-flow
|
|
3
|
+
description: Turn raw device or browser captures into App Store and Google Play screenshot sets. Use when asked to generate, polish, resize, frame, or validate store screenshots, to add headlines and device frames to app screens, or to set up a repeatable screenshot pipeline with Playwright, simctl, adb, or Maestro.
|
|
4
|
+
metadata:
|
|
5
|
+
author: adelpro
|
|
6
|
+
version: "0.1.1"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# capture-flow
|
|
10
|
+
|
|
11
|
+
`capture-flow` is a CLI that takes plain screenshots plus a small JSON manifest and
|
|
12
|
+
renders exact-size store assets with device frames and composed copy.
|
|
13
|
+
|
|
14
|
+
It has three stages:
|
|
15
|
+
|
|
16
|
+
1. **capture** — run your own screenshot commands (Playwright, `simctl`, `adb`, Maestro, anything).
|
|
17
|
+
2. **build** — map each raw capture to a slide and render every store size.
|
|
18
|
+
3. **verify** — check that every PNG is the exact size, 8-bit RGB, and has no alpha.
|
|
19
|
+
|
|
20
|
+
Do not invent your own resizing pipeline. Capture at native resolution, let `build` do the rest.
|
|
21
|
+
|
|
22
|
+
## Setup
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install --save-dev capture-flow
|
|
26
|
+
npx capture-flow --help
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Node.js **22.18+** is required. Playwright, Xcode, or Android tools are only needed for
|
|
30
|
+
the capture stage; `build` and `verify` run on Node alone.
|
|
31
|
+
|
|
32
|
+
If the project already has `capture-flow` in `package.json`, use `npx capture-flow`
|
|
33
|
+
or the local `node_modules/.bin/capture-flow`.
|
|
34
|
+
|
|
35
|
+
## Workflow
|
|
36
|
+
|
|
37
|
+
1. **Decide the target set** — device families, store sizes, languages, and slide count.
|
|
38
|
+
2. **Write a capture command** that saves full-resolution PNGs into a folder.
|
|
39
|
+
3. **Write `flow.json`** to run that command (or run it manually first).
|
|
40
|
+
4. **Write `shots.json`** mapping captures to slides.
|
|
41
|
+
5. **Run** everything and read the warnings.
|
|
42
|
+
6. **Fix** overflow / missing-screen warnings, rebuild.
|
|
43
|
+
7. **Verify** and inspect the first image of each output folder.
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npx capture-flow run --flow flow.json
|
|
47
|
+
npx capture-flow verify --dir store-assets
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Use `--json` on `build`, `run`, or `verify` when another program consumes the result.
|
|
51
|
+
|
|
52
|
+
## Capture rules
|
|
53
|
+
|
|
54
|
+
These prevent the most common bad output.
|
|
55
|
+
|
|
56
|
+
- Capture at the device's **native pixel size**, never upscale. The renderer can scale
|
|
57
|
+
down but cannot invent detail.
|
|
58
|
+
- Match the frame's screen aspect ratio. A mismatched capture is cropped or distorted.
|
|
59
|
+
- No transparency. `verify` rejects alpha; use an opaque background when capturing.
|
|
60
|
+
- One file per screen per family. Name them predictably (`iphone-01.png`, `android-01.png`, …).
|
|
61
|
+
- Hide dev tooling, cursors, toasts, cookie banners, and consent dialogs before the shot.
|
|
62
|
+
- Wait for real content (fonts, data, animation end) before `screenshot()`. A skeleton
|
|
63
|
+
or empty state is not a store screenshot.
|
|
64
|
+
- Keep text in screenshots readable at thumbnail size; avoid dense tables.
|
|
65
|
+
|
|
66
|
+
Reference dimensions (portrait):
|
|
67
|
+
|
|
68
|
+
| Family | Capture at | Typical CSS viewport |
|
|
69
|
+
| --- | --- | --- |
|
|
70
|
+
| `iphone` | 1320 × 2868 | 440 × 956 @3 |
|
|
71
|
+
| `ipad` | 2064 × 2752 | 768 × 1024 @2.6875 |
|
|
72
|
+
| `android-phone` | 1080 × 1920 or higher (9:16) | 360 × 640 @3 |
|
|
73
|
+
| `android-tablet` | 1600 × 2560 (16:10) | 800 × 1280 @2 |
|
|
74
|
+
| `mac` | 2880 × 1800 | — |
|
|
75
|
+
|
|
76
|
+
Native capture recipes for Playwright, iOS Simulator, and Android are in
|
|
77
|
+
[references/capture-recipes.md](references/capture-recipes.md).
|
|
78
|
+
|
|
79
|
+
## flow.json
|
|
80
|
+
|
|
81
|
+
A flow is an ordered list of shell commands. Paths are relative to the flow file.
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"manifest": "./shots.json",
|
|
86
|
+
"out": "./store-assets",
|
|
87
|
+
"captures": [
|
|
88
|
+
{ "name": "web (Playwright)", "command": "node capture.mjs" },
|
|
89
|
+
{ "name": "iOS", "command": "xcrun simctl io booted screenshot captures/ios-01.png" }
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
| Field | Required | Meaning |
|
|
95
|
+
| --- | --- | --- |
|
|
96
|
+
| `captures[].command` | yes | Shell command, run with the system shell. |
|
|
97
|
+
| `captures[].name` | no | Label in the log. |
|
|
98
|
+
| `captures[].cwd` | no | Working directory, relative to the flow file. |
|
|
99
|
+
| `captures[].env` | no | Extra environment variables for the step. |
|
|
100
|
+
| `manifest` | no | Manifest used by `run`. |
|
|
101
|
+
| `out` | no | Output directory used by `run`. |
|
|
102
|
+
|
|
103
|
+
Steps stop at the first non-zero exit code. Use `capture-flow capture --flow flow.json`
|
|
104
|
+
to run only the capture stage.
|
|
105
|
+
|
|
106
|
+
## shots.json
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"app": "My App",
|
|
111
|
+
"template": "noor",
|
|
112
|
+
"languages": ["ar-SA"],
|
|
113
|
+
"sizes": ["ios-6.9", "ios-6.3", "ipad-13", "play-phone", "play-tablet-10"],
|
|
114
|
+
"slides": [
|
|
115
|
+
{
|
|
116
|
+
"templateSlide": 0,
|
|
117
|
+
"headline": "استمع *بخشوع*",
|
|
118
|
+
"subtitle": "تلاوات كاملة ومؤشر طيفي.",
|
|
119
|
+
"captures": {
|
|
120
|
+
"iphone": "captures/iphone-01.png",
|
|
121
|
+
"android-phone": "captures/android-01.png",
|
|
122
|
+
"ipad": "captures/ipad-01.png",
|
|
123
|
+
"android-tablet": "captures/android-tablet-01.png"
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
| Field | Required | Meaning |
|
|
131
|
+
| --- | --- | --- |
|
|
132
|
+
| `app` | yes | Project name; also used in the report and fastlane paths. |
|
|
133
|
+
| `template` | yes | Built-in template id. |
|
|
134
|
+
| `languages` | yes | Store locales, e.g. `["en-US", "ar-SA"]`. `ar-SA` exports as `ar` on Google Play. |
|
|
135
|
+
| `slides` | yes | Ordered slides; each maps device family → capture. |
|
|
136
|
+
| `out` | no | Output directory, relative to the manifest. Default `store-assets`. |
|
|
137
|
+
| `structure` | no | `stores` (default), `flat`, or `fastlane`. |
|
|
138
|
+
| `sizes` | no | Subset of size ids; defaults to the default size of every family used. |
|
|
139
|
+
| `slides[].headline` | yes | `*word*` marks the accent run; `\n` is a hard break. |
|
|
140
|
+
| `slides[].subtitle` | no | Secondary copy. |
|
|
141
|
+
| `slides[].templateSlide` | no | Which template layout to use; defaults to the slide index. |
|
|
142
|
+
| `slides[].captures` | yes | Device family → image path (`png`, `jpg`, `jpeg`, `webp`). |
|
|
143
|
+
|
|
144
|
+
Families: `iphone`, `iphone-landscape`, `ipad`, `ipad-landscape`, `android-phone`,
|
|
145
|
+
`android-landscape`, `android-tablet`, `mac`, `watch`, `apple-tv`, `vision`, `play-feature`.
|
|
146
|
+
|
|
147
|
+
Store limits: App Store accepts up to 10 slides per size, Google Play up to 8.
|
|
148
|
+
Respect them before designing a long story.
|
|
149
|
+
|
|
150
|
+
Store sizes: `ios-6.9`, `ios-6.9-b`, `ios-6.5`, `ios-6.5-b`, `ios-6.3`, `ios-6.1`,
|
|
151
|
+
`ipad-13`, `ipad-12.9`, `ipad-11`, `play-phone`, `play-phone-hd`, `play-tablet-7`,
|
|
152
|
+
`play-tablet-10`, plus `-land` variants. Full pixel dimensions are in
|
|
153
|
+
[references/store-sizes.md](references/store-sizes.md).
|
|
154
|
+
|
|
155
|
+
Every slide must supply every family you want in the output. A slide without a family
|
|
156
|
+
produces a placeholder warning and an image with an empty frame — treat that as a bug.
|
|
157
|
+
|
|
158
|
+
## Choosing a template
|
|
159
|
+
|
|
160
|
+
| Category | Templates |
|
|
161
|
+
| --- | --- |
|
|
162
|
+
| `elegant` | `desert-dune`, `noir-gold`, `paper-and-ink` |
|
|
163
|
+
| `dark` | `midnight-neon`, `noor`, `obsidian` |
|
|
164
|
+
| `minimal` | `launchpad`, `mono-swiss`, `sage` |
|
|
165
|
+
| `gradient` | `aurora`, `glass-prism`, `sunset-boulevard` |
|
|
166
|
+
| `playful` | `bubblegum`, `clay-garden` |
|
|
167
|
+
| `bold` | `citrus-pop`, `loud-and-clear`, `split-tone` |
|
|
168
|
+
| `panorama` | `panorama-peaks`, `panorama-ribbon`, `panorama-waves` |
|
|
169
|
+
|
|
170
|
+
- Arabic-first copy: `noor` (Cairo + Noto Sans Arabic, emerald and gold).
|
|
171
|
+
- Native tablet composition: `mono-swiss`, `noir-gold`, `obsidian`, `panorama-*`.
|
|
172
|
+
- Phone-first templates adapt to iPad/Android tablet by swapping the frame and
|
|
173
|
+
reflowing the tall layout. This is acceptable but less deliberate.
|
|
174
|
+
|
|
175
|
+
A template owns the palette and typefaces. There are deliberately no per-manifest colour
|
|
176
|
+
or font overrides — mixing arbitrary colours into a designed system usually breaks it.
|
|
177
|
+
To customize, copy a template from `node_modules/capture-flow/vendor/wajha/templates/builtin/`,
|
|
178
|
+
edit its `theme`/`typeset`, and register it in your own build. Ask the user before
|
|
179
|
+
forking a template.
|
|
180
|
+
|
|
181
|
+
`templateSlide` selects a composition (hero, tilted, pair, hanging, …). Layouts differ
|
|
182
|
+
per template. To see the available layouts and their default copy, read the template
|
|
183
|
+
file:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
sed -n '/slides: \[/,/^ ]/p' node_modules/capture-flow/vendor/wajha/templates/builtin/noor.ts
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Out-of-range values fail with `"<template>" has <n>` — that number is the layout count.
|
|
190
|
+
|
|
191
|
+
## Output
|
|
192
|
+
|
|
193
|
+
The default `stores` structure is store-upload ready:
|
|
194
|
+
|
|
195
|
+
```text
|
|
196
|
+
store-assets/
|
|
197
|
+
App Store/
|
|
198
|
+
ar-SA/
|
|
199
|
+
iPhone 6.9/01.png … 10.png
|
|
200
|
+
iPhone 6.3/01.png …
|
|
201
|
+
iPad 13/01.png …
|
|
202
|
+
Google Play/
|
|
203
|
+
ar/
|
|
204
|
+
Phone/01.png …
|
|
205
|
+
Tablet 10/01.png …
|
|
206
|
+
capture-flow-report.json
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
`capture-flow-report.json` lists every file with its `width`, `height`, `family`,
|
|
210
|
+
`lang`, and `slide`, plus `warnings`. Read it instead of guessing what was produced.
|
|
211
|
+
|
|
212
|
+
`flat` writes `01-iphone-en-US.png`-style names; `fastlane` writes
|
|
213
|
+
`fastlane/screenshots/<locale>/`.
|
|
214
|
+
|
|
215
|
+
## Warnings and failures
|
|
216
|
+
|
|
217
|
+
| Symptom | Fix |
|
|
218
|
+
| --- | --- |
|
|
219
|
+
| `text overflows at WxH` | Shorten copy, remove a line break, use a different `templateSlide`, or pick a template with more copy room. |
|
|
220
|
+
| `uses a placeholder screen` | That slide has no capture for the family. Add one or drop the family/size. |
|
|
221
|
+
| `has no screenshot` | Slide → family mapping is incomplete for an exported size. |
|
|
222
|
+
| `Unknown device family` | Use a family from the list above. |
|
|
223
|
+
| `Unknown export size` | Use a size id from the table; `-land` is a suffix, not a family. |
|
|
224
|
+
| `capture not found` | Manifest paths are relative to the manifest file, not the terminal's cwd. |
|
|
225
|
+
| `verify` alpha error | Flatten the capture onto an opaque background; PNG must be 8-bit RGB. |
|
|
226
|
+
| Wrong dimensions in `verify` | Do not post-process built files; re-run `build` for that size. |
|
|
227
|
+
|
|
228
|
+
## Library use
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
import { build, verify, runCaptureFlow } from 'capture-flow';
|
|
232
|
+
|
|
233
|
+
await runCaptureFlow('flow.json');
|
|
234
|
+
const report = await build({ manifestPath: 'shots.json', outDir: 'store-assets' });
|
|
235
|
+
const result = verify('store-assets');
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
The package ships TypeScript, so run it through `tsx` or a bundler.
|
|
239
|
+
|
|
240
|
+
## Example
|
|
241
|
+
|
|
242
|
+
A complete, real setup (Playwright capture of a web app, Arabic RTL, five device
|
|
243
|
+
sizes, 20 output files) lives in the repository:
|
|
244
|
+
|
|
245
|
+
<https://github.com/adelpro/capture-flow/tree/main/examples/open-tilawa>
|
|
246
|
+
|
|
247
|
+
It shows consent-dialog dismissal, waiting for hydration, live audio state, and the
|
|
248
|
+
`عرض الكل` reciter flow. Adapt it rather than starting from a blank page.
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Capture recipes
|
|
2
|
+
|
|
3
|
+
`capture-flow` never takes screenshots itself. Each flow step is a shell command, so
|
|
4
|
+
use whatever produces correct native-resolution images.
|
|
5
|
+
|
|
6
|
+
Before building, check every raw capture:
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
magick identify -format '%f %wx%h %[channels]\n' captures/*.png
|
|
10
|
+
# macOS without ImageMagick:
|
|
11
|
+
sips -g pixelWidth -g pixelHeight captures/iphone-01.png
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The output must be opaque (no alpha). Flatten if needed:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
magick in.png -background '#000000' -alpha remove -alpha off out.png
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Web app with Playwright
|
|
21
|
+
|
|
22
|
+
Install Playwright outside the project if you do not want it in dependencies:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install --prefix /tmp/pw playwright
|
|
26
|
+
PLAYWRIGHT_BROWSERS_PATH=/tmp/pw npx --prefix /tmp/pw playwright install chromium
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`capture.mjs`:
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
import { chromium } from 'playwright';
|
|
33
|
+
|
|
34
|
+
const browser = await chromium.launch();
|
|
35
|
+
const context = await browser.newContext({
|
|
36
|
+
viewport: { width: 440, height: 956 },
|
|
37
|
+
deviceScaleFactor: 3, // 440×956 → 1320×2868
|
|
38
|
+
isMobile: true,
|
|
39
|
+
hasTouch: true,
|
|
40
|
+
locale: 'ar-SA',
|
|
41
|
+
timezoneId: 'Asia/Riyadh',
|
|
42
|
+
colorScheme: 'dark',
|
|
43
|
+
serviceWorkers: 'block', // stale caches hide new UI
|
|
44
|
+
});
|
|
45
|
+
const page = await context.newPage();
|
|
46
|
+
|
|
47
|
+
// Seed settings before the app boots (consent, theme, onboarding).
|
|
48
|
+
await context.addInitScript(() => {
|
|
49
|
+
localStorage.setItem('app:telemetry', 'off');
|
|
50
|
+
localStorage.setItem('app:onboarded', 'true');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
await page.goto('https://example.com', { waitUntil: 'commit', timeout: 120_000 });
|
|
54
|
+
await page.getByRole('button', { name: 'تشغيل' }).waitFor({ timeout: 120_000 });
|
|
55
|
+
await page.waitForTimeout(700); // let fonts, audio state, and animation settle
|
|
56
|
+
|
|
57
|
+
await page.screenshot({ path: 'captures/iphone-01.png' });
|
|
58
|
+
await browser.close();
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Rules of thumb:
|
|
62
|
+
|
|
63
|
+
- `waitUntil: 'commit'` plus an explicit UI wait beats `networkidle` on hydrated apps.
|
|
64
|
+
- Hydration on a cold CDN can take 20 s. Give `waitFor` a real timeout.
|
|
65
|
+
- Take one browser context per family, or resize the viewport and repeat.
|
|
66
|
+
- Keep `omitBackground` off; transparency fails `verify`.
|
|
67
|
+
- If a consent dialog appears, dismiss it once with a real click **and** seed the
|
|
68
|
+
setting, so retries stay clean.
|
|
69
|
+
- For audio/video states, start playback and wait for the visible time counter to move.
|
|
70
|
+
|
|
71
|
+
### When the app depends on a service worker or peer transport
|
|
72
|
+
|
|
73
|
+
If playback in the browser uses a transport the headless run cannot provide (WebTorrent,
|
|
74
|
+
WebRTC), point the request at a local/web seed instead of screenshotting an empty player:
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
await context.route('**/webtorrent/**', async (route) => {
|
|
78
|
+
const url = new URL(route.request().url());
|
|
79
|
+
await route.continue({ url: url.toString().replace('/webtorrent/', '/media/') });
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Document the fallback in the capture script so the next person knows why it exists.
|
|
84
|
+
|
|
85
|
+
## iOS Simulator
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
xcrun simctl boot "iPhone 17 Pro Max" 2>/dev/null || true
|
|
89
|
+
xcrun simctl bootstatus booted -b
|
|
90
|
+
|
|
91
|
+
# Deterministic status bar.
|
|
92
|
+
xcrun simctl status_bar booted override \
|
|
93
|
+
--time "9:41" --batteryState charged --batteryLevel 100 \
|
|
94
|
+
--wifiBars 3 --cellularBars 4
|
|
95
|
+
|
|
96
|
+
# Drive the app: deep link, Maestro, or XCTest.
|
|
97
|
+
xcrun simctl openurl booted "myapp://player"
|
|
98
|
+
|
|
99
|
+
xcrun simctl io booted screenshot captures/iphone-01.png
|
|
100
|
+
xcrun simctl status_bar booted clear
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`simctl io screenshot` captures at the simulated device's native pixel size. Pick a
|
|
104
|
+
device whose native resolution matches the size you export (iPhone 17 Pro Max →
|
|
105
|
+
1320 × 2868). For iPad, boot an iPad Pro 13″ simulator.
|
|
106
|
+
|
|
107
|
+
## Android emulator or device
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
adb shell screencap -p /sdcard/shot.png
|
|
111
|
+
adb pull /sdcard/shot.png captures/android-01.png
|
|
112
|
+
# or, without a temp file:
|
|
113
|
+
adb exec-out screencap -p > captures/android-01.png
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Deterministic status bar:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
adb shell settings put global sysui_demo_allowed 1
|
|
120
|
+
adb shell am broadcast -a com.android.systemui.demo -e command enter
|
|
121
|
+
adb shell am broadcast -a com.android.systemui.demo -e command clock -e hhmm 0941
|
|
122
|
+
adb shell am broadcast -a com.android.systemui.demo -e command battery -e level 100 -e plugged false
|
|
123
|
+
adb shell am broadcast -a com.android.systemui.demo -e command network -e wifi show -e level 4
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Use an emulator profile with the right resolution (1080 × 2400 for a modern phone,
|
|
127
|
+
1600 × 2560 for a 10-inch tablet in landscape) and set the locale:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
adb shell "setprop persist.sys.locale ar-SA; setprop persist.sys.language ar; setprop persist.sys.country SA"
|
|
131
|
+
adb shell am force-stop com.example.app
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Maestro
|
|
135
|
+
|
|
136
|
+
```yaml
|
|
137
|
+
appId: com.example.app
|
|
138
|
+
---
|
|
139
|
+
- launchApp:
|
|
140
|
+
clearState: false
|
|
141
|
+
- tapOn: "Get started"
|
|
142
|
+
- takeScreenshot: captures/android-01
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`takeScreenshot` writes `<name>.png` in the working directory of the Maestro run.
|
|
146
|
+
|
|
147
|
+
## Native app deep links
|
|
148
|
+
|
|
149
|
+
Prefer a deep link or launch argument over scripted tapping: it is stable across UI
|
|
150
|
+
changes and skips onboarding.
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
xcrun simctl openurl booted "myapp://track/123?autoplay=1"
|
|
154
|
+
adb shell am start -a android.intent.action.VIEW -d "myapp://track/123?autoplay=1" com.example.app
|
|
155
|
+
```
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Store sizes
|
|
2
|
+
|
|
3
|
+
Pixel sizes exported by `capture-flow build`. `default` means the size is produced
|
|
4
|
+
when `sizes` is omitted and the family has a capture. `required` marks the size the
|
|
5
|
+
store currently requires.
|
|
6
|
+
|
|
7
|
+
## App Store — iPhone
|
|
8
|
+
|
|
9
|
+
| id | pixels | devices | default | required |
|
|
10
|
+
| --- | --- | --- | --- | --- |
|
|
11
|
+
| `ios-6.9` | 1320 × 2868 | iPhone 17 Pro Max, 16 Pro Max | yes | yes |
|
|
12
|
+
| `ios-6.9-b` | 1290 × 2796 | iPhone 16 Plus, 15 Pro Max, 15 Plus, 14 Pro Max | no | no |
|
|
13
|
+
| `ios-6.5` | 1284 × 2778 | iPhone 14 Plus, 13 Pro Max, 12 Pro Max | no | no |
|
|
14
|
+
| `ios-6.5-b` | 1242 × 2688 | iPhone 11 Pro Max, XS Max | no | no |
|
|
15
|
+
| `ios-6.3` | 1206 × 2622 | iPhone 17 Pro, 17, 16 Pro | yes | yes |
|
|
16
|
+
| `ios-6.1` | 1170 × 2532 | iPhone 17e, 16e, 14, 13, 13 Pro, 12, 12 Pro | no | no |
|
|
17
|
+
|
|
18
|
+
## App Store — iPad
|
|
19
|
+
|
|
20
|
+
| id | pixels | devices | default | required |
|
|
21
|
+
| --- | --- | --- | --- | --- |
|
|
22
|
+
| `ipad-13` | 2064 × 2752 | iPad Pro 13″ (M4, M5), iPad Air 13″ | yes | yes |
|
|
23
|
+
| `ipad-12.9` | 2048 × 2732 | iPad Pro 12.9″ (2nd–6th gen) | no | no |
|
|
24
|
+
| `ipad-11` | 1668 × 2420 | iPad Pro 11″ (M4), iPad Air 11″ | no | no |
|
|
25
|
+
|
|
26
|
+
## Google Play — phone
|
|
27
|
+
|
|
28
|
+
| id | pixels | devices | default | required |
|
|
29
|
+
| --- | --- | --- | --- | --- |
|
|
30
|
+
| `play-phone` | 1080 × 1920 | all Android phones (9:16) | yes | yes |
|
|
31
|
+
| `play-phone-hd` | 1440 × 2560 | all Android phones (9:16, high resolution) | no | no |
|
|
32
|
+
|
|
33
|
+
## Google Play — tablet
|
|
34
|
+
|
|
35
|
+
| id | pixels | devices | default | required |
|
|
36
|
+
| --- | --- | --- | --- | --- |
|
|
37
|
+
| `play-tablet-7` | 1080 × 1920 | 7-inch Android tablets | yes | no |
|
|
38
|
+
| `play-tablet-10` | 1440 × 2560 | 10-inch Android tablets | yes | no |
|
|
39
|
+
|
|
40
|
+
## App Store — Mac
|
|
41
|
+
|
|
42
|
+
| id | pixels | default | required |
|
|
43
|
+
| --- | --- | --- | --- |
|
|
44
|
+
| `mac-2880` | 2880 × 1800 | yes | yes |
|
|
45
|
+
| `mac-2560` | 2560 × 1600 | no | no |
|
|
46
|
+
| `mac-1440` | 1440 × 900 | no | no |
|
|
47
|
+
| `mac-1280` | 1280 × 800 | no | no |
|
|
48
|
+
|
|
49
|
+
## Apple Watch
|
|
50
|
+
|
|
51
|
+
| id | pixels | default | required |
|
|
52
|
+
| --- | --- | --- | --- |
|
|
53
|
+
| `watch-ultra-3` | 422 × 514 | yes | yes |
|
|
54
|
+
| `watch-11` | 416 × 496 | no | no |
|
|
55
|
+
| `watch-ultra-2` | 410 × 502 | no | no |
|
|
56
|
+
| `watch-9` | 396 × 484 | no | no |
|
|
57
|
+
| `watch-6` | 368 × 448 | no | no |
|
|
58
|
+
| `watch-3` | 312 × 390 | no | no |
|
|
59
|
+
|
|
60
|
+
## Apple TV and Vision Pro
|
|
61
|
+
|
|
62
|
+
| id | pixels | default | required |
|
|
63
|
+
| --- | --- | --- | --- |
|
|
64
|
+
| `tv-4k` | 3840 × 2160 | yes | yes |
|
|
65
|
+
| `tv-hd` | 1920 × 1080 | no | no |
|
|
66
|
+
| `vision-pro` | 3840 × 2160 | yes | yes |
|
|
67
|
+
|
|
68
|
+
## Google Play feature graphic
|
|
69
|
+
|
|
70
|
+
| id | pixels | default | required |
|
|
71
|
+
| --- | --- | --- | --- |
|
|
72
|
+
| `play-feature` | 1024 × 500 | yes | yes |
|
|
73
|
+
|
|
74
|
+
## Landscape
|
|
75
|
+
|
|
76
|
+
Append `-land` and swap width and height. Supported for `iphone*`, `ipad*`, and
|
|
77
|
+
`play-phone*` sizes (for example `ios-6.9-land` = 2868 × 1320). Use the matching
|
|
78
|
+
`*-landscape` family with the capture taken in landscape.
|
|
79
|
+
|
|
80
|
+
## Capture aspect ratios
|
|
81
|
+
|
|
82
|
+
The device screen area uses these ratios. Match them to avoid cropping:
|
|
83
|
+
|
|
84
|
+
| Family | Screen aspect (w:h) | Notes |
|
|
85
|
+
| --- | --- | --- |
|
|
86
|
+
| `iphone` | 1320:2868 | 19.5:9, capture at 1320 × 2868 |
|
|
87
|
+
| `ipad` | 3:4 | 2064 × 2752 |
|
|
88
|
+
| `android-phone` | 20:9 | 1080 × 2400 or 1080 × 1920; 9:16 export crops top/bottom |
|
|
89
|
+
| `android-tablet` | 16:10 | 1600 × 2560 |
|
|
90
|
+
| `mac` | 16:10 | 2880 × 1800 |
|
|
91
|
+
| `watch` | 422:514 | — |
|