Skip to content
27 changes: 15 additions & 12 deletions bun.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
"@vercel/speed-insights": "^1.2.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"framer-motion": "^12.23.16",
"lucide-react": "^0.542.0",
"motion": "^12.23.12",
"next": "15.5.2",
"next": "^15.5.3",
"next-themes": "^0.4.6",
"ogl": "^1.0.11",
"react": "19.1.0",
Expand Down Expand Up @@ -120,23 +121,23 @@

"@jridgewell/trace-mapping": ["@jridgewell/[email protected]", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q=="],

"@next/env": ["@next/[email protected].2", "", {}, "sha512-Qe06ew4zt12LeO6N7j8/nULSOe3fMXE4dM6xgpBQNvdzyK1sv5y4oAP3bq4LamrvGCZtmRYnW8URFCeX5nFgGg=="],
"@next/env": ["@next/[email protected].3", "", {}, "sha512-RSEDTRqyihYXygx/OJXwvVupfr9m04+0vH8vyy0HfZ7keRto6VX9BbEk0J2PUk0VGy6YhklJUSrgForov5F9pw=="],

"@next/swc-darwin-arm64": ["@next/[email protected].2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-8bGt577BXGSd4iqFygmzIfTYizHb0LGWqH+qgIF/2EDxS5JsSdERJKA8WgwDyNBZgTIIA4D8qUtoQHmxIIquoQ=="],
"@next/swc-darwin-arm64": ["@next/[email protected].3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-nzbHQo69+au9wJkGKTU9lP7PXv0d1J5ljFpvb+LnEomLtSbJkbZyEs6sbF3plQmiOB2l9OBtN2tNSvCH1nQ9Jg=="],

"@next/swc-darwin-x64": ["@next/[email protected].2", "", { "os": "darwin", "cpu": "x64" }, "sha512-2DjnmR6JHK4X+dgTXt5/sOCu/7yPtqpYt8s8hLkHFK3MGkka2snTv3yRMdHvuRtJVkPwCGsvBSwmoQCHatauFQ=="],
"@next/swc-darwin-x64": ["@next/[email protected].3", "", { "os": "darwin", "cpu": "x64" }, "sha512-w83w4SkOOhekJOcA5HBvHyGzgV1W/XvOfpkrxIse4uPWhYTTRwtGEM4v/jiXwNSJvfRvah0H8/uTLBKRXlef8g=="],

"@next/swc-linux-arm64-gnu": ["@next/[email protected].2", "", { "os": "linux", "cpu": "arm64" }, "sha512-3j7SWDBS2Wov/L9q0mFJtEvQ5miIqfO4l7d2m9Mo06ddsgUK8gWfHGgbjdFlCp2Ek7MmMQZSxpGFqcC8zGh2AA=="],
"@next/swc-linux-arm64-gnu": ["@next/[email protected].3", "", { "os": "linux", "cpu": "arm64" }, "sha512-+m7pfIs0/yvgVu26ieaKrifV8C8yiLe7jVp9SpcIzg7XmyyNE7toC1fy5IOQozmr6kWl/JONC51osih2RyoXRw=="],

"@next/swc-linux-arm64-musl": ["@next/[email protected].2", "", { "os": "linux", "cpu": "arm64" }, "sha512-s6N8k8dF9YGc5T01UPQ08yxsK6fUow5gG1/axWc1HVVBYQBgOjca4oUZF7s4p+kwhkB1bDSGR8QznWrFZ/Rt5g=="],
"@next/swc-linux-arm64-musl": ["@next/[email protected].3", "", { "os": "linux", "cpu": "arm64" }, "sha512-u3PEIzuguSenoZviZJahNLgCexGFhso5mxWCrrIMdvpZn6lkME5vc/ADZG8UUk5K1uWRy4hqSFECrON6UKQBbQ=="],

"@next/swc-linux-x64-gnu": ["@next/[email protected].2", "", { "os": "linux", "cpu": "x64" }, "sha512-o1RV/KOODQh6dM6ZRJGZbc+MOAHww33Vbs5JC9Mp1gDk8cpEO+cYC/l7rweiEalkSm5/1WGa4zY7xrNwObN4+Q=="],
"@next/swc-linux-x64-gnu": ["@next/[email protected].3", "", { "os": "linux", "cpu": "x64" }, "sha512-lDtOOScYDZxI2BENN9m0pfVPJDSuUkAD1YXSvlJF0DKwZt0WlA7T7o3wrcEr4Q+iHYGzEaVuZcsIbCps4K27sA=="],

"@next/swc-linux-x64-musl": ["@next/[email protected].2", "", { "os": "linux", "cpu": "x64" }, "sha512-/VUnh7w8RElYZ0IV83nUcP/J4KJ6LLYliiBIri3p3aW2giF+PAVgZb6mk8jbQSB3WlTai8gEmCAr7kptFa1H6g=="],
"@next/swc-linux-x64-musl": ["@next/[email protected].3", "", { "os": "linux", "cpu": "x64" }, "sha512-9vWVUnsx9PrY2NwdVRJ4dUURAQ8Su0sLRPqcCCxtX5zIQUBES12eRVHq6b70bbfaVaxIDGJN2afHui0eDm+cLg=="],

"@next/swc-win32-arm64-msvc": ["@next/[email protected].2", "", { "os": "win32", "cpu": "arm64" }, "sha512-sMPyTvRcNKXseNQ/7qRfVRLa0VhR0esmQ29DD6pqvG71+JdVnESJaHPA8t7bc67KD5spP3+DOCNLhqlEI2ZgQg=="],
"@next/swc-win32-arm64-msvc": ["@next/[email protected].3", "", { "os": "win32", "cpu": "arm64" }, "sha512-1CU20FZzY9LFQigRi6jM45oJMU3KziA5/sSG+dXeVaTm661snQP6xu3ykGxxwU5sLG3sh14teO/IOEPVsQMRfA=="],

"@next/swc-win32-x64-msvc": ["@next/[email protected].2", "", { "os": "win32", "cpu": "x64" }, "sha512-W5VvyZHnxG/2ukhZF/9Ikdra5fdNftxI6ybeVKYvBPDtyx7x4jPPSNduUkfH5fo3zG0JQ0bPxgy41af2JX5D4Q=="],
"@next/swc-win32-x64-msvc": ["@next/[email protected].3", "", { "os": "win32", "cpu": "x64" }, "sha512-JMoLAq3n3y5tKXPQwCK5c+6tmwkuFDa2XAxz8Wm4+IVthdBZdZGh+lmiLUHg9f9IDwIQpUjp+ysd6OkYTyZRZw=="],

"@radix-ui/react-compose-refs": ["@radix-ui/[email protected]", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],

Expand Down Expand Up @@ -272,7 +273,7 @@

"fast-deep-equal": ["[email protected]", "", {}, "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w=="],

"framer-motion": ["[email protected].12", "", { "dependencies": { "motion-dom": "^12.23.12", "motion-utils": "^12.23.6", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg=="],
"framer-motion": ["[email protected].16", "", { "dependencies": { "motion-dom": "^12.23.12", "motion-utils": "^12.23.6", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-N81A8hiHqVsexOzI3wzkibyLURW1nEJsZaRuctPhG4AdbbciYu+bKJq9I2lQFzAO4Bx3h4swI6pBbF/Hu7f7BA=="],

"graceful-fs": ["[email protected]", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],

Expand Down Expand Up @@ -330,7 +331,7 @@

"nanoid": ["[email protected]", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],

"next": ["[email protected]", "", { "dependencies": { "@next/env": "15.5.2", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.5.2", "@next/swc-darwin-x64": "15.5.2", "@next/swc-linux-arm64-gnu": "15.5.2", "@next/swc-linux-arm64-musl": "15.5.2", "@next/swc-linux-x64-gnu": "15.5.2", "@next/swc-linux-x64-musl": "15.5.2", "@next/swc-win32-arm64-msvc": "15.5.2", "@next/swc-win32-x64-msvc": "15.5.2", "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-H8Otr7abj1glFhbGnvUt3gz++0AF1+QoCXEBmd/6aKbfdFwrn0LpA836Ed5+00va/7HQSDD+mOoVhn3tNy3e/Q=="],
"next": ["[email protected]", "", { "dependencies": { "@next/env": "15.5.3", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.5.3", "@next/swc-darwin-x64": "15.5.3", "@next/swc-linux-arm64-gnu": "15.5.3", "@next/swc-linux-arm64-musl": "15.5.3", "@next/swc-linux-x64-gnu": "15.5.3", "@next/swc-linux-x64-musl": "15.5.3", "@next/swc-win32-arm64-msvc": "15.5.3", "@next/swc-win32-x64-msvc": "15.5.3", "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-r/liNAx16SQj4D+XH/oI1dlpv9tdKJ6cONYPwwcCC46f2NjpaRWY+EKCzULfgQYV6YKXjHBchff2IZBSlZmJNw=="],

"next-themes": ["[email protected]", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="],

Expand Down Expand Up @@ -410,6 +411,8 @@

"@tailwindcss/oxide-wasm32-wasi/tslib": ["[email protected]", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],

"motion/framer-motion": ["[email protected]", "", { "dependencies": { "motion-dom": "^12.23.12", "motion-utils": "^12.23.6", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg=="],

"next/postcss": ["[email protected]", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
}
}
8 changes: 8 additions & 0 deletions next.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'avatars.githubusercontent.com',
},
],
},
async redirects() {
return [
{
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@
"@vercel/speed-insights": "^1.2.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"framer-motion": "^12.23.16",
"lucide-react": "^0.542.0",
"motion": "^12.23.12",
"next": "15.5.2",
"next": "^15.5.3",
"next-themes": "^0.4.6",
"ogl": "^1.0.11",
"react": "19.1.0",
Expand Down
26 changes: 26 additions & 0 deletions src/actions/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,32 @@ export async function getRepoStars(owner: string, repo: string) {
return data.stargazers_count as number;
}

export async function getRepoContributors(owner: string, repo: string) {
const res = await fetch(
`https://api.github.com/repos/${owner}/${repo}/contributors`,
{
headers: {
Accept: "application/vnd.github.v3+json",
"User-Agent": "ora-app",
},
next: { revalidate: 300 }, // cache for 5 minutes
},
);

if (!res.ok) {
throw new Error(`failed to fetch contributors: ${res.status}`);
}

const data = await res.json();

return data.map((contributor: any) => ({
name: contributor.login,
avatarUrl: contributor.avatar_url,
contributions: contributor.contributions,
profileUrl: contributor.html_url,
}));
}

export async function getLatestReleaseDmgUrl(owner: string, repo: string) {
const res = await fetch(
`https://api.github.com/repos/${owner}/${repo}/releases/latest`,
Expand Down
8 changes: 7 additions & 1 deletion src/actions/waitlist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ export async function addToWaitlist(email: string, ip: string) {
}

export async function getWaitlistCount() {
const count = await redis.scard("ora:waitlist");
// const count = await redis.scard("ora:waitlist");
let count: number;
try {
count = await redis.scard("ora:waitlist");
} catch {
count = 69
}
return count;
}

Expand Down
22 changes: 22 additions & 0 deletions src/components/contributors-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { getRepoContributors } from "@/actions/github";
import AvatarGroup from "./ui/avatar-group";

export async function ContributorsList() {
const contributors = await getRepoContributors("the-ora", "browser");

// Transform contributors to match AvatarGroup format
const avatarItems = contributors.map((contributor: any, index: number) => ({
id: index + 1,
name: contributor.name,
designation: `${contributor.contributions} contributions`,
image: contributor.avatarUrl,
}));

return (
<AvatarGroup
items={avatarItems}
maxVisible={5}
size="md"
/>
);
}
8 changes: 6 additions & 2 deletions src/components/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { PRESENTATION, SOCIALITEMS } from "@/data/presentation";
import { Logo } from "./logo";
import { BuyMeACoffeeBadge } from "./buymecoffe-badge";
import { Button } from "./ui/button";
import { ContributorsList } from "./contributors-list";

export function Footer() {
return (
Expand Down Expand Up @@ -29,8 +30,11 @@ export function Footer() {
))}
</div>
</div>
<div className="self-center py-2 px-3 bg-primary h-fit border hover:bg-primary/96 sm:self-start transition-transform duration-150 will-change-transform hover:scale-[1.02] active:scale-95">
<BuyMeACoffeeBadge className="size-20 sm:size-24" />
<div className="flex flex-col gap-4 ">
<div className="ml-auto self-center py-2 px-3 bg-primary h-fit border hover:bg-primary/96 sm:self-start transition-transform duration-150 will-change-transform hover:scale-[1.02] active:scale-95">
<BuyMeACoffeeBadge className="size-20 sm:size-24" />
</div>
<ContributorsList />
</div>
</div>
<div className="border-t">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Star } from "lucide-react";
import { ArrowRight } from "./animate-ui/icons/arrow-right";
import { formatNumber } from "@/lib/utils";

export async function GithubStarButton() {
export async function GithubStarsButton() {
const stars = await getRepoStars("the-ora", "browser");
return (
<Link href={PRESENTATION.urls.github} target="_blank">
Expand Down
74 changes: 56 additions & 18 deletions src/components/hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,46 +10,84 @@ import { getWaitlistCount } from "@/actions/waitlist";
import { getRepoStars } from "@/actions/github";
import { formatNumber } from "@/lib/utils";
import { Button } from "./ui/button";
import { GithubStarButton } from "./github-starts-button";
import { GithubStarsButton } from "./github-stars-button";
import { DownloadAlphaButton } from "./download-alpha-button";
import Image from "next/image";
import { AnimatedGroup } from "./ui/animated-group";

const transitionVariants = {
item: {
hidden: {
opacity: 0,
filter: 'blur(12px)',
y: 12,
},
visible: {
opacity: 1,
filter: 'blur(0px)',
y: 0,
transition: {
type: 'spring' as const,
bounce: 0.3,
duration: 1.5,
},
},
},
}

export async function Hero() {
const waitlistCount = await getWaitlistCount();

return (
<div className="h-screen overflow-clip">
<HeroBackground/>
<HeroBackground />
<main className="absolute inset-0 z-10 flex flex-col items-center gap-8 px-4 sm:gap-12 md:gap-16">
<div className="h-24"/>
<GithubStarButton/>
<div className="flex flex-col items-center gap-8">
<div className="h-24" />
<AnimatedGroup variants={transitionVariants}>
<GithubStarsButton />
</AnimatedGroup>
<AnimatedGroup variants={transitionVariants} className="flex flex-col items-center gap-8">
<h1 className="z-10 w-full max-w-[42rem] text-center font-medium text-4xl md:text-5xl">
{PRESENTATION.hero.title}
</h1>
<p className="w-full max-w-xl text-center text-base text-muted-foreground leading-6 px-4">
{PRESENTATION.hero.description}
</p>
</div>
<div className="flex flex-col gap-8 w-full max-w-md px-4">
</AnimatedGroup>
<AnimatedGroup
variants={{
container: {
visible: {
transition: {
staggerChildren: 0.05,
delayChildren: 0.75,
},
},
},
...transitionVariants,
}}
className="flex flex-col gap-8 w-full max-w-md px-4"
>
<WaitlistForm />
<div className="flex items-center gap-2 self-center">
<div className="size-2 animate-pulse rounded-full bg-green-400 shadow-2xl shadow-green-400" />
<p className="text-muted-foreground text-sm">
<span className="font-medium text-primary">
{formatNumber(waitlistCount)}{" "}
</span>
people have joined the waitlist for beta
</p>
<div className="flex items-center justify-center">
<div className="flex items-center gap-2 self-center">
<div className="size-2 animate-pulse rounded-full bg-green-400 shadow-2xl shadow-green-400" />
<p className="text-muted-foreground text-sm">
<span className="font-medium text-primary">
{formatNumber(waitlistCount)}{" "}
</span>
people have joined the waitlist for beta
</p>
</div>
</div>
</div>
<div className="hidden sm:block xl:w-7xl h-fit sm:h-[36rem] overflow-hidden p-2 border rounded-t-2xl blur-in-2xl bg-white/10 justify-center">
</AnimatedGroup>
<div className="hidden sm:block xl:w-7xl h-fit sm:h-[36rem] overflow-hidden p-2 border rounded-t-2xl blur-in-2xl bg-white/10 justify-center backdrop-blur-md">
<Image
src="/browser-3.png"
alt="Mockup"
width={1536}
height={973}
// className="w-full h-full"
// className="w-full h-full"
/>
</div>
</main>
Expand Down
Loading