Tech stack
open-design
Tech stack
Languages & runtime
- TypeScript-first. Project-owned entrypoints, modules, scripts, tests, reporters and configs default to TS. Residual
.js/.mjs/.cjsfiles need an explicit generated/vendor/compatibility reason and passpnpm check:residual-js(scripts/check-residual-js.ts). - Node.js ~24 (per
AGENTS.md— "Runtime target is Node~24"). - pnpm 10.33.2 via Corepack, monorepo (
pnpm-workspace.yaml) coveringapps/*,packages/*,tools/*,e2e. - Some daemon source files use
// @ts-nocheck(e.g.apps/daemon/src/server.ts:1,agents.ts:1) — the team gets type checking via the contracts package while keeping the dynamic Express-style code looser. Strict packages:contracts,sidecar-proto,sidecar,platform.
Frontend
- Next.js 16 App Router + React 18 (
apps/web/).- Single dynamic catch-all:
apps/web/app/[[...slug]]/page.tsx. Returns<ClientApp />+generateStaticParams(). The whole app is a SPA; routing handled at runtime byapps/web/src/router.tsreadingwindow.location. - No
app/api/*— every API call goes to the daemon via proxy.
- Single dynamic catch-all:
- Tailwind-style utility CSS in
index.css(no Tailwind toolchain visible; styling is hand-rolled with CSS variables matching the OKLch design-system tokens). - i18n: lightweight
apps/web/src/i18n/(manual dictionaries for English / Simplified Chinese / Japanese / Korean / Deutsch / Traditional Chinese, mirroring the README translations). - State in
apps/web/src/state/:config.ts— app config persisted tolocalStorage(api keys, model selection, theme, agent ids).projects.ts— REST helpers that hit/api/projects.maxTokens.ts+litellm-models.json— vendored model context/output caps from BerriAI/litellm.
- Iframe-based preview:
apps/web/src/runtime/srcdoc.ts:17-36— wraps HTML artifacts for<iframe sandbox="allow-scripts">previews; injects a localStorage shim so cross-origin sandbox doesn'tSecurityError.apps/web/src/runtime/react-component.ts:9-80— loads React 18 + Babel Standalone via CDN inside the iframe and transpiles user JSX at preview time.
- Markdown rendered by a hand-rolled mini-parser in
apps/web/src/runtime/markdown.tsx(no external lib). - ZIP export is a pure client-side stored-mode encoder with CRC32 in
apps/web/src/runtime/zip.ts:43-80— no JSZip dep.
Backend (daemon)
- Express + multer (
apps/daemon/src/server.ts:2-3) — one Node process listens onlocalhost:7456(overridable). HTTP + Server-Sent Events. - better-sqlite3 (per imports in
apps/daemon/src/db.ts) — single SQLite file at~/.open-design/app.sqlite(orOD_DATA_DIR-relative). node:child_processspawnfor every code-agent CLI; stdio piping with type-aware stream parsers.puppeteerfor PDF export (referenced indocs/architecture.md:181-183as the planned PDF path).
Desktop / packaged
- Electron (
apps/desktop/,apps/packaged/). - esbuild for sidecar bundling (
apps/daemon/sidecar/,apps/web/sidecar/,packages/sidecar/esbuild.config.mjs, etc.). pkg-style packaging is aspirational in docs; the active path is the Electron sidecar runtime.
Agent transport protocols
The daemon supports five distinct stream formats, one per family of CLIs:
| Format | Used by | Parser |
|---|---|---|
claude-stream-json |
Claude Code | apps/daemon/src/claude-stream.ts |
copilot-stream-json |
GitHub Copilot CLI | apps/daemon/src/copilot-stream.ts |
acp-json-rpc (Agent Client Protocol) |
Devin, Hermes, Kimi, Kiro | apps/daemon/src/acp.ts |
pi-rpc |
Pi (mariozechner/pi-ai) |
apps/daemon/src/pi-rpc.ts |
json-event-stream |
Codex, Gemini, OpenCode, Cursor Agent | apps/daemon/src/json-event-stream.ts |
plain (fallback) |
Qwen and any other | apps/daemon/src/json-event-stream.ts |
Each maps the CLI's native event shape onto a unified { type: 'text_delta' | 'thinking_delta' | 'tool_use' | 'tool_result' | 'status' | 'usage' | 'done', … } event the browser consumes via SSE.
Media providers
apps/daemon/src/media-models.ts and media.ts describe a provider router dispatching to:
| Provider | Surfaces | Notes |
|---|---|---|
| OpenAI | image (gpt-image-2/1, dall-e-3), audio (gpt-4o-mini-tts) | Auto-detects Azure OpenAI by base URL. Sync. |
| Volcengine (ByteDance) | image (Seedream), video (Seedance 2.0 t2v / i2v with audio) | Async task polling. |
| xAI Grok | image, video, audio (grok-imagine-*) |
Hybrid sync + async. |
| HyperFrames | video (HTML→MP4) | Local renderer, no API key. |
| Stubs (intentional) | placeholder generators | Gated by OD_MEDIA_ALLOW_STUBS=1; otherwise 503. |
Deployment
- Vercel (
apps/daemon/src/deploy.ts:1-616) — direct upload of an HTML file set + asset rewrites + optional analytics hook injection. Token in~/.open-design/vercel.json. - Local-only is the default topology.
Why these picks (where the source/docs hint at it)
- Next.js over a Vite SPA because of SSR for marketing + Vercel-deploy-as-first-class (
docs/architecture.md:107). - SQLite over a hosted DB — single-user, single-machine MVP, plain-file-friendly. The team deliberately rejected SQLite for artifacts (kept those as plain files) but kept SQLite for projects/conversations/messages metadata.
- Express over Fastify/Hono — pragmatic, well-known, fine for
localhost:7456traffic. - Hand-rolled markdown / ZIP / direction library — keeps frontend bundle small and avoids supply-chain footprint where the surface is small.
- Vendored React 18 + Babel for iframe — borrowed from Open CoDesign because it works and reinventing it isn't the bet.
- Five-format stream parsers instead of one canonical protocol — the bet is "agents have already converged on a few strong implementations; talk to all of them rather than reinvent" (
docs/agent-adapters.md:7-9). The cost is five parsers; the payoff is broad agent coverage.