add Slash commands regenerate and swipe (#5243) * add Slash commands regenerate and swipe * Update /swipe slash command direction handling * Fix await not working in groups * Add SLASH_COMMAND source to swipe functionality and update constants --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>

2415e6bc86157577024e17f1e4dbfc02abf019d9

Haochen <38915833+hcsum@users.noreply.github.com>

Signed
4 files changed, +124 -4Showing whitespace changes
public/script.js+1 -1
@@ -9692,7 +9692,7 @@ export async function swipe(event, direction, { source, repeated, message = chat
96929692
96939693 const mesId = Number(forceMesId ?? event?.currentTarget?.closest('.mes')?.getAttribute('mesid') ?? messageIndex ?? chat.length - 1);
96949694
9695- if (source === SWIPE_SOURCE.DELETE || source === SWIPE_SOURCE.BACK || source === SWIPE_SOURCE.AUTO_SWIPE) {
9695+ if ([SWIPE_SOURCE.DELETE, SWIPE_SOURCE.BACK, SWIPE_SOURCE.AUTO_SWIPE, SWIPE_SOURCE.SLASH_COMMAND].includes(source)) {
96969696 console.info(`The ${direction} swipe source on message #${mesId} is ${source}, Most checks have been bypassed. `);
96979697 } else {
96989698 //Only show an error if swipes are not hidden and a message is generating.
public/scripts/constants.js+1 -0
@@ -175,6 +175,7 @@ export const SWIPE_SOURCE = {
175175 KEYBOARD: 'keyboard',
176176 BACK: 'back',
177177 AUTO_SWIPE: 'auto_swipe',
178+ SLASH_COMMAND: 'slash_command',
178179};
179180
180181/**
public/scripts/group-chats.js+1 -1
@@ -183,7 +183,7 @@ async function regenerateGroup() {
183183
184184 const abortController = new AbortController();
185185 setExternalAbortController(abortController);
186186 return generateGroupWrapper(false, 'normal', { signal: abortController.signal });
187187}
188188
189189/**
public/scripts/slash-commands.js+121 -2
@@ -48,6 +48,7 @@ import {
4848 setCharacterName,
4949 setExtensionPrompt,
5050 showMoreMessages,
51+ swipe,
5152 stopGeneration,
5253 substituteParams,
5354 syncMesToSwipe,
@@ -62,7 +63,7 @@ import { getMessageTimeStamp, isMobile } from './RossAscends-mods.js';
6263import { hideChatMessageRange } from './chats.js';
6364import { getContext, saveMetadataDebounced } from './extensions.js';
6465import { getRegexedString, regex_placement } from './extensions/regex/engine.js';
6566import { findGroupMemberId, groups, is_group_generating, openGroupById, regenerateGroup, resetSelectedGroup, saveGroupChat, selected_group, getGroupMembers } from './group-chats.js';
6667import { chat_completion_sources, oai_settings, promptManager, ZAI_ENDPOINT } from './openai.js';
6768import { user_avatar } from './personas.js';
6869import { addEphemeralStoppingString, chat_styles, context_presets, flushEphemeralStoppingStrings, playMessageSound, power_user } from './power-user.js';
@@ -90,7 +91,7 @@ import { SlashCommandScope } from './slash-commands/SlashCommandScope.js';
9091import { t } from './i18n.js';
9192import { kai_settings } from './kai-settings.js';
9293import { instruct_presets, selectContextPreset, selectInstructPreset } from './instruct-mode.js';
9394import { debounce_timeout, SWIPE_DIRECTION, SWIPE_SOURCE } from './constants.js';
9495export {
9596 executeSlashCommands, executeSlashCommandsWithOptions, getSlashCommandsHelp, registerSlashCommand,
9697};
@@ -1133,6 +1134,66 @@ export function initDefaultSlashCommands() {
11331134 `,
11341135 }));
11351136 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1137+ name: 'regenerate',
1138+ callback: regenerateChatCallback,
1139+ aliases: ['regen'],
1140+ namedArgumentList: [
1141+ new SlashCommandNamedArgument(
1142+ 'await',
1143+ t`Whether to await for the regeneration before proceeding`,
1144+ [ARGUMENT_TYPE.BOOLEAN],
1145+ false,
1146+ false,
1147+ 'false',
1148+ ),
1149+ ],
1150+ helpString: `
1151+ <div>
1152+ ${t`Regenerates the latest reply in the chat.`}
1153+ </div>
1154+ <div>
1155+ ${t`If <code>await=true</code> named argument is passed, the command will await for the regeneration before proceeding.`}
1156+ </div>
1157+ `,
1158+ }));
1159+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1160+ name: 'swipe',
1161+ callback: swipeChatCallback,
1162+ namedArgumentList: [
1163+ new SlashCommandNamedArgument(
1164+ 'direction',
1165+ t`Swipe direction`,
1166+ [ARGUMENT_TYPE.STRING],
1167+ false,
1168+ false,
1169+ SWIPE_DIRECTION.RIGHT,
1170+ [
1171+ new SlashCommandEnumValue(SWIPE_DIRECTION.RIGHT, t`Swipe to the next reply`, enumTypes.enum, enumIcons.default),
1172+ new SlashCommandEnumValue(SWIPE_DIRECTION.LEFT, t`Swipe to the previous reply`, enumTypes.enum, enumIcons.default),
1173+ ],
1174+ [],
1175+ null,
1176+ true,
1177+ ),
1178+ new SlashCommandNamedArgument(
1179+ 'await',
1180+ t`Whether to await for the swipe action before proceeding`,
1181+ [ARGUMENT_TYPE.BOOLEAN],
1182+ false,
1183+ false,
1184+ 'false',
1185+ ),
1186+ ],
1187+ helpString: `
1188+ <div>
1189+ ${t`Swipes the latest reply. Defaults to <code>direction=right</code>; use <code>direction=left</code> to go to the previous reply. If no next swipe exists, behavior depends on message context.`}
1190+ </div>
1191+ <div>
1192+ ${t`If <code>await=true</code> named argument is passed, the command will await for the swipe action before proceeding.`}
1193+ </div>
1194+ `,
1195+ }));
1196+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
11361197 name: 'go',
11371198 callback: goToCharacterCallback,
11381199 returns: t`The character/group name`,
@@ -4538,6 +4599,64 @@ async function continueChatCallback(args, prompt) {
45384599 return '';
45394600}
45404601
4602+async function regenerateChatCallback(args) {
4603+ const shouldAwait = isTrueBoolean(args?.await);
4604+
4605+ const outerPromise = new Promise((outerResolve) => setTimeout(async () => {
4606+ try {
4607+ await waitUntilCondition(() => !is_send_press && !is_group_generating, 10000, 100);
4608+ } catch {
4609+ console.warn('Timeout waiting for generation unlock');
4610+ toastr.warning(t`Cannot run /regenerate command while the reply is being generated.`);
4611+ outerResolve(Promise.resolve(''));
4612+ return '';
4613+ }
4614+
4615+ if (selected_group) {
4616+ outerResolve(Promise.resolve(regenerateGroup()));
4617+ return '';
4618+ }
4619+
4620+ outerResolve(new Promise(innerResolve => setTimeout(() => {
4621+ innerResolve(Generate('regenerate'));
4622+ }, 1)));
4623+ return '';
4624+ }, 1));
4625+
4626+ if (shouldAwait) {
4627+ const innerPromise = await outerPromise;
4628+ await innerPromise;
4629+ }
4630+
4631+ return '';
4632+}
4633+
4634+async function swipeChatCallback(args) {
4635+ const shouldAwait = isTrueBoolean(args?.await);
4636+ const direction = args?.direction === SWIPE_DIRECTION.LEFT ? SWIPE_DIRECTION.LEFT : SWIPE_DIRECTION.RIGHT;
4637+
4638+ const outerPromise = new Promise((outerResolve) => setTimeout(async () => {
4639+ try {
4640+ await waitUntilCondition(() => !is_send_press && !is_group_generating, 10000, 100);
4641+ } catch {
4642+ console.warn('Timeout waiting for generation unlock');
4643+ toastr.warning(t`Cannot run /swipe command while the reply is being generated.`);
4644+ outerResolve(Promise.resolve(''));
4645+ return '';
4646+ }
4647+
4648+ outerResolve(Promise.resolve(swipe(null, direction, { source: SWIPE_SOURCE.SLASH_COMMAND, repeated: false })));
4649+ return '';
4650+ }, 1));
4651+
4652+ if (shouldAwait) {
4653+ const innerPromise = await outerPromise;
4654+ await innerPromise;
4655+ }
4656+
4657+ return '';
4658+}
4659+
45414660export async function generateSystemMessage(args, prompt) {
45424661 $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles: true }));
45434662