@patch-adams/core 1.4.37 → 1.5.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.d.cts CHANGED
@@ -291,6 +291,8 @@ declare const PatchAdamsConfigSchema: z.ZodObject<{
291
291
  /** Whether this plugin is enabled */
292
292
  enabled: z.ZodDefault<z.ZodBoolean>;
293
293
  }, z.ZodTypeAny, "passthrough">>>>;
294
+ /** Optional skin name — adds 'pa-skinned' + skin class to <html>, loads skin CSS/JS after core files */
295
+ skin: z.ZodOptional<z.ZodString>;
294
296
  }, "strip", z.ZodTypeAny, {
295
297
  remoteDomain: string;
296
298
  htmlClass: string;
@@ -341,6 +343,7 @@ declare const PatchAdamsConfigSchema: z.ZodObject<{
341
343
  /** Whether this plugin is enabled */
342
344
  enabled: z.ZodDefault<z.ZodBoolean>;
343
345
  }, z.ZodTypeAny, "passthrough">>;
346
+ skin?: string | undefined;
344
347
  }, {
345
348
  remoteDomain: string;
346
349
  htmlClass?: string | undefined;
@@ -391,6 +394,7 @@ declare const PatchAdamsConfigSchema: z.ZodObject<{
391
394
  /** Whether this plugin is enabled */
392
395
  enabled: z.ZodDefault<z.ZodBoolean>;
393
396
  }, z.ZodTypeAny, "passthrough">> | undefined;
397
+ skin?: string | undefined;
394
398
  }>;
395
399
  type PatchAdamsConfig = z.infer<typeof PatchAdamsConfigSchema>;
396
400
  type LrsBridgeConfig = z.infer<typeof LrsBridgeConfigSchema>;
@@ -698,6 +702,14 @@ declare class HtmlInjector {
698
702
  * Inject JS After loader at end of <body> (after __loadEntry)
699
703
  */
700
704
  private injectJsAfter;
705
+ /**
706
+ * Inject Skin CSS loader after CSS After (before </head>)
707
+ */
708
+ private injectSkinCss;
709
+ /**
710
+ * Inject Skin JS loader after JS After (before </body>)
711
+ */
712
+ private injectSkinJs;
701
713
  }
702
714
 
703
715
  /**
@@ -770,6 +782,14 @@ declare class StorylineHtmlInjector {
770
782
  * We inject before </body> so our code runs before the bootstrapper.
771
783
  */
772
784
  private injectJsAfter;
785
+ /**
786
+ * Inject Skin CSS loader after CSS After (before </head>)
787
+ */
788
+ private injectSkinCss;
789
+ /**
790
+ * Inject Skin JS loader after JS After (before </body>)
791
+ */
792
+ private injectSkinJs;
773
793
  }
774
794
 
775
795
  interface ManifestPaths {
@@ -777,6 +797,8 @@ interface ManifestPaths {
777
797
  cssAfter?: string;
778
798
  jsBefore?: string;
779
799
  jsAfter?: string;
800
+ skinCss?: string;
801
+ skinJs?: string;
780
802
  }
781
803
  /**
782
804
  * Update manifest files to include injected assets
@@ -840,6 +862,8 @@ interface PatchOptions {
840
862
  jsAfterContent?: string;
841
863
  /** Skip fetching remote files even if fetchFallbacks is enabled in config */
842
864
  skipFetch?: boolean;
865
+ /** Optional skin name — per-call override for config.skin */
866
+ skin?: string;
843
867
  }
844
868
  /**
845
869
  * Main Patcher class for patching e-learning course packages
@@ -912,7 +936,7 @@ declare class Patcher {
912
936
  * @param buffer - Input ZIP file as Buffer
913
937
  * @returns Patched ZIP file as Buffer
914
938
  */
915
- patchBuffer(buffer: Buffer): Promise<Buffer>;
939
+ patchBuffer(buffer: Buffer, options?: PatchOptions): Promise<Buffer>;
916
940
  }
917
941
 
918
942
  /**
@@ -1030,6 +1054,8 @@ interface JsBeforeOptions {
1030
1054
  metadata: CourseMetadata | null;
1031
1055
  /** LRS Bridge configuration */
1032
1056
  lrsBridge: LrsBridgeOptions;
1057
+ /** Optional skin name */
1058
+ skin?: string;
1033
1059
  }
1034
1060
  /**
1035
1061
  * Generate the blocking JS loader for the "before" slot
package/dist/index.d.ts CHANGED
@@ -291,6 +291,8 @@ declare const PatchAdamsConfigSchema: z.ZodObject<{
291
291
  /** Whether this plugin is enabled */
292
292
  enabled: z.ZodDefault<z.ZodBoolean>;
293
293
  }, z.ZodTypeAny, "passthrough">>>>;
294
+ /** Optional skin name — adds 'pa-skinned' + skin class to <html>, loads skin CSS/JS after core files */
295
+ skin: z.ZodOptional<z.ZodString>;
294
296
  }, "strip", z.ZodTypeAny, {
295
297
  remoteDomain: string;
296
298
  htmlClass: string;
@@ -341,6 +343,7 @@ declare const PatchAdamsConfigSchema: z.ZodObject<{
341
343
  /** Whether this plugin is enabled */
342
344
  enabled: z.ZodDefault<z.ZodBoolean>;
343
345
  }, z.ZodTypeAny, "passthrough">>;
346
+ skin?: string | undefined;
344
347
  }, {
345
348
  remoteDomain: string;
346
349
  htmlClass?: string | undefined;
@@ -391,6 +394,7 @@ declare const PatchAdamsConfigSchema: z.ZodObject<{
391
394
  /** Whether this plugin is enabled */
392
395
  enabled: z.ZodDefault<z.ZodBoolean>;
393
396
  }, z.ZodTypeAny, "passthrough">> | undefined;
397
+ skin?: string | undefined;
394
398
  }>;
395
399
  type PatchAdamsConfig = z.infer<typeof PatchAdamsConfigSchema>;
396
400
  type LrsBridgeConfig = z.infer<typeof LrsBridgeConfigSchema>;
@@ -698,6 +702,14 @@ declare class HtmlInjector {
698
702
  * Inject JS After loader at end of <body> (after __loadEntry)
699
703
  */
700
704
  private injectJsAfter;
705
+ /**
706
+ * Inject Skin CSS loader after CSS After (before </head>)
707
+ */
708
+ private injectSkinCss;
709
+ /**
710
+ * Inject Skin JS loader after JS After (before </body>)
711
+ */
712
+ private injectSkinJs;
701
713
  }
702
714
 
703
715
  /**
@@ -770,6 +782,14 @@ declare class StorylineHtmlInjector {
770
782
  * We inject before </body> so our code runs before the bootstrapper.
771
783
  */
772
784
  private injectJsAfter;
785
+ /**
786
+ * Inject Skin CSS loader after CSS After (before </head>)
787
+ */
788
+ private injectSkinCss;
789
+ /**
790
+ * Inject Skin JS loader after JS After (before </body>)
791
+ */
792
+ private injectSkinJs;
773
793
  }
774
794
 
775
795
  interface ManifestPaths {
@@ -777,6 +797,8 @@ interface ManifestPaths {
777
797
  cssAfter?: string;
778
798
  jsBefore?: string;
779
799
  jsAfter?: string;
800
+ skinCss?: string;
801
+ skinJs?: string;
780
802
  }
781
803
  /**
782
804
  * Update manifest files to include injected assets
@@ -840,6 +862,8 @@ interface PatchOptions {
840
862
  jsAfterContent?: string;
841
863
  /** Skip fetching remote files even if fetchFallbacks is enabled in config */
842
864
  skipFetch?: boolean;
865
+ /** Optional skin name — per-call override for config.skin */
866
+ skin?: string;
843
867
  }
844
868
  /**
845
869
  * Main Patcher class for patching e-learning course packages
@@ -912,7 +936,7 @@ declare class Patcher {
912
936
  * @param buffer - Input ZIP file as Buffer
913
937
  * @returns Patched ZIP file as Buffer
914
938
  */
915
- patchBuffer(buffer: Buffer): Promise<Buffer>;
939
+ patchBuffer(buffer: Buffer, options?: PatchOptions): Promise<Buffer>;
916
940
  }
917
941
 
918
942
  /**
@@ -1030,6 +1054,8 @@ interface JsBeforeOptions {
1030
1054
  metadata: CourseMetadata | null;
1031
1055
  /** LRS Bridge configuration */
1032
1056
  lrsBridge: LrsBridgeOptions;
1057
+ /** Optional skin name */
1058
+ skin?: string;
1033
1059
  }
1034
1060
  /**
1035
1061
  * Generate the blocking JS loader for the "before" slot
package/dist/index.js CHANGED
@@ -106,7 +106,9 @@ var PatchAdamsConfigSchema = z.object({
106
106
  /** LRS Bridge configuration for xAPI/LRS communication with Bravais */
107
107
  lrsBridge: LrsBridgeConfigSchema.default({}),
108
108
  /** Plugin configurations - each plugin is keyed by its name */
109
- plugins: PluginsConfigSchema
109
+ plugins: PluginsConfigSchema,
110
+ /** Optional skin name — adds 'pa-skinned' + skin class to <html>, loads skin CSS/JS after core files */
111
+ skin: z.string().min(1).optional()
110
112
  });
111
113
 
112
114
  // src/config/defaults.ts
@@ -4377,7 +4379,7 @@ function escapeJs(str) {
4377
4379
  return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r");
4378
4380
  }
4379
4381
  function generateJsBeforeLoader(options) {
4380
- const { remoteUrl, localPath, htmlClass, loadingClass, metadata, lrsBridge } = options;
4382
+ const { remoteUrl, localPath, htmlClass, loadingClass, metadata, lrsBridge, skin } = options;
4381
4383
  const lrsBridgeCode = generateLrsBridgeCode(lrsBridge);
4382
4384
  const courseLines = [];
4383
4385
  if (metadata) {
@@ -4420,12 +4422,13 @@ ${courseLines.join("\n")}
4420
4422
  window.pa_patcher = window.pa_patcher || {
4421
4423
  version: '1.0.25',
4422
4424
  htmlClass: '${htmlClass}',
4423
- loadingClass: '${loadingClass}',${courseBlock}
4425
+ loadingClass: '${loadingClass}',${skin ? `
4426
+ skin: '${escapeJs(skin)}',` : ""}${courseBlock}
4424
4427
  loaded: {
4425
4428
  cssBefore: false,
4426
4429
  cssAfter: false,
4427
4430
  jsBefore: false,
4428
- jsAfter: false
4431
+ jsAfter: false${skin ? ",\n skinCss: false,\n skinJs: false" : ""}
4429
4432
  }
4430
4433
  };
4431
4434
 
@@ -4546,7 +4549,8 @@ function buildJsBeforeOptions(config, metadata) {
4546
4549
  htmlClass: config.htmlClass,
4547
4550
  loadingClass: config.loadingClass,
4548
4551
  metadata: metadata ?? null,
4549
- lrsBridge
4552
+ lrsBridge,
4553
+ skin: config.skin
4550
4554
  };
4551
4555
  }
4552
4556
 
@@ -4669,6 +4673,196 @@ function buildJsAfterOptions(config) {
4669
4673
  };
4670
4674
  }
4671
4675
 
4676
+ // src/templates/skin-css.ts
4677
+ function generateSkinCssLoader(options) {
4678
+ const { remoteUrl, localPath, timeout } = options;
4679
+ return `<!-- === PATCH-ADAMS: SKIN CSS (async with fallback) === -->
4680
+ <script data-pa="skin-css-loader">
4681
+ (function() {
4682
+ 'use strict';
4683
+ var REMOTE_URL = "${remoteUrl}";
4684
+ var LOCAL_PATH = "${localPath}";
4685
+ var TIMEOUT = ${timeout};
4686
+
4687
+ function loadCSS(url, onSuccess, onError) {
4688
+ var link = document.createElement('link');
4689
+ link.rel = 'stylesheet';
4690
+ link.href = url;
4691
+ link.setAttribute('data-pa', 'skin-css');
4692
+
4693
+ link.onload = function() {
4694
+ if (onSuccess) onSuccess();
4695
+ };
4696
+
4697
+ link.onerror = function() {
4698
+ if (onError) onError();
4699
+ };
4700
+
4701
+ document.head.appendChild(link);
4702
+ return link;
4703
+ }
4704
+
4705
+ function loadCSSWithFallback() {
4706
+ var loaded = false;
4707
+ var timeoutId;
4708
+
4709
+ // Try remote first
4710
+ var remoteLink = loadCSS(
4711
+ REMOTE_URL,
4712
+ function() {
4713
+ if (loaded) return;
4714
+ loaded = true;
4715
+ clearTimeout(timeoutId);
4716
+ console.log('[PA-Patcher] Skin CSS loaded from remote:', REMOTE_URL);
4717
+ },
4718
+ function() {
4719
+ if (loaded) return;
4720
+ loaded = true;
4721
+ clearTimeout(timeoutId);
4722
+ loadLocalFallback();
4723
+ }
4724
+ );
4725
+
4726
+ // Timeout fallback
4727
+ timeoutId = setTimeout(function() {
4728
+ if (loaded) return;
4729
+ loaded = true;
4730
+ console.warn('[PA-Patcher] Skin CSS timed out, using local fallback');
4731
+ if (remoteLink.parentNode) {
4732
+ document.head.removeChild(remoteLink);
4733
+ }
4734
+ loadLocalFallback();
4735
+ }, TIMEOUT);
4736
+ }
4737
+
4738
+ function loadLocalFallback() {
4739
+ loadCSS(
4740
+ LOCAL_PATH,
4741
+ function() {
4742
+ console.log('[PA-Patcher] Skin CSS loaded from local fallback:', LOCAL_PATH);
4743
+ },
4744
+ function() {
4745
+ console.error('[PA-Patcher] Skin CSS failed to load from both remote and local');
4746
+ }
4747
+ );
4748
+ }
4749
+
4750
+ // Execute immediately
4751
+ loadCSSWithFallback();
4752
+ })();
4753
+ </script>`;
4754
+ }
4755
+ function buildSkinCssOptions(config) {
4756
+ if (!config.skin) return null;
4757
+ return {
4758
+ remoteUrl: `${config.remoteDomain}/skin/${config.skin}.css`,
4759
+ localPath: `skin/${config.skin}.css`,
4760
+ timeout: config.cssAfter.timeout
4761
+ // reuse cssAfter timeout
4762
+ };
4763
+ }
4764
+
4765
+ // src/templates/skin-js.ts
4766
+ function generateSkinJsLoader(options) {
4767
+ const { remoteUrl, localPath, timeout } = options;
4768
+ return `<!-- === PATCH-ADAMS: SKIN JS (async with fallback) === -->
4769
+ <script data-pa="skin-js-loader">
4770
+ (function() {
4771
+ 'use strict';
4772
+ var REMOTE_URL = "${remoteUrl}";
4773
+ var LOCAL_PATH = "${localPath}";
4774
+ var TIMEOUT = ${timeout};
4775
+
4776
+ function loadJS(url, onSuccess, onError) {
4777
+ var script = document.createElement('script');
4778
+ script.src = url;
4779
+ script.async = true;
4780
+ script.setAttribute('data-pa', 'skin-js');
4781
+
4782
+ script.onload = function() {
4783
+ if (onSuccess) onSuccess();
4784
+ };
4785
+
4786
+ script.onerror = function() {
4787
+ if (onError) onError();
4788
+ };
4789
+
4790
+ document.body.appendChild(script);
4791
+ return script;
4792
+ }
4793
+
4794
+ function loadJSWithFallback() {
4795
+ var loaded = false;
4796
+ var timeoutId;
4797
+
4798
+ // Try remote first
4799
+ var remoteScript = loadJS(
4800
+ REMOTE_URL,
4801
+ function() {
4802
+ if (loaded) return;
4803
+ loaded = true;
4804
+ clearTimeout(timeoutId);
4805
+ console.log('[PA-Patcher] Skin JS loaded from remote:', REMOTE_URL);
4806
+ if (window.pa_patcher && window.pa_patcher.loaded) {
4807
+ window.pa_patcher.loaded.skinJs = true;
4808
+ }
4809
+ },
4810
+ function() {
4811
+ if (loaded) return;
4812
+ loaded = true;
4813
+ clearTimeout(timeoutId);
4814
+ loadLocalFallback();
4815
+ }
4816
+ );
4817
+
4818
+ // Timeout fallback
4819
+ timeoutId = setTimeout(function() {
4820
+ if (loaded) return;
4821
+ loaded = true;
4822
+ console.warn('[PA-Patcher] Skin JS timed out, using local fallback');
4823
+ if (remoteScript.parentNode) {
4824
+ document.body.removeChild(remoteScript);
4825
+ }
4826
+ loadLocalFallback();
4827
+ }, TIMEOUT);
4828
+ }
4829
+
4830
+ function loadLocalFallback() {
4831
+ loadJS(
4832
+ LOCAL_PATH,
4833
+ function() {
4834
+ console.log('[PA-Patcher] Skin JS loaded from local fallback:', LOCAL_PATH);
4835
+ if (window.pa_patcher && window.pa_patcher.loaded) {
4836
+ window.pa_patcher.loaded.skinJs = true;
4837
+ }
4838
+ },
4839
+ function() {
4840
+ console.error('[PA-Patcher] Skin JS failed to load from both remote and local');
4841
+ }
4842
+ );
4843
+ }
4844
+
4845
+ // Execute after DOM is ready
4846
+ if (document.readyState === 'loading') {
4847
+ document.addEventListener('DOMContentLoaded', function() {
4848
+ setTimeout(loadJSWithFallback, 150);
4849
+ });
4850
+ } else {
4851
+ setTimeout(loadJSWithFallback, 150);
4852
+ }
4853
+ })();
4854
+ </script>`;
4855
+ }
4856
+ function buildSkinJsOptions(config) {
4857
+ if (!config.skin) return null;
4858
+ return {
4859
+ remoteUrl: `${config.remoteDomain}/skin/${config.skin}.js`,
4860
+ localPath: `skin/${config.skin}.js`,
4861
+ timeout: config.jsAfter.timeout
4862
+ // reuse jsAfter timeout
4863
+ };
4864
+ }
4865
+
4672
4866
  // src/patcher/html-injector.ts
4673
4867
  var HtmlInjector = class {
4674
4868
  config;
@@ -4732,6 +4926,10 @@ var HtmlInjector = class {
4732
4926
  if (this.config.jsAfter.enabled) {
4733
4927
  result = this.injectJsAfter(result);
4734
4928
  }
4929
+ if (this.config.skin) {
4930
+ result = this.injectSkinCss(result);
4931
+ result = this.injectSkinJs(result);
4932
+ }
4735
4933
  if (this.pluginAssets) {
4736
4934
  result = this.injectPluginAssets(result);
4737
4935
  }
@@ -4778,8 +4976,8 @@ ${pluginJs}
4778
4976
  * Also adds data attributes for course metadata
4779
4977
  */
4780
4978
  addHtmlAttributes(html) {
4781
- const { htmlClass, loadingClass } = this.config;
4782
- const classes = `${htmlClass} ${loadingClass}`;
4979
+ const { htmlClass, loadingClass, skin } = this.config;
4980
+ const classes = `${htmlClass} ${loadingClass}${skin ? ` pa-skinned ${skin}` : ""}`;
4783
4981
  const dataAttrs = [];
4784
4982
  if (this.metadata) {
4785
4983
  dataAttrs.push(`data-pa-course-id="${this.escapeAttr(this.metadata.courseId)}"`);
@@ -4788,6 +4986,9 @@ ${pluginJs}
4788
4986
  dataAttrs.push(`data-pa-title="${this.escapeAttr(this.metadata.title)}"`);
4789
4987
  }
4790
4988
  }
4989
+ if (skin) {
4990
+ dataAttrs.push(`data-pa-skin="${this.escapeAttr(skin)}"`);
4991
+ }
4791
4992
  const dataAttrString = dataAttrs.length > 0 ? " " + dataAttrs.join(" ") : "";
4792
4993
  const htmlTagPattern = /<html([^>]*)>/i;
4793
4994
  const match = html.match(htmlTagPattern);
@@ -4866,6 +5067,26 @@ ${loader}`);
4866
5067
  ${loader}`);
4867
5068
  }
4868
5069
  return html.replace(/<\/body>/i, `${loader}
5070
+ </body>`);
5071
+ }
5072
+ /**
5073
+ * Inject Skin CSS loader after CSS After (before </head>)
5074
+ */
5075
+ injectSkinCss(html) {
5076
+ const options = buildSkinCssOptions(this.config);
5077
+ if (!options) return html;
5078
+ const loader = generateSkinCssLoader(options);
5079
+ return html.replace(/<\/head>/i, `${loader}
5080
+ </head>`);
5081
+ }
5082
+ /**
5083
+ * Inject Skin JS loader after JS After (before </body>)
5084
+ */
5085
+ injectSkinJs(html) {
5086
+ const options = buildSkinJsOptions(this.config);
5087
+ if (!options) return html;
5088
+ const loader = generateSkinJsLoader(options);
5089
+ return html.replace(/<\/body>/i, `${loader}
4869
5090
  </body>`);
4870
5091
  }
4871
5092
  };
@@ -4909,6 +5130,10 @@ var StorylineHtmlInjector = class {
4909
5130
  if (this.config.jsAfter.enabled) {
4910
5131
  result = this.injectJsAfter(result);
4911
5132
  }
5133
+ if (this.config.skin) {
5134
+ result = this.injectSkinCss(result);
5135
+ result = this.injectSkinJs(result);
5136
+ }
4912
5137
  if (this.pluginAssets) {
4913
5138
  result = this.injectPluginAssets(result);
4914
5139
  }
@@ -4953,8 +5178,8 @@ ${pluginJs}
4953
5178
  * Also adds data attributes for course metadata
4954
5179
  */
4955
5180
  addHtmlAttributes(html) {
4956
- const { htmlClass, loadingClass } = this.config;
4957
- const classes = `${htmlClass} ${loadingClass}`;
5181
+ const { htmlClass, loadingClass, skin } = this.config;
5182
+ const classes = `${htmlClass} ${loadingClass}${skin ? ` pa-skinned ${skin}` : ""}`;
4958
5183
  const dataAttrs = [];
4959
5184
  if (this.metadata) {
4960
5185
  dataAttrs.push(`data-pa-course-id="${this.escapeAttr(this.metadata.courseId)}"`);
@@ -4964,6 +5189,9 @@ ${pluginJs}
4964
5189
  dataAttrs.push(`data-pa-title="${this.escapeAttr(this.metadata.title)}"`);
4965
5190
  }
4966
5191
  }
5192
+ if (skin) {
5193
+ dataAttrs.push(`data-pa-skin="${this.escapeAttr(skin)}"`);
5194
+ }
4967
5195
  const dataAttrString = dataAttrs.length > 0 ? " " + dataAttrs.join(" ") : "";
4968
5196
  const htmlTagPattern = /<html([^>]*)>/i;
4969
5197
  const match = html.match(htmlTagPattern);
@@ -5041,6 +5269,26 @@ ${loader}`);
5041
5269
  const options = buildJsAfterOptions(this.config);
5042
5270
  const loader = generateJsAfterLoader(options);
5043
5271
  return html.replace(/<\/body>/i, `${loader}
5272
+ </body>`);
5273
+ }
5274
+ /**
5275
+ * Inject Skin CSS loader after CSS After (before </head>)
5276
+ */
5277
+ injectSkinCss(html) {
5278
+ const options = buildSkinCssOptions(this.config);
5279
+ if (!options) return html;
5280
+ const loader = generateSkinCssLoader(options);
5281
+ return html.replace(/<\/head>/i, `${loader}
5282
+ </head>`);
5283
+ }
5284
+ /**
5285
+ * Inject Skin JS loader after JS After (before </body>)
5286
+ */
5287
+ injectSkinJs(html) {
5288
+ const options = buildSkinJsOptions(this.config);
5289
+ if (!options) return html;
5290
+ const loader = generateSkinJsLoader(options);
5291
+ return html.replace(/<\/body>/i, `${loader}
5044
5292
  </body>`);
5045
5293
  }
5046
5294
  };
@@ -5091,7 +5339,9 @@ var ManifestUpdater = class {
5091
5339
  paths.cssBefore,
5092
5340
  paths.cssAfter,
5093
5341
  paths.jsBefore,
5094
- paths.jsAfter
5342
+ paths.jsAfter,
5343
+ paths.skinCss,
5344
+ paths.skinJs
5095
5345
  ].filter(Boolean);
5096
5346
  if (filesToAdd.length === 0) {
5097
5347
  return [];
@@ -5881,6 +6131,20 @@ var Patcher = class {
5881
6131
  })
5882
6132
  );
5883
6133
  }
6134
+ if (this.config.skin) {
6135
+ const skinCssUrl = `${remoteDomain}/skin/${this.config.skin}.css`;
6136
+ fetchPromises.push(
6137
+ this.fetchFile(skinCssUrl).then((content) => {
6138
+ fetched.skinCss = content;
6139
+ })
6140
+ );
6141
+ const skinJsUrl = `${remoteDomain}/skin/${this.config.skin}.js`;
6142
+ fetchPromises.push(
6143
+ this.fetchFile(skinJsUrl).then((content) => {
6144
+ fetched.skinJs = content;
6145
+ })
6146
+ );
6147
+ }
5884
6148
  await Promise.all(fetchPromises);
5885
6149
  return fetched;
5886
6150
  }
@@ -5964,6 +6228,16 @@ var Patcher = class {
5964
6228
  console.log(`[Patcher] Wrote generated UUID to manifest identifier`);
5965
6229
  }
5966
6230
  }
6231
+ const effectiveSkin = options.skin || this.config.skin;
6232
+ if (options.skin && options.skin !== this.config.skin) {
6233
+ this.config.skin = options.skin;
6234
+ this.riseHtmlInjector = new HtmlInjector(this.config);
6235
+ this.storylineHtmlInjector = new StorylineHtmlInjector(this.config);
6236
+ console.log(`[Patcher] Using per-call skin override: ${options.skin}`);
6237
+ }
6238
+ if (effectiveSkin) {
6239
+ console.log(`[Patcher] Skin: ${effectiveSkin}`);
6240
+ }
5967
6241
  const htmlInjector = this.getHtmlInjector(toolInfo.tool);
5968
6242
  htmlInjector.setMetadata(metadata);
5969
6243
  let fetchedFallbacks = {};
@@ -6092,7 +6366,11 @@ var Patcher = class {
6092
6366
  buildManifestPaths(addedFiles) {
6093
6367
  const paths = {};
6094
6368
  for (const filePath of addedFiles) {
6095
- if (filePath.endsWith(this.config.cssBefore.filename)) {
6369
+ if (this.config.skin && filePath.endsWith(`skin/${this.config.skin}.css`)) {
6370
+ paths.skinCss = filePath;
6371
+ } else if (this.config.skin && filePath.endsWith(`skin/${this.config.skin}.js`)) {
6372
+ paths.skinJs = filePath;
6373
+ } else if (filePath.endsWith(this.config.cssBefore.filename)) {
6096
6374
  paths.cssBefore = filePath;
6097
6375
  } else if (filePath.endsWith(this.config.cssAfter.filename)) {
6098
6376
  paths.cssAfter = filePath;
@@ -6176,6 +6454,22 @@ var Patcher = class {
6176
6454
  added.push(path);
6177
6455
  if (fetched.jsAfter) console.log(`[Patcher] Using fetched content for ${path}`);
6178
6456
  }
6457
+ if (this.config.skin) {
6458
+ const skinCssPath = `${basePath}skin/${this.config.skin}.css`;
6459
+ const skinCssContent = fetched.skinCss ?? `/* PA-Patcher: Skin CSS (${this.config.skin}) */
6460
+ `;
6461
+ zip.addFile(skinCssPath, Buffer.from(skinCssContent, "utf-8"));
6462
+ added.push(skinCssPath);
6463
+ if (fetched.skinCss) console.log(`[Patcher] Using fetched skin CSS for ${skinCssPath}`);
6464
+ else console.log(`[Patcher] Added placeholder skin CSS: ${skinCssPath}`);
6465
+ const skinJsPath = `${basePath}skin/${this.config.skin}.js`;
6466
+ const skinJsContent = fetched.skinJs ?? `// PA-Patcher: Skin JS (${this.config.skin})
6467
+ `;
6468
+ zip.addFile(skinJsPath, Buffer.from(skinJsContent, "utf-8"));
6469
+ added.push(skinJsPath);
6470
+ if (fetched.skinJs) console.log(`[Patcher] Using fetched skin JS for ${skinJsPath}`);
6471
+ else console.log(`[Patcher] Added placeholder skin JS: ${skinJsPath}`);
6472
+ }
6179
6473
  return added;
6180
6474
  }
6181
6475
  /**
@@ -6190,9 +6484,9 @@ var Patcher = class {
6190
6484
  * @param buffer - Input ZIP file as Buffer
6191
6485
  * @returns Patched ZIP file as Buffer
6192
6486
  */
6193
- async patchBuffer(buffer) {
6487
+ async patchBuffer(buffer, options) {
6194
6488
  console.log(`[Patcher] patchBuffer called with ${buffer.length} bytes`);
6195
- const { buffer: patchedBuffer, result } = await this.patch(buffer);
6489
+ const { buffer: patchedBuffer, result } = await this.patch(buffer, options);
6196
6490
  console.log(`[Patcher] Patch result:`, JSON.stringify(result, null, 2));
6197
6491
  console.log(`[Patcher] Returning ${patchedBuffer.length} bytes`);
6198
6492
  return patchedBuffer;