@patch-adams/core 1.5.24 → 1.6.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/dist/index.cjs CHANGED
@@ -5696,6 +5696,139 @@ function buildSkinJsOptions(config) {
5696
5696
  };
5697
5697
  }
5698
5698
 
5699
+ // src/templates/class-mappings.ts
5700
+ function hasClassMappings(mappings) {
5701
+ if (!mappings) return false;
5702
+ if (mappings.course?.trim()) return true;
5703
+ if (mappings.lessons && Object.values(mappings.lessons).some((v) => v?.trim())) return true;
5704
+ if (mappings.blocks && Object.values(mappings.blocks).some((v) => v?.trim())) return true;
5705
+ return false;
5706
+ }
5707
+ function generateClassMappingsScript(mappings) {
5708
+ if (!hasClassMappings(mappings)) return "";
5709
+ const courseClasses = (mappings.course || "").trim();
5710
+ const blockMap = {};
5711
+ const lessonMap = {};
5712
+ if (mappings.blocks) {
5713
+ for (const [id, classes] of Object.entries(mappings.blocks)) {
5714
+ const trimmed = classes?.trim();
5715
+ if (trimmed) blockMap[id] = trimmed;
5716
+ }
5717
+ }
5718
+ if (mappings.lessons) {
5719
+ for (const [id, classes] of Object.entries(mappings.lessons)) {
5720
+ const trimmed = classes?.trim();
5721
+ if (trimmed) lessonMap[id] = trimmed;
5722
+ }
5723
+ }
5724
+ const hasBlocks = Object.keys(blockMap).length > 0;
5725
+ const hasLessons = Object.keys(lessonMap).length > 0;
5726
+ const parts = [];
5727
+ parts.push(`// PA-Patcher: Custom Class Mappings`);
5728
+ parts.push(`(function() {`);
5729
+ parts.push(` 'use strict';`);
5730
+ parts.push(``);
5731
+ if (courseClasses) {
5732
+ parts.push(` // Course-level classes (applied to <html>)`);
5733
+ parts.push(` var courseClasses = ${JSON.stringify(courseClasses)};`);
5734
+ parts.push(` courseClasses.split(' ').forEach(function(cls) {`);
5735
+ parts.push(` if (cls) document.documentElement.classList.add(cls);`);
5736
+ parts.push(` });`);
5737
+ parts.push(``);
5738
+ }
5739
+ if (hasBlocks) {
5740
+ parts.push(` // Block-level classes (applied to [data-block-id] elements)`);
5741
+ parts.push(` var blockMap = ${JSON.stringify(blockMap)};`);
5742
+ parts.push(``);
5743
+ parts.push(` function applyBlockClasses() {`);
5744
+ parts.push(` var ids = Object.keys(blockMap);`);
5745
+ parts.push(` for (var i = 0; i < ids.length; i++) {`);
5746
+ parts.push(` var el = document.querySelector('[data-block-id="' + ids[i] + '"]');`);
5747
+ parts.push(` if (el) {`);
5748
+ parts.push(` var classes = blockMap[ids[i]].split(' ');`);
5749
+ parts.push(` for (var j = 0; j < classes.length; j++) {`);
5750
+ parts.push(` if (classes[j]) el.classList.add(classes[j]);`);
5751
+ parts.push(` }`);
5752
+ parts.push(` }`);
5753
+ parts.push(` }`);
5754
+ parts.push(` }`);
5755
+ parts.push(``);
5756
+ }
5757
+ if (hasLessons) {
5758
+ parts.push(` // Lesson-level classes (toggled on <html> via hash navigation)`);
5759
+ parts.push(` var lessonMap = ${JSON.stringify(lessonMap)};`);
5760
+ parts.push(` var allLessonClasses = [];`);
5761
+ parts.push(` Object.keys(lessonMap).forEach(function(id) {`);
5762
+ parts.push(` lessonMap[id].split(' ').forEach(function(cls) {`);
5763
+ parts.push(` if (cls && allLessonClasses.indexOf(cls) === -1) allLessonClasses.push(cls);`);
5764
+ parts.push(` });`);
5765
+ parts.push(` });`);
5766
+ parts.push(``);
5767
+ parts.push(` var currentLessonId = null;`);
5768
+ parts.push(``);
5769
+ parts.push(` function detectCurrentLesson() {`);
5770
+ parts.push(` var hash = window.location.hash || '';`);
5771
+ parts.push(` var match = hash.match(/#\\/lessons\\/([^/]+)/);`);
5772
+ parts.push(` return match ? match[1] : null;`);
5773
+ parts.push(` }`);
5774
+ parts.push(``);
5775
+ parts.push(` function applyLessonClasses() {`);
5776
+ parts.push(` var lessonId = detectCurrentLesson();`);
5777
+ parts.push(` if (lessonId === currentLessonId) return;`);
5778
+ parts.push(` currentLessonId = lessonId;`);
5779
+ parts.push(` for (var i = 0; i < allLessonClasses.length; i++) {`);
5780
+ parts.push(` document.documentElement.classList.remove(allLessonClasses[i]);`);
5781
+ parts.push(` }`);
5782
+ parts.push(` if (lessonId && lessonMap[lessonId]) {`);
5783
+ parts.push(` var classes = lessonMap[lessonId].split(' ');`);
5784
+ parts.push(` for (var j = 0; j < classes.length; j++) {`);
5785
+ parts.push(` if (classes[j]) document.documentElement.classList.add(classes[j]);`);
5786
+ parts.push(` }`);
5787
+ parts.push(` }`);
5788
+ parts.push(` }`);
5789
+ parts.push(``);
5790
+ }
5791
+ parts.push(` // Initialization`);
5792
+ parts.push(` function init() {`);
5793
+ if (hasBlocks) {
5794
+ parts.push(` applyBlockClasses();`);
5795
+ }
5796
+ if (hasLessons) {
5797
+ parts.push(` applyLessonClasses();`);
5798
+ parts.push(``);
5799
+ parts.push(` // Watch for Rise SPA navigation`);
5800
+ parts.push(` window.addEventListener('hashchange', function() {`);
5801
+ parts.push(` applyLessonClasses();`);
5802
+ if (hasBlocks) {
5803
+ parts.push(` setTimeout(applyBlockClasses, 200);`);
5804
+ }
5805
+ parts.push(` });`);
5806
+ }
5807
+ if (hasBlocks) {
5808
+ parts.push(``);
5809
+ parts.push(` // MutationObserver for lazily-rendered Rise blocks`);
5810
+ parts.push(` var observer = new MutationObserver(function(mutations) {`);
5811
+ parts.push(` for (var i = 0; i < mutations.length; i++) {`);
5812
+ parts.push(` if (mutations[i].addedNodes.length > 0) {`);
5813
+ parts.push(` applyBlockClasses();`);
5814
+ parts.push(` return;`);
5815
+ parts.push(` }`);
5816
+ parts.push(` }`);
5817
+ parts.push(` });`);
5818
+ parts.push(` var container = document.querySelector('#app') || document.body;`);
5819
+ parts.push(` observer.observe(container, { childList: true, subtree: true });`);
5820
+ }
5821
+ parts.push(` }`);
5822
+ parts.push(``);
5823
+ parts.push(` if (document.readyState === 'loading') {`);
5824
+ parts.push(` document.addEventListener('DOMContentLoaded', init);`);
5825
+ parts.push(` } else {`);
5826
+ parts.push(` init();`);
5827
+ parts.push(` }`);
5828
+ parts.push(`})();`);
5829
+ return parts.join("\n");
5830
+ }
5831
+
5699
5832
  // src/patcher/html-injector.ts
5700
5833
  var HtmlInjector = class {
5701
5834
  config;
@@ -7086,6 +7219,13 @@ var Patcher = class {
7086
7219
  console.log(`[Patcher] Fetched ${fetchedCount} remote files`);
7087
7220
  }
7088
7221
  const pluginAssets = this.generatePluginAssets();
7222
+ if (hasClassMappings(options.classMappings)) {
7223
+ const classMappingsJs = generateClassMappingsScript(options.classMappings);
7224
+ if (classMappingsJs) {
7225
+ pluginAssets.jsAfter = classMappingsJs + (pluginAssets.jsAfter ? "\n" + pluginAssets.jsAfter : "");
7226
+ console.log("[Patcher] Class mappings JS will be injected inline into HTML");
7227
+ }
7228
+ }
7089
7229
  const hasPluginAssets = pluginAssets.cssBefore || pluginAssets.cssAfter || pluginAssets.jsBefore || pluginAssets.jsAfter;
7090
7230
  if (hasPluginAssets) {
7091
7231
  htmlInjector.setPluginAssets(pluginAssets);
@@ -7445,6 +7585,87 @@ var Logger = class {
7445
7585
  this.level = level;
7446
7586
  }
7447
7587
  };
7588
+ var DESERIALIZE_REGEX = /deserialize\s*\(\s*["']([A-Za-z0-9+/=]+)["']\s*\)/;
7589
+ var RISE_HTML_PATHS = [
7590
+ "scormcontent/index.html",
7591
+ "index.html"
7592
+ ];
7593
+ function extractCourseStructure(zipBuffer) {
7594
+ try {
7595
+ const zip = new AdmZip__default.default(zipBuffer);
7596
+ let htmlContent = null;
7597
+ for (const path of RISE_HTML_PATHS) {
7598
+ const entry = zip.getEntry(path);
7599
+ if (entry) {
7600
+ htmlContent = entry.getData().toString("utf-8");
7601
+ if (DESERIALIZE_REGEX.test(htmlContent)) break;
7602
+ htmlContent = null;
7603
+ }
7604
+ }
7605
+ if (!htmlContent) {
7606
+ return null;
7607
+ }
7608
+ const match = htmlContent.match(DESERIALIZE_REGEX);
7609
+ if (!match || !match[1]) {
7610
+ return null;
7611
+ }
7612
+ const jsonStr = Buffer.from(match[1], "base64").toString("utf-8");
7613
+ const data = JSON.parse(jsonStr);
7614
+ const course = data.course;
7615
+ if (!course || !Array.isArray(course.lessons)) {
7616
+ return null;
7617
+ }
7618
+ const lessons = course.lessons.filter((lesson) => !lesson.deleted).sort((a, b) => (a.position ?? 0) - (b.position ?? 0)).map((lesson) => {
7619
+ const blocks = (lesson.items || []).map((block) => {
7620
+ const structBlock = {
7621
+ id: block.id,
7622
+ type: block.type || "unknown"
7623
+ };
7624
+ if (block.family) structBlock.family = block.family;
7625
+ if (block.variant) structBlock.variant = block.variant;
7626
+ const title = deriveBlockTitle(block);
7627
+ if (title) structBlock.title = title;
7628
+ return structBlock;
7629
+ });
7630
+ return {
7631
+ id: lesson.id,
7632
+ title: lesson.title || "Untitled Lesson",
7633
+ type: lesson.type || "blocks",
7634
+ blocks
7635
+ };
7636
+ });
7637
+ return {
7638
+ courseTitle: course.title || course.name || "Untitled Course",
7639
+ courseId: course.id || void 0,
7640
+ lessons
7641
+ };
7642
+ } catch {
7643
+ return null;
7644
+ }
7645
+ }
7646
+ function deriveBlockTitle(block) {
7647
+ if (block.title && typeof block.title === "string") {
7648
+ return stripHtml(block.title).substring(0, 80);
7649
+ }
7650
+ const items = block.items;
7651
+ if (!Array.isArray(items) || items.length === 0) return void 0;
7652
+ const firstItem = items[0];
7653
+ if (!firstItem) return void 0;
7654
+ if (firstItem.heading) {
7655
+ return stripHtml(firstItem.heading).substring(0, 80);
7656
+ }
7657
+ if (firstItem.title && typeof firstItem.title === "string") {
7658
+ return stripHtml(firstItem.title).substring(0, 80);
7659
+ }
7660
+ if (firstItem.paragraph) {
7661
+ const text = stripHtml(firstItem.paragraph).substring(0, 60);
7662
+ if (text) return text + (firstItem.paragraph.length > 60 ? "..." : "");
7663
+ }
7664
+ return void 0;
7665
+ }
7666
+ function stripHtml(html) {
7667
+ return html.replace(/<[^>]*>/g, "").trim();
7668
+ }
7448
7669
 
7449
7670
  exports.AsyncAssetConfigSchema = AsyncAssetConfigSchema;
7450
7671
  exports.AuthoringToolDetector = AuthoringToolDetector;
@@ -7467,12 +7688,15 @@ exports.buildJsBeforeOptions = buildJsBeforeOptions;
7467
7688
  exports.defaultConfig = defaultConfig;
7468
7689
  exports.extractCourseFingerprint = extractCourseFingerprint;
7469
7690
  exports.extractCourseMetadata = extractCourseMetadata;
7691
+ exports.extractCourseStructure = extractCourseStructure;
7692
+ exports.generateClassMappingsScript = generateClassMappingsScript;
7470
7693
  exports.generateConfigTemplate = generateConfigTemplate;
7471
7694
  exports.generateCssAfterLoader = generateCssAfterLoader;
7472
7695
  exports.generateCssBeforeLoader = generateCssBeforeLoader;
7473
7696
  exports.generateJsAfterLoader = generateJsAfterLoader;
7474
7697
  exports.generateJsBeforeLoader = generateJsBeforeLoader;
7475
7698
  exports.generateLrsBridgeCode = generateLrsBridgeCode;
7699
+ exports.hasClassMappings = hasClassMappings;
7476
7700
  exports.loadConfig = loadConfig;
7477
7701
  exports.mergeWithDefaults = mergeWithDefaults;
7478
7702
  exports.pluginRegistry = pluginRegistry;