prreviewbuddy 0.82.4 → 0.83.3
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/CHANGELOG.md +35 -0
- package/dist/server.js +154 -46
- package/package.json +1 -1
- package/static/{chunk-Dio1flWJ.js → chunk-B2vcrGTe.js} +1 -1
- package/static/{chunk-C_KEGoSE.js → chunk-BL6xJoZm.js} +1 -1
- package/static/chunk-DwdwtB9r.js +1 -0
- package/static/help.js +1 -1
- package/static/notifications.js +1 -1
- package/static/reviews.js +1 -1
- package/static/runs.js +1 -1
- package/static/settings.js +1 -1
- package/static/shell.css +160 -35
- package/static/workspace.js +1 -1
- package/static/chunk-CmdZYpyj.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.83.3
|
|
4
|
+
|
|
5
|
+
**Peek at a collapsed sidebar.** Rest the pointer on the collapsed sidebar, or tab into it, and it
|
|
6
|
+
slides out to full width over the page, with every name and section, until you move away. The page
|
|
7
|
+
underneath stays where it was, and the sidebar stays collapsed.
|
|
8
|
+
|
|
9
|
+
## 0.83.2
|
|
10
|
+
|
|
11
|
+
**Send feedback is only in the sidebar.** A narrow window no longer draws a Send feedback link in
|
|
12
|
+
the top toolbar; it is in the sidebar, expanded or collapsed, and nowhere else.
|
|
13
|
+
|
|
14
|
+
## 0.83.1
|
|
15
|
+
|
|
16
|
+
**New review is the sidebar's one purple button, and Collapse sidebar moves to the sidebar's foot.**
|
|
17
|
+
New review is filled in purple wherever it is: the full-width button, the square + of the collapsed
|
|
18
|
+
sidebar, and the + in the top toolbar. Collapse sidebar is now the last row of the sidebar, below
|
|
19
|
+
Send feedback, and expands it again from the same place. While the sidebar is hidden, Show sidebar
|
|
20
|
+
in the top toolbar brings it back, and Send feedback is hidden with it.
|
|
21
|
+
|
|
22
|
+
## 0.83.0
|
|
23
|
+
|
|
24
|
+
**Start a review from New review in the sidebar, and collapse the sidebar to icons.** A New review
|
|
25
|
+
button, with its shortcut beside it, sits above Reviews in the sidebar on every workspace page. It
|
|
26
|
+
opens the same panel as ⌘P (Ctrl+P on other platforms), which works as before; that panel is now
|
|
27
|
+
called New review too. The button at the far left of the top toolbar collapses the sidebar to a
|
|
28
|
+
column of icons, with New review as a + and each name shown on hover, and expands it again. ⌘K H
|
|
29
|
+
hides the sidebar completely; the same toolbar button then brings it back, and a + in the toolbar
|
|
30
|
+
starts a review while it is hidden. Your choice is remembered.
|
|
31
|
+
|
|
32
|
+
## 0.82.5
|
|
33
|
+
|
|
34
|
+
**Help explains Collections more plainly.** The Collections topic in Workspace Help now says what
|
|
35
|
+
collections are for, how to add reviews, what happens when one is marked Done, and that collapsing
|
|
36
|
+
helps you stay focused. The note on how long reviews in a collection are kept is unchanged.
|
|
37
|
+
|
|
3
38
|
## 0.82.4
|
|
4
39
|
|
|
5
40
|
**⌘K L C lists your collections, to expand or collapse one.** Each row shows the collection's
|
package/dist/server.js
CHANGED
|
@@ -803,7 +803,10 @@ var PATHS = {
|
|
|
803
803
|
closed: "<circle cx=\"12\" cy=\"12\" r=\"9\"/><path d=\"m5.6 5.6 12.8 12.8\"/>",
|
|
804
804
|
reopened: "<circle cx=\"12\" cy=\"12\" r=\"9\"/><circle cx=\"12\" cy=\"12\" r=\"2\"/>",
|
|
805
805
|
warning: "<path d=\"M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z\"/><path d=\"M12 9v4M12 17h.01\"/>",
|
|
806
|
-
forward: "<path d=\"M5 12h14\"/><path d=\"m12 5 7 7-7 7\"/>"
|
|
806
|
+
forward: "<path d=\"M5 12h14\"/><path d=\"m12 5 7 7-7 7\"/>",
|
|
807
|
+
plus: "<path d=\"M12 5v14M5 12h14\"/>",
|
|
808
|
+
"sidebar-collapse": "<rect x=\"3\" y=\"4\" width=\"18\" height=\"16\" rx=\"2\"/><path d=\"M9 4v16\"/><path d=\"m16 9-3 3 3 3\"/>",
|
|
809
|
+
"sidebar-expand": "<rect x=\"3\" y=\"4\" width=\"18\" height=\"16\" rx=\"2\"/><path d=\"M9 4v16\"/><path d=\"m13 9 3 3-3 3\"/>"
|
|
807
810
|
};
|
|
808
811
|
/** The icon slot for one rail row. Hidden from assistive technology: the label beside it says it. */
|
|
809
812
|
function railIcon(icon) {
|
|
@@ -863,6 +866,18 @@ function serverStatusLabel(status) {
|
|
|
863
866
|
return "Workspace server running";
|
|
864
867
|
}
|
|
865
868
|
//#endregion
|
|
869
|
+
//#region ../../packages/review-harness/src/workspace/sidebar_state.ts
|
|
870
|
+
/**
|
|
871
|
+
* What the sidebar's own toggle, at its foot, will do when pressed, which is what it is called.
|
|
872
|
+
* Only Expanded and Compact: while the sidebar is hidden that toggle is not drawn, and the strip's
|
|
873
|
+
* restore control, which only ever says Show sidebar, stands in for it.
|
|
874
|
+
*/
|
|
875
|
+
function sidebarToggleLabel(sidebar) {
|
|
876
|
+
return sidebar?.compact ? "Expand sidebar" : "Collapse sidebar";
|
|
877
|
+
}
|
|
878
|
+
/** The strip's restore control, drawn only while the sidebar is hidden. */
|
|
879
|
+
var SHOW_SIDEBAR = "Show sidebar";
|
|
880
|
+
//#endregion
|
|
866
881
|
//#region ../../packages/review-harness/src/workspace/workspace_nav.ts
|
|
867
882
|
/**
|
|
868
883
|
* The workspace's own places, and the one model every page draws them from.
|
|
@@ -905,6 +920,13 @@ var WORKSPACE_PLACES = [
|
|
|
905
920
|
}
|
|
906
921
|
];
|
|
907
922
|
/**
|
|
923
|
+
* The sidebar's state, on `<html>` beside the theme, so the page is drawn that way from its first
|
|
924
|
+
* paint. The stylesheet reads it; `client/sidebar.ts` changes it.
|
|
925
|
+
*/
|
|
926
|
+
function sidebarAttributes(sidebar) {
|
|
927
|
+
return `${sidebar?.compact ? " data-sidebar-compact" : ""}${sidebar?.hidden ? " data-sidebar-hidden" : ""}`;
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
908
930
|
* The count beside Notifications. Always rendered, hidden at zero, so the page can reveal it when
|
|
909
931
|
* `/revision` or Mark all read reports a new number. See `client/unread_counts.ts`.
|
|
910
932
|
*/
|
|
@@ -934,7 +956,8 @@ function renderGlobalBar(options) {
|
|
|
934
956
|
const live = options.live ?? true;
|
|
935
957
|
return `<header class="prb-strip">
|
|
936
958
|
<span class="prb-wordmark"><b>pr</b>review<b>buddy</b></span>
|
|
937
|
-
${live ?
|
|
959
|
+
${live ? renderSidebarRestore() : ""}
|
|
960
|
+
${live ? renderNewReview() : ""}
|
|
938
961
|
${live ? renderHistoryControls() : ""}
|
|
939
962
|
${live ? renderStripReviews(options) : ""}
|
|
940
963
|
${live ? renderStripRuns(options) : ""}
|
|
@@ -980,6 +1003,37 @@ function renderNotificationsBell(options) {
|
|
|
980
1003
|
</a>`;
|
|
981
1004
|
}
|
|
982
1005
|
/**
|
|
1006
|
+
* Show sidebar: the way back from Hidden, and drawn only then (see `shell.css`). Among the strip's
|
|
1007
|
+
* controls rather than beside the wordmark, so it reads as a control and not as part of the brand.
|
|
1008
|
+
* Named in words and in the strip's own tooltip, never a `title`.
|
|
1009
|
+
*/
|
|
1010
|
+
function renderSidebarRestore() {
|
|
1011
|
+
return `<button type="button" class="prb-strip-action prb-sidebar-restore" data-sidebar-toggle="strip" aria-label="${SHOW_SIDEBAR}" data-tip="${SHOW_SIDEBAR}">${iconSvg("sidebar-expand")}</button>`;
|
|
1012
|
+
}
|
|
1013
|
+
/**
|
|
1014
|
+
* The sidebar's own toggle, the last row of its foot: Expanded and Compact, back and forth.
|
|
1015
|
+
*
|
|
1016
|
+
* In the same place in both, so it never moves under the pointer that pressed it. Hidden is not a
|
|
1017
|
+
* third press: it is its own act (⌘K H), and the strip's Show sidebar brings it back at the width it
|
|
1018
|
+
* had. The row's words say what pressing it will do, and in Compact they are its tooltip, as every
|
|
1019
|
+
* row's are. Both drawings are rendered and the stylesheet shows the one for the state, so nothing
|
|
1020
|
+
* needs repainting but the words. `client/sidebar.ts` keeps those true.
|
|
1021
|
+
*/
|
|
1022
|
+
function renderSidebarToggle(sidebar) {
|
|
1023
|
+
return `<button type="button" class="prb-rail-item prb-rail-link prb-sidebar-toggle" data-sidebar-toggle="rail" aria-expanded="${!sidebar?.compact}">
|
|
1024
|
+
<span class="prb-rail-icon" aria-hidden="true"><span class="prb-sidebar-collapse-icon">${iconSvg("sidebar-collapse")}</span><span class="prb-sidebar-expand-icon">${iconSvg("sidebar-expand")}</span></span>
|
|
1025
|
+
<span class="prb-rail-label"><span class="prb-rail-title">${sidebarToggleLabel(sidebar)}</span></span>
|
|
1026
|
+
</button>`;
|
|
1027
|
+
}
|
|
1028
|
+
/**
|
|
1029
|
+
* New review, in the strip: the square stand-in for the rail's labelled button, shown only while
|
|
1030
|
+
* the rail is hidden (a narrow window, a review's focus mode). See `renderNewReviewButton`, and
|
|
1031
|
+
* `shell.css` for the one rule that decides which of the two is showing.
|
|
1032
|
+
*/
|
|
1033
|
+
function renderNewReview() {
|
|
1034
|
+
return `<button type="button" class="prb-strip-action prb-strip-new-review" aria-label="New review" data-new-review="strip">${iconSvg("plus")}</button>`;
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
983
1037
|
* Back and Forward through this tab's workspace history, ahead of the places.
|
|
984
1038
|
*
|
|
985
1039
|
* Workspace-wide, so here in the strip rather than in a review's own bar: they move between views,
|
|
@@ -1058,9 +1112,20 @@ function renderServerStatus() {
|
|
|
1058
1112
|
<span class="prb-visually-hidden" data-server-status-label>${escapeHtml$1(serverStatusLabel("running"))}</span>
|
|
1059
1113
|
</span>`;
|
|
1060
1114
|
}
|
|
1061
|
-
/**
|
|
1115
|
+
/**
|
|
1116
|
+
* New review, at the head of the workspace rows: starting a review is the product's primary action,
|
|
1117
|
+
* so it is a labelled button rather than an icon to interpret, set apart from the places below it.
|
|
1118
|
+
*
|
|
1119
|
+
* What it does is wired by the command layer (`client/command_layer.ts`), which runs the very
|
|
1120
|
+
* command ⌘P runs, so the two cannot open different things. The shortcut beside the label is
|
|
1121
|
+
* painted there too, because only the browser knows the platform.
|
|
1122
|
+
*/
|
|
1123
|
+
function renderNewReviewButton() {
|
|
1124
|
+
return `<div class="prb-rail-new"><button type="button" class="prb-new-review" data-new-review="rail">${iconSvg("plus")}<span class="prb-new-review-label">New review</span><kbd class="prb-new-review-key" data-new-review-key aria-hidden="true"></kbd></button></div>`;
|
|
1125
|
+
}
|
|
1126
|
+
/** New review, then the workspace rows, for a rail group. */
|
|
1062
1127
|
function renderWorkspaceNav(options) {
|
|
1063
|
-
return WORKSPACE_PLACES.map((item) => `<a class="prb-rail-item prb-rail-link" href="${item.href}"${item.place === options.current ? " aria-current=\"true\"" : ""}>
|
|
1128
|
+
return renderNewReviewButton() + WORKSPACE_PLACES.map((item) => `<a class="prb-rail-item prb-rail-link" href="${item.href}"${item.place === options.current ? " aria-current=\"true\"" : ""}>
|
|
1064
1129
|
${railIcon(item.icon)}
|
|
1065
1130
|
<span class="prb-rail-label"><span class="prb-rail-title">${escapeHtml$1(item.label)}</span></span>
|
|
1066
1131
|
${item.place === "notifications" ? unreadCount(options.unread, "rail") : ""}
|
|
@@ -1097,20 +1162,9 @@ function renderSendFeedback() {
|
|
|
1097
1162
|
function renderWorkspaceRail(options) {
|
|
1098
1163
|
return `<nav class="prb-rail" aria-label="Workspace">
|
|
1099
1164
|
<div class="prb-rail-group">${renderWorkspaceNav(options)}</div>
|
|
1100
|
-
<div class="prb-rail-group prb-rail-foot">${renderSendFeedback()}</div>
|
|
1165
|
+
<div class="prb-rail-group prb-rail-foot">${renderSendFeedback()}${renderSidebarToggle(options.sidebar)}</div>
|
|
1101
1166
|
</nav>`;
|
|
1102
1167
|
}
|
|
1103
|
-
/**
|
|
1104
|
-
* What the rail carries that the strip does not, shown only while a narrow window hides the rail.
|
|
1105
|
-
*
|
|
1106
|
-
* Which is now the feedback form alone. Every workspace place is an icon in this same strip at
|
|
1107
|
-
* every width, so a text link beside one of them would be the same destination twice; before the
|
|
1108
|
-
* Reviews icon existed this is where Reviews had to go, and the filter that put it here is gone
|
|
1109
|
-
* with the reason for it.
|
|
1110
|
-
*/
|
|
1111
|
-
function renderHeaderNav(_options) {
|
|
1112
|
-
return `<nav class="prb-header-nav" aria-label="Workspace"><a class="prb-feedback" href="${escapeHtml$1(feedbackUrl())}" target="_blank" rel="noopener noreferrer"><span class="prb-feedback-icon" aria-hidden="true">${iconSvg("feedback")}</span><span class="prb-feedback-label">Send feedback</span></a></nav>`;
|
|
1113
|
-
}
|
|
1114
1168
|
//#endregion
|
|
1115
1169
|
//#region ../../packages/review-harness/src/domain/utils/file_path.ts
|
|
1116
1170
|
/**
|
|
@@ -6088,7 +6142,7 @@ function renderWorkspace(view) {
|
|
|
6088
6142
|
*/
|
|
6089
6143
|
const freshness = result ? view.freshness : void 0;
|
|
6090
6144
|
return `<!doctype html>
|
|
6091
|
-
<html lang="en"${themeAttribute(view.theme)}>
|
|
6145
|
+
<html lang="en"${themeAttribute(view.theme)}${sidebarAttributes(view.sidebar)}>
|
|
6092
6146
|
<head>
|
|
6093
6147
|
<meta charset="utf-8">
|
|
6094
6148
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
@@ -6681,6 +6735,7 @@ function renderRailFoot(view) {
|
|
|
6681
6735
|
return `<div class="prb-rail-group prb-rail-foot">
|
|
6682
6736
|
${view.token ? renderWorkspaceNav({ unread: view.unreadNotifications ?? 0 }) : ""}
|
|
6683
6737
|
${renderSendFeedback()}
|
|
6738
|
+
${view.token ? renderSidebarToggle(view.sidebar) : ""}
|
|
6684
6739
|
</div>`;
|
|
6685
6740
|
}
|
|
6686
6741
|
/**
|
|
@@ -9676,7 +9731,7 @@ function renderReviewsPage(view) {
|
|
|
9676
9731
|
const storedReviews = view.groups.reduce((total, group) => total + group.history.length, 0);
|
|
9677
9732
|
const renderSectionAt = (section) => renderSection(section, sections[section], (groups) => renderCards(groups, "home", (group) => collectedHome.has(group)), sectionOpen(section, view.sectionsCollapsed), sections[section].filter((group) => !inProgressOnly.has(group.latest.id) && !collectedHome.has(group)).length);
|
|
9678
9733
|
return `<!doctype html>
|
|
9679
|
-
<html lang="en"${themeAttribute(view.theme)}>
|
|
9734
|
+
<html lang="en"${themeAttribute(view.theme)}${sidebarAttributes(view.sidebar)}>
|
|
9680
9735
|
<head>
|
|
9681
9736
|
<meta charset="utf-8">
|
|
9682
9737
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
@@ -9699,7 +9754,8 @@ ${renderContextBar("Reviews", ordered.length === 0 ? "" : renderControls$1(queue
|
|
|
9699
9754
|
<div class="prb-shell">
|
|
9700
9755
|
${renderWorkspaceRail({
|
|
9701
9756
|
current: "reviews",
|
|
9702
|
-
unread: view.unreadNotifications ?? 0
|
|
9757
|
+
unread: view.unreadNotifications ?? 0,
|
|
9758
|
+
sidebar: view.sidebar
|
|
9703
9759
|
})}
|
|
9704
9760
|
<main class="prb-index">
|
|
9705
9761
|
${ordered.length === 0 ? "" : `<div class="prb-index-summary">
|
|
@@ -10617,7 +10673,7 @@ function readPreferences() {
|
|
|
10617
10673
|
try {
|
|
10618
10674
|
const parsed = JSON.parse(readFileSync(PREFERENCES_PATH, "utf8"));
|
|
10619
10675
|
if (!parsed || typeof parsed !== "object") return {};
|
|
10620
|
-
const { askWidth, dismissedNotices, promptsCollapsed, changeMapRegister, theme, reviewSections, runsSections, reviewLayout } = parsed;
|
|
10676
|
+
const { askWidth, dismissedNotices, promptsCollapsed, changeMapRegister, theme, reviewSections, runsSections, reviewLayout, sidebar } = parsed;
|
|
10621
10677
|
return {
|
|
10622
10678
|
...typeof askWidth === "number" && Number.isFinite(askWidth) ? { askWidth: clampAskWidth(askWidth) } : {},
|
|
10623
10679
|
...Array.isArray(dismissedNotices) ? { dismissedNotices: dismissedNotices.filter((key) => typeof key === "string" && NOTICE_KEY.test(key)) } : {},
|
|
@@ -10626,7 +10682,8 @@ function readPreferences() {
|
|
|
10626
10682
|
...isTheme(theme) ? { theme } : {},
|
|
10627
10683
|
...readReviewSections(reviewSections),
|
|
10628
10684
|
...readRunsSections(runsSections),
|
|
10629
|
-
...readLayout(reviewLayout)
|
|
10685
|
+
...readLayout(reviewLayout),
|
|
10686
|
+
...readSidebar(sidebar)
|
|
10630
10687
|
};
|
|
10631
10688
|
} catch {
|
|
10632
10689
|
return {};
|
|
@@ -10659,6 +10716,35 @@ function writeReviewLayout(patch) {
|
|
|
10659
10716
|
writeFileSync(PREFERENCES_PATH, `${JSON.stringify(next, null, 2)}\n`);
|
|
10660
10717
|
} catch {}
|
|
10661
10718
|
}
|
|
10719
|
+
/** Only the two booleans, and only when one is set; an empty object is no preference. */
|
|
10720
|
+
function readSidebar(value) {
|
|
10721
|
+
if (!value || typeof value !== "object") return {};
|
|
10722
|
+
const { compact, hidden } = value;
|
|
10723
|
+
const sidebar = {
|
|
10724
|
+
...typeof compact === "boolean" ? { compact } : {},
|
|
10725
|
+
...typeof hidden === "boolean" ? { hidden } : {}
|
|
10726
|
+
};
|
|
10727
|
+
return Object.keys(sidebar).length === 0 ? {} : { sidebar };
|
|
10728
|
+
}
|
|
10729
|
+
/** The sidebar fields a request may set: a boolean or nothing, each. */
|
|
10730
|
+
function readSidebarPatch(value) {
|
|
10731
|
+
const { sidebar } = readSidebar(value);
|
|
10732
|
+
return sidebar ?? null;
|
|
10733
|
+
}
|
|
10734
|
+
/** Remember a change to the sidebar, merged into what is stored. Read-modify-write, like every other field here. */
|
|
10735
|
+
function writeSidebar(patch) {
|
|
10736
|
+
try {
|
|
10737
|
+
mkdirSync(STORE_ROOT, { recursive: true });
|
|
10738
|
+
const current = readPreferences();
|
|
10739
|
+
writeFileSync(PREFERENCES_PATH, `${JSON.stringify({
|
|
10740
|
+
...current,
|
|
10741
|
+
sidebar: {
|
|
10742
|
+
...current.sidebar,
|
|
10743
|
+
...patch
|
|
10744
|
+
}
|
|
10745
|
+
}, null, 2)}\n`);
|
|
10746
|
+
} catch {}
|
|
10747
|
+
}
|
|
10662
10748
|
/** Remember whether "Your prompts" is folded. Read-modify-write, like every other field here. */
|
|
10663
10749
|
function writePromptsCollapsed(collapsed) {
|
|
10664
10750
|
try {
|
|
@@ -10879,7 +10965,7 @@ function renderSettingsPage(view) {
|
|
|
10879
10965
|
const { settings } = view;
|
|
10880
10966
|
const address = `http://localhost:${settings.port.configured}`;
|
|
10881
10967
|
return `<!doctype html>
|
|
10882
|
-
<html lang="en"${themeAttribute(settings.theme)}>
|
|
10968
|
+
<html lang="en"${themeAttribute(settings.theme)}${sidebarAttributes(view.sidebar)}>
|
|
10883
10969
|
<head>
|
|
10884
10970
|
<meta charset="utf-8">
|
|
10885
10971
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
@@ -10898,7 +10984,8 @@ ${renderContextBar("Settings", renderBack())}
|
|
|
10898
10984
|
<div class="prb-shell">
|
|
10899
10985
|
${renderWorkspaceRail({
|
|
10900
10986
|
current: "settings",
|
|
10901
|
-
unread: view.unreadNotifications ?? 0
|
|
10987
|
+
unread: view.unreadNotifications ?? 0,
|
|
10988
|
+
sidebar: view.sidebar
|
|
10902
10989
|
})}
|
|
10903
10990
|
<main class="prb-index prb-settings">
|
|
10904
10991
|
<div class="prb-page-panel">
|
|
@@ -11399,8 +11486,10 @@ var TOPICS = [
|
|
|
11399
11486
|
<li><code>prreviewbuddy review --pr <url></code> for a pull request</li>
|
|
11400
11487
|
<li><code>prreviewbuddy open</code> to list the reviews you have and open this workspace</li>
|
|
11401
11488
|
</ul>
|
|
11402
|
-
<p>Or
|
|
11403
|
-
|
|
11489
|
+
<p>Or click <b>New review</b> at the top of the sidebar, or press <kbd class="prb-kbd">⌘P</kbd>
|
|
11490
|
+
(<kbd class="prb-kbd">Ctrl+P</kbd> on other platforms), to start a review from anywhere in the
|
|
11491
|
+
workspace. With the sidebar collapsed, New review is the <b>+</b> at its top, and with the
|
|
11492
|
+
sidebar hidden, the <b>+</b> in the top toolbar. The panel follows the reviews it started while it stays open, and closing it
|
|
11404
11493
|
stops nothing. <code>prreviewbuddy help</code> lists every command, and
|
|
11405
11494
|
<code>prreviewbuddy review --help</code> every option a review takes.</p>
|
|
11406
11495
|
<p>There is no account and no API key. PR Review Buddy uses the coding agent installed on your
|
|
@@ -11498,18 +11587,13 @@ feature: E</pre>
|
|
|
11498
11587
|
{
|
|
11499
11588
|
id: "collections",
|
|
11500
11589
|
title: "Collections",
|
|
11501
|
-
body: `<p>
|
|
11502
|
-
|
|
11503
|
-
|
|
11504
|
-
|
|
11505
|
-
<
|
|
11506
|
-
|
|
11507
|
-
|
|
11508
|
-
leaves their other collections alone.</p>
|
|
11509
|
-
<p>Each collection lists its reviews that are not Done. Marking one Done moves it under Show
|
|
11510
|
-
completed, in every collection it is in, and Move to active brings it back. A review in a
|
|
11511
|
-
collection is listed there rather than under Active, Waiting or Done. A pinned review stays under
|
|
11512
|
-
Pinned. The Collections filter shows chosen collections, or Uncollected for reviews in none.</p>
|
|
11590
|
+
body: `<p>Collections help you organise related reviews, such as a refactor, release or feature.</p>
|
|
11591
|
+
<p>Create a collection from the Reviews page, then add reviews individually or select several and
|
|
11592
|
+
use <b>Manage collections</b>. A review can belong to multiple collections.</p>
|
|
11593
|
+
<p>Collections show reviews that still need attention. When you mark a review <b>Done</b>, it moves
|
|
11594
|
+
under <b>Show completed</b> within its collection, without losing its membership.</p>
|
|
11595
|
+
<p>You can collapse collections to stay focused, and expand them when you're ready to continue.
|
|
11596
|
+
Removing a review from all collections returns it to the normal review sections.</p>
|
|
11513
11597
|
<p>Reviews in a collection are kept for ninety days, like pinned and Done reviews, counted from
|
|
11514
11598
|
when you last opened or marked them. Adding one to a collection doesn't restart that count.
|
|
11515
11599
|
Deleting a collection keeps its reviews.</p>`
|
|
@@ -11599,7 +11683,7 @@ function renderContents() {
|
|
|
11599
11683
|
}
|
|
11600
11684
|
function renderHelpPage(view) {
|
|
11601
11685
|
return `<!doctype html>
|
|
11602
|
-
<html lang="en"${themeAttribute(view.theme)}>
|
|
11686
|
+
<html lang="en"${themeAttribute(view.theme)}${sidebarAttributes(view.sidebar)}>
|
|
11603
11687
|
<head>
|
|
11604
11688
|
<meta charset="utf-8">
|
|
11605
11689
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
@@ -11618,7 +11702,8 @@ ${renderContextBar("Help", "")}
|
|
|
11618
11702
|
<div class="prb-shell">
|
|
11619
11703
|
${renderWorkspaceRail({
|
|
11620
11704
|
current: "help",
|
|
11621
|
-
unread: view.unreadNotifications ?? 0
|
|
11705
|
+
unread: view.unreadNotifications ?? 0,
|
|
11706
|
+
sidebar: view.sidebar
|
|
11622
11707
|
})}
|
|
11623
11708
|
<main class="prb-index prb-help">
|
|
11624
11709
|
<div class="prb-page-panel">
|
|
@@ -11640,7 +11725,7 @@ ${renderWorkspaceRail({
|
|
|
11640
11725
|
function renderNotificationsPage(view) {
|
|
11641
11726
|
const unread = view.notifications.filter((notification) => notification.readAt === void 0).length;
|
|
11642
11727
|
return `<!doctype html>
|
|
11643
|
-
<html lang="en"${themeAttribute(view.theme)}>
|
|
11728
|
+
<html lang="en"${themeAttribute(view.theme)}${sidebarAttributes(view.sidebar)}>
|
|
11644
11729
|
<head>
|
|
11645
11730
|
<meta charset="utf-8">
|
|
11646
11731
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
@@ -11659,7 +11744,8 @@ ${renderContextBar("Activity", view.notifications.length === 0 ? "" : renderRead
|
|
|
11659
11744
|
<div class="prb-shell">
|
|
11660
11745
|
${renderWorkspaceRail({
|
|
11661
11746
|
current: "notifications",
|
|
11662
|
-
unread: view.unreadNotifications ?? 0
|
|
11747
|
+
unread: view.unreadNotifications ?? 0,
|
|
11748
|
+
sidebar: view.sidebar
|
|
11663
11749
|
})}
|
|
11664
11750
|
<main class="prb-index prb-notifications-page">
|
|
11665
11751
|
${view.notifications.length === 0 ? `<p class="prb-index-empty" data-notifications-empty>Nothing yet. When a review finishes or fails, or a branch or pull request moves, it appears here.</p>` : `<ol class="prb-notifications" data-notifications>
|
|
@@ -13044,7 +13130,7 @@ function renderRunsPage(view) {
|
|
|
13044
13130
|
const nothing = inProgress.length === 0 && recent.length === 0;
|
|
13045
13131
|
const notResponding = recent.some((entry) => entry.status === "not-responding");
|
|
13046
13132
|
return `<!doctype html>
|
|
13047
|
-
<html lang="en"${themeAttribute(view.theme)}>
|
|
13133
|
+
<html lang="en"${themeAttribute(view.theme)}${sidebarAttributes(view.sidebar)}>
|
|
13048
13134
|
<head>
|
|
13049
13135
|
<meta charset="utf-8">
|
|
13050
13136
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
@@ -13063,7 +13149,8 @@ ${renderContextBar("Runs", nothing ? "" : renderControls(notResponding))}
|
|
|
13063
13149
|
<div class="prb-shell">
|
|
13064
13150
|
${renderWorkspaceRail({
|
|
13065
13151
|
current: "runs",
|
|
13066
|
-
unread: view.unreadNotifications ?? 0
|
|
13152
|
+
unread: view.unreadNotifications ?? 0,
|
|
13153
|
+
sidebar: view.sidebar
|
|
13067
13154
|
})}
|
|
13068
13155
|
<main class="prb-index prb-runs">
|
|
13069
13156
|
<p class="prb-runs-lede">Monitor review executions and recent activity.</p>
|
|
@@ -14999,6 +15086,7 @@ async function handle(req, res, options) {
|
|
|
14999
15086
|
if (url.pathname === "/notifications/live") return handleLiveNotifications(req, res, url.searchParams, options.indexToken);
|
|
15000
15087
|
if (url.pathname === "/pin") return handlePin(req, res, options.indexToken);
|
|
15001
15088
|
if (url.pathname === "/sections") return handleSections(req, res, options.indexToken);
|
|
15089
|
+
if (url.pathname === "/sidebar") return handleSidebar(req, res, options.indexToken);
|
|
15002
15090
|
if (url.pathname === "/queue") return handleQueue(req, res, options.indexToken);
|
|
15003
15091
|
if (url.pathname === "/collections") return handleCollections(req, res, options.indexToken);
|
|
15004
15092
|
if (url.pathname === "/collections/members") return handleCollectionMembers(req, res, options.indexToken);
|
|
@@ -15148,6 +15236,7 @@ function indexView() {
|
|
|
15148
15236
|
unreadNotifications: unreadCount$1(),
|
|
15149
15237
|
driving: isDriving(),
|
|
15150
15238
|
theme: readPreferences().theme,
|
|
15239
|
+
sidebar: readPreferences().sidebar,
|
|
15151
15240
|
sectionsCollapsed: readPreferences().reviewSections
|
|
15152
15241
|
};
|
|
15153
15242
|
}
|
|
@@ -15238,7 +15327,8 @@ async function handleSettings(req, res, params, indexToken) {
|
|
|
15238
15327
|
sendHtml(res, 200, renderSettingsPage({
|
|
15239
15328
|
settings: readSettings(),
|
|
15240
15329
|
unreadNotifications: unreadCount$1(),
|
|
15241
|
-
driving: isDriving()
|
|
15330
|
+
driving: isDriving(),
|
|
15331
|
+
sidebar: readPreferences().sidebar
|
|
15242
15332
|
}), indexCookieHeader(indexToken));
|
|
15243
15333
|
}
|
|
15244
15334
|
/**
|
|
@@ -15250,7 +15340,8 @@ function handleHelp(req, res, params, indexToken) {
|
|
|
15250
15340
|
sendHtml(res, 200, renderHelpPage({
|
|
15251
15341
|
unreadNotifications: unreadCount$1(),
|
|
15252
15342
|
driving: isDriving(),
|
|
15253
|
-
theme: readPreferences().theme
|
|
15343
|
+
theme: readPreferences().theme,
|
|
15344
|
+
sidebar: readPreferences().sidebar
|
|
15254
15345
|
}), indexCookieHeader(indexToken));
|
|
15255
15346
|
}
|
|
15256
15347
|
/**
|
|
@@ -15365,7 +15456,8 @@ async function handleNotifications(req, res, params, indexToken) {
|
|
|
15365
15456
|
gone: new Set(notifications.filter((entry) => !loadWorkspace(entry.reviewId)).map((entry) => entry.id)),
|
|
15366
15457
|
unreadNotifications: unreadCount$1(),
|
|
15367
15458
|
driving: isDriving(),
|
|
15368
|
-
theme: readPreferences().theme
|
|
15459
|
+
theme: readPreferences().theme,
|
|
15460
|
+
sidebar: readPreferences().sidebar
|
|
15369
15461
|
}), indexCookieHeader(indexToken));
|
|
15370
15462
|
}
|
|
15371
15463
|
/**
|
|
@@ -15386,6 +15478,7 @@ function handleRuns(req, res, params, indexToken) {
|
|
|
15386
15478
|
unreadNotifications: unreadCount$1(),
|
|
15387
15479
|
driving: isDriving(),
|
|
15388
15480
|
theme: preferences.theme,
|
|
15481
|
+
sidebar: preferences.sidebar,
|
|
15389
15482
|
sectionsCollapsed: preferences.runsSections
|
|
15390
15483
|
}), indexCookieHeader(indexToken));
|
|
15391
15484
|
}
|
|
@@ -15799,6 +15892,18 @@ async function handleRunsSections(req, res, indexToken) {
|
|
|
15799
15892
|
writeRunsSectionCollapsed(body.section, body.collapsed);
|
|
15800
15893
|
send(res, 200, { ok: true });
|
|
15801
15894
|
}
|
|
15895
|
+
/**
|
|
15896
|
+
* The sidebar, from a workspace page that is not a review, with the same credential as `/sections`.
|
|
15897
|
+
* A review page sends the same field to its own `/prefs`, since it may hold no index cookie.
|
|
15898
|
+
*/
|
|
15899
|
+
async function handleSidebar(req, res, indexToken) {
|
|
15900
|
+
if (req.method !== "POST") return send(res, 405, { error: "Use POST." });
|
|
15901
|
+
if (!indexToken || !secretMatches(indexToken, indexCookie(req))) return send(res, 403, { error: "Not authorised." });
|
|
15902
|
+
const sidebar = readSidebarPatch((await readJson(req))?.sidebar);
|
|
15903
|
+
if (!sidebar) return send(res, 400, { error: "Not a sidebar setting." });
|
|
15904
|
+
writeSidebar(sidebar);
|
|
15905
|
+
send(res, 200, { ok: true });
|
|
15906
|
+
}
|
|
15802
15907
|
async function handleSections(req, res, indexToken) {
|
|
15803
15908
|
if (req.method !== "POST") return send(res, 405, { error: "Use POST." });
|
|
15804
15909
|
if (!indexToken || !secretMatches(indexToken, indexCookie(req))) return send(res, 403, { error: "Not authorised." });
|
|
@@ -16205,6 +16310,7 @@ async function sendWorkspacePage(res, workspace) {
|
|
|
16205
16310
|
repoPath: workspace.repoPath,
|
|
16206
16311
|
askWidth: readPreferences().askWidth,
|
|
16207
16312
|
theme: readPreferences().theme,
|
|
16313
|
+
sidebar: readPreferences().sidebar,
|
|
16208
16314
|
dismissedNotices: readPreferences().dismissedNotices,
|
|
16209
16315
|
promptsCollapsed: readPreferences().promptsCollapsed === true,
|
|
16210
16316
|
reviewLayout: readPreferences().reviewLayout ?? {},
|
|
@@ -17008,6 +17114,8 @@ async function handlePrefs(req, res) {
|
|
|
17008
17114
|
if (typeof askWidth === "number") writeAskWidth(askWidth);
|
|
17009
17115
|
else if (askWidth === null) clearAskWidth();
|
|
17010
17116
|
if (typeof body?.promptsCollapsed === "boolean") writePromptsCollapsed(body.promptsCollapsed);
|
|
17117
|
+
const sidebar = readSidebarPatch(body?.sidebar);
|
|
17118
|
+
if (sidebar) writeSidebar(sidebar);
|
|
17011
17119
|
const layout = readReviewLayoutPatch(body?.reviewLayout);
|
|
17012
17120
|
if (layout) writeReviewLayout(layout);
|
|
17013
17121
|
if (body?.changeMapRegister === "simple" || body?.changeMapRegister === "technical") writeChangeMapRegister(body.changeMapRegister);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prreviewbuddy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.83.3",
|
|
4
4
|
"description": "Understand code changes before you approve them. Local branch and pull request reviews with Claude Code or Codex.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"homepage": "https://prreviewbuddy.com",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{dt as e}from"./chunk-
|
|
1
|
+
import{dt as e}from"./chunk-DwdwtB9r.js";var t=`button:not([disabled])`;function n(e){return e.querySelector(`[data-menu-panel]`)}function r(e){return e.querySelector(`[data-menu-trigger]`)}function i(e){let t=n(e);return t?[...t.querySelectorAll(`input:not([disabled]), textarea:not([disabled]), button:not([disabled])`)].filter(e=>!e.closest(`[hidden]`)):[]}function a(e){let r=n(e);return r?[...r.querySelectorAll(t)].filter(e=>!e.closest(`[hidden]`)):[]}var o=`data-menu-placement`;function s(e){let t=e.ownerDocument.defaultView,n=0,r=t?.innerHeight??1/0;for(let i=e.parentElement;i&&t;i=i.parentElement){let e=t.getComputedStyle(i).overflowY;if(e===`visible`||e===``)continue;let a=i.getBoundingClientRect();n=Math.max(n,a.top),r=Math.min(r,a.bottom)}return{top:n,bottom:r}}var c=8,l=120;function u(e){let t=n(e),i=r(e);if(!t||!i)return;t.removeAttribute(o),t.style.removeProperty(`max-height`),t.style.removeProperty(`overflow-y`),t.style.removeProperty(`box-sizing`),t.scrollTop=0;let a=t.getBoundingClientRect(),u=i.getBoundingClientRect(),d=s(e),f=Math.max(0,a.top-u.bottom),p=d.bottom-u.bottom-f-c,m=u.top-d.top-f-c,h=a.height,g=h<=p?`below`:h<=m||m>p?`above`:`below`;g===`above`&&t.setAttribute(o,`above`);let _=g===`above`?m:p;h<=_||(t.style.boxSizing=`border-box`,t.style.maxHeight=`${Math.max(Math.floor(_),l)}px`,t.style.overflowY=`auto`)}function d(e){return!e.metaKey&&!e.ctrlKey&&!e.altKey&&!e.shiftKey?e.key===`j`?`ArrowDown`:e.key===`k`?`ArrowUp`:e.key:e.key}function f(t,o={}){let s=null,c=null,l=o.within?`${o.within} [data-menu]`:`[data-menu]`,f=e=>e&&(!o.within||e.closest(o.within))?e:null,p=(e,t={})=>{let i=n(e);if(!i||i.hidden)return;i.hidden=!0;let a=r(e);a?.setAttribute(`aria-expanded`,`false`);let l=s===e?c:null;s===e&&(s=null,c=null),o.onClose?.(e),t.returnFocus&&(l??(()=>a?.focus()))()},m=()=>{for(let e of t.querySelectorAll(l))p(e)},h=(e,t,i=null)=>{let l=n(e);l&&(m(),c=i,o.onOpen?.(e),l.hidden=!1,u(e),r(e)?.setAttribute(`aria-expanded`,`true`),s=e,t&&a(e)[0]?.focus())};return t.addEventListener(`click`,e=>{let t=e.target,r=f(t?.closest?.(`[data-menu]`)??null);if(!r){m();return}if(t?.closest(`[data-menu-trigger]`)){let t=e.detail===0;n(r)?.hidden?h(r,t):p(r);return}s&&s!==r&&p(s)}),t.addEventListener(`keydown`,t=>{if(s&&!s.isConnected&&(s=null),!s)return;let n=s;if(t.key===`Escape`){t.preventDefault(),p(n,{returnFocus:!0});return}if(!n.contains(t.target))return;if(t.key===`Tab`){if(n.hasAttribute(`data-menu-form`)){let e=i(n),r=e.indexOf(t.target);if(e.length>0){t.preventDefault(),e[(r+(t.shiftKey?-1:1)+e.length)%e.length].focus();return}}p(n);return}if(e(t.target))return;let r=a(n);if(r.length===0)return;let o=r.indexOf(t.target),c=d(t),l=c===`ArrowDown`?r[(o+1)%r.length]:c===`ArrowUp`?r[(o-1+r.length)%r.length]:c===`Home`?r[0]:c===`End`?r[r.length-1]:null;l&&(t.preventDefault(),l.focus(),l.scrollIntoView({block:`nearest`}))}),{open:(e,t={})=>h(e,t.focusFirst===!0,t.returnFocus??null),close:p,closeAll:m}}function p(e){return e<3e4?1e3:e<12e4?3e3:e<6e5?8e3:15e3}export{f as n,p as t};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{F as e}from"./chunk-
|
|
1
|
+
import{F as e}from"./chunk-DwdwtB9r.js";function t(e=document){for(let t of e.querySelectorAll(`.prb-index-status`))t.addEventListener(`click`,e=>{let n=t.querySelector(`select`);if(!(!n||e.target instanceof Node&&n.contains(e.target))){e.preventDefault(),n.focus();try{n.showPicker()}catch{}}})}function n(t,n){r(t,t=>e(t.dataset.section)?t.dataset.section:void 0,n)}function r(e,t,n){let r=Promise.resolve();for(let i of e){let e=t(i);if(e===void 0)continue;let a=i.open;i.addEventListener(`toggle`,()=>{if(i.open===a)return;a=i.open;let t=!i.open;r=r.then(()=>n(e,t)).catch(()=>{})})}}export{n,t as r,r as t};
|