THE CENTER
The Git Diff Viewer and Branch Diagram are critical interfaces for Human ◈ AI collaboration. They provide transparency through:- Fresh Diff Computation: No cached data, always current state
- Dual View Modes: Unified (quick overview) vs Split (detailed comparison)
- OKLab Gradients: Perceptually uniform color transitions reduce cognitive load
- SVG Layering: Proper visual hierarchy shows Git relationships clearly
- Interactive Filters: Focus on relevant branches for efficient review
Copy
┌─────────────────────────────────────────────────────────────────┐
│ AI Agent Human Developer │
│ generates reviews & validates │
│ code changes through diff view │
│ │ ▲ │
│ │ │ │
│ ▼ │ │
│ ┌─────────────────────────────────────┴────────────────┐ │
│ │ GitDiffViewer + Branch Diagram │ │
│ │ ┌──────────────┬──────────────┬─────────────────┐ │ │
│ │ │ Green = ADD │ Red = DELETE │ Context Lines │ │ │
│ │ └──────────────┴──────────────┴─────────────────┘ │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Diff Viewer Architecture
Fresh Diff Computation
Every diff request computes fresh from Git trees - no caching.Copy
User requests diff
↓
GitDiffViewer.init(container, repoPath, options)
↓ IPC invoke()
Rust Backend (git_viewers.rs)
├── Repository::open(path) ← FRESH open, no cache
├── repo.diff_tree_to_tree() ← Compute diff from Git trees
└── Parse diff → DiffResult struct
↓
TypeScript Processing
├── Organize changes by file
├── Parse unified diff format into hunks
└── Extract line-by-line changes
↓
Render based on viewMode
├── "unified" → renderUnifiedDiff()
└── "split" → renderSplitDiff()
Repository::open()called fresh each time- No static cache, no
lazy_static!, noOnceCell - Pure function: returns
Result<GitCommitDetail> - No persistent state between calls
Diff Data Flow
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ RUST BACKEND: FRESH DIFF COMPUTATION │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ 1. OPEN REPOSITORY (no cache) │
│ Repository::open(path) │
│ │
│ 2. COMPARE TREES │
│ repo.diff_tree_to_tree(parent_tree, commit_tree, None) │
│ │
│ 3. COUNT CHANGES (iterate diff) │
│ For each line in diff: │
│ ├── '+' → additions++ │
│ ├── '-' → deletions++ │
│ └── ' ' → (context, skip) │
│ │
│ 4. RETURN FRESH RESULT │
│ GitCommitDetail { additions, deletions, ... } │
│ No side effects, pure computation │
│ │
└───────────────────────────────────────────────────────────────────────┘
- Human always sees current state
- No stale diffs from previous sessions
- AI changes immediately visible
- Trust through transparency
Diff Parsing Pipeline
Unified Diff Format
Copy
diff --git a/src/main.rs b/src/main.rs ← File boundary
index abc123..def456 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -10,6 +10,7 @@ fn main() { ← Hunk header
println!("Hello"); ← Context (space)
+ println!("World"); ← Addition (+)
return 0; ← Context (space)
Parsing State Machine
Copy
Raw diff string
↓
┌─────────────────────────────────────────────────────────────────┐
│ STATE: SCANNING (looking for "diff --git" or "@@") │
├─────────────────────────────────────────────────────────────────┤
│ line.startsWith("diff --git") │
│ ↓ │
│ STATE: NEW_FILE │
│ ├── Extract oldPath, newPath via regex │
│ └── Create ParsedDiff { hunks: [], additions: 0 } │
│ │
│ line.startsWith("@@") │
│ ↓ │
│ STATE: NEW_HUNK │
│ ├── Extract oldStart, oldLines, newStart, newLines │
│ └── Create DiffHunk { lines: [] } │
│ │
│ STATE: PARSING_LINES │
│ ├── line[0] === '+' → type = 'add', additions++ │
│ ├── line[0] === '-' → type = 'del', deletions++ │
│ └── line[0] === ' ' → type = 'ctx' (context) │
└─────────────────────────────────────────────────────────────────┘
↓
ParsedDiff[] { oldPath, newPath, additions, deletions, hunks }
Line Type Classification
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ LINE TYPE CLASSIFICATION ALGORITHM │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ FOR each line in hunk (until next @@ or diff): │
│ │
│ ┌──────────────────────────────────┐ │
│ │ Check first character (line[0]) │ │
│ └────────────────┬─────────────────┘ │
│ ┌────────┼────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ '+' '-' ' ' │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ type='add' type='del' type='ctx' │
│ additions++ deletions++ (no count) │
│ │
│ Store: { type, content: line.slice(1) } │
│ │
│ CONTINUE until: │
│ ├── line.startsWith('@@') → new hunk │
│ └── line.startsWith('diff') → new file │
│ │
└───────────────────────────────────────────────────────────────────────┘
View Modes
Unified View
Single column, interleaved additions and deletions.Copy
@@ -10,3 +10,4 @@ fn main() {
┌───────┬───────┬───────┬────────────────────────────────────┐
│ OLD │ NEW │ TYPE │ CONTENT │
├───────┼───────┼───────┼────────────────────────────────────┤
│ 10 │ 10 │ ctx │ println!("Hello"); │
│ │ 11 │ + │ println!("World"); ← GREEN │
│ 11 │ 12 │ ctx │ return 0; │
└───────┴───────┴───────┴────────────────────────────────────┘
Characteristics:
├── Single content column
├── Two line number columns (old, new)
├── Prefix shows +/- for change type
└── Best for: reviewing small, focused changes
Split View
Side-by-side, old on left, new on right.Copy
@@ -10,3 +10,4 @@ fn main() {
┌─────────────────────────────┬─────────────────────────────────┐
│ OLD (Left) │ NEW (Right) │
├─────────────────────────────┼─────────────────────────────────┤
│ 10 │ println!("Hello"); │ 10 │ println!("Hello"); │
│ │ [empty placeholder] │ 11 │ println!("World"); ← GREEN │
│ 11 │ return 0; │ 12 │ return 0; │
└─────────────────────────────┴─────────────────────────────────┘
Characteristics:
├── Two content columns (grid-template-columns: 1fr 1fr)
├── Empty placeholders for added/deleted lines
├── Visual alignment of corresponding lines
└── Best for: comparing large refactors, side-by-side analysis
Color Coding (Both Views)
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ DIFF LINE COLOR CODING │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────┐ │
│ │ Check line.type │ │
│ └──────────────┬─────────────────────┘ │
│ ┌───────┼───────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ 'add' 'del' 'ctx' │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌───────┬───────┬───────┐ │
│ │ GREEN │ RED │ NONE │ │
│ └───────┴───────┴───────┘ │
│ │
│ COLOR MAP: │
│ ┌──────────┬──────────────────────┬──────────────────────┐ │
│ │ Type │ Background │ Text │ │
│ ├──────────┼──────────────────────┼──────────────────────┤ │
│ │ add │ var(--color-success-bg) │ var(--color-success) │ │
│ │ del │ var(--color-error-bg) │ var(--color-error) │ │
│ │ ctx │ transparent │ var(--color-text) │ │
│ └──────────┴──────────────────────┴──────────────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────────┘
| Type | Background | Text |
|---|---|---|
| add | Light green | Dark green |
| del | Light red | Dark red |
| ctx | Transparent | Default |
Branch Diagram Architecture
OKLab Color System
Perceptually uniform color interpolation for merge line gradients. Why OKLab over RGB:- RGB interpolation produces muddy midpoints
- HSL has hue discontinuities
- OKLab is scientifically proven to be perceptually uniform
Color Conversion Pipeline
Copy
Input: "#FF6B6B" (coral red)
↓
┌────────────────────────────────────────────────────────────────┐
│ hexToRgb() → RGB {r, g, b} │
│ rgbToOklab() → OKLab {L, a, b} │
│ interpolateOklab() → OKLab (t = 0.0 to 1.0) │
│ oklabToRgb() → RGB │
│ rgbToHex() → "#XXXXXX" │
└────────────────────────────────────────────────────────────────┘
↓
Gradient Stops (5 steps)
↓
SVG <linearGradient id="merge-gradient-0">
<stop offset="0%" stop-color="#FF6B6B" />
<stop offset="20%" stop-color="#XX..." />
<stop offset="40%" stop-color="#XX..." />
...
</linearGradient>
OKLab Transformation
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ OKLAB COLOR TRANSFORMATION │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ INPUT: RGB { r, g, b } │
│ OUTPUT: OKLab { L, a, b } │
│ │
│ STEP 1: sRGB → Linear RGB │
│ ─────────────────────────── │
│ Apply gamma correction inverse │
│ r_linear = srgbToLinear(r) │
│ g_linear = srgbToLinear(g) │
│ b_linear = srgbToLinear(b) │
│ │
│ STEP 2: Linear RGB → LMS (Cone Response) │
│ ───────────────────────────────────────── │
│ l_ = 0.4122 × r + 0.5363 × g + 0.0514 × b │
│ m_ = 0.2119 × r + 0.6807 × g + 0.1074 × b │
│ s_ = 0.0883 × r + 0.2817 × g + 0.6300 × b │
│ │
│ STEP 3: LMS → L'M'S' (Cube Root) │
│ ───────────────────────────────── │
│ l = ∛(l_) │
│ m = ∛(m_) │
│ s = ∛(s_) │
│ │
│ STEP 4: L'M'S' → OKLab │
│ ───────────────────────── │
│ L = 0.2105 × l + 0.7936 × m - 0.0041 × s │
│ a = 1.9780 × l - 2.4286 × m + 0.4506 × s │
│ b = 0.0259 × l + 0.7828 × m - 0.8087 × s │
│ │
│ Matrix coefficients from official OKLab specification │
│ │
└───────────────────────────────────────────────────────────────────────┘
SVG Layered Rendering
Painter’s Algorithm
SVG elements are rendered in DOM order - first in DOM = behind other elements.Copy
<svg>
<!-- LAYER 1: Timeline (first in DOM = bottom) -->
<line
x1="..."
y1="70"
x2="..."
y2="70"
stroke="#6366f1"
stroke-opacity="0.4"
/>
<!-- LAYER 2: Merge Lines (second in DOM = middle) -->
<g class="merge-lines-group">
<defs>
<linearGradient id="merge-gradient-0">...</linearGradient>
</defs>
<path d="..." stroke="url(#merge-gradient-0)" />
</g>
<!-- LAYER 3: Branch Nodes (last in DOM = top) -->
<g class="branch-nodes-group">
<rect ... />
<text>main</text>
</g>
</svg>
Visual Hierarchy
Copy
┌─────────────────────────────────────────────────────────────────┐
│ SVG Canvas │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ LAYER 1: Timeline line (bottom) │ │
│ │ ════════════════════════════════════════════════ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ LAYER 2: Merge lines (middle) │ │
│ │ ╭────────────────────────────╮ │ │
│ │ ╰──────────────╮ │ │ │
│ │ ╰─────────────╯ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ LAYER 3: Branch nodes (top) │ │
│ │ ┌────────┐ ┌────────┐ ┌────────┐ │ │
│ │ │ main │ │ develop│ │ feature│ │ │
│ │ └────────┘ └────────┘ └────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Interactive Filter System
Two Filter Modes
Copy
MODE 1: 'all-related'
─────────────────────
Shows:
├── Selected branches
├── Branches that MERGED INTO selected
└── Branches that selected MERGED INTO
Logic: OR between endpoints (at least one related)
Example: Select "main"
├── Shows: main
├── Shows: feature/A (merged into main)
└── Shows: develop (main merged into it)
MODE 2: 'between-selected'
────────────────────────
Shows:
├── ONLY selected branches
└── Hides all unselected branches
Logic: AND between endpoints (both must be selected)
Example: Select "main" and "develop"
├── Shows: main, develop
├── Shows: merge lines between main and develop
└── Hides: everything else
Bidirectional Merge Relationship Check
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ BRANCH RELATIONSHIP CHECK ALGORITHM │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ INPUT: branchName │
│ OUTPUT: boolean (is related to selected branches?) │
│ │
│ ┌────────────────────────────────┐ │
│ │ selectedBranches.size === 0? │──► YES ──► return true │
│ └───────────────┬────────────────┘ │
│ │ NO │
│ ▼ │
│ ┌────────────────────────────────┐ │
│ │ selectedBranches.has(branch)? │──► YES ──► return true │
│ └───────────────┬────────────────┘ │
│ │ NO │
│ ▼ │
│ ┌────────────────────────────────┐ │
│ │ filterMode === 'between-selected'? │──► YES ──► return false │
│ └───────────────┬────────────────┘ │
│ │ NO (mode = 'all-related') │
│ ▼ │
│ FOR each selected branch: │
│ ├── CHECK 1: This branch merged INTO selected? │
│ │ branch.merge_targets.includes(selected) │
│ │ │
│ ├── CHECK 2: Selected merged INTO this branch? │
│ │ branch.merged_from.includes(selected) │
│ │ │
│ ├── CHECK 3: Reverse check (selected → this) │
│ │ selected.merge_targets.includes(branchName) │
│ │ │
│ └── CHECK 4: Reverse check (this → selected) │
│ selected.merged_from.includes(branchName) │
│ │
│ If ANY check passes → return true │
│ If ALL checks fail → return false │
│ │
│ SYMMETRY: Relationship discovered from either direction │
│ │
└───────────────────────────────────────────────────────────────────────┘
Opacity-Based Visual Feedback
Copy
┌────────────────────────────────────────────────────────────────────────┐
│ FILTER VISUAL FEEDBACK │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ OPACITY VALUES: │
│ ┌──────────────┬──────────────┬──────────────┐ │
│ │ State │ Node Opacity │ Line Opacity │ │
│ ├──────────────┼──────────────┼──────────────┤ │
│ │ Filtered │ 15% │ 10% │ │
│ │ Active │ 100% │ 80% │ │
│ └──────────────┴──────────────┴──────────────┘ │
│ │
│ CONTRAST RATIO: │
│ Nodes: 100% / 15% = 6.67x │
│ Lines: 80% / 10% = 8x │
│ │
│ VISUAL EFFECT: │
│ ┌───────────────────────────────────────────┐ │
│ │ FILTERED: [░░░░░░] Ghosted, faded │ │
│ │ ACTIVE: [██████] Fully solid │ │
│ └───────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────────────┘
| State | Node Opacity | Line Opacity | Visibility |
|---|---|---|---|
| Filtered | 15% | 10% | Ghosted, faded |
| Active | 100% | 80% | Fully solid, prominent |
Integration Summary
How Claims Work Together
Copy
[Filter System]
↓ User selects branches
Visibility Determination
├── isBranchRelated()
└── isMergeLineBetweenSelected()
↓
┌────────────────┬─────────────────────┬─────────────────────┐
│ Timeline Layer │ Merge Lines Layer │ Branch Nodes Layer │
│ (always vis.) │ (OKLab gradients) │ (opacity filtered) │
└────────────────┴─────────────────────┴─────────────────────┘
↓
Final Rendered SVG
├── Filtered elements = 15% opacity
├── Active elements = 100% opacity
└── Gradients = OKLab smooth transitions
Data Flow
Copy
User Interaction
↓
Filter Selection
↓
selectedBranches Set → isBranchRelated() → Visible/Filtered
↓
renderMergeLines() + renderBranchNodes()
├── OKLab gradients applied
└── Opacity based on filter state
↓
SVG DOM Structure
├── <line> (timeline)
├── <g> merge-lines-group
└── <g> branch-nodes-group
Key Insight
Fresh computation + dual view modes + OKLab gradients + SVG layering = complete transparency in Git history visualization. Human and AI see the same clear representation, enabling efficient collaboration and trust.