--- name: Frontend build config type: architecture-map-page slice: 05-core-storage-hotkey-build last_verified: 2026/05/09 --- # Frontend build config > **Where you are:** [Architecture map](../README.md) → [Core, Storage, Hotkey, Build](README.md) → Frontend build config **Plain English summary.** Three small config files that pin how Vite, SvelteKit, and TypeScript-flavoured-jsconfig behave. The settings are deliberately minimal — the heavy lifting (routes, components, stores) lives in slice 1. ## At a glance - Files: `vite.config.js` (612 bytes), `svelte.config.js` (214 bytes), `jsconfig.json` (366 bytes). - External: Vite, SvelteKit, Tailwind v4 plugin. - Consumers: every dev / build invocation. ## `vite.config.js` ```js import { defineConfig } from "vite"; import { sveltekit } from "@sveltejs/kit/vite"; import tailwindcss from "@tailwindcss/vite"; const host = process.env.TAURI_DEV_HOST; export default defineConfig(async () => ({ plugins: [sveltekit(), tailwindcss()], clearScreen: false, server: { port: 1420, strictPort: true, host: host || false, hmr: host ? { protocol: "ws", host, port: 1421 } : undefined, watch: { ignored: ["**/src-tauri/**"], }, }, })); ``` ### Notable settings - **`port: 1420 + strictPort: true`.** Tauri's `beforeDevCommand` and the `run.sh` poll both target port 1420. `strictPort` means Vite errors out instead of falling back to 1421+, so a stuck process is loud. - **`clearScreen: false`.** Vite's default behaviour clears the terminal. Disabled here so the Tauri logs stay visible alongside Vite's. - **`TAURI_DEV_HOST` env var.** When set, Vite binds to the network interface and runs HMR on port 1421. Used for testing on a real device while developing on the desktop. Unset on local dev. - **`watch: { ignored: ["**/src-tauri/**"] }`.** Vite's file watcher ignores the Tauri directory; otherwise every Cargo build artefact change would trigger an HMR pass. ### Why Tailwind v4's Vite plugin Tailwind 4 ships a `@tailwindcss/vite` plugin that replaces the v3 PostCSS pipeline. The plugin reads CSS-in-JS / `@import "tailwindcss"` directly from the source files. The repo has no `tailwind.config.js` or `postcss.config.cjs` because v4 derives configuration from the CSS itself. ## `svelte.config.js` ```js import adapter from "@sveltejs/adapter-static"; const config = { kit: { adapter: adapter({ fallback: "index.html", }), }, }; export default config; ``` ### Notable settings - **`adapter-static`** — Magnotia is a Tauri app, not a server-rendered web app. Static adapter outputs a fully pre-rendered HTML/JS bundle that Tauri serves from its embedded webview. - **`fallback: "index.html"`** — every unknown route serves `index.html`, which lets the SvelteKit client router take over. Without this, `/history` typed directly into the URL bar would 404. ## `jsconfig.json` ```json { "extends": "./.svelte-kit/tsconfig.json", "compilerOptions": { "allowJs": true, "checkJs": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "skipLibCheck": true, "sourceMap": true, "strict": true, "moduleResolution": "bundler" } } ``` ### Notable settings - **`extends ".svelte-kit/tsconfig.json"`** — SvelteKit generates a tsconfig at `npm run dev:frontend`'s `svelte-kit sync` step. We extend it to add our own strictness flags. - **`allowJs: true + checkJs: true`** — the codebase is JavaScript with TypeScript-aware type checking via JSDoc. svelte-check enforces this. - **`strict: true`** — full strict mode. Null safety, no implicit any, etc. svelte-check is the gate (CI runs `npm run check`). - **`moduleResolution: "bundler"`** — TypeScript 5's bundler-aware resolution. Matches Vite's behaviour (no fake CommonJS round-trip). ## `static/` The static folder maps 1:1 to the served root. Listed here for completeness: - `favicon.png` — app icon (web). - `pcm-processor.js` — the audio worklet (cross-link: [`static-assets.md`](static-assets.md)). - `svelte.svg`, `tauri.svg`, `vite.svg` — placeholder logos. - `fonts/` — `atkinson-hyperlegible-next.woff2`, `instrument-serif-italic.woff2`, `jetbrains-mono.woff2`, `lexend-variable.woff2`, `opendyslexic.woff2`. - `textures/` — `grain.png`. ## Watch-outs - **`vite.config.js` is async-returning a config but does no awaiting.** Worth the simplification to a synchronous `defineConfig({ ... })` if no async setup is added. - **`svelte.config.js` does not use `vitePreprocess`.** SvelteKit 2 + Svelte 5 do not require it (Svelte 5's compiler reads JSDoc directly). Keeps the config minimal. - **`jsconfig.json` extends a generated file.** Running `svelte-kit sync` is part of `npm run check` and `npm run dev:frontend`. CI runs it explicitly. - **No `vitest` config.** Frontend unit tests are not part of the current workflow. Coverage is via `svelte-check` (types), e2e dogfooding, and Rust integration tests at the slice-2 boundary. ## See also - [Dev launcher and scripts](dev-launcher-and-scripts.md) - [Static assets](static-assets.md) - [Slice 1 frontend](../01-frontend/README.md) — the routes / components this config builds.