Ship Assistent disk persist, park LLM, and memory UI cleanup.
Split the extension into partials, persist chats on the data volume, park/warm the chat model around Generate, and drop dual raw/persona dump paths. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+84
-6
@@ -155,6 +155,8 @@
|
||||
activeChatId: null,
|
||||
restoringChat: false,
|
||||
chatsPanelOpen: false,
|
||||
chatsQuery: '',
|
||||
chatsSearchHits: null,
|
||||
slashIndex: 0,
|
||||
llmParked: false,
|
||||
memoryRows: [],
|
||||
@@ -1769,6 +1771,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
function chatMatchesQuery(chat, q) {
|
||||
if (!q) {
|
||||
return true;
|
||||
}
|
||||
const title = String(chat?.title || '').toLowerCase();
|
||||
if (title.includes(q)) {
|
||||
return true;
|
||||
}
|
||||
const msgs = chat?.messages || [];
|
||||
for (const m of msgs) {
|
||||
if (String(m?.content || '').toLowerCase().includes(q)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function renderChatsList() {
|
||||
const root = $('sa_chats_list');
|
||||
if (!root) {
|
||||
@@ -1776,19 +1795,28 @@
|
||||
}
|
||||
root.innerHTML = '';
|
||||
syncHistoryBadge();
|
||||
const chats = (state.chats || [])
|
||||
const q = (state.chatsQuery || '').trim().toLowerCase();
|
||||
let chats = (state.chats || [])
|
||||
.slice()
|
||||
.sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0))
|
||||
.filter((c) => (c.messages || []).length > 0);
|
||||
.filter((c) => (c.messages || []).length > 0 || (c.messages_count || 0) > 0);
|
||||
if (q) {
|
||||
const local = chats.filter((c) => chatMatchesQuery(c, q));
|
||||
const seen = new Set(local.map((c) => c.id));
|
||||
const extra = (state.chatsSearchHits || []).filter((h) => h && h.id && !seen.has(h.id));
|
||||
chats = local.concat(extra);
|
||||
}
|
||||
if (!chats.length) {
|
||||
root.innerHTML = '<div class="sa-chats-empty">Пока пусто. Напиши что-нибудь в чат — он появится здесь. Переключение вернёт и сообщения, и параметры Generate.</div>';
|
||||
root.innerHTML = q
|
||||
? '<div class="sa-chats-empty">Ничего не нашлось.</div>'
|
||||
: '<div class="sa-chats-empty">Пока пусто. Напиши что-нибудь в чат — он появится здесь. Переключение вернёт и сообщения, и параметры Generate.</div>';
|
||||
return;
|
||||
}
|
||||
for (const c of chats) {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'sa-chat-row' + (c.id === state.activeChatId ? ' sa-chat-row-active' : '');
|
||||
row.dataset.id = c.id;
|
||||
const n = (c.messages || []).length;
|
||||
const n = (c.messages || []).length || Number(c.messages_count) || 0;
|
||||
const bits = [];
|
||||
if (c.params?.width && c.params?.height) {
|
||||
bits.push(`${c.params.width}×${c.params.height}`);
|
||||
@@ -1818,6 +1846,11 @@
|
||||
btn?.setAttribute('aria-expanded', state.chatsPanelOpen ? 'true' : 'false');
|
||||
if (state.chatsPanelOpen) {
|
||||
saveActiveChatToStore();
|
||||
const search = $('sa_chats_search');
|
||||
if (search) {
|
||||
search.value = state.chatsQuery || '';
|
||||
search.focus();
|
||||
}
|
||||
renderChatsList();
|
||||
}
|
||||
}
|
||||
@@ -1878,7 +1911,23 @@
|
||||
return;
|
||||
}
|
||||
saveActiveChatToStore({ dropEmpty: true });
|
||||
const chat = findChat(id);
|
||||
let chat = findChat(id);
|
||||
if (!chat || !(chat.messages || []).length) {
|
||||
try {
|
||||
const full = await diskPersist()?.getChat?.(id);
|
||||
if (full) {
|
||||
const idx = (state.chats || []).findIndex((c) => c.id === id);
|
||||
if (idx >= 0) {
|
||||
state.chats[idx] = full;
|
||||
} else {
|
||||
state.chats.unshift(full);
|
||||
}
|
||||
chat = full;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Assistent: getChat failed', id, e);
|
||||
}
|
||||
}
|
||||
if (!chat) {
|
||||
setStatus('Чат не найден');
|
||||
return;
|
||||
@@ -3346,7 +3395,9 @@
|
||||
if (!state.busy) {
|
||||
stopBusyUi(src ? 'Generate готов' : 'Generate завершён (новое изображение не найдено)');
|
||||
}
|
||||
if (state.view === 'chat') {
|
||||
// Auto-critique loads the model itself on the next request — don't pay for it twice.
|
||||
const paneVisible = !!document.getElementById('swarm_assistent_root')?.offsetParent;
|
||||
if (state.view === 'chat' && paneVisible && !$('sa_auto_critique')?.checked) {
|
||||
warmLlm();
|
||||
}
|
||||
if (src) {
|
||||
@@ -5140,6 +5191,9 @@
|
||||
? `Карточка Assistent сохранена · ${data.path}`
|
||||
: `Черновик + wanted · ${data.path}`);
|
||||
refreshInventory(() => renderCardsList());
|
||||
if (enqueue || !data.installed) {
|
||||
refreshWantedQueue();
|
||||
}
|
||||
},
|
||||
0,
|
||||
(err) => setCardStatus(String(err || 'Ошибка сохранения')),
|
||||
@@ -5164,6 +5218,7 @@
|
||||
},
|
||||
(data) => {
|
||||
setCardStatus(data.already ? 'Уже в wanted' : `Wanted → ${data.path}`);
|
||||
refreshWantedQueue();
|
||||
},
|
||||
0,
|
||||
(err) => setCardStatus(String(err || 'Ошибка enqueue')),
|
||||
@@ -6275,6 +6330,7 @@
|
||||
splitter.classList.remove('sa-dragging');
|
||||
document.body.style.cursor = '';
|
||||
document.body.style.userSelect = '';
|
||||
saveUiStateToDisk();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6394,6 +6450,28 @@
|
||||
switchToChat(id);
|
||||
}
|
||||
});
|
||||
let chatsSearchTimer = null;
|
||||
$('sa_chats_search')?.addEventListener('input', () => {
|
||||
const q = ($('sa_chats_search')?.value || '').trim();
|
||||
state.chatsQuery = q;
|
||||
if (!q) {
|
||||
state.chatsSearchHits = null;
|
||||
renderChatsList();
|
||||
return;
|
||||
}
|
||||
renderChatsList();
|
||||
clearTimeout(chatsSearchTimer);
|
||||
chatsSearchTimer = setTimeout(async () => {
|
||||
try {
|
||||
const hits = await diskPersist()?.searchChats?.(q);
|
||||
if ((state.chatsQuery || '') !== q) {
|
||||
return;
|
||||
}
|
||||
state.chatsSearchHits = Array.isArray(hits) ? hits : [];
|
||||
renderChatsList();
|
||||
} catch (e) { /* ignore */ }
|
||||
}, 220);
|
||||
});
|
||||
|
||||
$('sa_tab_chat')?.addEventListener('click', () => setView('chat'));
|
||||
$('sa_tab_cards')?.addEventListener('click', () => setView('cards'));
|
||||
|
||||
Reference in New Issue
Block a user