From 3c29cc0142fb98f5c14b0832ee3ced87ffd09e22 Mon Sep 17 00:00:00 2001 From: DemonGatanjieu <1027066412@qq.com> Date: Fri, 4 Sep 2026 14:44:11 +0800 Subject: [PATCH] style(workbench): move filmstrip to left rail and enlarge thumbnail cards --- ARCHITECTURE.md | 2 +- web/modules/locales.js | 4 +- web/modules/ui_gallery_detail.js | 29 ++++++--- web/styles.css | 106 ++++++++++++++++++++++--------- 4 files changed, 99 insertions(+), 42 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index b19a90d..a5628d8 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -75,7 +75,7 @@ DOM or live LiteGraph state. primary model-browser surfaces. - `ui_gallery_detail.js` owns the full-screen / studio Image Detail Workbench, multi-dimensional image navigation (keyboard shortcuts, floating glass arrows, - bottom filmstrip rail), and segmented Bento parameter inspection with ComfyUI + left vertical thumbnail rail), and segmented Bento parameter inspection with ComfyUI canvas injection (`app.loadGraphData`). - `ui_notebooks.js`, `ui_recipes.js`, and `ui_recipe_detail.js` own Workspace presentation, featuring Bento-style visual cards, quick spec tags, real-time environment readiness indicators (🟢/🟡/🔴), one-click batch model matching, and lazy DOM rendering for node parameter trees. diff --git a/web/modules/locales.js b/web/modules/locales.js index 3129b8c..80e105d 100644 --- a/web/modules/locales.js +++ b/web/modules/locales.js @@ -88,7 +88,7 @@ export const i18n = { workbenchCopySuccess: '✅ 分享文本已复制', workbenchNext: '下一张 (→)', workbenchPrev: '上一张 (←)', - workbenchFilmstrip: '底栏缩略图', + workbenchFilmstrip: '侧栏缩略图', workbenchTabOverview: '核心参数', workbenchTabPrompts: '提示词工坊', workbenchTabModels: '模型资产', @@ -954,7 +954,7 @@ export const i18n = { workbenchCopySuccess: '✅ Share text copied', workbenchNext: 'Next (→)', workbenchPrev: 'Previous (←)', - workbenchFilmstrip: 'Filmstrip', + workbenchFilmstrip: 'Thumbnail Rail', workbenchTabOverview: 'Key Specs', workbenchTabPrompts: 'Prompts', workbenchTabModels: 'Models & LoRA', diff --git a/web/modules/ui_gallery_detail.js b/web/modules/ui_gallery_detail.js index 002b285..f0cd9a9 100644 --- a/web/modules/ui_gallery_detail.js +++ b/web/modules/ui_gallery_detail.js @@ -4,7 +4,7 @@ * Features: * - Fullscreen studio workspace / windowed modal toggle * - Prev / Next image navigation (keyboard arrows, floating glass arrows, counter jump) - * - Collapsible bottom thumbnail filmstrip with smooth auto-centering + * - Collapsible left thumbnail rail with smooth vertical auto-centering * - High-speed in-memory LRU metadata cache (imageMetadataCache) * - Preloading of adjacent images and AbortController request cancellation * - Segmented Bento Inspector: Key Specs Bento grid, Prompts Station, Models & LoRA with weight pills, Node structure @@ -225,7 +225,7 @@ function buildWorkbenchHeader(item, index, total, onNavigate) { ? 'anomalous-workbench-tool-btn is-active' : 'anomalous-workbench-tool-btn'; filmstripToggle.innerHTML = '🎞️'; - filmstripToggle.title = t('workbenchFilmstrip') || '底栏缩略图'; + filmstripToggle.title = t('workbenchFilmstrip') || (window.anomalous_browser_lang === 'zh' ? '侧栏缩略图' : 'Thumbnail Rail'); filmstripToggle.onclick = () => { wb.isFilmstripVisible = !wb.isFilmstripVisible; filmstripToggle.classList.toggle('is-active', wb.isFilmstripVisible); @@ -338,11 +338,14 @@ function setupStagePanZoom(stage, img) { } /** - * Build Bottom Filmstrip Rail + * Build Left Vertical Filmstrip Rail */ function buildFilmstripRail(items, currentIndex, onNavigate) { const rail = document.createElement('div'); rail.className = 'anomalous-workbench-filmstrip'; + if (wb && wb.isFilmstripVisible === false) { + rail.classList.add('is-hidden'); + } const track = document.createElement('div'); track.className = 'anomalous-workbench-filmstrip-track'; @@ -360,6 +363,11 @@ function buildFilmstripRail(items, currentIndex, onNavigate) { thumbImg.alt = ''; thumbWrap.appendChild(thumbImg); + const badge = document.createElement('span'); + badge.className = 'anomalous-workbench-thumb-index'; + badge.textContent = String(idx + 1); + thumbWrap.appendChild(badge); + thumbWrap.onclick = (e) => { e.stopPropagation(); if (idx !== currentIndex) onNavigate(idx); @@ -367,10 +375,12 @@ function buildFilmstripRail(items, currentIndex, onNavigate) { track.appendChild(thumbWrap); - // Auto-center active thumbnail + // Auto-center active thumbnail vertically if (idx === currentIndex) { requestAnimationFrame(() => { - thumbWrap.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }); + setTimeout(() => { + thumbWrap.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' }); + }, 40); }); } }); @@ -758,7 +768,7 @@ async function loadWorkbenchImage(index) { thumbs.forEach((el, i) => { el.classList.toggle('is-active', i === index); if (i === index) { - el.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }); + el.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' }); } }); } @@ -1321,14 +1331,15 @@ export async function showImageWorkbench(owner, sourceImage, imageUrl, options = zoomBar.append(fitBtn, actualBtn, zoomOutBtn, zoomInBtn); stageArea.appendChild(zoomBar); - // Bottom Filmstrip + // 4A. Left Vertical Filmstrip Rail const filmstripRail = buildFilmstripRail(items, currentIndex, (idx) => loadWorkbenchImage(idx)); wb.filmstripEl = filmstripRail; - stageArea.appendChild(filmstripRail); + bodyContainer.appendChild(filmstripRail); + // 4B. Center Canvas Stage Area bodyContainer.appendChild(stageArea); - // 4B. Right Inspector Panel + // 4C. Right Inspector Panel const inspector = document.createElement('div'); inspector.className = 'anomalous-workbench-inspector'; diff --git a/web/styles.css b/web/styles.css index 5d00a31..1f4b4b9 100644 --- a/web/styles.css +++ b/web/styles.css @@ -5556,10 +5556,10 @@ dialog.anomalous-gallery-viewer::backdrop { transform: scale(1.05); } -/* Main Body: Stage Area + Inspector */ +/* Main Body: Left Thumbnail Rail + Center Stage Area + Right Inspector */ .anomalous-workbench-body { display: grid; - grid-template-columns: minmax(0, 1.45fr) minmax(460px, 0.95fr); + grid-template-columns: auto minmax(0, 1.45fr) minmax(460px, 0.95fr); flex: 1; min-height: 0; overflow: hidden; @@ -5635,7 +5635,7 @@ dialog.anomalous-gallery-viewer::backdrop { /* Floating Zoom Bar */ .anomalous-workbench-zoom-bar { position: absolute; - bottom: 86px; + bottom: 18px; right: 18px; z-index: 12; display: flex; @@ -5666,45 +5666,69 @@ dialog.anomalous-gallery-viewer::backdrop { color: #fff; } -/* Bottom Filmstrip Rail */ +/* Left Filmstrip Rail */ .anomalous-workbench-filmstrip { - flex: 0 0 82px; - height: 82px; - padding: 8px 16px; - border-top: 1px solid rgba(255, 255, 255, 0.08); - background: #0b0d13; - overflow-x: auto; - overflow-y: hidden; - transition: all 0.22s cubic-bezier(0.16, 1, 0.3, 1); + width: 116px; + height: 100%; + min-height: 0; + padding: 12px 10px; + border-right: 1px solid rgba(255, 255, 255, 0.08); + background: #080a0f; + overflow-x: hidden; + overflow-y: auto; + box-sizing: border-box; + transition: width 0.22s cubic-bezier(0.16, 1, 0.3, 1), + padding 0.22s cubic-bezier(0.16, 1, 0.3, 1), + opacity 0.18s ease; } .anomalous-workbench-filmstrip.is-hidden { - flex: 0 0 0px; - height: 0px; - padding: 0; - border-top: none; - overflow: hidden; - opacity: 0; + width: 0 !important; + min-width: 0 !important; + padding: 0 !important; + border-right: none !important; + overflow: hidden !important; + opacity: 0 !important; + pointer-events: none !important; +} + +.anomalous-workbench-filmstrip::-webkit-scrollbar { + width: 4px; +} + +.anomalous-workbench-filmstrip::-webkit-scrollbar-track { + background: transparent; +} + +.anomalous-workbench-filmstrip::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.14); + border-radius: 4px; +} + +.anomalous-workbench-filmstrip::-webkit-scrollbar-thumb:hover { + background: rgba(45, 212, 191, 0.45); } .anomalous-workbench-filmstrip-track { display: flex; + flex-direction: column; align-items: center; - gap: 10px; - height: 100%; + gap: 12px; + width: 100%; } .anomalous-workbench-filmstrip-thumb { position: relative; - flex: 0 0 64px; - width: 64px; - height: 64px; - border-radius: 8px; + flex: 0 0 92px; + width: 92px; + height: 92px; + border-radius: 10px; overflow: hidden; border: 2px solid rgba(255, 255, 255, 0.1); - background: #141720; + background: #12151e; cursor: pointer; - transition: all 0.18s cubic-bezier(0.16, 1, 0.3, 1); + box-sizing: border-box; + transition: border-color 0.18s ease, transform 0.18s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.18s ease; } .anomalous-workbench-filmstrip-thumb img { @@ -5715,14 +5739,36 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-workbench-filmstrip-thumb:hover { - border-color: rgba(255, 255, 255, 0.35); - transform: translateY(-2px); + border-color: rgba(255, 255, 255, 0.45); + transform: scale(1.04); } .anomalous-workbench-filmstrip-thumb.is-active { border-color: #2dd4bf; - box-shadow: 0 0 14px rgba(45, 212, 191, 0.5); - transform: scale(1.06); + box-shadow: 0 0 16px rgba(45, 212, 191, 0.55), inset 0 0 0 1px #2dd4bf; + transform: scale(1.04); +} + +.anomalous-workbench-thumb-index { + position: absolute; + bottom: 4px; + right: 4px; + font-size: 10px; + font-weight: 700; + line-height: 1; + color: #94a3b8; + background: rgba(10, 13, 20, 0.82); + backdrop-filter: blur(4px); + padding: 2px 5px; + border-radius: 4px; + pointer-events: none; + transition: color 0.15s, background 0.15s; +} + +.anomalous-workbench-filmstrip-thumb.is-active .anomalous-workbench-thumb-index { + color: #2dd4bf; + background: rgba(13, 23, 27, 0.92); + font-weight: 800; } /* Right Inspector Panel */