updates UI

This commit is contained in:
mike
2026-07-01 03:46:10 +02:00
parent 145fa686e4
commit 970daeba31
5 changed files with 470 additions and 7 deletions

View File

@@ -1855,9 +1855,47 @@
/* ===================== SEGMENT TAB ===================== */
.segment-checker-hint { font-size: 10px; color: #444; margin-top: 6px; }
/* ===================== SERVICES STATUS LEDS ===================== */
.status-dot-led {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
transition: all 0.3s ease;
}
.status-dot-led.online {
background-color: #22c55e;
box-shadow: 0 0 8px rgba(34, 197, 94, 0.6);
animation: status-pulse 2s infinite ease-in-out;
}
.status-dot-led.busy {
background-color: #3b82f6;
box-shadow: 0 0 8px rgba(59, 130, 246, 0.6);
animation: status-pulse 0.8s infinite ease-in-out;
}
.status-dot-led.offline {
background-color: #ef4444;
box-shadow: 0 0 8px rgba(239, 68, 68, 0.6);
}
@keyframes status-pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.5; transform: scale(0.9); }
}
</style>
</head>
<body>
<!-- Floating services status container -->
<div class="services-status-container" style="position: fixed; top: 12px; right: 12px; z-index: 11000; display: flex; align-items: center; gap: 14px; background: rgba(18, 18, 18, 0.9); border: 1px solid #2e2e2e; padding: 6px 12px; border-radius: 20px; font-family: monospace; font-size: 11px; color: #aaa; box-shadow: 0 4px 12px rgba(0,0,0,0.5); backdrop-filter: blur(4px);">
<div style="display: flex; align-items: center; gap: 6px; cursor: help;" id="backendStatus" title="Backend: Offline (API server is down)">
<span class="status-dot-led offline" id="backendDot"></span>
<span>API</span>
</div>
<div style="display: flex; align-items: center; gap: 6px; cursor: help;" id="modelStatus" title="Model-Engine: Status unknown (API server is down)">
<span class="status-dot-led offline" id="modelDot"></span>
<span>Model</span>
</div>
</div>
<div id="privacyOverlay" class="privacy-overlay" onclick="if(privacyMode)disablePrivacyOverlay()">
<div class="priv-topbar">
<div class="priv-logo">S</div>
@@ -2383,6 +2421,17 @@
// --- HYDRATION_START ---
const PRELOADED_IMAGES = [
"20260701_031014_4_20260618_052526_image.png",
"20260701_030956_4_20260618_052526_image.png",
"20260701_030939_4_20260618_052526_image.png",
"20260701_030922_4_20260618_052526_image.png",
"20260701_030906_4_20260618_052526_image.png",
"20260701_025821_dup_20260628_035257_dup_20260628_033600_dup_view_000_000deg.nobg.png",
"20260701_025804_dup_20260628_035257_dup_20260628_033600_dup_view_000_000deg.nobg.png",
"20260701_025747_dup_20260628_035257_dup_20260628_033600_dup_view_000_000deg.nobg.png",
"20260701_025731_dup_20260628_035257_dup_20260628_033600_dup_view_000_000deg.nobg.png",
"20260701_025714_dup_20260628_035257_dup_20260628_033600_dup_view_000_000deg.nobg.png",
"20260701_025651_dup_20260628_035257_dup_20260628_033600_dup_view_000_000deg.nobg.png",
"image.png_face.png",
"20260628_235923_3_20260628_205049_image.nobg.png",
"20260701_005145_sc_image.png",
@@ -8053,7 +8102,8 @@
function isOrbitFile(fname) {
if (!fname) return false;
return fname.includes('_turntable/') || /deg\.png|deg_|\d+deg/i.test(fname);
const tags = fileTags[fname] || [];
return fname.includes('_turntable/') || tags.includes('ORBIT');
}
function computeVisibleIdx(names) {
@@ -9790,6 +9840,10 @@
}
async function lbAutoCrop() {
if (!_backendOnline) {
showToast("Cannot crop: Backend API is offline", "error");
return;
}
const fname = lbNames[lbIdx];
if (!fname) return;
const btn = document.getElementById('lbCropBtn');
@@ -9808,6 +9862,10 @@
}
async function lbRotate(degrees) {
if (!_backendOnline) {
showToast("Cannot rotate: Backend API is offline", "error");
return;
}
const fname = lbNames[lbIdx];
if (!fname || !/\.(png|jpg|jpeg|webp)$/i.test(fname)) { showToast('Select an image to rotate', 'info'); return; }
const btns = [document.getElementById('lbRotateLeftBtn'), document.getElementById('lbRotateRightBtn')];
@@ -9842,6 +9900,14 @@
}
async function lbTogglePose() {
if (!_backendOnline) {
showToast("Cannot estimate pose: Backend API is offline", "error");
return;
}
if (!_modelEngineOnline) {
showToast("Cannot estimate pose: Model-Engine is offline", "error");
return;
}
if (document.getElementById('poseCanvas')) { _removePoseOverlay(); return; }
const fname = lbNames[lbIdx];
if (!fname || !/\.(png|jpg|jpeg|webp)$/i.test(fname)) { showToast('Select an image', 'info'); return; }
@@ -10459,6 +10525,10 @@
}
function startManualCrop() {
if (!_backendOnline) {
showToast("Cannot crop: Backend API is offline", "error");
return;
}
const fname = lbNames[lbIdx];
if (!fname || !/\.(png|jpg|jpeg|webp)$/i.test(fname)) { showToast('Select an image to crop', 'info'); return; }
const viewer = document.getElementById('studioViewer');
@@ -10615,6 +10685,10 @@
}
function startManualPad() {
if (!_backendOnline) {
showToast("Cannot pad: Backend API is offline", "error");
return;
}
const fname = lbNames[lbIdx];
if (!fname || !/\.(png|jpg|jpeg|webp)$/i.test(fname)) { showToast('Select an image to pad', 'info'); return; }
const viewer = document.getElementById('studioViewer');
@@ -10740,6 +10814,10 @@
}
async function lbDuplicate() {
if (!_backendOnline) {
showToast("Cannot duplicate: Backend API is offline", "error");
return;
}
const fname = lbNames[lbIdx];
if (!fname) return;
const btn = document.getElementById('lbDuplicateBtn');
@@ -10797,6 +10875,10 @@
}
async function lbRemoveBg() {
if (!_backendOnline) {
showToast("Cannot remove background: Backend API is offline", "error");
return;
}
const fname = lbNames[lbIdx];
const btn = document.getElementById('lbNoBgBtn') || document.getElementById('sbNoBgBtn');
if (btn) { btn.disabled = true; btn.textContent = 'Removing…'; }
@@ -10819,6 +10901,10 @@
}
async function lbInvertAlpha() {
if (!_backendOnline) {
showToast("Cannot invert alpha: Backend API is offline", "error");
return;
}
const fname = lbNames[lbIdx];
if (!fname || !/\.(png|webp)$/i.test(fname)) { showToast('Invert needs a transparent PNG', 'info'); return; }
const btn = document.getElementById('lbInvertAlphaBtn');
@@ -10838,6 +10924,10 @@
}
async function lbMakeVideo() {
if (!_backendOnline) {
showToast("Cannot generate video: Backend API is offline", "error");
return;
}
// Use Shift+Click selected refs if any, otherwise all images in the current group.
let filenames;
if (_sbRefIndices.length >= 2) {
@@ -11640,7 +11730,11 @@
// Primary: pre-generated static JSON (fast, no DB round-trip per request)
try {
if (bypassStatic) throw new Error("Bypass static requested");
const r = await fetch(`${API}/output/_data/images.json?t=${Date.now()}`, { signal: AbortSignal.timeout(4000) });
let jsonUrl = `${API}/output/_data/images.json?t=${Date.now()}`;
if (typeof LOAD_ONLY_GROUP_ID !== 'undefined' && LOAD_ONLY_GROUP_ID) {
jsonUrl = `${API}/output/_data/group_${LOAD_ONLY_GROUP_ID}.json?t=${Date.now()}`;
}
const r = await fetch(jsonUrl, { signal: AbortSignal.timeout(4000) });
if (r.ok) {
const data = await r.json();
let imgs = data.images || [];
@@ -11654,7 +11748,10 @@
// Fallback: live API (handles first startup before static file is written)
if (!files) {
try {
const url = `${API}/images?archived=true${bypassStatic ? '&bypass_static=true' : ''}`;
let url = `${API}/images?archived=true${bypassStatic ? '&bypass_static=true' : ''}`;
if (typeof LOAD_ONLY_GROUP_ID !== 'undefined' && LOAD_ONLY_GROUP_ID) {
url += `&group_id=${encodeURIComponent(LOAD_ONLY_GROUP_ID)}`;
}
const r = await fetch(url, { signal: AbortSignal.timeout(12000) });
if (r.ok) {
const data = await r.json();
@@ -12055,6 +12152,26 @@
if (statusDot) statusDot.classList.remove('updating');
updateGalleryHighlight();
loadImages._inFlight = false;
if (typeof LOAD_ONLY_GROUP_ID !== 'undefined' && LOAD_ONLY_GROUP_ID) {
const studio = document.getElementById('studio');
if (studio && !studio.classList.contains('open')) {
const header = document.querySelector('.header');
if (header) header.style.display = 'none';
const galleryEl = document.getElementById('gallery');
if (galleryEl) galleryEl.style.display = 'none';
const galleryBtn = document.querySelector('.studio-topbar-left button');
if (galleryBtn && galleryBtn.textContent.includes('Gallery')) {
galleryBtn.style.display = 'none';
}
if (groupData.has(LOAD_ONLY_GROUP_ID)) {
openStudio(LOAD_ONLY_GROUP_ID, 0);
}
}
}
if (loadImages._needsRerun) {
loadImages._needsRerun = false;
const bp = !!loadImages._pendingBypass;
@@ -12158,6 +12275,55 @@
}
const API = 'http://127.0.0.1:8500';
const LOAD_ONLY_GROUP_ID = null;
let _backendOnline = true;
let _modelEngineOnline = true;
async function pollServicesStatus() {
try {
const r = await fetch(`${API}/services/status`, { signal: AbortSignal.timeout(2000) });
if (r.ok) {
const data = await r.json();
_backendOnline = true;
_modelEngineOnline = data.model_engine.online;
const backendDot = document.getElementById('backendDot');
if (backendDot) {
backendDot.className = 'status-dot-led ' + (data.backend.busy ? 'busy' : 'online');
backendDot.parentElement.title = data.backend.busy ? 'Backend: Active task running' : 'Backend: Idle (Online)';
}
const modelDot = document.getElementById('modelDot');
if (modelDot) {
modelDot.className = 'status-dot-led ' + (data.model_engine.online ? (data.model_engine.busy ? 'busy' : 'online') : 'offline');
modelDot.parentElement.title = data.model_engine.online
? (data.model_engine.busy ? 'Model-Engine: Active inference running' : 'Model-Engine: Idle (Online)')
: 'Model-Engine: Offline (ComfyUI backend is down)';
}
} else {
throw new Error("API responded with error");
}
} catch (e) {
_backendOnline = false;
_modelEngineOnline = false;
const backendDot = document.getElementById('backendDot');
if (backendDot) {
backendDot.className = 'status-dot-led offline';
backendDot.parentElement.title = 'Backend: Offline (API server is down)';
}
const modelDot = document.getElementById('modelDot');
if (modelDot) {
modelDot.className = 'status-dot-led offline';
modelDot.parentElement.title = 'Model-Engine: Status unknown (API server is down)';
}
}
}
// Start polling services status every 3 seconds
setInterval(pollServicesStatus, 3000);
setTimeout(pollServicesStatus, 100);
// --- selection state ---
let selectionMode = false;
@@ -13776,6 +13942,14 @@
}
async function submitSbGenerate() {
if (!_backendOnline) {
showToast("Cannot generate: Backend API is offline", "error");
return;
}
if (!_modelEngineOnline) {
showToast("Cannot generate: Model-Engine (ComfyUI) is offline", "error");
return;
}
if (!_fsModelFilename) return;
const promptVal = (document.getElementById('sbGenPromptInput')?.value || '').trim();
const hasPoses = _fsSelectedPoses.size > 0 || _sbSelectedAngles.size > 0;