diff --git a/public/locales/fr-fr.json b/public/locales/fr-fr.json
index 22d163a65..8caacde2d 100644
--- a/public/locales/fr-fr.json
+++ b/public/locales/fr-fr.json
@@ -1385,8 +1385,8 @@
"enable_functions_desc_1": "Autorise l'utilisation",
"enable_functions_desc_2": "outils de fonction",
"enable_functions_desc_3": "Peut être utilisé par diverses extensions pour fournir des fonctionnalités supplémentaires.",
- "Show model reasoning": "Afficher les pensées du modèle",
- "Display the model's internal thoughts in the response.": "Afficher les pensées internes du modèle dans la réponse.",
+ "Request model reasoning": "Demander les pensées du modèle",
+ "Allows the model to return its thinking process.": "Permet au modèle de retourner son processus de réflexion.",
"Confirm token parsing with": "Confirmer l'analyse des tokens avec",
"openai_logit_bias_no_items": "Aucun élément",
"api_no_connection": "Pas de connection...",
diff --git a/public/locales/zh-cn.json b/public/locales/zh-cn.json
index d951491c5..d09e70f83 100644
--- a/public/locales/zh-cn.json
+++ b/public/locales/zh-cn.json
@@ -266,8 +266,8 @@
"Use system prompt": "使用系统提示词",
"Merges_all_system_messages_desc_1": "合并所有系统消息,直到第一条具有非系统角色的消息,然后通过",
"Merges_all_system_messages_desc_2": "字段发送。",
- "Show model reasoning": "展示思维链",
- "Display the model's internal thoughts in the response.": "展示模型在回复时的内部思维链。",
+ "Request model reasoning": "请求思维链",
+ "Allows the model to return its thinking process.": "允许模型返回其思维过程。",
"Assistant Prefill": "AI预填",
"Expand the editor": "展开编辑器",
"Start Claude's answer with...": "以如下内容开始Claude的回答...",
diff --git a/public/locales/zh-tw.json b/public/locales/zh-tw.json
index c8ec26b22..039748265 100644
--- a/public/locales/zh-tw.json
+++ b/public/locales/zh-tw.json
@@ -2357,8 +2357,8 @@
"Forbid": "禁止",
"Aphrodite only. Determines the order of samplers. Skew is always applied post-softmax, so it's not included here.": "僅限 Aphrodite 使用。決定採樣器的順序。偏移總是在 softmax 後應用,因此不包括在此。",
"Aphrodite only. Determines the order of samplers.": "僅限 Aphrodite 使用。決定採樣器的順序。",
- "Show model reasoning": "顯示模型思維鏈",
- "Display the model's internal thoughts in the response.": "在回應中顯示模型的思維鏈(內部思考過程)。",
+ "Request model reasoning": "請求模型思維鏈",
+ "Allows the model to return its thinking process.": "讓模型回傳其思考過程。",
"Generic (OpenAI-compatible) [LM Studio, LiteLLM, etc.]": "通用(兼容 OpenAI)[LM Studio, LiteLLM 等]",
"Model ID (optional)": "模型 ID(可選)",
"DeepSeek API Key": "DeepSeek API 金鑰",
diff --git a/public/script.js b/public/script.js
index 48bf112a6..4e037ac46 100644
--- a/public/script.js
+++ b/public/script.js
@@ -95,6 +95,7 @@ import {
resetMovableStyles,
forceCharacterEditorTokenize,
applyPowerUserSettings,
+ generatedTextFiltered,
} from './scripts/power-user.js';
import {
@@ -169,6 +170,7 @@ import {
toggleDrawer,
isElementInViewport,
copyText,
+ escapeHtml,
} from './scripts/utils.js';
import { debounce_timeout } from './scripts/constants.js';
@@ -1993,14 +1995,15 @@ export async function sendTextareaMessage() {
* @param {boolean} isUser If the message was sent by the user
* @param {number} messageId Message index in chat array
* @param {object} [sanitizerOverrides] DOMPurify sanitizer option overrides
+ * @param {boolean} [isReasoning] If the message is reasoning output
* @returns {string} HTML string
*/
-export function messageFormatting(mes, ch_name, isSystem, isUser, messageId, sanitizerOverrides = {}) {
+export function messageFormatting(mes, ch_name, isSystem, isUser, messageId, sanitizerOverrides = {}, isReasoning = false) {
if (!mes) {
return '';
}
- if (Number(messageId) === 0 && !isSystem && !isUser) {
+ if (Number(messageId) === 0 && !isSystem && !isUser && !isReasoning) {
const mesBeforeReplace = mes;
const chatMessage = chat[messageId];
mes = substituteParams(mes, undefined, ch_name);
@@ -2029,6 +2032,9 @@ export function messageFormatting(mes, ch_name, isSystem, isUser, messageId, san
if (!isSystem) {
function getRegexPlacement() {
try {
+ if (isReasoning) {
+ return regex_placement.REASONING;
+ }
if (isUser) {
return regex_placement.USER_INPUT;
} else if (chat[messageId]?.extra?.type === 'narrator') {
@@ -2062,6 +2068,17 @@ export function messageFormatting(mes, ch_name, isSystem, isUser, messageId, san
mes = mes.replaceAll('<', '<').replaceAll('>', '>');
}
+ // Make sure reasoning strings are always shown, even if they include "<" or ">"
+ [power_user.reasoning.prefix, power_user.reasoning.suffix].forEach((reasoningString) => {
+ if (!reasoningString || !reasoningString.trim().length) {
+ return;
+ }
+ // Only replace the first occurrence of the reasoning string
+ if (mes.includes(reasoningString)) {
+ mes = mes.replace(reasoningString, escapeHtml(reasoningString));
+ }
+ });
+
if (!isSystem) {
// Save double quotes in tags as a special character to prevent them from being encoded
if (!power_user.encode_tags) {
@@ -2250,8 +2267,8 @@ function getMessageFromTemplate({
export function updateMessageBlock(messageId, message) {
const messageElement = $(`#chat [mesid="${messageId}"]`);
const text = message?.extra?.display_text ?? message.mes;
- messageElement.find('.mes_text').html(messageFormatting(text, message.name, message.is_system, message.is_user, messageId));
- messageElement.find('.mes_reasoning').html(messageFormatting(message.extra?.reasoning ?? '', '', false, false, -1));
+ messageElement.find('.mes_text').html(messageFormatting(text, message.name, message.is_system, message.is_user, messageId, {}, false));
+ messageElement.find('.mes_reasoning').html(messageFormatting(message.extra?.reasoning ?? '', '', false, false, messageId, {}, true));
addCopyToCodeBlocks(messageElement);
appendMediaToMessage(message, messageElement);
}
@@ -2408,9 +2425,10 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
mes.is_user,
chat.indexOf(mes),
sanitizerOverrides,
+ false,
);
- const bias = messageFormatting(mes.extra?.bias ?? '', '', false, false, -1);
- const reasoning = messageFormatting(mes.extra?.reasoning ?? '', '', false, false, -1);
+ const bias = messageFormatting(mes.extra?.bias ?? '', '', false, false, -1, {}, false);
+ const reasoning = messageFormatting(mes.extra?.reasoning ?? '', '', false, false, chat.indexOf(mes), {}, true);
let bookmarkLink = mes?.extra?.bookmark_link ?? '';
let params = {
@@ -3057,7 +3075,7 @@ export function isStreamingEnabled() {
(main_api == 'openai' &&
oai_settings.stream_openai &&
!noStreamSources.includes(oai_settings.chat_completion_source) &&
- !(oai_settings.chat_completion_source == chat_completion_sources.OPENAI && oai_settings.openai_model.startsWith('o1-')) &&
+ !(oai_settings.chat_completion_source == chat_completion_sources.OPENAI && (oai_settings.openai_model.startsWith('o1') || oai_settings.openai_model.startsWith('o3'))) &&
!(oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE && oai_settings.google_model.includes('bison')))
|| (main_api == 'kobold' && kai_settings.streaming_kobold && kai_flags.can_use_streaming)
|| (main_api == 'novel' && nai_settings.streaming_novel)
@@ -3153,7 +3171,7 @@ class StreamingProcessor {
this.sendTextarea.dispatchEvent(new Event('input', { bubbles: true }));
}
else {
- await saveReply(this.type, text, true);
+ await saveReply(this.type, text, true, '', [], '');
messageId = chat.length - 1;
this.#checkDomElements(messageId);
this.showMessageButtons(messageId);
@@ -3203,9 +3221,9 @@ class StreamingProcessor {
}
if (this.reasoning) {
- chat[messageId]['extra']['reasoning'] = this.reasoning;
+ chat[messageId]['extra']['reasoning'] = power_user.trim_spaces ? this.reasoning.trim() : this.reasoning;
if (this.messageReasoningDom instanceof HTMLElement) {
- const formattedReasoning = messageFormatting(this.reasoning, '', false, false, -1);
+ const formattedReasoning = messageFormatting(this.reasoning, '', false, false, messageId, {}, true);
this.messageReasoningDom.innerHTML = formattedReasoning;
}
}
@@ -3232,6 +3250,8 @@ class StreamingProcessor {
chat[messageId].is_system,
chat[messageId].is_user,
messageId,
+ {},
+ false,
);
if (this.messageTextDom instanceof HTMLElement) {
this.messageTextDom.innerHTML = formattedText;
@@ -3283,39 +3303,11 @@ class StreamingProcessor {
unblockGeneration();
generatedPromptCache = '';
- //console.log("Generated text size:", text.length, text)
-
const isAborted = this.abortController.signal.aborted;
- if (power_user.auto_swipe && !isAborted) {
- function containsBlacklistedWords(str, blacklist, threshold) {
- const regex = new RegExp(`\\b(${blacklist.join('|')})\\b`, 'gi');
- const matches = str.match(regex) || [];
- return matches.length >= threshold;
- }
-
- const generatedTextFiltered = (text) => {
- if (text) {
- if (power_user.auto_swipe_minimum_length) {
- if (text.length < power_user.auto_swipe_minimum_length && text.length !== 0) {
- console.log('Generated text size too small');
- return true;
- }
- }
- if (power_user.auto_swipe_blacklist_threshold) {
- if (containsBlacklistedWords(text, power_user.auto_swipe_blacklist, power_user.auto_swipe_blacklist_threshold)) {
- console.log('Generated text has blacklisted words');
- return true;
- }
- }
- }
- return false;
- };
-
- if (generatedTextFiltered(text)) {
- swipe_right();
- return;
- }
+ if (!isAborted && power_user.auto_swipe && generatedTextFiltered(text)) {
+ return swipe_right();
}
+
playMessageSound();
}
@@ -3373,7 +3365,7 @@ class StreamingProcessor {
const timestamps = [];
for await (const { text, swipes, logprobs, toolCalls, state } of this.generator()) {
timestamps.push(Date.now());
- if (this.isStopped) {
+ if (this.isStopped || this.abortController.signal.aborted) {
return;
}
@@ -3383,7 +3375,7 @@ class StreamingProcessor {
if (logprobs) {
this.messageLogprobs.push(...(Array.isArray(logprobs) ? logprobs : [logprobs]));
}
- this.reasoning = state?.reasoning ?? '';
+ this.reasoning = getRegexedString(state?.reasoning ?? '', regex_placement.REASONING);
await eventSource.emit(event_types.STREAM_TOKEN_RECEIVED, text);
await sw.tick(() => this.onProgressStreaming(this.messageId, this.continueMessage + text));
}
@@ -3850,14 +3842,6 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
coreChat.pop();
}
- const reasoning = new PromptReasoning();
- for (let i = coreChat.length - 1; i >= 0; i--) {
- if (reasoning.isLimitReached()) {
- break;
- }
- coreChat[i] = { ...coreChat[i], mes: reasoning.addToMessage(coreChat[i].mes, coreChat[i].extra?.reasoning) };
- }
-
coreChat = await Promise.all(coreChat.map(async (chatItem, index) => {
let message = chatItem.mes;
let regexType = chatItem.is_user ? regex_placement.USER_INPUT : regex_placement.AI_OUTPUT;
@@ -3877,6 +3861,27 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
};
}));
+ const reasoning = new PromptReasoning();
+ for (let i = coreChat.length - 1; i >= 0; i--) {
+ const depth = coreChat.length - i - 1;
+ const isPrefix = isContinue && i === coreChat.length - 1;
+ coreChat[i] = {
+ ...coreChat[i],
+ mes: reasoning.addToMessage(
+ coreChat[i].mes,
+ getRegexedString(
+ String(coreChat[i].extra?.reasoning ?? ''),
+ regex_placement.REASONING,
+ { isPrompt: true, depth: depth },
+ ),
+ isPrefix,
+ ),
+ };
+ if (reasoning.isLimitReached()) {
+ break;
+ }
+ }
+
// Determine token limit
let this_max_context = getMaxContextSize();
@@ -4462,7 +4467,13 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
// TODO: Make all extension prompts use an array/splice method
const lengthDiff = mesSend.length - cfgPrompt.depth;
const cfgDepth = lengthDiff >= 0 ? lengthDiff : 0;
- finalMesSend[cfgDepth].extensionPrompts.push(`${cfgPrompt.value}\n`);
+ const cfgMessage = finalMesSend[cfgDepth];
+ if (cfgMessage) {
+ if (!Array.isArray(finalMesSend[cfgDepth].extensionPrompts)) {
+ finalMesSend[cfgDepth].extensionPrompts = [];
+ }
+ finalMesSend[cfgDepth].extensionPrompts.push(`${cfgPrompt.value}\n`);
+ }
}
}
}
@@ -4785,6 +4796,11 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
const swipes = extractMultiSwipes(data, type);
messageChunk = cleanUpMessage(getMessage, isImpersonate, isContinue, false);
+ reasoning = getRegexedString(reasoning, regex_placement.REASONING);
+
+ if (power_user.trim_spaces) {
+ reasoning = reasoning.trim();
+ }
if (isContinue) {
getMessage = continue_mag + getMessage;
@@ -4843,32 +4859,9 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
}
const isAborted = abortController && abortController.signal.aborted;
- if (power_user.auto_swipe && !isAborted) {
- console.debug('checking for autoswipeblacklist on non-streaming message');
- function containsBlacklistedWords(getMessage, blacklist, threshold) {
- console.debug('checking blacklisted words');
- const regex = new RegExp(`\\b(${blacklist.join('|')})\\b`, 'gi');
- const matches = getMessage.match(regex) || [];
- return matches.length >= threshold;
- }
-
- const generatedTextFiltered = (getMessage) => {
- if (power_user.auto_swipe_blacklist_threshold) {
- if (containsBlacklistedWords(getMessage, power_user.auto_swipe_blacklist, power_user.auto_swipe_blacklist_threshold)) {
- console.debug('Generated text has blacklisted words');
- return true;
- }
- }
-
- return false;
- };
- if (generatedTextFiltered(getMessage)) {
- console.debug('swiping right automatically');
- is_send_press = false;
- swipe_right();
- // TODO: do we want to resolve after an auto-swipe?
- return;
- }
+ if (!isAborted && power_user.auto_swipe && generatedTextFiltered(getMessage)) {
+ is_send_press = false;
+ return swipe_right();
}
console.debug('/api/chats/save called by /Generate');
@@ -5719,15 +5712,26 @@ function extractMessageFromData(data) {
* @returns {string} Extracted reasoning
*/
function extractReasoningFromData(data) {
- if (main_api === 'openai' && oai_settings.show_thoughts) {
- switch (oai_settings.chat_completion_source) {
- case chat_completion_sources.DEEPSEEK:
- return data?.choices?.[0]?.message?.reasoning_content ?? '';
- case chat_completion_sources.OPENROUTER:
- return data?.choices?.[0]?.message?.reasoning ?? '';
- case chat_completion_sources.MAKERSUITE:
- return data?.responseContent?.parts?.filter(part => part.thought)?.map(part => part.text)?.join('\n\n') ?? '';
- }
+ switch (main_api) {
+ case 'textgenerationwebui':
+ switch (textgen_settings.type) {
+ case textgen_types.OPENROUTER:
+ return data?.choices?.[0]?.reasoning ?? '';
+ }
+ break;
+
+ case 'openai':
+ if (!oai_settings.show_thoughts) break;
+
+ switch (oai_settings.chat_completion_source) {
+ case chat_completion_sources.DEEPSEEK:
+ return data?.choices?.[0]?.message?.reasoning_content ?? '';
+ case chat_completion_sources.OPENROUTER:
+ return data?.choices?.[0]?.message?.reasoning ?? '';
+ case chat_completion_sources.MAKERSUITE:
+ return data?.responseContent?.parts?.filter(part => part.thought)?.map(part => part.text)?.join('\n\n') ?? '';
+ }
+ break;
}
return '';
@@ -5923,6 +5927,15 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes,
chat[chat.length - 1]['extra'] = {};
}
+ // Coerce null/undefined to empty string
+ if (!chat[chat.length - 1]['extra']['reasoning']) {
+ chat[chat.length - 1]['extra']['reasoning'] = '';
+ }
+
+ if (!reasoning) {
+ reasoning = '';
+ }
+
let oldMessage = '';
const generationFinished = new Date();
const img = extractImageFromMessage(getMessage);
@@ -7190,9 +7203,11 @@ function messageEditAuto(div) {
mes.is_system,
mes.is_user,
this_edit_mes_id,
+ {},
+ false,
));
mesBlock.find('.mes_bias').empty();
- mesBlock.find('.mes_bias').append(messageFormatting(bias, '', false, false, -1));
+ mesBlock.find('.mes_bias').append(messageFormatting(bias, '', false, false, -1, {}, false));
saveChatDebounced();
}
@@ -7214,10 +7229,12 @@ async function messageEditDone(div) {
mes.is_system,
mes.is_user,
this_edit_mes_id,
+ {},
+ false,
),
);
mesBlock.find('.mes_bias').empty();
- mesBlock.find('.mes_bias').append(messageFormatting(bias, '', false, false, -1));
+ mesBlock.find('.mes_bias').append(messageFormatting(bias, '', false, false, -1, {}, false));
appendMediaToMessage(mes, div.closest('.mes'));
addCopyToCodeBlocks(div.closest('.mes'));
@@ -8721,11 +8738,6 @@ const swipe_right = () => {
easing: animation_easing,
queue: false,
complete: async function () {
- /*if (!selected_group) {
- var typingIndicator = $("#typing_indicator_template .typing_indicator").clone();
- typingIndicator.find(".typing_indicator_name").text(characters[this_chid].name);
- } */
- /* $("#chat").append(typingIndicator); */
const is_animation_scroll = ($('#chat').scrollTop() >= ($('#chat').prop('scrollHeight') - $('#chat').outerHeight()) - 10);
//console.log(parseInt(chat[chat.length-1]['swipe_id']));
//console.log(chat[chat.length-1]['swipes'].length);
@@ -10860,6 +10872,8 @@ jQuery(async function () {
chat[this_edit_mes_id].is_system,
chat[this_edit_mes_id].is_user,
this_edit_mes_id,
+ {},
+ false,
));
appendMediaToMessage(chat[this_edit_mes_id], $(this).closest('.mes'));
addCopyToCodeBlocks($(this).closest('.mes'));
diff --git a/public/scripts/authors-note.js b/public/scripts/authors-note.js
index 2bf0b254d..326de966a 100644
--- a/public/scripts/authors-note.js
+++ b/public/scripts/authors-note.js
@@ -566,7 +566,7 @@ export function initAuthorsNote() {
namedArgumentList: [],
unnamedArgumentList: [
new SlashCommandArgument(
- 'position', [ARGUMENT_TYPE.STRING], false, false, null, ['system', 'user', 'assistant'],
+ 'role', [ARGUMENT_TYPE.STRING], false, false, null, ['system', 'user', 'assistant'],
),
],
helpString: `
diff --git a/public/scripts/backgrounds.js b/public/scripts/backgrounds.js
index 683e9c4ee..3deef929a 100644
--- a/public/scripts/backgrounds.js
+++ b/public/scripts/backgrounds.js
@@ -96,8 +96,13 @@ function highlightLockedBackground() {
});
}
+/**
+ * Locks the background for the current chat
+ * @param {Event} e Click event
+ * @returns {string} Empty string
+ */
function onLockBackgroundClick(e) {
- e.stopPropagation();
+ e?.stopPropagation();
const chatName = getCurrentChatId();
@@ -106,7 +111,7 @@ function onLockBackgroundClick(e) {
return '';
}
- const relativeBgImage = getUrlParameter(this);
+ const relativeBgImage = getUrlParameter(this) ?? background_settings.url;
saveBackgroundMetadata(relativeBgImage);
setCustomBackground();
@@ -114,8 +119,13 @@ function onLockBackgroundClick(e) {
return '';
}
+/**
+ * Locks the background for the current chat
+ * @param {Event} e Click event
+ * @returns {string} Empty string
+ */
function onUnlockBackgroundClick(e) {
- e.stopPropagation();
+ e?.stopPropagation();
removeBackgroundMetadata();
unsetCustomBackground();
highlightLockedBackground();
@@ -513,12 +523,12 @@ export function initBackgrounds() {
$('#add_bg_button').on('change', onBackgroundUploadSelected);
$('#bg-filter').on('input', onBackgroundFilterInput);
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'lockbg',
- callback: onLockBackgroundClick,
+ callback: () => onLockBackgroundClick(new CustomEvent('click')),
aliases: ['bglock'],
helpString: 'Locks a background for the currently selected chat',
}));
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'unlockbg',
- callback: onUnlockBackgroundClick,
+ callback: () => onUnlockBackgroundClick(new CustomEvent('click')),
aliases: ['bgunlock'],
helpString: 'Unlocks a background for the currently selected chat',
}));
diff --git a/public/scripts/extensions/connection-manager/index.js b/public/scripts/extensions/connection-manager/index.js
index e9d0dd2fe..f31e4b495 100644
--- a/public/scripts/extensions/connection-manager/index.js
+++ b/public/scripts/extensions/connection-manager/index.js
@@ -30,6 +30,7 @@ const CC_COMMANDS = [
'api-url',
'model',
'proxy',
+ 'stop-strings',
];
const TC_COMMANDS = [
@@ -43,6 +44,7 @@ const TC_COMMANDS = [
'context',
'instruct-state',
'tokenizer',
+ 'stop-strings',
];
const FANCY_NAMES = {
@@ -57,6 +59,7 @@ const FANCY_NAMES = {
'instruct': 'Instruct Template',
'context': 'Context Template',
'tokenizer': 'Tokenizer',
+ 'stop-strings': 'Custom Stopping Strings',
};
/**
@@ -138,6 +141,7 @@ const profilesProvider = () => [
* @property {string} [context] Context Template
* @property {string} [instruct-state] Instruct Mode
* @property {string} [tokenizer] Tokenizer
+ * @property {string} [stop-strings] Custom Stopping Strings
* @property {string[]} [exclude] Commands to exclude
*/
diff --git a/public/scripts/extensions/expressions/index.js b/public/scripts/extensions/expressions/index.js
index e5ba1ed21..d53b5950a 100644
--- a/public/scripts/extensions/expressions/index.js
+++ b/public/scripts/extensions/expressions/index.js
@@ -2178,7 +2178,6 @@ function migrateSettings() {
typeList: [ARGUMENT_TYPE.STRING],
isRequired: true,
enumProvider: commonEnumProviders.characters('character'),
- forceEnum: true,
}),
],
helpString: 'Returns the last set sprite / expression for the named character.',
diff --git a/public/scripts/extensions/gallery/index.js b/public/scripts/extensions/gallery/index.js
index c34ea56e3..fd8651bca 100644
--- a/public/scripts/extensions/gallery/index.js
+++ b/public/scripts/extensions/gallery/index.js
@@ -441,7 +441,6 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
description: 'character name',
typeList: [ARGUMENT_TYPE.STRING],
enumProvider: commonEnumProviders.characters('character'),
- forceEnum: true,
}),
SlashCommandNamedArgument.fromProps({
name: 'group',
diff --git a/public/scripts/extensions/quick-reply/src/SlashCommandHandler.js b/public/scripts/extensions/quick-reply/src/SlashCommandHandler.js
index 15f7524a4..b131e9668 100644
--- a/public/scripts/extensions/quick-reply/src/SlashCommandHandler.js
+++ b/public/scripts/extensions/quick-reply/src/SlashCommandHandler.js
@@ -883,6 +883,10 @@ export class SlashCommandHandler {
}
}
getQuickReply(args) {
+ if (!args.id && !args.label) {
+ toastr.error('Please provide a valid id or label.');
+ return '';
+ }
try {
return JSON.stringify(this.api.getQrByLabel(args.set, args.id !== undefined ? Number(args.id) : args.label));
} catch (ex) {
diff --git a/public/scripts/extensions/regex/editor.html b/public/scripts/extensions/regex/editor.html
index 9e699a622..8cfe9e008 100644
--- a/public/scripts/extensions/regex/editor.html
+++ b/public/scripts/extensions/regex/editor.html
@@ -94,6 +94,12 @@
World Info