diff options
| author | Joe Mou <dev@mou.fo> | 2026-08-11 13:32:44 -0400 |
|---|---|---|
| committer | Joe Mou <dev@mou.fo> | 2026-08-11 17:37:02 -0400 |
| commit | 866f99c414cb3d102483ce21c68a0e62b394baaa (patch) | |
| tree | 973b8e526bf51e8a05f6daab262d84035b7e2901 /cgithub | |
| parent | 10f923b51e2844486651493b44974e418a2be6d9 (diff) | |
Keep the ref picker on the current view and path
Diffstat (limited to 'cgithub')
| -rw-r--r-- | cgithub/public/static/refs.js | 6 | ||||
| -rw-r--r-- | cgithub/src/app.test.ts | 52 | ||||
| -rw-r--r-- | cgithub/src/app.ts | 12 | ||||
| -rw-r--r-- | cgithub/views/_nav.eta | 4 | ||||
| -rw-r--r-- | cgithub/views/_refs.eta | 6 | ||||
| -rw-r--r-- | cgithub/views/_tree.eta | 2 | ||||
| -rw-r--r-- | cgithub/views/blob.eta | 2 | ||||
| -rw-r--r-- | cgithub/views/commits.eta | 2 |
8 files changed, 74 insertions, 12 deletions
diff --git a/cgithub/public/static/refs.js b/cgithub/public/static/refs.js index ffff505..f3e07e8 100644 --- a/cgithub/public/static/refs.js +++ b/cgithub/public/static/refs.js @@ -7,7 +7,9 @@ document.addEventListener("DOMContentLoaded", () => { const dialog = document.querySelector(".ref-selector-menu"); if (dialog) { const list = dialog.querySelector(".ref-selector-list"); - const { owner, repo } = dialog.dataset; + // The view and path let the picker link to the same place on another ref + // rather than to the repository root. + const { owner, repo, view, path = "" } = dialog.dataset; const cache = {}; async function load(type) { @@ -17,7 +19,7 @@ document.addEventListener("DOMContentLoaded", () => { } list.textContent = "Loading…"; try { - const res = await fetch(`/api/${owner}/${repo}/refs/${type}`); + const res = await fetch(`/api/${owner}/${repo}/refs/${type}/${view}/${path}`); const html = res.ok ? await res.text() : ""; cache[type] = html || "Failed to load."; list.innerHTML = cache[type]; diff --git a/cgithub/src/app.test.ts b/cgithub/src/app.test.ts index 498d8a6..6544f03 100644 --- a/cgithub/src/app.test.ts +++ b/cgithub/src/app.test.ts @@ -8,6 +8,56 @@ import { createApp } from "./app.ts"; const eta = new Eta({ views: path.join(import.meta.dirname, "..", "views") }); const app = createApp(eta); +describe("branch picker", () => { + const repo = { owner: "actions", name: "deploy-pages" }; + const refs = [{ name: "main", isDefault: true }, { name: "v4" }]; + + it("should keep the current view and path", () => { + const blob = eta.render("_refs.eta", { + repo, + refs, + type: "branches", + view: "blob", + path: "src/main.ts", + }); + const tree = eta.render("_refs.eta", { + repo, + refs, + type: "branches", + view: "tree", + path: "src", + }); + + assert.match(blob, /href="\/actions\/deploy-pages\/blob\/main\/src\/main.ts"/); + assert.match(blob, /href="\/actions\/deploy-pages\/blob\/v4\/src\/main.ts"/); + assert.match(tree, /href="\/actions\/deploy-pages\/tree\/v4\/src"/); + }); + + it("should link to the root tree without a path", () => { + const html = eta.render("_refs.eta", { repo, refs, type: "branches", view: "tree", path: "" }); + + assert.match(html, /href="\/actions\/deploy-pages\/tree\/main"/); + }); + + it("should not build ref links for views it does not serve", async () => { + const res = await app.request("http://cgithub.example/api/a/b/refs/branches/raw/README.md"); + + assert.strictEqual(res.status, 404); + }); + + it("should tell the refs endpoint where the picker was opened", () => { + const nav = { repo, branch: "main", path: "src" }; + + const blob = eta.render("_nav.eta", { ...nav, view: "blob", path: "src/main.ts" }); + assert.match(blob, /data-view="blob" data-path="src\/main.ts"/); + assert.match( + eta.render("_nav.eta", { ...nav, view: "tree" }), + /data-view="tree" data-path="src"/, + ); + assert.doesNotMatch(eta.render("_nav.eta", { ...nav, view: "tree", path: "" }), /data-path=/); + }); +}); + describe("redirects to GitHub", () => { // An HTTP redirect would be caught by the extension's declarativeNetRequest // rules and bounced straight back here. @@ -21,7 +71,7 @@ describe("redirects to GitHub", () => { const body = await res.text(); assert.match( body, - /<meta http-equiv="refresh" content="2; url=https:\/\/github.com\/a\/b\/c\?x=1">/, + /<meta http-equiv="refresh" content="1; url=https:\/\/github.com\/a\/b\/c\?x=1">/, ); assert.doesNotMatch(body, /<script/); }); diff --git a/cgithub/src/app.ts b/cgithub/src/app.ts index 9a7ee8e..81e47fd 100644 --- a/cgithub/src/app.ts +++ b/cgithub/src/app.ts @@ -155,12 +155,18 @@ export function createApp(eta: Eta) { // be visited directly. Keep the site's initial page loads plain server- // rendered HTML while still surfacing data (branches/tags, latest commit) // that would otherwise require an extra scrape on every page view. - app.get("/api/:owner/:repo/refs/:type", async (c) => { - const { owner, repo, type } = c.req.param(); + // The view and path are where the picker was opened, so each ref can link to + // the same page on another ref. + app.get("/api/:owner/:repo/refs/:type/:view/:path{.*}?", async (c) => { + const { owner, repo, type, view, path = "" } = c.req.param(); if (type !== "branches" && type !== "tags") { return c.notFound(); } - return tryRenderFragment(c, "_refs.eta", getGitHubRefs(owner, repo, type)); + if (view !== "tree" && view !== "blob" && view !== "commits") { + return c.notFound(); + } + const refs = getGitHubRefs(owner, repo, type).then((data) => ({ ...data, view, path })); + return tryRenderFragment(c, "_refs.eta", refs); }); app.get("/api/:owner/:repo/latest-commit/:branch/:path{.*}?", async (c) => { diff --git a/cgithub/views/_nav.eta b/cgithub/views/_nav.eta index eb7ed42..a20fbbc 100644 --- a/cgithub/views/_nav.eta +++ b/cgithub/views/_nav.eta @@ -6,7 +6,7 @@ <button type="button" class="ref-selector-button branch" command="show-modal" commandfor="ref-selector-menu"> <img src="/static/icons/git-branch.svg" alt="branch" class="icon"> <%= refLabel %> ▾ </button> - <dialog class="ref-selector-menu" id="ref-selector-menu" closedby="any" data-owner="<%= it.repo.owner %>" data-repo="<%= it.repo.name %>"> + <dialog class="ref-selector-menu" id="ref-selector-menu" closedby="any" data-owner="<%= it.repo.owner %>" data-repo="<%= it.repo.name %>" data-view="<%= it.view %>"<% if (it.path && it.path !== "/") { %> data-path="<%= it.path %>"<% } %>> <div class="ref-selector-tabs"> <button type="button" class="active" data-ref-type="branches">Branches</button> <button type="button" data-ref-type="tags">Tags</button> @@ -22,7 +22,7 @@ currentPath += (currentPath ? '/' : '') + part; const isLast = i === pathParts.length - 1; if (isLast) { - %>/<%= part %><%= it.isTree ? "/" : "" %><% + %>/<%= part %><%= it.view === "tree" ? "/" : "" %><% } else { %>/<a href="/<%= it.repo.owner %>/<%= it.repo.name %>/tree/<%= it.branch %>/<%= currentPath %>"><%= part %></a><% } diff --git a/cgithub/views/_refs.eta b/cgithub/views/_refs.eta index 2450c4b..9d951cc 100644 --- a/cgithub/views/_refs.eta +++ b/cgithub/views/_refs.eta @@ -1,10 +1,14 @@ +<% +const linkPrefix = `/${it.repo.owner}/${it.repo.name}/${it.view}/`; +const linkSuffix = it.path ? `/${it.path}` : ""; +%> <% if (it.refs.length === 0) { %> <div class="ref-selector-empty">No <%= it.type %> found.</div> <% } else { %> <ul class="ref-selector-items"> <% for (const ref of it.refs) { %> <li> - <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/tree/<%= ref.name %>"><%= ref.name %></a> + <a href="<%= linkPrefix %><%= ref.name %><%= linkSuffix %>"><%= ref.name %></a> <% if (ref.isDefault) { %><span class="ref-selector-badge">default</span><% } %> </li> <% } %> diff --git a/cgithub/views/_tree.eta b/cgithub/views/_tree.eta index fc7707b..a3c943d 100644 --- a/cgithub/views/_tree.eta +++ b/cgithub/views/_tree.eta @@ -1,4 +1,4 @@ -<%~ include("./_nav.eta", {...it, isTree: true}) %> +<%~ include("./_nav.eta", {...it, view: "tree"}) %> <% const commitPath = it.path && it.path !== "/" ? it.path : ""; %> <div class="placeholder commit box" data-src="/api/<%= it.repo.owner %>/<%= it.repo.name %>/latest-commit/<%= it.branch %>/<%= commitPath %>"> diff --git a/cgithub/views/blob.eta b/cgithub/views/blob.eta index 50f92a9..ed1e84b 100644 --- a/cgithub/views/blob.eta +++ b/cgithub/views/blob.eta @@ -1,6 +1,6 @@ <% layout("./layout.eta"); it.searchType = "code" %> -<%~ include("./_nav.eta", it) %> +<%~ include("./_nav.eta", {...it, view: "blob"}) %> <div> <% if (it.language) { %> diff --git a/cgithub/views/commits.eta b/cgithub/views/commits.eta index 2ddcba3..5cf0beb 100644 --- a/cgithub/views/commits.eta +++ b/cgithub/views/commits.eta @@ -1,6 +1,6 @@ <% layout("./layout.eta") %> -<%~ include("./_nav.eta", {...it, isTree: false}) %> +<%~ include("./_nav.eta", {...it, view: "commits"}) %> <% for (const group of it.commitGroups) { %> <h3><%= group.title %></h3> |
