# Build your first MIDL site on localhost — agent quickstart

A self-contained starter for an AI agent (or developer) to stand up a first **MIDL** website running on
`http://localhost:3000`, using the public `@wisepunk/*` packages. Every step has a command to run and a
way to verify it worked — follow them in order.

**End state:** a tiny Node project that renders a MIDL document to HTML and serves it on localhost, with
no framework and no auth.

> Want the full capability map + how to discover the component vocabulary programmatically? See the
> companion [AGENT-REFERENCE.md](AGENT-REFERENCE.md). You can also enumerate every component without docs:
> `import { describeVocabulary, EXAMPLE_DOCS } from '@wisepunk/core'`.

---

## 0. What MIDL is (read once)

- A **MIDL document** is plain JSON describing a page as semantic *frames* (components) — not HTML.
- A **renderer** + a **pack** turn that JSON into HTML. The pack decides the look; the document stays the same.
- Two render targets ship on public npm: **`@wisepunk/renderer-html`** (→ an HTML string, framework-free)
  and **`@wisepunk/renderer-svelte`** (Svelte components). This guide uses `renderer-html`.
- A typed write-API client, **`@wisepunk/sdk`**, is optional (used in step 6 to pull a live page).

```js
renderMidl(doc, { pack, theme }) → { html, themeCss, validation }
```

---

## 1. Prerequisites

- **Node 18+** (for built-in `fetch`, used in step 6). Verify:

```sh
node -v        # expect v18 or higher
```

---

## 2. Create the project

```sh
mkdir my-midl-site && cd my-midl-site
npm init -y
npm pkg set type=module          # the code below is ESM
npm install @wisepunk/renderer-html @wisepunk/core
```

**Verify:** `ls node_modules/@wisepunk` lists `core` and `renderer-html`. (These are **public** packages — no token, no `.npmrc`.)

---

## 3. Write a MIDL document

Create **`src/home.mjs`**. A document is `{ midl, ns, frames }`; each frame has an `id`, a `type`
(namespaced like `ui:Hero`), and type-specific fields. A frame with `children` composes other frames by `id`.

```js
// src/home.mjs
export const home = {
  midl: '0.1',
  ns: { ui: 'https://midl.ai/ui#' },
  frames: [
    { id: 'page', type: 'ui:Page', children: ['hero', 'cta'] },
    { id: 'hero', type: 'ui:Hero', title: 'My first MIDL site' },
    { id: 'cta',  type: 'ui:Button', text: 'Get started', variant: 'default' }
  ]
};
```

---

## 4. Serve it on localhost

Create **`server.mjs`** — a zero-dependency Node HTTP server that renders the document on each request:

```js
// server.mjs
import { createServer } from 'node:http';
import { renderMidl } from '@wisepunk/renderer-html';
import { home } from './src/home.mjs';

const PORT = 3000;
const PACK = '@midl/pack-minimal-html'; // bare semantic tags, zero CSS needed

createServer((_req, res) => {
  const { html, themeCss, validation } = renderMidl(home, { pack: PACK });
  if (!validation.valid) console.warn('MIDL validation issues:', JSON.stringify(validation.errors));
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
  res.end(
    `<!doctype html><html><head><meta charset="utf-8">` +
    `<title>My MIDL site</title><style>${themeCss}</style></head>` +
    `<body>${html}</body></html>`
  );
}).listen(PORT, () => console.log(`MIDL site → http://localhost:${PORT}`));
```

Run it:

```sh
node server.mjs
```

**Verify** (in another terminal):

```sh
curl -s http://localhost:3000 | grep -o '<main>.*</main>'
# → <main><section><h1>My first MIDL site</h1></section><button>Get started</button></main>
```

The Hero's `title` renders as a real `<h1>` (the page's primary heading) — heading structure lives in the
document, so screen readers, crawlers, and agents all see it. If you see that line, your first MIDL site
is live on localhost. The server log should **not** print any
"validation issues."

---

## 5. Swap the look (packs)

The document never changes — change the **pack**. Set `PACK` in `server.mjs`:

- **`@midl/pack-minimal-html`** — plain semantic tags, no CSS. Works out of the box (used above).
- **`@midl/pack-tailwind-shadcn`** — Tailwind + shadcn class names + `themeCss` CSS variables. The HTML
  will carry utility classes; to *see* it styled, your page needs Tailwind (e.g. the Tailwind Play CDN)
  with the shadcn variables. For a first localhost run, stick with `minimal-html`.

> Pack ids are **literal strings**, not packages to install — they're built into `@wisepunk/core`.

---

## 6. (Optional) Pull a live page with the SDK

Render isn't only for hardcoded docs — you can fetch a stored page from a MIDL deployment and render it.
The public demo API serves the read endpoints without auth.

```sh
npm install @wisepunk/sdk
```

```js
// live.mjs
import { createClient } from '@wisepunk/sdk';
import { renderMidl } from '@wisepunk/renderer-html';

const midl = createClient({
  baseUrl: 'https://midl-web.rene-dad.workers.dev',
  apiKey: 'public-demo' // GET is public; the SDK just needs a non-empty key
});

const page = await midl.frames.get({ slug: 'home' });          // live document
const { html } = renderMidl(page.frame.content, { pack: '@midl/pack-minimal-html' });
console.log(html);
```

```sh
node live.mjs   # prints the live home page rendered to HTML
```

To **author** pages (create/update/delete) you need a real API key; `midl.frames.create({ content, slug })`
returns `{ id, slug }`. See `docs/WRITE-API-MANUAL.md`.

---

## Reference

### The MIDL document
- `midl` — schema version (`'0.1'`).
- `ns` — namespace map; `ui` points at the UI component vocabulary.
- `frames` — array of components. Each: `{ id, type, ...fields }`. A `ui:Page` lists child `id`s in
  `children`; children render in that order.
- Verified starter components: `ui:Page` (`children`), `ui:Hero` (`title` → `<h1>`),
  `ui:Heading` (`level` 1–6 + `text` → `<h1>`–`<h6>`), `ui:Text` (`text` → `<p>`), `ui:Button` (`text`,
  `variant`). The authoritative component set + their allowed fields live in the pack's manifest — if a
  field/type isn't allowed, `renderMidl` returns `validation.valid === false` with `validation.errors`
  (an array of messages) describing the problem. **Always check `validation` rather than guessing.**

### `renderMidl(doc, options)` → result
- `options.pack` — pack id string (default registry pack if omitted).
- `options.theme` — optional token map (DESIGN.md tokens) → mapped to `result.themeCss` (`:root { … }`).
- `result.html` — the rendered HTML string.
- `result.themeCss` — `:root` CSS variables for the theme (empty string when no theme given).
- `result.validation` — `{ valid, errors }` from checking the doc against the pack manifest (`errors` is a string array).

### Machine-readable surfaces (for discovery)
Against a live deployment (e.g. `https://midl-web.rene-dad.workers.dev`):
- `GET /render/<slug>.midl.json` — the raw MIDL document for a page.
- `GET /render/<slug>.facts.json` — extracted facts (agent/RAG view).
- `GET /api/agents/openapi.json` and `/.well-known/agent.json` — the API + tool descriptions.

---

## Agent gotchas

- **ESM only:** set `"type": "module"` (`npm pkg set type=module`) or the `import`s fail.
- **Packs aren't npm packages:** don't `npm install @midl/pack-*` — pass the id string to `renderMidl`; it's bundled in `@wisepunk/core`.
- **`renderMidl` returns a string, not a server** — you serve `html` (+ `<style>${themeCss}</style>`) however you like.
- **`minimal-html` needs no CSS; `tailwind-shadcn` does** — use `minimal-html` for the first run to avoid an unstyled page.
- **Node 18+** for the SDK's `fetch` (step 6).
- **Trust `validation`** — if `validation.valid` is false, fix the document to match the manifest before assuming a render bug.

---

## Done check

1. `node server.mjs` logs `MIDL site → http://localhost:3000`.
2. `curl -s http://localhost:3000 | grep -q 'My first MIDL site'` exits 0.
3. No "validation issues" in the server log.

All three → you've built and served your first MIDL site. Next: read [AGENT-REFERENCE.md](AGENT-REFERENCE.md)
for the full vocabulary + the validate→repair→render loop, add more frames (try `ui:Heading`), switch to the
`tailwind-shadcn` pack with Tailwind, or pull live content with the SDK.
