Просмотр исходного кода

ref: rename setInnerHtmlTrusted to setInnerHTML

Sv443 7 месяцев назад
Родитель
Сommit
4e4f4eb89f

+ 30 - 16
assets/translations/README.md

@@ -16,15 +16,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) | 292 (default locale) |  |
-| ‼️ | [`de_DE`](./de_DE.json) | `202/292` (69.2%) | ─ |
-| ─ | [`en_UK`](./en_UK.json) | `292` (100%) | `en_US` |
-| ‼️ | [`es_ES`](./es_ES.json) | `202/292` (69.2%) | ─ |
-| ‼️ | [`fr_FR`](./fr_FR.json) | `202/292` (69.2%) | ─ |
-| ‼️ | [`hi_IN`](./hi_IN.json) | `202/292` (69.2%) | ─ |
-| ‼️ | [`ja_JA`](./ja_JA.json) | `202/292` (69.2%) | ─ |
-| ‼️ | [`pt_BR`](./pt_BR.json) | `202/292` (69.2%) | ─ |
-| ‼️ | [`zh_CN`](./zh_CN.json) | `202/292` (69.2%) | ─ |
+| ─ | [`en_US`](./en_US.json) | 294 (default locale) |  |
+| ‼️ | [`de_DE`](./de_DE.json) | `202/294` (68.7%) | ─ |
+| ─ | [`en_UK`](./en_UK.json) | `294` (100%) | `en_US` |
+| ‼️ | [`es_ES`](./es_ES.json) | `202/294` (68.7%) | ─ |
+| ‼️ | [`fr_FR`](./fr_FR.json) | `202/294` (68.7%) | ─ |
+| ‼️ | [`hi_IN`](./hi_IN.json) | `202/294` (68.7%) | ─ |
+| ‼️ | [`ja_JA`](./ja_JA.json) | `202/294` (68.7%) | ─ |
+| ‼️ | [`pt_BR`](./pt_BR.json) | `202/294` (68.7%) | ─ |
+| ‼️ | [`zh_CN`](./zh_CN.json) | `202/294` (68.7%) | ─ |
 
 <sub>
 ✅ - Fully translated
@@ -45,7 +45,7 @@ This means to figure out which keys are untranslated, you will need to manually
 
 ### Missing keys:
 
-<details><summary><code>de_DE</code> - 90 missing keys <i>(click to show)</i></summary><br>
+<details><summary><code>de_DE</code> - 92 missing keys <i>(click to show)</i></summary><br>
 
 | Key | English text |
 | --- | ------------ |
@@ -69,6 +69,8 @@ This means to figure out which keys are untranslated, you will need to manually
 | `edit_entry` | `Edit this entry` |
 | `generic_error_toast` | `Encountered %1` |
 | `error` | `Error` |
+| `generic_error_dialog_message` | `Encountered an error.` |
+| `generic_error_dialog_open_console_note` | `Please open the JavaScript console (usually with Ctrl + Shift + K) and attach a screenshot of the error starting with "BetterYTM" to a [GitHub issue.](%1)` |
 | `open_lyrics_search_prompt` | `Enter the song title and artist to search for the lyrics:` |
 | `hotkey_input_click_to_change_tooltip` | `%1 - Currently set to: %2 - Enter any key combination to change. Note: some screen readers might block certain key combinations.` |
 | `hotkey_input_click_to_reset_tooltip` | `Reset to the last saved key combination` |
@@ -142,7 +144,7 @@ This means to figure out which keys are untranslated, you will need to manually
 
 <br></details>
 
-<details><summary><code>es_ES</code> - 90 missing keys <i>(click to show)</i></summary><br>
+<details><summary><code>es_ES</code> - 92 missing keys <i>(click to show)</i></summary><br>
 
 | Key | English text |
 | --- | ------------ |
@@ -166,6 +168,8 @@ This means to figure out which keys are untranslated, you will need to manually
 | `edit_entry` | `Edit this entry` |
 | `generic_error_toast` | `Encountered %1` |
 | `error` | `Error` |
+| `generic_error_dialog_message` | `Encountered an error.` |
+| `generic_error_dialog_open_console_note` | `Please open the JavaScript console (usually with Ctrl + Shift + K) and attach a screenshot of the error starting with "BetterYTM" to a [GitHub issue.](%1)` |
 | `open_lyrics_search_prompt` | `Enter the song title and artist to search for the lyrics:` |
 | `hotkey_input_click_to_change_tooltip` | `%1 - Currently set to: %2 - Enter any key combination to change. Note: some screen readers might block certain key combinations.` |
 | `hotkey_input_click_to_reset_tooltip` | `Reset to the last saved key combination` |
@@ -239,7 +243,7 @@ This means to figure out which keys are untranslated, you will need to manually
 
 <br></details>
 
-<details><summary><code>fr_FR</code> - 90 missing keys <i>(click to show)</i></summary><br>
+<details><summary><code>fr_FR</code> - 92 missing keys <i>(click to show)</i></summary><br>
 
 | Key | English text |
 | --- | ------------ |
@@ -263,6 +267,8 @@ This means to figure out which keys are untranslated, you will need to manually
 | `edit_entry` | `Edit this entry` |
 | `generic_error_toast` | `Encountered %1` |
 | `error` | `Error` |
+| `generic_error_dialog_message` | `Encountered an error.` |
+| `generic_error_dialog_open_console_note` | `Please open the JavaScript console (usually with Ctrl + Shift + K) and attach a screenshot of the error starting with "BetterYTM" to a [GitHub issue.](%1)` |
 | `open_lyrics_search_prompt` | `Enter the song title and artist to search for the lyrics:` |
 | `hotkey_input_click_to_change_tooltip` | `%1 - Currently set to: %2 - Enter any key combination to change. Note: some screen readers might block certain key combinations.` |
 | `hotkey_input_click_to_reset_tooltip` | `Reset to the last saved key combination` |
@@ -336,7 +342,7 @@ This means to figure out which keys are untranslated, you will need to manually
 
 <br></details>
 
-<details><summary><code>hi_IN</code> - 90 missing keys <i>(click to show)</i></summary><br>
+<details><summary><code>hi_IN</code> - 92 missing keys <i>(click to show)</i></summary><br>
 
 | Key | English text |
 | --- | ------------ |
@@ -360,6 +366,8 @@ This means to figure out which keys are untranslated, you will need to manually
 | `edit_entry` | `Edit this entry` |
 | `generic_error_toast` | `Encountered %1` |
 | `error` | `Error` |
+| `generic_error_dialog_message` | `Encountered an error.` |
+| `generic_error_dialog_open_console_note` | `Please open the JavaScript console (usually with Ctrl + Shift + K) and attach a screenshot of the error starting with "BetterYTM" to a [GitHub issue.](%1)` |
 | `open_lyrics_search_prompt` | `Enter the song title and artist to search for the lyrics:` |
 | `hotkey_input_click_to_change_tooltip` | `%1 - Currently set to: %2 - Enter any key combination to change. Note: some screen readers might block certain key combinations.` |
 | `hotkey_input_click_to_reset_tooltip` | `Reset to the last saved key combination` |
@@ -433,7 +441,7 @@ This means to figure out which keys are untranslated, you will need to manually
 
 <br></details>
 
-<details><summary><code>ja_JA</code> - 90 missing keys <i>(click to show)</i></summary><br>
+<details><summary><code>ja_JA</code> - 92 missing keys <i>(click to show)</i></summary><br>
 
 | Key | English text |
 | --- | ------------ |
@@ -457,6 +465,8 @@ This means to figure out which keys are untranslated, you will need to manually
 | `edit_entry` | `Edit this entry` |
 | `generic_error_toast` | `Encountered %1` |
 | `error` | `Error` |
+| `generic_error_dialog_message` | `Encountered an error.` |
+| `generic_error_dialog_open_console_note` | `Please open the JavaScript console (usually with Ctrl + Shift + K) and attach a screenshot of the error starting with "BetterYTM" to a [GitHub issue.](%1)` |
 | `open_lyrics_search_prompt` | `Enter the song title and artist to search for the lyrics:` |
 | `hotkey_input_click_to_change_tooltip` | `%1 - Currently set to: %2 - Enter any key combination to change. Note: some screen readers might block certain key combinations.` |
 | `hotkey_input_click_to_reset_tooltip` | `Reset to the last saved key combination` |
@@ -530,7 +540,7 @@ This means to figure out which keys are untranslated, you will need to manually
 
 <br></details>
 
-<details><summary><code>pt_BR</code> - 90 missing keys <i>(click to show)</i></summary><br>
+<details><summary><code>pt_BR</code> - 92 missing keys <i>(click to show)</i></summary><br>
 
 | Key | English text |
 | --- | ------------ |
@@ -554,6 +564,8 @@ This means to figure out which keys are untranslated, you will need to manually
 | `edit_entry` | `Edit this entry` |
 | `generic_error_toast` | `Encountered %1` |
 | `error` | `Error` |
+| `generic_error_dialog_message` | `Encountered an error.` |
+| `generic_error_dialog_open_console_note` | `Please open the JavaScript console (usually with Ctrl + Shift + K) and attach a screenshot of the error starting with "BetterYTM" to a [GitHub issue.](%1)` |
 | `open_lyrics_search_prompt` | `Enter the song title and artist to search for the lyrics:` |
 | `hotkey_input_click_to_change_tooltip` | `%1 - Currently set to: %2 - Enter any key combination to change. Note: some screen readers might block certain key combinations.` |
 | `hotkey_input_click_to_reset_tooltip` | `Reset to the last saved key combination` |
@@ -627,7 +639,7 @@ This means to figure out which keys are untranslated, you will need to manually
 
 <br></details>
 
-<details><summary><code>zh_CN</code> - 90 missing keys <i>(click to show)</i></summary><br>
+<details><summary><code>zh_CN</code> - 92 missing keys <i>(click to show)</i></summary><br>
 
 | Key | English text |
 | --- | ------------ |
@@ -651,6 +663,8 @@ This means to figure out which keys are untranslated, you will need to manually
 | `edit_entry` | `Edit this entry` |
 | `generic_error_toast` | `Encountered %1` |
 | `error` | `Error` |
+| `generic_error_dialog_message` | `Encountered an error.` |
+| `generic_error_dialog_open_console_note` | `Please open the JavaScript console (usually with Ctrl + Shift + K) and attach a screenshot of the error starting with "BetterYTM" to a [GitHub issue.](%1)` |
 | `open_lyrics_search_prompt` | `Enter the song title and artist to search for the lyrics:` |
 | `hotkey_input_click_to_change_tooltip` | `%1 - Currently set to: %2 - Enter any key combination to change. Note: some screen readers might block certain key combinations.` |
 | `hotkey_input_click_to_reset_tooltip` | `Reset to the last saved key combination` |

+ 3 - 0
assets/translations/en_US.json

@@ -74,6 +74,9 @@
     "middle_click_open_tab": "Middle-click to open in a new tab",
     "generic_error_toast": "Encountered %1",
     "error": "Error",
+    "generic_error_dialog_message": "Encountered an error.",
+    "generic_error_dialog_open_console_note": "Please open the JavaScript console (usually with Ctrl + Shift + K) and attach a screenshot of the error starting with \"BetterYTM\" to a [GitHub issue.](%1)",
+
     "active_mode_display": "%1 Mode",
     "active_mode_tooltip-1": "The %1 is currently active",
     "active_mode_tooltip-n": "The %1 are currently active",

+ 1 - 1
changelog.md

@@ -67,7 +67,7 @@
     - BytmDialog now has the option `removeListenersOnDestroy` (true by default) to configure removing all event listeners when the dialog is destroyed
     - BytmDialog's private members and methods have been changed to protected for easier extension (when using TypeScript)
   - Added interface functions:
-    - `setInnerHtmlTrusted()` to set the innerHTML property of an element to a sanitized string using the [Trusted Types API](https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API) and the library [DOMPurify](https://github.com/cure53/DOMPurify)
+    - `setInnerHtml()` to set the innerHTML property of an element to a sanitized string using the [Trusted Types API](https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API) and the library [DOMPurify](https://github.com/cure53/DOMPurify)
     - `getAutoLikeData()` to return the current auto-like data (authenticated function)
     - `saveAutoLikeData()` to overwrite the auto-like data (authenticated function)
     - `fetchVideoVotes()` to fetch the approximate like and dislike count of a video from [Return Youtube Dislike](https://returnyoutubedislike.com/)

+ 5 - 5
contributing.md

@@ -376,7 +376,7 @@ Functions marked with 🔒 need to be passed a per-session and per-plugin authen
   - [BytmDialog](#bytmdialog) - A class for creating and managing modal, fully customizable dialogs
   - [ExImDialog](#eximdialog) - Subclass of BytmDialog for allowing users to export and import serializable data
   - [MarkdownDialog](#markdowndialog) - Subclass of BytmDialog for displaying markdown content
-  - [setInnerHtmlTrusted()](#setinnerhtmltrusted) - Sets the innerHTML property of the specified element to the provided string, after sanitizing it (for compatibility with the [Trusted Types API](https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API))
+  - [setInnerHtml()](#setInnerHtml) - Sets the innerHTML property of the specified element to the provided string, after sanitizing it (for compatibility with the [Trusted Types API](https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API))
   - [addSelectorListener()](#addselectorlistener) - Adds a listener that checks for changes in DOM elements matching a CSS selector
   - [onInteraction()](#oninteraction) - Adds accessible event listeners to the specified element for button or link-like keyboard and mouse interactions
   - [getVideoTime()](#getvideotime) - Returns the current video time (on both YT and YTM)
@@ -654,10 +654,10 @@ Functions marked with 🔒 need to be passed a per-session and per-plugin authen
 
 <br>
 
-> #### setInnerHtmlTrusted()
+> #### setInnerHtml()
 > Usage:  
 > ```ts
-> unsafeWindow.BYTM.setInnerHtmlTrusted(element: HTMLElement, html: string): void
+> unsafeWindow.BYTM.setInnerHtml(element: HTMLElement, html: string): void
 > ```
 >   
 > Description:  
@@ -675,7 +675,7 @@ Functions marked with 🔒 need to be passed a per-session and per-plugin authen
 > const myElement = document.querySelector("#myElement");
 > const htmlString = "<img onload='alert(\"XSS attack!\")' src='https://picsum.photos/100/100'>";
 > 
-> unsafeWindow.BYTM.setInnerHtmlTrusted(myElement, htmlString);
+> unsafeWindow.BYTM.setInnerHtml(myElement, htmlString);
 > ```
 > </details>
 
@@ -1469,7 +1469,7 @@ Functions marked with 🔒 need to be passed a per-session and per-plugin authen
 >   small: true,
 >   verticalAlign: "top", // if the content's height changes, it's better to anchor it to the top or bottom
 >   // add elements to the header, body and footer here, in one of these ways:
->   // - setInnerHtmlTrusted(foo, "..."); // (see contributing guide)
+>   // - setInnerHtml(foo, "..."); // (see contributing guide)
 >   // - foo.appendChild(document.createElement("..."));
 >   // - ReactDOM.render(<MyComponent />, foo);
 >   // - etc.

+ 6 - 6
src/components/hotkeyInput.ts

@@ -1,5 +1,5 @@
 import { emitSiteEvent, siteEvents } from "../siteEvents.js";
-import { onInteraction, setInnerHtmlTrusted, t } from "../utils/index.js";
+import { onInteraction, setInnerHtml, t } from "../utils/index.js";
 import type { HotkeyObj } from "../types.js";
 import "./hotkeyInput.css";
 
@@ -51,7 +51,7 @@ export function createHotkeyInput({ initialValue, onChange, createTitle }: Hotke
     inputElem.innerText = curHk?.code ?? t("hotkey_input_click_to_change");
     inputElem.dataset.state = "inactive";
     inputElem.ariaLabel = inputElem.title = createTitle(hotkeyToString(curHk));
-    setInnerHtmlTrusted(infoElem, curHk ? getHotkeyInfoHtml(curHk) : "");
+    setInnerHtml(infoElem, curHk ? getHotkeyInfoHtml(curHk) : "");
   };
 
   const activate = () => {
@@ -72,14 +72,14 @@ export function createHotkeyInput({ initialValue, onChange, createTitle }: Hotke
     currentHotkey = initialValue!;
     deactivate();
     inputElem.innerText = initialValue!.code;
-    setInnerHtmlTrusted(infoElem, getHotkeyInfoHtml(initialValue!));
+    setInnerHtml(infoElem, getHotkeyInfoHtml(initialValue!));
     resetElem.classList.add("bytm-hidden");
   };
 
   onInteraction(resetElem, resetClicked);
 
   if(initialValue)
-    setInnerHtmlTrusted(infoElem, getHotkeyInfoHtml(initialValue));
+    setInnerHtml(infoElem, getHotkeyInfoHtml(initialValue));
 
   let lastKeyDown: HotkeyObj | undefined;
 
@@ -100,7 +100,7 @@ export function createHotkeyInput({ initialValue, onChange, createTitle }: Hotke
 
     inputElem.innerText = hotkey.code;
     inputElem.dataset.state = "inactive";
-    setInnerHtmlTrusted(infoElem, getHotkeyInfoHtml(hotkey));
+    setInnerHtml(infoElem, getHotkeyInfoHtml(hotkey));
     inputElem.ariaLabel = inputElem.title = t("hotkey_input_click_to_cancel_tooltip");
 
     onChange(hotkey);
@@ -144,7 +144,7 @@ export function createHotkeyInput({ initialValue, onChange, createTitle }: Hotke
 
     inputElem.innerText = hotkey.code;
     inputElem.dataset.state = "inactive";
-    setInnerHtmlTrusted(infoElem, getHotkeyInfoHtml(hotkey));
+    setInnerHtml(infoElem, getHotkeyInfoHtml(hotkey));
   });
 
   siteEvents.on("cfgMenuClosed", deactivate);

+ 2 - 2
src/components/longButton.ts

@@ -1,4 +1,4 @@
-import { onInteraction, resourceAsString, setInnerHtmlTrusted } from "../utils/index.js";
+import { onInteraction, resourceAsString, setInnerHtml } from "../utils/index.js";
 import { createRipple } from "./ripple.js";
 import type { ResourceKey } from "../types.js";
 
@@ -92,7 +92,7 @@ export async function createLongBtn({
   if("src" in rest)
     (imgElem as HTMLImageElement).src = rest.src;
   else
-    setInnerHtmlTrusted(imgElem, await resourceAsString(rest.resourceName as "_") ?? "");
+    setInnerHtml(imgElem, await resourceAsString(rest.resourceName as "_") ?? "");
 
   const txtElem = document.createElement("span");
   txtElem.classList.add("bytm-generic-long-btn-txt", "bytm-no-select");

+ 2 - 2
src/components/toast.ts

@@ -1,5 +1,5 @@
 import { clamp, pauseFor } from "@sv443-network/userutils";
-import { info, resourceAsString, setInnerHtmlTrusted } from "../utils/index.js";
+import { info, resourceAsString, setInnerHtml } from "../utils/index.js";
 import { getFeature } from "../config.js";
 import type { ResourceKey } from "../types.js";
 import "./toast.css";
@@ -74,7 +74,7 @@ export async function showIconToast({
     toastIcon.classList.add("bytm-toast-icon");
     const iconHtml = await resourceAsString(rest.icon);
     if(iconHtml)
-      setInnerHtmlTrusted(toastIcon, iconHtml);
+      setInnerHtml(toastIcon, iconHtml);
 
     if("iconFill" in rest && rest.iconFill)
       toastIcon.style.setProperty("--toast-icon-fill", rest.iconFill);

+ 2 - 2
src/components/toggleInput.ts

@@ -1,5 +1,5 @@
 import { randomId } from "@sv443-network/userutils";
-import { setInnerHtmlTrusted, t } from "../utils/index.js";
+import { setInnerHtml, t } from "../utils/index.js";
 import "./toggleInput.css";
 
 export interface ToggleInputProps {
@@ -47,7 +47,7 @@ export async function createToggleInput({
 
   const toggleKnobEl = document.createElement("div");
   toggleKnobEl.classList.add("bytm-toggle-input-knob");
-  setInnerHtmlTrusted(toggleKnobEl, "&nbsp;");
+  setInnerHtml(toggleKnobEl, "&nbsp;");
 
   const toggleElClicked = (e: Event) => {
     e.preventDefault();

+ 2 - 2
src/dialogs/changelog.ts

@@ -1,4 +1,4 @@
-import { getChangelogHtmlWithDetails, setInnerHtmlTrusted, t } from "../utils/index.js";
+import { getChangelogHtmlWithDetails, setInnerHtml, t } from "../utils/index.js";
 import { BytmDialog } from "../components/index.js";
 import { scriptInfo } from "../constants.js";
 
@@ -56,7 +56,7 @@ async function renderBody() {
   const mdContElem = document.createElement("div");
   mdContElem.id = "bytm-changelog-dialog-text";
   mdContElem.classList.add("bytm-markdown-container");
-  setInnerHtmlTrusted(mdContElem, await getChangelogHtmlWithDetails());
+  setInnerHtml(mdContElem, await getChangelogHtmlWithDetails());
 
   contElem.appendChild(mdContElem);
 

+ 2 - 2
src/dialogs/featHelp.ts

@@ -1,4 +1,4 @@
-import { resourceAsString, setInnerHtmlTrusted, t } from "../utils/index.js";
+import { resourceAsString, setInnerHtml, t } from "../utils/index.js";
 import { BytmDialog } from "../components/index.js";
 import { featInfo } from "../features/index.js";
 import type { FeatureKey } from "../types.js";
@@ -39,7 +39,7 @@ async function renderHeader() {
   const headerEl = document.createElement("div");
   const helpIconSvg = await resourceAsString("icon-help");
   if(helpIconSvg)
-    setInnerHtmlTrusted(headerEl, helpIconSvg);
+    setInnerHtml(headerEl, helpIconSvg);
 
   return headerEl;
 }

+ 2 - 2
src/dialogs/versionNotif.ts

@@ -1,5 +1,5 @@
 import { host, mode, scriptInfo } from "../constants.js";
-import { getChangelogMd, getResourceUrl, onInteraction, parseMarkdown, setInnerHtmlTrusted, t } from "../utils/index.js";
+import { getChangelogMd, getResourceUrl, onInteraction, parseMarkdown, setInnerHtml, t } from "../utils/index.js";
 import { BytmDialog, createToggleInput } from "../components/index.js";
 import { getFeature, getFeatures, setFeatures } from "../config.js";
 import pkg from "../../package.json" with { type: "json" };
@@ -89,7 +89,7 @@ async function renderBody({
   const changelogEl = document.createElement("p");
   changelogEl.id = "bytm-version-notif-changelog-cont";
   changelogEl.classList.add("bytm-markdown-container");
-  setInnerHtmlTrusted(changelogEl, changelogHtml);
+  setInnerHtml(changelogEl, changelogHtml);
 
   changelogEl.querySelectorAll("a").forEach((a) => {
     a.target = "_blank";

+ 6 - 6
src/dialogs/welcome.ts

@@ -1,4 +1,4 @@
-import { getResourceUrl, initTranslations, setInnerHtmlTrusted, setLocale, t, warn, type TrLocale } from "../utils/index.js";
+import { getResourceUrl, initTranslations, setInnerHtml, setLocale, t, warn, type TrLocale } from "../utils/index.js";
 import { BytmDialog } from "../components/index.js";
 import { openCfgMenu } from "../menu/menu_old.js";
 import { mode, scriptInfo } from "../constants.js";
@@ -171,11 +171,11 @@ function retranslateWelcomeMenu() {
       e.textContent = e.ariaLabel = t("close");
       e.ariaLabel = e.title = t("close_menu_tooltip");
     },
-    "#bytm-welcome-text-line1": (e: HTMLElement) => setInnerHtmlTrusted(e, e.ariaLabel = t("welcome_text_line_1")),
-    "#bytm-welcome-text-line2": (e: HTMLElement) => setInnerHtmlTrusted(e, e.ariaLabel = t("welcome_text_line_2", scriptInfo.name)),
-    "#bytm-welcome-text-line3": (e: HTMLElement) => setInnerHtmlTrusted(e, e.ariaLabel = t("welcome_text_line_3", scriptInfo.name, ...getLink(`${pkg.hosts.greasyfork}/feedback`), ...getLink(pkg.hosts.openuserjs))),
-    "#bytm-welcome-text-line4": (e: HTMLElement) => setInnerHtmlTrusted(e, e.ariaLabel = t("welcome_text_line_4", ...getLink(pkg.funding.url))),
-    "#bytm-welcome-text-line5": (e: HTMLElement) => setInnerHtmlTrusted(e, e.ariaLabel = t("welcome_text_line_5", ...getLink(pkg.bugs.url))),
+    "#bytm-welcome-text-line1": (e: HTMLElement) => setInnerHtml(e, e.ariaLabel = t("welcome_text_line_1")),
+    "#bytm-welcome-text-line2": (e: HTMLElement) => setInnerHtml(e, e.ariaLabel = t("welcome_text_line_2", scriptInfo.name)),
+    "#bytm-welcome-text-line3": (e: HTMLElement) => setInnerHtml(e, e.ariaLabel = t("welcome_text_line_3", scriptInfo.name, ...getLink(`${pkg.hosts.greasyfork}/feedback`), ...getLink(pkg.hosts.openuserjs))),
+    "#bytm-welcome-text-line4": (e: HTMLElement) => setInnerHtml(e, e.ariaLabel = t("welcome_text_line_4", ...getLink(pkg.funding.url))),
+    "#bytm-welcome-text-line5": (e: HTMLElement) => setInnerHtml(e, e.ariaLabel = t("welcome_text_line_5", ...getLink(pkg.bugs.url))),
   };
 
   for(const [selector, fn] of Object.entries(changes)) {

+ 3 - 3
src/features/input.ts

@@ -1,5 +1,5 @@
 import { DataStore, clamp, compress, decompress } from "@sv443-network/userutils";
-import { error, getVideoTime, info, log, warn, getDomain, compressionSupported, t, clearNode, resourceAsString, getCurrentChannelId, currentMediaType, sanitizeChannelId, addStyleFromResource, isValidChannelId, getVideoElement, setInnerHtmlTrusted } from "../utils/index.js";
+import { error, getVideoTime, info, log, warn, getDomain, compressionSupported, t, clearNode, resourceAsString, getCurrentChannelId, currentMediaType, sanitizeChannelId, addStyleFromResource, isValidChannelId, getVideoElement, setInnerHtml } from "../utils/index.js";
 import type { AutoLikeData, Domain } from "../types.js";
 import { disableBeforeUnload } from "./behavior.js";
 import { emitSiteEvent, siteEvents } from "../siteEvents.js";
@@ -369,7 +369,7 @@ async function addAutoLikeToggleBtn(siblingEl: HTMLElement, channelId: string, c
         const imgEl = buttonEl.querySelector<HTMLElement>(".bytm-generic-btn-img");
         const imgHtml = await resourceAsString(`icon-auto_like${toggled ? "_enabled" : ""}`);
         if(imgEl && imgHtml)
-          setInnerHtmlTrusted(imgEl, imgHtml);
+          setInnerHtml(imgEl, imgHtml);
 
         if(autoLikeStore.getData().channels.find((ch) => ch.id === chanId) === undefined) {
           await autoLikeStore.setData({
@@ -417,6 +417,6 @@ async function addAutoLikeToggleBtn(siblingEl: HTMLElement, channelId: string, c
     const imgEl = buttonEl.querySelector<HTMLElement>(".bytm-generic-btn-img");
     const imgHtml = await resourceAsString(`icon-auto_like${enabled ? "_enabled" : ""}`);
     if(imgEl && imgHtml)
-      setInnerHtmlTrusted(imgEl, imgHtml);
+      setInnerHtml(imgEl, imgHtml);
   });
 }

+ 5 - 5
src/features/layout.ts

@@ -2,7 +2,7 @@ import { addParent, autoPlural, debounce, fetchAdvanced, pauseFor } from "@sv443
 import { getFeature, getFeatures } from "../config.js";
 import { siteEvents } from "../siteEvents.js";
 import { addSelectorListener } from "../observers.js";
-import { error, getResourceUrl, log, warn, t, onInteraction, openInTab, getBestThumbnailUrl, getDomain, currentMediaType, domLoaded, waitVideoElementReady, addStyleFromResource, fetchVideoVotes, getWatchId, getLocale, tp, getVideoTime, setInnerHtmlTrusted } from "../utils/index.js";
+import { error, getResourceUrl, log, warn, t, onInteraction, openInTab, getBestThumbnailUrl, getDomain, currentMediaType, domLoaded, waitVideoElementReady, addStyleFromResource, fetchVideoVotes, getWatchId, getLocale, tp, getVideoTime, setInnerHtml } from "../utils/index.js";
 import { mode, scriptInfo } from "../constants.js";
 import { openCfgMenu } from "../menu/menu_old.js";
 import { createCircularBtn, createRipple } from "../components/index.js";
@@ -56,7 +56,7 @@ export async function improveLogo() {
     addSelectorListener("navBar", "ytmusic-logo a", {
       listener: (logoElem) => {
         logoElem.classList.add("bytm-mod-logo", "bytm-no-select");
-        setInnerHtmlTrusted(logoElem, svg);
+        setInnerHtml(logoElem, svg);
 
         logoElem.querySelectorAll("ellipse").forEach((e) => {
           e.classList.add("bytm-mod-logo-ellipse");
@@ -367,6 +367,9 @@ export async function fixSpacing() {
 export async function initAboveQueueBtns() {
   const { scrollToActiveSongBtn, clearQueueBtn } = getFeatures();
 
+  if(!await addStyleFromResource("css-above_queue_btns"))
+    error("Couldn't add CSS for above queue buttons");
+
   const contBtns = [
     {
       condition: scrollToActiveSongBtn,
@@ -426,9 +429,6 @@ export async function initAboveQueueBtns() {
           headerEl.classList[isFullscreen ? "add" : "remove"]("hidden");
         });
 
-        if(!await addStyleFromResource("css-above_queue_btns"))
-          return error("Couldn't add CSS for above queue buttons");
-
         const wrapperElem = document.createElement("div");
         wrapperElem.id = "bytm-above-queue-btn-wrapper";
 

+ 2 - 2
src/features/volume.ts

@@ -1,6 +1,6 @@
 import { addParent, type Stringifiable } from "@sv443-network/userutils";
 import { getFeature } from "../config.js";
-import { addStyleFromResource, error, log, resourceAsString, setGlobalCssVar, setInnerHtmlTrusted, t, waitVideoElementReady } from "../utils/index.js";
+import { addStyleFromResource, error, log, resourceAsString, setGlobalCssVar, setInnerHtml, t, waitVideoElementReady } from "../utils/index.js";
 import { siteEvents } from "../siteEvents.js";
 import { featInfo } from "./index.js";
 import "./volume.css";
@@ -78,7 +78,7 @@ async function addVolumeSliderLabel(sliderElem: HTMLInputElement, sliderContaine
     if(linkIconHtml) {
       const linkIconElem = document.createElement("div");
       linkIconElem.id = "bytm-vol-slider-shared";
-      setInnerHtmlTrusted(linkIconElem, linkIconHtml);
+      setInnerHtml(linkIconElem, linkIconHtml);
       linkIconElem.role = "alert";
       linkIconElem.ariaLive = "polite";
       linkIconElem.title = linkIconElem.ariaLabel = t("volume_shared_tooltip");

+ 3 - 1
src/index.ts

@@ -473,7 +473,9 @@ function registerDevCommands() {
     }
   });
 
-  GM.registerMenuCommand("Throw Error", () => error("Test error thrown by user command:", new SyntaxError("Test error")));
+  GM.registerMenuCommand("Throw specific Error", () => error("Test error thrown by user command:", new SyntaxError("Test error")));
+
+  GM.registerMenuCommand("Throw generic Error", () => error());
 
   GM.registerMenuCommand("Example MarkdownDialog", async () => {
     const mdDlg = new MarkdownDialog({

+ 2 - 2
src/interface.ts

@@ -1,7 +1,7 @@
 import * as UserUtils from "@sv443-network/userutils";
 import * as compareVersions from "compare-versions";
 import { mode, branch, host, buildNumber, compressionFormat, scriptInfo } from "./constants.js";
-import { getDomain, waitVideoElementReady, getResourceUrl, getSessionId, getVideoTime, log, setLocale, getLocale, hasKey, hasKeyFor, t, tp, type TrLocale, info, error, onInteraction, getThumbnailUrl, getBestThumbnailUrl, fetchVideoVotes, setInnerHtmlTrusted } from "./utils/index.js";
+import { getDomain, waitVideoElementReady, getResourceUrl, getSessionId, getVideoTime, log, setLocale, getLocale, hasKey, hasKeyFor, t, tp, type TrLocale, info, error, onInteraction, getThumbnailUrl, getBestThumbnailUrl, fetchVideoVotes, setInnerHtml } from "./utils/index.js";
 import { addSelectorListener } from "./observers.js";
 import { getFeatures, setFeatures } from "./config.js";
 import { autoLikeStore, featInfo, fetchLyricsUrlTop, getLyricsCacheEntry, sanitizeArtists, sanitizeSong } from "./features/index.js";
@@ -120,7 +120,7 @@ const globalFuncs: InterfaceFunctions = {
   getSessionId,
 
   // dom:
-  setInnerHtmlTrusted,
+  setInnerHtml,
   addSelectorListener,
   onInteraction,
   getVideoTime,

+ 3 - 3
src/menu/menu_old.ts

@@ -2,7 +2,7 @@ import { compress, debounce, isScrollable, type Stringifiable } from "@sv443-net
 import { type defaultData, formatVersion, getFeature, getFeatures, migrations, setFeatures } from "../config.js";
 import { buildNumber, compressionFormat, host, mode, scriptInfo } from "../constants.js";
 import { featInfo, disableBeforeUnload } from "../features/index.js";
-import { error, getResourceUrl, info, log, resourceAsString, getLocale, hasKey, initTranslations, setLocale, t, arrayWithSeparators, tp, type TrKey, onInteraction, getDomain, copyToClipboard, warn, compressionSupported, tryToDecompressAndParse, setInnerHtmlTrusted } from "../utils/index.js";
+import { error, getResourceUrl, info, log, resourceAsString, getLocale, hasKey, initTranslations, setLocale, t, arrayWithSeparators, tp, type TrKey, onInteraction, getDomain, copyToClipboard, warn, compressionSupported, tryToDecompressAndParse, setInnerHtml } from "../utils/index.js";
 import { emitSiteEvent, siteEvents } from "../siteEvents.js";
 import { getChangelogDialog, getFeatHelpDialog } from "../dialogs/index.js";
 import type { FeatureCategory, FeatureKey, FeatureConfig, HotkeyObj, FeatureInfo } from "../types.js";
@@ -420,7 +420,7 @@ async function mountCfgMenu() {
           adornmentElem = document.createElement("span");
           adornmentElem.id = `bytm-ftitem-${featKey}-adornment`;
           adornmentElem.classList.add("bytm-ftitem-adornment");
-          setInnerHtmlTrusted(adornmentElem, adornContent);
+          setInnerHtml(adornmentElem, adornContent);
         }
 
         let helpElem: undefined | HTMLDivElement;
@@ -438,7 +438,7 @@ async function mountCfgMenu() {
             helpElem.ariaLabel = helpElem.title = t("feature_help_button_tooltip", t(`feature_desc_${featKey}`));
             helpElem.role = "button";
             helpElem.tabIndex = 0;
-            setInnerHtmlTrusted(helpElem, helpElemImgHtml);
+            setInnerHtml(helpElem, helpElemImgHtml);
             onInteraction(helpElem, async (e: MouseEvent | KeyboardEvent) => {
               e.preventDefault();
               e.stopPropagation();

+ 2 - 2
src/types.ts

@@ -4,7 +4,7 @@ import type { scriptInfo } from "./constants.js";
 import type { addSelectorListener } from "./observers.js";
 import type resources from "../assets/resources.json";
 import type locales from "../assets/locales.json";
-import type { getResourceUrl, getSessionId, getVideoTime, TrLocale, t, tp, fetchVideoVotes, onInteraction, getThumbnailUrl, getBestThumbnailUrl, getLocale, hasKey, hasKeyFor, getDomain, waitVideoElementReady, setInnerHtmlTrusted } from "./utils/index.js";
+import type { getResourceUrl, getSessionId, getVideoTime, TrLocale, t, tp, fetchVideoVotes, onInteraction, getThumbnailUrl, getBestThumbnailUrl, getLocale, hasKey, hasKeyFor, getDomain, waitVideoElementReady, setInnerHtml } from "./utils/index.js";
 import type { SiteEventsMap } from "./siteEvents.js";
 import type { InterfaceEventsMap, getAutoLikeDataInterface, getFeaturesInterface, getPluginInfo, registerPlugin, saveAutoLikeDataInterface, saveFeaturesInterface, setLocaleInterface } from "./interface.js";
 import type { BytmDialog, ExImDialog, createCircularBtn, createHotkeyInput, createRipple, createToggleInput, showIconToast, showToast } from "./components/index.js";
@@ -281,7 +281,7 @@ export type InterfaceFunctions = {
 
   // dom:
   /** Sets the innerHTML property of the provided element to a sanitized version of the provided HTML string */
-  setInnerHtmlTrusted: typeof setInnerHtmlTrusted;
+  setInnerHtml: typeof setInnerHtml;
   /** Adds a listener to one of the already present SelectorObserver instances */
   addSelectorListener: typeof addSelectorListener;
   /** Registers accessible interaction listeners (click, enter, space) on the provided element */

+ 5 - 5
src/utils/dom.ts

@@ -171,7 +171,7 @@ export async function addStyle(css: string, ref?: string, transform: (css: strin
 }
 
 /**
- * Adds a global style element with the contents fetched from the specified CSS resource.  
+ * Adds a global style element with the contents fetched from the specified resource starting with `css-`  
  * The CSS can be transformed using the provided function before being added to the DOM.
  */
 export async function addStyleFromResource(key: ResourceKey & `css-${string}`, transform: (css: string) => string = (c) => c) {
@@ -183,12 +183,12 @@ export async function addStyleFromResource(key: ResourceKey & `css-${string}`, t
   return false;
 }
 
-/** Sets a global CSS variable on the &lt;document&gt; element */
+/** Sets a global CSS variable on the &lt;document&gt; element with the name `--bytm-global-${name}` */
 export function setGlobalCssVar(name: string, value: Stringifiable) {
-  document.documentElement.style.setProperty(`--bytm-global-${name}`, String(value));
+  document.documentElement.style.setProperty(`--bytm-global-${name.toLowerCase().trim()}`, String(value));
 }
 
-/** Sets multiple global CSS variables on the &lt;document&gt; element */
+/** Sets multiple global CSS variables on the &lt;document&gt; element with the name `--bytm-global-${name}` */
 export function setGlobalCssVars(vars: Record<string, Stringifiable>) {
   for(const [name, value] of Object.entries(vars))
     setGlobalCssVar(name, value);
@@ -236,7 +236,7 @@ export function copyToClipboard(text: Stringifiable) {
 let ttPolicy: TTPolicy | undefined;
 
 /** On Firefox, sets innerHTML directly, on Chromium, uses a [TrustedTypes](https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API) policy to set the HTML */
-export function setInnerHtmlTrusted(element: HTMLElement, html: string) {
+export function setInnerHtml(element: HTMLElement, html: string) {
   if(!ttPolicy && window?.trustedTypes?.createPolicy)
     ttPolicy = window.trustedTypes?.createPolicy("default", {
       createHTML: (unsafeHtml) =>

+ 7 - 10
src/utils/logging.ts

@@ -1,10 +1,11 @@
-import { clamp } from "@sv443-network/userutils";
+import { clamp, debounce } from "@sv443-network/userutils";
 import { scriptInfo } from "../constants.js";
 import { setGlobalProp } from "../interface.js";
 import { LogLevel } from "../types.js";
 import { MarkdownDialog, showIconToast } from "../components/index.js";
 import { t } from "./translations.js";
 import { getFeature } from "../config.js";
+import packageJson from "../../package.json" with { type: "json" };
 
 let curLogLevel = LogLevel.Info;
 
@@ -55,17 +56,13 @@ export function warn(...args: unknown[]): void {
   console.warn(consPrefix, ...args);
 }
 
-let errorDialog: MarkdownDialog;
-
 function getErrorDialog(errName: string, args: unknown[]) {
-  if(errorDialog)
-    return errorDialog;
-
-  return errorDialog = new MarkdownDialog({
+  return new MarkdownDialog({
     id: "generic-error",
     height: 400,
     width: 500,
     small: true,
+    destroyOnClose: true,
     renderHeader() {
       const header = document.createElement("h2");
       header.classList.add("bytm-dialog-title");
@@ -75,7 +72,7 @@ function getErrorDialog(errName: string, args: unknown[]) {
       header.textContent = header.ariaLabel = errName;
       return header;
     },
-    body: args.join(" "),
+    body: `${args.length > 0 ? args.join(" ") : t("generic_error_dialog_message")}\n\n${t("generic_error_dialog_open_console_note", packageJson.bugs.url)}`,
   });
 }
 
@@ -85,12 +82,12 @@ export function error(...args: unknown[]): void {
 
   if(getFeature("showToastOnGenericError")) {
     const errName = args.find(a => a instanceof Error)?.name ?? t("error");
-    showIconToast({
+    debounce(() => showIconToast({
       message: t("generic_error_toast", errName),
       icon: "icon-error",
       iconFill: "var(--bytm-error-col)",
       onClick: () => getErrorDialog(errName, Array.isArray(args) ? args : []).open(),
-    });
+    }))();
   }
 }