updates UI
This commit is contained in:
@@ -1330,10 +1330,20 @@
|
||||
inset: 0;
|
||||
background: #0a0a0a;
|
||||
z-index: 500;
|
||||
flex-direction: column;
|
||||
flex-direction: row; /* row layout to support full height sidebar */
|
||||
overflow: hidden;
|
||||
}
|
||||
.studio.open { display: flex; }
|
||||
|
||||
.studio-left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.studio-topbar {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
@@ -1348,10 +1358,6 @@
|
||||
.studio-topbar-center{ flex: 1; min-width: 0; display: flex; align-items: center; gap: 10px; overflow: hidden; }
|
||||
.studio-topbar-right { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
|
||||
|
||||
.studio-body {
|
||||
flex: 1; min-height: 0;
|
||||
display: flex; overflow: hidden;
|
||||
}
|
||||
.studio-main {
|
||||
flex: 1; min-width: 0;
|
||||
display: flex; flex-direction: column; overflow: hidden;
|
||||
@@ -1464,7 +1470,7 @@
|
||||
.sidebar-inner {
|
||||
flex: 1; min-height: 0;
|
||||
display: flex; flex-direction: column;
|
||||
padding-top: 36px; overflow: hidden;
|
||||
padding-top: 8px; overflow: hidden;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
.studio-sidebar.collapsed .sidebar-inner { opacity: 0; pointer-events: none; }
|
||||
@@ -1936,17 +1942,6 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Floating services status container -->
|
||||
<div class="services-status-container" style="position: fixed; bottom: 12px; left: 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>
|
||||
@@ -2069,12 +2064,23 @@
|
||||
</div>
|
||||
|
||||
<div class="header">
|
||||
<div class="controls">
|
||||
<div class="controls" style="display:flex;align-items:center;gap:8px">
|
||||
<span class="count" id="count">0 images</span>
|
||||
<button class="btn" id="archiveViewBtn" onclick="toggleArchiveView()" title="View archived items">📦 Archive</button>
|
||||
<a href="trash.html" class="btn" title="View database inconsistencies" style="text-decoration:none;display:inline-flex;align-items:center;">🗑️ Trash</a>
|
||||
<button class="btn" id="selectBtn" onclick="toggleSelectMode()">Select</button>
|
||||
<button class="btn" id="privacyBtn" onclick="togglePrivacyMode()" title="Privacy mode — hides content on focus loss (P)">🔓</button>
|
||||
<!-- Inline Services LEDs on Dashboard -->
|
||||
<div class="services-status-container" style="display:flex;align-items:center;gap:10px;background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.06);padding:4px 10px;border-radius:15px;font-family:monospace;font-size:10px;color:#888">
|
||||
<div style="display:flex;align-items:center;gap:4px;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:4px;cursor:help" id="modelStatus" title="Model-Engine: Status unknown (API server is down)">
|
||||
<span class="status-dot-led offline" id="modelDot"></span>
|
||||
<span>COMFY</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="upload-zone">
|
||||
<input type="file" id="uploadInput" multiple onchange="handleUpload(event)" />
|
||||
@@ -2103,30 +2109,65 @@
|
||||
|
||||
<!-- Studio view (replaces old lightbox + faceswap modal) -->
|
||||
<div id="studio" class="studio">
|
||||
<!-- Topbar -->
|
||||
<div class="studio-topbar">
|
||||
<div class="studio-topbar-left">
|
||||
<button class="btn" onclick="closeStudio()" style="font-size:11px;padding:4px 10px">✕ Gallery</button>
|
||||
<button class="btn" id="lbBackBtn" onclick="jumpToPrevGroup()" style="font-size:11px;padding:4px 10px;display:none" title="Go back to previously opened group">↺ Back</button>
|
||||
</div>
|
||||
<div class="studio-topbar-center">
|
||||
<input type="text" id="lbGroupNameInput"
|
||||
style="background:transparent;border:none;border-bottom:1px solid #2a2a2a;color:#fff;font-size:13px;font-weight:600;padding:2px 5px;min-width:80px;max-width:200px;outline:none"
|
||||
placeholder="Group name…"
|
||||
onblur="updateGroupName()" onkeydown="if(event.key==='Enter')this.blur()"
|
||||
title="Group name (applies to all images in this group)" />
|
||||
<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>
|
||||
</div>
|
||||
<div class="studio-topbar-right" style="display:flex;align-items:center;gap:6px">
|
||||
<button class="btn" id="studioPrivacyBtn" onclick="togglePrivacyMode()" title="Privacy mode — hides content on focus loss (P)" style="font-size:12px;padding:4px 8px">🔓</button>
|
||||
<button class="btn" id="sidebarInnerToggle" onclick="toggleSidebar()" title="Collapse/expand toolbox">⊟</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Left area: topbar + main viewer + filmstrip -->
|
||||
<div class="studio-left">
|
||||
<!-- Topbar -->
|
||||
<div class="studio-topbar">
|
||||
<div class="studio-topbar-left">
|
||||
<a href="./car.html" id="studioDashboardLink" class="btn" style="font-size:11px;padding:4px 10px;text-decoration:none;display:none;align-items:center;gap:4px">← Dashboard</a>
|
||||
<button class="btn" id="studioGalleryBtn" onclick="closeStudio()" style="font-size:11px;padding:4px 10px">✕ Gallery</button>
|
||||
<button class="btn" id="lbBackBtn" onclick="jumpToPrevGroup()" style="font-size:11px;padding:4px 10px;display:none" title="Go back to previously opened group">↺ Back</button>
|
||||
</div>
|
||||
<div class="studio-topbar-center" style="display:flex;align-items:center;gap:6px">
|
||||
<input type="text" id="lbGroupNameInput"
|
||||
style="background:transparent;border:none;border-bottom:1px solid #2a2a2a;color:#fff;font-size:13px;font-weight:600;padding:2px 5px;min-width:80px;max-width:200px;outline:none"
|
||||
placeholder="Group name…"
|
||||
onblur="updateGroupName()" onkeydown="if(event.key==='Enter')this.blur()"
|
||||
title="Group name (applies to all images in this group)" />
|
||||
|
||||
<span style="color:#444;font-size:13px">/</span>
|
||||
|
||||
<input type="text" id="lbTopbarImageNameInput"
|
||||
style="background:transparent;border:none;border-bottom:1px solid #2a2a2a;color:#ccc;font-size:12px;padding:2px 5px;min-width:80px;max-width:180px;outline:none"
|
||||
placeholder="Image name…"
|
||||
onblur="updateTopbarImageName()" onkeydown="if(event.key==='Enter')this.blur()"
|
||||
title="Image name / Character name" />
|
||||
|
||||
<div style="display:flex;gap:4px;margin-left:4px">
|
||||
<button class="sb-btn" onclick="copyRawFilename()" 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 raw filename (ID) 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>ID
|
||||
</button>
|
||||
<button class="sb-btn" onclick="copyFilepath()" 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 absolute file path 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>Path
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Body: main viewer + sidebar -->
|
||||
<div class="studio-body">
|
||||
<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>
|
||||
</div>
|
||||
<div class="studio-topbar-right" style="display:flex;align-items:center;gap:6px">
|
||||
<!-- Inline Services LEDs on Studio Topbar -->
|
||||
<div class="services-status-container" style="display:flex;align-items:center;gap:10px;background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.06);padding:4px 10px;border-radius:15px;font-family:monospace;font-size:10px;color:#888;margin-right:6px">
|
||||
<div style="display:flex;align-items:center;gap:4px;cursor:help" id="studioBackendStatus" title="Backend: Status unknown">
|
||||
<span class="status-dot-led offline" id="studioBackendDot"></span>
|
||||
<span>API</span>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:4px;cursor:help" id="studioModelStatus" title="Model-Engine: Status unknown">
|
||||
<span class="status-dot-led offline" id="studioModelDot"></span>
|
||||
<span>COMFY</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn" id="studioPrivacyBtn" onclick="togglePrivacyMode()" title="Privacy mode — hides content on focus loss (P)" style="font-size:12px;padding:4px 8px">🔓</button>
|
||||
<button class="btn" id="sidebarInnerToggle" onclick="toggleSidebar()" title="Collapse/expand toolbox">⊟</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main area: viewer + filmstrip -->
|
||||
<div class="studio-main">
|
||||
@@ -2179,19 +2220,20 @@
|
||||
<option value="15000">15s</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- Close .studio-main -->
|
||||
</div> <!-- Close .studio-left -->
|
||||
|
||||
<!-- Sidebar toolbox -->
|
||||
<!-- Sidebar toolbox -->
|
||||
<div id="studioSidebar" class="studio-sidebar">
|
||||
<div id="sidebarInner" class="sidebar-inner">
|
||||
<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')">Ceate</button>
|
||||
<button class="sb-tab" data-tab="faceswap" onclick="switchSidebarTab('faceswap')">👤</button>
|
||||
<button class="sb-tab" data-tab="scenery" onclick="switchSidebarTab('scenery')">🎬</button>
|
||||
<button class="sb-tab" data-tab="segment" onclick="switchSidebarTab('segment')">α</button>
|
||||
<button class="sb-tab" data-tab="orbit" onclick="switchSidebarTab('orbit')" id="sbTabOrbit">⚛</button>
|
||||
<button class="sb-tab" data-tab="designer" onclick="switchSidebarTab('designer')" id="sbTabDesigner">Design</button>
|
||||
<button class="sb-tab active" data-tab="info" onclick="switchSidebarTab('info')" title="Info">ℹ️</button>
|
||||
<button class="sb-tab" data-tab="generate" onclick="switchSidebarTab('generate')" title="Create">🪄</button>
|
||||
<button class="sb-tab" data-tab="faceswap" onclick="switchSidebarTab('faceswap')" title="Faceswap">👤</button>
|
||||
<button class="sb-tab" data-tab="scenery" onclick="switchSidebarTab('scenery')" title="Scenery">🎬</button>
|
||||
<button class="sb-tab" data-tab="segment" onclick="switchSidebarTab('segment')" title="Segment / Background">α</button>
|
||||
<button class="sb-tab" data-tab="orbit" onclick="switchSidebarTab('orbit')" id="sbTabOrbit" title="Orbit 3D">⚛</button>
|
||||
<button class="sb-tab" data-tab="designer" onclick="switchSidebarTab('designer')" id="sbTabDesigner" title="Design / Refine">🎨</button>
|
||||
</div>
|
||||
<div class="sb-panels">
|
||||
<!-- Info panel: static HTML with existing lb* IDs so updateStudio() just works -->
|
||||
@@ -2355,7 +2397,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Orbit preview modal -->
|
||||
<div id="orbitModal">
|
||||
@@ -2503,6 +2544,36 @@
|
||||
|
||||
// --- HYDRATION_START ---
|
||||
const PRELOADED_IMAGES = [
|
||||
"20260701_051754_image.png",
|
||||
"20260701_051737_image.png",
|
||||
"20260701_051721_image.png",
|
||||
"20260701_051704_image.png",
|
||||
"20260701_051647_image.png",
|
||||
"20260701_051614_image.png",
|
||||
"20260701_051541_image.png",
|
||||
"20260701_051520_image.png",
|
||||
"20260701_050249_image.png",
|
||||
"20260701_050232_image.png",
|
||||
"20260701_050215_image.png",
|
||||
"20260701_050158_image.png",
|
||||
"20260701_050141_image.png",
|
||||
"20260701_050124_image.png",
|
||||
"20260701_050108_image.png",
|
||||
"20260701_050051_image.png",
|
||||
"20260701_050035_image.png",
|
||||
"20260701_050018_image.png",
|
||||
"20260701_050001_image.png",
|
||||
"20260701_045945_image.png",
|
||||
"20260701_045928_image.png",
|
||||
"20260701_045912_image.png",
|
||||
"20260701_045838_image.png",
|
||||
"20260701_045822_image.png",
|
||||
"20260701_045805_image.png",
|
||||
"20260701_045749_image.png",
|
||||
"20260701_045732_image.png",
|
||||
"20260701_045716_image.png",
|
||||
"20260701_045659_1_20260619_202924_image.png",
|
||||
"20260620_014953_image.png",
|
||||
"20260630_130454_sc_0_20260622_101128_image.nobg.png",
|
||||
"20260630_131638_sc_2_20260622_101128_image.nobg.nobg.png",
|
||||
"20260630_151502_sc_0_20260622_101128_image.nobg.png",
|
||||
@@ -7232,7 +7303,6 @@
|
||||
"20260620_020313_2_20260618_172500_image.png",
|
||||
"20260620_020302_2_20260618_172500_image.png",
|
||||
"20260620_020251_2_20260618_172500_image.png",
|
||||
"20260620_014953_image.png",
|
||||
"20260620_014646_image.png",
|
||||
"20260620_014349_image.png",
|
||||
"20260620_014239_img_77.png",
|
||||
@@ -7943,6 +8013,7 @@
|
||||
// ============================================
|
||||
|
||||
let autoRefreshTimer = null;
|
||||
let isInitialLoad = true;
|
||||
let knownFiles = new Set();
|
||||
let currentFiles = [];
|
||||
let currentGroups = [];
|
||||
@@ -8175,6 +8246,7 @@
|
||||
|
||||
// --- studio view (replaces lightbox) ---
|
||||
let lbUrls = [], lbNames = [], lbIdx = 0;
|
||||
let _lastRenderedFilename = null;
|
||||
let _activeFilmstripTab = 'active'; // 'active', 'group', 'hidden', 'source', 'archived'
|
||||
let _multiSelectModeActive = false;
|
||||
const _selectedFilenames = new Set();
|
||||
@@ -9123,6 +9195,23 @@
|
||||
|
||||
function updateStudio() {
|
||||
const fname = lbNames[lbIdx];
|
||||
if (fname && fname !== _lastRenderedFilename) {
|
||||
// Image changed, force cancel any active text areas/edits
|
||||
_lastRenderedFilename = fname;
|
||||
const genPromptEl = document.getElementById('lbGenPrompt');
|
||||
if (genPromptEl) {
|
||||
genPromptEl.innerHTML = '';
|
||||
genPromptEl.onclick = () => editMetadataField('prompt');
|
||||
}
|
||||
const descEl = document.getElementById('lbDescription');
|
||||
if (descEl) {
|
||||
descEl.innerHTML = '';
|
||||
descEl.onclick = () => editMetadataField('description');
|
||||
}
|
||||
} else if (!fname) {
|
||||
_lastRenderedFilename = null;
|
||||
}
|
||||
|
||||
if (document.getElementById('poseCanvas')) _removePoseOverlay(); // skeleton is per-image
|
||||
document.getElementById('poseResults')?.remove();
|
||||
|
||||
@@ -9256,6 +9345,9 @@
|
||||
const nameInput = document.getElementById('lbNameInput');
|
||||
if (nameInput) { nameInput.value = imageNames[fname] || ''; nameInput.placeholder = fname; }
|
||||
|
||||
const topbarNameInput = document.getElementById('lbTopbarImageNameInput');
|
||||
if (topbarNameInput) { topbarNameInput.value = imageNames[fname] || ''; topbarNameInput.placeholder = fname; }
|
||||
|
||||
const clipEl = document.getElementById('lbClipDesc');
|
||||
if (clipEl) clipEl.textContent = clipDescriptions[fname] || '';
|
||||
|
||||
@@ -9682,6 +9774,8 @@
|
||||
});
|
||||
if (r.ok) {
|
||||
imageNames[fname] = newName;
|
||||
const topbarNameInput = document.getElementById('lbTopbarImageNameInput');
|
||||
if (topbarNameInput) topbarNameInput.value = newName;
|
||||
showToast(`Name updated`);
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -9689,6 +9783,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function updateTopbarImageName() {
|
||||
const fname = lbNames[lbIdx];
|
||||
const topbarNameInput = document.getElementById('lbTopbarImageNameInput');
|
||||
if (!topbarNameInput) return;
|
||||
const newName = topbarNameInput.value;
|
||||
|
||||
const nameInput = document.getElementById('lbNameInput');
|
||||
if (nameInput) nameInput.value = newName;
|
||||
|
||||
await updateName();
|
||||
}
|
||||
|
||||
async function updateGroupName() {
|
||||
const gid = lbCurrentGid;
|
||||
if (!gid) return;
|
||||
@@ -11375,10 +11481,21 @@
|
||||
async function _pollTurntableBadge() {
|
||||
try {
|
||||
let d = null;
|
||||
try {
|
||||
const r = await fetch(`${API}/output/_data/turntable_status.json?t=` + Date.now(), { signal: AbortSignal.timeout(2000) });
|
||||
if (r.ok) d = await r.json();
|
||||
} catch (_) {}
|
||||
const gid = lbCurrentGid;
|
||||
|
||||
if (gid) {
|
||||
try {
|
||||
const r = await fetch(`${API}/turntable/status/${encodeURIComponent(gid)}`);
|
||||
if (r.ok) d = await r.json();
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
if (!d) {
|
||||
try {
|
||||
const r = await fetch(`${API}/output/_data/turntable_status.json?t=` + Date.now(), { signal: AbortSignal.timeout(2000) });
|
||||
if (r.ok) d = await r.json();
|
||||
} catch (_) {}
|
||||
}
|
||||
if (!d) {
|
||||
const r = await fetch(`${API}/turntable/status`);
|
||||
if (r.ok) d = await r.json();
|
||||
@@ -11389,7 +11506,7 @@
|
||||
const tbsDot = document.getElementById('tbsDot');
|
||||
const tbsText = document.getElementById('tbsText');
|
||||
const total = d.n_total || 0;
|
||||
const done = d.n_complete || 0;
|
||||
const done = (d.n_complete !== undefined ? d.n_complete : d.n_done) || 0;
|
||||
if (total === 0) { badge.style.display = 'none'; }
|
||||
else if (done < total) { badge.textContent = `${done}/${total}`; badge.style.display = ''; }
|
||||
else { badge.style.display = 'none'; }
|
||||
@@ -11413,8 +11530,15 @@
|
||||
|
||||
// Refresh orbit tab badge on the tab button
|
||||
const tabBtn = document.getElementById('sbTabOrbit');
|
||||
if (tabBtn && done > 0) {
|
||||
tabBtn.textContent = `Orbit${done < total ? ' (' + done + '/' + total + ')' : ' ✓'}`;
|
||||
if (tabBtn) {
|
||||
tabBtn.title = `Orbit 3D (${done}/${total})`;
|
||||
if (total > 0 && done < total) {
|
||||
tabBtn.innerHTML = `⚛<span style="font-size: 8px; vertical-align: super; background: #fbbf24; color: #1e1b4b; padding: 1px 3px; border-radius: 6px; margin-left: 1px; display: inline-block; line-height: 1; font-weight: bold;">${done}/${total}</span>`;
|
||||
} else if (total > 0 && done === total) {
|
||||
tabBtn.innerHTML = `⚛<span style="font-size: 8px; vertical-align: super; background: #10b981; color: #fff; padding: 1px 3px; border-radius: 6px; margin-left: 1px; display: inline-block; line-height: 1; font-weight: bold;">✓</span>`;
|
||||
} else {
|
||||
tabBtn.innerHTML = `⚛`;
|
||||
}
|
||||
}
|
||||
} catch(_) {}
|
||||
}
|
||||
@@ -12155,7 +12279,8 @@
|
||||
}
|
||||
|
||||
document.getElementById('count').textContent = `${currentGroups.length} group${currentGroups.length !== 1 ? 's' : ''} · ${fileObjects.length} images`;
|
||||
if (newFileNames.size > 0) showToast(`${newFileNames.size} new image${newFileNames.size !== 1 ? 's' : ''} detected`, 'success');
|
||||
if (newFileNames.size > 0 && !isInitialLoad) showToast(`${newFileNames.size} new image${newFileNames.size !== 1 ? 's' : ''} detected`, 'success');
|
||||
isInitialLoad = false;
|
||||
|
||||
// If studio is open, sync lbUrls/lbNames from freshly-loaded groupData
|
||||
const studioEl = document.getElementById('studio');
|
||||
@@ -12419,18 +12544,36 @@
|
||||
_backendOnline = true;
|
||||
_modelEngineOnline = data.model_engine.online;
|
||||
|
||||
const backendClass = data.backend.busy ? 'status-dot-led busy' : 'status-dot-led online';
|
||||
const backendTitle = data.backend.busy ? 'Backend: Active task running' : 'Backend: Idle (Online)';
|
||||
|
||||
const modelClass = data.model_engine.online ? (data.model_engine.busy ? 'status-dot-led busy' : 'status-dot-led online') : 'status-dot-led offline';
|
||||
const modelTitle = data.model_engine.online
|
||||
? (data.model_engine.busy ? 'Model-Engine: Active inference running' : 'Model-Engine: Idle (Online)')
|
||||
: 'Model-Engine: Offline (ComfyUI backend is down)';
|
||||
|
||||
// Dashboard dots
|
||||
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)';
|
||||
backendDot.className = backendClass;
|
||||
backendDot.parentElement.title = backendTitle;
|
||||
}
|
||||
|
||||
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)';
|
||||
modelDot.className = modelClass;
|
||||
modelDot.parentElement.title = modelTitle;
|
||||
}
|
||||
|
||||
// Studio topbar dots
|
||||
const studioBackendDot = document.getElementById('studioBackendDot');
|
||||
if (studioBackendDot) {
|
||||
studioBackendDot.className = backendClass;
|
||||
studioBackendDot.parentElement.title = backendTitle;
|
||||
}
|
||||
const studioModelDot = document.getElementById('studioModelDot');
|
||||
if (studioModelDot) {
|
||||
studioModelDot.className = modelClass;
|
||||
studioModelDot.parentElement.title = modelTitle;
|
||||
}
|
||||
} else {
|
||||
throw new Error("API responded with error");
|
||||
@@ -12439,15 +12582,32 @@
|
||||
_backendOnline = false;
|
||||
_modelEngineOnline = false;
|
||||
|
||||
const offlineClass = 'status-dot-led offline';
|
||||
const backendOfflineTitle = 'Backend: Offline (API server is down)';
|
||||
const modelOfflineTitle = 'Model-Engine: Status unknown (API server is down)';
|
||||
|
||||
// Dashboard dots
|
||||
const backendDot = document.getElementById('backendDot');
|
||||
if (backendDot) {
|
||||
backendDot.className = 'status-dot-led offline';
|
||||
backendDot.parentElement.title = 'Backend: Offline (API server is down)';
|
||||
backendDot.className = offlineClass;
|
||||
backendDot.parentElement.title = backendOfflineTitle;
|
||||
}
|
||||
const modelDot = document.getElementById('modelDot');
|
||||
if (modelDot) {
|
||||
modelDot.className = 'status-dot-led offline';
|
||||
modelDot.parentElement.title = 'Model-Engine: Status unknown (API server is down)';
|
||||
modelDot.className = offlineClass;
|
||||
modelDot.parentElement.title = modelOfflineTitle;
|
||||
}
|
||||
|
||||
// Studio topbar dots
|
||||
const studioBackendDot = document.getElementById('studioBackendDot');
|
||||
if (studioBackendDot) {
|
||||
studioBackendDot.className = offlineClass;
|
||||
studioBackendDot.parentElement.title = backendOfflineTitle;
|
||||
}
|
||||
const studioModelDot = document.getElementById('studioModelDot');
|
||||
if (studioModelDot) {
|
||||
studioModelDot.className = offlineClass;
|
||||
studioModelDot.parentElement.title = modelOfflineTitle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13007,6 +13167,14 @@
|
||||
document.title = PRIV_TITLE;
|
||||
showPrivacyOverlay();
|
||||
}
|
||||
|
||||
// Direct-to-dashboard back link for group specific pages
|
||||
if (typeof LOAD_ONLY_GROUP_ID !== 'undefined' && LOAD_ONLY_GROUP_ID) {
|
||||
const dashLink = document.getElementById('studioDashboardLink');
|
||||
const galBtn = document.getElementById('studioGalleryBtn');
|
||||
if (dashLink) dashLink.style.display = 'inline-flex';
|
||||
if (galBtn) galBtn.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
async function toggleTagAction(tagName) {
|
||||
@@ -13095,7 +13263,7 @@
|
||||
fileDescriptions[fname] = newDesc;
|
||||
showToast('Successfully updated both prompt and description!', 'success');
|
||||
updateStudio();
|
||||
refreshNow();
|
||||
refreshNow(true);
|
||||
} else {
|
||||
showToast('Failed to update prompt or description', 'error');
|
||||
}
|
||||
@@ -13120,17 +13288,19 @@
|
||||
const el = document.getElementById(contentId);
|
||||
if (!el || el.querySelector('textarea')) return;
|
||||
|
||||
const buttonLabel = currentVal.trim() === '' ? '✨ Create' : '✨ Refine';
|
||||
|
||||
el.onclick = null;
|
||||
el.innerHTML = `
|
||||
<textarea id="editFieldArea" style="width:100%;min-height:80px;background:#0d0d0d;border:1px solid #333;color:#ccc;font-size:11px;font-family:monospace;padding:6px;border-radius:4px;outline:none;resize:vertical;line-height:1.4;margin-bottom:4px">${currentVal}</textarea>
|
||||
<textarea id="editFieldArea_${field}" style="width:100%;min-height:80px;background:#0d0d0d;border:1px solid #333;color:#ccc;font-size:11px;font-family:monospace;padding:6px;border-radius:4px;outline:none;resize:vertical;line-height:1.4;margin-bottom:4px">${currentVal}</textarea>
|
||||
<div style="display:flex;gap:6px;justify-content:flex-end">
|
||||
<button class="sb-btn" style="padding:2px 8px;font-size:10px" onclick="cancelEditField(event, '${field}')">Cancel</button>
|
||||
<button class="sb-btn" style="padding:2px 8px;font-size:10px" onclick="refineField(event, '${field}')">✨ Refine</button>
|
||||
<button class="sb-btn" style="padding:2px 8px;font-size:10px" onclick="refineField(event, '${field}')">${buttonLabel}</button>
|
||||
<button class="sb-btn" style="padding:2px 8px;font-size:10px;background:#10b981;color:#fff" onclick="saveEditField(event, '${field}')">Save</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const ta = document.getElementById('editFieldArea');
|
||||
const ta = document.getElementById(`editFieldArea_${field}`);
|
||||
if (ta) {
|
||||
ta.focus();
|
||||
ta.setSelectionRange(ta.value.length, ta.value.length);
|
||||
@@ -13151,7 +13321,7 @@
|
||||
const fname = lbNames[lbIdx];
|
||||
if (!fname) return;
|
||||
|
||||
const ta = document.getElementById('editFieldArea');
|
||||
const ta = document.getElementById(`editFieldArea_${field}`);
|
||||
if (!ta) return;
|
||||
const newVal = ta.value.trim();
|
||||
|
||||
@@ -13174,7 +13344,7 @@
|
||||
showToast('Character & scenery description updated successfully', 'success');
|
||||
}
|
||||
updateStudio();
|
||||
refreshNow();
|
||||
refreshNow(true);
|
||||
} else {
|
||||
showToast('Failed to save update', 'error');
|
||||
}
|
||||
@@ -13189,24 +13359,30 @@
|
||||
const fname = lbNames[lbIdx];
|
||||
if (!fname) return;
|
||||
|
||||
const ta = document.getElementById('editFieldArea');
|
||||
const ta = document.getElementById(`editFieldArea_${field}`);
|
||||
if (!ta) return;
|
||||
const currentVal = ta.value.trim();
|
||||
|
||||
const instruction = prompt(`Enter instructions to refine this ${field}:`, "Make it more detailed...");
|
||||
const actionVerb = currentVal === '' ? 'create' : 'refine';
|
||||
const instruction = prompt(`Enter instructions to ${actionVerb} this ${field}:`, actionVerb === 'create' ? "Write a detailed description of..." : "Make it more detailed...");
|
||||
if (!instruction) return;
|
||||
|
||||
showToast('AI is refining your text...', 'info');
|
||||
showToast(`AI is generating your ${field}...`, 'info');
|
||||
try {
|
||||
const r = await fetch(`${API}/refine-prompt`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ prompt: currentVal, user_instruction: instruction })
|
||||
body: JSON.stringify({
|
||||
prompt: currentVal,
|
||||
user_instruction: instruction,
|
||||
filename: fname,
|
||||
field: field
|
||||
})
|
||||
});
|
||||
if (r.ok) {
|
||||
const data = await r.json();
|
||||
ta.value = data.refined;
|
||||
showToast('Refined successfully! Click Save to apply.', 'success');
|
||||
showToast('Successfully updated! Click Save to apply.', 'success');
|
||||
} else {
|
||||
showToast('Failed to refine field: ' + await r.text(), 'error');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user