diff --git a/docker-compose.yml b/docker-compose.yml
index 11b3b1a..29e5baa 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -28,6 +28,8 @@ services:
build: ./backend
env_file:
- ./backend/.env
+ environment:
+ - ANONYMIZED_TELEMETRY=False
volumes:
- uploads_data:/app/uploads
- chroma_data:/app/chroma_data
@@ -43,6 +45,8 @@ services:
command: celery -A app.tasks worker --loglevel=info --concurrency=2
env_file:
- ./backend/.env
+ environment:
+ - ANONYMIZED_TELEMETRY=False
volumes:
- uploads_data:/app/uploads
- chroma_data:/app/chroma_data
diff --git a/frontend/index.html b/frontend/index.html
index 20e5c54..df9e727 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -5,6 +5,10 @@
-
π©Ί PedQuiz
+
setMenuOpen(false)}>π©Ί PedQuiz
+
{user && (
-
+ <>
+ {/* Desktop nav */}
+
+
+ {/* Mobile: jobs + hamburger */}
+
+
+
+
+ >
)}
+
+ {/* Mobile dropdown */}
+ {user && menuOpen && (
+
+
+ {navLinks.map(l => (
+
+ {l.label}
+
+ ))}
+
+
+
+ )}
)
}
diff --git a/frontend/src/index.css b/frontend/src/index.css
index e149321..31afc83 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -2,9 +2,9 @@
:root {
--bg: #f0f4f8;
--card-bg: #ffffff;
- --card-border: 1px solid rgba(0,0,0,0.05);
- --card-shadow: 0 1px 6px rgba(0,0,0,0.06), 0 0 0 1px rgba(0,0,0,0.03);
- --card-radius: 14px;
+ --card-border: 1px solid rgba(0,0,0,0.07);
+ --card-shadow: 0 1px 4px rgba(0,0,0,0.07), 0 4px 16px rgba(0,0,0,0.04);
+ --card-radius: 12px;
--primary: #2563eb;
--primary-hover: #1d4ed8;
--primary-fg: #ffffff;
@@ -30,68 +30,79 @@
--badge-radius: 12px;
}
-/* ββ Warm Brown / Parchment theme ββββββββββββββββββββββββββββββ */
+/* ββ Warm / Literary theme ββββββββββββββββββββββββββββββββββββββ */
[data-theme="markdown"] {
- --bg: #f2ebe0;
- --card-bg: #faf6ef;
- --card-border: 1px solid #d6c9b6;
- --card-shadow: 0 1px 3px rgba(80,50,20,0.07);
- --card-radius: 8px;
- --primary: #7c4a1e;
- --primary-hover: #633c18;
+ --bg: #f5f0e8;
+ --card-bg: #fdfaf4;
+ --card-border: 1px solid #e0d5c5;
+ --card-shadow: 0 2px 8px rgba(60,40,10,0.06);
+ --card-radius: 10px;
+ --primary: #8b5a2b;
+ --primary-hover: #6d4420;
--primary-fg: #fdf8f0;
- --text: #2c1a0e;
+ --text: #1e140a;
--text-muted: #7a5c42;
- --text-subtle: #a8886a;
- --border: #d6c9b6;
- --input-bg: #fdf8f0;
- --option-bg: #fdf8f0;
- --option-hover: #f5ede0;
- --option-sel-bg: #f5e6d3;
- --option-sel-bd: #7c4a1e;
- --correct-bg: #e6f4e6;
- --correct-bd: #8cbf8c;
- --correct-fg: #1a4d1a;
+ --text-subtle: #aa8a6a;
+ --border: #e0d5c5;
+ --input-bg: #fdfaf4;
+ --option-bg: #fdfaf4;
+ --option-hover: #f7f0e4;
+ --option-sel-bg: #f2e4d0;
+ --option-sel-bd: #8b5a2b;
+ --correct-bg: #e8f5e8;
+ --correct-bd: #8abf8a;
+ --correct-fg: #1a4a1a;
--wrong-bg: #fdecea;
- --wrong-bd: #d9908a;
- --wrong-fg: #6b1e1e;
- --expl-bg: #f8f3ea;
- --expl-bd: #b8945a;
- --navbar-bg: #2c1a0e;
- --navbar-fg: #f2e8d8;
+ --wrong-bd: #d99090;
+ --wrong-fg: #6b1a1a;
+ --expl-bg: #f9f4ea;
+ --expl-bd: #c4965a;
+ --navbar-bg: #1e140a;
+ --navbar-fg: #f0e8d8;
--badge-radius: 4px;
- --font-body: Georgia, 'Times New Roman', 'Palatino', serif;
- --font-heading: Georgia, 'Times New Roman', serif;
+ --font-body: 'Source Serif 4', Georgia, serif;
+ --font-heading: 'Playfair Display', 'Source Serif 4', serif;
+ --font-ui: 'Inter', system-ui, sans-serif;
}
-/* ββ Warm Brown theme overrides βββββββββββββββββββββββββββββββ */
+/* ββ Warm theme overrides ββββββββββββββββββββββββββββββββββββββ */
body[data-theme="markdown"] {
- font-family: var(--font-body);
- font-size: 16px;
- line-height: 1.75;
+ font-family: var(--font-ui);
+ font-size: 15.5px;
+ line-height: 1.7;
}
-[data-theme="markdown"] .navbar .logo { color: #d4a96a; }
-[data-theme="markdown"] h1,
-[data-theme="markdown"] h2,
-[data-theme="markdown"] h3 {
- font-family: var(--font-heading);
- font-weight: 700;
+[data-theme="markdown"] .navbar .logo { color: #e0a84a; font-family: var(--font-heading); letter-spacing: 0; }
+[data-theme="markdown"] h1 { font-family: var(--font-heading); font-weight: 700; letter-spacing: -0.02em; }
+[data-theme="markdown"] h2, [data-theme="markdown"] .card h2 { font-family: var(--font-heading); font-weight: 600; }
+[data-theme="markdown"] h3 { font-family: var(--font-ui); font-weight: 600; }
+[data-theme="markdown"] .card h2 { border-bottom: 1px solid var(--border); padding-bottom: 10px; margin-bottom: 18px; }
+[data-theme="markdown"] .question-card { border-left: 3px solid var(--primary); }
+[data-theme="markdown"] .question-card h3 {
+ font-family: var(--font-body);
+ font-size: 1.05rem;
+ line-height: 1.7;
+ font-weight: 400;
+ font-style: normal;
color: var(--text);
}
-[data-theme="markdown"] .card h2 { border-bottom: 1px solid var(--border); padding-bottom: 10px; }
-[data-theme="markdown"] .question-card { border-left: 4px solid var(--primary); }
-[data-theme="markdown"] .question-card h3 { font-family: var(--font-heading); font-size: 1.05rem; line-height: 1.65; }
-[data-theme="markdown"] .option { border-radius: 6px; font-size: 0.95rem; font-family: inherit; }
-[data-theme="markdown"] .option-letter { border-radius: 3px; font-family: Georgia, serif; font-style: italic; }
-[data-theme="markdown"] .explanation { font-family: inherit; line-height: 1.85; border-left: 3px solid var(--expl-bd); background: var(--expl-bg); }
-[data-theme="markdown"] .btn { font-family: inherit; }
-[data-theme="markdown"] .score-display .score-value { letter-spacing: -2px; font-family: Georgia, serif; }
+[data-theme="markdown"] .option { border-radius: 6px; font-size: 0.94rem; }
+[data-theme="markdown"] .option-letter { border-radius: 3px; font-family: var(--font-ui); font-weight: 600; }
+[data-theme="markdown"] .explanation {
+ font-family: var(--font-body);
+ font-size: 0.9rem;
+ line-height: 1.85;
+ border-left: 3px solid var(--expl-bd);
+ background: var(--expl-bg);
+ color: var(--text-muted);
+}
+[data-theme="markdown"] .btn { font-family: var(--font-ui); font-weight: 500; }
+[data-theme="markdown"] .score-display .score-value { font-family: var(--font-heading); letter-spacing: -3px; }
/* ββ Reset ββββββββββββββββββββββββββββββββββββββββββββββββββββ */
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
- font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
+ font-family: 'Inter', ui-sans-serif, system-ui, -apple-system, sans-serif;
background: var(--bg);
color: var(--text);
line-height: 1.6;
@@ -107,11 +118,11 @@ body {
.navbar .container { display: flex; justify-content: space-between; align-items: center; height: 52px; }
.navbar .logo { font-size: 1.15rem; font-weight: 700; color: #60a5fa; text-decoration: none; letter-spacing: -0.01em; white-space: nowrap; }
[data-theme="markdown"] .navbar .logo { color: #d4a96a; }
-.navbar nav { display: flex; gap: 4px; align-items: center; overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none; }
-.navbar nav::-webkit-scrollbar { display: none; }
-.navbar a { color: var(--navbar-fg); text-decoration: none; font-size: 0.83rem; opacity: 0.7; padding: 6px 10px; border-radius: 6px; white-space: nowrap; transition: opacity 0.15s, background 0.15s; }
-.navbar a:hover { opacity: 1; background: rgba(255,255,255,0.08); }
-.navbar button { background: transparent; border: 1px solid rgba(255,255,255,0.2); color: var(--navbar-fg); padding: 5px 14px; border-radius: 6px; cursor: pointer; font-size: 0.82rem; white-space: nowrap; }
+.nav-desktop { display: flex; gap: 4px; align-items: center; }
+.nav-mobile-controls { display: none; align-items: center; gap: 8px; }
+.navbar a { color: var(--navbar-fg); text-decoration: none; font-size: 0.83rem; padding: 6px 10px; border-radius: 6px; white-space: nowrap; transition: opacity 0.15s, background 0.15s; }
+.navbar a:hover { background: rgba(255,255,255,0.09); opacity: 1 !important; }
+.navbar button { background: transparent; border: 1px solid rgba(255,255,255,0.2); color: var(--navbar-fg); padding: 5px 14px; border-radius: 6px; cursor: pointer; font-size: 0.82rem; white-space: nowrap; font-family: inherit; }
/* ββ Cards ββββββββββββββββββββββββββββββββββββββββββββββββββββ */
.card {
@@ -305,8 +316,8 @@ body {
.card { padding: 18px 16px; }
.quiz-nav { flex-wrap: wrap; }
.quiz-nav-numbers { order: 3; width: 100%; margin-top: 8px; }
- .navbar .container { height: 48px; }
- .navbar .logo { font-size: 1rem; }
- .navbar a { font-size: 0.78rem; padding: 5px 7px; }
- .navbar button { padding: 4px 10px; font-size: 0.78rem; }
+ .navbar .container { height: 52px; }
+ /* Mobile: show hamburger, hide desktop nav */
+ .nav-desktop { display: none; }
+ .nav-mobile-controls { display: flex; }
}
diff --git a/frontend/src/pages/DocumentDetailPage.jsx b/frontend/src/pages/DocumentDetailPage.jsx
index 44acb78..2f0494a 100644
--- a/frontend/src/pages/DocumentDetailPage.jsx
+++ b/frontend/src/pages/DocumentDetailPage.jsx
@@ -14,8 +14,15 @@ function ExtractionProgress({ jobId, onDone, onClose }) {
const poll = useCallback(async () => {
try {
const res = await api.get(`/quizzes/job/${jobId}`)
- setSteps(res.data.steps || [])
+ const newSteps = res.data.steps || []
+ setSteps(newSteps)
setStatus(res.data.status)
+ // Update localStorage job tracker
+ const lastStep = newSteps.length ? newSteps[newSteps.length - 1].message : 'Processingβ¦'
+ const stored = JSON.parse(localStorage.getItem('pedquiz_jobs') || '[]')
+ const idx = stored.findIndex(j => j.jobId === jobId)
+ if (idx >= 0) { stored[idx].status = res.data.status; stored[idx].lastStep = lastStep.slice(0, 60) }
+ localStorage.setItem('pedquiz_jobs', JSON.stringify(stored))
if (res.data.status === 'completed') {
setQuizId(res.data.quiz_id)
clearInterval(intervalRef.current)
@@ -179,6 +186,11 @@ export default function DocumentDetailPage() {
})
// Async: show progress panel
if (res.data.job_id) {
+ // Track in localStorage so navbar badge can show it
+ const title = quizTitle || `Quiz: ${sectionName}`
+ const stored = JSON.parse(localStorage.getItem('pedquiz_jobs') || '[]')
+ stored.unshift({ jobId: res.data.job_id, title, status: 'running', lastStep: 'Startingβ¦', ts: Date.now() })
+ localStorage.setItem('pedquiz_jobs', JSON.stringify(stored.slice(0, 10)))
setActiveJob({ jobId: res.data.job_id, sectionName })
// If already completed (sync fallback), navigate directly
if (res.data.status === 'completed' && res.data.quiz_id) {