| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
feat(quiz): explicit single- and multiple-response questions The quiz builder had no way to say whether a question is pick-one or select-all-that-apply. Storage already allowed several correct options and the grader already did an exact-set match, so multi-answer worked by accident: nothing enforced single-select when authoring, and the learner got no affordance telling the two apart. - Questions carry response_type (single|multiple) and tasks carry grading_mode (all_or_nothing|partial_credit). Both live in the opaque contents JSON, so there is no migration. - Content authored before this has neither field. resolve_response_type is the single place inference lives: 2+ correct options was always a de-facto select-all. Stored questions are never rewritten, and an unrecognised value falls back to inference so a typo cannot grade a 3-correct question as a pick-one. - Single-response marks and selects like a radio group; multiple keeps toggling and says so. Both get role/aria-checked and keyboard activation, and a circle-vs-square indicator. - Partial credit awards (correct - incorrect) / total_correct, clamped, so selecting everything scores 0 rather than full marks. Default stays all-or-nothing, which reduces to the previous formula exactly. - The server grader and the learner's preview grader run the same shared helper on each side, since a divergence there shows one score and stores another. - _strip_answer_key stamps the resolved response_type on the outgoing copy: the learner needs the affordance, and the key it would otherwise be inferred from has just been removed. | 28 天前 | |
refactor(web): convert physical direction utilities to logical ones Machine-applied output of scripts/codemod-logical.mjs across the app, plus the handful of transforms it cannot do safely. ml-/mr- become ms-/me-, pl-/pr- become ps-/pe-, left-/right- become start-/end-, and text-left, border-l and the rounded corners follow. This is a pure rename: every pair emits identical CSS under dir=ltr, verified against tailwindcss 4.2.1, so left-to-right rendering is unchanged and any visual diff there is a bug rather than a judgement call. Transforms carry a physical sign, so renaming them is wrong. Toggle knobs, hover nudges and sliding panels get explicit rtl: pairs, and motion x-offsets multiply by the direction factor from useDirection. The step wizard folds text direction into its slide axis so "next" always moves toward the inline end. Flyout menus needed a real fix rather than a rename. HoverMenu positioned itself with `left: rect.right + 8`, which is fine with the sidebar on the left but placed the menu past the viewport once the sidebar moved to the right edge — in Arabic the menus simply did not appear. It now anchors to whichever edge it grows away from, which also avoids needing the menu's width before it renders. The admin list tooltips had the same bug. Inline styles stay physical wherever the value comes from a measured DOM rect: those are already in viewport coordinates, so converting them would flip twice. Only static design offsets moved to logical properties. Also removes the dead nx-* classes on the 404 page. They came from a Nextra template and never existed in this project's Tailwind build, so they emitted nothing — including the ltr:/rtl: pair, which looked like RTL support but was not. | 1 个月前 | |
Merge pull request #1079 from learnhouse/feat/formative-assignments feat(assignments): add formative (ungraded) mode with model answers | 11 天前 | |
refactor(web): convert physical direction utilities to logical ones Machine-applied output of scripts/codemod-logical.mjs across the app, plus the handful of transforms it cannot do safely. ml-/mr- become ms-/me-, pl-/pr- become ps-/pe-, left-/right- become start-/end-, and text-left, border-l and the rounded corners follow. This is a pure rename: every pair emits identical CSS under dir=ltr, verified against tailwindcss 4.2.1, so left-to-right rendering is unchanged and any visual diff there is a bug rather than a judgement call. Transforms carry a physical sign, so renaming them is wrong. Toggle knobs, hover nudges and sliding panels get explicit rtl: pairs, and motion x-offsets multiply by the direction factor from useDirection. The step wizard folds text direction into its slide axis so "next" always moves toward the inline end. Flyout menus needed a real fix rather than a rename. HoverMenu positioned itself with `left: rect.right + 8`, which is fine with the sidebar on the left but placed the menu past the viewport once the sidebar moved to the right edge — in Arabic the menus simply did not appear. It now anchors to whichever edge it grows away from, which also avoids needing the menu's width before it renders. The admin list tooltips had the same bug. Inline styles stay physical wherever the value comes from a measured DOM rect: those are already in viewport coordinates, so converting them would flip twice. Only static design offsets moved to logical properties. Also removes the dead nx-* classes on the 404 page. They came from a Nextra template and never existed in this project's Tailwind build, so they emitted nothing — including the ltr:/rtl: pair, which looked like RTL support but was not. | 1 个月前 | |
Hide decorative icons from assistive tech | 6 个月前 | |
fix(web): rename catalogPagination module to avoid case collision CatalogPagination.tsx and catalogPagination.ts differed only in their leading capital, so case-insensitive filesystems collapsed the two and resolved the component's own import to the wrong module. That failed 'next build' and produced 45 spurious tsc errors across 25 files. Rename the helpers module to catalogPaginationUtils.ts. | 1 个月前 | |
feat(communities): allow opt-in YouTube embeds in discussion posts Communities can enable rich content in discussions, off by default. When enabled, students can embed a YouTube video in a post; the API checks the tiptap doc against an allowlist of node and mark types and only accepts a YouTube embed with a valid video id. Disabling the flag renders existing embeds as plain links instead of dropping the post. | 9 天前 | |
refactor(web): convert physical direction utilities to logical ones Machine-applied output of scripts/codemod-logical.mjs across the app, plus the handful of transforms it cannot do safely. ml-/mr- become ms-/me-, pl-/pr- become ps-/pe-, left-/right- become start-/end-, and text-left, border-l and the rounded corners follow. This is a pure rename: every pair emits identical CSS under dir=ltr, verified against tailwindcss 4.2.1, so left-to-right rendering is unchanged and any visual diff there is a bug rather than a judgement call. Transforms carry a physical sign, so renaming them is wrong. Toggle knobs, hover nudges and sliding panels get explicit rtl: pairs, and motion x-offsets multiply by the direction factor from useDirection. The step wizard folds text direction into its slide axis so "next" always moves toward the inline end. Flyout menus needed a real fix rather than a rename. HoverMenu positioned itself with `left: rect.right + 8`, which is fine with the sidebar on the left but placed the menu past the viewport once the sidebar moved to the right edge — in Arabic the menus simply did not appear. It now anchors to whichever edge it grows away from, which also avoids needing the menu's width before it renders. The admin list tooltips had the same bug. Inline styles stay physical wherever the value comes from a measured DOM rect: those are already in viewport coordinates, so converting them would flip twice. Only static design offsets moved to logical properties. Also removes the dead nx-* classes on the 404 page. They came from a Nextra template and never existed in this project's Tailwind build, so they emitted nothing — including the ltr:/rtl: pair, which looked like RTL support but was not. | 1 个月前 | |
feat: add the demo entry points, routes and tests /demo/status and /demo/enter, and the surfaces that use them: an entry card on onboarding and the org picker, a DEMO badge so a shared sandbox is never mistaken for one of your own organizations, and a banner that says what the demo is and counts down to the next reset. The banner is fixed to the top of the dash column rather than sitting in flow — several dash pages are h-screen with their own inner scrolling, so a strip in flow made the column taller than the viewport, and the bottom edge is where the save bars and the mobile menu live. Copy goes through i18n like the rest of the app, and robots.txt asks the API whether an org is the demo before allowing it to be indexed. Tests cover the bundle, the sync, the exclusions, teardown and the routes. Several assert the negative case too, because a guard that is never exercised is a guard that quietly stops working: entering the demo must not consume a free-org slot, and the members filter must not touch a real organization. The 403 assertions are pinned to the guard's own message — RBAC answers 403 here as well, and one of these tests was passing for that reason rather than the intended one. Two unrelated test fixes, both the same shape: an opt-in live suite that failed instead of skipping on a developer machine. The Ollama tests checked that the port answered but not that the model was pulled, and the superadmin token tests imported the Enterprise package without forcing the deployment mode that makes those code paths reachable. DEMO_STACK.md documents running it locally; the scripts generate their own secrets rather than carrying any. | 1 个月前 | |
feat(branding): retire the Unsplash picker and use duotone icons Drop the Unsplash image picker from every upload surface (course, podcast, community, board and playground thumbnails, the editor image block, the sign-in background) and from the CLI setup flow, along with the unsplash-js dependency and its access key. Existing content and sign-in backgrounds that were picked from Unsplash keep rendering with their attribution; the picker is simply no longer offered. AI-generated backgrounds now upload as a custom photo instead of being stored as a remote URL. Switch the branding page's phosphor icons to the duotone weight, and cover the square logo upload wrapper. Also refresh apps/api/uv.lock, which the release bump left one version behind. | 9 天前 | |
feat: translations, feedback and improvements to the header | 7 个月前 | |
refactor: replace framer-motion with motion/react; update package dependencies and clean up imports | 6 个月前 | |
feat(web): Arabic typography, mirrored icons, and per-block content direction Everything a right-to-left locale needs beyond flipped margins. Arabic is set in Tajawal, forced rather than offered as a fallback. Layering it at the end of the stack does not work here: next/font injects a local "<Family> Fallback" face for CLS, and on macOS that adjusted system font covers Arabic — so per-glyph fallback resolved there and the Arabic webfont was never downloaded. Tajawal carries a Latin subset, so forcing it keeps a mixed screen in one typeface. It overrides the org's custom font too, which needs !important because that font is an inline style, and needs to exist in both @layer utilities and @layer base because the base font-family is declared in both — an override in one layer alone loses on html and body while still winning on their descendants. The same duplication applies to letter-spacing: the -0.02em tracking is tuned for Latin and crushes the joins between Arabic letters. Directional icons mirror via one CSS rule keyed on lucide's per-icon classes, with a data-dir-flip opt-in for libraries that emit none. That beats swapping components at ~250 call sites, each of which would need a mounted gate to avoid a hydration mismatch and would leave every new site a fresh bug. Vertical icons and media transport are deliberately excluded — play and skip are left-to-right everywhere. Authored content picks its own direction, because the UI language and the content language are independent: an English-UI org can host Arabic courses. Rich text uses unicode-bidi: plaintext, which resolves per block from the first strong character. That was the deciding factor over a TipTap extension, which would have written a new attribute into every paragraph of every stored activity and into the Yjs collab stream. Applied narrowly — a blanket rule would break UI strings starting with a digit. Slugs and hostnames are pinned left-to-right, since bidi-reordering a URL produces a different address. Some surfaces stay dir="ltr" because mirroring them is worse than not: CodeMirror computes its geometry physically and code is LTR by language spec; video.js and the audio scrubbers style controls physically, so a flipped DOM fills the progress bar and the buffered overlay in opposite directions; recharts emits absolute SVG coordinates, so flipping moves labels but not the ticks they belong to; the board canvas shares absolute coordinates live between collaborators; and math is LTR by specification. The certificate keeps its fixed layout because html2canvas reproduces logical properties poorly, and gains an await on document.fonts.ready — without it a certificate rasterised before the webfont loads is written to PDF with fallback glyphs, which nobody catches because the on-screen version looks fine. | 1 个月前 | |
refactor(web): convert physical direction utilities to logical ones Machine-applied output of scripts/codemod-logical.mjs across the app, plus the handful of transforms it cannot do safely. ml-/mr- become ms-/me-, pl-/pr- become ps-/pe-, left-/right- become start-/end-, and text-left, border-l and the rounded corners follow. This is a pure rename: every pair emits identical CSS under dir=ltr, verified against tailwindcss 4.2.1, so left-to-right rendering is unchanged and any visual diff there is a bug rather than a judgement call. Transforms carry a physical sign, so renaming them is wrong. Toggle knobs, hover nudges and sliding panels get explicit rtl: pairs, and motion x-offsets multiply by the direction factor from useDirection. The step wizard folds text direction into its slide axis so "next" always moves toward the inline end. Flyout menus needed a real fix rather than a rename. HoverMenu positioned itself with `left: rect.right + 8`, which is fine with the sidebar on the left but placed the menu past the viewport once the sidebar moved to the right edge — in Arabic the menus simply did not appear. It now anchors to whichever edge it grows away from, which also avoids needing the menu's width before it renders. The admin list tooltips had the same bug. Inline styles stay physical wherever the value comes from a measured DOM rect: those are already in viewport coordinates, so converting them would flip twice. Only static design offsets moved to logical properties. Also removes the dead nx-* classes on the 404 page. They came from a Nextra template and never existed in this project's Tailwind build, so they emitted nothing — including the ltr:/rtl: pair, which looked like RTL support but was not. | 1 个月前 | |
Merge pull request #1084 from learnhouse/feat/community-rich-content Add opt-in YouTube embeds to community discussions | 8 天前 | |
feat(branding): redesign the branding settings and add a square logo Rebuild the organization branding page around five tabs (logos, theme, sign-in page, social & links, previews). Every asset now sits next to miniatures of the real surfaces it lands on (public header, certificate, sign-in panel, organization switcher, dashboard sidebar, browser tab, explore card, link preview, footer), drawn live with the org's own uploads and theme so admins see the result before saving. Icons move from lucide to phosphor across the page. Add a square logo asset for the places that render the brand in a fixed square box. It is stored on the org config next to the favicon (customization.general.square_logo_image, v1 general.square_logo_image), uploaded through PUT /orgs/{id}/square_logo, and consumed through a shared OrgSquareLogo component whose fallback chain is square logo, then the wide logo letterboxed, then the caller's placeholder. The sign-in panel, mobile auth header, org hub, subscriptions and billing headers, profile org switcher, dashboard sidebar and mobile menu, and the certificate preview all use it. Remove the unused OrgEditImages component and allow *.lvh.me as a dev origin so a local multi-tenant run works under next dev. | 9 天前 | |
refactor(web): convert physical direction utilities to logical ones Machine-applied output of scripts/codemod-logical.mjs across the app, plus the handful of transforms it cannot do safely. ml-/mr- become ms-/me-, pl-/pr- become ps-/pe-, left-/right- become start-/end-, and text-left, border-l and the rounded corners follow. This is a pure rename: every pair emits identical CSS under dir=ltr, verified against tailwindcss 4.2.1, so left-to-right rendering is unchanged and any visual diff there is a bug rather than a judgement call. Transforms carry a physical sign, so renaming them is wrong. Toggle knobs, hover nudges and sliding panels get explicit rtl: pairs, and motion x-offsets multiply by the direction factor from useDirection. The step wizard folds text direction into its slide axis so "next" always moves toward the inline end. Flyout menus needed a real fix rather than a rename. HoverMenu positioned itself with `left: rect.right + 8`, which is fine with the sidebar on the left but placed the menu past the viewport once the sidebar moved to the right edge — in Arabic the menus simply did not appear. It now anchors to whichever edge it grows away from, which also avoids needing the menu's width before it renders. The admin list tooltips had the same bug. Inline styles stay physical wherever the value comes from a measured DOM rect: those are already in viewport coordinates, so converting them would flip twice. Only static design offsets moved to logical properties. Also removes the dead nx-* classes on the 404 page. They came from a Nextra template and never existed in this project's Tailwind build, so they emitted nothing — including the ltr:/rtl: pair, which looked like RTL support but was not. | 1 个月前 | |
fix(security): harden containers, dependencies, and URL handling - Move Docker images to python:3.14.7-alpine3.24 (apk instead of apt), uv/bun copied in from their images instead of pip install/curl, frozen installs, pip removed from runtime - Drop llama-index-core for a small tiktoken chunker, cutting ~30 transitive deps - Pin pillow, refresh JS lockfiles, add stricter URL/redirect validation on web - Canonicalize SQLite storage paths before authorizing course access, use full SHA-256 for the analytics cache key - Add regression tests for chunking, URL guards, and path containment | 12 天前 | |
fix(security): harden containers, dependencies, and URL handling - Move Docker images to python:3.14.7-alpine3.24 (apk instead of apt), uv/bun copied in from their images instead of pip install/curl, frozen installs, pip removed from runtime - Drop llama-index-core for a small tiktoken chunker, cutting ~30 transitive deps - Pin pillow, refresh JS lockfiles, add stricter URL/redirect validation on web - Canonicalize SQLite storage paths before authorizing course access, use full SHA-256 for the analytics cache key - Add regression tests for chunking, URL guards, and path containment | 12 天前 | |
refactor(web): convert physical direction utilities to logical ones Machine-applied output of scripts/codemod-logical.mjs across the app, plus the handful of transforms it cannot do safely. ml-/mr- become ms-/me-, pl-/pr- become ps-/pe-, left-/right- become start-/end-, and text-left, border-l and the rounded corners follow. This is a pure rename: every pair emits identical CSS under dir=ltr, verified against tailwindcss 4.2.1, so left-to-right rendering is unchanged and any visual diff there is a bug rather than a judgement call. Transforms carry a physical sign, so renaming them is wrong. Toggle knobs, hover nudges and sliding panels get explicit rtl: pairs, and motion x-offsets multiply by the direction factor from useDirection. The step wizard folds text direction into its slide axis so "next" always moves toward the inline end. Flyout menus needed a real fix rather than a rename. HoverMenu positioned itself with `left: rect.right + 8`, which is fine with the sidebar on the left but placed the menu past the viewport once the sidebar moved to the right edge — in Arabic the menus simply did not appear. It now anchors to whichever edge it grows away from, which also avoids needing the menu's width before it renders. The admin list tooltips had the same bug. Inline styles stay physical wherever the value comes from a measured DOM rect: those are already in viewport coordinates, so converting them would flip twice. Only static design offsets moved to logical properties. Also removes the dead nx-* classes on the 404 page. They came from a Nextra template and never existed in this project's Tailwind build, so they emitted nothing — including the ltr:/rtl: pair, which looked like RTL support but was not. | 1 个月前 | |
Fix: missing hardcoded strings to translation + add Slovak translation | 3 个月前 | |
refactor(web): convert physical direction utilities to logical ones Machine-applied output of scripts/codemod-logical.mjs across the app, plus the handful of transforms it cannot do safely. ml-/mr- become ms-/me-, pl-/pr- become ps-/pe-, left-/right- become start-/end-, and text-left, border-l and the rounded corners follow. This is a pure rename: every pair emits identical CSS under dir=ltr, verified against tailwindcss 4.2.1, so left-to-right rendering is unchanged and any visual diff there is a bug rather than a judgement call. Transforms carry a physical sign, so renaming them is wrong. Toggle knobs, hover nudges and sliding panels get explicit rtl: pairs, and motion x-offsets multiply by the direction factor from useDirection. The step wizard folds text direction into its slide axis so "next" always moves toward the inline end. Flyout menus needed a real fix rather than a rename. HoverMenu positioned itself with `left: rect.right + 8`, which is fine with the sidebar on the left but placed the menu past the viewport once the sidebar moved to the right edge — in Arabic the menus simply did not appear. It now anchors to whichever edge it grows away from, which also avoids needing the menu's width before it renders. The admin list tooltips had the same bug. Inline styles stay physical wherever the value comes from a measured DOM rect: those are already in viewport coordinates, so converting them would flip twice. Only static design offsets moved to logical properties. Also removes the dead nx-* classes on the 404 page. They came from a Nextra template and never existed in this project's Tailwind build, so they emitted nothing — including the ltr:/rtl: pair, which looked like RTL support but was not. | 1 个月前 | |
chore(lint): stage-2 backlog cleanup - config fix + autofix sweep - Disable no-undef for TS files (49 false positives: React, NodeJS, RequestInit and other TS global types; tsc owns this check) - eslint --fix sweep: removes 88 unused imports + misc auto-fixables across 72 files 1225 -> 993 problems. tsc error set verified identical to base (one png-import error disappears because its unused import was removed). | 3 个月前 | |
fix(UserAvatar): handle image loading errors and reset error state | 3 个月前 | |
refactor(auth, users): remove UserReadMinimal model and enhance security measures in user authentication and email verification processes | 4 个月前 | |
refactor(web): convert physical direction utilities to logical ones Machine-applied output of scripts/codemod-logical.mjs across the app, plus the handful of transforms it cannot do safely. ml-/mr- become ms-/me-, pl-/pr- become ps-/pe-, left-/right- become start-/end-, and text-left, border-l and the rounded corners follow. This is a pure rename: every pair emits identical CSS under dir=ltr, verified against tailwindcss 4.2.1, so left-to-right rendering is unchanged and any visual diff there is a bug rather than a judgement call. Transforms carry a physical sign, so renaming them is wrong. Toggle knobs, hover nudges and sliding panels get explicit rtl: pairs, and motion x-offsets multiply by the direction factor from useDirection. The step wizard folds text direction into its slide axis so "next" always moves toward the inline end. Flyout menus needed a real fix rather than a rename. HoverMenu positioned itself with `left: rect.right + 8`, which is fine with the sidebar on the left but placed the menu past the viewport once the sidebar moved to the right edge — in Arabic the menus simply did not appear. It now anchors to whichever edge it grows away from, which also avoids needing the menu's width before it renders. The admin list tooltips had the same bug. Inline styles stay physical wherever the value comes from a measured DOM rect: those are already in viewport coordinates, so converting them would flip twice. Only static design offsets moved to logical properties. Also removes the dead nx-* classes on the 404 page. They came from a Nextra template and never existed in this project's Tailwind build, so they emitted nothing — including the ltr:/rtl: pair, which looked like RTL support but was not. | 1 个月前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 28 天前 | ||
| 1 个月前 | ||
| 11 天前 | ||
| 1 个月前 | ||
| 6 个月前 | ||
| 1 个月前 | ||
| 9 天前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 9 天前 | ||
| 7 个月前 | ||
| 6 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 8 天前 | ||
| 9 天前 | ||
| 1 个月前 | ||
| 12 天前 | ||
| 12 天前 | ||
| 1 个月前 | ||
| 3 个月前 | ||
| 1 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 4 个月前 | ||
| 1 个月前 |