(onChange)
| 155 | |
| 156 | // ─── Wan2GP Server Config ──────────────────────────────────────────────────── |
| 157 | function Wan2gpConfigBar(onChange) { |
| 158 | const wrap = document.createElement('div'); |
| 159 | wrap.className = 'flex flex-col gap-3 p-3 rounded-xl bg-white/3 border border-white/5'; |
| 160 | wrap.innerHTML = ` |
| 161 | <div class="flex flex-col gap-0.5"> |
| 162 | <span class="text-xs font-bold text-white">Wan2GP server (optional)</span> |
| 163 | <span class="text-[11px] text-muted leading-relaxed"> |
| 164 | Run <a href="https://github.com/deepbeepmeep/Wan2GP" target="_blank" class="text-primary hover:underline">Wan2GP</a> |
| 165 | on a CUDA box (<code class="text-primary/80">python wgp.py --listen --server-name 0.0.0.0</code>) to unlock video models from this UI. |
| 166 | </span> |
| 167 | </div> |
| 168 | <div class="flex items-center gap-2"> |
| 169 | <input id="wan2gp-url" type="text" placeholder="http://127.0.0.1:7860" |
| 170 | class="flex-1 bg-white/5 border border-white/5 focus:border-primary/40 rounded-lg px-3 py-1.5 text-xs text-white placeholder-white/30 focus:outline-none"/> |
| 171 | <button id="wan2gp-test" class="px-3 py-1.5 rounded-lg text-xs font-bold bg-primary/20 text-primary border border-primary/30 hover:bg-primary/30 transition-all">Test</button> |
| 172 | <button id="wan2gp-save" class="px-3 py-1.5 rounded-lg text-xs font-bold bg-primary text-black hover:shadow-glow transition-all">Save</button> |
| 173 | </div> |
| 174 | <div id="wan2gp-status" class="text-[11px] text-muted">${t('localModels.notConfigured')}</div> |
| 175 | `; |
| 176 | |
| 177 | const input = wrap.querySelector('#wan2gp-url'); |
| 178 | const testBtn = wrap.querySelector('#wan2gp-test'); |
| 179 | const saveBtn = wrap.querySelector('#wan2gp-save'); |
| 180 | const statusEl = wrap.querySelector('#wan2gp-status'); |
| 181 | const setStatus = (text, kind = 'muted') => { |
| 182 | const colorMap = { muted: 'text-muted', ok: 'text-green-400', warn: 'text-yellow-400', err: 'text-red-400' }; |
| 183 | statusEl.className = `text-[11px] ${colorMap[kind] || colorMap.muted}`; |
| 184 | statusEl.textContent = text; |
| 185 | }; |
| 186 | |
| 187 | (async () => { |
| 188 | const cfg = await localAI.getWan2gpConfig(); |
| 189 | if (cfg.url) { |
| 190 | input.value = cfg.url; |
| 191 | const r = await localAI.probeWan2gp(cfg.url); |
| 192 | setStatus(r.ok ? `Connected · Gradio ${r.version}` : `Saved URL not reachable: ${r.error}`, r.ok ? 'ok' : 'warn'); |
| 193 | } else { |
| 194 | setStatus(t('localModels.notConfiguredNote'), 'muted'); |
| 195 | } |
| 196 | })(); |
| 197 | |
| 198 | testBtn.onclick = async () => { |
| 199 | const url = input.value.trim(); |
| 200 | if (!url) { setStatus('Enter a URL first', 'warn'); return; } |
| 201 | setStatus(t('localModels.probing'), 'muted'); |
| 202 | testBtn.disabled = true; |
| 203 | try { |
| 204 | const r = await localAI.probeWan2gp(url); |
| 205 | setStatus(r.ok ? `Reachable · Gradio ${r.version}` : `Unreachable: ${r.error}`, r.ok ? 'ok' : 'err'); |
| 206 | } finally { testBtn.disabled = false; } |
| 207 | }; |
| 208 | |
| 209 | saveBtn.onclick = async () => { |
| 210 | const url = input.value.trim(); |
| 211 | saveBtn.disabled = true; |
| 212 | try { |
| 213 | await localAI.setWan2gpUrl(url); |
| 214 | const r = url ? await localAI.probeWan2gp(url) : { ok: false, error: 'cleared' }; |
no test coverage detected