diff --git a/tour-comfy/car.html b/tour-comfy/car.html index 655e959..aa2f546 100644 --- a/tour-comfy/car.html +++ b/tour-comfy/car.html @@ -169,10 +169,10 @@ .pad-preview-overlay { position: absolute; - background: rgba(245,158,11,0.25); + background: rgba(245,158,11,0.4); border: 1px dashed #f59e0b; pointer-events: none; - z-index: 5; + z-index: 10; } .action-btn { @@ -1323,6 +1323,9 @@ position: relative; height: 100%; border-radius: 6px; overflow: hidden; } .studio-viewer.show-checker { + background-color: transparent; + } + #studioCheckerboard { background-color: #888; background-image: linear-gradient(45deg,#555 25%,transparent 25%), @@ -1331,11 +1334,13 @@ linear-gradient(-45deg,transparent 75%,#555 75%); background-size: 16px 16px; background-position: 0 0, 0 8px, 8px -8px, -8px 0; + z-index: 1; } #lbImg { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: 4px; cursor: zoom-in; transition: transform 0.25s; display: block; + z-index: 2; position: relative; } #lbImg.zoomed { cursor: zoom-out; transform: scale(2.2); transform-origin: center; } #lbVideo { max-width: 100%; max-height: 100%; border-radius: 4px; } @@ -1960,6 +1965,7 @@ onblur="updateGroupName()" onkeydown="if(event.key==='Enter')this.blur()" title="Group name (applies to all images in this group)" /> +
@@ -1975,6 +1981,7 @@
+
Hidden from preview
@@ -2051,7 +2058,7 @@ - + @@ -2220,8 +2227,8 @@ // --- HYDRATION_START --- const PRELOADED_IMAGES = [ - "20260627_232843_pad_20260626_035101_pad_20260626_034830_jb.nobg.png", "20260627_232502_pad_20260626_035101_pad_20260626_034830_jb.nobg.png", + "20260627_232843_pad_20260626_035101_pad_20260626_034830_jb.nobg.png", "20260627_232526_pad_20260626_035101_pad_20260626_034830_jb.nobg.png", "20260627_232302_pad_20260627_232133_image.png", "20260627_232238_pad_20260627_232133_image.png", @@ -6914,19 +6921,61 @@ updatePadPreview(); } + function getImageDisplayRect(img) { + if (!img || !img.naturalWidth) return null; + const rect = img.getBoundingClientRect(); + const container = img.parentElement; + const crect = container.getBoundingClientRect(); + + const iw = img.naturalWidth; + const ih = img.naturalHeight; + const cw = rect.width; + const ch = rect.height; + + const aspect = iw / ih; + const caspect = cw / ch; + + let dw, dh, dl, dt; + if (aspect > caspect) { + dw = cw; + dh = cw / aspect; + dl = 0; + dt = (ch - dh) / 2; + } else { + dh = ch; + dw = ch * aspect; + dt = 0; + dl = (cw - dw) / 2; + } + + // Adjust relative to container if needed, but since img is a flex item + // centered in .studio-viewer, its rect.left/top might already be offset. + // Actually, dl and dt above are relative to the img element's box. + // We want coordinates relative to .studio-viewer. + const finalLeft = rect.left - crect.left + dl; + const finalTop = rect.top - crect.top + dt; + + return { left: finalLeft, top: finalTop, width: dw, height: dh }; + } + function updatePadPreview() { const viewer = document.getElementById('studioViewer'); - if (!viewer) return; + const img = document.getElementById('lbImg'); + if (!viewer || !img || img.style.display === 'none') return; + // Clean up existing preview overlays viewer.querySelectorAll('.pad-preview-overlay').forEach(el => el.remove()); + const rect = getImageDisplayRect(img); + if (!rect) return; + const isManual = !!document.getElementById('padBar'); const getV = (side) => { const el = document.getElementById(isManual ? 'pad' + side : 'sbPad' + side); return el ? el.value : '0'; }; - const sides = ['Left', 'Right']; + const sides = ['Left', 'Right', 'Top', 'Bottom']; sides.forEach(side => { const vStr = getV(side); if (!vStr || vStr === '0' || vStr === '0.00') return; @@ -6934,19 +6983,24 @@ const val = parseFloat(vStr); if (isNaN(val) || val <= 0) return; - const isPercent = vStr.includes('%') || vStr.includes('.'); - const width = isPercent ? (val + '%') : (val + 'px'); - + const isPercent = vStr.includes('%') || (vStr.indexOf('.') !== -1 && !isNaN(vStr)); const overlay = document.createElement('div'); overlay.className = 'pad-preview-overlay'; - overlay.style.top = '0'; - overlay.style.bottom = '0'; - if (side === 'Left') { - overlay.style.left = '0'; - overlay.style.width = width; + + if (side === 'Left' || side === 'Right') { + const w = isPercent ? (rect.width * val / 100) : val; + overlay.style.width = w + 'px'; + overlay.style.top = rect.top + 'px'; + overlay.style.height = rect.height + 'px'; + if (side === 'Left') overlay.style.left = (rect.left - w) + 'px'; + else overlay.style.left = (rect.left + rect.width) + 'px'; } else { - overlay.style.right = '0'; - overlay.style.width = width; + const h = isPercent ? (rect.height * val / 100) : val; + overlay.style.height = h + 'px'; + overlay.style.left = rect.left + 'px'; + overlay.style.width = rect.width + 'px'; + if (side === 'Top') overlay.style.top = (rect.top - h) + 'px'; + else overlay.style.top = (rect.top + rect.height) + 'px'; } viewer.appendChild(overlay); }); @@ -7075,6 +7129,29 @@ const isVid = isVideo(fname) || fileContentType[fname] === 'video'; const lbImgEl = document.getElementById('lbImg'); const lbVideoEl = document.getElementById('lbVideo'); + + lbImgEl.onload = () => { + const dimEl = document.getElementById('lbImgDims'); + if (dimEl) dimEl.textContent = `${lbImgEl.naturalWidth} x ${lbImgEl.naturalHeight}`; + + // Use requestAnimationFrame to ensure layout has updated before measuring for checkerboard/previews + requestAnimationFrame(() => { + updatePadPreview(); + const cb = document.getElementById('sbCheckerboard'); + if (cb && cb.checked) toggleCheckerboard(true); + }); + }; + + // Show image dimensions + const dimEl = document.getElementById('lbImgDims'); + if (dimEl) { + if (!isVid && lbImgEl.naturalWidth) { + dimEl.textContent = `${lbImgEl.naturalWidth} x ${lbImgEl.naturalHeight}`; + } else { + dimEl.textContent = ''; + } + } + if (isVid) { lbImgEl.style.display = 'none'; lbVideoEl.style.display = ''; @@ -8201,7 +8278,7 @@ return ``; }; bar.innerHTML = - `Pad canvas (px or %):` + `Expand canvas (px or %):` + numInput('padTop','↑') + numInput('padRight','→') + numInput('padBottom','↓') @@ -8216,7 +8293,7 @@ + `` + `` - + ``; + + ``; viewer.appendChild(bar); updatePadPreview(); } @@ -8279,12 +8356,11 @@ refreshNow(true); } else { showToast('Padded', 'success'); - lbUrls[lbIdx] = IMAGE_FOLDER + fname + '?t=' + Date.now(); - updateStudio(); + _bustStudioImage(fname); } } else { showToast('Pad failed: ' + await r.text(), 'error'); - if (btn) { btn.disabled = false; btn.textContent = 'Pad'; } + if (btn) { btn.disabled = false; btn.textContent = 'Expand'; } } } catch (e) { showToast('Pad error: ' + e, 'error'); @@ -9802,6 +9878,15 @@ // Initialize document.addEventListener('DOMContentLoaded', () => { + window.addEventListener('resize', () => { + if (_isStudioOpen()) { + requestAnimationFrame(() => { + updatePadPreview(); + const cb = document.getElementById('sbCheckerboard'); + if (cb && cb.checked) toggleCheckerboard(true); + }); + } + }); loadPromptHistory(); loadImages(); autoRefreshTimer = setInterval(_timedRefresh, REFRESH_INTERVAL * 1000); @@ -11648,7 +11733,28 @@ function toggleCheckerboard(on) { const viewer = document.getElementById('studioViewer'); + const cbLayer = document.getElementById('studioCheckerboard'); + const img = document.getElementById('lbImg'); + if (viewer) viewer.classList.toggle('show-checker', on); + + if (cbLayer) { + if (on && img && img.style.display !== 'none') { + const rect = getImageDisplayRect(img); + if (rect) { + cbLayer.style.display = 'block'; + cbLayer.style.left = rect.left + 'px'; + cbLayer.style.top = rect.top + 'px'; + cbLayer.style.width = rect.width + 'px'; + cbLayer.style.height = rect.height + 'px'; + } else { + cbLayer.style.display = 'none'; + } + } else { + cbLayer.style.display = 'none'; + } + } + const cb = document.getElementById('sbCheckerboard'); if (cb) cb.checked = on; }