Extend Popup System with Placeholder, Tooltip, and Icon Support (+ those in slash commands) (#5322) * feat: add placeholder and tooltip support to popup system with icon buttons Add `placeholder` and `tooltip` options to main popup configuration. For INPUT type popups, placeholder applies to input field; for other types, tooltip applies to content area. Enhance custom buttons with optional `icon` parameter for Font Awesome icons and `tooltip` for hover text. Add tooltip support to custom inputs (placeholder for text/textarea, tooltip icon for checkboxes). * fix: preserve default toastClass when applying custom cssClass in /echo command Modify cssClass argument handling in echoCallback to append custom class to existing toastClass instead of replacing it. Use filter(Boolean).join(' ') to combine default and custom classes while handling undefined values. * feat: add placeholder, tooltip, and icon support to popup system slash commands Add `placeholder` and `tooltip` named arguments to /input command for input field customization. Add `tooltip` argument to /popup command for content area hover text. Enhance /buttons command to support button objects with `text`, `tooltip`, and `icon` (Font Awesome) properties alongside simple string labels. Update help text and examples for all three commands. Normalize button labels to ButtonLabel format internally * Fix jsdoc wording Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * fix: add validation for button labels in /buttons command Add validation check to ensure each button entry is either a string or an object with a non-empty string `text` property. Return empty string and log warning if validation fails. Fix capitalization of 'Popup' in /popup command return value description. --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

240e9ca9074fb21b65d7621351cf14dd01051e46

Wolfsblvt <wolfsblvt@gmail.com>

Signed
3 files changed, +125 -18Showing whitespace changes
public/css/popup.css+4 -0
@@ -161,6 +161,10 @@ body.no-blur .popup[open]::backdrop {
161.popup-inputs {161.popup-inputs {
162 margin-top: 10px;162 margin-top: 10px;
163 font-size: smaller;163 font-size: smaller;
164}
165
166.popup-inputs label span {
167 /* Make the title of the custom inputs a bit darker, but keep their text at default opacity. */
164 opacity: 0.7;168 opacity: 0.7;
165}169}
166170
public/scripts/popup.js+43 -6
@@ -40,6 +40,8 @@ export const POPUP_RESULT = {
40 * @property {string|boolean?} [okButton=null] - Custom text for the OK button. A set text will always show the button. `true` or `false` to explicitly show or hide the button. `null` will leave the behavior and display of the button unchanged, based on the popup type.40 * @property {string|boolean?} [okButton=null] - Custom text for the OK button. A set text will always show the button. `true` or `false` to explicitly show or hide the button. `null` will leave the behavior and display of the button unchanged, based on the popup type.
41 * @property {string|boolean?} [cancelButton=null] - Custom text for the Cancel button. A set text will always show the button. `true` or `false` to explicitly show or hide the button. `null` will leave the behavior and display of the button unchanged, based on the popup type.41 * @property {string|boolean?} [cancelButton=null] - Custom text for the Cancel button. A set text will always show the button. `true` or `false` to explicitly show or hide the button. `null` will leave the behavior and display of the button unchanged, based on the popup type.
42 * @property {number?} [rows=1] - The number of rows for the input field42 * @property {number?} [rows=1] - The number of rows for the input field
43 * @property {string?} [placeholder=null] - Placeholder text for the main interactive element (input field for INPUT type). For other popup types, use tooltip for additional hints or to describe content elements.
44 * @property {string?} [tooltip=null] - Tooltip text shown on hover for the main interactive element or content area
43 * @property {boolean?} [wide=false] - Whether to display the popup in wide mode (wide screen, 1/1 aspect ratio)45 * @property {boolean?} [wide=false] - Whether to display the popup in wide mode (wide screen, 1/1 aspect ratio)
44 * @property {boolean?} [wider=false] - Whether to display the popup in wider mode (just wider, no height scaling)46 * @property {boolean?} [wider=false] - Whether to display the popup in wider mode (just wider, no height scaling)
45 * @property {boolean?} [large=false] - Whether to display the popup in large mode (90% of screen)47 * @property {boolean?} [large=false] - Whether to display the popup in large mode (90% of screen)
@@ -61,8 +63,10 @@ export const POPUP_RESULT = {
61/**63/**
62 * @typedef {object} CustomPopupButton64 * @typedef {object} CustomPopupButton
63 * @property {string} text - The text of the button65 * @property {string} text - The text of the button
66 * @property {string?} [tooltip] - Optional tooltip text displayed when hovering over the button
64 * @property {POPUP_RESULT|number?} [result] - The result of the button - can also be a custom result value to make be able to find out that this button was clicked. If no result is specified, this button will **not** close the popup.67 * @property {POPUP_RESULT|number?} [result] - The result of the button - can also be a custom result value to make be able to find out that this button was clicked. If no result is specified, this button will **not** close the popup.
65 * @property {string[]|string?} [classes] - Optional custom CSS classes applied to the button68 * @property {string[]|string?} [classes] - Optional custom CSS classes applied to the button
69 * @property {string?} [icon] - Optional Font Awesome icon class (e.g. 'fa-wand-magic-sparkles') to display before the text
66 * @property {()=>void?} [action] - Optional action to perform when the button is clicked70 * @property {()=>void?} [action] - Optional action to perform when the button is clicked
67 * @property {boolean?} [appendAtEnd] - Whether to append the button to the end of the popup - by default it will be prepended71 * @property {boolean?} [appendAtEnd] - Whether to append the button to the end of the popup - by default it will be prepended
68 */72 */
@@ -71,7 +75,7 @@ export const POPUP_RESULT = {
71 * @typedef {object} CustomPopupInput75 * @typedef {object} CustomPopupInput
72 * @property {string} id - The id for the html element76 * @property {string} id - The id for the html element
73 * @property {string} label - The label text for the input77 * @property {string} label - The label text for the input
74 * @property {string?} [tooltip=null] - Optional tooltip icon displayed behind the label78 * @property {string?} [tooltip=null] - Optional tooltip to be displayed. Default placeholder in input controls, tooltip icon behind the checkbox for those.
75 * @property {boolean|string|undefined} [defaultState=false] - The default state when opening the popup (false if not set)79 * @property {boolean|string|undefined} [defaultState=false] - The default state when opening the popup (false if not set)
76 * @property {('checkbox'|'text'|'textarea')?} [type='checkbox'] - The type of the input (default is checkbox)80 * @property {('checkbox'|'text'|'textarea')?} [type='checkbox'] - The type of the input (default is checkbox)
77 * @property {number?} [rows=1] - The number of rows for the input field, if the input is 'textarea'81 * @property {number?} [rows=1] - The number of rows for the input field, if the input is 'textarea'
@@ -178,7 +182,7 @@ export class Popup {
178 * @param {string} [inputValue=''] - The initial value of the input field182 * @param {string} [inputValue=''] - The initial value of the input field
179 * @param {PopupOptions} [options={}] - Additional options for the popup183 * @param {PopupOptions} [options={}] - Additional options for the popup
180 */184 */
181 constructor(content, type, inputValue = '', { okButton = null, cancelButton = null, rows = 1, wide = false, wider = false, large = false, transparent = false, allowHorizontalScrolling = false, allowVerticalScrolling = false, leftAlign = false, animation = 'fast', defaultResult = POPUP_RESULT.AFFIRMATIVE, customButtons = null, customInputs = null, onClosing = null, onClose = null, onOpen = null, cropAspect = null, cropImage = null } = {}) {185 constructor(content, type, inputValue = '', { okButton = null, cancelButton = null, rows = 1, placeholder = null, tooltip = null, wide = false, wider = false, large = false, transparent = false, allowHorizontalScrolling = false, allowVerticalScrolling = false, leftAlign = false, animation = 'fast', defaultResult = POPUP_RESULT.AFFIRMATIVE, customButtons = null, customInputs = null, onClosing = null, onClose = null, onOpen = null, cropAspect = null, cropImage = null } = {}) {
182 Popup.util.popups.push(this);186 Popup.util.popups.push(this);
183187
184 // Make this popup uniquely identifiable188 // Make this popup uniquely identifiable
@@ -233,6 +237,15 @@ export class Popup {
233 this.cancelButton.textContent = typeof cancelButton === 'string' ? cancelButton : template.getAttribute('popup-button-cancel');237 this.cancelButton.textContent = typeof cancelButton === 'string' ? cancelButton : template.getAttribute('popup-button-cancel');
234 this.cancelButton.dataset.i18n = this.cancelButton.textContent;238 this.cancelButton.dataset.i18n = this.cancelButton.textContent;
235239
240 /** @param {HTMLElement} control @param {string} text Sets the title attribute and translation, if text is provided */
241 function setTitleFromTooltip(control, text) {
242 if (!text) return;
243 control.title = text;
244 if (!control.dataset.i18n) {
245 control.dataset.i18n = '[title]' + text; // Don't override an existing translation of main text with title translation
246 }
247 }
248
236 this.defaultResult = defaultResult;249 this.defaultResult = defaultResult;
237 this.customButtons = customButtons;250 this.customButtons = customButtons;
238 this.customButtons?.forEach((x, index) => {251 this.customButtons?.forEach((x, index) => {
@@ -243,9 +256,22 @@ export class Popup {
243 buttonElement.classList.add('menu_button', 'popup-button-custom', 'result-control');256 buttonElement.classList.add('menu_button', 'popup-button-custom', 'result-control');
244 buttonElement.classList.add(...(button.classes ?? []));257 buttonElement.classList.add(...(button.classes ?? []));
245 buttonElement.dataset.result = String(button.result); // This is expected to also write 'null' or 'staging', to indicate cancel and no action respectively258 buttonElement.dataset.result = String(button.result); // This is expected to also write 'null' or 'staging', to indicate cancel and no action respectively
259 buttonElement.tabIndex = 0;
260
261 if (button.icon) {
262 const icon = document.createElement('i');
263 icon.className = `fa-solid ${button.icon}`;
264 buttonElement.appendChild(icon);
265 const textSpan = document.createElement('span');
266 textSpan.textContent = button.text;
267 textSpan.dataset.i18n = button.text;
268 buttonElement.classList.add('menu_button_icon');
269 buttonElement.appendChild(textSpan);
270 } else {
246 buttonElement.textContent = button.text;271 buttonElement.textContent = button.text;
247 buttonElement.dataset.i18n = buttonElement.textContent;272 buttonElement.dataset.i18n = buttonElement.textContent;
248 buttonElement.tabIndex = 0;273 }
274 setTitleFromTooltip(buttonElement, button.tooltip);
249275
250 if (button.appendAtEnd) {276 if (button.appendAtEnd) {
251 this.buttonControls.appendChild(buttonElement);277 this.buttonControls.appendChild(buttonElement);
@@ -282,8 +308,7 @@ export class Popup {
282 if (input.tooltip) {308 if (input.tooltip) {
283 const tooltip = document.createElement('div');309 const tooltip = document.createElement('div');
284 tooltip.classList.add('fa-solid', 'fa-circle-info', 'opacity50p');310 tooltip.classList.add('fa-solid', 'fa-circle-info', 'opacity50p');
285 tooltip.title = input.tooltip;311 setTitleFromTooltip(tooltip, input.tooltip);
286 tooltip.dataset.i18n = '[title]' + input.tooltip;
287 label.appendChild(tooltip);312 label.appendChild(tooltip);
288 }313 }
289314
@@ -299,6 +324,7 @@ export class Popup {
299 inputElement.id = input.id;324 inputElement.id = input.id;
300 inputElement.value = String(input.defaultState ?? '');325 inputElement.value = String(input.defaultState ?? '');
301 inputElement.placeholder = input.tooltip ?? '';326 inputElement.placeholder = input.tooltip ?? '';
327 setTitleFromTooltip(inputElement, input.tooltip);
302328
303 const labelText = document.createElement('span');329 const labelText = document.createElement('span');
304 labelText.innerText = input.label;330 labelText.innerText = input.label;
@@ -317,8 +343,9 @@ export class Popup {
317 inputElement.classList.add('text_pole', 'result-control');343 inputElement.classList.add('text_pole', 'result-control');
318 inputElement.id = input.id;344 inputElement.id = input.id;
319 inputElement.value = String(input.defaultState ?? '');345 inputElement.value = String(input.defaultState ?? '');
320 inputElement.placeholder = input.tooltip ?? '';
321 inputElement.rows = input.rows ?? 1;346 inputElement.rows = input.rows ?? 1;
347 inputElement.placeholder = input.tooltip ?? '';
348 setTitleFromTooltip(inputElement, input.tooltip);
322349
323 const labelText = document.createElement('span');350 const labelText = document.createElement('span');
324 labelText.innerText = input.label;351 labelText.innerText = input.label;
@@ -405,6 +432,16 @@ export class Popup {
405 this.mainInput.value = inputValue;432 this.mainInput.value = inputValue;
406 this.mainInput.rows = rows ?? 1;433 this.mainInput.rows = rows ?? 1;
407434
435 // Apply placeholder and tooltip based on popup type
436 if (type === POPUP_TYPE.INPUT) {
437 // For INPUT type, apply to the main input element
438 this.mainInput.placeholder = placeholder ?? '';
439 setTitleFromTooltip(this.mainInput, tooltip);
440 } else {
441 // For other types, apply tooltip to the content area
442 setTitleFromTooltip(this.content, tooltip);
443 }
444
408 this.content.innerHTML = '';445 this.content.innerHTML = '';
409 if (content instanceof jQuery) {446 if (content instanceof jQuery) {
410 $(this.content).append(content);447 $(this.content).append(content);
public/scripts/slash-commands.js+78 -12
@@ -2550,6 +2550,16 @@ export function initDefaultSlashCommands() {
2550 typeList: [ARGUMENT_TYPE.NUMBER],2550 typeList: [ARGUMENT_TYPE.NUMBER],
2551 }),2551 }),
2552 SlashCommandNamedArgument.fromProps({2552 SlashCommandNamedArgument.fromProps({
2553 name: 'placeholder',
2554 description: t`placeholder text displayed in the input field when empty`,
2555 typeList: [ARGUMENT_TYPE.STRING],
2556 }),
2557 SlashCommandNamedArgument.fromProps({
2558 name: 'tooltip',
2559 description: t`tooltip text shown when hovering over the input field`,
2560 typeList: [ARGUMENT_TYPE.STRING],
2561 }),
2562 SlashCommandNamedArgument.fromProps({
2553 name: 'onSuccess',2563 name: 'onSuccess',
2554 description: t`closure to execute when the ok button is clicked or the input is closed as successful (via Enter, etc)`,2564 description: t`closure to execute when the ok button is clicked or the input is closed as successful (via Enter, etc)`,
2555 typeList: [ARGUMENT_TYPE.CLOSURE],2565 typeList: [ARGUMENT_TYPE.CLOSURE],
@@ -2571,6 +2581,14 @@ export function initDefaultSlashCommands() {
2571 ${t`Shows a popup with the provided text and an input field.`}2581 ${t`Shows a popup with the provided text and an input field.`}
2572 ${t`The <code>default</code> argument is the default value of the input field, and the text argument is the text to display.`}2582 ${t`The <code>default</code> argument is the default value of the input field, and the text argument is the text to display.`}
2573 </div>2583 </div>
2584 <div>
2585 <strong>${t`Example:`}</strong>
2586 <ul>
2587 <li>
2588 <pre><code>/input default="John" placeholder="Enter your name" tooltip="Your display name" What is your name?</code></pre>
2589 </li>
2590 </ul>
2591 </div>
2574 `,2592 `,
2575 }));2593 }));
2576 SlashCommandParser.addCommandObject(SlashCommand.fromProps({2594 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
@@ -2682,7 +2700,7 @@ export function initDefaultSlashCommands() {
2682 SlashCommandParser.addCommandObject(SlashCommand.fromProps({2700 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
2683 name: 'popup',2701 name: 'popup',
2684 callback: popupCallback,2702 callback: popupCallback,
2685 returns: t`popup text`,2703 returns: t`Popup text`,
2686 namedArgumentList: [2704 namedArgumentList: [
2687 SlashCommandNamedArgument.fromProps({2705 SlashCommandNamedArgument.fromProps({
2688 name: 'scroll',2706 name: 'scroll',
@@ -2737,6 +2755,11 @@ export function initDefaultSlashCommands() {
2737 enumList: commonEnumProviders.boolean('trueFalse')(),2755 enumList: commonEnumProviders.boolean('trueFalse')(),
2738 defaultValue: 'false',2756 defaultValue: 'false',
2739 }),2757 }),
2758 SlashCommandNamedArgument.fromProps({
2759 name: 'tooltip',
2760 description: t`tooltip text shown when hovering over the popup content area`,
2761 typeList: [ARGUMENT_TYPE.STRING],
2762 }),
2740 ],2763 ],
2741 unnamedArgumentList: [2764 unnamedArgumentList: [
2742 SlashCommandArgument.fromProps({2765 SlashCommandArgument.fromProps({
@@ -2770,7 +2793,7 @@ export function initDefaultSlashCommands() {
2770 namedArgumentList: [2793 namedArgumentList: [
2771 SlashCommandNamedArgument.fromProps({2794 SlashCommandNamedArgument.fromProps({
2772 name: 'labels',2795 name: 'labels',
2773 description: t`button labels`,2796 description: t`button labels - can be an array of strings or objects with text, tooltip, and icon properties`,
2774 typeList: [ARGUMENT_TYPE.LIST],2797 typeList: [ARGUMENT_TYPE.LIST],
2775 isRequired: true,2798 isRequired: true,
2776 }),2799 }),
@@ -2795,11 +2818,17 @@ export function initDefaultSlashCommands() {
2795 ${t`Returns the clicked button label into the pipe or empty string if canceled.`}2818 ${t`Returns the clicked button label into the pipe or empty string if canceled.`}
2796 </div>2819 </div>
2797 <div>2820 <div>
2821 ${t`Labels can be simple strings or objects with <code>text</code>, <code>tooltip</code>, and <code>icon</code> (Font Awesome class) properties.`}
2822 </div>
2823 <div>
2798 <strong>${t`Example:`}</strong>2824 <strong>${t`Example:`}</strong>
2799 <ul>2825 <ul>
2800 <li>2826 <li>
2801 <pre><code>/buttons labels=["Yes","No"] Do you want to continue?</code></pre>2827 <pre><code>/buttons labels=["Yes","No"] Do you want to continue?</code></pre>
2802 </li>2828 </li>
2829 <li>
2830 <pre><code>/buttons labels=[{"text":"Save","icon":"fa-floppy-disk","tooltip":"Save changes"},{"text":"Cancel"}] Choose an action</code></pre>
2831 </li>
2803 </ul>2832 </ul>
2804 </div>2833 </div>
2805 `,2834 `,
@@ -3953,10 +3982,15 @@ async function trimTokensCallback(arg, value) {
3953}3982}
39543983
3955/**3984/**
3956 * Displays a popup with buttons based on provided labels and handles button interactions.3985 * @typedef {object} ButtonLabel
3957 *3986 * @property {string} text - The button text
3987 * @property {string} [tooltip] - Optional tooltip text
3988 * @property {string} [icon] - Optional Font Awesome icon class (e.g., 'fa-floppy-disk')
3989 */
3990
3991/**
3958 * @param {object} args - Named arguments for the command3992 * @param {object} args - Named arguments for the command
3959 * @param {string} args.labels - JSON string of an array of button labels3993 * @param {string} args.labels - JSON string of an array of button labels (strings or ButtonLabel objects)
3960 * @param {string} [args.multiple=false] - Flag indicating if multiple buttons can be toggled3994 * @param {string} [args.multiple=false] - Flag indicating if multiple buttons can be toggled
3961 * @param {string} text - The text content to be displayed within the popup3995 * @param {string} text - The text content to be displayed within the popup
3962 *3996 *
@@ -3966,19 +4000,30 @@ async function trimTokensCallback(arg, value) {
3966 */4000 */
3967async function buttonsCallback(args, text) {4001async function buttonsCallback(args, text) {
3968 try {4002 try {
3969 /** @type {string[]} */4003 /** @type {(string|ButtonLabel)[]} */
3970 const buttons = JSON.parse(resolveVariable(args?.labels));4004 const rawButtons = JSON.parse(resolveVariable(args?.labels));
39714005
3972 if (!Array.isArray(buttons) || !buttons.length) {4006 if (!Array.isArray(rawButtons) || !rawButtons.length) {
3973 console.warn('WARN: Invalid labels provided for /buttons command');4007 console.warn('WARN: Invalid labels provided for /buttons command');
3974 return '';4008 return '';
3975 }4009 }
39764010
4011 // Normalize buttons to ButtonLabel format for consistent handling
4012 /** @type {ButtonLabel[]} */
4013 const buttons = rawButtons.map(btn => typeof btn === 'string' ? { text: btn } : btn);
4014
4015 // Validate raw buttons: each entry must be a string or a non-null object with a string `text` field that has content
4016 if (!buttons.every(btn => typeof btn === 'object' && btn !== null && typeof btn.text === 'string' && btn.text)) {
4017 console.warn('WARN: Invalid button label entry provided for /buttons command: each entry must be a string or an object with a "text" property');
4018 return '';
4019 }
4020
3977 /** @type {Set<number>} */4021 /** @type {Set<number>} */
3978 const multipleToggledState = new Set();4022 const multipleToggledState = new Set();
3979 const multiple = isTrueBoolean(args?.multiple);4023 const multiple = isTrueBoolean(args?.multiple);
39804024
3981 // Map custom buttons to results. Start at 2 because 1 and 0 are reserved for ok and cancel4025 // Map custom buttons to results. Start at 2 because 1 and 0 are reserved for ok and cancel
4026 /** @type {Map<number, ButtonLabel>} */
3982 const resultToButtonMap = new Map(buttons.map((button, index) => [index + 2, button]));4027 const resultToButtonMap = new Map(buttons.map((button, index) => [index + 2, button]));
39834028
3984 return new Promise(async (resolve) => {4029 return new Promise(async (resolve) => {
@@ -4013,7 +4058,25 @@ async function buttonsCallback(args, text) {
4013 buttonElement.dataset.result = String(result);4058 buttonElement.dataset.result = String(result);
4014 }4059 }
40154060
4016 buttonElement.innerText = button;4061 // Add icon if provided
4062 if (button.icon) {
4063 const icon = document.createElement('i');
4064 icon.className = `fa-solid ${button.icon}`;
4065 icon.style.marginRight = '0.5em';
4066 buttonElement.appendChild(icon);
4067 const textSpan = document.createElement('span');
4068 textSpan.textContent = button.text;
4069 buttonElement.appendChild(textSpan);
4070 } else {
4071 buttonElement.innerText = button.text;
4072 }
4073
4074 // Add tooltip if provided
4075 if (button.tooltip) {
4076 buttonElement.title = button.tooltip;
4077 buttonElement.dataset.i18n = '[title]' + button.tooltip;
4078 }
4079
4017 buttonContainer.appendChild(buttonElement);4080 buttonContainer.appendChild(buttonElement);
4018 }4081 }
40194082
@@ -4036,10 +4099,10 @@ async function buttonsCallback(args, text) {
4036 /** @returns {string} @param {string|number|boolean} result */4099 /** @returns {string} @param {string|number|boolean} result */
4037 function getResult(result) {4100 function getResult(result) {
4038 if (multiple) {4101 if (multiple) {
4039 const array = result === POPUP_RESULT.AFFIRMATIVE ? Array.from(multipleToggledState).map(r => resultToButtonMap.get(r) ?? '') : [];4102 const array = result === POPUP_RESULT.AFFIRMATIVE ? Array.from(multipleToggledState).map(r => resultToButtonMap.get(r)?.text ?? '') : [];
4040 return JSON.stringify(array);4103 return JSON.stringify(array);
4041 }4104 }
4042 return typeof result === 'number' ? resultToButtonMap.get(result) ?? '' : '';4105 return typeof result === 'number' ? resultToButtonMap.get(result)?.text ?? '' : '';
4043 }4106 }
4044 });4107 });
4045 } catch {4108 } catch {
@@ -4061,6 +4124,7 @@ async function popupCallback(args, value) {
4061 transparent: isTrueBoolean(args?.transparent),4124 transparent: isTrueBoolean(args?.transparent),
4062 okButton: args?.okButton !== undefined && typeof args?.okButton === 'string' ? args.okButton : t`OK`,4125 okButton: args?.okButton !== undefined && typeof args?.okButton === 'string' ? args.okButton : t`OK`,
4063 cancelButton: args?.cancelButton !== undefined && typeof args?.cancelButton === 'string' ? args.cancelButton : null,4126 cancelButton: args?.cancelButton !== undefined && typeof args?.cancelButton === 'string' ? args.cancelButton : null,
4127 tooltip: args?.tooltip !== undefined && typeof args?.tooltip === 'string' ? args.tooltip : null,
4064 };4128 };
4065 const result = await Popup.show.text(safeHeader, safeBody, popupOptions);4129 const result = await Popup.show.text(safeHeader, safeBody, popupOptions);
4066 return String(requestedResult ? result ?? '' : value);4130 return String(requestedResult ? result ?? '' : value);
@@ -4216,6 +4280,8 @@ async function inputCallback(args, prompt) {
4216 wide: isTrueBoolean(args?.wide),4280 wide: isTrueBoolean(args?.wide),
4217 okButton: args?.okButton !== undefined && typeof args?.okButton === 'string' ? args.okButton : t`Ok`,4281 okButton: args?.okButton !== undefined && typeof args?.okButton === 'string' ? args.okButton : t`Ok`,
4218 rows: args?.rows !== undefined && typeof args?.rows === 'string' ? isNaN(Number(args.rows)) ? 4 : Number(args.rows) : 4,4282 rows: args?.rows !== undefined && typeof args?.rows === 'string' ? isNaN(Number(args.rows)) ? 4 : Number(args.rows) : 4,
4283 placeholder: args?.placeholder !== undefined && typeof args?.placeholder === 'string' ? args.placeholder : null,
4284 tooltip: args?.tooltip !== undefined && typeof args?.tooltip === 'string' ? args.tooltip : null,
4219 };4285 };
4220 // Do not remove this delay, otherwise the prompt will not show up4286 // Do not remove this delay, otherwise the prompt will not show up
4221 await delay(1);4287 await delay(1);
@@ -4468,7 +4534,7 @@ async function echoCallback(args, value) {
4468 if (args.timeout && !isNaN(parseInt(args.timeout))) options.timeOut = parseInt(args.timeout);4534 if (args.timeout && !isNaN(parseInt(args.timeout))) options.timeOut = parseInt(args.timeout);
4469 if (args.extendedTimeout && !isNaN(parseInt(args.extendedTimeout))) options.extendedTimeOut = parseInt(args.extendedTimeout);4535 if (args.extendedTimeout && !isNaN(parseInt(args.extendedTimeout))) options.extendedTimeOut = parseInt(args.extendedTimeout);
4470 if (isTrueBoolean(args.preventDuplicates)) options.preventDuplicates = true;4536 if (isTrueBoolean(args.preventDuplicates)) options.preventDuplicates = true;
4471 if (args.cssClass) options.toastClass = args.cssClass;4537 if (args.cssClass) options.toastClass = [options.toastClass, args.cssClass].filter(Boolean).join(' ');
4472 options.escapeHtml = args.escapeHtml !== undefined ? isTrueBoolean(args.escapeHtml) : true;4538 options.escapeHtml = args.escapeHtml !== undefined ? isTrueBoolean(args.escapeHtml) : true;
44734539
4474 // Prepare possible await handling4540 // Prepare possible await handling