Skip to content

Commit 9d904e9

Browse files
committed
feat: reconcile landing home preview
1 parent 632ad00 commit 9d904e9

10 files changed

Lines changed: 327 additions & 26 deletions

File tree

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
name: Deploy Landing Preview
2+
3+
on:
4+
pull_request:
5+
types:
6+
- opened
7+
- reopened
8+
- synchronize
9+
paths:
10+
- "landing/**"
11+
- ".github/actions/setup-node-pnpm/action.yml"
12+
- ".github/workflows/landing-preview.yml"
13+
14+
concurrency:
15+
group: landing-preview-${{ github.event.pull_request.number }}
16+
cancel-in-progress: true
17+
18+
jobs:
19+
deploy-preview:
20+
if: github.event.pull_request.head.repo.full_name == github.repository
21+
runs-on: ubuntu-latest
22+
permissions:
23+
contents: read
24+
pull-requests: write
25+
env:
26+
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
27+
steps:
28+
- name: Checkout
29+
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
30+
31+
- name: Setup Node and pnpm
32+
uses: ./.github/actions/setup-node-pnpm
33+
with:
34+
install-deps: "false"
35+
36+
- name: Verify Vercel token
37+
run: |
38+
if [ -z "$VERCEL_TOKEN" ]; then
39+
echo "VERCEL_TOKEN secret is required for landing previews."
40+
exit 1
41+
fi
42+
43+
- name: Install landing dependencies
44+
working-directory: landing
45+
run: pnpm install --frozen-lockfile
46+
47+
- name: Pull Vercel preview environment
48+
working-directory: landing
49+
run: pnpm dlx vercel@latest pull --yes --environment=preview --token="$VERCEL_TOKEN"
50+
51+
- name: Build landing preview
52+
working-directory: landing
53+
run: pnpm dlx vercel@latest build --token="$VERCEL_TOKEN"
54+
55+
- name: Deploy landing preview
56+
id: deploy
57+
working-directory: landing
58+
run: |
59+
url="$(pnpm dlx vercel@latest deploy --prebuilt --token="$VERCEL_TOKEN")"
60+
echo "url=$url" >> "$GITHUB_OUTPUT"
61+
62+
- name: Comment preview URL
63+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
64+
env:
65+
PREVIEW_URL: ${{ steps.deploy.outputs.url }}
66+
with:
67+
script: |
68+
const marker = "<!-- agent-device-landing-vercel-preview -->";
69+
const body = `${marker}
70+
### Landing preview
71+
72+
Vercel preview: ${process.env.PREVIEW_URL}`;
73+
const { owner, repo } = context.repo;
74+
const issue_number = context.issue.number;
75+
const comments = await github.paginate(github.rest.issues.listComments, {
76+
owner,
77+
repo,
78+
issue_number,
79+
per_page: 100,
80+
});
81+
const existing = comments.find((comment) => comment.body?.includes(marker));
82+
83+
if (existing) {
84+
await github.rest.issues.updateComment({
85+
owner,
86+
repo,
87+
comment_id: existing.id,
88+
body,
89+
});
90+
} else {
91+
await github.rest.issues.createComment({
92+
owner,
93+
repo,
94+
issue_number,
95+
body,
96+
});
97+
}

landing/README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,63 @@ pnpm build
2121

2222
The Figma source file is treated as read-only. Local visual assets under `public/figma/` were exported from Figma screenshots so the site does not depend on short-lived Figma asset URLs.
2323

24+
## Figma Source Map
25+
26+
Source file: `Agent Device`, page `Website`, file key `Z6g76cuusJlN9s5agbZRpY`.
27+
28+
Use section-level screenshots and metadata instead of full-page exports. Full-page images are too large and make visual review harder. A current local audit lives in `/private/tmp/agent-device-figma-audit/`.
29+
30+
### Home
31+
32+
| Section | Figma node |
33+
| --- | --- |
34+
| Hero | `39:2482` |
35+
| Nav | `40:6100` |
36+
| Solutions | `39:2501` |
37+
| Lanes | `40:3750` |
38+
| Setup | `40:5567` |
39+
| Cloud/Open | `40:5761` |
40+
| Why | `40:5833` |
41+
| Up-sell | `364:2753` |
42+
| CTA | `40:6008` |
43+
| Insights | `234:2608` |
44+
| Footer | `40:6484` |
45+
46+
### Agentic QA
47+
48+
| Section | Figma node |
49+
| --- | --- |
50+
| Hero | `488:2260` |
51+
| Nav | `488:2353` |
52+
| Solutions | `488:2367` |
53+
| Why | `489:6121` |
54+
| Setup | `488:3121` |
55+
| Solutions | `498:8028` |
56+
| Solutions | `498:8303` |
57+
| CTA | `488:4642` |
58+
| Up-sell | `488:4584` |
59+
| FAQ | `489:7808` |
60+
| Insights | `488:4648` |
61+
| Footer | `488:4695` |
62+
63+
### Agentic Development
64+
65+
| Section | Figma node |
66+
| --- | --- |
67+
| Hero | `498:8567` |
68+
| Nav | `498:8583` |
69+
| Solutions | `498:8597` |
70+
| Why | `498:8627` |
71+
| Setup | `498:8645` |
72+
| Solutions | `498:8652` |
73+
| Setup | `510:2992` |
74+
| Cloud/Open | `510:4422` |
75+
| CTA | `498:8698` |
76+
| Up-sell | `498:8704` |
77+
| FAQ | `498:8762` |
78+
| Insights | `498:8778` |
79+
| Footer | `498:8825` |
80+
2481
## Deploy on Vercel
2582

2683
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { DarkNoisePanel } from "@/components/dark-noise-panel";
2+
import { PixelIcon, type PixelIconName } from "@/components/pixel-icon";
3+
import { ButtonLink } from "@/components/ui/button";
4+
import { DisplayHeading, Eyebrow, LeadText } from "@/components/ui/typography";
5+
6+
const platformCards: Array<{
7+
icon: PixelIconName;
8+
title: string;
9+
body: string;
10+
}> = [
11+
{
12+
icon: "terminal",
13+
title: "Anywhere agents run",
14+
body: "Drive local simulators from CLI, CI, and remote workspaces with the same agent-device commands.",
15+
},
16+
{
17+
icon: "devices",
18+
title: "Devices on demand",
19+
body: "Map iOS and Android sessions into the agent loop without changing how teams work.",
20+
},
21+
{
22+
icon: "cloud",
23+
title: "Parallel at scale",
24+
body: "Run the same verification workflow across platforms, builds, and device states.",
25+
},
26+
{
27+
icon: "snapshot",
28+
title: "Evidence at scale",
29+
body: "Screenshots, recordings, logs, and accessibility snapshots stay attached to each run.",
30+
},
31+
];
32+
33+
export function CloudOpenSection() {
34+
return (
35+
<section className="bg-white px-5 sm:px-8 lg:px-16">
36+
<DarkNoisePanel className="mx-auto min-h-[735px] max-w-[1312px] px-6 py-16 text-center sm:px-10 lg:h-[735px] lg:min-h-0 lg:px-16">
37+
<div className="mx-auto max-w-[820px]">
38+
<Eyebrow tone="light">Agent Device Cloud</Eyebrow>
39+
<div className="mx-auto mt-8 grid max-w-[280px] grid-cols-4 gap-2 text-white/70">
40+
{["terminal", "github", "devices", "cloud"].map((icon) => (
41+
<span
42+
className="flex aspect-square items-center justify-center rounded-[4px] border border-white/10 bg-white/[0.03]"
43+
key={icon}
44+
>
45+
<PixelIcon name={icon as PixelIconName} className="size-5" />
46+
</span>
47+
))}
48+
</div>
49+
<div className="mx-auto mt-4 flex size-20 items-center justify-center rounded-[4px] border border-[#8232ff]/40 bg-[#8232ff]/10 text-[#9d6dff]">
50+
<PixelIcon name="cloud" className="size-7" />
51+
</div>
52+
<DisplayHeading as="h2" size="section" tone="light" className="mt-9">
53+
Mobile execution wherever your agents live.
54+
</DisplayHeading>
55+
<LeadText tone="light" className="mx-auto mt-3 max-w-[600px] text-sm">
56+
Agent Device runs from any agent machine. Cloud lifts iOS and Android
57+
into the same loop when local hardware is not available.
58+
</LeadText>
59+
</div>
60+
61+
<div className="mt-12 grid gap-px overflow-hidden rounded-[4px] border border-white/10 bg-white/10 text-left md:grid-cols-4">
62+
{platformCards.map((card) => (
63+
<article className="bg-black/60 p-5" key={card.title}>
64+
<PixelIcon name={card.icon} className="size-5 text-[#9d6dff]" />
65+
<h3 className="mt-8 text-sm font-medium leading-[1.35] text-white">
66+
{card.title}
67+
</h3>
68+
<p className="mt-3 text-xs leading-[1.5] text-white/45">{card.body}</p>
69+
</article>
70+
))}
71+
</div>
72+
73+
<div className="mt-8 flex flex-col items-center justify-center gap-3 sm:flex-row">
74+
<ButtonLink
75+
href="https://github.com/callstackincubator/agent-device"
76+
size="compact"
77+
variant="primary"
78+
>
79+
Agent Device Cloud
80+
</ButtonLink>
81+
<ButtonLink
82+
href="https://www.callstack.com/contact?message=%22I%20want%20to%20chat%20about%20agent-device%22"
83+
size="compact"
84+
variant="secondary"
85+
>
86+
Get a Demo
87+
</ButtonLink>
88+
</div>
89+
</DarkNoisePanel>
90+
</section>
91+
);
92+
}

landing/src/components/home/footer.tsx

Lines changed: 63 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,75 @@
11
import Link from "next/link";
22

3-
const links = ["Agentic QA", "Agentic Development", "GitHub"];
3+
const footerGroups = [
4+
{
5+
title: "Agent Device",
6+
links: [
7+
{ href: "https://github.com/callstackincubator/agent-device", label: "GitHub" },
8+
{ href: "/agentic-qa", label: "Agentic QA" },
9+
{ href: "/agentic-development", label: "Agentic Development" },
10+
],
11+
},
12+
{
13+
title: "Documentation",
14+
links: [
15+
{ href: "https://github.com/callstackincubator/agent-device", label: "Documentation" },
16+
{ href: "https://github.com/callstackincubator/agent-device", label: "GitHub" },
17+
],
18+
},
19+
{
20+
title: "Callstack",
21+
links: [
22+
{ href: "https://www.callstack.com/open-source", label: "Open Source" },
23+
{ href: "https://www.callstack.com/services", label: "Services" },
24+
{ href: "https://www.callstack.com/case-studies", label: "Case Studies" },
25+
{ href: "https://www.callstack.com/technologies/react-native", label: "Technology" },
26+
],
27+
},
28+
{
29+
title: "Social",
30+
links: [
31+
{ href: "https://x.com/callstackio", label: "X" },
32+
{ href: "https://www.youtube.com/@callstackengineers", label: "YouTube" },
33+
{ href: "https://www.linkedin.com/company/callstackio", label: "LinkedIn" },
34+
{ href: "https://www.instagram.com/callstackio", label: "Instagram" },
35+
{ href: "https://github.com/callstack", label: "GitHub" },
36+
],
37+
},
38+
] as const;
439

540
export function Footer() {
641
return (
7-
<footer className="border-t border-black/10 bg-white px-5 py-16 sm:px-8 lg:px-16">
8-
<div className="mx-auto flex max-w-[1312px] flex-col justify-between gap-10 md:flex-row">
42+
<footer className="border-t border-black/10 bg-white px-5 py-20 sm:px-8 lg:px-16 lg:py-[100px]">
43+
<div className="mx-auto grid max-w-[1312px] gap-14 md:grid-cols-[1fr_2fr] lg:min-h-[130px]">
944
<div>
10-
<Link href="/" className="font-display text-2xl font-semibold text-black">
11-
Agent Device
45+
<Link href="https://www.callstack.com" className="font-display text-2xl font-semibold text-black">
46+
(callstack)
1247
</Link>
13-
<p className="mt-3 max-w-[420px] leading-[1.5] text-black/50">
14-
Mobile app automation for agents, built by Callstack.
48+
<p className="mt-4 max-w-[220px] text-sm font-medium leading-[1.45] text-black/50">
49+
Mobile by Callstack.
50+
<br />
51+
AI Native Engineering.
52+
<br />
53+
Made for React Native.
1554
</p>
1655
</div>
17-
<nav className="flex flex-wrap gap-6 text-sm font-medium text-black/60">
18-
{links.map((link) => (
19-
<Link
20-
href={link === "GitHub" ? "https://github.com/callstackincubator/agent-device" : `/${link.toLowerCase().replaceAll(" ", "-")}`}
21-
key={link}
22-
>
23-
{link}
24-
</Link>
56+
<nav
57+
aria-label="Footer navigation"
58+
className="grid gap-10 text-sm font-medium text-black/45 sm:grid-cols-2 lg:grid-cols-4 lg:gap-12"
59+
>
60+
{footerGroups.map((group) => (
61+
<div key={group.title}>
62+
<p className="font-medium text-black">{group.title}</p>
63+
<ul className="mt-4 grid gap-2">
64+
{group.links.map((link) => (
65+
<li key={`${group.title}-${link.label}`}>
66+
<Link className="transition hover:text-black" href={link.href}>
67+
{link.label}
68+
</Link>
69+
</li>
70+
))}
71+
</ul>
72+
</div>
2573
))}
2674
</nav>
2775
</div>

landing/src/components/home/home-page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { CloudOpenSection } from "@/components/home/cloud-open-section";
12
import { CtaSection } from "@/components/home/cta-section";
23
import { Footer } from "@/components/home/footer";
34
import { Hero } from "@/components/home/hero";
@@ -15,6 +16,7 @@ export function HomePage() {
1516
<Solutions />
1617
<ToolkitLanes />
1718
<UseCases />
19+
<CloudOpenSection />
1820
<WhyCallstack />
1921
<StackSupportSection />
2022
<CtaSection />

0 commit comments

Comments
 (0)