Rebuild website from scratch on Tailwind v4 + shadcn/ui

- Fresh Astro 5 project with Tailwind v4 and shadcn/ui olive preset
- All shadcn components installed (Card, Button, Badge, Separator, etc.)
- Homepage with hero, terminal demo, workflows, agents, sources, compute
- Full docs system with 24 markdown pages across 5 sections
- Sidebar navigation with active state highlighting
- Prose styles for markdown content using shadcn color tokens
- Dark/light theme toggle with localStorage persistence
- Shiki everforest syntax themes for code blocks
- 404 page with VT323 font
- /docs redirect to installation page
- GitHub star count fetch
- Earthy green/cream oklch color palette matching TUI theme

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Advait Paliwal
2026-03-24 15:57:03 -07:00
parent 7d3fbc3f6b
commit 8f8cf2a4a9
61 changed files with 9369 additions and 2633 deletions

View File

@@ -1,61 +0,0 @@
---
import { ViewTransitions } from 'astro:transitions';
import Nav from '../components/Nav.astro';
import Footer from '../components/Footer.astro';
import '../styles/global.css';
interface Props {
title: string;
description?: string;
active?: 'home' | 'docs';
}
const { title, description = 'Research-first AI agent', active = 'home' } = Astro.props;
---
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content={description} />
<title>{title}</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=VT323&display=swap" rel="stylesheet" />
<ViewTransitions fallback="none" />
<script is:inline>
(function() {
var stored = localStorage.getItem('theme');
var prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (stored === 'dark' || (!stored && prefersDark)) {
document.documentElement.classList.add('dark');
}
})();
</script>
<script is:inline>
document.addEventListener('astro:after-swap', function() {
var stored = localStorage.getItem('theme');
var prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
var shouldBeDark = stored === 'dark' || (!stored && prefersDark);
if (shouldBeDark) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
var isDark = document.documentElement.classList.contains('dark');
var sun = document.getElementById('sun-icon');
var moon = document.getElementById('moon-icon');
if (sun) sun.style.display = isDark ? 'block' : 'none';
if (moon) moon.style.display = isDark ? 'none' : 'block';
});
</script>
</head>
<body class="min-h-screen flex flex-col antialiased">
<Nav active={active} />
<main class="flex-1">
<slot />
</main>
<Footer />
</body>
</html>

View File

@@ -1,79 +0,0 @@
---
import Base from './Base.astro';
import Sidebar from '../components/Sidebar.astro';
interface Props {
title: string;
description?: string;
currentSlug: string;
}
const { title, description, currentSlug } = Astro.props;
---
<Base title={`${title} — Feynman Docs`} description={description} active="docs">
<div class="max-w-6xl mx-auto px-6">
<div class="flex gap-8">
<Sidebar currentSlug={currentSlug} />
<button id="mobile-menu-btn" class="lg:hidden fixed bottom-6 right-6 z-40 p-3 rounded-full bg-accent text-bg shadow-lg" aria-label="Toggle sidebar">
<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
<div id="mobile-overlay" class="hidden fixed inset-0 bg-black/50 z-30 lg:hidden"></div>
<article class="flex-1 min-w-0 py-8 max-w-3xl">
<h1 class="text-3xl font-bold mb-8 tracking-tight">{title}</h1>
<div class="prose">
<slot />
</div>
</article>
</div>
</div>
<script is:inline>
(function() {
function init() {
var btn = document.getElementById('mobile-menu-btn');
var sidebar = document.getElementById('sidebar');
var overlay = document.getElementById('mobile-overlay');
if (btn && sidebar && overlay) {
function toggle() {
sidebar.classList.toggle('hidden');
sidebar.classList.toggle('fixed');
sidebar.classList.toggle('inset-0');
sidebar.classList.toggle('z-40');
sidebar.classList.toggle('bg-bg');
sidebar.classList.toggle('w-full');
sidebar.classList.toggle('p-6');
overlay.classList.toggle('hidden');
}
btn.addEventListener('click', toggle);
overlay.addEventListener('click', toggle);
}
document.querySelectorAll('.prose pre').forEach(function(pre) {
if (pre.querySelector('.copy-code')) return;
var copyBtn = document.createElement('button');
copyBtn.className = 'copy-code';
copyBtn.setAttribute('aria-label', 'Copy code');
copyBtn.innerHTML = '<svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>';
pre.appendChild(copyBtn);
copyBtn.addEventListener('click', function() {
var code = pre.querySelector('code');
var text = code ? code.textContent : pre.textContent;
navigator.clipboard.writeText(text);
copyBtn.innerHTML = '<svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path d="M20 6L9 17l-5-5"/></svg>';
setTimeout(function() {
copyBtn.innerHTML = '<svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>';
}, 2000);
});
});
}
document.addEventListener('DOMContentLoaded', init);
document.addEventListener('astro:after-swap', init);
})();
</script>
</Base>

View File

@@ -0,0 +1,149 @@
---
import "@/styles/global.css"
import { ViewTransitions } from "astro:transitions"
interface Props {
title?: string
description?: string
active?: "home" | "docs"
}
const {
title = "Feynman - The open source AI research agent",
description = "An AI-powered research agent that helps you discover, analyze, and synthesize scientific literature.",
active = "home",
} = Astro.props
---
<html lang="en" class="dark">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="description" content={description} />
<title>{title}</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=VT323&display=swap" rel="stylesheet" />
<ViewTransitions />
<script is:inline>
;(function () {
const theme = localStorage.getItem("theme")
if (theme === "dark" || (!theme && window.matchMedia("(prefers-color-scheme: dark)").matches)) {
document.documentElement.classList.add("dark")
} else {
document.documentElement.classList.remove("dark")
}
})()
</script>
</head>
<body class="flex min-h-screen flex-col bg-background text-foreground antialiased">
<nav class="sticky top-0 z-50 bg-background">
<div class="mx-auto flex h-14 max-w-6xl items-center justify-between px-6">
<a href="/" class="flex items-center gap-2">
<span class="font-['VT323'] text-2xl text-primary">feynman</span>
</a>
<div class="flex items-center gap-6">
<a
href="/docs/getting-started/installation"
class:list={[
"text-sm transition-colors hover:text-foreground",
active === "docs" ? "text-foreground" : "text-muted-foreground",
]}
>
Docs
</a>
<a
href="https://github.com/getcompanion-ai/feynman"
target="_blank"
rel="noopener noreferrer"
class="text-sm text-muted-foreground transition-colors hover:text-foreground"
>
GitHub
</a>
<button
id="theme-toggle"
type="button"
class="inline-flex size-9 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
aria-label="Toggle theme"
>
<svg
id="sun-icon"
class="hidden size-4"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" />
</svg>
<svg
id="moon-icon"
class="hidden size-4"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M21.752 15.002A9.718 9.718 0 0118 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 003 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 009.002-5.998z" />
</svg>
</button>
</div>
</nav>
<main class="flex-1">
<slot />
</main>
<footer>
<div class="mx-auto flex max-w-6xl flex-col items-center justify-between gap-4 px-6 py-8 sm:flex-row">
<p class="text-sm text-muted-foreground">
&copy; {new Date().getFullYear()} Feynman. Open source under MIT.
</p>
<div class="flex items-center gap-4 text-sm">
<a href="/docs/getting-started/installation" class="text-muted-foreground transition-colors hover:text-foreground">Docs</a>
<a
href="https://github.com/getcompanion-ai/feynman"
target="_blank"
rel="noopener noreferrer"
class="text-muted-foreground transition-colors hover:text-foreground"
>
GitHub
</a>
</div>
</div>
</footer>
<script is:inline>
function updateThemeIcons() {
const isDark = document.documentElement.classList.contains("dark")
document.getElementById("sun-icon").classList.toggle("hidden", !isDark)
document.getElementById("moon-icon").classList.toggle("hidden", isDark)
}
function setupThemeToggle() {
updateThemeIcons()
document.getElementById("theme-toggle").addEventListener("click", function () {
document.documentElement.classList.toggle("dark")
const isDark = document.documentElement.classList.contains("dark")
localStorage.setItem("theme", isDark ? "dark" : "light")
updateThemeIcons()
})
}
setupThemeToggle()
document.addEventListener("astro:after-swap", function () {
const theme = localStorage.getItem("theme")
if (theme === "dark" || (!theme && window.matchMedia("(prefers-color-scheme: dark)").matches)) {
document.documentElement.classList.add("dark")
} else {
document.documentElement.classList.remove("dark")
}
setupThemeToggle()
})
</script>
</body>
</html>