Merge pull request #3930 from Yokayo/staging Update ru-ru translation
Signed| @@ -87,7 +87,7 @@ | |||
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | #rm_group_members:empty::before { | 89 | #rm_group_members:empty::before { |
| 90 | content: 'Group is empty'; | 90 | content: attr(group_empty_text); |
| 91 | 91 | ||
| 92 | font-weight: bolder; | 92 | font-weight: bolder; |
| 93 | width: 100%; | 93 | width: 100%; |
| @@ -115,7 +115,7 @@ | |||
| 115 | } | 115 | } |
| 116 | 116 | ||
| 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); |
| 119 | 119 | ||
| 120 | font-weight: bolder; | 120 | font-weight: bolder; |
| 121 | width: 100%; | 121 | width: 100%; |
| @@ -2052,8 +2052,8 @@ | |||
| 2052 | <div class="flex-container oneline-dropdown" title="Constrains effort on reasoning for reasoning models. 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. 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> entries</div> | 5972 | <div class="tag_view_counter"><span class="tag_view_counter_value"></span> <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> |
| @@ -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. В единицах 1e-4; разумное значение - 3. Установите в 0, чтобы отключить. См. статью 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 | } |
| @@ -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'; |
| 147 | 148 | ||
| 148 | import { | 149 | import { |
| @@ -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'; |
| 179 | import { debounce_timeout, IGNORE_SYMBOL } from './scripts/constants.js'; | 183 | import { debounce_timeout, IGNORE_SYMBOL } from './scripts/constants.js'; |
| 180 | 184 | ||
| @@ -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 | } |
| 1425 | 1430 | ||
| 1426 | function getEmptyBlock() { | 1431 | async 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 | } |
| 1438 | 1442 | ||
| 1439 | /** | 1443 | /** |
| 1440 | * @param {number} hidden Number of hidden characters | 1444 | * @param {number} hidden Number of hidden characters |
| 1441 | */ | 1445 | */ |
| 1442 | function getHiddenBlock(hidden) { | 1446 | async 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 | } |
| 1452 | 1453 | ||
| @@ -1526,10 +1527,11 @@ export async function printCharacters(fullRefresh = false) { | |||
| 1526 | 1527 | ||
| 1527 | const entities = getEntitiesList({ doFilter: true }); | 1528 | const entities = getEntitiesList({ doFilter: true }); |
| 1528 | 1529 | ||
| 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) { | |||
| 1566 | 1570 | ||
| 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')); | ||
| 1571 | 1577 | ||
| 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 | } |
| 8443 | 8450 | ||
| 8444 | dialogueCloseStop = true; | 8451 | dialogueCloseStop = true; |
| @@ -6,6 +6,7 @@ import { SlashCommand } from './slash-commands/SlashCommand.js'; | |||
| 6 | import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; | 6 | import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; |
| 7 | import { flashHighlight, stringFormat } from './utils.js'; | 7 | import { flashHighlight, stringFormat } from './utils.js'; |
| 8 | import { t } from './i18n.js'; | 8 | import { t } from './i18n.js'; |
| 9 | import { Popup } from './popup.js'; | ||
| 9 | 10 | ||
| 10 | const BG_METADATA_KEY = 'custom_background'; | 11 | const BG_METADATA_KEY = 'custom_background'; |
| 11 | const LIST_METADATA_KEY = 'chat_backgrounds'; | 12 | const 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'); |
| 296 | 297 | ||
| 297 | if (confirm) { | 298 | if (confirm) { |
| @@ -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'; |
| 17 | import { RA_CountCharTokens, humanizedDateTime, dragElement, favsToHotswap, getMessageTimeStamp } from './RossAscends-mods.js'; | 20 | import { RA_CountCharTokens, humanizedDateTime, dragElement, favsToHotswap, getMessageTimeStamp } from './RossAscends-mods.js'; |
| 18 | import { power_user, loadMovingUIState, sortEntitiesList } from './power-user.js'; | 21 | import { power_user, loadMovingUIState, sortEntitiesList } from './power-user.js'; |
| @@ -1374,6 +1377,8 @@ function getGroupCharacters({ doFilter, onlyMembers } = {}) { | |||
| 1374 | 1377 | ||
| 1375 | function printGroupCandidates() { | 1378 | function 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() { | |||
| 1401 | function printGroupMembers() { | 1408 | function 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(', '); |
| 1805 | 1817 | ||
| 1806 | if (!name) { | 1818 | if (!name) { |
| 1807 | name = `Group: ${memberNames}`; | 1819 | name = t`Group: ${memberNames}`; |
| 1808 | } | 1820 | } |
| 1809 | 1821 | ||
| 1810 | const avatar_url = $('#group_avatar_preview img').attr('src'); | 1822 | const avatar_url = $('#group_avatar_preview img').attr('src'); |
| @@ -394,7 +394,7 @@ function getHordeModelTemplate(option) { | |||
| 394 | `)); | 394 | `)); |
| 395 | } | 395 | } |
| 396 | 396 | ||
| 397 | jQuery(function () { | 397 | export 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 | } |
| 455 | 455 | ||
| @@ -3851,7 +3851,7 @@ function createLogitBiasListItem(entry) { | |||
| 3851 | } | 3851 | } |
| 3852 | 3852 | ||
| 3853 | async function createNewLogitBiasPreset() { | 3853 | async function createNewLogitBiasPreset() { |
| 3854 | const name = await callPopup('Preset name:', 'input'); | 3854 | const name = await Popup.show.input(t`Preset name:`, null); |
| 3855 | 3855 | ||
| 3856 | if (!name) { | 3856 | if (!name) { |
| 3857 | return; | 3857 | return; |
| @@ -22,7 +22,7 @@ import { | |||
| 22 | } from '../script.js'; | 22 | } from '../script.js'; |
| 23 | import { persona_description_positions, power_user } from './power-user.js'; | 23 | import { persona_description_positions, power_user } from './power-user.js'; |
| 24 | import { getTokenCountAsync } from './tokenizers.js'; | 24 | import { getTokenCountAsync } from './tokenizers.js'; |
| 25 | import { PAGINATION_TEMPLATE, clearInfoBlock, debounce, delay, download, ensureImageFormatSupported, flashHighlight, getBase64Async, getCharIndex, isFalseBoolean, isTrueBoolean, onlyUnique, parseJsonFile, setInfoBlock } from './utils.js'; | 25 | import { PAGINATION_TEMPLATE, clearInfoBlock, debounce, delay, download, ensureImageFormatSupported, flashHighlight, getBase64Async, getCharIndex, isFalseBoolean, isTrueBoolean, onlyUnique, parseJsonFile, setInfoBlock, localizePagination, renderPaginationDropdown, paginationDropdownChangeHandler } from './utils.js'; |
| 26 | import { debounce_timeout } from './constants.js'; | 26 | import { debounce_timeout } from './constants.js'; |
| 27 | import { FILTER_TYPES, FilterHelper } from './filters.js'; | 27 | import { FILTER_TYPES, FilterHelper } from './filters.js'; |
| 28 | import { groups, selected_group } from './group-chats.js'; | 28 | import { 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]; | ||
| 253 | 254 | ||
| 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; |
| @@ -27,7 +27,7 @@ import { debounce_timeout } from './constants.js'; | |||
| 27 | import { INTERACTABLE_CONTROL_CLASS } from './keyboard.js'; | 27 | import { INTERACTABLE_CONTROL_CLASS } from './keyboard.js'; |
| 28 | import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js'; | 28 | import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 29 | import { renderTemplateAsync } from './templates.js'; | 29 | import { renderTemplateAsync } from './templates.js'; |
| 30 | import { t } from './i18n.js'; | 30 | import { t, translate } from './i18n.js'; |
| 31 | 31 | ||
| 32 | export { | 32 | export { |
| 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'); |
| 324 | 324 | ||
| @@ -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 | } |
| 1063 | 1063 | ||
| @@ -1644,6 +1644,7 @@ function updateDrawTagFolder(element, tag) { | |||
| 1644 | 1644 | ||
| 1645 | // Draw/update css attributes for this class | 1645 | // 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 }))); |
| 1657 | 1658 | ||
| 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>`); | ||
| 1666 | 1660 | ||
| 1667 | appendTagToList(popupContent.find('#tag_to_delete'), tag); | 1661 | appendTagToList(popupContent.find('#tag_to_delete'), tag); |
| 1668 | 1662 | ||
| @@ -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 file | 9 | \ No newline at end of file | |
| @@ -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> | ||
| @@ -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> | ||
| @@ -7,6 +7,7 @@ import { renderTemplateAsync } from './templates.js'; | |||
| 7 | import { POPUP_TYPE, callGenericPopup } from './popup.js'; | 7 | import { POPUP_TYPE, callGenericPopup } from './popup.js'; |
| 8 | import { t } from './i18n.js'; | 8 | import { t } from './i18n.js'; |
| 9 | import { accountStorage } from './util/AccountStorage.js'; | 9 | import { accountStorage } from './util/AccountStorage.js'; |
| 10 | import { localizePagination, PAGINATION_TEMPLATE } from './utils.js'; | ||
| 10 | 11 | ||
| 11 | let mancerModels = []; | 12 | let mancerModels = []; |
| 12 | let togetherModels = []; | 13 | let 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) { | |||
| 385 | 384 | ||
| 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'}`; |
| 389 | 388 | ||
| 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}`; |
| 393 | 392 | ||
| 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()}`; |
| 397 | 396 | ||
| 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) { | |||
| 417 | 416 | ||
| 418 | // Update the current page value whenever the page changes | 417 | // 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; |
| @@ -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 | */ |
| 23 | export const PAGINATION_TEMPLATE = '<%= rangeStart %>-<%= rangeEnd %> of <%= totalNumber %>'; | 23 | export const PAGINATION_TEMPLATE = '<%= rangeStart %>-<%= rangeEnd %> .. <%= totalNumber %>'; |
| 24 | |||
| 25 | export 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 | */ | ||
| 36 | export 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 | |||
| 58 | export 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 | }; | ||
| 24 | 63 | ||
| 25 | /** | 64 | /** |
| 26 | * Navigation options for pagination. | 65 | * Navigation options for pagination. |
| @@ -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) { | |||
| 3258 | 3258 | ||
| 3259 | // Create wrapper div | 3259 | // 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:`; |
| 3262 | 3262 | ||
| 3263 | // Create container and append elements | 3263 | // Create container and append elements |
| 3264 | const container = document.createElement('div'); | 3264 | const container = document.createElement('div'); |
| @@ -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 | } |
| 5719 | 5720 | ||
| 5720 | .paginationjs-pages ul li a { | 5721 | .paginationjs-pages ul li a { |