prime8dev

prime8dev
git clone https://git.prime8.dev/prime8dev.git
Log | Files | Refs | README | LICENSE

commit ac27081f01517a2f6227d83e719389d804a0defd
parent 59d8935b670be1047bacd26405863ecdbc440edd
Author: Damian Myrda <damian@prime8.dev>
Date:   Sat, 31 Jan 2026 18:58:29 -0600

fix(git): convert relative links to blob URLs for browsing

Links like ./LICENSE now point to git.prime8.dev/-/blob/... instead of
raw file URLs, allowing users to browse files in the Git UI.

Diffstat:
Msrc/external/git.ts | 51+++++++++++++++++++++++++++++++--------------------
1 file changed, 31 insertions(+), 20 deletions(-)

diff --git a/src/external/git.ts b/src/external/git.ts @@ -6,6 +6,11 @@ export interface GitMetadata { dateModified: Date; } +interface RepoUrls { + web: string; + branch: string; +} + export async function fetchGitMetadataForProject( project: Project, ): Promise<GitMetadata | null> { @@ -18,10 +23,18 @@ export async function fetchGitMetadataForProject( const data = await response.json(); - const baseUrl = `${data.web_url.replace("http://", "https://")}/-/raw/${data.default_branch}`; + const urls: RepoUrls = { + web: data.web_url.replace("http://", "https://"), + branch: data.default_branch, + }; return { - readme: await fetchReadme(baseUrl, toRawUrl(data.readme_url)), + readme: await fetchReadme( + urls, + data.readme_url + .replace("http://", "https://") + .replace("/-/blob/", "/-/raw/"), + ), dateCreated: data.created_at, dateModified: data.updated_at, }; @@ -30,39 +43,37 @@ export async function fetchGitMetadataForProject( } } -function toRawUrl(blobUrl: string): string { - return blobUrl.replace("http://", "https://").replace("/-/blob/", "/-/raw/"); -} - async function fetchReadme( - baseUrl: string, - url: string, + urls: RepoUrls, + readmeUrl: string, ): Promise<string | null> { try { - const response = await fetch(url, { + const response = await fetch(readmeUrl, { headers: { "User-Agent": "prime8dev-portfolio" }, }); if (!response.ok) return null; const content = await response.text(); - return fixRelativePaths(baseUrl, content); + return fixRelativePaths(urls, content); } catch { return null; } } -function fixRelativePaths(baseUrl: string, markdown: string): string { - // Fix relative image paths: ![alt](./path) -> ![alt](baseUrl/path) - let fixed = markdown.replace( - /!\[([^\]]*)\]\(\.\/([^)]+)\)/g, - `![$1](${baseUrl}/$2)`, +function fixRelativePaths(urls: RepoUrls, markdown: string): string { + // blob = file browser URL (for links) + const blob = `${urls.web}/-/blob/${urls.branch}`; + markdown = markdown.replace( + /(?<!!)\[([^\]]+)\]\(\.\/([^)]+)\)/g, + `[$1](${blob}/$2)`, ); - // Fix relative link paths (not images): [text](./path) -> [text](baseUrl/path) - fixed = fixed.replace( - /(?<!!)\[([^\]]+)\]\(\.\/([^)]+)\)/g, - `[$1](${baseUrl}/$2)`, + // raw = direct file content URL (for images) + const raw = `${urls.web}/-/raw/${urls.branch}`; + markdown = markdown.replace( + /!\[([^\]]*)\]\(\.\/([^)]+)\)/g, + `![$1](${raw}/$2)`, ); - return fixed; + return markdown; }