Comet landing page.

feat: initial basic roadmap

ovyerus.com fc23ff38 93cd07dc

verified
Changed files
+226 -17
src
+1
package.json
···
},
"dependencies": {
"@fontsource-variable/work-sans": "^5.2.6",
"@tailwindcss/vite": "^4.1.11",
"astro": "^5.12.9",
"simple-icons-astro": "^15.10.0",
···
},
"dependencies": {
"@fontsource-variable/work-sans": "^5.2.6",
+
"@lucide/astro": "^0.539.0",
"@tailwindcss/vite": "^4.1.11",
"astro": "^5.12.9",
"simple-icons-astro": "^15.10.0",
+12
pnpm-lock.yaml
···
'@fontsource-variable/work-sans':
specifier: ^5.2.6
version: 5.2.6
'@tailwindcss/vite':
specifier: ^4.1.11
version: 4.1.11(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1))
···
'@jridgewell/trace-mapping@0.3.30':
resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==}
'@oslojs/encoding@1.1.0':
resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==}
···
dependencies:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
'@oslojs/encoding@1.1.0': {}
···
'@fontsource-variable/work-sans':
specifier: ^5.2.6
version: 5.2.6
+
'@lucide/astro':
+
specifier: ^0.539.0
+
version: 0.539.0(astro@5.12.9(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(rollup@4.46.2)(typescript@5.9.2))
'@tailwindcss/vite':
specifier: ^4.1.11
version: 4.1.11(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1))
···
'@jridgewell/trace-mapping@0.3.30':
resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==}
+
+
'@lucide/astro@0.539.0':
+
resolution: {integrity: sha512-PCAo48RsERkmyWX+bmssGlu0mb84cP8kLMyc1JuW5pRY4zkx7cnyZMr3RkfPFvDKJZf2/2KPoaZz8j6BjXaXcA==}
+
peerDependencies:
+
astro: ^4 || ^5
'@oslojs/encoding@1.1.0':
resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==}
···
dependencies:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
+
+
'@lucide/astro@0.539.0(astro@5.12.9(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(rollup@4.46.2)(typescript@5.9.2))':
+
dependencies:
+
astro: 5.12.9(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(rollup@4.46.2)(typescript@5.9.2)
'@oslojs/encoding@1.1.0': {}
+100
src/components/Roadmap.astro
···
···
+
---
+
import {
+
AppWindow,
+
Cloudy,
+
Copyright,
+
DatabaseZap,
+
FlaskConical,
+
LibraryBig,
+
Rss,
+
Scale,
+
Share2,
+
} from "@lucide/astro";
+
import RoadmapItem from "./RoadmapItem.astro";
+
---
+
+
<section class="flex flex-col gap-4 text-left">
+
<h2 class="text-4xl font-bold">Roadmap</h2>
+
<p>
+
A rough roadmap of what we have planned for the future. Subject to change at
+
any time.
+
</p>
+
+
<ol class="flex flex-col">
+
<RoadmapItem
+
title="Foundational libraries"
+
status="in-progress"
+
icon={LibraryBig}
+
>
+
<p>
+
From-scratch Elixir libraries and tooling to interact with the AT
+
Protocol.
+
</p>
+
<ul class="mt-0.5 list-disc">
+
<li class="ml-6">
+
<a
+
href="https://github.com/cometsh/atex"
+
class="inline-block underline"
+
>
+
atex
+
</a> - Core utilities and frameworks for AT Protocol
+
</li>
+
<li class="ml-6">
+
<a
+
href="https://github.com/cometsh/drinkup"
+
class="inline-block underline"
+
>
+
Drinkup
+
</a>
+
- AT Protocol firehose & subscription listener
+
</li>
+
</ul>
+
</RoadmapItem>
+
+
<RoadmapItem title="Basic MVP" icon={AppWindow}>
+
Basic chronological timeline, follows, uploading of music, and playback.
+
Private testing.
+
</RoadmapItem>
+
+
<RoadmapItem title="Audio CDN" icon={Cloudy}>
+
Custom CDN for converting uploaded audio to streaming-friendly formats for
+
the music player, reducing strain on self-hosters and mobile bandwidth.
+
</RoadmapItem>
+
+
<RoadmapItem title="Alpha release" icon={FlaskConical} />
+
+
<RoadmapItem title="Social graph" icon={Share2}>
+
Expand the basic follow-only social graph with blocks and mutes applying
+
to commenting on tracks and playlists.
+
</RoadmapItem>
+
+
<RoadmapItem title="Discovery algorithm & custom feeds" icon={Rss}>
+
Create a discovery/recommendations algorithm for finding new content.
+
Additionally, create tools to allow the creation of custom feeds (and
+
autoplay?) for those who want them
+
</RoadmapItem>
+
+
<RoadmapItem title="Moderation" icon={Scale}>
+
Moderation tools for dealing with offensive content, modelled after
+
Bluesky's labelling system.
+
</RoadmapItem>
+
+
<RoadmapItem title="Copyright" icon={Copyright}>
+
A system to deal with copyright violations and piracy on the platform,
+
forwarding DMCA requests to PDS hosts and blocking content from the app
+
where needed, while still fighting for fair use and remix culture.
+
</RoadmapItem>
+
+
<!-- <RoadmapItem title="Out of alpha" icon={Rocket}>
+
Our UI and UX should be nice and shiny, and most major features should be
+
implemented. We're ready for adoption!
+
</RoadmapItem> -->
+
+
<RoadmapItem title="Hosted PDS offering" icon={DatabaseZap}>
+
Provide a hosted PDS offering with increased storage limits to allow
+
uploading longer tracks (e.g. mixes and podcasts) or other larger AT
+
Protocol media, without having to deal with the technical hassle of
+
self-hosting.
+
</RoadmapItem>
+
</ol>
+
</section>
+104
src/components/RoadmapItem.astro
···
···
+
---
+
import { Check, Lightbulb, Clock, type Icon as IconType } from "@lucide/astro";
+
+
const statusStrings = {
+
planned: "Planned",
+
"in-progress": "In Progress",
+
complete: "Complete",
+
} as const;
+
+
interface Props {
+
title: string;
+
icon: typeof IconType;
+
status?: "planned" | "in-progress" | "complete";
+
}
+
+
const { title, icon: Icon, status = "planned" } = Astro.props;
+
let StatusIcon: typeof IconType;
+
+
switch (status) {
+
case "planned":
+
StatusIcon = Lightbulb;
+
break;
+
case "in-progress":
+
StatusIcon = Clock;
+
break;
+
case "complete":
+
StatusIcon = Check;
+
break;
+
}
+
---
+
+
<li class="roadmap-item">
+
<div
+
class="icon flex size-10 items-center justify-center rounded-lg border-2 border-black/20 bg-black/50"
+
>
+
<Icon aria-hidden="true" />
+
</div>
+
+
<article class="flex flex-col items-start gap-0.5">
+
<p
+
class="flex items-center gap-1 rounded-full border-2 border-black/20 px-2 py-0.5 text-xs tracking-wide uppercase"
+
class:list={{
+
"bg-black/50": status === "planned",
+
"bg-purple-950/70": status === "in-progress",
+
"bg-emerald-950/70": status === "complete",
+
}}
+
>
+
<StatusIcon size={12} aria-hidden="true" />
+
{statusStrings[status]}
+
</p>
+
<h3 class="text-lg font-semibold">{title}</h3>
+
<slot />
+
</article>
+
+
<hr aria-hidden="true" />
+
<hr aria-hidden="true" />
+
</li>
+
+
<style>
+
@reference "../style/main.css";
+
+
.roadmap-item {
+
display: grid;
+
grid-template-columns: 40px 1fr;
+
grid-template-rows: 2rem 40px auto;
+
grid-template-areas:
+
"top-vr blank"
+
"icon content"
+
"bottom-vr content";
+
+
& > div {
+
grid-area: icon;
+
}
+
+
& > article {
+
grid-area: content;
+
padding-left: 2rem;
+
}
+
+
& > hr {
+
@apply bg-black/60;
+
width: 0.25rem;
+
height: 100%;
+
border: none;
+
margin: 0 auto;
+
+
&:first-of-type {
+
grid-area: top-vr;
+
}
+
+
&:last-of-type {
+
grid-area: bottom-vr;
+
}
+
}
+
+
&:first-child > hr:first-of-type {
+
display: none;
+
}
+
+
&:last-child > hr:last-of-type {
+
display: none;
+
}
+
}
+
</style>
+1 -14
src/layouts/Layout.astro
···
<script
defer
src="https://stats.ovy.sh/script.js"
-
data-website-id="8ef06cd9-9afa-4b67-a4ce-789d4b2a1edf"
-
>
-
</script>
</head>
<body>
<slot />
</body>
</html>
-
-
<style>
-
html,
-
body {
-
margin: 0;
-
width: 100%;
-
height: 100%;
-
min-width: 100vw;
-
min-height: 100vh;
-
}
-
</style>
···
<script
defer
src="https://stats.ovy.sh/script.js"
+
data-website-id="8ef06cd9-9afa-4b67-a4ce-789d4b2a1edf"></script>
</head>
<body>
<slot />
</body>
</html>
+8 -3
src/pages/index.astro
···
---
import Logo from "../assets/logo.svg";
import Layout from "../layouts/Layout.astro";
import { Bluesky, Discord, Github } from "simple-icons-astro";
---
<Layout>
<main
-
class="flex flex-col items-center justify-center gap-3 text-center leading-none"
>
<div class="flex items-center justify-center gap-4">
<Logo width="48px" height="48px" class="rounded-md shadow-md" />
<h1 class="text-[64px] font-bold">Comet</h1>
</div>
<p class="leading-tight">
Decentralised music streaming on ATProtocol.<br />Coming soon.
</p>
-
<p class="flex items-center gap-2 font-semibold">
<span class="mr-2">Follow us:</span>
<a href="https://bsky.app/profile/comet.sh" rel="noopener me">
<Bluesky />
···
<a href="https://discord.gg/ZKK7DnubD9" rel="noopener"><Discord /></a>
<a href="https://github.com/cometsh" rel="noopener me"><Github /></a>
</p>
</main>
</Layout>
···
@reference "../style/main.css";
body {
-
@apply from-brand-light flex items-center justify-center bg-radial-[circle_at_50%_150%] via-amber-900 to-stone-950 text-white;
}
</style>
···
---
import Logo from "../assets/logo.svg";
import Layout from "../layouts/Layout.astro";
+
import Roadmap from "../components/Roadmap.astro";
import { Bluesky, Discord, Github } from "simple-icons-astro";
---
<Layout>
<main
+
class="flex h-full w-full max-w-xl flex-col items-center gap-3 px-4 py-8 text-center leading-none sm:py-16 lg:py-24 2xl:py-32"
>
<div class="flex items-center justify-center gap-4">
<Logo width="48px" height="48px" class="rounded-md shadow-md" />
<h1 class="text-[64px] font-bold">Comet</h1>
</div>
+
<p class="leading-tight">
Decentralised music streaming on ATProtocol.<br />Coming soon.
</p>
+
+
<p class="flex items-center gap-2 pb-7 font-semibold">
<span class="mr-2">Follow us:</span>
<a href="https://bsky.app/profile/comet.sh" rel="noopener me">
<Bluesky />
···
<a href="https://discord.gg/ZKK7DnubD9" rel="noopener"><Discord /></a>
<a href="https://github.com/cometsh" rel="noopener me"><Github /></a>
</p>
+
+
<Roadmap />
</main>
</Layout>
···
@reference "../style/main.css";
body {
+
@apply from-brand-light flex justify-center bg-radial-[circle_at_50%_150%] via-amber-900 to-stone-950 bg-fixed text-white;
}
</style>