Custom Abyss-Lab VN settings

Adds custom settings to abyss-lab.app make the horrible VN engine more usable.

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

You will need to install an extension such as Tampermonkey to install this script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         Custom Abyss-Lab VN settings
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  Adds custom settings to abyss-lab.app make the horrible VN engine more usable.
// @author       GillianMC
// @match        *://*.abyss-lab.app/honkai3rd/novels/ae
// @match        *://abyss-lab.app/honkai3rd/novels/ae
// @match        *://*.abyss-lab.app/honkai3rd/novels/duriduri
// @match        *://abyss-lab.app/honkai3rd/novels/duriduri
// @icon         https://www.google.com/s2/favicons?sz=64&domain=abyss-lab.app
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
  'use strict';

  const settings = JSON.parse(localStorage.getItem('abyssLabSettings')) || {
    fontSize: 1.5,
    lineHeight: 2.5,
    fontFamily: 'htmlHeitiFamily',
    customFont: '',
    paddingTop: 0,
    uiRestylingEnabled: true,
  };

  let areSettingsOpen = false;
  let originalStyles = {};

  function modifyCSSRule(selector, property, value) {
    for (let sheet of document.styleSheets) {
      if (sheet.href && (sheet.href.includes('gameDurandal.css') || (sheet.href.includes('game.css')))) {
        try {
          for (let i = 0; i < sheet.cssRules.length; i++) {
            let rule = sheet.cssRules[i];
            if (rule.selectorText === selector) {
              rule.style[property] = value;
              return;
            }
          }
        } catch (e) {
          console.warn(`Unable to modify ${property} for ${selector}:`, e);
        }
      }
    }
  }

  function applySettings() {
    modifyCSSRule('.dialog-text', 'font-size', `${settings.fontSize}rem`);
    modifyCSSRule('.dialog-text', 'line-height', `${settings.lineHeight}rem`);
    modifyCSSRule('.dialog-text', 'font-family', settings.fontFamily === 'Custom' ? settings.customFont : settings.fontFamily);
    modifyCSSRule('.dialog-text', 'padding-top', `${settings.paddingTop}rem`);
    modifyCSSRule('.dialog-chara-text', 'font-family', settings.fontFamily === 'Custom' ? settings.customFont : settings.fontFamily);
    modifyCSSRule('.history-text', 'font-size', `${settings.fontSize}rem`);
    modifyCSSRule('.history-text', 'line-height', `${settings.lineHeight}rem`);
    modifyCSSRule('.history-text', 'font-family', settings.fontFamily === 'Custom' ? settings.customFont : settings.fontFamily);
    modifyCSSRule('.history-text', 'padding-top', `${settings.paddingTop}rem`);

    if (settings.uiRestylingEnabled) {
      // Apply UI restyling
      modifyCSSRule('.dialog', 'bottom', `0rem`);
      modifyCSSRule('.dialog-overflow', 'height', `4.8rem`);
      modifyCSSRule('.buttonBar', 'width', `13rem`);
      modifyCSSRule('.buttonBar', 'height', `1.3rem`);
      modifyCSSRule('.buttonBar', 'top', `unset`);
      modifyCSSRule('.buttonBar', 'left', `37.6%`);
      modifyCSSRule('.buttonBar', 'margin', `unset`);
      modifyCSSRule('.buttonBar', 'bottom', `0.1rem`);
      modifyCSSRule('.dialog-btn', 'left', `0`);
      modifyCSSRule('.dialog-btn-history', 'width', `2.31rem`);
      modifyCSSRule('.dialog-btn-history', 'left', `0`);
      modifyCSSRule('.dialog-btn-skip', 'width', `2.31rem`);
      modifyCSSRule('.dialog-btn-skip', 'left', `2rem`);
      modifyCSSRule('.dialog-btn-autoplay', 'width', `2.31rem`);
      modifyCSSRule('.dialog-btn-autoplay', 'left', `4rem`);
      modifyCSSRule('.record_btn', 'width', `2.31rem`);
      modifyCSSRule('.record_btn', 'left', `6rem`);
      modifyCSSRule('.read_record_btn', 'width', `2.31rem`);
      modifyCSSRule('.read_record_btn', 'left', `8rem`);
      modifyCSSRule('.home_btn', 'width', `1.8rem`);
      modifyCSSRule('.home_btn', 'height', `1.8rem`);
    } else {
      // Reset UI restyling to default
      modifyCSSRule('.dialog', 'bottom', `1rem`);
      modifyCSSRule('.dialog-overflow', 'height', `5rem`);
      modifyCSSRule('.buttonBar', 'width', `40rem`);
      modifyCSSRule('.buttonBar', 'height', `3.6rem`);
      modifyCSSRule('.buttonBar', 'top', `0`);
      modifyCSSRule('.buttonBar', 'left', `0`);
      modifyCSSRule('.buttonBar', 'margin', `0.1rem`);
      modifyCSSRule('.buttonBar', 'bottom', `unset`);
      modifyCSSRule('.dialog-btn', 'left', `12.8rem`);
      modifyCSSRule('.dialog-btn-history', 'width', `6.4rem`);
      modifyCSSRule('.dialog-btn-history', 'left', `unset`);
      modifyCSSRule('.dialog-btn-skip', 'width', `6.4rem`);
      modifyCSSRule('.dialog-btn-skip', 'left', `0`);
      modifyCSSRule('.dialog-btn-autoplay', 'width', `6.4rem`);
      modifyCSSRule('.dialog-btn-autoplay', 'left', `6.4rem`);
      modifyCSSRule('.record_btn', 'width', `6.4rem`);
      modifyCSSRule('.record_btn', 'left', `0`);
      modifyCSSRule('.read_record_btn', 'width', `6.4rem`);
      modifyCSSRule('.read_record_btn', 'left', `6.4rem`);
      modifyCSSRule('.home_btn', 'width', `3.6rem`);
      modifyCSSRule('.home_btn', 'height', `3.6rem`);
    }
  }

  const settingsButton = document.createElement('button');
  settingsButton.textContent = '⚙️';
  Object.assign(settingsButton.style, {
    position: 'absolute', bottom: '0.1rem', right: '0.1rem', zIndex: '1000',
    fontSize: '0.6em', cursor: 'pointer', backgroundColor: 'transparent',
    border: 'none', color: '#fff'
  });

  const frameDiv = document.querySelector('.frame');
  (frameDiv || document.body).appendChild(settingsButton);

  const settingsMenu = document.createElement('div');
  Object.assign(settingsMenu.style, {
    position: 'absolute', bottom: '1.5rem', right: '0.6rem', backgroundColor: 'rgba(0, 0, 0, 0.8)',
    border: '0.1em solid #fff', padding: '1rem',
    zIndex: '1000', display: 'none', color: '#fff', fontSize: '0.75rem', width: '16em'
  });
  settingsMenu.innerHTML = `
    <h3 style="margin: 0 0 0.5rem; font-size: 1rem">Settings</h3>
    <label style="font-size: 0.7rem">Font Size: <span id="fontSizeValue" style="font-size: 0.7rem">${settings.fontSize}</span>rem</label>
    <input type="range" id="fontSize" min="0.3" max="3" step="0.1" value="${settings.fontSize}" style="width: 100%;">
    <br>
    <label style="font-size: 0.7rem">Line Spacing: <span id="lineHeightValue" style="font-size: 0.7rem">${settings.lineHeight}</span>rem</label>
    <input type="range" id="lineHeight" min="1" max="4" step="0.1" value="${settings.lineHeight}" style="width: 100%;">
    <br>
    <label style="font-size: 0.7rem">Top Padding: <span id="paddingTopValue" style="font-size: 0.7rem">${settings.paddingTop}</span>rem</label>
    <input type="range" id="paddingTop" min="0" max="2" step="0.1" value="${settings.paddingTop}" style="width: 100%;">
    <br>
    <label style="font-size: 0.7rem">Font Family:</label>
    <select id="fontFamily" style="width: 100%; font-size: 0.7rem;">
      <option value="htmlHeitiFamily">Default (htmlHeitiFamily)</option>
      <option value="Arial">Arial</option>
      <option value="Times New Roman">Times New Roman</option>
      <option value="Courier New">Courier New</option>
      <option value="Verdana">Verdana</option>
      <option value="Georgia">Georgia</option>
      <option value="Comic Sans MS">Comic Sans MS</option>
      <option value="sans-serif">System Sans-Serif</option>
      <option value="serif">System Serif</option>
      <option value="monospace">System Monospace</option>
      <option value="Custom">Custom</option>
    </select>
    <br>
    <div id="customFontContainer" style="display: none;">
      <label style="font-size: 0.7rem">Custom Font:</label>
      <input type="text" id="customFont" placeholder="Enter custom font name" style="width: 100%; font-size: 0.7em;">
    </div>
    <br>
    <label style="font-size: 0.7rem">UI Restyling:</label>
    <input type="checkbox" id="uiRestylingToggle" ${settings.uiRestylingEnabled ? 'checked' : ''}>
  `;

  (frameDiv || document.body).appendChild(settingsMenu);
  document.getElementById('fontFamily').value = settings.fontFamily;
  const customFontContainer = document.getElementById('customFontContainer');
  const customFontInput = document.getElementById('customFont');
  customFontInput.value = settings.customFont || '';
  customFontContainer.style.display = settings.fontFamily === 'Custom' ? 'block' : 'none';

  settingsButton.addEventListener('click', () => {
    settingsMenu.style.display = settingsMenu.style.display === 'none' ? 'block' : 'none';
    areSettingsOpen = settingsMenu.style.display === 'none' ? false : true;
  });

  document.getElementById('fontSize').addEventListener('input', (e) => {
    settings.fontSize = parseFloat(e.target.value);
    document.getElementById('fontSizeValue').textContent = e.target.value;
    applySettings();
    localStorage.setItem('abyssLabSettings', JSON.stringify(settings));
  });

  document.getElementById('lineHeight').addEventListener('input', (e) => {
    settings.lineHeight = parseFloat(e.target.value);
    document.getElementById('lineHeightValue').textContent = e.target.value;
    applySettings();
    localStorage.setItem('abyssLabSettings', JSON.stringify(settings));
  });

  document.getElementById('paddingTop').addEventListener('input', (e) => {
    settings.paddingTop = parseFloat(e.target.value);
    document.getElementById('paddingTopValue').textContent = e.target.value;
    applySettings();
    localStorage.setItem('abyssLabSettings', JSON.stringify(settings));
  });

  document.getElementById('fontFamily').addEventListener('change', (e) => {
    settings.fontFamily = e.target.value;
    customFontContainer.style.display = e.target.value === 'Custom' ? 'block' : 'none';
    applySettings();
    localStorage.setItem('abyssLabSettings', JSON.stringify(settings));
  });

  document.getElementById('customFont').addEventListener('input', (e) => {
    settings.customFont = e.target.value;
    if (settings.fontFamily === 'Custom') {
      applySettings();
      localStorage.setItem('abyssLabSettings', JSON.stringify(settings));
    }
  });

  document.getElementById('uiRestylingToggle').addEventListener('change', (e) => {
    settings.uiRestylingEnabled = e.target.checked;
    applySettings();
    localStorage.setItem('abyssLabSettings', JSON.stringify(settings));
  });

  // Create the hide button
  const hideButton = document.createElement('button');
  hideButton.textContent = 'Hide';
  Object.assign(hideButton.style, {
    position: 'absolute', bottom: '0.1rem', left: '0.1rem', zIndex: '1000',
    fontSize: '0.6em', cursor: 'pointer', backgroundColor: 'transparent',
    border: 'none', color: '#fff'
  });

  (frameDiv || document.body).appendChild(hideButton);

  // Create the overlay layer
  const overlay = document.createElement('div');
  Object.assign(overlay.style, {
    position: 'fixed', top: '0', left: '0', width: '100%', height: '100%',
    backgroundColor: 'rgba(0, 0, 0, 0)', zIndex: '9999', display: 'none'
  });

  (frameDiv || document.body).appendChild(overlay);

  function hideElements() {
    const elementsToHide = [
      '.dialog', '.buttonBar', '.dialog-btn', '.record_btn', '.read_record_btn', '.home_btn', '.history'
    ];

    elementsToHide.forEach(selector => {
      const elements = document.querySelectorAll(selector);
      elements.forEach(element => {
        // Store the original display style
        originalStyles[selector] = element.style.display;
        // Hide the element
        element.style.display = 'none';
      });
    });

    hideButton.style.display = 'none';
    settingsButton.style.display = 'none';
    settingsMenu.style.display = 'none';
    overlay.style.display = 'block'; // Show the overlay
  }

  // Function to restore elements
  function restoreElements() {
    Object.keys(originalStyles).forEach(selector => {
      const elements = document.querySelectorAll(selector);
      elements.forEach(element => {
        // Restore the original display style
        element.style.display = originalStyles[selector];
      });
    });

    hideButton.style.display = 'block';
    if (areSettingsOpen == true){
        settingsMenu.style.display = 'block';
    }
    settingsButton.style.display = 'block';
    overlay.style.display = 'none'; // Hide the overlay
  }

  // Add event listener to hide button
  hideButton.addEventListener('click', hideElements);

  // Add event listener to overlay to restore elements
  overlay.addEventListener('click', restoreElements);

  applySettings();
})();