coursecode 0.1.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 (362) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +322 -0
  3. package/THIRD_PARTY_NOTICES.md +22 -0
  4. package/bin/cli.js +331 -0
  5. package/framework/assets/logo-coursecode-black.svg +14 -0
  6. package/framework/assets/logo-coursecode-white.svg +14 -0
  7. package/framework/assets/logo-coursecode.svg +14 -0
  8. package/framework/css/01-base.css +160 -0
  9. package/framework/css/02-layout.css +499 -0
  10. package/framework/css/accessibility.css +834 -0
  11. package/framework/css/components/accordions.css +710 -0
  12. package/framework/css/components/assessments.css +520 -0
  13. package/framework/css/components/audio-player.css +570 -0
  14. package/framework/css/components/badges.css +80 -0
  15. package/framework/css/components/breadcrumbs.css +87 -0
  16. package/framework/css/components/buttons.css +707 -0
  17. package/framework/css/components/callouts.css +1280 -0
  18. package/framework/css/components/cards.css +475 -0
  19. package/framework/css/components/carousel.css +193 -0
  20. package/framework/css/components/checkbox-group.css +123 -0
  21. package/framework/css/components/checklist.css +203 -0
  22. package/framework/css/components/collapse.css +96 -0
  23. package/framework/css/components/comparison.css +33 -0
  24. package/framework/css/components/content-image.css +36 -0
  25. package/framework/css/components/document-gallery.css +425 -0
  26. package/framework/css/components/dropdown.css +115 -0
  27. package/framework/css/components/embed-frame.css +142 -0
  28. package/framework/css/components/engagement.css +412 -0
  29. package/framework/css/components/features.css +35 -0
  30. package/framework/css/components/flip-cards.css +253 -0
  31. package/framework/css/components/footer.css +353 -0
  32. package/framework/css/components/forms.css +294 -0
  33. package/framework/css/components/hero.css +216 -0
  34. package/framework/css/components/images.css +528 -0
  35. package/framework/css/components/interactive-timeline.css +274 -0
  36. package/framework/css/components/intro-cards.css +30 -0
  37. package/framework/css/components/lightbox.css +666 -0
  38. package/framework/css/components/loading.css +65 -0
  39. package/framework/css/components/modals.css +235 -0
  40. package/framework/css/components/notifications.css +107 -0
  41. package/framework/css/components/quote.css +150 -0
  42. package/framework/css/components/sidebar.css +684 -0
  43. package/framework/css/components/slide-header.css +52 -0
  44. package/framework/css/components/spinner.css +62 -0
  45. package/framework/css/components/stats.css +44 -0
  46. package/framework/css/components/steps.css +232 -0
  47. package/framework/css/components/tables.css +90 -0
  48. package/framework/css/components/tabs.css +347 -0
  49. package/framework/css/components/timeline.css +154 -0
  50. package/framework/css/components/toggle.css +95 -0
  51. package/framework/css/components/tooltip.css +226 -0
  52. package/framework/css/components/video-player.css +438 -0
  53. package/framework/css/design-tokens.css +707 -0
  54. package/framework/css/framework.css +86 -0
  55. package/framework/css/interactions/accessibility.css +75 -0
  56. package/framework/css/interactions/base.css +92 -0
  57. package/framework/css/interactions/drag-drop.css +295 -0
  58. package/framework/css/interactions/fill-in-the-blank.css +236 -0
  59. package/framework/css/interactions/hotspots.css +69 -0
  60. package/framework/css/interactions/index.css +45 -0
  61. package/framework/css/interactions/interactive-image.css +359 -0
  62. package/framework/css/interactions/likert.css +126 -0
  63. package/framework/css/interactions/matching.css +354 -0
  64. package/framework/css/interactions/numeric-input.css +78 -0
  65. package/framework/css/interactions/sequencing.css +378 -0
  66. package/framework/css/interactions/true-false.css +177 -0
  67. package/framework/css/layouts/article.css +258 -0
  68. package/framework/css/layouts/base.css +30 -0
  69. package/framework/css/layouts/canvas.css +38 -0
  70. package/framework/css/layouts/focused.css +236 -0
  71. package/framework/css/layouts/index.css +29 -0
  72. package/framework/css/layouts/presentation.css +191 -0
  73. package/framework/css/layouts/traditional.css +52 -0
  74. package/framework/css/responsive.css +439 -0
  75. package/framework/css/utilities/accessibility-utils.css +59 -0
  76. package/framework/css/utilities/animations.css +419 -0
  77. package/framework/css/utilities/borders.css +72 -0
  78. package/framework/css/utilities/colors.css +76 -0
  79. package/framework/css/utilities/container.css +46 -0
  80. package/framework/css/utilities/decorative.css +442 -0
  81. package/framework/css/utilities/display.css +257 -0
  82. package/framework/css/utilities/flexbox.css +80 -0
  83. package/framework/css/utilities/grid.css +69 -0
  84. package/framework/css/utilities/icons.css +534 -0
  85. package/framework/css/utilities/lists.css +190 -0
  86. package/framework/css/utilities/spacing.css +167 -0
  87. package/framework/css/utilities/tables.css +81 -0
  88. package/framework/css/utilities/typography.css +159 -0
  89. package/framework/css/utilities/visibility.css +117 -0
  90. package/framework/docs/COURSE_AUTHORING_GUIDE.md +1773 -0
  91. package/framework/docs/COURSE_OUTLINE_GUIDE.md +725 -0
  92. package/framework/docs/COURSE_OUTLINE_TEMPLATE.md +161 -0
  93. package/framework/docs/DATA_MODEL.md +409 -0
  94. package/framework/docs/FRAMEWORK_GUIDE.md +1088 -0
  95. package/framework/docs/USER_GUIDE.md +583 -0
  96. package/framework/docs/examples/cloudflare-channel-relay.js +169 -0
  97. package/framework/docs/examples/cloudflare-data-worker.js +102 -0
  98. package/framework/docs/examples/cloudflare-error-worker.js +228 -0
  99. package/framework/index.html +175 -0
  100. package/framework/js/app/AppActions.js +410 -0
  101. package/framework/js/app/AppState.js +225 -0
  102. package/framework/js/app/AppUI.js +616 -0
  103. package/framework/js/assessment/AssessmentActions.js +615 -0
  104. package/framework/js/assessment/AssessmentFactory.js +471 -0
  105. package/framework/js/assessment/AssessmentState.js +322 -0
  106. package/framework/js/assessment/AssessmentUI.js +451 -0
  107. package/framework/js/automation/api-engagement.js +196 -0
  108. package/framework/js/automation/api-interactions.js +167 -0
  109. package/framework/js/automation/api.js +242 -0
  110. package/framework/js/automation/index.js +41 -0
  111. package/framework/js/components/interactions/drag-drop.js +884 -0
  112. package/framework/js/components/interactions/fill-in.js +535 -0
  113. package/framework/js/components/interactions/hotspot.js +702 -0
  114. package/framework/js/components/interactions/interaction-base.js +511 -0
  115. package/framework/js/components/interactions/likert.js +301 -0
  116. package/framework/js/components/interactions/matching.js +699 -0
  117. package/framework/js/components/interactions/multiple-choice.js +377 -0
  118. package/framework/js/components/interactions/numeric.js +271 -0
  119. package/framework/js/components/interactions/sequencing.js +423 -0
  120. package/framework/js/components/interactions/true-false.js +241 -0
  121. package/framework/js/components/ui-components/accordion.js +442 -0
  122. package/framework/js/components/ui-components/alert.js +88 -0
  123. package/framework/js/components/ui-components/audio-player.js +1193 -0
  124. package/framework/js/components/ui-components/callout.js +121 -0
  125. package/framework/js/components/ui-components/carousel.js +145 -0
  126. package/framework/js/components/ui-components/checkbox-group.js +87 -0
  127. package/framework/js/components/ui-components/checklist.js +40 -0
  128. package/framework/js/components/ui-components/collapse.js +114 -0
  129. package/framework/js/components/ui-components/comparison.js +30 -0
  130. package/framework/js/components/ui-components/conditional-display.js +150 -0
  131. package/framework/js/components/ui-components/content-image.js +41 -0
  132. package/framework/js/components/ui-components/dropdown.js +262 -0
  133. package/framework/js/components/ui-components/embed-frame.js +274 -0
  134. package/framework/js/components/ui-components/features.js +33 -0
  135. package/framework/js/components/ui-components/flip-card.js +230 -0
  136. package/framework/js/components/ui-components/form-validator.js +76 -0
  137. package/framework/js/components/ui-components/hero.js +49 -0
  138. package/framework/js/components/ui-components/index.js +12 -0
  139. package/framework/js/components/ui-components/interactive-image.js +235 -0
  140. package/framework/js/components/ui-components/interactive-timeline.js +285 -0
  141. package/framework/js/components/ui-components/intro-cards.js +35 -0
  142. package/framework/js/components/ui-components/lightbox.js +652 -0
  143. package/framework/js/components/ui-components/modal.js +386 -0
  144. package/framework/js/components/ui-components/notifications.js +145 -0
  145. package/framework/js/components/ui-components/progress.js +88 -0
  146. package/framework/js/components/ui-components/quote.js +41 -0
  147. package/framework/js/components/ui-components/stats.js +33 -0
  148. package/framework/js/components/ui-components/steps.js +41 -0
  149. package/framework/js/components/ui-components/tabs.js +255 -0
  150. package/framework/js/components/ui-components/timeline.js +42 -0
  151. package/framework/js/components/ui-components/toggle-group.js +73 -0
  152. package/framework/js/components/ui-components/tooltip.js +458 -0
  153. package/framework/js/components/ui-components/value-display.js +133 -0
  154. package/framework/js/components/ui-components/video-player.js +686 -0
  155. package/framework/js/core/component-catalog.js +121 -0
  156. package/framework/js/core/event-bus.js +178 -0
  157. package/framework/js/core/interaction-catalog.js +149 -0
  158. package/framework/js/dev/runtime-linter.js +1725 -0
  159. package/framework/js/drivers/cmi5-driver.js +768 -0
  160. package/framework/js/drivers/driver-factory.js +77 -0
  161. package/framework/js/drivers/driver-interface.js +110 -0
  162. package/framework/js/drivers/http-driver-base.js +241 -0
  163. package/framework/js/drivers/lti-driver.js +508 -0
  164. package/framework/js/drivers/proxy-driver.js +444 -0
  165. package/framework/js/drivers/scorm-12-driver.js +560 -0
  166. package/framework/js/drivers/scorm-2004-driver.js +775 -0
  167. package/framework/js/drivers/scorm-driver-base.js +112 -0
  168. package/framework/js/engagement/engagement-manager.js +404 -0
  169. package/framework/js/engagement/engagement-progress.js +191 -0
  170. package/framework/js/engagement/engagement-trackers.js +215 -0
  171. package/framework/js/engagement/requirement-strategies.js +268 -0
  172. package/framework/js/main.js +727 -0
  173. package/framework/js/managers/accessibility-manager.js +499 -0
  174. package/framework/js/managers/assessment-manager.js +230 -0
  175. package/framework/js/managers/audio-manager.js +944 -0
  176. package/framework/js/managers/comment-manager.js +88 -0
  177. package/framework/js/managers/flag-manager.js +86 -0
  178. package/framework/js/managers/interaction-manager.js +254 -0
  179. package/framework/js/managers/interaction-registry.js +96 -0
  180. package/framework/js/managers/objective-manager.js +423 -0
  181. package/framework/js/managers/score-manager.js +441 -0
  182. package/framework/js/managers/video-manager.js +536 -0
  183. package/framework/js/navigation/Breadcrumbs.js +234 -0
  184. package/framework/js/navigation/NavigationActions.js +1132 -0
  185. package/framework/js/navigation/NavigationState.js +276 -0
  186. package/framework/js/navigation/NavigationUI.js +574 -0
  187. package/framework/js/navigation/document-gallery.js +357 -0
  188. package/framework/js/navigation/navigation-helpers.js +175 -0
  189. package/framework/js/navigation/navigation-validators.js +174 -0
  190. package/framework/js/state/index.js +8 -0
  191. package/framework/js/state/lms-connection.js +482 -0
  192. package/framework/js/state/lms-error-utils.js +58 -0
  193. package/framework/js/state/state-commits.js +200 -0
  194. package/framework/js/state/state-domains.js +86 -0
  195. package/framework/js/state/state-manager.js +502 -0
  196. package/framework/js/state/state-validation.js +311 -0
  197. package/framework/js/state/transaction-log.js +41 -0
  198. package/framework/js/state/xapi-statement-service.js +325 -0
  199. package/framework/js/utilities/access-control.js +99 -0
  200. package/framework/js/utilities/breakpoint-manager.js +315 -0
  201. package/framework/js/utilities/canvas-slide.js +35 -0
  202. package/framework/js/utilities/conditional-display.js +388 -0
  203. package/framework/js/utilities/course-channel.js +214 -0
  204. package/framework/js/utilities/course-helpers.js +420 -0
  205. package/framework/js/utilities/data-reporter.js +273 -0
  206. package/framework/js/utilities/error-reporter.js +313 -0
  207. package/framework/js/utilities/hotspot-helper.js +341 -0
  208. package/framework/js/utilities/icons.js +348 -0
  209. package/framework/js/utilities/logger.js +92 -0
  210. package/framework/js/utilities/markdown-renderer.js +45 -0
  211. package/framework/js/utilities/scroll-tracker.js +68 -0
  212. package/framework/js/utilities/ui-initializer.js +146 -0
  213. package/framework/js/utilities/utilities.js +293 -0
  214. package/framework/js/utilities/view-manager.js +227 -0
  215. package/framework/js/validation/html-validators.js +422 -0
  216. package/framework/js/validation/scorm-validators.js +438 -0
  217. package/framework/js/vendor/pipwerks.js +931 -0
  218. package/framework/scripts/generate-narration.js +629 -0
  219. package/framework/scripts/tts-providers/azure-provider.js +178 -0
  220. package/framework/scripts/tts-providers/base-provider.js +81 -0
  221. package/framework/scripts/tts-providers/deepgram-provider.js +135 -0
  222. package/framework/scripts/tts-providers/elevenlabs-provider.js +148 -0
  223. package/framework/scripts/tts-providers/google-provider.js +272 -0
  224. package/framework/scripts/tts-providers/index.js +158 -0
  225. package/framework/scripts/tts-providers/openai-provider.js +143 -0
  226. package/framework/version.json +63 -0
  227. package/lib/authoring-api.js +919 -0
  228. package/lib/build-linter.js +450 -0
  229. package/lib/build-packaging.js +186 -0
  230. package/lib/build.js +88 -0
  231. package/lib/cloud.js +691 -0
  232. package/lib/convert.js +341 -0
  233. package/lib/course-parser.js +936 -0
  234. package/lib/course-writer.js +258 -0
  235. package/lib/create.js +248 -0
  236. package/lib/css-index.js +237 -0
  237. package/lib/dev.js +51 -0
  238. package/lib/export-content.js +1246 -0
  239. package/lib/headless-browser.js +413 -0
  240. package/lib/import.js +377 -0
  241. package/lib/index.js +80 -0
  242. package/lib/info.js +79 -0
  243. package/lib/interaction-formatters.js +568 -0
  244. package/lib/manifest/cmi5-manifest.js +63 -0
  245. package/lib/manifest/lti-tool-config.js +53 -0
  246. package/lib/manifest/manifest-factory.js +99 -0
  247. package/lib/manifest/scorm-12-manifest.js +61 -0
  248. package/lib/manifest/scorm-2004-manifest.js +94 -0
  249. package/lib/manifest/scorm-proxy-manifest.js +104 -0
  250. package/lib/manifest-parser.js +96 -0
  251. package/lib/mcp-prompts.js +753 -0
  252. package/lib/mcp-server.js +316 -0
  253. package/lib/narration.js +53 -0
  254. package/lib/pdf-structure.js +142 -0
  255. package/lib/preview-export.js +231 -0
  256. package/lib/preview-routes-api.js +662 -0
  257. package/lib/preview-routes-editing.js +159 -0
  258. package/lib/preview-routes-lms.js +230 -0
  259. package/lib/preview-server.js +564 -0
  260. package/lib/project-utils.js +269 -0
  261. package/lib/proxy-templates/proxy.html +68 -0
  262. package/lib/proxy-templates/scorm-bridge.js +112 -0
  263. package/lib/scaffold.js +193 -0
  264. package/lib/schema-extractor.js +361 -0
  265. package/lib/slide-source-editor.js +586 -0
  266. package/lib/stub-player/app-viewer.js +195 -0
  267. package/lib/stub-player/app.js +370 -0
  268. package/lib/stub-player/catalog-panel.js +312 -0
  269. package/lib/stub-player/config-panel.js +1303 -0
  270. package/lib/stub-player/content-generator.js +586 -0
  271. package/lib/stub-player/content-viewer.js +173 -0
  272. package/lib/stub-player/debug-panel.js +420 -0
  273. package/lib/stub-player/edit-mode.js +922 -0
  274. package/lib/stub-player/edit-utils.js +400 -0
  275. package/lib/stub-player/header-bar.js +354 -0
  276. package/lib/stub-player/interaction-editor.js +210 -0
  277. package/lib/stub-player/interactions-panel.js +565 -0
  278. package/lib/stub-player/lms-api.js +1094 -0
  279. package/lib/stub-player/login-screen.js +74 -0
  280. package/lib/stub-player/outline-mode.js +689 -0
  281. package/lib/stub-player/styles/_assessments-panel.css +245 -0
  282. package/lib/stub-player/styles/_base.css +89 -0
  283. package/lib/stub-player/styles/_catalog-icons.css +96 -0
  284. package/lib/stub-player/styles/_catalog-panel.css +291 -0
  285. package/lib/stub-player/styles/_config-panel.css +636 -0
  286. package/lib/stub-player/styles/_content-viewer.css +834 -0
  287. package/lib/stub-player/styles/_debug-panel.css +576 -0
  288. package/lib/stub-player/styles/_edit-mode.css +128 -0
  289. package/lib/stub-player/styles/_header-bar.css +343 -0
  290. package/lib/stub-player/styles/_interaction-editor.css +140 -0
  291. package/lib/stub-player/styles/_interactions-panel.css +1038 -0
  292. package/lib/stub-player/styles/_login-screen.css +102 -0
  293. package/lib/stub-player/styles/_outline-mode.css +752 -0
  294. package/lib/stub-player/styles.css +15 -0
  295. package/lib/stub-player.js +160 -0
  296. package/lib/test-data-reporting.js +176 -0
  297. package/lib/test-error-reporting.js +146 -0
  298. package/lib/token.js +86 -0
  299. package/lib/upgrade.js +257 -0
  300. package/lib/validation-rules.js +517 -0
  301. package/lib/vite-plugin-content-discovery.js +296 -0
  302. package/package.json +108 -0
  303. package/schemas/XMLSchema.dtd +402 -0
  304. package/schemas/adlcp_v1p3.xsd +111 -0
  305. package/schemas/adlnav_v1p3.xsd +61 -0
  306. package/schemas/adlseq_v1p3.xsd +93 -0
  307. package/schemas/common/anyElement.xsd +27 -0
  308. package/schemas/common/dataTypes.xsd +138 -0
  309. package/schemas/common/elementNames.xsd +767 -0
  310. package/schemas/common/elementTypes.xsd +786 -0
  311. package/schemas/common/rootElement.xsd +31 -0
  312. package/schemas/common/vocabTypes.xsd +345 -0
  313. package/schemas/common/vocabValues.xsd +257 -0
  314. package/schemas/datatypes.dtd +203 -0
  315. package/schemas/ims_xml.xsd +35 -0
  316. package/schemas/imscp_v1p1.xsd +368 -0
  317. package/schemas/imsss_v1p0.xsd +67 -0
  318. package/schemas/imsss_v1p0auxresource.xsd +19 -0
  319. package/schemas/imsss_v1p0control.xsd +20 -0
  320. package/schemas/imsss_v1p0delivery.xsd +17 -0
  321. package/schemas/imsss_v1p0limit.xsd +47 -0
  322. package/schemas/imsss_v1p0objective.xsd +67 -0
  323. package/schemas/imsss_v1p0random.xsd +16 -0
  324. package/schemas/imsss_v1p0rollup.xsd +46 -0
  325. package/schemas/imsss_v1p0seqrule.xsd +108 -0
  326. package/schemas/imsss_v1p0util.xsd +94 -0
  327. package/schemas/license.txt +17 -0
  328. package/schemas/lom.xsd +102 -0
  329. package/schemas/lomCustom.xsd +62 -0
  330. package/schemas/lomLoose.xsd +62 -0
  331. package/schemas/lomStrict.xsd +62 -0
  332. package/schemas/xml.xsd +81 -0
  333. package/template/.env.example +92 -0
  334. package/template/course/assets/audio/example-intro.mp3 +0 -0
  335. package/template/course/assets/audio/example-ui-demo--compact-player.mp3 +0 -0
  336. package/template/course/assets/audio/example-ui-demo--demo-modal.mp3 +0 -0
  337. package/template/course/assets/audio/example-ui-demo--full-player.mp3 +0 -0
  338. package/template/course/assets/docs/example_md_1.md +39 -0
  339. package/template/course/assets/docs/example_md_2.md +41 -0
  340. package/template/course/assets/docs/example_pdf_1_thumbnail.png +0 -0
  341. package/template/course/assets/docs/example_pdf_2.pdf +0 -0
  342. package/template/course/assets/images/course-architecture.svg +36 -0
  343. package/template/course/assets/images/logo.svg +14 -0
  344. package/template/course/assets/widgets/counter-demo.html +190 -0
  345. package/template/course/assets/widgets/gravity-painter.html +384 -0
  346. package/template/course/course-config.js +539 -0
  347. package/template/course/icons.js +19 -0
  348. package/template/course/interactions/PLUGIN_GUIDE.md +97 -0
  349. package/template/course/slides/example-course-structure.js +138 -0
  350. package/template/course/slides/example-final-exam.js +144 -0
  351. package/template/course/slides/example-finishing.js +127 -0
  352. package/template/course/slides/example-interactions-showcase.js +615 -0
  353. package/template/course/slides/example-preview-tour.js +129 -0
  354. package/template/course/slides/example-remedial.js +143 -0
  355. package/template/course/slides/example-summary.js +103 -0
  356. package/template/course/slides/example-ui-showcase.js +1805 -0
  357. package/template/course/slides/example-welcome.js +123 -0
  358. package/template/course/slides/example-workflow.js +140 -0
  359. package/template/course/theme.css +165 -0
  360. package/template/eslint.config.js +47 -0
  361. package/template/package.json +28 -0
  362. package/template/vite.config.js +339 -0
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Component Catalog
3
+ *
4
+ * Single source of truth for all UI components (built-in and custom).
5
+ * Uses import.meta.glob for auto-discovery - just drop a file in the folder.
6
+ *
7
+ * Mirrors the interaction-catalog.js pattern for consistency.
8
+ */
9
+
10
+ const catalog = new Map();
11
+
12
+ /**
13
+ * Register a component type
14
+ * @param {string} type - Component type name (kebab-case, matches data-component value)
15
+ * @param {object} definition - { init?, metadata?, schema, styles? }
16
+ */
17
+ export function catalogComponent(type, { init, metadata, schema, styles }) {
18
+ if (!schema) {
19
+ throw new Error(`[ComponentCatalog] Cannot register "${type}" without a schema`);
20
+ }
21
+ catalog.set(type, { init, metadata, schema, styles });
22
+ }
23
+
24
+ /**
25
+ * Get the init function for a component type
26
+ * @param {string} type - Component type name
27
+ * @returns {function|undefined}
28
+ */
29
+ export function getComponentInit(type) {
30
+ return catalog.get(type)?.init;
31
+ }
32
+
33
+ /**
34
+ * Get metadata for a component type
35
+ * @param {string} type - Component type name
36
+ * @returns {object|undefined}
37
+ */
38
+ export function getComponentMetadata(type) {
39
+ return catalog.get(type)?.metadata;
40
+ }
41
+
42
+ /**
43
+ * Get schema for a component type
44
+ * @param {string} type - Component type name
45
+ * @returns {object|undefined}
46
+ */
47
+ export function getComponentSchema(type) {
48
+ return catalog.get(type)?.schema;
49
+ }
50
+
51
+ /**
52
+ * Get styles for a component type (for custom components with CSS-in-JS)
53
+ * @param {string} type - Component type name
54
+ * @returns {string|undefined}
55
+ */
56
+ export function getComponentStyles(type) {
57
+ return catalog.get(type)?.styles;
58
+ }
59
+
60
+ /**
61
+ * Get all registered schemas
62
+ * @returns {object} Map of type -> schema
63
+ */
64
+ export function getAllComponentSchemas() {
65
+ const schemas = {};
66
+ for (const [type, def] of catalog) {
67
+ if (def.schema) {
68
+ schemas[type] = def.schema;
69
+ }
70
+ }
71
+ return schemas;
72
+ }
73
+
74
+ /**
75
+ * Get all registered type names
76
+ * @returns {string[]}
77
+ */
78
+ export function getRegisteredComponentTypes() {
79
+ return [...catalog.keys()];
80
+ }
81
+
82
+ /**
83
+ * Check if a type is registered
84
+ * @param {string} type
85
+ * @returns {boolean}
86
+ */
87
+ export function isComponentRegistered(type) {
88
+ return catalog.has(type);
89
+ }
90
+
91
+ // =============================================================================
92
+ // Auto-discover Components (Built-in UI + Custom)
93
+ // =============================================================================
94
+
95
+ // Built-in UI components
96
+ const uiComponentModules = import.meta.glob('../components/ui-components/*.js', { eager: true });
97
+
98
+ // Course-specific custom components
99
+ const customComponentModules = import.meta.glob('../../../course/components/*.js', { eager: true });
100
+
101
+ /**
102
+ * Register all discovered modules.
103
+ * Convention: per-element initializer is exported as `init`.
104
+ */
105
+ function registerModules(modules) {
106
+ for (const [path, module] of Object.entries(modules)) {
107
+ // Skip index/base files
108
+ if (path.includes('index.js') || path.includes('-base')) continue;
109
+
110
+ // Each component exports: schema (required), metadata (optional), init (optional), styles (optional)
111
+ const { schema, metadata, styles, init } = module;
112
+
113
+ if (!schema?.type) continue;
114
+
115
+ catalogComponent(schema.type, { init, metadata, schema, styles });
116
+ }
117
+ }
118
+
119
+ // Register synchronously - modules are eagerly loaded
120
+ registerModules(uiComponentModules);
121
+ registerModules(customComponentModules);
@@ -0,0 +1,178 @@
1
+ import { generateId } from '../utilities/utilities.js';
2
+ import { logger } from '../utilities/logger.js';
3
+
4
+ class EventBus {
5
+ constructor() {
6
+ // Event listeners registry
7
+ this.events = {};
8
+ }
9
+
10
+ /**
11
+ * Subscribe to an event
12
+ *
13
+ * @param {string} event - Event name
14
+ * @param {Function} callback - Callback function
15
+ * @param {Object} options - Optional configuration
16
+ * @returns {Function} Unsubscribe function
17
+ */
18
+ on(event, callback, options = {}) {
19
+ if (!event || typeof callback !== 'function') {
20
+ throw new Error('Event name and callback are required');
21
+ }
22
+
23
+ if (!this.events[event]) {
24
+ this.events[event] = [];
25
+ }
26
+
27
+ const listener = {
28
+ callback,
29
+ once: options.once || false,
30
+ id: generateId('listener')
31
+ };
32
+
33
+ this.events[event].push(listener);
34
+
35
+ // Return unsubscribe function
36
+ return () => this.off(event, listener.id);
37
+ }
38
+
39
+ /**
40
+ * Subscribe to an event once (auto-unsubscribe after first trigger)
41
+ *
42
+ * @param {string} event - Event name
43
+ * @param {Function} callback - Callback function
44
+ * @returns {Function} Unsubscribe function
45
+ */
46
+ once(event, callback) {
47
+ return this.on(event, callback, { once: true });
48
+ }
49
+
50
+ /**
51
+ * Unsubscribe from an event
52
+ *
53
+ * @param {string} event - Event name
54
+ * @param {string|Function} listenerIdOrCallback - Listener ID or callback function
55
+ */
56
+ off(event, listenerIdOrCallback) {
57
+ if (!this.events[event]) return;
58
+
59
+ if (typeof listenerIdOrCallback === 'string') {
60
+ // Remove by ID
61
+ this.events[event] = this.events[event].filter(
62
+ listener => listener.id !== listenerIdOrCallback
63
+ );
64
+ } else if (typeof listenerIdOrCallback === 'function') {
65
+ // Remove by callback reference
66
+ this.events[event] = this.events[event].filter(
67
+ listener => listener.callback !== listenerIdOrCallback
68
+ );
69
+ }
70
+
71
+ // Clean up empty event arrays
72
+ if (this.events[event].length === 0) {
73
+ delete this.events[event];
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Emit an event
79
+ *
80
+ * @param {string} event - Event name
81
+ * @param {*} data - Event data
82
+ * @returns {boolean} True if event had listeners
83
+ */
84
+ emit(event, data) {
85
+ if (!this.events[event] || this.events[event].length === 0) {
86
+ return false;
87
+ }
88
+
89
+ // Automatically log events that follow the ':error' naming convention
90
+ if (event.endsWith(':error')) {
91
+ logger.error(`[EventBus Error] ${event}:`, JSON.stringify(data, null, 2));
92
+ }
93
+
94
+ // Create a copy of listeners to avoid issues if listeners modify the array
95
+ const listeners = [...this.events[event]];
96
+ const onceListeners = [];
97
+
98
+ listeners.forEach(listener => {
99
+ try {
100
+ listener.callback(data);
101
+
102
+ // Track once listeners for removal
103
+ if (listener.once) {
104
+ onceListeners.push(listener.id);
105
+ }
106
+ } catch (error) {
107
+ // Log the error but don't break other listeners
108
+ logger.error(`[EventBus] Error in listener for '${event}':`, error);
109
+ }
110
+ });
111
+
112
+ // Remove once listeners
113
+ onceListeners.forEach(id => this.off(event, id));
114
+
115
+ return true;
116
+ }
117
+
118
+ /**
119
+ * Emit an event asynchronously
120
+ *
121
+ * @param {string} event - Event name
122
+ * @param {*} data - Event data
123
+ * @returns {Promise} Resolves when all listeners have been called
124
+ */
125
+ async emitAsync(event, data) {
126
+ if (!this.events[event] || this.events[event].length === 0) {
127
+ return false;
128
+ }
129
+
130
+ const listeners = [...this.events[event]];
131
+ const onceListeners = [];
132
+
133
+ for (const listener of listeners) {
134
+ try {
135
+ await listener.callback(data);
136
+
137
+ if (listener.once) {
138
+ onceListeners.push(listener.id);
139
+ }
140
+ } catch (error) {
141
+ logger.error(`[EventBus] Error in async listener for '${event}':`, error);
142
+ }
143
+ }
144
+
145
+ // Remove once listeners
146
+ onceListeners.forEach(id => this.off(event, id));
147
+
148
+ return true;
149
+ }
150
+
151
+ /**
152
+ * Remove all listeners for an event or all events
153
+ *
154
+ * @param {string} event - Optional event name (if omitted, clears all)
155
+ */
156
+ clear(event) {
157
+ if (event) {
158
+ delete this.events[event];
159
+ } else {
160
+ this.events = {};
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Get listener count for an event
166
+ *
167
+ * @param {string} event - Event name
168
+ * @returns {number} Number of listeners
169
+ */
170
+ getListenerCount(event) {
171
+ return this.events[event] ? this.events[event].length : 0;
172
+ }
173
+ }
174
+
175
+ // Create global event bus instance
176
+ const eventBus = new EventBus();
177
+
178
+ export { EventBus, eventBus };
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Interaction Catalog
3
+ *
4
+ * Single source of truth for all interaction types (built-in and custom).
5
+ * Uses import.meta.glob for auto-discovery - just drop a file in the folder.
6
+ */
7
+
8
+ const catalog = new Map();
9
+
10
+ /**
11
+ * Register an interaction type
12
+ * @param {string} type - Interaction type name (kebab-case)
13
+ * @param {object} definition - { creator, metadata?, schema? }
14
+ */
15
+ export function catalogInteraction(type, { creator, metadata, schema }) {
16
+ if (!creator) {
17
+ throw new Error(`[InteractionCatalog] Cannot register "${type}" without a creator function`);
18
+ }
19
+ catalog.set(type, { creator, metadata, schema });
20
+ }
21
+
22
+ /**
23
+ * Get the creator function for an interaction type
24
+ * @param {string} type - Interaction type name
25
+ * @returns {function|undefined}
26
+ */
27
+ export function getCreator(type) {
28
+ return catalog.get(type)?.creator;
29
+ }
30
+
31
+ /**
32
+ * Get metadata for an interaction type
33
+ * @param {string} type - Interaction type name
34
+ * @returns {object|undefined}
35
+ */
36
+ export function getMetadata(type) {
37
+ return catalog.get(type)?.metadata;
38
+ }
39
+
40
+ /**
41
+ * Get schema for an interaction type
42
+ * @param {string} type - Interaction type name
43
+ * @returns {object|undefined}
44
+ */
45
+ export function getSchema(type) {
46
+ return catalog.get(type)?.schema;
47
+ }
48
+
49
+ /**
50
+ * Get all registered schemas
51
+ * @returns {object} Map of type -> schema
52
+ */
53
+ export function getAllSchemas() {
54
+ const schemas = {};
55
+ for (const [type, def] of catalog) {
56
+ if (def.schema) {
57
+ schemas[type] = def.schema;
58
+ }
59
+ }
60
+ return schemas;
61
+ }
62
+
63
+ /**
64
+ * Get all registered type names
65
+ * @returns {string[]}
66
+ */
67
+ export function getRegisteredTypes() {
68
+ return [...catalog.keys()];
69
+ }
70
+
71
+ /**
72
+ * Check if a type is registered
73
+ * @param {string} type
74
+ * @returns {boolean}
75
+ */
76
+ export function isRegistered(type) {
77
+ return catalog.has(type);
78
+ }
79
+
80
+ /**
81
+ * Get merged schema with base properties
82
+ * @param {string} type - Interaction type name
83
+ * @returns {object} Full schema with base properties merged
84
+ */
85
+ export function getFullSchema(type) {
86
+ const schema = getSchema(type);
87
+ if (!schema) return null;
88
+
89
+ return {
90
+ ...schema,
91
+ properties: {
92
+ ...baseSchema,
93
+ ...schema.properties
94
+ }
95
+ };
96
+ }
97
+
98
+ // =============================================================================
99
+ // Base Schema (shared properties for all interactions)
100
+ // =============================================================================
101
+
102
+ export const baseSchema = {
103
+ id: { type: 'string', required: true, description: 'Unique identifier for the interaction' },
104
+ prompt: { type: 'string', required: true, description: 'Question or instruction text' },
105
+ controlled: { type: 'boolean', default: false, description: 'If true, interaction is managed externally (e.g., by assessment)' },
106
+ feedback: { type: 'object', description: 'Custom feedback messages' }
107
+ };
108
+
109
+ // =============================================================================
110
+ // Auto-discover Interactions (Built-in + Custom)
111
+ // =============================================================================
112
+
113
+ // Built-in framework interactions
114
+ const builtInModules = import.meta.glob('../components/interactions/*.js', { eager: true });
115
+
116
+ // Course-specific custom interactions
117
+ const customModules = import.meta.glob('../../../course/interactions/*.js', { eager: true });
118
+
119
+ // Register all discovered modules
120
+ function registerModules(modules) {
121
+ for (const [path, module] of Object.entries(modules)) {
122
+ // Skip base module
123
+ if (path.includes('interaction-base')) continue;
124
+
125
+ // Each interaction exports: schema, metadata, and a create* function
126
+ const { schema, metadata } = module;
127
+
128
+ if (!schema?.type) continue;
129
+
130
+ // Find the creator function by export key name (not function.name which gets minified)
131
+ const creator = Object.entries(module)
132
+ .filter(([key, v]) => typeof v === 'function' && key.startsWith('create'))
133
+ .map(([, v]) => v)[0];
134
+
135
+ if (!creator) continue;
136
+
137
+ catalogInteraction(schema.type, { creator, metadata, schema });
138
+ }
139
+ }
140
+
141
+ // Register synchronously - modules are eagerly loaded
142
+ registerModules(builtInModules);
143
+ registerModules(customModules);
144
+
145
+ // Multiple-choice-single is an alias for multiple-choice
146
+ if (catalog.has('multiple-choice')) {
147
+ const mc = catalog.get('multiple-choice');
148
+ catalog.set('multiple-choice-single', mc);
149
+ }