dark-mode-toggle.user.js
dark-mode-toggle.user.js
(function() {
'use strict';
settings.exclusionList.forEach(excludedSite => {
const listItem = document.createElement('li');
listItem.textContent = excludedSite;
listItem.appendChild(removeButton);
uiElements.siteExclusionList.appendChild(listItem);
});
}
function saveSettings() {
saveSettingsDebounced();
savePerSiteSettings(); // Also save per-site settings
}
uiElements.positionSelect.value = settings.position;
uiElements.offsetXInput.value = settings.offsetX;
uiElements.offsetYInput.value = settings.offsetY;
uiElements.brightnessInput.value = settings.brightness;
uiElements.contrastInput.value = settings.contrast;
uiElements.sepiaInput.value = settings.sepia;
uiElements.themeColorInput.value = settings.themeColor;
uiElements.textColorInput.value = settings.textColor;
uiElements.fontFamilyInput.value = settings.fontFamily;
updateExclusionListDisplay(); // Update the exclusion list in the UI
}
// Function to update the button's class based on the dark mode state
function updateButtonState() {
const button = document.getElementById(BUTTON_ID);
if (!button) return;
if (darkModeEnabled) {
button.classList.add('dark');
} else {
button.classList.remove('dark');
}
}
if (darkModeEnabled) {
if (!isSiteExcluded(window.location.href)) {
updateDarkReaderConfig();
await GM.setValue('darkMode', true);
button.classList.add('dark');
console.log('Dark mode enabled.');
} else {
darkModeEnabled = false; // Revert the toggle
button.classList.remove('dark');
console.log('Site excluded. Dark mode disabled.');
DarkReader.disable(); // Ensure DarkReader is disabled.
await GM.setValue('darkMode', false); // Update the stored value.
}
} else {
DarkReader.disable();
await GM.setValue('darkMode', false);
button.classList.remove('dark');
console.log('Dark mode disabled.');
}
await savePerSiteSettings();
}
button.style.bottom = '';
button.style.top = '';
button.style.left = '';
button.style.right = '';
switch (position) {
case 'top-left':
button.style.top = `${offsetY}px`;
button.style.left = `${offsetX}px`;
break;
case 'top-right':
button.style.top = `${offsetY}px`;
button.style.right = `${offsetX}px`;
break;
case 'bottom-left':
button.style.bottom = `${offsetY}px`;
button.style.left = `${offsetX}px`;
break;
case 'bottom-right':
default:
button.style.bottom = `${offsetY}px`;
button.style.right = `${offsetX}px`;
break;
}
}
// Create UI
function createUI() {
const ui = document.createElement('div');
ui.id = UI_ID;
uiElements.siteExclusionInput = document.createElement('input');
uiElements.siteExclusionInput.type = 'text';
uiElements.siteExclusionInput.id = SITE_EXCLUSION_INPUT_ID;
uiElements.siteExclusionInput.placeholder = 'Enter URL to exclude';
uiElements.siteExclusionList = document.createElement('ul');
uiElements.siteExclusionList.id = SITE_EXCLUSION_LIST_ID;
ui.appendChild(siteExclusionLabel);
ui.appendChild(uiElements.siteExclusionInput);
ui.appendChild(addButton);
ui.appendChild(uiElements.siteExclusionList);
document.body.appendChild(ui);
}
// Re-apply the main styles to update the theme. This is a bit hacky, but
works.
GM.addStyle(generateStyles());
}
function generateStyles() {
const { themeColor, textColor, iconMoon, iconSun } = settings;
return `
#${BUTTON_ID} {
width: 80px;
height: 40px;
background-color: #fff;
border-radius: 20px;
border: none;
cursor: pointer;
z-index: 1000;
opacity: 0.8;
transition-property: transform, opacity, box-shadow, background-
color;
transition-duration: 0.3s;
transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1), ease,
ease, ease;
display: flex;
align-items: center;
padding: 0 4px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
position: fixed;
}
#${BUTTON_ID}:hover {
opacity: 1;
transform: scale(1.1);
transition: transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275),
box-shadow 0.2s;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5);
}
#${BUTTON_ID} .icon {
width: 32px;
height: 32px;
border-radius: 50%;
transition-property: transform, background-color, -webkit-mask-
image, mask-image;
transition-duration: 0.3s;
transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55),
ease, ease, ease;
z-index: 1;
display: flex;
justify-content: center;
align-items: center;
font-size: 20px;
color: #333;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
background: none;
-webkit-mask-image: url('data:image/svg+xml;utf8,${iconMoon}');
mask-image: url('data:image/svg+xml;utf8,${iconMoon}');
-webkit-mask-size: cover;
mask-size: cover;
background-color: #333;
}
#${BUTTON_ID}.dark {
background-color: #000;
}
#${BUTTON_ID}.dark .icon {
transform: translateX(40px);
color: #ffeb3b;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
background: none;
-webkit-mask-image: url('data:image/svg+xml;utf8,${iconSun}');
mask-image: url('data:image/svg+xml;utf8,${iconSun}');
-webkit-mask-size: cover;
mask-size: cover;
background-color: #fff;
}
/* UI Styles */
#${UI_ID} {
position: fixed;
top: 20px;
left: 20px;
background-color: ${themeColor};
border: 1px solid #ddd;
padding: 15px;
z-index: 1001;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
display: none;
color: ${textColor};
font-family: ${settings.fontFamily};
max-width: 90vw; /* 最大寬度為螢幕寬度的 90% */
max-height: 80vh; /* 最大高度為螢幕高度的 80% */
overflow: auto; /* 超出邊界時顯示滾動條 */
}
#${UI_ID}.visible {
display: block;
}
#${UI_ID} label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
#${UI_ID} ul#${SITE_EXCLUSION_LIST_ID} {
list-style-type: none;
padding: 0;
}
#${UI_ID} ul#${SITE_EXCLUSION_LIST_ID} li {
margin-bottom: 5px;
}
#${TOGGLE_UI_BUTTON_ID}:hover {
background-color: #eee;
}
#${RESET_SETTINGS_BUTTON_ID}:hover {
background-color: #da190b;
}
`;
}
const toggleUIButtonExists =
document.getElementById(TOGGLE_UI_BUTTON_ID);
if (!toggleUIButtonExists) {
console.log('Toggle UI button lost, recreating...');
createToggleUIButton();
}
}
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});