<feed xmlns='http://www.w3.org/2005/Atom'>
<title>mono.git/cgithub/src/app.ts, branch main</title>
<subtitle>Unnamed repository; edit this file 'description' to name the repository.</subtitle>
<id>http://git.mou.fo/2026/mono.git/atom/cgithub/src/app.ts?h=main</id>
<link rel='self' href='http://git.mou.fo/2026/mono.git/atom/cgithub/src/app.ts?h=main'/>
<link rel='alternate' type='text/html' href='http://git.mou.fo/2026/mono.git/'/>
<updated>2026-09-10T05:01:04Z</updated>
<entry>
<title>Redirect /notifications instead of scraping it as an owner</title>
<updated>2026-09-10T05:01:04Z</updated>
<author>
<name>Joe Mou</name>
<email>dev@mou.fo</email>
</author>
<published>2026-09-10T04:53:48Z</published>
<link rel='alternate' type='text/html' href='http://git.mou.fo/2026/mono.git/commit/?id=198f2a07f68a348dc0823b2bde3bec541e06db01'/>
<id>urn:sha1:198f2a07f68a348dc0823b2bde3bec541e06db01</id>
<content type='text'>
A signed-out request for github.com/notifications is bounced to a login
page, so the owner route followed that redirect and left the visitor on
/login. Hand the path over to GitHub before it reaches the route.
</content>
</entry>
<entry>
<title>Serve GitHub's global search when it names one repository</title>
<updated>2026-09-09T18:39:57Z</updated>
<author>
<name>Joe Mou</name>
<email>dev@mou.fo</email>
</author>
<published>2026-09-09T15:03:02Z</published>
<link rel='alternate' type='text/html' href='http://git.mou.fo/2026/mono.git/commit/?id=a7bc82cad42b7b8c4c8c52e1217f7f005b632e6d'/>
<id>urn:sha1:a7bc82cad42b7b8c4c8c52e1217f7f005b632e6d</id>
<content type='text'>
/search?q=repo:owner/name+...&amp;type=code matched the /:owner route and
rendered a profile page for an owner named "search", which GitHub 404s.
It is the URL GitHub's own search box produces, so it arrives whenever a
search is redirected here.

A /search route now takes it: when the query carries a repo: qualifier
and a type, it strips the qualifier and redirects to that repository's
search route, which already maps issues and pull requests to pages we
render and hands the rest to GitHub. Searches that name no single
repository -- bare queries, org:/user: scopes, a missing type -- keep
redirecting to github.com/search as they did before.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01GkGX6puS45xD5KzdV9tHEA
</content>
</entry>
<entry>
<title>Split the page shell out of the repository layout</title>
<updated>2026-09-05T01:49:59Z</updated>
<author>
<name>Joe Mou</name>
<email>dev@mou.fo</email>
</author>
<published>2026-09-05T01:26:21Z</published>
<link rel='alternate' type='text/html' href='http://git.mou.fo/2026/mono.git/commit/?id=53ac3bc511a45e4e2e3f234a8b8dcf853b0c38d9'/>
<id>urn:sha1:53ac3bc511a45e4e2e3f234a8b8dcf853b0c38d9</id>
<content type='text'>
layout.eta was two things at once: the HTML shell every page needs and
the repository chrome around it. owner.eta, being the one page that is
not about a repository, could use neither and carried its own copy of
the shell, which had drifted -- its footer named no build commit and
did not link to the original page on GitHub.

The shell moves to views/layouts/base.eta, taking its title and any
deferred scripts as layout data, and the chrome to views/layouts/repo.eta,
a layout over it that adds the clone commands, nav, search box, and the
"/" shortcut that focuses it. Repository pages point at repo.eta and
render as before; owner.eta uses base.eta directly and so gains the
footer it was missing.

Templates now live in more than one directory, so scripts/build.ts walks
views/ recursively and keys each template by its path underneath it.
Names crossing a directory are written from the views root
("/layouts/base.eta"), the one form both Eta's filesystem resolver and
the precompiled resolver in worker.ts agree on; siblings stay relative.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01Ba5p88cWtCq42vf3uweweL
</content>
</entry>
<entry>
<title>Handle route errors in middleware</title>
<updated>2026-09-04T03:28:52Z</updated>
<author>
<name>Joe Mou</name>
<email>dev@mou.fo</email>
</author>
<published>2026-09-04T03:08:08Z</published>
<link rel='alternate' type='text/html' href='http://git.mou.fo/2026/mono.git/commit/?id=5bfe8b345d2ee92111a2363321a248adb4f5195a'/>
<id>urn:sha1:5bfe8b345d2ee92111a2363321a248adb4f5195a</id>
<content type='text'>
Every page route wrapped its scrape in tryRender, which awaited the
promise, rendered the template on success, and turned a failure into a
redirect or an error page. Hono's onError does that mapping for all of
them at once, so handlers now await their scrape inline and both
wrappers are gone. Fragments under /api/ still fail as an empty body
rather than a page; onError checks for them first, since a fragment
turns a redirect into a 404 instead of following it.

RedirectError becomes an abstract base with InternalRedirectError and
ExternalRedirectError under it, so the handler dispatches on the type
instead of sniffing the location for an https:// prefix.

One behavior change: a thrown value that is not an Error no longer
renders error.eta, because Hono only routes Error instances to onError.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01QERwoRJhuqMhKzzCUW4Skv
</content>
</entry>
<entry>
<title>Clean up renderer, redirects, and build commit</title>
<updated>2026-09-04T03:05:33Z</updated>
<author>
<name>Joe Mou</name>
<email>dev@mou.fo</email>
</author>
<published>2026-09-03T19:07:25Z</published>
<link rel='alternate' type='text/html' href='http://git.mou.fo/2026/mono.git/commit/?id=439f783c9e3b18519505d1ad6b8533bea081cca0'/>
<id>urn:sha1:439f783c9e3b18519505d1ad6b8533bea081cca0</id>
<content type='text'>
</content>
</entry>
<entry>
<title>Render repository wikis</title>
<updated>2026-09-03T05:09:10Z</updated>
<author>
<name>Joe Mou</name>
<email>dev@mou.fo</email>
</author>
<published>2026-09-03T04:08:06Z</published>
<link rel='alternate' type='text/html' href='http://git.mou.fo/2026/mono.git/commit/?id=4c66a07a995c0d551cd284d6e72826b4f7748bab'/>
<id>urn:sha1:4c66a07a995c0d551cd284d6e72826b4f7748bab</id>
<content type='text'>
Wikis are one of the last parts of GitHub still served as plain Rails
HTML, with no embedded JSON to read, so this scrapes the markup: a page
and any older revision of it, the _Sidebar and _Footer a wiki can
define, and the page index. Every page is already in the sidebar of
every page, since the "Show N more pages" button only unhides the rest,
so linking the whole wiki costs no second request.

Routes mirror GitHub's and resolve in its order, so an underscore name
reserved for its own actions (_pages, _history, _new, _edit) can never
be mistaken for a page; the ones we don't serve fall through to the
usual redirect. A slug reaches us decoded and has to be encoded again
rather than interpolated the way a ref is, or a page named C# would
truncate at the fragment.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_013rQG1io1i4GJQW5RFcXNGd
</content>
</entry>
<entry>
<title>Display PDF blobs</title>
<updated>2026-08-27T18:53:38Z</updated>
<author>
<name>Joe Mou</name>
<email>dev@mou.fo</email>
</author>
<published>2026-08-27T18:08:06Z</published>
<link rel='alternate' type='text/html' href='http://git.mou.fo/2026/mono.git/commit/?id=0d60b8c8235c3c07f675aba7a56dd37b62519f9e'/>
<id>urn:sha1:0d60b8c8235c3c07f675aba7a56dd37b62519f9e</id>
<content type='text'>
GitHub renders a PDF with a viewer of its own and so ships no content for
one, leaving the blob view with nothing to show. Its raw bytes come as
application/octet-stream, which browsers download rather than display, so
serve them from a new embed route that retypes them and point an &lt;object&gt;
at that.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01BErPR92xppzGFXkPqXPYj2
</content>
</entry>
<entry>
<title>Use an HTTP redirect for same-origin requests</title>
<updated>2026-08-12T05:28:04Z</updated>
<author>
<name>Joe Mou</name>
<email>dev@mou.fo</email>
</author>
<published>2026-08-12T05:27:25Z</published>
<link rel='alternate' type='text/html' href='http://git.mou.fo/2026/mono.git/commit/?id=679a2b71c3e73dc1b8cf11806b3f3df49018b535'/>
<id>urn:sha1:679a2b71c3e73dc1b8cf11806b3f3df49018b535</id>
<content type='text'>
The meta refresh page renders as a broken image when the request came
from an &lt;img&gt; on one of our own pages. Such requests are already past
the redirect-loop hazard the refresh works around, so send them a real
redirect instead.
</content>
</entry>
<entry>
<title>Read the About box and latest release from JSON</title>
<updated>2026-08-11T22:25:26Z</updated>
<author>
<name>Joe Mou</name>
<email>dev@mou.fo</email>
</author>
<published>2026-08-11T21:41:01Z</published>
<link rel='alternate' type='text/html' href='http://git.mou.fo/2026/mono.git/commit/?id=21310d0366c0f21e73c1f4f78d38370edfb59445'/>
<id>urn:sha1:21310d0366c0f21e73c1f4f78d38370edfb59445</id>
<content type='text'>
GitHub rebuilt the repository sidebar, so the selectors parseRepoInfo used
matched nothing and the About box rendered empty. Everything but the latest
release is in the page's embedded payload as sidebarAbout; the latest release
is only in the _sidebar JSON already fetched for languages, so it arrives with
that fragment and is moved beside the release count.
</content>
</entry>
<entry>
<title>Keep the ref picker on the current view and path</title>
<updated>2026-08-11T21:37:02Z</updated>
<author>
<name>Joe Mou</name>
<email>dev@mou.fo</email>
</author>
<published>2026-08-11T17:32:44Z</published>
<link rel='alternate' type='text/html' href='http://git.mou.fo/2026/mono.git/commit/?id=866f99c414cb3d102483ce21c68a0e62b394baaa'/>
<id>urn:sha1:866f99c414cb3d102483ce21c68a0e62b394baaa</id>
<content type='text'>
</content>
</entry>
</feed>
