bc-deeplib 1.1.2 → 1.1.3

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/deeplib.js CHANGED
@@ -958,20 +958,34 @@ var GUI = class _GUI extends BaseModule {
958
958
  static {
959
959
  __name(this, "GUI");
960
960
  }
961
+ /** The singleton instance of the GUI controller. */
961
962
  static instance = null;
963
+ /** All subscreens managed by this GUI, including the main menu and module settings screens. */
962
964
  _subscreens;
965
+ /** The mod's main menu screen. */
963
966
  _mainMenu;
967
+ /** The currently active subscreen, or `null` if none is active. */
964
968
  _currentSubscreen = null;
969
+ /** Options defining how the mod's settings button is displayed and behaves. */
965
970
  _modButtonOptions;
971
+ /** Returns all registered subscreens. */
966
972
  get subscreens() {
967
973
  return this._subscreens;
968
974
  }
975
+ /** Returns the main menu subscreen instance. */
969
976
  get mainMenu() {
970
977
  return this._mainMenu;
971
978
  }
979
+ /** Returns the currently active subscreen. */
972
980
  get currentSubscreen() {
973
981
  return this._currentSubscreen;
974
982
  }
983
+ /**
984
+ * Sets the current subscreen.
985
+ * Accepts either a `BaseSubscreen` instance or the `name` of a subscreen.
986
+ *
987
+ * @throws If a string is provided but no subscreen with that name exists.
988
+ */
975
989
  set currentSubscreen(subscreen) {
976
990
  if (this._currentSubscreen) {
977
991
  this._currentSubscreen.unload();
@@ -988,7 +1002,11 @@ var GUI = class _GUI extends BaseModule {
988
1002
  this._currentSubscreen.resize(true);
989
1003
  }
990
1004
  }
991
- /** Creates the GUI instance and initializes the main menu. */
1005
+ /**
1006
+ * Creates the GUI instance and initializes the main menu.
1007
+ *
1008
+ * @throws If another `GUI` instance already exists.
1009
+ */
992
1010
  constructor(modButtonOptions) {
993
1011
  super();
994
1012
  if (_GUI.instance) {
@@ -1002,9 +1020,13 @@ var GUI = class _GUI extends BaseModule {
1002
1020
  this._modButtonOptions = modButtonOptions;
1003
1021
  _GUI.instance = this;
1004
1022
  }
1005
- get defaultSettings() {
1006
- return null;
1007
- }
1023
+ /**
1024
+ * Loads the GUI and registers the mod's settings button in the extensions menu.
1025
+ *
1026
+ * - Creates subscreens for each module's settings screen.
1027
+ * - Registers lifecycle callbacks for subscreens events.
1028
+ * - Sets up the main menu and its subscreens.
1029
+ */
1008
1030
  load() {
1009
1031
  for (const module of modules()) {
1010
1032
  if (!module.settingsScreen) continue;
@@ -1015,36 +1037,34 @@ var GUI = class _GUI extends BaseModule {
1015
1037
  Identifier: this._modButtonOptions.Identifier,
1016
1038
  ButtonText: this._modButtonOptions.ButtonText,
1017
1039
  Image: this._modButtonOptions.Image,
1018
- load: this._modButtonOptions.load || (() => {
1040
+ load: /* @__PURE__ */ __name(() => {
1019
1041
  setSubscreen(new MainMenu(this));
1020
- }),
1021
- run: this._modButtonOptions.run || (() => {
1042
+ }, "load"),
1043
+ run: /* @__PURE__ */ __name(() => {
1022
1044
  if (this._currentSubscreen) {
1023
- MainCanvas.textAlign = "left";
1024
1045
  this._currentSubscreen.run();
1025
- MainCanvas.textAlign = "center";
1026
1046
  const newCanvasPosition = [MainCanvas.canvas.offsetLeft, MainCanvas.canvas.offsetTop, MainCanvas.canvas.clientWidth, MainCanvas.canvas.clientHeight];
1027
1047
  if (!CommonArraysEqual(newCanvasPosition, DrawCanvasPosition)) {
1028
1048
  DrawCanvasPosition = newCanvasPosition;
1029
1049
  this._currentSubscreen.resize(false);
1030
1050
  }
1031
1051
  }
1032
- }),
1033
- click: this._modButtonOptions.click || (() => {
1052
+ }, "run"),
1053
+ click: /* @__PURE__ */ __name(() => {
1034
1054
  if (this._currentSubscreen) {
1035
1055
  this._currentSubscreen.click();
1036
1056
  }
1037
- }),
1038
- exit: this._modButtonOptions.exit || (() => {
1057
+ }, "click"),
1058
+ exit: /* @__PURE__ */ __name(() => {
1039
1059
  if (this._currentSubscreen) {
1040
1060
  this._currentSubscreen.exit();
1041
1061
  }
1042
- }),
1043
- unload: this._modButtonOptions.unload || (() => {
1062
+ }, "exit"),
1063
+ unload: /* @__PURE__ */ __name(() => {
1044
1064
  if (this._currentSubscreen) {
1045
1065
  this._currentSubscreen.unload();
1046
1066
  }
1047
- })
1067
+ }, "unload")
1048
1068
  });
1049
1069
  }
1050
1070
  };
@@ -1054,10 +1074,19 @@ var VersionModule = class _VersionModule extends BaseModule {
1054
1074
  static {
1055
1075
  __name(this, "VersionModule");
1056
1076
  }
1077
+ /** Whether the current session is running a new version compared to stored data */
1057
1078
  static isItNewVersion = false;
1079
+ /** The current mod version (retrieved from `ModSdkManager.ModInfo.version`) */
1058
1080
  static Version;
1081
+ /** Message to display when a new version is detected */
1059
1082
  static NewVersionMessage = "";
1083
+ /** List of registered migration handlers, sorted by version */
1060
1084
  static Migrators = [];
1085
+ /**
1086
+ * Initializes the module on load:
1087
+ * - Stores the current mod version.
1088
+ * - Hooks into `ChatRoomSync` to show a "new version" message when applicable.
1089
+ */
1061
1090
  load() {
1062
1091
  _VersionModule.Version = ModSdkManager.ModInfo.version;
1063
1092
  ModSdkManager.prototype.hookFunction(
@@ -1072,6 +1101,14 @@ var VersionModule = class _VersionModule extends BaseModule {
1072
1101
  "VersionModule"
1073
1102
  );
1074
1103
  }
1104
+ /**
1105
+ * Checks if the stored version differs from the current version.
1106
+ * If a new version is detected:
1107
+ * - Flags the session as updated.
1108
+ * - Runs applicable migrations.
1109
+ * - Updates stored version in player data.
1110
+ * - Saves `modStorage`.
1111
+ */
1075
1112
  static checkVersionUpdate() {
1076
1113
  const PreviousVersion = _VersionModule.loadVersion();
1077
1114
  const CurrentVersion = _VersionModule.Version;
@@ -1082,6 +1119,10 @@ var VersionModule = class _VersionModule extends BaseModule {
1082
1119
  }
1083
1120
  modStorage.save();
1084
1121
  }
1122
+ /**
1123
+ * Executes migrations for all registered migrators whose `MigrationVersion`
1124
+ * is newer than the previously stored version.
1125
+ */
1085
1126
  static checkVersionMigration() {
1086
1127
  const PreviousVersion = _VersionModule.loadVersion();
1087
1128
  for (const migrator of _VersionModule.Migrators) {
@@ -1091,16 +1132,27 @@ var VersionModule = class _VersionModule extends BaseModule {
1091
1132
  }
1092
1133
  }
1093
1134
  }
1135
+ /**
1136
+ * Registers a new migrator for handling version-specific changes.
1137
+ * Migrators are sorted by their `MigrationVersion` in ascending order.
1138
+ */
1094
1139
  static registerMigrator(migrator) {
1095
1140
  _VersionModule.Migrators.push(migrator);
1096
1141
  _VersionModule.Migrators.sort((a, b) => a.MigrationVersion.localeCompare(b.MigrationVersion));
1097
1142
  }
1143
+ /** Sets the message that will be displayed when a new version is detected. */
1098
1144
  static setNewVersionMessage(newVersionMessage) {
1099
1145
  _VersionModule.NewVersionMessage = newVersionMessage;
1100
1146
  }
1147
+ /** Sends the currently configured "new version" message to the local player. */
1101
1148
  static sendNewVersionMessage() {
1102
1149
  sendLocalMessage("deeplib-new-version", _VersionModule.NewVersionMessage);
1103
1150
  }
1151
+ /**
1152
+ * Determines if a given `candidate` version is newer than the `current` version.
1153
+ *
1154
+ * Version strings are expected in `MAJOR.MINOR.PATCH` format.
1155
+ */
1104
1156
  static isNewVersion(current, candidate) {
1105
1157
  if (current !== void 0) {
1106
1158
  const CURRENT_ = current.split("."), CANDIDATE_ = candidate.split(".");
@@ -1116,11 +1168,13 @@ var VersionModule = class _VersionModule extends BaseModule {
1116
1168
  }
1117
1169
  return false;
1118
1170
  }
1171
+ /** Saves the current mod version into persistent player storage. */
1119
1172
  static saveVersion() {
1120
1173
  if (modStorage.playerStorage) {
1121
1174
  Player[ModSdkManager.ModInfo.name].Version = _VersionModule.Version;
1122
1175
  }
1123
1176
  }
1177
+ /** Loads the stored mod version from persistent player storage. */
1124
1178
  static loadVersion() {
1125
1179
  return modStorage.playerStorage?.Version;
1126
1180
  }
@@ -1686,22 +1740,39 @@ var Modal = class _Modal {
1686
1740
  blocker;
1687
1741
  inputEl;
1688
1742
  timeoutId;
1743
+ /** Static modal queue. */
1689
1744
  static queue = [];
1745
+ /** Flag to indicate if a modal is currently being shown. */
1690
1746
  static processing = false;
1747
+ /**
1748
+ * Displays the modal and resolves with the chosen action and input value.
1749
+ */
1691
1750
  show() {
1692
1751
  return _Modal.enqueue(this);
1693
1752
  }
1753
+ /**
1754
+ * Shows a simple alert modal with a single "OK" button.
1755
+ */
1694
1756
  static async alert(msg, timeoutMs) {
1695
1757
  await new _Modal({ prompt: msg, buttons: [{ action: "close", text: "OK" }], timeoutMs }).show();
1696
1758
  }
1759
+ /**
1760
+ * Shows a confirmation modal with "Cancel" and "OK" buttons.
1761
+ * Returns true if "OK" is clicked.
1762
+ */
1697
1763
  static async confirm(msg) {
1698
1764
  const [action] = await new _Modal({ prompt: msg, buttons: [{ text: "Cancel", action: "cancel" }, { text: "OK", action: "ok" }] }).show();
1699
1765
  return action === "ok";
1700
1766
  }
1767
+ /**
1768
+ * Shows a prompt modal with an input field and "Submit"/"Cancel" buttons.
1769
+ * Returns the input value if submitted, otherwise null.
1770
+ */
1701
1771
  static async prompt(msg, defaultValue = "") {
1702
1772
  const [action, value] = await new _Modal({ prompt: msg, timeoutMs: 0, input: { type: "input", defaultValue }, buttons: [{ text: "Cancel", action: "cancel" }, { text: "Submit", action: "submit" }] }).show();
1703
1773
  return action === "submit" ? value : null;
1704
1774
  }
1775
+ /** Creates the input element for the modal, applying configuration and validation. */
1705
1776
  renderInput(cfg) {
1706
1777
  const el = document.createElement(cfg.type);
1707
1778
  el.classList.add("deeplib-modal-input");
@@ -1716,6 +1787,7 @@ var Modal = class _Modal {
1716
1787
  this.inputEl = el;
1717
1788
  return el;
1718
1789
  }
1790
+ /** Creates modal action buttons from configuration. */
1719
1791
  renderButtons() {
1720
1792
  const container = document.createElement("div");
1721
1793
  container.classList.add("deeplib-modal-button-container");
@@ -1731,6 +1803,7 @@ var Modal = class _Modal {
1731
1803
  });
1732
1804
  return container;
1733
1805
  }
1806
+ /** Creates the modal backdrop blocker with optional click-to-close behavior. */
1734
1807
  createBlocker() {
1735
1808
  const blocker = document.createElement("div");
1736
1809
  blocker.classList.add("deeplib-modal-blocker");
@@ -1739,6 +1812,7 @@ var Modal = class _Modal {
1739
1812
  blocker.addEventListener("click", () => this.close("close"));
1740
1813
  return blocker;
1741
1814
  }
1815
+ /** Implements a focus trap to keep keyboard navigation inside the modal. */
1742
1816
  setupFocusTrap() {
1743
1817
  const focusable = 'button, [href], input, textarea, select, [tabindex]:not([tabindex="-1"])';
1744
1818
  const elements = Array.from(this.dialog.querySelectorAll(focusable));
@@ -1770,6 +1844,7 @@ var Modal = class _Modal {
1770
1844
  (this.inputEl || first)?.focus();
1771
1845
  });
1772
1846
  }
1847
+ /** Closes the modal, cleans up DOM, resolves promise, and shows next queued modal. */
1773
1848
  close(action) {
1774
1849
  if (this.timeoutId) clearTimeout(this.timeoutId);
1775
1850
  this.dialog.close();
@@ -1802,7 +1877,6 @@ var Modal = class _Modal {
1802
1877
  }
1803
1878
  }
1804
1879
  };
1805
- window.Modal = Modal;
1806
1880
 
1807
1881
  // src/screens/main_menu.ts
1808
1882
  var MainMenu = class _MainMenu extends BaseSubscreen {
@@ -1994,6 +2068,7 @@ var GuiImportExport = class extends BaseSubscreen {
1994
2068
  resize() {
1995
2069
  super.resize();
1996
2070
  }
2071
+ /** Exports the mod data using the specified method. */
1997
2072
  async dataExport(transferMethod) {
1998
2073
  try {
1999
2074
  const data = LZString.compressToBase64(JSON.stringify(modStorage.playerStorage));
@@ -2009,6 +2084,7 @@ var GuiImportExport = class extends BaseSubscreen {
2009
2084
  deepLibLogger.error(`Data export failed for ${ModSdkManager.ModInfo.name}.`, error);
2010
2085
  }
2011
2086
  }
2087
+ /** Imports mod data using the specified method. */
2012
2088
  async dataImport(transferMethod) {
2013
2089
  try {
2014
2090
  let importedData = "";
@@ -2032,6 +2108,7 @@ var GuiImportExport = class extends BaseSubscreen {
2032
2108
  deepLibLogger.error(`Data import failed for ${ModSdkManager.ModInfo.name}.`, error);
2033
2109
  }
2034
2110
  }
2111
+ /** Saves data to a file using the browser's save dialog. */
2035
2112
  async exportToFile(data, defaultFileName) {
2036
2113
  const CUSTOM_EXTENSION = this.importExportOptions.customFileExtension.startsWith(".") ? this.importExportOptions.customFileExtension : "." + this.importExportOptions.customFileExtension;
2037
2114
  const suggestedName = defaultFileName.endsWith(CUSTOM_EXTENSION) ? defaultFileName : defaultFileName + CUSTOM_EXTENSION;
@@ -2071,6 +2148,7 @@ var GuiImportExport = class extends BaseSubscreen {
2071
2148
  URL.revokeObjectURL(link.href);
2072
2149
  }
2073
2150
  }
2151
+ /** Opens a file picker and reads the selected file's contents, importing the data. */
2074
2152
  async importFromFile() {
2075
2153
  const CUSTOM_EXTENSION = this.importExportOptions.customFileExtension.startsWith(".") ? this.importExportOptions.customFileExtension : "." + this.importExportOptions.customFileExtension;
2076
2154
  async function importFromFileInternal(file) {
@@ -2123,11 +2201,13 @@ var GuiImportExport = class extends BaseSubscreen {
2123
2201
  });
2124
2202
  }
2125
2203
  }
2204
+ /** Copies the given data to the clipboard. */
2126
2205
  async exportToClipboard(data) {
2127
2206
  return navigator.clipboard.writeText(data).catch((error) => {
2128
2207
  throw new Error("Failed to copy data to clipboard." + error);
2129
2208
  });
2130
2209
  }
2210
+ /** Prompts the user to enter data and returns it. */
2131
2211
  async importFromClipboard() {
2132
2212
  return Modal.prompt("Enter data to import").catch((error) => {
2133
2213
  throw new Error("Failed to read data from clipboard." + error);
@@ -2140,7 +2220,9 @@ var ModStorage = class _ModStorage {
2140
2220
  static {
2141
2221
  __name(this, "ModStorage");
2142
2222
  }
2223
+ /** Singleton instance of ModStorage */
2143
2224
  static _instance = null;
2225
+ /** The unique mod identifier used as key prefix in storage */
2144
2226
  modName;
2145
2227
  constructor(modName) {
2146
2228
  if (!_ModStorage._instance) {
@@ -2206,10 +2288,34 @@ var ModStorage = class _ModStorage {
2206
2288
 
2207
2289
  // src/utilities/elements/helpers.ts
2208
2290
  var domUtil = {
2291
+ /**
2292
+ * Automatically sets the position of the element based on the given position.
2293
+ * The position can be either a [x, y] tuple or a function returning such a tuple.
2294
+ * If both x and y are defined, the element's position is updated accordingly.
2295
+ */
2209
2296
  autoSetPosition,
2297
+ /**
2298
+ * Automatically sets the size of the element based on the given size.
2299
+ * The size can be either a [width, height] tuple or a function returning such a tuple.
2300
+ * If both width and height are defined, the element's size is updated accordingly.
2301
+ */
2210
2302
  autoSetSize,
2303
+ /**
2304
+ * Hides the element by setting its CSS display property to 'none'.
2305
+ * If the element cannot be found, the function does nothing.
2306
+ */
2211
2307
  hide,
2308
+ /**
2309
+ * Unhides the element by clearing its CSS display property (sets it to '').
2310
+ * If the element cannot be found, the function does nothing.
2311
+ */
2212
2312
  unhide,
2313
+ /**
2314
+ * Checks if the element has overflow content.
2315
+ * Returns an object indicating if there is any overflow,
2316
+ * and specifically if there is vertical or horizontal overflow.
2317
+ * Returns null if the element is not found.
2318
+ */
2213
2319
  hasOverflow
2214
2320
  };
2215
2321
  function autoSetPosition(_, position) {
@@ -2487,10 +2593,12 @@ var ModSdkManager = class _ModSdkManager {
2487
2593
  static SDK;
2488
2594
  static patchedFunctions = /* @__PURE__ */ new Map();
2489
2595
  static ModInfo;
2596
+ /** Registers a mod with the SDK and stores mod information. */
2490
2597
  constructor(info, options) {
2491
2598
  _ModSdkManager.SDK = bcModSdkRef.registerMod(info, options);
2492
2599
  _ModSdkManager.ModInfo = info;
2493
2600
  }
2601
+ /** Retrieves or initializes patch data for a given target function. */
2494
2602
  initPatchableFunction(target) {
2495
2603
  let result = _ModSdkManager.patchedFunctions.get(target);
2496
2604
  if (!result) {
@@ -2502,6 +2610,11 @@ var ModSdkManager = class _ModSdkManager {
2502
2610
  }
2503
2611
  return result;
2504
2612
  }
2613
+ /**
2614
+ * Hooks a function with a callback at a given priority.
2615
+ *
2616
+ * Prevents duplicate hooks.
2617
+ */
2505
2618
  hookFunction(target, priority, hook, module = null) {
2506
2619
  const data = this.initPatchableFunction(target);
2507
2620
  if (data.hooks.some((h) => h.hook === hook)) {
@@ -2517,12 +2630,23 @@ var ModSdkManager = class _ModSdkManager {
2517
2630
  data.hooks.sort((a, b) => b.priority - a.priority);
2518
2631
  return removeCallback;
2519
2632
  }
2633
+ /**
2634
+ * Applies patches to a target function.
2635
+ *
2636
+ * **This method is DANGEROUS** to use and has high potential to conflict with other mods.
2637
+ */
2520
2638
  patchFunction(target, patches) {
2521
2639
  _ModSdkManager.SDK?.patchFunction(target, patches);
2522
2640
  }
2641
+ /**
2642
+ * Removes all patches from a target function.
2643
+ */
2523
2644
  unpatchFunction(target) {
2524
2645
  _ModSdkManager.SDK?.removePatches(target);
2525
2646
  }
2647
+ /**
2648
+ * Removes all hooks associated with a specific module from a target function.
2649
+ */
2526
2650
  removeHookByModule(target, module) {
2527
2651
  const data = this.initPatchableFunction(target);
2528
2652
  for (let i = data.hooks.length - 1; i >= 0; i--) {
@@ -2533,6 +2657,9 @@ var ModSdkManager = class _ModSdkManager {
2533
2657
  }
2534
2658
  return true;
2535
2659
  }
2660
+ /**
2661
+ * Removes all hooks associated with a specific module across all patched functions.
2662
+ */
2536
2663
  removeAllHooksByModule(module) {
2537
2664
  for (const data of _ModSdkManager.patchedFunctions.values()) {
2538
2665
  for (let i = data.hooks.length - 1; i >= 0; i--) {
@@ -2548,6 +2675,10 @@ var ModSdkManager = class _ModSdkManager {
2548
2675
 
2549
2676
  // src/utilities/style.ts
2550
2677
  var Style = {
2678
+ /**
2679
+ * Injects a CSS style block directly into the document head using a <style> tag.
2680
+ * If a style element with the same `styleId` already exists, it won't inject again.
2681
+ */
2551
2682
  injectInline(styleId, styleSource) {
2552
2683
  const isStyleLoaded = document.getElementById(styleId);
2553
2684
  if (isStyleLoaded) return;
@@ -2556,6 +2687,10 @@ var Style = {
2556
2687
  styleElement.appendChild(document.createTextNode(styleSource));
2557
2688
  document.head.appendChild(styleElement);
2558
2689
  },
2690
+ /**
2691
+ * Injects a CSS stylesheet link into the document head using a <link> tag.
2692
+ * If a link element with the same `styleId` already exists, it won't inject again.
2693
+ */
2559
2694
  injectEmbed(styleId, styleLink) {
2560
2695
  const isStyleLoaded = document.getElementById(styleId);
2561
2696
  if (isStyleLoaded) return;
@@ -2565,15 +2700,24 @@ var Style = {
2565
2700
  styleElement.href = styleLink;
2566
2701
  document.head.appendChild(styleElement);
2567
2702
  },
2703
+ /**
2704
+ * Removes a style element from the document head by its ID.
2705
+ * Does nothing if the element is not found.
2706
+ */
2568
2707
  eject(id) {
2569
2708
  const style = document.getElementById(id);
2570
2709
  if (!style) return;
2571
2710
  style.remove();
2572
2711
  },
2712
+ /**
2713
+ * Reloads an inline style by removing the existing style element (if any)
2714
+ * and injecting the new styles inline again.
2715
+ */
2573
2716
  reload(styleId, styleSource) {
2574
2717
  Style.eject(styleId);
2575
2718
  Style.injectInline(styleId, styleSource);
2576
2719
  },
2720
+ /** Fetches the text content of a stylesheet or any resource at the given link. */
2577
2721
  async fetch(link) {
2578
2722
  return fetch(link).then((res) => res.text());
2579
2723
  }
@@ -2589,7 +2733,9 @@ var Localization = class _Localization {
2589
2733
  static PathToModTranslation;
2590
2734
  static PathToLibTranslation = `${PUBLIC_URL}/dl_translations/`;
2591
2735
  static DefaultLanguage = "en";
2736
+ /** Flag to prevent re-initialization */
2592
2737
  static initialized = false;
2738
+ /** Initialize the localization system by loading translation files. */
2593
2739
  static async init(initOptions) {
2594
2740
  if (_Localization.initialized) return;
2595
2741
  _Localization.initialized = true;
@@ -2615,12 +2761,18 @@ var Localization = class _Localization {
2615
2761
  _Localization.ModTranslation = { ...fallbackTranslation, ...modTranslation };
2616
2762
  }
2617
2763
  }
2764
+ /** Get a translated string from mod translations by source tag. */
2618
2765
  static getTextMod(srcTag) {
2619
2766
  return _Localization.ModTranslation?.[srcTag] || void 0;
2620
2767
  }
2768
+ /** Get a translated string from library translations by source tag. */
2621
2769
  static getTextLib(srcTag) {
2622
2770
  return _Localization.LibTranslation?.[srcTag] || void 0;
2623
2771
  }
2772
+ /**
2773
+ * Fetch and parse a language file from the given base URL and language code.
2774
+ * Falls back to default language if the requested language file is unavailable.
2775
+ */
2624
2776
  static async fetchLanguageFile(baseUrl, lang) {
2625
2777
  const response = await fetch(`${baseUrl}${lang}.lang`);
2626
2778
  if (lang !== _Localization.DefaultLanguage && !response.ok) {
@@ -2632,6 +2784,10 @@ var Localization = class _Localization {
2632
2784
  const langFileContent = await response.text();
2633
2785
  return this.parseLanguageFile(langFileContent);
2634
2786
  }
2787
+ /**
2788
+ * Parse the raw content of a language file into a TranslationDict.
2789
+ * Ignores empty lines and comments starting with '#'.
2790
+ */
2635
2791
  static parseLanguageFile(content) {
2636
2792
  const translations = {};
2637
2793
  const lines = content.split("\n");