|
4 | 4 | // @namespace https://github.com/utags |
5 | 5 | // @homepageURL https://github.com/utags/userscripts#readme |
6 | 6 | // @supportURL https://github.com/utags/userscripts/issues |
7 | | -// @version 0.4.2 |
| 7 | +// @version 0.4.3 |
8 | 8 | // @description Enhance Discourse forums with instant topic switching, current topic highlighting, and quick navigation to previous/next topics |
9 | 9 | // @description:zh-CN 增强 Discourse 论坛体验,提供即时话题切换、当前话题高亮和上一个/下一个话题的快速导航功能 |
10 | 10 | // @author Pipecraft |
|
53 | 53 | let userSettings = { |
54 | 54 | language: CONFIG.DEFAULT_LANGUAGE, |
55 | 55 | showNavigationButtons: true, |
| 56 | + darkMode: 'auto', // auto, light, dark |
56 | 57 | } |
57 | 58 |
|
58 | 59 | // Pre-initialized site-specific keys (calculated once at script load) |
|
89 | 90 | save: 'Save', |
90 | 91 | cancel: 'Cancel', |
91 | 92 | showNavigationButtons: 'Show navigation buttons', |
| 93 | + darkMode: 'Dark Mode', |
| 94 | + darkModeAuto: 'Auto', |
| 95 | + darkModeLight: 'Light', |
| 96 | + darkModeDark: 'Dark', |
92 | 97 | }, |
93 | 98 | 'zh-CN': { |
94 | 99 | viewTopicList: '查看话题列表(按 ` 键)', |
|
117 | 122 | save: '保存', |
118 | 123 | cancel: '取消', |
119 | 124 | showNavigationButtons: '显示导航按钮', |
| 125 | + darkMode: '深色模式', |
| 126 | + darkModeAuto: '自动', |
| 127 | + darkModeLight: '浅色', |
| 128 | + darkModeDark: '深色', |
120 | 129 | }, |
121 | 130 | } |
122 | 131 |
|
|
197 | 206 | </select> |
198 | 207 | </div> |
199 | 208 |
|
| 209 | + <div class="dtqs-setting-item"> |
| 210 | + <label for="dtqs-dark-mode-select">${t('darkMode')}</label> |
| 211 | + <select id="dtqs-dark-mode-select"> |
| 212 | + <option value="auto" ${userSettings.darkMode === 'auto' ? 'selected' : ''}>${t('darkModeAuto')}</option> |
| 213 | + <option value="light" ${userSettings.darkMode === 'light' ? 'selected' : ''}>${t('darkModeLight')}</option> |
| 214 | + <option value="dark" ${userSettings.darkMode === 'dark' ? 'selected' : ''}>${t('darkModeDark')}</option> |
| 215 | + </select> |
| 216 | + </div> |
| 217 | +
|
200 | 218 | <div class="dtqs-setting-item checkbox-item"> |
201 | 219 | <label for="dtqs-show-nav-buttons"> |
202 | 220 | <input type="checkbox" id="dtqs-show-nav-buttons" ${userSettings.showNavigationButtons ? 'checked' : ''}> |
|
225 | 243 | const languageSelect = document.getElementById('dtqs-language-select') |
226 | 244 | userSettings.language = languageSelect.value |
227 | 245 |
|
| 246 | + // Save dark mode setting |
| 247 | + const darkModeSelect = document.getElementById('dtqs-dark-mode-select') |
| 248 | + userSettings.darkMode = darkModeSelect.value |
| 249 | + |
228 | 250 | // Save navigation buttons setting |
229 | 251 | const showNavButtons = document.getElementById('dtqs-show-nav-buttons') |
230 | 252 | userSettings.showNavigationButtons = showNavButtons.checked |
|
235 | 257 | // Update language |
236 | 258 | currentLanguage = userSettings.language |
237 | 259 |
|
| 260 | + // Update dark mode |
| 261 | + detectDarkMode() |
| 262 | + |
238 | 263 | // Close dialog |
239 | 264 | closeSettingsDialog() |
240 | 265 |
|
|
337 | 362 | } |
338 | 363 |
|
339 | 364 | /** |
340 | | - * Detect dark mode |
| 365 | + * Detect dark mode based on user settings |
341 | 366 | */ |
342 | 367 | function detectDarkMode() { |
343 | | - // Check system dark mode |
344 | | - if (CONFIG.AUTO_DARK_MODE && window.matchMedia) { |
345 | | - // Check system preference |
346 | | - const systemDarkMode = window.matchMedia( |
347 | | - '(prefers-color-scheme: dark)' |
348 | | - ).matches |
| 368 | + let shouldUseDarkMode = false |
| 369 | + |
| 370 | + // Check user's dark mode preference |
| 371 | + switch (userSettings.darkMode) { |
| 372 | + case 'dark': |
| 373 | + // Force dark mode |
| 374 | + shouldUseDarkMode = true |
| 375 | + console.log('[DTQS] Dark mode: Force enabled by user setting') |
| 376 | + break |
349 | 377 |
|
350 | | - // Check if the Discourse site is in dark mode |
351 | | - const discourseBodyClass = |
352 | | - document.body.classList.contains('dark-scheme') || |
353 | | - document.documentElement.classList.contains('dark-scheme') || |
354 | | - document.body.dataset.colorScheme === 'dark' || |
355 | | - document.documentElement.dataset.colorScheme === 'dark' || |
356 | | - document.documentElement.dataset.themeType === 'dark' || |
357 | | - // linux.do |
358 | | - document.querySelector('header picture > source')?.media === 'all' |
| 378 | + case 'light': |
| 379 | + // Force light mode |
| 380 | + shouldUseDarkMode = false |
| 381 | + console.log('[DTQS] Dark mode: Force disabled by user setting') |
| 382 | + break |
359 | 383 |
|
360 | | - // Enable dark mode if the system or site uses it |
361 | | - isDarkMode = systemDarkMode || discourseBodyClass |
| 384 | + case 'auto': |
| 385 | + default: |
| 386 | + // Auto mode - check system and site preferences |
| 387 | + if (window.matchMedia) { |
| 388 | + // Check system preference |
| 389 | + const systemDarkMode = window.matchMedia( |
| 390 | + '(prefers-color-scheme: dark)' |
| 391 | + ).matches |
| 392 | + |
| 393 | + // Check if the Discourse site is in dark mode |
| 394 | + const discourseBodyClass = |
| 395 | + document.body.classList.contains('dark-scheme') || |
| 396 | + document.documentElement.classList.contains('dark-scheme') || |
| 397 | + document.body.dataset.colorScheme === 'dark' || |
| 398 | + document.documentElement.dataset.colorScheme === 'dark' || |
| 399 | + document.documentElement.dataset.themeType === 'dark' || |
| 400 | + // linux.do |
| 401 | + document.querySelector('header picture > source')?.media === 'all' |
| 402 | + |
| 403 | + // Enable dark mode if the system or site uses it |
| 404 | + shouldUseDarkMode = systemDarkMode || discourseBodyClass |
| 405 | + |
| 406 | + console.log( |
| 407 | + `[DTQS] Dark mode (auto): System: ${systemDarkMode}, Site: ${discourseBodyClass}, Final: ${shouldUseDarkMode}` |
| 408 | + ) |
| 409 | + } |
| 410 | + break |
| 411 | + } |
362 | 412 |
|
363 | | - console.log( |
364 | | - `[DTQS] Dark mode detection - System: ${systemDarkMode}, Site: ${discourseBodyClass}, Final: ${isDarkMode}` |
365 | | - ) |
| 413 | + // Update global dark mode state |
| 414 | + isDarkMode = shouldUseDarkMode |
366 | 415 |
|
367 | | - // Add or remove dark mode class |
368 | | - if (isDarkMode) { |
369 | | - document.body.classList.add('topic-list-viewer-dark-mode') |
370 | | - } else { |
371 | | - document.body.classList.remove('topic-list-viewer-dark-mode') |
372 | | - } |
| 416 | + // Add or remove dark mode class |
| 417 | + if (isDarkMode) { |
| 418 | + document.body.classList.add('topic-list-viewer-dark-mode') |
| 419 | + } else { |
| 420 | + document.body.classList.remove('topic-list-viewer-dark-mode') |
373 | 421 | } |
374 | 422 | } |
375 | 423 |
|
376 | 424 | /** |
377 | 425 | * Set up dark mode listener |
378 | 426 | */ |
379 | 427 | function setupDarkModeListener() { |
380 | | - if (CONFIG.AUTO_DARK_MODE && window.matchMedia) { |
| 428 | + if (window.matchMedia) { |
381 | 429 | // Listen for system dark mode changes |
382 | 430 | const darkModeMediaQuery = window.matchMedia( |
383 | 431 | '(prefers-color-scheme: dark)' |
384 | 432 | ) |
385 | 433 |
|
386 | | - // Add change listener |
387 | | - if (darkModeMediaQuery.addEventListener) { |
388 | | - darkModeMediaQuery.addEventListener('change', (e) => { |
| 434 | + // Add change listener (only trigger if user is in auto mode) |
| 435 | + const handleSystemChange = (e) => { |
| 436 | + if (userSettings.darkMode === 'auto') { |
389 | 437 | detectDarkMode() |
390 | | - }) |
| 438 | + } |
| 439 | + } |
| 440 | + |
| 441 | + if (darkModeMediaQuery.addEventListener) { |
| 442 | + darkModeMediaQuery.addEventListener('change', handleSystemChange) |
391 | 443 | } else if (darkModeMediaQuery.addListener) { |
392 | 444 | // Fallback for older browsers |
393 | | - darkModeMediaQuery.addListener((e) => { |
394 | | - detectDarkMode() |
395 | | - }) |
| 445 | + darkModeMediaQuery.addListener(handleSystemChange) |
396 | 446 | } |
397 | 447 |
|
398 | | - // Listen for Discourse theme changes |
| 448 | + // Listen for Discourse theme changes (only trigger if user is in auto mode) |
399 | 449 | const observer = new MutationObserver((mutations) => { |
400 | | - mutations.forEach((mutation) => { |
401 | | - if ( |
402 | | - mutation.attributeName === 'class' || |
403 | | - mutation.attributeName === 'data-color-scheme' |
404 | | - ) { |
405 | | - detectDarkMode() |
406 | | - } |
407 | | - }) |
| 450 | + if (userSettings.darkMode === 'auto') { |
| 451 | + mutations.forEach((mutation) => { |
| 452 | + if ( |
| 453 | + mutation.attributeName === 'class' || |
| 454 | + mutation.attributeName === 'data-color-scheme' |
| 455 | + ) { |
| 456 | + detectDarkMode() |
| 457 | + } |
| 458 | + }) |
| 459 | + } |
408 | 460 | }) |
409 | 461 |
|
410 | 462 | // Observe class changes on body and html elements |
|
1630 | 1682 | border: 1px solid #ccc; |
1631 | 1683 | background: #f5f5f5; |
1632 | 1684 | cursor: pointer; |
| 1685 | + font-weight: 500; |
| 1686 | + transition: all 0.2s ease; |
1633 | 1687 | } |
1634 | 1688 |
|
1635 | 1689 | .dtqs-buttons button:hover { |
1636 | 1690 | background: #e5e5e5; |
1637 | 1691 | } |
1638 | 1692 |
|
| 1693 | + /* Primary save button styling */ |
| 1694 | + .dtqs-buttons #dtqs-settings-save { |
| 1695 | + background: #007bff; |
| 1696 | + border-color: #007bff; |
| 1697 | + color: white; |
| 1698 | + font-weight: 600; |
| 1699 | + box-shadow: 0 2px 4px rgba(0, 123, 255, 0.2); |
| 1700 | + } |
| 1701 | +
|
| 1702 | + .dtqs-buttons #dtqs-settings-save:hover { |
| 1703 | + background: #0056b3; |
| 1704 | + border-color: #0056b3; |
| 1705 | + box-shadow: 0 3px 6px rgba(0, 123, 255, 0.3); |
| 1706 | + transform: translateY(-1px); |
| 1707 | + } |
| 1708 | +
|
| 1709 | + .dtqs-buttons #dtqs-settings-save:active { |
| 1710 | + background: #004085; |
| 1711 | + border-color: #004085; |
| 1712 | + transform: translateY(0); |
| 1713 | + box-shadow: 0 1px 2px rgba(0, 123, 255, 0.2); |
| 1714 | + } |
| 1715 | +
|
1639 | 1716 | /* Settings Dialog Dark Mode Styles */ |
1640 | 1717 | .topic-list-viewer-dark-mode #dtqs-settings-overlay { |
1641 | 1718 | background: rgba(0, 0, 0, 0.7); |
|
1687 | 1764 | background: #2a2a2a; |
1688 | 1765 | } |
1689 | 1766 |
|
| 1767 | + /* Primary save button styling for dark mode */ |
| 1768 | + .topic-list-viewer-dark-mode .dtqs-buttons #dtqs-settings-save { |
| 1769 | + background: #1976d2; |
| 1770 | + border-color: #1976d2; |
| 1771 | + color: white; |
| 1772 | + font-weight: 600; |
| 1773 | + box-shadow: 0 2px 4px rgba(25, 118, 210, 0.3); |
| 1774 | + } |
| 1775 | +
|
| 1776 | + .topic-list-viewer-dark-mode .dtqs-buttons #dtqs-settings-save:hover { |
| 1777 | + background: #1565c0; |
| 1778 | + border-color: #1565c0; |
| 1779 | + box-shadow: 0 3px 6px rgba(25, 118, 210, 0.4); |
| 1780 | + transform: translateY(-1px); |
| 1781 | + } |
| 1782 | +
|
| 1783 | + .topic-list-viewer-dark-mode .dtqs-buttons #dtqs-settings-save:active { |
| 1784 | + background: #0d47a1; |
| 1785 | + border-color: #0d47a1; |
| 1786 | + transform: translateY(0); |
| 1787 | + box-shadow: 0 1px 2px rgba(25, 118, 210, 0.3); |
| 1788 | + } |
| 1789 | +
|
1690 | 1790 | .topic-nav-container { |
1691 | 1791 | display: grid; |
1692 | 1792 | grid-template-columns: 1fr auto 1fr; |
|
0 commit comments