Merge pull request #3413 from SillyTavern/thinking-is-stylish Thinking is stylish - if you are not cool, I don't know how to help you

5494e89fdb71d0037c7024e57764e03a4cbadada

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
6 files changed, +282 -65Ignore whitespace
public/index.html+18 -7
@@ -3789,6 +3789,12 @@
37893789 Auto-Parse Reasoning
37903790 </small>
37913791 </label>
3792+ <label class="checkbox_label" for="reasoning_auto_expand" title="Automatically expand reasoning blocks." data-i18n="[title]reasoning_auto_expand">
3793+ <input id="reasoning_auto_expand" type="checkbox" />
3794+ <small data-i18n="Auto-Expand Reasoning">
3795+ Auto-Expand Reasoning
3796+ </small>
3797+ </label>
37923798 <label class="checkbox_label" for="reasoning_add_to_prompts" title="Add existing reasoning blocks to prompts. To add a new reasoning block, use the message edit menu." data-i18n="[title]reasoning_add_to_prompts">
37933799 <input id="reasoning_add_to_prompts" type="checkbox" />
37943800 <small data-i18n="Add Reasoning to Prompts">
@@ -6248,14 +6254,19 @@
62486254 </div>
62496255 </div>
62506256 <details class="mes_reasoning_details">
62516257 <summary class="mes_reasoning_summary flex-container">
6252- <span data-i18n="Reasoning">Reasoning</span>
6258+ <div class="mes_reasoning_header_block flex-container">
62536259 <div class="mes_reasoning_actionsmes_reasoning_header flex-container">
6254- <div class="mes_reasoning_edit_done mes_button fa-solid fa-check" title="Confirm" data-i18n="[title]Confirmedit"></div>
6260+ <span class="mes_reasoning_header_title" data-i18n="Thought for some time">Thought for some time</span>
62556261 <div class="mes_reasoning_edit_cancel mes_buttonmes_reasoning_arrow fa-solid fa-xmark" title="Cancel edit" datachevron-i18n="[title]Cancel editup"></div>
6256- <div class="mes_reasoning_edit mes_button fa-solid fa-pencil" title="Edit reasoning" data-i18n="[title]Edit reasoning"></div>
6262+ </div>
6263+ </div>
6264+ <div class="mes_reasoning_actions flex-container">
6265+ <div class="mes_reasoning_edit_done menu_button edit_button fa-solid fa-check" title="Confirm" data-i18n="[title]Confirmedit"></div>
6266+ <div class="mes_reasoning_delete menu_button edit_button fa-solid fa-trash-can" title="Remove reasoning" data-i18n="[title]Remove reasoning"></div>
6267+ <div class="mes_reasoning_edit_cancel menu_button edit_button fa-solid fa-xmark" title="Cancel edit" data-i18n="[title]Cancel edit"></div>
62576268 <div class="mes_reasoning_copy mes_button fa-solid fa-copy" title="Copy reasoning" data-i18n="[title]Copy reasoning"></div>
62586269 <div class="mes_reasoning_deletemes_reasoning_edit mes_button fa-solid fa-trash-canpencil" title="RemoveEdit reasoning" data-i18n="[title]RemoveEdit reasoning"></div>
62596270 </div>
62606271 </summary>
62616272 <div class="mes_reasoning"></div>
public/script.js+142 -25
@@ -225,7 +225,7 @@ import {
225225 instruct_presets,
226226 selectContextPreset,
227227} from './scripts/instruct-mode.js';
228228import { getCurrentLocale, initLocales, t } from './scripts/i18n.js';
229229import { getFriendlyTokenizerName, getTokenCount, getTokenCountAsync, initTokenizers, saveTokenCache, TOKENIZER_SUPPORTED_KEY } from './scripts/tokenizers.js';
230230import {
231231 user_avatar,
@@ -495,6 +495,7 @@ export const event_types = {
495495 /** @deprecated The event is aliased to STREAM_TOKEN_RECEIVED. */
496496 SMOOTH_STREAM_TOKEN_RECEIVED: 'stream_token_received',
497497 STREAM_TOKEN_RECEIVED: 'stream_token_received',
498+ STREAM_REASONING_DONE: 'stream_reasoning_done',
498499 FILE_ATTACHMENT_DELETED: 'file_attachment_deleted',
499500 WORLDINFO_FORCE_ACTIVATE: 'worldinfo_force_activate',
500501 OPEN_CHARACTER_LIBRARY: 'open_character_library',
@@ -2189,26 +2190,29 @@ function insertSVGIcon(mes, extra) {
21892190 modelName = extra.api;
21902191 }
21912192
2192- const image = new Image();
2193+ const insertOrReplaceSVG = (image, className, targetSelector, insertBefore) => {
2193- // Add classes for styling and identification
2194+ image.onload = async function () {
2194- image.classList.add('icon-svg', 'timestamp-icon');
2195+ let existingSVG = insertBefore ? mes.find(targetSelector).prev(`.${className}`) : mes.find(targetSelector).next(`.${className}`);
2195- image.src = `/img/${modelName}.svg`;
2196+ if (existingSVG.length) {
2196- image.title = `${extra?.api ? extra.api + ' - ' : ''}${extra?.model ?? ''}`;
2197+ existingSVG.replaceWith(image);
2197-
2198+ } else {
2198- image.onload = async function () {
2199+ if (insertBefore) mes.find(targetSelector).before(image);
2199- // Check if an SVG already exists adjacent to the timestamp
2200+ else mes.find(targetSelector).after(image);
2200- let existingSVG = mes.find('.timestamp').next('.timestamp-icon');
2201+ }
2201-
2202+ await SVGInject(image);
2202- if (existingSVG.length) {
2203+ };
2203- // Replace existing SVG
2204+ };
2204- existingSVG.replaceWith(image);
2205- } else {
2206- // Append the new SVG if none exists
2207- mes.find('.timestamp').after(image);
2208- }
22092205
2210- await SVGInject(image);
2206+ const createModelImage = (className, targetSelector, insertBefore) => {
2207+ const image = new Image();
2208+ image.classList.add('icon-svg', className);
2209+ image.src = `/img/${modelName}.svg`;
2210+ image.title = `${extra?.api ? extra.api + ' - ' : ''}${extra?.model ?? ''}`;
2211+ insertOrReplaceSVG(image, className, targetSelector, insertBefore);
22112212 };
2213+
2214+ createModelImage('timestamp-icon', '.timestamp');
2215+ createModelImage('thinking-icon', '.mes_reasoning_header_title', true);
22122216}
22132217
22142218
@@ -2220,6 +2224,7 @@ function getMessageFromTemplate({
22202224 avatarImg,
22212225 bias,
22222226 reasoning,
2227+ reasoningDuration,
22232228 isSystem,
22242229 title,
22252230 timerValue,
@@ -2252,6 +2257,13 @@ function getMessageFromTemplate({
22522257 timerValue && mes.find('.mes_timer').attr('title', timerTitle).text(timerValue);
22532258 bookmarkLink && updateBookmarkDisplay(mes);
22542259
2260+ if (reasoning) {
2261+ mes.addClass('reasoning');
2262+ }
2263+ if (reasoningDuration) {
2264+ updateReasoningTimeUI(mes.find('.mes_reasoning_header_title')[0], reasoningDuration, { forceEnd: true });
2265+ }
2266+
22552267 if (power_user.timestamp_model_icon && extra?.api) {
22562268 insertSVGIcon(mes, extra);
22572269 }
@@ -2269,6 +2281,7 @@ export function updateMessageBlock(messageId, message) {
22692281 const text = message?.extra?.display_text ?? message.mes;
22702282 messageElement.find('.mes_text').html(messageFormatting(text, message.name, message.is_system, message.is_user, messageId, {}, false));
22712283 messageElement.find('.mes_reasoning').html(messageFormatting(message.extra?.reasoning ?? '', '', false, false, messageId, {}, true));
2284+ messageElement.toggleClass('reasoning', !!message.extra?.reasoning);
22722285 addCopyToCodeBlocks(messageElement);
22732286 appendMediaToMessage(message, messageElement);
22742287}
@@ -2439,6 +2452,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
24392452 avatarImg: avatarImg,
24402453 bias: bias,
24412454 reasoning: reasoning,
2455+ reasoningDuration: mes.extra?.reasoning_duration,
24422456 isSystem: isSystem,
24432457 title: title,
24442458 bookmarkLink: bookmarkLink,
@@ -3108,8 +3122,12 @@ class StreamingProcessor {
31083122 this.messageDom = null;
31093123 this.messageTextDom = null;
31103124 this.messageTimerDom = null;
3125+ /** @type {HTMLElement} */
31113126 this.messageTokenCounterDom = null;
3127+ /** @type {HTMLElement} */
31123128 this.messageReasoningDom = null;
3129+ /** @type {HTMLElement} */
3130+ this.messageReasoningHeaderDom = null;
31133131 /** @type {HTMLTextAreaElement} */
31143132 this.sendTextarea = document.querySelector('#send_textarea');
31153133 this.type = type;
@@ -3126,6 +3144,15 @@ class StreamingProcessor {
31263144 this.messageLogprobs = [];
31273145 this.toolCalls = [];
31283146 this.reasoning = '';
3147+ this.reasoningStartTime = null;
3148+ this.reasoningEndTime = null;
3149+ }
3150+
3151+ #reasoningDuration() {
3152+ if (this.reasoningStartTime && this.reasoningEndTime) {
3153+ return (this.reasoningEndTime - this.reasoningStartTime);
3154+ }
3155+ return null;
31293156 }
31303157
31313158 #checkDomElements(messageId) {
@@ -3135,6 +3162,7 @@ class StreamingProcessor {
31353162 this.messageTimerDom = this.messageDom?.querySelector('.mes_timer');
31363163 this.messageTokenCounterDom = this.messageDom?.querySelector('.tokenCounterDisplay');
31373164 this.messageReasoningDom = this.messageDom?.querySelector('.mes_reasoning');
3165+ this.messageReasoningHeaderDom = this.messageDom?.querySelector('.mes_reasoning_header_title');
31383166 }
31393167 }
31403168
@@ -3182,7 +3210,7 @@ class StreamingProcessor {
31823210 return messageId;
31833211 }
31843212
31853213 async onProgressStreaming(messageId, text, isFinal) {
31863214 const isImpersonate = this.type == 'impersonate';
31873215 const isContinue = this.type == 'continue';
31883216
@@ -3209,6 +3237,8 @@ class StreamingProcessor {
32093237 this.sendTextarea.dispatchEvent(new Event('input', { bubbles: true }));
32103238 }
32113239 else {
3240+ const mesChanged = chat[messageId]['mes'] !== processedText;
3241+
32123242 this.#checkDomElements(messageId);
32133243 this.#updateMessageBlockVisibility();
32143244 const currentTime = new Date();
@@ -3221,11 +3251,25 @@ class StreamingProcessor {
32213251 }
32223252
32233253 if (this.reasoning) {
32243254 chat[messageId]['extra']['const reasoning'] = power_user.trim_spaces ? this.reasoning.trim() : this.reasoning;
3255+ const reasoningChanged = chat[messageId]['extra']['reasoning'] !== reasoning;
3256+ chat[messageId]['extra']['reasoning'] = reasoning;
3257+
3258+ if (reasoningChanged && this.reasoningStartTime === null) {
3259+ this.reasoningStartTime = Date.now();
3260+ }
3261+ if (!reasoningChanged && mesChanged && this.reasoningStartTime !== null && this.reasoningEndTime === null) {
3262+ this.reasoningEndTime = Date.now();
3263+ }
3264+ await this.#updateReasoningTime(messageId);
3265+
32253266 if (this.messageReasoningDom instanceof HTMLElement) {
32263267 const formattedReasoning = messageFormatting(this.reasoning, '', false, false, messageId, {}, true);
32273268 this.messageReasoningDom.innerHTML = formattedReasoning;
32283269 }
3270+ if (this.messageDom instanceof HTMLElement) {
3271+ this.messageDom.classList.add('reasoning');
3272+ }
32293273 }
32303274
32313275 // Don't waste time calculating token count for streaming
@@ -3271,11 +3315,24 @@ class StreamingProcessor {
32713315 }
32723316 }
32733317
3318+ async #updateReasoningTime(messageId, { forceEnd = false } = {}) {
3319+ const duration = this.#reasoningDuration();
3320+ chat[messageId]['extra']['reasoning_duration'] = duration;
3321+ updateReasoningTimeUI(this.messageReasoningHeaderDom, duration, { forceEnd: forceEnd });
3322+ await eventSource.emit(event_types.STREAM_REASONING_DONE, this.reasoning, duration);
3323+ }
3324+
32743325 async onFinishStreaming(messageId, text) {
32753326 this.hideMessageButtons(this.messageId);
32763327 await this.onProgressStreaming(messageId, text, true);
32773328 addCopyToCodeBlocks($(`#chat .mes[mesid="${messageId}"]`));
32783329
3330+ // Ensure reasoning finish time is recorded if not already
3331+ if (this.reasoningStartTime !== null && this.reasoningEndTime === null) {
3332+ this.reasoningEndTime = Date.now();
3333+ await this.#updateReasoningTime(messageId, { forceEnd: true });
3334+ }
3335+
32793336 if (Array.isArray(this.swipes) && this.swipes.length > 0) {
32803337 const message = chat[messageId];
32813338 const swipeInfo = {
@@ -3377,7 +3434,7 @@ class StreamingProcessor {
33773434 }
33783435 this.reasoning = getRegexedString(state?.reasoning ?? '', regex_placement.REASONING);
33793436 await eventSource.emit(event_types.STREAM_TOKEN_RECEIVED, text);
33803437 await sw.tick(async () => await this.onProgressStreaming(this.messageId, this.continueMessage + text));
33813438 }
33823439 const seconds = (timestamps[timestamps.length - 1] - timestamps[0]) / 1000;
33833440 console.warn(`Stream stats: ${timestamps.length} tokens, ${seconds.toFixed(2)} seconds, rate: ${Number(timestamps.length / seconds).toFixed(2)} TPS`);
@@ -5738,6 +5795,25 @@ function extractReasoningFromData(data) {
57385795}
57395796
57405797/**
5798+ * Updates the Reasoning controls
5799+ * @param {HTMLElement} element The element to update
5800+ * @param {number?} duration The duration of the reasoning in milliseconds
5801+ * @param {object} [options={}] Options for the function
5802+ * @param {boolean} [options.forceEnd=false] If true, there will be no "Thinking..." when no duration exists
5803+ */
5804+function updateReasoningTimeUI(element, duration, { forceEnd = false } = {}) {
5805+ if (duration) {
5806+ const durationStr = moment.duration(duration).locale(getCurrentLocale()).humanize({ s: 50, ss: 9 });
5807+ element.textContent = t`Thought for ${durationStr}`;
5808+ } else if (forceEnd) {
5809+ element.textContent = t`Thought for some time`;
5810+ } else {
5811+ element.textContent = t`Thinking...`;
5812+ }
5813+}
5814+
5815+
5816+/**
57415817 * Extracts multiswipe swipes from the response data.
57425818 * @param {Object} data Response data
57435819 * @param {string} type Type of generation
@@ -7118,8 +7194,10 @@ export function setGenerationParamsFromPreset(preset) {
71187194// Common code for message editor done and auto-save
71197195function updateMessage(div) {
71207196 const mesBlock = div.closest('.mes_block');
71217197 let text = mesBlock.find('.edit_textarea').val();
7122- const mes = chat[this_edit_mes_id];
7198+ ?? mesBlock.find('.mes_text').text();
7199+ const mesElement = div.closest('.mes');
7200+ const mes = chat[mesElement.attr('mesid')];
71237201
71247202 let regexPlacement;
71257203 if (mes.is_user) {
@@ -7238,6 +7316,11 @@ async function messageEditDone(div) {
72387316 appendMediaToMessage(mes, div.closest('.mes'));
72397317 addCopyToCodeBlocks(div.closest('.mes'));
72407318
7319+ const reasoningEditDone = mesBlock.find('.mes_reasoning_edit_done:visible');
7320+ if (reasoningEditDone.length > 0) {
7321+ reasoningEditDone.trigger('click');
7322+ }
7323+
72417324 await eventSource.emit(event_types.MESSAGE_UPDATED, this_edit_mes_id);
72427325 this_edit_mes_id = undefined;
72437326 await saveChatConditional();
@@ -10745,6 +10828,12 @@ jQuery(async function () {
1074510828 var edit_mes_id = $(this).closest('.mes').attr('mesid');
1074610829 this_edit_mes_id = edit_mes_id;
1074710830
10831+ // Also edit reasoning, if it exists
10832+ const reasoningEdit = $(this).closest('.mes_block').find('.mes_reasoning_edit:visible');
10833+ if (reasoningEdit.length > 0) {
10834+ reasoningEdit.trigger('click');
10835+ }
10836+
1074810837 var text = chat[edit_mes_id]['mes'];
1074910838 if (chat[edit_mes_id]['is_user']) {
1075010839 this_edit_mes_chname = name1;
@@ -10783,6 +10872,18 @@ jQuery(async function () {
1078310872 }
1078410873 });
1078510874
10875+ $(document).on('click', '.mes_reasoning_header', function () {
10876+ // If we are in message edit mode and reasoning area is closed, a click opens and edits it
10877+ const mes = $(this).closest('.mes');
10878+ const mesEditArea = mes.find('#curEditTextarea');
10879+ if (mesEditArea.length) {
10880+ const summary = $(mes).find('.mes_reasoning_summary');
10881+ if (!summary.attr('open')) {
10882+ summary.find('.mes_reasoning_edit').trigger('click');
10883+ }
10884+ }
10885+ });
10886+
1078610887 $(document).on('input', '#curEditTextarea', function () {
1078710888 if (power_user.auto_save_msg_edits === true) {
1078810889 messageEditAuto($(this));
@@ -10878,6 +10979,11 @@ jQuery(async function () {
1087810979 appendMediaToMessage(chat[this_edit_mes_id], $(this).closest('.mes'));
1087910980 addCopyToCodeBlocks($(this).closest('.mes'));
1088010981
10982+ const reasoningEditDone = $(this).closest('.mes_block').find('.mes_reasoning_edit_cancel:visible');
10983+ if (reasoningEditDone.length > 0) {
10984+ reasoningEditDone.trigger('click');
10985+ }
10986+
1088110987 await eventSource.emit(event_types.MESSAGE_UPDATED, this_edit_mes_id);
1088210988 this_edit_mes_id = undefined;
1088310989 });
@@ -11332,6 +11438,17 @@ jQuery(async function () {
1133211438 }
1133311439 });
1133411440
11441+ $(document).on('click', '.mes_reasoning_summary', function () {
11442+ // If you toggle summary header while editing reasoning, yup - we just cancel it
11443+ $(this).closest('.mes').find('.mes_reasoning_edit_cancel:visible').trigger('click');
11444+ });
11445+
11446+ $(document).on('click', '.mes_reasoning_details', function (e) {
11447+ if (!e.target.closest('.mes_reasoning_actions') && !e.target.closest('.mes_reasoning_header')) {
11448+ e.preventDefault();
11449+ }
11450+ });
11451+
1133511452 $(document).keyup(function (e) {
1133611453 if (e.key === 'Escape') {
1133711454 const isEditVisible = $('#curEditTextarea').is(':visible') || $('.reasoning_edit_textarea').length > 0;
public/scripts/RossAscends-mods.js+12 -3
@@ -1063,12 +1063,19 @@ export function initRossMods() {
10631063 // Ctrl+Enter for Regeneration Last Response. If editing, accept the edits instead
10641064 if (event.ctrlKey && event.key == 'Enter') {
10651065 const editMesDone = $('.mes_edit_done:visible');
1066+ const reasoningMesDone = $('.mes_reasoning_edit_done:visible');
10661067 if (editMesDone.length > 0) {
10671068 console.debug('Accepting edits with Ctrl+Enter');
10681069 $('#send_textarea').focustrigger('focus');
10691070 editMesDone.trigger('click');
10701071 return;
10711072 } else if (is_send_pressreasoningMesDone.length ==> false0) {
1073+ console.debug('Accepting edits with Ctrl+Enter');
1074+ $('#send_textarea').trigger('focus');
1075+ reasoningMesDone.trigger('click');
1076+ return;
1077+ }
1078+ else if (is_send_press == false) {
10721079 const skipConfirmKey = 'RegenerateWithCtrlEnter';
10731080 const skipConfirm = LoadLocalBool(skipConfirmKey);
10741081 function doRegenerate() {
@@ -1082,7 +1089,9 @@ export function initRossMods() {
10821089 let regenerateWithCtrlEnter = false;
10831090 const result = await Popup.show.confirm('Regenerate Message', 'Are you sure you want to regenerate the latest message?', {
10841091 customInputs: [{ id: 'regenerateWithCtrlEnter', label: 'Don\'t ask again' }],
10851092 onClose: (popup) => regenerateWithCtrlEnter = popup.inputResults.get('regenerateWithCtrlEnter') ?? false,{
1093+ regenerateWithCtrlEnter = popup.inputResults.get('regenerateWithCtrlEnter') ?? false;
1094+ },
10861095 });
10871096 if (!result) {
10881097 return;
public/scripts/power-user.js+1 -0
@@ -256,6 +256,7 @@ let power_user = {
256256 reasoning: {
257257 auto_parse: false,
258258 add_to_prompts: false,
259+ auto_expand: false,
259260 prefix: '<think>\n',
260261 suffix: '\n</think>',
261262 separator: '\n\n',
public/scripts/reasoning.js+25 -2
@@ -23,6 +23,18 @@ function getMessageFromJquery(element) {
2323}
2424
2525/**
26+ * Toggles the auto-expand state of reasoning blocks.
27+ */
28+function toggleReasoningAutoExpand() {
29+ const reasoningBlocks = document.querySelectorAll('details.mes_reasoning_details');
30+ reasoningBlocks.forEach((block) => {
31+ if (block instanceof HTMLDetailsElement) {
32+ block.open = power_user.reasoning.auto_expand;
33+ }
34+ });
35+}
36+
37+/**
2638 * Helper class for adding reasoning to messages.
2739 * Keeps track of the number of reasoning additions.
2840 */
@@ -118,6 +130,13 @@ function loadReasoningSettings() {
118130 power_user.reasoning.auto_parse = !!$(this).prop('checked');
119131 saveSettingsDebounced();
120132 });
133+
134+ $('#reasoning_auto_expand').prop('checked', power_user.reasoning.auto_expand);
135+ $('#reasoning_auto_expand').on('change', function () {
136+ power_user.reasoning.auto_expand = !!$(this).prop('checked');
137+ toggleReasoningAutoExpand();
138+ saveSettingsDebounced();
139+ });
121140}
122141
123142function registerReasoningSlashCommands() {
@@ -288,6 +307,8 @@ function setReasoningEventHandlers() {
288307 await saveChatConditional();
289308 updateMessageBlock(messageId, message);
290309 textarea.remove();
310+
311+ messageBlock.find('.mes_edit_done:visible').trigger('click');
291312 });
292313
293314 $(document).on('click', '.mes_reasoning_edit_cancel', function (e) {
@@ -297,6 +318,8 @@ function setReasoningEventHandlers() {
297318 const { messageBlock } = getMessageFromJquery(this);
298319 const textarea = messageBlock.find('.reasoning_edit_textarea');
299320 textarea.remove();
321+
322+ messageBlock.find('.mes_reasoning_edit_cancel:visible').trigger('click');
300323 });
301324
302325 $(document).on('click', '.mes_edit_add_reasoning', async function () {
@@ -311,9 +334,8 @@ function setReasoningEventHandlers() {
311334 }
312335
313336 message.extra.reasoning = PromptReasoning.REASONING_PLACEHOLDER;
314- await saveChatConditional();
315- closeMessageEditor();
316337 updateMessageBlock(messageId, message);
338+ await saveChatConditional();
317339 });
318340
319341 $(document).on('click', '.mes_reasoning_delete', async function (e) {
@@ -441,6 +463,7 @@ function registerReasoningAppEvents() {
441463
442464export function initReasoning() {
443465 loadReasoningSettings();
466+ toggleReasoningAutoExpand();
444467 setReasoningEventHandlers();
445468 registerReasoningSlashCommands();
446469 registerReasoningMacros();
public/style.css+84 -28
@@ -106,6 +106,8 @@
106106 --tool-cool-color-picker-btn-bg: transparent;
107107 --tool-cool-color-picker-btn-border-color: transparent;
108108
109+ --mes-right-spacing: 30px;
110+
109111 --avatar-base-height: 50px;
110112 --avatar-base-width: 50px;
111113 --avatar-base-border-radius: 2px;
@@ -342,18 +344,59 @@ input[type='checkbox']:focus-visible {
342344
343345.mes_reasoning {
344346 display: block;
345347 border-left: 1px2px solid var(--SmartThemeBorderColorSmartThemeEmColor);
346- background-color: var(--black30a);
348+ border-radius: 2px;
347- border-radius: 5px;
348349 padding: 5px;
349350 marginpadding-left: 5px 014px;
351+ margin-bottom: 0.5em;
350352 overflow-y: auto;
353+ color: var(--SmartThemeEmColor);
354+}
355+
356+.mes_reasoning_details {
357+ margin-right: var(--mes-right-spacing);
358+}
359+
360+.mes_reasoning_details .mes_reasoning_summary {
361+ margin-right: calc(var(--mes-right-spacing) * -1);
362+}
363+
364+.mes_reasoning *:last-child {
365+ margin-bottom: 0;
366+}
367+
368+.mes_reasoning em,
369+.mes_reasoning i,
370+.mes_reasoning u,
371+.mes_reasoning q,
372+.mes_reasoning blockquote {
373+ filter: saturate(0.5);
374+}
375+
376+.mes_reasoning_details .mes_reasoning em {
377+ color: color-mix(in srgb, var(--SmartThemeEmColor) 67%, var(--SmartThemeBlurTintColor) 33%)
378+}
379+
380+.mes_reasoning_header_block {
381+ flex-grow: 1;
351382}
352383
353384.mes_reasoning_summarymes_reasoning_header {
354385 cursor: pointer;
355386 position: relative;
356387 marginuser-select: 2pxnone;
388+ margin: 0.5em 2px;
389+ padding: 7px 14px;
390+ padding-right: calc(0.7em + 14px);
391+ border-radius: 10px;
392+ background-color: var(--grey30);
393+ font-size: calc(var(--mainFontSize) * 0.9);
394+ align-items: baseline;
395+}
396+
397+/* TWIMC: Remove with custom CSS to show the icon */
398+.mes_reasoning_header > .icon-svg {
399+ display: none;
357400}
358401
359402@supports not selector(:has(*)) {
@@ -365,27 +408,27 @@ input[type='checkbox']:focus-visible {
365408.mes_bias:empty,
366409.mes_reasoning:empty,
367410.mes_reasoning_details:has(.mes_reasoning:empty),
368-.mes_block:has(.edit_textarea) .mes_reasoning_details,
369411.mes_reasoning_details:not([open]) .mes_reasoning_actions,
370412.mes_reasoning_details:has(.reasoning_edit_textarea) .mes_reasoning,
371413.mes_reasoning_details:not(:has(.reasoning_edit_textarea)) .mes_reasoning_actions .mes_button.mes_reasoning_edit_donemes_reasoning_header,
372414.mes_reasoning_details:not(:has(.reasoning_edit_textarea)) .mes_reasoning_actions .mes_button.mes_reasoning_edit_canceledit_button,
373415.mes_reasoning_details:has(.reasoning_edit_textarea) .mes_reasoning_actions .mes_button:not(.mes_reasoning_edit_done, .mes_reasoning_edit_canceledit_button) {,
416+.mes_block:has(.edit_textarea):has(.reasoning_edit_textarea) .mes_reasoning_actions {
374417 display: none;
375418}
376419
377420.mes_reasoning_actionsmes_reasoning_details .mes_reasoning_arrow {
378421 position: absolute;
379422 righttop: 050%;
380423 topright: 07px;
424+ transform: translateY(-50%);
425+ font-size: calc(var(--mainFontSize) * 0.7);
426+ width: calc(var(--mainFontSize) * 0.7);
427+ height: calc(var(--mainFontSize) * 0.7);
428+}
381429
382- display: flex;
430+.mes_reasoning_details:not([open]) .mes_reasoning_arrow {
383- gap: 4px;
431+ transform: translateY(-50%) rotate(180deg);
384- flex-wrap: nowrap;
385- justify-content: flex-end;
386- transition: all 200ms;
387- overflow-x: hidden;
388- padding: 1px;
389432}
390433
391434.mes_reasoning_summary>span {
@@ -1100,13 +1143,8 @@ body .panelControlBar {
11001143 /*only affects bubblechat to make it sit nicely at the bottom*/
11011144}
11021145
11031146.last_mes:has(.mes_text:empty):has(.mes_reasoning_details[open]) .mes_reasoning:not(:empty) {
11041147 margin-bottom: 30pxvar(--mes-right-spacing);
1105-}
1106-
1107-.last_mes .mes_reasoning,
1108-.last_mes .mes_text {
1109- padding-right: 30px;
11101148}
11111149
11121150/* SWIPE RELATED STYLES*/
@@ -1330,6 +1368,7 @@ body.swipeAllMessages .mes:not(.last_mes) .swipes-counter {
13301368 padding-left: 0;
13311369 padding-top: 5px;
13321370 padding-bottom: 5px;
1371+ padding-right: var(--mes-right-spacing);
13331372}
13341373
13351374br {
@@ -3000,6 +3039,7 @@ input[type=search]:focus::-webkit-search-cancel-button {
30003039.mes_block .ch_name {
30013040 max-width: 100%;
30023041 min-height: 22px;
3042+ align-items: flex-start;
30033043}
30043044
30053045/*applies to both groups and solos chars in the char list*/
@@ -4224,7 +4264,13 @@ input[type="range"]::-webkit-slider-thumb {
42244264 transition: 0.3s ease-in-out;
42254265}
42264266
42274267.mes_edit_buttons .menu_buttonmes_reasoning_actions {
4268+ margin: 0;
4269+ margin-top: 0.5em;
4270+}
4271+
4272+.mes_edit_buttons .menu_button,
4273+.mes_reasoning_actions .edit_button {
42284274 opacity: 0.5;
42294275 padding: 0px;
42304276 font-size: 1rem;
@@ -4237,6 +4283,12 @@ input[type="range"]::-webkit-slider-thumb {
42374283 align-items: center;
42384284}
42394285
4286+.mes_reasoning_actions .edit_button {
4287+ margin-bottom: 0.5em;
4288+ opacity: 1;
4289+ filter: brightness(0.7);
4290+}
4291+
42404292.mes_reasoning_edit_cancel,
42414293.mes_edit_cancel.menu_button {
42424294 background-color: var(--crimson70a);
@@ -4263,6 +4315,10 @@ input[type="range"]::-webkit-slider-thumb {
42634315 field-sizing: content;
42644316}
42654317
4318+.mes.reasoning .mes_edit_add_reasoning {
4319+ display: none;
4320+}
4321+
42664322#anchor_order {
42674323 margin-bottom: 15px;
42684324}