diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 3d61084..26797fe 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -79,7 +79,11 @@ DOM or live LiteGraph state. native command/keybinding integration. - `ui_sidebar.js`, `ui_grid.js`, `ui_detail.js`, and `ui_gallery.js` own the primary model-browser surfaces, including the frosted-glass capsule parameter inspection - button with lightweight vector SVG icons and GPU-accelerated micro-interactions. + button with lightweight vector SVG icons and GPU-accelerated micro-interactions; + the sidebar bottom-left scan action (`#anomalous-scan-btn`) features a dedicated high-precision + vector radar SVG (`SCAN_RADAR_ICON_SVG`) with GPU-accelerated continuous sweep animation (`.anomalous-radar-spinning`), + completely decoupling from the settings modal's default layout reset button (`sidebarResetLayout: '📐 恢复默认窗口布局'`) + to eliminate visual collision and disambiguate user mental models. - `ui_gallery_detail.js` owns the studio modal Image Detail Workbench, multi-dimensional image navigation (keyboard shortcuts, floating glass arrows, left vertical thumbnail rail), streamlined workflow/material action buttons, diff --git a/web/modules/locales.js b/web/modules/locales.js index 1dc8eb9..75a07e7 100644 --- a/web/modules/locales.js +++ b/web/modules/locales.js @@ -636,7 +636,7 @@ export const i18n = { sidebarAssistant: '节点助手', sidebarPreflight: '📥 预检导入工作流', sidebarSettings: '设置中心', - sidebarScanWizard: '🔄 扫描向导 (Scan Wizard)', + sidebarScanWizard: '📡 模型扫描向导', sidebarScanComplete: '✅ 扫描/重命名已完成!数据已为您更新。', sidebarScanWizardTitle: '扫描向导', sidebarGlobalScanWizard: '全局扫描向导', @@ -708,7 +708,7 @@ export const i18n = { sidebarInfoComplete: '✅ 补全完成', sidebarAllInfoComplete: '✨ 所有模型信息已完整', sidebarUiScale: 'UI 缩放', - sidebarResetLayout: '🔄 重置界面布局', + sidebarResetLayout: '📐 恢复默认窗口布局', sidebarResetConfirm: '确认重置窗口位置、缩放和停靠状态吗?', sidebarInjectHash: '🟢 注入工作流哈希', sidebarSkipHash: '⚪ 不注入工作流哈希', @@ -1696,7 +1696,7 @@ export const i18n = { sidebarAssistant: 'Node Assistant', sidebarPreflight: '📥 Preflight Import', sidebarSettings: 'Settings Hub', - sidebarScanWizard: '🔄 Scan Wizard', + sidebarScanWizard: '📡 Model Scan Wizard', sidebarScanComplete: '✅ Scan/Rename completed! Data updated.', sidebarScanWizardTitle: 'Scan Wizard', sidebarGlobalScanWizard: 'Global Scan Wizard', @@ -1768,7 +1768,7 @@ export const i18n = { sidebarInfoComplete: '✅ Info Complete', sidebarAllInfoComplete: '✨ All Model Info is Complete', sidebarUiScale: 'UI Scale', - sidebarResetLayout: '🔄 Reset Layout', + sidebarResetLayout: '📐 Reset Window Layout', sidebarResetConfirm: 'Reset window position, scale and dock state?', sidebarInjectHash: '🟢 Inject Workflow Hash', sidebarSkipHash: '⚪ Skip Workflow Hash', diff --git a/web/modules/ui_sidebar.js b/web/modules/ui_sidebar.js index c241c46..77f54e1 100644 --- a/web/modules/ui_sidebar.js +++ b/web/modules/ui_sidebar.js @@ -10,7 +10,15 @@ import { updateScanProgress, finishScanProgress, failScanProgress } from './scan const t = (key, params) => translate(key, params); +const SCAN_RADAR_ICON_SVG = ``; +function setScanButtonState(btn, isScanning) { + if (!btn) return; + btn.innerHTML = SCAN_RADAR_ICON_SVG; + btn.classList.toggle('anomalous-radar-spinning', Boolean(isScanning)); + btn.style.opacity = isScanning ? '0.85' : '1'; + btn.style.animation = ''; +} export function createDOM() { localStorage.removeItem('anomalous_api_key'); @@ -342,7 +350,8 @@ export function createDOM() { const scanBtn = document.createElement('button'); scanBtn.id = 'anomalous-scan-btn'; scanBtn.title = t('sidebarScanWizard'); - scanBtn.innerHTML = `🔄`; + scanBtn.setAttribute('aria-label', t('sidebarScanWizard')); + setScanButtonState(scanBtn, false); scanBtn.style.background = 'transparent'; scanBtn.style.color = '#ccc'; scanBtn.style.border = 'none'; @@ -351,8 +360,8 @@ export function createDOM() { scanBtn.style.fontSize = '1.1em'; scanBtn.style.cursor = 'pointer'; scanBtn.style.transition = 'all 0.2s ease'; - scanBtn.onmouseover = () => { scanBtn.style.background = 'rgba(255,255,255,0.1)'; scanBtn.style.color = '#fff'; }; - scanBtn.onmouseout = () => { scanBtn.style.background = 'transparent'; scanBtn.style.color = '#ccc'; }; + scanBtn.onmouseover = () => { scanBtn.style.background = 'rgba(255,255,255,0.1)'; scanBtn.style.color = '#38bdf8'; }; + scanBtn.onmouseout = () => { scanBtn.style.background = 'transparent'; scanBtn.style.color = scanBtn.classList.contains('anomalous-radar-spinning') ? '#38bdf8' : '#ccc'; }; let isCurrentlyScanning = false; setInterval(async () => { @@ -383,12 +392,10 @@ export function createDOM() { if (isScanning && !isCurrentlyScanning) { isCurrentlyScanning = true; - scanBtn.innerHTML = `⏳`; - scanBtn.style.opacity = '0.7'; + setScanButtonState(scanBtn, true); } else if (!isScanning && isCurrentlyScanning) { isCurrentlyScanning = false; - scanBtn.innerHTML = `🔄`; - scanBtn.style.opacity = '1'; + setScanButtonState(scanBtn, false); finishScanProgress(); this.loadModels(); if (window.anomalous_reload_hashes) await window.anomalous_reload_hashes(); @@ -892,8 +899,7 @@ export function createDOM() { document.body.removeChild(wizard); if (typeof scanBtn !== 'undefined') { - scanBtn.innerHTML = `⏳`; - scanBtn.style.animation = 'anomalous-spin 2s linear infinite'; + setScanButtonState(scanBtn, true); } const customFolders = Array.from(selectedForScan.entries()).filter(([, files]) => files.size > 0); @@ -957,8 +963,7 @@ export function createDOM() { } if (typeof scanBtn !== 'undefined') { - scanBtn.innerHTML = `🔄`; - scanBtn.style.animation = ''; + setScanButtonState(scanBtn, false); } finishScanProgress(); if (enableAutoCheck && window.anomalous_resolve_all_missing_nodes) { @@ -994,8 +999,7 @@ export function createDOM() { if (data.status === 'ok') { updateScanProgress({ scanning: true, phase: 'preparing', recovered: data.recovered }); if (typeof scanBtn !== 'undefined') { - scanBtn.innerHTML = `⏳`; - scanBtn.style.animation = 'anomalous-spin 2s linear infinite'; + setScanButtonState(scanBtn, true); } // Start polling @@ -1014,8 +1018,7 @@ export function createDOM() { if (statusData.interrupted) failScanProgress(t('scanProgressInterrupted')); else finishScanProgress(); if (typeof scanBtn !== 'undefined') { - scanBtn.innerHTML = `🔄`; - scanBtn.style.animation = ''; + setScanButtonState(scanBtn, false); } if (enableAutoCheck && window.anomalous_resolve_all_missing_nodes) { @@ -1026,8 +1029,7 @@ export function createDOM() { } catch (err) { clearInterval(poll); if (typeof scanBtn !== 'undefined') { - scanBtn.innerHTML = `🔄`; - scanBtn.style.animation = ''; + setScanButtonState(scanBtn, false); } } }, 2000); @@ -1141,8 +1143,9 @@ export function createDOM() { updateLangClass(); modelsBtn.innerHTML = `🏠 ${t('models')}`; galleryBtn.innerHTML = `🖼️ ${t('gallery')}`; - scanBtn.title = t('scanTitle'); - scanBtn.innerHTML = `🔄`; + scanBtn.title = t('sidebarScanWizard'); + scanBtn.setAttribute('aria-label', t('sidebarScanWizard')); + setScanButtonState(scanBtn, isCurrentlyScanning); helpBtn.innerHTML = `❓ ${t('help')}`; nbBtn.title = t('recipeTitle'); nbBtn.innerHTML = `📑 ${t('recipeTitle')}`; diff --git a/web/styles.css b/web/styles.css index 532eeca..b9edc94 100644 --- a/web/styles.css +++ b/web/styles.css @@ -10531,5 +10531,61 @@ html.theme-abyssal-scarlet dialog.anomalous-gallery-viewer { box-shadow: 0 25px 80px rgba(0, 0, 0, 0.88), 0 0 40px rgba(220, 20, 60, 0.18) !important; } +/* 9. Model Scanner Radar Button & Spin Animation */ +#anomalous-scan-btn { + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + width: 32px !important; + height: 32px !important; + border-radius: 6px !important; + box-sizing: border-box !important; + vertical-align: middle !important; +} + +#anomalous-scan-btn svg { + transition: transform 0.2s ease, filter 0.2s ease; + transform-origin: center center; +} + +#anomalous-scan-btn:hover svg { + filter: drop-shadow(0 0 4px rgba(56, 189, 248, 0.6)); +} + +#anomalous-scan-btn.anomalous-radar-spinning { + color: #38bdf8 !important; + background: rgba(56, 189, 248, 0.12) !important; + border: 1px solid rgba(56, 189, 248, 0.35) !important; +} + +#anomalous-scan-btn.anomalous-radar-spinning svg { + animation: anomalous-radar-spin 1.4s linear infinite !important; + filter: drop-shadow(0 0 6px rgba(56, 189, 248, 0.75)) !important; +} + +@keyframes anomalous-radar-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +.theme-abyssal-scarlet #anomalous-scan-btn:hover svg { + filter: drop-shadow(0 0 5px rgba(255, 77, 109, 0.75)); +} + +.theme-abyssal-scarlet #anomalous-scan-btn.anomalous-radar-spinning { + color: #ff4d6d !important; + background: rgba(255, 77, 109, 0.14) !important; + border: 1px solid rgba(255, 77, 109, 0.4) !important; +} + +.theme-abyssal-scarlet #anomalous-scan-btn.anomalous-radar-spinning svg { + filter: drop-shadow(0 0 7px rgba(255, 77, 109, 0.85)) !important; +} + +