> ## Documentation Index
> Fetch the complete documentation index at: https://docs.avocadostudio.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Try the demo

> One command, a nine-page site to click through, what turning chat on actually costs, and which of the surprises are deliberate.

This page is for looking at Avocado Studio for the first time and deciding
whether it deserves a second look. One command, a real site, about ten minutes.
It assumes you have no project, no API key and no opinion yet.

**It is a research preview.** Everything described here was measured on a real
run rather than imagined, and the last section is a list of things that look
like defects and are not. But it is early: expect rough edges, and expect the
surface to move between releases.

If you have already decided and want Avocado in a site of your own, that is a
different job: [add it to your site](/quickstart) is where it lives. This page
spends its length on what the demo shows you.

## One command

```bash theme={null}
npm create avocado-site@latest my-site
cd my-site && npm run dev
```

A directory name on the command line means the demo, with no questions asked —
somebody who has typed a name has already answered the only one that mattered.
Run it with no argument and it asks which of the two jobs you want first, then
the directory name.

The first command creates the project **and installs it**. One measured run on a
warm npm cache took 6.1 seconds end to end, wrote 29 files, asked nothing and
printed no errors. What lands in `node_modules` is Next 15.5.25, React 19, the
five Avocado runtime packages — `site-sdk`, `orchestrator-core`, `blocks`,
`shared`, `preview-adapter` — and the editor CLI as a dev dependency. Node 22 or
newer; the SQLite binding ships a prebuilt binary for it, and older Node falls
through to compiling from source.

Both ports are chosen at scaffold time from what is actually free on your
machine, starting at 3000 for the site and 4100 for the editor, so this runs
happily beside whatever else you have going.

`npm run dev` then starts two processes and prints a banner:

```
  ▲ Site     http://localhost:3000
  🥑 Editor   http://localhost:4100/?siteId=my-site&session=dev
             ↑ open this one
```

It opens that second URL for you. Opening the site directly shows the finished
pages with no editor around them, which reads as "the product did not start".

<Note>
  **The `?siteId=` is load-bearing, not decoration.** The CLI serves a prebuilt
  editor bundle and injects the orchestrator and preview origins into it, but not
  a site id — so without one the bundle falls back to its own built-in default and
  asks for a session this project never configured. It happens to render anyway,
  because the orchestrator bootstraps an unknown session from the adapter and gets
  the same nine pages. That is exactly what makes it worth pinning: the demo looks
  correct while the id in the URL and the id in the route file are different
  strings. The id is derived from your directory name, so `my-site/` gets
  `siteId=my-site`, and the generated route file agrees.
</Note>

Two processes, both on your machine, no third service to deploy:

```mermaid theme={null}
graph LR
  E["Editor UI<br/>localhost:4100<br/>served by the CLI"]
  subgraph N["Your Next.js app — localhost:3000"]
    S["The pages"]
    O["Orchestrator<br/>/api/avocado"]
  end
  C["content/pages.json"]
  E -->|"chat · ops · publish"| O
  E -->|"iframe preview"| S
  S -->|"reads"| C
  O -->|"rewrites on publish"| C
```

The orchestrator runs **inside** the Next.js app rather than beside it. That is
library mode, and it is the shape most integrations use.

## What to click, and what each click proves

You land on the Avocado Hub: nine pages, sixty blocks, and all eighteen built-in
page block types on screen. The two built-in types missing from it are the site
header and the footer, which belong to the site rather than to a page — so every
type a page can hold is somewhere in here, already filled with real copy.

<Steps>
  <Step title="Switch pages in the preview">
    Nine slugs, from a home page of thirteen blocks down to four-block pages
    like `/oranges` and `/olives`. Every block type you would have to implement
    on your own site is already rendering in one of them.
  </Step>

  <Step title="Turn on the element picker, then click a block">
    The arrow-and-sparkle button in the chat composer — hover it and it reads
    **Select an element** — toggles click-to-select. Click a heading, an image, a card. The overlay
    highlights the block and the chat learns what you have in mind. Escape turns
    it back off.

    This proves the part of an integration that is hardest to get right: a DOM
    node in the rendered page maps back to a typed block in the content.
  </Step>

  <Step title="Edit a field in the property panel">
    With a block selected, the panel on the side lists its real fields and
    writes real values. No model is involved — the panel drives the same typed
    operations the chat does, straight to the orchestrator.
  </Step>

  <Step title="Undo it">
    Every applied change carries an undo in the chat entry that produced it, and
    a global undo and redo sit directly above the message box (Ctrl+Z, or Cmd+Z
    on a Mac). Undo is server-side history, not a browser trick.
  </Step>

  <Step title="Publish">
    Publishing is a separate, explicit step. The editor posts to the
    orchestrator, which hands the current draft to the adapter, which rewrites
    `content/pages.json`. A measured run answered
    `{"status":"ready","written":true,"count":9}`, the file on disk changed, and
    the new text appeared in the rendered HTML.
  </Step>

  <Step title="Then try the chat">
    Chat is the one thing that wants a model — and it answers without one. That
    is the next section.
  </Step>
</Steps>

## No API key is needed to look around

Everything above works with nothing configured. Chat does too, with a smaller
promise, and the editor says so rather than failing.

With no key, a notice sits directly above the message box saying so, and naming
the **full path** of the `.env.local` to add a key to. The chat is answered by a
deterministic planner: a short list of literal matchers, not a small model. So
the first-screen suggestions change to the ones that planner can actually
execute:

| Suggestion                                                                                          | What it does                                                                                                 |
| --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| **Add testimonials**                                                                                | Inserts a `Testimonials` block at the end of the page. Offered only when the page does not already have one. |
| **Add a rich text section**                                                                         | Inserts a `RichText` block with placeholder prose.                                                           |
| **Set the meta description to "Fresh avocado recipes, wellness tips, and sustainability stories."** | Writes that exact string to the page's meta description.                                                     |

Three at most, and each one really edits the page — which is the claim the first
screen is making. A clean-room run fired the keyless suggestions on all nine
pages: 26 of 26 clicks produced at least one operation and every one applied.

The honest limit is the shape of the planner, not its size. It matches literal
phrases — "rich text", "add testimonials", "set the meta description to …" — so
a sentence of your own invention usually comes back asking for a key rather than
guessing. The suggestions are curated around that, deliberately.

## What a key costs

Chat with a real planner behind it is the thing worth evaluating, and it runs on
**your** key. Avocado never resells tokens, which also means nothing between you
and the provider is metering the spend for you.

### The three keys

One line in `.env.local`, in the project root — the same folder as
`package.json`:

| Variable               | What it buys                                                                                                                                                                                         |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ANTHROPIC_API_KEY`    | The best-tested planner. Chat only; no image generation.                                                                                                                                             |
| `OPENAI_API_KEY`       | Planner plus image generation, and gpt-image editing.                                                                                                                                                |
| `GOOGLE_GENAI_API_KEY` | Planner plus image generation. Also needs `npm install @google/genai` — an optional peer dependency, so no package manager installs it for you and a Gemini plan fails on the first call without it. |

**Restart the dev server after adding one.** Keys are read at startup, so a
running server will not pick one up, and the symptom is a key that appears to
have been ignored.

### What a click costs

With a key, the first screen offers different suggestions: **Translate the whole
page to German** always, one add-a-section suggestion chosen from what is
already on the page, and **Turn the hero into a bold call-to-action** when the
page has a hero.

They are not priced the same, and nothing in the product says so. From the
orchestrator's own per-turn telemetry in a clean-room run on 0.11.3:

| Chip                                     | Page                     | Cost                                |
| ---------------------------------------- | ------------------------ | ----------------------------------- |
| **Translate the whole page to German**   | `/oranges`, four blocks  | **\$0.1685**                        |
| Turn the hero into a bold call-to-action | `/oranges`               | \$0.0471                            |
| Edit CTA *(a follow-up chip)*            | `/community`             | \$0.0474                            |
| Add an FAQ section                       | `/oranges`               | **free** — answered without a model |
| Add a testimonials section               | `/`                      | **free**                            |
| Add a closing call-to-action section     | `/community`             | **free**                            |
|                                          | **7 turns, 78 k tokens** | **\$0.2749**                        |

Two things in that table matter more than the numbers. **The three `Add …`
chips are genuinely free**, because the deterministic planner answers them
without calling anything. And **one chip is most of the bill** — the one a
curious person is most likely to press first. Translating a page rewrites every
text-bearing block on it, so its cost scales with the page rather than with the
sentence you typed; \$0.1685 is its price on the *cheapest* page in the demo,
and a four-block page is the cheapest thing it can be asked to do. Nothing in
the UI distinguishes the free chips from that one.

<Warning>
  Treat those as one run, not a price list. They come from a single session on one
  provider and model, against this demo's page sizes. Your numbers will differ.
  What generalises is the shape: whole-page rewrites cost roughly an order of
  magnitude more per click than single-block edits, and the difference is
  invisible at the moment you click.
</Warning>

Keep a spend limit on the key you paste in, as you would for anything else that
calls a model on your behalf.

## What you will notice

Four things read as defects on a first run. All four are working as designed,
and each one looks broken if nobody says so first.

**Click-to-select is off until you turn it on.** The picker defaults to off, so
a correct install looks inert: you click a heading in the preview and nothing
happens. The cursor button in the composer is the switch. It defaults off
because the preview is a live site — links, tabs and carousels should work when
you are reading rather than editing — and a picker that intercepts every click
makes the page impossible to browse.

**The first render of the editor takes a few seconds.** On a cold `.next`, the
editor's first load of the site measured 3.83 seconds to first byte while Next
compiled the route. Warm, the same request runs in 45 to 73 ms. The first paint
is the compiler, not the product. The cold load itself is clean: 26 requests,
none of them a failure.

**Publishing is a separate step, and the site proves it.** Open
`localhost:3000` directly and you see the **published** page; the draft appears
only under an editor context — the URL the editor's preview frame uses. So
after an edit, the plain site is meant to look unchanged until you publish, and
`content/pages.json` is meant to be byte-identical. Both are true, and both are
how you check that publish did something.

This paragraph used to say the opposite — that development resolves an editor
context for every request, so the bare URL shows unpublished edits. That was
true once and is not now, and it was the worst thing on this page to get wrong:
it told a reader to distrust the one check that tells them whether publish
worked.

**`npm run build && npm start` gives you a site that renders and an editor that
cannot load.** That is the production gate, and it is deliberate. The
orchestrator mounted inside your app can edit and publish your content, so with
`NODE_ENV=production` and no credential configured it refuses every request
rather than running open. A default that is convenient in development and
catastrophic in production has to pick production.

It says so once in the build output and once again at boot:

```
[auth] library mode: closed — NODE_ENV=production with no credential —
every request is refused. Set ACCESS_PASSWORD_HASH or
ORCHESTRATOR_ACCESS_TOKEN, or pass config.auth.
```

During `next build` that is a warning, with an added clause noting it was
evaluated at build time and that the variables belong in the deployment's
environment. At runtime it is an error, because then it means requests really
are being refused. Three things open the gate: `ACCESS_PASSWORD_HASH` (the
sha256 of a password the editor prompts for), `ORCHESTRATOR_ACCESS_TOKEN` (a
fixed bearer token, for scripts and CI), or an `auth` hook passed to
`createOrchestrator` in code — including `auth: () => true`, which is a line you
type on purpose and cannot arrive at by omission. A few paths stay open either
way, because they answer before any caller could hold a credential: `/health`,
`/auth/status`, `/auth/verify`, and the generated-images the rendered page loads
in `<img>` tags.

Set one of those and the editor reaches the password screen instead of the
error.

That sentence is newer than it sounds, and it took three releases to make true.
A production deployment has to be told where its editor lives, and each of the
three ways that could go wrong went wrong in turn: the generated orchestrator
route was never told at all (0.11.2), the answer omitted a header the editor
sends (0.11.3), and then the right answer went only to one of the two ways the
editor's address can be spelled (0.11.4). Every one of them had the same
symptom — the editor reporting it could not reach a server that was answering
`200` to `curl` the whole time — because a response a browser rejects is
indistinguishable, from inside the browser, from a server that is down.

A project scaffolded on 0.11.4 or later needs none of this explained: it writes
`EDITOR_CORS_ORIGINS`, `NEXT_PUBLIC_EDITOR_ORIGIN` and
`ORCHESTRATOR_CORS_ORIGINS` into `.env.local` with a comment on each saying
which code reads it, and `localhost` and `127.0.0.1` are treated as the same
address wherever you write either. An older project needs those lines added by
hand — see [the changelog](/changelog#0-11-2) for which does what, and
[publishing](/integration/publishing).

What a production build is good for is seeing what a crawler gets: per-page
titles, descriptions, Open Graph tags, a canonical link, and a real 404 on an
unknown slug. In this demo those come out identically in development, so
`npm start` confirms them rather than being the only way to see them. It is not
the way to try the editor. For that, stay in `npm run dev`, which needs none of
the above.

## Where to go next

<CardGroup cols={2}>
  <Card title="Wire it into an existing site" icon="wrench" href="/quickstart">
    The quickstart is the other half of this story: a prompt for your coding
    agent, or the same wiring as one shell block, against a Next.js app you
    already have.
  </Card>

  <Card title="Bring your real site in" icon="globe" href="/sites">
    Your components, your CMS, your content. Three honest paths, measured in
    days rather than minutes.
  </Card>

  <Card title="The SDK" icon="book" href="/integration">
    Routes, the page factory, custom blocks, CMS adapters, publishing — every
    seam an integration has to satisfy.
  </Card>

  <Card title="How it works" icon="diagram-project" href="/how-it-works">
    What happens between your sentence and the changed page, if you would rather
    understand the model before wiring anything.
  </Card>
</CardGroup>
