334 lines
15 KiB
Markdown
334 lines
15 KiB
Markdown
# ditto.site
|
|
|
|
[](https://github.com/ion-design/ditto.site/actions/workflows/ci.yml)
|
|
[](LICENSE)
|
|
[](.nvmrc)
|
|
|
|
ditto.site is a deterministic website compiler. Given a public URL, it compiles the
|
|
observed rendered site into a modern TypeScript app, defaulting to Next.js App
|
|
Router with an option for Vite React, using
|
|
browser-captured evidence: DOM, computed styles, layout boxes, CSS rules, fonts,
|
|
assets, source metadata, screenshots, interaction states, and motion specs where
|
|
they can be reproduced safely.
|
|
|
|
The compiler is not an LLM page author. The normal clone path is a pure
|
|
generation pipeline over a frozen capture, so the same capture produces
|
|
byte-stable output. The generated app uses `ditto` naming for clone-specific
|
|
runtime helpers and documentation.
|
|
|
|
## What It Produces
|
|
|
|
A generated app is a self-contained project under `generated/app/` during
|
|
validation and under `<out>/<site>/app` for delivery. The default framework is
|
|
Next.js App Router:
|
|
|
|
- `src/app/layout.tsx`: root layout, captured language, Next metadata, viewport,
|
|
JSON-LD, and shared multi-route shell.
|
|
- `src/app/page.tsx` and route pages: JSX reconstructed from the captured render
|
|
tree.
|
|
- `src/app/globals.css`: reset, font faces, design tokens, Tailwind setup when
|
|
enabled.
|
|
- `src/app/ditto.css`: clone-specific CSS that still needs stylesheet emission,
|
|
including pseudo-elements, keyframes, raw CSS fallbacks, and non-Tailwind
|
|
fidelity rules.
|
|
- `src/app/content.ts` or `content.tsx`: semantic editable data when repeated
|
|
regions are promoted into components or sections.
|
|
- `src/app/components/`, `src/app/sections/`, `src/app/svgs/`: generated modules
|
|
split from repeated components, page sections, and inline SVGs.
|
|
- `src/app/ditto/`: small generated runtime helpers for recognized interactions,
|
|
accordions, dropdown menus, and reproducible motion.
|
|
- `src/app/robots.ts`, `src/app/sitemap.ts`, `src/app/llms.txt/route.ts`, and
|
|
sometimes `src/app/llms-full.txt/route.ts`.
|
|
- App Router file-based icons such as `src/app/favicon.ico`, `src/app/icon.png`,
|
|
and `src/app/apple-icon.png` when the source exposes them.
|
|
- `public/assets/cloned/`: materialized images, fonts, manifest files, manifest
|
|
icons/screenshots, and other source assets needed by the clone.
|
|
- `AGENTS.md` and `ARCHITECTURE.md`: generated documentation for the delivered
|
|
app, derived from clone metadata.
|
|
|
|
With `--framework=vite` or API option `framework: "vite"`, the generated app is
|
|
a Vite React project. Single-page output uses `index.html`, `src/main.tsx`,
|
|
`src/page.tsx`, `src/globals.css`, and `src/ditto.css`. Multi-route Vite output
|
|
is a Vite multi-page app with one HTML entry per cloned route and route modules
|
|
under `src/routes/<routeKey>/`.
|
|
|
|
Validation builds keep internal `data-cid` attributes so gates can align source
|
|
and clone nodes exactly. The exported app strips validation-only ids and only
|
|
keeps deterministic `data-ditto-id` anchors where generated CSS or runtime
|
|
recipes still need a stable target.
|
|
|
|
## Quickstart
|
|
|
|
```bash
|
|
npm install
|
|
npx playwright install chromium
|
|
|
|
# Single-page clone, Tailwind output by default.
|
|
npm run clone -- https://example.com/
|
|
|
|
# Explicit product choices.
|
|
npm run clone -- https://example.com/ --mode=single --styling=tailwind
|
|
npm run clone -- https://example.com/ --mode=single --styling=css
|
|
npm run clone -- https://example.com/ --mode=multi --styling=tailwind
|
|
npm run clone -- https://example.com/ --mode=multi --styling=css
|
|
npm run clone -- https://example.com/ --mode=single --framework=vite
|
|
npm run clone -- https://example.com/ --mode=multi --framework=vite
|
|
|
|
# Deliver into a clean app directory.
|
|
npm run clone -- https://example.com/ --out=./output
|
|
|
|
# Benchmark and validation helpers.
|
|
npm run bench -- --tier=easy
|
|
npm run bench -- --tier=stage2 --reuse
|
|
npm run bench-site
|
|
npm run validate-site -- runs/site-example.com/<timestamp>
|
|
|
|
# Faster multi-page runs on larger sites. Validation is opt-in.
|
|
npm run clone -- https://example.com/ --mode=multi --concurrency=5
|
|
npm run clone -- https://example.com/ --mode=multi --validate --validate-concurrency=3 --viewport-concurrency=2
|
|
npm run validate-site -- runs/site-example.com/<timestamp> --validate-concurrency=3 --viewport-concurrency=2
|
|
```
|
|
|
|
The root scripts forward into the `compiler` workspace. Running the same commands
|
|
from `compiler/` also works.
|
|
|
|
Multi-page generation defaults to the fast no-validation path. For production
|
|
delivery, keep first response and QA as separate phases: run single-page first,
|
|
expand to multi-page with default CLI behavior or service `verify:false`, then
|
|
run strict validation separately. Use service `asyncVerify:true` when a DB worker
|
|
should persist the clone first and attach the verify report afterward. Capture
|
|
route parallelism is controlled by `--concurrency`; validation route and viewport
|
|
parallelism are controlled by `--validate-concurrency` and
|
|
`--viewport-concurrency`.
|
|
|
|
## Architecture
|
|
|
|
```
|
|
URL
|
|
-> capture
|
|
-> normalize render IR
|
|
-> infer assets, fonts, sections, recipes, SEO, metadata
|
|
-> generate app (Next by default, Vite optional)
|
|
-> materialize assets
|
|
-> build and validate
|
|
-> export delivery app
|
|
```
|
|
|
|
### 1. Capture
|
|
|
|
Capture is Playwright-based. A page is loaded once and resized through the target
|
|
viewports, normally `375`, `768`, `1280`, and `1920`, so responsive snapshots are
|
|
aligned to the same page state instead of four unrelated reloads.
|
|
|
|
The in-page walker records:
|
|
|
|
- DOM tree shape and text.
|
|
- Curated computed styles.
|
|
- Document-coordinate bounding boxes.
|
|
- Per-viewport visibility, scroll dimensions, and page backgrounds.
|
|
- Pseudo-element content and styles.
|
|
- Inline SVG markup.
|
|
- Source attributes that are safe and meaningful, including accessibility
|
|
attributes.
|
|
- Head metadata, link tags, JSON-LD, icons, manifest links, alternates, and SEO
|
|
discovery resources.
|
|
- CSS, fonts, images, SVGs, videos, manifests, and other linked assets.
|
|
|
|
Capture also handles common state problems before the snapshot:
|
|
|
|
- Cookie, consent, newsletter, and modal overlays are dismissed when a safe
|
|
deterministic rule recognizes them.
|
|
- Scroll-locked and iframe-backed overlays are removed only when they match the
|
|
blocker patterns.
|
|
- Poster-less videos get a representative still when possible.
|
|
- Lazy background images and responsive assets are backfilled from CSS, DOM, and
|
|
manifest evidence.
|
|
- Interaction capture stamps temporary `data-cid-cap` ids in the browser,
|
|
explores recognized controls, and records state deltas.
|
|
- Motion capture extracts declarative CSS keyframes, WAAPI effects, rotating text,
|
|
scroll reveals, and marquee-like tracks when they can be replayed deterministically.
|
|
|
|
### 2. Normalize
|
|
|
|
The normalizer merges viewport snapshots into one render IR. Each node receives a
|
|
stable pre-order id and carries per-viewport style, box, and visibility data.
|
|
Temporary capture ids are kept only as needed for interaction and motion mapping.
|
|
|
|
The IR is intentionally close to rendered browser facts. Higher-level intent is
|
|
inferred later so fidelity gates can always fall back to measured evidence when a
|
|
semantic guess is uncertain.
|
|
|
|
### 3. Infer
|
|
|
|
Inference is deterministic and local to the capture:
|
|
|
|
- Sections are detected from rendered structure and visible hierarchy.
|
|
- Design tokens are extracted from repeated color, spacing, and type values.
|
|
- Semantic color roles are assigned where source evidence and usage make them
|
|
stable enough.
|
|
- Primitive roles identify headings, links, buttons, images, icons, nav, badges,
|
|
forms, and related UI pieces.
|
|
- Asset and font graphs classify linked files, download them content-addressably,
|
|
rewrite references, and preserve fallbacks.
|
|
- Recipe inference recognizes repeated card grids, feature grids, product grids,
|
|
logo clouds, galleries, navs, and other common section patterns.
|
|
- Interaction recipes map captured deltas to small runtime helpers rather than
|
|
replaying arbitrary site JavaScript.
|
|
- Motion recipes emit reproducible templates for the safe declarative families
|
|
and freeze unsupported motion honestly.
|
|
- SEO inventory records source title, description, keywords, canonical URL,
|
|
robots/referrer/theme-color/color-scheme, Open Graph, Twitter cards, icons,
|
|
manifest links and assets, alternates/hreflang, JSON-LD, robots/sitemap links,
|
|
and `llms.txt` or `llms-full.txt` when discoverable.
|
|
- Code quality inventory records module organization, generated component split,
|
|
content-model extraction, metadata, and markup hygiene.
|
|
|
|
### 4. Generate
|
|
|
|
The generator emits a Next.js App Router app by default, or a Vite React app when
|
|
`framework` is `vite`. Tailwind v4 is the default styling mode; plain CSS remains
|
|
available.
|
|
|
|
In Tailwind mode, most exact geometry, typography, display, and spacing becomes
|
|
utility classes. Values that do not belong in class names, pseudo-elements,
|
|
keyframes, and recipe/runtime selectors stay in `ditto.css`. In CSS mode, shared
|
|
semantic classes and per-rule stylesheet emission preserve the same computed
|
|
style contract.
|
|
|
|
The generator also:
|
|
|
|
- Splits clean page regions into `sections/`.
|
|
- Extracts repeated DOM skeletons into `components/`.
|
|
- Keeps editable semantic data in `content.ts`, while validation ids and
|
|
per-instance class overrides stay in generated plumbing modules.
|
|
- Hoists inline SVGs into `svgs/` where that improves readability.
|
|
- Emits `ditto` runtime utilities only for recognized recipes that need them.
|
|
- Rewrites same-origin internal links for generated routes.
|
|
- Emits framework-appropriate metadata, JSON-LD, robots, sitemap, `llms.txt`,
|
|
icons, web manifests, and manifest assets from the SEO inventory.
|
|
- Emits generated `AGENTS.md` and `ARCHITECTURE.md` for the delivered app.
|
|
|
|
### 5. Multi-Route Cloning
|
|
|
|
Multi-route mode starts at one entry URL, crawls same-origin links, groups routes
|
|
by deterministic URL templates, and applies a CMS/template boundary:
|
|
|
|
- Singletons are reproduced.
|
|
- Pairs are reproduced because there is not enough evidence to collapse them.
|
|
- Larger collections are represented by the listing and one representative route.
|
|
- The full instance list is recorded as a handoff boundary rather than cloning
|
|
every CMS item.
|
|
|
|
Each selected route is captured separately. The site generator then emits one app
|
|
with shared assets, shared tokens, link rewriting, optional shared header/footer
|
|
chrome, and route-level pages. A multi-route job can reuse a prior single-page
|
|
entry capture to return the first page quickly and expand later.
|
|
|
|
### 6. Validate
|
|
|
|
Validation builds and serves the generated app, captures it with the same walker,
|
|
and grades deterministic gates:
|
|
|
|
- Build and static export success.
|
|
- Capture sanity: the source was not an empty shell, bot wall, or polluted overlay
|
|
state.
|
|
- Asset and font materialization.
|
|
- DOM shape and valid retags.
|
|
- Computed-style fidelity.
|
|
- Layout boxes, section positions, page dimensions, and responsive behavior.
|
|
- Byte determinism from regenerating the same frozen capture.
|
|
- Perceptual screenshot similarity.
|
|
- Interaction recipe behavior for menus, tabs, accordions, carousels, modals,
|
|
hover, and focus states that were captured.
|
|
- Motion reproduction for supported declarative families.
|
|
- Site-level link integrity and site determinism for multi-route output.
|
|
- Output quality, including componentization, naming, content extraction,
|
|
styling organization, and metadata hygiene.
|
|
|
|
The gates are intended to be deterministic grading functions. Failures should
|
|
produce a reproducible artifact and a narrow compiler improvement, not a manual
|
|
one-off patch to an output app.
|
|
|
|
## SEO And Documentation Layer
|
|
|
|
ditto.site treats source metadata as part of the clone contract. The current
|
|
generator preserves source-provided metadata in the generated framework shell
|
|
where possible, materializes linked icons and manifest assets, preserves JSON-LD
|
|
with safe script emission, and generates or preserves `llms.txt`.
|
|
|
|
If a source exposes `llms.txt` or `llms-full.txt`, those files are preserved as
|
|
static routes. If not, the generator creates a concise `llms.txt` from captured
|
|
route titles, descriptions, and visible content summaries. The generated app also
|
|
receives root `AGENTS.md` and `ARCHITECTURE.md` files that explain the app
|
|
structure, safe edit zones, `src/app/ditto`, `content.ts`, components, sections,
|
|
SVG modules, `ditto.css`, and `ditto-meta.ts`.
|
|
|
|
SEO inventory metrics are written beside generated artifacts as `seo.json` and
|
|
`seo.md` so coverage can be inspected without changing output behavior.
|
|
|
|
## Service Layer
|
|
|
|
The hosted service wraps the compiler without changing clone semantics:
|
|
|
|
```
|
|
compiler/ # capture, IR, inference, generation, validation
|
|
packages/core/ # compiler adapter and file-map collection
|
|
packages/db/ # Drizzle schema, migrations, repository, queue wrapper
|
|
packages/storage/ # local and S3/R2 artifact storage
|
|
packages/api/ # Hono REST API and MCP server
|
|
packages/worker/ # queued clone runner and optional verify harness
|
|
packages/test-utils/ # fixture server and integration-test helpers
|
|
```
|
|
|
|
REST accepts a URL and clone options, then returns either an inline result or an
|
|
async job id depending on whether a database queue is configured. The MCP server
|
|
uses a list-then-read model: agents get job metadata and file manifests first,
|
|
then request only the files they need.
|
|
|
|
See [docs/SERVICE.md](docs/SERVICE.md) and [docs/DEPLOY.md](docs/DEPLOY.md) for
|
|
the operational API and deployment details.
|
|
|
|
## Repository Map
|
|
|
|
| Path | Purpose |
|
|
| --- | --- |
|
|
| `compiler/` | deterministic clone compiler |
|
|
| `compiler/src/capture/` | Playwright capture, walker, assets, SEO resources, interactions, motion |
|
|
| `compiler/src/normalize/` | render IR construction |
|
|
| `compiler/src/infer/` | sections, tokens, assets, fonts, recipes, primitives |
|
|
| `compiler/src/generate/` | app generation, SEO/docs layer, code quality reports |
|
|
| `compiler/src/site/` | multi-route crawl/generate/validate flow |
|
|
| `compiler/src/validate/` | fidelity, perceptual, interaction, motion, and determinism gates |
|
|
| `compiler/benchmarks/` | benchmark URL lists |
|
|
| `packages/` | REST, MCP, queue, storage, and service adapters |
|
|
| `docs/SERVICE.md` | service architecture and API reference |
|
|
| `docs/DEPLOY.md` | Railway, Neon, and R2 deployment guide |
|
|
| `examples/` | benchmark result summaries, composites, motion evidence, and small runnable outputs |
|
|
|
|
## Deferred Work
|
|
|
|
ditto.site intentionally does not attempt arbitrary JavaScript replay. It does not
|
|
recreate full third-party applications, live personalization, auth, payments,
|
|
analytics behavior, or remote iframe internals. It can preserve static scaffolding
|
|
around those regions and may emit placeholders when that is the more faithful
|
|
self-contained representation.
|
|
|
|
Video-like animation replay, scroll-scrubbed canvases, WebGL, and finished
|
|
entrance animations remain outside the deterministic contract unless a safe,
|
|
observable recipe exists. Unsupported motion is frozen rather than shipped as a
|
|
broken imitation.
|
|
|
|
## Contributing
|
|
|
|
Use `npm run typecheck` and `npm test` before opening a PR. Browser tests require
|
|
Chromium; Postgres-backed tests use `TEST_DATABASE_URL` or the local compose
|
|
stack. Changes that alter compiler output should include focused fixture or
|
|
benchmark evidence.
|
|
|
|
See [CONTRIBUTING.md](CONTRIBUTING.md), [SECURITY.md](SECURITY.md), and
|
|
[CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).
|
|
|
|
## License
|
|
|
|
[MIT](LICENSE) © ion-design and contributors.
|