diff --git a/Assets/assistent.js b/Assets/assistent.js index 8612e43..89ef375 100644 --- a/Assets/assistent.js +++ b/Assets/assistent.js @@ -1,6 +1,6 @@ /** * Swarm Assistent — Krea 2 collaborative chat (Ollama via Swarm API). - * v0.7.2: Slim Exact in prompt; no Exact blob in live ctx; tighter preview filter; critique requires vision+JSON. + * v0.7.3: Silent apply+gen when user asks to generate; composer «Собрать + Gen» button. */ (function () { const LS_BASE = 'swarm_assistent_base_url'; @@ -102,6 +102,8 @@ exact: null, sessionExact: {}, lastUserParamIntent: false, + lastPatch: null, + pendingSilentGen: false, enabledSkills: [], kreaProfiles: { turbo: { steps: 8, cfg: 1, sigma_shift: 1.15 }, raw: { steps: 28, cfg: 4.5 } }, preferredEmbed: null, @@ -445,6 +447,28 @@ return /\b(steps?|cfg|seed|sigma|размер|aspect|ширин|высот|resolution|batch|турбо|turbo|raw)\b/i.test(String(text || '')); } + function userAsksGenerate(text) { + const t = String(text || ''); + if (!t.trim()) { + return false; + } + if (/^(gen|generate|go|рисуй|давай|ещё|еще)\s*[!.…]*$/i.test(t.trim())) { + return true; + } + return /\b(сгенерируй|сгенерировать|генерируй|generate|нарисуй|перегенерируй|перерисуй|сделай\s+(картинк|изображен|фото)|run\s+generat|\/gen)\b/i.test(t); + } + + function rememberLastPatch(patch) { + if (patch && typeof patch === 'object' && !isCardObject(patch)) { + state.lastPatch = patch; + const btn = $('sa_btn_build_gen'); + if (btn) { + btn.disabled = false; + btn.title = 'Применить последний патч Assistent и Generate'; + } + } + } + function applyAspectTableFrom(obj) { if (!obj || typeof obj !== 'object') { return false; @@ -918,13 +942,21 @@ }); } - function mountPatchActions(parent, patch) { + function mountPatchActions(parent, patch, { silent = false } = {}) { if (!parent || !patch) { return; } + rememberLastPatch(patch); retireStalePatchActions(); const wrap = parent.classList.contains('sa-patch') ? parent : null; const host = wrap || parent; + if (silent) { + const note = document.createElement('div'); + note.className = 'sa-patch-actions sa-patch-silent sa-patch-current'; + note.textContent = 'Применено автоматически · Generate…'; + host.appendChild(note); + return; + } const actions = document.createElement('div'); actions.className = 'sa-patch-actions sa-patch-current'; for (const [label, which] of [ @@ -956,6 +988,23 @@ syncPatchActionAvailability(); } + async function buildCurrentAndGenerate() { + if (state.busy || state.generating || isGenerateUnavailable()) { + setStatus('Занято — подожди или нажми Стоп'); + return; + } + const patch = state.lastPatch; + if (patch) { + setStatus('Собираю патч → Generate…'); + await applyPatch(patch, 'all'); + await runGenerateFromPatch({ ...patch, actions: ['generate'] }, { force: true }); + return; + } + // No Assistent patch yet — generate with whatever is already in SwarmUI. + setStatus('Generate с текущим промптом…'); + await runGenerateFromPatch({ actions: ['generate'] }, { force: true }); + } + function renderBoard() { const board = $('sa_board'); if (!board) { @@ -2606,12 +2655,14 @@ div.textContent = prose || text || ''; } if (finalPatch) { + rememberLastPatch(finalPatch); const wrap = document.createElement('div'); wrap.className = 'sa-patch'; const pre = document.createElement('pre'); pre.textContent = JSON.stringify(finalPatch, null, 2); wrap.appendChild(pre); - mountPatchActions(wrap, finalPatch); + const silent = !!(meta && meta.silentPatch); + mountPatchActions(wrap, finalPatch, { silent }); div.appendChild(wrap); } if (civitaiResults && civitaiResults.length) { @@ -2684,12 +2735,14 @@ setAssistantBody(el, prose || fullReply || ''); el.querySelectorAll('.sa-patch, .sa-civitai-list').forEach((n) => n.remove()); if (patch && !isCardObject(patch) && !(card && !patch.prompt && !patch.actions && !patch.loras)) { + rememberLastPatch(patch); const wrap = document.createElement('div'); wrap.className = 'sa-patch'; const pre = document.createElement('pre'); pre.textContent = JSON.stringify(patch, null, 2); wrap.appendChild(pre); - mountPatchActions(wrap, patch); + const silent = !!(meta && meta.silentPatch) || !!state.pendingSilentGen; + mountPatchActions(wrap, patch, { silent }); el.appendChild(wrap); } else if (card) { const wrap = document.createElement('div'); @@ -4212,6 +4265,9 @@ return; } const { patch } = extractPatch(reply); + if (patch) { + rememberLastPatch(patch); + } if (Array.isArray(patch?.actions) && patch.actions.map(String).includes('interrupt')) { doInterruptNow(); } @@ -4228,16 +4284,23 @@ return; } } - if (patch && $('sa_auto_apply')?.checked) { + const wantsGen = !!(opts.userWantsGenerate || state.pendingSilentGen + || (Array.isArray(patch?.actions) && patch.actions.map(String).includes('generate'))); + const doApply = !!(patch && (wantsGen || $('sa_auto_apply')?.checked)); + if (doApply) { await applyPatch(patch, 'all'); updateTasteFromPatch(patch, opts.userText || ''); - if (!fromAutoCritique) { - const src = await runGenerateFromPatch(patch); + if (!fromAutoCritique && (wantsGen || $('sa_auto_generate')?.checked)) { + const src = await runGenerateFromPatch( + { ...patch, actions: Array.isArray(patch.actions) ? patch.actions : ['generate'] }, + { force: wantsGen }, + ); if (src) { await maybeAutoCritique(src); } } } + state.pendingSilentGen = false; } async function applyQuickPatch(patch, note) { @@ -4502,6 +4565,7 @@ } if (!opts.fromAutoCritique && !opts.fromDownload && !opts.fromVisionHop) { state.lastUserParamIntent = userTextMentionsParams(text); + state.pendingSilentGen = userAsksGenerate(text); } if (!opts.fromAutoCritique && !opts.fromDownload && !opts.fromVisionHop && !opts.skipSlash) { @@ -4599,6 +4663,7 @@ const msgMeta = { persona: currentPersonaInfo(), pack, + silentPatch: !!state.pendingSilentGen, }; state.history.push({ role: 'user', content: text }); if (state.pendingPersonaNote) { @@ -4671,6 +4736,7 @@ await handleReplySideEffects(reply, civitaiResults, { ...opts, userText: text, + userWantsGenerate: !!state.pendingSilentGen || userAsksGenerate(text), attachedSlotIds: visionSlots.map((s) => s.id), }); }; @@ -5020,6 +5086,7 @@ $('sa_btn_clear_init')?.addEventListener('click', clearInitAndMask); $('sa_btn_clear_image')?.addEventListener('click', () => clearSlot(state.selectedSlotId)); $('sa_btn_send')?.addEventListener('click', () => sendChat()); + $('sa_btn_build_gen')?.addEventListener('click', () => buildCurrentAndGenerate()); $('sa_btn_interrupt')?.addEventListener('click', () => { doInterruptNow(); state.busy = false; diff --git a/Config/_base/core/core.md b/Config/_base/core/core.md index 47decca..a6cdc4b 100644 --- a/Config/_base/core/core.md +++ b/Config/_base/core/core.md @@ -95,7 +95,7 @@ A JSON block named "Live SwarmUI context" is attached. Treat it as ground truth ### Actions (auto-safe) -- `"generate"` — after Apply, start generation. +- `"generate"` — after Apply, start generation. When the user explicitly asks to generate, always include this; the UI applies silently (no Apply-button strip). - `"search_civitai"` — Civitai search; user Confirms downloads. - `"interrupt"` — stop generation. - `"memory_upsert"` / `"memory_forget"` — write or delete facts in vector memory. diff --git a/Config/_base/packs/write_prompt.md b/Config/_base/packs/write_prompt.md index 6f5361d..a059597 100644 --- a/Config/_base/packs/write_prompt.md +++ b/Config/_base/packs/write_prompt.md @@ -29,5 +29,5 @@ Good: `A fluffy red fox sitting alert in fresh powder snow, ears forward, breath - Explain briefly what you changed. - JSON patch with at least `prompt`, and `loras` when relevant. -- `actions: ["generate"]` when the user wants a new image. +- `actions: ["generate"]` when the user wants a new image — the UI will apply the patch and Generate **without** showing Apply buttons. - `aspect` (or width/height) only if framing should change. diff --git a/README.md b/README.md index 9e4decb..bcee8a0 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ SwarmUI extension for **collaborative Krea 2** prompting via **Ollama**: chat + board (Generate | Refs tabs), LoRA chips, **persona presets** (`Config/personas/`), **vector memory**, model cards with Civitai fetch, img2img/inpaint, slash commands, auto Generate. -**Version 0.7.2** — Exact KV (slim in prompt); block `ViewSpecial` model-card previews; critique requires vision + real JSON fence. +**Version 0.7.3** — Exact KV; silent apply+Generate when user asks; composer **Собрать + Gen**; block model-card previews; critique needs vision + JSON. ## Layout diff --git a/SwarmAssistentExtension.cs b/SwarmAssistentExtension.cs index e38c177..f066614 100644 --- a/SwarmAssistentExtension.cs +++ b/SwarmAssistentExtension.cs @@ -49,7 +49,7 @@ public class SwarmAssistentExtension : Extension ExtensionAuthor = "mrleo1nid"; Description = "Collaborative Krea 2 assistant: Ollama chat, persona presets, vector memory, model cards, Generate loop."; License = "MIT"; - Version = "0.7.2"; + Version = "0.7.3"; Tags = ["tabs", "ui", "llm", "ollama", "krea", "inpaint", "memory"]; } diff --git a/Tabs/Text2Image/Assistent.html b/Tabs/Text2Image/Assistent.html index c3243ad..6248d47 100644 --- a/Tabs/Text2Image/Assistent.html +++ b/Tabs/Text2Image/Assistent.html @@ -100,6 +100,7 @@
+