Replace split Assets JS with a built bundle and aligned C#/config so SwarmUI loads one script and patch apply stays consistent; drop legacy terse persona and memory seed. Co-authored-by: Cursor <cursoragent@cursor.com>
140 lines
3.8 KiB
JavaScript
140 lines
3.8 KiB
JavaScript
/** Sqlite persistence for chats + UI state. */
|
|
|
|
const LS_CHATS = 'swarm_assistent_chats_v1';
|
|
const SAVE_DEBOUNCE_MS = 700;
|
|
|
|
const timers = { chats: new Map(), ui: null };
|
|
|
|
function normalizeChat(raw) {
|
|
if (!raw || !raw.id) {
|
|
return null;
|
|
}
|
|
return {
|
|
id: String(raw.id),
|
|
title: String(raw.title || 'Новый чат'),
|
|
createdAt: Number(raw.createdAt) || Date.now(),
|
|
updatedAt: Number(raw.updatedAt) || Date.now(),
|
|
messages: Array.isArray(raw.messages) ? raw.messages : [],
|
|
messages_count: Number(raw.messages_count) || (Array.isArray(raw.messages) ? raw.messages.length : 0),
|
|
params: raw.params && typeof raw.params === 'object' ? raw.params : null,
|
|
};
|
|
}
|
|
|
|
export function attachPersist(SA, request = SA.request) {
|
|
async function loadChats() {
|
|
let chats = [];
|
|
try {
|
|
const data = await request('AssistentListChats', { with_messages: true });
|
|
chats = (data?.chats || []).map(normalizeChat).filter(Boolean);
|
|
} catch (e) {
|
|
console.warn('Assistent: disk chats unavailable', e);
|
|
return null;
|
|
}
|
|
return chats.sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
}
|
|
|
|
async function getChat(id) {
|
|
if (!id) {
|
|
return null;
|
|
}
|
|
const data = await request('AssistentGetChat', { id });
|
|
return normalizeChat(data?.chat);
|
|
}
|
|
|
|
async function searchChats(q) {
|
|
const query = String(q || '').trim();
|
|
if (query.length < 2) {
|
|
return [];
|
|
}
|
|
const data = await request('AssistentListChats', { q: query, with_messages: false, limit: 40 });
|
|
return (data?.chats || []).map(normalizeChat).filter(Boolean);
|
|
}
|
|
|
|
function saveChat(chat, { immediate = false } = {}) {
|
|
const clean = normalizeChat(chat);
|
|
if (!clean) {
|
|
return Promise.resolve(null);
|
|
}
|
|
const send = () => {
|
|
timers.chats.delete(clean.id);
|
|
return request('AssistentSaveChat', {
|
|
id: clean.id,
|
|
title: clean.title,
|
|
messages: clean.messages,
|
|
params: clean.params,
|
|
createdAt: clean.createdAt,
|
|
updatedAt: clean.updatedAt,
|
|
});
|
|
};
|
|
if (immediate) {
|
|
const pending = timers.chats.get(clean.id);
|
|
if (pending) {
|
|
clearTimeout(pending);
|
|
}
|
|
return send();
|
|
}
|
|
const pending = timers.chats.get(clean.id);
|
|
if (pending) {
|
|
clearTimeout(pending);
|
|
}
|
|
timers.chats.set(clean.id, setTimeout(() => {
|
|
send().catch((e) => console.warn('Assistent: chat save failed', clean.id, e));
|
|
}, SAVE_DEBOUNCE_MS));
|
|
return Promise.resolve(null);
|
|
}
|
|
|
|
function deleteChat(id) {
|
|
if (!id) {
|
|
return Promise.resolve(null);
|
|
}
|
|
const pending = timers.chats.get(id);
|
|
if (pending) {
|
|
clearTimeout(pending);
|
|
timers.chats.delete(id);
|
|
}
|
|
return request('AssistentDeleteChat', { id });
|
|
}
|
|
|
|
async function loadUiState() {
|
|
try {
|
|
const data = await request('AssistentGetUiState', {});
|
|
const ui = data?.ui_state;
|
|
return ui && typeof ui === 'object' ? ui : null;
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function saveUiState(uiState, { immediate = false } = {}) {
|
|
if (!uiState || typeof uiState !== 'object') {
|
|
return Promise.resolve(null);
|
|
}
|
|
const send = () => {
|
|
timers.ui = null;
|
|
return request('AssistentSaveUiState', { ui_state: uiState });
|
|
};
|
|
if (timers.ui) {
|
|
clearTimeout(timers.ui);
|
|
timers.ui = null;
|
|
}
|
|
if (immediate) {
|
|
return send();
|
|
}
|
|
timers.ui = setTimeout(() => {
|
|
send().catch((e) => console.warn('Assistent: ui-state save failed', e));
|
|
}, SAVE_DEBOUNCE_MS);
|
|
return Promise.resolve(null);
|
|
}
|
|
|
|
SA.persist = {
|
|
LS_CHATS,
|
|
loadChats,
|
|
getChat,
|
|
searchChats,
|
|
saveChat,
|
|
deleteChat,
|
|
loadUiState,
|
|
saveUiState,
|
|
};
|
|
}
|