Parcourir la source

增加高亮设置功能

sequoia00 il y a 1 mois
Parent
commit
36520bbe78
1 fichiers modifiés avec 136 ajouts et 27 suppressions
  1. 136 27
      static/web/viewer.html

+ 136 - 27
static/web/viewer.html

@@ -407,15 +407,20 @@
             letter-spacing: 0.06em;
         }
 
-        .reader-settings-options {
-            display: flex;
-            flex-wrap: wrap;
-            gap: 8px;
-        }
-
-        .reader-settings-option {
-            border: 1px solid rgba(15, 23, 42, 0.12);
-            background: #fff;
+        .reader-settings-options {
+            display: flex;
+            flex-wrap: wrap;
+            gap: 8px;
+        }
+
+        .reader-settings-options.reader-settings-options-column {
+            flex-direction: column;
+            align-items: stretch;
+        }
+
+        .reader-settings-option {
+            border: 1px solid rgba(15, 23, 42, 0.12);
+            background: #fff;
             border-radius: 999px;
             font-size: 12px;
             color: #0f172a;
@@ -423,11 +428,40 @@
             cursor: pointer;
         }
 
-        .reader-settings-option.is-selected {
-            background: #0f172a;
-            color: #fff;
-            border-color: #0f172a;
-        }
+        .reader-settings-option.is-selected {
+            background: #0f172a;
+            color: #fff;
+            border-color: #0f172a;
+        }
+
+        .reader-settings-toggle {
+            width: 100%;
+            justify-content: space-between;
+            align-items: center;
+            padding-inline: 12px;
+        }
+
+        .reader-highlight-colors {
+            display: flex;
+            flex-wrap: wrap;
+            gap: 10px;
+        }
+
+        .reader-highlight-color {
+            width: 28px;
+            height: 28px;
+            border-radius: 999px;
+            border: 2px solid rgba(15, 23, 42, 0.16);
+            cursor: pointer;
+            padding: 0;
+            background: var(--swatch-color);
+            box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.75);
+        }
+
+        .reader-highlight-color.is-selected {
+            border-color: #0f172a;
+            box-shadow: 0 0 0 3px rgba(15, 23, 42, 0.12), inset 0 0 0 1px rgba(255, 255, 255, 0.85);
+        }
 
         /* 顶部状态条 */
         #loading-indicator {
@@ -495,9 +529,6 @@
         }
 
         .tts-progress-highlight {
-            --highlight-bg-color: rgba(125, 190, 255, 0.16);
-            --highlight-selected-bg-color: rgba(125, 190, 255, 0.16);
-            background-color: rgba(125, 190, 255, 0.16) !important;
             pointer-events: none;
         }
 
@@ -1225,6 +1256,19 @@
                                     <button class="reader-settings-option" type="button" data-size="large">大</button>
                                 </div>
                             </div>
+                            <div class="reader-settings-group">
+                                <label class="reader-settings-label">文本高亮</label>
+                                <button id="ttsHighlightToggle" class="reader-settings-option reader-settings-toggle" type="button" aria-pressed="true">开启</button>
+                            </div>
+                            <div class="reader-settings-group">
+                                <label class="reader-settings-label">高亮颜色</label>
+                                <div class="reader-highlight-colors" data-setting-group="highlight-color">
+                                    <button class="reader-highlight-color" type="button" data-highlight-color="blue" data-color-label="浅蓝" style="--swatch-color: rgba(125, 190, 255, 0.28);" title="浅蓝" aria-label="浅蓝"></button>
+                                    <button class="reader-highlight-color" type="button" data-highlight-color="green" data-color-label="浅绿" style="--swatch-color: rgba(147, 211, 163, 0.28);" title="浅绿" aria-label="浅绿"></button>
+                                    <button class="reader-highlight-color" type="button" data-highlight-color="yellow" data-color-label="浅黄" style="--swatch-color: rgba(248, 223, 125, 0.3);" title="浅黄" aria-label="浅黄"></button>
+                                    <button class="reader-highlight-color" type="button" data-highlight-color="purple" data-color-label="浅紫" style="--swatch-color: rgba(196, 167, 255, 0.28);" title="浅紫" aria-label="浅紫"></button>
+                                </div>
+                            </div>
                         </div>
                     </div>
                 </aside>
@@ -1577,6 +1621,7 @@
                 const readerControlsCollapse = document.getElementById('readerControlsCollapse');
                 const readerSettingsButton = document.getElementById('readerSettingsButton');
                 const readerSettingsPanel = document.getElementById('readerSettingsPanel');
+                const ttsHighlightToggle = document.getElementById('ttsHighlightToggle');
                 const sidebarToggleButton = document.getElementById('sidebarToggleButton');
                 const browserFullscreenButton = document.getElementById('toggle-browser-fullscreen');
 
@@ -1591,6 +1636,14 @@
                 const NIGHT_MODE_STORAGE_KEY = 'reader_pro.night_mode';
                 const READER_SIZE_STORAGE_KEY = 'reader_pro.controls.size';
                 const READER_COLLAPSED_STORAGE_KEY = 'reader_pro.controls.collapsed';
+                const TTS_HIGHLIGHT_ENABLED_STORAGE_KEY = 'reader_pro.tts_highlight.enabled';
+                const TTS_HIGHLIGHT_COLOR_STORAGE_KEY = 'reader_pro.tts_highlight.color';
+                const TTS_HIGHLIGHT_COLORS = {
+                    blue: 'rgba(125, 190, 255, 0.16)',
+                    green: 'rgba(147, 211, 163, 0.16)',
+                    yellow: 'rgba(248, 223, 125, 0.18)',
+                    purple: 'rgba(196, 167, 255, 0.17)'
+                };
 
                 function updateReaderToggleIcon() {
                     const icon = readerControlsCollapse.querySelector('svg path');
@@ -1638,16 +1691,57 @@
                     document.body.classList.add(`reader-size-${normalized}`);
                     document.body.dataset.readerSize = normalized;
                     localStorage.setItem(READER_SIZE_STORAGE_KEY, normalized);
-                    document.querySelectorAll('[data-size]').forEach(button => {
-                        button.classList.toggle('is-selected', button.dataset.size === normalized);
-                    });
-                }
-
-                function initReaderControls() {
-                    setReaderControlsPosition('left');
-                    setReaderControlsSize(localStorage.getItem(READER_SIZE_STORAGE_KEY) || 'small');
-                    setReaderControlsCollapsed(localStorage.getItem(READER_COLLAPSED_STORAGE_KEY) === '1');
-                }
+                    document.querySelectorAll('[data-size]').forEach(button => {
+                        button.classList.toggle('is-selected', button.dataset.size === normalized);
+                    });
+                }
+
+                function isTtsHighlightEnabled() {
+                    return localStorage.getItem(TTS_HIGHLIGHT_ENABLED_STORAGE_KEY) !== '0';
+                }
+
+                function getTtsHighlightColorKey() {
+                    const colorKey = localStorage.getItem(TTS_HIGHLIGHT_COLOR_STORAGE_KEY) || 'blue';
+                    return Object.prototype.hasOwnProperty.call(TTS_HIGHLIGHT_COLORS, colorKey) ? colorKey : 'blue';
+                }
+
+                function applyTtsHighlightSettingsToUi() {
+                    const enabled = isTtsHighlightEnabled();
+                    const colorKey = getTtsHighlightColorKey();
+                    ttsHighlightToggle.classList.toggle('is-selected', enabled);
+                    ttsHighlightToggle.textContent = enabled ? '开启' : '关闭';
+                    ttsHighlightToggle.setAttribute('aria-pressed', enabled ? 'true' : 'false');
+                    document.querySelectorAll('[data-highlight-color]').forEach(button => {
+                        button.classList.toggle('is-selected', button.dataset.highlightColor === colorKey);
+                    });
+                }
+
+                function refreshActiveHighlightIfNeeded() {
+                    clearActiveTtsHighlight();
+                    if (!isTtsHighlightEnabled()) return;
+                    if (!playbackHighlightContext?.activeSentenceText && playbackHighlightContext?.activeSentenceIndex === null) return;
+                    restoreActiveSentenceHighlight();
+                }
+
+                function setTtsHighlightEnabled(enabled) {
+                    localStorage.setItem(TTS_HIGHLIGHT_ENABLED_STORAGE_KEY, enabled ? '1' : '0');
+                    applyTtsHighlightSettingsToUi();
+                    refreshActiveHighlightIfNeeded();
+                }
+
+                function setTtsHighlightColor(colorKey) {
+                    const normalized = Object.prototype.hasOwnProperty.call(TTS_HIGHLIGHT_COLORS, colorKey) ? colorKey : 'blue';
+                    localStorage.setItem(TTS_HIGHLIGHT_COLOR_STORAGE_KEY, normalized);
+                    applyTtsHighlightSettingsToUi();
+                    refreshActiveHighlightIfNeeded();
+                }
+
+                function initReaderControls() {
+                    setReaderControlsPosition('left');
+                    setReaderControlsSize(localStorage.getItem(READER_SIZE_STORAGE_KEY) || 'small');
+                    setReaderControlsCollapsed(localStorage.getItem(READER_COLLAPSED_STORAGE_KEY) === '1');
+                    applyTtsHighlightSettingsToUi();
+                }
 
                 function updateNightModeButton(isEnabled) {
                     const icon = nightModeButton.querySelector('svg');
@@ -1859,6 +1953,14 @@
                 readerSettingsPanel.querySelectorAll('[data-size]').forEach(button => {
                     button.addEventListener('click', () => setReaderControlsSize(button.dataset.size));
                 });
+                ttsHighlightToggle.addEventListener('click', () => {
+                    setTtsHighlightEnabled(!isTtsHighlightEnabled());
+                });
+                readerSettingsPanel.querySelectorAll('[data-highlight-color]').forEach(button => {
+                    button.addEventListener('click', () => {
+                        setTtsHighlightColor(button.dataset.highlightColor);
+                    });
+                });
                 document.addEventListener('fullscreenchange', updateBrowserFullscreenButton);
                 document.addEventListener('webkitfullscreenchange', updateBrowserFullscreenButton);
                 document.addEventListener('mozfullscreenchange', updateBrowserFullscreenButton);
@@ -2333,6 +2435,7 @@
                 }
 
                 function restoreActiveSentenceHighlight(retryCount = 0) {
+                    if (!isTtsHighlightEnabled()) return;
                     const activeSentenceIndex = playbackHighlightContext?.activeSentenceIndex;
                     const activeSentenceText = playbackHighlightContext?.activeSentenceText || '';
                     if (activeSentenceIndex === null && !activeSentenceText) return;
@@ -2348,6 +2451,7 @@
                 }
 
                 function updateHighlightWindow(rangeStart, rangeEnd) {
+                    if (!isTtsHighlightEnabled()) return;
                     const ctx = playbackHighlightContext;
                     if (!ctx?.textLayerIndex) return;
                     const layerText = ctx.textLayerIndex.text;
@@ -2385,6 +2489,10 @@
 
                         const overlay = document.createElement('div');
                         overlay.className = 'highlight appended tts-progress-highlight';
+                        const highlightColor = TTS_HIGHLIGHT_COLORS[getTtsHighlightColorKey()];
+                        overlay.style.setProperty('--highlight-bg-color', highlightColor, 'important');
+                        overlay.style.setProperty('--highlight-selected-bg-color', highlightColor, 'important');
+                        overlay.style.setProperty('background-color', highlightColor, 'important');
                         overlay.style.left = `${left}px`;
                         overlay.style.top = `${top}px`;
                         overlay.style.width = `${width}px`;
@@ -2419,6 +2527,7 @@
                     ctx.lastWindowKey = '';
                     ctx.activeSentenceIndex = sentenceIndex;
                     ctx.activeSentenceText = targetSentence;
+                    if (!isTtsHighlightEnabled()) return;
                     updateHighlightWindow(matchStart, matchEnd);
                 }