SillyTavern/public/scripts/extensions/quick-reply/index.js
2023-12-22 13:45:37 +00:00

203 lines
6.5 KiB
JavaScript

import { chat_metadata, eventSource, event_types, getRequestHeaders } from '../../../script.js';
import { extension_settings } from '../../extensions.js';
import { QuickReply } from './src/QuickReply.js';
import { QuickReplyConfig } from './src/QuickReplyConfig.js';
import { QuickReplyContextLink } from './src/QuickReplyContextLink.js';
import { QuickReplySet } from './src/QuickReplySet.js';
import { QuickReplySettings } from './src/QuickReplySettings.js';
import { ButtonUi } from './src/ui/ButtonUi.js';
import { SettingsUi } from './src/ui/SettingsUi.js';
//TODO popout QR button bar (allow separate popouts for each QR set?)
//TODO context menus
//TODO move advanced QR options into own UI class
//TODO slash commands
//TODO handle replacing QR set (create->overwrite): update configs that use this set
//TODO easy way to CRUD QRs and sets
//TODO easy way to set global and chat sets
const _VERBOSE = true;
export const log = (...msg) => _VERBOSE ? console.log('[QR2]', ...msg) : null;
export const warn = (...msg) => _VERBOSE ? console.warn('[QR2]', ...msg) : null;
const defaultConfig = {
setList: [{
set: 'Default',
isVisible: true,
}],
};
const defaultSettings = {
isEnabled: true,
isCombined: false,
config: defaultConfig,
};
/** @type {QuickReplySettings}*/
let settings;
/** @type {SettingsUi} */
let manager;
/** @type {ButtonUi} */
let buttons;
const loadSets = async () => {
const response = await fetch('/api/settings/get', {
method: 'POST',
headers: getRequestHeaders(),
body: JSON.stringify({}),
});
if (response.ok) {
const setList = (await response.json()).quickReplyPresets ?? [];
for (const set of setList) {
if (set.version == 2) {
QuickReplySet.list.push(QuickReplySet.from(set));
} else {
const qrs = new QuickReplySet();
qrs.name = set.name;
qrs.disableSend = set.quickActionEnabled ?? false;
qrs.placeBeforeInput = set.placeBeforeInputEnabled ?? false;
qrs.injectInput = set.AutoInputInject ?? false;
qrs.qrList = set.quickReplySlots.map((slot,idx)=>{
const qr = new QuickReply();
qr.id = idx + 1;
qr.label = slot.label;
qr.title = slot.title;
qr.message = slot.mes;
qr.isHidden = slot.hidden ?? false;
qr.executeOnStartup = slot.autoExecute_appStartup ?? false;
qr.executeOnUser = slot.autoExecute_userMessage ?? false;
qr.executeOnAi = slot.autoExecute_botMessage ?? false;
qr.executeOnChatChange = slot.autoExecute_chatLoad ?? false;
qr.contextList = (slot.contextMenu ?? []).map(it=>(QuickReplyContextLink.from({
set: it.preset,
isChained: it.chain,
})));
return qr;
});
QuickReplySet.list.push(qrs);
await qrs.save();
}
}
log('sets: ', QuickReplySet.list);
}
};
const loadSettings = async () => {
//TODO migrate old settings
if (!extension_settings.quickReplyV2) {
extension_settings.quickReplyV2 = defaultSettings;
}
try {
settings = QuickReplySettings.from(extension_settings.quickReplyV2);
} catch (ex) {
debugger;
settings = QuickReplySettings.from(defaultSettings);
}
};
const init = async () => {
await loadSets();
await loadSettings();
log('settings: ', settings);
manager = new SettingsUi(settings);
document.querySelector('#extensions_settings2').append(await manager.render());
buttons = new ButtonUi(settings);
buttons.show();
settings.onSave = ()=>buttons.refresh();
window['executeQuickReplyByName'] = async(name) => {
let qr = [...settings.config.setList, ...(settings.chatConfig?.setList ?? [])]
.map(it=>it.set.qrList)
.flat()
.find(it=>it.label == name)
;
if (!qr) {
let [setName, ...qrName] = name.split('.');
name = qrName.join('.');
let qrs = QuickReplySet.get(setName);
if (qrs) {
qr = qrs.qrList.find(it=>it.label == name);
}
}
if (qr && qr.onExecute) {
return await qr.onExecute();
}
};
if (settings.isEnabled) {
const qrList = [
...settings.config.setList.map(link=>link.set.qrList.filter(qr=>qr.executeOnStartup)).flat(),
...(settings.chatConfig?.setList?.map(link=>link.set.qrList.filter(qr=>qr.executeOnStartup))?.flat() ?? []),
];
for (const qr of qrList) {
await qr.onExecute();
}
}
};
eventSource.on(event_types.APP_READY, init);
const onChatChanged = async (chatIdx) => {
log('CHAT_CHANGED', chatIdx);
if (chatIdx) {
settings.chatConfig = QuickReplyConfig.from(chat_metadata.quickReply ?? {});
} else {
settings.chatConfig = null;
}
manager.rerender();
buttons.refresh();
if (settings.isEnabled) {
const qrList = [
...settings.config.setList.map(link=>link.set.qrList.filter(qr=>qr.executeOnChatChange)).flat(),
...(settings.chatConfig?.setList?.map(link=>link.set.qrList.filter(qr=>qr.executeOnChatChange))?.flat() ?? []),
];
for (const qr of qrList) {
await qr.onExecute();
}
}
};
eventSource.on(event_types.CHAT_CHANGED, onChatChanged);
const onUserMessage = async () => {
if (settings.isEnabled) {
const qrList = [
...settings.config.setList.map(link=>link.set.qrList.filter(qr=>qr.executeOnUser)).flat(),
...(settings.chatConfig?.setList?.map(link=>link.set.qrList.filter(qr=>qr.executeOnUser))?.flat() ?? []),
];
for (const qr of qrList) {
await qr.onExecute();
}
}
};
eventSource.on(event_types.USER_MESSAGE_RENDERED, onUserMessage);
const onAiMessage = async () => {
if (settings.isEnabled) {
const qrList = [
...settings.config.setList.map(link=>link.set.qrList.filter(qr=>qr.executeOnAi)).flat(),
...(settings.chatConfig?.setList?.map(link=>link.set.qrList.filter(qr=>qr.executeOnAi))?.flat() ?? []),
];
for (const qr of qrList) {
await qr.onExecute();
}
}
};
eventSource.on(event_types.CHARACTER_MESSAGE_RENDERED, onAiMessage);