Move reasoning-specific code into its own module
| @@ -238,7 +238,7 @@ import { getBackgrounds, initBackgrounds, loadBackgroundSettings, background_set | |||
| 238 | import { hideLoader, showLoader } from './scripts/loader.js'; | 238 | import { hideLoader, showLoader } from './scripts/loader.js'; |
| 239 | import { BulkEditOverlay, CharacterContextMenu } from './scripts/BulkEditOverlay.js'; | 239 | import { BulkEditOverlay, CharacterContextMenu } from './scripts/BulkEditOverlay.js'; |
| 240 | import { loadFeatherlessModels, loadMancerModels, loadOllamaModels, loadTogetherAIModels, loadInfermaticAIModels, loadOpenRouterModels, loadVllmModels, loadAphroditeModels, loadDreamGenModels, initTextGenModels, loadTabbyModels, loadGenericModels } from './scripts/textgen-models.js'; | 240 | import { loadFeatherlessModels, loadMancerModels, loadOllamaModels, loadTogetherAIModels, loadInfermaticAIModels, loadOpenRouterModels, loadVllmModels, loadAphroditeModels, loadDreamGenModels, initTextGenModels, loadTabbyModels, loadGenericModels } from './scripts/textgen-models.js'; |
| 241 | import { appendFileContent, hasPendingFileAttachment, populateFileAttachment, decodeStyleTags, encodeStyleTags, isExternalMediaAllowed, getCurrentEntityId, preserveNeutralChat, restoreNeutralChat, PromptReasoning } from './scripts/chats.js'; | 241 | import { appendFileContent, hasPendingFileAttachment, populateFileAttachment, decodeStyleTags, encodeStyleTags, isExternalMediaAllowed, getCurrentEntityId, preserveNeutralChat, restoreNeutralChat } from './scripts/chats.js'; |
| 242 | import { getPresetManager, initPresetManager } from './scripts/preset-manager.js'; | 242 | import { getPresetManager, initPresetManager } from './scripts/preset-manager.js'; |
| 243 | import { evaluateMacros, getLastMessageId, initMacros } from './scripts/macros.js'; | 243 | import { evaluateMacros, getLastMessageId, initMacros } from './scripts/macros.js'; |
| 244 | import { currentUser, setUserControls } from './scripts/user.js'; | 244 | import { currentUser, setUserControls } from './scripts/user.js'; |
| @@ -267,6 +267,7 @@ import { initSettingsSearch } from './scripts/setting-search.js'; | |||
| 267 | import { initBulkEdit } from './scripts/bulk-edit.js'; | 267 | import { initBulkEdit } from './scripts/bulk-edit.js'; |
| 268 | import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js'; | 268 | import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js'; |
| 269 | import { getContext } from './scripts/st-context.js'; | 269 | import { getContext } from './scripts/st-context.js'; |
| 270 | import { initReasoning, PromptReasoning } from './scripts/reasoning.js'; | ||
| 270 | 271 | ||
| 271 | // API OBJECT FOR EXTERNAL WIRING | 272 | // API OBJECT FOR EXTERNAL WIRING |
| 272 | globalThis.SillyTavern = { | 273 | globalThis.SillyTavern = { |
| @@ -982,6 +983,7 @@ async function firstLoadInit() { | |||
| 982 | initServerHistory(); | 983 | initServerHistory(); |
| 983 | initSettingsSearch(); | 984 | initSettingsSearch(); |
| 984 | initBulkEdit(); | 985 | initBulkEdit(); |
| 986 | initReasoning(); | ||
| 985 | await initScrapers(); | 987 | await initScrapers(); |
| 986 | doDailyExtensionUpdatesCheck(); | 988 | doDailyExtensionUpdatesCheck(); |
| 987 | await hideLoader(); | 989 | await hideLoader(); |
| @@ -23,9 +23,6 @@ import { | |||
| 23 | neutralCharacterName, | 23 | neutralCharacterName, |
| 24 | updateChatMetadata, | 24 | updateChatMetadata, |
| 25 | system_message_types, | 25 | system_message_types, |
| 26 | updateMessageBlock, | ||
| 27 | closeMessageEditor, | ||
| 28 | substituteParams, | ||
| 29 | } from '../script.js'; | 26 | } from '../script.js'; |
| 30 | import { selected_group } from './group-chats.js'; | 27 | import { selected_group } from './group-chats.js'; |
| 31 | import { power_user } from './power-user.js'; | 28 | import { power_user } from './power-user.js'; |
| @@ -40,7 +37,6 @@ import { | |||
| 40 | saveBase64AsFile, | 37 | saveBase64AsFile, |
| 41 | extractTextFromOffice, | 38 | extractTextFromOffice, |
| 42 | download, | 39 | download, |
| 43 | copyText, | ||
| 44 | } from './utils.js'; | 40 | } from './utils.js'; |
| 45 | import { extension_settings, renderExtensionTemplateAsync, saveMetadataDebounced } from './extensions.js'; | 41 | import { extension_settings, renderExtensionTemplateAsync, saveMetadataDebounced } from './extensions.js'; |
| 46 | import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js'; | 42 | import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js'; |
| @@ -1420,72 +1416,7 @@ export function registerFileConverter(mimeType, converter) { | |||
| 1420 | converters[mimeType] = converter; | 1416 | converters[mimeType] = converter; |
| 1421 | } | 1417 | } |
| 1422 | 1418 | ||
| 1423 | /** | ||
| 1424 | * Helper class for adding reasoning to messages. | ||
| 1425 | * Keeps track of the number of reasoning additions. | ||
| 1426 | */ | ||
| 1427 | export class PromptReasoning { | ||
| 1428 | static REASONING_PLACEHOLDER = '\u200B'; | ||
| 1429 | |||
| 1430 | constructor() { | ||
| 1431 | this.counter = 0; | ||
| 1432 | } | ||
| 1433 | |||
| 1434 | /** | ||
| 1435 | * Checks if the limit of reasoning additions has been reached. | ||
| 1436 | * @returns {boolean} True if the limit of reasoning additions has been reached, false otherwise. | ||
| 1437 | */ | ||
| 1438 | isLimitReached() { | ||
| 1439 | if (!power_user.reasoning.add_to_prompts) { | ||
| 1440 | return true; | ||
| 1441 | } | ||
| 1442 | |||
| 1443 | return this.counter >= power_user.reasoning.max_additions; | ||
| 1444 | } | ||
| 1445 | |||
| 1446 | /** | ||
| 1447 | * Add reasoning to a message according to the power user settings. | ||
| 1448 | * @param {string} content Message content | ||
| 1449 | * @param {string} reasoning Message reasoning | ||
| 1450 | * @returns {string} Message content with reasoning | ||
| 1451 | */ | ||
| 1452 | addToMessage(content, reasoning) { | ||
| 1453 | // Disabled or reached limit of additions | ||
| 1454 | if (!power_user.reasoning.add_to_prompts || this.counter >= power_user.reasoning.max_additions) { | ||
| 1455 | return content; | ||
| 1456 | } | ||
| 1457 | |||
| 1458 | // No reasoning provided or a placeholder | ||
| 1459 | if (!reasoning || reasoning === PromptReasoning.REASONING_PLACEHOLDER) { | ||
| 1460 | return content; | ||
| 1461 | } | ||
| 1462 | |||
| 1463 | // Increment the counter | ||
| 1464 | this.counter++; | ||
| 1465 | |||
| 1466 | // Substitute macros in variable parts | ||
| 1467 | const prefix = substituteParams(power_user.reasoning.prefix || ''); | ||
| 1468 | const separator = substituteParams(power_user.reasoning.separator || ''); | ||
| 1469 | const suffix = substituteParams(power_user.reasoning.suffix || ''); | ||
| 1470 | |||
| 1471 | // Combine parts with reasoning and content | ||
| 1472 | return `${prefix}${reasoning}${suffix}${separator}${content}`; | ||
| 1473 | } | ||
| 1474 | } | ||
| 1475 | |||
| 1476 | jQuery(function () { | 1419 | jQuery(function () { |
| 1477 | /** | ||
| 1478 | * Gets a message from a jQuery element. | ||
| 1479 | * @param {Element} element | ||
| 1480 | * @returns {{messageId: number, message: object, messageBlock: JQuery<HTMLElement>}} | ||
| 1481 | */ | ||
| 1482 | const getMessageFromJquery = function (element) { | ||
| 1483 | const messageBlock = $(element).closest('.mes'); | ||
| 1484 | const messageId = Number(messageBlock.attr('mesid')); | ||
| 1485 | const message = chat[messageId]; | ||
| 1486 | return { messageId: messageId, message, messageBlock }; | ||
| 1487 | }; | ||
| 1488 | |||
| 1489 | $(document).on('click', '.mes_hide', async function () { | 1420 | $(document).on('click', '.mes_hide', async function () { |
| 1490 | const messageBlock = $(this).closest('.mes'); | 1421 | const messageBlock = $(this).closest('.mes'); |
| 1491 | const messageId = Number(messageBlock.attr('mesid')); | 1422 | const messageId = Number(messageBlock.attr('mesid')); |
| @@ -1636,125 +1567,6 @@ jQuery(function () { | |||
| 1636 | $(document).on('click', '.mes_img_enlarge', enlargeMessageImage); | 1567 | $(document).on('click', '.mes_img_enlarge', enlargeMessageImage); |
| 1637 | $(document).on('click', '.mes_img_delete', deleteMessageImage); | 1568 | $(document).on('click', '.mes_img_delete', deleteMessageImage); |
| 1638 | 1569 | ||
| 1639 | $(document).on('click', '.mes_reasoning_copy', (e) => { | ||
| 1640 | e.stopPropagation(); | ||
| 1641 | e.preventDefault(); | ||
| 1642 | }); | ||
| 1643 | |||
| 1644 | $(document).on('click', '.mes_reasoning_edit', function (e) { | ||
| 1645 | e.stopPropagation(); | ||
| 1646 | e.preventDefault(); | ||
| 1647 | const { message, messageBlock } = getMessageFromJquery(this); | ||
| 1648 | if (!message?.extra) { | ||
| 1649 | return; | ||
| 1650 | } | ||
| 1651 | |||
| 1652 | const reasoning = message?.extra?.reasoning; | ||
| 1653 | const chatElement = document.getElementById('chat'); | ||
| 1654 | const textarea = document.createElement('textarea'); | ||
| 1655 | const reasoningBlock = messageBlock.find('.mes_reasoning'); | ||
| 1656 | textarea.classList.add('reasoning_edit_textarea'); | ||
| 1657 | textarea.value = reasoning === PromptReasoning.REASONING_PLACEHOLDER ? '' : reasoning; | ||
| 1658 | $(textarea).insertBefore(reasoningBlock); | ||
| 1659 | |||
| 1660 | if (!CSS.supports('field-sizing', 'content')) { | ||
| 1661 | const resetHeight = function () { | ||
| 1662 | const scrollTop = chatElement.scrollTop; | ||
| 1663 | textarea.style.height = '0px'; | ||
| 1664 | textarea.style.height = `${textarea.scrollHeight}px`; | ||
| 1665 | chatElement.scrollTop = scrollTop; | ||
| 1666 | }; | ||
| 1667 | |||
| 1668 | textarea.addEventListener('input', resetHeight); | ||
| 1669 | resetHeight(); | ||
| 1670 | } | ||
| 1671 | |||
| 1672 | textarea.focus(); | ||
| 1673 | textarea.setSelectionRange(textarea.value.length, textarea.value.length); | ||
| 1674 | |||
| 1675 | const textareaRect = textarea.getBoundingClientRect(); | ||
| 1676 | const chatRect = chatElement.getBoundingClientRect(); | ||
| 1677 | |||
| 1678 | // Scroll if textarea bottom is below visible area | ||
| 1679 | if (textareaRect.bottom > chatRect.bottom) { | ||
| 1680 | const scrollOffset = textareaRect.bottom - chatRect.bottom; | ||
| 1681 | chatElement.scrollTop += scrollOffset; | ||
| 1682 | } | ||
| 1683 | }); | ||
| 1684 | |||
| 1685 | $(document).on('click', '.mes_reasoning_edit_done', async function (e) { | ||
| 1686 | e.stopPropagation(); | ||
| 1687 | e.preventDefault(); | ||
| 1688 | const { message, messageId, messageBlock } = getMessageFromJquery(this); | ||
| 1689 | if (!message?.extra) { | ||
| 1690 | return; | ||
| 1691 | } | ||
| 1692 | |||
| 1693 | const textarea = messageBlock.find('.reasoning_edit_textarea'); | ||
| 1694 | const reasoning = String(textarea.val()); | ||
| 1695 | message.extra.reasoning = reasoning; | ||
| 1696 | await saveChatConditional(); | ||
| 1697 | updateMessageBlock(messageId, message); | ||
| 1698 | textarea.remove(); | ||
| 1699 | }); | ||
| 1700 | |||
| 1701 | $(document).on('click', '.mes_reasoning_edit_cancel', function (e) { | ||
| 1702 | e.stopPropagation(); | ||
| 1703 | e.preventDefault(); | ||
| 1704 | |||
| 1705 | const { messageBlock } = getMessageFromJquery(this); | ||
| 1706 | const textarea = messageBlock.find('.reasoning_edit_textarea'); | ||
| 1707 | textarea.remove(); | ||
| 1708 | }); | ||
| 1709 | |||
| 1710 | $(document).on('click', '.mes_edit_add_reasoning', async function () { | ||
| 1711 | const { message, messageId } = getMessageFromJquery(this); | ||
| 1712 | if (!message?.extra) { | ||
| 1713 | return; | ||
| 1714 | } | ||
| 1715 | |||
| 1716 | if (message.extra.reasoning) { | ||
| 1717 | toastr.info(t`Reasoning already exists.`, t`Edit Message`); | ||
| 1718 | return; | ||
| 1719 | } | ||
| 1720 | |||
| 1721 | message.extra.reasoning = PromptReasoning.REASONING_PLACEHOLDER; | ||
| 1722 | await saveChatConditional(); | ||
| 1723 | closeMessageEditor(); | ||
| 1724 | updateMessageBlock(messageId, message); | ||
| 1725 | }); | ||
| 1726 | |||
| 1727 | $(document).on('click', '.mes_reasoning_delete', async function (e) { | ||
| 1728 | e.stopPropagation(); | ||
| 1729 | e.preventDefault(); | ||
| 1730 | |||
| 1731 | const confirm = await Popup.show.confirm(t`Are you sure you want to clear the reasoning?`, t`Visible message contents will stay intact.`); | ||
| 1732 | |||
| 1733 | if (!confirm) { | ||
| 1734 | return; | ||
| 1735 | } | ||
| 1736 | |||
| 1737 | const { message, messageId } = getMessageFromJquery(this); | ||
| 1738 | if (!message?.extra) { | ||
| 1739 | return; | ||
| 1740 | } | ||
| 1741 | message.extra.reasoning = ''; | ||
| 1742 | await saveChatConditional(); | ||
| 1743 | updateMessageBlock(messageId, message); | ||
| 1744 | }); | ||
| 1745 | |||
| 1746 | $(document).on('pointerup', '.mes_reasoning_copy', async function () { | ||
| 1747 | const { message } = getMessageFromJquery(this); | ||
| 1748 | const reasoning = message?.extra?.reasoning; | ||
| 1749 | |||
| 1750 | if (!reasoning) { | ||
| 1751 | return; | ||
| 1752 | } | ||
| 1753 | |||
| 1754 | await copyText(reasoning); | ||
| 1755 | toastr.info(t`Copied!`, '', { timeOut: 2000 }); | ||
| 1756 | }); | ||
| 1757 | |||
| 1758 | $('#file_form_input').on('change', async () => { | 1570 | $('#file_form_input').on('change', async () => { |
| 1759 | const fileInput = document.getElementById('file_form_input'); | 1571 | const fileInput = document.getElementById('file_form_input'); |
| 1760 | if (!(fileInput instanceof HTMLInputElement)) return; | 1572 | if (!(fileInput instanceof HTMLInputElement)) return; |
| @@ -1621,7 +1621,6 @@ async function loadPowerUserSettings(settings, data) { | |||
| 1621 | loadMovingUIState(); | 1621 | loadMovingUIState(); |
| 1622 | loadCharListState(); | 1622 | loadCharListState(); |
| 1623 | toggleMDHotkeyIconDisplay(); | 1623 | toggleMDHotkeyIconDisplay(); |
| 1624 | loadReasoningSettings(); | ||
| 1625 | } | 1624 | } |
| 1626 | 1625 | ||
| 1627 | function toggleMDHotkeyIconDisplay() { | 1626 | function toggleMDHotkeyIconDisplay() { |
| @@ -1638,38 +1637,6 @@ function loadCharListState() { | |||
| 1638 | document.body.classList.toggle('charListGrid', power_user.charListGrid); | 1637 | document.body.classList.toggle('charListGrid', power_user.charListGrid); |
| 1639 | } | 1638 | } |
| 1640 | 1639 | ||
| 1641 | function loadReasoningSettings() { | ||
| 1642 | $('#reasoning_add_to_prompts').prop('checked', power_user.reasoning.add_to_prompts); | ||
| 1643 | $('#reasoning_add_to_prompts').on('change', function () { | ||
| 1644 | power_user.reasoning.add_to_prompts = !!$(this).prop('checked'); | ||
| 1645 | saveSettingsDebounced(); | ||
| 1646 | }); | ||
| 1647 | |||
| 1648 | $('#reasoning_prefix').val(power_user.reasoning.prefix); | ||
| 1649 | $('#reasoning_prefix').on('input', function () { | ||
| 1650 | power_user.reasoning.prefix = String($(this).val()); | ||
| 1651 | saveSettingsDebounced(); | ||
| 1652 | }); | ||
| 1653 | |||
| 1654 | $('#reasoning_suffix').val(power_user.reasoning.suffix); | ||
| 1655 | $('#reasoning_suffix').on('input', function () { | ||
| 1656 | power_user.reasoning.suffix = String($(this).val()); | ||
| 1657 | saveSettingsDebounced(); | ||
| 1658 | }); | ||
| 1659 | |||
| 1660 | $('#reasoning_separator').val(power_user.reasoning.separator); | ||
| 1661 | $('#reasoning_separator').on('input', function () { | ||
| 1662 | power_user.reasoning.separator = String($(this).val()); | ||
| 1663 | saveSettingsDebounced(); | ||
| 1664 | }); | ||
| 1665 | |||
| 1666 | $('#reasoning_max_additions').val(power_user.reasoning.max_additions); | ||
| 1667 | $('#reasoning_max_additions').on('input', function () { | ||
| 1668 | power_user.reasoning.max_additions = Number($(this).val()); | ||
| 1669 | saveSettingsDebounced(); | ||
| 1670 | }); | ||
| 1671 | } | ||
| 1672 | |||
| 1673 | function loadMovingUIState() { | 1640 | function loadMovingUIState() { |
| 1674 | if (!isMobile() | 1641 | if (!isMobile() |
| 1675 | && power_user.movingUIState | 1642 | && power_user.movingUIState |
| @@ -0,0 +1,228 @@ | |||
| 1 | import { chat, closeMessageEditor, saveChatConditional, saveSettingsDebounced, substituteParams, updateMessageBlock } from '../script.js'; | ||
| 2 | import { t } from './i18n.js'; | ||
| 3 | import { Popup } from './popup.js'; | ||
| 4 | import { power_user } from './power-user.js'; | ||
| 5 | import { copyText } from './utils.js'; | ||
| 6 | |||
| 7 | /** | ||
| 8 | * Gets a message from a jQuery element. | ||
| 9 | * @param {Element} element | ||
| 10 | * @returns {{messageId: number, message: object, messageBlock: JQuery<HTMLElement>}} | ||
| 11 | */ | ||
| 12 | function getMessageFromJquery(element) { | ||
| 13 | const messageBlock = $(element).closest('.mes'); | ||
| 14 | const messageId = Number(messageBlock.attr('mesid')); | ||
| 15 | const message = chat[messageId]; | ||
| 16 | return { messageId: messageId, message, messageBlock }; | ||
| 17 | } | ||
| 18 | |||
| 19 | /** | ||
| 20 | * Helper class for adding reasoning to messages. | ||
| 21 | * Keeps track of the number of reasoning additions. | ||
| 22 | */ | ||
| 23 | export class PromptReasoning { | ||
| 24 | static REASONING_PLACEHOLDER = '\u200B'; | ||
| 25 | |||
| 26 | constructor() { | ||
| 27 | this.counter = 0; | ||
| 28 | } | ||
| 29 | |||
| 30 | /** | ||
| 31 | * Checks if the limit of reasoning additions has been reached. | ||
| 32 | * @returns {boolean} True if the limit of reasoning additions has been reached, false otherwise. | ||
| 33 | */ | ||
| 34 | isLimitReached() { | ||
| 35 | if (!power_user.reasoning.add_to_prompts) { | ||
| 36 | return true; | ||
| 37 | } | ||
| 38 | |||
| 39 | return this.counter >= power_user.reasoning.max_additions; | ||
| 40 | } | ||
| 41 | |||
| 42 | /** | ||
| 43 | * Add reasoning to a message according to the power user settings. | ||
| 44 | * @param {string} content Message content | ||
| 45 | * @param {string} reasoning Message reasoning | ||
| 46 | * @returns {string} Message content with reasoning | ||
| 47 | */ | ||
| 48 | addToMessage(content, reasoning) { | ||
| 49 | // Disabled or reached limit of additions | ||
| 50 | if (!power_user.reasoning.add_to_prompts || this.counter >= power_user.reasoning.max_additions) { | ||
| 51 | return content; | ||
| 52 | } | ||
| 53 | |||
| 54 | // No reasoning provided or a placeholder | ||
| 55 | if (!reasoning || reasoning === PromptReasoning.REASONING_PLACEHOLDER) { | ||
| 56 | return content; | ||
| 57 | } | ||
| 58 | |||
| 59 | // Increment the counter | ||
| 60 | this.counter++; | ||
| 61 | |||
| 62 | // Substitute macros in variable parts | ||
| 63 | const prefix = substituteParams(power_user.reasoning.prefix || ''); | ||
| 64 | const separator = substituteParams(power_user.reasoning.separator || ''); | ||
| 65 | const suffix = substituteParams(power_user.reasoning.suffix || ''); | ||
| 66 | |||
| 67 | // Combine parts with reasoning and content | ||
| 68 | return `${prefix}${reasoning}${suffix}${separator}${content}`; | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | function loadReasoningSettings() { | ||
| 73 | $('#reasoning_add_to_prompts').prop('checked', power_user.reasoning.add_to_prompts); | ||
| 74 | $('#reasoning_add_to_prompts').on('change', function () { | ||
| 75 | power_user.reasoning.add_to_prompts = !!$(this).prop('checked'); | ||
| 76 | saveSettingsDebounced(); | ||
| 77 | }); | ||
| 78 | |||
| 79 | $('#reasoning_prefix').val(power_user.reasoning.prefix); | ||
| 80 | $('#reasoning_prefix').on('input', function () { | ||
| 81 | power_user.reasoning.prefix = String($(this).val()); | ||
| 82 | saveSettingsDebounced(); | ||
| 83 | }); | ||
| 84 | |||
| 85 | $('#reasoning_suffix').val(power_user.reasoning.suffix); | ||
| 86 | $('#reasoning_suffix').on('input', function () { | ||
| 87 | power_user.reasoning.suffix = String($(this).val()); | ||
| 88 | saveSettingsDebounced(); | ||
| 89 | }); | ||
| 90 | |||
| 91 | $('#reasoning_separator').val(power_user.reasoning.separator); | ||
| 92 | $('#reasoning_separator').on('input', function () { | ||
| 93 | power_user.reasoning.separator = String($(this).val()); | ||
| 94 | saveSettingsDebounced(); | ||
| 95 | }); | ||
| 96 | |||
| 97 | $('#reasoning_max_additions').val(power_user.reasoning.max_additions); | ||
| 98 | $('#reasoning_max_additions').on('input', function () { | ||
| 99 | power_user.reasoning.max_additions = Number($(this).val()); | ||
| 100 | saveSettingsDebounced(); | ||
| 101 | }); | ||
| 102 | } | ||
| 103 | |||
| 104 | function setReasoningEventHandlers(){ | ||
| 105 | $(document).on('click', '.mes_reasoning_copy', (e) => { | ||
| 106 | e.stopPropagation(); | ||
| 107 | e.preventDefault(); | ||
| 108 | }); | ||
| 109 | |||
| 110 | $(document).on('click', '.mes_reasoning_edit', function (e) { | ||
| 111 | e.stopPropagation(); | ||
| 112 | e.preventDefault(); | ||
| 113 | const { message, messageBlock } = getMessageFromJquery(this); | ||
| 114 | if (!message?.extra) { | ||
| 115 | return; | ||
| 116 | } | ||
| 117 | |||
| 118 | const reasoning = message?.extra?.reasoning; | ||
| 119 | const chatElement = document.getElementById('chat'); | ||
| 120 | const textarea = document.createElement('textarea'); | ||
| 121 | const reasoningBlock = messageBlock.find('.mes_reasoning'); | ||
| 122 | textarea.classList.add('reasoning_edit_textarea'); | ||
| 123 | textarea.value = reasoning === PromptReasoning.REASONING_PLACEHOLDER ? '' : reasoning; | ||
| 124 | $(textarea).insertBefore(reasoningBlock); | ||
| 125 | |||
| 126 | if (!CSS.supports('field-sizing', 'content')) { | ||
| 127 | const resetHeight = function () { | ||
| 128 | const scrollTop = chatElement.scrollTop; | ||
| 129 | textarea.style.height = '0px'; | ||
| 130 | textarea.style.height = `${textarea.scrollHeight}px`; | ||
| 131 | chatElement.scrollTop = scrollTop; | ||
| 132 | }; | ||
| 133 | |||
| 134 | textarea.addEventListener('input', resetHeight); | ||
| 135 | resetHeight(); | ||
| 136 | } | ||
| 137 | |||
| 138 | textarea.focus(); | ||
| 139 | textarea.setSelectionRange(textarea.value.length, textarea.value.length); | ||
| 140 | |||
| 141 | const textareaRect = textarea.getBoundingClientRect(); | ||
| 142 | const chatRect = chatElement.getBoundingClientRect(); | ||
| 143 | |||
| 144 | // Scroll if textarea bottom is below visible area | ||
| 145 | if (textareaRect.bottom > chatRect.bottom) { | ||
| 146 | const scrollOffset = textareaRect.bottom - chatRect.bottom; | ||
| 147 | chatElement.scrollTop += scrollOffset; | ||
| 148 | } | ||
| 149 | }); | ||
| 150 | |||
| 151 | $(document).on('click', '.mes_reasoning_edit_done', async function (e) { | ||
| 152 | e.stopPropagation(); | ||
| 153 | e.preventDefault(); | ||
| 154 | const { message, messageId, messageBlock } = getMessageFromJquery(this); | ||
| 155 | if (!message?.extra) { | ||
| 156 | return; | ||
| 157 | } | ||
| 158 | |||
| 159 | const textarea = messageBlock.find('.reasoning_edit_textarea'); | ||
| 160 | const reasoning = String(textarea.val()); | ||
| 161 | message.extra.reasoning = reasoning; | ||
| 162 | await saveChatConditional(); | ||
| 163 | updateMessageBlock(messageId, message); | ||
| 164 | textarea.remove(); | ||
| 165 | }); | ||
| 166 | |||
| 167 | $(document).on('click', '.mes_reasoning_edit_cancel', function (e) { | ||
| 168 | e.stopPropagation(); | ||
| 169 | e.preventDefault(); | ||
| 170 | |||
| 171 | const { messageBlock } = getMessageFromJquery(this); | ||
| 172 | const textarea = messageBlock.find('.reasoning_edit_textarea'); | ||
| 173 | textarea.remove(); | ||
| 174 | }); | ||
| 175 | |||
| 176 | $(document).on('click', '.mes_edit_add_reasoning', async function () { | ||
| 177 | const { message, messageId } = getMessageFromJquery(this); | ||
| 178 | if (!message?.extra) { | ||
| 179 | return; | ||
| 180 | } | ||
| 181 | |||
| 182 | if (message.extra.reasoning) { | ||
| 183 | toastr.info(t`Reasoning already exists.`, t`Edit Message`); | ||
| 184 | return; | ||
| 185 | } | ||
| 186 | |||
| 187 | message.extra.reasoning = PromptReasoning.REASONING_PLACEHOLDER; | ||
| 188 | await saveChatConditional(); | ||
| 189 | closeMessageEditor(); | ||
| 190 | updateMessageBlock(messageId, message); | ||
| 191 | }); | ||
| 192 | |||
| 193 | $(document).on('click', '.mes_reasoning_delete', async function (e) { | ||
| 194 | e.stopPropagation(); | ||
| 195 | e.preventDefault(); | ||
| 196 | |||
| 197 | const confirm = await Popup.show.confirm(t`Are you sure you want to clear the reasoning?`, t`Visible message contents will stay intact.`); | ||
| 198 | |||
| 199 | if (!confirm) { | ||
| 200 | return; | ||
| 201 | } | ||
| 202 | |||
| 203 | const { message, messageId } = getMessageFromJquery(this); | ||
| 204 | if (!message?.extra) { | ||
| 205 | return; | ||
| 206 | } | ||
| 207 | message.extra.reasoning = ''; | ||
| 208 | await saveChatConditional(); | ||
| 209 | updateMessageBlock(messageId, message); | ||
| 210 | }); | ||
| 211 | |||
| 212 | $(document).on('pointerup', '.mes_reasoning_copy', async function () { | ||
| 213 | const { message } = getMessageFromJquery(this); | ||
| 214 | const reasoning = message?.extra?.reasoning; | ||
| 215 | |||
| 216 | if (!reasoning) { | ||
| 217 | return; | ||
| 218 | } | ||
| 219 | |||
| 220 | await copyText(reasoning); | ||
| 221 | toastr.info(t`Copied!`, '', { timeOut: 2000 }); | ||
| 222 | }); | ||
| 223 | } | ||
| 224 | |||
| 225 | export function initReasoning() { | ||
| 226 | loadReasoningSettings(); | ||
| 227 | setReasoningEventHandlers(); | ||
| 228 | } | ||