@thanh01.pmt/interactive-quiz-kit 1.0.10 → 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/HEADLESS.md CHANGED
@@ -31,8 +31,8 @@ Nói cách khác, **chế độ headless cho phép bạn sử dụng tất cả
31
31
 
32
32
  * **Linh hoạt tối đa:** Bạn không bị trói buộc vào React. Bạn có thể xây dựng giao diện bằng Vue, Svelte, Angular, hoặc thậm chí là ứng dụng di động native (iOS, Android) và chỉ cần gọi đến logic lõi của thư viện.
33
33
  * **Tự động hóa & Tích hợp Backend:** Bạn có thể chạy các quy trình trên server, ví dụ:
34
- * Một cron job hàng đêm dùng module AI để tự động tạo ra hàng trăm câu hỏi mới và lưu vào cơ sở dữ liệu.
35
- * Xây dựng một API endpoint để quản lý, chỉnh sửa, và xuất bản các bài quiz.
34
+ * Một cron job hàng đêm dùng module AI để tự động tạo ra hàng trăm câu hỏi mới và lưu vào cơ sở dữ liệu.
35
+ * Xây dựng một API endpoint để quản lý, chỉnh sửa, và xuất bản các bài quiz.
36
36
  * **Tái sử dụng Logic:** Đảm bảo logic nghiệp vụ (ví dụ: cách chấm điểm một câu hỏi) là nhất quán ở mọi nơi, từ web, mobile đến các script nội bộ.
37
37
 
38
38
  ---
@@ -45,8 +45,8 @@ Dưới đây là từng thành phần, vai trò, và tầm quan trọng của c
45
45
 
46
46
  * **Vai trò:** Là "Hiến pháp" và "Bộ luật" của thư viện. Chúng định nghĩa cấu trúc dữ liệu và các quy tắc mà mọi thành phần khác phải tuân theo.
47
47
  * **Mô tả chi tiết:**
48
- * **`types.ts`:** Đây là file quan trọng nhất. Nó chứa các `interface` TypeScript như `QuizConfig`, `QuizQuestion`, `QuizResult`. Đây là "nguồn chân lý duy nhất" (single source of truth) cho hình dạng của dữ liệu. Bất kỳ hàm nào bạn viết để tương tác với quiz đều sẽ nhận và trả về các kiểu dữ liệu được định nghĩa ở đây.
49
- * **`schemas/`:** Chứa các file JSON Schema. Trong môi trường headless, chúng cực kỳ hữu ích để **xác thực (validate)** dữ liệu. Ví dụ, khi một hệ thống khác gửi cho bạn một file JSON `quiz.json`, bạn có thể dùng schema này để kiểm tra xem file đó có hợp lệ hay không trước khi xử lý.
48
+ * **`types.ts`:** Đây là file quan trọng nhất. Nó chứa các `interface` TypeScript như `QuizConfig`, `QuizQuestion`, `QuizResult`. Đây là "nguồn chân lý duy nhất" (single source of truth) cho hình dạng của dữ liệu. Bất kỳ hàm nào bạn viết để tương tác với quiz đều sẽ nhận và trả về các kiểu dữ liệu được định nghĩa ở đây.
49
+ * **`schemas/`:** Chứa các file JSON Schema. Trong môi trường headless, chúng cực kỳ hữu ích để **xác thực (validate)** dữ liệu. Ví dụ, khi một hệ thống khác gửi cho bạn một file JSON `quiz.json`, bạn có thể dùng schema này để kiểm tra xem file đó có hợp lệ hay không trước khi xử lý.
50
50
  * **Tầm quan trọng:** **Nền tảng (Fundamental)**. Không thể sử dụng thư viện nếu không hiểu các kiểu dữ liệu này.
51
51
 
52
52
  ---
@@ -55,12 +55,12 @@ Dưới đây là từng thành phần, vai trò, và tầm quan trọng của c
55
55
 
56
56
  * **Vai trò:** Công cụ để **tạo, sửa, xóa, và sắp xếp** các câu hỏi bên trong một đối tượng `QuizConfig`. Đây là service dành cho khâu *biên soạn (authoring)*.
57
57
  * **Mô tả chi tiết:**
58
- * Đây là một lớp (class) bạn khởi tạo với một đối tượng `QuizConfig` ban đầu. Nó sẽ làm việc trên một bản sao sâu (deep copy), đảm bảo không làm thay đổi đối tượng gốc một cách không mong muốn (nguyên tắc immutability).
59
- * **`createNewQuestionTemplate(type)` (Static Method):** Tạo ra một "khuôn mẫu" câu hỏi rỗng với các giá trị mặc định. Rất hữu ích khi bạn muốn thêm một câu hỏi mới.
60
- * **`addQuestion(question)`:** Thêm một câu hỏi vào danh sách.
61
- * **`updateQuestion(updatedQuestion)`:** Cập nhật một câu hỏi đã có dựa trên `id` của nó.
62
- * **`deleteQuestionByIndex(index)`:** Xóa một câu hỏi dựa trên vị trí của nó.
63
- * **`moveQuestion(fromIndex, toIndex)`:** Thay đổi thứ tự các câu hỏi.
58
+ * Đây là một lớp (class) bạn khởi tạo với một đối tượng `QuizConfig` ban đầu. Nó sẽ làm việc trên một bản sao sâu (deep copy), đảm bảo không làm thay đổi đối tượng gốc một cách không mong muốn (nguyên tắc immutability).
59
+ * **`createNewQuestionTemplate(type)` (Static Method):** Tạo ra một "khuôn mẫu" câu hỏi rỗng với các giá trị mặc định. Rất hữu ích khi bạn muốn thêm một câu hỏi mới.
60
+ * **`addQuestion(question)`:** Thêm một câu hỏi vào danh sách.
61
+ * **`updateQuestion(updatedQuestion)`:** Cập nhật một câu hỏi đã có dựa trên `id` của nó.
62
+ * **`deleteQuestionByIndex(index)`:** Xóa một câu hỏi dựa trên vị trí của nó.
63
+ * **`moveQuestion(fromIndex, toIndex)`:** Thay đổi thứ tự các câu hỏi.
64
64
  * **Tầm quan trọng:** **Cốt lõi (Core)** cho bất kỳ tác vụ nào liên quan đến việc quản lý nội dung quiz.
65
65
 
66
66
  * **Ví dụ sử dụng:**
@@ -91,10 +91,10 @@ Dưới đây là từng thành phần, vai trò, và tầm quan trọng của c
91
91
 
92
92
  * **Vai trò:** Quản lý toàn bộ vòng đời của một **phiên làm bài quiz thực tế**. Đây là service dành cho khâu *thực thi (runtime)*.
93
93
  * **Mô tả chi tiết:**
94
- * Khởi tạo với một `QuizConfig` và một bộ `callbacks`. Thiết kế dựa trên callback này làm cho nó cực kỳ linh hoạt để tích hợp vào bất kỳ hệ thống nào.
95
- * Nó quản lý trạng thái nội bộ: câu hỏi hiện tại, câu trả lời của người dùng, thời gian còn lại.
96
- * Cung cấp các phương thức công khai (public methods) như `nextQuestion()`, `previousQuestion()`, `submitAnswer()`, và `calculateResults()` để điều khiển luồng làm bài.
97
- * Logic chấm điểm phức tạp cho 12 loại câu hỏi được đóng gói hoàn toàn bên trong `evaluateQuestion`.
94
+ * Khởi tạo với một `QuizConfig` và một bộ `callbacks`. Thiết kế dựa trên callback này làm cho nó cực kỳ linh hoạt để tích hợp vào bất kỳ hệ thống nào.
95
+ * Nó quản lý trạng thái nội bộ: câu hỏi hiện tại, câu trả lời của người dùng, thời gian còn lại.
96
+ * Cung cấp các phương thức công khai (public methods) như `nextQuestion()`, `previousQuestion()`, `submitAnswer()`, và `calculateResults()` để điều khiển luồng làm bài.
97
+ * Logic chấm điểm phức tạp cho 12 loại câu hỏi được đóng gói hoàn toàn bên trong `evaluateQuestion`.
98
98
  * **Tầm quan trọng:** **Cốt lõi (Core)** cho bất kỳ ứng dụng nào muốn cho người dùng *làm* bài quiz.
99
99
 
100
100
  ---
@@ -103,9 +103,9 @@ Dưới đây là từng thành phần, vai trò, và tầm quan trọng của c
103
103
 
104
104
  * **Vai trò:** Cung cấp các hàm `async` để giao tiếp với AI và tự động tạo ra nội dung quiz.
105
105
  * **Mô tả chi tiết:**
106
- * Mỗi file là một "flow" có thể gọi được. Chúng nhận đầu vào có cấu trúc (nhờ `zod`) và trả về kết quả cũng có cấu trúc.
107
- * **`generate...Question`:** Các flow tạo câu hỏi đơn lẻ.
108
- * **`generateQuizPlan` & `generateQuestionsFromQuizPlan`:** Một quy trình 2 giai đoạn mạnh mẽ cho phép tạo ra một bài quiz hoàn chỉnh, cân bằng về chủ đề và độ khó, thay vì chỉ là một mớ câu hỏi ngẫu nhiên.
106
+ * Mỗi file là một "flow" có thể gọi được. Chúng nhận đầu vào có cấu trúc (nhờ `zod`) và trả về kết quả cũng có cấu trúc.
107
+ * **`generate...Question`:** Các flow tạo câu hỏi đơn lẻ.
108
+ * **`generateQuizPlan` & `generateQuestionsFromQuizPlan`:** Một quy trình 2 giai đoạn mạnh mẽ cho phép tạo ra một bài quiz hoàn chỉnh, cân bằng về chủ đề và độ khó, thay vì chỉ là một mớ câu hỏi ngẫu nhiên.
109
109
  * **Tầm quan trọng:** **Tính năng nâng cao (Advanced Feature)**. Lý tưởng cho các hệ thống muốn tự động hóa việc tạo nội dung.
110
110
 
111
111
  * **Ví dụ sử dụng (trong một script backend):**
@@ -136,9 +136,9 @@ Dưới đây là từng thành phần, vai trò, và tầm quan trọng của c
136
136
 
137
137
  * **Vai trò:** Cung cấp các công cụ chuyên biệt để hoàn thành các tác vụ cụ thể.
138
138
  * **Mô tả chi tiết:**
139
- * **`SCORMService.ts`:** Một lớp độc lập để nói chuyện với LMS. Nó không biết gì về quiz, chỉ biết về các lệnh SCORM. `QuizEngine` sẽ sử dụng nó.
140
- * **`generateSCORMManifest`, `generateLauncherHTML`:** Các hàm này nhận dữ liệu và trả về các chuỗi văn bản (XML, HTML). Chúng không có tác dụng phụ (side-effect) và hoàn toàn "thuần khiết" (pure), rất phù hợp cho môi trường headless.
141
- * **`idGenerators.ts`:** Cung cấp hàm `generateUniqueId`, một tiện ích nhỏ nhưng cần thiết khi bạn tạo dữ liệu mới.
139
+ * **`SCORMService.ts`:** Một lớp độc lập để nói chuyện với LMS. Nó không biết gì về quiz, chỉ biết về các lệnh SCORM. `QuizEngine` sẽ sử dụng nó.
140
+ * **`generateSCORMManifest`, `generateLauncherHTML`:** Các hàm này nhận dữ liệu và trả về các chuỗi văn bản (XML, HTML). Chúng không có tác dụng phụ (side-effect) và hoàn toàn "thuần khiết" (pure), rất phù hợp cho môi trường headless.
141
+ * **`idGenerators.ts`:** Cung cấp hàm `generateUniqueId`, một tiện ích nhỏ nhưng cần thiết khi bạn tạo dữ liệu mới.
142
142
  * **Tầm quan trọng:** **Hỗ trợ (Supporting)**. Cần thiết cho các chức năng cụ thể như SCORM hoặc khi cần tạo dữ liệu mới.
143
143
 
144
144
  Bằng cách kết hợp các thành phần này, bạn có thể xây dựng một hệ thống hoàn chỉnh ở phía backend để tạo, quản lý quiz, và thậm chí mô phỏng các phiên làm bài để kiểm thử mà không cần viết một dòng code giao diện nào.
@@ -330,4 +330,4 @@ console.log(launcherHTML);
330
330
  // fs.writeFileSync('dist/index.html', launcherHTML);
331
331
  ```
332
332
 
333
- **Lưu ý:** Hàm `exportQuizAsSCORMZip` được thiết kế để chạy trên trình duyệt vì nó tương tác với DOM để kích hoạt tải file. Nếu bạn muốn đóng gói ZIP ở phía backend, bạn cần sử dụng một thư viện như `jszip` và tự mình thực hiện các bước tương tự như trong file `services/scormPackaging.ts`.
333
+ **Lưu ý:** Hàm `exportQuizAsSCORMZip` được thiết kế để chạy trên trình duyệt vì nó tương tác với DOM để kích hoạt tải file. Nếu bạn muốn đóng gói ZIP ở phía backend, bạn cần sử dụng một thư viện như `jszip` và tự mình thực hiện các bước tương tự như trong file `services/scormPackaging.ts`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thanh01.pmt/interactive-quiz-kit",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "description": "A comprehensive library for creating, managing, and playing interactive quizzes, with AI generation and SCORM support.",
5
5
  "keywords": [
6
6
  "react",
@@ -18,112 +18,110 @@
18
18
  "type": "git",
19
19
  "url": "https://github.com/thanh01pmt/interactive-quiz-kit.git"
20
20
  },
21
- "main": "dist/index.js",
22
- "module": "dist/index.mjs",
23
- "types": "dist/index.d.ts",
21
+ "sideEffects": false,
22
+ "type": "module",
23
+ "main": "./dist/index.cjs",
24
+ "module": "./dist/index.js",
25
+ "types": "./dist/index.d.ts",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.js",
30
+ "require": "./dist/index.cjs"
31
+ },
32
+ "./player": {
33
+ "types": "./dist/player.d.ts",
34
+ "import": "./dist/player.js",
35
+ "require": "./dist/player.cjs"
36
+ },
37
+ "./react-ui": {
38
+ "types": "./dist/react-ui.d.ts",
39
+ "import": "./dist/react-ui.js",
40
+ "require": "./dist/react-ui.cjs"
41
+ },
42
+ "./ai": {
43
+ "types": "./dist/ai.d.ts",
44
+ "import": "./dist/ai.js",
45
+ "require": "./dist/ai.cjs"
46
+ },
47
+ "./authoring": {
48
+ "types": "./dist/authoring.d.ts",
49
+ "import": "./dist/authoring.js",
50
+ "require": "./dist/authoring.cjs"
51
+ },
52
+ "./package.json": "./package.json"
53
+ },
24
54
  "files": [
25
55
  "dist",
26
56
  "README.md",
27
- "HEADLESS.md",
28
- "schemas",
29
- "public/static/scratch-blocks/media"
57
+ "HEADLESS.md"
30
58
  ],
31
59
  "publishConfig": {
32
60
  "access": "public"
33
61
  },
34
62
  "scripts": {
35
- "build": "tsup src/index.ts --format cjs,esm --dts",
36
- "lint": "eslint src/",
37
- "test": "jest"
38
- },
39
- "peerDependencies": {
40
- "react": "^18.0.0",
41
- "react-dom": "^18.0.0"
63
+ "build": "tsup",
64
+ "build:scorm:js": "tsup --config tsup.scorm.config.ts",
65
+ "build:scorm:css": "tailwindcss -i ./src/styles/main.css -o ./scorm-bundle/styles.css --minify",
66
+ "build:scorm": "npm run build:scorm:js && npm run build:scorm:css",
67
+ "lint": "eslint . --ext .ts,.tsx",
68
+ "test": "jest",
69
+ "clean": "rm -rf dist"
42
70
  },
43
71
  "dependencies": {
44
- "@genkit-ai/googleai": "^1.8.0",
45
- "@genkit-ai/next": "^1.8.0",
46
- "@hookform/resolvers": "^4.1.3",
47
- "@radix-ui/react-accordion": "^1.2.3",
48
- "@radix-ui/react-alert-dialog": "^1.1.6",
49
- "@radix-ui/react-avatar": "^1.1.3",
50
- "@radix-ui/react-checkbox": "^1.1.4",
51
- "@radix-ui/react-dialog": "^1.1.6",
52
- "@radix-ui/react-dropdown-menu": "^2.1.6",
53
- "@radix-ui/react-label": "^2.1.2",
54
- "@radix-ui/react-menubar": "^1.1.6",
55
- "@radix-ui/react-popover": "^1.1.6",
56
- "@radix-ui/react-progress": "^1.1.2",
57
- "@radix-ui/react-radio-group": "^1.2.3",
58
- "@radix-ui/react-scroll-area": "^1.2.3",
59
- "@radix-ui/react-select": "^2.1.6",
60
- "@radix-ui/react-separator": "^1.1.2",
61
- "@radix-ui/react-slider": "^1.2.3",
62
- "@radix-ui/react-slot": "^1.1.2",
63
- "@radix-ui/react-switch": "^1.1.3",
64
- "@radix-ui/react-tabs": "^1.1.3",
65
- "@radix-ui/react-toast": "^1.2.6",
66
- "@radix-ui/react-tooltip": "^1.1.8",
72
+ "@radix-ui/react-accordion": "^1.2.11",
73
+ "@radix-ui/react-alert-dialog": "^1.1.14",
74
+ "@radix-ui/react-checkbox": "^1.3.2",
75
+ "@radix-ui/react-dialog": "^1.1.14",
76
+ "@radix-ui/react-label": "^2.1.7",
77
+ "@radix-ui/react-progress": "^1.1.7",
78
+ "@radix-ui/react-radio-group": "^1.3.7",
79
+ "@radix-ui/react-scroll-area": "^1.2.9",
80
+ "@radix-ui/react-select": "^2.2.5",
81
+ "@radix-ui/react-slot": "^1.2.3",
82
+ "@radix-ui/react-toast": "^1.2.14",
67
83
  "class-variance-authority": "^0.7.1",
68
84
  "clsx": "^2.1.1",
69
- "date-fns": "^3.6.0",
70
- "dotenv": "^16.5.0",
71
- "firebase": "^11.8.1",
72
- "genkit": "^1.8.0",
73
85
  "jszip": "^3.10.1",
74
- "lucide-react": "^0.475.0",
75
- "next": "15.3.3",
76
- "patch-package": "^8.0.0",
86
+ "path-browserify": "^1.0.1",
87
+ "radix-ui": "^1.4.2",
88
+ "react-markdown": "^9.0.1",
89
+ "rehype-highlight": "^7.0.0",
90
+ "rehype-katex": "^7.0.0",
91
+ "remark-gfm": "^4.0.0",
92
+ "remark-math": "^6.0.0",
93
+ "tailwind-merge": "^2.3.0",
94
+ "zod": "^3.23.8"
95
+ },
96
+ "peerDependencies": {
97
+ "@genkit-ai/googleai": "^1.0.0",
98
+ "genkit": "^1.0.0",
99
+ "lucide-react": "^0.300.0",
77
100
  "react": "^18.0.0",
78
- "react-day-picker": "^8.10.1",
79
- "react-dom": "^18.0.0",
80
- "react-hook-form": "^7.54.2",
81
- "recharts": "^2.15.1",
82
- "scratch-blocks": "^1.1.210",
83
- "tailwind-merge": "^3.0.1",
84
- "tailwindcss-animate": "^1.0.7",
85
- "zod": "^3.24.2"
101
+ "react-dom": "^18.0.0"
102
+ },
103
+ "peerDependenciesMeta": {
104
+ "genkit": {
105
+ "optional": true
106
+ },
107
+ "@genkit-ai/googleai": {
108
+ "optional": true
109
+ }
86
110
  },
87
111
  "devDependencies": {
88
112
  "@types/node": "^20",
89
113
  "@types/react": "^18",
90
114
  "@types/react-dom": "^18",
115
+ "autoprefixer": "^10.4.21",
116
+ "eslint": "^8.57.0",
117
+ "eslint-config-next": "14.2.3",
91
118
  "genkit-cli": "^1.8.0",
92
- "postcss": "^8",
93
- "tailwindcss": "^3.4.1",
94
- "tsup": "^8.5.0",
119
+ "postcss": "^8.5.6",
120
+ "react": "^18.3.1",
121
+ "react-dom": "^18.3.1",
122
+ "tailwindcss": "^3.4.17",
123
+ "tailwindcss-animate": "^1.0.7",
124
+ "tsup": "^8.0.2",
95
125
  "typescript": "^5"
96
- },
97
- "exports": {
98
- ".": {
99
- "import": {
100
- "types": "./dist/index.d.mts",
101
- "default": "./dist/index.mjs"
102
- },
103
- "require": {
104
- "types": "./dist/index.d.ts",
105
- "default": "./dist/index.js"
106
- }
107
- },
108
- "./headless": {
109
- "import": {
110
- "types": "./dist/headless.d.mts",
111
- "default": "./dist/headless.mjs"
112
- },
113
- "require": {
114
- "types": "./dist/headless.d.ts",
115
- "default": "./dist/headless.js"
116
- }
117
- },
118
- "./react-ui": {
119
- "import": {
120
- "types": "./dist/react-ui.d.mts",
121
- "default": "./dist/react-ui.mjs"
122
- },
123
- "require": {
124
- "types": "./dist/react-ui.d.ts",
125
- "default": "./dist/react-ui.js"
126
- }
127
- }
128
126
  }
129
127
  }