From 87b4cea290cf9831fc9cd87e3739ee15ab175852 Mon Sep 17 00:00:00 2001 From: rbetree Date: Fri, 2 Jan 2026 22:08:46 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=87=8D=E6=9E=84=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E6=A1=86=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/style.css | 193 ++++++++++++++++++++++++---------- src/script.js | 100 +++++++++--------- templates/layouts/default.hbs | 86 ++++++++++++--- 3 files changed, 260 insertions(+), 119 deletions(-) diff --git a/assets/style.css b/assets/style.css index 172d0de..9a693ec 100644 --- a/assets/style.css +++ b/assets/style.css @@ -746,11 +746,10 @@ body .content.expanded { position: relative; width: 100%; max-width: 600px; - --search-control-size: 32px; - --search-control-gap: 0.4rem; - --search-toggle-right: 0.8rem; - --search-icon-right: calc(var(--search-toggle-right) + var(--search-control-size) + var(--search-control-gap)); - --search-indicator-right: calc(var(--search-icon-right) + var(--search-control-size)); + display: flex; + align-items: stretch; + --search-status-right: 0.9rem; + --search-hint-right: 1.6rem; background: rgba(var(--card-bg-rgb), 0.65); border: 1px solid var(--border-color); backdrop-filter: blur(12px); @@ -768,7 +767,7 @@ body .content.expanded { .search-box::after { content: ''; position: absolute; - right: var(--search-indicator-right); + right: var(--search-status-right); top: 50%; transform: translateY(-50%); width: 6px; @@ -790,12 +789,16 @@ body .content.expanded { } .search-box input { + flex: 1; + min-width: 0; width: 100%; - padding: var(--spacing-md) calc(var(--search-indicator-right) + 1rem) var(--spacing-md) var(--spacing-lg); + padding: var(--spacing-md) calc(var(--spacing-lg) + 4.8rem) var(--spacing-md) var(--spacing-md); border: none; - border-radius: var(--radius-lg); + border-radius: 0 var(--radius-lg) var(--radius-lg) 0; background-color: transparent; color: var(--text-color); + font-family: inherit; + font-weight: inherit; font-size: 1rem; transition: background-color var(--transition-normal), color var(--transition-normal); box-shadow: none; @@ -809,63 +812,102 @@ body .content.expanded { .search-box input::placeholder { color: var(--text-muted); + font-family: inherit; + font-weight: inherit; } -.search-box i { +.search-shortcut-hint { position: absolute; - right: 1.5rem; top: 50%; + right: var(--search-hint-right); transform: translateY(-50%); + padding: 0.1rem 0.4rem; + border: 1px solid var(--border-color); + border-radius: var(--radius-md); + background: rgba(var(--card-bg-rgb), 0.25); + font-size: 0.78rem; + line-height: 1.2; color: var(--text-muted); - transition: all 0.3s ease; + opacity: 0.65; + pointer-events: none; + user-select: none; } -.search-box:focus-within .search-icon, -.search-box:focus-within .search-engine-toggle { +.search-box:focus-within .search-shortcut-hint { + opacity: 0.85; +} + +/* 搜索引擎前缀按钮(方案B:输入框前缀一体化) */ +.search-engine-button { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0 0.75rem; + width: 120px; + flex: 0 0 120px; + border: none; + border-right: 1px solid var(--border-color); + border-radius: var(--radius-lg) 0 0 var(--radius-lg); + background: transparent; + color: var(--text-muted); + cursor: pointer; + font: inherit; + transition: background var(--transition-normal), color var(--transition-normal), transform var(--transition-normal); +} + +.search-engine-button:hover { + background: rgba(var(--card-bg-rgb), 0.25); +} + +.search-engine-button:focus-visible { + outline: 2px solid var(--accent-color); + outline-offset: 2px; +} + +.search-box:focus-within .search-engine-button { color: var(--accent-color); } -.search-box.has-results .search-icon { - color: var(--success-color); +.search-engine-icon { + display: grid; + place-items: center; + height: 1.2em; + width: 1.2em; + min-width: 1.2em; + font-size: 1.25rem; + line-height: 1; + text-align: center; + flex: 0 0 1.2em; } -.search-box.no-results .search-icon { - color: var(--error-color); +.search-engine-icon.search-engine-icon-svg { + font-size: 1.25rem; } -/* 搜索图标和搜索引擎切换图标位置调整 */ -.search-box .search-icon { - right: var(--search-icon-right); - cursor: pointer; - transition: all 0.3s ease; - width: var(--search-control-size); - height: var(--search-control-size); - display: flex; - align-items: center; - justify-content: center; +.search-engine-icon.search-engine-icon-svg svg { + width: 100%; + height: 100%; + display: block; } -/* 下拉指示图标交互与状态 */ -.search-box .search-engine-toggle:hover { color: inherit; } +.search-engine-label { + flex: 1; + min-width: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: 0.95rem; +} -.search-box.dropdown-open .search-engine-toggle { transform: translateY(-50%) rotate(180deg); } - -.search-box .search-engine-toggle { - right: var(--search-toggle-right); - cursor: pointer; - font-size: 0.8rem; - width: var(--search-control-size); - height: var(--search-control-size); - display: flex; - align-items: center; - justify-content: center; +.search-box.dropdown-open .search-engine-button { + background: rgba(var(--card-bg-rgb), 0.25); } /* 搜索引擎下拉菜单 */ .search-engine-dropdown { position: absolute; - top: calc(100% + 5px); - right: 1rem; + top: calc(100% + 6px); + left: 0; background: rgba(var(--card-bg-rgb), 0.9); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); @@ -873,8 +915,11 @@ body .content.expanded { box-shadow: 0 4px 15px var(--shadow-color); display: none; z-index: 100; - padding: 0.5rem; + padding: 0.35rem; border: 1px solid var(--border-color); + min-width: 190px; + flex-direction: column; + gap: 0.25rem; } .search-engine-dropdown.active { @@ -884,32 +929,56 @@ body .content.expanded { .search-engine-option { display: flex; - justify-content: center; align-items: center; - width: 40px; + justify-content: flex-start; + gap: 0.6rem; + width: 100%; height: 40px; - border-radius: 50%; - margin: 0.3rem; + padding: 0 0.75rem; + border: none; + border-radius: var(--radius-md); cursor: pointer; transition: all 0.2s ease; - background-color: var(--card-bg-gradient-1); + background: transparent; + color: var(--text-color); + font: inherit; } .search-engine-option:hover { - background-color: var(--card-bg-gradient-2); - transform: translateY(-2px); - box-shadow: 0 3px 8px var(--shadow-color); + background: rgba(var(--card-bg-rgb), 0.22); +} + +.search-engine-option:focus-visible { + outline: 2px solid var(--accent-color); + outline-offset: 2px; } .search-engine-option.active { background-color: var(--secondary-bg); - color: white; + color: var(--text-bright); } .search-engine-option i { + display: grid; + place-items: center; position: static; transform: none; - font-size: 1.2rem; + font-size: 1.25rem; + width: 1.35em; + height: 1.35em; + line-height: 1; + text-align: center; + flex: 0 0 1.35em; +} + +.search-engine-option i.search-engine-option-svg svg { + width: 100%; + height: 100%; + display: block; +} + +.search-engine-option-label { + font-size: 0.95rem; } /* 页面容器 */ @@ -2186,12 +2255,24 @@ body .content.expanded { } .search-box input { - padding: 0.8rem 3rem 0.8rem 1.2rem; + padding: 0.8rem 3rem 0.8rem 1rem; font-size: 0.95rem; } - - .search-box i { + + .search-box::after { + right: 0.8rem; + } + + .search-shortcut-hint { right: 1.2rem; + font-size: 0.72rem; + padding: 0.1rem 0.35rem; + } + + .search-engine-button { + width: 104px; + flex: 0 0 104px; + padding: 0 0.6rem; } .sidebar .logo h1, diff --git a/src/script.js b/src/script.js index 389be78..8d08b7a 100644 --- a/src/script.js +++ b/src/script.js @@ -874,31 +874,32 @@ document.addEventListener('DOMContentLoaded', () => { items: [] }; - // 搜索引擎配置 - const searchEngines = { - local: { - name: '本地搜索', - icon: 'fas fa-search', - url: null // 本地搜索不需要URL - }, - google: { - name: 'Google搜索', - icon: 'fab fa-google', - url: 'https://www.google.com/search?q=' - }, - bing: { - name: 'Bing搜索', - icon: 'fab fa-microsoft', - url: 'https://www.bing.com/search?q=' - }, - baidu: { - name: '百度搜索', - icon: 'fas fa-paw', - url: 'https://www.baidu.com/s?wd=' - } + // 搜索引擎配置 + const searchEngines = { + local: { + name: '本地搜索', + iconSvg: ``, + url: null // 本地搜索不需要URL + }, + google: { + name: 'Google搜索', + iconSvg: ``, + url: 'https://www.google.com/search?q=' + }, + bing: { + name: 'Bing搜索', + iconSvg: ``, + url: 'https://www.bing.com/search?q=' + }, + duckduckgo: { + name: 'DuckDuckGo搜索', + shortName: 'duckgo', + // DuckDuckGo 使用内联 SVG,避免依赖不存在的 Font Awesome 品牌图标 + iconSvg: ``, + url: 'https://duckduckgo.com/?q=' + } }; - // 获取DOM元素 - 基本元素 const searchInput = document.getElementById('search'); const searchBox = document.querySelector('.search-box'); @@ -906,8 +907,13 @@ document.addEventListener('DOMContentLoaded', () => { const searchSections = searchResultsPage.querySelectorAll('.search-section'); // 搜索引擎相关元素 - const searchIcon = document.querySelector('.search-icon'); const searchEngineToggle = document.querySelector('.search-engine-toggle'); + const searchEngineToggleIcon = searchEngineToggle + ? searchEngineToggle.querySelector('.search-engine-icon') + : null; + const searchEngineToggleLabel = searchEngineToggle + ? searchEngineToggle.querySelector('.search-engine-label') + : null; const searchEngineDropdown = document.querySelector('.search-engine-dropdown'); const searchEngineOptions = document.querySelectorAll('.search-engine-option'); @@ -1515,6 +1521,7 @@ document.addEventListener('DOMContentLoaded', () => { // 初始化搜索引擎下拉菜单事件 const toggleEngineDropdown = () => { + if (!searchEngineDropdown) return; const next = !searchEngineDropdown.classList.contains('active'); searchEngineDropdown.classList.toggle('active', next); if (searchBox) { @@ -1525,18 +1532,12 @@ document.addEventListener('DOMContentLoaded', () => { } }; - if (searchIcon) { - searchIcon.addEventListener('click', (e) => { - e.stopPropagation(); - toggleEngineDropdown(); - }); - } - if (searchEngineToggle) { searchEngineToggle.addEventListener('click', (e) => { e.stopPropagation(); toggleEngineDropdown(); }); + // 键盘可访问性:Enter/Space 触发 searchEngineToggle.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { @@ -1574,7 +1575,9 @@ document.addEventListener('DOMContentLoaded', () => { updateSearchEngineUI(); // 关闭下拉菜单 - searchEngineDropdown.classList.remove('active'); + if (searchEngineDropdown) { + searchEngineDropdown.classList.remove('active'); + } if (searchBox) { searchBox.classList.remove('dropdown-open'); } @@ -1584,6 +1587,7 @@ document.addEventListener('DOMContentLoaded', () => { // 点击页面其他位置关闭下拉菜单 document.addEventListener('click', () => { + if (!searchEngineDropdown) return; searchEngineDropdown.classList.remove('active'); if (searchBox) { searchBox.classList.remove('dropdown-open'); @@ -1603,24 +1607,26 @@ document.addEventListener('DOMContentLoaded', () => { } }); - // 更新搜索图标以反映当前搜索引擎 - if (searchIcon) { - // 清除所有类,保留基本的search-icon类 - const classList = searchIcon.className.split(' ').filter(cls => cls === 'search-icon'); - searchIcon.className = classList.join(' '); + // 更新搜索引擎按钮(方案B:前缀按钮显示当前引擎) + const engine = searchEngines[currentSearchEngine]; + if (!engine) return; + const displayName = engine.shortName || engine.name.replace(/搜索$/, ''); - // 添加当前搜索引擎的图标类 - const engine = searchEngines[currentSearchEngine]; - if (engine) { - const iconClasses = engine.icon.split(' '); - iconClasses.forEach(cls => { - searchIcon.classList.add(cls); - }); - - // 更新标题提示 - searchIcon.setAttribute('title', engine.name); + if (searchEngineToggleIcon) { + if (engine.iconSvg) { + searchEngineToggleIcon.className = 'search-engine-icon search-engine-icon-svg'; + searchEngineToggleIcon.innerHTML = engine.iconSvg; + } else { + searchEngineToggleIcon.innerHTML = ''; + searchEngineToggleIcon.className = `search-engine-icon ${engine.icon}`; } } + if (searchEngineToggleLabel) { + searchEngineToggleLabel.textContent = displayName; + } + if (searchEngineToggle) { + searchEngineToggle.setAttribute('aria-label', `当前搜索引擎:${engine.name},点击切换`); + } } // 执行搜索(根据选择的搜索引擎) diff --git a/templates/layouts/default.hbs b/templates/layouts/default.hbs index 22e268d..1e1b6be 100644 --- a/templates/layouts/default.hbs +++ b/templates/layouts/default.hbs @@ -98,22 +98,76 @@