updates UI
This commit is contained in:
@@ -2146,6 +2146,12 @@
|
||||
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
||||
</svg>Path
|
||||
</button>
|
||||
<button class="sb-btn" onclick="copyPageUrl()" style="padding:2px 6px;font-size:9px;min-width:auto;display:inline-flex;align-items:center;gap:3px;height:20px;border-radius:4px" title="Copy current page URL to clipboard">
|
||||
<svg width="8" height="8" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
||||
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
||||
</svg>URL
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<span id="lbPoseTag" style="font-size:11px;color:#f59e0b;display:none"></span>
|
||||
@@ -2379,6 +2385,12 @@
|
||||
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
||||
</svg>Copy Path
|
||||
</button>
|
||||
<button class="sb-btn" onclick="copyPageUrl()" style="padding:2px 6px;font-size:9px;min-width:auto;display:inline-flex;align-items:center;gap:4px" title="Copy current page URL to clipboard">
|
||||
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="display:inline-block">
|
||||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
||||
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
||||
</svg>Copy URL
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<input type="text" class="sb-input" id="lbNameInput"
|
||||
@@ -2544,6 +2556,34 @@
|
||||
|
||||
// --- HYDRATION_START ---
|
||||
const PRELOADED_IMAGES = [
|
||||
"20260701_064213_sc_pad_view_000_000deg.png",
|
||||
"20260701_063422_sc_2_20260618_052526_image.nobg.nobg.png",
|
||||
"20260701_062850_sc_pad_view_000_000deg.png",
|
||||
"20260701_062218_pad_view_000_000deg.png",
|
||||
"20260701_062201_pad_view_000_000deg.png",
|
||||
"20260701_062144_pad_view_000_000deg.png",
|
||||
"20260701_062128_pad_view_000_000deg.png",
|
||||
"20260701_062111_pad_view_000_000deg.png",
|
||||
"20260701_062054_pad_view_000_000deg.png",
|
||||
"20260701_062037_pad_view_000_000deg.png",
|
||||
"20260701_061913_pad_view_000_000deg.png",
|
||||
"20260701_061854_pad_view_000_000deg.png",
|
||||
"20260701_061837_pad_view_000_000deg.png",
|
||||
"20260701_061814_pad_view_000_000deg.png",
|
||||
"20260701_060854_pad_view_000_000deg.png",
|
||||
"20260701_060215_pad_view_000_000deg.png",
|
||||
"20260701_060110_pad_view_000_000deg.png",
|
||||
"20260701_060054_pad_view_000_000deg.png",
|
||||
"20260701_055944_pad_view_000_000deg.png",
|
||||
"20260701_055727_pad_view_000_000deg.png",
|
||||
"20260701_055710_pad_view_000_000deg.png",
|
||||
"20260701_055654_pad_view_000_000deg.png",
|
||||
"20260701_055637_pad_view_000_000deg.png",
|
||||
"20260701_055621_pad_view_000_000deg.png",
|
||||
"20260701_055605_pad_view_000_000deg.png",
|
||||
"20260701_055542_pad_view_000_000deg.png",
|
||||
"up_f475c600_face.png",
|
||||
"20260701_055451_pad_view_000_000deg.png",
|
||||
"20260701_051754_image.png",
|
||||
"20260701_051737_image.png",
|
||||
"20260701_051721_image.png",
|
||||
@@ -3270,7 +3310,6 @@
|
||||
"_turntable/cg_d032c1ef/views/view_002_030deg.png",
|
||||
"20260618_054230_image.png_face.png",
|
||||
"cg_f1e85987_face.png",
|
||||
"up_f475c600_face.png",
|
||||
"20260618_174516_image.png_face.png",
|
||||
"up_76ef7e4f_face.png",
|
||||
"cg_e79533a4_face.png",
|
||||
@@ -4685,8 +4724,8 @@
|
||||
"_turntable/cg_7ec17537/views/view_003_045deg.png",
|
||||
"_turntable/cg_7ec17537/views/view_002_030deg.png",
|
||||
"_turntable/cg_7ec17537/views/view_001_015deg.png",
|
||||
"_turntable/cg_7ec17537/views/view_000_000deg.png",
|
||||
"_turntable/cg_7ec17537/views/20260628_033600_dup_view_000_000deg.png",
|
||||
"_turntable/cg_7ec17537/views/view_000_000deg.png",
|
||||
"_turntable/cg_7ec17537/views/20260628_033831_dup_20260628_033600_dup_view_000_000deg.png",
|
||||
"_turntable/up_154b502c/views/view_023_345deg.png",
|
||||
"_turntable/up_154b502c/views/view_022_330deg.png",
|
||||
@@ -8078,6 +8117,105 @@
|
||||
let _sbPadOutpaint = false;
|
||||
let _sbPadUniform = false;
|
||||
|
||||
// ---- scenery & art-form state ----
|
||||
let _sbGenSceneryType = 'black_void'; // 'black_void', 'inherit_empty', 'inherit_explicit', 'custom'
|
||||
let _sbGenSceneryCustomVal = '';
|
||||
let _sbGenArtFormType = 'default'; // 'default', 'art1', 'art2', 'custom'
|
||||
let _sbGenArtFormCustomVal = '';
|
||||
|
||||
function cleanPosePrompt(p) {
|
||||
if (!p) return "";
|
||||
let items = [];
|
||||
p.split('\n').forEach(line => {
|
||||
const parts = line.split(/(?<=[.!?])\s+/).map(s => s.trim()).filter(Boolean);
|
||||
items.push(...parts);
|
||||
});
|
||||
if (items.length === 0) return "";
|
||||
|
||||
// Check if first line/sentence matches any of our known sceneries (case-insensitive, strip punctuation)
|
||||
const firstItem = items[0].toLowerCase().replace(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g,"").trim();
|
||||
if (firstItem === "you are in a black void" || firstItem === "you are in black void" || firstItem === "inherit scene from reference image") {
|
||||
items.shift();
|
||||
}
|
||||
|
||||
if (items.length === 0) return "";
|
||||
|
||||
// Check if last line/sentence matches any of our known art-forms (case-insensitive, strip punctuation)
|
||||
const lastItem = items[items.length - 1].toLowerCase().replace(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g,"").trim();
|
||||
const knownArtForms = [
|
||||
"anatomically precise hyperrealistic high detail keep the characteristics of the reference image",
|
||||
"perfect anatomy photo-realistic detailled identical person from reference image",
|
||||
"perfect anatomy photo-realistic detailed identical person",
|
||||
"photo-realistic keep identical person from reference"
|
||||
];
|
||||
if (knownArtForms.some(art => lastItem.includes(art) || art.includes(lastItem))) {
|
||||
items.pop();
|
||||
}
|
||||
|
||||
return items.join('\n');
|
||||
}
|
||||
|
||||
function getCompoundPrompt(rawPrompt) {
|
||||
const core = cleanPosePrompt(rawPrompt).trim();
|
||||
if (!core) return "";
|
||||
|
||||
// 1. Get Scenery
|
||||
let scenery = "";
|
||||
if (_sbGenSceneryType === 'black_void') {
|
||||
scenery = "you are in a black void.";
|
||||
} else if (_sbGenSceneryType === 'inherit_explicit') {
|
||||
scenery = "inherit scene from reference image.";
|
||||
} else if (_sbGenSceneryType === 'custom') {
|
||||
scenery = _sbGenSceneryCustomVal.trim();
|
||||
if (scenery && !/[.!?]$/.test(scenery)) scenery += ".";
|
||||
}
|
||||
|
||||
// 2. Get Art-form
|
||||
let artForm = "";
|
||||
if (_sbGenArtFormType === 'default') {
|
||||
artForm = "photo-realistic keep identical person from reference";
|
||||
} else if (_sbGenArtFormType === 'art1') {
|
||||
artForm = "Anatomically precise, hyperrealistic, high detail, keep the characteristics of the reference image.";
|
||||
} else if (_sbGenArtFormType === 'art2') {
|
||||
artForm = "Perfect anatomy, photo-realistic, detailed, identical person from reference image";
|
||||
} else if (_sbGenArtFormType === 'custom') {
|
||||
artForm = _sbGenArtFormCustomVal.trim();
|
||||
}
|
||||
|
||||
// Combine them
|
||||
let parts = [];
|
||||
if (scenery) parts.push(scenery);
|
||||
parts.push(core);
|
||||
if (artForm) parts.push(artForm);
|
||||
|
||||
return parts.join('\n');
|
||||
}
|
||||
|
||||
function onSbSceneryChange(val) {
|
||||
_sbGenSceneryType = val;
|
||||
const el = document.getElementById('sbGenSceneryCustom');
|
||||
if (el) {
|
||||
el.style.display = val === 'custom' ? 'block' : 'none';
|
||||
}
|
||||
updateSbGenBtn();
|
||||
}
|
||||
function onSbSceneryCustomInput(val) {
|
||||
_sbGenSceneryCustomVal = val;
|
||||
updateSbGenBtn();
|
||||
}
|
||||
function onSbArtFormChange(val) {
|
||||
_sbGenArtFormType = val;
|
||||
const el = document.getElementById('sbGenArtFormCustom');
|
||||
if (el) {
|
||||
el.style.display = val === 'custom' ? 'block' : 'none';
|
||||
}
|
||||
updateSbGenBtn();
|
||||
}
|
||||
function onSbArtFormCustomInput(val) {
|
||||
_sbGenArtFormCustomVal = val;
|
||||
updateSbGenBtn();
|
||||
}
|
||||
|
||||
const CAMERA_ANGLES = [
|
||||
// Absolute camera positions
|
||||
{ name: 'Front', icon: '⊙', prompt: 'Head-on straight-on full-body portrait, frontal camera, realistic, transparent background', relative: false },
|
||||
@@ -11101,6 +11239,15 @@
|
||||
});
|
||||
}
|
||||
|
||||
function copyPageUrl() {
|
||||
const url = window.location.href;
|
||||
navigator.clipboard.writeText(url).then(() => {
|
||||
showToast('Page URL copied to clipboard', 'success');
|
||||
}).catch(err => {
|
||||
showToast('Failed to copy: ' + err, 'error');
|
||||
});
|
||||
}
|
||||
|
||||
async function lbRemoveBg() {
|
||||
if (!_backendOnline) {
|
||||
showToast("Cannot remove background: Backend API is offline", "error");
|
||||
@@ -13815,6 +13962,17 @@
|
||||
if (el) _sbPoseEdits[name] = el.value;
|
||||
});
|
||||
|
||||
// Preserve decorated prompt custom inputs
|
||||
const prevSceneryType = document.getElementById('sbGenScenerySelect')?.value ?? _sbGenSceneryType;
|
||||
const prevSceneryCustom = document.getElementById('sbGenSceneryCustom')?.value ?? _sbGenSceneryCustomVal;
|
||||
const prevArtFormType = document.getElementById('sbGenArtFormSelect')?.value ?? _sbGenArtFormType;
|
||||
const prevArtFormCustom = document.getElementById('sbGenArtFormCustom')?.value ?? _sbGenArtFormCustomVal;
|
||||
|
||||
_sbGenSceneryType = prevSceneryType;
|
||||
_sbGenSceneryCustomVal = prevSceneryCustom;
|
||||
_sbGenArtFormType = prevArtFormType;
|
||||
_sbGenArtFormCustomVal = prevArtFormCustom;
|
||||
|
||||
const donePoses = new Set();
|
||||
lbNames.forEach(n => { if (filePoses[n]) donePoses.add(filePoses[n]); });
|
||||
|
||||
@@ -13886,15 +14044,39 @@
|
||||
onclick="refineSbPrompt()" title="AI prompt refinement">✨ Refine</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sb-prompt-wrap" style="position:relative;margin-bottom:10px">
|
||||
<textarea class="sb-input" id="sbGenPromptInput" rows="2" autocomplete="off"
|
||||
placeholder="Prompt — overrides pose"
|
||||
oninput="updateSbGenBtn();autoGrowPrompt(this);showPromptSuggest(this)"
|
||||
onfocus="autoGrowPrompt(this);showPromptSuggest(this)"
|
||||
onblur="setTimeout(()=>hidePromptSuggest(),150)"
|
||||
onkeydown="if(event.key==='Escape')hidePromptSuggest()"
|
||||
style="resize:none;max-height:320px;overflow-y:auto;line-height:1.4;font-family:inherit">${escHtml(prevPromptVal)}</textarea>
|
||||
<div id="sbPromptSuggest" style="display:none;position:absolute;left:0;right:0;z-index:50;max-height:180px;overflow-y:auto;background:#18181b;border:1px solid #2a2a2a;border-radius:6px;margin-top:2px;box-shadow:0 6px 20px rgba(0,0,0,0.6)"></div>
|
||||
<div style="background:rgba(255,255,255,0.02);border:1px solid #222;border-radius:6px;padding:8px;margin-bottom:10px">
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:6px">
|
||||
<span style="font-size:10px;color:#888;font-weight:600;text-transform:uppercase;letter-spacing:0.5px">Scenery</span>
|
||||
<select id="sbGenScenerySelect" onchange="onSbSceneryChange(this.value)" style="background:#111;border:1px solid #2a2a2a;border-radius:4px;color:#ccc;font-size:11px;padding:2px 4px;cursor:pointer;width:150px">
|
||||
<option value="black_void" ${_sbGenSceneryType==='black_void'?'selected':''}>Black void</option>
|
||||
<option value="inherit_empty" ${_sbGenSceneryType==='inherit_empty'?'selected':''}>Inherit (empty)</option>
|
||||
<option value="inherit_explicit" ${_sbGenSceneryType==='inherit_explicit'?'selected':''}>Explicit inherit</option>
|
||||
<option value="custom" ${_sbGenSceneryType==='custom'?'selected':''}>Custom scenery...</option>
|
||||
</select>
|
||||
</div>
|
||||
<input type="text" id="sbGenSceneryCustom" placeholder="Type custom scenery description..." value="${escHtml(_sbGenSceneryCustomVal)}" style="display:${_sbGenSceneryType==='custom'?'block':'none'};width:100%;box-sizing:border-box;background:#111;border:1px solid #2a2a2a;border-radius:4px;color:#ccc;font-size:11px;padding:4px 6px;margin-bottom:6px" oninput="onSbSceneryCustomInput(this.value)">
|
||||
|
||||
<div class="sb-prompt-wrap" style="position:relative;margin-bottom:6px">
|
||||
<textarea class="sb-input" id="sbGenPromptInput" rows="2" autocomplete="off"
|
||||
placeholder="Pose or actions prompt (e.g. standing, hands on hips)"
|
||||
oninput="updateSbGenBtn();autoGrowPrompt(this);showPromptSuggest(this)"
|
||||
onfocus="autoGrowPrompt(this);showPromptSuggest(this)"
|
||||
onblur="setTimeout(()=>hidePromptSuggest(),150)"
|
||||
onkeydown="if(event.key==='Escape')hidePromptSuggest()"
|
||||
style="resize:none;max-height:320px;overflow-y:auto;line-height:1.4;font-family:inherit;margin-bottom:0">${escHtml(prevPromptVal)}</textarea>
|
||||
<div id="sbPromptSuggest" style="display:none;position:absolute;left:0;right:0;z-index:50;max-height:180px;overflow-y:auto;background:#18181b;border:1px solid #2a2a2a;border-radius:6px;margin-top:2px;box-shadow:0 6px 20px rgba(0,0,0,0.6)"></div>
|
||||
</div>
|
||||
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:6px">
|
||||
<span style="font-size:10px;color:#888;font-weight:600;text-transform:uppercase;letter-spacing:0.5px">Art-form</span>
|
||||
<select id="sbGenArtFormSelect" onchange="onSbArtFormChange(this.value)" style="background:#111;border:1px solid #2a2a2a;border-radius:4px;color:#ccc;font-size:11px;padding:2px 4px;cursor:pointer;width:150px">
|
||||
<option value="default" ${_sbGenArtFormType==='default'?'selected':''}>Realistic Portrait (default)</option>
|
||||
<option value="art1" ${_sbGenArtFormType==='art1'?'selected':''}>Anatomically precise (high detail)</option>
|
||||
<option value="art2" ${_sbGenArtFormType==='art2'?'selected':''}>Perfect anatomy (photo-realistic)</option>
|
||||
<option value="custom" ${_sbGenArtFormType==='custom'?'selected':''}>Custom art-form...</option>
|
||||
</select>
|
||||
</div>
|
||||
<input type="text" id="sbGenArtFormCustom" placeholder="Type custom art-form style..." value="${escHtml(_sbGenArtFormCustomVal)}" style="display:${_sbGenArtFormType==='custom'?'block':'none'};width:100%;box-sizing:border-box;background:#111;border:1px solid #2a2a2a;border-radius:4px;color:#ccc;font-size:11px;padding:4px 6px;margin-top:6px" oninput="onSbArtFormCustomInput(this.value)">
|
||||
</div>`;
|
||||
|
||||
html += '<div class="sb-sep"></div><div class="sb-label">Poses</div>';
|
||||
@@ -14474,7 +14656,8 @@
|
||||
// Use edited text if available, else original
|
||||
const edited = _sbPoseEdits[name];
|
||||
const orig = availablePoses[name]?.text ?? availablePoses[name] ?? name;
|
||||
prompts.push((edited !== undefined && edited.trim()) ? edited.trim() : String(orig));
|
||||
const rawPose = (edited !== undefined && edited.trim()) ? edited.trim() : String(orig);
|
||||
prompts.push(getCompoundPrompt(rawPose));
|
||||
});
|
||||
// Camera angles — treated as prompts with no pose tag
|
||||
_sbSelectedAngles.forEach(name => {
|
||||
@@ -14483,7 +14666,7 @@
|
||||
// shows a green ✓ once generated, mirroring pose tracking.
|
||||
if (ang) { prompts.push(ang.prompt); poses.push(name); }
|
||||
});
|
||||
if (promptVal) { prompts.push(promptVal); poses.push(null); savePromptHistory(promptVal); }
|
||||
if (promptVal) { prompts.push(getCompoundPrompt(promptVal)); poses.push(null); savePromptHistory(promptVal); }
|
||||
else if (_sbPadOutpaint) {
|
||||
const isZero = (v) => !v || v === '0' || v === '0%' || v === '0px';
|
||||
if (!isZero(_sbPadTop) || !isZero(_sbPadRight) || !isZero(_sbPadBottom) || !isZero(_sbPadLeft)) {
|
||||
|
||||
Reference in New Issue
Block a user