Browse Source

ref: move lyrics cache to own file

Sv443 1 năm trước cách đây
mục cha
commit
d95c425f09

+ 23 - 16
assets/translations/README.md

@@ -6,15 +6,15 @@ To submit or edit a translation, please follow [this guide](../../contributing.m
 ### Translation progress:
 | Locale | Translated keys | Based on |
 | ------ | --------------- | :------: |
-| [`en_US`](./en_US.json) | 158 (default locale) |  |
-| [`de_DE`](./de_DE.json) | 🚫 `123/158` (77.8%) | ─ |
-| [`en_UK`](./en_UK.json) | ✅ `158/158` (100.0%) | `en_US` |
-| [`es_ES`](./es_ES.json) | 🚫 `123/158` (77.8%) | ─ |
-| [`fr_FR`](./fr_FR.json) | 🚫 `123/158` (77.8%) | ─ |
-| [`hi_IN`](./hi_IN.json) | 🚫 `123/158` (77.8%) | ─ |
-| [`ja_JA`](./ja_JA.json) | 🚫 `123/158` (77.8%) | ─ |
-| [`pt_BR`](./pt_BR.json) | 🚫 `123/158` (77.8%) | ─ |
-| [`zh_CN`](./zh_CN.json) | 🚫 `123/158` (77.8%) | ─ |
+| [`en_US`](./en_US.json) | 159 (default locale) |  |
+| [`de_DE`](./de_DE.json) | 🚫 `123/159` (77.4%) | ─ |
+| [`en_UK`](./en_UK.json) | ✅ `159/159` (100.0%) | `en_US` |
+| [`es_ES`](./es_ES.json) | 🚫 `123/159` (77.4%) | ─ |
+| [`fr_FR`](./fr_FR.json) | 🚫 `123/159` (77.4%) | ─ |
+| [`hi_IN`](./hi_IN.json) | 🚫 `123/159` (77.4%) | ─ |
+| [`ja_JA`](./ja_JA.json) | 🚫 `123/159` (77.4%) | ─ |
+| [`pt_BR`](./pt_BR.json) | 🚫 `123/159` (77.4%) | ─ |
+| [`zh_CN`](./zh_CN.json) | 🚫 `123/159` (77.4%) | ─ |
 
 <br>
 
@@ -25,7 +25,7 @@ This means you need to manually check against the base translations for missing
 
 ### Missing keys:
 
-<details><summary><code>de_DE</code> - 35 missing keys <i>(click to show)</i></summary><br>
+<details><summary><code>de_DE</code> - 36 missing keys <i>(click to show)</i></summary><br>
 
 | Key | English text |
 | --- | ------------ |
@@ -43,6 +43,7 @@ This means you need to manually check against the base translations for missing
 | `close_and_ignore_until_reenabled` | `Close and ignore until re-enabled` |
 | `expand_release_notes` | `Click to expand the latest release notes` |
 | `collapse_release_notes` | `Click to collapse the latest release notes` |
+| `no_updates_found` | `No updates found.` |
 | `unit_entries-1` | `entry` |
 | `unit_entries-n` | `entries` |
 | `unit_days-1` | `day` |
@@ -67,7 +68,7 @@ This means you need to manually check against the base translations for missing
 
 <br></details>
 
-<details><summary><code>es_ES</code> - 35 missing keys <i>(click to show)</i></summary><br>
+<details><summary><code>es_ES</code> - 36 missing keys <i>(click to show)</i></summary><br>
 
 | Key | English text |
 | --- | ------------ |
@@ -85,6 +86,7 @@ This means you need to manually check against the base translations for missing
 | `close_and_ignore_until_reenabled` | `Close and ignore until re-enabled` |
 | `expand_release_notes` | `Click to expand the latest release notes` |
 | `collapse_release_notes` | `Click to collapse the latest release notes` |
+| `no_updates_found` | `No updates found.` |
 | `unit_entries-1` | `entry` |
 | `unit_entries-n` | `entries` |
 | `unit_days-1` | `day` |
@@ -109,7 +111,7 @@ This means you need to manually check against the base translations for missing
 
 <br></details>
 
-<details><summary><code>fr_FR</code> - 35 missing keys <i>(click to show)</i></summary><br>
+<details><summary><code>fr_FR</code> - 36 missing keys <i>(click to show)</i></summary><br>
 
 | Key | English text |
 | --- | ------------ |
@@ -127,6 +129,7 @@ This means you need to manually check against the base translations for missing
 | `close_and_ignore_until_reenabled` | `Close and ignore until re-enabled` |
 | `expand_release_notes` | `Click to expand the latest release notes` |
 | `collapse_release_notes` | `Click to collapse the latest release notes` |
+| `no_updates_found` | `No updates found.` |
 | `unit_entries-1` | `entry` |
 | `unit_entries-n` | `entries` |
 | `unit_days-1` | `day` |
@@ -151,7 +154,7 @@ This means you need to manually check against the base translations for missing
 
 <br></details>
 
-<details><summary><code>hi_IN</code> - 35 missing keys <i>(click to show)</i></summary><br>
+<details><summary><code>hi_IN</code> - 36 missing keys <i>(click to show)</i></summary><br>
 
 | Key | English text |
 | --- | ------------ |
@@ -169,6 +172,7 @@ This means you need to manually check against the base translations for missing
 | `close_and_ignore_until_reenabled` | `Close and ignore until re-enabled` |
 | `expand_release_notes` | `Click to expand the latest release notes` |
 | `collapse_release_notes` | `Click to collapse the latest release notes` |
+| `no_updates_found` | `No updates found.` |
 | `unit_entries-1` | `entry` |
 | `unit_entries-n` | `entries` |
 | `unit_days-1` | `day` |
@@ -193,7 +197,7 @@ This means you need to manually check against the base translations for missing
 
 <br></details>
 
-<details><summary><code>ja_JA</code> - 35 missing keys <i>(click to show)</i></summary><br>
+<details><summary><code>ja_JA</code> - 36 missing keys <i>(click to show)</i></summary><br>
 
 | Key | English text |
 | --- | ------------ |
@@ -211,6 +215,7 @@ This means you need to manually check against the base translations for missing
 | `close_and_ignore_until_reenabled` | `Close and ignore until re-enabled` |
 | `expand_release_notes` | `Click to expand the latest release notes` |
 | `collapse_release_notes` | `Click to collapse the latest release notes` |
+| `no_updates_found` | `No updates found.` |
 | `unit_entries-1` | `entry` |
 | `unit_entries-n` | `entries` |
 | `unit_days-1` | `day` |
@@ -235,7 +240,7 @@ This means you need to manually check against the base translations for missing
 
 <br></details>
 
-<details><summary><code>pt_BR</code> - 35 missing keys <i>(click to show)</i></summary><br>
+<details><summary><code>pt_BR</code> - 36 missing keys <i>(click to show)</i></summary><br>
 
 | Key | English text |
 | --- | ------------ |
@@ -253,6 +258,7 @@ This means you need to manually check against the base translations for missing
 | `close_and_ignore_until_reenabled` | `Close and ignore until re-enabled` |
 | `expand_release_notes` | `Click to expand the latest release notes` |
 | `collapse_release_notes` | `Click to collapse the latest release notes` |
+| `no_updates_found` | `No updates found.` |
 | `unit_entries-1` | `entry` |
 | `unit_entries-n` | `entries` |
 | `unit_days-1` | `day` |
@@ -277,7 +283,7 @@ This means you need to manually check against the base translations for missing
 
 <br></details>
 
-<details><summary><code>zh_CN</code> - 35 missing keys <i>(click to show)</i></summary><br>
+<details><summary><code>zh_CN</code> - 36 missing keys <i>(click to show)</i></summary><br>
 
 | Key | English text |
 | --- | ------------ |
@@ -295,6 +301,7 @@ This means you need to manually check against the base translations for missing
 | `close_and_ignore_until_reenabled` | `Close and ignore until re-enabled` |
 | `expand_release_notes` | `Click to expand the latest release notes` |
 | `collapse_release_notes` | `Click to collapse the latest release notes` |
+| `no_updates_found` | `No updates found.` |
 | `unit_entries-1` | `entry` |
 | `unit_entries-n` | `entries` |
 | `unit_days-1` | `day` |

+ 1 - 0
assets/translations/en_US.json

@@ -98,6 +98,7 @@
     "close_and_ignore_until_reenabled": "Close and ignore until re-enabled",
     "expand_release_notes": "Click to expand the latest release notes",
     "collapse_release_notes": "Click to collapse the latest release notes",
+    "no_updates_found": "No updates found.",
 
     "unit_entries-1": "entry",
     "unit_entries-n": "entries",

+ 5 - 138
src/features/lyrics.ts

@@ -1,148 +1,15 @@
-import { ConfigManager, autoPlural, clamp, compress, decompress, fetchAdvanced, insertAfter } from "@sv443-network/userutils";
+import { autoPlural, fetchAdvanced, insertAfter } from "@sv443-network/userutils";
 import Fuse from "fuse.js";
-import { constructUrlString, error, getResourceUrl, info, log, onSelectorOld, warn, t, tp, compressionSupported } from "../utils";
+import { constructUrlString, error, getResourceUrl, info, log, onSelectorOld, warn, t, tp } from "../utils";
 import { emitInterface } from "../interface";
-import { compressionFormat, mode, scriptInfo } from "../constants";
+import { mode, scriptInfo } from "../constants";
+import { getFeatures } from "../config";
+import { addLyricsCacheEntryPenalized, getLyricsCacheEntry } from "./lyricsCache";
 import type { LyricsCacheEntry } from "../types";
-import { getFeatures } from "src/config";
 
 /** Ratelimit budget timeframe in seconds - should reflect what's in geniURL's docs */
 const geniUrlRatelimitTimeframe = 30;
 
-//#MARKER new cache
-
-export type LyricsCache = {
-  cache: LyricsCacheEntry[];
-};
-
-let canCompress = true;
-
-const lyricsCacheMgr = new ConfigManager<LyricsCache>({
-  id: "bytm-lyrics-cache",
-  defaultConfig: {
-    cache: [],
-  },
-  formatVersion: 1,
-  encodeData: (data) => canCompress ? compress(data, compressionFormat, "string") : data,
-  decodeData: (data) => canCompress ? decompress(data, compressionFormat, "string") : data,
-});
-
-export async function initLyricsCache() {
-  canCompress = await compressionSupported();
-  const data = await lyricsCacheMgr.loadData();
-  log(`Loaded lyrics cache (${data.cache.length} entries):`, data);
-  emitInterface("bytm:lyricsCacheReady", data);
-  return data;
-}
-
-/**
- * Returns the cache entry for the passed artist and song, or undefined if it doesn't exist yet  
- * {@linkcode artist} and {@linkcode song} need to be sanitized first!
- * @param refreshEntry If true, the timestamp of the entry will be set to the current time
- */
-export function getLyricsCacheEntry(artist: string, song: string, refreshEntry = true) {
-  const { cache } = lyricsCacheMgr.getData();
-  const entry = cache.find(e => e.artist === artist && e.song === song);
-  if(entry && Date.now() - entry?.added > getFeatures().lyricsCacheTTL * 1000 * 60 * 60 * 24) {
-    deleteLyricsCacheEntry(artist, song);
-    return undefined;
-  }
-
-  // refresh timestamp of the entry by mutating cache
-  if(entry && refreshEntry)
-    updateLyricsCacheEntry(artist, song);
-  return entry;
-}
-
-function updateLyricsCacheEntry(artist: string, song: string) {
-  const { cache } = lyricsCacheMgr.getData();
-  const idx = cache.findIndex(e => e.artist === artist && e.song === song);
-  if(idx !== -1) {
-    const newEntry = cache.splice(idx, 1)[0]!;
-    newEntry.viewed = Date.now();
-    lyricsCacheMgr.setData({ cache: [ newEntry, ...cache ] });
-  }
-}
-
-function deleteLyricsCacheEntry(artist: string, song: string) {
-  const { cache } = lyricsCacheMgr.getData();
-  const idx = cache.findIndex(e => e.artist === artist && e.song === song);
-  if(idx !== -1) {
-    cache.splice(idx, 1);
-    lyricsCacheMgr.setData({ cache });
-  }
-}
-
-/** Clears the lyrics cache locally and deletes it from persistent storage - the window should be reloaded right after! */
-export function deleteLyricsCache() {
-  emitInterface("bytm:lyricsCacheCleared");
-  return lyricsCacheMgr.deleteConfig();
-}
-
-/** Clears the lyrics cache locally and clears it in persistent storage */
-export function clearLyricsCache() {
-  emitInterface("bytm:lyricsCacheCleared");
-  return lyricsCacheMgr.setData({ cache: [] });
-}
-
-/** Returns the full lyrics cache array */
-export function getLyricsCache() {
-  return lyricsCacheMgr.getData().cache;
-}
-
-/**
- * Adds the provided entry into the lyrics URL cache, synchronously to RAM and asynchronously to GM storage  
- * {@linkcode artist} and {@linkcode song} need to be sanitized first!
- */
-export function addLyricsCacheEntry(artist: string, song: string, url: string) {
-  const { cache } = lyricsCacheMgr.getData();
-  const entry = {
-    artist, song, url, viewed: Date.now(), added: Date.now(),
-  } satisfies LyricsCacheEntry;
-
-  cache.push(entry);
-  cache.sort((a, b) => b.viewed - a.viewed);
-
-  if(cache.length > getFeatures().lyricsCacheMaxSize)
-    cache.pop();
-
-  emitInterface("bytm:lyricsCacheEntryAdded", entry);
-
-  return lyricsCacheMgr.setData({ cache });
-}
-
-/**
- * Adds the provided entry into the lyrics URL cache, synchronously to RAM and asynchronously to GM storage  
- * Also adds a penalty to the viewed timestamp and added timestamp to decrease entry's lifespan in cache  
- *   
- * ⚠️ {@linkcode artist} and {@linkcode song} need to be sanitized first!
- * @param penaltyFr Fraction to remove from the timestamp values - has to be between 0 and 1 - default is 0 (no penalty) - (0.25 = only penalized by a quarter of the predefined max penalty)
- */
-export function addLyricsCacheEntryPenalized(artist: string, song: string, url: string, penaltyFr = 0) {
-  const { cache } = lyricsCacheMgr.getData();
-
-  penaltyFr = clamp(penaltyFr, 0, 1);
-
-  const viewedPenalty = 1000 * 60 * 60 * 24 * 5 * penaltyFr; // 5 days
-  const addedPenalty = 1000 * 60 * 60 * 24 * 15 * penaltyFr; // 15 days
-  const entry = {
-    artist,
-    song,
-    url,
-    viewed: Date.now() - viewedPenalty,
-    added: Date.now() - addedPenalty,
-  } satisfies LyricsCacheEntry;
-
-  cache.push(entry);
-  cache.sort((a, b) => b.viewed - a.viewed);
-  if(cache.length > getFeatures().lyricsCacheMaxSize)
-    cache.pop();
-
-  emitInterface("bytm:lyricsCacheEntryAdded", entry);
-
-  return lyricsCacheMgr.setData({ cache });
-}
-
 //#MARKER media control bar
 
 let currentSongTitle = "";

+ 138 - 0
src/features/lyricsCache.ts

@@ -0,0 +1,138 @@
+import { ConfigManager, clamp, compress, decompress } from "@sv443-network/userutils";
+import { compressionFormat } from "../constants";
+import { compressionSupported, log } from "../utils";
+import { emitInterface } from "../interface";
+import { getFeatures } from "../config";
+import type { LyricsCacheEntry } from "../types";
+
+export type LyricsCache = {
+  cache: LyricsCacheEntry[];
+};
+
+let canCompress = true;
+
+const lyricsCacheMgr = new ConfigManager<LyricsCache>({
+  id: "bytm-lyrics-cache",
+  defaultConfig: {
+    cache: [],
+  },
+  formatVersion: 1,
+  encodeData: (data) => canCompress ? compress(data, compressionFormat, "string") : data,
+  decodeData: (data) => canCompress ? decompress(data, compressionFormat, "string") : data,
+});
+
+export async function initLyricsCache() {
+  canCompress = await compressionSupported();
+  const data = await lyricsCacheMgr.loadData();
+  log(`Loaded lyrics cache (${data.cache.length} entries):`, data);
+  emitInterface("bytm:lyricsCacheReady", data);
+  return data;
+}
+
+/**
+ * Returns the cache entry for the passed artist and song, or undefined if it doesn't exist yet  
+ * {@linkcode artist} and {@linkcode song} need to be sanitized first!
+ * @param refreshEntry If true, the timestamp of the entry will be set to the current time
+ */
+export function getLyricsCacheEntry(artist: string, song: string, refreshEntry = true) {
+  const { cache } = lyricsCacheMgr.getData();
+  const entry = cache.find(e => e.artist === artist && e.song === song);
+  if(entry && Date.now() - entry?.added > getFeatures().lyricsCacheTTL * 1000 * 60 * 60 * 24) {
+    deleteLyricsCacheEntry(artist, song);
+    return undefined;
+  }
+
+  // refresh timestamp of the entry by mutating cache
+  if(entry && refreshEntry)
+    updateLyricsCacheEntry(artist, song);
+  return entry;
+}
+
+function updateLyricsCacheEntry(artist: string, song: string) {
+  const { cache } = lyricsCacheMgr.getData();
+  const idx = cache.findIndex(e => e.artist === artist && e.song === song);
+  if(idx !== -1) {
+    const newEntry = cache.splice(idx, 1)[0]!;
+    newEntry.viewed = Date.now();
+    lyricsCacheMgr.setData({ cache: [ newEntry, ...cache ] });
+  }
+}
+
+function deleteLyricsCacheEntry(artist: string, song: string) {
+  const { cache } = lyricsCacheMgr.getData();
+  const idx = cache.findIndex(e => e.artist === artist && e.song === song);
+  if(idx !== -1) {
+    cache.splice(idx, 1);
+    lyricsCacheMgr.setData({ cache });
+  }
+}
+
+/** Clears the lyrics cache locally and deletes it from persistent storage - the window should be reloaded right after! */
+export function deleteLyricsCache() {
+  emitInterface("bytm:lyricsCacheCleared");
+  return lyricsCacheMgr.deleteConfig();
+}
+
+/** Clears the lyrics cache locally and clears it in persistent storage */
+export function clearLyricsCache() {
+  emitInterface("bytm:lyricsCacheCleared");
+  return lyricsCacheMgr.setData({ cache: [] });
+}
+
+/** Returns the full lyrics cache array */
+export function getLyricsCache() {
+  return lyricsCacheMgr.getData().cache;
+}
+
+/**
+ * Adds the provided entry into the lyrics URL cache, synchronously to RAM and asynchronously to GM storage  
+ * {@linkcode artist} and {@linkcode song} need to be sanitized first!
+ */
+export function addLyricsCacheEntry(artist: string, song: string, url: string) {
+  const { cache } = lyricsCacheMgr.getData();
+  const entry = {
+    artist, song, url, viewed: Date.now(), added: Date.now(),
+  } satisfies LyricsCacheEntry;
+
+  cache.push(entry);
+  cache.sort((a, b) => b.viewed - a.viewed);
+
+  if(cache.length > getFeatures().lyricsCacheMaxSize)
+    cache.pop();
+
+  emitInterface("bytm:lyricsCacheEntryAdded", entry);
+
+  return lyricsCacheMgr.setData({ cache });
+}
+
+/**
+ * Adds the provided entry into the lyrics URL cache, synchronously to RAM and asynchronously to GM storage  
+ * Also adds a penalty to the viewed timestamp and added timestamp to decrease entry's lifespan in cache  
+ *   
+ * ⚠️ {@linkcode artist} and {@linkcode song} need to be sanitized first!
+ * @param penaltyFr Fraction to remove from the timestamp values - has to be between 0 and 1 - default is 0 (no penalty) - (0.25 = only penalized by a quarter of the predefined max penalty)
+ */
+export function addLyricsCacheEntryPenalized(artist: string, song: string, url: string, penaltyFr = 0) {
+  const { cache } = lyricsCacheMgr.getData();
+
+  penaltyFr = clamp(penaltyFr, 0, 1);
+
+  const viewedPenalty = 1000 * 60 * 60 * 24 * 5 * penaltyFr; // 5 days
+  const addedPenalty = 1000 * 60 * 60 * 24 * 15 * penaltyFr; // 15 days
+  const entry = {
+    artist,
+    song,
+    url,
+    viewed: Date.now() - viewedPenalty,
+    added: Date.now() - addedPenalty,
+  } satisfies LyricsCacheEntry;
+
+  cache.push(entry);
+  cache.sort((a, b) => b.viewed - a.viewed);
+  if(cache.length > getFeatures().lyricsCacheMaxSize)
+    cache.pop();
+
+  emitInterface("bytm:lyricsCacheEntryAdded", entry);
+
+  return lyricsCacheMgr.setData({ cache });
+}

+ 2 - 1
src/features/songLists.ts

@@ -2,7 +2,8 @@ import { autoPlural, openInNewTab, pauseFor } from "@sv443-network/userutils";
 import { clearInner, error, getResourceUrl, log, onSelectorOld, t, warn } from "../utils";
 import { SiteEventsMap, siteEvents } from "../siteEvents";
 import { emitInterface } from "../interface";
-import { fetchLyricsUrlTop, createLyricsBtn, sanitizeArtists, sanitizeSong, getLyricsCacheEntry, splitVideoTitle } from "./lyrics";
+import { fetchLyricsUrlTop, createLyricsBtn, sanitizeArtists, sanitizeSong, splitVideoTitle } from "./lyrics";
+import { getLyricsCacheEntry } from "./lyricsCache";
 import type { FeatureConfig, LyricsCacheEntry } from "../types";
 import "./songLists.css";
 

+ 12 - 17
src/index.ts

@@ -8,35 +8,30 @@ import { emitInterface, initInterface } from "./interface";
 import { addWelcomeMenu, showWelcomeMenu } from "./menu/welcomeMenu";
 import { initObservers, observers } from "./observers";
 import {
-  // other:
-  featInfo,
-
   // features:
+  featInfo,
   // layout
-  setLayoutConfig,
-  addWatermark,
+  setLayoutConfig, addWatermark,
   removeUpgradeTab, initVolumeFeatures,
   removeShareTrackingParam, fixSpacing,
   addScrollToActiveBtn,
   // song lists
-  setSongListsConfig,
-  initQueueButtons,
+  setSongListsConfig, initQueueButtons,
   // behavior
-  setBehaviorConfig,
-  initBeforeUnloadHook, disableBeforeUnload,
-  initAutoCloseToasts, initRememberSongTime,
-  disableDarkReader, enableLockVolume,
+  setBehaviorConfig, initBeforeUnloadHook,
+  disableBeforeUnload, initAutoCloseToasts,
+  initRememberSongTime, disableDarkReader,
+  enableLockVolume,
   // input
-  setInputConfig,
-  initArrowKeySkip, initSiteSwitch,
-  addAnchorImprovements, initNumKeysSkip,
+  setInputConfig, initArrowKeySkip,
+  initSiteSwitch, addAnchorImprovements,
+  initNumKeysSkip,
   // lyrics
   addMediaCtrlLyricsBtn,
   // menu
   addConfigMenuOption,
   // other
-  checkVersion,
-  initLyricsCache,
+  initVersionCheck, initLyricsCache,
 } from "./features/index";
 
 {
@@ -132,7 +127,7 @@ async function onDomLoad() {
   const features = getFeatures();
   const ftInit = [] as Promise<void>[];
 
-  await checkVersion();
+  await initVersionCheck();
 
   log(`DOM loaded. Initializing features for domain "${domain}"...`);
 

+ 1 - 2
src/interface.ts

@@ -3,8 +3,7 @@ import { mode, branch, host, buildNumber, compressionFormat, scriptInfo } from "
 import { getResourceUrl, getSessionId, getVideoTime, log, setLocale, getLocale, hasKey, hasKeyFor, NanoEmitter, t, tp, type TrLocale } from "./utils";
 import { addSelectorListener } from "./observers";
 import { getFeatures, saveFeatures } from "./config";
-import { featInfo } from "./features";
-import { fetchLyricsUrlTop, getLyricsCacheEntry, sanitizeArtists, sanitizeSong, type LyricsCache } from "./features/lyrics";
+import { featInfo, fetchLyricsUrlTop, getLyricsCacheEntry, sanitizeArtists, sanitizeSong, type LyricsCache } from "./features";
 import type { SiteEventsMap } from "./siteEvents";
 import type { FeatureConfig, FeatureInfo, LyricsCacheEntry } from "./types";
 import { BytmDialog, createHotkeyInput, createToggleInput } from "./components";

+ 2 - 0
src/types.ts

@@ -279,6 +279,8 @@ export interface FeatureConfig {
   locale: TrLocale;
   /** Whether to check for updates to the script */
   versionCheck: boolean;
+  /** Button to check for updates */
+  checkVersionNow: undefined;
   /** The console log level - 0 = Debug, 1 = Info */
   logLevel: LogLevel;
   /** Whether to show advanced settings in the config menu */