@vllnt/ui 0.2.0 → 0.2.1-canary.0aaaad2

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 (296) hide show
  1. package/CHANGELOG.md +46 -1
  2. package/README.md +27 -12
  3. package/dist/components/accordion/accordion.js +2 -2
  4. package/dist/components/activity-heatmap/activity-heatmap.js +15 -13
  5. package/dist/components/activity-log/activity-log.js +6 -5
  6. package/dist/components/agent-activity/agent-activity.js +311 -0
  7. package/dist/components/agent-activity/index.js +18 -0
  8. package/dist/components/ai-artifact/ai-artifact.js +422 -0
  9. package/dist/components/ai-artifact/index.js +24 -0
  10. package/dist/components/ai-chat-input/ai-chat-input.js +2 -2
  11. package/dist/components/ai-message-bubble/ai-message-bubble.js +1 -1
  12. package/dist/components/ai-sidebar/ai-sidebar.js +254 -0
  13. package/dist/components/ai-sidebar/index.js +22 -0
  14. package/dist/components/ai-source-citation/ai-source-citation.js +2 -2
  15. package/dist/components/ai-tool-call-display/ai-tool-call-display.js +4 -4
  16. package/dist/components/alert-pulse/alert-pulse.js +93 -0
  17. package/dist/components/alert-pulse/index.js +6 -0
  18. package/dist/components/anchor-port/anchor-port.js +51 -0
  19. package/dist/components/anchor-port/index.js +4 -0
  20. package/dist/components/animated-text/animated-text.js +2 -4
  21. package/dist/components/auto-reload/auto-reload.js +377 -0
  22. package/dist/components/auto-reload/index.js +6 -0
  23. package/dist/components/avatar/avatar.js +1 -1
  24. package/dist/components/avatar-group/avatar-group.js +4 -4
  25. package/dist/components/banner/banner.js +155 -0
  26. package/dist/components/banner/index.js +10 -0
  27. package/dist/components/bottom-activity-strip/bottom-activity-strip.js +91 -0
  28. package/dist/components/bottom-activity-strip/index.js +6 -0
  29. package/dist/components/bottom-bar/bottom-bar.js +25 -0
  30. package/dist/components/bottom-bar/index.js +4 -0
  31. package/dist/components/breadcrumb/breadcrumb.js +33 -26
  32. package/dist/components/button/button.js +1 -1
  33. package/dist/components/calendar/calendar.js +6 -6
  34. package/dist/components/callout/callout.js +1 -1
  35. package/dist/components/candlestick-chart/candlestick-chart.js +1 -1
  36. package/dist/components/canvas-shell/canvas-foundation-demo.js +183 -0
  37. package/dist/components/canvas-shell/canvas-shell-route-config.js +0 -0
  38. package/dist/components/canvas-shell/canvas-shell.js +261 -0
  39. package/dist/components/canvas-shell/index.js +4 -0
  40. package/dist/components/canvas-view/canvas-view.js +461 -0
  41. package/dist/components/canvas-view/index.js +6 -0
  42. package/dist/components/carousel/carousel.js +4 -4
  43. package/dist/components/chart/area-chart.js +1 -0
  44. package/dist/components/chart/line-chart.js +1 -0
  45. package/dist/components/chat-dock-section/chat-dock-section.js +56 -0
  46. package/dist/components/chat-dock-section/index.js +6 -0
  47. package/dist/components/checkbox/checkbox.js +2 -2
  48. package/dist/components/checklist/checklist.js +10 -3
  49. package/dist/components/checklist/index.js +3 -1
  50. package/dist/components/choropleth-map/choropleth-map.js +373 -0
  51. package/dist/components/choropleth-map/index.js +10 -0
  52. package/dist/components/chronological-timeline/chronological-timeline.js +338 -0
  53. package/dist/components/chronological-timeline/index.js +8 -0
  54. package/dist/components/civilization-card/civilization-card.js +258 -0
  55. package/dist/components/civilization-card/index.js +8 -0
  56. package/dist/components/code-block/code-block.js +2 -2
  57. package/dist/components/code-playground/code-playground.js +8 -7
  58. package/dist/components/combobox/combobox.js +46 -22
  59. package/dist/components/command/command.js +1 -1
  60. package/dist/components/comment-pin/comment-pin.js +104 -0
  61. package/dist/components/comment-pin/index.js +6 -0
  62. package/dist/components/comparison/comparison.js +9 -9
  63. package/dist/components/completion-dialog/completion-dialog.js +1 -1
  64. package/dist/components/connector-edge/connector-edge.js +66 -0
  65. package/dist/components/connector-edge/index.js +6 -0
  66. package/dist/components/content-intro/content-intro.js +7 -6
  67. package/dist/components/context-lens/context-lens.js +98 -0
  68. package/dist/components/context-lens/index.js +6 -0
  69. package/dist/components/context-menu/context-menu.js +4 -4
  70. package/dist/components/conversation-thread/conversation-thread.js +348 -0
  71. package/dist/components/conversation-thread/index.js +20 -0
  72. package/dist/components/cookie-consent/cookie-consent.js +1 -1
  73. package/dist/components/copy-button/copy-button.js +189 -0
  74. package/dist/components/copy-button/index.js +8 -0
  75. package/dist/components/countdown-timer/countdown-timer.js +8 -7
  76. package/dist/components/credit-badge/credit-badge.js +1 -7
  77. package/dist/components/curriculum/curriculum.js +349 -0
  78. package/dist/components/curriculum/index.js +10 -0
  79. package/dist/components/data-list/data-list.js +2 -1
  80. package/dist/components/data-table/data-table.js +5 -4
  81. package/dist/components/date-picker/date-picker.js +1 -1
  82. package/dist/components/dialog/dialog.js +1 -1
  83. package/dist/components/document-sibling-nav/document-sibling-nav.js +111 -0
  84. package/dist/components/document-sibling-nav/index.js +8 -0
  85. package/dist/components/dropdown-menu/dropdown-menu.js +3 -3
  86. package/dist/components/edge-label/edge-label.js +26 -0
  87. package/dist/components/edge-label/index.js +4 -0
  88. package/dist/components/empty-state/empty-state.js +93 -0
  89. package/dist/components/empty-state/index.js +8 -0
  90. package/dist/components/era-comparison/era-comparison.js +198 -0
  91. package/dist/components/era-comparison/index.js +16 -0
  92. package/dist/components/exercise/exercise.js +4 -4
  93. package/dist/components/faq/faq.js +2 -2
  94. package/dist/components/file-upload/file-upload.js +3 -3
  95. package/dist/components/filter-bar/filter-bar.js +2 -1
  96. package/dist/components/flashcard/flashcard.js +1 -1
  97. package/dist/components/floating-action-button/floating-action-button.js +1 -1
  98. package/dist/components/floating-toolbar/floating-toolbar.js +66 -0
  99. package/dist/components/floating-toolbar/index.js +6 -0
  100. package/dist/components/flow-diagram/flow-controls.js +9 -9
  101. package/dist/components/flow-diagram/flow-error-boundary.js +2 -2
  102. package/dist/components/flow-diagram/flow-fullscreen.js +2 -2
  103. package/dist/components/follow-mode/follow-mode.js +89 -0
  104. package/dist/components/follow-mode/index.js +6 -0
  105. package/dist/components/form/form.js +432 -0
  106. package/dist/components/form/index.js +20 -0
  107. package/dist/components/gantt-chart/gantt-chart.js +333 -0
  108. package/dist/components/gantt-chart/index.js +6 -0
  109. package/dist/components/geography-quiz-map/geography-quiz-map.js +343 -0
  110. package/dist/components/geography-quiz-map/index.js +12 -0
  111. package/dist/components/glass-panel/glass-panel.js +21 -0
  112. package/dist/components/glass-panel/index.js +4 -0
  113. package/dist/components/globe-3d/globe-3d.js +417 -0
  114. package/dist/components/globe-3d/index.js +10 -0
  115. package/dist/components/group-hull/group-hull.js +29 -0
  116. package/dist/components/group-hull/index.js +4 -0
  117. package/dist/components/handoff-beacon/handoff-beacon.js +78 -0
  118. package/dist/components/handoff-beacon/index.js +6 -0
  119. package/dist/components/heat-map-overlay/heat-map-overlay.js +215 -0
  120. package/dist/components/heat-map-overlay/index.js +6 -0
  121. package/dist/components/heat-overlay/heat-overlay.js +92 -0
  122. package/dist/components/heat-overlay/index.js +6 -0
  123. package/dist/components/historic-timeline/historic-timeline.js +342 -0
  124. package/dist/components/historic-timeline/index.js +6 -0
  125. package/dist/components/historical-figure-card/historical-figure-card.js +270 -0
  126. package/dist/components/historical-figure-card/index.js +6 -0
  127. package/dist/components/horizontal-scroll-row/horizontal-scroll-row.js +4 -4
  128. package/dist/components/index.js +568 -1
  129. package/dist/components/infinite-plane/index.js +6 -0
  130. package/dist/components/infinite-plane/infinite-plane.js +75 -0
  131. package/dist/components/input-otp/input-otp.js +1 -1
  132. package/dist/components/interactive-timeline/index.js +16 -0
  133. package/dist/components/interactive-timeline/interactive-timeline.js +708 -0
  134. package/dist/components/jarvis-dock/index.js +6 -0
  135. package/dist/components/jarvis-dock/jarvis-dock.js +98 -0
  136. package/dist/components/kbd/index.js +5 -0
  137. package/dist/components/kbd/kbd.js +117 -0
  138. package/dist/components/key-concept/key-concept.js +4 -4
  139. package/dist/components/keyboard-shortcuts-help/keyboard-shortcuts-help.js +2 -2
  140. package/dist/components/knowledge-check/index.js +6 -0
  141. package/dist/components/knowledge-check/knowledge-check.js +448 -0
  142. package/dist/components/learning-objectives/learning-objectives.js +12 -12
  143. package/dist/components/left-rail/index.js +4 -0
  144. package/dist/components/left-rail/left-rail.js +25 -0
  145. package/dist/components/live-cursor/index.js +6 -0
  146. package/dist/components/live-cursor/live-cursor.js +62 -0
  147. package/dist/components/live-feed/live-feed.js +15 -13
  148. package/dist/components/map-2d/index.js +20 -0
  149. package/dist/components/map-2d/map-2d.js +455 -0
  150. package/dist/components/map-timeline/index.js +16 -0
  151. package/dist/components/map-timeline/map-timeline.js +506 -0
  152. package/dist/components/market-treemap/market-treemap.js +1 -1
  153. package/dist/components/mdx-content/mdx-content.js +2 -1
  154. package/dist/components/menubar/menubar.js +4 -4
  155. package/dist/components/metric-cluster/index.js +6 -0
  156. package/dist/components/metric-cluster/metric-cluster.js +96 -0
  157. package/dist/components/metric-gauge/metric-gauge.js +1 -1
  158. package/dist/components/mini-map-panel/index.js +6 -0
  159. package/dist/components/mini-map-panel/mini-map-panel.js +74 -0
  160. package/dist/components/model-comparison/index.js +12 -0
  161. package/dist/components/model-comparison/model-comparison.js +211 -0
  162. package/dist/components/model-selector/model-selector.js +2 -2
  163. package/dist/components/multi-select/index.js +6 -0
  164. package/dist/components/multi-select/multi-select.js +258 -0
  165. package/dist/components/multi-select-lasso/index.js +6 -0
  166. package/dist/components/multi-select-lasso/multi-select-lasso.js +76 -0
  167. package/dist/components/navbar-saas/navbar-saas.js +3 -2
  168. package/dist/components/navigation-menu/navigation-menu.js +2 -2
  169. package/dist/components/newsletter-signup/index.js +8 -0
  170. package/dist/components/newsletter-signup/newsletter-signup.js +269 -0
  171. package/dist/components/number-input/number-input.js +1 -1
  172. package/dist/components/number-ticker/number-ticker.js +11 -4
  173. package/dist/components/object-card/index.js +6 -0
  174. package/dist/components/object-card/object-card.js +126 -0
  175. package/dist/components/object-handle/index.js +4 -0
  176. package/dist/components/object-handle/object-handle.js +38 -0
  177. package/dist/components/object-inspector/index.js +6 -0
  178. package/dist/components/object-inspector/object-inspector.js +136 -0
  179. package/dist/components/overview-board/index.js +8 -0
  180. package/dist/components/overview-board/overview-board.js +127 -0
  181. package/dist/components/parallel-timeline/index.js +6 -0
  182. package/dist/components/parallel-timeline/parallel-timeline.js +251 -0
  183. package/dist/components/password-input/password-input.js +1 -1
  184. package/dist/components/plan-badge/plan-badge.js +1 -7
  185. package/dist/components/playback-ghost/index.js +6 -0
  186. package/dist/components/playback-ghost/playback-ghost.js +83 -0
  187. package/dist/components/policy-delivery-panel/index.js +6 -0
  188. package/dist/components/policy-delivery-panel/policy-delivery-panel.js +99 -0
  189. package/dist/components/presence-stack/index.js +6 -0
  190. package/dist/components/presence-stack/presence-stack.js +108 -0
  191. package/dist/components/presence-sync-indicator/index.js +6 -0
  192. package/dist/components/presence-sync-indicator/presence-sync-indicator.js +73 -0
  193. package/dist/components/pricing-table/index.js +8 -0
  194. package/dist/components/pricing-table/pricing-table.js +247 -0
  195. package/dist/components/primary-source-viewer/index.js +26 -0
  196. package/dist/components/primary-source-viewer/primary-source-viewer.js +439 -0
  197. package/dist/components/pro-tip/pro-tip.js +6 -6
  198. package/dist/components/profile-section/profile-section.js +1 -1
  199. package/dist/components/progress-card/progress-card.js +4 -3
  200. package/dist/components/progress-tracker/index.js +20 -0
  201. package/dist/components/progress-tracker/progress-tracker.js +529 -0
  202. package/dist/components/prompt-templates/index.js +6 -0
  203. package/dist/components/prompt-templates/prompt-templates.js +403 -0
  204. package/dist/components/property-section/index.js +6 -0
  205. package/dist/components/property-section/property-section.js +101 -0
  206. package/dist/components/quiz/quiz.js +5 -5
  207. package/dist/components/radio-group/radio-group.js +2 -2
  208. package/dist/components/rating/rating.js +3 -3
  209. package/dist/components/relationship-inspector/index.js +6 -0
  210. package/dist/components/relationship-inspector/relationship-inspector.js +102 -0
  211. package/dist/components/resizable/resizable.js +1 -1
  212. package/dist/components/right-dock/index.js +4 -0
  213. package/dist/components/right-dock/right-dock.js +28 -0
  214. package/dist/components/route-map/index.js +6 -0
  215. package/dist/components/route-map/route-map.js +339 -0
  216. package/dist/components/routing-assignment-panel/index.js +6 -0
  217. package/dist/components/routing-assignment-panel/routing-assignment-panel.js +122 -0
  218. package/dist/components/run-timeline/index.js +6 -0
  219. package/dist/components/run-timeline/run-timeline.js +221 -0
  220. package/dist/components/runtime-overview-panel/index.js +6 -0
  221. package/dist/components/runtime-overview-panel/runtime-overview-panel.js +89 -0
  222. package/dist/components/scope-selector/scope-selector.js +7 -7
  223. package/dist/components/search-bar/search-bar.js +24 -2
  224. package/dist/components/search-dialog/search-dialog.js +1 -1
  225. package/dist/components/segmented-control/index.js +12 -0
  226. package/dist/components/segmented-control/segmented-control.js +61 -0
  227. package/dist/components/select/select.js +5 -5
  228. package/dist/components/selection-halo/index.js +6 -0
  229. package/dist/components/selection-halo/selection-halo.js +72 -0
  230. package/dist/components/selection-presence/index.js +6 -0
  231. package/dist/components/selection-presence/selection-presence.js +50 -0
  232. package/dist/components/severity-badge/severity-badge.js +2 -2
  233. package/dist/components/sheet/sheet.js +1 -1
  234. package/dist/components/sidebar/sidebar.js +1 -1
  235. package/dist/components/sidebar-toggle/sidebar-toggle.js +2 -2
  236. package/dist/components/slider/slider.js +1 -1
  237. package/dist/components/slideshow/slideshow.js +12 -11
  238. package/dist/components/snap-guides/index.js +6 -0
  239. package/dist/components/snap-guides/snap-guides.js +45 -0
  240. package/dist/components/social-fab/social-fab.js +2 -1
  241. package/dist/components/sparkline-grid/sparkline-grid.js +1 -1
  242. package/dist/components/spinner/spinner.js +3 -3
  243. package/dist/components/spinner/unicode-spinner.js +1 -0
  244. package/dist/components/stat-card/stat-card.js +3 -3
  245. package/dist/components/state-badge-overlay/index.js +6 -0
  246. package/dist/components/state-badge-overlay/state-badge-overlay.js +90 -0
  247. package/dist/components/status-board/status-board.js +1 -1
  248. package/dist/components/status-indicator/status-indicator.js +3 -3
  249. package/dist/components/step-by-step/step-by-step.js +8 -7
  250. package/dist/components/step-navigation/step-navigation.js +2 -2
  251. package/dist/components/stepper/stepper.js +3 -3
  252. package/dist/components/sticky-metric/index.js +6 -0
  253. package/dist/components/sticky-metric/sticky-metric.js +83 -0
  254. package/dist/components/story-map/index.js +8 -0
  255. package/dist/components/story-map/story-map.js +414 -0
  256. package/dist/components/subscription-card/subscription-card.js +1 -1
  257. package/dist/components/switch/switch.js +1 -1
  258. package/dist/components/table-of-contents-panel/table-of-contents-panel.js +5 -5
  259. package/dist/components/tags-input/index.js +4 -0
  260. package/dist/components/tags-input/tags-input.js +178 -0
  261. package/dist/components/terminal/terminal.js +48 -34
  262. package/dist/components/thinking-block/thinking-block.js +1 -1
  263. package/dist/components/thread-bubble/index.js +6 -0
  264. package/dist/components/thread-bubble/thread-bubble.js +85 -0
  265. package/dist/components/threshold-ring/index.js +6 -0
  266. package/dist/components/threshold-ring/threshold-ring.js +160 -0
  267. package/dist/components/ticker-tape/ticker-tape.js +3 -3
  268. package/dist/components/timeline/index.js +12 -0
  269. package/dist/components/timeline/timeline.js +239 -0
  270. package/dist/components/timeline-scrubber/index.js +6 -0
  271. package/dist/components/timeline-scrubber/timeline-scrubber.js +179 -0
  272. package/dist/components/tldr-section/tldr-section.js +2 -2
  273. package/dist/components/top-bar/index.js +4 -0
  274. package/dist/components/top-bar/top-bar.js +31 -0
  275. package/dist/components/tour/tour.js +1 -1
  276. package/dist/components/transaction-list/index.js +14 -0
  277. package/dist/components/transaction-list/transaction-list.js +247 -0
  278. package/dist/components/tree-view/index.js +6 -0
  279. package/dist/components/tree-view/tree-view.js +298 -0
  280. package/dist/components/tutorial-complete/tutorial-complete.js +8 -8
  281. package/dist/components/usage-breakdown/usage-breakdown.js +10 -5
  282. package/dist/components/video-embed/video-embed.js +2 -2
  283. package/dist/components/viewport-bookmarks/index.js +6 -0
  284. package/dist/components/viewport-bookmarks/viewport-bookmarks.js +116 -0
  285. package/dist/components/wallet-card/wallet-card.js +1 -1
  286. package/dist/components/watchlist/watchlist.js +5 -5
  287. package/dist/components/workspace-switcher/index.js +6 -0
  288. package/dist/components/workspace-switcher/workspace-switcher.js +61 -0
  289. package/dist/components/world-breadcrumbs/index.js +6 -0
  290. package/dist/components/world-breadcrumbs/world-breadcrumbs.js +114 -0
  291. package/dist/components/world-clock-bar/world-clock-bar.js +32 -12
  292. package/dist/components/zoom-hud/index.js +4 -0
  293. package/dist/components/zoom-hud/zoom-hud.js +61 -0
  294. package/dist/index.d.ts +7907 -226
  295. package/dist/index.js +3 -1
  296. package/package.json +9 -5
@@ -0,0 +1,529 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "../../lib/utils";
5
+ import { Badge } from "../badge";
6
+ import {
7
+ Card,
8
+ CardContent,
9
+ CardDescription,
10
+ CardHeader,
11
+ CardTitle
12
+ } from "../card";
13
+ import { CHECKLIST_PROGRESS_EVENT } from "../checklist";
14
+ import { ProgressBar } from "../progress-bar";
15
+ const ProgressTrackerContext = React.createContext(null);
16
+ function clampPercentage(value) {
17
+ if (Number.isNaN(value)) return 0;
18
+ if (value <= 1) return Math.round(Math.max(0, value) * 100);
19
+ return Math.round(Math.min(Math.max(0, value), 100));
20
+ }
21
+ const STATUS_LABELS = {
22
+ available: "Available",
23
+ completed: "Completed",
24
+ "in-progress": "In progress",
25
+ locked: "Locked"
26
+ };
27
+ const STATUS_CLASSES = {
28
+ available: "border-secondary bg-secondary text-secondary-foreground",
29
+ completed: "border-primary/20 bg-primary text-primary-foreground",
30
+ "in-progress": "border-primary/20 bg-primary/10 text-primary",
31
+ locked: "border-border bg-muted text-muted-foreground"
32
+ };
33
+ function getStatusLabel(status) {
34
+ return STATUS_LABELS[status];
35
+ }
36
+ function getStatusClasses(status) {
37
+ return STATUS_CLASSES[status];
38
+ }
39
+ function readPersistedChecklistItems(persistKey) {
40
+ if (!persistKey || typeof window === "undefined") return [];
41
+ try {
42
+ const saved = localStorage.getItem(`checklist:${persistKey}`);
43
+ if (!saved) return [];
44
+ const parsed = JSON.parse(saved);
45
+ return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
46
+ } catch {
47
+ return [];
48
+ }
49
+ }
50
+ function areStringArraysEqual(left, right) {
51
+ if (left.length !== right.length) return false;
52
+ return left.every((value, index) => value === right[index]);
53
+ }
54
+ function getChecklistPersistKey(event) {
55
+ if (!(event instanceof CustomEvent)) return null;
56
+ const detail = event.detail;
57
+ if (typeof detail !== "object" || detail === null) return null;
58
+ if (!("persistKey" in detail)) return null;
59
+ const { persistKey } = detail;
60
+ return typeof persistKey === "string" ? persistKey : null;
61
+ }
62
+ function getResolvedLessonProgress(module) {
63
+ if (!module.persistKey || !module.checklistItems?.length) {
64
+ return {
65
+ completedLessons: module.completedLessons ?? 0,
66
+ totalLessons: module.lessons
67
+ };
68
+ }
69
+ const validIds = new Set(module.checklistItems.map((item) => item.id));
70
+ const persistedIds = readPersistedChecklistItems(module.persistKey);
71
+ const completedLessons = persistedIds.filter((id) => validIds.has(id)).length;
72
+ return {
73
+ completedLessons,
74
+ totalLessons: module.checklistItems.length
75
+ };
76
+ }
77
+ function useChecklistProgress(checklistItems = [], persistKey) {
78
+ const total = checklistItems.length;
79
+ const [persistedIds, setPersistedIds] = React.useState(
80
+ () => readPersistedChecklistItems(persistKey)
81
+ );
82
+ const setPersistedIdsIfChanged = React.useCallback((nextIds) => {
83
+ setPersistedIds(
84
+ (currentIds) => areStringArraysEqual(currentIds, nextIds) ? currentIds : nextIds
85
+ );
86
+ }, []);
87
+ React.useEffect(() => {
88
+ setPersistedIdsIfChanged(readPersistedChecklistItems(persistKey));
89
+ }, [persistKey, setPersistedIdsIfChanged]);
90
+ React.useEffect(() => {
91
+ if (!persistKey || typeof window === "undefined") return;
92
+ const sync = (event) => {
93
+ const eventPersistKey = getChecklistPersistKey(event);
94
+ if (eventPersistKey && eventPersistKey !== persistKey) return;
95
+ setPersistedIdsIfChanged(readPersistedChecklistItems(persistKey));
96
+ };
97
+ const syncEventListener = (event) => {
98
+ sync(event);
99
+ };
100
+ window.addEventListener("storage", sync);
101
+ window.addEventListener("focus", sync);
102
+ window.addEventListener(CHECKLIST_PROGRESS_EVENT, syncEventListener);
103
+ return () => {
104
+ window.removeEventListener("storage", sync);
105
+ window.removeEventListener("focus", sync);
106
+ window.removeEventListener(CHECKLIST_PROGRESS_EVENT, syncEventListener);
107
+ };
108
+ }, [persistKey, setPersistedIdsIfChanged]);
109
+ if (!persistKey || total === 0) return null;
110
+ const validIds = new Set(checklistItems.map((item) => item.id));
111
+ const completedCount = persistedIds.filter((id) => validIds.has(id)).length;
112
+ return {
113
+ completedCount,
114
+ progress: total > 0 ? Math.round(completedCount / total * 100) : 0,
115
+ total
116
+ };
117
+ }
118
+ function useProgressTrackerContext() {
119
+ const context = React.useContext(ProgressTrackerContext);
120
+ if (!context) {
121
+ throw new Error(
122
+ "ProgressTracker compound components must be used within <ProgressTracker />."
123
+ );
124
+ }
125
+ return context;
126
+ }
127
+ const EMPTY_PROGRESS_TRACKER_MODULES = [];
128
+ const EMPTY_PROGRESS_TRACKER_SKILLS = [];
129
+ function ProgressTrackerRoot({
130
+ children,
131
+ className,
132
+ modules = EMPTY_PROGRESS_TRACKER_MODULES,
133
+ overallProgress,
134
+ streak = 0,
135
+ title = "Learning progress",
136
+ ...props
137
+ }) {
138
+ const contextValue = React.useMemo(
139
+ () => ({
140
+ modules,
141
+ overallProgress: clampPercentage(overallProgress),
142
+ streak,
143
+ title
144
+ }),
145
+ [modules, overallProgress, streak, title]
146
+ );
147
+ return /* @__PURE__ */ jsx(ProgressTrackerContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
148
+ "section",
149
+ {
150
+ "aria-label": title,
151
+ className: cn("grid gap-6", className),
152
+ ...props,
153
+ children
154
+ }
155
+ ) });
156
+ }
157
+ function ProgressTrackerOverview({
158
+ className,
159
+ description = "Track completion across modules, lessons, and exercises.",
160
+ label = "Overall progress",
161
+ ...props
162
+ }) {
163
+ const { modules, overallProgress, streak, title } = useProgressTrackerContext();
164
+ const trackedPersistKeys = React.useMemo(
165
+ () => modules.map((module) => module.persistKey).filter(Boolean),
166
+ [modules]
167
+ );
168
+ const [, forceChecklistRefresh] = React.useState(0);
169
+ React.useEffect(() => {
170
+ if (trackedPersistKeys.length === 0 || typeof window === "undefined") {
171
+ return;
172
+ }
173
+ const trackedKeys = new Set(trackedPersistKeys);
174
+ const sync = (event) => {
175
+ const eventPersistKey = getChecklistPersistKey(event);
176
+ if (eventPersistKey && !trackedKeys.has(eventPersistKey)) return;
177
+ forceChecklistRefresh((version) => version + 1);
178
+ };
179
+ const syncEventListener = (event) => {
180
+ sync(event);
181
+ };
182
+ window.addEventListener("storage", sync);
183
+ window.addEventListener("focus", sync);
184
+ window.addEventListener(CHECKLIST_PROGRESS_EVENT, syncEventListener);
185
+ return () => {
186
+ window.removeEventListener("storage", sync);
187
+ window.removeEventListener("focus", sync);
188
+ window.removeEventListener(CHECKLIST_PROGRESS_EVENT, syncEventListener);
189
+ };
190
+ }, [trackedPersistKeys]);
191
+ const radius = 54;
192
+ const circumference = 2 * Math.PI * radius;
193
+ const offset = circumference - overallProgress / 100 * circumference;
194
+ const completedModules = modules.filter(
195
+ (module) => module.status === "completed"
196
+ ).length;
197
+ const lessonTotals = modules.reduce(
198
+ (totals, module) => {
199
+ const resolvedProgress = getResolvedLessonProgress(module);
200
+ return {
201
+ completedLessons: totals.completedLessons + resolvedProgress.completedLessons,
202
+ totalLessons: totals.totalLessons + resolvedProgress.totalLessons
203
+ };
204
+ },
205
+ { completedLessons: 0, totalLessons: 0 }
206
+ );
207
+ const totalLessons = lessonTotals.totalLessons;
208
+ const completedLessons = lessonTotals.completedLessons;
209
+ const totalExercises = modules.reduce(
210
+ (sum, module) => sum + (module.exercises ?? 0),
211
+ 0
212
+ );
213
+ const completedExercises = modules.reduce(
214
+ (sum, module) => sum + (module.completedExercises ?? 0),
215
+ 0
216
+ );
217
+ return /* @__PURE__ */ jsxs(Card, { className: cn("overflow-hidden", className), ...props, children: [
218
+ /* @__PURE__ */ jsxs(CardHeader, { className: "gap-4 sm:flex-row sm:items-start sm:justify-between", children: [
219
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
220
+ /* @__PURE__ */ jsx(Badge, { className: "w-fit", variant: "secondary", children: label }),
221
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
222
+ /* @__PURE__ */ jsx(CardTitle, { children: title }),
223
+ /* @__PURE__ */ jsx(CardDescription, { children: description })
224
+ ] })
225
+ ] }),
226
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-full border bg-background px-3 py-1 text-sm text-muted-foreground whitespace-nowrap", children: [
227
+ /* @__PURE__ */ jsx(
228
+ "span",
229
+ {
230
+ "aria-hidden": "true",
231
+ className: "inline-flex size-2.5 rounded-full bg-primary"
232
+ }
233
+ ),
234
+ /* @__PURE__ */ jsxs("span", { children: [
235
+ /* @__PURE__ */ jsx("span", { className: "font-semibold text-foreground", children: streak }),
236
+ " day",
237
+ streak === 1 ? "" : "s",
238
+ " streak"
239
+ ] })
240
+ ] })
241
+ ] }),
242
+ /* @__PURE__ */ jsxs(CardContent, { className: "grid gap-6 lg:grid-cols-[auto,1fr] lg:items-center", children: [
243
+ /* @__PURE__ */ jsxs("div", { className: "mx-auto flex flex-col items-center gap-3 text-center", children: [
244
+ /* @__PURE__ */ jsxs("div", { className: "relative flex size-36 items-center justify-center", children: [
245
+ /* @__PURE__ */ jsxs("svg", { className: "size-36 -rotate-90", viewBox: "0 0 120 120", children: [
246
+ /* @__PURE__ */ jsx(
247
+ "circle",
248
+ {
249
+ className: "stroke-muted",
250
+ cx: "60",
251
+ cy: "60",
252
+ fill: "none",
253
+ r: radius,
254
+ strokeWidth: "10"
255
+ }
256
+ ),
257
+ /* @__PURE__ */ jsx(
258
+ "circle",
259
+ {
260
+ "aria-label": label,
261
+ "aria-valuemax": 100,
262
+ "aria-valuemin": 0,
263
+ "aria-valuenow": overallProgress,
264
+ className: "stroke-primary transition-all duration-500",
265
+ cx: "60",
266
+ cy: "60",
267
+ fill: "none",
268
+ r: radius,
269
+ role: "progressbar",
270
+ strokeDasharray: circumference,
271
+ strokeDashoffset: offset,
272
+ strokeLinecap: "round",
273
+ strokeWidth: "10"
274
+ }
275
+ )
276
+ ] }),
277
+ /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 flex flex-col items-center justify-center", children: [
278
+ /* @__PURE__ */ jsxs("span", { className: "text-3xl font-semibold text-foreground", children: [
279
+ overallProgress,
280
+ "%"
281
+ ] }),
282
+ /* @__PURE__ */ jsx("span", { className: "text-xs uppercase tracking-[0.2em] text-muted-foreground", children: "Complete" })
283
+ ] })
284
+ ] }),
285
+ /* @__PURE__ */ jsxs("p", { className: "max-w-52 text-sm text-muted-foreground", children: [
286
+ completedModules,
287
+ " of ",
288
+ modules.length,
289
+ " modules completed."
290
+ ] })
291
+ ] }),
292
+ /* @__PURE__ */ jsxs("dl", { className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-4", children: [
293
+ /* @__PURE__ */ jsxs("div", { className: "rounded-xl border bg-muted/30 p-4", children: [
294
+ /* @__PURE__ */ jsx("dt", { className: "text-sm text-muted-foreground", children: "Modules" }),
295
+ /* @__PURE__ */ jsxs("dd", { className: "mt-2 text-2xl font-semibold text-foreground", children: [
296
+ completedModules,
297
+ "/",
298
+ modules.length
299
+ ] })
300
+ ] }),
301
+ /* @__PURE__ */ jsxs("div", { className: "rounded-xl border bg-muted/30 p-4", children: [
302
+ /* @__PURE__ */ jsx("dt", { className: "text-sm text-muted-foreground", children: "Lessons" }),
303
+ /* @__PURE__ */ jsxs("dd", { className: "mt-2 text-2xl font-semibold text-foreground", children: [
304
+ completedLessons,
305
+ "/",
306
+ totalLessons
307
+ ] })
308
+ ] }),
309
+ /* @__PURE__ */ jsxs("div", { className: "rounded-xl border bg-muted/30 p-4", children: [
310
+ /* @__PURE__ */ jsx("dt", { className: "text-sm text-muted-foreground", children: "Exercises" }),
311
+ /* @__PURE__ */ jsxs("dd", { className: "mt-2 text-2xl font-semibold text-foreground", children: [
312
+ completedExercises,
313
+ "/",
314
+ totalExercises
315
+ ] })
316
+ ] }),
317
+ /* @__PURE__ */ jsxs("div", { className: "rounded-xl border bg-muted/30 p-4", children: [
318
+ /* @__PURE__ */ jsx("dt", { className: "text-sm text-muted-foreground", children: "Momentum" }),
319
+ /* @__PURE__ */ jsxs("dd", { className: "mt-2 text-2xl font-semibold text-foreground", children: [
320
+ streak,
321
+ " day",
322
+ streak === 1 ? "" : "s"
323
+ ] })
324
+ ] })
325
+ ] })
326
+ ] })
327
+ ] });
328
+ }
329
+ function ProgressTrackerModules({
330
+ children,
331
+ className,
332
+ ...props
333
+ }) {
334
+ return /* @__PURE__ */ jsx(
335
+ "div",
336
+ {
337
+ className: cn("grid gap-4 md:grid-cols-2 xl:grid-cols-3", className),
338
+ ...props,
339
+ children
340
+ }
341
+ );
342
+ }
343
+ function ProgressTrackerModule({
344
+ badge,
345
+ checklistItems,
346
+ className,
347
+ completedExercises,
348
+ completedLessons = 0,
349
+ currentLesson,
350
+ description,
351
+ exercises,
352
+ href,
353
+ id,
354
+ lessons,
355
+ persistKey,
356
+ progress,
357
+ skills = EMPTY_PROGRESS_TRACKER_SKILLS,
358
+ status,
359
+ timeSpent,
360
+ title,
361
+ ...props
362
+ }) {
363
+ const checklistProgress = useChecklistProgress(checklistItems, persistKey);
364
+ const resolvedLessons = checklistProgress?.completedCount ?? completedLessons;
365
+ const resolvedLessonTotal = checklistProgress?.total || lessons;
366
+ const progressPercent = checklistProgress?.progress ?? clampPercentage(progress);
367
+ const progressValue = Math.min(resolvedLessons, resolvedLessonTotal);
368
+ const safeExerciseTotal = exercises ?? 0;
369
+ const safeExerciseComplete = Math.min(
370
+ completedExercises ?? 0,
371
+ safeExerciseTotal
372
+ );
373
+ const card = /* @__PURE__ */ jsxs(
374
+ Card,
375
+ {
376
+ "aria-disabled": status === "locked",
377
+ className: cn(
378
+ "h-full",
379
+ status === "locked" && "opacity-80",
380
+ href && "transition-shadow hover:shadow-md focus-within:shadow-md",
381
+ className
382
+ ),
383
+ id,
384
+ ...props,
385
+ children: [
386
+ /* @__PURE__ */ jsxs(CardHeader, { className: "gap-4", children: [
387
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
388
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
389
+ /* @__PURE__ */ jsx(CardTitle, { className: "text-xl", children: title }),
390
+ description ? /* @__PURE__ */ jsx(CardDescription, { children: description }) : null
391
+ ] }),
392
+ /* @__PURE__ */ jsx(
393
+ Badge,
394
+ {
395
+ className: cn("whitespace-nowrap", getStatusClasses(status)),
396
+ variant: "outline",
397
+ children: getStatusLabel(status)
398
+ }
399
+ )
400
+ ] }),
401
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
402
+ badge ? /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: badge }) : null,
403
+ currentLesson ? /* @__PURE__ */ jsxs(Badge, { variant: "outline", children: [
404
+ "Current: ",
405
+ currentLesson
406
+ ] }) : null,
407
+ timeSpent ? /* @__PURE__ */ jsx(Badge, { variant: "outline", children: timeSpent }) : null
408
+ ] })
409
+ ] }),
410
+ /* @__PURE__ */ jsxs(CardContent, { className: "space-y-4", children: [
411
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
412
+ /* @__PURE__ */ jsx(
413
+ "div",
414
+ {
415
+ "aria-label": `${title} progress`,
416
+ "aria-valuemax": 100,
417
+ "aria-valuemin": 0,
418
+ "aria-valuenow": progressPercent,
419
+ role: "progressbar",
420
+ children: /* @__PURE__ */ jsx(
421
+ ProgressBar,
422
+ {
423
+ completedLabel: "lessons",
424
+ currentLabel: `${progressPercent}% complete`,
425
+ isComplete: status === "completed",
426
+ max: resolvedLessonTotal,
427
+ showLabels: true,
428
+ value: progressValue
429
+ }
430
+ )
431
+ }
432
+ ),
433
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-2 text-sm text-muted-foreground sm:grid-cols-2", children: [
434
+ /* @__PURE__ */ jsxs("span", { children: [
435
+ "Lessons:",
436
+ " ",
437
+ /* @__PURE__ */ jsxs("span", { className: "font-medium text-foreground", children: [
438
+ resolvedLessons,
439
+ "/",
440
+ resolvedLessonTotal
441
+ ] })
442
+ ] }),
443
+ /* @__PURE__ */ jsxs("span", { children: [
444
+ "Exercises:",
445
+ " ",
446
+ /* @__PURE__ */ jsxs("span", { className: "font-medium text-foreground", children: [
447
+ safeExerciseComplete,
448
+ "/",
449
+ safeExerciseTotal
450
+ ] })
451
+ ] })
452
+ ] })
453
+ ] }),
454
+ skills.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: skills.map((skill) => /* @__PURE__ */ jsx(ProgressTrackerBadge, { children: skill }, skill)) }) : null
455
+ ] })
456
+ ]
457
+ }
458
+ );
459
+ if (!href || status === "locked") return card;
460
+ return /* @__PURE__ */ jsx(
461
+ "a",
462
+ {
463
+ className: "block rounded-xl focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
464
+ href,
465
+ children: card
466
+ }
467
+ );
468
+ }
469
+ function ProgressTrackerStats({
470
+ children,
471
+ className,
472
+ ...props
473
+ }) {
474
+ return /* @__PURE__ */ jsx(
475
+ "div",
476
+ {
477
+ className: cn("grid gap-4 sm:grid-cols-2 xl:grid-cols-4", className),
478
+ ...props,
479
+ children
480
+ }
481
+ );
482
+ }
483
+ function ProgressTrackerStat({
484
+ className,
485
+ label,
486
+ value,
487
+ ...props
488
+ }) {
489
+ return /* @__PURE__ */ jsx(Card, { className: cn("h-full", className), ...props, children: /* @__PURE__ */ jsxs(CardContent, { className: "flex h-full flex-col justify-center gap-2 p-6", children: [
490
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: label }),
491
+ /* @__PURE__ */ jsx("span", { className: "text-2xl font-semibold text-foreground", children: value })
492
+ ] }) });
493
+ }
494
+ function ProgressTrackerBadge({
495
+ children,
496
+ className,
497
+ ...props
498
+ }) {
499
+ return /* @__PURE__ */ jsx(
500
+ Badge,
501
+ {
502
+ className: cn(
503
+ "px-3 py-1 text-[11px] uppercase tracking-wide whitespace-nowrap",
504
+ className
505
+ ),
506
+ variant: "secondary",
507
+ ...props,
508
+ children
509
+ }
510
+ );
511
+ }
512
+ const ProgressTracker = Object.assign(ProgressTrackerRoot, {
513
+ Badge: ProgressTrackerBadge,
514
+ Module: ProgressTrackerModule,
515
+ Modules: ProgressTrackerModules,
516
+ Overview: ProgressTrackerOverview,
517
+ Stat: ProgressTrackerStat,
518
+ Stats: ProgressTrackerStats
519
+ });
520
+ export {
521
+ ProgressTracker,
522
+ ProgressTrackerBadge,
523
+ ProgressTrackerModule,
524
+ ProgressTrackerModules,
525
+ ProgressTrackerOverview,
526
+ ProgressTrackerStat,
527
+ ProgressTrackerStats,
528
+ useProgressTrackerContext
529
+ };
@@ -0,0 +1,6 @@
1
+ import {
2
+ PromptTemplates
3
+ } from "./prompt-templates";
4
+ export {
5
+ PromptTemplates
6
+ };