diff --git a/src/components/LiteDownload.astro b/src/components/LiteDownload.astro index 9b5e87b..66966be 100644 --- a/src/components/LiteDownload.astro +++ b/src/components/LiteDownload.astro @@ -2,13 +2,13 @@ // One button that downloads Lite for the visitor's machine, so getting the app // takes one click instead of home → Lite page → install section → button. // -// The download URLs come from the latest release's asset list at build time -// (the file names carry the version). Without script, or for a machine there is +// The download URLs and sizes come from the latest release's asset list at +// build time (the file names carry the version). Without script, or for a machine there is // no build for (a phone, an Intel Mac), the button leads to the install // section instead, which lists every option. So does a Linux machine while the // latest release has no AppImage for it. import { getLang, localePath } from "~/i18n"; -import { getLatestLiteRelease } from "~/lib/github"; +import { formatSize, getLatestLiteRelease } from "~/lib/github"; interface Props { /** Filled for the page's main action; outlined where it sits beside other actions. */ @@ -19,10 +19,15 @@ const lang = getLang(Astro); const zh = lang === "zh-CN"; const release = await getLatestLiteRelease(); +const assetName = (suffix: string) => Object.keys(release?.assets ?? {}).find((n) => n.endsWith(suffix)); const asset = (suffix: string) => { - const name = Object.keys(release?.assets ?? {}).find((n) => n.endsWith(suffix)); + const name = assetName(suffix); return name ? release!.assets[name] : ""; }; +const size = (suffix: string) => { + const name = assetName(suffix); + return formatSize(name ? release!.sizes[name] : null) ?? ""; +}; const version = release?.tag.replace(/^v/, "") ?? ""; const install = `${localePath(lang, "/lite")}#install`; @@ -46,6 +51,11 @@ const look = data-arm64={asset("-arm64-setup.exe")} data-x86_64={asset("-x86_64.AppImage")} data-aarch64={asset("-aarch64.AppImage")} + data-mac-size={size("-arm64.dmg")} + data-x64-size={size("-x64-setup.exe")} + data-arm64-size={size("-arm64-setup.exe")} + data-x86_64-size={size("-x86_64.AppImage")} + data-aarch64-size={size("-aarch64.AppImage")} data-label-mac={t.mac} data-label-win={t.win} data-label-linux={t.linux} @@ -79,25 +89,26 @@ const look = const mac = !windows && ((ua && ua.platform === "macOS") || (/Macintosh/.test(s) && navigator.maxTouchPoints < 2)); const linux = !windows && !mac && !/Android/.test(s) && ((ua && ua.platform === "Linux") || /Linux/.test(s)); if (!windows && !mac && !linux) return; - const set = (el, url, label, arch) => { + const set = (el, url, label, arch, size) => { if (!url) return; const a = el.querySelector("[data-dl-link]"); const note = el.querySelector("[data-dl-note]"); a.href = url; a.textContent = label; if (el.dataset.note) { - note.textContent = `${el.dataset.note} · ${arch}`; + note.textContent = [el.dataset.note, arch, size].filter(Boolean).join(" · "); note.hidden = false; } }; const apply = (arch) => { for (const el of document.querySelectorAll("[data-lite-dl]")) { if (windows) { - set(el, arch === "arm64" ? el.dataset.arm64 : el.dataset.x64, el.dataset.labelWin, arch === "arm64" ? "ARM64" : "x64"); + const a = arch === "arm64" ? "arm64" : "x64"; + set(el, el.dataset[a], el.dataset.labelWin, a === "arm64" ? "ARM64" : "x64", el.dataset[`${a}Size`]); } else if (linux) { - set(el, el.dataset[arch], el.dataset.labelLinux, arch); + set(el, el.dataset[arch], el.dataset.labelLinux, arch, el.dataset[`${arch}Size`]); } else if (arch !== "x86") { - set(el, el.dataset.mac, el.dataset.labelMac, "Apple silicon"); + set(el, el.dataset.mac, el.dataset.labelMac, "Apple silicon", el.dataset.macSize); } } }; diff --git a/src/components/pages/CorePage.astro b/src/components/pages/CorePage.astro index 0dc7d1a..ff4e54a 100644 --- a/src/components/pages/CorePage.astro +++ b/src/components/pages/CorePage.astro @@ -9,7 +9,7 @@ import CopyCommand from "~/components/CopyCommand.astro"; import NeonMotion from "~/components/NeonMotion.astro"; import { getLang, localePath } from "~/i18n"; import { coreCopy } from "~/i18n/pages/core"; -import { getLatestCoreRelease, getReleases } from "~/lib/github"; +import { formatSize, getLatestCoreRelease, getReleases } from "~/lib/github"; import { coreLd } from "~/lib/structured-data"; const lang = getLang(Astro); @@ -33,11 +33,12 @@ const asServiceUser = "sudo -u thinkwatch THINKWATCH_HOME=/var/lib/thinkwatch tw // (twcore upgrade and the install script depend on them as well). Links point // at the latest release read at build time, or at GitHub's redirect to the // latest release when GitHub could not be reached; the version then comes from -// the committed list of releases (src/data/releases.json). +// the committed list of releases (src/data/releases.json) and no sizes are shown. const release = await getLatestCoreRelease(); const tag = release?.tag ?? (await getReleases()).find((r) => r.product === "core")?.tag; const version = tag?.replace(/^v/, ""); const download = (file: string) => release?.assets[file] ?? `${repo}/releases/latest/download/${file}`; +const size = (file: string) => formatSize(release?.sizes[file]); const binaries: { label: string; file: string; archive?: string }[] = [ { label: c.install.platforms.mac, file: "twcore-aarch64-apple-darwin" }, { label: c.install.platforms.winX64, file: "twcore-x86_64-pc-windows-msvc.exe" }, @@ -214,6 +215,7 @@ const fileLink = "break-all text-[var(--color-brand-1)] transition-colors hover: {b.label} {b.file} + {size(b.file) && {size(b.file)}} sha256 {b.archive && tar.gz} diff --git a/src/components/pages/LitePage.astro b/src/components/pages/LitePage.astro index 869fd88..d285d03 100644 --- a/src/components/pages/LitePage.astro +++ b/src/components/pages/LitePage.astro @@ -15,7 +15,7 @@ import SiteHeader from "~/components/SiteHeader.astro"; import SiteFooter from "~/components/SiteFooter.astro"; import { getLang, localePath, t } from "~/i18n"; import { liteCopy } from "~/i18n/pages/lite"; -import { getLatestLiteRelease } from "~/lib/github"; +import { formatSize, getLatestLiteRelease } from "~/lib/github"; import { liteLd } from "~/lib/structured-data"; const lang = getLang(Astro); @@ -29,30 +29,48 @@ const repo = "https://github.com/ThinkWatchProject/ThinkWatch-Lite"; const releases = `${repo}/releases`; const latestPage = `${releases}/latest`; -// Windows installers of the latest release. The file names carry the version, -// so they come from the release's asset list, looked up at build time; without -// it, the buttons lead to the latest release page instead. +// The installers of the latest release. The file names carry the version, so +// they come from the release's asset list, looked up at build time, along with +// their sizes; without it, the buttons lead to the latest release page instead +// and no size is shown. const liteRelease = await getLatestLiteRelease(); +const assetName = (suffix: string) => Object.keys(liteRelease?.assets ?? {}).find((n) => n.endsWith(suffix)); const assetUrl = (suffix: string) => { - const name = Object.keys(liteRelease?.assets ?? {}).find((n) => n.endsWith(suffix)); + const name = assetName(suffix); return name ? liteRelease!.assets[name] : null; }; +const assetSize = (suffix: string) => { + const name = assetName(suffix); + return formatSize(name ? liteRelease!.sizes[name] : null); +}; const winArchs = ["x64", "arm64"] as const; const win = Object.fromEntries( winArchs.map((arch) => [ arch, - { url: assetUrl(`-${arch}-setup.exe`) ?? latestPage, sha: assetUrl(`-${arch}-setup.exe.sha256`) }, + { + url: assetUrl(`-${arch}-setup.exe`) ?? latestPage, + sha: assetUrl(`-${arch}-setup.exe.sha256`), + size: assetSize(`-${arch}-setup.exe`), + }, ]), -) as Record<(typeof winArchs)[number], { url: string; sha: string | null }>; +) as Record<(typeof winArchs)[number], { url: string; sha: string | null; size: string | null }>; const linuxArchs = ["x86_64", "aarch64"] as const; const linux = Object.fromEntries( linuxArchs.map((arch) => [ arch, - { url: assetUrl(`-${arch}.AppImage`) ?? latestPage, sha: assetUrl(`-${arch}.AppImage.sha256`) }, + { + url: assetUrl(`-${arch}.AppImage`) ?? latestPage, + sha: assetUrl(`-${arch}.AppImage.sha256`), + size: assetSize(`-${arch}.AppImage`), + }, ]), -) as Record<(typeof linuxArchs)[number], { url: string; sha: string | null }>; +) as Record<(typeof linuxArchs)[number], { url: string; sha: string | null; size: string | null }>; const installScript = `${latestPage}/download/install.sh`; -const dmg = { url: assetUrl("-arm64.dmg") ?? latestPage, sha: assetUrl("-arm64.dmg.sha256") }; +const dmg = { + url: assetUrl("-arm64.dmg") ?? latestPage, + sha: assetUrl("-arm64.dmg.sha256"), + size: assetSize("-arm64.dmg"), +}; const liteVersion = liteRelease?.tag.replace(/^v/, "") ?? null; const installDocsHref = localePath(lang, "/docs/lite/install"); const sourceDocsHref = localePath(lang, "/docs/lite/run-from-source"); @@ -424,7 +442,8 @@ const archLabel = { x64: "x64", arm64: "ARM64" } as const; {c.install.mac.dmgDownload} {liteVersion && (
)} @@ -453,7 +472,8 @@ const archLabel = { x64: "x64", arm64: "ARM64" } as const; {liteVersion && (