annotate hg-web/src/components/app.tsx @ 279:b3b547563ec7

Add Google connector service and agent wiki Implement the C/Seobeo Google Drive and Gmail connector with encrypted OAuth storage, Zenbu authentication, browser testing, AI tool discovery, chunked HTTP decoding, and Bazel coverage. Consolidate repository guidance into progressive wiki documentation and enforce arena-first allocation for new first-party C code. Co-authored-by: Copilot <[email protected]> Copilot-Session: 84c338fd-0939-4bb3-b7f3-1062eb213e5d
author MrJuneJune <me@mrjunejune.com>
date Mon, 17 Aug 2026 22:22:36 -0700
parents 70de0c80d093
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
1 import React, { useState, useEffect, useCallback } from 'react';
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
2 import { Graph, useGraphData } from "hg-web/src/components/graph";
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
3 import { DirectoryBrowser } from "hg-web/src/components/directory-browser";
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
4 import { Header } from "hg-web/src/components/header";
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
5 import { Footer } from "hg-web/src/components/footer";
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
6 import { ThemeProvider, useTheme } from "hg-web/src/components/theme";
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
7
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
8 type Page = 'landing' | 'graph' | 'directory' | 'changeset';
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
9
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
10 type RouteState = {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
11 page: Page;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
12 graphCommit?: string;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
13 graphTip?: string;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
14 dirPath?: string;
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
15 changesetId?: string;
223
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
16 returnDepth?: number;
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
17 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
18
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
19 type ChangesetDetail = {
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
20 node: string;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
21 date: [number, number];
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
22 desc: string;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
23 branch: string;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
24 bookmarks: string[];
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
25 tags: string[];
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
26 user: string;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
27 parents: string[];
223
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
28 files: Array<{
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
29 file: string;
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
30 status: string;
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
31 }>;
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
32 diff: Array<{
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
33 blockno: number;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
34 lines: Array<{ t: string; n: number; l: string }>;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
35 }>;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
36 };
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
37
225
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
38 type DiffLine = ChangesetDetail['diff'][number]['lines'][number];
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
39
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
40 type DiffCell = {
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
41 lineNumber: number | null;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
42 text: string;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
43 kind: 'context' | 'add' | 'remove' | 'meta';
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
44 };
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
45
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
46 type SideBySideRow = {
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
47 left?: DiffCell;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
48 right?: DiffCell;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
49 range?: string;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
50 };
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
51
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
52 function trimDiffLine(line: string): string {
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
53 return line.endsWith('\n') ? line.slice(0, -1) : line;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
54 }
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
55
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
56 function contentDiffLine(line: DiffLine): string {
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
57 const text = trimDiffLine(line.l);
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
58 if ((line.t === '+' || line.t === '-' || line.t === '' || line.t === ' ') &&
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
59 text.startsWith(line.t || ' ')) {
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
60 return text.slice(1);
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
61 }
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
62 return text;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
63 }
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
64
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
65 function buildSideBySideRows(lines: DiffLine[]): SideBySideRow[] {
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
66 const rows: SideBySideRow[] = [];
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
67 let removals: DiffCell[] = [];
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
68 let additions: DiffCell[] = [];
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
69 let oldLine: number | null = null;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
70 let newLine: number | null = null;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
71 let inHunk = false;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
72
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
73 const flushChanges = () => {
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
74 const count = Math.max(removals.length, additions.length);
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
75 for (let index = 0; index < count; index++) {
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
76 rows.push({ left: removals[index], right: additions[index] });
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
77 }
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
78 removals = [];
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
79 additions = [];
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
80 };
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
81
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
82 for (const line of lines) {
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
83 if (line.t === '@') {
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
84 flushChanges();
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
85 const range = trimDiffLine(line.l);
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
86 const match = range.match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
87 oldLine = match ? Number(match[1]) : null;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
88 newLine = match ? Number(match[2]) : null;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
89 inHunk = true;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
90 rows.push({ range });
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
91 continue;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
92 }
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
93
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
94 if (!inHunk && (line.t === '-' || line.t === '+')) {
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
95 const cell: DiffCell = {
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
96 lineNumber: null,
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
97 text: trimDiffLine(line.l),
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
98 kind: 'meta',
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
99 };
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
100 if (line.t === '-') removals.push(cell);
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
101 else additions.push(cell);
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
102 continue;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
103 }
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
104
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
105 if (line.t === '-') {
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
106 removals.push({
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
107 lineNumber: oldLine,
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
108 text: contentDiffLine(line),
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
109 kind: 'remove',
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
110 });
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
111 if (oldLine !== null) oldLine++;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
112 continue;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
113 }
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
114
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
115 if (line.t === '+') {
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
116 additions.push({
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
117 lineNumber: newLine,
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
118 text: contentDiffLine(line),
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
119 kind: 'add',
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
120 });
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
121 if (newLine !== null) newLine++;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
122 continue;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
123 }
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
124
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
125 flushChanges();
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
126 const text = contentDiffLine(line);
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
127 rows.push({
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
128 left: { lineNumber: oldLine, text, kind: 'context' },
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
129 right: { lineNumber: newLine, text, kind: 'context' },
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
130 });
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
131 if (oldLine !== null) oldLine++;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
132 if (newLine !== null) newLine++;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
133 }
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
134
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
135 flushChanges();
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
136 return rows;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
137 }
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
138
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
139 function diffBlockFilename(
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
140 block: ChangesetDetail['diff'][number],
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
141 fallback?: string,
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
142 ): string {
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
143 if (fallback) return fallback;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
144 const newFileHeader = block.lines.find(
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
145 line => line.t === '+' && line.l.startsWith('+++ '),
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
146 );
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
147 if (!newFileHeader) return `Diff block ${block.blockno}`;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
148 return trimDiffLine(newFileHeader.l).replace(/^\+\+\+ (?:b\/)?/, '').split('\t')[0];
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
149 }
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
150
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
151 function SideBySideDiff({
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
152 block,
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
153 filename,
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
154 }: {
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
155 block: ChangesetDetail['diff'][number];
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
156 filename: string;
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
157 }) {
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
158 const rows = buildSideBySideRows(block.lines);
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
159 return (
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
160 <div className="side-by-side-diff">
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
161 <div className="diff-file-header">{filename}</div>
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
162 <div className="diff-column-headings">
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
163 <span>Before</span>
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
164 <span>After</span>
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
165 </div>
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
166 <div className="diff-grid">
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
167 {rows.map((row, index) => (
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
168 row.range ? (
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
169 <div className="diff-range-row" key={`range-${index}`}>{row.range}</div>
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
170 ) : (
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
171 <React.Fragment key={`row-${index}`}>
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
172 <span className={`diff-side-number diff-${row.left?.kind || 'empty'}`}>
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
173 {row.left?.lineNumber ?? ''}
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
174 </span>
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
175 <code className={`diff-side-code diff-left diff-${row.left?.kind || 'empty'}`}>
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
176 {row.left?.text ?? ''}
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
177 </code>
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
178 <span className={`diff-side-number diff-column-divider diff-${row.right?.kind || 'empty'}`}>
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
179 {row.right?.lineNumber ?? ''}
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
180 </span>
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
181 <code className={`diff-side-code diff-right diff-${row.right?.kind || 'empty'}`}>
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
182 {row.right?.text ?? ''}
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
183 </code>
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
184 </React.Fragment>
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
185 )
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
186 ))}
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
187 </div>
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
188 </div>
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
189 );
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
190 }
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
191
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
192 // Icons
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
193 const ICONS = {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
194 folder: "/icons/folder.png",
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
195 };
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
196
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
197 const GraphIcon = () => (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
198 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
199 <circle cx="6" cy="6" r="3"/>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
200 <circle cx="6" cy="18" r="3"/>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
201 <circle cx="18" cy="12" r="3"/>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
202 <line x1="6" y1="9" x2="6" y2="15"/>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
203 <path d="M8.5 7.5L15.5 11"/>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
204 </svg>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
205 );
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
206
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
207 const FolderIcon = () => (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
208 <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" stroke="none">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
209 <path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
210 </svg>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
211 );
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
212
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
213 const API_BASE = '/api/repo';
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
214
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
215 function parseRoute(): RouteState {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
216 const params = new URLSearchParams(window.location.search);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
217 const pathname = window.location.pathname;
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
218 const changesetMatch = pathname.match(/^\/changeset\/([^/]+)$/);
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
219
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
220 if (changesetMatch) {
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
221 try {
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
222 const changesetId = decodeURIComponent(changesetMatch[1]);
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
223 if (/^(?:[0-9a-f]{1,40}|tip)$/i.test(changesetId)) {
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
224 return { page: 'changeset', changesetId };
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
225 }
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
226 } catch {
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
227 return { page: 'landing' };
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
228 }
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
229 }
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
230
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
231 if (pathname.startsWith('/graph') || params.has('graph')) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
232 return {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
233 page: 'graph',
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
234 graphCommit: params.get('commit') || undefined,
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
235 graphTip: params.get('tip') || undefined,
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
236 };
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
237 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
238
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
239 if (pathname.startsWith('/directory') || params.has('path')) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
240 return {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
241 page: 'directory',
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
242 dirPath: params.get('path') || '',
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
243 };
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
244 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
245
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
246 return { page: 'landing' };
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
247 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
248
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
249 function buildUrl(state: RouteState): string {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
250 const params = new URLSearchParams();
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
251
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
252 switch (state.page) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
253 case 'graph':
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
254 if (state.graphCommit) params.set('commit', state.graphCommit);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
255 if (state.graphTip) params.set('tip', state.graphTip);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
256 return `/graph${params.toString() ? '?' + params.toString() : ''}`;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
257 case 'directory':
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
258 if (state.dirPath) params.set('path', state.dirPath);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
259 return `/directory${params.toString() ? '?' + params.toString() : ''}`;
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
260 case 'changeset':
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
261 return state.changesetId ? `/changeset/${encodeURIComponent(state.changesetId)}` : '/graph';
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
262 default:
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
263 return '/';
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
264 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
265 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
266
223
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
267 function isRouteState(value: unknown): value is RouteState {
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
268 if (!value || typeof value !== 'object' || !('page' in value)) return false;
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
269 return ['landing', 'graph', 'directory', 'changeset'].includes(
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
270 String((value as { page: unknown }).page),
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
271 );
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
272 }
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
273
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
274 // Landing Page Component
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
275 function LandingPage({
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
276 onNavigateToGraph,
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
277 onNavigateToDirectory,
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
278 onNavigateToChangeset,
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
279 }: {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
280 onNavigateToGraph: () => void;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
281 onNavigateToDirectory: (path?: string) => void;
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
282 onNavigateToChangeset: (node: string) => void;
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
283 }) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
284 const [directories, setDirectories] = useState<any[]>([]);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
285 const [files, setFiles] = useState<any[]>([]);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
286 const [dirLoading, setDirLoading] = useState(true);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
287
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
288 const { data: graphData, loading: graphLoading } = useGraphData();
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
289
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
290 useEffect(() => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
291 fetch(`${API_BASE}/list`)
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
292 .then(r => r.json())
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
293 .then(data => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
294 setDirectories(data.directories || []);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
295 setFiles(data.files || []);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
296 setDirLoading(false);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
297 })
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
298 .catch(() => setDirLoading(false));
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
299 }, []);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
300
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
301 const previewItems = [
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
302 ...directories.slice(0, 6),
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
303 ...files.slice(0, Math.max(0, 6 - directories.length))
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
304 ].slice(0, 6);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
305
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
306 return (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
307 <div className="landing-grid">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
308 {/* Graph Preview */}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
309 <div className="landing-section">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
310 <div className="landing-section-header">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
311 <span className="landing-section-title">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
312 <GraphIcon />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
313 Recent Commits
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
314 </span>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
315 <a href="/graph" className="landing-section-link" onClick={(e) => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
316 e.preventDefault();
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
317 onNavigateToGraph();
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
318 }}>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
319 View all
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
320 </a>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
321 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
322 <div className="landing-section-content">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
323 {graphLoading ? (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
324 <div className="loading-state">Loading commits...</div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
325 ) : graphData ? (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
326 <Graph
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
327 data={graphData}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
328 maxRows={8}
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
329 onCommitClick={onNavigateToChangeset}
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
330 />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
331 ) : (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
332 <div className="empty-state">Failed to load commits</div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
333 )}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
334 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
335 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
336
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
337 {/* Directory Preview */}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
338 <div className="landing-section">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
339 <div className="landing-section-header">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
340 <span className="landing-section-title">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
341 <FolderIcon />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
342 Repository Files
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
343 </span>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
344 <a href="/directory" className="landing-section-link" onClick={(e) => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
345 e.preventDefault();
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
346 onNavigateToDirectory();
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
347 }}>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
348 Browse all
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
349 </a>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
350 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
351 <div className="landing-section-content">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
352 {dirLoading ? (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
353 <div className="loading-state">Loading files...</div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
354 ) : previewItems.length > 0 ? (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
355 previewItems.map((item) => (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
356 <div
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
357 key={item.abspath}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
358 className="dir-item"
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
359 onClick={() => onNavigateToDirectory(item.abspath)}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
360 >
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
361 <span className="dir-item-icon">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
362 <img
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
363 className="icon-invert"
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
364 src={directories.includes(item) ? ICONS.folder : "/icons/file.svg"}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
365 alt=""
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
366 />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
367 </span>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
368 <span className="dir-item-name">{item.basename}</span>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
369 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
370 ))
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
371 ) : (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
372 <div className="empty-state">No files found</div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
373 )}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
374 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
375 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
376 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
377 );
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
378 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
379
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
380 // Graph Page Component
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
381 function GraphPage({
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
382 onBack,
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
383 initialCommit,
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
384 initialTip,
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
385 onOpenChangeset,
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
386 }: {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
387 onBack: () => void;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
388 initialCommit?: string;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
389 initialTip?: string;
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
390 onOpenChangeset: (node: string) => void;
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
391 }) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
392 const { data, loading, error, loadMore, hasMore, tip, currentCommit } = useGraphData({
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
393 initialCommit: initialCommit || null,
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
394 graphTop: initialTip || null,
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
395 });
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
396
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
397 useEffect(() => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
398 if (tip && currentCommit) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
399 const params = new URLSearchParams();
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
400 params.set('commit', currentCommit);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
401 params.set('tip', tip);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
402 const newUrl = `/graph?${params.toString()}`;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
403 window.history.replaceState({ page: 'graph', graphCommit: currentCommit, graphTip: tip }, '', newUrl);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
404 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
405 }, [currentCommit, tip]);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
406
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
407 return (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
408 <div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
409 <div className="page-header">
223
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
410 <button className="back-button" onClick={onBack} aria-label="Back">
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
411 &larr; Back
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
412 </button>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
413 <span className="page-title">Commit Graph</span>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
414 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
415
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
416 {tip && (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
417 <div className="graph-params">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
418 <span className="graph-param">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
419 <span className="graph-param-label">Tip:</span>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
420 <span className="graph-param-value">{tip.substring(0, 12)}</span>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
421 </span>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
422 {currentCommit && currentCommit !== tip && (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
423 <span className="graph-param">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
424 <span className="graph-param-label">Current:</span>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
425 <span className="graph-param-value">{currentCommit.substring(0, 12)}</span>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
426 </span>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
427 )}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
428 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
429 )}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
430
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
431 {error && (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
432 <div className="error-message">Error: {error}</div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
433 )}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
434
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
435 <Graph
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
436 data={data}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
437 loading={loading}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
438 hasMore={hasMore}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
439 onLoadMore={loadMore}
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
440 onCommitClick={onOpenChangeset}
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
441 />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
442 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
443 );
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
444 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
445
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
446 function ChangesetPage({
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
447 changesetId,
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
448 onBack,
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
449 onOpenChangeset,
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
450 }: {
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
451 changesetId: string;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
452 onBack: () => void;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
453 onOpenChangeset: (node: string) => void;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
454 }) {
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
455 const [changeset, setChangeset] = useState<ChangesetDetail | null>(null);
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
456 const [loading, setLoading] = useState(true);
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
457 const [error, setError] = useState<string | null>(null);
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
458
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
459 useEffect(() => {
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
460 const controller = new AbortController();
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
461
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
462 setLoading(true);
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
463 setError(null);
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
464 setChangeset(null);
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
465
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
466 fetch(`/api/changeset/${encodeURIComponent(changesetId)}`, { signal: controller.signal })
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
467 .then(async response => {
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
468 if (!response.ok) {
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
469 const message = await response.text();
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
470 throw new Error(message || `Unable to load changeset (${response.status})`);
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
471 }
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
472 return response.json() as Promise<ChangesetDetail>;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
473 })
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
474 .then(setChangeset)
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
475 .catch(err => {
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
476 if (err.name !== 'AbortError') setError(err.message);
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
477 })
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
478 .finally(() => {
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
479 if (!controller.signal.aborted) setLoading(false);
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
480 });
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
481
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
482 return () => controller.abort();
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
483 }, [changesetId]);
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
484
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
485 return (
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
486 <div>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
487 <div className="page-header">
223
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
488 <button className="back-button" onClick={onBack} aria-label="Back">
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
489 &larr; Back
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
490 </button>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
491 <span className="page-title">Changeset</span>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
492 </div>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
493
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
494 {loading && <div className="loading-state">Loading changeset...</div>}
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
495 {error && <div className="error-message">Error: {error}</div>}
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
496
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
497 {changeset && (
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
498 <article className="changeset-paper">
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
499 <div className="changeset-heading">
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
500 <code>{changeset.node}</code>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
501 <span className="changeset-branch">{changeset.branch}</span>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
502 </div>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
503 <h2>{changeset.desc}</h2>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
504 <div className="changeset-meta">
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
505 <span>{changeset.user}</span>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
506 <time dateTime={new Date(changeset.date[0] * 1000).toISOString()}>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
507 {new Date(changeset.date[0] * 1000).toLocaleString()}
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
508 </time>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
509 </div>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
510
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
511 {(changeset.bookmarks.length > 0 || changeset.tags.length > 0) && (
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
512 <div className="changeset-labels">
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
513 {changeset.bookmarks.map(bookmark => <span key={`bookmark-${bookmark}`}>{bookmark}</span>)}
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
514 {changeset.tags.map(tag => <span key={`tag-${tag}`}>{tag}</span>)}
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
515 </div>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
516 )}
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
517
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
518 {changeset.parents.length > 0 && (
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
519 <div className="changeset-parents">
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
520 <strong>Parents</strong>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
521 {changeset.parents.map(parent => (
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
522 <button type="button" key={parent} onClick={() => onOpenChangeset(parent)}>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
523 {parent.substring(0, 12)}
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
524 </button>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
525 ))}
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
526 </div>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
527 )}
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
528
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
529 {changeset.files.length > 0 && (
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
530 <div className="changeset-files">
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
531 <strong>Files</strong>
223
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
532 {changeset.files.map(file => (
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
533 <code key={file.file}>
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
534 <span className={`changeset-file-status status-${file.status}`}>
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
535 {file.status}
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
536 </span>
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
537 {file.file}
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
538 </code>
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
539 ))}
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
540 </div>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
541 )}
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
542
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
543 <section className="changeset-diff" aria-label="Changeset diff">
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
544 <h3>Diff</h3>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
545 {changeset.diff.length === 0 ? (
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
546 <div className="empty-state">No textual changes in this changeset.</div>
225
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
547 ) : changeset.diff.map((block, index) => (
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
548 <SideBySideDiff
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
549 key={block.blockno}
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
550 block={block}
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
551 filename={diffBlockFilename(block, changeset.files[index]?.file)}
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
552 />
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
553 ))}
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
554 </section>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
555 </article>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
556 )}
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
557 </div>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
558 );
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
559 }
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
560
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
561 // Directory Page Component
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
562 function DirectoryPage({
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
563 onBack,
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
564 initialPath,
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
565 onPathChange,
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
566 }: {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
567 onBack: () => void;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
568 initialPath?: string;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
569 onPathChange: (path: string) => void;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
570 }) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
571 return (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
572 <div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
573 <div className="page-header">
223
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
574 <button className="back-button" onClick={onBack} aria-label="Back">
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
575 &larr; Back
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
576 </button>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
577 <span className="page-title">Repository Files</span>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
578 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
579
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
580 <DirectoryBrowser
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
581 initialPath={initialPath}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
582 onPathChange={onPathChange}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
583 />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
584 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
585 );
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
586 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
587
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
588 // Main App Content (uses theme context)
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
589 function AppContent() {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
590 const [route, setRoute] = useState<RouteState>(parseRoute);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
591 const { isDark, toggleTheme } = useTheme();
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
592
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
593 // Handle browser back/forward
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
594 useEffect(() => {
223
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
595 const handlePopState = (event: PopStateEvent) => {
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
596 setRoute(isRouteState(event.state) ? event.state : parseRoute());
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
597 };
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
598 window.addEventListener('popstate', handlePopState);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
599 return () => window.removeEventListener('popstate', handlePopState);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
600 }, []);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
601
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
602 const navigate = useCallback((newRoute: RouteState) => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
603 const url = buildUrl(newRoute);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
604 window.history.pushState(newRoute, '', url);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
605 setRoute(newRoute);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
606 }, []);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
607
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
608 const navigateToLanding = useCallback(() => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
609 navigate({ page: 'landing' });
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
610 }, [navigate]);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
611
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
612 const navigateToGraph = useCallback((commit?: string, tip?: string) => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
613 navigate({ page: 'graph', graphCommit: commit, graphTip: tip });
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
614 }, [navigate]);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
615
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
616 const navigateToDirectory = useCallback((path?: string) => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
617 navigate({ page: 'directory', dirPath: path || '' });
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
618 }, [navigate]);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
619
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
620 const navigateToChangeset = useCallback((changesetId: string) => {
223
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
621 const returnDepth = route.page === 'changeset'
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
622 ? (route.returnDepth ?? 0) + 1
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
623 : 1;
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
624 navigate({ page: 'changeset', changesetId, returnDepth });
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
625 }, [navigate, route.page, route.returnDepth]);
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
626
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
627 const navigateBackFromChangeset = useCallback(() => {
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
628 if (route.returnDepth !== undefined && route.returnDepth > 0) {
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
629 window.history.go(-route.returnDepth);
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
630 return;
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
631 }
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
632 navigateToGraph();
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
633 }, [navigateToGraph, route.returnDepth]);
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
634
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
635 const handleDirectoryPathChange = useCallback((path: string) => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
636 // Update URL without full navigation
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
637 const params = new URLSearchParams();
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
638 if (path) params.set('path', path);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
639 const newUrl = `/directory${params.toString() ? '?' + params.toString() : ''}`;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
640 window.history.replaceState({ page: 'directory', dirPath: path }, '', newUrl);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
641 setRoute(prev => ({ ...prev, dirPath: path }));
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
642 }, []);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
643
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
644 return (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
645 <div className="app-container">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
646 <Header
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
647 title="Zenbu Repository"
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
648 showThemeToggle={true}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
649 isDark={isDark}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
650 onToggleTheme={toggleTheme}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
651 />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
652
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
653 {/* Navigation Tabs */}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
654 <div className="nav-tabs">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
655 <button
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
656 className={`nav-tab ${route.page === 'landing' ? 'active' : ''}`}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
657 onClick={navigateToLanding}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
658 >
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
659 Home
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
660 </button>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
661 <button
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
662 className={`nav-tab ${route.page === 'graph' || route.page === 'changeset' ? 'active' : ''}`}
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
663 onClick={() => navigateToGraph()}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
664 >
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
665 <GraphIcon />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
666 Graph
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
667 </button>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
668 <button
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
669 className={`nav-tab ${route.page === 'directory' ? 'active' : ''}`}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
670 onClick={() => navigateToDirectory()}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
671 >
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
672 <FolderIcon />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
673 Files
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
674 </button>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
675 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
676
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
677 {/* Page Content */}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
678 {route.page === 'landing' && (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
679 <LandingPage
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
680 onNavigateToGraph={() => navigateToGraph()}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
681 onNavigateToDirectory={navigateToDirectory}
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
682 onNavigateToChangeset={navigateToChangeset}
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
683 />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
684 )}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
685
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
686 {route.page === 'graph' && (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
687 <GraphPage
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
688 onBack={navigateToLanding}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
689 initialCommit={route.graphCommit}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
690 initialTip={route.graphTip}
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
691 onOpenChangeset={navigateToChangeset}
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
692 />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
693 )}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
694
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
695 {route.page === 'directory' && (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
696 <DirectoryPage
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
697 onBack={navigateToLanding}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
698 initialPath={route.dirPath}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
699 onPathChange={handleDirectoryPathChange}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
700 />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
701 )}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
702
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
703 {route.page === 'changeset' && route.changesetId && (
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
704 <ChangesetPage
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
705 changesetId={route.changesetId}
223
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
706 onBack={navigateBackFromChangeset}
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
707 onOpenChangeset={navigateToChangeset}
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
708 />
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
709 )}
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
710
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
711 <Footer />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
712 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
713 );
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
714 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
715
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
716 // App wrapper with ThemeProvider
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
717 function App() {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
718 return (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
719 <ThemeProvider>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
720 <AppContent />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
721 </ThemeProvider>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
722 );
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
723 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
724
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
725 export { App };