You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This document explains how the frontend codebase is built, what libraries are involved, and how different parts connect at compile time and runtime. It's written for developers who need to modify the build pipeline or understand how the frontend works.
4
-
5
-
## Overview
6
-
7
-
The frontend is a Svelte 5 single-page application bundled with Rollup. It uses TypeScript for type safety, Tailwind CSS v4 for styling, and a generated SDK for type-safe API calls. The build outputs static files to `public/build/` which are served by nginx in production or by a custom HTTPS dev server during development.
8
-
9
-
For details on the Svelte 5 runes API and migration patterns, see [Svelte 5 Migration](svelte5-migration.md).
3
+
The frontend is a Svelte 5 single-page application bundled with Rollup. It uses TypeScript for type safety, Tailwind CSS v4 for styling, and a generated SDK for type-safe API calls. The build outputs static files to `public/build/` which are served by nginx in production or by a custom HTTPS dev server during development. For details on the Svelte 5 runes API and migration patterns, see [Svelte 5 Migration](svelte5-migration.md).
10
4
11
5
```mermaid
12
6
graph LR
@@ -44,11 +38,7 @@ graph LR
44
38
45
39
## Rollup configuration
46
40
47
-
The `rollup.config.js` file configures the entire build pipeline. It produces ES modules with code splitting, enabling parallel loading of vendor code and application code.
48
-
49
-
### Entry point
50
-
51
-
The build starts from `src/main.ts`, which imports the API client setup, mounts the Svelte `App` component using Svelte 5's `mount()` function, and imports global CSS:
41
+
The `rollup.config.js` file configures the entire build pipeline, producing ES modules with code splitting for parallel loading of vendor and application code. The build starts from `src/main.ts`, which imports the API client setup, mounts the Svelte `App` component using Svelte 5's `mount()` function, and imports global CSS:
52
42
53
43
```typescript
54
44
import { mount } from'svelte';
@@ -61,57 +51,20 @@ const app = mount(App, {
61
51
});
62
52
```
63
53
64
-
### Code splitting
65
-
66
-
Rollup splits the bundle into chunks to improve load performance. The `manualChunks` configuration separates large dependencies:
67
-
68
-
| Chunk | Contents |
69
-
|-------|----------|
70
-
|`vendor`| Svelte, @mateothegreat/svelte5-router|
71
-
|`codemirror`| All CodeMirror packages for the editor |
72
-
| Application chunks | Route components and shared code |
73
-
74
-
This means users don't re-download vendor code when application code changes, and the editor chunk only loads when needed.
75
-
76
-
### Plugins
77
-
78
-
The plugin pipeline processes files in order:
54
+
Rollup splits the bundle into chunks to improve load performance. The `manualChunks` configuration separates large dependencies into a `vendor` chunk (Svelte, svelte5-router), a `codemirror` chunk (all CodeMirror packages), and application chunks for routes and shared code. This means users don't re-download vendor code when application code changes, and the editor chunk only loads when needed.
79
55
80
-
1.**replace** — Substitutes `process.env.VITE_BACKEND_URL` with an empty string, allowing relative API paths
81
-
2.**svelte** — Compiles `.svelte` files with TypeScript preprocessing via `svelte-preprocess`, with `runes: true` enabled for Svelte 5
82
-
3.**postcss** — Processes CSS through PostCSS, extracting styles to `bundle.css`
83
-
4.**typescript** — Compiles TypeScript files with source maps
84
-
5.**json** — Allows importing JSON files
85
-
6.**resolve** — Resolves `node_modules` imports for browser usage, preferring ES modules
86
-
7.**commonjs** — Converts CommonJS modules to ES modules
The plugin pipeline processes files in order: **replace** substitutes `process.env.VITE_BACKEND_URL` with an empty string for relative API paths; **svelte** compiles `.svelte` files with TypeScript preprocessing and `runes: true` for Svelte 5; **postcss** processes CSS and extracts styles to `bundle.css`; **typescript** compiles TypeScript with source maps; **json** allows importing JSON files; **resolve** handles `node_modules` imports preferring ES modules; **commonjs** converts CommonJS to ES modules; and **terser** (production only) minifies JavaScript, removes console logs, and runs two compression passes.
88
57
89
-
### Development server
90
-
91
-
In development mode (`npm run dev`), Rollup watches for changes and a custom HTTPS server starts automatically. The server handles two responsibilities:
92
-
93
-
1.**Static file serving** — Serves files from `public/`, falling back to `index.html` for SPA routing
94
-
2.**API proxying** — Forwards `/api/*` requests to the backend container over HTTPS
95
-
96
-
The proxy uses a custom `https.Agent` that trusts the local CA certificate at `/shared_ca/mkcert-ca.pem`, allowing secure communication with the backend during development. The server listens on port 5001.
58
+
In development mode (`npm run dev`), Rollup watches for changes and a custom HTTPS server starts automatically. The server serves files from `public/` with SPA fallback to `index.html`, and proxies `/api/*` requests to the backend container over HTTPS. The proxy uses a custom `https.Agent` that trusts the local CA certificate at `/shared_ca/mkcert-ca.pem`, allowing secure communication with the backend during development on port 5001.
97
59
98
60
## TypeScript configuration
99
61
100
-
The `tsconfig.json` configures TypeScript compilation:
101
-
102
-
- Target: ES2020 with ESNext modules
103
-
- Strict mode enabled
104
-
- Module resolution set to bundler mode for Rollup compatibility
105
-
- Svelte component types enabled via `svelte-preprocess`
106
-
107
-
TypeScript catches type errors during development and the build fails if any exist, preventing broken code from reaching production.
62
+
The `tsconfig.json` targets ES2020 with ESNext modules, enables strict mode, sets module resolution to bundler mode for Rollup compatibility, and enables Svelte component types via `svelte-preprocess`. TypeScript catches type errors during development and the build fails if any exist, preventing broken code from reaching production.
108
63
109
64
## API SDK generation
110
65
111
66
The frontend uses a generated SDK for type-safe API calls instead of manual fetch requests. This SDK is created from the backend's OpenAPI specification using `@hey-api/openapi-ts`.
Run `npm run generate:api` to regenerate the SDK. The configuration in `openapi-ts.config.ts` specifies:
124
-
125
-
- Input: `../docs/reference/openapi.json` (the backend's OpenAPI spec)
126
-
- Output: `src/lib/api/` with Prettier formatting
127
-
- Plugins: TypeScript types, SDK functions, and fetch client
128
-
129
-
### Generated files
130
-
131
-
| File | Purpose |
132
-
|------|---------|
133
-
|`types.gen.ts`| TypeScript interfaces for all request/response models |
134
-
|`sdk.gen.ts`| Function for each API endpoint, fully typed |
135
-
|`client.gen.ts`| HTTP client with interceptor support |
136
-
|`index.ts`| Re-exports types and SDK functions |
137
-
|`setup.ts`| Manual file that configures the client (not generated) |
138
-
139
-
### Client configuration
140
-
141
-
The `setup.ts` file configures the generated client:
76
+
Run `npm run generate:api` to regenerate the SDK. The configuration in `openapi-ts.config.ts` specifies the input as `../docs/reference/openapi.json`, outputs to `src/lib/api/` with Prettier formatting, and generates TypeScript types, SDK functions, and a fetch client. The generated files include `types.gen.ts` (TypeScript interfaces for all request/response models), `sdk.gen.ts` (functions for each API endpoint, fully typed), `client.gen.ts` (HTTP client with interceptor support), and `index.ts` (re-exports for convenient imports). There's also a manual `setup.ts` file that configures the client with relative URLs and cookie credentials, and adds an interceptor that automatically attaches CSRF tokens to mutating requests:
The interceptor automatically adds CSRF tokens to mutating requests, pulling the token from the auth store. This happens transparently for all SDK calls.
159
-
160
-
### Usage pattern
161
-
162
-
Components import SDK functions and types directly:
93
+
Components import SDK functions and types directly. The SDK returns `{ data, error }` tuples, making error handling explicit without try/catch boilerplate:
The SDK returns `{ data, error }` tuples, making error handling explicit without try/catch boilerplate.
176
-
177
101
## Tailwind CSS v4
178
102
179
-
The frontend uses Tailwind CSS v4 with the new CSS-first configuration. Unlike v3, there's no `tailwind.config.js` — all configuration lives in CSS.
180
-
181
-
### PostCSS integration
182
-
183
-
PostCSS processes CSS through `@tailwindcss/postcss`:
184
-
185
-
```javascript
186
-
// postcss.config.cjs
187
-
module.exports= {
188
-
plugins: {
189
-
"@tailwindcss/postcss": {},
190
-
},
191
-
}
192
-
```
193
-
194
-
### CSS configuration
195
-
196
-
The `src/app.css` file contains all Tailwind configuration using v4's new at-rules:
103
+
The frontend uses Tailwind CSS v4 with the new CSS-first configuration. Unlike v3, there's no `tailwind.config.js` — all configuration lives in CSS. PostCSS processes CSS through `@tailwindcss/postcss`, and the `src/app.css` file contains all Tailwind configuration using v4's new at-rules:
197
104
198
105
```css
199
-
/* Import Tailwind */
200
106
@import"tailwindcss";
201
107
202
-
/* Forms plugin */
203
-
@plugin "@tailwindcss/forms" {
204
-
strategy: class;
205
-
}
206
-
207
-
/* Class-based dark mode */
108
+
@plugin "@tailwindcss/forms" { strategy: class; }
208
109
@variant dark (&:where(.dark, .dark *));
209
110
210
-
/* Custom theme tokens */
211
111
@theme {
212
112
--color-primary: #3b82f6;
213
113
--color-bg-default: #f8fafc;
214
114
--font-sans: 'Inter', ui-sans-serif, system-ui;
215
-
/* ... */
216
115
}
217
116
218
-
/* Custom utilities */
219
117
@utility animate-fadeIn {
220
-
animation: fadeIn 0.3sease-in-out;
118
+
animation: var(--animate-fadeIn);
221
119
}
222
120
```
223
121
224
-
### Theme structure
225
-
226
-
The theme defines semantic color tokens for both light and dark modes:
227
-
228
-
| Token | Light | Dark |
229
-
|-------|-------|------|
230
-
| `bg-default` | `#f8fafc` | `#0f172a` |
231
-
| `fg-default` | `#1e293b` | `#e2e8f0` |
232
-
| `border-default` | `#e2e8f0` | `#334155` |
233
-
234
-
Components use these tokens (e.g., `bg-bg-default dark:bg-dark-bg-default`) for consistent theming. The `@variant dark` rule enables the `.dark` class on `<html>` to trigger dark mode.
122
+
The theme defines semantic color tokens for both light and dark modes (`bg-default`, `fg-default`, `border-default`, etc.) which components use for consistent theming. The `@variant dark` rule enables the `.dark` class on `<html>` to trigger dark mode. Styles are organized into Tailwind layers: **base** for element defaults, form styles, scrollbars, and CodeMirror overrides; **components** for reusable patterns like `.btn`, `.card`, and `.form-input-standard`.
235
123
236
-
### Layer organization
124
+
## Svelte stores and state
237
125
238
-
Styles are organized into Tailwind layers:
126
+
The frontend uses a hybrid approach to state management. Svelte stores in `src/stores/` handle global, shared state: `auth.ts` manages authentication state, login/logout, and CSRF tokens; `theme.ts` handles theme preference with localStorage persistence; `toastStore.ts` manages the toast notification queue; and `notificationStore.ts` handles server notifications with pagination. Stores use the generated SDK for API calls and persist state to localStorage where appropriate. The auth store exposes a `csrfToken` store that the API client interceptor reads for request signing.
239
127
240
-
- **base** — Element defaults, form styles, scrollbars, CodeMirror overrides
241
-
- **components** — Reusable patterns like `.btn`, `.card`, `.form-input-standard`
242
-
243
-
## Svelte stores and runes
244
-
245
-
The frontend uses a hybrid approach to state management:
| `theme.ts` | Theme preference (light/dark/auto) with localStorage persistence |
253
-
| `toastStore.ts` | Toast notifications queue |
254
-
| `notificationStore.ts` | Server notifications with pagination |
255
-
256
-
Stores use the generated SDK for API calls and persist state to localStorage where appropriate. The auth store exposes a `csrfToken` store that the API client interceptor reads for request signing.
Svelte 5 runes (`$state`, `$derived`, `$effect`) handle component-local state. Store subscriptions remain unchanged from Svelte 4 — the `$storeName` syntax auto-subscribes to any store:
259
129
260
130
```svelte
261
131
<script lang="ts">
262
132
import { isAuthenticated } from '../stores/auth';
263
133
264
-
// Component-local reactive state
265
134
let loading = $state(false);
266
135
let items = $state<Item[]>([]);
267
-
268
-
// Derived values
269
136
let itemCount = $derived(items.length);
270
-
271
-
// Store subscription (unchanged from Svelte 4)
272
-
// $isAuthenticated auto-subscribes to the store
273
137
</script>
274
138
275
139
{#if $isAuthenticated}
@@ -279,14 +143,6 @@ Stores use the generated SDK for API calls and persist state to localStorage whe
279
143
280
144
For detailed patterns and migration guidance, see [Svelte 5 Migration](svelte5-migration.md).
281
145
282
-
## Build commands
283
-
284
-
| Command | Purpose |
285
-
|---------|---------|
286
-
| `npm run dev` | Start Rollup in watch mode with HTTPS dev server |
287
-
| `npm run build` | Production build with minification |
288
-
| `npm run generate:api` | Regenerate SDK from OpenAPI spec |
289
-
290
146
## File structure
291
147
292
148
```
@@ -313,64 +169,30 @@ frontend/
313
169
└── openapi-ts.config.ts # SDK generator config
314
170
```
315
171
316
-
## Local development
317
-
318
-
Start the development stack:
319
-
320
-
```bash
321
-
# From project root
322
-
docker compose up -d
323
-
324
-
# In frontend directory
325
-
npm install
326
-
npm run dev
327
-
```
328
-
329
-
The dev server runs at `https://localhost:5001`. API requests proxy to the backend container. Changes to `.svelte`, `.ts`, and `.css` files trigger automatic rebuilds.
330
-
331
-
### Regenerating the API client
172
+
## Build commands
332
173
333
-
When backend endpoints change:
174
+
| Command | Purpose |
175
+
|---------|---------|
176
+
| `npm run dev` | Start Rollup in watch mode with HTTPS dev server |
177
+
| `npm run build` | Production build with minification |
178
+
| `npm run generate:api` | Regenerate SDK from OpenAPI spec |
334
179
335
-
1. Update the backend and restart it
336
-
2. Fetch the new OpenAPI spec (the docs workflow does this automatically)
337
-
3. Run `npm run generate:api`
338
-
4. Fix any TypeScript errors from changed types
180
+
## Local development
339
181
340
-
### Adding new routes
182
+
Start the development stack from the project root with `docker compose up -d`, then in the frontend directory run `npm install && npm run dev`. The dev server runs at `https://localhost:5001` with API requests proxying to the backend container. Changes to `.svelte`, `.ts`, and `.css` files trigger automatic rebuilds.
341
183
342
-
1. Create a component in `src/routes/`
343
-
2. Add a `<Route>` entry in `App.svelte`
344
-
3. Use SDK functions for API calls
345
-
4. Use semantic color tokens for styling
184
+
When backend endpoints change, update the backend and restart it, fetch the new OpenAPI spec (the docs workflow does this automatically), run `npm run generate:api`, and fix any TypeScript errors from changed types. To add new routes, create a component in `src/routes/`, add a `<Route>` entry in `App.svelte`, use SDK functions for API calls, and use semantic color tokens for styling.
346
185
347
186
## Production build
348
187
349
-
The production build runs `npm run build`, which:
350
-
351
-
1. Compiles TypeScript with source maps
352
-
2. Processes Svelte components in production mode (no dev warnings)
353
-
3. Extracts and minifies CSS
354
-
4. Splits code into chunks
355
-
5. Minifies JavaScript with Terser (removes console.log)
356
-
6. Outputs to `public/build/`
357
-
358
-
The Docker build copies `public/` to nginx, which serves static files and proxies `/api/` to the backend.
188
+
The production build runs `npm run build`, which compiles TypeScript with source maps, processes Svelte components in production mode without dev warnings, extracts and minifies CSS, splits code into chunks, minifies JavaScript with Terser removing console.log calls, and outputs everything to `public/build/`. The Docker build copies `public/` to nginx, which serves static files and proxies `/api/` to the backend.
359
189
360
190
## Troubleshooting
361
191
362
-
### TypeScript errors after SDK regeneration
363
-
364
-
If the backend changed response types, update components to match. The SDK provides exact types — check `types.gen.ts` for the new structure.
365
-
366
-
### Styles not applying
367
-
368
-
Ensure the class exists in Tailwind's default utilities or is defined in `app.css`. Check for typos in semantic token names (e.g., `bg-default` vs `bg-bg-default`).
369
-
370
-
### Dev server certificate errors
192
+
If you see TypeScript errors after SDK regeneration, check `types.gen.ts` for the new structure and update components to match the changed response types.
371
193
372
-
The dev server requires certificates at `./certs/server.key` and `./certs/server.crt`, and the CA at `/shared_ca/mkcert-ca.pem`. Run the cert-generator container first via Docker Compose.
194
+
For styles not applying, ensure the class exists in Tailwind's default utilities or is defined in `app.css`, and check for typos in semantic token names (e.g., `bg-default` vs `bg-bg-default`).
373
195
374
-
### API calls failing in development
196
+
Dev server certificate errors mean the certificates at `./certs/server.key` and `./certs/server.crt` are missing, or the CA at `/shared_ca/mkcert-ca.pem` isn't available — run the cert-generator container first via Docker Compose.
375
197
376
-
Verify the backend is running and healthy. The dev server proxies to `https://backend:443` — check Docker networking if the container can't resolve the hostname.
198
+
If API calls fail in development, verify the backend is running and healthy; the dev server proxies to `https://backend:443`, so check Docker networking if the container can't resolve the hostname.
0 commit comments