Update homepage messaging and carousel performance

This commit is contained in:
Mohamed Boudra
2026-06-01 16:30:17 +07:00
parent d406c29e16
commit a807eb0eb2
2 changed files with 235 additions and 9 deletions
@@ -90,6 +90,8 @@ export function LandingPage({ title, subtitle }: LandingPageProps) {
<SocialProofWall /> <SocialProofWall />
<MultiProviderSection /> <MultiProviderSection />
<SelfHostedSection /> <SelfHostedSection />
<WorkflowSection />
<SplitPanelsSection />
<ServiceProxySection /> <ServiceProxySection />
<ShortcutsSection /> <ShortcutsSection />
<LocalVoiceSection /> <LocalVoiceSection />
@@ -361,8 +363,8 @@ function MultiProviderSection() {
return ( return (
<FeatureSection <FeatureSection
title="Use the best agent for the job" title="Works with your tools"
description="Run multiple providers from a single interface. Paseo runs the native agent harness as you'd normally run it, with your skills, config and MCP servers intact." description="Run your agents from one interface. Paseo uses each provider's native harness, so your subscriptions, skills, config, and MCP servers keep working."
> >
<div className="grid grid-cols-1 gap-4 sm:grid-cols-3"> <div className="grid grid-cols-1 gap-4 sm:grid-cols-3">
{providers.map((p) => ( {providers.map((p) => (
@@ -661,14 +663,212 @@ function SelfHostedDiagram() {
function SelfHostedSection() { function SelfHostedSection() {
return ( return (
<FeatureSection <FeatureSection
title="Your agents, every surface" title="Runs where you work"
description="Run agents on your laptop, a VM, or a dev server. Control them from any device with a direct connection or an E2E encrypted relay." description="Start agents on your laptop, a VM, or a dev server. Use them from any device over a direct connection or the end-to-end encrypted relay."
> >
<SelfHostedDiagram /> <SelfHostedDiagram />
</FeatureSection> </FeatureSection>
); );
} }
const WORKFLOW_STEPS = ["Worktree", "Preview", "Review", "Commit", "PR", "Merge"] as const;
const REVIEW_FILES = [
{ path: "src/auth/session.ts", delta: "+42" },
{ path: "src/auth/middleware.ts", delta: "+18 -9" },
{ path: "tests/auth.test.ts", delta: "+31" },
] as const;
function WorkflowSection() {
return (
<FeatureSection
title="Review, preview, ship"
description="Create branches, preview the app in the browser, review the diff inline, then commit, open a PR, and merge without leaving Paseo."
>
<div className="overflow-hidden rounded-2xl border border-white/10 bg-white/[0.02]">
<WorkflowHeader />
<div className="grid gap-4 p-4 md:grid-cols-[1.1fr_0.9fr]">
<WorkflowPreview />
<WorkflowReviewAndShip />
</div>
</div>
</FeatureSection>
);
}
function WorkflowHeader() {
return (
<div className="flex flex-col gap-3 border-b border-white/10 px-5 py-4 sm:flex-row sm:items-center sm:justify-between">
<div className="flex items-center gap-2.5">
<div className="h-2 w-2 rounded-full bg-emerald-400" />
<span className="text-sm text-white/80">fix-auth</span>
<span className="rounded-full bg-white/10 px-2 py-0.5 text-xs text-white/40">worktree</span>
</div>
<div className="flex flex-wrap items-center gap-2 text-xs text-white/40">
{WORKFLOW_STEPS.map((step) => (
<span key={step} className="rounded-full border border-white/10 px-2 py-1">
{step}
</span>
))}
</div>
</div>
);
}
function WorkflowPreview() {
return (
<div className="overflow-hidden rounded-xl border border-white/10 bg-black/20">
<BrowserChrome />
<div className="space-y-5 p-5">
<PreviewHeader />
<div className="grid gap-3 sm:grid-cols-2">
<PreviewFormCard titleWidth="w-16" ctaClassName="bg-white/[0.06]" />
<PreviewFormCard titleWidth="w-20" ctaClassName="bg-emerald-400/20" />
</div>
</div>
</div>
);
}
function BrowserChrome() {
return (
<div className="flex items-center gap-2 border-b border-white/10 bg-white/[0.03] px-3 py-2">
<div className="flex gap-1.5">
<span className="h-2.5 w-2.5 rounded-full bg-red-400/60" />
<span className="h-2.5 w-2.5 rounded-full bg-amber-300/60" />
<span className="h-2.5 w-2.5 rounded-full bg-emerald-400/60" />
</div>
<div className="min-w-0 flex-1 rounded-md bg-black/30 px-2 py-1 text-center font-mono text-[10px] text-white/35">
web.fix-auth.my-app.localhost
</div>
</div>
);
}
function PreviewHeader() {
return (
<div className="space-y-2">
<div className="h-3 w-28 rounded-full bg-white/25" />
<div className="h-2 w-44 rounded-full bg-white/10" />
</div>
);
}
function PreviewFormCard({
titleWidth,
ctaClassName,
}: {
titleWidth: string;
ctaClassName: string;
}) {
return (
<div className="space-y-3 rounded-lg border border-white/10 bg-white/[0.03] p-4">
<div className={`h-2 rounded-full bg-white/15 ${titleWidth}`} />
<div className="h-8 rounded-md bg-white/10" />
<div className={`h-8 rounded-md ${ctaClassName}`} />
</div>
);
}
function WorkflowReviewAndShip() {
return (
<div className="space-y-4">
<InlineReviewPanel />
<ShipPanel />
</div>
);
}
function InlineReviewPanel() {
return (
<div className="rounded-xl border border-white/10 bg-white/[0.03] p-4">
<div className="mb-3 flex items-center justify-between gap-3">
<span className="text-sm text-white/80">Inline review</span>
<span className="text-xs text-white/35">3 files changed</span>
</div>
<div className="space-y-2">
{REVIEW_FILES.map((file) => (
<ReviewFileRow key={file.path} path={file.path} delta={file.delta} />
))}
</div>
</div>
);
}
function ReviewFileRow({ path, delta }: { path: string; delta: string }) {
return (
<div className="flex items-center justify-between gap-3 text-xs">
<span className="truncate font-mono text-white/50">{path}</span>
<span className="flex gap-1 font-mono">
{delta.split(" ").map((part) => (
<span
key={part}
className={part.startsWith("-") ? "text-red-300/70" : "text-emerald-300/70"}
>
{part}
</span>
))}
</span>
</div>
);
}
function ShipPanel() {
return (
<div className="rounded-xl border border-white/10 bg-white/[0.03] p-4">
<div className="mb-4 flex items-center justify-between gap-3">
<span className="text-sm text-white/80">Ready to ship</span>
<span className="rounded-full bg-emerald-400/10 px-2 py-1 text-xs text-emerald-300">
checks passed
</span>
</div>
<div className="grid grid-cols-3 gap-2 text-center text-xs">
<div className="rounded-lg border border-white/10 bg-white/10 px-3 py-2 text-white/70">
Commit
</div>
<div className="rounded-lg border border-white/10 bg-white/10 px-3 py-2 text-white/70">
Open PR
</div>
<div className="rounded-lg border border-emerald-400/20 bg-emerald-400/15 px-3 py-2 text-emerald-200">
Merge
</div>
</div>
</div>
);
}
function SplitPanelsSection() {
return (
<FeatureSection
title="Split panels"
description="Open agents, browsers, terminals, diffs, and logs in the same workspace. Split them side by side or group them in tabs."
>
<div className="rounded-2xl border border-white/10 bg-white/[0.02] p-3">
<div className="grid gap-3 md:h-[360px] md:grid-cols-[1.05fr_0.95fr]">
<PanelTile label="Agent" className="min-h-48 md:min-h-0" />
<div className="grid gap-3 md:grid-rows-[1fr_0.75fr]">
<PanelTile label="Browser" className="min-h-36" />
<div className="grid gap-3 sm:grid-cols-2">
<PanelTile label="Terminal" className="min-h-28" />
<PanelTile label="Diff" className="min-h-28" />
</div>
</div>
</div>
</div>
</FeatureSection>
);
}
function PanelTile({ label, className }: { label: string; className: string }) {
return (
<div
className={`flex items-center justify-center rounded-xl border border-white/10 bg-white/[0.03] text-sm text-white/70 ${className}`}
>
{label}
</div>
);
}
function ServiceProxySection() { function ServiceProxySection() {
const workspaces = [ const workspaces = [
{ name: "fix-auth", url: "web.fix-auth.my-app.localhost" }, { name: "fix-auth", url: "web.fix-auth.my-app.localhost" },
@@ -957,7 +1157,7 @@ function LocalVoiceSection() {
return ( return (
<FeatureSection <FeatureSection
title="Local voice" title="Voice control, fully local"
description="Fully local voice stack. Speech-to-text and text-to-speech run entirely on your machine, nothing leaves your network." description="Fully local voice stack. Speech-to-text and text-to-speech run entirely on your machine, nothing leaves your network."
> >
<div className="relative w-full rounded-2xl border border-white/10 bg-white/[0.02] overflow-hidden"> <div className="relative w-full rounded-2xl border border-white/10 bg-white/[0.02] overflow-hidden">
@@ -1709,10 +1909,14 @@ function SponsorCTA() {
> >
<div className="text-sm text-muted-foreground leading-relaxed space-y-3"> <div className="text-sm text-muted-foreground leading-relaxed space-y-3">
<p> <p>
I built Paseo because I wanted better tools for coding agents on my own setup. It&apos;s Paseo is an independent open source project for running coding agents across your own
an independent open source project, built around freedom of choice and real workflows. If machines, phone, desktop, and CLI.
you like what I&apos;m building, consider becoming a supporter.
</p> </p>
<p>
It&apos;s built around freedom of choice: use the provider you want, run it on your own
infrastructure, and keep your workflow portable.
</p>
<p>If you like Paseo, sponsorship is the best way to support continued development.</p>
<p>- Mo</p> <p>- Mo</p>
</div> </div>
<div className="pt-2"> <div className="pt-2">
+23 -1
View File
@@ -20,9 +20,30 @@
.social-proof-marquee { .social-proof-marquee {
margin-inline: calc(50% - 50vw); margin-inline: calc(50% - 50vw);
mask-image: linear-gradient(to right, transparent, black 8%, black 92%, transparent);
overflow-x: clip; overflow-x: clip;
padding-inline: max(1.5rem, calc((100vw - 80rem) / 2)); padding-inline: max(1.5rem, calc((100vw - 80rem) / 2));
position: relative;
}
.social-proof-marquee::before,
.social-proof-marquee::after {
content: "";
position: absolute;
top: 0;
bottom: 0;
width: max(4rem, 8vw);
z-index: 1;
pointer-events: none;
}
.social-proof-marquee::before {
left: 0;
background: linear-gradient(to right, var(--color-background), transparent);
}
.social-proof-marquee::after {
right: 0;
background: linear-gradient(to left, var(--color-background), transparent);
} }
.social-proof-row { .social-proof-row {
@@ -34,6 +55,7 @@
display: flex; display: flex;
width: max-content; width: max-content;
animation: social-proof-scroll 46s linear infinite; animation: social-proof-scroll 46s linear infinite;
will-change: transform;
} }
.social-proof-track-reverse { .social-proof-track-reverse {