| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
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. | 29 天前 | |
feat: add analytics feature | 6 个月前 | |
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. | 29 天前 | |
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. | 29 天前 | |
feat(i18n): resolve text direction from the active locale Adds the plumbing that makes direction a property of the language rather than something hardcoded, plus the codemod used to convert the codebase. Direction has to be resolved on the client. i18next's highest-priority detection source is localStorage, which the server cannot read, and reading the cookie in the root layout would deopt every page to dynamic rendering and still be wrong whenever the two disagree. So dir-init.js runs synchronously in <head> and writes lang/dir before the body paints — the same approach embed-bg.js already uses to avoid a flash. Its detection order mirrors lib/i18n.ts exactly, and the RTL list it carries is asserted against lib/direction.ts by a test, since it's a constant duplicated into a file the bundler never sees. AVAILABLE_LANGUAGES gains a `dir` field, so Persian works today and a future locale is a one-line change; nothing branches on "is Arabic". useDirection() exposes it to components along with a multiplier for physical offsets, and Radix's DirectionProvider makes align="end", collision flipping and arrow-key navigation direction-aware across ~40 call sites with no per-component edits. The codemod converts physical Tailwind utilities to logical ones. It parses with the TypeScript compiler API rather than running a regex over the file, because `pr-`, `mr-` and `pl-` appear in URLs, i18n keys and identifiers. space-x-*, divide-x* and inset-x-* are skipped — they already emit logical properties in v4 — and so is any 50% inset, which is a centering idiom whose paired translate may live in a sibling class or an inline style, and which lands off-centre in RTL if converted alone. Also drops tailwind.config.js. Tailwind v4 only loads it via an @config directive this project doesn't have, so it was dead — and a trap for anyone later adding an RTL plugin there. | 29 天前 | |
fix(web): make copilot math survive streaming, lists and RTL Five things the plain plugin wiring got wrong: - The model does not always use dollar delimiters. \( \) and \[ \] reached the reader as literal text with the backslashes stripped, so rewrite them before parsing — on one line when the formula sits inside a list item, table cell or blockquote, since a blank line would close the block it belongs to. - A display fence is only half-written while the answer streams. Left open, it swallows every chunk after it and paints the rest of the message as one parse error, so hold the equation back until it closes. - copilot.tsx is reachable from the org menu, which put KaTeX and its stylesheet in the bundle of every page. Load them on demand the first time a message actually contains math. - KaTeX positions its atoms as inline-blocks and sets no direction of its own, so an RTL page rendered equations backwards. Lock it the same way the editor's math block does. - Every streamed chunk re-rendered the whole transcript, re-parsing finished answers. Memoise the assistant message and give it a stable empty sources array. Long display math now scrolls inside the bubble instead of widening it. | 14 天前 | |
fix(feature-gate): let admins re-enable AI after disabling it Turning off "Enable AI Features" in Org Settings -> AI trapped admins on a "Feature Disabled" card with no way back: the toggle lived inside FeatureGate and vanished along with everything else it gated. On self-hosted the only way out was hitting the config API directly. FeatureGate now takes an opt-in allowWhenDisabled prop that suppresses just the admin-disabled card; plan gating still shows the upgrade card as before. OrgEditAI opts in and shows a notice when AI is off instead of hiding the switch. Gate branching moved into a pure resolveGateReason() so it's testable under bun, since apps/web tests have no React runtime. Also stopped useResolvedFeature from applying its plan fallback while the org is still loading: it was firing upgrade-shown analytics with a guessed plan before real entitlements arrived, including on public org pages. Added feature-gate-lockout.test.mjs (15 tests) and one i18n key in en.json/ar.json. eslint and tsc clean on changed files; not tested in a live browser. Fixes #1061 | 10 天前 | |
feat(web): dashboard UX — gating, leave-org, tooltips, first-chapter, Developers nav, usage upsell, delete-account danger zone, library sort UI | 2 个月前 | |
feat(web): dashboard UX — gating, leave-org, tooltips, first-chapter, Developers nav, usage upsell, delete-account danger zone, library sort UI | 2 个月前 | |
feat(web): dashboard UX — gating, leave-org, tooltips, first-chapter, Developers nav, usage upsell, delete-account danger zone, library sort UI | 2 个月前 | |
fix(feature-gate): let admins re-enable AI after disabling it Turning off "Enable AI Features" in Org Settings -> AI trapped admins on a "Feature Disabled" card with no way back: the toggle lived inside FeatureGate and vanished along with everything else it gated. On self-hosted the only way out was hitting the config API directly. FeatureGate now takes an opt-in allowWhenDisabled prop that suppresses just the admin-disabled card; plan gating still shows the upgrade card as before. OrgEditAI opts in and shows a notice when AI is off instead of hiding the switch. Gate branching moved into a pure resolveGateReason() so it's testable under bun, since apps/web tests have no React runtime. Also stopped useResolvedFeature from applying its plan fallback while the org is still loading: it was firing upgrade-shown analytics with a guessed plan before real entitlements arrived, including on public org pages. Added feature-gate-lockout.test.mjs (15 tests) and one i18n key in en.json/ar.json. eslint and tsc clean on changed files; not tested in a live browser. Fixes #1061 | 10 天前 | |
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. | 29 天前 | |
fix(editor,assignments): follow-up fixes for H5P resize, due dates - Re-chain due_date migration onto dev's real head; the old down_revision forked the graph, so `alembic upgrade head` aborted with MultipleHeads instead of applying it. - Fix unsaved-changes guard: it stripped `height` from H5P nodes unconditionally, so a custom-mode resize (which touches no other attribute) vanished with no save prompt. - Add a 3px drag threshold, stop arrow keys from committing past the clamp, and drop pointercancel instead of committing it; add aria-valuemin/max/text to the resize handle. - Trim stored deadlines with a time component to YYYY-MM-DD so the date input isn't blank and doesn't get silently overwritten on save. - Pin the H5P SIZE_MODES test to an ordered list; the old assertion missed a dropped 'auto'. - Mark due_date optional/nullable in the offline OpenAPI snapshot. | 10 天前 | |
fix: high-severity bug fixes + backend security hardening Backend security (broken access control / IDOR): cross-org create checks for usergroups/courses/podcasts/boards, usergroup member validation, MagicBlocks authz + session ownership, RAG chat ownership, chapter mass-assignment relocation, reorder cross-org grafting, student self-grading + assignment due-date, org Explore field lockdown, collection same-org links, trail read-access, content_files default-deny. Backend security (injection/file/config): RAG arbitrary file read, import manifest traversal, upload_sqlite traversal, upload type-from-content, CSV formula injection (org+analytics exports), magic-link open redirect, email/CORS host pinning, CSRF fullmatch + dev fail-open, JWT purpose confusion, playground org resolution. Backend bugs: await on 2 broken AI endpoints, AI credit refunds on failure, cookie max_age fix, third_party_login guard, credit summary extra_limit, admin_seats purchased seats, trail step serialization, certificate completion count, upvote race, webhook log prune, join_org transaction. Frontend bugs: fetch-race guards, DOMPurify hook leak, stable useDebounce, useOnboarding persistence, OAuth login route, CourseProgress null-guards, AuthContext stale closure + token refresh, AIChatBot reducer payload, contributor id normalization. Verification: backend 2529 tests pass, app boots; frontend tsc clean. Tests updated to assert new behavior. | 2 个月前 | |
fix: sort folders by org mode and hide certificate copy Folder content sorted by position only, ignoring org sort_mode, so dashboard drag order didn't match the public library page. Certificate copy was shown based on the learner's certificate row rather than whether the course has certification enabled, so uncertified courses displayed misleading "no certificate" warnings. Shared sort comparator: apps/web/lib/library/sort.ts. New gate: useCourseCertification hook. | 18 天前 | |
i18n(ar): complete the Arabic locale and localize dates and prices Arabic now has full key coverage: 0 missing, and the 20 values still matching English are brand names and URLs. Getting there took three passes, because the gaps had three different causes. 305 keys existed but held untranslated English — the entire Library feature among them, which is why it read as completely untranslated. Another 221 were absent from en.json altogether: the code called t() with a key no locale had, so i18next fell through to the English defaultValue in the call and rendered that forever, in every language. Those are added to en.json too, so the other twenty locales can now translate them. And the onboarding sidebar rendered step titles straight from the English step definitions instead of through t(), while its sibling component did the same lookup correctly. Dates and numbers move to lib/format, which loads the dayjs locale centrally. That fixes a bug predating this branch: all twelve dayjs call sites extended relativeTime locally and none imported a locale, so "2 hours ago" was English in all 22 languages. Several call sites also hardcoded 'en-US', assignment timestamps used 'en-UK' — not a valid tag, so it silently fell back to the browser default — and the thumbnails branched on `i18n.language === 'fr' ? 'fr-FR' : 'en-US'`, giving French dates to French and English to everyone else. Numerals are pinned to Latin digits and the Gregorian calendar for Arabic and Persian, behind one function. Intl.NumberFormat('ar') resolves to Arabic-Indic digits on most ICU builds and ar-SA defaults to the Hijri calendar; neither suits a screen where prices and analytics sit next to Latin-digit values from the API, and Arabic regions are split on this anyway. Month names and relative times still localize. Also translates the sixteen aria-labels in the dashboard navigation, which were the last hardcoded strings in those files — screen reader users were hearing English labels in every language. | 29 天前 | |
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. | 29 天前 | |
fix(api): tighten authorization and input handling across service layer Several service-layer entry points took an org id or resource uuid straight from the request and used it as the tenant filter, with the only gate being whether the caller was signed in at all. Route those through the existing org_auth helpers so membership decides what is visible, and treat a non-member the same way an anonymous caller is already treated on the endpoints that legitimately serve public data. Also: - validate the import package id before it reaches any filesystem call - serve non-media content as octet-stream with an attachment disposition - fail closed when the Google OAuth audience cannot be verified, and honour both env var spellings for the client id - route Google sign-in through the shared session chokepoint so an enrolled second factor is challenged - reject refresh tokens presented on the session path - clamp values read out of stored/submitted editor JSON before use - stream and cap the response bodies fetched from operator-supplied URLs - verify the SMTP relay certificate, and scope the emailed base URL to known-good hosts instead of a catch-all pattern | 1 个月前 | |
Merge pull request #1008 from mchtter/fix/dash-admin-denial-ux fix(web): make dashboard access consistent instead of list-dependent | 25 天前 | |
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. | 29 天前 | |
Merge dev; fix CI (bun lockfile, lint gate), stop tracking super-admin - Merge origin/dev; resolve verify-email.tsx conflict (keep email-verification event inside dev's binding-free catch). - Add posthog-js to the bun lockfile (CI uses `bun install --frozen-lockfile`; the stale lock was failing Build + next-lint). Revert the vestigial pnpm-lock.yaml change. Drop the @codemirror/language dep that caused a duplicate-version type error under bun. - Super-admin (/admin) is no longer tracked: revert the admin call-sites and add PostHogAdminGuard to opt out of all capture (autocapture, replay, pageviews) on /admin routes. - Make the strict changed-files lint gate pass: clear pre-existing no-unused-vars / no-empty / no-undef in touched files, and downgrade the react-compiler backlog rules (#800) to warnings (matching web-lint.yaml's "don't block on pre-existing issues" intent) so the gate enforces new debt without blocking on legacy code merely touched here. | 2 个月前 | |
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. | 29 天前 | |
feat(i18n): resolve text direction from the active locale Adds the plumbing that makes direction a property of the language rather than something hardcoded, plus the codemod used to convert the codebase. Direction has to be resolved on the client. i18next's highest-priority detection source is localStorage, which the server cannot read, and reading the cookie in the root layout would deopt every page to dynamic rendering and still be wrong whenever the two disagree. So dir-init.js runs synchronously in <head> and writes lang/dir before the body paints — the same approach embed-bg.js already uses to avoid a flash. Its detection order mirrors lib/i18n.ts exactly, and the RTL list it carries is asserted against lib/direction.ts by a test, since it's a constant duplicated into a file the bundler never sees. AVAILABLE_LANGUAGES gains a `dir` field, so Persian works today and a future locale is a one-line change; nothing branches on "is Arabic". useDirection() exposes it to components along with a multiplier for physical offsets, and Radix's DirectionProvider makes align="end", collision flipping and arrow-key navigation direction-aware across ~40 call sites with no per-component edits. The codemod converts physical Tailwind utilities to logical ones. It parses with the TypeScript compiler API rather than running a regex over the file, because `pr-`, `mr-` and `pl-` appear in URLs, i18n keys and identifiers. space-x-*, divide-x* and inset-x-* are skipped — they already emit logical properties in v4 — and so is any 50% inset, which is a centering idiom whose paired translate may live in a sibling class or an inline style, and which lands off-centre in RTL if converted alone. Also drops tailwind.config.js. Tailwind v4 only loads it via an @config directive this project doesn't have, so it was dead — and a trap for anyone later adding an RTL plugin there. | 29 天前 |