Quellcode durchsuchen

feat: more advanced mode stuff

Sv443 vor 1 Jahr
Ursprung
Commit
7f1f7cc669

+ 4 - 0
assets/icons/add_circle_small.svg

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg height="24" viewBox="0 -960 960 960" width="24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
+  <path fill="#ffffff" d="m 456.54655,-331.45728 h 46.9069 v -125.08927 h 125.08927 v -46.9069 H 503.45345 v -125.08927 h -46.9069 v 125.08927 H 331.45728 v 46.9069 h 125.08927 z m 23.50583,148.54349 q -61.63461,0 -115.86705,-23.39169 -54.23324,-23.39169 -94.35014,-63.49062 -40.11769,-40.0997 -63.51955,-94.3087 -23.40185,-54.20821 -23.40185,-115.84282 0,-61.63461 23.39169,-115.86705 23.39169,-54.23324 63.49062,-94.35014 40.0997,-40.11769 94.3087,-63.51955 54.20821,-23.40185 115.84282,-23.40185 61.63461,0 115.86705,23.39169 54.23324,23.39169 94.35014,63.49062 40.11769,40.0997 63.51955,94.3087 23.40185,54.20821 23.40185,115.84282 0,61.63461 -23.39169,115.86705 -23.39169,54.23324 -63.49062,94.35014 -40.0997,40.11769 -94.3087,63.51955 -54.20821,23.40185 -115.84282,23.40185 z M 480,-229.82148 q 104.76226,0 177.47039,-72.70813 72.70813,-72.70813 72.70813,-177.47039 0,-104.76226 -72.70813,-177.47039 Q 584.76226,-730.17852 480,-730.17852 q -104.76226,0 -177.47039,72.70813 -72.70813,72.70813 -72.70813,177.47039 0,104.76226 72.70813,177.47039 72.70813,72.70813 177.47039,72.70813 z M 480,-480 Z" style="stroke-width:0.781808" />
+</svg>

+ 1 - 0
assets/resources.json

@@ -7,6 +7,7 @@
   "img-lyrics": "icons/lyrics.svg",
   "img-skip_to": "icons/skip_to.svg",
   "img-spinner": "icons/spinner.svg",
+  "img-add_circle_small": "icons/add_circle_small.svg",
   "img-logo": "logo/logo_48.png",
   "img-close": "icons/close.png",
   "img-discord": "external/discord.png",

+ 30 - 23
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) | 151 (default locale) |  |
-| [`de_DE`](./de_DE.json) | 🚫 `125/151` (82.8%) | ─ |
-| [`en_UK`](./en_UK.json) | ✅ `151/151` (100.0%) | `en_US` |
-| [`es_ES`](./es_ES.json) | 🚫 `125/151` (82.8%) | ─ |
-| [`fr_FR`](./fr_FR.json) | 🚫 `125/151` (82.8%) | ─ |
-| [`hi_IN`](./hi_IN.json) | 🚫 `125/151` (82.8%) | ─ |
-| [`ja_JA`](./ja_JA.json) | 🚫 `125/151` (82.8%) | ─ |
-| [`pt_BR`](./pt_BR.json) | 🚫 `125/151` (82.8%) | ─ |
-| [`zh_CN`](./zh_CN.json) | 🚫 `125/151` (82.8%) | ─ |
+| [`en_US`](./en_US.json) | 152 (default locale) |  |
+| [`de_DE`](./de_DE.json) | 🚫 `125/152` (82.2%) | ─ |
+| [`en_UK`](./en_UK.json) | ✅ `152/152` (100.0%) | `en_US` |
+| [`es_ES`](./es_ES.json) | 🚫 `125/152` (82.2%) | ─ |
+| [`fr_FR`](./fr_FR.json) | 🚫 `125/152` (82.2%) | ─ |
+| [`hi_IN`](./hi_IN.json) | 🚫 `125/152` (82.2%) | ─ |
+| [`ja_JA`](./ja_JA.json) | 🚫 `125/152` (82.2%) | ─ |
+| [`pt_BR`](./pt_BR.json) | 🚫 `125/152` (82.2%) | ─ |
+| [`zh_CN`](./zh_CN.json) | 🚫 `125/152` (82.2%) | ─ |
 
 <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> - 26 missing keys <i>(click to show)</i></summary><br>
+<details><summary><code>de_DE</code> - 27 missing keys <i>(click to show)</i></summary><br>
 
 | Key | English text |
 | --- | ------------ |
@@ -45,7 +45,8 @@ This means you need to manually check against the base translations for missing
 | `unit_entries-n` | `entries` |
 | `unit_days-1` | `day` |
 | `unit_days-n` | `days` |
-| `advanced_feature_desc_template` | `[Advanced] %1` |
+| `feature_desc_geniUrlBase` | `Base URL of your geniURL instance, see https://github.com/Sv443/geniURL` |
+| `feature_helptext_geniUrlBase` | `If you have your own instance of geniURL running (for example to bypass rate limiting), you can enter its base URL here to use it for the genius.com lyrics button.\nIf you don't know what this is, you can leave this option as is.` |
 | `feature_desc_lyricsCacheMaxSize` | `Maximum amount of lyrics to keep in the cache` |
 | `feature_helptext_lyricsCacheMaxSize` | `The lyrics of songs you listen to are stored in a cache to reduce the amount of requests to the lyrics provider.\nThis feature allows you to set the maximum amount of lyrics to keep in the cache.\nWhen the limit is reached, the entry that was used last will be removed to make space for any new ones.` |
 | `feature_desc_lyricsCacheTTL` | `Max amount of days to keep a lyrics entry in the cache` |
@@ -58,7 +59,7 @@ This means you need to manually check against the base translations for missing
 
 <br></details>
 
-<details><summary><code>es_ES</code> - 26 missing keys <i>(click to show)</i></summary><br>
+<details><summary><code>es_ES</code> - 27 missing keys <i>(click to show)</i></summary><br>
 
 | Key | English text |
 | --- | ------------ |
@@ -78,7 +79,8 @@ This means you need to manually check against the base translations for missing
 | `unit_entries-n` | `entries` |
 | `unit_days-1` | `day` |
 | `unit_days-n` | `days` |
-| `advanced_feature_desc_template` | `[Advanced] %1` |
+| `feature_desc_geniUrlBase` | `Base URL of your geniURL instance, see https://github.com/Sv443/geniURL` |
+| `feature_helptext_geniUrlBase` | `If you have your own instance of geniURL running (for example to bypass rate limiting), you can enter its base URL here to use it for the genius.com lyrics button.\nIf you don't know what this is, you can leave this option as is.` |
 | `feature_desc_lyricsCacheMaxSize` | `Maximum amount of lyrics to keep in the cache` |
 | `feature_helptext_lyricsCacheMaxSize` | `The lyrics of songs you listen to are stored in a cache to reduce the amount of requests to the lyrics provider.\nThis feature allows you to set the maximum amount of lyrics to keep in the cache.\nWhen the limit is reached, the entry that was used last will be removed to make space for any new ones.` |
 | `feature_desc_lyricsCacheTTL` | `Max amount of days to keep a lyrics entry in the cache` |
@@ -91,7 +93,7 @@ This means you need to manually check against the base translations for missing
 
 <br></details>
 
-<details><summary><code>fr_FR</code> - 26 missing keys <i>(click to show)</i></summary><br>
+<details><summary><code>fr_FR</code> - 27 missing keys <i>(click to show)</i></summary><br>
 
 | Key | English text |
 | --- | ------------ |
@@ -111,7 +113,8 @@ This means you need to manually check against the base translations for missing
 | `unit_entries-n` | `entries` |
 | `unit_days-1` | `day` |
 | `unit_days-n` | `days` |
-| `advanced_feature_desc_template` | `[Advanced] %1` |
+| `feature_desc_geniUrlBase` | `Base URL of your geniURL instance, see https://github.com/Sv443/geniURL` |
+| `feature_helptext_geniUrlBase` | `If you have your own instance of geniURL running (for example to bypass rate limiting), you can enter its base URL here to use it for the genius.com lyrics button.\nIf you don't know what this is, you can leave this option as is.` |
 | `feature_desc_lyricsCacheMaxSize` | `Maximum amount of lyrics to keep in the cache` |
 | `feature_helptext_lyricsCacheMaxSize` | `The lyrics of songs you listen to are stored in a cache to reduce the amount of requests to the lyrics provider.\nThis feature allows you to set the maximum amount of lyrics to keep in the cache.\nWhen the limit is reached, the entry that was used last will be removed to make space for any new ones.` |
 | `feature_desc_lyricsCacheTTL` | `Max amount of days to keep a lyrics entry in the cache` |
@@ -124,7 +127,7 @@ This means you need to manually check against the base translations for missing
 
 <br></details>
 
-<details><summary><code>hi_IN</code> - 26 missing keys <i>(click to show)</i></summary><br>
+<details><summary><code>hi_IN</code> - 27 missing keys <i>(click to show)</i></summary><br>
 
 | Key | English text |
 | --- | ------------ |
@@ -144,7 +147,8 @@ This means you need to manually check against the base translations for missing
 | `unit_entries-n` | `entries` |
 | `unit_days-1` | `day` |
 | `unit_days-n` | `days` |
-| `advanced_feature_desc_template` | `[Advanced] %1` |
+| `feature_desc_geniUrlBase` | `Base URL of your geniURL instance, see https://github.com/Sv443/geniURL` |
+| `feature_helptext_geniUrlBase` | `If you have your own instance of geniURL running (for example to bypass rate limiting), you can enter its base URL here to use it for the genius.com lyrics button.\nIf you don't know what this is, you can leave this option as is.` |
 | `feature_desc_lyricsCacheMaxSize` | `Maximum amount of lyrics to keep in the cache` |
 | `feature_helptext_lyricsCacheMaxSize` | `The lyrics of songs you listen to are stored in a cache to reduce the amount of requests to the lyrics provider.\nThis feature allows you to set the maximum amount of lyrics to keep in the cache.\nWhen the limit is reached, the entry that was used last will be removed to make space for any new ones.` |
 | `feature_desc_lyricsCacheTTL` | `Max amount of days to keep a lyrics entry in the cache` |
@@ -157,7 +161,7 @@ This means you need to manually check against the base translations for missing
 
 <br></details>
 
-<details><summary><code>ja_JA</code> - 26 missing keys <i>(click to show)</i></summary><br>
+<details><summary><code>ja_JA</code> - 27 missing keys <i>(click to show)</i></summary><br>
 
 | Key | English text |
 | --- | ------------ |
@@ -177,7 +181,8 @@ This means you need to manually check against the base translations for missing
 | `unit_entries-n` | `entries` |
 | `unit_days-1` | `day` |
 | `unit_days-n` | `days` |
-| `advanced_feature_desc_template` | `[Advanced] %1` |
+| `feature_desc_geniUrlBase` | `Base URL of your geniURL instance, see https://github.com/Sv443/geniURL` |
+| `feature_helptext_geniUrlBase` | `If you have your own instance of geniURL running (for example to bypass rate limiting), you can enter its base URL here to use it for the genius.com lyrics button.\nIf you don't know what this is, you can leave this option as is.` |
 | `feature_desc_lyricsCacheMaxSize` | `Maximum amount of lyrics to keep in the cache` |
 | `feature_helptext_lyricsCacheMaxSize` | `The lyrics of songs you listen to are stored in a cache to reduce the amount of requests to the lyrics provider.\nThis feature allows you to set the maximum amount of lyrics to keep in the cache.\nWhen the limit is reached, the entry that was used last will be removed to make space for any new ones.` |
 | `feature_desc_lyricsCacheTTL` | `Max amount of days to keep a lyrics entry in the cache` |
@@ -190,7 +195,7 @@ This means you need to manually check against the base translations for missing
 
 <br></details>
 
-<details><summary><code>pt_BR</code> - 26 missing keys <i>(click to show)</i></summary><br>
+<details><summary><code>pt_BR</code> - 27 missing keys <i>(click to show)</i></summary><br>
 
 | Key | English text |
 | --- | ------------ |
@@ -210,7 +215,8 @@ This means you need to manually check against the base translations for missing
 | `unit_entries-n` | `entries` |
 | `unit_days-1` | `day` |
 | `unit_days-n` | `days` |
-| `advanced_feature_desc_template` | `[Advanced] %1` |
+| `feature_desc_geniUrlBase` | `Base URL of your geniURL instance, see https://github.com/Sv443/geniURL` |
+| `feature_helptext_geniUrlBase` | `If you have your own instance of geniURL running (for example to bypass rate limiting), you can enter its base URL here to use it for the genius.com lyrics button.\nIf you don't know what this is, you can leave this option as is.` |
 | `feature_desc_lyricsCacheMaxSize` | `Maximum amount of lyrics to keep in the cache` |
 | `feature_helptext_lyricsCacheMaxSize` | `The lyrics of songs you listen to are stored in a cache to reduce the amount of requests to the lyrics provider.\nThis feature allows you to set the maximum amount of lyrics to keep in the cache.\nWhen the limit is reached, the entry that was used last will be removed to make space for any new ones.` |
 | `feature_desc_lyricsCacheTTL` | `Max amount of days to keep a lyrics entry in the cache` |
@@ -223,7 +229,7 @@ This means you need to manually check against the base translations for missing
 
 <br></details>
 
-<details><summary><code>zh_CN</code> - 26 missing keys <i>(click to show)</i></summary><br>
+<details><summary><code>zh_CN</code> - 27 missing keys <i>(click to show)</i></summary><br>
 
 | Key | English text |
 | --- | ------------ |
@@ -243,7 +249,8 @@ This means you need to manually check against the base translations for missing
 | `unit_entries-n` | `entries` |
 | `unit_days-1` | `day` |
 | `unit_days-n` | `days` |
-| `advanced_feature_desc_template` | `[Advanced] %1` |
+| `feature_desc_geniUrlBase` | `Base URL of your geniURL instance, see https://github.com/Sv443/geniURL` |
+| `feature_helptext_geniUrlBase` | `If you have your own instance of geniURL running (for example to bypass rate limiting), you can enter its base URL here to use it for the genius.com lyrics button.\nIf you don't know what this is, you can leave this option as is.` |
 | `feature_desc_lyricsCacheMaxSize` | `Maximum amount of lyrics to keep in the cache` |
 | `feature_helptext_lyricsCacheMaxSize` | `The lyrics of songs you listen to are stored in a cache to reduce the amount of requests to the lyrics provider.\nThis feature allows you to set the maximum amount of lyrics to keep in the cache.\nWhen the limit is reached, the entry that was used last will be removed to make space for any new ones.` |
 | `feature_desc_lyricsCacheTTL` | `Max amount of days to keep a lyrics entry in the cache` |

+ 2 - 2
assets/translations/en_US.json

@@ -104,8 +104,6 @@
     "unit_days-1": "day",
     "unit_days-n": "days",
 
-    "advanced_feature_desc_template": "[Advanced] %1",
-
     "feature_category_layout": "Layout",
     "feature_category_songLists": "Song Lists",
     "feature_category_behavior": "Behavior",
@@ -152,6 +150,8 @@
     "feature_helptext_anchorImprovements": "Some elements on the page are only clickable with the left mouse button, which means you can't open them in a new tab by middle-clicking or through the context menu using shift + right-click. This feature adds links to a lot of them or enlarges existing ones to make clicking easier.",
 
     "feature_desc_geniusLyrics": "Add a button to the media controls of the currently playing song to open its lyrics on genius.com",
+    "feature_desc_geniUrlBase": "Base URL of your geniURL instance, see https://github.com/Sv443/geniURL",
+    "feature_helptext_geniUrlBase": "If you have your own instance of geniURL running (for example to bypass rate limiting), you can enter its base URL here to use it for the genius.com lyrics button.\nIf you don't know what this is, you can leave this option as is.",
     "feature_desc_lyricsCacheMaxSize": "Maximum amount of lyrics to keep in the cache",
     "feature_helptext_lyricsCacheMaxSize": "The lyrics of songs you listen to are stored in a cache to reduce the amount of requests to the lyrics provider.\nThis feature allows you to set the maximum amount of lyrics to keep in the cache.\nWhen the limit is reached, the entry that was used last will be removed to make space for any new ones.",
     "feature_desc_lyricsCacheTTL": "Max amount of days to keep a lyrics entry in the cache",

+ 8 - 2
src/components/BytmDialog.css

@@ -265,6 +265,10 @@
   width: 240px;
 }
 
+.bytm-ftconf-input[type=text] {
+  width: 240px;
+}
+
 .bytm-ftconf-input[type=checkbox] {
   margin-left: 5px;
 }
@@ -385,9 +389,11 @@ hr {
   display: inline-flex;
   justify-content: flex-start;
   align-items: center;
-  margin-left: 8px;
+  margin-right: 6px;
 }
 
-#bytm-ftitem-locale-adornment svg path {
+.bytm-ftitem-adornment svg path,
+.advancedModeIcon svg path
+{
   fill: var(--bytm-dialog-accent-col, #4595c7);
 }

+ 2 - 2
src/components/toggleInput.css

@@ -1,6 +1,6 @@
 .bytm-toggle-input-wrapper {
-  --toggle-height: 24px;
-  --toggle-width: 48px;
+  --toggle-height: 20px;
+  --toggle-width: 40px;
   --toggle-knob-offset: 4px;
   --toggle-color-on: var(--bytm-dialog-accent-col, #4595c7);
   --toggle-color-off: #707070;

+ 1 - 0
src/config.ts

@@ -56,6 +56,7 @@ export const migrations: ConfigMigrationsDict = {
       lyricsCacheTTL: getFeatureDefault("lyricsCacheTTL"),
       clearLyricsCache: undefined,
       advancedMode: getFeatureDefault("advancedMode"),
+      geniUrlBase: getFeatureDefault("geniUrlBase"),
     };
   },
 };

+ 9 - 0
src/dialogs/dialogs.css

@@ -66,3 +66,12 @@
   padding-left: 12px;
   font-size: 1.3rem;
 }
+
+.advanced-mode-icon {
+  display: inline-flex;
+  align-items: center;
+}
+
+.advanced-mode-icon svg path {
+  fill: #c5a73b;
+}

+ 18 - 0
src/features/index.ts

@@ -3,6 +3,7 @@ import langMapping from "../../assets/locales.json" assert { type: "json" };
 import { remSongMinPlayTime } from "./behavior";
 import { clearLyricsCache, getLyricsCache } from "./lyrics";
 import { FeatureInfo } from "../types";
+import { getFeatures } from "src/config";
 
 export * from "./layout";
 export * from "./behavior";
@@ -254,6 +255,15 @@ export const featInfo = {
     enable: () => void "TODO",
     disable: () => void "TODO",
   },
+  geniUrlBase: {
+    type: "text",
+    category: "lyrics",
+    default: "https://api.sv443.net/geniurl",
+    normalize: (val: string) => val.trim().replace(/\/+$/, ""),
+    advanced: true,
+    // TODO: to be reworked or removed in the big menu rework
+    textAdornment: async () => `<span class="advanced-mode-icon">${await resourceToHTMLString("img-add_circle_small") ?? ""}</span>`,
+  },
   lyricsCacheMaxSize: {
     type: "slider",
     category: "lyrics",
@@ -265,6 +275,8 @@ export const featInfo = {
     enable: () => void "TODO",
     change: () => void "TODO",
     advanced: true,
+    // TODO: to be reworked or removed in the big menu rework
+    textAdornment: async () => `<span class="advanced-mode-icon">${await resourceToHTMLString("img-add_circle_small") ?? ""}</span>`,
   },
   lyricsCacheTTL: {
     type: "slider",
@@ -277,6 +289,8 @@ export const featInfo = {
     enable: () => void "TODO",
     change: () => void "TODO",
     advanced: true,
+    // TODO: to be reworked or removed in the big menu rework
+    textAdornment: async () => `<span class="advanced-mode-icon">${await resourceToHTMLString("img-add_circle_small") ?? ""}</span>`,
   },
   clearLyricsCache: {
     type: "button",
@@ -290,6 +304,8 @@ export const featInfo = {
       }
     },
     advanced: true,
+    // TODO: to be reworked or removed in the big menu rework
+    textAdornment: async () => `<span class="advanced-mode-icon">${await resourceToHTMLString("img-add_circle_small") ?? ""}</span>`,
   },
 
   //#SECTION general
@@ -325,5 +341,7 @@ export const featInfo = {
     default: false,
     enable: () => void "TODO",
     disable: () => void "TODO",
+    // TODO: to be reworked or removed in the big menu rework
+    textAdornment: async () => getFeatures().advancedMode ? `<span class="advanced-mode-icon">${await resourceToHTMLString("img-add_circle_small") ?? ""}</span>` : undefined,
   },
 } as const satisfies FeatureInfo;

+ 1 - 5
src/features/lyrics.ts

@@ -6,10 +6,6 @@ import { compressionFormat, mode, scriptInfo } from "../constants";
 import type { LyricsCacheEntry } from "../types";
 import { getFeatures } from "src/config";
 
-/** Base URL of geniURL */
-export const geniUrlBase = "https://api.sv443.net/geniurl";
-/** GeniURL endpoint that gives song metadata when provided with a `?q` or `?artist` and `?song` parameter - [more info](https://api.sv443.net/geniurl) */
-const geniURLSearchUrl = `${geniUrlBase}/search`;
 /** Ratelimit budget timeframe in seconds - should reflect what's in geniURL's docs */
 const geniUrlRatelimitTimeframe = 30;
 
@@ -341,7 +337,7 @@ export async function fetchLyricsUrls(artist: string, song: string): Promise<Omi
     }
 
     const startTs = Date.now();
-    const fetchUrl = constructUrlString(geniURLSearchUrl, {
+    const fetchUrl = constructUrlString(`${getFeatures().geniUrlBase}/search`, {
       disableFuzzy: null,
       utm_source: scriptInfo.name,
       utm_content: `v${scriptInfo.version}${mode === "development" ? "-dev" : ""}`,

+ 2 - 2
src/index.ts

@@ -31,7 +31,7 @@ import {
   initArrowKeySkip, initSiteSwitch,
   addAnchorImprovements, initNumKeysSkip,
   // lyrics
-  addMediaCtrlLyricsBtn, geniUrlBase,
+  addMediaCtrlLyricsBtn,
   // menu
   addConfigMenuOption,
   // other
@@ -54,7 +54,7 @@ import {
   console.log([
     "Powered by:",
     "─ Lots of ambition and dedication",
-    `─ My song metadata API: ${geniUrlBase}`,
+    "─ My song metadata API: https://api.sv443.net/geniurl",
     "─ My userscript utility library: https://github.com/Sv443-Network/UserUtils",
     "─ The fuse.js library: https://github.com/krisk/Fuse",
     "─ This markdown parser library: https://github.com/markedjs/marked",

+ 0 - 11
src/menu/menu_old.css

@@ -381,14 +381,3 @@ hr {
   margin: 8px 0px 12px 0px;
   border: revert;
 }
-
-.bytm-ftitem-adornment {
-  display: inline-flex;
-  justify-content: flex-start;
-  align-items: center;
-  margin-left: 8px;
-}
-
-#bytm-ftitem-locale-adornment svg path {
-  fill: var(--bytm-dialog-accent-col, #4595c7);
-}

+ 38 - 14
src/menu/menu_old.ts

@@ -242,7 +242,7 @@ async function addCfgMenu() {
     {} as Record<FeatureCategory, Record<FeatureKey, unknown>>,
     );
 
-  const fmtVal = (v: unknown) => String(v).trim();
+  const fmtVal = (v: unknown) => typeof v === "object" ? JSON.stringify(v) : String(v).trim();
 
   for(const category in featureCfgWithCategories) {
     const featObj = featureCfgWithCategories[category as FeatureCategory];
@@ -278,18 +278,21 @@ async function addCfgMenu() {
       {
         const featLeftSideElem = document.createElement("div");
         featLeftSideElem.classList.add("bytm-ftitem-leftside");
+        if(getFeatures().advancedMode)
+          featLeftSideElem.title = `${featKey}${ftInfo.advanced ? " (advanced)" : ""} - Default: ${fmtVal(ftDefault)}`;
 
         const textElem = document.createElement("span");
-        textElem.textContent = ftInfo.advanced ? t("advanced_feature_desc_template", t(`feature_desc_${featKey}`)) : t(`feature_desc_${featKey}`);
+        textElem.textContent = t(`feature_desc_${featKey}`);
 
         let adornmentElem: undefined | HTMLElement;
 
         const adornContent = ftInfo.textAdornment?.();
-        if(typeof adornContent === "string" || adornContent instanceof Promise) {
+        const adornContentAw = adornContent instanceof Promise ? await adornContent : adornContent;
+        if((typeof adornContent === "string" || adornContent instanceof Promise) && typeof adornContentAw !== "undefined") {
           adornmentElem = document.createElement("span");
           adornmentElem.id = `bytm-ftitem-${featKey}-adornment`;
           adornmentElem.classList.add("bytm-ftitem-adornment");
-          adornmentElem.innerHTML = adornContent instanceof Promise ? await adornContent : adornContent;
+          adornmentElem.innerHTML = adornContentAw;
         }
 
         let helpElem: undefined | HTMLDivElement;
@@ -322,8 +325,8 @@ async function addCfgMenu() {
           }
         }
 
-        featLeftSideElem.appendChild(textElem);
         adornmentElem && featLeftSideElem.appendChild(adornmentElem);
+        featLeftSideElem.appendChild(textElem);
         helpElem && featLeftSideElem.appendChild(helpElem);
 
         ftConfElem.appendChild(featLeftSideElem);
@@ -344,6 +347,9 @@ async function addCfgMenu() {
         case "number":
           inputType = "number";
           break;
+        case "text":
+          inputType = "text";
+          break;
         case "select":
           inputTag = "select";
           inputType = undefined;
@@ -422,13 +428,32 @@ async function addCfgMenu() {
             }
           }
 
-          inputElem.addEventListener("input", () => {
-            let v: string | number = String(inputElem.value).trim();
-            if(["number", "slider"].includes(type) || v.match(/^-?\d+$/))
-              v = Number(v);
-            if(typeof initialVal !== "undefined")
-              confChanged(featKey as keyof FeatureConfig, initialVal, (type !== "toggle" ? v : inputElem.checked));
-          });
+          if(type === "text") {
+            let lastValue: string | undefined = inputElem.value && inputElem.value.length > 0 ? inputElem.value : ftInfo.default;
+            const textInputUpdate = () => {
+              let v: string | number = String(inputElem.value).trim();
+              if(type === "text" && ftInfo.normalize)
+                v = inputElem.value = ftInfo.normalize(String(v));
+              if(v === lastValue)
+                return;
+              lastValue = v;
+              if(v === "")
+                v = ftInfo.default;
+              if(typeof initialVal !== "undefined")
+                confChanged(featKey as keyof FeatureConfig, initialVal, v);
+            };
+            inputElem.addEventListener("blur", () => textInputUpdate());
+            inputElem.addEventListener("keydown", (e) => e.key === "Tab" && textInputUpdate());
+          }
+          else {
+            inputElem.addEventListener("input", () => {
+              let v: string | number = String(inputElem.value).trim();
+              if(["number", "slider"].includes(type) || v.match(/^-?\d+$/))
+                v = Number(v);
+              if(typeof initialVal !== "undefined")
+                confChanged(featKey as keyof FeatureConfig, initialVal, (type !== "toggle" ? v : inputElem.checked));
+            });
+          }
 
           if(labelElem) {
             labelElem.id = `bytm-ftconf-${featKey}-label`;
@@ -730,8 +755,7 @@ async function openHelpDialog(featureKey: FeatureKey) {
     const featDescElem = menuBgElem.querySelector<HTMLElement>("#bytm-feat-help-menu-desc")!;
     const helpTextElem = menuBgElem.querySelector<HTMLElement>("#bytm-feat-help-menu-text")!;
 
-    // @ts-ignore
-    featDescElem.textContent = featInfo[featureKey].advanced ? t("advanced_feature_desc_template", t(`feature_desc_${featureKey}`)) : t(`feature_desc_${featureKey}`);
+    featDescElem.textContent = t(`feature_desc_${featureKey}`);
 
     // @ts-ignore
     const helpText: string | undefined = featInfo[featureKey]?.helpText?.();

+ 8 - 1
src/types.ts

@@ -150,6 +150,11 @@ type FeatureTypeProps = ({
     type: "hotkey";
     default: HotkeyObj;
   } & FeatureFuncProps)
+  | {
+    type: "text";
+    default: string;
+    normalize?: (val: string) => string;
+  }
   | {
     type: "button";
     default: undefined;
@@ -188,7 +193,7 @@ export type FeatureInfo = Record<
      * HTML string that is appended to the end of a feature's text description
      * @deprecated TODO:FIXME: To be removed or changed in the big menu rework
      */
-    textAdornment?: () => (Promise<string> | string);
+    textAdornment?: () => (Promise<string | undefined> | string | undefined);
 
     /** Whether to only show this feature when advanced mode is activated (default false) */
     advanced?: boolean;
@@ -253,6 +258,8 @@ export interface FeatureConfig {
   //#SECTION lyrics
   /** Add a button to the media controls to open the current song's lyrics on genius.com in a new tab */
   geniusLyrics: boolean;
+  /** Base URL to use for GeniURL */
+  geniUrlBase: string;
   /** Max size of lyrics cache */
   lyricsCacheMaxSize: number;
   /** Max TTL of lyrics cache entries, in ms */

+ 2 - 3
src/utils/xhr.ts

@@ -1,5 +1,4 @@
 import type { Stringifiable } from "@sv443-network/userutils";
-import type { HttpUrlString } from "../types";
 
 /**
  * Constructs a URL from a base URL and a record of query parameters.  
@@ -7,7 +6,7 @@ import type { HttpUrlString } from "../types";
  * All values will be stringified using their `toString()` method and then URI-encoded.
  * @returns Returns a string instead of a URL object
  */
-export function constructUrlString(baseUrl: HttpUrlString, params: Record<string, Stringifiable | null>) {
+export function constructUrlString(baseUrl: string, params: Record<string, Stringifiable | null>) {
   return `${baseUrl}?${
     Object.entries(params)
       .filter(([,v]) => v !== undefined)
@@ -22,7 +21,7 @@ export function constructUrlString(baseUrl: HttpUrlString, params: Record<string
  * All values will be URI-encoded.  
  * @returns Returns a URL object instead of a string
  */
-export function constructUrl(base: HttpUrlString, params: Record<string, Stringifiable | null>) {
+export function constructUrl(base: string, params: Record<string, Stringifiable | null>) {
   return new URL(constructUrlString(base, params));
 }