Add a multi-window board so Generate stays live while refs feed vision.

Keep action buttons only on the latest patch, spin Apply+Generate while busy, and greet on first open.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-21 20:56:50 +03:00
co-authored by Cursor
parent 57b2023a50
commit bb8cb990aa
7 changed files with 959 additions and 234 deletions
+204 -5
View File
@@ -24,12 +24,46 @@
}
.sa-image-pane {
flex: 0 0 var(--sa-image-width, 34%);
max-width: 40%;
min-width: 11rem;
flex: 0 0 var(--sa-image-width, 42%);
max-width: 56%;
min-width: 14rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
gap: 0.45rem;
}
.sa-board-head {
display: flex;
align-items: baseline;
gap: 0.45rem;
min-height: 1.6rem;
}
.sa-board-title {
font-weight: 650;
letter-spacing: 0.04em;
text-transform: uppercase;
font-size: 0.78rem;
}
.sa-board-hint {
flex: 1;
font-size: 0.75rem;
opacity: 0.55;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.sa-board {
flex: 1;
display: grid;
grid-template-columns: 1fr 1fr;
grid-auto-rows: minmax(7.5rem, 1fr);
gap: 0.4rem;
min-height: 16rem;
outline: none;
}
.sa-splitter {
@@ -47,6 +81,123 @@
background: color-mix(in srgb, currentColor 35%, transparent);
}
.sa-slot {
position: relative;
display: flex;
align-items: center;
justify-content: center;
border: 1px dashed color-mix(in srgb, currentColor 28%, transparent);
border-radius: 0.5rem;
overflow: hidden;
background:
radial-gradient(ellipse at 30% 20%, color-mix(in srgb, currentColor 8%, transparent), transparent 55%),
color-mix(in srgb, currentColor 4%, transparent);
min-height: 7.5rem;
cursor: pointer;
outline: none;
transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.sa-slot.sa-has-image {
border-style: solid;
}
.sa-slot.sa-selected {
box-shadow: 0 0 0 2px color-mix(in srgb, currentColor 45%, transparent);
border-color: color-mix(in srgb, currentColor 55%, transparent);
}
.sa-slot.sa-dragover {
border-color: color-mix(in srgb, currentColor 70%, transparent);
box-shadow: inset 0 0 0 2px color-mix(in srgb, currentColor 25%, transparent);
}
.sa-slot.sa-slot-gen {
grid-column: 1 / -1;
min-height: 10rem;
}
.sa-board.sa-board-many .sa-slot.sa-slot-gen {
grid-column: auto;
min-height: 7.5rem;
}
.sa-slot img {
max-width: 100%;
max-height: 100%;
object-fit: contain;
display: block;
}
.sa-slot-bar {
position: absolute;
top: 0.3rem;
left: 0.3rem;
right: 0.3rem;
display: flex;
align-items: center;
gap: 0.25rem;
z-index: 2;
pointer-events: none;
}
.sa-slot-bar > * {
pointer-events: auto;
}
.sa-slot-chip {
padding: 0.12rem 0.4rem;
border-radius: 999px;
font-size: 0.68rem;
font-weight: 650;
letter-spacing: 0.04em;
text-transform: uppercase;
background: color-mix(in srgb, currentColor 18%, transparent);
border: 1px solid color-mix(in srgb, currentColor 28%, transparent);
}
.sa-slot-chip.sa-live {
background: color-mix(in srgb, #6ee7a8 28%, transparent);
}
.sa-slot-attach {
margin-left: auto;
display: flex;
align-items: center;
gap: 0.2rem;
font-size: 0.7rem;
opacity: 0.85;
background: color-mix(in srgb, currentColor 14%, transparent);
border-radius: 999px;
padding: 0.08rem 0.35rem;
}
.sa-slot-busy {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: color-mix(in srgb, #000 35%, transparent);
z-index: 3;
}
.sa-add-cell {
border: 1px dashed color-mix(in srgb, currentColor 22%, transparent);
border-radius: 0.5rem;
display: flex;
align-items: center;
justify-content: center;
opacity: 0.55;
cursor: pointer;
font-size: 0.85rem;
font-weight: 600;
}
.sa-add-cell:hover {
opacity: 0.9;
}
.sa-image-frame {
position: relative;
flex: 1;
@@ -427,6 +578,51 @@
margin-top: 0.45rem;
}
.sa-patch-stale {
margin-top: 0.4rem;
font-size: 0.78rem;
opacity: 0.5;
font-style: italic;
}
.sa-btn-gen {
display: inline-flex;
align-items: center;
gap: 0.4rem;
}
.sa-btn-gen:disabled {
opacity: 0.65;
pointer-events: none;
}
.sa-spinner-btn {
width: 0.7rem;
height: 0.7rem;
border-width: 2px;
}
.sa-msg.sa-welcome {
align-self: stretch;
max-width: 100%;
background: color-mix(in srgb, currentColor 6%, transparent);
border: 1px solid color-mix(in srgb, currentColor 16%, transparent);
}
.sa-welcome-title {
font-weight: 650;
margin-bottom: 0.35rem;
}
.sa-welcome ul {
margin: 0.2rem 0 0;
padding-left: 1.1rem;
}
.sa-welcome li {
margin: 0.15rem 0;
}
.sa-danger {
opacity: 0.85;
}
@@ -534,9 +730,12 @@
.sa-image-pane {
flex: 0 0 auto;
max-width: none;
max-height: 38%;
max-height: 46%;
width: 100% !important;
}
.sa-slot.sa-slot-gen {
grid-column: auto;
}
.sa-splitter {
display: none;
}
+707 -195
View File
File diff suppressed because it is too large Load Diff
+12 -3
View File
@@ -21,7 +21,8 @@ A JSON block named "Live SwarmUI context" is attached. Treat it as ground truth:
- Respect current width/height/steps/cfg/seed/sigma_shift/sampler unless the user asks to change them or the pack is `fix_params`.
- `wildcards` lists installed wildcard names (`__name__` syntax in prompts).
- `prompt_image_count` > 0 means Prompt Images are attached — warn if they may dominate.
- **Init / inpaint:** `has_init_image`, `has_mask_image`, `init_creativity` (aka denoise, 01), `mask_blur`, `mask_grow`. `has_vision_image` is the Assistent pane reference (can become Init/Mask).
- **Init / inpaint:** `has_init_image`, `has_mask_image`, `init_creativity` (aka denoise, 01), `mask_blur`, `mask_grow`.
- **Board (image windows):** `image_slots` lists windows. `generate` is the live current generation (view only unless attached). `ref1`… are user references. `attached_slot_ids` / `has_vision_image` = which windows are actually sent as vision this turn. To look at a window that was not attached, emit `look_at`.
## Output contract (mandatory)
@@ -47,6 +48,11 @@ A JSON block named "Live SwarmUI context" is attached. Treat it as ground truth:
"clear_mask_image": false,
"mask_blur": null,
"mask_grow": null,
"look_at": ["generate"],
"slot_to_init": null,
"slot_to_mask": null,
"snapshot_generate": false,
"select_slot": null,
"actions": ["generate"],
"search_query": null,
"notes": "one-line why"
@@ -63,8 +69,8 @@ A JSON block named "Live SwarmUI context" is attached. Treat it as ground truth:
### Init image / inpaint
- **img2img:** set `use_init_image: true` (uses Assistent vision / current Generate image) and `init_creativity` (0 = almost copy, 1 = almost new). Typical edits: **0.250.45**; restyle: **0.50.7**. Alias `denoise` is accepted.
- **Inpaint:** needs Init + Mask. Set `use_init_image: true` and `use_mask_image: true` only when the vision pane holds a proper mask (white = edit, black = keep). If the user has not painted a mask, tell them to use Swarm **Edit Image** / paint a mask, or press **As Mask** with a prepared mask — do not invent pixel masks.
- **img2img:** set `use_init_image: true` and optionally `slot_to_init: "generate"` or `"ref1"` (uses that board window) plus `init_creativity` (0 = almost copy, 1 = almost new). Typical edits: **0.250.45**; restyle: **0.50.7**. Alias `denoise` is accepted.
- **Inpaint:** needs Init + Mask. Set `use_init_image: true` and `use_mask_image: true` only when a board window holds a proper mask (white = edit, black = keep). Point to it with `slot_to_mask`. If the user has not painted a mask, tell them to use Swarm **Edit Image** / paint a mask, or press **As Mask** — do not invent pixel masks.
- `clear_init_image` / `clear_mask_image` to leave img2img mode.
- Prompt Images ≠ Init Image. Prefer Init for structural edits; Prompt Images for style refs (and warn they can dominate).
@@ -74,6 +80,9 @@ A JSON block named "Live SwarmUI context" is attached. Treat it as ground truth:
- `"use_init"` / `"use_mask"` — same as the boolean flags (optional).
- `"search_civitai"` — Civitai search; user must **Confirm** downloads.
- `"interrupt"` — stop generation.
- `look_at: ["generate", "ref1"]` — ask the UI to send those board windows as vision (a follow-up hop). Use when you need to see a slot that was not attached.
- `slot_to_init` / `slot_to_mask` — which board id to copy into Swarm Init / Mask.
- `snapshot_generate: true` — copy the live Generate window into a new/empty Ref.
- Pure Q&A with no prompt/param change: omit the JSON patch entirely (do not burn GPU).
### Auto-apply note
+3 -1
View File
@@ -1,6 +1,8 @@
# Mode: critique_image
Goal: look at the attached image (vision) and improve the next generation for **Krea 2**.
Goal: look at the attached / requested board image(s) and improve the next generation for **Krea 2**.
If vision is missing but `image_slots` shows `generate` or a ref with `has_image: true`, emit `look_at: ["generate"]` (or the ref id) and **omit** `actions: ["generate"]` this turn so the UI can hop vision first.
## How to critique
+13 -9
View File
@@ -1,21 +1,20 @@
# Swarm Assistent
SwarmUI extension for **collaborative Krea 2** prompting via **Ollama**: chat + vision, LoRA/trigger awareness, applyable patches, **img2img / inpaint**, auto Generate, and Civitai search with Confirm.
SwarmUI extension for **collaborative Krea 2** prompting via **Ollama**: chat + multi-window board (live Generate + refs), LoRA/trigger awareness, applyable patches, **img2img / inpaint**, auto Generate, and Civitai search with Confirm.
## Layout
- **Left:** vision reference (drop / paste / *Send to Assistent* from Generate)
- **Left — Board:** live **Generate** window + **Ref** windows (drop / paste / *Send to Assistent* / Snapshot gen). Per-window **vision** checkbox attaches that image to the next chat.
- **Splitter:** drag to resize panes
- **Right (wider):** chat
- **Right (wider):** chat — first visit shows a short how-to. Only the **latest** JSON proposal keeps action buttons; Apply + Generate spins/disables while a generation is running.
- **Top-right:** prompt pack + settings (Ollama URL, model, auto-apply / auto-generate)
## UX
- **Send to Assistent** under the current Generate image (and History) — loads vision + opens the Assistent tab
- Drag images from Generate/History or the OS onto the left pane
- Paste (`Ctrl+V`) while the Assistent tab is open
- **Use current** / **Clear** for the vision reference
- **As Init** / **As Mask** / **Clear Init** — wire the vision image into Swarm `Init Image` / `Mask Image` (img2img & inpaint)
- **Send to Assistent** under the current Generate image (and History) — adds a Ref window and opens the Assistent tab
- Drag / paste onto a Ref (drop on Generate snapshots into a new Ref)
- **Snapshot gen** copies the live Generate window into a Ref
- **As Init** / **As Mask** / **Clear Init** — selected window → Swarm `Init Image` / `Mask Image`
- Enter sends; Shift+Enter newline
- **Auto-apply** + **Auto-generate** (default on): patch from the LLM is applied and Generate runs when the patch changes prompt/params or includes `actions: ["generate"]`
- Pure Q&A without a patch does **not** start Generate
@@ -67,13 +66,18 @@ Live context (checkpoint, server inventory LoRAs + triggers, wildcards, current
"use_init_image": true,
"init_creativity": 0.45,
"use_mask_image": false,
"look_at": ["generate"],
"slot_to_init": "generate",
"snapshot_generate": false,
"actions": ["generate"],
"search_query": null
}
```
- `generate` — auto-generate after apply (when enabled)
- `use_init` / `use_mask` — set Assistent vision (or current Generate) as Init / Mask
- `look_at` — hop vision from named board windows (`generate`, `ref1`, …)
- `slot_to_init` / `slot_to_mask` — copy that window into Swarm Init / Mask
- `snapshot_generate` — copy live Generate into a Ref
- `search_civitai` + `search_query` — server searches Civitai, second LLM hop, Confirm cards in UI
- `interrupt` — stop current generation
+6 -3
View File
@@ -53,9 +53,9 @@ public class SwarmAssistentExtension : Extension
ScriptFiles.Add("Assets/assistent.js");
StyleSheetFiles.Add("Assets/assistent.css");
ExtensionAuthor = "mrleo1nid";
Description = "Collaborative Krea 2 assistant via Ollama: chat, vision, img2img/inpaint, Generate loop, Civitai Confirm.";
Description = "Collaborative Krea 2 assistant: Ollama chat, multi-window board, img2img/inpaint, Generate loop, Civitai Confirm.";
License = "MIT";
Version = "0.3.4";
Version = "0.4.0";
Tags = ["tabs", "ui", "llm", "ollama", "krea", "inpaint"];
}
@@ -449,7 +449,10 @@ public class SwarmAssistentExtension : Extension
|| obj["use_init_image"] != null || obj["clear_init_image"] != null
|| obj["init_creativity"] != null || obj["denoise"] != null
|| obj["use_mask_image"] != null || obj["clear_mask_image"] != null
|| obj["mask_blur"] != null || obj["mask_grow"] != null))
|| obj["mask_blur"] != null || obj["mask_grow"] != null
|| obj["look_at"] != null || obj["vision_from"] != null || obj["vision_slots"] != null
|| obj["slot_to_init"] != null || obj["slot_to_mask"] != null
|| obj["snapshot_generate"] != null || obj["select_slot"] != null))
{
return obj;
}
+11 -15
View File
@@ -4,22 +4,18 @@
</div>
<div class="sa-layout" id="sa_layout">
<aside class="sa-image-pane" id="sa_image_pane">
<div class="sa-image-frame" id="sa_image_frame" tabindex="0" title="Drop an image here, or paste (Ctrl+V). Drag from Generate / History.">
<img id="sa_image_preview" alt="Vision reference" hidden />
<div class="sa-image-empty" id="sa_image_empty">
<div class="sa-empty-title">Vision reference</div>
<div class="sa-empty-hint">Drop · paste · <em>Send to Assistent</em> from Generate</div>
</div>
<div class="sa-drop-overlay" id="sa_drop_overlay" hidden>Drop image</div>
<span class="sa-vision-chip" id="sa_vision_chip" hidden>vision</span>
<div class="sa-board-head">
<span class="sa-board-title">Board</span>
<span class="sa-board-hint">Generate live · refs for vision</span>
<button type="button" class="basic-button sa-icon-btn" id="sa_btn_add_ref" title="Add a reference window">+ Ref</button>
</div>
<div class="sa-board" id="sa_board" tabindex="0" title="Drop images onto a window, or paste (Ctrl+V). Click a window to select it."></div>
<div class="sa-image-actions">
<button type="button" class="basic-button" id="sa_btn_use_current" title="Grab the current Generate preview">Use current</button>
<button type="button" class="basic-button" id="sa_btn_as_init" title="Set vision image as Swarm Init Image (img2img)">As Init</button>
<button type="button" class="basic-button" id="sa_btn_as_mask" title="Set vision image as Mask (inpaint — white=edit)">As Mask</button>
<button type="button" class="basic-button" id="sa_btn_use_current" title="Copy current Generate into a reference window">Snapshot gen</button>
<button type="button" class="basic-button" id="sa_btn_as_init" title="Set selected window as Swarm Init Image (img2img)">As Init</button>
<button type="button" class="basic-button" id="sa_btn_as_mask" title="Set selected window as Mask (inpaint — white=edit)">As Mask</button>
<button type="button" class="basic-button" id="sa_btn_clear_init" title="Clear Init Image + Mask on Generate tab">Clear Init</button>
<button type="button" class="basic-button" id="sa_btn_clear_image" title="Clear vision image">Clear</button>
<label class="sa-check"><input type="checkbox" id="sa_attach_vision" checked /> Attach next</label>
<button type="button" class="basic-button" id="sa_btn_clear_image" title="Clear the selected reference window">Clear slot</button>
</div>
</aside>
<div class="sa-splitter" id="sa_splitter" role="separator" aria-orientation="vertical" title="Drag to resize"></div>
@@ -47,7 +43,7 @@
<label>Ollama URL <input type="text" id="sa_base_url" value="http://127.0.0.1:11434" /></label>
<button type="button" class="basic-button" id="sa_btn_refresh_models">Refresh models</button>
<button type="button" class="basic-button" id="sa_btn_refresh_inventory">Refresh inventory</button>
<label class="sa-check"><input type="checkbox" id="sa_auto_vision" /> Auto-attach current image</label>
<label class="sa-check"><input type="checkbox" id="sa_auto_vision" /> Auto-attach Generate window to chat</label>
<label class="sa-check"><input type="checkbox" id="sa_auto_apply" checked /> Auto-apply patch</label>
<label class="sa-check"><input type="checkbox" id="sa_auto_generate" checked /> Auto-generate after patch</label>
<label class="sa-check"><input type="checkbox" id="sa_auto_critique" /> Auto-critique after generate</label>
@@ -56,7 +52,7 @@
<div class="sa-messages" id="sa_messages">
<div class="sa-chat-empty" id="sa_chat_empty">
<div class="sa-chat-empty-title">Collaborative Krea 2</div>
<div class="sa-chat-empty-hint">Write a prompt, drop a reference on the left, or send the current Generate image.</div>
<div class="sa-chat-empty-hint">Write a prompt, drop refs on the board, or send Generate via <em>Send to Assistent</em>.</div>
</div>
</div>
<div class="sa-livebar" id="sa_livebar" hidden>