Improve repo tooling (#398)

* add basic git files to root

* make server part of monorepo

* import promo

* import libraries base

* import docs

* import desktop

* move docs and promo
This commit is contained in:
Husky
2026-04-20 11:44:38 +10:00
committed by GitHub
parent 5bbe406e4c
commit ff1144e016
177 changed files with 22218 additions and 37822 deletions
+171
View File
@@ -0,0 +1,171 @@
import { AnimatedNumber } from '@/components/animated-number'
import { Container } from '@/components/container'
import { Footer } from '@/components/footer'
import { GradientBackground } from '@/components/gradient'
import { Navbar } from '@/components/navbar'
import { Team } from '@/components/team'
import { Heading, Lead, Subheading } from '@/components/text'
import type { Metadata } from 'next'
import Link from 'next/link'
export const metadata: Metadata = {
title: 'About',
description:
'About Drop OSS - what we do, what we believe, and what we aim to achieve.',
}
function Header() {
return (
<Container className="mt-16">
<Heading as="h1">An open Steam.</Heading>
<Lead className="mt-6 max-w-3xl">
We&apos;re building an open-source alternative to platforms like Steam
and Epic Games, enabling anyone to have full control over their gaming.
</Lead>
<section className="mt-16 grid grid-cols-1 lg:grid-cols-2 lg:gap-12">
<div className="max-w-lg">
<h2 className="text-2xl font-medium tracking-tight">Our mission</h2>
<p className="mt-6 text-sm/6 text-zinc-400">
We aim to build a &quot;Steam-like experience for DRM-free
games&quot;. One of the major sticking points of DRM games, and why
platforms like Steam have done so well, is that they provide awesome
features like worldwide play-together, social features, and useful
overlays and tools. We aim to replicate this for <i>any</i> game.
</p>
<p className="mt-8 text-sm/6 text-zinc-400">
Don&apos;t get us wrong, we don&apos;t think Steam or Valve is bad.
They are unusually consumer-friendly, and provide a great service.
They will always have a place, providing a marketplace for DRM
games. Drop aims to be the alternative.
</p>
</div>
<div className="max-lg:mt-16 lg:col-span-1">
<Subheading>The Numbers</Subheading>
<hr className="mt-6 border-t border-zinc-800" />
<dl className="mt-6 grid grid-cols-1 gap-x-8 gap-y-4 sm:grid-cols-2">
<div className="flex flex-col gap-y-2 border-b border-dotted border-zinc-800 pb-4">
<dt className="text-sm/6 text-zinc-400">Lines of code</dt>
<dd className="order-first text-6xl font-medium tracking-tight">
<AnimatedNumber start={10} end={40} />k
</dd>
</div>
<div className="flex flex-col gap-y-2 border-b border-dotted border-zinc-800 pb-4">
<dt className="text-sm/6 text-zinc-400">
Individual open-source projects
</dt>
<dd className="order-first text-6xl font-medium tracking-tight">
&gt;
<AnimatedNumber start={0} end={5} />
</dd>
</div>
<div className="flex flex-col gap-y-2 max-sm:border-b max-sm:border-dotted max-sm:border-gray-200 max-sm:pb-4">
<dt className="text-sm/6 text-zinc-400">Docker pulls</dt>
<dd className="order-first text-6xl font-medium tracking-tight">
<AnimatedNumber start={0} end={48.8} decimals={1} />k
</dd>
</div>
<div className="flex flex-col gap-y-2">
<dt className="text-sm/6 text-zinc-400">Contributors</dt>
<dd className="order-first text-6xl font-medium tracking-tight">
&gt;
<AnimatedNumber start={0} end={15} />
</dd>
</div>
</dl>
</div>
</section>
</Container>
)
}
function FrequentlyAskedQuestions() {
return (
<Container className="mt-32">
<section id="faqs" className="scroll-mt-8">
<Subheading className="">Frequently asked questions</Subheading>
<Heading as="div" className="mt-2">
Your questions answered.
</Heading>
<div className="mt-16 mb-32 max-w-xl space-y-12">
<dl>
<dt className="text-sm font-semibold">
Do you intend to replace Steam?
</dt>
<dd className="mt-4 text-sm/6 text-zinc-400">
No. Drop is not a replacement for Steam, in the sense that we will
ever offer a marketplace for developers to sell games. Drop can
replace Steam <i>for an individual</i>, if they only played
DRM-free games that they bought from outside of Steam (like GOG).
</dd>
</dl>
<dl>
<dt className="text-sm font-semibold">
Will Drop ever cost money or require a subscription?
</dt>
<dd className="mt-4 text-sm/6 text-zinc-400">
We believe in <strong>paying for services, not code</strong>. All
our projects are and always will be open source (AGPLv3), and we
endeavour to allow users to self-host as much of it as possible.
Where that is not possible, we may launch a subscription service -
likely as a feature for multiplayer, to play between self-hosted
Drop instances. Naturally, this service will also be available to
self-host.
</dd>
</dl>
<dl>
<dt className="text-sm font-semibold">Is Drop legal?</dt>
<dd className="mt-4 text-sm/6 text-zinc-400">
Yes. Officially, Drop is <strong>only</strong> for DRM-free games,
like the ones you purchase from GOG or download from itch.io,{' '}
<strong>and</strong> that you have a license to redistribute. We
cannot control whether you use Drop with cracked, pirated, or
DRM-bypassed games or not, and do not condone the use of
torrenting and automation software to automatically download
games.
</dd>
</dl>
<dl>
<dt className="text-sm font-semibold">How can I support Drop or get involved?</dt>
<dd className="mt-4 text-sm/6 text-zinc-400">
Thank you for helping us out! If you&apos;re looking to contribute
code, check out our{' '}
<Link
href="https://developer.droposs.org/contributing"
className="text-blue-500 hover:underline"
target="_blank"
>
developer contributing guide
</Link>
. If you&apos;re looking to contribute financially, you can join
our{' '}
<Link
href="https://opencollective.com/drop-oss"
className="text-blue-500 hover:underline"
target="_blank"
>
OpenCollective
</Link>
, or click on individual contributors above and add to their
personal pockets.
</dd>
</dl>
</div>
</section>
</Container>
)
}
export default function About() {
return (
<main className="overflow-hidden">
<GradientBackground />
<Container>
<Navbar />
</Container>
<Header />
<Team />
<FrequentlyAskedQuestions />
<Footer />
</main>
)
}
+14
View File
@@ -0,0 +1,14 @@
import Comparison from "@/components/comparison";
import type { Metadata } from "next";
import { Suspense } from "react";
export const metadata: Metadata = {
title: 'Comparison',
description:
'A breakdown of the different projects you may be interested in, and the pros & cons of each.',
}
export default function ComparisonPage() {
return <Suspense><Comparison /></Suspense>
}
+42
View File
@@ -0,0 +1,42 @@
import { Container } from '@/components/container'
import DownloadCards from '@/components/downloads'
import { Footer } from '@/components/footer'
import { GradientBackground } from '@/components/gradient'
import { Navbar } from '@/components/navbar'
import { Heading, Lead, Subheading } from '@/components/text'
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Download',
description: 'Download the latest version of the Drop client.',
}
function Header() {
return (
<Container className="mt-16">
<Heading as="h1" className="leading-12">
Download
</Heading>
<Lead className="mt-6 max-w-3xl">
Download the latest version of the Drop Desktop Client for your platform.
</Lead>
<Subheading className="mt-2">Last updated 15-11-2025</Subheading>
</Container>
)
}
export default function DownloadPage() {
return (
<main className="overflow-hidden">
<GradientBackground />
<Container>
<Navbar />
</Container>
<Header />
<DownloadCards />
<Footer />
</main>
)
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

+25
View File
@@ -0,0 +1,25 @@
import { Container } from '@/components/container'
import { Footer } from '@/components/footer'
import { Gallery } from '@/components/gallery-modal'
import { GradientBackground } from '@/components/gradient'
import { Navbar } from '@/components/navbar'
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Gallery',
description: 'A collection of screenshots, across all components of Drop.',
}
export default function About() {
return (
<main className="overflow-hidden">
<GradientBackground />
<Container>
<Navbar />
</Container>
<Gallery />
<Footer />
</main>
)
}
+23
View File
@@ -0,0 +1,23 @@
import '@/styles/tailwind.css'
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: {
template: '%s - Drop OSS',
default: 'Drop - An open Steam',
},
}
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode
}>) {
return (
<html lang="en">
<body className="text-zinc-100 bg-zinc-950 antialiased">
{children}
</body>
</html>
)
}
+104
View File
@@ -0,0 +1,104 @@
import { Button } from '@/components/button'
import { Container } from '@/components/container'
import Content from '@/components/content'
import { Footer } from '@/components/footer'
import { GradientBackground } from '@/components/gradient'
import { Navbar } from '@/components/navbar'
import { fetchPostAuthors } from '@/components/post'
import { Heading, Subheading } from '@/components/text'
import { ChevronLeftIcon } from '@heroicons/react/16/solid'
import { allPosts } from 'content-collections'
import dayjs from 'dayjs'
import { notFound } from 'next/navigation'
export const generateStaticParams = async () =>
allPosts.map((post) => ({ slug: post._meta.path }))
export const generateMetadata = async ({
params,
}: {
params: Promise<{ slug: string }>
}) => {
const aParams = await params
const post = allPosts.find((post) => post._meta.path === aParams.slug)
if (!post) notFound()
return { title: post.title, description: post.excerpt }
}
export default async function BlogPost({
params,
}: {
params: Promise<{ slug: string }>
}) {
const aParams = await params
const post = allPosts.find((post) => post._meta.path === aParams.slug)
if (!post) notFound()
const postAuthors = fetchPostAuthors()
const author = post.author ? postAuthors[post.author] : undefined
const tags = (post.tags ?? '').split(',').map((e) => e.trim())
return (
<main className="overflow-hidden">
<GradientBackground />
<Container>
<Navbar />
<Subheading className="mt-16">
{dayjs(post.date).format('dddd, MMMM D, YYYY')}
</Subheading>
<Heading as="h1" className="mt-2">
{post.title}
</Heading>
<div className="mt-16 grid grid-cols-1 gap-8 pb-24 lg:grid-cols-[15rem_1fr] xl:grid-cols-[15rem_1fr_15rem]">
<div className="flex flex-wrap items-center gap-8 max-lg:justify-between lg:flex-col lg:items-start">
{author && (
<div className="flex items-center gap-3">
{author.avatar && (
<img
alt=""
src={author.avatar}
className="aspect-square size-6 rounded-full object-cover"
/>
)}
<div className="text-sm/5 text-zinc-300">{author.name}</div>
</div>
)}
{
<div className="flex flex-wrap gap-2">
{tags.map((tag) => (
<div
key={tag}
className="rounded-full border border-dotted border-zinc-800 bg-zinc-900 px-2 text-sm/6 font-medium text-zinc-400"
>
{tag}
</div>
))}
</div>
}
</div>
<div className="text-zinc-400">
<div className="max-w-2xl xl:mx-auto">
{post.image && (
<img
alt={post.title}
src={post.image}
className="mb-10 aspect-3/2 w-full rounded-2xl object-cover shadow-xl"
/>
)}
<Content content={post} />
<div className="mt-10">
<Button variant="outline" href="/news">
<ChevronLeftIcon className="size-4" />
Back to news
</Button>
</div>
</div>
</div>
</div>
</Container>
<Footer />
</main>
)
}
+12
View File
@@ -0,0 +1,12 @@
import News from '@/components/news'
import type { Metadata } from 'next'
import { Suspense } from 'react'
export const metadata: Metadata = {
title: 'News',
description: 'Stay up to date with updates and information about Drop.',
}
export default function NewsPage() {
return <Suspense><News /></Suspense>
}
+34
View File
@@ -0,0 +1,34 @@
import { Container } from '@/components/container'
import { Footer } from '@/components/footer'
import { GradientBackground } from '@/components/gradient'
import { Navbar } from '@/components/navbar'
import { Heading, Subheading } from '@/components/text'
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Not Found',
description: "We couldn't find this page, or doesn't exist.",
}
export default function About() {
return (
<main className="overflow-hidden">
<GradientBackground />
<Container>
<Navbar />
</Container>
<main className="grid min-h-full place-items-center px-6 py-24 sm:py-32 lg:px-8">
<div className="text-center">
<Subheading>404</Subheading>
<Heading>
Page not found
</Heading>
<p className="mt-6 text-lg font-medium text-pretty text-gray-500 sm:text-xl/8">
Sorry, we couldn&apos;t find the page you&apos;re looking for.
</p>
</div>
</main>
<Footer />
</main>
)
}
+176
View File
@@ -0,0 +1,176 @@
import { BentoCard } from '@/components/bento-card'
import { Button } from '@/components/button'
import { Container } from '@/components/container'
import { Footer } from '@/components/footer'
import { Gradient } from '@/components/gradient'
import { LogoCluster } from '@/components/logo-cluster'
import { Navbar } from '@/components/navbar'
import { Heading, Subheading } from '@/components/text'
import { ArrowDownCircleIcon } from '@heroicons/react/24/solid'
import type { Metadata } from 'next'
export const metadata: Metadata = {
description:
'Drop is an open-source, self-hostabled alternative to platforms like Steam and Epic Games.',
}
function Hero() {
return (
<div className="relative">
<Gradient className="absolute inset-2 bottom-0 rounded-4xl ring-1 ring-black/5 ring-inset" />
<Container className="relative">
<Navbar />
<div className="pt-16 pb-24 sm:pt-24 sm:pb-32 md:pt-32 md:pb-48">
<h1 className="font-display text-6xl/[0.9] font-medium tracking-tight text-balance text-zinc-100 sm:text-8xl/[0.8] md:text-9xl/[0.8]">
An open Steam.
</h1>
<p className="mt-8 max-w-lg text-xl/7 font-medium text-zinc-100/75 sm:text-2xl/8">
Drop is an open-source, self-hosted alternative to platforms like
Steam and Epic.
</p>
<div className="mt-12 flex flex-col gap-x-6 gap-y-4 sm:flex-row">
<Button href="https://docs.droposs.org/docs/guides/quickstart">
Get started
</Button>
<Button variant="outline" href="/about">
About
</Button>
</div>
</div>
</Container>
</div>
)
}
function FeatureSection() {
return (
<div className="py-24 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="mx-auto max-w-2xl sm:text-center">
<Subheading>Drop OSS</Subheading>
<Heading as="h3" className="mt-2 max-w-4xl">
The ultimate self-hosted game manager.
</Heading>
<p className="mt-6 text-lg/8 text-zinc-400">
Drop is built from the ground up to be flexible, fast, and
beautiful. It's designed to scale with your library, and handle
thousands of games.
</p>
</div>
</div>
<div className="relative overflow-hidden pt-16">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<img
alt="App screenshot"
src="/gallery/store.png"
width={1920}
height={1071}
className="mb-[-5%] rounded-xl shadow-2xl ring-1 ring-gray-900/10"
/>
<div aria-hidden="true" className="relative">
<div className="absolute -inset-x-20 bottom-0 bg-linear-to-t from-zinc-950 pt-[7%]" />
</div>
</div>
</div>
<div className="mx-auto mt-16 max-w-7xl px-6 sm:mt-20 md:mt-24 lg:px-8">
<dl className="mx-auto grid max-w-2xl grid-cols-1 gap-x-6 gap-y-10 text-base/7 text-zinc-400 sm:grid-cols-2 lg:mx-0 lg:max-w-none lg:grid-cols-3 lg:gap-x-8 lg:gap-y-16">
<div className="relative pl-9">
<dt className="inline font-semibold text-zinc-100">
<ArrowDownCircleIcon
aria-hidden="true"
className="absolute top-1 left-1 size-5 text-blue-600"
/>
ADASDASD
</dt>{' '}
<dd className="inline">ASDASDASDAS</dd>
</div>
</dl>
</div>
</div>
)
}
function BentoSection() {
return (
<Container>
<Subheading>Features</Subheading>
<Heading as="h3" className="mt-2 max-w-3xl">
Upgrade your games library.
</Heading>
<div className="mt-10 grid grid-cols-1 gap-4 sm:mt-16 lg:grid-cols-6 lg:grid-rows-2">
<BentoCard
eyebrow="Metadata"
title="Rich metadata editing"
description="Drop has a rich metadata editor - you can use Markdown, images, and update icons, descriptions, and names."
graphic={
<div className="flex h-full w-full items-center justify-center p-4">
<div className="bg-position-center h-full w-full grow rounded-lg bg-[url(/screenshots/metadata.webp)] bg-cover bg-no-repeat" />
</div>
}
fade={['bottom']}
className="max-lg:rounded-t-4xl lg:col-span-3 lg:rounded-tl-4xl"
/>
<BentoCard
eyebrow="Store"
title="Let your users discover games"
description="Drop has a fully featured store, where your users can discover and filter your game library, and create collections of their favourite games."
graphic={
<div className="flex h-full w-full items-center justify-center p-4">
<div className="bg-position-center h-full w-full grow rounded-lg bg-[url(/screenshots/storepage.png)] bg-cover bg-no-repeat" />
</div>
}
fade={['bottom']}
className="lg:col-span-3 lg:rounded-tr-4xl"
/>
<BentoCard
eyebrow="Authentication"
title="Flexible authentication"
description="Drop supports both simple and SSO authentication, with more features like SCIM on the way."
graphic={
<div className="flex h-full w-full items-center justify-center p-4">
<div className="bg-position-center h-full w-full grow rounded-lg bg-[url(/screenshots/authentication.png)] bg-cover bg-no-repeat" />
</div>
}
fade={['bottom']}
className="lg:col-span-2 lg:rounded-bl-4xl"
/>
<BentoCard
eyebrow="Metadata"
title="Automatically import metadata"
description="Drop can import metadata for your games from platforms like IGDB, GiantBomb, and PCGamingWiki."
graphic={<LogoCluster />}
className="lg:col-span-2"
/>
<BentoCard
eyebrow="News"
title="Keep your users up-to-date with server news"
description="Admins can write news articles that appear in-browser and client, to keep users up-to-date."
fade={['bottom']}
graphic={
<div className="flex h-full w-full items-center justify-center p-4">
<div className="bg-position-center h-full w-full grow rounded-lg bg-[url(/screenshots/news.png)] bg-cover bg-no-repeat" />
</div>
}
className="max-lg:rounded-b-4xl lg:col-span-2 lg:rounded-br-4xl"
/>
</div>
</Container>
)
}
export default function Home() {
return (
<div className="overflow-hidden">
<Hero />
<main>
<div className="bg-linear-to-b pb-16">
<FeatureSection />
<BentoSection />
</div>
</main>
<Footer />
</div>
)
}
+72
View File
@@ -0,0 +1,72 @@
import { Button } from '@/components/button'
import { Container } from '@/components/container'
import { Footer } from '@/components/footer'
import { GradientBackground } from '@/components/gradient'
import { Navbar } from '@/components/navbar'
import { Sponsors } from '@/components/sponsors'
import { Heading, Lead, Subheading } from '@/components/text'
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Sponsors',
description: 'Thanking the people who contribute financially to the project.',
}
function CTA() {
return (
<Container className="py-32">
<Subheading>Contribute</Subheading>
<Heading as="h3" className="mt-2">
How can I help out?
</Heading>
<Lead className="mt-6 max-w-3xl">
Sign up to our OpenCollective, or become a GitHub sponsor to give back
to the project.
</Lead>
<div className="mt-12 grid grid-cols-1 gap-12 lg:grid-cols-2">
<div className="max-w-lg">
<p className="text-sm/6 text-zinc-400">
If you&apos;re looking to give back to the project financially -
first off, thank you. It really does help a lot. There are two
options for contributing: our OpenCollective, which funds
project-only expenses, like infrastructure and domain names, and
GitHub Sponsors, which goes directly to developers, and help them
pay the bills and keep them motivated.
</p>
<div className="mt-6 space-x-4">
<Button
className="w-full sm:w-auto"
href="https://opencollective.com/drop-oss"
target="_blank"
>
OpenCollective &rarr;
</Button>
<Button
className="w-full sm:w-auto"
href="/about#team"
>
Team &rarr;
</Button>
</div>
</div>
</div>
</Container>
)
}
export default function About() {
return (
<main className="overflow-hidden">
<GradientBackground />
<Container>
<Navbar />
</Container>
<Sponsors />
<CTA />
<Footer />
</main>
)
}