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>
Signed| @@ -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", |
| @@ -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') }, |
| @@ -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 | } |
| 70 | 82 | ||
| 71 | function getLastMessageId({ exclude_swipe_in_propress = true, filter = null } = {}) { | 83 | function getLastMessageId({ exclude_swipe_in_propress = true, filter = null } = {}) { |
| @@ -16,7 +16,7 @@ import { enumTypes, SlashCommandEnumValue } from './slash-commands/SlashCommandE | |||
| 16 | import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; | 16 | import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; |
| 17 | import { textgen_types, textgenerationwebui_settings } from './textgen-settings.js'; | 17 | import { textgen_types, textgenerationwebui_settings } from './textgen-settings.js'; |
| 18 | import { applyStreamFadeIn } from './util/stream-fadein.js'; | 18 | import { applyStreamFadeIn } from './util/stream-fadein.js'; |
| 19 | import { copyText, escapeRegex, isFalseBoolean, isTrueBoolean, setDatasetProperty, trimSpaces } from './utils.js'; | 19 | import { copyText, escapeRegex, isFalseBoolean, isTrueBoolean, setDatasetProperty, stringToRange, trimSpaces } from './utils.js'; |
| 20 | 20 | ||
| 21 | /** | 21 | /** |
| 22 | * @typedef {object} ReasoningTemplate | 22 | * @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 | } |
| 1049 | 1123 | ||
| 1050 | function registerReasoningMacros() { | 1124 | function registerReasoningMacros() { |