mirror of
https://github.com/privacy-scaling-explorations/pse.dev.git
synced 2026-01-08 21:58:05 -05:00
2
.github/workflows/claude.yml
vendored
2
.github/workflows/claude.yml
vendored
@@ -49,4 +49,4 @@ jobs:
|
||||
uses: anthropics/claude-code-action@beta
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
timeout_minutes: "60"
|
||||
timeout_minutes: "60"
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { AppContent } from "@/components/ui/app-content"
|
||||
|
||||
import { Metadata } from "next"
|
||||
import { HomepageBanner } from "@/components/sections/HomepageBanner"
|
||||
import { Divider } from "@/components/divider"
|
||||
import { AppLink } from "@/components/app-link"
|
||||
import { HomepageBanner } from "@/components/sections/HomepageBanner"
|
||||
import { AppContent } from "@/components/ui/app-content"
|
||||
import { Metadata } from "next"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "About",
|
||||
@@ -11,7 +9,7 @@ export const metadata: Metadata = {
|
||||
}
|
||||
|
||||
export default async function AboutPage() {
|
||||
const OurWorkItems = [
|
||||
const WhatWeDoItems = [
|
||||
{
|
||||
label: "Incubation",
|
||||
description:
|
||||
@@ -30,96 +28,128 @@ export default async function AboutPage() {
|
||||
]
|
||||
|
||||
return (
|
||||
<Divider.Section>
|
||||
<div className="w-full bg-page-header-gradient dark:bg-transparent-gradient">
|
||||
<AppContent className="flex flex-col gap-16 py-16 w-full max-w-[978px] mx-auto">
|
||||
<div className="flex flex-col gap-10">
|
||||
<h2 className="font-sans text-base font-bold uppercase tracking-[4px] text-black dark:text-white text-center">
|
||||
Our Mission
|
||||
</h2>
|
||||
<span className="text-xl font-sans dark:text-tuatara-200 text-black lg:max-w-[730px] mx-auto">
|
||||
As Privacy Stewards of Ethereum (PSE), our mission is to deliver
|
||||
privacy to the{" "}
|
||||
<AppLink href="https://ethereum.org/en/" external>
|
||||
Ethereum ecosystem
|
||||
</AppLink>
|
||||
. We envision a future where privacy on Ethereum is the norm
|
||||
rather than the exception.
|
||||
<br /> <br />
|
||||
Building{" "}
|
||||
<AppLink
|
||||
href="https://www.activism.net/cypherpunk/manifesto.html"
|
||||
external
|
||||
>
|
||||
“an open society in the electronic age”
|
||||
</AppLink>{" "}
|
||||
{
|
||||
"requires privacy that is usable, scalable, and secure. We are a team of applied cryptographers, mathematicians, developers, designers, and coordinators working to embed programmable cryptography into Ethereum's application layer and make privacy accessible to individuals, developers, and institutions."
|
||||
}{" "}
|
||||
<br /> <br />
|
||||
Privacy is a cornerstone of freedom, safety, and is an{" "}
|
||||
<AppLink
|
||||
href="https://vitalik.eth.limo/general/2025/04/14/privacy.html"
|
||||
external
|
||||
>
|
||||
important guarantor for decentralization.
|
||||
</AppLink>{" "}
|
||||
{`We're building a future where digital infrastructure respects
|
||||
privacy by default, and permissions are purpose-specific,
|
||||
informed, uncoerced, and revocable.`}
|
||||
<br /> <br />
|
||||
{`Programmable cryptography unlocks transformative capabilities for
|
||||
digital commerce, identity, governance, and other systems of
|
||||
coordination. But the road to privacy isn't only technical. It
|
||||
requires shifts in user behavior, developer priorities, regulatory
|
||||
frameworks, and cultural norms. This is a collective challenge and
|
||||
we`}{" "}
|
||||
<AppLink href="https://discord.com/invite/sF5CT5rzrR" external>
|
||||
invite you
|
||||
</AppLink>{" "}
|
||||
to help us shape a more free digital future.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-10">
|
||||
<h2 className="font-sans text-base font-bold uppercase tracking-[4px] text-black dark:text-white text-center">
|
||||
Our Work
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 gap-10 lg:grid-cols-3 max-auto">
|
||||
{OurWorkItems?.map((item, index) => (
|
||||
<div className="flex flex-col gap-6 w-full lg:max-w-[300px]">
|
||||
<article className="flex flex-col gap-2" key={index}>
|
||||
<h6 className="font-sans text-xl font-medium text-black dark:text-white">
|
||||
{item.label}
|
||||
</h6>
|
||||
<p className="font-sans text-base font-normal text-black dark:text-white">
|
||||
{item.description}
|
||||
</p>
|
||||
</article>
|
||||
</div>
|
||||
))}
|
||||
<>
|
||||
<div className="w-full bg-white dark:bg-transparent-gradient">
|
||||
<div className="flex flex-col gap-16 py-16">
|
||||
<AppContent className=" w-full !max-w-[860px] mx-auto">
|
||||
<div className="flex flex-col gap-16">
|
||||
<div className="flex flex-col gap-10">
|
||||
<h2 className="font-sans text-base font-bold uppercase tracking-[4px] text-black dark:text-white text-center">
|
||||
Our Mission
|
||||
</h2>
|
||||
<span className="text-base lg:text-xl font-sans dark:text-tuatara-200 text-tuatara-500 mx-auto">
|
||||
As Privacy Stewards of Ethereum (PSE), our mission is to
|
||||
deliver privacy to the Ethereum ecosystem. <br />
|
||||
<br />
|
||||
Ethereum is becoming core infrastructure for digital identity,
|
||||
commerce, and collaboration. But building{" "}
|
||||
<AppLink
|
||||
external
|
||||
variant="blue"
|
||||
href="https://www.activism.net/cypherpunk/manifesto.html"
|
||||
>
|
||||
an open society in the electronic age
|
||||
</AppLink>{" "}
|
||||
requires privacy that is usable, scalable, and secure.
|
||||
<br />
|
||||
<br />
|
||||
Privacy is a cornerstone of freedom and an{" "}
|
||||
<AppLink
|
||||
href="https://vitalik.eth.limo/general/2025/04/14/privacy.html"
|
||||
external
|
||||
variant="blue"
|
||||
className="w-fit inline-flex"
|
||||
>
|
||||
important guarantor for decentralization.
|
||||
</AppLink>{" "}
|
||||
It gives people the ability to organize, express, and transact
|
||||
freely. We envision a future where digital infrastructure
|
||||
respects privacy by default, and permissions are
|
||||
purpose-specific, informed, uncoerced, and revocable.
|
||||
<br /> <br />
|
||||
{`Programmable cryptography unlocks transformative capabilities
|
||||
for digital voting, identity, transactions, and other key
|
||||
systems of coordination. But the road to privacy isn't only
|
||||
technical—it demands cultural, social, and institutional change.
|
||||
This is a collective challenge that we are committed to
|
||||
catalyzing.`}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col gap-10">
|
||||
<h2 className="font-sans text-base font-bold uppercase tracking-[4px] text-black dark:text-white text-center">
|
||||
How we Work
|
||||
</h2>
|
||||
<span className="text-base lg:text-xl font-sans dark:text-tuatara-200 text-tuatara-500 mx-auto">
|
||||
{`We are a team of cryptographers, mathematicians, developers,
|
||||
designers, and coordinators working to make privacy accessible
|
||||
by embedding programmable cryptography into Ethereum's
|
||||
application layer. `}
|
||||
<br />
|
||||
<br />
|
||||
Our work is exploratory and full stack. We focus on areas that
|
||||
are overlooked by academia or industry like foundational
|
||||
concepts that need clarity, implementation gaps that have
|
||||
stalled progress, and risky ideas with uncertain but
|
||||
transformative <br />
|
||||
<br />
|
||||
We believe meaningful progress comes from applying research to
|
||||
real-world needs. Our work is open source, and we maintain a
|
||||
feedback loop so that challenges from the field can help shape
|
||||
our focus. We aim to support the ecosystem by benchmarking new
|
||||
primitives, systematizing knowledge, and offering credible
|
||||
guidances for building a more private and resilient future for
|
||||
Ethereum.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</AppContent>
|
||||
|
||||
<div className="bg-cover-gradient dark:bg-anakiwa-975 dark:bg-none py-16 ">
|
||||
<AppContent className=" w-full !max-w-[860px] mx-auto">
|
||||
<div className="flex flex-col gap-10">
|
||||
<h2 className="font-sans text-base font-bold uppercase tracking-[4px] text-black dark:text-white text-center">
|
||||
What we do
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 gap-10 lg:grid-cols-3 max-auto">
|
||||
{WhatWeDoItems?.map((item, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex flex-col gap-6 w-full lg:max-w-[400px] p-5 rounded-[10px] bg-white border border-anakiwa-300 dark:border-anakiwa-400 dark:bg-anakiwa-975"
|
||||
>
|
||||
<article className="flex flex-col gap-2">
|
||||
<h6 className="font-sans text-xl font-medium text-tuatara-950 dark:text-anakiwa-400">
|
||||
{item.label}
|
||||
</h6>
|
||||
<p className="font-sans text-base font-normal text-tuatara-500 dark:text-tuatara-100">
|
||||
{item.description}
|
||||
</p>
|
||||
</article>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</AppContent>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-10">
|
||||
<h2 className="font-sans text-base font-bold uppercase tracking-[4px] text-black dark:text-white text-center">
|
||||
Our History
|
||||
</h2>
|
||||
<span className="text-xl font-sans dark:text-tuatara-200 text-black lg:max-w-[730px] mx-auto">
|
||||
We began in 2018 as Applied ZKP, a team supported by the{" "}
|
||||
<AppLink href="https://ethereum.foundation/" external>
|
||||
Ethereum Foundation
|
||||
</AppLink>{" "}
|
||||
to push zero-knowledge proofs from theory to practice. In 2021, we
|
||||
became Privacy & Scaling Explorations (PSE), expanding our scope
|
||||
to programmable cryptography and tools across the stack. In 2025,
|
||||
we refined our mission as Privacy Stewards for Ethereum, shifting
|
||||
our focus toward concrete ecosystem impact.
|
||||
</span>
|
||||
</div>
|
||||
</AppContent>
|
||||
<AppContent className="w-full !max-w-[860px] mx-auto">
|
||||
<div className="flex flex-col gap-10">
|
||||
<h2 className="font-sans text-base font-bold uppercase tracking-[4px] text-black dark:text-white text-center">
|
||||
Our History
|
||||
</h2>
|
||||
<span className="text-base lg:text-xl font-sans dark:text-tuatara-200 text-tuatara-500 mx-auto">
|
||||
We began in 2018 as Applied ZKP, a team supported by the
|
||||
Ethereum Foundation to push zero-knowledge proofs from theory to
|
||||
practice. In 2021, we became Privacy & Scaling Explorations
|
||||
(PSE), expanding our scope to programmable cryptography and
|
||||
tools across the stack. In 2025, we refined our mission as
|
||||
Privacy Stewards for Ethereum, shifting our focus toward
|
||||
ecosystem impact.
|
||||
</span>
|
||||
</div>
|
||||
</AppContent>
|
||||
</div>
|
||||
</div>
|
||||
<HomepageBanner />
|
||||
</Divider.Section>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@ import { LABELS } from "../labels"
|
||||
import { BlogRecentArticles } from "@/components/blog/blog-recent-articles"
|
||||
import { Icons } from "@/components/icons"
|
||||
import { HomepageBanner } from "@/components/sections/HomepageBanner"
|
||||
import { HomepageHeader } from "@/components/sections/HomepageHeader"
|
||||
import { HomepageVideoFeed } from "@/components/sections/HomepageVideoFeed"
|
||||
import { WhatWeDo } from "@/components/sections/WhatWeDo"
|
||||
import { OurWork } from "@/components/sections/OurWork"
|
||||
import { ParallaxHero } from "@/components/sections/ParallaxHero"
|
||||
import { AppContent } from "@/components/ui/app-content"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import Link from "next/link"
|
||||
import { Suspense } from "react"
|
||||
@@ -26,15 +27,18 @@ function BlogSection() {
|
||||
export default function IndexPage() {
|
||||
return (
|
||||
<section className="flex flex-col w-full">
|
||||
<HomepageHeader />
|
||||
<ParallaxHero />
|
||||
|
||||
<div className="flex flex-col justify-center bg-anakiwa-975 py-16">
|
||||
<div className="lg:max-w-[730px] flex flex-col mx-auto gap-10 justify-center items-center">
|
||||
<span className="text-white font-sans text-base lg:text-xl text-center">
|
||||
{LABELS.HOMEPAGE.MISSION}
|
||||
<div className="flex flex-col justify-center bg-white dark:bg-black py-16 lg:py-20">
|
||||
<AppContent className="lg:max-w-[890px] flex flex-col mx-auto gap-10 justify-center items-center">
|
||||
<span className="font-sans text-base text-center text-tuatara-950 dark:text-tuatara-100 font-bold uppercase tracking-[3.36px]">
|
||||
{LABELS.HOMEPAGE.MISSION_TITLE}
|
||||
</span>
|
||||
<span className="dark:text-tuatara-100 font-sans text-base lg:text-xl text-center text-tuatara-500 font-medium">
|
||||
{LABELS.HOMEPAGE.MISSION_DESCRIPTION}
|
||||
</span>
|
||||
<Link href="/about">
|
||||
<Button variant="transparent">
|
||||
<Button>
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="text-base font-medium uppercase">
|
||||
{LABELS.HOMEPAGE.MISSION_BUTTON}
|
||||
@@ -46,15 +50,15 @@ export default function IndexPage() {
|
||||
</div>
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</AppContent>
|
||||
</div>
|
||||
|
||||
<OurWork />
|
||||
|
||||
<BlogSection />
|
||||
|
||||
<HomepageVideoFeed />
|
||||
|
||||
<WhatWeDo />
|
||||
|
||||
<HomepageBanner />
|
||||
</section>
|
||||
)
|
||||
|
||||
@@ -33,7 +33,7 @@ export async function generateMetadata({
|
||||
const imageUrl =
|
||||
(project?.image ?? "")?.length > 0
|
||||
? `/project-banners/${project?.image}`
|
||||
: "/og-image.png"
|
||||
: "/share-image.png"
|
||||
|
||||
return {
|
||||
title: project?.name,
|
||||
|
||||
@@ -17,22 +17,24 @@ export const metadata: Metadata = {
|
||||
export default async function ProjectsPage() {
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<div className="w-full bg-page-header-gradient dark:bg-transparent-gradient">
|
||||
<AppContent className="flex flex-col gap-4 py-10 w-full">
|
||||
<Label.PageTitle label={LABELS.PROJECTS_PAGE.TITLE} />
|
||||
<h6 className="font-sans text-base font-normal text-primary md:text-[18px] md:leading-[27px] md:max-w-[700px]">
|
||||
{LABELS.PROJECTS_PAGE.SUBTITLE}
|
||||
</h6>
|
||||
</AppContent>
|
||||
</div>
|
||||
|
||||
<AppContent className="flex flex-col gap-10 py-10">
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<div className="flex flex-col gap-4">
|
||||
<ProjectFiltersBar />
|
||||
<ProjectResultBar />
|
||||
<AppContent className="flex flex-col gap-10 py-10 lg:py-16 w-full">
|
||||
<div className="flex flex-col gap-5">
|
||||
<div className="lg:w-1/2 mx-auto w-full">
|
||||
<div className="flex flex-col gap-10">
|
||||
<h1 className="dark:text-tuatara-100 text-tuatara-950 text-xl lg:text-3xl font-normal font-sans text-center">
|
||||
{LABELS.PROJECTS_PAGE.TITLE}
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
</Suspense>
|
||||
<div className="flex flex-col gap-5">
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<div className="lg:!w-1/2 mx-auto w-full">
|
||||
<ProjectFiltersBar />
|
||||
</div>
|
||||
<ProjectResultBar />
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
<ProjectList />
|
||||
</AppContent>
|
||||
</div>
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
"use client"
|
||||
|
||||
import { notFound } from "next/navigation"
|
||||
import { siteConfig } from "@/config/site"
|
||||
import { ProjectCategory, ProjectStatus } from "@/lib/types"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { LABELS } from "@/app/labels"
|
||||
import { AppContent } from "@/components/ui/app-content"
|
||||
import { Markdown, createMarkdownElement } from "@/components/ui/markdown"
|
||||
import { useProjects } from "@/app/providers/ProjectsProvider"
|
||||
import { AppLink } from "@/components/app-link"
|
||||
import { ProjectBlogArticles } from "@/components/blog/project-blog-articles"
|
||||
import { WikiCard } from "@/components/cards/wiki-card"
|
||||
import { Divider } from "@/components/divider"
|
||||
import { Icons } from "@/components/icons"
|
||||
import { ProjectLinkIconMap } from "@/components/mappings/project-links"
|
||||
import DiscoverMoreProjects from "@/components/project/discover-more-projects"
|
||||
import { ProjectTags } from "@/components/project/project-detail-tags"
|
||||
import ProjectExtraLinks from "@/components/project/project-extra-links"
|
||||
import { ProjectLinkIconMap } from "@/components/mappings/project-links"
|
||||
import { WikiSideNavigation } from "@/components/wiki-side-navigation"
|
||||
import { WikiCard } from "@/components/cards/wiki-card"
|
||||
import { ProjectTeamMembers } from "@/components/project/project-team"
|
||||
import { ProjectBlogArticles } from "@/components/blog/project-blog-articles"
|
||||
import { ProjectYouTubeVideos } from "@/components/sections/ProjectYouTubeVideos"
|
||||
import { useProjects } from "@/app/providers/ProjectsProvider"
|
||||
import { AppContent } from "@/components/ui/app-content"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { AppLink } from "@/components/app-link"
|
||||
import { Markdown, createMarkdownElement } from "@/components/ui/markdown"
|
||||
import { WikiSideNavigation } from "@/components/wiki-side-navigation"
|
||||
import { siteConfig } from "@/config/site"
|
||||
import { ProjectCategory, ProjectStatus } from "@/lib/types"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
const markdownContentClassName =
|
||||
"text-neutral-700 text-[22px] leading-6 font-bold pt-10 pb-4 dark:text-tuatara-100"
|
||||
@@ -60,7 +59,7 @@ const markdownComponents = {
|
||||
p: ({ ...props }) =>
|
||||
createMarkdownElement("p", {
|
||||
className:
|
||||
"py-2 leading-[150%] text-base text-slate-600 dark:text-tuatara-100",
|
||||
"py-2 leading-[150%] text-base lg:text-xl text-tuatara-500 dark:text-tuatara-100",
|
||||
...props,
|
||||
}),
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Metadata } from "next"
|
||||
import { LABELS } from "@/app/labels"
|
||||
import { AppContent } from "@/components/ui/app-content"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { AppLink } from "@/components/app-link"
|
||||
import { Icons } from "@/components/icons"
|
||||
import { ResearchList } from "@/components/research/research-list"
|
||||
import { AppContent } from "@/components/ui/app-content"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Metadata } from "next"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Research",
|
||||
@@ -12,15 +14,28 @@ export const metadata: Metadata = {
|
||||
|
||||
const ResearchPage = async () => {
|
||||
return (
|
||||
<div className="flex flex-col gap-10 lg:gap-32 pb-[128px] ">
|
||||
<div className="w-full bg-page-header-gradient dark:bg-transparent-gradient">
|
||||
<AppContent className="flex flex-col gap-4 py-10 w-full">
|
||||
<Label.PageTitle label={LABELS.RESEARCH_PAGE.TITLE} />
|
||||
<h6 className="font-sans text-base font-normal text-primary md:text-[18px] md:leading-[27px] md:max-w-[700px]">
|
||||
{LABELS.RESEARCH_PAGE.SUBTITLE}
|
||||
</h6>
|
||||
</AppContent>
|
||||
</div>
|
||||
<div className="flex flex-col gap-10 pb-[128px] ">
|
||||
<AppContent className="flex flex-col gap-4 py-10 w-full">
|
||||
<div className="flex flex-col gap-10">
|
||||
<h1 className="dark:text-tuatara-100 text-tuatara-950 text-xl lg:text-3xl font-normal font-sans text-center">
|
||||
{LABELS.RESEARCH_PAGE.TITLE}
|
||||
</h1>
|
||||
<div className="lg:!w-4/5 w-full flex lg:flex-row flex-col items-center gap-3 lg:gap-10 mx-auto justify-center">
|
||||
<span className="text-base lg:text-xl dark:text-tuatara-200 text-tuatara-950 font-sans text-center">
|
||||
{LABELS.RESEARCH_PAGE.SUBTITLE}
|
||||
</span>
|
||||
<AppLink variant="button" href="/about">
|
||||
<Button
|
||||
icon={Icons.arrowRight}
|
||||
iconPosition="right"
|
||||
className="uppercase"
|
||||
>
|
||||
{LABELS.COMMON.LEARN_MORE}
|
||||
</Button>
|
||||
</AppLink>
|
||||
</div>
|
||||
</div>
|
||||
</AppContent>
|
||||
|
||||
<AppContent className="flex flex-col gap-10">
|
||||
<ResearchList />
|
||||
|
||||
@@ -3,8 +3,8 @@ export const LABELS = {
|
||||
TITLE: "Blog",
|
||||
SUBTITLE:
|
||||
"Read our latest articles and stay updated on the latest news in the world of cryptography.",
|
||||
RECENT_ARTICLES: "Recent articles",
|
||||
SEE_MORE: "See more",
|
||||
RECENT_ARTICLES: "Recent posts",
|
||||
SEE_MORE: "More posts",
|
||||
READ_MORE: "Read more",
|
||||
SEARCH_PLACEHOLDER: "Search PSE's blog",
|
||||
},
|
||||
@@ -104,20 +104,21 @@ export const LABELS = {
|
||||
MORE_POSTS: "More posts",
|
||||
},
|
||||
HOMEPAGE: {
|
||||
MISSION:
|
||||
"Ethereum is the foundation of a freer digital world, but it will not reach its full potential without privacy. We’re building cryptographic tools, co-creating standards, and coordinating throughout the ecosystem to make privacy practical for Ethereum.",
|
||||
MISSION_TITLE: "Our Mission",
|
||||
MISSION_DESCRIPTION:
|
||||
"Ethereum is the foundation of a freer digital world, but it will not reach its full potential without privacy. We're building cryptographic tools, co-creating standards, and coordinating throughout the ecosystem to make privacy practical for Ethereum.",
|
||||
MISSION_BUTTON: "LEARN MORE ABOUT US",
|
||||
HEADER_TITLE: "Privacy Stewards of Ethereum",
|
||||
HEADER_SUBTITLE:
|
||||
"PSE is a research and development lab delivering privacy to the Ethereum ecosystem",
|
||||
JOIN_OUR_DISCORD: "Join our community",
|
||||
"PSE is a research and development lab delivering privacy to the Ethereum ecosystem.",
|
||||
JOIN_OUR_DISCORD: "Join our Discord",
|
||||
CONNECT_WITH_US: {
|
||||
TITLE: "Join our programs",
|
||||
DESCRIPTION:
|
||||
"Want to explore the world of programmable cryptography and learn how to make contributions to open-source projects? Join our free programs to start your journey!",
|
||||
},
|
||||
VIDEOS: "VIDEOS",
|
||||
VISIT_OUR_CHANNEL: "VISIT OUR CHANNEL",
|
||||
VISIT_OUR_CHANNEL: "More videos",
|
||||
ERROR_LOADING_VIDEOS: "Error loading videos",
|
||||
CHECK_OUT_OUR_YOUTUBE:
|
||||
"Check out our YouTube to learn the latest in advanced cryptography.",
|
||||
@@ -219,14 +220,12 @@ export const LABELS = {
|
||||
},
|
||||
},
|
||||
PROJECTS_PAGE: {
|
||||
TITLE: "Explore our Project Library",
|
||||
SUBTITLE:
|
||||
"Everything PSE works on is public and open source. All of our projects, whether research or development, are resources you can learn from and build with.",
|
||||
TITLE: "Explore Our Projects",
|
||||
},
|
||||
RESEARCH_PAGE: {
|
||||
TITLE: "Research",
|
||||
TITLE: "Explore Our Research",
|
||||
SUBTITLE:
|
||||
"Our research model is exploratory, iterative, and full-stack. We work on areas that are often overlooked by academia or industry — foundational concepts that need clarity, implementation gaps that have stalled progress, and risky ideas with uncertain but transformative potential. Sometimes our research unlocks a new breakthrough that leads to prototypes, and sometimes we just clarify assumptions and fill in theoretical gaps. We believe meaningful progress comes from applying research to real-world needs. Our work is open source, and we maintain a feedback loop so that challenges from the field can help shape our focus. We aim to support the broader ecosystem by systematizing knowledge, benchmarking new primitives, and offering credible guidance to advance the world of cryptography.",
|
||||
"Our research model is open source, iterative, and driven by real-world use cases.",
|
||||
ACTIVE_RESEARCH: "Active Research",
|
||||
PAST_RESEARCH: "Past Research",
|
||||
},
|
||||
@@ -245,13 +244,13 @@ export const LABELS = {
|
||||
},
|
||||
WHAT_WE_DO_SECTION: {
|
||||
BUILD: {
|
||||
TITLE: "Build",
|
||||
TITLE: "Building open source tools",
|
||||
DESCRIPTION:
|
||||
"We develop open source tools for things like data provenance, private identity, voting, and more to make privacy a core feature of Ethereum's application layer.",
|
||||
ACTION: "See our projects",
|
||||
},
|
||||
RESEARCH: {
|
||||
TITLE: "Research",
|
||||
TITLE: "Researching new applications",
|
||||
DESCRIPTION:
|
||||
"We benchmark primitives, systematize knowledge, and explore new ideas with the potential to transform the capabilities and impact of programmable cryptography.",
|
||||
ACTION: "See our research",
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import "@/globals.css"
|
||||
import Script from "next/script"
|
||||
import { Metadata, Viewport } from "next"
|
||||
|
||||
import { ThemeProvider } from "./components/layouts/ThemeProvider"
|
||||
import { GlobalProviderLayout } from "@/components/layouts/GlobalProviderLayout"
|
||||
import { SiteFooter } from "@/components/site-footer"
|
||||
import { SiteHeader } from "@/components/site-header"
|
||||
import { TailwindIndicator } from "@/components/tailwind-indicator"
|
||||
import { ThemeProvider } from "./components/layouts/ThemeProvider"
|
||||
import { siteConfig } from "@/config/site"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
import { Metadata, Viewport } from "next"
|
||||
import { DM_Sans, Inter, Space_Grotesk } from "next/font/google"
|
||||
import Script from "next/script"
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ["latin"],
|
||||
@@ -95,7 +93,7 @@ export const metadata: Metadata = {
|
||||
openGraph: {
|
||||
images: [
|
||||
{
|
||||
url: "/og-image.png",
|
||||
url: "/share-image.png",
|
||||
width: 1200,
|
||||
height: 630,
|
||||
},
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
"use client"
|
||||
|
||||
import "@/globals.css"
|
||||
import React from "react"
|
||||
import { LABELS } from "./labels"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Metadata } from "next"
|
||||
import Image from "next/image"
|
||||
import Link from "next/link"
|
||||
import { Metadata } from "next"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { LABELS } from "./labels"
|
||||
import React from "react"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "404: Page Not Found",
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
"use client"
|
||||
|
||||
import React from "react"
|
||||
import Link from "next/link"
|
||||
|
||||
import { Icons } from "./icons"
|
||||
import { cva } from "class-variance-authority"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cva } from "class-variance-authority"
|
||||
import Link from "next/link"
|
||||
import React from "react"
|
||||
|
||||
interface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
||||
children: React.ReactNode
|
||||
@@ -13,7 +12,7 @@ interface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
||||
to?: string
|
||||
external?: boolean
|
||||
withExternalIcon?: boolean
|
||||
variant?: "default" | "blue" | "button"
|
||||
variant?: "default" | "blue" | "button" | "nav"
|
||||
passHref?: boolean
|
||||
}
|
||||
|
||||
@@ -23,6 +22,7 @@ const linkClass = cva("inline-flex", {
|
||||
default:
|
||||
"text-black dark:text-white hover:text-orange duration-200 underline",
|
||||
blue: "text-anakiwa-500 hover:text-anakiwa-700",
|
||||
nav: "text-tuatara-100 hover:text-anakiwa-400 duration-200",
|
||||
button: "flex",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,28 +1,35 @@
|
||||
import { ReactNode } from "react"
|
||||
|
||||
import { AppContent } from "./ui/app-content"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
type BannerProps = {
|
||||
title: ReactNode
|
||||
subtitle?: string
|
||||
children?: ReactNode
|
||||
className?: string
|
||||
}
|
||||
|
||||
const Banner = ({ title, subtitle, children }: BannerProps) => {
|
||||
const Banner = ({ title, subtitle, children, className }: BannerProps) => {
|
||||
return (
|
||||
<section className="relative bg-background text-center">
|
||||
<section
|
||||
className={cn(
|
||||
"relative bg-cover-gradient dark:bg-anakiwa-975 text-center dark:bg-none",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className="py-16">
|
||||
<AppContent className="flex flex-col gap-6">
|
||||
<AppContent className="flex flex-col gap-8">
|
||||
<div className="flex flex-col items-center text-center">
|
||||
{typeof title === "string" ? (
|
||||
<h6 className="py-4 font-sans text-base font-bold uppercase tracking-[4px] text-primary dark:text-white">
|
||||
<h6 className="py-4 font-sans text-base font-bold uppercase tracking-[4px] text-tuatara-950 dark:text-tuatara-100">
|
||||
{title}
|
||||
</h6>
|
||||
) : (
|
||||
title
|
||||
)}
|
||||
{subtitle && (
|
||||
<span className="md:max-w-[600px] font-normal font-sans text-base text-primary dark:text-white">
|
||||
<span className="md:max-w-[600px] text-base lg:text-xl font-normal font-sans text-tuatara-950 dark:text-tuatara-100">
|
||||
{subtitle}
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
import { Icons } from "../icons"
|
||||
import { Button } from "../ui/button"
|
||||
import { LABELS } from "@/app/labels"
|
||||
import { Article } from "@/lib/content"
|
||||
import { cn } from "@/lib/utils"
|
||||
import Link from "next/link"
|
||||
import { LABELS } from "@/app/labels"
|
||||
|
||||
interface ArticleInEvidenceCardProps {
|
||||
article: Article
|
||||
@@ -67,7 +67,7 @@ export const ArticleInEvidenceCard = ({
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"duration-200 flex flex-col gap-4 text-left relative z-[1] w-full h-full",
|
||||
"duration-200 flex flex-col gap-4 text-left relative z-[1] aspect-video",
|
||||
{
|
||||
"px-5 lg:px-16 py-6 lg:py-16 ": size === "lg",
|
||||
"px-6 py-4 lg:p-8": size === "sm",
|
||||
@@ -172,7 +172,7 @@ export const ArticleInEvidenceCard = ({
|
||||
"relative flex flex-col gap-5 w-full items-center after:absolute after:inset-0 after:content-[''] after:bg-black after:opacity-20 group-hover:after:opacity-80 transition-opacity duration-300 after:z-[0]",
|
||||
{
|
||||
"aspect-video": !className?.includes("h-full"),
|
||||
"min-h-[148px]": !backgroundCover,
|
||||
"min-h-[200px]": !backgroundCover,
|
||||
"min-h-[177px] lg:min-h-[190px]": backgroundCover,
|
||||
},
|
||||
className
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { LABELS } from "@/app/labels"
|
||||
import { AppContent } from "../ui/app-content"
|
||||
import { getArticles, Article } from "@/lib/content"
|
||||
import Link from "next/link"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "../ui/button"
|
||||
import { Icons } from "../icons"
|
||||
import { AppContent } from "../ui/app-content"
|
||||
import { Button } from "../ui/button"
|
||||
import { LABELS } from "@/app/labels"
|
||||
import { getArticles, Article } from "@/lib/content"
|
||||
import { cn } from "@/lib/utils"
|
||||
import Link from "next/link"
|
||||
|
||||
const ArticleInEvidenceCard = ({
|
||||
article,
|
||||
@@ -60,7 +60,7 @@ const ArticleInEvidenceCard = ({
|
||||
<AsLinkWrapper href={`/blog/${article.id}`} asLink={asLink}>
|
||||
<div
|
||||
className={cn(
|
||||
"min-h-[177px] lg:min-h-[190px] relative flex flex-col gap-5 w-full items-center after:absolute after:inset-0 after:content-[''] after:bg-black after:opacity-20 group-hover:after:opacity-80 transition-opacity duration-300 after:z-[0]",
|
||||
"min-h-[177px] lg:min-h-[190px] relative w-full items-center after:absolute after:inset-0 after:content-[''] after:bg-black after:opacity-50 group-hover:after:opacity-80 transition-opacity duration-300 after:z-[0]",
|
||||
{
|
||||
"aspect-video": !className?.includes("h-full"),
|
||||
},
|
||||
@@ -73,58 +73,68 @@ const ArticleInEvidenceCard = ({
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"duration-200 flex flex-col gap-[10px] text-left relative z-[1] w-full h-full",
|
||||
{
|
||||
"px-5 lg:px-16 py-6 lg:py-16 ": size === "lg",
|
||||
"px-6 py-4 lg:p-8": size === "sm",
|
||||
"px-6 lg:p-16": size === "xl",
|
||||
},
|
||||
contentClassName
|
||||
)}
|
||||
className={cn("flex flex-col gap-[10px] h-full justify-center", {
|
||||
"px-5 lg:px-16 py-6 lg:py-16 ": size === "lg",
|
||||
"px-6 py-4 lg:p-8": size === "sm",
|
||||
"px-6 lg:p-16": size === "xl",
|
||||
})}
|
||||
>
|
||||
{article.date && showDate && (
|
||||
<span className="text-white text-xs font-sans font-bold tracking-[2.5px] text-left uppercase">
|
||||
{formatDate(article.date)}
|
||||
</span>
|
||||
)}
|
||||
<Link
|
||||
href={`/blog/${article.id}`}
|
||||
<div
|
||||
className={cn(
|
||||
" text-white font-display hover:text-anakiwa-400 transition-colors",
|
||||
{
|
||||
"text-[20px] font-semibold lg:font-bold lg:text-lg line-clamp-2 mt-auto":
|
||||
variant === "compact",
|
||||
"text-[20px] font-semibold lg:font-bold line-clamp-3 mt-auto":
|
||||
variant === "default",
|
||||
"text-[20px] font-bold lg:!text-[40px] lg:!leading-[44px] mt-auto":
|
||||
variant === "xl",
|
||||
},
|
||||
titleClassName
|
||||
"duration-200 flex flex-col gap-[10px] text-left relative z-[1] w-full",
|
||||
contentClassName
|
||||
)}
|
||||
>
|
||||
{article.title}
|
||||
</Link>
|
||||
<span className="text-sm text-white/80 uppercase font-inter">
|
||||
{article.authors?.join(", ")}
|
||||
</span>
|
||||
{article.tldr && !hideTldr && (
|
||||
<span
|
||||
className={
|
||||
"text-sm font-sans text-white font-normal line-clamp-2 lg:line-clamp-5 mt-auto hidden lg:block"
|
||||
}
|
||||
{article.date && showDate && (
|
||||
<span className="text-white text-xs font-sans font-bold tracking-[2.5px] text-left uppercase">
|
||||
{formatDate(article.date)}
|
||||
</span>
|
||||
)}
|
||||
<Link
|
||||
href={`/blog/${article.id}`}
|
||||
className={cn(
|
||||
" text-white font-display hover:text-anakiwa-400 transition-colors",
|
||||
{
|
||||
"text-[20px] font-semibold lg:font-bold lg:text-lg line-clamp-2":
|
||||
variant === "compact",
|
||||
"text-[20px] font-semibold lg:font-bold line-clamp-3":
|
||||
variant === "default",
|
||||
"text-[20px] font-bold lg:!text-[40px] lg:!leading-[44px]":
|
||||
variant === "xl",
|
||||
},
|
||||
titleClassName
|
||||
)}
|
||||
>
|
||||
{article.tldr}
|
||||
{article.title}
|
||||
</Link>
|
||||
<span className="text-sm text-white/80 uppercase font-inter">
|
||||
{article.authors?.join(", ")}
|
||||
</span>
|
||||
)}
|
||||
{article.tldr && !hideTldr && (
|
||||
<div className="mt-auto hidden lg:block w-full">
|
||||
<p
|
||||
className="text-sm font-sans text-white font-normal line-clamp-2 lg:line-clamp-3"
|
||||
style={{
|
||||
display: "-webkit-box",
|
||||
WebkitBoxOrient: "vertical",
|
||||
overflow: "hidden",
|
||||
wordBreak: "break-word",
|
||||
whiteSpace: "pre-wrap",
|
||||
}}
|
||||
>
|
||||
{article.tldr}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{showReadMore && (
|
||||
<Link href={`/blog/${article.id}`} className="ml-auto mt-4">
|
||||
<Button className="uppercase ml-auto" variant="secondary">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="!text-center">Read More</span>
|
||||
<Icons.arrowRight className="w-4 h-4" />
|
||||
</div>
|
||||
</Button>
|
||||
<Link href={`/blog/${article.id}`} className="ml-auto z-[1]">
|
||||
<div className="flex items-center gap-2 group">
|
||||
<span className="!text-center text-white uppercase group-hover:text-anakiwa-400 duration-200">
|
||||
Read More
|
||||
</span>
|
||||
<Icons.arrowRight className="w-4 h-4 text-white group-hover:text-anakiwa-400 group-hover:ml-1 duration-200" />
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
@@ -140,10 +150,10 @@ export async function BlogRecentArticles() {
|
||||
const otherArticles = articles.slice(1)
|
||||
|
||||
return (
|
||||
<div className="py-10 lg:py-16">
|
||||
<div className="py-16 lg:py-20">
|
||||
<AppContent>
|
||||
<div className="flex flex-col gap-10">
|
||||
<h3 className="font-sans text-base font-bold uppercase tracking-[4px] text-primary text-center">
|
||||
<h3 className="font-sans text-base font-bold uppercase tracking-[3.36px] text-tuatara-950 text-center dark:text-anakiwa-400">
|
||||
{LABELS.BLOG_PAGE.RECENT_ARTICLES}
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-5 gap-10 lg:gap-x-14 lg:max-w-[1200px] mx-auto relative">
|
||||
@@ -168,13 +178,13 @@ export async function BlogRecentArticles() {
|
||||
{article.title}
|
||||
</h4>
|
||||
{article.authors && (
|
||||
<span className="text-sm font-sans text-tuatara-400 uppercase">
|
||||
<span className="text-xs font-sans text-tuatara-400">
|
||||
{article.authors?.join(", ")}
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
))}
|
||||
<Link href="/blog" className="mt-auto">
|
||||
<Link href="/blog" className="mt-auto lg:mx-0 mx-auto">
|
||||
<Button className="uppercase">
|
||||
<div className="flex items-center gap-2">
|
||||
<span>{LABELS.BLOG_PAGE.SEE_MORE}</span>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"use client"
|
||||
|
||||
import { ProjectInterface } from "@/lib/types"
|
||||
import { AppContent } from "../ui/app-content"
|
||||
import { Article } from "@/lib/content"
|
||||
import { ArticleListCard } from "./article-list-card"
|
||||
import { useGetProjectRelatedArticles } from "@/hooks/useGetProjectRelatedArticles"
|
||||
import { Article } from "@/lib/content"
|
||||
import { ProjectInterface } from "@/lib/types"
|
||||
|
||||
export const ProjectBlogArticles = ({
|
||||
project,
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
"use client"
|
||||
|
||||
import { ReactNode } from "react"
|
||||
import Image from "next/image"
|
||||
|
||||
import { ProjectInterface, ProjectSectionLabelMapping } from "@/lib/types"
|
||||
import { cn, removeProtocol } from "@/lib/utils"
|
||||
import { LABELS } from "@/app/labels"
|
||||
|
||||
import { AppLink } from "../app-link"
|
||||
import { ThemesStatusMapping } from "../project/project-filters-bar"
|
||||
import { Card } from "./card"
|
||||
import { LABELS } from "@/app/labels"
|
||||
import { ProjectInterface, ProjectSectionLabelMapping } from "@/lib/types"
|
||||
import { cn, removeProtocol } from "@/lib/utils"
|
||||
import Image from "next/image"
|
||||
import { ReactNode } from "react"
|
||||
|
||||
interface WikiDetailProps {
|
||||
label: string
|
||||
|
||||
@@ -52,7 +52,7 @@ export const Icons = {
|
||||
>
|
||||
<path
|
||||
d="M21.1172 0.689941C9.51873 0.689941 0.117188 10.0915 0.117188 21.6899C0.117188 33.2884 9.51873 42.6899 21.1172 42.6899C32.7156 42.6899 42.1172 33.2884 42.1172 21.6899C42.1172 10.0915 32.7156 0.689941 21.1172 0.689941ZM15.3793 30.8233V12.5566L31.1988 21.6899L15.3793 30.8233Z"
|
||||
fill="#E3533A"
|
||||
fill="#A3DFF0"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
|
||||
@@ -53,8 +53,8 @@ export function MainNav({ items }: MainNavProps) {
|
||||
{
|
||||
"cursor-not-allowed": item.disabled,
|
||||
"border-transparent": item.href !== router,
|
||||
"!border-orange": item.href === router || isActive,
|
||||
"text-sm font-medium duration-200 ease-in-out hover:border-orange":
|
||||
"!border-anakiwa-400": item.href === router || isActive,
|
||||
"text-sm font-medium duration-200 ease-in-out hover:border-anakiwa-400":
|
||||
true,
|
||||
}
|
||||
)}
|
||||
|
||||
@@ -103,7 +103,7 @@ export default function ProjectCard({
|
||||
</Link>
|
||||
{(tldr ?? "")?.length > 0 && (
|
||||
<div className="flex flex-col h-24 gap-4">
|
||||
<p className="text-slate-900/80 line-clamp-4 dark:text-tuatara-200">
|
||||
<p className=" text-tuatara-500 text-base line-clamp-4 dark:text-tuatara-200">
|
||||
{tldr}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -152,7 +152,7 @@ export default function ProjectFiltersBar() {
|
||||
open={showModal}
|
||||
setOpen={setShowModal}
|
||||
>
|
||||
<div className="flex flex-col divide-y divide-tuatara-200">
|
||||
<div className="flex flex-col divide-y divide-tuatara-200 w-full">
|
||||
{Object.entries(filters).map(([key, items]) => {
|
||||
const filterLabel = FilterLabelMapping?.[key as ProjectFilter] ?? ""
|
||||
const type = FilterTypeMapping?.[key as ProjectFilter]
|
||||
@@ -252,7 +252,7 @@ export default function ProjectFiltersBar() {
|
||||
</Modal>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="grid items-center justify-between grid-cols-1 gap-3 md:grid-cols-5 md:gap-12">
|
||||
<div className="grid items-center justify-between grid-cols-1 gap-3 md:grid-cols-[1fr_122px_55px] md:gap-12">
|
||||
<div className="col-span-1 grid grid-cols-[1fr_auto] gap-2 md:col-span-3 md:gap-3">
|
||||
<Input
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -273,7 +273,7 @@ export default function ProjectFiltersBar() {
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Icons.Filter className="text-anakiwa-950 dark:text-anakiwa-400" />
|
||||
<span className="hidden md:block">
|
||||
<span className="hidden md:block text-sm">
|
||||
{LABELS.COMMON.FILTERS}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -146,7 +146,7 @@ export const ProjectList = () => {
|
||||
|
||||
return (
|
||||
<div className="relative grid items-start justify-between grid-cols-1">
|
||||
<div className="flex flex-col">
|
||||
<div className="flex flex-col gap-10">
|
||||
{ProjectStatusOrderList.map((status, index) => {
|
||||
const projects = projectsGroupByStatus[status as ProjectStatus] ?? []
|
||||
const description =
|
||||
@@ -162,7 +162,7 @@ export const ProjectList = () => {
|
||||
data-section={status}
|
||||
className="flex justify-between gap-10"
|
||||
>
|
||||
<div className={cn("flex w-full flex-col gap-10 pt-10")}>
|
||||
<div className={cn("flex w-full flex-col gap-10")}>
|
||||
<SectionWrapper
|
||||
title={status}
|
||||
description={description}
|
||||
|
||||
@@ -121,7 +121,7 @@ export default function ProjectCard({
|
||||
</h1>
|
||||
{(tldr ?? "")?.length > 0 && (
|
||||
<div className="flex flex-col h-24 gap-4">
|
||||
<p className="text-slate-900/80 line-clamp-3 dark:text-anakiwa-100">
|
||||
<p className="text-tuatara-500 text-base lg:text-xl line-clamp-3 dark:text-anakiwa-100">
|
||||
{tldr}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import { LABELS } from "@/app/labels"
|
||||
import { Icons } from "../icons"
|
||||
import { PageHeader } from "../page-header"
|
||||
import { Button } from "../ui/button"
|
||||
import { Label } from "../ui/label"
|
||||
import Image from "next/image"
|
||||
import PSELogo from "@/public/icons/archstar.webp"
|
||||
import { motion } from "framer-motion"
|
||||
import Link from "next/link"
|
||||
|
||||
export const HomepageHeader = () => {
|
||||
return (
|
||||
<PageHeader
|
||||
title={
|
||||
<motion.h1
|
||||
initial={{ y: 16, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
transition={{ duration: 0.8, cubicBezier: "easeOut" }}
|
||||
>
|
||||
<Label.PageTitle size="large" label={LABELS.HOMEPAGE.HEADER_TITLE} />
|
||||
</motion.h1>
|
||||
}
|
||||
subtitle={LABELS.HOMEPAGE.HEADER_SUBTITLE}
|
||||
image={
|
||||
<div className="m-auto flex h-[320px] w-full max-w-[280px] items-center justify-center md:m-0 md:h-full md:w-full lg:max-w-[380px]">
|
||||
<Image
|
||||
src={PSELogo}
|
||||
alt="pselogo"
|
||||
className="object-cover"
|
||||
priority
|
||||
quality={90}
|
||||
sizes="(max-width: 768px) 280px, (max-width: 1200px) 380px, 380px"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
actions={
|
||||
<div className="flex flex-col gap-4 lg:flex-row lg:gap-10">
|
||||
<Link href="/research" className="flex items-center gap-2 group">
|
||||
<Button className="w-full sm:w-auto">
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="text-base font-medium uppercase">
|
||||
{LABELS.COMMON.APPLIED_RESEARCH}
|
||||
</span>
|
||||
<Icons.arrowRight
|
||||
fill="white"
|
||||
className="h-5 duration-200 ease-in-out group-hover:translate-x-2"
|
||||
/>
|
||||
</div>
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/projects" className="flex items-center gap-2 group">
|
||||
<Button className="w-full sm:w-auto">
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="text-base font-medium uppercase">
|
||||
{LABELS.COMMON.DEVELOPMENT_PROJECTS}
|
||||
</span>
|
||||
<Icons.arrowRight
|
||||
fill="white"
|
||||
className="h-5 duration-200 ease-in-out group-hover:translate-x-2"
|
||||
/>
|
||||
</div>
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
"use client"
|
||||
|
||||
import { AppLink } from "../app-link"
|
||||
import { Icons } from "../icons"
|
||||
import { AppContent } from "../ui/app-content"
|
||||
import { Button } from "../ui/button"
|
||||
import { LABELS } from "@/app/labels"
|
||||
import { useYoutube } from "@/hooks/useYoutube"
|
||||
import { ArrowRight } from "lucide-react"
|
||||
import Image from "next/image"
|
||||
import Link from "next/link"
|
||||
import { Button } from "../ui/button"
|
||||
import { AppContent } from "../ui/app-content"
|
||||
import { Icons } from "../icons"
|
||||
import { useYoutube } from "@/hooks/useYoutube"
|
||||
import { AppLink } from "../app-link"
|
||||
|
||||
interface Video {
|
||||
id: string
|
||||
@@ -41,7 +41,7 @@ const VideoCard = ({ video }: { video: Video }) => {
|
||||
<Icons.play />
|
||||
</div>
|
||||
</div>
|
||||
<h3 className="font-sans text-sm font-normal line-clamp-3 text-white group-hover:text-tuatara-400 transition-colors">
|
||||
<h3 className="font-sans text-sm font-normal line-clamp-3 text-tuatara-950 dark:text-tuatara-100 group-hover:text-tuatara-400 transition-colors">
|
||||
{video.title}
|
||||
</h3>
|
||||
</Link>
|
||||
@@ -71,10 +71,10 @@ export const HomepageVideoFeed = () => {
|
||||
const { data: videos = [], isLoading, isError } = useYoutube()
|
||||
|
||||
return (
|
||||
<section className="mx-auto px-6 lg:px-8 py-10 lg:py-16 bg-tuatara-950 dark:bg-black w-full">
|
||||
<section className="mx-auto py-10 lg:pt-0 lg:pb-20 bg-white dark:bg-black w-full">
|
||||
<AppContent className="flex flex-col gap-8 lg:max-w-[1200px] w-full">
|
||||
<div className="col-span-1 lg:col-span-4">
|
||||
<h2 className="font-sans text-base font-bold uppercase tracking-[4px] text-white text-center">
|
||||
<h2 className="font-sans text-base font-bold uppercase tracking-[3.36px] text-tuatara-950 text-center dark:text-tuatara-100">
|
||||
{LABELS.HOMEPAGE.VIDEOS}
|
||||
</h2>
|
||||
</div>
|
||||
@@ -99,16 +99,16 @@ export const HomepageVideoFeed = () => {
|
||||
</div>
|
||||
<div className="lg:col-span-1">
|
||||
<div className="lg:p-6 flex flex-col gap-8">
|
||||
<span className="text-base text-white">
|
||||
<span className="text-base text-tuatara-950 dark:text-tuatara-100">
|
||||
{LABELS.HOMEPAGE.CHECK_OUT_OUR_YOUTUBE}
|
||||
</span>
|
||||
<AppLink
|
||||
href="https://www.youtube.com/@privacyscalingexplorations-1"
|
||||
external
|
||||
variant="button"
|
||||
className="group inline-flex"
|
||||
className="group mx-auto lg:mx-0 lg:inline-flex"
|
||||
>
|
||||
<Button className="w-full" variant="orange">
|
||||
<Button className="w-fit mx-auto lg:w-full" variant="orange">
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="font-medium uppercase">
|
||||
{LABELS.HOMEPAGE.VISIT_OUR_CHANNEL}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
"use client"
|
||||
|
||||
import { LABELS } from "@/app/labels"
|
||||
|
||||
import { Icons } from "../icons"
|
||||
import { AppContent } from "../ui/app-content"
|
||||
import { Button } from "../ui/button"
|
||||
import { LABELS } from "@/app/labels"
|
||||
import Link from "next/link"
|
||||
|
||||
type WhatWeDoContent = {
|
||||
type OurWorkContent = {
|
||||
title: string
|
||||
description: string
|
||||
icon: any
|
||||
@@ -15,8 +13,8 @@ type WhatWeDoContent = {
|
||||
link: string
|
||||
}
|
||||
|
||||
export const WhatWeDo = () => {
|
||||
const content: WhatWeDoContent[] = [
|
||||
export const OurWork = () => {
|
||||
const content: OurWorkContent[] = [
|
||||
{
|
||||
title: LABELS.WHAT_WE_DO_SECTION.BUILD.TITLE,
|
||||
description: LABELS.WHAT_WE_DO_SECTION.BUILD.DESCRIPTION,
|
||||
@@ -34,31 +32,28 @@ export const WhatWeDo = () => {
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="flex flex-col justify-center bg-anakiwa-975 py-16">
|
||||
<div className="flex flex-col justify-center bg-cover-gradient dark:bg-anakiwa-975 dark:bg-none py-16 lg:py-20">
|
||||
<AppContent className="mx-auto lg:max-w-[845px] w-full">
|
||||
<section className="flex flex-col gap-10">
|
||||
<h6 className="font-sans text-base font-bold uppercase tracking-[4px] text-primary text-center text-white">
|
||||
What we do
|
||||
<h6 className="font-sans text-base font-bold uppercase tracking-[3.36px] text-primary text-center text-tuatara-950 dark:text-tuatara-100">
|
||||
Our Work
|
||||
</h6>
|
||||
<div className="grid grid-cols-1 gap-10 lg:grid-cols-2 max-auto">
|
||||
<div className="grid grid-cols-1 gap-6 lg:gap-10 lg:grid-cols-2 max-auto">
|
||||
{content.map((item, index) => (
|
||||
<div className="flex flex-col gap-6 w-full lg:max-w-[300px]">
|
||||
<article className="flex flex-col gap-2" key={index}>
|
||||
<h6 className="font-sans text-xl font-medium text-white">
|
||||
<Link
|
||||
href={item.link}
|
||||
key={index}
|
||||
className="flex flex-col gap-6 w-full lg:max-w-[400px] p-6 lg:p-10 rounded-[10px] border border-anakiwa-300 dark:border-anakiwa-400 bg-white dark:bg-anakiwa-975 hover:scale-105 duration-200 ease-in-out dark:hover:bg-cover-gradient-dark"
|
||||
>
|
||||
<article className="flex flex-col gap-2">
|
||||
<h6 className="font-sans text-base lg:text-xl font-medium text-tuatara-950 dark:text-anakiwa-400">
|
||||
{item.title}
|
||||
</h6>
|
||||
<p className="font-sans text-base font-normal text-white">
|
||||
<p className="font-sans text-base font-normal text-tuatara-500 dark:text-tuatara-100">
|
||||
{item.description}
|
||||
</p>
|
||||
</article>
|
||||
{item.action && (
|
||||
<Link href={item.link}>
|
||||
<Button variant="transparent" className="uppercase">
|
||||
{item.action}
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
115
components/sections/ParallaxHero.tsx
Normal file
115
components/sections/ParallaxHero.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
"use client"
|
||||
|
||||
import { Icons } from "../icons"
|
||||
import { AppContent } from "../ui/app-content"
|
||||
import { Button } from "../ui/button"
|
||||
import { LABELS } from "@/app/labels"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { motion, useScroll, useTransform } from "framer-motion"
|
||||
import Link from "next/link"
|
||||
import { useRef } from "react"
|
||||
|
||||
export const ParallaxHero = () => {
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const { scrollYProgress } = useScroll({
|
||||
target: containerRef,
|
||||
offset: ["start start", "end start"],
|
||||
})
|
||||
|
||||
const backgroundY = useTransform(scrollYProgress, [0, 1], ["0%", "80%"])
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="relative w-full overflow-hidden h-[500px] lg:h-[600px]"
|
||||
style={{ isolation: "isolate" }}
|
||||
>
|
||||
<motion.div
|
||||
className={cn(
|
||||
"absolute inset-0 w-full h-[120%]-z-10 bg-cover bg-[0_-100px] lg:bg-[0_-150px]",
|
||||
"bg-[url('/hero/hero-mobile.jpg')] dark:bg-[url('/hero/hero-dark-mode-mobile.jpg')]", // mobile image
|
||||
"lg:bg-[url('/hero/hero.jpg')] lg:dark:bg-[url('/hero/hero-dark-mode.jpg')]" // desktop image
|
||||
)}
|
||||
style={{
|
||||
y: backgroundY,
|
||||
}}
|
||||
/>
|
||||
|
||||
<motion.div
|
||||
className="relative z-10 flex items-center justify-center h-full py-10 lg:py-[130px]"
|
||||
style={{
|
||||
y: useTransform(scrollYProgress, [0, 1], ["0%", "-25%"]),
|
||||
}}
|
||||
>
|
||||
<AppContent className="flex flex-col gap-10 max-w-[860px]">
|
||||
<motion.div
|
||||
initial={{ y: 16, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
transition={{ duration: 0.8, ease: [0.16, 1, 0.3, 1] }}
|
||||
className="flex flex-col gap-10 text-center"
|
||||
>
|
||||
<div className="flex flex-col gap-10">
|
||||
<div className="flex flex-col gap-[10px]">
|
||||
<motion.span
|
||||
style={{
|
||||
y: useTransform(scrollYProgress, [0, 1], ["0%", "-40%"]),
|
||||
}}
|
||||
className="font-sans font-medium leading-7 uppercase text-tuatara-800 dark:text-anakiwa-400"
|
||||
>
|
||||
{LABELS.HOMEPAGE.HEADER_TITLE}
|
||||
</motion.span>
|
||||
<motion.h1
|
||||
style={{
|
||||
y: useTransform(scrollYProgress, [0, 1], ["0%", "-35%"]),
|
||||
}}
|
||||
className="font-sans text-3xl lg:text-5xl font-medium text-tuatara-950 dark:text-tuatara-100"
|
||||
>
|
||||
{LABELS.HOMEPAGE.HEADER_SUBTITLE}
|
||||
</motion.h1>
|
||||
</div>
|
||||
<motion.div
|
||||
style={{
|
||||
y: useTransform(scrollYProgress, [0, 1], ["0%", "-30%"]),
|
||||
}}
|
||||
className="flex flex-wrap gap-4 lg:gap-8 mx-auto justify-center"
|
||||
>
|
||||
<Link
|
||||
href="/projects"
|
||||
className="flex items-center gap-2 group"
|
||||
>
|
||||
<Button className="w-full sm:w-auto">
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="text-base font-medium uppercase">
|
||||
{LABELS.COMMON.DEVELOPMENT_PROJECTS}
|
||||
</span>
|
||||
<Icons.arrowRight
|
||||
fill="white"
|
||||
className="h-5 duration-200 ease-in-out group-hover:translate-x-2"
|
||||
/>
|
||||
</div>
|
||||
</Button>
|
||||
</Link>
|
||||
<Link
|
||||
href="/research"
|
||||
className="flex items-center gap-2 group"
|
||||
>
|
||||
<Button className="w-full sm:w-auto">
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="text-base font-medium uppercase">
|
||||
{LABELS.COMMON.APPLIED_RESEARCH}
|
||||
</span>
|
||||
<Icons.arrowRight
|
||||
fill="white"
|
||||
className="h-5 duration-200 ease-in-out group-hover:translate-x-2"
|
||||
/>
|
||||
</div>
|
||||
</Button>
|
||||
</Link>
|
||||
</motion.div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</AppContent>
|
||||
</motion.div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -22,10 +22,12 @@ const ItemLabel = ({
|
||||
return (
|
||||
<div className="group flex items-center gap-2">
|
||||
{external && (
|
||||
<Icons.externalUrl className="text-white w-5 duration-200 group-hover:text-orange" />
|
||||
<Icons.externalUrl className="text-white w-5 duration-200 group-hover:text-anakiwa-400" />
|
||||
)}
|
||||
{icon && <div className="text-white group-hover:text-orange">{icon}</div>}
|
||||
<span className="mt-[0.9px] font-sans text-sm font-normal uppercase leading-[21px] text-white duration-200 group-hover:text-orange md:block">
|
||||
{icon && (
|
||||
<div className="text-white group-hover:text-anakiwa-400">{icon}</div>
|
||||
)}
|
||||
<span className="mt-[0.9px] font-sans text-sm font-normal uppercase leading-[21px] text-white duration-200 group-hover:text-anakiwa-400 md:block">
|
||||
{label}
|
||||
</span>
|
||||
</div>
|
||||
@@ -47,7 +49,7 @@ export function SiteFooter() {
|
||||
|
||||
return (
|
||||
<footer className="flex flex-col">
|
||||
<div className="bg-tuatara-950 text-white py-8 text-left text-[14px] dark:bg-black dark:border-t dark:border-anakiwa-800">
|
||||
<div className="bg-tuatara-950 text-white py-8 text-left text-[14px] dark:bg-black">
|
||||
<AppContent className="flex gap-10 py-2 lg:gap-24 justify-center">
|
||||
<LinksWrapper>
|
||||
{MAIN_NAV.map(
|
||||
@@ -56,7 +58,12 @@ export function SiteFooter() {
|
||||
indexKey
|
||||
) =>
|
||||
!onlyHeader && (
|
||||
<AppLink key={indexKey} href={href} external={external}>
|
||||
<AppLink
|
||||
key={indexKey}
|
||||
href={href}
|
||||
external={external}
|
||||
variant="button"
|
||||
>
|
||||
<ItemLabel label={title} />
|
||||
</AppLink>
|
||||
)
|
||||
@@ -66,6 +73,7 @@ export function SiteFooter() {
|
||||
<AppLink
|
||||
href={siteConfig.links.discord}
|
||||
className="flex items-start gap-2"
|
||||
variant="button"
|
||||
external
|
||||
>
|
||||
<ItemLabel
|
||||
@@ -76,6 +84,7 @@ export function SiteFooter() {
|
||||
<AppLink
|
||||
href={siteConfig.links.github}
|
||||
className="flex items-start gap-2"
|
||||
variant="button"
|
||||
external
|
||||
>
|
||||
<ItemLabel
|
||||
@@ -86,6 +95,7 @@ export function SiteFooter() {
|
||||
<AppLink
|
||||
href={siteConfig.links.twitter}
|
||||
className="flex items-center gap-2"
|
||||
variant="button"
|
||||
external
|
||||
>
|
||||
<ItemLabel
|
||||
@@ -100,6 +110,7 @@ export function SiteFooter() {
|
||||
<AppLink
|
||||
href={siteConfig.links.youtube}
|
||||
className="flex items-center gap-2"
|
||||
variant="button"
|
||||
external
|
||||
>
|
||||
<ItemLabel
|
||||
@@ -114,6 +125,7 @@ export function SiteFooter() {
|
||||
<AppLink
|
||||
href="/api/rss"
|
||||
className="flex items-center gap-2"
|
||||
variant="button"
|
||||
external
|
||||
>
|
||||
<ItemLabel
|
||||
@@ -127,6 +139,7 @@ export function SiteFooter() {
|
||||
</AppLink>
|
||||
<AppLink
|
||||
href={siteConfig.links.jobs}
|
||||
variant="button"
|
||||
external
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
@@ -134,13 +147,21 @@ export function SiteFooter() {
|
||||
</AppLink>
|
||||
</LinksWrapper>
|
||||
<LinksWrapper>
|
||||
<AppLink href={siteConfig.links.discord} external>
|
||||
<AppLink href={siteConfig.links.discord} external variant="button">
|
||||
<ItemLabel label="Feedback" />
|
||||
</AppLink>
|
||||
<AppLink href={siteConfig.links.privacyPolicy} external>
|
||||
<AppLink
|
||||
href={siteConfig.links.privacyPolicy}
|
||||
external
|
||||
variant="button"
|
||||
>
|
||||
<ItemLabel label={LABELS.COMMON.FOOTER.PRIVACY_POLICY} />
|
||||
</AppLink>
|
||||
<AppLink href={siteConfig.links.termOfUse} external>
|
||||
<AppLink
|
||||
href={siteConfig.links.termOfUse}
|
||||
external
|
||||
variant="button"
|
||||
>
|
||||
<ItemLabel label={LABELS.COMMON.FOOTER.TERMS_OF_USE} />
|
||||
</AppLink>
|
||||
</LinksWrapper>
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import NextImage from "next/image"
|
||||
import CloseVector from "@/public/icons/close-fill.svg"
|
||||
|
||||
import { NavItem } from "@/types/nav"
|
||||
import { siteConfig } from "@/config/site"
|
||||
import { interpolate } from "@/lib/utils"
|
||||
import { useAppSettings } from "@/hooks/useAppSettings"
|
||||
import { AppLink } from "./app-link"
|
||||
import { Icons } from "./icons"
|
||||
import { LABELS } from "@/app/labels"
|
||||
import { useGlobalProvider } from "@/app/providers/GlobalProvider"
|
||||
import {
|
||||
Discord,
|
||||
Github,
|
||||
Mirror,
|
||||
Twitter,
|
||||
} from "@/components/svgs/social-medias"
|
||||
import { LABELS } from "@/app/labels"
|
||||
import { Icons } from "./icons"
|
||||
import { siteConfig } from "@/config/site"
|
||||
import { useAppSettings } from "@/hooks/useAppSettings"
|
||||
import { interpolate } from "@/lib/utils"
|
||||
import CloseVector from "@/public/icons/close-fill.svg"
|
||||
import { NavItem } from "@/types/nav"
|
||||
import { SunMedium as SunIcon, Moon as MoonIcon } from "lucide-react"
|
||||
import { useGlobalProvider } from "@/app/providers/GlobalProvider"
|
||||
import { AppLink } from "./app-link"
|
||||
import NextImage from "next/image"
|
||||
import { useState } from "react"
|
||||
|
||||
export const SiteHeaderMobile = () => {
|
||||
const [header, setHeader] = useState(false)
|
||||
@@ -63,6 +62,7 @@ export const SiteHeaderMobile = () => {
|
||||
onClick={() => setHeader(false)}
|
||||
target={item?.external ? "_blank" : undefined}
|
||||
className="border-b-2 border-white p-4 uppercase"
|
||||
variant="nav"
|
||||
>
|
||||
{item.title}
|
||||
</AppLink>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { LucideIcon } from "lucide-react"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const buttonVariants = cva(
|
||||
"font-sans inline-flex items-center justify-center duration-200 rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background w-fit",
|
||||
"font-sans inline-flex items-center justify-center gap-2 duration-200 rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background w-fit",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
@@ -66,11 +66,21 @@ export interface ButtonProps
|
||||
VariantProps<typeof buttonVariants> {
|
||||
asChild?: boolean
|
||||
icon?: LucideIcon
|
||||
iconPosition?: "left" | "right"
|
||||
}
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
(
|
||||
{ className, variant, size, asChild = false, children, icon, ...props },
|
||||
{
|
||||
className,
|
||||
variant,
|
||||
size,
|
||||
asChild = false,
|
||||
children,
|
||||
icon,
|
||||
iconPosition = "left",
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const Comp = asChild ? Slot : "button"
|
||||
@@ -81,8 +91,9 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
ref={ref}
|
||||
{...props}
|
||||
>
|
||||
{Icon && <Icon size={18} />}
|
||||
{Icon && iconPosition === "left" && <Icon size={18} />}
|
||||
<span>{children}</span>
|
||||
{Icon && iconPosition === "right" && <Icon size={18} />}
|
||||
</Comp>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
"use client"
|
||||
|
||||
import katex from "katex"
|
||||
import React, { useCallback } from "react"
|
||||
import ReactMarkdown, { Components } from "react-markdown"
|
||||
import remarkGfm from "remark-gfm"
|
||||
import katex from "katex"
|
||||
import "katex/dist/katex.min.css"
|
||||
import rehypeRaw from "rehype-raw"
|
||||
import { TableRowCard } from "../cards/table-row-card"
|
||||
import { Accordion } from "./accordion"
|
||||
import Prism from "prismjs"
|
||||
import rehypeRaw from "rehype-raw"
|
||||
import "prismjs/themes/prism-tomorrow.css"
|
||||
import "prismjs/components/prism-javascript"
|
||||
import "prismjs/components/prism-typescript"
|
||||
@@ -22,8 +22,8 @@ import "prismjs/components/prism-yaml"
|
||||
import "prismjs/components/prism-python"
|
||||
import "prismjs/components/prism-rust"
|
||||
import "prismjs/components/prism-solidity"
|
||||
import { Icons } from "../icons"
|
||||
import { AppLink } from "../app-link"
|
||||
import { Icons } from "../icons"
|
||||
|
||||
const SCROLL_OFFSET = 150
|
||||
|
||||
@@ -637,7 +637,9 @@ const REACT_MARKDOWN_CONFIG = (darkMode: boolean): CustomComponents => ({
|
||||
if (containsMath(text)) {
|
||||
return (
|
||||
<p
|
||||
className={`text-tuatara-600 dark:text-tuatara-200 font-sans text-lg font-normal ${isMathOnly ? "math-only" : ""}`}
|
||||
className={`text-tuatara-600 dark:text-tuatara-200 font-sans text-lg font-normal ${
|
||||
isMathOnly ? "math-only" : ""
|
||||
}`}
|
||||
>
|
||||
<MathText text={text} />
|
||||
</p>
|
||||
@@ -664,7 +666,7 @@ const REACT_MARKDOWN_CONFIG = (darkMode: boolean): CustomComponents => ({
|
||||
if (containsMath(text)) {
|
||||
return (
|
||||
<li
|
||||
className="text-tuatara-600 font-sans text-lg font-normal dark:text-white"
|
||||
className="text-tuatara-500 font-sans text-base lg:text-xl font-normal dark:text-tuatara-100"
|
||||
{...props}
|
||||
>
|
||||
<MathText text={text} />
|
||||
@@ -674,7 +676,7 @@ const REACT_MARKDOWN_CONFIG = (darkMode: boolean): CustomComponents => ({
|
||||
|
||||
return (
|
||||
<li
|
||||
className="text-tuatara-600 font-sans text-lg font-normal dark:text-white"
|
||||
className="text-tuatara-500 font-sans text-base lg:text-xl font-normal dark:text-tuatara-100"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -69,6 +69,7 @@ We built and implemented a public lookup evaluation technique on BGG+ encodings.
|
||||
## ⚙️ Development Highlights
|
||||
|
||||
### [Verifiable OPRF (vOPRF)](https://pse.dev/en/projects/voprf)
|
||||
|
||||
We built the circuits necessary for the client-side proving part, and created the initial skeleton for client-server workflow. This demonstrates the concepts and gives us benchmarks of the performance. Next we will harden the protocol to [optionally] handle multiple servers that together compute the nullifier, and apply the protocol to specific use cases.
|
||||
|
||||
### [TLSNotary](https://pse.dev/en/projects/tlsn)
|
||||
@@ -108,14 +109,9 @@ We have started working on the on-chain verification of pods.
|
||||
### [zkID](https://pse.dev/en/projects/zk-id)
|
||||
|
||||
We continue the work on the zkp Wallet Unit. We completed the circuits over the P256 base field, including the first right-field circom implementation of ECDSA verification over P256. We are making progress towards compiling this for a Spartan + Hyrax backend over the T256 curve tower, and modifying the proof system to use re-randomisable commitments. This will yield a largely preprocessed and efficient prover.
|
||||
|
||||
Other significant strides across several key initiatives include:
|
||||
- Completing the 1st draft of the zkID roadmap, outlining our upcoming milestones and long-term vision.
|
||||
- Completing research into OFAC compliance considerations for zk-KYC.
|
||||
- Launched early outreach for zkID Day at Devconnect — [express interest here](https://docs.google.com/forms/d/1fQyL-2PaXx0d5-ieiJkwI5Ypl1p5VAbBA2i0AIrSlH8/edit).
|
||||
- Collected valuable community feedback on zkPDF to guide future improvements.
|
||||
- Integrating the Spartan Hydrax Backend.
|
||||
- We’ve launched early outreach for zkID Day at Devconnect — [express interest here](https://docs.google.com/forms/d/1fQyL-2PaXx0d5-ieiJkwI5Ypl1p5VAbBA2i0AIrSlH8/edit).
|
||||
|
||||
Other significant strides across several key initiatives include:
|
||||
- Completing the 1st draft of the zkID roadmap, outlining our upcoming milestones and long-term vision. - Completing research into OFAC compliance considerations for zk-KYC. - Launched early outreach for zkID Day at Devconnect — [express interest here](https://docs.google.com/forms/d/1fQyL-2PaXx0d5-ieiJkwI5Ypl1p5VAbBA2i0AIrSlH8/edit). - Collected valuable community feedback on zkPDF to guide future improvements. - Integrating the Spartan Hydrax Backend. - We’ve launched early outreach for zkID Day at Devconnect — [express interest here](https://docs.google.com/forms/d/1fQyL-2PaXx0d5-ieiJkwI5Ypl1p5VAbBA2i0AIrSlH8/edit).
|
||||
|
||||
### [Semaphore](https://pse.dev/en/projects/semaphore)
|
||||
|
||||
|
||||
@@ -8,27 +8,26 @@ tags: ["privacy", "governance", "DAOs", "MACI", "zkSNARKs"]
|
||||
projects: ["MACI"]
|
||||
---
|
||||
|
||||
|
||||
# Transparent by default
|
||||
|
||||
*“For over a century, it has been recognized that a key technical component making democracy work is the **secret ballot**: no one knows who you voted for, and furthermore, you do not have the ability to prove to anyone else who you voted for, even if you really want to.”*
|
||||
_“For over a century, it has been recognized that a key technical component making democracy work is the **secret ballot**: no one knows who you voted for, and furthermore, you do not have the ability to prove to anyone else who you voted for, even if you really want to.”_
|
||||
[- Vitalik Buterin](https://vitalik.eth.limo/general/2025/04/14/privacy.html)
|
||||
|
||||
Private voting prevents side games, which can lead to all sorts of perverse incentives (like bribes), which can corrupt votes or produce outcomes that are misaligned with the original intention of having the vote in the first place. Private voting also prevents collusion, where coordination between participants prevents individuals from expressing their true preferences. In general, [“whoever has the information has the power, ergo we need to avoid centralized control over information](https://vitalik.eth.limo/general/2025/04/14/privacy.html)” - so less information leak is a good thing.
|
||||
|
||||
Privacy is needed for technological progress. As Ethereum scales and adoption grows, financial and non-financial use cases for the technology increasingly rely on privacy as a necessary component. Lack of privacy locks us into a local maxima - the true potential of DAOs and decentralized governance only gets unlocked after privacy tools are good enough, and become fully integrated into the governance stack.
|
||||
|
||||
As important as privacy may be, there are also legitimate tradeoffs to recognize. Ethereum is fully transparent by default because technologies such as zero-knowledge proofs were not practically available when Ethereum was first launched. As a result, transparency - and the readability and accountability that comes with it - are still seen as positive attributes. A [recent post](https://blog.shutter.network/dao-voting-confidence-is-in-decline-how-to-restore-it/) by Decent DAO captures the sentiment well: *“DAOs were supposed to revolutionize governance. In the crypto community, many of us believed that by putting governance on the blockchain – transparently recorded and executed by code - we’d avoid the pitfalls of traditional systems.”*
|
||||
As important as privacy may be, there are also legitimate tradeoffs to recognize. Ethereum is fully transparent by default because technologies such as zero-knowledge proofs were not practically available when Ethereum was first launched. As a result, transparency - and the readability and accountability that comes with it - are still seen as positive attributes. A [recent post](https://blog.shutter.network/dao-voting-confidence-is-in-decline-how-to-restore-it/) by Decent DAO captures the sentiment well: _“DAOs were supposed to revolutionize governance. In the crypto community, many of us believed that by putting governance on the blockchain – transparently recorded and executed by code - we’d avoid the pitfalls of traditional systems.”_
|
||||
|
||||
In [an Optimism Collective discussion](https://gov.optimism.io/t/exploring-shielded-voting-enhancing-governance-on-optimism/8779/3) about shielded voting, community members have pointed out that private voting *“also eliminates the potential benefit of understanding the rationale behind others’ votes. Reading others’ reasoning before casting your vote can enrich your thought process by introducing new angles or considerations that you might not have initially considered (and even make you reconsider your initial stance).”*
|
||||
In [an Optimism Collective discussion](https://gov.optimism.io/t/exploring-shielded-voting-enhancing-governance-on-optimism/8779/3) about shielded voting, community members have pointed out that private voting _“also eliminates the potential benefit of understanding the rationale behind others’ votes. Reading others’ reasoning before casting your vote can enrich your thought process by introducing new angles or considerations that you might not have initially considered (and even make you reconsider your initial stance).”_
|
||||
|
||||
Transparency, for all its faults, is highly legible and traceable, so it produces information that may actually improve the quality of the vote. Public voting is a necessary mechanism to judge the quality of delegated or representative voting.
|
||||
Transparency, for all its faults, is highly legible and traceable, so it produces information that may actually improve the quality of the vote. Public voting is a necessary mechanism to judge the quality of delegated or representative voting.
|
||||
|
||||
Privacy shouldn’t be a binary all-or-nothing choice, but a trade-off dependent on the context. A modular instead of monolithic design choice. Let everyone have what they want, the Ethereum way!
|
||||
|
||||
# MACI: The private voting protocol for DAOs
|
||||
|
||||
The idea for [MACI](https://maci.pse.dev/) was first proposed in an [ethresearch post from Vitalik](https://ethresear.ch/t/minimal-anti-collusion-infrastructure/5413) in 2019. Since then, the case has been made again and again that [blockchain voting is important](https://vitalik.eth.limo/general/2021/05/25/voting2.html) and [privacy is essential](https://vitalik.eth.limo/general/2025/04/14/privacy.html). And more recently, we’ve seen huge [DAOs destabilized by vote buying](https://x.com/DefiIgnas/status/1909554283445387366). As DAOs evolve and mature, the need for private voting is becoming more apparent. [Decent DAO is exploring shielded voting](https://blog.shutter.network/dao-voting-confidence-is-in-decline-how-to-restore-it/), and community members have expressed the [desire for hidden ballots](https://x.com/LefterisJP/status/1921562225333916094) for important Ethereum DAO votes.
|
||||
The idea for [MACI](https://maci.pse.dev/) was first proposed in an [ethresearch post from Vitalik](https://ethresear.ch/t/minimal-anti-collusion-infrastructure/5413) in 2019. Since then, the case has been made again and again that [blockchain voting is important](https://vitalik.eth.limo/general/2021/05/25/voting2.html) and [privacy is essential](https://vitalik.eth.limo/general/2025/04/14/privacy.html). And more recently, we’ve seen huge [DAOs destabilized by vote buying](https://x.com/DefiIgnas/status/1909554283445387366). As DAOs evolve and mature, the need for private voting is becoming more apparent. [Decent DAO is exploring shielded voting](https://blog.shutter.network/dao-voting-confidence-is-in-decline-how-to-restore-it/), and community members have expressed the [desire for hidden ballots](https://x.com/LefterisJP/status/1921562225333916094) for important Ethereum DAO votes.
|
||||
|
||||
Privacy-preserving voting infrastructure such as MACI provides a solution to private voting, but adoption has been lacking, so we are working to change that. We hope that by integrating MACI into decentralized governance stacks and making improvements to the core protocol, DAOs and the communities they represent will have more options for higher quality, collective decision making - you can follow us on X to stay in the loop.
|
||||
|
||||
@@ -54,4 +53,4 @@ https://maci.pse.dev/docs/introduction
|
||||
|
||||
We hope you are now as motivated to solve privacy in the governance space as we are! As well as pursuing integrations, the MACI team is excited to be undertaking research into a new version of the protocol that makes notable improvements to the trust assumptions of the protocol - stay tuned for announcements on our work over the next month.
|
||||
|
||||
DAO stacks, voting stacks, and decentralizing governance infrastructure teams - if you’re interested in MACI as a secure, private, zkSNARK-powered voting plug-in for your stack, please reach out to us at [PSE Discord](https://pse.dev/discord).
|
||||
DAO stacks, voting stacks, and decentralizing governance infrastructure teams - if you’re interested in MACI as a secure, private, zkSNARK-powered voting plug-in for your stack, please reach out to us at [PSE Discord](https://pse.dev/discord).
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
}
|
||||
html {
|
||||
@apply font-sans;
|
||||
text-rendering: geometricPrecision;
|
||||
text-rendering: geometricPrecision !important;
|
||||
}
|
||||
body {
|
||||
@apply font-sans bg-background text-foreground;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react"
|
||||
import { Article } from "@/lib/content"
|
||||
import { useState, useEffect } from "react"
|
||||
|
||||
interface UseGetProjectRelatedArticlesProps {
|
||||
projectId: string
|
||||
|
||||
BIN
public/hero/hero-dark-mode-mobile.jpg
Normal file
BIN
public/hero/hero-dark-mode-mobile.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
BIN
public/hero/hero-dark-mode.jpg
Normal file
BIN
public/hero/hero-dark-mode.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 595 KiB |
BIN
public/hero/hero-mobile.jpg
Normal file
BIN
public/hero/hero-mobile.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
BIN
public/hero/hero.jpg
Normal file
BIN
public/hero/hero.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 173 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 497 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 743 KiB |
BIN
public/share-image.png
Normal file
BIN
public/share-image.png
Normal file
Binary file not shown.
@@ -22,6 +22,8 @@ module.exports = {
|
||||
"radial-gradient(114.29% 42.52% at 103.66% 58.94%, #D0F8F1 0%, #D1F3FF 18.23%, #ECF8FF 51.28%, #E1FFFA 80.21%, #D0F2FF 93.23%)",
|
||||
"cover-gradient":
|
||||
"linear-gradient(84deg, #FFF -1.95%, #C2E8F5 59.98%, #FFF 100.64%)",
|
||||
"cover-gradient-dark":
|
||||
"linear-gradient(179deg, #29ACCE -202.54%, rgba(0, 0, 0, 0.00) 192.47%)",
|
||||
"classic-gradient":
|
||||
"radial-gradient(325.52% 79.63% at 100% -0.02%, #FFF 0%, rgba(255, 255, 255, 0.00) 100%), radial-gradient(205.45% 61.89% at 2.34% 99.98%, #FFF 0%, rgba(255, 255, 255, 0.00) 100%)",
|
||||
"project-page-gradient":
|
||||
@@ -63,6 +65,7 @@ module.exports = {
|
||||
500: "#656A75",
|
||||
600: "#565964",
|
||||
700: "#4A4C54",
|
||||
800: "#414349",
|
||||
950: "#242528",
|
||||
},
|
||||
skeleton: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect, vi } from "vitest"
|
||||
import { GET } from "@/app/api/search/indexes/route"
|
||||
import { describe, it, expect, vi } from "vitest"
|
||||
|
||||
describe("/api/search/indexes", () => {
|
||||
describe("GET /api/search/indexes", () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest"
|
||||
import { GET } from "@/app/api/youtube/route"
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest"
|
||||
|
||||
// Mock fetch globally
|
||||
const mockFetch = vi.fn()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import "@testing-library/jest-dom"
|
||||
import "vitest-canvas-mock"
|
||||
import React from "react"
|
||||
|
||||
// Mock Next.js router
|
||||
import { vi, beforeAll, afterAll } from "vitest"
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* eslint-disable react/display-name */
|
||||
import React, { ReactElement } from "react"
|
||||
import { render, RenderOptions } from "@testing-library/react"
|
||||
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
|
||||
import { render, RenderOptions } from "@testing-library/react"
|
||||
import React, { ReactElement } from "react"
|
||||
import { vi } from "vitest"
|
||||
|
||||
interface WrapperProps {
|
||||
|
||||
Reference in New Issue
Block a user