annotate hg-web/src/components/directory-browser.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, useRef, useCallback } from 'react';
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
2 import createMarkdownModule from 'markdown_converter/markdown_to_html_wasm/markdown_to_html_bin.js';
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
3 import hljs from 'third_party/highlight/highlight.min.js';
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
4
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
5 // --- ICONS (served as static files) ---
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
6 const ICONS = {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
7 folder: "/icons/folder.png",
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
8 file: "/icons/file.svg",
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
9 close: "/icons/close.png"
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
10 };
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
11
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
12 const API_BASE = '/api/repo';
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
13
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
14 // File extensions that should be displayed as code
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
15 const CODE_EXTENSIONS = new Set([
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
16 'js', 'jsx', 'ts', 'tsx', 'py', 'rb', 'java', 'c', 'cpp', 'h', 'hpp',
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
17 'cs', 'go', 'rs', 'swift', 'kt', 'scala', 'php', 'pl', 'sh', 'bash',
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
18 'zsh', 'fish', 'ps1', 'bat', 'cmd', 'html', 'htm', 'css', 'scss',
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
19 'sass', 'less', 'json', 'xml', 'yaml', 'yml', 'toml', 'ini', 'cfg',
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
20 'conf', 'md', 'markdown', 'txt', 'log', 'sql', 'graphql', 'vue',
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
21 'svelte', 'astro', 'prisma', 'dockerfile', 'makefile', 'cmake',
225
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 224
diff changeset
22 'gradle', 'pom', 'lock', 'gitignore', 'env', 'example', 'sample',
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 224
diff changeset
23 'bzl', 'bazel'
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 224
diff changeset
24 ]);
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 224
diff changeset
25
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 224
diff changeset
26 const BAZEL_FILENAMES = new Set([
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 224
diff changeset
27 'build', 'build.bazel', 'module.bazel', 'workspace', 'workspace.bazel'
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
28 ]);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
29
224
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
30 type StaticPreviewKind = 'image' | 'video' | 'audio' | 'pdf';
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
31
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
32 const IMAGE_EXTENSIONS = new Set([
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
33 'png', 'jpg', 'jpeg', 'gif', 'webp', 'avif', 'bmp', 'ico', 'svg'
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
34 ]);
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
35 const VIDEO_EXTENSIONS = new Set(['mp4', 'm4v', 'webm', 'mov', 'ogv']);
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
36 const AUDIO_EXTENSIONS = new Set(['mp3', 'wav', 'ogg', 'oga', 'flac', 'm4a', 'aac']);
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
37
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
38 // Prefetch cache
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
39 const prefetchCache = new Map<string, Promise<any>>();
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
40
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
41 function isCodeFile(filename: string): boolean {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
42 const ext = filename.split('.').pop()?.toLowerCase() || '';
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
43 const basename = filename.toLowerCase();
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
44 return CODE_EXTENSIONS.has(ext) ||
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
45 CODE_EXTENSIONS.has(basename) ||
225
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 224
diff changeset
46 BAZEL_FILENAMES.has(basename) ||
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
47 basename === 'dockerfile' ||
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
48 basename === 'makefile' ||
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
49 basename.startsWith('.');
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
50 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
51
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
52 function isMarkdownFile(filename: string): boolean {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
53 const ext = filename.split('.').pop()?.toLowerCase() || '';
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
54 return ext === 'md' || ext === 'markdown';
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
55 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
56
224
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
57 function getStaticPreviewKind(filename: string): StaticPreviewKind | null {
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
58 const ext = filename.split('.').pop()?.toLowerCase() || '';
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
59 if (IMAGE_EXTENSIONS.has(ext)) return 'image';
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
60 if (VIDEO_EXTENSIONS.has(ext)) return 'video';
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
61 if (AUDIO_EXTENSIONS.has(ext)) return 'audio';
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
62 if (ext === 'pdf') return 'pdf';
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
63 return null;
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
64 }
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
65
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
66 function prefetchDirectory(path: string): void {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
67 const cacheKey = `dir:${path}`;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
68 if (prefetchCache.has(cacheKey)) return;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
69
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
70 const url = path
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
71 ? `${API_BASE}/list?path=${encodeURIComponent(path)}`
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
72 : `${API_BASE}/list`;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
73
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
74 prefetchCache.set(cacheKey, fetch(url).then(r => r.json()).catch(() => null));
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
75 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
76
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
77 function prefetchFile(path: string): void {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
78 const cacheKey = `file:${path}`;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
79 if (prefetchCache.has(cacheKey)) return;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
80
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
81 prefetchCache.set(cacheKey,
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
82 fetch(`${API_BASE}/file?path=${encodeURIComponent(path)}`)
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
83 .then(r => r.ok ? r.text() : null)
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
84 .catch(() => null)
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
85 );
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
86 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
87
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
88 async function getCachedFile(path: string): Promise<string | null> {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
89 const cacheKey = `file:${path}`;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
90 if (prefetchCache.has(cacheKey)) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
91 return prefetchCache.get(cacheKey);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
92 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
93 prefetchFile(path);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
94 return prefetchCache.get(cacheKey)!;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
95 }
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 * Component: Breadcrumb
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
99 */
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
100 function Breadcrumb({ currentPath, onNavigate }: { currentPath: string; onNavigate: (path: string) => void }) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
101 if (!currentPath) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
102 return (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
103 <nav className="breadcrumb">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
104 <span className="nav-item active">root</span>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
105 </nav>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
106 );
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
107 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
108
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
109 const parts = currentPath.split('/').filter(p => p);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
110 const crumbs = parts.map((part, index) => ({
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
111 name: part,
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
112 fullPath: parts.slice(0, index + 1).join('/')
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
113 }));
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
114
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
115 return (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
116 <nav className="breadcrumb">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
117 <a
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
118 href="#"
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
119 onClick={(e) => { e.preventDefault(); onNavigate(''); }}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
120 title="Go to Root"
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
121 >
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
122 root
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
123 </a>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
124 {crumbs.map((crumb, index) => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
125 const isLast = index === crumbs.length - 1;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
126 return (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
127 <React.Fragment key={crumb.fullPath}>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
128 <span className="separator">/</span>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
129 {isLast ? (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
130 <span className="nav-item active">{crumb.name}</span>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
131 ) : (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
132 <a
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
133 href="#"
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
134 onClick={(e) => { e.preventDefault(); onNavigate(crumb.fullPath); }}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
135 >
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
136 {crumb.name}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
137 </a>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
138 )}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
139 </React.Fragment>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
140 );
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
141 })}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
142 </nav>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
143 );
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
144 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
145
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
146 /**
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
147 * Component: FileViewer
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
148 * Shows file content inline with syntax highlighting
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
149 */
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
150 function FileViewer({ filePath, onClose }: { filePath: string; onClose: () => void }) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
151 const [content, setContent] = useState<string | null>(null);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
152 const [loading, setLoading] = useState(true);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
153 const codeRef = useRef<HTMLElement>(null);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
154
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
155 const filename = filePath.split('/').pop() || filePath;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
156
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
157 useEffect(() => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
158 setLoading(true);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
159 getCachedFile(filePath).then((text) => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
160 setContent(text);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
161 setLoading(false);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
162 });
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
163 }, [filePath]);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
164
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
165 useEffect(() => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
166 if (content && codeRef.current) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
167 hljs.highlightElement(codeRef.current);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
168 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
169 }, [content]);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
170
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
171 useEffect(() => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
172 const handleKeyDown = (e: KeyboardEvent) => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
173 if (e.key === 'Escape') onClose();
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
174 };
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
175 window.addEventListener('keydown', handleKeyDown);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
176 return () => window.removeEventListener('keydown', handleKeyDown);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
177 }, [onClose]);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
178
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
179 const getLanguage = () => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
180 const ext = filename.split('.').pop()?.toLowerCase() || '';
225
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 224
diff changeset
181 const basename = filename.toLowerCase();
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 224
diff changeset
182 if (BAZEL_FILENAMES.has(basename) || ext === 'bzl' || ext === 'bazel') {
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 224
diff changeset
183 return 'python';
70de0c80d093 [hg-web] Add side-by-side changeset diffs
MrJuneJune <me@mrjunejune.com>
parents: 224
diff changeset
184 }
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
185 const langMap: Record<string, string> = {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
186 js: 'javascript', jsx: 'javascript', ts: 'typescript', tsx: 'typescript',
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
187 py: 'python', rb: 'ruby', rs: 'rust', go: 'go', java: 'java',
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
188 c: 'c', cpp: 'cpp', h: 'c', hpp: 'cpp', cs: 'csharp',
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
189 sh: 'bash', bash: 'bash', zsh: 'bash', fish: 'bash',
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
190 json: 'json', yaml: 'yaml', yml: 'yaml', toml: 'toml',
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
191 html: 'html', htm: 'html', css: 'css', scss: 'scss', sass: 'scss',
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
192 sql: 'sql', md: 'markdown', markdown: 'markdown', xml: 'xml',
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
193 dockerfile: 'dockerfile', makefile: 'makefile'
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
194 };
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
195 return langMap[ext] || 'plaintext';
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
196 };
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
197
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
198 const addLineNumbers = (text: string) => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
199 const lines = text.split('\n');
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
200 return lines.map((_, i) => i + 1).join('\n');
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
201 };
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
202
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
203 return (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
204 <div className="file-viewer-overlay" onClick={onClose}>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
205 <div className="file-viewer" onClick={(e) => e.stopPropagation()}>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
206 <div className="file-viewer-header">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
207 <span className="file-viewer-title">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
208 <img src={ICONS.file} alt="" style={{ width: 16, height: 16 }} />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
209 {filename}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
210 </span>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
211 <button className="file-viewer-close" onClick={onClose} title="Close (Esc)">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
212 <img className="icon-invert" src={ICONS.close} alt="Close" />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
213 </button>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
214 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
215 <div className="file-viewer-content">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
216 {loading ? (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
217 <div className="file-viewer-loading">Loading...</div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
218 ) : content ? (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
219 <pre style={{ display: 'flex' }}>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
220 <span className="file-viewer-line-numbers">{addLineNumbers(content)}</span>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
221 <code ref={codeRef} className={`language-${getLanguage()}`}>{content}</code>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
222 </pre>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
223 ) : (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
224 <div className="file-viewer-loading">Unable to load file</div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
225 )}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
226 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
227 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
228 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
229 );
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 /**
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
233 * Component: MarkdownViewerModal
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
234 */
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
235 function MarkdownViewerModal({ filePath, onClose }: { filePath: string; onClose: () => void }) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
236 const [content, setContent] = useState<string | null>(null);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
237 const [loading, setLoading] = useState(true);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
238 const contentRef = useRef<HTMLDivElement>(null);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
239 const moduleRef = useRef<any>(null);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
240 const [wasmReady, setWasmReady] = useState(false);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
241
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
242 const filename = filePath.split('/').pop() || filePath;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
243
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
244 useEffect(() => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
245 createMarkdownModule().then((Module: any) => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
246 moduleRef.current = Module;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
247 setWasmReady(true);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
248 });
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
249 }, []);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
250
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
251 useEffect(() => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
252 setLoading(true);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
253 getCachedFile(filePath).then((text) => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
254 setContent(text);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
255 setLoading(false);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
256 });
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
257 }, [filePath]);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
258
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
259 useEffect(() => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
260 if (!content || !wasmReady || !contentRef.current || !moduleRef.current) return;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
261
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
262 const Module = moduleRef.current;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
263 const markdownToHtmlPtr = Module.cwrap('markdown_to_html', 'number', ['string']);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
264 const markdownFree = Module.cwrap('markdown_free', null, ['number']);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
265
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
266 const ptr = markdownToHtmlPtr(content);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
267 const html = Module.UTF8ToString(ptr);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
268 markdownFree(ptr);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
269 contentRef.current.innerHTML = html;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
270 }, [content, wasmReady]);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
271
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
272 useEffect(() => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
273 const handleKeyDown = (e: KeyboardEvent) => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
274 if (e.key === 'Escape') onClose();
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
275 };
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
276 window.addEventListener('keydown', handleKeyDown);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
277 return () => window.removeEventListener('keydown', handleKeyDown);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
278 }, [onClose]);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
279
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
280 return (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
281 <div className="file-viewer-overlay" onClick={onClose}>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
282 <div className="file-viewer" onClick={(e) => e.stopPropagation()}>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
283 <div className="file-viewer-header">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
284 <span className="file-viewer-title">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
285 <img src={ICONS.file} alt="" style={{ width: 16, height: 16 }} />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
286 {filename}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
287 </span>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
288 <button className="file-viewer-close" onClick={onClose} title="Close (Esc)">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
289 <img className="icon-invert" src={ICONS.close} alt="Close" />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
290 </button>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
291 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
292 <div className="file-viewer-content">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
293 {loading || !wasmReady ? (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
294 <div className="file-viewer-loading">Loading...</div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
295 ) : content ? (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
296 <div className="readme-content" ref={contentRef} />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
297 ) : (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
298 <div className="file-viewer-loading">Unable to load file</div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
299 )}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
300 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
301 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
302 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
303 );
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
304 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
305
224
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
306 function StaticFileViewer({ filePath, onClose }: { filePath: string; onClose: () => void }) {
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
307 const filename = filePath.split('/').pop() || filePath;
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
308 const previewKind = getStaticPreviewKind(filename);
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
309 const fileUrl = `${API_BASE}/file?path=${encodeURIComponent(filePath)}`;
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
310 const [failed, setFailed] = useState(false);
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
311
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
312 useEffect(() => {
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
313 const handleKeyDown = (event: KeyboardEvent) => {
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
314 if (event.key === 'Escape') onClose();
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
315 };
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
316 window.addEventListener('keydown', handleKeyDown);
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
317 return () => window.removeEventListener('keydown', handleKeyDown);
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
318 }, [onClose]);
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
319
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
320 return (
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
321 <div className="file-viewer-overlay" onClick={onClose}>
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
322 <div
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
323 className="file-viewer static-file-viewer"
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
324 role="dialog"
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
325 aria-modal="true"
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
326 aria-label={`Preview ${filename}`}
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
327 onClick={(event) => event.stopPropagation()}
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
328 >
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
329 <div className="file-viewer-header">
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
330 <span className="file-viewer-title">
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
331 <img src={ICONS.file} alt="" style={{ width: 16, height: 16 }} />
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
332 {filename}
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
333 </span>
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
334 <span className="file-viewer-actions">
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
335 <a href={fileUrl} download={filename}>Download</a>
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
336 <button className="file-viewer-close" onClick={onClose} title="Close (Esc)">
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
337 <img className="icon-invert" src={ICONS.close} alt="Close" />
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
338 </button>
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
339 </span>
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
340 </div>
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
341 <div className="file-viewer-content static-file-preview">
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
342 {failed && <div className="error-message">Unable to preview this file.</div>}
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
343 {!failed && previewKind === 'image' && (
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
344 <img
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
345 className="static-file-image"
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
346 src={fileUrl}
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
347 alt={filename}
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
348 onError={() => setFailed(true)}
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
349 />
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
350 )}
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
351 {!failed && previewKind === 'video' && (
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
352 <video
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
353 className="static-file-video"
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
354 src={fileUrl}
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
355 controls
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
356 onError={() => setFailed(true)}
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
357 />
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
358 )}
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
359 {!failed && previewKind === 'audio' && (
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
360 <audio
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
361 className="static-file-audio"
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
362 src={fileUrl}
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
363 controls
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
364 onError={() => setFailed(true)}
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
365 />
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
366 )}
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
367 {!failed && previewKind === 'pdf' && (
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
368 <iframe
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
369 className="static-file-pdf"
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
370 src={fileUrl}
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
371 title={filename}
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
372 onError={() => setFailed(true)}
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
373 />
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
374 )}
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
375 </div>
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
376 </div>
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
377 </div>
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
378 );
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
379 }
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
380
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
381 /**
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
382 * Component: FileList
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
383 */
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
384 function FileList({ directories, files, onNavigate, onOpenFile }: {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
385 directories: any[];
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
386 files: any[];
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
387 onNavigate: (path: string) => void;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
388 onOpenFile: (path: string) => void;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
389 }) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
390 const isEmpty = directories.length === 0 && files.length === 0;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
391
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
392 if (isEmpty) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
393 return (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
394 <div className="file-list-container">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
395 <div className="empty-state">This directory is empty.</div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
396 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
397 );
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
398 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
399
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
400 return (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
401 <div className="file-list-container">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
402 <div className="file-header">Files</div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
403 <div id="fileListBody">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
404 {directories.map((dir) => (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
405 <FileRow
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
406 key={dir.abspath}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
407 item={dir}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
408 iconUrl={ICONS.folder}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
409 isDir={true}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
410 onNavigate={onNavigate}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
411 onOpenFile={onOpenFile}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
412 />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
413 ))}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
414 {files.map((file) => (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
415 <FileRow
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
416 key={file.abspath}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
417 item={file}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
418 iconUrl={ICONS.file}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
419 isDir={false}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
420 onNavigate={onNavigate}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
421 onOpenFile={onOpenFile}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
422 />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
423 ))}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
424 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
425 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
426 );
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
427 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
428
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
429 /**
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
430 * Component: FileRow
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
431 */
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
432 function FileRow({ item, iconUrl, isDir, onNavigate, onOpenFile }: {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
433 item: { abspath: string; basename: string };
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
434 iconUrl: string;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
435 isDir: boolean;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
436 onNavigate: (path: string) => void;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
437 onOpenFile: (path: string) => void;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
438 }) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
439 const handleClick = (e: React.MouseEvent) => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
440 e.preventDefault();
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
441 if (isDir) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
442 onNavigate(item.abspath);
224
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
443 } else if (isCodeFile(item.basename) || getStaticPreviewKind(item.basename)) {
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
444 onOpenFile(item.abspath);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
445 } else {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
446 window.open(`/api/repo/file?path=${encodeURIComponent(item.abspath)}`, '_blank');
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
447 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
448 };
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
449
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
450 const handleMouseEnter = () => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
451 if (isDir) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
452 prefetchDirectory(item.abspath);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
453 } else if (isCodeFile(item.basename)) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
454 prefetchFile(item.abspath);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
455 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
456 };
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
457
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
458 const href = isDir
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
459 ? `#`
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
460 : `/api/repo/file?path=${encodeURIComponent(item.abspath)}`;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
461
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
462 return (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
463 <div className="file-row" onMouseEnter={handleMouseEnter}>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
464 <span className="icon">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
465 <img className="icon-invert" src={iconUrl} alt={isDir ? "Directory" : "File"} />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
466 </span>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
467 <span className="name">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
468 <a href={href} onClick={handleClick}>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
469 {item.basename}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
470 </a>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
471 </span>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
472 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
473 );
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
474 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
475
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
476 /**
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
477 * Component: ReadmeViewer
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
478 */
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
479 function ReadmeViewer({ content }: { content: string | null }) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
480 const contentRef = useRef<HTMLDivElement>(null);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
481 const moduleRef = useRef<any>(null);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
482 const [wasmReady, setWasmReady] = useState(false);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
483
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
484 useEffect(() => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
485 createMarkdownModule().then((Module: any) => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
486 moduleRef.current = Module;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
487 setWasmReady(true);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
488 });
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
489 }, []);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
490
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
491 useEffect(() => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
492 if (!content || !wasmReady || !contentRef.current || !moduleRef.current) return;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
493
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
494 const Module = moduleRef.current;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
495 const markdownToHtmlPtr = Module.cwrap('markdown_to_html', 'number', ['string']);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
496 const markdownFree = Module.cwrap('markdown_free', null, ['number']);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
497
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
498 const ptr = markdownToHtmlPtr(content);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
499 const html = Module.UTF8ToString(ptr);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
500 markdownFree(ptr);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
501 contentRef.current.innerHTML = html;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
502 }, [content, wasmReady]);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
503
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
504 if (!content) return null;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
505
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
506 return (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
507 <div className="readme-section">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
508 <div className="readme-header">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
509 <img className="icon-invert" src={ICONS.file} width="16" alt="" style={{ opacity: 0.5 }} />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
510 README.md
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
511 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
512 <div className="readme-content" ref={contentRef}>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
513 {!wasmReady && 'Loading...'}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
514 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
515 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
516 );
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
517 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
518
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
519 /**
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
520 * Directory Browser Component (no header/footer - for embedding in app)
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
521 */
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
522 interface DirectoryBrowserProps {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
523 initialPath?: string;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
524 onPathChange?: (path: string) => void;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
525 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
526
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
527 function DirectoryBrowser({ initialPath = '', onPathChange }: DirectoryBrowserProps) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
528 const [currentPath, setCurrentPath] = useState(initialPath);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
529 const [content, setContent] = useState<{ files: any[]; directories: any[] }>({ files: [], directories: [] });
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
530 const [readme, setReadme] = useState<string | null>(null);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
531 const [error, setError] = useState<string | null>(null);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
532 const [loading, setLoading] = useState(false);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
533 const [viewingFile, setViewingFile] = useState<string | null>(null);
223
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
534 const requestGeneration = useRef(0);
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
535
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
536 // Sync with initialPath prop
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
537 useEffect(() => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
538 setCurrentPath(initialPath);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
539 }, [initialPath]);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
540
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
541 useEffect(() => {
223
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
542 const generation = ++requestGeneration.current;
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
543 fetchDirectory(currentPath, generation);
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
544 fetchReadme(currentPath, generation);
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
545 }, [currentPath]);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
546
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
547 const navigate = useCallback((path: string) => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
548 setCurrentPath(path);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
549 onPathChange?.(path);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
550 }, [onPathChange]);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
551
223
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
552 const fetchDirectory = async (path: string, generation: number) => {
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
553 setLoading(true);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
554 setError(null);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
555 try {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
556 const cacheKey = `dir:${path}`;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
557 let data;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
558 if (prefetchCache.has(cacheKey)) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
559 data = await prefetchCache.get(cacheKey);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
560 prefetchCache.delete(cacheKey);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
561 } else {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
562 const url = path
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
563 ? `${API_BASE}/list?path=${encodeURIComponent(path)}`
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
564 : `${API_BASE}/list`;
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
565 const response = await fetch(url);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
566 if (response.ok) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
567 data = await response.json();
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
568 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
569 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
570
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
571 if (data?.error) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
572 throw new Error(data.error);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
573 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
574
223
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
575 if (generation === requestGeneration.current) {
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
576 setContent({
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
577 files: data?.files || [],
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
578 directories: data?.directories || []
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
579 });
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
580 }
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
581 } catch (err: any) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
582 console.error('Error loading directory:', err);
223
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
583 if (generation === requestGeneration.current) setError(err.message);
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
584 } finally {
223
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
585 if (generation === requestGeneration.current) setLoading(false);
193
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
223
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
589 const fetchReadme = async (path: string, generation: number) => {
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
590 if (generation === requestGeneration.current) setReadme(null);
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
591 try {
223
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
592 const url = path
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
593 ? `${API_BASE}/readme?path=${encodeURIComponent(path)}`
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
594 : `${API_BASE}/readme`;
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
595 const response = await fetch(url);
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
596 if (response.ok) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
597 const text = await response.text();
223
0e7b9464248d [hg-web] Add browser route regression coverage
MrJuneJune <me@mrjunejune.com>
parents: 193
diff changeset
598 if (generation === requestGeneration.current) setReadme(text || null);
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
599 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
600 } catch (err) {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
601 // Readme is optional
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
602 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
603 };
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
604
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
605 const handleOpenFile = useCallback((path: string) => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
606 setViewingFile(path);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
607 }, []);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
608
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
609 const handleCloseFile = useCallback(() => {
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
610 setViewingFile(null);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
611 }, []);
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
612
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
613 return (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
614 <>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
615 <Breadcrumb currentPath={currentPath} onNavigate={navigate} />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
616
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
617 {error && <div className="error-message">Error: {error}</div>}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
618
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
619 {loading ? (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
620 <div className="file-list-container">
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
621 <div className="loading-state">Loading files...</div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
622 </div>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
623 ) : (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
624 <>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
625 <FileList
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
626 directories={content.directories}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
627 files={content.files}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
628 onNavigate={navigate}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
629 onOpenFile={handleOpenFile}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
630 />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
631 <ReadmeViewer content={readme} />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
632 </>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
633 )}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
634
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
635 {/* File Viewer Modal */}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
636 {viewingFile && (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
637 isMarkdownFile(viewingFile) ? (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
638 <MarkdownViewerModal filePath={viewingFile} onClose={handleCloseFile} />
224
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
639 ) : getStaticPreviewKind(viewingFile) ? (
3007ef5fc0ed [hg-web] Simplify visuals and preview static files
MrJuneJune <me@mrjunejune.com>
parents: 223
diff changeset
640 <StaticFileViewer filePath={viewingFile} onClose={handleCloseFile} />
193
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
641 ) : (
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
642 <FileViewer filePath={viewingFile} onClose={handleCloseFile} />
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
643 )
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
644 )}
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
645 </>
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
646 );
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
647 }
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
648
9f4429c49733 [HgWeb] Making progress....
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
649 export { DirectoryBrowser };