Work on tl

7f9023d3c21cb026914efd5489add59b71e21297

Yokayo <nonameutubecomm@gmail.com>

13 files changed, +286 -145Showing whitespace changes
public/locales/ru-ru.json+142 -5
@@ -451,8 +451,8 @@
451 "Creator's Notes": "Заметки создателя",451 "Creator's Notes": "Заметки создателя",
452 "A-Z": "A-Z",452 "A-Z": "A-Z",
453 "Z-A": "Z-A",453 "Z-A": "Z-A",
454 "Newest": "Новейшие",454 "Newest": "Сначала новые",
455 "Oldest": "Старейшие",455 "Oldest": "Сначала старые",
456 "Favorites": "Избранные",456 "Favorites": "Избранные",
457 "Recent": "Последние",457 "Recent": "Последние",
458 "Most chats": "Больше всего чатов",458 "Most chats": "Больше всего чатов",
@@ -564,7 +564,7 @@
564 "Advanced Definition": "Расширенное описание",564 "Advanced Definition": "Расширенное описание",
565 "Character Lore": "Лор персонажа",565 "Character Lore": "Лор персонажа",
566 "Export and Download": "Экспортировать и скачать",566 "Export and Download": "Экспортировать и скачать",
567 "Duplicate Character": "Дублировать персонажа",567 "Duplicate Character": "Клонировать персонажа",
568 "Create Character": "Создать персонажа",568 "Create Character": "Создать персонажа",
569 "Delete Character": "Удалить персонажа",569 "Delete Character": "Удалить персонажа",
570 "View all tags": "Показать все тэги",570 "View all tags": "Показать все тэги",
@@ -1690,7 +1690,7 @@
1690 "Delete Message": "Удалить сообщение",1690 "Delete Message": "Удалить сообщение",
1691 "Delete Swipe": "Удалить свайп",1691 "Delete Swipe": "Удалить свайп",
1692 "Could not get a reply from API. Check your connection settings / API key and try again.": "Не удалось получить ответ от API. Проверьте настройки соединения и API-ключ и повторите попытку.",1692 "Could not get a reply from API. Check your connection settings / API key and try again.": "Не удалось получить ответ от API. Проверьте настройки соединения и API-ключ и повторите попытку.",
1693 "Connecting To Proxy": "Подключение к прокси",1693 "Connecting To Proxy": "Подключиться к прокси",
1694 "Are you sure you want to connect to the following proxy URL?": "Вы точно хотите соединиться с прокси по этому адресу?",1694 "Are you sure you want to connect to the following proxy URL?": "Вы точно хотите соединиться с прокси по этому адресу?",
1695 "API connection successful!": "Соединение с API установлено!",1695 "API connection successful!": "Соединение с API установлено!",
1696 "Proxy Saved": "Прокси сохранена",1696 "Proxy Saved": "Прокси сохранена",
@@ -1747,5 +1747,142 @@
1747 "Markdown Hotkeys": "Горячие клавиши для разметки",1747 "Markdown Hotkeys": "Горячие клавиши для разметки",
1748 "markdown_hotkeys_desc": "Включить горячие клавиши для вставки символов разметки в некоторых полях ввода. См. '/help hotkeys'.",1748 "markdown_hotkeys_desc": "Включить горячие клавиши для вставки символов разметки в некоторых полях ввода. См. '/help hotkeys'.",
1749 "Save and Update": "Сохранить и обновить",1749 "Save and Update": "Сохранить и обновить",
1750 "Profile name:": "Название профиля:"1750 "Profile name:": "Название профиля:",
1751 "API returned an error": "API вернуло ошибку",
1752 "Failed to save preset": "Не удалось сохранить пресет",
1753 "Preset name should be unique.": "Название пресета должно быть уникальным.",
1754 "Invalid file": "Невалидный файл",
1755 "No preset selected": "Пресет не выбран",
1756 "Invalid logit bias preset file.": "Файл пресета невалиден.",
1757 "Preset was not deleted from server": "Пресет не удалён с сервера",
1758 "Preset deleted": "Пресет удалён",
1759 "Delete the preset?": "Удалить пресет?",
1760 "Preset updated": "Пресет обновлён",
1761 "Entered reverse proxy address is not a valid URL": "Введённый адрес прокси невалиден",
1762 "An error occurred while counting tokens: Token budget exceeded.": "Ошибка при подсчёте токенов: Превышен бюджет токенов",
1763 "An error occurred while counting tokens: Invalid character name": "Ошибка при подсчёте токенов: Невалидное имя персонажа",
1764 "Not enough free tokens for mandatory prompts. Raise your token Limit or disable custom prompts.": "Недостаточно токенов для всех выбранных промптов. Повысьте лимит токенов или отключите часть промптов.",
1765 "The name of at least one character contained whitespaces or special characters. Please check your user and character name.": "В имени одного из персонажей содержится пробел или иной спецсимвол. Проверьте имена пользователя и персонажа.",
1766 "An unknown error occurred while counting tokens. Further information may be available in console.": "Неизвестная ошибка при подсчёте токенов. Проверьте консоль, возможно, подробная информация есть там.",
1767 "quota_error_1": "При обработке вашего запроса возникла ошибка.",
1768 "quota_error_2": "Убедитесь, что на вашем",
1769 "quota_error_3": "аккаунте OpenAI",
1770 "quota_error_4": "имеется достаточно кредитов.",
1771 "quota_error_5": "Если кредитов достаточно, то повторите попытку позднее.",
1772 "Proxy preset '${0}' not found": "Пресет '${0}' не найден",
1773 "Window.ai returned an error": "Window.ai вернул ошибку",
1774 "Get it here:": "Загрузите здесь:",
1775 "Extension is not installed": "Расширение не установлено",
1776 "Update or remove your reverse proxy settings.": "Измените или удалите ваши настройки прокси.",
1777 "An error occurred while importing prompts. More info available in console.": "В процессе импорта произошла ошибка. Подробную информацию см. в консоли.",
1778 "Could not import prompts. Export failed validation.": "Не удалось импортировать промпты. Не пройдена валидация при экспорте.",
1779 "Prompt import complete.": "Импорт завершён.",
1780 "Are you sure you want to delete this prompt?": "Вы точно хотите удалить этот промпт?",
1781 "Existing prompts with the same ID will be overridden. Do you want to proceed?": "Имеющиеся промпты с совпадающими идентификаторами будут перезаписаны. Продолжить?",
1782 "This will reset the prompt order for this character. You will not lose any prompts.": "Будет сброшен порядок промптов для этого персонажа. Сами промпты вы не потеряете.",
1783 "assistant_note_1": "Примечание:",
1784 "assistant_note_2": "это временный чат, он будет удалён, как только вы из него выйдете.",
1785 "help_hotkeys_20": "Горячие клавиши для разметки",
1786 "help_hotkeys_21": "Работают в окне ввода чата, а также в полях, отмеченных этим значком:",
1787 "help_hotkeys_22": "**полужирный**",
1788 "help_hotkeys_23": "*курсив*",
1789 "help_hotkeys_24": "__подчёркивание__",
1790 "help_hotkeys_25": "`inline-код`",
1791 "help_hotkeys_26": "~~зачёркнутый~~",
1792 "ext_regex_only_format_visual_desc": "Содержимое файла с историей чата останется нетронутым, изменения будут лишь визуальными (в UI).",
1793 "Could not convert file": "Не удалось сконвертировать файл",
1794 "Could not upload file": "Не удалось загрузить файл",
1795 "Could not download file": "Не удалось скачать файл",
1796 "File is too big. Maximum size is ${0}.": "Слишком большой файл. Максимальный размер: ${0}.",
1797 "Binary files are not supported. Select a text file or image.": "Бинарные файлы не поддерживаются. Выберите текстовый файл или изображение.",
1798 "No character or group selected": "Не выбрано ни одного персонажа или группы",
1799 "Could not delete file": "Не удалось удалить файл",
1800 "No attachments selected.": "Вложение не выбрано.",
1801 "Data Bank": "Банк данных",
1802 "No files were scraped.": "Не соскраплено ни одного файла.",
1803 "Scraped ${0} files from ${1} to ${2}.": "Соскраплено ${0} файлов из ${1} в ${2}.",
1804 "Check browser console for details.": "Подробности см. в консоли браузера.",
1805 "Scraping failed": "Ошибка скрапинга",
1806 "External media has been blocked": "Внешние медиа отключены",
1807 "Use the 'Ext. Media' button to allow it. Click on this message to dismiss.": "Разрешить можно с помощью кнопки 'Внешн. медиа'. Нажмите на это сообщение, чтобы его скрыть.",
1808 "Couldn't get CSRF token. Please refresh the page.": "Не удалось получить CSRF токен. Попробуйте перезагрузить страницу.",
1809 "Error": "Ошибка",
1810 "API Error": "Ошибка API",
1811 "Please wait until the chat is saved before switching characters.": "Пожалуйста, дождитесь сохранения чата, прежде чем переключать персонажа.",
1812 "Your chat is still saving...": "Чат всё ещё сохраняется...",
1813 "Character ${0} not found in the list": "Персонаж ${0} не найден в списке",
1814 "Streaming is enabled, but the version of Kobold used does not support token streaming.": "Включён стриминг текста, но ваша версия Kobold не поддерживает стриминг токенов.",
1815 "Streaming is not supported for the Legacy API. Update Ooba and use new API to enable streaming.": "Для устаревшего API стриминг недоступен. Обновите oobaboga и используйте новый API, чтобы включить стриминг.",
1816 "Verify that the server is running and accessible.": "Убедитесь, что сервер запущен и доступен по сети.",
1817 "ST Server cannot be reached": "Не удалось соединиться с ST Server",
1818 "You must first select a character to duplicate!": "Вы не выбрали персонажа, которого хотите клонировать!",
1819 "Character Duplicated": "Персонаж склонирован",
1820 "No character name provided.": "Вы не ввели имя персонажа.",
1821 "Rename Character": "Переименование",
1822 "No character selected.": "Вы не выбрали персонажа.",
1823 "New name:": "Новое имя:",
1824 "Same character name provided, so name did not change.": "Введено то же самое имя, ничего не изменилось.",
1825 "Character renamed and past chats updated!": "Персонаж переименован, а чаты обновлены!",
1826 "Character renamed!": "Персонаж переименован!",
1827 "Something went wrong. The page will be reloaded.": "Что-то пошло не так. Страница будет перезагружена.",
1828 "Past chat could not be updated: ${0}": "Не удалось обновить чат ${0}",
1829 "Trying to save group chat with regular saveChat function. Aborting to prevent corruption.": "Произошла попытка сохранения группового чата функцией saveChat. Откатываем изменения, чтобы предотвратить потерю данных.",
1830 "Check the server connection and reload the page to prevent data loss.": "Проверьте связь с сервером и перезагрузите страницу, чтобы избежать потери данных.",
1831 "Chat could not be saved": "Не удалось сохранить чат",
1832 "Settings could not be loaded after multiple attempts. Please try again later.": "Не удалось загрузить настройки за несколько попыток. Попробуйте позднее.",
1833 "Settings could not be saved": "Не удалось сохранить настройки",
1834 "Could not load chat data. Try reloading the page.": "Не удалось загрузить чат. Попробуйте обновить страницу.",
1835 "Invalid process (no 'type')": "Невалидный процесс (нет параметра 'type')",
1836 "Character Deleted: ${0}": "Персонаж удалён: ${0}",
1837 "Character Created: ${0}": "Персонаж создан: ${0}",
1838 "Group Created": "Группа создана",
1839 "Group Deleted": "Группа удалена",
1840 "Character Imported: ${0}": "Персонаж импортирован: ${0}",
1841 "Invalid swipe ID: ${0}": "Некорректный идентификатор свайпа: ${0}",
1842 "No messages to delete swipes from.": "Сообщение, из которого требуется удалить свайп, не найдено.",
1843 "Can't delete the last swipe.": "Невозможно удалить единственный свайп.",
1844 "GUI Settings preset is not supported for Horde. Please select another preset.": "Для Horde не поддерживаются пресеты настроек GUI. Пожалуйста, выберите другой пресет.",
1845 "Embedded lorebook will be removed from this character.": "Встроенный лорбук будет удалён из персонажа.",
1846 "Name is required": "Введите имя",
1847 "Cannot create characters while generating. Stop the request and try again.": "Во время генерации ответа создать персонажа невозможно. Остановите запрос и повторите попытку.",
1848 "Creation aborted": "Процесс создания прерван",
1849 "Failed to create character": "Не удалось создать персонажа",
1850 "Something went wrong while saving the character, or the image file provided was in an invalid format. Double check that the image is not a webp.": "Что-то пошло не так в процессе сохранения персонажа, либо вы загрузили файл некорректного формата. Проверьте, что изображение точно не в формате webp.",
1851 "Context template '${0}' not found": "Шаблон контекста '${0}' не найден",
1852 "Instruct template '${0}' not found": "Шаблон Instruct-режима '${0}' не найден",
1853 "Error: ${0} is not a valid API": "Ошибка: ${0} не является валидным API",
1854 "API set to ${0}, trying to connect..": "Установлено API ${0}, пробуем подключиться...",
1855 "Unsupported file type: ": "Неподдерживаемый тип файла: ",
1856 "Cannot import characters while generating. Stop the request and try again.": "Во время генерации ответа импорт персонажа невозможен. Остановите запрос и повторите попытку.",
1857 "Import aborted": "Процесс импорта прерван",
1858 "The file is likely invalid or corrupted.": "Вероятно, файл невалиден или повреждён.",
1859 "Could not import character": "Не удалось импортировать персонажа",
1860 "Cannot run /impersonate command while the reply is being generated.": "Во время генерации ответа выполнить /impersonate невозможно.",
1861 "Name must be provided as an argument to rename this chat.": "Для переименования чата необходимо предоставить новое имя в качестве аргумента.",
1862 "No chat selected that can be renamed.": "Чат, который требуется переименовать, не найден.",
1863 "Successfully renamed chat to: ${0}": "Чат успешно переименован в: ${0}",
1864 "Character ${0} not found. Skipping deletion.": "Персонаж ${0} не найден. Удаление пропускается.",
1865 "Failed to delete character": "При удалении персонажа произошло ошибка",
1866 "duplicate_confirm_1": "Вы точно хотите клонировать этого персонажа?",
1867 "duplicate_confirm_2": "Если вы хотите просто создать новый чат, воспользуйтесь кнопкой \"Начать новый чат\" в меню слева внизу.",
1868 "delete_confirm_1": "ОТМЕНИТЬ БУДЕТ НЕВОЗМОЖНО!",
1869 "delete_confirm_2": "Также удалить файлы чатов",
1870 "Delete the character?": "Удалить персонажа?",
1871 "Not a valid number": "Некорректное число",
1872 "Author's Note depth updated": "Глубина заметок автора обновлена",
1873 "Author's Note frequency updated": "Частота заметок автора обновлена",
1874 "Not a valid position": "Некорректная позиция",
1875 "Author's Note position updated": "Позиция заметок автора обновлена",
1876 "Something went wrong. Could not save character's author's note.": "Что-то пошло не так. Не удалось сохранить заметки автора для этого персонажа.",
1877 "Select a character before trying to use Author's Note": "Сначала необходимо выбрать персонажа",
1878 "Author's Note text updated": "Текст заметок автора обновлён",
1879 "Group Validation": "Валидация группы",
1880 "Warning: Listed member ${0} does not exist as a character. It will be removed from the group.": "Предупреждение: персонаж ${0} не существует в виде карточки. Он будет удалён из группы.",
1881 "Group Chat could not be saved": "Не удалось сохранить групповой чат",
1882 "Deleted group member swiped. To get a reply, add them back to the group.": "Вы пытаетесь свайпнуть удалённого члена группы. Чтобы получить ответ, добавьте этого персонажа обратно в группу.",
1883 "Currently no group selected.": "В данный момент не выбрано ни одной группы.",
1884 "Not so fast! Wait for the characters to stop typing before deleting the group.": "Чуть помедленнее! Перед удалением группы дождитесь, пока персонаж закончит печатать.",
1885 "Delete the group?": "Удалить группу?",
1886 "This will also delete all your chats with that group. If you want to delete a single conversation, select a \"View past chats\" option in the lower left menu.": "Вместе с ней будут удалены и все её чаты. Если требуется удалить только один чат, воспользуйтесь кнопкой \"Все чаты\" в меню в левом нижнем углу.",
1887 "Can't peek a character while group reply is being generated": "Невозможно открыть карточку персонажа во время генерации ответа"
1751}1888}
public/script.js+59 -64
@@ -383,8 +383,8 @@ DOMPurify.addHook('uponSanitizeElement', (node, _, config) => {
383383
384 if (localStorage.getItem(warningShownKey) === null) {384 if (localStorage.getItem(warningShownKey) === null) {
385 const warningToast = toastr.warning(385 const warningToast = toastr.warning(
386 'Use the "Ext. Media" button to allow it. Click on this message to dismiss.',386 t`Use the 'Ext. Media' button to allow it. Click on this message to dismiss.`,
387 'External media has been blocked',387 t`External media has been blocked`,
388 {388 {
389 timeOut: 0,389 timeOut: 0,
390 preventDuplicates: true,390 preventDuplicates: true,
@@ -924,7 +924,7 @@ async function firstLoadInit() {
924 token = tokenData.token;924 token = tokenData.token;
925 } catch {925 } catch {
926 hideLoader();926 hideLoader();
927 toastr.error('Couldn\'t get CSRF token. Please refresh the page.', 'Error', { timeOut: 0, extendedTimeOut: 0, preventDuplicates: true });927 toastr.error(t`Couldn't get CSRF token. Please refresh the page.`, t`Error`, { timeOut: 0, extendedTimeOut: 0, preventDuplicates: true });
928 throw new Error('Initialization failed');928 throw new Error('Initialization failed');
929 }929 }
930930
@@ -1140,7 +1140,7 @@ async function getStatusKobold() {
11401140
1141 // We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress.1141 // We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress.
1142 if (online_status === 'no_connection' && data.response) {1142 if (online_status === 'no_connection' && data.response) {
1143 toastr.error(data.response, 'API Error', { timeOut: 5000, preventDuplicates: true });1143 toastr.error(data.response, t`API Error`, { timeOut: 5000, preventDuplicates: true });
1144 }1144 }
1145 } catch (err) {1145 } catch (err) {
1146 console.error('Error getting status', err);1146 console.error('Error getting status', err);
@@ -1226,7 +1226,7 @@ async function getStatusTextgen() {
12261226
1227 // We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress.1227 // We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress.
1228 if (online_status === 'no_connection' && data.response) {1228 if (online_status === 'no_connection' && data.response) {
1229 toastr.error(data.response, 'API Error', { timeOut: 5000, preventDuplicates: true });1229 toastr.error(data.response, t`API Error`, { timeOut: 5000, preventDuplicates: true });
1230 }1230 }
1231 } catch (err) {1231 } catch (err) {
1232 if (err instanceof AbortReason) {1232 if (err instanceof AbortReason) {
@@ -1278,7 +1278,7 @@ export async function selectCharacterById(id) {
1278 }1278 }
12791279
1280 if (isChatSaving) {1280 if (isChatSaving) {
1281 toastr.info('Please wait until the chat is saved before switching characters.', 'Your chat is still saving...');1281 toastr.info(t`Please wait until the chat is saved before switching characters.`, t`Your chat is still saving...`);
1282 return;1282 return;
1283 }1283 }
12841284
@@ -1632,7 +1632,7 @@ export async function getOneCharacter(avatarUrl) {
1632 if (indexOf !== -1) {1632 if (indexOf !== -1) {
1633 characters[indexOf] = getData;1633 characters[indexOf] = getData;
1634 } else {1634 } else {
1635 toastr.error(`Character ${avatarUrl} not found in the list`, 'Error', { timeOut: 5000, preventDuplicates: true });1635 toastr.error(t`Character ${avatarUrl} not found in the list`, t`Error`, { timeOut: 5000, preventDuplicates: true });
1636 }1636 }
1637 }1637 }
1638}1638}
@@ -3374,7 +3374,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3374 await eventSource.emit(event_types.GENERATION_AFTER_COMMANDS, type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage }, dryRun);3374 await eventSource.emit(event_types.GENERATION_AFTER_COMMANDS, type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage }, dryRun);
33753375
3376 if (main_api == 'kobold' && kai_settings.streaming_kobold && !kai_flags.can_use_streaming) {3376 if (main_api == 'kobold' && kai_settings.streaming_kobold && !kai_flags.can_use_streaming) {
3377 toastr.error('Streaming is enabled, but the version of Kobold used does not support token streaming.', undefined, { timeOut: 10000, preventDuplicates: true });3377 toastr.error(t`Streaming is enabled, but the version of Kobold used does not support token streaming.`, undefined, { timeOut: 10000, preventDuplicates: true });
3378 unblockGeneration(type);3378 unblockGeneration(type);
3379 return Promise.resolve();3379 return Promise.resolve();
3380 }3380 }
@@ -3383,7 +3383,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3383 textgen_settings.streaming &&3383 textgen_settings.streaming &&
3384 textgen_settings.legacy_api &&3384 textgen_settings.legacy_api &&
3385 textgen_settings.type === OOBA) {3385 textgen_settings.type === OOBA) {
3386 toastr.error('Streaming is not supported for the Legacy API. Update Ooba and use new API to enable streaming.', undefined, { timeOut: 10000, preventDuplicates: true });3386 toastr.error(t`Streaming is not supported for the Legacy API. Update Ooba and use new API to enable streaming.`, undefined, { timeOut: 10000, preventDuplicates: true });
3387 unblockGeneration(type);3387 unblockGeneration(type);
3388 return Promise.resolve();3388 return Promise.resolve();
3389 }3389 }
@@ -3399,7 +3399,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
33993399
3400 if (!pingResult) {3400 if (!pingResult) {
3401 unblockGeneration(type);3401 unblockGeneration(type);
3402 toastr.error('Verify that the server is running and accessible.', 'ST Server cannot be reached');3402 toastr.error(t`Verify that the server is running and accessible.`, t`ST Server cannot be reached`);
3403 throw new Error('Server unreachable');3403 throw new Error('Server unreachable');
3404 }3404 }
34053405
@@ -4427,7 +4427,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4427 generatedPromptCache = '';4427 generatedPromptCache = '';
44284428
4429 if (data?.response) {4429 if (data?.response) {
4430 toastr.error(data.response, 'API Error', { preventDuplicates: true });4430 toastr.error(data.response, t`API Error`, { preventDuplicates: true });
4431 }4431 }
4432 throw new Error(data?.response);4432 throw new Error(data?.response);
4433 }4433 }
@@ -4518,7 +4518,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
45184518
4519 function onError(exception) {4519 function onError(exception) {
4520 if (typeof exception?.error?.message === 'string') {4520 if (typeof exception?.error?.message === 'string') {
4521 toastr.error(exception.error.message, 'Error', { timeOut: 10000, extendedTimeOut: 20000 });4521 toastr.error(exception.error.message, t`Error`, { timeOut: 10000, extendedTimeOut: 20000 });
4522 }4522 }
45234523
4524 generatedPromptCache = '';4524 generatedPromptCache = '';
@@ -4920,7 +4920,7 @@ function addChatsSeparator(mesSendString) {
49204920
4921async function duplicateCharacter() {4921async function duplicateCharacter() {
4922 if (!this_chid) {4922 if (!this_chid) {
4923 toastr.warning('You must first select a character to duplicate!');4923 toastr.warning(t`You must first select a character to duplicate!`);
4924 return '';4924 return '';
4925 }4925 }
49264926
@@ -4939,7 +4939,7 @@ async function duplicateCharacter() {
4939 body: JSON.stringify(body),4939 body: JSON.stringify(body),
4940 });4940 });
4941 if (response.ok) {4941 if (response.ok) {
4942 toastr.success('Character Duplicated');4942 toastr.success(t`Character Duplicated`);
4943 const data = await response.json();4943 const data = await response.json();
4944 await eventSource.emit(event_types.CHARACTER_DUPLICATED, { oldAvatar: body.avatar_url, newAvatar: data.path });4944 await eventSource.emit(event_types.CHARACTER_DUPLICATED, { oldAvatar: body.avatar_url, newAvatar: data.path });
4945 await getCharacters();4945 await getCharacters();
@@ -5786,23 +5786,23 @@ export function setSendButtonState(value) {
57865786
5787export async function renameCharacter(name = null, { silent = false, renameChats = null } = {}) {5787export async function renameCharacter(name = null, { silent = false, renameChats = null } = {}) {
5788 if (!name && silent) {5788 if (!name && silent) {
5789 toastr.warning('No character name provided.', 'Rename Character');5789 toastr.warning(t`No character name provided.`, t`Rename Character`);
5790 return false;5790 return false;
5791 }5791 }
5792 if (this_chid === undefined) {5792 if (this_chid === undefined) {
5793 toastr.warning('No character selected.', 'Rename Character');5793 toastr.warning(t`No character selected.`, t`Rename Character`);
5794 return false;5794 return false;
5795 }5795 }
57965796
5797 const oldAvatar = characters[this_chid].avatar;5797 const oldAvatar = characters[this_chid].avatar;
5798 const newValue = name || await callGenericPopup('<h3>New name:</h3>', POPUP_TYPE.INPUT, characters[this_chid].name);5798 const newValue = name || await callGenericPopup('<h3>' + t`New name:` + '</h3>', POPUP_TYPE.INPUT, characters[this_chid].name);
57995799
5800 if (!newValue) {5800 if (!newValue) {
5801 toastr.warning('No character name provided.', 'Rename Character');5801 toastr.warning(t`No character name provided.`, t`Rename Character`);
5802 return false;5802 return false;
5803 }5803 }
5804 if (newValue === characters[this_chid].name) {5804 if (newValue === characters[this_chid].name) {
5805 toastr.info('Same character name provided, so name did not change.', 'Rename Character');5805 toastr.info(t`Same character name provided, so name did not change.`, t`Rename Character`);
5806 return false;5806 return false;
5807 }5807 }
58085808
@@ -5849,9 +5849,9 @@ export async function renameCharacter(name = null, { silent = false, renameChats
5849 if (renamePastChatsConfirm) {5849 if (renamePastChatsConfirm) {
5850 await renamePastChats(newAvatar, newValue);5850 await renamePastChats(newAvatar, newValue);
5851 await reloadCurrentChat();5851 await reloadCurrentChat();
5852 toastr.success('Character renamed and past chats updated!', 'Rename Character');5852 toastr.success(t`Character renamed and past chats updated!`, t`Rename Character`);
5853 } else {5853 } else {
5854 toastr.success('Character renamed!', 'Rename Character');5854 toastr.success(t`Character renamed!`, t`Rename Character`);
5855 }5855 }
5856 }5856 }
5857 else {5857 else {
@@ -5864,8 +5864,8 @@ export async function renameCharacter(name = null, { silent = false, renameChats
5864 }5864 }
5865 catch (error) {5865 catch (error) {
5866 // Reloading to prevent data corruption5866 // Reloading to prevent data corruption
5867 if (!silent) await callPopup('Something went wrong. The page will be reloaded.', 'text');5867 if (!silent) await callPopup(t`Something went wrong. The page will be reloaded.`, 'text');
5868 else toastr.error('Something went wrong. The page will be reloaded.', 'Rename Character');5868 else toastr.error(t`Something went wrong. The page will be reloaded.`, t`Rename Character`);
58695869
5870 console.log('Renaming character error:', error);5870 console.log('Renaming character error:', error);
5871 location.reload();5871 location.reload();
@@ -5922,7 +5922,7 @@ async function renamePastChats(newAvatar, newValue) {
5922 }5922 }
5923 }5923 }
5924 } catch (error) {5924 } catch (error) {
5925 toastr.error(`Past chat could not be updated: ${file_name}`);5925 toastr.error(t`Past chat could not be updated: ${file_name}`);
5926 console.error(error);5926 console.error(error);
5927 }5927 }
5928 }5928 }
@@ -5971,7 +5971,7 @@ export async function saveChat(chatName, withMetadata, mesId) {
5971 characters[this_chid]['date_last_chat'] = Date.now();5971 characters[this_chid]['date_last_chat'] = Date.now();
5972 chat.forEach(function (item, i) {5972 chat.forEach(function (item, i) {
5973 if (item['is_group']) {5973 if (item['is_group']) {
5974 toastr.error('Trying to save group chat with regular saveChat function. Aborting to prevent corruption.');5974 toastr.error(t`Trying to save group chat with regular saveChat function. Aborting to prevent corruption.`);
5975 throw new Error('Group chat saved from saveChat');5975 throw new Error('Group chat saved from saveChat');
5976 }5976 }
5977 /*5977 /*
@@ -6016,7 +6016,7 @@ export async function saveChat(chatName, withMetadata, mesId) {
6016 contentType: 'application/json',6016 contentType: 'application/json',
6017 success: function (data) { },6017 success: function (data) { },
6018 error: function (jqXHR, exception) {6018 error: function (jqXHR, exception) {
6019 toastr.error('Check the server connection and reload the page to prevent data loss.', 'Chat could not be saved');6019 toastr.error(t`Check the server connection and reload the page to prevent data loss.`, t`Chat could not be saved`);
6020 console.log(exception);6020 console.log(exception);
6021 console.log(jqXHR);6021 console.log(jqXHR);
6022 },6022 },
@@ -6413,7 +6413,7 @@ export async function getSettings() {
64136413
6414 if (!response.ok) {6414 if (!response.ok) {
6415 reloadLoop();6415 reloadLoop();
6416 toastr.error('Settings could not be loaded after multiple attempts. Please try again later.');6416 toastr.error(t`Settings could not be loaded after multiple attempts. Please try again later.`);
6417 throw new Error('Error getting settings');6417 throw new Error('Error getting settings');
6418 }6418 }
64196419
@@ -6640,7 +6640,7 @@ export async function saveSettings(type) {
6640 eventSource.emit(event_types.SETTINGS_UPDATED);6640 eventSource.emit(event_types.SETTINGS_UPDATED);
6641 },6641 },
6642 error: function (jqXHR, exception) {6642 error: function (jqXHR, exception) {
6643 toastr.error('Check the server connection and reload the page to prevent data loss.', 'Settings could not be saved');6643 toastr.error(t`Check the server connection and reload the page to prevent data loss.t`, t`Settings could not be saved`);
6644 console.log(exception);6644 console.log(exception);
6645 console.log(jqXHR);6645 console.log(jqXHR);
6646 },6646 },
@@ -6906,7 +6906,7 @@ export async function displayPastChats() {
6906 const data = await (selected_group ? getGroupPastChats(selected_group) : getPastCharacterChats());6906 const data = await (selected_group ? getGroupPastChats(selected_group) : getPastCharacterChats());
69076907
6908 if (!data) {6908 if (!data) {
6909 toastr.error('Could not load chat data. Try reloading the page.');6909 toastr.error(t`Could not load chat data. Try reloading the page.`);
6910 return;6910 return;
6911 }6911 }
69126912
@@ -7039,7 +7039,7 @@ export function selectRightMenuWithAnimation(selectedMenuId) {
70397039
7040export function select_rm_info(type, charId, previousCharId = null) {7040export function select_rm_info(type, charId, previousCharId = null) {
7041 if (!type) {7041 if (!type) {
7042 toastr.error('Invalid process (no \'type\')');7042 toastr.error(t`Invalid process (no 'type')`);
7043 return;7043 return;
7044 }7044 }
7045 if (type !== 'group_create') {7045 if (type !== 'group_create') {
@@ -7047,20 +7047,20 @@ export function select_rm_info(type, charId, previousCharId = null) {
7047 }7047 }
70487048
7049 if (type === 'char_delete') {7049 if (type === 'char_delete') {
7050 toastr.warning(`Character Deleted: ${displayName}`);7050 toastr.warning(t`Character Deleted: ${displayName}`);
7051 }7051 }
7052 if (type === 'char_create') {7052 if (type === 'char_create') {
7053 toastr.success(`Character Created: ${displayName}`);7053 toastr.success(t`Character Created: ${displayName}`);
7054 }7054 }
7055 if (type === 'group_create') {7055 if (type === 'group_create') {
7056 toastr.success('Group Created');7056 toastr.success(t`Group Created`);
7057 }7057 }
7058 if (type === 'group_delete') {7058 if (type === 'group_delete') {
7059 toastr.warning('Group Deleted');7059 toastr.warning(t`Group Deleted`);
7060 }7060 }
70617061
7062 if (type === 'char_import') {7062 if (type === 'char_import') {
7063 toastr.success(`Character Imported: ${displayName}`);7063 toastr.success(t`Character Imported: ${displayName}`);
7064 }7064 }
70657065
7066 selectRightMenuWithAnimation('rm_characters_block');7066 selectRightMenuWithAnimation('rm_characters_block');
@@ -7515,25 +7515,25 @@ export function hideSwipeButtons() {
7515 */7515 */
7516export async function deleteSwipe(swipeId = null) {7516export async function deleteSwipe(swipeId = null) {
7517 if (swipeId && (isNaN(swipeId) || swipeId < 0)) {7517 if (swipeId && (isNaN(swipeId) || swipeId < 0)) {
7518 toastr.warning(`Invalid swipe ID: ${swipeId + 1}`);7518 toastr.warning(t`Invalid swipe ID: ${swipeId + 1}`);
7519 return;7519 return;
7520 }7520 }
75217521
7522 const lastMessage = chat[chat.length - 1];7522 const lastMessage = chat[chat.length - 1];
7523 if (!lastMessage || !Array.isArray(lastMessage.swipes) || !lastMessage.swipes.length) {7523 if (!lastMessage || !Array.isArray(lastMessage.swipes) || !lastMessage.swipes.length) {
7524 toastr.warning('No messages to delete swipes from.');7524 toastr.warning(t`No messages to delete swipes from.`);
7525 return;7525 return;
7526 }7526 }
75277527
7528 if (lastMessage.swipes.length <= 1) {7528 if (lastMessage.swipes.length <= 1) {
7529 toastr.warning('Can\'t delete the last swipe.');7529 toastr.warning(t`Can't delete the last swipe.`);
7530 return;7530 return;
7531 }7531 }
75327532
7533 swipeId = swipeId ?? lastMessage.swipe_id;7533 swipeId = swipeId ?? lastMessage.swipe_id;
75347534
7535 if (swipeId < 0 || swipeId >= lastMessage.swipes.length) {7535 if (swipeId < 0 || swipeId >= lastMessage.swipes.length) {
7536 toastr.warning(`Invalid swipe ID: ${swipeId + 1}`);7536 toastr.warning(t`Invalid swipe ID: ${swipeId + 1}`);
7537 return;7537 return;
7538 }7538 }
75397539
@@ -7672,7 +7672,7 @@ export function setGenerationProgress(progress) {
76727672
7673function isHordeGenerationNotAllowed() {7673function isHordeGenerationNotAllowed() {
7674 if (main_api == 'koboldhorde' && preset_settings == 'gui') {7674 if (main_api == 'koboldhorde' && preset_settings == 'gui') {
7675 toastr.error('GUI Settings preset is not supported for Horde. Please select another preset.');7675 toastr.error(t`GUI Settings preset is not supported for Horde. Please select another preset.`);
7676 return true;7676 return true;
7677 }7677 }
76787678
@@ -7721,7 +7721,7 @@ function openCharacterWorldPopup() {
7721 }7721 }
77227722
7723 $('#character_json_data').val(JSON.stringify(data));7723 $('#character_json_data').val(JSON.stringify(data));
7724 toastr.info('Embedded lorebook will be removed from this character.');7724 toastr.info(t`Embedded lorebook will be removed from this character.`);
7725 } catch {7725 } catch {
7726 console.error('Failed to parse character JSON data.');7726 console.error('Failed to parse character JSON data.');
7727 }7727 }
@@ -7912,11 +7912,11 @@ async function createOrEditCharacter(e) {
79127912
7913 if ($('#form_create').attr('actiontype') == 'createcharacter') {7913 if ($('#form_create').attr('actiontype') == 'createcharacter') {
7914 if (String($('#character_name_pole').val()).length === 0) {7914 if (String($('#character_name_pole').val()).length === 0) {
7915 toastr.error('Name is required');7915 toastr.error(t`Name is required`);
7916 return;7916 return;
7917 }7917 }
7918 if (is_group_generating || is_send_press) {7918 if (is_group_generating || is_send_press) {
7919 toastr.error('Cannot create characters while generating. Stop the request and try again.', 'Creation aborted');7919 toastr.error(t`Cannot create characters while generating. Stop the request and try again.`, t`Creation aborted`);
7920 return;7920 return;
7921 }7921 }
7922 try {7922 try {
@@ -8000,7 +8000,7 @@ async function createOrEditCharacter(e) {
80008000
8001 } catch (error) {8001 } catch (error) {
8002 console.error('Error creating character', error);8002 console.error('Error creating character', error);
8003 toastr.error('Failed to create character');8003 toastr.error(t`Failed to create character`);
8004 }8004 }
8005 } else {8005 } else {
8006 try {8006 try {
@@ -8059,7 +8059,7 @@ async function createOrEditCharacter(e) {
8059 }8059 }
8060 } catch (error) {8060 } catch (error) {
8061 console.log(error);8061 console.log(error);
8062 toastr.error('Something went wrong while saving the character, or the image file provided was in an invalid format. Double check that the image is not a webp.');8062 toastr.error(t`Something went wrong while saving the character, or the image file provided was in an invalid format. Double check that the image is not a webp.`);
8063 }8063 }
8064 }8064 }
8065}8065}
@@ -8551,7 +8551,7 @@ async function selectContextCallback(args, name) {
8551 const result = fuse.search(name);8551 const result = fuse.search(name);
85528552
8553 if (result.length === 0) {8553 if (result.length === 0) {
8554 !quiet && toastr.warning(`Context template "${name}" not found`);8554 !quiet && toastr.warning(t`Context template '${name}' not found`);
8555 return '';8555 return '';
8556 }8556 }
85578557
@@ -8571,7 +8571,7 @@ async function selectInstructCallback(args, name) {
8571 const result = fuse.search(name);8571 const result = fuse.search(name);
85728572
8573 if (result.length === 0) {8573 if (result.length === 0) {
8574 !quiet && toastr.warning(`Instruct template "${name}" not found`);8574 !quiet && toastr.warning(t`Instruct template '${name}' not found`);
8575 return '';8575 return '';
8576 }8576 }
85778577
@@ -8623,7 +8623,7 @@ async function connectAPISlash(args, text) {
86238623
8624 const apiConfig = CONNECT_API_MAP[text.toLowerCase()];8624 const apiConfig = CONNECT_API_MAP[text.toLowerCase()];
8625 if (!apiConfig) {8625 if (!apiConfig) {
8626 toastr.error(`Error: ${text} is not a valid API`);8626 toastr.error(t`Error: ${text} is not a valid API`);
8627 return '';8627 return '';
8628 }8628 }
86298629
@@ -8652,7 +8652,7 @@ async function connectAPISlash(args, text) {
8652 }8652 }
86538653
8654 const quiet = isTrueBoolean(args?.quiet);8654 const quiet = isTrueBoolean(args?.quiet);
8655 const toast = quiet ? jQuery() : toastr.info(`API set to ${text}, trying to connect..`);8655 const toast = quiet ? jQuery() : toastr.info(t`API set to ${text}, trying to connect..`);
86568656
8657 try {8657 try {
8658 await waitUntilCondition(() => online_status !== 'no_connection', 10000, 100);8658 await waitUntilCondition(() => online_status !== 'no_connection', 10000, 100);
@@ -8691,7 +8691,7 @@ export async function processDroppedFiles(files, data = new Map()) {
8691 const preservedName = data instanceof Map && data.get(file);8691 const preservedName = data instanceof Map && data.get(file);
8692 await importCharacter(file, preservedName);8692 await importCharacter(file, preservedName);
8693 } else {8693 } else {
8694 toastr.warning('Unsupported file type: ' + file.name);8694 toastr.warning(t`Unsupported file type: ` + file.name);
8695 }8695 }
8696 }8696 }
8697}8697}
@@ -8704,7 +8704,7 @@ export async function processDroppedFiles(files, data = new Map()) {
8704 */8704 */
8705async function importCharacter(file, preserveFileName = '') {8705async function importCharacter(file, preserveFileName = '') {
8706 if (is_group_generating || is_send_press) {8706 if (is_group_generating || is_send_press) {
8707 toastr.error('Cannot import characters while generating. Stop the request and try again.', 'Import aborted');8707 toastr.error(t`Cannot import characters while generating. Stop the request and try again.`, t`Import aborted`);
8708 throw new Error('Cannot import character while generating');8708 throw new Error('Cannot import character while generating');
8709 }8709 }
87108710
@@ -8731,7 +8731,7 @@ async function importCharacter(file, preserveFileName = '') {
8731 });8731 });
87328732
8733 if (data.error) {8733 if (data.error) {
8734 toastr.error('The file is likely invalid or corrupted.', 'Could not import character');8734 toastr.error(t`The file is likely invalid or corrupted.`, t`Could not import character`);
8735 return;8735 return;
8736 }8736 }
87378737
@@ -8784,7 +8784,7 @@ async function doImpersonate(args, prompt) {
8784 await waitUntilCondition(() => !is_send_press && !is_group_generating, 10000, 100);8784 await waitUntilCondition(() => !is_send_press && !is_group_generating, 10000, 100);
8785 } catch {8785 } catch {
8786 console.warn('Timeout waiting for generation unlock');8786 console.warn('Timeout waiting for generation unlock');
8787 toastr.warning('Cannot run /impersonate command while the reply is being generated.');8787 toastr.warning(t`Cannot run /impersonate command while the reply is being generated.`);
8788 return '';8788 return '';
8789 }8789 }
87908790
@@ -8846,19 +8846,19 @@ async function doDeleteChat() {
88468846
8847async function doRenameChat(_, chatName) {8847async function doRenameChat(_, chatName) {
8848 if (!chatName) {8848 if (!chatName) {
8849 toastr.warning('Name must be provided as an argument to rename this chat.');8849 toastr.warning(t`Name must be provided as an argument to rename this chat.`);
8850 return '';8850 return '';
8851 }8851 }
88528852
8853 const currentChatName = getCurrentChatId();8853 const currentChatName = getCurrentChatId();
8854 if (!currentChatName) {8854 if (!currentChatName) {
8855 toastr.warning('No chat selected that can be renamed.');8855 toastr.warning(t`No chat selected that can be renamed.`);
8856 return '';8856 return '';
8857 }8857 }
88588858
8859 await renameChat(currentChatName, chatName);8859 await renameChat(currentChatName, chatName);
88608860
8861 toastr.success(`Successfully renamed chat to: ${chatName}`);8861 toastr.success(t`Successfully renamed chat to: ${chatName}`);
8862 return '';8862 return '';
8863}8863}
88648864
@@ -8973,7 +8973,7 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {})
8973 for (const key of characterKey) {8973 for (const key of characterKey) {
8974 const character = characters.find(x => x.avatar == key);8974 const character = characters.find(x => x.avatar == key);
8975 if (!character) {8975 if (!character) {
8976 toastr.warning(`Character ${key} not found. Skipping deletion.`);8976 toastr.warning(t`Character ${key} not found. Skipping deletion.`);
8977 continue;8977 continue;
8978 }8978 }
89798979
@@ -8990,7 +8990,7 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {})
8990 });8990 });
89918991
8992 if (!response.ok) {8992 if (!response.ok) {
8993 toastr.error(`${response.status} ${response.statusText}`, 'Failed to delete character');8993 toastr.error(`${response.status} ${response.statusText}`, t`Failed to delete character`);
8994 continue;8994 continue;
8995 }8995 }
89968996
@@ -9665,12 +9665,7 @@ jQuery(async function () {
96659665
9666 let deleteChats = false;9666 let deleteChats = false;
96679667
9668 const confirm = await Popup.show.confirm('Delete the character?', `9668 const confirm = await Popup.show.confirm(t`Delete the character?`, await renderTemplateAsync('deleteConfirm'), {
9669 <b>THIS IS PERMANENT!<br><br>
9670 <label for="del_char_checkbox" class="checkbox_label justifyCenter">
9671 <input type="checkbox" id="del_char_checkbox" />
9672 <small>Also delete the chat files</small>
9673 </label></b>`, {
9674 onClose: () => deleteChats = !!$('#del_char_checkbox').prop('checked'),9669 onClose: () => deleteChats = !!$('#del_char_checkbox').prop('checked'),
9675 });9670 });
9676 if (!confirm) {9671 if (!confirm) {
public/scripts/PromptManager.js+7 -6
@@ -8,6 +8,7 @@ import { debounce, waitUntilCondition, escapeHtml } from './utils.js';
8import { debounce_timeout } from './constants.js';8import { debounce_timeout } from './constants.js';
9import { renderTemplateAsync } from './templates.js';9import { renderTemplateAsync } from './templates.js';
10import { Popup } from './popup.js';10import { Popup } from './popup.js';
11import { t } from './i18n.js';
1112
12function debouncePromise(func, delay) {13function debouncePromise(func, delay) {
13 let timeoutId;14 let timeoutId;
@@ -455,7 +456,7 @@ class PromptManager {
455456
456 // Delete selected prompt from list form and close edit form457 // Delete selected prompt from list form and close edit form
457 this.handleDeletePrompt = async (event) => {458 this.handleDeletePrompt = async (event) => {
458 Popup.show.confirm('Are you sure you want to delete this prompt?', null).then((userChoice) => {459 Popup.show.confirm(t`Are you sure you want to delete this prompt?`, null).then((userChoice) => {
459 if (!userChoice) return;460 if (!userChoice) return;
460 const promptID = document.getElementById(this.configuration.prefix + 'prompt_manager_footer_append_prompt').value;461 const promptID = document.getElementById(this.configuration.prefix + 'prompt_manager_footer_append_prompt').value;
461 const prompt = this.getPromptById(promptID);462 const prompt = this.getPromptById(promptID);
@@ -531,7 +532,7 @@ class PromptManager {
531532
532 // Import prompts for the selected character533 // Import prompts for the selected character
533 this.handleImport = () => {534 this.handleImport = () => {
534 Popup.show.confirm('Existing prompts with the same ID will be overridden. Do you want to proceed?', null)535 Popup.show.confirm(t`Existing prompts with the same ID will be overridden. Do you want to proceed?`, null)
535 .then(userChoice => {536 .then(userChoice => {
536 if (!userChoice) return;537 if (!userChoice) return;
537538
@@ -552,7 +553,7 @@ class PromptManager {
552 const data = JSON.parse(fileContent);553 const data = JSON.parse(fileContent);
553 this.import(data);554 this.import(data);
554 } catch (err) {555 } catch (err) {
555 toastr.error('An error occurred while importing prompts. More info available in console.');556 toastr.error(t`An error occurred while importing prompts. More info available in console.`);
556 console.log('An error occurred while importing prompts');557 console.log('An error occurred while importing prompts');
557 console.log(err.toString());558 console.log(err.toString());
558 }559 }
@@ -567,7 +568,7 @@ class PromptManager {
567568
568 // Restore default state of a characters prompt order569 // Restore default state of a characters prompt order
569 this.handleCharacterReset = () => {570 this.handleCharacterReset = () => {
570 Popup.show.confirm('This will reset the prompt order for this character. You will not lose any prompts.', null)571 Popup.show.confirm(t`This will reset the prompt order for this character. You will not lose any prompts.`, null)
571 .then(userChoice => {572 .then(userChoice => {
572 if (!userChoice) return;573 if (!userChoice) return;
573574
@@ -1649,7 +1650,7 @@ class PromptManager {
1649 };1650 };
16501651
1651 if (false === this.validateObject(controlObj, importData)) {1652 if (false === this.validateObject(controlObj, importData)) {
1652 toastr.warning('Could not import prompts. Export failed validation.');1653 toastr.warning(t`Could not import prompts. Export failed validation.`);
1653 return;1654 return;
1654 }1655 }
16551656
@@ -1672,7 +1673,7 @@ class PromptManager {
1672 throw new Error('Prompt order strategy not supported.');1673 throw new Error('Prompt order strategy not supported.');
1673 }1674 }
16741675
1675 toastr.success('Prompt import complete.');1676 toastr.success(t`Prompt import complete.`);
1676 this.saveServiceSettings().then(() => this.render());1677 this.saveServiceSettings().then(() => this.render());
1677 }1678 }
16781679
public/scripts/authors-note.js+10 -9
@@ -16,6 +16,7 @@ import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
16import { SlashCommand } from './slash-commands/SlashCommand.js';16import { SlashCommand } from './slash-commands/SlashCommand.js';
17import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js';17import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js';
18export { MODULE_NAME as NOTE_MODULE_NAME };18export { MODULE_NAME as NOTE_MODULE_NAME };
19import { t } from './i18n.js';
1920
20const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory21const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory
2122
@@ -37,7 +38,7 @@ const chara_note_position = {
3738
38function setNoteTextCommand(_, text) {39function setNoteTextCommand(_, text) {
39 $('#extension_floating_prompt').val(text).trigger('input');40 $('#extension_floating_prompt').val(text).trigger('input');
40 toastr.success('Author\'s Note text updated');41 toastr.success(t`Author's Note text updated`);
41 return '';42 return '';
42}43}
4344
@@ -45,12 +46,12 @@ function setNoteDepthCommand(_, text) {
45 const value = Number(text);46 const value = Number(text);
4647
47 if (Number.isNaN(value)) {48 if (Number.isNaN(value)) {
48 toastr.error('Not a valid number');49 toastr.error(t`Not a valid number`);
49 return;50 return;
50 }51 }
5152
52 $('#extension_floating_depth').val(Math.abs(value)).trigger('input');53 $('#extension_floating_depth').val(Math.abs(value)).trigger('input');
53 toastr.success('Author\'s Note depth updated');54 toastr.success(t`Author's Note depth updated`);
54 return '';55 return '';
55}56}
5657
@@ -58,12 +59,12 @@ function setNoteIntervalCommand(_, text) {
58 const value = Number(text);59 const value = Number(text);
5960
60 if (Number.isNaN(value)) {61 if (Number.isNaN(value)) {
61 toastr.error('Not a valid number');62 toastr.error(t`Not a valid number`);
62 return;63 return;
63 }64 }
6465
65 $('#extension_floating_interval').val(Math.abs(value)).trigger('input');66 $('#extension_floating_interval').val(Math.abs(value)).trigger('input');
66 toastr.success('Author\'s Note frequency updated');67 toastr.success(t`Author's Note frequency updated`);
67 return '';68 return '';
68}69}
6970
@@ -76,12 +77,12 @@ function setNotePositionCommand(_, text) {
76 const position = validPositions[text?.trim()];77 const position = validPositions[text?.trim()];
7778
78 if (Number.isNaN(position)) {79 if (Number.isNaN(position)) {
79 toastr.error('Not a valid position');80 toastr.error(t`Not a valid position`);
80 return;81 return;
81 }82 }
8283
83 $(`input[name="extension_floating_position"][value="${position}"]`).prop('checked', true).trigger('input');84 $(`input[name="extension_floating_position"][value="${position}"]`).prop('checked', true).trigger('input');
84 toastr.info('Author\'s Note position updated');85 toastr.info(t`Author's Note position updated`);
85 return '';86 return '';
86}87}
8788
@@ -206,7 +207,7 @@ function onExtensionFloatingCharaPromptInput() {
206 extension_settings.note.chara.push(tempCharaNote);207 extension_settings.note.chara.push(tempCharaNote);
207 } else {208 } else {
208 console.log('Character author\'s note error: No avatar name key could be found.');209 console.log('Character author\'s note error: No avatar name key could be found.');
209 toastr.error('Something went wrong. Could not save character\'s author\'s note.');210 toastr.error(t`Something went wrong. Could not save character's author's note.`);
210211
211 // Don't save settings if something went wrong212 // Don't save settings if something went wrong
212 return;213 return;
@@ -397,7 +398,7 @@ function onANMenuItemClick() {
397 //because this listener takes priority398 //because this listener takes priority
398 $('#options').stop().fadeOut(animation_duration);399 $('#options').stop().fadeOut(animation_duration);
399 } else {400 } else {
400 toastr.warning('Select a character before trying to use Author\'s Note', '', { timeOut: 2000 });401 toastr.warning(t`Select a character before trying to use Author's Note`, '', { timeOut: 2000 });
401 }402 }
402}403}
403404
public/scripts/chats.js+13 -12
@@ -40,6 +40,7 @@ import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
40import { ScraperManager } from './scrapers.js';40import { ScraperManager } from './scrapers.js';
41import { DragAndDropHandler } from './dragdrop.js';41import { DragAndDropHandler } from './dragdrop.js';
42import { renderTemplateAsync } from './templates.js';42import { renderTemplateAsync } from './templates.js';
43import { t } from './i18n.js';
4344
44/**45/**
45 * @typedef {Object} FileAttachment46 * @typedef {Object} FileAttachment
@@ -206,7 +207,7 @@ export async function populateFileAttachment(message, inputId = 'file_form_input
206 const fileText = await converter(file);207 const fileText = await converter(file);
207 base64Data = window.btoa(unescape(encodeURIComponent(fileText)));208 base64Data = window.btoa(unescape(encodeURIComponent(fileText)));
208 } catch (error) {209 } catch (error) {
209 toastr.error(String(error), 'Could not convert file');210 toastr.error(String(error), t`Could not convert file`);
210 console.error('Could not convert file', error);211 console.error('Could not convert file', error);
211 }212 }
212 }213 }
@@ -257,7 +258,7 @@ export async function uploadFileAttachment(fileName, base64Data) {
257 const responseData = await result.json();258 const responseData = await result.json();
258 return responseData.path;259 return responseData.path;
259 } catch (error) {260 } catch (error) {
260 toastr.error(String(error), 'Could not upload file');261 toastr.error(String(error), t`Could not upload file`);
261 console.error('Could not upload file', error);262 console.error('Could not upload file', error);
262 }263 }
263}264}
@@ -283,7 +284,7 @@ export async function getFileAttachment(url) {
283 const text = await result.text();284 const text = await result.text();
284 return text;285 return text;
285 } catch (error) {286 } catch (error) {
286 toastr.error(error, 'Could not download file');287 toastr.error(error, t`Could not download file`);
287 console.error('Could not download file', error);288 console.error('Could not download file', error);
288 }289 }
289}290}
@@ -299,13 +300,13 @@ async function validateFile(file) {
299 const isBinary = /^[\x00-\x08\x0E-\x1F\x7F-\xFF]*$/.test(fileText);300 const isBinary = /^[\x00-\x08\x0E-\x1F\x7F-\xFF]*$/.test(fileText);
300301
301 if (!isImage && file.size > fileSizeLimit) {302 if (!isImage && file.size > fileSizeLimit) {
302 toastr.error(`File is too big. Maximum size is ${humanFileSize(fileSizeLimit)}.`);303 toastr.error(t`File is too big. Maximum size is ${humanFileSize(fileSizeLimit)}.`);
303 return false;304 return false;
304 }305 }
305306
306 // If file is binary307 // If file is binary
307 if (isBinary && !isImage && !isConvertible(file.type)) {308 if (isBinary && !isImage && !isConvertible(file.type)) {
308 toastr.error('Binary files are not supported. Select a text file or image.');309 toastr.error(t`Binary files are not supported. Select a text file or image.`);
309 return false;310 return false;
310 }311 }
311312
@@ -521,7 +522,7 @@ async function openExternalMediaOverridesDialog() {
521 const entityId = getCurrentEntityId();522 const entityId = getCurrentEntityId();
522523
523 if (!entityId) {524 if (!entityId) {
524 toastr.info('No character or group selected');525 toastr.info(t`No character or group selected`);
525 return;526 return;
526 }527 }
527528
@@ -646,7 +647,7 @@ async function deleteFileFromServer(url, silent = false) {
646 await eventSource.emit(event_types.FILE_ATTACHMENT_DELETED, url);647 await eventSource.emit(event_types.FILE_ATTACHMENT_DELETED, url);
647 return true;648 return true;
648 } catch (error) {649 } catch (error) {
649 toastr.error(String(error), 'Could not delete file');650 toastr.error(String(error), t`Could not delete file`);
650 console.error('Could not delete file', error);651 console.error('Could not delete file', error);
651 return false;652 return false;
652 }653 }
@@ -1054,7 +1055,7 @@ async function openAttachmentManager() {
1054 const selectedAttachments = document.querySelectorAll('.attachmentListItemCheckboxContainer .attachmentListItemCheckbox:checked');1055 const selectedAttachments = document.querySelectorAll('.attachmentListItemCheckboxContainer .attachmentListItemCheckbox:checked');
10551056
1056 if (selectedAttachments.length === 0) {1057 if (selectedAttachments.length === 0) {
1057 toastr.info('No attachments selected.', 'Data Bank');1058 toastr.info(t`No attachments selected.`, t`Data Bank`);
1058 return;1059 return;
1059 }1060 }
10601061
@@ -1168,7 +1169,7 @@ async function runScraper(scraperId, target, callback) {
11681169
1169 if (files.length === 0) {1170 if (files.length === 0) {
1170 console.warn('Scraping returned no files');1171 console.warn('Scraping returned no files');
1171 toastr.info('No files were scraped.', 'Data Bank');1172 toastr.info(t`No files were scraped.`, t`Data Bank`);
1172 return;1173 return;
1173 }1174 }
11741175
@@ -1176,12 +1177,12 @@ async function runScraper(scraperId, target, callback) {
1176 await uploadFileAttachmentToServer(file, target);1177 await uploadFileAttachmentToServer(file, target);
1177 }1178 }
11781179
1179 toastr.success(`Scraped ${files.length} files from ${scraperId} to ${target}.`, 'Data Bank');1180 toastr.success(t`Scraped ${files.length} files from ${scraperId} to ${target}.`, t`Data Bank`);
1180 callback();1181 callback();
1181 }1182 }
1182 catch (error) {1183 catch (error) {
1183 console.error('Scraping failed', error);1184 console.error('Scraping failed', error);
1184 toastr.error('Check browser console for details.', 'Scraping failed');1185 toastr.error(t`Check browser console for details.`, t`Scraping failed`);
1185 }1186 }
1186}1187}
11871188
@@ -1208,7 +1209,7 @@ export async function uploadFileAttachmentToServer(file, target) {
1208 const fileText = await converter(file);1209 const fileText = await converter(file);
1209 base64Data = window.btoa(unescape(encodeURIComponent(fileText)));1210 base64Data = window.btoa(unescape(encodeURIComponent(fileText)));
1210 } catch (error) {1211 } catch (error) {
1211 toastr.error(String(error), 'Could not convert file');1212 toastr.error(String(error), t`Could not convert file`);
1212 console.error('Could not convert file', error);1213 console.error('Could not convert file', error);
1213 }1214 }
1214 } else {1215 } else {
public/scripts/extensions/regex/editor.html+1 -1
@@ -132,7 +132,7 @@
132 <small data-i18n="ext_regex_other_options" data-i18n="Ephemerality">Ephemerality</small>132 <small data-i18n="ext_regex_other_options" data-i18n="Ephemerality">Ephemerality</small>
133 <span class="fa-solid fa-circle-question note-link-span" title="By default, regex scripts alter the chat file directly and irreversibly.&#13;Enabling either (or both) of the options below will prevent chat file alteration, while still altering the specified item(s)."></span>133 <span class="fa-solid fa-circle-question note-link-span" title="By default, regex scripts alter the chat file directly and irreversibly.&#13;Enabling either (or both) of the options below will prevent chat file alteration, while still altering the specified item(s)."></span>
134 </span>134 </span>
135 <label class="checkbox flex-container" title="Chat history file contents won't change, but regex will be applied to the messages displayed in the Chat UI.">135 <label class="checkbox flex-container" data-i18n="[title]ext_regex_only_format_visual_desc" title="Chat history file contents won't change, but regex will be applied to the messages displayed in the Chat UI.">
136 <input type="checkbox" name="only_format_display" />136 <input type="checkbox" name="only_format_display" />
137 <span data-i18n="Only Format Display">Alter Chat Display</span>137 <span data-i18n="Only Format Display">Alter Chat Display</span>
138 </label>138 </label>
public/scripts/group-chats.js+10 -10
@@ -189,8 +189,8 @@ async function validateGroup(group) {
189 group.members = group.members.filter(member => {189 group.members = group.members.filter(member => {
190 const character = characters.find(x => x.avatar === member || x.name === member);190 const character = characters.find(x => x.avatar === member || x.name === member);
191 if (!character) {191 if (!character) {
192 const msg = `Warning: Listed member ${member} does not exist as a character. It will be removed from the group.`;192 const msg = t`Warning: Listed member ${member} does not exist as a character. It will be removed from the group.`;
193 toastr.warning(msg, 'Group Validation');193 toastr.warning(msg, t`Group Validation`);
194 console.warn(msg);194 console.warn(msg);
195 dirty = true;195 dirty = true;
196 }196 }
@@ -522,7 +522,7 @@ async function saveGroupChat(groupId, shouldSaveGroup) {
522 });522 });
523523
524 if (!response.ok) {524 if (!response.ok) {
525 toastr.error('Check the server connection and reload the page to prevent data loss.', 'Group Chat could not be saved');525 toastr.error(t`Check the server connection and reload the page to prevent data loss.`, t`Group Chat could not be saved`);
526 console.error('Group chat could not be saved', response);526 console.error('Group chat could not be saved', response);
527 return;527 return;
528 }528 }
@@ -837,7 +837,7 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
837 activatedMembers = activateSwipe(group.members);837 activatedMembers = activateSwipe(group.members);
838838
839 if (activatedMembers.length === 0) {839 if (activatedMembers.length === 0) {
840 toastr.warning('Deleted group member swiped. To get a reply, add them back to the group.');840 toastr.warning(t`Deleted group member swiped. To get a reply, add them back to the group.`);
841 throw new Error('Deleted group member swiped');841 throw new Error('Deleted group member swiped');
842 }842 }
843 }843 }
@@ -1368,15 +1368,15 @@ function isGroupMemberDisabled(avatarId) {
13681368
1369async function onDeleteGroupClick() {1369async function onDeleteGroupClick() {
1370 if (!openGroupId) {1370 if (!openGroupId) {
1371 toastr.warning('Currently no group selected.');1371 toastr.warning(t`Currently no group selected.`);
1372 return;1372 return;
1373 }1373 }
1374 if (is_group_generating) {1374 if (is_group_generating) {
1375 toastr.warning('Not so fast! Wait for the characters to stop typing before deleting the group.');1375 toastr.warning(t`Not so fast! Wait for the characters to stop typing before deleting the group.`);
1376 return;1376 return;
1377 }1377 }
13781378
1379 const confirm = await Popup.show.confirm('Delete the group?', '<p>This will also delete all your chats with that group. If you want to delete a single conversation, select a "View past chats" option in the lower left menu.</p>');1379 const confirm = await Popup.show.confirm(t`Delete the group?`, '<p>' + t`This will also delete all your chats with that group. If you want to delete a single conversation, select a "View past chats" option in the lower left menu.` + '</p>');
1380 if (confirm) {1380 if (confirm) {
1381 deleteGroup(openGroupId);1381 deleteGroup(openGroupId);
1382 }1382 }
@@ -1630,7 +1630,7 @@ function updateFavButtonState(state) {
16301630
1631export async function openGroupById(groupId) {1631export async function openGroupById(groupId) {
1632 if (isChatSaving) {1632 if (isChatSaving) {
1633 toastr.info('Please wait until the chat is saved before switching characters.', 'Your chat is still saving...');1633 toastr.info(t`Please wait until the chat is saved before switching characters.`, t`Your chat is still saving...`);
1634 return;1634 return;
1635 }1635 }
16361636
@@ -1659,7 +1659,7 @@ export async function openGroupById(groupId) {
16591659
1660function openCharacterDefinition(characterSelect) {1660function openCharacterDefinition(characterSelect) {
1661 if (is_group_generating) {1661 if (is_group_generating) {
1662 toastr.warning('Can\'t peek a character while group reply is being generated');1662 toastr.warning(t`Can't peek a character while group reply is being generated`);
1663 console.warn('Can\'t peek a character def while group reply is being generated');1663 console.warn('Can\'t peek a character def while group reply is being generated');
1664 return;1664 return;
1665 }1665 }
@@ -1908,7 +1908,7 @@ export async function saveGroupBookmarkChat(groupId, name, metadata, mesId) {
1908 });1908 });
19091909
1910 if (!response.ok) {1910 if (!response.ok) {
1911 toastr.error('Check the server connection and reload the page to prevent data loss.', 'Group chat could not be saved');1911 toastr.error(t`Check the server connection and reload the page to prevent data loss.`, t`Group chat could not be saved`);
1912 console.error('Group chat could not be saved', response);1912 console.error('Group chat could not be saved', response);
1913 }1913 }
1914}1914}
public/scripts/openai.js+25 -28
@@ -388,7 +388,7 @@ async function validateReverseProxy() {
388 new URL(oai_settings.reverse_proxy);388 new URL(oai_settings.reverse_proxy);
389 }389 }
390 catch (err) {390 catch (err) {
391 toastr.error('Entered reverse proxy address is not a valid URL');391 toastr.error(t`Entered reverse proxy address is not a valid URL`);
392 setOnlineStatus('no_connection');392 setOnlineStatus('no_connection');
393 resultCheckStatus();393 resultCheckStatus();
394 throw err;394 throw err;
@@ -399,7 +399,7 @@ async function validateReverseProxy() {
399 const confirmation = skipConfirm || await Popup.show.confirm(t`Connecting To Proxy`, await renderTemplateAsync('proxyConnectionWarning', { proxyURL: DOMPurify.sanitize(oai_settings.reverse_proxy) }));399 const confirmation = skipConfirm || await Popup.show.confirm(t`Connecting To Proxy`, await renderTemplateAsync('proxyConnectionWarning', { proxyURL: DOMPurify.sanitize(oai_settings.reverse_proxy) }));
400400
401 if (!confirmation) {401 if (!confirmation) {
402 toastr.error('Update or remove your reverse proxy settings.');402 toastr.error(t`Update or remove your reverse proxy settings.`);
403 setOnlineStatus('no_connection');403 setOnlineStatus('no_connection');
404 resultCheckStatus();404 resultCheckStatus();
405 throw new Error('Proxy connection denied.');405 throw new Error('Proxy connection denied.');
@@ -1225,15 +1225,15 @@ export async function prepareOpenAIMessages({
1225 await populateChatCompletion(prompts, chatCompletion, { bias, quietPrompt, quietImage, type, cyclePrompt, messages, messageExamples });1225 await populateChatCompletion(prompts, chatCompletion, { bias, quietPrompt, quietImage, type, cyclePrompt, messages, messageExamples });
1226 } catch (error) {1226 } catch (error) {
1227 if (error instanceof TokenBudgetExceededError) {1227 if (error instanceof TokenBudgetExceededError) {
1228 toastr.error('An error occurred while counting tokens: Token budget exceeded.');1228 toastr.error(t`An error occurred while counting tokens: Token budget exceeded.`);
1229 chatCompletion.log('Token budget exceeded.');1229 chatCompletion.log('Token budget exceeded.');
1230 promptManager.error = 'Not enough free tokens for mandatory prompts. Raise your token Limit or disable custom prompts.';1230 promptManager.error = t`Not enough free tokens for mandatory prompts. Raise your token Limit or disable custom prompts.`;
1231 } else if (error instanceof InvalidCharacterNameError) {1231 } else if (error instanceof InvalidCharacterNameError) {
1232 toastr.warning('An error occurred while counting tokens: Invalid character name');1232 toastr.warning(t`An error occurred while counting tokens: Invalid character name`);
1233 chatCompletion.log('Invalid character name');1233 chatCompletion.log('Invalid character name');
1234 promptManager.error = 'The name of at least one character contained whitespaces or special characters. Please check your user and character name.';1234 promptManager.error = t`The name of at least one character contained whitespaces or special characters. Please check your user and character name.`;
1235 } else {1235 } else {
1236 toastr.error('An unknown error occurred while counting tokens. Further information may be available in console.');1236 toastr.error(t`An unknown error occurred while counting tokens. Further information may be available in console.`);
1237 chatCompletion.log('----- Unexpected error while preparing prompts -----');1237 chatCompletion.log('----- Unexpected error while preparing prompts -----');
1238 chatCompletion.log(error);1238 chatCompletion.log(error);
1239 chatCompletion.log(error.stack);1239 chatCompletion.log(error.stack);
@@ -1287,11 +1287,8 @@ function tryParseStreamingError(response, decoded) {
1287 }1287 }
1288}1288}
12891289
1290function checkQuotaError(data) {1290async function checkQuotaError(data) {
1291 const errorText = `<h3>Encountered an error while processing your request.<br>1291 const errorText = await renderTemplateAsync('quotaError');
1292 Check you have credits available on your
1293 <a href="https://platform.openai.com/account/usage" target="_blank">OpenAI account</a>.<br>
1294 If you have sufficient credits, please try again later.</h3>`;
12951292
1296 if (!data) {1293 if (!data) {
1297 return;1294 return;
@@ -1927,11 +1924,11 @@ async function sendOpenAIRequest(type, messages, signal) {
1927 else {1924 else {
1928 const data = await response.json();1925 const data = await response.json();
19291926
1930 checkQuotaError(data);1927 await checkQuotaError(data);
1931 checkModerationError(data);1928 checkModerationError(data);
19321929
1933 if (data.error) {1930 if (data.error) {
1934 toastr.error(data.error.message || response.statusText, 'API returned an error');1931 toastr.error(data.error.message || response.statusText, t`API returned an error`);
1935 throw new Error(data);1932 throw new Error(data);
1936 }1933 }
19371934
@@ -2178,7 +2175,7 @@ function parseOpenAITextLogprobs(logprobs) {
21782175
2179function handleWindowError(err) {2176function handleWindowError(err) {
2180 const text = parseWindowError(err);2177 const text = parseWindowError(err);
2181 toastr.error(text, 'Window.ai returned an error');2178 toastr.error(text, t`Window.ai returned an error`);
2182 throw err;2179 throw err;
2183}2180}
21842181
@@ -3251,7 +3248,7 @@ async function getStatusOpen() {
3251}3248}
32523249
3253function showWindowExtensionError() {3250function showWindowExtensionError() {
3254 toastr.error('Get it here: <a href="https://windowai.io/" target="_blank">windowai.io</a>', 'Extension is not installed', {3251 toastr.error(t`Get it here:` + ' <a href="https://windowai.io/" target="_blank">windowai.io</a>', t`Extension is not installed`, {
3255 escapeHtml: false,3252 escapeHtml: false,
3256 timeOut: 0,3253 timeOut: 0,
3257 extendedTimeOut: 0,3254 extendedTimeOut: 0,
@@ -3370,7 +3367,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
3370 if (triggerUi) $('#settings_preset_openai').append(option).trigger('change');3367 if (triggerUi) $('#settings_preset_openai').append(option).trigger('change');
3371 }3368 }
3372 } else {3369 } else {
3373 toastr.error('Failed to save preset');3370 toastr.error(t`Failed to save preset`);
3374 throw new Error('Failed to save preset');3371 throw new Error('Failed to save preset');
3375 }3372 }
3376}3373}
@@ -3449,7 +3446,7 @@ async function createNewLogitBiasPreset() {
3449 }3446 }
34503447
3451 if (name in oai_settings.bias_presets) {3448 if (name in oai_settings.bias_presets) {
3452 toastr.error('Preset name should be unique.');3449 toastr.error(t`Preset name should be unique.`);
3453 return;3450 return;
3454 }3451 }
34553452
@@ -3493,7 +3490,7 @@ async function onPresetImportFileChange(e) {
3493 try {3490 try {
3494 presetBody = JSON.parse(importedFile);3491 presetBody = JSON.parse(importedFile);
3495 } catch (err) {3492 } catch (err) {
3496 toastr.error('Invalid file');3493 toastr.error(t`Invalid file`);
3497 return;3494 return;
3498 }3495 }
34993496
@@ -3534,7 +3531,7 @@ async function onPresetImportFileChange(e) {
3534 });3531 });
35353532
3536 if (!savePresetSettings.ok) {3533 if (!savePresetSettings.ok) {
3537 toastr.error('Failed to save preset');3534 toastr.error(t`Failed to save preset`);
3538 return;3535 return;
3539 }3536 }
35403537
@@ -3559,7 +3556,7 @@ async function onPresetImportFileChange(e) {
35593556
3560async function onExportPresetClick() {3557async function onExportPresetClick() {
3561 if (!oai_settings.preset_settings_openai) {3558 if (!oai_settings.preset_settings_openai) {
3562 toastr.error('No preset selected');3559 toastr.error(t`No preset selected`);
3563 return;3560 return;
3564 }3561 }
35653562
@@ -3600,12 +3597,12 @@ async function onLogitBiasPresetImportFileChange(e) {
3600 e.target.value = '';3597 e.target.value = '';
36013598
3602 if (name in oai_settings.bias_presets) {3599 if (name in oai_settings.bias_presets) {
3603 toastr.error('Preset name should be unique.');3600 toastr.error(t`Preset name should be unique.`);
3604 return;3601 return;
3605 }3602 }
36063603
3607 if (!Array.isArray(importedFile)) {3604 if (!Array.isArray(importedFile)) {
3608 toastr.error('Invalid logit bias preset file.');3605 toastr.error(t`Invalid logit bias preset file.`);
3609 return;3606 return;
3610 }3607 }
36113608
@@ -3664,16 +3661,16 @@ async function onDeletePresetClick() {
3664 });3661 });
36653662
3666 if (!response.ok) {3663 if (!response.ok) {
3667 toastr.warning('Preset was not deleted from server');3664 toastr.warning(t`Preset was not deleted from server`);
3668 } else {3665 } else {
3669 toastr.success('Preset deleted');3666 toastr.success(t`Preset deleted`);
3670 }3667 }
36713668
3672 saveSettingsDebounced();3669 saveSettingsDebounced();
3673}3670}
36743671
3675async function onLogitBiasPresetDeleteClick() {3672async function onLogitBiasPresetDeleteClick() {
3676 const value = await callPopup('Delete the preset?', 'confirm');3673 const value = await callPopup(t`Delete the preset?`, 'confirm');
36773674
3678 if (!value) {3675 if (!value) {
3679 return;3676 return;
@@ -4808,7 +4805,7 @@ function runProxyCallback(_, value) {
4808 const result = fuse.search(value);4805 const result = fuse.search(value);
48094806
4810 if (result.length === 0) {4807 if (result.length === 0) {
4811 toastr.warning(`Proxy preset "${value}" not found`);4808 toastr.warning(t`Proxy preset '${value}' not found`);
4812 return '';4809 return '';
4813 }4810 }
48144811
@@ -4982,7 +4979,7 @@ export function initOpenAI() {
4982 $('#update_oai_preset').on('click', async function () {4979 $('#update_oai_preset').on('click', async function () {
4983 const name = oai_settings.preset_settings_openai;4980 const name = oai_settings.preset_settings_openai;
4984 await saveOpenAIPreset(name, oai_settings);4981 await saveOpenAIPreset(name, oai_settings);
4985 toastr.success('Preset updated');4982 toastr.success(t`Preset updated`);
4986 });4983 });
49874984
4988 $('#impersonation_prompt_restore').on('click', function () {4985 $('#impersonation_prompt_restore').on('click', function () {
public/scripts/templates/assistantNote.html+1 -1
@@ -1,3 +1,3 @@
1<div>1<div>
2 <b>Note:</b> this chat is temporary and will be deleted as soon as you leave it.2 <b data-i18n="assistant_note_1">Note:</b> <span data-i18n="assistant_note_2">this chat is temporary and will be deleted as soon as you leave it.</span>
3</div>3</div>
public/scripts/templates/deleteConfirm.html+5 -0
@@ -0,0 +1,5 @@
1<b><span data-i18n="delete_confirm_1">THIS IS PERMANENT!</span><br><br>
2<label for="del_char_checkbox" class="checkbox_label justifyCenter">
3 <input type="checkbox" id="del_char_checkbox" />
4 <small data-i18n="delete_confirm_2">Also delete the chat files</small>
5</label></b>
\ No newline at end of file5 \ No newline at end of file
public/scripts/templates/duplicateConfirm.html+2 -2
@@ -1,5 +1,5 @@
1<div>1<div>
2 <h3>Are you sure you want to duplicate this character?</h3>2 <h3 data-i18n="duplicate_confirm_1">Are you sure you want to duplicate this character?</h3>
3 <span>If you just want to start a new chat with the same character, use "Start new chat" option in the bottom-left options menu.</span>3 <span data-i18n="duplicate_confirm_2">If you just want to start a new chat with the same character, use "Start new chat" option in the bottom-left options menu.</span>
4 <br>4 <br>
5</div>5</div>
public/scripts/templates/hotkeys.html+7 -7
@@ -14,18 +14,18 @@
14 <li><kbd data-i18n="help_hotkeys_19">Ctrl+Shift+Down</kbd> = <span data-i18n="help_hotkeys_20">Scroll chat to bottom</span></li>14 <li><kbd data-i18n="help_hotkeys_19">Ctrl+Shift+Down</kbd> = <span data-i18n="help_hotkeys_20">Scroll chat to bottom</span></li>
15</ul>15</ul>
16<div>16<div>
17 <strong>Markdown Hotkeys</strong>17 <strong data-i18n="help_hotkeys_20">Markdown Hotkeys</strong>
18</div>18</div>
19<div>19<div>
20 <small>20 <small>
21 <span>Works in the chatbar and textareas marked with this icon:</span>21 <span data-i18n="help_hotkeys_21">Works in the chatbar and textareas marked with this icon:</span>
22 <code><i class="fa-brands fa-markdown"></i></code>22 <code><i class="fa-brands fa-markdown"></i></code>
23 </small>23 </small>
24</div>24</div>
25<ul>25<ul>
26 <li><kbd>Ctrl+B</kbd> = <span>**bold**</span></li>26 <li><kbd>Ctrl+B</kbd> = <span data-i18n="help_hotkeys_22">**bold**</span></li>
27 <li><kbd>Ctrl+I</kbd> = <span>*italic*</span></li>27 <li><kbd>Ctrl+I</kbd> = <span data-i18n="help_hotkeys_23">*italic*</span></li>
28 <li><kbd>Ctrl+U</kbd> = <span>__underline__</span></li>28 <li><kbd>Ctrl+U</kbd> = <span data-i18n="help_hotkeys_24">__underline__</span></li>
29 <li><kbd>Ctrl+K</kbd> = <span>`inline code`</span></li>29 <li><kbd>Ctrl+K</kbd> = <span data-i18n="help_hotkeys_25">`inline code`</span></li>
30 <li><kbd>Ctrl+Shift+~</kbd> = <span>~~strikethrough~~</span></li>30 <li><kbd>Ctrl+Shift+~</kbd> = <span data-i18n="help_hotkeys_26">~~strikethrough~~</span></li>
31</ul>31</ul>
public/scripts/templates/quotaError.html+4 -0
@@ -0,0 +1,4 @@
1<h3><span data-i18n="quota_error_1">Encountered an error while processing your request.</span><br>
2 <span data-i18n="quota_error_2">Check you have credits available on your</span>
3 <a href="https://platform.openai.com/account/usage" target="_blank" data-i18n="quota_error_3">OpenAI account</a><span data-i18n="quota_error_4">.</span><br>
4 <span data-i18n="quota_error_5">If you have sufficient credits, please try again later.</span></h3>
\ No newline at end of file4 \ No newline at end of file