const $ = (id) => document.getElementById(id); function fmt(obj) { return typeof obj === 'string' ? obj : JSON.stringify(obj, null, 2); } async function api(path, opts = {}) { const res = await fetch(path, { headers: { 'Content-Type': 'application/json', ...(opts.headers || {}) }, ...opts, }); const text = await res.text(); let data; try { data = JSON.parse(text); } catch { data = text; } if (!res.ok) { const detail = data?.detail || text || res.statusText; throw new Error(`${res.status}: ${detail}`); } return data; } function initTabs() { const tabs = document.querySelectorAll('#debugTabs button'); tabs.forEach((btn) => { btn.addEventListener('click', () => { tabs.forEach((t) => t.classList.remove('active')); btn.classList.add('active'); document.querySelectorAll('.debug-panel').forEach((p) => p.classList.remove('active')); $(`panel-${btn.dataset.tab}`).classList.add('active'); }); }); } async function loadConfig() { const c = await api('/debug/config'); $('configOut').textContent = fmt(c); $('llmModel').placeholder = c.sd_prompt_model || c.system_model; return c; } async function loadPersonas() { const list = await api('/debug/personas'); const sel = $('sdPersona'); sel.innerHTML = ''; for (const p of list) { const opt = document.createElement('option'); opt.value = p.persona_id; opt.textContent = `${p.name} (${p.persona_id})`; sel.appendChild(opt); } } async function runSdPrompt() { $('sdScene').textContent = '…'; $('sdPrompts').textContent = '…'; const body = { persona_id: $('sdPersona').value, chat_excerpt: $('sdChat').value, outfit_json: $('sdOutfit').value || '[]', use_prose: $('sdUseProse') ? $('sdUseProse').checked : false, }; const app = $('sdAppearance').value.trim(); if (app) body.appearance_override = app; const data = await api('/debug/sd-prompt', { method: 'POST', body: JSON.stringify(body) }); $('sdScene').textContent = data.scene ? fmt(data.scene) : (data.error || '—'); const prompts = []; if (data.tags_only_full) prompts.push('=== TAGS + POV (no prose) ===\n' + data.tags_only_full); if (data.hybrid_full) prompts.push('\n=== HYBRID (Comfy) ===\n' + data.hybrid_full); if (!data.tags_only_full && data.tag_full) prompts.push('=== PROMPT ===\n' + data.tag_full); $('sdPrompts').textContent = prompts.join('\n') || data.error || '—'; $('sdLlmRaw').textContent = [ `model: ${data.sd_prompt_model}`, `dual: ${data.anima_dual}`, '', '--- system ---', data.builder_system || '', '', '--- user ---', data.builder_user || '', '', '--- raw ---', data.llm_raw || data.error || '', ].join('\n'); if (data.tag_full || data.hybrid_full) { const src = data.hybrid_full || data.tag_full; const parts = src.includes('__NEGATIVE_PROMPT__') ? src.split('\n\n__NEGATIVE_PROMPT__\n\n') : src.includes('\n\nNegative prompt:') ? src.split('\n\nNegative prompt:') : [src, '']; $('genPositive').value = parts[0] || ''; $('genNegative').value = parts[1] || ''; } } async function runLlm() { $('llmOut').textContent = '…'; const data = await api('/debug/llm', { method: 'POST', body: JSON.stringify({ model: $('llmModel').value.trim(), system: $('llmSystem').value, user: $('llmUser').value, }), }); $('llmOut').textContent = `model: ${data.model}\n\n${data.response}`; } function fillModelSelect(sel, options, configured) { const current = sel.querySelector('option')?.value ?? ''; sel.innerHTML = ``; for (const name of options || []) { const opt = document.createElement('option'); opt.value = name; opt.textContent = name; if (name === configured) opt.selected = true; sel.appendChild(opt); } } async function loadComfyModels() { $('comfyModelLists').textContent = 'Загрузка object_info…'; const data = await api('/debug/comfy/models'); const { models, configured } = data; fillModelSelect($('genUnet'), models.unets, configured.unet); fillModelSelect($('genClip'), models.clips, configured.clip); fillModelSelect($('genVae'), models.vaes, configured.vae); fillModelSelect($('genCkpt'), models.checkpoints, configured.checkpoint); const wrap = $('comfyModelLists'); wrap.innerHTML = ''; for (const [key, list] of Object.entries(models)) { const block = document.createElement('details'); block.className = 'model-list-block'; block.open = key === 'unets' || key === 'checkpoints'; block.innerHTML = `${key} (${list.length})`; const ul = document.createElement('ul'); for (const item of list) { const li = document.createElement('li'); li.textContent = item; ul.appendChild(li); } block.appendChild(ul); wrap.appendChild(block); } } async function comfyPing() { $('comfyPingOut').textContent = '…'; const data = await api('/debug/comfy/ping'); $('comfyPingOut').textContent = fmt(data); } async function comfyGenerate() { $('comfyGenOut').textContent = 'Генерация…'; $('comfyImgWrap').classList.add('hidden'); const body = { positive: $('genPositive').value, negative: $('genNegative').value, }; const u = $('genUnet').value; const c = $('genClip').value; const v = $('genVae').value; const ck = $('genCkpt').value; if (u) body.unet = u; if (c) body.clip = c; if (v) body.vae = v; if (ck) body.checkpoint = ck; const data = await api('/debug/comfy/generate', { method: 'POST', body: JSON.stringify(body), }); $('comfyGenOut').textContent = fmt(data); if (data.image_path) { $('comfyImg').src = data.image_path + '?t=' + Date.now(); $('comfyImgWrap').classList.remove('hidden'); } } async function comfyRaw() { $('comfyRawOut').textContent = '…'; const data = await api('/debug/comfy/raw', { method: 'POST', body: JSON.stringify({ method: $('rawMethod').value, path: $('rawPath').value, params_json: $('rawParams').value || '{}', body_json: $('rawBody').value || '', }), }); $('comfyRawOut').textContent = fmt(data); } function bind() { initTabs(); $('btnReloadConfig').addEventListener('click', loadConfig); $('btnSdPrompt').addEventListener('click', () => runSdPrompt().catch(showErr)); $('btnLlm').addEventListener('click', () => runLlm().catch(showErr)); $('btnComfyPing').addEventListener('click', () => comfyPing().catch(showErr)); $('btnComfyModels').addEventListener('click', () => loadComfyModels().catch(showErr)); $('btnComfyGen').addEventListener('click', () => comfyGenerate().catch(showErr)); $('btnComfyRaw').addEventListener('click', () => comfyRaw().catch(showErr)); } function showErr(e) { alert(e.message || String(e)); } bind(); loadConfig().catch(showErr); loadPersonas().catch(showErr);