# Frontend Architecture — Theseus

Single-page application served by the Rust backend via `include_str!`.
Vanilla JavaScript (14 modules), no build step, no framework.
Dark theme with 30+ CSS custom properties (`:root` in `style.css`).

---

## External Dependencies

Loaded as global `<script>` tags in `index.html`:

| Library | Version | Purpose |
|---------|---------|---------|
| Cytoscape.js | bundled (`cytoscape.min.js`) | Graph rendering & interaction |
| ELK.js | bundled (`elk.bundled.js`) | Layered graph layout (default) |
| cytoscape-elk | bundled | Cytoscape ↔ ELK bridge |
| Dagre | bundled (`dagre.min.js`) | Fallback / init layout |
| cytoscape-dagre | bundled | Cytoscape ↔ Dagre bridge |
| highlight.js | 11.9 (CDN) | Code-block syntax highlighting |
| marked.js | latest (CDN) | Markdown → HTML rendering |

---

## Module Inventory

Load order defined at the bottom of `index.html`:

| # | Module | Lines | Responsibility |
|---|--------|-------|----------------|
| 1 | `state.js` | ~14 | Global mutable state: `cy`, `currentProject`, `projects`, zoom/pan, `wsClient` |
| 2 | `api.js` | ~106 | `authFetch` / `apiFetch` wrappers, API-key management (localStorage), toast, `formatDate`, `escHtml` |
| 3 | `graph.js` | ~744 | **Core**: Cytoscape init, style rules, node color/icon system, `loadGraph()`, node panel rendering, context menu, box-select, screenshot upload, `init()` entry point |
| 4 | `projects.js` | ~359 | Sidebar rail, project CRUD (`loadProjects`, `createProject`, `deleteProject`), project manager modal (sort, batch delete) |
| 5 | `nodes.js` | ~83 | Node panel edit/save flow: `editNode()`, `cancelEdit()`, `saveNode()` |
| 6 | `edges.js` | ~6 | Placeholder — edge operations handled in `graph.js` context menu |
| 7 | `credentials.js` | ~156 | Credential panel CRUD, secret reveal toggle, form state |
| 8 | `timeline.js` | ~83 | Timeline view: `switchView()`, `loadTimeline()`, node click → graph |
| 9 | `search.js` | ~89 | Cross-project search: debounced input, `doSearch()`, result jump-to-node |
| 10 | `evidence.js` | ~77 | Evidence file attachments per node: upload, download, delete |
| 11 | `import.js` | ~85 | JSON import modal: preview, validate, `executeImport()` |
| 12 | `analysis.js` | ~217 | Attack-path analysis modal: stats bar, attack paths, vulns, dead ends, next steps, cross-connections |
| 13 | `daily_report.js` | ~60 | Daily report modal: date picker, markdown render, copy/download |
| 14 | `ws.js` | ~37 | WebSocket client: auto-reconnect, push-message routing (`projects` → reload list, `graph:<id>` → reload graph) |

---

## Architecture Patterns

### Module system
Global scope with `<script>` tags in dependency order. Each module attaches functions to `window`. Shared state lives in `state.js` globals (`cy`, `currentProject`, `projects`, etc.). No ES modules, no bundler.

### Data flow
```
Browser ──authFetch──▸ Rust /api/* ──SQLx──▸ SQLite
Browser ◂──WebSocket── /api/ws (push: "projects" | "graph:<id>")
```

- **Read**: `apiFetch(url)` → JSON `{success, data, error}` → mutate DOM.
- **Write**: `authFetch(url, {method, body})` → server-side WS broadcast → all clients refresh.
- **Real-time**: WebSocket in `ws.js` receives text messages; `"projects"` reloads project list, `"graph:<pid>"` reloads the graph if the pid matches `currentProject`.

### Authentication
API key stored in `localStorage` (`theseus.apiKey`). Every `authFetch` sets `X-API-Key` header. 401 responses trigger the login overlay.

### Graph rendering (`graph.js`)
- **Layout**: ELK layered (`direction: RIGHT`) for graph loads; Dagre for initial Cytoscape construction.
- **Node colors**: 6 semantic statuses derived from `node_type` + `properties.verdict` + `properties.source` via `nodeColor()`: `vuln`, `process`, `objective`, `dead`, `user_info`, `neutral`.
- **Corner icons**: SVG symbols rendered as data-URI background images — cross (dead), diamond (objective), flag (vuln), person (user_info), checkmark (success), play-arrow (process), dot (neutral).
- **Node sizing**: Dynamic width/height computed from label character width (CJK-aware: 13.5px per CJK char, 7.2px per Latin char).
- **Evidence borders**: `solid` = confirmed, `dashed` = unverified, `dotted` = hypothesis.
- **Positions**: Saved server-side on `dragfree` event; restored after layout on `layoutstop`.
- **Zoom/pan**: Preserved across `loadGraph()` calls within the same project; reset on project switch.

### UI layout
```
┌─────────┬──────────────────────────────────────┐
│ Sidebar │  Topbar (project name, actions, stats)│
│  Rail   ├──────────────────────────────────────┤
│ (avatar │  Cytoscape canvas  (#cy)             │
│  list)  │  ──── or ────                        │
│         │  Timeline view (#timeline-view)       │
├─────────┴──────────────────────────────────────┤
│ Slide-out panels: Node Panel, Credentials Panel│
│ Modals: Create, Manager, Analysis, Daily Report│
│ Overlays: Auth login, Search dropdown          │
└───────────────────────────────────────────────-┘
```

### Theming
All colors via CSS custom properties on `:root` (30+ vars). Semantic groups: background (`--bg-deepest` through `--bg-elevated`), border (`--border-subtle` through `--border-strong`), text (`--text-primary`/`secondary`/`muted`), accent (gold `#c8960c`), status colors (`--success`, `--warning`, `--danger`, `--info`). Radius tokens (`--radius-sm/md/lg`), shadow tokens, single `--transition` easing.

### Node type taxonomy
The graph models a penetration test. 13 node types: `objective`, `entry_point`, `host`, `service`, `vulnerability`, `privilege_escalation`, `exploitation`, `attack_step`, `reconnaissance`, `exfiltration`, `impact`, `credential`, `finding`, `note`, `asset`.

Each node carries `properties`: `verdict` (success/failed), `evidence` (confirmed/unverified/hypothesis), `source` (user/ai), `content` (markdown), `phase`, `target`, `screenshots[]`.

---

## Key Functions Reference

| Function | Module | Purpose |
|----------|--------|---------|
| `init()` | graph.js | Bootstrap: create Cytoscape instance, bind events, load projects, connect WS |
| `loadGraph()` | graph.js | Fetch nodes+edges, apply ELK layout, restore positions |
| `loadProjects()` | projects.js | Fetch project list, render sidebar |
| `selectProject(id)` | projects.js | Set `currentProject`, reset zoom, reload graph |
| `connectWs()` | ws.js | Open/reconnect WebSocket, dispatch push messages |
| `authFetch(url, opts)` | api.js | Fetch with API-key header injection |
| `apiFetch(url, opts)` | api.js | `authFetch` + JSON parse + 401 handling + toast on error |
| `nodeColor(type, props)` | graph.js | Map node data → semantic status string |
| `showAnalysis(ai)` | analysis.js | Fetch + render attack-path analysis (optionally AI-powered) |

---

## Third-party vendored files (`static/`)

- `cytoscape.min.js`, `cytoscape-dagre.js`, `cytoscape-elk.js`
- `dagre.min.js`, `elk.bundled.js`

No `package.json`, no `node_modules`, no build pipeline. All vendored or CDN-loaded.
