Mercurial
annotate hg-web/src/repo-browser.tsx @ 191:a06710325c30 hg-web
[HgWeb] Fully working copy.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Sat, 24 Jan 2026 21:51:51 -0800 |
| parents | a2725419f988 |
| children |
| rev | line source |
|---|---|
| 191 | 1 import React, { useState, useEffect, useRef, useCallback } from 'react'; |
|
190
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
2 import createMarkdownModule from 'markdown_converter/markdown_to_html_wasm/markdown_to_html_bin.js'; |
| 191 | 3 import hljs from 'third_party/highlight/highlight.min.js'; |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
4 |
| 191 | 5 // --- ICONS (served as static files) --- |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
6 const ICONS = { |
| 191 | 7 folder: "/icons/folder.png", |
| 8 file: "/icons/file.svg", | |
| 9 home: "/icons/home.png", | |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
10 repo: "/public/epi_all_colors.svg", |
| 191 | 11 close: "/icons/close.png" |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
12 }; |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
13 |
| 191 | 14 // SVG Icons for theme toggle |
| 15 const SunIcon = ({ color = "currentColor" }: { color?: string }) => ( | |
| 16 <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> | |
| 17 <circle cx="12" cy="12" r="5"/> | |
| 18 <line x1="12" y1="1" x2="12" y2="3"/> | |
| 19 <line x1="12" y1="21" x2="12" y2="23"/> | |
| 20 <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/> | |
| 21 <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/> | |
| 22 <line x1="1" y1="12" x2="3" y2="12"/> | |
| 23 <line x1="21" y1="12" x2="23" y2="12"/> | |
| 24 <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/> | |
| 25 <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/> | |
| 26 </svg> | |
| 27 ); | |
| 28 | |
| 29 const MoonIcon = ({ color = "currentColor" }: { color?: string }) => ( | |
| 30 <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> | |
| 31 <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/> | |
| 32 </svg> | |
| 33 ); | |
| 34 | |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
35 const API_BASE = '/api/repo'; |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
36 |
| 191 | 37 // File extensions that should be displayed as code |
| 38 const CODE_EXTENSIONS = new Set([ | |
| 39 'js', 'jsx', 'ts', 'tsx', 'py', 'rb', 'java', 'c', 'cpp', 'h', 'hpp', | |
| 40 'cs', 'go', 'rs', 'swift', 'kt', 'scala', 'php', 'pl', 'sh', 'bash', | |
| 41 'zsh', 'fish', 'ps1', 'bat', 'cmd', 'html', 'htm', 'css', 'scss', | |
| 42 'sass', 'less', 'json', 'xml', 'yaml', 'yml', 'toml', 'ini', 'cfg', | |
| 43 'conf', 'md', 'markdown', 'txt', 'log', 'sql', 'graphql', 'vue', | |
| 44 'svelte', 'astro', 'prisma', 'dockerfile', 'makefile', 'cmake', | |
| 45 'gradle', 'pom', 'lock', 'gitignore', 'env', 'example', 'sample' | |
| 46 ]); | |
| 47 | |
| 48 // Prefetch cache | |
| 49 const prefetchCache = new Map<string, Promise<any>>(); | |
| 50 | |
| 51 function isCodeFile(filename: string): boolean { | |
| 52 const ext = filename.split('.').pop()?.toLowerCase() || ''; | |
| 53 const basename = filename.toLowerCase(); | |
| 54 return CODE_EXTENSIONS.has(ext) || | |
| 55 CODE_EXTENSIONS.has(basename) || | |
| 56 basename === 'dockerfile' || | |
| 57 basename === 'makefile' || | |
| 58 basename.startsWith('.'); | |
| 59 } | |
| 60 | |
| 61 function isMarkdownFile(filename: string): boolean { | |
| 62 const ext = filename.split('.').pop()?.toLowerCase() || ''; | |
| 63 return ext === 'md' || ext === 'markdown'; | |
| 64 } | |
| 65 | |
| 66 function prefetchDirectory(path: string): void { | |
| 67 const cacheKey = `dir:${path}`; | |
| 68 if (prefetchCache.has(cacheKey)) return; | |
| 69 | |
| 70 const url = path | |
| 71 ? `${API_BASE}/list?path=${encodeURIComponent(path)}` | |
| 72 : `${API_BASE}/list`; | |
| 73 | |
| 74 prefetchCache.set(cacheKey, fetch(url).then(r => r.json()).catch(() => null)); | |
| 75 } | |
| 76 | |
| 77 function prefetchFile(path: string): void { | |
| 78 const cacheKey = `file:${path}`; | |
| 79 if (prefetchCache.has(cacheKey)) return; | |
| 80 | |
| 81 prefetchCache.set(cacheKey, | |
| 82 fetch(`${API_BASE}/file?path=${encodeURIComponent(path)}`) | |
| 83 .then(r => r.ok ? r.text() : null) | |
| 84 .catch(() => null) | |
| 85 ); | |
| 86 } | |
| 87 | |
| 88 async function getCachedFile(path: string): Promise<string | null> { | |
| 89 const cacheKey = `file:${path}`; | |
| 90 if (prefetchCache.has(cacheKey)) { | |
| 91 return prefetchCache.get(cacheKey); | |
| 92 } | |
| 93 prefetchFile(path); | |
| 94 return prefetchCache.get(cacheKey)!; | |
| 95 } | |
| 96 | |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
97 /** |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
98 * Component: Styles |
| 191 | 99 * Injected CSS for the polished look with dark/light mode support |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
100 */ |
| 191 | 101 const GlobalStyles = ({ isDark }: { isDark: boolean }) => ( |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
102 <style>{` |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
103 :root { |
| 191 | 104 --bg-color: ${isDark ? '#0d1117' : '#ffffff'}; |
| 105 --bg-subtle: ${isDark ? '#161b22' : '#f6f8fa'}; | |
| 106 --bg-code: ${isDark ? '#1c2128' : '#f6f8fa'}; | |
| 107 --border-color: ${isDark ? '#30363d' : '#d0d7de'}; | |
| 108 --accent-color: ${isDark ? '#58a6ff' : '#0969da'}; | |
| 109 --text-primary: ${isDark ? '#e6edf3' : '#1f2328'}; | |
| 110 --text-secondary: ${isDark ? '#8b949e' : '#656d76'}; | |
| 111 --hover-color: ${isDark ? '#1c2128' : '#f3f4f6'}; | |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
112 --radius: 6px; |
| 191 | 113 --code-bg: ${isDark ? '#161b22' : '#ffffff'}; |
| 114 } | |
| 115 | |
| 116 body { | |
| 117 background: var(--bg-color); | |
| 118 transition: background 0.2s; | |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
119 } |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
120 |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
121 .repo-container { |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
122 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
123 max-width: 980px; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
124 margin: 40px auto; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
125 color: var(--text-primary); |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
126 padding: 0 20px; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
127 } |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
128 |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
129 /* Header */ |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
130 .header { |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
131 display: flex; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
132 align-items: center; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
133 margin-bottom: 20px; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
134 gap: 15px; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
135 } |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
136 .header-icon { width: 32px; height: 32px; opacity: 0.8; } |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
137 .header h1 { margin: 0; font-size: 24px; font-weight: 600; } |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
138 .description { color: var(--text-secondary); margin: 0; font-size: 14px; } |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
139 |
| 191 | 140 /* Theme Toggle */ |
| 141 .theme-toggle { | |
| 142 margin-left: auto; | |
| 143 background: var(--bg-subtle); | |
| 144 border: 1px solid var(--border-color); | |
| 145 border-radius: var(--radius); | |
| 146 padding: 8px 12px; | |
| 147 cursor: pointer; | |
| 148 display: flex; | |
| 149 align-items: center; | |
| 150 gap: 6px; | |
| 151 color: var(--text-secondary); | |
| 152 font-size: 13px; | |
| 153 transition: all 0.2s; | |
| 154 } | |
| 155 .theme-toggle:hover { | |
| 156 background: var(--hover-color); | |
| 157 color: var(--text-primary); | |
| 158 } | |
| 159 .theme-toggle svg { | |
| 160 flex-shrink: 0; | |
| 161 } | |
| 162 | |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
163 /* Clone Box */ |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
164 .clone-box { |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
165 background: var(--bg-subtle); |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
166 border: 1px solid var(--border-color); |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
167 border-radius: var(--radius); |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
168 padding: 12px 16px; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
169 margin-bottom: 24px; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
170 display: flex; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
171 justify-content: space-between; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
172 align-items: center; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
173 } |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
174 .clone-label { font-weight: 600; font-size: 13px; margin-right: 10px; color: var(--text-primary); } |
| 191 | 175 .clone-url { |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
176 font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace; |
| 191 | 177 background: var(--bg-color); |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
178 border: 1px solid var(--border-color); |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
179 padding: 4px 8px; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
180 border-radius: 4px; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
181 font-size: 12px; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
182 color: var(--text-secondary); |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
183 flex-grow: 1; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
184 } |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
185 |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
186 /* Breadcrumb */ |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
187 #breadcrumb { |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
188 display: flex; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
189 align-items: center; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
190 font-size: 14px; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
191 margin-bottom: 16px; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
192 color: var(--text-secondary); |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
193 padding: 8px 0; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
194 } |
| 191 | 195 #breadcrumb a { |
| 196 color: var(--accent-color); | |
| 197 text-decoration: none; | |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
198 border-radius: 4px; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
199 padding: 2px 6px; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
200 } |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
201 #breadcrumb a:hover { background: var(--bg-subtle); text-decoration: underline; } |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
202 #breadcrumb .separator { margin: 0 4px; color: var(--text-secondary); opacity: 0.5; } |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
203 #breadcrumb .nav-item.active { font-weight: 600; color: var(--text-primary); padding: 2px 6px;} |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
204 |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
205 /* File List Table Structure */ |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
206 .file-list-container { |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
207 border: 1px solid var(--border-color); |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
208 border-radius: var(--radius); |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
209 overflow: hidden; |
| 191 | 210 background: var(--bg-color); |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
211 } |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
212 .file-header { |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
213 background: var(--bg-subtle); |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
214 border-bottom: 1px solid var(--border-color); |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
215 padding: 12px 16px; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
216 font-size: 13px; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
217 font-weight: 600; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
218 color: var(--text-secondary); |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
219 } |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
220 .empty-state { padding: 40px; text-align: center; color: var(--text-secondary); } |
| 191 | 221 .error-message { |
| 222 padding: 15px; border: 1px solid ${isDark ? '#f8514966' : '#ffdce0'}; | |
| 223 background: ${isDark ? '#f8514926' : '#ffebe9'}; color: ${isDark ? '#f85149' : '#cf222e'}; | |
| 224 border-radius: var(--radius); margin-bottom: 20px; | |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
225 } |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
226 |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
227 /* File Row */ |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
228 .file-row { |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
229 display: flex; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
230 align-items: center; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
231 padding: 10px 16px; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
232 border-bottom: 1px solid var(--border-color); |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
233 transition: background 0.1s; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
234 } |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
235 .file-row:last-child { border-bottom: none; } |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
236 .file-row:hover { background: var(--hover-color); } |
| 191 | 237 |
| 238 .file-row .icon img { | |
| 239 width: 20px; | |
| 240 height: 20px; | |
| 241 vertical-align: middle; | |
| 242 margin-right: 12px; | |
| 243 filter: ${isDark ? 'invert(0.8)' : 'none'}; | |
| 244 } | |
| 245 .file-row .name a { | |
| 246 color: var(--text-primary); | |
| 247 text-decoration: none; | |
| 248 font-size: 14px; | |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
249 } |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
250 .file-row .name a:hover { color: var(--accent-color); text-decoration: underline; } |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
251 |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
252 /* Readme */ |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
253 #readmeSection { margin-top: 32px; border: 1px solid var(--border-color); border-radius: var(--radius); } |
| 191 | 254 .readme-header { |
| 255 background: var(--bg-subtle); | |
| 256 padding: 10px 16px; | |
| 257 font-size: 12px; font-weight: 600; | |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
258 border-bottom: 1px solid var(--border-color); |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
259 display: flex; align-items: center; gap: 8px; |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
260 } |
| 191 | 261 .readme-header img { |
| 262 filter: ${isDark ? 'invert(0.7)' : 'none'}; | |
| 263 } | |
| 264 #readmeContent { padding: 32px; background: var(--bg-color); overflow-x: auto; color: var(--text-primary); } | |
| 265 | |
| 266 /* File Viewer */ | |
| 267 .file-viewer-overlay { | |
| 268 position: fixed; | |
| 269 inset: 0; | |
| 270 background: ${isDark ? 'rgba(0,0,0,0.7)' : 'rgba(0,0,0,0.5)'}; | |
| 271 display: flex; | |
| 272 justify-content: center; | |
| 273 align-items: center; | |
| 274 z-index: 1000; | |
| 275 padding: 20px; | |
| 276 } | |
| 277 .file-viewer { | |
| 278 background: var(--bg-color); | |
| 279 border: 1px solid var(--border-color); | |
| 280 border-radius: var(--radius); | |
| 281 width: 100%; | |
| 282 max-width: 900px; | |
| 283 max-height: 90vh; | |
| 284 display: flex; | |
| 285 flex-direction: column; | |
| 286 box-shadow: 0 8px 32px rgba(0,0,0,0.3); | |
| 287 } | |
| 288 .file-viewer-header { | |
| 289 display: flex; | |
| 290 align-items: center; | |
| 291 justify-content: space-between; | |
| 292 padding: 12px 16px; | |
| 293 background: var(--bg-subtle); | |
| 294 border-bottom: 1px solid var(--border-color); | |
| 295 border-radius: var(--radius) var(--radius) 0 0; | |
| 296 } | |
| 297 .file-viewer-title { | |
| 298 font-weight: 600; | |
| 299 font-size: 14px; | |
| 300 color: var(--text-primary); | |
| 301 display: flex; | |
| 302 align-items: center; | |
| 303 gap: 8px; | |
| 304 } | |
| 305 .file-viewer-close { | |
| 306 background: transparent; | |
| 307 border: none; | |
| 308 cursor: pointer; | |
| 309 padding: 4px; | |
| 310 border-radius: 4px; | |
| 311 display: flex; | |
| 312 align-items: center; | |
| 313 justify-content: center; | |
| 314 } | |
| 315 .file-viewer-close:hover { | |
| 316 background: var(--hover-color); | |
| 317 } | |
| 318 .file-viewer-close img { | |
| 319 width: 16px; | |
| 320 height: 16px; | |
| 321 filter: ${isDark ? 'invert(0.7)' : 'none'}; | |
| 322 opacity: 0.7; | |
| 323 } | |
| 324 .file-viewer-content { | |
| 325 overflow: auto; | |
| 326 flex: 1; | |
| 327 } | |
| 328 .file-viewer-content pre { | |
| 329 margin: 0; | |
| 330 padding: 16px; | |
| 331 background: var(--code-bg); | |
| 332 font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace; | |
| 333 font-size: 13px; | |
| 334 line-height: 1.5; | |
| 335 overflow-x: auto; | |
| 336 } | |
| 337 .file-viewer-content code { | |
| 338 background: transparent; | |
| 339 padding: 0; | |
| 340 } | |
| 341 .file-viewer-loading { | |
| 342 padding: 40px; | |
| 343 text-align: center; | |
| 344 color: var(--text-secondary); | |
| 345 } | |
| 346 .file-viewer-line-numbers { | |
| 347 display: inline-block; | |
| 348 user-select: none; | |
| 349 text-align: right; | |
| 350 padding-right: 16px; | |
| 351 margin-right: 16px; | |
| 352 border-right: 1px solid var(--border-color); | |
| 353 color: var(--text-secondary); | |
| 354 opacity: 0.5; | |
| 355 } | |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
356 `}</style> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
357 ); |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
358 |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
359 /** |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
360 * Component: Breadcrumb |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
361 */ |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
362 function Breadcrumb({ currentPath, onNavigate }) { |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
363 if (!currentPath) { |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
364 return ( |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
365 <nav id="breadcrumb"> |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
366 <span className="nav-item active">root</span> |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
367 </nav> |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
368 ); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
369 } |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
370 |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
371 const parts = currentPath.split('/').filter(p => p); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
372 const crumbs = parts.map((part, index) => ({ |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
373 name: part, |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
374 fullPath: parts.slice(0, index + 1).join('/') |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
375 })); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
376 |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
377 return ( |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
378 <nav id="breadcrumb"> |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
379 <a |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
380 href="/" |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
381 onClick={(e) => { e.preventDefault(); onNavigate(''); }} |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
382 title="Go to Root" |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
383 > |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
384 root |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
385 </a> |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
386 {crumbs.map((crumb, index) => { |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
387 const isLast = index === crumbs.length - 1; |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
388 return ( |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
389 <React.Fragment key={crumb.fullPath}> |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
390 <span className="separator">/</span> |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
391 {isLast ? ( |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
392 <span className="nav-item active">{crumb.name}</span> |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
393 ) : ( |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
394 <a |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
395 href={`?path=${encodeURIComponent(crumb.fullPath)}`} |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
396 onClick={(e) => { e.preventDefault(); onNavigate(crumb.fullPath); }} |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
397 > |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
398 {crumb.name} |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
399 </a> |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
400 )} |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
401 </React.Fragment> |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
402 ); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
403 })} |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
404 </nav> |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
405 ); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
406 } |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
407 |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
408 /** |
| 191 | 409 * Component: FileViewer |
| 410 * Shows file content inline with syntax highlighting | |
| 411 */ | |
| 412 function FileViewer({ filePath, onClose }: { filePath: string; onClose: () => void }) { | |
| 413 const [content, setContent] = useState<string | null>(null); | |
| 414 const [loading, setLoading] = useState(true); | |
| 415 const codeRef = useRef<HTMLElement>(null); | |
| 416 | |
| 417 const filename = filePath.split('/').pop() || filePath; | |
| 418 | |
| 419 useEffect(() => { | |
| 420 setLoading(true); | |
| 421 getCachedFile(filePath).then((text) => { | |
| 422 setContent(text); | |
| 423 setLoading(false); | |
| 424 }); | |
| 425 }, [filePath]); | |
| 426 | |
| 427 useEffect(() => { | |
| 428 if (content && codeRef.current) { | |
| 429 hljs.highlightElement(codeRef.current); | |
| 430 } | |
| 431 }, [content]); | |
| 432 | |
| 433 // Close on escape key | |
| 434 useEffect(() => { | |
| 435 const handleKeyDown = (e: KeyboardEvent) => { | |
| 436 if (e.key === 'Escape') onClose(); | |
| 437 }; | |
| 438 window.addEventListener('keydown', handleKeyDown); | |
| 439 return () => window.removeEventListener('keydown', handleKeyDown); | |
| 440 }, [onClose]); | |
| 441 | |
| 442 // Get language from file extension for highlight.js | |
| 443 const getLanguage = () => { | |
| 444 const ext = filename.split('.').pop()?.toLowerCase() || ''; | |
| 445 const langMap: Record<string, string> = { | |
| 446 js: 'javascript', jsx: 'javascript', ts: 'typescript', tsx: 'typescript', | |
| 447 py: 'python', rb: 'ruby', rs: 'rust', go: 'go', java: 'java', | |
| 448 c: 'c', cpp: 'cpp', h: 'c', hpp: 'cpp', cs: 'csharp', | |
| 449 sh: 'bash', bash: 'bash', zsh: 'bash', fish: 'bash', | |
| 450 json: 'json', yaml: 'yaml', yml: 'yaml', toml: 'toml', | |
| 451 html: 'html', htm: 'html', css: 'css', scss: 'scss', sass: 'scss', | |
| 452 sql: 'sql', md: 'markdown', markdown: 'markdown', xml: 'xml', | |
| 453 dockerfile: 'dockerfile', makefile: 'makefile' | |
| 454 }; | |
| 455 return langMap[ext] || 'plaintext'; | |
| 456 }; | |
| 457 | |
| 458 const addLineNumbers = (text: string) => { | |
| 459 const lines = text.split('\n'); | |
| 460 return lines.map((_, i) => i + 1).join('\n'); | |
| 461 }; | |
| 462 | |
| 463 return ( | |
| 464 <div className="file-viewer-overlay" onClick={onClose}> | |
| 465 <div className="file-viewer" onClick={(e) => e.stopPropagation()}> | |
| 466 <div className="file-viewer-header"> | |
| 467 <span className="file-viewer-title"> | |
| 468 <img src={ICONS.file} alt="" style={{ width: 16, height: 16 }} /> | |
| 469 {filename} | |
| 470 </span> | |
| 471 <button className="file-viewer-close" onClick={onClose} title="Close (Esc)"> | |
| 472 <img src={ICONS.close} alt="Close" /> | |
| 473 </button> | |
| 474 </div> | |
| 475 <div className="file-viewer-content"> | |
| 476 {loading ? ( | |
| 477 <div className="file-viewer-loading">Loading...</div> | |
| 478 ) : content ? ( | |
| 479 <pre style={{ display: 'flex' }}> | |
| 480 <span className="file-viewer-line-numbers">{addLineNumbers(content)}</span> | |
| 481 <code ref={codeRef} className={`language-${getLanguage()}`}>{content}</code> | |
| 482 </pre> | |
| 483 ) : ( | |
| 484 <div className="file-viewer-loading">Unable to load file</div> | |
| 485 )} | |
| 486 </div> | |
| 487 </div> | |
| 488 </div> | |
| 489 ); | |
| 490 } | |
| 491 | |
| 492 /** | |
| 493 * Component: MarkdownViewerModal | |
| 494 * Shows markdown content rendered in a modal | |
| 495 */ | |
| 496 function MarkdownViewerModal({ filePath, onClose }: { filePath: string; onClose: () => void }) { | |
| 497 const [content, setContent] = useState<string | null>(null); | |
| 498 const [loading, setLoading] = useState(true); | |
| 499 const contentRef = useRef<HTMLDivElement>(null); | |
| 500 const moduleRef = useRef<any>(null); | |
| 501 const [wasmReady, setWasmReady] = useState(false); | |
| 502 | |
| 503 const filename = filePath.split('/').pop() || filePath; | |
| 504 | |
| 505 useEffect(() => { | |
| 506 createMarkdownModule().then((Module: any) => { | |
| 507 moduleRef.current = Module; | |
| 508 setWasmReady(true); | |
| 509 }); | |
| 510 }, []); | |
| 511 | |
| 512 useEffect(() => { | |
| 513 setLoading(true); | |
| 514 getCachedFile(filePath).then((text) => { | |
| 515 setContent(text); | |
| 516 setLoading(false); | |
| 517 }); | |
| 518 }, [filePath]); | |
| 519 | |
| 520 useEffect(() => { | |
| 521 if (!content || !wasmReady || !contentRef.current || !moduleRef.current) return; | |
| 522 | |
| 523 const Module = moduleRef.current; | |
| 524 const markdownToHtmlPtr = Module.cwrap('markdown_to_html', 'number', ['string']); | |
| 525 const markdownFree = Module.cwrap('markdown_free', null, ['number']); | |
| 526 | |
| 527 const ptr = markdownToHtmlPtr(content); | |
| 528 const html = Module.UTF8ToString(ptr); | |
| 529 markdownFree(ptr); | |
| 530 contentRef.current.innerHTML = html; | |
| 531 }, [content, wasmReady]); | |
| 532 | |
| 533 // Close on escape key | |
| 534 useEffect(() => { | |
| 535 const handleKeyDown = (e: KeyboardEvent) => { | |
| 536 if (e.key === 'Escape') onClose(); | |
| 537 }; | |
| 538 window.addEventListener('keydown', handleKeyDown); | |
| 539 return () => window.removeEventListener('keydown', handleKeyDown); | |
| 540 }, [onClose]); | |
| 541 | |
| 542 return ( | |
| 543 <div className="file-viewer-overlay" onClick={onClose}> | |
| 544 <div className="file-viewer" onClick={(e) => e.stopPropagation()}> | |
| 545 <div className="file-viewer-header"> | |
| 546 <span className="file-viewer-title"> | |
| 547 <img src={ICONS.file} alt="" style={{ width: 16, height: 16 }} /> | |
| 548 {filename} | |
| 549 </span> | |
| 550 <button className="file-viewer-close" onClick={onClose} title="Close (Esc)"> | |
| 551 <img src={ICONS.close} alt="Close" /> | |
| 552 </button> | |
| 553 </div> | |
| 554 <div className="file-viewer-content"> | |
| 555 {loading || !wasmReady ? ( | |
| 556 <div className="file-viewer-loading">Loading...</div> | |
| 557 ) : content ? ( | |
| 558 <div id="readmeContent" ref={contentRef} style={{ padding: 32 }} /> | |
| 559 ) : ( | |
| 560 <div className="file-viewer-loading">Unable to load file</div> | |
| 561 )} | |
| 562 </div> | |
| 563 </div> | |
| 564 </div> | |
| 565 ); | |
| 566 } | |
| 567 | |
| 568 /** | |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
569 * Component: FileList |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
570 */ |
| 191 | 571 function FileList({ directories, files, onNavigate, onOpenFile }: { |
| 572 directories: any[]; | |
| 573 files: any[]; | |
| 574 onNavigate: (path: string) => void; | |
| 575 onOpenFile: (path: string) => void; | |
| 576 }) { | |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
577 const isEmpty = directories.length === 0 && files.length === 0; |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
578 |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
579 if (isEmpty) { |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
580 return ( |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
581 <div className="file-list-container"> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
582 <div className="empty-state">This directory is empty.</div> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
583 </div> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
584 ); |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
585 } |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
586 |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
587 return ( |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
588 <div className="file-list-container"> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
589 {/* Optional header row like GitHub */} |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
590 <div className="file-header"> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
591 Files |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
592 </div> |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
593 |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
594 <div id="fileListBody"> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
595 {directories.map((dir) => ( |
| 191 | 596 <FileRow |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
597 key={dir.abspath} |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
598 item={dir} |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
599 iconUrl={ICONS.folder} |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
600 isDir={true} |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
601 onNavigate={onNavigate} |
| 191 | 602 onOpenFile={onOpenFile} |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
603 /> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
604 ))} |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
605 |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
606 {files.map((file) => ( |
| 191 | 607 <FileRow |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
608 key={file.abspath} |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
609 item={file} |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
610 iconUrl={ICONS.file} |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
611 isDir={false} |
| 191 | 612 onNavigate={onNavigate} |
| 613 onOpenFile={onOpenFile} | |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
614 /> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
615 ))} |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
616 </div> |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
617 </div> |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
618 ); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
619 } |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
620 |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
621 /** |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
622 * Component: FileRow |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
623 */ |
| 191 | 624 function FileRow({ item, iconUrl, isDir, onNavigate, onOpenFile }: { |
| 625 item: { abspath: string; basename: string }; | |
| 626 iconUrl: string; | |
| 627 isDir: boolean; | |
| 628 onNavigate: (path: string) => void; | |
| 629 onOpenFile: (path: string) => void; | |
| 630 }) { | |
| 631 const handleClick = (e: React.MouseEvent) => { | |
| 632 e.preventDefault(); | |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
633 if (isDir) { |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
634 onNavigate(item.abspath); |
| 191 | 635 } else if (isCodeFile(item.basename)) { |
| 636 onOpenFile(item.abspath); | |
| 637 } else { | |
| 638 // For non-code files, open in new tab | |
| 639 window.open(`/api/repo/file?path=${encodeURIComponent(item.abspath)}`, '_blank'); | |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
640 } |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
641 }; |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
642 |
| 191 | 643 const handleMouseEnter = () => { |
| 644 // Prefetch on hover | |
| 645 if (isDir) { | |
| 646 prefetchDirectory(item.abspath); | |
| 647 } else if (isCodeFile(item.basename)) { | |
| 648 prefetchFile(item.abspath); | |
| 649 } | |
| 650 }; | |
| 651 | |
| 652 const href = isDir | |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
653 ? `?path=${encodeURIComponent(item.abspath)}` |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
654 : `/api/repo/file?path=${encodeURIComponent(item.abspath)}`; |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
655 |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
656 return ( |
| 191 | 657 <div className="file-row" onMouseEnter={handleMouseEnter}> |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
658 <span className="icon"> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
659 <img src={iconUrl} alt={isDir ? "Directory" : "File"} /> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
660 </span> |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
661 <span className="name"> |
| 191 | 662 <a href={href} onClick={handleClick}> |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
663 {item.basename} |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
664 </a> |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
665 </span> |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
666 </div> |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
667 ); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
668 } |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
669 |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
670 /** |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
671 * Component: ReadmeViewer |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
672 */ |
|
190
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
673 function ReadmeViewer({ content }: { content: string | null }) { |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
674 const contentRef = useRef<HTMLDivElement>(null); |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
675 const moduleRef = useRef<any>(null); |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
676 const [wasmReady, setWasmReady] = useState(false); |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
677 |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
678 useEffect(() => { |
| 191 | 679 createMarkdownModule().then((Module: any) => { |
|
190
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
680 moduleRef.current = Module; |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
681 setWasmReady(true); |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
682 }); |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
683 }, []); |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
684 |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
685 useEffect(() => { |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
686 if (!content || !wasmReady || !contentRef.current || !moduleRef.current) return; |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
687 |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
688 const Module = moduleRef.current; |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
689 const markdownToHtmlPtr = Module.cwrap('markdown_to_html', 'number', ['string']); |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
690 const markdownFree = Module.cwrap('markdown_free', null, ['number']); |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
691 |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
692 const ptr = markdownToHtmlPtr(content); |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
693 const html = Module.UTF8ToString(ptr); |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
694 markdownFree(ptr); |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
695 contentRef.current.innerHTML = html; |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
696 }, [content, wasmReady]); |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
697 |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
698 if (!content) return null; |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
699 |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
700 return ( |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
701 <div id="readmeSection"> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
702 <div className="readme-header"> |
| 191 | 703 <img src={ICONS.file} width="16" alt="" style={{opacity:0.5}} /> |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
704 README.md |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
705 </div> |
|
190
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
706 <div id="readmeContent" ref={contentRef}> |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
707 {!wasmReady && 'Loading...'} |
|
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
708 </div> |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
709 </div> |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
710 ); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
711 } |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
712 |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
713 /** |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
714 * Main Application Component |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
715 */ |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
716 function RepoBrowser() { |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
717 const [currentPath, setCurrentPath] = useState(getCurrentPath()); |
| 191 | 718 const [content, setContent] = useState<{ files: any[]; directories: any[] }>({ files: [], directories: [] }); |
| 719 const [readme, setReadme] = useState<string | null>(null); | |
| 720 const [error, setError] = useState<string | null>(null); | |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
721 const [loading, setLoading] = useState(false); |
| 191 | 722 const [isDarkMode, setIsDarkMode] = useState(() => { |
| 723 // Check localStorage or system preference | |
| 724 const saved = localStorage.getItem('theme'); | |
| 725 if (saved) return saved === 'dark'; | |
| 726 return window.matchMedia('(prefers-color-scheme: dark)').matches; | |
| 727 }); | |
| 728 const [viewingFile, setViewingFile] = useState<string | null>(null); | |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
729 |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
730 function getCurrentPath() { |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
731 const params = new URLSearchParams(window.location.search); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
732 return params.get('path') || ''; |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
733 } |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
734 |
| 191 | 735 // Persist theme preference |
| 736 useEffect(() => { | |
| 737 localStorage.setItem('theme', isDarkMode ? 'dark' : 'light'); | |
| 738 }, [isDarkMode]); | |
| 739 | |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
740 useEffect(() => { |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
741 const handlePopState = () => setCurrentPath(getCurrentPath()); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
742 window.addEventListener('popstate', handlePopState); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
743 return () => window.removeEventListener('popstate', handlePopState); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
744 }, []); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
745 |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
746 useEffect(() => { |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
747 fetchDirectory(currentPath); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
748 fetchReadme(currentPath); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
749 }, [currentPath]); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
750 |
| 191 | 751 const navigate = (path: string) => { |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
752 const newUrl = path ? `?path=${encodeURIComponent(path)}` : '/'; |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
753 window.history.pushState({ path }, '', newUrl); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
754 setCurrentPath(path); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
755 }; |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
756 |
| 191 | 757 const fetchDirectory = async (path: string) => { |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
758 setLoading(true); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
759 setError(null); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
760 try { |
| 191 | 761 // Check prefetch cache first |
| 762 const cacheKey = `dir:${path}`; | |
|
190
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
763 let data; |
| 191 | 764 if (prefetchCache.has(cacheKey)) { |
| 765 data = await prefetchCache.get(cacheKey); | |
| 766 prefetchCache.delete(cacheKey); // Clear after use for fresh data next time | |
| 767 } else { | |
| 768 const url = path | |
| 769 ? `${API_BASE}/list?path=${encodeURIComponent(path)}` | |
| 770 : `${API_BASE}/list`; | |
| 771 const response = await fetch(url); | |
| 772 if (response.ok) { | |
| 773 data = await response.json(); | |
| 774 } | |
| 775 } | |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
776 |
| 191 | 777 if (data?.error) { |
|
190
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
778 throw new Error(data.error); |
| 191 | 779 } |
| 780 | |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
781 setContent({ |
| 191 | 782 files: data?.files || [], |
| 783 directories: data?.directories || [] | |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
784 }); |
| 191 | 785 } catch (err: any) { |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
786 console.error('Error loading directory:', err); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
787 setError(err.message); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
788 } finally { |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
789 setLoading(false); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
790 } |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
791 }; |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
792 |
| 191 | 793 const fetchReadme = async (path: string) => { |
| 794 setReadme(null); | |
|
190
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
795 const readmePath = path ? `${path}/README.md` : 'README.md'; |
| 191 | 796 try { |
| 797 const response = await fetch(`${API_BASE}/file?path=${encodeURIComponent(readmePath)}`); | |
| 798 if (response.ok) { | |
| 799 const text = await response.text(); | |
| 800 setReadme(text); | |
| 801 } | |
| 802 } catch (err) { | |
| 803 // Readme is optional, ignore errors | |
|
190
a2725419f988
Updated so that bun builds will with already existing js files.
MrJuneJune <me@mrjunejune.com>
parents:
188
diff
changeset
|
804 } |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
805 }; |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
806 |
| 191 | 807 const handleOpenFile = useCallback((path: string) => { |
| 808 setViewingFile(path); | |
| 809 }, []); | |
| 810 | |
| 811 const handleCloseFile = useCallback(() => { | |
| 812 setViewingFile(null); | |
| 813 }, []); | |
| 814 | |
| 815 const toggleTheme = () => { | |
| 816 setIsDarkMode(prev => !prev); | |
| 817 }; | |
| 818 | |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
819 return ( |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
820 <> |
| 191 | 821 <GlobalStyles isDark={isDarkMode} /> |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
822 <div className="repo-container"> |
| 191 | 823 |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
824 {/* Header */} |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
825 <div className="header"> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
826 <img src={ICONS.repo} alt="Repo" className="header-icon" /> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
827 <div> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
828 <h1>Zenbu Repository</h1> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
829 <p className="description">Browse and manage the mercurial codebase</p> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
830 </div> |
| 191 | 831 <button className="theme-toggle" onClick={toggleTheme} title={isDarkMode ? 'Switch to light mode' : 'Switch to dark mode'}> |
| 832 {isDarkMode ? <SunIcon color="#f0c674" /> : <MoonIcon color="#6e7681" />} | |
| 833 {isDarkMode ? 'Light' : 'Dark'} | |
| 834 </button> | |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
835 </div> |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
836 |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
837 {/* Clone Bar */} |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
838 <div className="clone-box"> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
839 <div style={{display:'flex', alignItems:'center', width:'100%'}}> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
840 <span className="clone-label">Clone HTTPS</span> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
841 <code className="clone-url">hg clone http://zenbu.babocoder.com/repo</code> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
842 </div> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
843 </div> |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
844 |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
845 {/* Navigation & Content */} |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
846 <Breadcrumb currentPath={currentPath} onNavigate={navigate} /> |
| 191 | 847 |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
848 {error && <div className="error-message">Error: {error}</div>} |
| 191 | 849 |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
850 {loading ? ( |
| 191 | 851 <div className="file-list-container" style={{padding: '40px', textAlign: 'center', color: 'var(--text-secondary)'}}> |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
852 Loading files... |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
853 </div> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
854 ) : ( |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
855 <> |
| 191 | 856 <FileList |
| 857 directories={content.directories} | |
| 858 files={content.files} | |
| 859 onNavigate={navigate} | |
| 860 onOpenFile={handleOpenFile} | |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
861 /> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
862 <ReadmeViewer content={readme} /> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
863 </> |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
864 )} |
|
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
865 </div> |
| 191 | 866 |
| 867 {/* File Viewer Modal */} | |
| 868 {viewingFile && ( | |
| 869 isMarkdownFile(viewingFile) ? ( | |
| 870 <MarkdownViewerModal filePath={viewingFile} onClose={handleCloseFile} /> | |
| 871 ) : ( | |
| 872 <FileViewer filePath={viewingFile} onClose={handleCloseFile} /> | |
| 873 ) | |
| 874 )} | |
|
176
fed99fc04e12
[HgWeb] Problem with the emscript lol
MrJuneJune <me@mrjunejune.com>
parents:
175
diff
changeset
|
875 </> |
|
175
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
876 ); |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
877 } |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
878 |
|
71ad34a8bc9a
[HgWeb] Can stream hg response now. Added react page for hg web since we use json anyway.
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
879 export { RepoBrowser }; |