website / docs /reference /frontend-modules.md
Andrej Janchevski
docs: add technical documentation set
175b650
# 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 `<html>` 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.