root
Done
4df8760
<!doctype html>
<html lang="en" data-bs-theme="dark">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}DocuPDF{% endblock %}</title>
{% block styles %}{% endblock %}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/tom-select@2.2.2/dist/css/tom-select.bootstrap5.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<style>
/* === UNIFIED DESIGN SYSTEM === */
:root {
--bg-dark: #212529;
--bg-card: #2b3035;
--bg-elevated: #343a40;
--bg-hover: #3d444b;
--border-subtle: #495057;
--border-muted: #6c757d;
--text-primary: #e9ecef;
--text-muted: #adb5bd;
--accent-primary: #0d6efd;
--accent-info: #0dcaf0;
--accent-success: #198754;
--accent-warning: #ffc107;
--accent-danger: #dc3545;
--transition-fast: 0.15s ease;
--transition-normal: 0.25s ease;
--shadow-sm: 0 2px 4px rgba(0,0,0,0.3);
--shadow-md: 0 4px 12px rgba(0,0,0,0.4);
}
/* --- Unified Button Pill Style --- */
.btn-pill {
border-radius: 50px;
font-weight: 500;
transition: all var(--transition-fast);
}
.btn-pill:hover {
transform: translateY(-1px);
box-shadow: var(--shadow-sm);
}
.btn-pill:active {
transform: translateY(0);
}
/* --- Enhanced Button Hover Effects --- */
.btn {
transition: all var(--transition-fast);
}
.btn:hover {
transform: translateY(-1px);
}
.btn:active {
transform: translateY(0);
}
/* --- Unified Modal Headers --- */
.modal-content {
border: 1px solid var(--border-subtle);
}
.modal-header {
background: linear-gradient(180deg, var(--bg-card), var(--bg-dark));
border-bottom-color: var(--border-subtle);
}
.modal-footer {
border-top-color: var(--border-subtle);
}
/* --- Unified Card Styles --- */
.card {
transition: all var(--transition-fast);
}
.card:hover {
border-color: var(--accent-primary) !important;
}
/* --- Unified Note Section/Card --- */
.note-section, .note-card {
background: linear-gradient(135deg, rgba(13, 202, 240, 0.08), rgba(13, 202, 240, 0.15));
border: 1px solid rgba(13, 202, 240, 0.3);
border-radius: 10px;
padding: 12px;
transition: all var(--transition-fast);
}
.note-section:hover, .note-card:hover {
border-color: var(--accent-info);
box-shadow: 0 0 12px rgba(13, 202, 240, 0.15);
}
.note-section h6 {
color: var(--accent-info);
margin-bottom: 10px;
font-weight: 600;
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 0.5px;
}
/* --- Unified Progress Bars --- */
.progress-bar {
background: linear-gradient(90deg, var(--accent-primary), var(--accent-info));
}
/* --- Tom Select Dark Mode Fix --- */
.ts-control {
background-color: var(--bg-card) !important;
border-color: var(--border-subtle) !important;
color: #fff !important;
}
.ts-control input {
color: #fff !important;
}
.ts-dropdown, .ts-dropdown .optgroup-header {
background-color: var(--bg-card) !important;
border-color: var(--border-subtle) !important;
}
.ts-dropdown .option:hover, .ts-dropdown .active {
background-color: var(--accent-primary) !important;
}
.ts-control .item {
background-color: var(--accent-primary) !important;
color: #fff !important;
}
/* --- Unified Badge Styles --- */
.badge {
font-weight: 500;
padding: 0.5em 0.75em;
transition: all var(--transition-fast);
}
/* --- Unified Table Styles --- */
.table {
--bs-table-bg: transparent;
--bs-table-striped-bg: rgba(255, 255, 255, 0.03);
--bs-table-hover-bg: rgba(13, 110, 253, 0.1);
color: var(--text-primary);
border-color: var(--bg-elevated);
}
.table th {
background-color: var(--bg-dark);
color: var(--text-muted);
font-weight: 600;
}
.table td {
border-color: var(--bg-elevated);
vertical-align: middle;
}
/* --- Unified Nav Pills --- */
.nav-pills .nav-link {
color: var(--text-muted);
background-color: transparent;
border: 1px solid var(--border-subtle);
border-radius: 50px;
transition: all var(--transition-fast);
}
.nav-pills .nav-link:hover {
color: #fff;
background-color: var(--bg-elevated);
transform: translateY(-1px);
}
.nav-pills .nav-link.active {
color: #fff !important;
background-color: var(--accent-primary) !important;
border-color: var(--accent-primary);
}
/* --- Unified Form Controls --- */
.form-control, .form-select {
background-color: var(--bg-dark);
border-color: var(--border-subtle);
color: var(--text-primary);
transition: all var(--transition-fast);
}
.form-control:focus, .form-select:focus {
background-color: var(--bg-dark);
border-color: var(--accent-primary);
box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
color: var(--text-primary);
}
/* --- Smooth Page Transitions --- */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
.container, .container-fluid {
animation: fadeIn 0.25s ease-out;
}
/* --- Alert Animations --- */
.alert {
animation: slideInDown 0.25s ease-out;
}
@keyframes slideInDown {
from { opacity: 0; transform: translateY(-8px); }
to { opacity: 1; transform: translateY(0); }
}
/* --- Dropdown Animations --- */
.dropdown-menu {
animation: dropdownFadeIn 0.15s ease-out;
}
@keyframes dropdownFadeIn {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
/* --- Smooth List Item Transitions --- */
.list-group-item {
transition: all var(--transition-fast);
}
.list-group-item:hover {
background-color: var(--bg-hover);
}
/* --- Focus Ring Enhancement --- */
:focus-visible {
outline: 2px solid var(--accent-primary);
outline-offset: 2px;
}
/* --- Skeleton Loading Animation --- */
.skeleton {
background: linear-gradient(90deg, var(--bg-elevated) 25%, var(--bg-hover) 50%, var(--bg-elevated) 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
}
@keyframes skeleton-loading {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
#startup-sync-overlay {
position: fixed;
inset: 0;
z-index: 2000;
display: none;
align-items: center;
justify-content: center;
padding: 24px;
background:
radial-gradient(circle at top, rgba(13, 202, 240, 0.14), transparent 35%),
rgba(12, 16, 22, 0.92);
backdrop-filter: blur(10px);
}
#startup-sync-overlay.visible {
display: flex;
}
.startup-sync-card {
width: min(560px, 100%);
padding: 28px;
border-radius: 18px;
border: 1px solid rgba(13, 202, 240, 0.22);
background: linear-gradient(180deg, rgba(43, 48, 53, 0.96), rgba(24, 28, 32, 0.98));
box-shadow: 0 24px 80px rgba(0, 0, 0, 0.45);
}
.startup-sync-progress {
height: 14px;
background: rgba(255, 255, 255, 0.08);
border-radius: 999px;
overflow: hidden;
}
.startup-sync-progress .progress-bar {
transition: width 0.35s ease;
}
</style>
{% block head %}{% endblock %}
</head>
<body>
{% include '_navbar.html' %}
<div class="content-wrapper" style="min-height: calc(100vh - 56px); overflow-y: auto;">
{% block content %}{% endblock %}
</div>
<div id="startup-sync-overlay" aria-live="polite" aria-busy="true">
<div class="startup-sync-card text-white">
<div class="d-flex align-items-center justify-content-between gap-3 mb-3">
<div>
<div class="text-uppercase text-info small fw-semibold mb-1">Startup Sync</div>
<h4 class="mb-0">Loading Hugging Face dataset</h4>
</div>
<i class="bi bi-cloud-arrow-down fs-2 text-info"></i>
</div>
<p id="startup-sync-message" class="text-light mb-2">Preparing initial data sync...</p>
<p id="startup-sync-detail" class="text-secondary small mb-3"></p>
<div class="startup-sync-progress mb-2">
<div id="startup-sync-bar" class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%"></div>
</div>
<div class="d-flex justify-content-between align-items-center">
<span id="startup-sync-percent" class="small text-info">0%</span>
<span id="startup-sync-state" class="small text-secondary">Waiting for sync status...</span>
</div>
<div id="startup-sync-error" class="alert alert-danger mt-3 mb-0 d-none"></div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/tom-select@2.2.2/dist/js/tom-select.complete.min.js"></script>
<script>
(function() {
const overlay = document.getElementById('startup-sync-overlay');
const message = document.getElementById('startup-sync-message');
const detail = document.getElementById('startup-sync-detail');
const bar = document.getElementById('startup-sync-bar');
const percent = document.getElementById('startup-sync-percent');
const state = document.getElementById('startup-sync-state');
const errorBox = document.getElementById('startup-sync-error');
let wasVisible = false;
function setOverlayVisible(visible) {
overlay.classList.toggle('visible', visible);
wasVisible = wasVisible || visible;
}
async function pollStartupSync() {
try {
const response = await fetch('/hf-sync/status', { cache: 'no-store' });
if (!response.ok) {
return;
}
const data = await response.json();
const isStartupDownload = data.action === 'download' && (data.status === 'running' || data.status === 'failed');
if (!isStartupDownload) {
setOverlayVisible(false);
if (wasVisible && data.status === 'completed') {
window.location.reload();
}
return;
}
setOverlayVisible(true);
message.textContent = data.message || 'Syncing startup data...';
detail.textContent = data.detail || '';
state.textContent = data.status === 'failed' ? 'Sync failed' : 'Sync in progress';
percent.textContent = `${data.progress || 0}%`;
bar.style.width = `${data.progress || 0}%`;
if (data.status === 'failed') {
bar.classList.remove('progress-bar-animated');
bar.classList.add('bg-danger');
errorBox.classList.remove('d-none');
errorBox.textContent = data.error || 'The initial Hugging Face sync failed.';
} else {
bar.classList.add('progress-bar-animated');
bar.classList.remove('bg-danger');
errorBox.classList.add('d-none');
}
} catch (error) {
if (wasVisible) {
errorBox.classList.remove('d-none');
errorBox.textContent = `Unable to read sync status: ${error}`;
}
}
}
pollStartupSync();
window.setInterval(pollStartupSync, 1500);
})();
</script>
{% block scripts %}{% endblock %}
</body>
</html>