- Switch to Georgia to better match Word's serif rendering
- Explicit font-weight:normal on body/p so mammoth's <strong> wrapping
doesn't bleed into paragraph text
- Tighter heading/paragraph spacing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Removed narrow max-width/margin from generated HTML page
- iframe now spans full card width (100%)
- onload JS auto-sizes iframe height to content so nothing is cut off
- Falls back to 500px if cross-origin restriction ever applies
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the <iframe> with an inline <div> containing the mammoth HTML.
Content now flows full-width with the page instead of being cramped in
a fixed-height scrolling box. Download link still available below.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Assignment body was being run through esc() which turned <details>/<iframe>
tags into visible raw text. Set innerHTML directly to render the HTML.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each imported doc now shows as a collapsed summary line (📄 filename.docx)
that expands to reveal the iframe and download link when clicked.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each imported .docx now adds its iframe below any existing body content
so multiple documents can be attached to a single item.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
On upload, mammoth converts the doc to a styled HTML page saved alongside
the .docx. The item body gets an <iframe> pointing to the HTML so students
see the document inline. A download link below gives access to the original.
Falls back to a plain download link if HTML conversion fails.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Instead of parsing .docx content via mammoth and inserting raw text/HTML,
the import flow now saves the file to data/attachments/ (UUID filename) and
inserts an <a> link into the item body so students can open/download the doc.
- Added GET /api/attachments/:filename (no PIN — students access directly)
- Added POST /api/admin/upload-attachment (PIN required)
- Removed mammoth dependency from server
- Updated admin.js import handler to POST to new endpoint and set link HTML
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Lesson body is a contenteditable div — must use innerHTML, not .value.
Assignment body is a plain textarea — strip HTML tags before setting .value.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove all AI processing from the import-docx endpoint. Instead:
- Convert the .docx to HTML with mammoth.convertToHtml (preserves
bold, headings, lists, etc.) — the result goes straight into the
item body and renders in the lesson viewer
- Extract raw text in parallel to pick the document title from the
first non-empty line
- No external services, no AI, works on the deployed server
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The import-docx endpoint was hardcoded to local Ollama, which fails
on the deployed server where Ollama isn't running. Now mirrors the
image scan logic: tries local first, falls back to Ollama Cloud if
configured, using the same scan_primary setting.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
btoa(String.fromCharCode(...new Uint8Array(buf))) exceeds V8's function
argument stack limit for files larger than ~65k bytes. Process in 8k
chunks instead to handle any file size.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The old import scanned a hardcoded Seafile CloudStorage path that only
worked on one specific machine. Replaced with a standard <input type=file>
browser picker that reads the .docx as base64 and uploads it directly, so
the feature works everywhere. Server endpoint now accepts either a base64
payload or the legacy path. Also fixed a crash where resetImportSection()
still referenced the removed select element, which was breaking the item
editor for all item types.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Gradebook now groups items by unit with collapsible cards (same card
style as course detail) and a Course overall summary table at the bottom
- server.js: add unit_id/unit_name to gradebook API so items can be grouped
- Course detail: preserve which units are open and scroll position across
any in-place re-render (add unit, delete unit, save meta, return from
item editor) — no more jumping to the top with everything reset
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Student view auto-opens the unit whose item due-dates span today (falls back
to most-recent past unit, earliest future unit, then first with incomplete
work). Admin view starts all units expanded. Both share the same card design:
unit-number badge, name, progress/count indicator, animated chevron toggle.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cells now expand to show all tasks rather than clipping with overflow:hidden.
Column widths stay equal (CSS grid 1fr) — only height is flexible.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Week view days locked to 240px, month view cells to 100px — tasks scroll
within each card so one busy day no longer stretches the whole row.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove sessionStorage caching of the PIN so the gate always shows on
page load instead of auto-unlocking from a previous session.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Toast: replace buried #admin-msg hint with a floating green/red toast
(bottom-right, auto-dismisses after 3s) covering every save action —
lists, decks, items, grades, kids, units, courses, bulk imports, and
planner saves.
Planner batch add: clicking '+ Add' now queues items locally (shown
as dashed pending badges) without saving. A '💾 Save N items' button
appears in the toolbar; clicking it commits everything at once and
refreshes the planner. Queue clears on student change.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Gradebook: period dropdown (all time / 30 / 90 days) and CSV export for
Spelling Test History section
- Student portal spelling tab: shows last 10 tests as a Recent Tests list
- Planner: completed standalone spelling tests appear as read-only ⭐ badges
on the day they were taken (week and month views)
- Removed dead s.tests query from /api/overview (now served by dedicated endpoints)
- New endpoints: GET /api/spelling-tests/csv, GET /api/students/:id/tests,
GET /api/admin/students/:id/tests-range
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a GET /api/spelling-tests endpoint that returns all standalone test
results grouped by student. The Gradebook panel now shows a "Spelling Test
History" section below the course gradebook table. Removed the "Test results"
section from the Spelling panel left column.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Assign → button on each list row opens a popup to pick a kid directly
without scrolling up to the assignment dropdowns
- Hide/Unhide button on built-in lists (stored in localStorage) keeps
UNGROUPED clean; Show hidden toggle reveals them dimmed when needed
- Mastery progress bar on each kid showing X/Y words mastered for their
current assigned list (server now returns listProgress in /api/overview)
- Filter-lists / filter-decks search input for fast lookup by name
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Toggle buttons now match the rest of the admin UI (same font, padding,
border-radius). Both start secondary/gray; the active one turns blue.
List and deck group sections are collapsed by default instead of open.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New list, Bulk import, New deck, and Bulk import decks are now proper
buttons. Clicking one toggles the form open (highlighted blue) and
clicking again or cancelling closes it. Only one form open at a time.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Widened admin-main to 1400px max-width and added panel-cols/panel-col
wrappers so Kids, Courses, Spelling, and Flashcards all display content
on the left and forms/editors on the right on wider screens. Falls back
to single-column on smaller viewports.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Paste multiple lists/decks in one shot using # Name headers:
# Week 1
word1
word2 | example sentence
# Week 2
...
An optional group field applies to all imported items at once.
Progress shown inline; list/deck panel refreshes on completion.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Group headers now use a solid tan background, bold uppercase text, and a
▼/▶ arrow indicator so collapsible sections are visually obvious.
Uses native <details>/<summary> elements; ungrouped items show inline
when no named groups exist.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- group_name column added to lists and decks tables
- GET/POST/PUT endpoints for both include group_name
- Admin Spelling and Flashcards panels render items grouped under
labeled section headers (e.g. Bible 6, Bible 8) with ungrouped
items shown last
- Group input in each form has datalist autocomplete from existing groups
- Edit/copy populates the group field; Clear resets it
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- POST /api/courses/:id/duplicate deep-copies a course with all units,
items, and quiz questions; opens the copy immediately for renaming
- Roster changed from broken vertical stack to horizontal chip-pills
(fixes admin-card label override that was collapsing inline-flex)
- item-row padding reduced for tighter lists throughout admin
- Courses and Kids admin panels now use a 2-column auto-fill grid
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bind mount (./data:/app/data) was relative to the working directory,
so data was lost whenever the project was re-cloned or compose was run
from a different path. A named volume is managed by Docker and persists
across container rebuilds and image pulls.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the plain textarea with a WYSIWYG contenteditable editor so
the formatting the parent sees while writing matches what the student
sees when reading. Toolbar supports bold, italic, underline, H2/H3,
bullet/numbered lists, links, and YouTube embeds. Selection is saved
on editor blur and restored before each execCommand so toolbar clicks
don't lose the user's selection.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without this, local spelling.db was being baked into the image,
causing the bind mount to appear empty or stale on first start.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Week print: tasks grouped by day, each showing course name, type,
and full assignment body text. Month print: course name in calendar
cells + an Assignment Detail section below the calendar with body text.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Course detail API now returns item body text and quiz questions.
Print course renders body paragraphs and numbered quiz questions
with lettered choices (correct answers marked with ✓).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Month view toggle in planner (Mon-Fri calendar grid, 4-5 weeks)
- Prev/Next navigate by month when in month mode
- Print month report (landscape calendar layout)
- Print course button on course detail (all units + items)
- New GET /api/schedule-range endpoint for arbitrary date spans
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Streak tracker: server-side streak counting per kid, celebration overlay
when all today's tasks are done for the first time that day
- Kid themes: 5 colour palettes (blue/green/purple/orange/pink), picked via
colour dots on the kid home screen; parents can set it from the Kids panel
- Grading badge: Grading nav pill turns red and shows a pending count
- Print worksheets: 🖨 Worksheet button per word list in admin Spelling panel;
kids also get "Print my words" on the Spelling tab
- Parent feedback: gold 💬 badge on Today rows with unread comments; more
prominent callout styling inside the assignment view
- Planner drag-and-drop: tasks can be dragged between days; PATCH
/api/schedule/:id moves the task, drop zones highlight on hover
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Practice: fire streak badge, first-letter hint on wrong answers, missed-words drill session after practice completes.
Test: audio-only mode with listening state before input appears, larger re-read button.
Words: definition field (DB migration + admin editor + TTS playback).
TTS: replace speechSynthesis with local Piper via /api/tts, WAV cached in data/tts-cache, falls back to browser voice when Piper is not installed.
Dockerfile: switched to node:26-slim, installs Piper binary + en_US-lessac-medium voice.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New "Auto-schedule a course" section in the Weekly Planner lets you pick
a course, start date, and items-per-day, then fills the planner in order
skipping weekends. POST /api/schedule/auto handles the logic server-side.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Evidence on assignments: photo upload + notes + student self-note (needs help / not sure)
- Evidence required/optional/none flag per item; grading queue shows evidence with lightbox thumbnail
- Parent comment field when grading; shown to student on graded items
- Offline tasks: 3-state status (not started → in progress → done) with inline evidence form
- Retake policy per item: latest / highest / average — gradebook computes effective score accordingly
- Grading queue expanded to include lessons and practices (not just assignments) when evidence present
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- allow_retakes flag on items: quizzes, spelling tests, and assignments can all be retaken
- Quiz templates: save any quiz as a reusable template and load it into new items
- Score history: every quiz/test attempt logged to submission_history; click a gradebook score to see all attempts
- Due dates: optional due date per item, shown in course view and gradebook (overdue flagged with ⚠️)
- Gradebook CSV export: one-click download of any course gradebook
- Prerequisites: lock an item until a prior one is completed; locked items show 🔒 in course view
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>