reorder
This commit is contained in:
@@ -2004,9 +2004,9 @@
|
||||
<div class="sidebar-tabs">
|
||||
<button class="sb-tab active" data-tab="info" onclick="switchSidebarTab('info')">Info</button>
|
||||
<button class="sb-tab" data-tab="generate" onclick="switchSidebarTab('generate')">Generate</button>
|
||||
<button class="sb-tab" data-tab="faceswap" onclick="switchSidebarTab('faceswap')">Faceswap</button>
|
||||
<button class="sb-tab" data-tab="scenery" onclick="switchSidebarTab('scenery')">Scenery</button>
|
||||
<button class="sb-tab" data-tab="segment" onclick="switchSidebarTab('segment')">Segment</button>
|
||||
<button class="sb-tab" data-tab="faceswap" onclick="switchSidebarTab('faceswap')">Face</button>
|
||||
<button class="sb-tab" data-tab="scenery" onclick="switchSidebarTab('scenery')">Scene</button>
|
||||
<button class="sb-tab" data-tab="segment" onclick="switchSidebarTab('segment')">SAM</button>
|
||||
<button class="sb-tab" data-tab="orbit" onclick="switchSidebarTab('orbit')" id="sbTabOrbit">Orbit</button>
|
||||
</div>
|
||||
<div class="sb-panels">
|
||||
@@ -2220,6 +2220,18 @@
|
||||
|
||||
// --- 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_232526_pad_20260626_035101_pad_20260626_034830_jb.nobg.png",
|
||||
"20260627_232302_pad_20260627_232133_image.png",
|
||||
"20260627_232238_pad_20260627_232133_image.png",
|
||||
"up_6ed4293c_face.png",
|
||||
"20260627_232209_image.png",
|
||||
"20260627_232151_image.png",
|
||||
"20260627_232133_image.png",
|
||||
"20260627_230405_mr_image.png",
|
||||
"20260627_222806_mr_image.png",
|
||||
"20260627_230301_7_20260627_201343_image.nobg.png",
|
||||
"20260627_201629_7_20260627_201343_image.nobg.png",
|
||||
"20260627_201629_7_20260627_201343_image.png",
|
||||
"20260627_225051_mr_image.png",
|
||||
@@ -2234,7 +2246,6 @@
|
||||
"20260627_222926_image.png",
|
||||
"20260627_222915_image.png",
|
||||
"20260627_222904_image.png",
|
||||
"20260627_222806_mr_image.png",
|
||||
"20260627_222321_image.png",
|
||||
"20260627_222310_image.png",
|
||||
"20260627_222259_image.png",
|
||||
@@ -6733,6 +6744,7 @@
|
||||
let _sbGenJobId = null;
|
||||
let _sbGenJobPollTimer= null;
|
||||
let _followLatestGid = null; // set to gid after generation to auto-jump to newest image
|
||||
let _followLatestFilename = null;
|
||||
let _slideshowActive = false;
|
||||
let _slideshowTimer = null;
|
||||
let _slideshowInterval= 4000;
|
||||
@@ -6881,6 +6893,27 @@
|
||||
return val.toFixed(2);
|
||||
}
|
||||
|
||||
function syncPadFields(el) {
|
||||
const id = el.id; // padLeft or sbPadLeft
|
||||
let pairId = null;
|
||||
if (id.endsWith('Left')) pairId = id.replace('Left', 'Right');
|
||||
else if (id.endsWith('Right')) pairId = id.replace('Right', 'Left');
|
||||
else if (id.endsWith('Top')) pairId = id.replace('Top', 'Bottom');
|
||||
else if (id.endsWith('Bottom')) pairId = id.replace('Bottom', 'Top');
|
||||
|
||||
if (pairId) {
|
||||
const pairEl = document.getElementById(pairId);
|
||||
// only sync if the other side is 'empty' (0)
|
||||
if (pairEl && (pairEl.value === '0.00' || pairEl.value === '0' || pairEl.value === '')) {
|
||||
pairEl.value = el.value;
|
||||
}
|
||||
}
|
||||
if (id.startsWith('sbPad')) {
|
||||
updateSbGenBtn();
|
||||
}
|
||||
updatePadPreview();
|
||||
}
|
||||
|
||||
function updatePadPreview() {
|
||||
const viewer = document.getElementById('studioViewer');
|
||||
if (!viewer) return;
|
||||
@@ -8163,7 +8196,7 @@
|
||||
bar.id = 'padBar';
|
||||
bar.style.cssText = 'position:absolute;top:0;left:0;right:0;display:flex;gap:8px;padding:8px;background:rgba(0,0,0,0.85);z-index:101;justify-content:flex-end;align-items:center;flex-wrap:wrap;';
|
||||
const numInput = (id, label) => {
|
||||
const onInput = `updatePadPreview()`;
|
||||
const onInput = `syncPadFields(this)`;
|
||||
const onBlur = `this.value = formatPadValue(this.value); updatePadPreview()`;
|
||||
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>`;
|
||||
};
|
||||
@@ -8174,9 +8207,9 @@
|
||||
+ numInput('padBottom','↓')
|
||||
+ numInput('padLeft','←')
|
||||
+ `<select id="padFill" style="background:#111;border:1px solid #333;color:#aaa;border-radius:4px;font-size:11px;padding:2px 5px">
|
||||
<option value="transparent">Transparent</option>
|
||||
<option value="black">Black</option>
|
||||
<option value="white">White</option>
|
||||
<option value="transparent">Transparent</option>
|
||||
</select>`
|
||||
+ `<label style="display:flex;align-items:center;gap:4px;font-size:11px;color:#ccc;cursor:pointer" title="Keep original and pad a copy">`
|
||||
+ `<input type="checkbox" id="padAsCopy" checked style="cursor:pointer">Copy</label>`
|
||||
@@ -8204,7 +8237,7 @@
|
||||
|
||||
const isZero = (v) => !v || v === '0' || v === '0%' || v === '0px';
|
||||
if (isZero(top) && isZero(right) && isZero(bottom) && isZero(left)) { showToast('Enter at least one padding value', 'info'); return; }
|
||||
const fill = document.getElementById('padFill')?.value || 'black';
|
||||
const fill = document.getElementById('padFill')?.value || 'transparent';
|
||||
const asCopy = document.getElementById('padAsCopy')?.checked ?? true;
|
||||
const outpaint = document.getElementById('padOutpaint')?.checked ?? false;
|
||||
const btn = document.getElementById('padConfirmBtn');
|
||||
@@ -8241,6 +8274,7 @@
|
||||
}
|
||||
|
||||
updateStudio();
|
||||
_followLatestFilename = newFn;
|
||||
_followLatestGid = lbCurrentGid;
|
||||
refreshNow(true);
|
||||
} else {
|
||||
@@ -9184,8 +9218,15 @@
|
||||
const visibleIdx = names.map((n, i) => i).filter(i => !fileHidden[names[i]]);
|
||||
groupData.set(gid, { urls, names, visibleIdx });
|
||||
|
||||
// Auto-follow: if a generation just finished for this group, jump to the last (newest) image
|
||||
if (_followLatestGid === gid && _isStudioOpen() && lbCurrentGid === gid && names.length > 0) {
|
||||
// Auto-follow: if a generation just finished for this group, jump to the image
|
||||
if (_followLatestFilename && names.includes(_followLatestFilename) && _isStudioOpen() && lbCurrentGid === gid) {
|
||||
lbIdx = names.indexOf(_followLatestFilename);
|
||||
_followLatestFilename = null;
|
||||
_followLatestGid = null;
|
||||
lbUrls = urls;
|
||||
lbNames = names;
|
||||
updateStudio();
|
||||
} else if (_followLatestGid === gid && _isStudioOpen() && lbCurrentGid === gid && names.length > 0) {
|
||||
_followLatestGid = null;
|
||||
lbIdx = names.length - 1;
|
||||
lbUrls = urls;
|
||||
@@ -9777,6 +9818,15 @@
|
||||
if ((e.key === 'p' || e.key === 'P') && tag !== 'INPUT' && tag !== 'TEXTAREA') {
|
||||
togglePrivacyMode(); return;
|
||||
}
|
||||
if ((e.key === 'c' || e.key === 'C') && tag !== 'INPUT' && tag !== 'TEXTAREA') {
|
||||
if (document.getElementById('studio').classList.contains('open')) {
|
||||
const cb = document.getElementById('sbCheckerboard');
|
||||
const viewer = document.getElementById('studioViewer');
|
||||
const cur = viewer ? viewer.classList.contains('show-checker') : (cb ? cb.checked : false);
|
||||
toggleCheckerboard(!cur);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ((e.key === 'a' || e.key === 'A') && tag !== 'INPUT' && tag !== 'TEXTAREA') {
|
||||
if (document.getElementById('studio').classList.contains('open')) {
|
||||
startManualPad(); return;
|
||||
@@ -10178,7 +10228,7 @@
|
||||
<span style="font-size:11px;color:#666" title="Expand canvas before generation — gives the model space to outpaint">Pad:</span>
|
||||
${['Top','Right','Bottom','Left'].map(side => {
|
||||
const val = side==='Top'?_sbPadTop:side==='Right'?_sbPadRight:side==='Bottom'?_sbPadBottom:_sbPadLeft;
|
||||
const onInput = `updateSbGenBtn(); updatePadPreview()`;
|
||||
const onInput = `syncPadFields(this)`;
|
||||
const onBlur = `this.value = formatPadValue(this.value); ${onInput}`;
|
||||
return `<label style="display:flex;align-items:center;gap:2px;font-size:11px;color:#888">${side[0]}
|
||||
<input type="text" id="sbPad${side}" value="${val}" placeholder="px or %"
|
||||
@@ -11485,6 +11535,7 @@
|
||||
function renderSidebarSegment() {
|
||||
const panel = document.getElementById('sbPanelSegment');
|
||||
if (!panel) return;
|
||||
const isChecked = document.getElementById('studioViewer')?.classList.contains('show-checker');
|
||||
panel.innerHTML = `
|
||||
<div style="font-size:11px;color:#666;margin-bottom:12px;line-height:1.6">
|
||||
Remove background using SAM2 or rembg fallback. The original file is
|
||||
@@ -11499,7 +11550,7 @@
|
||||
<div id="sbSegStatus" style="font-size:11px;color:#aaa;min-height:16px;margin-bottom:10px"></div>
|
||||
<div class="sb-sep"></div>
|
||||
<label style="display:flex;align-items:center;gap:8px;font-size:12px;cursor:pointer;margin-bottom:6px">
|
||||
<input type="checkbox" id="sbCheckerboard" onchange="toggleCheckerboard(this.checked)">
|
||||
<input type="checkbox" id="sbCheckerboard" onchange="toggleCheckerboard(this.checked)" ${isChecked ? 'checked' : ''}>
|
||||
<span>Checkerboard (show transparency)</span>
|
||||
</label>
|
||||
<div style="font-size:10px;color:#555;line-height:1.4">
|
||||
@@ -11598,6 +11649,8 @@
|
||||
function toggleCheckerboard(on) {
|
||||
const viewer = document.getElementById('studioViewer');
|
||||
if (viewer) viewer.classList.toggle('show-checker', on);
|
||||
const cb = document.getElementById('sbCheckerboard');
|
||||
if (cb) cb.checked = on;
|
||||
}
|
||||
|
||||
// ---- set preferred ----
|
||||
|
||||
@@ -1456,7 +1456,7 @@ def _batch_worker(job_id: str, filenames: list, prompts: list[str], poses: list,
|
||||
seed: int, max_area: int, group_id: str | None = None,
|
||||
wireframe_ref: str | None = None, wireframe_time: float = 0.5,
|
||||
pad_top: Any = 0, pad_right: Any = 0,
|
||||
pad_bottom: Any = 0, pad_left: Any = 0, pad_fill: str = "black",
|
||||
pad_bottom: Any = 0, pad_left: Any = 0, pad_fill: str = "transparent",
|
||||
pad_outpaint: bool = False):
|
||||
output_dir = _load_output_dir()
|
||||
for fname in filenames:
|
||||
@@ -3571,13 +3571,13 @@ class PadRequest(BaseModel):
|
||||
bottom: int | float | str = 0
|
||||
left: int | float | str = 0
|
||||
as_copy: bool = True
|
||||
fill: str = "black" # "black", "white", "transparent"
|
||||
fill: str = "transparent" # "black", "white", "transparent"
|
||||
outpaint: bool = False
|
||||
prompt: str | None = None
|
||||
|
||||
|
||||
def _apply_manual_pad(pil: Image.Image, top, right, bottom, left,
|
||||
fill: str = "black") -> Image.Image:
|
||||
fill: str = "transparent") -> Image.Image:
|
||||
"""Expand canvas by padding pixels on each side. Fill with black, white, or transparency.
|
||||
Supports pixel values (int) or percentages (str like "10%" or float < 1.0).
|
||||
"""
|
||||
|
||||
@@ -55,12 +55,13 @@ Keep full characteristics of reference image.
|
||||
|
||||
# Three-quarter (18)
|
||||
|
||||
You are in a black empty void.
|
||||
high quality, masterpiece, detailed, detailed skin.
|
||||
Head-on a right-front detailed full-nude-body. Top-to-toe photo.
|
||||
female teenage, photorealistic, sharp focus, transparent background.
|
||||
Identical person from reference image.
|
||||
Keep characteristics of reference image.
|
||||
You are in a black empty void, transparent background.
|
||||
masterpiece, high quality, detailed, detailed skin.
|
||||
Head-on a right-front.
|
||||
Detailed full-nude-body.
|
||||
Top-to-toe photo.
|
||||
female teenage, hyper realistic.
|
||||
Anatomically precise. Keep characteristics of reference image.
|
||||
|
||||
# Three-quarter
|
||||
|
||||
|
||||
Reference in New Issue
Block a user