Add reasoning-collapse, reasoning-expand, reasoning-toggle slash commands with range support (#5296) * Initial plan * Add reasoning-collapse, reasoning-expand, reasoning-toggle slash commands Co-authored-by: Cohee1207 <18619528+Cohee1207@users.noreply.github.com> * Add message range support to reasoning commands and allChatRange macro Co-authored-by: Cohee1207 <18619528+Cohee1207@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Cohee1207 <18619528+Cohee1207@users.noreply.github.com>

2e476bc742829d5d3b9360f764dbb327f650331a

Copilot <198982749+Copilot@users.noreply.github.com>

Signed
4 files changed, +88 -14Ignore whitespace
package-lock.json+0 -13
@@ -549,7 +549,6 @@
549 "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.22.12.tgz",549 "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.22.12.tgz",
550 "integrity": "sha512-xcmww1O/JFP2MrlGUMd3Q78S3Qu6W3mYTXYuIqFq33EorgYHV/HqymHfXy9GjiCJ7OI+7lWx6nYFOzU7M4rd1Q==",550 "integrity": "sha512-xcmww1O/JFP2MrlGUMd3Q78S3Qu6W3mYTXYuIqFq33EorgYHV/HqymHfXy9GjiCJ7OI+7lWx6nYFOzU7M4rd1Q==",
551 "license": "MIT",551 "license": "MIT",
552 "peer": true,
553 "dependencies": {552 "dependencies": {
554 "@jimp/core": "^0.22.12"553 "@jimp/core": "^0.22.12"
555 }554 }
@@ -1011,7 +1010,6 @@
1011 "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-1.6.0.tgz",1010 "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-1.6.0.tgz",
1012 "integrity": "sha512-uSUD1mqXN9i1SGSz5ov3keRZ7S9L32/mAQG08wUwZiEi5FpbV0K8A8l1zkazAIZi9IJzLlTauRNU41Mi8IF9fA==",1011 "integrity": "sha512-uSUD1mqXN9i1SGSz5ov3keRZ7S9L32/mAQG08wUwZiEi5FpbV0K8A8l1zkazAIZi9IJzLlTauRNU41Mi8IF9fA==",
1013 "license": "MIT",1012 "license": "MIT",
1014 "peer": true,
1015 "dependencies": {1013 "dependencies": {
1016 "@jimp/core": "1.6.0",1014 "@jimp/core": "1.6.0",
1017 "@jimp/types": "1.6.0",1015 "@jimp/types": "1.6.0",
@@ -1043,7 +1041,6 @@
1043 "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.22.12.tgz",1041 "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.22.12.tgz",
1044 "integrity": "sha512-dghs92qM6MhHj0HrV2qAwKPMklQtjNpoYgAB94ysYpsXslhRTiPisueSIELRwZGEr0J0VUxpUY7HgJwlSIgGZw==",1042 "integrity": "sha512-dghs92qM6MhHj0HrV2qAwKPMklQtjNpoYgAB94ysYpsXslhRTiPisueSIELRwZGEr0J0VUxpUY7HgJwlSIgGZw==",
1045 "license": "MIT",1043 "license": "MIT",
1046 "peer": true,
1047 "dependencies": {1044 "dependencies": {
1048 "@jimp/utils": "^0.22.12"1045 "@jimp/utils": "^0.22.12"
1049 },1046 },
@@ -1139,7 +1136,6 @@
1139 "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.22.12.tgz",1136 "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.22.12.tgz",
1140 "integrity": "sha512-xslz2ZoFZOPLY8EZ4dC29m168BtDx95D6K80TzgUi8gqT7LY6CsajWO0FAxDwHz6h0eomHMfyGX0stspBrTKnQ==",1137 "integrity": "sha512-xslz2ZoFZOPLY8EZ4dC29m168BtDx95D6K80TzgUi8gqT7LY6CsajWO0FAxDwHz6h0eomHMfyGX0stspBrTKnQ==",
1141 "license": "MIT",1138 "license": "MIT",
1142 "peer": true,
1143 "dependencies": {1139 "dependencies": {
1144 "@jimp/utils": "^0.22.12"1140 "@jimp/utils": "^0.22.12"
1145 },1141 },
@@ -1176,7 +1172,6 @@
1176 "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.22.12.tgz",1172 "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.22.12.tgz",
1177 "integrity": "sha512-xImhTE5BpS8xa+mAN6j4sMRWaUgUDLoaGHhJhpC+r7SKKErYDR0WQV4yCE4gP+N0gozD0F3Ka1LUSaMXrn7ZIA==",1173 "integrity": "sha512-xImhTE5BpS8xa+mAN6j4sMRWaUgUDLoaGHhJhpC+r7SKKErYDR0WQV4yCE4gP+N0gozD0F3Ka1LUSaMXrn7ZIA==",
1178 "license": "MIT",1174 "license": "MIT",
1179 "peer": true,
1180 "dependencies": {1175 "dependencies": {
1181 "@jimp/utils": "^0.22.12",1176 "@jimp/utils": "^0.22.12",
1182 "tinycolor2": "^1.6.0"1177 "tinycolor2": "^1.6.0"
@@ -1220,7 +1215,6 @@
1220 "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.22.12.tgz",1215 "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.22.12.tgz",
1221 "integrity": "sha512-FNuUN0OVzRCozx8XSgP9MyLGMxNHHJMFt+LJuFjn1mu3k0VQxrzqbN06yIl46TVejhyAhcq5gLzqmSCHvlcBVw==",1216 "integrity": "sha512-FNuUN0OVzRCozx8XSgP9MyLGMxNHHJMFt+LJuFjn1mu3k0VQxrzqbN06yIl46TVejhyAhcq5gLzqmSCHvlcBVw==",
1222 "license": "MIT",1217 "license": "MIT",
1223 "peer": true,
1224 "dependencies": {1218 "dependencies": {
1225 "@jimp/utils": "^0.22.12"1219 "@jimp/utils": "^0.22.12"
1226 },1220 },
@@ -1308,7 +1302,6 @@
1308 "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.22.12.tgz",1302 "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.22.12.tgz",
1309 "integrity": "sha512-3NyTPlPbTnGKDIbaBgQ3HbE6wXbAlFfxHVERmrbqAi8R3r6fQPxpCauA8UVDnieg5eo04D0T8nnnNIX//i/sXg==",1303 "integrity": "sha512-3NyTPlPbTnGKDIbaBgQ3HbE6wXbAlFfxHVERmrbqAi8R3r6fQPxpCauA8UVDnieg5eo04D0T8nnnNIX//i/sXg==",
1310 "license": "MIT",1304 "license": "MIT",
1311 "peer": true,
1312 "dependencies": {1305 "dependencies": {
1313 "@jimp/utils": "^0.22.12"1306 "@jimp/utils": "^0.22.12"
1314 },1307 },
@@ -1321,7 +1314,6 @@
1321 "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.22.12.tgz",1314 "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.22.12.tgz",
1322 "integrity": "sha512-9YNEt7BPAFfTls2FGfKBVgwwLUuKqy+E8bDGGEsOqHtbuhbshVGxN2WMZaD4gh5IDWvR+emmmPPWGgaYNYt1gA==",1315 "integrity": "sha512-9YNEt7BPAFfTls2FGfKBVgwwLUuKqy+E8bDGGEsOqHtbuhbshVGxN2WMZaD4gh5IDWvR+emmmPPWGgaYNYt1gA==",
1323 "license": "MIT",1316 "license": "MIT",
1324 "peer": true,
1325 "dependencies": {1317 "dependencies": {
1326 "@jimp/utils": "^0.22.12"1318 "@jimp/utils": "^0.22.12"
1327 },1319 },
@@ -1922,7 +1914,6 @@
1922 "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==",1914 "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==",
1923 "dev": true,1915 "dev": true,
1924 "license": "MIT",1916 "license": "MIT",
1925 "peer": true,
1926 "dependencies": {1917 "dependencies": {
1927 "@types/body-parser": "*",1918 "@types/body-parser": "*",
1928 "@types/express-serve-static-core": "^4.17.33",1919 "@types/express-serve-static-core": "^4.17.33",
@@ -2612,7 +2603,6 @@
2612 "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",2603 "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
2613 "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",2604 "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
2614 "license": "MIT",2605 "license": "MIT",
2615 "peer": true,
2616 "bin": {2606 "bin": {
2617 "acorn": "bin/acorn"2607 "acorn": "bin/acorn"
2618 },2608 },
@@ -3232,7 +3222,6 @@
3232 }3222 }
3233 ],3223 ],
3234 "license": "MIT",3224 "license": "MIT",
3235 "peer": true,
3236 "dependencies": {3225 "dependencies": {
3237 "baseline-browser-mapping": "^2.9.0",3226 "baseline-browser-mapping": "^2.9.0",
3238 "caniuse-lite": "^1.0.30001759",3227 "caniuse-lite": "^1.0.30001759",
@@ -4567,7 +4556,6 @@
4567 "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",4556 "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
4568 "dev": true,4557 "dev": true,
4569 "license": "MIT",4558 "license": "MIT",
4570 "peer": true,
4571 "dependencies": {4559 "dependencies": {
4572 "@eslint-community/eslint-utils": "^4.2.0",4560 "@eslint-community/eslint-utils": "^4.2.0",
4573 "@eslint-community/regexpp": "^4.6.1",4561 "@eslint-community/regexpp": "^4.6.1",
@@ -8125,7 +8113,6 @@
8125 "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",8113 "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
8126 "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",8114 "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
8127 "license": "MIT",8115 "license": "MIT",
8128 "peer": true,
8129 "dependencies": {8116 "dependencies": {
8130 "fast-deep-equal": "^3.1.3",8117 "fast-deep-equal": "^3.1.3",
8131 "fast-uri": "^3.0.1",8118 "fast-uri": "^3.0.1",
public/scripts/macros.js+1 -0
@@ -653,6 +653,7 @@ export function evaluateMacros(content, env, postProcessFn) {
653 { regex: /{{firstDisplayedMessageId}}/gi, replace: () => String(getFirstDisplayedMessageId() ?? '') },653 { regex: /{{firstDisplayedMessageId}}/gi, replace: () => String(getFirstDisplayedMessageId() ?? '') },
654 { regex: /{{lastSwipeId}}/gi, replace: () => String(getLastSwipeId() ?? '') },654 { regex: /{{lastSwipeId}}/gi, replace: () => String(getLastSwipeId() ?? '') },
655 { regex: /{{currentSwipeId}}/gi, replace: () => String(getCurrentSwipeId() ?? '') },655 { regex: /{{currentSwipeId}}/gi, replace: () => String(getCurrentSwipeId() ?? '') },
656 { regex: /{{allChatRange}}/gi, replace: () => chat.length === 0 ? '' : `0-${chat.length - 1}` },
656 { regex: /{{reverse:(.+?)}}/gi, replace: (_, str) => Array.from(str).reverse().join('') },657 { regex: /{{reverse:(.+?)}}/gi, replace: (_, str) => Array.from(str).reverse().join('') },
657 { regex: /\{\{\/\/([\s\S]*?)\}\}/gm, replace: () => '' },658 { regex: /\{\{\/\/([\s\S]*?)\}\}/gm, replace: () => '' },
658 { regex: /{{time}}/gi, replace: () => moment().format('LT') },659 { regex: /{{time}}/gi, replace: () => moment().format('LT') },
public/scripts/macros/definitions/chat-macros.js+12 -0
@@ -66,6 +66,18 @@ export function registerChatMacros() {
66 returnType: MacroValueType.INTEGER,66 returnType: MacroValueType.INTEGER,
67 handler: () => String(getCurrentSwipeId() ?? ''),67 handler: () => String(getCurrentSwipeId() ?? ''),
68 });68 });
69
70 MacroRegistry.registerMacro('allChatRange', {
71 category: MacroCategory.CHAT,
72 description: 'Range of all message IDs in the chat (e.g. "0-10"). Empty string if the chat is empty.',
73 returns: 'Range string from 0 to last message ID, or empty string.',
74 handler: () => {
75 if (!Array.isArray(chat) || chat.length === 0) {
76 return '';
77 }
78 return `0-${chat.length - 1}`;
79 },
80 });
69}81}
7082
71function getLastMessageId({ exclude_swipe_in_propress = true, filter = null } = {}) {83function getLastMessageId({ exclude_swipe_in_propress = true, filter = null } = {}) {
public/scripts/reasoning.js+75 -1
@@ -16,7 +16,7 @@ import { enumTypes, SlashCommandEnumValue } from './slash-commands/SlashCommandE
16import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';16import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
17import { textgen_types, textgenerationwebui_settings } from './textgen-settings.js';17import { textgen_types, textgenerationwebui_settings } from './textgen-settings.js';
18import { applyStreamFadeIn } from './util/stream-fadein.js';18import { applyStreamFadeIn } from './util/stream-fadein.js';
19import { copyText, escapeRegex, isFalseBoolean, isTrueBoolean, setDatasetProperty, trimSpaces } from './utils.js';19import { copyText, escapeRegex, isFalseBoolean, isTrueBoolean, setDatasetProperty, stringToRange, trimSpaces } from './utils.js';
2020
21/**21/**
22 * @typedef {object} ReasoningTemplate22 * @typedef {object} ReasoningTemplate
@@ -1045,6 +1045,80 @@ function registerReasoningSlashCommands() {
1045 </div>1045 </div>
1046 `,1046 `,
1047 }));1047 }));
1048
1049 /**
1050 * Gets the reasoning details elements for a message range.
1051 * @param {string} value Unnamed argument value (message ID or range)
1052 * @returns {JQuery<HTMLElement>|null} The reasoning details elements, or null if not found
1053 */
1054 function getReasoningDetailsElements(value) {
1055 const range = value ? stringToRange(String(value), 0, chat.length - 1) : { start: chat.length - 1, end: chat.length - 1 };
1056 if (!range) {
1057 toastr.warning(t`Invalid message ID or range: ${value}`);
1058 return null;
1059 }
1060 const selector = Array.from({ length: range.end - range.start + 1 }, (_, i) =>
1061 `#chat [mesid="${range.start + i}"] .mes_reasoning_details`,
1062 ).join(',');
1063 const details = $(selector);
1064 if (details.length === 0) {
1065 toastr.warning(t`No reasoning blocks found for the specified messages.`);
1066 return null;
1067 }
1068 return details;
1069 }
1070
1071 const reasoningVisibilityArgs = [
1072 SlashCommandArgument.fromProps({
1073 description: 'Message ID or range (e.g. 0-10). If not provided, the last message is used.',
1074 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.RANGE],
1075 enumProvider: commonEnumProviders.messages(),
1076 }),
1077 ];
1078
1079 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1080 name: 'reasoning-collapse',
1081 aliases: ['collapse-reasoning'],
1082 helpString: t`Collapse the reasoning block of a message or range of messages.`,
1083 unnamedArgumentList: reasoningVisibilityArgs,
1084 callback: (_args, value) => {
1085 const details = getReasoningDetailsElements(value);
1086 if (details) details.removeAttr('open');
1087 return '';
1088 },
1089 }));
1090
1091 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1092 name: 'reasoning-expand',
1093 aliases: ['expand-reasoning'],
1094 helpString: t`Expand the reasoning block of a message or range of messages.`,
1095 unnamedArgumentList: reasoningVisibilityArgs,
1096 callback: (_args, value) => {
1097 const details = getReasoningDetailsElements(value);
1098 if (details) details.attr('open', '');
1099 return '';
1100 },
1101 }));
1102
1103 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1104 name: 'reasoning-toggle',
1105 aliases: ['toggle-reasoning'],
1106 helpString: t`Toggle the reasoning block of a message or range of messages. Expanded blocks will be collapsed, and collapsed blocks will be expanded.`,
1107 unnamedArgumentList: reasoningVisibilityArgs,
1108 callback: (_args, value) => {
1109 const details = getReasoningDetailsElements(value);
1110 if (!details) return '';
1111 details.each(function () {
1112 const $el = $(this);
1113 if ($el.attr('open') !== undefined) {
1114 $el.removeAttr('open');
1115 } else {
1116 $el.attr('open', '');
1117 }
1118 });
1119 return '';
1120 },
1121 }));
1048}1122}
10491123
1050function registerReasoningMacros() {1124function registerReasoningMacros() {