Mercurial
comparison hg-web/src/repo-browser.tsx @ 190:a2725419f988 hg-web
Updated so that bun builds will with already existing js files.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Sat, 24 Jan 2026 21:06:42 -0800 |
| parents | 32ce881452fa |
| children | a06710325c30 |
comparison
equal
deleted
inserted
replaced
| 188:32ce881452fa | 190:a2725419f988 |
|---|---|
| 1 import React, { useState, useEffect } from 'react'; | 1 import React, { useState, useEffect, useRef } from 'react'; |
| 2 import renderMarkdown from './markdown_to_html_bin.js'; | 2 import createMarkdownModule from 'markdown_converter/markdown_to_html_wasm/markdown_to_html_bin.js'; |
| 3 | |
| 4 console.log(renderMarkdown); | |
| 5 | 3 |
| 6 // --- ICONS (Using CDN Links) --- | 4 // --- ICONS (Using CDN Links) --- |
| 7 const ICONS = { | 5 const ICONS = { |
| 8 folder: "https://cdn-icons-png.flaticon.com/512/11471/11471391.png", | 6 folder: "https://cdn-icons-png.flaticon.com/512/11471/11471391.png", |
| 9 file: "https://raw.githubusercontent.com/PKief/vscode-material-icon-theme/main/icons/document.svg", | 7 file: "https://raw.githubusercontent.com/PKief/vscode-material-icon-theme/main/icons/document.svg", |
| 271 } | 269 } |
| 272 | 270 |
| 273 /** | 271 /** |
| 274 * Component: ReadmeViewer | 272 * Component: ReadmeViewer |
| 275 */ | 273 */ |
| 276 function ReadmeViewer({ content }) { | 274 function ReadmeViewer({ content }: { content: string | null }) { |
| 275 const contentRef = useRef<HTMLDivElement>(null); | |
| 276 const moduleRef = useRef<any>(null); | |
| 277 const [wasmReady, setWasmReady] = useState(false); | |
| 278 | |
| 279 useEffect(() => { | |
| 280 createMarkdownModule().then((Module) => { | |
| 281 moduleRef.current = Module; | |
| 282 setWasmReady(true); | |
| 283 }); | |
| 284 }, []); | |
| 285 | |
| 286 useEffect(() => { | |
| 287 if (!content || !wasmReady || !contentRef.current || !moduleRef.current) return; | |
| 288 | |
| 289 const Module = moduleRef.current; | |
| 290 const markdownToHtmlPtr = Module.cwrap('markdown_to_html', 'number', ['string']); | |
| 291 const markdownFree = Module.cwrap('markdown_free', null, ['number']); | |
| 292 | |
| 293 const ptr = markdownToHtmlPtr(content); | |
| 294 const html = Module.UTF8ToString(ptr); | |
| 295 markdownFree(ptr); | |
| 296 contentRef.current.innerHTML = html; | |
| 297 }, [content, wasmReady]); | |
| 298 | |
| 277 if (!content) return null; | 299 if (!content) return null; |
| 278 | 300 |
| 279 return ( | 301 return ( |
| 280 <div id="readmeSection"> | 302 <div id="readmeSection"> |
| 281 <div className="readme-header"> | 303 <div className="readme-header"> |
| 282 <img src="https://img.icons8.com/material-outlined/24/000000/menu--v1.png" width="16" alt="" style={{opacity:0.5}} /> | 304 <img src="https://img.icons8.com/material-outlined/24/000000/menu--v1.png" width="16" alt="" style={{opacity:0.5}} /> |
| 283 README.md | 305 README.md |
| 284 </div> | 306 </div> |
| 285 <div id="readmeContent"></div> | 307 <div id="readmeContent" ref={contentRef}> |
| 308 {!wasmReady && 'Loading...'} | |
| 309 </div> | |
| 286 </div> | 310 </div> |
| 287 ); | 311 ); |
| 288 } | 312 } |
| 289 | 313 |
| 290 /** | 314 /** |
| 326 const url = path | 350 const url = path |
| 327 ? `${API_BASE}/list?path=${encodeURIComponent(path)}` | 351 ? `${API_BASE}/list?path=${encodeURIComponent(path)}` |
| 328 : `${API_BASE}/list`; | 352 : `${API_BASE}/list`; |
| 329 | 353 |
| 330 const response = await fetch(url); | 354 const response = await fetch(url); |
| 331 const data = await response.json(); | 355 let data; |
| 332 | 356 if (response.ok) |
| 333 if (data.error) throw new Error(data.error); | 357 data = await response.json(); |
| 358 | |
| 359 if (data.error) | |
| 360 throw new Error(data.error); | |
| 334 | 361 |
| 335 setContent({ | 362 setContent({ |
| 336 files: data.files || [], | 363 files: data.files || [], |
| 337 directories: data.directories || [] | 364 directories: data.directories || [] |
| 338 }); | 365 }); |
| 343 setLoading(false); | 370 setLoading(false); |
| 344 } | 371 } |
| 345 }; | 372 }; |
| 346 | 373 |
| 347 const fetchReadme = async (path) => { | 374 const fetchReadme = async (path) => { |
| 348 setReadme(null); | 375 const readmePath = path ? `${path}/README.md` : 'README.md'; |
| 349 try { | 376 const response = await fetch(`${API_BASE}/file?path=${encodeURIComponent(readmePath)}`); |
| 350 const readmePath = path ? `${path}/README.md` : 'README.md'; | 377 console.log(response); |
| 351 const response = await fetch(`${API_BASE}/readme?path=${encodeURIComponent(readmePath)}`); | 378 if (response.ok) |
| 352 | 379 { |
| 353 if (response.ok) { | 380 const text = await response.text(); |
| 354 const text = await response.text(); | 381 setReadme(text); |
| 355 setReadme(text); | 382 } |
| 356 } | |
| 357 } catch (err) { /* Silently fail */ } | |
| 358 }; | 383 }; |
| 359 | 384 |
| 360 return ( | 385 return ( |
| 361 <> | 386 <> |
| 362 <GlobalStyles /> | 387 <GlobalStyles /> |