Show persona and pack chips on assistant chat messages.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-21 21:30:37 +03:00
co-authored by Cursor
parent ca98ab577a
commit a49746f25c
+97 -15
View File
@@ -87,6 +87,7 @@
inventoryFetchedAt: 0,
taste: { styles: [], likes: [], avoid: [], notes: '', updated: 0 },
streamEl: null,
streamMeta: null,
critiqueHopUsed: false,
visionHopUsed: false,
busyPhase: 'idle',
@@ -1981,7 +1982,60 @@
await sendChat({ fromAutoCritique: true, forceSlotIds: [GEN_ID] });
}
function appendMessage(role, text, patch, civitaiResults) {
function currentPersonaInfo() {
const id = ($('sa_persona')?.value || localStorage.getItem(LS_PERSONA) || 'neutral').trim() || 'neutral';
const known = (state.personas || []).find((p) => p && p.id === id);
return {
id,
title: (known && known.title) || ({
neutral: 'Нейтральный',
lewd: 'Пошляк',
aggressive: 'Агрессивный',
}[id] || id),
};
}
function mountAssistantMeta(div, meta = {}) {
if (!div || div.querySelector('.sa-msg-meta')) {
return;
}
const persona = meta.persona || currentPersonaInfo();
const pack = meta.pack || $('sa_pack')?.value || '';
div.dataset.persona = persona.id || 'neutral';
if (pack) {
div.dataset.pack = pack;
}
const row = document.createElement('div');
row.className = 'sa-msg-meta';
const chip = document.createElement('span');
chip.className = `sa-persona-mark sa-persona-${persona.id || 'neutral'}`;
chip.textContent = persona.title || persona.id;
chip.title = `Характер: ${persona.title || persona.id}${pack ? ` · режим ${pack}` : ''}`;
row.appendChild(chip);
if (pack && pack !== 'write_prompt') {
const packEl = document.createElement('span');
packEl.className = 'sa-pack-mark';
packEl.textContent = pack.replace(/_/g, ' ');
packEl.title = `Режим: ${pack}`;
row.appendChild(packEl);
}
div.insertBefore(row, div.firstChild);
}
function setAssistantBody(div, text) {
if (!div) {
return;
}
let body = div.querySelector('.sa-msg-body');
if (!body) {
body = document.createElement('div');
body.className = 'sa-msg-body';
div.appendChild(body);
}
body.textContent = text || '';
}
function appendMessage(role, text, patch, civitaiResults, meta) {
const box = $('sa_messages');
if (!box) {
return null;
@@ -1989,9 +2043,16 @@
hideChatEmpty();
const div = document.createElement('div');
div.className = `sa-msg ${role}`;
if (role === 'assistant') {
mountAssistantMeta(div, meta);
}
const { prose, patch: extracted } = role === 'assistant' ? extractPatch(text) : { prose: text, patch: null };
const finalPatch = patch || extracted;
if (role === 'assistant') {
setAssistantBody(div, prose || text || '');
} else {
div.textContent = prose || text || '';
}
if (finalPatch) {
const wrap = document.createElement('div');
wrap.className = 'sa-patch';
@@ -2009,7 +2070,7 @@
return div;
}
function beginStreamMessage() {
function beginStreamMessage(meta) {
const box = $('sa_messages');
if (!box) {
return null;
@@ -2017,27 +2078,37 @@
hideChatEmpty();
const div = document.createElement('div');
div.className = 'sa-msg assistant sa-streaming sa-typing';
div.innerHTML = '<span class="sa-dots" aria-hidden="true"><i></i><i></i><i></i></span><span class="sa-typing-label">Waiting for the model…</span>';
mountAssistantMeta(div, meta);
const body = document.createElement('div');
body.className = 'sa-msg-body';
body.innerHTML = '<span class="sa-dots" aria-hidden="true"><i></i><i></i><i></i></span><span class="sa-typing-label">Waiting for the model…</span>';
div.appendChild(body);
box.appendChild(div);
box.scrollTop = box.scrollHeight;
state.streamEl = div;
state.streamMeta = meta || null;
return div;
}
function appendStreamDelta(delta) {
if (!state.streamEl) {
beginStreamMessage();
beginStreamMessage(state.streamMeta || undefined);
}
if (state.streamEl) {
if (state.streamEl.classList.contains('sa-typing')) {
state.streamEl.classList.remove('sa-typing');
state.streamEl.textContent = '';
setAssistantBody(state.streamEl, '');
}
state.gotDelta = true;
if (state.busyPhase !== 'refining') {
setBusyPhase('streaming');
}
state.streamEl.textContent += delta;
let body = state.streamEl.querySelector('.sa-msg-body');
if (!body) {
setAssistantBody(state.streamEl, '');
body = state.streamEl.querySelector('.sa-msg-body');
}
body.textContent += delta;
const box = $('sa_messages');
if (box) {
box.scrollTop = box.scrollHeight;
@@ -2047,14 +2118,18 @@
function finalizeStreamMessage(fullReply, civitaiResults) {
const el = state.streamEl;
const meta = state.streamMeta;
state.streamEl = null;
state.streamMeta = null;
if (!el) {
appendMessage('assistant', fullReply, null, civitaiResults);
appendMessage('assistant', fullReply, null, civitaiResults, meta || undefined);
return;
}
el.classList.remove('sa-streaming', 'sa-typing');
mountAssistantMeta(el, meta || undefined);
const { prose, patch } = extractPatch(fullReply);
el.textContent = prose || fullReply || '';
setAssistantBody(el, prose || fullReply || '');
el.querySelectorAll('.sa-patch, .sa-civitai-list').forEach((n) => n.remove());
if (patch) {
const wrap = document.createElement('div');
wrap.className = 'sa-patch';
@@ -3294,6 +3369,10 @@
}
}
const msgMeta = {
persona: currentPersonaInfo(),
pack,
};
state.history.push({ role: 'user', content: text });
appendMessage('user', text);
$('sa_input').value = '';
@@ -3346,7 +3425,7 @@
const finishOk = async (reply, civitaiResults) => {
state.busy = false;
setInterruptVisible(state.generating);
state.history.push({ role: 'assistant', content: reply });
state.history.push({ role: 'assistant', content: reply, persona, pack });
stopBusyUi('Done');
await handleReplySideEffects(reply, civitaiResults, {
...opts,
@@ -3362,15 +3441,16 @@
if (state.streamEl) {
state.streamEl.classList.remove('sa-streaming', 'sa-typing');
state.streamEl.classList.add('error');
state.streamEl.textContent = msg;
setAssistantBody(state.streamEl, msg);
state.streamEl = null;
state.streamMeta = null;
} else {
appendMessage('error', msg);
}
};
if (typeof makeWSRequest === 'function') {
beginStreamMessage();
beginStreamMessage(msgMeta);
makeWSRequest(
'AssistentChatWS',
payload,
@@ -3390,7 +3470,8 @@
if (data.clear_stream) {
if (state.streamEl) {
state.streamEl.classList.add('sa-typing');
state.streamEl.innerHTML = '<span class="sa-dots" aria-hidden="true"><i></i><i></i><i></i></span><span class="sa-typing-label">Refining…</span>';
const body = state.streamEl.querySelector('.sa-msg-body') || state.streamEl;
body.innerHTML = '<span class="sa-dots" aria-hidden="true"><i></i><i></i><i></i></span><span class="sa-typing-label">Refining…</span>';
}
setBusyPhase('refining');
return;
@@ -3400,7 +3481,7 @@
return;
}
if (data.done || data.reply != null) {
const reply = data.reply || (state.streamEl && state.streamEl.textContent) || '';
const reply = data.reply || (state.streamEl?.querySelector('.sa-msg-body')?.textContent) || '';
const civitai = data.civitai_results || [];
finalizeStreamMessage(reply, civitai);
finishOk(reply, civitai);
@@ -3413,6 +3494,7 @@
if (state.streamEl) {
state.streamEl.remove();
state.streamEl = null;
state.streamMeta = null;
}
genericRequest(
'AssistentChat',
@@ -3423,7 +3505,7 @@
return;
}
const reply = data.reply || '';
appendMessage('assistant', reply, null, data.civitai_results || []);
appendMessage('assistant', reply, null, data.civitai_results || [], msgMeta);
finishOk(reply, data.civitai_results || []);
},
0,
@@ -3443,7 +3525,7 @@
return;
}
const reply = data.reply || '';
appendMessage('assistant', reply, null, data.civitai_results || []);
appendMessage('assistant', reply, null, data.civitai_results || [], msgMeta);
finishOk(reply, data.civitai_results || []);
},
0,