1084 lines
61 KiB
HTML
1084 lines
61 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>Herdr — one terminal for the whole herd</title>
|
||
<meta
|
||
name="description"
|
||
content="Terminal-native agent runtime and multiplexer. Run AI coding agents in real panes, detach and reattach, SSH in, see state, and orchestrate through the CLI/socket API."
|
||
/>
|
||
<meta name="theme-color" content="#f0eee9" />
|
||
<link rel="canonical" href="https://herdr.dev/" />
|
||
|
||
<meta property="og:type" content="website" />
|
||
<meta property="og:site_name" content="Herdr" />
|
||
<meta property="og:title" content="Herdr — one terminal for the whole herd" />
|
||
<meta
|
||
property="og:description"
|
||
content="Terminal-native agent runtime and multiplexer. Detach, reattach, SSH in, and keep every agent visible."
|
||
/>
|
||
<meta property="og:url" content="https://herdr.dev/" />
|
||
<meta
|
||
property="og:image"
|
||
content="https://herdr.dev/assets/og-card-v6.png"
|
||
/>
|
||
<meta property="og:image:width" content="1200" />
|
||
<meta property="og:image:height" content="630" />
|
||
<meta property="og:image:alt" content="Herdr — One terminal. The whole herd." />
|
||
|
||
<meta name="twitter:card" content="summary_large_image" />
|
||
<meta name="twitter:title" content="Herdr — one terminal for the whole herd" />
|
||
<meta
|
||
name="twitter:description"
|
||
content="Terminal-native agent runtime and multiplexer. Detach, reattach, SSH in, and keep every agent visible."
|
||
/>
|
||
<meta
|
||
name="twitter:image"
|
||
content="https://herdr.dev/assets/og-card-v6.png"
|
||
/>
|
||
<meta name="twitter:image:alt" content="Herdr — One terminal. The whole herd." />
|
||
|
||
<link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon.png?v=14" />
|
||
<link rel="apple-touch-icon" href="/assets/logo.png" />
|
||
<link
|
||
rel="preload"
|
||
href="/assets/jbm.woff2"
|
||
as="font"
|
||
type="font/woff2"
|
||
crossorigin
|
||
/>
|
||
<script>
|
||
(function () {
|
||
var saved = localStorage.getItem("herdr-palette");
|
||
var system = window.matchMedia("(prefers-color-scheme: dark)").matches
|
||
? "tokyo-night"
|
||
: "tokyo-night-day";
|
||
document.documentElement.setAttribute("data-palette", saved || system);
|
||
})();
|
||
</script>
|
||
<link rel="stylesheet" href="/css/style.css?v=21" />
|
||
</head>
|
||
<body>
|
||
<header class="site-header">
|
||
<nav class="nav" aria-label="Primary navigation">
|
||
<a class="brand" href="/" aria-label="Herdr home">
|
||
<span class="brand-logo" aria-hidden="true"></span>
|
||
<span>herdr</span>
|
||
</a>
|
||
<details class="nav-menu">
|
||
<summary class="nav-menu-button" aria-label="Open navigation">
|
||
<span></span>
|
||
<span></span>
|
||
<span></span>
|
||
</summary>
|
||
<div class="nav-menu-panel">
|
||
<a href="/">Home</a>
|
||
<a href="/compare/">Compare</a>
|
||
<a href="/releases/">Releases</a>
|
||
<a href="/blog/">Blog</a>
|
||
<a href="/docs/quick-start/">Quick start</a>
|
||
<a href="/docs/">Docs</a>
|
||
<a href="/docs/socket-api/">API</a>
|
||
<a href="/docs/install/">Install</a>
|
||
<a href="https://github.com/ogulcancelik/herdr">GitHub</a>
|
||
</div>
|
||
</details>
|
||
<div class="nav-links">
|
||
<a href="/">Home</a>
|
||
<a href="/compare/">Compare</a>
|
||
<a href="/releases/">Releases</a>
|
||
<a href="/blog/">Blog</a>
|
||
<a href="/docs/quick-start/">Quick start</a>
|
||
<a href="/docs/">Docs</a>
|
||
<a href="/docs/socket-api/">API</a>
|
||
</div>
|
||
<div class="nav-tools">
|
||
<a class="nav-icon-link" href="https://github.com/ogulcancelik/herdr" aria-label="Herdr on GitHub">
|
||
<svg viewBox="0 0 16 16" aria-hidden="true">
|
||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82A7.64 7.64 0 0 1 8 3.87c.68 0 1.36.09 2 .26 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z" />
|
||
</svg>
|
||
</a>
|
||
<a class="nav-icon-link" href="https://x.com/herdrdev" aria-label="Herdr on X">
|
||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231Zm-1.161 17.52h1.833L7.084 4.126H5.117Z" />
|
||
</svg>
|
||
</a>
|
||
<a class="nav-cta" href="/docs/install/">Install</a>
|
||
</div>
|
||
</nav>
|
||
</header>
|
||
|
||
<main>
|
||
<section class="hero" id="hero">
|
||
<div class="hero-left">
|
||
<div class="hero-copy">
|
||
<p class="eyebrow">
|
||
<span class="status-dot dot-done"></span>
|
||
Terminal-native agent runtime
|
||
</p>
|
||
<h1>One terminal. The whole herd.</h1>
|
||
<p class="hero-lede">
|
||
Herdr is an agent runtime that runs inside your terminal.
|
||
Keep your shell, SSH setup, fonts, and keybinds; add
|
||
tmux-style persistence, mouse-native panes,
|
||
<span class="state blocked">blocked</span>,
|
||
<span class="state working">working</span>, and
|
||
<span class="state done">done</span> agent state, and an
|
||
API agents can drive.
|
||
</p>
|
||
<div class="hero-actions">
|
||
<a class="button button-primary" href="/docs/quick-start/"
|
||
>Quick start</a
|
||
>
|
||
<a class="button button-secondary" href="/docs/"
|
||
>Read the docs</a
|
||
>
|
||
</div>
|
||
<div class="install-panel" aria-label="Install options">
|
||
<div class="install-methods" role="group" aria-label="Install method">
|
||
<span class="install-label">Install with</span>
|
||
<button
|
||
class="install-method is-active"
|
||
type="button"
|
||
data-command="curl -fsSL https://herdr.dev/install.sh | sh"
|
||
data-size="compact"
|
||
aria-pressed="true"
|
||
>Script</button>
|
||
<button
|
||
class="install-method"
|
||
type="button"
|
||
data-command="brew install herdr"
|
||
data-size="normal"
|
||
aria-pressed="false"
|
||
>Homebrew</button>
|
||
<button
|
||
class="install-method"
|
||
type="button"
|
||
data-command="nix run github:ogulcancelik/herdr"
|
||
data-size="normal"
|
||
aria-pressed="false"
|
||
>Nix flake</button>
|
||
</div>
|
||
<div class="install-card" aria-label="Install command">
|
||
<span class="prompt">$</span>
|
||
<code id="install-cmd" class="is-compact"
|
||
>curl -fsSL https://herdr.dev/install.sh | sh</code
|
||
>
|
||
<button id="copy-btn" type="button">Copy</button>
|
||
</div>
|
||
</div>
|
||
<p class="hero-note">Single Rust binary · no Electron</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="hero-right">
|
||
<figure class="hero-product-shot">
|
||
<div
|
||
class="herdr-ui-mock"
|
||
role="img"
|
||
aria-label="Stylized Herdr session with spaces, tabs, agent states, terminal panes, and a local docs server"
|
||
>
|
||
<aside class="mock-sidebar">
|
||
<div class="mock-sidebar-title">spaces</div>
|
||
<div class="mock-space active">
|
||
<span class="mock-dot dot-idle"></span>
|
||
<div><strong>herdr</strong><small>master</small></div>
|
||
</div>
|
||
<div class="mock-space">
|
||
<span class="mock-dot dot-blocked"></span>
|
||
<div><strong>llm-proxy</strong><small>master</small></div>
|
||
</div>
|
||
<div class="mock-space">
|
||
<span class="mock-dot dot-done"></span>
|
||
<div><strong>qmp</strong><small>autoresearch/qmp-ra…</small></div>
|
||
</div>
|
||
<div class="mock-sidebar-spacer"></div>
|
||
<div class="mock-sidebar-row"><span>new</span><span>menu</span></div>
|
||
<div class="mock-agent-title"><span>agents</span><span>all</span></div>
|
||
<div class="mock-agent"><span>✓</span><div><strong>doc</strong><small>idle · pi</small></div></div>
|
||
<div class="mock-agent working"><span class="braille-spinner"></span><div><strong>custom-row</strong><small>working · pi</small></div></div>
|
||
<div class="mock-agent blocked"><span>◉</span><div><strong>llm-proxy</strong><small>blocked · claude</small></div></div>
|
||
<div class="mock-agent done"><span>●</span><div><strong>qmp</strong><small>done · codex</small></div></div>
|
||
</aside>
|
||
<section class="mock-main">
|
||
<div class="mock-tabs">
|
||
<span>doc</span><span>custom-row</span><span class="active">herdr</span><span>+</span>
|
||
</div>
|
||
<div class="mock-panes">
|
||
<div class="mock-pane focused">
|
||
<div class="mock-prompt"><span>~/Projects/herdr</span> <em>master</em></div>
|
||
<div><b>❯</b> <span class="cmd">printf</span> <span class="str">"one terminal. the whole herd.\n"</span> | <span class="cmd">cowsay</span></div>
|
||
<pre>-------------------------------
|
||
< one terminal. the whole herd. >
|
||
-------------------------------
|
||
\ ^__^
|
||
\ (oo)\_______
|
||
(__)\ )\/\
|
||
||----w |
|
||
|| ||</pre>
|
||
<div class="mock-prompt"><span>~/Projects/herdr</span> <em>master</em></div>
|
||
<div><b>❯</b> <span class="cursor"></span></div>
|
||
</div>
|
||
<div class="mock-pane">
|
||
<div class="mock-prompt"><span>~/Projects/herdr-worktrees/docs-site/website</span> <em>docs-site*</em></div>
|
||
<div><b>❯</b> <span class="cmd">bun</span> run dev</div>
|
||
<div class="dim">$ node scripts/prepare-docs.mjs && astro dev</div>
|
||
<div><span class="time">02:10:44</span> <span class="blue">[types]</span> Generated 0ms</div>
|
||
<div><span class="time">02:10:44</span> <span class="blue">[content]</span> Synced content</div>
|
||
<br />
|
||
<div><span class="badge">astro</span> <span class="green">v5.18.1</span> ready in 668 ms</div>
|
||
<br />
|
||
<div class="url-line">┃ Local <span>http://localhost:4321/</span></div>
|
||
<div class="dim"> Network use --host to expose</div>
|
||
<br />
|
||
<div><span class="time">02:10:44</span> <span class="green">watching for file changes...</span></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
<figcaption>
|
||
The whole herd in one terminal.
|
||
</figcaption>
|
||
</figure>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="logo-cloud" aria-labelledby="logo-cloud-title">
|
||
<div class="logo-cloud-heading">
|
||
<p class="section-kicker">Used in the wild</p>
|
||
<h2 id="logo-cloud-title">Popular with engineers from</h2>
|
||
</div>
|
||
<div class="logo-marquee" aria-label="Company logos">
|
||
<div class="logo-track">
|
||
<div class="logo-row">
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/jetbrains.svg" alt="JetBrains" /><span>JetBrains</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/docker.svg" alt="Docker" /><span>Docker</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/vercel.svg" alt="Vercel" /><span>Vercel</span></div>
|
||
<div class="logo-card"><img width="94" height="32" src="/assets/company-logos/kimi-with-icon-light.svg" alt="Kimi" /><span>Kimi</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/sentry.svg" alt="Sentry" /><span>Sentry</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/google.svg" alt="Google" /><span>Google</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/nvidia.svg" alt="NVIDIA" /><span>NVIDIA</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/aws.svg" alt="AWS" /><span>AWS</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/bytedance.svg" alt="ByteDance" /><span>ByteDance</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/tencent.svg" alt="Tencent" /><span>Tencent</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/alibaba.svg" alt="Alibaba" /><span>Alibaba</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/salesforce.svg" alt="Salesforce" /><span>Salesforce</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/ibm.svg" alt="IBM" /><span>IBM</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/atlassian.svg" alt="Atlassian" /><span>Atlassian</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/whop.svg?v=2" alt="Whop" /><span>Whop</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/automattic.svg" alt="Automattic" /><span>Automattic</span></div>
|
||
</div>
|
||
<div class="logo-row" aria-hidden="true">
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/jetbrains.svg" alt="" /><span>JetBrains</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/docker.svg" alt="" /><span>Docker</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/vercel.svg" alt="" /><span>Vercel</span></div>
|
||
<div class="logo-card"><img width="94" height="32" src="/assets/company-logos/kimi-with-icon-light.svg" alt="" /><span>Kimi</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/sentry.svg" alt="" /><span>Sentry</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/google.svg" alt="" /><span>Google</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/nvidia.svg" alt="" /><span>NVIDIA</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/aws.svg" alt="" /><span>AWS</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/bytedance.svg" alt="" /><span>ByteDance</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/tencent.svg" alt="" /><span>Tencent</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/alibaba.svg" alt="" /><span>Alibaba</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/salesforce.svg" alt="" /><span>Salesforce</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/ibm.svg" alt="" /><span>IBM</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/atlassian.svg" alt="" /><span>Atlassian</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/whop.svg?v=2" alt="" /><span>Whop</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/automattic.svg" alt="" /><span>Automattic</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="section intro-section">
|
||
<p class="section-kicker">The missing layer</p>
|
||
<div class="section-heading-wide">
|
||
<h2>
|
||
Keep your terminal. Add the control surface.
|
||
</h2>
|
||
<p>
|
||
Desktop agent terminals give you app UI. tmux gives you
|
||
persistence. Herdr sits between them: a lightweight TUI
|
||
process with real terminal panes, clickable layout,
|
||
agent state, detach/reattach, remote attach, and a
|
||
runtime API.
|
||
</p>
|
||
</div>
|
||
<div class="contrast-grid" aria-label="What Herdr is not">
|
||
<article>
|
||
<span>not a terminal emulator</span>
|
||
<p>Use Ghostty, Kitty, iTerm, Alacritty, or any terminal you already trust.</p>
|
||
</article>
|
||
<article>
|
||
<span>not a browser dashboard</span>
|
||
<p>No web view, hosted control plane, account, or reconstructed chat surface.</p>
|
||
</article>
|
||
<article>
|
||
<span>more than tmux for agents</span>
|
||
<p>Persistence plus mouse-native panes, state rollups, and commands agents can call.</p>
|
||
</article>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="section compare-section">
|
||
<div class="section-heading-wide">
|
||
<div>
|
||
<p class="section-kicker">Positioning</p>
|
||
<h2>tmux persistence, agent awareness, no terminal replacement.</h2>
|
||
</div>
|
||
<p>
|
||
tmux and Zellij own persistent terminal sessions. Desktop agent apps own dashboards, editors, or worktree flows. Herdr keeps the multiplexer inside your terminal and makes it agent-aware.
|
||
<a href="/compare/">See the full comparison →</a>
|
||
</p>
|
||
</div>
|
||
<div class="table-wrap">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Capability</th>
|
||
<th>tmux / Zellij</th>
|
||
<th>agent apps</th>
|
||
<th>worktree orchestrators</th>
|
||
<th>Herdr</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Runs inside your terminal</td>
|
||
<td>✓</td>
|
||
<td>—</td>
|
||
<td>—</td>
|
||
<td>✓</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Persistent PTY sessions</td>
|
||
<td>✓</td>
|
||
<td>limited</td>
|
||
<td>embedded</td>
|
||
<td>✓</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Remote SSH attach</td>
|
||
<td>✓</td>
|
||
<td>limited</td>
|
||
<td>remote projects</td>
|
||
<td>✓</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Semantic agent state</td>
|
||
<td>—</td>
|
||
<td>partial</td>
|
||
<td>workspace status</td>
|
||
<td>✓</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Direct agent attach</td>
|
||
<td>—</td>
|
||
<td>—</td>
|
||
<td>—</td>
|
||
<td>✓</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Agents can orchestrate it</td>
|
||
<td>scriptable</td>
|
||
<td>partial</td>
|
||
<td>workflow-owned</td>
|
||
<td>✓</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="compare-cards" aria-label="Mobile comparison">
|
||
<article>
|
||
<h3>Runs inside your terminal</h3>
|
||
<span>tmux/Zellij <b>✓</b></span>
|
||
<span>agent apps <b>—</b></span>
|
||
<span>worktree apps <b>—</b></span>
|
||
<span class="herdr">Herdr <b>✓</b></span>
|
||
</article>
|
||
<article>
|
||
<h3>Persistent PTY sessions</h3>
|
||
<span>tmux/Zellij <b>✓</b></span>
|
||
<span>agent apps <b>limited</b></span>
|
||
<span>worktree apps <b>embedded</b></span>
|
||
<span class="herdr">Herdr <b>✓</b></span>
|
||
</article>
|
||
<article>
|
||
<h3>Remote SSH attach</h3>
|
||
<span>tmux/Zellij <b>✓</b></span>
|
||
<span>agent apps <b>limited</b></span>
|
||
<span>worktree apps <b>remote projects</b></span>
|
||
<span class="herdr">Herdr <b>✓</b></span>
|
||
</article>
|
||
<article>
|
||
<h3>Semantic agent state</h3>
|
||
<span>tmux/Zellij <b>—</b></span>
|
||
<span>agent apps <b>partial</b></span>
|
||
<span>worktree apps <b>workspace status</b></span>
|
||
<span class="herdr">Herdr <b>✓</b></span>
|
||
</article>
|
||
<article>
|
||
<h3>Direct agent attach</h3>
|
||
<span>tmux/Zellij <b>—</b></span>
|
||
<span>agent apps <b>—</b></span>
|
||
<span>worktree apps <b>—</b></span>
|
||
<span class="herdr">Herdr <b>✓</b></span>
|
||
</article>
|
||
<article>
|
||
<h3>Agents can orchestrate it</h3>
|
||
<span>tmux/Zellij <b>scriptable</b></span>
|
||
<span>agent apps <b>partial</b></span>
|
||
<span>worktree apps <b>workflow-owned</b></span>
|
||
<span class="herdr">Herdr <b>✓</b></span>
|
||
</article>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="section feature-split">
|
||
<div class="feature-copy">
|
||
<p class="section-kicker">Your terminal</p>
|
||
<h2>Same shell, same keybinds, real panes.</h2>
|
||
<p>
|
||
Herdr does not replace your terminal or wrap agents in a
|
||
reconstructed chat view. It runs real processes in PTYs,
|
||
then adds layout, click targets, state, persistence, and
|
||
automation around them.
|
||
</p>
|
||
<ul class="check-list">
|
||
<li>Use the mouse for panes, tabs, workspaces, and menus.</li>
|
||
<li>Keep your terminal fonts, shortcuts, SSH, and shell setup.</li>
|
||
<li>Stay keyboard-first, or disable mouse input entirely.</li>
|
||
</ul>
|
||
</div>
|
||
<figure class="feature-mock" role="img" aria-label="Herdr showing split terminal panes">
|
||
<div class="mock-panes feature-mock-panes">
|
||
<div class="mock-pane focused">
|
||
<div class="mock-prompt"><span>~/Projects/herdr</span> <em>master</em></div>
|
||
<div><b>❯</b> <span class="cmd">just</span> <span class="str">test</span></div>
|
||
<div class="dim">cargo nextest run</div>
|
||
<div><span class="time">02:14:22</span> <span class="green">PASS</span> app::state::tests</div>
|
||
<div><span class="time">02:14:23</span> <span class="green">PASS</span> platform::pty::tests</div>
|
||
<div><span class="time">02:14:24</span> <span class="green">PASS</span> protocol::wire::tests</div>
|
||
<br />
|
||
<div><span class="green">3 passed</span> · 0 failed · 0.8s</div>
|
||
<div class="mock-prompt"><span>~/Projects/herdr</span> <em>master</em></div>
|
||
<div><b>❯</b> <span class="cursor"></span></div>
|
||
</div>
|
||
<div class="mock-pane">
|
||
<div class="mock-prompt"><span>~/Projects/herdr</span> <em>master</em></div>
|
||
<div><b>❯</b> <span class="cmd">git</span> <span class="str">status</span></div>
|
||
<div class="dim">On branch master</div>
|
||
<div>Your branch is up to date.</div>
|
||
<br />
|
||
<div class="dim">Changes not staged:</div>
|
||
<div><span class="red">M</span> src/app/state.rs</div>
|
||
<div><span class="green">??</span> docs/next/</div>
|
||
<br />
|
||
<div class="dim">2 files changed, 14 insertions</div>
|
||
</div>
|
||
</div>
|
||
</figure>
|
||
</section>
|
||
|
||
<section class="section pillars" aria-labelledby="pillars-title">
|
||
<div class="section-heading">
|
||
<p class="section-kicker">Core pillars</p>
|
||
<h2 id="pillars-title">Why terminal users switch.</h2>
|
||
</div>
|
||
<div class="pillar-grid">
|
||
<article class="pillar-card">
|
||
<span class="card-index">01</span>
|
||
<h3>Survives disconnects</h3>
|
||
<p>
|
||
Panes keep running in a background session server.
|
||
Close the terminal, lose SSH, or detach on purpose;
|
||
come back to the same herd later.
|
||
</p>
|
||
</article>
|
||
<article class="pillar-card">
|
||
<span class="card-index">02</span>
|
||
<h3>Runs where code lives</h3>
|
||
<p>
|
||
Use Herdr locally, inside any SSH session, or as a
|
||
thin client to a remote host. Keep credentials and
|
||
repos on the machine doing the work.
|
||
</p>
|
||
</article>
|
||
<article class="pillar-card">
|
||
<span class="card-index">03</span>
|
||
<h3>Mouse-native TUI</h3>
|
||
<p>
|
||
Click panes, tabs, workspaces, and agents. Drag
|
||
borders, select text, use right-click menus, then
|
||
stay on the keyboard whenever that is faster.
|
||
</p>
|
||
</article>
|
||
<article class="pillar-card">
|
||
<span class="card-index">04</span>
|
||
<h3>Agents can drive it</h3>
|
||
<p>
|
||
The CLI and socket API let agents create panes, run
|
||
commands, read output, wait for changes, and operate
|
||
their workspace without hand-shuffled terminals.
|
||
</p>
|
||
</article>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="section access-section">
|
||
<div class="section-heading">
|
||
<p class="section-kicker">Local or remote</p>
|
||
<h2>Run Herdr where the work lives.</h2>
|
||
</div>
|
||
<div class="access-grid">
|
||
<article class="access-card">
|
||
<span class="access-label">local</span>
|
||
<h3>Your machine</h3>
|
||
<p>
|
||
Start a local session, split panes, create tabs, and
|
||
keep agents running while your terminal comes and
|
||
goes.
|
||
</p>
|
||
<pre><code>$ herdr</code></pre>
|
||
</article>
|
||
<article class="access-card">
|
||
<span class="access-label">ssh</span>
|
||
<h3>Any SSH client</h3>
|
||
<p>
|
||
Use Herdr like tmux on a remote box. The session
|
||
stays on the server after you detach.
|
||
</p>
|
||
<pre><code>$ ssh you@server
|
||
$ herdr</code></pre>
|
||
</article>
|
||
<article class="access-card">
|
||
<span class="access-label">thin client</span>
|
||
<h3>Remote attach</h3>
|
||
<p>
|
||
Point your local Herdr client at an SSH config host
|
||
and stream the remote TUI back to your terminal.
|
||
</p>
|
||
<pre><code>$ herdr --remote workbox</code></pre>
|
||
</article>
|
||
</div>
|
||
<div class="mobile-proof">
|
||
<div class="mobile-copy">
|
||
<p class="section-kicker">Responsive TUI</p>
|
||
<h3>Mobile-first when the terminal gets small.</h3>
|
||
<p>
|
||
Herdr stays usable over SSH from a phone or tablet.
|
||
The terminal view remains real, while narrow screens
|
||
get a switcher built for touch-sized decisions.
|
||
</p>
|
||
</div>
|
||
<div class="mobile-shots">
|
||
<figure>
|
||
<img
|
||
src="/assets/mobile-agent-session-v2.jpeg"
|
||
alt="Herdr agent session over SSH on a phone"
|
||
loading="lazy"
|
||
/>
|
||
<figcaption>agent session over SSH</figcaption>
|
||
</figure>
|
||
<figure>
|
||
<img
|
||
src="/assets/mobile-switch-menu-v2.jpeg"
|
||
alt="Herdr responsive switch menu on a phone"
|
||
loading="lazy"
|
||
/>
|
||
<figcaption>responsive switch menu</figcaption>
|
||
</figure>
|
||
<p class="mobile-shout">
|
||
screenshots taken with
|
||
<a href="https://getmoshi.app/" target="_blank" rel="noopener">moshi</a>
|
||
❤️ on iPhone
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="section api-section">
|
||
<div class="api-copy">
|
||
<p class="section-kicker">Runtime surface</p>
|
||
<h2>The UI is also a control surface.</h2>
|
||
<p>
|
||
Herdr exposes workspace, tab, pane, output, and wait
|
||
operations through a CLI and newline-delimited JSON
|
||
socket API. Agents can operate their own workspace
|
||
instead of asking you to shuffle terminals by hand.
|
||
</p>
|
||
<div class="inline-links">
|
||
<a href="/docs/cli-reference/">CLI reference →</a>
|
||
<a href="/docs/socket-api/">Socket API →</a>
|
||
<a href="/docs/agent-skill/">Agent skill →</a>
|
||
</div>
|
||
</div>
|
||
<div class="code-card" aria-label="Herdr CLI orchestration example">
|
||
<pre><code><span class="comment"># create workspace structure</span>
|
||
<span class="cmd">herdr</span> workspace create <span class="flag">--cwd</span> <span class="str">~/project</span> <span class="flag">--label</span> <span class="str">api</span>
|
||
<span class="cmd">herdr</span> tab create <span class="flag">--label</span> <span class="str">logs</span>
|
||
|
||
<span class="comment"># split a pane and run work</span>
|
||
<span class="cmd">herdr</span> pane split <span class="str">1-1</span> <span class="flag">--direction</span> <span class="str">right</span>
|
||
<span class="cmd">herdr</span> pane run <span class="str">1-2</span> <span class="str">"just test"</span>
|
||
|
||
<span class="comment"># wait, inspect, and continue</span>
|
||
<span class="cmd">herdr</span> wait agent-status <span class="str">1-1</span> <span class="flag">--status</span> <span class="str">done</span>
|
||
<span class="cmd">herdr</span> pane read <span class="str">1-2</span> <span class="flag">--source</span> <span class="str">recent-unwrapped</span></code></pre>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="section awareness-section">
|
||
<div class="section-heading">
|
||
<p class="section-kicker">Agent awareness</p>
|
||
<h2>Keep the herd in sight.</h2>
|
||
</div>
|
||
<div class="awareness-grid">
|
||
<article class="awareness-card">
|
||
<figure class="awareness-mock" role="img" aria-label="Herdr workspace sidebar showing state rollups">
|
||
<div class="mock-sidebar">
|
||
<div class="mock-sidebar-title">spaces</div>
|
||
<div class="mock-space active">
|
||
<span class="mock-dot dot-done"></span>
|
||
<div><strong>herdr</strong><small>master</small></div>
|
||
</div>
|
||
<div class="mock-space">
|
||
<span class="mock-dot dot-working"></span>
|
||
<div><strong>llm-proxy</strong><small>master</small></div>
|
||
</div>
|
||
<div class="mock-space">
|
||
<span class="mock-dot dot-blocked"></span>
|
||
<div><strong>docs-site</strong><small>docs/palette</small></div>
|
||
</div>
|
||
<div class="mock-space">
|
||
<span class="mock-dot dot-idle"></span>
|
||
<div><strong>qmp</strong><small>autoresearch/…</small></div>
|
||
</div>
|
||
<div class="mock-sidebar-spacer"></div>
|
||
</div>
|
||
</figure>
|
||
<h3>Workspace rollups</h3>
|
||
<p>
|
||
Each workspace rolls up to its most urgent agent
|
||
state, so the whole session is scannable.
|
||
</p>
|
||
</article>
|
||
<article class="awareness-card">
|
||
<figure class="awareness-mock" role="img" aria-label="Herdr agent list showing states">
|
||
<div class="mock-sidebar">
|
||
<div class="mock-agent-title"><span>agents</span><span>all</span></div>
|
||
<div class="mock-agent"><span>✓</span><div><strong>doc</strong><small>idle · pi</small></div></div>
|
||
<div class="mock-agent working"><span class="braille-spinner"></span><div><strong>custom-row</strong><small>working · pi</small></div></div>
|
||
<div class="mock-agent blocked"><span>◉</span><div><strong>llm-proxy</strong><small>blocked · claude</small></div></div>
|
||
<div class="mock-agent done"><span>●</span><div><strong>qmp</strong><small>done · codex</small></div></div>
|
||
<div class="mock-sidebar-spacer"></div>
|
||
</div>
|
||
</figure>
|
||
<h3>Agent list</h3>
|
||
<p>
|
||
See which agents are blocked, working, done, idle, or
|
||
unknown without opening every pane.
|
||
</p>
|
||
</article>
|
||
</div>
|
||
<div class="state-legend">
|
||
<span><i class="status-dot dot-blocked"></i>blocked</span>
|
||
<span><i class="status-dot dot-working"></i>working</span>
|
||
<span><i class="status-dot dot-done"></i>done</span>
|
||
<span><i class="status-dot dot-idle"></i>idle</span>
|
||
</div>
|
||
</section>
|
||
|
||
|
||
<section class="section integrations-section">
|
||
<div class="section-heading">
|
||
<p class="section-kicker">Integrations</p>
|
||
<h2>Bring your existing agents into the herd.</h2>
|
||
</div>
|
||
<div class="agent-grid" aria-label="Supported agents">
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/pi.svg')"></span><span>Pi</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/claude.svg')"></span><span>Claude Code</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/codex-mask.svg')"></span><span>Codex</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/droid.png')"></span><span>Droid</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/amp.svg')"></span><span>Amp</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/opencode.svg')"></span><span>OpenCode</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/grok.svg')"></span><span>Grok CLI</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/hermes.svg')"></span><span>Hermes</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/cursor.svg')"></span><span>Cursor</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/antigravity.svg')"></span><span>Antigravity</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/kimi.svg')"></span><span>Kimi</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/kiro-mask.svg')"></span><span>Kiro</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/github-copilot.svg')"></span><span>Copilot</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/qoder-mask.svg')"></span><span>Qoder CLI</span></div>
|
||
</div>
|
||
<p class="integration-note">
|
||
Any terminal agent works in Herdr. Use the Herdr API to add
|
||
richer state, orchestration, and custom workflows;
|
||
<a href="/docs/integrations/">see example integrations</a>
|
||
or build your own for anything.
|
||
</p>
|
||
</section>
|
||
|
||
<section class="final-cta">
|
||
<p class="section-kicker">Start the herd</p>
|
||
<h2>Install Herdr, run two agents, detach once.</h2>
|
||
<p>
|
||
The value shows up the first time the herd keeps running
|
||
after your terminal disappears.
|
||
</p>
|
||
<div class="hero-actions center">
|
||
<a class="button button-primary" href="/docs/install/"
|
||
>Install Herdr</a
|
||
>
|
||
<a class="button button-secondary" href="/docs/quick-start/"
|
||
>Quick start</a
|
||
>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
|
||
<footer class="footer">
|
||
<a class="brand" href="/" aria-label="Herdr home">
|
||
<span class="brand-logo" aria-hidden="true"></span>
|
||
<span>herdr</span>
|
||
</a>
|
||
<div>
|
||
<a href="/compare/">Compare</a>
|
||
<a href="/docs/">Docs</a>
|
||
<a href="/docs/configuration/">Configuration</a>
|
||
<a href="/docs/socket-api/">API</a>
|
||
<a href="https://github.com/ogulcancelik/herdr/releases"
|
||
>Releases</a
|
||
>
|
||
<a href="mailto:hey@herdr.dev">hey@herdr.dev</a>
|
||
</div>
|
||
<nav class="footer-social" aria-label="Social links">
|
||
<a href="https://github.com/ogulcancelik/herdr" aria-label="Herdr on GitHub">
|
||
<svg viewBox="0 0 16 16" aria-hidden="true">
|
||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82A7.64 7.64 0 0 1 8 3.87c.68 0 1.36.09 2 .26 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z" />
|
||
</svg>
|
||
</a>
|
||
<a href="https://x.com/herdrdev" aria-label="Herdr on X">
|
||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231Zm-1.161 17.52h1.833L7.084 4.126H5.117Z" />
|
||
</svg>
|
||
</a>
|
||
</nav>
|
||
</footer>
|
||
|
||
<div class="logo-menu" id="logo-menu" hidden>
|
||
<a href="/assets/logo.svg" download="herdr-logo.svg">Download SVG</a>
|
||
<a href="/assets/logo.png" download="herdr-logo.png">Download PNG</a>
|
||
<button type="button" id="copy-logo-url">Copy SVG URL</button>
|
||
</div>
|
||
|
||
<div class="theme-hint" id="theme-hint" title="Press / or t to switch theme">
|
||
<span class="theme-hint-dot"></span>
|
||
<span class="theme-hint-label" id="theme-hint-label">light</span>
|
||
<span class="theme-hint-key">[/]</span>
|
||
</div>
|
||
|
||
<div class="theme-overlay" id="theme-overlay" hidden>
|
||
<div class="theme-overlay-panel" role="dialog" aria-label="Theme switcher" aria-modal="true">
|
||
<div class="theme-overlay-title">settings</div>
|
||
<div class="theme-overlay-tabs">
|
||
<span class="active">theme</span>
|
||
</div>
|
||
<div class="theme-overlay-list" id="theme-overlay-list" role="listbox" aria-label="Themes">
|
||
<div class="theme-sep"><span>dark</span></div>
|
||
<button type="button" role="option" data-value="catppuccin">catppuccin</button>
|
||
<button type="button" role="option" data-value="terminal">terminal</button>
|
||
<button type="button" role="option" data-value="tokyo-night">tokyo night</button>
|
||
<button type="button" role="option" data-value="dracula">dracula</button>
|
||
<button type="button" role="option" data-value="nord">nord</button>
|
||
<button type="button" role="option" data-value="gruvbox">gruvbox</button>
|
||
<button type="button" role="option" data-value="one-dark">one dark</button>
|
||
<button type="button" role="option" data-value="solarized">solarized</button>
|
||
<button type="button" role="option" data-value="kanagawa">kanagawa</button>
|
||
<button type="button" role="option" data-value="rose-pine">rose pine</button>
|
||
<button type="button" role="option" data-value="vesper">vesper</button>
|
||
<div class="theme-sep"><span>light</span></div>
|
||
<button type="button" role="option" data-value="catppuccin-latte">catppuccin latte</button>
|
||
<button type="button" role="option" data-value="tokyo-night-day">tokyo day</button>
|
||
<button type="button" role="option" data-value="gruvbox-light">gruvbox light</button>
|
||
<button type="button" role="option" data-value="one-light">one light</button>
|
||
<button type="button" role="option" data-value="solarized-light">solarized light</button>
|
||
<button type="button" role="option" data-value="kanagawa-lotus">kanagawa lotus</button>
|
||
<button type="button" role="option" data-value="rose-pine-dawn">rose pine dawn</button>
|
||
</div>
|
||
<div class="theme-overlay-footer">
|
||
<span class="theme-overlay-hints"><span class="key">↑↓</span> select</span>
|
||
<span class="theme-overlay-actions"><span class="theme-apply-btn">↵ apply</span><button class="theme-close-btn" type="button" id="theme-close-btn">esc close</button></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
(function () {
|
||
var overlay = document.getElementById("theme-overlay");
|
||
var list = document.getElementById("theme-overlay-list");
|
||
var hintLabel = document.getElementById("theme-hint-label");
|
||
if (!overlay || !list) return;
|
||
|
||
var options = Array.prototype.slice.call(list.querySelectorAll("[role='option']"));
|
||
var media = window.matchMedia("(prefers-color-scheme: dark)");
|
||
var saved = localStorage.getItem("herdr-palette") || "";
|
||
var committed = saved || defaultPalette();
|
||
|
||
function defaultPalette() {
|
||
return media.matches ? "tokyo-night" : "tokyo-night-day";
|
||
}
|
||
|
||
function setPalette(value) {
|
||
document.documentElement.setAttribute("data-palette", value || defaultPalette());
|
||
}
|
||
|
||
function updateUI(value) {
|
||
var activeValue = value || defaultPalette();
|
||
options.forEach(function (opt) {
|
||
var selected = opt.dataset.value === activeValue;
|
||
opt.setAttribute("aria-selected", String(selected));
|
||
});
|
||
var active = options.find(function (o) { return o.dataset.value === activeValue; });
|
||
if (hintLabel && active) hintLabel.textContent = active.textContent.trim();
|
||
}
|
||
|
||
function openOverlay() {
|
||
overlay.hidden = false;
|
||
document.body.style.overflow = "hidden";
|
||
committed = localStorage.getItem("herdr-palette") || defaultPalette();
|
||
updateUI(committed);
|
||
var active = list.querySelector("[aria-selected='true']") || options[0];
|
||
if (active) active.focus();
|
||
}
|
||
|
||
function closeOverlay() {
|
||
overlay.hidden = true;
|
||
document.body.style.overflow = "";
|
||
}
|
||
|
||
function toggleOverlay() {
|
||
if (overlay.hidden) openOverlay();
|
||
else closeOverlay();
|
||
}
|
||
|
||
function preview(index) {
|
||
if (index < 0 || index >= options.length) return;
|
||
setPalette(options[index].dataset.value);
|
||
updateUI(options[index].dataset.value);
|
||
options[index].focus();
|
||
}
|
||
|
||
function commit(value) {
|
||
setPalette(value);
|
||
localStorage.setItem("herdr-palette", value);
|
||
updateUI(value);
|
||
closeOverlay();
|
||
}
|
||
|
||
function revert() {
|
||
setPalette(committed);
|
||
updateUI(committed);
|
||
closeOverlay();
|
||
}
|
||
|
||
media.addEventListener("change", function () {
|
||
if (!localStorage.getItem("herdr-palette")) {
|
||
setPalette("");
|
||
updateUI("");
|
||
}
|
||
});
|
||
|
||
options.forEach(function (opt, idx) {
|
||
opt.addEventListener("click", function () {
|
||
commit(opt.dataset.value);
|
||
});
|
||
opt.addEventListener("mouseenter", function () {
|
||
preview(idx);
|
||
});
|
||
});
|
||
|
||
document.addEventListener("keydown", function (e) {
|
||
if ((e.key === "t" || e.key === "/") && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
||
var tag = document.activeElement && document.activeElement.tagName;
|
||
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return;
|
||
e.preventDefault();
|
||
toggleOverlay();
|
||
return;
|
||
}
|
||
if (overlay.hidden) return;
|
||
var current = document.activeElement;
|
||
var idx = options.indexOf(current);
|
||
if (e.key === "ArrowDown") {
|
||
e.preventDefault();
|
||
var nextIdx = idx >= 0 ? Math.min(options.length - 1, idx + 1) : 0;
|
||
preview(nextIdx);
|
||
} else if (e.key === "ArrowUp") {
|
||
e.preventDefault();
|
||
var prevIdx = idx >= 0 ? Math.max(0, idx - 1) : options.length - 1;
|
||
preview(prevIdx);
|
||
} else if (e.key === "Enter" && idx >= 0) {
|
||
e.preventDefault();
|
||
commit(options[idx].dataset.value);
|
||
} else if (e.key === "Escape") {
|
||
e.preventDefault();
|
||
revert();
|
||
}
|
||
});
|
||
|
||
var themeHint = document.getElementById("theme-hint");
|
||
if (localStorage.getItem("herdr-theme-hint-seen") === "1") {
|
||
themeHint.classList.add("is-seen");
|
||
}
|
||
themeHint.addEventListener("mouseenter", function () {
|
||
localStorage.setItem("herdr-theme-hint-seen", "1");
|
||
themeHint.classList.add("is-seen");
|
||
}, { once: true });
|
||
themeHint.addEventListener("click", toggleOverlay);
|
||
document.getElementById("theme-close-btn").addEventListener("click", revert);
|
||
|
||
setPalette(saved);
|
||
updateUI(saved);
|
||
})();
|
||
|
||
(function () {
|
||
var menu = document.getElementById("logo-menu");
|
||
var copyButton = document.getElementById("copy-logo-url");
|
||
var logos = document.querySelectorAll(".brand-logo");
|
||
if (!menu || !logos.length) return;
|
||
|
||
function hideMenu() {
|
||
menu.hidden = true;
|
||
}
|
||
|
||
function showMenu(event) {
|
||
event.preventDefault();
|
||
menu.hidden = false;
|
||
var menuWidth = menu.offsetWidth;
|
||
var menuHeight = menu.offsetHeight;
|
||
var left = Math.min(event.clientX, window.innerWidth - menuWidth - 12);
|
||
var top = Math.min(event.clientY, window.innerHeight - menuHeight - 12);
|
||
menu.style.left = Math.max(12, left) + "px";
|
||
menu.style.top = Math.max(12, top) + "px";
|
||
}
|
||
|
||
logos.forEach(function (logo) {
|
||
logo.addEventListener("contextmenu", showMenu);
|
||
});
|
||
|
||
document.addEventListener("click", function (event) {
|
||
if (!menu.contains(event.target)) hideMenu();
|
||
});
|
||
document.addEventListener("keydown", function (event) {
|
||
if (event.key === "Escape") hideMenu();
|
||
});
|
||
window.addEventListener("scroll", hideMenu, { passive: true });
|
||
window.addEventListener("resize", hideMenu);
|
||
|
||
if (copyButton) {
|
||
copyButton.addEventListener("click", async function () {
|
||
var url = new URL("/assets/logo.svg", window.location.href).href;
|
||
try {
|
||
await navigator.clipboard.writeText(url);
|
||
copyButton.textContent = "Copied";
|
||
setTimeout(function () {
|
||
copyButton.textContent = "Copy SVG URL";
|
||
}, 1500);
|
||
} catch (error) {
|
||
window.prompt("Copy logo URL", url);
|
||
}
|
||
});
|
||
}
|
||
})();
|
||
|
||
(function () {
|
||
var reduceMotion = window.matchMedia(
|
||
"(prefers-reduced-motion: reduce)",
|
||
).matches;
|
||
if (reduceMotion) return;
|
||
|
||
document.querySelectorAll(".logo-marquee").forEach(function (marquee) {
|
||
var track = marquee.querySelector(".logo-track");
|
||
if (!track || !track.getAnimations) return;
|
||
|
||
function setPlaybackRate(rate) {
|
||
var animation = track.getAnimations().find(function (candidate) {
|
||
return candidate.animationName === "logo-marquee";
|
||
});
|
||
if (!animation) return;
|
||
|
||
if (animation.updatePlaybackRate) {
|
||
animation.updatePlaybackRate(rate);
|
||
} else {
|
||
animation.playbackRate = rate;
|
||
}
|
||
}
|
||
|
||
marquee.addEventListener("mouseenter", function () {
|
||
setPlaybackRate(0.6);
|
||
});
|
||
marquee.addEventListener("mouseleave", function () {
|
||
setPlaybackRate(1);
|
||
});
|
||
});
|
||
})();
|
||
|
||
(function () {
|
||
var copyButton = document.getElementById("copy-btn");
|
||
var command = document.getElementById("install-cmd");
|
||
var methods = Array.prototype.slice.call(
|
||
document.querySelectorAll(".install-method"),
|
||
);
|
||
if (!copyButton || !command || methods.length === 0) return;
|
||
|
||
methods.forEach(function (method) {
|
||
method.addEventListener("click", function () {
|
||
methods.forEach(function (entry) {
|
||
var active = entry === method;
|
||
entry.classList.toggle("is-active", active);
|
||
entry.setAttribute("aria-pressed", String(active));
|
||
});
|
||
command.textContent = method.dataset.command;
|
||
command.classList.toggle(
|
||
"is-compact",
|
||
method.dataset.size === "compact",
|
||
);
|
||
copyButton.textContent = "Copy";
|
||
});
|
||
});
|
||
|
||
copyButton.addEventListener("click", async function () {
|
||
try {
|
||
await navigator.clipboard.writeText(
|
||
command.textContent.trim(),
|
||
);
|
||
copyButton.textContent = "Copied";
|
||
setTimeout(function () {
|
||
copyButton.textContent = "Copy";
|
||
}, 1500);
|
||
} catch (error) {
|
||
copyButton.textContent = "Select";
|
||
}
|
||
});
|
||
})();
|
||
|
||
(function () {
|
||
var frames = ["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];
|
||
var els = document.querySelectorAll(".braille-spinner");
|
||
if (!els.length) return;
|
||
var states = Array.prototype.map.call(els, function () { return 0; });
|
||
els.forEach(function (el, idx) {
|
||
el.textContent = frames[0];
|
||
});
|
||
setInterval(function () {
|
||
els.forEach(function (el, idx) {
|
||
states[idx] = (states[idx] + 1) % frames.length;
|
||
el.textContent = frames[states[idx]];
|
||
});
|
||
}, 80);
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|