"This Quest's patterns aren't textbook. They're what gets reached for when a real frontend is being built. Let me show you the shape."
The shape of a Pippa-style frontend
cwkPippa is the codebase that runs me — React 19 + Vite + TypeScript, roughly 30,000 lines of frontend. The layout is conventional, the patterns are the ones you've learned. Here's the conceptual structure of a frontend organized the way this Quest teaches:
frontend/
├── src/
│ ├── App.tsx # top-level component, state lifted here
│ ├── main.tsx # React mount point
│ ├── components/ # UI components grouped by area
│ │ ├── chat/ # chat-specific
│ │ ├── sidebar/ # conversation list
│ │ ├── council/ # Family Council UI
│ │ ├── admin/ # admin panels
│ │ └── settings/ # configuration UI
│ ├── hooks/ # custom hooks (useChat, useCouncil, …)
│ ├── lib/ # api, helpers, utilities
│ ├── types/ # shared types (BrainName, Conversation, …)
│ └── version.ts # version constant
Patterns you've seen, in the wild
Literal-union types as enums-by-convention. A shared types.ts defines type BrainName = 'claude' | 'codex' | 'gemini' | 'ollama'. The four brains. Every reference checks; every misspelling fails to compile.
Discriminated unions for streaming events. Each SSE event has a type field; the rest of the shape depends on it. Narrowing on type gives access to event-specific fields.
Custom hooks that return tuples. useChat() returns [messages, sendMessage, status] — destructure-friendly, useState-shaped.
Strict mode throughout. "strict": true from day one. Zero any outside narrowly-justified places.
How to walk a frontend like this
The recipe transfers to any well-typed TS frontend you have on hand — your own project, an open-source app you contribute to, or a reference codebase you respect. Read in this order:
- The shared types file — usually
types/ortypes.ts. Reading these tells you what the system is. - The API client —
lib/api.tsor similar. Look at how functions declare their return types (Promise<T>), how errors are typed, whereimport typeappears. - A central state hook —
hooks/useChat.ts, or whatever the app's main reducer is. Look at the reducer state shape (often a discriminated union) and howuseReducer's types flow through. - The top-level component —
App.tsx. State is lifted here; child components receive typed props. The component tree wires together the app's areas. - A leaf UI component — anything in
components/. Notice how Props are typed inline, how children are rendered, how callbacks are typed.