reorder
This commit is contained in:
@@ -169,10 +169,10 @@
|
|||||||
|
|
||||||
.pad-preview-overlay {
|
.pad-preview-overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background: rgba(245,158,11,0.25);
|
background: rgba(245,158,11,0.4);
|
||||||
border: 1px dashed #f59e0b;
|
border: 1px dashed #f59e0b;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
z-index: 5;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-btn {
|
.action-btn {
|
||||||
@@ -1323,6 +1323,9 @@
|
|||||||
position: relative; height: 100%; border-radius: 6px; overflow: hidden;
|
position: relative; height: 100%; border-radius: 6px; overflow: hidden;
|
||||||
}
|
}
|
||||||
.studio-viewer.show-checker {
|
.studio-viewer.show-checker {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
#studioCheckerboard {
|
||||||
background-color: #888;
|
background-color: #888;
|
||||||
background-image:
|
background-image:
|
||||||
linear-gradient(45deg,#555 25%,transparent 25%),
|
linear-gradient(45deg,#555 25%,transparent 25%),
|
||||||
@@ -1331,11 +1334,13 @@
|
|||||||
linear-gradient(-45deg,transparent 75%,#555 75%);
|
linear-gradient(-45deg,transparent 75%,#555 75%);
|
||||||
background-size: 16px 16px;
|
background-size: 16px 16px;
|
||||||
background-position: 0 0, 0 8px, 8px -8px, -8px 0;
|
background-position: 0 0, 0 8px, 8px -8px, -8px 0;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
#lbImg {
|
#lbImg {
|
||||||
max-width: 100%; max-height: 100%;
|
max-width: 100%; max-height: 100%;
|
||||||
object-fit: contain; border-radius: 4px;
|
object-fit: contain; border-radius: 4px;
|
||||||
cursor: zoom-in; transition: transform 0.25s; display: block;
|
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; }
|
#lbImg.zoomed { cursor: zoom-out; transform: scale(2.2); transform-origin: center; }
|
||||||
#lbVideo { max-width: 100%; max-height: 100%; border-radius: 4px; }
|
#lbVideo { max-width: 100%; max-height: 100%; border-radius: 4px; }
|
||||||
@@ -1960,6 +1965,7 @@
|
|||||||
onblur="updateGroupName()" onkeydown="if(event.key==='Enter')this.blur()"
|
onblur="updateGroupName()" onkeydown="if(event.key==='Enter')this.blur()"
|
||||||
title="Group name (applies to all images in this group)" />
|
title="Group name (applies to all images in this group)" />
|
||||||
<span id="lbPoseTag" style="font-size:11px;color:#f59e0b;display:none"></span>
|
<span id="lbPoseTag" style="font-size:11px;color:#f59e0b;display:none"></span>
|
||||||
|
<span id="lbImgDims" style="font-size:11px;color:#444;margin:0 8px"></span>
|
||||||
<span id="lbCounter" style="font-size:12px;color:#555"></span>
|
<span id="lbCounter" style="font-size:12px;color:#555"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="studio-topbar-right">
|
<div class="studio-topbar-right">
|
||||||
@@ -1975,6 +1981,7 @@
|
|||||||
<div class="studio-viewer-row">
|
<div class="studio-viewer-row">
|
||||||
<button class="studio-nav-btn" id="lbPrev" onclick="lbNav(-1)">‹</button>
|
<button class="studio-nav-btn" id="lbPrev" onclick="lbNav(-1)">‹</button>
|
||||||
<div class="studio-viewer" id="studioViewer">
|
<div class="studio-viewer" id="studioViewer">
|
||||||
|
<div id="studioCheckerboard" style="display:none;position:absolute;pointer-events:none;z-index:1"></div>
|
||||||
<img id="lbImg" src="" alt="" onclick="toggleImageZoom(this)" />
|
<img id="lbImg" src="" alt="" onclick="toggleImageZoom(this)" />
|
||||||
<video id="lbVideo" style="display:none;max-width:100%;max-height:100%;border-radius:4px" controls autoplay muted loop></video>
|
<video id="lbVideo" style="display:none;max-width:100%;max-height:100%;border-radius:4px" controls autoplay muted loop></video>
|
||||||
<div class="lb-hidden-badge">Hidden from preview</div>
|
<div class="lb-hidden-badge">Hidden from preview</div>
|
||||||
@@ -2051,7 +2058,7 @@
|
|||||||
<button class="sb-btn" id="lbCropBtn" style="display:none" onclick="lbAutoCrop()" title="Crop away transparent border">Crop</button>
|
<button class="sb-btn" id="lbCropBtn" style="display:none" onclick="lbAutoCrop()" title="Crop away transparent border">Crop</button>
|
||||||
<button class="sb-btn" id="lbDuplicateBtn" onclick="lbDuplicate()" title="Duplicate image into same group">Duplicate</button>
|
<button class="sb-btn" id="lbDuplicateBtn" onclick="lbDuplicate()" title="Duplicate image into same group">Duplicate</button>
|
||||||
<button class="sb-btn" id="lbManualCropBtn" onclick="startManualCrop()" title="Drag to crop image">Crop…</button>
|
<button class="sb-btn" id="lbManualCropBtn" onclick="startManualCrop()" title="Drag to crop image">Crop…</button>
|
||||||
<button class="sb-btn" id="lbManualPadBtn" onclick="startManualPad()" title="Expand canvas by padding each side">Pad…</button>
|
<button class="sb-btn" id="lbManualPadBtn" onclick="startManualPad()" title="Expand canvas by padding each side">Expand…</button>
|
||||||
<button class="sb-btn" id="lbRotateLeftBtn" onclick="lbRotate(-90)" title="Rotate 90° counter-clockwise">⟲ 90°</button>
|
<button class="sb-btn" id="lbRotateLeftBtn" onclick="lbRotate(-90)" title="Rotate 90° counter-clockwise">⟲ 90°</button>
|
||||||
<button class="sb-btn" id="lbRotateRightBtn" onclick="lbRotate(90)" title="Rotate 90° clockwise">⟳ 90°</button>
|
<button class="sb-btn" id="lbRotateRightBtn" onclick="lbRotate(90)" title="Rotate 90° clockwise">⟳ 90°</button>
|
||||||
<button class="sb-btn" id="lbPoseBtn" onclick="lbTogglePose()" title="Preview body pose skeleton">Pose</button>
|
<button class="sb-btn" id="lbPoseBtn" onclick="lbTogglePose()" title="Preview body pose skeleton">Pose</button>
|
||||||
@@ -2220,8 +2227,8 @@
|
|||||||
|
|
||||||
// --- HYDRATION_START ---
|
// --- HYDRATION_START ---
|
||||||
const PRELOADED_IMAGES = [
|
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_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_232526_pad_20260626_035101_pad_20260626_034830_jb.nobg.png",
|
||||||
"20260627_232302_pad_20260627_232133_image.png",
|
"20260627_232302_pad_20260627_232133_image.png",
|
||||||
"20260627_232238_pad_20260627_232133_image.png",
|
"20260627_232238_pad_20260627_232133_image.png",
|
||||||
@@ -6914,19 +6921,61 @@
|
|||||||
updatePadPreview();
|
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() {
|
function updatePadPreview() {
|
||||||
const viewer = document.getElementById('studioViewer');
|
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
|
// Clean up existing preview overlays
|
||||||
viewer.querySelectorAll('.pad-preview-overlay').forEach(el => el.remove());
|
viewer.querySelectorAll('.pad-preview-overlay').forEach(el => el.remove());
|
||||||
|
|
||||||
|
const rect = getImageDisplayRect(img);
|
||||||
|
if (!rect) return;
|
||||||
|
|
||||||
const isManual = !!document.getElementById('padBar');
|
const isManual = !!document.getElementById('padBar');
|
||||||
const getV = (side) => {
|
const getV = (side) => {
|
||||||
const el = document.getElementById(isManual ? 'pad' + side : 'sbPad' + side);
|
const el = document.getElementById(isManual ? 'pad' + side : 'sbPad' + side);
|
||||||
return el ? el.value : '0';
|
return el ? el.value : '0';
|
||||||
};
|
};
|
||||||
|
|
||||||
const sides = ['Left', 'Right'];
|
const sides = ['Left', 'Right', 'Top', 'Bottom'];
|
||||||
sides.forEach(side => {
|
sides.forEach(side => {
|
||||||
const vStr = getV(side);
|
const vStr = getV(side);
|
||||||
if (!vStr || vStr === '0' || vStr === '0.00') return;
|
if (!vStr || vStr === '0' || vStr === '0.00') return;
|
||||||
@@ -6934,19 +6983,24 @@
|
|||||||
const val = parseFloat(vStr);
|
const val = parseFloat(vStr);
|
||||||
if (isNaN(val) || val <= 0) return;
|
if (isNaN(val) || val <= 0) return;
|
||||||
|
|
||||||
const isPercent = vStr.includes('%') || vStr.includes('.');
|
const isPercent = vStr.includes('%') || (vStr.indexOf('.') !== -1 && !isNaN(vStr));
|
||||||
const width = isPercent ? (val + '%') : (val + 'px');
|
|
||||||
|
|
||||||
const overlay = document.createElement('div');
|
const overlay = document.createElement('div');
|
||||||
overlay.className = 'pad-preview-overlay';
|
overlay.className = 'pad-preview-overlay';
|
||||||
overlay.style.top = '0';
|
|
||||||
overlay.style.bottom = '0';
|
if (side === 'Left' || side === 'Right') {
|
||||||
if (side === 'Left') {
|
const w = isPercent ? (rect.width * val / 100) : val;
|
||||||
overlay.style.left = '0';
|
overlay.style.width = w + 'px';
|
||||||
overlay.style.width = width;
|
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 {
|
} else {
|
||||||
overlay.style.right = '0';
|
const h = isPercent ? (rect.height * val / 100) : val;
|
||||||
overlay.style.width = width;
|
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);
|
viewer.appendChild(overlay);
|
||||||
});
|
});
|
||||||
@@ -7075,6 +7129,29 @@
|
|||||||
const isVid = isVideo(fname) || fileContentType[fname] === 'video';
|
const isVid = isVideo(fname) || fileContentType[fname] === 'video';
|
||||||
const lbImgEl = document.getElementById('lbImg');
|
const lbImgEl = document.getElementById('lbImg');
|
||||||
const lbVideoEl = document.getElementById('lbVideo');
|
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) {
|
if (isVid) {
|
||||||
lbImgEl.style.display = 'none';
|
lbImgEl.style.display = 'none';
|
||||||
lbVideoEl.style.display = '';
|
lbVideoEl.style.display = '';
|
||||||
@@ -8201,7 +8278,7 @@
|
|||||||
return `<label style="display:flex;align-items:center;gap:3px;font-size:11px;color:#ccc">${label}<input type="text" id="${id}" value="0.00" oninput="${onInput}" onblur="${onBlur}" style="width:52px;background:#111;border:1px solid #333;color:#ccc;border-radius:4px;padding:2px 4px;font-size:11px;text-align:right" placeholder="px or %"></label>`;
|
return `<label style="display:flex;align-items:center;gap:3px;font-size:11px;color:#ccc">${label}<input type="text" id="${id}" value="0.00" oninput="${onInput}" onblur="${onBlur}" style="width:52px;background:#111;border:1px solid #333;color:#ccc;border-radius:4px;padding:2px 4px;font-size:11px;text-align:right" placeholder="px or %"></label>`;
|
||||||
};
|
};
|
||||||
bar.innerHTML =
|
bar.innerHTML =
|
||||||
`<span style="flex:1;font-size:11px;color:#aaa">Pad canvas (px or %):</span>`
|
`<span style="flex:1;font-size:11px;color:#aaa">Expand canvas (px or %):</span>`
|
||||||
+ numInput('padTop','↑')
|
+ numInput('padTop','↑')
|
||||||
+ numInput('padRight','→')
|
+ numInput('padRight','→')
|
||||||
+ numInput('padBottom','↓')
|
+ numInput('padBottom','↓')
|
||||||
@@ -8216,7 +8293,7 @@
|
|||||||
+ `<label style="display:flex;align-items:center;gap:4px;font-size:11px;color:#ccc;cursor:pointer" title="Instruct the model to fill in the padded area">`
|
+ `<label style="display:flex;align-items:center;gap:4px;font-size:11px;color:#ccc;cursor:pointer" title="Instruct the model to fill in the padded area">`
|
||||||
+ `<input type="checkbox" id="padOutpaint" style="cursor:pointer">Outpaint</label>`
|
+ `<input type="checkbox" id="padOutpaint" style="cursor:pointer">Outpaint</label>`
|
||||||
+ `<button class="sb-btn" onclick="cancelManualPad()">Cancel</button>`
|
+ `<button class="sb-btn" onclick="cancelManualPad()">Cancel</button>`
|
||||||
+ `<button class="sb-btn primary" id="padConfirmBtn" onclick="confirmManualPad()">Pad</button>`;
|
+ `<button class="sb-btn primary" id="padConfirmBtn" onclick="confirmManualPad()">Expand</button>`;
|
||||||
viewer.appendChild(bar);
|
viewer.appendChild(bar);
|
||||||
updatePadPreview();
|
updatePadPreview();
|
||||||
}
|
}
|
||||||
@@ -8279,12 +8356,11 @@
|
|||||||
refreshNow(true);
|
refreshNow(true);
|
||||||
} else {
|
} else {
|
||||||
showToast('Padded', 'success');
|
showToast('Padded', 'success');
|
||||||
lbUrls[lbIdx] = IMAGE_FOLDER + fname + '?t=' + Date.now();
|
_bustStudioImage(fname);
|
||||||
updateStudio();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
showToast('Pad failed: ' + await r.text(), 'error');
|
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) {
|
} catch (e) {
|
||||||
showToast('Pad error: ' + e, 'error');
|
showToast('Pad error: ' + e, 'error');
|
||||||
@@ -9802,6 +9878,15 @@
|
|||||||
|
|
||||||
// Initialize
|
// Initialize
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
if (_isStudioOpen()) {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
updatePadPreview();
|
||||||
|
const cb = document.getElementById('sbCheckerboard');
|
||||||
|
if (cb && cb.checked) toggleCheckerboard(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
loadPromptHistory();
|
loadPromptHistory();
|
||||||
loadImages();
|
loadImages();
|
||||||
autoRefreshTimer = setInterval(_timedRefresh, REFRESH_INTERVAL * 1000);
|
autoRefreshTimer = setInterval(_timedRefresh, REFRESH_INTERVAL * 1000);
|
||||||
@@ -11648,7 +11733,28 @@
|
|||||||
|
|
||||||
function toggleCheckerboard(on) {
|
function toggleCheckerboard(on) {
|
||||||
const viewer = document.getElementById('studioViewer');
|
const viewer = document.getElementById('studioViewer');
|
||||||
|
const cbLayer = document.getElementById('studioCheckerboard');
|
||||||
|
const img = document.getElementById('lbImg');
|
||||||
|
|
||||||
if (viewer) viewer.classList.toggle('show-checker', on);
|
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');
|
const cb = document.getElementById('sbCheckerboard');
|
||||||
if (cb) cb.checked = on;
|
if (cb) cb.checked = on;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user