diff --git a/README.md b/README.md index ac6e13d..04a2fa0 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,10 @@ Toán tương tác cho học sinh THCS Việt Nam (lớp 6-9). Số học, Đạ ## Status -3 bài hình học đầu tiên đã ra mắt. Số học và Đại số vẫn "Sắp ra mắt". +5 bài đã ra mắt — số học, đại số, hình học đều có ít nhất một bài. +- Lớp 6 — Ước chung lớn nhất (Euclid): `/so-hoc/uoc-chung-lon-nhat/` +- Lớp 7 — Hiệu hai bình phương: `/dai-so/hieu-hai-binh-phuong/` - Lớp 7 — Tam giác bằng nhau (SSS): `/hinh-hoc/tam-giac-bang-nhau/` - Lớp 8 — Tam giác đồng dạng: `/hinh-hoc/tam-giac-dong-dang/` - Lớp 9 — Góc nội tiếp: `/hinh-hoc/goc-noi-tiep/` @@ -34,7 +36,8 @@ Live URL: https://tiennm99.github.io/mathmax/ - **Static**: SvelteKit + `@sveltejs/adapter-static`, `paths.base = '/mathmax'`, output `build/`. - **Styling**: Tailwind 3 (PostCSS) + Be Vietnam Pro (woff2 qua `@fontsource`). Tick palette `colors.pair.{1,2,3}` được khai báo trong `tailwind.config.js`. - **Language**: JavaScript only (Svelte 5, JSDoc qua `jsconfig.json` với `checkJs: true`). -- **Math engine**: `src/lib/geom-engine/` — module thuần (không phụ thuộc DOM): `vec`, `triangle`, `circle`, `ticks`. Vitest unit tests đi kèm. +- **Math engines**: `src/lib/geom-engine/` (vec, triangle, circle, ticks) và `src/lib/numtheory-engine/` (gcd, lcm, gcdSteps). Module thuần, không phụ thuộc DOM. Vitest unit tests đi kèm. +- **Math typography**: `src/lib/components/tex.svelte` — wrapper KaTeX duy nhất. SSR qua `renderToString`, không cần JS phía client để hiển thị. - **Lessons**: mỗi bài là một `+page.svelte`; copy tiếng Việt colocate trong `src/lib/lessons//copy.vi.js`. - **Drag**: Svelte action `use:draggable` (`src/lib/actions/draggable.svelte.js`) — Pointer Events + bàn phím mũi tên cho a11y. - **i18n**: Hiện chỉ có tiếng Việt. Site chrome ở `src/lib/i18n/site.vi.js`. English thêm sau bằng cách tạo `*.en.js` song song. diff --git a/RUNBOOK.md b/RUNBOOK.md index 8e8d7c7..27b3e30 100644 --- a/RUNBOOK.md +++ b/RUNBOOK.md @@ -55,3 +55,7 @@ When the trigger fires: ## Initial CI note Initial CI uses `npm install` (no lockfile committed yet). After first successful run, user commits `package-lock.json` locally and we switch CI to `npm ci` in a follow-up PR. + +## Bundle notes + +- **KaTeX** (`katex@^0.16`) is loaded site-wide via `import 'katex/dist/katex.min.css'` in `+layout.svelte`. CSS + woff2 fonts add ~280KB total to the static asset payload. Math is server-rendered via `katex.renderToString` (in `src/lib/components/tex.svelte`), so KaTeX **JavaScript runs only at build time** — the runtime cost is the CSS + fonts, not the JS module. If Lighthouse perf drops below 90 after content growth, consider scoping CSS imports per-route. diff --git a/src/lib/i18n/site.vi.js b/src/lib/i18n/site.vi.js index 30fb9b4..78986f7 100644 --- a/src/lib/i18n/site.vi.js +++ b/src/lib/i18n/site.vi.js @@ -26,14 +26,14 @@ export const topics = { 'so-hoc': { title: 'Số học', blurb: 'Phép tính, ước-bội, phân số, số nguyên. Trực quan hoá thuật toán và quy luật.', - status: 'comingSoon', - href: null, + status: 'live', + href: '/so-hoc/', }, 'dai-so': { title: 'Đại số', blurb: 'Biểu thức, phương trình, hàm số. Thao tác kéo-thả các đối tượng đại số.', - status: 'comingSoon', - href: null, + status: 'live', + href: '/dai-so/', }, 'hinh-hoc': { title: 'Hình học', diff --git a/src/lib/lessons/registry.js b/src/lib/lessons/registry.js index 968fe03..b05266e 100644 --- a/src/lib/lessons/registry.js +++ b/src/lib/lessons/registry.js @@ -1,3 +1,5 @@ +import { vi as gcdCopy } from './uoc-chung-lon-nhat/copy.vi.js'; +import { vi as diffSquaresCopy } from './hieu-hai-binh-phuong/copy.vi.js'; import { vi as sssCopy } from './tam-giac-bang-nhau/copy.vi.js'; import { vi as similarityCopy } from './tam-giac-dong-dang/copy.vi.js'; import { vi as inscribedCopy } from './goc-noi-tiep/copy.vi.js'; @@ -7,8 +9,9 @@ import { vi as inscribedCopy } from './goc-noi-tiep/copy.vi.js'; * gradeLabel: string, intro: string, [k: string]: any}} LessonCopy */ +// Order: by topic (số học → đại số → hình học), then by grade ascending. /** @type {LessonCopy[]} */ -export const lessons = [sssCopy, similarityCopy, inscribedCopy]; +export const lessons = [gcdCopy, diffSquaresCopy, sssCopy, similarityCopy, inscribedCopy]; /** @param {string} topic */ export function lessonsByTopic(topic) { diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 3c1606a..737c70c 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,5 +1,6 @@ diff --git a/src/routes/dai-so/+page.svelte b/src/routes/dai-so/+page.svelte new file mode 100644 index 0000000..263eb1c --- /dev/null +++ b/src/routes/dai-so/+page.svelte @@ -0,0 +1,59 @@ + + + + {topic.title} — {copy.site.title} + + + +
+
+ MathMax + +
+
+ +
+
+ + +
+

{topic.title}

+

{topic.blurb}

+
+ + +
+
+ + diff --git a/src/routes/dai-so/hieu-hai-binh-phuong/+page.svelte b/src/routes/dai-so/hieu-hai-binh-phuong/+page.svelte new file mode 100644 index 0000000..7843202 --- /dev/null +++ b/src/routes/dai-so/hieu-hai-binh-phuong/+page.svelte @@ -0,0 +1,187 @@ + + + + {m.title} — {copy.site.title} + + + +
+
+ MathMax +
+
+ +
+
+ + +
+
{m.gradeLabel}
+

{m.title}

+

{m.intro}

+
+ +
+

{m.instruction}

+
+ + +
+
+ +
+
+

{m.diagramSquareLabel}

+ + + {#if stripW > 0} + + {/if} + {#if rectH > 0} + + {/if} + + + + + + a = {aSafe} + a + {#if cutSize > 0} + b = {bSafe} + b + {/if} + +
+ +
+

{m.diagramRectLabel}

+ + {#if rectH > 0} + + + + + + + {:else} + + + a = b → a − b = 0 → diện tích = 0 + {/if} + + a + b = {aSafe + bSafe} + {#if rectH > 0} + a − b = {aSafe - bSafe} + {/if} + +
+
+ +

{m.rearrangeNote}

+ +
+

{m.numericTitle}

+
+
a²: {aSq}
+
b²: {bSq}
+
a² − b²: {diff}
+
(a + b)(a − b): {product}
+
+
+ +
+
+ +
+

{m.identityTitle}

+

{m.identityStatement}

+ +
+ +
+

{m.exampleTitle}

+

{m.exampleBody}

+
+ +
+ {m.nextTeaser} +
+
+
diff --git a/src/routes/so-hoc/+page.svelte b/src/routes/so-hoc/+page.svelte new file mode 100644 index 0000000..2863864 --- /dev/null +++ b/src/routes/so-hoc/+page.svelte @@ -0,0 +1,59 @@ + + + + {topic.title} — {copy.site.title} + + + +
+
+ MathMax + +
+
+ +
+
+ + +
+

{topic.title}

+

{topic.blurb}

+
+ + +
+
+ + diff --git a/src/routes/so-hoc/uoc-chung-lon-nhat/+page.svelte b/src/routes/so-hoc/uoc-chung-lon-nhat/+page.svelte new file mode 100644 index 0000000..a2992d1 --- /dev/null +++ b/src/routes/so-hoc/uoc-chung-lon-nhat/+page.svelte @@ -0,0 +1,168 @@ + + + + {m.title} — {copy.site.title} + + + +
+
+ MathMax +
+
+ +
+
+ + +
+
{m.gradeLabel}
+

{m.title}

+

{m.intro}

+
+ +
+

{m.instruction}

+
+ + +
+
+ + + +
+
+ +
+

{m.rowDescription}

+
+ + + + + + + + + + + {#each visibleSteps as step, i (i)} + + + + + + + {/each} + {#if visibleSteps.length === 0} + + {/if} + +
#{m.tableHeaderA}{m.tableHeaderQ} · {m.tableHeaderB}+ {m.tableHeaderR}
{i + 1}{step.a}= {step.q} · {step.b}+ {step.r}
Nhấn "{m.stepBtn}" để bắt đầu
+
+
+ + {#if result !== null} +
+
+ ✓ {m.resultLabel}({aSafe}, {bSafe}) = {result} +
+
+ +
+ {#if lcmValue !== null} +
+
{m.lcmLabel}:
+ +
+ {/if} +
+ {/if} + +
+

{m.theoremTitle}

+

{m.theoremStatement}

+
+ +
+

{m.exampleTitle}

+

{m.exampleBody}

+
+ + +
+