From 9b50861a326ea57c741dba85db2ebcf07f5cd67c Mon Sep 17 00:00:00 2001 From: christopherkindl <53372002+christopherkindl@users.noreply.github.com> Date: Mon, 10 Aug 2026 17:04:21 +0200 Subject: [PATCH 01/29] docs: redesign homepage --- .../docs/app/[lang]/(home)/components/cta.tsx | 2 +- .../(home)/graphics/monorepo-visual.tsx | 43 + .../(home)/graphics/provider-badges.tsx | 42 + .../app/[lang]/(home)/graphics/providers.tsx | 948 ------------------ .../(home)/graphics/remote-cache-visual.tsx | 21 + .../[lang]/(home)/graphics/sizing-string.ts | 3 +- .../app/[lang]/(home)/highlighted-code.tsx | 37 + apps/docs/app/[lang]/(home)/layout.tsx | 2 +- apps/docs/app/[lang]/(home)/page.tsx | 311 +++--- apps/docs/app/[lang]/layout.tsx | 2 +- apps/docs/app/styles/geistdocs.css | 6 +- apps/docs/components/geistdocs/footer.tsx | 59 -- apps/docs/components/logos.tsx | 6 +- .../remote-cache-counter/client.tsx | 13 +- apps/docs/components/testimonials.tsx | 20 +- apps/docs/components/ui/button.tsx | 60 -- apps/docs/geistdocs.tsx | 11 +- apps/docs/package.json | 2 +- pnpm-lock.yaml | 46 +- 19 files changed, 387 insertions(+), 1247 deletions(-) create mode 100644 apps/docs/app/[lang]/(home)/graphics/monorepo-visual.tsx create mode 100644 apps/docs/app/[lang]/(home)/graphics/provider-badges.tsx delete mode 100644 apps/docs/app/[lang]/(home)/graphics/providers.tsx create mode 100644 apps/docs/app/[lang]/(home)/graphics/remote-cache-visual.tsx create mode 100644 apps/docs/app/[lang]/(home)/highlighted-code.tsx delete mode 100644 apps/docs/components/geistdocs/footer.tsx delete mode 100644 apps/docs/components/ui/button.tsx diff --git a/apps/docs/app/[lang]/(home)/components/cta.tsx b/apps/docs/app/[lang]/(home)/components/cta.tsx index cf25a9b56e9d1..5e7dbc666aaac 100644 --- a/apps/docs/app/[lang]/(home)/components/cta.tsx +++ b/apps/docs/app/[lang]/(home)/components/cta.tsx @@ -1,5 +1,5 @@ import DynamicLink from "fumadocs-core/dynamic-link"; -import { Button } from "@/components/ui/button"; +import { Button } from "@vercel/geistdocs/components/button"; type CTAProps = { title: string; diff --git a/apps/docs/app/[lang]/(home)/graphics/monorepo-visual.tsx b/apps/docs/app/[lang]/(home)/graphics/monorepo-visual.tsx new file mode 100644 index 0000000000000..495912f8b8517 --- /dev/null +++ b/apps/docs/app/[lang]/(home)/graphics/monorepo-visual.tsx @@ -0,0 +1,43 @@ +import { SiNextdotjs, SiNuxt, SiSvelte } from "@icons-pack/react-simple-icons"; + +const tasks = [ + { label: "apps/web", Logo: SiNextdotjs, themeAware: true }, + { label: "apps/docs", Logo: SiSvelte }, + { label: "app/blog", Logo: SiNuxt }, +]; + +export function MonorepoVisual() { + return ( + + ); +} diff --git a/apps/docs/app/[lang]/(home)/graphics/provider-badges.tsx b/apps/docs/app/[lang]/(home)/graphics/provider-badges.tsx new file mode 100644 index 0000000000000..714ddc5656f64 --- /dev/null +++ b/apps/docs/app/[lang]/(home)/graphics/provider-badges.tsx @@ -0,0 +1,42 @@ +import { + SiCircleci, + SiGithub, + SiGitlab, + SiJenkins, + SiVercel, +} from "@icons-pack/react-simple-icons"; + +const providers = [ + { label: "GitHub", Logo: SiGithub, themeAware: true }, + { label: "GitLab", Logo: SiGitlab }, + { label: "Vercel", Logo: SiVercel, themeAware: true }, + { label: "Jenkins", Logo: SiJenkins, logoClassName: "dark:fill-gray-1000" }, + { + label: "CircleCI", + Logo: SiCircleci, + logoClassName: "dark:fill-gray-1000", + }, +]; + +export function ProviderBadges() { + return ( +
+
+ {providers.map(({ label, Logo, logoClassName, themeAware }) => ( + + + ))} +
+
+ ); +} diff --git a/apps/docs/app/[lang]/(home)/graphics/providers.tsx b/apps/docs/app/[lang]/(home)/graphics/providers.tsx deleted file mode 100644 index 4e7c1e2c19e89..0000000000000 --- a/apps/docs/app/[lang]/(home)/graphics/providers.tsx +++ /dev/null @@ -1,948 +0,0 @@ -"use client"; -import { useTheme } from "next-themes"; -import { useEffect, useState } from "react"; -import { sizingString } from "./sizing-string"; - -export const CiProviders = () => { - const { resolvedTheme } = useTheme(); - - const [mounted, setMounted] = useState(false); - - useEffect(() => { - setMounted(true); - }, []); - - if (!mounted) return
; - - if (resolvedTheme === "dark") { - return ; - } - return ; -}; - -const ProvidersLight = () => { - return ( -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- ); -}; - -export const ProvidersDark = () => { - return ( -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- ); -}; diff --git a/apps/docs/app/[lang]/(home)/graphics/remote-cache-visual.tsx b/apps/docs/app/[lang]/(home)/graphics/remote-cache-visual.tsx new file mode 100644 index 0000000000000..7cf2d5dc8539e --- /dev/null +++ b/apps/docs/app/[lang]/(home)/graphics/remote-cache-visual.tsx @@ -0,0 +1,21 @@ +export function RemoteCacheVisual() { + return ( + + ); +} diff --git a/apps/docs/app/[lang]/(home)/graphics/sizing-string.ts b/apps/docs/app/[lang]/(home)/graphics/sizing-string.ts index 7769c8062c6e9..217ee4d018f56 100644 --- a/apps/docs/app/[lang]/(home)/graphics/sizing-string.ts +++ b/apps/docs/app/[lang]/(home)/graphics/sizing-string.ts @@ -1,2 +1 @@ -export const sizingString = - "mx-auto h-[160px] max-w-full xs:w-[326px] sm:w-[542px] md:w-[194px] lg:w-[255px] xl:w-[298px] xs:w-full"; +export const sizingString = "size-full"; diff --git a/apps/docs/app/[lang]/(home)/highlighted-code.tsx b/apps/docs/app/[lang]/(home)/highlighted-code.tsx new file mode 100644 index 0000000000000..04e8478c1aca0 --- /dev/null +++ b/apps/docs/app/[lang]/(home)/highlighted-code.tsx @@ -0,0 +1,37 @@ +import { CodeBlock } from "@vercel/geistdocs/components/code-block"; +import { geistShikiTheme } from "@vercel/geistdocs/shiki-theme"; +import { highlight } from "fumadocs-core/highlight"; +import type { ComponentProps } from "react"; +import type { BundledLanguage } from "shiki"; + +type HighlightedCodeProps = { + code: string; + lang: BundledLanguage; + caption: string; +}; + +export const HighlightedCode = async ({ + code, + lang, + caption, +}: HighlightedCodeProps) => { + const rendered = await highlight(code, { + lang, + engine: "js", + theme: geistShikiTheme, + components: { + pre: ({ children, className, style }: ComponentProps<"pre">) => ( + + {children} + + ), + }, + }); + + return ( +
+
{rendered}
+ {caption} +
+ ); +}; diff --git a/apps/docs/app/[lang]/(home)/layout.tsx b/apps/docs/app/[lang]/(home)/layout.tsx index 906e06918d529..d00309cb6f97f 100644 --- a/apps/docs/app/[lang]/(home)/layout.tsx +++ b/apps/docs/app/[lang]/(home)/layout.tsx @@ -6,7 +6,7 @@ const Layout = async ({ children, params }: LayoutProps<"/[lang]">) => { return ( -
{children}
+
{children}
); }; diff --git a/apps/docs/app/[lang]/(home)/page.tsx b/apps/docs/app/[lang]/(home)/page.tsx index 209e467d8ba4f..be5ba190def73 100644 --- a/apps/docs/app/[lang]/(home)/page.tsx +++ b/apps/docs/app/[lang]/(home)/page.tsx @@ -1,35 +1,43 @@ -import { DynamicCodeBlock } from "fumadocs-ui/components/dynamic-codeblock"; import Link from "next/link"; -import { Button } from "@/components/ui/button"; -import { Grid } from "@/components/grid/grid"; -import { GridCell } from "@/components/grid/grid-cell"; -import { Snippet } from "@/components/snippet"; +import { + CommandPromptContent, + CommandPromptCopy, + CommandPromptList, + CommandPromptPrefix, + CommandPromptRoot, + CommandPromptSurface, + CommandPromptTrigger, + CommandPromptTriggerDivider, + CommandPromptViewport, +} from "@vercel/geistdocs/components/command-prompt"; +import { Button } from "@vercel/geistdocs/components/button"; import { Testimonials } from "@/components/testimonials"; -import { ArrowRight } from "@/components/icons/arrow-right"; import { RemoteCacheCounterClient } from "@/components/remote-cache-counter/client"; import { createMetadata } from "@/lib/create-metadata"; -import { shikiTheme } from "@/lib/shiki-theme"; -import { CiProviders } from "./graphics/providers"; -import { RemoteCachingGraphic } from "./graphics/remote-caching"; -import { EffortlessGraphic } from "./graphics/effortless"; -import { DottedLines } from "./graphics/dotted-lines"; +import { HighlightedCode } from "./highlighted-code"; +import { ProviderBadges } from "./graphics/provider-badges"; +import { RemoteCacheVisual } from "./graphics/remote-cache-visual"; +import { MonorepoVisual } from "./graphics/monorepo-visual"; const FEATURES = [ { title: "Works with any provider", - description: "Integrate with any CI provider for speed at all scales", - illustration: + description: + "Integrate with any CI provider to keep every workflow fast as your team and codebase grow.", + illustration: , }, { title: "Remote Caching", - description: "Never do the same work twice", - illustration: + description: + "Share task outputs across machines and CI so your team never repeats the same work.", + illustration: , }, { title: "Effortless monorepos", - description: "Easily define your workflows for local development and CI", - illustration: - } + description: + "Define dependable workflows once, then run them consistently across local development and CI.", + illustration: , + }, ]; const simpleTurboJson = `{ @@ -51,148 +59,161 @@ turbo run build`; export const metadata = createMetadata({ description: "Turborepo is a build system optimized for JavaScript and TypeScript, written in Rust.", - canonicalPath: "/" + canonicalPath: "/", }); export default function HomePage() { return ( -
- - - -
-

- Make ship happen -

-

- Turborepo is the build system for JavaScript and TypeScript - codebases +

+
+
+

+ The build system that scales with your codebase +

+

+ Turborepo is the build system for JavaScript and TypeScript + codebases +

+ + + + For humans + + + + For agents + + + + $ + + + npm i turbo + + + npx skills add vercel/turborepo + + + + + +
+
+
+
+
+

+ Scale your workflows +

+

+ Optimize your local and CI tasks to save years of engineering time and + compute. +

+
    + {FEATURES.map((feature) => ( +
  • +
    {feature.illustration} -

    +

    +
    +

    {feature.title}

    -

    +

    {feature.description}

    - ))} -
- - -
-
-

- Simple setup -

-

- Start a new repository or migrate an existing repo incrementally - in minutes. -

-
- -
-
-
- - - Declaring a build task - -
-
- - - Linking to Remote Cache and running tasks - -
+ + ))} + + +
+
+
+

Simple setup

+

+ Start a new repository or migrate an existing repo incrementally + in minutes. +

- - -

- What builders say about Turborepo + +

+
+ + +
+
+
+

+ What builders say about Turborepo +

+ +
+
+
+

+ Deploy your Turborepo today

- - - - -
-

- Deploy your Turborepo today. -

-
- - -
+
+ + + + $ + + + npm i turbo + + + + +
- - +
+
); } diff --git a/apps/docs/app/[lang]/layout.tsx b/apps/docs/app/[lang]/layout.tsx index 616fd827898da..aac829b6fb283 100644 --- a/apps/docs/app/[lang]/layout.tsx +++ b/apps/docs/app/[lang]/layout.tsx @@ -1,9 +1,9 @@ import "../global.css"; import type { Metadata } from "next"; import { VercelToolbar } from "@vercel/toolbar/next"; +import { Footer } from "@vercel/geistdocs/footer"; import { Navbar } from "@vercel/geistdocs/navbar"; import { FaviconHandler } from "@/components/favicon-handler"; -import { Footer } from "@/components/geistdocs/footer"; import { GeistdocsProvider } from "@/components/geistdocs/provider"; import { basePath } from "@/geistdocs"; import { config } from "@/lib/geistdocs/config"; diff --git a/apps/docs/app/styles/geistdocs.css b/apps/docs/app/styles/geistdocs.css index 9c263bbc8e3ef..88e73bde94cbf 100644 --- a/apps/docs/app/styles/geistdocs.css +++ b/apps/docs/app/styles/geistdocs.css @@ -16,6 +16,8 @@ @theme inline { --breakpoint-xs: 400px; --breakpoint-2xs: 360px; + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); --color-background: var(--background); --color-foreground: var(--foreground); --color-card: var(--card); @@ -84,10 +86,10 @@ --sidebar-foreground: oklch(0.985 0 0); } -/* Keep the site's body colors (the package defaults to bg-background-100). */ +/* Use the Geist secondary background across every page. */ @layer base { body { - @apply bg-background text-foreground; + @apply bg-background-200 text-foreground; } } diff --git a/apps/docs/components/geistdocs/footer.tsx b/apps/docs/components/geistdocs/footer.tsx deleted file mode 100644 index 26da921275fb9..0000000000000 --- a/apps/docs/components/geistdocs/footer.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import Link from "next/link"; -import { SiVercel } from "@icons-pack/react-simple-icons"; -import { ThemeToggle } from "@vercel/geistdocs/controls"; -import { footerLinks } from "@/geistdocs"; - -type FooterLink = { - href: string; - label: string; - id?: string; -}; - -const FooterSection = ({ - title, - links -}: { - title: string; - links: FooterLink[]; -}) => ( -
-

{title}

-
    - {links.map((link) => ( -
  • - {link.href.startsWith("http") ? ( - - {link.label} - - ) : ( - - {link.label} - - )} -
  • - ))} -
-
-); - -export const Footer = () => ( -
-
-
- -
- - - - -
-
-
- -
-
-
-); diff --git a/apps/docs/components/logos.tsx b/apps/docs/components/logos.tsx index d84bfbaa15a0d..3784d6250d2a8 100644 --- a/apps/docs/components/logos.tsx +++ b/apps/docs/components/logos.tsx @@ -16,9 +16,11 @@ export function VercelLogo({ className }: { className?: string }): JSX.Element { } export function TurborepoLogo({ - className + className, + monochrome = false }: { className?: string; + monochrome?: boolean; }): JSX.Element { const gradientId = `turborepo-logo-gradient-${React.useId().replace(/:/g, "")}`; @@ -52,7 +54,7 @@ export function TurborepoLogo({ fillRule="evenodd" clipRule="evenodd" d="M40.2115 14.744V7.125C56.7719 8.0104 69.9275 21.7208 69.9275 38.5016C69.9275 55.2824 56.7719 68.989 40.2115 69.8782V62.2592C52.5539 61.3776 62.3275 51.0644 62.3275 38.5016C62.3275 25.9388 52.5539 15.6256 40.2115 14.744ZM20.5048 54.0815C17.233 50.3043 15.124 45.4935 14.7478 40.2115H7.125C7.5202 47.6025 10.4766 54.3095 15.1088 59.4737L20.501 54.0815H20.5048ZM36.7916 69.8782V62.2592C31.5058 61.883 26.695 59.7778 22.9178 56.5022L17.5256 61.8944C22.6936 66.5304 29.4006 69.483 36.7878 69.8782H36.7916Z" - fill={`url(#${gradientId})`} + fill={monochrome ? "currentColor" : `url(#${gradientId})`} /> ); diff --git a/apps/docs/components/remote-cache-counter/client.tsx b/apps/docs/components/remote-cache-counter/client.tsx index 304deb534a03e..5935afbae6aa7 100644 --- a/apps/docs/components/remote-cache-counter/client.tsx +++ b/apps/docs/components/remote-cache-counter/client.tsx @@ -6,7 +6,7 @@ import { cn } from "@/lib/utils"; import { useTurborepoMinutesSaved } from "./use-turborepo-minutes-saved"; const counterFormatter = Intl.NumberFormat(undefined, { - maximumFractionDigits: 0 + maximumFractionDigits: 0, }); // A number to start the counter at that is lower than the actual time saved @@ -14,7 +14,7 @@ const counterFormatter = Intl.NumberFormat(undefined, { const ARBITRARY_START_NUMBER = 540070107 / 60; export function RemoteCacheCounterClient({ - className + className, }: { className?: string; }) { @@ -26,11 +26,11 @@ export function RemoteCacheCounterClient({ const springValue = useSpring(displayValue, { mass: 1, stiffness: 170, - damping: 60 + damping: 60, }); const display = useTransform(springValue, (val) => - counterFormatter.format(val) + counterFormatter.format(val), ); useEffect(() => { @@ -39,7 +39,10 @@ export function RemoteCacheCounterClient({ return ( {display} diff --git a/apps/docs/components/testimonials.tsx b/apps/docs/components/testimonials.tsx index aa0c9c30fdd23..34a1b7e49445a 100644 --- a/apps/docs/components/testimonials.tsx +++ b/apps/docs/components/testimonials.tsx @@ -14,33 +14,31 @@ export const Card = ({ name, alias, avatar, url, children }: CardProps) => { -
+
{name}
-
- {name} -
-
- {alias} -
+
{name}
+
{alias}
-
{children}
+
+ {children} +
); }; export const Testimonials = () => { return ( -
+
svg]:px-3", - sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", - lg: "h-10 rounded-md px-6 has-[>svg]:px-4", - icon: "size-9", - "icon-sm": "size-8", - "icon-lg": "size-10" - } - }, - defaultVariants: { - variant: "default", - size: "default" - } - } -); - -function Button({ - className, - variant, - size, - asChild = false, - ...props -}: React.ComponentProps<"button"> & - VariantProps & { - asChild?: boolean; - }) { - const Comp = asChild ? SlotPrimitive.Slot : "button"; - - return ( - - ); -} - -export { Button, buttonVariants }; diff --git a/apps/docs/geistdocs.tsx b/apps/docs/geistdocs.tsx index 5184938cf55e7..73befb31108c3 100644 --- a/apps/docs/geistdocs.tsx +++ b/apps/docs/geistdocs.tsx @@ -1,7 +1,16 @@ import type { GeistdocsAgentReadinessConfig } from "@vercel/geistdocs/config"; import { LogoTurborepo } from "@vercel/geistdocs/assets/logos/logo-turborepo"; +import { TurborepoLogo } from "@/components/logos"; -export const Logo = () => ; +export const Logo = () => ( + <> + + + +); export const github = { branch: "main", diff --git a/apps/docs/package.json b/apps/docs/package.json index d45071ab4cc78..0995ba7da27d3 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -25,7 +25,7 @@ "fumadocs-mdx": "14.0.4", "fumadocs-openapi": "10.2.7", "fumadocs-ui": "16.2.2", - "geist": "^1.7.1", + "geist": "^1.7.2", "linkedom": "0.18.12", "lucide-react": "^0.555.0", "motion": "^12.23.25", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5fe1eeb73a26c..9e5df99a51a1e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -109,8 +109,8 @@ importers: specifier: 16.2.2 version: 16.2.2(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(lucide-react@0.555.0(react@19.2.4))(next@16.3.0(@opentelemetry/api@1.9.0)(@types/node@24.10.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tailwindcss@4.1.18) geist: - specifier: ^1.7.1 - version: 1.7.1(next@16.3.0(@opentelemetry/api@1.9.0)(@types/node@24.10.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) + specifier: ^1.7.2 + version: 1.7.2(next@16.3.0(@opentelemetry/api@1.9.0)(@types/node@24.10.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) linkedom: specifier: 0.18.12 version: 0.18.12 @@ -2044,6 +2044,13 @@ packages: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 + '@napi-rs/wasm-runtime@1.2.2': + resolution: {integrity: sha512-JfB4kuJQjaoHuCTseIINHtHWeJnvgEcxjwA5t/Y00ZgaOO1Crz3fjT/p8kT28zA/Caz7oiUMn3d6H2yOVCVwuw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} + peerDependencies: + '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.3 + '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.3 + '@next/env@16.3.0': resolution: {integrity: sha512-o9r1S0BNiNreHP9Vs+Qnqd9kviDkJh8xIACY7UFZSmiGbbQRzPBBosvHzAU4TULHOIuOj/18RSsyz2qrREmIFw==} @@ -4710,6 +4717,9 @@ packages: '@tybys/wasm-util@0.10.2': resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + '@types/async-retry@1.4.5': resolution: {integrity: sha512-YrdjSD+yQv7h6d5Ip+PMxh3H6ZxKyQk0Ts+PvaNRInxneG9PFVZjFg77ILAN+N6qYf7g4giSJ1l+ZjQ1zeegvA==} @@ -5491,6 +5501,11 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + acorn@8.18.0: + resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} + engines: {node: '>=0.4.0'} + hasBin: true + agent-base@7.1.4: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} @@ -6992,8 +7007,8 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - geist@1.7.1: - resolution: {integrity: sha512-XF8DM30ODhDu0Ng5S2kOS8j4ZkG/6z2fKWAiFJA7wG0Hc92ZXgjYLrwbD9bVU4nGVzwboPtG+QtaPGsfgnXLaA==} + geist@1.7.2: + resolution: {integrity: sha512-Gu5lDFa3pLRyoBlBPf0QIFHVdWAnpco7fS1bJm41jyLPFoguBgiubseUN2oLXMgqZ7uxAxDoXcHMhCY/fOTTgg==} peerDependencies: next: '>=13.2.0' @@ -11485,6 +11500,13 @@ snapshots: '@tybys/wasm-util': 0.10.2 optional: true + '@napi-rs/wasm-runtime@1.2.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@tybys/wasm-util': 0.10.3 + optional: true + '@next/env@16.3.0': {} '@next/swc-darwin-arm64@16.3.0': @@ -11585,7 +11607,7 @@ snapshots: '@oxc-resolver/binding-wasm32-wasi@11.19.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: - '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + '@napi-rs/wasm-runtime': 1.2.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -14013,6 +14035,11 @@ snapshots: tslib: 2.8.1 optional: true + '@tybys/wasm-util@0.10.3': + dependencies: + tslib: 2.8.1 + optional: true + '@types/async-retry@1.4.5': dependencies: '@types/retry': 0.12.5 @@ -14715,13 +14742,16 @@ snapshots: acorn-walk@8.3.5: dependencies: - acorn: 8.17.0 + acorn: 8.18.0 optional: true acorn@8.16.0: {} acorn@8.17.0: {} + acorn@8.18.0: + optional: true + agent-base@7.1.4: {} agent-base@9.0.0: {} @@ -16312,7 +16342,7 @@ snapshots: function-bind@1.1.2: {} - geist@1.7.1(next@16.3.0(@opentelemetry/api@1.9.0)(@types/node@24.10.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)): + geist@1.7.2(next@16.3.0(@opentelemetry/api@1.9.0)(@types/node@24.10.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)): dependencies: next: 16.3.0(@opentelemetry/api@1.9.0)(@types/node@24.10.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -19974,7 +20004,7 @@ snapshots: '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 '@types/node': 18.17.4 - acorn: 8.17.0 + acorn: 8.18.0 acorn-walk: 8.3.5 arg: 4.1.3 create-require: 1.1.1 From 383bf341082ff050c6ddf6a077bff0118cfe959b Mon Sep 17 00:00:00 2001 From: christopherkindl <53372002+christopherkindl@users.noreply.github.com> Date: Mon, 10 Aug 2026 17:16:20 +0200 Subject: [PATCH 02/29] docs: prevent counter layout shift --- apps/docs/components/remote-cache-counter/client.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs/components/remote-cache-counter/client.tsx b/apps/docs/components/remote-cache-counter/client.tsx index 5935afbae6aa7..9b91bffdaab08 100644 --- a/apps/docs/components/remote-cache-counter/client.tsx +++ b/apps/docs/components/remote-cache-counter/client.tsx @@ -11,7 +11,7 @@ const counterFormatter = Intl.NumberFormat(undefined, { // A number to start the counter at that is lower than the actual time saved // to make the counter not start at 0 -const ARBITRARY_START_NUMBER = 540070107 / 60; +const ARBITRARY_START_NUMBER = 16_000_000; export function RemoteCacheCounterClient({ className, From 4022cc40b5aacf896f20a3f9a0e18002f7d027e1 Mon Sep 17 00:00:00 2001 From: christopherkindl <53372002+christopherkindl@users.noreply.github.com> Date: Mon, 10 Aug 2026 17:18:02 +0200 Subject: [PATCH 03/29] docs: adjust counter start value --- apps/docs/components/remote-cache-counter/client.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs/components/remote-cache-counter/client.tsx b/apps/docs/components/remote-cache-counter/client.tsx index 9b91bffdaab08..7ab91acdfce68 100644 --- a/apps/docs/components/remote-cache-counter/client.tsx +++ b/apps/docs/components/remote-cache-counter/client.tsx @@ -11,7 +11,7 @@ const counterFormatter = Intl.NumberFormat(undefined, { // A number to start the counter at that is lower than the actual time saved // to make the counter not start at 0 -const ARBITRARY_START_NUMBER = 16_000_000; +const ARBITRARY_START_NUMBER = 12_434_353; export function RemoteCacheCounterClient({ className, From e16f2193cc82b6a153fca570081aebc9ef3c0f18 Mon Sep 17 00:00:00 2001 From: christopherkindl <53372002+christopherkindl@users.noreply.github.com> Date: Mon, 10 Aug 2026 17:30:29 +0200 Subject: [PATCH 04/29] docs: update homepage hero copy --- apps/docs/app/[lang]/(home)/page.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/docs/app/[lang]/(home)/page.tsx b/apps/docs/app/[lang]/(home)/page.tsx index be5ba190def73..b19fbcb51e11c 100644 --- a/apps/docs/app/[lang]/(home)/page.tsx +++ b/apps/docs/app/[lang]/(home)/page.tsx @@ -71,8 +71,7 @@ export default function HomePage() { The build system that scales with your codebase

- Turborepo is the build system for JavaScript and TypeScript - codebases + Turborepo is the build system for coding agents

From 548aa5447d00e3d4391074b0c20639379a23768b Mon Sep 17 00:00:00 2001 From: christopherkindl <53372002+christopherkindl@users.noreply.github.com> Date: Mon, 10 Aug 2026 18:18:47 +0200 Subject: [PATCH 05/29] update --- apps/docs/app/[lang]/(home)/page.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/docs/app/[lang]/(home)/page.tsx b/apps/docs/app/[lang]/(home)/page.tsx index b19fbcb51e11c..2d351fbae25f2 100644 --- a/apps/docs/app/[lang]/(home)/page.tsx +++ b/apps/docs/app/[lang]/(home)/page.tsx @@ -71,7 +71,8 @@ export default function HomePage() { The build system that scales with your codebase

- Turborepo is the build system for coding agents + Turborepo is the build system for JavaScript and TypeScript + codebases

@@ -133,7 +134,7 @@ export default function HomePage() {

Scale your workflows

-

+

Optimize your local and CI tasks to save years of engineering time and compute.

From c2a2f4a532541dc3c2e5ef9828c05baa8d0a6a81 Mon Sep 17 00:00:00 2001 From: christopherkindl <53372002+christopherkindl@users.noreply.github.com> Date: Mon, 10 Aug 2026 22:28:21 +0200 Subject: [PATCH 06/29] feat: Redesign documentation homepage --- .../graphics/agent-workflow-visuals.tsx | 214 ++++++++++++++++++ .../(home)/graphics/remote-cache-visual.tsx | 2 +- apps/docs/app/[lang]/(home)/page.tsx | 125 +++++++--- .../api/remote-cache-minutes-saved/route.ts | 12 +- .../remote-cache-counter/client.tsx | 10 +- .../remote-cache-counter/constants.ts | 1 + 6 files changed, 321 insertions(+), 43 deletions(-) create mode 100644 apps/docs/app/[lang]/(home)/graphics/agent-workflow-visuals.tsx create mode 100644 apps/docs/components/remote-cache-counter/constants.ts diff --git a/apps/docs/app/[lang]/(home)/graphics/agent-workflow-visuals.tsx b/apps/docs/app/[lang]/(home)/graphics/agent-workflow-visuals.tsx new file mode 100644 index 0000000000000..e793e42e89ad2 --- /dev/null +++ b/apps/docs/app/[lang]/(home)/graphics/agent-workflow-visuals.tsx @@ -0,0 +1,214 @@ +import { IconCheck } from "@vercel/geistdocs/assets/icons/icon-check"; +import type { ReactNode } from "react"; + +const validationTasks = [ + { name: "lint", time: "320ms" }, + { name: "test", time: "680ms" }, + { name: "build", time: "1.1s" }, +]; + +function ChatTail({ side }: { side: "left" | "right" }) { + return ( + + ); +} + +function ChatBubble({ + align, + children, +}: { + align: "start" | "end"; + children: ReactNode; +}) { + return ( +
+
+ {children} + +
+
+ ); +} + +export function ValidationLoopsVisual() { + return ( +