|
import { Fuse } from '../lib.js'; |
|
|
|
import { |
|
shuffle, |
|
onlyUnique, |
|
debounce, |
|
delay, |
|
isDataURL, |
|
createThumbnail, |
|
extractAllWords, |
|
saveBase64AsFile, |
|
PAGINATION_TEMPLATE, |
|
getBase64Async, |
|
resetScrollHeight, |
|
initScrollHeight, |
|
localizePagination, |
|
renderPaginationDropdown, |
|
paginationDropdownChangeHandler, |
|
waitUntilCondition, |
|
} from './utils.js'; |
|
import { RA_CountCharTokens, humanizedDateTime, dragElement, favsToHotswap, getMessageTimeStamp } from './RossAscends-mods.js'; |
|
import { power_user, loadMovingUIState, sortEntitiesList } from './power-user.js'; |
|
import { debounce_timeout } from './constants.js'; |
|
|
|
import { |
|
chat, |
|
sendSystemMessage, |
|
printMessages, |
|
substituteParams, |
|
characters, |
|
default_avatar, |
|
addOneMessage, |
|
clearChat, |
|
Generate, |
|
select_rm_info, |
|
setCharacterId, |
|
setCharacterName, |
|
setEditedMessageId, |
|
is_send_press, |
|
name1, |
|
resetChatState, |
|
setSendButtonState, |
|
getCharacters, |
|
system_message_types, |
|
online_status, |
|
talkativeness_default, |
|
selectRightMenuWithAnimation, |
|
deleteLastMessage, |
|
showSwipeButtons, |
|
hideSwipeButtons, |
|
chat_metadata, |
|
updateChatMetadata, |
|
getThumbnailUrl, |
|
getRequestHeaders, |
|
setMenuType, |
|
menu_type, |
|
select_selected_character, |
|
cancelTtsPlay, |
|
displayPastChats, |
|
sendMessageAsUser, |
|
getBiasStrings, |
|
saveChatConditional, |
|
deactivateSendButtons, |
|
activateSendButtons, |
|
eventSource, |
|
event_types, |
|
getCurrentChatId, |
|
setScenarioOverride, |
|
system_avatar, |
|
isChatSaving, |
|
setExternalAbortController, |
|
baseChatReplace, |
|
depth_prompt_depth_default, |
|
loadItemizedPrompts, |
|
animation_duration, |
|
depth_prompt_role_default, |
|
shouldAutoContinue, |
|
unshallowCharacter, |
|
} from '../script.js'; |
|
import { printTagList, createTagMapFromList, applyTagsOnCharacterSelect, tag_map, applyTagsOnGroupSelect } from './tags.js'; |
|
import { FILTER_TYPES, FilterHelper } from './filters.js'; |
|
import { isExternalMediaAllowed } from './chats.js'; |
|
import { POPUP_TYPE, Popup, callGenericPopup } from './popup.js'; |
|
import { t } from './i18n.js'; |
|
import { accountStorage } from './util/AccountStorage.js'; |
|
|
|
export { |
|
selected_group, |
|
openGroupId, |
|
is_group_automode_enabled, |
|
hideMutedSprites, |
|
is_group_generating, |
|
group_generation_id, |
|
groups, |
|
saveGroupChat, |
|
generateGroupWrapper, |
|
deleteGroup, |
|
getGroupAvatar, |
|
getGroups, |
|
regenerateGroup, |
|
resetSelectedGroup, |
|
select_group_chats, |
|
getGroupChatNames, |
|
}; |
|
|
|
let is_group_generating = false; |
|
let is_group_automode_enabled = false; |
|
let hideMutedSprites = false; |
|
let groups = []; |
|
let selected_group = null; |
|
let group_generation_id = null; |
|
let fav_grp_checked = false; |
|
let openGroupId = null; |
|
let newGroupMembers = []; |
|
|
|
export const group_activation_strategy = { |
|
NATURAL: 0, |
|
LIST: 1, |
|
MANUAL: 2, |
|
POOLED: 3, |
|
}; |
|
|
|
export const group_generation_mode = { |
|
SWAP: 0, |
|
APPEND: 1, |
|
APPEND_DISABLED: 2, |
|
}; |
|
|
|
const DEFAULT_AUTO_MODE_DELAY = 5; |
|
|
|
export const groupCandidatesFilter = new FilterHelper(debounce(printGroupCandidates, debounce_timeout.quick)); |
|
let autoModeWorker = null; |
|
const saveGroupDebounced = debounce(async (group, reload) => await _save(group, reload), debounce_timeout.relaxed); |
|
|
|
let groupChatQueueOrder = new Map(); |
|
|
|
function setAutoModeWorker() { |
|
clearInterval(autoModeWorker); |
|
const autoModeDelay = groups.find(x => x.id === selected_group)?.auto_mode_delay ?? DEFAULT_AUTO_MODE_DELAY; |
|
autoModeWorker = setInterval(groupChatAutoModeWorker, autoModeDelay * 1000); |
|
} |
|
|
|
async function _save(group, reload = true) { |
|
await fetch('/api/groups/edit', { |
|
method: 'POST', |
|
headers: getRequestHeaders(), |
|
body: JSON.stringify(group), |
|
}); |
|
if (reload) { |
|
await getCharacters(); |
|
} |
|
} |
|
|
|
|
|
async function regenerateGroup() { |
|
let generationId = getLastMessageGenerationId(); |
|
|
|
while (chat.length > 0) { |
|
const lastMes = chat[chat.length - 1]; |
|
const this_generationId = lastMes.extra?.gen_id; |
|
|
|
|
|
if ((generationId && this_generationId) && generationId !== this_generationId) { |
|
break; |
|
} |
|
|
|
else if (lastMes.is_user || lastMes.is_system) { |
|
break; |
|
} |
|
|
|
await deleteLastMessage(); |
|
} |
|
|
|
const abortController = new AbortController(); |
|
setExternalAbortController(abortController); |
|
generateGroupWrapper(false, 'normal', { signal: abortController.signal }); |
|
} |
|
|
|
async function loadGroupChat(chatId) { |
|
const response = await fetch('/api/chats/group/get', { |
|
method: 'POST', |
|
headers: getRequestHeaders(), |
|
body: JSON.stringify({ id: chatId }), |
|
}); |
|
|
|
if (response.ok) { |
|
const data = await response.json(); |
|
return data; |
|
} |
|
|
|
return []; |
|
} |
|
|
|
async function validateGroup(group) { |
|
if (!group) return; |
|
|
|
|
|
let dirty = false; |
|
group.members = group.members.filter(member => { |
|
const character = characters.find(x => x.avatar === member || x.name === member); |
|
if (!character) { |
|
const msg = t`Warning: Listed member ${member} does not exist as a character. It will be removed from the group.`; |
|
toastr.warning(msg, t`Group Validation`); |
|
console.warn(msg); |
|
dirty = true; |
|
} |
|
return character; |
|
}); |
|
|
|
|
|
if (Array.isArray(group.chats)) { |
|
const lengthBefore = group.chats.length; |
|
group.chats = group.chats.filter(onlyUnique); |
|
const lengthAfter = group.chats.length; |
|
if (lengthBefore !== lengthAfter) { |
|
dirty = true; |
|
} |
|
} |
|
|
|
if (dirty) { |
|
await editGroup(group.id, true, false); |
|
} |
|
} |
|
|
|
export async function getGroupChat(groupId, reload = false) { |
|
const group = groups.find((x) => x.id === groupId); |
|
if (!group) { |
|
console.warn('Group not found', groupId); |
|
return; |
|
} |
|
|
|
|
|
await validateGroup(group); |
|
await unshallowGroupMembers(groupId); |
|
|
|
const chat_id = group.chat_id; |
|
const data = await loadGroupChat(chat_id); |
|
const metadata = group.chat_metadata ?? {}; |
|
let freshChat = false; |
|
|
|
await loadItemizedPrompts(getCurrentChatId()); |
|
|
|
if (Array.isArray(data) && data.length) { |
|
data[0].is_group = true; |
|
chat.splice(0, chat.length, ...data); |
|
await printMessages(); |
|
} else { |
|
freshChat = !metadata.tainted; |
|
if (group && Array.isArray(group.members) && freshChat) { |
|
for (let member of group.members) { |
|
const character = characters.find(x => x.avatar === member || x.name === member); |
|
if (!character) { |
|
continue; |
|
} |
|
|
|
const mes = await getFirstCharacterMessage(character); |
|
|
|
|
|
if (!(mes?.mes)) { |
|
continue; |
|
} |
|
|
|
chat.push(mes); |
|
await eventSource.emit(event_types.MESSAGE_RECEIVED, (chat.length - 1), 'first_message'); |
|
addOneMessage(mes); |
|
await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, (chat.length - 1), 'first_message'); |
|
} |
|
await saveGroupChat(groupId, false); |
|
} |
|
} |
|
|
|
updateChatMetadata(metadata, true); |
|
|
|
if (reload) { |
|
select_group_chats(groupId, true); |
|
} |
|
|
|
await eventSource.emit(event_types.CHAT_CHANGED, getCurrentChatId()); |
|
if (freshChat) await eventSource.emit(event_types.GROUP_CHAT_CREATED); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function getGroupMembers(groupId = selected_group) { |
|
const group = groups.find((x) => x.id === groupId); |
|
return group?.members.map(member => characters.find(x => x.avatar === member)) ?? []; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
export function getGroupNames() { |
|
if (!selected_group) { |
|
return []; |
|
} |
|
const groupMembers = groups.find(x => x.id == selected_group)?.members; |
|
return Array.isArray(groupMembers) |
|
? groupMembers.map(x => characters.find(y => y.avatar === x)?.name).filter(x => x) |
|
: []; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function findGroupMemberId(arg, full = false) { |
|
arg = arg?.trim(); |
|
|
|
if (!arg) { |
|
console.warn('WARN: No argument provided for findGroupMemberId'); |
|
return; |
|
} |
|
|
|
const group = groups.find(x => x.id == selected_group); |
|
|
|
if (!group || !Array.isArray(group.members)) { |
|
console.warn('WARN: No group found for selected group ID'); |
|
return; |
|
} |
|
|
|
const index = parseInt(arg); |
|
const searchByString = isNaN(index); |
|
|
|
if (searchByString) { |
|
const memberNames = group.members.map(x => ({ |
|
avatar: x, |
|
name: characters.find(y => y.avatar === x)?.name, |
|
index: characters.findIndex(y => y.avatar === x), |
|
})); |
|
const fuse = new Fuse(memberNames, { keys: ['avatar', 'name'] }); |
|
const result = fuse.search(arg); |
|
|
|
if (!result.length) { |
|
console.warn(`WARN: No group member found using string ${arg}`); |
|
return; |
|
} |
|
|
|
const chid = result[0].item.index; |
|
|
|
if (chid === -1) { |
|
console.warn(`WARN: No character found for group member ${arg}`); |
|
return; |
|
} |
|
|
|
console.log(`Targeting group member ${chid} (${arg}) from search result`, result[0]); |
|
|
|
return !full ? chid : { ...{ id: chid }, ...result[0].item }; |
|
} |
|
else { |
|
const memberAvatar = group.members[index]; |
|
|
|
if (memberAvatar === undefined) { |
|
console.warn(`WARN: No group member found at index ${index}`); |
|
return; |
|
} |
|
|
|
const chid = characters.findIndex(x => x.avatar === memberAvatar); |
|
|
|
if (chid === -1) { |
|
console.warn(`WARN: No character found for group member ${memberAvatar} at index ${index}`); |
|
return; |
|
} |
|
|
|
console.log(`Targeting group member ${memberAvatar} at index ${index}`); |
|
|
|
return !full ? chid : { |
|
id: chid, |
|
avatar: memberAvatar, |
|
name: characters.find(y => y.avatar === memberAvatar)?.name, |
|
index: index, |
|
}; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function getGroupDepthPrompts(groupId, characterId) { |
|
if (!groupId) { |
|
return []; |
|
} |
|
|
|
console.debug('getGroupDepthPrompts entered for group: ', groupId); |
|
const group = groups.find(x => x.id === groupId); |
|
|
|
if (!group || !Array.isArray(group.members) || !group.members.length) { |
|
return []; |
|
} |
|
|
|
if (group.generation_mode === group_generation_mode.SWAP) { |
|
return []; |
|
} |
|
|
|
const depthPrompts = []; |
|
|
|
for (const member of group.members) { |
|
const index = characters.findIndex(x => x.avatar === member); |
|
const character = characters[index]; |
|
|
|
if (index === -1 || !character) { |
|
console.debug(`Skipping missing member: ${member}`); |
|
continue; |
|
} |
|
|
|
if (group.disabled_members.includes(member) && characterId !== index) { |
|
console.debug(`Skipping disabled group member: ${member}`); |
|
continue; |
|
} |
|
|
|
const depthPromptText = baseChatReplace(character.data?.extensions?.depth_prompt?.prompt?.trim(), name1, character.name) || ''; |
|
const depthPromptDepth = character.data?.extensions?.depth_prompt?.depth ?? depth_prompt_depth_default; |
|
const depthPromptRole = character.data?.extensions?.depth_prompt?.role ?? depth_prompt_role_default; |
|
|
|
if (depthPromptText) { |
|
depthPrompts.push({ text: depthPromptText, depth: depthPromptDepth, role: depthPromptRole }); |
|
} |
|
} |
|
|
|
return depthPrompts; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function getGroupCharacterCards(groupId, characterId) { |
|
console.debug('getGroupCharacterCards entered for group: ', groupId); |
|
const group = groups.find(x => x.id === groupId); |
|
|
|
if (!group || !group?.generation_mode || !Array.isArray(group.members) || !group.members.length) { |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function customBaseChatReplace(value, fieldName, characterName, trim) { |
|
if (!value) { |
|
return ''; |
|
} |
|
|
|
|
|
value = value.replace(/<FIELDNAME>/gi, fieldName); |
|
value = trim ? value.trim() : value; |
|
return baseChatReplace(value, name1, characterName); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function replaceAndPrepareForJoin(value, characterName, fieldName, preprocess = null) { |
|
value = value.trim(); |
|
if (!value) { |
|
return ''; |
|
} |
|
|
|
|
|
if (typeof preprocess === 'function') { |
|
value = preprocess(value); |
|
} |
|
|
|
|
|
const prefix = customBaseChatReplace(group.generation_mode_join_prefix, fieldName, characterName, false); |
|
const suffix = customBaseChatReplace(group.generation_mode_join_suffix, fieldName, characterName, false); |
|
|
|
value = customBaseChatReplace(value, fieldName, characterName, true); |
|
|
|
return `${prefix}${value}${suffix}`; |
|
} |
|
|
|
const scenarioOverride = chat_metadata['scenario']; |
|
|
|
let descriptions = []; |
|
let personalities = []; |
|
let scenarios = []; |
|
let mesExamplesArray = []; |
|
|
|
for (const member of group.members) { |
|
const index = characters.findIndex(x => x.avatar === member); |
|
const character = characters[index]; |
|
|
|
if (index === -1 || !character) { |
|
console.debug(`Skipping missing member: ${member}`); |
|
continue; |
|
} |
|
|
|
if (group.disabled_members.includes(member) && characterId !== index && group.generation_mode !== group_generation_mode.APPEND_DISABLED) { |
|
console.debug(`Skipping disabled group member: ${member}`); |
|
continue; |
|
} |
|
|
|
descriptions.push(replaceAndPrepareForJoin(character.description, character.name, 'Description')); |
|
personalities.push(replaceAndPrepareForJoin(character.personality, character.name, 'Personality')); |
|
scenarios.push(replaceAndPrepareForJoin(character.scenario, character.name, 'Scenario')); |
|
mesExamplesArray.push(replaceAndPrepareForJoin(character.mes_example, character.name, 'Example Messages', (x) => !x.startsWith('<START>') ? `<START>\n${x}` : x)); |
|
} |
|
|
|
const description = descriptions.filter(x => x.length).join('\n'); |
|
const personality = personalities.filter(x => x.length).join('\n'); |
|
const scenario = scenarioOverride?.trim() || scenarios.filter(x => x.length).join('\n'); |
|
const mesExamples = mesExamplesArray.filter(x => x.length).join('\n'); |
|
|
|
return { description, personality, scenario, mesExamples }; |
|
} |
|
|
|
async function getFirstCharacterMessage(character) { |
|
let messageText = character.first_mes; |
|
|
|
|
|
if (Array.isArray(character.data?.alternate_greetings)) { |
|
const messageTexts = [character.first_mes, ...character.data.alternate_greetings].filter(x => x); |
|
messageText = messageTexts[Math.floor(Math.random() * messageTexts.length)]; |
|
} |
|
|
|
|
|
const eventArgs = { input: messageText, output: '', character: character }; |
|
await eventSource.emit(event_types.CHARACTER_FIRST_MESSAGE_SELECTED, eventArgs); |
|
if (eventArgs.output) { |
|
messageText = eventArgs.output; |
|
} |
|
|
|
const mes = {}; |
|
mes['is_user'] = false; |
|
mes['is_system'] = false; |
|
mes['name'] = character.name; |
|
mes['send_date'] = getMessageTimeStamp(); |
|
mes['original_avatar'] = character.avatar; |
|
mes['extra'] = { 'gen_id': Date.now() * Math.random() * 1000000 }; |
|
mes['mes'] = messageText |
|
? substituteParams(messageText.trim(), name1, character.name) |
|
: ''; |
|
mes['force_avatar'] = |
|
character.avatar != 'none' |
|
? getThumbnailUrl('avatar', character.avatar) |
|
: default_avatar; |
|
return mes; |
|
} |
|
|
|
function resetSelectedGroup() { |
|
selected_group = null; |
|
is_group_generating = false; |
|
} |
|
|
|
async function saveGroupChat(groupId, shouldSaveGroup) { |
|
const group = groups.find(x => x.id == groupId); |
|
const chat_id = group.chat_id; |
|
group['date_last_chat'] = Date.now(); |
|
const response = await fetch('/api/chats/group/save', { |
|
method: 'POST', |
|
headers: getRequestHeaders(), |
|
body: JSON.stringify({ id: chat_id, chat: [...chat] }), |
|
}); |
|
|
|
if (!response.ok) { |
|
toastr.error(t`Check the server connection and reload the page to prevent data loss.`, t`Group Chat could not be saved`); |
|
console.error('Group chat could not be saved', response); |
|
return; |
|
} |
|
|
|
if (shouldSaveGroup) { |
|
await editGroup(groupId, false, false); |
|
} |
|
} |
|
|
|
export async function renameGroupMember(oldAvatar, newAvatar, newName) { |
|
|
|
for (const group of groups) { |
|
try { |
|
|
|
const memberIndex = group.members.findIndex(x => x == oldAvatar); |
|
|
|
|
|
if (memberIndex == -1) { |
|
continue; |
|
} |
|
|
|
|
|
group.members[memberIndex] = newAvatar; |
|
await editGroup(group.id, true, false); |
|
console.log(`Renamed character ${newName} in group: ${group.name}`); |
|
|
|
|
|
for (const chatId of group.chats) { |
|
const messages = await loadGroupChat(chatId); |
|
|
|
|
|
let hadChanges = false; |
|
|
|
if (Array.isArray(messages) && messages.length) { |
|
|
|
for (const message of messages) { |
|
|
|
if (message.is_user || message.is_system) { |
|
continue; |
|
} |
|
|
|
|
|
|
|
if (message.force_avatar && message.force_avatar.indexOf(encodeURIComponent(oldAvatar)) !== -1) { |
|
message.name = newName; |
|
message.force_avatar = message.force_avatar.replace(encodeURIComponent(oldAvatar), encodeURIComponent(newAvatar)); |
|
message.original_avatar = newAvatar; |
|
hadChanges = true; |
|
} |
|
} |
|
|
|
if (hadChanges) { |
|
const saveChatResponse = await fetch('/api/chats/group/save', { |
|
method: 'POST', |
|
headers: getRequestHeaders(), |
|
body: JSON.stringify({ id: chatId, chat: [...messages] }), |
|
}); |
|
|
|
if (!saveChatResponse.ok) { |
|
throw new Error('Group member could not be renamed'); |
|
} |
|
|
|
console.log(`Renamed character ${newName} in group chat: ${chatId}`); |
|
} |
|
} |
|
} |
|
} |
|
catch (error) { |
|
console.log(`An error during renaming the character ${newName} in group: ${group.name}`); |
|
console.error(error); |
|
} |
|
} |
|
} |
|
|
|
async function getGroups() { |
|
const response = await fetch('/api/groups/all', { |
|
method: 'POST', |
|
headers: getRequestHeaders(), |
|
}); |
|
|
|
if (response.ok) { |
|
const data = await response.json(); |
|
groups = data.sort((a, b) => a.id - b.id); |
|
|
|
|
|
for (const group of groups) { |
|
if (typeof group.id === 'number') { |
|
group.id = String(group.id); |
|
} |
|
if (group.disabled_members == undefined) { |
|
group.disabled_members = []; |
|
} |
|
if (group.chat_id == undefined) { |
|
group.chat_id = group.id; |
|
group.chats = [group.id]; |
|
group.members = group.members |
|
.map(x => characters.find(y => y.name == x)?.avatar) |
|
.filter(x => x) |
|
.filter(onlyUnique); |
|
} |
|
if (group.past_metadata == undefined) { |
|
group.past_metadata = {}; |
|
} |
|
if (typeof group.chat_id === 'number') { |
|
group.chat_id = String(group.chat_id); |
|
} |
|
if (Array.isArray(group.chats) && group.chats.some(x => typeof x === 'number')) { |
|
group.chats = group.chats.map(x => String(x)); |
|
} |
|
} |
|
} |
|
} |
|
|
|
export function getGroupBlock(group) { |
|
let count = 0; |
|
let namesList = []; |
|
|
|
|
|
if (Array.isArray(group.members) && group.members.length) { |
|
for (const member of group.members) { |
|
const character = characters.find(x => x.avatar === member || x.name === member); |
|
if (character) { |
|
namesList.push(character.name); |
|
count++; |
|
} |
|
} |
|
} |
|
|
|
const template = $('#group_list_template .group_select').clone(); |
|
template.data('id', group.id); |
|
template.attr('data-grid', group.id); |
|
template.find('.ch_name').text(group.name).attr('title', `[Group] ${group.name}`); |
|
template.find('.group_fav_icon').css('display', 'none'); |
|
template.addClass(group.fav ? 'is_fav' : ''); |
|
template.find('.ch_fav').val(group.fav); |
|
template.find('.group_select_counter').text(count + ' ' + (count != 1 ? t`characters` : t`character`)); |
|
template.find('.group_select_block_list').text(namesList.join(', ')); |
|
|
|
|
|
const tagsElement = template.find('.tags'); |
|
printTagList(tagsElement, { forEntityOrKey: group.id, tagOptions: { isCharacterList: true } }); |
|
|
|
const avatar = getGroupAvatar(group); |
|
if (avatar) { |
|
$(template).find('.avatar').replaceWith(avatar); |
|
} |
|
|
|
return template; |
|
} |
|
|
|
function updateGroupAvatar(group) { |
|
$('#group_avatar_preview').empty().append(getGroupAvatar(group)); |
|
|
|
$('.group_select').each(function () { |
|
if ($(this).data('id') == group.id) { |
|
$(this).find('.avatar').replaceWith(getGroupAvatar(group)); |
|
} |
|
}); |
|
|
|
favsToHotswap(); |
|
} |
|
|
|
|
|
function isValidImageUrl(url) { |
|
|
|
if (Object.keys(url).length === 0) { |
|
return false; |
|
} |
|
return isDataURL(url) || (url && (url.startsWith('user') || url.startsWith('/user'))); |
|
} |
|
|
|
function getGroupAvatar(group) { |
|
if (!group) { |
|
return $(`<div class="avatar"><img src="${default_avatar}"></div>`); |
|
} |
|
|
|
if (isValidImageUrl(group.avatar_url)) { |
|
return $(`<div class="avatar" title="[Group] ${group.name}"><img src="${group.avatar_url}"></div>`); |
|
} |
|
|
|
const memberAvatars = []; |
|
if (group && Array.isArray(group.members) && group.members.length) { |
|
for (const member of group.members) { |
|
const charIndex = characters.findIndex(x => x.avatar === member); |
|
if (charIndex !== -1 && characters[charIndex].avatar !== 'none') { |
|
const avatar = getThumbnailUrl('avatar', characters[charIndex].avatar); |
|
memberAvatars.push(avatar); |
|
} |
|
if (memberAvatars.length === 4) { |
|
break; |
|
} |
|
} |
|
} |
|
|
|
const avatarCount = memberAvatars.length; |
|
|
|
if (avatarCount >= 1 && avatarCount <= 4) { |
|
const groupAvatar = $(`#group_avatars_template .collage_${avatarCount}`).clone(); |
|
|
|
for (let i = 0; i < avatarCount; i++) { |
|
groupAvatar.find(`.img_${i + 1}`).attr('src', memberAvatars[i]); |
|
} |
|
|
|
groupAvatar.attr('title', `[Group] ${group.name}`); |
|
return groupAvatar; |
|
} |
|
|
|
|
|
if (avatarCount === 0) { |
|
return $('<div class="missing-avatar fa-solid fa-user-slash"></div>'); |
|
} |
|
|
|
|
|
const groupAvatar = $('#group_avatars_template .collage_1').clone(); |
|
groupAvatar.find('.img_1').attr('src', group.avatar_url || system_avatar); |
|
groupAvatar.attr('title', `[Group] ${group.name}`); |
|
return groupAvatar; |
|
} |
|
|
|
function getGroupChatNames(groupId) { |
|
const group = groups.find(x => x.id === groupId); |
|
|
|
if (!group) { |
|
return []; |
|
} |
|
|
|
const names = []; |
|
for (const chatId of group.chats) { |
|
names.push(chatId); |
|
} |
|
return names; |
|
} |
|
|
|
async function generateGroupWrapper(by_auto_mode, type = null, params = {}) { |
|
function throwIfAborted() { |
|
if (params.signal instanceof AbortSignal && params.signal.aborted) { |
|
throw new Error('AbortSignal was fired. Group generation stopped'); |
|
} |
|
} |
|
|
|
if (online_status === 'no_connection') { |
|
is_group_generating = false; |
|
setSendButtonState(false); |
|
return Promise.resolve(); |
|
} |
|
|
|
if (is_group_generating) { |
|
return Promise.resolve(); |
|
} |
|
|
|
|
|
if (menu_type !== 'group_edit') { |
|
select_group_chats(selected_group); |
|
await delay(1); |
|
} |
|
|
|
|
|
let textResult = ''; |
|
const group = groups.find((x) => x.id === selected_group); |
|
|
|
if (!group || !Array.isArray(group.members) || !group.members.length) { |
|
sendSystemMessage(system_message_types.EMPTY, '', { isSmallSys: true }); |
|
return Promise.resolve(); |
|
} |
|
|
|
try { |
|
await unshallowGroupMembers(selected_group); |
|
|
|
throwIfAborted(); |
|
hideSwipeButtons(); |
|
is_group_generating = true; |
|
setCharacterName(''); |
|
setCharacterId(undefined); |
|
const userInput = String($('#send_textarea').val()); |
|
|
|
|
|
group_generation_id = Date.now(); |
|
const lastMessage = chat[chat.length - 1]; |
|
let activationText = ''; |
|
let isUserInput = false; |
|
|
|
if (userInput?.length && !by_auto_mode) { |
|
isUserInput = true; |
|
activationText = userInput; |
|
} else { |
|
if (lastMessage && !lastMessage.is_system) { |
|
activationText = lastMessage.mes; |
|
} |
|
} |
|
|
|
const activationStrategy = Number(group.activation_strategy ?? group_activation_strategy.NATURAL); |
|
const enabledMembers = group.members.filter(x => !group.disabled_members.includes(x)); |
|
let activatedMembers = []; |
|
|
|
if (params && typeof params.force_chid == 'number') { |
|
activatedMembers = [params.force_chid]; |
|
} else if (type === 'quiet') { |
|
activatedMembers = activateSwipe(group.members); |
|
|
|
if (activatedMembers.length === 0) { |
|
activatedMembers = activateListOrder(group.members.slice(0, 1)); |
|
} |
|
} |
|
else if (type === 'swipe' || type === 'continue') { |
|
activatedMembers = activateSwipe(group.members); |
|
|
|
if (activatedMembers.length === 0) { |
|
toastr.warning(t`Deleted group member swiped. To get a reply, add them back to the group.`); |
|
throw new Error('Deleted group member swiped'); |
|
} |
|
} |
|
else if (type === 'impersonate') { |
|
activatedMembers = activateImpersonate(group.members); |
|
} |
|
else if (activationStrategy === group_activation_strategy.NATURAL) { |
|
activatedMembers = activateNaturalOrder(enabledMembers, activationText, lastMessage, group.allow_self_responses, isUserInput); |
|
} |
|
else if (activationStrategy === group_activation_strategy.LIST) { |
|
activatedMembers = activateListOrder(enabledMembers); |
|
} |
|
else if (activationStrategy === group_activation_strategy.POOLED) { |
|
activatedMembers = activatePooledOrder(enabledMembers, lastMessage, isUserInput); |
|
} |
|
else if (activationStrategy === group_activation_strategy.MANUAL && !isUserInput) { |
|
activatedMembers = shuffle(enabledMembers).slice(0, 1).map(x => characters.findIndex(y => y.avatar === x)).filter(x => x !== -1); |
|
} |
|
|
|
if (activatedMembers.length === 0) { |
|
|
|
|
|
|
|
const bias = getBiasStrings(userInput, type); |
|
await sendMessageAsUser(userInput, bias.messageBias); |
|
await saveChatConditional(); |
|
$('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles: true })); |
|
} |
|
groupChatQueueOrder = new Map(); |
|
|
|
if (power_user.show_group_chat_queue) { |
|
for (let i = 0; i < activatedMembers.length; ++i) { |
|
groupChatQueueOrder.set(characters[activatedMembers[i]].avatar, i + 1); |
|
} |
|
} |
|
await eventSource.emit(event_types.GROUP_WRAPPER_STARTED, { selected_group, type }); |
|
|
|
for (const chId of activatedMembers) { |
|
throwIfAborted(); |
|
deactivateSendButtons(); |
|
const generateType = type == 'swipe' || type == 'impersonate' || type == 'quiet' || type == 'continue' ? type : 'group_chat'; |
|
setCharacterId(chId); |
|
setCharacterName(characters[chId].name); |
|
if (power_user.show_group_chat_queue) { |
|
printGroupMembers(); |
|
} |
|
await eventSource.emit(event_types.GROUP_MEMBER_DRAFTED, chId); |
|
|
|
|
|
textResult = await Generate(generateType, { automatic_trigger: by_auto_mode, ...(params || {}) }); |
|
let messageChunk = textResult?.messageChunk; |
|
|
|
if (messageChunk) { |
|
while (shouldAutoContinue(messageChunk, type === 'impersonate')) { |
|
textResult = await Generate('continue', { automatic_trigger: by_auto_mode, ...(params || {}) }); |
|
messageChunk = textResult?.messageChunk; |
|
} |
|
} |
|
if (power_user.show_group_chat_queue) { |
|
groupChatQueueOrder.delete(characters[chId].avatar); |
|
groupChatQueueOrder.forEach((value, key, map) => map.set(key, value - 1)); |
|
} |
|
} |
|
} finally { |
|
is_group_generating = false; |
|
setSendButtonState(false); |
|
setCharacterId(undefined); |
|
if (power_user.show_group_chat_queue) { |
|
groupChatQueueOrder = new Map(); |
|
printGroupMembers(); |
|
} |
|
setCharacterName(''); |
|
activateSendButtons(); |
|
showSwipeButtons(); |
|
await eventSource.emit(event_types.GROUP_WRAPPER_FINISHED, { selected_group, type }); |
|
} |
|
|
|
return Promise.resolve(textResult); |
|
} |
|
|
|
function getLastMessageGenerationId() { |
|
let generationId = null; |
|
if (chat.length > 0) { |
|
const lastMes = chat[chat.length - 1]; |
|
if (!lastMes.is_user && !lastMes.is_system && lastMes.extra) { |
|
generationId = lastMes.extra.gen_id; |
|
} |
|
} |
|
return generationId; |
|
} |
|
|
|
function activateImpersonate(members) { |
|
const randomIndex = Math.floor(Math.random() * members.length); |
|
const activatedMembers = [members[randomIndex]]; |
|
const memberIds = activatedMembers |
|
.map((x) => characters.findIndex((y) => y.avatar === x)) |
|
.filter((x) => x !== -1); |
|
return memberIds; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function activateSwipe(members) { |
|
let activatedNames = []; |
|
const lastMessage = chat[chat.length - 1]; |
|
|
|
if (lastMessage.is_user || lastMessage.is_system || lastMessage.extra?.type === system_message_types.NARRATOR) { |
|
for (const message of chat.slice().reverse()) { |
|
if (message.is_user || message.is_system || message.extra?.type === system_message_types.NARRATOR) { |
|
continue; |
|
} |
|
|
|
if (message.original_avatar) { |
|
activatedNames.push(message.original_avatar); |
|
break; |
|
} |
|
} |
|
|
|
if (activatedNames.length === 0) { |
|
activatedNames.push(shuffle(members.slice())[0]); |
|
} |
|
} |
|
|
|
|
|
if (!lastMessage.original_avatar) { |
|
const matches = characters.filter(x => x.name == lastMessage.name); |
|
|
|
for (const match of matches) { |
|
if (members.includes(match.avatar)) { |
|
activatedNames.push(match.avatar); |
|
break; |
|
} |
|
} |
|
} |
|
else { |
|
activatedNames.push(lastMessage.original_avatar); |
|
} |
|
|
|
const memberIds = activatedNames |
|
.map((x) => characters.findIndex((y) => y.avatar === x)) |
|
.filter((x) => x !== -1); |
|
return memberIds; |
|
} |
|
|
|
function activateListOrder(members) { |
|
let activatedMembers = members.filter(onlyUnique); |
|
|
|
|
|
const memberIds = activatedMembers |
|
.map((x) => characters.findIndex((y) => y.avatar === x)) |
|
.filter((x) => x !== -1); |
|
return memberIds; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function activatePooledOrder(members, lastMessage, isUserInput) { |
|
|
|
let activatedMember = null; |
|
|
|
const spokenSinceUser = []; |
|
|
|
for (const message of chat.slice().reverse()) { |
|
if (message.is_user || isUserInput) { |
|
break; |
|
} |
|
|
|
if (message.is_system || message.extra?.type === system_message_types.NARRATOR) { |
|
continue; |
|
} |
|
|
|
if (message.original_avatar) { |
|
spokenSinceUser.push(message.original_avatar); |
|
} |
|
} |
|
|
|
const haveNotSpoken = members.filter(x => !spokenSinceUser.includes(x)); |
|
|
|
if (haveNotSpoken.length) { |
|
activatedMember = haveNotSpoken[Math.floor(Math.random() * haveNotSpoken.length)]; |
|
} |
|
|
|
if (activatedMember === null) { |
|
const lastMessageAvatar = members.length > 1 && lastMessage && !lastMessage.is_user && lastMessage.original_avatar; |
|
const randomPool = lastMessageAvatar ? members.filter(x => x !== lastMessage.original_avatar) : members; |
|
activatedMember = randomPool[Math.floor(Math.random() * randomPool.length)]; |
|
} |
|
|
|
const memberId = characters.findIndex(y => y.avatar === activatedMember); |
|
return memberId !== -1 ? [memberId] : []; |
|
} |
|
|
|
function activateNaturalOrder(members, input, lastMessage, allowSelfResponses, isUserInput) { |
|
let activatedMembers = []; |
|
|
|
|
|
let bannedUser = !isUserInput && lastMessage && !lastMessage.is_user && lastMessage.name; |
|
|
|
|
|
if (allowSelfResponses) { |
|
bannedUser = undefined; |
|
} |
|
|
|
|
|
if (input && input.length) { |
|
for (let inputWord of extractAllWords(input)) { |
|
for (let member of members) { |
|
const character = characters.find(x => x.avatar === member); |
|
|
|
if (!character || character.name === bannedUser) { |
|
continue; |
|
} |
|
|
|
if (extractAllWords(character.name).includes(inputWord)) { |
|
activatedMembers.push(member); |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
const chattyMembers = []; |
|
|
|
const shuffledMembers = shuffle([...members]); |
|
for (let member of shuffledMembers) { |
|
const character = characters.find((x) => x.avatar === member); |
|
|
|
if (!character || character.name === bannedUser) { |
|
continue; |
|
} |
|
|
|
const rollValue = Math.random(); |
|
const talkativeness = isNaN(character.talkativeness) |
|
? talkativeness_default |
|
: Number(character.talkativeness); |
|
if (talkativeness >= rollValue) { |
|
activatedMembers.push(member); |
|
} |
|
if (talkativeness > 0) { |
|
chattyMembers.push(member); |
|
} |
|
} |
|
|
|
|
|
let retries = 0; |
|
|
|
const randomPool = chattyMembers.length > 0 ? chattyMembers : members; |
|
while (activatedMembers.length === 0 && ++retries <= randomPool.length) { |
|
const randomIndex = Math.floor(Math.random() * randomPool.length); |
|
const character = characters.find((x) => x.avatar === randomPool[randomIndex]); |
|
|
|
if (!character) { |
|
continue; |
|
} |
|
|
|
activatedMembers.push(randomPool[randomIndex]); |
|
} |
|
|
|
|
|
activatedMembers = activatedMembers.filter(onlyUnique); |
|
|
|
|
|
const memberIds = activatedMembers |
|
.map((x) => characters.findIndex((y) => y.avatar === x)) |
|
.filter((x) => x !== -1); |
|
return memberIds; |
|
} |
|
|
|
async function deleteGroup(id) { |
|
const group = groups.find((x) => x.id === id); |
|
|
|
const response = await fetch('/api/groups/delete', { |
|
method: 'POST', |
|
headers: getRequestHeaders(), |
|
body: JSON.stringify({ id: id }), |
|
}); |
|
|
|
if (group && Array.isArray(group.chats)) { |
|
for (const chatId of group.chats) { |
|
await eventSource.emit(event_types.GROUP_CHAT_DELETED, chatId); |
|
} |
|
} |
|
|
|
if (response.ok) { |
|
await clearChat(); |
|
selected_group = null; |
|
delete tag_map[id]; |
|
resetChatState(); |
|
await printMessages(); |
|
await getCharacters(); |
|
|
|
select_rm_info('group_delete', id); |
|
|
|
$('#rm_button_selected_ch').children('h2').text(''); |
|
} |
|
} |
|
|
|
export async function editGroup(id, immediately, reload = true) { |
|
let group = groups.find((x) => x.id === id); |
|
|
|
if (!group) { |
|
return; |
|
} |
|
|
|
if (id === selected_group) { |
|
group['chat_metadata'] = structuredClone(chat_metadata); |
|
} |
|
|
|
if (immediately) { |
|
return await _save(group, reload); |
|
} |
|
|
|
saveGroupDebounced(group, reload); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
export async function unshallowGroupMembers(groupId) { |
|
const group = groups.find(x => x.id == groupId); |
|
if (!group) { |
|
return; |
|
} |
|
const members = group.members; |
|
if (!Array.isArray(members)) { |
|
return; |
|
} |
|
for (const member of members) { |
|
const index = characters.findIndex(x => x.avatar === member); |
|
if (index === -1) { |
|
continue; |
|
} |
|
await unshallowCharacter(String(index)); |
|
} |
|
} |
|
|
|
let groupAutoModeAbortController = null; |
|
|
|
async function groupChatAutoModeWorker() { |
|
if (!is_group_automode_enabled || online_status === 'no_connection') { |
|
return; |
|
} |
|
|
|
if (!selected_group || is_send_press || is_group_generating) { |
|
return; |
|
} |
|
|
|
const group = groups.find((x) => x.id === selected_group); |
|
|
|
if (!group || !Array.isArray(group.members) || !group.members.length) { |
|
return; |
|
} |
|
|
|
groupAutoModeAbortController = new AbortController(); |
|
await generateGroupWrapper(true, 'auto', { signal: groupAutoModeAbortController.signal }); |
|
} |
|
|
|
async function modifyGroupMember(groupId, groupMember, isDelete) { |
|
const id = groupMember.data('id'); |
|
const thisGroup = groups.find((x) => x.id == groupId); |
|
const membersArray = thisGroup?.members ?? newGroupMembers; |
|
|
|
if (isDelete) { |
|
const index = membersArray.findIndex((x) => x === id); |
|
if (index !== -1) { |
|
membersArray.splice(membersArray.indexOf(id), 1); |
|
} |
|
} else { |
|
membersArray.unshift(id); |
|
} |
|
|
|
if (openGroupId) { |
|
await unshallowGroupMembers(openGroupId); |
|
await editGroup(openGroupId, false, false); |
|
updateGroupAvatar(thisGroup); |
|
} |
|
|
|
printGroupCandidates(); |
|
printGroupMembers(); |
|
|
|
const groupHasMembers = getGroupCharacters({ doFilter: false, onlyMembers: true }).length > 0; |
|
$('#rm_group_submit').prop('disabled', !groupHasMembers); |
|
} |
|
|
|
async function reorderGroupMember(chat_id, groupMember, direction) { |
|
const id = groupMember.data('id'); |
|
const thisGroup = groups.find((x) => x.id == chat_id); |
|
const memberArray = thisGroup?.members ?? newGroupMembers; |
|
|
|
const indexOf = memberArray.indexOf(id); |
|
if (direction == 'down') { |
|
const next = memberArray[indexOf + 1]; |
|
if (next) { |
|
memberArray[indexOf + 1] = memberArray[indexOf]; |
|
memberArray[indexOf] = next; |
|
} |
|
} |
|
if (direction == 'up') { |
|
const prev = memberArray[indexOf - 1]; |
|
if (prev) { |
|
memberArray[indexOf - 1] = memberArray[indexOf]; |
|
memberArray[indexOf] = prev; |
|
} |
|
} |
|
|
|
printGroupMembers(); |
|
|
|
|
|
if (openGroupId) { |
|
await editGroup(chat_id, false, false); |
|
updateGroupAvatar(thisGroup); |
|
} |
|
} |
|
|
|
async function onGroupActivationStrategyInput(e) { |
|
if (openGroupId) { |
|
let _thisGroup = groups.find((x) => x.id == openGroupId); |
|
_thisGroup.activation_strategy = Number(e.target.value); |
|
await editGroup(openGroupId, false, false); |
|
} |
|
} |
|
|
|
async function onGroupGenerationModeInput(e) { |
|
if (openGroupId) { |
|
let _thisGroup = groups.find((x) => x.id == openGroupId); |
|
_thisGroup.generation_mode = Number(e.target.value); |
|
await editGroup(openGroupId, false, false); |
|
|
|
toggleHiddenControls(_thisGroup); |
|
} |
|
} |
|
|
|
async function onGroupAutoModeDelayInput(e) { |
|
if (openGroupId) { |
|
let _thisGroup = groups.find((x) => x.id == openGroupId); |
|
_thisGroup.auto_mode_delay = Number(e.target.value); |
|
await editGroup(openGroupId, false, false); |
|
setAutoModeWorker(); |
|
} |
|
} |
|
|
|
async function onGroupGenerationModeTemplateInput(e) { |
|
if (openGroupId) { |
|
let _thisGroup = groups.find((x) => x.id == openGroupId); |
|
const prop = $(e.target).attr('setting'); |
|
_thisGroup[prop] = String(e.target.value); |
|
await editGroup(openGroupId, false, false); |
|
} |
|
} |
|
|
|
async function onGroupNameInput() { |
|
if (openGroupId) { |
|
let _thisGroup = groups.find((x) => x.id == openGroupId); |
|
_thisGroup.name = $(this).val(); |
|
$('#rm_button_selected_ch').children('h2').text(_thisGroup.name); |
|
await editGroup(openGroupId); |
|
} |
|
} |
|
|
|
function isGroupMember(group, avatarId) { |
|
if (group && Array.isArray(group.members)) { |
|
return group.members.includes(avatarId); |
|
} else { |
|
return newGroupMembers.includes(avatarId); |
|
} |
|
} |
|
|
|
function getGroupCharacters({ doFilter, onlyMembers } = {}) { |
|
function sortMembersFn(a, b) { |
|
const membersArray = thisGroup?.members ?? newGroupMembers; |
|
const aIndex = membersArray.indexOf(a.item.avatar); |
|
const bIndex = membersArray.indexOf(b.item.avatar); |
|
return aIndex - bIndex; |
|
} |
|
|
|
const thisGroup = openGroupId && groups.find((x) => x.id == openGroupId); |
|
let candidates = characters |
|
.filter((x) => isGroupMember(thisGroup, x.avatar) == onlyMembers) |
|
.map((x) => ({ item: x, id: characters.indexOf(x), type: 'character' })); |
|
|
|
if (doFilter) { |
|
candidates = groupCandidatesFilter.applyFilters(candidates); |
|
} |
|
|
|
if (onlyMembers) { |
|
candidates.sort(sortMembersFn); |
|
} else { |
|
const useFilterOrder = doFilter && !!$('#rm_group_filter').val(); |
|
sortEntitiesList(candidates, useFilterOrder, groupCandidatesFilter); |
|
} |
|
|
|
groupCandidatesFilter.clearFuzzySearchCaches(); |
|
return candidates; |
|
} |
|
|
|
function printGroupCandidates() { |
|
const storageKey = 'GroupCandidates_PerPage'; |
|
const pageSize = Number(accountStorage.getItem(storageKey)) || 5; |
|
const sizeChangerOptions = [5, 10, 25, 50, 100, 200, 500, 1000]; |
|
$('#rm_group_add_members_pagination').pagination({ |
|
dataSource: getGroupCharacters({ doFilter: true, onlyMembers: false }), |
|
pageRange: 1, |
|
position: 'top', |
|
showPageNumbers: false, |
|
prevText: '<', |
|
nextText: '>', |
|
formatNavigator: PAGINATION_TEMPLATE, |
|
formatSizeChanger: renderPaginationDropdown(pageSize, sizeChangerOptions), |
|
showNavigator: true, |
|
showSizeChanger: true, |
|
pageSize, |
|
afterSizeSelectorChange: function (e, size) { |
|
accountStorage.setItem(storageKey, e.target.value); |
|
paginationDropdownChangeHandler(e, size); |
|
}, |
|
callback: function (data) { |
|
$('#rm_group_add_members').empty(); |
|
for (const i of data) { |
|
$('#rm_group_add_members').append(getGroupCharacterBlock(i.item)); |
|
} |
|
localizePagination($('#rm_group_add_members_pagination')); |
|
}, |
|
}); |
|
} |
|
|
|
function printGroupMembers() { |
|
const storageKey = 'GroupMembers_PerPage'; |
|
$('.rm_group_members_pagination').each(function () { |
|
let that = this; |
|
const pageSize = Number(accountStorage.getItem(storageKey)) || 5; |
|
const sizeChangerOptions = [5, 10, 25, 50, 100, 200, 500, 1000]; |
|
$(this).pagination({ |
|
dataSource: getGroupCharacters({ doFilter: false, onlyMembers: true }), |
|
pageRange: 1, |
|
position: 'top', |
|
showPageNumbers: false, |
|
prevText: '<', |
|
nextText: '>', |
|
formatNavigator: PAGINATION_TEMPLATE, |
|
showNavigator: true, |
|
showSizeChanger: true, |
|
formatSizeChanger: renderPaginationDropdown(pageSize, sizeChangerOptions), |
|
pageSize, |
|
afterSizeSelectorChange: function (e, size) { |
|
accountStorage.setItem(storageKey, e.target.value); |
|
paginationDropdownChangeHandler(e, size); |
|
}, |
|
callback: function (data) { |
|
$('.rm_group_members').empty(); |
|
for (const i of data) { |
|
$('.rm_group_members').append(getGroupCharacterBlock(i.item)); |
|
} |
|
localizePagination($(that)); |
|
}, |
|
}); |
|
}); |
|
} |
|
|
|
function getGroupCharacterBlock(character) { |
|
const avatar = getThumbnailUrl('avatar', character.avatar); |
|
const template = $('#group_member_template .group_member').clone(); |
|
const isFav = character.fav || character.fav == 'true'; |
|
template.data('id', character.avatar); |
|
template.find('.avatar img').attr({ 'src': avatar, 'title': character.avatar }); |
|
template.find('.ch_name').text(character.name); |
|
template.attr('data-chid', characters.indexOf(character)); |
|
template.find('.ch_fav').val(isFav); |
|
template.toggleClass('is_fav', isFav); |
|
|
|
const auxFieldName = power_user.aux_field || 'character_version'; |
|
const auxFieldValue = (character.data && character.data[auxFieldName]) || ''; |
|
if (auxFieldValue) { |
|
template.find('.character_version').text(auxFieldValue); |
|
} |
|
else { |
|
template.find('.character_version').hide(); |
|
} |
|
|
|
let queuePosition = groupChatQueueOrder.get(character.avatar); |
|
if (queuePosition) { |
|
template.find('.queue_position').text(queuePosition); |
|
template.toggleClass('is_queued', queuePosition > 1); |
|
template.toggleClass('is_active', queuePosition === 1); |
|
} |
|
|
|
template.toggleClass('disabled', isGroupMemberDisabled(character.avatar)); |
|
|
|
|
|
const tagsElement = template.find('.tags'); |
|
printTagList(tagsElement, { forEntityOrKey: characters.indexOf(character), tagOptions: { isCharacterList: true } }); |
|
|
|
if (!openGroupId) { |
|
template.find('[data-action="speak"]').hide(); |
|
template.find('[data-action="enable"]').hide(); |
|
template.find('[data-action="disable"]').hide(); |
|
} |
|
|
|
return template; |
|
} |
|
|
|
function isGroupMemberDisabled(avatarId) { |
|
const thisGroup = openGroupId && groups.find((x) => x.id == openGroupId); |
|
return Boolean(thisGroup && thisGroup.disabled_members.includes(avatarId)); |
|
} |
|
|
|
async function onDeleteGroupClick() { |
|
if (!openGroupId) { |
|
toastr.warning(t`Currently no group selected.`); |
|
return; |
|
} |
|
if (is_group_generating) { |
|
toastr.warning(t`Not so fast! Wait for the characters to stop typing before deleting the group.`); |
|
return; |
|
} |
|
|
|
const confirm = await Popup.show.confirm(t`Delete the group?`, '<p>' + t`This will also delete all your chats with that group. If you want to delete a single conversation, select a "View past chats" option in the lower left menu.` + '</p>'); |
|
if (confirm) { |
|
deleteGroup(openGroupId); |
|
} |
|
} |
|
|
|
async function onFavoriteGroupClick() { |
|
updateFavButtonState(!fav_grp_checked); |
|
if (openGroupId) { |
|
let _thisGroup = groups.find((x) => x.id == openGroupId); |
|
_thisGroup.fav = fav_grp_checked; |
|
await editGroup(openGroupId, false, false); |
|
favsToHotswap(); |
|
} |
|
} |
|
|
|
async function onGroupSelfResponsesClick() { |
|
if (openGroupId) { |
|
let _thisGroup = groups.find((x) => x.id == openGroupId); |
|
const value = $(this).prop('checked'); |
|
_thisGroup.allow_self_responses = value; |
|
await editGroup(openGroupId, false, false); |
|
} |
|
} |
|
|
|
async function onHideMutedSpritesClick(value) { |
|
if (openGroupId) { |
|
let _thisGroup = groups.find((x) => x.id == openGroupId); |
|
_thisGroup.hideMutedSprites = value; |
|
console.log(`_thisGroup.hideMutedSprites = ${_thisGroup.hideMutedSprites}`); |
|
await editGroup(openGroupId, false, false); |
|
await eventSource.emit(event_types.GROUP_UPDATED); |
|
} |
|
} |
|
|
|
function toggleHiddenControls(group, generationMode = null) { |
|
const isJoin = [group_generation_mode.APPEND, group_generation_mode.APPEND_DISABLED].includes(generationMode ?? group?.generation_mode); |
|
$('#rm_group_generation_mode_join_prefix').parent().toggle(isJoin); |
|
$('#rm_group_generation_mode_join_suffix').parent().toggle(isJoin); |
|
|
|
if (!CSS.supports('field-sizing', 'content')) { |
|
initScrollHeight($('#rm_group_generation_mode_join_prefix')); |
|
initScrollHeight($('#rm_group_generation_mode_join_suffix')); |
|
} |
|
} |
|
|
|
function select_group_chats(groupId, skipAnimation) { |
|
openGroupId = groupId; |
|
newGroupMembers = []; |
|
const group = openGroupId && groups.find((x) => x.id == openGroupId); |
|
const groupName = group?.name ?? ''; |
|
const replyStrategy = Number(group?.activation_strategy ?? group_activation_strategy.NATURAL); |
|
const generationMode = Number(group?.generation_mode ?? group_generation_mode.SWAP); |
|
|
|
setMenuType(group ? 'group_edit' : 'group_create'); |
|
$('#group_avatar_preview').empty().append(getGroupAvatar(group)); |
|
$('#rm_group_restore_avatar').toggle(!!group && isValidImageUrl(group.avatar_url)); |
|
$('#rm_group_filter').val('').trigger('input'); |
|
$('#rm_group_activation_strategy').val(replyStrategy); |
|
$(`#rm_group_activation_strategy option[value="${replyStrategy}"]`).prop('selected', true); |
|
$('#rm_group_generation_mode').val(generationMode); |
|
$(`#rm_group_generation_mode option[value="${generationMode}"]`).prop('selected', true); |
|
$('#rm_group_chat_name').val(groupName); |
|
|
|
if (!skipAnimation) { |
|
selectRightMenuWithAnimation('rm_group_chats_block'); |
|
} |
|
|
|
|
|
applyTagsOnGroupSelect(groupId); |
|
|
|
|
|
printGroupCandidates(); |
|
printGroupMembers(); |
|
|
|
const groupHasMembers = !!$('#rm_group_members').children().length; |
|
$('#rm_group_submit').prop('disabled', !groupHasMembers); |
|
$('#rm_group_allow_self_responses').prop('checked', group && group.allow_self_responses); |
|
$('#rm_group_hidemutedsprites').prop('checked', group && group.hideMutedSprites); |
|
$('#rm_group_automode_delay').val(group?.auto_mode_delay ?? DEFAULT_AUTO_MODE_DELAY); |
|
|
|
$('#rm_group_generation_mode_join_prefix').val(group?.generation_mode_join_prefix ?? '').attr('setting', 'generation_mode_join_prefix'); |
|
$('#rm_group_generation_mode_join_suffix').val(group?.generation_mode_join_suffix ?? '').attr('setting', 'generation_mode_join_suffix'); |
|
toggleHiddenControls(group, generationMode); |
|
|
|
|
|
if (openGroupId) { |
|
$('#rm_group_submit').hide(); |
|
$('#rm_group_delete').show(); |
|
$('#rm_group_scenario').show(); |
|
$('#group-metadata-controls .chat_lorebook_button').removeClass('disabled').prop('disabled', false); |
|
$('#group_open_media_overrides').show(); |
|
const isMediaAllowed = isExternalMediaAllowed(); |
|
$('#group_media_allowed_icon').toggle(isMediaAllowed); |
|
$('#group_media_forbidden_icon').toggle(!isMediaAllowed); |
|
} else { |
|
$('#rm_group_submit').show(); |
|
if ($('#groupAddMemberListToggle .inline-drawer-content').css('display') !== 'block') { |
|
$('#groupAddMemberListToggle').trigger('click'); |
|
} |
|
$('#rm_group_delete').hide(); |
|
$('#rm_group_scenario').hide(); |
|
$('#group-metadata-controls .chat_lorebook_button').addClass('disabled').prop('disabled', true); |
|
$('#group_open_media_overrides').hide(); |
|
} |
|
|
|
updateFavButtonState(group?.fav ?? false); |
|
setAutoModeWorker(); |
|
|
|
|
|
if (group) { |
|
$('#rm_group_automode_label').show(); |
|
$('#rm_button_selected_ch').children('h2').text(groupName); |
|
} |
|
else { |
|
$('#rm_group_automode_label').hide(); |
|
} |
|
|
|
|
|
if (!CSS.supports('field-sizing', 'content')) { |
|
$('#rm_group_chats_block .autoSetHeight').each(element => { |
|
resetScrollHeight(element); |
|
}); |
|
} |
|
|
|
hideMutedSprites = group?.hideMutedSprites ?? false; |
|
$('#rm_group_hidemutedsprites').prop('checked', hideMutedSprites); |
|
|
|
eventSource.emit('groupSelected', { detail: { id: openGroupId, group: group } }); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function uploadGroupAvatar(event) { |
|
if (!(event.target instanceof HTMLInputElement) || !event.target.files.length) { |
|
return; |
|
} |
|
|
|
const file = event.target.files[0]; |
|
|
|
if (!file) { |
|
return; |
|
} |
|
|
|
const result = await getBase64Async(file); |
|
|
|
$('#dialogue_popup').addClass('large_dialogue_popup wide_dialogue_popup'); |
|
|
|
const croppedImage = await callGenericPopup('Set the crop position of the avatar image', POPUP_TYPE.CROP, '', { cropImage: result }); |
|
|
|
if (!croppedImage) { |
|
return; |
|
} |
|
|
|
let thumbnail = await createThumbnail(String(croppedImage), 200, 300); |
|
|
|
thumbnail = thumbnail.replace(/^data:image\/[a-z]+;base64,/, ''); |
|
let _thisGroup = groups.find((x) => x.id == openGroupId); |
|
|
|
const filename = _thisGroup ? `${_thisGroup.id}_${humanizedDateTime()}` : humanizedDateTime(); |
|
let thumbnailUrl = await saveBase64AsFile(thumbnail, String(openGroupId ?? ''), filename, 'jpg'); |
|
if (!openGroupId) { |
|
$('#group_avatar_preview img').attr('src', thumbnailUrl); |
|
$('#rm_group_restore_avatar').show(); |
|
return; |
|
} |
|
|
|
_thisGroup.avatar_url = thumbnailUrl; |
|
$('#group_avatar_preview').empty().append(getGroupAvatar(_thisGroup)); |
|
$('#rm_group_restore_avatar').show(); |
|
await editGroup(openGroupId, true, true); |
|
} |
|
|
|
async function restoreGroupAvatar() { |
|
const confirm = await Popup.show.confirm('Are you sure you want to restore the group avatar?', 'Your custom image will be deleted, and a collage will be used instead.'); |
|
if (!confirm) { |
|
return; |
|
} |
|
|
|
if (!openGroupId) { |
|
$('#group_avatar_preview img').attr('src', default_avatar); |
|
$('#rm_group_restore_avatar').hide(); |
|
return; |
|
} |
|
|
|
let _thisGroup = groups.find((x) => x.id == openGroupId); |
|
_thisGroup.avatar_url = ''; |
|
$('#group_avatar_preview').empty().append(getGroupAvatar(_thisGroup)); |
|
$('#rm_group_restore_avatar').hide(); |
|
await editGroup(openGroupId, true, true); |
|
} |
|
|
|
async function onGroupActionClick(event) { |
|
event.stopPropagation(); |
|
const action = $(this).data('action'); |
|
const member = $(this).closest('.group_member'); |
|
|
|
if (action === 'remove') { |
|
await modifyGroupMember(openGroupId, member, true); |
|
} |
|
|
|
if (action === 'add') { |
|
await modifyGroupMember(openGroupId, member, false); |
|
} |
|
|
|
if (action === 'enable') { |
|
member.removeClass('disabled'); |
|
const _thisGroup = groups.find(x => x.id === openGroupId); |
|
const index = _thisGroup.disabled_members.indexOf(member.data('id')); |
|
if (index !== -1) { |
|
_thisGroup.disabled_members.splice(index, 1); |
|
await editGroup(openGroupId, false, false); |
|
} |
|
} |
|
|
|
if (action === 'disable') { |
|
member.addClass('disabled'); |
|
const _thisGroup = groups.find(x => x.id === openGroupId); |
|
if (!_thisGroup.disabled_members.includes(member.data('id'))) { |
|
_thisGroup.disabled_members.push(member.data('id')); |
|
await editGroup(openGroupId, false, false); |
|
} |
|
} |
|
|
|
if (action === 'up' || action === 'down') { |
|
await reorderGroupMember(openGroupId, member, action); |
|
} |
|
|
|
if (action === 'view') { |
|
await openCharacterDefinition(member); |
|
} |
|
|
|
if (action === 'speak') { |
|
const chid = Number(member.attr('data-chid')); |
|
if (Number.isInteger(chid)) { |
|
Generate('normal', { force_chid: chid }); |
|
} |
|
} |
|
|
|
await eventSource.emit(event_types.GROUP_UPDATED); |
|
} |
|
|
|
function updateFavButtonState(state) { |
|
fav_grp_checked = state; |
|
$('#rm_group_fav').val(fav_grp_checked); |
|
$('#group_favorite_button').toggleClass('fav_on', fav_grp_checked); |
|
$('#group_favorite_button').toggleClass('fav_off', !fav_grp_checked); |
|
} |
|
|
|
export async function openGroupById(groupId) { |
|
if (isChatSaving) { |
|
toastr.info(t`Please wait until the chat is saved before switching characters.`, t`Your chat is still saving...`); |
|
return false; |
|
} |
|
|
|
if (!groups.find(x => x.id === groupId)) { |
|
console.log('Group not found', groupId); |
|
return false; |
|
} |
|
|
|
if (!is_send_press && !is_group_generating) { |
|
select_group_chats(groupId); |
|
|
|
if (selected_group !== groupId) { |
|
groupChatQueueOrder = new Map(); |
|
await clearChat(); |
|
cancelTtsPlay(); |
|
selected_group = groupId; |
|
setCharacterId(undefined); |
|
setCharacterName(''); |
|
setEditedMessageId(undefined); |
|
updateChatMetadata({}, true); |
|
chat.length = 0; |
|
await getGroupChat(groupId); |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
async function openCharacterDefinition(characterSelect) { |
|
if (is_group_generating) { |
|
toastr.warning(t`Can't peek a character while group reply is being generated`); |
|
console.warn('Can\'t peek a character def while group reply is being generated'); |
|
return; |
|
} |
|
|
|
const chid = characterSelect.attr('data-chid'); |
|
|
|
if (chid === null || chid === undefined) { |
|
return; |
|
} |
|
|
|
await unshallowCharacter(chid); |
|
setCharacterId(chid); |
|
select_selected_character(chid); |
|
|
|
RA_CountCharTokens(); |
|
|
|
applyTagsOnCharacterSelect.call(characterSelect); |
|
} |
|
|
|
function filterGroupMembers() { |
|
const searchValue = String($(this).val()).toLowerCase(); |
|
groupCandidatesFilter.setFilterData(FILTER_TYPES.SEARCH, searchValue); |
|
} |
|
|
|
async function createGroup() { |
|
let name = $('#rm_group_chat_name').val(); |
|
let allowSelfResponses = !!$('#rm_group_allow_self_responses').prop('checked'); |
|
let activationStrategy = Number($('#rm_group_activation_strategy').find(':selected').val()) ?? group_activation_strategy.NATURAL; |
|
let generationMode = Number($('#rm_group_generation_mode').find(':selected').val()) ?? group_generation_mode.SWAP; |
|
let autoModeDelay = Number($('#rm_group_automode_delay').val()) ?? DEFAULT_AUTO_MODE_DELAY; |
|
const members = newGroupMembers; |
|
const memberNames = characters.filter(x => members.includes(x.avatar)).map(x => x.name).join(', '); |
|
|
|
if (!name) { |
|
name = t`Group: ${memberNames}`; |
|
} |
|
|
|
const avatar_url = $('#group_avatar_preview img').attr('src'); |
|
|
|
const chatName = humanizedDateTime(); |
|
const chats = [chatName]; |
|
|
|
const createGroupResponse = await fetch('/api/groups/create', { |
|
method: 'POST', |
|
headers: getRequestHeaders(), |
|
body: JSON.stringify({ |
|
name: name, |
|
members: members, |
|
avatar_url: isValidImageUrl(avatar_url) ? avatar_url : default_avatar, |
|
allow_self_responses: allowSelfResponses, |
|
hideMutedSprites: hideMutedSprites, |
|
activation_strategy: activationStrategy, |
|
generation_mode: generationMode, |
|
disabled_members: [], |
|
chat_metadata: {}, |
|
fav: fav_grp_checked, |
|
chat_id: chatName, |
|
chats: chats, |
|
auto_mode_delay: autoModeDelay, |
|
}), |
|
}); |
|
|
|
if (createGroupResponse.ok) { |
|
newGroupMembers = []; |
|
const data = await createGroupResponse.json(); |
|
createTagMapFromList('#groupTagList', data.id); |
|
await getCharacters(); |
|
select_rm_info('group_create', data.id); |
|
} |
|
} |
|
|
|
export async function createNewGroupChat(groupId) { |
|
const group = groups.find(x => x.id === groupId); |
|
|
|
if (!group) { |
|
return; |
|
} |
|
|
|
const oldChatName = group.chat_id; |
|
const newChatName = humanizedDateTime(); |
|
|
|
if (typeof group.past_metadata !== 'object') { |
|
group.past_metadata = {}; |
|
} |
|
|
|
await clearChat(); |
|
chat.length = 0; |
|
if (oldChatName) { |
|
group.past_metadata[oldChatName] = Object.assign({}, chat_metadata); |
|
} |
|
group.chats.push(newChatName); |
|
group.chat_id = newChatName; |
|
group.chat_metadata = {}; |
|
updateChatMetadata(group.chat_metadata, true); |
|
|
|
await editGroup(group.id, true, false); |
|
await getGroupChat(group.id); |
|
} |
|
|
|
export async function getGroupPastChats(groupId) { |
|
const group = groups.find(x => x.id === groupId); |
|
|
|
if (!group) { |
|
return []; |
|
} |
|
|
|
const chats = []; |
|
|
|
try { |
|
for (const chatId of group.chats) { |
|
const messages = await loadGroupChat(chatId); |
|
let this_chat_file_size = (JSON.stringify(messages).length / 1024).toFixed(2) + 'kb'; |
|
let chat_items = messages.length; |
|
const lastMessage = messages.length ? messages[messages.length - 1].mes : '[The chat is empty]'; |
|
const lastMessageDate = messages.length ? (messages[messages.length - 1].send_date || Date.now()) : Date.now(); |
|
chats.push({ |
|
'file_name': chatId, |
|
'mes': lastMessage, |
|
'last_mes': lastMessageDate, |
|
'file_size': this_chat_file_size, |
|
'chat_items': chat_items, |
|
}); |
|
} |
|
} catch (err) { |
|
console.error(err); |
|
} |
|
return chats; |
|
} |
|
|
|
export async function openGroupChat(groupId, chatId) { |
|
await waitUntilCondition(() => !isChatSaving, debounce_timeout.extended, 10); |
|
const group = groups.find(x => x.id === groupId); |
|
|
|
if (!group || !group.chats.includes(chatId)) { |
|
return; |
|
} |
|
|
|
await clearChat(); |
|
chat.length = 0; |
|
const previousChat = group.chat_id; |
|
group.past_metadata[previousChat] = Object.assign({}, chat_metadata); |
|
group.chat_id = chatId; |
|
group.chat_metadata = group.past_metadata[chatId] || {}; |
|
group['date_last_chat'] = Date.now(); |
|
updateChatMetadata(group.chat_metadata, true); |
|
|
|
await editGroup(groupId, true, false); |
|
await getGroupChat(groupId); |
|
} |
|
|
|
export async function renameGroupChat(groupId, oldChatId, newChatId) { |
|
const group = groups.find(x => x.id === groupId); |
|
|
|
if (!group || !group.chats.includes(oldChatId)) { |
|
return; |
|
} |
|
|
|
if (group.chat_id === oldChatId) { |
|
group.chat_id = newChatId; |
|
} |
|
|
|
group.chats.splice(group.chats.indexOf(oldChatId), 1); |
|
group.chats.push(newChatId); |
|
group.past_metadata[newChatId] = (group.past_metadata[oldChatId] || {}); |
|
delete group.past_metadata[oldChatId]; |
|
|
|
await editGroup(groupId, true, true); |
|
} |
|
|
|
export async function deleteGroupChat(groupId, chatId) { |
|
const group = groups.find(x => x.id === groupId); |
|
|
|
if (!group || !group.chats.includes(chatId)) { |
|
return; |
|
} |
|
|
|
group.chats.splice(group.chats.indexOf(chatId), 1); |
|
group.chat_metadata = {}; |
|
group.chat_id = ''; |
|
delete group.past_metadata[chatId]; |
|
updateChatMetadata(group.chat_metadata, true); |
|
|
|
const response = await fetch('/api/chats/group/delete', { |
|
method: 'POST', |
|
headers: getRequestHeaders(), |
|
body: JSON.stringify({ id: chatId }), |
|
}); |
|
|
|
if (response.ok) { |
|
if (group.chats.length) { |
|
await openGroupChat(groupId, group.chats[group.chats.length - 1]); |
|
} else { |
|
await createNewGroupChat(groupId); |
|
} |
|
|
|
await eventSource.emit(event_types.GROUP_CHAT_DELETED, chatId); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
export async function importGroupChat(formData, eventTarget) { |
|
const headers = getRequestHeaders(); |
|
delete headers['Content-Type']; |
|
const fetchResult = await fetch('/api/chats/group/import', { |
|
method: 'POST', |
|
headers: headers, |
|
body: formData, |
|
cache: 'no-cache', |
|
}); |
|
|
|
if (fetchResult.ok) { |
|
const data = await fetchResult.json(); |
|
if (data.res) { |
|
const chatId = data.res; |
|
const group = groups.find(x => x.id == selected_group); |
|
|
|
if (group) { |
|
group.chats.push(chatId); |
|
await editGroup(selected_group, true, true); |
|
await displayPastChats(); |
|
} |
|
} |
|
} |
|
|
|
if (eventTarget instanceof HTMLInputElement) { |
|
eventTarget.value = ''; |
|
} |
|
} |
|
|
|
export async function saveGroupBookmarkChat(groupId, name, metadata, mesId) { |
|
const group = groups.find(x => x.id === groupId); |
|
|
|
if (!group) { |
|
return; |
|
} |
|
|
|
group.past_metadata[name] = { ...chat_metadata, ...(metadata || {}) }; |
|
group.chats.push(name); |
|
|
|
const trimmed_chat = (mesId !== undefined && mesId >= 0 && mesId < chat.length) |
|
? chat.slice(0, parseInt(mesId) + 1) |
|
: chat; |
|
|
|
await editGroup(groupId, true, false); |
|
|
|
const response = await fetch('/api/chats/group/save', { |
|
method: 'POST', |
|
headers: getRequestHeaders(), |
|
body: JSON.stringify({ id: name, chat: [...trimmed_chat] }), |
|
}); |
|
|
|
if (!response.ok) { |
|
toastr.error(t`Check the server connection and reload the page to prevent data loss.`, t`Group chat could not be saved`); |
|
console.error('Group chat could not be saved', response); |
|
} |
|
} |
|
|
|
function onSendTextareaInput() { |
|
if (is_group_automode_enabled) { |
|
|
|
is_group_automode_enabled = false; |
|
$('#rm_group_automode').prop('checked', false); |
|
} |
|
} |
|
|
|
function stopAutoModeGeneration() { |
|
if (groupAutoModeAbortController) { |
|
groupAutoModeAbortController.abort(); |
|
} |
|
|
|
is_group_automode_enabled = false; |
|
$('#rm_group_automode').prop('checked', false); |
|
} |
|
|
|
function doCurMemberListPopout() { |
|
|
|
if ($('#groupMemberListPopout').length === 0) { |
|
console.debug('did not see popout yet, creating'); |
|
const memberListClone = $(this).parent().parent().find('.inline-drawer-content').html(); |
|
const template = $('#zoomed_avatar_template').html(); |
|
const controlBarHtml = `<div class="panelControlBar flex-container"> |
|
<div id="groupMemberListPopoutheader" class="fa-solid fa-grip drag-grabber hoverglow"></div> |
|
<div id="groupMemberListPopoutClose" class="fa-solid fa-circle-xmark hoverglow"></div> |
|
</div>`; |
|
const newElement = $(template); |
|
|
|
newElement.attr('id', 'groupMemberListPopout') |
|
.removeClass('zoomed_avatar') |
|
.addClass('draggable') |
|
.empty() |
|
.append(controlBarHtml) |
|
.append(memberListClone); |
|
|
|
|
|
newElement.find('.group_pagination').empty(); |
|
|
|
$('body').append(newElement); |
|
loadMovingUIState(); |
|
$('#groupMemberListPopout').fadeIn(animation_duration); |
|
dragElement(newElement); |
|
$('#groupMemberListPopoutClose').off('click').on('click', function () { |
|
$('#groupMemberListPopout').fadeOut(animation_duration, () => { $('#groupMemberListPopout').remove(); }); |
|
}); |
|
|
|
|
|
printGroupMembers(); |
|
} else { |
|
console.debug('saw existing popout, removing'); |
|
$('#groupMemberListPopout').fadeOut(animation_duration, () => { $('#groupMemberListPopout').remove(); }); |
|
} |
|
} |
|
|
|
jQuery(() => { |
|
if (!CSS.supports('field-sizing', 'content')) { |
|
$(document).on('input', '#rm_group_chats_block .autoSetHeight', function () { |
|
resetScrollHeight($(this)); |
|
}); |
|
} |
|
|
|
$(document).on('click', '.group_select', function () { |
|
const groupId = $(this).attr('data-chid') || $(this).attr('data-grid'); |
|
openGroupById(groupId); |
|
}); |
|
$('#rm_group_filter').on('input', filterGroupMembers); |
|
$('#rm_group_submit').on('click', createGroup); |
|
$('#rm_group_scenario').on('click', setScenarioOverride); |
|
$('#rm_group_automode').on('input', function () { |
|
const value = $(this).prop('checked'); |
|
is_group_automode_enabled = value; |
|
eventSource.once(event_types.GENERATION_STOPPED, stopAutoModeGeneration); |
|
}); |
|
$('#rm_group_hidemutedsprites').on('input', function () { |
|
const value = $(this).prop('checked'); |
|
hideMutedSprites = value; |
|
onHideMutedSpritesClick(value); |
|
|
|
}); |
|
$('#send_textarea').on('keyup', onSendTextareaInput); |
|
$('#groupCurrentMemberPopoutButton').on('click', doCurMemberListPopout); |
|
$('#rm_group_chat_name').on('input', onGroupNameInput); |
|
$('#rm_group_delete').off().on('click', onDeleteGroupClick); |
|
$('#group_favorite_button').on('click', onFavoriteGroupClick); |
|
$('#rm_group_allow_self_responses').on('input', onGroupSelfResponsesClick); |
|
$('#rm_group_activation_strategy').on('change', onGroupActivationStrategyInput); |
|
$('#rm_group_generation_mode').on('change', onGroupGenerationModeInput); |
|
$('#rm_group_automode_delay').on('input', onGroupAutoModeDelayInput); |
|
$('#rm_group_generation_mode_join_prefix').on('input', onGroupGenerationModeTemplateInput); |
|
$('#rm_group_generation_mode_join_suffix').on('input', onGroupGenerationModeTemplateInput); |
|
$('#group_avatar_button').on('input', uploadGroupAvatar); |
|
$('#rm_group_restore_avatar').on('click', restoreGroupAvatar); |
|
$(document).on('click', '.group_member .right_menu_button', onGroupActionClick); |
|
}); |
|
|