11import { MDXContent } from "@content-collections/mdx/react" ;
22import { Icon } from "@iconify-icon/react" ;
33import { createFileRoute , Link } from "@tanstack/react-router" ;
4+ import { Download } from "lucide-react" ;
45import semver from "semver" ;
56
67import { cn } from "@hypr/utils" ;
78
89import { type ChangelogWithMeta , getChangelogList } from "@/changelog" ;
910import { defaultMDXComponents } from "@/components/mdx" ;
11+ import { getDownloadLinks , groupDownloadLinks } from "@/utils/download" ;
1012
1113export const Route = createFileRoute ( "/_view/changelog/" ) ( {
1214 component : Component ,
@@ -43,17 +45,20 @@ function Component() {
4345 < div className = "max-w-6xl mx-auto border-x border-neutral-100 bg-white" >
4446 < div className = "px-6 py-16 lg:py-24" >
4547 < HeroSection />
46- < div className = "mt-16 max-w-4xl mx-auto" >
47- { changelogs . map ( ( changelog , index ) => (
48- < ChangelogSection
49- key = { changelog . slug }
50- changelog = { changelog }
51- isFirst = { index === 0 }
52- isLast = { index === changelogs . length - 1 }
53- />
54- ) ) }
55- </ div >
5648 </ div >
49+ < div className = "mt-16" >
50+ { changelogs . map ( ( changelog , index ) => (
51+ < div key = { changelog . slug } >
52+ < div className = "max-w-4xl mx-auto px-6" >
53+ < ChangelogSection changelog = { changelog } isFirst = { index === 0 } />
54+ </ div >
55+ { index < changelogs . length - 1 && (
56+ < div className = "border-b border-neutral-100 my-12" />
57+ ) }
58+ </ div >
59+ ) ) }
60+ </ div >
61+ < div className = "px-6 pb-16 lg:pb-24" > </ div >
5762 </ div >
5863 </ main >
5964 ) ;
@@ -75,38 +80,48 @@ function HeroSection() {
7580function ChangelogSection ( {
7681 changelog,
7782 isFirst,
78- isLast,
7983} : {
8084 changelog : ChangelogWithMeta ;
8185 isFirst : boolean ;
82- isLast : boolean ;
8386} ) {
8487 const currentVersion = semver . parse ( changelog . version ) ;
8588 const isPrerelease = currentVersion && currentVersion . prerelease . length > 0 ;
89+ const nightlyNumber =
90+ isPrerelease && currentVersion ?. prerelease [ 0 ] === "nightly"
91+ ? currentVersion . prerelease [ 1 ]
92+ : null ;
8693
8794 return (
88- < section
89- className = { cn ( [
90- "grid grid-cols-1 md:grid-cols-[200px_1fr] gap-6 md:gap-12" ,
91- ! isLast && "border-b border-neutral-100 pb-12 mb-12" ,
92- ] ) }
93- >
94- < div className = "md:sticky md:top-24 md:self-start" >
95- < div className = "flex items-center gap-2" >
96- < h2 className = "text-lg font-semibold text-stone-700" >
97- { changelog . version }
98- </ h2 >
95+ < section className = "grid grid-cols-1 md:grid-cols-[160px_1fr] gap-6 md:gap-12" >
96+ < div className = "md:sticky md:top-24 md:self-start space-y-6" >
97+ < div className = "flex flex-col gap-1" >
9998 { isFirst && (
100- < span className = "inline-flex items-center gap-1 px-1.5 py-0.5 text-xs font-medium bg-linear-to-t from-amber-200 to-amber-100 text-amber-900 rounded-full" >
99+ < span className = "inline-flex items-center gap-1 px-1.5 py-0.5 text-xs font-medium bg-linear-to-t from-amber-200 to-amber-100 text-amber-900 rounded-full w-fit " >
101100 < Icon icon = "ri:rocket-fill" className = "text-xs" />
101+ Stable
102102 </ span >
103103 ) }
104104 { isPrerelease && (
105- < span className = "inline-flex items-center gap-1 px-1.5 py-0.5 text-xs font-medium bg-linear-to-b from-[#03BCF1] to-[#127FE5] text-white rounded-full" >
106- < Icon icon = "ri:moon-fill" className = "text-xs" />
107- </ span >
105+ < div className = "flex items-center gap-1.5" >
106+ < span className = "inline-flex items-center gap-1 px-1.5 py-0.5 text-xs font-medium bg-linear-to-b from-[#03BCF1] to-[#127FE5] text-white rounded-full" >
107+ < Icon icon = "ri:moon-fill" className = "text-xs" />
108+ Beta
109+ </ span >
110+ { nightlyNumber !== null && (
111+ < span className = "inline-flex items-center px-1.5 py-0.5 text-xs font-medium bg-stone-100 text-stone-600 rounded-full" >
112+ #{ nightlyNumber }
113+ </ span >
114+ ) }
115+ </ div >
108116 ) }
117+ < h2 className = "text-4xl font-mono font-medium text-stone-700" >
118+ { currentVersion
119+ ? `${ currentVersion . major } .${ currentVersion . minor } .${ currentVersion . patch } `
120+ : changelog . version }
121+ </ h2 >
109122 </ div >
123+
124+ < DownloadLinks version = { changelog . version } />
110125 </ div >
111126
112127 < div >
@@ -126,3 +141,60 @@ function ChangelogSection({
126141 </ section >
127142 ) ;
128143}
144+
145+ function DownloadLinks ( { version } : { version : string } ) {
146+ const links = getDownloadLinks ( version ) ;
147+ const grouped = groupDownloadLinks ( links ) ;
148+
149+ return (
150+ < div className = "space-y-6" >
151+ < div >
152+ < h3 className = "flex items-center gap-1.5 text-xs font-medium text-stone-500 uppercase tracking-wider mb-2" >
153+ < Icon icon = "simple-icons:apple" className = "text-sm" />
154+ macOS
155+ </ h3 >
156+ < div className = "space-y-1.5" >
157+ { grouped . macos . map ( ( link ) => (
158+ < a
159+ key = { link . url }
160+ href = { link . url }
161+ className = { cn ( [
162+ "flex items-center gap-2 px-4 h-8 text-sm rounded-full transition-all" ,
163+ "bg-linear-to-b from-white to-stone-50 text-neutral-700" ,
164+ "border border-neutral-300" ,
165+ "hover:shadow-sm hover:scale-[102%] active:scale-[98%]" ,
166+ ] ) }
167+ >
168+ < Download className = "size-3.5 shrink-0" />
169+ < span className = "truncate" > { link . label } </ span >
170+ </ a >
171+ ) ) }
172+ </ div >
173+ </ div >
174+
175+ < div >
176+ < h3 className = "flex items-center gap-1.5 text-xs font-medium text-stone-500 uppercase tracking-wider mb-2" >
177+ < Icon icon = "simple-icons:linux" className = "text-sm" />
178+ Linux
179+ </ h3 >
180+ < div className = "space-y-1.5" >
181+ { grouped . linux . map ( ( link ) => (
182+ < a
183+ key = { link . url }
184+ href = { link . url }
185+ className = { cn ( [
186+ "flex items-center gap-2 px-4 h-8 text-sm rounded-full transition-all" ,
187+ "bg-linear-to-b from-white to-stone-50 text-neutral-700" ,
188+ "border border-neutral-300" ,
189+ "hover:shadow-sm hover:scale-[102%] active:scale-[98%]" ,
190+ ] ) }
191+ >
192+ < Download className = "size-3.5 shrink-0" />
193+ < span className = "truncate" > { link . label } </ span >
194+ </ a >
195+ ) ) }
196+ </ div >
197+ </ div >
198+ </ div >
199+ ) ;
200+ }
0 commit comments