diff --git a/tour-comfy/car.html b/tour-comfy/car.html
index aa2f546..9048f31 100644
--- a/tour-comfy/car.html
+++ b/tour-comfy/car.html
@@ -2227,6 +2227,7 @@
// --- HYDRATION_START ---
const PRELOADED_IMAGES = [
+ "20260628_001216_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",
@@ -6923,16 +6924,24 @@
function getImageDisplayRect(img) {
if (!img || !img.naturalWidth) return null;
+
+ // If the image is zoomed, getBoundingClientRect() returns the SCALED dimensions.
+ // We need to account for this to find the actual visible image area.
+ const isZoomed = img.classList.contains('zoomed');
+ const scale = isZoomed ? 2.2 : 1.0;
+
const rect = img.getBoundingClientRect();
const container = img.parentElement;
const crect = container.getBoundingClientRect();
+ // The natural aspect ratio
const iw = img.naturalWidth;
const ih = img.naturalHeight;
- const cw = rect.width;
- const ch = rect.height;
-
const aspect = iw / ih;
+
+ // The container dimensions for the image (before scaling)
+ const cw = rect.width / scale;
+ const ch = rect.height / scale;
const caspect = cw / ch;
let dw, dh, dl, dt;
@@ -6948,10 +6957,41 @@
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.
+ // Adjust for scale: the visual center stays the same,
+ // but the width/height and offsets from the top-left change.
+ if (isZoomed) {
+ const centerRelX = cw / 2;
+ const centerRelY = ch / 2;
+
+ // dw, dh are unscaled visual dimensions of the content.
+ // dl, dt are unscaled offsets within the img element's box.
+
+ // We want the scaled versions.
+ dw *= scale;
+ dh *= scale;
+
+ // The top-left of the content relative to the center of the img element:
+ const contentRelCenterX = dl - centerRelX;
+ const contentRelCenterY = dt - centerRelY;
+
+ // The top-left of the content relative to the center of the img element (scaled):
+ const contentRelCenterXScaled = contentRelCenterX * scale;
+ const contentRelCenterYScaled = contentRelCenterY * scale;
+
+ // The new offsets relative to the top-left of the scaled img element:
+ // Actually, rect.left/top are already for the scaled element's bounding box.
+ // We just need to find where the content starts within that bounding box.
+ const finalDl = contentRelCenterXScaled + (rect.width / 2);
+ const finalDt = contentRelCenterYScaled + (rect.height / 2);
+
+ return {
+ left: rect.left - crect.left + finalDl,
+ top: rect.top - crect.top + finalDt,
+ width: dw,
+ height: dh
+ };
+ }
+
const finalLeft = rect.left - crect.left + dl;
const finalTop = rect.top - crect.top + dt;
@@ -7125,23 +7165,20 @@
const fname = lbNames[lbIdx];
if (document.getElementById('poseCanvas')) _removePoseOverlay(); // skeleton is per-image
document.getElementById('poseResults')?.remove();
- _fsModelFilename = fname; // keep faceswap/scenery/segment in sync
- 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);
- });
+ // Overlay re-sync is now handled by ResizeObserver
};
+ _fsModelFilename = fname; // keep faceswap/scenery/segment in sync
+ const isVid = isVideo(fname) || fileContentType[fname] === 'video';
+
// Show image dimensions
const dimEl = document.getElementById('lbImgDims');
if (dimEl) {
@@ -7163,6 +7200,10 @@
lbImgEl.style.display = '';
lbImgEl.classList.remove('zoomed');
lbImgEl.src = lbUrls[lbIdx];
+ // If it's already in cache, onload might not fire or already fired
+ if (lbImgEl.complete && lbImgEl.naturalWidth) {
+ lbImgEl.onload();
+ }
}
const backBtn = document.getElementById('lbBackBtn');
@@ -9878,6 +9919,20 @@
// Initialize
document.addEventListener('DOMContentLoaded', () => {
+ const img = document.getElementById('lbImg');
+ if (img) {
+ const ro = new ResizeObserver(() => {
+ if (_isStudioOpen()) {
+ requestAnimationFrame(() => {
+ updatePadPreview();
+ const cb = document.getElementById('sbCheckerboard');
+ if (cb && cb.checked) toggleCheckerboard(true);
+ });
+ }
+ });
+ ro.observe(img);
+ }
+
window.addEventListener('resize', () => {
if (_isStudioOpen()) {
requestAnimationFrame(() => {
@@ -10134,7 +10189,11 @@
}
function toggleImageZoom() {
- document.getElementById('lbImg')?.classList.toggle('zoomed');
+ const img = document.getElementById('lbImg');
+ if (img) {
+ img.classList.toggle('zoomed');
+ // The ResizeObserver will handle the follow-up logic automatically.
+ }
}
// ---- sidebar panels ----
@@ -11731,7 +11790,7 @@
if (btn) btn.disabled = false;
}
- function toggleCheckerboard(on) {
+ function toggleCheckerboard(on, isRetry=false) {
const viewer = document.getElementById('studioViewer');
const cbLayer = document.getElementById('studioCheckerboard');
const img = document.getElementById('lbImg');