Await for profiles loading before continuing
| @@ -462,6 +462,7 @@ export const event_types = { | |||
| 462 | LLM_FUNCTION_TOOL_CALL: 'llm_function_tool_call', | 462 | LLM_FUNCTION_TOOL_CALL: 'llm_function_tool_call', |
| 463 | ONLINE_STATUS_CHANGED: 'online_status_changed', | 463 | ONLINE_STATUS_CHANGED: 'online_status_changed', |
| 464 | IMAGE_SWIPED: 'image_swiped', | 464 | IMAGE_SWIPED: 'image_swiped', |
| 465 | CONNECTION_PROFILE_LOADED: 'connection_profile_loaded', | ||
| 465 | }; | 466 | }; |
| 466 | 467 | ||
| 467 | export const eventSource = new EventEmitter(); | 468 | export const eventSource = new EventEmitter(); |
| @@ -1,13 +1,13 @@ | |||
| 1 | import { main_api, saveSettingsDebounced } from '../../../script.js'; | 1 | import { event_types, eventSource, main_api, saveSettingsDebounced } from '../../../script.js'; |
| 2 | import { extension_settings, renderExtensionTemplateAsync } from '../../extensions.js'; | 2 | import { extension_settings, renderExtensionTemplateAsync } from '../../extensions.js'; |
| 3 | import { callGenericPopup, Popup, POPUP_TYPE } from '../../popup.js'; | 3 | import { callGenericPopup, Popup, POPUP_TYPE } from '../../popup.js'; |
| 4 | import { executeSlashCommandsWithOptions } from '../../slash-commands.js'; | 4 | import { executeSlashCommandsWithOptions } from '../../slash-commands.js'; |
| 5 | import { SlashCommand } from '../../slash-commands/SlashCommand.js'; | 5 | import { SlashCommand } from '../../slash-commands/SlashCommand.js'; |
| 6 | import { ARGUMENT_TYPE, SlashCommandArgument } from '../../slash-commands/SlashCommandArgument.js'; | 6 | import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js'; |
| 7 | import { enumIcons } from '../../slash-commands/SlashCommandCommonEnumsProvider.js'; | 7 | import { commonEnumProviders, enumIcons } from '../../slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 8 | import { enumTypes, SlashCommandEnumValue } from '../../slash-commands/SlashCommandEnumValue.js'; | 8 | import { enumTypes, SlashCommandEnumValue } from '../../slash-commands/SlashCommandEnumValue.js'; |
| 9 | import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js'; | 9 | import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js'; |
| 10 | import { collapseSpaces, getUniqueName, uuidv4 } from '../../utils.js'; | 10 | import { collapseSpaces, getUniqueName, isFalseBoolean, uuidv4 } from '../../utils.js'; |
| 11 | 11 | ||
| 12 | const MODULE_NAME = 'connection-manager'; | 12 | const MODULE_NAME = 'connection-manager'; |
| 13 | const NONE = '<None>'; | 13 | const NONE = '<None>'; |
| @@ -51,7 +51,7 @@ const FANCY_NAMES = { | |||
| 51 | 51 | ||
| 52 | /** @type {() => SlashCommandEnumValue[]} */ | 52 | /** @type {() => SlashCommandEnumValue[]} */ |
| 53 | const profilesProvider = () => [ | 53 | const profilesProvider = () => [ |
| 54 | new SlashCommandEnumValue(NONE, NONE), | 54 | new SlashCommandEnumValue(NONE), |
| 55 | ...extension_settings.connectionManager.profiles.map(p => new SlashCommandEnumValue(p.name, null, enumTypes.name, enumIcons.server)), | 55 | ...extension_settings.connectionManager.profiles.map(p => new SlashCommandEnumValue(p.name, null, enumTypes.name, enumIcons.server)), |
| 56 | ]; | 56 | ]; |
| 57 | 57 | ||
| @@ -155,7 +155,7 @@ async function createConnectionProfile(forceName = null) { | |||
| 155 | return null; | 155 | return null; |
| 156 | } | 156 | } |
| 157 | 157 | ||
| 158 | if (isNameTaken(name)) { | 158 | if (isNameTaken(name) || name === NONE) { |
| 159 | toastr.error('A profile with the same name already exists.'); | 159 | toastr.error('A profile with the same name already exists.'); |
| 160 | return null; | 160 | return null; |
| 161 | } | 161 | } |
| @@ -304,6 +304,8 @@ async function renderDetailsContent(details, detailsContent) { | |||
| 304 | profiles.addEventListener('change', async function () { | 304 | profiles.addEventListener('change', async function () { |
| 305 | const selectedProfile = profiles.selectedOptions[0]; | 305 | const selectedProfile = profiles.selectedOptions[0]; |
| 306 | if (!selectedProfile) { | 306 | if (!selectedProfile) { |
| 307 | // Safety net for preventing the command getting stuck | ||
| 308 | await eventSource.emit(event_types.CONNECTION_PROFILE_LOADED, NONE); | ||
| 307 | return; | 309 | return; |
| 308 | } | 310 | } |
| 309 | 311 | ||
| @@ -314,6 +316,7 @@ async function renderDetailsContent(details, detailsContent) { | |||
| 314 | 316 | ||
| 315 | // None option selected | 317 | // None option selected |
| 316 | if (!profileId) { | 318 | if (!profileId) { |
| 319 | await eventSource.emit(event_types.CONNECTION_PROFILE_LOADED, NONE); | ||
| 317 | return; | 320 | return; |
| 318 | } | 321 | } |
| 319 | 322 | ||
| @@ -325,6 +328,7 @@ async function renderDetailsContent(details, detailsContent) { | |||
| 325 | } | 328 | } |
| 326 | 329 | ||
| 327 | await applyConnectionProfile(profile); | 330 | await applyConnectionProfile(profile); |
| 331 | await eventSource.emit(event_types.CONNECTION_PROFILE_LOADED, profile.name); | ||
| 328 | }); | 332 | }); |
| 329 | 333 | ||
| 330 | const reloadButton = document.getElementById('reload_connection_profile'); | 334 | const reloadButton = document.getElementById('reload_connection_profile'); |
| @@ -337,6 +341,7 @@ async function renderDetailsContent(details, detailsContent) { | |||
| 337 | } | 341 | } |
| 338 | await applyConnectionProfile(profile); | 342 | await applyConnectionProfile(profile); |
| 339 | await renderDetailsContent(details, detailsContent); | 343 | await renderDetailsContent(details, detailsContent); |
| 344 | await eventSource.emit(event_types.CONNECTION_PROFILE_LOADED, profile.name); | ||
| 340 | toastr.success('Connection profile reloaded', '', { timeOut: 1500 }); | 345 | toastr.success('Connection profile reloaded', '', { timeOut: 1500 }); |
| 341 | }); | 346 | }); |
| 342 | 347 | ||
| @@ -351,6 +356,7 @@ async function renderDetailsContent(details, detailsContent) { | |||
| 351 | saveSettingsDebounced(); | 356 | saveSettingsDebounced(); |
| 352 | renderConnectionProfiles(profiles); | 357 | renderConnectionProfiles(profiles); |
| 353 | await renderDetailsContent(details, detailsContent); | 358 | await renderDetailsContent(details, detailsContent); |
| 359 | await eventSource.emit(event_types.CONNECTION_PROFILE_LOADED, profile.name); | ||
| 354 | }); | 360 | }); |
| 355 | 361 | ||
| 356 | const updateButton = document.getElementById('update_connection_profile'); | 362 | const updateButton = document.getElementById('update_connection_profile'); |
| @@ -364,6 +370,7 @@ async function renderDetailsContent(details, detailsContent) { | |||
| 364 | await updateConnectionProfile(profile); | 370 | await updateConnectionProfile(profile); |
| 365 | await renderDetailsContent(details, detailsContent); | 371 | await renderDetailsContent(details, detailsContent); |
| 366 | saveSettingsDebounced(); | 372 | saveSettingsDebounced(); |
| 373 | await eventSource.emit(event_types.CONNECTION_PROFILE_LOADED, profile.name); | ||
| 367 | toastr.success('Connection profile updated', '', { timeOut: 1500 }); | 374 | toastr.success('Connection profile updated', '', { timeOut: 1500 }); |
| 368 | }); | 375 | }); |
| 369 | 376 | ||
| @@ -372,6 +379,7 @@ async function renderDetailsContent(details, detailsContent) { | |||
| 372 | await deleteConnectionProfile(); | 379 | await deleteConnectionProfile(); |
| 373 | renderConnectionProfiles(profiles); | 380 | renderConnectionProfiles(profiles); |
| 374 | await renderDetailsContent(details, detailsContent); | 381 | await renderDetailsContent(details, detailsContent); |
| 382 | await eventSource.emit(event_types.CONNECTION_PROFILE_LOADED, NONE); | ||
| 375 | }); | 383 | }); |
| 376 | 384 | ||
| 377 | /** @type {HTMLDetailsElement} */ | 385 | /** @type {HTMLDetailsElement} */ |
| @@ -391,7 +399,17 @@ async function renderDetailsContent(details, detailsContent) { | |||
| 391 | isRequired: false, | 399 | isRequired: false, |
| 392 | }), | 400 | }), |
| 393 | ], | 401 | ], |
| 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) => { | ||
| 395 | if (!value || typeof value !== 'string') { | 413 | if (!value || typeof value !== 'string') { |
| 396 | const selectedProfile = extension_settings.connectionManager.selectedProfile; | 414 | const selectedProfile = extension_settings.connectionManager.selectedProfile; |
| 397 | const profile = extension_settings.connectionManager.profiles.find(p => p.id === selectedProfile); | 415 | const profile = extension_settings.connectionManager.profiles.find(p => p.id === selectedProfile); |
| @@ -413,9 +431,16 @@ async function renderDetailsContent(details, detailsContent) { | |||
| 413 | return ''; | 431 | return ''; |
| 414 | } | 432 | } |
| 415 | 433 | ||
| 434 | const shouldAwait = !isFalseBoolean(String(args?.await)); | ||
| 435 | const awaitPromise = new Promise((resolve) => eventSource.once(event_types.CONNECTION_PROFILE_LOADED, resolve)); | ||
| 436 | |||
| 416 | profiles.selectedIndex = Array.from(profiles.options).findIndex(o => o.value === profile.id); | 437 | profiles.selectedIndex = Array.from(profiles.options).findIndex(o => o.value === profile.id); |
| 417 | profiles.dispatchEvent(new Event('change')); | 438 | profiles.dispatchEvent(new Event('change')); |
| 418 | 439 | ||
| 440 | if (shouldAwait) { | ||
| 441 | await awaitPromise; | ||
| 442 | } | ||
| 443 | |||
| 419 | return profile.name; | 444 | return profile.name; |
| 420 | }, | 445 | }, |
| 421 | })); | 446 | })); |