This is the frontend for Sol, built with Next.js (App Router), React 19, TypeScript, Tailwind CSS v4, and shadcn/ui.
The whole app is one page: a node canvas for generating images and video, at /.
- The canvas is the app.
/renders it full-bleed with no site chrome. Which document is open is resolved client-side (last opened → most recent → create), so there is no id in the URL and no redirect on the way in. - The browser never talks to a model vendor. It posts a provider id and a prompt to the .NET API, which decrypts the key, calls upstream and stores the result. That keeps API keys off the client, sidesteps CORS, and works with providers that allow-list egress IPs.
- Everything is device-scoped. There are no accounts; the device cookie from
POST /api/v1/device/handshakeis the only credential. Seedocs/device-identification.md. - Feature code lives under
features/<domain>; shared primitives undercomponents/uiandcomponents/layout.features/canvasowns the graph,features/aithe provider config,features/settingsthe settings dialog. - API calls run from the browser against the Next.js rewrite (
/api/*→ the .NET API) so the device cookie stays first-party. Seenext.config.tsfor why that rewrite exists.
The API surface is documented in docs/ai-canvas-api.md.
The app needs the .NET API and its dependencies:
# Terminal 1 — Postgres, Redis, RabbitMQ
docker compose up -d
# Terminal 2 — .NET API (http://localhost:5298)
dotnet run --project ../src/Sol.Api
# Terminal 3 — Next.js app (http://localhost:3000)
npm run devCopy .env.example to .env.local (already present in this checkout) and adjust if the API
runs on a different port.
Before anything can be generated, open the settings dialog (gear icon, top right) and add a provider with an API key.
npm run test # Vitest — canvas graph logic, sync conflicts, config parsing
npm run test:e2e # Playwright — requires the .NET API running on :5298Playwright runs serially: every spec drives the same API and database, so concurrency produces contention rather than coverage.
npm run lint # ESLint
npx tsc --noEmit # Type-check
npm run build # Production build