An interactive visual code review tool that analyzes merge request diffs, builds dependency graphs, and renders an interactive HTML5 Canvas for navigating code changes.
Two analysis modes:
- Roslyn mode — C# repositories with a
.slnfile: resolves class/interface dependencies, constructor injection, method calls, and inheritance across projects using the Roslyn compiler platform. - Lite mode — Any repository (Python, TypeScript, JavaScript, etc.): detects import-based dependencies from source files without requiring a compiler.
Output is a single self-contained HTML file you can open in any browser.
# 1. Build the canvas UI (one-time)
cd canvas-ui && npm install && npm run build && cd ..
# 2. Analyze a single MR
dotnet run -- --mr 647 --repo /path/to/your-service
# 3. Or batch-process all MRs where you're a reviewer
bash run-codeatlas.shFor first-time setup, see SETUP.md.
- .NET 9 SDK
- Node.js 18+ and npm
- glab CLI authenticated (
glab auth login) - For Roslyn mode: a C# repository with a
.slnfile - For Lite mode: any repository with Python/TypeScript/JavaScript source files
The canvas frontend must be built before running the tool:
cd canvas-ui
npm install
npm run buildThis produces canvas-ui/dist/index.html — the .NET tool embeds graph data into this template.
dotnet run -- --mr <branch-or-mr-id> --repo <path-to-repo> [--target <branch>] [--sln <solution>] [--json]| Flag | Description | Default |
|---|---|---|
--mr |
MR ID (numeric) or branch name | required |
--repo |
Path to the repository | required |
--target |
Target branch for diff comparison | main |
--sln |
Explicit path to .sln file |
auto-detected |
--json |
Output JSON to stdout instead of HTML | off |
Mode selection is automatic: if a .sln file is found (or provided via --sln), Roslyn mode is used. Otherwise, Lite mode runs.
Examples:
# By MR ID (Roslyn mode — .sln auto-detected)
dotnet run -- --mr 647 --repo /path/to/csharp-service
# By branch name
dotnet run -- --mr feature/my-branch --repo /path/to/csharp-service
# Specific target branch
dotnet run -- --mr 52476 --repo /path/to/repo --target release/2.0
# Explicit solution file (when multiple .sln exist)
dotnet run -- --mr 52476 --repo /path/to/repo --sln /path/to/repo/App.sln
# Lite mode (Python/TS/JS repo — no .sln)
dotnet run -- --mr 123 --repo /path/to/python-service
# JSON output for debugging
dotnet run -- --mr 647 --repo /path/to/your-service --jsonOutput is written to output-codeatlas/<mr-id>.html.
From this directory (tools/CodeAtlas):
# Process all MRs where you're a reviewer
bash run-codeatlas.sh
# Specify a GitLab group (overrides config)
bash run-codeatlas.sh --group=myorg/mygroup
# Start the AI chat proxy alongside analysis
bash run-codeatlas.sh --with-chat| Flag | Description |
|---|---|
--with-chat |
Start codeatlas-chat-proxy.py on port 7823 before processing |
--group=<group> |
GitLab group to query (overrides gitlab_group in config) |
The batch script:
- Queries
glab mr listfor MRs where you're assigned as reviewer - Maps repo names to local paths using
repos_parent_dirfromcodeatlas-config.json - Skips MRs whose output HTML is newer than the MR's
updated_attimestamp - Skips repos not cloned locally
- Applies
repo_overridesfor custom.slnpaths
Copy from codeatlas-config.example.json:
{
"repos_parent_dir": "/path/to/parent/folder/containing/all/repos",
"gitlab_group": "myorg/mygroup",
"repo_overrides": {
"some-monorepo": {
"sln": "/path/to/monorepo/SubDir/Solution.sln"
}
}
}| Key | Description |
|---|---|
repos_parent_dir |
Parent directory containing all cloned repos as direct children |
gitlab_group |
GitLab group for batch MR queries (can be overridden with --group=) |
repo_overrides |
Per-repo settings — currently supports sln for explicit solution file paths |
Place in any repo root to customize the canvas layout:
{
"nodeWidth": 520,
"rankDirection": "LR",
"defaultZoom": 0.8
}| Key | Description | Default |
|---|---|---|
nodeWidth |
Card width in pixels | 520 |
rankDirection |
LR (left-to-right) or TB (top-to-bottom) |
TB |
defaultZoom |
Initial zoom level | 1.0 |
- Pan — drag empty space
- Zoom — mouse wheel (zooms toward cursor)
- Fit All / Reset — toolbar buttons or
0key
- Each changed file renders as a card with diff-highlighted code (green = additions, red = deletions)
- Border colors: blue = changed, green = new, gray = unchanged dependency
- Click to select, double-click or
Enterto expand into full code viewer - Expanded viewer has collapsible diff sections,
Tab/Shift+Tabto navigate between files
- Roslyn mode: arrows show interface implementations, constructor injection, method calls, inheritance
- Lite mode: arrows show import/require relationships between files
- Hover an edge for a tooltip with dependency details
- Selecting a node dims unrelated nodes/edges to highlight the dependency chain
Stackable visual layers toggled via toolbar or keyboard:
-
Risk Heatmap (
h) — colors nodes by risk score:risk = (additions + deletions) × (fanOut + fanIn + 1) × (isNew ? 2 : 1)- Critical (≥ 90th percentile) — red
- High (≥ 75th) — amber
- Medium (≥ 50th) — blue tint
- Low (< 50th) — unchanged
-
Critical Paths (
i) — thickens borders based on impact radius (transitive dependency count via BFS)
Both overlays can be active simultaneously.
- Full — complete code card with diff preview
- Glyph — compact card showing class name + diff stats
- Dot — minimal dot representation at far zoom
- Auto-switches based on zoom level, or cycle manually with
skey - Font scales inversely with zoom for readability
- Mark files as Reviewed (
r), Flagged (f), or Unreviewed (u) - Also supports Needs Attention status (cycle with click)
- Progress bar shows review completion
- State persisted per-branch in
localStorage - Filter canvas by review status via toolbar
- Toggle with
akey or toolbar button - Sends the current graph context (files, diffs, dependencies) to Claude via the local chat proxy
- Requires
codeatlas-chat-proxy.pyrunning on port 7823 (auto-started with--with-chat)
/or toolbar search to filter nodes by filename- Matching nodes highlighted, non-matching dimmed
| Key | Action |
|---|---|
Tab / Shift+Tab |
Select next / previous file |
Enter |
Expand selected node |
Escape |
Close expanded view / search / help |
+ / - |
Zoom in / out |
0 |
Fit all nodes in view |
r / f / u |
Mark reviewed / flagged / unreviewed |
s |
Cycle LOD mode: auto → glyph → full |
h |
Toggle risk heatmap |
i |
Toggle critical paths |
a |
Toggle AI chat panel |
| Arrow keys | Pan canvas |
? |
Show shortcuts overlay |
CodeAtlas/
├── Program.cs # 3-line entry point
├── CliRunner.cs # CLI argument parsing and orchestration
├── Defaults.cs # Shared constants (timeouts, defaults)
├── GitCommandRunner.cs # Shared git/CLI command execution
├── MrAnalyzer.cs # Roslyn analysis: loads .sln, resolves dependencies
├── MrAnalyzerLite.cs # Lite analysis: import-based edges (no compiler)
├── ImportEdgeBuilder.cs # Import/require edge detection (Python, TS, JS)
├── DiffParser.cs # Git diff → structured sections
├── GraphHelpers.cs # Shared utilities (file type detection, naming)
├── MrGraph.cs # Data models (MrGraph, MrFileNode, MrEdge, etc.)
├── MrHtmlRenderer.cs # Embeds JSON graph data into canvas-ui HTML
├── WorktreeHelper.cs # Git worktree management for MR branch checkout
├── CodeAtlas.csproj # .NET 9 project
├── run-codeatlas.sh # Batch runner for all reviewer MRs
├── codeatlas-chat-proxy.py # HTTP proxy (port 7823) forwarding to Claude CLI
├── codeatlas-config.json # Local config (git-ignored)
├── codeatlas-config.example.json
├── output-codeatlas/ # Generated HTML files (git-ignored)
└── canvas-ui/ # Preact + TypeScript frontend
├── src/
│ ├── main.tsx # Entry point, graph initialization, ErrorBoundary
│ ├── types.ts # Shared TypeScript types (MrGraph, MrFileNode, etc.)
│ ├── theme/
│ │ └── tokens.ts # Design tokens (colors, spacing)
│ ├── state/ # @preact/signals-based state management
│ │ ├── graph-store.ts # Graph data, node positions, edges
│ │ ├── ui-store.ts # Zoom, pan, selection, overlays, filters, LOD
│ │ ├── review-store.ts # Review status tracking (localStorage-persisted)
│ │ ├── analysis-store.ts # Risk score computation
│ │ └── chat-store.ts # Chat panel state
│ ├── canvas/ # Canvas rendering pipeline
│ │ ├── canvas-renderer.ts # Main render loop (reactive via signals)
│ │ ├── node-painter.ts # File card rendering (full + compact)
│ │ ├── arrow-painter.ts # Dependency edge rendering
│ │ ├── project-painter.ts # Project group backgrounds
│ │ ├── label-painter.ts # Text label rendering utilities
│ │ ├── lod-fade-manager.ts # Level-of-detail transition animations
│ │ └── interaction.ts # Mouse/touch input handling
│ ├── layout/ # Graph layout algorithms
│ │ ├── dagre-layout.ts # Dagre-based hierarchical layout
│ │ ├── arch-layout.ts # Architecture view layout
│ │ └── arrow-router.ts # Edge routing between nodes
│ ├── ui/ # Preact UI components
│ │ ├── App.tsx # Root application component
│ │ ├── Toolbar.tsx # Top toolbar (zoom, filters, overlays)
│ │ ├── CodeCard.tsx # Expanded code viewer
│ │ ├── CodeLine.tsx # Single line renderer with syntax highlighting
│ │ ├── CardOverlay.tsx # Card action overlay (review status)
│ │ ├── EdgeTooltip.tsx # Dependency edge tooltip
│ │ ├── ChatPanel.tsx # AI chat panel
│ │ ├── ErrorBoundary.tsx # Error boundary wrapper
│ │ └── KeyboardShortcuts.tsx # Keyboard handler + help overlay
│ ├── syntax/
│ │ └── highlighter.ts # Syntax highlighting engine
│ └── utils/
│ ├── diff-utils.ts # Diff parsing utilities
│ └── chat-context.ts # Chat context builder
├── package.json
├── tsconfig.json
└── vite.config.ts # Vite config (single-file HTML output via inlining)
Program.cs→ delegates toCliRunnerwhich parses CLI argumentsCliRunner→ creates a git worktree for the MR branch viaWorktreeHelperMrAnalyzer(Roslyn) orMrAnalyzerLite(import-based) → diffs against target branch, buildsMrGraphcontaining files and dependency edgesMrHtmlRenderer→ serializesMrGraphto JSON, embeds intocanvas-ui/dist/index.html- Browser →
main.tsxreads embedded JSON, runs dagre layout, starts reactive canvas render loop
Roslyn mode (C# repos with .sln):
- Loads the solution via
Microsoft.CodeAnalysis - Walks syntax trees to find class declarations, interface implementations, constructor parameters, method calls
- Resolves cross-project references through the Roslyn compilation model
- Produces typed edges:
implements,injects,calls,inherits
Lite mode (all other repos):
- Scans changed files for
import/require/fromstatements - Resolves relative paths to match other changed files
- Supports Python, TypeScript, and JavaScript
- Produces
importsedges
The codeatlas-chat-proxy.py script runs a local HTTP server on port 7823 that bridges the canvas UI's chat panel to the Claude CLI:
# Start manually
python3 codeatlas-chat-proxy.py
# Or auto-start via batch mode
bash run-codeatlas.sh --with-chatThe proxy accepts POST requests with the graph context and user message, forwards to claude CLI, and streams the response back.
cd canvas-ui
npm run devStarts Vite dev server with hot reload. Note: graph data comes from the embedded JSON, so you'll need a previously generated HTML file's data for testing.
# Backend
dotnet build
# Frontend
cd canvas-ui && npm run buildThe Vite build produces a single inlined HTML file (all JS/CSS bundled inline) so the output is fully self-contained.