--- name: clone-prototype description: Clone a real app's screens as pixel-accurate, self-contained HTML artboards on the prototype canvas. Overlay a grid on the reference and sample colours visually, derive one measured design-token block, generate one HTML file per screen from a single script, verify by re-rendering, and park the reference underneath its mockup. Use when asked to 100% copy / clone an app's UI, rebuild screens from screenshots or Mobbin, extract a design system from reference images, or check a mockup against its reference. license: Apache-2.0 compatibility: Requires the refkit and artgen commands from super-prototyping-tools, plus Google Chrome for the shoot subcommand. Reference captures are supplied by you; nothing is fetched. --- # Clone prototype Seven phases, in order. **Never skip ahead.** Tokens before HTML, sampling before tokens. Every colour and every metric in the final HTML must trace back to a measurement, not to a guess that "looks about right". The one thing built out of order is Phase 5's reference row: it needs no measurement, so it goes up first (see Phase 5). Output lands in `mockups/canvases//` and the canvas picks it up automatically. The `prototype-canvas` skill covers how folders become tldraw pages and how `layout.json` rows work. Name the folder for the source, e.g. `notion-ios`. Toolkit: `refkit` (grid / sample / bands / bbox / scan / hairline / font / shoot / diff / blend / tokens / batch / ink / crops / key / montage), plus `artgen` for the rare asset that has to be drawn. Both are commands on your PATH; `shoot` additionally needs Google Chrome (on Windows, Edge will do). ```bash refkit --help ``` Not on PATH, or `sp --version` prints something lower than the version this skill shipped with? The plugin cannot install it. Leave a higher version alone, because reinstalling over it would be a downgrade, and the tag may not even exist. Once per machine, adding `--force` to reinstall over a lower version: ```bash uv tool install "git+https://github.com/ReScienceLab/super-prototyping#subdirectory=tools" ``` **Before Phase 1, check the project ignores what a run produces**, once per project: a run writes third-party captures into `ref-*.html` and `assets/refs/`, and committing those is hard to undo. Work in `mockups/canvases//scratch/`. Each rule is checked on its own, and the `**/` matters — a pattern with a slash in the middle only matches at the root: ```bash for p in 'ref-*.html' '**/assets/refs/' 'scratch/'; do grep -qxF "$p" .gitignore 2>/dev/null || echo "$p" >> .gitignore done ``` Then name the board folder once and address everything through it: captures under `$B/assets/refs/`, everything a run derives under `$B/scratch/`. A `-o` without a directory writes into whatever the current directory happens to be, which is the user's project root as often as not: ```bash B=mockups/canvases/ ``` Worked examples and the folder skeleton ship with the plugin, which is installed outside your project. Address them through the kit root: ```bash KIT="$(sp root)" ls "$KIT/mockups/canvases" ``` A folder this skill names but that listing does not show means the plugin was installed sparsely, which is supported. Work from `templates` and carry on; nothing here needs an example to be present. --- ## Phase 0: collect references Get the highest-resolution capture you can; every later measurement is capped by it. - **The user's own screenshot** is usually the authority on *which* screens and *which* scroll state. Save each one as its own crop (`p1.png … pN.png`) in `$B/assets/refs/` before doing anything else. Image caches rotate and the attachment will disappear mid-task. - **Mobbin MCP** (`mcp__mobbin__search_screens`, `search_flows`), when available. Run one search per screen, `platform: "ios"`, and keep `task_intent` **identical** across every call in the run. Describe the screen in plain language *including its literal on-screen strings*; that is what actually matches. Use `exclude_screen_ids` (a JSON array of quoted UUID strings) to push past near-misses you already rejected. Download with `curl -sL `. Results are ~299 × 678 webp, fine for placing on the canvas but **too small to be the only sampling source**. Cite results as markdown links to their `mobbin_url`. - **A native-resolution capture of any screen from the same app** (@2x/@3x, e.g. 1179 × 2556) settles ink, scrim and accent values that a downscaled strip cannot. It does not have to be one of the screens you are cloning. ### Check the colour space before you sample anything A capture straight off a device is often **untagged Display P3**, and every tool in this kit reads raw bytes. Sampled as-is, a P3 capture and an sRGB one of the same screen disagree by 5-10 levels on any saturated colour, and nothing about either looks wrong on its own. Convert to sRGB first, and convert the whole set, so one token cannot end up averaging two spaces. The test is cheap and it is the only thing that finds this: **sample one element that appears in every capture** (a brand mark, an accent button, the page ground) and compare across batches. Values that split into two clusters are two colour spaces, not two colours. The `claude-ios` run had captures 01-07 in sRGB and 08-15 untagged P3; the same brand orange read `#E07A54` on one half and `#D97757` on the other, and the page ground split with it. One `sips`/Pillow conversion pass up front collapses both. Two oranges may still survive the conversion, and then they are real: that run kept `#D97757` for the star mark and `#CB6442` for the send button, on the same screen. Convert first, *then* decide what is one token and what is two. Record the capture scale once, in **capture px per design pt**, and reuse it everywhere: ``` scale = screen_inner_width_px / device_pt_width # e.g. 300 / 393 = 0.7634 ``` Cross-check against height (`649 / 852 = 0.7617`). If the two disagree by more than ~1%, the crop is wrong. Recrop before sampling. --- ## Phase 1: grid on the image, then LOOK **Draw the grid onto the pixels and read the result with your eyes.** That is the rule that makes this work. Sampling coordinates blind produces numbers with no idea which UI element they belong to, and those numbers end up in the wrong token. ```bash refkit grid "$B/assets/refs/p4.png" \ -o "$B/scratch/g04.png" --zoom 3 --minor 10 --major 50 ``` Then **read `g04.png` as an image**. Cyan every 10 source px, red and labelled every 50. Walk the screen element by element and write down the region each one occupies in source coordinates. Only then sample. [`references/measuring.md`](references/measuring.md) is the technique: the four kinds of colour region and the command each one needs, how gutters, row heights and radii come off the same grid, and how to measure the type face instead of guessing it. Read it before you sample anything. ### Deliverable of this phase A table with an **evidence** column, one row per token. Anything without evidence does not become a token. | token | value | evidence | |---|---|---| | `--n-font` | `-apple-system, "SF Pro"…` | `refkit font` on the page title, 0.93 (2nd 0.87) | | `--n-text` | `#2C2C2C` | H1 core ink @3x, 3 screens | | `--n-hairline` | `#E9E8E7` | 1pt coverage solve, settings dividers | | `--n-border` | `#EFEEEC` | card outline solve | Write the machine half of that table as you measure: `probes.json` in the canvas folder, committed with the boards, one entry per measurement, `{"id", "img", "cmd", "box"}`. Phase 4 replays it against your renders, so every number that justified a token is re-checked after the token changes. Without it the evidence drifts: one finished run shipped three rows still citing an alpha of `.174` after the token had become `.10`, and `refkit tokens` cannot catch that; it checks that tokens exist, not that their evidence still agrees with them. Two things `batch` will not tell you. It compares the **first colour a probe prints**, and `sample` prints its flat-fill census first, so an ink probe needs `--only ink` or it silently compares two backgrounds and reports a perfect Δ 0. And a key starting with `_` in a probe is ignored, which is where the sanity note below lives. **Every probe box carries a one-line sanity note** proving the window holds the element and only it. The window is wrong far more often than the measurement is: a probe at `cy=681` for a button row that sits at 564, or a box that catches the neighbouring label instead of the glyph, returns a confident, plausible number either way. Re-sample rather than argue. Where the strip and a native capture disagree, **the native capture wins.** --- ## Phase 2: design system before any screen Write one `:root` block and inline it, **byte-identically**, into every artboard. Artboards render in `