From cc6c1dd00d50a3b2c22668d4f08d5c5a79579085 Mon Sep 17 00:00:00 2001 From: Owais Jamil Date: Wed, 25 Mar 2026 23:30:42 +0000 Subject: [PATCH] fix: readme rendering --- apps/twisted/tests/unit/tangled-normalizers.spec.ts | 23 +++++++++++++++++++++++ apps/twisted/src/features/repo/RepoDetailPage.vue | 15 ++++++++++----- apps/twisted/src/services/tangled/normalizers.ts | 15 +++++++++++++++ apps/twisted/src/services/tangled/queries.ts | 23 ++++++++++++++++++++++- 4 file(s) changed, 70 insertion(s)(+), 6 deletion(s)(-) diff --git a/apps/twisted/tests/unit/tangled-normalizers.spec.ts b/apps/twisted/tests/unit/tangled-normalizers.spec.ts --- a/apps/twisted/tests/unit/tangled-normalizers.spec.ts +++ b/apps/twisted/tests/unit/tangled-normalizers.spec.ts @@ -4,6 +4,7 @@ normalizeLogText, normalizeRepoRecord, normalizeTree, + normalizeTreeReadme, } from "@/services/tangled/normalizers.js"; import { buildPublicRawUrl, resolveRepoRelativePath } from "@/services/tangled/repo-assets.js"; import { getAtUriRkey, parseAtUri } from "@/services/tangled/uris.js"; @@ -84,6 +85,28 @@ ["README.md", "file"], ["vendor/lib", "submodule"], ]); + }); + + it("preserves the README filename discovered by the tree endpoint", () => { + const readme = normalizeTreeReadme({ + files: [], + lastCommit: { + hash: "a", + message: "docs", + when: "2026-03-23T00:00:00Z", + author: { name: "Test", email: "test@example.com", when: "" }, + }, + readme: { + filename: "README", + contents: "plain text readme", + }, + ref: "main", + }); + + expect(readme).toEqual({ + path: "README", + content: "plain text readme", + }); }); it("parses wrapped commit arrays from repo log payloads", () => { diff --git a/apps/twisted/src/features/repo/RepoDetailPage.vue b/apps/twisted/src/features/repo/RepoDetailPage.vue --- a/apps/twisted/src/features/repo/RepoDetailPage.vue +++ b/apps/twisted/src/features/repo/RepoDetailPage.vue @@ -83,7 +83,7 @@ import { useRepoRecord, useDefaultBranch, - useRepoBlob, + useRepoReadme, useRepoLanguages, useRepoLog, useRepoIssues, @@ -130,14 +130,19 @@ const branchQuery = useDefaultBranch(owner, repoName, { enabled: hasRecord }); const defaultBranch = computed(() => branchQuery.data.value?.name ?? ""); const hasBranch = computed(() => !!branchQuery.data.value?.name); + const readmeQuery = useRepoReadme(owner, repoName, defaultBranch, { enabled: hasBranch }); const markdownContext = computed(() => { - if (!owner.value || !repoName.value || !defaultBranch.value) return undefined; + if (!owner.value || !repoName.value || !defaultBranch.value || !readmeQuery.data.value?.path) return undefined; - return { owner: owner.value, repo: repoName.value, branch: defaultBranch.value, sourcePath: "README.md" }; + return { + owner: owner.value, + repo: repoName.value, + branch: defaultBranch.value, + sourcePath: readmeQuery.data.value.path, + }; }); const languagesQuery = useRepoLanguages(owner, repoName, undefined, { enabled: hasBranch }); - const readmeQuery = useRepoBlob(owner, repoName, defaultBranch, "README.md", { readme: true, enabled: hasBranch }); const logQuery = useRepoLog(owner, repoName, defaultBranch, { limit: 20, enabled: hasBranch }); const repo = computed((): RepoDetail | undefined => { @@ -148,7 +153,7 @@ stars: starCountQuery.data.value ?? rec.stars, defaultBranch: defaultBranch.value || undefined, languages: languagesQuery.data.value, - readme: readmeQuery.data.value?.isBinary ? undefined : readmeQuery.data.value?.content, + readme: readmeQuery.data.value?.content, }; }); diff --git a/apps/twisted/src/services/tangled/normalizers.ts b/apps/twisted/src/services/tangled/normalizers.ts --- a/apps/twisted/src/services/tangled/normalizers.ts +++ b/apps/twisted/src/services/tangled/normalizers.ts @@ -48,6 +48,21 @@ return output.files.map((entry) => normalizeTreeEntry(entry, currentPath)); } +export type RepoReadme = { + path: string; + content: string; +}; + +export function normalizeTreeReadme(output: ShTangledRepoTree.$output, currentPath = ""): RepoReadme | undefined { + const readme = output.readme; + if (!readme?.filename) return undefined; + + return { + path: currentPath ? `${currentPath}/${readme.filename}` : readme.filename, + content: readme.contents ?? "", + }; +} + export type BlobContent = { path: string; content: string; diff --git a/apps/twisted/src/services/tangled/queries.ts b/apps/twisted/src/services/tangled/queries.ts --- a/apps/twisted/src/services/tangled/queries.ts +++ b/apps/twisted/src/services/tangled/queries.ts @@ -34,6 +34,7 @@ } from "./endpoints.js"; import { normalizeTree, + normalizeTreeReadme, normalizeBlob, normalizeDefaultBranch, normalizeLanguages, @@ -53,7 +54,7 @@ normalizeStringRecord, } from "./normalizers.js"; -export type { CommitEntry, BranchEntry, BlobContent, DefaultBranchInfo } from "./normalizers.js"; +export type { CommitEntry, BranchEntry, BlobContent, DefaultBranchInfo, RepoReadme } from "./normalizers.js"; const MIN = 60_000; @@ -104,6 +105,26 @@ enabled: computed(() => isEnabled(hasText(h) && hasText(r) && hasText(ref), options.enabled)), staleTime: 2 * MIN, gcTime: 10 * MIN, + }); +} + +/** README discovered by the repo tree endpoint for a ref root. */ +export function useRepoReadme( + handle: MaybeRef, + repo: MaybeRef, + ref: MaybeRef, + options: { enabled?: MaybeRef } = {}, +) { + const h = computed(() => toValue(handle).trim()); + const r = computed(() => toValue(repo).trim()); + + return useQuery({ + queryKey: computed(() => ["readme", h.value, r.value, toValue(ref)]), + queryFn: () => + fetchRepoTree(h.value, r.value, { repo: `${h.value}/${r.value}`, ref: toValue(ref) }).then(normalizeTreeReadme), + enabled: computed(() => isEnabled(hasText(h) && hasText(r) && hasText(ref), options.enabled)), + staleTime: 5 * MIN, + gcTime: 30 * MIN, }); } -- tangled.sh