From eff2e1e7b31f046c974b88b409296af81b9f5615 Mon Sep 17 00:00:00 2001 From: Joe Mou Date: Thu, 22 Jan 2026 18:35:27 -0500 Subject: Add blob view for displaying file contents --- cgithub/src/index.ts | 23 ++++++++++++++- cgithub/src/scraper.ts | 77 ++++++++++++++++++++++++++++++++++++++++++++++++ cgithub/static/style.css | 14 +++++++++ cgithub/views/blob.eta | 37 +++++++++++++++++++++++ cgithub/views/tree.eta | 2 +- 5 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 cgithub/views/blob.eta diff --git a/cgithub/src/index.ts b/cgithub/src/index.ts index df0a116..a546f13 100644 --- a/cgithub/src/index.ts +++ b/cgithub/src/index.ts @@ -2,7 +2,7 @@ import { Hono } from "hono"; import { serve } from "@hono/node-server"; import { serveStatic } from "@hono/node-server/serve-static"; import { Eta } from "eta"; -import { getGitHubTree } from "./scraper.js"; +import { getGitHubTree, getGitHubBlob } from "./scraper.js"; const app = new Hono(); const eta = new Eta({ views: "./views" }); @@ -30,6 +30,27 @@ app.get("/:owner/:repo/tree/:branch/:path{.*}?", async (c) => { } }); +app.get("/:owner/:repo/blob/:branch/:path{.*}", async (c) => { + try { + const owner = c.req.param("owner"); + const repo = c.req.param("repo"); + const branch = c.req.param("branch"); + const path = c.req.param("path"); + + const data = await getGitHubBlob(owner, repo, branch, path); + const html = await eta.renderAsync("blob.eta", data); + + return c.html(html); + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : "Unknown error"; + return c.html( + `

Error

${errorMessage}

`, + 500, + ); + } +}); + const port = parseInt(process.env.PORT || "3000", 10); serve({ fetch: app.fetch, port }, (info) => { console.log(`Server running on http://localhost:${info.port}`); diff --git a/cgithub/src/scraper.ts b/cgithub/src/scraper.ts index add03b2..fa8d655 100644 --- a/cgithub/src/scraper.ts +++ b/cgithub/src/scraper.ts @@ -15,6 +15,20 @@ export interface GitHubTree { }; } +export interface GitHubBlob { + path: string; + branch: string; + content: string; + rawContent: string; + language: string | null; + size: number; + repo: { + name: string; + owner: string; + isPublic: boolean; + }; +} + export async function getGitHubTree( owner: string, repo: string, @@ -89,3 +103,66 @@ export async function getGitHubTree( }, }; } + +export async function getGitHubBlob( + owner: string, + repo: string, + branch: string, + path: string, +): Promise { + const url = `https://github.com/${owner}/${repo}/blob/${branch}/${path}`; + + const response = await fetch(url, { + headers: { + "User-Agent": + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36", + Accept: + "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Language": "en-US,en;q=0.5", + }, + }); + const html = await response.text(); + + // GitHub embeds file data in a JSON script tag + let payload; + + const match = html.match( + /