From 316bf2bc4426b98f09888cc0158d609ebe3fea26 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Thu, 9 Apr 2026 15:22:51 -0700 Subject: [PATCH 01/12] feat(website): replace FullStackSection with LibrariesSection teaser cards Co-Authored-By: Claude Sonnet 4.6 --- apps/website/src/app/page.tsx | 6 +- .../components/landing/LibrariesSection.tsx | 151 ++++++++++++++++++ 2 files changed, 154 insertions(+), 3 deletions(-) create mode 100644 apps/website/src/components/landing/LibrariesSection.tsx diff --git a/apps/website/src/app/page.tsx b/apps/website/src/app/page.tsx index 2316ffa5c..f7eaac84a 100644 --- a/apps/website/src/app/page.tsx +++ b/apps/website/src/app/page.tsx @@ -6,7 +6,7 @@ import { DeepAgentsShowcase } from '../components/landing/DeepAgentsShowcase'; import { StatsStrip } from '../components/landing/StatsStrip'; // import { SocialProof } from '../components/landing/SocialProof'; import { ProblemSection } from '../components/landing/ProblemSection'; -import { FullStackSection } from '../components/landing/FullStackSection'; +import { LibrariesSection } from '../components/landing/LibrariesSection'; import { ChatFeaturesSection } from '../components/landing/ChatFeaturesSection'; import { FairComparisonSection } from '../components/landing/FairComparisonSection'; import { WhitePaperSection } from '../components/landing/WhitePaperSection'; @@ -32,8 +32,8 @@ export default async function HomePage() { {/* */} {/* 3. Problem — last-mile gap narrative */} - {/* 4. Architecture — three-layer stack diagram */} - + {/* 4. Libraries — teaser cards for Angular, Render, Chat */} + {/* 5. Chat features — interactive 4-tab scenarios */} {/* 6. Value — why this product, with interactive code tabs */} diff --git a/apps/website/src/components/landing/LibrariesSection.tsx b/apps/website/src/components/landing/LibrariesSection.tsx new file mode 100644 index 000000000..3c06b0dc0 --- /dev/null +++ b/apps/website/src/components/landing/LibrariesSection.tsx @@ -0,0 +1,151 @@ +'use client'; +import { motion } from 'framer-motion'; +import Link from 'next/link'; +import { tokens } from '@cacheplane/design-tokens'; + +const LIBRARIES = [ + { + id: 'angular', + tag: 'Agent', + pkg: '@cacheplane/angular', + color: tokens.colors.accent, + rgb: '0,64,144', + oneLiner: 'Signal-native streaming for LangGraph agents', + chips: ['agent()', 'provideAgent()', 'interrupt()', 'MockStreamTransport'], + href: '/angular', + ctaLabel: 'Explore Angular', + }, + { + id: 'render', + tag: 'Gen UI', + pkg: '@cacheplane/render', + color: '#1a7a40', + rgb: '26,122,64', + oneLiner: 'Agents that render UI — without coupling to your frontend', + chips: ['', 'defineAngularRegistry()', 'signalStateStore()', 'JSON patch'], + href: '/render', + ctaLabel: 'Explore Render', + }, + { + id: 'chat', + tag: 'Chat', + pkg: '@cacheplane/chat', + color: '#5a00c8', + rgb: '90,0,200', + oneLiner: 'Batteries-included agent chat — fully featured from day one', + chips: ['', '', '', ''], + href: '/chat', + ctaLabel: 'Explore Chat', + }, +]; + +export function LibrariesSection() { + return ( +
+ +

+ The Cacheplane Stack +

+

+ Three libraries. One architecture. +

+

+ Everything your Angular team needs to ship AI agents to production. +

+
+ +
+ {LIBRARIES.map((lib, i) => ( + + + {lib.tag} + + +

+ {lib.pkg} +

+ +

+ {lib.oneLiner} +

+ +
+ {lib.chips.map(chip => ( + + {chip} + + ))} +
+ + + {lib.ctaLabel} → + +
+ ))} +
+
+ ); +} From 0a41e5d585815fb704f04f55fc4610ca5e1fed96 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Thu, 9 Apr 2026 15:23:48 -0700 Subject: [PATCH 02/12] feat(website): add Libraries column to footer for SEO/bot discovery Co-Authored-By: Claude Sonnet 4.6 --- apps/website/src/components/shared/Footer.tsx | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/apps/website/src/components/shared/Footer.tsx b/apps/website/src/components/shared/Footer.tsx index c7c1d8930..6e608444f 100644 --- a/apps/website/src/components/shared/Footer.tsx +++ b/apps/website/src/components/shared/Footer.tsx @@ -100,7 +100,7 @@ export function Footer() { transition={{ duration: 0.5 }}> {/* Top section: brand + columns */} -
+
{/* Brand */}

🛩️ Angular Agent Framework

@@ -165,6 +165,26 @@ export function Footer() {
+ {/* Libraries column */} +
+ Libraries + (e.currentTarget.style.color = tokens.colors.accent)} + onMouseLeave={(e) => (e.currentTarget.style.color = tokens.colors.textSecondary)}> + Angular + + (e.currentTarget.style.color = tokens.colors.accent)} + onMouseLeave={(e) => (e.currentTarget.style.color = tokens.colors.textSecondary)}> + Render + + (e.currentTarget.style.color = tokens.colors.accent)} + onMouseLeave={(e) => (e.currentTarget.style.color = tokens.colors.textSecondary)}> + Chat + +
+ {/* Resources column */}
Resources From f7e1d70310c0293896e0f7b77d998fa46163b2f3 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Thu, 9 Apr 2026 15:25:32 -0700 Subject: [PATCH 03/12] feat(website): merge DocsMobileNav into header for unified mobile navigation Co-Authored-By: Claude Sonnet 4.6 --- .../docs/[library]/[section]/[slug]/page.tsx | 6 +- .../src/components/docs/DocsMobileNav.tsx | 89 ------------------- apps/website/src/components/shared/Nav.tsx | 75 +++++++++++++++- 3 files changed, 75 insertions(+), 95 deletions(-) delete mode 100644 apps/website/src/components/docs/DocsMobileNav.tsx diff --git a/apps/website/src/app/docs/[library]/[section]/[slug]/page.tsx b/apps/website/src/app/docs/[library]/[section]/[slug]/page.tsx index f59a87063..03ee749ca 100644 --- a/apps/website/src/app/docs/[library]/[section]/[slug]/page.tsx +++ b/apps/website/src/app/docs/[library]/[section]/[slug]/page.tsx @@ -5,7 +5,6 @@ import { DocsSearch } from '../../../../../components/docs/DocsSearch'; import { getDocBySlug, getAllDocSlugs } from '../../../../../lib/docs'; import { ApiDocRenderer, type ApiDocEntry } from '../../../../../components/docs/ApiDocRenderer'; import { DocsTOC } from '../../../../../components/docs/DocsTOC'; -import { DocsMobileNav } from '../../../../../components/docs/DocsMobileNav'; import { extractHeadings } from '../../../../../lib/extract-headings'; import { getLibraryConfig, type LibraryId } from '../../../../../lib/docs-config'; import fs from 'fs'; @@ -50,10 +49,7 @@ export default async function DocsPage({ params }: { params: Promise<{ library:
-
- -
- + {section === 'api' && (() => { const entries = loadApiDocs(library); const nameMap = API_NAME_MAP[library] ?? {}; diff --git a/apps/website/src/components/docs/DocsMobileNav.tsx b/apps/website/src/components/docs/DocsMobileNav.tsx deleted file mode 100644 index 580107945..000000000 --- a/apps/website/src/components/docs/DocsMobileNav.tsx +++ /dev/null @@ -1,89 +0,0 @@ -'use client'; -import { useState } from 'react'; -import Link from 'next/link'; -import { useRouter } from 'next/navigation'; -import { docsConfig, getLibraryConfig, type LibraryId } from '../../lib/docs-config'; -import { tokens } from '@cacheplane/design-tokens'; - -export function DocsMobileNav({ activeLibrary, activeSection, activeSlug }: { activeLibrary: LibraryId; activeSection: string; activeSlug: string }) { - const [open, setOpen] = useState(false); - const router = useRouter(); - const libConfig = getLibraryConfig(activeLibrary); - - return ( -
- - - {open && ( - - )} -
- ); -} diff --git a/apps/website/src/components/shared/Nav.tsx b/apps/website/src/components/shared/Nav.tsx index 341ec9f0b..f5a91bb35 100644 --- a/apps/website/src/components/shared/Nav.tsx +++ b/apps/website/src/components/shared/Nav.tsx @@ -1,7 +1,9 @@ 'use client'; import { useState } from 'react'; import Link from 'next/link'; +import { usePathname } from 'next/navigation'; import { tokens } from '@cacheplane/design-tokens'; +import { docsConfig } from '../../lib/docs-config'; const links = [ { label: 'Pilot to Prod', href: '/pilot-to-prod', external: false }, @@ -37,6 +39,21 @@ function CloseIcon() { export function Nav() { const [open, setOpen] = useState(false); + const pathname = usePathname(); + const isDocsPage = pathname.startsWith('/docs'); + const pathParts = pathname.split('/').filter(Boolean); // ['docs', library, section, slug] + const activeLibrary = isDocsPage && pathParts.length >= 2 ? pathParts[1] : ''; + const activeSection = isDocsPage && pathParts.length >= 3 ? pathParts[2] : ''; + const activeSlug = isDocsPage && pathParts.length >= 4 ? pathParts[3] : ''; + const [openSections, setOpenSections] = useState>(() => new Set(activeSection ? [activeSection] : [])); + + const toggleSection = (id: string) => { + setOpenSections(prev => { + const next = new Set(prev); + if (next.has(id)) next.delete(id); else next.add(id); + return next; + }); + }; return (
+ {isDocsPage && ( + <> +
+ + Documentation + + {docsConfig.filter(lib => lib.id === activeLibrary || !activeLibrary).map((lib) => ( +
+ + {lib.title} + + {lib.sections.map((section) => ( +
+ + {openSections.has(section.id) && section.pages.map((page) => { + const isActive = page.section === activeSection && page.slug === activeSlug; + return ( + setOpen(false)} + className="block pl-6 py-1 text-sm" + style={{ + color: isActive ? tokens.colors.accent : tokens.colors.textSecondary, + background: isActive ? tokens.colors.accentSurface : 'transparent', + borderRadius: 4, + }} + > + {page.title} + + ); + })} +
+ ))} +
+ ))} + + )}
)} From 63daf39e8e424122782dace3b2ecf0a019437773 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Thu, 9 Apr 2026 15:30:28 -0700 Subject: [PATCH 04/12] feat(website): add /angular landing page with full conversion funnel Co-Authored-By: Claude Opus 4.6 --- apps/website/src/app/angular/page.tsx | 31 ++++ .../landing/angular/AngularCodeShowcase.tsx | 92 ++++++++++++ .../landing/angular/AngularComparison.tsx | 100 +++++++++++++ .../landing/angular/AngularFeaturesGrid.tsx | 85 +++++++++++ .../landing/angular/AngularFooterCTA.tsx | 60 ++++++++ .../landing/angular/AngularHero.tsx | 77 ++++++++++ .../angular/AngularProblemSolution.tsx | 90 ++++++++++++ .../landing/angular/AngularWhitePaperGate.tsx | 137 ++++++++++++++++++ 8 files changed, 672 insertions(+) create mode 100644 apps/website/src/app/angular/page.tsx create mode 100644 apps/website/src/components/landing/angular/AngularCodeShowcase.tsx create mode 100644 apps/website/src/components/landing/angular/AngularComparison.tsx create mode 100644 apps/website/src/components/landing/angular/AngularFeaturesGrid.tsx create mode 100644 apps/website/src/components/landing/angular/AngularFooterCTA.tsx create mode 100644 apps/website/src/components/landing/angular/AngularHero.tsx create mode 100644 apps/website/src/components/landing/angular/AngularProblemSolution.tsx create mode 100644 apps/website/src/components/landing/angular/AngularWhitePaperGate.tsx diff --git a/apps/website/src/app/angular/page.tsx b/apps/website/src/app/angular/page.tsx new file mode 100644 index 000000000..161c12124 --- /dev/null +++ b/apps/website/src/app/angular/page.tsx @@ -0,0 +1,31 @@ +// apps/website/src/app/angular/page.tsx +import { AngularHero } from '../../components/landing/angular/AngularHero'; +import { AngularProblemSolution } from '../../components/landing/angular/AngularProblemSolution'; +import { AngularFeaturesGrid } from '../../components/landing/angular/AngularFeaturesGrid'; +import { AngularCodeShowcase } from '../../components/landing/angular/AngularCodeShowcase'; +import { AngularComparison } from '../../components/landing/angular/AngularComparison'; +import { AngularWhitePaperGate } from '../../components/landing/angular/AngularWhitePaperGate'; +import { AngularFooterCTA } from '../../components/landing/angular/AngularFooterCTA'; +import { tokens } from '@cacheplane/design-tokens'; + +export const metadata = { + title: '@cacheplane/angular — Agent Streaming for Angular', + description: 'Ship LangGraph agents in Angular. Signal-native streaming, thread persistence, interrupts, and deterministic testing.', +}; + +export default function AngularPage() { + return ( +
+