Wirely is an AI web UI ideation and editing workspace built with Next.js App Router.
Create a project, pick enabled models/providers, generate one or many page outputs, then iterate HTML in a canvas-style editor with safe previewing and persisted history.
- Multi-mode generation:
single_pageconcept_variantsinformation_architecture
- Multi-output planning for coordinated page sets (up to 3 outputs per run).
- BYOK provider settings per user (Google, OpenRouter, Z.ai, Unsplash).
- Encrypted user API key storage (AES-256-GCM, user-bound AAD, key versioning).
- Quality gating and repair pass for generated HTML.
- Stock image slot planning plus Unsplash resolution and metadata injection.
- Canvas editor with persistent project/page/conversation history in Postgres.
- Safe iframe preview constraints for generated content.
- Route/runtime guardrails:
- rate limits
- request body size limits
- structured logging with sensitive-field redaction
- Next.js 16 (App Router) + React 19 + TypeScript (
strict) - Bun scripts/test runner
- Tailwind CSS v4 + Radix/shadcn-style components
- Zustand editor state store
- Drizzle ORM + Neon Postgres
- AI SDK providers:
- Google (
@ai-sdk/google) - OpenRouter (
@openrouter/ai-sdk-provider) - Z.ai (OpenAI-compatible client via
@ai-sdk/openai)
- Google (
- Clerk authentication (Google OAuth flow)
- Home (
/) loads user session, project history, and enabled model settings. - Settings (
/setting/*) manages profile, provider keys, and enabled models. - Editor (
/wire/[id]) loads pages + conversation history. - Generation requests hit:
POST /api/projects/[projectId]/generate(authenticated proxy)POST /api/wire/[id](planning/generation/repair pipeline)
- Outputs and messages persist to Postgres (
projects,project_pages,conversation_messages,generation_runs,generation_outputs). - Preview renderer sanitizes output and applies iframe security constraints.
- Bun (latest stable recommended)
- Postgres database (Neon or compatible)
- Clerk app configured for Google OAuth
bun installCreate .env.local from the example:
cp .env.example .env.localGenerate an encryption secret for user API keys:
openssl rand -base64 32Use the generated value for USER_API_KEY_MASTER_SECRET_BASE64.
bun run db:migratebun run devOpen http://localhost:3000.
- Sign in via
/login. - Add provider keys at
/setting/provider. - Enable models at
/setting/model. - Create a project from home and generate outputs.
From .env.example:
| Variable | Required | Purpose |
|---|---|---|
DATABASE_URL |
Yes | Postgres connection string. |
USER_API_KEY_MASTER_SECRET_BASE64 |
Yes | 32-byte base64 secret for encrypting BYOK provider keys. |
USER_API_KEY_KEY_VERSION |
Yes | Positive integer key version for new encryptions. |
USER_API_KEY_PREVIOUS_MASTER_SECRET_BASE64 |
Optional | Previous 32-byte base64 secret for key rotation compatibility. |
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY |
Yes | Clerk frontend key. |
CLERK_SECRET_KEY |
Yes | Clerk backend key. |
NEXT_PUBLIC_CLERK_SIGN_IN_URL |
Yes | Sign-in route, defaults to /login. |
REQUEST_BODY_MAX_BYTES |
Optional | Max JSON body size (default 65536). |
DB_POOL_MAX |
Optional | DB pool size (default 5). |
DB_POOL_CONNECTION_TIMEOUT_MS |
Optional | DB connection timeout ms (default 3000). |
DB_POOL_IDLE_TIMEOUT_MS |
Optional | DB idle timeout ms (default 10000). |
GOOGLE_GENERATIVE_AI_API_KEY |
Optional | Server key used for project title generation fallback. |
OPENROUTER_API_KEY |
Optional | Present in env example for compatibility; user-level key settings are used for generation. |
Notes:
- Generation requests require user-scoped provider keys saved in settings.
- Unsplash is configured in-app via BYOK (
/setting/provider), not via top-level env var.
| Command | Description |
|---|---|
bun run dev |
Start local Next.js dev server. |
bun run build |
Production build. |
bun run start |
Start production server. |
bun run lint |
Run ESLint. |
bun run test |
Run Bun tests. |
bun run db:generate |
Generate Drizzle migration files. |
bun run db:migrate |
Apply Drizzle migrations. |
bun run db:backfill:page-html |
Currently points to a missing script path (see Known Limitations). |
GET /api/projects- list current user projects.POST /api/projects- create a project (and initial page).POST /api/projects/[projectId]/generate- authenticated proxy into wire generation route.POST /api/wire/[id]- main planning/generation/repair pipeline.GET /api/profile/ai-settings- read provider-key/model settings flags.PATCH /api/profile/ai-settings- update encrypted provider keys and enabled models.POST /api/projects/[projectId]/pages- create page.PATCH /api/projects/[projectId]/pages/[pageId]- update page.DELETE /api/projects/[projectId]/pages/[pageId]- delete page.
- Rate limiting on
POST /api/wire/[id]:- 5 requests per minute
- 30 requests per hour
- keyed by
userId + IP + route
- JSON body guard rejects oversized payloads with
413. - Structured logger redacts sensitive fields and avoids stack traces in production.
- Security headers are applied globally in
next.config.ts. - Generated HTML is sanitized and constrained before iframe preview.
app/ # Next.js routes, pages, API handlers
components/ # Editor UI, canvas, renderers
lib/ # Core logic (auth, db, generation pipeline, security)
store/ # Zustand stores
hooks/ # React hooks
drizzle/ # SQL migrations and metadata snapshots
test/ # Bun tests
Run quality checks before opening a PR:
bun run lint
bun run testIf you change schema:
bun run db:generate
bun run db:migratedb:backfill:page-htmlcurrently referencesscripts/backfill-page-html-from-conversation.ts, but that file is not present in this repository snapshot.- Rate limiting uses an in-memory store by default (not shared across instances).
- Fork the repo.
- Create a branch for your change.
- Keep changes scoped and include tests when behavior changes.
- Run lint/tests locally.
- Open a pull request with context, rationale, and validation notes.
No LICENSE file is currently present in this repository.
Add one before distributing or accepting external contributions under a specific open source license.