diff hg-web/src/index.html @ 104:2301aeb7503b

[Hg Web] Super simple mercurial server.
author June Park <parkjune1995@gmail.com>
date Sat, 03 Jan 2026 10:20:45 -0800
parents
children ffb764d2fcc5
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hg-web/src/index.html	Sat Jan 03 10:20:45 2026 -0800
@@ -0,0 +1,175 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Zenbu Repository</title>
+    <link rel="stylesheet" href="/base.css">
+    <link rel="stylesheet" href="/index.css">
+</head>
+<body>
+    <main>
+        <div class="header">
+            <h1>Zenbu Repository</h1>
+            <p class="description">Browse and clone this mercurial repository</p>
+        </div>
+
+        <div class="clone-info">
+            <strong>Clone this repository:</strong><br>
+            <code>hg clone http://zenbu.babocoder.com</code>
+        </div>
+
+        <div class="breadcrumb" id="breadcrumb"></div>
+
+        <div class="file-list" id="fileList"></div>
+
+        <div class="readme-section" id="readmeSection" style="display: none;">
+            <h2>README</h2>
+            <div class="readme-content" id="readmeContent"></div>
+        </div>
+
+        <div class="empty-state" id="emptyState" style="display: none;">
+            <p>No files found in this directory</p>
+        </div>
+    </main>
+
+    <script src="/markdown_to_html.js"></script>
+    <script>
+        const API_BASE = '/api/repo';
+
+        // Get current path from URL
+        function getCurrentPath() {
+            const params = new URLSearchParams(window.location.search);
+            return params.get('path') || '';
+        }
+
+        // Update URL without reloading
+        function updateURL(path) {
+            const url = path ? `?path=${encodeURIComponent(path)}` : '/';
+            window.history.pushState({path}, '', url);
+        }
+
+        // Render breadcrumb
+        function renderBreadcrumb(path) {
+            const breadcrumb = document.getElementById('breadcrumb');
+            if (!path) {
+                breadcrumb.innerHTML = '<a href="/">Root</a>';
+                return;
+            }
+
+            const parts = path.split('/').filter(p => p);
+            let currentPath = '';
+            let html = '<a href="/">Root</a>';
+
+            parts.forEach((part, index) => {
+                currentPath += (currentPath ? '/' : '') + part;
+                html += ` <span>/</span> `;
+                if (index === parts.length - 1) {
+                    html += `<span>${part}</span>`;
+                } else {
+                    html += `<a href="?path=${encodeURIComponent(currentPath)}">${part}</a>`;
+                }
+            });
+
+            breadcrumb.innerHTML = html;
+        }
+
+        // Render file list
+        function renderFiles(files) {
+            const fileList = document.getElementById('fileList');
+            const emptyState = document.getElementById('emptyState');
+
+            if (!files || files.length === 0) {
+                fileList.style.display = 'none';
+                emptyState.style.display = 'block';
+                return;
+            }
+
+            emptyState.style.display = 'none';
+            fileList.style.display = 'block';
+
+            // Sort: directories first, then files, alphabetically
+            files.sort((a, b) => {
+                if (a.type !== b.type) {
+                    return a.type === 'directory' ? -1 : 1;
+                }
+                return a.name.localeCompare(b.name);
+            });
+
+            let html = '';
+            files.forEach(file => {
+                const icon = file.type === 'directory' ? '📁' : '📄';
+                const className = file.type;
+                const href = file.type === 'directory'
+                    ? `?path=${encodeURIComponent(file.path)}`
+                    : `/api/repo/file?path=${encodeURIComponent(file.path)}`;
+                const target = file.type === 'directory' ? '' : 'target="_blank"';
+
+                html += `
+                    <div class="file-item ${className}">
+                        <span class="icon">${icon}</span>
+                        <span class="name">
+                            <a href="${href}" ${target}>${file.name}</a>
+                        </span>
+                    </div>
+                `;
+            });
+
+            fileList.innerHTML = html;
+        }
+
+        // Load and render README
+        async function loadReadme(path) {
+            const readmeSection = document.getElementById('readmeSection');
+            const readmeContent = document.getElementById('readmeContent');
+
+            try {
+                const readmePath = path ? `${path}/README.md` : 'README.md';
+                const response = await fetch(`/api/repo/readme?path=${encodeURIComponent(readmePath)}`);
+
+                if (response.ok) {
+                    const markdown = await response.text();
+                    readmeSection.style.display = 'block';
+                    renderMarkdown(readmeContent, markdown);
+                } else {
+                    readmeSection.style.display = 'none';
+                }
+            } catch (error) {
+                readmeSection.style.display = 'none';
+            }
+        }
+
+        // Load directory contents
+        async function loadDirectory(path) {
+            try {
+                const url = path ? `${API_BASE}/list?path=${encodeURIComponent(path)}` : `${API_BASE}/list`;
+                const response = await fetch(url);
+                const data = await response.json();
+
+                if (data.error) {
+                    throw new Error(data.error);
+                }
+
+                renderBreadcrumb(path);
+                renderFiles(data.files);
+                loadReadme(path);
+            } catch (error) {
+                console.error('Error loading directory:', error);
+                document.getElementById('fileList').innerHTML = `
+                    <div class="error-message">Error loading directory: ${error.message}</div>
+                `;
+            }
+        }
+
+        // Handle browser back/forward
+        window.addEventListener('popstate', (event) => {
+            const path = event.state?.path || '';
+            loadDirectory(path);
+        });
+
+        // Initial load
+        const currentPath = getCurrentPath();
+        loadDirectory(currentPath);
+    </script>
+</body>
+</html>