Files
SparkyUI/model-manager/app/static/index.html
T
TBNilles 399acabd58 feat(model-manager): "Free GPU memory" button to unload ComfyUI models
ComfyUI caches the last model when RAM is plentiful (unified memory), so
memory doesn't drop after switching models even though models are being
swapped, not accumulated. Add a sidebar "Free GPU memory" button that
proxies ComfyUI's POST /free (unload_models + free_memory) via a new
/api/comfyui/free endpoint (COMFYUI_URL env). Verified it releases ~7GB.
README documents this plus the --disable-smart-memory auto-unload option.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 17:14:37 -04:00

226 lines
8.3 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>SparkyUI · Model Manager</title>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<div class="app">
<aside class="sidebar">
<div class="brand">
<span class="spark"></span>
<div>
<div class="brand-title">SparkyUI</div>
<div class="brand-sub">Model Manager</div>
</div>
</div>
<nav>
<button class="nav-item active" data-view="installed">
<span class="nav-ico"></span> Installed Models
</button>
<button class="nav-item" data-view="gallery">
<span class="nav-ico">🖼</span> Gallery
</button>
<button class="nav-item" data-view="browse">
<span class="nav-ico">🔍</span> Browse CivitAI
</button>
<button class="nav-item" data-view="download">
<span class="nav-ico"></span> Add / Download
</button>
<button class="nav-item" data-view="settings">
<span class="nav-ico"></span> Settings
</button>
</nav>
<div class="sidebar-foot">
<button class="btn" id="freeMemBtn" title="Unload all models from ComfyUI and free GPU/RAM">
⏏ Free GPU memory
</button>
<span class="form-msg" id="freeMemMsg"></span>
</div>
</aside>
<main class="content">
<!-- Installed Models -->
<section class="view active" id="view-installed">
<header class="view-head">
<h1>Installed Models</h1>
<div class="head-actions">
<input type="search" id="modelSearch" placeholder="Filter…" />
<button class="btn" id="refreshModels">Refresh</button>
</div>
</header>
<div id="modelsContainer" class="models-container">
<p class="empty">Loading…</p>
</div>
</section>
<!-- Gallery (generated photos) -->
<section class="view" id="view-gallery">
<header class="view-head">
<h1>Gallery</h1>
<div class="head-actions">
<span class="form-msg" id="galleryToast"></span>
<button class="btn" id="refreshGallery">Refresh</button>
<button class="btn danger" id="deleteAllGallery">Delete all</button>
</div>
</header>
<div id="galleryGrid" class="gallery-grid">
<p class="empty">Loading…</p>
</div>
<div class="browse-more">
<button class="btn" id="galleryMore" style="display:none">Load more</button>
</div>
</section>
<!-- Browse CivitAI -->
<section class="view" id="view-browse">
<header class="view-head">
<h1>Browse CivitAI</h1>
<div class="head-actions">
<span class="form-msg" id="browseToast"></span>
</div>
</header>
<div class="browse-controls">
<input type="search" id="bSearch" placeholder="Search models…" />
<select id="bType">
<option value="">All types</option>
<option value="Checkpoint">Checkpoint</option>
<option value="LORA">LoRA</option>
<option value="LoCon">LyCORIS</option>
<option value="TextualInversion">Embedding</option>
<option value="Controlnet">ControlNet</option>
<option value="VAE">VAE</option>
<option value="Upscaler">Upscaler</option>
<option value="Hypernetwork">Hypernetwork</option>
<option value="Poses">Poses</option>
</select>
<select id="bSort">
<option>Most Downloaded</option>
<option>Highest Rated</option>
<option>Newest</option>
</select>
<select id="bPeriod">
<option>AllTime</option>
<option>Year</option>
<option>Month</option>
<option>Week</option>
<option>Day</option>
</select>
<div class="multi-dd" id="bBaseDD">
<button type="button" class="btn multi-btn" id="bBaseBtn">Base model ▾</button>
<div class="multi-panel" id="bBasePanel" hidden>
<div class="multi-panel-actions">
<button type="button" class="link-btn" id="bBaseClear">Clear</button>
</div>
<!-- checkboxes injected by app.js -->
</div>
</div>
<label class="check"><input type="checkbox" id="bNsfw" /> NSFW</label>
<button class="btn primary" id="bGo">Search</button>
</div>
<div id="browseGrid" class="browse-grid">
<p class="empty">Search the CivitAI catalog, then click <b>Download</b> on a model.</p>
</div>
<div class="browse-more">
<button class="btn" id="bMore" style="display:none">Load more</button>
</div>
</section>
<!-- Add / Download -->
<section class="view" id="view-download">
<header class="view-head"><h1>Add / Download Model</h1></header>
<div class="card form-card">
<label>Download URL</label>
<input type="text" id="dlUrl"
placeholder="Direct URL, CivitAI model link, or HuggingFace resolve URL" />
<div class="source-hint" id="sourceHint"></div>
<div class="row">
<div class="col">
<label>Model type</label>
<select id="dlType"></select>
<div class="hint">CivitAI links auto-detect type; leave as-is to use it.</div>
</div>
<div class="col">
<label>Filename (optional)</label>
<input type="text" id="dlFilename" placeholder="Auto-detected if blank" />
</div>
</div>
<div class="form-actions">
<button class="btn primary" id="startDownload">Start Download</button>
<span class="form-msg" id="dlMsg"></span>
</div>
</div>
<h2 class="subhead">Downloads</h2>
<div id="downloadsContainer" class="downloads-container">
<p class="empty">No downloads yet.</p>
</div>
</section>
<!-- Settings -->
<section class="view" id="view-settings">
<header class="view-head"><h1>Settings</h1></header>
<div class="card form-card">
<h2 class="subhead first">API Keys</h2>
<p class="hint">
Stored persistently and sent as auth headers when downloading from the
matching site. Leave blank to keep the existing value.
</p>
<label>CivitAI API Key <span class="badge" id="civitaiBadge"></span></label>
<input type="password" id="civitaiKey" placeholder="••••••••" autocomplete="off" />
<label>HuggingFace Token <span class="badge" id="hfBadge"></span></label>
<input type="password" id="hfToken" placeholder="••••••••" autocomplete="off" />
<div class="form-actions">
<button class="btn primary" id="saveSettings">Save</button>
<span class="form-msg" id="settingsMsg"></span>
</div>
</div>
<div class="card form-card">
<h2 class="subhead first">Model Storage</h2>
<p class="hint">
Models are saved into the project <code>models/</code> directory, sorted into
ComfyUI's standard sub-folders by type. ComfyUI reads from the same folder.
</p>
</div>
</section>
</main>
</div>
<!-- Lightbox for full-size photo viewing -->
<div id="lightbox" class="lightbox" hidden>
<button class="lb-close" id="lbClose" title="Close"></button>
<img id="lbImage" alt="" />
<div class="lb-bar">
<span id="lbName" class="lb-name"></span>
<span class="lb-actions">
<a id="lbOpen" class="btn" target="_blank" rel="noopener">Open original</a>
<button class="btn danger" id="lbDelete">Delete</button>
</span>
</div>
</div>
<!-- In-app confirmation dialog (replaces native window.confirm) -->
<div id="confirmModal" class="modal-overlay" hidden>
<div class="modal">
<p id="confirmMsg"></p>
<div class="modal-actions">
<button class="btn" id="confirmCancel">Cancel</button>
<button class="btn danger-solid" id="confirmOk">Delete</button>
</div>
</div>
</div>
<script src="/app.js"></script>
</body>
</html>