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
7268async function doOnboarding(avatarId) {7268async function doOnboarding(avatarId) {
7269 const template = $('#onboarding_template .onboarding');7269 const template = $('#onboarding_template .onboarding');
7270 let userName = await callGenericPopup(template, POPUP_TYPE.INPUT, currentUser?.name || name1, { rows: 2, wider: true, cancelButton: false });7270 let userName = await callGenericPopup(template, POPUP_TYPE.INPUT, currentUser?.name || name1, { wider: true, cancelButton: false });
72717271
7272 if (userName) {7272 if (userName) {
7273 userName = String(userName).replace('\n', ' ');7273 userName = String(userName).replace('\n', ' ');
public/scripts/extensions/caption/index.js+1 -1
@@ -315,7 +315,7 @@ async function captionMultimodal(base64Img, externalPrompt) {
315 let prompt = externalPrompt || extension_settings.caption.prompt || PROMPT_DEFAULT;315 let prompt = externalPrompt || extension_settings.caption.prompt || PROMPT_DEFAULT;
316316
317 if (!externalPrompt && extension_settings.caption.prompt_ask) {317 if (!externalPrompt && extension_settings.caption.prompt_ask) {
318 const customPrompt = await callGenericPopup('Enter a comment or question:', POPUP_TYPE.INPUT, prompt, { rows: 2 });318 const customPrompt = await callGenericPopup('Enter a comment or question:', POPUP_TYPE.INPUT, prompt, { rows: 4 });
319 if (!customPrompt) {319 if (!customPrompt) {
320 throw new Error('User aborted the caption sending.');320 throw new Error('User aborted the caption sending.');
321 }321 }
public/scripts/extensions/connection-manager/index.js+1 -2
@@ -286,7 +286,7 @@ async function createConnectionProfile(forceName = null) {
286 });286 });
287 const isNameTaken = (n) => extension_settings.connectionManager.profiles.some(p => p.name === n);287 const isNameTaken = (n) => extension_settings.connectionManager.profiles.some(p => p.name === n);
288 const suggestedName = getUniqueName(collapseSpaces(`${profile.api ?? ''} ${profile.model ?? ''} - ${profile.preset ?? ''}`), isNameTaken);288 const suggestedName = getUniqueName(collapseSpaces(`${profile.api ?? ''} ${profile.model ?? ''} - ${profile.preset ?? ''}`), isNameTaken);
289 let name = forceName ?? await callGenericPopup(template, POPUP_TYPE.INPUT, suggestedName, { rows: 2 });289 let name = forceName ?? await callGenericPopup(template, POPUP_TYPE.INPUT, suggestedName);
290 // If it's cancelled, it will be false290 // If it's cancelled, it will be false
291 if (!name) {291 if (!name) {
292 return null;292 return null;
@@ -607,7 +607,6 @@ async function renderDetailsContent(detailsContent) {
607 }, {});607 }, {});
608 const template = $(await renderExtensionTemplateAsync(MODULE_NAME, 'edit', { name: profile.name, settings }));608 const template = $(await renderExtensionTemplateAsync(MODULE_NAME, 'edit', { name: profile.name, settings }));
609 let newName = await callGenericPopup(template, POPUP_TYPE.INPUT, profile.name, {609 let newName = await callGenericPopup(template, POPUP_TYPE.INPUT, profile.name, {
610 rows: 2,
611 customButtons: [{610 customButtons: [{
612 text: t`Save and Update`,611 text: t`Save and Update`,
613 classes: ['popup-button-ok'],612 classes: ['popup-button-ok'],
public/scripts/popup.js+8 -1
@@ -461,6 +461,13 @@ export class Popup {
461 if (input instanceof HTMLInputElement && !shouldSendOnEnter())461 if (input instanceof HTMLInputElement && !shouldSendOnEnter())
462 return;462 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
464 evt.preventDefault();471 evt.preventDefault();
465 evt.stopPropagation();472 evt.stopPropagation();
466 const result = Number(document.activeElement.getAttribute('data-result') ?? this.defaultResult);473 const result = Number(document.activeElement.getAttribute('data-result') ?? this.defaultResult);
@@ -688,7 +695,7 @@ export class Popup {
688 };695 };
689}696}
690697
691class PopupUtils {698export class PopupUtils {
692 /**699 /**
693 * Builds popup content with header and text below700 * Builds popup content with header and text below
694 *701 *
public/scripts/scrapers.js+1 -1
@@ -528,7 +528,7 @@ class YouTubeScraper {
528 async scrape() {528 async scrape() {
529 let lang = '';529 let lang = '';
530 const template = $(await renderExtensionTemplateAsync('attachments', 'youtube-scrape', {}));530 const template = $(await renderExtensionTemplateAsync('attachments', 'youtube-scrape', {}));
531 const videoUrl = await callGenericPopup(template, POPUP_TYPE.INPUT, '', { wide: false, large: false, okButton: 'Scrape', cancelButton: 'Cancel', rows: 2 });531 const videoUrl = await callGenericPopup(template, POPUP_TYPE.INPUT, '', { wide: false, large: false, okButton: 'Scrape', cancelButton: 'Cancel' });
532532
533 template.find('input[name="youtubeLanguageCode"]').on('input', function () {533 template.find('input[name="youtubeLanguageCode"]').on('input', function () {
534 lang = String($(this).val()).trim();534 lang = String($(this).val()).trim();