feat(ui): rich forge entity views — commits, files/diff, timeline, checks, metadata chips

- server: new read routes for PR/MR commits, timeline, reviews, commit-statuses across github/gitlab/gitea; enrich PR/issue summaries with labels/assignees/milestone/commentsCount
- ui: forge facade gains getCommits/getTimeline/getChecks; shared ForgeEntityDetailView + section components; mounted into PR/MR views and issue sections
This commit is contained in:
2026-08-16 16:29:24 +00:00
parent f02c33700b
commit 92f0eced34
44 changed files with 3996 additions and 684 deletions
+30
View File
@@ -5,6 +5,8 @@ import type {
GitHubIssueGetResult,
GitHubIssuesListResult,
GitHubPullRequestContextResult,
GitHubPullRequestCommitsResult,
GitHubPullRequestTimelineResult,
GitHubPullRequestsListResult,
GitHubPullRequest,
GitHubPullRequestCreateInput,
@@ -295,4 +297,32 @@ export const createWebGitHubAPI = ({ urls }: WebGitHubAPIOptions): GitHubAPI =>
}
return payload;
},
async prCommits(directory: string, number: number, options?: { sourceRepo?: { owner: string; repo: string } | null }): Promise<GitHubPullRequestCommitsResult> {
const params = new URLSearchParams({ directory, number: String(number) });
if (options?.sourceRepo?.owner && options.sourceRepo.repo) {
params.set('owner', options.sourceRepo.owner);
params.set('repo', options.sourceRepo.repo);
}
const response = await runtimeFetch(urls.api('/api/github/pulls/commits', params), { method: 'GET', headers: { Accept: 'application/json' } });
const payload = await jsonOrNull<GitHubPullRequestCommitsResult & { error?: string }>(response);
if (!response.ok || !payload) {
throw new Error(payload?.error || response.statusText || 'Failed to load pull request commits');
}
return payload;
},
async prTimeline(directory: string, number: number, options?: { sourceRepo?: { owner: string; repo: string } | null }): Promise<GitHubPullRequestTimelineResult> {
const params = new URLSearchParams({ directory, number: String(number) });
if (options?.sourceRepo?.owner && options.sourceRepo.repo) {
params.set('owner', options.sourceRepo.owner);
params.set('repo', options.sourceRepo.repo);
}
const response = await runtimeFetch(urls.api('/api/github/pulls/timeline', params), { method: 'GET', headers: { Accept: 'application/json' } });
const payload = await jsonOrNull<GitHubPullRequestTimelineResult & { error?: string }>(response);
if (!response.ok || !payload) {
throw new Error(payload?.error || response.statusText || 'Failed to load pull request timeline');
}
return payload;
},
});