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,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vite-plugin-content-discovery.js - Build-time content manifest generation
|
|
3
|
+
*
|
|
4
|
+
* - Injects data-edit-path attributes into slide templates during build
|
|
5
|
+
* - Generates content manifest at build end
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { parseCourse, parseElements } from './course-parser.js';
|
|
11
|
+
|
|
12
|
+
// Manifest file path (set in configResolved)
|
|
13
|
+
let manifestFilePath = null;
|
|
14
|
+
let galleryManifestFilePath = null;
|
|
15
|
+
let coursePath = null;
|
|
16
|
+
|
|
17
|
+
let isDevMode = false;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Create the Vite plugin
|
|
21
|
+
* @param {object} options - Plugin options
|
|
22
|
+
* @returns {object} Vite plugin
|
|
23
|
+
*/
|
|
24
|
+
export default function contentDiscoveryPlugin(options = {}) {
|
|
25
|
+
return {
|
|
26
|
+
name: 'content-discovery',
|
|
27
|
+
|
|
28
|
+
configResolved(config) {
|
|
29
|
+
const root = config.root || process.cwd();
|
|
30
|
+
const outDir = config.build?.outDir || 'dist';
|
|
31
|
+
|
|
32
|
+
// Set paths
|
|
33
|
+
coursePath = options.coursePath || path.join(root, 'course');
|
|
34
|
+
|
|
35
|
+
manifestFilePath = path.join(root, outDir, '_content-manifest.json');
|
|
36
|
+
galleryManifestFilePath = path.join(root, outDir, '_gallery-manifest.json');
|
|
37
|
+
isDevMode = config.command === 'serve';
|
|
38
|
+
|
|
39
|
+
if (isDevMode) {
|
|
40
|
+
console.log('[content-discovery] Dev mode - manifest generation disabled');
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
// Transform slide files to inject data-edit-path attributes
|
|
45
|
+
transform(code, id) {
|
|
46
|
+
// Only process slide files
|
|
47
|
+
if (!id.includes('/slides/') || !id.endsWith('.js')) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Find template literals with .innerHTML = `...`
|
|
52
|
+
const pattern = /\.innerHTML\s*=\s*`/g;
|
|
53
|
+
let result = code;
|
|
54
|
+
let offset = 0;
|
|
55
|
+
|
|
56
|
+
let match;
|
|
57
|
+
while ((match = pattern.exec(code)) !== null) {
|
|
58
|
+
const startPos = match.index + match[0].length - 1;
|
|
59
|
+
const templateEnd = findTemplateEnd(code, startPos);
|
|
60
|
+
if (templateEnd === -1) continue;
|
|
61
|
+
|
|
62
|
+
const templateContent = code.slice(startPos + 1, templateEnd);
|
|
63
|
+
const injectedContent = injectEditPaths(templateContent);
|
|
64
|
+
|
|
65
|
+
if (injectedContent !== templateContent) {
|
|
66
|
+
const before = result.slice(0, startPos + 1 + offset);
|
|
67
|
+
const after = result.slice(templateEnd + offset);
|
|
68
|
+
result = before + injectedContent + after;
|
|
69
|
+
offset += injectedContent.length - templateContent.length;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (result !== code) {
|
|
74
|
+
return { code: result, map: null };
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
async closeBundle() {
|
|
80
|
+
if (isDevMode) return;
|
|
81
|
+
|
|
82
|
+
if (!fs.existsSync(coursePath)) {
|
|
83
|
+
console.warn(`[content-discovery] Course directory not found: ${coursePath}`);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.log('[content-discovery] Generating content manifest...');
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const manifest = await parseCourse(coursePath, options);
|
|
91
|
+
fs.writeFileSync(manifestFilePath, JSON.stringify(manifest, null, 2));
|
|
92
|
+
|
|
93
|
+
const slideCount = Object.keys(manifest.slides).length;
|
|
94
|
+
const assessmentCount = manifest.assessments.length;
|
|
95
|
+
let interactionCount = 0;
|
|
96
|
+
for (const slideData of Object.values(manifest.slides)) {
|
|
97
|
+
interactionCount += slideData.interactions?.length || 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
console.log('[content-discovery] Generated manifest:');
|
|
101
|
+
console.log(` - ${slideCount} slides`);
|
|
102
|
+
console.log(` - ${assessmentCount} assessments`);
|
|
103
|
+
console.log(` - ${interactionCount} interactions`);
|
|
104
|
+
console.log(` → ${manifestFilePath}`);
|
|
105
|
+
|
|
106
|
+
} catch (err) {
|
|
107
|
+
console.error('[content-discovery] Error generating manifest:', err.message);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Generate gallery manifest
|
|
111
|
+
generateGalleryManifest(options.galleryConfig);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Generate the document gallery manifest by scanning the docs directory.
|
|
118
|
+
* @param {object} galleryConfig - Gallery configuration from course-config.js
|
|
119
|
+
*/
|
|
120
|
+
function generateGalleryManifest(galleryConfig) {
|
|
121
|
+
if (!galleryConfig?.enabled) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const directory = galleryConfig.directory || 'assets/docs';
|
|
126
|
+
const docsDir = path.join(coursePath, directory);
|
|
127
|
+
const allowedTypes = new Set(galleryConfig.fileTypes || ['pdf', 'md', 'jpg', 'png']);
|
|
128
|
+
|
|
129
|
+
if (!fs.existsSync(docsDir)) {
|
|
130
|
+
console.log(`[content-discovery] Gallery directory not found: ${docsDir}`);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const items = [];
|
|
135
|
+
const files = fs.readdirSync(docsDir);
|
|
136
|
+
|
|
137
|
+
// Build a set of thumbnail files for quick lookup
|
|
138
|
+
const thumbnailFiles = new Set(
|
|
139
|
+
files.filter(f => f.match(/_thumbnail\.(png|jpg|jpeg|webp)$/i))
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
for (const file of files) {
|
|
143
|
+
// Skip hidden files, thumbnails, and non-matching types
|
|
144
|
+
if (file.startsWith('.')) continue;
|
|
145
|
+
if (file.match(/_thumbnail\.(png|jpg|jpeg|webp)$/i)) continue;
|
|
146
|
+
|
|
147
|
+
const ext = path.extname(file).slice(1).toLowerCase();
|
|
148
|
+
if (!allowedTypes.has(ext)) continue;
|
|
149
|
+
|
|
150
|
+
// Determine document type
|
|
151
|
+
let type;
|
|
152
|
+
if (['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'].includes(ext)) {
|
|
153
|
+
type = 'image';
|
|
154
|
+
} else if (ext === 'pdf') {
|
|
155
|
+
type = 'pdf';
|
|
156
|
+
} else if (ext === 'md') {
|
|
157
|
+
type = 'markdown';
|
|
158
|
+
} else {
|
|
159
|
+
type = 'file';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Build path relative to the dist/course root
|
|
163
|
+
const src = `course/${directory}/${file}`;
|
|
164
|
+
|
|
165
|
+
// Check for companion thumbnail
|
|
166
|
+
const baseName = path.basename(file, path.extname(file));
|
|
167
|
+
let thumbnail = null;
|
|
168
|
+
for (const thumbExt of ['png', 'jpg', 'jpeg', 'webp']) {
|
|
169
|
+
const thumbFile = `${baseName}_thumbnail.${thumbExt}`;
|
|
170
|
+
if (thumbnailFiles.has(thumbFile)) {
|
|
171
|
+
thumbnail = `course/${directory}/${thumbFile}`;
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Generate label from filename
|
|
177
|
+
const label = baseName
|
|
178
|
+
.replace(/[_-]/g, ' ')
|
|
179
|
+
.replace(/\b\w/g, c => c.toUpperCase());
|
|
180
|
+
|
|
181
|
+
items.push({ src, type, label, ...(thumbnail ? { thumbnail } : {}) });
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Sort alphabetically by label
|
|
185
|
+
items.sort((a, b) => a.label.localeCompare(b.label));
|
|
186
|
+
|
|
187
|
+
const manifest = { items };
|
|
188
|
+
fs.writeFileSync(galleryManifestFilePath, JSON.stringify(manifest, null, 2));
|
|
189
|
+
|
|
190
|
+
console.log(`[content-discovery] Generated gallery manifest: ${items.length} documents`);
|
|
191
|
+
console.log(` → ${galleryManifestFilePath}`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Find the end of a template literal (handles nested ${...})
|
|
196
|
+
*/
|
|
197
|
+
function findTemplateEnd(source, startPos) {
|
|
198
|
+
if (source[startPos] !== '`') return -1;
|
|
199
|
+
|
|
200
|
+
let i = startPos + 1;
|
|
201
|
+
let depth = 0;
|
|
202
|
+
|
|
203
|
+
while (i < source.length) {
|
|
204
|
+
const char = source[i];
|
|
205
|
+
const prevChar = i > 0 ? source[i - 1] : '';
|
|
206
|
+
|
|
207
|
+
if (prevChar === '\\') { i++; continue; }
|
|
208
|
+
if (char === '$' && source[i + 1] === '{') { depth++; i += 2; continue; }
|
|
209
|
+
if (depth > 0) {
|
|
210
|
+
if (char === '{') depth++;
|
|
211
|
+
if (char === '}') depth--;
|
|
212
|
+
i++;
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
if (char === '`') {
|
|
216
|
+
return i;
|
|
217
|
+
}
|
|
218
|
+
i++;
|
|
219
|
+
}
|
|
220
|
+
return -1;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Inject data-edit-path attributes into HTML content
|
|
225
|
+
*/
|
|
226
|
+
// Inline elements inside text-bearing blocks (headings, paragraphs, list items)
|
|
227
|
+
// are formatting — part of the parent's text flow, not standalone edit targets.
|
|
228
|
+
// But inline elements inside layout containers (div, section) ARE standalone content.
|
|
229
|
+
const INLINE_TAGS = new Set([
|
|
230
|
+
'span', 'strong', 'em', 'b', 'i', 'u', 'a', 'code', 'small',
|
|
231
|
+
'sub', 'sup', 'mark', 'abbr', 'cite', 'q', 'time', 'var', 'kbd', 'samp'
|
|
232
|
+
]);
|
|
233
|
+
const TEXT_BLOCK_TAGS = new Set([
|
|
234
|
+
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'li',
|
|
235
|
+
'td', 'th', 'dt', 'dd', 'blockquote', 'figcaption', 'summary', 'caption'
|
|
236
|
+
]);
|
|
237
|
+
|
|
238
|
+
function injectEditPaths(html) {
|
|
239
|
+
const elements = parseElements(html);
|
|
240
|
+
const elementsByPath = new Map(elements.map(el => [el.path, el]));
|
|
241
|
+
|
|
242
|
+
// Sort by offset descending so we can inject without shifting positions
|
|
243
|
+
const sortedElements = [...elements]
|
|
244
|
+
.filter(el => el.startOffset !== null && el.innerEnd !== null)
|
|
245
|
+
.filter(el => {
|
|
246
|
+
// Skip inline elements that are formatting inside text-bearing blocks
|
|
247
|
+
if (INLINE_TAGS.has(el.tag) && el.parentPath) {
|
|
248
|
+
const parent = elementsByPath.get(el.parentPath);
|
|
249
|
+
if (parent && TEXT_BLOCK_TAGS.has(parent.tag)) return false;
|
|
250
|
+
}
|
|
251
|
+
return true;
|
|
252
|
+
})
|
|
253
|
+
.filter(el => el.innerText) // Skip elements with no editable text content
|
|
254
|
+
.filter(el => {
|
|
255
|
+
// Skip elements with non-icon ${...} expressions — those are dynamic
|
|
256
|
+
// and can't be meaningfully edited. iconManager.getIcon() is allowed
|
|
257
|
+
// since the server reconciles rendered SVGs with source expressions.
|
|
258
|
+
if (el.innerEnd === null) return true;
|
|
259
|
+
const raw = html.slice(el.innerStart, el.innerEnd);
|
|
260
|
+
// Extract ${...} expressions using brace balancing (handles nested braces)
|
|
261
|
+
const expressions = [];
|
|
262
|
+
for (let i = 0; i < raw.length; i++) {
|
|
263
|
+
if (raw[i] === '$' && raw[i + 1] === '{') {
|
|
264
|
+
let depth = 1;
|
|
265
|
+
let j = i + 2;
|
|
266
|
+
while (j < raw.length && depth > 0) {
|
|
267
|
+
if (raw[j] === '{') depth++;
|
|
268
|
+
else if (raw[j] === '}') depth--;
|
|
269
|
+
j++;
|
|
270
|
+
}
|
|
271
|
+
expressions.push(raw.slice(i, j));
|
|
272
|
+
i = j - 1;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (expressions.length === 0) return true;
|
|
276
|
+
return expressions.every(expr => /\bgetIcon\b/.test(expr));
|
|
277
|
+
})
|
|
278
|
+
.sort((a, b) => b.startOffset - a.startOffset);
|
|
279
|
+
|
|
280
|
+
let result = html;
|
|
281
|
+
for (const el of sortedElements) {
|
|
282
|
+
// Find the first > after startOffset (end of opening tag)
|
|
283
|
+
const tagEnd = result.indexOf('>', el.startOffset);
|
|
284
|
+
if (tagEnd === -1) continue;
|
|
285
|
+
|
|
286
|
+
// Check if already has data-edit-path
|
|
287
|
+
const openingTag = result.slice(el.startOffset, tagEnd);
|
|
288
|
+
if (openingTag.includes('data-edit-path')) continue;
|
|
289
|
+
|
|
290
|
+
// Inject the attribute before the >
|
|
291
|
+
const attr = ` data-edit-path="${el.path}"`;
|
|
292
|
+
result = result.slice(0, tagEnd) + attr + result.slice(tagEnd);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return result;
|
|
296
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "coursecode",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Multi-format course authoring framework with CLI tools (SCORM 2004, SCORM 1.2, cmi5, LTI 1.3)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"coursecode": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"lib",
|
|
12
|
+
"framework",
|
|
13
|
+
"!framework/dist",
|
|
14
|
+
"!framework/dist/**",
|
|
15
|
+
"schemas",
|
|
16
|
+
"template",
|
|
17
|
+
"!template/.gitattributes",
|
|
18
|
+
"!template/**/.gitkeep",
|
|
19
|
+
"README.md",
|
|
20
|
+
"THIRD_PARTY_NOTICES.md"
|
|
21
|
+
],
|
|
22
|
+
"exports": {
|
|
23
|
+
".": "./lib/index.js",
|
|
24
|
+
"./manifest": "./lib/manifest/manifest-factory.js",
|
|
25
|
+
"./build-packaging": "./lib/build-packaging.js",
|
|
26
|
+
"./stub-player": "./lib/stub-player.js",
|
|
27
|
+
"./course-parser": "./lib/course-parser.js",
|
|
28
|
+
"./vite-plugin-content-discovery": "./lib/vite-plugin-content-discovery.js"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"dev": "VITE_COURSECODE_LOCAL=true npx vite build --config vite.framework-dev.config.js --mode development --watch",
|
|
32
|
+
"preview": "node lib/preview-server.js --framework-dev",
|
|
33
|
+
"preview:scorm12": "LMS_FORMAT=scorm1.2 node lib/preview-server.js --framework-dev",
|
|
34
|
+
"preview:cmi5": "LMS_FORMAT=cmi5 node lib/preview-server.js --framework-dev",
|
|
35
|
+
"preview:lti": "LMS_FORMAT=lti node lib/preview-server.js --framework-dev",
|
|
36
|
+
"lint": "eslint .",
|
|
37
|
+
"lint:fix": "eslint . --fix",
|
|
38
|
+
"build": "npx vite build --config vite.framework-dev.config.js",
|
|
39
|
+
"build:dev": "npx vite build --config vite.framework-dev.config.js",
|
|
40
|
+
"build:scorm2004": "LMS_FORMAT=scorm2004 npx vite build --config vite.framework-dev.config.js",
|
|
41
|
+
"build:scorm12": "LMS_FORMAT=scorm1.2 npx vite build --config vite.framework-dev.config.js",
|
|
42
|
+
"build:cmi5": "LMS_FORMAT=cmi5 npx vite build --config vite.framework-dev.config.js",
|
|
43
|
+
"build:lti": "LMS_FORMAT=lti npx vite build --config vite.framework-dev.config.js",
|
|
44
|
+
"package": "npm run lint && PACKAGE=true npx vite build --config vite.framework-dev.config.js",
|
|
45
|
+
"package:scorm2004": "npm run lint && LMS_FORMAT=scorm2004 PACKAGE=true npx vite build --config vite.framework-dev.config.js",
|
|
46
|
+
"package:scorm12": "npm run lint && LMS_FORMAT=scorm1.2 PACKAGE=true npx vite build --config vite.framework-dev.config.js",
|
|
47
|
+
"package:cmi5": "npm run lint && LMS_FORMAT=cmi5 PACKAGE=true npx vite build --config vite.framework-dev.config.js",
|
|
48
|
+
"package:lti": "npm run lint && LMS_FORMAT=lti PACKAGE=true npx vite build --config vite.framework-dev.config.js",
|
|
49
|
+
"test": "vitest run --config tests/vitest.config.js",
|
|
50
|
+
"test:cloud": "vitest run --config tests/vitest.cloud.config.js",
|
|
51
|
+
"test:watch": "vitest --config tests/vitest.config.js",
|
|
52
|
+
"prepublishOnly": "echo '📦 Preparing coursecode for npm...'"
|
|
53
|
+
},
|
|
54
|
+
"repository": {
|
|
55
|
+
"type": "git",
|
|
56
|
+
"url": "git+https://github.com/course-code-framework/coursecode.git"
|
|
57
|
+
},
|
|
58
|
+
"keywords": [
|
|
59
|
+
"coursecode",
|
|
60
|
+
"scorm",
|
|
61
|
+
"scorm-2004",
|
|
62
|
+
"cmi5",
|
|
63
|
+
"lti",
|
|
64
|
+
"xapi",
|
|
65
|
+
"elearning",
|
|
66
|
+
"lms",
|
|
67
|
+
"learning-management",
|
|
68
|
+
"course",
|
|
69
|
+
"training",
|
|
70
|
+
"vite",
|
|
71
|
+
"framework"
|
|
72
|
+
],
|
|
73
|
+
"author": "Seth Vincent",
|
|
74
|
+
"license": "MIT",
|
|
75
|
+
"bugs": {
|
|
76
|
+
"url": "https://github.com/course-code-framework/coursecode/issues"
|
|
77
|
+
},
|
|
78
|
+
"homepage": "https://github.com/course-code-framework/coursecode/blob/main/framework/docs/USER_GUIDE.md",
|
|
79
|
+
"engines": {
|
|
80
|
+
"node": ">=18.0.0"
|
|
81
|
+
},
|
|
82
|
+
"dependencies": {
|
|
83
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
84
|
+
"commander": "^14.0.3",
|
|
85
|
+
"lz-string": "^1.5.0",
|
|
86
|
+
"mammoth": "^1.8.0",
|
|
87
|
+
"marked": "^17.0.2",
|
|
88
|
+
"marked-gfm-heading-id": "^4.1.3",
|
|
89
|
+
"node-pptx-parser": "^1.0.1",
|
|
90
|
+
"pdf2json": "^4.0.2",
|
|
91
|
+
"pdf2md": "^1.0.2",
|
|
92
|
+
"puppeteer-core": "^24.37.2",
|
|
93
|
+
"ws": "^8.18.0"
|
|
94
|
+
},
|
|
95
|
+
"devDependencies": {
|
|
96
|
+
"@vitejs/plugin-legacy": "^7.2.1",
|
|
97
|
+
"@xapi/cmi5": "^1.4.0",
|
|
98
|
+
"acorn": "^8.15.0",
|
|
99
|
+
"archiver": "^7.0.1",
|
|
100
|
+
"eslint": "^10.0.0",
|
|
101
|
+
"globals": "^17.3.0",
|
|
102
|
+
"jose": "^6.1.3",
|
|
103
|
+
"pptxgenjs": "^4.0.1",
|
|
104
|
+
"vite": "^7.2.2",
|
|
105
|
+
"vite-plugin-static-copy": "^3.1.4",
|
|
106
|
+
"vitest": "^4.0.18"
|
|
107
|
+
}
|
|
108
|
+
}
|