| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="utf-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1"> |
| <title>Deep Chaos Scheduler — Presentation</title> |
| <style> |
| *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } |
| |
| body { |
| background: #0b0c0f; |
| color: #e8e6df; |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; |
| min-height: 100vh; |
| display: flex; |
| flex-direction: column; |
| } |
| |
| header { |
| background: #111217; |
| border-bottom: 1px solid #1e2028; |
| padding: 14px 24px; |
| display: flex; |
| align-items: center; |
| gap: 20px; |
| flex-wrap: wrap; |
| } |
| |
| .logo { |
| font-family: monospace; |
| font-size: 18px; |
| font-weight: 700; |
| letter-spacing: -0.5px; |
| color: #f5a524; |
| } |
| .logo span { color: #6b6a63; font-weight: 400; font-size: 13px; letter-spacing: 2px; margin-left: 6px; text-transform: uppercase; } |
| |
| .tab-bar { |
| display: flex; |
| gap: 6px; |
| margin-left: auto; |
| } |
| |
| .tab-btn { |
| background: transparent; |
| border: 1px solid #2a2b32; |
| color: #9a9890; |
| padding: 6px 16px; |
| border-radius: 6px; |
| font-size: 13px; |
| cursor: pointer; |
| transition: all 0.15s; |
| white-space: nowrap; |
| } |
| .tab-btn:hover { border-color: #f5a524; color: #f5a524; } |
| .tab-btn.active { |
| background: #f5a524; |
| border-color: #f5a524; |
| color: #0b0c0f; |
| font-weight: 600; |
| } |
| |
| .viewer-wrap { |
| flex: 1; |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| padding: 24px 16px; |
| gap: 16px; |
| } |
| |
| .controls { |
| display: flex; |
| align-items: center; |
| gap: 12px; |
| } |
| |
| .nav-btn { |
| background: #1a1b22; |
| border: 1px solid #2a2b32; |
| color: #e8e6df; |
| width: 36px; |
| height: 36px; |
| border-radius: 6px; |
| font-size: 16px; |
| cursor: pointer; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| transition: all 0.15s; |
| } |
| .nav-btn:hover:not(:disabled) { border-color: #f5a524; color: #f5a524; } |
| .nav-btn:disabled { opacity: 0.3; cursor: default; } |
| |
| .page-info { |
| font-size: 13px; |
| color: #6b6a63; |
| min-width: 80px; |
| text-align: center; |
| } |
| |
| .zoom-btn { |
| background: #1a1b22; |
| border: 1px solid #2a2b32; |
| color: #e8e6df; |
| padding: 6px 12px; |
| border-radius: 6px; |
| font-size: 13px; |
| cursor: pointer; |
| transition: all 0.15s; |
| } |
| .zoom-btn:hover { border-color: #f5a524; color: #f5a524; } |
| |
| #canvas-container { |
| background: #1a1b22; |
| border: 1px solid #2a2b32; |
| border-radius: 8px; |
| overflow: auto; |
| max-width: 100%; |
| max-height: calc(100vh - 180px); |
| display: flex; |
| align-items: flex-start; |
| justify-content: center; |
| padding: 16px; |
| } |
| |
| canvas { display: block; border-radius: 4px; } |
| |
| #loading { |
| color: #6b6a63; |
| font-size: 14px; |
| padding: 60px; |
| } |
| |
| #error-msg { |
| color: #ff6b6b; |
| font-size: 14px; |
| padding: 40px; |
| display: none; |
| } |
| |
| footer { |
| text-align: center; |
| padding: 12px; |
| font-size: 12px; |
| color: #3a3b42; |
| border-top: 1px solid #1e2028; |
| } |
| footer a { color: #f5a524; text-decoration: none; } |
| </style> |
| </head> |
| <body> |
|
|
| <header> |
| <div class="logo">DEEP CHAOS<span>Scheduler</span></div> |
| <div class="tab-bar"> |
| <button class="tab-btn active" onclick="loadDoc('deep_chaos_presentation.pdf', this)">Presentation</button> |
| <button class="tab-btn" onclick="loadDoc('deep-chaos.pdf', this)">Deep Chaos</button> |
| </div> |
| </header> |
|
|
| <div class="viewer-wrap"> |
| <div class="controls"> |
| <button class="nav-btn" id="prev-btn" onclick="changePage(-1)" disabled>←</button> |
| <span class="page-info" id="page-info">— / —</span> |
| <button class="nav-btn" id="next-btn" onclick="changePage(1)" disabled>→</button> |
| <button class="zoom-btn" onclick="changeZoom(-0.2)">−</button> |
| <span class="page-info" id="zoom-info">100%</span> |
| <button class="zoom-btn" onclick="changeZoom(0.2)">+</button> |
| </div> |
|
|
| <div id="canvas-container"> |
| <div id="loading">Loading PDF...</div> |
| <div id="error-msg">Failed to load PDF.</div> |
| <canvas id="pdf-canvas" style="display:none"></canvas> |
| </div> |
| </div> |
|
|
| <footer> |
| <a href="https://github.com/JuiceB0xC0de/deep-chaos-scheduler">github.com/JuiceB0xC0de/deep-chaos-scheduler</a> |
| · |
| <a href="https://huggingface.co/juiceb0xc0de">juiceb0xc0de on HuggingFace</a> |
| </footer> |
|
|
| <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script> |
| <script> |
| pdfjsLib.GlobalWorkerOptions.workerSrc = |
| 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js'; |
| |
| let pdfDoc = null; |
| let currentPage = 1; |
| let scale = 1.4; |
| let rendering = false; |
| |
| const canvas = document.getElementById('pdf-canvas'); |
| const ctx = canvas.getContext('2d'); |
| const loading = document.getElementById('loading'); |
| const errorMsg = document.getElementById('error-msg'); |
| |
| function showLoading(show) { |
| loading.style.display = show ? 'block' : 'none'; |
| canvas.style.display = show ? 'none' : 'block'; |
| errorMsg.style.display = 'none'; |
| } |
| |
| function updateControls() { |
| document.getElementById('page-info').textContent = |
| pdfDoc ? `${currentPage} / ${pdfDoc.numPages}` : '— / —'; |
| document.getElementById('prev-btn').disabled = currentPage <= 1; |
| document.getElementById('next-btn').disabled = !pdfDoc || currentPage >= pdfDoc.numPages; |
| document.getElementById('zoom-info').textContent = Math.round(scale * 100) + '%'; |
| } |
| |
| async function renderPage(num) { |
| if (!pdfDoc || rendering) return; |
| rendering = true; |
| const page = await pdfDoc.getPage(num); |
| const viewport = page.getViewport({ scale }); |
| canvas.width = viewport.width; |
| canvas.height = viewport.height; |
| await page.render({ canvasContext: ctx, viewport }).promise; |
| rendering = false; |
| showLoading(false); |
| updateControls(); |
| } |
| |
| async function loadDoc(url, btn) { |
| document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); |
| btn.classList.add('active'); |
| pdfDoc = null; |
| currentPage = 1; |
| showLoading(true); |
| updateControls(); |
| try { |
| pdfDoc = await pdfjsLib.getDocument(url).promise; |
| await renderPage(1); |
| } catch (e) { |
| loading.style.display = 'none'; |
| errorMsg.style.display = 'block'; |
| console.error(e); |
| } |
| } |
| |
| function changePage(delta) { |
| if (!pdfDoc) return; |
| const next = currentPage + delta; |
| if (next < 1 || next > pdfDoc.numPages) return; |
| currentPage = next; |
| showLoading(true); |
| renderPage(currentPage); |
| } |
| |
| function changeZoom(delta) { |
| scale = Math.max(0.5, Math.min(3.0, scale + delta)); |
| updateControls(); |
| if (pdfDoc) renderPage(currentPage); |
| } |
| |
| document.addEventListener('keydown', e => { |
| if (e.key === 'ArrowRight' || e.key === 'ArrowDown') changePage(1); |
| if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') changePage(-1); |
| }); |
| |
| |
| loadDoc('deep_chaos_presentation.pdf', document.querySelector('.tab-btn')); |
| </script> |
| </body> |
| </html> |
|
|