# Make it real: from a chat prototype to a project (https://lazyfrog.dev/docs/guide/make-it-real-from-a-chat-prototype-to-a-project)

{/* Generated by scripts/generate-docs.ts from services/gateway/src/guide.md. Do not edit. */}

The user made something inside their AI app (a Claude artifact, a page ChatGPT previewed from a
code block, a ChatGPT Site) and wants it to become a real app: its own address, data that is
kept, visitors' accounts, their domain. There is no repository to import: you send its files, and
the import lays them out as a project. The prototype stays as it is where it was made.

1. **Its source.** In the chat where it was made you have it; otherwise ask the user to paste it,
   or to carry on in that chat. Don't change it yet: the import reads it as it is.
2. **The import.** `project` `import` with `source: {files: [{path, content}, …]}` (or `project`
   `create` with the same `source`; `name` optional): paths as the prototype has them
   (`index.html`, `App.tsx`, `styles.css`), text as it is, an image or a font as `encoding:
   "base64"`; up to 100 files and 3 MB together (a binary file up to 1 MB). Bigger: import the
   page or component and what it needs, then `fs_write` the rest into the project (the refusal
   says so). It returns a job: follow it with `job` `get`. The import detects what the prototype
   is and lays it out:
   - one HTML page (its script and style inline or from a CDN): a static site, the page at
     `workspace/site/index.html`; a form on it works without a server (see Forms);
   - one React component that keeps nothing: a Vite React app served as a static service
     (`workspace/src/<Name>.tsx`, mounted by `src/main.tsx` and `index.html`, with
     `allowedHosts` set for the preview's address), its imports in `package.json`;
   - a component that calls the AI app (`window.claude`, `window.openai`), keeps data (an
     artifact's storage, `localStorage`) or reads another service: a Next.js app
     (`workspace/components/<Name>.tsx` shown by `app/page.tsx`, with `lib/db.ts` on
     `DATABASE_URL`, a first migration and the health route) so the server side has a place.
   Nothing in the prototype is rewritten except the entry point that mounts it. Dependencies are
   installed, the preview started and scanned, and the adaptation committed. `preview_screenshot`:
   it should look like the prototype before anything changes.
3. **The checklist.** The job's result carries `checklist`, and the same words are the project's
   app info (`project` `about`), so the owner sees them too: what moved (file → place), what must
   change (each call the import found, with its file and lines, and what to do about it), what
   LazyFrog adds. Plan from it ("Before you build something new"): the prototype is most of the
   brief, so save what it does as `mustHaves` and the checklist's change items as `doneWhen`
   items, and ask only what it can't answer (the name, whose data it shows). The checklist lists
   facts found in the code; it doesn't see everything (a library that keeps state, an image from
   elsewhere), so read the code once yourself.
4. **What must change**, each one a `doneWhen` item:
   - **Kept data** (`localStorage`, `sessionStorage`, IndexedDB, an artifact's own storage, a
     Site's D1 database, which is SQLite) lives in one browser or on the AI's platform. Move it
     to PostgreSQL through `DATABASE_URL`: a migration (`migrations/0002_….sql`) with a table for
     each kind of thing (not one table of keys and values), API routes in the app that read and
     write it, and the page calling them. SQLite's SQL mostly carries over (`?` becomes `$1`,
     `AUTOINCREMENT` an identity column). Data the user wants to keep: ask them to export it from
     the prototype (a JSON or CSV download it can offer), then load it with `db_query`.
   - **Calls to the AI app** (`window.claude`, `window.openai`) work only inside that app, on the
     viewer's own account. An AI feature becomes an API route of the app that calls the model
     through LazyFrog's AI gateway (`$LAZYFROG_AI_URL`, `http://ai.local`; see "Email, AI and
     PDFs for the app") with the owner's provider key and a monthly cap. Say plainly that the
     owner now pays for it, not each visitor.
   - **Connected-app data** (the user's mail, calendar, files or a business tool, read through
     their AI app's connectors): the app can't use those connections. Either the owner connects
     the source to the app itself (its API, with a key through `secrets request`), or they export
     the data and you import it into a table. Tell them which, in plain words.
   - **Imports from a CDN** (`https://esm.sh/…`, unpkg, jsdelivr): the package is in
     `package.json` already; import it by name and remove the address.
   - **Sign-in** (a Site's own, or none at all): the app's own accounts, see "Sign-in for the
     app's users".
   - **Files** (uploads, a Site's file storage): `/data`, see "Files and uploads". Images loaded
     from elsewhere: copy them into the project.
   - **Links to the prototype's address**: the project's own address.
   Keep the app info current as you go (`project` `about` with the whole record), so the owner's
   page says what is done.
5. **What LazyFrog adds**, in a line or two for the user: a public address anyone can open
   without an account (`https://<project>.lazyfrog.app`, and their own domain on a paid plan),
   forms that reach them, email through their own provider, sign-in for their visitors, a preview
   to try changes on before they go live, snapshots, and an export whenever they want one.
6. **Publish** when they say so (see Publishing).
