diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js
index e9396e6e8..092b23cde 100644
--- a/public/scripts/slash-commands.js
+++ b/public/scripts/slash-commands.js
@@ -177,6 +177,7 @@ parser.addCommand('run', runCallback, ['call', 'exec'], '(names=off/on [message index or range]) – returns the specified message or range of messages as a string.', true, true);
parser.addCommand('setinput', setInputCallback, [], '(text) – sets the user input to the specified text and passes it to the next command through the pipe.', true, true);
parser.addCommand('popup', popupCallback, [], '(text) – shows a blocking popup with the specified text.', true, true);
+parser.addCommand('buttons', buttonsCallback, [], 'labels=["a","b"] (text) – shows a blocking popup with the specified text and buttons. Returns the clicked button label into the pipe or empty string if canceled.', true, true);
parser.addCommand('trimtokens', trimTokensCallback, [], 'limit=number (direction=start/end [text]) – trims the start or end of text to the specified number of tokens.', true, true);
parser.addCommand('trimstart', trimStartCallback, [], '(text) – trims the text to the start of the first full sentence.', true, true);
parser.addCommand('trimend', trimEndCallback, [], '(text) – trims the text to the end of the last full sentence.', true, true);
@@ -255,6 +256,44 @@ function trimTokensCallback(arg, value) {
}
}
+async function buttonsCallback(args, text) {
+ try {
+ const buttons = JSON.parse(resolveVariable(args?.labels));
+
+ if (!Array.isArray(buttons) || !buttons.length) {
+ console.warn('WARN: Invalid labels provided for /buttons command');
+ return '';
+ }
+
+ return new Promise(async (resolve) => {
+ const safeValue = DOMPurify.sanitize(text || '');
+
+ const buttonContainer = document.createElement('div');
+ buttonContainer.classList.add('flex-container', 'flexFlowColumn', 'wide100p', 'm-t-1');
+
+ for (const button of buttons) {
+ const buttonElement = document.createElement('div');
+ buttonElement.classList.add('menu_button', 'wide100p');
+ buttonElement.addEventListener('click', () => {
+ resolve(button);
+ $('#dialogue_popup_ok').trigger('click');
+ });
+ buttonElement.innerText = button;
+ buttonContainer.appendChild(buttonElement);
+ }
+
+ const popupContainer = document.createElement('div');
+ popupContainer.innerHTML = safeValue;
+ popupContainer.appendChild(buttonContainer);
+ callPopup(popupContainer, 'text', '', { okButton: 'Cancel' })
+ .then(() => resolve(''))
+ .catch(() => resolve(''));
+ })
+ } catch {
+ return '';
+ }
+}
+
async function popupCallback(_, value) {
const safeValue = DOMPurify.sanitize(value || '');
await delay(1);