mirror of
https://github.com/priyanshujain/margin.git
synced 2026-10-02 19:17:03 +00:00
changes
This commit is contained in:
1 parent
b4097cc47f
commit
61bd7cf6e5
6 files changed
+87
-114
No files matched your search
@@ -1,10 +1,12 @@
|
|||||||
---
|
---
|
||||||
import { site, platforms } from "../data/site";
|
import { site, platforms } from "../data/site";
|
||||||
|
import { getRelease } from "../data/release";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
large?: boolean;
|
large?: boolean;
|
||||||
}
|
}
|
||||||
const { large = false } = Astro.props;
|
const { large = false } = Astro.props;
|
||||||
|
const release = await getRelease();
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="dl">
|
<div class="dl">
|
||||||
@@ -26,7 +28,7 @@ const { large = false } = Astro.props;
|
|||||||
platforms.map((p) => (
|
platforms.map((p) => (
|
||||||
<a
|
<a
|
||||||
class="dl__chip"
|
class="dl__chip"
|
||||||
href={site.releasesUrl}
|
href={release.urls[p.id]}
|
||||||
data-download-os={p.id}
|
data-download-os={p.id}
|
||||||
rel="noopener"
|
rel="noopener"
|
||||||
download
|
download
|
||||||
@@ -57,6 +59,7 @@ const { large = false } = Astro.props;
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
flex: none;
|
||||||
width: 44px;
|
width: 44px;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
---
|
---
|
||||||
import { site } from "../data/site";
|
import { site, platforms } from "../data/site";
|
||||||
|
import { getRelease } from "../data/release";
|
||||||
|
|
||||||
const { repo, releasesUrl } = site;
|
const { repo, releasesUrl } = site;
|
||||||
|
const { urls, version } = await getRelease();
|
||||||
|
const matches = Object.fromEntries(platforms.map((p) => [p.id, p.match]));
|
||||||
---
|
---
|
||||||
|
|
||||||
<script define:vars={{ repo, releasesUrl }}>
|
<script define:vars={{ repo, releasesUrl, urls, version, matches }}>
|
||||||
const SUFFIXES = {
|
|
||||||
mac: [".dmg"],
|
|
||||||
windows: [".msi", ".exe"],
|
|
||||||
linux: [".AppImage", ".deb", ".rpm"],
|
|
||||||
};
|
|
||||||
const LABELS = { mac: "macOS", windows: "Windows", linux: "Linux" };
|
const LABELS = { mac: "macOS", windows: "Windows", linux: "Linux" };
|
||||||
|
|
||||||
function detectOS() {
|
function detectOS() {
|
||||||
@@ -21,32 +20,31 @@ const { repo, releasesUrl } = site;
|
|||||||
}
|
}
|
||||||
|
|
||||||
function pickAsset(assets, os) {
|
function pickAsset(assets, os) {
|
||||||
for (const sfx of SUFFIXES[os] || []) {
|
for (const sfx of matches[os] || []) {
|
||||||
const hit = assets.find((a) => a.name.toLowerCase().endsWith(sfx.toLowerCase()));
|
const hit = assets.find((a) => a.name.toLowerCase().endsWith(sfx.toLowerCase()));
|
||||||
if (hit) return hit.browser_download_url;
|
if (hit) return hit.browser_download_url;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function apply(map, ver) {
|
||||||
const os = detectOS();
|
const os = detectOS();
|
||||||
|
|
||||||
document.querySelectorAll("[data-download]").forEach((el) => {
|
document.querySelectorAll("[data-download]").forEach((el) => {
|
||||||
el.dataset.detectedOs = os;
|
|
||||||
const label = el.querySelector("[data-dl-label]");
|
const label = el.querySelector("[data-dl-label]");
|
||||||
if (label) label.textContent = "Download for " + LABELS[os];
|
if (label) label.textContent = "Download for " + LABELS[os];
|
||||||
|
if (map[os]) el.href = map[os];
|
||||||
});
|
});
|
||||||
|
document.querySelectorAll("[data-download-os]").forEach((el) => {
|
||||||
document.querySelectorAll(".dl__alt [data-download-os]").forEach((el) => {
|
const url = map[el.dataset.downloadOs];
|
||||||
|
if (url) el.href = url;
|
||||||
|
});
|
||||||
|
document.querySelectorAll(".dl__os [data-download-os]").forEach((el) => {
|
||||||
if (el.dataset.downloadOs === os) el.remove();
|
if (el.dataset.downloadOs === os) el.remove();
|
||||||
});
|
});
|
||||||
|
if (ver) document.querySelectorAll("[data-version]").forEach((el) => (el.textContent = ver));
|
||||||
document.querySelectorAll("[data-osrow]").forEach((row) => {
|
|
||||||
const primary = row.querySelector(`[data-download-os="${os}"]`);
|
|
||||||
if (primary) {
|
|
||||||
primary.classList.add("is-detected");
|
|
||||||
row.prepend(primary);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
apply(urls, version);
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
@@ -56,23 +54,11 @@ const { repo, releasesUrl } = site;
|
|||||||
if (!res.ok) return;
|
if (!res.ok) return;
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
const assets = data.assets || [];
|
const assets = data.assets || [];
|
||||||
const version = data.tag_name || "";
|
const fresh = {};
|
||||||
|
for (const os of Object.keys(matches)) fresh[os] = pickAsset(assets, os) || urls[os] || releasesUrl;
|
||||||
document.querySelectorAll("[data-download]").forEach((el) => {
|
apply(fresh, data.tag_name || version);
|
||||||
const url = pickAsset(assets, el.dataset.detectedOs || os);
|
|
||||||
if (url) el.href = url;
|
|
||||||
});
|
|
||||||
document.querySelectorAll("[data-download-os]").forEach((el) => {
|
|
||||||
const url = pickAsset(assets, el.dataset.downloadOs);
|
|
||||||
if (url) el.href = url;
|
|
||||||
});
|
|
||||||
if (version) {
|
|
||||||
document.querySelectorAll("[data-version]").forEach((el) => {
|
|
||||||
el.textContent = version;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
/* keep the releases-page fallback links */
|
/* keep the build-time links */
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
@@ -1,14 +1,17 @@
|
|||||||
---
|
---
|
||||||
import { site } from "../data/site";
|
import { site } from "../data/site";
|
||||||
|
import { getRelease } from "../data/release";
|
||||||
import Logo from "./Logo.astro";
|
import Logo from "./Logo.astro";
|
||||||
|
|
||||||
|
const release = await getRelease();
|
||||||
|
|
||||||
const cols = [
|
const cols = [
|
||||||
{
|
{
|
||||||
title: "Get Margin",
|
title: "Get Margin",
|
||||||
links: [
|
links: [
|
||||||
{ label: "Download for macOS", href: site.releasesUrl, os: "mac" },
|
{ label: "Download for macOS", href: release.urls.mac, os: "mac" },
|
||||||
{ label: "Download for Windows", href: site.releasesUrl, os: "windows" },
|
{ label: "Download for Windows", href: release.urls.windows, os: "windows" },
|
||||||
{ label: "Download for Linux", href: site.releasesUrl, os: "linux" },
|
{ label: "Download for Linux", href: release.urls.linux, os: "linux" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
import AppWindow from "./AppWindow.astro";
|
import AppWindow from "./AppWindow.astro";
|
||||||
|
import DownloadButtons from "./DownloadButtons.astro";
|
||||||
import OfferBadge from "./OfferBadge.astro";
|
import OfferBadge from "./OfferBadge.astro";
|
||||||
import { site, platforms } from "../data/site";
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<section class="hero">
|
<section class="hero">
|
||||||
@@ -21,25 +21,7 @@ import { site, platforms } from "../data/site";
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container hero__get" id="download">
|
<div class="container hero__get" id="download">
|
||||||
<div class="hero__buttons" data-osrow>
|
<DownloadButtons large />
|
||||||
{
|
|
||||||
platforms.map((p) => (
|
|
||||||
<a
|
|
||||||
class="osbtn"
|
|
||||||
href={site.releasesUrl}
|
|
||||||
data-download-os={p.id}
|
|
||||||
rel="noopener"
|
|
||||||
download
|
|
||||||
>
|
|
||||||
<img class="osbtn__icon" src={`/logos/${p.logo}`} alt="" />
|
|
||||||
<span>Download for {p.label}</span>
|
|
||||||
</a>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<p class="hero__meta">
|
|
||||||
<span data-version>v0.1.2</span> · Works offline · No account
|
|
||||||
</p>
|
|
||||||
<div class="hero__offer">
|
<div class="hero__offer">
|
||||||
<OfferBadge />
|
<OfferBadge />
|
||||||
</div>
|
</div>
|
||||||
@@ -75,62 +57,9 @@ import { site, platforms } from "../data/site";
|
|||||||
margin-top: clamp(24px, 3vw, 32px);
|
margin-top: clamp(24px, 3vw, 32px);
|
||||||
scroll-margin-top: 96px;
|
scroll-margin-top: 96px;
|
||||||
}
|
}
|
||||||
.hero__buttons {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
.osbtn {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
padding: 0.82em 1.35em;
|
|
||||||
background: #ffffff;
|
|
||||||
border: 1px solid var(--line-strong);
|
|
||||||
border-radius: 999px;
|
|
||||||
color: #23201b;
|
|
||||||
font-family: var(--font-ui);
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 600;
|
|
||||||
box-shadow: var(--shadow-page);
|
|
||||||
transition:
|
|
||||||
transform 0.18s var(--ease),
|
|
||||||
box-shadow 0.18s var(--ease),
|
|
||||||
border-color 0.18s var(--ease);
|
|
||||||
}
|
|
||||||
.osbtn:hover {
|
|
||||||
transform: translateY(-1px);
|
|
||||||
box-shadow: var(--shadow-pop);
|
|
||||||
}
|
|
||||||
.osbtn.is-detected {
|
|
||||||
border-color: var(--ink);
|
|
||||||
}
|
|
||||||
.osbtn__icon {
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
flex: none;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
.hero__meta {
|
|
||||||
margin: 16px 0 0;
|
|
||||||
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
color: var(--ink-faint);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.hero__offer {
|
.hero__offer {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-top: 22px;
|
margin-top: 22px;
|
||||||
}
|
}
|
||||||
@media (max-width: 480px) {
|
|
||||||
.hero__buttons {
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: stretch;
|
|
||||||
}
|
|
||||||
.osbtn {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
// Resolves the latest GitHub release to a direct download URL per OS, at build
|
||||||
|
// time. The picked file follows each platform's `match` order in site.ts.
|
||||||
|
// Falls back to the releases page if the release can't be read.
|
||||||
|
|
||||||
|
import { site, platforms } from "./site";
|
||||||
|
|
||||||
|
type Asset = { name: string; browser_download_url: string };
|
||||||
|
|
||||||
|
export type Release = {
|
||||||
|
version: string;
|
||||||
|
urls: Record<string, string>;
|
||||||
|
};
|
||||||
|
|
||||||
|
function pickAsset(assets: Asset[], match: string[]): string | null {
|
||||||
|
for (const sfx of match) {
|
||||||
|
const hit = assets.find((a) => a.name.toLowerCase().endsWith(sfx.toLowerCase()));
|
||||||
|
if (hit) return hit.browser_download_url;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fallback(): Release {
|
||||||
|
return {
|
||||||
|
version: "",
|
||||||
|
urls: Object.fromEntries(platforms.map((p) => [p.id, site.releasesUrl])),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchRelease(): Promise<Release> {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`https://api.github.com/repos/${site.repo}/releases/latest`, {
|
||||||
|
headers: { Accept: "application/vnd.github+json" },
|
||||||
|
signal: AbortSignal.timeout(8000),
|
||||||
|
});
|
||||||
|
if (!res.ok) return fallback();
|
||||||
|
const data = await res.json();
|
||||||
|
const assets: Asset[] = data.assets || [];
|
||||||
|
const urls = Object.fromEntries(
|
||||||
|
platforms.map((p) => [p.id, pickAsset(assets, p.match) ?? site.releasesUrl]),
|
||||||
|
);
|
||||||
|
return { version: data.tag_name ?? "", urls };
|
||||||
|
} catch {
|
||||||
|
return fallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let cached: Promise<Release> | null = null;
|
||||||
|
|
||||||
|
export function getRelease(): Promise<Release> {
|
||||||
|
cached ??= fetchRelease();
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
@@ -39,8 +39,8 @@ export type Platform = {
|
|||||||
|
|
||||||
export const platforms: Platform[] = [
|
export const platforms: Platform[] = [
|
||||||
{ id: "mac", label: "macOS", logo: "macos.svg", match: [".dmg"] },
|
{ id: "mac", label: "macOS", logo: "macos.svg", match: [".dmg"] },
|
||||||
{ id: "windows", label: "Windows", logo: "windows.svg", match: [".msi", ".exe"] },
|
{ id: "windows", label: "Windows", logo: "windows.svg", match: [".exe", ".msi"] },
|
||||||
{ id: "linux", label: "Linux", logo: "linux.svg", match: [".AppImage", ".deb", ".rpm"] },
|
{ id: "linux", label: "Linux", logo: "linux.svg", match: [".deb", ".AppImage", ".rpm"] },
|
||||||
];
|
];
|
||||||
|
|
||||||
// Full-color store logos, served from /public/logos. Shown on white tiles so
|
// Full-color store logos, served from /public/logos. Shown on white tiles so
|
||||||
|
|||||||
Reference in new issue
Block a user