Await for profiles loading before continuing

10ddf77948e0d93af1f14a8ea9801ce8a4b5d397

Cohee <18619528+Cohee1207@users.noreply.github.com>

2 files changed, +33 -7Ignore whitespace
public/script.js+1 -0
@@ -462,6 +462,7 @@ export const event_types = {
462462 LLM_FUNCTION_TOOL_CALL: 'llm_function_tool_call',
463463 ONLINE_STATUS_CHANGED: 'online_status_changed',
464464 IMAGE_SWIPED: 'image_swiped',
465+ CONNECTION_PROFILE_LOADED: 'connection_profile_loaded',
465466};
466467
467468export const eventSource = new EventEmitter();
public/scripts/extensions/connection-manager/index.js+32 -7
@@ -1,13 +1,13 @@
11import { event_types, eventSource, main_api, saveSettingsDebounced } from '../../../script.js';
22import { extension_settings, renderExtensionTemplateAsync } from '../../extensions.js';
33import { callGenericPopup, Popup, POPUP_TYPE } from '../../popup.js';
44import { executeSlashCommandsWithOptions } from '../../slash-commands.js';
55import { SlashCommand } from '../../slash-commands/SlashCommand.js';
66import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js';
77import { commonEnumProviders, enumIcons } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';
88import { enumTypes, SlashCommandEnumValue } from '../../slash-commands/SlashCommandEnumValue.js';
99import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';
1010import { collapseSpaces, getUniqueName, isFalseBoolean, uuidv4 } from '../../utils.js';
1111
1212const MODULE_NAME = 'connection-manager';
1313const NONE = '<None>';
@@ -51,7 +51,7 @@ const FANCY_NAMES = {
5151
5252/** @type {() => SlashCommandEnumValue[]} */
5353const profilesProvider = () => [
5454 new SlashCommandEnumValue(NONE, NONE),
5555 ...extension_settings.connectionManager.profiles.map(p => new SlashCommandEnumValue(p.name, null, enumTypes.name, enumIcons.server)),
5656];
5757
@@ -155,7 +155,7 @@ async function createConnectionProfile(forceName = null) {
155155 return null;
156156 }
157157
158158 if (isNameTaken(name) || name === NONE) {
159159 toastr.error('A profile with the same name already exists.');
160160 return null;
161161 }
@@ -304,6 +304,8 @@ async function renderDetailsContent(details, detailsContent) {
304304 profiles.addEventListener('change', async function () {
305305 const selectedProfile = profiles.selectedOptions[0];
306306 if (!selectedProfile) {
307+ // Safety net for preventing the command getting stuck
308+ await eventSource.emit(event_types.CONNECTION_PROFILE_LOADED, NONE);
307309 return;
308310 }
309311
@@ -314,6 +316,7 @@ async function renderDetailsContent(details, detailsContent) {
314316
315317 // None option selected
316318 if (!profileId) {
319+ await eventSource.emit(event_types.CONNECTION_PROFILE_LOADED, NONE);
317320 return;
318321 }
319322
@@ -325,6 +328,7 @@ async function renderDetailsContent(details, detailsContent) {
325328 }
326329
327330 await applyConnectionProfile(profile);
331+ await eventSource.emit(event_types.CONNECTION_PROFILE_LOADED, profile.name);
328332 });
329333
330334 const reloadButton = document.getElementById('reload_connection_profile');
@@ -337,6 +341,7 @@ async function renderDetailsContent(details, detailsContent) {
337341 }
338342 await applyConnectionProfile(profile);
339343 await renderDetailsContent(details, detailsContent);
344+ await eventSource.emit(event_types.CONNECTION_PROFILE_LOADED, profile.name);
340345 toastr.success('Connection profile reloaded', '', { timeOut: 1500 });
341346 });
342347
@@ -351,6 +356,7 @@ async function renderDetailsContent(details, detailsContent) {
351356 saveSettingsDebounced();
352357 renderConnectionProfiles(profiles);
353358 await renderDetailsContent(details, detailsContent);
359+ await eventSource.emit(event_types.CONNECTION_PROFILE_LOADED, profile.name);
354360 });
355361
356362 const updateButton = document.getElementById('update_connection_profile');
@@ -364,6 +370,7 @@ async function renderDetailsContent(details, detailsContent) {
364370 await updateConnectionProfile(profile);
365371 await renderDetailsContent(details, detailsContent);
366372 saveSettingsDebounced();
373+ await eventSource.emit(event_types.CONNECTION_PROFILE_LOADED, profile.name);
367374 toastr.success('Connection profile updated', '', { timeOut: 1500 });
368375 });
369376
@@ -372,6 +379,7 @@ async function renderDetailsContent(details, detailsContent) {
372379 await deleteConnectionProfile();
373380 renderConnectionProfiles(profiles);
374381 await renderDetailsContent(details, detailsContent);
382+ await eventSource.emit(event_types.CONNECTION_PROFILE_LOADED, NONE);
375383 });
376384
377385 /** @type {HTMLDetailsElement} */
@@ -391,7 +399,17 @@ async function renderDetailsContent(details, detailsContent) {
391399 isRequired: false,
392400 }),
393401 ],
394- callback: async (_args, value) => {
402+ namedArgumentList: [
403+ SlashCommandNamedArgument.fromProps({
404+ name: 'await',
405+ description: 'Wait for the connection profile to be applied before returning.',
406+ isRequired: false,
407+ typeList: [ARGUMENT_TYPE.BOOLEAN],
408+ defaultValue: 'true',
409+ enumList: commonEnumProviders.boolean('trueFalse')(),
410+ }),
411+ ],
412+ callback: async (args, value) => {
395413 if (!value || typeof value !== 'string') {
396414 const selectedProfile = extension_settings.connectionManager.selectedProfile;
397415 const profile = extension_settings.connectionManager.profiles.find(p => p.id === selectedProfile);
@@ -413,9 +431,16 @@ async function renderDetailsContent(details, detailsContent) {
413431 return '';
414432 }
415433
434+ const shouldAwait = !isFalseBoolean(String(args?.await));
435+ const awaitPromise = new Promise((resolve) => eventSource.once(event_types.CONNECTION_PROFILE_LOADED, resolve));
436+
416437 profiles.selectedIndex = Array.from(profiles.options).findIndex(o => o.value === profile.id);
417438 profiles.dispatchEvent(new Event('change'));
418439
440+ if (shouldAwait) {
441+ await awaitPromise;
442+ }
443+
419444 return profile.name;
420445 },
421446 }));