@reviewflow/ui 6.24.0 → 6.26.0
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/package.json +17 -2
- package/src/components/branch-diff/diff-font.ts +26 -0
- package/src/components/branch-diff/diff-themes.ts +53 -1
- package/src/components/branch-diff/reviewflow-diff-selection.ts +11 -0
- package/src/components/branch-diff/reviewflow-diff-themes.ts +2218 -0
- package/src/components/branch-diff/use-diff-settings.tsx +15 -3
- package/src/components/branch-diff/whitespace-button.tsx +2 -2
- package/src/components/diff-settings-section.tsx +189 -0
- package/src/components/diff-theme-preview.tsx +63 -0
- package/src/components/file-kind.ts +91 -0
- package/src/components/file-type-icon.tsx +70 -0
- package/src/components/pull-request-diff-annotations.tsx +5 -2
- package/src/components/pull-request-diff-jump.ts +59 -1
- package/src/components/pull-request-diff-overview-tree.ts +57 -0
- package/src/components/pull-request-diff-overview.tsx +488 -88
- package/src/components/pull-request-diff-tree-files.ts +64 -0
- package/src/components/pull-request-diff-tree.tsx +232 -0
- package/src/components/pull-request-diff-view.tsx +111 -27
- package/src/components/pull-request-diff-viewer.tsx +23 -2
- package/src/components/pull-request-files.tsx +4 -4
- package/src/components/pull-request-panel.tsx +12 -6
- package/src/components/pull-request-screen.tsx +16 -4
- package/src/components/scroll-parent.ts +20 -0
- package/src/components/tooltip-icon-button.tsx +27 -0
- package/src/editor/code-editor.tsx +1 -0
- package/src/editor/code-token-colors.css +39 -0
- package/src/editor/codeHighlight.ts +2 -1
- package/src/editor/markdown-editor.css +1 -34
- package/src/editor/markdown-editor.tsx +1 -0
- package/src/index.ts +6 -6
- package/src/palette.ts +22 -0
- package/src/prStatus.ts +8 -1
- package/src/slots.tsx +8 -0
|
@@ -10,7 +10,6 @@ import { formatRelativeDate, pluralOf } from "@reviewflow/sdk";
|
|
|
10
10
|
import {
|
|
11
11
|
Button,
|
|
12
12
|
Code,
|
|
13
|
-
IconButton,
|
|
14
13
|
InfoMessage,
|
|
15
14
|
Menu,
|
|
16
15
|
MenuItem,
|
|
@@ -54,6 +53,7 @@ import {
|
|
|
54
53
|
PullRequestPartPending,
|
|
55
54
|
PullRequestPartReading,
|
|
56
55
|
} from "./pull-request-part-notice.tsx";
|
|
56
|
+
import { TooltipIconButton } from "./tooltip-icon-button.tsx";
|
|
57
57
|
|
|
58
58
|
type Scope = "all" | "since";
|
|
59
59
|
|
|
@@ -130,7 +130,7 @@ function ByCommitButton({
|
|
|
130
130
|
onToggle,
|
|
131
131
|
}: ByCommitButtonProps): ReactNode {
|
|
132
132
|
return (
|
|
133
|
-
<
|
|
133
|
+
<TooltipIconButton
|
|
134
134
|
icon={<GitCommitRegularIcon />}
|
|
135
135
|
size="sm"
|
|
136
136
|
accent={byCommit ? "brand" : "neutral"}
|
|
@@ -173,7 +173,7 @@ function CommitStepper({
|
|
|
173
173
|
if (commit === undefined) return null;
|
|
174
174
|
return (
|
|
175
175
|
<View className="min-w-0 shrink flex-row items-center gap-xs">
|
|
176
|
-
<
|
|
176
|
+
<TooltipIconButton
|
|
177
177
|
icon={<CaretLeftRegularIcon />}
|
|
178
178
|
size="sm"
|
|
179
179
|
accent="neutral"
|
|
@@ -207,7 +207,7 @@ function CommitStepper({
|
|
|
207
207
|
/>
|
|
208
208
|
))}
|
|
209
209
|
</Menu>
|
|
210
|
-
<
|
|
210
|
+
<TooltipIconButton
|
|
211
211
|
icon={<CaretRightRegularIcon />}
|
|
212
212
|
size="sm"
|
|
213
213
|
accent="neutral"
|
|
@@ -40,7 +40,7 @@ import { ChatCircleDotsRegularIcon } from "alouette-icons/phosphor-icons/ChatCir
|
|
|
40
40
|
import type { ReactNode } from "react";
|
|
41
41
|
import { useState } from "react";
|
|
42
42
|
import { usePullRequestDataSource } from "../dataSource.ts";
|
|
43
|
-
import {
|
|
43
|
+
import { usePrStatusAccent } from "../prStatus.ts";
|
|
44
44
|
import { emojify } from "../services/emojify.ts";
|
|
45
45
|
import { errorToMessage } from "../services/errorToMessage.ts";
|
|
46
46
|
import { formatDataAge } from "../services/formatDataAge.ts";
|
|
@@ -156,6 +156,12 @@ interface MergeLineProps {
|
|
|
156
156
|
children?: ReactNode;
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
function StatusBadge({ status }: { status: PrStatus }): ReactNode {
|
|
160
|
+
return (
|
|
161
|
+
<Badge accent={usePrStatusAccent(status)}>{prStatusLabels[status]}</Badge>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
159
165
|
function MergeLine({
|
|
160
166
|
status,
|
|
161
167
|
author,
|
|
@@ -165,9 +171,7 @@ function MergeLine({
|
|
|
165
171
|
}: MergeLineProps): ReactNode {
|
|
166
172
|
return (
|
|
167
173
|
<View className="flex-row flex-wrap items-center gap-xs">
|
|
168
|
-
{status === null ? null :
|
|
169
|
-
<Badge accent={prStatusAccents[status]}>{prStatusLabels[status]}</Badge>
|
|
170
|
-
)}
|
|
174
|
+
{status === null ? null : <StatusBadge status={status} />}
|
|
171
175
|
<Text className="text-sm font-body-bold">
|
|
172
176
|
{author ?? "unknown author"}
|
|
173
177
|
</Text>
|
|
@@ -1163,7 +1167,8 @@ function PullRequestActions({ detail, onEdited }: EditableProps): ReactNode {
|
|
|
1163
1167
|
export interface PullRequestPanelProps extends CandidatesProps {
|
|
1164
1168
|
/** why the editors stay read-only, when GitHub refused what they offer */
|
|
1165
1169
|
candidatesError: string | null;
|
|
1166
|
-
|
|
1170
|
+
/** absent: the first tab, Review for a reviewer and Conversation for the author */
|
|
1171
|
+
tab?: PullRequestTab;
|
|
1167
1172
|
onTabChange: (tab: PullRequestTab) => void;
|
|
1168
1173
|
/** the commit whose diff is open on the Commits tab; absent: the tab holds it */
|
|
1169
1174
|
openCommit?: string | null;
|
|
@@ -1183,7 +1188,7 @@ export function PullRequestPanel({
|
|
|
1183
1188
|
candidates,
|
|
1184
1189
|
candidatesError,
|
|
1185
1190
|
onEdited,
|
|
1186
|
-
tab,
|
|
1191
|
+
tab: tabProp,
|
|
1187
1192
|
onTabChange,
|
|
1188
1193
|
openCommit,
|
|
1189
1194
|
onOpenCommitChange,
|
|
@@ -1194,6 +1199,7 @@ export function PullRequestPanel({
|
|
|
1194
1199
|
"checks",
|
|
1195
1200
|
).value;
|
|
1196
1201
|
const viewerIsAuthor = isViewerAuthor(detail);
|
|
1202
|
+
const tab = tabProp ?? (viewerIsAuthor ? "timeline" : "review");
|
|
1197
1203
|
const reviewTab = (
|
|
1198
1204
|
<Tab
|
|
1199
1205
|
value="review"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { pullRequestPartsStaleAfter } from "@reviewflow/sdk";
|
|
2
2
|
import type {
|
|
3
|
+
DiffFont,
|
|
3
4
|
DiffWhitespace,
|
|
4
5
|
PullRequestDetailResponse,
|
|
5
6
|
PullRequestEdit,
|
|
@@ -63,7 +64,7 @@ interface PullRequestBodyProps {
|
|
|
63
64
|
ownerRepo: string;
|
|
64
65
|
number: number;
|
|
65
66
|
headerStart?: ReactNode;
|
|
66
|
-
tab
|
|
67
|
+
tab?: PullRequestTab;
|
|
67
68
|
onTabChange: (tab: PullRequestTab) => void;
|
|
68
69
|
openCommit?: string | null;
|
|
69
70
|
onOpenCommitChange?: (oid: string | null) => void;
|
|
@@ -203,8 +204,11 @@ function PullRequestBody({
|
|
|
203
204
|
export interface PullRequestScreenProps {
|
|
204
205
|
ownerRepo: string;
|
|
205
206
|
number: number;
|
|
206
|
-
/**
|
|
207
|
-
|
|
207
|
+
/**
|
|
208
|
+
* The tab shown; controlled, so an app can keep it in its url. Absent: the
|
|
209
|
+
* first tab, Review for a reviewer and Conversation for the author.
|
|
210
|
+
*/
|
|
211
|
+
tab?: PullRequestTab;
|
|
208
212
|
onTabChange: (tab: PullRequestTab) => void;
|
|
209
213
|
/**
|
|
210
214
|
* The commit whose diff is open on the Commits tab, by oid. Absent: the
|
|
@@ -239,8 +243,12 @@ export interface PullRequestScreenProps {
|
|
|
239
243
|
* Absent: they live as long as the screen.
|
|
240
244
|
*/
|
|
241
245
|
draftStore?: DraftStore;
|
|
242
|
-
/** the diff's syntax theme family; default `
|
|
246
|
+
/** the diff's syntax theme family; default `reviewflow` */
|
|
243
247
|
diffTheme?: DiffTheme;
|
|
248
|
+
/** the diff's code font; default `alouette` */
|
|
249
|
+
diffFont?: DiffFont;
|
|
250
|
+
/** the CSS `font-family` of the `custom` diff font */
|
|
251
|
+
diffCustomFont?: string;
|
|
244
252
|
/** wrap long lines in the diff; default true */
|
|
245
253
|
diffWordWrap?: boolean;
|
|
246
254
|
/** whether the diff starts with whitespace changes hidden; the toolbar toggle then owns it */
|
|
@@ -271,6 +279,8 @@ export function PullRequestScreen({
|
|
|
271
279
|
slots = {},
|
|
272
280
|
draftStore,
|
|
273
281
|
diffTheme,
|
|
282
|
+
diffFont,
|
|
283
|
+
diffCustomFont,
|
|
274
284
|
diffWordWrap,
|
|
275
285
|
initialWhitespace,
|
|
276
286
|
diffFileOrder,
|
|
@@ -281,6 +291,8 @@ export function PullRequestScreen({
|
|
|
281
291
|
<PullRequestSlotsProvider value={slots}>
|
|
282
292
|
<DiffSettingsProvider
|
|
283
293
|
theme={diffTheme}
|
|
294
|
+
font={diffFont}
|
|
295
|
+
customFont={diffCustomFont}
|
|
284
296
|
wordWrap={diffWordWrap}
|
|
285
297
|
initialWhitespace={initialWhitespace}
|
|
286
298
|
fileOrder={diffFileOrder}
|
|
@@ -41,6 +41,26 @@ export const scrollToTopOf = (
|
|
|
41
41
|
container.scrollTop -= hiddenAbove(element, container, stickyTop);
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Scrolls `element`'s scroll parent alone, the least that shows it whole
|
|
46
|
+
* below the `stickyTop` pixels a pinned row covers: the page around it stays
|
|
47
|
+
* where the reader has it, unlike `scrollIntoView`.
|
|
48
|
+
*/
|
|
49
|
+
export const revealWithinScrollParent = (
|
|
50
|
+
element: HTMLElement,
|
|
51
|
+
stickyTop: number,
|
|
52
|
+
): void => {
|
|
53
|
+
const container = scrollParentOf(element);
|
|
54
|
+
if (container === null) return;
|
|
55
|
+
const bounds = container.getBoundingClientRect();
|
|
56
|
+
const { top, bottom } = element.getBoundingClientRect();
|
|
57
|
+
const hidden = bounds.top + stickyTop - top;
|
|
58
|
+
if (hidden > 0) container.scrollTop -= hidden;
|
|
59
|
+
else if (bottom > bounds.bottom) {
|
|
60
|
+
container.scrollTop += bottom - bounds.bottom;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
44
64
|
/** As `scrollToTopOf`, only when `element`'s top is hidden above that bar. */
|
|
45
65
|
export const revealTopOf = (element: HTMLElement, stickyTop: number): void => {
|
|
46
66
|
const container = scrollParentOf(element);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { IconButtonProps } from "alouette";
|
|
2
|
+
import { IconButton, Tooltip } from "alouette";
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* An icon-only button whose label shows on hover, so its purpose is not left
|
|
7
|
+
* to the glyph. A disabled one shows its reason instead, in the tooltip the
|
|
8
|
+
* button renders itself. To replace by alouette's own once released:
|
|
9
|
+
* https://github.com/christophehurpeau/alouette/pull/448
|
|
10
|
+
*/
|
|
11
|
+
export function TooltipIconButton(props: IconButtonProps): ReactNode {
|
|
12
|
+
const { disabledReason, "aria-label": label } = props;
|
|
13
|
+
return (
|
|
14
|
+
<Tooltip
|
|
15
|
+
content={disabledReason ? null : label}
|
|
16
|
+
// the label already names the button: as its description too, a screen reader reads it twice
|
|
17
|
+
render={(triggerProps) => (
|
|
18
|
+
<IconButton
|
|
19
|
+
{...props}
|
|
20
|
+
{...triggerProps}
|
|
21
|
+
aria-describedby={undefined}
|
|
22
|
+
accessibilityHint={undefined}
|
|
23
|
+
/>
|
|
24
|
+
)}
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -6,6 +6,7 @@ import type { CodeLanguage } from "./codeEditorExtensions.ts";
|
|
|
6
6
|
import { codeEditorExtensions } from "./codeEditorExtensions.ts";
|
|
7
7
|
import { changeBetween, externalUpdate } from "./extensions.ts";
|
|
8
8
|
import "./markdown-editor.css";
|
|
9
|
+
import "./code-token-colors.css";
|
|
9
10
|
|
|
10
11
|
export interface CodeEditorProps {
|
|
11
12
|
value: string;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/* Generated by build-code-theme.ts from the palette in src/palette.ts. DO NOT EDIT. */
|
|
2
|
+
/* The code tokens of the editors, in fences and in the frontmatter's yaml. */
|
|
3
|
+
|
|
4
|
+
.markdown-editor {
|
|
5
|
+
--md-tok-comment: #63707e;
|
|
6
|
+
--md-tok-keyword: #cd112a;
|
|
7
|
+
--md-tok-string: #064d8e;
|
|
8
|
+
--md-tok-constant: #0c71c1;
|
|
9
|
+
--md-tok-property: #0c71c1;
|
|
10
|
+
--md-tok-type: #986b0c;
|
|
11
|
+
--md-tok-function: #8837df;
|
|
12
|
+
--md-tok-invalid: #cd112a;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.dark .markdown-editor {
|
|
16
|
+
--md-tok-comment: #b4bdc7;
|
|
17
|
+
--md-tok-keyword: #f56d68;
|
|
18
|
+
--md-tok-string: #97caf6;
|
|
19
|
+
--md-tok-constant: #47a5f4;
|
|
20
|
+
--md-tok-property: #47a5f4;
|
|
21
|
+
--md-tok-type: #e2b83c;
|
|
22
|
+
--md-tok-function: #af87e8;
|
|
23
|
+
--md-tok-invalid: #f56d68;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* the "system" preference never sets .dark: the palette resolves it with this
|
|
27
|
+
media query, so the dark tokens are repeated here */
|
|
28
|
+
@media (prefers-color-scheme: dark) {
|
|
29
|
+
.system .markdown-editor {
|
|
30
|
+
--md-tok-comment: #b4bdc7;
|
|
31
|
+
--md-tok-keyword: #f56d68;
|
|
32
|
+
--md-tok-string: #97caf6;
|
|
33
|
+
--md-tok-constant: #47a5f4;
|
|
34
|
+
--md-tok-property: #47a5f4;
|
|
35
|
+
--md-tok-type: #e2b83c;
|
|
36
|
+
--md-tok-function: #af87e8;
|
|
37
|
+
--md-tok-invalid: #f56d68;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -13,7 +13,8 @@ import { tags } from "@lezer/highlight";
|
|
|
13
13
|
* **Every style is scoped to a nested language**: `@lezer/markdown` tags its
|
|
14
14
|
* own nodes too (`heading1`, `strong`), and an unscoped style would paint prose
|
|
15
15
|
* a second time over the `md-*` classes. Classes, not colours: the palette is
|
|
16
|
-
* in `
|
|
16
|
+
* in `code-token-colors.css`, generated with the diff's reviewflow theme
|
|
17
|
+
* (`build-code-theme.ts`), switched with the theme.
|
|
17
18
|
*
|
|
18
19
|
* Coarse on purpose — a parent tag catches its children, so a language emitting
|
|
19
20
|
* a tag nobody listed still lands somewhere sensible. Ported from
|
|
@@ -28,14 +28,7 @@
|
|
|
28
28
|
--md-frontmatter-bg: color-mix(in srgb, var(--color-muted) 7%, transparent);
|
|
29
29
|
--md-quote-bg: color-mix(in srgb, var(--color-muted) 6%, transparent);
|
|
30
30
|
|
|
31
|
-
--md-tok-
|
|
32
|
-
--md-tok-keyword: #cf222e;
|
|
33
|
-
--md-tok-string: #0a3069;
|
|
34
|
-
--md-tok-constant: #0550ae;
|
|
35
|
-
--md-tok-property: #0550ae;
|
|
36
|
-
--md-tok-type: #116329;
|
|
37
|
-
--md-tok-function: #8250df;
|
|
38
|
-
--md-tok-invalid: #82071e;
|
|
31
|
+
/* the --md-tok-* colors are generated, in code-token-colors.css */
|
|
39
32
|
|
|
40
33
|
/* fills a column the caller grew; the editor still sizes to its content, and
|
|
41
34
|
the page — the app layout is the scroll container — scrolls a long one */
|
|
@@ -53,32 +46,6 @@
|
|
|
53
46
|
transition: border-color 150ms ease-in;
|
|
54
47
|
}
|
|
55
48
|
|
|
56
|
-
.dark .markdown-editor {
|
|
57
|
-
--md-tok-comment: #8b949e;
|
|
58
|
-
--md-tok-keyword: #ff7b72;
|
|
59
|
-
--md-tok-string: #a5d6ff;
|
|
60
|
-
--md-tok-constant: #79c0ff;
|
|
61
|
-
--md-tok-property: #79c0ff;
|
|
62
|
-
--md-tok-type: #7ee787;
|
|
63
|
-
--md-tok-function: #d2a8ff;
|
|
64
|
-
--md-tok-invalid: #f85149;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/* the "system" preference never sets .dark: the palette resolves it with this
|
|
68
|
-
media query, so the dark tokens are repeated here */
|
|
69
|
-
@media (prefers-color-scheme: dark) {
|
|
70
|
-
.system .markdown-editor {
|
|
71
|
-
--md-tok-comment: #8b949e;
|
|
72
|
-
--md-tok-keyword: #ff7b72;
|
|
73
|
-
--md-tok-string: #a5d6ff;
|
|
74
|
-
--md-tok-constant: #79c0ff;
|
|
75
|
-
--md-tok-property: #79c0ff;
|
|
76
|
-
--md-tok-type: #7ee787;
|
|
77
|
-
--md-tok-function: #d2a8ff;
|
|
78
|
-
--md-tok-invalid: #f85149;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
49
|
.markdown-editor:hover {
|
|
83
50
|
border-color: var(--color-interactive-outlined-hover);
|
|
84
51
|
}
|
package/src/index.ts
CHANGED
|
@@ -34,18 +34,18 @@ export {
|
|
|
34
34
|
isDiffFileOrder,
|
|
35
35
|
} from "./components/branch-diff/use-diff-settings.tsx";
|
|
36
36
|
export type { DiffFileOrder } from "./components/branch-diff/use-diff-settings.tsx";
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
diffThemes,
|
|
42
|
-
} from "./components/branch-diff/diff-themes.ts";
|
|
37
|
+
// From the sdk, not `branch-diff/diff-themes.ts`: that one registers the reviewflow themes with
|
|
38
|
+
// @pierre/diffs, which this index would put in every importer's main bundle.
|
|
39
|
+
export type { DiffTheme } from "@reviewflow/sdk";
|
|
40
|
+
export { diffThemeNames, diffThemeOptions, diffThemes } from "@reviewflow/sdk";
|
|
43
41
|
// DiffWorkerPool is the `@reviewflow/ui/diff-worker-pool` subpath, not exported here: its web
|
|
44
42
|
// variant pulls @pierre/diffs and shiki, which this index would put in every importer's main
|
|
45
43
|
// bundle. A lazily loaded diff viewer imports the subpath.
|
|
46
44
|
export type { CollapsedFiles } from "./components/branch-diff/use-collapsed-files.ts";
|
|
47
45
|
export { useCollapsedFiles } from "./components/branch-diff/use-collapsed-files.ts";
|
|
48
46
|
export { WhitespaceButton } from "./components/branch-diff/whitespace-button.tsx";
|
|
47
|
+
export type { DiffDisplaySettings } from "./components/diff-settings-section.tsx";
|
|
48
|
+
export { DiffSettingsSection } from "./components/diff-settings-section.tsx";
|
|
49
49
|
export { CopyableCodeBlock } from "./components/copyable-code-block.tsx";
|
|
50
50
|
export { PullRequestReviewDraftProvider } from "./services/PullRequestReviewDraftProvider.tsx";
|
|
51
51
|
export type { DraftStore } from "./services/PullRequestDraftsProvider.tsx";
|
package/src/palette.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { AccentName, PaletteSpec } from "alouette/theme-generator";
|
|
2
|
+
|
|
3
|
+
// Hues fitted (in OKLCH) on GitHub Primer's own scales, so reviewflow reads as
|
|
4
|
+
// a companion to the GitHub UI it augments: green brand and success, Primer
|
|
5
|
+
// blue for info, its slightly pink red, its amber-to-cream yellow ramp, and the
|
|
6
|
+
// blue-tinted grays instead of alouette's neutral ones.
|
|
7
|
+
// Read by the app's `build-theme.ts` (its alouette palette) and by this
|
|
8
|
+
// package's `build-code-theme.ts` (the reviewflow code theme).
|
|
9
|
+
export const reviewflowPalette = {
|
|
10
|
+
grayscale: { type: "grayscale", hue: 250, intensity: 0.18 },
|
|
11
|
+
brand: { type: "accent", hue: 148, hueHi: 152 },
|
|
12
|
+
success: { type: "accent", hue: 148, hueHi: 152 },
|
|
13
|
+
danger: { type: "accent", hue: 21, hueHi: 26 },
|
|
14
|
+
info: { type: "accent", hue: 260, hueHi: 242 },
|
|
15
|
+
warning: { type: "brightAccent", hue: 52, hueHi: 99 },
|
|
16
|
+
} as const satisfies Partial<Record<AccentName, PaletteSpec>>;
|
|
17
|
+
|
|
18
|
+
// Hues alouette has no accent for, used by the code theme only: Primer's
|
|
19
|
+
// purple, for functions as GitHub colors them, its tints pinker as Primer's.
|
|
20
|
+
export const reviewflowCodePalette = {
|
|
21
|
+
purple: { type: "accent", hue: 295, hueHi: 305, intensity: 0.85 },
|
|
22
|
+
} as const satisfies Record<string, PaletteSpec>;
|
package/src/prStatus.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { PrStatus } from "@reviewflow/sdk";
|
|
2
2
|
import type { Accent } from "alouette";
|
|
3
|
+
import { usePullRequestSlots } from "./slots.tsx";
|
|
3
4
|
|
|
4
|
-
/** A merged pull request takes the brand accent; an app with its own accent for it passes one
|
|
5
|
+
/** A merged pull request takes the brand accent; an app with its own accent for it passes one in the `prStatusAccents` slot. */
|
|
5
6
|
export const prStatusAccents: Record<PrStatus, Accent | undefined> = {
|
|
6
7
|
approved: "success",
|
|
7
8
|
changesRequested: "danger",
|
|
@@ -11,3 +12,9 @@ export const prStatusAccents: Record<PrStatus, Accent | undefined> = {
|
|
|
11
12
|
noReviewers: "warning",
|
|
12
13
|
waitingReview: "info",
|
|
13
14
|
};
|
|
15
|
+
|
|
16
|
+
/** The badge accent of `status`: the app's (the `prStatusAccents` slot) over the ui's. */
|
|
17
|
+
export const usePrStatusAccent = (status: PrStatus): Accent | undefined => {
|
|
18
|
+
const { prStatusAccents: appAccents = {} } = usePullRequestSlots();
|
|
19
|
+
return status in appAccents ? appAccents[status] : prStatusAccents[status];
|
|
20
|
+
};
|
package/src/slots.tsx
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { PrStatus } from "@reviewflow/sdk";
|
|
2
|
+
import type { Accent } from "alouette";
|
|
1
3
|
import type { ReactNode } from "react";
|
|
2
4
|
import { createContext, use } from "react";
|
|
3
5
|
|
|
@@ -25,6 +27,12 @@ export interface PullRequestSlots {
|
|
|
25
27
|
* checkout is configured. Absent: the message stands alone.
|
|
26
28
|
*/
|
|
27
29
|
renderRepositoryAction?: (ownerRepo: string) => ReactNode;
|
|
30
|
+
/**
|
|
31
|
+
* The status badge's accent, per status, over `prStatusAccents`: an app with
|
|
32
|
+
* an accent of its own for one (a theme it generates beside alouette's, cast
|
|
33
|
+
* to `Accent`) passes it here.
|
|
34
|
+
*/
|
|
35
|
+
prStatusAccents?: Partial<Record<PrStatus, Accent | undefined>>;
|
|
28
36
|
}
|
|
29
37
|
|
|
30
38
|
const SlotsContext = createContext<PullRequestSlots>({});
|