Merge pull request #3497 from SillyTavern/fix-char-rename-aux-connections Fix renaming characters losing connections of several aux fields

fa4a75215be893798e838b2a3b27d2fbf729486e

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
4 files changed, +72 -12Ignore whitespace
public/script.js+40 -3
@@ -497,6 +497,7 @@ export const event_types = {
497497 // TODO: Naming convention is inconsistent with other events
498498 CHARACTER_DELETED: 'characterDeleted',
499499 CHARACTER_DUPLICATED: 'character_duplicated',
500+ CHARACTER_RENAMED: 'character_renamed',
500501 /** @deprecated The event is aliased to STREAM_TOKEN_RECEIVED. */
501502 SMOOTH_STREAM_TOKEN_RECEIVED: 'stream_token_received',
502503 STREAM_TOKEN_RECEIVED: 'stream_token_received',
@@ -1031,12 +1032,22 @@ export function setAnimationDuration(ms = null) {
10311032 document.documentElement.style.setProperty('--animation-duration', `${animation_duration}ms`);
10321033}
10331034
1035+/**
1036+ * Sets the currently active character
1037+ * @param {object|number|string} [entityOrKey] - An entity with id property (character, group, tag), or directly an id or tag key. If not provided, the active character is reset to `null`.
1038+ */
10341039export function setActiveCharacter(entityOrKey) {
10351040 active_character = entityOrKey ? getTagKeyForEntity(entityOrKey) : null;
1041+ if (active_character) active_group = null;
10361042}
10371043
1044+/**
1045+ * Sets the currently active group.
1046+ * @param {object|number|string} [entityOrKey] - An entity with id property (character, group, tag), or directly an id or tag key. If not provided, the active group is reset to `null`.
1047+ */
10381048export function setActiveGroup(entityOrKey) {
10391049 active_group = entityOrKey ? getTagKeyForEntity(entityOrKey) : null;
1050+ if (active_group) active_character = null;
10401051}
10411052
10421053/**
@@ -6246,9 +6257,35 @@ export async function renameCharacter(name = null, { silent = false, renameChats
62466257 const data = await response.json();
62476258 const newAvatar = data.avatar;
62486259
6249- // Replace tags list
6260+ const oldName = getCharaFilename(null, { manualAvatarKey: oldAvatar });
6261+ const newName = getCharaFilename(null, { manualAvatarKey: newAvatar });
6262+
6263+ // Replace other auxillery fields where was referenced by avatar key
6264+ // Tag List
62506265 renameTagKey(oldAvatar, newAvatar);
62516266
6267+ // Addtional lore books
6268+ const charLore = world_info.charLore?.find(x => x.name == oldName);
6269+ if (charLore) {
6270+ charLore.name = newName;
6271+ saveSettingsDebounced();
6272+ }
6273+
6274+ // Char-bound Author's Notes
6275+ const charNote = extension_settings.note.chara?.find(x => x.name == oldName);
6276+ if (charNote) {
6277+ charNote.name = newName;
6278+ saveSettingsDebounced();
6279+ }
6280+
6281+ // Update active character, if the current one was the currently active one
6282+ if (active_character === oldAvatar) {
6283+ active_character = newAvatar;
6284+ saveSettingsDebounced();
6285+ }
6286+
6287+ await eventSource.emit(event_types.CHARACTER_RENAMED, oldAvatar, newAvatar);
6288+
62526289 // Reload characters list
62536290 await getCharacters();
62546291
public/scripts/RossAscends-mods.js+17 -2
@@ -280,17 +280,32 @@ async function RA_autoloadchat() {
280280 // active character is the name, we should look it up in the character list and get the id
281281 if (active_character !== null && active_character !== undefined) {
282282 const active_character_id = characters.findIndex(x => getTagKeyForEntity(x) === active_character);
283283 if (active_character_id !== null-1) {
284284 await selectCharacterById(String(active_character_id));
285285
286286 // Do a little tomfoolery to spoof the tag selector
287287 const selectedCharElement = $(`#rm_print_characters_block .character_select[chid="${active_character_id}"]`);
288288 applyTagsOnCharacterSelect.call(selectedCharElement);
289+ } else {
290+ setActiveCharacter(null);
291+ saveSettingsDebounced();
292+ console.warn(`Currently active character with ID ${active_character} not found. Resetting to no active character.`);
289293 }
290294 }
291295
292296 if (active_group !== null && active_group !== undefined) {
293- await openGroupById(String(active_group));
297+ if (active_character) {
298+ console.warn('Active character and active group are both set. Only active character will be loaded. Resetting active group.');
299+ setActiveGroup(null);
300+ saveSettingsDebounced();
301+ } else {
302+ const result = await openGroupById(String(active_group));
303+ if (!result) {
304+ setActiveGroup(null);
305+ saveSettingsDebounced();
306+ console.warn(`Currently active group with ID ${active_group} not found. Resetting to no active group.`);
307+ }
308+ }
294309 }
295310
296311 // if the character list hadn't been loaded yet, try again.
public/scripts/group-chats.js+5 -2
@@ -1664,12 +1664,12 @@ function updateFavButtonState(state) {
16641664export async function openGroupById(groupId) {
16651665 if (isChatSaving) {
16661666 toastr.info(t`Please wait until the chat is saved before switching characters.`, t`Your chat is still saving...`);
16671667 return false;
16681668 }
16691669
16701670 if (!groups.find(x => x.id === groupId)) {
16711671 console.log('Group not found', groupId);
16721672 return false;
16731673 }
16741674
16751675 if (!is_send_press && !is_group_generating) {
@@ -1686,8 +1686,11 @@ export async function openGroupById(groupId) {
16861686 updateChatMetadata({}, true);
16871687 chat.length = 0;
16881688 await getGroupChat(groupId);
1689+ return true;
16891690 }
16901691 }
1692+
1693+ return false;
16911694}
16921695
16931696function openCharacterDefinition(characterSelect) {
public/scripts/utils.js+10 -5
@@ -1007,13 +1007,18 @@ export function getImageSizeFromDataURL(dataUrl) {
10071007 });
10081008}
10091009
1010-export function getCharaFilename(chid) {
1010+/**
1011+ * Gets the filename of the character avatar without extension
1012+ * @param {number?} [chid=null] - Character ID. If not provided, uses the current character ID
1013+ * @param {object} [options={}] - Options arguments
1014+ * @param {string?} [options.manualAvatarKey=null] - Manually take the following avatar key, instead of using the chid to determine the name
1015+ * @returns {string?} The filename of the character avatar without extension, or null if the character ID is invalid
1016+ */
1017+export function getCharaFilename(chid = null, { manualAvatarKey = null } = {}) {
10111018 const context = getContext();
10121019 const fileName = manualAvatarKey ?? context.characters[chid ?? context.characterId]?.avatar;
10131020
1014- if (fileName) {
1021+ return fileName?.replace(/\.[^/.]+$/, '') ?? null;
1015- return fileName.replace(/\.[^/.]+$/, '');
1016- }
10171022}
10181023
10191024/**