This commit is contained in:
mike
2026-06-27 23:36:41 +02:00
parent 2ada5fb559
commit bf3773bfd4
3 changed files with 74 additions and 20 deletions

View File

@@ -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 ----

View File

@@ -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).
"""

View File

@@ -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