diff --git a/apps/web/src/routes/_view/changelog/$slug.tsx b/apps/web/src/routes/_view/changelog/$slug.tsx index 3fd4d8e248..4ff775e723 100644 --- a/apps/web/src/routes/_view/changelog/$slug.tsx +++ b/apps/web/src/routes/_view/changelog/$slug.tsx @@ -1,12 +1,20 @@ import { MDXContent } from "@content-collections/mdx/react"; import { Icon } from "@iconify-icon/react"; import { createFileRoute, Link, notFound } from "@tanstack/react-router"; -import { ChevronLeft } from "lucide-react"; +import { Download } from "lucide-react"; +import { useState } from "react"; import semver from "semver"; -import { getChangelogBySlug, getChangelogList } from "@/changelog"; +import { cn } from "@hypr/utils"; + +import { + type ChangelogWithMeta, + getChangelogBySlug, + getChangelogList, +} from "@/changelog"; import { defaultMDXComponents } from "@/components/mdx"; import { NotFoundContent } from "@/components/not-found"; +import { getDownloadLinks, groupDownloadLinks } from "@/utils/download"; export const Route = createFileRoute("/_view/changelog/$slug")({ component: Component, @@ -67,20 +75,12 @@ export const Route = createFileRoute("/_view/changelog/$slug")({ }); function Component() { - const { changelog, diffUrl } = Route.useLoaderData(); + const { changelog, allChangelogs, diffUrl } = Route.useLoaderData(); const currentVersion = semver.parse(changelog.version); const isPrerelease = !!( currentVersion && currentVersion.prerelease.length > 0 ); - const isLatest = changelog.newerSlug === null; - - let prereleaseType = ""; - let buildNumber = ""; - if (isPrerelease && currentVersion && currentVersion.prerelease.length > 0) { - prereleaseType = currentVersion.prerelease[0]?.toString() || ""; - buildNumber = currentVersion.prerelease[1]?.toString() || ""; - } return (
-
-
- - - All versions - +
+
+
+ Hyprnote +

+ {changelog.version} +

+
-
-

+ +

+ +
+
+ Hyprnote +

{changelog.version}

- {isLatest && ( - - - Latest - - )} - {prereleaseType && ( - - - {prereleaseType} - - )} - {buildNumber && ( - - #{buildNumber} - - )}
- {diffUrl && ( - - - View Diff - - )} +
@@ -141,7 +134,268 @@ function Component() { />
+ + {diffUrl && ( + <> +
+
+

+ View the Code +

+

+ Curious about what changed? See the full diff on GitHub. +

+ + + View Diff on GitHub + +
+ + )} + +
+ +
+ +
); } + +function DownloadLinksHero({ version }: { version: string }) { + const links = getDownloadLinks(version); + const grouped = groupDownloadLinks(links); + + return ( +
+
+

+ + macOS +

+
+ {grouped.macos.map((link) => ( + + + {link.label} + + ))} +
+
+ +
+

+ + Linux +

+
+ {grouped.linux.map((link) => ( + + + {link.label} + + ))} +
+
+
+ ); +} + +function DownloadLinksHeroMobile({ version }: { version: string }) { + const links = getDownloadLinks(version); + const grouped = groupDownloadLinks(links); + const [activeIndex, setActiveIndex] = useState(0); + + const allLinks = [...grouped.macos, ...grouped.linux]; + + return ( +
+
+
+
+ {allLinks.map((link) => ( +
+ + +
+
+ {link.platform} +
+
{link.label}
+
+
+
+ ))} +
+
+ +
+ {allLinks.map((_, index) => ( +
+
+
+ ); +} + +function RelatedReleases({ + currentSlug, + allChangelogs, +}: { + currentSlug: string; + allChangelogs: ChangelogWithMeta[]; +}) { + const currentIndex = allChangelogs.findIndex((c) => c.slug === currentSlug); + if (currentIndex === -1) return null; + + const total = allChangelogs.length; + let startIndex: number; + let endIndex: number; + + if (total <= 5) { + startIndex = 0; + endIndex = total; + } else if (currentIndex <= 2) { + startIndex = 0; + endIndex = 5; + } else if (currentIndex >= total - 2) { + startIndex = total - 5; + endIndex = total; + } else { + startIndex = currentIndex - 2; + endIndex = currentIndex + 3; + } + + const relatedChangelogs = allChangelogs.slice(startIndex, endIndex); + + return ( +
+
+

+ Other Releases +

+

Explore more versions of Hyprnote

+
+ +
+ {relatedChangelogs.map((release) => { + const version = semver.parse(release.version); + const isPrerelease = version && version.prerelease.length > 0; + const nightlyNumber = + isPrerelease && version?.prerelease[0] === "nightly" + ? version.prerelease[1] + : null; + const isCurrent = release.slug === currentSlug; + + return ( + +
+ Hyprnote + +
+

+ {version + ? `${version.major}.${version.minor}.${version.patch}` + : release.version} +

+ {nightlyNumber !== null && ( + + #{nightlyNumber} + + )} +
+
+ + ); + })} +
+ +
+ + View all releases + + +
+
+ ); +} diff --git a/apps/web/src/routes/_view/changelog/index.tsx b/apps/web/src/routes/_view/changelog/index.tsx index b0851517ef..3103f8b427 100644 --- a/apps/web/src/routes/_view/changelog/index.tsx +++ b/apps/web/src/routes/_view/changelog/index.tsx @@ -1,12 +1,14 @@ import { MDXContent } from "@content-collections/mdx/react"; import { Icon } from "@iconify-icon/react"; import { createFileRoute, Link } from "@tanstack/react-router"; +import { Download } from "lucide-react"; import semver from "semver"; import { cn } from "@hypr/utils"; import { type ChangelogWithMeta, getChangelogList } from "@/changelog"; import { defaultMDXComponents } from "@/components/mdx"; +import { getDownloadLinks, groupDownloadLinks } from "@/utils/download"; export const Route = createFileRoute("/_view/changelog/")({ component: Component, @@ -43,17 +45,20 @@ function Component() {
-
- {changelogs.map((changelog, index) => ( - - ))} -
+
+ {changelogs.map((changelog, index) => ( +
+
+ +
+ {index < changelogs.length - 1 && ( +
+ )} +
+ ))} +
+
); @@ -75,38 +80,48 @@ function HeroSection() { function ChangelogSection({ changelog, isFirst, - isLast, }: { changelog: ChangelogWithMeta; isFirst: boolean; - isLast: boolean; }) { const currentVersion = semver.parse(changelog.version); const isPrerelease = currentVersion && currentVersion.prerelease.length > 0; + const nightlyNumber = + isPrerelease && currentVersion?.prerelease[0] === "nightly" + ? currentVersion.prerelease[1] + : null; return ( -
-
-
-

- {changelog.version} -

+
+
+
{isFirst && ( - + + Stable )} {isPrerelease && ( - - - +
+ + + Beta + + {nightlyNumber !== null && ( + + #{nightlyNumber} + + )} +
)} +

+ {currentVersion + ? `${currentVersion.major}.${currentVersion.minor}.${currentVersion.patch}` + : changelog.version} +

+ +
@@ -126,3 +141,60 @@ function ChangelogSection({
); } + +function DownloadLinks({ version }: { version: string }) { + const links = getDownloadLinks(version); + const grouped = groupDownloadLinks(links); + + return ( +
+
+

+ + macOS +

+
+ {grouped.macos.map((link) => ( + + + {link.label} + + ))} +
+
+ +
+

+ + Linux +

+
+ {grouped.linux.map((link) => ( + + + {link.label} + + ))} +
+
+
+ ); +} diff --git a/apps/web/src/utils/download.ts b/apps/web/src/utils/download.ts new file mode 100644 index 0000000000..675b45c7c4 --- /dev/null +++ b/apps/web/src/utils/download.ts @@ -0,0 +1,53 @@ +export type DownloadPlatform = "macos" | "linux"; +export type DownloadArch = "aarch64" | "x86_64"; +export type DownloadFormat = "dmg" | "appimage" | "deb"; + +export interface DownloadLink { + platform: DownloadPlatform; + arch: DownloadArch; + format: DownloadFormat; + url: string; + label: string; +} + +export function getDownloadLinks(version: string): DownloadLink[] { + const baseUrl = `https://github.com/fastrepl/hyprnote/releases/download/desktop_v${version}`; + + return [ + { + platform: "macos", + arch: "aarch64", + format: "dmg", + url: `${baseUrl}/hyprnote-macos-aarch64.dmg`, + label: "Apple Silicon", + }, + { + platform: "macos", + arch: "x86_64", + format: "dmg", + url: `${baseUrl}/hyprnote-macos-x86_64.dmg`, + label: "Intel", + }, + { + platform: "linux", + arch: "x86_64", + format: "appimage", + url: `${baseUrl}/hyprnote-linux-x86_64.AppImage`, + label: "AppImage", + }, + { + platform: "linux", + arch: "x86_64", + format: "deb", + url: `${baseUrl}/hyprnote-linux-x86_64.deb`, + label: "Debian", + }, + ]; +} + +export function groupDownloadLinks(links: DownloadLink[]) { + return { + macos: links.filter((link) => link.platform === "macos"), + linux: links.filter((link) => link.platform === "linux"), + }; +}