459 lines
22 KiB
HTML
459 lines
22 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
|
|
<head>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
<style>
|
|
/* INJECTED CSS */
|
|
__CSS_CONTENT__
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<!-- COMPACT SINGLE-LINE HEADER -->
|
|
<div id="header">
|
|
<button class="session-popup-btn" onclick="toggleSessionPopup()" title="Session Management">
|
|
SESSION
|
|
</button>
|
|
<button class="filter-popup-btn" onclick="toggleFiltersPopup()" title="Open Filters & Info">
|
|
FILTERS
|
|
</button>
|
|
|
|
<div style="flex-grow: 1;"></div>
|
|
|
|
<div class="meta-box">
|
|
<input type="text" inputmode="numeric" id="goto-input" min="1" placeholder="Go to # "
|
|
title="Jump to image number (press Enter)" style="width: 50px;">
|
|
</div>
|
|
|
|
<div class="meta-box">
|
|
<span class="meta-label">Cols</span>
|
|
<input type="text" inputmode="numeric" id="col-count" min="0" placeholder="Auto"
|
|
title="Set grid columns (0 = Auto)">
|
|
</div>
|
|
|
|
<div class="meta-box" style="overflow: visible;"> <span class="meta-label">Sort</span>
|
|
|
|
<div class="sort-container" id="sort-container" ">
|
|
<button class=" sort-btn" id="sort-main-btn" onclick="toggleSortPopup()">
|
|
<span id="sort-primary-label">Oldest</span>
|
|
<span style="opacity:0.5; font-size:8px;">▼</span>
|
|
</button>
|
|
|
|
<div id="sort-popup" class="sort-popup-panel" style="display: none;">
|
|
<div class="sort-col">
|
|
<div class="sort-col-header" style="color:var(--accent);">PRIMARY</div>
|
|
<div id="sort-list-primary">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="sort-col">
|
|
<div class="sort-col-header" style="color:var(--accent-sch);">SECONDARY</div>
|
|
<div id="sort-list-secondary">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button class="action-btn" onclick="resetZoom()" title="Reset Zoom (0)">⊙</button>
|
|
|
|
<button class="fs-btn" onclick="toggleFullscreen()" title="Toggle Fullscreen">⛶</button>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div id="filters-popup" class="filters-popup" style="display: none;">
|
|
<div class="filters-popup-header">
|
|
<h3>Filters & Info</h3>
|
|
<button class="close-popup-btn" onclick="toggleFiltersPopup()">✕</button>
|
|
</div>
|
|
|
|
<div class="filters-popup-content">
|
|
<!-- TOP-LEVEL FILTERS SECTION -->
|
|
<div class="popup-section"
|
|
style="border-bottom: 2px solid #333; padding-bottom: 16px; margin-bottom: 16px;">
|
|
<div class="popup-section-title">VISIBILITY</div>
|
|
<div style="display: flex; gap: 10px; margin-top: 10px;">
|
|
<button id="top-filter-favorites" class="filter-btn top-filter-btn active"
|
|
onclick="toggleFavoritesFilter()" title="Toggle visibility of favorited images">
|
|
⭐ Favorites
|
|
</button>
|
|
<button id="top-filter-nonfavorited" class="filter-btn top-filter-btn active"
|
|
onclick="toggleNonFavoritedFilter()" title="Toggle visibility of non-favorited images">
|
|
◯ Non-Favorited
|
|
</button>
|
|
<button id="top-filter-rejected" class="filter-btn top-filter-btn" onclick="toggleRejectedFilter()"
|
|
title="Toggle visibility of rejected images">
|
|
✕ Rejected
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- SEARCH FILTER SECTION -->
|
|
<div class="popup-section">
|
|
<div class="popup-section-title">SEARCH FILTER</div>
|
|
<div class="search-filter-container">
|
|
<div class="search-filter-row">
|
|
<select id="search-filter-type" class="search-filter-select">
|
|
<option value="">Select filter type...</option>
|
|
<option value="model">Model</option>
|
|
<option value="positive">Positive Prompt</option>
|
|
<option value="negative">Negative Prompt</option>
|
|
<option value="sampler">Sampler</option>
|
|
<option value="scheduler">Scheduler</option>
|
|
<option value="lora">LoRA</option>
|
|
</select>
|
|
<input type="text" id="search-filter-input" class="search-filter-input"
|
|
placeholder="Enter search term..." onkeypress="if(event.key==='Enter') addSearchFilter()">
|
|
<button class="search-filter-add-btn" onclick="addSearchFilter()">ADD</button>
|
|
</div>
|
|
<div id="active-search-filters" class="active-search-filters"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Meta Info Section -->
|
|
|
|
|
|
<!-- Filters Section -->
|
|
<div class="popup-section">
|
|
<div class="popup-section-title">FILTERS <span
|
|
style="font-size: 9px; color: #555; font-weight: normal;">(Shift+Click to isolate)</span></div>
|
|
<br />
|
|
<div class="filter-row">
|
|
<span class="filter-label">Model</span>
|
|
<div id="filter-model" class="filter-buttons-container"></div>
|
|
</div>
|
|
|
|
<div class="filter-row">
|
|
<span class="filter-label">Positive Prompt</span>
|
|
<div id="filter-positive" class="filter-buttons-container"></div>
|
|
</div>
|
|
|
|
<div class="filter-row">
|
|
<span class="filter-label">Negative Prompt</span>
|
|
<div id="filter-negative" class="filter-buttons-container"></div>
|
|
</div>
|
|
|
|
<div class="filter-row">
|
|
<span class="filter-label">Sampler</span>
|
|
<div id="filter-sampler" class="filter-buttons-container"></div>
|
|
</div>
|
|
|
|
<div class="filter-row">
|
|
<span class="filter-label">Scheduler</span>
|
|
<div id="filter-scheduler" class="filter-buttons-container"></div>
|
|
</div>
|
|
|
|
<div class="filter-row">
|
|
<span class="filter-label">Denoise</span>
|
|
<div id="filter-denoise" class="filter-buttons-container"></div>
|
|
</div>
|
|
|
|
<div class="filter-row">
|
|
<span class="filter-label">LoRA</span>
|
|
<div id="filter-lora" class="filter-buttons-container"></div>
|
|
</div>
|
|
|
|
<div class="filter-row">
|
|
<span class="filter-label">Size</span>
|
|
<div id="filter-size" class="filter-buttons-container"></div>
|
|
</div>
|
|
|
|
<div class="filter-row">
|
|
<span class="filter-label">Seed</span>
|
|
<div id="filter-seed" class="filter-buttons-container"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Keyboard Shortcuts Reference -->
|
|
<div class="popup-section">
|
|
<div class="popup-section-title">KEYBOARD SHORTCUTS</div>
|
|
<table style="width: 100%; border-collapse: collapse; margin-top: 8px; font-size: 11px;">
|
|
<tr style="border-bottom: 1px solid #333;">
|
|
<td style="padding: 4px 8px; color: #0af; font-family: monospace;">Space</td>
|
|
<td style="padding: 4px 8px; color: #ccc;">Scroll down one row</td>
|
|
</tr>
|
|
<tr style="border-bottom: 1px solid #333;">
|
|
<td style="padding: 4px 8px; color: #0af; font-family: monospace;">Shift+Space</td>
|
|
<td style="padding: 4px 8px; color: #ccc;">Scroll up one row</td>
|
|
</tr>
|
|
<tr style="border-bottom: 1px solid #333;">
|
|
<td style="padding: 4px 8px; color: #0af; font-family: monospace;">Arrow Keys</td>
|
|
<td style="padding: 4px 8px; color: #ccc;">Pan in any direction</td>
|
|
</tr>
|
|
<tr style="border-bottom: 1px solid #333;">
|
|
<td style="padding: 4px 8px; color: #0af; font-family: monospace;">+ / -</td>
|
|
<td style="padding: 4px 8px; color: #ccc;">Zoom in / out</td>
|
|
</tr>
|
|
<tr style="border-bottom: 1px solid #333;">
|
|
<td style="padding: 4px 8px; color: #0af; font-family: monospace;">0</td>
|
|
<td style="padding: 4px 8px; color: #ccc;">Reset zoom to 1:1</td>
|
|
</tr>
|
|
<tr style="border-bottom: 1px solid #333;">
|
|
<td style="padding: 4px 8px; color: #0af; font-family: monospace;">F</td>
|
|
<td style="padding: 4px 8px; color: #ccc;">Auto-fit first row to viewport</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding: 4px 8px; color: #0af; font-family: monospace;">Shift+Click</td>
|
|
<td style="padding: 4px 8px; color: #ccc;">Isolate a single filter</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Overlay for popup -->
|
|
<div id="filters-overlay" class="filters-overlay" onclick="toggleFiltersPopup()" style="display: none;"></div>
|
|
|
|
|
|
<!-- SESSION POPUP PANEL -->
|
|
<div id="session-popup" class="session-dropdown" style="display: none;">
|
|
<div class="session-dropdown-content">
|
|
<input id="session-input" class="session-name-input" type="text" value="__TITLE__"
|
|
placeholder="Session name...">
|
|
<div class="session-actions">
|
|
<button class="session-action-btn save-btn" onclick="saveState()">SAVE</button>
|
|
<button class="session-action-btn load-btn" onclick="loadSession()">LOAD</button>
|
|
</div>
|
|
|
|
<!-- NEW: Export Favorites Section -->
|
|
<div style="border-top: 1px solid #333; margin-top: 12px; padding-top: 12px;">
|
|
<button class="session-action-btn"
|
|
style="background: #00d1b2; width: 100%; font-size: 11px; padding: 10px;"
|
|
onclick="exportFavorites()" title="Export favorited images to benchmark_favorites folder">
|
|
📦 COPY FAVORITES TO BENCHMARK FAVORITES FOLDER
|
|
</button>
|
|
|
|
<div style="margin-top: 8px; display: flex; align-items: center; gap: 6px;">
|
|
<input type="checkbox" checked id="pack-metadata-checkbox"
|
|
style="width: 14px; height: 14px; cursor: pointer;">
|
|
<label for="pack-metadata-checkbox"
|
|
style="font-size: 10px; color: #999; cursor: pointer; user-select: none;">
|
|
Pack metadata into copied favorites (for CivitAI)
|
|
</label>
|
|
</div>
|
|
|
|
<div style="margin-top: 8px; display: flex; align-items: center; gap: 6px;">
|
|
<input type="checkbox" id="organize-by-prompt-checkbox"
|
|
style="width: 14px; height: 14px; cursor: pointer;">
|
|
<label for="organize-by-prompt-checkbox"
|
|
style="font-size: 10px; color: #999; cursor: pointer; user-select: none;">
|
|
Organize into separate folders by prompt (Prompt1, Prompt2, etc.)
|
|
</label>
|
|
</div>
|
|
<div style="margin-top: 8px; display: flex; align-items: center; gap: 6px;">
|
|
|
|
<input type="checkbox" id="organize-by-lora-checkbox"
|
|
style="width: 14px; height: 14px; cursor: pointer;">
|
|
<label for="organize-by-lora-checkbox"
|
|
style="font-size: 10px; color: #999; cursor: pointer; user-select: none;">
|
|
Organize into separate folders by lora sets (loras1, loras2, etc.)
|
|
</label>
|
|
|
|
</div>
|
|
|
|
<div id="export-status" style="margin-top: 8px; font-size: 10px; color: #666; min-height: 16px;"></div>
|
|
</div>
|
|
|
|
<!-- SCAN EXTERNAL DIRECTORY Section -->
|
|
<div style="border-top: 1px solid #333; margin-top: 12px; padding-top: 12px;">
|
|
<div style="font-size: 10px; color: #888; margin-bottom: 6px; font-weight: bold; letter-spacing: 0.5px;">
|
|
SCAN EXTERNAL DIRECTORY
|
|
</div>
|
|
<div style="font-size: 9px; color: #666; margin-bottom: 8px;">
|
|
Load any folder of images into the dashboard. Reads PNG metadata for filtering/sorting.
|
|
</div>
|
|
<div style="display: flex; gap: 6px; align-items: center;">
|
|
<input type="text" id="scan-directory-input"
|
|
placeholder="Paste absolute directory path..."
|
|
style="flex: 1; background: #000; border: 1px solid #333; color: #fff;
|
|
padding: 8px; font-size: 11px; font-family: monospace; border-radius: 3px;"
|
|
onkeydown="if(event.key==='Enter')scanDirectory()">
|
|
<button class="session-action-btn" id="scan-directory-btn"
|
|
style="background: #6366f1; white-space: nowrap; padding: 8px 16px; font-size: 11px; font-weight: bold;"
|
|
onclick="scanDirectory()">
|
|
SCAN
|
|
</button>
|
|
</div>
|
|
<div id="scan-status" style="margin-top: 6px; font-size: 10px; color: #666; min-height: 16px;"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="viewport">
|
|
<div id="canvas">
|
|
<div class="grid" id="grid"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- MOBILE NAVIGATION BUTTONS -->
|
|
<button id="mobile-nav-up" class="mobile-nav-btn mobile-nav-up" onclick="scrollUpOneRow()" title="Scroll Up">
|
|
▲
|
|
</button>
|
|
<button id="mobile-nav-down" class="mobile-nav-btn mobile-nav-down" onclick="scrollDownOneRow()"
|
|
title="Scroll Down">
|
|
▼
|
|
</button>
|
|
|
|
<div class="json-container">
|
|
<!-- Button row -->
|
|
<div style="display: flex; gap: 0;">
|
|
<button id="json-btn-good" class="json-view-btn" onclick="viewGoodJSON()"
|
|
title="Click to view accepted configs JSON">
|
|
View Configs (0)
|
|
</button>
|
|
<button id="json-btn-favorite" class="json-view-btn" onclick="viewFavoritedJSON()"
|
|
title="Click to view favorited configs JSON">
|
|
View Favorited (0)
|
|
</button>
|
|
<button id="json-btn-bad" class="json-view-btn" onclick="viewRejectedJSON()"
|
|
title="Click to view rejected configs JSON">
|
|
View Rejected (0)
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Hidden JSON display areas (shown when button is clicked) -->
|
|
<div id="json-bar-good" class="json-bar" style="display: none;" title="ACCEPTED CONFIGS (Click to select)"
|
|
onclick="selectJSON('json-bar-good')">[]</div>
|
|
<div id="json-bar-favorite" class="json-bar" style="display: none;" title="FAVORITED CONFIGS (Click to select)"
|
|
onclick="selectJSON('json-bar-favorite')">[]</div>
|
|
<div id="json-bar-bad" class="json-bar" style="display: none;" title="REJECTED CONFIGS (Click to select)"
|
|
onclick="selectJSON('json-bar-bad')">[]</div>
|
|
</div>
|
|
|
|
<div id="modal">
|
|
<div class="modal-main">
|
|
<div class="preview"><img id="m-img" src=""></div>
|
|
<div class="controls">
|
|
<h2 style="margin:0 0 10px 0; color:#fff;">Revise & Generate</h2>
|
|
<button class="modal-action-btn copy-comfy-btn" onclick="copyConfigsAsComfyNodes(window.currentModalId)"
|
|
style="background: #6366f1; margin-top: 10px;">
|
|
📋 Copy Configs To Clipboard As Comfy Nodes
|
|
</button>
|
|
<div class="field"><label>Model</label><input id="f-model" disabled style="opacity:0.6;"></div>
|
|
<div class="field"><label>Seed</label><input id="f-seed" type="number" disabled style="opacity:0.6;">
|
|
</div>
|
|
<div class="field"><label>Positive Prompt</label><textarea id="f-pos" rows="3"
|
|
style="background:#000; border:1px solid #333; color:#fff; padding:8px; font-size:12px; font-family:inherit; resize:vertical;"></textarea>
|
|
</div>
|
|
<div class="field"><label>Negative Prompt</label><textarea id="f-neg" rows="2"
|
|
style="background:#000; border:1px solid #333; color:#fff; padding:8px; font-size:12px; font-family:inherit; resize:vertical;"></textarea>
|
|
</div>
|
|
<div style="border-top:1px solid #333; margin:10px 0; padding-top:10px;">
|
|
<div style="font-size:11px; color:#666; margin-bottom:8px;">ADJUST PARAMETERS:</div>
|
|
</div>
|
|
<div class="field"><label>Sampler</label><input id="f-smp"></div>
|
|
<div class="field"><label>Scheduler</label><input id="f-sch"></div>
|
|
<div class="field"><label>Steps</label><input id="f-stp" type="number"></div>
|
|
<div class="field"><label>CFG</label><input id="f-cfg" type="number" step="0.5"></div>
|
|
<div class="field"><label>Denoise</label><input id="f-den" type="number" step="0.05"></div>
|
|
<div class="field"><label>LoRA</label><input id="f-lor"></div>
|
|
<div style="margin-top:auto; display:flex; gap:10px;">
|
|
<button class="action-btn"
|
|
style="flex:2; background:var(--accent); color:#000; border:none; height:36px; font-weight:800"
|
|
onclick="triggerGen(this)">GENERATE NEW</button>
|
|
<button class="action-btn" style="flex:1" onclick="closeM()">CANCEL</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="reel-wrap">
|
|
<div style="font-size:10px; font-weight:bold; color:#666;">RELATED VARIANTS</div>
|
|
<div class="reel" id="reel"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// INJECTED DATA
|
|
const TARGET_NODE_ID = "__NODE_ID__";
|
|
let fullManifest = __JSON_DATA__;
|
|
|
|
// INJECTED LOGIC
|
|
__JS_CONTENT__
|
|
|
|
</script>
|
|
<style>
|
|
/* Default behavior: The button is visible */
|
|
.gh-hide-on-mobile {
|
|
display: block;
|
|
}
|
|
|
|
/* CONDITION 1: Hide if screen is narrower than 768px (Mobile/Tablets) */
|
|
@media (max-width: 768px) {
|
|
.gh-hide-on-mobile {
|
|
display: none !important;
|
|
}
|
|
}
|
|
|
|
/* CONDITION 2: Hide if screen is in Portrait Mode (Taller than it is wide) */
|
|
@media (orientation: portrait) {
|
|
.gh-hide-on-mobile {
|
|
display: none !important;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<div class="gh-hide-on-mobile"
|
|
style="position: absolute; top: 2px; left: 50%; transform: translateX(-50%); z-index: 999;">
|
|
<a href="https://github.com/JasonHoku/ComfyUI-Ultimate-Auto-Sampler-Config-Grid-Testing-Suite" target="_blank"
|
|
style="
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
background-color: #313131;
|
|
border: 1px solid rgba(27, 31, 35, 0.15);
|
|
border-radius: 6px;
|
|
color: #e6e6e6;
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
line-height: 1;
|
|
padding: 3px 10px;
|
|
line-height: 20px;
|
|
text-decoration: none;
|
|
white-space: nowrap;
|
|
" onmouseover="this.style.backgroundColor='#747474'" onmouseout="this.style.backgroundColor='#313131'">
|
|
<svg height="20" viewBox="0 0 16 16" width="20" style="fill: currentColor; opacity: 0.7;">
|
|
<path
|
|
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z">
|
|
</path>
|
|
</svg>
|
|
|
|
<span>Star</span>
|
|
|
|
<span id="gh-star-count" style="
|
|
background-color: rgba(27, 31, 35, 0.08);
|
|
padding: 2px 6px;
|
|
border-radius: 10px;
|
|
font-size: 12px;
|
|
display: none; /* Hidden until loaded */
|
|
"></span>
|
|
</a>
|
|
|
|
</div>
|
|
|
|
<script>
|
|
(async function () {
|
|
try {
|
|
const repo = 'JasonHoku/ComfyUI-Ultimate-Auto-Sampler-Config-Grid-Testing-Suite';
|
|
const response = await fetch(`https://api.github.com/repos/${repo}`);
|
|
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
const countSpan = document.getElementById('gh-star-count');
|
|
|
|
// Format number (e.g. 1500 -> 1.5k) if needed, or just show raw
|
|
countSpan.textContent = data.stargazers_count;
|
|
countSpan.style.display = 'inline-block'; // Show it now that we have data
|
|
}
|
|
} catch (e) {
|
|
console.log('Could not fetch GitHub stars');
|
|
}
|
|
})();
|
|
</script>
|
|
|
|
</body>
|
|
|
|
</html> |