diff --git a/build/Filters/Links.hs b/build/Filters/Links.hs index 326b78f..7a0c3dd 100644 --- a/build/Filters/Links.hs +++ b/build/Filters/Links.hs @@ -70,6 +70,7 @@ domainIcon url | "archive.org" `T.isInfixOf` url = "internet-archive" -- Code / software | "github.com" `T.isInfixOf` url = "github" + | "git.levineuwirth.org" `T.isInfixOf` url = "forgejo" | "tensorflow.org" `T.isInfixOf` url = "tensorflow" -- AI companies | "anthropic.com" `T.isInfixOf` url = "anthropic" diff --git a/static/css/popups.css b/static/css/popups.css index f575815..d66400d 100644 --- a/static/css/popups.css +++ b/static/css/popups.css @@ -97,6 +97,10 @@ mask-image: url('/images/link-icons/github.svg'); -webkit-mask-image: url('/images/link-icons/github.svg'); } +.popup-source[data-popup-source="forgejo"]::before { + mask-image: url('/images/link-icons/forgejo.svg'); + -webkit-mask-image: url('/images/link-icons/forgejo.svg'); +} .popup-source[data-popup-source="youtube"]::before { mask-image: url('/images/link-icons/youtube.svg'); -webkit-mask-image: url('/images/link-icons/youtube.svg'); diff --git a/static/css/typography.css b/static/css/typography.css index 84c071a..0458d6e 100644 --- a/static/css/typography.css +++ b/static/css/typography.css @@ -626,6 +626,11 @@ a[data-link-icon="github"]::after { -webkit-mask-image: url('/images/link-icons/github.svg'); } +a[data-link-icon="forgejo"]::after { + mask-image: url('/images/link-icons/forgejo.svg'); + -webkit-mask-image: url('/images/link-icons/forgejo.svg'); +} + a[data-link-icon="worldcat"]::after { mask-image: url('/images/link-icons/worldcat.svg'); -webkit-mask-image: url('/images/link-icons/worldcat.svg'); diff --git a/static/images/link-icons/forgejo.svg b/static/images/link-icons/forgejo.svg new file mode 100644 index 0000000..e5b1297 --- /dev/null +++ b/static/images/link-icons/forgejo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/js/popups.js b/static/js/popups.js index ec9fce6..af3f3d0 100644 --- a/static/js/popups.js +++ b/static/js/popups.js @@ -108,6 +108,7 @@ if (/arxiv\.org\/(?:abs|pdf)\/\d{4}\.\d{4,5}/.test(href)) return arxivContent; if (/(?:dx\.)?doi\.org\/10\./.test(href)) return doiContent; if (/github\.com\/[^/]+\/[^/?#]+/.test(href)) return githubContent; + if (/git\.levineuwirth\.org\/[^/]+\/[^/?#]+/.test(href)) return forgejoContent; if (/openlibrary\.org\/(?:works|books)\//.test(href)) return openlibraryContent; if (/(?:bio|med)rxiv\.org\/content\/10\./.test(href)) return biorxivContent; if (/(?:youtube\.com\/watch|youtu\.be\/)/.test(href)) return youtubeContent; @@ -411,6 +412,32 @@ .catch(function () { return null; }); } + /* 6.5 Forgejo — repo description, language, stars */ + function forgejoContent(target) { + var href = target.getAttribute('href'); + if (!href || cache[href]) return Promise.resolve(cache[href] || null); + + var m = href.match(/git\.levineuwirth\.org\/([^/]+)\/([^/?#]+)/); + if (!m) return Promise.resolve(null); + + var apiUrl = 'https://git.levineuwirth.org/api/v1/repos/' + m[1] + '/' + m[2]; + return fetch(apiUrl) + .then(function (r) { return r.ok ? r.json() : null; }) + .then(function (data) { + if (!data || !data.full_name) return null; + var meta = [data.language, data.stars_count != null ? '\u2605\u00a0' + data.stars_count : null] + .filter(Boolean).join(' \u00b7 '); + return store(href, + '