Work on tl
| @@ -451,8 +451,8 @@ | ||
| 451 | 451 | "Creator's Notes": "Заметки создателя", |
| 452 | 452 | "A-Z": "A-Z", |
| 453 | 453 | "Z-A": "Z-A", |
| 454 | 454 | "Newest": "НовейшиеСначала новые", |
| 455 | 455 | "Oldest": "СтарейшиеСначала старые", |
| 456 | 456 | "Favorites": "Избранные", |
| 457 | 457 | "Recent": "Последние", |
| 458 | 458 | "Most chats": "Больше всего чатов", |
| @@ -564,7 +564,7 @@ | ||
| 564 | 564 | "Advanced Definition": "Расширенное описание", |
| 565 | 565 | "Character Lore": "Лор персонажа", |
| 566 | 566 | "Export and Download": "Экспортировать и скачать", |
| 567 | 567 | "Duplicate Character": "ДублироватьКлонировать персонажа", |
| 568 | 568 | "Create Character": "Создать персонажа", |
| 569 | 569 | "Delete Character": "Удалить персонажа", |
| 570 | 570 | "View all tags": "Показать все тэги", |
| @@ -1690,7 +1690,7 @@ | ||
| 1690 | 1690 | "Delete Message": "Удалить сообщение", |
| 1691 | 1691 | "Delete Swipe": "Удалить свайп", |
| 1692 | 1692 | "Could not get a reply from API. Check your connection settings / API key and try again.": "Не удалось получить ответ от API. Проверьте настройки соединения и API-ключ и повторите попытку.", |
| 1693 | 1693 | "Connecting To Proxy": "ПодключениеПодключиться к прокси", |
| 1694 | 1694 | "Are you sure you want to connect to the following proxy URL?": "Вы точно хотите соединиться с прокси по этому адресу?", |
| 1695 | 1695 | "API connection successful!": "Соединение с API установлено!", |
| 1696 | 1696 | "Proxy Saved": "Прокси сохранена", |
| @@ -1747,5 +1747,142 @@ | ||
| 1747 | 1747 | "Markdown Hotkeys": "Горячие клавиши для разметки", |
| 1748 | 1748 | "markdown_hotkeys_desc": "Включить горячие клавиши для вставки символов разметки в некоторых полях ввода. См. '/help hotkeys'.", |
| 1749 | 1749 | "Save and Update": "Сохранить и обновить", |
| 1750 | 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 | } |
| @@ -383,8 +383,8 @@ DOMPurify.addHook('uponSanitizeElement', (node, _, config) => { | ||
| 383 | 383 | |
| 384 | 384 | if (localStorage.getItem(warningShownKey) === null) { |
| 385 | 385 | const warningToast = toastr.warning( |
| 386 | 386 | 't`Use the "'Ext. Media"' button to allow it. Click on this message to dismiss.'`, |
| 387 | 387 | 't`External media has been blocked'`, |
| 388 | 388 | { |
| 389 | 389 | timeOut: 0, |
| 390 | 390 | preventDuplicates: true, |
| @@ -924,7 +924,7 @@ async function firstLoadInit() { | ||
| 924 | 924 | token = tokenData.token; |
| 925 | 925 | } catch { |
| 926 | 926 | hideLoader(); |
| 927 | 927 | toastr.error('t`Couldn\'t get CSRF token. Please refresh the page.'`, 't`Error'`, { timeOut: 0, extendedTimeOut: 0, preventDuplicates: true }); |
| 928 | 928 | throw new Error('Initialization failed'); |
| 929 | 929 | } |
| 930 | 930 | |
| @@ -1140,7 +1140,7 @@ async function getStatusKobold() { | ||
| 1140 | 1140 | |
| 1141 | 1141 | // We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress. |
| 1142 | 1142 | if (online_status === 'no_connection' && data.response) { |
| 1143 | 1143 | toastr.error(data.response, 't`API Error'`, { timeOut: 5000, preventDuplicates: true }); |
| 1144 | 1144 | } |
| 1145 | 1145 | } catch (err) { |
| 1146 | 1146 | console.error('Error getting status', err); |
| @@ -1226,7 +1226,7 @@ async function getStatusTextgen() { | ||
| 1226 | 1226 | |
| 1227 | 1227 | // We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress. |
| 1228 | 1228 | if (online_status === 'no_connection' && data.response) { |
| 1229 | 1229 | toastr.error(data.response, 't`API Error'`, { timeOut: 5000, preventDuplicates: true }); |
| 1230 | 1230 | } |
| 1231 | 1231 | } catch (err) { |
| 1232 | 1232 | if (err instanceof AbortReason) { |
| @@ -1278,7 +1278,7 @@ export async function selectCharacterById(id) { | ||
| 1278 | 1278 | } |
| 1279 | 1279 | |
| 1280 | 1280 | if (isChatSaving) { |
| 1281 | 1281 | toastr.info('t`Please wait until the chat is saved before switching characters.'`, 't`Your chat is still saving...'`); |
| 1282 | 1282 | return; |
| 1283 | 1283 | } |
| 1284 | 1284 | |
| @@ -1632,7 +1632,7 @@ export async function getOneCharacter(avatarUrl) { | ||
| 1632 | 1632 | if (indexOf !== -1) { |
| 1633 | 1633 | characters[indexOf] = getData; |
| 1634 | 1634 | } else { |
| 1635 | 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 | 3374 | await eventSource.emit(event_types.GENERATION_AFTER_COMMANDS, type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage }, dryRun); |
| 3375 | 3375 | |
| 3376 | 3376 | if (main_api == 'kobold' && kai_settings.streaming_kobold && !kai_flags.can_use_streaming) { |
| 3377 | 3377 | toastr.error('t`Streaming is enabled, but the version of Kobold used does not support token streaming.'`, undefined, { timeOut: 10000, preventDuplicates: true }); |
| 3378 | 3378 | unblockGeneration(type); |
| 3379 | 3379 | return Promise.resolve(); |
| 3380 | 3380 | } |
| @@ -3383,7 +3383,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 3383 | 3383 | textgen_settings.streaming && |
| 3384 | 3384 | textgen_settings.legacy_api && |
| 3385 | 3385 | textgen_settings.type === OOBA) { |
| 3386 | 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 | 3387 | unblockGeneration(type); |
| 3388 | 3388 | return Promise.resolve(); |
| 3389 | 3389 | } |
| @@ -3399,7 +3399,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 3399 | 3399 | |
| 3400 | 3400 | if (!pingResult) { |
| 3401 | 3401 | unblockGeneration(type); |
| 3402 | 3402 | toastr.error('t`Verify that the server is running and accessible.'`, 't`ST Server cannot be reached'`); |
| 3403 | 3403 | throw new Error('Server unreachable'); |
| 3404 | 3404 | } |
| 3405 | 3405 | |
| @@ -4427,7 +4427,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 4427 | 4427 | generatedPromptCache = ''; |
| 4428 | 4428 | |
| 4429 | 4429 | if (data?.response) { |
| 4430 | 4430 | toastr.error(data.response, 't`API Error'`, { preventDuplicates: true }); |
| 4431 | 4431 | } |
| 4432 | 4432 | throw new Error(data?.response); |
| 4433 | 4433 | } |
| @@ -4518,7 +4518,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 4518 | 4518 | |
| 4519 | 4519 | function onError(exception) { |
| 4520 | 4520 | if (typeof exception?.error?.message === 'string') { |
| 4521 | 4521 | toastr.error(exception.error.message, 't`Error'`, { timeOut: 10000, extendedTimeOut: 20000 }); |
| 4522 | 4522 | } |
| 4523 | 4523 | |
| 4524 | 4524 | generatedPromptCache = ''; |
| @@ -4920,7 +4920,7 @@ function addChatsSeparator(mesSendString) { | ||
| 4920 | 4920 | |
| 4921 | 4921 | async function duplicateCharacter() { |
| 4922 | 4922 | if (!this_chid) { |
| 4923 | 4923 | toastr.warning('t`You must first select a character to duplicate!'`); |
| 4924 | 4924 | return ''; |
| 4925 | 4925 | } |
| 4926 | 4926 | |
| @@ -4939,7 +4939,7 @@ async function duplicateCharacter() { | ||
| 4939 | 4939 | body: JSON.stringify(body), |
| 4940 | 4940 | }); |
| 4941 | 4941 | if (response.ok) { |
| 4942 | 4942 | toastr.success('t`Character Duplicated'`); |
| 4943 | 4943 | const data = await response.json(); |
| 4944 | 4944 | await eventSource.emit(event_types.CHARACTER_DUPLICATED, { oldAvatar: body.avatar_url, newAvatar: data.path }); |
| 4945 | 4945 | await getCharacters(); |
| @@ -5786,23 +5786,23 @@ export function setSendButtonState(value) { | ||
| 5786 | 5786 | |
| 5787 | 5787 | export async function renameCharacter(name = null, { silent = false, renameChats = null } = {}) { |
| 5788 | 5788 | if (!name && silent) { |
| 5789 | 5789 | toastr.warning('t`No character name provided.'`, 't`Rename Character'`); |
| 5790 | 5790 | return false; |
| 5791 | 5791 | } |
| 5792 | 5792 | if (this_chid === undefined) { |
| 5793 | 5793 | toastr.warning('t`No character selected.'`, 't`Rename Character'`); |
| 5794 | 5794 | return false; |
| 5795 | 5795 | } |
| 5796 | 5796 | |
| 5797 | 5797 | const oldAvatar = characters[this_chid].avatar; |
| 5798 | 5798 | const newValue = name || await callGenericPopup('<h3>' + t`New name:` + '</h3>', POPUP_TYPE.INPUT, characters[this_chid].name); |
| 5799 | 5799 | |
| 5800 | 5800 | if (!newValue) { |
| 5801 | 5801 | toastr.warning('t`No character name provided.'`, 't`Rename Character'`); |
| 5802 | 5802 | return false; |
| 5803 | 5803 | } |
| 5804 | 5804 | if (newValue === characters[this_chid].name) { |
| 5805 | 5805 | toastr.info('t`Same character name provided, so name did not change.'`, 't`Rename Character'`); |
| 5806 | 5806 | return false; |
| 5807 | 5807 | } |
| 5808 | 5808 | |
| @@ -5849,9 +5849,9 @@ export async function renameCharacter(name = null, { silent = false, renameChats | ||
| 5849 | 5849 | if (renamePastChatsConfirm) { |
| 5850 | 5850 | await renamePastChats(newAvatar, newValue); |
| 5851 | 5851 | await reloadCurrentChat(); |
| 5852 | 5852 | toastr.success('t`Character renamed and past chats updated!'`, 't`Rename Character'`); |
| 5853 | 5853 | } else { |
| 5854 | 5854 | toastr.success('t`Character renamed!'`, 't`Rename Character'`); |
| 5855 | 5855 | } |
| 5856 | 5856 | } |
| 5857 | 5857 | else { |
| @@ -5864,8 +5864,8 @@ export async function renameCharacter(name = null, { silent = false, renameChats | ||
| 5864 | 5864 | } |
| 5865 | 5865 | catch (error) { |
| 5866 | 5866 | // Reloading to prevent data corruption |
| 5867 | 5867 | if (!silent) await callPopup('t`Something went wrong. The page will be reloaded.'`, 'text'); |
| 5868 | 5868 | else toastr.error('t`Something went wrong. The page will be reloaded.'`, 't`Rename Character'`); |
| 5869 | 5869 | |
| 5870 | 5870 | console.log('Renaming character error:', error); |
| 5871 | 5871 | location.reload(); |
| @@ -5922,7 +5922,7 @@ async function renamePastChats(newAvatar, newValue) { | ||
| 5922 | 5922 | } |
| 5923 | 5923 | } |
| 5924 | 5924 | } catch (error) { |
| 5925 | 5925 | toastr.error(t`Past chat could not be updated: ${file_name}`); |
| 5926 | 5926 | console.error(error); |
| 5927 | 5927 | } |
| 5928 | 5928 | } |
| @@ -5971,7 +5971,7 @@ export async function saveChat(chatName, withMetadata, mesId) { | ||
| 5971 | 5971 | characters[this_chid]['date_last_chat'] = Date.now(); |
| 5972 | 5972 | chat.forEach(function (item, i) { |
| 5973 | 5973 | if (item['is_group']) { |
| 5974 | 5974 | toastr.error('t`Trying to save group chat with regular saveChat function. Aborting to prevent corruption.'`); |
| 5975 | 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 | 6016 | contentType: 'application/json', |
| 6017 | 6017 | success: function (data) { }, |
| 6018 | 6018 | error: function (jqXHR, exception) { |
| 6019 | 6019 | toastr.error('t`Check the server connection and reload the page to prevent data loss.'`, 't`Chat could not be saved'`); |
| 6020 | 6020 | console.log(exception); |
| 6021 | 6021 | console.log(jqXHR); |
| 6022 | 6022 | }, |
| @@ -6413,7 +6413,7 @@ export async function getSettings() { | ||
| 6413 | 6413 | |
| 6414 | 6414 | if (!response.ok) { |
| 6415 | 6415 | reloadLoop(); |
| 6416 | 6416 | toastr.error('t`Settings could not be loaded after multiple attempts. Please try again later.'`); |
| 6417 | 6417 | throw new Error('Error getting settings'); |
| 6418 | 6418 | } |
| 6419 | 6419 | |
| @@ -6640,7 +6640,7 @@ export async function saveSettings(type) { | ||
| 6640 | 6640 | eventSource.emit(event_types.SETTINGS_UPDATED); |
| 6641 | 6641 | }, |
| 6642 | 6642 | error: function (jqXHR, exception) { |
| 6643 | 6643 | toastr.error('t`Check the server connection and reload the page to prevent data loss.'t`, 't`Settings could not be saved'`); |
| 6644 | 6644 | console.log(exception); |
| 6645 | 6645 | console.log(jqXHR); |
| 6646 | 6646 | }, |
| @@ -6906,7 +6906,7 @@ export async function displayPastChats() { | ||
| 6906 | 6906 | const data = await (selected_group ? getGroupPastChats(selected_group) : getPastCharacterChats()); |
| 6907 | 6907 | |
| 6908 | 6908 | if (!data) { |
| 6909 | 6909 | toastr.error('t`Could not load chat data. Try reloading the page.'`); |
| 6910 | 6910 | return; |
| 6911 | 6911 | } |
| 6912 | 6912 | |
| @@ -7039,7 +7039,7 @@ export function selectRightMenuWithAnimation(selectedMenuId) { | ||
| 7039 | 7039 | |
| 7040 | 7040 | export function select_rm_info(type, charId, previousCharId = null) { |
| 7041 | 7041 | if (!type) { |
| 7042 | 7042 | toastr.error('t`Invalid process (no \'type\')'`); |
| 7043 | 7043 | return; |
| 7044 | 7044 | } |
| 7045 | 7045 | if (type !== 'group_create') { |
| @@ -7047,20 +7047,20 @@ export function select_rm_info(type, charId, previousCharId = null) { | ||
| 7047 | 7047 | } |
| 7048 | 7048 | |
| 7049 | 7049 | if (type === 'char_delete') { |
| 7050 | 7050 | toastr.warning(t`Character Deleted: ${displayName}`); |
| 7051 | 7051 | } |
| 7052 | 7052 | if (type === 'char_create') { |
| 7053 | 7053 | toastr.success(t`Character Created: ${displayName}`); |
| 7054 | 7054 | } |
| 7055 | 7055 | if (type === 'group_create') { |
| 7056 | 7056 | toastr.success('t`Group Created'`); |
| 7057 | 7057 | } |
| 7058 | 7058 | if (type === 'group_delete') { |
| 7059 | 7059 | toastr.warning('t`Group Deleted'`); |
| 7060 | 7060 | } |
| 7061 | 7061 | |
| 7062 | 7062 | if (type === 'char_import') { |
| 7063 | 7063 | toastr.success(t`Character Imported: ${displayName}`); |
| 7064 | 7064 | } |
| 7065 | 7065 | |
| 7066 | 7066 | selectRightMenuWithAnimation('rm_characters_block'); |
| @@ -7515,25 +7515,25 @@ export function hideSwipeButtons() { | ||
| 7515 | 7515 | */ |
| 7516 | 7516 | export async function deleteSwipe(swipeId = null) { |
| 7517 | 7517 | if (swipeId && (isNaN(swipeId) || swipeId < 0)) { |
| 7518 | 7518 | toastr.warning(t`Invalid swipe ID: ${swipeId + 1}`); |
| 7519 | 7519 | return; |
| 7520 | 7520 | } |
| 7521 | 7521 | |
| 7522 | 7522 | const lastMessage = chat[chat.length - 1]; |
| 7523 | 7523 | if (!lastMessage || !Array.isArray(lastMessage.swipes) || !lastMessage.swipes.length) { |
| 7524 | 7524 | toastr.warning('t`No messages to delete swipes from.'`); |
| 7525 | 7525 | return; |
| 7526 | 7526 | } |
| 7527 | 7527 | |
| 7528 | 7528 | if (lastMessage.swipes.length <= 1) { |
| 7529 | 7529 | toastr.warning('t`Can\'t delete the last swipe.'`); |
| 7530 | 7530 | return; |
| 7531 | 7531 | } |
| 7532 | 7532 | |
| 7533 | 7533 | swipeId = swipeId ?? lastMessage.swipe_id; |
| 7534 | 7534 | |
| 7535 | 7535 | if (swipeId < 0 || swipeId >= lastMessage.swipes.length) { |
| 7536 | 7536 | toastr.warning(t`Invalid swipe ID: ${swipeId + 1}`); |
| 7537 | 7537 | return; |
| 7538 | 7538 | } |
| 7539 | 7539 | |
| @@ -7672,7 +7672,7 @@ export function setGenerationProgress(progress) { | ||
| 7672 | 7672 | |
| 7673 | 7673 | function isHordeGenerationNotAllowed() { |
| 7674 | 7674 | if (main_api == 'koboldhorde' && preset_settings == 'gui') { |
| 7675 | 7675 | toastr.error('t`GUI Settings preset is not supported for Horde. Please select another preset.'`); |
| 7676 | 7676 | return true; |
| 7677 | 7677 | } |
| 7678 | 7678 | |
| @@ -7721,7 +7721,7 @@ function openCharacterWorldPopup() { | ||
| 7721 | 7721 | } |
| 7722 | 7722 | |
| 7723 | 7723 | $('#character_json_data').val(JSON.stringify(data)); |
| 7724 | 7724 | toastr.info('t`Embedded lorebook will be removed from this character.'`); |
| 7725 | 7725 | } catch { |
| 7726 | 7726 | console.error('Failed to parse character JSON data.'); |
| 7727 | 7727 | } |
| @@ -7912,11 +7912,11 @@ async function createOrEditCharacter(e) { | ||
| 7912 | 7912 | |
| 7913 | 7913 | if ($('#form_create').attr('actiontype') == 'createcharacter') { |
| 7914 | 7914 | if (String($('#character_name_pole').val()).length === 0) { |
| 7915 | 7915 | toastr.error('t`Name is required'`); |
| 7916 | 7916 | return; |
| 7917 | 7917 | } |
| 7918 | 7918 | if (is_group_generating || is_send_press) { |
| 7919 | 7919 | toastr.error('t`Cannot create characters while generating. Stop the request and try again.'`, 't`Creation aborted'`); |
| 7920 | 7920 | return; |
| 7921 | 7921 | } |
| 7922 | 7922 | try { |
| @@ -8000,7 +8000,7 @@ async function createOrEditCharacter(e) { | ||
| 8000 | 8000 | |
| 8001 | 8001 | } catch (error) { |
| 8002 | 8002 | console.error('Error creating character', error); |
| 8003 | 8003 | toastr.error('t`Failed to create character'`); |
| 8004 | 8004 | } |
| 8005 | 8005 | } else { |
| 8006 | 8006 | try { |
| @@ -8059,7 +8059,7 @@ async function createOrEditCharacter(e) { | ||
| 8059 | 8059 | } |
| 8060 | 8060 | } catch (error) { |
| 8061 | 8061 | console.log(error); |
| 8062 | 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 | 8551 | const result = fuse.search(name); |
| 8552 | 8552 | |
| 8553 | 8553 | if (result.length === 0) { |
| 8554 | 8554 | !quiet && toastr.warning(t`Context template "'${name}"' not found`); |
| 8555 | 8555 | return ''; |
| 8556 | 8556 | } |
| 8557 | 8557 | |
| @@ -8571,7 +8571,7 @@ async function selectInstructCallback(args, name) { | ||
| 8571 | 8571 | const result = fuse.search(name); |
| 8572 | 8572 | |
| 8573 | 8573 | if (result.length === 0) { |
| 8574 | 8574 | !quiet && toastr.warning(t`Instruct template "'${name}"' not found`); |
| 8575 | 8575 | return ''; |
| 8576 | 8576 | } |
| 8577 | 8577 | |
| @@ -8623,7 +8623,7 @@ async function connectAPISlash(args, text) { | ||
| 8623 | 8623 | |
| 8624 | 8624 | const apiConfig = CONNECT_API_MAP[text.toLowerCase()]; |
| 8625 | 8625 | if (!apiConfig) { |
| 8626 | 8626 | toastr.error(t`Error: ${text} is not a valid API`); |
| 8627 | 8627 | return ''; |
| 8628 | 8628 | } |
| 8629 | 8629 | |
| @@ -8652,7 +8652,7 @@ async function connectAPISlash(args, text) { | ||
| 8652 | 8652 | } |
| 8653 | 8653 | |
| 8654 | 8654 | const quiet = isTrueBoolean(args?.quiet); |
| 8655 | 8655 | const toast = quiet ? jQuery() : toastr.info(t`API set to ${text}, trying to connect..`); |
| 8656 | 8656 | |
| 8657 | 8657 | try { |
| 8658 | 8658 | await waitUntilCondition(() => online_status !== 'no_connection', 10000, 100); |
| @@ -8691,7 +8691,7 @@ export async function processDroppedFiles(files, data = new Map()) { | ||
| 8691 | 8691 | const preservedName = data instanceof Map && data.get(file); |
| 8692 | 8692 | await importCharacter(file, preservedName); |
| 8693 | 8693 | } else { |
| 8694 | 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 | */ |
| 8705 | 8705 | async function importCharacter(file, preserveFileName = '') { |
| 8706 | 8706 | if (is_group_generating || is_send_press) { |
| 8707 | 8707 | toastr.error('t`Cannot import characters while generating. Stop the request and try again.'`, 't`Import aborted'`); |
| 8708 | 8708 | throw new Error('Cannot import character while generating'); |
| 8709 | 8709 | } |
| 8710 | 8710 | |
| @@ -8731,7 +8731,7 @@ async function importCharacter(file, preserveFileName = '') { | ||
| 8731 | 8731 | }); |
| 8732 | 8732 | |
| 8733 | 8733 | if (data.error) { |
| 8734 | 8734 | toastr.error('t`The file is likely invalid or corrupted.'`, 't`Could not import character'`); |
| 8735 | 8735 | return; |
| 8736 | 8736 | } |
| 8737 | 8737 | |
| @@ -8784,7 +8784,7 @@ async function doImpersonate(args, prompt) { | ||
| 8784 | 8784 | await waitUntilCondition(() => !is_send_press && !is_group_generating, 10000, 100); |
| 8785 | 8785 | } catch { |
| 8786 | 8786 | console.warn('Timeout waiting for generation unlock'); |
| 8787 | 8787 | toastr.warning('t`Cannot run /impersonate command while the reply is being generated.'`); |
| 8788 | 8788 | return ''; |
| 8789 | 8789 | } |
| 8790 | 8790 | |
| @@ -8846,19 +8846,19 @@ async function doDeleteChat() { | ||
| 8846 | 8846 | |
| 8847 | 8847 | async function doRenameChat(_, chatName) { |
| 8848 | 8848 | if (!chatName) { |
| 8849 | 8849 | toastr.warning('t`Name must be provided as an argument to rename this chat.'`); |
| 8850 | 8850 | return ''; |
| 8851 | 8851 | } |
| 8852 | 8852 | |
| 8853 | 8853 | const currentChatName = getCurrentChatId(); |
| 8854 | 8854 | if (!currentChatName) { |
| 8855 | 8855 | toastr.warning('t`No chat selected that can be renamed.'`); |
| 8856 | 8856 | return ''; |
| 8857 | 8857 | } |
| 8858 | 8858 | |
| 8859 | 8859 | await renameChat(currentChatName, chatName); |
| 8860 | 8860 | |
| 8861 | 8861 | toastr.success(t`Successfully renamed chat to: ${chatName}`); |
| 8862 | 8862 | return ''; |
| 8863 | 8863 | } |
| 8864 | 8864 | |
| @@ -8973,7 +8973,7 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {}) | ||
| 8973 | 8973 | for (const key of characterKey) { |
| 8974 | 8974 | const character = characters.find(x => x.avatar == key); |
| 8975 | 8975 | if (!character) { |
| 8976 | 8976 | toastr.warning(t`Character ${key} not found. Skipping deletion.`); |
| 8977 | 8977 | continue; |
| 8978 | 8978 | } |
| 8979 | 8979 | |
| @@ -8990,7 +8990,7 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {}) | ||
| 8990 | 8990 | }); |
| 8991 | 8991 | |
| 8992 | 8992 | if (!response.ok) { |
| 8993 | 8993 | toastr.error(`${response.status} ${response.statusText}`, 't`Failed to delete character'`); |
| 8994 | 8994 | continue; |
| 8995 | 8995 | } |
| 8996 | 8996 | |
| @@ -9665,12 +9665,7 @@ jQuery(async function () { | ||
| 9665 | 9665 | |
| 9666 | 9666 | let deleteChats = false; |
| 9667 | 9667 | |
| 9668 | 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 | 9669 | onClose: () => deleteChats = !!$('#del_char_checkbox').prop('checked'), |
| 9675 | 9670 | }); |
| 9676 | 9671 | if (!confirm) { |
| @@ -8,6 +8,7 @@ import { debounce, waitUntilCondition, escapeHtml } from './utils.js'; | ||
| 8 | 8 | import { debounce_timeout } from './constants.js'; |
| 9 | 9 | import { renderTemplateAsync } from './templates.js'; |
| 10 | 10 | import { Popup } from './popup.js'; |
| 11 | +import { t } from './i18n.js'; | |
| 11 | 12 | |
| 12 | 13 | function debouncePromise(func, delay) { |
| 13 | 14 | let timeoutId; |
| @@ -455,7 +456,7 @@ class PromptManager { | ||
| 455 | 456 | |
| 456 | 457 | // Delete selected prompt from list form and close edit form |
| 457 | 458 | this.handleDeletePrompt = async (event) => { |
| 458 | 459 | Popup.show.confirm('t`Are you sure you want to delete this prompt?'`, null).then((userChoice) => { |
| 459 | 460 | if (!userChoice) return; |
| 460 | 461 | const promptID = document.getElementById(this.configuration.prefix + 'prompt_manager_footer_append_prompt').value; |
| 461 | 462 | const prompt = this.getPromptById(promptID); |
| @@ -531,7 +532,7 @@ class PromptManager { | ||
| 531 | 532 | |
| 532 | 533 | // Import prompts for the selected character |
| 533 | 534 | this.handleImport = () => { |
| 534 | 535 | Popup.show.confirm('t`Existing prompts with the same ID will be overridden. Do you want to proceed?'`, null) |
| 535 | 536 | .then(userChoice => { |
| 536 | 537 | if (!userChoice) return; |
| 537 | 538 | |
| @@ -552,7 +553,7 @@ class PromptManager { | ||
| 552 | 553 | const data = JSON.parse(fileContent); |
| 553 | 554 | this.import(data); |
| 554 | 555 | } catch (err) { |
| 555 | 556 | toastr.error('t`An error occurred while importing prompts. More info available in console.'`); |
| 556 | 557 | console.log('An error occurred while importing prompts'); |
| 557 | 558 | console.log(err.toString()); |
| 558 | 559 | } |
| @@ -567,7 +568,7 @@ class PromptManager { | ||
| 567 | 568 | |
| 568 | 569 | // Restore default state of a characters prompt order |
| 569 | 570 | this.handleCharacterReset = () => { |
| 570 | 571 | Popup.show.confirm('t`This will reset the prompt order for this character. You will not lose any prompts.'`, null) |
| 571 | 572 | .then(userChoice => { |
| 572 | 573 | if (!userChoice) return; |
| 573 | 574 | |
| @@ -1649,7 +1650,7 @@ class PromptManager { | ||
| 1649 | 1650 | }; |
| 1650 | 1651 | |
| 1651 | 1652 | if (false === this.validateObject(controlObj, importData)) { |
| 1652 | 1653 | toastr.warning('t`Could not import prompts. Export failed validation.'`); |
| 1653 | 1654 | return; |
| 1654 | 1655 | } |
| 1655 | 1656 | |
| @@ -1672,7 +1673,7 @@ class PromptManager { | ||
| 1672 | 1673 | throw new Error('Prompt order strategy not supported.'); |
| 1673 | 1674 | } |
| 1674 | 1675 | |
| 1675 | 1676 | toastr.success('t`Prompt import complete.'`); |
| 1676 | 1677 | this.saveServiceSettings().then(() => this.render()); |
| 1677 | 1678 | } |
| 1678 | 1679 | |
| @@ -16,6 +16,7 @@ import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; | ||
| 16 | 16 | import { SlashCommand } from './slash-commands/SlashCommand.js'; |
| 17 | 17 | import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js'; |
| 18 | 18 | export { MODULE_NAME as NOTE_MODULE_NAME }; |
| 19 | +import { t } from './i18n.js'; | |
| 19 | 20 | |
| 20 | 21 | const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory |
| 21 | 22 | |
| @@ -37,7 +38,7 @@ const chara_note_position = { | ||
| 37 | 38 | |
| 38 | 39 | function setNoteTextCommand(_, text) { |
| 39 | 40 | $('#extension_floating_prompt').val(text).trigger('input'); |
| 40 | 41 | toastr.success('t`Author\'s Note text updated'`); |
| 41 | 42 | return ''; |
| 42 | 43 | } |
| 43 | 44 | |
| @@ -45,12 +46,12 @@ function setNoteDepthCommand(_, text) { | ||
| 45 | 46 | const value = Number(text); |
| 46 | 47 | |
| 47 | 48 | if (Number.isNaN(value)) { |
| 48 | 49 | toastr.error('t`Not a valid number'`); |
| 49 | 50 | return; |
| 50 | 51 | } |
| 51 | 52 | |
| 52 | 53 | $('#extension_floating_depth').val(Math.abs(value)).trigger('input'); |
| 53 | 54 | toastr.success('t`Author\'s Note depth updated'`); |
| 54 | 55 | return ''; |
| 55 | 56 | } |
| 56 | 57 | |
| @@ -58,12 +59,12 @@ function setNoteIntervalCommand(_, text) { | ||
| 58 | 59 | const value = Number(text); |
| 59 | 60 | |
| 60 | 61 | if (Number.isNaN(value)) { |
| 61 | 62 | toastr.error('t`Not a valid number'`); |
| 62 | 63 | return; |
| 63 | 64 | } |
| 64 | 65 | |
| 65 | 66 | $('#extension_floating_interval').val(Math.abs(value)).trigger('input'); |
| 66 | 67 | toastr.success('t`Author\'s Note frequency updated'`); |
| 67 | 68 | return ''; |
| 68 | 69 | } |
| 69 | 70 | |
| @@ -76,12 +77,12 @@ function setNotePositionCommand(_, text) { | ||
| 76 | 77 | const position = validPositions[text?.trim()]; |
| 77 | 78 | |
| 78 | 79 | if (Number.isNaN(position)) { |
| 79 | 80 | toastr.error('t`Not a valid position'`); |
| 80 | 81 | return; |
| 81 | 82 | } |
| 82 | 83 | |
| 83 | 84 | $(`input[name="extension_floating_position"][value="${position}"]`).prop('checked', true).trigger('input'); |
| 84 | 85 | toastr.info('t`Author\'s Note position updated'`); |
| 85 | 86 | return ''; |
| 86 | 87 | } |
| 87 | 88 | |
| @@ -206,7 +207,7 @@ function onExtensionFloatingCharaPromptInput() { | ||
| 206 | 207 | extension_settings.note.chara.push(tempCharaNote); |
| 207 | 208 | } else { |
| 208 | 209 | console.log('Character author\'s note error: No avatar name key could be found.'); |
| 209 | 210 | toastr.error('t`Something went wrong. Could not save character\'s author\'s note.'`); |
| 210 | 211 | |
| 211 | 212 | // Don't save settings if something went wrong |
| 212 | 213 | return; |
| @@ -397,7 +398,7 @@ function onANMenuItemClick() { | ||
| 397 | 398 | //because this listener takes priority |
| 398 | 399 | $('#options').stop().fadeOut(animation_duration); |
| 399 | 400 | } else { |
| 400 | 401 | toastr.warning('t`Select a character before trying to use Author\'s Note'`, '', { timeOut: 2000 }); |
| 401 | 402 | } |
| 402 | 403 | } |
| 403 | 404 | |
| @@ -40,6 +40,7 @@ import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js'; | ||
| 40 | 40 | import { ScraperManager } from './scrapers.js'; |
| 41 | 41 | import { DragAndDropHandler } from './dragdrop.js'; |
| 42 | 42 | import { renderTemplateAsync } from './templates.js'; |
| 43 | +import { t } from './i18n.js'; | |
| 43 | 44 | |
| 44 | 45 | /** |
| 45 | 46 | * @typedef {Object} FileAttachment |
| @@ -206,7 +207,7 @@ export async function populateFileAttachment(message, inputId = 'file_form_input | ||
| 206 | 207 | const fileText = await converter(file); |
| 207 | 208 | base64Data = window.btoa(unescape(encodeURIComponent(fileText))); |
| 208 | 209 | } catch (error) { |
| 209 | 210 | toastr.error(String(error), 't`Could not convert file'`); |
| 210 | 211 | console.error('Could not convert file', error); |
| 211 | 212 | } |
| 212 | 213 | } |
| @@ -257,7 +258,7 @@ export async function uploadFileAttachment(fileName, base64Data) { | ||
| 257 | 258 | const responseData = await result.json(); |
| 258 | 259 | return responseData.path; |
| 259 | 260 | } catch (error) { |
| 260 | 261 | toastr.error(String(error), 't`Could not upload file'`); |
| 261 | 262 | console.error('Could not upload file', error); |
| 262 | 263 | } |
| 263 | 264 | } |
| @@ -283,7 +284,7 @@ export async function getFileAttachment(url) { | ||
| 283 | 284 | const text = await result.text(); |
| 284 | 285 | return text; |
| 285 | 286 | } catch (error) { |
| 286 | 287 | toastr.error(error, 't`Could not download file'`); |
| 287 | 288 | console.error('Could not download file', error); |
| 288 | 289 | } |
| 289 | 290 | } |
| @@ -299,13 +300,13 @@ async function validateFile(file) { | ||
| 299 | 300 | const isBinary = /^[\x00-\x08\x0E-\x1F\x7F-\xFF]*$/.test(fileText); |
| 300 | 301 | |
| 301 | 302 | if (!isImage && file.size > fileSizeLimit) { |
| 302 | 303 | toastr.error(t`File is too big. Maximum size is ${humanFileSize(fileSizeLimit)}.`); |
| 303 | 304 | return false; |
| 304 | 305 | } |
| 305 | 306 | |
| 306 | 307 | // If file is binary |
| 307 | 308 | if (isBinary && !isImage && !isConvertible(file.type)) { |
| 308 | 309 | toastr.error('t`Binary files are not supported. Select a text file or image.'`); |
| 309 | 310 | return false; |
| 310 | 311 | } |
| 311 | 312 | |
| @@ -521,7 +522,7 @@ async function openExternalMediaOverridesDialog() { | ||
| 521 | 522 | const entityId = getCurrentEntityId(); |
| 522 | 523 | |
| 523 | 524 | if (!entityId) { |
| 524 | 525 | toastr.info('t`No character or group selected'`); |
| 525 | 526 | return; |
| 526 | 527 | } |
| 527 | 528 | |
| @@ -646,7 +647,7 @@ async function deleteFileFromServer(url, silent = false) { | ||
| 646 | 647 | await eventSource.emit(event_types.FILE_ATTACHMENT_DELETED, url); |
| 647 | 648 | return true; |
| 648 | 649 | } catch (error) { |
| 649 | 650 | toastr.error(String(error), 't`Could not delete file'`); |
| 650 | 651 | console.error('Could not delete file', error); |
| 651 | 652 | return false; |
| 652 | 653 | } |
| @@ -1054,7 +1055,7 @@ async function openAttachmentManager() { | ||
| 1054 | 1055 | const selectedAttachments = document.querySelectorAll('.attachmentListItemCheckboxContainer .attachmentListItemCheckbox:checked'); |
| 1055 | 1056 | |
| 1056 | 1057 | if (selectedAttachments.length === 0) { |
| 1057 | 1058 | toastr.info('t`No attachments selected.'`, 't`Data Bank'`); |
| 1058 | 1059 | return; |
| 1059 | 1060 | } |
| 1060 | 1061 | |
| @@ -1168,7 +1169,7 @@ async function runScraper(scraperId, target, callback) { | ||
| 1168 | 1169 | |
| 1169 | 1170 | if (files.length === 0) { |
| 1170 | 1171 | console.warn('Scraping returned no files'); |
| 1171 | 1172 | toastr.info('t`No files were scraped.'`, 't`Data Bank'`); |
| 1172 | 1173 | return; |
| 1173 | 1174 | } |
| 1174 | 1175 | |
| @@ -1176,12 +1177,12 @@ async function runScraper(scraperId, target, callback) { | ||
| 1176 | 1177 | await uploadFileAttachmentToServer(file, target); |
| 1177 | 1178 | } |
| 1178 | 1179 | |
| 1179 | 1180 | toastr.success(t`Scraped ${files.length} files from ${scraperId} to ${target}.`, 't`Data Bank'`); |
| 1180 | 1181 | callback(); |
| 1181 | 1182 | } |
| 1182 | 1183 | catch (error) { |
| 1183 | 1184 | console.error('Scraping failed', error); |
| 1184 | 1185 | toastr.error('t`Check browser console for details.'`, 't`Scraping failed'`); |
| 1185 | 1186 | } |
| 1186 | 1187 | } |
| 1187 | 1188 | |
| @@ -1208,7 +1209,7 @@ export async function uploadFileAttachmentToServer(file, target) { | ||
| 1208 | 1209 | const fileText = await converter(file); |
| 1209 | 1210 | base64Data = window.btoa(unescape(encodeURIComponent(fileText))); |
| 1210 | 1211 | } catch (error) { |
| 1211 | 1212 | toastr.error(String(error), 't`Could not convert file'`); |
| 1212 | 1213 | console.error('Could not convert file', error); |
| 1213 | 1214 | } |
| 1214 | 1215 | } else { |
| @@ -132,7 +132,7 @@ | ||
| 132 | 132 | <small data-i18n="ext_regex_other_options" data-i18n="Ephemerality">Ephemerality</small> |
| 133 | 133 | <span class="fa-solid fa-circle-question note-link-span" title="By default, regex scripts alter the chat file directly and irreversibly. Enabling either (or both) of the options below will prevent chat file alteration, while still altering the specified item(s)."></span> |
| 134 | 134 | </span> |
| 135 | 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 | 136 | <input type="checkbox" name="only_format_display" /> |
| 137 | 137 | <span data-i18n="Only Format Display">Alter Chat Display</span> |
| 138 | 138 | </label> |
| @@ -189,8 +189,8 @@ async function validateGroup(group) { | ||
| 189 | 189 | group.members = group.members.filter(member => { |
| 190 | 190 | const character = characters.find(x => x.avatar === member || x.name === member); |
| 191 | 191 | if (!character) { |
| 192 | 192 | const msg = t`Warning: Listed member ${member} does not exist as a character. It will be removed from the group.`; |
| 193 | 193 | toastr.warning(msg, 't`Group Validation'`); |
| 194 | 194 | console.warn(msg); |
| 195 | 195 | dirty = true; |
| 196 | 196 | } |
| @@ -522,7 +522,7 @@ async function saveGroupChat(groupId, shouldSaveGroup) { | ||
| 522 | 522 | }); |
| 523 | 523 | |
| 524 | 524 | if (!response.ok) { |
| 525 | 525 | toastr.error('t`Check the server connection and reload the page to prevent data loss.'`, 't`Group Chat could not be saved'`); |
| 526 | 526 | console.error('Group chat could not be saved', response); |
| 527 | 527 | return; |
| 528 | 528 | } |
| @@ -837,7 +837,7 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) { | ||
| 837 | 837 | activatedMembers = activateSwipe(group.members); |
| 838 | 838 | |
| 839 | 839 | if (activatedMembers.length === 0) { |
| 840 | 840 | toastr.warning('t`Deleted group member swiped. To get a reply, add them back to the group.'`); |
| 841 | 841 | throw new Error('Deleted group member swiped'); |
| 842 | 842 | } |
| 843 | 843 | } |
| @@ -1368,15 +1368,15 @@ function isGroupMemberDisabled(avatarId) { | ||
| 1368 | 1368 | |
| 1369 | 1369 | async function onDeleteGroupClick() { |
| 1370 | 1370 | if (!openGroupId) { |
| 1371 | 1371 | toastr.warning('t`Currently no group selected.'`); |
| 1372 | 1372 | return; |
| 1373 | 1373 | } |
| 1374 | 1374 | if (is_group_generating) { |
| 1375 | 1375 | toastr.warning('t`Not so fast! Wait for the characters to stop typing before deleting the group.'`); |
| 1376 | 1376 | return; |
| 1377 | 1377 | } |
| 1378 | 1378 | |
| 1379 | 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 | 1380 | if (confirm) { |
| 1381 | 1381 | deleteGroup(openGroupId); |
| 1382 | 1382 | } |
| @@ -1630,7 +1630,7 @@ function updateFavButtonState(state) { | ||
| 1630 | 1630 | |
| 1631 | 1631 | export async function openGroupById(groupId) { |
| 1632 | 1632 | if (isChatSaving) { |
| 1633 | 1633 | toastr.info('t`Please wait until the chat is saved before switching characters.'`, 't`Your chat is still saving...'`); |
| 1634 | 1634 | return; |
| 1635 | 1635 | } |
| 1636 | 1636 | |
| @@ -1659,7 +1659,7 @@ export async function openGroupById(groupId) { | ||
| 1659 | 1659 | |
| 1660 | 1660 | function openCharacterDefinition(characterSelect) { |
| 1661 | 1661 | if (is_group_generating) { |
| 1662 | 1662 | toastr.warning('t`Can\'t peek a character while group reply is being generated'`); |
| 1663 | 1663 | console.warn('Can\'t peek a character def while group reply is being generated'); |
| 1664 | 1664 | return; |
| 1665 | 1665 | } |
| @@ -1908,7 +1908,7 @@ export async function saveGroupBookmarkChat(groupId, name, metadata, mesId) { | ||
| 1908 | 1908 | }); |
| 1909 | 1909 | |
| 1910 | 1910 | if (!response.ok) { |
| 1911 | 1911 | toastr.error('t`Check the server connection and reload the page to prevent data loss.'`, 't`Group chat could not be saved'`); |
| 1912 | 1912 | console.error('Group chat could not be saved', response); |
| 1913 | 1913 | } |
| 1914 | 1914 | } |
| @@ -388,7 +388,7 @@ async function validateReverseProxy() { | ||
| 388 | 388 | new URL(oai_settings.reverse_proxy); |
| 389 | 389 | } |
| 390 | 390 | catch (err) { |
| 391 | 391 | toastr.error('t`Entered reverse proxy address is not a valid URL'`); |
| 392 | 392 | setOnlineStatus('no_connection'); |
| 393 | 393 | resultCheckStatus(); |
| 394 | 394 | throw err; |
| @@ -399,7 +399,7 @@ async function validateReverseProxy() { | ||
| 399 | 399 | const confirmation = skipConfirm || await Popup.show.confirm(t`Connecting To Proxy`, await renderTemplateAsync('proxyConnectionWarning', { proxyURL: DOMPurify.sanitize(oai_settings.reverse_proxy) })); |
| 400 | 400 | |
| 401 | 401 | if (!confirmation) { |
| 402 | 402 | toastr.error('t`Update or remove your reverse proxy settings.'`); |
| 403 | 403 | setOnlineStatus('no_connection'); |
| 404 | 404 | resultCheckStatus(); |
| 405 | 405 | throw new Error('Proxy connection denied.'); |
| @@ -1225,15 +1225,15 @@ export async function prepareOpenAIMessages({ | ||
| 1225 | 1225 | await populateChatCompletion(prompts, chatCompletion, { bias, quietPrompt, quietImage, type, cyclePrompt, messages, messageExamples }); |
| 1226 | 1226 | } catch (error) { |
| 1227 | 1227 | if (error instanceof TokenBudgetExceededError) { |
| 1228 | 1228 | toastr.error('t`An error occurred while counting tokens: Token budget exceeded.'`); |
| 1229 | 1229 | chatCompletion.log('Token budget exceeded.'); |
| 1230 | 1230 | promptManager.error = 't`Not enough free tokens for mandatory prompts. Raise your token Limit or disable custom prompts.'`; |
| 1231 | 1231 | } else if (error instanceof InvalidCharacterNameError) { |
| 1232 | 1232 | toastr.warning('t`An error occurred while counting tokens: Invalid character name'`); |
| 1233 | 1233 | chatCompletion.log('Invalid character name'); |
| 1234 | 1234 | promptManager.error = 't`The name of at least one character contained whitespaces or special characters. Please check your user and character name.'`; |
| 1235 | 1235 | } else { |
| 1236 | 1236 | toastr.error('t`An unknown error occurred while counting tokens. Further information may be available in console.'`); |
| 1237 | 1237 | chatCompletion.log('----- Unexpected error while preparing prompts -----'); |
| 1238 | 1238 | chatCompletion.log(error); |
| 1239 | 1239 | chatCompletion.log(error.stack); |
| @@ -1287,11 +1287,8 @@ function tryParseStreamingError(response, decoded) { | ||
| 1287 | 1287 | } |
| 1288 | 1288 | } |
| 1289 | 1289 | |
| 1290 | 1290 | async 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>`; | |
| 1295 | 1292 | |
| 1296 | 1293 | if (!data) { |
| 1297 | 1294 | return; |
| @@ -1927,11 +1924,11 @@ async function sendOpenAIRequest(type, messages, signal) { | ||
| 1927 | 1924 | else { |
| 1928 | 1925 | const data = await response.json(); |
| 1929 | 1926 | |
| 1930 | 1927 | await checkQuotaError(data); |
| 1931 | 1928 | checkModerationError(data); |
| 1932 | 1929 | |
| 1933 | 1930 | if (data.error) { |
| 1934 | 1931 | toastr.error(data.error.message || response.statusText, 't`API returned an error'`); |
| 1935 | 1932 | throw new Error(data); |
| 1936 | 1933 | } |
| 1937 | 1934 | |
| @@ -2178,7 +2175,7 @@ function parseOpenAITextLogprobs(logprobs) { | ||
| 2178 | 2175 | |
| 2179 | 2176 | function handleWindowError(err) { |
| 2180 | 2177 | const text = parseWindowError(err); |
| 2181 | 2178 | toastr.error(text, 't`Window.ai returned an error'`); |
| 2182 | 2179 | throw err; |
| 2183 | 2180 | } |
| 2184 | 2181 | |
| @@ -3251,7 +3248,7 @@ async function getStatusOpen() { | ||
| 3251 | 3248 | } |
| 3252 | 3249 | |
| 3253 | 3250 | function showWindowExtensionError() { |
| 3254 | 3251 | toastr.error('t`Get it here:` + ' <a href="https://windowai.io/" target="_blank">windowai.io</a>', 't`Extension is not installed'`, { |
| 3255 | 3252 | escapeHtml: false, |
| 3256 | 3253 | timeOut: 0, |
| 3257 | 3254 | extendedTimeOut: 0, |
| @@ -3370,7 +3367,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) { | ||
| 3370 | 3367 | if (triggerUi) $('#settings_preset_openai').append(option).trigger('change'); |
| 3371 | 3368 | } |
| 3372 | 3369 | } else { |
| 3373 | 3370 | toastr.error('t`Failed to save preset'`); |
| 3374 | 3371 | throw new Error('Failed to save preset'); |
| 3375 | 3372 | } |
| 3376 | 3373 | } |
| @@ -3449,7 +3446,7 @@ async function createNewLogitBiasPreset() { | ||
| 3449 | 3446 | } |
| 3450 | 3447 | |
| 3451 | 3448 | if (name in oai_settings.bias_presets) { |
| 3452 | 3449 | toastr.error('t`Preset name should be unique.'`); |
| 3453 | 3450 | return; |
| 3454 | 3451 | } |
| 3455 | 3452 | |
| @@ -3493,7 +3490,7 @@ async function onPresetImportFileChange(e) { | ||
| 3493 | 3490 | try { |
| 3494 | 3491 | presetBody = JSON.parse(importedFile); |
| 3495 | 3492 | } catch (err) { |
| 3496 | 3493 | toastr.error('t`Invalid file'`); |
| 3497 | 3494 | return; |
| 3498 | 3495 | } |
| 3499 | 3496 | |
| @@ -3534,7 +3531,7 @@ async function onPresetImportFileChange(e) { | ||
| 3534 | 3531 | }); |
| 3535 | 3532 | |
| 3536 | 3533 | if (!savePresetSettings.ok) { |
| 3537 | 3534 | toastr.error('t`Failed to save preset'`); |
| 3538 | 3535 | return; |
| 3539 | 3536 | } |
| 3540 | 3537 | |
| @@ -3559,7 +3556,7 @@ async function onPresetImportFileChange(e) { | ||
| 3559 | 3556 | |
| 3560 | 3557 | async function onExportPresetClick() { |
| 3561 | 3558 | if (!oai_settings.preset_settings_openai) { |
| 3562 | 3559 | toastr.error('t`No preset selected'`); |
| 3563 | 3560 | return; |
| 3564 | 3561 | } |
| 3565 | 3562 | |
| @@ -3600,12 +3597,12 @@ async function onLogitBiasPresetImportFileChange(e) { | ||
| 3600 | 3597 | e.target.value = ''; |
| 3601 | 3598 | |
| 3602 | 3599 | if (name in oai_settings.bias_presets) { |
| 3603 | 3600 | toastr.error('t`Preset name should be unique.'`); |
| 3604 | 3601 | return; |
| 3605 | 3602 | } |
| 3606 | 3603 | |
| 3607 | 3604 | if (!Array.isArray(importedFile)) { |
| 3608 | 3605 | toastr.error('t`Invalid logit bias preset file.'`); |
| 3609 | 3606 | return; |
| 3610 | 3607 | } |
| 3611 | 3608 | |
| @@ -3664,16 +3661,16 @@ async function onDeletePresetClick() { | ||
| 3664 | 3661 | }); |
| 3665 | 3662 | |
| 3666 | 3663 | if (!response.ok) { |
| 3667 | 3664 | toastr.warning('t`Preset was not deleted from server'`); |
| 3668 | 3665 | } else { |
| 3669 | 3666 | toastr.success('t`Preset deleted'`); |
| 3670 | 3667 | } |
| 3671 | 3668 | |
| 3672 | 3669 | saveSettingsDebounced(); |
| 3673 | 3670 | } |
| 3674 | 3671 | |
| 3675 | 3672 | async function onLogitBiasPresetDeleteClick() { |
| 3676 | 3673 | const value = await callPopup('t`Delete the preset?'`, 'confirm'); |
| 3677 | 3674 | |
| 3678 | 3675 | if (!value) { |
| 3679 | 3676 | return; |
| @@ -4808,7 +4805,7 @@ function runProxyCallback(_, value) { | ||
| 4808 | 4805 | const result = fuse.search(value); |
| 4809 | 4806 | |
| 4810 | 4807 | if (result.length === 0) { |
| 4811 | 4808 | toastr.warning(t`Proxy preset "'${value}"' not found`); |
| 4812 | 4809 | return ''; |
| 4813 | 4810 | } |
| 4814 | 4811 | |
| @@ -4982,7 +4979,7 @@ export function initOpenAI() { | ||
| 4982 | 4979 | $('#update_oai_preset').on('click', async function () { |
| 4983 | 4980 | const name = oai_settings.preset_settings_openai; |
| 4984 | 4981 | await saveOpenAIPreset(name, oai_settings); |
| 4985 | 4982 | toastr.success('t`Preset updated'`); |
| 4986 | 4983 | }); |
| 4987 | 4984 | |
| 4988 | 4985 | $('#impersonation_prompt_restore').on('click', function () { |
| @@ -1,3 +1,3 @@ | ||
| 1 | 1 | <div> |
| 2 | 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 | 3 | </div> |
| @@ -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> | |
| 5 | \ No newline at end of file | |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <div> |
| 2 | 2 | <h3 data-i18n="duplicate_confirm_1">Are you sure you want to duplicate this character?</h3> |
| 3 | 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 | 4 | <br> |
| 5 | 5 | </div> |
| @@ -14,18 +14,18 @@ | ||
| 14 | 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 | 15 | </ul> |
| 16 | 16 | <div> |
| 17 | 17 | <strong data-i18n="help_hotkeys_20">Markdown Hotkeys</strong> |
| 18 | 18 | </div> |
| 19 | 19 | <div> |
| 20 | 20 | <small> |
| 21 | 21 | <span data-i18n="help_hotkeys_21">Works in the chatbar and textareas marked with this icon:</span> |
| 22 | 22 | <code><i class="fa-brands fa-markdown"></i></code> |
| 23 | 23 | </small> |
| 24 | 24 | </div> |
| 25 | 25 | <ul> |
| 26 | 26 | <li><kbd>Ctrl+B</kbd> = <span data-i18n="help_hotkeys_22">**bold**</span></li> |
| 27 | 27 | <li><kbd>Ctrl+I</kbd> = <span data-i18n="help_hotkeys_23">*italic*</span></li> |
| 28 | 28 | <li><kbd>Ctrl+U</kbd> = <span data-i18n="help_hotkeys_24">__underline__</span></li> |
| 29 | 29 | <li><kbd>Ctrl+K</kbd> = <span data-i18n="help_hotkeys_25">`inline code`</span></li> |
| 30 | 30 | <li><kbd>Ctrl+Shift+~</kbd> = <span data-i18n="help_hotkeys_26">~~strikethrough~~</span></li> |
| 31 | 31 | </ul> |
| @@ -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> | |
| 4 | \ No newline at end of file | |