跪拜 Guibai
← Back to the summary

A Playwright Pipeline That Reverse-Engineers and Rebuilds Any Website with Parallel AI Agents

1. Foreword

Hello everyone, I'm alien. Today we'll talk about a feature that can clone beautiful websites with one click — the usage and implementation principles of ai-website-cloner-template.

What can you gain from this document?

The usage of ai-website-cloner-template is very simple:

Step 1: Download the template from GitHub to your local machine:

git clone https://github.com/JCodesMore/ai-website-cloner-template.git

Step 2: Run Claude Code and execute /clone-website (this is essentially a skill at the template project level)

3dfdca83a7eb81576305b5fc2833fe5d.png

Without further ado, let's use the Juejin website as an example (https://juejin.cn) and first look at the achieved effect:

PC-side effect comparison (left: generated / right: original):

bcf4f44235c451291204bf95bb67a957.png

Mobile-side effect comparison (left: generated / right: original):

cd1b1a44d37879542b551e1cec909794.png

Multi-device compatibility scenario:

2026-07-048.14.44-ezgif.com-video-to-gif-converter.gif

Overall evaluation of the generated code:

Next, let's look at how ai-website-cloner-template handles this internally.

2. Core Implementation

2.1 Core Principles

138c7c494a76e62e4047bcf0308ab52a.png

The principle of ai-website-cloner-template can be divided into four parts:

Phase 1: Reconnaissance

After getting the target URL, the first thing is to figure out "what it looks like and how it moves."

Let's take a look:

05b16ab2b73f8287c4b3a9cdf0498ce8.png

c2d06a90278674467aabd50efeaf39a3.png

215ca30db1f8b3a7d8a605ac1caf930b.png

Phase 2: Foundation

After reconnaissance, start laying the groundwork. This step must be completed first before all subsequent components can begin work.

Phase 3: Spec & Build

This is the most critical stage of the pipeline and the part that best embodies the "spec-driven" philosophy.

121af4ffe5e688769d4854a5fd094b78.png

Phase 4: Assembly & QA

All components are written; now they need to be assembled into a complete page and verified to see if it really resembles the target site.

In the implementation process, the core logic is to truly obtain the page state inside the browser, and Playwright plays a core role.

2.2 Introduction to Playwright

First, let's look at what Playwright is?

Playwright is an open-source library by Microsoft for end-to-end testing and browser automation. It allows you to control real browsers (Chrome, Firefox, Safari) with code, automatically completing operations like clicking, filling, taking screenshots, and reading page content.

For example, if we want to launch a browser in a Node environment, we can directly call the corresponding API:

// 1. Launch browser
const { chromium } = require('playwright');
const browser = await chromium.launch({ headless: true });  

You can also create an independent incognito window:

// 2. Create a new context (independent incognito window)                                                                                                                 
const context = await browser.newContext(); 

Operating the browser essentially allows us to leverage this capability to complete automated testing verification like e2e:

// Click
await page.click('button#submit');  
// Navigate
await page.goto('https://example.com'); 

You can also take screenshots to capture the effect:

await page.screenshot({ path: 'screenshot.png' });                                                                                                

Interested students can research it; here is a summary of some API capabilities:

1bbe988dbfe40bc21918c37a106a3d51.png

3. Specific Implementation Ideas

After introducing the core principles, let's pick some key scenarios and look at the core implementation:

Multi-viewport screenshots:

const viewports = [
  { name: "desktop-1440", width: 1440, height: 900 },
  { name: "tablet-768", width: 768, height: 1024 },
  { name: "mobile-390", width: 390, height: 844 },
];

for (const vp of viewports) {
  const ctx = await browser.newContext({
    viewport: { width: vp.width, height: vp.height },
    locale: "zh-CN",
    userAgent: "Mozilla/5.0 ... Chrome/131.0.0.0 ...",
  });
  const page = await ctx.newPage();
  await page.goto(url, { waitUntil: "networkidle", timeout: 60000 });
  await page.waitForTimeout(2500); // wait for hydration
  await page.screenshot({ path: `${outDir}/${vp.name}-full.png`, fullPage: true });
  await ctx.close();
}

Capture one fullPage screenshot across three standard viewports. Note that after waitUntil: "networkidle", you need to wait an additional 2.5 seconds — Juejin uses a lot of lazy-loaded images and IntersectionObserver; networkidle does not mean "completely stable."

Style restoration: extracting global style tokens:

page.evaluate() runs a piece of JS, traversing all DOM nodes and calling getComputedStyle():

const tokens = await page.evaluate(() => {
  const all = document.querySelectorAll("*");
  const fonts = new Set(), sizes = new Set(), colors = new Set();
  for (const el of all) {
    const s = getComputedStyle(el);
    fonts.add(s.fontFamily);
    sizes.add(s.fontSize);
    if (s.color !== "rgba(0, 0, 0, 0)") colors.add(s.color);
  }
  return { fonts: [...fonts], sizes: [...sizes].sort(), colors: [...colors] };
});

After capturing Juejin's real tokens, they look like this:

Primary Blue:  rgb(30, 128, 255)  →  #1e80ff
Page Background:  rgb(242, 243, 245) →  #f2f3f5
Card Background:  rgb(255, 255, 255) →  #ffffff
Body Text:    rgb(37, 41, 51)    →  #252933
Secondary Text:  rgb(81, 87, 103)   →  #515767
Tertiary Text:  rgb(134, 144, 156) →  #86909c
Border:    rgb(228, 230, 235) →  #e4e6eb
Danger Red:  rgb(246, 66, 66)   →  #f64242
Font Family:  PingFang SC, Microsoft YaHei, Helvetica Neue

Mandatory interaction scanning:

This step is the essence of the entire skill. Static screenshots can only see "what it looks like now," but the site is "alive": tab switching changes content, scrolling changes header styles, hovering changes card background colors.

Take Juejin as an example for a complete sweep:

// Scroll sweep: capture header styles at different scrollY
for (const y of [0, 300, 600, 900, 1200, 1500, 1800, 2094]) {
  await page.evaluate((sy) => window.scrollTo({ top: sy, behavior: "instant" }), y);
  await page.waitForTimeout(400);
  const data = await page.evaluate(() => {
    const header = document.querySelector("header");
    return {
      scrollY: window.scrollY,
      headerBg: header ? getComputedStyle(header).backgroundColor : null,
      headerShadow: header ? getComputedStyle(header).boxShadow : null,
    };
  });
  scrollSnapshots.push(data);
}

// Click sweep: click the "Latest" tab to see if content changes
const tabClick = await page.evaluate(() => {
  const target = [...document.querySelectorAll("a, button, div, span")].find(
    (el) => el.textContent?.trim() === "最新" && el.children.length === 0
  );
  if (!target) return { ok: false };
  target.click();
  return { ok: true };
});
await page.waitForTimeout(1500);
const latestContent = await page.evaluate(() => {
  return document.querySelectorAll(".entry").length; // Real data: 1 card
});

Actual testing found that after switching Juejin's "Latest" tab, the number of articles dropped from 16 to 1, indicating this is not a pure UI switch but a real re-fetch of data. When cloning, useState is used to switch between two static mocks, but production deployment requires backend API cooperation.

Page topology mapping:

List all sections and annotate interaction models:

1. Header       — Static (no scroll change)
2. LeftSidebar  — Click (navigation)
3. ArticleList  — Click (tab switch)
4. ArticleCard  — Hover (subtle bg)
5. CtaBanner    — Static
6. RightRail    — Static (refresh button mock)
7. Footer       — Static
8. MobileAppPill — Static (position: fixed)

The output of this step is PAGE_TOPOLOGY.md, serving as an index for subsequent spec files.

d2413f24dfb89d57ada3b56c89939dbe.png

The above is the Phase 1 part. The subsequent two parts systematically process styles, assets, and the specification specs for generating components, which will not be introduced in detail here. Let's look at how visual diffing is done in Phase 4.

The QA script iterates through three viewports to capture target and clone screenshots respectively:

for (const vp of viewports) {
  const ourCtx = await browser.newContext({ viewport: vp });
  const ourPage = await ourCtx.newPage();
  await ourPage.goto("http://localhost:3010/", { waitUntil: "networkidle" });
  await ourPage.screenshot({ path: `clone-${vp.name}-full.png`, fullPage: true });

  const targetCtx = await browser.newContext({ viewport: vp });
  const targetPage = await targetCtx.newPage();
  await targetPage.goto("https://juejin.cn/", { waitUntil: "networkidle" });
  await targetPage.screenshot({ path: `target-${vp.name}-full.png`, fullPage: true });
}

Priority for finding differences in comparison:

  1. Size differences: width, height, padding, gap (most easily exposed)
  2. Color differences: background, text, borders (most sensitive to the naked eye)
  3. Layout differences: flex direction, grid column count, sticky failure

In the Juejin practical test, mobile immediately exposed 3 differences (tag wrap, author truncate, button-in-anchor), while desktop/tablet passed in one go.

4. Summary

The real value of ai-website-cloner-template is not "being able to clone websites"—that is the job of DevTools itself. It reconstructs a seemingly simple "screenshot + copy code" into a reusable, verifiable, concurrent engineering pipeline: first Reconnaissance to capture computed values, then Foundation to build the skeleton, then Spec & Build's spec-driven multi-agent parallel generation, and finally Assembly & QA for multi-viewport visual regression. The reversal of the order to write spec first, then write code is the watershed from "AI toy" to "AI engineering."

I suggest everyone clone a copy locally, pick an admired site to run through it, and personally experience the real feeling of "spec-driven + multi-agent parallelism." Those who act first see the future first. Thanks for reading, see you in the next article. Friends who like it can like + bookmark to support updates on "whimsical ideas" in AI scenarios.

Comments

Top 1 of 2 from juejin.cn, machine-translated. The original thread is authoritative.

凉瓜拌面

If it's drawn with canvas, it basically won't achieve the effect at all.

浪乘画舸忆蟾蜍

Let the OP test it.