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
This commit is contained in:
Cohee 2025-02-02 21:46:23 +02:00 committed by GitHub
commit 5494e89fdb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 283 additions and 66 deletions

View File

@ -3789,6 +3789,12 @@
Auto-Parse Reasoning
</small>
</label>
<label class="checkbox_label" for="reasoning_auto_expand" title="Automatically expand reasoning blocks." data-i18n="[title]reasoning_auto_expand">
<input id="reasoning_auto_expand" type="checkbox" />
<small data-i18n="Auto-Expand Reasoning">
Auto-Expand Reasoning
</small>
</label>
<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">
<input id="reasoning_add_to_prompts" type="checkbox" />
<small data-i18n="Add Reasoning to Prompts">
@ -6248,14 +6254,19 @@
</div>
</div>
<details class="mes_reasoning_details">
<summary class="mes_reasoning_summary">
<span data-i18n="Reasoning">Reasoning</span>
<div class="mes_reasoning_actions">
<div class="mes_reasoning_edit_done mes_button fa-solid fa-check" title="Confirm" data-i18n="[title]Confirmedit"></div>
<div class="mes_reasoning_edit_cancel mes_button fa-solid fa-xmark" title="Cancel edit" data-i18n="[title]Cancel edit"></div>
<div class="mes_reasoning_edit mes_button fa-solid fa-pencil" title="Edit reasoning" data-i18n="[title]Edit reasoning"></div>
<summary class="mes_reasoning_summary flex-container">
<div class="mes_reasoning_header_block flex-container">
<div class="mes_reasoning_header flex-container">
<span class="mes_reasoning_header_title" data-i18n="Thought for some time">Thought for some time</span>
<div class="mes_reasoning_arrow fa-solid fa-chevron-up"></div>
</div>
</div>
<div class="mes_reasoning_actions flex-container">
<div class="mes_reasoning_edit_done menu_button edit_button fa-solid fa-check" title="Confirm" data-i18n="[title]Confirmedit"></div>
<div class="mes_reasoning_delete menu_button edit_button fa-solid fa-trash-can" title="Remove reasoning" data-i18n="[title]Remove reasoning"></div>
<div class="mes_reasoning_edit_cancel menu_button edit_button fa-solid fa-xmark" title="Cancel edit" data-i18n="[title]Cancel edit"></div>
<div class="mes_reasoning_copy mes_button fa-solid fa-copy" title="Copy reasoning" data-i18n="[title]Copy reasoning"></div>
<div class="mes_reasoning_delete mes_button fa-solid fa-trash-can" title="Remove reasoning" data-i18n="[title]Remove reasoning"></div>
<div class="mes_reasoning_edit mes_button fa-solid fa-pencil" title="Edit reasoning" data-i18n="[title]Edit reasoning"></div>
</div>
</summary>
<div class="mes_reasoning"></div>

View File

@ -225,7 +225,7 @@ import {
instruct_presets,
selectContextPreset,
} from './scripts/instruct-mode.js';
import { initLocales, t } from './scripts/i18n.js';
import { getCurrentLocale, initLocales, t } from './scripts/i18n.js';
import { getFriendlyTokenizerName, getTokenCount, getTokenCountAsync, initTokenizers, saveTokenCache, TOKENIZER_SUPPORTED_KEY } from './scripts/tokenizers.js';
import {
user_avatar,
@ -495,6 +495,7 @@ export const event_types = {
/** @deprecated The event is aliased to STREAM_TOKEN_RECEIVED. */
SMOOTH_STREAM_TOKEN_RECEIVED: 'stream_token_received',
STREAM_TOKEN_RECEIVED: 'stream_token_received',
STREAM_REASONING_DONE: 'stream_reasoning_done',
FILE_ATTACHMENT_DELETED: 'file_attachment_deleted',
WORLDINFO_FORCE_ACTIVATE: 'worldinfo_force_activate',
OPEN_CHARACTER_LIBRARY: 'open_character_library',
@ -2189,26 +2190,29 @@ function insertSVGIcon(mes, extra) {
modelName = extra.api;
}
const image = new Image();
// Add classes for styling and identification
image.classList.add('icon-svg', 'timestamp-icon');
image.src = `/img/${modelName}.svg`;
image.title = `${extra?.api ? extra.api + ' - ' : ''}${extra?.model ?? ''}`;
image.onload = async function () {
// Check if an SVG already exists adjacent to the timestamp
let existingSVG = mes.find('.timestamp').next('.timestamp-icon');
if (existingSVG.length) {
// Replace existing SVG
existingSVG.replaceWith(image);
} else {
// Append the new SVG if none exists
mes.find('.timestamp').after(image);
}
await SVGInject(image);
const insertOrReplaceSVG = (image, className, targetSelector, insertBefore) => {
image.onload = async function () {
let existingSVG = insertBefore ? mes.find(targetSelector).prev(`.${className}`) : mes.find(targetSelector).next(`.${className}`);
if (existingSVG.length) {
existingSVG.replaceWith(image);
} else {
if (insertBefore) mes.find(targetSelector).before(image);
else mes.find(targetSelector).after(image);
}
await SVGInject(image);
};
};
const createModelImage = (className, targetSelector, insertBefore) => {
const image = new Image();
image.classList.add('icon-svg', className);
image.src = `/img/${modelName}.svg`;
image.title = `${extra?.api ? extra.api + ' - ' : ''}${extra?.model ?? ''}`;
insertOrReplaceSVG(image, className, targetSelector, insertBefore);
};
createModelImage('timestamp-icon', '.timestamp');
createModelImage('thinking-icon', '.mes_reasoning_header_title', true);
}
@ -2220,6 +2224,7 @@ function getMessageFromTemplate({
avatarImg,
bias,
reasoning,
reasoningDuration,
isSystem,
title,
timerValue,
@ -2252,6 +2257,13 @@ function getMessageFromTemplate({
timerValue && mes.find('.mes_timer').attr('title', timerTitle).text(timerValue);
bookmarkLink && updateBookmarkDisplay(mes);
if (reasoning) {
mes.addClass('reasoning');
}
if (reasoningDuration) {
updateReasoningTimeUI(mes.find('.mes_reasoning_header_title')[0], reasoningDuration, { forceEnd: true });
}
if (power_user.timestamp_model_icon && extra?.api) {
insertSVGIcon(mes, extra);
}
@ -2269,6 +2281,7 @@ export function updateMessageBlock(messageId, message) {
const text = message?.extra?.display_text ?? message.mes;
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));
messageElement.toggleClass('reasoning', !!message.extra?.reasoning);
addCopyToCodeBlocks(messageElement);
appendMediaToMessage(message, messageElement);
}
@ -2439,6 +2452,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
avatarImg: avatarImg,
bias: bias,
reasoning: reasoning,
reasoningDuration: mes.extra?.reasoning_duration,
isSystem: isSystem,
title: title,
bookmarkLink: bookmarkLink,
@ -3108,8 +3122,12 @@ class StreamingProcessor {
this.messageDom = null;
this.messageTextDom = null;
this.messageTimerDom = null;
/** @type {HTMLElement} */
this.messageTokenCounterDom = null;
/** @type {HTMLElement} */
this.messageReasoningDom = null;
/** @type {HTMLElement} */
this.messageReasoningHeaderDom = null;
/** @type {HTMLTextAreaElement} */
this.sendTextarea = document.querySelector('#send_textarea');
this.type = type;
@ -3126,6 +3144,15 @@ class StreamingProcessor {
this.messageLogprobs = [];
this.toolCalls = [];
this.reasoning = '';
this.reasoningStartTime = null;
this.reasoningEndTime = null;
}
#reasoningDuration() {
if (this.reasoningStartTime && this.reasoningEndTime) {
return (this.reasoningEndTime - this.reasoningStartTime);
}
return null;
}
#checkDomElements(messageId) {
@ -3135,6 +3162,7 @@ class StreamingProcessor {
this.messageTimerDom = this.messageDom?.querySelector('.mes_timer');
this.messageTokenCounterDom = this.messageDom?.querySelector('.tokenCounterDisplay');
this.messageReasoningDom = this.messageDom?.querySelector('.mes_reasoning');
this.messageReasoningHeaderDom = this.messageDom?.querySelector('.mes_reasoning_header_title');
}
}
@ -3182,7 +3210,7 @@ class StreamingProcessor {
return messageId;
}
onProgressStreaming(messageId, text, isFinal) {
async onProgressStreaming(messageId, text, isFinal) {
const isImpersonate = this.type == 'impersonate';
const isContinue = this.type == 'continue';
@ -3209,6 +3237,8 @@ class StreamingProcessor {
this.sendTextarea.dispatchEvent(new Event('input', { bubbles: true }));
}
else {
const mesChanged = chat[messageId]['mes'] !== processedText;
this.#checkDomElements(messageId);
this.#updateMessageBlockVisibility();
const currentTime = new Date();
@ -3221,11 +3251,25 @@ class StreamingProcessor {
}
if (this.reasoning) {
chat[messageId]['extra']['reasoning'] = power_user.trim_spaces ? this.reasoning.trim() : this.reasoning;
const reasoning = power_user.trim_spaces ? this.reasoning.trim() : this.reasoning;
const reasoningChanged = chat[messageId]['extra']['reasoning'] !== reasoning;
chat[messageId]['extra']['reasoning'] = reasoning;
if (reasoningChanged && this.reasoningStartTime === null) {
this.reasoningStartTime = Date.now();
}
if (!reasoningChanged && mesChanged && this.reasoningStartTime !== null && this.reasoningEndTime === null) {
this.reasoningEndTime = Date.now();
}
await this.#updateReasoningTime(messageId);
if (this.messageReasoningDom instanceof HTMLElement) {
const formattedReasoning = messageFormatting(this.reasoning, '', false, false, messageId, {}, true);
this.messageReasoningDom.innerHTML = formattedReasoning;
}
if (this.messageDom instanceof HTMLElement) {
this.messageDom.classList.add('reasoning');
}
}
// Don't waste time calculating token count for streaming
@ -3271,11 +3315,24 @@ class StreamingProcessor {
}
}
async #updateReasoningTime(messageId, { forceEnd = false } = {}) {
const duration = this.#reasoningDuration();
chat[messageId]['extra']['reasoning_duration'] = duration;
updateReasoningTimeUI(this.messageReasoningHeaderDom, duration, { forceEnd: forceEnd });
await eventSource.emit(event_types.STREAM_REASONING_DONE, this.reasoning, duration);
}
async onFinishStreaming(messageId, text) {
this.hideMessageButtons(this.messageId);
this.onProgressStreaming(messageId, text, true);
await this.onProgressStreaming(messageId, text, true);
addCopyToCodeBlocks($(`#chat .mes[mesid="${messageId}"]`));
// Ensure reasoning finish time is recorded if not already
if (this.reasoningStartTime !== null && this.reasoningEndTime === null) {
this.reasoningEndTime = Date.now();
await this.#updateReasoningTime(messageId, { forceEnd: true });
}
if (Array.isArray(this.swipes) && this.swipes.length > 0) {
const message = chat[messageId];
const swipeInfo = {
@ -3377,7 +3434,7 @@ class StreamingProcessor {
}
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));
await sw.tick(async () => await this.onProgressStreaming(this.messageId, this.continueMessage + text));
}
const seconds = (timestamps[timestamps.length - 1] - timestamps[0]) / 1000;
console.warn(`Stream stats: ${timestamps.length} tokens, ${seconds.toFixed(2)} seconds, rate: ${Number(timestamps.length / seconds).toFixed(2)} TPS`);
@ -5737,6 +5794,25 @@ function extractReasoningFromData(data) {
return '';
}
/**
* Updates the Reasoning controls
* @param {HTMLElement} element The element to update
* @param {number?} duration The duration of the reasoning in milliseconds
* @param {object} [options={}] Options for the function
* @param {boolean} [options.forceEnd=false] If true, there will be no "Thinking..." when no duration exists
*/
function updateReasoningTimeUI(element, duration, { forceEnd = false } = {}) {
if (duration) {
const durationStr = moment.duration(duration).locale(getCurrentLocale()).humanize({ s: 50, ss: 9 });
element.textContent = t`Thought for ${durationStr}`;
} else if (forceEnd) {
element.textContent = t`Thought for some time`;
} else {
element.textContent = t`Thinking...`;
}
}
/**
* Extracts multiswipe swipes from the response data.
* @param {Object} data Response data
@ -7118,8 +7194,10 @@ export function setGenerationParamsFromPreset(preset) {
// Common code for message editor done and auto-save
function updateMessage(div) {
const mesBlock = div.closest('.mes_block');
let text = mesBlock.find('.edit_textarea').val();
const mes = chat[this_edit_mes_id];
let text = mesBlock.find('.edit_textarea').val()
?? mesBlock.find('.mes_text').text();
const mesElement = div.closest('.mes');
const mes = chat[mesElement.attr('mesid')];
let regexPlacement;
if (mes.is_user) {
@ -7238,6 +7316,11 @@ async function messageEditDone(div) {
appendMediaToMessage(mes, div.closest('.mes'));
addCopyToCodeBlocks(div.closest('.mes'));
const reasoningEditDone = mesBlock.find('.mes_reasoning_edit_done:visible');
if (reasoningEditDone.length > 0) {
reasoningEditDone.trigger('click');
}
await eventSource.emit(event_types.MESSAGE_UPDATED, this_edit_mes_id);
this_edit_mes_id = undefined;
await saveChatConditional();
@ -10745,6 +10828,12 @@ jQuery(async function () {
var edit_mes_id = $(this).closest('.mes').attr('mesid');
this_edit_mes_id = edit_mes_id;
// Also edit reasoning, if it exists
const reasoningEdit = $(this).closest('.mes_block').find('.mes_reasoning_edit:visible');
if (reasoningEdit.length > 0) {
reasoningEdit.trigger('click');
}
var text = chat[edit_mes_id]['mes'];
if (chat[edit_mes_id]['is_user']) {
this_edit_mes_chname = name1;
@ -10783,6 +10872,18 @@ jQuery(async function () {
}
});
$(document).on('click', '.mes_reasoning_header', function () {
// If we are in message edit mode and reasoning area is closed, a click opens and edits it
const mes = $(this).closest('.mes');
const mesEditArea = mes.find('#curEditTextarea');
if (mesEditArea.length) {
const summary = $(mes).find('.mes_reasoning_summary');
if (!summary.attr('open')) {
summary.find('.mes_reasoning_edit').trigger('click');
}
}
});
$(document).on('input', '#curEditTextarea', function () {
if (power_user.auto_save_msg_edits === true) {
messageEditAuto($(this));
@ -10878,6 +10979,11 @@ jQuery(async function () {
appendMediaToMessage(chat[this_edit_mes_id], $(this).closest('.mes'));
addCopyToCodeBlocks($(this).closest('.mes'));
const reasoningEditDone = $(this).closest('.mes_block').find('.mes_reasoning_edit_cancel:visible');
if (reasoningEditDone.length > 0) {
reasoningEditDone.trigger('click');
}
await eventSource.emit(event_types.MESSAGE_UPDATED, this_edit_mes_id);
this_edit_mes_id = undefined;
});
@ -11332,6 +11438,17 @@ jQuery(async function () {
}
});
$(document).on('click', '.mes_reasoning_summary', function () {
// If you toggle summary header while editing reasoning, yup - we just cancel it
$(this).closest('.mes').find('.mes_reasoning_edit_cancel:visible').trigger('click');
});
$(document).on('click', '.mes_reasoning_details', function (e) {
if (!e.target.closest('.mes_reasoning_actions') && !e.target.closest('.mes_reasoning_header')) {
e.preventDefault();
}
});
$(document).keyup(function (e) {
if (e.key === 'Escape') {
const isEditVisible = $('#curEditTextarea').is(':visible') || $('.reasoning_edit_textarea').length > 0;

View File

@ -1063,12 +1063,19 @@ export function initRossMods() {
// Ctrl+Enter for Regeneration Last Response. If editing, accept the edits instead
if (event.ctrlKey && event.key == 'Enter') {
const editMesDone = $('.mes_edit_done:visible');
const reasoningMesDone = $('.mes_reasoning_edit_done:visible');
if (editMesDone.length > 0) {
console.debug('Accepting edits with Ctrl+Enter');
$('#send_textarea').focus();
$('#send_textarea').trigger('focus');
editMesDone.trigger('click');
return;
} else if (is_send_press == false) {
} else if (reasoningMesDone.length > 0) {
console.debug('Accepting edits with Ctrl+Enter');
$('#send_textarea').trigger('focus');
reasoningMesDone.trigger('click');
return;
}
else if (is_send_press == false) {
const skipConfirmKey = 'RegenerateWithCtrlEnter';
const skipConfirm = LoadLocalBool(skipConfirmKey);
function doRegenerate() {
@ -1082,7 +1089,9 @@ export function initRossMods() {
let regenerateWithCtrlEnter = false;
const result = await Popup.show.confirm('Regenerate Message', 'Are you sure you want to regenerate the latest message?', {
customInputs: [{ id: 'regenerateWithCtrlEnter', label: 'Don\'t ask again' }],
onClose: (popup) => regenerateWithCtrlEnter = popup.inputResults.get('regenerateWithCtrlEnter') ?? false,
onClose: (popup) => {
regenerateWithCtrlEnter = popup.inputResults.get('regenerateWithCtrlEnter') ?? false;
},
});
if (!result) {
return;

View File

@ -256,6 +256,7 @@ let power_user = {
reasoning: {
auto_parse: false,
add_to_prompts: false,
auto_expand: false,
prefix: '<think>\n',
suffix: '\n</think>',
separator: '\n\n',

View File

@ -22,6 +22,18 @@ function getMessageFromJquery(element) {
return { messageId: messageId, message, messageBlock };
}
/**
* Toggles the auto-expand state of reasoning blocks.
*/
function toggleReasoningAutoExpand() {
const reasoningBlocks = document.querySelectorAll('details.mes_reasoning_details');
reasoningBlocks.forEach((block) => {
if (block instanceof HTMLDetailsElement) {
block.open = power_user.reasoning.auto_expand;
}
});
}
/**
* Helper class for adding reasoning to messages.
* Keeps track of the number of reasoning additions.
@ -118,6 +130,13 @@ function loadReasoningSettings() {
power_user.reasoning.auto_parse = !!$(this).prop('checked');
saveSettingsDebounced();
});
$('#reasoning_auto_expand').prop('checked', power_user.reasoning.auto_expand);
$('#reasoning_auto_expand').on('change', function () {
power_user.reasoning.auto_expand = !!$(this).prop('checked');
toggleReasoningAutoExpand();
saveSettingsDebounced();
});
}
function registerReasoningSlashCommands() {
@ -288,6 +307,8 @@ function setReasoningEventHandlers() {
await saveChatConditional();
updateMessageBlock(messageId, message);
textarea.remove();
messageBlock.find('.mes_edit_done:visible').trigger('click');
});
$(document).on('click', '.mes_reasoning_edit_cancel', function (e) {
@ -297,6 +318,8 @@ function setReasoningEventHandlers() {
const { messageBlock } = getMessageFromJquery(this);
const textarea = messageBlock.find('.reasoning_edit_textarea');
textarea.remove();
messageBlock.find('.mes_reasoning_edit_cancel:visible').trigger('click');
});
$(document).on('click', '.mes_edit_add_reasoning', async function () {
@ -311,9 +334,8 @@ function setReasoningEventHandlers() {
}
message.extra.reasoning = PromptReasoning.REASONING_PLACEHOLDER;
await saveChatConditional();
closeMessageEditor();
updateMessageBlock(messageId, message);
await saveChatConditional();
});
$(document).on('click', '.mes_reasoning_delete', async function (e) {
@ -441,6 +463,7 @@ function registerReasoningAppEvents() {
export function initReasoning() {
loadReasoningSettings();
toggleReasoningAutoExpand();
setReasoningEventHandlers();
registerReasoningSlashCommands();
registerReasoningMacros();

View File

@ -106,6 +106,8 @@
--tool-cool-color-picker-btn-bg: transparent;
--tool-cool-color-picker-btn-border-color: transparent;
--mes-right-spacing: 30px;
--avatar-base-height: 50px;
--avatar-base-width: 50px;
--avatar-base-border-radius: 2px;
@ -342,18 +344,59 @@ input[type='checkbox']:focus-visible {
.mes_reasoning {
display: block;
border: 1px solid var(--SmartThemeBorderColor);
background-color: var(--black30a);
border-radius: 5px;
border-left: 2px solid var(--SmartThemeEmColor);
border-radius: 2px;
padding: 5px;
margin: 5px 0;
padding-left: 14px;
margin-bottom: 0.5em;
overflow-y: auto;
color: var(--SmartThemeEmColor);
}
.mes_reasoning_summary {
.mes_reasoning_details {
margin-right: var(--mes-right-spacing);
}
.mes_reasoning_details .mes_reasoning_summary {
margin-right: calc(var(--mes-right-spacing) * -1);
}
.mes_reasoning *:last-child {
margin-bottom: 0;
}
.mes_reasoning em,
.mes_reasoning i,
.mes_reasoning u,
.mes_reasoning q,
.mes_reasoning blockquote {
filter: saturate(0.5);
}
.mes_reasoning_details .mes_reasoning em {
color: color-mix(in srgb, var(--SmartThemeEmColor) 67%, var(--SmartThemeBlurTintColor) 33%)
}
.mes_reasoning_header_block {
flex-grow: 1;
}
.mes_reasoning_header {
cursor: pointer;
position: relative;
margin: 2px;
user-select: none;
margin: 0.5em 2px;
padding: 7px 14px;
padding-right: calc(0.7em + 14px);
border-radius: 10px;
background-color: var(--grey30);
font-size: calc(var(--mainFontSize) * 0.9);
align-items: baseline;
}
/* TWIMC: Remove with custom CSS to show the icon */
.mes_reasoning_header > .icon-svg {
display: none;
}
@supports not selector(:has(*)) {
@ -365,27 +408,27 @@ input[type='checkbox']:focus-visible {
.mes_bias:empty,
.mes_reasoning:empty,
.mes_reasoning_details:has(.mes_reasoning:empty),
.mes_block:has(.edit_textarea) .mes_reasoning_details,
.mes_reasoning_details:not([open]) .mes_reasoning_actions,
.mes_reasoning_details:has(.reasoning_edit_textarea) .mes_reasoning,
.mes_reasoning_details:not(:has(.reasoning_edit_textarea)) .mes_reasoning_actions .mes_button.mes_reasoning_edit_done,
.mes_reasoning_details:not(:has(.reasoning_edit_textarea)) .mes_reasoning_actions .mes_button.mes_reasoning_edit_cancel,
.mes_reasoning_details:has(.reasoning_edit_textarea) .mes_reasoning_actions .mes_button:not(.mes_reasoning_edit_done, .mes_reasoning_edit_cancel) {
.mes_reasoning_details:has(.reasoning_edit_textarea) .mes_reasoning_header,
.mes_reasoning_details:not(:has(.reasoning_edit_textarea)) .mes_reasoning_actions .edit_button,
.mes_reasoning_details:has(.reasoning_edit_textarea) .mes_reasoning_actions .mes_button:not(.edit_button),
.mes_block:has(.edit_textarea):has(.reasoning_edit_textarea) .mes_reasoning_actions {
display: none;
}
.mes_reasoning_actions {
.mes_reasoning_details .mes_reasoning_arrow {
position: absolute;
right: 0;
top: 0;
top: 50%;
right: 7px;
transform: translateY(-50%);
font-size: calc(var(--mainFontSize) * 0.7);
width: calc(var(--mainFontSize) * 0.7);
height: calc(var(--mainFontSize) * 0.7);
}
display: flex;
gap: 4px;
flex-wrap: nowrap;
justify-content: flex-end;
transition: all 200ms;
overflow-x: hidden;
padding: 1px;
.mes_reasoning_details:not([open]) .mes_reasoning_arrow {
transform: translateY(-50%) rotate(180deg);
}
.mes_reasoning_summary>span {
@ -1100,13 +1143,8 @@ body .panelControlBar {
/*only affects bubblechat to make it sit nicely at the bottom*/
}
.last_mes:has(.mes_text:empty):has(.mes_reasoning_details[open]) .mes_reasoning:not(:empty) {
margin-bottom: 30px;
}
.last_mes .mes_reasoning,
.last_mes .mes_text {
padding-right: 30px;
.last_mes:has(.mes_text:empty):has(.mes_reasoning_details) .mes_reasoning:not(:empty) {
margin-bottom: var(--mes-right-spacing);
}
/* SWIPE RELATED STYLES*/
@ -1330,6 +1368,7 @@ body.swipeAllMessages .mes:not(.last_mes) .swipes-counter {
padding-left: 0;
padding-top: 5px;
padding-bottom: 5px;
padding-right: var(--mes-right-spacing);
}
br {
@ -3000,6 +3039,7 @@ input[type=search]:focus::-webkit-search-cancel-button {
.mes_block .ch_name {
max-width: 100%;
min-height: 22px;
align-items: flex-start;
}
/*applies to both groups and solos chars in the char list*/
@ -4224,7 +4264,13 @@ input[type="range"]::-webkit-slider-thumb {
transition: 0.3s ease-in-out;
}
.mes_edit_buttons .menu_button {
.mes_reasoning_actions {
margin: 0;
margin-top: 0.5em;
}
.mes_edit_buttons .menu_button,
.mes_reasoning_actions .edit_button {
opacity: 0.5;
padding: 0px;
font-size: 1rem;
@ -4237,6 +4283,12 @@ input[type="range"]::-webkit-slider-thumb {
align-items: center;
}
.mes_reasoning_actions .edit_button {
margin-bottom: 0.5em;
opacity: 1;
filter: brightness(0.7);
}
.mes_reasoning_edit_cancel,
.mes_edit_cancel.menu_button {
background-color: var(--crimson70a);
@ -4263,6 +4315,10 @@ input[type="range"]::-webkit-slider-thumb {
field-sizing: content;
}
.mes.reasoning .mes_edit_add_reasoning {
display: none;
}
#anchor_order {
margin-bottom: 15px;
}