From 82dff4f2047e87d8ad6639fb8a14487b88a5a96b Mon Sep 17 00:00:00 2001 From: Ivruix <52746744+Ivruix@users.noreply.github.com> Date: Mon, 2 Dec 2024 20:37:17 +0000 Subject: [PATCH 1/8] Add docker tutorial to Russian readme --- .github/readme-ru_ru.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/.github/readme-ru_ru.md b/.github/readme-ru_ru.md index 3b55ab7a9..1d2fbdc68 100644 --- a/.github/readme-ru_ru.md +++ b/.github/readme-ru_ru.md @@ -209,6 +209,44 @@ SillyTavern поддерживает расширения. 5. Запустите лаунчер установки: `chmod +x install.sh && ./install.sh` and choose what you wanna install 6. После завершения установки, запустите лаунчер следующей командой: `chmod +x launcher.sh && ./launcher.sh` +## 🐋 Установка с помощью Docker + +Предполагается, что вы уже установили Docker, имеете доступ к командной строке для установки контейнеров и знакомы с их базовым управлением. + +### Сборка образа самостоятельно + +У нас есть подробное руководство по использованию SillyTavern в Docker [здесь](http://docs.sillytavern.app/installation/docker/), которое охватывает установку на Windows, macOS и Linux! Ознакомьтесь с ним, если хотите создать образ самостоятельно. + +### Использование реестра контейнеров GitHub (самый простой способ) + +Для работы SillyTavern вам понадобятся две обязательные настройки каталогов и одна настройка порта. В команде замените указанные значения на свои: + +#### Переменные контейнера + +##### Маппинг томов + +* [config] - директория, где на вашем хосте будут храниться файлы конфигурации SillyTavern. +* [data] - директория, где на вашем хосте будут храниться пользовательские данные SillyTavern (включая персонажей). +* [plugins] - (необязательно) директория, где на вашем хосте будут храниться плагины сервера SillyTavern. + +##### Маппинг портов + +* [PublicPort] - Порт, через который будет передаваться трафик. Это обязательно, так как вы будете обращаться к контейнеру извне его виртуальной машины. НЕ ОТКРЫВАЙТЕ этот порт в интернет без реализации дополнительного уровня безопасности. + +##### Дополнительные настройки + +* [DockerNet] - Docker сеть, к которой контейнер должен быть подключен. Если вы не знаете, что это, обратитесь к [официальной документации Docker](https://docs.docker.com/reference/cli/docker/network/). +* [version] - на правой части этой страницы GitHub вы найдете раздел "Packages". Выберите пакет "sillytavern", чтобы увидеть версии образов. Тег "latest" позволит вам обновляться до текущего релиза. Также доступны теги "staging" и "release", которые соответствуют ночным сборкам соответствующих веток. Однако это может быть нецелесообразно, если вы используете расширения, которые могут ломаться и требуют времени для обновления. + +#### Команда установки + +1. Откройте командную строку +2. Выполните следующую команду + +`docker create --name='sillytavern' --net='[DockerNet]' -p '8000:8000/tcp' -v '[plugins]':'/home/node/app/plugins':'rw' -v '[config]':'/home/node/app/config':'rw' -v '[data]':'/home/node/app/data':'rw' 'ghcr.io/sillytavern/sillytavern:[version]'` + +> Заметьте, что 8000 является портом по умолчанию. Не забудьте использовать соответствующий порт, если вы измените его в конфиге. + ## 📱 Мобильные устройства - Установка при помощи termux > **ОБРАТИТЕ ВНИМАНИЕ!** From 69da293fb9cbe83e1e5d5c87a7ad1ffdfbff49fd Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Tue, 3 Dec 2024 23:27:39 +0200 Subject: [PATCH 2/8] Show used preset in itemization dialog --- public/script.js | 4 +++- public/scripts/templates/itemizationChat.html | 13 +++++++++++-- public/scripts/templates/itemizationText.html | 13 +++++++++++-- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/public/script.js b/public/script.js index 716c9f86f..6f34c2440 100644 --- a/public/script.js +++ b/public/script.js @@ -241,7 +241,7 @@ import { hideLoader, showLoader } from './scripts/loader.js'; import { BulkEditOverlay, CharacterContextMenu } from './scripts/BulkEditOverlay.js'; import { loadFeatherlessModels, loadMancerModels, loadOllamaModels, loadTogetherAIModels, loadInfermaticAIModels, loadOpenRouterModels, loadVllmModels, loadAphroditeModels, loadDreamGenModels, initTextGenModels, loadTabbyModels } from './scripts/textgen-models.js'; import { appendFileContent, hasPendingFileAttachment, populateFileAttachment, decodeStyleTags, encodeStyleTags, isExternalMediaAllowed, getCurrentEntityId, preserveNeutralChat, restoreNeutralChat } from './scripts/chats.js'; -import { initPresetManager } from './scripts/preset-manager.js'; +import { getPresetManager, initPresetManager } from './scripts/preset-manager.js'; import { MacrosParser, evaluateMacros, getLastMessageId, initMacros } from './scripts/macros.js'; import { currentUser, setUserControls } from './scripts/user.js'; import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup, fixToastrForDialogs } from './scripts/popup.js'; @@ -4494,6 +4494,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro instruction: main_api !== 'openai' && power_user.sysprompt.enabled ? substituteParams(power_user.prefer_character_prompt && system ? system : power_user.sysprompt.content) : '', userPersona: (power_user.persona_description_position == persona_description_positions.IN_PROMPT ? (persona || '') : ''), tokenizer: getFriendlyTokenizerName(main_api).tokenizerName || '', + presetName: getPresetManager()?.getSelectedPresetName() || '', }; //console.log(additionalPromptStuff); @@ -5160,6 +5161,7 @@ export async function itemizedParams(itemizedPrompts, thisPromptSet, incomingMes dataBankVectorsStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].dataBankVectorsString), modelUsed: chat[incomingMesId]?.extra?.model, apiUsed: chat[incomingMesId]?.extra?.api, + presetName: itemizedPrompts[thisPromptSet].presetName || t`(Unknown)`, }; const getFriendlyName = (value) => $(`#rm_api_block select option[value="${value}"]`).first().text() || value; diff --git a/public/scripts/templates/itemizationChat.html b/public/scripts/templates/itemizationChat.html index 038cdaf40..0da1f20cc 100644 --- a/public/scripts/templates/itemizationChat.html +++ b/public/scripts/templates/itemizationChat.html @@ -4,8 +4,17 @@ -API/Model Used: {{mainApiFriendlyName}} {{#if apiUsed}}({{apiUsed}}){{/if}} {{#if modelUsed}}– {{modelUsed}}{{/if}}
-Tokenizer: {{selectedTokenizer}}
+
+
+ API/Model: {{mainApiFriendlyName}} {{#if apiUsed}}({{apiUsed}}){{/if}} {{#if modelUsed}}– {{modelUsed}}{{/if}} +
+
+ Preset: {{presetName}} + | + Tokenizer: {{selectedTokenizer}} +
+
+ Only the white numbers really matter. All numbers are estimates. Grey color items may not have been included in the context due to certain prompt format settings. diff --git a/public/scripts/templates/itemizationText.html b/public/scripts/templates/itemizationText.html index 1fbf46959..3b9c31296 100644 --- a/public/scripts/templates/itemizationText.html +++ b/public/scripts/templates/itemizationText.html @@ -4,8 +4,17 @@ -API/Model Used: {{mainApiFriendlyName}} {{#if apiUsed}}({{apiUsed}}){{/if}} {{#if modelUsed}}– {{modelUsed}}{{/if}}
-Tokenizer: {{selectedTokenizer}}
+
+
+ API/Model: {{mainApiFriendlyName}} {{#if apiUsed}}({{apiUsed}}){{/if}} {{#if modelUsed}}– {{modelUsed}}{{/if}} +
+
+ Preset: {{presetName}} + | + Tokenizer: {{selectedTokenizer}} +
+
+ Only the white numbers really matter. All numbers are estimates. Grey color items may not have been included in the context due to certain prompt format settings. From 8de1d26eaa752d9fb5e8d89e2c7541fb25c30294 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 4 Dec 2024 00:00:08 +0200 Subject: [PATCH 3/8] NanoGPT: Unhide sampling parameters --- public/index.html | 10 +++++----- public/scripts/openai.js | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/public/index.html b/public/index.html index 743cb57f1..d8296c1fb 100644 --- a/public/index.html +++ b/public/index.html @@ -669,7 +669,7 @@ -
+
Temperature
@@ -682,7 +682,7 @@
-
+
Frequency Penalty
@@ -695,7 +695,7 @@
-
+
Presence Penalty
@@ -721,7 +721,7 @@
-
+
Top P
@@ -958,7 +958,7 @@
-
+
Seed
diff --git a/public/scripts/openai.js b/public/scripts/openai.js index 8e2dae2e2..e4ba08ae0 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -1812,6 +1812,7 @@ async function sendOpenAIRequest(type, messages, signal) { const isPerplexity = oai_settings.chat_completion_source == chat_completion_sources.PERPLEXITY; const isGroq = oai_settings.chat_completion_source == chat_completion_sources.GROQ; const is01AI = oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI; + const isNano = oai_settings.chat_completion_source == chat_completion_sources.NANOGPT; const isTextCompletion = isOAI && textCompletionModels.includes(oai_settings.openai_model); const isQuiet = type === 'quiet'; const isImpersonate = type === 'impersonate'; @@ -1971,7 +1972,7 @@ async function sendOpenAIRequest(type, messages, signal) { delete generate_data.stop; } - if ((isOAI || isOpenRouter || isMistral || isCustom || isCohere) && oai_settings.seed >= 0) { + if ((isOAI || isOpenRouter || isMistral || isCustom || isCohere || isNano) && oai_settings.seed >= 0) { generate_data['seed'] = oai_settings.seed; } From 79700fd983a3297407807848755e5a003886b165 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 4 Dec 2024 00:44:50 +0200 Subject: [PATCH 4/8] Add Kobold Lite chats import --- public/script.js | 54 +++++++++++--------- public/scripts/group-chats.js | 56 +++++++++++---------- src/endpoints/chats.js | 93 +++++++++++++++++++++++++---------- 3 files changed, 129 insertions(+), 74 deletions(-) diff --git a/public/script.js b/public/script.js index 6f34c2440..5205a27cd 100644 --- a/public/script.js +++ b/public/script.js @@ -7754,25 +7754,31 @@ export async function saveChatConditional() { } } -async function importCharacterChat(formData) { - await jQuery.ajax({ - type: 'POST', - url: '/api/chats/import', - data: formData, - beforeSend: function () { - }, - cache: false, - contentType: false, - processData: false, - success: async function (data) { - if (data.res) { - await displayPastChats(); - } - }, - error: function () { - $('#create_button').removeAttr('disabled'); - }, +/** + * Saves the chat to the server. + * @param {FormData} formData Form data to send to the server. + * @param {EventTarget} eventTarget Event target to trigger the event on. + */ +async function importCharacterChat(formData, eventTarget) { + const headers = getRequestHeaders(); + delete headers['Content-Type']; + const fetchResult = await fetch('/api/chats/import', { + method: 'POST', + body: formData, + headers: headers, + cache: 'no-cache', }); + + if (fetchResult.ok) { + const data = await fetchResult.json(); + if (data.res) { + await displayPastChats(); + } + } + + if (eventTarget instanceof HTMLInputElement) { + eventTarget.value = ''; + } } function updateViewMessageIds(startFromZero = false) { @@ -10829,13 +10835,13 @@ jQuery(async function () { }); $('#chat_import_file').on('change', async function (e) { - var file = e.target.files[0]; + const file = e.target.files[0]; if (!file) { return; } - var ext = file.name.match(/\.(\w+)$/); + const ext = file.name.match(/\.(\w+)$/); if ( !ext || (ext[1].toLowerCase() != 'json' && ext[1].toLowerCase() != 'jsonl') @@ -10848,17 +10854,17 @@ jQuery(async function () { return; } - var format = ext[1].toLowerCase(); + const format = ext[1].toLowerCase(); $('#chat_import_file_type').val(format); - var formData = new FormData($('#form_import_chat').get(0)); + const formData = new FormData($('#form_import_chat').get(0)); formData.append('user_name', name1); $('#select_chat_div').html(''); if (selected_group) { - await importGroupChat(formData); + await importGroupChat(formData, e.originalEvent.target); } else { - await importCharacterChat(formData); + await importCharacterChat(formData, e.originalEvent.target); } }); diff --git a/public/scripts/group-chats.js b/public/scripts/group-chats.js index a74ef0d72..0195b1d8d 100644 --- a/public/scripts/group-chats.js +++ b/public/scripts/group-chats.js @@ -1863,32 +1863,38 @@ export async function deleteGroupChat(groupId, chatId) { } } -export async function importGroupChat(formData) { - await jQuery.ajax({ - type: 'POST', - url: '/api/chats/group/import', - data: formData, - beforeSend: function () { - }, - cache: false, - contentType: false, - processData: false, - success: async function (data) { - if (data.res) { - const chatId = data.res; - const group = groups.find(x => x.id == selected_group); - - if (group) { - group.chats.push(chatId); - await editGroup(selected_group, true, true); - await displayPastChats(); - } - } - }, - error: function () { - $('#create_button').removeAttr('disabled'); - }, +/** + * Imports a group chat from a file and adds it to the group. + * @param {FormData} formData Form data to send to the server + * @param {EventTarget} eventTarget Element that triggered the import + */ +export async function importGroupChat(formData, eventTarget) { + const headers = getRequestHeaders(); + delete headers['Content-Type']; + const fetchResult = await fetch('/api/chats/group/import', { + method: 'POST', + headers: headers, + body: formData, + cache: 'no-cache', }); + + if (fetchResult.ok) { + const data = await fetchResult.json(); + if (data.res) { + const chatId = data.res; + const group = groups.find(x => x.id == selected_group); + + if (group) { + group.chats.push(chatId); + await editGroup(selected_group, true, true); + await displayPastChats(); + } + } + } + + if (eventTarget instanceof HTMLInputElement) { + eventTarget.value = ''; + } } export async function saveGroupBookmarkChat(groupId, name, metadata, mesId) { diff --git a/src/endpoints/chats.js b/src/endpoints/chats.js index aec0ba6e4..dd12adf5b 100644 --- a/src/endpoints/chats.js +++ b/src/endpoints/chats.js @@ -190,6 +190,44 @@ function importCAIChat(userName, characterName, jsonData) { return newChats; } +/** + * Imports a chat from Kobold Lite format. + * @param {string} _userName User name + * @param {string} _characterName Character name + * @param {object} data JSON data + * @returns {string} Chat data + */ +function importKoboldLiteChat(_userName, _characterName, data) { + const inputToken = '{{[INPUT]}}'; + const outputToken = '{{[OUTPUT]}}'; + + /** @type {function(string): object} */ + function processKoboldMessage(msg) { + const isUser = msg.includes(inputToken) || msg.includes(outputToken); + return { + name: isUser ? header.user_name : header.character_name, + is_user: isUser, + mes: msg.replace(inputToken, '').replace(outputToken, '').trim(), + send_date: Date.now(), + }; + } + + // Create the header + const header = { + user_name: data.savedsettings.chatname, + character_name: data.savedsettings.chatopponent, + }; + // Format messages + const formattedMessages = data.actions.map(processKoboldMessage); + // Add prompt if available + if (data.prompt) { + formattedMessages.unshift(processKoboldMessage(data.prompt)); + } + // Combine header and messages + const chatData = [header, ...formattedMessages]; + return chatData.map(obj => JSON.stringify(obj)).join('\n'); +} + /** * Flattens `msg` and `swipes` data from Chub Chat format. * Only changes enough to make it compatible with the standard chat serialization format. @@ -413,7 +451,7 @@ router.post('/import', urlencodedParser, function (request, response) { const format = request.body.file_type; const avatarUrl = (request.body.avatar_url).replace('.png', ''); const characterName = request.body.character_name; - const userName = request.body.user_name || 'You'; + const userName = request.body.user_name || 'User'; if (!request.file) { return response.sendStatus(400); @@ -426,33 +464,38 @@ router.post('/import', urlencodedParser, function (request, response) { if (format === 'json') { fs.unlinkSync(pathToUpload); const jsonData = JSON.parse(data); - if (jsonData.histories !== undefined) { - // CAI Tools format - const chats = importCAIChat(userName, characterName, jsonData); - for (const chat of chats) { - const fileName = `${characterName} - ${humanizedISO8601DateTime()} imported.jsonl`; - const filePath = path.join(request.user.directories.chats, avatarUrl, fileName); - writeFileAtomicSync(filePath, chat, 'utf8'); - } - return response.send({ res: true }); - } else if (Array.isArray(jsonData.data_visible)) { - // oobabooga's format - const chat = importOobaChat(userName, characterName, jsonData); - const fileName = `${characterName} - ${humanizedISO8601DateTime()} imported.jsonl`; - const filePath = path.join(request.user.directories.chats, avatarUrl, fileName); - writeFileAtomicSync(filePath, chat, 'utf8'); - return response.send({ res: true }); - } else if (Array.isArray(jsonData.messages)) { - // Agnai format - const chat = importAgnaiChat(userName, characterName, jsonData); - const fileName = `${characterName} - ${humanizedISO8601DateTime()} imported.jsonl`; - const filePath = path.join(request.user.directories.chats, avatarUrl, fileName); - writeFileAtomicSync(filePath, chat, 'utf8'); - return response.send({ res: true }); - } else { + + /** @type {function(string, string, object): string|string[]} */ + let importFunc; + + if (jsonData.savedsettings !== undefined) { // Kobold Lite format + importFunc = importKoboldLiteChat; + } else if (jsonData.histories !== undefined) { // CAI Tools format + importFunc = importCAIChat; + } else if (Array.isArray(jsonData.data_visible)) { // oobabooga's format + importFunc = importOobaChat; + } else if (Array.isArray(jsonData.messages)) { // Agnai's format + importFunc = importAgnaiChat; + } else { // Unknown format console.log('Incorrect chat format .json'); return response.send({ error: true }); } + + const handleChat = (chat) => { + const fileName = `${characterName} - ${humanizedISO8601DateTime()} imported.jsonl`; + const filePath = path.join(request.user.directories.chats, avatarUrl, fileName); + writeFileAtomicSync(filePath, chat, 'utf8'); + }; + + const chat = importFunc(userName, characterName, jsonData); + + if (Array.isArray(chat)) { + chat.forEach(handleChat); + } else { + handleChat(chat); + } + + return response.send({ res: true }); } if (format === 'jsonl') { From 8ef49b40b230a50b70a92d1eee245f76978efbb9 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 4 Dec 2024 01:05:14 +0200 Subject: [PATCH 5/8] Add error handling to group parsing in chat search --- src/endpoints/chats.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/endpoints/chats.js b/src/endpoints/chats.js index aec0ba6e4..95bdfaa17 100644 --- a/src/endpoints/chats.js +++ b/src/endpoints/chats.js @@ -561,10 +561,14 @@ router.post('/search', jsonParser, function (request, response) { let targetGroup; for (const groupFile of groupFiles) { - const groupData = JSON.parse(fs.readFileSync(path.join(groupDir, groupFile), 'utf8')); - if (groupData.id === group_id) { - targetGroup = groupData; - break; + try { + const groupData = JSON.parse(fs.readFileSync(path.join(groupDir, groupFile), 'utf8')); + if (groupData.id === group_id) { + targetGroup = groupData; + break; + } + } catch (error) { + console.error(groupFile, 'group file is corrupted:', error); } } From e6be28acea70ea6899f415d51aa8d78848909206 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 4 Dec 2024 01:32:27 +0200 Subject: [PATCH 6/8] llama.cpp: Don't send empty DRY sequence breakers Fixes #3048 --- public/scripts/textgen-settings.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/public/scripts/textgen-settings.js b/public/scripts/textgen-settings.js index 0dfae364f..27b32aa6c 100644 --- a/public/scripts/textgen-settings.js +++ b/public/scripts/textgen-settings.js @@ -1339,6 +1339,9 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate, 'dry_sequence_breakers': sequenceBreakers, }; params = Object.assign(params, llamaCppParams); + if (!Array.isArray(sequenceBreakers) || sequenceBreakers.length === 0) { + delete params.dry_sequence_breakers; + } } eventSource.emitAndWait(event_types.TEXT_COMPLETION_SETTINGS_READY, params); From e9fc488661571f67e38fc7e8f6499eed836c6e7e Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 4 Dec 2024 12:53:34 +0000 Subject: [PATCH 7/8] Properly check for -0 --- public/scripts/slash-commands.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index a5ef925d9..bf54f55e5 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -3025,7 +3025,7 @@ async function sendUserMessageCallback(args, text) { let insertAt = Number(args?.at); // Convert possible depth parameter to index - if (!isNaN(insertAt) && (insertAt < 0 || insertAt === Number(-0))) { + if (!isNaN(insertAt) && (insertAt < 0 || Object.is(insertAt, -0))) { // Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7) insertAt = chat.length + insertAt; } @@ -3399,7 +3399,7 @@ export async function sendMessageAs(args, text) { let insertAt = Number(args.at); // Convert possible depth parameter to index - if (!isNaN(insertAt) && (insertAt < 0 || insertAt === Number(-0))) { + if (!isNaN(insertAt) && (insertAt < 0 || Object.is(insertAt, -0))) { // Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7) insertAt = chat.length + insertAt; } @@ -3453,7 +3453,7 @@ export async function sendNarratorMessage(args, text) { let insertAt = Number(args.at); // Convert possible depth parameter to index - if (!isNaN(insertAt) && (insertAt < 0 || insertAt === Number(-0))) { + if (!isNaN(insertAt) && (insertAt < 0 || Object.is(insertAt, -0))) { // Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7) insertAt = chat.length + insertAt; } @@ -3542,7 +3542,7 @@ async function sendCommentMessage(args, text) { let insertAt = Number(args.at); // Convert possible depth parameter to index - if (!isNaN(insertAt) && (insertAt < 0 || insertAt === Number(-0))) { + if (!isNaN(insertAt) && (insertAt < 0 || Object.is(insertAt, -0))) { // Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7) insertAt = chat.length + insertAt; } From 23e59a1189dda4a665a66c6ff290a3575fb4947b Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 4 Dec 2024 12:57:02 +0000 Subject: [PATCH 8/8] Document that -0 is supported --- public/scripts/slash-commands.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index bf54f55e5..a36cdce2e 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -269,7 +269,7 @@ export function initDefaultSlashCommands() { }), SlashCommandNamedArgument.fromProps({ name: 'at', - description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.', + description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.', typeList: [ARGUMENT_TYPE.NUMBER], enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), }), @@ -325,7 +325,7 @@ export function initDefaultSlashCommands() { ), SlashCommandNamedArgument.fromProps({ name: 'at', - description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.', + description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.', typeList: [ARGUMENT_TYPE.NUMBER], enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), }), @@ -388,7 +388,7 @@ export function initDefaultSlashCommands() { ), SlashCommandNamedArgument.fromProps({ name: 'at', - description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.', + description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.', typeList: [ARGUMENT_TYPE.NUMBER], enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), }), @@ -606,7 +606,7 @@ export function initDefaultSlashCommands() { ), SlashCommandNamedArgument.fromProps({ name: 'at', - description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.', + description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.', typeList: [ARGUMENT_TYPE.NUMBER], enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), }),