[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-92301":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":8,"htmlUrl":8,"language":9,"languages":8,"totalLinesOfCode":8,"stars":10,"forks":11,"watchers":12,"openIssues":13,"contributorsCount":14,"subscribersCount":14,"size":14,"stars1d":14,"stars7d":14,"stars30d":15,"stars90d":14,"forks30d":14,"starsTrendScore":14,"compositeScore":16,"rankGlobal":8,"rankLanguage":8,"license":17,"archived":18,"fork":18,"defaultBranch":19,"hasWiki":18,"hasPages":18,"topics":20,"createdAt":8,"pushedAt":8,"updatedAt":21,"readmeContent":22,"aiSummary":23,"trendingCount":14,"starSnapshotCount":14,"syncStatus":24,"lastSyncTime":25,"discoverSource":26},92301,"ditto.site","ion-design\u002Fditto.site","ion-design",null,"TypeScript",442,59,87,1,0,178,52.33,"MIT License",false,"main",[],"2026-07-22 04:02:05","\u003Cp align=\"center\">\n  \u003Cimg src=\"docs\u002Fassets\u002Fditto.svg\" alt=\"ditto.site logo\" width=\"112\" \u002F>\n\u003C\u002Fp>\n\n# [ditto.site](https:\u002F\u002Fditto.site)\n\n[![CI](https:\u002F\u002Fgithub.com\u002Fion-design\u002Fditto.site\u002Factions\u002Fworkflows\u002Fci.yml\u002Fbadge.svg)](https:\u002F\u002Fgithub.com\u002Fion-design\u002Fditto.site\u002Factions\u002Fworkflows\u002Fci.yml)\n[![License: MIT](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FLicense-MIT-yellow.svg)](LICENSE)\n[![Node](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fnode-%3E%3D20-brightgreen.svg)](.nvmrc)\n\nditto.site turns a public URL into a self-contained TypeScript app. It captures\nwhat the browser actually rendered, then emits a deterministic Next.js App\nRouter project by default, or Vite React when requested.\n\nThe compiler is not an LLM page author. It is a capture-to-code pipeline: same\nfrozen capture in, byte-stable app out.\n\n> **\"Cloning\" here means generating a codebase from a live URL — not `git clone`.**\n> You don't need an existing repository, and you don't need the site's source. Point\n> ditto.site at a public URL and it writes you a fresh project from what the page\n> renders in a browser.\n\nRead the public development and evaluation method in\n[docs\u002FMETHODOLOGY.md](docs\u002FMETHODOLOGY.md). For a map of all the docs, see\n[docs\u002FREADME.md](docs\u002FREADME.md).\n\n## Usage\n\n- REST API: `https:\u002F\u002Fapi.ditto.site`\n- MCP server: `https:\u002F\u002Fapi.ditto.site\u002Fmcp`\n\nGet a hosted key at `https:\u002F\u002Fwww.ditto.site\u002Fapi-key`, or call the verified-email\nsignup flow directly:\n\n```bash\ncurl -sS -X POST \"https:\u002F\u002Fapi.ditto.site\u002Fv1\u002Fsignup\u002Frequest\" \\\n  -H \"content-type: application\u002Fjson\" \\\n  -d '{\"email\":\"you@example.com\"}'\n```\n\nThe emailed verification link lands on `\u002Fapi-key?token=...`, which calls\n`POST \u002Fv1\u002Fsignup\u002Fverify` and displays the new `dtto_live_...` key once.\n\n> **Keys are secrets.** Put your key in an environment variable (`export\n> DITTO_API_KEY=dtto_live_...`) and reference `$DITTO_API_KEY` in every command —\n> never paste the raw key inline, where it lands in shell history, logs, or a chat.\n> Don't commit it. You can rotate a key anytime from the dashboard.\n\n### REST API\n\nStart a clone job:\n\n```bash\nexport DITTO_API_URL=\"https:\u002F\u002Fapi.ditto.site\"\nexport DITTO_API_KEY=\"ditto_live_example\"\n\ncurl -sS -X POST \"$DITTO_API_URL\u002Fv1\u002Fclones\" \\\n  -H \"authorization: Bearer $DITTO_API_KEY\" \\\n  -H \"content-type: application\u002Fjson\" \\\n  -d '{\n    \"url\": \"https:\u002F\u002Fexample.com\u002F\",\n    \"options\": {\n      \"mode\": \"single\",\n      \"styling\": \"tailwind\",\n      \"framework\": \"next\"\n    }\n  }'\n```\n\nThe service returns either a queued job or an inline result. A finished result\nis a file map — every generated file keyed by its app-relative path:\n\n```json\n{\n  \"jobId\": \"job_123\",\n  \"status\": \"succeeded\",\n  \"files\": {\n    \"package.json\": { \"type\": \"text\", \"content\": \"{ ... }\", \"bytes\": 812, \"sha256\": \"...\" },\n    \"src\u002Fapp\u002Fpage.tsx\": { \"type\": \"text\", \"content\": \"export default ...\", \"bytes\": 2048, \"sha256\": \"...\" },\n    \"public\u002Fassets\u002Flogo.png\": { \"type\": \"binary\", \"url\": \"...\u002Ffiles\u002Fpublic\u002Fassets\u002Flogo.png\", \"bytes\": 5123, \"sha256\": \"...\" }\n  }\n}\n```\n\n**Turn that JSON into a project on disk** with the official unpacker — from a\nchecked-out `ditto.site` repo with dependencies installed, pipe the response\nstraight in with no temp file:\n\n```bash\ncurl -sS -X POST \"$DITTO_API_URL\u002Fv1\u002Fclones\" \\\n  -H \"authorization: Bearer $DITTO_API_KEY\" \\\n  -H \"content-type: application\u002Fjson\" \\\n  -d '{\"url\":\"https:\u002F\u002Fexample.com\u002F\",\"options\":{\"mode\":\"single\"}}' \\\n  | npm run --silent unpack -- - .\u002Fout\n```\n\n`npm run unpack -- \u003Cclone.json|-> \u003Cout-dir>` writes the text files inline and\nmaterializes binary assets (inline base64, or fetched from their `url` using\n`$DITTO_API_URL` \u002F `$DITTO_API_KEY`). The CLI package is repo-local until the\nnpm distribution story is ready, so do not use `npx ditto` yet. See\n[`packages\u002Fcli`](packages\u002Fcli\u002FREADME.md) for options.\n\nIf you got back a queued job (`{ \"jobId\": \"job_123\", \"status\": \"queued\" }`),\npoll it, then unpack the finished result — or download the whole app as one\narchive:\n\n```bash\nJOB_ID=\"job_123\"\n\n# poll status, then unpack the finished file map\ncurl -sS -H \"authorization: Bearer $DITTO_API_KEY\" \\\n  \"$DITTO_API_URL\u002Fv1\u002Fclones\u002F$JOB_ID\u002Fresult\" \\\n  | npm run --silent unpack -- - .\u002Fout\n\n# ...or grab the whole app as a single archive\ncurl -L -H \"authorization: Bearer $DITTO_API_KEY\" \\\n  \"$DITTO_API_URL\u002Fv1\u002Fclones\u002F$JOB_ID\u002Fbundle?format=tgz\" \\\n  -o ditto-clone.tgz\n```\n\nUseful options:\n\n| Option | Values | Default |\n| --- | --- | --- |\n| `mode` | `single`, `multi` | `single` |\n| `styling` | `tailwind`, `css` | `tailwind` |\n| `framework` | `next`, `vite` | `next` |\n| `verify` | `true`, `false` | `false` |\n| `asyncVerify` | `true`, `false` | `false` |\n| `maxRoutes` | number | service default |\n\nREST endpoints:\n\n| Method | Path | Purpose |\n| --- | --- | --- |\n| `POST` | `\u002Fv1\u002Fclones` | Start a clone |\n| `GET` | `\u002Fv1\u002Fclones\u002F:id` | Read job status and metadata |\n| `GET` | `\u002Fv1\u002Fclones\u002F:id\u002Fresult` | Read the eager file map |\n| `GET` | `\u002Fv1\u002Fclones\u002F:id\u002Ffiles\u002F*` | Stream one generated file |\n| `GET` | `\u002Fv1\u002Fclones\u002F:id\u002Fbundle?format=tgz` | Download the whole app |\n| `DELETE` | `\u002Fv1\u002Fclones\u002F:id` | Delete a clone and its artifacts |\n\n### MCP\n\nConnect an MCP client to the hosted Streamable HTTP endpoint:\n\n```json\n{\n  \"mcpServers\": {\n    \"ditto\": {\n      \"url\": \"https:\u002F\u002Fapi.ditto.site\u002Fmcp\",\n      \"headers\": {\n        \"Authorization\": \"Bearer ${DITTO_API_KEY}\"\n      }\n    }\n  }\n}\n```\n\nThe MCP server is designed for agents. It returns job ids, metadata, manifests,\nand file references first, then lets the agent read only the files it needs.\n\nCore MCP tools:\n\n| Tool | Purpose |\n| --- | --- |\n| `clone_website` | Start a clone and return `{ jobId, status }` |\n| `get_clone_status` | Poll job progress |\n| `get_clone_result` | Read result metadata without file contents |\n| `list_clone_files` | List generated file paths, sizes, and hashes |\n| `read_clone_files` | Read selected text files or binary URLs |\n| `get_clone_bundle` | Get a download URL for the generated app |\n\nExample agent prompt:\n\n```text\nUse the ditto MCP server to clone https:\u002F\u002Fexample.com as a Next.js app.\nWait for the job to finish, list the generated files, then read package.json,\nsrc\u002Fapp\u002Fpage.tsx, and src\u002Fapp\u002Fditto.css.\n```\n\n### Local CLI\n\n```bash\n# this git clone gets the ditto.site tool itself — the URL you clone into a\n# codebase comes later, as the argument to `npm run clone`.\ngit clone https:\u002F\u002Fgithub.com\u002Fion-design\u002Fditto.site.git\ncd ditto.site\n\nnpm ci\nnpx playwright install chromium\n\nnpm run clone -- https:\u002F\u002Fexample.com\u002F --out=.\u002Foutput\n```\n\nThe generated app lands under `output\u002F\u003Csite>\u002Fapp`. On success the CLI prints a\ncopy-paste-safe summary — a single quoted `cd … && npm install && npm run dev`\nline and pointers to the safe-to-edit files (`src\u002Fapp\u002Fcontent.ts`,\n`src\u002Fapp\u002Fcomponents\u002F`; the app's `AGENTS.md` has the full guide).\n\nTo skip the copy-paste entirely and go straight to a running preview:\n\n```bash\nnpm run clone -- https:\u002F\u002Fexample.com\u002F --serve        # clone, npm install, npm run dev\nnpm run clone -- https:\u002F\u002Fexample.com\u002F --open         # ...and open the browser too\n```\n\nCommon local variants:\n\n```bash\nnpm run clone -- https:\u002F\u002Fexample.com\u002F --mode=multi\nnpm run clone -- https:\u002F\u002Fexample.com\u002F --styling=css\nnpm run clone -- https:\u002F\u002Fexample.com\u002F --framework=vite\nnpm run validate-site -- runs\u002Fsite-example.com\u002F\u003Ctimestamp>\n```\n\nWithout `--out`, runs land under `runs\u002F\u003Csite>\u002F\u003Ctimestamp>\u002F` and a stable\n`runs\u002F\u003Csite>\u002Flatest` symlink always points at the newest clone, so scripts and\n`cd` targets don't depend on the timestamp.\n\n### Local REST And MCP Service\n\nQuick inline mode, with no database:\n\n```bash\nnpm ci\nnpx playwright install chromium\n\nSSRF_ALLOW_LOOPBACK=true npm run dev:api\n```\n\nThen call the local REST API:\n\n```bash\ncurl -sS -X POST \"http:\u002F\u002Flocalhost:8787\u002Fv1\u002Fclones\" \\\n  -H \"content-type: application\u002Fjson\" \\\n  -d '{\"url\":\"https:\u002F\u002Fexample.com\u002F\",\"options\":{\"mode\":\"single\"}}'\n```\n\nFor the queued service with Postgres and MinIO:\n\n```bash\ndocker compose up -d\ncp .env.example .env\n\nDATABASE_URL=postgresql:\u002F\u002Fpostgres:postgres@localhost:5432\u002Fditto_site \\\n  npm run db:migrate\n\nnpm run dev:api\nnpm run dev:worker\n```\n\nThe local MCP endpoint is `http:\u002F\u002Flocalhost:8787\u002Fmcp`.\n\n## What You Get\n\nA generated app includes:\n\n- a runnable Next.js or Vite React project,\n- reconstructed pages and route modules,\n- captured assets, fonts, icons, manifest files, and metadata,\n- `robots`, `sitemap`, `llms.txt`, and JSON-LD when discoverable,\n- small `ditto` runtime helpers for recognized interactions and motion,\n- generated `AGENTS.md` and `ARCHITECTURE.md` handoff docs.\n\nDelivery output is under `generated\u002Fapp\u002F` during validation and under\n`\u003Cout>\u002F\u003Csite>\u002Fapp` for CLI delivery.\n\n## How It Works\n\n```text\nURL\n  -> browser capture\n  -> normalized render IR\n  -> deterministic inference\n  -> app generation\n  -> asset materialization\n  -> optional validation\n```\n\nCapture records DOM, computed styles, layout boxes, source metadata, CSS, fonts,\nassets, screenshots, interaction states, and reproducible motion where it can be\nobserved safely. Unsupported app logic, auth, payments, personalization, and\narbitrary third-party JavaScript are not replayed.\n\nFor the detailed service API, see [docs\u002FSERVICE.md](docs\u002FSERVICE.md). For\ndeployment, see [docs\u002FDEPLOY.md](docs\u002FDEPLOY.md). For the development method\nbehind the compiler, see [docs\u002FMETHODOLOGY.md](docs\u002FMETHODOLOGY.md).\n\nHosted deployments should keep `\u002Fv1\u002Fclones*` and `\u002Fmcp` behind API-key auth.\nWhen `SIGNUP_ENABLED=true` in DB mode, the Resend-backed\n`POST \u002Fv1\u002Fsignup\u002Frequest` and `POST \u002Fv1\u002Fsignup\u002Fverify` flow can publicly mint\n`dtto_live_...` keys from verified email links while storing only key hashes.\nKeep `SIGNUP_DIRECT_ENABLED=false` in production unless direct unauthenticated\nminting is intentional.\n\n## Repository Map\n\n| Path | Purpose |\n| --- | --- |\n| `compiler\u002F` | deterministic capture, inference, generation, and validation |\n| `packages\u002Fcore\u002F` | compiler adapter and file-map helpers |\n| `packages\u002Fcli\u002F` | `ditto` CLI — unpack a clone result JSON into a project tree |\n| `packages\u002Fapi\u002F` | Hono REST API and MCP server |\n| `packages\u002Fdb\u002F` | Drizzle schema, migrations, repository, and queue wrapper |\n| `packages\u002Fstorage\u002F` | local and S3\u002FR2 artifact storage |\n| `packages\u002Fworker\u002F` | queued clone runner and optional verification |\n| `docs\u002F` | methodology, service, deployment, release, and responsible-use docs |\n| `examples\u002F` | benchmark results and visual evidence |\n\n## Responsible Use\n\nUse ditto.site only where you have the right to inspect, copy, transform, and\noperate on the target content. Do not use it for phishing, impersonation,\ncredential capture, bypassing access controls, or high-volume third-party\ncapture without permission.\n\nSee [docs\u002FRESPONSIBLE_USE.md](docs\u002FRESPONSIBLE_USE.md).\n\n## Contributing\n\n```bash\nnpm ci\nnpx playwright install chromium\nnpm run typecheck\nnpm test\n```\n\nBrowser tests require Chromium. Postgres-backed tests use `TEST_DATABASE_URL` or\nthe local compose stack. Changes that alter compiler output should include a\nfocused fixture or benchmark note.\n\nThe repository is MIT-licensed open source. The npm workspaces are intentionally\nmarked `private` until the package boundaries are ready for public npm\npublishing.\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md), [SECURITY.md](SECURITY.md),\n[SUPPORT.md](SUPPORT.md), and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).\n\n## License\n\n[MIT](LICENSE) © ion-design and contributors.\n","ditto.site 是一个将公开网页 URL 转换为可运行前端项目的代码生成工具。它通过真实浏览器渲染捕获页面结构与样式，输出确定性、字节稳定的 TypeScript 项目（默认为 Next.js App Router，也支持 Vite + React），并内置 Tailwind CSS 样式还原能力。整个流程不依赖 LLM 生成逻辑，而是基于结构化 DOM 分析与模板化代码合成，确保结果可复现、可版本控制。适用于快速克隆竞品页面、搭建原型、迁移静态站点或教学演示等需要从渲染结果反向生成工程化前端代码的场景。",2,"2026-07-08 04:30:02","CREATED_QUERY"]