File size: 7,795 Bytes
175b650 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | # 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.
|