Merge pull request #2955 from Yokayo/staging Add rename buttons for advanced formatting presets

bdaf7b9258cbb426abeb042549a4b0521c5f3395

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

Signed
5 files changed, +167 -56Showing whitespace changes
public/index.html+6 -3
@@ -3178,9 +3178,10 @@
31783178 </h4>
31793179 <div class="flex-container" title="Select your current Context Template" data-i18n="[title]Select your current Context Template">
31803180 <select id="context_presets" data-preset-manager-for="context" class="flex1 text_pole"></select>
31813181 <div class="flex-container justifyCenter gap3px">
31823182 <input type="file" hidden data-preset-manager-file="context" accept=".json, .settings">
31833183 <i data-preset-manager-update="context" class="menu_button fa-solid fa-save" title="Update current template" data-i18n="[title]Update current template"></i>
3184+ <i data-preset-manager-rename="context" class="menu_button fa-pencil fa-solid" title="Rename current template" data-i18n="[title]Rename current template"></i>
31843185 <i data-preset-manager-new="context" class="menu_button fa-solid fa-file-circle-plus" title="Save template as" data-i18n="[title]Save template as"></i>
31853186 <i data-preset-manager-import="context" class="displayNone menu_button fa-solid fa-file-import" title="Import template" data-i18n="[title]Import template"></i>
31863187 <i data-preset-manager-export="context" class="displayNone menu_button fa-solid fa-file-export" title="Export template" data-i18n="[title]Export template"></i>
@@ -3289,9 +3290,10 @@
32893290
32903291 <div class="flex-container" title="Select your current Instruct Template" data-i18n="[title]Select your current Instruct Template">
32913292 <select id="instruct_presets" data-preset-manager-for="instruct" class="flex1 text_pole"></select>
32923293 <div class="flex-container margin0 justifyCenter gap3px">
32933294 <input type="file" hidden data-preset-manager-file="instruct" accept=".json, .settings">
32943295 <i data-preset-manager-update="instruct" class="menu_button fa-solid fa-save" title="Update current template" data-i18n="[title]Update current template"></i>
3296+ <i data-preset-manager-rename="instruct" class="menu_button fa-pencil fa-solid" title="Rename current template" data-i18n="[title]Rename current template"></i>
32953297 <i data-preset-manager-new="instruct" class="menu_button fa-solid fa-file-circle-plus" title="Save template as" data-i18n="[title]Save template as"></i>
32963298 <i data-preset-manager-import="instruct" class="displayNone menu_button fa-solid fa-file-import" title="Import template" data-i18n="[title]Import template"></i>
32973299 <i data-preset-manager-export="instruct" class="displayNone menu_button fa-solid fa-file-export" title="Export template" data-i18n="[title]Export template"></i>
@@ -3469,9 +3471,10 @@
34693471
34703472 <div class="flex-container" title="Select your current System Prompt" data-i18n="[title]Select your current System Prompt">
34713473 <select id="sysprompt_select" data-preset-manager-for="sysprompt" class="flex1 text_pole"></select>
34723474 <div class="flex-container margin0 justifyCenter gap3px">
34733475 <input type="file" hidden data-preset-manager-file="sysprompt" accept=".json, .settings">
34743476 <i data-preset-manager-update="sysprompt" class="menu_button fa-solid fa-save" title="Update current prompt" data-i18n="[title]Update current prompt"></i>
3477+ <i data-preset-manager-rename="sysprompt" class="menu_button fa-pencil fa-solid" title="Rename current prompt" data-i18n="[title]Rename current prompt"></i>
34753478 <i data-preset-manager-new="sysprompt" class="menu_button fa-solid fa-file-circle-plus" title="Save prompt as" data-i18n="[title]Save prompt as"></i>
34763479 <i data-preset-manager-import="sysprompt" class="displayNone menu_button fa-solid fa-file-import" title="Import template" data-i18n="[title]Import template"></i>
34773480 <i data-preset-manager-export="sysprompt" class="displayNone menu_button fa-solid fa-file-export" title="Export template" data-i18n="[title]Export template"></i>
public/locales/ru-ru.json+71 -5
@@ -1720,7 +1720,7 @@
17201720 "Proxy Preset": "Пресет для прокси",
17211721 "Enter a name:": "Введите название:",
17221722 "Are you sure you want to delete the selected profile?": "Вы точно хотите удалить выбранный профиль?",
17231723 "instruct_enabled": "ВключитьВкл/выкл Instruct-режим",
17241724 "Instruct Template": "Шаблон Instruct-режима",
17251725 "instruct_template_activation_regex_desc": "Автоматически активировать этот шаблон в момент подключения к API или выбора модели, если название модели соответствует этому рег. выражению.",
17261726 "instruct_bind_to_context": "При включении этой опции Шаблон контекста будет выбираться, исходя из выбранного в текущий момент Шаблона Instruct-режима, либо по вашему желанию.",
@@ -1730,11 +1730,11 @@
17301730 "Export Advanced Formatting settings": "Экспорт настроек Расширенного форматирования",
17311731 "Select your current System Prompt": "Выберите текущий системный промпт",
17321732 "Prompt Content": "Текст промпта",
17331733 "Update current prompt": "Обновить текущийСохранить промпт",
17341734 "Save prompt as": "Сохранить как...",
17351735 "Import template": "Импорт шаблона",
17361736 "Export template": "Экспорт шаблона",
17371737 "Restore current prompt": "Восстановить текущий промпт",
17381738 "comma delimited,no spaces between": "через запятую,без пробелов в промежутках",
17391739 "User Message Sequences": "Строки для сообщений пользователя",
17401740 "Assistant Message Sequences": "Строки для сообщений ассистента",
@@ -1757,7 +1757,7 @@
17571757 "Preset was not deleted from server": "Пресет не удалён с сервера",
17581758 "Preset deleted": "Пресет удалён",
17591759 "Delete the preset?": "Удалить пресет?",
17601760 "Preset updated": "Пресет обновлёнсохранён",
17611761 "Entered reverse proxy address is not a valid URL": "Введённый адрес прокси невалиден",
17621762 "An error occurred while counting tokens: Token budget exceeded.": "Ошибка при подсчёте токенов: Превышен бюджет токенов",
17631763 "An error occurred while counting tokens: Invalid character name": "Ошибка при подсчёте токенов: Невалидное имя персонажа",
@@ -1893,5 +1893,71 @@
18931893 "Base": "Основание",
18941894 "DRY_Base_desc": "Определяет, насколько быстро возрастает штраф с увеличением длины строки.",
18951895 "Allowed Length": "Допустимая длина",
18961896 "DRY_Allowed_Length_desc": "Длина повторяющейся строки, при превышении которой DRY начинает налагать штраф.",
1897+ "Invalid data provided for master import": "Глоб. импорт не может быть выполнен по причине невалидности данных",
1898+ "Importing instruct template...": "Импортируем...",
1899+ "Instruct template detected": "Обнаружен шаблон Instruct-режима",
1900+ "Importing as context template...": "Импортируем...",
1901+ "Context template detected": "Обнаружен шаблон контекста",
1902+ "Importing as system prompt...": "Импортируем...",
1903+ "System prompt detected": "Обнаружен системный промпт",
1904+ "Importing as settings preset...": "Импортируем...",
1905+ "Text Completion settings detected": "Обнаружены настройки Text Completion",
1906+ "No valid sections found in imported data": "Не найдено ни одной валидной секции данных",
1907+ "No sections selected for import": "Не выбрано ни одной секции для импорта",
1908+ "Import": "Импортировать",
1909+ "Imported ${0} settings: ${1}": "Импортировано ${0} настроек: ${1}",
1910+ "Export": "Экспортировать",
1911+ "No sections selected for export": "Не выбрано ни одной секции для экспорта",
1912+ "Cannot update GUI preset": "Пресет для GUI обновить невозможно",
1913+ "Template updated": "Шаблон сохранён",
1914+ "Hint: Use a character/group name to bind preset to a specific chat.": "Совет: введите имя персонажа/группы, чтобы привязать пресет к определённому чату.",
1915+ "Preset name:": "Название пресета:",
1916+ "Template name:": "Название шаблона:",
1917+ "Preset saved": "Пресет сохранён",
1918+ "Template saved": "Шаблон сохранён",
1919+ "Preset could not be saved": "Не удалось сохранить пресет",
1920+ "Preset could not be renamed": "Не удалось обновить пресет",
1921+ "Preset renamed": "Пресет переименован",
1922+ "Template renamed": "Шаблон переименован",
1923+ "Cannot delete GUI preset": "Пресет для GUI удалить невозможно",
1924+ "Failed to restore default preset": "Не удалось восстановить пресет по умолчанию",
1925+ "Failed to restore default template": "Не удалось восстановить шаблон по умолчанию",
1926+ "Rename preset": "Переименовать пресет",
1927+ "Rename template": "Переименовать шаблон",
1928+ "Enter a new name:": "Введите новое название:",
1929+ "Preset imported": "Пресет импортирован",
1930+ "Template imported": "Шаблон импортирован",
1931+ "Delete this preset?": "Удалить этот пресет?",
1932+ "Delete this template?": "Удалить этот шаблон?",
1933+ "This action is irreversible and your current settings will be overwritten.": "Отменить это действие невозможно. Ваши текущие настройки будут перезаписаны.",
1934+ "Preset deleted": "Пресет удалён",
1935+ "Template deleted": "Шаблон удалён",
1936+ "Template was not deleted from server": "Шаблон не удалён с сервера",
1937+ "Cannot restore GUI preset": "Пресет для Gui восстановить нельзя",
1938+ "Default preset cannot be restored": "Невозможно восстановить пресет по умолчанию",
1939+ "Default template cannot be restored": "Невозможно восстановить шаблон по умолчанию",
1940+ "Resetting a <b>default preset</b> will restore the default settings": "Сброс <b>стандартного пресета</b> восстановит настройки по умолчанию.",
1941+ "Resetting a <b>default template</b> will restore the default settings.": "Сброс <b>стандартного шаблона</b> восстановит настройки по умолчанию.",
1942+ "Are you sure?": "Вы уверены?",
1943+ "Default preset restored": "Стандартный пресет восстановлен",
1944+ "Default template restored": "Стандартный шаблон восстановлен",
1945+ "Resetting a <b>custom preset</b> will restore to the last saved state.": "Сброс <b>пользовательского пресета</b> откатит его к последнему сохранённому состоянию.",
1946+ "Resetting a <b>custom template</b> will restore to the last saved state.": "Сброс <b>пользовательского шаблона</b> откатит его к последнему сохранённому состоянию.",
1947+ "Preset restored": "Пресет восстановлен",
1948+ "Template restored": "Шаблон восстановлен",
1949+ "Update current template": "Сохранить шаблон",
1950+ "Rename current template": "Переименовать шаблон",
1951+ "Save template as": "Сохранить как...",
1952+ "Restore current template": "Восстановить шаблон",
1953+ "Delete the template": "Удалить шаблон",
1954+ "Rename current prompt": "Переименовать промпт",
1955+ "Select your current Context Template": "Выберите активный шаблон контекста",
1956+ "Select your current Instruct Template": "Выберите активный шаблон Instruct-режима",
1957+ "and connect to an": "и подключитесь к",
1958+ "You can add more": "Можете добавить больше",
1959+ "from other websites": "с других сайтов.",
1960+ "Go to the": "Загляните в",
1961+ "to install additional features.": ", чтобы установить разные дополнительные ресурсы.",
1962+ "or_welcome": "; также доступен"
18971963}
public/script.js+1 -1
@@ -6755,7 +6755,7 @@ export async function saveSettings(type) {
67556755 eventSource.emit(event_types.SETTINGS_UPDATED);
67566756 },
67576757 error: function (jqXHR, exception) {
67586758 toastr.error(t`Check the server connection and reload the page to prevent data loss.t`, t`Settings could not be saved`);
67596759 console.log(exception);
67606760 console.log(jqXHR);
67616761 },
public/scripts/preset-manager.js+85 -43
@@ -34,6 +34,7 @@ import {
3434 textgenerationwebui_settings as textgen_settings,
3535} from './textgen-settings.js';
3636import { download, parseJsonFile, waitUntilCondition } from './utils.js';
37+import { t } from './i18n.js';
3738
3839const presetManagers = {};
3940
@@ -194,32 +195,32 @@ class PresetManager {
194195 */
195196 static async performMasterImport(data, fileName) {
196197 if (!data || typeof data !== 'object') {
197198 toastr.error('t`Invalid data provided for master import'`);
198199 return;
199200 }
200201
201202 // Check for legacy file imports
202203 // 1. Instruct Template
203204 if (this.isPossiblyInstructData(data)) {
204205 toastr.info('t`Importing instruct template...'`, 't`Instruct template detected'`);
205206 return await getPresetManager('instruct').savePreset(data.name, data);
206207 }
207208
208209 // 2. Context Template
209210 if (this.isPossiblyContextData(data)) {
210211 toastr.info('t`Importing as context template...'`, 't`Context template detected'`);
211212 return await getPresetManager('context').savePreset(data.name, data);
212213 }
213214
214215 // 3. System Prompt
215216 if (this.isPossiblySystemPromptData(data)) {
216217 toastr.info('t`Importing as system prompt...'`, 't`System prompt detected'`);
217218 return await getPresetManager('sysprompt').savePreset(data.name, data);
218219 }
219220
220221 // 4. Text Completion settings
221222 if (this.isPossiblyTextCompletionData(data)) {
222223 toastr.info('t`Importing as settings preset...'`, 't`Text Completion settings detected'`);
223224 return await getPresetManager('textgenerationwebui').savePreset(fileName, data);
224225 }
225226
@@ -231,7 +232,7 @@ class PresetManager {
231232 }
232233
233234 if (validSections.length === 0) {
234235 toastr.error('t`No valid sections found in imported data'`);
235236 return;
236237 }
237238
@@ -242,8 +243,8 @@ class PresetManager {
242243
243244 const html = $(await renderTemplateAsync('masterImport', { sections: sectionNames }));
244245 const popup = new Popup(html, POPUP_TYPE.CONFIRM, '', {
245246 okButton: 't`Import'`,
246247 cancelButton: 't`Cancel'`,
247248 });
248249
249250 const result = await popup.show();
@@ -257,7 +258,7 @@ class PresetManager {
257258 const confirmedSections = html.find('input:checked').map((_, el) => el instanceof HTMLInputElement && el.value).get();
258259
259260 if (confirmedSections.length === 0) {
260261 toastr.info('t`No sections selected for import'`);
261262 return;
262263 }
263264
@@ -270,7 +271,7 @@ class PresetManager {
270271 }
271272 }
272273
273274 toastr.success(t`Imported ${importedSections.length} settings: ${importedSections.join(', ')}`);
274275 }
275276
276277 /**
@@ -285,8 +286,8 @@ class PresetManager {
285286 const html = $(await renderTemplateAsync('masterExport', { sections: sectionNames }));
286287
287288 const popup = new Popup(html, POPUP_TYPE.CONFIRM, '', {
288289 okButton: 't`Export'`,
289290 cancelButton: 't`Cancel'`,
290291 });
291292
292293 const result = await popup.show();
@@ -300,7 +301,7 @@ class PresetManager {
300301 const data = {};
301302
302303 if (confirmedSections.length === 0) {
303304 toastr.info('t`No sections selected for export'`);
304305 return;
305306 }
306307
@@ -366,21 +367,21 @@ class PresetManager {
366367 console.log(selected);
367368
368369 if (selected.val() == 'gui') {
369370 toastr.info('t`Cannot update GUI preset'`);
370371 return;
371372 }
372373
373374 const name = selected.text();
374375 await this.savePreset(name);
375376
376377 const successToast = !this.isAdvancedFormatting() ? 't`Preset updated'` : 't`Template updated'`;
377378 toastr.success(successToast);
378379 }
379380
380381 async savePresetAs() {
381382 const inputValue = this.getSelectedPresetName();
382383 const popupText = !this.isAdvancedFormatting() ? '<h4>' + t`Hint: Use a character/group name to bind preset to a specific chat.` + '</h4>' : '';
383384 const headerText = !this.isAdvancedFormatting() ? 't`Preset name:'` : 't`Template name:'`;
384385 const name = await Popup.show.input(headerText, popupText, inputValue);
385386 if (!name) {
386387 console.log('Preset name not provided');
@@ -389,7 +390,7 @@ class PresetManager {
389390
390391 await this.savePreset(name);
391392
392393 const successToast = !this.isAdvancedFormatting() ? 't`Preset saved'` : 't`Template saved'`;
393394 toastr.success(successToast);
394395 }
395396
@@ -411,7 +412,7 @@ class PresetManager {
411412 });
412413
413414 if (!response.ok) {
414415 toastr.error('t`Check the server connection and reload the page to prevent data loss.'`, 't`Preset could not be saved'`);
415416 console.error('Preset could not be saved', response);
416417 throw new Error('Preset could not be saved');
417418 }
@@ -422,6 +423,19 @@ class PresetManager {
422423 this.updateList(name, preset);
423424 }
424425
426+ async renamePreset(newName) {
427+ const oldName = this.getSelectedPresetName();
428+ try {
429+ await this.savePreset(newName);
430+ await this.deletePreset(oldName);
431+ } catch (error) {
432+ toastr.error(t`Check the server connection and reload the page to prevent data loss.`, t`Preset could not be renamed`);
433+ console.error('Preset could not be renamed', error);
434+ throw new Error('Preset could not be renamed');
435+ }
436+
437+ }
438+
425439 getPresetList() {
426440 let presets = [];
427441 let preset_names = {};
@@ -585,13 +599,14 @@ class PresetManager {
585599 return settings;
586600 }
587601
588- async deleteCurrentPreset() {
602+ // pass no arguments to delete current preset
603+ async deletePreset(name) {
589604 const { preset_names, presets } = this.getPresetList();
590- const value = this.getSelectedPreset();
605+ const value = name ? (this.isKeyedApi() ? this.findPreset(name) : name) : this.getSelectedPreset();
591606 const nameToDelete = name || this.getSelectedPresetName();
592607
593608 if (value == 'gui') {
594609 toastr.info('t`Cannot delete GUI preset'`);
595610 return;
596611 }
597612
@@ -605,7 +620,10 @@ class PresetManager {
605620 delete preset_names[nameToDelete];
606621 }
607622
608- if (Object.keys(preset_names).length) {
623+ // switch in UI only when deleting currently selected preset
624+ const switchPresets = !name || this.getSelectedPresetName() == name;
625+
626+ if (Object.keys(preset_names).length && switchPresets) {
609627 const nextPresetName = Object.keys(preset_names)[0];
610628 const newValue = preset_names[nextPresetName];
611629 $(this.select).find(`option[value="${newValue}"]`).attr('selected', 'true');
@@ -629,7 +647,7 @@ class PresetManager {
629647 });
630648
631649 if (!response.ok) {
632650 const errorToast = !this.isAdvancedFormatting() ? 't`Failed to restore default preset'` : 't`Failed to restore default template'`;
633651 toastr.error(errorToast);
634652 return;
635653 }
@@ -721,7 +739,8 @@ async function waitForConnection() {
721739export async function initPresetManager() {
722740 eventSource.on(event_types.CHAT_CHANGED, autoSelectPreset);
723741 registerPresetManagers();
724742 SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'preset',
743+ name: 'preset',
725744 callback: presetCommandCallback,
726745 returns: 'current preset',
727746 unnamedArgumentList: [
@@ -774,6 +793,29 @@ export async function initPresetManager() {
774793 await presetManager.savePresetAs();
775794 });
776795
796+ $(document).on('click', '[data-preset-manager-rename]', async function () {
797+ const apiId = $(this).data('preset-manager-rename');
798+ const presetManager = getPresetManager(apiId);
799+
800+ if (!presetManager) {
801+ console.warn(`Preset Manager not found for API: ${apiId}`);
802+ return;
803+ }
804+
805+ const popupHeader = !presetManager.isAdvancedFormatting() ? t`Rename preset` : t`Rename template`;
806+ const oldName = presetManager.getSelectedPresetName();
807+ const newName = await Popup.show.input(popupHeader, t`Enter a new name:`, oldName);
808+ if (!newName || oldName === newName) {
809+ console.debug(!presetManager.isAdvancedFormatting() ? 'Preset rename cancelled' : 'Template rename cancelled');
810+ return;
811+ }
812+
813+ await presetManager.renamePreset(newName);
814+
815+ const successToast = !presetManager.isAdvancedFormatting() ? t`Preset renamed` : t`Template renamed`;
816+ toastr.success(successToast);
817+ });
818+
777819 $(document).on('click', '[data-preset-manager-export]', async function () {
778820 const apiId = $(this).data('preset-manager-export');
779821 const presetManager = getPresetManager(apiId);
@@ -816,7 +858,7 @@ export async function initPresetManager() {
816858 data['name'] = name;
817859
818860 await presetManager.savePreset(name, data);
819861 const successToast = !presetManager.isAdvancedFormatting() ? 't`Preset imported'` : 't`Template imported'`;
820862 toastr.success(successToast);
821863 e.target.value = null;
822864 });
@@ -830,19 +872,19 @@ export async function initPresetManager() {
830872 return;
831873 }
832874
833875 const headerText = !presetManager.isAdvancedFormatting() ? 't`Delete this preset?'` : 't`Delete this template?'`;
834876 const confirm = await Popup.show.confirm(headerText, 't`This action is irreversible and your current settings will be overwritten.'`);
835877 if (!confirm) {
836878 return;
837879 }
838880
839881 const result = await presetManager.deleteCurrentPresetdeletePreset();
840882
841883 if (result) {
842884 const successToast = !presetManager.isAdvancedFormatting() ? 't`Preset deleted'` : 't`Template deleted'`;
843885 toastr.success(successToast);
844886 } else {
845887 const warningToast = !presetManager.isAdvancedFormatting() ? 't`Preset was not deleted from server'` : 't`Template was not deleted from server'`;
846888 toastr.warning(warningToast);
847889 }
848890
@@ -862,7 +904,7 @@ export async function initPresetManager() {
862904 const data = await presetManager.getDefaultPreset(name);
863905
864906 if (name == 'gui') {
865907 toastr.info('t`Cannot restore GUI preset'`);
866908 return;
867909 }
868910
@@ -872,37 +914,37 @@ export async function initPresetManager() {
872914
873915 if (data.isDefault) {
874916 if (Object.keys(data.preset).length === 0) {
875917 const errorToast = !presetManager.isAdvancedFormatting() ? 't`Default preset cannot be restored'` : 't`Default template cannot be restored'`;
876918 toastr.error(errorToast);
877919 return;
878920 }
879921
880922 const confirmText = !presetManager.isAdvancedFormatting()
881923 ? 't`Resetting a <b>default preset</b> will restore the default settings.'`
882924 : 't`Resetting a <b>default template</b> will restore the default settings.'`;
883925 const confirm = await Popup.show.confirm('t`Are you sure?'`, confirmText);
884926 if (!confirm) {
885927 return;
886928 }
887929
888930 await presetManager.deleteCurrentPresetdeletePreset();
889931 await presetManager.savePreset(name, data.preset);
890932 const option = presetManager.findPreset(name);
891933 presetManager.selectPreset(option);
892934 const successToast = !presetManager.isAdvancedFormatting() ? 't`Default preset restored'` : 't`Default template restored'`;
893935 toastr.success(successToast);
894936 } else {
895937 const confirmText = !presetManager.isAdvancedFormatting()
896938 ? 't`Resetting a <b>custom preset</b> will restore to the last saved state.'`
897939 : 't`Resetting a <b>custom template</b> will restore to the last saved state.'`;
898940 const confirm = await Popup.show.confirm('t`Are you sure?'`, confirmText);
899941 if (!confirm) {
900942 return;
901943 }
902944
903945 const option = presetManager.findPreset(name);
904946 presetManager.selectPreset(option);
905947 const successToast = !presetManager.isAdvancedFormatting() ? 't`Preset restored'` : 't`Template restored'`;
906948 toastr.success(successToast);
907949 }
908950 });
public/scripts/templates/welcome.html+4 -4
@@ -10,7 +10,7 @@
1010 <span data-i18n="Click _space">Click </span>
1111 <button class="menu_button menu_button_icon drawer-opener inline-flex" data-target="sys-settings-button">
1212 <i class="fa-solid fa-plug"></i>
1313 <span data-i18n="[title]API Connections">API Connections</span>
1414 </button>
1515 <span data-i18n="and connect to an">and connect to an</span>
1616 <a href="https://docs.sillytavern.app/usage/api-connections/" target="_blank">
@@ -21,7 +21,7 @@
2121 <span data-i18n="Click _space">Click </span>
2222 <button class="menu_button menu_button_icon drawer-opener inline-flex" data-target="rightNavHolder">
2323 <i class="fa-solid fa-address-card"></i>
2424 <span data-i18n="[title]Character Management">Character Management</span>
2525 </button>
2626 <span data-i18n="and pick a character."> and pick a character.</span>
2727 </li>
@@ -33,7 +33,7 @@
3333 <i class="fa-solid fa-image-portrait"></i>
3434 <span data-i18n="Sample characters">Sample characters</span>
3535 </button>
3636 <span data-i18n="oror_welcome">or</span>
3737 <button class="external_import_button menu_button menu_button_icon inline-flex">
3838 <i class="fa-solid fa-cloud-arrow-down"></i>
3939 <span data-i18n="Import Characters">Import characters</span>
@@ -48,7 +48,7 @@
4848
4949 <button class="menu_button menu_button_icon drawer-opener inline-flex" data-target="extensions-settings-button">
5050 <i class="fa-solid fa-cubes"></i>
5151 <span data-i18n="[title]Extensions">Extensions</span>
5252 </button>
5353
5454 <span data-i18n="to install additional features.">to install additional features.</span>