From a49746f25ce79865eed4ab70f9c3e08ed8d62832 Mon Sep 17 00:00:00 2001 From: Leonid Pershin Date: Fri, 21 Aug 2026 21:30:37 +0300 Subject: [PATCH] Show persona and pack chips on assistant chat messages. Co-authored-by: Cursor --- Assets/assistent.js | 114 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 98 insertions(+), 16 deletions(-) diff --git a/Assets/assistent.js b/Assets/assistent.js index 37d6323..90e2219 100644 --- a/Assets/assistent.js +++ b/Assets/assistent.js @@ -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; - div.textContent = prose || text || ''; + 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 = 'Waiting for the model…'; + mountAssistantMeta(div, meta); + const body = document.createElement('div'); + body.className = 'sa-msg-body'; + body.innerHTML = 'Waiting for the model…'; + 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 = 'Refining…'; + const body = state.streamEl.querySelector('.sa-msg-body') || state.streamEl; + body.innerHTML = 'Refining…'; } 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,