@hydralms/components 0.2.0 → 0.3.0

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.
Files changed (283) hide show
  1. package/dist/StudentProfile-BVfZMbnV.cjs +1 -0
  2. package/dist/StudentProfile-DeMxdrL3.js +3275 -0
  3. package/dist/assessment-toolbar/question-navigator.d.ts +1 -1
  4. package/dist/assessment-toolbar/timer-display.d.ts +1 -1
  5. package/dist/common/index.d.ts +2 -1
  6. package/dist/common/pagination.d.ts +26 -0
  7. package/dist/common/types.d.ts +1 -0
  8. package/dist/components.css +1 -1
  9. package/dist/content/audio-player.d.ts +22 -0
  10. package/dist/content/code-block.d.ts +30 -0
  11. package/dist/content/embed-block.d.ts +28 -0
  12. package/dist/content/index.d.ts +6 -0
  13. package/dist/content/types.d.ts +24 -0
  14. package/dist/curriculum/course-card.d.ts +51 -0
  15. package/dist/curriculum/index.d.ts +2 -0
  16. package/dist/curriculum/types.d.ts +2 -2
  17. package/dist/index.cjs +1 -1
  18. package/dist/index.d.ts +1 -0
  19. package/dist/index.js +494 -444
  20. package/dist/license/HydraContext.d.ts +16 -0
  21. package/dist/license/ProBadge.d.ts +6 -0
  22. package/dist/license/index.d.ts +7 -0
  23. package/dist/license/tiers.d.ts +3 -0
  24. package/dist/license/useHydraLicense.d.ts +6 -0
  25. package/dist/license/validate.d.ts +13 -0
  26. package/dist/license/withProGate.d.ts +6 -0
  27. package/dist/modules/AssignmentModule/AssignmentModule.d.ts +4 -7
  28. package/dist/modules/AssignmentModule/types.d.ts +5 -1
  29. package/dist/modules/CertificateModule/CertificateModule.d.ts +4 -8
  30. package/dist/modules/CertificateModule/types.d.ts +6 -4
  31. package/dist/modules/CourseCatalogModule/CourseCatalogModule.d.ts +5 -0
  32. package/dist/modules/CourseCatalogModule/types.d.ts +43 -0
  33. package/dist/modules/CoursePlayer/CoursePlayer.d.ts +4 -1
  34. package/dist/modules/DiscussionModule/DiscussionModule.d.ts +4 -7
  35. package/dist/modules/ExamModule/ExamModule.d.ts +4 -7
  36. package/dist/modules/ExamModule/types.d.ts +5 -14
  37. package/dist/modules/FlashcardLab/FlashcardLab.d.ts +4 -1
  38. package/dist/modules/FlashcardLab/types.d.ts +2 -0
  39. package/dist/modules/GradeCenterModule/GradeCenterModule.d.ts +4 -8
  40. package/dist/modules/GradeCenterModule/types.d.ts +2 -0
  41. package/dist/modules/QuizModule/QuizModule.d.ts +4 -1
  42. package/dist/modules/QuizModule/types.d.ts +5 -14
  43. package/dist/modules/StudentDashboardModule/StudentDashboardModule.d.ts +5 -0
  44. package/dist/modules/StudentDashboardModule/types.d.ts +54 -0
  45. package/dist/modules/StudentProfileModule/StudentProfileModule.d.ts +5 -0
  46. package/dist/modules/StudentProfileModule/types.d.ts +43 -0
  47. package/dist/modules/SurveyModule/SurveyModule.d.ts +4 -6
  48. package/dist/modules/SurveyModule/types.d.ts +2 -0
  49. package/dist/modules/_shared/assessment-intro.d.ts +16 -0
  50. package/dist/modules/_shared/assessment-results.d.ts +23 -0
  51. package/dist/modules/_shared/types.d.ts +10 -0
  52. package/dist/modules/_shared/use-timer.d.ts +9 -0
  53. package/dist/modules/index.d.ts +6 -0
  54. package/dist/modules.cjs +1 -1
  55. package/dist/modules.js +1266 -854
  56. package/dist/progress/types.d.ts +2 -0
  57. package/dist/provider/HydraProvider.d.ts +5 -1
  58. package/dist/questions/choice.d.ts +1 -1
  59. package/dist/questions/confidence-indicator.d.ts +37 -0
  60. package/dist/questions/essay.d.ts +1 -1
  61. package/dist/questions/fill-in-the-blank.d.ts +1 -1
  62. package/dist/questions/hotspot.d.ts +1 -1
  63. package/dist/questions/index.d.ts +2 -0
  64. package/dist/questions/inline-choice.d.ts +1 -1
  65. package/dist/questions/matching.d.ts +1 -1
  66. package/dist/questions/multiple-choice.d.ts +1 -1
  67. package/dist/questions/numeric.d.ts +1 -1
  68. package/dist/questions/ordering.d.ts +1 -1
  69. package/dist/questions/question-renderer.d.ts +1 -1
  70. package/dist/questions/scenario.d.ts +1 -1
  71. package/dist/questions/spreadsheet.d.ts +1 -1
  72. package/dist/questions/true-false.d.ts +1 -1
  73. package/dist/sections/AnnouncementFeed/AnnouncementFeed.d.ts +1 -1
  74. package/dist/sections/AnnouncementFeed/types.d.ts +15 -1
  75. package/dist/sections/AssessmentReview/AssessmentReview.d.ts +1 -1
  76. package/dist/sections/AssessmentReview/types.d.ts +6 -0
  77. package/dist/sections/AssignmentSubmission/AssignmentSubmission.d.ts +1 -1
  78. package/dist/sections/AssignmentSubmission/types.d.ts +6 -0
  79. package/dist/sections/CertificateViewer/CertificateViewer.d.ts +1 -1
  80. package/dist/sections/CertificateViewer/certificate-variants.d.ts +42 -0
  81. package/dist/sections/CertificateViewer/types.d.ts +6 -0
  82. package/dist/sections/CourseCatalog/CourseCatalog.d.ts +2 -0
  83. package/dist/sections/CourseCatalog/types.d.ts +80 -0
  84. package/dist/sections/CourseOutline/CourseOutline.d.ts +1 -1
  85. package/dist/sections/CourseOutline/types.d.ts +6 -0
  86. package/dist/sections/DiscussionThread/DiscussionThread.d.ts +1 -1
  87. package/dist/sections/DiscussionThread/types.d.ts +6 -0
  88. package/dist/sections/EnrollmentWizard/EnrollmentWizard.d.ts +2 -0
  89. package/dist/sections/EnrollmentWizard/types.d.ts +66 -0
  90. package/dist/sections/ExamSession/ExamSession.d.ts +1 -1
  91. package/dist/sections/ExamSession/types.d.ts +6 -0
  92. package/dist/sections/FlashcardStudySession/FlashcardStudySession.d.ts +1 -1
  93. package/dist/sections/FlashcardStudySession/types.d.ts +6 -0
  94. package/dist/sections/ForumBoard/ForumBoard.d.ts +1 -1
  95. package/dist/sections/ForumBoard/types.d.ts +14 -0
  96. package/dist/sections/GradebookTable/GradebookTable.d.ts +1 -1
  97. package/dist/sections/GradebookTable/types.d.ts +14 -0
  98. package/dist/sections/LecturePlayer/LecturePlayer.d.ts +1 -1
  99. package/dist/sections/LecturePlayer/types.d.ts +8 -0
  100. package/dist/sections/LessonPage/LessonPage.d.ts +1 -1
  101. package/dist/sections/LessonPage/types.d.ts +6 -0
  102. package/dist/sections/PracticeQuiz/PracticeQuiz.d.ts +1 -1
  103. package/dist/sections/PracticeQuiz/types.d.ts +6 -0
  104. package/dist/sections/ProgressDashboard/ProgressDashboard.d.ts +1 -1
  105. package/dist/sections/ProgressDashboard/types.d.ts +6 -0
  106. package/dist/sections/QuizSession/QuizSession.d.ts +1 -1
  107. package/dist/sections/QuizSession/types.d.ts +6 -0
  108. package/dist/sections/RequirementsChecklist/RequirementsChecklist.d.ts +1 -1
  109. package/dist/sections/RequirementsChecklist/types.d.ts +6 -0
  110. package/dist/sections/ResourceLibrary/ResourceLibrary.d.ts +1 -1
  111. package/dist/sections/ResourceLibrary/types.d.ts +15 -1
  112. package/dist/sections/RubricView/RubricView.d.ts +1 -1
  113. package/dist/sections/RubricView/types.d.ts +6 -0
  114. package/dist/sections/ScrollableQuiz/ScrollableQuiz.d.ts +1 -1
  115. package/dist/sections/ScrollableQuiz/types.d.ts +6 -0
  116. package/dist/sections/StudentProfile/StudentProfile.d.ts +2 -0
  117. package/dist/sections/StudentProfile/types.d.ts +98 -0
  118. package/dist/sections/SurveyForm/SurveyForm.d.ts +1 -1
  119. package/dist/sections/SurveyForm/types.d.ts +6 -0
  120. package/dist/sections/_shared/merge-answers.d.ts +9 -0
  121. package/dist/sections/_shared/section-shell.d.ts +20 -0
  122. package/dist/sections/_shared/use-assessment-session.d.ts +30 -0
  123. package/dist/sections/index.d.ts +6 -0
  124. package/dist/sections.cjs +1 -1
  125. package/dist/sections.js +268 -307
  126. package/dist/tabs-BsfVo2Bl.cjs +173 -0
  127. package/dist/{tabs-Wf3h_Cx3.js → tabs-BuY1iNJE.js} +7532 -6807
  128. package/dist/ui/badge.d.ts +1 -1
  129. package/dist/ui/index.d.ts +2 -0
  130. package/dist/ui/progress.d.ts +1 -1
  131. package/dist/ui/rich-text-editor.d.ts +3 -1
  132. package/dist/ui/toast.d.ts +43 -0
  133. package/dist/utils/debounce.d.ts +5 -1
  134. package/dist/utils/pick-palette-color.d.ts +19 -0
  135. package/dist/video/types.d.ts +15 -0
  136. package/dist/video/video-player.d.ts +1 -1
  137. package/dist/withProGate-BWqcKdPM.js +137 -0
  138. package/dist/withProGate-DX6XqKLp.cjs +1 -0
  139. package/package.json +34 -220
  140. package/src/assessment-toolbar/question-navigator.tsx +10 -5
  141. package/src/assessment-toolbar/timer-display.tsx +4 -3
  142. package/src/assessment-toolbar/use-countdown.ts +1 -1
  143. package/src/common/empty-state.tsx +1 -0
  144. package/src/common/index.ts +2 -0
  145. package/src/common/pagination.tsx +135 -0
  146. package/src/common/search-input.tsx +2 -1
  147. package/src/common/types.ts +2 -0
  148. package/src/content/attachment-list.tsx +2 -0
  149. package/src/content/audio-player.tsx +196 -0
  150. package/src/content/code-block.tsx +113 -0
  151. package/src/content/content-block.tsx +64 -0
  152. package/src/content/embed-block.tsx +78 -0
  153. package/src/content/file-upload-zone.tsx +10 -0
  154. package/src/content/index.ts +6 -0
  155. package/src/content/types.ts +5 -0
  156. package/src/curriculum/course-card.tsx +199 -0
  157. package/src/curriculum/curriculum-item.tsx +3 -3
  158. package/src/curriculum/curriculum-tree.tsx +20 -13
  159. package/src/curriculum/index.ts +2 -0
  160. package/src/curriculum/types.ts +2 -2
  161. package/src/flashcards/flashcard.tsx +28 -8
  162. package/src/index.ts +3 -0
  163. package/src/license/HydraContext.tsx +62 -0
  164. package/src/license/ProBadge.tsx +43 -0
  165. package/src/license/index.ts +7 -0
  166. package/src/license/tiers.ts +24 -0
  167. package/src/license/useHydraLicense.ts +10 -0
  168. package/src/license/validate.ts +90 -0
  169. package/src/license/withProGate.tsx +21 -0
  170. package/src/modules/AssignmentModule/AssignmentModule.tsx +17 -8
  171. package/src/modules/AssignmentModule/types.ts +5 -1
  172. package/src/modules/CertificateModule/CertificateModule.tsx +21 -9
  173. package/src/modules/CertificateModule/types.ts +6 -4
  174. package/src/modules/CourseCatalogModule/CourseCatalogModule.tsx +126 -0
  175. package/src/modules/CourseCatalogModule/types.ts +47 -0
  176. package/src/modules/CoursePlayer/CoursePlayer.tsx +37 -22
  177. package/src/modules/DiscussionModule/DiscussionModule.tsx +57 -22
  178. package/src/modules/ExamModule/ExamModule.tsx +64 -198
  179. package/src/modules/ExamModule/types.ts +5 -14
  180. package/src/modules/FlashcardLab/FlashcardLab.tsx +10 -5
  181. package/src/modules/FlashcardLab/types.ts +2 -0
  182. package/src/modules/GradeCenterModule/GradeCenterModule.tsx +7 -2
  183. package/src/modules/GradeCenterModule/types.ts +2 -0
  184. package/src/modules/QuizModule/QuizModule.tsx +49 -169
  185. package/src/modules/QuizModule/types.ts +5 -15
  186. package/src/modules/StudentDashboardModule/StudentDashboardModule.tsx +117 -0
  187. package/src/modules/StudentDashboardModule/types.ts +56 -0
  188. package/src/modules/StudentProfileModule/StudentProfileModule.tsx +289 -0
  189. package/src/modules/StudentProfileModule/types.ts +45 -0
  190. package/src/modules/SurveyModule/SurveyModule.tsx +9 -4
  191. package/src/modules/SurveyModule/types.ts +2 -0
  192. package/src/modules/_shared/assessment-intro.tsx +75 -0
  193. package/src/modules/_shared/assessment-results.tsx +133 -0
  194. package/src/modules/_shared/types.ts +11 -0
  195. package/src/modules/_shared/use-timer.ts +49 -0
  196. package/src/modules/index.ts +9 -0
  197. package/src/progress/achievement-badge.tsx +3 -3
  198. package/src/progress/grade-indicator.tsx +9 -1
  199. package/src/progress/progress-ring.tsx +2 -1
  200. package/src/progress/stat-card.tsx +8 -1
  201. package/src/progress/types.ts +2 -0
  202. package/src/provider/HydraProvider.tsx +15 -6
  203. package/src/questions/choice.tsx +13 -6
  204. package/src/questions/confidence-indicator.tsx +107 -0
  205. package/src/questions/essay.tsx +6 -4
  206. package/src/questions/fill-in-the-blank.tsx +8 -4
  207. package/src/questions/hotspot.tsx +4 -4
  208. package/src/questions/index.ts +2 -0
  209. package/src/questions/inline-choice.tsx +5 -4
  210. package/src/questions/matching.tsx +5 -4
  211. package/src/questions/multiple-choice.tsx +13 -6
  212. package/src/questions/numeric.tsx +8 -4
  213. package/src/questions/ordering.tsx +12 -4
  214. package/src/questions/question-renderer.tsx +3 -2
  215. package/src/questions/scenario.tsx +4 -4
  216. package/src/questions/spreadsheet.tsx +5 -4
  217. package/src/questions/true-false.tsx +13 -6
  218. package/src/sections/AnnouncementFeed/AnnouncementFeed.tsx +64 -8
  219. package/src/sections/AnnouncementFeed/types.ts +15 -1
  220. package/src/sections/AssessmentReview/AssessmentReview.tsx +37 -0
  221. package/src/sections/AssessmentReview/types.ts +6 -0
  222. package/src/sections/AssignmentSubmission/AssignmentSubmission.tsx +37 -1
  223. package/src/sections/AssignmentSubmission/types.ts +6 -0
  224. package/src/sections/CertificateViewer/CertificateViewer.tsx +29 -227
  225. package/src/sections/CertificateViewer/certificate-variants.tsx +170 -0
  226. package/src/sections/CertificateViewer/types.ts +6 -0
  227. package/src/sections/CourseCatalog/CourseCatalog.tsx +220 -0
  228. package/src/sections/CourseCatalog/types.ts +76 -0
  229. package/src/sections/CourseOutline/CourseOutline.tsx +41 -0
  230. package/src/sections/CourseOutline/types.ts +6 -0
  231. package/src/sections/DiscussionThread/DiscussionThread.tsx +42 -1
  232. package/src/sections/DiscussionThread/types.ts +6 -0
  233. package/src/sections/EnrollmentWizard/EnrollmentWizard.tsx +343 -0
  234. package/src/sections/EnrollmentWizard/types.ts +65 -0
  235. package/src/sections/ExamSession/ExamSession.tsx +100 -94
  236. package/src/sections/ExamSession/types.ts +6 -0
  237. package/src/sections/FlashcardStudySession/FlashcardStudySession.tsx +53 -36
  238. package/src/sections/FlashcardStudySession/types.ts +6 -0
  239. package/src/sections/ForumBoard/ForumBoard.tsx +59 -1
  240. package/src/sections/ForumBoard/types.ts +14 -0
  241. package/src/sections/GradebookTable/GradebookTable.tsx +54 -1
  242. package/src/sections/GradebookTable/types.ts +14 -0
  243. package/src/sections/LecturePlayer/LecturePlayer.tsx +63 -37
  244. package/src/sections/LecturePlayer/types.ts +8 -0
  245. package/src/sections/LessonPage/LessonPage.tsx +36 -5
  246. package/src/sections/LessonPage/types.ts +6 -0
  247. package/src/sections/PracticeQuiz/PracticeQuiz.tsx +106 -74
  248. package/src/sections/PracticeQuiz/types.ts +6 -0
  249. package/src/sections/ProgressDashboard/ProgressDashboard.tsx +64 -10
  250. package/src/sections/ProgressDashboard/types.ts +6 -0
  251. package/src/sections/QuizSession/QuizSession.tsx +71 -82
  252. package/src/sections/QuizSession/types.ts +6 -0
  253. package/src/sections/RequirementsChecklist/RequirementsChecklist.tsx +41 -1
  254. package/src/sections/RequirementsChecklist/types.ts +6 -0
  255. package/src/sections/ResourceLibrary/ResourceLibrary.tsx +64 -8
  256. package/src/sections/ResourceLibrary/types.ts +15 -1
  257. package/src/sections/RubricView/RubricView.tsx +37 -1
  258. package/src/sections/RubricView/types.ts +6 -0
  259. package/src/sections/ScrollableQuiz/ScrollableQuiz.tsx +36 -15
  260. package/src/sections/ScrollableQuiz/types.ts +6 -0
  261. package/src/sections/StudentProfile/StudentProfile.tsx +279 -0
  262. package/src/sections/StudentProfile/types.ts +99 -0
  263. package/src/sections/SurveyForm/SurveyForm.tsx +32 -5
  264. package/src/sections/SurveyForm/types.ts +6 -0
  265. package/src/sections/_shared/merge-answers.ts +22 -0
  266. package/src/sections/_shared/section-shell.tsx +64 -0
  267. package/src/sections/_shared/use-assessment-session.ts +125 -0
  268. package/src/sections/index.ts +22 -0
  269. package/src/social/user-avatar.tsx +9 -5
  270. package/src/styles/globals.css +39 -41
  271. package/src/ui/badge.tsx +8 -0
  272. package/src/ui/index.ts +2 -0
  273. package/src/ui/progress.tsx +4 -0
  274. package/src/ui/rich-text-editor.tsx +10 -0
  275. package/src/ui/rich-text-toolbar.tsx +2 -1
  276. package/src/ui/toast.tsx +170 -0
  277. package/src/utils/debounce.ts +8 -2
  278. package/src/utils/pick-palette-color.ts +33 -0
  279. package/src/video/types.ts +16 -0
  280. package/src/video/video-player.tsx +13 -1
  281. package/dist/ForumBoard-CHXU3mjC.js +0 -2207
  282. package/dist/ForumBoard-d1w5-r6n.cjs +0 -1
  283. package/dist/tabs-DRM2Iq_J.cjs +0 -172
@@ -0,0 +1,170 @@
1
+ import {
2
+ createContext,
3
+ useCallback,
4
+ useContext,
5
+ useEffect,
6
+ useRef,
7
+ useState,
8
+ type ReactNode,
9
+ } from "react";
10
+ import { createPortal } from "react-dom";
11
+ import { cva } from "class-variance-authority";
12
+ import { X, CheckCircle2, AlertCircle, AlertTriangle, Info } from "lucide-react";
13
+ import { cn } from "../lib/utils";
14
+
15
+ const toastVariants = cva(
16
+ "pointer-events-auto relative flex w-full items-start gap-3 rounded-lg border p-4 pr-8 shadow-lg text-sm transition-all",
17
+ {
18
+ variants: {
19
+ variant: {
20
+ success:
21
+ "border-success/50 bg-success/5 text-success [&>svg]:text-success",
22
+ error:
23
+ "border-destructive/50 bg-destructive/5 text-destructive [&>svg]:text-destructive",
24
+ warning:
25
+ "border-warning/50 bg-warning/5 text-warning [&>svg]:text-warning",
26
+ info: "border-info/50 bg-info/5 text-info [&>svg]:text-info",
27
+ },
28
+ },
29
+ defaultVariants: {
30
+ variant: "info",
31
+ },
32
+ },
33
+ );
34
+
35
+ type ToastVariant = "success" | "error" | "warning" | "info";
36
+
37
+ /** Options for creating a toast notification. */
38
+ interface ToastOptions {
39
+ /** Toast message */
40
+ message: string;
41
+ /** Optional title */
42
+ title?: string;
43
+ /** Visual variant */
44
+ variant?: ToastVariant;
45
+ /** Auto-dismiss duration in ms (default 5000, 0 = no auto-dismiss) */
46
+ duration?: number;
47
+ }
48
+
49
+ interface ToastItem extends ToastOptions {
50
+ id: string;
51
+ }
52
+
53
+ interface ToastContextValue {
54
+ toast: (options: ToastOptions) => void;
55
+ }
56
+
57
+ const ToastContext = createContext<ToastContextValue | null>(null);
58
+
59
+ const VARIANT_ICONS: Record<ToastVariant, React.ElementType> = {
60
+ success: CheckCircle2,
61
+ error: AlertCircle,
62
+ warning: AlertTriangle,
63
+ info: Info,
64
+ };
65
+
66
+ function ToastCard({
67
+ item,
68
+ onDismiss,
69
+ }: {
70
+ item: ToastItem;
71
+ onDismiss: (id: string) => void;
72
+ }) {
73
+ const timerRef = useRef<ReturnType<typeof setTimeout>>(undefined);
74
+ const duration = item.duration ?? 5000;
75
+ const Icon = VARIANT_ICONS[item.variant ?? "info"];
76
+
77
+ useEffect(() => {
78
+ if (duration > 0) {
79
+ timerRef.current = setTimeout(() => onDismiss(item.id), duration);
80
+ }
81
+ return () => clearTimeout(timerRef.current);
82
+ }, [duration, item.id, onDismiss]);
83
+
84
+ return (
85
+ <div
86
+ data-slot="toast"
87
+ role="status"
88
+ aria-live="polite"
89
+ className={cn(toastVariants({ variant: item.variant ?? "info" }))}
90
+ >
91
+ <Icon className="size-4 shrink-0 translate-y-0.5" />
92
+ <div className="flex-1 space-y-1">
93
+ {item.title && (
94
+ <p className="font-medium leading-none">{item.title}</p>
95
+ )}
96
+ <p className="text-sm opacity-90">{item.message}</p>
97
+ </div>
98
+ <button
99
+ type="button"
100
+ onClick={() => onDismiss(item.id)}
101
+ aria-label="Dismiss notification"
102
+ className="absolute right-2 top-2 rounded-md p-1 opacity-70 hover:opacity-100 transition-opacity"
103
+ >
104
+ <X className="size-3.5" />
105
+ </button>
106
+ </div>
107
+ );
108
+ }
109
+
110
+ const MAX_VISIBLE = 3;
111
+ let idCounter = 0;
112
+
113
+ /**
114
+ * ToastProvider wraps your app to enable toast notifications.
115
+ * Renders a fixed-position toast container via portal.
116
+ *
117
+ * @example
118
+ * <ToastProvider>
119
+ * <App />
120
+ * </ToastProvider>
121
+ */
122
+ function ToastProvider({ children }: { children: ReactNode }) {
123
+ const [toasts, setToasts] = useState<ToastItem[]>([]);
124
+
125
+ const toast = useCallback((options: ToastOptions) => {
126
+ const id = `toast-${++idCounter}`;
127
+ setToasts((prev) => [...prev.slice(-(MAX_VISIBLE - 1)), { ...options, id }]);
128
+ }, []);
129
+
130
+ const dismiss = useCallback((id: string) => {
131
+ setToasts((prev) => prev.filter((t) => t.id !== id));
132
+ }, []);
133
+
134
+ return (
135
+ <ToastContext.Provider value={{ toast }}>
136
+ {children}
137
+ {typeof document !== "undefined" &&
138
+ createPortal(
139
+ <div
140
+ data-slot="toaster"
141
+ className="fixed top-4 right-4 z-50 flex flex-col gap-2 w-80 pointer-events-none"
142
+ >
143
+ {toasts.map((item) => (
144
+ <ToastCard key={item.id} item={item} onDismiss={dismiss} />
145
+ ))}
146
+ </div>,
147
+ document.body,
148
+ )}
149
+ </ToastContext.Provider>
150
+ );
151
+ }
152
+
153
+ /**
154
+ * Hook to trigger toast notifications. Must be used within a ToastProvider.
155
+ *
156
+ * @example
157
+ * const { toast } = useToast();
158
+ * toast({ message: "Saved!", variant: "success" });
159
+ */
160
+ function useToast(): ToastContextValue {
161
+ const ctx = useContext(ToastContext);
162
+ if (!ctx) throw new Error("useToast must be used within a ToastProvider");
163
+ return ctx;
164
+ }
165
+
166
+ /** Convenience alias for ToastProvider. */
167
+ const Toaster = ToastProvider;
168
+
169
+ export { ToastProvider, Toaster, useToast, toastVariants };
170
+ export type { ToastOptions, ToastVariant };
@@ -1,10 +1,16 @@
1
+ type Cancellable<T extends (...args: Parameters<T>) => void> = ((
2
+ ...args: Parameters<T>
3
+ ) => void) & { cancel: () => void };
4
+
1
5
  export function debounce<T extends (...args: Parameters<T>) => void>(
2
6
  fn: T,
3
7
  delay: number,
4
- ): (...args: Parameters<T>) => void {
8
+ ): Cancellable<T> {
5
9
  let timer: ReturnType<typeof setTimeout>;
6
- return (...args: Parameters<T>) => {
10
+ const debounced = (...args: Parameters<T>) => {
7
11
  clearTimeout(timer);
8
12
  timer = setTimeout(() => fn(...args), delay);
9
13
  };
14
+ debounced.cancel = () => clearTimeout(timer);
15
+ return debounced;
10
16
  }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Palette CSS variable names matching --palette-0 through --palette-3
3
+ * defined in globals.css.
4
+ */
5
+ export const PALETTE_VARS = [
6
+ "var(--palette-0)",
7
+ "var(--palette-1)",
8
+ "var(--palette-2)",
9
+ "var(--palette-3)",
10
+ ] as const;
11
+
12
+ /** Number of palette colors available. */
13
+ export const PALETTE_COUNT = PALETTE_VARS.length;
14
+
15
+ /**
16
+ * Pick a palette CSS variable by cycling through the 4 palette colors.
17
+ * Useful for assigning varied colors to list items by index.
18
+ */
19
+ export function pickPaletteColor(index: number): string {
20
+ return PALETTE_VARS[((index % PALETTE_COUNT) + PALETTE_COUNT) % PALETTE_COUNT];
21
+ }
22
+
23
+ /** Palette variant name for CVA-based components (badge, progress). */
24
+ export type PaletteVariant = "palette0" | "palette1" | "palette2" | "palette3";
25
+
26
+ /**
27
+ * Pick a palette variant name by cycling through the 4 palette variants.
28
+ * Useful for assigning varied CVA variants to list items by index.
29
+ */
30
+ export function pickPaletteVariant(index: number): PaletteVariant {
31
+ const i = ((index % PALETTE_COUNT) + PALETTE_COUNT) % PALETTE_COUNT;
32
+ return `palette${i}` as PaletteVariant;
33
+ }
@@ -9,6 +9,20 @@
9
9
  * onEnded={() => markLessonComplete()}
10
10
  * />
11
11
  */
12
+ /** A text track (captions, subtitles) for the video element. */
13
+ export interface VideoTrack {
14
+ /** URL of the track file (e.g., .vtt) */
15
+ src: string;
16
+ /** Track kind */
17
+ kind: "captions" | "subtitles" | "descriptions" | "chapters" | "metadata";
18
+ /** Human-readable track label (e.g., "English") */
19
+ label: string;
20
+ /** BCP 47 language code (e.g., "en") */
21
+ srcLang: string;
22
+ /** Whether this track should be active by default */
23
+ default?: boolean;
24
+ }
25
+
12
26
  export interface VideoPlayerProps {
13
27
  /** URL of the video source */
14
28
  src?: string;
@@ -30,6 +44,8 @@ export interface VideoPlayerProps {
30
44
  readOnly?: boolean;
31
45
  /** Aspect ratio of the video container */
32
46
  aspectRatio?: "16/9" | "4/3" | "1/1";
47
+ /** Array of text tracks (captions, subtitles) for the video */
48
+ tracks?: VideoTrack[];
33
49
  /** CSS class name for the root element */
34
50
  className?: string;
35
51
  /** Inline styles for the root element */
@@ -14,6 +14,7 @@ export const VideoPlayer = ({
14
14
  onTimeUpdate,
15
15
  readOnly = false,
16
16
  aspectRatio = "16/9",
17
+ tracks,
17
18
  className,
18
19
  style,
19
20
  }: VideoPlayerProps) => {
@@ -103,7 +104,18 @@ export const VideoPlayer = ({
103
104
  onEnded={onEnded}
104
105
  onTimeUpdate={handleTimeUpdate}
105
106
  style={{ width: "100%", aspectRatio, display: "block" }}
106
- />
107
+ >
108
+ {tracks?.map((track) => (
109
+ <track
110
+ key={`${track.kind}-${track.srcLang}`}
111
+ src={track.src}
112
+ kind={track.kind}
113
+ label={track.label}
114
+ srcLang={track.srcLang}
115
+ default={track.default}
116
+ />
117
+ ))}
118
+ </video>
107
119
  </div>
108
120
  </div>
109
121
  );