diff --git a/public/index.html b/public/index.html index 6c56b3f4d..a5f7041f7 100644 --- a/public/index.html +++ b/public/index.html @@ -1294,8 +1294,8 @@ TFS
- - + +
@@ -1477,8 +1477,8 @@
Exponent - - + +
diff --git a/public/script.js b/public/script.js index 999d15078..1029b53b6 100644 --- a/public/script.js +++ b/public/script.js @@ -606,7 +606,6 @@ export const printCharactersDebounced = debounce(() => { printCharacters(false); export const system_message_types = { HELP: 'help', WELCOME: 'welcome', - GROUP: 'group', EMPTY: 'empty', GENERIC: 'generic', NARRATOR: 'narrator', @@ -699,14 +698,6 @@ async function getSystemMessages() { uses_system_ui: true, mes: await renderTemplateAsync('welcome', { displayVersion }), }, - group: { - name: systemUserName, - force_avatar: system_avatar, - is_user: false, - is_system: true, - is_group: true, - mes: 'Group chat created. Say \'Hi\' to lovely people!', - }, empty: { name: systemUserName, force_avatar: system_avatar, diff --git a/public/scripts/bookmarks.js b/public/scripts/bookmarks.js index 83a2b16d4..61baf34a4 100644 --- a/public/scripts/bookmarks.js +++ b/public/scripts/bookmarks.js @@ -318,16 +318,6 @@ export async function convertSoloToGroupChat() { const groupChat = chat.slice(); const genIdFirst = Date.now(); - // Add something if the chat is empty - if (groupChat.length === 0) { - const newMessage = { - ...system_messages[system_message_types.GROUP], - send_date: getMessageTimeStamp(), - extra: { type: system_message_types.GROUP }, - }; - groupChat.push(newMessage); - } - for (let index = 0; index < groupChat.length; index++) { const message = groupChat[index]; diff --git a/public/scripts/extensions.js b/public/scripts/extensions.js index a197e3a41..773990fab 100644 --- a/public/scripts/extensions.js +++ b/public/scripts/extensions.js @@ -7,7 +7,7 @@ import { renderTemplate, renderTemplateAsync } from './templates.js'; import { delay, isSubsetOf, sanitizeSelector, setValueByPath } from './utils.js'; import { getContext } from './st-context.js'; import { isAdmin } from './user.js'; -import { t } from './i18n.js'; +import { addLocaleData, getCurrentLocale, t } from './i18n.js'; import { debounce_timeout } from './constants.js'; import { accountStorage } from './util/AccountStorage.js'; @@ -384,7 +384,7 @@ async function activateExtensions() { if (meetsModuleRequirements && !isDisabled) { try { console.debug('Activating extension', name); - const promise = Promise.all([addExtensionScript(name, manifest), addExtensionStyle(name, manifest)]); + const promise = addExtensionLocale(name, manifest).finally(() => Promise.all([addExtensionScript(name, manifest), addExtensionStyle(name, manifest)])); await promise .then(() => activeExtensions.add(name)) .catch(err => console.log('Could not activate extension', name, err)); @@ -576,6 +576,42 @@ function addExtensionScript(name, manifest) { }); } +/** + * Adds a localization data for an extension. + * @param {string} name Extension name + * @param {object} manifest Manifest object + */ +function addExtensionLocale(name, manifest) { + // No i18n data in the manifest + if (!manifest.i18n || typeof manifest.i18n !== 'object') { + return Promise.resolve(); + } + + const currentLocale = getCurrentLocale(); + const localeFile = manifest.i18n[currentLocale]; + + // Manifest doesn't provide a locale file for the current locale + if (!localeFile) { + return Promise.resolve(); + } + + return fetch(`/scripts/extensions/${name}/${localeFile}`) + .then(async response => { + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + const data = await response.json(); + + if (data && typeof data === 'object') { + addLocaleData(currentLocale, data); + } + }) + .catch(err => { + console.log('Could not load extension locale data for ' + name, err); + }); +} + /** * Generates HTML string for displaying an extension in the UI. * diff --git a/public/scripts/group-chats.js b/public/scripts/group-chats.js index f191ff983..d1431d74f 100644 --- a/public/scripts/group-chats.js +++ b/public/scripts/group-chats.js @@ -228,9 +228,6 @@ export async function getGroupChat(groupId, reload = false) { chat.splice(0, chat.length, ...data); await printMessages(); } else { - sendSystemMessage(system_message_types.GROUP, '', { isSmallSys: true }); - await eventSource.emit(event_types.MESSAGE_RECEIVED, (chat.length - 1)); - await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, (chat.length - 1)); if (group && Array.isArray(group.members)) { for (let member of group.members) { const character = characters.find(x => x.avatar === member || x.name === member); diff --git a/public/scripts/i18n.js b/public/scripts/i18n.js index 39dccda4d..9dd213b04 100644 --- a/public/scripts/i18n.js +++ b/public/scripts/i18n.js @@ -11,6 +11,30 @@ var localeData; export const getCurrentLocale = () => localeFile; +/** + * Adds additional localization data to the current locale file. + * @param {string} localeId Locale ID (e.g. 'fr-fr' or 'zh-cn') + * @param {Record} data Localization data to add + */ +export function addLocaleData(localeId, data) { + if (!localeData) { + console.warn('Localization data not loaded yet. Additional data will not be added.'); + return; + } + + if (localeId !== localeFile) { + console.debug('Ignoring addLocaleData call for different locale', localeId); + return; + } + + for (const [key, value] of Object.entries(data)) { + // Overrides for default locale data are not allowed + if (!Object.hasOwn(localeData, key)) { + localeData[key] = value; + } + } +} + /** * An observer that will check if any new i18n elements are added to the document * @type {MutationObserver} diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js index ba8669a71..b06b2dacd 100644 --- a/public/scripts/power-user.js +++ b/public/scripts/power-user.js @@ -644,7 +644,6 @@ async function CreateZenSliders(elmnt) { } if (sliderID == 'min_temp_textgenerationwebui' || sliderID == 'max_temp_textgenerationwebui' || - sliderID == 'dynatemp_exponent_textgenerationwebui' || sliderID == 'smoothing_curve_textgenerationwebui' || sliderID == 'smoothing_factor_textgenerationwebui' || sliderID == 'dry_multiplier_textgenerationwebui' || diff --git a/public/scripts/st-context.js b/public/scripts/st-context.js index 2393a4800..ad7021622 100644 --- a/public/scripts/st-context.js +++ b/public/scripts/st-context.js @@ -54,7 +54,7 @@ import { writeExtensionField, } from './extensions.js'; import { groups, openGroupChat, selected_group } from './group-chats.js'; -import { t, translate } from './i18n.js'; +import { addLocaleData, getCurrentLocale, t, translate } from './i18n.js'; import { hideLoader, showLoader } from './loader.js'; import { MacrosParser } from './macros.js'; import { getChatCompletionModel, oai_settings } from './openai.js'; @@ -165,6 +165,8 @@ export function getContext() { isMobile, t, translate, + getCurrentLocale, + addLocaleData, tags, tagMap: tag_map, menuType: menu_type, diff --git a/src/endpoints/extensions.js b/src/endpoints/extensions.js index 95e5e7eba..542be64ba 100644 --- a/src/endpoints/extensions.js +++ b/src/endpoints/extensions.js @@ -323,7 +323,7 @@ router.get('/discover', jsonParser, function (request, response) { // Combine all extensions const allExtensions = [...builtInExtensions, ...userExtensions, ...globalExtensions]; - console.info('Extensions available for', request.user.profile.handle, allExtensions); + console.debug('Extensions available for', request.user.profile.handle, allExtensions); return response.send(allExtensions); });