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 @@
549549 "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.22.12.tgz",
550550 "integrity": "sha512-xcmww1O/JFP2MrlGUMd3Q78S3Qu6W3mYTXYuIqFq33EorgYHV/HqymHfXy9GjiCJ7OI+7lWx6nYFOzU7M4rd1Q==",
551551 "license": "MIT",
552- "peer": true,
553552 "dependencies": {
554553 "@jimp/core": "^0.22.12"
555554 }
@@ -1011,7 +1010,6 @@
10111010 "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-1.6.0.tgz",
10121011 "integrity": "sha512-uSUD1mqXN9i1SGSz5ov3keRZ7S9L32/mAQG08wUwZiEi5FpbV0K8A8l1zkazAIZi9IJzLlTauRNU41Mi8IF9fA==",
10131012 "license": "MIT",
1014- "peer": true,
10151013 "dependencies": {
10161014 "@jimp/core": "1.6.0",
10171015 "@jimp/types": "1.6.0",
@@ -1043,7 +1041,6 @@
10431041 "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.22.12.tgz",
10441042 "integrity": "sha512-dghs92qM6MhHj0HrV2qAwKPMklQtjNpoYgAB94ysYpsXslhRTiPisueSIELRwZGEr0J0VUxpUY7HgJwlSIgGZw==",
10451043 "license": "MIT",
1046- "peer": true,
10471044 "dependencies": {
10481045 "@jimp/utils": "^0.22.12"
10491046 },
@@ -1139,7 +1136,6 @@
11391136 "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.22.12.tgz",
11401137 "integrity": "sha512-xslz2ZoFZOPLY8EZ4dC29m168BtDx95D6K80TzgUi8gqT7LY6CsajWO0FAxDwHz6h0eomHMfyGX0stspBrTKnQ==",
11411138 "license": "MIT",
1142- "peer": true,
11431139 "dependencies": {
11441140 "@jimp/utils": "^0.22.12"
11451141 },
@@ -1176,7 +1172,6 @@
11761172 "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.22.12.tgz",
11771173 "integrity": "sha512-xImhTE5BpS8xa+mAN6j4sMRWaUgUDLoaGHhJhpC+r7SKKErYDR0WQV4yCE4gP+N0gozD0F3Ka1LUSaMXrn7ZIA==",
11781174 "license": "MIT",
1179- "peer": true,
11801175 "dependencies": {
11811176 "@jimp/utils": "^0.22.12",
11821177 "tinycolor2": "^1.6.0"
@@ -1220,7 +1215,6 @@
12201215 "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.22.12.tgz",
12211216 "integrity": "sha512-FNuUN0OVzRCozx8XSgP9MyLGMxNHHJMFt+LJuFjn1mu3k0VQxrzqbN06yIl46TVejhyAhcq5gLzqmSCHvlcBVw==",
12221217 "license": "MIT",
1223- "peer": true,
12241218 "dependencies": {
12251219 "@jimp/utils": "^0.22.12"
12261220 },
@@ -1308,7 +1302,6 @@
13081302 "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.22.12.tgz",
13091303 "integrity": "sha512-3NyTPlPbTnGKDIbaBgQ3HbE6wXbAlFfxHVERmrbqAi8R3r6fQPxpCauA8UVDnieg5eo04D0T8nnnNIX//i/sXg==",
13101304 "license": "MIT",
1311- "peer": true,
13121305 "dependencies": {
13131306 "@jimp/utils": "^0.22.12"
13141307 },
@@ -1321,7 +1314,6 @@
13211314 "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.22.12.tgz",
13221315 "integrity": "sha512-9YNEt7BPAFfTls2FGfKBVgwwLUuKqy+E8bDGGEsOqHtbuhbshVGxN2WMZaD4gh5IDWvR+emmmPPWGgaYNYt1gA==",
13231316 "license": "MIT",
1324- "peer": true,
13251317 "dependencies": {
13261318 "@jimp/utils": "^0.22.12"
13271319 },
@@ -1922,7 +1914,6 @@
19221914 "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==",
19231915 "dev": true,
19241916 "license": "MIT",
1925- "peer": true,
19261917 "dependencies": {
19271918 "@types/body-parser": "*",
19281919 "@types/express-serve-static-core": "^4.17.33",
@@ -2612,7 +2603,6 @@
26122603 "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
26132604 "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
26142605 "license": "MIT",
2615- "peer": true,
26162606 "bin": {
26172607 "acorn": "bin/acorn"
26182608 },
@@ -3232,7 +3222,6 @@
32323222 }
32333223 ],
32343224 "license": "MIT",
3235- "peer": true,
32363225 "dependencies": {
32373226 "baseline-browser-mapping": "^2.9.0",
32383227 "caniuse-lite": "^1.0.30001759",
@@ -4567,7 +4556,6 @@
45674556 "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
45684557 "dev": true,
45694558 "license": "MIT",
4570- "peer": true,
45714559 "dependencies": {
45724560 "@eslint-community/eslint-utils": "^4.2.0",
45734561 "@eslint-community/regexpp": "^4.6.1",
@@ -8125,7 +8113,6 @@
81258113 "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
81268114 "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
81278115 "license": "MIT",
8128- "peer": true,
81298116 "dependencies": {
81308117 "fast-deep-equal": "^3.1.3",
81318118 "fast-uri": "^3.0.1",
public/scripts/macros.js+1 -0
@@ -653,6 +653,7 @@ export function evaluateMacros(content, env, postProcessFn) {
653653 { regex: /{{firstDisplayedMessageId}}/gi, replace: () => String(getFirstDisplayedMessageId() ?? '') },
654654 { regex: /{{lastSwipeId}}/gi, replace: () => String(getLastSwipeId() ?? '') },
655655 { regex: /{{currentSwipeId}}/gi, replace: () => String(getCurrentSwipeId() ?? '') },
656+ { regex: /{{allChatRange}}/gi, replace: () => chat.length === 0 ? '' : `0-${chat.length - 1}` },
656657 { regex: /{{reverse:(.+?)}}/gi, replace: (_, str) => Array.from(str).reverse().join('') },
657658 { regex: /\{\{\/\/([\s\S]*?)\}\}/gm, replace: () => '' },
658659 { regex: /{{time}}/gi, replace: () => moment().format('LT') },
public/scripts/macros/definitions/chat-macros.js+12 -0
@@ -66,6 +66,18 @@ export function registerChatMacros() {
6666 returnType: MacroValueType.INTEGER,
6767 handler: () => String(getCurrentSwipeId() ?? ''),
6868 });
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+ });
6981}
7082
7183function 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
1616import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
1717import { textgen_types, textgenerationwebui_settings } from './textgen-settings.js';
1818import { applyStreamFadeIn } from './util/stream-fadein.js';
1919import { copyText, escapeRegex, isFalseBoolean, isTrueBoolean, setDatasetProperty, stringToRange, trimSpaces } from './utils.js';
2020
2121/**
2222 * @typedef {object} ReasoningTemplate
@@ -1045,6 +1045,80 @@ function registerReasoningSlashCommands() {
10451045 </div>
10461046 `,
10471047 }));
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+ }));
10481122}
10491123
10501124function registerReasoningMacros() {