Merge pull request #3651 from SillyTavern/input-handling-args Add `/input` arguments to execute closures on success and on cancel
Signed| @@ -69,7 +69,7 @@ import { SlashCommand } from './slash-commands/SlashCommand.js'; | ||
| 69 | 69 | import { SlashCommandAbortController } from './slash-commands/SlashCommandAbortController.js'; |
| 70 | 70 | import { SlashCommandNamedArgumentAssignment } from './slash-commands/SlashCommandNamedArgumentAssignment.js'; |
| 71 | 71 | import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js'; |
| 72 | 72 | import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js'; |
| 73 | 73 | import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 74 | 74 | import { SlashCommandBreakController } from './slash-commands/SlashCommandBreakController.js'; |
| 75 | 75 | import { SlashCommandExecutionError } from './slash-commands/SlashCommandExecutionError.js'; |
| @@ -1287,26 +1287,52 @@ export function initDefaultSlashCommands() { | ||
| 1287 | 1287 | callback: inputCallback, |
| 1288 | 1288 | returns: 'user input', |
| 1289 | 1289 | namedArgumentList: [ |
| 1290 | 1290 | new SlashCommandNamedArgument.fromProps({ |
| 1291 | - 'default', 'default value of the input field', [ARGUMENT_TYPE.STRING], false, false, '"string"', | |
| 1291 | + name: 'default', | |
| 1292 | - ), | |
| 1292 | + description: 'default value of the input field', | |
| 1293 | - new SlashCommandNamedArgument( | |
| 1293 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 1294 | - 'large', 'show large input field', [ARGUMENT_TYPE.BOOLEAN], false, false, 'off', commonEnumProviders.boolean('onOff')(), | |
| 1294 | + }), | |
| 1295 | - ), | |
| 1295 | + SlashCommandNamedArgument.fromProps({ | |
| 1296 | - new SlashCommandNamedArgument( | |
| 1296 | + name: 'large', | |
| 1297 | - 'wide', 'show wide input field', [ARGUMENT_TYPE.BOOLEAN], false, false, 'off', commonEnumProviders.boolean('onOff')(), | |
| 1297 | + description: 'show large input field', | |
| 1298 | - ), | |
| 1298 | + typeList: [ARGUMENT_TYPE.BOOLEAN], | |
| 1299 | - new SlashCommandNamedArgument( | |
| 1299 | + defaultValue: 'off', | |
| 1300 | - 'okButton', 'text for the ok button', [ARGUMENT_TYPE.STRING], false, | |
| 1300 | + enumList: commonEnumProviders.boolean('onOff')(), | |
| 1301 | 1301 | }), |
| 1302 | 1302 | new SlashCommandNamedArgument.fromProps({ |
| 1303 | - 'rows', 'number of rows for the input field', [ARGUMENT_TYPE.NUMBER], false, | |
| 1303 | + name: 'wide', | |
| 1304 | - ), | |
| 1304 | + description: 'show wide input field', | |
| 1305 | + typeList: [ARGUMENT_TYPE.BOOLEAN], | |
| 1306 | + defaultValue: 'off', | |
| 1307 | + enumList: commonEnumProviders.boolean('onOff')(), | |
| 1308 | + }), | |
| 1309 | + SlashCommandNamedArgument.fromProps({ | |
| 1310 | + name: 'okButton', | |
| 1311 | + description: 'text for the ok button', | |
| 1312 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 1313 | + defaultValue: 'Ok', | |
| 1314 | + }), | |
| 1315 | + SlashCommandNamedArgument.fromProps({ | |
| 1316 | + name: 'rows', | |
| 1317 | + description: 'number of rows for the input field', | |
| 1318 | + typeList: [ARGUMENT_TYPE.NUMBER], | |
| 1319 | + }), | |
| 1320 | + SlashCommandNamedArgument.fromProps({ | |
| 1321 | + name: 'onSuccess', | |
| 1322 | + description: 'closure to execute when the ok button is clicked or the input is closed as successful (via Enter, etc)', | |
| 1323 | + typeList: [ARGUMENT_TYPE.CLOSURE], | |
| 1324 | + }), | |
| 1325 | + SlashCommandNamedArgument.fromProps({ | |
| 1326 | + name: 'onCancel', | |
| 1327 | + description: 'closure to execute when the cancel button is clicked or the input is closed as cancelled (via Escape, etc)', | |
| 1328 | + typeList: [ARGUMENT_TYPE.CLOSURE], | |
| 1329 | + }), | |
| 1305 | 1330 | ], |
| 1306 | 1331 | unnamedArgumentList: [ |
| 1307 | 1332 | new SlashCommandArgument.fromProps({ |
| 1308 | 1333 | description: 'text to display', [ARGUMENT_TYPE.STRING], false, |
| 1309 | - ), | |
| 1334 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 1335 | + }), | |
| 1310 | 1336 | ], |
| 1311 | 1337 | helpString: ` |
| 1312 | 1338 | <div> |
| @@ -2546,6 +2572,7 @@ async function delayCallback(_, amount) { | ||
| 2546 | 2572 | return ''; |
| 2547 | 2573 | } |
| 2548 | 2574 | |
| 2575 | + | |
| 2549 | 2576 | async function inputCallback(args, prompt) { |
| 2550 | 2577 | const safeValue = DOMPurify.sanitize(prompt || ''); |
| 2551 | 2578 | const defaultInput = args?.default !== undefined && typeof args?.default === 'string' ? args.default : ''; |
| @@ -2559,6 +2586,26 @@ async function inputCallback(args, prompt) { | ||
| 2559 | 2586 | await delay(1); |
| 2560 | 2587 | const result = await callGenericPopup(safeValue, POPUP_TYPE.INPUT, defaultInput, popupOptions); |
| 2561 | 2588 | await delay(1); |
| 2589 | + | |
| 2590 | + // Input will return null on nothing entered, and false on cancel clicked | |
| 2591 | + if (result === null || result === false) { | |
| 2592 | + // Veryify if a cancel handler exists and it is valid | |
| 2593 | + if (args?.onCancel) { | |
| 2594 | + if (!(args.onCancel instanceof SlashCommandClosure)) { | |
| 2595 | + throw new Error('argument \'onCancel\' must be a closure for command /input'); | |
| 2596 | + } | |
| 2597 | + await args.onCancel.execute(); | |
| 2598 | + } | |
| 2599 | + } else { | |
| 2600 | + // Verify if an ok handler exists and it is valid | |
| 2601 | + if (args?.onSuccess) { | |
| 2602 | + if (!(args.onSuccess instanceof SlashCommandClosure)) { | |
| 2603 | + throw new Error('argument \'onSuccess\' must be a closure for command /input'); | |
| 2604 | + } | |
| 2605 | + await args.onSuccess.execute(); | |
| 2606 | + } | |
| 2607 | + } | |
| 2608 | + | |
| 2562 | 2609 | return String(result || ''); |
| 2563 | 2610 | } |
| 2564 | 2611 | |