File size: 3,190 Bytes
1966985
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# Phase 0: Research & Technical Decisions

## 1. Graph Visualization Library

**Decision**: Use `react-force-graph-2d`.

**Rationale**:
-   **Performance**: Uses HTML5 Canvas/WebGL for rendering, capable of handling thousands of nodes (meeting the "personal knowledge base" scale requirement).
-   **React Integration**: Native React component wrapper around `force-graph`, managing the lifecycle and updates declarative.
-   **Feature Set**: Built-in zoom/pan, auto-centering, node/link interactions (hover/click), and flexible styling.
-   **Maintainability**: Widely used, active community.

**Alternatives Considered**:
-   `vis-network`: Good but heavier and imperative API is harder to integrate cleanly with modern React hooks.
-   `d3-force` (raw): Too low-level. Would require rebuilding canvas rendering, zoom/pan logic, and interaction handlers from scratch.
-   `cytoscape.js`: Powerful but focused more on graph theory analysis; visual customization is CSS-like but sometimes more complex for "floating particle" aesthetics.

## 2. Data Structure & API

**Decision**: Flat structure with `nodes` and `links` arrays.

**Schema**:
```json
{
  "nodes": [
    { "id": "path/to/note.md", "label": "Note Title", "val": 1, "group": "folder-name" }
  ],
  "links": [
    { "source": "path/to/source.md", "target": "path/to/target.md" }
  ]
}
```

**Rationale**:
-   Matches the expected input format of `react-force-graph`.
-   `val` property allows automatic node sizing based on degree (calculated on backend or frontend). Backend calculation is preferred for caching/performance.
-   `id` uses the file path to ensure uniqueness and easy mapping back to navigation events.

## 3. Theme Integration

**Decision**: Pass dynamic colors via React props, reading from CSS variables or a ThemeContext.

**Strategy**:
-   The `GraphView` component will hook into the current theme (light/dark).
-   Colors for background, nodes, and text will be passed to `<ForceGraph2D />`.
-   **Light Mode**: White background, dark grey nodes/links.
-   **Dark Mode**: `hsl(var(--background))` (usually dark), light grey nodes.
-   **Groups**: Use a categorical color scale for folders (e.g., D3 scale or a fixed palette).

## 4. Unlinked Notes

**Decision**: Include all notes in the `nodes` array, even if they have no entries in `links`.

**Physics**:
-   The force simulation will naturally push unconnected nodes away from the center cluster but keep them within the viewport if a bounding box or gravity is applied.
-   We will apply a weak `d3.forceManyBody` (repulsion) and a central `d3.forceCenter` to keep the "cloud" visible.

## 5. Backend Implementation

**Decision**: Add `get_graph_data` to `IndexerService`.

**Logic**:
1.  Fetch all notes from `note_metadata` (id, title).
2.  Fetch all links from `note_links` where `is_resolved=1`.
3.  Compute link counts for node sizing (optional optimization: do this in SQL or Python).
4.  Return JSON.
5.  **Caching**: Use a simple in-memory cache with a short TTL (e.g., 5 minutes) or invalidation on note create/update events to ensure sub-2s response times for large vaults. *For V1, direct SQL query is likely fast enough for <1000 notes.*