# The website and the in-browser build

The site at `xpsflow.pages.dev` is a static build: a landing page, these docs, and the
workbench running entirely in the browser. Nothing on it talks to a server.

## How the browser build works

`scripts/build_site.py` assembles `dist/`:

| path | what it is |
|---|---|
| `index.html`, `site.css`, `site.js` | the landing page |
| `docs/*.html` and `docs/*.md` | each documentation page, with its Markdown twin linked as `rel="alternate"` |
| `app/` | the workbench front end from `src/xpsflow/web/static`, re-pointed at a Web Worker |
| `app/worker.js` | loads Pyodide, installs the wheels and runs `xpsflow.web.browser.BrowserApp` |
| `app/browser-backend.js` | patches `fetch` and `window.open` so every `/api/...` call goes to the worker |
| `app/pyodide/` | the pinned Pyodide runtime and the wheels it needs, resolved from its lock file |
| `app/wheels/` | the pure-Python wheels not in Pyodide (lmfit, asteval, dill) and the xpsflow wheel |
| `_headers`, `_redirects`, `robots.txt`, `sitemap.xml`, `llms.txt`, `.well-known/security.txt` | hosting and discovery files |

`BrowserApp` mirrors the FastAPI endpoints over one `Session`, so the same JavaScript runs
against either backend. The first visit downloads about 25 MB (cached afterwards); fits run at
roughly a third of native speed. PDF export and the tool-calling assistant need the local
install, because WeasyPrint has no WebAssembly build and the assistant needs a model endpoint.

## Building and previewing

```bash
python scripts/build_site.py --out dist --no-runtime   # pages only, fast
python scripts/build_site.py --out dist                 # with the runtime
python -m http.server --directory dist 8000
```

## Deployment

`.github/workflows/deploy.yml` builds the site on every push to `main` that touches the
package, the docs or the site, and publishes it to Cloudflare Pages with
`wrangler pages deploy`. It needs two repository secrets: `CLOUDFLARE_API_TOKEN` (a token
with Cloudflare Pages: Edit) and `CLOUDFLARE_ACCOUNT_ID`. A manual deploy from a machine with
wrangler logged in is `npx wrangler pages deploy dist --project-name=xpsflow`.

## Headers

The `_headers` file sets a Content Security Policy with `'wasm-unsafe-eval'` (required for
WebAssembly), restricts workers and connections to the site's own origin, denies framing, and
marks the runtime, wheels, fonts and vendor files as immutable so browsers cache them for a
year.
