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 | import { SlashCommandAbortController } from './slash-commands/SlashCommandAbortController.js'; | 69 | import { SlashCommandAbortController } from './slash-commands/SlashCommandAbortController.js'; |
| 70 | import { SlashCommandNamedArgumentAssignment } from './slash-commands/SlashCommandNamedArgumentAssignment.js'; | 70 | import { SlashCommandNamedArgumentAssignment } from './slash-commands/SlashCommandNamedArgumentAssignment.js'; |
| 71 | import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js'; | 71 | import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js'; |
| 72 | import { POPUP_TYPE, Popup, callGenericPopup } from './popup.js'; | 72 | import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js'; |
| 73 | import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; | 73 | import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 74 | import { SlashCommandBreakController } from './slash-commands/SlashCommandBreakController.js'; | 74 | import { SlashCommandBreakController } from './slash-commands/SlashCommandBreakController.js'; |
| 75 | import { SlashCommandExecutionError } from './slash-commands/SlashCommandExecutionError.js'; | 75 | import { SlashCommandExecutionError } from './slash-commands/SlashCommandExecutionError.js'; |
| @@ -1287,26 +1287,52 @@ export function initDefaultSlashCommands() { | |||
| 1287 | callback: inputCallback, | 1287 | callback: inputCallback, |
| 1288 | returns: 'user input', | 1288 | returns: 'user input', |
| 1289 | namedArgumentList: [ | 1289 | namedArgumentList: [ |
| 1290 | new SlashCommandNamedArgument( | 1290 | 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 | new SlashCommandNamedArgument( | 1302 | 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 | unnamedArgumentList: [ | 1331 | unnamedArgumentList: [ |
| 1307 | new SlashCommandArgument( | 1332 | SlashCommandArgument.fromProps({ |
| 1308 | 'text to display', [ARGUMENT_TYPE.STRING], false, | 1333 | description: 'text to display', |
| 1309 | ), | 1334 | typeList: [ARGUMENT_TYPE.STRING], |
| 1335 | }), | ||
| 1310 | ], | 1336 | ], |
| 1311 | helpString: ` | 1337 | helpString: ` |
| 1312 | <div> | 1338 | <div> |
| @@ -2546,6 +2572,7 @@ async function delayCallback(_, amount) { | |||
| 2546 | return ''; | 2572 | return ''; |
| 2547 | } | 2573 | } |
| 2548 | 2574 | ||
| 2575 | |||
| 2549 | async function inputCallback(args, prompt) { | 2576 | async function inputCallback(args, prompt) { |
| 2550 | const safeValue = DOMPurify.sanitize(prompt || ''); | 2577 | const safeValue = DOMPurify.sanitize(prompt || ''); |
| 2551 | const defaultInput = args?.default !== undefined && typeof args?.default === 'string' ? args.default : ''; | 2578 | const defaultInput = args?.default !== undefined && typeof args?.default === 'string' ? args.default : ''; |
| @@ -2559,6 +2586,26 @@ async function inputCallback(args, prompt) { | |||
| 2559 | await delay(1); | 2586 | await delay(1); |
| 2560 | const result = await callGenericPopup(safeValue, POPUP_TYPE.INPUT, defaultInput, popupOptions); | 2587 | const result = await callGenericPopup(safeValue, POPUP_TYPE.INPUT, defaultInput, popupOptions); |
| 2561 | await delay(1); | 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 | return String(result || ''); | 2609 | return String(result || ''); |
| 2563 | } | 2610 | } |
| 2564 | 2611 | ||