<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>duodiff — a fast TUI directory comparison tool</title>
<meta name="description" content="duodiff is a fast, cross-platform terminal user interface (TUI) directory comparison tool written in Rust: side-by-side tree view, async background scanner, flexible comparison modes, built-in diff, and mouse support." />
<link rel="canonical" href="https://akunzai.github.io/duodiff/" />
<meta property="og:type" content="website" />
<meta property="og:title" content="duodiff — a fast TUI directory comparison tool" />
<meta property="og:description" content="Fast, cross-platform terminal user interface (TUI) directory comparison tool written in Rust." />
<meta property="og:url" content="https://akunzai.github.io/duodiff/" />
<meta property="og:image" content="https://akunzai.github.io/duodiff/demo.gif" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="duodiff — a fast TUI directory comparison tool" />
<meta name="twitter:description" content="Fast, cross-platform terminal user interface (TUI) directory comparison tool written in Rust." />
<meta name="twitter:image" content="https://akunzai.github.io/duodiff/demo.gif" />
<style>
:root {
--bg: #0f172a;
--bg-elev: #1e293b;
--bg-card: rgba(30, 41, 59, 0.7);
--border: #334155;
--fg: #f8fafc;
--muted: #94a3b8;
--dim: #64748b;
--blue: #3b82f6;
--green: #10b981;
--purple: #8b5cf6;
--cyan: #06b6d4;
--yellow: #f59e0b;
--red: #ef4444;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
background: var(--bg);
color: var(--fg);
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
code, kbd, pre {
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
a { color: var(--blue); text-decoration: none; transition: color 0.2s; }
a:hover { color: #60a5fa; text-decoration: underline; }
.wrap { max-width: 960px; margin: 0 auto; padding: 0 20px; }
header.hero {
padding: 80px 0 48px;
text-align: center;
background:
radial-gradient(1200px 450px at 50% -10%, rgba(59, 130, 246, 0.15), transparent 75%);
border-bottom: 1px solid var(--border);
}
.logo {
display: inline-flex; align-items: baseline; gap: 2px;
font-family: ui-monospace, monospace;
font-size: 48px; font-weight: 700; letter-spacing: -1.5px;
}
.logo .br { color: var(--dim); }
.logo .name { color: var(--fg); background: linear-gradient(135deg, #3b82f6, #06b6d4); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
.tagline {
margin: 20px auto 0; max-width: 680px;
font-size: 20px; color: var(--muted);
}
.tagline strong { color: var(--cyan); font-weight: 600; }
.cta { margin-top: 32px; display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; }
.btn {
display: inline-block; padding: 12px 24px; border-radius: 8px;
font-weight: 600; font-size: 15px; border: 1px solid var(--border);
transition: all 0.2s;
}
.btn.primary { background: var(--blue); color: #0f172a; border-color: var(--blue); }
.btn.primary:hover { background: #60a5fa; border-color: #60a5fa; text-decoration: none; transform: translateY(-1px); }
.btn.ghost { color: var(--fg); background: var(--bg-card); backdrop-filter: blur(8px); }
.btn.ghost:hover { border-color: var(--blue); text-decoration: none; transform: translateY(-1px); }
.install { margin: 40px auto 0; max-width: 900px; text-align: left; }
.term {
background: var(--bg-elev); border: 1px solid var(--border);
border-radius: 12px; overflow: hidden;
box-shadow: 0 20px 50px rgba(0,0,0,0.5);
}
.term .bar {
display: flex; align-items: center; gap: 7px;
padding: 12px 16px; border-bottom: 1px solid var(--border);
}
.term .bar i { width: 12px; height: 12px; border-radius: 50%; display: inline-block; }
.term .bar .r { background: #ef4444; } .term .bar .y { background: #f59e0b; } .term .bar .g { background: #10b981; }
.term .bar span { margin-left: 10px; color: var(--dim); font-size: 13px; font-family: monospace; }
.term pre { margin: 0; padding: 18px 20px; overflow-x: auto; font-size: 14.5px; }
.term .prompt { color: var(--green); user-select: none; }
.term .cmd { color: var(--fg); }
.hint { margin-top: 14px; font-size: 13.5px; color: var(--dim); text-align: center; }
section { padding: 64px 0; border-bottom: 1px solid var(--border); }
h2 { font-size: 28px; margin: 0 0 8px; letter-spacing: -0.5px; }
.sub { color: var(--muted); margin: 0 0 32px; font-size: 16px; }
.shot {
border: 1px solid var(--border); border-radius: 14px; overflow: hidden;
background: var(--bg-elev); box-shadow: 0 20px 50px rgba(0,0,0,0.5);
}
.shot img { display: block; width: 100%; height: auto; }
figure { margin: 0; }
figcaption { margin-top: 14px; color: var(--dim); font-size: 13.5px; text-align: center; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; }
.card {
background: var(--bg-card); border: 1px solid var(--border);
border-radius: 12px; padding: 24px;
backdrop-filter: blur(8px);
transition: transform 0.2s, border-color 0.2s;
}
.card:hover {
transform: translateY(-2px);
border-color: var(--blue);
}
.card h3 { margin: 0 0 10px; font-size: 18px; font-weight: 600; }
.card p { margin: 0; color: var(--muted); font-size: 14.5px; }
.req { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 20px; }
.req .card p code { color: var(--cyan); }
footer { padding: 48px 0 64px; text-align: center; color: var(--dim); font-size: 14px; border-top: 1px solid transparent; }
footer p { margin: 8px 0; }
footer a { color: var(--muted); }
footer a:hover { color: var(--blue); }
@media (max-width: 520px) {
.logo { font-size: 38px; }
.tagline { font-size: 18px; }
header.hero { padding: 60px 0 36px; }
}
</style>
</head>
<body>
<header class="hero">
<div class="wrap">
<div class="logo"><span class="br">>_ </span><span class="name">duodiff</span></div>
<p class="tagline">A fast, cross-platform terminal user interface (TUI) directory comparison tool written in Rust — <strong>align, diff, filter, and sync</strong> directories in real-time.</p>
<div class="cta">
<a class="btn primary" href="https://github.com/akunzai/duodiff">View on GitHub</a>
<a class="btn ghost" href="https://github.com/akunzai/duodiff/releases/latest">Download a release</a>
</div>
<div class="install">
<div class="term">
<div class="bar"><i class="r"></i><i class="y"></i><i class="g"></i><span>install.sh</span></div>
<pre><span class="prompt">$ </span><span class="cmd">curl -fsSL https://raw.githubusercontent.com/akunzai/duodiff/main/install.sh | bash</span></pre>
</div>
<p class="hint">Detects your platform, verifies its SHA-256 checksum, and installs to <code>~/.local/bin</code> — Linux, macOS & Windows (Git Bash).<br />
More paths (Homebrew, Scoop, PowerShell, crates.io, source, <code>duodiff --upgrade</code>): <a href="https://github.com/akunzai/duodiff/blob/main/docs/INSTALL.md">docs/INSTALL.md</a></p>
</div>
</div>
</header>
<section id="demo">
<div class="wrap">
<figure>
<div class="shot"><img src="demo.gif" alt="duodiff demo: pane focus, filtered file diff, and per-hunk merge" loading="lazy" /></div>
<figcaption>Compare directories side-by-side, filter to a file, and merge individual change blocks in the built-in diff view.</figcaption>
</figure>
</div>
</section>
<section id="why">
<div class="wrap">
<h2>Why duodiff?</h2>
<p class="sub">Clean, robust, and lightning-fast directory comparisons.</p>
<div class="grid">
<div class="card">
<h3>Side-by-Side Double-Pane Tree</h3>
<p>Balanced view showing matching, differing, and missing folders/files side-by-side. Focus highlights and synchronized mouse scrolling keep context clear.</p>
</div>
<div class="card">
<h3>Asynchronous Background Scanner</h3>
<p>All directory alignment calculations and file content MD5 streaming checksum computations run in non-blocking background tasks, keeping the UI fully interactive.</p>
</div>
<div class="card">
<h3>Built-in Color Diff & Sync</h3>
<p>Compare file differences instantly in-app, or launch your preferred external diff tool. Sync files with confirmation gates protecting against overwrite.</p>
</div>
</div>
</div>
</section>
<section id="features">
<div class="wrap">
<h2>Features</h2>
<p class="sub">A complete set of tools to explore, diff, and resolve directory differences.</p>
<div class="grid">
<div class="card">
<h3>Flexible Comparison</h3>
<p>Toggle between <strong>Fast mode</strong> (compares file size and modification time) and <strong>Precise mode</strong> (compares content via streaming MD5 hashes) at the press of a key (<kbd>c</kbd>).</p>
</div>
<div class="card">
<h3>Integrated Editor Diff</h3>
<p>View visual line-level diffs inside the app, or launch external diff tools (<code>vim</code>, <code>nvim</code>, <code>code</code>, <code>meld</code>, <code>difft</code>, etc.) and editors using your <code>$EDITOR</code> environment variable.</p>
</div>
<div class="card">
<h3>Fast Search & Filter</h3>
<p>Search files in the tree view instantly using the slash key (<kbd>/</kbd>). Commit search queries or toggle showing only differing items (<kbd>f</kbd>).</p>
</div>
<div class="card">
<h3>Vim Bindings & Mouse</h3>
<p>Navigable via standard arrow keys or <code>hjkl</code> keys. Wheel scrolls, clicks select, and double-clicks enter diff views or toggle directory trees.</p>
</div>
<div class="card">
<h3>Config UI</h3>
<p>Built-in configuration screen (<kbd>C</kbd>) that automatically detects system-installed diff tools and edits <code>~/.config/duodiff/config.toml</code>.</p>
</div>
<div class="card">
<h3>Self-Upgrading</h3>
<p>Supports self-checking and direct in-app upgrading from release binaries using <code>duodiff --upgrade</code>. Background updates check once a day politely.</p>
</div>
</div>
</div>
</section>
<section id="requirements">
<div class="wrap">
<h2>Requirements</h2>
<p class="sub">Zero external runtime dependencies. Runs standalone on macOS, Linux, and Windows.</p>
<div class="req">
<div class="card"><h3>Any terminal</h3><p>Requires a terminal supporting truecolor or 256 colors. Unicode and emoji font support makes icons cleaner.</p></div>
<div class="card"><h3>Rust Toolchain</h3><p>Required <strong>only</strong> if building from source. Pin versions using <code>mise</code> or build with <code>cargo build</code>.</p></div>
<div class="card"><h3>Optional: Diff Tools</h3><p>For launching external diff editors from the UI, standard tools like <code>code</code>, <code>nvim</code>, or <code>meld</code> are supported.</p></div>
</div>
</div>
</section>
<footer>
<div class="wrap">
<p>
<a href="https://github.com/akunzai/duodiff">GitHub</a> ·
<a href="https://github.com/akunzai/duodiff/blob/main/docs/SHORTCUTS.md">Shortcuts</a> ·
<a href="https://github.com/akunzai/duodiff/blob/main/docs/INSTALL.md">Installation</a> ·
<a href="https://github.com/akunzai/duodiff/blob/main/CONTRIBUTING.md">Contributing</a>
</p>
<p>MIT licensed · Built with Rust + Ratatui</p>
</div>
</footer>
</body>
</html>