Skip to main content

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
┌─────────────────────────────────────────────────────────────────┐
│  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.
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()
Stateless Design:
  • Repository::open() called fresh each time
  • No static cache, no lazy_static!, no OnceCell
  • Pure function: returns Result<GitCommitDetail>
  • No persistent state between calls

Diff Data Flow

┌───────────────────────────────────────────────────────────────────────┐
│  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                                 │
│                                                                       │
└───────────────────────────────────────────────────────────────────────┘
Why this matters:
  • Human always sees current state
  • No stale diffs from previous sessions
  • AI changes immediately visible
  • Trust through transparency

Diff Parsing Pipeline

Unified Diff Format

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

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

┌───────────────────────────────────────────────────────────────────────┐
│  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.
@@ -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.
@@ -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)

┌───────────────────────────────────────────────────────────────────────┐
│  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)    │       │
│  └──────────┴──────────────────────┴──────────────────────┘           │
│                                                                       │
└───────────────────────────────────────────────────────────────────────┘
TypeBackgroundText
addLight greenDark green
delLight redDark red
ctxTransparentDefault

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

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

┌───────────────────────────────────────────────────────────────────────┐
│  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.
<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

┌─────────────────────────────────────────────────────────────────┐
│                        SVG Canvas                               │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  LAYER 1: Timeline line (bottom)                        │    │
│  │  ════════════════════════════════════════════════       │    │
│  └─────────────────────────────────────────────────────────┘    │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  LAYER 2: Merge lines (middle)                          │    │
│  │       ╭────────────────────────────╮                    │    │
│  │       ╰──────────────╮             │                    │    │
│  │                      ╰─────────────╯                    │    │
│  └─────────────────────────────────────────────────────────┘    │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  LAYER 3: Branch nodes (top)                            │    │
│  │     ┌────────┐       ┌────────┐       ┌────────┐        │    │
│  │     │ main   │       │ develop│       │ feature│        │    │
│  │     └────────┘       └────────┘       └────────┘        │    │
│  └─────────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────┘
No CSS z-index used - relies entirely on SVG painter’s model for correct layering.

Interactive Filter System

Two Filter Modes

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

┌───────────────────────────────────────────────────────────────────────┐
│  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

┌────────────────────────────────────────────────────────────────────────┐
│  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         │                         │
│  └───────────────────────────────────────────┘                         │
│                                                                        │
└────────────────────────────────────────────────────────────────────────┘
StateNode OpacityLine OpacityVisibility
Filtered15%10%Ghosted, faded
Active100%80%Fully solid, prominent
Contrast ratio: 6.67x for nodes, 8x for lines - clear visual distinction.

Integration Summary

How Claims Work Together

[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

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.