Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion www/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pnpm test:unit
| `app/` | Routes. A page's body is its `page.tsx`; files only that route uses sit beside it. The home page's sections are in `app/(home)/_home/` |
| `features/<name>/` | Code more than one route uses: downloads, license, announcements, analytics, docs, desktop telemetry |
| `components/ui/` | shadcn primitives and `cn` |
| `components/site/` | The site's shared blocks: nav, footer, shell, FAQ, buttons, logos, JSON-LD |
| `components/site/` | The site's shared blocks: nav, footer, FAQ, buttons, logos, JSON-LD |
| `content/` | MDX: `docs/`, `blog/`, `changelog/`, `announcements/` |

New code starts beside the route that uses it and moves to `features/` when a second route needs it. `sitemap.ts` reads each route's data directly. Tests sit beside their code as `*.test.ts`.
Expand Down
8 changes: 4 additions & 4 deletions www/app/(home)/_home/home-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,15 +190,15 @@ export const FEATURES: {
{
key: "plugins",
heading: "Open source, extended by plugins",
body: "Each plugin gives the agent live data from one platform. Eight ship today.",
body: "Each plugin gives the agent live data from one platform. Ten ship today.",
points: [
{
title: "More places to search",
body: "Google Search, YouTube, TikTok, Google Maps, Amazon, Walmart, Indeed and the open web.",
body: "Google Search, Reddit, YouTube, Instagram, TikTok, Google Maps, Amazon, Walmart, Indeed and the open web.",
},
{
title: "Open by pull request",
body: "Our plugins are code in the public repo; anyone can list an MCP server in the registry.",
title: "Open to everyone",
body: "Every plugin is open source, and anyone can add their own MCP server to the registry.",
},
],
action: { label: "See the plugins", href: "/plugins" },
Expand Down
11 changes: 8 additions & 3 deletions www/app/(home)/_home/home-scene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import heroScene from "@/app/(home)/_home/home-hero-scene.webp";
* positioned parent.
*
* `next/image` serves AVIF or WebP at the width each screen needs, from the
* 3296px master, and inlines a tiny blurred preview so the scene shows before
* the image arrives.
* 3296px master. Until it arrives, a gradient of the scene's own sky and ground
* stands in, so white copy stays readable from the first paint. No
* `placeholder="blur"`: its SVG blur filter is costly to paint at full width.
*
* `sizes` is the width the scene is drawn at, not the box width: the 1.73:1 image
* covers its box, so in a tall box it renders 1.73 times the box height wide.
Expand All @@ -22,14 +23,18 @@ const FILM_GRAIN = `url("data:image/svg+xml,${encodeURIComponent(GRAIN_SVG)}")`;
export function HomeScene({ sizes, priority = false }: { sizes: string; priority?: boolean }) {
return (
<>
{/* Averages of the scene's bands, sampled from the master: sky, haze, ground. */}
<div
aria-hidden
className="absolute inset-0 bg-linear-to-b from-[#21a0ff] via-[#41abf7] via-55% to-[#9fb2a5]"
/>
<Image
src={heroScene}
alt=""
fill
priority={priority}
sizes={sizes}
quality={85}
placeholder="blur"
draggable={false}
className="object-cover object-center select-none"
/>
Expand Down
2 changes: 1 addition & 1 deletion www/app/(home)/announcements/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { formatAnnouncementDate } from "../format-announcement-date";
/**
* One announcement, laid out like a blog post. The header and image come from
* `features/announcements/announcements-data.ts`, the text from its MDX body.
* Site design via the `/announcements/` prefix in `components/site/site-shell.tsx`.
* Site design from `app/(home)/layout.tsx`.
*/

const BASE_URL = "https://www.surfsense.com";
Expand Down
5 changes: 2 additions & 3 deletions www/app/(home)/announcements/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ import { AnnouncementCard } from "./announcement-card";
import { FeaturedAnnouncement } from "./featured-announcement";

/**
* Rendered in the site design: listed in `SITE_DESIGN_ROUTES` in
* `components/site/site-shell.tsx`, so the palette, column, navigation and
* footer come from the site shell. Laid out like the blog index: the newest
* Rendered in the site design: the palette, column, navigation and footer come
* from `app/(home)/layout.tsx`. Laid out like the blog index: the newest
* announcement as a wide feature, the rest as cards, each linking to its page.
*
* Every published announcement is listed, expired or not and whatever its
Expand Down
4 changes: 1 addition & 3 deletions www/app/(home)/contact/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ import {
*
* Rendered in the site design: the palette, ruled column, navigation and footer
* all come from `app/(home)/layout.tsx`, and every style on this page resolves
* from `app/(home)/home.css`. It is listed in `SITE_DESIGN_ROUTES` in
* `components/site/site-shell.tsx`, which is what puts it inside the ruled
* column rather than on the older gradient ground.
* from `app/(home)/home.css`.
*
* A server component with no client JavaScript. Every element on the page is a
* link.
Expand Down
3 changes: 1 addition & 2 deletions www/app/(home)/downloads/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import { AllReleasesLink, OSDownloadGrid } from "./download-panels";
/**
* Rendered in the site design: the palette, column, navigation and footer all
* come from `app/(home)/layout.tsx`; the page uses the homepage's centred heads
* and rounded cards. Listed in `SITE_DESIGN_ROUTES` in
* `components/site/site-shell.tsx`.
* and rounded cards.
*/

export const metadata: Metadata = {
Expand Down
5 changes: 2 additions & 3 deletions www/app/(home)/free/[model_slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,8 @@ import { HomeDownloadButton } from "@/features/downloads/home-download-button";
* false, so none of it survived. What replaced it says only what the desktop
* app does: no account, your own key or a local model, files on your disk.
*
* Rendered in the site design via the `/free/` prefix in `SITE_DESIGN_PREFIXES`
* (`components/site/site-shell.tsx`). A server component, no client JavaScript
* beyond the hero button.
* Rendered in the site design from `app/(home)/layout.tsx`. A server component,
* no client JavaScript beyond the hero button.
*/

interface PageProps {
Expand Down
3 changes: 1 addition & 2 deletions www/app/(home)/free/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import { LinkSquare02Icon } from "@/components/ui/icons";
/**
* Rendered in the site design: the palette, ruled column, navigation and footer
* all come from `app/(home)/layout.tsx`; the page uses Tailwind plus the shared
* `siteText` styles. Listed in `SITE_DESIGN_ROUTES` in
* `components/site/site-shell.tsx`.
* `siteText` styles.
*
* The copy, metadata, keyword set and structured data are the ones this page
* already ranks on and are left alone.
Expand Down
32 changes: 21 additions & 11 deletions www/app/(home)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Fraunces } from "next/font/google";
import { getStarCount, STARS_HREF } from "@/components/site/github-stars";
import { SiteShell } from "@/components/site/site-shell";
import { SiteFooter } from "@/components/site/site-footer";
import { SiteNav } from "@/components/site/site-nav";
import { SiteSmoothScroll } from "@/components/site/site-smooth-scroll";
import "./home.css";

// Brand wordmark only; loaded here so only the marketing pages fetch it.
Expand All @@ -12,24 +14,32 @@ const fraunces = Fraunces({
});

/**
* Layout for every marketing route.
* Layout for every marketing route: the `.ss-home` palette, the nav, the ruled
* column and the footer.
*
* A server component so it can await the star count, which is cached in Next's
* Data Cache and therefore fetched at most once an hour rather than once per
* visitor. The route branching that needs `usePathname` lives in `SiteShell`,
* the client half.
* Nothing here branches on the path: a `usePathname` check once served the
* cached homepage without `.ss-home`, so it painted unstyled until hydration.
*
* The fetch is cached, so it does not opt these routes into dynamic rendering:
* they stay static and are revalidated on the same hourly schedule.
* The star count is cached in Next's Data Cache, fetched at most once an hour,
* so these routes stay static.
*/
export default async function HomePageLayout({ children }: { children: React.ReactNode }) {
const starCount = await getStarCount();

return (
<div className={`${fraunces.variable} contents`}>
<SiteShell starCount={starCount} starsHref={STARS_HREF}>
{children}
</SiteShell>
<main className="ss-home min-h-screen">
{/* Outside the page slot, so React keeps the nav mounted across navigations. */}
<SiteNav starCount={starCount} starsHref={STARS_HREF} />
<SiteSmoothScroll />

{/* Full width, so backgrounds can bleed; `.ss-home-page` (home.css) holds
each section to the column instead, and the footer caps its own content. */}
<div className="ss-home-page">
{children}
<SiteFooter />
</div>
</main>
</div>
);
}
3 changes: 1 addition & 2 deletions www/app/(home)/license/activate/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import { cn } from "@/components/ui/cn";
/**
* Rendered in the site design: the palette, column, navigation and footer all
* come from `app/(home)/layout.tsx`; the page uses the homepage's centred heads,
* laid out like `/pricing/individual`, which shares `GuideSteps`. Listed in
* `SITE_DESIGN_ROUTES` in `components/site/site-shell.tsx`.
* laid out like `/pricing/individual`, which shares `GuideSteps`.
*
* The one place the activation steps live. All three license emails link here
* (`app/license/email/message.py`), as do `/license` and `/license/success`,
Expand Down
3 changes: 1 addition & 2 deletions www/app/(home)/license/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import { ResendForm } from "@/features/license/license-forms";
/**
* Rendered in the site design: the palette, column, navigation and footer all
* come from `app/(home)/layout.tsx`; the page uses the homepage's centred heads
* and the same email pill as `/downloads`. Listed in `SITE_DESIGN_ROUTES` in
* `components/site/site-shell.tsx`.
* and the same email pill as `/downloads`.
*
* This page does one thing: send a licence file back to the address it was
* issued to. Without accounts, that file is the only thing a customer holds,
Expand Down
3 changes: 1 addition & 2 deletions www/app/(home)/plugins/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import { registryPlugins } from "./registry-plugins";
/**
* Plugins page: every plugin SurfSense lists, as a browsable gallery.
*
* Rendered in the site design from `app/(home)/layout.tsx`; listed in
* `SITE_DESIGN_ROUTES` in `components/site/site-shell.tsx`. Replaces the old
* Rendered in the site design from `app/(home)/layout.tsx`. Replaces the old
* `/connectors` index, which redirects here from `next.config.ts`. Other publishers'
* plugins are read from the registry container when the page is rendered.
*/
Expand Down
2 changes: 1 addition & 1 deletion www/app/(home)/pricing/individual/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
* and the Stripe Payment Link, which needs no account. Its job is that nobody
* reaches Stripe unsure what they are buying.
*
* Rendered in the site design (listed in `SITE_DESIGN_ROUTES`). Copy and its
* Rendered in the site design from `app/(home)/layout.tsx`. Copy and its
* sources are in `individual-content.ts`.
*/

Expand Down
5 changes: 2 additions & 3 deletions www/app/(home)/privacy/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ import { siteText } from "@/components/site/site-text";
* Privacy Policy. Plain long-form prose, laid out like a blog post: the
* padded `max-w-3xl` + Tailwind `prose` wrapper is copied from
* `app/(home)/blog/[slug]/page.tsx` so a legal page reads like body text
* rather than like a landing page. Listed in `SITE_DESIGN_ROUTES` in
* `components/site/site-shell.tsx`, which supplies the light palette, the
* navigation and the footer.
* rather than like a landing page. `app/(home)/layout.tsx` supplies the light
* palette, the navigation and the footer.
*
* Every factual claim here was checked against the code, not against the
* previous policy or the marketing copy. The version this replaced described
Expand Down
5 changes: 2 additions & 3 deletions www/app/(home)/terms/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import { siteText } from "@/components/site/site-text";
/**
* Terms of Service. Plain long-form prose, laid out like a blog post: the
* padded `max-w-3xl` + Tailwind `prose` wrapper is copied from
* `app/(home)/blog/[slug]/page.tsx`. Listed in `SITE_DESIGN_ROUTES` in
* `components/site/site-shell.tsx`, which supplies the light palette, the
* navigation and the footer.
* `app/(home)/blog/[slug]/page.tsx`. `app/(home)/layout.tsx` supplies the light
* palette, the navigation and the footer.
*
* Substantive changes from the version this replaces, all because the product
* changed shape:
Expand Down
Binary file modified www/components/site/site-footer-scene.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 8 additions & 7 deletions www/components/site/site-footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import { cn } from "@/components/ui/cn";
/**
* Site footer: the site's links set straight on the page's ground, which fades
* into an engraved Golden Gate Bridge that fills the open band below.
* Rendered by `app/(home)/layout.tsx` for every route under `(home)` except the
* auth pages.
* Rendered by `app/(home)/layout.tsx` for every route under `(home)`.
*/

const LINK_CLASS =
Expand All @@ -20,21 +19,23 @@ const WORDMARK_CLASS =

export function SiteFooter() {
return (
<footer className="relative isolate overflow-hidden">
// `content-visibility` skips the footer's layout and paint until it nears the screen.
<footer className="relative isolate overflow-hidden [contain-intrinsic-size:auto_900px] [content-visibility:auto]">
{/* Sized by width and anchored to the bottom, so the bridge stays in the open band
however tall the links make the footer. Covering the footer instead would
blow the 3:1 scene up well past its 4344px source and blur the linework.
blow the 3:1 scene up well past its source and blur the linework.
From lg the whole panorama shows; below it the scene is wider than the screen
and the left offsets keep the main tower, a third of the way in, on screen. */}
and the left offsets keep the main tower, a third of the way in, on screen.
The source is 2172px, not 4344px: a quarter of the pixels to decode, and its
darker lines hold up when stretched on retina screens. */}
<div aria-hidden="true" className="pointer-events-none absolute inset-0 -z-10">
<div className="absolute bottom-0 -left-[60%] aspect-[4344/1448] w-[360%] sm:-left-[15%] sm:w-[220%] lg:left-0 lg:w-full">
<div className="absolute bottom-0 -left-[60%] aspect-[2172/724] w-[360%] sm:-left-[15%] sm:w-[220%] lg:left-0 lg:w-full">
<Image
src={footerScene}
alt=""
fill
sizes="(max-width: 639px) 360vw, (max-width: 1023px) 220vw, 100vw"
quality={85}
placeholder="blur"
className="object-cover object-bottom select-none"
/>
<div className="absolute inset-x-0 top-0 h-[45%] bg-linear-to-b from-background via-background/80 to-transparent" />
Expand Down
117 changes: 0 additions & 117 deletions www/components/site/site-shell.tsx

This file was deleted.

Loading