Skip to content

Commit

Permalink
landing page
Browse files Browse the repository at this point in the history
  • Loading branch information
harshsbhat committed Dec 19, 2024
1 parent 5d2dc33 commit 91a1b18
Show file tree
Hide file tree
Showing 12 changed files with 689 additions and 5 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
},
"dependencies": {
"@hookform/resolvers": "^3.9.1",
"@radix-ui/react-accordion": "^1.2.2",
"@radix-ui/react-aspect-ratio": "^1.1.0",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.1.3",
Expand Down Expand Up @@ -55,6 +56,7 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"drizzle-orm": "^0.33.0",
"embla-carousel-react": "^8.5.1",
"framer-motion": "^11.13.1",
"geist": "^1.3.0",
"lucide-react": "^0.468.0",
Expand Down
90 changes: 90 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file added public/editor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 15 additions & 3 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import Features from "@/components/features/page";
import Hero from "@/components/landing/hero";
import FAQ from "@/components/landing/faq";
import HowToUse from "@/components/landing/how-to-use";
import Features from "@/components/landing/features";
import { Footer } from "@/components/landing/Footer";

export default function Home() {

return (
<div>
<main>
<div className="mb-10">
<Hero />
</div>
</div>
<h2 className="mt-20 text-center mb-3 text-2xl font-bold mx-auto relative z-20 py-4 bg-clip-text text-transparent bg-gradient-to-b from-neutral-800 via-neutral-700 to-neutral-700 dark:from-neutral-800 dark:via-white dark:to-white w-full">
Quickstart
</h2>
<HowToUse />
<Features />
<FAQ />
<Footer />
</main>
);
}
44 changes: 44 additions & 0 deletions src/components/landing/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Link from 'next/link'


export function Footer() {
return (
<footer className="border-t mt-28 py-12 text-center text-sm text-gray-500">
<div className="container mx-auto px-4">
<p className="mb-2">
Built by{' '}
<Link
href="https://twitter.com/HarshBhatX"
target="_blank"
rel="noopener noreferrer"
className="font-medium text-gray-700 hover:text-gray-900 transition-colors"
>
@HarshBhatX
</Link>{' '}
</p>
<p>
Sealnotes is open-source on{' '}
<Link
href="https://github.com/harshsbhat/sealnotes"
target="_blank"
rel="noopener noreferrer"
className="font-medium text-gray-700 hover:text-gray-900 transition-colors"
>
Github
</Link>{' '}
and uses{' '}
<Link
href="https://upstash.com"
target="_blank"
rel="noopener noreferrer"
className="font-medium text-gray-700 hover:text-gray-900 transition-colors"
>
Upstash
</Link>{' '}
for storing encrypted data
</p>
</div>
</footer>
)
}

71 changes: 71 additions & 0 deletions src/components/landing/faq.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"use client"

import { useState } from 'react'
import { ChevronDown } from 'lucide-react'

// FAQ data
const faqs = [
{
question: "What is zero knowledge app?",
answer: "A Zero-Knowledge (ZK) App leverages zero-knowledge proofs (ZKPs) to ensure data privacy and security while enabling verifiable interactions. We don't store any of your personal information or your password. We only encrypt text which never leaves your browser."
},
{
question: "How can I recover my site if I forget my password?",
answer: "Since this is a ZK app. There is no way for us to know which text belongs to what user. So unfortunately it is not possible to recover the notes without the password. Even if they are recovered it is not possible to decrypt them."
},
{
question: "How can I backup my notes?",
answer: "Currently you cannot backup your notes. I am working on that feature."
},
{
question: "How can I share my notes?",
answer: "As of now you will need to share both the name and password for your notepad. In future I am planning to allow users to create public notepads that can be shared without a password."
},
{
question: "How do you verify the password if it is never sent to the server?",
answer: "We use your password to encrypt your text on the client side. The encrypted text is then sent to the server. When you access your page, we retrieve the encrypted text from the server and decrypt it using your password. If the password is correct, the text is successfully decrypted; otherwise, it remains encrypted."
}
]

export default function FAQ() {
const [openItems, setOpenItems] = useState<string[]>([])

const toggleItem = (value: string) => {
setOpenItems(prev =>
prev.includes(value)
? prev.filter(item => item !== value)
: [...prev, value]
)
}

return (
<div className="mt-15 max-w-[1000px] mx-auto p-6 bg-white rounded-lg shadow-md">
<h2 className="mb-3 text-2xl sm:text-3xl md:text-4xl lg:text-3xl font-bold max-w-3xl py-4 bg-clip-text text-transparent bg-gradient-to-b from-neutral-800 via-neutral-700 to-neutral-700 dark:from-neutral-800 dark:via-white dark:to-white">
FAQs
</h2>
<div className="space-y-4">
{faqs.map((faq, index) => (
<div key={index} className="border border-gray-200 rounded-md overflow-hidden">
<button
className="w-full px-4 py-3 bg-gray-50 hover:bg-gray-100 transition-colors duration-200 flex justify-between items-center"
onClick={() => toggleItem(`item-${index}`)}
>
<span className="text-left text-gray-700 font-medium">{faq.question}</span>
<ChevronDown
className={`h-5 w-5 text-gray-500 transition-transform duration-200 ${
openItems.includes(`item-${index}`) ? 'transform rotate-180' : ''
}`}
/>
</button>
{openItems.includes(`item-${index}`) && (
<div className="px-4 py-3 bg-white">
<p className="text-gray-600">{faq.answer}</p>
</div>
)}
</div>
))}
</div>
</div>
)
}

86 changes: 86 additions & 0 deletions src/components/landing/features.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { LockKeyhole, IdCard, Github, GlobeLock, type LucideIcon } from 'lucide-react'
import { Button } from "@/components/ui/button"
import Link from 'next/link';
import { Card, CardContent } from "@/components/ui/card"

type FeatureProps = {
icon: LucideIcon;
title: string;
description: string;
};

export default function Features() {
const features: FeatureProps[] = [
{
icon: LockKeyhole,
title: "Set a password for your notes",
description: "We never store your password. Instead, your password is used as a key to encrypt your notepad. The encrypted output is then stored in our database, and without the password, it is essentially just a random set of characters."
},
{
icon: GlobeLock,
title: "Hashed Site names",
description: "Your site or notepad names are hashed before being stored in our database. This ensures that even if your password is compromised, it is impossible for anyone to determine your site name."
},
{
icon: IdCard,
title: "No login" ,
description: "Since we only need a password to encrypt your notes, there's no need for you to log in or provide your email or any other personal information."
},
{
icon: Github,
title: "Fully open-source",
description: "We are fully open-source on Github. You can feel free to fork the repo and self-deploy or make some customized changes for yourself."
},
];

return (
<div className='flex justify-center items-center my-10'>
<Card className="overflow-hidden w-[1000px]">
<div className="flex flex-col lg:flex-row">
<CardContent className="flex-1 p-6 lg:p-8">
<div className="space-y-6">
<div>
<h3 className="text-sm font-medium text-zinc-500">Encrypted Notepad</h3>
<h2 className="mt-2 text-2xl lg:text-3xl font-semibold tracking-tight">
Protect your notes with a secure password.
</h2>
</div>
<div className="space-y-5 mb-10">
{features.map((feature, index) => (
<Feature
key={index}
icon={feature.icon}
title={feature.title}
description={feature.description}
/>
))}
</div>

</div>
</CardContent>
<div className="flex-1 bg-zinc-100 min-h-[300px] lg:min-h-0 hidden lg:block">
<img
src="/editor.png"
alt="Password manager interface"
className="h-full w-full object-cover"
/>
</div>
</div>
</Card>
</div>
)
}

function Feature({ icon: Icon, title, description }: FeatureProps) {
return (
<div className="flex items-start gap-3">
<div className="mt-1 rounded-lg border border-black/10 p-1 flex-shrink-0">
<Icon className="h-4 w-4" />
</div>
<div>
<h3 className="font-medium">{title}</h3>
<p className="text-sm text-zinc-500">{description}</p>
</div>
</div>
)
}
Loading

0 comments on commit 91a1b18

Please sign in to comment.