Added init website for gitea-mirror
24
www/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# build output
|
||||||
|
dist/
|
||||||
|
# generated types
|
||||||
|
.astro/
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# logs
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
|
||||||
|
# environment variables
|
||||||
|
.env
|
||||||
|
.env.production
|
||||||
|
|
||||||
|
# macOS-specific files
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# jetbrains setting folder
|
||||||
|
.idea/
|
||||||
13
www/README.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Astro with Tailwind
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bun create astro@latest -- --template with-tailwindcss
|
||||||
|
```
|
||||||
|
|
||||||
|
[](https://stackblitz.com/github/withastro/astro/tree/latest/examples/with-tailwindcss)
|
||||||
|
[](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/with-tailwindcss)
|
||||||
|
[](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/with-tailwindcss/devcontainer.json)
|
||||||
|
|
||||||
|
Astro comes with [Tailwind](https://tailwindcss.com) support out of the box. This example showcases how to style your Astro project with Tailwind.
|
||||||
|
|
||||||
|
For complete setup instructions, please see our [Tailwind Integration Guide](https://docs.astro.build/en/guides/integrations-guide/tailwind).
|
||||||
14
www/astro.config.mjs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
// @ts-check
|
||||||
|
import { defineConfig } from 'astro/config';
|
||||||
|
import tailwindcss from '@tailwindcss/vite';
|
||||||
|
|
||||||
|
import react from '@astrojs/react';
|
||||||
|
|
||||||
|
// https://astro.build/config
|
||||||
|
export default defineConfig({
|
||||||
|
vite: {
|
||||||
|
plugins: [tailwindcss()]
|
||||||
|
},
|
||||||
|
|
||||||
|
integrations: [react()]
|
||||||
|
});
|
||||||
1022
www/bun.lock
Normal file
21
www/components.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://ui.shadcn.com/schema.json",
|
||||||
|
"style": "new-york",
|
||||||
|
"rsc": false,
|
||||||
|
"tsx": true,
|
||||||
|
"tailwind": {
|
||||||
|
"config": "",
|
||||||
|
"css": "src/styles/global.css",
|
||||||
|
"baseColor": "neutral",
|
||||||
|
"cssVariables": true,
|
||||||
|
"prefix": ""
|
||||||
|
},
|
||||||
|
"aliases": {
|
||||||
|
"components": "@/components",
|
||||||
|
"utils": "@/lib/utils",
|
||||||
|
"ui": "@/components/ui",
|
||||||
|
"lib": "@/lib",
|
||||||
|
"hooks": "@/hooks"
|
||||||
|
},
|
||||||
|
"iconLibrary": "lucide"
|
||||||
|
}
|
||||||
32
www/package.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "www",
|
||||||
|
"type": "module",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "astro dev",
|
||||||
|
"build": "astro build",
|
||||||
|
"preview": "astro preview",
|
||||||
|
"astro": "astro"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@astrojs/mdx": "^4.3.0",
|
||||||
|
"@astrojs/react": "^4.3.0",
|
||||||
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
|
"@tailwindcss/vite": "^4.1.3",
|
||||||
|
"@types/canvas-confetti": "^1.9.0",
|
||||||
|
"@types/react": "^19.1.8",
|
||||||
|
"@types/react-dom": "^19.1.6",
|
||||||
|
"astro": "^5.11.0",
|
||||||
|
"canvas-confetti": "^1.9.3",
|
||||||
|
"class-variance-authority": "^0.7.1",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"lucide-react": "^0.525.0",
|
||||||
|
"react": "^19.1.0",
|
||||||
|
"react-dom": "^19.1.0",
|
||||||
|
"tailwind-merge": "^3.3.1",
|
||||||
|
"tailwindcss": "^4.1.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"tw-animate-css": "^1.3.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
www/public/assets/activity.png
Normal file
|
After Width: | Height: | Size: 854 KiB |
BIN
www/public/assets/activity_mobile.png
Normal file
|
After Width: | Height: | Size: 276 KiB |
BIN
www/public/assets/configuration.png
Normal file
|
After Width: | Height: | Size: 950 KiB |
BIN
www/public/assets/configuration_mobile.png
Normal file
|
After Width: | Height: | Size: 255 KiB |
BIN
www/public/assets/dashboard.png
Normal file
|
After Width: | Height: | Size: 943 KiB |
BIN
www/public/assets/dashboard_mobile.png
Normal file
|
After Width: | Height: | Size: 215 KiB |
BIN
www/public/assets/logo-no-bg.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
www/public/assets/logo.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
www/public/assets/organisation.png
Normal file
|
After Width: | Height: | Size: 844 KiB |
BIN
www/public/assets/organisation_mobile.png
Normal file
|
After Width: | Height: | Size: 221 KiB |
BIN
www/public/assets/repositories.png
Normal file
|
After Width: | Height: | Size: 970 KiB |
BIN
www/public/assets/repositories_mobile.png
Normal file
|
After Width: | Height: | Size: 227 KiB |
19
www/public/favicon.svg
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
|
<stop offset="0%" style="stop-color:#f97316;stop-opacity:1" />
|
||||||
|
<stop offset="100%" style="stop-color:#ea580c;stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<\!-- Background circle -->
|
||||||
|
<circle cx="16" cy="16" r="15" fill="url(#grad)" />
|
||||||
|
|
||||||
|
<\!-- Mirror/sync icon -->
|
||||||
|
<g fill="white" transform="translate(16, 16)">
|
||||||
|
<\!-- First arrow (top) -->
|
||||||
|
<path d="M -8 -2 L -3 -2 L -3 -5 L 2 0 L -3 5 L -3 2 L -8 2 C -8 2 -10 2 -10 0 C -10 -2 -8 -2 -8 -2 Z" />
|
||||||
|
<\!-- Second arrow (bottom) -->
|
||||||
|
<path d="M 8 2 L 3 2 L 3 5 L -2 0 L 3 -5 L 3 -2 L 8 -2 C 8 -2 10 -2 10 0 C 10 2 8 2 8 2 Z" opacity="0.8" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 781 B |
19
www/src/components/Button.astro
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
// Click button, get confetti!
|
||||||
|
// Styled by Tailwind :)
|
||||||
|
---
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="appearance-none py-2 px-4 bg-purple-500 text-white font-semibold rounded-lg shadow-md hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-purple-400 focus:ring-opacity-75"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import confetti from 'canvas-confetti';
|
||||||
|
const button = document.body.querySelector('button');
|
||||||
|
|
||||||
|
if (button) {
|
||||||
|
button.addEventListener('click', () => confetti());
|
||||||
|
}
|
||||||
|
</script>
|
||||||
59
www/src/components/ui/button.tsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import { Slot } from "@radix-ui/react-slot"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const buttonVariants = cva(
|
||||||
|
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default:
|
||||||
|
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
||||||
|
destructive:
|
||||||
|
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||||
|
outline:
|
||||||
|
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
||||||
|
secondary:
|
||||||
|
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
||||||
|
ghost:
|
||||||
|
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||||
|
link: "text-primary underline-offset-4 hover:underline",
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
||||||
|
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
||||||
|
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
||||||
|
icon: "size-9",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
size: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
function Button({
|
||||||
|
className,
|
||||||
|
variant,
|
||||||
|
size,
|
||||||
|
asChild = false,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"button"> &
|
||||||
|
VariantProps<typeof buttonVariants> & {
|
||||||
|
asChild?: boolean
|
||||||
|
}) {
|
||||||
|
const Comp = asChild ? Slot : "button"
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Comp
|
||||||
|
data-slot="button"
|
||||||
|
className={cn(buttonVariants({ variant, size, className }))}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Button, buttonVariants }
|
||||||
16
www/src/layouts/main.astro
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
import '../styles/global.css';
|
||||||
|
const { content } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
|
<title>{content.title}</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<slot />
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
6
www/src/lib/utils.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { clsx, type ClassValue } from "clsx"
|
||||||
|
import { twMerge } from "tailwind-merge"
|
||||||
|
|
||||||
|
export function cn(...inputs: ClassValue[]) {
|
||||||
|
return twMerge(clsx(inputs))
|
||||||
|
}
|
||||||
344
www/src/pages/index.astro
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
---
|
||||||
|
import '../styles/global.css';
|
||||||
|
import { Button } from '../components/ui/button';
|
||||||
|
|
||||||
|
const features = [
|
||||||
|
{
|
||||||
|
title: "Automated Mirroring",
|
||||||
|
description: "Set it and forget it. Automatically sync your GitHub repositories to Gitea on a schedule.",
|
||||||
|
icon: "🔄"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Bulk Operations",
|
||||||
|
description: "Mirror entire organizations or user accounts with a single configuration.",
|
||||||
|
icon: "📦"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Preserve Structure",
|
||||||
|
description: "Maintain your GitHub organization structure or customize how repos are organized.",
|
||||||
|
icon: "🏗️"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Real-time Status",
|
||||||
|
description: "Monitor mirror progress with live updates and detailed activity logs.",
|
||||||
|
icon: "📊"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Secure & Private",
|
||||||
|
description: "Self-hosted solution keeps your code on your infrastructure.",
|
||||||
|
icon: "🔒"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Open Source",
|
||||||
|
description: "Free, transparent, and community-driven development.",
|
||||||
|
icon: "💚"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const steps = [
|
||||||
|
{ number: "1", title: "Install", description: "Deploy with Docker or run directly with Bun" },
|
||||||
|
{ number: "2", title: "Connect", description: "Add your GitHub and Gitea credentials" },
|
||||||
|
{ number: "3", title: "Configure", description: "Select repositories or organizations to mirror" },
|
||||||
|
{ number: "4", title: "Relax", description: "Let Gitea Mirror handle the synchronization" }
|
||||||
|
];
|
||||||
|
|
||||||
|
const screenshots = [
|
||||||
|
{ src: "/assets/dashboard.png", alt: "Dashboard view", mobile: "/assets/dashboard_mobile.png" },
|
||||||
|
{ src: "/assets/repositories.png", alt: "Repository management", mobile: "/assets/repositories_mobile.png" },
|
||||||
|
{ src: "/assets/configuration.png", alt: "Configuration interface", mobile: "/assets/configuration_mobile.png" },
|
||||||
|
{ src: "/assets/activity.png", alt: "Activity monitoring", mobile: "/assets/activity_mobile.png" }
|
||||||
|
];
|
||||||
|
---
|
||||||
|
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
|
<meta name="generator" content={Astro.generator} />
|
||||||
|
|
||||||
|
<!-- SEO Meta Tags -->
|
||||||
|
<title>Gitea Mirror - Automated GitHub to Gitea Repository Backup & Sync</title>
|
||||||
|
<meta name="description" content="Automatically mirror and backup your GitHub repositories to self-hosted Gitea. Keep your code safe with scheduled syncing, bulk operations, and real-time monitoring." />
|
||||||
|
<meta name="keywords" content="github backup, gitea mirror, repository sync, github to gitea, git mirror, code backup, self-hosted git, repository migration" />
|
||||||
|
<meta name="robots" content="index, follow" />
|
||||||
|
|
||||||
|
<!-- Open Graph -->
|
||||||
|
<meta property="og:title" content="Gitea Mirror - Automated GitHub to Gitea Repository Backup" />
|
||||||
|
<meta property="og:description" content="Keep your GitHub repositories safely backed up to your self-hosted Gitea instance with automated mirroring." />
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:image" content="/assets/dashboard.png" />
|
||||||
|
|
||||||
|
<!-- Twitter Card -->
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta name="twitter:title" content="Gitea Mirror - GitHub to Gitea Backup Solution" />
|
||||||
|
<meta name="twitter:description" content="Automated repository mirroring from GitHub to self-hosted Gitea instances." />
|
||||||
|
<meta name="twitter:image" content="/assets/dashboard.png" />
|
||||||
|
|
||||||
|
<!-- Canonical URL -->
|
||||||
|
<link rel="canonical" href="https://gitea-mirror.com" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="min-h-screen bg-background text-foreground antialiased">
|
||||||
|
<!-- Hero Section -->
|
||||||
|
<section class="relative overflow-hidden px-6 py-24 sm:py-32 lg:px-8">
|
||||||
|
<div class="absolute inset-0 -z-10 bg-gradient-to-br from-primary/5 via-transparent to-transparent"></div>
|
||||||
|
<div class="animate-float absolute -top-20 -right-20 h-72 w-72 rounded-full bg-primary/10 blur-3xl"></div>
|
||||||
|
<div class="animate-float-delayed absolute -bottom-20 -left-20 h-72 w-72 rounded-full bg-primary/10 blur-3xl"></div>
|
||||||
|
|
||||||
|
<div class="mx-auto max-w-7xl text-center">
|
||||||
|
<div class="animate-fade-in">
|
||||||
|
<h1 class="text-4xl font-bold tracking-tight sm:text-6xl bg-gradient-to-r from-foreground to-foreground/70 bg-clip-text">
|
||||||
|
Keep Your GitHub Code
|
||||||
|
<span class="block text-primary">Safe & Synced</span>
|
||||||
|
</h1>
|
||||||
|
<p class="mt-6 text-lg leading-8 text-muted-foreground max-w-2xl mx-auto">
|
||||||
|
Automatically mirror your GitHub repositories to self-hosted Gitea.
|
||||||
|
Never worry about losing access to your code with continuous backup and synchronization.
|
||||||
|
</p>
|
||||||
|
<div class="mt-10 flex items-center justify-center gap-4 flex-wrap">
|
||||||
|
<Button size="lg" className="animate-fade-in-up" asChild>
|
||||||
|
<a href="https://github.com/yourusername/gitea-mirror" target="_blank" rel="noopener noreferrer">
|
||||||
|
Get Started Free
|
||||||
|
<svg className="ml-2 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</Button>
|
||||||
|
<Button size="lg" variant="outline" className="animate-fade-in-up animation-delay-200" asChild>
|
||||||
|
<a href="https://github.com/yourusername/gitea-mirror" target="_blank" rel="noopener noreferrer">
|
||||||
|
View on GitHub
|
||||||
|
</a>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Problem/Solution Section -->
|
||||||
|
<section class="px-6 py-24 sm:py-32 lg:px-8 bg-muted/30">
|
||||||
|
<div class="mx-auto max-w-7xl">
|
||||||
|
<div class="mx-auto max-w-2xl text-center">
|
||||||
|
<h2 class="text-3xl font-bold tracking-tight sm:text-4xl">Why You Need Repository Mirroring</h2>
|
||||||
|
<p class="mt-6 text-lg leading-8 text-muted-foreground">
|
||||||
|
GitHub is great, but what happens if you lose access? Service outages, account issues, or policy changes
|
||||||
|
can lock you out of your own code. Gitea Mirror ensures you always have a backup on infrastructure you control.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Features Grid -->
|
||||||
|
<section class="px-6 py-24 sm:py-32 lg:px-8">
|
||||||
|
<div class="mx-auto max-w-7xl">
|
||||||
|
<div class="mx-auto max-w-2xl text-center mb-16">
|
||||||
|
<h2 class="text-3xl font-bold tracking-tight sm:text-4xl">Everything You Need for Reliable Backups</h2>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
|
{features.map((feature, index) => (
|
||||||
|
<div
|
||||||
|
class="group relative rounded-2xl border bg-card p-8 hover:shadow-lg transition-all duration-300 hover:-translate-y-1 animate-fade-in-up"
|
||||||
|
style={`animation-delay: ${index * 100}ms`}
|
||||||
|
>
|
||||||
|
<div class="text-4xl mb-4">{feature.icon}</div>
|
||||||
|
<h3 class="text-xl font-semibold mb-2">{feature.title}</h3>
|
||||||
|
<p class="text-muted-foreground">{feature.description}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Screenshots Section -->
|
||||||
|
<section class="px-6 py-24 sm:py-32 lg:px-8 bg-muted/30">
|
||||||
|
<div class="mx-auto max-w-7xl">
|
||||||
|
<div class="mx-auto max-w-2xl text-center mb-16">
|
||||||
|
<h2 class="text-3xl font-bold tracking-tight sm:text-4xl">See It In Action</h2>
|
||||||
|
<p class="mt-6 text-lg leading-8 text-muted-foreground">
|
||||||
|
A clean, intuitive interface for managing your repository mirrors
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="relative">
|
||||||
|
<div class="flex overflow-x-auto gap-6 pb-4 snap-x snap-mandatory scrollbar-hide">
|
||||||
|
{screenshots.map((screenshot, index) => (
|
||||||
|
<div
|
||||||
|
class="flex-none w-full sm:w-[48%] lg:w-[32%] snap-center animate-fade-in-up"
|
||||||
|
style={`animation-delay: ${index * 150}ms`}
|
||||||
|
>
|
||||||
|
<picture>
|
||||||
|
<source media="(max-width: 640px)" srcset={screenshot.mobile} />
|
||||||
|
<img
|
||||||
|
src={screenshot.src}
|
||||||
|
alt={screenshot.alt}
|
||||||
|
class="rounded-lg shadow-2xl w-full"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
</picture>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- How It Works -->
|
||||||
|
<section class="px-6 py-24 sm:py-32 lg:px-8">
|
||||||
|
<div class="mx-auto max-w-7xl">
|
||||||
|
<div class="mx-auto max-w-2xl text-center mb-16">
|
||||||
|
<h2 class="text-3xl font-bold tracking-tight sm:text-4xl">Get Started in Minutes</h2>
|
||||||
|
</div>
|
||||||
|
<div class="mx-auto max-w-3xl">
|
||||||
|
<div class="space-y-8">
|
||||||
|
{steps.map((step, index) => (
|
||||||
|
<div
|
||||||
|
class="flex gap-4 animate-fade-in-left"
|
||||||
|
style={`animation-delay: ${index * 200}ms`}
|
||||||
|
>
|
||||||
|
<div class="flex-none">
|
||||||
|
<div class="flex h-12 w-12 items-center justify-center rounded-full bg-primary text-primary-foreground font-bold text-lg">
|
||||||
|
{step.number}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-auto">
|
||||||
|
<h3 class="text-xl font-semibold mb-1">{step.title}</h3>
|
||||||
|
<p class="text-muted-foreground">{step.description}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Open Source Section -->
|
||||||
|
<section class="px-6 py-24 sm:py-32 lg:px-8 bg-muted/30">
|
||||||
|
<div class="mx-auto max-w-7xl text-center">
|
||||||
|
<div class="mx-auto max-w-2xl">
|
||||||
|
<h2 class="text-3xl font-bold tracking-tight sm:text-4xl mb-6">Free & Open Source</h2>
|
||||||
|
<p class="text-lg leading-8 text-muted-foreground mb-8">
|
||||||
|
Gitea Mirror is completely free and open source. Self-host with confidence knowing you have
|
||||||
|
full control over your infrastructure and data.
|
||||||
|
</p>
|
||||||
|
<div class="rounded-lg border bg-card/50 p-6 mb-8">
|
||||||
|
<p class="text-sm text-muted-foreground">
|
||||||
|
<span class="font-semibold text-foreground">Coming Soon:</span> Support development with an optional
|
||||||
|
supporter tier ($20/year) for priority features and dedicated support.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-center gap-4 flex-wrap">
|
||||||
|
<Button size="lg" asChild>
|
||||||
|
<a href="https://github.com/yourusername/gitea-mirror" target="_blank" rel="noopener noreferrer">
|
||||||
|
View Source Code
|
||||||
|
<svg className="ml-2 h-4 w-4" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer class="border-t">
|
||||||
|
<div class="mx-auto max-w-7xl px-6 py-12 lg:px-8">
|
||||||
|
<div class="flex flex-col items-center justify-between gap-4 sm:flex-row">
|
||||||
|
<p class="text-sm text-muted-foreground">
|
||||||
|
© 2025 Gitea Mirror. Open source under MIT License.
|
||||||
|
</p>
|
||||||
|
<div class="flex gap-6">
|
||||||
|
<a href="https://github.com/yourusername/gitea-mirror" class="text-sm text-muted-foreground hover:text-foreground transition-colors">
|
||||||
|
GitHub
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/yourusername/gitea-mirror/wiki" class="text-sm text-muted-foreground hover:text-foreground transition-colors">
|
||||||
|
Documentation
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/yourusername/gitea-mirror/issues" class="text-sm text-muted-foreground hover:text-foreground transition-colors">
|
||||||
|
Support
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Smooth scroll for screenshot carousel */
|
||||||
|
.scrollbar-hide {
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
scrollbar-width: none;
|
||||||
|
}
|
||||||
|
.scrollbar-hide::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CSS Animations */
|
||||||
|
@keyframes fade-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in-up {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in-left {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes float {
|
||||||
|
0%, 100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translateY(-20px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-fade-in {
|
||||||
|
animation: fade-in 0.6s ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-fade-in-up {
|
||||||
|
opacity: 0;
|
||||||
|
animation: fade-in-up 0.6s ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-fade-in-left {
|
||||||
|
opacity: 0;
|
||||||
|
animation: fade-in-left 0.6s ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-float {
|
||||||
|
animation: float 6s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-float-delayed {
|
||||||
|
animation: float 6s ease-in-out 3s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animation-delay-200 {
|
||||||
|
animation-delay: 200ms;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
16
www/src/pages/markdown-page.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
title: 'Markdown + Tailwind'
|
||||||
|
layout: ../layouts/main.astro
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="grid place-items-center h-screen content-center">
|
||||||
|
<div class="py-2 px-4 bg-purple-500 text-white font-semibold rounded-lg shadow-md">
|
||||||
|
Tailwind classes also work in Markdown!
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
href="/"
|
||||||
|
class="p-4 underline hover:text-purple-500 transition-colors ease-in-out duration-200"
|
||||||
|
>
|
||||||
|
Go home
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
120
www/src/styles/global.css
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
@import 'tailwindcss';
|
||||||
|
@import "tw-animate-css";
|
||||||
|
|
||||||
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
|
@theme inline {
|
||||||
|
--radius-sm: calc(var(--radius) - 4px);
|
||||||
|
--radius-md: calc(var(--radius) - 2px);
|
||||||
|
--radius-lg: var(--radius);
|
||||||
|
--radius-xl: calc(var(--radius) + 4px);
|
||||||
|
--color-background: var(--background);
|
||||||
|
--color-foreground: var(--foreground);
|
||||||
|
--color-card: var(--card);
|
||||||
|
--color-card-foreground: var(--card-foreground);
|
||||||
|
--color-popover: var(--popover);
|
||||||
|
--color-popover-foreground: var(--popover-foreground);
|
||||||
|
--color-primary: var(--primary);
|
||||||
|
--color-primary-foreground: var(--primary-foreground);
|
||||||
|
--color-secondary: var(--secondary);
|
||||||
|
--color-secondary-foreground: var(--secondary-foreground);
|
||||||
|
--color-muted: var(--muted);
|
||||||
|
--color-muted-foreground: var(--muted-foreground);
|
||||||
|
--color-accent: var(--accent);
|
||||||
|
--color-accent-foreground: var(--accent-foreground);
|
||||||
|
--color-destructive: var(--destructive);
|
||||||
|
--color-border: var(--border);
|
||||||
|
--color-input: var(--input);
|
||||||
|
--color-ring: var(--ring);
|
||||||
|
--color-chart-1: var(--chart-1);
|
||||||
|
--color-chart-2: var(--chart-2);
|
||||||
|
--color-chart-3: var(--chart-3);
|
||||||
|
--color-chart-4: var(--chart-4);
|
||||||
|
--color-chart-5: var(--chart-5);
|
||||||
|
--color-sidebar: var(--sidebar);
|
||||||
|
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||||
|
--color-sidebar-primary: var(--sidebar-primary);
|
||||||
|
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||||
|
--color-sidebar-accent: var(--sidebar-accent);
|
||||||
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||||
|
--color-sidebar-border: var(--sidebar-border);
|
||||||
|
--color-sidebar-ring: var(--sidebar-ring);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--radius: 0.625rem;
|
||||||
|
--background: oklch(1 0 0);
|
||||||
|
--foreground: oklch(0.145 0 0);
|
||||||
|
--card: oklch(1 0 0);
|
||||||
|
--card-foreground: oklch(0.145 0 0);
|
||||||
|
--popover: oklch(1 0 0);
|
||||||
|
--popover-foreground: oklch(0.145 0 0);
|
||||||
|
--primary: oklch(0.205 0 0);
|
||||||
|
--primary-foreground: oklch(0.985 0 0);
|
||||||
|
--secondary: oklch(0.97 0 0);
|
||||||
|
--secondary-foreground: oklch(0.205 0 0);
|
||||||
|
--muted: oklch(0.97 0 0);
|
||||||
|
--muted-foreground: oklch(0.556 0 0);
|
||||||
|
--accent: oklch(0.97 0 0);
|
||||||
|
--accent-foreground: oklch(0.205 0 0);
|
||||||
|
--destructive: oklch(0.577 0.245 27.325);
|
||||||
|
--border: oklch(0.922 0 0);
|
||||||
|
--input: oklch(0.922 0 0);
|
||||||
|
--ring: oklch(0.708 0 0);
|
||||||
|
--chart-1: oklch(0.646 0.222 41.116);
|
||||||
|
--chart-2: oklch(0.6 0.118 184.704);
|
||||||
|
--chart-3: oklch(0.398 0.07 227.392);
|
||||||
|
--chart-4: oklch(0.828 0.189 84.429);
|
||||||
|
--chart-5: oklch(0.769 0.188 70.08);
|
||||||
|
--sidebar: oklch(0.985 0 0);
|
||||||
|
--sidebar-foreground: oklch(0.145 0 0);
|
||||||
|
--sidebar-primary: oklch(0.205 0 0);
|
||||||
|
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||||
|
--sidebar-accent: oklch(0.97 0 0);
|
||||||
|
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||||
|
--sidebar-border: oklch(0.922 0 0);
|
||||||
|
--sidebar-ring: oklch(0.708 0 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: oklch(0.145 0 0);
|
||||||
|
--foreground: oklch(0.985 0 0);
|
||||||
|
--card: oklch(0.205 0 0);
|
||||||
|
--card-foreground: oklch(0.985 0 0);
|
||||||
|
--popover: oklch(0.205 0 0);
|
||||||
|
--popover-foreground: oklch(0.985 0 0);
|
||||||
|
--primary: oklch(0.922 0 0);
|
||||||
|
--primary-foreground: oklch(0.205 0 0);
|
||||||
|
--secondary: oklch(0.269 0 0);
|
||||||
|
--secondary-foreground: oklch(0.985 0 0);
|
||||||
|
--muted: oklch(0.269 0 0);
|
||||||
|
--muted-foreground: oklch(0.708 0 0);
|
||||||
|
--accent: oklch(0.269 0 0);
|
||||||
|
--accent-foreground: oklch(0.985 0 0);
|
||||||
|
--destructive: oklch(0.704 0.191 22.216);
|
||||||
|
--border: oklch(1 0 0 / 10%);
|
||||||
|
--input: oklch(1 0 0 / 15%);
|
||||||
|
--ring: oklch(0.556 0 0);
|
||||||
|
--chart-1: oklch(0.488 0.243 264.376);
|
||||||
|
--chart-2: oklch(0.696 0.17 162.48);
|
||||||
|
--chart-3: oklch(0.769 0.188 70.08);
|
||||||
|
--chart-4: oklch(0.627 0.265 303.9);
|
||||||
|
--chart-5: oklch(0.645 0.246 16.439);
|
||||||
|
--sidebar: oklch(0.205 0 0);
|
||||||
|
--sidebar-foreground: oklch(0.985 0 0);
|
||||||
|
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||||
|
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||||
|
--sidebar-accent: oklch(0.269 0 0);
|
||||||
|
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||||
|
--sidebar-border: oklch(1 0 0 / 10%);
|
||||||
|
--sidebar-ring: oklch(0.556 0 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
@apply border-border outline-ring/50;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
@apply bg-background text-foreground;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
www/tsconfig.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"extends": "astro/tsconfigs/strict",
|
||||||
|
"include": [
|
||||||
|
".astro/types.d.ts",
|
||||||
|
"**/*"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"jsxImportSource": "react",
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"./src/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||