annotate hg-web/src/components/graph.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 3007ef5fc0ed
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, { useEffect, useRef, useState, useCallback, useMemo } from 'react';
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
2
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
3 // Configuration constants for the layout
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
4 const rowHeight = 40;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
5 const colWidth = 20;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
6 const nodeRadius = 4.5;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
7
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
8 // --- Interfaces ---
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
9
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
10 interface Changeset {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
11 node: string;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
12 date: [number, number];
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
13 desc: string;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
14 branch: string;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
15 bookmarks: string[];
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
16 tags: string[];
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
17 user: string;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
18 phase: string;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
19 col: number;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
20 row: number;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
21 color: number;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
22 edges: Array<{
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
23 bcolor: string;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
24 col: number;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
25 color: number;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
26 nextcol: number;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
27 width: number;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
28 }>;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
29 parents: string[];
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
30 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
31
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
32 interface GraphData {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
33 node: string;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
34 changeset_count: number;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
35 changesets: Changeset[];
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
36 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
37
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
38 interface UseGraphDataOptions {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
39 initialCommit?: string | null;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
40 graphTop?: string | null;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
41 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
42
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
43 interface UseGraphDataResult {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
44 data: GraphData | null;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
45 loading: boolean;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
46 error: string | null;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
47 loadMore: () => void;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
48 hasMore: boolean;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
49 tip: string | null;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
50 currentCommit: string | null;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
51 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
52
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
53 // --- Hook Logic ---
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
54
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
55 function useGraphData({ initialCommit = null, graphTop = null }: UseGraphDataOptions = {}): UseGraphDataResult {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
56 const [data, setData] = useState<GraphData | null>(null);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
57 const [loading, setLoading] = useState(false);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
58 const [error, setError] = useState<string | null>(null);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
59 const [tip, setTip] = useState<string | null>(graphTop);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
60 const [currentCommit, setCurrentCommit] = useState<string | null>(initialCommit);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
61 const [hasMore, setHasMore] = useState(true);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
62
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
63 const fetchData = useCallback(async (commit: string | null, tipNode: string | null, append: boolean = false) => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
64 if (loading) return;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
65 setLoading(true);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
66 setError(null);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
67
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
68 try {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
69 const url = !commit
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
70 ? `/api/graph/tip?style=json`
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
71 : `/api/graph/${commit}?graphtop=${tipNode}&style=json`;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
72
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
73 const response = await fetch(url);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
74 if (!response.ok) throw new Error(`Fetch failed: ${response.status}`);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
75
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
76 const result: GraphData = await response.json();
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
77
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
78 setData(prev => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
79 if (append && prev) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
80 const existingNodes = new Set(prev.changesets.map(cs => cs.node));
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
81 const newChangesets = result.changesets.filter(cs => !existingNodes.has(cs.node));
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
82
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
83 // Re-index rows to ensure they increment correctly for the canvas height
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
84 const startRow = prev.changesets.length;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
85 const reindexed = newChangesets.map((cs, idx) => ({
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
86 ...cs,
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
87 row: startRow + idx
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
88 }));
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
89
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
90 return {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
91 ...result,
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
92 changesets: [...prev.changesets, ...reindexed]
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
93 };
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
94 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
95 return result;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
96 });
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
97
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
98 if (!tip && !append) setTip(result.node);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
99
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
100 if (result.changesets.length > 0) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
101 const lastNode = result.changesets[result.changesets.length - 1].node;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
102 setCurrentCommit(lastNode);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
103 setHasMore(result.changesets.length >= 30);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
104 } else {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
105 setHasMore(false);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
106 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
107 } catch (err: any) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
108 setError(err.message);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
109 } finally {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
110 setLoading(false);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
111 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
112 }, [tip, loading]);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
113
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
114 useEffect(() => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
115 fetchData(initialCommit, graphTop, false);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
116 }, [initialCommit, graphTop]);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
117
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
118 const loadMore = useCallback(() => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
119 if (!loading && hasMore && currentCommit && tip) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
120 fetchData(currentCommit, tip, true);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
121 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
122 }, [loading, hasMore, currentCommit, tip, fetchData]);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
123
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
124 return { data, loading, error, loadMore, hasMore, tip, currentCommit };
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
125 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
126
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
127 // --- Pencil Rendering Logic ---
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
128
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
129 const drawPencilLine = (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
130 ctx: CanvasRenderingContext2D,
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
131 x1: number, y1: number,
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
132 x2: number, y2: number,
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
133 texture: CanvasPattern | null, // Ensure type safety
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
134 isCurve: boolean = false
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
135 ) => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
136 const strokes = 3;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
137 ctx.save();
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
138
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
139 for (let s = 0; s < strokes; s++) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
140 ctx.beginPath();
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
141 ctx.strokeStyle = texture;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
142 ctx.globalAlpha = 0.2 + (s * 0.2);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
143 ctx.lineWidth = 1.5 - (s * 0.2); // Pencil lines are usually thinner
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
144
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
145 // 2. Realistic Jitter: Actually return a random small number
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
146 const jitter = () => (Math.random() - 0.5) * 1.5;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
147
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
148 ctx.moveTo(x1 + jitter(), y1 + jitter());
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
149
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
150 if (isCurve) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
151 const cpY = y1 + (y2 - y1) / 2;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
152 ctx.bezierCurveTo(
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
153 x1 + jitter(), cpY + jitter(),
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
154 x2 + jitter(), cpY + jitter(),
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
155 x2 + jitter(), y2 + jitter()
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
156 );
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
157 } else {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
158 ctx.lineTo(x2 + jitter(), y2 + jitter());
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
159 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
160
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
161 ctx.stroke();
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
162 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
163 ctx.restore();
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
164 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
165
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
166 // --- Main Component ---
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
167
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
168 interface GraphProps {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
169 data: GraphData | null;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
170 loading?: boolean;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
171 hasMore?: boolean;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
172 onLoadMore?: () => void;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
173 onCommitClick?: (node: string) => void;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
174 maxRows?: number;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
175 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
176
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
177 const Graph = ({ data, loading, hasMore, onLoadMore, onCommitClick, maxRows }: GraphProps) => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
178 const canvasRef = useRef<HTMLCanvasElement>(null);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
179 const containerRef = useRef<HTMLDivElement>(null);
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
180 const [assetError, setAssetError] = useState<string | null>(null);
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
181
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
182 const changesets = useMemo(() =>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
183 maxRows && data?.changesets ? data.changesets.slice(0, maxRows) : data?.changesets || [], [data, maxRows]);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
184
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
185 useEffect(() => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
186 const canvas = canvasRef.current;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
187 if (!canvas || !changesets.length) return;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
188
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
189 const ctx = canvas.getContext('2d');
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
190 if (!ctx) return;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
191
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
192 let cancelled = false;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
193 let pencilPattern: CanvasPattern | null = null;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
194 let loadedAssets = 0;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
195 const pencilImage = new Image();
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
196 const pandaImage = new Image();
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
197 const dpr = window.devicePixelRatio || 1;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
198 const maxCol = Math.max(...changesets.map(cs => cs.col), 0);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
199 const canvasWidth = (maxCol + 2) * colWidth;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
200
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
201 // Scale for high-DPI screens
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
202 canvas.width = canvasWidth * dpr;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
203 canvas.height = changesets.length * rowHeight * dpr;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
204 canvas.style.width = `${canvasWidth}px`;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
205 canvas.style.height = `${changesets.length * rowHeight}px`;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
206 ctx.scale(dpr, dpr);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
207 ctx.clearRect(0, 0, canvasWidth, changesets.length * rowHeight);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
208
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
209 const getX = (col: number) => (col + 1) * colWidth;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
210 const getY = (row: number) => (row * rowHeight) + (rowHeight / 2);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
211
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
212 const renderCanvas = () => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
213 if (!pencilPattern) return; // Don't draw if the pattern isn't ready
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
214
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
215 // Pass 1: Draw Connecting Edges
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
216 changesets.forEach((cs, i) => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
217 if (!cs.edges) return;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
218 cs.edges.forEach(edge => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
219 const sX = getX(edge.col), sY = getY(i);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
220 const eX = getX(edge.nextcol), eY = getY(i + 1);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
221
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
222 drawPencilLine(ctx, sX, sY, eX, eY, pencilPattern, edge.col !== edge.nextcol);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
223 });
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
224 });
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
225
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
226 // Pass 2: Draw Commit Nodes
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
227 changesets.forEach((cs, i) => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
228 const x = getX(cs.col), y = getY(i);
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
229 ctx.drawImage(pandaImage, x-10, y-10, 20, 20);
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 };
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
232
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
233 const handleAssetLoad = () => {
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
234 loadedAssets++;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
235 if (loadedAssets !== 2 || cancelled) return;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
236 pencilPattern = ctx.createPattern(pencilImage, "repeat");
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
237 renderCanvas();
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
238 };
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
239
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
240 const handleAssetError = () => {
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
241 if (!cancelled) setAssetError('Unable to load the graph artwork.');
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
242 };
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
243
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
244 setAssetError(null);
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
245 pencilImage.onload = handleAssetLoad;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
246 pencilImage.onerror = handleAssetError;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
247 pandaImage.onload = handleAssetLoad;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
248 pandaImage.onerror = handleAssetError;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
249 pencilImage.src = "/pencil_lines.png";
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
250 pandaImage.src = "/panda.png";
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
251
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
252 return () => {
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
253 cancelled = true;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
254 pencilImage.onload = null;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
255 pencilImage.onerror = null;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
256 pandaImage.onload = null;
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
257 pandaImage.onerror = null;
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
258 };
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
259 }, [changesets]);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
260
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
261 // Handle Infinite Scroll via Intersection Observer
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
262 useEffect(() => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
263 if (!onLoadMore || !hasMore) return;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
264 const observer = new IntersectionObserver((entries) => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
265 if (entries[0].isIntersecting && !loading) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
266 onLoadMore();
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
267 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
268 }, { threshold: 0.1 });
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
269
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
270 const sentinel = document.getElementById('infinite-scroll-sentinel');
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
271 if (sentinel) observer.observe(sentinel);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
272 return () => observer.disconnect();
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
273 }, [onLoadMore, hasMore, loading]);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
274
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
275 return (
224
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 221
diff changeset
276 <div className="graph-container">
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
277 {assetError && <div className="error-message">{assetError}</div>}
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
278 <div
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
279 ref={containerRef}
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
280 className="graph-wrapper"
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
281 >
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
282 <div className="graph-canvas-column">
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
283 <canvas ref={canvasRef} style={{ display: 'block' }} />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
284 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
285
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
286 <div className="graph-details-column">
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
287 {changesets.map((cs) => (
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
288 <button
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
289 type="button"
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
290 key={cs.node}
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
291 className="graph-row"
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
292 onClick={() => onCommitClick?.(cs.node)}
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
293 aria-label={`Open changeset ${cs.node.substring(0, 12)}: ${cs.desc}`}
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
294 >
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
295 <span className="graph-row-meta">
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
296 <span className="graph-hash">{cs.node.substring(0, 12)}</span>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
297 <span className="graph-user">{cs.user.split(' <')[0]}</span>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
298 {cs.branch && <span className="graph-branch">{cs.branch}</span>}
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
299 </span>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
300 <span className="graph-desc">{cs.desc}</span>
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
301 </button>
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
302 ))}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
303 <div id="infinite-scroll-sentinel" style={{ height: '50px' }} />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
304 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
305 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
306
221
ce7f4400c2de [hg-web] Harden forge and add changeset UI
MrJuneJune <me@mrjunejune.com>
parents: 194
diff changeset
307 {loading && <div className="graph-loading-row">Loading repository history...</div>}
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
308 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
309 );
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
310 };
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
311
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
312 export { Graph, useGraphData };
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
313 export type { GraphData, Changeset, UseGraphDataResult };