A student opening /org/[slug] on self-hosted stayed on the admin
dashboard. The existing routeUserToNextPage redirect is gated by
shouldRedirectOnAuth (login/signup/onboarding only) and runs once at app
init, so it never caught this route. Add a reactive guard in the admin
org layout that sends students to /lms, gated on
appInitApi.isInitializedAndReady so the role is actually known before
deciding (the student flag defaults to false while loading).
The auth button derived from stores that hydrate in stages, so it
flashed through Login -> Dashboard -> Go to LMS as isLoggedIn and the
role/basePath settled at different times. Gate the label on
appInitApi.isInitializedAndReady so it only renders once the account
(and thus role) has loaded, and show a spinner in the button while a
logged-in user's experience resolves. Add separate loading and disabled
flags to authAction and wire them through all theme navs.
Loosen the custom-domain regex to accept apex domains (1+ labels) in
addition to subdomains; the TLD already allowed any letters. Suppress
the CNAME record for apex hostnames since a CNAME is invalid at the zone
apex, and surface only the A record there. Drop the frontend apex
rejection and the misleading ".com" input suffix, and render the
domain-input prefix/suffix addons only when set.
Serve widgets from short public URLs via tenant-router with R2-backed
assets and runtime API_URL injection, so copy-paste snippets hide infra
paths. Update widget payloads, CSP, upload purge URLs, and dev-only API
inject; document format/stage workflow in AGENTS.md and fix ui-prefix-check
YAML syntax.
The /org/[slug] routes loaded 250+ JS chunks and, after a deploy, every
chunk missed Cloudflare's edge cache and stampeded the single origin (up
to 48s to serve one static file) — the "keeps loading" lockout.
- tenant-router: opt /_app/immutable/* into the edge cache via
cf.cacheEverything; setting Cache-Control alone never populated it
- course/api: mark the 14 API singletons /* @__PURE__ */ so the barrel
tree-shakes instead of dragging every API into each importer
- courses page: import directly instead of via the feature barrels
Cuts the courses route from 257 to 225 static chunks and keeps static
assets off the origin after the first edge fill.
Sync modal editor state from the updated feed on open, clear editFeed
on reset, and remount TextEditor so a second edit shows the latest HTML
instead of stale content.
Gate student content by completion order, track per-video watch progress
with seek enforcement, and auto-complete lessons at the configured
threshold. Show ring progress in the lesson header, keep watch progress
on rewatch, and sync progression locks client-side so the next item
unlocks after completion.
- Add paginated recommended courses to LMS dashboard (top 3, list
view)
and explore page (12 per page); exclude courses blocking new
students
- Polish course preview modal: remove padding, render requirements as
HTML, round banner image, secondary close button
- Redirect to course start page after successful enrollment instead of
/lms;
keep spinner active until navigation completes
- Auto-enable "allow new students" when a course is published
- Move Explore after My Learning in LMS sidebar nav
- Move Certificates after Content in course sidebar
- Add hiddenColumns prop to CourseListRow; show "Learn more" button
instead of actions menu in LMS explore context
- Updated the CSS classes for the AI assistant bar to adjust positioning based on the new side panel width variable.
- Introduced a new `onWidthPreview` prop in the SidePanelRail component to handle live resizing and update the side panel width dynamically.
- Added width state management in the SidePanelStore to ensure proper width handling during panel interactions.
- Improved layout handling in the course layout component to accommodate the new side panel width adjustments.
- Implemented a new endpoint to delete failed manual p1080 renditions, restoring the master manifest and updating metadata.
- Enhanced the HLS upload process with better error handling and cleanup for aborted uploads.
- Introduced tests for HLS encoder functionality to ensure correct selection of rungs based on video source properties.
- Updated video upload component to manage reserved asset IDs and perform cleanup on abo
- Introduced a concurrency gate to limit parallel uploads, preventing R2 overload.
- Implemented batching for presign requests, allowing up to 1500 paths per request.
- Added content type mapping for HLS uploads to ensure correct MIME types.
- Refactored upload logic to utilize the new concurrency and presign features, improving reliability and performance.
- Replaced email/token-based authentication with HTTP Basic Auth for the queue dashboard in production.
- Updated course components to use courseId instead of courseSlug for better consistency.
- Adjusted role-based security checks to utilize isStudentExperience for conditional rendering.
- Enhanced view-as-student functionality to redirect correctly based on courseId.
New features:
- HLS video encoding/streaming (hls routes, hls-encoder) and a media
transcripts pipeline (transcripts route + media-transcript queries)
- AI assistant chat vs. run model: agent-course-generation worker/queue,
run-monitor pane, chat-changes card, content ask-ai bar
- Per-theme course-page previews across org landing-page themes (bold,
classic, corporate, editorial, etc.) with a shared course token/component set
- View-as-student mode and login-link / link-invite invite flows
Refactor:
- Extract packages/core consolidating config (env, storage, dashboard-url),
services and utils previously duplicated under apps/api/src/config
- Remove legacy agent service modules (chat-tools, chat-context,
context-window, document, fetch-url, lesson-content)
- Relax Cross-Origin-Resource-Policy to allow cross-origin media embedding
- Add format script to tenant-router so turbo run format covers it