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
package/lib/convert.js ADDED
@@ -0,0 +1,341 @@
1
+ /**
2
+ * Convert Command
3
+ *
4
+ * Converts docx, pptx, pdf, and md files to the converted references directory.
5
+ */
6
+
7
+ import fs from 'fs/promises';
8
+ import path from 'path';
9
+
10
+ const SUPPORTED_EXTENSIONS = ['.docx', '.pptx', '.pdf', '.md'];
11
+
12
+ /**
13
+ * Main convert function
14
+ */
15
+ export async function convert(source, options) {
16
+ const {
17
+ output = './course/references/converted',
18
+ format = 'all',
19
+ dryRun = false,
20
+ overwrite = false,
21
+ flatten = false
22
+ } = options;
23
+
24
+ console.log('\n📄 Converting documents to markdown...\n');
25
+
26
+ // Resolve paths
27
+ const sourcePath = path.resolve(source);
28
+ const outputPath = path.resolve(output);
29
+
30
+ // Check if source exists
31
+ try {
32
+ await fs.access(sourcePath);
33
+ } catch {
34
+ console.error(`❌ Source path not found: ${sourcePath}`);
35
+ process.exit(1);
36
+ }
37
+
38
+ // Determine if source is file or directory
39
+ const sourceStat = await fs.stat(sourcePath);
40
+ const isDirectory = sourceStat.isDirectory();
41
+
42
+ // Find files to convert
43
+ let files = [];
44
+ if (isDirectory) {
45
+ files = await findFiles(sourcePath, format);
46
+ } else {
47
+ const ext = path.extname(sourcePath).toLowerCase();
48
+ if (SUPPORTED_EXTENSIONS.includes(ext)) {
49
+ if (format === 'all' || format === ext.slice(1)) {
50
+ files = [sourcePath];
51
+ }
52
+ }
53
+ }
54
+
55
+ // Filter by format if specified
56
+ if (format !== 'all') {
57
+ const targetExt = `.${format}`;
58
+ files = files.filter(f => path.extname(f).toLowerCase() === targetExt);
59
+ }
60
+
61
+ if (files.length === 0) {
62
+ console.log(` Source: ${sourcePath}\n`);
63
+ console.log(' No supported files found (.docx, .pptx, .pdf, .md)\n');
64
+ console.log(` Place source documents in ${source} and try again.\n`);
65
+ return;
66
+ }
67
+
68
+ console.log(` Source: ${sourcePath}`);
69
+ console.log(` Output: ${outputPath}\n`);
70
+
71
+ if (dryRun) {
72
+ console.log(' Would convert:');
73
+ for (const file of files) {
74
+ const _relativeSrc = path.relative(sourcePath, file);
75
+ const outputName = getOutputName(file, sourcePath, outputPath, flatten, isDirectory);
76
+ const relativeOut = path.relative(outputPath, outputName);
77
+ const exists = await fileExists(outputName);
78
+ const suffix = exists ? ' (exists, would skip)' : '';
79
+ console.log(` ${path.basename(file)} → ${relativeOut}${suffix}`);
80
+ }
81
+ const wouldConvert = await countConvertible(files, sourcePath, outputPath, flatten, isDirectory, overwrite);
82
+ console.log(`\n ${files.length} files found, ${wouldConvert} would be converted.\n`);
83
+ return;
84
+ }
85
+
86
+ // Create output directory
87
+ await fs.mkdir(outputPath, { recursive: true });
88
+
89
+ // Convert files
90
+ let converted = 0;
91
+ let skipped = 0;
92
+
93
+ for (const file of files) {
94
+ const outputFile = getOutputName(file, sourcePath, outputPath, flatten, isDirectory);
95
+ const ext = path.extname(file).toLowerCase();
96
+ const exists = await fileExists(outputFile);
97
+
98
+ if (exists && !overwrite) {
99
+ console.log(` ⊘ ${path.basename(file)} → skipped (already exists, use --overwrite)`);
100
+ skipped++;
101
+ continue;
102
+ }
103
+
104
+ try {
105
+ const result = await convertFile(file);
106
+
107
+ // Ensure output directory exists
108
+ await fs.mkdir(path.dirname(outputFile), { recursive: true });
109
+
110
+ // Write output
111
+ // Write output
112
+ await fs.writeFile(outputFile, result.markdown, 'utf-8');
113
+
114
+ // Write structured data if available (for AI analysis)
115
+ if (result.data) {
116
+ const jsonPath = outputFile.replace(/\.md$/, '.json');
117
+ await fs.writeFile(jsonPath, JSON.stringify(result.data, null, 2), 'utf-8');
118
+ }
119
+
120
+ let suffix = '';
121
+ if (ext === '.md') {
122
+ suffix = ' (copied)';
123
+ } else if (ext === '.pptx' && result.slideCount) {
124
+ suffix = ` (${result.slideCount} slides)`;
125
+ }
126
+ if (result.warnings?.length > 0) {
127
+ console.log(` ⚠ ${path.basename(file)} → ${path.basename(outputFile)}${suffix} (review recommended)`);
128
+ } else {
129
+ console.log(` ✓ ${path.basename(file)} → ${path.basename(outputFile)}${suffix}`);
130
+ }
131
+ converted++;
132
+ } catch (error) {
133
+ console.log(` ✗ ${path.basename(file)} → failed: ${error.message}`);
134
+ }
135
+ }
136
+
137
+ console.log(`\nDone! ${converted} converted, ${skipped} skipped.`);
138
+
139
+ // Show next steps only if we converted files
140
+ if (converted > 0) {
141
+ printNextSteps(outputPath);
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Find all supported files in a directory recursively
147
+ */
148
+ async function findFiles(dir, format) {
149
+ const files = [];
150
+ const entries = await fs.readdir(dir, { withFileTypes: true });
151
+
152
+ for (const entry of entries) {
153
+ const fullPath = path.join(dir, entry.name);
154
+
155
+ if (entry.isDirectory()) {
156
+ // Skip md output directory to avoid confusion
157
+ if (entry.name === 'converted') continue;
158
+ const subFiles = await findFiles(fullPath, format);
159
+ files.push(...subFiles);
160
+ } else if (entry.isFile()) {
161
+ const ext = path.extname(entry.name).toLowerCase();
162
+ if (SUPPORTED_EXTENSIONS.includes(ext)) {
163
+ files.push(fullPath);
164
+ }
165
+ }
166
+ }
167
+
168
+ return files;
169
+ }
170
+
171
+ /**
172
+ * Get output filename for a source file
173
+ */
174
+ function getOutputName(file, sourcePath, outputPath, flatten, isSourceDir) {
175
+ const baseName = path.basename(file, path.extname(file)) + '.md';
176
+
177
+ if (flatten || !isSourceDir) {
178
+ return path.join(outputPath, baseName);
179
+ }
180
+
181
+ // Preserve directory structure
182
+ const relativePath = path.relative(sourcePath, file);
183
+ const relativeDir = path.dirname(relativePath);
184
+ return path.join(outputPath, relativeDir, baseName);
185
+ }
186
+
187
+ /**
188
+ * Check if file exists
189
+ */
190
+ async function fileExists(filePath) {
191
+ try {
192
+ await fs.access(filePath);
193
+ return true;
194
+ } catch {
195
+ return false;
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Count how many files would be converted (for dry run)
201
+ */
202
+ async function countConvertible(files, sourcePath, outputPath, flatten, isSourceDir, overwrite) {
203
+ let count = 0;
204
+ for (const file of files) {
205
+ const outputFile = getOutputName(file, sourcePath, outputPath, flatten, isSourceDir);
206
+ const exists = await fileExists(outputFile);
207
+ if (!exists || overwrite) {
208
+ count++;
209
+ }
210
+ }
211
+ return count;
212
+ }
213
+
214
+ /**
215
+ * Convert a single file based on its extension
216
+ */
217
+ async function convertFile(filePath) {
218
+ const ext = path.extname(filePath).toLowerCase();
219
+
220
+ switch (ext) {
221
+ case '.docx':
222
+ return await convertDocx(filePath);
223
+ case '.pptx':
224
+ return await convertPptx(filePath);
225
+ case '.pdf':
226
+ return await convertPdf(filePath);
227
+ case '.md':
228
+ return await copyMd(filePath);
229
+ default:
230
+ throw new Error(`Unsupported format: ${ext}`);
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Convert DOCX to markdown using mammoth
236
+ */
237
+ async function convertDocx(filePath) {
238
+ const mammoth = await import('mammoth');
239
+ const result = await mammoth.convertToMarkdown({ path: filePath });
240
+
241
+ return {
242
+ markdown: result.value,
243
+ warnings: result.messages.filter(m => m.type === 'warning')
244
+ };
245
+ }
246
+
247
+ /**
248
+ * Convert PPTX to markdown using node-pptx-parser
249
+ */
250
+ async function convertPptx(filePath) {
251
+ const PptxParser = (await import('node-pptx-parser')).default;
252
+
253
+ const parser = new PptxParser(filePath);
254
+ const textContent = await parser.extractText();
255
+
256
+ let markdown = '';
257
+ let slideNum = 0;
258
+
259
+ for (const slide of textContent) {
260
+ slideNum++;
261
+ markdown += `# Slide ${slideNum}\n\n`;
262
+
263
+ // Add slide text content
264
+ if (slide.text && slide.text.length > 0) {
265
+ for (const text of slide.text) {
266
+ if (text && text.trim()) {
267
+ markdown += `${text.trim()}\n\n`;
268
+ }
269
+ }
270
+ }
271
+
272
+ markdown += '---\n\n';
273
+ }
274
+
275
+ return {
276
+ markdown: markdown.trim(),
277
+ slideCount: slideNum,
278
+ warnings: []
279
+ };
280
+ }
281
+
282
+ /**
283
+ * Convert PDF to markdown/json using pdf2json custom parser
284
+ */
285
+ async function convertPdf(filePath) {
286
+ const { parsePdfStructure } = await import('./pdf-structure.js');
287
+ const structure = await parsePdfStructure(filePath);
288
+
289
+ // Convert structure to Markdown
290
+ let markdown = '';
291
+ for (const element of structure.elements) {
292
+ if (element.type.startsWith('h')) {
293
+ const level = element.type.substring(1);
294
+ markdown += `${'#'.repeat(parseInt(level))} ${element.text}\n\n`;
295
+ } else {
296
+ markdown += `${element.text}\n\n`;
297
+ }
298
+ }
299
+
300
+ return {
301
+ markdown: markdown.trim(),
302
+ data: structure, // Return structured data for JSON output if needed
303
+ warnings: []
304
+ };
305
+ }
306
+
307
+ /**
308
+ * Copy markdown files directly (they're already in the right format)
309
+ */
310
+ async function copyMd(filePath) {
311
+ const content = await fs.readFile(filePath, 'utf-8');
312
+ return {
313
+ markdown: content,
314
+ warnings: []
315
+ };
316
+ }
317
+
318
+ /**
319
+ * Print next steps for AI-assisted workflow
320
+ */
321
+ function printNextSteps(outputPath) {
322
+ const relativeOutput = path.relative(process.cwd(), outputPath);
323
+
324
+ console.log(`
325
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
326
+ 📋 Next steps (AI-assisted workflow):
327
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
328
+
329
+ 1. CREATE OUTLINE: Ask your AI assistant to reference:
330
+ - framework/docs/COURSE_OUTLINE_GUIDE.md
331
+ - framework/docs/COURSE_AUTHORING_GUIDE.md
332
+ - ${relativeOutput}/*.md (your converted files)
333
+
334
+ 2. IMPLEMENT COURSE: Then reference:
335
+ - framework/docs/COURSE_AUTHORING_GUIDE.md
336
+ - your-outline.md
337
+
338
+ Attach files using your AI tool's method (drag-drop, @file, #file, etc.)
339
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
340
+ `);
341
+ }