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.
- package/LICENSE +21 -0
- package/README.md +322 -0
- package/THIRD_PARTY_NOTICES.md +22 -0
- package/bin/cli.js +331 -0
- package/framework/assets/logo-coursecode-black.svg +14 -0
- package/framework/assets/logo-coursecode-white.svg +14 -0
- package/framework/assets/logo-coursecode.svg +14 -0
- package/framework/css/01-base.css +160 -0
- package/framework/css/02-layout.css +499 -0
- package/framework/css/accessibility.css +834 -0
- package/framework/css/components/accordions.css +710 -0
- package/framework/css/components/assessments.css +520 -0
- package/framework/css/components/audio-player.css +570 -0
- package/framework/css/components/badges.css +80 -0
- package/framework/css/components/breadcrumbs.css +87 -0
- package/framework/css/components/buttons.css +707 -0
- package/framework/css/components/callouts.css +1280 -0
- package/framework/css/components/cards.css +475 -0
- package/framework/css/components/carousel.css +193 -0
- package/framework/css/components/checkbox-group.css +123 -0
- package/framework/css/components/checklist.css +203 -0
- package/framework/css/components/collapse.css +96 -0
- package/framework/css/components/comparison.css +33 -0
- package/framework/css/components/content-image.css +36 -0
- package/framework/css/components/document-gallery.css +425 -0
- package/framework/css/components/dropdown.css +115 -0
- package/framework/css/components/embed-frame.css +142 -0
- package/framework/css/components/engagement.css +412 -0
- package/framework/css/components/features.css +35 -0
- package/framework/css/components/flip-cards.css +253 -0
- package/framework/css/components/footer.css +353 -0
- package/framework/css/components/forms.css +294 -0
- package/framework/css/components/hero.css +216 -0
- package/framework/css/components/images.css +528 -0
- package/framework/css/components/interactive-timeline.css +274 -0
- package/framework/css/components/intro-cards.css +30 -0
- package/framework/css/components/lightbox.css +666 -0
- package/framework/css/components/loading.css +65 -0
- package/framework/css/components/modals.css +235 -0
- package/framework/css/components/notifications.css +107 -0
- package/framework/css/components/quote.css +150 -0
- package/framework/css/components/sidebar.css +684 -0
- package/framework/css/components/slide-header.css +52 -0
- package/framework/css/components/spinner.css +62 -0
- package/framework/css/components/stats.css +44 -0
- package/framework/css/components/steps.css +232 -0
- package/framework/css/components/tables.css +90 -0
- package/framework/css/components/tabs.css +347 -0
- package/framework/css/components/timeline.css +154 -0
- package/framework/css/components/toggle.css +95 -0
- package/framework/css/components/tooltip.css +226 -0
- package/framework/css/components/video-player.css +438 -0
- package/framework/css/design-tokens.css +707 -0
- package/framework/css/framework.css +86 -0
- package/framework/css/interactions/accessibility.css +75 -0
- package/framework/css/interactions/base.css +92 -0
- package/framework/css/interactions/drag-drop.css +295 -0
- package/framework/css/interactions/fill-in-the-blank.css +236 -0
- package/framework/css/interactions/hotspots.css +69 -0
- package/framework/css/interactions/index.css +45 -0
- package/framework/css/interactions/interactive-image.css +359 -0
- package/framework/css/interactions/likert.css +126 -0
- package/framework/css/interactions/matching.css +354 -0
- package/framework/css/interactions/numeric-input.css +78 -0
- package/framework/css/interactions/sequencing.css +378 -0
- package/framework/css/interactions/true-false.css +177 -0
- package/framework/css/layouts/article.css +258 -0
- package/framework/css/layouts/base.css +30 -0
- package/framework/css/layouts/canvas.css +38 -0
- package/framework/css/layouts/focused.css +236 -0
- package/framework/css/layouts/index.css +29 -0
- package/framework/css/layouts/presentation.css +191 -0
- package/framework/css/layouts/traditional.css +52 -0
- package/framework/css/responsive.css +439 -0
- package/framework/css/utilities/accessibility-utils.css +59 -0
- package/framework/css/utilities/animations.css +419 -0
- package/framework/css/utilities/borders.css +72 -0
- package/framework/css/utilities/colors.css +76 -0
- package/framework/css/utilities/container.css +46 -0
- package/framework/css/utilities/decorative.css +442 -0
- package/framework/css/utilities/display.css +257 -0
- package/framework/css/utilities/flexbox.css +80 -0
- package/framework/css/utilities/grid.css +69 -0
- package/framework/css/utilities/icons.css +534 -0
- package/framework/css/utilities/lists.css +190 -0
- package/framework/css/utilities/spacing.css +167 -0
- package/framework/css/utilities/tables.css +81 -0
- package/framework/css/utilities/typography.css +159 -0
- package/framework/css/utilities/visibility.css +117 -0
- package/framework/docs/COURSE_AUTHORING_GUIDE.md +1773 -0
- package/framework/docs/COURSE_OUTLINE_GUIDE.md +725 -0
- package/framework/docs/COURSE_OUTLINE_TEMPLATE.md +161 -0
- package/framework/docs/DATA_MODEL.md +409 -0
- package/framework/docs/FRAMEWORK_GUIDE.md +1088 -0
- package/framework/docs/USER_GUIDE.md +583 -0
- package/framework/docs/examples/cloudflare-channel-relay.js +169 -0
- package/framework/docs/examples/cloudflare-data-worker.js +102 -0
- package/framework/docs/examples/cloudflare-error-worker.js +228 -0
- package/framework/index.html +175 -0
- package/framework/js/app/AppActions.js +410 -0
- package/framework/js/app/AppState.js +225 -0
- package/framework/js/app/AppUI.js +616 -0
- package/framework/js/assessment/AssessmentActions.js +615 -0
- package/framework/js/assessment/AssessmentFactory.js +471 -0
- package/framework/js/assessment/AssessmentState.js +322 -0
- package/framework/js/assessment/AssessmentUI.js +451 -0
- package/framework/js/automation/api-engagement.js +196 -0
- package/framework/js/automation/api-interactions.js +167 -0
- package/framework/js/automation/api.js +242 -0
- package/framework/js/automation/index.js +41 -0
- package/framework/js/components/interactions/drag-drop.js +884 -0
- package/framework/js/components/interactions/fill-in.js +535 -0
- package/framework/js/components/interactions/hotspot.js +702 -0
- package/framework/js/components/interactions/interaction-base.js +511 -0
- package/framework/js/components/interactions/likert.js +301 -0
- package/framework/js/components/interactions/matching.js +699 -0
- package/framework/js/components/interactions/multiple-choice.js +377 -0
- package/framework/js/components/interactions/numeric.js +271 -0
- package/framework/js/components/interactions/sequencing.js +423 -0
- package/framework/js/components/interactions/true-false.js +241 -0
- package/framework/js/components/ui-components/accordion.js +442 -0
- package/framework/js/components/ui-components/alert.js +88 -0
- package/framework/js/components/ui-components/audio-player.js +1193 -0
- package/framework/js/components/ui-components/callout.js +121 -0
- package/framework/js/components/ui-components/carousel.js +145 -0
- package/framework/js/components/ui-components/checkbox-group.js +87 -0
- package/framework/js/components/ui-components/checklist.js +40 -0
- package/framework/js/components/ui-components/collapse.js +114 -0
- package/framework/js/components/ui-components/comparison.js +30 -0
- package/framework/js/components/ui-components/conditional-display.js +150 -0
- package/framework/js/components/ui-components/content-image.js +41 -0
- package/framework/js/components/ui-components/dropdown.js +262 -0
- package/framework/js/components/ui-components/embed-frame.js +274 -0
- package/framework/js/components/ui-components/features.js +33 -0
- package/framework/js/components/ui-components/flip-card.js +230 -0
- package/framework/js/components/ui-components/form-validator.js +76 -0
- package/framework/js/components/ui-components/hero.js +49 -0
- package/framework/js/components/ui-components/index.js +12 -0
- package/framework/js/components/ui-components/interactive-image.js +235 -0
- package/framework/js/components/ui-components/interactive-timeline.js +285 -0
- package/framework/js/components/ui-components/intro-cards.js +35 -0
- package/framework/js/components/ui-components/lightbox.js +652 -0
- package/framework/js/components/ui-components/modal.js +386 -0
- package/framework/js/components/ui-components/notifications.js +145 -0
- package/framework/js/components/ui-components/progress.js +88 -0
- package/framework/js/components/ui-components/quote.js +41 -0
- package/framework/js/components/ui-components/stats.js +33 -0
- package/framework/js/components/ui-components/steps.js +41 -0
- package/framework/js/components/ui-components/tabs.js +255 -0
- package/framework/js/components/ui-components/timeline.js +42 -0
- package/framework/js/components/ui-components/toggle-group.js +73 -0
- package/framework/js/components/ui-components/tooltip.js +458 -0
- package/framework/js/components/ui-components/value-display.js +133 -0
- package/framework/js/components/ui-components/video-player.js +686 -0
- package/framework/js/core/component-catalog.js +121 -0
- package/framework/js/core/event-bus.js +178 -0
- package/framework/js/core/interaction-catalog.js +149 -0
- package/framework/js/dev/runtime-linter.js +1725 -0
- package/framework/js/drivers/cmi5-driver.js +768 -0
- package/framework/js/drivers/driver-factory.js +77 -0
- package/framework/js/drivers/driver-interface.js +110 -0
- package/framework/js/drivers/http-driver-base.js +241 -0
- package/framework/js/drivers/lti-driver.js +508 -0
- package/framework/js/drivers/proxy-driver.js +444 -0
- package/framework/js/drivers/scorm-12-driver.js +560 -0
- package/framework/js/drivers/scorm-2004-driver.js +775 -0
- package/framework/js/drivers/scorm-driver-base.js +112 -0
- package/framework/js/engagement/engagement-manager.js +404 -0
- package/framework/js/engagement/engagement-progress.js +191 -0
- package/framework/js/engagement/engagement-trackers.js +215 -0
- package/framework/js/engagement/requirement-strategies.js +268 -0
- package/framework/js/main.js +727 -0
- package/framework/js/managers/accessibility-manager.js +499 -0
- package/framework/js/managers/assessment-manager.js +230 -0
- package/framework/js/managers/audio-manager.js +944 -0
- package/framework/js/managers/comment-manager.js +88 -0
- package/framework/js/managers/flag-manager.js +86 -0
- package/framework/js/managers/interaction-manager.js +254 -0
- package/framework/js/managers/interaction-registry.js +96 -0
- package/framework/js/managers/objective-manager.js +423 -0
- package/framework/js/managers/score-manager.js +441 -0
- package/framework/js/managers/video-manager.js +536 -0
- package/framework/js/navigation/Breadcrumbs.js +234 -0
- package/framework/js/navigation/NavigationActions.js +1132 -0
- package/framework/js/navigation/NavigationState.js +276 -0
- package/framework/js/navigation/NavigationUI.js +574 -0
- package/framework/js/navigation/document-gallery.js +357 -0
- package/framework/js/navigation/navigation-helpers.js +175 -0
- package/framework/js/navigation/navigation-validators.js +174 -0
- package/framework/js/state/index.js +8 -0
- package/framework/js/state/lms-connection.js +482 -0
- package/framework/js/state/lms-error-utils.js +58 -0
- package/framework/js/state/state-commits.js +200 -0
- package/framework/js/state/state-domains.js +86 -0
- package/framework/js/state/state-manager.js +502 -0
- package/framework/js/state/state-validation.js +311 -0
- package/framework/js/state/transaction-log.js +41 -0
- package/framework/js/state/xapi-statement-service.js +325 -0
- package/framework/js/utilities/access-control.js +99 -0
- package/framework/js/utilities/breakpoint-manager.js +315 -0
- package/framework/js/utilities/canvas-slide.js +35 -0
- package/framework/js/utilities/conditional-display.js +388 -0
- package/framework/js/utilities/course-channel.js +214 -0
- package/framework/js/utilities/course-helpers.js +420 -0
- package/framework/js/utilities/data-reporter.js +273 -0
- package/framework/js/utilities/error-reporter.js +313 -0
- package/framework/js/utilities/hotspot-helper.js +341 -0
- package/framework/js/utilities/icons.js +348 -0
- package/framework/js/utilities/logger.js +92 -0
- package/framework/js/utilities/markdown-renderer.js +45 -0
- package/framework/js/utilities/scroll-tracker.js +68 -0
- package/framework/js/utilities/ui-initializer.js +146 -0
- package/framework/js/utilities/utilities.js +293 -0
- package/framework/js/utilities/view-manager.js +227 -0
- package/framework/js/validation/html-validators.js +422 -0
- package/framework/js/validation/scorm-validators.js +438 -0
- package/framework/js/vendor/pipwerks.js +931 -0
- package/framework/scripts/generate-narration.js +629 -0
- package/framework/scripts/tts-providers/azure-provider.js +178 -0
- package/framework/scripts/tts-providers/base-provider.js +81 -0
- package/framework/scripts/tts-providers/deepgram-provider.js +135 -0
- package/framework/scripts/tts-providers/elevenlabs-provider.js +148 -0
- package/framework/scripts/tts-providers/google-provider.js +272 -0
- package/framework/scripts/tts-providers/index.js +158 -0
- package/framework/scripts/tts-providers/openai-provider.js +143 -0
- package/framework/version.json +63 -0
- package/lib/authoring-api.js +919 -0
- package/lib/build-linter.js +450 -0
- package/lib/build-packaging.js +186 -0
- package/lib/build.js +88 -0
- package/lib/cloud.js +691 -0
- package/lib/convert.js +341 -0
- package/lib/course-parser.js +936 -0
- package/lib/course-writer.js +258 -0
- package/lib/create.js +248 -0
- package/lib/css-index.js +237 -0
- package/lib/dev.js +51 -0
- package/lib/export-content.js +1246 -0
- package/lib/headless-browser.js +413 -0
- package/lib/import.js +377 -0
- package/lib/index.js +80 -0
- package/lib/info.js +79 -0
- package/lib/interaction-formatters.js +568 -0
- package/lib/manifest/cmi5-manifest.js +63 -0
- package/lib/manifest/lti-tool-config.js +53 -0
- package/lib/manifest/manifest-factory.js +99 -0
- package/lib/manifest/scorm-12-manifest.js +61 -0
- package/lib/manifest/scorm-2004-manifest.js +94 -0
- package/lib/manifest/scorm-proxy-manifest.js +104 -0
- package/lib/manifest-parser.js +96 -0
- package/lib/mcp-prompts.js +753 -0
- package/lib/mcp-server.js +316 -0
- package/lib/narration.js +53 -0
- package/lib/pdf-structure.js +142 -0
- package/lib/preview-export.js +231 -0
- package/lib/preview-routes-api.js +662 -0
- package/lib/preview-routes-editing.js +159 -0
- package/lib/preview-routes-lms.js +230 -0
- package/lib/preview-server.js +564 -0
- package/lib/project-utils.js +269 -0
- package/lib/proxy-templates/proxy.html +68 -0
- package/lib/proxy-templates/scorm-bridge.js +112 -0
- package/lib/scaffold.js +193 -0
- package/lib/schema-extractor.js +361 -0
- package/lib/slide-source-editor.js +586 -0
- package/lib/stub-player/app-viewer.js +195 -0
- package/lib/stub-player/app.js +370 -0
- package/lib/stub-player/catalog-panel.js +312 -0
- package/lib/stub-player/config-panel.js +1303 -0
- package/lib/stub-player/content-generator.js +586 -0
- package/lib/stub-player/content-viewer.js +173 -0
- package/lib/stub-player/debug-panel.js +420 -0
- package/lib/stub-player/edit-mode.js +922 -0
- package/lib/stub-player/edit-utils.js +400 -0
- package/lib/stub-player/header-bar.js +354 -0
- package/lib/stub-player/interaction-editor.js +210 -0
- package/lib/stub-player/interactions-panel.js +565 -0
- package/lib/stub-player/lms-api.js +1094 -0
- package/lib/stub-player/login-screen.js +74 -0
- package/lib/stub-player/outline-mode.js +689 -0
- package/lib/stub-player/styles/_assessments-panel.css +245 -0
- package/lib/stub-player/styles/_base.css +89 -0
- package/lib/stub-player/styles/_catalog-icons.css +96 -0
- package/lib/stub-player/styles/_catalog-panel.css +291 -0
- package/lib/stub-player/styles/_config-panel.css +636 -0
- package/lib/stub-player/styles/_content-viewer.css +834 -0
- package/lib/stub-player/styles/_debug-panel.css +576 -0
- package/lib/stub-player/styles/_edit-mode.css +128 -0
- package/lib/stub-player/styles/_header-bar.css +343 -0
- package/lib/stub-player/styles/_interaction-editor.css +140 -0
- package/lib/stub-player/styles/_interactions-panel.css +1038 -0
- package/lib/stub-player/styles/_login-screen.css +102 -0
- package/lib/stub-player/styles/_outline-mode.css +752 -0
- package/lib/stub-player/styles.css +15 -0
- package/lib/stub-player.js +160 -0
- package/lib/test-data-reporting.js +176 -0
- package/lib/test-error-reporting.js +146 -0
- package/lib/token.js +86 -0
- package/lib/upgrade.js +257 -0
- package/lib/validation-rules.js +517 -0
- package/lib/vite-plugin-content-discovery.js +296 -0
- package/package.json +108 -0
- package/schemas/XMLSchema.dtd +402 -0
- package/schemas/adlcp_v1p3.xsd +111 -0
- package/schemas/adlnav_v1p3.xsd +61 -0
- package/schemas/adlseq_v1p3.xsd +93 -0
- package/schemas/common/anyElement.xsd +27 -0
- package/schemas/common/dataTypes.xsd +138 -0
- package/schemas/common/elementNames.xsd +767 -0
- package/schemas/common/elementTypes.xsd +786 -0
- package/schemas/common/rootElement.xsd +31 -0
- package/schemas/common/vocabTypes.xsd +345 -0
- package/schemas/common/vocabValues.xsd +257 -0
- package/schemas/datatypes.dtd +203 -0
- package/schemas/ims_xml.xsd +35 -0
- package/schemas/imscp_v1p1.xsd +368 -0
- package/schemas/imsss_v1p0.xsd +67 -0
- package/schemas/imsss_v1p0auxresource.xsd +19 -0
- package/schemas/imsss_v1p0control.xsd +20 -0
- package/schemas/imsss_v1p0delivery.xsd +17 -0
- package/schemas/imsss_v1p0limit.xsd +47 -0
- package/schemas/imsss_v1p0objective.xsd +67 -0
- package/schemas/imsss_v1p0random.xsd +16 -0
- package/schemas/imsss_v1p0rollup.xsd +46 -0
- package/schemas/imsss_v1p0seqrule.xsd +108 -0
- package/schemas/imsss_v1p0util.xsd +94 -0
- package/schemas/license.txt +17 -0
- package/schemas/lom.xsd +102 -0
- package/schemas/lomCustom.xsd +62 -0
- package/schemas/lomLoose.xsd +62 -0
- package/schemas/lomStrict.xsd +62 -0
- package/schemas/xml.xsd +81 -0
- package/template/.env.example +92 -0
- package/template/course/assets/audio/example-intro.mp3 +0 -0
- package/template/course/assets/audio/example-ui-demo--compact-player.mp3 +0 -0
- package/template/course/assets/audio/example-ui-demo--demo-modal.mp3 +0 -0
- package/template/course/assets/audio/example-ui-demo--full-player.mp3 +0 -0
- package/template/course/assets/docs/example_md_1.md +39 -0
- package/template/course/assets/docs/example_md_2.md +41 -0
- package/template/course/assets/docs/example_pdf_1_thumbnail.png +0 -0
- package/template/course/assets/docs/example_pdf_2.pdf +0 -0
- package/template/course/assets/images/course-architecture.svg +36 -0
- package/template/course/assets/images/logo.svg +14 -0
- package/template/course/assets/widgets/counter-demo.html +190 -0
- package/template/course/assets/widgets/gravity-painter.html +384 -0
- package/template/course/course-config.js +539 -0
- package/template/course/icons.js +19 -0
- package/template/course/interactions/PLUGIN_GUIDE.md +97 -0
- package/template/course/slides/example-course-structure.js +138 -0
- package/template/course/slides/example-final-exam.js +144 -0
- package/template/course/slides/example-finishing.js +127 -0
- package/template/course/slides/example-interactions-showcase.js +615 -0
- package/template/course/slides/example-preview-tour.js +129 -0
- package/template/course/slides/example-remedial.js +143 -0
- package/template/course/slides/example-summary.js +103 -0
- package/template/course/slides/example-ui-showcase.js +1805 -0
- package/template/course/slides/example-welcome.js +123 -0
- package/template/course/slides/example-workflow.js +140 -0
- package/template/course/theme.css +165 -0
- package/template/eslint.config.js +47 -0
- package/template/package.json +28 -0
- package/template/vite.config.js +339 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema Extractor - Build-time AST extraction using acorn
|
|
3
|
+
*
|
|
4
|
+
* Extracts interaction schemas WITHOUT importing files (avoiding dependency chain).
|
|
5
|
+
* Used by course-parser.js and other build tools.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as acorn from 'acorn';
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
|
|
13
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const INTERACTIONS_DIR = path.join(__dirname, '../framework/js/components/interactions');
|
|
15
|
+
const UI_COMPONENTS_DIR = path.join(__dirname, '../framework/js/components/ui-components');
|
|
16
|
+
|
|
17
|
+
// Base schema properties shared by all interactions
|
|
18
|
+
const baseSchema = {
|
|
19
|
+
id: { type: 'string', required: true, description: 'Unique identifier' },
|
|
20
|
+
prompt: { type: 'string', required: true, description: 'Question or instruction text' },
|
|
21
|
+
controlled: { type: 'boolean', default: false, description: 'Managed externally' },
|
|
22
|
+
feedback: { type: 'object', description: 'Custom feedback messages' }
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Cache after first load
|
|
26
|
+
let schemasCache = null;
|
|
27
|
+
let metadataCache = null;
|
|
28
|
+
let componentSchemasCache = null;
|
|
29
|
+
let componentMetadataCache = null;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get all interaction schemas (auto-discovered)
|
|
33
|
+
*/
|
|
34
|
+
export function getAllSchemas() {
|
|
35
|
+
if (schemasCache) return schemasCache;
|
|
36
|
+
loadAll();
|
|
37
|
+
return schemasCache;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get all interaction metadata (auto-discovered)
|
|
42
|
+
*/
|
|
43
|
+
export function getAllMetadata() {
|
|
44
|
+
if (metadataCache) return metadataCache;
|
|
45
|
+
loadAll();
|
|
46
|
+
return metadataCache;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Load schemas and metadata from all interaction files
|
|
51
|
+
*/
|
|
52
|
+
function loadAll() {
|
|
53
|
+
schemasCache = {};
|
|
54
|
+
metadataCache = {};
|
|
55
|
+
|
|
56
|
+
// Scan built-in interactions
|
|
57
|
+
scanDirectory(INTERACTIONS_DIR);
|
|
58
|
+
|
|
59
|
+
// Scan course-specific custom interactions
|
|
60
|
+
const customDir = path.join(__dirname, '../course/interactions');
|
|
61
|
+
scanDirectory(customDir);
|
|
62
|
+
|
|
63
|
+
// Alias
|
|
64
|
+
if (schemasCache['multiple-choice']) {
|
|
65
|
+
schemasCache['multiple-choice-single'] = schemasCache['multiple-choice'];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Scan a directory for interaction files
|
|
71
|
+
*/
|
|
72
|
+
function scanDirectory(dir) {
|
|
73
|
+
if (!fs.existsSync(dir)) return;
|
|
74
|
+
|
|
75
|
+
for (const file of fs.readdirSync(dir)) {
|
|
76
|
+
if (!file.endsWith('.js') || file === 'interaction-base.js') continue;
|
|
77
|
+
|
|
78
|
+
const filePath = path.join(dir, file);
|
|
79
|
+
const exports = extractExports(filePath);
|
|
80
|
+
|
|
81
|
+
// Find standardized 'schema' and 'metadata' exports
|
|
82
|
+
const { schema, metadata } = exports;
|
|
83
|
+
|
|
84
|
+
if (schema?.type) {
|
|
85
|
+
schemasCache[schema.type] = schema;
|
|
86
|
+
}
|
|
87
|
+
if (metadata?.creator) {
|
|
88
|
+
metadataCache[metadata.creator] = metadata;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get registered interaction types
|
|
95
|
+
*/
|
|
96
|
+
export function getRegisteredTypes() {
|
|
97
|
+
return Object.keys(getAllSchemas());
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get schema for a type
|
|
102
|
+
*/
|
|
103
|
+
export function getSchema(type) {
|
|
104
|
+
return getAllSchemas()[type] || null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Get full schema with base properties merged
|
|
109
|
+
*/
|
|
110
|
+
export function getFullSchema(type) {
|
|
111
|
+
const schema = getSchema(type);
|
|
112
|
+
if (!schema) return null;
|
|
113
|
+
return { ...schema, properties: { ...baseSchema, ...schema.properties } };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get factory name for a type (from metadata)
|
|
118
|
+
*/
|
|
119
|
+
export function getFactoryName(type) {
|
|
120
|
+
const schema = getSchema(type);
|
|
121
|
+
if (!schema) return null;
|
|
122
|
+
|
|
123
|
+
// Derive factory name from type: 'multiple-choice' -> 'createMultipleChoiceQuestion'
|
|
124
|
+
const camelCase = type.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
125
|
+
return `create${camelCase.charAt(0).toUpperCase() + camelCase.slice(1)}Question`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// =============================================================================
|
|
129
|
+
// COMPONENT SCHEMAS (UI Components)
|
|
130
|
+
// =============================================================================
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Get all component schemas (auto-discovered)
|
|
134
|
+
*/
|
|
135
|
+
export function getAllComponentSchemas() {
|
|
136
|
+
if (componentSchemasCache) return componentSchemasCache;
|
|
137
|
+
loadAllComponents();
|
|
138
|
+
return componentSchemasCache;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Load schemas from all component files
|
|
143
|
+
*/
|
|
144
|
+
function loadAllComponents() {
|
|
145
|
+
componentSchemasCache = {};
|
|
146
|
+
componentMetadataCache = {};
|
|
147
|
+
|
|
148
|
+
// Scan built-in UI components
|
|
149
|
+
scanComponentDirectory(UI_COMPONENTS_DIR);
|
|
150
|
+
|
|
151
|
+
// Scan course-specific custom components
|
|
152
|
+
const customDir = path.join(__dirname, '../course/components');
|
|
153
|
+
scanComponentDirectory(customDir);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Scan a directory for component files
|
|
158
|
+
*/
|
|
159
|
+
function scanComponentDirectory(dir) {
|
|
160
|
+
if (!fs.existsSync(dir)) return;
|
|
161
|
+
|
|
162
|
+
for (const file of fs.readdirSync(dir)) {
|
|
163
|
+
if (!file.endsWith('.js') || file === 'index.js' || file.includes('-base')) continue;
|
|
164
|
+
|
|
165
|
+
const filePath = path.join(dir, file);
|
|
166
|
+
const exports = extractExports(filePath);
|
|
167
|
+
|
|
168
|
+
const { schema, metadata } = exports;
|
|
169
|
+
|
|
170
|
+
if (schema?.type) {
|
|
171
|
+
componentSchemasCache[schema.type] = schema;
|
|
172
|
+
if (metadata) componentMetadataCache[schema.type] = metadata;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Get component schema for a type
|
|
179
|
+
*/
|
|
180
|
+
export function getComponentSchema(type) {
|
|
181
|
+
return getAllComponentSchemas()[type] || null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Get registered component types
|
|
186
|
+
*/
|
|
187
|
+
export function getRegisteredComponentTypes() {
|
|
188
|
+
return Object.keys(getAllComponentSchemas());
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Get all component metadata (auto-discovered)
|
|
193
|
+
*/
|
|
194
|
+
export function getAllComponentMetadata() {
|
|
195
|
+
if (!componentMetadataCache) loadAllComponents();
|
|
196
|
+
return componentMetadataCache;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Get metadata for a component type
|
|
201
|
+
*/
|
|
202
|
+
export function getComponentMetadata(type) {
|
|
203
|
+
return getAllComponentMetadata()[type] || null;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Build reverse map: engagementTracking value -> component type
|
|
208
|
+
* e.g. { viewAllTabs: 'tabs', viewAllPanels: 'accordion', ... }
|
|
209
|
+
*/
|
|
210
|
+
export function getEngagementTrackingMap() {
|
|
211
|
+
const metadata = getAllComponentMetadata();
|
|
212
|
+
const map = {};
|
|
213
|
+
for (const [type, meta] of Object.entries(metadata)) {
|
|
214
|
+
if (meta.engagementTracking) {
|
|
215
|
+
map[meta.engagementTracking] = type;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return map;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// =============================================================================
|
|
222
|
+
// AST EXTRACTION (acorn)
|
|
223
|
+
// =============================================================================
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Extract all named exports from a file
|
|
227
|
+
*/
|
|
228
|
+
function extractExports(filePath) {
|
|
229
|
+
const source = fs.readFileSync(filePath, 'utf-8');
|
|
230
|
+
const exports = {};
|
|
231
|
+
|
|
232
|
+
try {
|
|
233
|
+
const ast = acorn.parse(source, { ecmaVersion: 'latest', sourceType: 'module' });
|
|
234
|
+
|
|
235
|
+
for (const node of ast.body) {
|
|
236
|
+
if (node.type === 'ExportNamedDeclaration' && node.declaration?.type === 'VariableDeclaration') {
|
|
237
|
+
for (const decl of node.declaration.declarations) {
|
|
238
|
+
exports[decl.id.name] = toValue(decl.init);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
} catch (_e) {
|
|
243
|
+
// Silently skip parse errors
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return exports;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Convert AST node to JavaScript value
|
|
251
|
+
*/
|
|
252
|
+
function toValue(node) {
|
|
253
|
+
if (!node) return null;
|
|
254
|
+
|
|
255
|
+
switch (node.type) {
|
|
256
|
+
case 'Literal':
|
|
257
|
+
return node.value;
|
|
258
|
+
case 'Identifier':
|
|
259
|
+
if (node.name === 'true') return true;
|
|
260
|
+
if (node.name === 'false') return false;
|
|
261
|
+
return null;
|
|
262
|
+
case 'ObjectExpression': {
|
|
263
|
+
const obj = {};
|
|
264
|
+
for (const p of node.properties) {
|
|
265
|
+
if (p.type !== 'Property') continue;
|
|
266
|
+
const key = p.key.name || p.key.value;
|
|
267
|
+
obj[key] = toValue(p.value);
|
|
268
|
+
}
|
|
269
|
+
return obj;
|
|
270
|
+
}
|
|
271
|
+
case 'ArrayExpression':
|
|
272
|
+
return node.elements.map(toValue);
|
|
273
|
+
case 'TemplateLiteral':
|
|
274
|
+
return node.quasis.map(q => q.value.cooked).join('');
|
|
275
|
+
default:
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// =============================================================================
|
|
281
|
+
// ICON EXTRACTION (regex-based — icons are simple key→SVG maps)
|
|
282
|
+
// =============================================================================
|
|
283
|
+
|
|
284
|
+
let iconsCache = null;
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Get all icons from DEFAULT_ICONS and course/icons.js.
|
|
288
|
+
* Returns { name: { category, svg, source } }
|
|
289
|
+
*/
|
|
290
|
+
export function getAllIcons() {
|
|
291
|
+
if (iconsCache) return iconsCache;
|
|
292
|
+
iconsCache = {};
|
|
293
|
+
|
|
294
|
+
// Built-in icons
|
|
295
|
+
const iconsFile = path.join(__dirname, '../framework/js/utilities/icons.js');
|
|
296
|
+
if (fs.existsSync(iconsFile)) {
|
|
297
|
+
const builtIn = extractIconsFromSource(fs.readFileSync(iconsFile, 'utf-8'));
|
|
298
|
+
for (const [name, info] of Object.entries(builtIn)) {
|
|
299
|
+
iconsCache[name] = { ...info, source: 'built-in' };
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Custom course icons
|
|
304
|
+
const customFile = path.join(__dirname, '../course/icons.js');
|
|
305
|
+
if (fs.existsSync(customFile)) {
|
|
306
|
+
const custom = extractIconsFromSource(fs.readFileSync(customFile, 'utf-8'));
|
|
307
|
+
for (const [name, info] of Object.entries(custom)) {
|
|
308
|
+
iconsCache[name] = { ...info, source: 'custom' };
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return iconsCache;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Extract icon keys, SVG content, and categories from source code.
|
|
317
|
+
* Parses comment lines (e.g. `// System UI`) as category headers.
|
|
318
|
+
* Returns { name: { category, svg } }
|
|
319
|
+
*/
|
|
320
|
+
function extractIconsFromSource(source) {
|
|
321
|
+
const icons = {};
|
|
322
|
+
let currentCategory = 'Uncategorized';
|
|
323
|
+
|
|
324
|
+
// Match lines inside object literals: category comments and key-value pairs
|
|
325
|
+
const lines = source.split('\n');
|
|
326
|
+
let inObject = false;
|
|
327
|
+
|
|
328
|
+
for (const line of lines) {
|
|
329
|
+
const trimmed = line.trim();
|
|
330
|
+
|
|
331
|
+
// Detect start of an object (const DEFAULT_ICONS = { or export const customIcons = {)
|
|
332
|
+
if (/(?:const|let|var)\s+\w+\s*=\s*\{/.test(trimmed)) {
|
|
333
|
+
inObject = true;
|
|
334
|
+
currentCategory = 'Uncategorized';
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (!inObject) continue;
|
|
339
|
+
|
|
340
|
+
// Detect end of object
|
|
341
|
+
if (trimmed === '};') {
|
|
342
|
+
inObject = false;
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Category comment: `// System UI` or `// Media`
|
|
347
|
+
const categoryMatch = trimmed.match(/^\/\/\s*(.+)/);
|
|
348
|
+
if (categoryMatch) {
|
|
349
|
+
currentCategory = categoryMatch[1].trim();
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Icon entry: `'icon-name': '<path .../>',`
|
|
354
|
+
const iconMatch = trimmed.match(/^'([^']+)'\s*:\s*'(.+?)'\s*,?\s*$/);
|
|
355
|
+
if (iconMatch) {
|
|
356
|
+
icons[iconMatch[1]] = { category: currentCategory, svg: iconMatch[2] };
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return icons;
|
|
361
|
+
}
|