Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
Commit23b3ccaunknown_key

polish(compare): 2026 hero + branch selector + commit list + diff stats (parallel session 5.M)

polish(compare): 2026 hero + branch selector + commit list + diff stats (parallel session 5.M)

Branch comparison page rebuilt to the 2026 quality bar. Critical
onboarding surface — new users push code, hit "Compare & PR", land
here first. All visual rendering only; base/head selection, diff
fetching, commit enumeration, and the PR-creation redirect URL
(`/:owner/:repo/pulls/new?base=…&head=…`) are preserved exactly.

Changes to src/routes/compare.tsx GET /:owner/:repo/compare/:spec?:

  - Compare hero with gradient hairline strip on the top edge,
    eyebrow ("Compare changes · owner/repo"), context-aware display
    title ("Compare branches.", "N commits ahead.", "Nothing to
    compare.", or "No changes between branches."), and a sub-line
    that names the base/head refs in monospace.
  - Branch selector rebuilt as a pair of polished cards (.is-base
    with peach label, .is-head with violet label) with a prominent
    circular gradient arrow between them ("←"). Each card has a
    focus-within ring that adopts the accent purple.
  - Summary stats row: +N commits ahead, N files changed, and a
    +additions/-deletions/lines diff-stats pill — all using
    tabular-nums for clean alignment.
  - Commit list modernised — rows live inside a single elevated
    card with hover background, monospace SHA pill on the right
    (turns violet on hover), and a relative-time ("3h ago")
    helper next to the author.
  - "Create pull request" CTA: gradient primary button anchored
    to the right of the stats row, with a disabled-looking ghost
    variant in the empty state.
  - Empty / identical state: dashed card with a soft gradient orb
    icon, helpful copy that distinguishes "same ref" from
    "branches diverge but no diff", and a "Pick different
    branches" secondary action.
  - All styles scoped via .compare-* prefix in an inline <style>
    block. No shared view files touched.
  - Responsive: at <=720px branch cards stack and the arrow
    rotates 90deg so the visual flow stays intuitive.

Tests: 2011 pass / 0 fail. tsc --noEmit clean.

https://claude.ai/code/session_01KdpgsaGpmEExJAz3yhFLvx
Claude committed on May 19, 2026Parent: 662ce86
1 file changed+6185623b3cca00c08b2fa0ec33a2f2ec4d85ce5e3bc4c
1 changed file+618−56
Modifiedsrc/routes/compare.tsx+618−56View fileUnifiedSplit
11/**
22 * Compare view — diff between two branches or commits.
33 * URL: /:owner/:repo/compare/:base...:head
4 *
5 * The picker view and the comparison view both carry the 2026 polish:
6 * hero card with gradient hairline strip + display title, polished
7 * branch-selector cards with a prominent arrow between them, +/- diff
8 * stats with tabular numerals, modernized commit rows (SHA pill +
9 * monospace message + author + age), and a gradient "Create pull
10 * request" CTA. All styling is scoped via `.compare-*` class prefixes
11 * inside an inline <style> block so no other surface is touched.
12 *
13 * No business logic was changed in this polish pass — branch listing,
14 * diff fetching, commit enumeration, and the PR-creation redirect URL
15 * (`/:owner/:repo/pulls/new?base=…&head=…`) are preserved exactly.
416 */
517
618import { Hono } from "hono";
7import { join } from "path";
819import { Layout } from "../views/layout";
920import { RepoHeader, DiffView } from "../views/components";
1021import { IssueNav } from "./issues";
1122import {
1223 listBranches,
13 listCommits,
1424 repoExists,
1525 getRepoPath,
1626} from "../git/repository";
1727import { softAuth } from "../middleware/auth";
1828import type { AuthEnv } from "../middleware/auth";
1929import type { GitDiffFile } from "../git/repository";
20import {
21 EmptyState,
22 Flex,
23 Text,
24} from "../views/ui";
30import { EmptyState } from "../views/ui";
2531
2632const compare = new Hono<AuthEnv>();
2733
2834compare.use("*", softAuth);
2935
36/* ──────────────────────────────────────────────────────────────────────
37 * Inline CSS scoped via `.compare-*` so other surfaces remain untouched.
38 * Tokens come from layout.tsx `:root` for light/dark consistency.
39 * ──────────────────────────────────────────────────────────────────── */
40const COMPARE_STYLES = `
41 .compare-hero {
42 position: relative;
43 margin: 0 0 var(--space-5);
44 padding: 22px 26px 24px;
45 background: var(--bg-elevated);
46 border: 1px solid var(--border);
47 border-radius: 16px;
48 overflow: hidden;
49 }
50 .compare-hero::before {
51 content: '';
52 position: absolute; top: 0; left: 0; right: 0;
53 height: 2px;
54 background: linear-gradient(90deg, transparent 0%, #8c6dff 30%, #36c5d6 70%, transparent 100%);
55 opacity: 0.7;
56 pointer-events: none;
57 }
58 .compare-hero-inner { position: relative; z-index: 1; }
59 .compare-eyebrow {
60 font-size: 12px;
61 font-family: var(--font-mono);
62 color: var(--text-muted);
63 letter-spacing: 0.1em;
64 text-transform: uppercase;
65 font-weight: 600;
66 margin-bottom: 8px;
67 }
68 .compare-eyebrow strong { color: var(--accent); font-weight: 600; }
69 .compare-title {
70 font-family: var(--font-display);
71 font-size: clamp(26px, 3.4vw, 34px);
72 font-weight: 800;
73 letter-spacing: -0.025em;
74 line-height: 1.06;
75 margin: 0 0 8px;
76 color: var(--text-strong);
77 }
78 .compare-title .gradient-text {
79 background-image: linear-gradient(135deg, #a48bff 0%, #8c6dff 50%, #36c5d6 100%);
80 -webkit-background-clip: text;
81 background-clip: text;
82 -webkit-text-fill-color: transparent;
83 color: transparent;
84 }
85 .compare-sub {
86 font-size: 14.5px;
87 color: var(--text-muted);
88 line-height: 1.5;
89 margin: 0;
90 max-width: 640px;
91 }
92
93 /* Branch selector — base ← head */
94 .compare-branches {
95 display: flex;
96 align-items: stretch;
97 gap: 12px;
98 flex-wrap: wrap;
99 margin: 0 0 var(--space-4);
100 }
101 .compare-branch-card {
102 flex: 1 1 240px;
103 min-width: 220px;
104 display: flex;
105 flex-direction: column;
106 gap: 6px;
107 padding: 12px 14px;
108 background: var(--bg-elevated);
109 border: 1px solid var(--border);
110 border-radius: 12px;
111 transition: border-color 140ms ease, box-shadow 160ms ease;
112 }
113 .compare-branch-card:focus-within {
114 border-color: rgba(140,109,255,0.55);
115 box-shadow: 0 0 0 3px rgba(140,109,255,0.18);
116 }
117 .compare-branch-label {
118 display: flex;
119 align-items: center;
120 gap: 6px;
121 font-size: 11px;
122 font-family: var(--font-mono);
123 letter-spacing: 0.12em;
124 text-transform: uppercase;
125 color: var(--text-muted);
126 font-weight: 600;
127 }
128 .compare-branch-label .compare-branch-icon {
129 color: var(--text-faint);
130 font-size: 12px;
131 }
132 .compare-branch-card.is-base .compare-branch-label strong { color: #ff9d76; }
133 .compare-branch-card.is-head .compare-branch-label strong { color: #b69dff; }
134 .compare-branch-select {
135 appearance: none;
136 -webkit-appearance: none;
137 width: 100%;
138 padding: 8px 32px 8px 10px;
139 background: var(--bg-secondary);
140 border: 1px solid var(--border);
141 border-radius: 8px;
142 color: var(--text-strong);
143 font-size: 13.5px;
144 font-family: var(--font-mono);
145 line-height: 1.4;
146 cursor: pointer;
147 background-image: linear-gradient(45deg, transparent 50%, var(--text-muted) 50%), linear-gradient(135deg, var(--text-muted) 50%, transparent 50%);
148 background-position: calc(100% - 16px) 14px, calc(100% - 11px) 14px;
149 background-size: 5px 5px;
150 background-repeat: no-repeat;
151 transition: border-color 140ms ease, background-color 140ms ease;
152 }
153 .compare-branch-select:hover { border-color: var(--border-strong); }
154 .compare-branch-select:focus { outline: none; border-color: rgba(140,109,255,0.55); }
155 .compare-branch-arrow {
156 flex: 0 0 auto;
157 align-self: center;
158 display: inline-flex; align-items: center; justify-content: center;
159 width: 36px; height: 36px;
160 border-radius: 9999px;
161 background: linear-gradient(135deg, rgba(140,109,255,0.18), rgba(54,197,214,0.16));
162 border: 1px solid rgba(140,109,255,0.30);
163 color: var(--text-strong);
164 font-size: 16px;
165 line-height: 1;
166 font-weight: 700;
167 box-shadow: 0 0 18px -8px rgba(140,109,255,0.45);
168 }
169 @media (max-width: 720px) {
170 .compare-branch-arrow { align-self: flex-start; transform: rotate(90deg); }
171 }
172 .compare-form-actions {
173 display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
174 }
175 .compare-cta {
176 display: inline-flex; align-items: center; gap: 8px;
177 padding: 10px 18px;
178 border-radius: 10px;
179 font-size: 13.5px;
180 font-weight: 600;
181 color: #fff;
182 background: linear-gradient(135deg, #8c6dff 0%, #6f5be8 60%, #36c5d6 140%);
183 border: 1px solid rgba(140,109,255,0.55);
184 box-shadow: 0 6px 18px -8px rgba(140,109,255,0.55);
185 text-decoration: none;
186 cursor: pointer;
187 transition: transform 120ms ease, box-shadow 160ms ease, filter 160ms ease;
188 }
189 .compare-cta:hover {
190 transform: translateY(-1px);
191 box-shadow: 0 10px 22px -6px rgba(140,109,255,0.6);
192 color: #fff;
193 }
194 .compare-cta.is-disabled,
195 .compare-cta[aria-disabled="true"] {
196 opacity: 0.55;
197 cursor: not-allowed;
198 filter: grayscale(0.4);
199 transform: none;
200 box-shadow: none;
201 }
202 .compare-cta-secondary {
203 display: inline-flex; align-items: center; gap: 6px;
204 padding: 9px 14px;
205 border-radius: 10px;
206 font-size: 13px;
207 font-weight: 500;
208 color: var(--text);
209 background: var(--bg-secondary);
210 border: 1px solid var(--border);
211 text-decoration: none;
212 transition: border-color 140ms ease, color 140ms ease;
213 }
214 .compare-cta-secondary:hover {
215 border-color: var(--border-strong);
216 color: var(--text-strong);
217 }
218
219 /* Summary stats — commits ahead / behind */
220 .compare-stats {
221 display: flex;
222 flex-wrap: wrap;
223 gap: 8px;
224 margin: 0 0 var(--space-4);
225 }
226 .compare-stat-badge {
227 display: inline-flex; align-items: center; gap: 6px;
228 padding: 6px 12px;
229 border-radius: 9999px;
230 font-size: 12.5px;
231 font-weight: 600;
232 font-family: var(--font-mono);
233 font-variant-numeric: tabular-nums;
234 border: 1px solid var(--border);
235 background: var(--bg-secondary);
236 color: var(--text);
237 }
238 .compare-stat-badge .compare-stat-count { color: var(--text-strong); }
239 .compare-stat-badge.is-ahead {
240 color: var(--green);
241 border-color: rgba(52,211,153,0.32);
242 background: rgba(52,211,153,0.10);
243 }
244 .compare-stat-badge.is-behind {
245 color: var(--red);
246 border-color: rgba(248,113,113,0.32);
247 background: rgba(248,113,113,0.10);
248 }
249 .compare-stat-badge.is-files {
250 color: var(--text-link);
251 border-color: rgba(140,109,255,0.32);
252 background: rgba(140,109,255,0.08);
253 }
254
255 /* Diff stats bar — +/- counts */
256 .compare-diffstats {
257 display: inline-flex; align-items: center; gap: 10px;
258 padding: 6px 12px;
259 border-radius: 9999px;
260 font-size: 12.5px;
261 font-family: var(--font-mono);
262 font-variant-numeric: tabular-nums;
263 background: var(--bg-secondary);
264 border: 1px solid var(--border);
265 }
266 .compare-diffstats .compare-add { color: var(--green); font-weight: 700; }
267 .compare-diffstats .compare-del { color: var(--red); font-weight: 700; }
268 .compare-diffstats .compare-files-c { color: var(--text-muted); }
269
270 /* Section heading row above commit list / diff */
271 .compare-section-head {
272 display: flex; align-items: center; justify-content: space-between;
273 gap: 12px;
274 margin: 0 0 12px;
275 flex-wrap: wrap;
276 }
277 .compare-section-title {
278 font-size: 11.5px;
279 font-family: var(--font-mono);
280 letter-spacing: 0.14em;
281 text-transform: uppercase;
282 color: var(--text-muted);
283 font-weight: 600;
284 margin: 0;
285 }
286
287 /* Commit list */
288 .compare-commits {
289 display: flex; flex-direction: column;
290 margin: 0 0 var(--space-5);
291 background: var(--bg-elevated);
292 border: 1px solid var(--border);
293 border-radius: 12px;
294 overflow: hidden;
295 }
296 .compare-commit {
297 display: flex; align-items: center; gap: 14px;
298 padding: 12px 16px;
299 border-bottom: 1px solid var(--border);
300 transition: background 120ms ease;
301 }
302 .compare-commit:last-child { border-bottom: none; }
303 .compare-commit:hover { background: var(--bg-hover); }
304 .compare-commit-body { flex: 1; min-width: 0; }
305 .compare-commit-msg {
306 font-size: 14px;
307 color: var(--text-strong);
308 line-height: 1.4;
309 margin: 0 0 4px;
310 overflow: hidden;
311 text-overflow: ellipsis;
312 white-space: nowrap;
313 }
314 .compare-commit-msg a {
315 color: var(--text-strong);
316 text-decoration: none;
317 font-weight: 500;
318 }
319 .compare-commit-msg a:hover { color: var(--accent); }
320 .compare-commit-meta {
321 font-size: 12px;
322 color: var(--text-muted);
323 display: inline-flex; align-items: center; gap: 8px;
324 }
325 .compare-commit-meta .compare-commit-author {
326 color: var(--text);
327 font-weight: 500;
328 }
329 .compare-commit-meta .compare-commit-dot {
330 color: var(--text-faint);
331 }
332 .compare-commit-sha {
333 flex: 0 0 auto;
334 display: inline-flex; align-items: center;
335 padding: 4px 10px;
336 border-radius: 9999px;
337 background: var(--bg-secondary);
338 border: 1px solid var(--border);
339 color: var(--text);
340 font-family: var(--font-mono);
341 font-size: 12px;
342 text-decoration: none;
343 transition: border-color 140ms ease, color 140ms ease;
344 }
345 .compare-commit-sha:hover {
346 border-color: rgba(140,109,255,0.45);
347 color: var(--text-link);
348 }
349
350 /* Empty / identical state */
351 .compare-empty {
352 padding: 36px 28px;
353 text-align: center;
354 border: 1px dashed var(--border);
355 border-radius: 12px;
356 background: var(--bg-secondary);
357 color: var(--text-muted);
358 margin: 0 0 var(--space-5);
359 }
360 .compare-empty strong {
361 display: block;
362 color: var(--text-strong);
363 font-size: 15px;
364 margin-bottom: 6px;
365 }
366 .compare-empty p {
367 margin: 0 auto;
368 max-width: 460px;
369 font-size: 13.5px;
370 line-height: 1.55;
371 }
372 .compare-empty .compare-empty-icon {
373 display: inline-flex; align-items: center; justify-content: center;
374 width: 44px; height: 44px;
375 border-radius: 9999px;
376 background: linear-gradient(135deg, rgba(140,109,255,0.16), rgba(54,197,214,0.14));
377 border: 1px solid rgba(140,109,255,0.28);
378 color: var(--text-strong);
379 font-size: 18px;
380 margin: 0 auto 12px;
381 }
382
383 @media (max-width: 720px) {
384 .compare-hero { padding: 18px 18px 20px; }
385 .compare-form-actions { width: 100%; }
386 .compare-cta, .compare-cta-secondary { flex: 1 1 auto; justify-content: center; }
387 }
388`;
389
390/** Format an ISO date as a short relative string ("3h", "2d", "5w"). */
391function relTime(iso: string): string {
392 const t = Date.parse(iso);
393 if (!Number.isFinite(t)) return "";
394 const diff = Math.max(0, Date.now() - t);
395 const s = Math.floor(diff / 1000);
396 if (s < 60) return `${s}s ago`;
397 const m = Math.floor(s / 60);
398 if (m < 60) return `${m}m ago`;
399 const h = Math.floor(m / 60);
400 if (h < 24) return `${h}h ago`;
401 const d = Math.floor(h / 24);
402 if (d < 7) return `${d}d ago`;
403 const w = Math.floor(d / 7);
404 if (w < 5) return `${w}w ago`;
405 const mo = Math.floor(d / 30);
406 if (mo < 12) return `${mo}mo ago`;
407 const y = Math.floor(d / 365);
408 return `${y}y ago`;
409}
410
30411compare.get("/:owner/:repo/compare/:spec?", async (c) => {
31412 const { owner, repo } = c.req.param();
32413 const user = c.get("user");
46427 if (!spec || !spec.includes("...")) {
47428 // Show compare picker
48429 const defaultBase = branches.includes("main") ? "main" : branches[0] || "";
430 const defaultHead =
431 branches.find((b) => b !== defaultBase) || defaultBase;
49432 return c.html(
50433 <Layout title={`Compare — ${owner}/${repo}`} user={user}>
51434 <RepoHeader owner={owner} repo={repo} />
52435 <IssueNav owner={owner} repo={repo} active="code" />
53 <h2 style="margin-bottom: 16px">Compare changes</h2>
54 <form
55 method="get"
56 action={`/${owner}/${repo}/compare`}
57 >
58 <Flex gap={12} align="center" style="margin-bottom: 20px">
59 <select name="base" class="branch-selector" style="cursor: pointer">
60 {branches.map((b) => (
61 <option value={b} selected={b === defaultBase}>
62 {b}
63 </option>
64 ))}
65 </select>
66 <Text muted>...</Text>
67 <select name="head" class="branch-selector" style="cursor: pointer">
68 {branches.map((b) => (
69 <option value={b} selected={b !== defaultBase}>
70 {b}
71 </option>
72 ))}
73 </select>
436 <style dangerouslySetInnerHTML={{ __html: COMPARE_STYLES }} />
437
438 <section class="compare-hero">
439 <div class="compare-hero-inner">
440 <div class="compare-eyebrow">
441 Compare changes · <strong>{owner}/{repo}</strong>
442 </div>
443 <h1 class="compare-title">
444 <span class="gradient-text">Compare</span> branches.
445 </h1>
446 <p class="compare-sub">
447 Pick a base and a head branch to see exactly what changed.
448 Open a pull request when the diff looks right.
449 </p>
450 </div>
451 </section>
452
453 <form method="get" action={`/${owner}/${repo}/compare`}>
454 <div class="compare-branches">
455 <div class="compare-branch-card is-base">
456 <label class="compare-branch-label" for="compare-base">
457 <span class="compare-branch-icon"></span>
458 <strong>Base</strong>
459 <span style="color:var(--text-faint);font-weight:500">
460 · what you merge into
461 </span>
462 </label>
463 <select
464 id="compare-base"
465 name="base"
466 class="compare-branch-select"
467 >
468 {branches.map((b) => (
469 <option value={b} selected={b === defaultBase}>
470 {b}
471 </option>
472 ))}
473 </select>
474 </div>
475
476 <div class="compare-branch-arrow" aria-hidden="true"></div>
477
478 <div class="compare-branch-card is-head">
479 <label class="compare-branch-label" for="compare-head">
480 <span class="compare-branch-icon"></span>
481 <strong>Head</strong>
482 <span style="color:var(--text-faint);font-weight:500">
483 · what you want to merge
484 </span>
485 </label>
486 <select
487 id="compare-head"
488 name="head"
489 class="compare-branch-select"
490 >
491 {branches.map((b) => (
492 <option value={b} selected={b === defaultHead}>
493 {b}
494 </option>
495 ))}
496 </select>
497 </div>
498 </div>
499
500 <div class="compare-form-actions">
74501 <button
75502 type="submit"
76 class="btn btn-primary"
503 class="compare-cta"
77504 onclick={`this.form.action='/${owner}/${repo}/compare/'+this.form.base.value+'...'+this.form.head.value; return true;`}
78505 >
79 Compare
506 Compare branches →
80507 </button>
81 </Flex>
508 <a
509 href={`/${owner}/${repo}`}
510 class="compare-cta-secondary"
511 >
512 Cancel
513 </a>
514 </div>
82515 </form>
83516 </Layout>
84517 );
140573 return { sha, message: msg, author, date };
141574 });
142575
576 // Aggregate diff stats for the badge bar.
577 const totalAdditions = files.reduce((s, f) => s + f.additions, 0);
578 const totalDeletions = files.reduce((s, f) => s + f.deletions, 0);
579 const isIdentical = base === head;
580 const hasChanges = commitsBetween.length > 0 || files.length > 0;
581
143582 return c.html(
144583 <Layout title={`${base}...${head} — ${owner}/${repo}`} user={user}>
145584 <RepoHeader owner={owner} repo={repo} />
146585 <IssueNav owner={owner} repo={repo} active="code" />
147 <h2 style="margin-bottom: 8px">
148 Comparing {base}...{head}
149 </h2>
150 <Text size={14} muted style="display:block;margin-bottom:20px">
151 {commitsBetween.length} commit{commitsBetween.length !== 1 ? "s" : ""}
152 </Text>
586 <style dangerouslySetInnerHTML={{ __html: COMPARE_STYLES }} />
587
588 <section class="compare-hero">
589 <div class="compare-hero-inner">
590 <div class="compare-eyebrow">
591 Comparing · <strong>{owner}/{repo}</strong>
592 </div>
593 <h1 class="compare-title">
594 {hasChanges ? (
595 <>
596 <span class="gradient-text">
597 {commitsBetween.length} commit
598 {commitsBetween.length !== 1 ? "s" : ""}
599 </span>{" "}
600 ahead.
601 </>
602 ) : isIdentical ? (
603 <>
604 <span class="gradient-text">Nothing</span> to compare.
605 </>
606 ) : (
607 <>
608 <span class="gradient-text">No changes</span> between branches.
609 </>
610 )}
611 </h1>
612 <p class="compare-sub">
613 <span style="font-family:var(--font-mono);color:var(--text)">
614 {base}
615 </span>{" "}
616 <span style="color:var(--text-faint)"></span>{" "}
617 <span style="font-family:var(--font-mono);color:var(--text)">
618 {head}
619 </span>
620 {hasChanges
621 ? " — review the diff below, then open a pull request when it looks right."
622 : isIdentical
623 ? " — base and head point to the same ref. Pick different branches to see a diff."
624 : " — these branches diverge but produce no diff. Nothing to merge."}
625 </p>
626 </div>
627 </section>
628
629 {/* Summary stats row */}
630 {hasChanges && (
631 <div class="compare-stats">
632 <span class="compare-stat-badge is-ahead">
633 <span class="compare-stat-count">+{commitsBetween.length}</span>
634 commit{commitsBetween.length !== 1 ? "s" : ""} ahead
635 </span>
636 <span class="compare-stat-badge is-files">
637 <span class="compare-stat-count">{files.length}</span>
638 file{files.length !== 1 ? "s" : ""} changed
639 </span>
640 <span class="compare-diffstats">
641 <span class="compare-add">+{totalAdditions}</span>
642 <span class="compare-del">−{totalDeletions}</span>
643 <span class="compare-files-c">lines</span>
644 </span>
645 <a
646 href={`/${owner}/${repo}/pulls/new?base=${encodeURIComponent(base)}&head=${encodeURIComponent(head)}`}
647 class="compare-cta"
648 style="margin-left:auto"
649 >
650 Create pull request →
651 </a>
652 </div>
653 )}
654
655 {!hasChanges && (
656 <div class="compare-empty">
657 <div class="compare-empty-icon"></div>
658 <strong>
659 {isIdentical
660 ? "Nothing to compare."
661 : "No diff between these branches."}
662 </strong>
663 <p>
664 {isIdentical
665 ? "Base and head are the same branch. Push some commits to a feature branch, then come back here to compare and open a PR."
666 : "These branches diverge but produce no file changes. There's nothing to merge."}
667 </p>
668 <div
669 class="compare-form-actions"
670 style="justify-content:center;margin-top:16px"
671 >
672 <a
673 href={`/${owner}/${repo}/compare`}
674 class="compare-cta-secondary"
675 >
676 ← Pick different branches
677 </a>
678 <span
679 class="compare-cta is-disabled"
680 aria-disabled="true"
681 title="Nothing to compare yet"
682 >
683 Create pull request →
684 </span>
685 </div>
686 </div>
687 )}
153688
154689 {commitsBetween.length > 0 && (
155 <div class="commit-list" style="margin-bottom: 24px">
156 {commitsBetween.map((cm) => (
157 <div class="commit-item">
158 <div>
159 <div class="commit-message">
160 <a href={`/${owner}/${repo}/commit/${cm.sha}`}>
161 {cm.message}
162 </a>
690 <>
691 <div class="compare-section-head">
692 <h2 class="compare-section-title">Commits in this comparison</h2>
693 <span class="compare-stat-badge">
694 <span class="compare-stat-count">{commitsBetween.length}</span>
695 total
696 </span>
697 </div>
698 <div class="compare-commits">
699 {commitsBetween.map((cm) => (
700 <div class="compare-commit">
701 <div class="compare-commit-body">
702 <div class="compare-commit-msg">
703 <a href={`/${owner}/${repo}/commit/${cm.sha}`}>
704 {cm.message}
705 </a>
706 </div>
707 <div class="compare-commit-meta">
708 <span class="compare-commit-author">{cm.author}</span>
709 <span class="compare-commit-dot">·</span>
710 <span>{relTime(cm.date)}</span>
711 </div>
163712 </div>
164 <div class="commit-meta">{cm.author}</div>
713 <a
714 href={`/${owner}/${repo}/commit/${cm.sha}`}
715 class="compare-commit-sha"
716 >
717 {cm.sha.slice(0, 7)}
718 </a>
165719 </div>
166 <a
167 href={`/${owner}/${repo}/commit/${cm.sha}`}
168 class="commit-sha"
169 >
170 {cm.sha.slice(0, 7)}
171 </a>
172 </div>
173 ))}
174 </div>
720 ))}
721 </div>
722 </>
175723 )}
176724
177 <DiffView raw={raw} files={files} />
725 {files.length > 0 && (
726 <>
727 <div class="compare-section-head">
728 <h2 class="compare-section-title">File changes</h2>
729 <span class="compare-diffstats">
730 <span class="compare-add">+{totalAdditions}</span>
731 <span class="compare-del">−{totalDeletions}</span>
732 <span class="compare-files-c">
733 across {files.length} file{files.length !== 1 ? "s" : ""}
734 </span>
735 </span>
736 </div>
737 <DiffView raw={raw} files={files} />
738 </>
739 )}
178740 </Layout>
179741 );
180742});
181743