From 858e5f2efb3ba31a4ec11870fe87171cbb46cb65 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Wed, 5 Jun 2024 02:19:22 +0200 Subject: [PATCH 1/3] /rename-char slash command - Rename char slash command (with optional parameters for silent and rename old chats) - JSDoc fix for STscript named args in callbacks --- public/script.js | 113 +++++++++++------- public/scripts/slash-commands.js | 24 ++++ public/scripts/slash-commands/SlashCommand.js | 7 +- 3 files changed, 97 insertions(+), 47 deletions(-) diff --git a/public/script.js b/public/script.js index 98044e233..198e44c78 100644 --- a/public/script.js +++ b/public/script.js @@ -5431,70 +5431,91 @@ export function setSendButtonState(value) { is_send_press = value; } -async function renameCharacter() { +export async function renameCharacter(name = null, { silent = false, renameChats = null } = {}) { + if (!name && silent) { + toastr.warning('No character name provided.', 'Rename Character'); + return false; + } + const oldAvatar = characters[this_chid].avatar; - const newValue = await callPopup('

New name:

', 'input', characters[this_chid].name); + const newValue = name || await callPopup('

New name:

', 'input', characters[this_chid].name); - if (newValue && newValue !== characters[this_chid].name) { - const body = JSON.stringify({ avatar_url: oldAvatar, new_name: newValue }); - const response = await fetch('/api/characters/rename', { - method: 'POST', - headers: getRequestHeaders(), - body, - }); + if (!newValue) { + toastr.warning('No character name provided.', 'Rename Character'); + return false; + } + if (newValue === characters[this_chid].name) { + toastr.info('Same character name provided, so name did not change.', 'Rename Character'); + return false; + } - try { - if (response.ok) { - const data = await response.json(); - const newAvatar = data.avatar; + const body = JSON.stringify({ avatar_url: oldAvatar, new_name: newValue }); + const response = await fetch('/api/characters/rename', { + method: 'POST', + headers: getRequestHeaders(), + body, + }); - // Replace tags list - renameTagKey(oldAvatar, newAvatar); + try { + if (response.ok) { + const data = await response.json(); + const newAvatar = data.avatar; - // Reload characters list - await getCharacters(); + // Replace tags list + renameTagKey(oldAvatar, newAvatar); - // Find newly renamed character - const newChId = characters.findIndex(c => c.avatar == data.avatar); + // Reload characters list + await getCharacters(); - if (newChId !== -1) { - // Select the character after the renaming - this_chid = -1; - await selectCharacterById(String(newChId)); + // Find newly renamed character + const newChId = characters.findIndex(c => c.avatar == data.avatar); - // Async delay to update UI - await delay(1); + if (newChId !== -1) { + // Select the character after the renaming + this_chid = -1; + await selectCharacterById(String(newChId)); - if (this_chid === -1) { - throw new Error('New character not selected'); - } + // Async delay to update UI + await delay(1); - // Also rename as a group member - await renameGroupMember(oldAvatar, newAvatar, newValue); - const renamePastChatsConfirm = await callPopup(`

Character renamed!

-

Past chats will still contain the old character name. Would you like to update the character name in previous chats as well?

- Sprites folder (if any) should be renamed manually.`, 'confirm'); - - if (renamePastChatsConfirm) { - await renamePastChats(newAvatar, newValue); - await reloadCurrentChat(); - toastr.success('Character renamed and past chats updated!'); - } + if (this_chid === -1) { + throw new Error('New character not selected'); } - else { - throw new Error('Newly renamed character was lost?'); + + // Also rename as a group member + await renameGroupMember(oldAvatar, newAvatar, newValue); + const renamePastChatsConfirm = renameChats !== null ? renameChats + : silent ? false : await callPopup(`

Character renamed!

+

Past chats will still contain the old character name. Would you like to update the character name in previous chats as well?

+ Sprites folder (if any) should be renamed manually.`, 'confirm'); + + if (renamePastChatsConfirm) { + await renamePastChats(newAvatar, newValue); + await reloadCurrentChat(); + toastr.success('Character renamed and past chats updated!', 'Rename Character'); + } else { + toastr.success('Character renamed!', 'Rename Character'); } } else { - throw new Error('Could not rename the character'); + throw new Error('Newly renamed character was lost?'); } } - catch { - // Reloading to prevent data corruption - await callPopup('Something went wrong. The page will be reloaded.', 'text'); - location.reload(); + else { + throw new Error('Could not rename the character'); } } + catch (error) { + // Reloading to prevent data corruption + if (!silent) await callPopup('Something went wrong. The page will be reloaded.', 'text'); + else toastr.error('Something went wrong. The page will be reloaded.', 'Rename Character'); + + console.log('Renaming character error:', error); + location.reload(); + return false; + } + + return true; } async function renamePastChats(newAvatar, newValue) { diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index 5baa11435..c910a20bf 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -23,6 +23,7 @@ import { name2, reloadCurrentChat, removeMacros, + renameCharacter, saveChatConditional, sendMessageAsUser, sendSystemMessage, @@ -323,6 +324,29 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ helpString: 'Opens up a chat with the character or group by its name', aliases: ['char'], })); +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'rename-char', + /** @param {{silent: string, renameChats: string}} options @param {string} name */ + callback: async ({ silent = 'true', renameChats = null }, name) => { + const renamed = await renameCharacter(name, { silent: silent === 'true', renameChats: renameChats !== null ? renameChats === 'true' : null }); + return String(renamed); + }, + returns: 'true/false - Whether the rename was successful', + namedArgumentList: [ + new SlashCommandNamedArgument( + 'silent', 'Hide any blocking popups. (if false, the name is optional. If not supplied, a popup asking for it will appear)', [ARGUMENT_TYPE.BOOLEAN], false, false, 'true' + ), + new SlashCommandNamedArgument( + 'renameChats', 'Rename char in all previous chats', [ARGUMENT_TYPE.BOOLEAN], false, false, '' + ), + ], + unnamedArgumentList: [ + new SlashCommandArgument( + 'new char name', [ARGUMENT_TYPE.STRING], true, + ), + ], + helpString: 'Renames the current character.' +})); SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'sysgen', callback: generateSystemMessage, diff --git a/public/scripts/slash-commands/SlashCommand.js b/public/scripts/slash-commands/SlashCommand.js index 182c31882..bb0e2f9af 100644 --- a/public/scripts/slash-commands/SlashCommand.js +++ b/public/scripts/slash-commands/SlashCommand.js @@ -17,6 +17,11 @@ import { SlashCommandScope } from './SlashCommandScope.js'; * }} NamedArguments */ +/** + * Alternative object for local JSDocs, where you don't need existing pipe, scope, etc. arguments + * @typedef {{[id:string]:string|SlashCommandClosure}} NamedArgumentsCapture + */ + /** * @typedef {string|SlashCommandClosure|(string|SlashCommandClosure)[]} UnnamedArguments */ @@ -28,7 +33,7 @@ export class SlashCommand { * Creates a SlashCommand from a properties object. * @param {Object} props * @param {string} [props.name] - * @param {(namedArguments:NamedArguments, unnamedArguments:string|SlashCommandClosure|(string|SlashCommandClosure)[])=>string|SlashCommandClosure|void|Promise} [props.callback] + * @param {(namedArguments:NamedArguments|NamedArgumentsCapture, unnamedArguments:string|SlashCommandClosure|(string|SlashCommandClosure)[])=>string|SlashCommandClosure|void|Promise} [props.callback] * @param {string} [props.helpString] * @param {boolean} [props.splitUnnamedArgument] * @param {string[]} [props.aliases] From f04bbdf112337c4ca206e5769658aa61c07dd379 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Wed, 5 Jun 2024 20:24:50 +0200 Subject: [PATCH 2/3] Small changes to /rename-char --- public/script.js | 4 ++++ public/scripts/slash-commands.js | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/public/script.js b/public/script.js index 198e44c78..6de80e06e 100644 --- a/public/script.js +++ b/public/script.js @@ -5436,6 +5436,10 @@ export async function renameCharacter(name = null, { silent = false, renameChats toastr.warning('No character name provided.', 'Rename Character'); return false; } + if (this_chid === undefined) { + toastr.warning('No character selected.', 'Rename Character'); + return false; + } const oldAvatar = characters[this_chid].avatar; const newValue = name || await callPopup('

New name:

', 'input', characters[this_chid].name); diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index c910a20bf..a1ba74f33 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -328,7 +328,7 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'rename-char', /** @param {{silent: string, renameChats: string}} options @param {string} name */ callback: async ({ silent = 'true', renameChats = null }, name) => { - const renamed = await renameCharacter(name, { silent: silent === 'true', renameChats: renameChats !== null ? renameChats === 'true' : null }); + const renamed = await renameCharacter(name, { silent: isTrueBoolean(silent), renameChats: renameChats !== null ? isTrueBoolean(renameChats) : null }); return String(renamed); }, returns: 'true/false - Whether the rename was successful', @@ -337,7 +337,7 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ 'silent', 'Hide any blocking popups. (if false, the name is optional. If not supplied, a popup asking for it will appear)', [ARGUMENT_TYPE.BOOLEAN], false, false, 'true' ), new SlashCommandNamedArgument( - 'renameChats', 'Rename char in all previous chats', [ARGUMENT_TYPE.BOOLEAN], false, false, '' + 'chats', 'Rename char in all previous chats', [ARGUMENT_TYPE.BOOLEAN], false, false, '' ), ], unnamedArgumentList: [ From 2c787f23c7d1a6ffd18d8d6f0901174218bc5ee0 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 5 Jun 2024 22:53:41 +0300 Subject: [PATCH 3/3] Fix named argument. --- public/scripts/slash-commands.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index a1ba74f33..0917297db 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -326,18 +326,18 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ })); SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'rename-char', - /** @param {{silent: string, renameChats: string}} options @param {string} name */ - callback: async ({ silent = 'true', renameChats = null }, name) => { - const renamed = await renameCharacter(name, { silent: isTrueBoolean(silent), renameChats: renameChats !== null ? isTrueBoolean(renameChats) : null }); + /** @param {{silent: string, chats: string}} options @param {string} name */ + callback: async ({ silent = 'true', chats = null }, name) => { + const renamed = await renameCharacter(name, { silent: isTrueBoolean(silent), renameChats: chats !== null ? isTrueBoolean(chats) : null }); return String(renamed); }, returns: 'true/false - Whether the rename was successful', namedArgumentList: [ new SlashCommandNamedArgument( - 'silent', 'Hide any blocking popups. (if false, the name is optional. If not supplied, a popup asking for it will appear)', [ARGUMENT_TYPE.BOOLEAN], false, false, 'true' + 'silent', 'Hide any blocking popups. (if false, the name is optional. If not supplied, a popup asking for it will appear)', [ARGUMENT_TYPE.BOOLEAN], false, false, 'true', ), new SlashCommandNamedArgument( - 'chats', 'Rename char in all previous chats', [ARGUMENT_TYPE.BOOLEAN], false, false, '' + 'chats', 'Rename char in all previous chats', [ARGUMENT_TYPE.BOOLEAN], false, false, '', ), ], unnamedArgumentList: [ @@ -345,7 +345,7 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ 'new char name', [ARGUMENT_TYPE.STRING], true, ), ], - helpString: 'Renames the current character.' + helpString: 'Renames the current character.', })); SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'sysgen',