// ==UserScript==
// @name WTR-Lab Reader & UI Enhancer
// @namespace http://tampermonkey.net/
// @version 2.1
// @description Adds a responsive configuration panel to adjust the width of the reader content and bottom navigation bar on wtr-lab.com.
// @author MasuRiii
// @license MIT
// @match https://wtr-lab.com/en/novel/*/*/chapter-*
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @icon https://www.google.com/s2/favicons?sz=64&domain=wtr-lab.com
// ==/UserScript==
(function() {
'use strict';
// --- CONFIGURATION ---
const DEBUG_KEY = 'wtr_lab_enhancer_debug';
const STEP_WIDTH = 50;
const MIN_WIDTH = 300;
// Configuration for elements that can be modified
const configs = {
reader: {
key: 'wtr_lab_reader_width',
selector: '.fix-size.card',
defaultWidth: 760,
label: 'Reader Content Width'
},
nav: {
key: 'wtr_lab_nav_width',
selector: 'nav.bottom-reader-nav .fix-size',
defaultWidth: 760,
label: 'Bottom Navigator Width'
}
};
// --- DEBUG LOGGER ---
let isDebugEnabled = GM_getValue(DEBUG_KEY, false);
const log = (...args) => {
if (isDebugEnabled) {
console.log('[WTR-Lab Enhancer]', ...args);
}
};
const toggleDebugLogging = () => {
isDebugEnabled = !isDebugEnabled;
GM_setValue(DEBUG_KEY, isDebugEnabled);
alert(`Debug logging is now ${isDebugEnabled ? 'ENABLED' : 'DISABLED'}.`);
log(`Debug logging state changed to: ${isDebugEnabled}`);
};
// --- CORE LOGIC ---
// Applies a CSS rule to the page to set the width for a given element.
const applyStyle = (configName, width) => {
const styleId = `custom-width-styler-${configName}`;
let styleElement = document.getElementById(styleId);
if (!styleElement) {
styleElement = document.createElement('style');
styleElement.id = styleId;
document.head.appendChild(styleElement);
}
const config = configs[configName];
styleElement.textContent = `
${config.selector} {
max-width: ${width}px !important;
}
`;
log(`Style applied for '${configName}': max-width set to ${width}px`);
};
// Saves a value to Tampermonkey's storage.
const saveValue = (key, value) => {
GM_setValue(key, value);
log(`Value saved for key '${key}':`, value);
};
// Loads a value from Tampermonkey's storage.
const loadValue = (key, defaultValue) => {
return GM_getValue(key, defaultValue);
};
// --- UI CONFIGURATION PANEL ---
// Creates and injects the panel's HTML and CSS into the page.
const createConfigPanel = () => {
const panelHTML = `
<div id="wtr-config-overlay" style="display: none;">
<div id="wtr-config-panel">
<h2>WTR-Lab Enhancer Settings</h2>
<div id="wtr-config-sections"></div>
<button id="wtr-config-close-btn" class="wtr-config-button">Close</button>
</div>
</div>
`;
document.body.insertAdjacentHTML('beforeend', panelHTML);
const panelCSS = `
#wtr-config-overlay {
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background-color: rgba(0, 0, 0, 0.7); z-index: 9999;
display: flex; justify-content: center; align-items: center;
}
#wtr-config-panel {
background: #2c2c2c; color: #f1f1f1; padding: 25px;
border-radius: 8px; width: 90%; max-width: 500px;
box-shadow: 0 5px 15px rgba(0,0,0,0.5); font-family: sans-serif;
display: flex; flex-direction: column; gap: 20px;
}
#wtr-config-panel h2 { margin: 0 0 10px 0; text-align: center; }
.wtr-config-section {
display: flex; flex-direction: column; gap: 8px;
padding: 15px; border: 1px solid #444; border-radius: 5px;
}
.wtr-config-controls {
display: flex; gap: 10px; align-items: center;
flex-wrap: wrap; /* Allows controls to wrap on small screens */
}
.wtr-config-controls input[type="number"] {
flex-grow: 1; /* Allows input to fill available space */
min-width: 60px;
text-align: center; background: #444; color: #fff;
border: 1px solid #666; border-radius: 4px; padding: 8px;
}
.wtr-config-button {
padding: 8px 12px; border: none; border-radius: 4px; cursor: pointer;
background-color: #007bff; color: white; font-weight: bold;
flex-shrink: 0; /* Prevents buttons from shrinking */
}
.wtr-config-button.control {
width: 40px; /* Smaller, fixed width for +/- buttons */
}
.wtr-config-button.reset { background-color: #dc3545; }
#wtr-config-close-btn { background-color: #6c757d; align-self: center; width: 100px; }
`;
GM_addStyle(panelCSS);
// Dynamically create control sections based on the configs object
const sectionsContainer = document.getElementById('wtr-config-sections');
for (const [name, config] of Object.entries(configs)) {
const sectionHTML = `
<div class="wtr-config-section">
<label for="wtr-${name}-width-input">${config.label} (px)</label>
<div class="wtr-config-controls">
<button id="wtr-${name}-decrease-btn" class="wtr-config-button control">-</button>
<input type="number" id="wtr-${name}-width-input" min="${MIN_WIDTH}" step="10">
<button id="wtr-${name}-increase-btn" class="wtr-config-button control">+</button>
<button id="wtr-${name}-reset-btn" class="wtr-config-button reset">Reset</button>
</div>
</div>
`;
sectionsContainer.insertAdjacentHTML('beforeend', sectionHTML);
}
// Attach event listeners
attachPanelEventListeners();
};
// Wires up all the buttons and inputs in the panel.
const attachPanelEventListeners = () => {
const overlay = document.getElementById('wtr-config-overlay');
overlay.addEventListener('click', (e) => {
if (e.target === overlay) hideConfigPanel();
});
document.getElementById('wtr-config-close-btn').addEventListener('click', hideConfigPanel);
// Helper function to update and save settings
const updateSetting = (configName, newWidth) => {
const validatedWidth = Math.max(MIN_WIDTH, parseInt(newWidth, 10));
if (isNaN(validatedWidth)) return;
const config = configs[configName];
applyStyle(configName, validatedWidth);
saveValue(config.key, validatedWidth);
// *** FIX: Use configName to target the correct input field ***
document.getElementById(`wtr-${configName}-width-input`).value = validatedWidth;
};
// Set up controls for each configured element
for (const [name, config] of Object.entries(configs)) {
const input = document.getElementById(`wtr-${name}-width-input`);
const decreaseBtn = document.getElementById(`wtr-${name}-decrease-btn`);
const increaseBtn = document.getElementById(`wtr-${name}-increase-btn`);
const resetBtn = document.getElementById(`wtr-${name}-reset-btn`);
increaseBtn.addEventListener('click', () => {
const currentWidth = parseInt(input.value, 10);
updateSetting(name, currentWidth + STEP_WIDTH);
});
decreaseBtn.addEventListener('click', () => {
const currentWidth = parseInt(input.value, 10);
updateSetting(name, currentWidth - STEP_WIDTH);
});
resetBtn.addEventListener('click', () => {
updateSetting(name, config.defaultWidth);
});
input.addEventListener('change', () => {
updateSetting(name, input.value);
});
}
};
// Shows the configuration panel and populates it with current values.
const showConfigPanel = () => {
log('Showing configuration panel.');
// Populate inputs with current values
for (const [name, config] of Object.entries(configs)) {
const currentWidth = loadValue(config.key, config.defaultWidth);
document.getElementById(`wtr-${name}-width-input`).value = currentWidth;
}
document.getElementById('wtr-config-overlay').style.display = 'flex';
};
// Hides the configuration panel.
const hideConfigPanel = () => {
log('Hiding configuration panel.');
document.getElementById('wtr-config-overlay').style.display = 'none';
};
// --- INITIALIZATION ---
const init = () => {
log('Initializing script...');
// Create and inject the panel into the page
createConfigPanel();
// Apply initial styles on page load
for (const [name, config] of Object.entries(configs)) {
const initialWidth = loadValue(config.key, config.defaultWidth);
applyStyle(name, initialWidth);
log(`Initial width for '${name}' set to ${initialWidth}px`);
}
// Register the new, unified menu commands
GM_registerMenuCommand('Configure Settings', showConfigPanel);
GM_registerMenuCommand('Toggle Debug Logging', toggleDebugLogging);
log('Initialization complete.');
};
// Run the script
init();
})();