Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>ExperimentLab — {% block title %}{% endblock %}</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com" /> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" /> | |
| <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" /> | |
| <script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script> | |
| </head> | |
| <body> | |
| <div class="toast-container" id="toast-container"></div> | |
| <div class="app-shell"> | |
| <!-- Sidebar --> | |
| <nav class="sidebar" id="sidebar"> | |
| <div class="sidebar-header"> | |
| <div class="brand-icon"> | |
| <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#a5b4fc" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"> | |
| <path d="M9 3h6"/> | |
| <path d="M9 3v8l-5 7a2 2 0 0 0 1.8 3h12.4A2 2 0 0 0 20 18l-5-7V3"/> | |
| <line x1="6.5" y1="16" x2="17.5" y2="16"/> | |
| </svg> | |
| </div> | |
| <div> | |
| <div class="brand-name">ExperimentLab</div> | |
| <div class="brand-sub">Statistical Experimentation Suite</div> | |
| </div> | |
| </div> | |
| <div class="nav-section-label">Modules</div> | |
| <ul class="nav-list"> | |
| <li> | |
| <a href="/power" class="nav-link {% if active == 'power' %}active{% endif %}"> | |
| <span class="nav-icon"> | |
| <svg width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"> | |
| <polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/> | |
| </svg> | |
| </span> | |
| <div class="nav-text"> | |
| <span class="nav-title">Power Calculator</span> | |
| <span class="nav-desc">Sample size planning</span> | |
| </div> | |
| </a> | |
| </li> | |
| <li> | |
| <a href="/ab-test" class="nav-link {% if active == 'ab_test' %}active{% endif %}"> | |
| <span class="nav-icon"> | |
| <svg width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"> | |
| <rect x="3" y="3" width="8" height="18" rx="1.5"/> | |
| <rect x="13" y="3" width="8" height="18" rx="1.5"/> | |
| </svg> | |
| </span> | |
| <div class="nav-text"> | |
| <span class="nav-title">A/B Test Analyzer</span> | |
| <span class="nav-desc">Significance & effect size</span> | |
| </div> | |
| </a> | |
| </li> | |
| <li> | |
| <a href="/doe" class="nav-link {% if active == 'doe' %}active{% endif %}"> | |
| <span class="nav-icon"> | |
| <svg width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"> | |
| <rect x="3" y="3" width="7" height="7" rx="1"/> | |
| <rect x="14" y="3" width="7" height="7" rx="1"/> | |
| <rect x="3" y="14" width="7" height="7" rx="1"/> | |
| <rect x="14" y="14" width="7" height="7" rx="1"/> | |
| </svg> | |
| </span> | |
| <div class="nav-text"> | |
| <span class="nav-title">Two-Factor DoE</span> | |
| <span class="nav-desc">Factorial experiment analysis</span> | |
| </div> | |
| </a> | |
| </li> | |
| <li> | |
| <a href="/sequential" class="nav-link {% if active == 'sequential' %}active{% endif %}"> | |
| <span class="nav-icon"> | |
| <svg width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"> | |
| <polyline points="23 6 13.5 15.5 8.5 10.5 1 18"/> | |
| <polyline points="17 6 23 6 23 12"/> | |
| </svg> | |
| </span> | |
| <div class="nav-text"> | |
| <span class="nav-title">Sequential Testing</span> | |
| <span class="nav-desc">Peeking problem demo</span> | |
| </div> | |
| </a> | |
| </li> | |
| </ul> | |
| <div class="sidebar-footer"> | |
| Built by <a href="https://mnoorchenar.github.io/" target="_blank" rel="noopener">Mohammad Noorchenarboo</a> | |
| </div> | |
| </nav> | |
| <!-- Main column --> | |
| <div class="main-col"> | |
| <!-- Top bar --> | |
| <div class="top-bar"> | |
| <span class="top-bar-title">{% block page_title %}{% endblock %}</span> | |
| <div class="top-bar-right"> | |
| <span class="last-run-label" id="last-run-label" style="display:none;"> | |
| Updated <span id="last-run-time">just now</span> | |
| </span> | |
| <span class="kbd-hint"><kbd>Ctrl</kbd><kbd>⏎</kbd> to run</span> | |
| <span class="status-chip ready" id="status-chip">Ready</span> | |
| <button class="hamburger" id="hamburger" aria-label="Toggle navigation">☰</button> | |
| </div> | |
| <div class="progress-bar" id="progress-bar"></div> | |
| </div> | |
| <!-- Content area --> | |
| <div class="content-area"> | |
| {% block content %}{% endblock %} | |
| </div> | |
| </div> | |
| </div> | |
| <script src="{{ url_for('static', filename='js/app.js') }}"></script> | |
| <script> | |
| // Sidebar toggle | |
| const hamburger = document.getElementById('hamburger'); | |
| const sidebar = document.getElementById('sidebar'); | |
| hamburger.addEventListener('click', () => sidebar.classList.toggle('open')); | |
| document.querySelectorAll('.nav-link').forEach(l => l.addEventListener('click', () => sidebar.classList.remove('open'))); | |
| // Progress bar + status chip | |
| const progressBar = document.getElementById('progress-bar'); | |
| const statusChip = document.getElementById('status-chip'); | |
| function showSpinner() { | |
| progressBar.classList.add('running'); | |
| statusChip.textContent = 'Computing\u2026'; | |
| statusChip.className = 'status-chip computing'; | |
| } | |
| function hideSpinner() { | |
| progressBar.classList.remove('running'); | |
| statusChip.textContent = 'Ready'; | |
| statusChip.className = 'status-chip ready'; | |
| if (typeof markLastRun === 'function') markLastRun(); | |
| } | |
| function showError(msg) { | |
| progressBar.classList.remove('running'); | |
| statusChip.textContent = 'Error'; | |
| statusChip.className = 'status-chip error'; | |
| if (typeof showToast === 'function') showToast(msg, 'error'); | |
| } | |
| function hideError() { /* no-op */ } | |
| // Plotly helper | |
| const PLOTLY_CONFIG = { | |
| responsive: true, | |
| displaylogo: false, | |
| modeBarButtonsToRemove: ['lasso2d', 'select2d', 'toImage', 'resetScale2d'], | |
| toImageButtonOptions: { format: 'png', filename: 'experimentlab_chart', scale: 2 } | |
| }; | |
| function renderChart(divId, chartJson) { | |
| const parsed = JSON.parse(chartJson); | |
| Plotly.newPlot(divId, parsed.data, parsed.layout, PLOTLY_CONFIG); | |
| } | |
| </script> | |
| {% block scripts %}{% endblock %} | |
| </body> | |
| </html> | |