diff --git a/docs/guides/docusaurus-site-guide.md b/docs/guides/docusaurus-site-guide.md
index ae404f4d..3dea82c3 100644
--- a/docs/guides/docusaurus-site-guide.md
+++ b/docs/guides/docusaurus-site-guide.md
@@ -243,6 +243,17 @@ The site deploys automatically via GitHub Actions when changes to `website/` are
**First-time setup**: Enable GitHub Pages in repo Settings → Pages → Source: GitHub Actions
+## Style Guidelines
+
+**No emoji**: Do not use emoji in the site content, components, or documentation. Use icon fonts (Lucide) for visual indicators instead.
+
+**Icons**: The site uses [Lucide](https://lucide.dev) icon font. Use CSS classes like `icon-copy`, `icon-check`, `icon-arrow-right`. Example:
+```jsx
+
+```
+
+**Terminal aesthetic**: Maintain the TUI/terminal visual style. Use monospace fonts, muted colors with bright accents, and clean 1px borders.
+
## Common Tasks
### Add a new docs section
diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js
index 11b429c0..b5985405 100644
--- a/website/docusaurus.config.js
+++ b/website/docusaurus.config.js
@@ -30,6 +30,10 @@ const config = {
projectName: 'sidecar',
trailingSlash: false,
+ customFields: {
+ githubUrl: 'https://github.com/marcus/sidecar',
+ },
+
onBrokenLinks: 'throw',
// Even if you don't use internationalization, you can use this field to set
@@ -40,6 +44,35 @@ const config = {
locales: ['en'],
},
+ headTags: [
+ {
+ tagName: 'link',
+ attributes: {
+ rel: 'preconnect',
+ href: 'https://fonts.googleapis.com',
+ },
+ },
+ {
+ tagName: 'link',
+ attributes: {
+ rel: 'preconnect',
+ href: 'https://fonts.gstatic.com',
+ crossorigin: 'anonymous',
+ },
+ },
+ ],
+
+ stylesheets: [
+ {
+ href: 'https://fonts.googleapis.com/css2?family=Google+Sans+Code:ital,wght@0,300..800;1,300..800&family=JetBrains+Mono:wght@400;500;600;700&display=swap',
+ type: 'text/css',
+ },
+ {
+ href: 'https://cdn.jsdelivr.net/npm/lucide-static@latest/font/lucide.css',
+ type: 'text/css',
+ },
+ ],
+
presets: [
[
'classic',
@@ -73,7 +106,9 @@ const config = {
// Replace with your project's social card
image: 'img/docusaurus-social-card.jpg',
colorMode: {
- respectPrefersColorScheme: true,
+ defaultMode: 'dark',
+ disableSwitch: true,
+ respectPrefersColorScheme: false,
},
navbar: {
title: 'Sidecar',
diff --git a/website/src/css/custom.css b/website/src/css/custom.css
index 2bc6a4cf..d350db7a 100644
--- a/website/src/css/custom.css
+++ b/website/src/css/custom.css
@@ -1,30 +1,986 @@
+/* External fonts - @import must be at top */
+@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap');
+@import url('https://cdn.jsdelivr.net/npm/lucide-static@latest/font/lucide.css');
+
/**
- * Any CSS included here will be global. The classic template
- * bundles Infima by default. Infima is a CSS framework designed to
- * work well for content-centric websites.
+ * Sidecar TUI-themed Docusaurus styles
+ * Monokai-ish dark base with bright terminal accents
*/
-/* You can override the default Infima variables here. */
:root {
- --ifm-color-primary: #2e8555;
- --ifm-color-primary-dark: #29784c;
- --ifm-color-primary-darker: #277148;
- --ifm-color-primary-darkest: #205d3b;
- --ifm-color-primary-light: #33925d;
- --ifm-color-primary-lighter: #359962;
- --ifm-color-primary-lightest: #3cad6e;
- --ifm-code-font-size: 95%;
- --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
-}
-
-/* For readability concerns, you should choose a lighter palette in dark mode. */
+ /* Docusaurus base - monokai green */
+ --ifm-color-primary: #a6e22e;
+ --ifm-color-primary-dark: #92d521;
+ --ifm-color-primary-darker: #86c81e;
+ --ifm-color-primary-darkest: #6aa314;
+ --ifm-color-primary-light: #b4f042;
+ --ifm-color-primary-lighter: #baf24d;
+ --ifm-color-primary-lightest: #c8f66a;
+
+ --ifm-background-color: #121417;
+ --ifm-background-surface-color: #171a1f;
+ --ifm-font-color-base: #e8e8e3;
+ --ifm-font-color-secondary: rgba(232, 232, 227, 0.78);
+
+ --ifm-code-font-size: 92%;
+ /* Primary: Google Sans Code (self-host if needed), Secondary: JetBrains Mono */
+ --ifm-font-family-base: "Google Sans Code", "JetBrains Mono", ui-monospace,
+ SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
+ --ifm-heading-font-family: var(--ifm-font-family-base);
+
+ /* Sidecar-ish accents */
+ --sc-pink: #f92672;
+ --sc-blue: #66d9ef;
+ --sc-purple: #ae81ff;
+ --sc-yellow: #e6db74;
+ --sc-orange: #fd971f;
+
+ /* TUI surfaces */
+ --sc-panel: #171a1f;
+ --sc-panel-2: #1b1f26;
+ --sc-border: rgba(255, 255, 255, 0.08);
+ --sc-border-2: rgba(255, 255, 255, 0.12);
+ --sc-glow: rgba(102, 217, 239, 0.08);
+
+ --sc-radius: 14px;
+ --sc-shadow: 0 18px 60px rgba(0, 0, 0, 0.45);
+
+ --docusaurus-highlighted-code-line-bg: rgba(166, 226, 46, 0.15);
+}
+
+/* Dark theme overrides */
[data-theme='dark'] {
- --ifm-color-primary: #25c2a0;
- --ifm-color-primary-dark: #21af90;
- --ifm-color-primary-darker: #1fa588;
- --ifm-color-primary-darkest: #1a8870;
- --ifm-color-primary-light: #29d5b0;
- --ifm-color-primary-lighter: #32d8b4;
- --ifm-color-primary-lightest: #4fddbf;
- --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
+ --ifm-color-primary: #a6e22e;
+ --ifm-color-primary-dark: #92d521;
+ --ifm-color-primary-darker: #86c81e;
+ --ifm-color-primary-darkest: #6aa314;
+ --ifm-color-primary-light: #b4f042;
+ --ifm-color-primary-lighter: #baf24d;
+ --ifm-color-primary-lightest: #c8f66a;
+ --ifm-background-color: #121417;
+ --ifm-background-surface-color: #171a1f;
+ --docusaurus-highlighted-code-line-bg: rgba(166, 226, 46, 0.15);
+}
+
+/* Navbar styling */
+html[data-theme="dark"] .navbar {
+ background: rgba(18, 20, 23, 0.7);
+ backdrop-filter: blur(10px);
+ border-bottom: 1px solid var(--sc-border);
+}
+
+html[data-theme="dark"] .navbar__brand {
+ font-weight: 700;
+ letter-spacing: 0.2px;
+}
+
+html[data-theme="dark"] .footer {
+ border-top: 1px solid var(--sc-border);
+ background: #0f1114;
+}
+
+/* Full-page gradient background */
+html[data-theme="dark"]::before {
+ content: "";
+ position: fixed;
+ inset: 0;
+ z-index: -1;
+ background:
+ radial-gradient(ellipse 1200px 600px at 15% 5%, rgba(249, 38, 114, 0.12), transparent 50%),
+ radial-gradient(ellipse 1200px 600px at 85% 8%, rgba(102, 217, 239, 0.10), transparent 50%),
+ radial-gradient(ellipse 1400px 800px at 50% 40%, rgba(166, 226, 46, 0.06), transparent 50%),
+ radial-gradient(ellipse 1200px 600px at 20% 70%, rgba(174, 129, 255, 0.08), transparent 50%),
+ radial-gradient(ellipse 1200px 600px at 80% 90%, rgba(230, 219, 116, 0.06), transparent 50%);
+ pointer-events: none;
+}
+
+/* Terminal-bright links */
+a {
+ text-decoration-thickness: 1px;
+ text-underline-offset: 3px;
+}
+
+a:hover {
+ text-decoration-thickness: 2px;
+}
+
+/* ---------- Homepage layout ---------- */
+
+.sc-hero {
+ position: relative;
+ padding: 56px 0 22px;
+ overflow: hidden;
+}
+
+.sc-hero::before {
+ content: "";
+ position: absolute;
+ inset: -60px;
+ background:
+ radial-gradient(900px 420px at 20% 10%, rgba(249, 38, 114, 0.14), transparent 60%),
+ radial-gradient(900px 420px at 80% 5%, rgba(102, 217, 239, 0.12), transparent 60%),
+ radial-gradient(900px 520px at 55% 85%, rgba(166, 226, 46, 0.10), transparent 60%);
+ pointer-events: none;
+}
+
+.sc-hero::after {
+ /* subtle scanlines */
+ content: "";
+ position: absolute;
+ inset: 0;
+ background: linear-gradient(
+ to bottom,
+ rgba(255, 255, 255, 0.03),
+ rgba(255, 255, 255, 0.0) 2px
+ );
+ background-size: 100% 10px;
+ opacity: 0.07;
+ mix-blend-mode: overlay;
+ pointer-events: none;
+}
+
+.sc-heroInner {
+ position: relative;
+ display: grid;
+ grid-template-columns: 1.05fr 1fr;
+ gap: 28px;
+ align-items: start;
+}
+
+@media (max-width: 996px) {
+ .sc-heroInner {
+ grid-template-columns: 1fr;
+ }
+}
+
+.sc-title {
+ font-size: clamp(34px, 4.2vw, 52px);
+ line-height: 1.03;
+ letter-spacing: -0.7px;
+ margin: 0 0 12px;
+}
+
+.sc-subtitle {
+ margin: 0 0 18px;
+ font-size: 15px;
+ line-height: 1.55;
+ color: var(--ifm-font-color-secondary);
+ max-width: 58ch;
+}
+
+.sc-badges {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ margin: 0 0 18px;
+}
+
+.sc-badge {
+ border: 1px solid var(--sc-border);
+ background: rgba(23, 26, 31, 0.6);
+ border-radius: 999px;
+ padding: 6px 10px;
+ font-size: 12px;
+ color: rgba(232, 232, 227, 0.86);
+}
+
+.sc-actions {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ margin-top: 6px;
+}
+
+.sc-btn {
+ display: inline-flex;
+ align-items: center;
+ gap: 10px;
+ border-radius: 12px;
+ padding: 10px 12px;
+ border: 1px solid var(--sc-border-2);
+ background: rgba(23, 26, 31, 0.8);
+ box-shadow: 0 10px 30px rgba(0,0,0,0.35);
+ font-weight: 650;
+ letter-spacing: 0.2px;
+ color: var(--ifm-font-color-base);
+}
+
+.sc-btnPrimary {
+ border-color: rgba(166, 226, 46, 0.35);
+ background:
+ linear-gradient(180deg, rgba(166, 226, 46, 0.16), rgba(23, 26, 31, 0.86));
+}
+
+.sc-btn:hover {
+ transform: translateY(-1px);
+ transition: transform 160ms ease, border-color 160ms ease;
+ border-color: rgba(255,255,255,0.18);
+ text-decoration: none;
+ color: var(--ifm-font-color-base);
+}
+
+.sc-codeInline {
+ font-size: 12px;
+ padding: 3px 8px;
+ border-radius: 999px;
+ border: 1px solid var(--sc-border);
+ background: rgba(0,0,0,0.35);
+ color: var(--sc-yellow);
+}
+
+/* ---------- TUI frame ---------- */
+
+.sc-frame {
+ border-radius: var(--sc-radius);
+ background: linear-gradient(180deg, rgba(27, 31, 38, 0.92), rgba(23, 26, 31, 0.92));
+ border: 1px solid var(--sc-border);
+ box-shadow: var(--sc-shadow);
+ overflow: hidden;
+}
+
+.sc-frameTop {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 10px 12px;
+ border-bottom: 1px solid var(--sc-border);
+ background: rgba(18, 20, 23, 0.55);
+}
+
+.sc-dots {
+ display: inline-flex;
+ gap: 6px;
+ align-items: center;
+}
+
+.sc-dot {
+ width: 10px;
+ height: 10px;
+ border-radius: 999px;
+ border: 1px solid var(--sc-border);
+ background: rgba(255,255,255,0.06);
+}
+
+.sc-topRight {
+ display: inline-flex;
+ gap: 8px;
+ align-items: center;
+ color: rgba(232,232,227,0.6);
+ font-size: 12px;
+}
+
+.sc-tabs {
+ display: flex;
+ gap: 8px;
+ padding: 10px 12px;
+ border-bottom: 1px solid var(--sc-border);
+ background: rgba(23, 26, 31, 0.55);
+}
+
+.sc-tab {
+ font-size: 12px;
+ padding: 6px 10px;
+ border-radius: 999px;
+ border: 1px solid var(--sc-border);
+ color: rgba(232,232,227,0.78);
+ background: rgba(0,0,0,0.15);
+ cursor: pointer;
+ font-family: var(--ifm-font-family-base);
+ transition: border-color 120ms ease, background 120ms ease;
+}
+
+.sc-tab:hover {
+ border-color: rgba(255,255,255,0.15);
+ background: rgba(255,255,255,0.04);
+}
+
+.sc-tabActive {
+ color: rgba(232,232,227,0.92);
+ border-color: rgba(102, 217, 239, 0.35);
+ background: rgba(102, 217, 239, 0.08);
+ box-shadow: 0 0 0 3px rgba(102, 217, 239, 0.06);
+}
+
+.sc-tabActive:hover {
+ border-color: rgba(102, 217, 239, 0.5);
+ background: rgba(102, 217, 239, 0.12);
+}
+
+.sc-frameBody {
+ display: grid;
+ grid-template-columns: 220px 1fr;
+ min-height: 360px;
+}
+
+@media (max-width: 996px) {
+ .sc-frameBody {
+ grid-template-columns: 1fr;
+ }
+}
+
+.sc-frameBodySingle {
+ height: 380px;
+ overflow: hidden;
+}
+
+.sc-frameBodySingle .sc-pane {
+ height: 100%;
+ overflow-y: auto;
+ animation: fadeIn 150ms ease;
+}
+
+@keyframes fadeIn {
+ from { opacity: 0; transform: translateY(4px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+.sc-frameFooter {
+ padding: 8px 12px;
+ border-top: 1px solid var(--sc-border);
+ background: rgba(18, 20, 23, 0.55);
+ font-size: 12px;
+}
+
+.sc-sidebar {
+ border-right: 1px solid var(--sc-border);
+ background: rgba(18, 20, 23, 0.30);
+ padding: 12px;
+}
+
+.sc-pane {
+ padding: 12px;
+}
+
+.sc-sectionTitle {
+ font-size: 12px;
+ color: rgba(232,232,227,0.68);
+ margin: 0 0 8px;
+ letter-spacing: 0.35px;
+}
+
+.sc-list {
+ display: grid;
+ gap: 6px;
+}
+
+.sc-item {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ border: 1px solid transparent;
+ border-radius: 10px;
+ padding: 8px 8px;
+ color: rgba(232,232,227,0.78);
+ background: rgba(0,0,0,0.12);
+ font-size: 13px;
+}
+
+.sc-itemActive {
+ border-color: rgba(174, 129, 255, 0.35);
+ background: rgba(174, 129, 255, 0.08);
+}
+
+.sc-bullet {
+ width: 8px;
+ height: 8px;
+ border-radius: 999px;
+ background: rgba(255,255,255,0.18);
+ border: 1px solid var(--sc-border);
+ flex-shrink: 0;
+}
+
+.sc-bulletGreen { background: rgba(166, 226, 46, 0.45); }
+.sc-bulletPink { background: rgba(249, 38, 114, 0.45); }
+.sc-bulletBlue { background: rgba(102, 217, 239, 0.45); }
+
+.sc-codeBlock {
+ border-radius: 12px;
+ border: 1px solid var(--sc-border);
+ background: rgba(0,0,0,0.28);
+ padding: 10px 12px;
+ overflow: auto;
+ font-size: 12px;
+ line-height: 1.45;
+ color: rgba(232,232,227,0.88);
+ box-shadow: 0 0 0 1px rgba(255,255,255,0.02) inset;
+}
+
+.sc-lineDim { color: rgba(232,232,227,0.55); }
+.sc-lineGreen { color: var(--ifm-color-primary); }
+.sc-linePink { color: var(--sc-pink); }
+.sc-lineBlue { color: var(--sc-blue); }
+.sc-lineYellow { color: var(--sc-yellow); }
+
+/* ---------- Feature grid ---------- */
+
+.sc-grid {
+ padding: 26px 0 46px;
+}
+
+.sc-gridInner {
+ display: grid;
+ grid-template-columns: repeat(12, 1fr);
+ gap: 16px;
+}
+
+.sc-card {
+ grid-column: span 6;
+ border-radius: var(--sc-radius);
+ border: 1px solid var(--sc-border);
+ background: linear-gradient(180deg, rgba(23, 26, 31, 0.88), rgba(15, 17, 20, 0.88));
+ box-shadow: 0 12px 40px rgba(0,0,0,0.35);
+ padding: 18px 18px 16px;
+}
+
+@media (max-width: 996px) {
+ .sc-card { grid-column: span 12; }
+}
+
+.sc-cardHeader {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 10px;
+ margin-bottom: 8px;
+}
+
+.sc-cardTitle {
+ font-size: 13px;
+ margin: 0;
+ letter-spacing: 0.2px;
+}
+
+.sc-chip {
+ font-size: 11px;
+ border-radius: 999px;
+ padding: 3px 8px;
+ border: 1px solid var(--sc-border);
+ color: rgba(232,232,227,0.7);
+ background: rgba(0,0,0,0.22);
+}
+
+.sc-cardBody {
+ margin: 0;
+ color: rgba(232,232,227,0.72);
+ font-size: 13px;
+ line-height: 1.65;
+}
+
+/* ---------- Copy button ---------- */
+
+.sc-copyBtn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 28px;
+ height: 28px;
+ border-radius: 6px;
+ border: 1px solid var(--sc-border);
+ background: rgba(0,0,0,0.25);
+ color: rgba(232,232,227,0.6);
+ cursor: pointer;
+ font-family: 'lucide';
+ font-size: 14px;
+ transition: all 120ms ease;
+}
+
+.sc-copyBtn:hover {
+ border-color: rgba(255,255,255,0.2);
+ color: rgba(232,232,227,0.9);
+ background: rgba(255,255,255,0.05);
+}
+
+.sc-copyBtn .icon-check {
+ color: var(--ifm-color-primary);
+}
+
+/* ---------- Install block ---------- */
+
+.sc-installBlock {
+ position: relative;
+}
+
+.sc-installHeader {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 6px;
+}
+
+.sc-installCommand {
+ word-break: break-all;
+}
+
+/* ---------- Feature cards enhanced ---------- */
+
+.sc-gridFeatures {
+ grid-template-columns: repeat(12, 1fr);
+}
+
+.sc-card {
+ grid-column: span 6;
+ cursor: pointer;
+ transition: all 200ms ease;
+}
+
+.sc-card:hover {
+ border-color: rgba(255,255,255,0.15);
+ transform: translateY(-2px);
+}
+
+/* Hero card - double wide, accent colors */
+.sc-cardHero {
+ grid-column: span 12;
+ background: linear-gradient(135deg,
+ rgba(166, 226, 46, 0.08) 0%,
+ rgba(23, 26, 31, 0.92) 50%,
+ rgba(102, 217, 239, 0.06) 100%);
+ border-color: rgba(166, 226, 46, 0.2);
+ padding: 22px 22px 20px;
+}
+
+.sc-cardHero .sc-cardTitle {
+ font-size: 15px;
+}
+
+.sc-cardHero .sc-cardBody {
+ font-size: 14px;
+ max-width: 80ch;
+}
+
+.sc-cardHero .sc-chip {
+ background: rgba(166, 226, 46, 0.12);
+ border-color: rgba(166, 226, 46, 0.25);
+ color: var(--ifm-color-primary);
+}
+
+/* Highlighted card animation */
+.sc-cardHighlighted {
+ border-color: rgba(102, 217, 239, 0.4);
+ box-shadow:
+ 0 12px 40px rgba(0,0,0,0.35),
+ 0 0 0 1px rgba(102, 217, 239, 0.15),
+ 0 0 20px rgba(102, 217, 239, 0.1);
+ animation: cardPulse 600ms ease;
+}
+
+.sc-cardHero.sc-cardHighlighted {
+ border-color: rgba(166, 226, 46, 0.5);
+ box-shadow:
+ 0 12px 40px rgba(0,0,0,0.35),
+ 0 0 0 1px rgba(166, 226, 46, 0.2),
+ 0 0 20px rgba(166, 226, 46, 0.12);
+}
+
+@keyframes cardPulse {
+ 0% { transform: scale(1); }
+ 50% { transform: scale(1.01); }
+ 100% { transform: scale(1); }
+}
+
+@media (max-width: 996px) {
+ .sc-card,
+ .sc-cardHero {
+ grid-column: span 12;
+ }
+}
+
+/* ---------- Main sections ---------- */
+
+.sc-main {
+ position: relative;
+}
+
+/* ---------- Component showcase section ---------- */
+
+.sc-showcase {
+ padding: 48px 0;
+ background: linear-gradient(180deg, rgba(15, 17, 20, 0.5), transparent);
+}
+
+.sc-showcaseTitle {
+ font-size: 24px;
+ margin: 0 0 8px;
+ letter-spacing: -0.3px;
+}
+
+.sc-showcaseSubtitle {
+ font-size: 14px;
+ color: var(--ifm-font-color-secondary);
+ margin: 0 0 32px;
+}
+
+.sc-showcaseFullWidth {
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+}
+
+.sc-componentSection {
+ border-radius: var(--sc-radius);
+ border: 1px solid var(--sc-border);
+ padding: 24px;
+ margin: 0 auto;
+ width: 100%;
+ max-width: 1400px;
+}
+
+/* Gradient variants */
+.sc-gradientGreen {
+ background: linear-gradient(30deg,
+ rgba(166, 226, 46, 0.12) 0%,
+ rgba(23, 26, 31, 0.95) 40%,
+ rgba(23, 26, 31, 0.95) 100%);
+ border-color: rgba(166, 226, 46, 0.2);
+}
+
+.sc-gradientBlue {
+ background: linear-gradient(30deg,
+ rgba(102, 217, 239, 0.12) 0%,
+ rgba(23, 26, 31, 0.95) 40%,
+ rgba(23, 26, 31, 0.95) 100%);
+ border-color: rgba(102, 217, 239, 0.2);
+}
+
+.sc-gradientPurple {
+ background: linear-gradient(30deg,
+ rgba(174, 129, 255, 0.12) 0%,
+ rgba(23, 26, 31, 0.95) 40%,
+ rgba(23, 26, 31, 0.95) 100%);
+ border-color: rgba(174, 129, 255, 0.2);
+}
+
+.sc-gradientPink {
+ background: linear-gradient(30deg,
+ rgba(249, 38, 114, 0.12) 0%,
+ rgba(23, 26, 31, 0.95) 40%,
+ rgba(23, 26, 31, 0.95) 100%);
+ border-color: rgba(249, 38, 114, 0.2);
+}
+
+.sc-gradientYellow {
+ background: linear-gradient(30deg,
+ rgba(230, 219, 116, 0.12) 0%,
+ rgba(23, 26, 31, 0.95) 40%,
+ rgba(23, 26, 31, 0.95) 100%);
+ border-color: rgba(230, 219, 116, 0.2);
+}
+
+.sc-componentContent {
+ display: grid;
+ grid-template-columns: 1fr 1.5fr;
+ gap: 32px;
+ align-items: start;
+}
+
+@media (max-width: 996px) {
+ .sc-componentContent {
+ grid-template-columns: 1fr;
+ }
+}
+
+.sc-componentInfo {
+ position: sticky;
+ top: 80px;
+}
+
+.sc-componentTitle {
+ font-size: 18px;
+ margin: 0 0 16px;
+ color: var(--ifm-font-color-base);
+ letter-spacing: 0.2px;
+}
+
+.sc-componentFeatures {
+ display: grid;
+ gap: 10px;
+}
+
+.sc-componentFeature {
+ display: flex;
+ align-items: flex-start;
+ gap: 10px;
+ font-size: 13px;
+ color: rgba(232,232,227,0.72);
+ line-height: 1.5;
+}
+
+.sc-featureIcon {
+ color: var(--ifm-color-primary);
+ font-size: 12px;
+ margin-top: 3px;
+ flex-shrink: 0;
+}
+
+.sc-componentMockup {
+ flex: 1;
+}
+
+/* ---------- Mockup styles ---------- */
+
+.sc-mockup {
+ border-radius: 12px;
+ border: 1px solid var(--sc-border);
+ background: rgba(0, 0, 0, 0.4);
+ overflow: hidden;
+ font-size: 13px;
+}
+
+.sc-mockupHeader {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 10px 14px;
+ border-bottom: 1px solid var(--sc-border);
+ background: rgba(18, 20, 23, 0.6);
+}
+
+.sc-mockupTitle {
+ font-weight: 600;
+ color: rgba(232,232,227,0.9);
+}
+
+.sc-mockupBody {
+ display: grid;
+ grid-template-columns: 180px 1fr;
+ min-height: 280px;
+}
+
+@media (max-width: 768px) {
+ .sc-mockupBody {
+ grid-template-columns: 1fr;
+ }
+}
+
+.sc-mockupSidebar {
+ padding: 12px;
+ border-right: 1px solid var(--sc-border);
+ background: rgba(18, 20, 23, 0.3);
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+}
+
+.sc-mockupMain {
+ padding: 12px;
+}
+
+.sc-mockupItem {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 8px 10px;
+ border-radius: 8px;
+ background: rgba(0,0,0,0.2);
+ border: 1px solid transparent;
+ font-size: 12px;
+ color: rgba(232,232,227,0.78);
+}
+
+.sc-mockupItemActive {
+ border-color: rgba(174, 129, 255, 0.35);
+ background: rgba(174, 129, 255, 0.1);
+}
+
+.sc-mockupFooter {
+ padding: 8px 14px;
+ border-top: 1px solid var(--sc-border);
+ background: rgba(18, 20, 23, 0.6);
+ font-size: 11px;
+}
+
+.sc-mockupDetail,
+.sc-mockupDiff,
+.sc-mockupPreview,
+.sc-mockupConvo,
+.sc-mockupWorktree {
+ background: rgba(0, 0, 0, 0.25);
+ border-radius: 8px;
+ border: 1px solid var(--sc-border);
+ padding: 12px;
+}
+
+.sc-bulletYellow { background: rgba(230, 219, 116, 0.45); }
+
+/* ---------- Features section ---------- */
+
+.sc-features {
+ padding: 48px 0;
+}
+
+.sc-featuresTitle {
+ font-size: 24px;
+ margin: 0 0 24px;
+ letter-spacing: -0.3px;
+}
+
+.sc-featuresGrid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ gap: 16px;
+}
+
+.sc-featureListItem {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ padding: 16px;
+ border-radius: 12px;
+ border: 1px solid var(--sc-border);
+ background: rgba(23, 26, 31, 0.4);
+ transition: border-color 150ms ease;
+}
+
+.sc-featureListItem:hover {
+ border-color: rgba(255,255,255,0.12);
+}
+
+.sc-featureListHeader {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 12px;
+}
+
+.sc-featureListIcon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 32px;
+ height: 32px;
+ border-radius: 8px;
+ background: rgba(255, 255, 255, 0.04);
+ border: 1px solid var(--sc-border);
+ color: rgba(232, 232, 227, 0.5);
+ font-size: 14px;
+ flex-shrink: 0;
+}
+
+.sc-featureListTitle {
+ font-size: 15px;
+ font-weight: 600;
+ margin: 0;
+ line-height: 1.3;
+}
+
+/* Feature title color variants */
+.sc-featureColor-green { color: var(--ifm-color-primary); }
+.sc-featureColor-blue { color: var(--sc-blue); }
+.sc-featureColor-pink { color: var(--sc-pink); }
+.sc-featureColor-purple { color: var(--sc-purple); }
+.sc-featureColor-yellow { color: var(--sc-yellow); }
+.sc-featureColor-orange { color: var(--sc-orange); }
+
+.sc-featureListDesc {
+ font-size: 13px;
+ margin: 0;
+ color: rgba(232,232,227,0.65);
+ line-height: 1.5;
+}
+
+/* ---------- Agents section ---------- */
+
+.sc-agents {
+ padding: 48px 0 64px;
+ background: linear-gradient(180deg, transparent, rgba(15, 17, 20, 0.4));
+}
+
+.sc-agentsTitle {
+ font-size: 24px;
+ margin: 0 0 8px;
+ letter-spacing: -0.3px;
+ text-align: center;
+}
+
+.sc-agentsSubtitle {
+ font-size: 14px;
+ color: var(--ifm-font-color-secondary);
+ margin: 0 0 32px;
+ text-align: center;
+ max-width: 60ch;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.sc-agentsGrid {
+ display: grid;
+ grid-template-columns: repeat(5, 1fr);
+ gap: 16px;
+ max-width: 1200px;
+ margin: 0 auto;
+}
+
+@media (max-width: 1100px) {
+ .sc-agentsGrid {
+ grid-template-columns: repeat(3, 1fr);
+ }
+}
+
+@media (max-width: 768px) {
+ .sc-agentsGrid {
+ grid-template-columns: repeat(2, 1fr);
+ }
+}
+
+@media (max-width: 500px) {
+ .sc-agentsGrid {
+ grid-template-columns: 1fr;
+ }
+}
+
+.sc-agentCard {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 12px;
+ padding: 20px 16px;
+ border-radius: var(--sc-radius);
+ border: 1px solid var(--sc-border);
+ background: rgba(23, 26, 31, 0.5);
+ transition: all 200ms ease;
+ text-align: center;
+}
+
+.sc-agentCard:hover {
+ border-color: rgba(255,255,255,0.15);
+ transform: translateY(-2px);
+ box-shadow: 0 8px 24px rgba(0,0,0,0.25);
+}
+
+.sc-agentLogo {
+ width: 48px;
+ height: 48px;
+ flex-shrink: 0;
+}
+
+.sc-agentLogo svg {
+ width: 100%;
+ height: 100%;
+}
+
+.sc-agentInfo {
+ flex: 1;
+ min-width: 0;
+}
+
+.sc-agentName {
+ font-size: 14px;
+ margin: 0 0 4px;
+ color: var(--ifm-font-color-base);
+ font-weight: 600;
+}
+
+.sc-agentDesc {
+ font-size: 12px;
+ margin: 0;
+ color: rgba(232,232,227,0.6);
+ line-height: 1.4;
+}
+
+.sc-agentsNote {
+ font-size: 13px;
+ color: var(--ifm-font-color-secondary);
+ text-align: center;
+ margin: 24px auto 0;
+ max-width: 60ch;
}
diff --git a/website/src/pages/index.js b/website/src/pages/index.js
index 971662be..b21b2fc1 100644
--- a/website/src/pages/index.js
+++ b/website/src/pages/index.js
@@ -1,48 +1,1041 @@
+import { useState, useCallback, useEffect } from 'react';
import Link from '@docusaurus/Link';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import Layout from '@theme/Layout';
-import Heading from '@theme/Heading';
-import styles from './index.module.css';
+const TABS = ['td', 'git', 'files', 'conversations', 'worktrees'];
-function HomepageHeader() {
- const {siteConfig} = useDocusaurusContext();
+const INSTALL_COMMAND = 'curl -fsSL https://raw.githubusercontent.com/marcus/sidecar/main/scripts/setup.sh | bash';
+
+function CopyButton({ text }) {
+ const [copied, setCopied] = useState(false);
+
+ const handleCopy = useCallback(async () => {
+ try {
+ await navigator.clipboard.writeText(text);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ } catch (err) {
+ console.error('Failed to copy:', err);
+ }
+ }, [text]);
+
+ return (
+
+
+
+ );
+}
+
+function TdPane() {
+ return (
+ <>
+
Tasks
+
+
+
+ td-a1b2c3 Implement auth flow
+
+
+
+ td-d4e5f6 Add rate limiting
+
+
+
+ td-g7h8i9 Fix memory leak
+
+
+
+
+
td-a1b2c3 | in_progress
+
+
Title: Implement auth flow
+
Status: in_progress
+
Created: 2h ago
+
+
Subtasks:
+
[x] Create auth middleware
+
[x] Add JWT validation
+
[ ] Write integration tests
+
+ >
+ );
+}
+
+function GitPane() {
+ return (
+ <>
+ Changes
+
+
+
+ M internal/auth/middleware.go
+
+
+
+ A internal/auth/jwt.go
+
+
+
+ D internal/auth/old_auth.go
+
+
+
+
+
internal/auth/middleware.go
+
+
@@ -42,6 +42,18 @@
+
+ func AuthMiddleware(next http.Handler) http.Handler {'{'}
+
+ return http.HandlerFunc(func(w, r) {'{'}
+
+ token := r.Header.Get("Authorization")
+
+ if !ValidateJWT(token) {'{'}
+
+ http.Error(w, "Unauthorized", 401)
+
+ return
+
+ {'}'}
+
+ next.ServeHTTP(w, r)
+
+ {'}'})
+
+ {'}'}
+
+ >
+ );
+}
+
+function FilesPane() {
+ return (
+ <>
+ Project Files
+
+
+ [v]
+ internal/
+
+
+ [v]
+ auth/
+
+
+
+ middleware.go
+
+
+
+ jwt.go
+
+
+ [>]
+ plugins/
+
+
+
+
+
middleware.go | 156 lines
+
+
package auth
+
+
import (
+
"net/http"
+
"strings"
+
)
+
+
// AuthMiddleware validates JWT tokens
+
func AuthMiddleware (next http.Handler)...
+
+ >
+ );
+}
+
+function ConversationsPane() {
return (
-
-
-
- Welcome to {siteConfig.title}
-
-
{siteConfig.tagline}
-
-
- Get Started
-
+ <>
+
All Sessions chronological
+
+
+
+ auth-flow | Claude | 24m
+
+
+ rate-limit | Cursor | 2h
+
+
+
+ refactor | Gemini | 1d
+
+
+
+
+
auth-flow | Claude Code
+
+
User: Add JWT auth to the API
+
+
Claude: I'll implement JWT authentication.
+
First, let me check the existing auth...
+
+
-> Read internal/auth/middleware.go
+
-> Edit internal/auth/jwt.go
+
+
12.4k tokens | 24 minutes
+
+ >
+ );
+}
+
+function WorktreesPane() {
+ return (
+ <>
+
Worktrees zero commands
+
+
+
+ main
+
+
+
+ feature/auth PR #47
+
+
+
+ fix/memory PR #52
+
+
+
+
+
feature/auth | Ready to merge
+
+
Task: td-a1b2c3 from td
+
Prompts: 3 configured
+
+
Actions:
+
[n] New worktree + agent
+
[s] Send task from td
+
[p] Run prompt sequence
+
+
* 3 commits ahead | checks passing
-
+ >
+ );
+}
+
+function Frame({ activeTab, onTabChange }) {
+ const [time, setTime] = useState(() => {
+ const now = new Date();
+ return now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false });
+ });
+
+ useEffect(() => {
+ const updateTime = () => {
+ const now = new Date();
+ setTime(now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false }));
+ };
+ const interval = setInterval(updateTime, 1000);
+ return () => clearInterval(interval);
+ }, []);
+
+ const renderPane = () => {
+ switch (activeTab) {
+ case 'td': return
;
+ case 'git': return
;
+ case 'files': return
;
+ case 'conversations': return
;
+ case 'worktrees': return
;
+ default: return
;
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ sidecar
+ {time}
+
+
+
+
+ {TABS.map((tab) => (
+ onTabChange(tab)}
+ type="button"
+ >
+ {tab}
+
+ ))}
+
+
+
+
+
+ tab
+ switch |
+ enter
+ select |
+ ?
+ help |
+ q
+ quit
+
+
+ );
+}
+
+function FeatureCard({ id, title, chip, children, isHighlighted, isHero, onClick }) {
+ return (
+
e.key === 'Enter' && onClick?.()}
+ >
+
+
{title}
+ {chip}
+
+
{children}
+
+ );
+}
+
+// Mockup screens for component deep dive
+function TdMockup() {
+ return (
+
+
+ Task Management
+ 3 tasks | 1 in progress
+
+
+
+
+
+
+
td-a1b2c3
+
Implement auth
+
+
+
+
+
+
td-d4e5f6
+
Rate limiting
+
+
+
+
+
+
td-g7h8i9
+
Memory leak
+
+
+
+
+
+
Implement auth flow
+
+
Status: in_progress
+
Created: 2h ago
+
Epic: td-epic-auth
+
+
+
Subtasks
+
+
[x] Create auth middleware
+
[x] Add JWT validation
+
[ ] Write integration tests
+
[ ] Update API docs
+
+
+
+
+
+
+ n new |
+ e edit |
+ s status |
+ / search
+
+
+ );
+}
+
+function GitMockup() {
+ return (
+
+
+ Git Status
+ feature/auth-flow | 3 changed
+
+
+
+
Staged
+
+ M
+ middleware.go
+
+
+ A
+ jwt.go
+
+
Unstaged
+
+ D
+ old_auth.go
+
+
+
+
+
internal/auth/middleware.go
+
+
@@ -42,6 +42,14 @@
+
+func AuthMiddleware(next http.Handler) {'{'}
+
+ return http.HandlerFunc(func(w, r) {'{'}
+
+ token := r.Header.Get("Auth")
+
+ if !ValidateJWT(token) {'{'}
+
+ http.Error(w, "Unauth", 401)
+
+ {'}'}
+
+ {'}'})
+
+{'}'}
+
+
+
+
+
+ a stage |
+ u unstage |
+ c commit |
+ d diff
+
+
+ );
+}
+
+function FilesMockup() {
+ return (
+
+
+ File Browser
+ sidecar/internal
+
+
+
+
+ [v]
+ internal/
+
+
+ [v]
+ auth/
+
+
+
+ middleware.go
+
+
+
+ jwt.go
+
+
+ [>]
+ plugins/
+
+
+ [>]
+ app/
+
+
+
+
+
middleware.go | 156 lines | Go
+
+
package auth
+
+
import (
+
"net/http"
+
"strings"
+
)
+
+
// AuthMiddleware validates requests
+
func AuthMiddleware(next)...
+
+
+
+
+
+ enter open |
+ / search |
+ e editor |
+ g goto
+
+
+ );
+}
+
+function ConversationsMockup() {
+ return (
+
+
+ Conversations
+ 18 sessions | all agents
+
+
+
+
+
+
+
auth-flow Claude
+
24m ago | 12.4k
+
+
+
+
+
+
rate-limit Cursor
+
2h ago | 8.2k
+
+
+
+
+
+
refactor Gemini
+
1d ago | 24.1k
+
+
+
+
+
+
+
User
+
Add JWT authentication to the API endpoints
+
+
+
Claude
+
I'll implement JWT authentication for your API.
+
Let me check the existing auth setup...
+
+
+
-> Read middleware.go
+
-> Edit jwt.go
+
-> Write tests/auth_test.go
+
+
+
+
+
+ enter expand |
+ / search |
+ y copy |
+ j/k nav
+
+
+ );
+}
+
+function WorktreesMockup() {
+ return (
+
+
+ Worktrees
+ zero commands | auto everything
+
+
+
+
+
+
+
+
feature/auth
+
PR #47 | ready
+
+
+
+
+
+
fix/memory
+
td-g7h8i9
+
+
+
+
+
+
feature/auth
+
+
PR: #47 Add JWT auth
+
Task: td-a1b2c3 from td
+
Status: Ready to merge
+
+
+
Quick actions
+
[n] New worktree + start agent
+
[s] Send task from td
+
[p] Run prompt sequence
+
[m] Merge & cleanup
+
+
+
+
+
+ n new |
+ s send task |
+ p prompts |
+ m merge
+
+
+ );
+}
+
+function ComponentSection({ id, title, features, gradient, MockupComponent }) {
+ return (
+
+
+
+
{title}
+
+ {features.map((feature, idx) => (
+
+
+ {feature}
+
+ ))}
+
+
+
+
+
+
+
+ );
+}
+
+function FeatureListItem({ icon, title, description, color }) {
+ return (
+
);
}
export default function Home() {
- const {siteConfig} = useDocusaurusContext();
+ const { siteConfig } = useDocusaurusContext();
+ const [activeTab, setActiveTab] = useState('td');
+
+ const handleTabChange = (tab) => {
+ setActiveTab(tab);
+ };
+
+ const handleCardClick = (tab) => {
+ setActiveTab(tab);
+ };
+
return (
-
-
-
-
-
- Sidecar provides a unified terminal interface for viewing Claude Code
- conversations, git status, and task progress. Built for developers who
- want visibility into their AI coding sessions without leaving the terminal.
-
+ title="Terminal UI for AI coding sessions"
+ description="Sidecar is a unified terminal interface for monitoring AI coding agent sessions: tasks, git status, files, conversations."
+ >
+
+
+
+
+
+ Sidecar
+ |
+ a terminal cockpit for AI coding
+
+
+
+ Monitor agent sessions without leaving the terminal: task flow, git diffs, file browsing,
+ and conversation history--designed for the "split the terminal and ship" workflow.
+
+
+
+
+
+
+ Get started
curl | bash
+
+
+ Read docs
?
+
+
+ GitHub
+
+
+
+
+
+
+
+ Quick install
+
+
+
+ $
+ {INSTALL_COMMAND}
+
+
+ $
+ sidecar
+
+
+
+
+
+
+
+
+
+
+ {/* Feature Cards */}
+
+
+
+ {/* TD Hero Card - double wide */}
+ handleCardClick('td')}
+ >
+ Give agents structured work so they can operate autonomously for longer. Tasks persist across
+ context windows, keeping agents on track with clear objectives. Built-in review workflow
+ lets you verify work before moving to the next task.
+
+
+ {/* Regular feature cards */}
+ handleCardClick('git')}
+ >
+ Split-pane diffs, commit context, and a fast loop for staging/review--without bouncing to an IDE.
+
+
+ handleCardClick('files')}
+ >
+ Navigate your codebase with a tree view, preview file contents, and jump to any file instantly.
+
+
+ handleCardClick('conversations')}
+ >
+ Chronological view across Claude, Cursor, Gemini, and all adapters. See every session in one place,
+ search across agents, and pick up exactly where any agent left off.
+
+
+ handleCardClick('worktrees')}
+ >
+ Create worktrees, pass tasks from td, or kick off with configured prompts--all without typing git commands.
+ Everything is automatic: create, switch, merge, delete.
+
+
+
+
+
+ {/* Component Showcase Sections */}
+
+
+
Component Deep Dive
+
Each plugin is designed for the AI-assisted development workflow
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Comprehensive Features Section */}
+
+
+
Features
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Supported Agents */}
+
+
+
Works with your favorite coding agents
+
+ Sidecar reads session data from multiple AI coding tools, giving you a unified view of agent activity
+
+
+
+
+
+
+
Claude Code
+
Anthropic's official CLI for Claude
+
+
+
+
+
+
+
+
+
+
+
+
+
Codex
+
OpenAI's code generation model
+
+
+
+
+
+
+
Gemini CLI
+
Google's multimodal AI assistant
+
+
+
+
+
+
+
Opencode
+
Terminal-first AI coding assistant
+
+
+
+
+
+
+
Cursor
+
AI-first code editor (cursor-agent)
+
+
+
+
+
+ Each agent stores session data in its own format. Sidecar normalizes and displays them in a unified interface.
+
+
+
);