Internal console for FoldForge — submit protein-design workflows, watch live progress over SSE, and inspect results (structures, quality metrics, designed sequences).
Talks to the gateway HTTP API. Types
are generated from the gateway's OpenAPI spec, so the client stays in lockstep
with the contract.
- Next.js (App Router) + React 19 + TypeScript — UI
- TanStack Query — data fetching / caching / polling
- openapi-typescript + openapi-fetch — type-safe client generated from the spec
- Tailwind CSS — styling
- Server-Sent Events — live step progress via the gateway's
/v1/workflows/{id}/events - Pure-TypeScript 3D structure viewer — a dependency-free PDB/mmCIF backbone
renderer on
<canvas>(no NGL/Mol*), so there's no heavyweight 3D dependency to install
The OpenAPI spec is vendored (not a git submodule) at spec/gateway.v1.yaml
so codegen and CI need no submodule init. Resync from the proto repo when the
gateway contract changes:
npm run sync-spec # copy ../proto/openapi/gateway.v1.yaml -> spec/
npm run gen-api # spec/ -> src/lib/api.gen.ts (also runs on prebuild)Override the proto location with PROTO_DIR=/path/to/proto npm run sync-spec.
cp .env.example .env.local # set NEXT_PUBLIC_GATEWAY_URL + GATEWAY_API_TOKEN
pnpm install
npm run gen-api # generate the typed client
npm run dev # http://localhost:3000The browser never sees the API token: all calls go to Next route handlers under
/api/* (src/app/api/[...path]/route.ts), which proxy to the gateway with the
bearer token attached server-side. SSE streams pass through unbuffered.
spec/gateway.v1.yaml vendored OpenAPI spec (source of truth for types)
scripts/sync-spec.mjs resync spec from ../proto
src/lib/
api.gen.ts generated (gitignored)
client.ts openapi-fetch clients (server + browser)
types.ts schema type aliases
hooks.ts TanStack Query hooks + SSE stream hook (auto-reconnect)
structure.ts pure PDB/mmCIF parser → CA backbone trace
format.ts pure formatters (relativeTime/formatBytes/formatDuration)
query-provider.tsx QueryClientProvider
src/app/
page.tsx workflow list (paginated, label filter, empty/error states)
submit/page.tsx submit (presets + live DAG preview w/ param hints + Cmd/Ctrl+Enter)
workflows/[id]/page.tsx live detail: progress, per-step + total duration, outputs
(size + copy-URI + download), metrics, sequences, 3D viewer, retry/cancel
error.tsx app-level error boundary (branded recovery, not a blank crash)
not-found.tsx branded 404 for unmatched routes
api/[...path]/route.ts gateway proxy (token injection + SSE passthrough + Last-Event-ID)
src/components/
StructureViewer.tsx canvas 3D backbone viewer (honest mock labeling)
DagPreview.tsx topological-wave DAG layout, colored by tool, per-node param hints
StateBadge.tsx CopyButton.tsx NavBar.tsx ui.tsx
Artifact download links point at the gateway's tenant-scoped
/v1/workflows/{id}/artifacts/{key} endpoint (through the auth-injecting proxy), so
they only resolve for a workflow the caller owns.
| command | what |
|---|---|
npm run dev |
dev server |
npm run build |
production build (runs gen-api first) |
npm run gen-api |
regenerate the typed client from the spec |
npm run sync-spec |
pull the latest spec from the proto repo |
npm run typecheck |
tsc --noEmit |
npm run lint |
eslint |
npm test |
node test runner (pure-logic unit tests) |