# Frontend modules Module reference for `src/frontend/src/`. The SPA is Vue 3 + Vite + Pinia + Fomantic UI. State is held in Pinia stores; HTTP via axios; streaming inference via a `fetch`-based composable that parses [SSE](../glossary.md#sse-server-sent-events). For an overview of how the SPA fits into the deployed container, see [explanation/architecture.md](../explanation/architecture.md). ## Project layout ``` src/frontend/src/ ├── main.js # bootstrap (Pinia, router, theme init) ├── App.vue # shell with NavBar, RouterView, GraphBackground ├── api/ # axios client + per-method API wrappers ├── composables/ # reusable logic (SSE stream) ├── stores/ # Pinia stores (one per demo + theme) ├── router/ # vue-router routes incl. SPA 404 ├── views/ # page-level components ├── components/ # feature-grouped components ├── data/ # static data (CV) └── styles/ # design tokens + theme + responsive CSS ``` ## `main.js` — bootstrap Imports Fomantic CSS, design-token sheets, the App shell, the router and the theme store. Creates a Pinia instance, wires it into the app, calls `useThemeStore().init()` to apply the persisted theme, and mounts to `#app`. ## `api/` — HTTP client and endpoint wrappers ### `client.js` Creates the axios instance. The base URL is `${import.meta.env.VITE_API_BASE_URL ?? 'http://localhost:8000'}/api/v1`. The `??` (not `||`) is load-bearing: the production env file sets `VITE_API_BASE_URL=` to an empty string so the SPA hits the same origin, and `??` preserves that empty string while `||` would have fallen back to `localhost`. Also exports `apiError(error)` — pulls the structured `{ code, message, details }` envelope back out of an axios error. ### `coins.js`, `graphGeneration.js`, `kgAnomaly.js`, `health.js` Thin wrappers around the discovery endpoints. Each function returns the parsed JSON response. The streaming endpoints are *not* hit through these wrappers — they go through `useSseStream` directly. ## `composables/useSseStream.js` The SSE client. Implements: - `InferenceHttpError` — typed error with `status`, `code`, `details` (matches the backend error envelope). - `useSseStream({ path, body, onProgress, onPreview, onResult, signal })` — POSTs to `${baseURL}${path}`, reads the response body as a stream, parses SSE frames, and dispatches them. Honours an `AbortSignal` for client-side cancellation. The base URL is computed the same way as in `client.js` — same `??` rule applies. This composable is the only place in the frontend that talks to the streaming endpoints. Stores instantiate it from their action functions. ## `stores/` — Pinia state ### `theme.js` Stores the active theme (`light` / `dark` / `auto`), persists it to `localStorage`, and toggles `` classes on change. `init()` reads the persisted value at app start. ### `coinsDemo.js` Holds: - The dataset / algorithm / query-structure metadata fetched from `/coins/*`. - The user's currently selected `(dataset_id, queryStructure, algorithm, anchors, variables, relations, topK)`. - `loading`, `error`, `errorCode`, `result` for the prediction call. Getters expose `selectedStructure` and `allowedAlgorithms` (the intersection of the algorithms the dataset has and the ones that support the chosen query structure). Actions wrap the API client functions. ### `multiproxanDemo.js` Mirrors `coinsDemo.js` for the graph-generation demo. State includes the chosen `model_type` (discrete / continuous), `sampling_mode` (standard / multiprox), generation parameters, the live preview URL, the `kg_log_likelihood` trace (only for the kg-anomaly correction case but kept here too for future symmetry), and the continuation `state` blob between Gibbs rounds. ### `kgAnomalyDemo.js` The KG-anomaly demo state. Holds the picked sample subgraph, the user's edits, the running progress, the preview reel and the `kg_log_likelihood` trace. ## `router/index.js` Five named routes: | Path | Name | View | |---|---|---| | `/` | `home` | `HomeView.vue` | | `/cv` | `cv` | `CVView.vue` | | `/demos/coins` | `demo-coins` | `CoinsView.vue` (lazy) | | `/demos/multiproxan` | `demo-multiproxan` | `MultiProxAnView.vue` (lazy) | | `/demos/kganomaly` | `demo-kganomaly` | `KgAnomalyView.vue` (lazy) | | `/:pathMatch(.*)*` | `not-found` | `NotFoundView.vue` | The catch-all is the [Lara Croft 404](../glossary.md#hf-space) — the Django backend serves the SPA shell for unknown paths and Vue Router takes it from there. `meta.title` is applied to `document.title` in `router.afterEach`. ## `views/` — pages | View | Purpose | |---|---| | `HomeView.vue` | Hero intro, demo preview cards, fact of the day, system status. | | `CVView.vue` | CV rendering driven by `data/cv.js`. | | `NotFoundView.vue` | Lara Croft 404 with random hero image and credits. | | `demos/CoinsView.vue` | The COINs demo (query construction, prediction results). | | `demos/MultiProxAnView.vue` | Graph-generation demo (parameter panel, preview reel, chain GIF). | | `demos/KgAnomalyView.vue` | KG-anomaly demo (subgraph picker, change list, log-likelihood plot). | ## `components/` — feature groups Components are grouped by feature folder, not by type. Cross-feature primitives live in `common/` and `layout/`. | Folder | Notable components | |---|---| | `background/` | `GraphBackground.vue`, `FloatingMotif.vue` — animated graph drawing background. | | `common/` | `EtaTracker.js`, `FitText.vue`, `InferenceErrorBanner.vue`, `PreviewReel.vue`, `SseProgressBar.vue`, `StepTimeline.vue` — primitives used across demos. | | `layout/` | `NavBar.vue`, `PageSection.vue`, `ThemeToggle.vue`. | | `home/` | `HeroIntro.vue`, `DemoPreviewCard.vue`, `FactOfTheDay.vue`, `SystemStatus.vue`. | | `cv/` | `Education.vue`, `WorkExperience.vue`, `Publication.vue`, `ProjectCard.vue`, `Skill.vue`, `Language.vue`, `LogoOrAcronym.vue`, `Reference.vue`, `SocialLink.vue`. | | `coins/` | `AlgorithmSelector.vue`, `QueryStructurePicker.vue`, `QueryGraph.vue`, `QueryDescription.vue`, `SearchableEntityDropdown.vue`, `SearchableRelationDropdown.vue`, `SearchablePicker.vue`, `PredictionList.vue`, `ResultsDashboard.vue`, `CommunityRankCallout.vue`, `TimingPanel.vue`. | | `multiproxan/` | `ParametersPanel.vue` — parameter form for both sampling modes. | | `kganomaly/` | `SampleSubgraphCard.vue`, `ChangeList.vue` — subgraph picker and per-edge change explanations. | ## `data/cv.js` A static JS module exporting the CV's structured data (education, work, publications, etc.). Updating the CV is a plain code edit — no DB. ## `styles/` | File | Purpose | |---|---| | `tokens.css` | Design tokens (color, spacing, font sizes). | | `theme.css` | Light / dark theme variable bindings. | | `responsive.css` | Breakpoint helpers. | ## Build and env - `npm run dev` — Vite dev server on `http://localhost:5173`, proxied to the Django dev server at `http://localhost:8000` via `VITE_API_BASE_URL` from `.env.development`. - `npm run build` — emits `dist/` (the production bundle that the container copies into Django's `dist/` so WhiteNoise serves it). - `.env.development` — `VITE_API_BASE_URL=http://localhost:8000`. - `.env.production` — `VITE_API_BASE_URL=` (empty for same-origin). ## See also - [explanation/architecture.md](../explanation/architecture.md) — how the SPA is served by the Django container. - [reference/sse-protocol.md](sse-protocol.md) — exactly what the demo stores parse from streaming responses. - [guides/local-development.md](../guides/local-development.md) — running the frontend dev server alongside Django.