Feat: Improve multiline input handling in popups (#4756) * feat: improve multiline input handling in popups - Added Ctrl+Enter requirement for submission in multiline input popups to prevent accidental sends - Exported PopupUtils class for external use * refactor: remove redundant higher/different rows from input popups - Removed rows: 2 from callGenericPopup calls where default behavior is sufficient - Increased rows from 2 to 4 in caption extension for better multiline input experience

81367629fe2d3d1baf3df71f2cf735ab0aa9ea6e

Wolfsblvt <wolfsblvt@gmail.com>

Signed
5 files changed, +12 -6Ignore whitespace
public/script.js+1 -1
@@ -7267,7 +7267,7 @@ export function setUserName(value, { toastPersonaNameChange = true } = {}) {
72677267
72687268async function doOnboarding(avatarId) {
72697269 const template = $('#onboarding_template .onboarding');
72707270 let userName = await callGenericPopup(template, POPUP_TYPE.INPUT, currentUser?.name || name1, { rows: 2, wider: true, cancelButton: false });
72717271
72727272 if (userName) {
72737273 userName = String(userName).replace('\n', ' ');
public/scripts/extensions/caption/index.js+1 -1
@@ -315,7 +315,7 @@ async function captionMultimodal(base64Img, externalPrompt) {
315315 let prompt = externalPrompt || extension_settings.caption.prompt || PROMPT_DEFAULT;
316316
317317 if (!externalPrompt && extension_settings.caption.prompt_ask) {
318318 const customPrompt = await callGenericPopup('Enter a comment or question:', POPUP_TYPE.INPUT, prompt, { rows: 24 });
319319 if (!customPrompt) {
320320 throw new Error('User aborted the caption sending.');
321321 }
public/scripts/extensions/connection-manager/index.js+1 -2
@@ -286,7 +286,7 @@ async function createConnectionProfile(forceName = null) {
286286 });
287287 const isNameTaken = (n) => extension_settings.connectionManager.profiles.some(p => p.name === n);
288288 const suggestedName = getUniqueName(collapseSpaces(`${profile.api ?? ''} ${profile.model ?? ''} - ${profile.preset ?? ''}`), isNameTaken);
289289 let name = forceName ?? await callGenericPopup(template, POPUP_TYPE.INPUT, suggestedName, { rows: 2 });
290290 // If it's cancelled, it will be false
291291 if (!name) {
292292 return null;
@@ -607,7 +607,6 @@ async function renderDetailsContent(detailsContent) {
607607 }, {});
608608 const template = $(await renderExtensionTemplateAsync(MODULE_NAME, 'edit', { name: profile.name, settings }));
609609 let newName = await callGenericPopup(template, POPUP_TYPE.INPUT, profile.name, {
610- rows: 2,
611610 customButtons: [{
612611 text: t`Save and Update`,
613612 classes: ['popup-button-ok'],
public/scripts/popup.js+8 -1
@@ -461,6 +461,13 @@ export class Popup {
461461 if (input instanceof HTMLInputElement && !shouldSendOnEnter())
462462 return;
463463
464+ // If this is a multiline input popup, we should still not simply send on enter, that'd be weird.
465+ // Let's still make it possible if CTRL is toggled though
466+ if ((textarea instanceof HTMLTextAreaElement || input instanceof HTMLInputElement)
467+ && !evt.ctrlKey && this.mainInput.rows > 1) {
468+ return;
469+ }
470+
464471 evt.preventDefault();
465472 evt.stopPropagation();
466473 const result = Number(document.activeElement.getAttribute('data-result') ?? this.defaultResult);
@@ -688,7 +695,7 @@ export class Popup {
688695 };
689696}
690697
691698export class PopupUtils {
692699 /**
693700 * Builds popup content with header and text below
694701 *
public/scripts/scrapers.js+1 -1
@@ -528,7 +528,7 @@ class YouTubeScraper {
528528 async scrape() {
529529 let lang = '';
530530 const template = $(await renderExtensionTemplateAsync('attachments', 'youtube-scrape', {}));
531531 const videoUrl = await callGenericPopup(template, POPUP_TYPE.INPUT, '', { wide: false, large: false, okButton: 'Scrape', cancelButton: 'Cancel', rows: 2 });
532532
533533 template.find('input[name="youtubeLanguageCode"]').on('input', function () {
534534 lang = String($(this).val()).trim();