Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Jung Chunker UI</title> | |
| <style> | |
| :root { --bg: #fdf6e3; --text: #586e75; --accent: #268bd2; } | |
| body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: var(--bg); color: var(--text); margin: 0; display: flex; height: 100vh; } | |
| /* Sidebar for the Tree */ | |
| #sidebar { width: 350px; border-right: 1px solid #ddd; overflow-y: auto; padding: 20px; background: #eee8d5; } | |
| #viewer { flex-grow: 1; padding: 40px; overflow-y: auto; line-height: 1.6; } | |
| .tree-node { margin-left: 15px; border-left: 2px solid #ccc; padding-left: 10px; margin-bottom: 10px; } | |
| .summary-block { font-weight: bold; color: var(--accent); cursor: pointer; display: block; margin-top: 15px; } | |
| .leaf-node { font-size: 0.9em; cursor: pointer; color: #657b83; display: block; margin: 5px 0; } | |
| .leaf-node:hover { text-decoration: underline; } | |
| h1 { font-size: 1.2em; border-bottom: 1px solid #ccc; padding-bottom: 10px; } | |
| .chunk-content { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); } | |
| .upload-section { margin-bottom: 20px; padding: 15px; background: #fff; border-radius: 5px; } | |
| /* Styling for different levels */ | |
| .summary_l1 { color: #268bd2; font-weight: bold; margin-left: 10px; border-left: 2px solid #268bd2; } | |
| .summary_l2 { color: #d33682; font-weight: 800; margin-left: 5px; border-left: 4px solid #d33682; font-size: 1.1em; } | |
| .summary_l3 { color: #b58900; font-weight: 900; text-transform: uppercase; border: 2px solid #b58900; padding: 5px; text-align: center; } | |
| .leaf-node { color: #657b83; margin-left: 20px; font-size: 0.9em; } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="sidebar"> | |
| <h1>Jungian Chunker</h1> | |
| <div class="upload-section"> | |
| <input type="file" id="pdfUpload" accept=".pdf"><br><br> | |
| <label><input type="radio" name="mode" value="range" checked onclick="toggleRange(true)"> Page Range</label> | |
| <label><input type="radio" name="mode" value="whole" onclick="toggleRange(false)"> Whole Book</label> | |
| <div id="range-inputs" style="margin-top:10px;"> | |
| <input type="number" id="startP" placeholder="Start" style="width:50px"> | |
| <input type="number" id="endP" placeholder="End" style="width:50px"> | |
| </div> | |
| <button onclick="uploadFile()" style="margin-top:10px; width:100%;">Process Jungian Tree</button> | |
| <div id="status" style="font-size: 0.8em; margin-top: 10px; color: var(--accent);">Status: Idle</div> | |
| <!-- Download Button --> | |
| <button id="downloadBtn" style="display:none; margin-top:10px; background:#2aa198; color:white; border:none; padding:10px; cursor:pointer; width:100%;"> | |
| π₯ Download Knowledge Tree | |
| </button> | |
| </div> | |
| <div id="tree-container"></div> | |
| </div> | |
| <script> | |
| function toggleRange(show) { | |
| document.getElementById('range-inputs').style.display = show ? 'block' : 'none'; | |
| } | |
| async function uploadFile() { | |
| const fileInput = document.getElementById('pdfUpload'); | |
| const isWhole = document.querySelector('input[name="mode"]:checked').value === 'whole'; | |
| const status = document.getElementById('status'); | |
| // Explicitly grab the values from the inputs | |
| const startVal = document.getElementById('startP').value; | |
| const endVal = document.getElementById('endP').value; | |
| if (fileInput.files.length === 0) return alert("Please select a PDF"); | |
| const formData = new FormData(); | |
| formData.append('file', fileInput.files[0]); | |
| formData.append('whole', isWhole); | |
| formData.append('start', startVal || 20); // Fallback to 20 if empty | |
| formData.append('end', endVal || 30); // Fallback to 30 if empty | |
| console.log("Sending Range:", startVal, "to", endVal); // Check browser console | |
| status.innerText = "Status: Uploading..."; | |
| await fetch('/upload', { method: 'POST', body: formData }); | |
| status.innerText = "Status: Processing (Waiting for first chunk...)"; | |
| listenToStream(); | |
| } | |
| // --- VIEW function --- | |
| function view(title, text) { | |
| const display = document.getElementById('content-display'); | |
| display.innerHTML = `<h2>${title}</h2><div style="white-space: pre-wrap;">${text}</div>`; | |
| } | |
| // --- listenToStream Function -- | |
| function listenToStream() { | |
| const eventSource = new EventSource('/stream'); | |
| const container = document.getElementById('tree-container'); | |
| const status = document.getElementById('status'); | |
| const dlBtn = document.getElementById('downloadBtn'); | |
| container.innerHTML = ""; | |
| eventSource.onmessage = (event) => { | |
| const data = JSON.parse(event.data); | |
| if (data.type === 'done') { | |
| status.innerText = "β EXTRACTION FINISHED!"; | |
| //dlBtn.style.display = "block"; // Show the button | |
| //dlBtn.onclick = () => window.location.href = '/download-latest'; | |
| //eventSource.close(); | |
| //return; | |
| const dlBtn = document.getElementById('downloadBtn'); | |
| dlBtn.style.display = "block"; // Show the button | |
| dlBtn.innerHTML = "π₯ Download All Files (.zip)"; | |
| dlBtn.onclick = () => window.location.href = '/download-all'; // Points to the ZIP endpoint | |
| eventSource.close(); | |
| } | |
| // Add Leaf or Summary to the UI | |
| const node = document.createElement('div'); | |
| //node.className = data.type === 'summary' ? "tree-node summary-block" : "tree-node leaf-node"; | |
| //node.innerHTML = (data.type === 'summary' ? "β " : "β ") + (data.name || data.filename); | |
| // Pass the content to the view function when clicked | |
| //node.onclick = () => view(data.name || data.filename, data.content); | |
| node.className = `tree-node ${data.type}`; // Uses summary_l1, summary_l2, etc. | |
| let prefix = "β "; | |
| if (data.type === 'summary_l1') prefix = "β "; | |
| if (data.type === 'summary_l2') prefix = "π "; | |
| if (data.type === 'summary_l3') prefix = "π "; | |
| //node.innerHTML = prefix + data.name; | |
| // innerHTML with "OR" check | |
| node.innerHTML = prefix + (data.name || data.filename || "Untitled Chunk"); | |
| node.onclick = () => view(data.name, data.content); | |
| container.appendChild(node); | |
| status.innerText = `Status: Created ${data.type}...`; | |
| }; | |
| } | |
| </script> | |
| </body> | |
| </html> |