prreviewbuddy 0.12.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/README.md +61 -0
- package/dist/log-DdPaO6Wo.js +6640 -0
- package/dist/main.js +873 -0
- package/dist/server.js +3849 -0
- package/package.json +27 -0
- package/static/comfortaa-700.woff2 +0 -0
- package/static/icon128.png +0 -0
- package/static/reviews.js +1 -0
- package/static/shell.css +2018 -0
- package/static/sidebar.css +4802 -0
- package/static/workspace.js +0 -0
|
@@ -0,0 +1,4802 @@
|
|
|
1
|
+
/* The review's own styles, dressing the markup in domain/ui/components/review_tabs.ts. It lives
|
|
2
|
+
here rather than in the extension because the markup does: both surfaces render the same review,
|
|
3
|
+
and a sheet that only one of them owned would drift the moment the other changed a class name.
|
|
4
|
+
Both ship a copy, and the two builds copy it out of this directory.
|
|
5
|
+
|
|
6
|
+
Everything below is written for the harder of the two surfaces. This sheet is injected into
|
|
7
|
+
github.com through the extension's manifest.json content_scripts.css, so it is
|
|
8
|
+
extension-origin CSS and url() below resolves against the extension root, not GitHub's.
|
|
9
|
+
Remote @import is not an option here: GitHub sends `style-src 'unsafe-inline'
|
|
10
|
+
github.githubassets.com` and `font-src github.githubassets.com`, so a Google Fonts @import is
|
|
11
|
+
dropped and even a data: URI font would be refused. The font has to ship with us.
|
|
12
|
+
|
|
13
|
+
(A Space Mono @import used to sit here and was blocked for exactly that reason. Nothing was
|
|
14
|
+
depending on it: the only rule that named Space Mono was the wordmark, now Comfortaa, and every
|
|
15
|
+
mono rule below asks for the ui-monospace stack outright. There is no font left to bundle.)
|
|
16
|
+
|
|
17
|
+
There is deliberately no @font-face here. A relative url() would resolve against github.com and
|
|
18
|
+
be refused by `font-src`, so content/brand_font.ts registers the family from the isolated world
|
|
19
|
+
instead, where the fetch is ours rather than the page's. */
|
|
20
|
+
|
|
21
|
+
/* ── Severity pill palette ──
|
|
22
|
+
The single source for every graded pill in the sidebar: Issues' severity badges and the
|
|
23
|
+
Questions tab's importance badges. Both scales express "how much should this worry me", so
|
|
24
|
+
a MEDIUM question and a MAJOR issue must be the same amber — when these were two hand-copied
|
|
25
|
+
sets of hex values they had already drifted apart by an alpha step or two, which is exactly
|
|
26
|
+
the kind of difference that reads as "these tabs are from different products". Questions map
|
|
27
|
+
onto three of the four: high→danger, medium→warning, low→neutral (info is Issues-only, for
|
|
28
|
+
the extra "minor" rung a three-level scale doesn't have). */
|
|
29
|
+
.pr-helper-sidebar {
|
|
30
|
+
--pr-helper-pill-danger-bg: rgba(255, 80, 80, 0.15);
|
|
31
|
+
--pr-helper-pill-danger-fg: #cf222e;
|
|
32
|
+
--pr-helper-pill-warning-bg: rgba(240, 178, 50, 0.18);
|
|
33
|
+
--pr-helper-pill-warning-fg: #9a6700;
|
|
34
|
+
--pr-helper-pill-info-bg: rgba(84, 174, 255, 0.15);
|
|
35
|
+
--pr-helper-pill-info-fg: #0969da;
|
|
36
|
+
--pr-helper-pill-neutral-bg: rgba(175, 184, 193, 0.2);
|
|
37
|
+
--pr-helper-pill-neutral-fg: #57606a;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* ── Sidebar shell ── */
|
|
41
|
+
.pr-helper-sidebar {
|
|
42
|
+
position: fixed;
|
|
43
|
+
top: 0;
|
|
44
|
+
right: 0;
|
|
45
|
+
width: 460px;
|
|
46
|
+
min-width: 380px;
|
|
47
|
+
max-width: 650px;
|
|
48
|
+
height: 100vh;
|
|
49
|
+
background: #f8fafc;
|
|
50
|
+
/* The inset purple edge is the rail, carried into the panel: it is the LEADING edge as the
|
|
51
|
+
sidebar slides out, so the panel reads as being drawn from the button rather than arriving
|
|
52
|
+
over the top of it. An inset shadow rather than a wider border-left on purpose — this box is
|
|
53
|
+
content-box with an explicit pixel width, so thickening the border would shift every column
|
|
54
|
+
inside it by 3px. */
|
|
55
|
+
box-shadow: inset 4px 0 0 #9333ea, -3px 0 16px rgba(0, 0, 0, 0.18);
|
|
56
|
+
border-left: 1px solid rgba(0, 0, 0, 0.08);
|
|
57
|
+
z-index: 999999;
|
|
58
|
+
transform: translateX(100%);
|
|
59
|
+
transition: transform 0.3s ease;
|
|
60
|
+
display: flex;
|
|
61
|
+
flex-direction: column;
|
|
62
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.pr-helper-sidebar.open { transform: translateX(0); }
|
|
66
|
+
|
|
67
|
+
/* ── Resize handle ── */
|
|
68
|
+
.pr-helper-resize-handle {
|
|
69
|
+
position: absolute;
|
|
70
|
+
left: -3px;
|
|
71
|
+
top: 0;
|
|
72
|
+
bottom: 0;
|
|
73
|
+
width: 6px;
|
|
74
|
+
cursor: ew-resize;
|
|
75
|
+
z-index: 10;
|
|
76
|
+
background: transparent;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.pr-helper-resize-handle:hover { background: rgba(147, 51, 234, 0.25); }
|
|
80
|
+
|
|
81
|
+
/* ── Generating checklist (rendered inside the Overview tab) ── */
|
|
82
|
+
/* Deliberately not a card: a bordered panel floating in an otherwise empty tab makes the
|
|
83
|
+
whitespace around it the loudest thing on screen. Instead this claims the whole content
|
|
84
|
+
area — same full-bleed treatment as the "No review yet" intro it replaces — so the tab
|
|
85
|
+
itself reads as working. The body cluster centres in the free space via `margin: auto 0`
|
|
86
|
+
(not justify-content, which would fight the footer) and the footer settles at the
|
|
87
|
+
bottom, keeping Cancel in one predictable place through every generating phase. */
|
|
88
|
+
.pr-helper-generating {
|
|
89
|
+
min-height: 100%;
|
|
90
|
+
box-sizing: border-box;
|
|
91
|
+
display: flex;
|
|
92
|
+
flex-direction: column;
|
|
93
|
+
padding: 24px 8px 8px;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Block flow inside, not a nested flex column — the children (checklist, phrase stack,
|
|
97
|
+
progress bar) already rely on normal block sizing and auto margins to centre. */
|
|
98
|
+
.pr-helper-generating-body {
|
|
99
|
+
margin: auto 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.pr-helper-generating-footer {
|
|
103
|
+
flex-shrink: 0;
|
|
104
|
+
padding-top: 16px;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.pr-helper-generating-title {
|
|
108
|
+
font-size: 12px;
|
|
109
|
+
font-weight: 700;
|
|
110
|
+
color: #9333ea;
|
|
111
|
+
margin: 0 0 16px;
|
|
112
|
+
text-transform: uppercase;
|
|
113
|
+
letter-spacing: 0.06em;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
@keyframes pr-helper-pulse {
|
|
117
|
+
0%, 100% { opacity: 0.35; transform: scale(0.8); }
|
|
118
|
+
50% { opacity: 1; transform: scale(1.15); }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* Thin and treated as a decorative divider, not a primary UI element — in the waiting
|
|
122
|
+
phase it has no real percentage to show (see the shimmer variant below), so giving it
|
|
123
|
+
heavy visual weight would overstate how much it's actually communicating. */
|
|
124
|
+
.pr-helper-progress-bar {
|
|
125
|
+
position: relative;
|
|
126
|
+
height: 3px;
|
|
127
|
+
margin-bottom: 20px;
|
|
128
|
+
background: rgba(147, 51, 234, 0.12);
|
|
129
|
+
border-radius: 3px;
|
|
130
|
+
overflow: hidden;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.pr-helper-progress-bar-fill {
|
|
134
|
+
height: 100%;
|
|
135
|
+
background: linear-gradient(90deg, #9333ea, #c084fc);
|
|
136
|
+
width: 0%;
|
|
137
|
+
transition: width 0.4s ease;
|
|
138
|
+
animation: pr-helper-bar-pulse 1.8s ease-in-out infinite;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
@keyframes pr-helper-bar-pulse {
|
|
142
|
+
0%, 100% { opacity: 1; }
|
|
143
|
+
50% { opacity: 0.55; }
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* Waiting-phase progress bar: no meaningful percentage to show once the LLM call is
|
|
147
|
+
in flight, so a highlight sweeps across the full track on a loop instead of implying
|
|
148
|
+
real completion — reads as "data is moving" without a fabricated number behind it. */
|
|
149
|
+
.pr-helper-progress-bar-shimmer::after {
|
|
150
|
+
content: '';
|
|
151
|
+
position: absolute;
|
|
152
|
+
top: 0;
|
|
153
|
+
left: 0;
|
|
154
|
+
height: 100%;
|
|
155
|
+
width: 40%;
|
|
156
|
+
background: linear-gradient(90deg, transparent, #c084fc, transparent);
|
|
157
|
+
animation: pr-helper-shimmer-sweep 1.6s ease-in-out infinite;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
@keyframes pr-helper-shimmer-sweep {
|
|
161
|
+
0% { transform: translateX(-100%); }
|
|
162
|
+
100% { transform: translateX(250%); }
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* Spacing between this footer cluster (elapsed → hint → powered-by) is carried entirely
|
|
166
|
+
by each element's own margin-bottom, deliberately never margin-top on the element
|
|
167
|
+
after — adjacent margins collapse to their max rather than summing, so relying on the
|
|
168
|
+
"next" element's top margin instead of the "previous" element's bottom margin was
|
|
169
|
+
quietly shrinking these gaps down to whichever was smaller (that's what made "Powered
|
|
170
|
+
by" read as glued to the bar above it). */
|
|
171
|
+
.pr-helper-generating-elapsed {
|
|
172
|
+
margin: 0 0 4px;
|
|
173
|
+
font-size: 11px;
|
|
174
|
+
color: #9333ea;
|
|
175
|
+
font-weight: 600;
|
|
176
|
+
font-variant-numeric: tabular-nums;
|
|
177
|
+
text-align: center;
|
|
178
|
+
opacity: 0;
|
|
179
|
+
transition: opacity 0.6s ease;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.pr-helper-generating-elapsed.visible {
|
|
183
|
+
opacity: 1;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.pr-helper-generating-powered-by {
|
|
187
|
+
margin: 0;
|
|
188
|
+
font-size: 10px;
|
|
189
|
+
color: var(--color-fg-muted, #57606a);
|
|
190
|
+
opacity: 0.7;
|
|
191
|
+
text-align: center;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/* A hint line reserved for the same "past 2 minutes" moment as the direct headline —
|
|
195
|
+
hidden until content.ts's timer reveals it, never a separate banner of its own. */
|
|
196
|
+
.pr-helper-escalation-hint {
|
|
197
|
+
font-size: 11px;
|
|
198
|
+
color: var(--color-fg-muted, #57606a);
|
|
199
|
+
margin: 0 0 4px;
|
|
200
|
+
text-align: center;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* Cancel is deliberately understated — a plain text button, not a bordered/filled
|
|
204
|
+
action — and identically placed across every generating state (see CANCEL_BUTTON in
|
|
205
|
+
review_tabs.ts) so it never reads as a new control appearing mid-wait. Centered via
|
|
206
|
+
auto margins on a block button so it's consistent whether the parent card is
|
|
207
|
+
left-aligned (the connecting checklist) or centered (the focused waiting view). */
|
|
208
|
+
.pr-helper-cancel-button {
|
|
209
|
+
display: block;
|
|
210
|
+
margin: 10px auto 0;
|
|
211
|
+
font-size: 11px;
|
|
212
|
+
font-weight: 600;
|
|
213
|
+
font-family: inherit;
|
|
214
|
+
padding: 2px 4px;
|
|
215
|
+
border: none;
|
|
216
|
+
background: transparent;
|
|
217
|
+
color: var(--color-fg-muted, #57606a);
|
|
218
|
+
cursor: pointer;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.pr-helper-cancel-button:hover { color: #cf222e; text-decoration: underline; }
|
|
222
|
+
|
|
223
|
+
/* ── Brand mark + thinking dots (waiting/finalizing phase) ── */
|
|
224
|
+
.pr-helper-brand-mark {
|
|
225
|
+
display: block;
|
|
226
|
+
width: 24px;
|
|
227
|
+
height: 24px;
|
|
228
|
+
margin: 0 auto 20px;
|
|
229
|
+
border-radius: 7px;
|
|
230
|
+
animation: pr-helper-pulse 2s ease-in-out infinite;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.pr-helper-generating-focused {
|
|
234
|
+
text-align: center;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/* Scaled up from the old card-sized type: at full-bleed the mark and headline are the
|
|
238
|
+
only things in the viewport, so 12px uppercase micro-copy read as a widget label
|
|
239
|
+
stranded in whitespace. Sentence case at 15px reads as the page speaking. */
|
|
240
|
+
.pr-helper-generating-focused .pr-helper-brand-mark {
|
|
241
|
+
width: 40px;
|
|
242
|
+
height: 40px;
|
|
243
|
+
border-radius: 11px;
|
|
244
|
+
margin-bottom: 22px;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.pr-helper-generating-focused .pr-helper-generating-title {
|
|
248
|
+
font-size: 15px;
|
|
249
|
+
font-weight: 700;
|
|
250
|
+
text-transform: none;
|
|
251
|
+
letter-spacing: -0.01em;
|
|
252
|
+
margin-bottom: 14px;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/* Held well short of the panel edge so the eye stays on the centred cluster rather than
|
|
256
|
+
tracking a full-width rule across the sidebar. */
|
|
257
|
+
.pr-helper-generating-focused .pr-helper-progress-bar {
|
|
258
|
+
max-width: 220px;
|
|
259
|
+
margin: 0 auto 20px;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/* Ambient rotating phrases: all six are stacked in the same box and cross-fade on a
|
|
263
|
+
loop via animation-delay offsets, rather than any JS timer swapping text — keeps the
|
|
264
|
+
whole thing driven by the same one-off render as everything else in this card. */
|
|
265
|
+
.pr-helper-thinking-phrases {
|
|
266
|
+
position: relative;
|
|
267
|
+
height: 22px;
|
|
268
|
+
margin: 0 0 14px;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.pr-helper-thinking-phrase {
|
|
272
|
+
position: absolute;
|
|
273
|
+
inset: 0;
|
|
274
|
+
margin: 0;
|
|
275
|
+
font-size: 15px;
|
|
276
|
+
letter-spacing: -0.01em;
|
|
277
|
+
font-weight: 700;
|
|
278
|
+
color: #9333ea;
|
|
279
|
+
opacity: 0;
|
|
280
|
+
animation: pr-helper-thinking-phrase-cycle 15s ease-in-out infinite;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/* Windows are cut to the phrase count: 4 phrases share a 15s cycle, so each owns 25% of
|
|
284
|
+
the timeline and stays visible for ~its whole slot (3%–23%) with a short cross-fade at
|
|
285
|
+
each edge. Adding or removing a phrase in THINKING_PHRASES means re-cutting these
|
|
286
|
+
percentages, or the stack develops dead gaps where nothing is on screen. */
|
|
287
|
+
@keyframes pr-helper-thinking-phrase-cycle {
|
|
288
|
+
0% { opacity: 0; }
|
|
289
|
+
3% { opacity: 1; }
|
|
290
|
+
23% { opacity: 1; }
|
|
291
|
+
27% { opacity: 0; }
|
|
292
|
+
100% { opacity: 0; }
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.pr-helper-thinking-dots {
|
|
296
|
+
display: flex;
|
|
297
|
+
justify-content: center;
|
|
298
|
+
gap: 6px;
|
|
299
|
+
margin: 0 0 22px;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.pr-helper-thinking-dots span {
|
|
303
|
+
width: 7px;
|
|
304
|
+
height: 7px;
|
|
305
|
+
border-radius: 50%;
|
|
306
|
+
background: #9333ea;
|
|
307
|
+
animation: pr-helper-thinking-bounce 1.1s ease-in-out infinite;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.pr-helper-thinking-dots span:nth-child(2) { animation-delay: 0.15s; }
|
|
311
|
+
.pr-helper-thinking-dots span:nth-child(3) { animation-delay: 0.3s; }
|
|
312
|
+
|
|
313
|
+
@keyframes pr-helper-thinking-bounce {
|
|
314
|
+
0%, 60%, 100% { opacity: 0.3; transform: translateY(0); }
|
|
315
|
+
30% { opacity: 1; transform: translateY(-4px); }
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/* ── Generation error (rendered inside the Overview tab) ── */
|
|
319
|
+
.pr-helper-generating-error {
|
|
320
|
+
flex-shrink: 0;
|
|
321
|
+
padding: 18px 20px;
|
|
322
|
+
background: rgba(255, 80, 80, 0.05);
|
|
323
|
+
border: 1px solid rgba(255, 80, 80, 0.2);
|
|
324
|
+
border-radius: 10px;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.pr-helper-generating-error-text {
|
|
328
|
+
font-size: 13px;
|
|
329
|
+
color: #cf222e;
|
|
330
|
+
margin: 0 0 14px;
|
|
331
|
+
line-height: 1.5;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.pr-helper-retry-button {
|
|
335
|
+
font-size: 11px;
|
|
336
|
+
font-weight: 600;
|
|
337
|
+
font-family: inherit;
|
|
338
|
+
padding: 6px 14px;
|
|
339
|
+
border-radius: 6px;
|
|
340
|
+
border: 1px solid #cf222e;
|
|
341
|
+
background: transparent;
|
|
342
|
+
color: #cf222e;
|
|
343
|
+
cursor: pointer;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.pr-helper-retry-button:hover { background: rgba(255, 80, 80, 0.08); }
|
|
347
|
+
|
|
348
|
+
/* ── Review tabs ── */
|
|
349
|
+
.pr-helper-review-tabs {
|
|
350
|
+
display: flex;
|
|
351
|
+
flex-wrap: wrap;
|
|
352
|
+
gap: 2px;
|
|
353
|
+
padding: 10px 10px 0;
|
|
354
|
+
border-bottom: 1px solid var(--color-border-default, #d0d7de);
|
|
355
|
+
background: #f8fafc;
|
|
356
|
+
flex-shrink: 0;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/* Sized and weighted to read as navigation, not content — one step down from the section
|
|
360
|
+
titles/body text below it so the tab strip doesn't out-compete the thing it's routing to. */
|
|
361
|
+
.pr-helper-review-tab {
|
|
362
|
+
font-size: 13px;
|
|
363
|
+
font-weight: 500;
|
|
364
|
+
font-family: inherit;
|
|
365
|
+
padding: 9px 12px;
|
|
366
|
+
border: none;
|
|
367
|
+
background: transparent;
|
|
368
|
+
border-radius: 6px 6px 0 0;
|
|
369
|
+
cursor: pointer;
|
|
370
|
+
color: var(--color-fg-muted, #57606a);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.pr-helper-review-tab.active {
|
|
374
|
+
background: #ffffff;
|
|
375
|
+
color: #9333ea;
|
|
376
|
+
font-weight: 600;
|
|
377
|
+
box-shadow: inset 0 -2px 0 #9333ea;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/* Locked until a review exists. Faded rather than hidden so the reviewer can see what
|
|
381
|
+
they're about to unlock, and not-allowed on hover pairs with the title tooltip that
|
|
382
|
+
says why. Kept clickable at the DOM level (aria-disabled, not disabled) — see
|
|
383
|
+
renderReviewTabsNav — so that tooltip actually fires. */
|
|
384
|
+
.pr-helper-review-tab-locked {
|
|
385
|
+
opacity: 0.4;
|
|
386
|
+
cursor: not-allowed;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.pr-helper-review-tab-locked:hover {
|
|
390
|
+
background: transparent;
|
|
391
|
+
color: var(--color-fg-muted, #57606a);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.pr-helper-tab-divider {
|
|
395
|
+
width: 1px;
|
|
396
|
+
align-self: stretch;
|
|
397
|
+
margin: 4px 4px;
|
|
398
|
+
background: var(--color-border-default, #d0d7de);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/*
|
|
402
|
+
* The content canvas.
|
|
403
|
+
*
|
|
404
|
+
* Everything in the sidebar used to sit on the same white, so a card with a hairline border was
|
|
405
|
+
* the only thing separating content from chrome — and on the Assistant tab, where there is one
|
|
406
|
+
* message and no cards at all, nothing separated them. Recessing the scroll area half a step gives
|
|
407
|
+
* every tab the same figure/ground relationship without enclosing anything in a box.
|
|
408
|
+
*
|
|
409
|
+
* #f8fafc rather than a new value: it is what the footer already uses, so the two recessed
|
|
410
|
+
* surfaces at the top and bottom of the sidebar are the same colour rather than two near-identical
|
|
411
|
+
* greys. Cards, inputs and the toolbar stay white and now read as raised against it.
|
|
412
|
+
*/
|
|
413
|
+
.pr-helper-review-panel {
|
|
414
|
+
flex: 1;
|
|
415
|
+
min-height: 0;
|
|
416
|
+
overflow-y: auto;
|
|
417
|
+
padding: 14px 16px;
|
|
418
|
+
background: #f8fafc;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/*
|
|
422
|
+
* The strip between the tab bar and the canvas: which analysis is on screen, and whether it is
|
|
423
|
+
* still current. White, so it belongs to the chrome above rather than the content below, and
|
|
424
|
+
* closed with a divider so the canvas visibly starts underneath it.
|
|
425
|
+
*
|
|
426
|
+
* Collapses entirely when empty — on History, and before any analysis exists — so no divider or
|
|
427
|
+
* padding is left floating with nothing above it.
|
|
428
|
+
*/
|
|
429
|
+
.pr-helper-analysis-toolbar {
|
|
430
|
+
flex-shrink: 0;
|
|
431
|
+
padding: 10px 16px;
|
|
432
|
+
background: #ffffff;
|
|
433
|
+
border-bottom: 1px solid var(--color-border-default, #d0d7de);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.pr-helper-analysis-toolbar:empty {
|
|
437
|
+
display: none;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/* The toolbar owns the spacing now, so the block inside it sheds the margin it needed as content. */
|
|
441
|
+
.pr-helper-analysis-toolbar .pr-helper-analysis-provenance {
|
|
442
|
+
margin: 0;
|
|
443
|
+
padding: 0;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/* The banner keeps its box but sits inside the toolbar's padding rather than the panel's. */
|
|
447
|
+
.pr-helper-analysis-toolbar .pr-helper-update-notice {
|
|
448
|
+
margin: 10px 0 0;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.pr-helper-review-empty {
|
|
452
|
+
font-size: 13px;
|
|
453
|
+
color: var(--color-fg-muted, #57606a);
|
|
454
|
+
padding: 12px 0;
|
|
455
|
+
margin: 0;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/* ── No-review empty state ── */
|
|
459
|
+
/* Centred in the panel's full height rather than sitting at the top like a stray
|
|
460
|
+
paragraph — the vertical centring is what makes this read as an intentional screen
|
|
461
|
+
instead of an unfinished one. min-height (not height) so it still scrolls rather than
|
|
462
|
+
clipping in a short window. */
|
|
463
|
+
.pr-helper-review-intro {
|
|
464
|
+
min-height: 100%;
|
|
465
|
+
display: flex;
|
|
466
|
+
flex-direction: column;
|
|
467
|
+
align-items: center;
|
|
468
|
+
justify-content: center;
|
|
469
|
+
text-align: center;
|
|
470
|
+
padding: 24px 8px;
|
|
471
|
+
box-sizing: border-box;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.pr-helper-intro-mark {
|
|
475
|
+
width: 40px;
|
|
476
|
+
height: 40px;
|
|
477
|
+
border-radius: 10px;
|
|
478
|
+
margin-bottom: 12px;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
.pr-helper-intro-brand {
|
|
482
|
+
font-size: 10px;
|
|
483
|
+
font-weight: 700;
|
|
484
|
+
letter-spacing: 0.08em;
|
|
485
|
+
text-transform: uppercase;
|
|
486
|
+
color: #9333ea;
|
|
487
|
+
margin: 0 0 10px;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.pr-helper-intro-title {
|
|
491
|
+
font-size: 15px;
|
|
492
|
+
font-weight: 700;
|
|
493
|
+
color: var(--color-fg-default, #1f2328);
|
|
494
|
+
margin: 0 0 8px;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
.pr-helper-intro-body {
|
|
498
|
+
font-size: 13px;
|
|
499
|
+
line-height: 1.55;
|
|
500
|
+
color: var(--color-fg-muted, #57606a);
|
|
501
|
+
margin: 0 0 18px;
|
|
502
|
+
max-width: 280px;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.pr-helper-generate-review-button {
|
|
506
|
+
font-size: 13px;
|
|
507
|
+
font-weight: 600;
|
|
508
|
+
font-family: inherit;
|
|
509
|
+
padding: 9px 20px;
|
|
510
|
+
border: none;
|
|
511
|
+
border-radius: 7px;
|
|
512
|
+
background: #9333ea;
|
|
513
|
+
color: #ffffff;
|
|
514
|
+
cursor: pointer;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.pr-helper-generate-review-button:hover { background: #7c3aed; }
|
|
518
|
+
|
|
519
|
+
/* ── Overview tab ── */
|
|
520
|
+
.pr-helper-risk-low { background: rgba(63, 185, 80, 0.12); color: #1a7f37; }
|
|
521
|
+
.pr-helper-risk-medium { background: rgba(240, 178, 50, 0.15); color: #9a6700; }
|
|
522
|
+
.pr-helper-risk-high { background: rgba(255, 80, 80, 0.12); color: #cf222e; }
|
|
523
|
+
|
|
524
|
+
.pr-helper-risk-text {
|
|
525
|
+
display: inline-block;
|
|
526
|
+
font-size: 10px;
|
|
527
|
+
font-weight: 700;
|
|
528
|
+
text-transform: uppercase;
|
|
529
|
+
letter-spacing: 0.04em;
|
|
530
|
+
padding: 2px 8px;
|
|
531
|
+
border-radius: 100px;
|
|
532
|
+
white-space: nowrap;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
.pr-helper-risk-headline {
|
|
536
|
+
padding: 16px 18px;
|
|
537
|
+
border-radius: 10px;
|
|
538
|
+
/* Was #f8fafc, which is now the canvas colour itself. */
|
|
539
|
+
background: #ffffff;
|
|
540
|
+
border: 1px solid var(--color-border-default, #d0d7de);
|
|
541
|
+
margin-bottom: 14px;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
.pr-helper-risk-headline-top {
|
|
545
|
+
display: flex;
|
|
546
|
+
align-items: center;
|
|
547
|
+
gap: 8px;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.pr-helper-risk-dot {
|
|
551
|
+
width: 10px;
|
|
552
|
+
height: 10px;
|
|
553
|
+
border-radius: 50%;
|
|
554
|
+
flex-shrink: 0;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
.pr-helper-risk-dot-low { background: #1a7f37; }
|
|
558
|
+
.pr-helper-risk-dot-medium { background: #9a6700; }
|
|
559
|
+
.pr-helper-risk-dot-high { background: #cf222e; }
|
|
560
|
+
|
|
561
|
+
.pr-helper-risk-headline-label {
|
|
562
|
+
font-size: 16px;
|
|
563
|
+
font-weight: 700;
|
|
564
|
+
color: var(--color-fg-default, #24292f);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
.pr-helper-risk-headline-takeaway {
|
|
568
|
+
font-size: 13px;
|
|
569
|
+
color: var(--color-fg-muted, #57606a);
|
|
570
|
+
margin: 8px 0 0;
|
|
571
|
+
line-height: 1.5;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
.pr-helper-overview-stats {
|
|
575
|
+
display: grid;
|
|
576
|
+
grid-template-columns: repeat(4, 1fr);
|
|
577
|
+
gap: 8px;
|
|
578
|
+
margin-bottom: 14px;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/* A <button>, so the browser's default control styling has to be unwound before the card
|
|
582
|
+
reads as a card again — font is the one that bites: buttons don't inherit it, so without
|
|
583
|
+
this the value and label drop to the UA's 13.333px system font. */
|
|
584
|
+
.pr-helper-overview-stat {
|
|
585
|
+
position: relative; /* anchors the coverage tooltip */
|
|
586
|
+
display: flex;
|
|
587
|
+
flex-direction: column;
|
|
588
|
+
align-items: center;
|
|
589
|
+
text-align: center;
|
|
590
|
+
padding: 8px 4px;
|
|
591
|
+
border: 1px solid var(--color-border-default, #d0d7de);
|
|
592
|
+
border-radius: 8px;
|
|
593
|
+
background: #ffffff;
|
|
594
|
+
font: inherit;
|
|
595
|
+
cursor: pointer;
|
|
596
|
+
transition: background 0.12s, border-color 0.12s;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/* Matches the history/file rows rather than the filled buttons: these are shortcuts, and a
|
|
600
|
+
row of four solid purple blocks would read as the primary action of the Overview. */
|
|
601
|
+
.pr-helper-overview-stat:hover {
|
|
602
|
+
background: rgba(147, 51, 234, 0.06);
|
|
603
|
+
border-color: #9333ea;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
.pr-helper-overview-stat:focus-visible {
|
|
607
|
+
outline: 2px solid #9333ea;
|
|
608
|
+
outline-offset: 1px;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
.pr-helper-overview-stat-value {
|
|
612
|
+
font-size: 18px;
|
|
613
|
+
font-weight: 700;
|
|
614
|
+
color: var(--color-fg-default, #24292f);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/* "50 / 482" is eight glyphs where the other three cards show one or two, and the grid gives
|
|
618
|
+
each card a quarter of a narrow sidebar. Dropping two points and holding the digits to a
|
|
619
|
+
fixed advance keeps it on one line without shrinking the cards that don't need it. */
|
|
620
|
+
.pr-helper-overview-stat-value-wide {
|
|
621
|
+
font-size: 16px;
|
|
622
|
+
white-space: nowrap;
|
|
623
|
+
font-variant-numeric: tabular-nums;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/* position: relative anchors the two decorations that flank it. Both are taken out of flow on
|
|
627
|
+
purpose: they used to sit inside this box, so the card centred "Files →" as a unit and the
|
|
628
|
+
word itself landed ~6px left of the number above it. With the info icon present the two
|
|
629
|
+
roughly cancelled, which is why only the cards without one looked wrong. */
|
|
630
|
+
.pr-helper-overview-stat-label {
|
|
631
|
+
position: relative;
|
|
632
|
+
font-size: 11px;
|
|
633
|
+
color: var(--color-fg-muted, #57606a);
|
|
634
|
+
margin-top: 3px;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/* Explains a capped review, and only ever appears on one. Sits ahead of the 11px label rather
|
|
638
|
+
than beside the value, so it's sized to that row — against the bold number it read as a
|
|
639
|
+
glyph belonging to the count. */
|
|
640
|
+
.pr-helper-overview-stat-info {
|
|
641
|
+
position: absolute;
|
|
642
|
+
right: calc(100% + 3px);
|
|
643
|
+
top: 50%;
|
|
644
|
+
transform: translateY(-50%);
|
|
645
|
+
display: inline-flex;
|
|
646
|
+
align-items: center;
|
|
647
|
+
/* The glyph draws its own ring, so there is no border or font styling here — only the box it
|
|
648
|
+
sits in. It inherits the label's muted colour through the SVG's fill="currentColor", which
|
|
649
|
+
is also why it firms up with the label on hover instead of needing its own rule. */
|
|
650
|
+
margin-right: 3px;
|
|
651
|
+
line-height: 0;
|
|
652
|
+
vertical-align: -2px;
|
|
653
|
+
cursor: default;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
.pr-helper-overview-stat-info::after {
|
|
657
|
+
content: attr(data-tip);
|
|
658
|
+
position: absolute;
|
|
659
|
+
top: calc(100% + 4px);
|
|
660
|
+
/* Anchored to the card's right edge and allowed to overflow it leftward. Constrained to the
|
|
661
|
+
card's own width this would render as a one-word-per-line column — the cards are a quarter
|
|
662
|
+
of a narrow sidebar. Coverage is the rightmost card, so growing leftward stays in bounds. */
|
|
663
|
+
right: 0;
|
|
664
|
+
left: auto;
|
|
665
|
+
width: 180px;
|
|
666
|
+
z-index: 2;
|
|
667
|
+
padding: 6px 8px;
|
|
668
|
+
border-radius: 6px;
|
|
669
|
+
background: #24292f;
|
|
670
|
+
color: #ffffff;
|
|
671
|
+
font-size: 11px;
|
|
672
|
+
font-weight: 400;
|
|
673
|
+
line-height: 1.4;
|
|
674
|
+
text-align: left;
|
|
675
|
+
white-space: normal;
|
|
676
|
+
opacity: 0;
|
|
677
|
+
pointer-events: none;
|
|
678
|
+
transition: opacity 0.12s;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
.pr-helper-overview-stat-info:hover::after,
|
|
682
|
+
.pr-helper-overview-stat:focus-visible .pr-helper-overview-stat-info::after {
|
|
683
|
+
opacity: 1;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/* Deliberately faint: the arrow is a hint that the card goes somewhere, not a second label.
|
|
687
|
+
It firms up on hover, where the reviewer is already about to click. */
|
|
688
|
+
/* Hangs off the label's right edge rather than extending it, so the label text stays centred
|
|
689
|
+
under the value. No gap: at the sidebar's 380px minimum each card is ~81px wide and the
|
|
690
|
+
longest label ("Questions") leaves comfortable margin — this used to read "Guide topics",
|
|
691
|
+
which was the tightest case and is why the arrow is positioned absolutely rather than inline. */
|
|
692
|
+
.pr-helper-overview-stat-arrow {
|
|
693
|
+
position: absolute;
|
|
694
|
+
left: 100%;
|
|
695
|
+
top: 0;
|
|
696
|
+
opacity: 0.45;
|
|
697
|
+
transition: opacity 0.12s;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
.pr-helper-overview-stat:hover .pr-helper-overview-stat-arrow { opacity: 1; }
|
|
701
|
+
|
|
702
|
+
.pr-helper-overview-stat:hover .pr-helper-overview-stat-label { color: #9333ea; }
|
|
703
|
+
|
|
704
|
+
.pr-helper-review-summary {
|
|
705
|
+
font-size: 14px;
|
|
706
|
+
line-height: 1.55;
|
|
707
|
+
color: var(--color-fg-default, #24292f);
|
|
708
|
+
margin: 0 0 14px;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
.pr-helper-sidebar code {
|
|
712
|
+
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
|
|
713
|
+
font-size: 0.9em;
|
|
714
|
+
background: var(--color-neutral-muted, rgba(175, 184, 193, 0.2));
|
|
715
|
+
border-radius: 4px;
|
|
716
|
+
padding: 0.1em 0.4em;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
.pr-helper-explain-simply-button {
|
|
720
|
+
font-size: 12px;
|
|
721
|
+
font-weight: 600;
|
|
722
|
+
font-family: inherit;
|
|
723
|
+
padding: 7px 14px;
|
|
724
|
+
border-radius: 6px;
|
|
725
|
+
border: 1px solid #9333ea;
|
|
726
|
+
background: transparent;
|
|
727
|
+
color: #9333ea;
|
|
728
|
+
cursor: pointer;
|
|
729
|
+
margin: 0 0 14px;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
.pr-helper-explain-simply-button:hover { background: rgba(147, 51, 234, 0.08); }
|
|
733
|
+
|
|
734
|
+
.pr-helper-explain-simply-done {
|
|
735
|
+
font-size: 13px;
|
|
736
|
+
color: var(--color-fg-muted, #57606a);
|
|
737
|
+
margin: 0 0 14px;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
.pr-helper-review-panel h4 {
|
|
741
|
+
font-size: 11px;
|
|
742
|
+
text-transform: uppercase;
|
|
743
|
+
letter-spacing: 0.06em;
|
|
744
|
+
color: #94a3b8;
|
|
745
|
+
margin: 16px 0 8px;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
.pr-helper-review-panel ul {
|
|
749
|
+
margin: 0;
|
|
750
|
+
padding-left: 18px;
|
|
751
|
+
font-size: 14px;
|
|
752
|
+
line-height: 1.5;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/* ── Pill tooltips ──
|
|
756
|
+
Every pill in the sidebar is one uppercase word standing in for a whole axis of judgement, and
|
|
757
|
+
MAJOR sitting next to "medium confidence" gives no hint that they answer different questions.
|
|
758
|
+
Hovering explains them; see ui/components/pill_tooltips.ts for the copy and why it is worded
|
|
759
|
+
the way it is.
|
|
760
|
+
|
|
761
|
+
Deliberately not the native `title` attribute. That fires after roughly a second of stillness,
|
|
762
|
+
renders in the OS font at the cursor rather than against the card, and cannot be seen at all
|
|
763
|
+
while the pointer is moving — which is exactly the moment a reviewer scanning a list of
|
|
764
|
+
findings wants the answer. This is the same `content: attr(data-tip)` mechanism the Overview's
|
|
765
|
+
coverage note already uses, generalised so any pill can opt in.
|
|
766
|
+
|
|
767
|
+
The pill itself is NOT positioned, on purpose: the ::after resolves against the badge row
|
|
768
|
+
instead, so a 220px card hanging off a pill near the right edge of a 380px sidebar cannot
|
|
769
|
+
overflow and get clipped by the panel's overflow-y. Anchoring to the row also lands every
|
|
770
|
+
tooltip in the same place, so moving along a row of pills swaps the text rather than making
|
|
771
|
+
the box jump. Each anchor is a flex row of pills, listed here rather than given a shared
|
|
772
|
+
class because they already exist and their layout is unrelated. */
|
|
773
|
+
.pr-helper-issue-badges,
|
|
774
|
+
.pr-helper-history-item-top,
|
|
775
|
+
.pr-helper-history-followup-top {
|
|
776
|
+
position: relative;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
.pr-helper-tip::after {
|
|
780
|
+
content: attr(data-tip);
|
|
781
|
+
position: absolute;
|
|
782
|
+
top: calc(100% + 6px);
|
|
783
|
+
/* Both edges pinned, so the tooltip is exactly as wide as the row it explains. */
|
|
784
|
+
left: 0;
|
|
785
|
+
right: 0;
|
|
786
|
+
z-index: 3;
|
|
787
|
+
padding: 7px 9px;
|
|
788
|
+
border-radius: 6px;
|
|
789
|
+
background: #24292f;
|
|
790
|
+
color: #ffffff;
|
|
791
|
+
/* The pills set uppercase, letter-spacing and 700 weight on themselves, all of which the
|
|
792
|
+
::after would otherwise inherit and turn a sentence into a ransom note. */
|
|
793
|
+
font-size: 11px;
|
|
794
|
+
font-weight: 400;
|
|
795
|
+
font-family: inherit;
|
|
796
|
+
text-transform: none;
|
|
797
|
+
letter-spacing: normal;
|
|
798
|
+
line-height: 1.45;
|
|
799
|
+
text-align: left;
|
|
800
|
+
white-space: normal;
|
|
801
|
+
opacity: 0;
|
|
802
|
+
pointer-events: none;
|
|
803
|
+
transition: opacity 0.1s;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
/* Delay on the way in, none on the way out: a reviewer dragging the pointer across a card
|
|
807
|
+
should not trip four tooltips in sequence, but one they've finished reading should vanish the
|
|
808
|
+
instant they leave. Two rules rather than one because a transition-delay applies to both
|
|
809
|
+
directions. */
|
|
810
|
+
.pr-helper-tip:hover::after,
|
|
811
|
+
.pr-helper-tip:focus-visible::after {
|
|
812
|
+
opacity: 1;
|
|
813
|
+
transition: opacity 0.1s 0.35s;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/* ── Issues tab ── */
|
|
817
|
+
.pr-helper-issue-card {
|
|
818
|
+
border: 1px solid var(--color-border-default, #d0d7de);
|
|
819
|
+
border-radius: 8px;
|
|
820
|
+
padding: 16px;
|
|
821
|
+
margin-bottom: 12px;
|
|
822
|
+
/* Stated rather than inherited: the surface behind this is now the grey canvas. */
|
|
823
|
+
background: #ffffff;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
.pr-helper-issue-header {
|
|
827
|
+
display: flex;
|
|
828
|
+
flex-direction: column;
|
|
829
|
+
align-items: flex-start;
|
|
830
|
+
gap: 8px;
|
|
831
|
+
margin-bottom: 8px;
|
|
832
|
+
text-align: left;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
.pr-helper-issue-badges {
|
|
836
|
+
display: flex;
|
|
837
|
+
align-items: center;
|
|
838
|
+
justify-content: flex-start;
|
|
839
|
+
flex-wrap: wrap;
|
|
840
|
+
gap: 6px;
|
|
841
|
+
width: 100%;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
.pr-helper-severity-badge {
|
|
845
|
+
font-size: 11px;
|
|
846
|
+
font-weight: 700;
|
|
847
|
+
text-transform: uppercase;
|
|
848
|
+
padding: 3px 9px;
|
|
849
|
+
border-radius: 100px;
|
|
850
|
+
white-space: nowrap;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
.pr-helper-severity-critical .pr-helper-severity-badge { background: var(--pr-helper-pill-danger-bg); color: var(--pr-helper-pill-danger-fg); }
|
|
854
|
+
.pr-helper-severity-major .pr-helper-severity-badge { background: var(--pr-helper-pill-warning-bg); color: var(--pr-helper-pill-warning-fg); }
|
|
855
|
+
.pr-helper-severity-minor .pr-helper-severity-badge { background: var(--pr-helper-pill-info-bg); color: var(--pr-helper-pill-info-fg); }
|
|
856
|
+
.pr-helper-severity-nit .pr-helper-severity-badge { background: var(--pr-helper-pill-neutral-bg); color: var(--pr-helper-pill-neutral-fg); }
|
|
857
|
+
|
|
858
|
+
.pr-helper-issue-title { font-size: 14px; font-weight: 600; margin: 0; text-align: left; width: 100%; }
|
|
859
|
+
|
|
860
|
+
.pr-helper-issue-desc {
|
|
861
|
+
font-size: 13px;
|
|
862
|
+
line-height: 1.5;
|
|
863
|
+
color: var(--color-fg-muted, #57606a);
|
|
864
|
+
margin: 0 0 8px;
|
|
865
|
+
text-align: left;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
/* "Reviewed with N existing discussion threads" — a provenance note under the Overview summary,
|
|
869
|
+
sized to be findable but not to compete with the summary itself. */
|
|
870
|
+
.pr-helper-discussion-awareness {
|
|
871
|
+
font-size: 12px;
|
|
872
|
+
line-height: 1.4;
|
|
873
|
+
margin: -4px 0 12px;
|
|
874
|
+
color: var(--color-fg-muted, #57606a);
|
|
875
|
+
text-align: left;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/* "This PR changes 482 files..." — the same provenance register as discussion awareness above,
|
|
879
|
+
deliberately so: both tell the reviewer what the review was actually given. */
|
|
880
|
+
.pr-helper-coverage-caveat {
|
|
881
|
+
font-size: 12px;
|
|
882
|
+
line-height: 1.4;
|
|
883
|
+
margin: -4px 0 12px;
|
|
884
|
+
color: var(--color-fg-muted, #57606a);
|
|
885
|
+
text-align: left;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
/* "Already raised by @x" — deliberately quiet. It qualifies the finding above it rather than
|
|
889
|
+
competing with the severity badge, so it reads as a footnote, not a second headline. */
|
|
890
|
+
/* The one line on a finding that can tell a reviewer to stop reading, so it is set as a status
|
|
891
|
+
rather than as a note. It was a muted 12px line with a 2px rule beside it, sitting between the
|
|
892
|
+
description and the file path; a reader looking straight at two of them asked where the feature
|
|
893
|
+
had got to. Weight and a tint, not a colour shout — it still must not outrank the severity. */
|
|
894
|
+
.pr-helper-issue-discussion {
|
|
895
|
+
font-size: 12px;
|
|
896
|
+
line-height: 1.45;
|
|
897
|
+
margin: 0 0 8px;
|
|
898
|
+
padding: 6px 10px;
|
|
899
|
+
border-radius: 6px;
|
|
900
|
+
border-left: 3px solid var(--pr-helper-pill-neutral-bg, #d0d7de);
|
|
901
|
+
background: var(--pr-helper-discussion-bg, rgba(15, 23, 42, 0.04));
|
|
902
|
+
color: var(--color-fg-muted, #57606a);
|
|
903
|
+
text-align: left;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
.pr-helper-issue-discussion-lead {
|
|
907
|
+
font-weight: 600;
|
|
908
|
+
color: var(--color-fg-default, #1f2328);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
.pr-helper-issue-discussion-unresolved {
|
|
912
|
+
border-left-color: var(--pr-helper-pill-warning-bg, #d4a72c);
|
|
913
|
+
background: var(--pr-helper-discussion-unresolved-bg, rgba(212, 167, 44, 0.1));
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
.pr-helper-issue-discussion-link {
|
|
917
|
+
color: inherit;
|
|
918
|
+
text-decoration: none;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
.pr-helper-issue-discussion-link:hover { text-decoration: underline; }
|
|
922
|
+
|
|
923
|
+
.pr-helper-issue-loc {
|
|
924
|
+
display: block;
|
|
925
|
+
cursor: pointer;
|
|
926
|
+
margin-top: 6px;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
.pr-helper-issue-loc:hover .pr-helper-path-name { text-decoration: underline; }
|
|
930
|
+
|
|
931
|
+
/* ── Questions tab ── */
|
|
932
|
+
/* Deliberately reuses .pr-helper-issue-header/-title/-badges/-desc/-loc from the Issues tab
|
|
933
|
+
(see renderQuestionCard) rather than defining a parallel set: a question and an issue are
|
|
934
|
+
the same shape of thing — a bordered card with a title, a severity-style badge and a short
|
|
935
|
+
explanation — so they share the ruleset and only the card shell and its own bits below are
|
|
936
|
+
Questions-specific. */
|
|
937
|
+
.pr-helper-question-card {
|
|
938
|
+
border: 1px solid var(--color-border-default, #d0d7de);
|
|
939
|
+
border-radius: 8px;
|
|
940
|
+
padding: 16px;
|
|
941
|
+
margin-bottom: 12px;
|
|
942
|
+
/* Stated rather than inherited: the surface behind this is now the grey canvas. */
|
|
943
|
+
background: #ffffff;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
/* Answered is additive, same principle as a completed Guide section: dimming or collapsing the
|
|
947
|
+
card would throw away the reviewer's own record that they resolved it. The check mark is the
|
|
948
|
+
whole "done" signal; nothing else about the card recedes. */
|
|
949
|
+
.pr-helper-question-card-answered { box-shadow: inset 3px 0 0 #1a7f37; }
|
|
950
|
+
|
|
951
|
+
.pr-helper-question-card h5 {
|
|
952
|
+
font-size: 10px;
|
|
953
|
+
text-transform: uppercase;
|
|
954
|
+
letter-spacing: 0.06em;
|
|
955
|
+
color: #94a3b8;
|
|
956
|
+
margin: 10px 0 4px;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
.pr-helper-question-importance-badge {
|
|
960
|
+
font-size: 11px;
|
|
961
|
+
font-weight: 700;
|
|
962
|
+
text-transform: uppercase;
|
|
963
|
+
padding: 3px 9px;
|
|
964
|
+
border-radius: 100px;
|
|
965
|
+
white-space: nowrap;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
.pr-helper-question-importance-high { background: var(--pr-helper-pill-danger-bg); color: var(--pr-helper-pill-danger-fg); }
|
|
969
|
+
.pr-helper-question-importance-medium { background: var(--pr-helper-pill-warning-bg); color: var(--pr-helper-pill-warning-fg); }
|
|
970
|
+
.pr-helper-question-importance-low { background: var(--pr-helper-pill-neutral-bg); color: var(--pr-helper-pill-neutral-fg); }
|
|
971
|
+
|
|
972
|
+
/* ── Questions progress header ──
|
|
973
|
+
Deliberately lighter than the Guide's progress box (no bar, no "next up" line): the Guide is
|
|
974
|
+
a workflow you walk in order, so it earns a full dashboard, whereas this is a flat list where
|
|
975
|
+
the only useful fact is how many are still open. */
|
|
976
|
+
.pr-helper-questions-progress {
|
|
977
|
+
display: flex;
|
|
978
|
+
align-items: center;
|
|
979
|
+
gap: 8px;
|
|
980
|
+
font-size: 12px;
|
|
981
|
+
color: var(--color-fg-muted, #57606a);
|
|
982
|
+
padding-bottom: 12px;
|
|
983
|
+
margin-bottom: 4px;
|
|
984
|
+
border-bottom: 1px solid var(--color-border-default, #d0d7de);
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
.pr-helper-questions-progress-count { font-weight: 600; }
|
|
988
|
+
|
|
989
|
+
.pr-helper-questions-progress-check { color: #1a7f37; font-weight: 700; }
|
|
990
|
+
|
|
991
|
+
.pr-helper-question-actions {
|
|
992
|
+
display: flex;
|
|
993
|
+
align-items: center;
|
|
994
|
+
justify-content: space-between;
|
|
995
|
+
flex-wrap: wrap;
|
|
996
|
+
gap: 12px;
|
|
997
|
+
margin-top: 12px;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/* Deliberately not a filled button, matching .pr-helper-guide-start-cta: this is "here's the
|
|
1001
|
+
next step", not a demand for a click. */
|
|
1002
|
+
.pr-helper-question-jump {
|
|
1003
|
+
padding: 0;
|
|
1004
|
+
border: none;
|
|
1005
|
+
background: transparent;
|
|
1006
|
+
color: #9333ea;
|
|
1007
|
+
font-size: 13px;
|
|
1008
|
+
font-weight: 700;
|
|
1009
|
+
font-family: inherit;
|
|
1010
|
+
cursor: pointer;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
.pr-helper-question-jump { text-decoration: none; }
|
|
1014
|
+
.pr-helper-question-jump:hover { color: #7c3aed; text-decoration: underline; }
|
|
1015
|
+
|
|
1016
|
+
/* Same button geometry as .pr-helper-explain-simply-button (the sidebar's existing outlined
|
|
1017
|
+
secondary), but neutral rather than purple: purple is spoken for by the primary action
|
|
1018
|
+
sitting next to it, and an outlined *neutral* button is the standard way to say "real
|
|
1019
|
+
control, lower emphasis". It was plain text before, which made a state-changing action look
|
|
1020
|
+
like a caption. */
|
|
1021
|
+
.pr-helper-question-answer-toggle {
|
|
1022
|
+
font-size: 12px;
|
|
1023
|
+
font-weight: 600;
|
|
1024
|
+
font-family: inherit;
|
|
1025
|
+
padding: 5px 12px;
|
|
1026
|
+
border-radius: 6px;
|
|
1027
|
+
border: 1px solid var(--color-border-default, #d0d7de);
|
|
1028
|
+
background: transparent;
|
|
1029
|
+
color: var(--color-fg-muted, #57606a);
|
|
1030
|
+
cursor: pointer;
|
|
1031
|
+
margin-left: auto;
|
|
1032
|
+
white-space: nowrap;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
.pr-helper-question-answer-toggle:hover {
|
|
1036
|
+
background: rgba(175, 184, 193, 0.12);
|
|
1037
|
+
color: var(--color-fg-default, #24292f);
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
/* Answered: the button becomes the card's "undo", so it picks up the same green the card's
|
|
1041
|
+
edge uses — the control and the state it produced read as one thing. */
|
|
1042
|
+
.pr-helper-question-answer-toggle[data-answered="true"] {
|
|
1043
|
+
border-color: rgba(26, 127, 55, 0.4);
|
|
1044
|
+
color: #1a7f37;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
.pr-helper-question-answer-toggle[data-answered="true"]:hover { background: rgba(26, 127, 55, 0.08); }
|
|
1048
|
+
|
|
1049
|
+
/* ── File path display, shared by every tab ──
|
|
1050
|
+
One construct wherever a file is named: the Guide's section files, the Issues and Questions
|
|
1051
|
+
location refs, and the Files tab's browser rows. They were three near-identical pairs of
|
|
1052
|
+
rules with drifting sizes and colours; a reviewer moving between tabs was seeing the same
|
|
1053
|
+
file rendered three different ways.
|
|
1054
|
+
|
|
1055
|
+
The split exists because a deep monorepo path is mostly shared prefix — at full weight it
|
|
1056
|
+
wraps to three lines and buries the filename, which is the only part that identifies it.
|
|
1057
|
+
It's also what stops the overflow: a path is a single unbroken token with no break
|
|
1058
|
+
opportunity, so rendered whole it pushes past the fixed-width sidebar and drags a horizontal
|
|
1059
|
+
scrollbar onto the panel. The directory truncates; only the short filename has to fit. Every
|
|
1060
|
+
call site puts the full path on a title attribute, so nothing is unrecoverable. */
|
|
1061
|
+
.pr-helper-path-dir {
|
|
1062
|
+
display: block;
|
|
1063
|
+
/* Declared, never inherited. Without this the directory picked up whatever font its ancestor
|
|
1064
|
+
chain happened to supply — and the three call sites have different chains, so the same class
|
|
1065
|
+
rendered monospace under one tab and the sidebar's sans-serif under another, with the
|
|
1066
|
+
filename below it always monospace. Half a path in each font. It's a path either way, so it
|
|
1067
|
+
matches the filename. */
|
|
1068
|
+
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
|
|
1069
|
+
font-size: 11px;
|
|
1070
|
+
color: var(--color-fg-muted, #57606a);
|
|
1071
|
+
opacity: 0.85;
|
|
1072
|
+
white-space: nowrap;
|
|
1073
|
+
overflow: hidden;
|
|
1074
|
+
text-overflow: ellipsis;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
/* The trailing slash marks the line as a directory rather than a truncated filename. Generated
|
|
1078
|
+
here rather than baked into the markup so it stays out of the title attribute and the
|
|
1079
|
+
data-file path, which must remain the exact path GitHub's diff anchor is hashed from. */
|
|
1080
|
+
.pr-helper-path-dir::after {
|
|
1081
|
+
content: "/";
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
/* Wrapped rather than truncated, unlike the directory above: a prefix is disposable context,
|
|
1085
|
+
but clipping the filename would cost the reviewer the identifier itself. */
|
|
1086
|
+
.pr-helper-path-name {
|
|
1087
|
+
display: block;
|
|
1088
|
+
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
|
|
1089
|
+
font-size: 13px;
|
|
1090
|
+
font-weight: 600;
|
|
1091
|
+
color: #9333ea;
|
|
1092
|
+
overflow-wrap: anywhere;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
/* No Files-tab size override here on purpose. A filename means the same thing in every tab,
|
|
1096
|
+
so it should look the same in every tab — the Files rows earn their prominence from the
|
|
1097
|
+
row's own padding and layout, not from larger type than the identical reference in Issues,
|
|
1098
|
+
Questions or the Guide. */
|
|
1099
|
+
|
|
1100
|
+
.pr-helper-question-group { margin-bottom: 4px; }
|
|
1101
|
+
|
|
1102
|
+
.pr-helper-question-group-title {
|
|
1103
|
+
font-size: 11px;
|
|
1104
|
+
font-weight: 700;
|
|
1105
|
+
text-transform: uppercase;
|
|
1106
|
+
letter-spacing: 0.04em;
|
|
1107
|
+
color: #9333ea;
|
|
1108
|
+
margin: 14px 0 6px;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
.pr-helper-question-group:first-child .pr-helper-question-group-title { margin-top: 0; }
|
|
1112
|
+
|
|
1113
|
+
/* ── Files tab ── */
|
|
1114
|
+
.pr-helper-file-stat {
|
|
1115
|
+
font-size: 12px;
|
|
1116
|
+
color: var(--color-fg-muted, #57606a);
|
|
1117
|
+
white-space: nowrap;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
.pr-helper-file-stat-issues { color: #9a6700; }
|
|
1121
|
+
|
|
1122
|
+
/* ── Guide tab ── */
|
|
1123
|
+
.pr-helper-guide {
|
|
1124
|
+
display: flex;
|
|
1125
|
+
flex-direction: column;
|
|
1126
|
+
gap: 4px;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
.pr-helper-suggested-path {
|
|
1130
|
+
padding: 14px 16px;
|
|
1131
|
+
border-radius: 8px;
|
|
1132
|
+
background: rgba(147, 51, 234, 0.06);
|
|
1133
|
+
border: 1px solid rgba(147, 51, 234, 0.2);
|
|
1134
|
+
margin-bottom: 12px;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
.pr-helper-suggested-path-title {
|
|
1138
|
+
font-size: 11px;
|
|
1139
|
+
font-weight: 700;
|
|
1140
|
+
text-transform: uppercase;
|
|
1141
|
+
letter-spacing: 0.04em;
|
|
1142
|
+
color: #9333ea;
|
|
1143
|
+
margin: 0 0 6px;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
.pr-helper-suggested-path-list {
|
|
1147
|
+
margin: 0;
|
|
1148
|
+
padding-left: 18px;
|
|
1149
|
+
font-size: 13px;
|
|
1150
|
+
line-height: 1.5;
|
|
1151
|
+
color: var(--color-fg-default, #24292f);
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
.pr-helper-suggested-path-list li { margin-bottom: 3px; }
|
|
1155
|
+
|
|
1156
|
+
.pr-helper-suggested-path-time {
|
|
1157
|
+
font-size: 12px;
|
|
1158
|
+
color: var(--color-fg-muted, #57606a);
|
|
1159
|
+
margin: 8px 0 0;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
/* Every section done — the box swaps its purple "here's where to go" identity for a green
|
|
1163
|
+
"you're finished" one. Same geometry, so the Guide doesn't reflow underneath it. */
|
|
1164
|
+
.pr-helper-suggested-path-complete {
|
|
1165
|
+
background: rgba(26, 127, 55, 0.07);
|
|
1166
|
+
border-color: rgba(26, 127, 55, 0.25);
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
.pr-helper-suggested-path-complete .pr-helper-suggested-path-title { color: #1a7f37; }
|
|
1170
|
+
|
|
1171
|
+
.pr-helper-progress-counter {
|
|
1172
|
+
display: flex;
|
|
1173
|
+
align-items: center;
|
|
1174
|
+
gap: 6px;
|
|
1175
|
+
font-size: 13px;
|
|
1176
|
+
font-weight: 600;
|
|
1177
|
+
color: var(--color-fg-default, #24292f);
|
|
1178
|
+
margin: 0 0 8px;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
.pr-helper-progress-check {
|
|
1182
|
+
color: #1a7f37;
|
|
1183
|
+
font-weight: 700;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
.pr-helper-progress-bar {
|
|
1187
|
+
height: 4px;
|
|
1188
|
+
border-radius: 100px;
|
|
1189
|
+
background: rgba(140, 149, 159, 0.25);
|
|
1190
|
+
overflow: hidden;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
.pr-helper-progress-bar-fill {
|
|
1194
|
+
height: 100%;
|
|
1195
|
+
border-radius: 100px;
|
|
1196
|
+
background: #9333ea;
|
|
1197
|
+
transition: width 0.35s ease;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
.pr-helper-suggested-path-complete .pr-helper-progress-bar-fill { background: #1a7f37; }
|
|
1201
|
+
|
|
1202
|
+
.pr-helper-progress-next {
|
|
1203
|
+
font-size: 13px;
|
|
1204
|
+
color: var(--color-fg-default, #24292f);
|
|
1205
|
+
margin: 10px 0 0;
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
.pr-helper-progress-next-label {
|
|
1209
|
+
font-size: 11px;
|
|
1210
|
+
font-weight: 700;
|
|
1211
|
+
text-transform: uppercase;
|
|
1212
|
+
letter-spacing: 0.04em;
|
|
1213
|
+
color: var(--color-fg-muted, #57606a);
|
|
1214
|
+
margin-right: 4px;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
.pr-helper-guide-section {
|
|
1218
|
+
/* The disclosure arrow is the only thing that lives left of the card's text column;
|
|
1219
|
+
everything else — badges, title, meta row, body — is indented by this gutter so the
|
|
1220
|
+
card reads as a single left edge instead of three competing ones. Width of the arrow
|
|
1221
|
+
(10px) + the header row's gap (8px). */
|
|
1222
|
+
--pr-helper-guide-gutter: 18px;
|
|
1223
|
+
border: 1px solid var(--color-border-default, #d0d7de);
|
|
1224
|
+
border-radius: 8px;
|
|
1225
|
+
padding: 16px;
|
|
1226
|
+
margin-bottom: 12px;
|
|
1227
|
+
/* Drives the just-completed wash below: the class add fades in, the class removal fades
|
|
1228
|
+
back out, so no keyframe or animationend bookkeeping is involved. */
|
|
1229
|
+
transition: background-color 0.6s ease, border-color 0.6s ease;
|
|
1230
|
+
/* Stated rather than inherited: the surface behind this is now the grey canvas. */
|
|
1231
|
+
background: #ffffff;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
/* Additive only — a completed section keeps full contrast on its title, meta row and badges.
|
|
1235
|
+
See the comment in renderGuideSectionCard: these cards are the reviewer's record of what
|
|
1236
|
+
they've covered, not spent items to be dimmed out of the way. */
|
|
1237
|
+
/* The green edge is an inset shadow rather than a thicker border-left: widening the border
|
|
1238
|
+
would pull the card's content in by 2px at the exact moment the section flips to completed,
|
|
1239
|
+
so the whole card would visibly jog sideways during the transition. */
|
|
1240
|
+
.pr-helper-guide-section-completed {
|
|
1241
|
+
box-shadow: inset 3px 0 0 #1a7f37;
|
|
1242
|
+
background: rgba(26, 127, 55, 0.04);
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
.pr-helper-guide-section-highlight {
|
|
1246
|
+
background: rgba(26, 127, 55, 0.16);
|
|
1247
|
+
border-color: rgba(26, 127, 55, 0.45);
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
/* The auto margin is the split point of the badge row — everything after it goes right. */
|
|
1251
|
+
.pr-helper-guide-section-badges .pr-helper-start-here-badge,
|
|
1252
|
+
.pr-helper-guide-section-badges .pr-helper-completed-badge {
|
|
1253
|
+
margin-right: auto;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
.pr-helper-guide-check {
|
|
1257
|
+
color: #1a7f37;
|
|
1258
|
+
font-size: 13px;
|
|
1259
|
+
font-weight: 700;
|
|
1260
|
+
flex-shrink: 0;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
.pr-helper-guide-section-header {
|
|
1264
|
+
display: flex;
|
|
1265
|
+
flex-direction: column;
|
|
1266
|
+
gap: 4px;
|
|
1267
|
+
width: 100%;
|
|
1268
|
+
border: none;
|
|
1269
|
+
background: transparent;
|
|
1270
|
+
padding: 0;
|
|
1271
|
+
cursor: pointer;
|
|
1272
|
+
font-family: inherit;
|
|
1273
|
+
text-align: left;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
/* Split row: the status badge (Start here / Continue / Completed, plus its check) sits on the
|
|
1277
|
+
left at the title's gutter — it's about the reviewer's position and belongs with the title
|
|
1278
|
+
it labels — while the attention badge is pushed to the right edge as a standing property of
|
|
1279
|
+
the section. flex-end rather than space-between is what makes the attention badge hold the
|
|
1280
|
+
right edge even when there is no status badge to space against. */
|
|
1281
|
+
.pr-helper-guide-section-badges {
|
|
1282
|
+
display: flex;
|
|
1283
|
+
align-items: center;
|
|
1284
|
+
flex-wrap: wrap;
|
|
1285
|
+
justify-content: flex-end;
|
|
1286
|
+
gap: 8px;
|
|
1287
|
+
padding-left: var(--pr-helper-guide-gutter);
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
.pr-helper-guide-section-header-top {
|
|
1291
|
+
display: flex;
|
|
1292
|
+
align-items: baseline;
|
|
1293
|
+
gap: 8px;
|
|
1294
|
+
width: 100%;
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
/* Fixed width rather than glyph width: the gutter every other row is indented by has to be
|
|
1298
|
+
predictable, and ▼ measures differently across the fonts GitHub hands us. */
|
|
1299
|
+
.pr-helper-guide-disclosure {
|
|
1300
|
+
width: 10px;
|
|
1301
|
+
font-size: 10px;
|
|
1302
|
+
color: var(--color-fg-muted, #57606a);
|
|
1303
|
+
transition: transform 0.15s ease;
|
|
1304
|
+
flex-shrink: 0;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
.pr-helper-guide-section-collapsed .pr-helper-guide-disclosure { transform: rotate(-90deg); }
|
|
1308
|
+
.pr-helper-guide-section-collapsed .pr-helper-guide-section-body { display: none; }
|
|
1309
|
+
|
|
1310
|
+
.pr-helper-guide-section-title {
|
|
1311
|
+
font-size: 14px;
|
|
1312
|
+
font-weight: 700;
|
|
1313
|
+
flex: 1;
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
.pr-helper-guide-badge {
|
|
1317
|
+
font-size: 11px;
|
|
1318
|
+
font-weight: 700;
|
|
1319
|
+
text-transform: uppercase;
|
|
1320
|
+
letter-spacing: 0.04em;
|
|
1321
|
+
padding: 3px 9px;
|
|
1322
|
+
border-radius: 100px;
|
|
1323
|
+
white-space: nowrap;
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
/* Start Here is metadata (which section the model recommends first), same role as the
|
|
1327
|
+
attention badge next to it — not a control. The action to launch guided navigation lives
|
|
1328
|
+
in .pr-helper-guide-start-cta at the bottom of the card instead; see UX principle in
|
|
1329
|
+
renderGuideSectionCard's comment: badges communicate state, buttons perform actions. */
|
|
1330
|
+
.pr-helper-start-here-badge {
|
|
1331
|
+
background: #9333ea;
|
|
1332
|
+
color: #ffffff;
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
/* Filled, not tinted — it's the direct counterpart to Start here, and a completed section
|
|
1336
|
+
should read as an equally definite state rather than a faded one. */
|
|
1337
|
+
.pr-helper-completed-badge {
|
|
1338
|
+
background: #1a7f37;
|
|
1339
|
+
color: #ffffff;
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
.pr-helper-attention-badge.pr-helper-attention-high { background: rgba(255, 80, 80, 0.12); color: #cf222e; }
|
|
1343
|
+
.pr-helper-attention-badge.pr-helper-attention-medium { background: rgba(240, 178, 50, 0.15); color: #9a6700; }
|
|
1344
|
+
.pr-helper-attention-badge.pr-helper-attention-low { background: rgba(175, 184, 193, 0.2); color: #57606a; }
|
|
1345
|
+
|
|
1346
|
+
.pr-helper-guide-section-body {
|
|
1347
|
+
margin-top: 8px;
|
|
1348
|
+
padding-left: var(--pr-helper-guide-gutter);
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
/* Deliberately not a filled button — the Guide is calm and information-focused, and a
|
|
1352
|
+
full-width primary button here competed with the summary/checklist for attention. This
|
|
1353
|
+
reads as "you're ready, here's the next step" rather than a CTA demanding a click. */
|
|
1354
|
+
.pr-helper-guide-start-cta {
|
|
1355
|
+
display: inline-block;
|
|
1356
|
+
margin-top: 12px;
|
|
1357
|
+
padding: 0;
|
|
1358
|
+
border: none;
|
|
1359
|
+
background: transparent;
|
|
1360
|
+
color: #9333ea;
|
|
1361
|
+
font-size: 13px;
|
|
1362
|
+
font-weight: 700;
|
|
1363
|
+
font-family: inherit;
|
|
1364
|
+
cursor: pointer;
|
|
1365
|
+
text-align: left;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
.pr-helper-guide-start-cta:hover { color: #7c3aed; text-decoration: underline; }
|
|
1369
|
+
|
|
1370
|
+
.pr-helper-guide-section-actions {
|
|
1371
|
+
display: flex;
|
|
1372
|
+
align-items: center;
|
|
1373
|
+
justify-content: space-between;
|
|
1374
|
+
gap: 12px;
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
/* Quieter than the CTA beside it on purpose: undo is a recovery path for a stray "Finish
|
|
1378
|
+
section", not something to invite. */
|
|
1379
|
+
.pr-helper-guide-uncomplete {
|
|
1380
|
+
margin-top: 12px;
|
|
1381
|
+
padding: 0;
|
|
1382
|
+
border: none;
|
|
1383
|
+
background: transparent;
|
|
1384
|
+
color: var(--color-fg-muted, #57606a);
|
|
1385
|
+
font-size: 12px;
|
|
1386
|
+
font-family: inherit;
|
|
1387
|
+
cursor: pointer;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
.pr-helper-guide-uncomplete:hover { color: var(--color-fg-default, #24292f); text-decoration: underline; }
|
|
1391
|
+
|
|
1392
|
+
.pr-helper-guide-summary {
|
|
1393
|
+
font-size: 14px;
|
|
1394
|
+
font-weight: 600;
|
|
1395
|
+
margin: 0 0 8px;
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
.pr-helper-guide-purpose,
|
|
1399
|
+
.pr-helper-guide-why {
|
|
1400
|
+
font-size: 13px;
|
|
1401
|
+
color: var(--color-fg-muted, #57606a);
|
|
1402
|
+
line-height: 1.55;
|
|
1403
|
+
margin: 0 0 10px;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
.pr-helper-guide-meta-row {
|
|
1407
|
+
font-size: 12px;
|
|
1408
|
+
color: var(--color-fg-muted, #57606a);
|
|
1409
|
+
padding-left: var(--pr-helper-guide-gutter);
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
.pr-helper-guide-section-body h5 {
|
|
1413
|
+
font-size: 10px;
|
|
1414
|
+
text-transform: uppercase;
|
|
1415
|
+
letter-spacing: 0.06em;
|
|
1416
|
+
color: #94a3b8;
|
|
1417
|
+
margin: 10px 0 4px;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
.pr-helper-guide-files {
|
|
1421
|
+
margin: 0;
|
|
1422
|
+
padding-left: 0;
|
|
1423
|
+
list-style: none;
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
/* The row is a block, not a text line, so the directory, filename and focus note can each
|
|
1427
|
+
take their own line and carry their own weight. */
|
|
1428
|
+
/* Padded on every side, then pulled back out by the same amount horizontally: the row's text
|
|
1429
|
+
stays aligned with the rest of the card's text column, while the current-target highlight
|
|
1430
|
+
gets room to sit around the content instead of hugging it. */
|
|
1431
|
+
.pr-helper-guide-file {
|
|
1432
|
+
display: block;
|
|
1433
|
+
cursor: pointer;
|
|
1434
|
+
padding: 8px;
|
|
1435
|
+
margin: 0 -8px;
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
/* Underline only the filename on hover — dragging an underline through the directory and focus
|
|
1439
|
+
prose would make the whole three-line row look like one long link. */
|
|
1440
|
+
.pr-helper-guide-file:hover .pr-helper-path-name { text-decoration: underline; }
|
|
1441
|
+
|
|
1442
|
+
.pr-helper-guide-file-focus {
|
|
1443
|
+
font-size: 12px;
|
|
1444
|
+
line-height: 1.5;
|
|
1445
|
+
color: var(--color-fg-muted, #57606a);
|
|
1446
|
+
margin-top: 2px;
|
|
1447
|
+
padding-left: 2px;
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
.pr-helper-guide-checklist {
|
|
1451
|
+
margin: 0;
|
|
1452
|
+
padding: 10px 12px 10px 26px;
|
|
1453
|
+
font-size: 13px;
|
|
1454
|
+
line-height: 1.5;
|
|
1455
|
+
color: var(--color-fg-default, #24292f);
|
|
1456
|
+
background: #f8fafc;
|
|
1457
|
+
border: 1px solid var(--color-border-default, #d0d7de);
|
|
1458
|
+
border-radius: 6px;
|
|
1459
|
+
list-style: none;
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
.pr-helper-guide-checklist li {
|
|
1463
|
+
position: relative;
|
|
1464
|
+
margin-bottom: 5px;
|
|
1465
|
+
line-height: 1.5;
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
.pr-helper-guide-checklist li:last-child { margin-bottom: 0; }
|
|
1469
|
+
|
|
1470
|
+
.pr-helper-guide-checklist li::before {
|
|
1471
|
+
content: '•';
|
|
1472
|
+
position: absolute;
|
|
1473
|
+
left: -14px;
|
|
1474
|
+
color: #9333ea;
|
|
1475
|
+
font-weight: 700;
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
/* Section tags shown on Issues/Questions items, and section badges on Files rows —
|
|
1479
|
+
both point back to the Guide, reusing the same visual language as file locations. */
|
|
1480
|
+
.pr-helper-section-tag {
|
|
1481
|
+
font-size: 11px;
|
|
1482
|
+
font-weight: 600;
|
|
1483
|
+
color: #9333ea;
|
|
1484
|
+
background: rgba(147, 51, 234, 0.08);
|
|
1485
|
+
border: none;
|
|
1486
|
+
border-radius: 100px;
|
|
1487
|
+
padding: 3px 9px;
|
|
1488
|
+
cursor: pointer;
|
|
1489
|
+
font-family: inherit;
|
|
1490
|
+
white-space: nowrap;
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
.pr-helper-section-tag:hover { background: rgba(147, 51, 234, 0.15); }
|
|
1494
|
+
|
|
1495
|
+
.pr-helper-file-section-badge {
|
|
1496
|
+
display: inline-block;
|
|
1497
|
+
width: fit-content;
|
|
1498
|
+
font-size: 11px;
|
|
1499
|
+
color: #9333ea;
|
|
1500
|
+
background: rgba(147, 51, 234, 0.08);
|
|
1501
|
+
border-radius: 100px;
|
|
1502
|
+
padding: 2px 9px;
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
/* ── History tab ── */
|
|
1506
|
+
.pr-helper-history-group-label {
|
|
1507
|
+
font-size: 10px;
|
|
1508
|
+
font-weight: 700;
|
|
1509
|
+
text-transform: uppercase;
|
|
1510
|
+
letter-spacing: 0.06em;
|
|
1511
|
+
color: #94a3b8;
|
|
1512
|
+
margin: 14px 0 6px;
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
.pr-helper-history-item {
|
|
1516
|
+
display: flex;
|
|
1517
|
+
flex-direction: column;
|
|
1518
|
+
gap: 6px;
|
|
1519
|
+
padding: 12px 14px;
|
|
1520
|
+
border-radius: 6px;
|
|
1521
|
+
cursor: pointer;
|
|
1522
|
+
font-size: 13px;
|
|
1523
|
+
border: 1px solid var(--color-border-default, #d0d7de);
|
|
1524
|
+
margin-bottom: 8px;
|
|
1525
|
+
/* Stated rather than inherited: the surface behind this is now the grey canvas. */
|
|
1526
|
+
background: #ffffff;
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
.pr-helper-history-item:hover { background: rgba(147, 51, 234, 0.06); }
|
|
1530
|
+
|
|
1531
|
+
/* Follow-ups hang off the review they were added to. Indented and left-ruled so the parent
|
|
1532
|
+
relationship reads structurally, and with no hover or cursor affordance — a follow-up carries no
|
|
1533
|
+
summary, risk or verdict, so a row that looked clickable would promise a view that cannot exist. */
|
|
1534
|
+
.pr-helper-history-followup {
|
|
1535
|
+
display: flex;
|
|
1536
|
+
flex-direction: column;
|
|
1537
|
+
gap: 4px;
|
|
1538
|
+
margin: -4px 0 8px 16px;
|
|
1539
|
+
padding: 8px 12px;
|
|
1540
|
+
border-left: 2px solid var(--color-border-default, #d0d7de);
|
|
1541
|
+
font-size: 12px;
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
.pr-helper-history-followup-top,
|
|
1545
|
+
.pr-helper-history-followup-bottom {
|
|
1546
|
+
display: flex;
|
|
1547
|
+
align-items: center;
|
|
1548
|
+
justify-content: space-between;
|
|
1549
|
+
gap: 8px;
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
.pr-helper-history-followup-tag {
|
|
1553
|
+
padding: 1px 6px;
|
|
1554
|
+
border-radius: 10px;
|
|
1555
|
+
font-size: 10px;
|
|
1556
|
+
text-transform: uppercase;
|
|
1557
|
+
letter-spacing: 0.03em;
|
|
1558
|
+
color: var(--color-fg-muted, #57606a);
|
|
1559
|
+
background: var(--color-neutral-subtle, rgba(175, 184, 193, 0.2));
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
.pr-helper-history-followup-scope {
|
|
1563
|
+
color: var(--color-fg-default, #1f2328);
|
|
1564
|
+
overflow: hidden;
|
|
1565
|
+
text-overflow: ellipsis;
|
|
1566
|
+
white-space: nowrap;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
.pr-helper-history-followup-added {
|
|
1570
|
+
flex-shrink: 0;
|
|
1571
|
+
color: var(--color-fg-muted, #57606a);
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
/* An investigation arriving borrows the follow-up row wholesale — same indent, same rule, same
|
|
1575
|
+
two-line geometry, same lack of any clickable affordance — because it is the same kind of thing:
|
|
1576
|
+
something added to the review above it, not a review of its own. Only the left edge and the agent
|
|
1577
|
+
name are purple, which is what investigations are coloured everywhere else, so the two kinds of
|
|
1578
|
+
event stay distinguishable at a glance without a second layout to keep in step. */
|
|
1579
|
+
.pr-helper-history-investigation-event {
|
|
1580
|
+
border-left-color: #9333ea;
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
.pr-helper-history-investigation-event-agent {
|
|
1584
|
+
flex-shrink: 0;
|
|
1585
|
+
font-weight: 600;
|
|
1586
|
+
color: #7e22ce;
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
.pr-helper-history-item-selected {
|
|
1590
|
+
border-color: #9333ea;
|
|
1591
|
+
background: rgba(147, 51, 234, 0.06);
|
|
1592
|
+
box-shadow: inset 0 0 0 1px #9333ea;
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
.pr-helper-history-item-top,
|
|
1596
|
+
.pr-helper-history-item-bottom {
|
|
1597
|
+
display: flex;
|
|
1598
|
+
justify-content: space-between;
|
|
1599
|
+
align-items: center;
|
|
1600
|
+
gap: 8px;
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
.pr-helper-history-model {
|
|
1604
|
+
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
|
|
1605
|
+
font-size: 12px;
|
|
1606
|
+
font-weight: 500;
|
|
1607
|
+
color: var(--color-fg-muted, #57606a);
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
.pr-helper-history-risk {
|
|
1611
|
+
font-size: 11px;
|
|
1612
|
+
font-weight: 700;
|
|
1613
|
+
text-transform: uppercase;
|
|
1614
|
+
letter-spacing: 0.04em;
|
|
1615
|
+
padding: 3px 9px;
|
|
1616
|
+
border-radius: 100px;
|
|
1617
|
+
flex-shrink: 0;
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
.pr-helper-history-issues,
|
|
1621
|
+
.pr-helper-history-options {
|
|
1622
|
+
font-size: 12px;
|
|
1623
|
+
color: var(--color-fg-muted, #57606a);
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
.pr-helper-history-time {
|
|
1627
|
+
font-size: 12px;
|
|
1628
|
+
font-weight: 600;
|
|
1629
|
+
color: var(--color-fg-muted, #57606a);
|
|
1630
|
+
flex-shrink: 0;
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
.pr-helper-history-status {
|
|
1634
|
+
font-size: 10px;
|
|
1635
|
+
font-weight: 700;
|
|
1636
|
+
text-transform: uppercase;
|
|
1637
|
+
letter-spacing: 0.04em;
|
|
1638
|
+
flex-shrink: 0;
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
.pr-helper-history-status-generating .pr-helper-history-status { color: #9333ea; }
|
|
1642
|
+
.pr-helper-history-status-failed .pr-helper-history-status { color: #cf222e; }
|
|
1643
|
+
|
|
1644
|
+
/* ── Assistant tab ── */
|
|
1645
|
+
/* The panel is the scroll container for every other tab; the Assistant instead owns the full
|
|
1646
|
+
height and scrolls internally, so the composer can sit outside the scrolled region. Scoped
|
|
1647
|
+
with :has() so this only applies while the Assistant tab is mounted — no class to toggle
|
|
1648
|
+
in content.ts and nothing to unset when another tab renders over it. */
|
|
1649
|
+
.pr-helper-review-panel:has(> .pr-helper-chat) {
|
|
1650
|
+
display: flex;
|
|
1651
|
+
flex-direction: column;
|
|
1652
|
+
overflow: hidden;
|
|
1653
|
+
padding: 0;
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
.pr-helper-chat {
|
|
1657
|
+
flex: 1;
|
|
1658
|
+
min-height: 0;
|
|
1659
|
+
display: flex;
|
|
1660
|
+
flex-direction: column;
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
/* Only the conversation scrolls; the chips live in the pinned composer below it. */
|
|
1664
|
+
.pr-helper-chat-scroll {
|
|
1665
|
+
flex: 1;
|
|
1666
|
+
min-height: 0;
|
|
1667
|
+
overflow-y: auto;
|
|
1668
|
+
padding: 14px 16px;
|
|
1669
|
+
/* The Assistant's canvas. Its panel drops padding to zero and delegates scrolling here, so the
|
|
1670
|
+
recess has to be applied to this element rather than to the panel. */
|
|
1671
|
+
background: #f8fafc;
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
.pr-helper-chat-composer {
|
|
1675
|
+
flex-shrink: 0;
|
|
1676
|
+
padding: 10px 16px 12px;
|
|
1677
|
+
background: #ffffff;
|
|
1678
|
+
border-top: 1px solid var(--color-border-default, #d0d7de);
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
.pr-helper-chat-log {
|
|
1682
|
+
display: flex;
|
|
1683
|
+
flex-direction: column;
|
|
1684
|
+
gap: 8px;
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
.pr-helper-chat-msg {
|
|
1688
|
+
font-size: 13px;
|
|
1689
|
+
padding: 10px 12px;
|
|
1690
|
+
border-radius: 8px;
|
|
1691
|
+
max-width: 90%;
|
|
1692
|
+
line-height: 1.5;
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
/* Full width, unlike the user bubble: a reply now carries headings, lists and code blocks,
|
|
1696
|
+
and holding it to 90% only buys an indent that costs a wrapped line on every list item. */
|
|
1697
|
+
.pr-helper-chat-msg.role-assistant {
|
|
1698
|
+
background: rgba(147, 51, 234, 0.08);
|
|
1699
|
+
align-self: flex-start;
|
|
1700
|
+
max-width: 100%;
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
.pr-helper-chat-msg.role-user {
|
|
1704
|
+
background: #0b0f17;
|
|
1705
|
+
color: #f8fafc;
|
|
1706
|
+
align-self: flex-end;
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
/* Markdown lands on assistant bubbles only (see renderAssistantTab), so everything below is
|
|
1710
|
+
styled for the light bubble above and never has to survive the dark user one. Scoped to
|
|
1711
|
+
the bubble class so none of it leaks into the other tabs' prose. */
|
|
1712
|
+
.pr-helper-markdown > *:first-child { margin-top: 0; }
|
|
1713
|
+
.pr-helper-markdown > *:last-child { margin-bottom: 0; }
|
|
1714
|
+
|
|
1715
|
+
.pr-helper-markdown p { margin: 0 0 10px; }
|
|
1716
|
+
|
|
1717
|
+
/* h4-h6 in the markup — sized as section labels rather than document headings, because the
|
|
1718
|
+
bubble they sit in is 13px and the panel already owns the real heading hierarchy. */
|
|
1719
|
+
.pr-helper-markdown h4,
|
|
1720
|
+
.pr-helper-markdown h5,
|
|
1721
|
+
.pr-helper-markdown h6 {
|
|
1722
|
+
margin: 14px 0 6px;
|
|
1723
|
+
font-weight: 600;
|
|
1724
|
+
line-height: 1.35;
|
|
1725
|
+
color: var(--color-fg-default, #24292f);
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
.pr-helper-markdown h4 { font-size: 13px; }
|
|
1729
|
+
.pr-helper-markdown h5 { font-size: 12.5px; }
|
|
1730
|
+
.pr-helper-markdown h6 { font-size: 12px; }
|
|
1731
|
+
|
|
1732
|
+
.pr-helper-markdown ul,
|
|
1733
|
+
.pr-helper-markdown ol {
|
|
1734
|
+
margin: 0 0 10px;
|
|
1735
|
+
padding-left: 18px;
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
.pr-helper-markdown li + li { margin-top: 4px; }
|
|
1739
|
+
|
|
1740
|
+
/* The sidebar is narrow and a code block must never widen it — the block scrolls, the panel
|
|
1741
|
+
doesn't. min-width:0 lets the pre actually shrink inside the flex column above it. */
|
|
1742
|
+
.pr-helper-markdown pre {
|
|
1743
|
+
margin: 0 0 10px;
|
|
1744
|
+
padding: 10px 12px;
|
|
1745
|
+
min-width: 0;
|
|
1746
|
+
overflow-x: auto;
|
|
1747
|
+
background: var(--color-neutral-muted, rgba(175, 184, 193, 0.2));
|
|
1748
|
+
border-radius: 6px;
|
|
1749
|
+
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
|
|
1750
|
+
font-size: 12px;
|
|
1751
|
+
line-height: 1.45;
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
/* ── The copy control on a fenced block ──
|
|
1755
|
+
The wrapper carries the block's bottom margin so the <pre> can sit flush against the button's
|
|
1756
|
+
corner, and so the surrounding stylesheets keep addressing one element per block.
|
|
1757
|
+
|
|
1758
|
+
Shown, not revealed on hover: the reason the button exists is that a snippet in a box that
|
|
1759
|
+
scrolls sideways is awkward to select, and a control you have to find by moving the pointer over
|
|
1760
|
+
the thing is barely better than the selection it replaces. */
|
|
1761
|
+
.pr-helper-code-block {
|
|
1762
|
+
position: relative;
|
|
1763
|
+
margin: 0 0 10px;
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
.pr-helper-code-block > pre { margin: 0; }
|
|
1767
|
+
|
|
1768
|
+
.pr-helper-code-copy {
|
|
1769
|
+
position: absolute;
|
|
1770
|
+
top: 6px;
|
|
1771
|
+
right: 6px;
|
|
1772
|
+
padding: 2px 8px;
|
|
1773
|
+
border: 1px solid var(--color-border-default, #d0d7de);
|
|
1774
|
+
border-radius: 100px;
|
|
1775
|
+
background: var(--color-canvas-default, #fff);
|
|
1776
|
+
color: var(--color-fg-muted, #57606a);
|
|
1777
|
+
font-family: inherit;
|
|
1778
|
+
font-size: 11px;
|
|
1779
|
+
font-weight: 600;
|
|
1780
|
+
line-height: 18px;
|
|
1781
|
+
cursor: pointer;
|
|
1782
|
+
/* The block scrolls under it, so it needs to sit on its own ground rather than let code slide
|
|
1783
|
+
through it. */
|
|
1784
|
+
opacity: 0.9;
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
.pr-helper-code-copy:hover {
|
|
1788
|
+
opacity: 1;
|
|
1789
|
+
border-color: #9333ea;
|
|
1790
|
+
color: #9333ea;
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
.pr-helper-code-copy.copied {
|
|
1794
|
+
border-color: #1a7f37;
|
|
1795
|
+
color: #1a7f37;
|
|
1796
|
+
opacity: 1;
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
/* Inline code is a chip; inside a block that background and padding would double up. */
|
|
1800
|
+
.pr-helper-markdown pre code {
|
|
1801
|
+
background: none;
|
|
1802
|
+
padding: 0;
|
|
1803
|
+
font-size: inherit;
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
.pr-helper-markdown blockquote {
|
|
1807
|
+
margin: 0 0 10px;
|
|
1808
|
+
padding-left: 10px;
|
|
1809
|
+
border-left: 3px solid var(--color-border-default, #d0d7de);
|
|
1810
|
+
color: var(--color-fg-muted, #57606a);
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
.pr-helper-markdown hr {
|
|
1814
|
+
margin: 12px 0;
|
|
1815
|
+
border: none;
|
|
1816
|
+
border-top: 1px solid var(--color-border-default, #d0d7de);
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
.pr-helper-markdown a {
|
|
1820
|
+
color: #0969da;
|
|
1821
|
+
text-decoration: none;
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
.pr-helper-markdown a:hover { text-decoration: underline; }
|
|
1825
|
+
|
|
1826
|
+
.pr-helper-chat-input-row {
|
|
1827
|
+
display: flex;
|
|
1828
|
+
gap: 6px;
|
|
1829
|
+
align-items: flex-end;
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
.pr-helper-chat-input {
|
|
1833
|
+
flex: 1;
|
|
1834
|
+
padding: 8px 10px;
|
|
1835
|
+
border: 1px solid var(--color-border-default, #d0d7de);
|
|
1836
|
+
border-radius: 8px;
|
|
1837
|
+
font-size: 13px;
|
|
1838
|
+
font-family: inherit;
|
|
1839
|
+
outline: none;
|
|
1840
|
+
resize: none;
|
|
1841
|
+
max-height: 96px;
|
|
1842
|
+
line-height: 1.5;
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
.pr-helper-chat-input:focus {
|
|
1846
|
+
border-color: #9333ea;
|
|
1847
|
+
box-shadow: 0 0 0 2px rgba(147, 51, 234, 0.15);
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
/* Takes the textarea's place while a reply is in flight. Mirrors its box model exactly —
|
|
1851
|
+
same padding, a transparent border standing in for the real one, and a 1.5-line content
|
|
1852
|
+
box — so the swap never changes the composer's height and the Cancel button beside it
|
|
1853
|
+
doesn't shift. */
|
|
1854
|
+
.pr-helper-chat-thinking {
|
|
1855
|
+
flex: 1;
|
|
1856
|
+
display: flex;
|
|
1857
|
+
align-items: center;
|
|
1858
|
+
justify-content: center;
|
|
1859
|
+
box-sizing: content-box;
|
|
1860
|
+
padding: 8px 10px;
|
|
1861
|
+
border: 1px solid transparent;
|
|
1862
|
+
font-size: 13px;
|
|
1863
|
+
line-height: 1.5;
|
|
1864
|
+
min-height: 1.5em;
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
/* An author display:flex would otherwise beat the UA stylesheet's [hidden] { display: none }. */
|
|
1868
|
+
.pr-helper-chat-thinking[hidden] { display: none; }
|
|
1869
|
+
|
|
1870
|
+
/* The dots are shared with the Overview's generating state, where they sit in a centred
|
|
1871
|
+
column with the status copy below them — that bottom margin has no place in a composer row. */
|
|
1872
|
+
.pr-helper-chat-thinking .pr-helper-thinking-dots { margin: 0; }
|
|
1873
|
+
|
|
1874
|
+
.pr-helper-chat-send {
|
|
1875
|
+
padding: 8px 14px;
|
|
1876
|
+
border: none;
|
|
1877
|
+
border-radius: 8px;
|
|
1878
|
+
background: #9333ea;
|
|
1879
|
+
color: #ffffff;
|
|
1880
|
+
font-size: 13px;
|
|
1881
|
+
font-weight: 600;
|
|
1882
|
+
font-family: inherit;
|
|
1883
|
+
cursor: pointer;
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
.pr-helper-chat-send:hover { background: #7c3aed; }
|
|
1887
|
+
|
|
1888
|
+
/* Same button, destructive colour while a request is in flight — clicking it aborts the
|
|
1889
|
+
reply rather than sending anything. */
|
|
1890
|
+
.pr-helper-chat-send-cancel { background: #cf222e; }
|
|
1891
|
+
.pr-helper-chat-send-cancel:hover { background: #a40e26; }
|
|
1892
|
+
|
|
1893
|
+
/* The chips sit in the composer, above the input row. Capped and independently scrollable so
|
|
1894
|
+
a session with many section prompts can never push the input off the bottom of the panel. */
|
|
1895
|
+
.pr-helper-chat-suggestions { margin-bottom: 8px; }
|
|
1896
|
+
|
|
1897
|
+
.pr-helper-chat-suggestions-list {
|
|
1898
|
+
display: flex;
|
|
1899
|
+
flex-direction: column;
|
|
1900
|
+
gap: 8px;
|
|
1901
|
+
max-height: 30vh;
|
|
1902
|
+
overflow-y: auto;
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
.pr-helper-chat-suggestions-list[hidden] { display: none; }
|
|
1906
|
+
|
|
1907
|
+
/* Deliberately quieter than the chips it reveals: once the reviewer is typing their own
|
|
1908
|
+
questions, this is a door they may never open again, not a call to action. */
|
|
1909
|
+
.pr-helper-suggestions-toggle {
|
|
1910
|
+
display: flex;
|
|
1911
|
+
align-items: center;
|
|
1912
|
+
gap: 5px;
|
|
1913
|
+
padding: 0;
|
|
1914
|
+
border: none;
|
|
1915
|
+
background: none;
|
|
1916
|
+
font-size: 11px;
|
|
1917
|
+
font-weight: 700;
|
|
1918
|
+
font-family: inherit;
|
|
1919
|
+
text-transform: uppercase;
|
|
1920
|
+
letter-spacing: 0.06em;
|
|
1921
|
+
color: #94a3b8;
|
|
1922
|
+
cursor: pointer;
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
.pr-helper-suggestions-toggle:hover { color: #9333ea; }
|
|
1926
|
+
|
|
1927
|
+
.pr-helper-suggestions-toggle::before {
|
|
1928
|
+
content: '';
|
|
1929
|
+
width: 0;
|
|
1930
|
+
height: 0;
|
|
1931
|
+
border-left: 4px solid currentColor;
|
|
1932
|
+
border-top: 3.5px solid transparent;
|
|
1933
|
+
border-bottom: 3.5px solid transparent;
|
|
1934
|
+
transition: transform 0.15s ease;
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
.pr-helper-suggestions-toggle[aria-expanded='true']::before { transform: rotate(90deg); }
|
|
1938
|
+
|
|
1939
|
+
/* Open, the toggle is a label for the list right beneath it rather than a lone control. */
|
|
1940
|
+
.pr-helper-suggestions-toggle[aria-expanded='true'] { margin-bottom: 8px; }
|
|
1941
|
+
|
|
1942
|
+
.pr-helper-quick-prompts {
|
|
1943
|
+
display: flex;
|
|
1944
|
+
flex-wrap: wrap;
|
|
1945
|
+
gap: 6px;
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
.pr-helper-quick-prompt-button {
|
|
1949
|
+
font-size: 12px;
|
|
1950
|
+
font-weight: 600;
|
|
1951
|
+
font-family: inherit;
|
|
1952
|
+
padding: 6px 12px;
|
|
1953
|
+
border-radius: 100px;
|
|
1954
|
+
border: 1px solid var(--color-border-default, #d0d7de);
|
|
1955
|
+
background: transparent;
|
|
1956
|
+
color: var(--color-fg-default, #24292f);
|
|
1957
|
+
cursor: pointer;
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
.pr-helper-quick-prompt-button:hover { border-color: #9333ea; color: #9333ea; }
|
|
1961
|
+
|
|
1962
|
+
.pr-helper-quick-prompts-section-group,
|
|
1963
|
+
.pr-helper-quick-prompts-saved-group { display: contents; }
|
|
1964
|
+
|
|
1965
|
+
/* ── The reviewer's own prompts ──
|
|
1966
|
+
The chip and its edit control read as one object, so the border and the hover colour belong to
|
|
1967
|
+
the chip and not to either half. When each half carried its own, they drew themselves in
|
|
1968
|
+
different greys and only the side under the pointer lit up, which split one prompt into two
|
|
1969
|
+
controls that happened to be touching. */
|
|
1970
|
+
.pr-helper-saved-prompt-chip {
|
|
1971
|
+
display: inline-flex;
|
|
1972
|
+
align-items: stretch;
|
|
1973
|
+
/* The same pill as every other prompt button. It was 6px, so a saved prompt was round on the
|
|
1974
|
+
left where the button's own radius showed through and square on the right where this one did,
|
|
1975
|
+
which reads as an unfinished chip rather than as a different kind of one. */
|
|
1976
|
+
border-radius: 100px;
|
|
1977
|
+
border: 1px solid var(--color-border-default, #d0d7de);
|
|
1978
|
+
color: var(--color-fg-default, #24292f);
|
|
1979
|
+
overflow: hidden;
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
.pr-helper-saved-prompt-chip:hover { border-color: #9333ea; color: #9333ea; }
|
|
1983
|
+
|
|
1984
|
+
/* Both halves are surfaces on the chip: no border of their own, no radius of their own, and the
|
|
1985
|
+
colour they draw in is whatever the chip is currently showing. */
|
|
1986
|
+
.pr-helper-saved-prompt-chip .pr-helper-saved-prompt-use,
|
|
1987
|
+
.pr-helper-saved-prompt-chip .pr-helper-saved-prompt-edit {
|
|
1988
|
+
border: 0;
|
|
1989
|
+
border-radius: 0;
|
|
1990
|
+
background: transparent;
|
|
1991
|
+
color: inherit;
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
.pr-helper-saved-prompt-chip .pr-helper-saved-prompt-use:hover { color: inherit; }
|
|
1995
|
+
|
|
1996
|
+
.pr-helper-saved-prompt-edit {
|
|
1997
|
+
/* Wider on the right than the left: the pencil sits inside a curve now, and even padding would
|
|
1998
|
+
crowd it against the arc. */
|
|
1999
|
+
padding: 0 11px 0 7px;
|
|
2000
|
+
font: inherit;
|
|
2001
|
+
font-size: 11px;
|
|
2002
|
+
opacity: 0.55;
|
|
2003
|
+
cursor: pointer;
|
|
2004
|
+
}
|
|
2005
|
+
.pr-helper-saved-prompt-edit:hover { opacity: 1; }
|
|
2006
|
+
|
|
2007
|
+
/* ── The prompt editor ──
|
|
2008
|
+
A name above a paragraph. The two fields were bare boxes with a hairline around them, which
|
|
2009
|
+
read as a form someone had not finished rather than as the place a prompt is written. */
|
|
2010
|
+
.pr-helper-saved-prompt-label {
|
|
2011
|
+
display: block;
|
|
2012
|
+
margin: 0 0 6px;
|
|
2013
|
+
font-size: 11px;
|
|
2014
|
+
font-weight: 600;
|
|
2015
|
+
letter-spacing: 0.03em;
|
|
2016
|
+
text-transform: uppercase;
|
|
2017
|
+
color: var(--color-fg-muted, #57606a);
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
/* One rule for both, because a name and a prompt are the same kind of thing to type into and two
|
|
2021
|
+
sets of numbers is how they drift apart. Filled rather than outlined: on a white card an inset
|
|
2022
|
+
field says "type here" without needing a heavier border to do it. */
|
|
2023
|
+
.pr-helper-saved-prompt-name,
|
|
2024
|
+
.pr-helper-saved-prompt-text {
|
|
2025
|
+
width: 100%;
|
|
2026
|
+
box-sizing: border-box;
|
|
2027
|
+
padding: 9px 11px;
|
|
2028
|
+
border: 1px solid rgba(0, 0, 0, 0.12);
|
|
2029
|
+
border-radius: 8px;
|
|
2030
|
+
background: #f6f8fa;
|
|
2031
|
+
color: #1f2328;
|
|
2032
|
+
font: inherit;
|
|
2033
|
+
font-size: 13px;
|
|
2034
|
+
line-height: 1.5;
|
|
2035
|
+
transition: border-color 0.12s ease, box-shadow 0.12s ease, background 0.12s ease;
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
.pr-helper-saved-prompt-name::placeholder,
|
|
2039
|
+
.pr-helper-saved-prompt-text::placeholder { color: #8c959f; }
|
|
2040
|
+
|
|
2041
|
+
.pr-helper-saved-prompt-name:hover,
|
|
2042
|
+
.pr-helper-saved-prompt-text:hover { border-color: rgba(0, 0, 0, 0.22); }
|
|
2043
|
+
|
|
2044
|
+
/* The field lifts to white on focus and takes the product's purple, so the one being typed into
|
|
2045
|
+
is obvious without moving anything. outline: none is safe here only because the ring below
|
|
2046
|
+
replaces it; it is a visible focus indicator, not the absence of one. */
|
|
2047
|
+
.pr-helper-saved-prompt-name:focus,
|
|
2048
|
+
.pr-helper-saved-prompt-text:focus {
|
|
2049
|
+
outline: none;
|
|
2050
|
+
background: #ffffff;
|
|
2051
|
+
border-color: #9333ea;
|
|
2052
|
+
box-shadow: 0 0 0 3px rgba(147, 51, 234, 0.16);
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
.pr-helper-saved-prompt-name { margin-bottom: 16px; }
|
|
2056
|
+
|
|
2057
|
+
.pr-helper-saved-prompt-text {
|
|
2058
|
+
min-height: 132px;
|
|
2059
|
+
resize: vertical;
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
/* The field that was rejected says so itself. An error message under a form with three controls
|
|
2063
|
+
leaves the reviewer to work out which one it is about. */
|
|
2064
|
+
.pr-helper-saved-prompt-name[aria-invalid='true'],
|
|
2065
|
+
.pr-helper-saved-prompt-text[aria-invalid='true'] {
|
|
2066
|
+
border-color: #cf222e;
|
|
2067
|
+
background: rgba(207, 34, 46, 0.04);
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
.pr-helper-saved-prompt-name[aria-invalid='true']:focus,
|
|
2071
|
+
.pr-helper-saved-prompt-text[aria-invalid='true']:focus {
|
|
2072
|
+
border-color: #cf222e;
|
|
2073
|
+
box-shadow: 0 0 0 3px rgba(207, 34, 46, 0.14);
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
.pr-helper-saved-prompt-error {
|
|
2077
|
+
display: flex;
|
|
2078
|
+
align-items: baseline;
|
|
2079
|
+
gap: 6px;
|
|
2080
|
+
margin: 10px 0 0;
|
|
2081
|
+
font-size: 12px;
|
|
2082
|
+
line-height: 1.5;
|
|
2083
|
+
color: #cf222e;
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
.pr-helper-saved-prompt-error::before {
|
|
2087
|
+
content: '!';
|
|
2088
|
+
flex: none;
|
|
2089
|
+
width: 15px;
|
|
2090
|
+
height: 15px;
|
|
2091
|
+
border-radius: 50%;
|
|
2092
|
+
background: #cf222e;
|
|
2093
|
+
color: #ffffff;
|
|
2094
|
+
font-size: 10px;
|
|
2095
|
+
font-weight: 700;
|
|
2096
|
+
line-height: 15px;
|
|
2097
|
+
text-align: center;
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
.pr-helper-quick-prompts-divider {
|
|
2101
|
+
font-size: 10px;
|
|
2102
|
+
font-weight: 700;
|
|
2103
|
+
text-transform: uppercase;
|
|
2104
|
+
letter-spacing: 0.06em;
|
|
2105
|
+
color: #94a3b8;
|
|
2106
|
+
margin-bottom: 6px;
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
/* display:contents above, so the divider and its chip row become items of the suggestions
|
|
2110
|
+
stack directly — one gap governs the whole footer instead of a margin per group. */
|
|
2111
|
+
|
|
2112
|
+
/* ── Header — dark brand strip ── */
|
|
2113
|
+
.pr-helper-sidebar-header {
|
|
2114
|
+
background: #0b0f17;
|
|
2115
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
|
2116
|
+
padding: 12px 16px 14px 20px;
|
|
2117
|
+
flex-shrink: 0;
|
|
2118
|
+
position: relative;
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
.pr-helper-sidebar-header::before {
|
|
2122
|
+
content: '';
|
|
2123
|
+
position: absolute;
|
|
2124
|
+
left: 0;
|
|
2125
|
+
top: 0;
|
|
2126
|
+
bottom: 0;
|
|
2127
|
+
width: 3px;
|
|
2128
|
+
background: #9333ea;
|
|
2129
|
+
border-radius: 0 2px 2px 0;
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2132
|
+
.pr-helper-header-top {
|
|
2133
|
+
display: flex;
|
|
2134
|
+
justify-content: space-between;
|
|
2135
|
+
align-items: center;
|
|
2136
|
+
margin-bottom: 8px;
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2139
|
+
/* The wordmark, two-tone: "pr" and "buddy" in the accent purple, "review" in the neutral ink.
|
|
2140
|
+
This header is dark (#0b0f17 above), so the ink is near-white. Values are literal rather than
|
|
2141
|
+
tokens because this sheet is injected into GitHub, where our :root never applies. */
|
|
2142
|
+
.pr-helper-brand-text {
|
|
2143
|
+
font-family: 'Comfortaa', ui-rounded, 'SF Pro Rounded', 'Segoe UI Rounded', system-ui, sans-serif;
|
|
2144
|
+
font-size: 14px;
|
|
2145
|
+
font-weight: 700;
|
|
2146
|
+
letter-spacing: 0;
|
|
2147
|
+
text-transform: none;
|
|
2148
|
+
color: #f8fafc;
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
.pr-helper-brand-text .pr-helper-wordmark-accent { color: #a855f7; }
|
|
2152
|
+
|
|
2153
|
+
/* Anchors the bookmark so it is a sibling of the title rather than something the title has to
|
|
2154
|
+
flow around. Matches .pr-meta in popup.css, which solves the identical problem. */
|
|
2155
|
+
.pr-helper-pr-headline {
|
|
2156
|
+
position: relative;
|
|
2157
|
+
margin-bottom: 6px;
|
|
2158
|
+
}
|
|
2159
|
+
|
|
2160
|
+
/* Two lines, clamped — not one line ellipsised as before. A PR title carrying a ticket prefix
|
|
2161
|
+
("PR #38921 · [INF-3765] …") spends most of its first line before reaching any words that
|
|
2162
|
+
distinguish it, so a single line truncated at the sidebar's width told the reviewer almost
|
|
2163
|
+
nothing. The right padding reserves the bookmark's column so the second line clamps beside it. */
|
|
2164
|
+
.pr-helper-pr-title {
|
|
2165
|
+
font-size: 14px;
|
|
2166
|
+
font-weight: 600;
|
|
2167
|
+
color: #f8fafc;
|
|
2168
|
+
line-height: 1.35;
|
|
2169
|
+
padding-right: 32px;
|
|
2170
|
+
display: -webkit-box;
|
|
2171
|
+
-webkit-box-orient: vertical;
|
|
2172
|
+
-webkit-line-clamp: 2;
|
|
2173
|
+
overflow: hidden;
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
/* The popup's .pr-meta-stats: byline, then size, as plain coloured text rather than pills. The
|
|
2177
|
+
pills these used to be competed with the severity badges further down the sidebar, which are the
|
|
2178
|
+
ones that actually mean something. */
|
|
2179
|
+
.pr-helper-header-meta {
|
|
2180
|
+
display: flex;
|
|
2181
|
+
align-items: center;
|
|
2182
|
+
gap: 10px;
|
|
2183
|
+
flex-wrap: wrap;
|
|
2184
|
+
font-size: 12px;
|
|
2185
|
+
color: #94a3b8;
|
|
2186
|
+
}
|
|
2187
|
+
|
|
2188
|
+
.pr-helper-additions-stat { color: #4ade80; }
|
|
2189
|
+
.pr-helper-deletions-stat { color: #f87171; }
|
|
2190
|
+
|
|
2191
|
+
/* ── Save for later (header bookmark) ──
|
|
2192
|
+
The popup's .pr-meta-save, positioned against the headline for the same reason: it acts on the
|
|
2193
|
+
PR the title names. Saved state is the filled/outline path swap plus the brand colour, never
|
|
2194
|
+
colour alone — see the aria-pressed written alongside it in paintSavedForLaterButton. */
|
|
2195
|
+
.pr-helper-pr-save {
|
|
2196
|
+
position: absolute;
|
|
2197
|
+
top: -2px;
|
|
2198
|
+
right: 0;
|
|
2199
|
+
width: 26px;
|
|
2200
|
+
height: 26px;
|
|
2201
|
+
padding: 0;
|
|
2202
|
+
border: none;
|
|
2203
|
+
background: transparent;
|
|
2204
|
+
color: #94a3b8;
|
|
2205
|
+
border-radius: 6px;
|
|
2206
|
+
cursor: pointer;
|
|
2207
|
+
display: flex;
|
|
2208
|
+
align-items: center;
|
|
2209
|
+
justify-content: center;
|
|
2210
|
+
transition: background 0.15s, color 0.15s;
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2213
|
+
.pr-helper-pr-save svg { fill: currentColor; }
|
|
2214
|
+
.pr-helper-pr-save:hover { background: rgba(255, 255, 255, 0.08); color: #f8fafc; }
|
|
2215
|
+
|
|
2216
|
+
.pr-helper-bookmark-filled { display: none; }
|
|
2217
|
+
.pr-helper-pr-save.saved { color: #a855f7; }
|
|
2218
|
+
.pr-helper-pr-save.saved .pr-helper-bookmark-outline { display: none; }
|
|
2219
|
+
.pr-helper-pr-save.saved .pr-helper-bookmark-filled { display: inline; }
|
|
2220
|
+
.pr-helper-pr-save.saved:hover { background: rgba(168, 85, 247, 0.16); color: #a855f7; }
|
|
2221
|
+
|
|
2222
|
+
/* The byline leads the meta row. Tighter gap than the row's own 6px so the avatar and login read
|
|
2223
|
+
as one unit rather than as two more badges, and no pill treatment: it names a person, while the
|
|
2224
|
+
badges beside it measure the diff. */
|
|
2225
|
+
.pr-helper-pr-author {
|
|
2226
|
+
display: flex;
|
|
2227
|
+
align-items: center;
|
|
2228
|
+
gap: 5px;
|
|
2229
|
+
min-width: 0;
|
|
2230
|
+
max-width: 45%;
|
|
2231
|
+
flex: 0 1 auto;
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
/* Scoped rather than a bare `.hidden`: this sheet is injected into github.com, where an
|
|
2235
|
+
unprefixed class would collide with GitHub's own. Same precedent as the footer's rule above. */
|
|
2236
|
+
.pr-helper-pr-author.hidden { display: none; }
|
|
2237
|
+
|
|
2238
|
+
/* Sized to the 11px meta line, not to the 28px Saved Reviews row: here the avatar is an accent on
|
|
2239
|
+
a line of text, not the leading element of a list row. Colours are literal because this sheet
|
|
2240
|
+
is injected into GitHub, where our :root tokens never apply. */
|
|
2241
|
+
.pr-helper-pr-author-avatar {
|
|
2242
|
+
position: relative;
|
|
2243
|
+
width: 16px;
|
|
2244
|
+
height: 16px;
|
|
2245
|
+
border-radius: 50%;
|
|
2246
|
+
flex-shrink: 0;
|
|
2247
|
+
background: rgba(255, 255, 255, 0.08);
|
|
2248
|
+
display: flex;
|
|
2249
|
+
align-items: center;
|
|
2250
|
+
justify-content: center;
|
|
2251
|
+
font-size: 9px;
|
|
2252
|
+
font-weight: 600;
|
|
2253
|
+
line-height: 1;
|
|
2254
|
+
color: #94a3b8;
|
|
2255
|
+
text-transform: uppercase;
|
|
2256
|
+
overflow: hidden;
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
/* Covers the initial underneath, so a failed load needs no more than removing this element. */
|
|
2260
|
+
.pr-helper-pr-author-avatar img {
|
|
2261
|
+
position: absolute;
|
|
2262
|
+
inset: 0;
|
|
2263
|
+
width: 100%;
|
|
2264
|
+
height: 100%;
|
|
2265
|
+
border-radius: 50%;
|
|
2266
|
+
object-fit: cover;
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2269
|
+
.pr-helper-pr-author-name {
|
|
2270
|
+
font-size: 11px;
|
|
2271
|
+
color: #cbd5e1;
|
|
2272
|
+
overflow: hidden;
|
|
2273
|
+
text-overflow: ellipsis;
|
|
2274
|
+
white-space: nowrap;
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
.pr-helper-pr-branch {
|
|
2278
|
+
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
|
|
2279
|
+
font-size: 11px;
|
|
2280
|
+
color: #64748b;
|
|
2281
|
+
background: rgba(255, 255, 255, 0.05);
|
|
2282
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
2283
|
+
border-radius: 4px;
|
|
2284
|
+
padding: 2px 7px;
|
|
2285
|
+
white-space: nowrap;
|
|
2286
|
+
overflow: hidden;
|
|
2287
|
+
text-overflow: ellipsis;
|
|
2288
|
+
/* Was a flat 160px cap, which clipped branch names well short of the header's actual
|
|
2289
|
+
width. flex-grow:0 keeps the pill hugging its own text like the FILES badge next to
|
|
2290
|
+
it, rather than stretching to fill the row's leftover space — it only shrinks (and
|
|
2291
|
+
then ellipsizes) once the row genuinely runs out of room. min-width:0 is required for
|
|
2292
|
+
a flex item's text-overflow to ever kick in — without it the row would just overflow
|
|
2293
|
+
instead of truncating. */
|
|
2294
|
+
flex: 0 1 auto;
|
|
2295
|
+
min-width: 0;
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
/* Workspace controls, grouped: Back/Forward belong with Hide rather than with the branding,
|
|
2299
|
+
which is also what says they act on the sidebar and not on the page under it. */
|
|
2300
|
+
.pr-helper-header-actions {
|
|
2301
|
+
display: flex;
|
|
2302
|
+
align-items: center;
|
|
2303
|
+
gap: 2px;
|
|
2304
|
+
flex-shrink: 0;
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
/* Separates navigation from Hide so three icons in a row don't read as one control group. */
|
|
2308
|
+
.pr-helper-header-actions-divider {
|
|
2309
|
+
width: 1px;
|
|
2310
|
+
height: 14px;
|
|
2311
|
+
background: rgba(255, 255, 255, 0.12);
|
|
2312
|
+
margin: 0 4px;
|
|
2313
|
+
}
|
|
2314
|
+
|
|
2315
|
+
.pr-helper-collapse-button,
|
|
2316
|
+
.pr-helper-nav-button {
|
|
2317
|
+
padding: 4px;
|
|
2318
|
+
background: none;
|
|
2319
|
+
border: none;
|
|
2320
|
+
cursor: pointer;
|
|
2321
|
+
color: #64748b;
|
|
2322
|
+
border-radius: 4px;
|
|
2323
|
+
display: flex;
|
|
2324
|
+
align-items: center;
|
|
2325
|
+
font-family: inherit;
|
|
2326
|
+
transition: color 0.15s;
|
|
2327
|
+
flex-shrink: 0;
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2330
|
+
.pr-helper-collapse-button:hover,
|
|
2331
|
+
.pr-helper-nav-button:not(:disabled):hover { color: #f8fafc; }
|
|
2332
|
+
|
|
2333
|
+
.pr-helper-collapse-button svg,
|
|
2334
|
+
.pr-helper-nav-button svg { width: 16px; height: 16px; }
|
|
2335
|
+
|
|
2336
|
+
/* The real disabled attribute here, unlike the review tabs, which use aria-disabled
|
|
2337
|
+
specifically to keep their explanatory tooltip alive through pointer events. A Back button
|
|
2338
|
+
with nowhere to go has nothing to explain, so swallowing the events is the better trade and
|
|
2339
|
+
comes with the right semantics for assistive tech for free. */
|
|
2340
|
+
.pr-helper-nav-button:disabled {
|
|
2341
|
+
color: #334155;
|
|
2342
|
+
cursor: default;
|
|
2343
|
+
}
|
|
2344
|
+
|
|
2345
|
+
/* The Files tab (search + list) owns the panel's full height and scrolls internally, the
|
|
2346
|
+
same way the Assistant tab does — see the :has() rule above it — so the search bar can
|
|
2347
|
+
stay pinned above a scrolling list instead of scrolling away with it. Scoped with
|
|
2348
|
+
:has() rather than a class so nothing needs unsetting when another tab renders over it. */
|
|
2349
|
+
.pr-helper-review-panel:has(> .pr-helper-search-container) {
|
|
2350
|
+
display: flex;
|
|
2351
|
+
flex-direction: column;
|
|
2352
|
+
overflow: hidden;
|
|
2353
|
+
padding: 0;
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2356
|
+
/* ── Search ── */
|
|
2357
|
+
.pr-helper-search-container {
|
|
2358
|
+
flex-shrink: 0;
|
|
2359
|
+
padding: 16px 16px 14px;
|
|
2360
|
+
border-bottom: 1px solid var(--color-border-default, #d0d7de);
|
|
2361
|
+
background: #ffffff;
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2364
|
+
/* The count's positioning anchor, and the reason it exists: .pr-helper-search-container has
|
|
2365
|
+
asymmetric padding, so an absolute count anchored to it centres on the container's box rather
|
|
2366
|
+
than the input's and lands on the input's right border. This box *is* the input's box. */
|
|
2367
|
+
.pr-helper-search-field {
|
|
2368
|
+
position: relative;
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
.pr-helper-search-input {
|
|
2372
|
+
width: 100%;
|
|
2373
|
+
padding: 10px 36px 10px 12px;
|
|
2374
|
+
border: 1px solid var(--color-border-default, #d0d7de);
|
|
2375
|
+
border-radius: 8px;
|
|
2376
|
+
font-size: 14px;
|
|
2377
|
+
line-height: 20px;
|
|
2378
|
+
color: var(--color-fg-default, #24292f);
|
|
2379
|
+
background: #ffffff;
|
|
2380
|
+
box-sizing: border-box;
|
|
2381
|
+
font-family: inherit;
|
|
2382
|
+
outline: none;
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2385
|
+
.pr-helper-search-input:focus {
|
|
2386
|
+
border-color: #9333ea;
|
|
2387
|
+
box-shadow: 0 0 0 2px rgba(147, 51, 234, 0.15);
|
|
2388
|
+
}
|
|
2389
|
+
|
|
2390
|
+
.pr-helper-search-count {
|
|
2391
|
+
position: absolute;
|
|
2392
|
+
/* Inside the 36px right gutter the input reserves for it (padding-right), clear of the border
|
|
2393
|
+
rather than sitting on it. */
|
|
2394
|
+
right: 12px;
|
|
2395
|
+
top: 50%;
|
|
2396
|
+
transform: translateY(-50%);
|
|
2397
|
+
font-size: 11px;
|
|
2398
|
+
color: var(--color-fg-muted, #57606a);
|
|
2399
|
+
display: none;
|
|
2400
|
+
}
|
|
2401
|
+
|
|
2402
|
+
.pr-helper-no-results {
|
|
2403
|
+
padding: 24px 16px;
|
|
2404
|
+
text-align: center;
|
|
2405
|
+
color: var(--color-fg-muted, #57606a);
|
|
2406
|
+
font-size: 14px;
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
/* ── File list ── */
|
|
2410
|
+
.pr-helper-file-list {
|
|
2411
|
+
flex: 1;
|
|
2412
|
+
min-height: 0;
|
|
2413
|
+
overflow-y: auto;
|
|
2414
|
+
padding: 6px 0;
|
|
2415
|
+
/* Same canvas as the other two scroll areas. The rows below stay white, so the grey shows
|
|
2416
|
+
through at the group headers and separates the groups without adding a rule between them. */
|
|
2417
|
+
background: #f8fafc;
|
|
2418
|
+
}
|
|
2419
|
+
|
|
2420
|
+
.pr-helper-file-item {
|
|
2421
|
+
position: relative;
|
|
2422
|
+
padding: 16px;
|
|
2423
|
+
display: flex;
|
|
2424
|
+
align-items: flex-start;
|
|
2425
|
+
cursor: pointer;
|
|
2426
|
+
color: var(--color-fg-default, #24292f);
|
|
2427
|
+
text-decoration: none;
|
|
2428
|
+
border-left: 2px solid transparent;
|
|
2429
|
+
background: #ffffff;
|
|
2430
|
+
transition: background 0.12s, border-left-color 0.12s;
|
|
2431
|
+
box-sizing: border-box;
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
.pr-helper-file-item:hover {
|
|
2435
|
+
background: rgba(147, 51, 234, 0.04);
|
|
2436
|
+
border-left-color: #9333ea;
|
|
2437
|
+
}
|
|
2438
|
+
|
|
2439
|
+
.pr-helper-file-content {
|
|
2440
|
+
flex: 1;
|
|
2441
|
+
min-width: 0;
|
|
2442
|
+
display: flex;
|
|
2443
|
+
flex-direction: column;
|
|
2444
|
+
/* The gap between filename, badges and description — a single flex gap since all three
|
|
2445
|
+
are direct children of this box, rather than separate margins on each that could
|
|
2446
|
+
drift out of step with one another. */
|
|
2447
|
+
gap: 6px;
|
|
2448
|
+
}
|
|
2449
|
+
|
|
2450
|
+
/* Review context layered onto the raw file rows. The Files tab used to render either GitHub's
|
|
2451
|
+
file browser or a list of AI file summaries, never both; now it's one list carrying GitHub's
|
|
2452
|
+
facts (path, +/- counts, search) and the review's meaning together. These two rules are
|
|
2453
|
+
additive — a file the review didn't cover simply omits them and the row looks as it always
|
|
2454
|
+
did, which is also what every row looks like before a review has been generated. */
|
|
2455
|
+
.pr-helper-file-review-badges {
|
|
2456
|
+
display: flex;
|
|
2457
|
+
align-items: center;
|
|
2458
|
+
gap: 6px;
|
|
2459
|
+
flex-wrap: wrap;
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
/* Borrows the History tab's group-label treatment (.pr-helper-history-group-label) — both
|
|
2463
|
+
partition a flat list into labelled runs, so they should read the same way. */
|
|
2464
|
+
.pr-helper-file-group-header {
|
|
2465
|
+
font-size: 10px;
|
|
2466
|
+
font-weight: 600;
|
|
2467
|
+
text-transform: uppercase;
|
|
2468
|
+
letter-spacing: 0.4px;
|
|
2469
|
+
color: var(--color-fg-muted, #57606a);
|
|
2470
|
+
/* Left inset matches a row's text, not the panel edge: .pr-helper-file-item is 16px of padding
|
|
2471
|
+
inside a 2px left border, so its filename starts 18px in. A header at 2px reads as belonging
|
|
2472
|
+
to the panel rather than to the rows it labels. */
|
|
2473
|
+
padding: 12px 16px 4px 18px;
|
|
2474
|
+
display: flex;
|
|
2475
|
+
align-items: center;
|
|
2476
|
+
justify-content: space-between;
|
|
2477
|
+
gap: 8px;
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
/* Quieter than .pr-helper-action-btn — this acts on a whole group, so it must not out-shout the
|
|
2481
|
+
per-row actions or read as the primary thing to do on the tab. */
|
|
2482
|
+
.pr-helper-file-group-action {
|
|
2483
|
+
font-family: inherit;
|
|
2484
|
+
font-size: 11px;
|
|
2485
|
+
letter-spacing: 0;
|
|
2486
|
+
text-transform: none;
|
|
2487
|
+
padding: 2px 8px;
|
|
2488
|
+
border-radius: 4px;
|
|
2489
|
+
background: #ffffff;
|
|
2490
|
+
border: 1px solid var(--color-border-default, #d0d7de);
|
|
2491
|
+
color: #9333ea;
|
|
2492
|
+
cursor: pointer;
|
|
2493
|
+
white-space: nowrap;
|
|
2494
|
+
}
|
|
2495
|
+
|
|
2496
|
+
.pr-helper-file-group-action:hover:not(:disabled) {
|
|
2497
|
+
border-color: #9333ea;
|
|
2498
|
+
background: #faf5ff;
|
|
2499
|
+
}
|
|
2500
|
+
|
|
2501
|
+
.pr-helper-file-group-action:disabled {
|
|
2502
|
+
opacity: 0.5;
|
|
2503
|
+
cursor: default;
|
|
2504
|
+
}
|
|
2505
|
+
|
|
2506
|
+
.pr-helper-file-group-header:first-child { padding-top: 2px; }
|
|
2507
|
+
|
|
2508
|
+
/* Amber rather than the section badge's purple: purple means "the review has something to say
|
|
2509
|
+
about this file", and this badge means the exact opposite — nothing could read it. Shares the
|
|
2510
|
+
hue with .pr-helper-file-stat-issues, the row's other "needs your attention" signal. */
|
|
2511
|
+
.pr-helper-file-badge-unavailable {
|
|
2512
|
+
display: inline-block;
|
|
2513
|
+
width: fit-content;
|
|
2514
|
+
font-size: 11px;
|
|
2515
|
+
color: #9a6700;
|
|
2516
|
+
background: rgba(154, 103, 0, 0.1);
|
|
2517
|
+
border-radius: 100px;
|
|
2518
|
+
padding: 2px 9px;
|
|
2519
|
+
}
|
|
2520
|
+
|
|
2521
|
+
.pr-helper-file-review-focus {
|
|
2522
|
+
font-size: 12px;
|
|
2523
|
+
line-height: 1.5;
|
|
2524
|
+
color: var(--color-fg-muted, #57606a);
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
/* ── The findings themselves, under the file row ──
|
|
2528
|
+
Indented behind a hairline rather than boxed: these are pointers into the Issues and Questions
|
|
2529
|
+
tabs, and giving each one a card would make the row look like the place the finding lives. The
|
|
2530
|
+
rule reads as "belonging to the file above" without competing with the real cards. */
|
|
2531
|
+
.pr-helper-file-findings {
|
|
2532
|
+
display: flex;
|
|
2533
|
+
flex-direction: column;
|
|
2534
|
+
gap: 6px;
|
|
2535
|
+
margin-top: 6px;
|
|
2536
|
+
padding-left: 8px;
|
|
2537
|
+
border-left: 2px solid var(--color-border-muted, #d8dee4);
|
|
2538
|
+
}
|
|
2539
|
+
|
|
2540
|
+
.pr-helper-file-finding-heading {
|
|
2541
|
+
margin: 0 0 3px;
|
|
2542
|
+
font-size: 11px;
|
|
2543
|
+
font-weight: 600;
|
|
2544
|
+
text-transform: uppercase;
|
|
2545
|
+
letter-spacing: 0.03em;
|
|
2546
|
+
color: var(--color-fg-muted, #57606a);
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
.pr-helper-file-finding-list {
|
|
2550
|
+
list-style: none;
|
|
2551
|
+
margin: 0;
|
|
2552
|
+
padding: 0;
|
|
2553
|
+
display: flex;
|
|
2554
|
+
flex-direction: column;
|
|
2555
|
+
gap: 2px;
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2558
|
+
.pr-helper-file-finding {
|
|
2559
|
+
display: flex;
|
|
2560
|
+
align-items: baseline;
|
|
2561
|
+
gap: 6px;
|
|
2562
|
+
width: 100%;
|
|
2563
|
+
text-align: left;
|
|
2564
|
+
background: none;
|
|
2565
|
+
border: 0;
|
|
2566
|
+
border-radius: 5px;
|
|
2567
|
+
padding: 3px 4px;
|
|
2568
|
+
margin: 0;
|
|
2569
|
+
font: inherit;
|
|
2570
|
+
font-size: 12px;
|
|
2571
|
+
line-height: 1.45;
|
|
2572
|
+
color: var(--color-fg-default, #1f2328);
|
|
2573
|
+
cursor: pointer;
|
|
2574
|
+
}
|
|
2575
|
+
|
|
2576
|
+
.pr-helper-file-finding:hover { background: rgba(175, 184, 193, 0.18); }
|
|
2577
|
+
|
|
2578
|
+
.pr-helper-file-finding:focus-visible {
|
|
2579
|
+
outline: 2px solid var(--color-accent-fg, #0969da);
|
|
2580
|
+
outline-offset: -1px;
|
|
2581
|
+
}
|
|
2582
|
+
|
|
2583
|
+
/* Clamped in CSS, never in the markup: the full title stays in the DOM for find-in-page and in
|
|
2584
|
+
the title attribute for hover. Two lines is enough for the long, specific finding titles that
|
|
2585
|
+
are worth reading and short enough that five findings still fit on a screen. */
|
|
2586
|
+
.pr-helper-file-finding-label {
|
|
2587
|
+
flex: 1;
|
|
2588
|
+
display: -webkit-box;
|
|
2589
|
+
-webkit-line-clamp: 2;
|
|
2590
|
+
-webkit-box-orient: vertical;
|
|
2591
|
+
overflow: hidden;
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
/* Severity as a dot, not a pill. The Issues tab spells the word out on a card the reviewer has
|
|
2595
|
+
arrived at to read; here the finding is one line in a list under a filename, and four uppercase
|
|
2596
|
+
pills down the left edge would out-shout the titles they are grading. Colour carries the rank,
|
|
2597
|
+
the tooltip carries the meaning, and the word itself is one click away. */
|
|
2598
|
+
.pr-helper-file-finding-dot {
|
|
2599
|
+
flex-shrink: 0;
|
|
2600
|
+
width: 7px;
|
|
2601
|
+
height: 7px;
|
|
2602
|
+
border-radius: 50%;
|
|
2603
|
+
align-self: center;
|
|
2604
|
+
background: var(--pr-helper-pill-neutral-fg);
|
|
2605
|
+
}
|
|
2606
|
+
|
|
2607
|
+
.pr-helper-file-finding-dot.pr-helper-severity-critical { background: var(--pr-helper-pill-danger-fg); }
|
|
2608
|
+
.pr-helper-file-finding-dot.pr-helper-severity-major { background: var(--pr-helper-pill-warning-fg); }
|
|
2609
|
+
.pr-helper-file-finding-dot.pr-helper-severity-minor { background: var(--pr-helper-pill-info-fg); }
|
|
2610
|
+
.pr-helper-file-finding-dot.pr-helper-severity-nit { background: var(--pr-helper-pill-neutral-fg); }
|
|
2611
|
+
|
|
2612
|
+
/* Struck through, not hidden: a refuted finding is retired but stays on the record, and the
|
|
2613
|
+
reviewer may well want to open it to read why. The strike is on the label alone so the pill
|
|
2614
|
+
stays readable. */
|
|
2615
|
+
.pr-helper-file-finding-is-refuted .pr-helper-file-finding-label {
|
|
2616
|
+
text-decoration: line-through;
|
|
2617
|
+
color: var(--color-fg-muted, #57606a);
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2620
|
+
.pr-helper-file-finding-is-refuted .pr-helper-file-finding-dot { opacity: 0.4; }
|
|
2621
|
+
|
|
2622
|
+
.pr-helper-file-finding-refuted {
|
|
2623
|
+
flex-shrink: 0;
|
|
2624
|
+
align-self: center;
|
|
2625
|
+
font-size: 10px;
|
|
2626
|
+
color: var(--color-fg-muted, #57606a);
|
|
2627
|
+
background: rgba(175, 184, 193, 0.22);
|
|
2628
|
+
border-radius: 100px;
|
|
2629
|
+
padding: 1px 7px;
|
|
2630
|
+
}
|
|
2631
|
+
|
|
2632
|
+
.pr-helper-file-finding-followup {
|
|
2633
|
+
flex-shrink: 0;
|
|
2634
|
+
align-self: center;
|
|
2635
|
+
font-size: 10px;
|
|
2636
|
+
color: var(--color-fg-muted, #57606a);
|
|
2637
|
+
background: rgba(175, 184, 193, 0.22);
|
|
2638
|
+
border-radius: 100px;
|
|
2639
|
+
padding: 1px 7px;
|
|
2640
|
+
}
|
|
2641
|
+
|
|
2642
|
+
/* Same treatment and duration as .pr-helper-guide-section-highlight, which is the other "you asked
|
|
2643
|
+
to be taken here, this is the one" flash in the sidebar. Blue rather than that one's green: green
|
|
2644
|
+
there means completed, and a finding you have just jumped to is not. */
|
|
2645
|
+
.pr-helper-finding-highlight {
|
|
2646
|
+
background: rgba(9, 105, 218, 0.14);
|
|
2647
|
+
border-color: rgba(9, 105, 218, 0.45);
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2650
|
+
|
|
2651
|
+
.pr-helper-file-stats {
|
|
2652
|
+
font-size: 12px;
|
|
2653
|
+
color: var(--color-fg-muted, #57606a);
|
|
2654
|
+
white-space: nowrap;
|
|
2655
|
+
padding: 2px 8px;
|
|
2656
|
+
background: rgba(175, 184, 193, 0.15);
|
|
2657
|
+
border-radius: 10px;
|
|
2658
|
+
flex-shrink: 0;
|
|
2659
|
+
align-self: flex-start;
|
|
2660
|
+
transition: opacity 0.12s;
|
|
2661
|
+
}
|
|
2662
|
+
|
|
2663
|
+
.pr-helper-file-item:hover .pr-helper-file-stats { opacity: 0; }
|
|
2664
|
+
|
|
2665
|
+
/* ── Hover-reveal action buttons ── */
|
|
2666
|
+
.pr-helper-file-actions {
|
|
2667
|
+
position: absolute;
|
|
2668
|
+
right: 12px;
|
|
2669
|
+
top: 50%;
|
|
2670
|
+
transform: translateY(-50%);
|
|
2671
|
+
display: flex;
|
|
2672
|
+
gap: 4px;
|
|
2673
|
+
opacity: 0;
|
|
2674
|
+
pointer-events: none;
|
|
2675
|
+
transition: opacity 0.12s;
|
|
2676
|
+
}
|
|
2677
|
+
|
|
2678
|
+
/* Opaque too, and for the same reason as the buttons: making only the buttons solid still lets
|
|
2679
|
+
text show through the gaps between them. The tray occludes as one block.
|
|
2680
|
+
|
|
2681
|
+
The colour tracks the row's own state rather than being a flat white, because the row tints on
|
|
2682
|
+
hover — #fbf7fe is that 4% purple composited over white, precomputed because a translucent
|
|
2683
|
+
overlay on a translucent row would double the tint.
|
|
2684
|
+
|
|
2685
|
+
Bordered and shadowed, because the tray is not part of the row: it is absolutely positioned over
|
|
2686
|
+
the top of it, occluding whatever text is underneath. Without an edge, three buttons simply
|
|
2687
|
+
appeared mid-sentence with no account of where the words went — the outline plus the shadow say
|
|
2688
|
+
"this is one panel, floating above", which is exactly what it is.
|
|
2689
|
+
|
|
2690
|
+
Radius 8 against the buttons' 4: an outer corner has to be looser than the corners it contains,
|
|
2691
|
+
or the nesting reads as a mistake. Padding is symmetric now for the same reason — the old
|
|
2692
|
+
`0 … 14px` let the buttons sit flush against the right edge, which is invisible without a border
|
|
2693
|
+
and lopsided with one. */
|
|
2694
|
+
.pr-helper-file-actions {
|
|
2695
|
+
background: #ffffff;
|
|
2696
|
+
padding: 4px 6px;
|
|
2697
|
+
border: 1px solid rgba(147, 51, 234, 0.25);
|
|
2698
|
+
border-radius: 8px;
|
|
2699
|
+
box-shadow: 0 2px 6px rgba(31, 35, 40, 0.10);
|
|
2700
|
+
/* The tray's default `stretch` left the "Analysing…" label top-aligned in a box the taller
|
|
2701
|
+
Cancel button had grown, so the two read as being on different lines. */
|
|
2702
|
+
align-items: center;
|
|
2703
|
+
}
|
|
2704
|
+
|
|
2705
|
+
.pr-helper-file-item:hover .pr-helper-file-actions {
|
|
2706
|
+
opacity: 1;
|
|
2707
|
+
pointer-events: auto;
|
|
2708
|
+
background: #fbf7fe;
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2711
|
+
/* Opts out of the hover gate above, and must: this is the only indication that a follow-up is
|
|
2712
|
+
running and the only way to stop it, so a version of it you have to hover to find would leave
|
|
2713
|
+
the run as invisible as it was before the Cancel existed. */
|
|
2714
|
+
.pr-helper-file-actions-running {
|
|
2715
|
+
opacity: 1;
|
|
2716
|
+
pointer-events: auto;
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2719
|
+
/* Matches .pr-helper-suggested-area-busy — the two say the same thing about the same operation. */
|
|
2720
|
+
.pr-helper-file-action-busy {
|
|
2721
|
+
font-size: 11px;
|
|
2722
|
+
color: var(--color-fg-muted, #57606a);
|
|
2723
|
+
white-space: nowrap;
|
|
2724
|
+
}
|
|
2725
|
+
|
|
2726
|
+
@keyframes pr-helper-spin { to { transform: rotate(360deg); } }
|
|
2727
|
+
|
|
2728
|
+
/* A spinner rather than a progress bar, on every in-flight LLM call the sidebar shows.
|
|
2729
|
+
|
|
2730
|
+
Deliberately indeterminate: each of these is a single LLM call, so there is no percentage to
|
|
2731
|
+
report and no intermediate step to name. A bar that filled at a made-up rate would be a
|
|
2732
|
+
progress claim we cannot support — the same objection as any other number the product states
|
|
2733
|
+
without being able to back it. What the reviewer actually needs to know is "still running,
|
|
2734
|
+
not stuck", which motion conveys and static text does not.
|
|
2735
|
+
|
|
2736
|
+
One rule for all three selectors on purpose. "Applying…" on an investigation and "Analysing…" on
|
|
2737
|
+
a file are the same promise to the reviewer — one request is out, wait for it — and two
|
|
2738
|
+
different-looking spinners would imply a difference that does not exist. Adding a fourth waiting
|
|
2739
|
+
state means adding it to this list, not inventing another animation. */
|
|
2740
|
+
.pr-helper-file-action-busy::before,
|
|
2741
|
+
.pr-helper-suggested-area-busy::before,
|
|
2742
|
+
.pr-helper-investigate-applying::before {
|
|
2743
|
+
content: '';
|
|
2744
|
+
display: inline-block;
|
|
2745
|
+
width: 10px;
|
|
2746
|
+
height: 10px;
|
|
2747
|
+
margin-right: 6px;
|
|
2748
|
+
vertical-align: -1px;
|
|
2749
|
+
border: 1.5px solid rgba(147, 51, 234, 0.25);
|
|
2750
|
+
border-top-color: #9333ea;
|
|
2751
|
+
border-radius: 50%;
|
|
2752
|
+
animation: pr-helper-spin 0.7s linear infinite;
|
|
2753
|
+
}
|
|
2754
|
+
|
|
2755
|
+
/* Explain and Copy — the row itself scrolls to the diff, so both stay explicit buttons.
|
|
2756
|
+
One shared outlined-secondary style: the actions are peers and shouldn't rank each other. */
|
|
2757
|
+
.pr-helper-action-btn {
|
|
2758
|
+
font-size: 12px;
|
|
2759
|
+
font-family: inherit;
|
|
2760
|
+
padding: 4px 10px;
|
|
2761
|
+
border-radius: 4px;
|
|
2762
|
+
/* Opaque, not transparent. The tray these sit in is absolutely positioned over the row, so a
|
|
2763
|
+
see-through button lets the description and badges read straight through its label. Harmless
|
|
2764
|
+
while the row only had Explain and Copy over mostly-empty space; immediately visible once a
|
|
2765
|
+
third button widened the tray far enough to reach the text. */
|
|
2766
|
+
background: #ffffff;
|
|
2767
|
+
border: 1px solid #9333ea;
|
|
2768
|
+
color: #9333ea;
|
|
2769
|
+
cursor: pointer;
|
|
2770
|
+
line-height: 18px;
|
|
2771
|
+
white-space: nowrap;
|
|
2772
|
+
transition: background 0.1s;
|
|
2773
|
+
}
|
|
2774
|
+
|
|
2775
|
+
.pr-helper-action-btn:hover:not(:disabled) { background: #f4e8fd; }
|
|
2776
|
+
|
|
2777
|
+
/* Reads as "not right now" rather than broken — the row is still hovering, so the button has
|
|
2778
|
+
to stay visible while it waits out an in-flight assistant request. */
|
|
2779
|
+
.pr-helper-action-btn:disabled {
|
|
2780
|
+
opacity: 0.55;
|
|
2781
|
+
cursor: default;
|
|
2782
|
+
}
|
|
2783
|
+
|
|
2784
|
+
/* ── Scrollbar ── */
|
|
2785
|
+
.pr-helper-file-list::-webkit-scrollbar { width: 6px; }
|
|
2786
|
+
.pr-helper-file-list::-webkit-scrollbar-track { background: transparent; }
|
|
2787
|
+
.pr-helper-file-list::-webkit-scrollbar-thumb {
|
|
2788
|
+
background: rgba(175, 184, 193, 0.4);
|
|
2789
|
+
border-radius: 3px;
|
|
2790
|
+
}
|
|
2791
|
+
.pr-helper-file-list::-webkit-scrollbar-thumb:hover {
|
|
2792
|
+
background: rgba(175, 184, 193, 0.6);
|
|
2793
|
+
}
|
|
2794
|
+
|
|
2795
|
+
/* ── Toast ── */
|
|
2796
|
+
.pr-helper-toast {
|
|
2797
|
+
position: fixed;
|
|
2798
|
+
bottom: 24px;
|
|
2799
|
+
right: 24px;
|
|
2800
|
+
padding: 8px 16px;
|
|
2801
|
+
background: #2da44e;
|
|
2802
|
+
color: #ffffff;
|
|
2803
|
+
border-radius: 6px;
|
|
2804
|
+
font-size: 13px;
|
|
2805
|
+
z-index: 1000000;
|
|
2806
|
+
opacity: 0;
|
|
2807
|
+
transform: translateY(10px);
|
|
2808
|
+
transition: all 0.3s ease;
|
|
2809
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
2810
|
+
display: flex;
|
|
2811
|
+
align-items: center;
|
|
2812
|
+
gap: 8px;
|
|
2813
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
|
2814
|
+
}
|
|
2815
|
+
|
|
2816
|
+
.pr-helper-toast svg { width: 16px; height: 16px; flex-shrink: 0; }
|
|
2817
|
+
.pr-helper-toast.show { opacity: 1; transform: translateY(0); }
|
|
2818
|
+
.pr-helper-toast.error { background: #cf222e; }
|
|
2819
|
+
|
|
2820
|
+
/* ── Rating prompt ── */
|
|
2821
|
+
.pr-helper-rating-prompt {
|
|
2822
|
+
position: fixed;
|
|
2823
|
+
bottom: 24px;
|
|
2824
|
+
right: 24px;
|
|
2825
|
+
width: 300px;
|
|
2826
|
+
padding: 16px;
|
|
2827
|
+
background: #ffffff;
|
|
2828
|
+
color: #1f2328;
|
|
2829
|
+
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
2830
|
+
border-radius: 10px;
|
|
2831
|
+
z-index: 1000000;
|
|
2832
|
+
opacity: 0;
|
|
2833
|
+
transform: translateY(10px);
|
|
2834
|
+
transition: all 0.3s ease;
|
|
2835
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
|
|
2836
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
|
2837
|
+
}
|
|
2838
|
+
|
|
2839
|
+
.pr-helper-rating-prompt.show { opacity: 1; transform: translateY(0); }
|
|
2840
|
+
|
|
2841
|
+
.pr-helper-rating-prompt-title {
|
|
2842
|
+
margin: 0 0 6px;
|
|
2843
|
+
font-size: 14px;
|
|
2844
|
+
font-weight: 600;
|
|
2845
|
+
}
|
|
2846
|
+
|
|
2847
|
+
.pr-helper-rating-prompt-body {
|
|
2848
|
+
margin: 0 0 14px;
|
|
2849
|
+
font-size: 12px;
|
|
2850
|
+
line-height: 1.5;
|
|
2851
|
+
color: #57606a;
|
|
2852
|
+
}
|
|
2853
|
+
|
|
2854
|
+
.pr-helper-rating-prompt-actions {
|
|
2855
|
+
display: flex;
|
|
2856
|
+
flex-direction: column;
|
|
2857
|
+
gap: 6px;
|
|
2858
|
+
}
|
|
2859
|
+
|
|
2860
|
+
.pr-helper-rating-prompt-btn {
|
|
2861
|
+
padding: 8px 12px;
|
|
2862
|
+
border-radius: 6px;
|
|
2863
|
+
border: 1px solid rgba(0, 0, 0, 0.12);
|
|
2864
|
+
background: #f6f8fa;
|
|
2865
|
+
color: #1f2328;
|
|
2866
|
+
font-size: 12px;
|
|
2867
|
+
font-family: inherit;
|
|
2868
|
+
cursor: pointer;
|
|
2869
|
+
text-align: center;
|
|
2870
|
+
}
|
|
2871
|
+
|
|
2872
|
+
.pr-helper-rating-prompt-btn:hover { border-color: #9333ea; color: #9333ea; }
|
|
2873
|
+
|
|
2874
|
+
.pr-helper-rating-prompt-btn-primary {
|
|
2875
|
+
background: #9333ea;
|
|
2876
|
+
border-color: #9333ea;
|
|
2877
|
+
color: #ffffff;
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2880
|
+
.pr-helper-rating-prompt-btn-primary:hover { background: #7c3aed; border-color: #7c3aed; color: #ffffff; }
|
|
2881
|
+
|
|
2882
|
+
.pr-helper-rating-prompt-btn-quiet {
|
|
2883
|
+
background: transparent;
|
|
2884
|
+
border-color: transparent;
|
|
2885
|
+
color: #57606a;
|
|
2886
|
+
}
|
|
2887
|
+
|
|
2888
|
+
.pr-helper-rating-prompt-btn-quiet:hover { border-color: transparent; color: #1f2328; }
|
|
2889
|
+
|
|
2890
|
+
/* ── Guided navigation overlay ── */
|
|
2891
|
+
/* Bottom-LEFT: toast (above, bottom-right) and rating-prompt (above, bottom-right) both live
|
|
2892
|
+
in the opposite corner, so this never collides with either. Kept intentionally small —
|
|
2893
|
+
media-player-controls sized, not a second sidebar (no checklist/issues/summary here). */
|
|
2894
|
+
.pr-helper-nav-overlay {
|
|
2895
|
+
position: fixed;
|
|
2896
|
+
bottom: 24px;
|
|
2897
|
+
left: 24px;
|
|
2898
|
+
z-index: 999997; /* below toggle-tab's 999998 and sidebar's 999999; sidebar is docked right so no structural overlap regardless */
|
|
2899
|
+
width: 280px;
|
|
2900
|
+
max-width: calc(100vw - 48px);
|
|
2901
|
+
padding: 12px 14px;
|
|
2902
|
+
background: #ffffff;
|
|
2903
|
+
color: #1f2328;
|
|
2904
|
+
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
2905
|
+
border-radius: 10px;
|
|
2906
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
|
|
2907
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
|
2908
|
+
transition: width 0.2s ease, height 0.2s ease, padding 0.2s ease, border-radius 0.2s ease, left 0.2s ease, background 0.2s ease;
|
|
2909
|
+
}
|
|
2910
|
+
|
|
2911
|
+
/* The tab is a permanent sibling of the panel, only shown while collapsed — see
|
|
2912
|
+
navigation_overlay.ts's showNavigationOverlay() for why (keeps expand/collapse a single
|
|
2913
|
+
class toggle rather than a DOM rebuild, so restoring never re-fetches or resets anything). */
|
|
2914
|
+
.pr-helper-nav-overlay-tab { display: none; }
|
|
2915
|
+
|
|
2916
|
+
/* "Hide guide" collapses to this — mirrors .pr-helper-toggle-button's own vertical-tab
|
|
2917
|
+
pattern (sidebar.css, further down) but anchored to the left edge, flush like that one is
|
|
2918
|
+
flush right, so it reads as the same interaction pattern reused rather than a new one. */
|
|
2919
|
+
.pr-helper-nav-overlay.pr-helper-nav-overlay-collapsed {
|
|
2920
|
+
left: 0;
|
|
2921
|
+
width: auto;
|
|
2922
|
+
padding: 0;
|
|
2923
|
+
background: #9333ea;
|
|
2924
|
+
border: 1px solid #7c3aed;
|
|
2925
|
+
border-left: none;
|
|
2926
|
+
border-radius: 0 6px 6px 0;
|
|
2927
|
+
box-shadow: 2px 0 8px rgba(147, 51, 234, 0.3);
|
|
2928
|
+
}
|
|
2929
|
+
|
|
2930
|
+
.pr-helper-nav-overlay-collapsed .pr-helper-nav-overlay-panel { display: none; }
|
|
2931
|
+
|
|
2932
|
+
.pr-helper-nav-overlay-collapsed .pr-helper-nav-overlay-tab {
|
|
2933
|
+
display: flex;
|
|
2934
|
+
align-items: center;
|
|
2935
|
+
justify-content: center;
|
|
2936
|
+
gap: 8px;
|
|
2937
|
+
padding: 16px 8px;
|
|
2938
|
+
border: none;
|
|
2939
|
+
background: transparent;
|
|
2940
|
+
color: #ffffff;
|
|
2941
|
+
cursor: pointer;
|
|
2942
|
+
font-family: inherit;
|
|
2943
|
+
font-size: 11px;
|
|
2944
|
+
font-weight: 600;
|
|
2945
|
+
letter-spacing: 0.04em;
|
|
2946
|
+
writing-mode: vertical-rl;
|
|
2947
|
+
transition: padding 0.2s ease;
|
|
2948
|
+
}
|
|
2949
|
+
|
|
2950
|
+
.pr-helper-nav-overlay-collapsed .pr-helper-nav-overlay-tab:hover { padding-left: 10px; }
|
|
2951
|
+
|
|
2952
|
+
.pr-helper-nav-overlay-header {
|
|
2953
|
+
display: flex;
|
|
2954
|
+
align-items: flex-start;
|
|
2955
|
+
justify-content: space-between;
|
|
2956
|
+
gap: 8px;
|
|
2957
|
+
}
|
|
2958
|
+
|
|
2959
|
+
.pr-helper-nav-overlay-eyebrow {
|
|
2960
|
+
font-size: 10px;
|
|
2961
|
+
font-weight: 700;
|
|
2962
|
+
text-transform: uppercase;
|
|
2963
|
+
letter-spacing: 0.04em;
|
|
2964
|
+
color: #9333ea;
|
|
2965
|
+
margin-bottom: 2px;
|
|
2966
|
+
}
|
|
2967
|
+
|
|
2968
|
+
.pr-helper-nav-overlay-title {
|
|
2969
|
+
font-size: 13px;
|
|
2970
|
+
font-weight: 600;
|
|
2971
|
+
line-height: 1.3;
|
|
2972
|
+
}
|
|
2973
|
+
|
|
2974
|
+
/* A text label, not a bare "×" — a lone icon is easy to click without realizing what it does
|
|
2975
|
+
(or that the sidebar's toggle tab reopens it), so it's spelled out. */
|
|
2976
|
+
.pr-helper-nav-overlay-close {
|
|
2977
|
+
flex-shrink: 0;
|
|
2978
|
+
background: transparent;
|
|
2979
|
+
border: none;
|
|
2980
|
+
color: #57606a;
|
|
2981
|
+
font-size: 11px;
|
|
2982
|
+
font-weight: 600;
|
|
2983
|
+
font-family: inherit;
|
|
2984
|
+
line-height: 1.3;
|
|
2985
|
+
cursor: pointer;
|
|
2986
|
+
padding: 2px 4px;
|
|
2987
|
+
white-space: nowrap;
|
|
2988
|
+
}
|
|
2989
|
+
|
|
2990
|
+
.pr-helper-nav-overlay-close:hover { color: #1f2328; text-decoration: underline; }
|
|
2991
|
+
|
|
2992
|
+
.pr-helper-nav-overlay-progress {
|
|
2993
|
+
margin-top: 2px;
|
|
2994
|
+
font-size: 11px;
|
|
2995
|
+
color: #57606a;
|
|
2996
|
+
}
|
|
2997
|
+
|
|
2998
|
+
/* Sits between the progress line and the scrollable item list — outside the list on purpose,
|
|
2999
|
+
so it never competes for that 160px scroll region. */
|
|
3000
|
+
.pr-helper-nav-overlay-focus {
|
|
3001
|
+
margin-top: 10px;
|
|
3002
|
+
padding-bottom: 10px;
|
|
3003
|
+
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
|
3004
|
+
}
|
|
3005
|
+
|
|
3006
|
+
.pr-helper-nav-overlay-focus-eyebrow {
|
|
3007
|
+
font-size: 10px;
|
|
3008
|
+
font-weight: 700;
|
|
3009
|
+
text-transform: uppercase;
|
|
3010
|
+
letter-spacing: 0.04em;
|
|
3011
|
+
color: #9333ea;
|
|
3012
|
+
margin-bottom: 4px;
|
|
3013
|
+
}
|
|
3014
|
+
|
|
3015
|
+
/* Shown in full, never clipped — a half-sentence ("Verify cached targets, polymorphic…") is
|
|
3016
|
+
worse than no note at all, since the reviewer can't act on the part that got cut. Length is
|
|
3017
|
+
controlled upstream instead: the prompt caps focus at one or two sentences (~20-35 words),
|
|
3018
|
+
which wraps to about four lines here. The panel is still never widened to fit this text —
|
|
3019
|
+
keeping the navigator narrow matters more than saving a line of wrapping. */
|
|
3020
|
+
.pr-helper-nav-overlay-focus-text {
|
|
3021
|
+
margin: 0;
|
|
3022
|
+
font-size: 11px;
|
|
3023
|
+
line-height: 1.45;
|
|
3024
|
+
color: #57606a;
|
|
3025
|
+
}
|
|
3026
|
+
|
|
3027
|
+
.pr-helper-nav-overlay-items {
|
|
3028
|
+
list-style: none;
|
|
3029
|
+
margin: 10px 0;
|
|
3030
|
+
padding: 0;
|
|
3031
|
+
max-height: 160px;
|
|
3032
|
+
overflow-y: auto;
|
|
3033
|
+
display: flex;
|
|
3034
|
+
flex-direction: column;
|
|
3035
|
+
gap: 2px;
|
|
3036
|
+
/* macOS defaults to overlay scrollbars that are invisible until actively scrolling, so a
|
|
3037
|
+
long file list (large PRs) reads as cut off rather than scrollable — force a slim,
|
|
3038
|
+
always-visible scrollbar instead of relying on the OS setting. */
|
|
3039
|
+
scrollbar-width: thin;
|
|
3040
|
+
scrollbar-color: rgba(0, 0, 0, 0.25) transparent;
|
|
3041
|
+
}
|
|
3042
|
+
|
|
3043
|
+
.pr-helper-nav-overlay-items::-webkit-scrollbar { width: 6px; }
|
|
3044
|
+
.pr-helper-nav-overlay-items::-webkit-scrollbar-track { background: transparent; }
|
|
3045
|
+
.pr-helper-nav-overlay-items::-webkit-scrollbar-thumb {
|
|
3046
|
+
background: rgba(0, 0, 0, 0.25);
|
|
3047
|
+
border-radius: 3px;
|
|
3048
|
+
}
|
|
3049
|
+
.pr-helper-nav-overlay-items::-webkit-scrollbar-thumb:hover { background: rgba(0, 0, 0, 0.4); }
|
|
3050
|
+
|
|
3051
|
+
.pr-helper-nav-item {
|
|
3052
|
+
display: flex;
|
|
3053
|
+
align-items: center;
|
|
3054
|
+
gap: 6px;
|
|
3055
|
+
padding: 4px 6px;
|
|
3056
|
+
border-radius: 4px;
|
|
3057
|
+
font-size: 12px;
|
|
3058
|
+
cursor: pointer;
|
|
3059
|
+
white-space: nowrap;
|
|
3060
|
+
overflow: hidden;
|
|
3061
|
+
text-overflow: ellipsis;
|
|
3062
|
+
/* Without this, the flex column parent's max-height (below) makes rows shrink to fit
|
|
3063
|
+
instead of overflowing — on a long PR every row gets squeezed short rather than the
|
|
3064
|
+
list scrolling, which is the actual bug this fixes (not a missing scrollbar). */
|
|
3065
|
+
flex-shrink: 0;
|
|
3066
|
+
}
|
|
3067
|
+
|
|
3068
|
+
.pr-helper-nav-item:hover { background: rgba(0, 0, 0, 0.04); }
|
|
3069
|
+
|
|
3070
|
+
.pr-helper-nav-item-marker { flex-shrink: 0; width: 14px; text-align: center; color: #57606a; }
|
|
3071
|
+
|
|
3072
|
+
.pr-helper-nav-item-label { overflow: hidden; text-overflow: ellipsis; }
|
|
3073
|
+
|
|
3074
|
+
/* The current item is visually distinct (fill, not just a different glyph) so the active
|
|
3075
|
+
target is unmissable at a glance — persistent, not a timed highlight. */
|
|
3076
|
+
.pr-helper-nav-item-current {
|
|
3077
|
+
background: rgba(147, 51, 234, 0.12);
|
|
3078
|
+
font-weight: 600;
|
|
3079
|
+
}
|
|
3080
|
+
.pr-helper-nav-item-current .pr-helper-nav-item-marker { color: #9333ea; }
|
|
3081
|
+
.pr-helper-nav-item-current:hover { background: rgba(147, 51, 234, 0.18); }
|
|
3082
|
+
|
|
3083
|
+
/* Neutral, not GitHub's approval-green — "visited" means scrolled past, not reviewed or
|
|
3084
|
+
approved, and the color shouldn't suggest otherwise. */
|
|
3085
|
+
.pr-helper-nav-item-visited .pr-helper-nav-item-marker { color: #57606a; }
|
|
3086
|
+
|
|
3087
|
+
.pr-helper-nav-overlay-controls {
|
|
3088
|
+
display: flex;
|
|
3089
|
+
gap: 8px;
|
|
3090
|
+
}
|
|
3091
|
+
|
|
3092
|
+
.pr-helper-nav-overlay-btn {
|
|
3093
|
+
flex: 1;
|
|
3094
|
+
padding: 8px 10px;
|
|
3095
|
+
border-radius: 6px;
|
|
3096
|
+
border: 1px solid rgba(0, 0, 0, 0.12);
|
|
3097
|
+
background: #f6f8fa;
|
|
3098
|
+
color: #1f2328;
|
|
3099
|
+
font-size: 12px;
|
|
3100
|
+
font-weight: 600;
|
|
3101
|
+
font-family: inherit;
|
|
3102
|
+
cursor: pointer;
|
|
3103
|
+
text-align: center;
|
|
3104
|
+
}
|
|
3105
|
+
|
|
3106
|
+
.pr-helper-nav-overlay-btn:hover { border-color: #9333ea; color: #9333ea; }
|
|
3107
|
+
|
|
3108
|
+
.pr-helper-nav-overlay-btn-primary {
|
|
3109
|
+
background: #9333ea;
|
|
3110
|
+
border-color: #9333ea;
|
|
3111
|
+
color: #ffffff;
|
|
3112
|
+
}
|
|
3113
|
+
|
|
3114
|
+
.pr-helper-nav-overlay-btn-primary:hover { background: #7c3aed; border-color: #7c3aed; color: #ffffff; }
|
|
3115
|
+
|
|
3116
|
+
/* Held for ~250ms between the Finish click and the overlay teardown (markOverlayFinishing)
|
|
3117
|
+
so the button commits to the action before everything it lives in disappears. Wins over
|
|
3118
|
+
:hover, which is necessarily still active at the moment of the click. */
|
|
3119
|
+
.pr-helper-nav-overlay-btn {
|
|
3120
|
+
transition: background-color 0.25s ease, border-color 0.25s ease, color 0.25s ease;
|
|
3121
|
+
}
|
|
3122
|
+
|
|
3123
|
+
.pr-helper-nav-overlay-btn-primary.pr-helper-nav-overlay-btn-finishing,
|
|
3124
|
+
.pr-helper-nav-overlay-btn-primary.pr-helper-nav-overlay-btn-finishing:hover {
|
|
3125
|
+
background: #1a7f37;
|
|
3126
|
+
border-color: #1a7f37;
|
|
3127
|
+
color: #ffffff;
|
|
3128
|
+
}
|
|
3129
|
+
|
|
3130
|
+
/* Marks the currently-navigated-to file on the GitHub page — a separate fixed-position box
|
|
3131
|
+
tracking the target's getBoundingClientRect(), not an outline on the container itself (see
|
|
3132
|
+
github_diff_navigator.ts's applyHighlight() for why: GitHub's containment/stacking context
|
|
3133
|
+
swallows a direct outline). Persistent — removed only when navigateTo() moves to a
|
|
3134
|
+
different target, never timed out. pointer-events: none so it never blocks clicks/selection
|
|
3135
|
+
on the diff underneath. */
|
|
3136
|
+
.pr-helper-nav-spotlight {
|
|
3137
|
+
position: fixed;
|
|
3138
|
+
border: 2px solid #9333ea;
|
|
3139
|
+
border-radius: 6px;
|
|
3140
|
+
pointer-events: none;
|
|
3141
|
+
z-index: 999996; /* below the overlay's 999997, above ordinary page content */
|
|
3142
|
+
/* No dimming wash behind this. It used to carry `box-shadow: 0 0 0 2000px rgba(147,51,234,
|
|
3143
|
+
0.04)` — a full-page purple tint over everything outside the box, painted above all of
|
|
3144
|
+
GitHub's own content. At 4% it read as subtle until something saturated sat under it:
|
|
3145
|
+
GitHub's blue focus ring looked like it was rendering *behind* the page. The border and
|
|
3146
|
+
the file-tree row tint below already say which file is current without recolouring the
|
|
3147
|
+
rest of GitHub. */
|
|
3148
|
+
}
|
|
3149
|
+
|
|
3150
|
+
/* Applied by GitHubDiffNavigator to the current target's container and its first child.
|
|
3151
|
+
Suppresses GitHub's own styling on that one file, for as long as it's the highlighted one:
|
|
3152
|
+
|
|
3153
|
+
- Its rounded border, which the spotlight now sits directly on top of (the spotlight traces
|
|
3154
|
+
the container's own rect exactly — see applyHighlight's rect handling).
|
|
3155
|
+
- Its selected-file styling, which GitHub applies to the container when the file is reached
|
|
3156
|
+
through GitHub's own file tree. Blanked as outline and box-shadow as well as border-color,
|
|
3157
|
+
because only the border was ever suppressed and the rest showed through as blue edges
|
|
3158
|
+
around a purple box. Note this is only half of "one highlight at a time" — it covers our
|
|
3159
|
+
highlight landing on a file GitHub already marked. The other half is the reverse order, and
|
|
3160
|
+
that one can't be done in CSS: see watchForGitHubFileNavigation().
|
|
3161
|
+
- The focus ring GitHub puts on the file header's collapse caret when a #diff-<hash> fragment
|
|
3162
|
+
targets the file. GitHubDiffNavigator.releaseStolenFocus already blurs the focus we caused;
|
|
3163
|
+
this covers GitHub re-focusing it afterwards on its own. Scoped to the highlighted file
|
|
3164
|
+
rather than applied globally so keyboard focus everywhere else on the page still shows a
|
|
3165
|
+
ring — it's a deliberate, narrow trade of that affordance inside one file for a clean box. */
|
|
3166
|
+
.pr-helper-nav-target {
|
|
3167
|
+
border-color: transparent !important;
|
|
3168
|
+
outline: none !important;
|
|
3169
|
+
box-shadow: none !important;
|
|
3170
|
+
}
|
|
3171
|
+
|
|
3172
|
+
.pr-helper-nav-target :focus-visible {
|
|
3173
|
+
outline: none !important;
|
|
3174
|
+
box-shadow: none !important;
|
|
3175
|
+
}
|
|
3176
|
+
|
|
3177
|
+
/* The "Collapse file" bubble GitHub pops when its caret takes focus. GitHubDiffNavigator's
|
|
3178
|
+
focus guard is what actually prevents this (no focus, no tooltip); this is the backstop for
|
|
3179
|
+
the moments the guard isn't holding — GitHub renders these as <tool-tip> custom elements next
|
|
3180
|
+
to their trigger, so hiding the ones inside the highlighted file leaves every other tooltip
|
|
3181
|
+
on the page working normally. */
|
|
3182
|
+
.pr-helper-nav-target tool-tip {
|
|
3183
|
+
display: none !important;
|
|
3184
|
+
}
|
|
3185
|
+
|
|
3186
|
+
/* Marks the current target's row in GitHub's own file-tree sidebar — a plain class toggle
|
|
3187
|
+
works here (unlike the diff container, this row isn't subject to CSS containment), so no
|
|
3188
|
+
spotlight-tracking is needed. !important guards against GitHub's own selected/hover-state
|
|
3189
|
+
background rules on the same row winning by specificity. */
|
|
3190
|
+
.pr-helper-nav-tree-highlight {
|
|
3191
|
+
background: rgba(147, 51, 234, 0.14) !important;
|
|
3192
|
+
box-shadow: inset 3px 0 0 #9333ea;
|
|
3193
|
+
}
|
|
3194
|
+
|
|
3195
|
+
/* Marks the comment behind an "Already raised by @x" link, applied by
|
|
3196
|
+
github_comment_highlighter.ts after the jump. Same purple, same "border, no wash" treatment as
|
|
3197
|
+
the diff spotlight above, so following a link from the Issues tab to a prior comment looks
|
|
3198
|
+
like following one to a file.
|
|
3199
|
+
|
|
3200
|
+
Every declaration is !important because this is a fight with GitHub, not with ourselves: the
|
|
3201
|
+
jump keeps the #discussion_r<id> fragment (it's what expands a collapsed or resolved thread),
|
|
3202
|
+
and that fragment makes GitHub paint its own accent-blue border on the targeted comment. A
|
|
3203
|
+
plain class loses to GitHub's own rules on specificity, so ours have to outrank them.
|
|
3204
|
+
|
|
3205
|
+
No fixed-position spotlight box here, unlike the diff container: a timeline comment is
|
|
3206
|
+
ordinary flow content with no CSS containment to swallow the border. */
|
|
3207
|
+
.pr-helper-comment-highlight {
|
|
3208
|
+
border-color: #9333ea !important;
|
|
3209
|
+
border-radius: 6px !important;
|
|
3210
|
+
box-shadow: 0 0 0 2px #9333ea !important;
|
|
3211
|
+
}
|
|
3212
|
+
|
|
3213
|
+
/* The fragment's element is often a stub *inside* the comment box we highlighted, so GitHub's
|
|
3214
|
+
blue can still be sitting on a descendant after the rule above has recoloured the box itself.
|
|
3215
|
+
Scoped under our class so :target styling anywhere else on the page is left alone. */
|
|
3216
|
+
.pr-helper-comment-highlight :target {
|
|
3217
|
+
border-color: #9333ea !important;
|
|
3218
|
+
box-shadow: none !important;
|
|
3219
|
+
}
|
|
3220
|
+
|
|
3221
|
+
/* Marks the current target's row inside the Guide tab's section card, kept in sync with the
|
|
3222
|
+
overlay by PRHelperSidebar.syncGuidedNavUi(). */
|
|
3223
|
+
.pr-helper-guide-file-current {
|
|
3224
|
+
background: rgba(147, 51, 234, 0.12);
|
|
3225
|
+
border-radius: 6px;
|
|
3226
|
+
}
|
|
3227
|
+
|
|
3228
|
+
/* ── Toggle button — vertical tab ── */
|
|
3229
|
+
.pr-helper-toggle-button {
|
|
3230
|
+
position: fixed;
|
|
3231
|
+
top: 50%;
|
|
3232
|
+
right: 0;
|
|
3233
|
+
transform: translateY(-50%);
|
|
3234
|
+
background: #9333ea;
|
|
3235
|
+
border: 1px solid #7c3aed;
|
|
3236
|
+
border-right: none;
|
|
3237
|
+
border-radius: 6px 0 0 6px;
|
|
3238
|
+
padding: 16px 8px;
|
|
3239
|
+
cursor: pointer;
|
|
3240
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
|
3241
|
+
font-size: 11px;
|
|
3242
|
+
font-weight: 600;
|
|
3243
|
+
color: #ffffff;
|
|
3244
|
+
z-index: 999998;
|
|
3245
|
+
display: flex;
|
|
3246
|
+
flex-direction: column;
|
|
3247
|
+
align-items: center;
|
|
3248
|
+
gap: 8px;
|
|
3249
|
+
writing-mode: vertical-rl;
|
|
3250
|
+
transition: all 0.2s ease;
|
|
3251
|
+
box-shadow: -2px 0 8px rgba(147, 51, 234, 0.3);
|
|
3252
|
+
letter-spacing: 0.04em;
|
|
3253
|
+
}
|
|
3254
|
+
|
|
3255
|
+
.pr-helper-toggle-button:hover {
|
|
3256
|
+
background: #7c3aed;
|
|
3257
|
+
border-color: #6d28d9;
|
|
3258
|
+
padding-right: 10px;
|
|
3259
|
+
}
|
|
3260
|
+
|
|
3261
|
+
.pr-helper-toggle-button:disabled { opacity: 0.6; cursor: not-allowed; }
|
|
3262
|
+
|
|
3263
|
+
.pr-helper-toggle-button svg {
|
|
3264
|
+
width: 14px;
|
|
3265
|
+
height: 14px;
|
|
3266
|
+
flex-shrink: 0;
|
|
3267
|
+
transform: rotate(90deg);
|
|
3268
|
+
}
|
|
3269
|
+
|
|
3270
|
+
/* ── Floater → sidebar morph ──
|
|
3271
|
+
The floater is the origin point of the sidebar, not a separate control that opens one. Two
|
|
3272
|
+
phases, deliberately sequential rather than one combined move:
|
|
3273
|
+
|
|
3274
|
+
1. The button itself stretches along the edge it already sits on — from its own height,
|
|
3275
|
+
centred, out to the full viewport — becoming a purple rail. Its label fades as it goes,
|
|
3276
|
+
since a vertical word stretched over 900px is just noise.
|
|
3277
|
+
2. The sidebar slides out of that rail, using the SAME translateX transition the base rule
|
|
3278
|
+
has always used. Nothing about phase 2 is new, which is the point: the only new motion is
|
|
3279
|
+
phase 1, along an axis the button already occupies.
|
|
3280
|
+
|
|
3281
|
+
Splitting them this way keeps the sidebar's own transform and transition untouched. An earlier
|
|
3282
|
+
version grew a clip-path window over the panel instead, which had to override transform, fight
|
|
3283
|
+
the base transition, and hand both back on teardown — where clip-path reverts to the
|
|
3284
|
+
non-interpolable `none`, snapping the whole panel into view before sliding it off.
|
|
3285
|
+
|
|
3286
|
+
The rail is the real button, not a stand-in, so the purple, the radius and the shadow are the
|
|
3287
|
+
button's own by construction. z-index 999998 keeps it one below the sidebar, so phase 2 covers
|
|
3288
|
+
it and the restore afterwards is never seen. */
|
|
3289
|
+
/* Height AND width are deliberately absent here and driven inline from JS at both ends. The
|
|
3290
|
+
button's resting size follows its label, i.e. `auto`, which cannot interpolate — so the start
|
|
3291
|
+
values have to be written inline as pixels, and an inline value outranks this rule, meaning any
|
|
3292
|
+
end value declared here would simply never win. Everything else is fine to own from CSS.
|
|
3293
|
+
|
|
3294
|
+
The rail narrows as it grows: the button thins to exactly the width of the purple edge the
|
|
3295
|
+
sidebar carries (see the sidebar's inset box-shadow), so the line the button becomes is the same
|
|
3296
|
+
line the panel draws out. A rail at the button's full width reads as a slab. `overflow: hidden`
|
|
3297
|
+
keeps the label clipped rather than spilling as it goes. */
|
|
3298
|
+
.pr-helper-toggle-button--stretching {
|
|
3299
|
+
top: 0;
|
|
3300
|
+
transform: translateY(0);
|
|
3301
|
+
border-radius: 0;
|
|
3302
|
+
cursor: default;
|
|
3303
|
+
padding: 0;
|
|
3304
|
+
min-width: 0;
|
|
3305
|
+
overflow: hidden;
|
|
3306
|
+
box-sizing: border-box;
|
|
3307
|
+
transition:
|
|
3308
|
+
top 0.26s cubic-bezier(0.22, 0.61, 0.36, 1),
|
|
3309
|
+
height 0.26s cubic-bezier(0.22, 0.61, 0.36, 1),
|
|
3310
|
+
width 0.26s cubic-bezier(0.22, 0.61, 0.36, 1),
|
|
3311
|
+
padding 0.26s cubic-bezier(0.22, 0.61, 0.36, 1),
|
|
3312
|
+
transform 0.26s cubic-bezier(0.22, 0.61, 0.36, 1),
|
|
3313
|
+
border-radius 0.26s ease;
|
|
3314
|
+
}
|
|
3315
|
+
|
|
3316
|
+
/* The stretch reads as the button becoming an edge, so its content has to go. Faster than the
|
|
3317
|
+
stretch itself — the label should be gone well before the rail reaches full height. */
|
|
3318
|
+
.pr-helper-toggle-button--stretching svg,
|
|
3319
|
+
.pr-helper-toggle-button--stretching span {
|
|
3320
|
+
opacity: 0;
|
|
3321
|
+
transition: opacity 0.12s ease-out;
|
|
3322
|
+
}
|
|
3323
|
+
|
|
3324
|
+
/* Suppresses every transition for one frame so the rail can be handed back to its resting
|
|
3325
|
+
geometry without animating there. Applied while the open sidebar covers it. */
|
|
3326
|
+
.pr-helper-toggle-button--instant {
|
|
3327
|
+
transition: none;
|
|
3328
|
+
}
|
|
3329
|
+
|
|
3330
|
+
/* Phase 2's timing. Marginally slower than the base 0.3s and eased to match phase 1, so the two
|
|
3331
|
+
halves read as one movement rather than two clips spliced together. */
|
|
3332
|
+
.pr-helper-sidebar--from-rail {
|
|
3333
|
+
transition: transform 0.32s cubic-bezier(0.22, 0.61, 0.36, 1);
|
|
3334
|
+
}
|
|
3335
|
+
|
|
3336
|
+
/* Shown only while the panel has nothing in it at all — a first open, waiting on PR data. Bars,
|
|
3337
|
+
deliberately: a skeleton shaped like findings and pills would be promising content the review
|
|
3338
|
+
may not have. */
|
|
3339
|
+
.pr-helper-panel-skeleton {
|
|
3340
|
+
padding: 20px;
|
|
3341
|
+
}
|
|
3342
|
+
|
|
3343
|
+
.pr-helper-panel-skeleton-bar {
|
|
3344
|
+
position: relative;
|
|
3345
|
+
height: 12px;
|
|
3346
|
+
margin-bottom: 12px;
|
|
3347
|
+
border-radius: 4px;
|
|
3348
|
+
background: rgba(147, 51, 234, 0.09);
|
|
3349
|
+
overflow: hidden;
|
|
3350
|
+
}
|
|
3351
|
+
|
|
3352
|
+
.pr-helper-panel-skeleton-bar::after {
|
|
3353
|
+
content: '';
|
|
3354
|
+
position: absolute;
|
|
3355
|
+
top: 0;
|
|
3356
|
+
left: 0;
|
|
3357
|
+
height: 100%;
|
|
3358
|
+
width: 40%;
|
|
3359
|
+
background: linear-gradient(90deg, transparent, rgba(147, 51, 234, 0.18), transparent);
|
|
3360
|
+
animation: pr-helper-shimmer-sweep 1.6s ease-in-out infinite;
|
|
3361
|
+
}
|
|
3362
|
+
|
|
3363
|
+
/* What the panel says when GitHub cannot be reached and nothing has ever loaded. Full-bleed and
|
|
3364
|
+
quiet rather than a bordered error card: this is the entire content of the surface, not an alert
|
|
3365
|
+
sitting on top of content, and a panel whose only occupant is a red box overstates a connection
|
|
3366
|
+
problem the reviewer can usually fix in one click. */
|
|
3367
|
+
.pr-helper-panel-unreachable {
|
|
3368
|
+
padding: 28px 20px;
|
|
3369
|
+
}
|
|
3370
|
+
|
|
3371
|
+
.pr-helper-panel-unreachable-title {
|
|
3372
|
+
font-size: 13px;
|
|
3373
|
+
font-weight: 700;
|
|
3374
|
+
color: #9333ea;
|
|
3375
|
+
text-transform: uppercase;
|
|
3376
|
+
letter-spacing: 0.06em;
|
|
3377
|
+
margin-bottom: 10px;
|
|
3378
|
+
}
|
|
3379
|
+
|
|
3380
|
+
.pr-helper-panel-unreachable-body {
|
|
3381
|
+
margin: 0 0 16px;
|
|
3382
|
+
font-size: 13px;
|
|
3383
|
+
line-height: 1.55;
|
|
3384
|
+
color: var(--color-fg-muted, #57606a);
|
|
3385
|
+
}
|
|
3386
|
+
|
|
3387
|
+
.pr-helper-panel-retry {
|
|
3388
|
+
padding: 7px 14px;
|
|
3389
|
+
font-family: inherit;
|
|
3390
|
+
font-size: 12px;
|
|
3391
|
+
font-weight: 600;
|
|
3392
|
+
color: #ffffff;
|
|
3393
|
+
background: #9333ea;
|
|
3394
|
+
border: 1px solid #7c3aed;
|
|
3395
|
+
border-radius: 6px;
|
|
3396
|
+
cursor: pointer;
|
|
3397
|
+
transition: background 0.2s ease, border-color 0.2s ease;
|
|
3398
|
+
}
|
|
3399
|
+
|
|
3400
|
+
.pr-helper-panel-retry:hover {
|
|
3401
|
+
background: #7c3aed;
|
|
3402
|
+
border-color: #6d28d9;
|
|
3403
|
+
}
|
|
3404
|
+
|
|
3405
|
+
.pr-helper-panel-skeleton-bar:nth-child(1) { width: 55%; }
|
|
3406
|
+
.pr-helper-panel-skeleton-bar:nth-child(2) { width: 100%; }
|
|
3407
|
+
.pr-helper-panel-skeleton-bar:nth-child(3) { width: 85%; }
|
|
3408
|
+
.pr-helper-panel-skeleton-bar:nth-child(4) { width: 70%; }
|
|
3409
|
+
|
|
3410
|
+
/* ── Save for later ──
|
|
3411
|
+
Bottom-centre: the guided-nav overlay owns bottom-left, toasts and the rating prompt own
|
|
3412
|
+
bottom-right, and the toggle tab owns the right edge. z-index deliberately sits one below the
|
|
3413
|
+
nav overlay's 999997 — if a reviewer is mid-navigation, navigation is the thing that must stay
|
|
3414
|
+
clickable. Dark and purple rather than following GitHub's theme, because sidebar.css has no
|
|
3415
|
+
theme handling to inherit and a surface that guesses wrong is worse than one that commits. */
|
|
3416
|
+
.pr-helper-save-bar {
|
|
3417
|
+
position: fixed;
|
|
3418
|
+
bottom: 24px;
|
|
3419
|
+
left: 50%;
|
|
3420
|
+
transform: translateX(-50%);
|
|
3421
|
+
z-index: 999996;
|
|
3422
|
+
/* A column so the optional label editor can expand underneath the action row without the two
|
|
3423
|
+
competing for the same horizontal space. With the editor hidden this is still one row. */
|
|
3424
|
+
display: flex;
|
|
3425
|
+
flex-direction: column;
|
|
3426
|
+
max-width: calc(100vw - 48px);
|
|
3427
|
+
background: #161b26;
|
|
3428
|
+
border: 1px solid rgba(147, 51, 234, 0.35);
|
|
3429
|
+
border-radius: 10px;
|
|
3430
|
+
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.4);
|
|
3431
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
|
3432
|
+
opacity: 0;
|
|
3433
|
+
/* Only opacity and the entry lift are transitioned — transform also carries the sidebar offset,
|
|
3434
|
+
which must track a resize drag frame-for-frame rather than easing behind it. */
|
|
3435
|
+
transition: opacity 0.3s ease;
|
|
3436
|
+
pointer-events: none;
|
|
3437
|
+
}
|
|
3438
|
+
|
|
3439
|
+
.pr-helper-save-bar-visible { opacity: 1; pointer-events: auto; }
|
|
3440
|
+
|
|
3441
|
+
/* GitHub's layout is dense at narrow widths, and this is a floating element on somebody else's
|
|
3442
|
+
page — the right answer to "will it fit?" is not to shrink it until it does. */
|
|
3443
|
+
.pr-helper-save-bar-too-narrow { display: none; }
|
|
3444
|
+
|
|
3445
|
+
.pr-helper-save-bar-action {
|
|
3446
|
+
display: flex;
|
|
3447
|
+
align-items: center;
|
|
3448
|
+
gap: 12px;
|
|
3449
|
+
padding: 12px 16px;
|
|
3450
|
+
border: none;
|
|
3451
|
+
background: transparent;
|
|
3452
|
+
color: #f8fafc;
|
|
3453
|
+
cursor: pointer;
|
|
3454
|
+
font-family: inherit;
|
|
3455
|
+
text-align: left;
|
|
3456
|
+
border-radius: 10px 0 0 10px;
|
|
3457
|
+
transition: background 0.15s;
|
|
3458
|
+
}
|
|
3459
|
+
|
|
3460
|
+
.pr-helper-save-bar-action:hover { background: rgba(147, 51, 234, 0.12); }
|
|
3461
|
+
.pr-helper-save-bar-icon { flex-shrink: 0; color: #94a3b8; transition: color 0.15s; }
|
|
3462
|
+
.pr-helper-save-bar-saved .pr-helper-save-bar-icon { color: #a855f7; }
|
|
3463
|
+
|
|
3464
|
+
.pr-helper-save-bar-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
3465
|
+
|
|
3466
|
+
.pr-helper-save-bar-title {
|
|
3467
|
+
font-size: 13px;
|
|
3468
|
+
font-weight: 600;
|
|
3469
|
+
line-height: 1.3;
|
|
3470
|
+
}
|
|
3471
|
+
|
|
3472
|
+
.pr-helper-save-bar-detail {
|
|
3473
|
+
font-size: 12px;
|
|
3474
|
+
color: #94a3b8;
|
|
3475
|
+
line-height: 1.3;
|
|
3476
|
+
}
|
|
3477
|
+
|
|
3478
|
+
.pr-helper-save-bar-dismiss {
|
|
3479
|
+
display: flex;
|
|
3480
|
+
align-items: center;
|
|
3481
|
+
padding: 0 12px;
|
|
3482
|
+
border: none;
|
|
3483
|
+
border-left: 1px solid rgba(255, 255, 255, 0.08);
|
|
3484
|
+
background: transparent;
|
|
3485
|
+
color: #64748b;
|
|
3486
|
+
cursor: pointer;
|
|
3487
|
+
border-radius: 0 10px 10px 0;
|
|
3488
|
+
transition: color 0.15s, background 0.15s;
|
|
3489
|
+
}
|
|
3490
|
+
|
|
3491
|
+
.pr-helper-save-bar-dismiss:hover { color: #f8fafc; background: rgba(255, 255, 255, 0.05); }
|
|
3492
|
+
|
|
3493
|
+
.pr-helper-save-bar-row { display: flex; align-items: stretch; }
|
|
3494
|
+
|
|
3495
|
+
/* The optional follow-up to a save. Appears only once the PR is saved, so saving itself stays one
|
|
3496
|
+
click — choosing labels is never a step on the way to it. */
|
|
3497
|
+
.pr-helper-save-bar-labels-action {
|
|
3498
|
+
display: flex;
|
|
3499
|
+
align-items: center;
|
|
3500
|
+
padding: 0 14px;
|
|
3501
|
+
border: none;
|
|
3502
|
+
border-left: 1px solid rgba(255, 255, 255, 0.08);
|
|
3503
|
+
background: transparent;
|
|
3504
|
+
color: #a855f7;
|
|
3505
|
+
cursor: pointer;
|
|
3506
|
+
font-family: inherit;
|
|
3507
|
+
font-size: 12px;
|
|
3508
|
+
font-weight: 600;
|
|
3509
|
+
white-space: nowrap;
|
|
3510
|
+
transition: background 0.15s;
|
|
3511
|
+
}
|
|
3512
|
+
|
|
3513
|
+
.pr-helper-save-bar-labels-action[hidden] { display: none; }
|
|
3514
|
+
.pr-helper-save-bar-labels-action:hover { background: rgba(147, 51, 234, 0.12); }
|
|
3515
|
+
|
|
3516
|
+
.pr-helper-save-bar-labels {
|
|
3517
|
+
display: flex;
|
|
3518
|
+
align-items: center;
|
|
3519
|
+
gap: 6px;
|
|
3520
|
+
flex-wrap: wrap;
|
|
3521
|
+
padding: 10px 16px 12px;
|
|
3522
|
+
border-top: 1px solid rgba(255, 255, 255, 0.08);
|
|
3523
|
+
}
|
|
3524
|
+
|
|
3525
|
+
.pr-helper-save-bar-labels[hidden] { display: none; }
|
|
3526
|
+
|
|
3527
|
+
.pr-helper-save-bar-chip {
|
|
3528
|
+
display: inline-flex;
|
|
3529
|
+
align-items: center;
|
|
3530
|
+
gap: 4px;
|
|
3531
|
+
padding: 2px 8px;
|
|
3532
|
+
border-radius: 100px;
|
|
3533
|
+
background: rgba(147, 51, 234, 0.12);
|
|
3534
|
+
color: #d8b4fe;
|
|
3535
|
+
font-size: 11px;
|
|
3536
|
+
font-weight: 600;
|
|
3537
|
+
white-space: nowrap;
|
|
3538
|
+
}
|
|
3539
|
+
|
|
3540
|
+
.pr-helper-save-bar-chip-remove {
|
|
3541
|
+
border: none;
|
|
3542
|
+
background: transparent;
|
|
3543
|
+
color: #94a3b8;
|
|
3544
|
+
font-size: 13px;
|
|
3545
|
+
line-height: 1;
|
|
3546
|
+
padding: 0;
|
|
3547
|
+
cursor: pointer;
|
|
3548
|
+
transition: color 0.15s;
|
|
3549
|
+
}
|
|
3550
|
+
|
|
3551
|
+
.pr-helper-save-bar-chip-remove:hover { color: #f87171; }
|
|
3552
|
+
|
|
3553
|
+
.pr-helper-save-bar-label-input {
|
|
3554
|
+
flex: 1 1 140px;
|
|
3555
|
+
min-width: 120px;
|
|
3556
|
+
padding: 5px 8px;
|
|
3557
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
3558
|
+
border-radius: 6px;
|
|
3559
|
+
background: #0b0f17;
|
|
3560
|
+
color: #f8fafc;
|
|
3561
|
+
font-family: inherit;
|
|
3562
|
+
font-size: 12px;
|
|
3563
|
+
}
|
|
3564
|
+
|
|
3565
|
+
.pr-helper-save-bar-label-input::placeholder { color: #64748b; }
|
|
3566
|
+
.pr-helper-save-bar-label-input:focus { outline: none; border-color: #9333ea; }
|
|
3567
|
+
.pr-helper-save-bar-label-input:disabled { cursor: not-allowed; opacity: 0.6; }
|
|
3568
|
+
|
|
3569
|
+
.pr-helper-save-bar-label-done {
|
|
3570
|
+
border: none;
|
|
3571
|
+
background: transparent;
|
|
3572
|
+
color: #a855f7;
|
|
3573
|
+
font-family: inherit;
|
|
3574
|
+
font-size: 12px;
|
|
3575
|
+
font-weight: 600;
|
|
3576
|
+
padding: 4px 6px;
|
|
3577
|
+
cursor: pointer;
|
|
3578
|
+
}
|
|
3579
|
+
|
|
3580
|
+
.pr-helper-save-bar-label-done:hover { text-decoration: underline; }
|
|
3581
|
+
|
|
3582
|
+
/* ── Selected-text → Assistant ──
|
|
3583
|
+
Mounted on document.body, not inside the sidebar: the sidebar is a scrolling, clipped
|
|
3584
|
+
flex column, so a popover anchored to viewport coordinates has to live outside it to sit
|
|
3585
|
+
over the selected text. z-index clears the sidebar's own 999999 for the same reason.
|
|
3586
|
+
See selection_action.ts. */
|
|
3587
|
+
.pr-helper-selection-popover {
|
|
3588
|
+
position: fixed;
|
|
3589
|
+
z-index: 1000000;
|
|
3590
|
+
display: flex;
|
|
3591
|
+
flex-direction: column;
|
|
3592
|
+
background: #ffffff;
|
|
3593
|
+
border: 1px solid rgba(0, 0, 0, 0.12);
|
|
3594
|
+
border-radius: 8px;
|
|
3595
|
+
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
|
|
3596
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
|
3597
|
+
}
|
|
3598
|
+
|
|
3599
|
+
/* Not clipped with overflow: hidden — the copy tooltip has to escape the popover — so the two
|
|
3600
|
+
bar buttons round their own outer corners instead. */
|
|
3601
|
+
.pr-helper-selection-bar {
|
|
3602
|
+
position: relative;
|
|
3603
|
+
display: flex;
|
|
3604
|
+
align-items: stretch;
|
|
3605
|
+
}
|
|
3606
|
+
|
|
3607
|
+
.pr-helper-selection-trigger {
|
|
3608
|
+
display: flex;
|
|
3609
|
+
align-items: center;
|
|
3610
|
+
gap: 6px;
|
|
3611
|
+
padding: 6px 10px;
|
|
3612
|
+
border: none;
|
|
3613
|
+
border-radius: 7px 0 0 7px;
|
|
3614
|
+
background: #9333ea;
|
|
3615
|
+
color: #ffffff;
|
|
3616
|
+
font-family: inherit;
|
|
3617
|
+
font-size: 12px;
|
|
3618
|
+
font-weight: 600;
|
|
3619
|
+
cursor: pointer;
|
|
3620
|
+
white-space: nowrap;
|
|
3621
|
+
}
|
|
3622
|
+
|
|
3623
|
+
.pr-helper-selection-trigger:hover { background: #7c3aed; }
|
|
3624
|
+
|
|
3625
|
+
/* Deliberately secondary to the trigger: icon-only and unfilled, so the bar still reads as one
|
|
3626
|
+
primary action plus a utility rather than two competing CTAs. */
|
|
3627
|
+
.pr-helper-selection-copy {
|
|
3628
|
+
display: flex;
|
|
3629
|
+
align-items: center;
|
|
3630
|
+
justify-content: center;
|
|
3631
|
+
padding: 6px 10px;
|
|
3632
|
+
border: none;
|
|
3633
|
+
border-radius: 0 7px 7px 0;
|
|
3634
|
+
background: transparent;
|
|
3635
|
+
color: var(--color-fg-muted, #656d76);
|
|
3636
|
+
cursor: pointer;
|
|
3637
|
+
}
|
|
3638
|
+
|
|
3639
|
+
.pr-helper-selection-copy:hover {
|
|
3640
|
+
background: rgba(147, 51, 234, 0.1);
|
|
3641
|
+
color: #9333ea;
|
|
3642
|
+
}
|
|
3643
|
+
|
|
3644
|
+
.pr-helper-selection-copy:focus-visible {
|
|
3645
|
+
outline: 2px solid #9333ea;
|
|
3646
|
+
outline-offset: -2px;
|
|
3647
|
+
}
|
|
3648
|
+
|
|
3649
|
+
.pr-helper-selection-check-icon { display: none; }
|
|
3650
|
+
.pr-helper-selection-copy-done .pr-helper-selection-copy-icon { display: none; }
|
|
3651
|
+
.pr-helper-selection-copy-done .pr-helper-selection-check-icon { display: block; }
|
|
3652
|
+
.pr-helper-selection-copy-done .pr-helper-selection-copy { color: #1a7f37; }
|
|
3653
|
+
|
|
3654
|
+
/* Hover *and* focus, since the popover is keyboard reachable and a title attribute would only
|
|
3655
|
+
cover the pointer. */
|
|
3656
|
+
.pr-helper-selection-tip {
|
|
3657
|
+
position: absolute;
|
|
3658
|
+
top: 100%;
|
|
3659
|
+
right: 0;
|
|
3660
|
+
margin-top: 6px;
|
|
3661
|
+
padding: 4px 8px;
|
|
3662
|
+
border-radius: 6px;
|
|
3663
|
+
background: rgba(28, 30, 33, 0.92);
|
|
3664
|
+
color: #ffffff;
|
|
3665
|
+
font-size: 11px;
|
|
3666
|
+
line-height: 1.4;
|
|
3667
|
+
white-space: nowrap;
|
|
3668
|
+
pointer-events: none;
|
|
3669
|
+
opacity: 0;
|
|
3670
|
+
transition: opacity 90ms ease-in;
|
|
3671
|
+
}
|
|
3672
|
+
|
|
3673
|
+
.pr-helper-selection-copy:hover ~ .pr-helper-selection-tip,
|
|
3674
|
+
.pr-helper-selection-copy:focus-visible ~ .pr-helper-selection-tip {
|
|
3675
|
+
opacity: 1;
|
|
3676
|
+
}
|
|
3677
|
+
|
|
3678
|
+
/* Stage 1 is the trigger bar; clicking it swaps to the action menu in place, so the
|
|
3679
|
+
popover never moves out from under the cursor between the two stages. */
|
|
3680
|
+
.pr-helper-selection-menu { display: none; }
|
|
3681
|
+
.pr-helper-selection-popover-expanded .pr-helper-selection-bar { display: none; }
|
|
3682
|
+
|
|
3683
|
+
.pr-helper-selection-popover-expanded .pr-helper-selection-menu {
|
|
3684
|
+
display: flex;
|
|
3685
|
+
flex-direction: column;
|
|
3686
|
+
min-width: 220px;
|
|
3687
|
+
padding: 4px;
|
|
3688
|
+
}
|
|
3689
|
+
|
|
3690
|
+
.pr-helper-selection-menu-item {
|
|
3691
|
+
padding: 7px 10px;
|
|
3692
|
+
border: none;
|
|
3693
|
+
border-radius: 6px;
|
|
3694
|
+
background: transparent;
|
|
3695
|
+
color: var(--color-fg-default, #24292f);
|
|
3696
|
+
font-family: inherit;
|
|
3697
|
+
font-size: 12px;
|
|
3698
|
+
text-align: left;
|
|
3699
|
+
cursor: pointer;
|
|
3700
|
+
white-space: nowrap;
|
|
3701
|
+
}
|
|
3702
|
+
|
|
3703
|
+
.pr-helper-selection-menu-item:hover {
|
|
3704
|
+
background: rgba(147, 51, 234, 0.1);
|
|
3705
|
+
color: #9333ea;
|
|
3706
|
+
}
|
|
3707
|
+
|
|
3708
|
+
/* The flat layout has no trigger to expand: its two items are the popover. Used where the list is
|
|
3709
|
+
short enough that a click to reveal it costs more than it saves, which is the assistant's own
|
|
3710
|
+
replies. */
|
|
3711
|
+
.pr-helper-selection-popover-flat .pr-helper-selection-menu {
|
|
3712
|
+
display: flex;
|
|
3713
|
+
flex-direction: column;
|
|
3714
|
+
min-width: 150px;
|
|
3715
|
+
padding: 4px;
|
|
3716
|
+
}
|
|
3717
|
+
|
|
3718
|
+
/* Copy is a utility rather than a question, so it sits under a rule and reads quieter. It keeps
|
|
3719
|
+
the popover open, the same as the icon does in the bar above. */
|
|
3720
|
+
.pr-helper-selection-copy-item {
|
|
3721
|
+
margin-top: 4px;
|
|
3722
|
+
padding-top: 8px;
|
|
3723
|
+
border-top: 1px solid rgba(0, 0, 0, 0.08);
|
|
3724
|
+
color: var(--color-fg-muted, #656d76);
|
|
3725
|
+
}
|
|
3726
|
+
|
|
3727
|
+
.pr-helper-selection-copy-done .pr-helper-selection-copy-item { color: #1a7f37; }
|
|
3728
|
+
|
|
3729
|
+
/* The last action ("Ask a question") is a different kind of thing from the four canned
|
|
3730
|
+
prompts — it opens an empty question rather than pre-filling one. Not in the flat layout,
|
|
3731
|
+
where the last item is Copy and has a rule of its own. */
|
|
3732
|
+
.pr-helper-selection-popover:not(.pr-helper-selection-popover-flat) .pr-helper-selection-menu-item:last-child {
|
|
3733
|
+
border-top: 1px solid rgba(0, 0, 0, 0.08);
|
|
3734
|
+
border-radius: 0 0 6px 6px;
|
|
3735
|
+
margin-top: 4px;
|
|
3736
|
+
padding-top: 8px;
|
|
3737
|
+
color: var(--color-fg-muted, #656d76);
|
|
3738
|
+
}
|
|
3739
|
+
|
|
3740
|
+
/* The guide-progress transitions are feedback, not information — every state they animate is
|
|
3741
|
+
also expressed by a check icon, a Completed pill and a counter, so honouring a reduced-motion
|
|
3742
|
+
preference costs the reviewer nothing. Scoped to these rules rather than a blanket
|
|
3743
|
+
`* { transition: none }` so the sidebar's existing loading/thinking indicators are untouched. */
|
|
3744
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3745
|
+
.pr-helper-guide-section,
|
|
3746
|
+
.pr-helper-progress-bar-fill,
|
|
3747
|
+
.pr-helper-nav-overlay-btn,
|
|
3748
|
+
.pr-helper-selection-tip {
|
|
3749
|
+
transition-duration: 0.01ms;
|
|
3750
|
+
}
|
|
3751
|
+
|
|
3752
|
+
/* The morph is pure feedback — where the sidebar came from, nothing the reviewer needs to read.
|
|
3753
|
+
Collapsing it to an instant open costs them nothing, and the JS still cleans up on the
|
|
3754
|
+
transitionend these durations still fire. */
|
|
3755
|
+
.pr-helper-toggle-button--stretching,
|
|
3756
|
+
.pr-helper-toggle-button--stretching svg,
|
|
3757
|
+
.pr-helper-toggle-button--stretching span,
|
|
3758
|
+
.pr-helper-sidebar--from-rail {
|
|
3759
|
+
transition-duration: 0.01ms;
|
|
3760
|
+
}
|
|
3761
|
+
}
|
|
3762
|
+
|
|
3763
|
+
/* Suggested themes — the Guide's second half.
|
|
3764
|
+
Deliberately quieter than a Guide section card: these are places nobody looked, so they must
|
|
3765
|
+
not compete visually with sections describing code the review actually read. Flat background,
|
|
3766
|
+
no border, no status affordances, nothing clickable. The reviewer should be able to tell the
|
|
3767
|
+
two apart without reading the heading. */
|
|
3768
|
+
.pr-helper-suggested-areas {
|
|
3769
|
+
margin-top: 20px;
|
|
3770
|
+
padding-top: 14px;
|
|
3771
|
+
border-top: 1px solid var(--color-border-muted, #d8dee4);
|
|
3772
|
+
}
|
|
3773
|
+
|
|
3774
|
+
.pr-helper-suggested-areas-heading {
|
|
3775
|
+
margin: 0 0 4px;
|
|
3776
|
+
font-size: 12px;
|
|
3777
|
+
font-weight: 600;
|
|
3778
|
+
color: var(--color-fg-default, #1f2328);
|
|
3779
|
+
}
|
|
3780
|
+
|
|
3781
|
+
.pr-helper-suggested-areas-intro {
|
|
3782
|
+
margin: 0 0 10px;
|
|
3783
|
+
font-size: 11px;
|
|
3784
|
+
line-height: 1.4;
|
|
3785
|
+
color: var(--color-fg-muted, #57606a);
|
|
3786
|
+
}
|
|
3787
|
+
|
|
3788
|
+
.pr-helper-suggested-area {
|
|
3789
|
+
padding: 8px 10px;
|
|
3790
|
+
margin-bottom: 6px;
|
|
3791
|
+
border-radius: 6px;
|
|
3792
|
+
background: var(--color-canvas-subtle, #f6f8fa);
|
|
3793
|
+
}
|
|
3794
|
+
|
|
3795
|
+
.pr-helper-suggested-area-head {
|
|
3796
|
+
display: flex;
|
|
3797
|
+
align-items: baseline;
|
|
3798
|
+
justify-content: space-between;
|
|
3799
|
+
gap: 8px;
|
|
3800
|
+
}
|
|
3801
|
+
|
|
3802
|
+
.pr-helper-suggested-area-title {
|
|
3803
|
+
font-size: 12px;
|
|
3804
|
+
font-weight: 600;
|
|
3805
|
+
color: var(--color-fg-default, #1f2328);
|
|
3806
|
+
}
|
|
3807
|
+
|
|
3808
|
+
.pr-helper-suggested-area-count {
|
|
3809
|
+
flex-shrink: 0;
|
|
3810
|
+
font-size: 11px;
|
|
3811
|
+
font-variant-numeric: tabular-nums;
|
|
3812
|
+
color: var(--color-fg-muted, #57606a);
|
|
3813
|
+
}
|
|
3814
|
+
|
|
3815
|
+
.pr-helper-suggested-area-why,
|
|
3816
|
+
.pr-helper-suggested-area-risk {
|
|
3817
|
+
margin: 4px 0 0;
|
|
3818
|
+
font-size: 11px;
|
|
3819
|
+
line-height: 1.45;
|
|
3820
|
+
color: var(--color-fg-muted, #57606a);
|
|
3821
|
+
}
|
|
3822
|
+
|
|
3823
|
+
/* Amber, matching the Files tab's unavailable badge: something to watch, not something found. */
|
|
3824
|
+
.pr-helper-suggested-area-risk {
|
|
3825
|
+
color: var(--color-attention-fg, #9a6700);
|
|
3826
|
+
}
|
|
3827
|
+
|
|
3828
|
+
/* Muted, unlike the section badges beside it: "Explained" is a note about what the reviewer has
|
|
3829
|
+
already asked, not a claim that the review covered this file. It should read as quieter than a
|
|
3830
|
+
real review badge, because it means less. */
|
|
3831
|
+
.pr-helper-file-badge-explained {
|
|
3832
|
+
display: inline-block;
|
|
3833
|
+
padding: 1px 6px;
|
|
3834
|
+
border-radius: 10px;
|
|
3835
|
+
font-size: 10px;
|
|
3836
|
+
color: var(--color-fg-muted, #57606a);
|
|
3837
|
+
background: var(--color-neutral-subtle, rgba(175, 184, 193, 0.2));
|
|
3838
|
+
}
|
|
3839
|
+
|
|
3840
|
+
.pr-helper-file-explained-focus {
|
|
3841
|
+
font-style: italic;
|
|
3842
|
+
color: var(--color-fg-muted, #57606a);
|
|
3843
|
+
}
|
|
3844
|
+
|
|
3845
|
+
/* Busy state: the label sits beside a real Cancel, since a follow-up never shows the generating
|
|
3846
|
+
view that normally hosts one. */
|
|
3847
|
+
.pr-helper-suggested-area-running {
|
|
3848
|
+
display: flex;
|
|
3849
|
+
align-items: center;
|
|
3850
|
+
gap: 8px;
|
|
3851
|
+
margin-top: 10px;
|
|
3852
|
+
}
|
|
3853
|
+
|
|
3854
|
+
.pr-helper-suggested-area-busy {
|
|
3855
|
+
font-size: 11px;
|
|
3856
|
+
color: var(--color-fg-muted, #57606a);
|
|
3857
|
+
}
|
|
3858
|
+
|
|
3859
|
+
.pr-helper-suggested-area-cancel {
|
|
3860
|
+
padding: 4px 10px;
|
|
3861
|
+
font-size: 11px;
|
|
3862
|
+
font-family: inherit;
|
|
3863
|
+
color: var(--color-danger-fg, #cf222e);
|
|
3864
|
+
background: transparent;
|
|
3865
|
+
border: 1px solid var(--color-border-default, #d0d7de);
|
|
3866
|
+
border-radius: 6px;
|
|
3867
|
+
cursor: pointer;
|
|
3868
|
+
}
|
|
3869
|
+
|
|
3870
|
+
.pr-helper-suggested-area-cancel:hover {
|
|
3871
|
+
border-color: var(--color-danger-fg, #cf222e);
|
|
3872
|
+
}
|
|
3873
|
+
|
|
3874
|
+
/* The action that turns a suggested area into a reviewed one. Deliberately quiet — an area card
|
|
3875
|
+
is an offer, not an outstanding task, and a row of primary buttons would read as a to-do list
|
|
3876
|
+
the reviewer is behind on. It gains weight on hover, where intent is already expressed. */
|
|
3877
|
+
.pr-helper-suggested-area-action {
|
|
3878
|
+
margin: 10px 0 0;
|
|
3879
|
+
padding: 4px 10px;
|
|
3880
|
+
font-size: 11px;
|
|
3881
|
+
font-family: inherit;
|
|
3882
|
+
color: var(--color-accent-fg, #0969da);
|
|
3883
|
+
background: transparent;
|
|
3884
|
+
border: 1px solid var(--color-border-default, #d0d7de);
|
|
3885
|
+
border-radius: 6px;
|
|
3886
|
+
cursor: pointer;
|
|
3887
|
+
}
|
|
3888
|
+
|
|
3889
|
+
.pr-helper-suggested-area-action:hover:not(:disabled) {
|
|
3890
|
+
background: var(--color-accent-subtle, #ddf4ff);
|
|
3891
|
+
border-color: var(--color-accent-fg, #0969da);
|
|
3892
|
+
}
|
|
3893
|
+
|
|
3894
|
+
.pr-helper-suggested-area-action:disabled {
|
|
3895
|
+
color: var(--color-fg-muted, #57606a);
|
|
3896
|
+
cursor: default;
|
|
3897
|
+
}
|
|
3898
|
+
|
|
3899
|
+
.pr-helper-suggested-area-remainder {
|
|
3900
|
+
margin: 8px 0 0;
|
|
3901
|
+
font-size: 11px;
|
|
3902
|
+
line-height: 1.4;
|
|
3903
|
+
color: var(--color-fg-muted, #57606a);
|
|
3904
|
+
}
|
|
3905
|
+
|
|
3906
|
+
/* Issue confidence — only rendered below high, so it always means "this is qualified".
|
|
3907
|
+
Quieter than the severity badge beside it: severity is the finding's weight and stays the
|
|
3908
|
+
card's primary signal, while confidence modifies it. */
|
|
3909
|
+
.pr-helper-issue-confidence {
|
|
3910
|
+
font-size: 10px;
|
|
3911
|
+
font-weight: 600;
|
|
3912
|
+
padding: 1px 6px;
|
|
3913
|
+
border-radius: 10px;
|
|
3914
|
+
white-space: nowrap;
|
|
3915
|
+
background: var(--color-neutral-muted, rgba(175, 184, 193, 0.2));
|
|
3916
|
+
color: var(--color-fg-muted, #57606a);
|
|
3917
|
+
}
|
|
3918
|
+
|
|
3919
|
+
.pr-helper-issue-confidence-low {
|
|
3920
|
+
color: var(--color-attention-fg, #9a6700);
|
|
3921
|
+
}
|
|
3922
|
+
|
|
3923
|
+
/* Provisional risk. Sits inline with the risk label rather than replacing it — the verdict is
|
|
3924
|
+
still the headline, it just stops claiming to be final. */
|
|
3925
|
+
.pr-helper-risk-provisional {
|
|
3926
|
+
font-size: 10px;
|
|
3927
|
+
font-weight: 600;
|
|
3928
|
+
text-transform: uppercase;
|
|
3929
|
+
letter-spacing: 0.04em;
|
|
3930
|
+
padding: 2px 6px;
|
|
3931
|
+
border-radius: 10px;
|
|
3932
|
+
border: 1px solid var(--color-attention-emphasis, #9a6700);
|
|
3933
|
+
color: var(--color-attention-fg, #9a6700);
|
|
3934
|
+
}
|
|
3935
|
+
|
|
3936
|
+
.pr-helper-risk-provisional-reason {
|
|
3937
|
+
margin: 6px 0 0;
|
|
3938
|
+
font-size: 11px;
|
|
3939
|
+
line-height: 1.45;
|
|
3940
|
+
color: var(--color-fg-muted, #57606a);
|
|
3941
|
+
}
|
|
3942
|
+
|
|
3943
|
+
/* Review details — provenance, collapsed.
|
|
3944
|
+
Deliberately the quietest thing on the Overview: it answers "how was this made?", which is a
|
|
3945
|
+
question the reviewer only asks when something looks off. It sits below the suggested path and
|
|
3946
|
+
above Next steps, so the page still ends on what to do rather than on how it was produced. */
|
|
3947
|
+
.pr-helper-review-details {
|
|
3948
|
+
margin: 4px 0 14px;
|
|
3949
|
+
font-size: 12px;
|
|
3950
|
+
color: var(--color-fg-muted, #57606a);
|
|
3951
|
+
}
|
|
3952
|
+
|
|
3953
|
+
.pr-helper-review-details-summary {
|
|
3954
|
+
cursor: pointer;
|
|
3955
|
+
list-style: none;
|
|
3956
|
+
display: inline-flex;
|
|
3957
|
+
align-items: center;
|
|
3958
|
+
gap: 4px;
|
|
3959
|
+
padding: 2px 0;
|
|
3960
|
+
user-select: none;
|
|
3961
|
+
}
|
|
3962
|
+
|
|
3963
|
+
.pr-helper-review-details-summary::-webkit-details-marker { display: none; }
|
|
3964
|
+
|
|
3965
|
+
/* Own marker so it can rotate — the native one cannot be styled consistently across the themes
|
|
3966
|
+
GitHub renders this sidebar inside. */
|
|
3967
|
+
.pr-helper-review-details-summary::before {
|
|
3968
|
+
content: '▶';
|
|
3969
|
+
font-size: 8px;
|
|
3970
|
+
transition: transform 0.12s;
|
|
3971
|
+
}
|
|
3972
|
+
|
|
3973
|
+
.pr-helper-review-details[open] .pr-helper-review-details-summary::before {
|
|
3974
|
+
transform: rotate(90deg);
|
|
3975
|
+
}
|
|
3976
|
+
|
|
3977
|
+
.pr-helper-review-details-summary:hover { color: #9333ea; }
|
|
3978
|
+
|
|
3979
|
+
.pr-helper-review-details-list {
|
|
3980
|
+
margin: 6px 0 0;
|
|
3981
|
+
padding-left: 18px;
|
|
3982
|
+
line-height: 1.5;
|
|
3983
|
+
}
|
|
3984
|
+
|
|
3985
|
+
.pr-helper-review-details-list li { margin-bottom: 3px; }
|
|
3986
|
+
|
|
3987
|
+
/* ── Repository Investigation ───────────────────────────────────────────────
|
|
3988
|
+
The escalation action on issue and question cards, plus the modal that shows the generated
|
|
3989
|
+
prompt. Deliberately a secondary/outlined button, not a new primary style: escalating to a
|
|
3990
|
+
repository agent is one option among several on a finding, not the thing the card is asking
|
|
3991
|
+
the reviewer to do. */
|
|
3992
|
+
|
|
3993
|
+
.pr-helper-issue-actions {
|
|
3994
|
+
display: flex;
|
|
3995
|
+
align-items: center;
|
|
3996
|
+
justify-content: flex-end;
|
|
3997
|
+
gap: 12px;
|
|
3998
|
+
margin-top: 12px;
|
|
3999
|
+
}
|
|
4000
|
+
|
|
4001
|
+
/* margin-left:auto rather than relying on the row's space-between: the jump link beside it is
|
|
4002
|
+
conditional (a question without a file has none), and space-between alone would swing this
|
|
4003
|
+
group to the left edge whenever it is the row's only child. Wraps because three controls do
|
|
4004
|
+
not fit a 320px sidebar on one line. */
|
|
4005
|
+
.pr-helper-question-actions-group {
|
|
4006
|
+
display: flex;
|
|
4007
|
+
align-items: center;
|
|
4008
|
+
flex-wrap: wrap;
|
|
4009
|
+
justify-content: flex-end;
|
|
4010
|
+
gap: 8px;
|
|
4011
|
+
margin-left: auto;
|
|
4012
|
+
}
|
|
4013
|
+
|
|
4014
|
+
/* Same geometry as .pr-helper-question-answer-toggle, so the action reads identically whether it
|
|
4015
|
+
sits alone on an issue card or beside Jump to file / Mark answered on a question card. */
|
|
4016
|
+
.pr-helper-investigate-btn {
|
|
4017
|
+
padding: 5px 10px;
|
|
4018
|
+
border-radius: 6px;
|
|
4019
|
+
border: 1px solid rgba(0, 0, 0, 0.15);
|
|
4020
|
+
background: #ffffff;
|
|
4021
|
+
color: #1f2328;
|
|
4022
|
+
font-size: 12px;
|
|
4023
|
+
font-weight: 600;
|
|
4024
|
+
font-family: inherit;
|
|
4025
|
+
cursor: pointer;
|
|
4026
|
+
white-space: nowrap;
|
|
4027
|
+
}
|
|
4028
|
+
|
|
4029
|
+
.pr-helper-investigate-btn:hover {
|
|
4030
|
+
border-color: #9333ea;
|
|
4031
|
+
color: #9333ea;
|
|
4032
|
+
}
|
|
4033
|
+
|
|
4034
|
+
.pr-helper-prompt-modal {
|
|
4035
|
+
position: fixed;
|
|
4036
|
+
inset: 0;
|
|
4037
|
+
/* Above the sidebar's 999999 — the modal is body-mounted and must sit over it, not behind. */
|
|
4038
|
+
z-index: 1000001;
|
|
4039
|
+
display: flex;
|
|
4040
|
+
align-items: center;
|
|
4041
|
+
justify-content: center;
|
|
4042
|
+
opacity: 0;
|
|
4043
|
+
transition: opacity 0.15s ease;
|
|
4044
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
|
4045
|
+
}
|
|
4046
|
+
|
|
4047
|
+
.pr-helper-prompt-modal.show { opacity: 1; }
|
|
4048
|
+
|
|
4049
|
+
.pr-helper-prompt-modal-backdrop {
|
|
4050
|
+
position: absolute;
|
|
4051
|
+
inset: 0;
|
|
4052
|
+
background: rgba(0, 0, 0, 0.45);
|
|
4053
|
+
}
|
|
4054
|
+
|
|
4055
|
+
.pr-helper-prompt-modal-card {
|
|
4056
|
+
position: relative;
|
|
4057
|
+
width: min(680px, calc(100vw - 48px));
|
|
4058
|
+
max-height: calc(100vh - 96px);
|
|
4059
|
+
display: flex;
|
|
4060
|
+
flex-direction: column;
|
|
4061
|
+
padding: 20px;
|
|
4062
|
+
background: #ffffff;
|
|
4063
|
+
color: #1f2328;
|
|
4064
|
+
border-radius: 12px;
|
|
4065
|
+
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.28);
|
|
4066
|
+
transform: translateY(8px);
|
|
4067
|
+
transition: transform 0.15s ease;
|
|
4068
|
+
}
|
|
4069
|
+
|
|
4070
|
+
.pr-helper-prompt-modal.show .pr-helper-prompt-modal-card { transform: translateY(0); }
|
|
4071
|
+
|
|
4072
|
+
.pr-helper-prompt-modal-title {
|
|
4073
|
+
margin: 0 0 10px;
|
|
4074
|
+
font-size: 16px;
|
|
4075
|
+
font-weight: 600;
|
|
4076
|
+
}
|
|
4077
|
+
|
|
4078
|
+
.pr-helper-prompt-modal-body {
|
|
4079
|
+
margin: 0 0 8px;
|
|
4080
|
+
font-size: 13px;
|
|
4081
|
+
line-height: 1.5;
|
|
4082
|
+
color: #57606a;
|
|
4083
|
+
}
|
|
4084
|
+
|
|
4085
|
+
/* Monospace and scrollable: this is a prompt about code, and the diff inside it only reads
|
|
4086
|
+
correctly with aligned columns. flex:1 lets it absorb the card's spare height so the actions
|
|
4087
|
+
stay pinned at the bottom on a short viewport. */
|
|
4088
|
+
/* The slot that replaces the textarea when a caller supplies `content`. Duplicated in
|
|
4089
|
+
popup.css with the rest of the modal — see prompt_modal.ts. */
|
|
4090
|
+
.pr-helper-prompt-modal-content {
|
|
4091
|
+
display: flex;
|
|
4092
|
+
flex-direction: column;
|
|
4093
|
+
min-height: 0;
|
|
4094
|
+
flex: 1;
|
|
4095
|
+
margin: 8px 0 12px;
|
|
4096
|
+
}
|
|
4097
|
+
|
|
4098
|
+
.pr-helper-prompt-modal-field {
|
|
4099
|
+
flex: 1;
|
|
4100
|
+
min-height: 220px;
|
|
4101
|
+
margin: 8px 0 16px;
|
|
4102
|
+
padding: 12px;
|
|
4103
|
+
border: 1px solid rgba(0, 0, 0, 0.15);
|
|
4104
|
+
border-radius: 8px;
|
|
4105
|
+
background: #f6f8fa;
|
|
4106
|
+
color: #1f2328;
|
|
4107
|
+
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
|
|
4108
|
+
font-size: 12px;
|
|
4109
|
+
line-height: 1.5;
|
|
4110
|
+
resize: none;
|
|
4111
|
+
white-space: pre;
|
|
4112
|
+
overflow: auto;
|
|
4113
|
+
}
|
|
4114
|
+
|
|
4115
|
+
.pr-helper-prompt-modal-actions {
|
|
4116
|
+
display: flex;
|
|
4117
|
+
justify-content: flex-end;
|
|
4118
|
+
gap: 8px;
|
|
4119
|
+
}
|
|
4120
|
+
|
|
4121
|
+
.pr-helper-prompt-modal-btn {
|
|
4122
|
+
padding: 8px 16px;
|
|
4123
|
+
border-radius: 6px;
|
|
4124
|
+
border: 1px solid rgba(0, 0, 0, 0.15);
|
|
4125
|
+
background: #ffffff;
|
|
4126
|
+
color: #1f2328;
|
|
4127
|
+
font-size: 13px;
|
|
4128
|
+
font-weight: 600;
|
|
4129
|
+
font-family: inherit;
|
|
4130
|
+
cursor: pointer;
|
|
4131
|
+
}
|
|
4132
|
+
|
|
4133
|
+
.pr-helper-prompt-modal-btn:hover { border-color: rgba(0, 0, 0, 0.3); }
|
|
4134
|
+
|
|
4135
|
+
.pr-helper-prompt-modal-btn-primary {
|
|
4136
|
+
background: #9333ea;
|
|
4137
|
+
border-color: #9333ea;
|
|
4138
|
+
color: #ffffff;
|
|
4139
|
+
}
|
|
4140
|
+
|
|
4141
|
+
.pr-helper-prompt-modal-btn-primary:hover {
|
|
4142
|
+
background: #7c3aed;
|
|
4143
|
+
border-color: #7c3aed;
|
|
4144
|
+
}
|
|
4145
|
+
|
|
4146
|
+
.pr-helper-prompt-modal-btn-done {
|
|
4147
|
+
background: #1a7f37;
|
|
4148
|
+
border-color: #1a7f37;
|
|
4149
|
+
}
|
|
4150
|
+
|
|
4151
|
+
.pr-helper-prompt-modal-btn-done:hover {
|
|
4152
|
+
background: #1a7f37;
|
|
4153
|
+
border-color: #1a7f37;
|
|
4154
|
+
}
|
|
4155
|
+
|
|
4156
|
+
/* ── PR update / analysis staleness ─────────────────────────────────────── */
|
|
4157
|
+
|
|
4158
|
+
/*
|
|
4159
|
+
* (The per-tab inset workaround that used to live here is gone: the staleness block moved out of
|
|
4160
|
+
* the review panel into its own toolbar, so it no longer inherits whatever padding and flex
|
|
4161
|
+
* behaviour a given tab happens to impose on the panel.)
|
|
4162
|
+
*/
|
|
4163
|
+
|
|
4164
|
+
/*
|
|
4165
|
+
* The provenance line is not dismissible and is rendered on every tab that shows the current
|
|
4166
|
+
* analysis. It is deliberately quiet when the analysis is current and picks up colour only when
|
|
4167
|
+
* the pull request has moved past it — see ui/components/pr_update_notice.ts.
|
|
4168
|
+
*/
|
|
4169
|
+
.pr-helper-analysis-provenance {
|
|
4170
|
+
display: flex;
|
|
4171
|
+
align-items: center;
|
|
4172
|
+
gap: 6px;
|
|
4173
|
+
flex-wrap: wrap;
|
|
4174
|
+
margin: 0 0 10px;
|
|
4175
|
+
padding: 0 2px;
|
|
4176
|
+
/* Body size and the UI font: this is product copy about the analysis, not a debug string. */
|
|
4177
|
+
font-size: 13px;
|
|
4178
|
+
color: var(--color-fg-muted, #57606a);
|
|
4179
|
+
}
|
|
4180
|
+
|
|
4181
|
+
.pr-helper-provenance-label {
|
|
4182
|
+
font-weight: 500;
|
|
4183
|
+
}
|
|
4184
|
+
|
|
4185
|
+
/*
|
|
4186
|
+
* The identifier, and only the identifier, stays monospace — a commit SHA is a technical value and
|
|
4187
|
+
* a proportional font makes it harder to compare against one on GitHub's own page. The pill is what
|
|
4188
|
+
* makes that legible as a *reference* rather than as leaked internals: smaller and lighter than the
|
|
4189
|
+
* phrase beside it, so the row reads "Analysis at «commit»" instead of one undifferentiated string.
|
|
4190
|
+
*/
|
|
4191
|
+
.pr-helper-provenance-sha {
|
|
4192
|
+
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
|
|
4193
|
+
font-size: 11px;
|
|
4194
|
+
padding: 1px 6px;
|
|
4195
|
+
border-radius: 4px;
|
|
4196
|
+
color: var(--color-fg-muted, #57606a);
|
|
4197
|
+
background: var(--color-canvas-subtle, #f6f8fa);
|
|
4198
|
+
border: 1px solid var(--color-border-muted, #d8dee4);
|
|
4199
|
+
}
|
|
4200
|
+
|
|
4201
|
+
/*
|
|
4202
|
+
* Purple, and deliberately not blue — that is the whole point of the colour choice.
|
|
4203
|
+
*
|
|
4204
|
+
* Three actions can appear in this region and they need to rank unambiguously: this one is
|
|
4205
|
+
* proactive and always available, "Analyse new changes" is the real work and only exists when
|
|
4206
|
+
* there is something to analyse, and "Not now" declines. Making this blue would put it in a dead
|
|
4207
|
+
* heat with the primary CTA, and its previous neutral-grey-with-a-blue-hover did both wrong at
|
|
4208
|
+
* once — passive at rest, then competing on hover.
|
|
4209
|
+
*
|
|
4210
|
+
* Purple is the product's own accent, so a tint of it reads as "ours, secondary" without borrowing
|
|
4211
|
+
* the primary action's colour. Every value here already exists in this file: the .06 fill is the
|
|
4212
|
+
* Suggested Review Path card's, .12 is the header chip's hover, .25 is the resize handle's, and
|
|
4213
|
+
* #9333ea is the accent itself.
|
|
4214
|
+
*/
|
|
4215
|
+
.pr-helper-provenance-check {
|
|
4216
|
+
margin-left: auto;
|
|
4217
|
+
padding: 1px 8px;
|
|
4218
|
+
font-size: 11px;
|
|
4219
|
+
font-family: inherit;
|
|
4220
|
+
font-weight: 500;
|
|
4221
|
+
color: #9333ea;
|
|
4222
|
+
background: rgba(147, 51, 234, 0.06);
|
|
4223
|
+
border: 1px solid rgba(147, 51, 234, 0.25);
|
|
4224
|
+
border-radius: 999px;
|
|
4225
|
+
cursor: pointer;
|
|
4226
|
+
white-space: nowrap;
|
|
4227
|
+
}
|
|
4228
|
+
|
|
4229
|
+
.pr-helper-provenance-check:hover:not(:disabled) {
|
|
4230
|
+
background: rgba(147, 51, 234, 0.12);
|
|
4231
|
+
border-color: #9333ea;
|
|
4232
|
+
}
|
|
4233
|
+
|
|
4234
|
+
.pr-helper-provenance-check:disabled {
|
|
4235
|
+
cursor: default;
|
|
4236
|
+
opacity: 0.65;
|
|
4237
|
+
}
|
|
4238
|
+
|
|
4239
|
+
.pr-helper-provenance-stale {
|
|
4240
|
+
display: inline-flex;
|
|
4241
|
+
align-items: center;
|
|
4242
|
+
padding: 1px 6px;
|
|
4243
|
+
border-radius: 999px;
|
|
4244
|
+
font-weight: 600;
|
|
4245
|
+
color: var(--color-attention-fg, #9a6700);
|
|
4246
|
+
background: var(--color-attention-subtle, #fff8c5);
|
|
4247
|
+
}
|
|
4248
|
+
|
|
4249
|
+
/*
|
|
4250
|
+
* Amber, but as an accent rather than as the whole component.
|
|
4251
|
+
*
|
|
4252
|
+
* This used GitHub's `--color-attention-subtle`, which is a *solid* pale yellow (#fff8c5) — next
|
|
4253
|
+
* to the Suggested Review Path card's 6% purple tint it read as a warning rather than as one more
|
|
4254
|
+
* surface in the sidebar's palette, and it competed with the amber pill directly above it saying
|
|
4255
|
+
* the same thing. Both were shouting the same message.
|
|
4256
|
+
*
|
|
4257
|
+
* So it now follows the idiom the rest of this file already uses for soft surfaces: the hue at a
|
|
4258
|
+
* low alpha over the canvas, matching `.pr-helper-suggested-path` (rgba(147,51,234,.06) / .2).
|
|
4259
|
+
* The triplet is the same amber as the medium-risk pill, so this state and the risk indicator are
|
|
4260
|
+
* visibly the same colour rather than two hand-picked yellows.
|
|
4261
|
+
*
|
|
4262
|
+
* Alpha sits slightly above the purple card's because yellow carries far less contrast against
|
|
4263
|
+
* white at equal alpha — 6% amber is effectively invisible. This is the point where it still reads
|
|
4264
|
+
* as deliberately tinted without becoming the loudest thing on screen.
|
|
4265
|
+
*/
|
|
4266
|
+
.pr-helper-update-notice {
|
|
4267
|
+
display: flex;
|
|
4268
|
+
align-items: center;
|
|
4269
|
+
justify-content: space-between;
|
|
4270
|
+
gap: 12px;
|
|
4271
|
+
flex-wrap: wrap;
|
|
4272
|
+
margin: 0 0 12px;
|
|
4273
|
+
padding: 10px 12px;
|
|
4274
|
+
border: 1px solid rgba(240, 178, 50, 0.32);
|
|
4275
|
+
border-radius: 8px;
|
|
4276
|
+
background: rgba(240, 178, 50, 0.1);
|
|
4277
|
+
}
|
|
4278
|
+
|
|
4279
|
+
.pr-helper-update-notice-failed {
|
|
4280
|
+
border-color: var(--color-border-default, #d0d7de);
|
|
4281
|
+
background: var(--color-canvas-subtle, #f6f8fa);
|
|
4282
|
+
}
|
|
4283
|
+
|
|
4284
|
+
.pr-helper-update-notice-running {
|
|
4285
|
+
border-color: var(--color-border-default, #d0d7de);
|
|
4286
|
+
background: var(--color-canvas-subtle, #f6f8fa);
|
|
4287
|
+
}
|
|
4288
|
+
|
|
4289
|
+
.pr-helper-update-notice-body {
|
|
4290
|
+
display: flex;
|
|
4291
|
+
flex-direction: column;
|
|
4292
|
+
gap: 2px;
|
|
4293
|
+
min-width: 0;
|
|
4294
|
+
}
|
|
4295
|
+
|
|
4296
|
+
.pr-helper-update-notice-title {
|
|
4297
|
+
font-size: 12px;
|
|
4298
|
+
font-weight: 600;
|
|
4299
|
+
color: var(--color-fg-default, #1f2328);
|
|
4300
|
+
}
|
|
4301
|
+
|
|
4302
|
+
.pr-helper-update-notice-detail {
|
|
4303
|
+
font-size: 11px;
|
|
4304
|
+
color: var(--color-fg-muted, #57606a);
|
|
4305
|
+
}
|
|
4306
|
+
|
|
4307
|
+
.pr-helper-update-notice-actions {
|
|
4308
|
+
display: flex;
|
|
4309
|
+
align-items: center;
|
|
4310
|
+
gap: 6px;
|
|
4311
|
+
}
|
|
4312
|
+
|
|
4313
|
+
.pr-helper-update-notice-action {
|
|
4314
|
+
padding: 4px 10px;
|
|
4315
|
+
font-size: 11px;
|
|
4316
|
+
font-family: inherit;
|
|
4317
|
+
font-weight: 600;
|
|
4318
|
+
color: #ffffff;
|
|
4319
|
+
background: var(--color-accent-fg, #0969da);
|
|
4320
|
+
border: 1px solid var(--color-accent-fg, #0969da);
|
|
4321
|
+
border-radius: 6px;
|
|
4322
|
+
cursor: pointer;
|
|
4323
|
+
white-space: nowrap;
|
|
4324
|
+
}
|
|
4325
|
+
|
|
4326
|
+
.pr-helper-update-notice-action:hover {
|
|
4327
|
+
filter: brightness(0.94);
|
|
4328
|
+
}
|
|
4329
|
+
|
|
4330
|
+
.pr-helper-update-notice-dismiss,
|
|
4331
|
+
.pr-helper-update-notice-cancel {
|
|
4332
|
+
padding: 4px 10px;
|
|
4333
|
+
font-size: 11px;
|
|
4334
|
+
font-family: inherit;
|
|
4335
|
+
color: var(--color-fg-muted, #57606a);
|
|
4336
|
+
background: transparent;
|
|
4337
|
+
border: 1px solid var(--color-border-default, #d0d7de);
|
|
4338
|
+
border-radius: 6px;
|
|
4339
|
+
cursor: pointer;
|
|
4340
|
+
white-space: nowrap;
|
|
4341
|
+
}
|
|
4342
|
+
|
|
4343
|
+
.pr-helper-update-notice-dismiss:hover,
|
|
4344
|
+
.pr-helper-update-notice-cancel:hover {
|
|
4345
|
+
background: var(--color-canvas-default, #ffffff);
|
|
4346
|
+
color: var(--color-fg-default, #1f2328);
|
|
4347
|
+
}
|
|
4348
|
+
|
|
4349
|
+
/* ── Reconciled finding statuses ────────────────────────────────────────── */
|
|
4350
|
+
|
|
4351
|
+
.pr-helper-finding-status {
|
|
4352
|
+
font-size: 11px;
|
|
4353
|
+
font-weight: 700;
|
|
4354
|
+
text-transform: uppercase;
|
|
4355
|
+
letter-spacing: 0.02em;
|
|
4356
|
+
padding: 1px 6px;
|
|
4357
|
+
border-radius: 999px;
|
|
4358
|
+
white-space: nowrap;
|
|
4359
|
+
}
|
|
4360
|
+
|
|
4361
|
+
.pr-helper-finding-status-resolved {
|
|
4362
|
+
color: var(--color-success-fg, #1a7f37);
|
|
4363
|
+
background: var(--color-success-subtle, #dafbe1);
|
|
4364
|
+
}
|
|
4365
|
+
|
|
4366
|
+
.pr-helper-finding-status-still-open {
|
|
4367
|
+
color: var(--color-danger-fg, #cf222e);
|
|
4368
|
+
background: var(--color-danger-subtle, #ffebe9);
|
|
4369
|
+
}
|
|
4370
|
+
|
|
4371
|
+
.pr-helper-finding-status-affected {
|
|
4372
|
+
color: var(--color-attention-fg, #9a6700);
|
|
4373
|
+
background: var(--color-attention-subtle, #fff8c5);
|
|
4374
|
+
}
|
|
4375
|
+
|
|
4376
|
+
.pr-helper-finding-status-new {
|
|
4377
|
+
color: var(--color-accent-fg, #0969da);
|
|
4378
|
+
background: var(--color-accent-subtle, #ddf4ff);
|
|
4379
|
+
}
|
|
4380
|
+
|
|
4381
|
+
/*
|
|
4382
|
+
* A resolved finding is kept on screen — the reviewer needs to see that it was dealt with — but
|
|
4383
|
+
* muted, so the findings still needing attention are what the eye lands on first.
|
|
4384
|
+
*/
|
|
4385
|
+
.pr-helper-issue-resolved {
|
|
4386
|
+
opacity: 0.72;
|
|
4387
|
+
}
|
|
4388
|
+
|
|
4389
|
+
.pr-helper-reconciliation-note {
|
|
4390
|
+
margin: 6px 0 0;
|
|
4391
|
+
padding-left: 8px;
|
|
4392
|
+
border-left: 2px solid var(--color-border-muted, #d8dee4);
|
|
4393
|
+
font-size: 11px;
|
|
4394
|
+
font-style: italic;
|
|
4395
|
+
color: var(--color-fg-muted, #57606a);
|
|
4396
|
+
}
|
|
4397
|
+
|
|
4398
|
+
/* ── History: analyses that follow an earlier one ───────────────────────── */
|
|
4399
|
+
|
|
4400
|
+
/*
|
|
4401
|
+
* An update carries no indent or edge marker of its own.
|
|
4402
|
+
*
|
|
4403
|
+
* It had both, and they cost more than they bought: the indent narrowed the card so it no longer
|
|
4404
|
+
* lined up with the analyses above and below it, and the coloured left edge replaced the card's own
|
|
4405
|
+
* border on one side so the outline changed weight halfway round. Both were saying what the
|
|
4406
|
+
* "New changes …" row below already says in words, which is the same double-signalling the update
|
|
4407
|
+
* banner and the staleness pill were guilty of. Stated once, in the row, is enough.
|
|
4408
|
+
*/
|
|
4409
|
+
.pr-helper-history-delta {
|
|
4410
|
+
display: flex;
|
|
4411
|
+
align-items: center;
|
|
4412
|
+
gap: 6px;
|
|
4413
|
+
flex-wrap: wrap;
|
|
4414
|
+
margin-top: 6px;
|
|
4415
|
+
font-size: 10px;
|
|
4416
|
+
color: var(--color-fg-muted, #57606a);
|
|
4417
|
+
}
|
|
4418
|
+
|
|
4419
|
+
.pr-helper-history-delta-label {
|
|
4420
|
+
font-weight: 600;
|
|
4421
|
+
text-transform: uppercase;
|
|
4422
|
+
letter-spacing: 0.03em;
|
|
4423
|
+
}
|
|
4424
|
+
|
|
4425
|
+
.pr-helper-history-delta-range {
|
|
4426
|
+
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
|
|
4427
|
+
}
|
|
4428
|
+
|
|
4429
|
+
.pr-helper-history-delta-partial {
|
|
4430
|
+
padding: 0 5px;
|
|
4431
|
+
border-radius: 999px;
|
|
4432
|
+
font-weight: 600;
|
|
4433
|
+
color: var(--color-attention-fg, #9a6700);
|
|
4434
|
+
background: var(--color-attention-subtle, #fff8c5);
|
|
4435
|
+
}
|
|
4436
|
+
|
|
4437
|
+
/* ── Repository investigation: the inbound half ─────────────────────────── */
|
|
4438
|
+
|
|
4439
|
+
/*
|
|
4440
|
+
* The paired actions. "Investigate Repository" and "Add Investigation" are the two halves of one
|
|
4441
|
+
* loop and sit together on the finding they concern, so they share .pr-helper-investigate-btn's
|
|
4442
|
+
* geometry exactly — a second control with its own weight would read as an unrelated action that
|
|
4443
|
+
* happened to land nearby.
|
|
4444
|
+
*
|
|
4445
|
+
* The add half is tinted rather than filled. It is the step the reviewer is being invited towards
|
|
4446
|
+
* once a prompt exists, which deserves to be findable; a solid button would outrank the finding's
|
|
4447
|
+
* own text, and the card is a finding first and a control surface second.
|
|
4448
|
+
*/
|
|
4449
|
+
.pr-helper-investigate-btn-add {
|
|
4450
|
+
border-color: rgba(147, 51, 234, 0.4);
|
|
4451
|
+
color: #7e22ce;
|
|
4452
|
+
background: rgba(147, 51, 234, 0.06);
|
|
4453
|
+
}
|
|
4454
|
+
|
|
4455
|
+
.pr-helper-investigate-btn-add:hover {
|
|
4456
|
+
border-color: #9333ea;
|
|
4457
|
+
background: rgba(147, 51, 234, 0.12);
|
|
4458
|
+
color: #7e22ce;
|
|
4459
|
+
}
|
|
4460
|
+
|
|
4461
|
+
/* Reports rather than invites — see renderInvestigationActions. Deliberately not a disabled
|
|
4462
|
+
<button>: it is not an action the reviewer is being denied, it is a statement of fact. */
|
|
4463
|
+
.pr-helper-investigate-added {
|
|
4464
|
+
display: inline-flex;
|
|
4465
|
+
align-items: center;
|
|
4466
|
+
padding: 5px 10px;
|
|
4467
|
+
font-size: 12px;
|
|
4468
|
+
font-weight: 600;
|
|
4469
|
+
color: var(--color-success-fg, #1a7f37);
|
|
4470
|
+
white-space: nowrap;
|
|
4471
|
+
}
|
|
4472
|
+
|
|
4473
|
+
/* Same pill geometry as .pr-helper-finding-status — the two axes sit side by side in one badge row
|
|
4474
|
+
and must read as peers, since a finding can legitimately carry both. */
|
|
4475
|
+
.pr-helper-investigation-impact {
|
|
4476
|
+
font-size: 11px;
|
|
4477
|
+
font-weight: 700;
|
|
4478
|
+
text-transform: uppercase;
|
|
4479
|
+
letter-spacing: 0.02em;
|
|
4480
|
+
padding: 1px 6px;
|
|
4481
|
+
border-radius: 999px;
|
|
4482
|
+
white-space: nowrap;
|
|
4483
|
+
}
|
|
4484
|
+
|
|
4485
|
+
/* Purple throughout, matching the Repository AI accent used on the investigate buttons: these
|
|
4486
|
+
verdicts all came from outside the diff, and the colour is what says so at a glance. Weight
|
|
4487
|
+
within the family tracks how much the evidence moved the finding. */
|
|
4488
|
+
.pr-helper-investigation-impact-confirmed {
|
|
4489
|
+
color: #6b21a8;
|
|
4490
|
+
background: rgba(147, 51, 234, 0.14);
|
|
4491
|
+
}
|
|
4492
|
+
|
|
4493
|
+
/* Not struck through: the pill is the verdict, not the thing that was disproven. Striking a status
|
|
4494
|
+
label reads as the status having been withdrawn. The strike goes on the claim instead — see
|
|
4495
|
+
.pr-helper-issue-investigation-refuted below, and the Files tab, which already does this. */
|
|
4496
|
+
.pr-helper-investigation-impact-refuted {
|
|
4497
|
+
color: var(--color-fg-muted, #57606a);
|
|
4498
|
+
background: var(--color-neutral-muted, rgba(175, 184, 193, 0.2));
|
|
4499
|
+
}
|
|
4500
|
+
|
|
4501
|
+
.pr-helper-investigation-impact-revised {
|
|
4502
|
+
color: #7e22ce;
|
|
4503
|
+
background: rgba(147, 51, 234, 0.1);
|
|
4504
|
+
}
|
|
4505
|
+
|
|
4506
|
+
.pr-helper-investigation-impact-unchanged {
|
|
4507
|
+
color: var(--color-fg-muted, #57606a);
|
|
4508
|
+
background: var(--color-neutral-muted, rgba(175, 184, 193, 0.2));
|
|
4509
|
+
}
|
|
4510
|
+
|
|
4511
|
+
/*
|
|
4512
|
+
* A refuted finding stays on the list and is muted, exactly as a resolved one is: the reviewer
|
|
4513
|
+
* needs to see that the concern was investigated and does not hold, and deleting it would destroy
|
|
4514
|
+
* the audit trail this whole loop exists to build.
|
|
4515
|
+
*
|
|
4516
|
+
* Only the claim is struck — the issue title, or the question that was asked. Everything below it
|
|
4517
|
+
* is why we now know better: the answer from the investigation, the note, the provenance. That is
|
|
4518
|
+
* evidence the reviewer escalated for, and it is not retracted by the thing it disproved. Dimming
|
|
4519
|
+
* the whole card would bury the payoff of the loop along with the claim.
|
|
4520
|
+
*/
|
|
4521
|
+
.pr-helper-issue-investigation-refuted .pr-helper-issue-title {
|
|
4522
|
+
text-decoration: line-through;
|
|
4523
|
+
color: var(--color-fg-muted, #57606a);
|
|
4524
|
+
}
|
|
4525
|
+
|
|
4526
|
+
/* `major → minor`. Monospace so the two grades line up as values being compared rather than as
|
|
4527
|
+
prose, and the old one struck through so which is current needs no explanation. */
|
|
4528
|
+
.pr-helper-investigation-revision {
|
|
4529
|
+
margin: 4px 0 0;
|
|
4530
|
+
font-size: 11px;
|
|
4531
|
+
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
|
|
4532
|
+
color: var(--color-fg-muted, #57606a);
|
|
4533
|
+
}
|
|
4534
|
+
|
|
4535
|
+
.pr-helper-investigation-revision-from {
|
|
4536
|
+
text-decoration: line-through;
|
|
4537
|
+
}
|
|
4538
|
+
|
|
4539
|
+
.pr-helper-investigation-revision-to {
|
|
4540
|
+
font-weight: 700;
|
|
4541
|
+
color: #7e22ce;
|
|
4542
|
+
}
|
|
4543
|
+
|
|
4544
|
+
/* Matches .pr-helper-reconciliation-note, with a purple rule instead of grey — same kind of thing
|
|
4545
|
+
(a one-line reason from the model), different provenance. */
|
|
4546
|
+
.pr-helper-investigation-note {
|
|
4547
|
+
margin: 6px 0 0;
|
|
4548
|
+
padding-left: 8px;
|
|
4549
|
+
border-left: 2px solid rgba(147, 51, 234, 0.35);
|
|
4550
|
+
font-size: 11px;
|
|
4551
|
+
font-style: italic;
|
|
4552
|
+
color: var(--color-fg-muted, #57606a);
|
|
4553
|
+
}
|
|
4554
|
+
|
|
4555
|
+
/* The answer to an open question is not a footnote — it is what the reviewer escalated for. Set at
|
|
4556
|
+
body weight with a solid rule, unlike the italic muted note above. */
|
|
4557
|
+
.pr-helper-investigation-answer {
|
|
4558
|
+
padding-left: 8px;
|
|
4559
|
+
border-left: 2px solid #9333ea;
|
|
4560
|
+
}
|
|
4561
|
+
|
|
4562
|
+
.pr-helper-investigation-provenance {
|
|
4563
|
+
display: flex;
|
|
4564
|
+
align-items: center;
|
|
4565
|
+
gap: 6px;
|
|
4566
|
+
flex-wrap: wrap;
|
|
4567
|
+
margin: 8px 0 0;
|
|
4568
|
+
font-size: 10px;
|
|
4569
|
+
color: var(--color-fg-muted, #57606a);
|
|
4570
|
+
}
|
|
4571
|
+
|
|
4572
|
+
.pr-helper-investigation-provenance-label {
|
|
4573
|
+
font-weight: 600;
|
|
4574
|
+
text-transform: uppercase;
|
|
4575
|
+
letter-spacing: 0.03em;
|
|
4576
|
+
}
|
|
4577
|
+
|
|
4578
|
+
.pr-helper-investigation-provenance-agent {
|
|
4579
|
+
font-weight: 600;
|
|
4580
|
+
color: #7e22ce;
|
|
4581
|
+
}
|
|
4582
|
+
|
|
4583
|
+
.pr-helper-investigation-view {
|
|
4584
|
+
padding: 0;
|
|
4585
|
+
border: 0;
|
|
4586
|
+
background: none;
|
|
4587
|
+
font-family: inherit;
|
|
4588
|
+
font-size: 10px;
|
|
4589
|
+
color: var(--color-accent-fg, #0969da);
|
|
4590
|
+
cursor: pointer;
|
|
4591
|
+
text-decoration: underline;
|
|
4592
|
+
}
|
|
4593
|
+
|
|
4594
|
+
/* History row. Same geometry as .pr-helper-history-delta, and for the same reason it carries no
|
|
4595
|
+
indent or edge marker: the row says in words what kind of analysis this is. */
|
|
4596
|
+
.pr-helper-history-investigation {
|
|
4597
|
+
display: flex;
|
|
4598
|
+
align-items: center;
|
|
4599
|
+
gap: 6px;
|
|
4600
|
+
flex-wrap: wrap;
|
|
4601
|
+
margin-top: 6px;
|
|
4602
|
+
font-size: 10px;
|
|
4603
|
+
color: var(--color-fg-muted, #57606a);
|
|
4604
|
+
}
|
|
4605
|
+
|
|
4606
|
+
.pr-helper-history-investigation-label {
|
|
4607
|
+
font-weight: 600;
|
|
4608
|
+
text-transform: uppercase;
|
|
4609
|
+
letter-spacing: 0.03em;
|
|
4610
|
+
}
|
|
4611
|
+
|
|
4612
|
+
.pr-helper-history-investigation-agent {
|
|
4613
|
+
font-weight: 600;
|
|
4614
|
+
color: #7e22ce;
|
|
4615
|
+
}
|
|
4616
|
+
|
|
4617
|
+
/*
|
|
4618
|
+
* The Add Investigation modal's content slot.
|
|
4619
|
+
*
|
|
4620
|
+
* Sidebar-only, unlike the .pr-helper-prompt-modal shell above which is duplicated into popup.css.
|
|
4621
|
+
* The shell is shared because both surfaces open modals; this content is not, because findings only
|
|
4622
|
+
* exist in the sidebar and the popup has no way to reach it. Same rule the context picker follows
|
|
4623
|
+
* in reverse — its styles live only in popup.css, where that surface is reachable.
|
|
4624
|
+
*/
|
|
4625
|
+
.pr-helper-add-investigation {
|
|
4626
|
+
display: flex;
|
|
4627
|
+
flex-direction: column;
|
|
4628
|
+
gap: 6px;
|
|
4629
|
+
min-height: 0;
|
|
4630
|
+
}
|
|
4631
|
+
|
|
4632
|
+
.pr-helper-add-investigation-label {
|
|
4633
|
+
font-size: 11px;
|
|
4634
|
+
font-weight: 600;
|
|
4635
|
+
text-transform: uppercase;
|
|
4636
|
+
letter-spacing: 0.03em;
|
|
4637
|
+
color: var(--color-fg-muted, #57606a);
|
|
4638
|
+
}
|
|
4639
|
+
|
|
4640
|
+
.pr-helper-add-investigation-select {
|
|
4641
|
+
padding: 6px 8px;
|
|
4642
|
+
border: 1px solid var(--color-border-default, rgba(0, 0, 0, 0.15));
|
|
4643
|
+
border-radius: 6px;
|
|
4644
|
+
background: var(--color-canvas-default, #ffffff);
|
|
4645
|
+
color: var(--color-fg-default, #1f2328);
|
|
4646
|
+
font-family: inherit;
|
|
4647
|
+
font-size: 13px;
|
|
4648
|
+
}
|
|
4649
|
+
|
|
4650
|
+
/* Monospace and generous, because an agent's report is structured text with file paths and line
|
|
4651
|
+
references in it — proportional type turns those into something the reviewer has to decode. */
|
|
4652
|
+
.pr-helper-add-investigation-field {
|
|
4653
|
+
flex: 1;
|
|
4654
|
+
min-height: 220px;
|
|
4655
|
+
padding: 10px;
|
|
4656
|
+
border: 1px solid var(--color-border-default, rgba(0, 0, 0, 0.15));
|
|
4657
|
+
border-radius: 6px;
|
|
4658
|
+
background: var(--color-canvas-default, #ffffff);
|
|
4659
|
+
color: var(--color-fg-default, #1f2328);
|
|
4660
|
+
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
|
|
4661
|
+
font-size: 12px;
|
|
4662
|
+
line-height: 1.5;
|
|
4663
|
+
resize: vertical;
|
|
4664
|
+
}
|
|
4665
|
+
|
|
4666
|
+
.pr-helper-add-investigation-error {
|
|
4667
|
+
margin: 0;
|
|
4668
|
+
font-size: 12px;
|
|
4669
|
+
color: var(--color-danger-fg, #cf222e);
|
|
4670
|
+
}
|
|
4671
|
+
|
|
4672
|
+
/* The reminder of which finding sent them out, so the reviewer can tell at a glance that they are
|
|
4673
|
+
pasting into the right place — the one thing a paste box cannot otherwise confirm. */
|
|
4674
|
+
.pr-helper-add-investigation-origin {
|
|
4675
|
+
margin: 0;
|
|
4676
|
+
font-size: 11px;
|
|
4677
|
+
color: var(--color-fg-muted, #57606a);
|
|
4678
|
+
}
|
|
4679
|
+
|
|
4680
|
+
.pr-helper-add-investigation-viewprompt {
|
|
4681
|
+
padding: 0;
|
|
4682
|
+
border: 0;
|
|
4683
|
+
background: none;
|
|
4684
|
+
font-family: inherit;
|
|
4685
|
+
font-size: 11px;
|
|
4686
|
+
color: var(--color-accent-fg, #0969da);
|
|
4687
|
+
cursor: pointer;
|
|
4688
|
+
text-decoration: underline;
|
|
4689
|
+
}
|
|
4690
|
+
|
|
4691
|
+
/*
|
|
4692
|
+
* The completed-investigation report viewer (investigation_output_modal.ts).
|
|
4693
|
+
*
|
|
4694
|
+
* Sidebar-only for the same reason the Add Investigation slot above is: it is reached from a
|
|
4695
|
+
* finding's provenance line, and findings do not exist in the popup.
|
|
4696
|
+
*
|
|
4697
|
+
* The card is 680px and the content is a document, so this is the one prompt-modal surface that
|
|
4698
|
+
* sets prose rather than a field: proportional type, left-aligned, scrolling inside the card so the
|
|
4699
|
+
* action row stays pinned. The report is a scroll container in its own right rather than letting
|
|
4700
|
+
* the slot scroll, so the header stays with the text it describes.
|
|
4701
|
+
*/
|
|
4702
|
+
.pr-helper-investigation-report {
|
|
4703
|
+
/* Overrides the slot's `display:flex`. Every other content slot lays out controls, so a flex
|
|
4704
|
+
column is right for them; this one is a document, and as flex items its paragraphs would
|
|
4705
|
+
shrink to fit the constrained height and clip rather than scroll. */
|
|
4706
|
+
display: block;
|
|
4707
|
+
min-height: 0;
|
|
4708
|
+
max-height: 60vh;
|
|
4709
|
+
overflow-y: auto;
|
|
4710
|
+
padding-right: 4px;
|
|
4711
|
+
font-size: 13px;
|
|
4712
|
+
line-height: 1.55;
|
|
4713
|
+
color: var(--color-fg-default, #1f2328);
|
|
4714
|
+
}
|
|
4715
|
+
|
|
4716
|
+
/* Focus is visible because the report is keyboard-scrollable (tabIndex 0 in the module) — an
|
|
4717
|
+
outline is the only thing telling a keyboard user the arrow keys now move this panel. */
|
|
4718
|
+
.pr-helper-investigation-report:focus-visible {
|
|
4719
|
+
outline: 2px solid var(--color-accent-fg, #0969da);
|
|
4720
|
+
outline-offset: 2px;
|
|
4721
|
+
border-radius: 4px;
|
|
4722
|
+
}
|
|
4723
|
+
|
|
4724
|
+
.pr-helper-investigation-report-meta {
|
|
4725
|
+
display: flex;
|
|
4726
|
+
align-items: baseline;
|
|
4727
|
+
gap: 8px;
|
|
4728
|
+
flex-wrap: wrap;
|
|
4729
|
+
margin: 0;
|
|
4730
|
+
font-size: 11px;
|
|
4731
|
+
color: var(--color-fg-muted, #57606a);
|
|
4732
|
+
}
|
|
4733
|
+
|
|
4734
|
+
/* Same purple and weight as .pr-helper-investigation-provenance-agent, so the line the reviewer
|
|
4735
|
+
clicked and the line at the top of what opened read as one object rather than two labels. */
|
|
4736
|
+
.pr-helper-investigation-report-agent {
|
|
4737
|
+
font-weight: 600;
|
|
4738
|
+
color: #7e22ce;
|
|
4739
|
+
}
|
|
4740
|
+
|
|
4741
|
+
.pr-helper-investigation-report-origin {
|
|
4742
|
+
margin: 4px 0 0;
|
|
4743
|
+
font-size: 11px;
|
|
4744
|
+
color: var(--color-fg-muted, #57606a);
|
|
4745
|
+
}
|
|
4746
|
+
|
|
4747
|
+
.pr-helper-investigation-report-rule {
|
|
4748
|
+
margin: 12px 0;
|
|
4749
|
+
border: none;
|
|
4750
|
+
border-top: 1px solid var(--color-border-default, #d0d7de);
|
|
4751
|
+
}
|
|
4752
|
+
|
|
4753
|
+
.pr-helper-investigation-report-empty {
|
|
4754
|
+
margin: 0;
|
|
4755
|
+
font-size: 13px;
|
|
4756
|
+
font-style: italic;
|
|
4757
|
+
color: var(--color-fg-muted, #57606a);
|
|
4758
|
+
}
|
|
4759
|
+
|
|
4760
|
+
/* Inline code chips, repeated here rather than reused.
|
|
4761
|
+
*
|
|
4762
|
+
* The chip rule the rest of the extension uses is scoped `.pr-helper-sidebar code`, and this modal
|
|
4763
|
+
* is mounted on document.body to escape the sidebar's stacking context — so it never matched, and
|
|
4764
|
+
* an agent's backticked file paths arrived as bare monospace with no chip. Identical declarations
|
|
4765
|
+
* on purpose: the two must look the same, since the finding's title appears in both places.
|
|
4766
|
+
*
|
|
4767
|
+
* `.pr-helper-markdown pre code` (one class, two types) outranks this, so code inside a fenced
|
|
4768
|
+
* block still gets its background and padding stripped rather than double-chipped. */
|
|
4769
|
+
.pr-helper-prompt-modal-report code {
|
|
4770
|
+
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
|
|
4771
|
+
font-size: 0.9em;
|
|
4772
|
+
background: var(--color-neutral-muted, rgba(175, 184, 193, 0.2));
|
|
4773
|
+
border-radius: 4px;
|
|
4774
|
+
padding: 0.1em 0.4em;
|
|
4775
|
+
/* Long paths must not widen the card. Breaking mid-token is right for a chip holding
|
|
4776
|
+
`ocean/deployment/templates/ocean-environment-variables-secret.yaml` and wrong for prose,
|
|
4777
|
+
which is why it sits on code rather than on the report. */
|
|
4778
|
+
overflow-wrap: anywhere;
|
|
4779
|
+
}
|
|
4780
|
+
|
|
4781
|
+
/* .pr-helper-markdown is sized for the 13px assistant bubble in a narrow sidebar (see its block
|
|
4782
|
+
above). Here it has the full card width, so headings get back the step the bubble gave up —
|
|
4783
|
+
an agent's numbered answers are the report's structure and need to read as headings. Nothing
|
|
4784
|
+
else is overridden: the colours, code blocks and lists were already right. */
|
|
4785
|
+
.pr-helper-prompt-modal-report .pr-helper-markdown h4 { font-size: 14px; }
|
|
4786
|
+
.pr-helper-prompt-modal-report .pr-helper-markdown h5 { font-size: 13px; }
|
|
4787
|
+
.pr-helper-prompt-modal-report .pr-helper-markdown h6 { font-size: 12.5px; }
|
|
4788
|
+
|
|
4789
|
+
/* Mirrors .pr-helper-investigate-added's geometry so the inbound slot keeps its width through the
|
|
4790
|
+
request → applying → added transitions, rather than the button row reflowing twice mid-run.
|
|
4791
|
+
That geometry is why this keeps its own type treatment rather than adopting the smaller, muted
|
|
4792
|
+
one "Analysing…" uses: the spinner is the part that had to match, and matching the font size too
|
|
4793
|
+
would shrink the slot and reintroduce the reflow this was built to avoid. */
|
|
4794
|
+
.pr-helper-investigate-applying {
|
|
4795
|
+
display: inline-flex;
|
|
4796
|
+
align-items: center;
|
|
4797
|
+
padding: 5px 10px;
|
|
4798
|
+
font-size: 12px;
|
|
4799
|
+
font-weight: 600;
|
|
4800
|
+
color: #7e22ce;
|
|
4801
|
+
white-space: nowrap;
|
|
4802
|
+
}
|