From a8b5b6cf02cd55309eaa07a5538f44b8832fd1f0 Mon Sep 17 00:00:00 2001 From: codebanditssss Date: Wed, 3 Jun 2026 22:12:25 +0530 Subject: [PATCH] feat: add docs routes and layout --- frontend/src/landing/app/docs/404/page.tsx | 15 + .../src/landing/app/docs/[[...slug]]/page.tsx | 85 +++ frontend/src/landing/app/docs/docs.css | 593 ++++++++++++++++++ frontend/src/landing/app/docs/layout.tsx | 129 ++++ frontend/src/landing/app/docs/not-found.tsx | 5 + 5 files changed, 827 insertions(+) create mode 100644 frontend/src/landing/app/docs/404/page.tsx create mode 100644 frontend/src/landing/app/docs/[[...slug]]/page.tsx create mode 100644 frontend/src/landing/app/docs/docs.css create mode 100644 frontend/src/landing/app/docs/layout.tsx create mode 100644 frontend/src/landing/app/docs/not-found.tsx diff --git a/frontend/src/landing/app/docs/404/page.tsx b/frontend/src/landing/app/docs/404/page.tsx new file mode 100644 index 000000000..48c771ae9 --- /dev/null +++ b/frontend/src/landing/app/docs/404/page.tsx @@ -0,0 +1,15 @@ +import type { Metadata } from "next"; +import { DocsMissingPage } from "@/components/docs/DocsMissingPage"; + +export const metadata: Metadata = { + title: "Docs page not found", + description: "This docs page moved or does not exist.", + robots: { + index: false, + follow: true, + }, +}; + +export default function Docs404Page() { + return ; +} diff --git a/frontend/src/landing/app/docs/[[...slug]]/page.tsx b/frontend/src/landing/app/docs/[[...slug]]/page.tsx new file mode 100644 index 000000000..2294e9844 --- /dev/null +++ b/frontend/src/landing/app/docs/[[...slug]]/page.tsx @@ -0,0 +1,85 @@ +import { notFound } from "next/navigation"; +import { + DocsPage, + DocsBody, + DocsTitle, + DocsDescription, +} from "fumadocs-ui/page"; +import type { Metadata } from "next"; +import { source } from "@/lib/source"; +import { getMDXComponents } from "@/components/docs/mdx-components"; + +interface PageProps { + params: Promise<{ slug?: string[] }>; +} + +export default async function DocsSlugPage({ params }: PageProps) { + const { slug } = await params; + const page = source.getPage(slug); + if (!page) notFound(); + + const MDX = page.data.body; + + return ( + + {page.data.title} + {page.data.description} + + + + + ); +} + +export async function generateStaticParams() { + return source.generateParams(); +} + +export async function generateMetadata({ + params, +}: PageProps): Promise { + const { slug } = await params; + const page = source.getPage(slug); + if (!page) { + return { + title: "Docs page not found", + description: "This docs page moved or does not exist.", + robots: { + index: false, + follow: true, + }, + }; + } + + return { + title: page.data.title, + description: page.data.description, + alternates: { + canonical: `https://aoagents.dev${page.url}`, + }, + openGraph: { + title: page.data.title, + description: page.data.description, + }, + }; +} diff --git a/frontend/src/landing/app/docs/docs.css b/frontend/src/landing/app/docs/docs.css new file mode 100644 index 000000000..9fe5f5739 --- /dev/null +++ b/frontend/src/landing/app/docs/docs.css @@ -0,0 +1,593 @@ +/* + * AO Warm Terminal theme for Fumadocs + * ==================================== + * CRITICAL: Fumadocs Tailwind utilities use --color-fd-* variables (NOT --fd-*). + * We must override --color-fd-* to change colors. These are inside layer(fumadocs), + * so our unlayered :root overrides win. + */ +@import "fumadocs-ui/style.css" layer(fumadocs); + +/* ═══════════════════════════════════════════════════════════════════════════ + 1. COLOR OVERRIDES — override --color-fd-* with AO design tokens + ═══════════════════════════════════════════════════════════════════════════ */ + +:root { + /* Docs primary accent = AMBER (matches dashboard Orchestrator button + active items) */ + --docs-accent: var(--color-accent-amber); + --docs-accent-dim: var(--color-accent-amber-dim); + --docs-accent-border: var(--color-accent-amber-border); + + --color-fd-background: var(--color-bg-base); + --color-fd-foreground: var(--color-text-primary); + --color-fd-muted: var(--color-bg-elevated); + --color-fd-muted-foreground: var(--color-text-secondary); + --color-fd-popover: var(--color-bg-elevated); + --color-fd-popover-foreground: var(--color-text-primary); + --color-fd-card: var(--color-bg-surface); + --color-fd-card-foreground: var(--color-text-primary); + --color-fd-border: var(--color-border-default); + --color-fd-primary: var(--docs-accent); + --color-fd-primary-foreground: #ffffff; + --color-fd-secondary: var(--color-bg-elevated); + --color-fd-secondary-foreground: var(--color-text-primary); + --color-fd-accent: var(--docs-accent-dim); + --color-fd-accent-foreground: var(--docs-accent); + --color-fd-ring: var(--docs-accent); +} + +.dark { + --docs-accent: var(--color-accent-amber); + --docs-accent-dim: var(--color-accent-amber-dim); + --docs-accent-border: var(--color-accent-amber-border); + + --color-fd-background: var(--color-bg-base); + --color-fd-foreground: var(--color-text-primary); + --color-fd-muted: var(--color-bg-surface); + --color-fd-muted-foreground: var(--color-text-secondary); + --color-fd-popover: var(--color-bg-elevated); + --color-fd-popover-foreground: var(--color-text-primary); + --color-fd-card: var(--color-bg-surface); + --color-fd-card-foreground: var(--color-text-primary); + --color-fd-border: var(--color-border-default); + --color-fd-primary: var(--docs-accent); + --color-fd-primary-foreground: var(--color-bg-base); + --color-fd-secondary: var(--color-bg-elevated); + --color-fd-secondary-foreground: var(--color-text-primary); + --color-fd-accent: var(--docs-accent-dim); + --color-fd-accent-foreground: var(--docs-accent); + --color-fd-ring: var(--docs-accent); +} + +/* Sidebar-specific dark overrides */ +.dark #nd-sidebar { + --color-fd-muted: var(--color-bg-surface); + --color-fd-secondary: var(--color-bg-elevated); + --color-fd-muted-foreground: var(--color-text-secondary); +} + +/* Sidebar background — match dashboard's very dark sidebar */ +#nd-sidebar { + background-color: var(--color-bg-sidebar) !important; + border-right-color: var(--color-border-subtle) !important; +} + +/* ═══════════════════════════════════════════════════════════════════════════ + 1b. FLAT BACKGROUND — kill the body gradient on docs pages + ═══════════════════════════════════════════════════════════════════════════ */ + +#nd-docs-layout { + background: var(--color-bg-base) !important; +} + +/* ═══════════════════════════════════════════════════════════════════════════ + 1c. SEMANTIC COLORS — override fumadocs info/warning/error colors to amber + ═══════════════════════════════════════════════════════════════════════════ */ + +:root { + --color-fd-info: var(--docs-accent); + --color-fd-warning: var(--color-accent-amber); + --color-fd-success: #16a34a; + --color-fd-error: #dc2626; +} + +.dark { + --color-fd-info: var(--docs-accent); + --color-fd-warning: var(--color-accent-amber); + --color-fd-success: #22c55e; + --color-fd-error: #ef4444; +} + +/* ═══════════════════════════════════════════════════════════════════════════ + 1c. FORCE WARM LINK COLORS — fumadocs uses text-fd-primary (accent blue) + on too many elements. Override sidebar, TOC, and breadcrumb links + to warm gray. Only prose body links stay accent. + ═══════════════════════════════════════════════════════════════════════════ */ + +/* Sidebar links: warm stone gray, not periwinkle (desktop + mobile) */ +#nd-sidebar a:not([data-active="true"]), +#nd-sidebar button, +#nd-sidebar-mobile a:not([data-active="true"]), +#nd-sidebar-mobile button { + color: var(--color-text-secondary) !important; +} + +#nd-sidebar a:not([data-active="true"]):hover, +#nd-sidebar button:hover, +#nd-sidebar-mobile a:not([data-active="true"]):hover, +#nd-sidebar-mobile button:hover { + color: var(--color-text-primary) !important; +} + +/* Sidebar active link: amber accent (desktop + mobile) */ +#nd-sidebar a[data-active="true"], +#nd-sidebar-mobile a[data-active="true"] { + color: var(--docs-accent) !important; +} + +/* TOC links: warm gray, not blue */ +#nd-toc a, +#nd-tocnav a { + color: var(--color-text-secondary) !important; +} + +#nd-toc a:hover, +#nd-tocnav a:hover { + color: var(--color-text-primary) !important; +} + +#nd-toc a[data-active="true"], +#nd-tocnav a[data-active="true"] { + color: var(--docs-accent) !important; +} + +/* Breadcrumb: warm gray */ +#nd-page nav a, +#nd-page [aria-label="breadcrumb"] a { + color: var(--color-text-secondary) !important; + text-decoration: none !important; +} + +#nd-page nav a:hover, +#nd-page [aria-label="breadcrumb"] a:hover { + color: var(--color-text-primary) !important; +} + +/* Page title breadcrumb link (the blue "Installation" at top) */ +#nd-page a[href^="/docs"] { + color: var(--color-text-secondary) !important; +} + +#nd-page a[href^="/docs"]:hover { + color: var(--color-text-primary) !important; +} + +/* Step number circles: amber */ +#nd-docs-layout .fd-step::before { + background: var(--docs-accent) !important; +} + +/* Footer prev/next: warm gray, not blue */ +#nd-page footer a, +#nd-page [class*="footer"] a { + color: var(--color-text-secondary) !important; + border-color: var(--color-border-default) !important; +} + +#nd-page footer a:hover, +#nd-page [class*="footer"] a:hover { + color: var(--color-text-primary) !important; + border-color: var(--docs-accent) !important; +} + +/* ═══════════════════════════════════════════════════════════════════════════ + 2. TYPOGRAPHY — Geist 13px body, JetBrains Mono code (dashboard match) + ═══════════════════════════════════════════════════════════════════════════ */ + +#nd-docs-layout { + font-family: + var(--font-geist-sans), + ui-sans-serif, + system-ui, + -apple-system, + sans-serif; + font-size: 13px; + letter-spacing: -0.011em; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +#nd-docs-layout code, +#nd-docs-layout kbd, +#nd-docs-layout pre { + font-family: + var(--font-jetbrains-mono), + ui-monospace, + "SFMono-Regular", + monospace; +} + +/* Sidebar — smaller text like dashboard */ +#nd-sidebar { + font-size: 12.5px; +} + +/* TOC — compact */ +#nd-toc, +#nd-tocnav { + font-size: 11.5px; +} + +/* ═══════════════════════════════════════════════════════════════════════════ + 3. PROSE — headings, links, tables, inline code + ═══════════════════════════════════════════════════════════════════════════ */ + +#nd-docs-layout .prose h1 { + color: var(--color-text-primary); + font-weight: 700; + font-size: 1.65em; + letter-spacing: -0.02em; +} + +#nd-docs-layout .prose h2 { + color: var(--color-text-primary); + font-weight: 680; + font-size: 1.3em; + letter-spacing: -0.015em; +} + +#nd-docs-layout .prose h3 { + color: var(--color-text-primary); + font-weight: 640; + font-size: 1.1em; +} + +#nd-docs-layout .prose h4 { + color: var(--color-text-primary); + font-weight: 620; +} + +/* Heading anchors — strip card border/bg */ +#nd-docs-layout .prose h1 a[data-card], +#nd-docs-layout .prose h2 a[data-card], +#nd-docs-layout .prose h3 a[data-card], +#nd-docs-layout .prose h4 a[data-card] { + border: none; + background: none; + padding: 0; + color: inherit; +} + +/* Links — amber accent like dashboard */ +#nd-docs-layout .prose a { + color: var(--docs-accent); + text-decoration: none; +} + +#nd-docs-layout .prose a:hover { + text-decoration: underline; + text-underline-offset: 3px; +} + +/* Tables — dashboard style: uppercase headers, dense */ +#nd-docs-layout .prose table { + font-size: 12.5px; +} + +#nd-docs-layout .prose th { + background-color: var(--color-bg-subtle); + color: var(--color-text-tertiary, var(--color-text-secondary)); + font-size: 10px; + font-weight: 600; + letter-spacing: 0.06em; + text-transform: uppercase; + padding: 0.5rem 0.75rem; +} + +#nd-docs-layout .prose td { + padding: 0.5rem 0.75rem; +} + +/* Inline code */ +#nd-docs-layout :not(pre) > code { + font-size: 0.85em; + padding: 0.12em 0.4em; + border-radius: 4px; + background-color: var(--color-bg-subtle); + border: 1px solid var(--color-border-subtle); + color: var(--docs-accent); + font-weight: 450; +} + +/* No underlines in sidebar (desktop + mobile), TOC, or nav */ +#nd-sidebar a, +#nd-sidebar button, +#nd-sidebar-mobile a, +#nd-sidebar-mobile button, +#nd-toc a, +#nd-tocnav a, +#nd-page nav a { + text-decoration: none !important; +} + +/* ═══════════════════════════════════════════════════════════════════════════ + 4. CODE BLOCKS — Shiki, single border, copy button + ═══════════════════════════════════════════════════════════════════════════ */ + +.shiki span { + color: var(--shiki-light) !important; + background-color: transparent !important; +} + +.dark .shiki span { + color: var(--shiki-dark) !important; + background-color: transparent !important; +} + +#nd-docs-layout pre, +pre.shiki, +pre.shiki code { + background-color: var(--color-bg-inset, var(--color-bg-surface)) !important; +} + +#nd-docs-layout pre { + font-size: 12px; + line-height: 1.7; + overflow-x: auto; + tab-size: 2; +} + +#nd-docs-layout pre:not(figure *) { + border-radius: 6px; + border: 1px solid var(--color-border-subtle); + padding: 0.875rem 1rem; +} + +#nd-docs-layout figure pre { + border: none; + border-radius: 0; + margin: 0; +} + +#nd-docs-layout figure.shiki { + background-color: var(--color-bg-inset, var(--color-bg-surface)) !important; + border-color: var(--color-border-subtle) !important; + border-radius: 6px; +} + +/* Copy button fade on hover */ +#nd-docs-layout figure[data-rehype-pretty-code-figure] button, +#nd-docs-layout [data-rehype-pretty-code-figure] button[aria-label] { + opacity: 0; + transition: opacity 0.15s ease; +} + +#nd-docs-layout figure[data-rehype-pretty-code-figure]:hover button, +#nd-docs-layout [data-rehype-pretty-code-figure]:hover button[aria-label] { + opacity: 1; +} + +/* ═══════════════════════════════════════════════════════════════════════════ + 5. CALLOUTS + ═══════════════════════════════════════════════════════════════════════════ */ + +#nd-docs-layout [data-callout] { + border-radius: 6px; + border-left-width: 3px; + padding: 0.625rem 0.875rem; + margin: 1.25rem 0; + font-size: 12.5px; +} + +/* Force info callout to amber (fumadocs uses --color-fd-info internally) */ +#nd-docs-layout [data-callout][data-type="info"] { + --callout-color: var(--docs-accent) !important; +} + +/* Edit on GitHub button — force amber colors */ +#nd-docs-layout a[href*="github.com"][href*="/blob/"] { + color: var(--color-text-secondary) !important; +} + +#nd-docs-layout a[href*="github.com"][href*="/blob/"]:hover { + color: var(--docs-accent) !important; +} + +/* ═══════════════════════════════════════════════════════════════════════════ + 6. SCROLLBAR + ═══════════════════════════════════════════════════════════════════════════ */ + +#nd-docs-layout pre::-webkit-scrollbar { + height: 4px; +} + +#nd-docs-layout pre::-webkit-scrollbar-track { + background: transparent; +} + +#nd-docs-layout pre::-webkit-scrollbar-thumb { + background: var(--color-scrollbar, rgba(255, 240, 220, 0.15)); + border-radius: 2px; +} + +/* ═══════════════════════════════════════════════════════════════════════════ + 7. SEARCH MODAL + ═══════════════════════════════════════════════════════════════════════════ */ + +[data-search-dialog] { + --color-fd-background: var(--color-bg-elevated); + --color-fd-border: var(--color-border-default); + --color-fd-card: var(--color-bg-surface); +} + +/* ═══════════════════════════════════════════════════════════════════════════ + 8. DENSITY — tighter spacing to match dashboard + ═══════════════════════════════════════════════════════════════════════════ */ + +#nd-docs-layout .prose > * + * { + margin-top: 1em; +} + +#nd-docs-layout .prose > h2 { + margin-top: 2em; +} + +#nd-docs-layout .prose > h3 { + margin-top: 1.5em; +} + +/* ═══════════════════════════════════════════════════════════════════════════ + 9. DOCS 404 + ═══════════════════════════════════════════════════════════════════════════ */ + +#nd-docs-layout .docs-missing-wrap { + display: flex; + min-height: calc(100vh - var(--fd-nav-height, 0px) - 8rem); + align-items: center; + padding-block: 2rem; +} + +#nd-docs-layout .docs-missing-card { + width: 100%; + max-width: 56rem; + margin-inline: auto; + overflow: hidden; + border: 1px solid var(--color-border-default); + border-radius: 8px; + background: var(--color-bg-surface); +} + +#nd-docs-layout .docs-missing-label { + padding: 0.75rem 1.5rem; + border-bottom: 1px solid var(--color-border-subtle); + background: var(--color-bg-inset); + color: var(--color-accent-amber); + font-family: var(--font-mono); + font-size: 0.6875rem; + font-weight: 600; + letter-spacing: 0.22em; + text-transform: uppercase; +} + +#nd-docs-layout .docs-missing-content { + display: grid; + grid-template-columns: minmax(0, 1fr) 15rem; + gap: 1.5rem; + align-items: start; + padding: 1.5rem; +} + +#nd-docs-layout .docs-missing-copy h2 { + max-width: 42rem; + margin: 0; + color: var(--color-text-primary); + font-size: 1.875rem; + font-weight: 650; + line-height: 1.15; + letter-spacing: 0; +} + +#nd-docs-layout .docs-missing-copy p { + max-width: 42rem; + margin: 0.75rem 0 0; + color: var(--color-text-secondary); + font-size: 0.875rem; + line-height: 1.65; +} + +#nd-docs-layout .docs-missing-actions { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + margin-top: 1.5rem; +} + +#nd-docs-layout .docs-missing-primary, +#nd-docs-layout .docs-missing-secondary { + display: inline-flex; + height: 2.5rem; + align-items: center; + justify-content: center; + border-radius: 6px; + padding: 0 1rem; + font-size: 0.875rem; + font-weight: 600; + text-decoration: none; + transition: border-color 0.15s ease, color 0.15s ease, opacity 0.15s ease; +} + +#nd-docs-layout .docs-missing-primary { + background: var(--color-accent-amber); + color: #1a1918; +} + +#nd-docs-layout .docs-missing-primary:hover { + opacity: 0.9; +} + +#nd-docs-layout .docs-missing-secondary { + border: 1px solid var(--color-border-default); + color: var(--color-text-secondary); +} + +#nd-docs-layout .docs-missing-secondary:hover { + color: var(--color-text-primary); +} + +#nd-docs-layout .docs-missing-terminal { + min-width: 0; + padding: 1rem; + border: 1px solid var(--color-border-subtle); + border-radius: 6px; + background: var(--color-bg-inset); + color: var(--color-text-secondary); + font-family: var(--font-mono); + font-size: 0.75rem; + line-height: 1.6; +} + +#nd-docs-layout .docs-missing-terminal p { + margin: 0; +} + +#nd-docs-layout .docs-missing-terminal p + p { + margin-top: 0.375rem; +} + +#nd-docs-layout .docs-missing-dots { + display: flex; + gap: 0.375rem; + margin-bottom: 0.75rem; +} + +#nd-docs-layout .docs-missing-dots span { + width: 0.5rem; + height: 0.5rem; + border-radius: 999px; +} + +#nd-docs-layout .docs-missing-dot-red { + background: #ef4444; +} + +#nd-docs-layout .docs-missing-dot-yellow { + background: #f59e0b; +} + +#nd-docs-layout .docs-missing-dot-green { + background: #22c55e; +} + +#nd-docs-layout .docs-missing-command { + color: var(--color-text-tertiary); +} + +#nd-docs-layout .docs-missing-status { + color: var(--color-text-primary); + font-weight: 600; +} + +@media (max-width: 900px) { + #nd-docs-layout .docs-missing-content { + grid-template-columns: 1fr; + } +} diff --git a/frontend/src/landing/app/docs/layout.tsx b/frontend/src/landing/app/docs/layout.tsx new file mode 100644 index 000000000..68e0ec3ca --- /dev/null +++ b/frontend/src/landing/app/docs/layout.tsx @@ -0,0 +1,129 @@ +import type { ReactNode } from "react"; +import { DocsLayout } from "fumadocs-ui/layouts/docs"; +import { RootProvider } from "fumadocs-ui/provider"; +import type { LinkItemType } from "fumadocs-ui/layouts/shared"; +import { source } from "@/lib/source"; +import "./docs.css"; + +function GithubIcon({ size = 16 }: { size?: number } = {}) { + return ( + + + + ); +} + +function XIcon() { + return ( + + + + ); +} + +function DiscordIcon() { + return ( + + + + ); +} + +const links: LinkItemType[] = [ + { + type: "icon", + label: "X (Twitter)", + icon: , + text: "X", + url: "https://x.com/aoagents", + external: true, + }, + { + type: "icon", + label: "Discord", + icon: , + text: "Discord", + url: "https://discord.gg/UZv7JjxbwG", + external: true, + }, +]; + +async function GitHubStars() { + let stars: string | null = null; + try { + const res = await fetch( + "https://api.github.com/repos/ComposioHQ/agent-orchestrator", + { next: { revalidate: 3600 } }, + ); + if (res.ok) { + const data = await res.json(); + const count = data.stargazers_count as number; + stars = count >= 1000 ? `${(count / 1000).toFixed(1)}K` : String(count); + } + } catch { + /* GitHub API failed — hide stars */ + } + + return stars ? ( + + + + + {stars} + + ) : null; +} + +export default function Layout({ children }: { children: ReactNode }) { + return ( + + + + AO + + ), + }} + sidebar={{ + defaultOpenLevel: 1, + collapsible: true, + banner: ( + + + ComposioHQ/agent-orchestrator + + + ), + }} + > + {children} + + + ); +} \ No newline at end of file diff --git a/frontend/src/landing/app/docs/not-found.tsx b/frontend/src/landing/app/docs/not-found.tsx new file mode 100644 index 000000000..7569c68e3 --- /dev/null +++ b/frontend/src/landing/app/docs/not-found.tsx @@ -0,0 +1,5 @@ +import { DocsMissingPage } from "@/components/docs/DocsMissingPage"; + +export default function DocsNotFound() { + return ; +}