aboutsummaryrefslogtreecommitdiff
path: root/public/static
diff options
context:
space:
mode:
authorJoe Mou <dev@mou.fo>2026-08-11 08:09:47 -0400
committerJoe Mou <dev@mou.fo>2026-08-11 08:13:58 -0400
commitb43ce92c7b16bdefedb7c4c2aef41f9417f1ddf6 (patch)
tree29ff6b47a16e62a58e17921f2c1200249c1845c0 /public/static
parentecd01c4617ec0a6af05cfc4388bdc82986f25961 (diff)
Add language percentages to repo pages
GitHub no longer server-renders the repository sidebar; its React UI lazily fetches /:owner/:repo/_sidebar as JSON, which carries the language breakdown (name, percentage, color). getGitHubLanguages() uses that endpoint, and a new /api/:owner/:repo/languages fragment route renders the bar and legend the same way refs and latest-commit already work, so repo page loads still cost a single scrape. The latest-commit placeholder loader in refs.js is generalized over .placeholder[data-src] so both placeholders share it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'public/static')
-rw-r--r--public/static/refs.js21
-rw-r--r--public/static/style.css37
2 files changed, 46 insertions, 12 deletions
diff --git a/public/static/refs.js b/public/static/refs.js
index 28b8373..ffff505 100644
--- a/public/static/refs.js
+++ b/public/static/refs.js
@@ -1,8 +1,8 @@
-// Progressive enhancement for two bits of data GitHub itself loads lazily:
-// the branch/tag dropdown and the latest-commit bar above a file listing.
-// Both degrade gracefully without JS: the button's command/commandfor still
-// opens the (empty) dialog natively; the commit placeholder just never fills
-// in.
+// Progressive enhancement for the bits of data GitHub itself loads lazily:
+// the branch/tag dropdown, the latest-commit bar above a file listing, and the
+// language breakdown. All degrade gracefully without JS: the button's
+// command/commandfor still opens the (empty) dialog natively; the placeholders
+// just never fill in.
document.addEventListener("DOMContentLoaded", () => {
const dialog = document.querySelector(".ref-selector-menu");
if (dialog) {
@@ -44,17 +44,16 @@ document.addEventListener("DOMContentLoaded", () => {
}
}
- const commitPlaceholder = document.querySelector(".latest-commit-placeholder");
- if (commitPlaceholder) {
- fetch(commitPlaceholder.dataset.src)
+ for (const placeholder of document.querySelectorAll(".placeholder[data-src]")) {
+ fetch(placeholder.dataset.src)
.then((res) => (res.ok ? res.text() : ""))
.then((html) => {
if (html.trim()) {
- commitPlaceholder.outerHTML = html;
+ placeholder.outerHTML = html;
} else {
- commitPlaceholder.remove();
+ placeholder.remove();
}
})
- .catch(() => commitPlaceholder.remove());
+ .catch(() => placeholder.remove());
}
});
diff --git a/public/static/style.css b/public/static/style.css
index 983cb86..ed64bc6 100644
--- a/public/static/style.css
+++ b/public/static/style.css
@@ -218,10 +218,45 @@ body > footer {
font-size: 0.85rem;
}
-.latest-commit-placeholder {
+.placeholder {
opacity: 0.7;
}
+.languages {
+ .language-bar {
+ display: flex;
+ height: .5rem;
+ border-radius: .25rem;
+ overflow: hidden;
+
+ span {
+ /* Keep slivers of tiny languages visible. */
+ min-width: 1px;
+ }
+ }
+
+ .language-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: .2rem 1rem;
+ list-style: none;
+ margin: .5rem 0 0;
+ padding: 0;
+ font-size: 0.85rem;
+ }
+
+ .language-dot {
+ display: inline-block;
+ width: .6rem;
+ height: .6rem;
+ border-radius: 50%;
+ }
+
+ .language-percentage {
+ opacity: 0.7;
+ }
+}
+
.repo-info {
.description {
margin-bottom: .2rem;