Merge pull request #3930 from Yokayo/staging Update ru-ru translation

bb9fe64652e78a8d6768264ccbe80334c184e533

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

Signed
17 files changed, +257 -94Ignore whitespace
public/css/rm-groups.css+2 -2
@@ -87,7 +87,7 @@
87}87}
8888
89#rm_group_members:empty::before {89#rm_group_members:empty::before {
90 content: 'Group is empty';90 content: attr(group_empty_text);
9191
92 font-weight: bolder;92 font-weight: bolder;
93 width: 100%;93 width: 100%;
@@ -115,7 +115,7 @@
115}115}
116116
117#rm_group_add_members:empty::before {117#rm_group_add_members:empty::before {
118 content: 'No characters available';118 content: attr(no_characters_text);
119119
120 font-weight: bolder;120 font-weight: bolder;
121 width: 100%;121 width: 100%;
public/index.html+9 -9
@@ -2052,8 +2052,8 @@
2052 <div class="flex-container oneline-dropdown" title="Constrains effort on reasoning for reasoning models.&#10;Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response." data-i18n="[title]Constrains effort on reasoning for reasoning models.">2052 <div class="flex-container oneline-dropdown" title="Constrains effort on reasoning for reasoning models.&#10;Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response." data-i18n="[title]Constrains effort on reasoning for reasoning models.">
2053 <label for="openai_reasoning_effort">2053 <label for="openai_reasoning_effort">
2054 <span data-i18n="Reasoning Effort">Reasoning Effort</span>2054 <span data-i18n="Reasoning Effort">Reasoning Effort</span>
2055 <i data-source="openai,custom,xai,openrouter" class="opacity50p fa-solid fa-circle-info" title="OpenAI-style options: low, medium, high. Minimum and maximum are aliased to low and high. Auto does not send an effort level."></i>2055 <i data-source="openai,custom,xai,openrouter" class="opacity50p fa-solid fa-circle-info" title="OpenAI-style options: low, medium, high. Minimum and maximum are aliased to low and high. Auto does not send an effort level." data-i18n="[title]OpenAI-style options: low, medium, high. Minimum and maximum are aliased to low and high. Auto does not send an effort level."></i>
2056 <i data-source="claude,makersuite" class="opacity50p fa-solid fa-circle-info" title="Allocates a portion of the response length for thinking (low: 10%, medium: 25%, high: 50%). Other options are model-dependent."></i>2056 <i data-source="claude,makersuite" class="opacity50p fa-solid fa-circle-info" title="Allocates a portion of the response length for thinking (low: 10%, medium: 25%, high: 50%). Other options are model-dependent." data-i18n="[title]Allocates a portion of the response length for thinking (low: 10%, medium: 25%, high: 50%). Other options are model-dependent."></i>
2057 </label>2057 </label>
2058 <select id="openai_reasoning_effort">2058 <select id="openai_reasoning_effort">
2059 <option data-i18n="openai_reasoning_effort_auto" value="auto">Auto</option>2059 <option data-i18n="openai_reasoning_effort_auto" value="auto">Auto</option>
@@ -2498,8 +2498,8 @@
2498 <option value="search" data-i18n="Search" hidden>A-Z</option>2498 <option value="search" data-i18n="Search" hidden>A-Z</option>
2499 <option value="asc">A-Z</option>2499 <option value="asc">A-Z</option>
2500 <option value="desc">Z-A</option>2500 <option value="desc">Z-A</option>
2501 <option value="date_asc">Date Asc</option>2501 <option data-i18n="Date Asc" value="date_asc">Date Asc</option>
2502 <option value="date_desc">Date Desc</option>2502 <option data-i18n="Date Desc" value="date_desc">Date Desc</option>
2503 </select>2503 </select>
2504 <select id="featherless_category_selection" class="text_pole">2504 <select id="featherless_category_selection" class="text_pole">
2505 <option value="" disabled selected data-i18n="category">category</option>2505 <option value="" disabled selected data-i18n="category">category</option>
@@ -2509,7 +2509,7 @@
2509 <option value="All" data-i18n="All">All</option>2509 <option value="All" data-i18n="All">All</option>
2510 </select>2510 </select>
2511 <select id="featherless_class_selection" class="text_pole">2511 <select id="featherless_class_selection" class="text_pole">
2512 <option value="" selected data-i18n="class">All Classes</option>2512 <option value="" selected data-i18n="All Classes">All Classes</option>
2513 </select>2513 </select>
2514 <div id="featherless_model_pagination_container" class="flex1"></div>2514 <div id="featherless_model_pagination_container" class="flex1"></div>
2515 <i id="featherless_model_grid_toggle" class="fa-solid fa-table-cells-large menu_button" data-i18n="[title]Toggle grid view" title="Toggle grid view"></i>2515 <i id="featherless_model_grid_toggle" class="fa-solid fa-table-cells-large menu_button" data-i18n="[title]Toggle grid view" title="Toggle grid view"></i>
@@ -5531,7 +5531,7 @@
5531 <div class="inline-drawer-content">5531 <div class="inline-drawer-content">
5532 <div id="currentGroupMembers" name="Current Group Members" class="flex-container flexFlowColumn overflowYAuto flex1">5532 <div id="currentGroupMembers" name="Current Group Members" class="flex-container flexFlowColumn overflowYAuto flex1">
5533 <div id="rm_group_members_pagination" class="rm_group_members_pagination group_pagination"></div>5533 <div id="rm_group_members_pagination" class="rm_group_members_pagination group_pagination"></div>
5534 <div id="rm_group_members" class="rm_group_members overflowYAuto flex-container"></div>5534 <div id="rm_group_members" class="rm_group_members overflowYAuto flex-container" group_empty_text="Group is empty." data-i18n="[group_empty_text]Group is empty."></div>
5535 </div>5535 </div>
5536 </div>5536 </div>
5537 </div>5537 </div>
@@ -5549,7 +5549,7 @@
5549 <div class="tags rm_tag_filter"></div>5549 <div class="tags rm_tag_filter"></div>
5550 </div>5550 </div>
5551 <div id="rm_group_add_members_pagination" class="group_pagination"></div>5551 <div id="rm_group_add_members_pagination" class="group_pagination"></div>
5552 <div id="rm_group_add_members" class="overflowYAuto flex-container"></div>5552 <div id="rm_group_add_members" class="overflowYAuto flex-container" no_characters_text="No characters available" data-i18n="[no_characters_text]No characters available"></div>
5553 </div>5553 </div>
5554 </div>5554 </div>
5555 </div>5555 </div>
@@ -5969,7 +5969,7 @@
5969 <div class="tag_view_color_picker" data-value="color"></div>5969 <div class="tag_view_color_picker" data-value="color"></div>
5970 <div class="tag_view_color_picker" data-value="color2"></div>5970 <div class="tag_view_color_picker" data-value="color2"></div>
5971 <div class="tag_view_name" contenteditable="true"></div>5971 <div class="tag_view_name" contenteditable="true"></div>
5972 <div class="tag_view_counter"><span class="tag_view_counter_value"></span>&nbsp;entries</div>5972 <div class="tag_view_counter"><span class="tag_view_counter_value"></span>&nbsp;<span data-i18n="tag_entries">entries</span></div>
5973 <div title="Delete tag" class="tag_delete fa-solid fa-trash-can right_menu_button" data-i18n="[title]Delete tag"></div>5973 <div title="Delete tag" class="tag_delete fa-solid fa-trash-can right_menu_button" data-i18n="[title]Delete tag"></div>
5974 </div>5974 </div>
5975 </div>5975 </div>
@@ -6704,7 +6704,7 @@
6704 </div>6704 </div>
6705 <div class="flex-container wide100pLess70px character_select_container height100p alignitemscenter">6705 <div class="flex-container wide100pLess70px character_select_container height100p alignitemscenter">
6706 <div class="wide100p character_name_block">6706 <div class="wide100p character_name_block">
6707 <span class="ch_name">Go back</span>6707 <span class="ch_name" data-i18n="Go back">Go back</span>
6708 </div>6708 </div>
6709 </div>6709 </div>
6710 </div>6710 </div>
public/locales/ru-ru.json+105 -22
@@ -52,7 +52,7 @@
52 "Presence Penalty": "Штраф за присутствие",52 "Presence Penalty": "Штраф за присутствие",
53 "Top A": "Top А",53 "Top A": "Top А",
54 "Tail Free Sampling": "Tail Free Sampling",54 "Tail Free Sampling": "Tail Free Sampling",
55 "Rep. Pen. Slope": "Rep. Pen. Slope",55 "Rep. Pen. Slope": "Рост штрафа за повтор к концу промпта",
56 "Top K": "Top K",56 "Top K": "Top K",
57 "Top P": "Top P",57 "Top P": "Top P",
58 "Do Sample": "Включить сэмплинг",58 "Do Sample": "Включить сэмплинг",
@@ -162,9 +162,9 @@
162 "Story String": "Строка истории",162 "Story String": "Строка истории",
163 "Example Separator": "Разделитель примеров сообщений",163 "Example Separator": "Разделитель примеров сообщений",
164 "Chat Start": "Начало чата",164 "Chat Start": "Начало чата",
165 "Activation Regex": "Regex для активации",165 "Activation Regex": "Рег. выражение для активации",
166 "Instruct Mode": "Режим Instruct",166 "Instruct Mode": "Режим Instruct",
167 "Wrap Sequences with Newline": "Отделять строки символом новой строки",167 "Wrap Sequences with Newline": "Каждая строка из шаблона на новой строке",
168 "Include Names": "Добавлять имена",168 "Include Names": "Добавлять имена",
169 "Force for Groups and Personas": "Также для групп и персон",169 "Force for Groups and Personas": "Также для групп и персон",
170 "System Prompt": "Системный промпт",170 "System Prompt": "Системный промпт",
@@ -299,7 +299,7 @@
299 "AI Horde": "AI Horde",299 "AI Horde": "AI Horde",
300 "NovelAI": "NovelAI",300 "NovelAI": "NovelAI",
301 "OpenAI API key": "Ключ для API OpenAI",301 "OpenAI API key": "Ключ для API OpenAI",
302 "Trim spaces": "Обрезать пробелы",302 "Trim spaces": "Обрезать пробелы в начале и конце",
303 "Trim Incomplete Sentences": "Удалять неоконченные предложения",303 "Trim Incomplete Sentences": "Удалять неоконченные предложения",
304 "Include Newline": "Добавлять новую строку",304 "Include Newline": "Добавлять новую строку",
305 "Non-markdown strings": "Строки без разметки",305 "Non-markdown strings": "Строки без разметки",
@@ -510,7 +510,7 @@
510 "New preset": "Новый пресет",510 "New preset": "Новый пресет",
511 "Delete preset": "Удалить пресет",511 "Delete preset": "Удалить пресет",
512 "API Connections": "Соединения с API",512 "API Connections": "Соединения с API",
513 "Can help with bad responses by queueing only the approved workers. May slowdown the response time.": "Может помочь с плохими ответами ставя в очередь только подтвержденных работников. Может замедлить время ответа.",513 "Can help with bad responses by queueing only the approved workers. May slowdown the response time.": "Может помочь при плохих ответах, делая запросы только к доверенным рабочим машинам. Может замедлить время ответа.",
514 "Clear your API key": "Стереть ключ от API",514 "Clear your API key": "Стереть ключ от API",
515 "Refresh models": "Обновить модели",515 "Refresh models": "Обновить модели",
516 "Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "Получите свой OpenRouter API токен используя OAuth. У вас будет открыта вкладка openrouter.ai",516 "Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "Получите свой OpenRouter API токен используя OAuth. У вас будет открыта вкладка openrouter.ai",
@@ -551,7 +551,7 @@
551 "Token counts may be inaccurate and provided just for reference.": "Счетчик токенов может быть неточным, используйте как ориентир",551 "Token counts may be inaccurate and provided just for reference.": "Счетчик токенов может быть неточным, используйте как ориентир",
552 "Click to select a new avatar for this character": "Нажмите чтобы выбрать новый аватар для этого персонажа",552 "Click to select a new avatar for this character": "Нажмите чтобы выбрать новый аватар для этого персонажа",
553 "Example: [{{user}} is a 28-year-old Romanian cat girl.]": "Пример:\n [{{user}} is a 28-year-old Romanian cat girl.]",553 "Example: [{{user}} is a 28-year-old Romanian cat girl.]": "Пример:\n [{{user}} is a 28-year-old Romanian cat girl.]",
554 "Toggle grid view": "Переключить вид сетки",554 "Toggle grid view": "Сменить вид сетки",
555 "Add to Favorites": "Добавить в Избранное",555 "Add to Favorites": "Добавить в Избранное",
556 "Advanced Definition": "Расширенное описание",556 "Advanced Definition": "Расширенное описание",
557 "Character Lore": "Лор персонажа",557 "Character Lore": "Лор персонажа",
@@ -624,7 +624,7 @@
624 "UI Theme": "Тема UI",624 "UI Theme": "Тема UI",
625 "This message is invisible for the AI": "Это сообщение невидимо для ИИ",625 "This message is invisible for the AI": "Это сообщение невидимо для ИИ",
626 "Sampler Priority": "Приоритет сэмплеров",626 "Sampler Priority": "Приоритет сэмплеров",
627 "Ooba only. Determines the order of samplers.": "Только oobabooga. Определяет порядок сэмплеров.",627 "Ooba only. Determines the order of samplers.": "Только для oobabooga. Определяет порядок сэмплеров.",
628 "Load default order": "Загрузить стандартный порядок",628 "Load default order": "Загрузить стандартный порядок",
629 "Max Tokens Second": "Макс. кол-во токенов в секунду",629 "Max Tokens Second": "Макс. кол-во токенов в секунду",
630 "CFG": "CFG",630 "CFG": "CFG",
@@ -695,7 +695,7 @@
695 "Medium": "Средний",695 "Medium": "Средний",
696 "Aggressive": "Агрессивный",696 "Aggressive": "Агрессивный",
697 "Very aggressive": "Очень агрессивный",697 "Very aggressive": "Очень агрессивный",
698 "Eta_Cutoff_desc": "Eta cutoff - основной параметр специальной техники сэмплинга под названием Eta Sampling.&#13;В единицах 1e-4; разумное значение - 3.&#13;Установите в 0, чтобы отключить.&#13;См. статью Truncation Sampling as Language Model Desmoothing от Хьюитт и др. (2022) для получения подробной информации.",698 "Eta_Cutoff_desc": "Eta cutoff - основной параметр специальной техники сэмплинга под названием Eta Sampling.\nВ единицах 1e-4; разумное значение - 3.\nУстановите в 0, чтобы отключить.\nСм. статью Truncation Sampling as Language Model Desmoothing от Хьюитт и др. (2022) для получения подробной информации.",
699 "Learn how to contribute your idle GPU cycles to the Horde": "Узнайте, как использовать время простоя вашего GPU для помощи Horde",699 "Learn how to contribute your idle GPU cycles to the Horde": "Узнайте, как использовать время простоя вашего GPU для помощи Horde",
700 "Use the appropriate tokenizer for Google models via their API. Slower prompt processing, but offers much more accurate token counting.": "Используйте соответствующий токенизатор для моделей Google через их API. Медленная обработка подсказок, но предлагает намного более точный подсчет токенов.",700 "Use the appropriate tokenizer for Google models via their API. Slower prompt processing, but offers much more accurate token counting.": "Используйте соответствующий токенизатор для моделей Google через их API. Медленная обработка подсказок, но предлагает намного более точный подсчет токенов.",
701 "Load koboldcpp order": "Загрузить порядок из koboldcpp",701 "Load koboldcpp order": "Загрузить порядок из koboldcpp",
@@ -964,7 +964,7 @@
964 "char_import_3": "Персонаж с JanitorAI (прямая ссылка или UUID)",964 "char_import_3": "Персонаж с JanitorAI (прямая ссылка или UUID)",
965 "char_import_4": "Персонаж с Pygmalion.chat (прямая ссылка или UUID)",965 "char_import_4": "Персонаж с Pygmalion.chat (прямая ссылка или UUID)",
966 "char_import_5": "Персонаж с AICharacterCards.com (прямая ссылка или ID)",966 "char_import_5": "Персонаж с AICharacterCards.com (прямая ссылка или ID)",
967 "char_import_6": "Прямая ссылка на PNG-файл (чтобы узнать список разрешённых хостов, загляните в",967 "char_import_6": "Прямая ссылка на PNG-файл (список разрешённых хостов находится в",
968 "char_import_7": ")",968 "char_import_7": ")",
969 "Grammar String": "Грамматика",969 "Grammar String": "Грамматика",
970 "GBNF or EBNF, depends on the backend in use. If you're using this you should know which.": "GBNF или EBNF, зависит от бэкенда. Если вы это используете, то, скорее всего, сами знаете, какой именно.",970 "GBNF or EBNF, depends on the backend in use. If you're using this you should know which.": "GBNF или EBNF, зависит от бэкенда. Если вы это используете, то, скорее всего, сами знаете, какой именно.",
@@ -1016,7 +1016,7 @@
1016 "prompt_manager_relative": "Относительная",1016 "prompt_manager_relative": "Относительная",
1017 "prompt_manager_depth": "Глубина",1017 "prompt_manager_depth": "Глубина",
1018 "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Глубина вставки. 0 = после последнего сообщения, 1 = перед последним сообщением, и т.д.",1018 "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Глубина вставки. 0 = после последнего сообщения, 1 = перед последним сообщением, и т.д.",
1019 "The prompt to be sent.": "Отправляемый ИИ промпт.",1019 "The prompt to be sent.": "Текст промпта.",
1020 "prompt_manager_forbid_overrides": "Запретить перезапись",1020 "prompt_manager_forbid_overrides": "Запретить перезапись",
1021 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Карточка персонажа не сможет перезаписать этот промпт, даже если настройки отдают приоритет именно ей.",1021 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Карточка персонажа не сможет перезаписать этот промпт, даже если настройки отдают приоритет именно ей.",
1022 "image_inlining_hint_1": "Отправлять картинки как часть промпта, если позволяет модель (такой функционал поддерживают GPT-4V, Claude 3 или Llava 13B). Чтобы добавить в чат изображение, используйте на нужном сообщении действие",1022 "image_inlining_hint_1": "Отправлять картинки как часть промпта, если позволяет модель (такой функционал поддерживают GPT-4V, Claude 3 или Llava 13B). Чтобы добавить в чат изображение, используйте на нужном сообщении действие",
@@ -1232,7 +1232,7 @@
1232 "Top P & Min P": "Top P & Min P",1232 "Top P & Min P": "Top P & Min P",
1233 "llama.cpp only. Determines the order of samplers. If Mirostat mode is not 0, sampler order is ignored.": "llama.cpp only. Determines the order of samplers. If Mirostat mode is not 0, sampler order is ignored.",1233 "llama.cpp only. Determines the order of samplers. If Mirostat mode is not 0, sampler order is ignored.": "llama.cpp only. Determines the order of samplers. If Mirostat mode is not 0, sampler order is ignored.",
1234 "Helps the model to associate messages with characters.": "Помогает модели связывать сообщения с персонажами.",1234 "Helps the model to associate messages with characters.": "Помогает модели связывать сообщения с персонажами.",
1235 "character_names_default": "Except for groups and past personas. Otherwise, make sure you provide names in the prompt.",1235 "character_names_default": "Добавлять префиксы для групповых чатов и предыдущих персон. В остальных случаях указывайте имена в промпте иными способами.",
1236 "Completion": "Completion Object",1236 "Completion": "Completion Object",
1237 "character_names_completion": "Только латинские буквы, цифры и знак подчёркивания. Работает не для всех бэкендов, в частности для Claude, MistralAI, Google.",1237 "character_names_completion": "Только латинские буквы, цифры и знак подчёркивания. Работает не для всех бэкендов, в частности для Claude, MistralAI, Google.",
1238 "Use AI21 Tokenizer": "Использовать токенайзер AI21",1238 "Use AI21 Tokenizer": "Использовать токенайзер AI21",
@@ -1277,7 +1277,7 @@
1277 "Will be inserted as a last prompt line when using system/neutral generation.": "Will be inserted as a last prompt line when using system/neutral generation.",1277 "Will be inserted as a last prompt line when using system/neutral generation.": "Will be inserted as a last prompt line when using system/neutral generation.",
1278 "If a stop sequence is generated, everything past it will be removed from the output (inclusive).": "Если ИИ генерирует стоп-строку, то всё после неё будет вырезано из ответа (включая и саму стоп-строку).",1278 "If a stop sequence is generated, everything past it will be removed from the output (inclusive).": "Если ИИ генерирует стоп-строку, то всё после неё будет вырезано из ответа (включая и саму стоп-строку).",
1279 "Will be inserted at the start of the chat history if it doesn't start with a User message.": "Вставляется в начале истории чата, если она начинается не с сообщения пользователя.",1279 "Will be inserted at the start of the chat history if it doesn't start with a User message.": "Вставляется в начале истории чата, если она начинается не с сообщения пользователя.",
1280 "Global World Info/Lorebook activation settings": "Настройки активации глобального лорбука / Информации о мире",1280 "Global World Info/Lorebook activation settings": "Глобальные настройки активации лорбука / Информации о мире",
1281 "Click to expand": "Щёлкните, чтобы развернуть",1281 "Click to expand": "Щёлкните, чтобы развернуть",
1282 "Insertion Strategy": "Как инжектить",1282 "Insertion Strategy": "Как инжектить",
1283 "Only the entries with the most number of key matches will be selected for Inclusion Group filtering": "Only the entries with the most number of key matches will be selected for Inclusion Group filtering",1283 "Only the entries with the most number of key matches will be selected for Inclusion Group filtering": "Only the entries with the most number of key matches will be selected for Inclusion Group filtering",
@@ -1646,8 +1646,8 @@
1646 "mui_reset": "Сброс",1646 "mui_reset": "Сброс",
1647 "Quick 'Impersonate' button": "Быстрое перевоплощение",1647 "Quick 'Impersonate' button": "Быстрое перевоплощение",
1648 "Show a button in the input area to ask the AI to impersonate your character for a single message": "Показать в поле ввода кнопку, по нажатии на которую ИИ сгенерирует одно сообщение от лица вашего персонажа.",1648 "Show a button in the input area to ask the AI to impersonate your character for a single message": "Показать в поле ввода кнопку, по нажатии на которую ИИ сгенерирует одно сообщение от лица вашего персонажа.",
1649 "Separators as Stop Strings": "Разделители как стоп-строки",1649 "Separators as Stop Strings": "Разделители в качестве стоп-строк",
1650 "Names as Stop Strings": "Имена как стоп-строки",1650 "Names as Stop Strings": "Имена в качестве стоп-строк",
1651 "Add Character and User names to a list of stopping strings.": "Добавлять имена персонажа и пользователя в список стоп-строк.",1651 "Add Character and User names to a list of stopping strings.": "Добавлять имена персонажа и пользователя в список стоп-строк.",
1652 "context_allow_post_history_instructions": "Добавлять в конец промпта инструкции после истории. Работает только при наличии таких инструкций в карточке И при включенной опции ''Приоритет инструкциям из карточек''.\nНЕ РЕКОМЕНДУЕТСЯ ДЛЯ МОДЕЛЕЙ TEXT COMPLETION, МОЖЕТ ПОРТИТЬ ВЫХОДНОЙ ТЕКСТ.",1652 "context_allow_post_history_instructions": "Добавлять в конец промпта инструкции после истории. Работает только при наличии таких инструкций в карточке И при включенной опции ''Приоритет инструкциям из карточек''.\nНЕ РЕКОМЕНДУЕТСЯ ДЛЯ МОДЕЛЕЙ TEXT COMPLETION, МОЖЕТ ПОРТИТЬ ВЫХОДНОЙ ТЕКСТ.",
1653 "First User Prefix": "Первый префикс пользователя",1653 "First User Prefix": "Первый префикс пользователя",
@@ -1914,8 +1914,8 @@
1914 "Cannot restore GUI preset": "Пресет для Gui восстановить нельзя",1914 "Cannot restore GUI preset": "Пресет для Gui восстановить нельзя",
1915 "Default preset cannot be restored": "Невозможно восстановить пресет по умолчанию",1915 "Default preset cannot be restored": "Невозможно восстановить пресет по умолчанию",
1916 "Default template cannot be restored": "Невозможно восстановить шаблон по умолчанию",1916 "Default template cannot be restored": "Невозможно восстановить шаблон по умолчанию",
1917 "Resetting a <b>default preset</b> will restore the default settings": "Сброс <b>стандартного пресета</b> восстановит настройки по умолчанию.",1917 "Resetting a <b>default preset</b> will restore the default settings.": "Сброс <b>комплектного пресета</b> восстановит настройки по умолчанию.",
1918 "Resetting a <b>default template</b> will restore the default settings.": "Сброс <b>стандартного шаблона</b> восстановит настройки по умолчанию.",1918 "Resetting a <b>default template</b> will restore the default settings.": "Сброс <b>комплектного шаблона</b> восстановит настройки по умолчанию.",
1919 "Are you sure?": "Вы уверены?",1919 "Are you sure?": "Вы уверены?",
1920 "Default preset restored": "Стандартный пресет восстановлен",1920 "Default preset restored": "Стандартный пресет восстановлен",
1921 "Default template restored": "Стандартный шаблон восстановлен",1921 "Default template restored": "Стандартный шаблон восстановлен",
@@ -2046,11 +2046,11 @@
2046 "prompt_post_processing_merge": "Объединять идущие подряд сообщения с одной ролью",2046 "prompt_post_processing_merge": "Объединять идущие подряд сообщения с одной ролью",
2047 "prompt_post_processing_semi": "Semi-strict (чередовать роли)",2047 "prompt_post_processing_semi": "Semi-strict (чередовать роли)",
2048 "prompt_post_processing_strict": "Strict (чередовать роли, сначала пользователь)",2048 "prompt_post_processing_strict": "Strict (чередовать роли, сначала пользователь)",
2049 "Select Horde models": "Выбрать модель из Horde",2049 "Select Horde models": "Выберите модель из Horde",
2050 "Model ID (optional)": "Идентификатор модели (необязательно)",2050 "Model ID (optional)": "Идентификатор модели (необязательно)",
2051 "Derive context size from backend": "Использовать бэкенд для определения размера контекста",2051 "Derive context size from backend": "Использовать бэкенд для определения размера контекста",
2052 "Rename current preset": "Переименовать пресет",2052 "Rename current preset": "Переименовать пресет",
2053 "No Worlds active. Click here to select.": "Нет активных миров. Нажмите, чтобы выбрать.",2053 "No Worlds active. Click here to select.": "Активных миров нет, ЛКМ для выбора.",
2054 "Title/Memo": "Название",2054 "Title/Memo": "Название",
2055 "Strategy": "Статус",2055 "Strategy": "Статус",
2056 "Position": "Позиция",2056 "Position": "Позиция",
@@ -2169,7 +2169,7 @@
2169 "instruct_derived": "Считывать из метаданных модели (по возможности)",2169 "instruct_derived": "Считывать из метаданных модели (по возможности)",
2170 "Confirm token parsing with": "Чтобы убедиться в правильности выделения токенов, используйте",2170 "Confirm token parsing with": "Чтобы убедиться в правильности выделения токенов, используйте",
2171 "Reasoning Effort": "Рассуждения",2171 "Reasoning Effort": "Рассуждения",
2172 "Constrains effort on reasoning for reasoning models.": "Регулирует объём внутренних рассуждений модели (reasoning), для моделей которые поддерживают эту возможность.\nНа данный момент поддерживаются три значения: Подробные, Обычные, Поверхностные.\nПри менее подробном рассуждении ответ получается быстрее, а также экономятся токены, уходящие на рассуждения.",2172 "Constrains effort on reasoning for reasoning models.": "Регулирует объём внутренних рассуждений модели (reasoning), для моделей, которые поддерживают эту возможность.\nПри менее подробном рассуждении ответ получается быстрее, а также экономятся токены, уходящие на рассуждения.",
2173 "openai_reasoning_effort_low": "Поверхностные",2173 "openai_reasoning_effort_low": "Поверхностные",
2174 "openai_reasoning_effort_medium": "Обычные",2174 "openai_reasoning_effort_medium": "Обычные",
2175 "openai_reasoning_effort_high": "Подробные",2175 "openai_reasoning_effort_high": "Подробные",
@@ -2274,8 +2274,8 @@
2274 "Persona Name Not Set": "У персоны отсутствует имя",2274 "Persona Name Not Set": "У персоны отсутствует имя",
2275 "You must bind a name to this persona before you can set a lorebook.": "Перед привязкой лорбука персоне необходимо присвоить имя.",2275 "You must bind a name to this persona before you can set a lorebook.": "Перед привязкой лорбука персоне необходимо присвоить имя.",
2276 "Default Persona Removed": "Персона по умолчанию снята",2276 "Default Persona Removed": "Персона по умолчанию снята",
2277 "Persona is locked to the current character": "Персона закреплена за этим персонажем",2277 "Persona is locked to the current character": "Персона закреплена за текущим персонажем",
2278 "Persona is locked to the current chat": "Персона закреплена за этим чатом",2278 "Persona is locked to the current chat": "Персона закреплена за текущим чатом",
2279 "characters": "перс.",2279 "characters": "перс.",
2280 "character": "персонаж",2280 "character": "персонаж",
2281 "in this group": "в группе",2281 "in this group": "в группе",
@@ -2336,5 +2336,88 @@
2336 "Reasoning already exists.": "Рассуждения уже присутствуют.",2336 "Reasoning already exists.": "Рассуждения уже присутствуют.",
2337 "Edit Message": "Редактирование",2337 "Edit Message": "Редактирование",
2338 "Status check bypassed": "Проверка статуса отключена",2338 "Status check bypassed": "Проверка статуса отключена",
2339 "Valid": "Работает"2339 "Valid": "Работает",
2340 "Use Group Scoring": "Использовать Group Scoring",
2341 "Only the entries with the most number of key matches will be selected for Inclusion Group filtering": "До групповых фильтров будут допущены только записи с наибольшим кол-вом совпадений",
2342 "Can be used to automatically activate Quick Replies": "Используется для автоматической активации быстрых ответов (Quick Replies)",
2343 "( None )": "(Отсутствует)",
2344 "Tie this entry to specific characters or characters with specific tags": "Привязать запись к опред. персонажам или персонажам с заданными тегами",
2345 "Move Entry to Another Lorebook": "Переместить запись в другой лорбук",
2346 "There are no other lorebooks to move to.": "Некуда перемещать: не найдено других лорбуков.",
2347 "Select Target Lorebook": "Выберите куда переместить",
2348 "Move '${0}' to:": "Переместить '${0}' в:",
2349 "Please select a target lorebook.": "Выберите лорбук, в который будет перемещена запись.",
2350 "Scan depth cannot be negative": "Глубина сканирования не может быть отрицательной",
2351 "Scan depth cannot exceed ${0}": "Глубина сканирования не может превышать ${0}",
2352 "Select your current Reasoning Template": "Выберите текущий Шаблон рассуждений",
2353 "Delete template": "Удалить шаблон",
2354 "Reasoning Template": "Шаблон рассуждений",
2355 "openai_reasoning_effort_auto": "Авто",
2356 "openai_reasoning_effort_minimum": "Минимальные",
2357 "openai_reasoning_effort_maximum": "Максимальные",
2358 "OpenAI-style options: low, medium, high. Minimum and maximum are aliased to low and high. Auto does not send an effort level.": "OpenAI принимает следующее: low (Поверхностные), medium (Обычные), high (Подробные). Minimum (Минимальные) - то же самое, что low. Maximum (Максимальные) - то же самое, что high. При выборе Auto (Авто) значение не отсылается вообще.",
2359 "Allocates a portion of the response length for thinking (low: 10%, medium: 25%, high: 50%). Other options are model-dependent.": "Резервирует часть ответа для рассуждений (Поверхностные: 10% ответа, Обычные: 25%, Подробные: 50%). Остальные значения зависят от конкретной модели.",
2360 "xAI Model": "Модель xAI",
2361 "xAI API Key": "Ключ от API xAI",
2362 "HuggingFace Token": "Токен HuggingFace",
2363 "Endpoint URL": "Адрес эндпоинта",
2364 "Example: https://****.endpoints.huggingface.cloud": "Пример: https://****.endpoints.huggingface.cloud",
2365 "Featherless Model Selection": "Выбор модели из Featherless",
2366 "category": "категория",
2367 "Top": "Топовые",
2368 "All Classes": "Все классы",
2369 "Date Asc": "Дата, возрастание",
2370 "Date Desc": "Дата, убывание",
2371 "Background Image": "Фоновое изображение",
2372 "Delete the background?": "Удалить фон?",
2373 "Tags_as_Folders_desc": "Чтобы тег отображался как папка, его нужно отметить таковым в меню управления тегами. Нажмите сюда, чтобы открыть его.",
2374 "tag_entries": "раз исп.",
2375 "Multiple personas are connected to this character.\nSelect a persona to use for this chat.": "К этому персонажу привязано несколько персон.\nВыберите персону, которую хотите использовать в этом чате.",
2376 "Select Persona": "Выберите персону",
2377 "Completion Object": "Как часть Completion Object",
2378 "Move ${0} to:": "Переместить '${0}' в:",
2379 "Chat Scenario Override": "Перезапись сценария чата",
2380 "Unique to this chat.": "Действует только в рамках текущего чата.",
2381 "All group members will use the following scenario text instead of what is specified in their character cards.": "Все участники группы будут использовать этот сценарий вместо того, который указан в карточке.",
2382 "Checkpoints inherit the scenario override from their parent, and can be changed individually after that.": "Чекпоинты наследуют сценарий родителя, после отделения его можно менять.",
2383 "Delete Tag": "Удалить тег",
2384 "Do you want to delete the tag": "Вы точно хотите удалить тег",
2385 "If you want to merge all references to this tag into another tag, select it below:": "Если хотите заменить ссылки на этот тег на какой-то другой, то выберите из списка:",
2386 "Open Folder (Show all characters even if not selected)": "Открытая папка (показать всех персонажей, включая невыбранных)",
2387 "Closed Folder (Hide all characters unless selected)": "Закрытая папка (скрыть всех персонажей, кроме выбранных)",
2388 "No Folder": "Не папка",
2389 "Show only favorites": "Показать только избранных персонажей",
2390 "Show only groups": "Показать только группы",
2391 "Show only folders": "Показать только папки",
2392 "Manage tags": "Панель управления тегами",
2393 "Show Tag List": "Показать список тегов",
2394 "Clear all filters": "Сбросить все фильтры",
2395 "There are no items to display.": "Отображать абсолютно нечего.",
2396 "Characters and groups hidden by filters or closed folders": "Персонажи и группы скрыты настройками фильтров либо закрытыми папками",
2397 "Otterly empty": "Всё что можно, всё выдрано",
2398 "Here be dragons": "Список настолько очистился, что в него вернулись драконы",
2399 "Kiwibunga": "Настолько пусто, что киви прилетела посидеть",
2400 "Pump-a-Rum": "Пу-пу-пу",
2401 "Croak it": "Только кваканье лягушек и стрёкот сверчков",
2402 "${0} character hidden.": "Персонажей скрыто: ${0}.",
2403 "${0} characters hidden.": "Персонажей скрыто: ${0}.",
2404 "/ page": "/ стр.",
2405 "Context Length": "Размер контекста",
2406 "Added On": "Добавлена",
2407 "Class": "Класс",
2408 "Bulk_edit_characters": "Массовое редактирование персонажей\n\nЛКМ, чтобы выделить либо отменить выделение персонажа\nShift+ЛКМ, чтобы массово выделить либо отменить выделение персонажей\nПКМ, чтобы выбрать действие",
2409 "Bulk select all characters": "Выбрать всех персонажей",
2410 "Duplicate": "Клонировать",
2411 "Next page": "След. страница",
2412 "Previous page": "Пред. страница",
2413 "Group: ${0}": "Группа: ${0}",
2414 "You deleted a character/chat and arrived back here for safety reasons! Pick another character!": "Вы удалили персонажа или чат, и мы из соображений безопасности перенесли вас на эту страницу! Выберите другого персонажа!",
2415 "Group is empty.": "Группа пуста.",
2416 "No characters available": "Персонажей нет",
2417 "Choose what to export": "Выберите, что экспортировать",
2418 "Text Completion Preset": "Пресет для режима Text Completion",
2419 "Update enabled": "Обновить включенные",
2420 "Could not connect to API": "Не удалось подключиться к API",
2421 "Connected to API": "Соединение с API установлено",
2422 "Go back": "Назад"
2340}2423}
public/script.js+33 -26
@@ -143,6 +143,7 @@ import {
143 getHordeModels,143 getHordeModels,
144 adjustHordeGenerationParams,144 adjustHordeGenerationParams,
145 MIN_LENGTH,145 MIN_LENGTH,
146 initHorde,
146} from './scripts/horde.js';147} from './scripts/horde.js';
147148
148import {149import {
@@ -175,6 +176,9 @@ import {
175 saveBase64AsFile,176 saveBase64AsFile,
176 uuidv4,177 uuidv4,
177 equalsIgnoreCaseAndAccents,178 equalsIgnoreCaseAndAccents,
179 localizePagination,
180 renderPaginationDropdown,
181 paginationDropdownChangeHandler,
178} from './scripts/utils.js';182} from './scripts/utils.js';
179import { debounce_timeout, IGNORE_SYMBOL } from './scripts/constants.js';183import { debounce_timeout, IGNORE_SYMBOL } from './scripts/constants.js';
180184
@@ -994,6 +998,7 @@ async function firstLoadInit() {
994 initAuthorsNote();998 initAuthorsNote();
995 await initPersonas();999 await initPersonas();
996 initWorldInfo();1000 initWorldInfo();
1001 initHorde();
997 initRossMods();1002 initRossMods();
998 initStats();1003 initStats();
999 initCfg();1004 initCfg();
@@ -1423,30 +1428,26 @@ function getBackBlock() {
1423 return template;1428 return template;
1424}1429}
14251430
1426function getEmptyBlock() {1431async function getEmptyBlock() {
1427 const icons = ['fa-dragon', 'fa-otter', 'fa-kiwi-bird', 'fa-crow', 'fa-frog'];1432 const icons = ['fa-dragon', 'fa-otter', 'fa-kiwi-bird', 'fa-crow', 'fa-frog'];
1428 const texts = ['Here be dragons', 'Otterly empty', 'Kiwibunga', 'Pump-a-Rum', 'Croak it'];1433 const texts = [t`Here be dragons`, t`Otterly empty`, t`Kiwibunga`, t`Pump-a-Rum`, t`Croak it`];
1429 const roll = new Date().getMinutes() % icons.length;1434 const roll = new Date().getMinutes() % icons.length;
1430 const emptyBlock = `1435 const params = {
1431 <div class="text_block empty_block">1436 text: texts[roll],
1432 <i class="fa-solid ${icons[roll]} fa-4x"></i>1437 icon: icons[roll],
1433 <h1>${texts[roll]}</h1>1438 };
1434 <p>There are no items to display.</p>1439 const emptyBlock = await renderTemplateAsync('emptyBlock', params);
1435 </div>`;
1436 return $(emptyBlock);1440 return $(emptyBlock);
1437}1441}
14381442
1439/**1443/**
1440 * @param {number} hidden Number of hidden characters1444 * @param {number} hidden Number of hidden characters
1441 */1445 */
1442function getHiddenBlock(hidden) {1446async function getHiddenBlock(hidden) {
1443 const hiddenBlock = `1447 const params = {
1444 <div class="text_block hidden_block">1448 text: (hidden > 1 ? t`${hidden} characters hidden.` : t`${hidden} character hidden.`),
1445 <small>1449 };
1446 <p>${hidden} ${hidden > 1 ? 'characters' : 'character'} hidden.</p>1450 const hiddenBlock = await renderTemplateAsync('hiddenBlock', params);
1447 <div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]Characters and groups hidden by filters or closed folders" title="Characters and groups hidden by filters or closed folders"></div>
1448 </small>
1449 </div>`;
1450 return $(hiddenBlock);1451 return $(hiddenBlock);
1451}1452}
14521453
@@ -1526,10 +1527,11 @@ export async function printCharacters(fullRefresh = false) {
15261527
1527 const entities = getEntitiesList({ doFilter: true });1528 const entities = getEntitiesList({ doFilter: true });
15281529
1530 const pageSize = Number(accountStorage.getItem(storageKey)) || per_page_default;
1531 const sizeChangerOptions = [10, 25, 50, 100, 250, 500, 1000];
1529 $('#rm_print_characters_pagination').pagination({1532 $('#rm_print_characters_pagination').pagination({
1530 dataSource: entities,1533 dataSource: entities,
1531 pageSize: Number(accountStorage.getItem(storageKey)) || per_page_default,1534 pageSize,
1532 sizeChangerOptions: [10, 25, 50, 100, 250, 500, 1000],
1533 pageRange: 1,1535 pageRange: 1,
1534 pageNumber: saveCharactersPage || 1,1536 pageNumber: saveCharactersPage || 1,
1535 position: 'top',1537 position: 'top',
@@ -1538,14 +1540,16 @@ export async function printCharacters(fullRefresh = false) {
1538 prevText: '<',1540 prevText: '<',
1539 nextText: '>',1541 nextText: '>',
1540 formatNavigator: PAGINATION_TEMPLATE,1542 formatNavigator: PAGINATION_TEMPLATE,
1543 formatSizeChanger: renderPaginationDropdown(pageSize, sizeChangerOptions),
1541 showNavigator: true,1544 showNavigator: true,
1542 callback: function (/** @type {Entity[]} */ data) {1545 callback: async function (/** @type {Entity[]} */ data) {
1543 $(listId).empty();1546 $(listId).empty();
1544 if (power_user.bogus_folders && isBogusFolderOpen()) {1547 if (power_user.bogus_folders && isBogusFolderOpen()) {
1545 $(listId).append(getBackBlock());1548 $(listId).append(getBackBlock());
1546 }1549 }
1547 if (!data.length) {1550 if (!data.length) {
1548 $(listId).append(getEmptyBlock());1551 const emptyBlock = await getEmptyBlock();
1552 $(listId).append(emptyBlock);
1549 }1553 }
1550 let displayCount = 0;1554 let displayCount = 0;
1551 for (const i of data) {1555 for (const i of data) {
@@ -1566,13 +1570,16 @@ export async function printCharacters(fullRefresh = false) {
15661570
1567 const hidden = (characters.length + groups.length) - displayCount;1571 const hidden = (characters.length + groups.length) - displayCount;
1568 if (hidden > 0 && entitiesFilter.hasAnyFilter()) {1572 if (hidden > 0 && entitiesFilter.hasAnyFilter()) {
1569 $(listId).append(getHiddenBlock(hidden));1573 const hiddenBlock = await getHiddenBlock(hidden);
1574 $(listId).append(hiddenBlock);
1570 }1575 }
1576 localizePagination($('#rm_print_characters_pagination'));
15711577
1572 eventSource.emit(event_types.CHARACTER_PAGE_LOADED);1578 eventSource.emit(event_types.CHARACTER_PAGE_LOADED);
1573 },1579 },
1574 afterSizeSelectorChange: function (e) {1580 afterSizeSelectorChange: function (e, size) {
1575 accountStorage.setItem(storageKey, e.target.value);1581 accountStorage.setItem(storageKey, e.target.value);
1582 paginationDropdownChangeHandler(e, size);
1576 },1583 },
1577 afterPaging: function (e) {1584 afterPaging: function (e) {
1578 saveCharactersPage = e;1585 saveCharactersPage = e;
@@ -8430,15 +8437,15 @@ export function callPopup(text, type, inputValue = '', { okButton, rows, wide, w
8430 function getOkButtonText() {8437 function getOkButtonText() {
8431 if (['text', 'char_not_selected'].includes(popup_type)) {8438 if (['text', 'char_not_selected'].includes(popup_type)) {
8432 $dialoguePopupCancel.css('display', 'none');8439 $dialoguePopupCancel.css('display', 'none');
8433 return okButton ?? 'Ok';8440 return okButton ?? t`Ok`;
8434 } else if (['delete_extension'].includes(popup_type)) {8441 } else if (['delete_extension'].includes(popup_type)) {
8435 return okButton ?? 'Ok';8442 return okButton ?? t`Ok`;
8436 } else if (['new_chat', 'confirm'].includes(popup_type)) {8443 } else if (['new_chat', 'confirm'].includes(popup_type)) {
8437 return okButton ?? 'Yes';8444 return okButton ?? t`Yes`;
8438 } else if (['input'].includes(popup_type)) {8445 } else if (['input'].includes(popup_type)) {
8439 return okButton ?? t`Save`;8446 return okButton ?? t`Save`;
8440 }8447 }
8441 return okButton ?? 'Delete';8448 return okButton ?? t`Delete`;
8442 }8449 }
84438450
8444 dialogueCloseStop = true;8451 dialogueCloseStop = true;
public/scripts/backgrounds.js+2 -1
@@ -6,6 +6,7 @@ import { SlashCommand } from './slash-commands/SlashCommand.js';
6import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';6import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
7import { flashHighlight, stringFormat } from './utils.js';7import { flashHighlight, stringFormat } from './utils.js';
8import { t } from './i18n.js';8import { t } from './i18n.js';
9import { Popup } from './popup.js';
910
10const BG_METADATA_KEY = 'custom_background';11const BG_METADATA_KEY = 'custom_background';
11const LIST_METADATA_KEY = 'chat_backgrounds';12const LIST_METADATA_KEY = 'chat_backgrounds';
@@ -291,7 +292,7 @@ async function onDeleteBackgroundClick(e) {
291 const bgToDelete = $(this).closest('.bg_example');292 const bgToDelete = $(this).closest('.bg_example');
292 const url = bgToDelete.data('url');293 const url = bgToDelete.data('url');
293 const isCustom = bgToDelete.attr('custom') === 'true';294 const isCustom = bgToDelete.attr('custom') === 'true';
294 const confirm = await callPopup('<h3>Delete the background?</h3>', 'confirm');295 const confirm = await Popup.show.confirm(t`Delete the background?`, null);
295 const bg = bgToDelete.attr('bgfile');296 const bg = bgToDelete.attr('bgfile');
296297
297 if (confirm) {298 if (confirm) {
public/scripts/group-chats.js+19 -7
@@ -13,6 +13,9 @@ import {
13 getBase64Async,13 getBase64Async,
14 resetScrollHeight,14 resetScrollHeight,
15 initScrollHeight,15 initScrollHeight,
16 localizePagination,
17 renderPaginationDropdown,
18 paginationDropdownChangeHandler,
16} from './utils.js';19} from './utils.js';
17import { RA_CountCharTokens, humanizedDateTime, dragElement, favsToHotswap, getMessageTimeStamp } from './RossAscends-mods.js';20import { RA_CountCharTokens, humanizedDateTime, dragElement, favsToHotswap, getMessageTimeStamp } from './RossAscends-mods.js';
18import { power_user, loadMovingUIState, sortEntitiesList } from './power-user.js';21import { power_user, loadMovingUIState, sortEntitiesList } from './power-user.js';
@@ -1374,6 +1377,8 @@ function getGroupCharacters({ doFilter, onlyMembers } = {}) {
13741377
1375function printGroupCandidates() {1378function printGroupCandidates() {
1376 const storageKey = 'GroupCandidates_PerPage';1379 const storageKey = 'GroupCandidates_PerPage';
1380 const pageSize = Number(accountStorage.getItem(storageKey)) || 5;
1381 const sizeChangerOptions = [5, 10, 25, 50, 100, 200, 500, 1000];
1377 $('#rm_group_add_members_pagination').pagination({1382 $('#rm_group_add_members_pagination').pagination({
1378 dataSource: getGroupCharacters({ doFilter: true, onlyMembers: false }),1383 dataSource: getGroupCharacters({ doFilter: true, onlyMembers: false }),
1379 pageRange: 1,1384 pageRange: 1,
@@ -1382,18 +1387,20 @@ function printGroupCandidates() {
1382 prevText: '<',1387 prevText: '<',
1383 nextText: '>',1388 nextText: '>',
1384 formatNavigator: PAGINATION_TEMPLATE,1389 formatNavigator: PAGINATION_TEMPLATE,
1390 formatSizeChanger: renderPaginationDropdown(pageSize, sizeChangerOptions),
1385 showNavigator: true,1391 showNavigator: true,
1386 showSizeChanger: true,1392 showSizeChanger: true,
1387 pageSize: Number(accountStorage.getItem(storageKey)) || 5,1393 pageSize,
1388 sizeChangerOptions: [5, 10, 25, 50, 100, 200, 500, 1000],1394 afterSizeSelectorChange: function (e, size) {
1389 afterSizeSelectorChange: function (e) {
1390 accountStorage.setItem(storageKey, e.target.value);1395 accountStorage.setItem(storageKey, e.target.value);
1396 paginationDropdownChangeHandler(e, size);
1391 },1397 },
1392 callback: function (data) {1398 callback: function (data) {
1393 $('#rm_group_add_members').empty();1399 $('#rm_group_add_members').empty();
1394 for (const i of data) {1400 for (const i of data) {
1395 $('#rm_group_add_members').append(getGroupCharacterBlock(i.item));1401 $('#rm_group_add_members').append(getGroupCharacterBlock(i.item));
1396 }1402 }
1403 localizePagination($('#rm_group_add_members_pagination'));
1397 },1404 },
1398 });1405 });
1399}1406}
@@ -1401,6 +1408,9 @@ function printGroupCandidates() {
1401function printGroupMembers() {1408function printGroupMembers() {
1402 const storageKey = 'GroupMembers_PerPage';1409 const storageKey = 'GroupMembers_PerPage';
1403 $('.rm_group_members_pagination').each(function () {1410 $('.rm_group_members_pagination').each(function () {
1411 let that = this;
1412 const pageSize = Number(accountStorage.getItem(storageKey)) || 5;
1413 const sizeChangerOptions = [5, 10, 25, 50, 100, 200, 500, 1000];
1404 $(this).pagination({1414 $(this).pagination({
1405 dataSource: getGroupCharacters({ doFilter: false, onlyMembers: true }),1415 dataSource: getGroupCharacters({ doFilter: false, onlyMembers: true }),
1406 pageRange: 1,1416 pageRange: 1,
@@ -1411,16 +1421,18 @@ function printGroupMembers() {
1411 formatNavigator: PAGINATION_TEMPLATE,1421 formatNavigator: PAGINATION_TEMPLATE,
1412 showNavigator: true,1422 showNavigator: true,
1413 showSizeChanger: true,1423 showSizeChanger: true,
1414 pageSize: Number(accountStorage.getItem(storageKey)) || 5,1424 formatSizeChanger: renderPaginationDropdown(pageSize, sizeChangerOptions),
1415 sizeChangerOptions: [5, 10, 25, 50, 100, 200, 500, 1000],1425 pageSize,
1416 afterSizeSelectorChange: function (e) {1426 afterSizeSelectorChange: function (e, size) {
1417 accountStorage.setItem(storageKey, e.target.value);1427 accountStorage.setItem(storageKey, e.target.value);
1428 paginationDropdownChangeHandler(e, size);
1418 },1429 },
1419 callback: function (data) {1430 callback: function (data) {
1420 $('.rm_group_members').empty();1431 $('.rm_group_members').empty();
1421 for (const i of data) {1432 for (const i of data) {
1422 $('.rm_group_members').append(getGroupCharacterBlock(i.item));1433 $('.rm_group_members').append(getGroupCharacterBlock(i.item));
1423 }1434 }
1435 localizePagination($(that));
1424 },1436 },
1425 });1437 });
1426 });1438 });
@@ -1804,7 +1816,7 @@ async function createGroup() {
1804 const memberNames = characters.filter(x => members.includes(x.avatar)).map(x => x.name).join(', ');1816 const memberNames = characters.filter(x => members.includes(x.avatar)).map(x => x.name).join(', ');
18051817
1806 if (!name) {1818 if (!name) {
1807 name = `Group: ${memberNames}`;1819 name = t`Group: ${memberNames}`;
1808 }1820 }
18091821
1810 const avatar_url = $('#group_avatar_preview img').attr('src');1822 const avatar_url = $('#group_avatar_preview img').attr('src');
public/scripts/horde.js+3 -3
@@ -394,7 +394,7 @@ function getHordeModelTemplate(option) {
394 `));394 `));
395}395}
396396
397jQuery(function () {397export function initHorde () {
398 $('#horde_model').on('mousedown change', async function (e) {398 $('#horde_model').on('mousedown change', async function (e) {
399 console.log('Horde model change', e);399 console.log('Horde model change', e);
400 horde_settings.models = $('#horde_model').val();400 horde_settings.models = $('#horde_model').val();
@@ -441,7 +441,7 @@ jQuery(function () {
441 if (!isMobile()) {441 if (!isMobile()) {
442 $('#horde_model').select2({442 $('#horde_model').select2({
443 width: '100%',443 width: '100%',
444 placeholder: 'Select Horde models',444 placeholder: t`Select Horde models`,
445 allowClear: true,445 allowClear: true,
446 closeOnSelect: false,446 closeOnSelect: false,
447 templateSelection: function (data) {447 templateSelection: function (data) {
@@ -451,5 +451,5 @@ jQuery(function () {
451 templateResult: getHordeModelTemplate,451 templateResult: getHordeModelTemplate,
452 });452 });
453 }453 }
454});454}
455455
public/scripts/openai.js+1 -1
@@ -3851,7 +3851,7 @@ function createLogitBiasListItem(entry) {
3851}3851}
38523852
3853async function createNewLogitBiasPreset() {3853async function createNewLogitBiasPreset() {
3854 const name = await callPopup('Preset name:', 'input');3854 const name = await Popup.show.input(t`Preset name:`, null);
38553855
3856 if (!name) {3856 if (!name) {
3857 return;3857 return;
public/scripts/personas.js+7 -3
@@ -22,7 +22,7 @@ import {
22} from '../script.js';22} from '../script.js';
23import { persona_description_positions, power_user } from './power-user.js';23import { persona_description_positions, power_user } from './power-user.js';
24import { getTokenCountAsync } from './tokenizers.js';24import { getTokenCountAsync } from './tokenizers.js';
25import { PAGINATION_TEMPLATE, clearInfoBlock, debounce, delay, download, ensureImageFormatSupported, flashHighlight, getBase64Async, getCharIndex, isFalseBoolean, isTrueBoolean, onlyUnique, parseJsonFile, setInfoBlock } from './utils.js';25import { PAGINATION_TEMPLATE, clearInfoBlock, debounce, delay, download, ensureImageFormatSupported, flashHighlight, getBase64Async, getCharIndex, isFalseBoolean, isTrueBoolean, onlyUnique, parseJsonFile, setInfoBlock, localizePagination, renderPaginationDropdown, paginationDropdownChangeHandler } from './utils.js';
26import { debounce_timeout } from './constants.js';26import { debounce_timeout } from './constants.js';
27import { FILTER_TYPES, FilterHelper } from './filters.js';27import { FILTER_TYPES, FilterHelper } from './filters.js';
28import { groups, selected_group } from './group-chats.js';28import { groups, selected_group } from './group-chats.js';
@@ -250,16 +250,18 @@ export async function getUserAvatars(doRender = true, openPageAt = '') {
250 const storageKey = 'Personas_PerPage';250 const storageKey = 'Personas_PerPage';
251 const listId = '#user_avatar_block';251 const listId = '#user_avatar_block';
252 const perPage = Number(accountStorage.getItem(storageKey)) || 5;252 const perPage = Number(accountStorage.getItem(storageKey)) || 5;
253 const sizeChangerOptions = [5, 10, 25, 50, 100, 250, 500, 1000];
253254
254 $('#persona_pagination_container').pagination({255 $('#persona_pagination_container').pagination({
255 dataSource: entities,256 dataSource: entities,
256 pageSize: perPage,257 pageSize: perPage,
257 sizeChangerOptions: [5, 10, 25, 50, 100, 250, 500, 1000],258 sizeChangerOptions,
258 pageRange: 1,259 pageRange: 1,
259 pageNumber: savePersonasPage || 1,260 pageNumber: savePersonasPage || 1,
260 position: 'top',261 position: 'top',
261 showPageNumbers: false,262 showPageNumbers: false,
262 showSizeChanger: true,263 showSizeChanger: true,
264 formatSizeChanger: renderPaginationDropdown(perPage, sizeChangerOptions),
263 prevText: '<',265 prevText: '<',
264 nextText: '>',266 nextText: '>',
265 formatNavigator: PAGINATION_TEMPLATE,267 formatNavigator: PAGINATION_TEMPLATE,
@@ -270,9 +272,11 @@ export async function getUserAvatars(doRender = true, openPageAt = '') {
270 $(listId).append(getUserAvatarBlock(item));272 $(listId).append(getUserAvatarBlock(item));
271 }273 }
272 updatePersonaUIStates();274 updatePersonaUIStates();
275 localizePagination($('#persona_pagination_container'));
273 },276 },
274 afterSizeSelectorChange: function (e) {277 afterSizeSelectorChange: function (e, size) {
275 accountStorage.setItem(storageKey, e.target.value);278 accountStorage.setItem(storageKey, e.target.value);
279 paginationDropdownChangeHandler(e, size);
276 },280 },
277 afterPaging: function (e) {281 afterPaging: function (e) {
278 savePersonasPage = e;282 savePersonasPage = e;
public/scripts/tags.js+5 -11
@@ -27,7 +27,7 @@ import { debounce_timeout } from './constants.js';
27import { INTERACTABLE_CONTROL_CLASS } from './keyboard.js';27import { INTERACTABLE_CONTROL_CLASS } from './keyboard.js';
28import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js';28import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js';
29import { renderTemplateAsync } from './templates.js';29import { renderTemplateAsync } from './templates.js';
30import { t } from './i18n.js';30import { t, translate } from './i18n.js';
3131
32export {32export {
33 TAG_FOLDER_TYPES,33 TAG_FOLDER_TYPES,
@@ -318,7 +318,7 @@ function getTagBlock(tag, entities, hidden = 0, isUseless = false) {
318 template.find('.avatar').css({ 'background-color': tag.color, 'color': tag.color2 }).attr('title', `[Folder] ${tag.name}`);318 template.find('.avatar').css({ 'background-color': tag.color, 'color': tag.color2 }).attr('title', `[Folder] ${tag.name}`);
319 template.find('.ch_name').text(tag.name).attr('title', `[Folder] ${tag.name}`);319 template.find('.ch_name').text(tag.name).attr('title', `[Folder] ${tag.name}`);
320 template.find('.bogus_folder_hidden_counter').text(hidden > 0 ? `${hidden} hidden` : '');320 template.find('.bogus_folder_hidden_counter').text(hidden > 0 ? `${hidden} hidden` : '');
321 template.find('.bogus_folder_counter').text(`${count} ${count != 1 ? 'characters' : 'character'}`);321 template.find('.bogus_folder_counter').text(`${count} ` + (count != 1 ? t`characters` : t`character`));
322 template.find('.bogus_folder_icon').addClass(tagFolder.fa_icon);322 template.find('.bogus_folder_icon').addClass(tagFolder.fa_icon);
323 if (isUseless) template.addClass('useless');323 if (isUseless) template.addClass('useless');
324324
@@ -1057,7 +1057,7 @@ function appendTagToList(listElement, tag, { removable = false, isFilter = false
1057 tagElement.attr('title', tag.title);1057 tagElement.attr('title', tag.title);
1058 }1058 }
1059 if (tag.icon) {1059 if (tag.icon) {
1060 tagElement.find('.tag_name').text('').attr('title', `${tag.name} ${tag.title || ''}`.trim()).addClass(tag.icon);1060 tagElement.find('.tag_name').text('').attr('title', `${translate(tag.name)} ${tag.title || ''}`.trim()).addClass(tag.icon);
1061 tagElement.addClass('actionable');1061 tagElement.addClass('actionable');
1062 }1062 }
10631063
@@ -1644,6 +1644,7 @@ function updateDrawTagFolder(element, tag) {
16441644
1645 // Draw/update css attributes for this class1645 // Draw/update css attributes for this class
1646 folderElement.attr('title', tagFolder.tooltip);1646 folderElement.attr('title', tagFolder.tooltip);
1647 folderElement.attr('data-i18n', '[title]' + tagFolder.tooltip);
1647 const indicator = folderElement.find('.tag_folder_indicator');1648 const indicator = folderElement.find('.tag_folder_indicator');
1648 indicator.text(tagFolder.icon);1649 indicator.text(tagFolder.icon);
1649 indicator.css('color', tagFolder.color);1650 indicator.css('color', tagFolder.color);
@@ -1655,14 +1656,7 @@ async function onTagDeleteClick() {
1655 const tag = tags.find(x => x.id === id);1656 const tag = tags.find(x => x.id === id);
1656 const otherTags = sortTags(tags.filter(x => x.id !== id).map(x => ({ id: x.id, name: x.name })));1657 const otherTags = sortTags(tags.filter(x => x.id !== id).map(x => ({ id: x.id, name: x.name })));
16571658
1658 const popupContent = $(`1659 const popupContent = $(await renderTemplateAsync('deleteTag', { otherTags }));
1659 <h3>Delete Tag</h3>
1660 <div>Do you want to delete the tag <div id="tag_to_delete" class="tags_inline inline-flex margin-r2"></div>?</div>
1661 <div class="m-t-2 marginBot5">If you want to merge all references to this tag into another tag, select it below:</div>
1662 <select id="merge_tag_select">
1663 <option value="">--- None ---</option>
1664 ${otherTags.map(x => `<option value="${x.id}">${x.name}</option>`).join('')}
1665 </select>`);
16661660
1667 appendTagToList(popupContent.find('#tag_to_delete'), tag);1661 appendTagToList(popupContent.find('#tag_to_delete'), tag);
16681662
public/scripts/templates/deleteTag.html+9 -0
@@ -0,0 +1,9 @@
1<h3 data-i18n="Delete Tag">Delete Tag</h3>
2<div><span data-i18n="Do you want to delete the tag">Do you want to delete the tag</span> <div id="tag_to_delete" class="tags_inline inline-flex margin-r2"></div>?</div>
3<div class="m-t-2 marginBot5" data-i18n="If you want to merge all references to this tag into another tag, select it below:">If you want to merge all references to this tag into another tag, select it below:</div>
4<select id="merge_tag_select">
5 <option value="">--- None ---</option>
6 {{#each otherTags}}
7 <option value="{{this.id}}">{{this.name}}</option>
8 {{/each}}
9</select>
\ No newline at end of file9 \ No newline at end of file
public/scripts/templates/emptyBlock.html+7 -0
@@ -0,0 +1,7 @@
1<div class="text_block empty_block">
2 <i class="fa-solid {{icon}} fa-4x"></i>
3 <h1>{{text}}</h1>
4 <p data-i18n="There are no items to display.">
5 There are no items to display.
6 </p>
7</div>
public/scripts/templates/hiddenBlock.html+6 -0
@@ -0,0 +1,6 @@
1<div class="text_block hidden_block">
2 <small>
3 <p>{{text}}</p>
4 <div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]Characters and groups hidden by filters or closed folders" title="Characters and groups hidden by filters or closed folders"></div>
5 </small>
6</div>
public/scripts/textgen-models.js+6 -6
@@ -7,6 +7,7 @@ import { renderTemplateAsync } from './templates.js';
7import { POPUP_TYPE, callGenericPopup } from './popup.js';7import { POPUP_TYPE, callGenericPopup } from './popup.js';
8import { t } from './i18n.js';8import { t } from './i18n.js';
9import { accountStorage } from './util/AccountStorage.js';9import { accountStorage } from './util/AccountStorage.js';
10import { localizePagination, PAGINATION_TEMPLATE } from './utils.js';
1011
11let mancerModels = [];12let mancerModels = [];
12let togetherModels = [];13let togetherModels = [];
@@ -361,9 +362,7 @@ export async function loadFeatherlessModels(data) {
361 showSizeChanger: false,362 showSizeChanger: false,
362 prevText: '<',363 prevText: '<',
363 nextText: '>',364 nextText: '>',
364 formatNavigator: function (currentPage, totalPage) {365 formatNavigator: PAGINATION_TEMPLATE,
365 return (currentPage - 1) * perPage + 1 + ' - ' + currentPage * perPage + ' of ' + totalPage * perPage;
366 },
367 showNavigator: true,366 showNavigator: true,
368 callback: function (modelsOnPage, pagination) {367 callback: function (modelsOnPage, pagination) {
369 modelCardBlock.innerHTML = '';368 modelCardBlock.innerHTML = '';
@@ -385,15 +384,15 @@ export async function loadFeatherlessModels(data) {
385384
386 const modelClassDiv = document.createElement('div');385 const modelClassDiv = document.createElement('div');
387 modelClassDiv.classList.add('model-class');386 modelClassDiv.classList.add('model-class');
388 modelClassDiv.textContent = `Class: ${model.model_class || 'N/A'}`;387 modelClassDiv.textContent = t`Class` + `: ${model.model_class || 'N/A'}`;
389388
390 const contextLengthDiv = document.createElement('div');389 const contextLengthDiv = document.createElement('div');
391 contextLengthDiv.classList.add('model-context-length');390 contextLengthDiv.classList.add('model-context-length');
392 contextLengthDiv.textContent = `Context Length: ${model.context_length}`;391 contextLengthDiv.textContent = t`Context Length` + `: ${model.context_length}`;
393392
394 const dateAddedDiv = document.createElement('div');393 const dateAddedDiv = document.createElement('div');
395 dateAddedDiv.classList.add('model-date-added');394 dateAddedDiv.classList.add('model-date-added');
396 dateAddedDiv.textContent = `Added On: ${new Date(model.created * 1000).toLocaleDateString()}`;395 dateAddedDiv.textContent = t`Added On` + `: ${new Date(model.created * 1000).toLocaleDateString()}`;
397396
398 detailsContainer.appendChild(modelClassDiv);397 detailsContainer.appendChild(modelClassDiv);
399 detailsContainer.appendChild(contextLengthDiv);398 detailsContainer.appendChild(contextLengthDiv);
@@ -417,6 +416,7 @@ export async function loadFeatherlessModels(data) {
417416
418 // Update the current page value whenever the page changes417 // Update the current page value whenever the page changes
419 featherlessCurrentPage = pagination.pageNumber;418 featherlessCurrentPage = pagination.pageNumber;
419 localizePagination(paginationContainer);
420 },420 },
421 afterSizeSelectorChange: function (e) {421 afterSizeSelectorChange: function (e) {
422 const newPerPage = e.target.value;422 const newPerPage = e.target.value;
public/scripts/utils.js+40 -1
@@ -20,7 +20,46 @@ import { getCurrentLocale, t } from './i18n.js';
20 * Pagination status string template.20 * Pagination status string template.
21 * @type {string}21 * @type {string}
22 */22 */
23export const PAGINATION_TEMPLATE = '<%= rangeStart %>-<%= rangeEnd %> of <%= totalNumber %>';23export const PAGINATION_TEMPLATE = '<%= rangeStart %>-<%= rangeEnd %> .. <%= totalNumber %>';
24
25export const localizePagination = function(container) {
26 container.find('[title="Next page"]').attr('title', t`Next page`);
27 container.find('[title="Previous page"]').attr('title', t`Previous page`);
28};
29
30/**
31 * Renders a dropdown for selecting page size in pagination.
32 * @param {number} pageSize Page size
33 * @param {number[]} sizeChangerOptions Array of page size options
34 * @returns {string} The rendered dropdown element as a string
35 */
36export const renderPaginationDropdown = function(pageSize, sizeChangerOptions) {
37 const sizeSelect = document.createElement('select');
38 sizeSelect.classList.add('J-paginationjs-size-select');
39
40 if (sizeChangerOptions.indexOf(pageSize) === -1) {
41 sizeChangerOptions.unshift(pageSize);
42 sizeChangerOptions.sort((a, b) => a - b);
43 }
44
45 for (let i = 0; i < sizeChangerOptions.length; i++) {
46 const option = document.createElement('option');
47 option.value = `${sizeChangerOptions[i]}`;
48 option.textContent = `${sizeChangerOptions[i]} ${t`/ page`}`;
49 if (sizeChangerOptions[i] === pageSize) {
50 option.setAttribute('selected', 'selected');
51 }
52 sizeSelect.appendChild(option);
53 }
54
55 return sizeSelect.outerHTML;
56};
57
58export const paginationDropdownChangeHandler = function(event, size) {
59 let dropdown = $(event?.originalEvent?.currentTarget || event.delegateTarget).find('select');
60 dropdown.find('[selected]').removeAttr('selected');
61 dropdown.find(`[value=${size}]`).attr('selected', '');
62};
2463
25/**64/**
26 * Navigation options for pagination.65 * Navigation options for pagination.
public/scripts/world-info.js+2 -2
@@ -2658,7 +2658,7 @@ export async function getWorldEntry(name, data, entry) {
2658 if (!isMobile()) {2658 if (!isMobile()) {
2659 $(characterFilter).select2({2659 $(characterFilter).select2({
2660 width: '100%',2660 width: '100%',
2661 placeholder: 'Tie this entry to specific characters or characters with specific tags',2661 placeholder: t`Tie this entry to specific characters or characters with specific tags`,
2662 allowClear: true,2662 allowClear: true,
2663 closeOnSelect: false,2663 closeOnSelect: false,
2664 });2664 });
@@ -3258,7 +3258,7 @@ export async function getWorldEntry(name, data, entry) {
32583258
3259 // Create wrapper div3259 // Create wrapper div
3260 const wrapper = document.createElement('div');3260 const wrapper = document.createElement('div');
3261 wrapper.textContent = t`Move "${sourceName}" to:`;3261 wrapper.textContent = t`Move '${sourceName}' to:`;
32623262
3263 // Create container and append elements3263 // Create container and append elements
3264 const container = document.createElement('div');3264 const container = document.createElement('div');
public/style.css+1 -0
@@ -5715,6 +5715,7 @@ body:not(.movingUI) .drawer-content.maximized {
5715 width: unset;5715 width: unset;
5716 margin: 0;5716 margin: 0;
5717 font-size: calc(var(--mainFontSize) * 0.85);5717 font-size: calc(var(--mainFontSize) * 0.85);
5718 padding-right: 20px;
5718}5719}
57195720
5720.paginationjs-pages ul li a {5721.paginationjs-pages ul li a {