From 38580a48c1bd4b9fc6a87da27ebf53d164e6a5b0 Mon Sep 17 00:00:00 2001 From: theMackabu Date: Thu, 23 Apr 2026 13:40:28 -0700 Subject: [PATCH 1/9] make command palette items flush --- apps/dashboard/package.json | 2 +- .../src/components/navigation/command-palette.tsx | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index 30c3e93..53fa016 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -7,7 +7,7 @@ }, "scripts": { "predev": "node ../../scripts/link-worktree-dev-vars.mjs", - "dev": "vite dev --port 3000", + "dev": "NODE_OPTIONS='--max-old-space-size=4096' vite dev --port 3000", "build": "NODE_OPTIONS='--max-old-space-size=4096' vite build", "preview": "vite preview", "test": "vitest run", diff --git a/apps/dashboard/src/components/navigation/command-palette.tsx b/apps/dashboard/src/components/navigation/command-palette.tsx index 1a7b729..af200bf 100644 --- a/apps/dashboard/src/components/navigation/command-palette.tsx +++ b/apps/dashboard/src/components/navigation/command-palette.tsx @@ -90,7 +90,7 @@ export function CommandPalette() { value={search} onValueChange={setSearch} /> - + {getEmptyMessage( search, @@ -98,12 +98,17 @@ export function CommandPalette() { )} {Array.from(groups.entries()).map(([groupName, groupItems]) => ( - + {groupItems.map((item) => ( handleSelect(item)} + className="rounded-none !px-4" > {item.icon && ( Date: Thu, 23 Apr 2026 13:58:48 -0700 Subject: [PATCH 2/9] add commits viewer --- .../src/components/layouts/dashboard-tabs.tsx | 1 + .../components/repo/code-explorer-toolbar.tsx | 2 +- .../src/components/repo/latest-commit-bar.tsx | 9 + .../src/components/repo/repo-commits-list.tsx | 330 ++++++++++++++++++ apps/dashboard/src/lib/github-revalidation.ts | 2 +- apps/dashboard/src/lib/github.functions.ts | 60 ++++ apps/dashboard/src/lib/github.query.ts | 21 ++ apps/dashboard/src/lib/github.types.ts | 13 + apps/dashboard/src/lib/query-client.tsx | 12 + apps/dashboard/src/lib/tab-store.ts | 9 +- apps/dashboard/src/routeTree.gen.ts | 22 ++ .../_protected/$owner/$repo/commits.$.tsx | 65 ++++ 12 files changed, 543 insertions(+), 3 deletions(-) create mode 100644 apps/dashboard/src/components/repo/repo-commits-list.tsx create mode 100644 apps/dashboard/src/routes/_protected/$owner/$repo/commits.$.tsx diff --git a/apps/dashboard/src/components/layouts/dashboard-tabs.tsx b/apps/dashboard/src/components/layouts/dashboard-tabs.tsx index 468362c..450f48e 100644 --- a/apps/dashboard/src/components/layouts/dashboard-tabs.tsx +++ b/apps/dashboard/src/components/layouts/dashboard-tabs.tsx @@ -35,6 +35,7 @@ const tabIconMap = { review: ReviewsIcon, repo: ArchiveIcon, commit: GitCommitIcon, + commits: GitCommitIcon, } as const; function useScrollShadows(tabCount: number) { diff --git a/apps/dashboard/src/components/repo/code-explorer-toolbar.tsx b/apps/dashboard/src/components/repo/code-explorer-toolbar.tsx index 016bb71..a07a484 100644 --- a/apps/dashboard/src/components/repo/code-explorer-toolbar.tsx +++ b/apps/dashboard/src/components/repo/code-explorer-toolbar.tsx @@ -59,7 +59,7 @@ export function CodeExplorerToolbar({ ); } -function BranchSelector({ +export function BranchSelector({ repo, currentRef, scope, diff --git a/apps/dashboard/src/components/repo/latest-commit-bar.tsx b/apps/dashboard/src/components/repo/latest-commit-bar.tsx index 9d056b9..f2d1478 100644 --- a/apps/dashboard/src/components/repo/latest-commit-bar.tsx +++ b/apps/dashboard/src/components/repo/latest-commit-bar.tsx @@ -95,6 +95,15 @@ export function LatestCommitBar({ {formatRelativeTime(commit.date)} + + + Commits + ); diff --git a/apps/dashboard/src/components/repo/repo-commits-list.tsx b/apps/dashboard/src/components/repo/repo-commits-list.tsx new file mode 100644 index 0000000..290e003 --- /dev/null +++ b/apps/dashboard/src/components/repo/repo-commits-list.tsx @@ -0,0 +1,330 @@ +import { GitBranchIcon, GitCommitIcon } from "@diffkit/icons"; +import { Button } from "@diffkit/ui/components/button"; +import { Skeleton } from "@diffkit/ui/components/skeleton"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@diffkit/ui/components/tooltip"; +import { useInfiniteQuery, useQuery } from "@tanstack/react-query"; +import { Link, useNavigate } from "@tanstack/react-router"; +import { useCallback, useEffect, useMemo, useRef } from "react"; +import { formatRelativeTime } from "#/lib/format-relative-time"; +import { + type GitHubQueryScope, + githubRepoCommitsQueryOptions, + githubRepoOverviewQueryOptions, +} from "#/lib/github.query"; +import type { RepoCommitSummary } from "#/lib/github.types"; +import { useRegisterTab } from "#/lib/use-register-tab"; +import { BranchSelector } from "./code-explorer-toolbar"; + +const COMMITS_PAGE_SIZE = 30; +const skeletonRows = [ + "commit-0", + "commit-1", + "commit-2", + "commit-3", + "commit-4", +]; + +const commitDateFormatter = new Intl.DateTimeFormat("en", { + month: "short", + day: "numeric", + year: "numeric", +}); + +export function RepoCommitsPage({ + owner, + repo, + currentRef, + scope, +}: { + owner: string; + repo: string; + currentRef: string; + scope: GitHubQueryScope; +}) { + const navigate = useNavigate(); + const loadMoreRef = useRef(null); + const overviewQuery = useQuery( + githubRepoOverviewQueryOptions(scope, { + owner, + repo, + }), + ); + const commitsQuery = useInfiniteQuery( + githubRepoCommitsQueryOptions(scope, { + owner, + repo, + ref: currentRef, + perPage: COMMITS_PAGE_SIZE, + }), + ); + + const commits = useMemo( + () => commitsQuery.data?.pages.flatMap((page) => page.commits) ?? [], + [commitsQuery.data], + ); + const groups = useMemo(() => groupCommitsByDay(commits), [commits]); + const repoData = overviewQuery.data; + + const handleBranchChange = useCallback( + (branch: string) => { + if (branch === currentRef) return; + void navigate({ + to: "/$owner/$repo/commits/$", + params: { owner, repo, _splat: branch }, + }); + }, + [currentRef, navigate, owner, repo], + ); + + useRegisterTab( + repoData + ? { + type: "commits", + title: `${repoData.name} commits`, + url: `/${owner}/${repo}/commits/${currentRef}`, + repo: `${owner}/${repo}`, + iconColor: "text-muted-foreground", + tabId: `commits:${owner}/${repo}`, + } + : null, + ); + + useEffect(() => { + const target = loadMoreRef.current; + if (!target) return; + + const observer = new IntersectionObserver( + ([entry]) => { + if ( + entry?.isIntersecting && + commitsQuery.hasNextPage && + !commitsQuery.isFetchingNextPage + ) { + void commitsQuery.fetchNextPage(); + } + }, + { rootMargin: "500px 0px" }, + ); + + observer.observe(target); + return () => observer.disconnect(); + }, [ + commitsQuery.hasNextPage, + commitsQuery.isFetchingNextPage, + commitsQuery.fetchNextPage, + ]); + + if (overviewQuery.error) throw overviewQuery.error; + if (commitsQuery.error) throw commitsQuery.error; + + return ( +
+
+
+
+ + {owner}/{repo} + +

Commits

+
+ {repoData ? ( + + ) : ( +
+ + + {currentRef} + +
+ )} +
+ +
+ {commitsQuery.isPending ? ( + + ) : commits.length === 0 ? ( +
+ No commits found for this ref. +
+ ) : ( +
    + {groups.map((group) => ( +
  1. +
    + Commits on {group.label} +
    +
      + {group.commits.map((commit) => ( + + ))} +
    +
  2. + ))} +
+ )} +
+ +
+ {commitsQuery.isFetchingNextPage ? ( +
+ +
+ ) : commitsQuery.hasNextPage ? ( + + ) : commits.length > 0 ? ( + + End of commit history + + ) : null} +
+
+
+ ); +} + +function CommitRow({ + commit, + owner, + repo, +}: { + commit: RepoCommitSummary; + owner: string; + repo: string; +}) { + const firstLine = commit.message.split("\n")[0] || commit.sha; + const authorName = commit.author?.login ?? commit.authorName ?? "Unknown"; + const shortSha = commit.sha.slice(0, 7); + + return ( +
  • +
    + {commit.author ? ( + + {commit.author.login} + + ) : ( +
    + +
    + )} +
    + + {firstLine} + +

    + {authorName}{" "} + committed{" "} + {commit.date ? formatRelativeTime(commit.date) : "recently"} +

    +
    +
    +
    + + + + {shortSha} + + + + {commit.sha} + + +
    +
  • + ); +} + +function RepoCommitsListSkeleton() { + return ( + <> +
    + +
    + + + ); +} + +function RepoCommitsRowsSkeleton() { + return ( +
    + {skeletonRows.map((key) => ( +
    +
    + +
    + + +
    +
    + +
    + ))} +
    + ); +} + +function groupCommitsByDay(commits: RepoCommitSummary[]) { + const groups = new Map(); + + for (const commit of commits) { + const label = formatCommitDay(commit.date); + const group = groups.get(label) ?? []; + group.push(commit); + groups.set(label, group); + } + + return Array.from(groups.entries()).map(([label, groupedCommits]) => ({ + label, + commits: groupedCommits, + })); +} + +function formatCommitDay(date: string) { + const parsed = new Date(date); + if (Number.isNaN(parsed.getTime())) return "recent history"; + return commitDateFormatter.format(parsed); +} diff --git a/apps/dashboard/src/lib/github-revalidation.ts b/apps/dashboard/src/lib/github-revalidation.ts index c5cf3c5..78164dd 100644 --- a/apps/dashboard/src/lib/github-revalidation.ts +++ b/apps/dashboard/src/lib/github-revalidation.ts @@ -422,7 +422,7 @@ export function getGitHubWebhookRevalidationSignalKeys( export function getGitHubRevalidationSignalKeysForTab(tab: Tab) { const [owner, repo] = tab.repo.split("/"); - if (tab.type === "repo") { + if (tab.type === "repo" || tab.type === "commits") { return [ githubRevalidationSignalKeys.repoCode({ owner, diff --git a/apps/dashboard/src/lib/github.functions.ts b/apps/dashboard/src/lib/github.functions.ts index 8e48e29..1ff1024 100644 --- a/apps/dashboard/src/lib/github.functions.ts +++ b/apps/dashboard/src/lib/github.functions.ts @@ -47,6 +47,7 @@ import type { RepoCollaborator, RepoCommitDetail, RepoCommitInput, + RepoCommitsPage, RepoContributorsResult, RepoOverview, RepoParticipationStats, @@ -9563,6 +9564,65 @@ export const getRefHeadCommit = createServerFn({ method: "GET" }) }).catch(() => null); }); +type RepoCommitsInput = { + owner: string; + repo: string; + ref: string; + page?: number; + perPage?: number; +}; + +export const getRepoCommits = createServerFn({ method: "GET" }) + .inputValidator(identityValidator) + .handler(async ({ data }): Promise => { + const context = await getGitHubContextForRepository(data); + if (!context) return { commits: [], nextPage: null }; + + const perPage = Math.min(Math.max(data.perPage ?? 15, 1), 30); + const page = Math.max(data.page ?? 1, 1); + + return getCachedGitHubRequest< + Awaited>["data"], + RepoCommitsPage + >({ + context, + resource: "repo.commits.v1", + params: { ...data, page, perPage }, + freshForMs: githubCachePolicy.detail.staleTimeMs, + signalKeys: [githubRevalidationSignalKeys.repoCode(data)], + namespaceKeys: [githubRevalidationSignalKeys.repoCode(data)], + cacheMode: "split", + request: (headers) => + context.octokit.rest.repos.listCommits({ + owner: data.owner, + repo: data.repo, + sha: data.ref, + page, + per_page: perPage, + headers, + }), + mapData: (commits) => ({ + commits: commits.map((commit) => ({ + sha: commit.sha, + message: commit.commit.message, + date: + commit.commit.committer?.date ?? commit.commit.author?.date ?? "", + authorName: + commit.commit.author?.name ?? commit.commit.committer?.name ?? null, + author: commit.author + ? { + login: commit.author.login, + avatarUrl: commit.author.avatar_url, + url: commit.author.html_url, + type: commit.author.type, + } + : null, + })), + nextPage: commits.length === perPage ? page + 1 : null, + }), + }).catch(() => ({ commits: [], nextPage: null })); + }); + // --------------------------------------------------------------------------- // Batch tree entry commits (single GraphQL query for all entries in a dir) // --------------------------------------------------------------------------- diff --git a/apps/dashboard/src/lib/github.query.ts b/apps/dashboard/src/lib/github.query.ts index 154f9a4..07717b9 100644 --- a/apps/dashboard/src/lib/github.query.ts +++ b/apps/dashboard/src/lib/github.query.ts @@ -30,6 +30,7 @@ import { getRepoBranches, getRepoCollaborators, getRepoCommit, + getRepoCommits, getRepoContributors, getRepoDiscussions, getRepoFileContent, @@ -246,6 +247,10 @@ export const githubQueryKeys = { scope: GitHubQueryScope, input: { owner: string; repo: string; ref: string }, ) => ["github", scope.userId, "repo", "refHeadCommit", input] as const, + commits: ( + scope: GitHubQueryScope, + input: { owner: string; repo: string; ref: string; perPage?: number }, + ) => ["github", scope.userId, "repo", "commits", input] as const, treeEntryCommits: ( scope: GitHubQueryScope, input: { owner: string; repo: string; ref: string; dirPath: string }, @@ -854,6 +859,22 @@ export function githubRefHeadCommitQueryOptions( }); } +export function githubRepoCommitsQueryOptions( + scope: GitHubQueryScope, + input: { owner: string; repo: string; ref: string; perPage?: number }, +) { + return infiniteQueryOptions({ + queryKey: githubQueryKeys.repo.commits(scope, input), + queryFn: ({ pageParam }) => + getRepoCommits({ data: { ...input, page: pageParam } }), + initialPageParam: 1, + getNextPageParam: (lastPage) => lastPage.nextPage ?? undefined, + staleTime: githubCachePolicy.detail.staleTimeMs, + gcTime: githubCachePolicy.detail.gcTimeMs, + meta: tabPersistedMeta, + }); +} + export function githubTreeEntryCommitsQueryOptions( scope: GitHubQueryScope, input: { diff --git a/apps/dashboard/src/lib/github.types.ts b/apps/dashboard/src/lib/github.types.ts index 826e412..b651efd 100644 --- a/apps/dashboard/src/lib/github.types.ts +++ b/apps/dashboard/src/lib/github.types.ts @@ -572,6 +572,19 @@ export type FileLastCommit = { author: GitHubActor | null; }; +export type RepoCommitSummary = { + sha: string; + message: string; + date: string; + authorName: string | null; + author: GitHubActor | null; +}; + +export type RepoCommitsPage = { + commits: RepoCommitSummary[]; + nextPage: number | null; +}; + export type RepoBranch = { name: string; isProtected: boolean; diff --git a/apps/dashboard/src/lib/query-client.tsx b/apps/dashboard/src/lib/query-client.tsx index 9c626f0..b04e764 100644 --- a/apps/dashboard/src/lib/query-client.tsx +++ b/apps/dashboard/src/lib/query-client.tsx @@ -135,6 +135,18 @@ function matchesTabQuery(queryKey: readonly unknown[], tab: Tab) { ); } + if (tab.type === "commits") { + return ( + resourceType === "repo" && + (resourceName === "overview" || + resourceName === "branches" || + resourceName === "commits") && + isRepoQueryKeyInput(input) && + input.owner === owner && + input.repo === repo + ); + } + return ( resourceType === "issues" && (resourceName === "page" || diff --git a/apps/dashboard/src/lib/tab-store.ts b/apps/dashboard/src/lib/tab-store.ts index c2f6463..1f03e07 100644 --- a/apps/dashboard/src/lib/tab-store.ts +++ b/apps/dashboard/src/lib/tab-store.ts @@ -1,6 +1,12 @@ import { useSyncExternalStore } from "react"; -export type TabType = "pull" | "issue" | "review" | "repo" | "commit"; +export type TabType = + | "pull" + | "issue" + | "review" + | "repo" + | "commit" + | "commits"; export interface Tab { id: string; @@ -24,6 +30,7 @@ const VALID_TAB_TYPES = { review: true, repo: true, commit: true, + commits: true, } satisfies Record; function isValidTabType(type: unknown): type is TabType { diff --git a/apps/dashboard/src/routeTree.gen.ts b/apps/dashboard/src/routeTree.gen.ts index 98651e0..67692a7 100644 --- a/apps/dashboard/src/routeTree.gen.ts +++ b/apps/dashboard/src/routeTree.gen.ts @@ -38,6 +38,7 @@ import { Route as ProtectedOwnerRepoPullPullIdRouteImport } from './routes/_prot import { Route as ProtectedOwnerRepoIssuesNewRouteImport } from './routes/_protected/$owner/$repo/issues.new' import { Route as ProtectedOwnerRepoIssuesIssueIdRouteImport } from './routes/_protected/$owner/$repo/issues.$issueId' import { Route as ProtectedOwnerRepoCompareSplatRouteImport } from './routes/_protected/$owner/$repo/compare.$' +import { Route as ProtectedOwnerRepoCommitsSplatRouteImport } from './routes/_protected/$owner/$repo/commits.$' import { Route as ProtectedOwnerRepoCommitShaRouteImport } from './routes/_protected/$owner/$repo/commit.$sha' import { Route as ProtectedOwnerRepoBlobSplatRouteImport } from './routes/_protected/$owner/$repo/blob.$' @@ -193,6 +194,12 @@ const ProtectedOwnerRepoCompareSplatRoute = path: '/$owner/$repo/compare/$', getParentRoute: () => ProtectedRoute, } as any) +const ProtectedOwnerRepoCommitsSplatRoute = + ProtectedOwnerRepoCommitsSplatRouteImport.update({ + id: '/$owner/$repo/commits/$', + path: '/$owner/$repo/commits/$', + getParentRoute: () => ProtectedRoute, + } as any) const ProtectedOwnerRepoCommitShaRoute = ProtectedOwnerRepoCommitShaRouteImport.update({ id: '/$owner/$repo/commit/$sha', @@ -230,6 +237,7 @@ export interface FileRoutesByFullPath { '/$owner/$repo/': typeof ProtectedOwnerRepoIndexRoute '/$owner/$repo/blob/$': typeof ProtectedOwnerRepoBlobSplatRoute '/$owner/$repo/commit/$sha': typeof ProtectedOwnerRepoCommitShaRoute + '/$owner/$repo/commits/$': typeof ProtectedOwnerRepoCommitsSplatRoute '/$owner/$repo/compare/$': typeof ProtectedOwnerRepoCompareSplatRoute '/$owner/$repo/issues/$issueId': typeof ProtectedOwnerRepoIssuesIssueIdRoute '/$owner/$repo/issues/new': typeof ProtectedOwnerRepoIssuesNewRoute @@ -261,6 +269,7 @@ export interface FileRoutesByTo { '/$owner/$repo': typeof ProtectedOwnerRepoIndexRoute '/$owner/$repo/blob/$': typeof ProtectedOwnerRepoBlobSplatRoute '/$owner/$repo/commit/$sha': typeof ProtectedOwnerRepoCommitShaRoute + '/$owner/$repo/commits/$': typeof ProtectedOwnerRepoCommitsSplatRoute '/$owner/$repo/compare/$': typeof ProtectedOwnerRepoCompareSplatRoute '/$owner/$repo/issues/$issueId': typeof ProtectedOwnerRepoIssuesIssueIdRoute '/$owner/$repo/issues/new': typeof ProtectedOwnerRepoIssuesNewRoute @@ -295,6 +304,7 @@ export interface FileRoutesById { '/_protected/$owner/$repo/': typeof ProtectedOwnerRepoIndexRoute '/_protected/$owner/$repo/blob/$': typeof ProtectedOwnerRepoBlobSplatRoute '/_protected/$owner/$repo/commit/$sha': typeof ProtectedOwnerRepoCommitShaRoute + '/_protected/$owner/$repo/commits/$': typeof ProtectedOwnerRepoCommitsSplatRoute '/_protected/$owner/$repo/compare/$': typeof ProtectedOwnerRepoCompareSplatRoute '/_protected/$owner/$repo/issues/$issueId': typeof ProtectedOwnerRepoIssuesIssueIdRoute '/_protected/$owner/$repo/issues/new': typeof ProtectedOwnerRepoIssuesNewRoute @@ -329,6 +339,7 @@ export interface FileRouteTypes { | '/$owner/$repo/' | '/$owner/$repo/blob/$' | '/$owner/$repo/commit/$sha' + | '/$owner/$repo/commits/$' | '/$owner/$repo/compare/$' | '/$owner/$repo/issues/$issueId' | '/$owner/$repo/issues/new' @@ -360,6 +371,7 @@ export interface FileRouteTypes { | '/$owner/$repo' | '/$owner/$repo/blob/$' | '/$owner/$repo/commit/$sha' + | '/$owner/$repo/commits/$' | '/$owner/$repo/compare/$' | '/$owner/$repo/issues/$issueId' | '/$owner/$repo/issues/new' @@ -393,6 +405,7 @@ export interface FileRouteTypes { | '/_protected/$owner/$repo/' | '/_protected/$owner/$repo/blob/$' | '/_protected/$owner/$repo/commit/$sha' + | '/_protected/$owner/$repo/commits/$' | '/_protected/$owner/$repo/compare/$' | '/_protected/$owner/$repo/issues/$issueId' | '/_protected/$owner/$repo/issues/new' @@ -620,6 +633,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ProtectedOwnerRepoCompareSplatRouteImport parentRoute: typeof ProtectedRoute } + '/_protected/$owner/$repo/commits/$': { + id: '/_protected/$owner/$repo/commits/$' + path: '/$owner/$repo/commits/$' + fullPath: '/$owner/$repo/commits/$' + preLoaderRoute: typeof ProtectedOwnerRepoCommitsSplatRouteImport + parentRoute: typeof ProtectedRoute + } '/_protected/$owner/$repo/commit/$sha': { id: '/_protected/$owner/$repo/commit/$sha' path: '/$owner/$repo/commit/$sha' @@ -663,6 +683,7 @@ interface ProtectedRouteChildren { ProtectedOwnerRepoIndexRoute: typeof ProtectedOwnerRepoIndexRoute ProtectedOwnerRepoBlobSplatRoute: typeof ProtectedOwnerRepoBlobSplatRoute ProtectedOwnerRepoCommitShaRoute: typeof ProtectedOwnerRepoCommitShaRoute + ProtectedOwnerRepoCommitsSplatRoute: typeof ProtectedOwnerRepoCommitsSplatRoute ProtectedOwnerRepoCompareSplatRoute: typeof ProtectedOwnerRepoCompareSplatRoute ProtectedOwnerRepoIssuesIssueIdRoute: typeof ProtectedOwnerRepoIssuesIssueIdRoute ProtectedOwnerRepoIssuesNewRoute: typeof ProtectedOwnerRepoIssuesNewRoute @@ -685,6 +706,7 @@ const ProtectedRouteChildren: ProtectedRouteChildren = { ProtectedOwnerRepoIndexRoute: ProtectedOwnerRepoIndexRoute, ProtectedOwnerRepoBlobSplatRoute: ProtectedOwnerRepoBlobSplatRoute, ProtectedOwnerRepoCommitShaRoute: ProtectedOwnerRepoCommitShaRoute, + ProtectedOwnerRepoCommitsSplatRoute: ProtectedOwnerRepoCommitsSplatRoute, ProtectedOwnerRepoCompareSplatRoute: ProtectedOwnerRepoCompareSplatRoute, ProtectedOwnerRepoIssuesIssueIdRoute: ProtectedOwnerRepoIssuesIssueIdRoute, ProtectedOwnerRepoIssuesNewRoute: ProtectedOwnerRepoIssuesNewRoute, diff --git a/apps/dashboard/src/routes/_protected/$owner/$repo/commits.$.tsx b/apps/dashboard/src/routes/_protected/$owner/$repo/commits.$.tsx new file mode 100644 index 0000000..1bfd42d --- /dev/null +++ b/apps/dashboard/src/routes/_protected/$owner/$repo/commits.$.tsx @@ -0,0 +1,65 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { useMemo } from "react"; +import { RepoCommitsPage } from "#/components/repo/repo-commits-list"; +import { + githubRepoBranchesQueryOptions, + githubRepoCommitsQueryOptions, + githubRepoOverviewQueryOptions, +} from "#/lib/github.query"; +import { parseRepoRef } from "#/lib/parse-repo-ref"; +import { buildSeo, formatPageTitle } from "#/lib/seo"; + +export const Route = createFileRoute("/_protected/$owner/$repo/commits/$")({ + ssr: false, + loader: async ({ context, params }) => { + const scope = { userId: context.user.id }; + const splat = params._splat ?? ""; + const overviewOptions = githubRepoOverviewQueryOptions(scope, { + owner: params.owner, + repo: params.repo, + }); + const branchesOptions = githubRepoBranchesQueryOptions(scope, { + owner: params.owner, + repo: params.repo, + }); + + const [overview, branches] = await Promise.all([ + context.queryClient.ensureQueryData(overviewOptions), + context.queryClient.ensureQueryData(branchesOptions), + ]); + const { ref } = parseRepoRef(splat, { + branches: branches ?? undefined, + defaultBranch: overview?.defaultBranch, + }); + + void context.queryClient.prefetchInfiniteQuery( + githubRepoCommitsQueryOptions(scope, { + owner: params.owner, + repo: params.repo, + ref, + perPage: 30, + }), + ); + + return { ref }; + }, + head: ({ match, params }) => + buildSeo({ + path: match.pathname, + title: formatPageTitle(`Commits ยท ${params.owner}/${params.repo}`), + description: `View commit history for ${params.owner}/${params.repo}.`, + robots: "noindex", + }), + component: CommitsRoute, +}); + +function CommitsRoute() { + const { user } = Route.useRouteContext(); + const { owner, repo } = Route.useParams(); + const { ref } = Route.useLoaderData(); + const scope = useMemo(() => ({ userId: user.id }), [user.id]); + + return ( + + ); +} From f76217ae2d27416643f094a43f47fbb6dd5429ff Mon Sep 17 00:00:00 2001 From: theMackabu Date: Thu, 23 Apr 2026 14:04:54 -0700 Subject: [PATCH 3/9] add file history browser views --- .../src/components/repo/code-file-view.tsx | 37 +++++++++++++++++-- .../src/components/repo/folder-view.tsx | 1 + .../src/components/repo/latest-commit-bar.tsx | 26 +++++++++++-- .../src/components/repo/repo-commits-list.tsx | 28 +++++++++++--- apps/dashboard/src/lib/github.functions.ts | 2 + apps/dashboard/src/lib/github.query.ts | 16 +++++++- .../_protected/$owner/$repo/commits.$.tsx | 15 ++++++-- 7 files changed, 108 insertions(+), 17 deletions(-) diff --git a/apps/dashboard/src/components/repo/code-file-view.tsx b/apps/dashboard/src/components/repo/code-file-view.tsx index 9ebf429..62217cd 100644 --- a/apps/dashboard/src/components/repo/code-file-view.tsx +++ b/apps/dashboard/src/components/repo/code-file-view.tsx @@ -217,7 +217,13 @@ export function CodeFileView({ const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${currentRef}/${path}`; return (
    - +
    - +
    - +
    {formatRelativeTime(commit.date)} + + + Commits +
    ); diff --git a/apps/dashboard/src/components/repo/folder-view.tsx b/apps/dashboard/src/components/repo/folder-view.tsx index 07510fe..8f64cbd 100644 --- a/apps/dashboard/src/components/repo/folder-view.tsx +++ b/apps/dashboard/src/components/repo/folder-view.tsx @@ -56,6 +56,7 @@ export function FolderView({ scope={scope} defaultBranch={repo.defaultBranch} defaultBranchTip={repo.latestCommit} + path={currentPath} />
    {entries.map((entry, index) => ( diff --git a/apps/dashboard/src/components/repo/latest-commit-bar.tsx b/apps/dashboard/src/components/repo/latest-commit-bar.tsx index f2d1478..8ad049b 100644 --- a/apps/dashboard/src/components/repo/latest-commit-bar.tsx +++ b/apps/dashboard/src/components/repo/latest-commit-bar.tsx @@ -10,6 +10,7 @@ import { Link } from "@tanstack/react-router"; import { formatRelativeTime } from "#/lib/format-relative-time"; import { type GitHubQueryScope, + githubFileLastCommitQueryOptions, githubRefHeadCommitQueryOptions, } from "#/lib/github.query"; import type { RepoOverview } from "#/lib/github.types"; @@ -21,6 +22,7 @@ export function LatestCommitBar({ scope, defaultBranch, defaultBranchTip, + path, }: { owner: string; repoName: string; @@ -28,20 +30,34 @@ export function LatestCommitBar({ scope: GitHubQueryScope; defaultBranch: string; defaultBranchTip: RepoOverview["latestCommit"]; + path?: string; }) { - const tipQuery = useQuery({ + const pathCommitQuery = useQuery({ + ...githubFileLastCommitQueryOptions(scope, { + owner, + repo: repoName, + ref, + path: path ?? "", + }), + enabled: !!path, + refetchOnMount: false, + refetchOnWindowFocus: false, + }); + const refCommitQuery = useQuery({ ...githubRefHeadCommitQueryOptions(scope, { owner, repo: repoName, ref, }), + enabled: !path, placeholderData: - ref === defaultBranch && defaultBranchTip != null + !path && ref === defaultBranch && defaultBranchTip != null ? defaultBranchTip : undefined, refetchOnMount: false, refetchOnWindowFocus: false, }); + const tipQuery = path ? pathCommitQuery : refCommitQuery; const commit = tipQuery.data; @@ -97,7 +113,11 @@ export function LatestCommitBar({ {formatRelativeTime(commit.date)} diff --git a/apps/dashboard/src/components/repo/repo-commits-list.tsx b/apps/dashboard/src/components/repo/repo-commits-list.tsx index 290e003..4a89ce8 100644 --- a/apps/dashboard/src/components/repo/repo-commits-list.tsx +++ b/apps/dashboard/src/components/repo/repo-commits-list.tsx @@ -38,11 +38,13 @@ export function RepoCommitsPage({ owner, repo, currentRef, + currentPath, scope, }: { owner: string; repo: string; currentRef: string; + currentPath: string; scope: GitHubQueryScope; }) { const navigate = useNavigate(); @@ -58,6 +60,7 @@ export function RepoCommitsPage({ owner, repo, ref: currentRef, + ...(currentPath ? { path: currentPath } : {}), perPage: COMMITS_PAGE_SIZE, }), ); @@ -74,21 +77,31 @@ export function RepoCommitsPage({ if (branch === currentRef) return; void navigate({ to: "/$owner/$repo/commits/$", - params: { owner, repo, _splat: branch }, + params: { + owner, + repo, + _splat: currentPath ? `${branch}/${currentPath}` : branch, + }, }); }, - [currentRef, navigate, owner, repo], + [currentPath, currentRef, navigate, owner, repo], ); useRegisterTab( repoData ? { type: "commits", - title: `${repoData.name} commits`, - url: `/${owner}/${repo}/commits/${currentRef}`, + title: currentPath + ? `${currentPath.split("/").pop()} commits` + : `${repoData.name} commits`, + url: currentPath + ? `/${owner}/${repo}/commits/${currentRef}/${currentPath}` + : `/${owner}/${repo}/commits/${currentRef}`, repo: `${owner}/${repo}`, iconColor: "text-muted-foreground", - tabId: `commits:${owner}/${repo}`, + tabId: currentPath + ? `commits:${owner}/${repo}:${currentPath}` + : `commits:${owner}/${repo}`, } : null, ); @@ -134,6 +147,11 @@ export function RepoCommitsPage({ {owner}/{repo}

    Commits

    + {currentPath && ( +

    + {currentPath} +

    + )}
    {repoData ? ( ["github", scope.userId, "repo", "refHeadCommit", input] as const, commits: ( scope: GitHubQueryScope, - input: { owner: string; repo: string; ref: string; perPage?: number }, + input: { + owner: string; + repo: string; + ref: string; + path?: string; + perPage?: number; + }, ) => ["github", scope.userId, "repo", "commits", input] as const, treeEntryCommits: ( scope: GitHubQueryScope, @@ -861,7 +867,13 @@ export function githubRefHeadCommitQueryOptions( export function githubRepoCommitsQueryOptions( scope: GitHubQueryScope, - input: { owner: string; repo: string; ref: string; perPage?: number }, + input: { + owner: string; + repo: string; + ref: string; + path?: string; + perPage?: number; + }, ) { return infiniteQueryOptions({ queryKey: githubQueryKeys.repo.commits(scope, input), diff --git a/apps/dashboard/src/routes/_protected/$owner/$repo/commits.$.tsx b/apps/dashboard/src/routes/_protected/$owner/$repo/commits.$.tsx index 1bfd42d..139d279 100644 --- a/apps/dashboard/src/routes/_protected/$owner/$repo/commits.$.tsx +++ b/apps/dashboard/src/routes/_protected/$owner/$repo/commits.$.tsx @@ -27,7 +27,7 @@ export const Route = createFileRoute("/_protected/$owner/$repo/commits/$")({ context.queryClient.ensureQueryData(overviewOptions), context.queryClient.ensureQueryData(branchesOptions), ]); - const { ref } = parseRepoRef(splat, { + const { ref, path } = parseRepoRef(splat, { branches: branches ?? undefined, defaultBranch: overview?.defaultBranch, }); @@ -37,11 +37,12 @@ export const Route = createFileRoute("/_protected/$owner/$repo/commits/$")({ owner: params.owner, repo: params.repo, ref, + ...(path ? { path } : {}), perPage: 30, }), ); - return { ref }; + return { ref, path }; }, head: ({ match, params }) => buildSeo({ @@ -56,10 +57,16 @@ export const Route = createFileRoute("/_protected/$owner/$repo/commits/$")({ function CommitsRoute() { const { user } = Route.useRouteContext(); const { owner, repo } = Route.useParams(); - const { ref } = Route.useLoaderData(); + const { ref, path } = Route.useLoaderData(); const scope = useMemo(() => ({ userId: user.id }), [user.id]); return ( - + ); } From 6fc34d7385b88b0c93486fea9842161bba8c8eb1 Mon Sep 17 00:00:00 2001 From: theMackabu Date: Thu, 23 Apr 2026 14:15:02 -0700 Subject: [PATCH 4/9] add dynamic label to commit/history button --- .../src/components/layouts/dashboard-tabs.tsx | 32 ++++++- .../src/components/repo/code-file-view.tsx | 2 +- .../src/components/repo/folder-view.tsx | 1 + .../src/components/repo/latest-commit-bar.tsx | 4 +- .../src/components/repo/repo-commits-list.tsx | 86 ++++++++++++------- 5 files changed, 88 insertions(+), 37 deletions(-) diff --git a/apps/dashboard/src/components/layouts/dashboard-tabs.tsx b/apps/dashboard/src/components/layouts/dashboard-tabs.tsx index 450f48e..4037c26 100644 --- a/apps/dashboard/src/components/layouts/dashboard-tabs.tsx +++ b/apps/dashboard/src/components/layouts/dashboard-tabs.tsx @@ -299,7 +299,9 @@ function ScrollActiveTabIntoView({ useEffect(() => { const container = scrollRef.current; if (!container) return; - const activeTab = container.querySelector(".active"); + const activeTab = container.querySelector( + "[data-active='true']", + ); if (!activeTab) return; const { left: cLeft, right: cRight } = container.getBoundingClientRect(); @@ -333,9 +335,11 @@ const DetailTab = memo(function DetailTab({ onContextMenu: () => void; routerRef: React.RefObject>; }) { + const pathname = useRouterState({ select: (s) => s.location.pathname }); const preloadTab = () => { void preloadRouteOnce(routerRef.current, tab.url); }; + const isActive = isTabActive(tab, pathname); return ( {tab.avatarUrl ? ( ); }); + +function isTabActive(tab: Tab, pathname: string) { + if (tab.type === "repo") { + const repoPath = `/${tab.repo}`; + return ( + pathname === repoPath || + pathname === `${repoPath}/` || + pathname.startsWith(`${repoPath}/tree/`) || + pathname.startsWith(`${repoPath}/blob/`) + ); + } + + return normalizePath(pathname) === normalizePath(tab.url); +} + +function normalizePath(path: string) { + if (path.length > 1 && path.endsWith("/")) return path.slice(0, -1); + return path; +} diff --git a/apps/dashboard/src/components/repo/code-file-view.tsx b/apps/dashboard/src/components/repo/code-file-view.tsx index 62217cd..9a4bc84 100644 --- a/apps/dashboard/src/components/repo/code-file-view.tsx +++ b/apps/dashboard/src/components/repo/code-file-view.tsx @@ -544,7 +544,7 @@ function FileCommitBar({ className="-my-1 -mr-1 flex items-center gap-1 rounded-md px-2 py-1.5 font-medium text-foreground transition-colors hover:bg-surface-2" > - Commits + History
    diff --git a/apps/dashboard/src/components/repo/folder-view.tsx b/apps/dashboard/src/components/repo/folder-view.tsx index 8f64cbd..8c0b282 100644 --- a/apps/dashboard/src/components/repo/folder-view.tsx +++ b/apps/dashboard/src/components/repo/folder-view.tsx @@ -57,6 +57,7 @@ export function FolderView({ defaultBranch={repo.defaultBranch} defaultBranchTip={repo.latestCommit} path={currentPath} + historyLabel="History" />
    {entries.map((entry, index) => ( diff --git a/apps/dashboard/src/components/repo/latest-commit-bar.tsx b/apps/dashboard/src/components/repo/latest-commit-bar.tsx index 8ad049b..b3b0850 100644 --- a/apps/dashboard/src/components/repo/latest-commit-bar.tsx +++ b/apps/dashboard/src/components/repo/latest-commit-bar.tsx @@ -23,6 +23,7 @@ export function LatestCommitBar({ defaultBranch, defaultBranchTip, path, + historyLabel = "Commits", }: { owner: string; repoName: string; @@ -31,6 +32,7 @@ export function LatestCommitBar({ defaultBranch: string; defaultBranchTip: RepoOverview["latestCommit"]; path?: string; + historyLabel?: string; }) { const pathCommitQuery = useQuery({ ...githubFileLastCommitQueryOptions(scope, { @@ -122,7 +124,7 @@ export function LatestCommitBar({ className="-my-1 -mr-1 flex items-center gap-1 rounded-md px-2 py-1.5 font-medium text-foreground transition-colors hover:bg-surface-2" > - Commits + {historyLabel}
    diff --git a/apps/dashboard/src/components/repo/repo-commits-list.tsx b/apps/dashboard/src/components/repo/repo-commits-list.tsx index 4a89ce8..b210908 100644 --- a/apps/dashboard/src/components/repo/repo-commits-list.tsx +++ b/apps/dashboard/src/components/repo/repo-commits-list.tsx @@ -71,6 +71,7 @@ export function RepoCommitsPage({ ); const groups = useMemo(() => groupCommitsByDay(commits), [commits]); const repoData = overviewQuery.data; + const pathSegments = currentPath ? currentPath.split("/") : []; const handleBranchChange = useCallback( (branch: string) => { @@ -99,9 +100,7 @@ export function RepoCommitsPage({ : `/${owner}/${repo}/commits/${currentRef}`, repo: `${owner}/${repo}`, iconColor: "text-muted-foreground", - tabId: currentPath - ? `commits:${owner}/${repo}:${currentPath}` - : `commits:${owner}/${repo}`, + tabId: `commits:${owner}/${repo}`, } : null, ); @@ -137,37 +136,62 @@ export function RepoCommitsPage({ return (
    -
    -
    - - {owner}/{repo} - -

    Commits

    - {currentPath && ( -

    - {currentPath} +

    +
    +
    + +
    +
    +

    + + {owner} + + / + + {repo} + + {pathSegments.map((segment, index) => ( + + / + {segment} + + ))} +

    +

    + {currentPath + ? "Commits that touched this path." + : "Commits on this repository."}

    +
    +
    +
    + {repoData ? ( + + ) : ( +
    + + + {currentRef} + +
    )}
    - {repoData ? ( - - ) : ( -
    - - - {currentRef} - -
    - )}
    From 8dee5a6dfd4d3c93359afc3ee658342260122dc1 Mon Sep 17 00:00:00 2001 From: theMackabu Date: Thu, 23 Apr 2026 14:18:35 -0700 Subject: [PATCH 5/9] improve path segment labels --- .../src/components/repo/repo-commits-list.tsx | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/apps/dashboard/src/components/repo/repo-commits-list.tsx b/apps/dashboard/src/components/repo/repo-commits-list.tsx index b210908..50b8b6f 100644 --- a/apps/dashboard/src/components/repo/repo-commits-list.tsx +++ b/apps/dashboard/src/components/repo/repo-commits-list.tsx @@ -73,6 +73,11 @@ export function RepoCommitsPage({ const repoData = overviewQuery.data; const pathSegments = currentPath ? currentPath.split("/") : []; + const pathBreadcrumbs = pathSegments.map((segment, index) => ({ + segment, + path: pathSegments.slice(0, index + 1).join("/"), + })); + const handleBranchChange = useCallback( (branch: string) => { if (branch === currentRef) return; @@ -144,27 +149,34 @@ export function RepoCommitsPage({

    {owner} / {repo} - {pathSegments.map((segment, index) => ( - + {pathBreadcrumbs.map((breadcrumb) => ( + / - {segment} + + {breadcrumb.segment} + ))}

    From 850a1eb9f33a2e4b54274fbbea1247b3541ff318 Mon Sep 17 00:00:00 2001 From: theMackabu Date: Thu, 23 Apr 2026 14:21:46 -0700 Subject: [PATCH 6/9] apply coderabbit suggestions --- apps/dashboard/package.json | 5 +++-- .../src/components/repo/repo-commits-list.tsx | 21 +++++++++++++++++-- pnpm-lock.yaml | 18 ++++++++++++++++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index 53fa016..2901649 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -7,8 +7,8 @@ }, "scripts": { "predev": "node ../../scripts/link-worktree-dev-vars.mjs", - "dev": "NODE_OPTIONS='--max-old-space-size=4096' vite dev --port 3000", - "build": "NODE_OPTIONS='--max-old-space-size=4096' vite build", + "dev": "cross-env NODE_OPTIONS=--max-old-space-size=4096 vite dev --port 3000", + "build": "cross-env NODE_OPTIONS=--max-old-space-size=4096 vite build", "preview": "vite preview", "test": "vitest run", "format": "biome format", @@ -60,6 +60,7 @@ "@types/react": "^19.2.0", "@types/react-dom": "^19.2.0", "@vitejs/plugin-react": "^5.1.4", + "cross-env": "^10.1.0", "drizzle-kit": "^0.31.10", "jsdom": "^28.1.0", "react-scan": "^0.5.3", diff --git a/apps/dashboard/src/components/repo/repo-commits-list.tsx b/apps/dashboard/src/components/repo/repo-commits-list.tsx index 50b8b6f..ca9d62b 100644 --- a/apps/dashboard/src/components/repo/repo-commits-list.tsx +++ b/apps/dashboard/src/components/repo/repo-commits-list.tsx @@ -119,7 +119,8 @@ export function RepoCommitsPage({ if ( entry?.isIntersecting && commitsQuery.hasNextPage && - !commitsQuery.isFetchingNextPage + !commitsQuery.isFetchingNextPage && + !commitsQuery.isFetchNextPageError ) { void commitsQuery.fetchNextPage(); } @@ -132,11 +133,14 @@ export function RepoCommitsPage({ }, [ commitsQuery.hasNextPage, commitsQuery.isFetchingNextPage, + commitsQuery.isFetchNextPageError, commitsQuery.fetchNextPage, ]); if (overviewQuery.error) throw overviewQuery.error; - if (commitsQuery.error) throw commitsQuery.error; + if (commitsQuery.isError && !commitsQuery.isFetchNextPageError) { + throw commitsQuery.error; + } return (
    @@ -241,6 +245,19 @@ export function RepoCommitsPage({
    + ) : commitsQuery.isFetchNextPageError ? ( +
    +

    + Couldn't load more commits. +

    + +
    ) : commitsQuery.hasNextPage ? (
    ); diff --git a/apps/dashboard/src/components/repo/commits-link.tsx b/apps/dashboard/src/components/repo/commits-link.tsx new file mode 100644 index 0000000..b08e4ca --- /dev/null +++ b/apps/dashboard/src/components/repo/commits-link.tsx @@ -0,0 +1,45 @@ +import { GitCommitIcon } from "@diffkit/icons"; +import { cn } from "@diffkit/ui/lib/utils"; +import { Link } from "@tanstack/react-router"; +import type { ReactNode } from "react"; + +const commitsLinkClassName = + "-my-1 -mr-1 flex items-center gap-1 rounded-md px-2 py-1.5 font-medium text-foreground transition-colors hover:bg-surface-2"; + +export function buildCommitsLinkSplat(currentRef: string, path?: string) { + return path ? `${currentRef}/${path}` : currentRef; +} + +export function CommitsLink({ + owner, + repo, + currentRef, + path, + children = "History", + "aria-label": ariaLabel = "View commits", + className, +}: { + owner: string; + repo: string; + currentRef: string; + path?: string; + children?: ReactNode; + "aria-label"?: string; + className?: string; +}) { + return ( + + + {children} + + ); +} diff --git a/apps/dashboard/src/components/repo/latest-commit-bar.tsx b/apps/dashboard/src/components/repo/latest-commit-bar.tsx index b3b0850..5665efc 100644 --- a/apps/dashboard/src/components/repo/latest-commit-bar.tsx +++ b/apps/dashboard/src/components/repo/latest-commit-bar.tsx @@ -14,6 +14,7 @@ import { githubRefHeadCommitQueryOptions, } from "#/lib/github.query"; import type { RepoOverview } from "#/lib/github.types"; +import { CommitsLink } from "./commits-link"; export function LatestCommitBar({ owner, @@ -113,19 +114,9 @@ export function LatestCommitBar({ {formatRelativeTime(commit.date)} - - - {historyLabel} - + + {historyLabel} +
    ); diff --git a/apps/dashboard/src/lib/github.functions.ts b/apps/dashboard/src/lib/github.functions.ts index a2f5e76..695d317 100644 --- a/apps/dashboard/src/lib/github.functions.ts +++ b/apps/dashboard/src/lib/github.functions.ts @@ -1962,7 +1962,7 @@ async function getInstallationAccessIndex( const { installations, installationsAvailable, appUserOctokit } = await getGitHubAppUserInstallations(context.session.user.id); - if (!installationsAvailable) { + if (!installationsAvailable || !appUserOctokit) { debug( "installation-access", "app-user token unavailable, index not available (fail-open)", @@ -2012,7 +2012,7 @@ async function getInstallationAccessIndex( // this endpoint requires a GitHub App user-to-server token. const repos = await listPaginatedGitHubItems({ request: (page) => - appUserOctokit!.rest.apps.listInstallationReposForAuthenticatedUser( + appUserOctokit.rest.apps.listInstallationReposForAuthenticatedUser( { installation_id: installation.id, page, @@ -9985,7 +9985,10 @@ export const getNotifications = createServerFn({ method: "GET" }) }; // Fetch subject detail + comments (and reviews for PRs) in parallel - const subjectUrl = n.subject.url!; + const subjectUrl = n.subject.url; + if (!subjectUrl) { + return; + } const commentsUrl = `${subjectUrl}/comments`; const isPR = n.subject.type === "PullRequest"; const reviewsUrl = isPR ? `${subjectUrl}/reviews` : null; diff --git a/apps/dashboard/src/lib/protected-auth-cache.ts b/apps/dashboard/src/lib/protected-auth-cache.ts index ca83a83..a181800 100644 --- a/apps/dashboard/src/lib/protected-auth-cache.ts +++ b/apps/dashboard/src/lib/protected-auth-cache.ts @@ -39,4 +39,4 @@ export function clearProtectedRouteCachedAuth(): void { return; } cachedAuth = null; -} \ No newline at end of file +} From 032fceb0b4cf22c90b8555b12ff1420529c41998 Mon Sep 17 00:00:00 2001 From: theMackabu Date: Sat, 25 Apr 2026 17:12:47 -0700 Subject: [PATCH 9/9] shrink avatar, remove skeleton for infinte scroller --- .../src/components/repo/repo-commits-list.tsx | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/apps/dashboard/src/components/repo/repo-commits-list.tsx b/apps/dashboard/src/components/repo/repo-commits-list.tsx index ca9d62b..38bf301 100644 --- a/apps/dashboard/src/components/repo/repo-commits-list.tsx +++ b/apps/dashboard/src/components/repo/repo-commits-list.tsx @@ -1,6 +1,7 @@ import { GitBranchIcon, GitCommitIcon } from "@diffkit/icons"; import { Button } from "@diffkit/ui/components/button"; import { Skeleton } from "@diffkit/ui/components/skeleton"; +import { Spinner } from "@diffkit/ui/components/spinner"; import { Tooltip, TooltipContent, @@ -242,8 +243,8 @@ export function RepoCommitsPage({
    {commitsQuery.isFetchingNextPage ? ( -
    - +
    +
    ) : commitsQuery.isFetchNextPageError ? (
    @@ -297,17 +298,17 @@ function CommitRow({ {commit.author.login} ) : ( -
    - +
    +
    )}
    @@ -325,7 +326,7 @@ function CommitRow({

    -
    +
    - +
    - +
    ))}