annotate hg-web/src/components/directory-browser.tsx @ 270:358dd5950985

sync production config on every deploy Always install the ignored repository .config into /etc/mrjunejune before promoting the new bundle. Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Fri, 07 Aug 2026 13:16:47 -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 };