diff --git a/docs/bun.lockb b/docs/bun.lockb index e9ce2f2..f678335 100755 Binary files a/docs/bun.lockb and b/docs/bun.lockb differ diff --git a/docs/components/clipboard-button.tsx b/docs/components/clipboard-button.tsx index 71ba153..cc0c283 100644 --- a/docs/components/clipboard-button.tsx +++ b/docs/components/clipboard-button.tsx @@ -3,14 +3,14 @@ import {useState} from 'react' import {Copy, Check} from 'lucide-react' import {useCopyToClipboard} from 'usehooks-ts' -import {Button} from '@/components/ui/button' +import {Button, ButtonProps} from '@/components/ui/button' -interface ClipboardButtonProps { +interface ClipboardButtonProps extends ButtonProps { text: string } export function ClipboardButton( - {text}: ClipboardButtonProps = {text: 'Copy me!'} + {text, ...rest}: ClipboardButtonProps = {text: 'Copy me!'} ) { const [isCopied, setIsCopied] = useState(false) const [_, copy] = useCopyToClipboard() @@ -24,14 +24,15 @@ export function ClipboardButton( return ( ) diff --git a/docs/components/landing.tsx b/docs/components/landing.tsx index beacadb..72d590d 100644 --- a/docs/components/landing.tsx +++ b/docs/components/landing.tsx @@ -1,36 +1,448 @@ -import {ArrowRightIcon} from 'lucide-react' +import {useState, useEffect} from 'react' import Link from 'next/link' +import Image from 'next/image' +import { + ArrowRight, + Code2, + Lock, + Zap, + Cloud, + Terminal, + Box, + Puzzle, + Layers, + Copy, + ArrowUpRight, + CheckCircle +} from 'lucide-react' +import Lottie from 'lottie-react' + +import {Button} from '@/components/ui/button' +import {Card, CardContent, CardHeader, CardTitle} from '@/components/ui/card' import {ClipboardButton} from './clipboard-button' -import Logo from './logo' -import {Button} from './ui/button' +import {HoverBorderGradient} from './ui/hover-border-gradient' +import { + GlowingStarsCard, + GlowingStarsDescription, + GlowingStarsTitle +} from './ui/glowing-stars-card' +import {cn} from '@lib/utils' +import {useTheme} from 'nextra-theme-docs' -export const Landing: React.FC = () => { - return ( -
-
-
- +const GradientBackground = ({children, className = ''}) => ( +
+
+ {children} +
+) + +const PatternBackground = ({pattern, className = ''}) => ( +
+) + +const FeatureCard = ({title, description, icon, pattern}) => ( + + + +
+
{icon}
+
+
+
+

{title}

+

{description}

+
+
+) + +const TechnologyCard = ({title, description, logo, link}) => ( +
+
+ {`${title} +
+
+

{title}

+ {/* */} +
+

{description}

+ + Learn more + + +
+) + +const RuntimeCard = ({title, description, logo, link, logoClassName = ''}) => ( + + + +
+ {`${title}
-

- The next generation of building APIs + {title} + + + +

{description}

+ + Learn more + + +
+ +) + +const patterns = { + dots: ``, + lines: ``, + squares: ``, + circles: ``, + zigzag: ``, + waves: ``, + triangles: ``, + hexagons: `` +} + +export function Landing() { + const {resolvedTheme} = useTheme() + const [animationData, setAnimationData] = useState(null) + + useEffect(() => { + fetch( + 'https://lottie.host/c32b1c68-74ef-4a25-91b2-11f792317480/we8GALnU48.json' + ) + .then(response => response.json()) + .then(data => setAnimationData(data)) + .catch(error => console.error('Error loading Lottie animation:', error)) + }, []) + + return ( +
+ {/* Hero */} +
+

+ The Next Generation of +
+ Building APIs

-
-

- A code-first framework for GraphQL API development, where your - schema reflects your functionality. +

+ Used by innovative teams worldwide, Pylon enables you to create + + {' '} + high-quality GraphQL APIs{' '} + + without defining any schema. +

+ + + Get started + + + + {/* */} +
+
+
+
+
+
+
+
+ + + Copy code + +
+
+
+                
+                  npm create
+                  pylon@latest
+                
+              
+
+
+
+

+ + {/* Features */} +
+
+
+

+ What's in Pylon? +

+

+ Everything you need to build production-ready GraphQL APIs. +

+
+
+ } + pattern={patterns.dots} + /> + } + pattern={patterns.lines} + /> + } + pattern={patterns.squares} + /> + } + pattern={patterns.circles} + /> + } + pattern={patterns.zigzag} + /> + } + pattern={patterns.waves} + /> + } + pattern={patterns.triangles} + /> + } + pattern={patterns.hexagons} + /> + + + + Pylon 2.3 + +
+ + Full Support for TypeScript Interfaces and Unions in Pylon + + +
+
+ +
+
+
+ + {/* Foundation / Powered By */} +
+
+

+ Built on a Foundation of Fast, Production-Grade Tooling

+ +
+ {/* Powered By Box with Lottie Animation */} +
+

+ Powered By +

+ {animationData && ( + + )} +
+ + {/* Technology Cards */} +
+ + + +
+ + {/* Supported Runtimes */} +
+

+ Supported Runtimes +

+
+ + + + +
+

+ Pylon is designed to be runtime-agnostic, allowing you to deploy + your GraphQL API to various environments. Choose the runtime + that best fits your project's needs and infrastructure + requirements. +

+
+
-
-
- - - -
+ + + {/* Improved CTA */} +
+
+
+
+

+ Start Building Powerful APIs Today +

+

+ Join hundreds of developers who are revolutionizing API + development with Pylon. +

+
+ +
+ + + + Documentation + + + +

+ Explore our comprehensive documentation to get started with + Pylon and learn about all its features. +

+ +
+
+ + + + Open Source + + + +

+ Pylon is open source. Contribute, report issues, or star our + GitHub repository to support the project. +

+ +
+
+
+ +
+
+ "Pylon is the foundation of our greater vision to make backend + development easier and faster. It's revolutionizing how we build + and scale APIs." +
+
+ Nico Schett +
+

Nico Schett

+

CEO, Cronit

+
+
+
+
+
+

) } diff --git a/docs/components/ui/card.tsx b/docs/components/ui/card.tsx index 7820300..2e90a32 100644 --- a/docs/components/ui/card.tsx +++ b/docs/components/ui/card.tsx @@ -4,8 +4,8 @@ import {cn} from '@/lib/utils' const Card = React.forwardRef< HTMLDivElement, - React.HTMLAttributes ->(({className, ...props}, ref) => ( + React.HTMLAttributes & {showGradient?: boolean} +>(({className, showGradient = true, ...props}, ref) => (
-
+ {showGradient && ( +
+ )} {props.children}
)) diff --git a/docs/components/ui/glowing-stars-card.tsx b/docs/components/ui/glowing-stars-card.tsx new file mode 100644 index 0000000..b792be1 --- /dev/null +++ b/docs/components/ui/glowing-stars-card.tsx @@ -0,0 +1,169 @@ +'use client' + +import React, {useEffect, useRef, useState} from 'react' +import {AnimatePresence, motion} from 'framer-motion' +import {cn} from '@/lib/utils' +import {useTheme} from 'nextra-theme-docs' + +export const GlowingStarsCard = ({ + className, + children, + hoverEfect = true +}: { + className?: string + children?: React.ReactNode + hoverEfect?: boolean +}) => { + const [mouseEnter, setMouseEnter] = useState(false) + + return ( +
{ + if (!hoverEfect) return + setMouseEnter(true) + }} + onMouseLeave={() => { + if (!hoverEfect) return + setMouseEnter(false) + }} + className={cn( + 'p-6 max-w-md h-full w-full rounded-xl border border-[#eaeaea] dark:border-gray-800', + className + )}> +
+ +
+
{children}
+
+ ) +} + +export const GlowingStarsDescription = ({ + className, + children +}: { + className?: string + children?: React.ReactNode +}) => { + return ( +

+ {children} +

+ ) +} + +export const GlowingStarsTitle = ({ + className, + children +}: { + className?: string + children?: React.ReactNode +}) => { + return ( +

+ {children} +

+ ) +} + +export const Illustration = ({mouseEnter}: {mouseEnter: boolean}) => { + const stars = 108 + const columns = 18 + + const [glowingStars, setGlowingStars] = useState([]) + + const highlightedStars = useRef([]) + + useEffect(() => { + const interval = setInterval(() => { + highlightedStars.current = Array.from({length: 5}, () => + Math.floor(Math.random() * stars) + ) + setGlowingStars([...highlightedStars.current]) + }, 3000) + + return () => clearInterval(interval) + }, []) + + return ( +
+ {[...Array(stars)].map((_, starIdx) => { + const isGlowing = glowingStars.includes(starIdx) + const delay = (starIdx % 10) * 0.1 + const staticDelay = starIdx * 0.01 + return ( +
+ + {mouseEnter && } + + {isGlowing && } + +
+ ) + })} +
+ ) +} + +const Star = ({isGlowing, delay}: {isGlowing: boolean; delay: number}) => { + const {resolvedTheme} = useTheme() + const isDarkTheme = resolvedTheme === 'dark' + return ( + + ) +} + +const Glow = ({delay}: {delay: number}) => { + return ( + + ) +} diff --git a/docs/components/ui/hover-border-gradient.tsx b/docs/components/ui/hover-border-gradient.tsx new file mode 100644 index 0000000..8f885d5 --- /dev/null +++ b/docs/components/ui/hover-border-gradient.tsx @@ -0,0 +1,118 @@ +'use client' +import React, {useState, useEffect} from 'react' +import {motion} from 'framer-motion' +import {useTheme} from 'nextra-theme-docs' + +import {cn} from '@/lib/utils' + +type Direction = 'TOP' | 'LEFT' | 'BOTTOM' | 'RIGHT' + +export function HoverBorderGradient({ + children, + containerClassName, + className, + as: Tag = 'button', + duration = 1, + clockwise = true, + ...props +}: React.PropsWithChildren< + { + as?: React.ElementType + containerClassName?: string + className?: string + duration?: number + clockwise?: boolean + } & React.HTMLAttributes +>) { + const [hovered, setHovered] = useState(false) + const [direction, setDirection] = useState('TOP') + + const {resolvedTheme} = useTheme() + + // Usually, we wouldnt need this, but the useTheme hook from nextra-theme-docs seem to have (a) bug(s) + const theme = + resolvedTheme && resolvedTheme === 'system' + ? 'dark' + : resolvedTheme ?? 'dark' + + const rotateDirection = (currentDirection: Direction): Direction => { + const directions: Direction[] = ['TOP', 'LEFT', 'BOTTOM', 'RIGHT'] + const currentIndex = directions.indexOf(currentDirection) + const nextIndex = clockwise + ? (currentIndex - 1 + directions.length) % directions.length + : (currentIndex + 1) % directions.length + return directions[nextIndex] + } + + const movingMap: Record<'dark' | 'light', Record> = { + dark: { + TOP: 'radial-gradient(20.7% 50% at 50% 0%, #3275F8 0%, rgba(255, 255, 255, 0) 100%)', + LEFT: 'radial-gradient(16.6% 43.1% at 0% 50%, #3275F8 0%, rgba(255, 255, 255, 0) 100%)', + BOTTOM: + 'radial-gradient(20.7% 50% at 50% 100%, #3275F8 0%, rgba(255, 255, 255, 0) 100%)', + RIGHT: + 'radial-gradient(16.2% 41.199999999999996% at 100% 50%, #3275F8 0%, rgba(255, 255, 255, 0) 100%)' + }, + light: { + TOP: 'radial-gradient(20.7% 50% at 50% 0%, #3275F8 0%, rgba(255, 255, 255, 1) 100%)', + LEFT: 'radial-gradient(16.6% 43.1% at 0% 50%, #3275F8 0%, rgba(255, 255, 255, 1) 100%)', + BOTTOM: + 'radial-gradient(20.7% 50% at 50% 100%, #3275F8 0%, rgba(255, 255, 255, 1) 100%)', + RIGHT: + 'radial-gradient(16.2% 41.199999999999996% at 100% 50%, #3275F8 0%, rgba(255, 255, 255, 1) 100%)' + } + } + + const highlight = + theme === 'dark' + ? 'radial-gradient(75% 181.15942028985506% at 50% 50%, #3275F8 0%, rgba(255, 255, 255, 0) 100%)' + : 'radial-gradient(75% 181.15942028985506% at 50% 50%, #3275F8 0%, rgba(255, 255, 255, 1) 100%)' + + useEffect(() => { + if (!hovered) { + const interval = setInterval(() => { + setDirection(prevState => rotateDirection(prevState)) + }, duration * 1000) + return () => clearInterval(interval) + } + }, [hovered]) + return ( + ) => { + setHovered(true) + }} + onMouseLeave={() => setHovered(false)} + className={cn( + 'relative flex rounded-full border content-center bg-border dark:hover:bg-white/10 transition duration-500 dark:bg-white/20 items-center flex-col flex-nowrap gap-10 h-min justify-center overflow-visible p-px decoration-clone w-fit', + containerClassName + )} + {...props}> +
+ {children} +
+ +
+ + ) +} diff --git a/docs/next.config.js b/docs/next.config.js index bdb7cb3..b172428 100644 --- a/docs/next.config.js +++ b/docs/next.config.js @@ -1,9 +1,17 @@ const withNextra = require('nextra')({ theme: 'nextra-theme-docs', - themeConfig: './theme.config.tsx' + themeConfig: './theme.config.tsx', + cleanDistDir: true }) module.exports = withNextra({ + images: { + domains: [ + 'raw.githubusercontent.com', + 'avatars.githubusercontent.com', + 'upload.wikimedia.org' + ] + }, async redirects() { return [ { diff --git a/docs/package.json b/docs/package.json index c61d538..b86eda0 100644 --- a/docs/package.json +++ b/docs/package.json @@ -25,6 +25,8 @@ "@vercel/analytics": "^1.3.1", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", + "framer-motion": "^11.11.11", + "lottie-react": "^2.4.0", "lucide-react": "^0.440.0", "next": "^13.0.6", "nextra": "latest", diff --git a/docs/pages/_meta.json b/docs/pages/_meta.json index 1013037..d73da76 100644 --- a/docs/pages/_meta.json +++ b/docs/pages/_meta.json @@ -5,7 +5,7 @@ "display": "hidden", "theme": { "timestamp": false, - "layout": "full" + "layout": "raw" } }, "docs": { diff --git a/docs/theme.config.tsx b/docs/theme.config.tsx index 0e0587e..e075139 100644 --- a/docs/theme.config.tsx +++ b/docs/theme.config.tsx @@ -23,13 +23,23 @@ const config: DocsThemeConfig = { docsRepositoryBase: 'https://github.com/getcronit/pylon/tree/main/docs', footer: { text: ( - - {new Date().getFullYear()} ©{' '} - - cronit - - . Imprint - +
+

+ Built by{' '} + + Cronit + + . The source code is available on{' '} + + GitHub + + . +

+
) }, nextThemes: {