Swap the 📚 emoji in the header for an inline SVG mark - a rounded
square with a play triangle, echoing that this is a video-course
library. Uses var(--accent)/var(--bg-secondary) rather than fixed
colors so it re-colors automatically with whichever of the app's
theme presets is active, same as everything else in the header.
Applied consistently across all 5 templates; the small secondary
footer brand mark (still plain 📚 text) is untouched - this only
replaces the prominent header lockup.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Three comparison-driven upgrades against typical course/media platforms:
- Lesson page gets a collapsible "Lessons in this section" list so you
can jump between siblings without leaving the page, instead of only
global Prev/Next buttons. find_lesson_in_tree() now also returns the
DirectoryNode that owns the lesson (its .lessons list is the sibling
set) since DirectoryNode has no parent pointer; a new
get_section_lessons() resolves each sibling's progress the same way
view_lesson() already does for the current lesson.
- Course tree section headers show "X/Y watched" instead of a flat item
count, via DynamicCourseParser._calculate_completion_stats (already
computed recursive completion for any node, just never called
per-section) exposed as a Jinja global.
- Library browser gets a list/grid toggle for browsing large category
folders by poster-style thumbnail instead of a 32px-icon list.
Client-side only, persisted via localStorage, defaults to the
existing list view; both folder browsing and search results already
funnel through the same render function so grid mode covers both.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
view_lesson() called update_lesson_progress() with no arguments on
every page load, which defaults completed=False and progress_seconds=0
and silently overwrote whatever was already saved - just opening an
already-watched lesson reset its progress/completed state unless the
client happened to report real values within the next 15 seconds.
Replaced with touch_lesson_accessed(), which only bumps last_accessed.
Also fixes the "Resume from Xs" feature this fed into: it read a value
that was never populated on this route, and even when given a real
one, checked activeMedia.duration synchronously before metadata had
loaded, so it silently never applied. Now waits for loadedmetadata (or
resolves immediately if already available) and defers to an explicit
note-timestamp jump (?t=) when both are present.
Notes Hub gets a live search box filtering by note text, lesson title,
or course name (client-side, no reload), with a "no notes match" state
- useful now that a single category folder can hold dozens of courses'
worth of timestamped notes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replace the single free-text note per lesson with a list of timestamped
note entries, migrated transparently from the old format. Adds a
floating notes panel on the lesson page with an always-reachable
quick-capture bar (press N to pause and jump in), inline edit/delete,
click-to-seek timestamps, and a live "Saved Xs ago" indicator. Notes
Hub, the study guide export, and the Outline push all updated to
render multiple timestamped notes per lesson.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Templates only, no backend changes:
- Fixed the real bug: JS-generated status/loading text and a few component
styles used hardcoded hex (#007acc, #999/#666, #28a745, #ff6b6b) instead
of the CSS variables the theme system already provides, so they didn't
track the user's chosen theme/accent - most visible on Light or any named
theme other than the default. Added --success/--error to all 5 templates'
:root blocks and replaced every non-:root occurrence. Left the
.lesson-type badge colors and generic UI chrome grays alone (intentional,
not theme-dependent). Verified live by switching to Nord and Light themes.
- Dashboard cards (Library Stats, Next Up, Pick Back Up, Recently Added,
Continue Watching, Recently Viewed) are now collapsible, state remembered
per device via localStorage - the no-course dashboard had grown to 7
stacked cards.
- Course page's three action buttons get consistent compact sizing and
icons instead of a plain stack.
- Library browser and transcript search show shimmering skeleton rows
instead of bare "Loading..."/"Searching..." text while a fetch is in
flight.
- Hover treatment made consistent: transcript results get the same lift
.lesson-item already had, heatmap cells get a GitHub-style hover
scale-up, and Notes Hub links get a background-chip hover.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Six more dashboard features, all built on the per-course progress-file
scanning infrastructure from earlier sessions:
- Notes Hub (/notes): every lesson note across the library in one place,
newest first, reusing the existing /recent/open cross-course jump route.
- Transcript search: opt-in checkbox on the Library search box, searching
inside .srt/.vtt files. Kept cheap by doing a raw substring match as the
filter step and only extracting a snippet for files that actually match.
- Stale course nudges ("Pick Back Up"): courses with progress that haven't
been touched in 14+ days.
- Study guide export: compiles a course's notes into one downloadable
markdown file, section/lesson structure preserved.
- Next Up queue: an ordered, persisted "what to tackle next" list with
up/down reordering and a queue button on every course row.
- Activity heatmap: a 90-day contribution-style calendar in the Library
Stats card.
_scan_library_activity() now does one walk over every course's progress
file per dashboard load; library stats, stale courses, and the heatmap all
derive from that single scan instead of three independent ones.
Also refactored the duplicated lesson-progress-key lookup (used in three
places now) into _resolve_lesson_progress_key(), and flagged a pre-existing
bug found along the way (not fixed here, kept out of scope): subtitle files
never actually attach to a Lesson object, so the video player's caption
track has never had anything to render.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Lesson page's global keyboard shortcuts (space/arrows for play-pause/
seek/volume) fired regardless of focus, so typing a space in the notes
textarea toggled the video instead of typing. Now skipped entirely
whenever a text field/select has focus.
- Outline topics no longer spawn their own top-level collection each time.
A topic is now a document inside one fixed, user-configured collection
(Settings -> Outline Integration -> Default collection), and a lesson's
note becomes a child document nested under its topic - matching how
notes actually get organized in a real Outline instance instead of
cluttering the collections list with one per topic.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Pick a topic on a lesson's note and it pushes to that topic's collection
in a self-hosted Outline instance when you leave the page; notes without a
topic stay local-only. Settings gets a new Outline Integration card
(URL/token, Save, Test Connection).
Credential handling: the API token lives in its own outline_config.json
in DATA_DIR, deliberately kept out of the general settings flow (GET
/api/settings is fetched on every page load by theme.js - no place for a
secret to ride along). GET /api/outline/config only ever returns whether
it's configured, never the token itself.
The topic chooser reflects Outline's live collection list rather than a
locally cached copy, and resolves a newly-typed topic name to a real
collection immediately (find-by-name-or-create) rather than waiting until
the note is pushed. That's not just an optimization: the push itself fires
via navigator.sendBeacon() on pagehide, which can't read a response, so a
page that fires pagehide more than once for the same load (a back/forward-
cache restore, for instance) would otherwise re-send the same "create a
new topic" intent every time and spawn duplicate collections. Verified
live against a local mock Outline server that firing pagehide repeatedly
for the same lesson creates the collection/document once and updates
thereafter.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Eight usability additions on top of the mobile redesign: course cover-image
thumbnails, a debounced library search, a Continue Watching section split
from Recently Viewed, per-lesson playback speed control, an installable
PWA manifest/icons, name sort in the library browser, per-lesson notes, and
a bulk mark-course-as-watched action. Also fixes two real bugs found along
the way: lesson_view.html was missing the viewport meta tag (so lesson
pages weren't actually mobile-responsive), and update_lesson_progress
always overwrote the whole progress entry, which would have silently
deleted a saved note on the next routine autosave.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>