@@ -69,14 +69,15 @@ 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
- import { POPUP_TYPE , Popup , callGenericPopup } from './popup.js' ;
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' ;
76
76
import { slashCommandReturnHelper } from './slash-commands/SlashCommandReturnHelper.js' ;
77
77
import { accountStorage } from './util/AccountStorage.js' ;
78
78
import { SlashCommandDebugController } from './slash-commands/SlashCommandDebugController.js' ;
79
79
import { SlashCommandScope } from './slash-commands/SlashCommandScope.js' ;
80
+ import { t } from './i18n.js' ;
80
81
export {
81
82
executeSlashCommands , executeSlashCommandsWithOptions , getSlashCommandsHelp , registerSlashCommand ,
82
83
} ;
@@ -1556,16 +1557,28 @@ export function initDefaultSlashCommands() {
1556
1557
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
1557
1558
name : 'buttons' ,
1558
1559
callback : buttonsCallback ,
1559
- returns : 'clicked button label' ,
1560
+ returns : 'clicked button label (or array of labels if multiple is enabled) ' ,
1560
1561
namedArgumentList : [
1561
- new SlashCommandNamedArgument (
1562
- 'labels' , 'button labels' , [ ARGUMENT_TYPE . LIST ] , true ,
1563
- ) ,
1562
+ SlashCommandNamedArgument . fromProps ( {
1563
+ name : 'labels' ,
1564
+ description : 'button labels' ,
1565
+ typeList : [ ARGUMENT_TYPE . LIST ] ,
1566
+ isRequired : true ,
1567
+ } ) ,
1568
+ SlashCommandNamedArgument . fromProps ( {
1569
+ name : 'multiple' ,
1570
+ description : 'if enabled multiple buttons can be clicked/toggled, and all clicked buttons are returned as an array' ,
1571
+ typeList : [ ARGUMENT_TYPE . BOOLEAN ] ,
1572
+ enumList : commonEnumProviders . boolean ( 'trueFalse' ) ( ) ,
1573
+ defaultValue : 'false' ,
1574
+ } ) ,
1564
1575
] ,
1565
1576
unnamedArgumentList : [
1566
- new SlashCommandArgument (
1567
- 'text' , [ ARGUMENT_TYPE . STRING ] , true ,
1568
- ) ,
1577
+ SlashCommandArgument . fromProps ( {
1578
+ description : 'text' ,
1579
+ typeList : [ ARGUMENT_TYPE . STRING ] ,
1580
+ isRequired : true ,
1581
+ } ) ,
1569
1582
] ,
1570
1583
helpString : `
1571
1584
<div>
@@ -2377,6 +2390,18 @@ async function trimTokensCallback(arg, value) {
2377
2390
}
2378
2391
}
2379
2392
2393
+ /**
2394
+ * Displays a popup with buttons based on provided labels and handles button interactions.
2395
+ *
2396
+ * @param {object } args - Named arguments for the command
2397
+ * @param {string } args.labels - JSON string of an array of button labels
2398
+ * @param {string } [args.multiple=false] - Flag indicating if multiple buttons can be toggled
2399
+ * @param {string } text - The text content to be displayed within the popup
2400
+ *
2401
+ * @returns {Promise<string> } - A promise that resolves to a string of the button labels selected
2402
+ * If 'multiple' is true, returns a JSON string array of labels.
2403
+ * If 'multiple' is false, returns a single label string.
2404
+ */
2380
2405
async function buttonsCallback ( args , text ) {
2381
2406
try {
2382
2407
/** @type {string[] } */
@@ -2387,6 +2412,10 @@ async function buttonsCallback(args, text) {
2387
2412
return '' ;
2388
2413
}
2389
2414
2415
+ /** @type {Set<number> } */
2416
+ const multipleToggledState = new Set ( ) ;
2417
+ const multiple = isTrueBoolean ( args ?. multiple ) ;
2418
+
2390
2419
// Map custom buttons to results. Start at 2 because 1 and 0 are reserved for ok and cancel
2391
2420
const resultToButtonMap = new Map ( buttons . map ( ( button , index ) => [ index + 2 , button ] ) ) ;
2392
2421
@@ -2404,11 +2433,24 @@ async function buttonsCallback(args, text) {
2404
2433
2405
2434
for ( const [ result , button ] of resultToButtonMap ) {
2406
2435
const buttonElement = document . createElement ( 'div' ) ;
2407
- buttonElement . classList . add ( 'menu_button' , 'result-control' , 'wide100p' ) ;
2408
- buttonElement . dataset . result = String ( result ) ;
2409
- buttonElement . addEventListener ( 'click' , async ( ) => {
2410
- await popup . complete ( result ) ;
2411
- } ) ;
2436
+ buttonElement . classList . add ( 'menu_button' , 'wide100p' ) ;
2437
+
2438
+ if ( multiple ) {
2439
+ buttonElement . classList . add ( 'toggleable' ) ;
2440
+ buttonElement . dataset . toggleValue = String ( result ) ;
2441
+ buttonElement . addEventListener ( 'click' , async ( ) => {
2442
+ buttonElement . classList . toggle ( 'toggled' ) ;
2443
+ if ( buttonElement . classList . contains ( 'toggled' ) ) {
2444
+ multipleToggledState . add ( result ) ;
2445
+ } else {
2446
+ multipleToggledState . delete ( result ) ;
2447
+ }
2448
+ } ) ;
2449
+ } else {
2450
+ buttonElement . classList . add ( 'result-control' ) ;
2451
+ buttonElement . dataset . result = String ( result ) ;
2452
+ }
2453
+
2412
2454
buttonElement . innerText = button ;
2413
2455
buttonContainer . appendChild ( buttonElement ) ;
2414
2456
}
@@ -2424,10 +2466,19 @@ async function buttonsCallback(args, text) {
2424
2466
popupContainer . style . flexDirection = 'column' ;
2425
2467
popupContainer . style . maxHeight = '80vh' ; // Limit the overall height of the popup
2426
2468
2427
- popup = new Popup ( popupContainer , POPUP_TYPE . TEXT , '' , { okButton : ' Cancel' , allowVerticalScrolling : true } ) ;
2469
+ popup = new Popup ( popupContainer , POPUP_TYPE . TEXT , '' , { okButton : multiple ? t `Ok` : t ` Cancel` , allowVerticalScrolling : true } ) ;
2428
2470
popup . show ( )
2429
- . then ( ( result => resolve ( typeof result === 'number' ? resultToButtonMap . get ( result ) ?? '' : '' ) ) )
2471
+ . then ( ( result => resolve ( getResult ( result ) ) ) )
2430
2472
. catch ( ( ) => resolve ( '' ) ) ;
2473
+
2474
+ /** @returns {string } @param {string|number|boolean } result */
2475
+ function getResult ( result ) {
2476
+ if ( multiple ) {
2477
+ const array = result === POPUP_RESULT . AFFIRMATIVE ? Array . from ( multipleToggledState ) . map ( r => resultToButtonMap . get ( r ) ?? '' ) : [ ] ;
2478
+ return JSON . stringify ( array ) ;
2479
+ }
2480
+ return typeof result === 'number' ? resultToButtonMap . get ( result ) ?? '' : '' ;
2481
+ }
2431
2482
} ) ;
2432
2483
} catch {
2433
2484
return '' ;
0 commit comments