Commit Graph
100 Commits
Author SHA1 Message Date
Rotimi Best f6bd0a5b1a fix(404): redirect Go Home button to classroomio.com 2026-06-16 23:51:07 +01:00
Rotimi Best 219ed2eb92 feat(course): crop cover image to 280x200 on upload
The course cover Upload tab stored the raw file and relied on CSS
object-cover to fake the 280x200 frame at display time. Route uploads
through the existing ImageCropper at a 280/200 aspect ratio so the saved
asset matches the frame. Also teach the cropper UploadTrigger to accept
drag-and-drop so the prior drop-to-upload UX is preserved.
2026-06-16 23:50:55 +01:00
Rotimi Best a3a3c5bf48 fix(landing): render footer grid content for all template variants
The footer grid (brand, socials, columns, CTAs) was only rendered for
the 'classic' variant. For 7 other variants (minimal, bold, saas, tech,
studio, corporate, terminal) the grid content was defined as a snippet
but never invoked, silently dropping social links and other footer
content.
2026-06-16 23:49:33 +01:00
Rotimi Best f35fba36f6 fix(landing): remove excess opacity from studio course card preview image 2026-06-16 23:47:19 +01:00
Rotimi Best 0225614356 fix(ui): keep hovered active underline tab text legible in dark mode
The active tab's data-[state=active]:text-foreground rule outranked the
plain text-primary hover class, so hovering the active tab left white
text on the white hover pill (invisible in dark mode). Mark the hover
text-primary as important so the hovered active tab matches the hover
color of inactive tabs.
2026-06-16 23:39:23 +01:00
Rotimi Best b0e7641082 fix(landing): raise select dropdown z-index above editor overlay
Select.Content portals to document.body at z-50 but the landing page
editor wrapper sits at z-250, causing the dropdown to render behind
the overlay.
2026-06-16 23:25:58 +01:00
Rotimi Best 3f4e00bde8 fix(landing): rename catalog 'Programs' labels to 'Courses'
The org landing templates used 'Program(s)' in catalog headings, the
'view all' link, and the studio/terminal hero counters. That collided
with the app's actual Programs feature and confused users into thinking
a landing-page Program meant an app Program. Default copy now says
'Courses'; renamed the programsCount var to coursesCount to match.
2026-06-16 23:25:11 +01:00
Rotimi Best 58aea2ab15 fix(org-site): 404 on subdomains that don't resolve to an org
A subdomain like xxazzz.myclassroomio.com set isOrgSite=true with a null
org, and the course page fell back to resolving the org from the course
itself — so courses rendered under subdomains that don't exist. Guard
the (org-site) layout so an unresolved org site 404s instead.
2026-06-16 22:57:54 +01:00
Rotimi Best 05a684caa5 fix(course): preserve cover image aspect ratio in settings preview
The preview forced images into a 280x200 box with default object-fit,
squeezing non-matching aspect ratios. Use object-cover so the image
fills the frame at its true aspect ratio and crops overflow instead.
2026-06-16 22:49:38 +01:00
Rotimi Best 7022537471 fix(course): stop capping public course slug length
Public course slugs carry a -<timestamp> uniqueness suffix that pushes
them past the 80-char ZSlug cap, so the org-site lookup returned a 400
and the page fell through to the landing page instead of redirecting to
the first lesson. The course.slug column is unbounded, so validate the
public-course route param with a no-max ZCourseSlug.
2026-06-16 22:44:26 +01:00
Rotimi BestandCursor bbf02df65b fix(core): sanitize optional reward description in course metadata
Use sanitizeOptionalHtml for reward.description now that the DB type
allows it to be omitted, fixing @cio/core build failures.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 20:56:42 +01:00
Rotimi BestandCursor ab735505e5 fix(db): align course metadata types with validation schema
Make reward.description and instructor.coursesNo optional in the course
metadata JSON type so API builds match Zod validation.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 20:36:01 +01:00
Rotimi Best e9c41fa595 Merge branch 'main' of https://github.com/classroomio/classroomio 2026-06-16 20:25:28 +01:00
Rotimi Best 2350315321 fix(course): make coursesNo and reward description optional; render landing HTML safely
- Allow coursesNo to be omitted in instructor metadata (not required)
- Allow reward description to be omitted when reward object is present
- Replace raw {@html} with SafeHtmlContent in course-pricing,
  course-info-blocks, and embed landing-page components
2026-06-16 02:57:23 +01:00
Rotimi Best 38d912a0b1 docs: add early adopters blog post 2026-06-15 12:03:09 +01:00
Rotimi Best 3e9d8bf3f5 ui: email preview 2026-06-15 08:31:16 +01:00
Rotimi Best b67faada08 fix(storybook): make ResourceListRow course demo responsive
Mirror the course list-row fix in the demo story: make the group a
container query that stacks below 48rem, cap tags at 3 with a +N pill,
and render the Default story full-width (padded) so both the stacked and
column layouts are visible. Uses unprefixed utilities since Storybook
generates those from its own Tailwind scan (prefixed classes only ship
in the prebuilt @cio/ui output.css).
2026-06-15 08:30:05 +01:00
Rotimi Best 0b80440c9c fix(course): make course list-row responsive and cap visible tags
The rigid 7-track grid always rendered in one row, so when the list was
squeezed the tag track shrank below the badge widths and tags overflowed
into the lessons/exercises column. Make the ResourceListRow.Group a
container query and stack each row's grid into a single column below
48rem, passing the column template via a CSS variable so the responsive
override applies. Also cap the row at 3 tags with a +N pill.
2026-06-15 08:29:44 +01:00
Rotimi Best 8503e8595e fix(org-site): size section-cap icons in the landing-page editor
The moving-icons family sizes its SVG via a numeric `size` prop and only
forwards `class` to the wrapper div, so the cap's `size-3.5` class never
reached the SVG and icons rendered at the default 24px, overflowing the
cap. Pass size={14} to the cap icon.
2026-06-14 19:07:19 +01:00
Rotimi Best f74788ab45 fix(storybook): import email templates from isolated entry to avoid Vite bundling error 2026-06-14 18:35:03 +01:00
Rotimi Best 2a6275ea0f refactor(email): unify email template shell between production and Storybook 2026-06-14 18:21:26 +01:00
Rotimi Best e89023adf5 fix(org-site): redirect students away from the admin dashboard to the LMS
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).
2026-06-14 10:31:29 +01:00
Rotimi Best 0c395e9f1b fix(org-site): stabilize landing-page nav auth button
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.
2026-06-14 10:10:14 +01:00
Rotimi Best a411c50526 fix(ui): correct underline tab hover text color
The hovered tab used text-primary-foreground for inactive tabs, which is
near-white — the same token as the hover pill background — so the label
disappeared. Drop the override and keep the hover label on text-primary.
2026-06-14 10:09:55 +01:00
Rotimi Best c97284f870 feat(domains): support apex domains and non-.com TLDs
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.
2026-06-14 10:09:40 +01:00
Rotimi Best ee4d4922d6 fix: return clear error when social auth provider is not configured 2026-06-14 09:56:30 +01:00
Rotimi Best 64571dd5ec docs: add stage and commit skill, format 2026-06-14 09:24:15 +01:00
Rotimi Best 0975f82c8b fix(widget): powered toggle persistence, copy buttons, version dates, showLessonsCount, color pickers, and loading spinner 2026-06-13 11:24:20 +01:00
Rotimi Best 094683b551 fix: pull user from locals and theme picker default 2026-06-13 09:55:34 +01:00
Rotimi Best 977176dbe2 fix: remove dependency from tenant router 2026-06-12 10:06:06 +01:00
Rotimi Best cd3b252ea2 fix(tenant-router): build @cio/utils before wrangler bundle
Wrangler resolves @cio/utils/constants/* from dist/. Run utils build in
predeploy, predev, and pretypecheck so Cloudflare deploy does not fail with
a missing embeds module.
2026-06-12 02:27:49 +01:00
Rotimi Best ec6d97c482 chore: remove unused log 2026-06-12 02:25:02 +01:00
Rotimi Best 0b5fc27d1c refactor(tenant-router): import embed routes from @cio/utils
Replace the cross-package relative source import with the workspace
package subpath so embed routing uses the canonical constants module.
2026-06-12 02:23:48 +01:00
Rotimi Best 367ddcddd7 feat(embeds): add embed.classroomio.com public widget host
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.
2026-06-12 02:06:37 +01:00
Rotimi Best c868b2f23f perf: edge-cache static assets and shrink org-route bundle
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.
2026-06-12 01:51:05 +01:00
Rotimi Best 8fd80e9dc5 fix: ui prefix breaking sidebar 2026-06-12 01:03:17 +01:00
Rotimi Best 6c918329cb chore: update lock file 2026-06-11 23:21:38 +01:00
Rotimi Best 899a9f2b51 fix: apply ui: prefix to all packages/ui components and automate it 2026-06-11 23:18:40 +01:00
Rotimi Best 89e44d4fab fix(newsfeed): load saved content when reopening the edit modal
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.
2026-06-11 20:32:39 +01:00
Rotimi Best bbd243d214 fix: render newsfeed content as html 2026-06-11 16:01:10 +01:00
Rotimi Best 52d9bf18c3 feat: add sequential progression and video watch lesson completion
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.
2026-06-11 14:48:34 +01:00
Rotimi Best c8154409f5 feat: improve LMS course discovery and enrollment UX
- 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
2026-06-11 14:22:18 +01:00
Rotimi Best 4e24e3cb63 docs: add content to stubbed pages 2026-06-10 09:32:45 +01:00
Rotimi Best dcb5cfbc7b feat: make explore more visible in lms 2026-06-10 09:18:20 +01:00
Rotimi Best 02ae9b81b8 fix: get the type of settings 2026-06-10 09:08:57 +01:00
Rotimi Best ab25b8e5d7 chore: improve enrollment documentation 2026-06-10 02:56:00 +01:00
Rotimi Best bb88847570 chore: performance tracking on root routes 2026-06-09 22:43:07 +01:00
Rotimi Best 8961912530 fix: improve plyr render for youtube video 2026-06-09 22:42:26 +01:00
Rotimi Best 201cda75ca fix: make Ask AI button opaque in dark mode 2026-06-09 22:40:24 +01:00
Rotimi Best cf79b24dc3 fix: restore list markers in lesson editor to match saved prose output
Tailwind's list-style reset stripped bullets and numbers while editing. Mirror dashboard prose list styles in the editor CSS instead.
2026-06-09 22:28:28 +01:00
Rotimi Best f615b06bf8 refactor: update landing page metadata schema 2026-06-08 00:52:37 +01:00
Rotimi Best cde7b501d1 docs: invite students guide 2026-06-08 00:47:33 +01:00
Rotimi Best 82d2ba4014 chore: increase the cache for mutable manifest 2026-06-08 00:47:08 +01:00
Rotimi Best e1e56a70be fix: courseNo vs coursesNo mismatch on course save 2026-06-08 00:45:00 +01:00
Rotimi Best 5f07184b29 fix: NoSuchKey, The specified key does not exist. 2026-06-07 23:47:14 +01:00
Rotimi Best d7225a77b2 chore: move templates images to cdn 2026-06-07 23:23:27 +01:00
Rotimi Best 03dcf812a5 feat: fit ai chat trigger to center of page
- 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.
2026-06-07 23:21:19 +01:00
Rotimi Best 4eee866e85 feat: make video uploads faster
- 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
2026-06-07 23:20:42 +01:00
Rotimi Best 27a612e92f feat: let AI chat read lesson video transcripts on demand
Add get_lesson_transcript (teacher) and read_lesson_transcript (student)
tools that fetch a lesson's uploaded-video transcript from media_transcript
by the video assetIds. The agents call them only when a question is about
the video's spoken content, so text-only chats keep a cacheable prefix.
2026-06-07 22:30:12 +01:00
Rotimi Best 4221f93584 feat: enhance HLS upload process with concurrency control and presign batching
- 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.
2026-06-07 21:17:47 +01:00
Rotimi Best 48ce4d4d64 chore: update queue dashboard authentication and improve course component handling
- 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.
2026-06-07 20:32:37 +01:00
Rotimi Best 3561dc0fda fix: undo org search from tenant-router 2026-06-07 19:35:24 +01:00
Rotimi Best 2f1b6376ff ci: use pnpm/action-setup@v4 so CI reads the pinned pnpm version
The openapi/storybook/embeds workflows pinned pnpm 8 and cypress pinned
pnpm 6 via action-setup@v2. The repo's lockfile is lockfileVersion 9.0
(written by the pinned packageManager pnpm@10.19.0), which pnpm 8/6 cannot
read — it ignored the lockfile and --frozen-lockfile failed with
ERR_PNPM_LOCKFILE_CONFIG_MISMATCH. Drop the explicit version and let
action-setup@v4 resolve pnpm from the packageManager field, matching the
already-working publish-mcp / publish-course-app workflows.
2026-06-07 19:05:15 +01:00
Rotimi Best 59164c8cee chore: untrack .claude local artifacts
.claude/skills is now a symlink to the repo-root skills/ dir, so the
duplicate tracked copy under .claude/skills/ no longer exists on disk and
showed as a permanent deletion. Drop it from tracking and gitignore the
symlink plus the scheduled_tasks.lock runtime file.
2026-06-07 18:57:45 +01:00
Rotimi Best fa6ca75f67 fix(core): declare moved deps (redis, pdf-parse, mammoth, jszip) in @cio/core
These were used by the redis client and document-parsing services that moved
from apps/api into packages/core, but the dependencies stayed in apps/api,
breaking the @cio/core build with TS2307 module-not-found errors. Move them
(and @types/pdf-parse) to where the code now lives.
2026-06-07 18:50:06 +01:00
Rotimi Best d5b07d356e feat: HLS streaming, transcripts, agent course generation & landing-page course previews (#663)
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
2026-06-07 18:43:05 +01:00
Rotimi Best a2e70779a0 fix: update Dockerfile to change port and memory settings 2026-06-06 09:24:19 +01:00
Rotimi Best 9f5c117fa8 docs: add walkthrough widget PRD (#661) 2026-05-29 14:22:04 +01:00
Rotimi Best 7ee544230f fix(dashboard): proxy API auth on custom domains 2026-05-28 08:57:28 +01:00
Rotimi Best 730bcee553 fix: send memberid on checkout 2026-05-27 20:44:56 +01:00
Rotimi Best 607377f191 fix: broken upgrade 2026-05-27 18:35:32 +01:00
Rotimi Best 591a56af2d fix: keep english terms in other translations 2026-05-27 15:28:19 +01:00
Rotimi Best a6331f0d6a fix: update translations 2026-05-27 15:17:05 +01:00
Rotimi Best d855b45eda fix: placeholder names inside {…} were translated 2026-05-27 14:57:21 +01:00
Rotimi Best 9082cbebf4 chore: add more agent upcoming features 2026-05-27 11:55:40 +01:00
Rotimi BestandCursor f0ef45fd67 fix(api): stop SSR org lookups from hitting the global rate limit
Resolve sessions before rate limiting so keys are per-user, exempt trusted server API key calls and get-session, and raise the GET /organization budget for layout loads.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-27 11:44:57 +01:00
Rotimi Best 78ab13f54b feat: preview templates, thumbnail features, 2 new org templates 2026-05-23 12:24:57 +01:00
Rotimi Best 4fdd4115b9 chore: marketing updates and video player improvement 2026-05-22 01:28:18 +01:00
Rotimi Best f0fad9512c chore: force pnpm i in upload api spec workflow 2026-05-21 22:59:55 +01:00
Rotimi Best 594bb615d8 debug: reorder comment 2026-05-21 22:34:11 +01:00
Rotimi Best e7bb0f2f26 debug: trigger ci 2026-05-21 22:30:51 +01:00
Rotimi Best 30e5f9cd54 chore: auto compact chat 2026-05-21 19:46:17 +01:00
Rotimi Best 4e4119d597 fix(agent): base64-envelope /agent/* bodies to bypass Render CF WAF 2026-05-21 19:43:23 +01:00
Rotimi Best a4d86dc3c2 debug: update proxy logs 2026-05-21 18:06:25 +01:00
Rotimi Best fbbce6b7c0 fix: ci fixes, tenant router debug, select width improvement 2026-05-21 17:52:28 +01:00
Rotimi Best 0657c32c97 feat: connect sentry to dash and api 2026-05-21 17:18:08 +01:00
Rotimi Best 25a4b1fd19 chore: improve width size on community ask page. 2026-05-21 16:40:05 +01:00
Rotimi Best 6f84002fa5 chore: community page fixes. 2026-05-21 16:38:35 +01:00
Rotimi Best 9435490c43 chore: improve posthog session identificaion 2026-05-21 15:08:40 +01:00
Rotimi Best 9043bede59 chore: handle case where theme is undefined 2026-05-21 14:11:30 +01:00
Rotimi Best ec92fc5083 fix: approximated not verifying domain, change request type for /agents
route
2026-05-21 14:05:37 +01:00
Rotimi Best c1bbd02f8e chore: improve ui experience of custom domain setup 2026-05-21 11:30:12 +01:00
Rotimi Best 61cbb258ea chore: move lms to myclassroomio.com and use approximated for custom
domain
2026-05-21 11:29:47 +01:00
Rotimi Best c090b7b4d6 fix: truncate long error message in ai chat 2026-05-21 11:02:46 +01:00
Rotimi Best c875f7bf86 chore: use primary color for sidebar active state 2026-05-21 10:42:56 +01:00
Rotimi Best 29b02c0011 chore: auto enroll, custom domain improvement, course pricing style improv. 2026-05-20 21:58:42 +01:00
Rotimi Best 38c0c0b4f0 feat: enhance tenant provisioning hook to support OAuth state resolution for organization enrollment 2026-05-20 17:12:00 +01:00
Rotimi Best f82e4ff4c4 debug: tenant provisioning hook 2026-05-20 16:50:07 +01:00
Rotimi Best f7570d9a21 refactor: remove request logging from createProfileHook for cleaner output 2026-05-20 16:35:32 +01:00
Rotimi Best 266f9f2b22 chore: simplify workflow for self hosters. 2026-05-20 16:33:11 +01:00