{"id":51708765,"url":"https://github.com/byte271/mirrorframe","last_synced_at":"2026-07-20T17:35:54.941Z","repository":{"id":370294135,"uuid":"1292991123","full_name":"byte271/mirrorframe","owner":"byte271","description":"Capture a live webpage's design system, motion, and interaction state machines, then reconstruct it as verified React — with per-node pixel verification and an honest skip-reason taxonomy.","archived":false,"fork":false,"pushed_at":"2026-07-09T15:30:01.000Z","size":1614,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-07-20T17:35:53.175Z","etag":null,"topics":["ai-agents","claude-skill","code-generation","design-systems","design-tokens","playwright","react","reverse-engineering","visual-regression","web-scraping"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/byte271.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-07-08T02:50:40.000Z","updated_at":"2026-07-09T15:30:47.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/byte271/mirrorframe","commit_stats":null,"previous_names":["byte271/mirrorframe"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/byte271/mirrorframe","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byte271%2Fmirrorframe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byte271%2Fmirrorframe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byte271%2Fmirrorframe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byte271%2Fmirrorframe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/byte271","download_url":"https://codeload.github.com/byte271/mirrorframe/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byte271%2Fmirrorframe/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35695246,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-07-20T02:08:10.276Z","status":"ssl_error","status_checked_at":"2026-07-20T02:08:09.736Z","response_time":111,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-agents","claude-skill","code-generation","design-systems","design-tokens","playwright","react","reverse-engineering","visual-regression","web-scraping"],"created_at":"2026-07-16T19:00:21.318Z","updated_at":"2026-07-20T17:35:54.935Z","avatar_url":"https://github.com/byte271.png","language":"JavaScript","funding_links":[],"categories":["Source Catalog"],"sub_categories":[],"readme":"# Mirrorframe\n\nClone a live web page into a **verified, self-contained React app** — capture it with a\nheadless browser, distill a **Design Genome** (tokens + motion + interaction state\nmachines), reconstruct it deterministically, and prove the result **per node and per\ninteraction state** by visual diff. Built entirely on technology that exists today\n(Playwright, WAAPI, MutationObserver, esbuild, pixelmatch). No LLM in the loop: every\noutput is inspectable and reproducible.\n\n**This project is also packaged as a Claude Skill** — an AI agent should start from\n[`SKILL.md`](SKILL.md) and the deeper docs in [`references/`](references/). This README\nis the human-facing overview.\n\n\u003e **Why 0.x:** the CLI flags, genome schema, and report format may still change. 0.x is\n\u003e a statement of API instability, not of dishonest numbers — every number below is\n\u003e reproducible from a clean clone.\n\n## Quick start\n\nRequires Node.js ≥ 18. No other machine state is assumed.\n\n```bash\ngit clone \u003cthis-repo\u003e \u0026\u0026 cd mirrorframe\nnpm install            # postinstall downloads the Playwright Chromium binary\n\n# clone a live URL (only pages you own or that are permissively licensed):\nnode scripts/mirrorframe.js run --url https://your-site.example --out ./mf-out/yours\n\n# or a local page directory containing an index.html (served automatically):\nnode scripts/mirrorframe.js run --dir ./fixtures/tabs --out ./mf-out/tabs\n\n# optional capture viewport: --width 1280 --height 800   (defaults shown)\n# optional responsive re-capture (new in 0.4): --breakpoints 480,768\n\nnpm run verify         # regression gate: full pipeline on all 15 bundled fixtures\n```\n\nOpen the reconstruction at `\u003cout\u003e/recon-app/index.html` — a self-contained React app\n(`app.jsx`, `styles.css`, `bundle.js`) with all assets bundled locally in `\u003cout\u003e/assets/`.\n\n## How it works (4 automatic stages inside `run`)\n\n1. **Capture** → `capture.json`: headless Chromium walks the DOM recording computed\n   styles per node, plus every motion/interaction signal listed below, and downloads\n   all referenced assets.\n2. **Genome** → `genome.json`: clusters design tokens, compiles motion DNA and\n   interaction DNA (recovered state machines), annotates the structure.\n3. **Reconstruct** → `recon-app/`: deterministic React + CSS codegen with the recovered\n   state machines re-wired as real listeners/observers/animations.\n4. **Converge** → `convergence.json` + `summary.json`: re-renders the reconstruction at\n   the identical viewport, verifies per node and per state, auto-corrects residual\n   drift, and replays every recovered interaction and scroll state.\n\n## Feature list (v0.4.0 — everything below is implemented and verified)\n\n### Capture \u0026 assets\n- Full computed-style capture per node (layout, color, type, borders, shadows,\n  radii, filters, transforms) at a configurable viewport\n- **Images** (`\u003cimg\u003e`) and **CSS background-images** downloaded and rewritten to\n  local bundled copies (8MB/asset, 192MB/capture budgets; misses reported, never hidden)\n- **Web fonts**: every `@font-face` is downloaded, bundled, and re-declared locally\n- **SVG passthrough**: inline SVG markup is preserved verbatim\n- **`\u003cvideo\u003e`** (new in 0.2): source + poster bundled locally (64MB/asset budget),\n  playback attributes (autoplay/loop/muted/playsinline/controls) preserved, real\n  `\u003cvideo\u003e` re-emitted; degrades to poster/first-frame still if the source is\n  unfetchable — never an empty box\n- **`\u003ccanvas\u003e`** (new in 0.2): the currently painted frame is snapshotted and\n  re-emitted as a pixel-true still\n- Same-origin iframes captured; cross-origin content masked with a fixed reason\n- **Pseudo-elements** (new in 0.4): `::before`/`::after` content (badges, timeline\n  dots, underline accents, decorative marks) is captured per originating element\n  and re-emitted as per-node pseudo rules; pseudo background-images are bundled\n- **Responsive breakpoints** (new in 0.4): `--breakpoints 480,768` re-captures the\n  page at extra viewport widths and emits the per-node style differences as real\n  `@media` rules — the clone reflows like the original instead of being a\n  single-width photograph; each width is pixel-verified against ground truth\n\n### Motion (the \"individual frame\" tier)\n- CSS `@keyframes` animations and transitions (recovered via WAAPI `getAnimations()`)\n- `:hover` rules recovered verbatim from same-origin stylesheets; **JS-driven hover\n  effects** recovered by a synthetic hover probe\n- **Pointer choreography** (new in 0.3): mouse-movement effects are recovered by\n  sampling the page over a 3×3 pointer grid and fitting every transform-matrix\n  component as a plane over the pointer (least squares, R² ≥ 0.85) — parallax\n  layers, 3D tilt cards (`matrix3d`), magnetic offsets, pointer-linked scale.\n  **Smoothing/lag is recovered too**: the exponential time constant of the\n  \"ultra-smooth\" trailing chase is measured from a pointer-jump step response\n  and replayed with the same lerp in the reconstruction. Non-planar pointer\n  physics is reported as unclassified, never guessed\n- **Scroll choreography**: scroll-position–linked style tracks are sampled at multiple\n  scroll depths and re-emitted; verified by scroll-state replay at 25/50/75%\n- **Sequential scroll-reveal stagger** (new in 0.3): reveal mutations are\n  timestamped, so elements that fade/slide in one by one are replayed with their\n  observed per-element offsets; each participant's own computed `transition`\n  (duration, easing curve, `transition-delay`) is re-emitted verbatim — CSS\n  `nth-child` delay staggers and JS-timer staggers both survive the clone\n- **Chronograph frame sampler** (new in 0.2): time-driven motion with *no declared\n  animation object* (rAF loops, JS tickers) is detected and recorded at **every\n  animation frame** for a bounded window (2400ms, ≤120 tracks) — transform, opacity,\n  filter, clip-path per frame — then replayed verbatim as looping WAAPI animations\n  built from the sampled offsets\n\n### Interaction state machines (behavior graphing)\nRecovered from the page by synthetic probes — never assumed or hardcoded:\n- `reveal` — IntersectionObserver scroll-reveal (hidden → visible class machines)\n- `toggle` — second click reverses the first (accordions, expand/collapse)\n- `exclusive` — one class moves between siblings (tabs, segmented controls)\n- `pair` — one trigger adds the class, another removes it (modal open/close,\n  including triggers only reachable *inside* the open state)\n- Ambient (timer-driven) mutations such as autoplay carousels are detected during an\n  input-free watch and excluded from learning rather than mislearned\n- A probe that crashes on a page-script conflict records `probe-error` for that\n  candidate and the sweep continues (new in 0.2)\n- **Keyboard/focus recovery** (new in 0.4): CSS `:focus`/`:focus-visible`/\n  `:focus-within` rules recovered verbatim; a bounded keyboard agent probes every\n  tabbable element and recovers JS-driven focus styling — style deltas compile to\n  CSS `:focus` rules, class toggles are re-wired as real `focus`/`blur` listeners\n\n### Verification (honest by construction)\n- **Per-node**: every tracked node gets its own similarity score and one status —\n  `pass` / `style-verified` / `corrected` / `failed` / `animated-unstable` /\n  `hidden-at-capture` / `time-varying-replicated` (new in 0.2) / `skipped(reason)`\n- **Residual auto-correction**: failing nodes get their reconstructed computed styles\n  diffed against capture, patched with a per-node override, and re-verified once\n- **Layout convergence**: rendered rects measured against captured rects; drifting\n  nodes get authored sizes baked in bounded passes\n- **Behavioral replay**: every recovered interaction state is replayed with real\n  clicks and pixel-diffed against a ground-truth screenshot of the original\n- **Scroll replay**: the page is verified at multiple scroll depths, not just the fold\n- **Pointer replay** (new in 0.3): ground-truth screenshots at fixed pointer\n  checkpoints are re-shot on the reconstruction with the same real mouse moves\n  (settle time scaled to the largest recovered smoothing tau) and pixel-diffed;\n  replay shots are settle-aware, waiting until consecutive frames match so\n  staggered reveals and lag-smoothed effects finish before the diff\n- **Focus replay** (new in 0.4): capture screenshots the first Tab stops after\n  real Tab key presses (so `:focus-visible` fires exactly as it does for keyboard\n  users); convergence replays the same Tab sequence and pixel-diffs each stop\n- **Breakpoint replay** (new in 0.4): the reconstruction is resized to every\n  captured breakpoint width and pixel-diffed against the original at that width\n- **Time-varying honesty** (new in 0.2): bundled video/canvas and frame-sampled nodes\n  have their *mechanism* replicated, but pixels depend on the playback instant — they\n  are masked from diffs with the fixed reason `time-varying-media`, itemized, never\n  averaged in or hidden\n- **Fixed skip-reason taxonomy** (`scripts/lib/reasons.js`, 10 reasons): every skipped\n  node/probe/page carries exactly one reason; skipped elements keep their layout\n  footprint via placeholders and are masked from the diff\n- Bounded everything: navigation fallback, probe budgets, node/candidate caps, 300s\n  pipeline watchdog with distinct exit codes (0 ok / 3 page skipped / 4 crash)\n\n## Verified results (clean run, `npm run verify`)\n\nAll 15 bundled fixtures pass with **0 failed nodes, 0 failed states, 0 failed\nscroll/pointer/focus/breakpoint states** — including `stress-pseudo`\n(::before/::after badges, dots, underlines, new in 0.4), `stress-keyboard`\n(:focus/:focus-visible + JS focus styling, real-Tab replay, new in 0.4),\n`stress-responsive` (grid reflow at 768/480px, verified at both widths, new\nin 0.4), `stress-stagger`\n(CSS-delay + JS-timer sequential reveals, new in 0.3), `stress-pointer`\n(parallax/tilt/smooth-follower pointer choreography, new in 0.3), `stress-frames`\n(rAF inline-style motion + animated canvas), `stress-scale` (230 nodes / 120\ninteractive), `stress-edgecss` (container queries, `:has()`), `stress-iframe`,\n`stress-flaky` (404/hung assets), and `stress-carousel` (autoplay → excluded,\nnot mislearned).\n\nThe pipeline has also been spot-validated against complex real-world production pages\n(hundreds of nodes; heavy video, scroll choreography, and rAF motion) with aggregate\nsimilarities ≥ 99.9%, all scroll states passing, media and web fonts fully bundled,\nand zero crashes — any sub-threshold node is itemized in the per-node report rather\nthan averaged away. Nothing from third-party pages is bundled in this repo.\n\n## Reading the report\n\nSuccess = zero `failed` nodes and zero failed states. `style-verified`,\n`animated-unstable`, `hidden-at-capture`, `time-varying-replicated`, and\n`skipped(reason)` are acceptable statuses when explained — see `SKILL.md` for the\nfull interpretation guide. The one-screen summary is `summary.json`; the full\nper-node table is `convergence.json`; residual maps are `diff-*.png`.\n\n## Artifacts (`--out` directory)\n\n| File | Meaning |\n|---|---|\n| `capture.json` | evidence bundle: node tree, computed styles, animations, scroll/frame tracks, probes |\n| `genome.json` | Design Genome: tokens, motion DNA, interaction DNA, annotated structure |\n| `assets/` | locally bundled images, fonts, videos, posters, media stills |\n| `recon-app/` | generated React app (jsx + css + bundle + html) |\n| `original-*.png` / `state-*.png` / `scroll-*.png` / `pointer-*.png` / `focus-*.png` / `bp-*.png` | ground-truth screenshots |\n| `recon-*.png` / `diff-*.png` | reconstruction screenshots + pixelmatch residual maps |\n| `convergence.json` | per-node table, correction log, state/scroll replay results, aggregates |\n| `summary.json` | one-screen quantitative summary |\n\n## Project layout\n\n```\nSKILL.md                        skill entrypoint: trigger description + agent workflow\nscripts/mirrorframe.js          CLI: capture → genome → reconstruct → converge\nscripts/verify-all.js           release verification: all fixtures + results table\nscripts/batch-verify.js         batch validation against a list of live URLs\nscripts/lib/props.js            shared tracked-property list + color normalization\nscripts/lib/reasons.js          fixed skip-reason taxonomy (10 reasons)\nscripts/lib/assets.js           asset downloader/bundler (images, fonts, video)\nscripts/lib/capture.js          capture substrate: DOM/style walk, motion + interaction probes\nscripts/lib/genome.js           genome compiler: tokens, tracks, behavior classification\nscripts/lib/reconstruct.js      deterministic React + CSS codegen (state machines re-wired)\nscripts/lib/converge.js         per-node + aggregate diff, auto-correction, state replay\nreferences/behavior-patterns.md recovered-pattern catalog (reveal/toggle/exclusive/pair)\nreferences/limitations.md       hard scope limits + expected non-pass statuses\nreferences/roadmap.md           forward planning, feasible-now vs research-level\nfixtures/                       15 self-authored test pages (4 core + 11 stress)\n```\n\n## Known limitations (deliberate scope — see references/limitations.md)\n\n- Canvas/WebGL *draw streams* are not reconstructed — a canvas becomes a pixel-true\n  still of its captured frame, not a live re-render\n- Pointer physics beyond a planar matrix-component model (proximity-gated magnets,\n  springs with overshoot) is reported as unclassified, not approximated; recovered\n  smoothing is a single exponential time constant per node\n- No general physics/inertia/spring recovery; frame tracks replay observed motion verbatim\n- Behavior graphing covers class-toggle machines driven by click/scroll/hover/focus —\n  no drags, arrow-key/shortcut machines, or DOM insertion/removal machines\n- One output target: React + plain CSS\n- Responsive re-capture happens at the widths you pass via `--breakpoints`; the exact\n  authored media-query thresholds between them are not inferred\n- \"Any website\" is explicitly not claimed — read the per-node report before trusting output\n\n## Licensing\n\nMIT (see `LICENSE`). All fixtures are authored for this repo (system font stacks, no\nexternal assets). Runtime dependencies: playwright (Apache-2.0), esbuild (MIT),\npixelmatch (ISC), pngjs (MIT), react / react-dom (MIT) — all permissive.\n\nOnly run against pages you own or that are permissively licensed; cloning third-party\ncommercial sites raises IP/ToS issues this tool does not resolve for you.\n\n## Honest engineering notes\n\n- Recover constraints, not pixels: fixed `width`/`height` are never baked into base\n  rules — bold text is *allowed* to widen a tab (layout convergence bakes sizes only\n  for measured drift, as a reported correction).\n- Recover state machines, not frames — except where frames *are* the ground truth:\n  the Chronograph tier replays sampled per-frame motion verbatim and says so.\n- Every claim in this README maps to a check in `npm run verify`; anything that failed\n  or could not be observed is reported per node in `convergence.json` rather than\n  averaged away.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbyte271%2Fmirrorframe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbyte271%2Fmirrorframe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbyte271%2Fmirrorframe/lists"}