@volter/twin-github 0.1.2 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -36
- package/client/github-mirror.css +277 -319
- package/client/github-mirror.d.ts +418 -0
- package/client/github-mirror.js +485 -0
- package/client/github-mirror.tsx +153 -357
- package/client/pulls-rest.ts +159 -0
- package/client/pulls-workspace.tsx +841 -0
- package/dist/client/github-mirror.bundle.js +239 -0
- package/dist/client/github-mirror.css +916 -0
- package/dist/client/github-mirror.d.ts +418 -0
- package/dist/client/github-mirror.js +485 -0
- package/dist/client/github-mirror.tsx +1315 -0
- package/dist/client/pulls-rest.d.ts +42 -0
- package/dist/client/pulls-rest.js +140 -0
- package/dist/client/pulls-rest.ts +159 -0
- package/dist/client/pulls-workspace.bundle.js +22 -0
- package/dist/client/pulls-workspace.d.ts +114 -0
- package/dist/client/pulls-workspace.js +418 -0
- package/dist/client/pulls-workspace.tsx +841 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +40 -0
- package/dist/src/generated/graphql-sdl.gen.json +1 -0
- package/dist/src/generated/graphql.gen.json +1 -0
- package/dist/src/generated/surface.gen.json +1 -0
- package/dist/src/generated/ui.gen.json +1 -0
- package/dist/src/github-budget.d.ts +69 -0
- package/dist/src/github-budget.js +172 -0
- package/dist/src/github-capabilities.d.ts +5 -0
- package/dist/src/github-capabilities.js +4468 -0
- package/dist/src/github-conformance.d.ts +43 -0
- package/dist/src/github-conformance.js +76 -0
- package/dist/src/github-connector.d.ts +307 -0
- package/dist/src/github-connector.js +1398 -0
- package/dist/src/github-events.d.ts +41 -0
- package/dist/src/github-events.js +232 -0
- package/dist/src/github-git-http.d.ts +49 -0
- package/dist/src/github-git-http.js +185 -0
- package/dist/src/github-git-plane.d.ts +114 -0
- package/dist/src/github-git-plane.js +407 -0
- package/dist/src/github-mirror-state.d.ts +2 -0
- package/dist/src/github-mirror-state.js +335 -0
- package/dist/src/github-mirror-ui.d.ts +20 -0
- package/dist/src/github-mirror-ui.js +101 -0
- package/dist/src/github-server.d.ts +14 -0
- package/dist/src/github-server.js +240 -0
- package/dist/src/github-shared.d.ts +12 -0
- package/dist/src/github-shared.js +21 -0
- package/dist/src/github-twin.d.ts +1411 -0
- package/dist/src/github-twin.js +4084 -0
- package/dist/src/github-ui-conformance.d.ts +4 -0
- package/dist/src/github-ui-conformance.js +105 -0
- package/dist/src/github-ui-structure.d.ts +18 -0
- package/dist/src/github-ui-structure.js +251 -0
- package/dist/src/graphql-wire.d.ts +22 -0
- package/dist/src/graphql-wire.js +89 -0
- package/dist/src/index.d.ts +17 -0
- package/dist/src/index.js +104 -0
- package/dist/src/manifest.d.ts +8 -0
- package/dist/src/manifest.js +597 -0
- package/dist/src/npm-registry.d.ts +7 -0
- package/dist/src/npm-registry.js +47 -0
- package/dist/src/screens/app-installation.d.ts +3 -0
- package/dist/src/screens/app-installation.js +166 -0
- package/dist/src/screens/app-manifest.d.ts +3 -0
- package/dist/src/screens/app-manifest.js +81 -0
- package/dist/src/screens/oauth.d.ts +15 -0
- package/dist/src/screens/oauth.js +257 -0
- package/dist/src/screens/session.d.ts +8 -0
- package/dist/src/screens/session.js +170 -0
- package/dist/src/semantics/actions.d.ts +2 -0
- package/dist/src/semantics/actions.js +413 -0
- package/dist/src/semantics/activity.d.ts +4 -0
- package/dist/src/semantics/activity.js +161 -0
- package/dist/src/semantics/apps.d.ts +2 -0
- package/dist/src/semantics/apps.js +144 -0
- package/dist/src/semantics/branches.d.ts +2 -0
- package/dist/src/semantics/branches.js +136 -0
- package/dist/src/semantics/checks.d.ts +2 -0
- package/dist/src/semantics/checks.js +176 -0
- package/dist/src/semantics/code-scanning-upload.d.ts +2 -0
- package/dist/src/semantics/code-scanning-upload.js +97 -0
- package/dist/src/semantics/codespaces.d.ts +2 -0
- package/dist/src/semantics/codespaces.js +58 -0
- package/dist/src/semantics/commits.d.ts +2 -0
- package/dist/src/semantics/commits.js +109 -0
- package/dist/src/semantics/contents.d.ts +2 -0
- package/dist/src/semantics/contents.js +131 -0
- package/dist/src/semantics/deployments.d.ts +2 -0
- package/dist/src/semantics/deployments.js +127 -0
- package/dist/src/semantics/gists.d.ts +2 -0
- package/dist/src/semantics/gists.js +86 -0
- package/dist/src/semantics/git.d.ts +2 -0
- package/dist/src/semantics/git.js +235 -0
- package/dist/src/semantics/graphql.d.ts +7 -0
- package/dist/src/semantics/graphql.js +512 -0
- package/dist/src/semantics/index.d.ts +5 -0
- package/dist/src/semantics/index.js +62 -0
- package/dist/src/semantics/issues.d.ts +2 -0
- package/dist/src/semantics/issues.js +456 -0
- package/dist/src/semantics/keys.d.ts +2 -0
- package/dist/src/semantics/keys.js +66 -0
- package/dist/src/semantics/labels.d.ts +2 -0
- package/dist/src/semantics/labels.js +100 -0
- package/dist/src/semantics/meta.d.ts +12 -0
- package/dist/src/semantics/meta.js +144 -0
- package/dist/src/semantics/notifications.d.ts +4 -0
- package/dist/src/semantics/notifications.js +62 -0
- package/dist/src/semantics/orgs.d.ts +2 -0
- package/dist/src/semantics/orgs.js +420 -0
- package/dist/src/semantics/packages.d.ts +2 -0
- package/dist/src/semantics/packages.js +55 -0
- package/dist/src/semantics/pages.d.ts +2 -0
- package/dist/src/semantics/pages.js +176 -0
- package/dist/src/semantics/projects.d.ts +2 -0
- package/dist/src/semantics/projects.js +239 -0
- package/dist/src/semantics/pulls.d.ts +2 -0
- package/dist/src/semantics/pulls.js +462 -0
- package/dist/src/semantics/push-reactions.d.ts +101 -0
- package/dist/src/semantics/push-reactions.js +513 -0
- package/dist/src/semantics/releases.d.ts +19 -0
- package/dist/src/semantics/releases.js +231 -0
- package/dist/src/semantics/repo-invitations.d.ts +2 -0
- package/dist/src/semantics/repo-invitations.js +98 -0
- package/dist/src/semantics/repos.d.ts +17 -0
- package/dist/src/semantics/repos.js +390 -0
- package/dist/src/semantics/rulesets.d.ts +2 -0
- package/dist/src/semantics/rulesets.js +111 -0
- package/dist/src/semantics/search.d.ts +2 -0
- package/dist/src/semantics/search.js +84 -0
- package/dist/src/semantics/security.d.ts +2 -0
- package/dist/src/semantics/security.js +177 -0
- package/dist/src/semantics/shared.d.ts +61 -0
- package/dist/src/semantics/shared.js +151 -0
- package/dist/src/semantics/users.d.ts +2 -0
- package/dist/src/semantics/users.js +255 -0
- package/dist/src/semantics/webhooks.d.ts +2 -0
- package/dist/src/semantics/webhooks.js +107 -0
- package/dist/test-fixtures/github-a11y-reference.pr-list.SOURCE.md +56 -0
- package/dist/test-fixtures/github-a11y-reference.pr-list.json +1447 -0
- package/dist/test-fixtures/github-comment-schema.SOURCE.md +11 -0
- package/dist/test-fixtures/github-comment-schema.json +702 -0
- package/dist/test-fixtures/github-known-deviations.json +94 -0
- package/dist/test-fixtures/github-openapi-operations.SOURCE.md +76 -0
- package/dist/test-fixtures/github-openapi-operations.json +362 -0
- package/dist/test-fixtures/github-pull-schema.SOURCE.md +37 -0
- package/dist/test-fixtures/github-pull-schema.json +3601 -0
- package/dist/test-fixtures/github-review-schema.SOURCE.md +12 -0
- package/dist/test-fixtures/github-review-schema.json +236 -0
- package/package.json +20 -11
- package/src/cli.ts +7 -6
- package/src/generated/graphql-sdl.gen.json +1 -0
- package/src/generated/graphql.gen.json +1 -0
- package/src/generated/surface.gen.json +1 -0
- package/src/generated/ui.gen.json +1 -0
- package/src/github-a11y-snapshot.uitest.ts +5 -5
- package/src/github-budget.ts +4 -4
- package/src/github-capabilities.ts +1993 -556
- package/src/github-conformance.ts +12 -7
- package/src/github-connector.ts +108 -96
- package/src/github-events.ts +225 -95
- package/src/github-git-http.ts +48 -84
- package/src/github-git-plane.ts +247 -385
- package/src/github-journey.uitest.ts +28 -44
- package/src/github-mirror-state.ts +23 -61
- package/src/github-mirror-ui.ts +16 -10
- package/src/github-server.ts +158 -60
- package/src/github-shared.ts +1 -1
- package/src/github-twin.ts +1525 -4494
- package/src/github-ui-conformance.ts +7 -8
- package/src/github-ui-structure.ts +19 -18
- package/src/graphql-wire.ts +115 -0
- package/src/index.ts +50 -7
- package/src/manifest.ts +605 -0
- package/src/npm-registry.ts +43 -0
- package/src/screens/app-installation.tsx +217 -0
- package/src/screens/app-manifest.tsx +97 -0
- package/src/screens/oauth.tsx +258 -0
- package/src/screens/session.tsx +193 -0
- package/src/semantics/actions.ts +395 -0
- package/src/semantics/activity.ts +171 -0
- package/src/semantics/apps.ts +133 -0
- package/src/semantics/branches.ts +133 -0
- package/src/semantics/checks.ts +163 -0
- package/src/semantics/code-scanning-upload.ts +92 -0
- package/src/semantics/codespaces.ts +58 -0
- package/src/semantics/commits.ts +109 -0
- package/src/semantics/contents.ts +112 -0
- package/src/semantics/deployments.ts +116 -0
- package/src/semantics/gists.ts +85 -0
- package/src/semantics/git.ts +226 -0
- package/src/semantics/graphql.ts +505 -0
- package/src/semantics/index.ts +68 -0
- package/src/semantics/issues.ts +434 -0
- package/src/semantics/keys.ts +66 -0
- package/src/semantics/labels.ts +91 -0
- package/src/semantics/meta.ts +139 -0
- package/src/semantics/notifications.ts +58 -0
- package/src/semantics/orgs.ts +383 -0
- package/src/semantics/packages.ts +59 -0
- package/src/semantics/pages.ts +154 -0
- package/src/semantics/projects.ts +246 -0
- package/src/semantics/pulls.ts +421 -0
- package/src/semantics/push-reactions.ts +471 -0
- package/src/semantics/releases.ts +213 -0
- package/src/semantics/repo-invitations.ts +112 -0
- package/src/semantics/repos.ts +373 -0
- package/src/semantics/rulesets.ts +107 -0
- package/src/semantics/search.ts +83 -0
- package/src/semantics/security.ts +153 -0
- package/src/semantics/shared.ts +181 -0
- package/src/semantics/users.ts +252 -0
- package/src/semantics/webhooks.ts +101 -0
- package/test-fixtures/github-known-deviations.json +7 -8
- package/test-fixtures/github-openapi-operations.json +46 -227
- package/src/github-graphql.ts +0 -398
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { ChangedFile, Comment, Commit, PullRequest, Review } from "./github-mirror.js";
|
|
3
|
+
import type { Check, RepoFacts, StateEvent } from "./pulls-rest.js";
|
|
4
|
+
export declare function Octicon({ name, size, className }: {
|
|
5
|
+
name: string;
|
|
6
|
+
size?: number;
|
|
7
|
+
className?: string;
|
|
8
|
+
}): React.JSX.Element;
|
|
9
|
+
/** A person's default picture, as GitHub draws one for an account without an upload: a 5x5 mirrored identicon. */
|
|
10
|
+
export declare function Avatar({ login, size, src }: {
|
|
11
|
+
login?: string;
|
|
12
|
+
size?: number;
|
|
13
|
+
src?: string;
|
|
14
|
+
}): React.JSX.Element;
|
|
15
|
+
/** GitHub's relative time: "now", "3 minutes ago", "yesterday", then "on Sep 24". */
|
|
16
|
+
export declare function timeAgo(iso?: string, now?: number): string;
|
|
17
|
+
/** A label pill in the label's own colour, its text dark or light by the colour's lightness. */
|
|
18
|
+
export declare function Label({ name, color }: {
|
|
19
|
+
name: string;
|
|
20
|
+
color?: string;
|
|
21
|
+
}): React.JSX.Element;
|
|
22
|
+
type PullState = "open" | "merged" | "closed" | "draft";
|
|
23
|
+
export declare function pullState(pr: PullRequest): PullState;
|
|
24
|
+
export declare function StateBadge({ pr }: {
|
|
25
|
+
pr: PullRequest;
|
|
26
|
+
}): React.JSX.Element;
|
|
27
|
+
/** The CI rollup beside a title: a green check, a red cross or an amber dot. */
|
|
28
|
+
export declare function CiStatus({ status }: {
|
|
29
|
+
status?: string;
|
|
30
|
+
}): React.JSX.Element | null;
|
|
31
|
+
/** What the latest review by each reviewer says, as GitHub decides a pull request's review state. */
|
|
32
|
+
export declare function reviewDecision(pr: PullRequest, reviews: Review[]): "approved" | "changes_requested" | "review_required" | undefined;
|
|
33
|
+
export type RepoTab = "code" | "issues" | "pulls" | "actions" | "projects" | "security" | "insights" | "settings" | "discussions";
|
|
34
|
+
export declare function RepoHeader({ repo, viewer, active, facts, openPulls, projects, onTab, onHome, onInbox, onSearch }: {
|
|
35
|
+
repo: string;
|
|
36
|
+
viewer?: string;
|
|
37
|
+
active?: RepoTab;
|
|
38
|
+
facts?: RepoFacts;
|
|
39
|
+
openPulls: number;
|
|
40
|
+
projects: number;
|
|
41
|
+
onTab: (tab: RepoTab) => void;
|
|
42
|
+
onHome: () => void;
|
|
43
|
+
onInbox: () => void;
|
|
44
|
+
onSearch: () => void;
|
|
45
|
+
}): React.JSX.Element;
|
|
46
|
+
/** The page footer every GitHub page ends with. */
|
|
47
|
+
export declare function SiteFooter(): React.JSX.Element;
|
|
48
|
+
/** The list's rows: each pull request's state icon, title, CI, labels, meta line, assignees and comment count. */
|
|
49
|
+
export declare function PrList({ prs, reviews, labelColors, showRepo, onSelect }: {
|
|
50
|
+
prs: PullRequest[];
|
|
51
|
+
reviews: Review[];
|
|
52
|
+
labelColors?: Record<string, string>;
|
|
53
|
+
showRepo?: boolean;
|
|
54
|
+
selectedKey?: string;
|
|
55
|
+
onSelect?: (key: string) => void;
|
|
56
|
+
}): React.JSX.Element;
|
|
57
|
+
export declare function PullsListPage({ repo, prs, reviews, facts, listState, onListState, onSelect }: {
|
|
58
|
+
repo: string;
|
|
59
|
+
prs: PullRequest[];
|
|
60
|
+
reviews: Review[];
|
|
61
|
+
facts?: RepoFacts;
|
|
62
|
+
listState: "open" | "closed";
|
|
63
|
+
onListState: (s: "open" | "closed") => void;
|
|
64
|
+
onSelect: (key: string) => void;
|
|
65
|
+
}): React.JSX.Element;
|
|
66
|
+
export type PullTab = "conversation" | "commits" | "checks" | "files";
|
|
67
|
+
export type PullActions = {
|
|
68
|
+
comment: (body: string) => Promise<void>;
|
|
69
|
+
merge: (title: string, message: string) => Promise<void>;
|
|
70
|
+
close: (body?: string) => Promise<void>;
|
|
71
|
+
reopen: () => Promise<void>;
|
|
72
|
+
};
|
|
73
|
+
/** The Conversation tab's timeline, the merge box and the comment form. */
|
|
74
|
+
export declare function Conversation({ pr, reviews, commits, comments, reviewComments, checks, stateEvents, viewer, actions }: {
|
|
75
|
+
pr?: PullRequest;
|
|
76
|
+
reviews: Review[];
|
|
77
|
+
commits: Commit[];
|
|
78
|
+
comments: Comment[];
|
|
79
|
+
reviewComments: Comment[];
|
|
80
|
+
files?: ChangedFile[];
|
|
81
|
+
checks?: Check[];
|
|
82
|
+
stateEvents?: StateEvent[];
|
|
83
|
+
viewer?: string;
|
|
84
|
+
actions?: PullActions;
|
|
85
|
+
}): React.JSX.Element | null;
|
|
86
|
+
/** The sidebar: Reviewers, Assignees, Labels, Projects, Milestone, Development, Participants. */
|
|
87
|
+
export declare function Aside({ pr, reviews, comments, labelColors }: {
|
|
88
|
+
pr?: PullRequest;
|
|
89
|
+
reviews: Review[];
|
|
90
|
+
commits?: Commit[];
|
|
91
|
+
files?: ChangedFile[];
|
|
92
|
+
comments?: Comment[];
|
|
93
|
+
labelColors?: Record<string, string>;
|
|
94
|
+
}): React.JSX.Element;
|
|
95
|
+
/** The Files changed tab: each file's header (diffstat, path) and its unified diff with both line-number gutters. */
|
|
96
|
+
export declare function FilesDiff({ files }: {
|
|
97
|
+
files: ChangedFile[];
|
|
98
|
+
}): React.JSX.Element;
|
|
99
|
+
export declare function PullPage({ pr, reviews, commits, comments, reviewComments, files, checks, stateEvents, viewer, labelColors, tab, onTab, actions }: {
|
|
100
|
+
pr?: PullRequest;
|
|
101
|
+
reviews: Review[];
|
|
102
|
+
commits: Commit[];
|
|
103
|
+
comments: Comment[];
|
|
104
|
+
reviewComments: Comment[];
|
|
105
|
+
files: ChangedFile[];
|
|
106
|
+
checks: Check[];
|
|
107
|
+
stateEvents: StateEvent[];
|
|
108
|
+
viewer?: string;
|
|
109
|
+
labelColors: Record<string, string>;
|
|
110
|
+
tab: PullTab;
|
|
111
|
+
onTab: (t: PullTab) => void;
|
|
112
|
+
actions: PullActions;
|
|
113
|
+
}): React.JSX.Element;
|
|
114
|
+
export {};
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
// The pull-request WORKSPACE: GitHub's repository header, its pull request list (/{owner}/{repo}/pulls) and a pull
|
|
3
|
+
// request's page (/{owner}/{repo}/pull/{n}, with its Commits, Checks and Files changed tabs). Authored, never
|
|
4
|
+
// captured (docs/contributing/architecture.md, "Screens"): the glyphs are Primer's published Octicons (MIT), the rest
|
|
5
|
+
// is written here. Everything shown was read through GitHub's REST API (pulls-rest.ts) and every control writes
|
|
6
|
+
// through it: a comment (POST /issues/{n}/comments), a merge (PUT /pulls/{n}/merge), close and reopen
|
|
7
|
+
// (PATCH /pulls/{n} state).
|
|
8
|
+
import React, { useState } from "react";
|
|
9
|
+
// ── Octicons (16px viewBox unless noted) ────────────────────────────────────────────────────────────────────────
|
|
10
|
+
const ICONS = {
|
|
11
|
+
"mark-github": "M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z",
|
|
12
|
+
"git-pull-request": "M1.5 3.25a2.25 2.25 0 1 1 3 2.122v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.25 2.25 0 0 1 1.5 3.25Zm5.677-.177L9.573.677A.25.25 0 0 1 10 .854V2.5h1A2.5 2.5 0 0 1 13.5 5v5.628a2.251 2.251 0 1 1-1.5 0V5a1 1 0 0 0-1-1h-1v1.646a.25.25 0 0 1-.427.177L7.177 3.427a.25.25 0 0 1 0-.354ZM3.75 2.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm0 9.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm8.25.75a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Z",
|
|
13
|
+
"git-merge": "M5.45 5.154A4.25 4.25 0 0 0 9.25 7.5h1.378a2.251 2.251 0 1 1 0 1.5H9.25A5.734 5.734 0 0 1 5 7.123v3.505a2.25 2.25 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.95-.218ZM4.25 13.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm8.5-4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5ZM5 3.25a.75.75 0 1 0 0 .005V3.25Z",
|
|
14
|
+
"git-pull-request-closed": "M3.25 1A2.25 2.25 0 0 1 4 5.372v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.251 2.251 0 0 1 3.25 1Zm9.5 5.5a.75.75 0 0 1 .75.75v3.378a2.251 2.251 0 1 1-1.5 0V7.25a.75.75 0 0 1 .75-.75Zm-2.03-5.273a.75.75 0 0 1 1.06 0l.97.97.97-.97a.748.748 0 0 1 1.265.332.75.75 0 0 1-.205.729l-.97.97.97.97a.751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018l-.97-.97-.97.97a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734l.97-.97-.97-.97a.75.75 0 0 1 0-1.06ZM2.5 3.25a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0ZM3.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm9.5 0a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z",
|
|
15
|
+
"git-pull-request-draft": "M3.25 1A2.25 2.25 0 0 1 4 5.372v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.251 2.251 0 0 1 3.25 1Zm9.5 14a2.25 2.25 0 1 1 0-4.5 2.25 2.25 0 0 1 0 4.5ZM2.5 3.25a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0ZM3.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm9.5 0a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM14 7.5a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0Zm0-4.25a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0Z",
|
|
16
|
+
check: "M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z",
|
|
17
|
+
x: "M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z",
|
|
18
|
+
"dot-fill": "M8 4a4 4 0 1 1 0 8 4 4 0 0 1 0-8Z",
|
|
19
|
+
comment: "M1 2.75C1 1.784 1.784 1 2.75 1h10.5c.966 0 1.75.784 1.75 1.75v7.5A1.75 1.75 0 0 1 13.25 12H9.06l-2.573 2.573A1.458 1.458 0 0 1 4 13.543V12H2.75A1.75 1.75 0 0 1 1 10.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h4.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z",
|
|
20
|
+
"comment-discussion": "M1.75 1h8.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 10.25 10H7.061l-2.574 2.573A1.458 1.458 0 0 1 2 11.543V10h-.25A1.75 1.75 0 0 1 0 8.25v-5.5C0 1.784.784 1 1.75 1ZM1.5 2.75v5.5c0 .138.112.25.25.25h1a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h3.5a.25.25 0 0 0 .25-.25v-5.5a.25.25 0 0 0-.25-.25h-8.5a.25.25 0 0 0-.25.25Zm13 2a.25.25 0 0 0-.25-.25h-.5a.75.75 0 0 1 0-1.5h.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 14.25 12H14v1.543a1.458 1.458 0 0 1-2.487 1.03L9.22 12.28a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215l2.22 2.22v-2.19a.75.75 0 0 1 .75-.75h1a.25.25 0 0 0 .25-.25Z",
|
|
21
|
+
code: "m11.28 3.22 4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L13.94 8l-3.72-3.72a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215Zm-6.56 0a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L2.06 8l3.72 3.72a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L.47 8.53a.75.75 0 0 1 0-1.06Z",
|
|
22
|
+
"issue-opened": "M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Z",
|
|
23
|
+
play: "M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm4.879-2.773 4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559V5.442a.25.25 0 0 1 .379-.215Z",
|
|
24
|
+
table: "M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25ZM6.5 6.5v8h7.75a.25.25 0 0 0 .25-.25V6.5Zm8-1.5V1.75a.25.25 0 0 0-.25-.25H6.5V5Zm-13 1.5v7.75c0 .138.112.25.25.25H5v-8ZM5 5V1.5H1.75a.25.25 0 0 0-.25.25V5Z",
|
|
25
|
+
shield: "M7.467.133a1.748 1.748 0 0 1 1.066 0l5.25 1.68A1.75 1.75 0 0 1 15 3.48V7c0 1.566-.32 3.182-1.303 4.682-.983 1.498-2.585 2.813-5.032 3.855a1.697 1.697 0 0 1-1.33 0c-2.447-1.042-4.049-2.357-5.032-3.855C1.32 10.182 1 8.566 1 7V3.48a1.755 1.755 0 0 1 1.217-1.667Zm.61 1.429a.25.25 0 0 0-.153 0l-5.25 1.68a.25.25 0 0 0-.174.238V7c0 1.358.275 2.666 1.057 3.86.784 1.194 2.121 2.34 4.366 3.297a.196.196 0 0 0 .154 0c2.245-.956 3.582-2.104 4.366-3.298C13.225 9.666 13.5 8.36 13.5 7V3.48a.251.251 0 0 0-.174-.237l-5.25-1.68Z",
|
|
26
|
+
graph: "M1.5 1.75V13.5h13.75a.75.75 0 0 1 0 1.5H.75a.75.75 0 0 1-.75-.75V1.75a.75.75 0 0 1 1.5 0Zm14.28 2.53-5.25 5.25a.75.75 0 0 1-1.06 0L7 7.06 4.28 9.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.25-3.25a.75.75 0 0 1 1.06 0L10 7.94l4.72-4.72a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z",
|
|
27
|
+
gear: "M8 0a8.2 8.2 0 0 1 .701.031C9.444.095 9.99.645 10.16 1.29l.288 1.107c.018.066.079.158.212.224.231.114.454.243.668.386.123.082.233.09.299.071l1.103-.303c.644-.176 1.392.021 1.82.63.27.385.506.792.704 1.218.315.675.111 1.422-.364 1.891l-.814.806c-.049.048-.098.147-.088.294.016.257.016.515 0 .772-.01.147.038.246.088.294l.814.806c.475.469.679 1.216.364 1.891a7.977 7.977 0 0 1-.704 1.217c-.428.61-1.176.807-1.82.63l-1.102-.302c-.067-.019-.177-.011-.3.071a5.909 5.909 0 0 1-.668.386c-.133.066-.194.158-.211.224l-.29 1.106c-.168.646-.715 1.196-1.458 1.26a8.006 8.006 0 0 1-1.402 0c-.743-.064-1.289-.614-1.458-1.26l-.289-1.106c-.018-.066-.079-.158-.212-.224a5.738 5.738 0 0 1-.668-.386c-.123-.082-.233-.09-.299-.071l-1.103.303c-.644.176-1.392-.021-1.82-.63a8.12 8.12 0 0 1-.704-1.218c-.315-.675-.111-1.422.363-1.891l.815-.806c.05-.048.098-.147.088-.294a6.214 6.214 0 0 1 0-.772c.01-.147-.038-.246-.088-.294l-.815-.806C.635 6.045.431 5.298.746 4.623a7.92 7.92 0 0 1 .704-1.217c.428-.61 1.176-.807 1.82-.63l1.102.302c.067.019.177.011.3-.071.214-.143.437-.272.668-.386.133-.066.194-.158.211-.224l.29-1.106C6.009.645 6.556.095 7.299.03 7.53.01 7.764 0 8 0Zm-.571 1.525c-.036.003-.108.036-.137.146l-.289 1.105c-.147.561-.549.967-.998 1.189-.173.086-.34.183-.5.29-.417.278-.97.423-1.529.27l-1.103-.303c-.109-.03-.175.016-.195.045-.22.312-.412.644-.573.99-.014.031-.021.11.059.19l.815.806c.411.406.562.957.53 1.456a4.709 4.709 0 0 0 0 .582c.032.499-.119 1.05-.53 1.456l-.815.806c-.081.08-.073.159-.059.19.162.346.353.677.573.989.02.03.085.076.195.046l1.102-.303c.56-.153 1.113-.008 1.53.27.161.107.328.204.501.29.447.222.85.629.997 1.189l.289 1.105c.029.109.101.143.137.146a6.6 6.6 0 0 0 1.142 0c.036-.003.108-.036.137-.146l.289-1.105c.147-.561.549-.967.998-1.189.173-.086.34-.183.5-.29.417-.278.97-.423 1.529-.27l1.103.303c.109.029.175-.016.195-.045.22-.313.411-.644.573-.99.014-.031.021-.11-.059-.19l-.815-.806c-.411-.406-.562-.957-.53-1.456a4.709 4.709 0 0 0 0-.582c-.032-.499.119-1.05.53-1.456l.815-.806c.081-.08.073-.159.059-.19a6.464 6.464 0 0 0-.573-.989c-.02-.03-.085-.076-.195-.046l-1.102.303c-.56.153-1.113.008-1.53-.27a4.44 4.44 0 0 0-.501-.29c-.447-.222-.85-.629-.997-1.189l-.289-1.105c-.029-.11-.101-.143-.137-.146a6.6 6.6 0 0 0-1.142 0ZM11 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0ZM9.5 8a1.5 1.5 0 1 0-3.001.001A1.5 1.5 0 0 0 9.5 8Z",
|
|
28
|
+
"git-commit": "M11.93 8.5a4.002 4.002 0 0 1-7.86 0H.75a.75.75 0 0 1 0-1.5h3.32a4.002 4.002 0 0 1 7.86 0h3.32a.75.75 0 0 1 0 1.5Zm-1.43-.75a2.5 2.5 0 1 0-5 0 2.5 2.5 0 0 0 5 0Z",
|
|
29
|
+
"file-diff": "M1 1.75C1 .784 1.784 0 2.75 0h7.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16H2.75A1.75 1.75 0 0 1 1 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h10.5a.25.25 0 0 0 .25-.25V4.664a.25.25 0 0 0-.073-.177l-2.914-2.914a.25.25 0 0 0-.177-.073ZM8 3.25a.75.75 0 0 1 .75.75v1.5h1.5a.75.75 0 0 1 0 1.5h-1.5v1.5a.75.75 0 0 1-1.5 0V7h-1.5a.75.75 0 0 1 0-1.5h1.5V4A.75.75 0 0 1 8 3.25Zm-3 8a.75.75 0 0 1 .75-.75h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1-.75-.75Z",
|
|
30
|
+
checklist: "M2.5 1.75v11.5c0 .138.112.25.25.25h3.17a.75.75 0 0 1 0 1.5H2.75A1.75 1.75 0 0 1 1 13.25V1.75C1 .784 1.784 0 2.75 0h8.5C12.216 0 13 .784 13 1.75v7.736a.75.75 0 0 1-1.5 0V1.75a.25.25 0 0 0-.25-.25h-8.5a.25.25 0 0 0-.25.25Zm13.274 9.537v-.001l-4.557 4.45a.75.75 0 0 1-1.055-.008l-1.943-1.95a.75.75 0 0 1 1.062-1.058l1.419 1.425 4.026-3.932a.75.75 0 1 1 1.048 1.074ZM4.75 4h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM4 7.75A.75.75 0 0 1 4.75 7h2a.75.75 0 0 1 0 1.5h-2A.75.75 0 0 1 4 7.75Z",
|
|
31
|
+
eye: "M8 2c1.981 0 3.671.992 4.933 2.078 1.27 1.091 2.187 2.345 2.637 3.023a1.62 1.62 0 0 1 0 1.798c-.45.678-1.367 1.932-2.637 3.023C11.67 13.008 9.981 14 8 14c-1.981 0-3.671-.992-4.933-2.078C1.797 10.83.88 9.576.43 8.898a1.62 1.62 0 0 1 0-1.798c.45-.677 1.367-1.931 2.637-3.022C4.33 2.992 6.019 2 8 2ZM1.679 7.932a.12.12 0 0 0 0 .136c.411.622 1.241 1.75 2.366 2.717C5.176 11.758 6.527 12.5 8 12.5c1.473 0 2.825-.742 3.955-1.715 1.124-.967 1.954-2.096 2.366-2.717a.12.12 0 0 0 0-.136c-.412-.621-1.242-1.75-2.366-2.717C10.824 4.242 9.473 3.5 8 3.5c-1.473 0-2.825.742-3.955 1.715-1.124.967-1.954 2.096-2.366 2.717ZM8 10a2 2 0 1 1-.001-3.999A2 2 0 0 1 8 10Z",
|
|
32
|
+
search: "M10.68 11.74a6 6 0 0 1-7.922-8.982 6 6 0 0 1 8.982 7.922l3.04 3.04a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215ZM11.5 7a4.499 4.499 0 1 0-8.997 0A4.499 4.499 0 0 0 11.5 7Z",
|
|
33
|
+
"three-bars": "M1 2.75A.75.75 0 0 1 1.75 2h12.5a.75.75 0 0 1 0 1.5H1.75A.75.75 0 0 1 1 2.75Zm0 5A.75.75 0 0 1 1.75 7h12.5a.75.75 0 0 1 0 1.5H1.75A.75.75 0 0 1 1 7.75ZM1.75 12h12.5a.75.75 0 0 1 0 1.5H1.75a.75.75 0 0 1 0-1.5Z",
|
|
34
|
+
inbox: "M2.8 2.06A1.75 1.75 0 0 1 4.41 1h7.18c.7 0 1.333.417 1.61 1.06l2.74 6.395c.04.093.06.194.06.295v4.5A1.75 1.75 0 0 1 14.25 15H1.75A1.75 1.75 0 0 1 0 13.25v-4.5c0-.101.02-.202.06-.295Zm1.61.44a.25.25 0 0 0-.23.152L1.887 8H4.75a.75.75 0 0 1 .6.3L6.625 10h2.75l1.275-1.7a.75.75 0 0 1 .6-.3h2.863L11.82 2.652a.25.25 0 0 0-.23-.152Zm10.09 7h-2.875l-1.275 1.7a.75.75 0 0 1-.6.3h-3.5a.75.75 0 0 1-.6-.3L4.375 9.5H1.5v3.75c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25Z",
|
|
35
|
+
plus: "M7.75 2a.75.75 0 0 1 .75.75V7h4.25a.75.75 0 0 1 0 1.5H8.5v4.25a.75.75 0 0 1-1.5 0V8.5H2.75a.75.75 0 0 1 0-1.5H7V2.75A.75.75 0 0 1 7.75 2Z",
|
|
36
|
+
"triangle-down": "m4.427 7.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427Z",
|
|
37
|
+
"chevron-down": "M12.78 5.22a.749.749 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.06 0L3.22 6.28a.749.749 0 1 1 1.06-1.06L8 8.939l3.72-3.719a.749.749 0 0 1 1.06 0Z",
|
|
38
|
+
tag: "M1 7.775V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 0 1 0 2.474l-5.026 5.026a1.75 1.75 0 0 1-2.474 0l-6.25-6.25A1.752 1.752 0 0 1 1 7.775Zm1.5 0c0 .066.026.13.073.177l6.25 6.25a.25.25 0 0 0 .354 0l5.025-5.025a.25.25 0 0 0 0-.354l-6.25-6.25a.25.25 0 0 0-.177-.073H2.75a.25.25 0 0 0-.25.25ZM6 5a1 1 0 1 1 0 2 1 1 0 0 1 0-2Z",
|
|
39
|
+
milestone: "M7.75 0a.75.75 0 0 1 .75.75V3h3.634c.414 0 .814.147 1.13.414l2.07 1.75a1.75 1.75 0 0 1 0 2.672l-2.07 1.75a1.75 1.75 0 0 1-1.13.414H8.5v5.25a.75.75 0 0 1-1.5 0V10H2.75A1.75 1.75 0 0 1 1 8.25v-3.5C1 3.784 1.784 3 2.75 3H7V.75A.75.75 0 0 1 7.75 0Zm4.384 8.5a.25.25 0 0 0 .161-.06l2.07-1.75a.248.248 0 0 0 0-.38l-2.07-1.75a.25.25 0 0 0-.161-.06H2.75a.25.25 0 0 0-.25.25v3.5c0 .138.112.25.25.25h9.384Z",
|
|
40
|
+
copy: "M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Zm5-5C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z",
|
|
41
|
+
"kebab-horizontal": "M8 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM1.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm13 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z",
|
|
42
|
+
"skip": "M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Z",
|
|
43
|
+
};
|
|
44
|
+
export function Octicon({ name, size = 16, className }) {
|
|
45
|
+
return (_jsx("svg", { className: `octicon octicon-${name}${className ? ` ${className}` : ""}`, viewBox: "0 0 16 16", width: size, height: size, "aria-hidden": "true", fill: "currentColor", children: _jsx("path", { d: ICONS[name] ?? "" }) }));
|
|
46
|
+
}
|
|
47
|
+
// ── Small shared pieces ─────────────────────────────────────────────────────────────────────────────────────────
|
|
48
|
+
function hashOf(text) {
|
|
49
|
+
let h = 2166136261;
|
|
50
|
+
for (let i = 0; i < text.length; i++)
|
|
51
|
+
h = Math.imul(h ^ text.charCodeAt(i), 16777619);
|
|
52
|
+
return h >>> 0;
|
|
53
|
+
}
|
|
54
|
+
/** A person's default picture, as GitHub draws one for an account without an upload: a 5x5 mirrored identicon. */
|
|
55
|
+
export function Avatar({ login, size = 20, src }) {
|
|
56
|
+
if (src)
|
|
57
|
+
return _jsx("img", { className: "gh-avatar", src: src, width: size, height: size, alt: `@${login ?? ""}` });
|
|
58
|
+
const h = hashOf(login || "ghost");
|
|
59
|
+
const hue = h % 360;
|
|
60
|
+
const cells = [];
|
|
61
|
+
for (let y = 0; y < 5; y++)
|
|
62
|
+
for (let x = 0; x < 3; x++) {
|
|
63
|
+
if ((h >> (y * 3 + x)) & 1) {
|
|
64
|
+
cells.push(_jsx("rect", { x: 1 + x, y: 1 + y, width: 1, height: 1 }, `${x}-${y}`));
|
|
65
|
+
if (x < 2)
|
|
66
|
+
cells.push(_jsx("rect", { x: 5 - x, y: 1 + y, width: 1, height: 1 }, `${4 - x}-${y}m`));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return (_jsxs("svg", { className: "gh-avatar", width: size, height: size, viewBox: "0 0 7 7", role: "img", "aria-label": `@${login ?? "ghost"}`, shapeRendering: "crispEdges", children: [_jsx("rect", { width: "7", height: "7", fill: "#f0f0f0" }), _jsx("g", { fill: `hsl(${hue} 55% 58%)`, children: cells })] }));
|
|
70
|
+
}
|
|
71
|
+
/** GitHub's relative time: "now", "3 minutes ago", "yesterday", then "on Sep 24". */
|
|
72
|
+
export function timeAgo(iso, now = Date.now()) {
|
|
73
|
+
if (!iso)
|
|
74
|
+
return "";
|
|
75
|
+
const t = Date.parse(iso);
|
|
76
|
+
if (!Number.isFinite(t))
|
|
77
|
+
return "";
|
|
78
|
+
const s = Math.max(0, Math.round((now - t) / 1000));
|
|
79
|
+
if (s < 45)
|
|
80
|
+
return "now";
|
|
81
|
+
const m = Math.round(s / 60);
|
|
82
|
+
if (m < 60)
|
|
83
|
+
return m === 1 ? "1 minute ago" : `${m} minutes ago`;
|
|
84
|
+
const h = Math.round(m / 60);
|
|
85
|
+
if (h < 24)
|
|
86
|
+
return h === 1 ? "1 hour ago" : `${h} hours ago`;
|
|
87
|
+
const d = Math.round(h / 24);
|
|
88
|
+
if (d === 1)
|
|
89
|
+
return "yesterday";
|
|
90
|
+
if (d < 30)
|
|
91
|
+
return `${d} days ago`;
|
|
92
|
+
return `on ${new Date(t).toLocaleDateString("en-US", { month: "short", day: "numeric", ...(new Date(t).getFullYear() !== new Date(now).getFullYear() ? { year: "numeric" } : {}) })}`;
|
|
93
|
+
}
|
|
94
|
+
function RelativeTime({ iso }) {
|
|
95
|
+
return _jsx("time", { dateTime: iso, title: iso ? new Date(iso).toLocaleString("en-US", { dateStyle: "medium", timeStyle: "short" }) : undefined, children: timeAgo(iso) });
|
|
96
|
+
}
|
|
97
|
+
/** A label pill in the label's own colour, its text dark or light by the colour's lightness. */
|
|
98
|
+
export function Label({ name, color }) {
|
|
99
|
+
const hex = (color || "ededed").replace(/^#/, "");
|
|
100
|
+
const [r, g, b] = [0, 2, 4].map((i) => parseInt(hex.slice(i, i + 2), 16) || 0);
|
|
101
|
+
const light = (r * 299 + g * 587 + b * 114) / 1000 > 150;
|
|
102
|
+
return _jsx("span", { className: "gh-label", style: { background: `#${hex}`, color: light ? "#1f2328" : "#ffffff" }, children: name });
|
|
103
|
+
}
|
|
104
|
+
/** A body as GitHub renders its Markdown, for what a comment ordinarily holds: paragraphs, inline code, bold, links. */
|
|
105
|
+
function Markdown({ text }) {
|
|
106
|
+
const inline = (line, key) => line.split(/(`[^`]+`|\*\*[^*]+\*\*|https?:\/\/\S+)/g).map((part, i) => {
|
|
107
|
+
if (/^`[^`]+`$/.test(part))
|
|
108
|
+
return _jsx("code", { children: part.slice(1, -1) }, `${key}-${i}`);
|
|
109
|
+
if (/^\*\*[^*]+\*\*$/.test(part))
|
|
110
|
+
return _jsx("strong", { children: part.slice(2, -2) }, `${key}-${i}`);
|
|
111
|
+
if (/^https?:\/\//.test(part))
|
|
112
|
+
return _jsx("a", { href: part, target: "_blank", rel: "noreferrer", children: part }, `${key}-${i}`);
|
|
113
|
+
return part;
|
|
114
|
+
});
|
|
115
|
+
const paragraphs = (text ?? "").trim().split(/\n{2,}/).filter(Boolean);
|
|
116
|
+
if (!paragraphs.length)
|
|
117
|
+
return _jsx("p", { className: "gh-muted", children: _jsx("em", { children: "No description provided." }) });
|
|
118
|
+
return _jsx(_Fragment, { children: paragraphs.map((p, i) => _jsx("p", { children: p.split("\n").flatMap((line, j) => (j ? [_jsx("br", {}, `br-${i}-${j}`)] : []).concat(inline(line, `${i}-${j}`))) }, i)) });
|
|
119
|
+
}
|
|
120
|
+
export function pullState(pr) {
|
|
121
|
+
if (pr.merged)
|
|
122
|
+
return "merged";
|
|
123
|
+
if (pr.state === "closed")
|
|
124
|
+
return "closed";
|
|
125
|
+
return pr.draft ? "draft" : "open";
|
|
126
|
+
}
|
|
127
|
+
const STATE_ICON = { open: "git-pull-request", merged: "git-merge", closed: "git-pull-request-closed", draft: "git-pull-request-draft" };
|
|
128
|
+
const STATE_WORD = { open: "Open", merged: "Merged", closed: "Closed", draft: "Draft" };
|
|
129
|
+
export function StateBadge({ pr }) {
|
|
130
|
+
const s = pullState(pr);
|
|
131
|
+
return _jsxs("span", { className: `state-badge gh-state gh-state-${s}`, children: [_jsx(Octicon, { name: STATE_ICON[s] }), STATE_WORD[s]] });
|
|
132
|
+
}
|
|
133
|
+
const CI_ICON = { success: "check", failure: "x", error: "x", pending: "dot-fill" };
|
|
134
|
+
/** The CI rollup beside a title: a green check, a red cross or an amber dot. */
|
|
135
|
+
export function CiStatus({ status }) {
|
|
136
|
+
if (!status)
|
|
137
|
+
return null;
|
|
138
|
+
const word = status === "success" ? "All checks have passed" : status === "pending" ? "Some checks haven't completed yet" : "Some checks were not successful";
|
|
139
|
+
return _jsx("span", { className: `ci-status ${status}`, title: `checks: ${status}`, "aria-label": word, children: _jsx(Octicon, { name: CI_ICON[status] ?? "dot-fill" }) });
|
|
140
|
+
}
|
|
141
|
+
/** What the latest review by each reviewer says, as GitHub decides a pull request's review state. */
|
|
142
|
+
export function reviewDecision(pr, reviews) {
|
|
143
|
+
const latest = new Map();
|
|
144
|
+
for (const r of reviews) {
|
|
145
|
+
if (r.repo !== pr.repo || Number(r.number) !== Number(pr.number) || !r.reviewer)
|
|
146
|
+
continue;
|
|
147
|
+
const st = String(r.state ?? "").toLowerCase();
|
|
148
|
+
if (st === "approved" || st === "changes_requested" || st === "dismissed")
|
|
149
|
+
latest.set(r.reviewer, st);
|
|
150
|
+
}
|
|
151
|
+
const states = [...latest.values()];
|
|
152
|
+
if (states.includes("changes_requested"))
|
|
153
|
+
return "changes_requested";
|
|
154
|
+
if (states.includes("approved"))
|
|
155
|
+
return "approved";
|
|
156
|
+
return (pr.requestedReviewers ?? []).length ? "review_required" : undefined;
|
|
157
|
+
}
|
|
158
|
+
export function RepoHeader({ repo, viewer, active, facts, openPulls, projects, onTab, onHome, onInbox, onSearch }) {
|
|
159
|
+
const all = repo === "all" || !repo;
|
|
160
|
+
const [owner, name] = all ? ["", ""] : repo.split("/");
|
|
161
|
+
const tabs = [
|
|
162
|
+
{ id: "code", label: "Code", icon: "code" },
|
|
163
|
+
{ id: "issues", label: "Issues", icon: "issue-opened", count: facts?.openIssues ?? 0 },
|
|
164
|
+
{ id: "pulls", label: "Pull requests", icon: "git-pull-request", count: openPulls },
|
|
165
|
+
...(facts?.hasDiscussions ? [{ id: "discussions", label: "Discussions", icon: "comment-discussion" }] : []),
|
|
166
|
+
{ id: "actions", label: "Actions", icon: "play" },
|
|
167
|
+
{ id: "projects", label: "Projects", icon: "table", ...(projects ? { count: projects } : {}) },
|
|
168
|
+
{ id: "security", label: "Security", icon: "shield" },
|
|
169
|
+
{ id: "insights", label: "Insights", icon: "graph" },
|
|
170
|
+
{ id: "settings", label: "Settings", icon: "gear" },
|
|
171
|
+
];
|
|
172
|
+
return (_jsxs("header", { className: "gh-header", children: [_jsxs("div", { className: "gh-globalbar", children: [_jsx("button", { className: "gh-iconbtn", "aria-label": "Open global navigation menu", children: _jsx(Octicon, { name: "three-bars" }) }), _jsx("a", { className: "gh-logo", href: "/", "aria-label": "Homepage", onClick: (e) => { e.preventDefault(); onHome(); }, children: _jsx(Octicon, { name: "mark-github", size: 32 }) }), _jsx("nav", { className: "gh-context", "aria-label": "Page context", children: all ? _jsx("strong", { children: "Pull requests" }) : (_jsxs(_Fragment, { children: [_jsx("a", { href: `/${owner}`, onClick: (e) => e.preventDefault(), children: owner }), _jsx("span", { className: "gh-context-sep", children: "/" }), _jsx("a", { href: `/${repo}`, onClick: (e) => { e.preventDefault(); onTab("code"); }, children: _jsx("strong", { children: name }) })] })) }), _jsx("div", { className: "gh-spacer" }), _jsxs("button", { className: "gh-search", onClick: onSearch, children: [_jsx(Octicon, { name: "search" }), _jsxs("span", { children: ["Type ", _jsx("kbd", { children: "/" }), " to search"] })] }), _jsx("span", { className: "gh-divider" }), _jsxs("button", { className: "gh-iconbtn gh-iconbtn-wide", "aria-label": "Create something new", children: [_jsx(Octicon, { name: "plus" }), _jsx(Octicon, { name: "triangle-down" })] }), _jsx("button", { className: "gh-iconbtn", "aria-label": "Your issues", onClick: () => onTab("issues"), children: _jsx(Octicon, { name: "issue-opened" }) }), _jsx("button", { className: "gh-iconbtn", "aria-label": "Your pull requests", onClick: onHome, children: _jsx(Octicon, { name: "git-pull-request" }) }), _jsx("button", { className: "gh-iconbtn", "aria-label": "You have no unread notifications", onClick: onInbox, children: _jsx(Octicon, { name: "inbox" }) }), _jsx("span", { className: "gh-viewer", children: _jsx(Avatar, { login: viewer, size: 32 }) })] }), all ? null : (_jsx("nav", { className: "gh-localbar", "aria-label": "Repository", children: tabs.map((t) => (_jsxs("a", { href: `/${repo}${t.id === "code" ? "" : `/${t.id}`}`, className: `gh-tab${active === t.id ? " selected" : ""}`, "aria-current": active === t.id ? "page" : undefined, onClick: (e) => { e.preventDefault(); onTab(t.id); }, children: [_jsx(Octicon, { name: t.icon }), _jsx("span", { children: t.label }), t.count ? _jsx("span", { className: "Counter", children: t.count }) : null] }, t.id))) }))] }));
|
|
173
|
+
}
|
|
174
|
+
/** The page footer every GitHub page ends with. */
|
|
175
|
+
export function SiteFooter() {
|
|
176
|
+
return (_jsxs("footer", { className: "gh-footer", children: [_jsxs("span", { className: "gh-footer-brand", children: [_jsx(Octicon, { name: "mark-github", size: 24 }), " \u00A9 ", new Date().getFullYear(), " GitHub, Inc."] }), _jsx("ul", { children: ["Terms", "Privacy", "Security", "Status", "Community", "Docs", "Contact", "Manage cookies", "Do not share my personal information"].map((t) => _jsx("li", { children: t }, t)) })] }));
|
|
177
|
+
}
|
|
178
|
+
// ── The list page ──────────────────────────────────────────────────────────────────────────────────────────────
|
|
179
|
+
function rowMeta(pr) {
|
|
180
|
+
const s = pullState(pr);
|
|
181
|
+
if (s === "merged")
|
|
182
|
+
return _jsxs(_Fragment, { children: ["#", pr.number, " by ", _jsx("a", { className: "gh-muted-link", children: pr.author }), " was merged ", _jsx(RelativeTime, { iso: pr.mergedAt })] });
|
|
183
|
+
if (s === "closed")
|
|
184
|
+
return _jsxs(_Fragment, { children: ["#", pr.number, " by ", _jsx("a", { className: "gh-muted-link", children: pr.author }), " was closed ", _jsx(RelativeTime, { iso: pr.closedAt ?? pr.updatedAt })] });
|
|
185
|
+
return _jsxs(_Fragment, { children: ["#", pr.number, " opened ", _jsx(RelativeTime, { iso: pr.createdAt }), " by ", _jsx("a", { className: "gh-muted-link", children: pr.author }), s === "draft" ? " • Draft" : ""] });
|
|
186
|
+
}
|
|
187
|
+
const DECISION_WORD = { approved: "Approved", changes_requested: "Changes requested", review_required: "Review required" };
|
|
188
|
+
/** The list's rows: each pull request's state icon, title, CI, labels, meta line, assignees and comment count. */
|
|
189
|
+
export function PrList({ prs, reviews, labelColors = {}, showRepo, onSelect }) {
|
|
190
|
+
return (_jsx("div", { className: "gh-rows", children: prs.map((pr) => {
|
|
191
|
+
const s = pullState(pr);
|
|
192
|
+
const decision = s === "open" || s === "draft" ? reviewDecision(pr, reviews) : undefined;
|
|
193
|
+
const comments = pr.commentsCount ?? 0;
|
|
194
|
+
return (_jsxs("div", { className: "pr-row", "data-number": pr.number, children: [_jsx("span", { className: `pr-state-icon ${s}`, "aria-label": `${STATE_WORD[s]} pull request`, children: _jsx(Octicon, { name: STATE_ICON[s] }) }), _jsxs("div", { className: "pr-row-main", children: [_jsxs("div", { className: "pr-row-title-line", children: [showRepo ? _jsx("span", { className: "pr-row-repo", children: pr.repo }) : null, _jsx("a", { className: "pr-row-title", href: `/${pr.repo}/pull/${pr.number}`, onClick: (e) => { e.preventDefault(); onSelect?.(`${pr.repo}#${pr.number}`); }, children: pr.title }), _jsx(CiStatus, { status: pr.ciStatus }), (pr.labels ?? []).map((l) => _jsx(Label, { name: l.name, color: labelColors[`${pr.repo}:${l.name}`] ?? l.color }, l.name))] }), _jsxs("div", { className: "pr-row-meta", children: [rowMeta(pr), decision ? _jsxs(_Fragment, { children: [" \u2022 ", _jsx("a", { className: `gh-muted-link decision-${decision}`, children: DECISION_WORD[decision] })] }) : null] })] }), _jsxs("div", { className: "pr-row-side", children: [_jsx("span", { className: "pr-row-assignees", children: (pr.assignees ?? []).map((a) => _jsx(Avatar, { login: a, size: 20 }, a)) }), _jsx("span", { className: "pr-row-comments", children: comments ? _jsxs("a", { className: "gh-muted-link", "aria-label": `${comments} comments`, children: [_jsx(Octicon, { name: "comment" }), " ", comments] }) : null })] })] }, `${pr.repo}#${pr.number}`));
|
|
195
|
+
}) }));
|
|
196
|
+
}
|
|
197
|
+
export function PullsListPage({ repo, prs, reviews, facts, listState, onListState, onSelect }) {
|
|
198
|
+
const open = prs.filter((p) => pullState(p) === "open" || pullState(p) === "draft");
|
|
199
|
+
const closed = prs.filter((p) => pullState(p) === "merged" || pullState(p) === "closed");
|
|
200
|
+
const shown = listState === "open" ? open : closed;
|
|
201
|
+
const labelColors = Object.fromEntries((facts?.labels ?? []).map((l) => [`${repo}:${l.name}`, l.color ?? "ededed"]));
|
|
202
|
+
for (const p of prs)
|
|
203
|
+
for (const l of p.labels ?? [])
|
|
204
|
+
if (l.color && !labelColors[`${p.repo}:${l.name}`])
|
|
205
|
+
labelColors[`${p.repo}:${l.name}`] = l.color;
|
|
206
|
+
const all = repo === "all" || !repo;
|
|
207
|
+
return (_jsxs("div", { className: "gh-container gh-list-page", children: [_jsxs("div", { className: "gh-subnav", children: [_jsxs("div", { className: "gh-filter", children: [_jsxs("button", { className: "gh-btn gh-filter-btn", children: ["Filters ", _jsx(Octicon, { name: "triangle-down" })] }), _jsxs("label", { className: "gh-filter-input", children: [_jsx(Octicon, { name: "search" }), _jsx("input", { "aria-label": "Search all pull requests", value: `is:pr is:${listState} `, readOnly: true })] })] }), all ? null : (_jsxs("div", { className: "gh-subnav-links", children: [_jsxs("a", { className: "gh-subnav-item", children: [_jsx(Octicon, { name: "tag" }), " Labels ", _jsx("span", { className: "Counter", children: facts?.labels.length ?? 0 })] }), _jsxs("a", { className: "gh-subnav-item", children: [_jsx(Octicon, { name: "milestone" }), " Milestones ", _jsx("span", { className: "Counter", children: facts?.milestones ?? 0 })] })] })), all ? null : _jsx("button", { className: "gh-btn gh-btn-primary", children: "New pull request" })] }), _jsxs("div", { className: "gh-box", children: [_jsxs("div", { className: "gh-box-header", children: [_jsxs("div", { className: "gh-states", role: "group", "aria-label": "Pull request state", children: [_jsxs("button", { className: `gh-states-link${listState === "open" ? " selected" : ""}`, "aria-pressed": listState === "open", onClick: () => onListState("open"), children: [_jsx(Octicon, { name: "git-pull-request" }), " ", open.length, " Open"] }), _jsxs("button", { className: `gh-states-link${listState === "closed" ? " selected" : ""}`, "aria-pressed": listState === "closed", onClick: () => onListState("closed"), children: [_jsx(Octicon, { name: "check" }), " ", closed.length, " Closed"] })] }), _jsx("div", { className: "gh-box-filters", children: ["Author", "Label", "Projects", "Milestones", "Reviews", "Assignee", "Sort"].map((f) => _jsxs("span", { className: "gh-select-menu", children: [f, " ", _jsx(Octicon, { name: "triangle-down" })] }, f)) })] }), shown.length ? _jsx(PrList, { prs: shown, reviews: reviews, labelColors: labelColors, showRepo: all, onSelect: onSelect }) : (_jsxs("div", { className: "gh-blankslate", children: [_jsx(Octicon, { name: "git-pull-request", size: 24 }), _jsx("h3", { children: listState === "open" ? "There aren’t any open pull requests." : "There aren’t any closed pull requests." }), _jsxs("p", { children: ["You could search ", _jsx("a", { children: "all of GitHub" }), " or try an ", _jsx("a", { children: "advanced search" }), "."] })] }))] }), _jsxs("p", { className: "gh-protip", children: [_jsx("strong", { children: "ProTip!" }), " Filter pull requests by the default branch with ", _jsx("a", { children: "base:main" }), "."] })] }));
|
|
208
|
+
}
|
|
209
|
+
function shortSha(sha) { return sha ? sha.slice(0, 7) : ""; }
|
|
210
|
+
function DiffStat({ additions = 0, deletions = 0 }) {
|
|
211
|
+
const total = additions + deletions;
|
|
212
|
+
const greens = total ? Math.round((additions / total) * 5) : 0;
|
|
213
|
+
const reds = total ? Math.min(5 - greens, Math.round((deletions / total) * 5) || (deletions ? 1 : 0)) : 0;
|
|
214
|
+
return (_jsxs("span", { className: "gh-diffstat", children: [_jsxs("span", { className: "gh-add", children: ["+", additions] }), " ", _jsxs("span", { className: "gh-del", children: ["\u2212", deletions] }), _jsx("span", { className: "gh-diffstat-blocks", children: Array.from({ length: 5 }, (_, i) => _jsx("span", { className: `gh-block ${i < greens ? "add" : i < greens + reds ? "del" : "neutral"}` }, i)) })] }));
|
|
215
|
+
}
|
|
216
|
+
function PullHeader({ pr, commits, checks, files, tab, onTab }) {
|
|
217
|
+
const s = pullState(pr);
|
|
218
|
+
const n = pr.commitsCount ?? commits;
|
|
219
|
+
const commitWord = `${n} commit${n === 1 ? "" : "s"}`;
|
|
220
|
+
const [owner] = pr.repo.split("/");
|
|
221
|
+
const ref = (branch) => _jsx("span", { className: "gh-commit-ref", title: `${owner}:${branch}`, children: branch });
|
|
222
|
+
const tabs = [
|
|
223
|
+
{ id: "conversation", label: "Conversation", icon: "comment-discussion", count: (pr.commentsCount ?? 0) + (pr.reviewCount ?? 0) + (pr.reviewCommentsCount ?? 0) },
|
|
224
|
+
{ id: "commits", label: "Commits", icon: "git-commit", count: n },
|
|
225
|
+
{ id: "checks", label: "Checks", icon: "checklist", count: checks },
|
|
226
|
+
{ id: "files", label: "Files changed", icon: "file-diff", count: pr.changedFiles ?? files },
|
|
227
|
+
];
|
|
228
|
+
return (_jsxs("div", { className: "gh-pull-header", children: [_jsxs("div", { className: "gh-pull-title-row", children: [_jsxs("h1", { className: "gh-pull-title", children: [_jsx("bdi", { children: pr.title }), " ", _jsxs("span", { className: "gh-pull-number", children: ["#", pr.number] })] }), _jsxs("div", { className: "gh-pull-title-actions", children: [_jsx("button", { className: "gh-btn gh-btn-sm", children: "Edit" }), _jsxs("button", { className: "gh-btn gh-btn-sm gh-btn-primary", children: [_jsx(Octicon, { name: "code" }), " Code ", _jsx(Octicon, { name: "triangle-down" })] })] })] }), _jsxs("div", { className: "gh-pull-meta state-line", children: [_jsx(StateBadge, { pr: pr }), _jsxs("span", { className: "gh-pull-meta-text", children: [s === "merged" ? (_jsxs(_Fragment, { children: [_jsx("a", { className: "gh-author", children: pr.mergedBy ?? pr.author }), " merged ", commitWord, " into ", ref(pr.baseBranch), " from ", ref(pr.headBranch), " ", _jsx(RelativeTime, { iso: pr.mergedAt })] })) : (_jsxs(_Fragment, { children: [_jsx("a", { className: "gh-author", children: pr.author }), " wants to merge ", commitWord, " into ", ref(pr.baseBranch), " from ", ref(pr.headBranch)] })), _jsx("button", { className: "gh-copy", "aria-label": "Copy head branch name to clipboard", onClick: () => void navigator.clipboard?.writeText(pr.headBranch ?? ""), children: _jsx(Octicon, { name: "copy" }) })] })] }), _jsxs("nav", { className: "gh-pull-tabs", "aria-label": "Pull request tabs", children: [tabs.map((t) => (_jsxs("a", { href: "#", className: `gh-pull-tab${tab === t.id ? " selected" : ""}`, "aria-current": tab === t.id ? "page" : undefined, onClick: (e) => { e.preventDefault(); onTab(t.id); }, children: [_jsx(Octicon, { name: t.icon }), " ", t.label, " ", _jsx("span", { className: "Counter", children: t.count })] }, t.id))), _jsx("span", { className: "gh-pull-tabs-fill" }), _jsx(DiffStat, { additions: pr.additions, deletions: pr.deletions })] })] }));
|
|
229
|
+
}
|
|
230
|
+
function association(login, pr, given) {
|
|
231
|
+
const out = [];
|
|
232
|
+
if (given && given !== "NONE")
|
|
233
|
+
out.push(given === "OWNER" ? "Owner" : given === "MEMBER" ? "Member" : given === "COLLABORATOR" ? "Collaborator" : given === "CONTRIBUTOR" ? "Contributor" : "");
|
|
234
|
+
else if (login && login === pr.repo.split("/")[0])
|
|
235
|
+
out.push("Owner");
|
|
236
|
+
if (login && login === pr.author)
|
|
237
|
+
out.push("Author");
|
|
238
|
+
return out.filter(Boolean);
|
|
239
|
+
}
|
|
240
|
+
function CommentBox({ login, when, body, pr, viewer, verb = "commented", given }) {
|
|
241
|
+
return (_jsxs("div", { className: "timeline-item gh-comment-item", children: [_jsx("span", { className: "gh-timeline-avatar", children: _jsx(Avatar, { login: login, size: 40 }) }), _jsxs("article", { className: `comment gh-comment${login && login === viewer ? " gh-comment-self" : ""}`, children: [_jsxs("header", { className: "gh-comment-header", children: [_jsxs("span", { children: [_jsx("a", { className: "gh-author", children: login }), " ", _jsxs("span", { className: "gh-muted", children: [verb, " ", _jsx(RelativeTime, { iso: when })] })] }), _jsxs("span", { className: "gh-comment-badges", children: [association(login, pr, given).map((b) => _jsx("span", { className: "gh-role", children: b }, b)), _jsx("button", { className: "gh-iconbtn gh-iconbtn-sm", "aria-label": "Show options", children: _jsx(Octicon, { name: "kebab-horizontal" }) })] })] }), _jsx("div", { className: "gh-comment-body gh-markdown", children: _jsx(Markdown, { text: body }) })] })] }));
|
|
242
|
+
}
|
|
243
|
+
function TimelineEvent({ icon, tone, children }) {
|
|
244
|
+
return (_jsxs("div", { className: "gh-event", children: [_jsx("span", { className: `gh-event-badge${tone ? ` ${tone}` : ""}`, children: _jsx(Octicon, { name: icon }) }), _jsx("div", { className: "gh-event-body", children: children })] }));
|
|
245
|
+
}
|
|
246
|
+
/** The Conversation tab's timeline, the merge box and the comment form. */
|
|
247
|
+
export function Conversation({ pr, reviews, commits, comments, reviewComments, checks = [], stateEvents, viewer, actions }) {
|
|
248
|
+
if (!pr)
|
|
249
|
+
return null;
|
|
250
|
+
const s = pullState(pr);
|
|
251
|
+
const entries = [];
|
|
252
|
+
const at = (iso) => { const t = Date.parse(iso ?? ""); return Number.isFinite(t) ? t : 0; };
|
|
253
|
+
if (commits.length) {
|
|
254
|
+
// commits pushed together read as one event: "<login> added N commits"
|
|
255
|
+
const by = commits[0].authorLogin ?? commits[0].author;
|
|
256
|
+
entries.push({
|
|
257
|
+
at: at(commits[0].date), key: "commits", node: (_jsxs("div", { className: "gh-commits-event", children: [_jsxs(TimelineEvent, { icon: "git-commit", children: [_jsx(Avatar, { login: by, size: 20 }), " ", _jsx("a", { className: "gh-author", children: by }), " ", _jsxs("span", { className: "gh-muted", children: ["added ", commits.length, " commit", commits.length === 1 ? "" : "s", " ", _jsx(RelativeTime, { iso: commits[0].date })] })] }), commits.map((c) => (_jsxs("div", { className: "gh-commit-line", children: [_jsx(Avatar, { login: c.authorLogin ?? c.author, size: 16 }), _jsx("span", { className: "gh-commit-message", children: (c.message ?? "").split("\n")[0] }), _jsx("span", { className: "gh-commit-status" }), _jsx("code", { className: "gh-sha", children: shortSha(c.sha) })] }, c.sha)))] })),
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
comments.forEach((c, i) => entries.push({ at: at(c.createdAt), key: `c-${c.id ?? i}`, node: _jsx(CommentBox, { login: c.author, when: c.createdAt, body: c.body, pr: pr, viewer: viewer, given: c.authorAssociation }) }));
|
|
261
|
+
reviews.forEach((r, i) => {
|
|
262
|
+
const st = String(r.state ?? "").toLowerCase();
|
|
263
|
+
const word = st === "approved" ? "approved these changes" : st === "changes_requested" ? "requested changes" : st === "dismissed" ? "reviewed (dismissed)" : "reviewed";
|
|
264
|
+
const tone = st === "approved" ? "approved" : st === "changes_requested" ? "changes" : "";
|
|
265
|
+
entries.push({
|
|
266
|
+
at: at(r.submittedAt), key: `r-${r.id ?? i}`, node: (_jsxs("div", { className: "review-card gh-review", children: [_jsxs(TimelineEvent, { icon: st === "approved" ? "check" : st === "changes_requested" ? "file-diff" : "eye", tone: tone, children: [_jsx(Avatar, { login: r.reviewer, size: 20 }), " ", _jsx("a", { className: "gh-author", children: r.reviewer }), " ", _jsxs("span", { className: "gh-muted", children: [word, " ", _jsx(RelativeTime, { iso: r.submittedAt })] })] }), r.body ? _jsx("div", { className: "gh-review-body", children: _jsx(CommentBox, { login: r.reviewer, when: r.submittedAt, body: r.body, pr: pr, viewer: viewer, verb: "left a comment" }) }) : null] })),
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
reviewComments.forEach((c, i) => entries.push({ at: at(c.createdAt), key: `rc-${c.id ?? i}`, node: _jsx(CommentBox, { login: c.author, when: c.createdAt, body: c.body, pr: pr, viewer: viewer, verb: `commented on ${c.path ?? "a file"}` }) }));
|
|
270
|
+
if (s === "merged")
|
|
271
|
+
entries.push({
|
|
272
|
+
at: at(pr.mergedAt), key: "merged", node: (_jsxs(TimelineEvent, { icon: "git-merge", tone: "merged", children: [_jsx(Avatar, { login: pr.mergedBy ?? pr.author, size: 20 }), " ", _jsx("a", { className: "gh-author", children: pr.mergedBy ?? pr.author }), " ", _jsx("span", { className: "gh-muted", children: "merged commit" }), " ", _jsx("code", { className: "gh-sha", children: shortSha(pr.mergeCommit) }), " ", _jsx("span", { className: "gh-muted", children: "into" }), " ", _jsx("span", { className: "gh-commit-ref", children: pr.baseBranch }), " ", _jsx("span", { className: "gh-muted", children: _jsx(RelativeTime, { iso: pr.mergedAt }) })] })),
|
|
273
|
+
});
|
|
274
|
+
// closed and reopened, from the timeline GitHub keeps (a merge closes too; the merged event says it)
|
|
275
|
+
const changes = stateEvents ?? (s === "closed" ? [{ event: "closed", at: pr.closedAt ?? pr.updatedAt }] : []);
|
|
276
|
+
changes.filter((e) => !(s === "merged" && e.event === "closed")).forEach((e, i) => {
|
|
277
|
+
const who = e.actor ?? pr.author;
|
|
278
|
+
entries.push({
|
|
279
|
+
at: at(e.at), key: `state-${i}`, node: (_jsxs(TimelineEvent, { icon: e.event === "closed" ? "git-pull-request-closed" : "git-pull-request", tone: e.event === "closed" ? "closed" : "reopened", children: [_jsx(Avatar, { login: who, size: 20 }), " ", _jsx("a", { className: "gh-author", children: who }), " ", _jsxs("span", { className: "gh-muted", children: [e.event === "closed" ? "closed" : "reopened", " this ", _jsx(RelativeTime, { iso: e.at })] })] })),
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
entries.sort((a, b) => a.at - b.at);
|
|
283
|
+
return (_jsxs("section", { className: "conversation gh-conversation", children: [_jsxs("div", { className: "gh-timeline", children: [_jsx(CommentBox, { login: pr.author, when: pr.createdAt, body: pr.body, pr: pr, viewer: viewer }), entries.map((e) => _jsx(React.Fragment, { children: e.node }, e.key))] }), _jsx(MergeBox, { pr: pr, reviews: reviews, checks: checks, actions: actions }), _jsx(CommentForm, { pr: pr, viewer: viewer, actions: actions })] }));
|
|
284
|
+
}
|
|
285
|
+
function MergeSection({ ok, tone, icon, title, detail, children }) {
|
|
286
|
+
return (_jsxs("div", { className: "gh-merge-section", children: [_jsx("span", { className: `gh-merge-section-icon ${tone ?? (ok ? "ok" : "warn")}`, children: _jsx(Octicon, { name: icon, size: 16 }) }), _jsxs("div", { className: "gh-merge-section-text", children: [_jsx("strong", { children: title }), detail ? _jsx("div", { className: "gh-muted", children: detail }) : null, children] })] }));
|
|
287
|
+
}
|
|
288
|
+
function MergeBox({ pr, reviews, checks, actions }) {
|
|
289
|
+
const [confirming, setConfirming] = useState(false);
|
|
290
|
+
const [title, setTitle] = useState("");
|
|
291
|
+
const [message, setMessage] = useState("");
|
|
292
|
+
const [busy, setBusy] = useState(false);
|
|
293
|
+
const [error, setError] = useState("");
|
|
294
|
+
const s = pullState(pr);
|
|
295
|
+
if (s === "merged") {
|
|
296
|
+
return (_jsxs("div", { className: "gh-mergebox", children: [_jsx("span", { className: "gh-mergebox-icon merged", children: _jsx(Octicon, { name: "git-merge", size: 24 }) }), _jsx("div", { className: "gh-mergebox-panel", children: _jsx(MergeSection, { tone: "merged", icon: "git-merge", title: "Pull request successfully merged and closed", detail: `You’re all set — the ${pr.headBranch} branch can be safely deleted.` }) })] }));
|
|
297
|
+
}
|
|
298
|
+
if (s === "closed") {
|
|
299
|
+
return (_jsxs("div", { className: "gh-mergebox", children: [_jsx("span", { className: "gh-mergebox-icon closed", children: _jsx(Octicon, { name: "git-pull-request-closed", size: 24 }) }), _jsx("div", { className: "gh-mergebox-panel", children: _jsx(MergeSection, { tone: "closed", icon: "git-pull-request-closed", title: "Closed with unmerged commits", detail: "This pull request is closed, but the branch has unmerged commits. You can reopen it to continue the review." }) })] }));
|
|
300
|
+
}
|
|
301
|
+
const decision = reviewDecision(pr, reviews);
|
|
302
|
+
const approvals = new Set(reviews.filter((r) => String(r.state).toLowerCase() === "approved").map((r) => r.reviewer)).size;
|
|
303
|
+
const failing = checks.filter((c) => ["failure", "error", "timed_out", "cancelled", "action_required"].includes(c.state));
|
|
304
|
+
const pending = checks.filter((c) => c.state === "pending" || c.state === "queued" || c.state === "in_progress");
|
|
305
|
+
const passed = checks.length - failing.length - pending.length;
|
|
306
|
+
const conflicts = pr.mergeable === false;
|
|
307
|
+
const plural = (n, w) => `${n} ${w}${n === 1 ? "" : "s"}`;
|
|
308
|
+
async function run(work) {
|
|
309
|
+
setBusy(true);
|
|
310
|
+
setError("");
|
|
311
|
+
try {
|
|
312
|
+
await work();
|
|
313
|
+
setConfirming(false);
|
|
314
|
+
}
|
|
315
|
+
catch (e) {
|
|
316
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
317
|
+
}
|
|
318
|
+
finally {
|
|
319
|
+
setBusy(false);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return (_jsxs("div", { className: "gh-mergebox", children: [_jsx("span", { className: `gh-mergebox-icon ${conflicts || failing.length ? "neutral" : "open"}`, children: _jsx(Octicon, { name: "git-merge", size: 24 }) }), _jsxs("div", { className: "gh-mergebox-panel", children: [decision === undefined ? null : decision === "approved" ? (_jsx(MergeSection, { ok: true, icon: "check", title: "Changes approved", detail: `${plural(approvals, "approving review")} by reviewers with write access.` })) : decision === "changes_requested" ? (_jsx(MergeSection, { tone: "bad", icon: "file-diff", title: "Changes requested", detail: "A reviewer has requested changes to this pull request." })) : (_jsx(MergeSection, { tone: "warn", icon: "dot-fill", title: "Review requested", detail: `Review has been requested on this pull request. ${(pr.requestedReviewers ?? []).join(", ")} ${(pr.requestedReviewers ?? []).length === 1 ? "is" : "are"} requested.` })), checks.length ? (_jsx(MergeSection, { ok: !failing.length && !pending.length, tone: failing.length ? "bad" : pending.length ? "warn" : "ok", icon: failing.length ? "x" : pending.length ? "dot-fill" : "check", title: failing.length ? "Some checks were not successful" : pending.length ? "Some checks haven’t completed yet" : "All checks have passed", detail: [failing.length ? `${failing.length} failing` : "", pending.length ? `${pending.length} pending` : "", passed ? plural(passed, "successful check") : ""].filter(Boolean).join(", "), children: _jsx("ul", { className: "gh-checks-list", children: checks.map((c) => (_jsxs("li", { children: [_jsx("span", { className: `ci-status ${c.state === "success" ? "success" : c.state === "pending" ? "pending" : "failure"}`, children: _jsx(Octicon, { name: CI_ICON[c.state] ?? (c.state === "success" ? "check" : "x") }) }), _jsx("strong", { children: c.name }), c.description ? _jsxs("span", { className: "gh-muted", children: [" \u2014 ", c.description] }) : null, c.targetUrl ? _jsx("a", { className: "gh-check-details", href: c.targetUrl, target: "_blank", rel: "noreferrer", children: "Details" }) : null] }, c.name))) }) })) : null, conflicts
|
|
323
|
+
? _jsx(MergeSection, { tone: "bad", icon: "x", title: "This branch has conflicts that must be resolved", detail: "Resolve conflicts then push again." })
|
|
324
|
+
: _jsx(MergeSection, { ok: true, icon: "check", title: "No conflicts with base branch", detail: "Merging can be performed automatically." }), _jsxs("div", { className: "gh-merge-actions", children: [confirming ? (_jsxs("div", { className: "gh-merge-confirm", children: [_jsx("input", { className: "gh-input", "aria-label": "Commit title", value: title, onChange: (e) => setTitle(e.target.value) }), _jsx("textarea", { className: "gh-input", "aria-label": "Commit message", value: message, onChange: (e) => setMessage(e.target.value), rows: 3 }), _jsxs("div", { className: "gh-merge-confirm-buttons", children: [_jsx("button", { className: "gh-btn gh-btn-primary", disabled: busy, onClick: () => void run(() => actions.merge(title, message)), children: busy ? "Merging…" : "Confirm merge" }), _jsx("button", { className: "gh-btn", disabled: busy, onClick: () => setConfirming(false), children: "Cancel" })] })] })) : (_jsxs("div", { className: "gh-merge-button-row", children: [_jsxs("span", { className: "gh-btn-group", children: [_jsx("button", { className: "gh-btn gh-btn-primary", disabled: conflicts || !actions, onClick: () => {
|
|
325
|
+
const [owner] = pr.repo.split("/");
|
|
326
|
+
setTitle(`Merge pull request #${pr.number} from ${owner}/${pr.headBranch}`);
|
|
327
|
+
setMessage(pr.title ?? "");
|
|
328
|
+
setConfirming(true);
|
|
329
|
+
}, children: "Merge pull request" }), _jsx("button", { className: "gh-btn gh-btn-primary gh-btn-caret", "aria-label": "Select merge method", disabled: conflicts || !actions, children: _jsx(Octicon, { name: "triangle-down" }) })] }), _jsx("span", { className: "gh-muted gh-merge-hint", children: "You can also merge this with the command line." })] })), error ? _jsx("div", { className: "gh-flash-error", role: "alert", children: error }) : null] })] })] }));
|
|
330
|
+
}
|
|
331
|
+
function CommentForm({ pr, viewer, actions }) {
|
|
332
|
+
const [body, setBody] = useState("");
|
|
333
|
+
const [busy, setBusy] = useState(false);
|
|
334
|
+
const [error, setError] = useState("");
|
|
335
|
+
const s = pullState(pr);
|
|
336
|
+
async function run(work, clear = true) {
|
|
337
|
+
setBusy(true);
|
|
338
|
+
setError("");
|
|
339
|
+
try {
|
|
340
|
+
await work();
|
|
341
|
+
if (clear)
|
|
342
|
+
setBody("");
|
|
343
|
+
}
|
|
344
|
+
catch (e) {
|
|
345
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
346
|
+
}
|
|
347
|
+
finally {
|
|
348
|
+
setBusy(false);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return (_jsxs("div", { className: "timeline-item gh-comment-form-item", children: [_jsx("span", { className: "gh-timeline-avatar", children: _jsx(Avatar, { login: viewer, size: 40 }) }), _jsxs("div", { className: "gh-comment-form", children: [_jsx("h2", { className: "gh-comment-form-title", children: "Add a comment" }), _jsxs("div", { className: "gh-comment-form-box", children: [_jsxs("div", { className: "gh-comment-form-tabs", children: [_jsx("span", { className: "selected", children: "Write" }), _jsx("span", { children: "Preview" })] }), _jsx("textarea", { className: "gh-comment-textarea", "aria-label": "Markdown value", placeholder: "Use Markdown to format your comment", value: body, onChange: (e) => setBody(e.target.value) }), _jsxs("div", { className: "gh-comment-form-hint", children: [_jsx(Octicon, { name: "file-diff" }), " Markdown is supported"] })] }), error ? _jsx("div", { className: "gh-flash-error", role: "alert", children: error }) : null, _jsxs("div", { className: "gh-comment-form-actions", children: [s === "open" || s === "draft" ? (_jsxs("button", { className: "gh-btn", disabled: busy || !actions, onClick: () => void run(() => actions.close(body.trim() || undefined)), children: [_jsx(Octicon, { name: "git-pull-request-closed", className: "gh-closed-fg" }), " ", body.trim() ? "Close with comment" : "Close pull request"] })) : s === "closed" ? (_jsx("button", { className: "gh-btn", disabled: busy || !actions, onClick: () => void run(() => actions.reopen(), false), children: "Reopen pull request" })) : null, _jsx("button", { className: "gh-btn gh-btn-primary", disabled: busy || !body.trim() || !actions, onClick: () => void run(() => actions.comment(body.trim())), children: "Comment" })] })] })] }));
|
|
352
|
+
}
|
|
353
|
+
/** The sidebar: Reviewers, Assignees, Labels, Projects, Milestone, Development, Participants. */
|
|
354
|
+
export function Aside({ pr, reviews, comments = [], labelColors = {} }) {
|
|
355
|
+
if (!pr)
|
|
356
|
+
return _jsx("aside", { className: "sidebar" });
|
|
357
|
+
const latest = new Map();
|
|
358
|
+
for (const r of reviews)
|
|
359
|
+
if (r.reviewer)
|
|
360
|
+
latest.set(r.reviewer, String(r.state ?? "").toLowerCase());
|
|
361
|
+
const requested = (pr.requestedReviewers ?? []).filter((l) => !latest.has(l));
|
|
362
|
+
const participants = [...new Set([pr.author, ...reviews.map((r) => r.reviewer), ...comments.map((c) => c.author)].filter(Boolean))];
|
|
363
|
+
return (_jsxs("aside", { className: "sidebar gh-sidebar", children: [_jsxs("section", { className: "gh-sidebar-item", children: [_jsxs("h3", { children: ["Reviewers", _jsx(Octicon, { name: "gear" })] }), latest.size || requested.length ? (_jsxs("ul", { className: "gh-sidebar-list", children: [[...latest].map(([login, st]) => (_jsxs("li", { children: [_jsx(Avatar, { login: login, size: 20 }), " ", _jsx("a", { className: "gh-author", children: login }), _jsx("span", { className: `gh-review-state ${st}`, title: st, children: _jsx(Octicon, { name: st === "approved" ? "check" : st === "changes_requested" ? "file-diff" : "comment" }) })] }, login))), requested.map((login) => (_jsxs("li", { children: [_jsx(Avatar, { login: login, size: 20 }), " ", _jsx("a", { className: "gh-author", children: login }), _jsx("span", { className: "gh-review-state pending", title: "Awaiting requested review", children: _jsx(Octicon, { name: "dot-fill" }) })] }, login)))] })) : _jsx("p", { className: "gh-muted", children: "No reviews" })] }), _jsxs("section", { className: "gh-sidebar-item", children: [_jsxs("h3", { children: ["Assignees", _jsx(Octicon, { name: "gear" })] }), (pr.assignees ?? []).length ? _jsx("ul", { className: "gh-sidebar-list", children: (pr.assignees ?? []).map((a) => _jsxs("li", { children: [_jsx(Avatar, { login: a, size: 20 }), " ", _jsx("a", { className: "gh-author", children: a })] }, a)) })
|
|
364
|
+
: _jsxs("p", { className: "gh-muted", children: ["No one\u2014", _jsx("a", { children: "assign yourself" })] })] }), _jsxs("section", { className: "gh-sidebar-item", children: [_jsxs("h3", { children: ["Labels", _jsx(Octicon, { name: "gear" })] }), (pr.labels ?? []).length ? _jsx("div", { className: "gh-sidebar-labels", children: (pr.labels ?? []).map((l) => _jsx(Label, { name: l.name, color: labelColors[`${pr.repo}:${l.name}`] ?? l.color }, l.name)) }) : _jsx("p", { className: "gh-muted", children: "None yet" })] }), _jsxs("section", { className: "gh-sidebar-item", children: [_jsxs("h3", { children: ["Projects", _jsx(Octicon, { name: "gear" })] }), _jsx("p", { className: "gh-muted", children: "None yet" })] }), _jsxs("section", { className: "gh-sidebar-item", children: [_jsxs("h3", { children: ["Milestone", _jsx(Octicon, { name: "gear" })] }), _jsx("p", { className: "gh-muted", children: pr.milestone ?? "No milestone" })] }), _jsxs("section", { className: "gh-sidebar-item", children: [_jsxs("h3", { children: ["Development", _jsx(Octicon, { name: "gear" })] }), _jsx("p", { className: "gh-muted", children: "Successfully merging this pull request may close these issues." }), _jsx("p", { className: "gh-muted", children: "None yet" })] }), _jsxs("section", { className: "gh-sidebar-item", children: [_jsxs("h3", { className: "gh-sidebar-plain", children: [participants.length, " participant", participants.length === 1 ? "" : "s"] }), _jsx("div", { className: "gh-participants", children: participants.map((p) => _jsx(Avatar, { login: p, size: 26 }, p)) })] })] }));
|
|
365
|
+
}
|
|
366
|
+
// ── Commits, Checks and Files changed tabs ─────────────────────────────────────────────────────────────────────
|
|
367
|
+
function CommitsTab({ commits }) {
|
|
368
|
+
const days = new Map();
|
|
369
|
+
for (const c of commits) {
|
|
370
|
+
const day = c.date ? new Date(c.date).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" }) : "";
|
|
371
|
+
days.set(day, [...(days.get(day) ?? []), c]);
|
|
372
|
+
}
|
|
373
|
+
if (!commits.length)
|
|
374
|
+
return _jsx("div", { className: "gh-blankslate", children: _jsx("h3", { children: "No commits" }) });
|
|
375
|
+
return (_jsx("div", { className: "gh-commits-tab", children: [...days].map(([day, list]) => (_jsxs("div", { className: "gh-commit-group", children: [_jsxs("h3", { className: "gh-commit-group-title", children: [_jsx(Octicon, { name: "git-commit" }), " Commits on ", day] }), _jsx("div", { className: "gh-box", children: list.map((c) => (_jsxs("div", { className: "gh-commit-row", children: [_jsxs("div", { children: [_jsx("div", { className: "gh-commit-row-title", children: (c.message ?? "").split("\n")[0] }), _jsxs("div", { className: "gh-commit-row-meta", children: [_jsx(Avatar, { login: c.authorLogin ?? c.author, size: 16 }), " ", _jsx("a", { className: "gh-author", children: c.authorLogin ?? c.author }), " ", _jsxs("span", { className: "gh-muted", children: ["committed ", _jsx(RelativeTime, { iso: c.date })] })] })] }), _jsxs("div", { className: "gh-commit-row-side", children: [_jsx("code", { className: "gh-sha gh-sha-btn", children: shortSha(c.sha) }), _jsx("button", { className: "gh-iconbtn gh-iconbtn-sm", "aria-label": "Copy full SHA", children: _jsx(Octicon, { name: "copy" }) }), _jsx("button", { className: "gh-iconbtn gh-iconbtn-sm", "aria-label": "Browse the repository at this point", children: _jsx(Octicon, { name: "code" }) })] })] }, c.sha))) })] }, day))) }));
|
|
376
|
+
}
|
|
377
|
+
function ChecksTab({ checks }) {
|
|
378
|
+
if (!checks.length)
|
|
379
|
+
return _jsxs("div", { className: "gh-blankslate", children: [_jsx(Octicon, { name: "checklist", size: 24 }), _jsx("h3", { children: "No checks" }), _jsx("p", { children: "This commit has no checks yet." })] });
|
|
380
|
+
return (_jsxs("div", { className: "gh-box gh-checks-tab", children: [_jsx("div", { className: "gh-box-header", children: _jsxs("strong", { children: [checks.length, " check", checks.length === 1 ? "" : "s"] }) }), checks.map((c) => (_jsxs("div", { className: "gh-check-row", children: [_jsx("span", { className: `ci-status ${c.state === "success" ? "success" : c.state === "pending" ? "pending" : "failure"}`, children: _jsx(Octicon, { name: CI_ICON[c.state] ?? "x" }) }), _jsx("strong", { children: c.name }), _jsx("span", { className: "gh-muted", children: c.description }), _jsx("span", { className: "gh-spacer" }), c.targetUrl ? _jsx("a", { href: c.targetUrl, target: "_blank", rel: "noreferrer", children: "Details" }) : null] }, c.name)))] }));
|
|
381
|
+
}
|
|
382
|
+
function diffLines(patch) {
|
|
383
|
+
const out = [];
|
|
384
|
+
let oldNo = 0, newNo = 0;
|
|
385
|
+
for (const line of patch.split("\n")) {
|
|
386
|
+
const m = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/.exec(line);
|
|
387
|
+
if (m) {
|
|
388
|
+
oldNo = Number(m[1]);
|
|
389
|
+
newNo = Number(m[2]);
|
|
390
|
+
out.push({ kind: "hunk", text: line });
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
if (line.startsWith("\\")) {
|
|
394
|
+
out.push({ kind: "note", text: line });
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
if (line.startsWith("+"))
|
|
398
|
+
out.push({ kind: "add", text: line.slice(1), newNo: newNo++ });
|
|
399
|
+
else if (line.startsWith("-"))
|
|
400
|
+
out.push({ kind: "del", text: line.slice(1), oldNo: oldNo++ });
|
|
401
|
+
else
|
|
402
|
+
out.push({ kind: "ctx", text: line.slice(1), oldNo: oldNo++, newNo: newNo++ });
|
|
403
|
+
}
|
|
404
|
+
return out;
|
|
405
|
+
}
|
|
406
|
+
/** The Files changed tab: each file's header (diffstat, path) and its unified diff with both line-number gutters. */
|
|
407
|
+
export function FilesDiff({ files }) {
|
|
408
|
+
if (!files.length)
|
|
409
|
+
return _jsx("div", { className: "gh-blankslate", children: _jsx("h3", { children: "No changes to show" }) });
|
|
410
|
+
return (_jsxs("section", { className: "files-diff gh-files", "data-testid": "files-diff", children: [_jsxs("div", { className: "gh-files-toolbar", children: [_jsxs("span", { className: "gh-select-menu", children: ["Changes from all commits ", _jsx(Octicon, { name: "triangle-down" })] }), _jsxs("span", { className: "gh-select-menu", children: ["File filter ", _jsx(Octicon, { name: "triangle-down" })] }), _jsxs("span", { className: "gh-select-menu", children: ["Conversations ", _jsx(Octicon, { name: "triangle-down" })] }), _jsx("span", { className: "gh-spacer" }), _jsxs("span", { className: "gh-muted", children: ["0 / ", files.length, " files viewed"] }), _jsxs("button", { className: "gh-btn gh-btn-sm gh-btn-primary", children: ["Review changes ", _jsx(Octicon, { name: "triangle-down" })] })] }), files.map((file) => (_jsxs("article", { className: "diff-file gh-file", children: [_jsxs("header", { className: "gh-file-header", children: [_jsx(Octicon, { name: "chevron-down" }), _jsx("span", { className: "gh-file-count", children: (file.additions ?? 0) + (file.deletions ?? 0) }), _jsx(DiffStat, { additions: file.additions, deletions: file.deletions }), _jsx("span", { className: "gh-file-name", children: file.filename }), _jsx("button", { className: "gh-iconbtn gh-iconbtn-sm", "aria-label": "Copy file name to clipboard", children: _jsx(Octicon, { name: "copy" }) }), _jsx("span", { className: "gh-spacer" }), _jsxs("label", { className: "gh-viewed", children: [_jsx("input", { type: "checkbox", readOnly: true }), " Viewed"] })] }), file.patch ? (_jsx("table", { className: "gh-diff-table", "data-testid": "diff-hunk", children: _jsx("tbody", { children: diffLines(file.patch).map((l, i) => l.kind === "hunk" || l.kind === "note" ? (_jsxs("tr", { className: `gh-diff-${l.kind}`, children: [_jsx("td", { className: "gh-diff-num", colSpan: 2 }), _jsx("td", { className: "gh-diff-code", children: l.text })] }, i)) : (_jsxs("tr", { className: `gh-diff-${l.kind}`, children: [_jsx("td", { className: "gh-diff-num", children: l.oldNo ?? "" }), _jsx("td", { className: "gh-diff-num", children: l.newNo ?? "" }), _jsxs("td", { className: "gh-diff-code", children: [_jsx("span", { className: "gh-diff-marker", children: l.kind === "add" ? "+" : l.kind === "del" ? "-" : " " }), l.text || "\u00a0"] })] }, i))) }) })) : _jsx("div", { className: "gh-file-nodiff gh-muted", children: file.status === "removed" ? "This file was deleted." : "Binary file not shown." })] }, file.filename)))] }));
|
|
411
|
+
}
|
|
412
|
+
export function PullPage({ pr, reviews, commits, comments, reviewComments, files, checks, stateEvents, viewer, labelColors, tab, onTab, actions }) {
|
|
413
|
+
if (!pr)
|
|
414
|
+
return _jsx("div", { className: "gh-container", children: _jsx("div", { className: "gh-blankslate", children: _jsx("h3", { children: "Loading pull request\u2026" }) }) });
|
|
415
|
+
return (_jsxs("div", { className: "gh-container gh-pull-page", children: [_jsx(PullHeader, { pr: pr, commits: commits.length, checks: checks.length, files: files.length, tab: tab, onTab: onTab }), tab === "conversation" ? (_jsxs("div", { className: "gh-pull-layout", children: [_jsx(Conversation, { pr: pr, reviews: reviews, commits: commits, comments: comments, reviewComments: reviewComments, checks: checks, stateEvents: stateEvents, viewer: viewer, actions: actions }), _jsx(Aside, { pr: pr, reviews: reviews, comments: comments, labelColors: labelColors })] })) : tab === "commits" ? _jsx(CommitsTab, { commits: commits })
|
|
416
|
+
: tab === "checks" ? _jsx(ChecksTab, { checks: checks })
|
|
417
|
+
: _jsx(FilesDiff, { files: files })] }));
|
|
418
|
+
}
|