Ship Assistent 0.7.6: multi-chat history, UI polish, and race fixes.

Persist chats with Generate param snapshots, always open a new chat on start, and abort in-flight work on clear/switch so replies cannot land in the wrong chat.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-22 00:06:11 +03:00
co-authored by Cursor
parent 25c98b2d1d
commit 6d8aaefc38
6 changed files with 1260 additions and 140 deletions
+290 -7
View File
@@ -335,6 +335,83 @@
align-items: center; align-items: center;
} }
.sa-live-params {
font-size: 0.75rem;
opacity: 0.78;
padding: 0.2rem 0.15rem 0.35rem;
letter-spacing: 0.01em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-variant-numeric: tabular-nums;
}
.sa-more-wrap {
position: relative;
display: inline-flex;
align-items: center;
gap: 0.15rem;
}
.sa-more-menu {
position: absolute;
z-index: 40;
bottom: calc(100% + 0.25rem);
left: 0;
min-width: 11rem;
display: flex;
flex-direction: column;
gap: 0.15rem;
padding: 0.35rem;
border-radius: 0.45rem;
border: 1px solid color-mix(in srgb, currentColor 28%, transparent);
background: color-mix(in srgb, #1a1a1a 92%, currentColor);
box-shadow: 0 8px 24px color-mix(in srgb, #000 45%, transparent);
}
#sa_clear_more_wrap .sa-more-menu {
left: auto;
right: 0;
bottom: auto;
top: calc(100% + 0.25rem);
}
.sa-more-item {
appearance: none;
border: 0;
background: transparent;
color: inherit;
text-align: left;
padding: 0.4rem 0.55rem;
border-radius: 0.3rem;
cursor: pointer;
font-size: 0.85rem;
}
.sa-more-item:hover {
background: color-mix(in srgb, currentColor 12%, transparent);
}
.sa-mode-badge {
display: inline-flex;
align-items: center;
padding: 0.12rem 0.45rem;
border-radius: 999px;
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.03em;
text-transform: lowercase;
border: 1px solid color-mix(in srgb, currentColor 30%, transparent);
background: color-mix(in srgb, currentColor 8%, transparent);
opacity: 0.92;
flex: 0 0 auto;
}
.sa-mode-badge.sa-mode-hot {
border-color: color-mix(in srgb, #f5a623 55%, currentColor);
background: color-mix(in srgb, #f5a623 18%, transparent);
}
.sa-chat-pane { .sa-chat-pane {
flex: 1 1 auto; flex: 1 1 auto;
display: flex; display: flex;
@@ -348,6 +425,7 @@
} }
.sa-chat-header { .sa-chat-header {
position: relative;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
@@ -355,6 +433,7 @@
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
border-bottom: 1px solid color-mix(in srgb, currentColor 18%, transparent); border-bottom: 1px solid color-mix(in srgb, currentColor 18%, transparent);
background: color-mix(in srgb, currentColor 5%, transparent); background: color-mix(in srgb, currentColor 5%, transparent);
flex-wrap: wrap;
} }
.sa-chat-title { .sa-chat-title {
@@ -363,6 +442,144 @@
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.45rem; gap: 0.45rem;
flex-wrap: wrap;
}
.sa-sessions-bar {
display: inline-flex;
align-items: center;
gap: 0.3rem;
margin-left: 0.15rem;
}
.sa-sessions-toggle {
font-size: 0.78rem !important;
padding: 0.18rem 0.5rem !important;
}
.sa-session-label {
font-size: 0.78rem;
font-weight: 500;
opacity: 0.75;
max-width: 10rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: pointer;
border-radius: 0.3rem;
padding: 0.1rem 0.35rem;
}
.sa-session-label:hover {
opacity: 1;
background: color-mix(in srgb, currentColor 10%, transparent);
}
.sa-chats-panel {
position: absolute;
z-index: 50;
top: calc(100% - 1px);
left: 0.5rem;
right: 0.5rem;
max-width: 26rem;
max-height: min(50vh, 22rem);
display: flex;
flex-direction: column;
gap: 0.35rem;
padding: 0.55rem;
border-radius: 0 0 0.5rem 0.5rem;
border: 1px solid color-mix(in srgb, currentColor 28%, transparent);
background: color-mix(in srgb, #161616 94%, currentColor);
box-shadow: 0 12px 28px color-mix(in srgb, #000 40%, transparent);
}
.sa-chats-panel-head {
font-size: 0.78rem;
font-weight: 650;
opacity: 0.85;
letter-spacing: 0.03em;
}
.sa-chats-panel-hint {
font-size: 0.72rem;
opacity: 0.65;
line-height: 1.35;
}
.sa-chats-list {
overflow: auto;
display: flex;
flex-direction: column;
gap: 0.25rem;
min-height: 0;
}
.sa-chats-empty {
font-size: 0.82rem;
opacity: 0.65;
padding: 0.5rem 0.25rem;
}
.sa-chat-row {
display: flex;
align-items: stretch;
gap: 0.2rem;
border-radius: 0.4rem;
border: 1px solid transparent;
background: color-mix(in srgb, currentColor 5%, transparent);
}
.sa-chat-row-active {
border-color: color-mix(in srgb, #6cf 45%, currentColor);
background: color-mix(in srgb, #6cf 12%, transparent);
}
.sa-chat-row-main {
appearance: none;
border: 0;
background: transparent;
color: inherit;
text-align: left;
flex: 1;
min-width: 0;
padding: 0.4rem 0.5rem;
cursor: pointer;
display: flex;
flex-direction: column;
gap: 0.12rem;
}
.sa-chat-row-title {
font-size: 0.85rem;
font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.sa-chat-row-meta {
font-size: 0.72rem;
opacity: 0.7;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.sa-chat-row-del {
appearance: none;
border: 0;
background: transparent;
color: inherit;
opacity: 0.55;
cursor: pointer;
padding: 0 0.55rem;
font-size: 1.1rem;
line-height: 1;
}
.sa-chat-row-del:hover {
opacity: 1;
color: #f66;
} }
.sa-live-dot { .sa-live-dot {
@@ -818,9 +1035,18 @@
.sa-chips { .sa-chips {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: nowrap;
gap: 0.35rem; gap: 0.35rem;
align-items: center; align-items: center;
overflow-x: auto;
overflow-y: hidden;
max-width: 100%;
padding-bottom: 0.15rem;
scrollbar-width: thin;
}
.sa-chips::-webkit-scrollbar {
height: 4px;
} }
.sa-chip { .sa-chip {
@@ -835,6 +1061,7 @@
letter-spacing: 0.02em; letter-spacing: 0.02em;
cursor: pointer; cursor: pointer;
line-height: 1.3; line-height: 1.3;
flex: 0 0 auto;
} }
.sa-chip:hover { .sa-chip:hover {
@@ -1080,14 +1307,14 @@
.sa-card-row { .sa-card-row {
display: flex; display: flex;
gap: 0.45rem; gap: 0.55rem;
align-items: flex-start; align-items: center;
} }
.sa-card-row-thumb { .sa-card-row-thumb {
width: 2.4rem; width: 3.6rem;
height: 2.4rem; height: 3.6rem;
border-radius: 0.3rem; border-radius: 0.35rem;
object-fit: cover; object-fit: cover;
flex: 0 0 auto; flex: 0 0 auto;
background: color-mix(in srgb, currentColor 8%, transparent); background: color-mix(in srgb, currentColor 8%, transparent);
@@ -1099,12 +1326,68 @@
flex: 1; flex: 1;
} }
.sa-card-row-name {
font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.sa-card-row-chat {
flex: 0 0 auto;
padding: 0.2rem 0.45rem !important;
font-size: 0.85rem;
}
.sa-btn-gen.sa-has-patch {
border-color: color-mix(in srgb, #3fb950 55%, currentColor);
box-shadow: inset 0 0 0 1px color-mix(in srgb, #3fb950 25%, transparent);
}
.sa-patch-details {
margin: 0.35rem 0 0.15rem;
}
.sa-patch-details > summary {
cursor: pointer;
font-size: 0.82rem;
opacity: 0.88;
user-select: none;
list-style: none;
}
.sa-patch-details > summary::-webkit-details-marker {
display: none;
}
.sa-patch-details > summary::before {
content: '▸ ';
opacity: 0.7;
}
.sa-patch-details[open] > summary::before {
content: '▾ ';
}
.sa-patch-details pre {
margin-top: 0.35rem;
max-height: 14rem;
overflow: auto;
}
.sa-patch-auto {
opacity: 0.95;
}
.sa-lora-chips { .sa-lora-chips {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: nowrap;
gap: 0.3rem; gap: 0.3rem;
align-items: center; align-items: center;
min-height: 0; min-height: 0;
overflow-x: auto;
max-width: 100%;
scrollbar-width: thin;
} }
.sa-lora-chip { .sa-lora-chip {
+934 -124
View File
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -1,6 +1,6 @@
{ {
"welcome_html": "<div class=\"sa-welcome-title\">Assistent · Krea 2</div><ul><li><strong>Generate</strong> слева — живой просмотр. В чат сам не уходит.</li><li><strong>Refs</strong> — референсы на отдельной вкладке: drop / paste / Снимок gen.</li><li>Галочка vision на окне — отправить кадр модели.</li><li>Чипсы aspect / seed / Vary / Turbo·RAW. В чате: <code>/help</code>.</li><li>Кнопки патча только у последнего предложения.</li></ul>Напиши, что сгенерировать — или кинь референс и попроси правку.", "welcome_html": "<div class=\"sa-welcome-title\">Assistent · Krea 2</div><ul><li><strong>Generate</strong> слева — живой просмотр. В чат сам не уходит.</li><li><strong>Refs</strong> — референсы на отдельной вкладке: drop / paste / Снимок gen.</li><li>Галочка vision на окне — отправить кадр модели.</li><li>Чипсы aspect / seed / Vary / Turbo·RAW. В чате: <code>/help</code>.</li><li>Кнопки патча только у последнего предложения.</li></ul>Напиши, что сгенерировать — или кинь референс и попроси правку.",
"help_text": "Slash-команды (без LLM):\n/help — этот список\n/debug — сводка UI/Exact\n/debug ask · /why — сводка + короткий ответ модели\n/gen — Generate сейчас\n/look generate|refN — прикрепить окно к vision\n/init /mask /clear — Init / Mask / Clear Init\n/interrupt — остановить генерацию\n/aspect 16:9 — размер из таблицы 1K\n/seed lock|random — зафиксировать или рандомизировать seed\n/vary — новый seed, тот же промпт\n/pack write|critique|compose|params|inpaint|describe|card\n/civitai <query> — поиск LoRA (Confirm в чате)\n/inventory — rescan моделей + обновить список LoRA\n\nЧипсы над полем ввода делают то же для aspect / seed / vary / Turbo·RAW.", "help_text": "Slash-команды (без LLM):\n/help — этот список\n/new — новый чат\n/history — список чатов\n/debug — сводка UI/Exact\n/debug ask · /why — сводка + короткий ответ модели\n/gen — Generate сейчас\n/look generate|refN — прикрепить окно к vision\n/init /mask /clear — Init / Mask / Clear Init\n/interrupt — остановить генерацию\n/aspect 16:9 — размер из таблицы 1K\n/seed lock|random — зафиксировать или рандомизировать seed\n/vary — новый seed, тот же промпт\n/pack write|critique|compose|params|inpaint|describe|card\n/civitai <query> — поиск LoRA (Confirm в чате)\n/inventory — rescan моделей + обновить список LoRA\n\nЧипсы над полем ввода делают то же для aspect / seed / vary / Turbo·RAW.\nПри старте всегда новый чат; смена чата восстанавливает параметры.",
"chips": [ "chips": [
{ "label": "1:1", "action": "aspect", "value": "1:1", "title": "1024×1024" }, { "label": "1:1", "action": "aspect", "value": "1:1", "title": "1024×1024" },
{ "label": "4:5", "action": "aspect", "value": "4:5", "title": "928×1152" }, { "label": "4:5", "action": "aspect", "value": "4:5", "title": "928×1152" },
@@ -17,6 +17,8 @@
], ],
"slash": [ "slash": [
{ "cmd": "/help", "hint": "список команд", "action": "help" }, { "cmd": "/help", "hint": "список команд", "action": "help" },
{ "cmd": "/new", "hint": "новый чат", "action": "new" },
{ "cmd": "/history", "hint": "история чатов", "action": "history" },
{ "cmd": "/debug", "hint": "сводка · ask = с LLM", "action": "debug" }, { "cmd": "/debug", "hint": "сводка · ask = с LLM", "action": "debug" },
{ "cmd": "/why", "hint": "debug + пояснение LLM", "action": "why" }, { "cmd": "/why", "hint": "debug + пояснение LLM", "action": "why" },
{ "cmd": "/gen", "hint": "Generate сейчас", "action": "gen" }, { "cmd": "/gen", "hint": "Generate сейчас", "action": "gen" },
+1 -1
View File
@@ -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. 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.3**Exact KV; silent apply+Generate when user asks; composer **Собрать + Gen**; block model-card previews; critique needs vision + JSON. **Version 0.7.6**Bugfix: clear/new abort in-flight chat; Send blocked while generating; Interrupt always drops stream bubble; patch-clear resets lastPatch.
## Layout ## Layout
+1 -1
View File
@@ -49,7 +49,7 @@ public class SwarmAssistentExtension : Extension
ExtensionAuthor = "mrleo1nid"; ExtensionAuthor = "mrleo1nid";
Description = "Collaborative Krea 2 assistant: Ollama chat, persona presets, vector memory, model cards, Generate loop."; Description = "Collaborative Krea 2 assistant: Ollama chat, persona presets, vector memory, model cards, Generate loop.";
License = "MIT"; License = "MIT";
Version = "0.7.3"; Version = "0.7.6";
Tags = ["tabs", "ui", "llm", "ollama", "krea", "inpaint", "memory"]; Tags = ["tabs", "ui", "llm", "ollama", "krea", "inpaint", "memory"];
} }
+31 -6
View File
@@ -16,12 +16,18 @@
<button type="button" class="basic-button sa-icon-btn" id="sa_btn_add_ref" title="Добавить окно референса">+ Ref</button> <button type="button" class="basic-button sa-icon-btn" id="sa_btn_add_ref" title="Добавить окно референса">+ Ref</button>
</div> </div>
<div class="sa-board" id="sa_board" tabindex="0" title="Перетащи изображение на окно или вставь (Ctrl+V). Кликни, чтобы выбрать."></div> <div class="sa-board" id="sa_board" tabindex="0" title="Перетащи изображение на окно или вставь (Ctrl+V). Кликни, чтобы выбрать."></div>
<div class="sa-live-params" id="sa_live_params" title="Текущие параметры Generate"></div>
<div class="sa-image-actions" id="sa_image_actions"> <div class="sa-image-actions" id="sa_image_actions">
<button type="button" class="basic-button" id="sa_btn_use_current" title="Скопировать текущий Generate в Ref">Снимок gen</button> <button type="button" class="basic-button" id="sa_btn_use_current" title="Скопировать текущий Generate в Ref">Снимок gen</button>
<button type="button" class="basic-button" id="sa_btn_as_init" title="Выбранное окно → Init Image (img2img)">Как Init</button>
<button type="button" class="basic-button" id="sa_btn_as_mask" title="Выбранное окно → Mask (inpaint, белое=правка)">Как Mask</button>
<button type="button" class="basic-button" id="sa_btn_clear_init" title="Очистить Init Image + Mask">Сброс Init</button>
<button type="button" class="basic-button" id="sa_btn_clear_image" title="Очистить выбранный Ref">Очистить слот</button> <button type="button" class="basic-button" id="sa_btn_clear_image" title="Очистить выбранный Ref">Очистить слот</button>
<div class="sa-more-wrap" id="sa_board_more_wrap">
<button type="button" class="basic-button sa-icon-btn" id="sa_btn_board_more" title="Init / Mask / Сброс" aria-expanded="false" aria-haspopup="true"></button>
<div class="sa-more-menu" id="sa_board_more_menu" hidden role="menu">
<button type="button" class="sa-more-item" id="sa_btn_as_init" role="menuitem">Как Init (img2img)</button>
<button type="button" class="sa-more-item" id="sa_btn_as_mask" role="menuitem">Как Mask (inpaint)</button>
<button type="button" class="sa-more-item" id="sa_btn_clear_init" role="menuitem">Сброс Init + Mask</button>
</div>
</div>
</div> </div>
</aside> </aside>
<div class="sa-splitter" id="sa_splitter" role="separator" aria-orientation="vertical" title="Потяни, чтобы изменить ширину"></div> <div class="sa-splitter" id="sa_splitter" role="separator" aria-orientation="vertical" title="Потяни, чтобы изменить ширину"></div>
@@ -30,11 +36,21 @@
<div class="sa-chat-title"> <div class="sa-chat-title">
Assistent Assistent
<span class="sa-live-dot" id="sa_live_dot" hidden></span> <span class="sa-live-dot" id="sa_live_dot" hidden></span>
<div class="sa-sessions-bar">
<button type="button" class="basic-button sa-icon-btn" id="sa_btn_new_chat" title="Новый чат">+</button>
<button type="button" class="basic-button sa-sessions-toggle" id="sa_btn_chats" title="История чатов" aria-expanded="false">История</button>
<span class="sa-session-label" id="sa_session_label" title="Текущий чат — клик открывает Историю" role="button" tabindex="0">Новый чат</span>
</div>
<div class="sa-subtabs" role="tablist"> <div class="sa-subtabs" role="tablist">
<button type="button" class="sa-subtab sa-subtab-active" data-view="chat" id="sa_tab_chat">Чат</button> <button type="button" class="sa-subtab sa-subtab-active" data-view="chat" id="sa_tab_chat">Чат</button>
<button type="button" class="sa-subtab" data-view="cards" id="sa_tab_cards">Карточки</button> <button type="button" class="sa-subtab" data-view="cards" id="sa_tab_cards">Карточки</button>
</div> </div>
</div> </div>
<div class="sa-chats-panel" id="sa_chats_panel" hidden>
<div class="sa-chats-panel-head">Сохранённые чаты</div>
<div class="sa-chats-list" id="sa_chats_list"></div>
<div class="sa-chats-panel-hint">Старт = всегда новый чат. Клик по чату восстанавливает сообщения и параметры Generate.</div>
</div>
<div class="sa-header-right"> <div class="sa-header-right">
<select id="sa_persona" class="sa-select" title="Характер / тон"> <select id="sa_persona" class="sa-select" title="Характер / тон">
<option value="neutral">Нейтральный</option> <option value="neutral">Нейтральный</option>
@@ -42,6 +58,7 @@
<select id="sa_pack" class="sa-select" title="Пакет промпта"> <select id="sa_pack" class="sa-select" title="Пакет промпта">
<option value="write_prompt">Написать промпт</option> <option value="write_prompt">Написать промпт</option>
</select> </select>
<span class="sa-mode-badge" id="sa_mode_badge" title="Активный pack">write</span>
<select id="sa_model" class="sa-select sa-model-select" title="Модель Ollama (чат)"> <select id="sa_model" class="sa-select sa-model-select" title="Модель Ollama (чат)">
<option value="">Загрузка моделей…</option> <option value="">Загрузка моделей…</option>
</select> </select>
@@ -100,9 +117,16 @@
</div> </div>
<div class="sa-composer-actions"> <div class="sa-composer-actions">
<button type="button" class="basic-button sa-primary" id="sa_btn_send">Отправить</button> <button type="button" class="basic-button sa-primary" id="sa_btn_send">Отправить</button>
<button type="button" class="basic-button sa-btn-gen" id="sa_btn_build_gen" title="Применить последний патч Assistent (если есть) и Generate">Собрать + Gen</button> <button type="button" class="basic-button sa-btn-gen" id="sa_btn_build_gen" title="Нет патча — Generate с текущим промптом SwarmUI">Собрать + Gen</button>
<button type="button" class="basic-button" id="sa_btn_interrupt" title="Прервать генерацию / чат" hidden>Стоп</button> <button type="button" class="basic-button" id="sa_btn_interrupt" title="Прервать генерацию / чат" hidden>Стоп</button>
<button type="button" class="basic-button" id="sa_btn_clear">Очистить чат</button> <div class="sa-more-wrap" id="sa_clear_more_wrap">
<button type="button" class="basic-button" id="sa_btn_clear" title="Очистить чат">Очистить</button>
<button type="button" class="basic-button sa-icon-btn" id="sa_btn_clear_more" title="Ещё варианты очистки" aria-expanded="false"></button>
<div class="sa-more-menu" id="sa_clear_more_menu" hidden role="menu">
<button type="button" class="sa-more-item" id="sa_btn_clear_confirm" role="menuitem">Очистить весь чат…</button>
<button type="button" class="sa-more-item" id="sa_btn_clear_patches" role="menuitem">Убрать только патчи</button>
</div>
</div>
<span class="sa-status" id="sa_status"></span> <span class="sa-status" id="sa_status"></span>
</div> </div>
</div> </div>
@@ -139,8 +163,9 @@
<textarea id="sa_card_json" rows="8" hidden placeholder="Card JSON…" spellcheck="false"></textarea> <textarea id="sa_card_json" rows="8" hidden placeholder="Card JSON…" spellcheck="false"></textarea>
</div> </div>
<div class="sa-cards-actions"> <div class="sa-cards-actions">
<button type="button" class="basic-button sa-primary" id="sa_btn_card_to_chat" title="В чат: используй эту модель/LoRA">В чат</button>
<button type="button" class="basic-button" id="sa_btn_card_meta">Загрузить Civitai meta</button> <button type="button" class="basic-button" id="sa_btn_card_meta">Загрузить Civitai meta</button>
<button type="button" class="basic-button sa-primary" id="sa_btn_card_generate">Сгенерировать с Assistent</button> <button type="button" class="basic-button" id="sa_btn_card_generate">Сгенерировать с Assistent</button>
<button type="button" class="basic-button" id="sa_btn_card_save">Сохранить карточку</button> <button type="button" class="basic-button" id="sa_btn_card_save">Сохранить карточку</button>
<button type="button" class="basic-button" id="sa_btn_card_wanted" title="В очередь на следующий up">В wanted</button> <button type="button" class="basic-button" id="sa_btn_card_wanted" title="В очередь на следующий up">В wanted</button>
<span class="sa-status" id="sa_card_status"></span> <span class="sa-status" id="sa_card_status"></span>